From 19e0dd31407ca9763b796926e5169c262a9984fe Mon Sep 17 00:00:00 2001 From: Wim Taymans Date: Tue, 24 Jul 2007 11:52:56 +0000 Subject: [PATCH] Move SDP and RTSP from helper objects in -good to a reusable library. Original commit message from CVS: * configure.ac: * gst-libs/gst/Makefile.am: * gst-libs/gst/rtsp/Makefile.am: * gst-libs/gst/rtsp/gstrtspbase64.c: (gst_rtsp_base64_encode): * gst-libs/gst/rtsp/gstrtspbase64.h: * gst-libs/gst/rtsp/gstrtspconnection.c: (inet_aton), (gst_rtsp_connection_create), (gst_rtsp_connection_connect), (add_auth_header), (add_date_header), (gst_rtsp_connection_write), (gst_rtsp_connection_send), (read_line), (read_string), (read_key), (parse_response_status), (parse_request_line), (parse_line), (gst_rtsp_connection_read), (read_body), (gst_rtsp_connection_receive), (gst_rtsp_connection_close), (gst_rtsp_connection_free), (gst_rtsp_connection_next_timeout), (gst_rtsp_connection_reset_timeout), (gst_rtsp_connection_flush), (gst_rtsp_connection_set_auth): * gst-libs/gst/rtsp/gstrtspconnection.h: * gst-libs/gst/rtsp/gstrtspdefs.c: (rtsp_init_status), (gst_rtsp_strresult), (gst_rtsp_method_as_text), (gst_rtsp_version_as_text), (gst_rtsp_header_as_text), (gst_rtsp_status_as_text), (gst_rtsp_find_header_field), (gst_rtsp_find_method): * gst-libs/gst/rtsp/gstrtspdefs.h: * gst-libs/gst/rtsp/gstrtspmessage.c: (key_value_foreach), (gst_rtsp_message_new), (gst_rtsp_message_init), (gst_rtsp_message_new_request), (gst_rtsp_message_init_request), (gst_rtsp_message_new_response), (gst_rtsp_message_init_response), (gst_rtsp_message_init_data), (gst_rtsp_message_unset), (gst_rtsp_message_free), (gst_rtsp_message_add_header), (gst_rtsp_message_remove_header), (gst_rtsp_message_get_header), (gst_rtsp_message_append_headers), (gst_rtsp_message_set_body), (gst_rtsp_message_take_body), (gst_rtsp_message_get_body), (gst_rtsp_message_steal_body), (dump_mem), (dump_key_value), (gst_rtsp_message_dump): * gst-libs/gst/rtsp/gstrtspmessage.h: * gst-libs/gst/rtsp/gstrtsprange.c: (parse_npt_time), (parse_npt_range), (parse_clock_range), (parse_smpte_range), (gst_rtsp_range_parse), (gst_rtsp_range_free): * gst-libs/gst/rtsp/gstrtsprange.h: * gst-libs/gst/rtsp/gstrtsptransport.c: (gst_rtsp_transport_new), (gst_rtsp_transport_init), (gst_rtsp_transport_get_mime), (gst_rtsp_transport_get_manager), (parse_mode), (parse_range), (range_as_text), (rtsp_transport_mode_as_text), (rtsp_transport_profile_as_text), (rtsp_transport_ltrans_as_text), (gst_rtsp_transport_parse), (gst_rtsp_transport_as_text), (gst_rtsp_transport_free): * gst-libs/gst/rtsp/gstrtsptransport.h: * gst-libs/gst/rtsp/gstrtspurl.c: (gst_rtsp_url_parse), (gst_rtsp_url_free), (gst_rtsp_url_set_port), (gst_rtsp_url_get_port), (gst_rtsp_url_get_request_uri): * gst-libs/gst/rtsp/gstrtspurl.h: * gst-libs/gst/sdp/Makefile.am: * gst-libs/gst/sdp/gstsdp.h: * gst-libs/gst/sdp/gstsdpmessage.c: (gst_sdp_origin_init), (gst_sdp_connection_init), (gst_sdp_bandwidth_init), (gst_sdp_time_init), (gst_sdp_zone_init), (gst_sdp_key_init), (gst_sdp_attribute_init), (gst_sdp_message_new), (gst_sdp_message_init), (gst_sdp_message_uninit), (gst_sdp_message_free), (gst_sdp_media_new), (gst_sdp_media_init), (gst_sdp_media_uninit), (gst_sdp_media_free), (gst_sdp_message_set_origin), (gst_sdp_message_get_origin), (gst_sdp_message_set_connection), (gst_sdp_message_get_connection), (gst_sdp_message_add_bandwidth), (gst_sdp_message_add_time), (gst_sdp_message_add_zone), (gst_sdp_message_set_key), (gst_sdp_message_get_key), (gst_sdp_message_get_attribute_val_n), (gst_sdp_message_get_attribute_val), (gst_sdp_message_add_attribute), (gst_sdp_message_add_media), (gst_sdp_media_add_attribute), (gst_sdp_media_add_bandwidth), (gst_sdp_media_add_format), (gst_sdp_media_get_attribute), (gst_sdp_media_get_attribute_val_n), (gst_sdp_media_get_attribute_val), (gst_sdp_media_get_format), (read_string), (read_string_del), (gst_sdp_parse_line), (gst_sdp_message_parse_buffer), (print_media), (gst_sdp_message_dump): * gst-libs/gst/sdp/gstsdpmessage.h: * pkgconfig/gstreamer-plugins-base-uninstalled.pc.in: Move SDP and RTSP from helper objects in -good to a reusable library. Use a proper gst_ namespace. --- ChangeLog | 80 ++ configure.ac | 2 + gst-libs/gst/Makefile.am | 2 + gst-libs/gst/rtsp/Makefile.am | 29 + gst-libs/gst/rtsp/gstrtspbase64.c | 70 + gst-libs/gst/rtsp/gstrtspbase64.h | 31 + gst-libs/gst/rtsp/gstrtspconnection.c | 1132 +++++++++++++++++ gst-libs/gst/rtsp/gstrtspconnection.h | 105 ++ gst-libs/gst/rtsp/gstrtspdefs.c | 316 +++++ gst-libs/gst/rtsp/gstrtspdefs.h | 249 ++++ gst-libs/gst/rtsp/gstrtspmessage.c | 471 +++++++ gst-libs/gst/rtsp/gstrtspmessage.h | 144 +++ gst-libs/gst/rtsp/gstrtsprange.c | 176 +++ gst-libs/gst/rtsp/gstrtsprange.h | 97 ++ gst-libs/gst/rtsp/gstrtsptransport.c | 589 +++++++++ gst-libs/gst/rtsp/gstrtsptransport.h | 151 +++ gst-libs/gst/rtsp/gstrtspurl.c | 211 +++ gst-libs/gst/rtsp/gstrtspurl.h | 77 ++ gst-libs/gst/sdp/Makefile.am | 12 + gst-libs/gst/sdp/gstsdp.h | 51 + gst-libs/gst/sdp/gstsdpmessage.c | 936 ++++++++++++++ gst-libs/gst/sdp/gstsdpmessage.h | 200 +++ .../gstreamer-plugins-base-uninstalled.pc.in | 2 +- 23 files changed, 5132 insertions(+), 1 deletion(-) create mode 100644 gst-libs/gst/rtsp/Makefile.am create mode 100644 gst-libs/gst/rtsp/gstrtspbase64.c create mode 100644 gst-libs/gst/rtsp/gstrtspbase64.h create mode 100644 gst-libs/gst/rtsp/gstrtspconnection.c create mode 100644 gst-libs/gst/rtsp/gstrtspconnection.h create mode 100644 gst-libs/gst/rtsp/gstrtspdefs.c create mode 100644 gst-libs/gst/rtsp/gstrtspdefs.h create mode 100644 gst-libs/gst/rtsp/gstrtspmessage.c create mode 100644 gst-libs/gst/rtsp/gstrtspmessage.h create mode 100644 gst-libs/gst/rtsp/gstrtsprange.c create mode 100644 gst-libs/gst/rtsp/gstrtsprange.h create mode 100644 gst-libs/gst/rtsp/gstrtsptransport.c create mode 100644 gst-libs/gst/rtsp/gstrtsptransport.h create mode 100644 gst-libs/gst/rtsp/gstrtspurl.c create mode 100644 gst-libs/gst/rtsp/gstrtspurl.h create mode 100644 gst-libs/gst/sdp/Makefile.am create mode 100644 gst-libs/gst/sdp/gstsdp.h create mode 100644 gst-libs/gst/sdp/gstsdpmessage.c create mode 100644 gst-libs/gst/sdp/gstsdpmessage.h diff --git a/ChangeLog b/ChangeLog index 35dcccc8f1..c756e792f7 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,83 @@ +2007-07-24 Wim Taymans + + * configure.ac: + * gst-libs/gst/Makefile.am: + * gst-libs/gst/rtsp/Makefile.am: + * gst-libs/gst/rtsp/gstrtspbase64.c: (gst_rtsp_base64_encode): + * gst-libs/gst/rtsp/gstrtspbase64.h: + * gst-libs/gst/rtsp/gstrtspconnection.c: (inet_aton), + (gst_rtsp_connection_create), (gst_rtsp_connection_connect), + (add_auth_header), (add_date_header), (gst_rtsp_connection_write), + (gst_rtsp_connection_send), (read_line), (read_string), (read_key), + (parse_response_status), (parse_request_line), (parse_line), + (gst_rtsp_connection_read), (read_body), + (gst_rtsp_connection_receive), (gst_rtsp_connection_close), + (gst_rtsp_connection_free), (gst_rtsp_connection_next_timeout), + (gst_rtsp_connection_reset_timeout), (gst_rtsp_connection_flush), + (gst_rtsp_connection_set_auth): + * gst-libs/gst/rtsp/gstrtspconnection.h: + * gst-libs/gst/rtsp/gstrtspdefs.c: (rtsp_init_status), + (gst_rtsp_strresult), (gst_rtsp_method_as_text), + (gst_rtsp_version_as_text), (gst_rtsp_header_as_text), + (gst_rtsp_status_as_text), (gst_rtsp_find_header_field), + (gst_rtsp_find_method): + * gst-libs/gst/rtsp/gstrtspdefs.h: + * gst-libs/gst/rtsp/gstrtspmessage.c: (key_value_foreach), + (gst_rtsp_message_new), (gst_rtsp_message_init), + (gst_rtsp_message_new_request), (gst_rtsp_message_init_request), + (gst_rtsp_message_new_response), (gst_rtsp_message_init_response), + (gst_rtsp_message_init_data), (gst_rtsp_message_unset), + (gst_rtsp_message_free), (gst_rtsp_message_add_header), + (gst_rtsp_message_remove_header), (gst_rtsp_message_get_header), + (gst_rtsp_message_append_headers), (gst_rtsp_message_set_body), + (gst_rtsp_message_take_body), (gst_rtsp_message_get_body), + (gst_rtsp_message_steal_body), (dump_mem), (dump_key_value), + (gst_rtsp_message_dump): + * gst-libs/gst/rtsp/gstrtspmessage.h: + * gst-libs/gst/rtsp/gstrtsprange.c: (parse_npt_time), + (parse_npt_range), (parse_clock_range), (parse_smpte_range), + (gst_rtsp_range_parse), (gst_rtsp_range_free): + * gst-libs/gst/rtsp/gstrtsprange.h: + * gst-libs/gst/rtsp/gstrtsptransport.c: (gst_rtsp_transport_new), + (gst_rtsp_transport_init), (gst_rtsp_transport_get_mime), + (gst_rtsp_transport_get_manager), (parse_mode), (parse_range), + (range_as_text), (rtsp_transport_mode_as_text), + (rtsp_transport_profile_as_text), (rtsp_transport_ltrans_as_text), + (gst_rtsp_transport_parse), (gst_rtsp_transport_as_text), + (gst_rtsp_transport_free): + * gst-libs/gst/rtsp/gstrtsptransport.h: + * gst-libs/gst/rtsp/gstrtspurl.c: (gst_rtsp_url_parse), + (gst_rtsp_url_free), (gst_rtsp_url_set_port), + (gst_rtsp_url_get_port), (gst_rtsp_url_get_request_uri): + * gst-libs/gst/rtsp/gstrtspurl.h: + * gst-libs/gst/sdp/Makefile.am: + * gst-libs/gst/sdp/gstsdp.h: + * gst-libs/gst/sdp/gstsdpmessage.c: (gst_sdp_origin_init), + (gst_sdp_connection_init), (gst_sdp_bandwidth_init), + (gst_sdp_time_init), (gst_sdp_zone_init), (gst_sdp_key_init), + (gst_sdp_attribute_init), (gst_sdp_message_new), + (gst_sdp_message_init), (gst_sdp_message_uninit), + (gst_sdp_message_free), (gst_sdp_media_new), (gst_sdp_media_init), + (gst_sdp_media_uninit), (gst_sdp_media_free), + (gst_sdp_message_set_origin), (gst_sdp_message_get_origin), + (gst_sdp_message_set_connection), (gst_sdp_message_get_connection), + (gst_sdp_message_add_bandwidth), (gst_sdp_message_add_time), + (gst_sdp_message_add_zone), (gst_sdp_message_set_key), + (gst_sdp_message_get_key), (gst_sdp_message_get_attribute_val_n), + (gst_sdp_message_get_attribute_val), + (gst_sdp_message_add_attribute), (gst_sdp_message_add_media), + (gst_sdp_media_add_attribute), (gst_sdp_media_add_bandwidth), + (gst_sdp_media_add_format), (gst_sdp_media_get_attribute), + (gst_sdp_media_get_attribute_val_n), + (gst_sdp_media_get_attribute_val), (gst_sdp_media_get_format), + (read_string), (read_string_del), (gst_sdp_parse_line), + (gst_sdp_message_parse_buffer), (print_media), + (gst_sdp_message_dump): + * gst-libs/gst/sdp/gstsdpmessage.h: + * pkgconfig/gstreamer-plugins-base-uninstalled.pc.in: + Move SDP and RTSP from helper objects in -good to a reusable library. + Use a proper gst_ namespace. + 2007-07-23 Sebastian Dröge * ext/vorbis/vorbisdec.c: (vorbis_dec_push_forward), diff --git a/configure.ac b/configure.ac index 3bf6d8581d..58503f974d 100644 --- a/configure.ac +++ b/configure.ac @@ -646,6 +646,8 @@ gst-libs/gst/interfaces/Makefile gst-libs/gst/netbuffer/Makefile gst-libs/gst/riff/Makefile gst-libs/gst/rtp/Makefile +gst-libs/gst/rtsp/Makefile +gst-libs/gst/sdp/Makefile gst-libs/gst/tag/Makefile gst-libs/gst/pbutils/Makefile gst-libs/gst/video/Makefile diff --git a/gst-libs/gst/Makefile.am b/gst-libs/gst/Makefile.am index f1fd4c39d3..f4db55ace6 100644 --- a/gst-libs/gst/Makefile.am +++ b/gst-libs/gst/Makefile.am @@ -12,6 +12,8 @@ SUBDIRS = \ pbutils \ riff \ rtp \ + rtsp \ + sdp \ video noinst_HEADERS = gettext.h gst-i18n-plugin.h diff --git a/gst-libs/gst/rtsp/Makefile.am b/gst-libs/gst/rtsp/Makefile.am new file mode 100644 index 0000000000..38763ba528 --- /dev/null +++ b/gst-libs/gst/rtsp/Makefile.am @@ -0,0 +1,29 @@ +libgstrtspincludedir = $(includedir)/gstreamer-@GST_MAJORMINOR@/gst/rtsp + +libgstrtspinclude_HEADERS = gstrtspbase64.h \ + gstrtspconnection.h \ + gstrtspdefs.h \ + gstrtspmessage.h \ + gstrtsprange.h \ + gstrtsptransport.h \ + gstrtspurl.h + +#gstrtspextreal.h +#gstrtspextwms.h + +lib_LTLIBRARIES = libgstrtsp-@GST_MAJORMINOR@.la + +libgstrtsp_@GST_MAJORMINOR@_la_SOURCES = gstrtspbase64.c \ + gstrtspconnection.c \ + gstrtspdefs.c \ + gstrtspmessage.c \ + gstrtsprange.c \ + gstrtsptransport.c \ + gstrtspurl.c + +#gstrtspextwms.c +#rtspextreal.c + +libgstrtsp_@GST_MAJORMINOR@_la_CFLAGS = $(GST_PLUGINS_BASE_CFLAGS) $(GST_BASE_CFLAGS) $(GST_CFLAGS) +libgstrtsp_@GST_MAJORMINOR@_la_LIBADD = $(GST_LIBS) +libgstrtsp_@GST_MAJORMINOR@_la_LDFLAGS = $(GST_LIB_LDFLAGS) $(GST_ALL_LDFLAGS) $(GST_LT_LDFLAGS) diff --git a/gst-libs/gst/rtsp/gstrtspbase64.c b/gst-libs/gst/rtsp/gstrtspbase64.c new file mode 100644 index 0000000000..253004af2a --- /dev/null +++ b/gst-libs/gst/rtsp/gstrtspbase64.c @@ -0,0 +1,70 @@ +/* GStreamer + * Copyright (C) <2007> Mike Smith + * + * 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 "gstrtspbase64.h" + +static char base64table[64] = { + 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', + 'P', + 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', + 'f', + 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', + 'v', + 'w', 'x', 'y', 'z', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '+', '/' +}; + +/* This isn't efficient, but it doesn't need to be */ +gchar * +gst_rtsp_base64_encode (gchar * data, gint len) +{ + gchar *out = g_malloc (len * 4 / 3 + 4); + gchar *result = out; + int chunk; + + while (len > 0) { + chunk = (len > 3) ? 3 : len; + *out++ = base64table[(*data & 0xFC) >> 2]; + *out++ = base64table[((*data & 0x03) << 4) | ((*(data + 1) & 0xF0) >> 4)]; + switch (chunk) { + case 3: + *out++ = + base64table[((*(data + 1) & 0x0F) << 2) | ((*(data + + 2) & 0xC0) >> 6)]; + *out++ = base64table[(*(data + 2)) & 0x3F]; + break; + case 2: + *out++ = base64table[((*(data + 1) & 0x0F) << 2)]; + *out++ = '='; + break; + case 1: + *out++ = '='; + *out++ = '='; + break; + } + data += chunk; + len -= chunk; + } + *out = 0; + + return result; +} diff --git a/gst-libs/gst/rtsp/gstrtspbase64.h b/gst-libs/gst/rtsp/gstrtspbase64.h new file mode 100644 index 0000000000..24183b094e --- /dev/null +++ b/gst-libs/gst/rtsp/gstrtspbase64.h @@ -0,0 +1,31 @@ +/* GStreamer + * Copyright (C) <2007> Mike Smith + * + * 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 __BASE64_H__ +#define __BASE64_H__ + +#include + +G_BEGIN_DECLS + +gchar *gst_rtsp_base64_encode(gchar *data, gint len); + +G_END_DECLS + +#endif diff --git a/gst-libs/gst/rtsp/gstrtspconnection.c b/gst-libs/gst/rtsp/gstrtspconnection.c new file mode 100644 index 0000000000..629932b4bc --- /dev/null +++ b/gst-libs/gst/rtsp/gstrtspconnection.c @@ -0,0 +1,1132 @@ +/* GStreamer + * Copyright (C) <2005,2006> 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. + */ +/* + * Unless otherwise indicated, Source Code is licensed under MIT license. + * See further explanation attached in License Statement (distributed in the file + * LICENSE). + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#ifdef HAVE_CONFIG_H +# include +#endif + +#include +#include +#include +#include +#include +#include +#include + + +/* we include this here to get the G_OS_* defines */ +#include + +#ifdef G_OS_WIN32 +#include +#define EINPROGRESS WSAEINPROGRESS +#else +#include +#include +#include +#include +#include +#endif + +#ifdef HAVE_FIONREAD_IN_SYS_FILIO +#include +#endif + +#include "gstrtspconnection.h" +#include "gstrtspbase64.h" + +/* the select call is also performed on the control sockets, that way + * we can send special commands to unlock or restart the select call */ +#define CONTROL_RESTART 'R' /* restart the select call */ +#define CONTROL_STOP 'S' /* stop the select call */ +#define CONTROL_SOCKETS(conn) conn->control_sock +#define WRITE_SOCKET(conn) conn->control_sock[1] +#define READ_SOCKET(conn) conn->control_sock[0] + +#define SEND_COMMAND(conn, command) \ +G_STMT_START { \ + unsigned char c; c = command; \ + write (WRITE_SOCKET(conn), &c, 1); \ +} G_STMT_END + +#define READ_COMMAND(conn, command, res) \ +G_STMT_START { \ + res = read(READ_SOCKET(conn), &command, 1); \ +} G_STMT_END + +#ifdef G_OS_WIN32 +#define IOCTL_SOCKET ioctlsocket +#define CLOSE_SOCKET(sock) closesocket(sock); +#else +#define IOCTL_SOCKET ioctl +#define CLOSE_SOCKET(sock) close(sock); +#endif + +#ifdef G_OS_WIN32 +static int +inet_aton (const char *c, struct in_addr *paddr) +{ + /* note that inet_addr is deprecated on unix because + * inet_addr returns -1 (INADDR_NONE) for the valid 255.255.255.255 + * address. */ + paddr->s_addr = inet_addr (c); + + if (paddr->s_addr == INADDR_NONE) + return 0; + + return 1; +} +#endif + +GstRTSPResult +gst_rtsp_connection_create (GstRTSPUrl * url, GstRTSPConnection ** conn) +{ + gint ret; + GstRTSPConnection *newconn; + +#ifdef G_OS_WIN32 + unsigned long flags; +#endif /* G_OS_WIN32 */ + + g_return_val_if_fail (conn != NULL, GST_RTSP_EINVAL); + + newconn = g_new0 (GstRTSPConnection, 1); + +#ifdef G_OS_WIN32 + /* This should work on UNIX too. PF_UNIX sockets replaced with pipe */ + /* pipe( CONTROL_SOCKETS(newconn) ) */ + if ((ret = pipe (CONTROL_SOCKETS (newconn))) < 0) + goto no_socket_pair; + + ioctlsocket (READ_SOCKET (newconn), FIONBIO, &flags); + ioctlsocket (WRITE_SOCKET (newconn), FIONBIO, &flags); +#else + if ((ret = + socketpair (PF_UNIX, SOCK_STREAM, 0, CONTROL_SOCKETS (newconn))) < 0) + goto no_socket_pair; + + fcntl (READ_SOCKET (newconn), F_SETFL, O_NONBLOCK); + fcntl (WRITE_SOCKET (newconn), F_SETFL, O_NONBLOCK); +#endif + + newconn->url = url; + newconn->fd = -1; + newconn->timer = g_timer_new (); + + newconn->auth_method = GST_RTSP_AUTH_NONE; + newconn->username = NULL; + newconn->passwd = NULL; + + *conn = newconn; + + return GST_RTSP_OK; + + /* ERRORS */ +no_socket_pair: + { + g_free (newconn); + return GST_RTSP_ESYS; + } +} + +GstRTSPResult +gst_rtsp_connection_connect (GstRTSPConnection * conn, GTimeVal * timeout) +{ + gint fd; + struct sockaddr_in sa_in; + struct hostent *hostinfo; + char **addrs; + const gchar *ip; + gchar ipbuf[INET_ADDRSTRLEN]; + struct in_addr addr; + gint ret; + guint16 port; + GstRTSPUrl *url; + fd_set writefds; + fd_set readfds; + struct timeval tv, *tvp; + gint max_fd, retval; + +#ifdef G_OS_WIN32 + unsigned long flags; +#endif /* G_OS_WIN32 */ + + g_return_val_if_fail (conn != NULL, GST_RTSP_EINVAL); + g_return_val_if_fail (conn->url != NULL, GST_RTSP_EINVAL); + g_return_val_if_fail (conn->fd < 0, GST_RTSP_EINVAL); + + url = conn->url; + + /* 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_ntop (AF_INET, (struct in_addr *) addrs[0], ipbuf, + sizeof (ipbuf)); + } + + /* get the port from the url */ + gst_rtsp_url_get_port (url, &port); + + memset (&sa_in, 0, sizeof (sa_in)); + sa_in.sin_family = AF_INET; /* network socket */ + sa_in.sin_port = htons (port); /* on port */ + sa_in.sin_addr.s_addr = inet_addr (ip); /* on host ip */ + + fd = socket (AF_INET, SOCK_STREAM, 0); + if (fd == -1) + goto sys_error; + + /* set to non-blocking mode so that we can cancel the connect */ +#ifndef G_OS_WIN32 + fcntl (fd, F_SETFL, O_NONBLOCK); +#else + ioctlsocket (fd, FIONBIO, &flags); +#endif /* G_OS_WIN32 */ + + /* we are going to connect ASYNC now */ + ret = connect (fd, (struct sockaddr *) &sa_in, sizeof (sa_in)); + if (ret == 0) + goto done; + if (errno != EINPROGRESS) + goto sys_error; + + /* wait for connect to complete up to the specified timeout or until we got + * interrupted. */ + FD_ZERO (&writefds); + FD_SET (fd, &writefds); + FD_ZERO (&readfds); + FD_SET (READ_SOCKET (conn), &readfds); + + if (timeout->tv_sec != 0 || timeout->tv_usec != 0) { + tv.tv_sec = timeout->tv_sec; + tv.tv_usec = timeout->tv_usec; + tvp = &tv; + } else { + tvp = NULL; + } + + max_fd = MAX (fd, READ_SOCKET (conn)); + + do { + retval = select (max_fd + 1, &readfds, &writefds, NULL, tvp); + } while ((retval == -1 && errno == EINTR)); + + if (retval == 0) + goto timeout; + else if (retval == -1) + goto sys_error; + +done: + conn->fd = fd; + conn->ip = g_strdup (ip); + + return GST_RTSP_OK; + +sys_error: + { + if (fd != -1) + CLOSE_SOCKET (fd); + return GST_RTSP_ESYS; + } +not_resolved: + { + return GST_RTSP_ENET; + } +not_ip: + { + return GST_RTSP_ENOTIP; + } +timeout: + { + return GST_RTSP_ETIMEOUT; + } +} + +static void +add_auth_header (GstRTSPConnection * conn, GstRTSPMessage * message) +{ + switch (conn->auth_method) { + case GST_RTSP_AUTH_BASIC:{ + gchar *user_pass = + g_strdup_printf ("%s:%s", conn->username, conn->passwd); + gchar *user_pass64 = + gst_rtsp_base64_encode (user_pass, strlen (user_pass)); + gchar *auth_string = g_strdup_printf ("Basic %s", user_pass64); + + gst_rtsp_message_add_header (message, GST_RTSP_HDR_AUTHORIZATION, + auth_string); + + g_free (user_pass); + g_free (user_pass64); + g_free (auth_string); + break; + } + default: + /* Nothing to do */ + break; + } +} + +static void +add_date_header (GstRTSPMessage * message) +{ + GTimeVal tv; + gchar date_string[100]; + + g_get_current_time (&tv); + strftime (date_string, sizeof (date_string), "%a, %d %b %Y %H:%M:%S GMT", + gmtime (&tv.tv_sec)); + + gst_rtsp_message_add_header (message, GST_RTSP_HDR_DATE, date_string); +} + +GstRTSPResult +gst_rtsp_connection_write (GstRTSPConnection * conn, const guint8 * data, + guint size, GTimeVal * timeout) +{ + guint towrite; + fd_set writefds; + fd_set readfds; + int max_fd; + gint retval; + struct timeval tv, *tvp; + + g_return_val_if_fail (conn != NULL, GST_RTSP_EINVAL); + g_return_val_if_fail (data != NULL || size == 0, GST_RTSP_EINVAL); + + FD_ZERO (&writefds); + FD_SET (conn->fd, &writefds); + FD_ZERO (&readfds); + FD_SET (READ_SOCKET (conn), &readfds); + + max_fd = MAX (conn->fd, READ_SOCKET (conn)); + + if (timeout) { + tv.tv_sec = timeout->tv_sec; + tv.tv_usec = timeout->tv_usec; + tvp = &tv; + } else { + tvp = NULL; + } + + towrite = size; + + while (towrite > 0) { + gint written; + + do { + retval = select (max_fd + 1, &readfds, &writefds, NULL, tvp); + } while ((retval == -1 && errno == EINTR)); + + if (retval == 0) + goto timeout; + + if (retval == -1) + goto select_error; + + if (FD_ISSET (READ_SOCKET (conn), &readfds)) { + /* read all stop commands */ + while (TRUE) { + gchar command; + int res; + + READ_COMMAND (conn, command, res); + if (res <= 0) { + /* no more commands */ + break; + } + } + goto stopped; + } + + /* now we can write */ + written = write (conn->fd, data, towrite); + if (written < 0) { + if (errno != EAGAIN && errno != EINTR) + goto write_error; + } else { + towrite -= written; + data += written; + } + } + return GST_RTSP_OK; + + /* ERRORS */ +timeout: + { + return GST_RTSP_ETIMEOUT; + } +select_error: + { + return GST_RTSP_ESYS; + } +stopped: + { + return GST_RTSP_EINTR; + } +write_error: + { + return GST_RTSP_ESYS; + } +} + +GstRTSPResult +gst_rtsp_connection_send (GstRTSPConnection * conn, GstRTSPMessage * message, + GTimeVal * timeout) +{ + GString *str = NULL; + GstRTSPResult res; + +#ifdef G_OS_WIN32 + WSADATA w; + int error; +#endif + + g_return_val_if_fail (conn != NULL, GST_RTSP_EINVAL); + g_return_val_if_fail (message != NULL, GST_RTSP_EINVAL); + +#ifdef G_OS_WIN32 + error = WSAStartup (0x0202, &w); + + if (error) + goto startup_error; + + if (w.wVersion != 0x0202) + goto version_error; +#endif + + str = g_string_new (""); + + switch (message->type) { + case GST_RTSP_MESSAGE_REQUEST: + /* create request string, add CSeq */ + g_string_append_printf (str, "%s %s RTSP/1.0\r\n" + "CSeq: %d\r\n", + gst_rtsp_method_as_text (message->type_data.request.method), + message->type_data.request.uri, conn->cseq++); + /* add session id if we have one */ + if (conn->session_id[0] != '\0') { + gst_rtsp_message_add_header (message, GST_RTSP_HDR_SESSION, + conn->session_id); + } + /* add any authentication headers */ + add_auth_header (conn, message); + break; + case GST_RTSP_MESSAGE_RESPONSE: + /* create response string */ + g_string_append_printf (str, "RTSP/1.0 %d %s\r\n", + message->type_data.response.code, message->type_data.response.reason); + break; + case GST_RTSP_MESSAGE_DATA: + { + guint8 data_header[4]; + + /* prepare data header */ + data_header[0] = '$'; + data_header[1] = message->type_data.data.channel; + data_header[2] = (message->body_size >> 8) & 0xff; + data_header[3] = message->body_size & 0xff; + + /* create string with header and data */ + str = g_string_append_len (str, (gchar *) data_header, 4); + str = + g_string_append_len (str, (gchar *) message->body, + message->body_size); + break; + } + default: + g_assert_not_reached (); + break; + } + + /* append headers and body */ + if (message->type != GST_RTSP_MESSAGE_DATA) { + /* add date header */ + add_date_header (message); + + /* append headers */ + gst_rtsp_message_append_headers (message, str); + + /* append Content-Length and body if needed */ + if (message->body != NULL && message->body_size > 0) { + gchar *len; + + len = g_strdup_printf ("%d", message->body_size); + g_string_append_printf (str, "%s: %s\r\n", + gst_rtsp_header_as_text (GST_RTSP_HDR_CONTENT_LENGTH), len); + g_free (len); + /* header ends here */ + g_string_append (str, "\r\n"); + str = + g_string_append_len (str, (gchar *) message->body, + message->body_size); + } else { + /* just end headers */ + g_string_append (str, "\r\n"); + } + } + + /* write request */ + res = + gst_rtsp_connection_write (conn, (guint8 *) str->str, str->len, timeout); + + g_string_free (str, TRUE); + + return res; + +#ifdef G_OS_WIN32 +startup_error: + { + g_warning ("Error %d on WSAStartup", error); + return GST_RTSP_EWSASTART; + } +version_error: + { + g_warning ("Windows sockets are not version 0x202 (current 0x%x)", + w.wVersion); + WSACleanup (); + return GST_RTSP_EWSAVERSION; + } +#endif +} + +static GstRTSPResult +read_line (gint fd, gchar * buffer, guint size) +{ + guint idx; + gchar c; + gint r; + + idx = 0; + while (TRUE) { + r = read (fd, &c, 1); + if (r == 0) { + goto eof; + } else if (r < 0) { + if (errno != EAGAIN && errno != EINTR) + goto read_error; + } else { + if (c == '\n') /* end on \n */ + break; + if (c == '\r') /* ignore \r */ + continue; + + if (idx < size - 1) + buffer[idx++] = c; + } + } + buffer[idx] = '\0'; + + return GST_RTSP_OK; + +eof: + { + return GST_RTSP_EEOF; + } +read_error: + { + return GST_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 GstRTSPResult +parse_response_status (gchar * buffer, GstRTSPMessage * msg) +{ + GstRTSPResult res; + gchar versionstr[20]; + gchar codestr[4]; + gint code; + gchar *bptr; + + bptr = buffer; + + read_string (versionstr, sizeof (versionstr), &bptr); + read_string (codestr, sizeof (codestr), &bptr); + code = atoi (codestr); + + while (g_ascii_isspace (*bptr)) + bptr++; + + if (strcmp (versionstr, "RTSP/1.0") == 0) + GST_RTSP_CHECK (gst_rtsp_message_init_response (msg, code, bptr, NULL), + parse_error); + else if (strncmp (versionstr, "RTSP/", 5) == 0) { + GST_RTSP_CHECK (gst_rtsp_message_init_response (msg, code, bptr, NULL), + parse_error); + msg->type_data.response.version = GST_RTSP_VERSION_INVALID; + } else + goto parse_error; + + return GST_RTSP_OK; + +parse_error: + { + return GST_RTSP_EPARSE; + } +} + +static GstRTSPResult +parse_request_line (gchar * buffer, GstRTSPMessage * msg) +{ + GstRTSPResult res = GST_RTSP_OK; + gchar versionstr[20]; + gchar methodstr[20]; + gchar urlstr[4096]; + gchar *bptr; + GstRTSPMethod method; + + bptr = buffer; + + read_string (methodstr, sizeof (methodstr), &bptr); + method = gst_rtsp_find_method (methodstr); + + read_string (urlstr, sizeof (urlstr), &bptr); + if (*urlstr == '\0') + res = GST_RTSP_EPARSE; + + read_string (versionstr, sizeof (versionstr), &bptr); + + if (*bptr != '\0') + res = GST_RTSP_EPARSE; + + if (strcmp (versionstr, "RTSP/1.0") == 0) { + if (gst_rtsp_message_init_request (msg, method, urlstr) != GST_RTSP_OK) + res = GST_RTSP_EPARSE; + } else if (strncmp (versionstr, "RTSP/", 5) == 0) { + if (gst_rtsp_message_init_request (msg, method, urlstr) != GST_RTSP_OK) + res = GST_RTSP_EPARSE; + msg->type_data.request.version = GST_RTSP_VERSION_INVALID; + } else { + gst_rtsp_message_init_request (msg, method, urlstr); + msg->type_data.request.version = GST_RTSP_VERSION_INVALID; + res = GST_RTSP_EPARSE; + } + + return res; +} + +/* parsing lines means reading a Key: Value pair */ +static GstRTSPResult +parse_line (gchar * buffer, GstRTSPMessage * msg) +{ + gchar key[32]; + gchar *bptr; + GstRTSPHeaderField field; + + bptr = buffer; + + /* read key */ + read_key (key, sizeof (key), &bptr); + if (*bptr != ':') + goto no_column; + + bptr++; + + field = gst_rtsp_find_header_field (key); + if (field != GST_RTSP_HDR_INVALID) { + while (g_ascii_isspace (*bptr)) + bptr++; + gst_rtsp_message_add_header (msg, field, bptr); + } + + return GST_RTSP_OK; + +no_column: + { + return GST_RTSP_EPARSE; + } +} + +GstRTSPResult +gst_rtsp_connection_read (GstRTSPConnection * conn, guint8 * data, guint size, + GTimeVal * timeout) +{ + fd_set readfds; + guint toread; + gint retval; + struct timeval tv_timeout, *ptv_timeout = NULL; + +#ifndef G_OS_WIN32 + gint avail; +#else + gulong avail; +#endif + + g_return_val_if_fail (conn != NULL, GST_RTSP_EINVAL); + g_return_val_if_fail (data != NULL, GST_RTSP_EINVAL); + + if (size == 0) + return GST_RTSP_OK; + + toread = size; + + /* if the call fails, just go in the select.. it should not fail. Else if + * there is enough data to read, skip the select call al together.*/ + if (IOCTL_SOCKET (conn->fd, FIONREAD, &avail) < 0) + avail = 0; + else if (avail >= toread) + goto do_read; + + /* configure timeout if any */ + if (timeout != NULL) { + tv_timeout.tv_sec = timeout->tv_sec; + tv_timeout.tv_usec = timeout->tv_usec; + ptv_timeout = &tv_timeout; + } + + FD_ZERO (&readfds); + FD_SET (conn->fd, &readfds); + FD_SET (READ_SOCKET (conn), &readfds); + + while (toread > 0) { + gint bytes; + + do { + retval = select (FD_SETSIZE, &readfds, NULL, NULL, ptv_timeout); + } while ((retval == -1 && errno == EINTR)); + + if (retval == -1) + goto select_error; + + /* check for timeout */ + if (retval == 0) + goto select_timeout; + + if (FD_ISSET (READ_SOCKET (conn), &readfds)) { + /* read all stop commands */ + while (TRUE) { + gchar command; + int res; + + READ_COMMAND (conn, command, res); + if (res <= 0) { + /* no more commands */ + break; + } + } + goto stopped; + } + + do_read: + /* if we get here there is activity on the real fd since the select + * completed and the control socket was not readable. */ + bytes = read (conn->fd, data, toread); + + if (bytes == 0) { + goto eof; + } else if (bytes < 0) { + if (errno != EAGAIN && errno != EINTR) + goto read_error; + } else { + toread -= bytes; + data += bytes; + } + } + return GST_RTSP_OK; + + /* ERRORS */ +select_error: + { + return GST_RTSP_ESYS; + } +select_timeout: + { + return GST_RTSP_ETIMEOUT; + } +stopped: + { + return GST_RTSP_EINTR; + } +eof: + { + return GST_RTSP_EEOF; + } +read_error: + { + return GST_RTSP_ESYS; + } +} + +static GstRTSPResult +read_body (GstRTSPConnection * conn, glong content_length, GstRTSPMessage * msg, + GTimeVal * timeout) +{ + guint8 *body; + GstRTSPResult res; + + if (content_length <= 0) { + body = NULL; + content_length = 0; + goto done; + } + + body = g_malloc (content_length + 1); + body[content_length] = '\0'; + + GST_RTSP_CHECK (gst_rtsp_connection_read (conn, body, content_length, + timeout), read_error); + + content_length += 1; + +done: + gst_rtsp_message_take_body (msg, (guint8 *) body, content_length); + + return GST_RTSP_OK; + + /* ERRORS */ +read_error: + { + g_free (body); + return res; + } +} + +GstRTSPResult +gst_rtsp_connection_receive (GstRTSPConnection * conn, GstRTSPMessage * msg, + GTimeVal * timeout) +{ + gchar buffer[4096]; + gint line; + glong content_length; + GstRTSPResult res; + gboolean need_body; + + g_return_val_if_fail (conn != NULL, GST_RTSP_EINVAL); + g_return_val_if_fail (msg != NULL, GST_RTSP_EINVAL); + + line = 0; + + need_body = TRUE; + + res = GST_RTSP_OK; + /* parse first line and headers */ + while (res == GST_RTSP_OK) { + guint8 c; + + /* read first character, this identifies data messages */ + GST_RTSP_CHECK (gst_rtsp_connection_read (conn, &c, 1, timeout), + read_error); + + /* check for data packet, first character is $ */ + if (c == '$') { + guint16 size; + + /* data packets are $<1 byte channel><2 bytes length,BE> */ + + /* read channel, which is the next char */ + GST_RTSP_CHECK (gst_rtsp_connection_read (conn, &c, 1, timeout), + read_error); + + /* now we create a data message */ + gst_rtsp_message_init_data (msg, c); + + /* next two bytes are the length of the data */ + GST_RTSP_CHECK (gst_rtsp_connection_read (conn, (guint8 *) & size, 2, + timeout), read_error); + + size = GUINT16_FROM_BE (size); + + /* and read the body */ + res = read_body (conn, size, msg, timeout); + need_body = FALSE; + break; + } else { + gint offset = 0; + + /* we have a regular response */ + if (c != '\r') { + buffer[0] = c; + offset = 1; + } + /* should not happen */ + if (c == '\n') + break; + + /* read lines */ + GST_RTSP_CHECK (read_line (conn->fd, buffer + offset, + sizeof (buffer) - offset), read_error); + + if (buffer[0] == '\0') + break; + + if (line == 0) { + /* first line, check for response status */ + if (g_str_has_prefix (buffer, "RTSP")) { + res = parse_response_status (buffer, msg); + } else { + res = parse_request_line (buffer, msg); + } + } else { + /* else just parse the line */ + parse_line (buffer, msg); + } + } + line++; + } + + /* read the rest of the body if needed */ + if (need_body) { + gchar *session_id; + gchar *hdrval; + + /* see if there is a Content-Length header */ + if (gst_rtsp_message_get_header (msg, GST_RTSP_HDR_CONTENT_LENGTH, + &hdrval, 0) == GST_RTSP_OK) { + /* there is, read the body */ + content_length = atol (hdrval); + GST_RTSP_CHECK (read_body (conn, content_length, msg, timeout), + read_error); + } + + /* save session id in the connection for further use */ + if (gst_rtsp_message_get_header (msg, GST_RTSP_HDR_SESSION, + &session_id, 0) == GST_RTSP_OK) { + gint maxlen, i; + + /* default session timeout */ + conn->timeout = 60; + + maxlen = sizeof (conn->session_id) - 1; + /* the sessionid can have attributes marked with ; + * Make sure we strip them */ + for (i = 0; session_id[i] != '\0'; i++) { + if (session_id[i] == ';') { + maxlen = i; + /* parse timeout */ + do { + i++; + } while (g_ascii_isspace (session_id[i])); + if (g_str_has_prefix (&session_id[i], "timeout=")) { + gint to; + + /* if we parsed something valid, configure */ + if ((to = atoi (&session_id[i + 9])) > 0) + conn->timeout = to; + } + break; + } + } + + /* make sure to not overflow */ + strncpy (conn->session_id, session_id, maxlen); + conn->session_id[maxlen] = '\0'; + } + } + return res; + +read_error: + { + return res; + } +} + +GstRTSPResult +gst_rtsp_connection_close (GstRTSPConnection * conn) +{ + gint res; + + g_return_val_if_fail (conn != NULL, GST_RTSP_EINVAL); + + g_free (conn->ip); + conn->ip = NULL; + + if (conn->fd != -1) { + res = CLOSE_SOCKET (conn->fd); +#ifdef G_OS_WIN32 + WSACleanup (); +#endif + conn->fd = -1; + if (res != 0) + goto sys_error; + } + + return GST_RTSP_OK; + +sys_error: + { + return GST_RTSP_ESYS; + } +} + +GstRTSPResult +gst_rtsp_connection_free (GstRTSPConnection * conn) +{ + GstRTSPResult res; + + g_return_val_if_fail (conn != NULL, GST_RTSP_EINVAL); + +#ifdef G_OS_WIN32 + WSACleanup (); +#endif + res = gst_rtsp_connection_close (conn); + g_timer_destroy (conn->timer); + g_free (conn->username); + g_free (conn->passwd); + g_free (conn); + + return res; +} + +GstRTSPResult +gst_rtsp_connection_next_timeout (GstRTSPConnection * conn, GTimeVal * timeout) +{ + gdouble elapsed; + glong sec; + gulong usec; + + g_return_val_if_fail (conn != NULL, GST_RTSP_EINVAL); + g_return_val_if_fail (timeout != NULL, GST_RTSP_EINVAL); + + elapsed = g_timer_elapsed (conn->timer, &usec); + if (elapsed >= conn->timeout) { + sec = 0; + usec = 0; + } else { + sec = conn->timeout - elapsed; + } + + timeout->tv_sec = sec; + timeout->tv_usec = usec; + + return GST_RTSP_OK; +} + +GstRTSPResult +gst_rtsp_connection_reset_timeout (GstRTSPConnection * conn) +{ + g_return_val_if_fail (conn != NULL, GST_RTSP_EINVAL); + + g_timer_start (conn->timer); + + return GST_RTSP_OK; +} + +GstRTSPResult +gst_rtsp_connection_flush (GstRTSPConnection * conn, gboolean flush) +{ + g_return_val_if_fail (conn != NULL, GST_RTSP_EINVAL); + + if (flush) { + SEND_COMMAND (conn, CONTROL_STOP); + } else { + while (TRUE) { + gchar command; + int res; + + READ_COMMAND (conn, command, res); + if (res <= 0) { + /* no more commands */ + break; + } + } + } + return GST_RTSP_OK; +} + +GstRTSPResult +gst_rtsp_connection_set_auth (GstRTSPConnection * conn, + GstRTSPAuthMethod method, gchar * user, gchar * pass) +{ + /* Digest isn't implemented yet */ + if (method == GST_RTSP_AUTH_DIGEST) + return GST_RTSP_ENOTIMPL; + + /* Make sure the username and passwd are being set for authentication */ + if (method == GST_RTSP_AUTH_NONE && (user == NULL || pass == NULL)) + return GST_RTSP_EINVAL; + + /* ":" chars are not allowed in usernames for basic auth */ + if (method == GST_RTSP_AUTH_BASIC && g_strrstr (user, ":") != NULL) + return GST_RTSP_EINVAL; + + g_free (conn->username); + g_free (conn->passwd); + + conn->auth_method = method; + conn->username = g_strdup (user); + conn->passwd = g_strdup (pass); + + return GST_RTSP_OK; +} diff --git a/gst-libs/gst/rtsp/gstrtspconnection.h b/gst-libs/gst/rtsp/gstrtspconnection.h new file mode 100644 index 0000000000..7702b2e396 --- /dev/null +++ b/gst-libs/gst/rtsp/gstrtspconnection.h @@ -0,0 +1,105 @@ +/* GStreamer + * Copyright (C) <2005,2006> 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. + */ +/* + * Unless otherwise indicated, Source Code is licensed under MIT license. + * See further explanation attached in License Statement (distributed in the file + * LICENSE). + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#ifndef __GST_RTSP_CONNECTION_H__ +#define __GST_RTSP_CONNECTION_H__ + +#include + +#include +#include +#include + +G_BEGIN_DECLS + +typedef struct _GstRTSPConnection +{ + /* URL for the connection */ + GstRTSPUrl *url; + + /* connection state */ + gint fd; + gint control_sock[2]; + gchar *ip; + + /* Session state */ + gint cseq; /* sequence number */ + gchar session_id[512]; /* session id */ + gint timeout; /* session timeout in seconds */ + GTimer *timer; /* timeout timer */ + + /* Authentication */ + GstRTSPAuthMethod auth_method; + gchar *username; + gchar *passwd; +} GstRTSPConnection; + +/* opening/closing a connection */ +GstRTSPResult gst_rtsp_connection_create (GstRTSPUrl *url, GstRTSPConnection **conn); +GstRTSPResult gst_rtsp_connection_connect (GstRTSPConnection *conn, GTimeVal *timeout); +GstRTSPResult gst_rtsp_connection_close (GstRTSPConnection *conn); +GstRTSPResult gst_rtsp_connection_free (GstRTSPConnection *conn); + +/* sending/receiving raw bytes */ +GstRTSPResult gst_rtsp_connection_read (GstRTSPConnection * conn, guint8 * data, + guint size, GTimeVal * timeout); +GstRTSPResult gst_rtsp_connection_write (GstRTSPConnection * conn, const guint8 * data, + guint size, GTimeVal * timeout); + +/* sending/receiving messages */ +GstRTSPResult gst_rtsp_connection_send (GstRTSPConnection *conn, GstRTSPMessage *message, GTimeVal *timeout); +GstRTSPResult gst_rtsp_connection_receive (GstRTSPConnection *conn, GstRTSPMessage *message, GTimeVal *timeout); + +/* reset the timeout */ +GstRTSPResult gst_rtsp_connection_next_timeout (GstRTSPConnection *conn, GTimeVal *timeout); +GstRTSPResult gst_rtsp_connection_reset_timeout (GstRTSPConnection *conn); + +/* flushing state */ +GstRTSPResult gst_rtsp_connection_flush (GstRTSPConnection *conn, gboolean flush); + +/* Configure Authentication data */ +GstRTSPResult gst_rtsp_connection_set_auth (GstRTSPConnection *conn, GstRTSPAuthMethod method, + gchar *user, gchar *pass); + +G_END_DECLS + +#endif /* __GST_RTSP_CONNECTION_H__ */ diff --git a/gst-libs/gst/rtsp/gstrtspdefs.c b/gst-libs/gst/rtsp/gstrtspdefs.c new file mode 100644 index 0000000000..921692ad1a --- /dev/null +++ b/gst-libs/gst/rtsp/gstrtspdefs.c @@ -0,0 +1,316 @@ +/* GStreamer + * Copyright (C) <2005,2006> 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. + */ +/* + * Unless otherwise indicated, Source Code is licensed under MIT license. + * See further explanation attached in License Statement (distributed in the file + * LICENSE). + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include + +extern int h_errno; + +#include "gstrtspdefs.h" + +#ifndef G_OS_WIN32 +#include +#endif + +static const gchar *rtsp_results[] = { + "OK", + /* errors */ + "Generic error", + "Invalid parameter specified", + "Operation interrupted", + "Out of memory", + "Cannot resolve host", + "Function not implemented", + "System error: %s", + "Parse error", + "Error on WSAStartup", + "Windows sockets are not version 0x202", + "Received end-of-file", + "Network error: %s", + "Host is not a valid IP address", + "Timeout while waiting for server response", + "Unknown error (%d)", + NULL +}; + +static const gchar *rtsp_methods[] = { + "DESCRIBE", + "ANNOUNCE", + "GET_PARAMETER", + "OPTIONS", + "PAUSE", + "PLAY", + "RECORD", + "REDIRECT", + "SETUP", + "SET_PARAMETER", + "TEARDOWN", + NULL +}; + +static 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 */ + + /* Real extensions */ + "ClientChallenge", /* ClientChallenge */ + "RealChallenge1", /* RealChallenge1 */ + "RealChallenge2", /* RealChallenge2 */ + "RealChallenge3", /* RealChallenge3 */ + "Subscribe", /* Subscribe */ + "Alert", /* Alert */ + "ClientID", /* ClientID */ + "CompanyID", /* CompanyID */ + "GUID", /* GUID */ + "RegionData", /* RegionData */ + "SupportsMaximumASMBandwidth", /* SupportsMaximumASMBandwidth */ + "Language", /* Language */ + "PlayerStarttime", /* PlayerStarttime */ + + NULL +}; + +#define DEF_STATUS(c, t) \ + g_hash_table_insert (statuses, GUINT_TO_POINTER(c), t) + +static GHashTable * +rtsp_init_status (void) +{ + GHashTable *statuses = g_hash_table_new (NULL, NULL); + + DEF_STATUS (GST_RTSP_STS_CONTINUE, "Continue"); + DEF_STATUS (GST_RTSP_STS_OK, "OK"); + DEF_STATUS (GST_RTSP_STS_CREATED, "Created"); + DEF_STATUS (GST_RTSP_STS_LOW_ON_STORAGE, "Low on Storage Space"); + DEF_STATUS (GST_RTSP_STS_MULTIPLE_CHOICES, "Multiple Choices"); + DEF_STATUS (GST_RTSP_STS_MOVED_PERMANENTLY, "Moved Permanently"); + DEF_STATUS (GST_RTSP_STS_MOVE_TEMPORARILY, "Moved Temporarily"); + DEF_STATUS (GST_RTSP_STS_SEE_OTHER, "See Other"); + DEF_STATUS (GST_RTSP_STS_NOT_MODIFIED, "Not Modified"); + DEF_STATUS (GST_RTSP_STS_USE_PROXY, "Use Proxy"); + DEF_STATUS (GST_RTSP_STS_BAD_REQUEST, "Bad Request"); + DEF_STATUS (GST_RTSP_STS_UNAUTHORIZED, "Unauthorized"); + DEF_STATUS (GST_RTSP_STS_PAYMENT_REQUIRED, "Payment Required"); + DEF_STATUS (GST_RTSP_STS_FORBIDDEN, "Forbidden"); + DEF_STATUS (GST_RTSP_STS_NOT_FOUND, "Not Found"); + DEF_STATUS (GST_RTSP_STS_METHOD_NOT_ALLOWED, "Method Not Allowed"); + DEF_STATUS (GST_RTSP_STS_NOT_ACCEPTABLE, "Not Acceptable"); + DEF_STATUS (GST_RTSP_STS_PROXY_AUTH_REQUIRED, + "Proxy Authentication Required"); + DEF_STATUS (GST_RTSP_STS_REQUEST_TIMEOUT, "Request Time-out"); + DEF_STATUS (GST_RTSP_STS_GONE, "Gone"); + DEF_STATUS (GST_RTSP_STS_LENGTH_REQUIRED, "Length Required"); + DEF_STATUS (GST_RTSP_STS_PRECONDITION_FAILED, "Precondition Failed"); + DEF_STATUS (GST_RTSP_STS_REQUEST_ENTITY_TOO_LARGE, + "Request Entity Too Large"); + DEF_STATUS (GST_RTSP_STS_REQUEST_URI_TOO_LARGE, "Request-URI Too Large"); + DEF_STATUS (GST_RTSP_STS_UNSUPPORTED_MEDIA_TYPE, "Unsupported Media Type"); + DEF_STATUS (GST_RTSP_STS_PARAMETER_NOT_UNDERSTOOD, + "Parameter Not Understood"); + DEF_STATUS (GST_RTSP_STS_CONFERENCE_NOT_FOUND, "Conference Not Found"); + DEF_STATUS (GST_RTSP_STS_NOT_ENOUGH_BANDWIDTH, "Not Enough Bandwidth"); + DEF_STATUS (GST_RTSP_STS_SESSION_NOT_FOUND, "Session Not Found"); + DEF_STATUS (GST_RTSP_STS_METHOD_NOT_VALID_IN_THIS_STATE, + "Method Not Valid in This State"); + DEF_STATUS (GST_RTSP_STS_HEADER_FIELD_NOT_VALID_FOR_RESOURCE, + "Header Field Not Valid for Resource"); + DEF_STATUS (GST_RTSP_STS_INVALID_RANGE, "Invalid Range"); + DEF_STATUS (GST_RTSP_STS_PARAMETER_IS_READONLY, "Parameter Is Read-Only"); + DEF_STATUS (GST_RTSP_STS_AGGREGATE_OPERATION_NOT_ALLOWED, + "Aggregate operation not allowed"); + DEF_STATUS (GST_RTSP_STS_ONLY_AGGREGATE_OPERATION_ALLOWED, + "Only aggregate operation allowed"); + DEF_STATUS (GST_RTSP_STS_UNSUPPORTED_TRANSPORT, "Unsupported transport"); + DEF_STATUS (GST_RTSP_STS_DESTINATION_UNREACHABLE, "Destination unreachable"); + DEF_STATUS (GST_RTSP_STS_INTERNAL_SERVER_ERROR, "Internal Server Error"); + DEF_STATUS (GST_RTSP_STS_NOT_IMPLEMENTED, "Not Implemented"); + DEF_STATUS (GST_RTSP_STS_BAD_GATEWAY, "Bad Gateway"); + DEF_STATUS (GST_RTSP_STS_SERVICE_UNAVAILABLE, "Service Unavailable"); + DEF_STATUS (GST_RTSP_STS_GATEWAY_TIMEOUT, "Gateway Time-out"); + DEF_STATUS (GST_RTSP_STS_RTSP_VERSION_NOT_SUPPORTED, + "RTSP Version not supported"); + DEF_STATUS (GST_RTSP_STS_OPTION_NOT_SUPPORTED, "Option not supported"); + + return statuses; +} + +gchar * +gst_rtsp_strresult (GstRTSPResult result) +{ + gint idx; + gchar *res; + + idx = ABS (result); + idx = CLAMP (idx, 0, -GST_RTSP_ELAST); + + switch (idx) { + case -GST_RTSP_ESYS: + res = g_strdup_printf (rtsp_results[idx], g_strerror (errno)); + break; + case -GST_RTSP_ENET: +#ifndef G_OS_WIN32 + res = g_strdup_printf (rtsp_results[idx], hstrerror (h_errno)); +#else + res = + g_strdup + ("not supported on win32, implement me in a different way ??"); +#endif + break; + case -GST_RTSP_ELAST: + res = g_strdup_printf (rtsp_results[idx], result); + break; + default: + res = g_strdup (rtsp_results[idx]); + break; + } + return res; +} + +const gchar * +gst_rtsp_method_as_text (GstRTSPMethod method) +{ + gint i; + + if (method == GST_RTSP_INVALID) + return NULL; + + i = 0; + while ((method & 1) == 0) { + i++; + method >>= 1; + } + return rtsp_methods[i]; +} + +const gchar * +gst_rtsp_version_as_text (GstRTSPVersion version) +{ + switch (version) { + case GST_RTSP_VERSION_1_0: + return "1.0"; + + default: + return "0.0"; + } +} + +const gchar * +gst_rtsp_header_as_text (GstRTSPHeaderField field) +{ + if (field == GST_RTSP_HDR_INVALID) + return NULL; + else + return rtsp_headers[field - 1]; +} + +const gchar * +gst_rtsp_status_as_text (GstRTSPStatusCode code) +{ + static GHashTable *statuses; + + if (G_UNLIKELY (statuses == NULL)) + statuses = rtsp_init_status (); + + return g_hash_table_lookup (statuses, GUINT_TO_POINTER (code)); +} + +GstRTSPHeaderField +gst_rtsp_find_header_field (const gchar * header) +{ + gint idx; + + for (idx = 0; rtsp_headers[idx]; idx++) { + if (g_ascii_strcasecmp (rtsp_headers[idx], header) == 0) { + return idx + 1; + } + } + return GST_RTSP_HDR_INVALID; +} + +GstRTSPMethod +gst_rtsp_find_method (const gchar * method) +{ + gint idx; + + for (idx = 0; rtsp_methods[idx]; idx++) { + if (g_ascii_strcasecmp (rtsp_methods[idx], method) == 0) { + return (1 << idx); + } + } + return GST_RTSP_INVALID; +} diff --git a/gst-libs/gst/rtsp/gstrtspdefs.h b/gst-libs/gst/rtsp/gstrtspdefs.h new file mode 100644 index 0000000000..28a1c06ccd --- /dev/null +++ b/gst-libs/gst/rtsp/gstrtspdefs.h @@ -0,0 +1,249 @@ +/* GStreamer + * Copyright (C) <2005,2006> 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. + */ +/* + * Unless otherwise indicated, Source Code is licensed under MIT license. + * See further explanation attached in License Statement (distributed in the file + * LICENSE). + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#ifndef __GST_RTSP_DEFS_H__ +#define __GST_RTSP_DEFS_H__ + +#include + +G_BEGIN_DECLS + +#define GST_RTSP_CHECK(stmt, label) \ +G_STMT_START { \ + if (G_UNLIKELY ((res = (stmt)) != GST_RTSP_OK)) \ + goto label; \ +} G_STMT_END + +typedef enum { + GST_RTSP_OK = 0, + /* errors */ + GST_RTSP_ERROR = -1, + GST_RTSP_EINVAL = -2, + GST_RTSP_EINTR = -3, + GST_RTSP_ENOMEM = -4, + GST_RTSP_ERESOLV = -5, + GST_RTSP_ENOTIMPL = -6, + GST_RTSP_ESYS = -7, + GST_RTSP_EPARSE = -8, + GST_RTSP_EWSASTART = -9, + GST_RTSP_EWSAVERSION = -10, + GST_RTSP_EEOF = -11, + GST_RTSP_ENET = -12, + GST_RTSP_ENOTIP = -13, + GST_RTSP_ETIMEOUT = -14, + + GST_RTSP_ELAST = -15, +} GstRTSPResult; + +typedef enum { + GST_RTSP_FAM_NONE, + GST_RTSP_FAM_INET, + GST_RTSP_FAM_INET6, +} GstRTSPFamily; + +typedef enum { + GST_RTSP_STATE_INVALID, + GST_RTSP_STATE_INIT, + GST_RTSP_STATE_READY, + GST_RTSP_STATE_SEEKING, + GST_RTSP_STATE_PLAYING, + GST_RTSP_STATE_RECORDING, +} GstRTSPState; + +typedef enum { + GST_RTSP_VERSION_INVALID = 0x00, + GST_RTSP_VERSION_1_0 = 0x10, +} GstRTSPVersion; + +typedef enum { + GST_RTSP_INVALID = 0, + GST_RTSP_DESCRIBE = (1 << 0), + GST_RTSP_ANNOUNCE = (1 << 1), + GST_RTSP_GET_PARAMETER = (1 << 2), + GST_RTSP_OPTIONS = (1 << 3), + GST_RTSP_PAUSE = (1 << 4), + GST_RTSP_PLAY = (1 << 5), + GST_RTSP_RECORD = (1 << 6), + GST_RTSP_REDIRECT = (1 << 7), + GST_RTSP_SETUP = (1 << 8), + GST_RTSP_SET_PARAMETER = (1 << 9), + GST_RTSP_TEARDOWN = (1 << 10), +} GstRTSPMethod; + +/* Authentication methods, ordered by strength */ +typedef enum { + GST_RTSP_AUTH_NONE = 0x00, + GST_RTSP_AUTH_BASIC = 0x01, + GST_RTSP_AUTH_DIGEST = 0x02 +} GstRTSPAuthMethod; + +/* Strongest available authentication method */ +#define GST_RTSP_AUTH_MAX GST_RTSP_AUTH_DIGEST + +typedef enum { + GST_RTSP_HDR_INVALID, + + /* + * R = Request + * r = response + * g = general + * e = entity + */ + GST_RTSP_HDR_ACCEPT, /* Accept R opt. entity */ + GST_RTSP_HDR_ACCEPT_ENCODING, /* Accept-Encoding R opt. entity */ + GST_RTSP_HDR_ACCEPT_LANGUAGE, /* Accept-Language R opt. all */ + GST_RTSP_HDR_ALLOW, /* Allow r opt. all */ + GST_RTSP_HDR_AUTHORIZATION, /* Authorization R opt. all */ + GST_RTSP_HDR_BANDWIDTH, /* Bandwidth R opt. all */ + GST_RTSP_HDR_BLOCKSIZE, /* Blocksize R opt. all but OPTIONS, TEARDOWN */ + GST_RTSP_HDR_CACHE_CONTROL, /* Cache-Control g opt. SETUP */ + GST_RTSP_HDR_CONFERENCE, /* Conference R opt. SETUP */ + GST_RTSP_HDR_CONNECTION, /* Connection g req. all */ + GST_RTSP_HDR_CONTENT_BASE, /* Content-Base e opt. entity */ + GST_RTSP_HDR_CONTENT_ENCODING, /* Content-Encoding e req. SET_PARAMETER, DESCRIBE, ANNOUNCE */ + GST_RTSP_HDR_CONTENT_LANGUAGE, /* Content-Language e req. DESCRIBE, ANNOUNCE */ + GST_RTSP_HDR_CONTENT_LENGTH, /* Content-Length e req. SET_PARAMETER, ANNOUNCE, entity */ + GST_RTSP_HDR_CONTENT_LOCATION, /* Content-Location e opt. entity */ + GST_RTSP_HDR_CONTENT_TYPE, /* Content-Type e req. SET_PARAMETER, ANNOUNCE, entity */ + GST_RTSP_HDR_CSEQ, /* CSeq g req. all */ + GST_RTSP_HDR_DATE, /* Date g opt. all */ + GST_RTSP_HDR_EXPIRES, /* Expires e opt. DESCRIBE, ANNOUNCE */ + GST_RTSP_HDR_FROM, /* From R opt. all */ + GST_RTSP_HDR_IF_MODIFIED_SINCE, /* If-Modified-Since R opt. DESCRIBE, SETUP */ + GST_RTSP_HDR_LAST_MODIFIED, /* Last-Modified e opt. entity */ + GST_RTSP_HDR_PROXY_AUTHENTICATE, /* Proxy-Authenticate */ + GST_RTSP_HDR_PROXY_REQUIRE, /* Proxy-Require R req. all */ + GST_RTSP_HDR_PUBLIC, /* Public r opt. all */ + GST_RTSP_HDR_RANGE, /* Range Rr opt. PLAY, PAUSE, RECORD */ + GST_RTSP_HDR_REFERER, /* Referer R opt. all */ + GST_RTSP_HDR_REQUIRE, /* Require R req. all */ + GST_RTSP_HDR_RETRY_AFTER, /* Retry-After r opt. all */ + GST_RTSP_HDR_RTP_INFO, /* RTP-Info r req. PLAY */ + GST_RTSP_HDR_SCALE, /* Scale Rr opt. PLAY, RECORD */ + GST_RTSP_HDR_SESSION, /* Session Rr req. all but SETUP, OPTIONS */ + GST_RTSP_HDR_SERVER, /* Server r opt. all */ + GST_RTSP_HDR_SPEED, /* Speed Rr opt. PLAY */ + GST_RTSP_HDR_TRANSPORT, /* Transport Rr req. SETUP */ + GST_RTSP_HDR_UNSUPPORTED, /* Unsupported r req. all */ + GST_RTSP_HDR_USER_AGENT, /* User-Agent R opt. all */ + GST_RTSP_HDR_VIA, /* Via g opt. all */ + GST_RTSP_HDR_WWW_AUTHENTICATE, /* WWW-Authenticate r opt. all */ + + /* Real extensions */ + GST_RTSP_HDR_CLIENT_CHALLENGE, /* ClientChallenge */ + GST_RTSP_HDR_REAL_CHALLENGE1, /* RealChallenge1 */ + GST_RTSP_HDR_REAL_CHALLENGE2, /* RealChallenge2 */ + GST_RTSP_HDR_REAL_CHALLENGE3, /* RealChallenge3 */ + GST_RTSP_HDR_SUBSCRIBE, /* Subscribe */ + GST_RTSP_HDR_ALERT, /* Alert */ + GST_RTSP_HDR_CLIENT_ID, /* ClientID */ + GST_RTSP_HDR_COMPANY_ID, /* CompanyID */ + GST_RTSP_HDR_GUID, /* GUID */ + GST_RTSP_HDR_REGION_DATA, /* RegionData */ + GST_RTSP_HDR_MAX_ASM_WIDTH, /* SupportsMaximumASMBandwidth */ + GST_RTSP_HDR_LANGUAGE, /* Language */ + GST_RTSP_HDR_PLAYER_START_TIME, /* PlayerStarttime */ + + +} GstRTSPHeaderField; + +typedef enum { + GST_RTSP_STS_INVALID = 0, + GST_RTSP_STS_CONTINUE = 100, + GST_RTSP_STS_OK = 200, + GST_RTSP_STS_CREATED = 201, + GST_RTSP_STS_LOW_ON_STORAGE = 250, + GST_RTSP_STS_MULTIPLE_CHOICES = 300, + GST_RTSP_STS_MOVED_PERMANENTLY = 301, + GST_RTSP_STS_MOVE_TEMPORARILY = 302, + GST_RTSP_STS_SEE_OTHER = 303, + GST_RTSP_STS_NOT_MODIFIED = 304, + GST_RTSP_STS_USE_PROXY = 305, + GST_RTSP_STS_BAD_REQUEST = 400, + GST_RTSP_STS_UNAUTHORIZED = 401, + GST_RTSP_STS_PAYMENT_REQUIRED = 402, + GST_RTSP_STS_FORBIDDEN = 403, + GST_RTSP_STS_NOT_FOUND = 404, + GST_RTSP_STS_METHOD_NOT_ALLOWED = 405, + GST_RTSP_STS_NOT_ACCEPTABLE = 406, + GST_RTSP_STS_PROXY_AUTH_REQUIRED = 407, + GST_RTSP_STS_REQUEST_TIMEOUT = 408, + GST_RTSP_STS_GONE = 410, + GST_RTSP_STS_LENGTH_REQUIRED = 411, + GST_RTSP_STS_PRECONDITION_FAILED = 412, + GST_RTSP_STS_REQUEST_ENTITY_TOO_LARGE = 413, + GST_RTSP_STS_REQUEST_URI_TOO_LARGE = 414, + GST_RTSP_STS_UNSUPPORTED_MEDIA_TYPE = 415, + GST_RTSP_STS_PARAMETER_NOT_UNDERSTOOD = 451, + GST_RTSP_STS_CONFERENCE_NOT_FOUND = 452, + GST_RTSP_STS_NOT_ENOUGH_BANDWIDTH = 453, + GST_RTSP_STS_SESSION_NOT_FOUND = 454, + GST_RTSP_STS_METHOD_NOT_VALID_IN_THIS_STATE = 455, + GST_RTSP_STS_HEADER_FIELD_NOT_VALID_FOR_RESOURCE = 456, + GST_RTSP_STS_INVALID_RANGE = 457, + GST_RTSP_STS_PARAMETER_IS_READONLY = 458, + GST_RTSP_STS_AGGREGATE_OPERATION_NOT_ALLOWED = 459, + GST_RTSP_STS_ONLY_AGGREGATE_OPERATION_ALLOWED = 460, + GST_RTSP_STS_UNSUPPORTED_TRANSPORT = 461, + GST_RTSP_STS_DESTINATION_UNREACHABLE = 462, + GST_RTSP_STS_INTERNAL_SERVER_ERROR = 500, + GST_RTSP_STS_NOT_IMPLEMENTED = 501, + GST_RTSP_STS_BAD_GATEWAY = 502, + GST_RTSP_STS_SERVICE_UNAVAILABLE = 503, + GST_RTSP_STS_GATEWAY_TIMEOUT = 504, + GST_RTSP_STS_RTSP_VERSION_NOT_SUPPORTED = 505, + GST_RTSP_STS_OPTION_NOT_SUPPORTED = 551, +} GstRTSPStatusCode; + +gchar* gst_rtsp_strresult (GstRTSPResult result); + +const gchar* gst_rtsp_method_as_text (GstRTSPMethod method); +const gchar* gst_rtsp_version_as_text (GstRTSPVersion version); +const gchar* gst_rtsp_header_as_text (GstRTSPHeaderField field); +const gchar* gst_rtsp_status_as_text (GstRTSPStatusCode code); + +GstRTSPHeaderField gst_rtsp_find_header_field (const gchar *header); +GstRTSPMethod gst_rtsp_find_method (const gchar *method); + +G_END_DECLS + +#endif /* __GST_RTSP_DEFS_H__ */ diff --git a/gst-libs/gst/rtsp/gstrtspmessage.c b/gst-libs/gst/rtsp/gstrtspmessage.c new file mode 100644 index 0000000000..15f189f411 --- /dev/null +++ b/gst-libs/gst/rtsp/gstrtspmessage.c @@ -0,0 +1,471 @@ +/* GStreamer + * Copyright (C) <2005,2006> Wim Taymans + * <2006> Lutz Mueller + * + * 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. + */ +/* + * Unless otherwise indicated, Source Code is licensed under MIT license. + * See further explanation attached in License Statement (distributed in the file + * LICENSE). + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include + +#include "gstrtspmessage.h" + +typedef struct _RTSPKeyValue +{ + GstRTSPHeaderField field; + gchar *value; +} RTSPKeyValue; + +static void +key_value_foreach (GArray * array, GFunc func, gpointer user_data) +{ + guint i; + + g_return_if_fail (array != NULL); + + for (i = 0; i < array->len; i++) { + (*func) (&g_array_index (array, RTSPKeyValue, i), user_data); + } +} + +GstRTSPResult +gst_rtsp_message_new (GstRTSPMessage ** msg) +{ + GstRTSPMessage *newmsg; + + g_return_val_if_fail (msg != NULL, GST_RTSP_EINVAL); + + newmsg = g_new0 (GstRTSPMessage, 1); + + *msg = newmsg; + + return gst_rtsp_message_init (newmsg); +} + +GstRTSPResult +gst_rtsp_message_init (GstRTSPMessage * msg) +{ + g_return_val_if_fail (msg != NULL, GST_RTSP_EINVAL); + + gst_rtsp_message_unset (msg); + + msg->type = GST_RTSP_MESSAGE_INVALID; + msg->hdr_fields = g_array_new (FALSE, FALSE, sizeof (RTSPKeyValue)); + + return GST_RTSP_OK; +} + +GstRTSPResult +gst_rtsp_message_new_request (GstRTSPMessage ** msg, GstRTSPMethod method, + const gchar * uri) +{ + GstRTSPMessage *newmsg; + + g_return_val_if_fail (msg != NULL, GST_RTSP_EINVAL); + g_return_val_if_fail (uri != NULL, GST_RTSP_EINVAL); + + newmsg = g_new0 (GstRTSPMessage, 1); + + *msg = newmsg; + + return gst_rtsp_message_init_request (newmsg, method, uri); +} + +GstRTSPResult +gst_rtsp_message_init_request (GstRTSPMessage * msg, GstRTSPMethod method, + const gchar * uri) +{ + g_return_val_if_fail (msg != NULL, GST_RTSP_EINVAL); + g_return_val_if_fail (uri != NULL, GST_RTSP_EINVAL); + + gst_rtsp_message_unset (msg); + + msg->type = GST_RTSP_MESSAGE_REQUEST; + msg->type_data.request.method = method; + msg->type_data.request.uri = g_strdup (uri); + msg->type_data.request.version = GST_RTSP_VERSION_1_0; + msg->hdr_fields = g_array_new (FALSE, FALSE, sizeof (RTSPKeyValue)); + + return GST_RTSP_OK; +} + +GstRTSPResult +gst_rtsp_message_new_response (GstRTSPMessage ** msg, GstRTSPStatusCode code, + const gchar * reason, const GstRTSPMessage * request) +{ + GstRTSPMessage *newmsg; + + g_return_val_if_fail (msg != NULL, GST_RTSP_EINVAL); + + newmsg = g_new0 (GstRTSPMessage, 1); + + *msg = newmsg; + + return gst_rtsp_message_init_response (newmsg, code, reason, request); +} + +GstRTSPResult +gst_rtsp_message_init_response (GstRTSPMessage * msg, GstRTSPStatusCode code, + const gchar * reason, const GstRTSPMessage * request) +{ + g_return_val_if_fail (msg != NULL, GST_RTSP_EINVAL); + + gst_rtsp_message_unset (msg); + + if (reason == NULL) + reason = gst_rtsp_status_as_text (code); + + msg->type = GST_RTSP_MESSAGE_RESPONSE; + msg->type_data.response.code = code; + msg->type_data.response.reason = g_strdup (reason); + msg->type_data.response.version = GST_RTSP_VERSION_1_0; + msg->hdr_fields = g_array_new (FALSE, FALSE, sizeof (RTSPKeyValue)); + + if (request) { + gchar *header; + + /* copy CSEQ */ + if (gst_rtsp_message_get_header (request, GST_RTSP_HDR_CSEQ, &header, + 0) == GST_RTSP_OK) { + gst_rtsp_message_add_header (msg, GST_RTSP_HDR_CSEQ, header); + } + + /* copy session id */ + if (gst_rtsp_message_get_header (request, GST_RTSP_HDR_SESSION, &header, + 0) == GST_RTSP_OK) { + char *pos; + + header = g_strdup (header); + if ((pos = strchr (header, ';'))) { + *pos = '\0'; + } + g_strchomp (header); + gst_rtsp_message_add_header (msg, GST_RTSP_HDR_SESSION, header); + g_free (header); + } + + /* FIXME copy more headers? */ + } + + return GST_RTSP_OK; +} + +GstRTSPResult +gst_rtsp_message_init_data (GstRTSPMessage * msg, guint8 channel) +{ + g_return_val_if_fail (msg != NULL, GST_RTSP_EINVAL); + + gst_rtsp_message_unset (msg); + + msg->type = GST_RTSP_MESSAGE_DATA; + msg->type_data.data.channel = channel; + + return GST_RTSP_OK; +} + +GstRTSPResult +gst_rtsp_message_unset (GstRTSPMessage * msg) +{ + g_return_val_if_fail (msg != NULL, GST_RTSP_EINVAL); + + switch (msg->type) { + case GST_RTSP_MESSAGE_INVALID: + break; + case GST_RTSP_MESSAGE_REQUEST: + g_free (msg->type_data.request.uri); + break; + case GST_RTSP_MESSAGE_RESPONSE: + g_free (msg->type_data.response.reason); + break; + case GST_RTSP_MESSAGE_DATA: + break; + default: + g_assert_not_reached (); + break; + } + + if (msg->hdr_fields != NULL) + g_array_free (msg->hdr_fields, TRUE); + + g_free (msg->body); + + memset (msg, 0, sizeof *msg); + + return GST_RTSP_OK; +} + +GstRTSPResult +gst_rtsp_message_free (GstRTSPMessage * msg) +{ + GstRTSPResult res; + + g_return_val_if_fail (msg != NULL, GST_RTSP_EINVAL); + + res = gst_rtsp_message_unset (msg); + if (res == GST_RTSP_OK) + g_free (msg); + + return res; +} + +GstRTSPResult +gst_rtsp_message_add_header (GstRTSPMessage * msg, GstRTSPHeaderField field, + const gchar * value) +{ + RTSPKeyValue key_value; + + g_return_val_if_fail (msg != NULL, GST_RTSP_EINVAL); + g_return_val_if_fail (value != NULL, GST_RTSP_EINVAL); + + key_value.field = field; + key_value.value = g_strdup (value); + + g_array_append_val (msg->hdr_fields, key_value); + + + return GST_RTSP_OK; +} + +GstRTSPResult +gst_rtsp_message_remove_header (GstRTSPMessage * msg, GstRTSPHeaderField field, + gint indx) +{ + GstRTSPResult res = GST_RTSP_ENOTIMPL; + guint i = 0; + gint cnt = 0; + + g_return_val_if_fail (msg != NULL, GST_RTSP_EINVAL); + + while (i < msg->hdr_fields->len) { + RTSPKeyValue key_value = g_array_index (msg->hdr_fields, RTSPKeyValue, i); + + if (key_value.field == field && (indx == -1 || cnt++ == indx)) { + g_array_remove_index (msg->hdr_fields, i); + res = GST_RTSP_OK; + if (indx != -1) + break; + } else { + i++; + } + } + + return res; +} + +GstRTSPResult +gst_rtsp_message_get_header (const GstRTSPMessage * msg, + GstRTSPHeaderField field, gchar ** value, gint indx) +{ + guint i; + gint cnt = 0; + + g_return_val_if_fail (msg != NULL, GST_RTSP_EINVAL); + + for (i = 0; i < msg->hdr_fields->len; i++) { + RTSPKeyValue key_value = g_array_index (msg->hdr_fields, RTSPKeyValue, i); + + if (key_value.field == field && cnt++ == indx) { + if (value) + *value = key_value.value; + return GST_RTSP_OK; + } + } + + return GST_RTSP_ENOTIMPL; +} + +GstRTSPResult +gst_rtsp_message_append_headers (const GstRTSPMessage * msg, GString * str) +{ + guint i; + + g_return_val_if_fail (msg != NULL, GST_RTSP_EINVAL); + g_return_val_if_fail (str != NULL, GST_RTSP_EINVAL); + + for (i = 0; i < msg->hdr_fields->len; i++) { + RTSPKeyValue key_value = g_array_index (msg->hdr_fields, RTSPKeyValue, i); + const gchar *keystr = gst_rtsp_header_as_text (key_value.field); + + g_string_append_printf (str, "%s: %s\r\n", keystr, key_value.value); + } + return GST_RTSP_OK; +} + +GstRTSPResult +gst_rtsp_message_set_body (GstRTSPMessage * msg, const guint8 * data, + guint size) +{ + g_return_val_if_fail (msg != NULL, GST_RTSP_EINVAL); + + return gst_rtsp_message_take_body (msg, g_memdup (data, size), size); +} + +GstRTSPResult +gst_rtsp_message_take_body (GstRTSPMessage * msg, guint8 * data, guint size) +{ + g_return_val_if_fail (msg != NULL, GST_RTSP_EINVAL); + g_return_val_if_fail (data != NULL || size == 0, GST_RTSP_EINVAL); + + if (msg->body) + g_free (msg->body); + + msg->body = data; + msg->body_size = size; + + return GST_RTSP_OK; +} + +GstRTSPResult +gst_rtsp_message_get_body (const GstRTSPMessage * msg, guint8 ** data, + guint * size) +{ + g_return_val_if_fail (msg != NULL, GST_RTSP_EINVAL); + g_return_val_if_fail (data != NULL, GST_RTSP_EINVAL); + g_return_val_if_fail (size != NULL, GST_RTSP_EINVAL); + + *data = msg->body; + *size = msg->body_size; + + return GST_RTSP_OK; +} + +GstRTSPResult +gst_rtsp_message_steal_body (GstRTSPMessage * msg, guint8 ** data, guint * size) +{ + g_return_val_if_fail (msg != NULL, GST_RTSP_EINVAL); + g_return_val_if_fail (data != NULL, GST_RTSP_EINVAL); + g_return_val_if_fail (size != NULL, GST_RTSP_EINVAL); + + *data = msg->body; + *size = msg->body_size; + + msg->body = NULL; + msg->body_size = 0; + + return GST_RTSP_OK; +} + +static void +dump_mem (guint8 * mem, guint 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 data, gpointer user_data) +{ + RTSPKeyValue *key_value = (RTSPKeyValue *) data; + + g_print (" key: '%s', value: '%s'\n", + gst_rtsp_header_as_text (key_value->field), key_value->value); +} + +GstRTSPResult +gst_rtsp_message_dump (GstRTSPMessage * msg) +{ + guint8 *data; + guint size; + + g_return_val_if_fail (msg != NULL, GST_RTSP_EINVAL); + + switch (msg->type) { + case GST_RTSP_MESSAGE_REQUEST: + g_print ("RTSP request message %p\n", msg); + g_print (" request line:\n"); + g_print (" method: '%s'\n", + gst_rtsp_method_as_text (msg->type_data.request.method)); + g_print (" uri: '%s'\n", msg->type_data.request.uri); + g_print (" version: '%s'\n", + gst_rtsp_version_as_text (msg->type_data.request.version)); + g_print (" headers:\n"); + key_value_foreach (msg->hdr_fields, dump_key_value, NULL); + g_print (" body:\n"); + gst_rtsp_message_get_body (msg, &data, &size); + dump_mem (data, size); + break; + case GST_RTSP_MESSAGE_RESPONSE: + g_print ("RTSP 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 (" version: '%s'\n", + gst_rtsp_version_as_text (msg->type_data.response.version)); + g_print (" headers:\n"); + key_value_foreach (msg->hdr_fields, dump_key_value, NULL); + gst_rtsp_message_get_body (msg, &data, &size); + g_print (" body: length %d\n", size); + dump_mem (data, size); + break; + case GST_RTSP_MESSAGE_DATA: + g_print ("RTSP data message %p\n", msg); + g_print (" channel: '%d'\n", msg->type_data.data.channel); + g_print (" size: '%d'\n", msg->body_size); + gst_rtsp_message_get_body (msg, &data, &size); + dump_mem (data, size); + break; + default: + g_print ("unsupported message type %d\n", msg->type); + return GST_RTSP_EINVAL; + } + return GST_RTSP_OK; +} diff --git a/gst-libs/gst/rtsp/gstrtspmessage.h b/gst-libs/gst/rtsp/gstrtspmessage.h new file mode 100644 index 0000000000..ee654222f9 --- /dev/null +++ b/gst-libs/gst/rtsp/gstrtspmessage.h @@ -0,0 +1,144 @@ +/* GStreamer + * Copyright (C) <2005,2006> 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. + */ +/* + * Unless otherwise indicated, Source Code is licensed under MIT license. + * See further explanation attached in License Statement (distributed in the file + * LICENSE). + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#ifndef __GST_RTSP_MESSAGE_H__ +#define __GST_RTSP_MESSAGE_H__ + +#include + +#include + +G_BEGIN_DECLS + +typedef enum +{ + GST_RTSP_MESSAGE_INVALID, + GST_RTSP_MESSAGE_REQUEST, + GST_RTSP_MESSAGE_RESPONSE, + GST_RTSP_MESSAGE_DATA, +} GstRTSPMsgType; + +typedef struct _GstRTSPMessage +{ + GstRTSPMsgType type; + + union { + struct { + GstRTSPMethod method; + gchar *uri; + GstRTSPVersion version; + } request; + struct { + GstRTSPStatusCode code; + gchar *reason; + GstRTSPVersion version; + } response; + struct { + guint8 channel; + } data; + } type_data; + + GArray *hdr_fields; + + guint8 *body; + guint body_size; + +} GstRTSPMessage; + +GstRTSPResult gst_rtsp_message_new (GstRTSPMessage **msg); +GstRTSPResult gst_rtsp_message_init (GstRTSPMessage *msg); + +GstRTSPResult gst_rtsp_message_new_request (GstRTSPMessage **msg, + GstRTSPMethod method, + const gchar *uri); +GstRTSPResult gst_rtsp_message_init_request (GstRTSPMessage *msg, + GstRTSPMethod method, + const gchar *uri); + +GstRTSPResult gst_rtsp_message_new_response (GstRTSPMessage **msg, + GstRTSPStatusCode code, + const gchar *reason, + const GstRTSPMessage *request); +GstRTSPResult gst_rtsp_message_init_response (GstRTSPMessage *msg, + GstRTSPStatusCode code, + const gchar *reason, + const GstRTSPMessage *request); + +GstRTSPResult gst_rtsp_message_init_data (GstRTSPMessage *msg, + guint8 channel); + +GstRTSPResult gst_rtsp_message_unset (GstRTSPMessage *msg); +GstRTSPResult gst_rtsp_message_free (GstRTSPMessage *msg); + + +GstRTSPResult gst_rtsp_message_add_header (GstRTSPMessage *msg, + GstRTSPHeaderField field, + const gchar *value); +GstRTSPResult gst_rtsp_message_remove_header (GstRTSPMessage *msg, + GstRTSPHeaderField field, + gint indx); +GstRTSPResult gst_rtsp_message_get_header (const GstRTSPMessage *msg, + GstRTSPHeaderField field, + gchar **value, + gint indx); + +GstRTSPResult gst_rtsp_message_append_headers (const GstRTSPMessage *msg, + GString *str); + +GstRTSPResult gst_rtsp_message_set_body (GstRTSPMessage *msg, + const guint8 *data, + guint size); +GstRTSPResult gst_rtsp_message_take_body (GstRTSPMessage *msg, + guint8 *data, + guint size); +GstRTSPResult gst_rtsp_message_get_body (const GstRTSPMessage *msg, + guint8 **data, + guint *size); +GstRTSPResult gst_rtsp_message_steal_body (GstRTSPMessage *msg, + guint8 **data, + guint *size); + +GstRTSPResult gst_rtsp_message_dump (GstRTSPMessage *msg); + +G_END_DECLS + +#endif /* __GST_RTSP_MESSAGE_H__ */ diff --git a/gst-libs/gst/rtsp/gstrtsprange.c b/gst-libs/gst/rtsp/gstrtsprange.c new file mode 100644 index 0000000000..7ba8449be9 --- /dev/null +++ b/gst-libs/gst/rtsp/gstrtsprange.c @@ -0,0 +1,176 @@ +/* GStreamer + * Copyright (C) <2005,2006> 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. + */ +/* + * Unless otherwise indicated, Source Code is licensed under MIT license. + * See further explanation attached in License Statement (distributed in the file + * LICENSE). + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include +#include + +#include "gstrtsprange.h" + +/* npt-time = "now" | npt-sec | npt-hhmmss + * npt-sec = 1*DIGIT [ "." *DIGIT ] + * npt-hhmmss = npt-hh ":" npt-mm ":" npt-ss [ "." *DIGIT ] + * npt-hh = 1*DIGIT ; any positive number + * npt-mm = 1*2DIGIT ; 0-59 + * npt-ss = 1*2DIGIT ; 0-59 + */ +static GstRTSPResult +parse_npt_time (const gchar * str, GstRTSPTime * time) +{ + if (strcmp (str, "now") == 0) { + time->type = GST_RTSP_TIME_NOW; + } else if (str[0] == '\0') { + time->type = GST_RTSP_TIME_END; + } else if (strstr (str, ":")) { + gfloat seconds; + gint hours, mins; + + sscanf (str, "%2d:%2d:%f", &hours, &mins, &seconds); + + time->type = GST_RTSP_TIME_SECONDS; + time->seconds = ((hours * 60) + mins) * 60 + seconds; + } else { + gfloat seconds; + + sscanf (str, "%f", &seconds); + + time->type = GST_RTSP_TIME_SECONDS; + time->seconds = seconds; + } + return GST_RTSP_OK; +} + +/* npt-range = ( npt-time "-" [ npt-time ] ) | ( "-" npt-time ) + */ +static GstRTSPResult +parse_npt_range (const gchar * str, GstRTSPTimeRange * range) +{ + GstRTSPResult res; + gchar *p; + + range->unit = GST_RTSP_RANGE_NPT; + + /* find '-' separator */ + p = strstr (str, "-"); + if (p == NULL) + return GST_RTSP_EINVAL; + + if ((res = parse_npt_time (str, &range->min)) != GST_RTSP_OK) + goto done; + + res = parse_npt_time (p + 1, &range->max); + +done: + return res; +} + +static GstRTSPResult +parse_clock_range (const gchar * str, GstRTSPTimeRange * range) +{ + return GST_RTSP_ENOTIMPL; +} + +static GstRTSPResult +parse_smpte_range (const gchar * str, GstRTSPTimeRange * range) +{ + return GST_RTSP_ENOTIMPL; +} + +/** + * rtsp_range_parse: + * @rangestr: a range string to parse + * @range: location to hold the #GstRTSPTimeRange result + * + * Parse @rangestr to a #GstRTSPTimeRange. + * + * Returns: #GST_RTSP_OK on success. + */ +GstRTSPResult +gst_rtsp_range_parse (const gchar * rangestr, GstRTSPTimeRange ** range) +{ + GstRTSPResult ret; + GstRTSPTimeRange *res; + gchar *p; + + g_return_val_if_fail (rangestr != NULL, GST_RTSP_EINVAL); + g_return_val_if_fail (range != NULL, GST_RTSP_EINVAL); + + res = g_new0 (GstRTSPTimeRange, 1); + + p = (gchar *) rangestr; + /* first figure out the units of the range */ + if (g_str_has_prefix (p, "npt=")) { + ret = parse_npt_range (p + 4, res); + } else if (g_str_has_prefix (p, "clock=")) { + ret = parse_clock_range (p + 6, res); + } else if (g_str_has_prefix (p, "smpte=")) { + res->unit = GST_RTSP_RANGE_SMPTE; + ret = parse_smpte_range (p + 6, res); + } else if (g_str_has_prefix (p, "smpte-30-drop=")) { + res->unit = GST_RTSP_RANGE_SMPTE_30_DROP; + ret = parse_smpte_range (p + 14, res); + } else if (g_str_has_prefix (p, "smpte-25=")) { + res->unit = GST_RTSP_RANGE_SMPTE_25; + ret = parse_smpte_range (p + 9, res); + } else + goto invalid; + + if (ret == GST_RTSP_OK) + *range = res; + + return ret; + + /* ERRORS */ +invalid: + { + gst_rtsp_range_free (res); + return GST_RTSP_EINVAL; + } +} + +void +gst_rtsp_range_free (GstRTSPTimeRange * range) +{ + if (range == NULL) + return; + + g_free (range); +} diff --git a/gst-libs/gst/rtsp/gstrtsprange.h b/gst-libs/gst/rtsp/gstrtsprange.h new file mode 100644 index 0000000000..d18e4640b9 --- /dev/null +++ b/gst-libs/gst/rtsp/gstrtsprange.h @@ -0,0 +1,97 @@ +/* GStreamer + * Copyright (C) <2005,2006> 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. + */ +/* + * Unless otherwise indicated, Source Code is licensed under MIT license. + * See further explanation attached in License Statement (distributed in the file + * LICENSE). + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#ifndef __GST_RTSP_RANGE_H__ +#define __GST_RTSP_RANGE_H__ + +#include + +#include + +G_BEGIN_DECLS + +/** + * GstRTSPRangeUnit: + * @GST_RTSP_RANGE_SMPTE: + * @GST_RTSP_RANGE_SMPTE_30_DROP: + * @GST_RTSP_RANGE_SMPTE_25: + * @GST_RTSP_RANGE_NPT: + * @GST_RTSP_RANGE_CLOCK: + * + * Different possible time range units. + */ +typedef enum +{ + GST_RTSP_RANGE_SMPTE, + GST_RTSP_RANGE_SMPTE_30_DROP, + GST_RTSP_RANGE_SMPTE_25, + GST_RTSP_RANGE_NPT, + GST_RTSP_RANGE_CLOCK +} GstRTSPRangeUnit; + +typedef struct _GstRTSPTimeRange GstRTSPTimeRange; +typedef struct _GstRTSPTime GstRTSPTime; + +typedef enum { + GST_RTSP_TIME_SECONDS, + GST_RTSP_TIME_NOW, + GST_RTSP_TIME_END +} GstRTSPTimeType; + +struct _GstRTSPTime { + GstRTSPTimeType type; + gdouble seconds; +}; + +struct _GstRTSPTimeRange { + GstRTSPRangeUnit unit; + + GstRTSPTime min; + GstRTSPTime max; +}; + +GstRTSPResult gst_rtsp_range_parse (const gchar *rangestr, GstRTSPTimeRange **range); +void gst_rtsp_range_free (GstRTSPTimeRange *range); + +G_END_DECLS + +#endif /* __GST_RTSP_RANGE_H__ */ diff --git a/gst-libs/gst/rtsp/gstrtsptransport.c b/gst-libs/gst/rtsp/gstrtsptransport.c new file mode 100644 index 0000000000..080167ec89 --- /dev/null +++ b/gst-libs/gst/rtsp/gstrtsptransport.c @@ -0,0 +1,589 @@ +/* GStreamer + * Copyright (C) <2005,2006,2007> Wim Taymans + * <2007> Peter Kjellerstedt + * + * 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. + */ +/* + * Unless otherwise indicated, Source Code is licensed under MIT license. + * See further explanation attached in License Statement (distributed in the file + * LICENSE). + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include +#include + +#include "gstrtsptransport.h" + +#define MAX_MANAGERS 2 + +typedef enum +{ + RTSP_TRANSPORT_DELIVERY = 1 << 0, /* multicast | unicast */ + RTSP_TRANSPORT_DESTINATION = 1 << 1, + RTSP_TRANSPORT_SOURCE = 1 << 2, + RTSP_TRANSPORT_INTERLEAVED = 1 << 3, + RTSP_TRANSPORT_APPEND = 1 << 4, + RTSP_TRANSPORT_TTL = 1 << 5, + RTSP_TRANSPORT_LAYERS = 1 << 6, + RTSP_TRANSPORT_PORT = 1 << 7, + RTSP_TRANSPORT_CLIENT_PORT = 1 << 8, + RTSP_TRANSPORT_SERVER_PORT = 1 << 9, + RTSP_TRANSPORT_SSRC = 1 << 10, + RTSP_TRANSPORT_MODE = 1 << 11, +} RTSPTransportParameter; + +typedef struct +{ + const gchar *name; + const GstRTSPTransMode mode; + const gchar *gst_mime; + const gchar *manager[MAX_MANAGERS]; +} GstRTSPTransMap; + +static const GstRTSPTransMap transports[] = { + {"rtp", GST_RTSP_TRANS_RTP, "application/x-rtp", {"gstrtpbin", "rtpdec"}}, + {"x-real-rdt", GST_RTSP_TRANS_RDT, "application/x-rdt", {NULL, NULL}}, + {"x-pn-tng", GST_RTSP_TRANS_RDT, "application/x-rdt", {NULL, NULL}}, + {NULL, GST_RTSP_TRANS_UNKNOWN, NULL, {NULL, NULL}} +}; + +typedef struct +{ + const gchar *name; + const GstRTSPProfile profile; +} RTSPProfileMap; + +static const RTSPProfileMap profiles[] = { + {"avp", GST_RTSP_PROFILE_AVP}, + {"savp", GST_RTSP_PROFILE_SAVP}, + {NULL, GST_RTSP_PROFILE_UNKNOWN} +}; + +typedef struct +{ + const gchar *name; + const GstRTSPLowerTrans ltrans; +} RTSPLTransMap; + +static const RTSPLTransMap ltrans[] = { + {"udp", GST_RTSP_LOWER_TRANS_UDP}, + {"mcast", GST_RTSP_LOWER_TRANS_UDP_MCAST}, + {"tcp", GST_RTSP_LOWER_TRANS_TCP}, + {NULL, GST_RTSP_LOWER_TRANS_UNKNOWN} +}; + +#define RTSP_TRANSPORT_PARAMETER_IS_UNIQUE(param) \ +G_STMT_START { \ + if ((transport_params & (param)) != 0) \ + goto invalid_transport; \ + transport_params |= (param); \ +} G_STMT_END + +GstRTSPResult +gst_rtsp_transport_new (GstRTSPTransport ** transport) +{ + GstRTSPTransport *trans; + + g_return_val_if_fail (transport != NULL, GST_RTSP_EINVAL); + + trans = g_new0 (GstRTSPTransport, 1); + + *transport = trans; + + return gst_rtsp_transport_init (trans); +} + +GstRTSPResult +gst_rtsp_transport_init (GstRTSPTransport * transport) +{ + g_return_val_if_fail (transport != NULL, GST_RTSP_EINVAL); + + g_free (transport->destination); + g_free (transport->source); + + memset (transport, 0, sizeof (GstRTSPTransport)); + + transport->trans = GST_RTSP_TRANS_RTP; + transport->profile = GST_RTSP_PROFILE_AVP; + transport->lower_transport = GST_RTSP_LOWER_TRANS_UDP_MCAST; + transport->mode_play = TRUE; + transport->mode_record = FALSE; + transport->interleaved.min = -1; + transport->interleaved.max = -1; + transport->port.min = -1; + transport->port.max = -1; + transport->client_port.min = -1; + transport->client_port.max = -1; + transport->server_port.min = -1; + transport->server_port.max = -1; + + return GST_RTSP_OK; +} + +GstRTSPResult +gst_rtsp_transport_get_mime (GstRTSPTransMode trans, const gchar ** mime) +{ + gint i; + + g_return_val_if_fail (mime != NULL, GST_RTSP_EINVAL); + + for (i = 0; transports[i].name; i++) + if (transports[i].mode == trans) + break; + *mime = transports[i].gst_mime; + + return GST_RTSP_OK; +} + +GstRTSPResult +gst_rtsp_transport_get_manager (GstRTSPTransMode trans, const gchar ** manager, + guint option) +{ + gint i; + + g_return_val_if_fail (manager != NULL, GST_RTSP_EINVAL); + + for (i = 0; transports[i].name; i++) + if (transports[i].mode == trans) + break; + + if (option < MAX_MANAGERS) + *manager = transports[i].manager[option]; + else + *manager = NULL; + + return GST_RTSP_OK; +} + +static void +parse_mode (GstRTSPTransport * transport, const gchar * str) +{ + transport->mode_play = (strstr (str, "play") != NULL); + transport->mode_record = (strstr (str, "record") != NULL); +} + +static void +parse_range (const gchar * str, GstRTSPRange * range) +{ + gchar *minus; + gchar *tmp; + + /* even though strtol() allows white space, plus and minus in front of + * the number, we do not allow it + */ + if (g_ascii_isspace (*str) || *str == '+' || *str == '-') + goto invalid_range; + + minus = strstr (str, "-"); + if (minus) { + if (g_ascii_isspace (minus[1]) || minus[1] == '+' || minus[1] == '-') + goto invalid_range; + + range->min = strtol (str, &tmp, 10); + if (str == tmp || tmp != minus) + goto invalid_range; + + range->max = strtol (minus + 1, &tmp, 10); + if (*tmp && *tmp != ';') + goto invalid_range; + } else { + range->min = strtol (str, &tmp, 10); + if (str == tmp || (*tmp && *tmp != ';')) + goto invalid_range; + + range->max = -1; + } + + return; + +invalid_range: + { + range->min = -1; + range->max = -1; + return; + } +} + +static gchar * +range_as_text (const GstRTSPRange * range) +{ + if (range->min < 0) + return NULL; + else if (range->max < 0) + return g_strdup_printf ("%d", range->min); + else + return g_strdup_printf ("%d-%d", range->min, range->max); +} + +static const gchar * +rtsp_transport_mode_as_text (const GstRTSPTransport * transport) +{ + gint i; + + for (i = 0; transports[i].name; i++) + if (transports[i].mode == transport->trans) + return transports[i].name; + + return NULL; +} + +static const gchar * +rtsp_transport_profile_as_text (const GstRTSPTransport * transport) +{ + gint i; + + for (i = 0; profiles[i].name; i++) + if (profiles[i].profile == transport->profile) + return profiles[i].name; + + return NULL; +} + +static const gchar * +rtsp_transport_ltrans_as_text (const GstRTSPTransport * transport) +{ + gint i; + + /* need to special case GST_RTSP_LOWER_TRANS_UDP_MCAST */ + if (transport->lower_transport == GST_RTSP_LOWER_TRANS_UDP_MCAST) + return "udp"; + + for (i = 0; ltrans[i].name; i++) + if (ltrans[i].ltrans == transport->lower_transport) + return ltrans[i].name; + + return NULL; +} + +GstRTSPResult +gst_rtsp_transport_parse (const gchar * str, GstRTSPTransport * transport) +{ + gchar **split, *down, **transp = NULL; + guint transport_params = 0; + gint i; + + g_return_val_if_fail (transport != NULL, GST_RTSP_EINVAL); + g_return_val_if_fail (str != NULL, GST_RTSP_EINVAL); + + gst_rtsp_transport_init (transport); + + /* case insensitive */ + down = g_ascii_strdown (str, -1); + + split = g_strsplit (down, ";", 0); + g_free (down); + + /* First field contains the transport/profile/lower_transport */ + if (split[0] == NULL) + goto invalid_transport; + + transp = g_strsplit (split[0], "/", 0); + + if (transp[0] == NULL || transp[1] == NULL) + goto invalid_transport; + + for (i = 0; transports[i].name; i++) + if (strcmp (transp[0], transports[i].name) == 0) + break; + transport->trans = transports[i].mode; + + for (i = 0; profiles[i].name; i++) + if (strcmp (transp[1], profiles[i].name) == 0) + break; + transport->profile = profiles[i].profile; + + if (transp[2] != NULL) { + for (i = 0; ltrans[i].name; i++) + if (strcmp (transp[2], ltrans[i].name) == 0) + break; + transport->lower_transport = ltrans[i].ltrans; + } else { + /* specifying the lower transport is optional */ + if (transport->trans == GST_RTSP_TRANS_RTP && + transport->profile == GST_RTSP_PROFILE_AVP) + transport->lower_transport = GST_RTSP_LOWER_TRANS_UDP_MCAST; + else + transport->lower_transport = GST_RTSP_LOWER_TRANS_UNKNOWN; + } + + g_strfreev (transp); + transp = NULL; + + if (transport->trans == GST_RTSP_TRANS_UNKNOWN || + transport->profile == GST_RTSP_PROFILE_UNKNOWN || + transport->lower_transport == GST_RTSP_LOWER_TRANS_UNKNOWN) + goto unsupported_transport; + + i = 1; + while (split[i]) { + if (strcmp (split[i], "multicast") == 0) { + RTSP_TRANSPORT_PARAMETER_IS_UNIQUE (RTSP_TRANSPORT_DELIVERY); + if (transport->lower_transport == GST_RTSP_LOWER_TRANS_TCP) + goto invalid_transport; + transport->lower_transport = GST_RTSP_LOWER_TRANS_UDP_MCAST; + } else if (strcmp (split[i], "unicast") == 0) { + RTSP_TRANSPORT_PARAMETER_IS_UNIQUE (RTSP_TRANSPORT_DELIVERY); + if (transport->lower_transport == GST_RTSP_LOWER_TRANS_UDP_MCAST) + transport->lower_transport = GST_RTSP_LOWER_TRANS_UDP; + } else if (g_str_has_prefix (split[i], "destination=")) { + RTSP_TRANSPORT_PARAMETER_IS_UNIQUE (RTSP_TRANSPORT_DESTINATION); + transport->destination = g_strdup (split[i] + 12); + } else if (g_str_has_prefix (split[i], "source=")) { + RTSP_TRANSPORT_PARAMETER_IS_UNIQUE (RTSP_TRANSPORT_SOURCE); + transport->source = g_strdup (split[i] + 7); + } else if (g_str_has_prefix (split[i], "layers=")) { + RTSP_TRANSPORT_PARAMETER_IS_UNIQUE (RTSP_TRANSPORT_LAYERS); + transport->layers = strtoul (split[i] + 7, NULL, 10); + } else if (g_str_has_prefix (split[i], "mode=")) { + RTSP_TRANSPORT_PARAMETER_IS_UNIQUE (RTSP_TRANSPORT_MODE); + parse_mode (transport, split[i] + 5); + if (!transport->mode_play && !transport->mode_record) + goto invalid_transport; + } else if (strcmp (split[i], "append") == 0) { + RTSP_TRANSPORT_PARAMETER_IS_UNIQUE (RTSP_TRANSPORT_APPEND); + transport->append = TRUE; + } else if (g_str_has_prefix (split[i], "interleaved=")) { + RTSP_TRANSPORT_PARAMETER_IS_UNIQUE (RTSP_TRANSPORT_INTERLEAVED); + parse_range (split[i] + 12, &transport->interleaved); + if (transport->interleaved.min < 0 || + transport->interleaved.min >= 256 || + transport->interleaved.max >= 256) + goto invalid_transport; + } else if (g_str_has_prefix (split[i], "ttl=")) { + RTSP_TRANSPORT_PARAMETER_IS_UNIQUE (RTSP_TRANSPORT_TTL); + transport->ttl = strtoul (split[i] + 4, NULL, 10); + if (transport->ttl >= 256) + goto invalid_transport; + } else if (g_str_has_prefix (split[i], "port=")) { + RTSP_TRANSPORT_PARAMETER_IS_UNIQUE (RTSP_TRANSPORT_PORT); + parse_range (split[i] + 5, &transport->port); + if (transport->port.min < 0 || + transport->port.min >= 65536 || transport->port.max >= 65536) + goto invalid_transport; + } else if (g_str_has_prefix (split[i], "client_port=")) { + RTSP_TRANSPORT_PARAMETER_IS_UNIQUE (RTSP_TRANSPORT_CLIENT_PORT); + parse_range (split[i] + 12, &transport->client_port); + if (transport->client_port.min < 0 || + transport->client_port.min >= 65536 || + transport->client_port.max >= 65536) + goto invalid_transport; + } else if (g_str_has_prefix (split[i], "server_port=")) { + RTSP_TRANSPORT_PARAMETER_IS_UNIQUE (RTSP_TRANSPORT_SERVER_PORT); + parse_range (split[i] + 12, &transport->server_port); + if (transport->server_port.min < 0 || + transport->server_port.min >= 65536 || + transport->server_port.max >= 65536) + goto invalid_transport; + } else if (g_str_has_prefix (split[i], "ssrc=")) { + RTSP_TRANSPORT_PARAMETER_IS_UNIQUE (RTSP_TRANSPORT_SSRC); + transport->ssrc = strtoul (split[i] + 5, NULL, 16); + } else { + /* unknown field... */ + g_warning ("unknown transport field \"%s\"", split[i]); + } + i++; + } + g_strfreev (split); + + return GST_RTSP_OK; + +unsupported_transport: + { + g_strfreev (split); + return GST_RTSP_ERROR; + } +invalid_transport: + { + g_strfreev (transp); + g_strfreev (split); + return GST_RTSP_EINVAL; + } +} + +gchar * +gst_rtsp_transport_as_text (GstRTSPTransport * transport) +{ + GPtrArray *strs; + gchar *res; + const gchar *tmp; + + g_return_val_if_fail (transport != NULL, NULL); + + strs = g_ptr_array_new (); + + /* add the transport specifier */ + if ((tmp = rtsp_transport_mode_as_text (transport)) == NULL) + goto invalid_transport; + g_ptr_array_add (strs, g_ascii_strup (tmp, -1)); + + g_ptr_array_add (strs, g_strdup ("/")); + + if ((tmp = rtsp_transport_profile_as_text (transport)) == NULL) + goto invalid_transport; + g_ptr_array_add (strs, g_ascii_strup (tmp, -1)); + + if (transport->trans != GST_RTSP_TRANS_RTP || + transport->profile != GST_RTSP_PROFILE_AVP || + transport->lower_transport == GST_RTSP_LOWER_TRANS_TCP) { + g_ptr_array_add (strs, g_strdup ("/")); + + if ((tmp = rtsp_transport_ltrans_as_text (transport)) == NULL) + goto invalid_transport; + g_ptr_array_add (strs, g_ascii_strup (tmp, -1)); + } + + /* + * the order of the following parameters is the same as the one specified in + * RFC 2326 to please some weird RTSP clients that require it + */ + + /* add the unicast/multicast parameter */ + if (transport->lower_transport == GST_RTSP_LOWER_TRANS_UDP_MCAST) + g_ptr_array_add (strs, g_strdup (";multicast")); + else + g_ptr_array_add (strs, g_strdup (";unicast")); + + /* add the destination parameter */ + if (transport->destination != NULL) { + g_ptr_array_add (strs, g_strdup (";destination=")); + g_ptr_array_add (strs, g_strdup (transport->destination)); + } + + /* add the source parameter */ + if (transport->source != NULL) { + g_ptr_array_add (strs, g_strdup (";source=")); + g_ptr_array_add (strs, g_strdup (transport->source)); + } + + /* add the interleaved parameter */ + if (transport->lower_transport == GST_RTSP_LOWER_TRANS_TCP && + transport->interleaved.min >= 0) { + if (transport->interleaved.min < 256 && transport->interleaved.max < 256) { + g_ptr_array_add (strs, g_strdup (";interleaved=")); + g_ptr_array_add (strs, range_as_text (&transport->interleaved)); + } else + goto invalid_transport; + } + + /* add the append parameter */ + if (transport->mode_record && transport->append) + g_ptr_array_add (strs, g_strdup (";append")); + + /* add the ttl parameter */ + if (transport->lower_transport == GST_RTSP_LOWER_TRANS_UDP_MCAST && + transport->ttl != 0) { + if (transport->ttl < 256) { + g_ptr_array_add (strs, g_strdup (";ttl=")); + g_ptr_array_add (strs, g_strdup_printf ("%u", transport->ttl)); + } else + goto invalid_transport; + } + + /* add the layers parameter */ + if (transport->layers != 0) { + g_ptr_array_add (strs, g_strdup (";layers=")); + g_ptr_array_add (strs, g_strdup_printf ("%u", transport->layers)); + } + + /* add the port parameter */ + if (transport->trans == GST_RTSP_TRANS_RTP && transport->port.min >= 0) { + if (transport->port.min < 65536 && transport->port.max < 65536) { + g_ptr_array_add (strs, g_strdup (";port=")); + g_ptr_array_add (strs, range_as_text (&transport->port)); + } else + goto invalid_transport; + } + + /* add the client_port parameter */ + if (transport->trans == GST_RTSP_TRANS_RTP && transport->client_port.min >= 0) { + if (transport->client_port.min < 65536 && + transport->client_port.max < 65536) { + g_ptr_array_add (strs, g_strdup (";client_port=")); + g_ptr_array_add (strs, range_as_text (&transport->client_port)); + } else + goto invalid_transport; + } + + /* add the server_port parameter */ + if (transport->trans == GST_RTSP_TRANS_RTP && transport->server_port.min >= 0) { + if (transport->server_port.min < 65536 && + transport->server_port.max < 65536) { + g_ptr_array_add (strs, g_strdup (";server_port=")); + g_ptr_array_add (strs, range_as_text (&transport->server_port)); + } else + goto invalid_transport; + } + + /* add the ssrc parameter */ + if (transport->lower_transport != GST_RTSP_LOWER_TRANS_UDP_MCAST && + transport->ssrc != 0) { + g_ptr_array_add (strs, g_strdup (";ssrc=")); + g_ptr_array_add (strs, g_strdup_printf ("%08X", transport->ssrc)); + } + + /* add the mode parameter */ + if (transport->mode_play && transport->mode_record) + g_ptr_array_add (strs, g_strdup (";mode=\"PLAY,RECORD\"")); + else if (transport->mode_record) + g_ptr_array_add (strs, g_strdup (";mode=\"RECORD\"")); + else if (transport->mode_play) + g_ptr_array_add (strs, g_strdup (";mode=\"PLAY\"")); + + /* add a terminating NULL */ + g_ptr_array_add (strs, NULL); + + res = g_strjoinv (NULL, (gchar **) strs->pdata); + g_strfreev ((gchar **) g_ptr_array_free (strs, FALSE)); + + return res; + +invalid_transport: + { + g_ptr_array_add (strs, NULL); + g_strfreev ((gchar **) g_ptr_array_free (strs, FALSE)); + return NULL; + } +} + +GstRTSPResult +gst_rtsp_transport_free (GstRTSPTransport * transport) +{ + g_return_val_if_fail (transport != NULL, GST_RTSP_EINVAL); + + gst_rtsp_transport_init (transport); + g_free (transport); + + return GST_RTSP_OK; +} diff --git a/gst-libs/gst/rtsp/gstrtsptransport.h b/gst-libs/gst/rtsp/gstrtsptransport.h new file mode 100644 index 0000000000..39e4fddec1 --- /dev/null +++ b/gst-libs/gst/rtsp/gstrtsptransport.h @@ -0,0 +1,151 @@ +/* GStreamer + * Copyright (C) <2005,2006> 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. + */ +/* + * Unless otherwise indicated, Source Code is licensed under MIT license. + * See further explanation attached in License Statement (distributed in the file + * LICENSE). + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#ifndef __GST_RTSP_TRANSPORT_H__ +#define __GST_RTSP_TRANSPORT_H__ + +#include + +G_BEGIN_DECLS + +/** + * GstRTSPTransMode: + * @GST_RTSP_TRANS_UNKNOWN: invalid tansport mode + * @GST_RTSP_TRANS_RTP: transfer RTP data + * @GST_RTSP_TRANS_RDT: transfer RDT (RealMedia) data + * + * The transfer mode to use. + */ +typedef enum { + GST_RTSP_TRANS_UNKNOWN = 0, + GST_RTSP_TRANS_RTP = (1 << 0), + GST_RTSP_TRANS_RDT = (1 << 1) +} GstRTSPTransMode; + +/** + * GstRTSPProfile: + * @GST_RTSP_PROFILE_UNKNOWN: invalid profile + * @GST_RTSP_PROFILE_AVP: the Audio/Visual profile + * @GST_RTSP_PROFILE_SAVP: the secure Audio/Visual profile + * + * The transfer profile to use. + */ +typedef enum { + GST_RTSP_PROFILE_UNKNOWN = 0, + GST_RTSP_PROFILE_AVP = (1 << 0), + GST_RTSP_PROFILE_SAVP = (1 << 1) +} GstRTSPProfile; + +/** + * GstRTSPLowerTrans: + * @GST_RTSP_LOWER_TRANS_UNKNOWN: invalid transport flag + * @GST_RTSP_LOWER_TRANS_UDP: stream data over UDP + * @GST_RTSP_LOWER_TRANS_UDP_MCAST: stream data over UDP multicast + * @GST_RTSP_LOWER_TRANS_TCP: stream data over TCP + * + * The different transport methods. + */ +typedef enum { + GST_RTSP_LOWER_TRANS_UNKNOWN = 0, + GST_RTSP_LOWER_TRANS_UDP = (1 << 0), + GST_RTSP_LOWER_TRANS_UDP_MCAST = (1 << 1), + GST_RTSP_LOWER_TRANS_TCP = (1 << 2) +} GstRTSPLowerTrans; + +/** + * RTSPRange: + * @min: minimum value of the range + * @max: maximum value of the range + * + * A type to specify a range. + */ +typedef struct +{ + gint min; + gint max; +} GstRTSPRange; + +/** + * GstRTSPTransport: + * + * A structure holding the RTSP transport values. + */ +typedef struct _GstRTSPTransport { + /*< private >*/ + GstRTSPTransMode trans; + GstRTSPProfile profile; + GstRTSPLowerTrans lower_transport; + + gchar *destination; + gchar *source; + guint layers; + gboolean mode_play; + gboolean mode_record; + gboolean append; + GstRTSPRange interleaved; + + /* multicast specific */ + guint ttl; + + /* UDP specific */ + GstRTSPRange port; + GstRTSPRange client_port; + GstRTSPRange server_port; + /* RTP specific */ + guint ssrc; + +} GstRTSPTransport; + +GstRTSPResult gst_rtsp_transport_new (GstRTSPTransport **transport); +GstRTSPResult gst_rtsp_transport_init (GstRTSPTransport *transport); + +GstRTSPResult gst_rtsp_transport_parse (const gchar *str, GstRTSPTransport *transport); +gchar* gst_rtsp_transport_as_text (GstRTSPTransport *transport); + +GstRTSPResult gst_rtsp_transport_get_mime (GstRTSPTransMode trans, const gchar **mime); +GstRTSPResult gst_rtsp_transport_get_manager (GstRTSPTransMode trans, const gchar **manager, guint option); + +GstRTSPResult gst_rtsp_transport_free (GstRTSPTransport *transport); + +G_END_DECLS + +#endif /* __GST_RTSP_TRANSPORT_H__ */ diff --git a/gst-libs/gst/rtsp/gstrtspurl.c b/gst-libs/gst/rtsp/gstrtspurl.c new file mode 100644 index 0000000000..34ce9714ed --- /dev/null +++ b/gst-libs/gst/rtsp/gstrtspurl.c @@ -0,0 +1,211 @@ +/* GStreamer + * Copyright (C) <2005,2006> 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. + */ +/* + * Unless otherwise indicated, Source Code is licensed under MIT license. + * See further explanation attached in License Statement (distributed in the file + * LICENSE). + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include +#include + +#include "gstrtspurl.h" + +#define RTSP_PROTO "rtsp://" +#define RTSP_PROTO_LEN 7 +#define RTSPU_PROTO "rtspu://" +#define RTSPU_PROTO_LEN 8 +#define RTSPT_PROTO "rtspt://" +#define RTSPT_PROTO_LEN 8 + +/* format is rtsp[u]://[user:passwd@]host[:port]/abspath[?query] */ + +GstRTSPResult +gst_rtsp_url_parse (const gchar * urlstr, GstRTSPUrl ** url) +{ + GstRTSPUrl *res; + gchar *p, *delim, *at, *col; + + g_return_val_if_fail (urlstr != NULL, GST_RTSP_EINVAL); + g_return_val_if_fail (url != NULL, GST_RTSP_EINVAL); + + res = g_new0 (GstRTSPUrl, 1); + + p = (gchar *) urlstr; + if (g_str_has_prefix (p, RTSP_PROTO)) { + res->transports = + GST_RTSP_LOWER_TRANS_TCP | GST_RTSP_LOWER_TRANS_UDP | + GST_RTSP_LOWER_TRANS_UDP_MCAST; + p += RTSP_PROTO_LEN; + } else if (g_str_has_prefix (p, RTSPU_PROTO)) { + res->transports = GST_RTSP_LOWER_TRANS_UDP | GST_RTSP_LOWER_TRANS_UDP_MCAST; + p += RTSPU_PROTO_LEN; + } else if (g_str_has_prefix (p, RTSPT_PROTO)) { + res->transports = GST_RTSP_LOWER_TRANS_TCP; + p += RTSPT_PROTO_LEN; + } else + goto invalid; + + delim = strpbrk (p, "/?"); + at = strchr (p, '@'); + + if (at && delim && at > delim) + at = NULL; + + if (at) { + col = strchr (p, ':'); + + /* must have a ':' and it must be before the '@' */ + if (col == NULL || col > at) + goto invalid; + + res->user = g_strndup (p, col - p); + col++; + res->passwd = g_strndup (col, at - col); + + /* move to host */ + p = at + 1; + } + + col = strchr (p, ':'); + /* we have a ':' and a delimiter but the ':' is after the delimiter, it's + * not really part of the hostname */ + if (col && delim && col >= delim) + col = NULL; + + if (col) { + res->host = g_strndup (p, col - p); + p = col + 1; + res->port = strtoul (p, (char **) &p, 10); + if (delim) + p = delim; + } else { + /* no port specified, set to 0. _get_port() will return the default port. */ + res->port = 0; + if (!delim) { + res->host = g_strdup (p); + p = NULL; + } else { + res->host = g_strndup (p, delim - p); + p = delim; + } + } + + if (p && *p == '/') { + delim = strchr (p, '?'); + if (!delim) { + res->abspath = g_strdup (p); + p = NULL; + } else { + res->abspath = g_strndup (p, delim - p); + p = delim; + } + } else { + res->abspath = g_strdup ("/"); + } + + if (p && *p == '?') + res->query = g_strdup (p + 1); + + *url = res; + + return GST_RTSP_OK; + + /* ERRORS */ +invalid: + { + gst_rtsp_url_free (res); + return GST_RTSP_EINVAL; + } +} + +void +gst_rtsp_url_free (GstRTSPUrl * url) +{ + if (url == NULL) + return; + + g_free (url->user); + g_free (url->passwd); + g_free (url->host); + g_free (url->abspath); + g_free (url->query); + g_free (url); +} + +GstRTSPResult +gst_rtsp_url_set_port (GstRTSPUrl * url, guint16 port) +{ + g_return_val_if_fail (url != NULL, GST_RTSP_EINVAL); + + url->port = port; + + return GST_RTSP_OK; +} + +GstRTSPResult +gst_rtsp_url_get_port (GstRTSPUrl * url, guint16 * port) +{ + g_return_val_if_fail (url != NULL, GST_RTSP_EINVAL); + g_return_val_if_fail (port != NULL, GST_RTSP_EINVAL); + + /* if a port was specified, use that else use the default port. */ + if (url->port != 0) + *port = url->port; + else + *port = GST_RTSP_DEFAULT_PORT; + + return GST_RTSP_OK; +} + +gchar * +gst_rtsp_url_get_request_uri (GstRTSPUrl * url) +{ + gchar *uri; + + g_return_val_if_fail (url != NULL, NULL); + + if (url->port != 0) { + uri = g_strdup_printf ("rtsp://%s:%u%s%s%s", url->host, url->port, + url->abspath, url->query ? "?" : "", url->query ? url->query : ""); + } else { + uri = g_strdup_printf ("rtsp://%s%s%s%s", url->host, url->abspath, + url->query ? "?" : "", url->query ? url->query : ""); + } + + return uri; +} diff --git a/gst-libs/gst/rtsp/gstrtspurl.h b/gst-libs/gst/rtsp/gstrtspurl.h new file mode 100644 index 0000000000..cbe6cca3b9 --- /dev/null +++ b/gst-libs/gst/rtsp/gstrtspurl.h @@ -0,0 +1,77 @@ +/* GStreamer + * Copyright (C) <2005,2006> 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. + */ +/* + * Unless otherwise indicated, Source Code is licensed under MIT license. + * See further explanation attached in License Statement (distributed in the file + * LICENSE). + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#ifndef __GST_RTSP_URL_H__ +#define __GST_RTSP_URL_H__ + +#include + +#include +#include + +G_BEGIN_DECLS + +#define GST_RTSP_DEFAULT_PORT 554 + +typedef struct _GstRTSPUrl GstRTSPUrl; + +struct _GstRTSPUrl { + GstRTSPLowerTrans transports; + GstRTSPFamily family; + gchar *user; + gchar *passwd; + gchar *host; + guint16 port; + gchar *abspath; + gchar *query; +}; + +GstRTSPResult gst_rtsp_url_parse (const gchar *urlstr, GstRTSPUrl **url); +void gst_rtsp_url_free (GstRTSPUrl *url); +gchar* gst_rtsp_url_get_request_uri (GstRTSPUrl *url); + +GstRTSPResult gst_rtsp_url_set_port (GstRTSPUrl *url, guint16 port); +GstRTSPResult gst_rtsp_url_get_port (GstRTSPUrl *url, guint16 *port); + +G_END_DECLS + +#endif /* __GST_RTSP_URL_H__ */ diff --git a/gst-libs/gst/sdp/Makefile.am b/gst-libs/gst/sdp/Makefile.am new file mode 100644 index 0000000000..fedab63ff6 --- /dev/null +++ b/gst-libs/gst/sdp/Makefile.am @@ -0,0 +1,12 @@ +libgstsdpincludedir = $(includedir)/gstreamer-@GST_MAJORMINOR@/gst/sdp + +libgstsdpinclude_HEADERS = gstsdp.h \ + gstsdpmessage.h + +lib_LTLIBRARIES = libgstsdp-@GST_MAJORMINOR@.la + +libgstsdp_@GST_MAJORMINOR@_la_SOURCES = gstsdpmessage.c + +libgstsdp_@GST_MAJORMINOR@_la_CFLAGS = $(GST_PLUGINS_BASE_CFLAGS) $(GST_BASE_CFLAGS) $(GST_CFLAGS) +libgstsdp_@GST_MAJORMINOR@_la_LIBADD = $(GST_LIBS) +libgstsdp_@GST_MAJORMINOR@_la_LDFLAGS = $(GST_LIB_LDFLAGS) $(GST_ALL_LDFLAGS) $(GST_LT_LDFLAGS) diff --git a/gst-libs/gst/sdp/gstsdp.h b/gst-libs/gst/sdp/gstsdp.h new file mode 100644 index 0000000000..a16518f518 --- /dev/null +++ b/gst-libs/gst/sdp/gstsdp.h @@ -0,0 +1,51 @@ +/* GStreamer + * Copyright (C) <2005,2006> 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. + */ +/* + * Unless otherwise indicated, Source Code is licensed under MIT license. + * See further explanation attached in License Statement (distributed in the file + * LICENSE). + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#ifndef __GST_SDP_H__ +#define __GST_SDP_H__ + +typedef enum { + GST_SDP_OK = 0, + GST_SDP_EINVAL = -1 +} GstSDPResult; + +#endif /* __GST_SDP_H__ */ diff --git a/gst-libs/gst/sdp/gstsdpmessage.c b/gst-libs/gst/sdp/gstsdpmessage.c new file mode 100644 index 0000000000..2bf7f8ab27 --- /dev/null +++ b/gst-libs/gst/sdp/gstsdpmessage.c @@ -0,0 +1,936 @@ +/* GStreamer + * Copyright (C) <2005,2006> 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. + */ +/* + * Unless otherwise indicated, Source Code is licensed under MIT license. + * See further explanation attached in License Statement (distributed in the file + * LICENSE). + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include +#include + +#include "gstsdpmessage.h" + +/* FIXME, is currently allocated on the stack */ +#define MAX_LINE_LEN 1024 * 16 + +#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,init_func) \ +G_STMT_START { \ + if (field) { \ + guint i; \ + for(i=0; ilen; i++) \ + init_func (&g_array_index(field, type, i)); \ + g_array_set_size (field,0); \ + } \ + else \ + field = g_array_new (FALSE, TRUE, sizeof(type)); \ +} G_STMT_END + +#define DEFINE_STRING_SETTER(field) \ +GstSDPResult gst_sdp_message_set_##field (GstSDPMessage *msg, gchar *val) { \ + g_free (msg->field); \ + msg->field = g_strdup (val); \ + return GST_SDP_OK; \ +} +#define DEFINE_STRING_GETTER(field) \ +char* gst_sdp_message_get_##field (GstSDPMessage *msg) { \ + return msg->field; \ +} + +#define DEFINE_ARRAY_LEN(field) \ +gint gst_sdp_message_##field##_len (GstSDPMessage *msg) { \ + return ((msg)->field->len); \ +} +#define DEFINE_ARRAY_GETTER(method,field,type) \ +type gst_sdp_message_get_##method (GstSDPMessage *msg, guint idx) { \ + return g_array_index ((msg)->field, type, idx); \ +} +#define DEFINE_ARRAY_P_GETTER(method,field,type) \ +type * gst_sdp_message_get_##method (GstSDPMessage *msg, guint idx) { \ + return &g_array_index ((msg)->field, type, idx); \ +} +#define DEFINE_ARRAY_ADDER(method,field,type,dup_method) \ +GstSDPResult gst_sdp_message_add_##method (GstSDPMessage *msg, type val) { \ + type v = dup_method(val); \ + g_array_append_val((msg)->field, v); \ + return GST_SDP_OK; \ +} + +static void +gst_sdp_origin_init (GstSDPOrigin * origin) +{ + FREE_STRING (origin->username); + FREE_STRING (origin->sess_id); + FREE_STRING (origin->sess_version); + FREE_STRING (origin->nettype); + FREE_STRING (origin->addrtype); + FREE_STRING (origin->addr); +} + +static void +gst_sdp_connection_init (GstSDPConnection * connection) +{ + FREE_STRING (connection->nettype); + FREE_STRING (connection->addrtype); + FREE_STRING (connection->address); + connection->ttl = 0; + connection->addr_number = 0; +} + +static void +gst_sdp_bandwidth_init (GstSDPBandwidth * bandwidth) +{ + FREE_STRING (bandwidth->bwtype); + bandwidth->bandwidth = 0; +} + +static void +gst_sdp_time_init (GstSDPTime * time) +{ + FREE_STRING (time->start); + FREE_STRING (time->stop); + time->n_repeat = 0; +} + +static void +gst_sdp_zone_init (GstSDPZone * zone) +{ + FREE_STRING (zone->time); + FREE_STRING (zone->typed_time); +} + +static void +gst_sdp_key_init (GstSDPKey * key) +{ + FREE_STRING (key->type); + FREE_STRING (key->data); +} + +static void +gst_sdp_attribute_init (GstSDPAttribute * attr) +{ + FREE_STRING (attr->key); + FREE_STRING (attr->value); +} + +/** + * gst_sdp_message_new: + * @msg: pointer to new #GstSDPMessage + * + * Allocate a new GstSDPMessage and store the result in @msg. + * + * Returns: a #GstSDPResult. + */ +GstSDPResult +gst_sdp_message_new (GstSDPMessage ** msg) +{ + GstSDPMessage *newmsg; + + g_return_val_if_fail (msg != NULL, GST_SDP_EINVAL); + + newmsg = g_new0 (GstSDPMessage, 1); + + *msg = newmsg; + + return gst_sdp_message_init (newmsg); +} + +/** + * gst_sdp_message_init: + * @msg: an #GstSDPMessage + * + * Initialize @msg so that its contents are as if it was freshly allocated + * with gst_sdp_message_new(). This function is mostly used to initialize a message + * allocated on the stack. gst_sdp_message_uninit() undoes this operation. + * + * When this function is invoked on newly allocated data (with malloc or on the + * stack), its contents should be set to 0 before calling this function. + * + * Returns: a #GstSDPResult. + */ +GstSDPResult +gst_sdp_message_init (GstSDPMessage * msg) +{ + g_return_val_if_fail (msg != NULL, GST_SDP_EINVAL); + + FREE_STRING (msg->version); + gst_sdp_origin_init (&msg->origin); + FREE_STRING (msg->session_name); + FREE_STRING (msg->information); + FREE_STRING (msg->uri); + INIT_ARRAY (msg->emails, gchar *, g_free); + INIT_ARRAY (msg->phones, gchar *, g_free); + gst_sdp_connection_init (&msg->connection); + INIT_ARRAY (msg->bandwidths, GstSDPBandwidth, gst_sdp_bandwidth_init); + INIT_ARRAY (msg->times, GstSDPTime, gst_sdp_time_init); + INIT_ARRAY (msg->zones, GstSDPZone, gst_sdp_zone_init); + gst_sdp_key_init (&msg->key); + INIT_ARRAY (msg->attributes, GstSDPAttribute, gst_sdp_attribute_init); + INIT_ARRAY (msg->medias, GstSDPMedia, gst_sdp_media_uninit); + + return GST_SDP_OK; +} + +/** + * gst_sdp_message_uninit: + * @msg: an #GstSDPMessage + * + * Free all resources allocated in @msg. @msg should not be used anymore after + * this function. This function should be used when @msg was allocated on the + * stack and initialized with gst_sdp_message_init(). + * + * Returns: a #GstSDPResult. + */ +GstSDPResult +gst_sdp_message_uninit (GstSDPMessage * msg) +{ + g_return_val_if_fail (msg != NULL, GST_SDP_EINVAL); + + gst_sdp_message_init (msg); + + 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 GST_SDP_OK; +} + +/** + * gst_sdp_message_free: + * @msg: an #GstSDPMessage + * + * Free all resources allocated by @msg. @msg should not be used anymore after + * this function. This function should be used when @msg was dynamically + * allocated with gst_sdp_message_new(). + * + * Returns: a #GstSDPResult. + */ +GstSDPResult +gst_sdp_message_free (GstSDPMessage * msg) +{ + g_return_val_if_fail (msg != NULL, GST_SDP_EINVAL); + + gst_sdp_message_uninit (msg); + g_free (msg); + + return GST_SDP_OK; +} + +/** + * gst_sdp_media_new: + * @media: pointer to new #GstSDPMedia + * + * Allocate a new GstSDPMedia and store the result in @media. + * + * Returns: a #GstSDPResult. + */ +GstSDPResult +gst_sdp_media_new (GstSDPMedia ** media) +{ + GstSDPMedia *newmedia; + + g_return_val_if_fail (media != NULL, GST_SDP_EINVAL); + + newmedia = g_new0 (GstSDPMedia, 1); + + *media = newmedia; + + return gst_sdp_media_init (newmedia); +} + +/** + * gst_sdp_media_init: + * @media: a #GstSDPMedia + * + * Initialize @media so that its contents are as if it was freshly allocated + * with gst_sdp_media_new(). This function is mostly used to initialize a media + * allocated on the stack. gst_sdp_media_uninit() undoes this operation. + * + * When this function is invoked on newly allocated data (with malloc or on the + * stack), its contents should be set to 0 before calling this function. + * + * Returns: a #GstSDPResult. + */ +GstSDPResult +gst_sdp_media_init (GstSDPMedia * media) +{ + g_return_val_if_fail (media != NULL, GST_SDP_EINVAL); + + FREE_STRING (media->media); + media->port = 0; + media->num_ports = 0; + FREE_STRING (media->proto); + INIT_ARRAY (media->fmts, gchar *, g_free); + FREE_STRING (media->information); + INIT_ARRAY (media->connections, GstSDPConnection, gst_sdp_connection_init); + INIT_ARRAY (media->bandwidths, GstSDPBandwidth, gst_sdp_bandwidth_init); + gst_sdp_key_init (&media->key); + INIT_ARRAY (media->attributes, GstSDPAttribute, gst_sdp_attribute_init); + + return GST_SDP_OK; +} + +/** + * gst_sdp_media_uninit: + * @media: an #GstSDPMedia + * + * Free all resources allocated in @media. @media should not be used anymore after + * this function. This function should be used when @media was allocated on the + * stack and initialized with gst_sdp_media_init(). + * + * Returns: a #GstSDPResult. + */ +GstSDPResult +gst_sdp_media_uninit (GstSDPMedia * media) +{ + g_return_val_if_fail (media != NULL, GST_SDP_EINVAL); + + gst_sdp_media_init (media); + FREE_ARRAY (media->fmts); + FREE_ARRAY (media->connections); + FREE_ARRAY (media->bandwidths); + FREE_ARRAY (media->attributes); + + return GST_SDP_OK; +} + +/** + * gst_sdp_media_free: + * @media: an #GstSDPMedia + * + * Free all resources allocated by @media. @media should not be used anymore after + * this function. This function should be used when @media was dynamically + * allocated with gst_sdp_media_new(). + * + * Returns: a #GstSDPResult. + */ +GstSDPResult +gst_sdp_media_free (GstSDPMedia * media) +{ + g_return_val_if_fail (media != NULL, GST_SDP_EINVAL); + + gst_sdp_media_uninit (media); + g_free (media); + + return GST_SDP_OK; +} + +DEFINE_STRING_SETTER (version); +DEFINE_STRING_GETTER (version); + +GstSDPResult +gst_sdp_message_set_origin (GstSDPMessage * 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 GST_SDP_OK; +} + +GstSDPOrigin * +gst_sdp_message_get_origin (GstSDPMessage * 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); + +GstSDPResult +gst_sdp_message_set_connection (GstSDPMessage * 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 GST_SDP_OK; +} + +GstSDPConnection * +gst_sdp_message_get_connection (GstSDPMessage * msg) +{ + return &msg->connection; +} + +DEFINE_ARRAY_LEN (bandwidths); +DEFINE_ARRAY_P_GETTER (bandwidth, bandwidths, GstSDPBandwidth); + +GstSDPResult +gst_sdp_message_add_bandwidth (GstSDPMessage * msg, gchar * bwtype, + gint bandwidth) +{ + GstSDPBandwidth bw; + + bw.bwtype = g_strdup (bwtype); + bw.bandwidth = bandwidth; + + g_array_append_val (msg->bandwidths, bw); + + return GST_SDP_OK; +} + +DEFINE_ARRAY_LEN (times); +DEFINE_ARRAY_P_GETTER (time, times, GstSDPTime); + +GstSDPResult +gst_sdp_message_add_time (GstSDPMessage * msg, gchar * time) +{ + return GST_SDP_OK; +} + +DEFINE_ARRAY_LEN (zones); +DEFINE_ARRAY_P_GETTER (zone, zones, GstSDPZone); +GstSDPResult +gst_sdp_message_add_zone (GstSDPMessage * msg, gchar * time, gchar * typed_time) +{ + GstSDPZone zone; + + zone.time = g_strdup (time); + zone.typed_time = g_strdup (typed_time); + + g_array_append_val (msg->zones, zone); + + return GST_SDP_OK; +} + +GstSDPResult +gst_sdp_message_set_key (GstSDPMessage * msg, gchar * type, gchar * data) +{ + REPLACE_STRING (msg->key.type, type); + REPLACE_STRING (msg->key.data, data); + return GST_SDP_OK; +} + +GstSDPKey * +gst_sdp_message_get_key (GstSDPMessage * msg) +{ + return &msg->key; +} + + +DEFINE_ARRAY_LEN (attributes); +DEFINE_ARRAY_P_GETTER (attribute, attributes, GstSDPAttribute); +gchar * +gst_sdp_message_get_attribute_val_n (GstSDPMessage * msg, gchar * key, + guint nth) +{ + guint i; + + for (i = 0; i < msg->attributes->len; i++) { + GstSDPAttribute *attr; + + attr = &g_array_index (msg->attributes, GstSDPAttribute, i); + if (!strcmp (attr->key, key)) { + if (nth == 0) + return attr->value; + else + nth--; + } + } + return NULL; +} + +gchar * +gst_sdp_message_get_attribute_val (GstSDPMessage * msg, gchar * key) +{ + return gst_sdp_message_get_attribute_val_n (msg, key, 0); +} + +GstSDPResult +gst_sdp_message_add_attribute (GstSDPMessage * msg, gchar * key, gchar * value) +{ + GstSDPAttribute attr; + + attr.key = g_strdup (key); + attr.value = g_strdup (value); + + g_array_append_val (msg->attributes, attr); + + return GST_SDP_OK; +} + +DEFINE_ARRAY_LEN (medias); +DEFINE_ARRAY_P_GETTER (media, medias, GstSDPMedia); + +/** + * gst_sdp_message_add_media: + * @msg: an #GstSDPMessage + * @media: an #GstSDPMedia to add + * + * Adds @media to the array of medias in @msg. This function takes ownership of + * the contents of @media so that @media will have to be reinitialized with + * gst_media_init() before it can be used again. + * + * Returns: an #GstSDPResult. + */ +GstSDPResult +gst_sdp_message_add_media (GstSDPMessage * msg, GstSDPMedia * media) +{ + gint len; + GstSDPMedia *nmedia; + + len = msg->medias->len; + g_array_set_size (msg->medias, len + 1); + nmedia = &g_array_index (msg->medias, GstSDPMedia, len); + + memcpy (nmedia, media, sizeof (GstSDPMedia)); + memset (media, 0, sizeof (GstSDPMedia)); + + return GST_SDP_OK; +} + +/* media access */ + +GstSDPResult +gst_sdp_media_add_attribute (GstSDPMedia * media, gchar * key, gchar * value) +{ + GstSDPAttribute attr; + + attr.key = g_strdup (key); + attr.value = g_strdup (value); + + g_array_append_val (media->attributes, attr); + + return GST_SDP_OK; +} + +GstSDPResult +gst_sdp_media_add_bandwidth (GstSDPMedia * media, gchar * bwtype, + gint bandwidth) +{ + GstSDPBandwidth bw; + + bw.bwtype = g_strdup (bwtype); + bw.bandwidth = bandwidth; + + g_array_append_val (media->bandwidths, bw); + + return GST_SDP_OK; +} + +GstSDPResult +gst_sdp_media_add_format (GstSDPMedia * media, gchar * format) +{ + gchar *fmt; + + fmt = g_strdup (format); + + g_array_append_val (media->fmts, fmt); + + return GST_SDP_OK; +} + +GstSDPAttribute * +gst_sdp_media_get_attribute (GstSDPMedia * media, guint idx) +{ + return &g_array_index (media->attributes, GstSDPAttribute, idx); +} + +gchar * +gst_sdp_media_get_attribute_val_n (GstSDPMedia * media, gchar * key, guint nth) +{ + guint i; + + for (i = 0; i < media->attributes->len; i++) { + GstSDPAttribute *attr; + + attr = &g_array_index (media->attributes, GstSDPAttribute, i); + if (!strcmp (attr->key, key)) { + if (nth == 0) + return attr->value; + else + nth--; + } + } + return NULL; +} + +gchar * +gst_sdp_media_get_attribute_val (GstSDPMedia * media, gchar * key) +{ + return gst_sdp_media_get_attribute_val_n (media, key, 0); +} + +gchar * +gst_sdp_media_get_format (GstSDPMedia * media, guint idx) +{ + if (idx >= media->fmts->len) + return NULL; + return g_array_index (media->fmts, gchar *, idx); +} + +static void +read_string (gchar * dest, guint size, gchar ** src) +{ + guint 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, guint size, gchar del, gchar ** src) +{ + guint 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; + GstSDPMessage *msg; + GstSDPMedia *media; +} SDPContext; + +static gboolean +gst_sdp_parse_line (SDPContext * c, gchar type, gchar * buffer) +{ + gchar str[8192]; + 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_warning ("wrong SDP version"); + gst_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': + gst_sdp_message_add_email (c->msg, buffer); + break; + case 'p': + gst_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[MAX_LINE_LEN]; + + read_string_del (str, sizeof (str), ':', &p); + read_string (str2, sizeof (str2), &p); + if (c->state == SDP_SESSION) + gst_sdp_message_add_bandwidth (c->msg, str, atoi (str2)); + else + gst_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) + gst_sdp_message_add_attribute (c->msg, str, p); + else + gst_sdp_media_add_attribute (c->media, str, p); + break; + case 'm': + { + gchar *slash; + GstSDPMedia nmedia; + + c->state = SDP_MEDIA; + memset (&nmedia, 0, sizeof (nmedia)); + gst_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); + gst_sdp_media_add_format (&nmedia, str); + } while (*p != '\0'); + + gst_sdp_message_add_media (c->msg, &nmedia); + c->media = + &g_array_index (c->msg->medias, GstSDPMedia, c->msg->medias->len - 1); + break; + } + default: + break; + } + return TRUE; +} + +GstSDPResult +gst_sdp_message_parse_buffer (guint8 * data, guint size, GstSDPMessage * msg) +{ + gchar *p; + SDPContext c; + gchar type; + gchar buffer[MAX_LINE_LEN]; + guint idx = 0; + + g_return_val_if_fail (msg != NULL, GST_SDP_EINVAL); + g_return_val_if_fail (data != NULL, GST_SDP_EINVAL); + g_return_val_if_fail (size != 0, GST_SDP_EINVAL); + + c.state = SDP_SESSION; + c.msg = msg; + c.media = NULL; + + p = (gchar *) 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'; + gst_sdp_parse_line (&c, type, buffer); + + line_done: + while (*p != '\n' && *p != '\0') + p++; + if (*p == '\n') + p++; + } + + return GST_SDP_OK; +} + +static void +print_media (GstSDPMedia * 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) { + guint 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) { + guint i; + + g_print (" attributes:\n"); + for (i = 0; i < media->attributes->len; i++) { + GstSDPAttribute *attr = + &g_array_index (media->attributes, GstSDPAttribute, i); + + g_print (" attribute '%s' : '%s'\n", attr->key, attr->value); + } + } +} + +GstSDPResult +gst_sdp_message_dump (GstSDPMessage * msg) +{ + g_return_val_if_fail (msg != NULL, GST_SDP_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) { + guint 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) { + guint 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) { + guint i; + + g_print (" attributes:\n"); + for (i = 0; i < msg->attributes->len; i++) { + GstSDPAttribute *attr = + &g_array_index (msg->attributes, GstSDPAttribute, i); + + g_print (" attribute '%s' : '%s'\n", attr->key, attr->value); + } + } + if (msg->medias->len > 0) { + guint 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, GstSDPMedia, i)); + } + } + return GST_SDP_OK; +} diff --git a/gst-libs/gst/sdp/gstsdpmessage.h b/gst-libs/gst/sdp/gstsdpmessage.h new file mode 100644 index 0000000000..e381ace52c --- /dev/null +++ b/gst-libs/gst/sdp/gstsdpmessage.h @@ -0,0 +1,200 @@ +/* GStreamer + * Copyright (C) <2005,2006> 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. + */ +/* + * Unless otherwise indicated, Source Code is licensed under MIT license. + * See further explanation attached in License Statement (distributed in the file + * LICENSE). + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#ifndef __GST_SDP_MESSAGE_H__ +#define __GST_SDP_MESSAGE_H__ + +#include + +#include + +G_BEGIN_DECLS + +typedef struct { + gchar *username; + gchar *sess_id; + gchar *sess_version; + gchar *nettype; + gchar *addrtype; + gchar *addr; +} GstSDPOrigin; + +typedef struct { + gchar *nettype; + gchar *addrtype; + gchar *address; + gint ttl; + gint addr_number; +} GstSDPConnection; + +#define GST_SDP_BWTYPE_CT "CT" /* conference total */ +#define GST_SDP_BWTYPE_AS "AS" /* application specific */ +#define GST_SDP_BWTYPE_EXT_PREFIX "X-" /* extension prefix */ + +typedef struct { + gchar *bwtype; + gint bandwidth; +} GstSDPBandwidth; + +typedef struct { + gchar *start; + gchar *stop; + gint n_repeat; + gchar **repeat; +} GstSDPTime; + +typedef struct { + gchar *time; + gchar *typed_time; +} GstSDPZone; + +typedef struct { + gchar *type; + gchar *data; +} GstSDPKey; + +typedef struct { + gchar *key; + gchar *value; +} GstSDPAttribute; + +typedef struct { + gchar *media; + gint port; + gint num_ports; + gchar *proto; + GArray *fmts; + gchar *information; + GArray *connections; + GArray *bandwidths; + GstSDPKey key; + GArray *attributes; +} GstSDPMedia; + +typedef struct { + gchar *version; + GstSDPOrigin origin; + gchar *session_name; + gchar *information; + gchar *uri; + GArray *emails; + GArray *phones; + GstSDPConnection connection; + GArray *bandwidths; + GArray *times; + GArray *zones; + GstSDPKey key; + GArray *attributes; + GArray *medias; +} GstSDPMessage; + +/* Session descriptions */ +GstSDPResult gst_sdp_message_new (GstSDPMessage **msg); +GstSDPResult gst_sdp_message_init (GstSDPMessage *msg); +GstSDPResult gst_sdp_message_uninit (GstSDPMessage *msg); +GstSDPResult gst_sdp_message_free (GstSDPMessage *msg); + +GstSDPResult gst_sdp_message_parse_buffer (guint8 *data, guint size, GstSDPMessage *msg); + +GstSDPResult gst_sdp_message_set_version (GstSDPMessage *msg, gchar *version); +gchar* gst_sdp_message_get_version (GstSDPMessage *msg); +GstSDPResult gst_sdp_message_set_origin (GstSDPMessage *msg, gchar *username, gchar *sess_id, + gchar *sess_version, gchar *nettype, + gchar *addrtype, gchar *addr); +GstSDPOrigin* gst_sdp_message_get_origin (GstSDPMessage *msg); +GstSDPResult gst_sdp_message_set_session_name (GstSDPMessage *msg, gchar *session_name); +gchar* gst_sdp_message_get_session_name (GstSDPMessage *msg); +GstSDPResult gst_sdp_message_set_information (GstSDPMessage *msg, gchar *information); +gchar* gst_sdp_message_get_information (GstSDPMessage *msg); +GstSDPResult gst_sdp_message_set_uri (GstSDPMessage *msg, gchar *uri); +gchar* gst_sdp_message_get_uri (GstSDPMessage *msg); +gint gst_sdp_message_emails_len (GstSDPMessage *msg); +gchar* gst_sdp_message_get_email (GstSDPMessage *msg, guint idx); +GstSDPResult gst_sdp_message_add_email (GstSDPMessage *msg, gchar *email); +gint gst_sdp_message_phones_len (GstSDPMessage *msg); +gchar* gst_sdp_message_get_phone (GstSDPMessage *msg, guint idx); +GstSDPResult gst_sdp_message_add_phone (GstSDPMessage *msg, gchar *phone); +GstSDPResult gst_sdp_message_set_connection (GstSDPMessage *msg, gchar *nettype, gchar *addrtype, + gchar *address, gint ttl, gint addr_number); +GstSDPConnection* gst_sdp_message_get_connection (GstSDPMessage *msg); +gint gst_sdp_message_bandwidths_len (GstSDPMessage *msg); +GstSDPBandwidth* gst_sdp_message_get_bandwidth (GstSDPMessage *msg, guint idx); +GstSDPResult gst_sdp_message_add_bandwidth (GstSDPMessage *msg, gchar *bwtype, gint bandwidth); +gint gst_sdp_message_times_len (GstSDPMessage *msg); +GstSDPTime* gst_sdp_message_get_time (GstSDPMessage *msg, guint idx); +GstSDPResult gst_sdp_message_add_time (GstSDPMessage *msg, gchar *time); +gint gst_sdp_message_zones_len (GstSDPMessage *msg); +GstSDPZone* gst_sdp_message_get_zone (GstSDPMessage *msg, guint idx); +GstSDPResult gst_sdp_message_add_zone (GstSDPMessage *msg, gchar *time, gchar *typed_time); +GstSDPResult gst_sdp_message_set_key (GstSDPMessage *msg, gchar *type, gchar *data); +GstSDPKey* gst_sdp_message_get_key (GstSDPMessage *msg); +gint gst_sdp_message_attributes_len (GstSDPMessage *msg); +GstSDPAttribute* gst_sdp_message_get_attribute (GstSDPMessage *msg, guint idx); +gchar* gst_sdp_message_get_attribute_val (GstSDPMessage *msg, gchar *key); +gchar* gst_sdp_message_get_attribute_val_n (GstSDPMessage *msg, gchar *key, guint nth); +GstSDPResult gst_sdp_message_add_attribute (GstSDPMessage *msg, gchar *key, gchar *value); +gint gst_sdp_message_medias_len (GstSDPMessage *msg); +GstSDPMedia* gst_sdp_message_get_media (GstSDPMessage *msg, guint idx); +GstSDPResult gst_sdp_message_add_media (GstSDPMessage *msg, GstSDPMedia *media); + + +GstSDPResult gst_sdp_message_dump (GstSDPMessage *msg); + +/* Media descriptions */ +GstSDPResult gst_sdp_media_new (GstSDPMedia **media); +GstSDPResult gst_sdp_media_init (GstSDPMedia *media); +GstSDPResult gst_sdp_media_uninit (GstSDPMedia *media); +GstSDPResult gst_sdp_media_free (GstSDPMedia *media); + +GstSDPResult gst_sdp_media_add_bandwidth (GstSDPMedia * media, gchar * bwtype, gint bandwidth); + +GstSDPResult gst_sdp_media_add_attribute (GstSDPMedia *media, gchar * key, gchar * value); +GstSDPAttribute * gst_sdp_media_get_attribute (GstSDPMedia *media, guint idx); +gchar* gst_sdp_media_get_attribute_val (GstSDPMedia *media, gchar *key); +gchar* gst_sdp_media_get_attribute_val_n (GstSDPMedia *media, gchar *key, guint nth); + +GstSDPResult gst_sdp_media_add_format (GstSDPMedia * media, gchar * format); +gchar* gst_sdp_media_get_format (GstSDPMedia *media, guint idx); + +G_END_DECLS + +#endif /* __GST_SDP_MESSAGE_H__ */ diff --git a/pkgconfig/gstreamer-plugins-base-uninstalled.pc.in b/pkgconfig/gstreamer-plugins-base-uninstalled.pc.in index 2f098f6c5a..2431d4e241 100644 --- a/pkgconfig/gstreamer-plugins-base-uninstalled.pc.in +++ b/pkgconfig/gstreamer-plugins-base-uninstalled.pc.in @@ -9,7 +9,7 @@ Description: Streaming media framework, base plugins libraries, uninstalled Version: @VERSION@ Requires: gstreamer-@GST_MAJORMINOR@ -Libs: -L${libdir}/audio -L${libdir}/cdda -L${libdir}/floatcast -L${libdir}/interfaces -L${libdir}/netbuffer -L${libdir}/riff -L${libdir}/rtp -L${libdir}/tag -L${libdir}/pbutils -L${libdir}/video +Libs: -L${libdir}/audio -L${libdir}/cdda -L${libdir}/floatcast -L${libdir}/interfaces -L${libdir}/netbuffer -L${libdir}/riff -L${libdir}/rtp -L${libdir}/rtsp -L${libdir}/sdp -L${libdir}/tag -L${libdir}/pbutils -L${libdir}/video Cflags: -I${includedir} libraries=audio cdda floatcast interfaces netbuffer riff tag pbutils video