media: add GstNetTimeProvider support
authorWim Taymans <wim.taymans@collabora.co.uk>
Tue, 9 Apr 2013 20:35:28 +0000 (22:35 +0200)
committerWim Taymans <wim.taymans@collabora.co.uk>
Tue, 9 Apr 2013 20:38:44 +0000 (22:38 +0200)
Add a property to let the media provide a GstNetTimeProvider for its clock.
Make methods to get the clock and nettimeprovider
Add a x-gst-clock property to the SDP with the IP and port number of the nettime
provider and also the current time of the clock. This should make it possible
for (GStreamer) clients to slave their clock to the server clock.

gst/rtsp-server/Makefile.am
gst/rtsp-server/rtsp-media.c
gst/rtsp-server/rtsp-media.h
gst/rtsp-server/rtsp-sdp.c

index 008e418..2f9dfc6 100644 (file)
@@ -45,6 +45,7 @@ libgstrtspserver_@GST_API_VERSION@_la_LDFLAGS = $(GST_LIB_LDFLAGS) $(GST_ALL_LDF
 libgstrtspserver_@GST_API_VERSION@_la_LIBADD = \
        $(GST_PLUGINS_BASE_LIBS) $(GST_BASE_LIBS) \
     -lgstrtp-@GST_API_VERSION@ -lgstrtsp-@GST_API_VERSION@ \
+            -lgstnet-@GST_API_VERSION@ \
             -lgstsdp-@GST_API_VERSION@ \
             -lgstapp-@GST_API_VERSION@ \
            $(GST_LIBS) $(GIO_LIBS) $(LIBM)
index 1cce85d..0b0d33c 100644 (file)
@@ -58,6 +58,9 @@ struct _GstRTSPMediaPrivate
   GSource *source;
   guint id;
 
+  gboolean time_provider;
+  GstNetTimeProvider *nettime;
+
   gboolean is_live;
   gboolean seekable;
   gboolean buffering;
@@ -78,6 +81,7 @@ struct _GstRTSPMediaPrivate
 //#define DEFAULT_PROTOCOLS      GST_RTSP_LOWER_TRANS_UDP_MCAST
 #define DEFAULT_EOS_SHUTDOWN    FALSE
 #define DEFAULT_BUFFER_SIZE     0x80000
+#define DEFAULT_TIME_PROVIDER   FALSE
 
 /* define to dump received RTCP packets */
 #undef DUMP_STATS
@@ -91,6 +95,7 @@ enum
   PROP_EOS_SHUTDOWN,
   PROP_BUFFER_SIZE,
   PROP_ELEMENT,
+  PROP_TIME_PROVIDER,
   PROP_LAST
 };
 
@@ -165,6 +170,11 @@ gst_rtsp_media_class_init (GstRTSPMediaClass * klass)
           "The GstBin to use for streaming the media", GST_TYPE_ELEMENT,
           G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE));
 
+  g_object_class_install_property (gobject_class, PROP_EOS_SHUTDOWN,
+      g_param_spec_boolean ("time-provider", "Time Provider",
+          "Use a NetTimeProvider for clients",
+          DEFAULT_TIME_PROVIDER, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+
   gst_rtsp_media_signals[SIGNAL_NEW_STREAM] =
       g_signal_new ("new-stream", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST,
       G_STRUCT_OFFSET (GstRTSPMediaClass, new_stream), NULL, NULL,
@@ -213,6 +223,7 @@ gst_rtsp_media_init (GstRTSPMedia * media)
   priv->protocols = DEFAULT_PROTOCOLS;
   priv->eos_shutdown = DEFAULT_EOS_SHUTDOWN;
   priv->buffer_size = DEFAULT_BUFFER_SIZE;
+  priv->time_provider = DEFAULT_TIME_PROVIDER;
 }
 
 static void
@@ -232,6 +243,8 @@ gst_rtsp_media_finalize (GObject * obj)
 
   if (priv->pipeline)
     gst_object_unref (priv->pipeline);
+  if (priv->nettime)
+    gst_object_unref (priv->nettime);
   gst_object_unref (priv->element);
   if (priv->auth)
     g_object_unref (priv->auth);
@@ -269,6 +282,9 @@ gst_rtsp_media_get_property (GObject * object, guint propid,
     case PROP_BUFFER_SIZE:
       g_value_set_uint (value, gst_rtsp_media_get_buffer_size (media));
       break;
+    case PROP_TIME_PROVIDER:
+      g_value_set_boolean (value, gst_rtsp_media_is_time_provider (media));
+      break;
     default:
       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, propid, pspec);
   }
@@ -299,6 +315,9 @@ gst_rtsp_media_set_property (GObject * object, guint propid,
     case PROP_BUFFER_SIZE:
       gst_rtsp_media_set_buffer_size (media, g_value_get_uint (value));
       break;
+    case PROP_TIME_PROVIDER:
+      gst_rtsp_media_use_time_provider (media, g_value_get_boolean (value));
+      break;
     default:
       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, propid, pspec);
   }
