Ported to 0.9.
authorWim Taymans <wim.taymans@gmail.com>
Wed, 11 May 2005 07:44:44 +0000 (07:44 +0000)
committerWim Taymans <wim.taymans@gmail.com>
Wed, 11 May 2005 07:44:44 +0000 (07:44 +0000)
Original commit message from CVS:
Ported to 0.9.
Set up transports, init UDP ports, init RTP session managers.

23 files changed:
ChangeLog
gst/rtsp/.gitignore [new file with mode: 0644]
gst/rtsp/Makefile.am [new file with mode: 0644]
gst/rtsp/gstrtsp.c [new file with mode: 0644]
gst/rtsp/gstrtsp.h [new file with mode: 0644]
gst/rtsp/gstrtspsrc.c [new file with mode: 0644]
gst/rtsp/gstrtspsrc.h [new file with mode: 0644]
gst/rtsp/rtsp.h [new file with mode: 0644]
gst/rtsp/rtspconnection.c [new file with mode: 0644]
gst/rtsp/rtspconnection.h [new file with mode: 0644]
gst/rtsp/rtspdefs.c [new file with mode: 0644]
gst/rtsp/rtspdefs.h [new file with mode: 0644]
gst/rtsp/rtspmessage.c [new file with mode: 0644]
gst/rtsp/rtspmessage.h [new file with mode: 0644]
gst/rtsp/rtspstream.h [new file with mode: 0644]
gst/rtsp/rtsptransport.c [new file with mode: 0644]
gst/rtsp/rtsptransport.h [new file with mode: 0644]
gst/rtsp/rtspurl.c [new file with mode: 0644]
gst/rtsp/rtspurl.h [new file with mode: 0644]
gst/rtsp/sdp.h [new file with mode: 0644]
gst/rtsp/sdpmessage.c [new file with mode: 0644]
gst/rtsp/sdpmessage.h [new file with mode: 0644]
gst/rtsp/test.c [new file with mode: 0644]

index a67e3d5a3948d25f740836117066e8f07408b38f..3953beaa905f2fad4d212f7320c0fbcdf459dc91 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,66 @@
+2005-05-11  Wim Taymans  <wim@fluendo.com>
+
+       * gst/rtsp/.cvsignore:
+       * gst/rtsp/Makefile.am:
+       * gst/rtsp/gstrtsp.c: (plugin_init):
+       * gst/rtsp/gstrtsp.h:
+       * gst/rtsp/gstrtspsrc.c: (gst_rtsp_proto_get_type),
+       (gst_rtspsrc_get_type), (gst_rtspsrc_base_init),
+       (gst_rtspsrc_class_init), (gst_rtspsrc_init),
+       (gst_rtspsrc_set_property), (gst_rtspsrc_get_property),
+       (gst_rtspsrc_create_stream), (rtspsrc_add_element),
+       (gst_rtspsrc_stream_setup_rtp),
+       (gst_rtspsrc_stream_configure_transport), (find_stream),
+       (gst_rtspsrc_loop), (gst_rtspsrc_send), (gst_rtspsrc_open),
+       (gst_rtspsrc_close), (gst_rtspsrc_play), (gst_rtspsrc_pause),
+       (gst_rtspsrc_activate), (gst_rtspsrc_change_state):
+       * gst/rtsp/gstrtspsrc.h:
+       * gst/rtsp/rtsp.h:
+       * gst/rtsp/rtspconnection.c: (rtsp_connection_open),
+       (rtsp_connection_create), (append_header), (rtsp_connection_send),
+       (read_line), (read_string), (read_key), (parse_response_status),
+       (parse_line), (read_body), (rtsp_connection_receive),
+       (rtsp_connection_close):
+       * gst/rtsp/rtspconnection.h:
+       * gst/rtsp/rtspdefs.c: (rtsp_init_status), (rtsp_method_as_text),
+       (rtsp_header_as_text), (rtsp_status_as_text),
+       (rtsp_status_to_string), (rtsp_find_header_field):
+       * gst/rtsp/rtspdefs.h:
+       * gst/rtsp/rtspmessage.c: (rtsp_message_new_request),
+       (rtsp_message_init_request), (rtsp_message_new_response),
+       (rtsp_message_init_response), (rtsp_message_init_data),
+       (rtsp_message_add_header), (rtsp_message_remove_header),
+       (rtsp_message_get_header), (rtsp_message_get_header_copy),
+       (rtsp_message_set_body), (rtsp_message_set_body_copy),
+       (rtsp_message_get_body), (rtsp_message_get_body_copy), (dump_mem),
+       (dump_key_value), (rtsp_message_dump):
+       * gst/rtsp/rtspmessage.h:
+       * gst/rtsp/rtspstream.h:
+       * gst/rtsp/rtsptransport.c: (rtsp_transport_new),
+       (rtsp_transport_init), (parse_mode), (parse_range),
+       (rtsp_transport_parse), (rtsp_transport_free):
+       * gst/rtsp/rtsptransport.h:
+       * gst/rtsp/rtspurl.c: (rtsp_url_parse), (rtsp_url_free):
+       * gst/rtsp/rtspurl.h:
+       * gst/rtsp/sdp.h:
+       * gst/rtsp/sdpmessage.c: (sdp_message_new), (sdp_message_init),
+       (sdp_message_clean), (sdp_message_free), (sdp_media_new),
+       (sdp_media_init), (sdp_message_set_origin),
+       (sdp_message_get_origin), (sdp_message_set_connection),
+       (sdp_message_get_connection), (sdp_message_add_bandwidth),
+       (sdp_message_add_time), (sdp_message_add_zone),
+       (sdp_message_set_key), (sdp_message_get_key),
+       (sdp_message_get_attribute_val), (sdp_message_add_attribute),
+       (sdp_message_add_media), (sdp_media_add_attribute),
+       (sdp_media_add_bandwidth), (sdp_media_add_format),
+       (sdp_media_get_attribute_val), (read_string), (read_string_del),
+       (sdp_parse_line), (sdp_message_parse_buffer), (print_media),
+       (sdp_message_dump):
+       * gst/rtsp/sdpmessage.h:
+       * gst/rtsp/test.c: (main):
+       Ported to 0.9.
+       Set up transports, init UDP ports, init RTP session managers.
+
 2005-05-11  Wim Taymans  <wim@fluendo.com>
 
        * gst/rtp/Makefile.am:
