rtsp-server: add API to enable retransmission requests
authorMathieu Duponchelle <mathieu@centricular.com>
Thu, 29 Mar 2018 20:49:26 +0000 (22:49 +0200)
committerMathieu Duponchelle <mathieu@centricular.com>
Fri, 30 Mar 2018 15:55:32 +0000 (17:55 +0200)
"do-retransmission" was previously set when rtx-time != 0,
which made no sense as do-retransmission is used to enable
the sending of retransmission requests, where as rtx-time
is used by the peer to enable storing of buffers in order
to respond to retransmission requests.

rtsp-media now also provides a callback for the
request-aux-receiver signal.

https://bugzilla.gnome.org/show_bug.cgi?id=794822

docs/libs/gst-rtsp-server-sections.txt
gst/rtsp-server/rtsp-media-factory.c
gst/rtsp-server/rtsp-media-factory.h
gst/rtsp-server/rtsp-media.c
gst/rtsp-server/rtsp-media.h
gst/rtsp-server/rtsp-stream.c
gst/rtsp-server/rtsp-stream.h

index 103cd4a..956ac0a 100644 (file)
@@ -178,6 +178,9 @@ gst_rtsp_media_get_buffer_size
 gst_rtsp_media_set_retransmission_time
 gst_rtsp_media_get_retransmission_time
 
+gst_rtsp_media_set_do_retransmission
+gst_rtsp_media_get_do_retransmission
+
 gst_rtsp_media_set_latency
 gst_rtsp_media_get_latency
 
@@ -277,6 +280,9 @@ gst_rtsp_media_factory_set_suspend_mode
 gst_rtsp_media_factory_set_retransmission_time
 gst_rtsp_media_factory_get_retransmission_time
 
+gst_rtsp_media_factory_set_do_retransmission
+gst_rtsp_media_factory_get_do_retransmission
+
 gst_rtsp_media_factory_set_latency
 gst_rtsp_media_factory_get_latency
 
@@ -617,6 +623,7 @@ gst_rtsp_stream_update_crypto
 
 gst_rtsp_stream_set_pt_map
 gst_rtsp_stream_request_aux_sender
+gst_rtsp_stream_request_aux_receiver
 
 gst_rtsp_stream_seekable
 
index 5987d1b..80208d3 100644 (file)
@@ -65,6 +65,7 @@ struct _GstRTSPMediaFactoryPrivate
 
   GstClockTime rtx_time;
   guint latency;
+  gboolean do_retransmission;
 
   GMutex medias_lock;
   GHashTable *medias;           /* protected by medias_lock */
@@ -87,6 +88,7 @@ struct _GstRTSPMediaFactoryPrivate
 #define DEFAULT_LATENCY         200
 #define DEFAULT_TRANSPORT_MODE  GST_RTSP_TRANSPORT_MODE_PLAY
 #define DEFAULT_STOP_ON_DISCONNECT TRUE
