? grammar.y
? parse.l
? rtsp.diff
? types.h
Index: gstrtsp.c
===================================================================
RCS file: /cvs/gstreamer/gst-plugins-good/gst/rtsp/gstrtsp.c,v
retrieving revision 1.4
diff -u -3 -p -b -B -d -r1.4 gstrtsp.c
--- gstrtsp.c	9 Feb 2006 14:20:14 -0000	1.4
+++ gstrtsp.c	31 Aug 2006 20:40:06 -0000
@@ -21,7 +21,6 @@
 #include "config.h"
 #endif
 
-#include "gstrtpdec.h"
 #include "gstrtspsrc.h"
 
 static gboolean
@@ -30,8 +29,6 @@ plugin_init (GstPlugin * plugin)
   if (!gst_element_register (plugin, "rtspsrc", GST_RANK_NONE,
           GST_TYPE_RTSPSRC))
     return FALSE;
-  if (!gst_element_register (plugin, "rtpdec", GST_RANK_NONE, GST_TYPE_RTPDEC))
-    return FALSE;
 
   return TRUE;
 }
Index: gstrtspsrc.c
===================================================================
RCS file: /cvs/gstreamer/gst-plugins-good/gst/rtsp/gstrtspsrc.c,v
retrieving revision 1.32
diff -u -3 -p -b -B -d -r1.32 gstrtspsrc.c
--- gstrtspsrc.c	22 Aug 2006 17:20:41 -0000	1.32
+++ gstrtspsrc.c	31 Aug 2006 20:40:07 -0000
@@ -22,8 +22,9 @@
  * <refsect2>
  * <para>
  * Makes a connection to an RTSP server and read the data.
- * rtspsrc strictly follows RFC 2326 and therefore does not (yet) support
- * RealMedia/Quicktime/Microsoft extensions.
+ * rtspsrc follows RFC 2326 as much as possible. However, if rtspsrc encounters
+ * a server with proprietary extensions (i.e. RealMedia), rtspsrc will try
+ * to handle these even if they are against specifications.
  * </para>
  * <para>
  * RTSP supports transport over TCP or UDP in unicast or multicast mode. By
@@ -36,7 +37,8 @@
  * For each stream listed in the SDP a new rtp_stream%d pad will be created
  * with caps derived from the SDP media description. This is a caps of mime type
  * "application/x-rtp" that can be connected to any available rtp depayloader
- * element. 
+ * element. For REAL streams, a pad will be created with caps of mime type
+ * "application/vnd.rn-realmedia".
  * </para>
  * <para>
  * rtspsrc will internally instantiate an RTP session manager element
@@ -92,6 +94,12 @@ GST_STATIC_PAD_TEMPLATE ("rtcp_stream%d"
     GST_PAD_SOMETIMES,
     GST_STATIC_CAPS_ANY);
 
+static GstStaticPadTemplate realtemplate =
+GST_STATIC_PAD_TEMPLATE ("real",
+    GST_PAD_SRC,
+    GST_PAD_SOMETIMES,
+    GST_STATIC_CAPS ("application/vnd.rn-realmedia"));
+
 enum
 {
   /* FILL ME */
@@ -131,14 +139,13 @@ gst_rtsp_proto_get_type (void)
   return rtsp_proto_type;
 }
 
-
 static void gst_rtspsrc_base_init (gpointer g_class);
-static void gst_rtspsrc_class_init (GstRTSPSrc * klass);
-static void gst_rtspsrc_init (GstRTSPSrc * rtspsrc);
 static void gst_rtspsrc_finalize (GObject * object);
 
 static void gst_rtspsrc_uri_handler_init (gpointer g_iface,
     gpointer iface_data);
+static gboolean gst_rtspsrc_uri_set_uri (GstURIHandler * handler,
+    const gchar * uri);
 
 static GstStateChangeReturn gst_rtspsrc_change_state (GstElement * element,
     GstStateChange transition);