diff --git a/gst/rtsp/.gitignore b/gst/rtsp/.gitignore
new file mode 100644 (file)
index 0000000..10846d7
--- /dev/null
@@ -0,0 +1,8 @@
+Makefile
+Makefile.in
+*.o
+*.lo
+*.la
+.deps
+.libs
+test
diff --git a/gst/rtsp/Makefile.am b/gst/rtsp/Makefile.am
new file mode 100644 (file)
index 0000000..2a90944
--- /dev/null
@@ -0,0 +1,22 @@
+
+plugin_LTLIBRARIES = libgstrtsp.la
+
+libgstrtsp_la_SOURCES = gstrtsp.c gstrtspsrc.c \
+                       rtspconnection.c        \
+                       rtspdefs.c              \
+                       rtspmessage.c           \
+                       rtsptransport.c         \
+                       rtspurl.c               \
+                       sdpmessage.c
+
+libgstrtsp_la_CFLAGS = $(GST_CFLAGS)
+libgstrtsp_la_LIBADD =
+libgstrtsp_la_LDFLAGS = $(GST_PLUGIN_LDFLAGS)
+
+check_PROGRAMS = test
+
+test_SOURCES = test.c rtspdefs.c rtspurl.c rtspconnection.c rtspmessage.c rtsptransport.c sdpmessage.c
+test_CFLAGS = $(GST_CFLAGS)
+test_LDFLAGS = $(GST_LIBS)
+
+noinst_HEADERS = gstrtspsrc.h gstrtsp.h rtsptransport.h
diff --git a/gst/rtsp/gstrtsp.c b/gst/rtsp/gstrtsp.c
new file mode 100644 (file)
index 0000000..46d7949
--- /dev/null
@@ -0,0 +1,40 @@
+/* GStreamer
+ * Copyright (C) <1999> Erik Walthinsen <omega@cse.ogi.edu>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "gstrtspsrc.h"
+
+static gboolean
+plugin_init (GstPlugin * plugin)
+{
+  if (!gst_element_register (plugin, "rtspsrc", GST_RANK_NONE,
+          GST_TYPE_RTSPSRC))
+    return FALSE;
+
+  return TRUE;
+}
+
+GST_PLUGIN_DEFINE (GST_VERSION_MAJOR,
+    GST_VERSION_MINOR,
+    "rtsp",
+    "transfer data via RTSP",
+    plugin_init, VERSION, GST_LICENSE, GST_PACKAGE, GST_ORIGIN)
diff --git a/gst/rtsp/gstrtsp.h b/gst/rtsp/gstrtsp.h
new file mode 100644 (file)
index 0000000..1b4f6a6
--- /dev/null
@@ -0,0 +1,30 @@
+/* GStreamer
+ * Copyright (C) <1999> Erik Walthinsen <omega@cse.ogi.edu>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+
+#ifndef __GST_RTSP_H__
+#define __GST_RTSP_H__
+
+G_BEGIN_DECLS
+
+
+G_END_DECLS
+
+#endif /* __GST_RTSP_H__ */
+
diff --git a/gst/rtsp/gstrtspsrc.c b/gst/rtsp/gstrtspsrc.c
new file mode 100644 (file)
index 0000000..d607852
--- /dev/null
@@ -0,0 +1,868 @@
+/* GStreamer
+ * Copyright (C) <2005> Wim Taymans <wim@fluendo.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <unistd.h>
+#include <string.h>
+
+#include "gstrtspsrc.h"
+#include "sdp.h"
+
+/* elementfactory information */
+static GstElementDetails gst_rtspsrc_details =
+GST_ELEMENT_DETAILS ("RTSP packet receiver",
+    "Source/Network",
+    "Receive data over the network via RTSP",
+    "Wim Taymans <wim@fluendo.com>");
+
+static GstStaticPadTemplate rtptemplate =
+GST_STATIC_PAD_TEMPLATE ("rtp_stream%d",
+    GST_PAD_SRC,
+    GST_PAD_SOMETIMES,
+    GST_STATIC_CAPS_ANY);
+
+static GstStaticPadTemplate rtcptemplate =
+GST_STATIC_PAD_TEMPLATE ("rtcp_stream%d",
+    GST_PAD_SRC,
+    GST_PAD_SOMETIMES,
+    GST_STATIC_CAPS_ANY);
+
+enum
+{
+  /* FILL ME */
+  LAST_SIGNAL
+};
+
+#define DEFAULT_LOCATION       NULL
+#define DEFAULT_PROTOCOLS      GST_RTSP_PROTO_UDP_UNICAST | GST_RTSP_PROTO_UDP_MULTICAST | GST_RTSP_PROTO_TCP
+#define DEFAULT_DEBUG          FALSE
+
+enum
+{
+  PROP_0,
+  PROP_LOCATION,
+  PROP_PROTOCOLS,
+  PROP_DEBUG,
+  /* FILL ME */
+};
+
+#define GST_TYPE_RTSP_PROTO (gst_rtsp_proto_get_type())
+static GType
+gst_rtsp_proto_get_type (void)
+{
+  static GType rtsp_proto_type = 0;
+  static GFlagsValue rtsp_proto[] = {
+    {GST_RTSP_PROTO_UDP_UNICAST, "UDP Unicast", "UDP unicast mode"},
+    {GST_RTSP_PROTO_UDP_MULTICAST, "UDP Multicast", "UDP Multicast mode"},
+    {GST_RTSP_PROTO_TCP, "TCP", "TCP interleaved mode"},
+    {0, NULL, NULL},
+  };
+
+  if (!rtsp_proto_type) {
+    rtsp_proto_type = g_flags_register_static ("GstRTSPProto", rtsp_proto);
+  }
+  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 GstElementStateReturn gst_rtspsrc_change_state (GstElement * element);
+static gboolean gst_rtspsrc_activate (GstPad * pad, GstActivateMode mode);
+
+static void gst_rtspsrc_set_property (GObject * object, guint prop_id,
+    const GValue * value, GParamSpec * pspec);
+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 guint gst_rtspsrc_signals[LAST_SIGNAL] = { 0 }; */
+
+GType
+gst_rtspsrc_get_type (void)
+{
+  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
+    };
+
+    rtspsrc_type =
+        g_type_register_static (GST_TYPE_ELEMENT, "GstRTSPSrc", &rtspsrc_info,
+        0);
+  }
+  return rtspsrc_type;
+}
+
+static void
+gst_rtspsrc_base_init (gpointer g_class)
+{
+  GstElementClass *element_class = GST_ELEMENT_CLASS (g_class);
+
+  gst_element_class_add_pad_template (element_class,
+      gst_static_pad_template_get (&rtptemplate));
+  gst_element_class_add_pad_template (element_class,
+      gst_static_pad_template_get (&rtcptemplate));
+
+  gst_element_class_set_details (element_class, &gst_rtspsrc_details);
+}
+
+static void
+gst_rtspsrc_class_init (GstRTSPSrc * klass)
+{
+  GObjectClass *gobject_class;
+  GstElementClass *gstelement_class;
+
+  gobject_class = (GObjectClass *) klass;
+  gstelement_class = (GstElementClass *) klass;
+
+  parent_class = g_type_class_ref (GST_TYPE_ELEMENT);
+
+  gobject_class->set_property = gst_rtspsrc_set_property;
+  gobject_class->get_property = gst_rtspsrc_get_property;
+
+  g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_LOCATION,
+      g_param_spec_string ("location", "RTSP Location",
+          "Location of the RTSP url to read",
+          DEFAULT_LOCATION, G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
+
+  g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_PROTOCOLS,
+      g_param_spec_flags ("protocols", "Protocols", "Allowed protocols",
+          GST_TYPE_RTSP_PROTO, DEFAULT_PROTOCOLS,
+          G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
+
+  g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_DEBUG,
+      g_param_spec_boolean ("debug", "Debug",
+          "Dump request qnd response messages to stdout",
+          DEFAULT_DEBUG, G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
+
+  gstelement_class->change_state = gst_rtspsrc_change_state;
+}
+
+static void
+gst_rtspsrc_init (GstRTSPSrc * src)
+{
+  /*
+     src->srcpad =
+     gst_pad_new_from_template (gst_static_pad_template_get (&srctemplate),
+     "src");
+     gst_pad_set_loop_function (src->srcpad, gst_rtspsrc_loop);
+     gst_pad_set_activate_function (src->srcpad, gst_rtspsrc_activate);
+     gst_element_add_pad (GST_ELEMENT (src), src->srcpad);
+   */
+}
+
+static void
+gst_rtspsrc_set_property (GObject * object, guint prop_id, const GValue * value,
+    GParamSpec * pspec)
+{
+  GstRTSPSrc *rtspsrc;
+
+  rtspsrc = GST_RTSPSRC (object);
+
+  switch (prop_id) {
+    case PROP_LOCATION:
+      g_free (rtspsrc->location);
+      rtspsrc->location = g_value_dup_string (value);
+      break;
+    case PROP_PROTOCOLS:
+      rtspsrc->protocols = g_value_get_flags (value);
+      break;
+    case PROP_DEBUG:
+      rtspsrc->debug = g_value_get_boolean (value);
+      break;
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+      break;
+  }
+}
+
+static void
+gst_rtspsrc_get_property (GObject * object, guint prop_id, GValue * value,
+    GParamSpec * pspec)
+{
+  GstRTSPSrc *rtspsrc;
+
+  rtspsrc = GST_RTSPSRC (object);
+
+  switch (prop_id) {
+    case PROP_LOCATION:
+      g_value_set_string (value, rtspsrc->location);
+      break;
+    case PROP_PROTOCOLS:
+      g_value_set_flags (value, rtspsrc->protocols);
+      break;
+    case PROP_DEBUG:
+      g_value_set_boolean (value, rtspsrc->debug);
+      break;
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+      break;
+  }
+}
+
+static GstRTSPStream *
+gst_rtspsrc_create_stream (GstRTSPSrc * src)
+{
+  GstRTSPStream *s;
+
+  s = g_new0 (GstRTSPStream, 1);
+  s->parent = src;
+
+  src->streams = g_list_append (src->streams, s);
+
+  return s;
+}
+
+static gboolean
+rtspsrc_add_element (GstRTSPSrc * src, GstElement * element)
+{
+  gst_object_set_parent (GST_OBJECT (element), GST_OBJECT (src));
+  gst_element_set_manager (element, GST_ELEMENT_MANAGER (src));
+  gst_element_set_scheduler (element, GST_ELEMENT_SCHEDULER (src));
+
+  return TRUE;
+}
+
+static gboolean
+gst_rtspsrc_stream_setup_rtp (GstRTSPStream * stream, gint * rtpport,
+    gint * rtcpport)
+{
+  GstElement *rtpsrc;
+  GstElementStateReturn ret;
+  GstRTSPSrc *src;
+
+  src = stream->parent;
+
+  if (!(stream->rtpsrc =
+          gst_element_make_from_uri (GST_URI_SRC, "udp://0.0.0.0:0", NULL)))
+    goto no_udp_rtp_protocol;
+
+  /* we manage this element */
+  rtspsrc_add_element (src, stream->rtpsrc);
+
+  if ((ret =
+          gst_element_set_state (stream->rtpsrc,
+              GST_STATE_PAUSED)) != GST_STATE_SUCCESS)
+    goto start_rtp_failure;
+
+  if (!(stream->rtcpsrc =
+          gst_element_make_from_uri (GST_URI_SRC, "udp://0.0.0.0:0", NULL)))
+    goto no_udp_rtcp_protocol;
+
+  /* we manage this element */
+  rtspsrc_add_element (src, stream->rtcpsrc);
+
+  if ((ret =
+          gst_element_set_state (stream->rtcpsrc,
+              GST_STATE_PAUSED)) != GST_STATE_SUCCESS)
+    goto start_rtcp_failure;
+
+  g_object_get (G_OBJECT (stream->rtpsrc), "port", rtpport, NULL);
+  g_object_get (G_OBJECT (stream->rtcpsrc), "port", rtcpport, NULL);
+
+  return TRUE;
+
+  /* ERRORS, FIXME, cleanup */
+no_udp_rtp_protocol:
+  {
+    GST_DEBUG ("could not get UDP source for rtp");
+    return FALSE;
+  }
+no_udp_rtcp_protocol:
+  {
+    GST_DEBUG ("could not get UDP source for rtcp");
+    return FALSE;
+  }
+start_rtp_failure:
+  {
+    GST_DEBUG ("could not start UDP source for rtp");
+    return FALSE;
+  }
+start_rtcp_failure:
+  {
+    GST_DEBUG ("could not start UDP source for rtcp");
+    return FALSE;
+  }
+}
+
+static gboolean
+gst_rtspsrc_stream_configure_transport (GstRTSPStream * stream,
+    RTSPTransport * transport)
+{
+  GstRTSPSrc *src;
+
+  src = stream->parent;
+
+  if (transport->lower_transport == RTSP_LOWER_TRANS_TCP) {
+    GstPad *pad;
+
+    /* configure for interleaved delivery */
+    if (!(stream->rtpdec = gst_element_factory_make ("rtpdec", NULL)))
+      goto no_element;
+
+    /* we manage this element */
+    rtspsrc_add_element (src, stream->rtpdec);
+    stream->rtpdecrtp = gst_element_get_pad (stream->rtpdec, "sinkrtp");
+    stream->rtpdecrtcp = gst_element_get_pad (stream->rtpdec, "sinkrtcp");
+
+    /* FIXME, make sure it outputs the caps */
+    pad = gst_element_get_pad (stream->rtpdec, "srcrtp");
+    gst_element_add_ghost_pad (GST_ELEMENT (src), pad, "srcrtp");
+    gst_object_unref (GST_OBJECT (pad));
+  } else {
+    /* configure for UDP delivery, FIXME */
+  }
+  return TRUE;
+
+no_element:
+  {
+    GST_DEBUG ("no rtpdec element found");
+    return FALSE;
+  }
+}
+
+static gint
+find_stream (GstRTSPStream * stream, gconstpointer a)
+{
+  gint channel = GPOINTER_TO_INT (a);
+
+  if (stream->rtpchannel == channel || stream->rtcpchannel == channel)
+    return 0;
+
+  return -1;
+}
+
+static void
+gst_rtspsrc_loop (GstRTSPSrc * src)
+{
+  RTSPMessage response = { 0 };
+  RTSPResult res;
+  gint channel;
+  GList *lstream;
+  GstRTSPStream *stream;
+  GstPadChainFunction chainfunc;
+  GstPad *outpad;
+  guint8 *data;
+  gint size;
+
+  do {
+    GST_DEBUG ("doing reveive");
+    if ((res = rtsp_connection_receive (src->connection, &response)) < 0)
+      goto receive_error;
+    GST_DEBUG ("got packet");
+  }
+  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);
+  if (!lstream)
+    goto unknown_stream;
+
+  stream = (GstRTSPStream *) lstream->data;
+  if (channel == stream->rtpchannel)
+    outpad = stream->rtpdecrtp;
+  else if (channel == stream->rtcpchannel)
+    outpad = stream->rtpdecrtcp;
+
+  rtsp_message_get_body (&response, &data, &size);
+
+  /* channels are not correct on some servers, do extra check */
+  if (data[1] >= 200 && data[1] <= 204) {
+    /* hmm RTCP message */
+    outpad = stream->rtpdecrtcp;
+  }
+
+  if ((chainfunc = GST_RPAD_CHAINFUNC (outpad))) {
+    GstBuffer *buf;
+
+    buf = gst_buffer_new_and_alloc (size);
+    memcpy (GST_BUFFER_DATA (buf), data, size);
+
+    if (chainfunc (outpad, buf) != GST_FLOW_OK)
+      goto need_pause;
+  }
+
+unknown_stream:
+
+  return;
+
+receive_error:
+  {
+    GST_ELEMENT_ERROR (src, RESOURCE, WRITE,
+        ("Could not receive message."), (NULL));
+    /*
+       gst_pad_push_event (src->srcpad, gst_event_new (GST_EVENT_EOS));
+     */
+    goto need_pause;
+  }
+need_pause:
+  {
+    gst_task_pause (src->task);
+    return;
+  }
+}
+
+static gboolean
+gst_rtspsrc_send (GstRTSPSrc * src, RTSPMessage * request,
+    RTSPMessage * response)
+{
+  RTSPResult res;
+
+  if (src->debug) {
+    rtsp_message_dump (request);
+  }
+  if ((res = rtsp_connection_send (src->connection, request)) < 0)
+    goto send_error;
+
+  if ((res = rtsp_connection_receive (src->connection, response)) < 0)
+    goto receive_error;
+  if (response->type_data.response.code != RTSP_STS_OK)
+    goto error_response;
+
+  if (src->debug) {
+    rtsp_message_dump (response);
+  }
+
+  return TRUE;
+
+send_error:
+  {
+    GST_ELEMENT_ERROR (src, RESOURCE, WRITE,
+        ("Could not send message."), (NULL));
+    return FALSE;
+  }
+receive_error:
+  {
+    GST_ELEMENT_ERROR (src, RESOURCE, READ,
+        ("Could not receive message."), (NULL));
+    return FALSE;
+  }
+error_response:
+  {
+    rtsp_message_dump (request);
+    rtsp_message_dump (response);
+    GST_ELEMENT_ERROR (src, RESOURCE, READ, ("Got error response."), (NULL));
+    return FALSE;
+  }
+}
+
+static gboolean
+gst_rtspsrc_open (GstRTSPSrc * src)
+{
+  RTSPUrl *url;
+  RTSPResult res;
+  RTSPMessage request = { 0 };
+  RTSPMessage response = { 0 };
+  guint8 *data;
+  guint size;
+  SDPMessage sdp = { 0 };
+  GstRTSPProto protocols;
+
+  /* parse url */
+  GST_DEBUG ("parsing url...");
+  if ((res = rtsp_url_parse (src->location, &url)) < 0)
+    goto invalid_url;
+
+  /* open connection */
+  GST_DEBUG ("opening connection...");
+  if ((res = rtsp_connection_open (url, &src->connection)) < 0)
+    goto could_not_open;
+
+  /* create DESCRIBE */
+  GST_DEBUG ("create describe...");
+  if ((res =
+          rtsp_message_init_request (RTSP_DESCRIBE, src->location,
+              &request)) < 0)
+    goto create_request_failed;
+  /* we accept SDP for now */
+  rtsp_message_add_header (&request, RTSP_HDR_ACCEPT, "application/sdp");
+
+  /* send DESCRIBE */
+  GST_DEBUG ("send describe...");
+  if (!gst_rtspsrc_send (src, &request, &response))
+    goto send_error;
+
+  /* parse SDP */
+  rtsp_message_get_body (&response, &data, &size);
+
+  GST_DEBUG ("parse sdp...");
+  sdp_message_init (&sdp);
+  sdp_message_parse_buffer (data, size, &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;
+      gchar *setup_url;
+      gchar *control_url;
+      gchar *transports;
+      GstRTSPStream *stream;
+
+      media = sdp_message_get_media (&sdp, i);
+
+      stream = gst_rtspsrc_create_stream (src);
+
+      GST_DEBUG ("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;
+      }
+
+      /* FIXME, check absolute/relative URL */
+      setup_url = g_strdup_printf ("%s/%s", src->location, control_url);
+
+      GST_DEBUG ("setup %s", setup_url);
+      /* create SETUP request */
+      if ((res =
+              rtsp_message_init_request (RTSP_SETUP, setup_url,
+                  &request)) < 0) {
+        g_free (setup_url);
+        goto create_request_failed;
+      }
+      g_free (setup_url);
+
+
+      transports = g_strdup ("");
+      if (protocols & GST_RTSP_PROTO_UDP_UNICAST) {
+        gchar *new;
+        gint rtpport, rtcpport;
+        gchar *trxparams;
+
+        /* allocate two udp ports */
+        gst_rtspsrc_stream_setup_rtp (stream, &rtpport, &rtcpport);
+
+        trxparams = g_strdup_printf ("client_port=%d-%d", rtpport, rtcpport);
+        new = g_strconcat (transports, "RTP/AVP/UDP;unicast;", trxparams, NULL);
+        g_free (trxparams);
+        g_free (transports);
+        transports = new;
+      }
+      if (protocols & GST_RTSP_PROTO_UDP_MULTICAST) {
+        gchar *new;
+
+        new =
+            g_strconcat (transports, transports[0] ? "," : "",
+            ",RTP/AVP/UDP;multicast", NULL);
+        g_free (transports);
+        transports = new;
+      }
+      if (protocols & GST_RTSP_PROTO_TCP) {
+        gchar *new;
+
+        new =
+            g_strconcat (transports, transports[0] ? "," : "", ",RTP/AVP/TCP",
+            NULL);
+        g_free (transports);
+        transports = new;
+      }
+
+      /* select transport, copy is made when adding to header */
+      rtsp_message_add_header (&request, RTSP_HDR_TRANSPORT, transports);
+      g_free (transports);
+
+      rtsp_message_dump (&request);
+
+      if (!gst_rtspsrc_send (src, &request, &response))
+        goto send_error;
+
+      /* parse response transport */
+      {
+        gchar *resptrans;
+        RTSPTransport transport = { 0 };
+
+        rtsp_message_get_header (&response, RTSP_HDR_TRANSPORT, &resptrans);
+
+        /* update allowed transports for other streams */
+        rtsp_transport_parse (resptrans, &transport);
+        if (transport.lower_transport == RTSP_LOWER_TRANS_TCP) {
+          protocols = GST_RTSP_PROTO_TCP;
+          src->interleaved = TRUE;
+        } else {
+          if (transport.multicast) {
+            /* disable unicast */
+            protocols = GST_RTSP_PROTO_UDP_MULTICAST;
+          } else {
+            /* disable multicast */
+            protocols = GST_RTSP_PROTO_UDP_UNICAST;
+          }
+        }
+        gst_rtspsrc_stream_configure_transport (stream, &transport);
+        rtsp_transport_init (&transport);
+      }
+    }
+  }
+
+  return TRUE;
+
+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));
+    return FALSE;
+  }
+create_request_failed:
+  {
+    GST_ELEMENT_ERROR (src, LIBRARY, INIT,
+        ("Could not create request."), (NULL));
+    return FALSE;
+  }
+send_error:
+  {
+    GST_ELEMENT_ERROR (src, RESOURCE, WRITE,
+        ("Could not send message."), (NULL));
+    return FALSE;
+  }
+}
+
+static gboolean
+gst_rtspsrc_close (GstRTSPSrc * src)
+{
+  RTSPMessage request = { 0 };
+  RTSPMessage response = { 0 };
+  RTSPResult res;
+
+  GST_DEBUG ("TEARDOWN...");
+  /* do TEARDOWN */
+  if ((res =
+          rtsp_message_init_request (RTSP_TEARDOWN, src->location,
+              &request)) < 0)
+    goto create_request_failed;
+
+  if (!gst_rtspsrc_send (src, &request, &response))
+    goto send_error;
+
+  /* close connection */
+  GST_DEBUG ("closing connection...");
+  if ((res = rtsp_connection_close (src->connection)) < 0)
+    goto close_failed;
+
+  return TRUE;
+
+create_request_failed:
+  {
+    GST_ELEMENT_ERROR (src, LIBRARY, INIT,
+        ("Could not create request."), (NULL));
+    return FALSE;
+  }
+send_error:
+  {
+    GST_ELEMENT_ERROR (src, RESOURCE, WRITE,
+        ("Could not send message."), (NULL));
+    return FALSE;
+  }
+close_failed:
+  {
+    GST_ELEMENT_ERROR (src, RESOURCE, CLOSE, ("Close failed."), (NULL));
+    return FALSE;
+  }
+}
+
+static gboolean
+gst_rtspsrc_play (GstRTSPSrc * src)
+{
+  RTSPMessage request = { 0 };
+  RTSPMessage response = { 0 };
+  RTSPResult res;
+
+  GST_DEBUG ("PLAY...");
+  /* do play */
+  if ((res =
+          rtsp_message_init_request (RTSP_PLAY, src->location, &request)) < 0)
+    goto create_request_failed;
+
+  if (!gst_rtspsrc_send (src, &request, &response))
+    goto send_error;
+
+  if (GST_ELEMENT_SCHEDULER (src) && src->interleaved) {
+    src->task =
+        gst_scheduler_create_task (GST_ELEMENT_SCHEDULER (src),
+        (GstTaskFunction) gst_rtspsrc_loop, src);
+
+    gst_task_start (src->task);
+  }
+
+  return TRUE;
+
+create_request_failed:
+  {
+    GST_ELEMENT_ERROR (src, LIBRARY, INIT,
+        ("Could not create request."), (NULL));
+    return FALSE;
+  }
+send_error:
+  {
+    GST_ELEMENT_ERROR (src, RESOURCE, WRITE,
+        ("Could not send message."), (NULL));
+    return FALSE;
+  }
+}
+
+static gboolean
+gst_rtspsrc_pause (GstRTSPSrc * src)
+{
+  RTSPMessage request = { 0 };
+  RTSPMessage response = { 0 };
+  RTSPResult res;
+
+  GST_DEBUG ("PAUSE...");
+  /* do pause */
+  if ((res =
+          rtsp_message_init_request (RTSP_PAUSE, src->location, &request)) < 0)
+    goto create_request_failed;
+
+  if (!gst_rtspsrc_send (src, &request, &response))
+    goto send_error;
+
+  return TRUE;
+
+create_request_failed:
+  {
+    GST_ELEMENT_ERROR (src, LIBRARY, INIT,
+        ("Could not create request."), (NULL));
+    return FALSE;
+  }
+send_error:
+  {
+    GST_ELEMENT_ERROR (src, RESOURCE, WRITE,
+        ("Could not send message."), (NULL));
+    return FALSE;
+  }
+}
+
+static gboolean
+gst_rtspsrc_activate (GstPad * pad, GstActivateMode mode)
+{
+  gboolean result;
+  GstRTSPSrc *rtspsrc;
+
+  rtspsrc = GST_RTSPSRC (GST_OBJECT_PARENT (pad));
+
+  switch (mode) {
+    case GST_ACTIVATE_PUSH:
+      /* if we have a scheduler we can start the task */
+      if (GST_ELEMENT_SCHEDULER (rtspsrc) && rtspsrc->interleaved) {
+        GST_STREAM_LOCK (pad);
+        GST_RPAD_TASK (pad) =
+            gst_scheduler_create_task (GST_ELEMENT_SCHEDULER (rtspsrc),
+            (GstTaskFunction) gst_rtspsrc_loop, pad);
+
+        gst_task_start (GST_RPAD_TASK (pad));
+        GST_STREAM_UNLOCK (pad);
+        result = TRUE;
+      }
+      break;
+    case GST_ACTIVATE_PULL:
+      result = FALSE;
+      break;
+    case GST_ACTIVATE_NONE:
+      /* step 1, unblock clock sync (if any) */
+
+      /* step 2, make sure streaming finishes */
+      GST_STREAM_LOCK (pad);
+      gst_rtspsrc_close (rtspsrc);
+
+      /* step 3, stop the task */
+      if (GST_RPAD_TASK (pad)) {
+        gst_task_stop (GST_RPAD_TASK (pad));
+        gst_object_unref (GST_OBJECT (GST_RPAD_TASK (pad)));
+        GST_RPAD_TASK (pad) = NULL;
+      }
+      GST_STREAM_UNLOCK (pad);
+
+      result = TRUE;
+      break;
+  }
+  return result;
+}
+
+static GstElementStateReturn
+gst_rtspsrc_change_state (GstElement * element)
+{
+  GstRTSPSrc *rtspsrc;
+  GstElementState transition;
+  GstElementStateReturn ret;
+
+  rtspsrc = GST_RTSPSRC (element);
+
+  transition = GST_STATE_TRANSITION (rtspsrc);
+
+  switch (transition) {
+    case GST_STATE_NULL_TO_READY:
+      break;
+    case GST_STATE_READY_TO_PAUSED:
+      rtspsrc->interleaved = FALSE;
+      gst_rtspsrc_open (rtspsrc);
+      /* need to play now for the preroll, might delay that to
+       * next state when we have NO_PREROLL as a return value */
+      gst_rtspsrc_play (rtspsrc);
+      break;
+    case GST_STATE_PAUSED_TO_PLAYING:
+      break;
+    default:
+      break;
+  }
+
+  ret = GST_ELEMENT_CLASS (parent_class)->change_state (element);
+
+  switch (transition) {
+    case GST_STATE_PLAYING_TO_PAUSED:
+      break;
+    case GST_STATE_PAUSED_TO_READY:
+      gst_rtspsrc_pause (rtspsrc);
+      break;
+    case GST_STATE_READY_TO_NULL:
+      break;
+    default:
+      break;
+  }
+
+  return ret;
+}
diff --git a/gst/rtsp/gstrtspsrc.h b/gst/rtsp/gstrtspsrc.h
new file mode 100644 (file)
index 0000000..0a64e02
--- /dev/null
@@ -0,0 +1,106 @@
+/* GStreamer
+ * Copyright (C) <1999> Erik Walthinsen <omega@cse.ogi.edu>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+
+#ifndef __GST_RTSPSRC_H__
+#define __GST_RTSPSRC_H__
+
+#include <gst/gst.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+#include "gstrtsp.h"
+#include "rtsp.h"
+
+#define GST_TYPE_RTSPSRC \
+  (gst_rtspsrc_get_type())
+#define GST_RTSPSRC(obj) \
+  (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_RTSPSRC,GstRTSPSrc))
+#define GST_RTSPSRC_CLASS(klass) \
+  (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_RTSPSRC,GstRTSPSrc))
+#define GST_IS_RTSPSRC(obj) \
+  (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_RTSPSRC))
+#define GST_IS_RTSPSRC_CLASS(obj) \
+  (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_RTSPSRC))
+
+typedef struct _GstRTSPSrc GstRTSPSrc;
+typedef struct _GstRTSPSrcClass GstRTSPSrcClass;
+
+/* flags with allowed protocols */
+typedef enum
+{
+  GST_RTSP_PROTO_UDP_UNICAST   = (1 << 0),
+  GST_RTSP_PROTO_UDP_MULTICAST = (1 << 1),
+  GST_RTSP_PROTO_TCP           = (1 << 2),
+} GstRTSPProto;
+
+typedef struct _GstRTSPStream GstRTSPStream;
+
+struct _GstRTSPStream {
+  gint               rtpchannel;
+  gint               rtcpchannel;
+
+  GstRTSPSrc *parent;
+
+  /* our udp sources */
+  GstElement *rtpsrc;
+  GstElement *rtcpsrc;
+
+  /* our udp sink back to the server */
+  GstElement *rtcpsink;
+
+  /* the rtp decoder */
+  GstElement *rtpdec;
+  GstPad     *rtpdecrtp;
+  GstPad     *rtpdecrtcp;
+};
+
+struct _GstRTSPSrc {
+  GstElement element;
+
+  gboolean      interleaved;
+  GstTask      *task;
+
+  GList                *streams;
+
+  gchar                *location;
+  gboolean      debug;
+
+  GstRTSPProto  protocols;
+
+  RTSPConnection *connection;
+  RTSPMessage  *request;
+  RTSPMessage  *response;
+};
+
+struct _GstRTSPSrcClass {
+  GstElementClass parent_class;
+};
+
+GType gst_rtspsrc_get_type(void);
+
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+
+#endif /* __GST_RTSPSRC_H__ */
diff --git a/gst/rtsp/rtsp.h b/gst/rtsp/rtsp.h
new file mode 100644 (file)
index 0000000..fd5ab2a
--- /dev/null
@@ -0,0 +1,30 @@
+/* GStreamer
+ * Copyright (C) <2005> Wim Taymans <wim@fluendo.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifndef __RTSP_H__
+#define __RTSP_H__
+
+#include <rtspconnection.h>
+#include <rtspdefs.h>
+#include <rtspmessage.h>
+#include <rtspstream.h>
+#include <rtsptransport.h>
+#include <rtspurl.h>
+
+#endif /* __RTSP_H__ */
diff --git a/gst/rtsp/rtspconnection.c b/gst/rtsp/rtspconnection.c
new file mode 100644 (file)
index 0000000..f9a1b42
--- /dev/null
@@ -0,0 +1,432 @@
+/* GStreamer
+ * Copyright (C) <2005> Wim Taymans <wim@fluendo.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#include <arpa/inet.h>
+#include <netdb.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "rtspconnection.h"
+
+RTSPResult
+rtsp_connection_open (RTSPUrl * url, RTSPConnection ** conn)
+{
+  gint fd;
+  struct sockaddr_in sin;
+  struct hostent *hostinfo;
+  char **addrs;
+  gchar *ip;
+  struct in_addr addr;
+  gint ret;
+
+  if (url == NULL || conn == NULL)
+    return RTSP_EINVAL;
+
+  if (url->protocol != RTSP_PROTO_TCP)
+    return RTSP_ENOTIMPL;
+
+  /* first check if it already is an IP address */
+  if (inet_aton (url->host, &addr)) {
+    ip = url->host;
+  } else {
+    hostinfo = gethostbyname (url->host);
+    if (!hostinfo)
+      goto not_resolved;        /* h_errno set */
+
+    if (hostinfo->h_addrtype != AF_INET)
+      goto not_ip;              /* host not an IP host */
+
+    addrs = hostinfo->h_addr_list;
+    ip = inet_ntoa (*(struct in_addr *) *addrs);
+  }
+
+  fd = socket (AF_INET, SOCK_STREAM, 0);
+  if (fd == -1)
+    goto sys_error;
+
+  memset (&sin, 0, sizeof (sin));
+  sin.sin_family = AF_INET;     /* network socket */
+  sin.sin_port = htons (url->port);     /* on port */
+  sin.sin_addr.s_addr = inet_addr (ip); /* on host ip */
+
+  ret = connect (fd, (struct sockaddr *) &sin, sizeof (sin));
+  if (ret != 0)
+    goto sys_error;
+
+  return rtsp_connection_create (fd, conn);
+
+sys_error:
+  {
+    return RTSP_ESYS;
+  }
+not_resolved:
+  {
+    g_warning ("could not resolve host \"%s\"\n", url->host);
+    return RTSP_ESYS;
+  }
+not_ip:
+  {
+    g_warning ("host \"%s\" is not IP\n", url->host);
+    return RTSP_ESYS;
+  }
+}
+
+RTSPResult
+rtsp_connection_create (gint fd, RTSPConnection ** conn)
+{
+  RTSPConnection *newconn;
+
+  /* FIXME check fd */
+
+  newconn = g_new (RTSPConnection, 1);
+
+  newconn->fd = fd;
+  newconn->cseq = 0;
+  newconn->session_id[0] = 0;
+  newconn->state = RTSP_STATE_INIT;
+
+  *conn = newconn;
+
+  return RTSP_OK;
+}
+
+static void
+append_header (gint key, gchar * value, GString * str)
+{
+  const gchar *keystr = rtsp_header_as_text (key);
+
+  g_string_append_printf (str, "%s: %s\r\n", keystr, value);
+}
+
+RTSPResult
+rtsp_connection_send (RTSPConnection * conn, RTSPMessage * message)
+{
+  GString *str;
+
+  if (conn == NULL || message == NULL)
+    return RTSP_EINVAL;
+
+  str = g_string_new ("");
+
+  g_string_append_printf (str, "%s %s RTSP/1.0\r\n"
+      "CSeq: %d\r\n",
+      rtsp_method_as_text (message->type_data.request.method),
+      message->type_data.request.uri, conn->cseq);
+
+  if (conn->session_id[0] != '\0') {
+    rtsp_message_add_header (message, RTSP_HDR_SESSION, conn->session_id);
+  }
+
+  g_hash_table_foreach (message->hdr_fields, (GHFunc) append_header, str);
+
+
+  g_string_append (str, "\r\n");
+
+  write (conn->fd, str->str, str->len);
+  g_string_free (str, TRUE);
+
+  return RTSP_OK;
+}
+
+static RTSPResult
+read_line (gint fd, gchar * buffer, guint size)
+{
+  gint idx;
+  gchar c;
+  gint ret;
+
+  idx = 0;
+  while (TRUE) {
+    ret = read (fd, &c, 1);
+    if (ret < 1)
+      goto error;
+
+    if (c == '\n')              /* end on \n */
+      break;
+    if (c == '\r')              /* ignore \r */
+      continue;
+
+    if (idx < size - 1)
+      buffer[idx++] = c;
+  }
+  buffer[idx] = '\0';
+
+  return RTSP_OK;
+
+error:
+  {
+    perror ("read");
+    return RTSP_ESYS;
+  }
+}
+
+static void
+read_string (gchar * dest, gint size, gchar ** src)
+{
+  gint idx;
+
+  idx = 0;
+  /* skip spaces */
+  while (g_ascii_isspace (**src))
+    (*src)++;
+
+  while (!g_ascii_isspace (**src) && **src != '\0') {
+    if (idx < size - 1)
+      dest[idx++] = **src;
+    (*src)++;
+  }
+  if (size > 0)
+    dest[idx] = '\0';
+}
+
+static void
+read_key (gchar * dest, gint size, gchar ** src)
+{
+  gint idx;
+
+  idx = 0;
+  while (**src != ':' && **src != '\0') {
+    if (idx < size - 1)
+      dest[idx++] = **src;
+    (*src)++;
+  }
+  if (size > 0)
+    dest[idx] = '\0';
+}
+
+static RTSPResult
+parse_response_status (gchar * buffer, RTSPMessage * msg)
+{
+  gchar versionstr[20];
+  gchar codestr[4];
+  gint code;
+  gchar *bptr;
+
+  bptr = buffer;
+
+  read_string (versionstr, sizeof (versionstr), &bptr);
+  if (strcmp (versionstr, "RTSP/1.0") != 0)
+    goto wrong_version;
+
+  read_string (codestr, sizeof (codestr), &bptr);
+  code = atoi (codestr);
+
+  while (g_ascii_isspace (*bptr))
+    bptr++;
+
+  rtsp_message_init_response (code, bptr, NULL, msg);
+
+  return RTSP_OK;
+
+wrong_version:
+  {
+    return RTSP_EINVAL;
+  }
+}
+
+static RTSPResult
+parse_line (gchar * buffer, RTSPMessage * msg)
+{
+  gchar key[32];
+  gchar *bptr;
+  RTSPHeaderField field;
+
+  bptr = buffer;
+
+  read_key (key, sizeof (key), &bptr);
+  if (*bptr != ':')
+    return RTSP_EINVAL;
+
+  bptr++;
+
+  field = rtsp_find_header_field (key);
+  if (field == -1) {
+    g_warning ("unknown header field '%s'\n", key);
+  } else {
+    while (g_ascii_isspace (*bptr))
+      bptr++;
+    rtsp_message_add_header (msg, field, bptr);
+  }
+
+  return RTSP_OK;
+}
+
+static RTSPResult
+read_body (gint fd, glong content_length, RTSPMessage * msg)
+{
+  gchar *body, *bodyptr;
+  gint to_read, r;
+
+  if (content_length <= 0) {
+    rtsp_message_set_body (msg, NULL, 0);
+    return RTSP_OK;
+  }
+
+  body = g_malloc (content_length);
+  bodyptr = body;
+  to_read = content_length;
+  while (to_read > 0) {
+    r = read (fd, bodyptr, to_read);
+
+    to_read -= r;
+    bodyptr += r;
+  }
+
+  rtsp_message_set_body (msg, body, content_length);
+
+  return RTSP_OK;
+}
+
+RTSPResult
+rtsp_connection_receive (RTSPConnection * conn, RTSPMessage * msg)
+{
+  gchar buffer[4096];
+  gint line;
+  gchar *hdrval;
+  glong content_length;
+  RTSPResult res;
+  gboolean need_body;
+
+  if (conn == NULL || msg == NULL)
+    return RTSP_EINVAL;
+
+  line = 0;
+
+  need_body = TRUE;
+
+  /* parse first line and headers */
+  while (TRUE) {
+    gchar c;
+    gint ret;
+
+    ret = read (conn->fd, &c, 1);
+    if (ret < 0)
+      goto read_error;
+    if (ret < 1)
+      break;
+
+    /* check for data packet */
+    if (c == '$') {
+      guint16 size;
+
+      /* read channel */
+      ret = read (conn->fd, &c, 1);
+      if (ret < 0)
+        goto read_error;
+      if (ret < 1)
+        goto error;
+
+      rtsp_message_init_data ((gint) c, msg);
+
+      ret = read (conn->fd, &size, 2);
+      if (ret < 0)
+        goto read_error;
+      if (ret < 2)
+        goto error;
+
+      size = GUINT16_FROM_BE (size);
+
+      read_body (conn->fd, size, msg);
+      need_body = FALSE;
+      break;
+    } else {
+      gint offset = 0;
+
+      if (c != '\r') {
+        buffer[0] = c;
+        offset = 1;
+      }
+      /* should not happen */
+      if (c == '\n')
+        break;
+
+      read_line (conn->fd, buffer + offset, sizeof (buffer) - offset);
+
+      if (buffer[0] == '\0')
+        break;
+
+      if (line == 0) {
+        if (g_str_has_prefix (buffer, "RTSP")) {
+          parse_response_status (buffer, msg);
+        } else {
+          g_warning ("parsing request not implemented\n");
+          goto error;
+        }
+      } else {
+        parse_line (buffer, msg);
+      }
+    }
+    line++;
+  }
+
+  if (need_body) {
+    /* parse body */
+    res = rtsp_message_get_header (msg, RTSP_HDR_CONTENT_LENGTH, &hdrval);
+    if (res == RTSP_OK) {
+      content_length = atol (hdrval);
+      read_body (conn->fd, content_length, msg);
+    }
+
+    /* save session id */
+    {
+      gchar *session_id;
+
+      if (rtsp_message_get_header (msg, RTSP_HDR_SESSION,
+              &session_id) == RTSP_OK) {
+        strncpy (conn->session_id, session_id, sizeof (conn->session_id) - 1);
+        conn->session_id[sizeof (conn->session_id) - 1] = '\0';
+      }
+    }
+  }
+
+  return RTSP_OK;
+
+error:
+  {
+    return RTSP_EPARSE;
+  }
+read_error:
+  {
+    perror ("read");
+    return RTSP_ESYS;
+  }
+}
+
+RTSPResult
+rtsp_connection_close (RTSPConnection * conn)
+{
+  gint res;
+
+  if (conn == NULL)
+    return RTSP_EINVAL;
+
+  res = close (conn->fd);
+  conn->fd = -1;
+  if (res != 0)
+    goto sys_error;
+
+  return RTSP_OK;
+
+sys_error:
+  {
+    return RTSP_ESYS;
+  }
+}
diff --git a/gst/rtsp/rtspconnection.h b/gst/rtsp/rtspconnection.h
new file mode 100644 (file)
index 0000000..aeb8537
--- /dev/null
@@ -0,0 +1,56 @@
+/* GStreamer
+ * Copyright (C) <2005> Wim Taymans <wim@fluendo.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifndef __RTSP_CONNECTION_H__
+#define __RTSP_CONNECTION_H__
+
+#include <glib.h>
+
+#include <rtspdefs.h>
+#include <rtspurl.h>
+#include <rtspstream.h>
+#include <rtspmessage.h>
+
+G_BEGIN_DECLS
+
+typedef struct _RTSPConnection
+{
+  gint          fd;
+
+  gint                 cseq;
+  gchar                session_id[512];
+  
+  RTSPState     state;
+
+  int           num_streams;
+  RTSPStream  **streams;
+  
+} RTSPConnection;
+
+RTSPResult     rtsp_connection_open    (RTSPUrl *url, RTSPConnection **conn);
+RTSPResult     rtsp_connection_create  (gint fd, RTSPConnection **conn);
+
+RTSPResult     rtsp_connection_send    (RTSPConnection *conn, RTSPMessage *message);
+RTSPResult     rtsp_connection_receive (RTSPConnection *conn, RTSPMessage *message);
+
+RTSPResult     rtsp_connection_close   (RTSPConnection *conn);
+
+G_END_DECLS
+
+#endif /* __RTSP_CONNECTION_H__ */
diff --git a/gst/rtsp/rtspdefs.c b/gst/rtsp/rtspdefs.c
new file mode 100644 (file)
index 0000000..729e3b5
--- /dev/null
@@ -0,0 +1,171 @@
+/* GStreamer
+ * Copyright (C) <2005> Wim Taymans <wim@fluendo.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#include "rtspdefs.h"
+
+const gchar *rtsp_methods[] = {
+  "DESCRIBE",
+  "ANNOUNCE",
+  "GET_PARAMETER",
+  "OPTIONS",
+  "PAUSE",
+  "PLAY",
+  "RECORD",
+  "REDIRECT",
+  "SETUP",
+  "SET_PARAMETER",
+  "TEARDOWN",
+  NULL
+};
+
+const gchar *rtsp_headers[] = {
+  "Accept",                     /* Accept               R      opt.      entity */
+  "Accept-Encoding",            /* Accept-Encoding      R      opt.      entity */
+  "Accept-Language",            /* Accept-Language      R      opt.      all */
+  "Allow",                      /* Allow                r      opt.      all */
+  "Authorization",              /* Authorization        R      opt.      all */
+  "Bandwidth",                  /* Bandwidth            R      opt.      all */
+  "Blocksize",                  /* Blocksize            R      opt.      all but OPTIONS, TEARDOWN */
+  "Cache-Control",              /* Cache-Control        g      opt.      SETUP */
+  "Conference",                 /* Conference           R      opt.      SETUP */
+  "Connection",                 /* Connection           g      req.      all */
+  "Content-Base",               /* Content-Base         e      opt.      entity */
+  "Content-Encoding",           /* Content-Encoding     e      req.      SET_PARAMETER, DESCRIBE, ANNOUNCE */
+  "Content-Language",           /* Content-Language     e      req.      DESCRIBE, ANNOUNCE */
+  "Content-Length",             /* Content-Length       e      req.      SET_PARAMETER, ANNOUNCE, entity */
+  "Content-Location",           /* Content-Location     e      opt.      entity */
+  "Content-Type",               /* Content-Type         e      req.      SET_PARAMETER, ANNOUNCE, entity */
+  "CSeq",                       /* CSeq                 g      req.      all */
+  "Date",                       /* Date                 g      opt.      all */
+  "Expires",                    /* Expires              e      opt.      DESCRIBE, ANNOUNCE */
+  "From",                       /* From                 R      opt.      all */
+  "If-Modified-Since",          /* If-Modified-Since    R      opt.      DESCRIBE, SETUP */
+  "Last-Modified",              /* Last-Modified        e      opt.      entity */
+  "Proxy-Authenticate",         /* Proxy-Authenticate */
+  "Proxy-Require",              /* Proxy-Require        R      req.      all */
+  "Public",                     /* Public               r      opt.      all */
+  "Range",                      /* Range                Rr     opt.      PLAY, PAUSE, RECORD */
+  "Referer",                    /* Referer              R      opt.      all */
+  "Require",                    /* Require              R      req.      all */
+  "Retry-After",                /* Retry-After          r      opt.      all */
+  "RTP-Info",                   /* RTP-Info             r      req.      PLAY */
+  "Scale",                      /* Scale                Rr     opt.      PLAY, RECORD */
+  "Session",                    /* Session              Rr     req.      all but SETUP, OPTIONS */
+  "Server",                     /* Server               r      opt.      all */
+  "Speed",                      /* Speed                Rr     opt.      PLAY */
+  "Transport",                  /* Transport            Rr     req.      SETUP */
+  "Unsupported",                /* Unsupported          r      req.      all */
+  "User-Agent",                 /* User-Agent           R      opt.      all */
+  "Via",                        /* Via                  g      opt.      all */
+  "WWW-Authenticate",           /* WWW-Authenticate     r      opt.      all */
+  NULL
+};
+
+#define DEF_STATUS(c,t)
+
+void
+rtsp_init_status (void)
+{
+  DEF_STATUS (RTSP_STS_CONTINUE, "Continue");
+  DEF_STATUS (RTSP_STS_OK, "OK");
+  DEF_STATUS (RTSP_STS_CREATED, "Created");
+  DEF_STATUS (RTSP_STS_LOW_ON_STORAGE, "Low on Storage Space");
+  DEF_STATUS (RTSP_STS_MULTIPLE_CHOICES, "Multiple Choices");
+  DEF_STATUS (RTSP_STS_MOVED_PERMANENTLY, "Moved Permanently");
+  DEF_STATUS (RTSP_STS_MOVE_TEMPORARILY, "Moved Temporarily");
+  DEF_STATUS (RTSP_STS_SEE_OTHER, "See Other");
+  DEF_STATUS (RTSP_STS_NOT_MODIFIED, "Not Modified");
+  DEF_STATUS (RTSP_STS_USE_PROXY, "Use Proxy");
+  DEF_STATUS (RTSP_STS_BAD_REQUEST, "Bad Request");
+  DEF_STATUS (RTSP_STS_UNAUTHORIZED, "Unauthorized");
+  DEF_STATUS (RTSP_STS_PAYMENT_REQUIRED, "Payment Required");
+  DEF_STATUS (RTSP_STS_FORBIDDEN, "Forbidden");
+  DEF_STATUS (RTSP_STS_NOT_FOUND, "Not Found");
+  DEF_STATUS (RTSP_STS_METHOD_NOT_ALLOWED, "Method Not Allowed");
+  DEF_STATUS (RTSP_STS_NOT_ACCEPTABLE, "Not Acceptable");
+  DEF_STATUS (RTSP_STS_PROXY_AUTH_REQUIRED, "Proxy Authentication Required");
+  DEF_STATUS (RTSP_STS_REQUEST_TIMEOUT, "Request Time-out");
+  DEF_STATUS (RTSP_STS_GONE, "Gone");
+  DEF_STATUS (RTSP_STS_LENGTH_REQUIRED, "Length Required");
+  DEF_STATUS (RTSP_STS_PRECONDITION_FAILED, "Precondition Failed");
+  DEF_STATUS (RTSP_STS_REQUEST_ENTITY_TOO_LARGE, "Request Entity Too Large");
+  DEF_STATUS (RTSP_STS_REQUEST_URI_TOO_LARGE, "Request-URI Too Large");
+  DEF_STATUS (RTSP_STS_UNSUPPORTED_MEDIA_TYPE, "Unsupported Media Type");
+  DEF_STATUS (RTSP_STS_PARAMETER_NOT_UNDERSTOOD, "Parameter Not Understood");
+  DEF_STATUS (RTSP_STS_CONFERENCE_NOT_FOUND, "Conference Not Found");
+  DEF_STATUS (RTSP_STS_NOT_ENOUGH_BANDWIDTH, "Not Enough Bandwidth");
+  DEF_STATUS (RTSP_STS_SESSION_NOT_FOUND, "Session Not Found");
+  DEF_STATUS (RTSP_STS_METHOD_NOT_VALID_IN_THIS_STATE,
+      "Method Not Valid in This State");
+  DEF_STATUS (RTSP_STS_HEADER_FIELD_NOT_VALID_FOR_RESOURCE,
+      "Header Field Not Valid for Resource");
+  DEF_STATUS (RTSP_STS_INVALID_RANGE, "Invalid Range");
+  DEF_STATUS (RTSP_STS_PARAMETER_IS_READONLY, "Parameter Is Read-Only");
+  DEF_STATUS (RTSP_STS_AGGREGATE_OPERATION_NOT_ALLOWED,
+      "Aggregate operation not allowed");
+  DEF_STATUS (RTSP_STS_ONLY_AGGREGATE_OPERATION_ALLOWED,
+      "Only aggregate operation allowed");
+  DEF_STATUS (RTSP_STS_UNSUPPORTED_TRANSPORT, "Unsupported transport");
+  DEF_STATUS (RTSP_STS_DESTINATION_UNREACHABLE, "Destination unreachable");
+  DEF_STATUS (RTSP_STS_INTERNAL_SERVER_ERROR, "Internal Server Error");
+  DEF_STATUS (RTSP_STS_NOT_IMPLEMENTED, "Not Implemented");
+  DEF_STATUS (RTSP_STS_BAD_GATEWAY, "Bad Gateway");
+  DEF_STATUS (RTSP_STS_SERVICE_UNAVAILABLE, "Service Unavailable");
+  DEF_STATUS (RTSP_STS_GATEWAY_TIMEOUT, "Gateway Time-out");
+  DEF_STATUS (RTSP_STS_RTSP_VERSION_NOT_SUPPORTED,
+      "RTSP Version not supported");
+  DEF_STATUS (RTSP_STS_OPTION_NOT_SUPPORTED, "Option not supported");
+}
+
+const gchar *
+rtsp_method_as_text (RTSPMethod method)
+{
+  return rtsp_methods[method];
+}
+
+const gchar *
+rtsp_header_as_text (RTSPHeaderField field)
+{
+  return rtsp_headers[field];
+}
+
+const gchar *
+rtsp_status_as_text (RTSPStatusCode code)
+{
+  return NULL;
+}
+
+const gchar *
+rtsp_status_to_string (RTSPStatusCode code)
+{
+  return NULL;
+}
+
+RTSPHeaderField
+rtsp_find_header_field (gchar * header)
+{
+  gint idx;
+
+  for (idx = 0; rtsp_headers[idx]; idx++) {
+    if (g_ascii_strcasecmp (rtsp_headers[idx], header) == 0) {
+      return idx;
+    }
+  }
+  return -1;
+}
diff --git a/gst/rtsp/rtspdefs.h b/gst/rtsp/rtspdefs.h
new file mode 100644 (file)
index 0000000..063ab3c
--- /dev/null
@@ -0,0 +1,174 @@
+/* GStreamer
+ * Copyright (C) <2005> Wim Taymans <wim@fluendo.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifndef __RTSP_DEFS_H__
+#define __RTSP_DEFS_H__
+
+#include <glib.h>
+
+G_BEGIN_DECLS
+
+typedef enum {
+  RTSP_OK      =  0,
+  /* errors */
+  RTSP_EINVAL  = -1,
+  RTSP_ENOMEM  = -2,
+  RTSP_ERESOLV = -3,
+  RTSP_ENOTIMPL        = -4,
+  RTSP_ESYS    = -5,
+  RTSP_EPARSE  = -6,
+} RTSPResult;
+
+typedef enum {
+  RTSP_PROTO_TCP,
+  RTSP_PROTO_UDP,
+} RTSPProto;
+
+typedef enum {
+  RTSP_FAM_NONE,
+  RTSP_FAM_INET,
+  RTSP_FAM_INET6,
+} RTSPFamily;
+
+typedef enum {
+  RTSP_STATE_INIT,
+  RTSP_STATE_READY,
+  RTSP_STATE_PLAYING,
+  RTSP_STATE_RECORDING,
+} RTSPState;
+
+typedef enum {
+  RTSP_DESCRIBE,
+  RTSP_ANNOUNCE,
+  RTSP_GET_PARAMETER,
+  RTSP_OPTIONS,
+  RTSP_PAUSE,
+  RTSP_PLAY,
+  RTSP_RECORD,
+  RTSP_REDIRECT,
+  RTSP_SETUP,
+  RTSP_SET_PARAMETER,
+  RTSP_TEARDOWN,
+} RTSPMethod;
+
+typedef enum {
+  /*
+   * R = Request
+   * r = response
+   * g = general
+   * e = entity
+   */
+  RTSP_HDR_ACCEPT,             /* Accept               R      opt.      entity */
+  RTSP_HDR_ACCEPT_ENCODING,    /* Accept-Encoding      R      opt.      entity */
+  RTSP_HDR_ACCEPT_LANGUAGE,    /* Accept-Language      R      opt.      all */
+  RTSP_HDR_ALLOW,              /* Allow                r      opt.      all */
+  RTSP_HDR_AUTHORIZATION,      /* Authorization        R      opt.      all */
+  RTSP_HDR_BANDWIDTH,          /* Bandwidth            R      opt.      all */
+  RTSP_HDR_BLOCKSIZE,          /* Blocksize            R      opt.      all but OPTIONS, TEARDOWN */
+  RTSP_HDR_CACHE_CONTROL,      /* Cache-Control        g      opt.      SETUP */
+  RTSP_HDR_CONFERENCE,         /* Conference           R      opt.      SETUP */
+  RTSP_HDR_CONNECTION,         /* Connection           g      req.      all */
+  RTSP_HDR_CONTENT_BASE,       /* Content-Base         e      opt.      entity */
+  RTSP_HDR_CONTENT_ENCODING,   /* Content-Encoding     e      req.      SET_PARAMETER, DESCRIBE, ANNOUNCE */
+  RTSP_HDR_CONTENT_LANGUAGE,   /* Content-Language     e      req.      DESCRIBE, ANNOUNCE */
+  RTSP_HDR_CONTENT_LENGTH,     /* Content-Length       e      req.      SET_PARAMETER, ANNOUNCE, entity */
+  RTSP_HDR_CONTENT_LOCATION,   /* Content-Location     e      opt.      entity */
+  RTSP_HDR_CONTENT_TYPE,       /* Content-Type         e      req.      SET_PARAMETER, ANNOUNCE, entity */
+  RTSP_HDR_CSEQ,               /* CSeq                 g      req.      all */
+  RTSP_HDR_DATE,               /* Date                 g      opt.      all */
+  RTSP_HDR_EXPIRES,            /* Expires              e      opt.      DESCRIBE, ANNOUNCE */
+  RTSP_HDR_FROM,               /* From                 R      opt.      all */
+  RTSP_HDR_IF_MODIFIED_SINCE,  /* If-Modified-Since    R      opt.      DESCRIBE, SETUP */
+  RTSP_HDR_LAST_MODIFIED,      /* Last-Modified        e      opt.      entity */
+  RTSP_HDR_PROXY_AUTHENTICATE, /* Proxy-Authenticate */
+  RTSP_HDR_PROXY_REQUIRE,      /* Proxy-Require        R      req.      all */
+  RTSP_HDR_PUBLIC,             /* Public               r      opt.      all */
+  RTSP_HDR_RANGE,              /* Range                Rr     opt.      PLAY, PAUSE, RECORD */
+  RTSP_HDR_REFERER,            /* Referer              R      opt.      all */
+  RTSP_HDR_REQUIRE,            /* Require              R      req.      all */
+  RTSP_HDR_RETRY_AFTER,                /* Retry-After          r      opt.      all */
+  RTSP_HDR_RTP_INFO,           /* RTP-Info             r      req.      PLAY */
+  RTSP_HDR_SCALE,              /* Scale                Rr     opt.      PLAY, RECORD */
+  RTSP_HDR_SESSION,            /* Session              Rr     req.      all but SETUP, OPTIONS */
+  RTSP_HDR_SERVER,             /* Server               r      opt.      all */
+  RTSP_HDR_SPEED,              /* Speed                Rr     opt.      PLAY */
+  RTSP_HDR_TRANSPORT,          /* Transport            Rr     req.      SETUP */
+  RTSP_HDR_UNSUPPORTED,                /* Unsupported          r      req.      all */
+  RTSP_HDR_USER_AGENT,         /* User-Agent           R      opt.      all */
+  RTSP_HDR_VIA,                        /* Via                  g      opt.      all */
+  RTSP_HDR_WWW_AUTHENTICATE,   /* WWW-Authenticate     r      opt.      all */
+
+} RTSPHeaderField;
+
+typedef enum {
+  RTSP_STS_CONTINUE                            = 100, 
+  RTSP_STS_OK                                  = 200, 
+  RTSP_STS_CREATED                             = 201, 
+  RTSP_STS_LOW_ON_STORAGE                      = 250, 
+  RTSP_STS_MULTIPLE_CHOICES                    = 300, 
+  RTSP_STS_MOVED_PERMANENTLY                   = 301, 
+  RTSP_STS_MOVE_TEMPORARILY                    = 302, 
+  RTSP_STS_SEE_OTHER                           = 303, 
+  RTSP_STS_NOT_MODIFIED                        = 304, 
+  RTSP_STS_USE_PROXY                           = 305, 
+  RTSP_STS_BAD_REQUEST                                 = 400, 
+  RTSP_STS_UNAUTHORIZED                        = 401, 
+  RTSP_STS_PAYMENT_REQUIRED                    = 402, 
+  RTSP_STS_FORBIDDEN                           = 403, 
+  RTSP_STS_NOT_FOUND                           = 404, 
+  RTSP_STS_METHOD_NOT_ALLOWED                  = 405, 
+  RTSP_STS_NOT_ACCEPTABLE                      = 406, 
+  RTSP_STS_PROXY_AUTH_REQUIRED                         = 407, 
+  RTSP_STS_REQUEST_TIMEOUT                     = 408, 
+  RTSP_STS_GONE                                = 410, 
+  RTSP_STS_LENGTH_REQUIRED                     = 411, 
+  RTSP_STS_PRECONDITION_FAILED                         = 412, 
+  RTSP_STS_REQUEST_ENTITY_TOO_LARGE            = 413, 
+  RTSP_STS_REQUEST_URI_TOO_LARGE               = 414, 
+  RTSP_STS_UNSUPPORTED_MEDIA_TYPE              = 415, 
+  RTSP_STS_PARAMETER_NOT_UNDERSTOOD            = 451, 
+  RTSP_STS_CONFERENCE_NOT_FOUND                = 452, 
+  RTSP_STS_NOT_ENOUGH_BANDWIDTH                = 453, 
+  RTSP_STS_SESSION_NOT_FOUND                   = 454, 
+  RTSP_STS_METHOD_NOT_VALID_IN_THIS_STATE      = 455, 
+  RTSP_STS_HEADER_FIELD_NOT_VALID_FOR_RESOURCE         = 456, 
+  RTSP_STS_INVALID_RANGE                       = 457, 
+  RTSP_STS_PARAMETER_IS_READONLY               = 458, 
+  RTSP_STS_AGGREGATE_OPERATION_NOT_ALLOWED     = 459, 
+  RTSP_STS_ONLY_AGGREGATE_OPERATION_ALLOWED    = 460, 
+  RTSP_STS_UNSUPPORTED_TRANSPORT               = 461, 
+  RTSP_STS_DESTINATION_UNREACHABLE             = 462, 
+  RTSP_STS_INTERNAL_SERVER_ERROR               = 500, 
+  RTSP_STS_NOT_IMPLEMENTED                     = 501, 
+  RTSP_STS_BAD_GATEWAY                                 = 502, 
+  RTSP_STS_SERVICE_UNAVAILABLE                         = 503, 
+  RTSP_STS_GATEWAY_TIMEOUT                     = 504, 
+  RTSP_STS_RTSP_VERSION_NOT_SUPPORTED          = 505, 
+  RTSP_STS_OPTION_NOT_SUPPORTED                = 551, 
+} RTSPStatusCode;
+
+const gchar*   rtsp_method_as_text     (RTSPMethod method);
+const gchar*   rtsp_header_as_text     (RTSPHeaderField field);
+const gchar*   rtsp_status_as_text     (RTSPStatusCode code);
+const gchar*   rtsp_status_to_string   (RTSPStatusCode code);
+RTSPHeaderField        rtsp_find_header_field  (gchar *header);
+
+G_END_DECLS
+
+#endif /* __RTSP_DEFS_H__ */
diff --git a/gst/rtsp/rtspmessage.c b/gst/rtsp/rtspmessage.c
new file mode 100644 (file)
index 0000000..2720ff7
--- /dev/null
@@ -0,0 +1,307 @@
+/* GStreamer
+ * Copyright (C) <2005> Wim Taymans <wim@fluendo.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#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)
+{
+  if (msg == NULL || uri == NULL)
+    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->body) {
+    g_free (msg->body);
+    msg->body = NULL;
+  }
+  msg->body_size = 0;
+
+  return RTSP_OK;
+}
+
+RTSPResult
+rtsp_message_new_response (RTSPStatusCode code, gchar * reason,
+    RTSPMessage * request, RTSPMessage ** msg)
+{
+  RTSPMessage *newmsg;
+
+  if (msg == NULL || reason == NULL || request == NULL)
+    return RTSP_EINVAL;
+
+  newmsg = g_new0 (RTSPMessage, 1);
+
+  *msg = newmsg;
+
+  return rtsp_message_init_response (code, reason, request, newmsg);
+}
+
+RTSPResult
+rtsp_message_init_response (RTSPStatusCode code, gchar * reason,
+    RTSPMessage * request, RTSPMessage * msg)
+{
+  if (reason == NULL || msg == NULL)
+    return RTSP_EINVAL;
+
+  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;
+
+  if (request) {
+    /* FIXME copy headers */
+  }
+
+  return RTSP_OK;
+}
+
+RTSPResult
+rtsp_message_init_data (gint channel, RTSPMessage * msg)
+{
+  if (msg == NULL)
+    return RTSP_EINVAL;
+
+  msg->type = RTSP_MESSAGE_DATA;
+  msg->type_data.data.channel = channel;
+
+  return RTSP_OK;
+}
+
+
+RTSPResult
+rtsp_message_add_header (RTSPMessage * msg, RTSPHeaderField field,
+    gchar * value)
+{
+  if (msg == NULL || value == NULL)
+    return RTSP_EINVAL;
+
+  g_hash_table_insert (msg->hdr_fields, GINT_TO_POINTER (field),
+      g_strdup (value));
+
+  return RTSP_OK;
+}
+
+RTSPResult
+rtsp_message_remove_header (RTSPMessage * msg, RTSPHeaderField field)
+{
+  if (msg == NULL)
+    return RTSP_EINVAL;
+
+  g_hash_table_remove (msg->hdr_fields, GINT_TO_POINTER (field));
+
+  return RTSP_ENOTIMPL;
+}
+
+RTSPResult
+rtsp_message_get_header (RTSPMessage * msg, RTSPHeaderField field,
+    gchar ** value)
+{
+  gchar *val;
+
+  if (msg == NULL || value == NULL)
+    return RTSP_EINVAL;
+
+  val = g_hash_table_lookup (msg->hdr_fields, GINT_TO_POINTER (field));
+  if (val == NULL)
+    return RTSP_ENOTIMPL;
+
+  *value = val;
+
+  return RTSP_OK;
+}
+
+RTSPResult
+rtsp_message_get_header_copy (RTSPMessage * msg, RTSPHeaderField field,
+    gchar ** value)
+{
+  gchar *val;
+
+  if (msg == NULL || value == NULL)
+    return RTSP_EINVAL;
+
+  val = g_hash_table_lookup (msg->hdr_fields, GINT_TO_POINTER (field));
+
+  *value = g_strdup (val);
+
+  return RTSP_OK;
+}
+
+
+RTSPResult
+rtsp_message_set_body (RTSPMessage * msg, guint8 * data, guint size)
+{
+  if (msg == NULL)
+    return RTSP_EINVAL;
+
+  if (msg->body)
+    g_free (msg->body);
+
+  msg->body = data;
+  msg->body_size = size;
+
+  return RTSP_OK;
+}
+
+RTSPResult
+rtsp_message_set_body_copy (RTSPMessage * msg, guint8 * data, guint size)
+{
+  return RTSP_ENOTIMPL;
+}
+
+
+RTSPResult
+rtsp_message_get_body (RTSPMessage * msg, guint8 ** data, guint * size)
+{
+  if (msg == NULL || data == NULL || size == NULL)
+    return RTSP_EINVAL;
+
+  *data = msg->body;
+  *size = msg->body_size;
+
+  return RTSP_OK;
+}
+
+RTSPResult
+rtsp_message_get_body_copy (RTSPMessage * msg, guint8 ** data, guint * size)
+{
+  if (msg == NULL || data == NULL || size == NULL)
+    return RTSP_EINVAL;
+
+  *data = g_memdup (msg->body, msg->body_size);
+  *size = msg->body_size;
+
+  return RTSP_OK;
+}
+
+static void
+dump_mem (guint8 * mem, gint size)
+{
+  guint i, j;
+  GString *string = g_string_sized_new (50);
+  GString *chars = g_string_sized_new (18);
+
+  i = j = 0;
+  while (i < size) {
+    if (g_ascii_isprint (mem[i]))
+      g_string_append_printf (chars, "%c", mem[i]);
+    else
+      g_string_append_printf (chars, ".");
+
+    g_string_append_printf (string, "%02x ", mem[i]);
+
+    j++;
+    i++;
+
+    if (j == 16 || i == size) {
+      g_print ("%08x (%p): %-48.48s %-16.16s\n", i - j, mem + i - j,
+          string->str, chars->str);
+      g_string_set_size (string, 0);
+      g_string_set_size (chars, 0);
+      j = 0;
+    }
+  }
+  g_string_free (string, TRUE);
+  g_string_free (chars, TRUE);
+}
+
+static void
+dump_key_value (gpointer key, gpointer value, gpointer data)
+{
+  RTSPHeaderField field = GPOINTER_TO_INT (key);
+
+  g_print ("   key: '%s', value: '%s'\n", rtsp_header_as_text (field),
+      (gchar *) value);
+}
+
+RTSPResult
+rtsp_message_dump (RTSPMessage * msg)
+{
+  guint8 *data;
+  guint size;
+
+  if (msg == NULL)
+    return RTSP_EINVAL;
+
+  if (msg->type == RTSP_MESSAGE_REQUEST) {
+    g_print ("request message %p\n", msg);
+    g_print (" request line:\n");
+    g_print ("   method: '%s'\n",
+        rtsp_method_as_text (msg->type_data.request.method));
+    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_print (" body:\n");
+    rtsp_message_get_body (msg, &data, &size);
+    dump_mem (data, size);
+  } else if (msg->type == RTSP_MESSAGE_RESPONSE) {
+    g_print ("response message %p\n", msg);
+    g_print (" status line:\n");
+    g_print ("   code:   '%d'\n", msg->type_data.response.code);
+    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_print (" body:\n");
+    rtsp_message_get_body (msg, &data, &size);
+    dump_mem (data, size);
+  } else if (msg->type == RTSP_MESSAGE_DATA) {
+    g_print ("data message %p\n", msg);
+    g_print (" channel: '%d'\n", msg->type_data.data.channel);
+    g_print (" size:    '%d'\n", msg->body_size);
+    rtsp_message_get_body (msg, &data, &size);
+    dump_mem (data, size);
+  } else {
+    g_print ("unsupported packet type %d\n", msg->type);
+    return RTSP_EINVAL;
+  }
+  return RTSP_OK;
+}
diff --git a/gst/rtsp/rtspmessage.h b/gst/rtsp/rtspmessage.h
new file mode 100644 (file)
index 0000000..7bed76f
--- /dev/null
@@ -0,0 +1,88 @@
+/* GStreamer
+ * Copyright (C) <2005> Wim Taymans <wim@fluendo.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifndef __RTSP_MESSAGE_H__
+#define __RTSP_MESSAGE_H__
+
+#include <glib.h>
+
+#include <rtspdefs.h>
+
+G_BEGIN_DECLS
+
+typedef enum
+{
+  RTSP_MESSAGE_REQUEST,
+  RTSP_MESSAGE_RESPONSE,
+  RTSP_MESSAGE_DATA,
+} RTSPMsgType;
+
+typedef struct _RTSPMessage
+{
+  RTSPMsgType   type;
+
+  union {
+    struct {
+      RTSPMethod        method;
+      gchar            *uri;
+    } request;
+    struct {
+      RTSPStatusCode    code;
+      gchar            *reason;
+    } response;
+    struct {
+      gint              channel;
+    } data;
+  } type_data;
+
+  GHashTable    *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_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_get_header_copy    (RTSPMessage *msg, RTSPHeaderField field, gchar **value);
+
+RTSPResult     rtsp_message_set_body           (RTSPMessage *msg, guint8 *data, guint size);
+RTSPResult     rtsp_message_set_body_copy      (RTSPMessage *msg, guint8 *data, guint size);
+
+RTSPResult     rtsp_message_get_body           (RTSPMessage *msg, guint8 **data, guint *size);
+RTSPResult     rtsp_message_get_body_copy      (RTSPMessage *msg, guint8 **data, guint *size);
+
+RTSPResult     rtsp_message_dump               (RTSPMessage *msg);
+
+G_END_DECLS
+
+#endif /* __RTSP_MESSAGE_H__ */
diff --git a/gst/rtsp/rtspstream.h b/gst/rtsp/rtspstream.h
new file mode 100644 (file)
index 0000000..1a25142
--- /dev/null
@@ -0,0 +1,32 @@
+/* GStreamer
+ * Copyright (C) <2005> Wim Taymans <wim@fluendo.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifndef __RTSP_STREAM_H__
+#define __RTSP_STREAM_H__
+
+#include <rtspdefs.h>
+
+G_BEGIN_DECLS
+
+typedef struct _RTSPStream {
+} RTSPStream;
+
+G_END_DECLS
+
+#endif /* __RTSP_STREAM_H__ */
diff --git a/gst/rtsp/rtsptransport.c b/gst/rtsp/rtsptransport.c
new file mode 100644 (file)
index 0000000..addf354
--- /dev/null
@@ -0,0 +1,144 @@
+/* GStreamer
+ * Copyright (C) <2005> Wim Taymans <wim@fluendo.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#include "rtsptransport.h"
+
+RTSPResult
+rtsp_transport_new (RTSPTransport ** transport)
+{
+  RTSPTransport *trans;
+
+  if (transport == NULL)
+    return RTSP_EINVAL;
+
+  trans = g_new0 (RTSPTransport, 1);
+
+  *transport = trans;
+
+  return rtsp_transport_init (trans);
+}
+
+RTSPResult
+rtsp_transport_init (RTSPTransport * transport)
+{
+  g_free (transport->destination);
+  g_free (transport->source);
+  g_free (transport->ssrc);
+
+  memset (transport, 0, sizeof (RTSPTransport));
+
+  transport->trans = RTSP_TRANS_RTP;
+  transport->profile = RTSP_PROFILE_AVP;
+  transport->lower_transport = RTSP_LOWER_TRANS_UNKNOWN;
+  transport->mode_play = TRUE;
+  transport->mode_record = FALSE;
+
+  return RTSP_OK;
+}
+
+static void
+parse_mode (RTSPTransport * transport, gchar * str)
+{
+  if (strstr (str, "\"PLAY\""))
+    transport->mode_play = TRUE;
+  else
+    transport->mode_play = FALSE;
+  if (strstr (str, "\"RECORD\""))
+    transport->mode_record = TRUE;
+  else
+    transport->mode_record = FALSE;
+}
+
+static void
+parse_range (RTSPTransport * transport, gchar * str, RTSPRange * range)
+{
+  gchar *minus;
+
+  minus = strstr (str, "-");
+  if (minus) {
+    range->min = atoi (str);
+    range->max = atoi (minus + 1);
+  } else {
+    range->min = atoi (str);
+    range->max = -1;
+  }
+}
+
+RTSPResult
+rtsp_transport_parse (gchar * str, RTSPTransport * transport)
+{
+  gchar **split;
+  gint i;
+
+  if (str == NULL || transport == NULL)
+    return RTSP_EINVAL;
+
+  rtsp_transport_init (transport);
+
+  split = g_strsplit (str, ";", 0);
+  i = 0;
+  while (split[i]) {
+    if (g_str_has_prefix (split[i], "RTP/AVP/UDP")) {
+      transport->lower_transport = RTSP_LOWER_TRANS_UDP;
+    } else if (g_str_has_prefix (split[i], "RTP/AVP/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")) {
+      transport->multicast = FALSE;
+    } else if (g_str_has_prefix (split[i], "destination=")) {
+      transport->destination = g_strdup (split[i] + 12);
+    } else if (g_str_has_prefix (split[i], "source=")) {
+      transport->source = g_strdup (split[i] + 7);
+    } else if (g_str_has_prefix (split[i], "layers=")) {
+      transport->layers = atoi (split[i] + 7);
+    } else if (g_str_has_prefix (split[i], "mode=")) {
+      parse_mode (transport, split[i] + 5);
+    } else if (g_str_has_prefix (split[i], "append")) {
+      transport->append = TRUE;
+    } else if (g_str_has_prefix (split[i], "interleaved=")) {
+      parse_range (transport, split[i] + 12, &transport->interleaved);
+    } else if (g_str_has_prefix (split[i], "ttl=")) {
+      transport->ttl = atoi (split[i] + 4);
+    } else if (g_str_has_prefix (split[i], "port=")) {
+      parse_range (transport, split[i] + 5, &transport->port);
+    } else if (g_str_has_prefix (split[i], "client_port=")) {
+      parse_range (transport, split[i] + 12, &transport->client_port);
+    } else if (g_str_has_prefix (split[i], "server_port=")) {
+      parse_range (transport, split[i] + 12, &transport->server_port);
+    } else if (g_str_has_prefix (split[i], "ssrc=")) {
+      transport->ssrc = g_strdup (split[i] + 5);
+    } else {
+      /* unknown field... */
+      g_warning ("unknown transport field \"%s\"", split[i]);
+    }
+    i++;
+  }
+  g_strfreev (split);
+
+  return RTSP_OK;
+}
+
+RTSPResult
+rtsp_transport_free (RTSPTransport * transport)
+{
+  rtsp_transport_init (transport);
+  g_free (transport);
+  return RTSP_OK;
+}
diff --git a/gst/rtsp/rtsptransport.h b/gst/rtsp/rtsptransport.h
new file mode 100644 (file)
index 0000000..288275f
--- /dev/null
@@ -0,0 +1,81 @@
+/* GStreamer
+ * Copyright (C) <2005> Wim Taymans <wim@fluendo.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifndef __RTSP_TRANSPORT_H__
+#define __RTSP_TRANSPORT_H__
+
+#include <rtspdefs.h>
+
+G_BEGIN_DECLS
+
+typedef enum {
+  RTSP_TRANS_RTP,
+} RTSPTransMode;
+
+typedef enum {
+  RTSP_PROFILE_AVP,
+} RTSPProfile;
+
+typedef enum {
+  RTSP_LOWER_TRANS_UNKNOWN,
+  RTSP_LOWER_TRANS_UDP,
+  RTSP_LOWER_TRANS_TCP,
+} RTSPLowerTrans;
+
+typedef struct
+{
+  gint min;
+  gint max;
+} RTSPRange;
+
+typedef struct _RTSPTransport {
+  RTSPTransMode         trans;
+  RTSPProfile    profile;
+  RTSPLowerTrans lower_transport;
+
+  gboolean      multicast;
+  gchar                *destination;
+  gchar                *source;
+  gint                  layers;
+  gboolean      mode_play;
+  gboolean      mode_record;
+  gboolean      append;
+  RTSPRange     interleaved;
+
+  /* mulitcast specific */
+  gint ttl;
+
+  /* RTP specific */
+  RTSPRange     port;
+  RTSPRange     client_port;
+  RTSPRange     server_port;
+  gchar                *ssrc;
+  
+} RTSPTransport;
+
+RTSPResult      rtsp_transport_new      (RTSPTransport **transport);
+RTSPResult      rtsp_transport_init     (RTSPTransport *transport);
+
+RTSPResult     rtsp_transport_parse    (gchar *str, RTSPTransport *transport);
+
+RTSPResult      rtsp_transport_free     (RTSPTransport *transport);
+
+G_END_DECLS
+
+#endif /* __RTSP_TRANSPORT_H__ */
diff --git a/gst/rtsp/rtspurl.c b/gst/rtsp/rtspurl.c
new file mode 100644 (file)
index 0000000..25c85a2
--- /dev/null
@@ -0,0 +1,101 @@
+/* GStreamer
+ * Copyright (C) <2005> Wim Taymans <wim@fluendo.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+#include <stdlib.h>
+
+#include "rtspurl.h"
+
+#define RTSP_PROTO     "rtsp://"
+#define RTSP_PROTO_LEN 7
+#define RTSPU_PROTO    "rtspu://"
+#define RTSPU_PROTO_LEN        8
+
+/* format is rtsp[u]://[user:passwd@]host[:port]/abspath */
+
+RTSPResult
+rtsp_url_parse (const gchar * urlstr, RTSPUrl ** url)
+{
+  RTSPUrl *res;
+  gchar *p, *slash, *at, *col;
+
+  res = g_new0 (RTSPUrl, 1);
+
+  p = (gchar *) urlstr;
+  if (g_str_has_prefix (p, RTSP_PROTO)) {
+    res->protocol = RTSP_PROTO_TCP;
+    p += RTSP_PROTO_LEN;
+  } else if (g_str_has_prefix (p, RTSPU_PROTO)) {
+    res->protocol = RTSP_PROTO_UDP;
+    p += RTSPU_PROTO_LEN;
+  } else {
+    return RTSP_EINVAL;
+  }
+
+  slash = g_strrstr (p, "/");
+  at = g_strrstr (p, "@");
+
+  if (at && slash && at > slash)
+    at = NULL;
+
+  if (at) {
+    col = g_strrstr (p, ":");
+
+    if (col == NULL)
+      return RTSP_EINVAL;
+
+    res->user = g_strndup (p, col - p);
+    col++;
+    res->passwd = g_strndup (col, col - at);
+
+    p = at + 1;
+  }
+
+  col = g_strrstr (p, ":");
+  if (col) {
+    res->host = g_strndup (p, col - p);
+    p = col + 1;
+    res->port = strtoul (p, (char **) &p, 10);
+    if (slash)
+      p = slash + 1;
+  } else {
+    res->port = RTSP_DEFAULT_PORT;
+    if (!slash) {
+      res->host = g_strdup (p);
+      p = NULL;
+    } else {
+      res->host = g_strndup (p, slash - p);
+      p = slash + 1;
+    }
+  }
+  if (p)
+    res->abspath = g_strdup (p);
+
+  *url = res;
+
+  return RTSP_OK;
+}
+
+void
+rtsp_url_free (RTSPUrl * url)
+{
+  g_free (url->user);
+  g_free (url->passwd);
+  g_free (url->host);
+  g_free (url->abspath);
+  g_free (url);
+}
diff --git a/gst/rtsp/rtspurl.h b/gst/rtsp/rtspurl.h
new file mode 100644 (file)
index 0000000..2492b87
--- /dev/null
@@ -0,0 +1,46 @@
+/* GStreamer
+ * Copyright (C) <2005> Wim Taymans <wim@fluendo.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifndef __RTSP_URL_H__
+#define __RTSP_URL_H__
+
+#include <glib.h>
+
+#include <rtspdefs.h>
+
+G_BEGIN_DECLS
+
+#define RTSP_DEFAULT_PORT      554
+
+typedef struct _RTSPUrl {
+  RTSPProto   protocol;
+  RTSPFamily  family;
+  gchar      *user;
+  gchar      *passwd;
+  gchar      *host;
+  guint16     port;
+  gchar      *abspath;
+} RTSPUrl;
+
+RTSPResult     rtsp_url_parse          (const gchar *urlstr, RTSPUrl **url);
+void           rtsp_url_free           (RTSPUrl *url);
+
+G_END_DECLS
+
+#endif /* __RTSP_URL_H__ */
diff --git a/gst/rtsp/sdp.h b/gst/rtsp/sdp.h
new file mode 100644 (file)
index 0000000..6efb9f8
--- /dev/null
@@ -0,0 +1,25 @@
+/* GStreamer
+ * Copyright (C) <2005> Wim Taymans <wim@fluendo.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifndef __SDP_H__
+#define __SDP_H__
+
+#include <sdpmessage.h>
+
+#endif /* __SDP_H__ */
diff --git a/gst/rtsp/sdpmessage.c b/gst/rtsp/sdpmessage.c
new file mode 100644 (file)
index 0000000..a9044c9
--- /dev/null
@@ -0,0 +1,709 @@
+/* GStreamer
+ * Copyright (C) <2005> Wim Taymans <wim@fluendo.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#include <stdlib.h>
+#include <string.h>
+
+#include "sdpmessage.h"
+
+#define FREE_STRING(field)     g_free ((field)); (field) = NULL;
+#define FREE_ARRAY(field)      \
+G_STMT_START {                 \
+  if (field)                   \
+    g_array_free (field, TRUE);        \
+  field = NULL;                        \
+} G_STMT_END
+#define REPLACE_STRING(field,val)      FREE_STRING(field);field=g_strdup (val);
+
+#define INIT_ARRAY(field,type) \
+G_STMT_START {                 \
+  if (field)                   \
+    g_array_set_size (field,0);        \
+  else                         \
+    field = g_array_new (FALSE, TRUE, sizeof(type));   \
+} G_STMT_END
+
+#define DEFINE_STRING_SETTER(field)                                    \
+RTSPResult sdp_message_set_##field (SDPMessage *msg, gchar *val) {     \
+  g_free (msg->field);                                                 \
+  msg->field = g_strdup (val);                                         \
+  return RTSP_OK;                                                      \
+}
+#define DEFINE_STRING_GETTER(field)                                    \
+char* sdp_message_get_##field (SDPMessage *msg) {                      \
+  return msg->field;                                                   \
+}
+
+#define DEFINE_ARRAY_LEN(field)                                                \
+gint sdp_message_##field##_len (SDPMessage *msg) {                     \
+  return ((msg)->field->len);                                          \
+}
+#define DEFINE_ARRAY_GETTER(method,field,type)                                 \
+type sdp_message_get_##method (SDPMessage *msg, gint i) {              \
+  return g_array_index ((msg)->field, type, i);                                \
+}
+#define DEFINE_ARRAY_P_GETTER(method,field,type)                                       \
+type * sdp_message_get_##method (SDPMessage *msg, gint i) {            \
+  return &g_array_index ((msg)->field, type, i);                               \
+}
+#define DEFINE_ARRAY_ADDER(method,field,type,dup_method)                       \
+RTSPResult sdp_message_add_##method (SDPMessage *msg, type val) {      \
+  type v = dup_method(val);                                            \
+  g_array_append_val((msg)->field, v);                                 \
+  return RTSP_OK;                                                      \
+}
+
+
+
+RTSPResult
+sdp_message_new (SDPMessage ** msg)
+{
+  SDPMessage *newmsg;
+
+  if (msg == NULL)
+    return RTSP_EINVAL;
+
+  newmsg = g_new0 (SDPMessage, 1);
+
+  *msg = newmsg;
+
+  return sdp_message_init (newmsg);
+}
+
+RTSPResult
+sdp_message_init (SDPMessage * msg)
+{
+  if (msg == NULL)
+    return RTSP_EINVAL;
+
+  FREE_STRING (msg->version);
+  FREE_STRING (msg->origin.username);
+  FREE_STRING (msg->origin.sess_id);
+  FREE_STRING (msg->origin.sess_version);
+  FREE_STRING (msg->origin.nettype);
+  FREE_STRING (msg->origin.addrtype);
+  FREE_STRING (msg->origin.addr);
+  FREE_STRING (msg->session_name);
+  FREE_STRING (msg->information);
+  FREE_STRING (msg->uri);
+  INIT_ARRAY (msg->emails, gchar *);
+  INIT_ARRAY (msg->phones, gchar *);
+  FREE_STRING (msg->connection.nettype);
+  FREE_STRING (msg->connection.addrtype);
+  FREE_STRING (msg->connection.address);
+  msg->connection.ttl = 0;
+  msg->connection.addr_number = 0;
+  INIT_ARRAY (msg->bandwidths, SDPBandwidth);
+  INIT_ARRAY (msg->times, SDPTime);
+  INIT_ARRAY (msg->zones, SDPZone);
+  FREE_STRING (msg->key.type);
+  FREE_STRING (msg->key.data);
+  INIT_ARRAY (msg->attributes, SDPAttribute);
+  INIT_ARRAY (msg->medias, SDPMedia);
+
+  return RTSP_OK;
+}
+
+RTSPResult
+sdp_message_clean (SDPMessage * msg)
+{
+  if (msg == NULL)
+    return RTSP_EINVAL;
+
+  FREE_ARRAY (msg->emails);
+  FREE_ARRAY (msg->phones);
+  FREE_ARRAY (msg->bandwidths);
+  FREE_ARRAY (msg->times);
+  FREE_ARRAY (msg->zones);
+  FREE_ARRAY (msg->attributes);
+  FREE_ARRAY (msg->medias);
+
+  return RTSP_OK;
+}
+
+RTSPResult
+sdp_message_free (SDPMessage * msg)
+{
+  if (msg == NULL)
+    return RTSP_EINVAL;
+
+  sdp_message_clean (msg);
+
+  g_free (msg);
+
+  return RTSP_OK;
+}
+
+
+RTSPResult
+sdp_media_new (SDPMedia ** media)
+{
+  SDPMedia *newmedia;
+
+  if (media == NULL)
+    return RTSP_EINVAL;
+
+  newmedia = g_new0 (SDPMedia, 1);
+
+  *media = newmedia;
+
+  return sdp_media_init (newmedia);
+}
+
+RTSPResult
+sdp_media_init (SDPMedia * media)
+{
+  if (media == NULL)
+    return RTSP_EINVAL;
+
+  FREE_STRING (media->media);
+  media->port = 0;
+  media->num_ports = 0;
+  FREE_STRING (media->proto);
+  INIT_ARRAY (media->fmts, gchar *);
+  FREE_STRING (media->information);
+  INIT_ARRAY (media->connections, SDPConnection);
+  INIT_ARRAY (media->bandwidths, SDPBandwidth);
+  FREE_STRING (media->key.type);
+  FREE_STRING (media->key.data);
+  INIT_ARRAY (media->attributes, SDPAttribute);
+
+  return RTSP_OK;
+}
+
+DEFINE_STRING_SETTER (version);
+DEFINE_STRING_GETTER (version);
+
+RTSPResult
+sdp_message_set_origin (SDPMessage * msg, gchar * username, gchar * sess_id,
+    gchar * sess_version, gchar * nettype, gchar * addrtype, gchar * addr)
+{
+  REPLACE_STRING (msg->origin.username, username);
+  REPLACE_STRING (msg->origin.sess_id, sess_id);
+  REPLACE_STRING (msg->origin.sess_version, sess_version);
+  REPLACE_STRING (msg->origin.nettype, nettype);
+  REPLACE_STRING (msg->origin.addrtype, addrtype);
+  REPLACE_STRING (msg->origin.addr, addr);
+  return RTSP_OK;
+}
+
+SDPOrigin *
+sdp_message_get_origin (SDPMessage * msg)
+{
+  return &msg->origin;
+}
+
+DEFINE_STRING_SETTER (session_name);
+DEFINE_STRING_GETTER (session_name);
+DEFINE_STRING_SETTER (information);
+DEFINE_STRING_GETTER (information);
+DEFINE_STRING_SETTER (uri);
+DEFINE_STRING_GETTER (uri);
+
+DEFINE_ARRAY_LEN (emails);
+DEFINE_ARRAY_GETTER (email, emails, gchar *);
+DEFINE_ARRAY_ADDER (email, emails, gchar *, g_strdup);
+
+DEFINE_ARRAY_LEN (phones);
+DEFINE_ARRAY_GETTER (phone, phones, gchar *);
+DEFINE_ARRAY_ADDER (phone, phones, gchar *, g_strdup);
+
+RTSPResult
+sdp_message_set_connection (SDPMessage * msg, gchar * nettype, gchar * addrtype,
+    gchar * address, gint ttl, gint addr_number)
+{
+  REPLACE_STRING (msg->connection.nettype, nettype);
+  REPLACE_STRING (msg->connection.addrtype, addrtype);
+  REPLACE_STRING (msg->connection.address, address);
+  msg->connection.ttl = ttl;
+  msg->connection.addr_number = addr_number;
+  return RTSP_OK;
+}
+
+SDPConnection *
+sdp_message_get_connection (SDPMessage * msg)
+{
+  return &msg->connection;
+}
+
+DEFINE_ARRAY_LEN (bandwidths);
+DEFINE_ARRAY_P_GETTER (bandwidth, bandwidths, SDPBandwidth);
+
+RTSPResult
+sdp_message_add_bandwidth (SDPMessage * msg, gchar * bwtype, gint bandwidth)
+{
+  SDPBandwidth bw;
+
+  bw.bwtype = g_strdup (bwtype);
+  bw.bandwidth = bandwidth;
+
+  g_array_append_val (msg->bandwidths, bw);
+
+  return RTSP_OK;
+}
+
+DEFINE_ARRAY_LEN (times);
+DEFINE_ARRAY_P_GETTER (time, times, SDPTime);
+
+RTSPResult
+sdp_message_add_time (SDPMessage * msg, gchar * time)
+{
+  return RTSP_OK;
+}
+
+DEFINE_ARRAY_LEN (zones);
+DEFINE_ARRAY_P_GETTER (zone, zones, SDPZone);
+RTSPResult
+sdp_message_add_zone (SDPMessage * msg, gchar * time, gchar * typed_time)
+{
+  SDPZone zone;
+
+  zone.time = g_strdup (time);
+  zone.typed_time = g_strdup (typed_time);
+
+  g_array_append_val (msg->zones, zone);
+
+  return RTSP_OK;
+}
+
+RTSPResult
+sdp_message_set_key (SDPMessage * msg, gchar * type, gchar * data)
+{
+  REPLACE_STRING (msg->key.type, type);
+  REPLACE_STRING (msg->key.data, data);
+  return RTSP_OK;
+}
+
+SDPKey *
+sdp_message_get_key (SDPMessage * msg)
+{
+  return &msg->key;
+}
+
+
+DEFINE_ARRAY_LEN (attributes);
+DEFINE_ARRAY_P_GETTER (attribute, attributes, SDPAttribute);
+gchar *
+sdp_message_get_attribute_val (SDPMessage * msg, gchar * key)
+{
+  gint i;
+
+  for (i = 0; i < msg->attributes->len; i++) {
+    SDPAttribute *attr;
+
+    attr = &g_array_index (msg->attributes, SDPAttribute, i);
+    if (!strcmp (attr->key, key))
+      return attr->value;
+  }
+  return NULL;
+}
+
+RTSPResult
+sdp_message_add_attribute (SDPMessage * msg, gchar * key, gchar * value)
+{
+  SDPAttribute attr;
+
+  attr.key = g_strdup (key);
+  attr.value = g_strdup (value);
+
+  g_array_append_val (msg->attributes, attr);
+
+  return RTSP_OK;
+}
+
+DEFINE_ARRAY_LEN (medias);
+DEFINE_ARRAY_P_GETTER (media, medias, SDPMedia);
+RTSPResult
+sdp_message_add_media (SDPMessage * msg, SDPMedia * media)
+{
+  gint len;
+  SDPMedia *nmedia;
+
+  len = msg->medias->len;
+  g_array_set_size (msg->medias, len + 1);
+  nmedia = &g_array_index (msg->medias, SDPMedia, len);
+
+  memcpy (nmedia, media, sizeof (SDPMedia));
+
+  return RTSP_OK;
+}
+
+/* media access */
+
+RTSPResult
+sdp_media_add_attribute (SDPMedia * media, gchar * key, gchar * value)
+{
+  SDPAttribute attr;
+
+  attr.key = g_strdup (key);
+  attr.value = g_strdup (value);
+
+  g_array_append_val (media->attributes, attr);
+
+  return RTSP_OK;
+}
+
+RTSPResult
+sdp_media_add_bandwidth (SDPMedia * media, gchar * bwtype, gint bandwidth)
+{
+  SDPBandwidth bw;
+
+  bw.bwtype = g_strdup (bwtype);
+  bw.bandwidth = bandwidth;
+
+  g_array_append_val (media->bandwidths, bw);
+
+  return RTSP_OK;
+}
+
+RTSPResult
+sdp_media_add_format (SDPMedia * media, gchar * format)
+{
+  gchar *fmt;
+
+  fmt = g_strdup (format);
+
+  g_array_append_val (media->fmts, fmt);
+
+  return RTSP_OK;
+}
+
+gchar *
+sdp_media_get_attribute_val (SDPMedia * media, gchar * key)
+{
+  gint i;
+
+  for (i = 0; i < media->attributes->len; i++) {
+    SDPAttribute *attr;
+
+    attr = &g_array_index (media->attributes, SDPAttribute, i);
+    if (!strcmp (attr->key, key))
+      return attr->value;
+  }
+  return NULL;
+}
+
+static void
+read_string (gchar * dest, gint size, gchar ** src)
+{
+  gint idx;
+
+  idx = 0;
+  /* skip spaces */
+  while (g_ascii_isspace (**src))
+    (*src)++;
+
+  while (!g_ascii_isspace (**src) && **src != '\0') {
+    if (idx < size - 1)
+      dest[idx++] = **src;
+    (*src)++;
+  }
+  if (size > 0)
+    dest[idx] = '\0';
+}
+
+static void
+read_string_del (gchar * dest, gint size, gchar del, gchar ** src)
+{
+  gint idx;
+
+  idx = 0;
+  /* skip spaces */
+  while (g_ascii_isspace (**src))
+    (*src)++;
+
+  while (**src != del && **src != '\0') {
+    if (idx < size - 1)
+      dest[idx++] = **src;
+    (*src)++;
+  }
+  if (size > 0)
+    dest[idx] = '\0';
+}
+
+enum
+{
+  SDP_SESSION,
+  SDP_MEDIA,
+};
+
+typedef struct
+{
+  gint state;
+  SDPMessage *msg;
+  SDPMedia *media;
+} SDPContext;
+
+static gboolean
+sdp_parse_line (SDPContext * c, gchar type, guint8 * buffer)
+{
+  gchar str[4096];
+  gchar *p = buffer;
+
+#define READ_STRING(field) read_string (str, sizeof(str), &p);REPLACE_STRING (field, str);
+#define READ_INT(field) read_string (str, sizeof(str), &p);field = atoi(str);
+
+  switch (type) {
+    case 'v':
+      if (buffer[0] != '0')
+        g_print ("wrong SDP version\n");
+      sdp_message_set_version (c->msg, buffer);
+      break;
+    case 'o':
+      READ_STRING (c->msg->origin.username);
+      READ_STRING (c->msg->origin.sess_id);
+      READ_STRING (c->msg->origin.sess_version);
+      READ_STRING (c->msg->origin.nettype);
+      READ_STRING (c->msg->origin.addrtype);
+      READ_STRING (c->msg->origin.addr);
+      break;
+    case 's':
+      REPLACE_STRING (c->msg->session_name, buffer);
+      break;
+    case 'i':
+      if (c->state == SDP_SESSION) {
+        REPLACE_STRING (c->msg->information, buffer);
+      } else {
+        REPLACE_STRING (c->media->information, buffer);
+      }
+      break;
+    case 'u':
+      REPLACE_STRING (c->msg->uri, buffer);
+      break;
+    case 'e':
+      sdp_message_add_email (c->msg, buffer);
+      break;
+    case 'p':
+      sdp_message_add_phone (c->msg, buffer);
+      break;
+    case 'c':
+      READ_STRING (c->msg->connection.nettype);
+      READ_STRING (c->msg->connection.addrtype);
+      READ_STRING (c->msg->connection.address);
+      READ_INT (c->msg->connection.ttl);
+      READ_INT (c->msg->connection.addr_number);
+      break;
+    case 'b':
+    {
+      gchar str2[4096];
+
+      read_string_del (str, sizeof (str), ':', &p);
+      read_string (str2, sizeof (str2), &p);
+      if (c->state == SDP_SESSION)
+        sdp_message_add_bandwidth (c->msg, str, atoi (str2));
+      else
+        sdp_media_add_bandwidth (c->media, str, atoi (str2));
+      break;
+    }
+    case 't':
+      break;
+    case 'k':
+
+      break;
+    case 'a':
+      read_string_del (str, sizeof (str), ':', &p);
+      if (p != '\0')
+        p++;
+      if (c->state == SDP_SESSION)
+        sdp_message_add_attribute (c->msg, str, p);
+      else
+        sdp_media_add_attribute (c->media, str, p);
+      break;
+    case 'm':
+    {
+      gchar *slash;
+      SDPMedia nmedia = { 0 };
+
+      c->state = SDP_MEDIA;
+      sdp_media_init (&nmedia);
+
+      READ_STRING (nmedia.media);
+      read_string (str, sizeof (str), &p);
+      slash = g_strrstr (str, "/");
+      if (slash) {
+        *slash = '\0';
+        nmedia.port = atoi (str);
+        nmedia.num_ports = atoi (slash + 1);
+      } else {
+        nmedia.port = atoi (str);
+        nmedia.num_ports = -1;
+      }
+      READ_STRING (nmedia.proto);
+      do {
+        read_string (str, sizeof (str), &p);
+        sdp_media_add_format (&nmedia, str);
+      } while (*p != '\0');
+
+      g_print ("%p\n", &nmedia);
+      sdp_message_add_media (c->msg, &nmedia);
+      c->media =
+          &g_array_index (c->msg->medias, SDPMedia, c->msg->medias->len - 1);
+      g_print ("%p\n", c->media);
+      break;
+    }
+    default:
+      break;
+  }
+  return TRUE;
+}
+
+RTSPResult
+sdp_message_parse_buffer (guint8 * data, guint size, SDPMessage * msg)
+{
+  gchar *p;
+  SDPContext c;
+  gchar type;
+  gchar buffer[4096];
+  gint idx = 0;
+
+  if (msg == NULL || data == NULL || size == 0)
+    return RTSP_EINVAL;
+
+  c.state = SDP_SESSION;
+  c.msg = msg;
+
+  p = data;
+  while (TRUE) {
+    while (g_ascii_isspace (*p))
+      p++;
+
+    type = *p++;
+    if (type == '\0')
+      break;
+
+    if (*p != '=')
+      goto line_done;
+    p++;
+
+    idx = 0;
+    while (*p != '\n' && *p != '\r' && *p != '\0') {
+      if (idx < sizeof (buffer) - 1)
+        buffer[idx++] = *p;
+      p++;
+    }
+    buffer[idx] = '\0';
+    sdp_parse_line (&c, type, buffer);
+
+  line_done:
+    while (*p != '\n' && *p != '\0')
+      p++;
+    if (*p == '\n')
+      p++;
+  }
+
+  return RTSP_OK;
+}
+
+static void
+print_media (SDPMedia * media)
+{
+  g_print ("   media:       '%s'\n", media->media);
+  g_print ("   port:        '%d'\n", media->port);
+  g_print ("   num_ports:   '%d'\n", media->num_ports);
+  g_print ("   proto:       '%s'\n", media->proto);
+  if (media->fmts->len > 0) {
+    gint i;
+
+    g_print ("   formats:\n");
+    for (i = 0; i < media->fmts->len; i++) {
+      g_print ("    format  '%s'\n", g_array_index (media->fmts, gchar *, i));
+    }
+  }
+  g_print ("   information: '%s'\n", media->information);
+  g_print ("   key:\n");
+  g_print ("    type:       '%s'\n", media->key.type);
+  g_print ("    data:       '%s'\n", media->key.data);
+  if (media->attributes->len > 0) {
+    gint i;
+
+    g_print ("   attributes:\n");
+    for (i = 0; i < media->attributes->len; i++) {
+      SDPAttribute *attr = &g_array_index (media->attributes, SDPAttribute, i);
+
+      g_print ("    attribute '%s' : '%s'\n", attr->key, attr->value);
+    }
+  }
+}
+
+RTSPResult
+sdp_message_dump (SDPMessage * msg)
+{
+  if (msg == NULL)
+    return RTSP_EINVAL;
+
+  g_print ("sdp packet %p:\n", msg);
+  g_print (" version:       '%s'\n", msg->version);
+  g_print (" origin:\n");
+  g_print ("  username:     '%s'\n", msg->origin.username);
+  g_print ("  sess_id:      '%s'\n", msg->origin.sess_id);
+  g_print ("  sess_version: '%s'\n", msg->origin.sess_version);
+  g_print ("  nettype:      '%s'\n", msg->origin.nettype);
+  g_print ("  addrtype:     '%s'\n", msg->origin.addrtype);
+  g_print ("  addr:         '%s'\n", msg->origin.addr);
+  g_print (" session_name:  '%s'\n", msg->session_name);
+  g_print (" information:   '%s'\n", msg->information);
+  g_print (" uri:           '%s'\n", msg->uri);
+  if (msg->emails->len > 0) {
+    gint i;
+
+    g_print (" emails:\n");
+    for (i = 0; i < msg->emails->len; i++) {
+      g_print ("  email '%s'\n", g_array_index (msg->emails, gchar *, i));
+    }
+  }
+  if (msg->phones->len > 0) {
+    gint i;
+
+    g_print (" phones:\n");
+    for (i = 0; i < msg->phones->len; i++) {
+      g_print ("  phone '%s'\n", g_array_index (msg->phones, gchar *, i));
+    }
+  }
+  g_print (" connection:\n");
+  g_print ("  nettype:      '%s'\n", msg->connection.nettype);
+  g_print ("  addrtype:     '%s'\n", msg->connection.addrtype);
+  g_print ("  address:      '%s'\n", msg->connection.address);
+  g_print ("  ttl:          '%d'\n", msg->connection.ttl);
+  g_print ("  addr_number:  '%d'\n", msg->connection.addr_number);
+  g_print (" key:\n");
+  g_print ("  type:         '%s'\n", msg->key.type);
+  g_print ("  data:         '%s'\n", msg->key.data);
+  if (msg->attributes->len > 0) {
+    gint i;
+
+    g_print (" attributes:\n");
+    for (i = 0; i < msg->attributes->len; i++) {
+      SDPAttribute *attr = &g_array_index (msg->attributes, SDPAttribute, i);
+
+      g_print ("  attribute '%s' : '%s'\n", attr->key, attr->value);
+    }
+  }
+  if (msg->medias->len > 0) {
+    gint i;
+
+    g_print (" medias:\n");
+    for (i = 0; i < msg->medias->len; i++) {
+      g_print ("  media %d:\n", i);
+      print_media (&g_array_index (msg->medias, SDPMedia, i));
+    }
+  }
+
+
+  return RTSP_OK;
+}
diff --git a/gst/rtsp/sdpmessage.h b/gst/rtsp/sdpmessage.h
new file mode 100644 (file)
index 0000000..64b8ed9
--- /dev/null
@@ -0,0 +1,165 @@
+/* GStreamer
+ * Copyright (C) <2005> Wim Taymans <wim@fluendo.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifndef __SDP_MESSAGE_H__
+#define __SDP_MESSAGE_H__
+
+#include <glib.h>
+
+#include <rtspdefs.h>
+
+G_BEGIN_DECLS
+
+typedef struct {
+  gchar *username;
+  gchar *sess_id;
+  gchar *sess_version;
+  gchar *nettype;
+  gchar *addrtype;
+  gchar *addr;
+} SDPOrigin;
+
+typedef struct {
+  gchar *nettype; 
+  gchar *addrtype; 
+  gchar *address;
+  gint   ttl;
+  gint   addr_number;
+} SDPConnection;
+
+typedef struct {
+  gchar *bwtype;
+  gint   bandwidth;
+} SDPBandwidth;
+
+typedef struct {
+  gchar *start;
+  gchar *stop;
+  gint   n_repeat;
+  gchar *repeat[];
+} SDPTime;
+
+typedef struct {
+  gchar *time;
+  gchar *typed_time;
+} SDPZone;
+
+typedef struct {
+  gchar *type;
+  gchar *data;
+} SDPKey;
+
+typedef struct {
+  gchar *key;
+  gchar *value;
+} SDPAttribute;
+
+typedef struct {
+  gchar        *media;
+  gint          port;
+  gint          num_ports;
+  gchar        *proto;
+  GArray       *fmts;
+  gchar        *information;
+  GArray       *connections;
+  GArray       *bandwidths;
+  SDPKey        key;
+  GArray       *attributes;
+} SDPMedia;
+
+typedef struct {
+  gchar        *version;
+  SDPOrigin     origin;
+  gchar        *session_name;
+  gchar        *information;
+  gchar        *uri;   
+  GArray       *emails;
+  GArray       *phones;
+  SDPConnection  connection;
+  GArray       *bandwidths;
+  GArray       *times;
+  GArray       *zones;
+  SDPKey        key;
+  GArray       *attributes;
+  GArray       *medias;
+} SDPMessage;
+
+/* Session descriptions */
+RTSPResult     sdp_message_new                 (SDPMessage **msg);
+RTSPResult     sdp_message_init                (SDPMessage *msg);
+RTSPResult     sdp_message_clean               (SDPMessage *msg);
+RTSPResult     sdp_message_free                (SDPMessage *msg);
+
+RTSPResult     sdp_message_parse_buffer        (guint8 *data, guint size, SDPMessage *msg);
+
+RTSPResult     sdp_message_set_version         (SDPMessage *msg, gchar *version);
+gchar*         sdp_message_get_version         (SDPMessage *msg);
+RTSPResult     sdp_message_set_origin          (SDPMessage *msg, gchar *username, gchar *sess_id,
+                                                gchar *sess_version, gchar *nettype,
+                                                gchar *addrtype, gchar *addr);
+SDPOrigin*     sdp_message_get_origin          (SDPMessage *msg);
+RTSPResult     sdp_message_set_session_name    (SDPMessage *msg, gchar *session_name);
+gchar*         sdp_message_get_session_name    (SDPMessage *msg);
+RTSPResult     sdp_message_set_information     (SDPMessage *msg, gchar *information);
+gchar*         sdp_message_get_information     (SDPMessage *msg);
+RTSPResult     sdp_message_set_uri             (SDPMessage *msg, gchar *uri);
+gchar*         sdp_message_get_uri             (SDPMessage *msg);
+gint           sdp_message_emails_len          (SDPMessage *msg);
+gchar*         sdp_message_get_email           (SDPMessage *msg, gint i);
+RTSPResult     sdp_message_add_email           (SDPMessage *msg, gchar *email);
+gint           sdp_message_phones_len          (SDPMessage *msg);
+gchar*         sdp_message_get_phone           (SDPMessage *msg, gint i);
+RTSPResult     sdp_message_add_phone           (SDPMessage *msg, gchar *phone);
+RTSPResult     sdp_message_set_connection      (SDPMessage *msg, gchar *nettype, gchar *addrtype,
+                                                gchar *address, gint ttl, gint addr_number);
+SDPConnection*         sdp_message_get_connection      (SDPMessage *msg);
+gint           sdp_message_bandwidths_len      (SDPMessage *msg);
+SDPBandwidth*  sdp_message_get_bandwidth       (SDPMessage *msg, gint i);
+RTSPResult     sdp_message_add_bandwidth       (SDPMessage *msg, gchar *bwtype, gint bandwidth);
+gint           sdp_message_times_len           (SDPMessage *msg);
+SDPTime*       sdp_message_get_time            (SDPMessage *msg, gint i);
+RTSPResult     sdp_message_add_time            (SDPMessage *msg, gchar *time);
+gint           sdp_message_zones_len           (SDPMessage *msg);
+SDPZone*       sdp_message_get_zone            (SDPMessage *msg, gint i);
+RTSPResult     sdp_message_add_zone            (SDPMessage *msg, gchar *time, gchar *typed_time);
+RTSPResult     sdp_message_set_key             (SDPMessage *msg, gchar *type, gchar *data);
+SDPKey*        sdp_message_get_key             (SDPMessage *msg);
+gint           sdp_message_attributes_len      (SDPMessage *msg);
+SDPAttribute*  sdp_message_get_attribute       (SDPMessage *msg, gint i);
+gchar*         sdp_message_get_attribute_val   (SDPMessage *msg, gchar *key);
+RTSPResult     sdp_message_add_attribute       (SDPMessage *msg, gchar *key, gchar *value);
+gint           sdp_message_medias_len          (SDPMessage *msg);
+SDPMedia*      sdp_message_get_media           (SDPMessage *msg, gint i);
+RTSPResult     sdp_message_add_media           (SDPMessage *msg, SDPMedia *media);
+
+
+RTSPResult     sdp_message_dump                (SDPMessage *msg);
+
+/* Media descriptions */
+RTSPResult     sdp_media_new                   (SDPMedia **media);
+RTSPResult     sdp_media_init                  (SDPMedia *media);
+RTSPResult     sdp_media_clean                 (SDPMedia *media);
+RTSPResult     sdp_media_free                  (SDPMedia *media);
+
+gchar*         sdp_media_get_attribute_val     (SDPMedia *media, gchar *key);
+
+
+G_END_DECLS
+
+#endif /* __SDP_MESSAGE_H__ */
diff --git a/gst/rtsp/test.c b/gst/rtsp/test.c
new file mode 100644 (file)
index 0000000..4cf1dab
--- /dev/null
@@ -0,0 +1,179 @@
+/* GStreamer
+ * Copyright (C) <2005> Wim Taymans <wim@fluendo.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#include <stdio.h>
+
+#include "sdp.h"
+#include "rtsp.h"
+
+int
+main (int argc, gchar * argv[])
+{
+  RTSPUrl *url;
+  RTSPConnection *conn;
+  RTSPResult res;
+  RTSPMessage request = { 0 };
+  gchar *urlstr;
+  RTSPMessage response = { 0 };
+  SDPMessage sdp = { 0 };
+
+  urlstr = "rtsp://thread:5454/south-rtsp.mp3";
+
+  /* create url */
+  g_print ("parsing url \"%s\"...\n", urlstr);
+  res = rtsp_url_parse (urlstr, &url);
+  if (res != RTSP_OK) {
+    g_print ("error parsing url \"%s\"\n", urlstr);
+    return (-1);
+  }
+
+  g_print ("  url host: %s\n", url->host);
+  g_print ("  url port: %d\n", url->port);
+  g_print ("  url path: %s\n", url->abspath);
+
+  /* open connection */
+  g_print ("opening connection...\n");
+  res = rtsp_connection_open (url, &conn);
+  if (res != RTSP_OK) {
+    g_print ("error opening connection to \"%s\"\n", urlstr);
+    return (-1);
+  }
+
+  /* do describe */
+  {
+    res = rtsp_message_init_request (RTSP_DESCRIBE, urlstr, &request);
+    if (res != RTSP_OK) {
+      g_print ("error creating request\n");
+      return (-1);
+    }
+    rtsp_message_add_header (&request, RTSP_HDR_ACCEPT, "application/sdp");
+
+    rtsp_message_dump (&request);
+
+    res = rtsp_connection_send (conn, &request);
+    if (res != RTSP_OK) {
+      g_print ("error sending request\n");
+      return (-1);
+    }
+
+    res = rtsp_connection_receive (conn, &response);
+    if (res != RTSP_OK) {
+      g_print ("error receiving response\n");
+      return (-1);
+    }
+    rtsp_message_dump (&response);
+  }
+
+  /* parse SDP */
+  {
+    guint8 *data;
+    guint size;
+
+    rtsp_message_get_body (&response, &data, &size);
+
+    sdp_message_init (&sdp);
+    sdp_message_parse_buffer (data, size, &sdp);
+
+    sdp_message_dump (&sdp);
+  }
+
+  /* do setup */
+  {
+    gint i;
+
+    for (i = 0; i < sdp_message_medias_len (&sdp); i++) {
+      SDPMedia *media;
+      gchar *setup_url;
+      gchar *control_url;
+
+      media = sdp_message_get_media (&sdp, i);
+
+      g_print ("setup media %d\n", i);
+      control_url = sdp_media_get_attribute_val (media, "control");
+
+      setup_url = g_strdup_printf ("%s/%s", urlstr, control_url);
+
+      g_print ("setup %s\n", setup_url);
+      res = rtsp_message_init_request (RTSP_SETUP, setup_url, &request);
+      if (res != RTSP_OK) {
+        g_print ("error creating request\n");
+        return (-1);
+      }
+
+      rtsp_message_add_header (&request, RTSP_HDR_TRANSPORT,
+          //"RTP/AVP/UDP;unicast;client_port=5000-5001,RTP/AVP/UDP;multicast,RTP/AVP/TCP");
+          "RTP/AVP/TCP");
+      rtsp_message_dump (&request);
+
+      res = rtsp_connection_send (conn, &request);
+      if (res != RTSP_OK) {
+        g_print ("error sending request\n");
+        return (-1);
+      }
+
+      res = rtsp_connection_receive (conn, &response);
+      if (res != RTSP_OK) {
+        g_print ("error receiving response\n");
+        return (-1);
+      }
+      rtsp_message_dump (&response);
+    }
+  }
+  /* do play */
+  {
+    res = rtsp_message_init_request (RTSP_PLAY, urlstr, &request);
+    if (res != RTSP_OK) {
+      g_print ("error creating request\n");
+      return (-1);
+    }
+    rtsp_message_dump (&request);
+
+    res = rtsp_connection_send (conn, &request);
+    if (res != RTSP_OK) {
+      g_print ("error sending request\n");
+      return (-1);
+    }
+
+    res = rtsp_connection_receive (conn, &response);
+    if (res != RTSP_OK) {
+      g_print ("error receiving response\n");
+      return (-1);
+    }
+    rtsp_message_dump (&response);
+  }
+
+  while (TRUE) {
+    res = rtsp_connection_receive (conn, &response);
+    if (res != RTSP_OK) {
+      g_print ("error receiving response\n");
+      return (-1);
+    }
+    rtsp_message_dump (&response);
+  }
+
+  /* close connection */
+  g_print ("closing connection...\n");
+  res = rtsp_connection_close (conn);
+  if (res != RTSP_OK) {
+    g_print ("error closing connection to \"%s\"\n", urlstr);
+    return (-1);
+  }
+
+  return 0;
+}