+#define DEFAULT_DO_RETRANSMISSION FALSE
 
 enum
 {
@@ -264,6 +266,7 @@ gst_rtsp_media_factory_init (GstRTSPMediaFactory * factory)
   priv->transport_mode = DEFAULT_TRANSPORT_MODE;
   priv->stop_on_disconnect = DEFAULT_STOP_ON_DISCONNECT;
   priv->publish_clock_mode = GST_RTSP_PUBLISH_CLOCK_MODE_CLOCK;
+  priv->do_retransmission = DEFAULT_DO_RETRANSMISSION;
 
   g_mutex_init (&priv->lock);
   g_mutex_init (&priv->medias_lock);
@@ -1086,6 +1089,55 @@ gst_rtsp_media_factory_get_retransmission_time (GstRTSPMediaFactory * factory)
 }
 
 /**
+ * gst_rtsp_media_factory_set_do_retransmission:
+ *
+ * Set whether retransmission requests will be sent for
+ * receiving media
+ *
+ * Since: 1.16
+ */
+void
+gst_rtsp_media_factory_set_do_retransmission (GstRTSPMediaFactory * factory,
+                                              gboolean do_retransmission)
+{
+  GstRTSPMediaFactoryPrivate *priv;
+
+  g_return_if_fail (GST_IS_RTSP_MEDIA_FACTORY (factory));
+
+  priv = factory->priv;
+
+  GST_DEBUG_OBJECT (factory, "Do retransmission %d", do_retransmission);
+
+  GST_RTSP_MEDIA_FACTORY_LOCK (factory);
+  priv->do_retransmission = do_retransmission;
+  GST_RTSP_MEDIA_FACTORY_UNLOCK (factory);
+}
+
+/**
+ * gst_rtsp_media_factory_get_do_retransmission:
+ *
+ * Returns: Whether retransmission requests will be sent for receiving media
+ *
+ * Since: 1.16
+ */
+gboolean
+gst_rtsp_media_factory_get_do_retransmission (GstRTSPMediaFactory * factory)
+{
+  GstRTSPMediaFactoryPrivate *priv;
+  gboolean res;
+
+  g_return_val_if_fail (GST_IS_RTSP_MEDIA_FACTORY (factory), 0);
+
+  priv = factory->priv;
+
+  GST_RTSP_MEDIA_FACTORY_LOCK (factory);
+  res = priv->do_retransmission;
+  GST_RTSP_MEDIA_FACTORY_UNLOCK (factory);
+
+  return res;
+}
+
+/**
  * gst_rtsp_media_factory_set_latency:
  * @factory: a #GstRTSPMediaFactory
  * @latency: latency in milliseconds
@@ -1584,6 +1636,7 @@ default_configure (GstRTSPMediaFactory * factory, GstRTSPMedia * media)
   gst_rtsp_media_set_profiles (media, profiles);
   gst_rtsp_media_set_protocols (media, protocols);
   gst_rtsp_media_set_retransmission_time (media, rtx_time);
+  gst_rtsp_media_set_do_retransmission (media, priv->do_retransmission);
   gst_rtsp_media_set_latency (media, latency);
   gst_rtsp_media_set_transport_mode (media, transport_mode);
   gst_rtsp_media_set_stop_on_disconnect (media, stop_on_disconnect);
index 350dae8..7fb7fcc 100644 (file)
@@ -199,6 +199,13 @@ GST_RTSP_SERVER_API
 GstClockTime          gst_rtsp_media_factory_get_retransmission_time (GstRTSPMediaFactory * factory);
 
 GST_RTSP_SERVER_API
+void                  gst_rtsp_media_factory_set_do_retransmission (GstRTSPMediaFactory * factory,
+                                                                    gboolean do_retransmission);
+
+GST_RTSP_SERVER_API
+gboolean              gst_rtsp_media_factory_get_do_retransmission (GstRTSPMediaFactory * factory);
+
+GST_RTSP_SERVER_API
 void                  gst_rtsp_media_factory_set_latency      (GstRTSPMediaFactory * factory,
                                                                guint                 latency);
 
index a1ab7aa..217dc35 100644 (file)
@@ -139,6 +139,7 @@ struct _GstRTSPMediaPrivate
 
   GList *payloads;              /* protected by lock */
   GstClockTime rtx_time;        /* protected by lock */
+  gboolean do_retransmission;   /* protected by lock */
   guint latency;                /* protected by lock */
   GstClock *clock;              /* protected by lock */
   GstRTSPPublishClockMode publish_clock_mode;
@@ -161,6 +162,8 @@ struct _GstRTSPMediaPrivate
 #define DEFAULT_TRANSPORT_MODE  GST_RTSP_TRANSPORT_MODE_PLAY
 #define DEFAULT_STOP_ON_DISCONNECT TRUE
 
+#define DEFAULT_DO_RETRANSMISSION FALSE
+
 /* define to dump received RTCP packets */
 #undef DUMP_STATS
 
@@ -444,6 +447,7 @@ gst_rtsp_media_init (GstRTSPMedia * media)
   priv->transport_mode = DEFAULT_TRANSPORT_MODE;
   priv->stop_on_disconnect = DEFAULT_STOP_ON_DISCONNECT;
   priv->publish_clock_mode = GST_RTSP_PUBLISH_CLOCK_MODE_CLOCK;
+  priv->do_retransmission = DEFAULT_DO_RETRANSMISSION;
 }
 
 static void
@@ -1408,9 +1412,6 @@ gst_rtsp_media_set_retransmission_time (GstRTSPMedia * media, GstClockTime time)
 
     gst_rtsp_stream_set_retransmission_time (stream, time);
   }
-
-  if (priv->rtpbin)
-    g_object_set (priv->rtpbin, "do-retransmission", time > 0, NULL);
   g_mutex_unlock (&priv->lock);
 }
 
