summaryrefslogtreecommitdiffstats
path: root/gst/rtsp
diff options
context:
space:
mode:
Diffstat (limited to 'gst/rtsp')
-rw-r--r--gst/rtsp/.gitignore8
-rw-r--r--gst/rtsp/Makefile.am22
-rw-r--r--gst/rtsp/gstrtsp.c40
-rw-r--r--gst/rtsp/gstrtsp.h30
-rw-r--r--gst/rtsp/gstrtspsrc.c868
-rw-r--r--gst/rtsp/gstrtspsrc.h106
-rw-r--r--gst/rtsp/rtsp.h30
-rw-r--r--gst/rtsp/rtspconnection.c432
-rw-r--r--gst/rtsp/rtspconnection.h56
-rw-r--r--gst/rtsp/rtspdefs.c171
-rw-r--r--gst/rtsp/rtspdefs.h174
-rw-r--r--gst/rtsp/rtspmessage.c307
-rw-r--r--gst/rtsp/rtspmessage.h88
-rw-r--r--gst/rtsp/rtspstream.h32
-rw-r--r--gst/rtsp/rtsptransport.c144
-rw-r--r--gst/rtsp/rtsptransport.h81
-rw-r--r--gst/rtsp/rtspurl.c101
-rw-r--r--gst/rtsp/rtspurl.h46
-rw-r--r--gst/rtsp/sdp.h25
-rw-r--r--gst/rtsp/sdpmessage.c709
-rw-r--r--gst/rtsp/sdpmessage.h165
-rw-r--r--gst/rtsp/test.c179
22 files changed, 3814 insertions, 0 deletions
diff --git a/gst/rtsp/.gitignore b/gst/rtsp/.gitignore
new file mode 100644
index 00000000..10846d78
--- /dev/null
+++ b/gst/rtsp/.gitignore
@@ -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
index 00000000..2a90944b
--- /dev/null
+++ b/gst/rtsp/Makefile.am
@@ -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
index 00000000..46d79491
--- /dev/null
+++ b/gst/rtsp/gstrtsp.c
@@ -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
index 00000000..1b4f6a69
--- /dev/null
+++ b/gst/rtsp/gstrtsp.h
@@ -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
index 00000000..d6078529
--- /dev/null
+++ b/gst/rtsp/gstrtspsrc.c
@@ -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
index 00000000..0a64e023
--- /dev/null
+++ b/gst/rtsp/gstrtspsrc.h
@@ -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
index 00000000..fd5ab2a1
--- /dev/null
+++ b/gst/rtsp/rtsp.h
@@ -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
index 00000000..f9a1b420
--- /dev/null
+++ b/gst/rtsp/rtspconnection.c
@@ -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
index 00000000..aeb85375
--- /dev/null
+++ b/gst/rtsp/rtspconnection.h
@@ -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
index 00000000..729e3b55
--- /dev/null
+++ b/gst/rtsp/rtspdefs.c
@@ -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
index 00000000..063ab3c9
--- /dev/null
+++ b/gst/rtsp/rtspdefs.h
@@ -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
index 00000000..2720ff7d
--- /dev/null
+++ b/gst/rtsp/rtspmessage.c
@@ -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
index 00000000..7bed76f9
--- /dev/null
+++ b/gst/rtsp/rtspmessage.h
@@ -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
index 00000000..1a251422
--- /dev/null
+++ b/gst/rtsp/rtspstream.h
@@ -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
index 00000000..addf354f
--- /dev/null
+++ b/gst/rtsp/rtsptransport.c
@@ -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
index 00000000..288275fb
--- /dev/null
+++ b/gst/rtsp/rtsptransport.h
@@ -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
index 00000000..25c85a2f
--- /dev/null
+++ b/gst/rtsp/rtspurl.c
@@ -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
index 00000000..2492b87b
--- /dev/null
+++ b/gst/rtsp/rtspurl.h
@@ -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
index 00000000..6efb9f8b
--- /dev/null
+++ b/gst/rtsp/sdp.h
@@ -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
index 00000000..a9044c96
--- /dev/null
+++ b/gst/rtsp/sdpmessage.c
@@ -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
index 00000000..64b8ed94
--- /dev/null
+++ b/gst/rtsp/sdpmessage.h
@@ -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
index 00000000..4cf1dab3
--- /dev/null
+++ b/gst/rtsp/test.c
@@ -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;
+}