@@ -413,6 +432,7 @@ gst_rtsp_media_take_pipeline (GstRTSPMedia * media, GstPipeline * pipeline)
 {
   GstRTSPMediaPrivate *priv;
   GstElement *old;
+  GstNetTimeProvider *nettime;
 
   g_return_if_fail (GST_IS_RTSP_MEDIA (media));
   g_return_if_fail (GST_IS_PIPELINE (pipeline));
@@ -422,11 +442,16 @@ gst_rtsp_media_take_pipeline (GstRTSPMedia * media, GstPipeline * pipeline)
   g_mutex_lock (&priv->lock);
   old = priv->pipeline;
   priv->pipeline = GST_ELEMENT_CAST (pipeline);
+  nettime = priv->nettime;
+  priv->nettime = NULL;
   g_mutex_unlock (&priv->lock);
 
   if (old)
     gst_object_unref (old);
 
+  if (nettime)
+    gst_object_unref (nettime);
+
   gst_object_ref (priv->element);
   gst_bin_add (GST_BIN_CAST (pipeline), priv->element);
 }
@@ -670,6 +695,53 @@ gst_rtsp_media_get_buffer_size (GstRTSPMedia * media)
 }
 
 /**
+ * gst_rtsp_media_use_time_provider:
+ * @media: a #GstRTSPMedia
+ *
+ * Set @media to provide a GstNetTimeProvider.
+ */
+void
+gst_rtsp_media_use_time_provider (GstRTSPMedia * media, gboolean time_provider)
+{
+  GstRTSPMediaPrivate *priv;
+
+  g_return_if_fail (GST_IS_RTSP_MEDIA (media));
+
+  priv = media->priv;
+
+  g_mutex_lock (&priv->lock);
+  priv->time_provider = time_provider;
+  g_mutex_unlock (&priv->lock);
+}
+
+/**
+ * gst_rtsp_media_is_time_provider:
+ * @media: a #GstRTSPMedia
+ *
+ * Check if @media can provide a #GstNetTimeProvider for its pipeline clock.
+ *
+ * Use gst_rtsp_media_get_time_provider() to get the network clock.
+ *
+ * Returns: %TRUE if @media can provide a #GstNetTimeProvider.
+ */
+gboolean
+gst_rtsp_media_is_time_provider (GstRTSPMedia * media)
+{
+  GstRTSPMediaPrivate *priv;
+  gboolean res;
+
+  g_return_val_if_fail (GST_IS_RTSP_MEDIA (media), FALSE);
+
+  priv = media->priv;
+
+  g_mutex_unlock (&priv->lock);
+  res = priv->time_provider;
+  g_mutex_unlock (&priv->lock);
+
+  return res;
+}
+
+/**
  * gst_rtsp_media_set_auth:
  * @media: a #GstRTSPMedia
  * @auth: a #GstRTSPAuth
@@ -1536,6 +1608,10 @@ finish_unprepare (GstRTSPMedia * media)
   gst_bin_remove (GST_BIN (priv->pipeline), priv->rtpbin);
   priv->rtpbin = NULL;
 
+  if (priv->nettime)
+    gst_object_unref (priv->nettime);
+  priv->nettime = NULL;
+
   gst_object_unref (priv->pipeline);
   priv->pipeline = NULL;
 
@@ -1633,6 +1709,88 @@ is_busy:
   }
 }
 
+/* should be called with state-lock */
+static GstClock *
+get_clock_unlocked (GstRTSPMedia * media)
+{
+  if (media->priv->status != GST_RTSP_MEDIA_STATUS_PREPARED) {
+    GST_DEBUG_OBJECT (media, "media was not prepared");
+    return NULL;
+  }
+  return gst_pipeline_get_clock (GST_PIPELINE_CAST (media->priv->pipeline));
+}
+
+/**
+ * gst_rtsp_media_get_clock:
+ * @media: a #GstRTSPMedia
+ *
+ * Get the clock that is used by the pipeline in @media.
+ *
+ * @media must be prepared before this method returns a valid clock object.
+ *
+ * Returns: the #GstClock used by @media. unref after usage.
+ */
+GstClock *
+gst_rtsp_media_get_clock (GstRTSPMedia * media)
+{
+  GstClock *clock;
+  GstRTSPMediaPrivate *priv;
+
+  g_return_val_if_fail (GST_IS_RTSP_MEDIA (media), NULL);
+
+  priv = media->priv;
+
+  g_rec_mutex_lock (&priv->state_lock);
+  clock = get_clock_unlocked (media);
+  g_rec_mutex_unlock (&priv->state_lock);
+
+  return clock;
+
+}
+
+/**
+ * gst_rtsp_media_get_time_provider:
+ * @media: a #GstRTSPMedia
+ * @address: an address or NULL
+ * @port: a port or 0
+ *
+ * Get the #GstNetTimeProvider for the clock used by @media. The time provider
+ * will listen on @address and @port for client time requests.
+ *
+ * Returns: the #GstNetTimeProvider of @media.
+ */
+GstNetTimeProvider *
+gst_rtsp_media_get_time_provider (GstRTSPMedia * media, const gchar * address,
+    guint16 port)
+{
+  GstRTSPMediaPrivate *priv;
+  GstNetTimeProvider *provider = NULL;
+
+  g_return_val_if_fail (GST_IS_RTSP_MEDIA (media), NULL);
+
+  priv = media->priv;
+
+  g_rec_mutex_lock (&priv->state_lock);
+  if (priv->time_provider) {
+    if ((provider = priv->nettime) == NULL) {
+      GstClock *clock;
+
+      if (priv->time_provider && (clock = get_clock_unlocked (media))) {
+        provider = gst_net_time_provider_new (clock, address, port);
+        gst_object_unref (clock);
+
+        priv->nettime = provider;
+      }
+    }
+  }
+  g_rec_mutex_unlock (&priv->state_lock);
+
+  if (provider)
+    gst_object_ref (provider);
+
+  return provider;
+}
+
 /**
  * gst_rtsp_media_set_state:
  * @media: a #GstRTSPMedia
index f68a899..a103ad2 100644 (file)
@@ -20,6 +20,7 @@
 #include <gst/gst.h>
 #include <gst/rtsp/gstrtsprange.h>
 #include <gst/rtsp/gstrtspurl.h>
+#include <gst/net/gstnet.h>
 
 #ifndef __GST_RTSP_MEDIA_H__
 #define __GST_RTSP_MEDIA_H__
@@ -139,6 +140,10 @@ GstRTSPAddressPool *  gst_rtsp_media_get_address_pool (GstRTSPMedia *media);
 void                  gst_rtsp_media_set_buffer_size  (GstRTSPMedia *media, guint size);
 guint                 gst_rtsp_media_get_buffer_size  (GstRTSPMedia *media);
 
+void                  gst_rtsp_media_use_time_provider (GstRTSPMedia *media, gboolean time_provider);
+gboolean              gst_rtsp_media_is_time_provider  (GstRTSPMedia *media);
+GstNetTimeProvider *  gst_rtsp_media_get_time_provider (GstRTSPMedia *media,
+                                                        const gchar *address, guint16 port);
 
 /* prepare the media for playback */
 gboolean              gst_rtsp_media_prepare          (GstRTSPMedia *media);
