RTSP: add support for Quicktime tunneled RTSP
authorWim Taymans <wim.taymans@collabora.co.uk>
Mon, 2 Mar 2009 15:03:49 +0000 (16:03 +0100)
committerWim Taymans <wim.taymans@collabora.co.uk>
Mon, 2 Mar 2009 15:03:49 +0000 (16:03 +0100)
Add support for tunneling RTSP over HTTP.
Fix documentation some more.
See also #573173.

API: RTSP:gst_rtsp_connection_is_tunneled()
API: RTSP:gst_rtsp_connection_set_tunneled()

docs/libs/gst-plugins-base-libs-sections.txt
gst-libs/gst/rtsp/gstrtspconnection.c
gst-libs/gst/rtsp/gstrtspconnection.h
win32/common/libgstrtsp.def

index 6c0b750..56fedfa 100644 (file)
@@ -1210,14 +1210,30 @@ gst_rtsp_connection_accept
 gst_rtsp_connection_connect
 gst_rtsp_connection_close
 gst_rtsp_connection_free
+
 gst_rtsp_connection_read
 gst_rtsp_connection_write
+gst_rtsp_connection_poll
+
 gst_rtsp_connection_send
 gst_rtsp_connection_receive
+
 gst_rtsp_connection_next_timeout
 gst_rtsp_connection_reset_timeout
+
 gst_rtsp_connection_flush
+
 gst_rtsp_connection_set_auth
+gst_rtsp_connection_set_auth_param
+gst_rtsp_connection_clear_auth_params
+
+gst_rtsp_connection_set_qos_dscp
+
+gst_rtsp_connection_get_ip
+gst_rtsp_connection_get_url
+
+gst_rtsp_connection_set_tunneled
+gst_rtsp_connection_is_tunneled
 
 GstRTSPWatch
 GstRTSPWatchFuncs
index b181eaf..70dc9ac 100644 (file)
 #include "gstrtspbase64.h"
 #include "md5.h"
 
+static GstRTSPResult read_line (gint fd, guint8 * buffer, guint * idx,
+    guint size);
+static GstRTSPResult parse_key_value (guint8 * buffer, gchar * key,
+    guint keysize, gchar ** value);
+static void parse_string (gchar * dest, gint size, gchar ** src);
+
 #ifdef G_OS_WIN32
 #define READ_SOCKET(fd, buf, len) recv (fd, (char *)buf, len, 0)
 #define WRITE_SOCKET(fd, buf, len) send (fd, (const char *)buf, len, 0)
 #define ERRNO_IS_EINPROGRESS (errno == EINPROGRESS)
 #endif
 
-#define ADD_POLLFD(fdset, pfd, fd)       \
-G_STMT_START {                           \
-  pfd.fd = fd;                           \
-  gst_poll_add_fd (fdset, &pfd);         \
+#define ADD_POLLFD(fdset, pfd, fd)        \
+G_STMT_START {                            \
+  (pfd)->fd = fd;                         \
+  gst_poll_add_fd (fdset, pfd);           \
 } G_STMT_END
 
-#define REMOVE_POLLFD(fdset, pfd)        \
-G_STMT_START {                           \
-  if (pfd.fd != -1) {                    \
-    GST_DEBUG ("remove fd %d", pfd.fd);  \
-    gst_poll_remove_fd (fdset, &pfd);    \
-    CLOSE_SOCKET (pfd.fd);               \
-    pfd.fd = -1;                         \
-  }                                      \
+#define REMOVE_POLLFD(fdset, pfd)          \
+G_STMT_START {                             \
+  if ((pfd)->fd != -1) {                   \
+    GST_DEBUG ("remove fd %d", (pfd)->fd); \
+    gst_poll_remove_fd (fdset, pfd);       \
+    CLOSE_SOCKET ((pfd)->fd);              \
+    (pfd)->fd = -1;                        \
+  }                                        \
 } G_STMT_END
 
 struct _GstRTSPConnection
@@ -144,6 +150,8 @@ struct _GstRTSPConnection
   GstPollFD *readfd;
   GstPollFD *writefd;
 
+  gboolean tunneled;
+
   GstPoll *fdset;
   gchar *ip;
 
@@ -247,6 +255,7 @@ gst_rtsp_connection_create (GstRTSPUrl * url, GstRTSPConnection ** conn)
   newconn->fd1.fd = -1;
   newconn->timer = g_timer_new ();
   newconn->timeout = 60;
+  newconn->tunneled = FALSE;
 
   newconn->auth_method = GST_RTSP_AUTH_NONE;
   newconn->username = NULL;
@@ -331,7 +340,7 @@ gst_rtsp_connection_accept (gint sock, GstRTSPConnection ** conn)
 
   /* now create the connection object */
   gst_rtsp_connection_create (url, &newconn);
-  ADD_POLLFD (newconn->fdset, newconn->fd0, fd);
+  ADD_POLLFD (newconn->fdset, &newconn->fd0, fd);
 
   newconn->readfd = &newconn->fd0;
   newconn->writefd = &newconn->fd0;
@@ -347,53 +356,26 @@ accept_failed:
   }
 }
 
