diff --git a/ChangeLog b/ChangeLog index a67e3d5a39..3953beaa90 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,66 @@ +2005-05-11 Wim Taymans + + * gst/rtsp/.cvsignore: + * gst/rtsp/Makefile.am: + * gst/rtsp/gstrtsp.c: (plugin_init): + * gst/rtsp/gstrtsp.h: + * gst/rtsp/gstrtspsrc.c: (gst_rtsp_proto_get_type), + (gst_rtspsrc_get_type), (gst_rtspsrc_base_init), + (gst_rtspsrc_class_init), (gst_rtspsrc_init), + (gst_rtspsrc_set_property), (gst_rtspsrc_get_property), + (gst_rtspsrc_create_stream), (rtspsrc_add_element), + (gst_rtspsrc_stream_setup_rtp), + (gst_rtspsrc_stream_configure_transport), (find_stream), + (gst_rtspsrc_loop), (gst_rtspsrc_send), (gst_rtspsrc_open), + (gst_rtspsrc_close), (gst_rtspsrc_play), (gst_rtspsrc_pause), + (gst_rtspsrc_activate), (gst_rtspsrc_change_state): + * gst/rtsp/gstrtspsrc.h: + * gst/rtsp/rtsp.h: + * gst/rtsp/rtspconnection.c: (rtsp_connection_open), + (rtsp_connection_create), (append_header), (rtsp_connection_send), + (read_line), (read_string), (read_key), (parse_response_status), + (parse_line), (read_body), (rtsp_connection_receive), + (rtsp_connection_close): + * gst/rtsp/rtspconnection.h: + * gst/rtsp/rtspdefs.c: (rtsp_init_status), (rtsp_method_as_text), + (rtsp_header_as_text), (rtsp_status_as_text), + (rtsp_status_to_string), (rtsp_find_header_field): + * gst/rtsp/rtspdefs.h: + * gst/rtsp/rtspmessage.c: (rtsp_message_new_request), + (rtsp_message_init_request), (rtsp_message_new_response), + (rtsp_message_init_response), (rtsp_message_init_data), + (rtsp_message_add_header), (rtsp_message_remove_header), + (rtsp_message_get_header), (rtsp_message_get_header_copy), + (rtsp_message_set_body), (rtsp_message_set_body_copy), + (rtsp_message_get_body), (rtsp_message_get_body_copy), (dump_mem), + (dump_key_value), (rtsp_message_dump): + * gst/rtsp/rtspmessage.h: + * gst/rtsp/rtspstream.h: + * gst/rtsp/rtsptransport.c: (rtsp_transport_new), + (rtsp_transport_init), (parse_mode), (parse_range), + (rtsp_transport_parse), (rtsp_transport_free): + * gst/rtsp/rtsptransport.h: + * gst/rtsp/rtspurl.c: (rtsp_url_parse), (rtsp_url_free): + * gst/rtsp/rtspurl.h: + * gst/rtsp/sdp.h: + * gst/rtsp/sdpmessage.c: (sdp_message_new), (sdp_message_init), + (sdp_message_clean), (sdp_message_free), (sdp_media_new), + (sdp_media_init), (sdp_message_set_origin), + (sdp_message_get_origin), (sdp_message_set_connection), + (sdp_message_get_connection), (sdp_message_add_bandwidth), + (sdp_message_add_time), (sdp_message_add_zone), + (sdp_message_set_key), (sdp_message_get_key), + (sdp_message_get_attribute_val), (sdp_message_add_attribute), + (sdp_message_add_media), (sdp_media_add_attribute), + (sdp_media_add_bandwidth), (sdp_media_add_format), + (sdp_media_get_attribute_val), (read_string), (read_string_del), + (sdp_parse_line), (sdp_message_parse_buffer), (print_media), + (sdp_message_dump): + * gst/rtsp/sdpmessage.h: + * gst/rtsp/test.c: (main): + Ported to 0.9. + Set up transports, init UDP ports, init RTP session managers. + 2005-05-11 Wim Taymans * gst/rtp/Makefile.am: diff --git a/gst/rtsp/.gitignore b/gst/rtsp/.gitignore new file mode 100644 index 0000000000..10846d78b1 --- /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 0000000000..2a90944b1e --- /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 0000000000..46d794919f --- /dev/null +++ b/gst/rtsp/gstrtsp.c @@ -0,0 +1,40 @@ +/* GStreamer + * Copyright (C) <1999> Erik Walthinsen + * + * 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 0000000000..1b4f6a6999 --- /dev/null +++ b/gst/rtsp/gstrtsp.h @@ -0,0 +1,30 @@ +/* GStreamer + * Copyright (C) <1999> Erik Walthinsen + * + * 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 0000000000..d6078529e9 --- /dev/null +++ b/gst/rtsp/gstrtspsrc.c @@ -0,0 +1,868 @@ +/* GStreamer + * Copyright (C) <2005> Wim Taymans + * + * 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 +#include + +#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 "); + +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 0000000000..0a64e02387 --- /dev/null +++ b/gst/rtsp/gstrtspsrc.h @@ -0,0 +1,106 @@ +/* GStreamer + * Copyright (C) <1999> Erik Walthinsen + * + * 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 + +#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 0000000000..fd5ab2a1eb --- /dev/null +++ b/gst/rtsp/rtsp.h @@ -0,0 +1,30 @@ +/* GStreamer + * Copyright (C) <2005> Wim Taymans + * + * 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 +#include +#include +#include +#include +#include + +#endif /* __RTSP_H__ */ diff --git a/gst/rtsp/rtspconnection.c b/gst/rtsp/rtspconnection.c new file mode 100644 index 0000000000..f9a1b42081 --- /dev/null +++ b/gst/rtsp/rtspconnection.c @@ -0,0 +1,432 @@ +/* GStreamer + * Copyright (C) <2005> Wim Taymans + * + * 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 +#include +#include +#include +#include +#include + +#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 0000000000..aeb85375fb --- /dev/null +++ b/gst/rtsp/rtspconnection.h @@ -0,0 +1,56 @@ +/* GStreamer + * Copyright (C) <2005> Wim Taymans + * + * 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 + +#include +#include +#include +#include + +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 0000000000..729e3b5535 --- /dev/null +++ b/gst/rtsp/rtspdefs.c @@ -0,0 +1,171 @@ +/* GStreamer + * Copyright (C) <2005> Wim Taymans + * + * 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 0000000000..063ab3c9bc --- /dev/null +++ b/gst/rtsp/rtspdefs.h @@ -0,0 +1,174 @@ +/* GStreamer + * Copyright (C) <2005> Wim Taymans + * + * 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 + +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 0000000000..2720ff7d0d --- /dev/null +++ b/gst/rtsp/rtspmessage.c @@ -0,0 +1,307 @@ +/* GStreamer + * Copyright (C) <2005> Wim Taymans + * + * 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 0000000000..7bed76f971 --- /dev/null +++ b/gst/rtsp/rtspmessage.h @@ -0,0 +1,88 @@ +/* GStreamer + * Copyright (C) <2005> Wim Taymans + * + * 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 + +#include + +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 0000000000..1a25142215 --- /dev/null +++ b/gst/rtsp/rtspstream.h @@ -0,0 +1,32 @@ +/* GStreamer + * Copyright (C) <2005> Wim Taymans + * + * 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 + +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 0000000000..addf354f99 --- /dev/null +++ b/gst/rtsp/rtsptransport.c @@ -0,0 +1,144 @@ +/* GStreamer + * Copyright (C) <2005> Wim Taymans + * + * 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 0000000000..288275fbcd --- /dev/null +++ b/gst/rtsp/rtsptransport.h @@ -0,0 +1,81 @@ +/* GStreamer + * Copyright (C) <2005> Wim Taymans + * + * 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 + +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 0000000000..25c85a2f54 --- /dev/null +++ b/gst/rtsp/rtspurl.c @@ -0,0 +1,101 @@ +/* GStreamer + * Copyright (C) <2005> Wim Taymans + * + * 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 + +#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 0000000000..2492b87b39 --- /dev/null +++ b/gst/rtsp/rtspurl.h @@ -0,0 +1,46 @@ +/* GStreamer + * Copyright (C) <2005> Wim Taymans + * + * 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 + +#include + +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 0000000000..6efb9f8b39 --- /dev/null +++ b/gst/rtsp/sdp.h @@ -0,0 +1,25 @@ +/* GStreamer + * Copyright (C) <2005> Wim Taymans + * + * 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 + +#endif /* __SDP_H__ */ diff --git a/gst/rtsp/sdpmessage.c b/gst/rtsp/sdpmessage.c new file mode 100644 index 0000000000..a9044c9672 --- /dev/null +++ b/gst/rtsp/sdpmessage.c @@ -0,0 +1,709 @@ +/* GStreamer + * Copyright (C) <2005> Wim Taymans + * + * 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 +#include + +#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 0000000000..64b8ed9413 --- /dev/null +++ b/gst/rtsp/sdpmessage.h @@ -0,0 +1,165 @@ +/* GStreamer + * Copyright (C) <2005> Wim Taymans + * + * 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 + +#include + +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 0000000000..4cf1dab374 --- /dev/null +++ b/gst/rtsp/test.c @@ -0,0 +1,179 @@ +/* GStreamer + * Copyright (C) <2005> Wim Taymans + * + * 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 + +#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; +}