rtspsrc: add support for NetClientClock
authorWim Taymans <wim.taymans@collabora.co.uk>
Thu, 11 Apr 2013 14:00:05 +0000 (15:00 +0100)
committerWim Taymans <wim.taymans@collabora.co.uk>
Thu, 11 Apr 2013 14:00:05 +0000 (15:00 +0100)
When the server suggests a GstNetTimeProvider in the SDP, set up a
GstNetClientClock that slaves to the remote clock and suggest this clock in
provide_clock.

gst/rtsp/Makefile.am
gst/rtsp/gstrtspsrc.c
gst/rtsp/gstrtspsrc.h

index 5c2307d..f4f43d7 100644 (file)
@@ -4,9 +4,9 @@ libgstrtsp_la_SOURCES = gstrtsp.c gstrtspsrc.c \
                        gstrtpdec.c gstrtspext.c
 
 libgstrtsp_la_CFLAGS = $(GST_PLUGINS_BASE_CFLAGS) $(GST_CFLAGS)
-libgstrtsp_la_LIBADD = $(GST_PLUGINS_BASE_LIBS) $(GST_BASE_LIBS) \
+libgstrtsp_la_LIBADD = $(GST_PLUGINS_BASE_LIBS) $(GST_LIBS) $(GST_BASE_LIBS) \
                       -lgstrtp-@GST_API_VERSION@ -lgstrtsp-@GST_API_VERSION@ \
-                      -lgstsdp-@GST_API_VERSION@ $(GST_LIBS)
+                      -lgstsdp-@GST_API_VERSION@ -lgstnet-@GST_API_VERSION@ $(GST_LIBS)
 libgstrtsp_la_LDFLAGS = $(GST_PLUGIN_LDFLAGS)
 libgstrtsp_la_LIBTOOLFLAGS = --tag=disable-static
 
index 902dc2c..6112693 100644 (file)
@@ -89,6 +89,7 @@
 #include <stdio.h>
 #include <stdarg.h>
 
+#include <gst/net/gstnet.h>
 #include <gst/sdp/gstsdpmessage.h>
 #include <gst/rtp/gstrtppayloads.h>
 
@@ -238,6 +239,8 @@ static void gst_rtspsrc_set_property (GObject * object, guint prop_id,
 static void gst_rtspsrc_get_property (GObject * object, guint prop_id,
     GValue * value, GParamSpec * pspec);
 
+static GstClock *gst_rtspsrc_provide_clock (GstElement * element);
+
 static void gst_rtspsrc_uri_handler_init (gpointer g_iface,
     gpointer iface_data);
 
@@ -534,6 +537,7 @@ gst_rtspsrc_class_init (GstRTSPSrcClass * klass)
           DEFAULT_MULTICAST_IFACE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
 
   gstelement_class->send_event = gst_rtspsrc_send_event;
+  gstelement_class->provide_clock = gst_rtspsrc_provide_clock;
   gstelement_class->change_state = gst_rtspsrc_change_state;
 
   gst_element_class_add_pad_template (gstelement_class,
@@ -618,6 +622,8 @@ gst_rtspsrc_finalize (GObject * object)
     gst_sdp_message_free (rtspsrc->sdp);
     rtspsrc->sdp = NULL;
   }
+  if (rtspsrc->provided_clock)
+    gst_object_unref (rtspsrc->provided_clock);
 
   /* free locks */
   g_rec_mutex_clear (&rtspsrc->stream_rec_lock);
@@ -626,6 +632,18 @@ gst_rtspsrc_finalize (GObject * object)
   G_OBJECT_CLASS (parent_class)->finalize (object);
 }
 
+static GstClock *
+gst_rtspsrc_provide_clock (GstElement * element)
+{
+  GstRTSPSrc *src = GST_RTSPSRC (element);
+  GstClock *clock;
+
+  if ((clock = src->provided_clock) != NULL)
+    gst_object_ref (clock);
+
+  return clock;
+}
+
 /* a proxy string of the format [user:passwd@]host[:port] */
 static gboolean
 gst_rtspsrc_set_proxy (GstRTSPSrc * rtsp, const gchar * proxy)