@@ -151,6 +156,8 @@ GstRTSPStream *       gst_rtsp_media_create_stream    (GstRTSPMedia *media,
                                                        GstPad *srcpad);
 
 /* dealing with the media */
+GstClock *            gst_rtsp_media_get_clock        (GstRTSPMedia *media);
+
 guint                 gst_rtsp_media_n_streams        (GstRTSPMedia *media);
 GstRTSPStream *       gst_rtsp_media_get_stream       (GstRTSPMedia *media, guint idx);
 
index 5966a4a..7597bb4 100644 (file)
@@ -158,6 +158,30 @@ gst_rtsp_sdp_from_media (GstSDPMessage * sdp, GstSDPInfo * info,
     gst_caps_unref (caps);
   }
 
+  {
+    GstNetTimeProvider *provider;
+
+    if ((provider =
+            gst_rtsp_media_get_time_provider (media, info->server_ip, 0))) {
+      GstClock *clock;
+      gchar *address, *str;
+      gint port;
+
+      g_object_get (provider, "clock", &clock, "address", &address, "port",
+          &port, NULL);
+
+      str = g_strdup_printf ("GstNetTimeProvider %s %s:%d %" G_GUINT64_FORMAT,
+          g_type_name (G_TYPE_FROM_INSTANCE (clock)), address, port,
+          gst_clock_get_time (clock));
+
+      gst_sdp_message_add_attribute (sdp, "x-gst-clock", str);
+      g_free (str);
+      gst_object_unref (clock);
+      g_free (address);
+      gst_object_unref (provider);
+    }
+  }
+
   return TRUE;
 
   /* ERRORS */