-/**
- * gst_rtsp_connection_connect:
- * @conn: a #GstRTSPConnection 
- * @timeout: a #GTimeVal timeout
- *
- * Attempt to connect to the url of @conn made with
- * gst_rtsp_connection_create(). If @timeout is #NULL this function can block
- * forever. If @timeout contains a valid timeout, this function will return
- * #GST_RTSP_ETIMEOUT after the timeout expired.
- *
- * This function can be cancelled with gst_rtsp_connection_flush().
- *
- * Returns: #GST_RTSP_OK when a connection could be made.
- */
-GstRTSPResult
-gst_rtsp_connection_connect (GstRTSPConnection * conn, GTimeVal * timeout)
+static const gchar *
+do_resolve (const gchar * host)
 {
-  gint fd;
-  struct sockaddr_in sa_in;
   struct hostent *hostinfo;
-  const gchar *ip;
   struct in_addr addr;
-  gint ret;
-  guint16 port;
-  GstRTSPUrl *url;
-  GstClockTime to;
-  gint retval;
-
+  const gchar *ip;
 #ifdef G_OS_WIN32
-  unsigned long flags = 1;
   struct in_addr *addrp;
 #else
   char **addrs;
   gchar ipbuf[INET_ADDRSTRLEN];
 #endif /* G_OS_WIN32 */
 
-  g_return_val_if_fail (conn != NULL, GST_RTSP_EINVAL);
-  g_return_val_if_fail (conn->url != NULL, GST_RTSP_EINVAL);
-  g_return_val_if_fail (conn->fd0.fd < 0, GST_RTSP_EINVAL);
-
-  url = conn->url;
+  ip = NULL;
 
   /* first check if it already is an IP address */
-  if (inet_aton (url->host, &addr)) {
-    ip = url->host;
+  if (inet_aton (host, &addr)) {
+    ip = host;
   } else {
-    hostinfo = gethostbyname (url->host);
+    hostinfo = gethostbyname (host);
     if (!hostinfo)
       goto not_resolved;        /* h_errno set */
 
@@ -409,9 +391,35 @@ gst_rtsp_connection_connect (GstRTSPConnection * conn, GTimeVal * timeout)
         sizeof (ipbuf));
 #endif /* G_OS_WIN32 */
   }
+  return ip;
 
-  /* get the port from the url */
-  gst_rtsp_url_get_port (url, &port);
+  /* ERRORS */
+not_resolved:
+  {
+    GST_ERROR ("could not resolve %s", host);
+    return NULL;
+  }
+not_ip:
+  {
+    GST_ERROR ("not an IP address");
+    return NULL;
+  }
+}
+
+static GstRTSPResult
+do_connect (const gchar * ip, guint16 port, GstPollFD * fdout,
+    GstPoll * fdset, GTimeVal * timeout)
+{
+  gint fd;
+  struct sockaddr_in sa_in;
+  gint ret;
+#ifdef G_OS_WIN32
+  unsigned long flags = 1;
+#endif /* G_OS_WIN32 */
+  GstClockTime to;
+  gint retval;
+
+  g_message ("connect %s:%u", ip, port);
 
   memset (&sa_in, 0, sizeof (sa_in));
   sa_in.sin_family = AF_INET;   /* network socket */
@@ -420,7 +428,7 @@ gst_rtsp_connection_connect (GstRTSPConnection * conn, GTimeVal * timeout)
 
   fd = socket (AF_INET, SOCK_STREAM, 0);
   if (fd == -1)
-    goto sys_error;
+    goto no_socket;
 
   /* set to non-blocking mode so that we can cancel the connect */
 #ifndef G_OS_WIN32
@@ -430,10 +438,7 @@ gst_rtsp_connection_connect (GstRTSPConnection * conn, GTimeVal * timeout)
 #endif /* G_OS_WIN32 */
 
   /* add the socket to our fdset */
-  ADD_POLLFD (conn->fdset, conn->fd0, fd);
-
-  conn->readfd = &conn->fd0;
-  conn->writefd = &conn->fd0;
+  ADD_POLLFD (fdset, fdout, fd);
 
   /* we are going to connect ASYNC now */
   ret = connect (fd, (struct sockaddr *) &sa_in, sizeof (sa_in));
@@ -444,12 +449,12 @@ gst_rtsp_connection_connect (GstRTSPConnection * conn, GTimeVal * timeout)
 
   /* wait for connect to complete up to the specified timeout or until we got
    * interrupted. */
-  gst_poll_fd_ctl_write (conn->fdset, conn->writefd, TRUE);
+  gst_poll_fd_ctl_write (fdset, fdout, TRUE);
 
   to = timeout ? GST_TIMEVAL_TO_TIME (*timeout) : GST_CLOCK_TIME_NONE;
 
   do {
-    retval = gst_poll_wait (conn->fdset, to);
+    retval = gst_poll_wait (fdset, to);
   } while (retval == -1 && (errno == EINTR || errno == EAGAIN));
 
   if (retval == 0)
@@ -458,7 +463,7 @@ gst_rtsp_connection_connect (GstRTSPConnection * conn, GTimeVal * timeout)
     goto sys_error;
 
   /* we can still have an error connecting on windows */
-  if (gst_poll_fd_has_error (conn->fdset, conn->writefd)) {
+  if (gst_poll_fd_has_error (fdset, fdout)) {
     socklen_t len = sizeof (errno);
 #ifndef G_OS_WIN32
     getsockopt (fd, SOL_SOCKET, SO_ERROR, &errno, &len);
@@ -468,37 +473,286 @@ gst_rtsp_connection_connect (GstRTSPConnection * conn, GTimeVal * timeout)
     goto sys_error;
   }
 
-  gst_poll_fd_ignored (conn->fdset, conn->writefd);
-  gst_poll_fd_ignored (conn->fdset, conn->readfd);
-
+  gst_poll_fd_ignored (fdset, fdout);
 done:
-  conn->ip = g_strdup (ip);
 
   return GST_RTSP_OK;
 
+  /* ERRORS */
+no_socket:
+  {
+    GST_ERROR ("no socket %d (%s)", errno, g_strerror (errno));
+    return GST_RTSP_ESYS;
+  }
 sys_error:
   {
     GST_ERROR ("system error %d (%s)", errno, g_strerror (errno));
-    REMOVE_POLLFD (conn->fdset, conn->fd0);
-    REMOVE_POLLFD (conn->fdset, conn->fd1);
+    REMOVE_POLLFD (fdset, fdout);
     return GST_RTSP_ESYS;
   }
+timeout:
+  {
+    GST_ERROR ("timeout");
+    REMOVE_POLLFD (fdset, fdout);
+    return GST_RTSP_ETIMEOUT;
+  }
+}
+
+static GstRTSPResult
+setup_tunneling (GstRTSPConnection * conn, GTimeVal * timeout)
+{
+  gchar sessionid[24];
+  gint i;
+  GstRTSPResult res;
+  gchar *str;
+  guint idx, line;
+  gint retval;
+  GstClockTime to;
+  const gchar *ip;
+  guint16 port;
+  gchar codestr[4];
+  gint code;
+
+  /* create a random sessionid */
+  for (i = 0; i < 24; i++)
+    sessionid[i] = g_random_int_range ('a', 'z');
+  sessionid[23] = '\0';
+
+  /* */
+  str = g_strdup_printf ("GET %s HTTP/1.0\r\n"
+      "x-sessioncookie: %s\r\n"
+      "Accept: application/x-rtsp-tunnelled\r\n"
+      "Pragma: no-cache\r\n"
+      "Cache-Control: no-cache\r\n" "\r\n", conn->url->host, sessionid);
+
+  /* we start by writing to this fd */
+  conn->writefd = &conn->fd0;
+
+  res = gst_rtsp_connection_write (conn, (guint8 *) str, strlen (str), timeout);
+  g_free (str);
+  if (res != GST_RTSP_OK)
+    goto write_failed;
+
+  gst_poll_fd_ctl_write (conn->fdset, &conn->fd0, FALSE);
+  gst_poll_fd_ctl_read (conn->fdset, &conn->fd0, TRUE);
+
+  to = timeout ? GST_TIMEVAL_TO_TIME (*timeout) : GST_CLOCK_TIME_NONE;
+
+  line = 0;
+  while (TRUE) {
+    guint8 buffer[4096];
+
+    idx = 0;
+    while (TRUE) {
+      res = read_line (conn->fd0.fd, buffer, &idx, sizeof (buffer));
+      if (res == GST_RTSP_EEOF)
+        goto eof;
+      if (res == GST_RTSP_OK)
+        break;
+      if (res != GST_RTSP_EINTR)
+        goto read_error;
+
+      do {
+        retval = gst_poll_wait (conn->fdset, to);
+      } while (retval == -1 && (errno == EINTR || errno == EAGAIN));
+
+      /* check for timeout */
+      if (retval == 0)
+        goto timeout;
+
+      if (retval == -1) {
+        if (errno == EBUSY)
+          goto stopped;
+        else
+          goto select_error;
+      }
+    }
+
+    /* check for last line */
+    if (buffer[0] == '\r')
+      buffer[0] = '\0';
+    if (buffer[0] == '\0')
+      break;
+
+    if (line == 0) {
+      /* first line, parse response */
+      gchar versionstr[20];
+      gchar *bptr;
+
+      bptr = (gchar *) buffer;
+
+      parse_string (versionstr, sizeof (versionstr), &bptr);
+      parse_string (codestr, sizeof (codestr), &bptr);
+      code = atoi (codestr);
+
+      if (code != 200)
+        goto wrong_result;
+    } else {
+      gchar key[32];
+      gchar *value;
+
+      /* other lines, parse key/value */
+      res = parse_key_value (buffer, key, sizeof (key), &value);
+      if (res == GST_RTSP_OK) {
+        /* we got a new ip address */
+        if (g_ascii_strcasecmp (key, "x-server-ip-address") == 0) {
+          g_free (conn->ip);
+          conn->ip = g_strdup (value);
+        }
+      }
+    }
+    line++;
+  }
+
+  if (!(ip = do_resolve (conn->ip)))
+    goto not_resolved;
+
+  /* get the port from the url */
+  gst_rtsp_url_get_port (conn->url, &port);
+
+  /* connect to the host/port */
+  res = do_connect (ip, port, &conn->fd1, conn->fdset, timeout);
+  if (res != GST_RTSP_OK)
+    goto connect_failed;
+
+  /* this is now our writing socket */
+  conn->writefd = &conn->fd1;
+
+  /* */
+  str = g_strdup_printf ("POST %s HTTP/1.0\r\n"
+      "x-sessioncookie: %s\r\n"
+      "Content-Type: application/x-rtsp-tunnelled\r\n"
+      "Pragma: no-cache\r\n"
+      "Cache-Control: no-cache\r\n"
+      "Content-Length: 32767\r\n"
+      "Expires: Sun, 9 Jan 1972 00:00:00 GMT\r\n"
+      "\r\n", conn->url->host, sessionid);
+
+  /* we start by writing to this fd */
+  conn->writefd = &conn->fd1;
+
+  res = gst_rtsp_connection_write (conn, (guint8 *) str, strlen (str), timeout);
+  g_free (str);
+  if (res != GST_RTSP_OK)
+    goto write_failed;
+
+  return res;
+
+  /* ERRORS */
+write_failed:
+  {
+    GST_ERROR ("write failed", res);
+    return res;
+  }
+eof:
+  {
+    return GST_RTSP_EEOF;
+  }
+read_error:
+  {
+    return res;
+  }
+timeout:
+  {
+    return GST_RTSP_ETIMEOUT;
+  }
+select_error:
+  {
+    return GST_RTSP_ESYS;
+  }
+stopped:
+  {
+    return GST_RTSP_EINTR;
+  }
+wrong_result:
+  {
+    GST_ERROR ("got failure response %d %s", code, codestr);
+    return GST_RTSP_ERROR;
+  }
+not_resolved:
+  {
+    GST_ERROR ("could not resolve %s", conn->ip);
+    return GST_RTSP_ENET;
+  }
+connect_failed:
+  {
+    GST_ERROR ("failed to connect");
+    return res;
+  }
+}
+
+/**
+ * gst_rtsp_connection_connect:
+ * @conn: a #GstRTSPConnection 
+ * @timeout: a #GTimeVal timeout
+ *
+ * Attempt to connect to the url of @conn made with
+ * gst_rtsp_connection_create(). If @timeout is #NULL this function can block
+ * forever. If @timeout contains a valid timeout, this function will return
+ * #GST_RTSP_ETIMEOUT after the timeout expired.
+ *
+ * This function can be cancelled with gst_rtsp_connection_flush().
+ *
+ * Returns: #GST_RTSP_OK when a connection could be made.
+ */
+GstRTSPResult
+gst_rtsp_connection_connect (GstRTSPConnection * conn, GTimeVal * timeout)
+{
+  GstRTSPResult res;
+  const gchar *ip;
+  guint16 port;
+  GstRTSPUrl *url;
+#ifdef G_OS_WIN32
+  unsigned long flags = 1;
+#endif /* G_OS_WIN32 */
+
+  g_return_val_if_fail (conn != NULL, GST_RTSP_EINVAL);
+  g_return_val_if_fail (conn->url != NULL, GST_RTSP_EINVAL);
+  g_return_val_if_fail (conn->fd0.fd < 0, GST_RTSP_EINVAL);
+
+  url = conn->url;
+
+  if (!(ip = do_resolve (url->host)))
+    goto not_resolved;
+
+  /* get the port from the url */
+  gst_rtsp_url_get_port (url, &port);
+
+  /* connect to the host/port */
+  res = do_connect (ip, port, &conn->fd0, conn->fdset, timeout);
+  if (res != GST_RTSP_OK)
+    goto connect_failed;
+
+  g_free (conn->ip);
+  conn->ip = g_strdup (ip);
+
+  /* this is our read URL */
+  conn->readfd = &conn->fd0;
+
+  if (conn->tunneled) {
+    res = setup_tunneling (conn, timeout);
+    if (res != GST_RTSP_OK)
+      goto tunneling_failed;
+  } else {
+    conn->writefd = &conn->fd0;
+  }
+
+  return GST_RTSP_OK;
+
 not_resolved:
   {
     GST_ERROR ("could not resolve %s", url->host);
     return GST_RTSP_ENET;
   }
-not_ip:
+connect_failed:
   {
-    GST_ERROR ("not an IP address");
-    return GST_RTSP_ENOTIP;
+    GST_ERROR ("failed to connect");
+    return res;
   }
-timeout:
+tunneling_failed:
   {
-    GST_ERROR ("timeout");
-    REMOVE_POLLFD (conn->fdset, conn->fd0);
-    REMOVE_POLLFD (conn->fdset, conn->fd1);
-    return GST_RTSP_ETIMEOUT;
+    GST_ERROR ("failed to setup tunneling");
+    return res;
   }
 }
 
@@ -922,20 +1176,31 @@ GstRTSPResult
 gst_rtsp_connection_send (GstRTSPConnection * conn, GstRTSPMessage * message,
     GTimeVal * timeout)
 {
-  GString *str = NULL;
+  GString *string = NULL;
   GstRTSPResult res;
+  gchar *str;
+  gsize len;
 
   g_return_val_if_fail (conn != NULL, GST_RTSP_EINVAL);
   g_return_val_if_fail (message != NULL, GST_RTSP_EINVAL);
 
-  if (!(str = message_to_string (conn, message)))
+  if (!(string = message_to_string (conn, message)))
     goto no_message;
 
+  if (conn->tunneled) {
+    str = g_base64_encode ((const guchar *) string->str, string->len);
+    g_string_free (string, TRUE);
+    len = strlen (str);
+  } else {
+    str = string->str;
+    len = string->len;
+    g_string_free (string, FALSE);
+  }
+
   /* write request */
-  res =
-      gst_rtsp_connection_write (conn, (guint8 *) str->str, str->len, timeout);
+  res = gst_rtsp_connection_write (conn, (guint8 *) str, len, timeout);
 
-  g_string_free (str, TRUE);
+  g_free (str);
 
   return res;
 
@@ -1056,38 +1321,59 @@ parse_request_line (guint8 * buffer, GstRTSPMessage * msg)
   return res;
 }
 
-/* parsing lines means reading a Key: Value pair */
 static GstRTSPResult
-parse_line (guint8 * buffer, GstRTSPMessage * msg)
+parse_key_value (guint8 * buffer, gchar * key, guint keysize, gchar ** value)
 {
-  gchar key[32];
   gchar *bptr;
-  GstRTSPHeaderField field;
 
   bptr = (gchar *) buffer;
 
   /* read key */
-  parse_key (key, sizeof (key), &bptr);
+  parse_key (key, keysize, &bptr);
   if (*bptr != ':')
     goto no_column;
 
   bptr++;
+  while (g_ascii_isspace (*bptr))
+    bptr++;
 
-  field = gst_rtsp_find_header_field (key);
-  if (field != GST_RTSP_HDR_INVALID) {
-    while (g_ascii_isspace (*bptr))
-      bptr++;
-    gst_rtsp_message_add_header (msg, field, bptr);
-  }
+  *value = bptr;
 
   return GST_RTSP_OK;
 
+  /* ERRORS */
 no_column:
   {
     return GST_RTSP_EPARSE;
   }
 }
 
+/* parsing lines means reading a Key: Value pair */
+static GstRTSPResult
+parse_line (guint8 * buffer, GstRTSPMessage * msg)
+{
+  GstRTSPResult res;
+  gchar key[32];
+  gchar *value;
+  GstRTSPHeaderField field;
+
+  res = parse_key_value (buffer, key, sizeof (key), &value);
+  if (res != GST_RTSP_OK)
+    goto parse_error;
+
+  field = gst_rtsp_find_header_field (key);
+  if (field != GST_RTSP_HDR_INVALID)
+    gst_rtsp_message_add_header (msg, field, value);
+
+  return GST_RTSP_OK;
+
+  /* ERRORS */
+parse_error:
+  {
+    return res;
+  }
+}
+
 /* returns:
  *  GST_RTSP_OK when a complete message was read.
  *  GST_RTSP_EEOF: when the socket is closed
@@ -1456,8 +1742,8 @@ gst_rtsp_connection_close (GstRTSPConnection * conn)
   g_free (conn->ip);
   conn->ip = NULL;
 
-  REMOVE_POLLFD (conn->fdset, conn->fd0);
-  REMOVE_POLLFD (conn->fdset, conn->fd1);
+  REMOVE_POLLFD (conn->fdset, &conn->fd0);
+  REMOVE_POLLFD (conn->fdset, &conn->fd1);
   conn->writefd = NULL;
   conn->readfd = NULL;
 
@@ -1902,6 +2188,45 @@ gst_rtsp_connection_get_ip (const GstRTSPConnection * conn)
   return conn->ip;
 }
 
+/**
+ * gst_rtsp_connection_set_tunneled:
+ * @conn: a #GstRTSPConnection
+ * @tunneled: the new state
+ *
+ * Set the HTTP tunneling state of the connection. This must be configured before
+ * the @conn is connected.
+ *
+ * Since: 0.10.23
+ */
+void
+gst_rtsp_connection_set_tunneled (GstRTSPConnection * conn, gboolean tunneled)
+{
+  g_return_if_fail (conn != NULL);
+  g_return_if_fail (conn->readfd == NULL);
+  g_return_if_fail (conn->writefd == NULL);
+
+  conn->tunneled = tunneled;
+}
+
+/**
+ * gst_rtsp_connection_is_tunneled:
+ * @conn: a #GstRTSPConnection
+ *
+ * Get the tunneling state of the connection. 
+ *
+ * Returns: if @conn is using HTTP tunneling.
+ *
+ * Since: 0.10.23
+ */
+gboolean
+gst_rtsp_connection_is_tunneled (GstRTSPConnection * conn)
+{
+  g_return_val_if_fail (conn != NULL, FALSE);
+
+  return conn->tunneled;
+}
+
+
 #define READ_COND   (G_IO_IN | G_IO_HUP | G_IO_ERR)
 #define WRITE_COND  (G_IO_OUT | G_IO_ERR)
 
@@ -2080,6 +2405,7 @@ static GSourceFuncs gst_rtsp_source_funcs = {
  * @conn: a #GstRTSPConnection
  * @funcs: watch functions
  * @user_data: user data to pass to @funcs
+ * @notify: notify when @user_data is not referenced anymore
  *
  * Create a watch object for @conn. The functions provided in @funcs will be
  * called with @user_data when activity happened on the watch.
@@ -2151,7 +2477,7 @@ gst_rtsp_watch_attach (GstRTSPWatch * watch, GMainContext * context)
 }
 
 /**
- * gst_rtsp_watch_free:
+ * gst_rtsp_watch_unref:
  * @watch: a #GstRTSPWatch
  *
  * Decreases the reference count of @watch by one. If the resulting reference
index 92e4df2..33e583b 100644 (file)
@@ -65,6 +65,7 @@ GstRTSPResult      gst_rtsp_connection_connect       (GstRTSPConnection *conn, G
 GstRTSPResult      gst_rtsp_connection_close         (GstRTSPConnection *conn);
 GstRTSPResult      gst_rtsp_connection_free          (GstRTSPConnection *conn);
 
+
 /* sending/receiving raw bytes */
 GstRTSPResult      gst_rtsp_connection_read          (GstRTSPConnection * conn, guint8 * data,
                                                       guint size, GTimeVal * timeout);
@@ -105,6 +106,9 @@ GstRTSPResult      gst_rtsp_connection_set_qos_dscp  (GstRTSPConnection *conn,
 GstRTSPUrl *       gst_rtsp_connection_get_url       (const GstRTSPConnection *conn);
 const gchar *      gst_rtsp_connection_get_ip        (const GstRTSPConnection *conn);
 
+void               gst_rtsp_connection_set_tunneled  (GstRTSPConnection *conn, gboolean tunneled);
+gboolean           gst_rtsp_connection_is_tunneled   (GstRTSPConnection *conn);
+
 /* async IO */
 
 /**
index 05c466b..6bcc2a4 100644 (file)
@@ -11,6 +11,7 @@ EXPORTS
        gst_rtsp_connection_free
        gst_rtsp_connection_get_ip
        gst_rtsp_connection_get_url
+       gst_rtsp_connection_is_tunneled
        gst_rtsp_connection_next_timeout
        gst_rtsp_connection_poll
        gst_rtsp_connection_read
@@ -20,6 +21,7 @@ EXPORTS
        gst_rtsp_connection_set_auth
        gst_rtsp_connection_set_auth_param
        gst_rtsp_connection_set_qos_dscp
+       gst_rtsp_connection_set_tunneled
        gst_rtsp_connection_write
        gst_rtsp_event_get_type
        gst_rtsp_extension_after_send