@@ -1325,6 +1343,10 @@ gst_rtspsrc_cleanup (GstRTSPSrc * src)
     gst_event_unref (src->start_segment);
     src->start_segment = NULL;
   }
+  if (src->provided_clock) {
+    gst_object_unref (src->provided_clock);
+    src->provided_clock = NULL;
+  }
 }
 
 #define PARSE_INT(p, del, res)          \
@@ -5600,6 +5622,66 @@ gst_rtspsrc_parse_range (GstRTSPSrc * src, const gchar * range,
   return TRUE;
 }
 
+/* Parse clock profived by the server with following syntax:
+ *
+ * "GstNetTimeProvider <wrapped-clock> <server-IP:port> <clock-time>"
+ */
+static gboolean
+gst_rtspsrc_parse_gst_clock (GstRTSPSrc * src, const gchar * gstclock)
+{
+  gboolean res = FALSE;
+
+  if (g_str_has_prefix (gstclock, "GstNetTimeProvider ")) {
+    gchar **fields = NULL, **parts = NULL;
+    gchar *remote_ip, *str;
+    gint port;
+    GstClockTime base_time;
+    GstClock *netclock;
+
+    fields = g_strsplit (gstclock, " ", 0);
+
+    /* wrapped clock, not very interesting for now */
+    if (fields[1] == NULL)
+      goto cleanup;
+
+    /* remote IP address and port */
+    if ((str = fields[2]) == NULL)
+      goto cleanup;
+
+    parts = g_strsplit (str, ":", 0);
+
+    if ((remote_ip = parts[0]) == NULL)
+      goto cleanup;
+
+    if ((str = parts[1]) == NULL)
+      goto cleanup;
+
+    port = atoi (str);
+    if (port == 0)
+      goto cleanup;
+
+    /* base-time */
+    if ((str = fields[3]) == NULL)
+      goto cleanup;
+
+    base_time = g_ascii_strtoull (str, NULL, 10);
+
+    netclock =
+        gst_net_client_clock_new ((gchar *) "GstRTSPClock", remote_ip, port,
+        base_time);
+
+    if (src->provided_clock)
+      gst_object_unref (src->provided_clock);
+    src->provided_clock = netclock;
+
+    res = TRUE;
+  cleanup:
+    g_strfreev (fields);
+    g_strfreev (parts);
+  }
+  return res;
+}
+
 /* must be called with the RTSP state lock */
 static GstRTSPResult
 gst_rtspsrc_open_from_sdp (GstRTSPSrc * src, GstSDPMessage * sdp,
@@ -5635,6 +5717,22 @@ gst_rtspsrc_open_from_sdp (GstRTSPSrc * src, GstSDPMessage * sdp,
         break;
     }
   }
+  /* parse clock information. This is GStreamer specific, a server can tell the
+   * client what clock it is using and wrap that in a network clock. The
+   * advantage of that is that we can slave to it. */
+  {
+    const gchar *gstclock;
+
+    for (i = 0;; i++) {
+      gstclock = gst_sdp_message_get_attribute_val_n (sdp, "x-gst-clock", i);
+      if (gstclock == NULL)
+        break;
+
+      /* parse the clock and expose it in the provide_clock method */
+      if (gst_rtspsrc_parse_gst_clock (src, gstclock))
+        break;
+    }
+  }
   /* try to find a global control attribute. Note that a '*' means that we should
    * do aggregate control with the current url (so we don't do anything and
    * leave the current connection as is) */
index 42065f9..31d1361 100644 (file)
@@ -233,6 +233,7 @@ struct _GstRTSPSrc {
   GstRTSPTimeRange  *range;
   gchar             *control;
   guint              next_port_num;
+  GstClock          *provided_clock;
 
   /* supported methods */
   gint               methods;