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 008e4189da8b292f0c3d96c58e7f4f329f364bad..2f9dfc62afccd3c963fc679173866bc4c1cb62a5 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 1cce85d2fae2ff039f24c2e00fcba7573e92e54e..0b0d33ceca0f73becb434a9ac3edc85fd2e251d6 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);
 }
@@ -669,6 +694,53 @@ gst_rtsp_media_get_buffer_size (GstRTSPMedia * media)
   return res;
 }
 
+/**
+ * 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
@@ -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 f68a899652629c48bd9e5bc17499581b42ef3780..a103ad2741afa93236f8d94268ea9da36a72b24d 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 5966a4aa1102ab6f32a009d425607eca716c552a..7597bb48a30d8039ac2dbae2bfe6cf8006bdbfee 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 */