@@ -1440,6 +1441,54 @@ gst_rtsp_media_get_retransmission_time (GstRTSPMedia * media)
 }
 
 /**
+ * gst_rtsp_media_set_do_retransmission:
+ *
+ * Set whether retransmission requests will be sent
+ *
+ * Since: 1.16
+ */
+void
+gst_rtsp_media_set_do_retransmission (GstRTSPMedia * media, gboolean do_retransmission)
+{
+  GstRTSPMediaPrivate *priv;
+
+  g_return_if_fail (GST_IS_RTSP_MEDIA (media));
+
+  priv = media->priv;
+
+  g_mutex_lock (&priv->lock);
+  priv->do_retransmission = do_retransmission;
+
+  if (priv->rtpbin)
+    g_object_set (priv->rtpbin, "do-retransmission", do_retransmission, NULL);
+  g_mutex_unlock (&priv->lock);
+}
+
+/**
+ * gst_rtsp_media_get_do_retransmission:
+ *
+ * Returns: Whether retransmission requests will be sent
+ *
+ * Since: 1.16
+ */
+gboolean
+gst_rtsp_media_get_do_retransmission (GstRTSPMedia * media)
+{
+  GstRTSPMediaPrivate *priv;
+  gboolean res;
+
+  g_return_val_if_fail (GST_IS_RTSP_MEDIA (media), 0);
+
+  priv = media->priv;
+
+  g_mutex_lock (&priv->lock);
+  res = priv->do_retransmission;
+  g_mutex_unlock (&priv->lock);
+
+  return res;
+}
+
+/**
  * gst_rtsp_media_set_latency:
  * @media: a #GstRTSPMedia
  * @latency: latency in milliseconds
@@ -2997,6 +3046,32 @@ request_aux_sender (GstElement * rtpbin, guint sessid, GstRTSPMedia * media)
   GstRTSPMediaPrivate *priv = media->priv;
   GstRTSPStream *stream = NULL;
   guint i;
+  GstElement *res = NULL;
+
+  g_mutex_lock (&priv->lock);
+  for (i = 0; i < priv->streams->len; i++) {
+    stream = g_ptr_array_index (priv->streams, i);
+
+    if (sessid == gst_rtsp_stream_get_index (stream))
+      break;
+
+    stream = NULL;
+  }
+  g_mutex_unlock (&priv->lock);
+
+  if (stream)
+    res = gst_rtsp_stream_request_aux_sender (stream, sessid);
+
+  return res;
+}
+
+static GstElement *
+request_aux_receiver (GstElement * rtpbin, guint sessid, GstRTSPMedia * media)
+{
+  GstRTSPMediaPrivate *priv = media->priv;
+  GstRTSPStream *stream = NULL;
+  guint i;
+  GstElement *res = NULL;
 
   g_mutex_lock (&priv->lock);
   for (i = 0; i < priv->streams->len; i++) {
@@ -3004,10 +3079,15 @@ request_aux_sender (GstElement * rtpbin, guint sessid, GstRTSPMedia * media)
 
     if (sessid == gst_rtsp_stream_get_index (stream))
       break;
+
+    stream = NULL;
   }
   g_mutex_unlock (&priv->lock);
 
-  return gst_rtsp_stream_request_aux_sender (stream, sessid);
+  if (stream)
+    res = gst_rtsp_stream_request_aux_receiver (stream, sessid);
+
+  return res;
 }
 
 static gboolean
@@ -3034,6 +3114,11 @@ start_prepare (GstRTSPMedia * media)
           (GCallback) request_aux_sender, media);
     }
 
+    if (priv->do_retransmission) {
+      g_signal_connect (priv->rtpbin, "request-aux-receiver",
+          (GCallback) request_aux_receiver, media);
+    }
+
     if (!gst_rtsp_stream_join_bin (stream, GST_BIN (priv->pipeline),
             priv->rtpbin, GST_STATE_NULL)) {
       goto join_bin_failed;
@@ -3041,7 +3126,7 @@ start_prepare (GstRTSPMedia * media)
   }
 
   if (priv->rtpbin)
-    g_object_set (priv->rtpbin, "do-retransmission", priv->rtx_time > 0, NULL);
+    g_object_set (priv->rtpbin, "do-retransmission", priv->do_retransmission, NULL);
 
   for (walk = priv->dynamic; walk; walk = g_list_next (walk)) {
     GstElement *elem = walk->data;
index 4d47517..84dfb14 100644 (file)
@@ -282,6 +282,13 @@ GST_RTSP_SERVER_API
 GstClockTime          gst_rtsp_media_get_retransmission_time  (GstRTSPMedia *media);
 
 GST_RTSP_SERVER_API
+void                  gst_rtsp_media_set_do_retransmission (GstRTSPMedia * media,
+                                                            gboolean do_retransmission);
+
+GST_RTSP_SERVER_API
+gboolean              gst_rtsp_media_get_do_retransmission (GstRTSPMedia * media);
+
+GST_RTSP_SERVER_API
 void                  gst_rtsp_media_set_latency      (GstRTSPMedia *media, guint latency);
 
 GST_RTSP_SERVER_API
index f640d5a..abc0a56 100644 (file)
@@ -124,6 +124,7 @@ struct _GstRTSPStreamPrivate
 
   /* retransmission */
   GstElement *rtxsend;