@@ -148,30 +155,15 @@ static void gst_rtspsrc_set_property (GO
 static void gst_rtspsrc_get_property (GObject * object, guint prop_id,
     GValue * value, GParamSpec * pspec);
 
-static void gst_rtspsrc_loop (GstRTSPSrc * src);
-
-static GstElementClass *parent_class = NULL;
+static void gst_rtspsrc_loop (gpointer data);
+static void gst_rtspsrc_loop_real (gpointer data);
+static gboolean gst_rtspsrc_open_real (GstRTSPSrc * src);
 
 /*static guint gst_rtspsrc_signals[LAST_SIGNAL] = { 0 }; */
 
-GType
-gst_rtspsrc_get_type (void)
+static void
+_do_init (GType rtspsrc_type)
 {
-  static GType rtspsrc_type = 0;
-
-  if (!rtspsrc_type) {
-    static const GTypeInfo rtspsrc_info = {
-      sizeof (GstRTSPSrcClass),
-      gst_rtspsrc_base_init,
-      NULL,
-      (GClassInitFunc) gst_rtspsrc_class_init,
-      NULL,
-      NULL,
-      sizeof (GstRTSPSrc),
-      0,
-      (GInstanceInitFunc) gst_rtspsrc_init,
-      NULL
-    };
     static const GInterfaceInfo urihandler_info = {
       gst_rtspsrc_uri_handler_init,
       NULL,
@@ -180,16 +172,13 @@ gst_rtspsrc_get_type (void)
 
     GST_DEBUG_CATEGORY_INIT (rtspsrc_debug, "rtspsrc", 0, "RTSP src");
 
-    rtspsrc_type =
-        g_type_register_static (GST_TYPE_ELEMENT, "GstRTSPSrc", &rtspsrc_info,
-        0);
-
     g_type_add_interface_static (rtspsrc_type, GST_TYPE_URI_HANDLER,
         &urihandler_info);
-  }
-  return rtspsrc_type;
 }
 
+GST_BOILERPLATE_FULL (GstRTSPSrc, gst_rtspsrc, GstBin, GST_TYPE_BIN,
+    _do_init);
+
 static void
 gst_rtspsrc_base_init (gpointer g_class)
 {
@@ -199,12 +188,14 @@ gst_rtspsrc_base_init (gpointer g_class)
       gst_static_pad_template_get (&rtptemplate));
   gst_element_class_add_pad_template (element_class,
       gst_static_pad_template_get (&rtcptemplate));
+  gst_element_class_add_pad_template (element_class,
+      gst_static_pad_template_get (&realtemplate));
 
   gst_element_class_set_details (element_class, &gst_rtspsrc_details);
 }
 
 static void
-gst_rtspsrc_class_init (GstRTSPSrc * klass)
+gst_rtspsrc_class_init (GstRTSPSrcClass * klass)
 {
   GObjectClass *gobject_class;
   GstElementClass *gstelement_class;
@@ -244,21 +234,27 @@ gst_rtspsrc_class_init (GstRTSPSrc * kla
 }
 
 static void
-gst_rtspsrc_init (GstRTSPSrc * src)
+gst_rtspsrc_init (GstRTSPSrc * src, GstRTSPSrcClass * g_class)
 {
-  src->stream_rec_lock = g_new (GStaticRecMutex, 1);
-  g_static_rec_mutex_init (src->stream_rec_lock);
+  src->data.rtsp.stream_rec_lock = g_new (GStaticRecMutex, 1);
+  g_static_rec_mutex_init (src->data.rtsp.stream_rec_lock);
 }
 
 static void
 gst_rtspsrc_finalize (GObject * object)
 {
-  GstRTSPSrc *rtspsrc;
+  GstRTSPSrc *rtspsrc = GST_RTSPSRC (object);
+  GList *p;
 
-  rtspsrc = GST_RTSPSRC (object);
+  p = rtspsrc->data.rtsp.streams;
+  while (p) {
+    g_free (p->data);
+    p = p->next;
+  }
+  g_list_free (p);
 
-  g_static_rec_mutex_free (rtspsrc->stream_rec_lock);
-  g_free (rtspsrc->stream_rec_lock);
+  g_static_rec_mutex_free (rtspsrc->data.rtsp.stream_rec_lock);
+  g_free (rtspsrc->data.rtsp.stream_rec_lock);
 
   G_OBJECT_CLASS (parent_class)->finalize (object);
 }
@@ -273,8 +269,8 @@ gst_rtspsrc_set_property (GObject * obje
 
   switch (prop_id) {
     case PROP_LOCATION:
-      g_free (rtspsrc->location);
-      rtspsrc->location = g_value_dup_string (value);
+      gst_rtspsrc_uri_set_uri (GST_URI_HANDLER (rtspsrc),
+          g_value_get_string (value));
       break;
     case PROP_PROTOCOLS:
       rtspsrc->protocols = g_value_get_flags (value);
@@ -318,86 +314,6 @@ gst_rtspsrc_get_property (GObject * obje
   }
 }
 
-static GstRTSPStream *
-gst_rtspsrc_create_stream (GstRTSPSrc * src)
-{
-  GstRTSPStream *s;
-
-  s = g_new0 (GstRTSPStream, 1);
-  s->parent = src;
-  s->id = src->numstreams++;
-
-  src->streams = g_list_append (src->streams, s);
-
-  return s;
-}
-
-#if 0
-static void
-gst_rtspsrc_free_stream (GstRTSPSrc * src, GstRTSPStream * stream)
-{
-  if (stream->caps) {
-    gst_caps_unref (stream->caps);
-    stream->caps = NULL;
-  }
-
-  src->streams = g_list_remove (src->streams, stream);
-  src->numstreams--;
-
-  g_free (stream);
-}
-#endif
-
-static gboolean
-gst_rtspsrc_add_element (GstRTSPSrc * src, GstElement * element)
-{
-  gst_object_set_parent (GST_OBJECT (element), GST_OBJECT (src));
-
-  return TRUE;
-}
-
-static GstStateChangeReturn
-gst_rtspsrc_set_state (GstRTSPSrc * src, GstState state)
-{
-  GstStateChangeReturn ret;
-  GList *streams;
-
-  ret = GST_STATE_CHANGE_SUCCESS;
-
-  /* for all streams */
-  for (streams = src->streams; streams; streams = g_list_next (streams)) {
-    GstRTSPStream *stream;
-
-    stream = (GstRTSPStream *) streams->data;
-
-    /* first our rtp session manager */
-    if (stream->rtpdec) {
-      if ((ret =
-              gst_element_set_state (stream->rtpdec,
-                  state)) == GST_STATE_CHANGE_FAILURE)
-        goto done;
-    }
-
-    /* then our sources */
-    if (stream->rtpsrc) {
-      if ((ret =
-              gst_element_set_state (stream->rtpsrc,
-                  state)) == GST_STATE_CHANGE_FAILURE)
-        goto done;
-    }
-
-    if (stream->rtcpsrc) {
-      if ((ret =
-              gst_element_set_state (stream->rtcpsrc,
-                  state)) == GST_STATE_CHANGE_FAILURE)
-        goto done;
-    }
-  }
-
-done:
-  return ret;
-}
-
 #define PARSE_INT(p, del, res)          \
 G_STMT_START {                          \
   gchar *t = p;                         \
@@ -445,8 +361,10 @@ gst_rtspsrc_parse_rtpmap (gchar * rtpmap
     return FALSE;
 
   PARSE_STRING (p, "/", *name);
-  if (*name == NULL)
-    return FALSE;
+  if (*name == NULL) {
+    *name = p;
+    return TRUE;
+  }
 
   t = p;
   p = strstr (p, "/");
@@ -570,19 +488,31 @@ gst_rtspsrc_media_to_caps (SDPMedia * me
   return caps;
 }
 
+static void
+foreach_add (gpointer data, gpointer user_data)
+{
+  GstElement *src = GST_ELEMENT (user_data);
+  GstPad *pad;
+  gchar *name;
+  
+  name = g_strdup_printf ("rtp_stream%d", src->numsrcpads);
+  pad = gst_ghost_pad_new (name, GST_PAD (data));
+  g_free (name);
+
+  gst_element_add_pad (src, pad);
+  GST_DEBUG_OBJECT (src, "Added pad %s.%s.", GST_DEBUG_PAD_NAME (pad));
+}
+
 static gboolean
-gst_rtspsrc_stream_setup_rtp (GstRTSPStream * stream, SDPMedia * media,
-    gint * rtpport, gint * rtcpport)
+gst_rtspsrc_stream_setup_rtp (GstRTSPSrc * src, GstRTSPStream * stream,
+    SDPMedia * media, gint * rtpport, gint * rtcpport)
 {
   GstStateChangeReturn ret;
-  GstRTSPSrc *src;
   GstCaps *caps;
   GstElement *tmp, *rtp, *rtcp;
   gint tmp_rtp, tmp_rtcp;
   guint count;
 
-  src = stream->parent;
-
   tmp = NULL;
   rtp = NULL;
   rtcp = NULL;
@@ -653,14 +583,14 @@ again:
 
   /* we manage these elements */
   stream->rtpsrc = rtp;
-  gst_rtspsrc_add_element (src, stream->rtpsrc);
   stream->rtcpsrc = rtcp;
-  gst_rtspsrc_add_element (src, stream->rtcpsrc);
-
-  caps = gst_rtspsrc_media_to_caps (media);
+  gst_bin_add (GST_BIN (src), rtp);
+  gst_bin_add (GST_BIN (src), rtcp);
 
   /* set caps */
+  caps = gst_rtspsrc_media_to_caps (media);
   g_object_set (G_OBJECT (stream->rtpsrc), "caps", caps, NULL);
+  gst_caps_unref (caps);
 
   return TRUE;
 
@@ -716,23 +646,19 @@ cleanup:
 }
 
 static gboolean
-gst_rtspsrc_stream_configure_transport (GstRTSPStream * stream,
-    SDPMedia * media, RTSPTransport * transport)
+gst_rtspsrc_stream_configure_transport (GstRTSPSrc * src,
+    GstRTSPStream * stream, RTSPTransport * transport)
 {
-  GstRTSPSrc *src;
   GstPad *pad;
   GstStateChangeReturn ret;
-  gchar *name;
-
-  src = stream->parent;
-
-  GST_DEBUG ("configuring RTP transport for stream %p", stream);
+  GstIterator *it;
 
+  GST_DEBUG_OBJECT (src, "configuring RTP transport for stream %p", stream);
   if (!(stream->rtpdec = gst_element_factory_make ("rtpdec", NULL)))
     goto no_element;
 
   /* we manage this element */
-  gst_rtspsrc_add_element (src, stream->rtpdec);
+  gst_bin_add (GST_BIN (src), stream->rtpdec);
 
   if ((ret =
           gst_element_set_state (stream->rtpdec,
@@ -750,9 +676,6 @@ gst_rtspsrc_stream_configure_transport (
     stream->rtcpchannel = transport->interleaved.max;
     GST_DEBUG ("stream %p on channels %d-%d", stream,
         stream->rtpchannel, stream->rtcpchannel);
-
-    /* also store the caps in the stream */
-    stream->caps = gst_rtspsrc_media_to_caps (media);
   } else {
     /* configure for UDP delivery, we need to connect the udp pads to
      * the rtp session plugin. */
@@ -770,10 +692,10 @@ gst_rtspsrc_stream_configure_transport (
     gst_pad_use_fixed_caps (pad);
     gst_pad_set_caps (pad, stream->caps);
   }
-  name = g_strdup_printf ("rtp_stream%d", stream->id);
-  gst_element_add_pad (GST_ELEMENT_CAST (src), gst_ghost_pad_new (name, pad));
-  g_free (name);
-  gst_object_unref (pad);
+
+  it = gst_element_iterate_src_pads (GST_ELEMENT (stream->rtpdec));
+  gst_iterator_foreach (it, foreach_add, src);
+  gst_iterator_free (it);
 
   return TRUE;
 
@@ -820,7 +742,7 @@ gst_rtspsrc_combine_flows (GstRTSPSrc * 
     goto done;
 
   /* only return NOT_LINKED if all other pads returned NOT_LINKED */
-  for (streams = src->streams; streams; streams = g_list_next (streams)) {
+  for (streams = src->data.rtsp.streams; streams; streams = g_list_next (streams)) {
     GstRTSPStream *ostream = (GstRTSPStream *) streams->data;
 
     ret = ostream->last_ret;
@@ -836,8 +758,9 @@ done:
 }
 
 static void
-gst_rtspsrc_loop (GstRTSPSrc * src)
+gst_rtspsrc_loop (gpointer user_data)
 {
+  GstRTSPSrc *src = GST_RTSPSRC (user_data);
   RTSPMessage response = { 0 };
   RTSPResult res;
   gint channel;
@@ -858,9 +781,8 @@ gst_rtspsrc_loop (GstRTSPSrc * src)
   while (response.type != RTSP_MESSAGE_DATA);
 
   channel = response.type_data.data.channel;
-
-  lstream = g_list_find_custom (src->streams, GINT_TO_POINTER (channel),
-      (GCompareFunc) find_stream);
+  lstream = g_list_find_custom (src->data.rtsp.streams,
+      GINT_TO_POINTER (channel), (GCompareFunc) find_stream);
   if (!lstream)
     goto unknown_stream;
 
@@ -886,13 +808,7 @@ gst_rtspsrc_loop (GstRTSPSrc * src)
 
   /* and chain buffer to internal element */
   {
-    GstBuffer *buf;
-
-    /* strip the trailing \0 */
-    size -= 1;
-
-    buf = gst_buffer_new_and_alloc (size);
-    memcpy (GST_BUFFER_DATA (buf), data, size);
+    GstBuffer *buf = gst_buffer_new ();
 
     if (caps)
       gst_buffer_set_caps (buf, caps);
@@ -900,8 +816,15 @@ gst_rtspsrc_loop (GstRTSPSrc * src)
     GST_DEBUG_OBJECT (src, "pushing data of size %d on channel %d", size,
         channel);
 
-    /* chain to the peer pad */
-    ret = gst_pad_chain (outpad, buf);
+    /* strip the trailing \0 */
+    gst_buffer_set_data (buf, response.body, response.body_size - 1);
+    buf->malloc_data = response.body;
+    response.body = NULL;
+    response.body_size = 0;
+    rtsp_message_unset (&response);
+
+    if ((ret = gst_pad_chain (outpad, buf)) != GST_FLOW_OK)
+      goto chain_failed;
 
     /* combine all streams */
     ret = gst_rtspsrc_combine_flows (src, stream, ret);
@@ -920,22 +843,84 @@ receive_error:
   {
     GST_ELEMENT_ERROR (src, RESOURCE, WRITE,
         ("Could not receive message."), (NULL));
-    ret = GST_FLOW_UNEXPECTED;
-    /*
-       gst_pad_push_event (src->srcpad, gst_event_new (GST_EVENT_EOS));
-     */
-    goto need_pause;
+    rtsp_message_unset (&response);
+    gst_task_pause (src->data.rtsp.task);
+    return;
+  }
+chain_failed:
+  {
+    GST_DEBUG_OBJECT (src, "Could not chain buffer for channel "
+        "%d on pad %s.%s: %s.", channel, GST_DEBUG_PAD_NAME (outpad),
+        gst_flow_get_name (ret));
+    gst_task_pause (src->data.rtsp.task);
+    return;
   }
 need_pause:
   {
     GST_DEBUG_OBJECT (src, "pausing task, reason %d (%s)", ret,
         gst_flow_get_name (ret));
-    gst_task_pause (src->task);
+    gst_task_pause (src->data.rtsp.task);
     return;
   }
 }
 
 static gboolean
+gst_rtspsrc_parse_methods (GstRTSPSrc * src, RTSPMessage * response)
+{
+  gchar *respoptions = NULL;
+
+  /* Try Allow Header first, then Public Header. These fields
+   * are not required. If they are missing, assume that the
+   * server supports DESCRIBE and SETUP. If not, make sure 
+   * the server supports DESCRIBE and SETUP. */
+  rtsp_message_get_header (response, RTSP_HDR_ALLOW, &respoptions);
+  if (!respoptions)
+    rtsp_message_get_header (response, RTSP_HDR_PUBLIC, &respoptions);
+  if (!respoptions) {
+    src->methods = RTSP_DESCRIBE | RTSP_SETUP;
+  } else {
+    gchar **options = g_strsplit (respoptions, ",", 0);
+    gint i = 0;
+
+    while (options[i]) {
+      gchar *stripped;
+      gint method;
+
+      stripped = g_strdup (options[i]);
+      stripped = g_strstrip (stripped);
+      method = rtsp_find_method (stripped);
+      g_free (stripped);
+
+      /* keep bitfield of supported methods */
+      if (method != -1)
+        src->methods |= method;
+      i++;
+    }
+    g_strfreev (options);
+
+    if (!(src->methods & RTSP_DESCRIBE))
+      goto no_describe;
+    if (!(src->methods & RTSP_SETUP))
+      goto no_setup;
+  }
+  return TRUE;
+
+  /* Errors */
+no_describe:
+  {
+    GST_ELEMENT_ERROR (src, RESOURCE, WRITE,
+        ("Server does not support DESCRIBE."), (NULL));
+    return FALSE;
+  }
+no_setup:
+  {
+    GST_ELEMENT_ERROR (src, RESOURCE, WRITE,
+        ("Server does not support SETUP."), (NULL));
+    return FALSE;
+  }
+}
+
+static gboolean
 gst_rtspsrc_send (GstRTSPSrc * src, RTSPMessage * request,
     RTSPMessage * response, RTSPStatusCode * code)
 {
@@ -977,9 +962,16 @@ receive_error:
   }
 error_response:
   {
+    switch (response->type_data.response.code) {
+      case RTSP_STS_NOT_FOUND:
+        GST_ELEMENT_ERROR (src, RESOURCE, NOT_FOUND, ("%s",
+              response->type_data.response.reason), (NULL));
+        break;
+      default:
     GST_ELEMENT_ERROR (src, RESOURCE, READ, ("Got error response: %d (%s).",
             response->type_data.response.code,
             response->type_data.response.reason), (NULL));
+    }
     return FALSE;
   }
 }
@@ -987,30 +979,25 @@ error_response:
 static gboolean
 gst_rtspsrc_open (GstRTSPSrc * src)
 {
-  RTSPUrl *url;
   RTSPResult res;
   RTSPMessage request = { 0 };
   RTSPMessage response = { 0 };
   guint8 *data;
-  guint size;
+  guint size, n_streams;
   SDPMessage sdp = { 0 };
-  GstRTSPProto protocols;
-
-  /* parse url */
-  GST_DEBUG_OBJECT (src, "parsing url...");
-  if ((res = rtsp_url_parse (src->location, &url)) < 0)
-    goto invalid_url;
+  gchar *s;
+  gint i;
+  gint protocols = src->protocols;
 
   /* open connection */
   GST_DEBUG_OBJECT (src, "opening connection...");
-  if ((res = rtsp_connection_open (url, &src->connection)) < 0)
+  if ((res = rtsp_connection_open (src->url, &src->connection)) < 0)
     goto could_not_open;
 
   /* create OPTIONS */
   GST_DEBUG_OBJECT (src, "create options...");
-  if ((res =
-          rtsp_message_init_request (RTSP_OPTIONS, src->location,
-              &request)) < 0)
+  if ((res = rtsp_message_init_request (&request, RTSP_OPTIONS,
+          src->location)) < 0)
     goto create_request_failed;
 
   /* send OPTIONS */
@@ -1018,58 +1005,32 @@ gst_rtspsrc_open (GstRTSPSrc * src)
   if (!gst_rtspsrc_send (src, &request, &response, NULL))
     goto send_error;
 
-  {
-    gchar *respoptions = NULL;
-    gchar **options;
-    gint i;
-
-    /* Try Allow Header first */
-    rtsp_message_get_header (&response, RTSP_HDR_ALLOW, &respoptions);
-    if (!respoptions) {
-      /* Then maybe Public Header... */
-      rtsp_message_get_header (&response, RTSP_HDR_PUBLIC, &respoptions);
-      if (!respoptions) {
-        /* this field is not required, assume the server supports
-         * DESCRIBE and SETUP*/
-        GST_DEBUG_OBJECT (src, "could not get OPTIONS");
-        src->options = RTSP_DESCRIBE | RTSP_SETUP;
-        goto no_options;
-      }
-    }
-
-    /* parse options */
-    options = g_strsplit (respoptions, ",", 0);
-
-    i = 0;
-    while (options[i]) {
-      gchar *stripped;
-      gint method;
-
-      stripped = g_strdup (options[i]);
-      stripped = g_strstrip (stripped);
-      method = rtsp_find_method (stripped);
-      g_free (stripped);
-
-      /* keep bitfield of supported methods */
-      if (method != -1)
-        src->options |= method;
-      i++;
+  /*
+   * Real servers don't follow the specification. They check for
+   * undocumented fields.
+   */
+  rtsp_message_get_header (&response, RTSP_HDR_SERVER, &s);
+  if (s && strstr (s, "Real")) {
+    rtsp_message_unset (&request);
+    rtsp_message_unset (&response);
+    rtsp_connection_close (src->connection);
+    rtsp_connection_free (src->connection);
+    return gst_rtspsrc_open_real (src);
     }
-    g_strfreev (options);
 
-  no_options:
-    /* we need describe and setup */
-    if (!(src->options & RTSP_DESCRIBE))
-      goto no_describe;
-    if (!(src->options & RTSP_SETUP))
-      goto no_setup;
+  /* parse OPTIONS */
+  if (!gst_rtspsrc_parse_methods (src, &response)) {
+    rtsp_message_unset (&request);
+    rtsp_message_unset (&response);
+    rtsp_connection_close (src->connection);
+    rtsp_connection_free (src->connection);
+    return FALSE;
   }
 
   /* create DESCRIBE */
   GST_DEBUG_OBJECT (src, "create describe...");
-  if ((res =
-          rtsp_message_init_request (RTSP_DESCRIBE, src->location,
-              &request)) < 0)
+  if ((res = rtsp_message_init_request (&request, RTSP_DESCRIBE,
+          src->location)) < 0)
     goto create_request_failed;
   /* we accept SDP for now */
   rtsp_message_add_header (&request, RTSP_HDR_ACCEPT, "application/sdp");
@@ -1102,22 +1063,14 @@ gst_rtspsrc_open (GstRTSPSrc * src)
   if (src->debug)
     sdp_message_dump (&sdp);
 
-  /* we allow all configured protocols */
-  protocols = src->protocols;
   /* setup streams */
-  {
-    gint i;
-
-    for (i = 0; i < sdp_message_medias_len (&sdp); i++) {
-      SDPMedia *media;
+  n_streams = sdp_message_medias_len (&sdp);
+  for (i = 0; i < n_streams; i++) {
+    SDPMedia *media = sdp_message_get_media (&sdp, i);
       gchar *setup_url;
       gchar *control_url;
       gchar *transports;
-      GstRTSPStream *stream;
-
-      media = sdp_message_get_media (&sdp, i);
-
-      stream = gst_rtspsrc_create_stream (src);
+    GstRTSPStream *stream = NULL;
 
       GST_DEBUG_OBJECT (src, "setup media %d", i);
       control_url = sdp_media_get_attribute_val (media, "control");
@@ -1136,13 +1089,13 @@ gst_rtspsrc_open (GstRTSPSrc * src)
 
       GST_DEBUG_OBJECT (src, "setup %s", setup_url);
       /* create SETUP request */
-      if ((res =
-              rtsp_message_init_request (RTSP_SETUP, setup_url,
-                  &request)) < 0) {
+    res = rtsp_message_init_request (&request, RTSP_SETUP, setup_url);
         g_free (setup_url);
+    if (res < 0)
         goto create_request_failed;
-      }
-      g_free (setup_url);
+
+    stream = g_new0 (GstRTSPStream, 1);
+    src->data.rtsp.streams = g_list_append (src->data.rtsp.streams, stream);
 
       transports = g_strdup ("");
       if (protocols & GST_RTSP_PROTO_UDP_UNICAST) {
@@ -1151,7 +1104,7 @@ gst_rtspsrc_open (GstRTSPSrc * src)
         gchar *trxparams;
 
         /* allocate two udp ports */
-        if (!gst_rtspsrc_stream_setup_rtp (stream, media, &rtpport, &rtcpport))
+      if (!gst_rtspsrc_stream_setup_rtp (src, stream, media, &rtpport, &rtcpport))
           goto setup_rtp_failed;
 
         GST_DEBUG_OBJECT (src, "setting up RTP ports %d-%d", rtpport, rtcpport);
@@ -1207,7 +1159,13 @@ gst_rtspsrc_open (GstRTSPSrc * src)
         if (transport.lower_transport == RTSP_LOWER_TRANS_TCP) {
           GST_DEBUG_OBJECT (src, "stream %d as TCP", i);
           protocols = GST_RTSP_PROTO_TCP;
-          src->interleaved = TRUE;
+        if (!src->data.rtsp.task) {
+          src->data.rtsp.task = gst_task_create (gst_rtspsrc_loop, src);
+          gst_task_set_lock (src->data.rtsp.task, src->data.rtsp.stream_rec_lock);
+        }
+
+        /* also store the caps in the stream */
+        stream->caps = gst_rtspsrc_media_to_caps (media);
         } else {
           if (transport.multicast) {
             /* disable unicast */
@@ -1220,7 +1178,7 @@ gst_rtspsrc_open (GstRTSPSrc * src)
           }
         }
         /* now configure the stream with the transport */
-        if (!gst_rtspsrc_stream_configure_transport (stream, media, &transport)) {
+      if (!gst_rtspsrc_stream_configure_transport (src, stream, &transport)) {
           GST_DEBUG_OBJECT (src,
               "could not configure stream transport, skipping stream");
         }
@@ -1228,61 +1186,68 @@ gst_rtspsrc_open (GstRTSPSrc * src)
         rtsp_transport_init (&transport);
       }
     }
-  }
+
+  rtsp_message_unset (&request);
+  rtsp_message_unset (&response);
+  gst_element_no_more_pads (GST_ELEMENT (src));
   return TRUE;
 
   /* ERRORS */
-invalid_url:
-  {
-    GST_ELEMENT_ERROR (src, RESOURCE, NOT_FOUND,
-        ("Not a valid RTSP url."), (NULL));
-    return FALSE;
-  }
 could_not_open:
   {
     GST_ELEMENT_ERROR (src, RESOURCE, OPEN_READ_WRITE,
         ("Could not open connection."), (NULL));
+    rtsp_message_unset (&request);
+    rtsp_message_unset (&response);
     return FALSE;
   }
 create_request_failed:
   {
     GST_ELEMENT_ERROR (src, LIBRARY, INIT,
         ("Could not create request."), (NULL));
+    rtsp_message_unset (&request);
+    rtsp_message_unset (&response);
+    rtsp_connection_close (src->connection);
+    rtsp_connection_free (src->connection);
     return FALSE;
   }
 send_error:
   {
     GST_ELEMENT_ERROR (src, RESOURCE, WRITE,
         ("Could not send message."), (NULL));
-    return FALSE;
-  }
-no_describe:
-  {
-    GST_ELEMENT_ERROR (src, RESOURCE, WRITE,
-        ("Server does not support DESCRIBE."), (NULL));
-    return FALSE;
-  }
-no_setup:
-  {
-    GST_ELEMENT_ERROR (src, RESOURCE, WRITE,
-        ("Server does not support SETUP."), (NULL));
+    rtsp_message_unset (&request);
+    rtsp_message_unset (&response);
+    rtsp_connection_close (src->connection);
+    rtsp_connection_free (src->connection);
     return FALSE;
   }
 wrong_content_type:
   {
     GST_ELEMENT_ERROR (src, RESOURCE, WRITE,
         ("Server does not support SDP."), (NULL));
+    rtsp_message_unset (&request);
+    rtsp_message_unset (&response);
+    rtsp_connection_close (src->connection);
+    rtsp_connection_free (src->connection);
     return FALSE;
   }
 setup_rtp_failed:
   {
     GST_ELEMENT_ERROR (src, RESOURCE, WRITE, ("Could not setup rtp."), (NULL));
+    rtsp_message_unset (&request);
+    rtsp_message_unset (&response);
+    rtsp_connection_close (src->connection);
+    rtsp_connection_free (src->connection);
     return FALSE;
   }
 no_transport:
   {
     GST_ELEMENT_ERROR (src, RESOURCE, WRITE,
         ("Server did not select transport."), (NULL));
+    rtsp_message_unset (&request);
+    rtsp_message_unset (&response);
+    rtsp_connection_close (src->connection);
+    rtsp_connection_free (src->connection);
     return FALSE;
   }
 }
@@ -1292,40 +1257,28 @@ gst_rtspsrc_close (GstRTSPSrc * src)
 {
   RTSPMessage request = { 0 };
   RTSPMessage response = { 0 };
-  RTSPResult res;
 
   GST_DEBUG_OBJECT (src, "TEARDOWN...");
 
   /* stop task if any */
-  if (src->task) {
-    gst_task_stop (src->task);
-
-    /* make sure it is not running */
-    g_static_rec_mutex_lock (src->stream_rec_lock);
-    g_static_rec_mutex_unlock (src->stream_rec_lock);
-
-    /* no wait for the task to finish */
-    gst_task_join (src->task);
-
-    /* and free the task */
-    gst_object_unref (GST_OBJECT (src->task));
-    src->task = NULL;
+  if (src->data.rtsp.task) {
+    gst_task_join (src->data.rtsp.task);
+  } else if (src->data.realpad) {
+    gst_pad_stop_task (src->data.realpad);
   }
 
-  if (src->options & RTSP_PLAY) {
-    /* do TEARDOWN */
-    if ((res =
-            rtsp_message_init_request (RTSP_TEARDOWN, src->location,
-                &request)) < 0)
+  if (src->methods & RTSP_PLAY) {
+    if (rtsp_message_init_request (&request, RTSP_TEARDOWN, src->location) < 0)
       goto create_request_failed;
-
     if (!gst_rtspsrc_send (src, &request, &response, NULL))
       goto send_error;
+    rtsp_message_unset (&request);
+    rtsp_message_unset (&response);
   }
 
   /* close connection */
   GST_DEBUG_OBJECT (src, "closing connection...");
-  if ((res = rtsp_connection_close (src->connection)) < 0)
+  if (rtsp_connection_close (src->connection) < 0)
     goto close_failed;
 
   return TRUE;
@@ -1335,12 +1288,16 @@ create_request_failed:
   {
     GST_ELEMENT_ERROR (src, LIBRARY, INIT,
         ("Could not create request."), (NULL));
+    rtsp_message_unset (&request);
+    rtsp_message_unset (&response);
     return FALSE;
   }
 send_error:
   {
     GST_ELEMENT_ERROR (src, RESOURCE, WRITE,
         ("Could not send message."), (NULL));
+    rtsp_message_unset (&request);
+    rtsp_message_unset (&response);
     return FALSE;
   }
 close_failed:
@@ -1355,28 +1312,21 @@ gst_rtspsrc_play (GstRTSPSrc * src)
 {
   RTSPMessage request = { 0 };
   RTSPMessage response = { 0 };
-  RTSPResult res;
-
-  if (!(src->options & RTSP_PLAY))
-    return TRUE;
 
   GST_DEBUG_OBJECT (src, "PLAY...");
-
-  /* do play */
-  if ((res =
-          rtsp_message_init_request (RTSP_PLAY, src->location, &request)) < 0)
+  if (src->methods & RTSP_PLAY) {
+    if (rtsp_message_init_request (&request, RTSP_PLAY, src->location) < 0)
     goto create_request_failed;
-
   if (!gst_rtspsrc_send (src, &request, &response, NULL))
     goto send_error;
-
-  if (src->interleaved) {
-    src->task = gst_task_create ((GstTaskFunction) gst_rtspsrc_loop, src);
-
-    gst_task_set_lock (src->task, src->stream_rec_lock);
-    gst_task_start (src->task);
+    rtsp_message_unset (&request);
+    rtsp_message_unset (&response);
   }
 
+  if ((src->data.rtsp.task && !gst_task_start (src->data.rtsp.task)) ||
+      (src->data.realpad &&
+       !gst_pad_start_task (src->data.realpad, gst_rtspsrc_loop_real, src)))
+    goto task_error;
   return TRUE;
 
   /* ERRORS */
@@ -1384,12 +1334,22 @@ create_request_failed:
   {
     GST_ELEMENT_ERROR (src, LIBRARY, INIT,
         ("Could not create request."), (NULL));
+    rtsp_message_unset (&request);
+    rtsp_message_unset (&response);
     return FALSE;
   }
 send_error:
   {
     GST_ELEMENT_ERROR (src, RESOURCE, WRITE,
         ("Could not send message."), (NULL));
+    rtsp_message_unset (&request);
+    rtsp_message_unset (&response);
+    return FALSE;
+  }
+task_error:
+  {
+    GST_ELEMENT_ERROR (src, RESOURCE, WRITE,
+        ("Could not start task."), (NULL));
     return FALSE;
   }
 }
@@ -1399,20 +1359,20 @@ gst_rtspsrc_pause (GstRTSPSrc * src)
 {
   RTSPMessage request = { 0 };
   RTSPMessage response = { 0 };
-  RTSPResult res;
-
-  if (!(src->options & RTSP_PAUSE))
-    return TRUE;
 
   GST_DEBUG_OBJECT (src, "PAUSE...");
-  /* do pause */
-  if ((res =
-          rtsp_message_init_request (RTSP_PAUSE, src->location, &request)) < 0)
+  if (src->methods & RTSP_PAUSE) {
+    if (rtsp_message_init_request (&request, RTSP_PAUSE, src->location) < 0)
     goto create_request_failed;
-
   if (!gst_rtspsrc_send (src, &request, &response, NULL))
     goto send_error;
+    rtsp_message_unset (&request);
+    rtsp_message_unset (&response);
+  }
 
+  if ((src->data.rtsp.task && !gst_task_pause (src->data.rtsp.task)) ||
+      (src->data.realpad && !gst_pad_pause_task (src->data.realpad)))
+    goto task_error;
   return TRUE;
 
   /* ERRORS */
@@ -1420,12 +1380,22 @@ create_request_failed:
   {
     GST_ELEMENT_ERROR (src, LIBRARY, INIT,
         ("Could not create request."), (NULL));
+    rtsp_message_unset (&request);
+    rtsp_message_unset (&response);
     return FALSE;
   }
 send_error:
   {
     GST_ELEMENT_ERROR (src, RESOURCE, WRITE,
         ("Could not send message."), (NULL));
+    rtsp_message_unset (&request);
+    rtsp_message_unset (&response);
+    return FALSE;
+  }
+task_error:
+  {
+    GST_ELEMENT_ERROR (src, RESOURCE, WRITE,
+        ("Could not stop task."), (NULL));
     return FALSE;
   }
 }
@@ -1443,8 +1412,7 @@ gst_rtspsrc_change_state (GstElement * e
     case GST_STATE_CHANGE_NULL_TO_READY:
       break;
     case GST_STATE_CHANGE_READY_TO_PAUSED:
-      rtspsrc->interleaved = FALSE;
-      rtspsrc->options = 0;
+      rtspsrc->methods = 0;
       if (!gst_rtspsrc_open (rtspsrc))
         goto open_failed;
       break;
@@ -1459,10 +1427,6 @@ gst_rtspsrc_change_state (GstElement * e
   if (ret == GST_STATE_CHANGE_FAILURE)
     goto done;
 
-  ret = gst_rtspsrc_set_state (rtspsrc, GST_STATE_PENDING (rtspsrc));
-  if (ret == GST_STATE_CHANGE_FAILURE)
-    goto done;
-
   switch (transition) {
     case GST_STATE_CHANGE_READY_TO_PAUSED:
       ret = GST_STATE_CHANGE_NO_PREROLL;
@@ -1508,14 +1472,24 @@ gst_rtspsrc_uri_get_uri (GstURIHandler *
 {
   GstRTSPSrc *src = GST_RTSPSRC (handler);
 
-  return g_strdup (src->location);
+  return src->location;
 }
 
 static gboolean
 gst_rtspsrc_uri_set_uri (GstURIHandler * handler, const gchar * uri)
 {
   GstRTSPSrc *src = GST_RTSPSRC (handler);
+  RTSPResult res;
+
+  if (src->location && uri && !strcmp (uri, src->location))
+    return TRUE;
 
+  rtsp_url_free (src->url);
+  if ((res = rtsp_url_parse (uri, &src->url)) < 0) {
+    GST_ELEMENT_ERROR (src, RESOURCE, NOT_FOUND,
+        ("Not a valid RTSP url."), (NULL));
+    return FALSE;
+  }
   g_free (src->location);
   src->location = g_strdup (uri);
 
@@ -1532,3 +1506,993 @@ gst_rtspsrc_uri_handler_init (gpointer g
   iface->get_uri = gst_rtspsrc_uri_get_uri;
   iface->set_uri = gst_rtspsrc_uri_set_uri;
 }
+
+/* Below is real REAL code. Really. */
+
+#define ASSERT_SIZE(n) if (size < (n)) goto not_enough_data;
+
+static void
+gst_rtspsrc_loop_real (gpointer user_data)
+{
+  GstRTSPSrc *src = GST_RTSPSRC (user_data);
+  RTSPMessage response = { 0 };
+  RTSPResult res;
+  GstBuffer *buf;
+  GstCaps *caps;
+  GstFlowReturn ret;
+  guint8 *data;
+  guint size, channel = 0;
+  gboolean length_included_flag;
+  gboolean need_reliable_flag = 0;
+  gboolean is_reliable;
+  guint16 seq_no;
+  gboolean back_to_back;
+  gboolean slow_data;
+  guint asm_rule;
+  guint32 ts;
+  guint16 total_reliable;
+  guint16 packet_type, packet_length;
+
+  if ((res = rtsp_connection_receive (src->connection, &response)) < 0)
+    goto receive_error;
+  if (response.type != RTSP_MESSAGE_DATA)
+    goto no_data;
+
+  /* Data is in RDT format. */
+  rtsp_message_get_body (&response, &data, &size);
+  ASSERT_SIZE (1);
+  while (size > 0) {
+    length_included_flag = (data[0] & 0x80) >> 7;
+    need_reliable_flag   = (data[0] & 0x40) >> 6;
+    is_reliable          = (data[0] & 0x01) >> 0;
+    channel              = (data[0] & 0x3e) >> 1;
+    GST_DEBUG_OBJECT (src, "length_included_flag=%i "
+        "need_reliable_flag=%i is_reliable=%i", length_included_flag,
+        need_reliable_flag, is_reliable);
+    if (!length_included_flag)
+      break;
+    ASSERT_SIZE (5);
+    packet_type = GST_READ_UINT16_BE (data + 1);
+    packet_length = GST_READ_UINT16_BE (data + 3);
+    GST_DEBUG_OBJECT (src, "Skipping packet of type %02x and length=%d...",
+        packet_type, packet_length);
+    ASSERT_SIZE (packet_length);
+    data += packet_length;
+    size -= packet_length;
+  }
+  ASSERT_SIZE (3);
+  seq_no = GST_READ_UINT16_BE (data + 1);
+  ASSERT_SIZE (4);
+  back_to_back = (data[3] >> 7) & 0x01;
+  slow_data    = (data[3] >> 6) & 0x01;
+  asm_rule     = ((data[3] << 2) & 0xf) >> 2;
+  ASSERT_SIZE (8);
+  ts = GST_READ_UINT32_BE (data + 4);
+  if (need_reliable_flag) {
+    ASSERT_SIZE (10);
+    total_reliable = GST_READ_UINT16_BE (data + 8);
+    data += 10;
+    size -= 10;
+  } else {
+    data += 9;
+    size -= 9;
+  }
+  GST_DEBUG_OBJECT (src, "Passing on packet %d: "
+      "back_to_back=%i slow_data=%i asm_rule=%i timestamp=%u",
+      seq_no, back_to_back, slow_data, asm_rule, ts);
+
+  caps = gst_caps_new_simple ("application/vnd.rn-realmedia", NULL);
+
+  buf = gst_buffer_new_and_alloc (12);
+  gst_buffer_set_caps (buf, caps);
+  GST_WRITE_UINT16_BE (GST_BUFFER_DATA (buf) + 0, 0);        /* version   */
+  GST_WRITE_UINT16_BE (GST_BUFFER_DATA (buf) + 2, size + 9); /* length    */
+  GST_WRITE_UINT16_BE (GST_BUFFER_DATA (buf) + 4, channel);  /* stream    */
+  GST_WRITE_UINT32_BE (GST_BUFFER_DATA (buf) + 6, ts);       /* timestamp */
+  GST_WRITE_UINT16_BE (GST_BUFFER_DATA (buf) + 10, 0);       /* flags     */
+  if ((ret = gst_pad_push (src->data.realpad, buf)) != GST_FLOW_OK)
+    goto push_failed;
+
+  buf = gst_buffer_new ();
+  gst_buffer_set_caps (buf, caps);
+  gst_buffer_set_data (buf, data, size - 3);
+  buf->malloc_data = response.body;
+  response.body = NULL;
+  response.body_size = 0;
+  rtsp_message_unset (&response);
+  if ((ret = gst_pad_push (src->data.realpad, buf)) != GST_FLOW_OK)
+    goto push_failed;
+
+  gst_caps_unref (caps);
+
+  return;
+
+not_enough_data:
+  {
+    GST_DEBUG_OBJECT (src, "Not enough data.");
+    rtsp_message_unset (&response);
+    return;
+  }
+no_data:
+  {
+    GST_DEBUG_OBJECT (src, "Received a reponse of type %d.", response.type);
+    rtsp_message_unset (&response);
+    return;
+  }
+receive_error:
+  {
+    GST_ELEMENT_ERROR (src, RESOURCE, WRITE,
+        ("Could not receive message."), (NULL));
+    rtsp_message_unset (&response);
+    gst_task_pause (src->data.rtsp.task);
+    return;
+  }
+push_failed:
+  {
+    GST_ELEMENT_ERROR (src, RESOURCE, WRITE,
+        ("Could not push buffer: %s.", gst_flow_get_name (ret)), (NULL));
+    gst_caps_unref (caps);
+    rtsp_message_unset (&response);
+    gst_task_pause (src->data.rtsp.task);
+    return;
+  }
+}
+
+/*
+ * The following code has been copied from
+ * xine-lib-1.1.1/src/input/libreal/real.c.
+ */
+
+#include <stdint.h>
+
+#if G_BYTE_ORDER == G_LITTLE_ENDIAN
+#define be2me_32(x) GUINT32_SWAP_LE_BE_IA32(x)
+#define le2me_32(x) (x)
+#else
+#define le2me_32(x) GUINT32_SWAP_LE_BE_IA32(x)
+#define be2me_32(x) (x)
+#endif
+
+#define ALE_32(x) (le2me_32(*(uint32_t*)(x)))
+#define LE_32(x) ALE_32(x)
+
+const unsigned char xor_table[] = {
+    0x05, 0x18, 0x74, 0xd0, 0x0d, 0x09, 0x02, 0x53,
+    0xc0, 0x01, 0x05, 0x05, 0x67, 0x03, 0x19, 0x70,
+    0x08, 0x27, 0x66, 0x10, 0x10, 0x72, 0x08, 0x09,
+    0x63, 0x11, 0x03, 0x71, 0x08, 0x08, 0x70, 0x02,
+    0x10, 0x57, 0x05, 0x18, 0x54, 0x00, 0x00, 0x00 };
+
+#define BE_32C(x,y) do { *(uint32_t *)(x) = be2me_32((y)); } while(0)
+#define LE_32C(x,y) do { *(uint32_t *)(x) = le2me_32((y)); } while(0)
+
+static void
+hash (char * field, char * param)
+{
+  uint32_t a, b, c, d;
+ 
+  /* fill variables */
+  a = LE_32(field);
+  b = LE_32(field+4);
+  c = LE_32(field+8);
+  d = LE_32(field+12);
+  
+  a = ((b & c) | (~b & d)) + LE_32((param+0x00)) + a - 0x28955B88;
+  a = ((a << 0x07) | (a >> 0x19)) + b;
+  d = ((a & b) | (~a & c)) + LE_32((param+0x04)) + d - 0x173848AA;
+  d = ((d << 0x0c) | (d >> 0x14)) + a;
+  c = ((d & a) | (~d & b)) + LE_32((param+0x08)) + c + 0x242070DB;
+  c = ((c << 0x11) | (c >> 0x0f)) + d;
+  b = ((c & d) | (~c & a)) + LE_32((param+0x0c)) + b - 0x3E423112;
+  b = ((b << 0x16) | (b >> 0x0a)) + c;
+  a = ((b & c) | (~b & d)) + LE_32((param+0x10)) + a - 0x0A83F051;
+  a = ((a << 0x07) | (a >> 0x19)) + b;
+  d = ((a & b) | (~a & c)) + LE_32((param+0x14)) + d + 0x4787C62A;
+  d = ((d << 0x0c) | (d >> 0x14)) + a;
+  c = ((d & a) | (~d & b)) + LE_32((param+0x18)) + c - 0x57CFB9ED;
+  c = ((c << 0x11) | (c >> 0x0f)) + d;
+  b = ((c & d) | (~c & a)) + LE_32((param+0x1c)) + b - 0x02B96AFF;
+  b = ((b << 0x16) | (b >> 0x0a)) + c;
+  a = ((b & c) | (~b & d)) + LE_32((param+0x20)) + a + 0x698098D8;
+  a = ((a << 0x07) | (a >> 0x19)) + b;
+  d = ((a & b) | (~a & c)) + LE_32((param+0x24)) + d - 0x74BB0851;
+  d = ((d << 0x0c) | (d >> 0x14)) + a;
+  c = ((d & a) | (~d & b)) + LE_32((param+0x28)) + c - 0x0000A44F;
+  c = ((c << 0x11) | (c >> 0x0f)) + d;
+  b = ((c & d) | (~c & a)) + LE_32((param+0x2C)) + b - 0x76A32842;
+  b = ((b << 0x16) | (b >> 0x0a)) + c;
+  a = ((b & c) | (~b & d)) + LE_32((param+0x30)) + a + 0x6B901122;
+  a = ((a << 0x07) | (a >> 0x19)) + b;
+  d = ((a & b) | (~a & c)) + LE_32((param+0x34)) + d - 0x02678E6D;
+  d = ((d << 0x0c) | (d >> 0x14)) + a;
+  c = ((d & a) | (~d & b)) + LE_32((param+0x38)) + c - 0x5986BC72;
+  c = ((c << 0x11) | (c >> 0x0f)) + d;
+  b = ((c & d) | (~c & a)) + LE_32((param+0x3c)) + b + 0x49B40821;
+  b = ((b << 0x16) | (b >> 0x0a)) + c;
+  
+  a = ((b & d) | (~d & c)) + LE_32((param+0x04)) + a - 0x09E1DA9E;
+  a = ((a << 0x05) | (a >> 0x1b)) + b;
+  d = ((a & c) | (~c & b)) + LE_32((param+0x18)) + d - 0x3FBF4CC0;
+  d = ((d << 0x09) | (d >> 0x17)) + a;
+  c = ((d & b) | (~b & a)) + LE_32((param+0x2c)) + c + 0x265E5A51;
+  c = ((c << 0x0e) | (c >> 0x12)) + d;
+  b = ((c & a) | (~a & d)) + LE_32((param+0x00)) + b - 0x16493856;
+  b = ((b << 0x14) | (b >> 0x0c)) + c;
+  a = ((b & d) | (~d & c)) + LE_32((param+0x14)) + a - 0x29D0EFA3;
+  a = ((a << 0x05) | (a >> 0x1b)) + b;
+  d = ((a & c) | (~c & b)) + LE_32((param+0x28)) + d + 0x02441453;
+  d = ((d << 0x09) | (d >> 0x17)) + a;
+  c = ((d & b) | (~b & a)) + LE_32((param+0x3c)) + c - 0x275E197F;
+  c = ((c << 0x0e) | (c >> 0x12)) + d;
+  b = ((c & a) | (~a & d)) + LE_32((param+0x10)) + b - 0x182C0438;
+  b = ((b << 0x14) | (b >> 0x0c)) + c;
+  a = ((b & d) | (~d & c)) + LE_32((param+0x24)) + a + 0x21E1CDE6;
+  a = ((a << 0x05) | (a >> 0x1b)) + b;
+  d = ((a & c) | (~c & b)) + LE_32((param+0x38)) + d - 0x3CC8F82A;
+  d = ((d << 0x09) | (d >> 0x17)) + a;
+  c = ((d & b) | (~b & a)) + LE_32((param+0x0c)) + c - 0x0B2AF279;
+  c = ((c << 0x0e) | (c >> 0x12)) + d;
+  b = ((c & a) | (~a & d)) + LE_32((param+0x20)) + b + 0x455A14ED;
+  b = ((b << 0x14) | (b >> 0x0c)) + c;
+  a = ((b & d) | (~d & c)) + LE_32((param+0x34)) + a - 0x561C16FB;
+  a = ((a << 0x05) | (a >> 0x1b)) + b;
+  d = ((a & c) | (~c & b)) + LE_32((param+0x08)) + d - 0x03105C08;
+  d = ((d << 0x09) | (d >> 0x17)) + a;
+  c = ((d & b) | (~b & a)) + LE_32((param+0x1c)) + c + 0x676F02D9;
+  c = ((c << 0x0e) | (c >> 0x12)) + d;
+  b = ((c & a) | (~a & d)) + LE_32((param+0x30)) + b - 0x72D5B376;
+  b = ((b << 0x14) | (b >> 0x0c)) + c;
+  
+  a = (b ^ c ^ d) + LE_32((param+0x14)) + a - 0x0005C6BE;
+  a = ((a << 0x04) | (a >> 0x1c)) + b;
+  d = (a ^ b ^ c) + LE_32((param+0x20)) + d - 0x788E097F;
+  d = ((d << 0x0b) | (d >> 0x15)) + a;
+  c = (d ^ a ^ b) + LE_32((param+0x2c)) + c + 0x6D9D6122;
+  c = ((c << 0x10) | (c >> 0x10)) + d;
+  b = (c ^ d ^ a) + LE_32((param+0x38)) + b - 0x021AC7F4;
+  b = ((b << 0x17) | (b >> 0x09)) + c;
+  a = (b ^ c ^ d) + LE_32((param+0x04)) + a - 0x5B4115BC;
+  a = ((a << 0x04) | (a >> 0x1c)) + b;
+  d = (a ^ b ^ c) + LE_32((param+0x10)) + d + 0x4BDECFA9;
+  d = ((d << 0x0b) | (d >> 0x15)) + a;
+  c = (d ^ a ^ b) + LE_32((param+0x1c)) + c - 0x0944B4A0;
+  c = ((c << 0x10) | (c >> 0x10)) + d;
+  b = (c ^ d ^ a) + LE_32((param+0x28)) + b - 0x41404390;
+  b = ((b << 0x17) | (b >> 0x09)) + c;
+  a = (b ^ c ^ d) + LE_32((param+0x34)) + a + 0x289B7EC6;
+  a = ((a << 0x04) | (a >> 0x1c)) + b;
+  d = (a ^ b ^ c) + LE_32((param+0x00)) + d - 0x155ED806;
+  d = ((d << 0x0b) | (d >> 0x15)) + a;
+  c = (d ^ a ^ b) + LE_32((param+0x0c)) + c - 0x2B10CF7B;
+  c = ((c << 0x10) | (c >> 0x10)) + d;
+  b = (c ^ d ^ a) + LE_32((param+0x18)) + b + 0x04881D05;
+  b = ((b << 0x17) | (b >> 0x09)) + c;
+  a = (b ^ c ^ d) + LE_32((param+0x24)) + a - 0x262B2FC7;
+  a = ((a << 0x04) | (a >> 0x1c)) + b;
+  d = (a ^ b ^ c) + LE_32((param+0x30)) + d - 0x1924661B;
+  d = ((d << 0x0b) | (d >> 0x15)) + a;
+  c = (d ^ a ^ b) + LE_32((param+0x3c)) + c + 0x1fa27cf8;
+  c = ((c << 0x10) | (c >> 0x10)) + d;
+  b = (c ^ d ^ a) + LE_32((param+0x08)) + b - 0x3B53A99B;
+  b = ((b << 0x17) | (b >> 0x09)) + c;
+  
+  a = ((~d | b) ^ c)  + LE_32((param+0x00)) + a - 0x0BD6DDBC;
+  a = ((a << 0x06) | (a >> 0x1a)) + b; 
+  d = ((~c | a) ^ b)  + LE_32((param+0x1c)) + d + 0x432AFF97;
+  d = ((d << 0x0a) | (d >> 0x16)) + a; 
+  c = ((~b | d) ^ a)  + LE_32((param+0x38)) + c - 0x546BDC59;
+  c = ((c << 0x0f) | (c >> 0x11)) + d; 
+  b = ((~a | c) ^ d)  + LE_32((param+0x14)) + b - 0x036C5FC7;
+  b = ((b << 0x15) | (b >> 0x0b)) + c; 
+  a = ((~d | b) ^ c)  + LE_32((param+0x30)) + a + 0x655B59C3;
+  a = ((a << 0x06) | (a >> 0x1a)) + b; 
+  d = ((~c | a) ^ b)  + LE_32((param+0x0C)) + d - 0x70F3336E;
+  d = ((d << 0x0a) | (d >> 0x16)) + a; 
+  c = ((~b | d) ^ a)  + LE_32((param+0x28)) + c - 0x00100B83;
+  c = ((c << 0x0f) | (c >> 0x11)) + d; 
+  b = ((~a | c) ^ d)  + LE_32((param+0x04)) + b - 0x7A7BA22F;
+  b = ((b << 0x15) | (b >> 0x0b)) + c; 
+  a = ((~d | b) ^ c)  + LE_32((param+0x20)) + a + 0x6FA87E4F;
+  a = ((a << 0x06) | (a >> 0x1a)) + b; 
+  d = ((~c | a) ^ b)  + LE_32((param+0x3c)) + d - 0x01D31920;
+  d = ((d << 0x0a) | (d >> 0x16)) + a; 
+  c = ((~b | d) ^ a)  + LE_32((param+0x18)) + c - 0x5CFEBCEC;
+  c = ((c << 0x0f) | (c >> 0x11)) + d; 
+  b = ((~a | c) ^ d)  + LE_32((param+0x34)) + b + 0x4E0811A1;
+  b = ((b << 0x15) | (b >> 0x0b)) + c; 
+  a = ((~d | b) ^ c)  + LE_32((param+0x10)) + a - 0x08AC817E;
+  a = ((a << 0x06) | (a >> 0x1a)) + b; 
+  d = ((~c | a) ^ b)  + LE_32((param+0x2c)) + d - 0x42C50DCB;
+  d = ((d << 0x0a) | (d >> 0x16)) + a; 
+  c = ((~b | d) ^ a)  + LE_32((param+0x08)) + c + 0x2AD7D2BB;
+  c = ((c << 0x0f) | (c >> 0x11)) + d; 
+  b = ((~a | c) ^ d)  + LE_32((param+0x24)) + b - 0x14792C6F;
+  b = ((b << 0x15) | (b >> 0x0b)) + c; 
+
+  a += LE_32(field);
+  b += LE_32(field+4);
+  c += LE_32(field+8);
+  d += LE_32(field+12);
+
+  LE_32C(field, a);
+  LE_32C(field+4, b);
+  LE_32C(field+8, c);
+  LE_32C(field+12, d);
+}
+
+static void
+call_hash (char * key, char * challenge, int len)
+{
+  uint8_t *ptr1, *ptr2;
+  uint32_t a, b, c, d, tmp;
+  
+  ptr1=(uint8_t *) (key+16);
+  ptr2=(uint8_t *) (key+20);
+  
+  a = LE_32(ptr1);
+  b = (a >> 3) & 0x3f;
+  a += len * 8;
+  LE_32C(ptr1, a);
+  
+  if (a < (len << 3))
+    ptr2 += 4;
+
+  tmp = LE_32(ptr2) + (len >> 0x1d);
+  LE_32C(ptr2, tmp);
+  a = 64 - b;
+  c = 0;  
+  if (a <= len)
+  {
+
+    memcpy(key+b+24, challenge, a);
+    hash(key, key+24);
+    c = a;
+    d = c + 0x3f;
+    
+    while ( d < len ) {
+      hash(key, challenge+d-0x3f);
+      d += 64;
+      c += 64;
+    }
+    b = 0;
+  }
+
+  memcpy(key+b+24, challenge+c, len-c);
+}
+
+static void
+real_calc_response_and_checksum (char * response, char * chksum,
+    char * challenge)
+{
+  int   ch_len, table_len, resp_len;
+  int   i;
+  char *ptr;
+  char  buf[128];
+  char field[128];
+  char zres[20];
+  char buf1[128];
+  char buf2[128];
+
+  /* initialize return values */
+  memset(response, 0, 64);
+  memset(chksum, 0, 34);
+
+  /* initialize buffer */
+  memset(buf, 0, 128);
+  ptr=buf;
+  BE_32C(ptr, 0xa1e9149d);
+  ptr+=4;
+  BE_32C(ptr, 0x0e6b3b59);
+  ptr+=4;
+
+  if ((ch_len = MIN (strlen (challenge), 56)) == 40) {
+    challenge[32] = 0;
+    ch_len = 32;
+  }
+  memcpy (ptr, challenge, ch_len);
+
+  /* xor challenge bytewise with xor_table */
+  table_len = MIN (strlen ((char *) xor_table), 56);
+  for (i = 0; i < table_len; i++)
+    ptr[i] = ptr[i] ^ xor_table[i];
+
+  /* initialize our field */
+  BE_32C (field,      0x01234567);
+  BE_32C (field +  4, 0x89ABCDEF);
+  BE_32C (field +  8, 0xFEDCBA98);
+  BE_32C (field + 12, 0x76543210);
+  BE_32C (field + 16, 0x00000000);
+  BE_32C (field + 20, 0x00000000);
+
+  /* calculate response */
+  call_hash (field, buf, 64);
+  memset (buf1, 0, 64);
+  *buf1 = 128;
+  memcpy (buf2, field+16, 8);
+  i = ( LE_32((buf2)) >> 3 ) & 0x3f;
+  if (i < 56)
+    i = 56 - i;
+  else
+    i = 120 - i;
+  call_hash (field, buf1, i);
+  call_hash (field, buf2, 8);
+  memcpy (zres, field, 16);
+
+  /* convert zres to ascii string */
+  for (i=0; i<16; i++ ) {
+    char a, b;
+
+    a = (zres[i] >> 4) & 15;
+    b = zres[i] & 15;
+
+    response[i*2]   = ((a<10) ? (a+48) : (a+87)) & 255;
+    response[i*2+1] = ((b<10) ? (b+48) : (b+87)) & 255;
+  }
+
+  /* add tail */
+  resp_len = strlen (response);
+  strcpy (&response[resp_len], "01d0a8e3");
+
+  /* calculate checksum */
+  for (i=0; i<resp_len/4; i++)
+    chksum[i] = response[i*4];
+}
+
+/* End of code copied from xine */
+
+static void
+b64_decode (gchar * buf, guint * size)
+{
+  char dtable[256];
+  int i, j, k = 0, n = strlen (buf);
+
+  for (i = 0; i < 255; i++)
+    dtable[i] = 0x80;
+  for (i = 'A'; i <= 'Z'; i++)
+    dtable[i] = 0 + (i - 'A');
+  for (i = 'a'; i <= 'z'; i++)
+    dtable[i] = 26 + (i - 'a');
+  for (i = '0'; i <= '9'; i++)
+    dtable[i] = 52 + (i - '0');
+  dtable['+'] = 62;
+  dtable['/'] = 63;
+  dtable['='] = 0;
+
+  for (j = 0; j < n; j += 4) {
+    char a[4], b[4];
+
+    for (i = 0; i < 4; i++) {
+      int c = buf[i + j];
+
+      if (dtable[c] & 0x80) {
+        if (size)
+          *size = 0;
+        return;
+      }
+      a[i] = (char) c;
+      b[i] = (char) dtable[c];
+    }
+    buf[k++] = (b[0] << 2) | (b[1] >> 4);
+    buf[k++] = (b[1] << 4) | (b[2] >> 2);
+    buf[k++] = (b[2] << 6) | b[3];
+    i = a[2] == '=' ? 1 : (a[3] == '=' ? 2 : 3);
+    if (i < 3) {
+      buf[k] = 0;
+      if (size)
+        *size = k;
+      return;
+    }
+  }
+  buf[k] = 0;
+  if (size)
+    *size = k;
+  return;
+}
+
+static gboolean
+gst_rtspsrc_open_real (GstRTSPSrc * src)
+{
+  RTSPResult res;
+  RTSPMessage request = { 0 };
+  RTSPMessage response = { 0 };
+  guint8 *data;
+  guint size, n_streams, os = 0;
+  SDPMessage sdp = { 0 };
+  const gchar *challenge1 = NULL;
+  gchar *value, checksum[34], challenge2[64], *s;
+  gint i;
+  guint p = 0;
+  guint32 max_bit_rate = 0, avg_bit_rate = 0;
+  guint32 max_packet_size = 0, avg_packet_size = 0;
+  guint32 start_time, preroll, duration = 0;
+  gchar *title, *author, *copyright, *comment;
+  GstBuffer *buf;
+  GstCaps *caps = NULL;
+
+  /* Open connection. */
+  GST_DEBUG_OBJECT (src, "opening connection...");
+  if ((res = rtsp_connection_open (src->url, &src->connection)) < 0)
+    goto could_not_open;
+
+  /* Create OPTIONS. */
+  GST_DEBUG_OBJECT (src, "create options...");
+  s = g_strdup_printf ("rtsp://%s:%d", src->url->host, src->url->port);
+  res = rtsp_message_init_request (&request, RTSP_OPTIONS, s);
+  g_free (s);
+  if (res < 0)
+    goto create_request_failed;
+  rtsp_message_add_header (&request, RTSP_HDR_USER_AGENT,
+      "RealMedia Player (" GST_PACKAGE_NAME ")");
+  rtsp_message_add_custom_header (&request, "ClientChallenge",
+      "9e26d33f2984236010ef6253fb1887f7");
+
+  /* Send OPTIONS. */
+  GST_DEBUG_OBJECT (src, "send options...");
+  if (!gst_rtspsrc_send (src, &request, &response, NULL))
+    goto send_error;
+
+  /* Parse OPTIONS. Note that at least the server on
+   * http://www.tagesschau.de does not advertise support for PLAY
+   * although it supports this command.
+   */
+  if (!gst_rtspsrc_parse_methods (src, &response)) {
+    rtsp_message_unset (&request);
+    rtsp_message_unset (&response);
+    rtsp_connection_close (src->connection);
+    rtsp_connection_free (src->connection);
+    return FALSE;
+  }
+  src->methods |= RTSP_PLAY;
+  rtsp_message_get_custom_header (&response, "RealChallenge1", &challenge1);
+  if (!challenge1)
+    goto no_challenge1;
+  real_calc_response_and_checksum (challenge2, checksum, (char *) challenge1);
+
+  /* Create DESCRIBE. */
+  GST_DEBUG_OBJECT (src, "create describe...");
+  if ((res = rtsp_message_init_request (&request, RTSP_DESCRIBE,
+          src->location)) < 0)
+    goto create_request_failed;
+  rtsp_message_add_header (&request, RTSP_HDR_ACCEPT, "application/sdp");
+
+  /* Send DESCRIBE. */
+  GST_DEBUG_OBJECT (src, "send describe...");
+  if (!gst_rtspsrc_send (src, &request, &response, NULL))
+    goto send_error;
+
+  /* Parse SDP. */
+  GST_DEBUG_OBJECT (src, "parse sdp...");
+  {
+    gchar *respcont = NULL;
+
+    rtsp_message_get_header (&response, RTSP_HDR_CONTENT_TYPE, &respcont);
+    if (respcont)
+      if (!g_ascii_strcasecmp (respcont, "application/sdp") == 0)
+        goto wrong_content_type;
+  }
+  rtsp_message_get_body (&response, &data, &size);
+  sdp_message_init (&sdp);
+  sdp_message_parse_buffer (data, size, &sdp);
+  if (src->debug)
+    sdp_message_dump (&sdp);
+
+  /* setup streams */
+  n_streams = sdp_message_medias_len (&sdp);
+  for (i = 0; i < n_streams; i++) {
+    SDPMedia *media = sdp_message_get_media (&sdp, i);
+    gchar *control_url;
+    gchar *setup_url;
+
+    GST_DEBUG_OBJECT (src, "setup media %d", i);
+    control_url = sdp_media_get_attribute_val (media, "control");
+    if (control_url == NULL) {
+      GST_DEBUG ("no control url found, skipping stream");
+      continue;
+    }
+    
+    /* Check absolute/relative URL. */
+    if (g_str_has_prefix (control_url, "rtsp://")) {
+      setup_url = g_strdup (control_url);
+    } else {
+      setup_url = g_strdup_printf ("%s/%s", src->location, control_url);
+    }
+
+    /* Create SETUP. */
+    res = rtsp_message_init_request (&request, RTSP_SETUP, setup_url);
+    g_free (setup_url);
+    if (res < 0)
+      goto create_request_failed;
+
+    rtsp_message_add_header (&request, RTSP_HDR_TRANSPORT,
+        "x-pn-tng/tcp,rtp/avp/tcp;unicast;mode=play");
+    if (!i) {
+      value = g_strdup_printf ("%s, sd=%s", challenge2, checksum);
+      rtsp_message_add_custom_header (&request, "RealChallenge2", value);
+      g_free (value);
+    }
+
+    /* Send SETUP. */
+    if (!gst_rtspsrc_send (src, &request, &response, NULL))
+      goto send_error;
+  }
+
+  src->data.realpad = gst_pad_new_from_static_template (&realtemplate, NULL);
+  caps = gst_caps_new_simple ("application/vnd.rn-realmedia", NULL);
+  gst_pad_set_caps (src->data.realpad, caps);
+  gst_caps_unref (caps);
+  gst_element_add_pad (GST_ELEMENT (src), src->data.realpad);
+
+  /* PROP */
+  for (i = 0; i < n_streams; i++) {
+    SDPMedia *media = sdp_message_get_media (&sdp, i);
+
+    value = sdp_media_get_attribute_val (media, "MaxBitRate");
+    if (value && !strncmp (value, "integer;", 8))
+      max_bit_rate += atoi (value + 8);
+    value = sdp_media_get_attribute_val (media, "AvgBitRate");
+    if (value && !strncmp (value, "integer;", 8))
+      avg_bit_rate += atoi (value + 8);
+    value = sdp_media_get_attribute_val (media, "MaxPacketSize");
+    if (value && !strncmp (value, "integer;", 8))
+      max_packet_size = MAX (max_packet_size, atoi (value + 8));
+    value = sdp_media_get_attribute_val (media, "AvgPacketSize");
+    if (value && !strncmp (value, "integer;", 8))
+      avg_packet_size = (avg_packet_size * i + atoi (value + 8)) / (i + 1);
+    value = sdp_media_get_attribute_val (media, "Duration");
+    if (value && !strncmp (value, "integer;", 8))
+      duration = MAX (duration, atoi (value + 8));
+  }
+  size = 50;
+  if (gst_pad_alloc_buffer (src->data.realpad, 0, size,
+        GST_PAD_CAPS (src->data.realpad), &buf) != GST_FLOW_OK)
+    goto alloc_buffer_failed;
+  memcpy (buf->data + 0, "PROP", 4);
+  GST_WRITE_UINT32_BE (buf->data +  4, 50             );
+  GST_WRITE_UINT16_BE (buf->data +  8, 0              );
+  GST_WRITE_UINT32_BE (buf->data + 10, max_bit_rate   );
+  GST_WRITE_UINT32_BE (buf->data + 14, avg_bit_rate   );
+  GST_WRITE_UINT32_BE (buf->data + 18, max_packet_size);
+  GST_WRITE_UINT32_BE (buf->data + 22, avg_packet_size);
+  GST_WRITE_UINT32_BE (buf->data + 26, 0              );
+  GST_WRITE_UINT32_BE (buf->data + 30, duration       );
+  GST_WRITE_UINT32_BE (buf->data + 34, 0              );
+  GST_WRITE_UINT32_BE (buf->data + 38, 0              );
+  GST_WRITE_UINT32_BE (buf->data + 42, 0              );
+  GST_WRITE_UINT16_BE (buf->data + 46, n_streams      );
+  GST_WRITE_UINT16_BE (buf->data + 48, 0              );
+  if (gst_pad_push (src->data.realpad, buf) != GST_FLOW_OK)
+    goto push_failed;
+
+  /* CONT */
+  os = size;
+  title     = sdp_message_get_attribute_val (&sdp, "Title"    );
+  author    = sdp_message_get_attribute_val (&sdp, "Author"   );
+  comment   = sdp_message_get_attribute_val (&sdp, "Comment"  );
+  copyright = sdp_message_get_attribute_val (&sdp, "Copyright");
+  if (!title)
+    title = "";
+  else if (!strncmp (title, "buffer;\"", 8)) {
+    title += 8;
+    title[strlen (title) - 1] = '\0';
+    b64_decode (title, NULL);
+  }
+  if (!author)
+    author = "";
+  else if (!strncmp (author, "buffer;\"", 8)) {
+    author += 8;
+    author[strlen (author) - 1] = '\0';
+    b64_decode (author, NULL);
+  }
+  if (!comment)
+    comment = "";
+  else if (!strncmp (comment, "buffer;\"", 8)) {
+    comment += 8;
+    comment[strlen (comment) - 1] = '\0';
+    b64_decode (comment, NULL);
+  }
+  if (!copyright)
+    copyright = "";
+  else if (!strncmp (copyright, "buffer;\"", 8)) {
+    copyright += 8;
+    copyright[strlen (copyright) - 1] = '\0';
+    b64_decode (copyright, NULL);
+  }
+  size = 20 + strlen (title) + strlen (author)
+    + strlen (comment) + strlen (copyright);
+  if (gst_pad_alloc_buffer (src->data.realpad, os, size,
+        GST_PAD_CAPS (src->data.realpad), &buf) != GST_FLOW_OK)
+    goto alloc_buffer_failed;
+  memcpy (buf->data, "CONT", 4);
+  GST_WRITE_UINT32_BE (buf->data + 4, size);
+  GST_WRITE_UINT16_BE (buf->data + 8, strlen (title));
+  memcpy (buf->data + 10, title, strlen (title));
+  p = strlen (title);
+  GST_WRITE_UINT16_BE (buf->data + 10 + p, strlen (author));
+  memcpy (buf->data + 12 + p, author, strlen (author));
+  p += strlen (author);
+  GST_WRITE_UINT16_BE (buf->data + 12 + p, strlen (copyright));
+  memcpy (buf->data + 14 + p, copyright, strlen (copyright));
+  p += strlen (copyright);
+  GST_WRITE_UINT16_BE (buf->data + 14 + p, strlen (comment));
+  memcpy (buf->data + 16 + p, comment, strlen (comment));
+  if (gst_pad_push (src->data.realpad, buf) != GST_FLOW_OK)
+    goto push_failed;
+
+  /* MDPR */
+  for (i = 0; i < n_streams; i++) {
+    SDPMedia *media = sdp_message_get_media (&sdp, i);
+    gchar *opaque_data, *asm_rule_book, *type_specific_data;
+    guint opaque_data_len;
+    gchar *stream_name, *mime_type;
+    guint16 num_rules, j, sel, codec;
+    guint32 type_specific_data_len, len;
+
+    os = size;
+    value = sdp_media_get_attribute_val (media, "MaxBitRate");
+    if (value && !strncmp (value, "integer;", 8))
+      max_bit_rate = atoi (value + 8);
+    else
+      max_bit_rate = 0;
+    value = sdp_media_get_attribute_val (media, "AvgBitRate");
+    if (value && !strncmp (value, "integer;", 8))
+      avg_bit_rate = atoi (value + 8);
+    else
+      avg_bit_rate = 0;
+    value = sdp_media_get_attribute_val (media, "MaxPacketSize");
+    if (value && !strncmp (value, "integer;", 8))
+      max_packet_size = atoi (value + 8);
+    else
+      max_packet_size = 0;
+    value = sdp_media_get_attribute_val (media, "AvgPacketSize");
+    if (value && !strncmp (value, "integer;", 8))
+      avg_packet_size = atoi (value + 8);
+    else
+      avg_packet_size = 0;
+    value = sdp_media_get_attribute_val (media, "StartTime");
+    if (value && !strncmp (value, "integer;", 8))
+      start_time = atoi (value + 8);
+    else
+      start_time = 0;
+    value = sdp_media_get_attribute_val (media, "Preroll");
+    if (value && !strncmp (value, "integer;", 8))
+      preroll = atoi (value + 8);
+    else
+      preroll = 0;
+    value = sdp_media_get_attribute_val (media, "Duration");
+    if (value && !strncmp (value, "integer;", 8))
+      duration = atoi (value + 8);
+    else
+      duration = 0;
+    value = sdp_media_get_attribute_val (media, "StreamName");
+    if (value && !strncmp (value, "string;\"", 8)) {
+      stream_name = value + 8;
+      stream_name[strlen (stream_name) - 1] = '\0';
+    } else
+      stream_name = "";
+    value = sdp_media_get_attribute_val (media, "mimetype");
+    if (value && !strncmp (value, "string;\"", 8)) {
+      mime_type = value + 8;
+      mime_type[strlen (mime_type) - 1] = '\0';
+    } else
+      mime_type = "";
+
+    /* FIXME: Depending on the current bandwidth, we need to select one
+     * bandwith out of a list offered by the server. Someone needs to write
+     * a parser for strings like
+     *
+     * #($Bandwidth < 67959),TimestampDelivery=T,DropByN=T,priority=9;
+     * #($Bandwidth >= 67959) && ($Bandwidth < 167959),AverageBandwidth=67959,
+     * Priority=9;#($Bandwidth >= 67959) && ($Bandwidth < 167959),
+     * AverageBandwidth=0,Priority=5,OnDepend=\"1\";
+     * #($Bandwidth >= 167959) && ($Bandwidth < 267959),
+     * AverageBandwidth=167959,Priority=9;
+     * #($Bandwidth >= 167959) && ($Bandwidth < 267959),AverageBandwidth=0,
+     * Priority=5,OnDepend=\"3\";#($Bandwidth >= 267959),
+     * AverageBandwidth=267959,Priority=9;#($Bandwidth >= 267959),
+     * AverageBandwidth=0,Priority=5,OnDepend=\"5\";
+     *
+     * As I don't know how to do that, I just use the first entry (sel = 0).
+     * But to give you a starting point, I offer you above string
+     * in the variable 'asm_rule_book'.
+     */
+    value = sdp_media_get_attribute_val (media, "ASMRuleBook");
+    if (value && !strncmp (value, "string;\"", 8)) {
+      asm_rule_book = value + 8;
+      asm_rule_book[strlen (asm_rule_book) - 1] = '\0';
+    } else
+      asm_rule_book = "";
+    sel = 0;
+
+    value = sdp_media_get_attribute_val (media, "OpaqueData");
+    if (strncmp (value, "buffer;\"", 8))
+      goto strange_opaque_data;
+    opaque_data = value + 8;
+    opaque_data[strlen (opaque_data) - 1] = '\0';
+    b64_decode (opaque_data, &opaque_data_len);
+
+    if (opaque_data_len < 4)
+      goto strange_opaque_data;
+    if (strncmp (opaque_data, "MLTI", 4))
+      goto strange_opaque_data;
+    opaque_data += 4;
+    opaque_data_len -= 4;
+
+    if (opaque_data_len < 2)
+      goto strange_opaque_data;
+    num_rules = GST_READ_UINT16_BE (opaque_data);
+    opaque_data += 2;
+    opaque_data_len -= 2;
+
+    if (sel >= num_rules)
+      goto strange_opaque_data;
+
+    if (opaque_data_len < 2 * sel)
+      goto strange_opaque_data;
+    opaque_data += 2 * sel;
+    opaque_data_len -= 2 * sel;
+
+    if (opaque_data_len < 2)
+      goto strange_opaque_data;
+    codec = GST_READ_UINT16_BE (opaque_data);
+    opaque_data += 2;
+    opaque_data_len -= 2;
+
+    if (opaque_data_len < 2 * (num_rules - sel - 1))
+      goto strange_opaque_data;
+    opaque_data += 2 * (num_rules - sel - 1);
+    opaque_data_len -= 2 * (num_rules - sel - 1);
+
+    if (opaque_data_len < 2)
+      goto strange_opaque_data;
+    num_rules = GST_READ_UINT16_BE (opaque_data);
+    opaque_data += 2;
+    opaque_data_len -= 2;
+
+    if (codec > num_rules)
+      goto strange_opaque_data;
+
+    for (j = 0; j < codec; j++) {
+      if (opaque_data_len < 4)
+        goto strange_opaque_data;
+      len = GST_READ_UINT32_BE (opaque_data);
+      opaque_data += 4;
+      opaque_data_len -= 4;
+
+      if (opaque_data_len < len)
+        goto strange_opaque_data;
+      opaque_data += len;
+      opaque_data_len -= len;
+    }
+
+    if (opaque_data_len < 4)
+      goto strange_opaque_data;
+    type_specific_data_len = GST_READ_UINT32_BE (opaque_data);
+    opaque_data += 4;
+    opaque_data_len -= 4;
+
+    if (opaque_data_len < type_specific_data_len)
+      goto strange_opaque_data;
+    type_specific_data = opaque_data;
+
+    size = 46 + strlen (stream_name) + strlen (mime_type)
+      + type_specific_data_len;
+    if (gst_pad_alloc_buffer (src->data.realpad, os, size,
+          GST_PAD_CAPS (src->data.realpad), &buf) != GST_FLOW_OK)
+      goto alloc_buffer_failed;
+    memcpy (buf->data, "MDPR", 4);
+    GST_WRITE_UINT32_BE (buf->data + 4, size);
+    GST_WRITE_UINT16_BE (buf->data + 8, 0);
+    GST_WRITE_UINT16_BE (buf->data + 10, i);
+    GST_WRITE_UINT32_BE (buf->data + 12, max_bit_rate);
+    GST_WRITE_UINT32_BE (buf->data + 16, avg_bit_rate);
+    GST_WRITE_UINT32_BE (buf->data + 20, max_packet_size);
+    GST_WRITE_UINT32_BE (buf->data + 24, avg_packet_size);
+    GST_WRITE_UINT32_BE (buf->data + 28, start_time);
+    GST_WRITE_UINT32_BE (buf->data + 32, preroll);
+    GST_WRITE_UINT32_BE (buf->data + 36, duration);
+    buf->data[40] = strlen (stream_name);
+    memcpy (buf->data + 41, stream_name, strlen (stream_name));
+    p = strlen (stream_name);
+    buf->data[41 + p] = strlen (mime_type);
+    memcpy (buf->data + 42 + p, mime_type, strlen (mime_type));
+    p += strlen (mime_type);
+    GST_WRITE_UINT32_BE (buf->data + 42 + p, type_specific_data_len);
+    memcpy (buf->data + 46 + p, type_specific_data, type_specific_data_len);
+    if (gst_pad_push (src->data.realpad, buf) != GST_FLOW_OK)
+      goto push_failed;
+  }
+
+  /* DATA */
+  os = size;
+  size =  18;
+  if (gst_pad_alloc_buffer (src->data.realpad, os, size,
+        GST_PAD_CAPS (src->data.realpad), &buf) != GST_FLOW_OK)
+    goto alloc_buffer_failed;
+  memcpy (buf->data, "DATA", 4);
+  GST_WRITE_UINT32_BE (buf->data + 4, size);
+  GST_WRITE_UINT16_BE (buf->data + 8, 0);
+  GST_WRITE_UINT32_BE (buf->data + 10, 0); /* number of packets */
+  GST_WRITE_UINT32_BE (buf->data + 14, 0); /* next data header */
+  if (gst_pad_push (src->data.realpad, buf) != GST_FLOW_OK)
+    goto push_failed;
+
+  /* create SET_PARAMETER */
+  if ((res = rtsp_message_init_request (&request, RTSP_SET_PARAMETER,
+          src->location)) < 0)
+    goto create_request_failed;
+  rtsp_message_add_custom_header (&request, "Subscribe",
+      "stream=0;rule=5,stream=0;rule=6,stream=1;rule=0,stream=1;rule=1");
+
+  /* send SET_PARAMETER */
+  if (!gst_rtspsrc_send (src, &request, &response, NULL))
+    goto send_error;
+
+  rtsp_message_unset (&request);
+  rtsp_message_unset (&response);
+  gst_element_no_more_pads (GST_ELEMENT (src));
+  return TRUE;
+
+  /* ERRORS */
+could_not_open:
+  {
+    GST_ELEMENT_ERROR (src, RESOURCE, OPEN_READ_WRITE,
+        ("Could not open connection."), (NULL));
+    goto reset;
+  }
+no_challenge1:
+  {
+    GST_ELEMENT_ERROR (src, RESOURCE, READ,
+        ("Could not find challenge tag."), (NULL));
+    goto reset;
+  }
+create_request_failed:
+  {
+    GST_ELEMENT_ERROR (src, LIBRARY, INIT,
+        ("Could not create request."), (NULL));
+    goto reset;
+  }
+send_error:
+  {
+    GST_ELEMENT_ERROR (src, RESOURCE, WRITE,
+        ("Could not send message."), (NULL));
+    goto reset;
+  }
+wrong_content_type:
+  {
+    GST_ELEMENT_ERROR (src, RESOURCE, WRITE,
+        ("Server does not support SDP."), (NULL));
+    goto reset;
+  }
+alloc_buffer_failed:
+  {
+    GST_ELEMENT_ERROR (src, RESOURCE, WRITE,
+        ("Allocation of buffer failed."), (NULL));
+    goto reset;
+  }
+push_failed:
+  {
+    GST_ELEMENT_ERROR (src, RESOURCE, WRITE,
+        ("Push failed."), (NULL));
+    goto reset;
+  }
+strange_opaque_data:
+  {
+    GST_ELEMENT_ERROR (src, RESOURCE, WRITE,
+        ("Strange opaque data."), (NULL));
+    goto reset;
+  }
+reset:
+  rtsp_message_unset (&request);
+  rtsp_message_unset (&response);
+  if (src->connection) {
+    rtsp_connection_close (src->connection);
+    rtsp_connection_free (src->connection);
+    src->connection = NULL;
+  }
+  if (src->data.realpad) {
+    gst_element_remove_pad (GST_ELEMENT (src), src->data.realpad);
+    src->data.realpad = NULL;
+  }
+  return FALSE;
+}
+
Index: gstrtspsrc.h
===================================================================
RCS file: /cvs/gstreamer/gst-plugins-good/gst/rtsp/gstrtspsrc.h,v
retrieving revision 1.10
diff -u -3 -p -b -B -d -r1.10 gstrtspsrc.h
--- gstrtspsrc.h	22 Aug 2006 16:45:37 -0000	1.10
+++ gstrtspsrc.h	31 Aug 2006 20:40:07 -0000
@@ -60,10 +60,6 @@ typedef enum
 typedef struct _GstRTSPStream GstRTSPStream;
 
 struct _GstRTSPStream {
-  gint        id;
-
-  GstRTSPSrc *parent;
-
   GstFlowReturn last_ret;
 
   /* for interleaved mode */
@@ -75,9 +71,6 @@ struct _GstRTSPStream {
   GstElement *rtpsrc;
   GstElement *rtcpsrc;
 
-  /* our udp sink back to the server */
-  GstElement *rtcpsink;
-
   /* the RTP decoder */
   GstElement *rtpdec;
   GstPad     *rtpdecrtp;
@@ -85,23 +78,30 @@ struct _GstRTSPStream {
 };
 
 struct _GstRTSPSrc {
-  GstElement element;
+  GstBin element;
+
+  /* Either REAL stream or standard RTSP */
+  gboolean is_real;
+  union {
+    GstPad *realpad;
+    struct {
 
   /* task and mutex for interleaved mode */
   gboolean         interleaved;
   GstTask         *task;
   GStaticRecMutex *stream_rec_lock;
 
-  gint             numstreams;
   GList           *streams;
+    } rtsp;
+  } data;
 
+  RTSPUrl       *url;
   gchar           *location;
   gboolean         debug;
   guint   	   retry;
 
   GstRTSPProto     protocols;
-  /* supported options */
-  gint             options;
+  RTSPMethod     methods;
 
   RTSPConnection  *connection;
   RTSPMessage     *request;
@@ -109,7 +109,7 @@ struct _GstRTSPSrc {
 };
 
 struct _GstRTSPSrcClass {
-  GstElementClass parent_class;
+  GstBinClass parent_class;
 };
 
 GType gst_rtspsrc_get_type(void);
Index: rtspconnection.c
===================================================================
RCS file: /cvs/gstreamer/gst-plugins-good/gst/rtsp/rtspconnection.c,v
retrieving revision 1.14
diff -u -3 -p -b -B -d -r1.14 rtspconnection.c
--- rtspconnection.c	24 Jul 2006 11:00:34 -0000	1.14
+++ rtspconnection.c	31 Aug 2006 20:40:07 -0000
@@ -135,7 +135,6 @@ rtsp_connection_create (gint fd, RTSPCon
   newconn->fd = fd;
   newconn->cseq = 0;
   newconn->session_id[0] = 0;
-  newconn->state = RTSP_STATE_INIT;
 
   *conn = newconn;
 
@@ -150,6 +149,13 @@ append_header (gint key, gchar * value, 
   g_string_append_printf (str, "%s: %s\r\n", keystr, value);
 }
 
+static void
+append_custom_header (gpointer key, gpointer value, gpointer data)
+{
+  g_string_append_printf ((GString *) data, "%s: %s\r\n", (gchar *) key,
+      (gchar *) value);
+}
+
 RTSPResult
 rtsp_connection_send (RTSPConnection * conn, RTSPMessage * message)
 {
@@ -186,6 +192,7 @@ rtsp_connection_send (RTSPConnection * c
 
   /* append headers */
   g_hash_table_foreach (message->hdr_fields, (GHFunc) append_header, str);
+    g_hash_table_foreach (message->custom_hdr_fields, append_custom_header, str);
 
   /* append Content-Length and body if needed */
   if (message->body != NULL && message->body_size > 0) {
@@ -333,7 +340,7 @@ parse_response_status (gchar * buffer, R
   while (g_ascii_isspace (*bptr))
     bptr++;
 
-  rtsp_message_init_response (code, bptr, NULL, msg);
+  rtsp_message_init_response (msg, code, bptr, NULL);
 
   return RTSP_OK;
 
@@ -365,7 +372,7 @@ parse_request_line (gchar * buffer, RTSP
   if (strcmp (versionstr, "RTSP/1.0") != 0)
     goto wrong_version;
 
-  rtsp_message_init_request (method, urlstr, msg);
+  rtsp_message_init_request (msg, method, urlstr);
 
   return RTSP_OK;
 
@@ -397,11 +404,12 @@ parse_line (gchar * buffer, RTSPMessage 
   bptr++;
 
   field = rtsp_find_header_field (key);
-  if (field != -1) {
     while (g_ascii_isspace (*bptr))
       bptr++;
+  if (field != -1)
     rtsp_message_add_header (msg, field, bptr);
-  }
+  else
+    rtsp_message_add_custom_header (msg, key, bptr);
 
   return RTSP_OK;
 }
@@ -490,7 +498,7 @@ rtsp_connection_receive (RTSPConnection 
         goto error;
 
       /* now we create a data message */
-      rtsp_message_init_data ((gint) c, msg);
+      rtsp_message_init_data (msg, (gint) c);
 
       /* next two bytes are the length of the data */
       ret = read (conn->fd, &size, 2);
Index: rtspconnection.h
===================================================================
RCS file: /cvs/gstreamer/gst-plugins-good/gst/rtsp/rtspconnection.h,v
retrieving revision 1.4
diff -u -3 -p -b -B -d -r1.4 rtspconnection.h
--- rtspconnection.h	6 Dec 2005 19:44:55 -0000	1.4
+++ rtspconnection.h	31 Aug 2006 20:40:07 -0000
@@ -34,8 +34,6 @@ typedef struct _RTSPConnection
 
   gint          cseq;                   /* sequence number */
   gchar         session_id[512];        /* session id */
-  
-  RTSPState     state;                  /* state of the connection */
 } RTSPConnection;
 
 /* opening/closing a connection */
Index: rtspdefs.h
===================================================================
RCS file: /cvs/gstreamer/gst-plugins-good/gst/rtsp/rtspdefs.h,v
retrieving revision 1.6
diff -u -3 -p -b -B -d -r1.6 rtspdefs.h
--- rtspdefs.h	10 Jul 2006 16:41:57 -0000	1.6
+++ rtspdefs.h	31 Aug 2006 20:40:07 -0000
@@ -50,13 +50,6 @@ typedef enum {
 } RTSPFamily;
 
 typedef enum {
-  RTSP_STATE_INIT,
-  RTSP_STATE_READY,
-  RTSP_STATE_PLAYING,
-  RTSP_STATE_RECORDING,
-} RTSPState;
-
-typedef enum {
   RTSP_DESCRIBE         = (1 <<  0),
   RTSP_ANNOUNCE         = (1 <<  1),
   RTSP_GET_PARAMETER    = (1 <<  2),
Index: rtspmessage.c
===================================================================
RCS file: /cvs/gstreamer/gst-plugins-good/gst/rtsp/rtspmessage.c,v
retrieving revision 1.4
diff -u -3 -p -b -B -d -r1.4 rtspmessage.c
--- rtspmessage.c	9 Feb 2006 14:20:14 -0000	1.4
+++ rtspmessage.c	31 Aug 2006 20:40:07 -0000
@@ -20,35 +20,32 @@
 #include "rtspmessage.h"
 
 RTSPResult
-rtsp_message_new_request (RTSPMethod method, gchar * uri, RTSPMessage ** msg)
-{
-  RTSPMessage *newmsg;
-
-  if (msg == NULL || uri == NULL)
-    return RTSP_EINVAL;
-
-  newmsg = g_new0 (RTSPMessage, 1);
-
-  *msg = newmsg;
-
-  return rtsp_message_init_request (method, uri, newmsg);
-}
-
-RTSPResult
-rtsp_message_init_request (RTSPMethod method, gchar * uri, RTSPMessage * msg)
+rtsp_message_unset (RTSPMessage * msg)
 {
-  if (msg == NULL || uri == NULL)
+  if (!msg)
     return RTSP_EINVAL;
 
-  msg->type = RTSP_MESSAGE_REQUEST;
-  msg->type_data.request.method = method;
-  g_free (msg->type_data.request.uri);
-  msg->type_data.request.uri = g_strdup (uri);
-
   if (msg->hdr_fields != NULL)
     g_hash_table_destroy (msg->hdr_fields);
-  msg->hdr_fields =
-      g_hash_table_new_full (g_direct_hash, g_direct_equal, NULL, g_free);
+  if (msg->custom_hdr_fields != NULL)
+    g_hash_table_destroy (msg->custom_hdr_fields);
+
+  switch (msg->type) {
+    case RTSP_MESSAGE_REQUEST:
+      if (msg->type_data.request.uri) {
+        g_free (msg->type_data.request.uri);
+        msg->type_data.request.uri = NULL;
+      }
+      break;
+    case RTSP_MESSAGE_RESPONSE:
+      if (msg->type_data.response.reason) {
+        g_free (msg->type_data.response.reason);
+        msg->type_data.response.reason = NULL;
+      }
+      break;
+    default:
+      break;
+  }
 
   if (msg->body) {
     g_free (msg->body);
@@ -60,43 +57,43 @@ rtsp_message_init_request (RTSPMethod me
 }
 
 RTSPResult
-rtsp_message_new_response (RTSPStatusCode code, gchar * reason,
-    RTSPMessage * request, RTSPMessage ** msg)
+rtsp_message_init_request (RTSPMessage * msg, RTSPMethod method, gchar * uri)
 {
-  RTSPMessage *newmsg;
-
-  if (msg == NULL || reason == NULL || request == NULL)
+  if (msg == NULL || uri == NULL)
     return RTSP_EINVAL;
 
-  newmsg = g_new0 (RTSPMessage, 1);
+  rtsp_message_unset (msg);
 
-  *msg = newmsg;
+  msg->type = RTSP_MESSAGE_REQUEST;
+  msg->type_data.request.method = method;
+  msg->type_data.request.uri = g_strdup (uri);
 
-  return rtsp_message_init_response (code, reason, request, newmsg);
+  msg->hdr_fields =
+      g_hash_table_new_full (g_direct_hash, g_direct_equal, NULL, g_free);
+  msg->custom_hdr_fields =
+    g_hash_table_new (g_str_hash, g_str_equal);
+
+  return RTSP_OK;
 }
 
 RTSPResult
-rtsp_message_init_response (RTSPStatusCode code, gchar * reason,
-    RTSPMessage * request, RTSPMessage * msg)
+rtsp_message_init_response (RTSPMessage * msg, RTSPStatusCode code,
+    gchar * reason, RTSPMessage * request)
 {
   if (reason == NULL || msg == NULL)
     return RTSP_EINVAL;
 
+  rtsp_message_unset (msg);
+
   msg->type = RTSP_MESSAGE_RESPONSE;
   msg->type_data.response.code = code;
   g_free (msg->type_data.response.reason);
   msg->type_data.response.reason = g_strdup (reason);
 
-  if (msg->hdr_fields != NULL)
-    g_hash_table_destroy (msg->hdr_fields);
   msg->hdr_fields =
       g_hash_table_new_full (g_direct_hash, g_direct_equal, NULL, g_free);
-
-  if (msg->body) {
-    g_free (msg->body);
-    msg->body = NULL;
-  }
-  msg->body_size = 0;
+  msg->custom_hdr_fields =
+    g_hash_table_new (g_str_hash, g_str_equal);
 
   if (request) {
     /* FIXME copy headers */
@@ -106,11 +103,13 @@ rtsp_message_init_response (RTSPStatusCo
 }
 
 RTSPResult
-rtsp_message_init_data (gint channel, RTSPMessage * msg)
+rtsp_message_init_data (RTSPMessage * msg, gint channel)
 {
   if (msg == NULL)
     return RTSP_EINVAL;
 
+  rtsp_message_unset (msg);
+
   msg->type = RTSP_MESSAGE_DATA;
   msg->type_data.data.channel = channel;
 
@@ -139,7 +138,7 @@ rtsp_message_remove_header (RTSPMessage 
 
   g_hash_table_remove (msg->hdr_fields, GINT_TO_POINTER (field));
 
-  return RTSP_ENOTIMPL;
+  return RTSP_OK;
 }
 
 RTSPResult
@@ -161,6 +160,48 @@ rtsp_message_get_header (RTSPMessage * m
 }
 
 RTSPResult
+rtsp_message_add_custom_header (RTSPMessage * msg, const gchar * field,
+    const gchar * value)
+{
+  if (msg == NULL || field == NULL || value == NULL)
+    return RTSP_EINVAL;
+
+  g_hash_table_insert (msg->custom_hdr_fields, g_strdup (field),
+      g_strdup (value));
+
+  return RTSP_OK;
+}
+
+RTSPResult
+rtsp_message_remove_custom_header (RTSPMessage * msg, const gchar * field)
+{
+  if (msg == NULL || field == NULL)
+    return RTSP_EINVAL;
+
+  g_hash_table_remove (msg->custom_hdr_fields, field);
+
+  return RTSP_OK;
+}
+
+RTSPResult
+rtsp_message_get_custom_header (RTSPMessage * msg, const gchar * field,
+    const gchar ** value)
+{
+  gchar *val;
+
+  if (msg == NULL || field == NULL || value == NULL)
+    return RTSP_EINVAL;
+
+  val = g_hash_table_lookup (msg->custom_hdr_fields, field);
+  if (val == NULL)
+    return RTSP_ENOTIMPL;
+
+  *value = val;
+
+  return RTSP_OK;
+}
+
+RTSPResult
 rtsp_message_set_body (RTSPMessage * msg, guint8 * data, guint size)
 {
   if (msg == NULL)
@@ -236,6 +277,12 @@ dump_key_value (gpointer key, gpointer v
       (gchar *) value);
 }
 
+static void
+dump_custom_key_value (gpointer key, gpointer value, gpointer data)
+{
+  g_print ("   key: '%s', value: '%s'\n", (gchar *) key, (gchar *) value);
+}
+
 RTSPResult
 rtsp_message_dump (RTSPMessage * msg)
 {
@@ -253,6 +300,7 @@ rtsp_message_dump (RTSPMessage * msg)
     g_print ("   uri:    '%s'\n", msg->type_data.request.uri);
     g_print (" headers:\n");
     g_hash_table_foreach (msg->hdr_fields, dump_key_value, NULL);
+    g_hash_table_foreach (msg->custom_hdr_fields, dump_custom_key_value, NULL);
     g_print (" body:\n");
     rtsp_message_get_body (msg, &data, &size);
     dump_mem (data, size);
@@ -263,6 +311,7 @@ rtsp_message_dump (RTSPMessage * msg)
     g_print ("   reason: '%s'\n", msg->type_data.response.reason);
     g_print (" headers:\n");
     g_hash_table_foreach (msg->hdr_fields, dump_key_value, NULL);
+    g_hash_table_foreach (msg->custom_hdr_fields, dump_custom_key_value, NULL);
     rtsp_message_get_body (msg, &data, &size);
     g_print (" body: length %d\n", size);
     dump_mem (data, size);
Index: rtspmessage.h
===================================================================
RCS file: /cvs/gstreamer/gst-plugins-good/gst/rtsp/rtspmessage.h,v
retrieving revision 1.4
diff -u -3 -p -b -B -d -r1.4 rtspmessage.h
--- rtspmessage.h	6 Dec 2005 19:44:55 -0000	1.4
+++ rtspmessage.h	31 Aug 2006 20:40:07 -0000
@@ -52,28 +52,27 @@ typedef struct _RTSPMessage
   } type_data;
 
   GHashTable    *hdr_fields;
+  GHashTable    *custom_hdr_fields;
 
   guint8        *body;
   guint          body_size;
 
 } RTSPMessage;
 
-RTSPResult      rtsp_message_new_request        (RTSPMethod method, gchar *uri, RTSPMessage **msg);
-RTSPResult      rtsp_message_init_request       (RTSPMethod method, gchar *uri, RTSPMessage *msg);
-
-RTSPResult      rtsp_message_new_response       (RTSPStatusCode code, gchar *reason, 
-                                                 RTSPMessage *request, RTSPMessage **msg);
-RTSPResult      rtsp_message_init_response      (RTSPStatusCode code, gchar *reason, 
-                                                 RTSPMessage *request, RTSPMessage *msg);
-RTSPResult      rtsp_message_init_data          (gint channel, RTSPMessage *msg);
-
-RTSPResult      rtsp_message_free               (RTSPMessage *msg);
+RTSPResult rtsp_message_init_request  (RTSPMessage *msg, RTSPMethod method, gchar *uri);
+RTSPResult rtsp_message_init_response (RTSPMessage *msg, RTSPStatusCode code, gchar *reason, RTSPMessage *response);
+RTSPResult rtsp_message_init_data     (RTSPMessage *msg, gint channel);
+RTSPResult rtsp_message_unset         (RTSPMessage *msg);
 
 
 RTSPResult      rtsp_message_add_header         (RTSPMessage *msg, RTSPHeaderField field, gchar *value);
 RTSPResult      rtsp_message_remove_header      (RTSPMessage *msg, RTSPHeaderField field);
 RTSPResult      rtsp_message_get_header         (RTSPMessage *msg, RTSPHeaderField field, gchar **value);
 
+RTSPResult rtsp_message_add_custom_header    (RTSPMessage *msg, const gchar *field, const gchar *value);
+RTSPResult rtsp_message_remove_custom_header (RTSPMessage *msg, const gchar *field);
+RTSPResult rtsp_message_get_custom_header    (RTSPMessage *msg, const gchar *field, const gchar **value);
+
 RTSPResult      rtsp_message_set_body           (RTSPMessage *msg, guint8 *data, guint size);
 RTSPResult      rtsp_message_take_body          (RTSPMessage *msg, guint8 *data, guint size);
 RTSPResult      rtsp_message_get_body           (RTSPMessage *msg, guint8 **data, guint *size);
Index: rtsptransport.c
===================================================================
RCS file: /cvs/gstreamer/gst-plugins-good/gst/rtsp/rtsptransport.c,v
retrieving revision 1.5
diff -u -3 -p -b -B -d -r1.5 rtsptransport.c
--- rtsptransport.c	15 Feb 2006 10:15:47 -0000	1.5
+++ rtsptransport.c	31 Aug 2006 20:40:07 -0000
@@ -100,6 +100,8 @@ rtsp_transport_parse (gchar * str, RTSPT
       transport->lower_transport = RTSP_LOWER_TRANS_TCP;
     } else if (g_str_has_prefix (split[i], "rtp/avp")) {
       transport->lower_transport = RTSP_LOWER_TRANS_UDP;
+    } else if (g_str_has_prefix (split[i], "x-pn-tng/tcp")) {
+      transport->lower_transport = RTSP_LOWER_TRANS_TCP;
     } else if (g_str_has_prefix (split[i], "multicast")) {
       transport->multicast = TRUE;
     } else if (g_str_has_prefix (split[i], "unicast")) {
Index: rtspurl.c
===================================================================
RCS file: /cvs/gstreamer/gst-plugins-good/gst/rtsp/rtspurl.c,v
retrieving revision 1.6
diff -u -3 -p -b -B -d -r1.6 rtspurl.c
--- rtspurl.c	8 May 2006 14:35:20 -0000	1.6
+++ rtspurl.c	31 Aug 2006 20:40:07 -0000
@@ -104,6 +104,8 @@ invalid:
 void
 rtsp_url_free (RTSPUrl * url)
 {
+  if (!url)
+    return;
   g_free (url->user);
   g_free (url->passwd);
   g_free (url->host);