+  GstElement *rtxreceive;
   guint rtx_pt;
   GstClockTime rtx_time;
 
@@ -298,6 +299,8 @@ gst_rtsp_stream_finalize (GObject * obj)
     g_object_unref (priv->pool);
   if (priv->rtxsend)
     g_object_unref (priv->rtxsend);
+  if (priv->rtxreceive)
+    g_object_unref (priv->rtxreceive);
 
   for (i = 0; i < 2; i++) {
     if (priv->socket_v4[i])
@@ -2328,6 +2331,81 @@ gst_rtsp_stream_request_aux_sender (GstRTSPStream * stream, guint sessid)
   return bin;
 }
 
+static void
+add_rtx_pt (gpointer key, GstCaps *caps, GstStructure *pt_map)
+{
+  guint pt = GPOINTER_TO_INT (key);
+  const GstStructure *s = gst_caps_get_structure (caps, 0);
+  const gchar *apt;
+
+  if (!g_strcmp0 (gst_structure_get_string (s, "encoding-name"), "RTX") &&
+      (apt = gst_structure_get_string (s, "apt"))) {
+    gst_structure_set (pt_map, apt, G_TYPE_UINT, pt, NULL);
+  }
+}
+
+/* Call with priv->lock taken */
+static void
+update_rtx_receive_pt_map (GstRTSPStream * stream)
+{
+  GstStructure *pt_map;
+
+  if (!stream->priv->rtxreceive)
+    goto done;
+
+  pt_map = gst_structure_new_empty ("application/x-rtp-pt-map");
+  g_hash_table_foreach (stream->priv->ptmap, (GHFunc) add_rtx_pt, pt_map);
+  g_object_set (stream->priv->rtxreceive, "payload-type-map", pt_map, NULL);
+  gst_structure_free (pt_map);
+
+done:
+  return;
+}
+
+/**
+ * gst_rtsp_stream_request_aux_receiver:
+ * @stream: a #GstRTSPStream
+ * @sessid: the session id
+ *
+ * Creating a rtxreceive bin
+ *
+ * Returns: (transfer full) (nullable): a #GstElement.
+ *
+ * Since: 1.16
+ */
+GstElement *
+gst_rtsp_stream_request_aux_receiver (GstRTSPStream * stream, guint sessid)
+{
+  GstElement *bin;
+  GstPad *pad;
+  gchar *name;
+
+  g_return_val_if_fail (GST_IS_RTSP_STREAM (stream), NULL);
+
+  g_mutex_lock (&stream->priv->lock);
+
+  bin = gst_bin_new (NULL);
+  stream->priv->rtxreceive = gst_element_factory_make ("rtprtxreceive", NULL);
+  update_rtx_receive_pt_map (stream);
+  gst_bin_add (GST_BIN (bin), gst_object_ref (stream->priv->rtxreceive));
+
+  pad = gst_element_get_static_pad (stream->priv->rtxreceive, "src");
+  name = g_strdup_printf ("src_%u", sessid);
+  gst_element_add_pad (bin, gst_ghost_pad_new (name, pad));
+  g_free (name);
+  gst_object_unref (pad);
+
+  pad = gst_element_get_static_pad (stream->priv->rtxreceive, "sink");
+  name = g_strdup_printf ("sink_%u", sessid);
+  gst_element_add_pad (bin, gst_ghost_pad_new (name, pad));
+  g_free (name);
+  gst_object_unref (pad);
+
+  g_mutex_unlock (&stream->priv->lock);
+
+  return bin;
+}
+
 /**
  * gst_rtsp_stream_set_pt_map:
  * @stream: a #GstRTSPStream
@@ -2346,6 +2424,7 @@ gst_rtsp_stream_set_pt_map (GstRTSPStream * stream, guint pt, GstCaps * caps)
 
   g_mutex_lock (&priv->lock);
   g_hash_table_insert (priv->ptmap, GINT_TO_POINTER (pt), gst_caps_ref (caps));
+  update_rtx_receive_pt_map (stream);
   g_mutex_unlock (&priv->lock);
 }
 
index 9e3ec54..89839f8 100644 (file)
@@ -280,6 +280,9 @@ GST_RTSP_SERVER_API
 GstElement *      gst_rtsp_stream_request_aux_sender         (GstRTSPStream * stream, guint sessid);
 
 GST_RTSP_SERVER_API
+GstElement *      gst_rtsp_stream_request_aux_receiver       (GstRTSPStream * stream, guint sessid);
+
+GST_RTSP_SERVER_API
 gboolean          gst_rtsp_stream_allocate_udp_sockets       (GstRTSPStream * stream, GSocketFamily family,
                                                               GstRTSPTransport *transport, gboolean use_client_settings);