diff -r 000000000000 -r 0e761a78d257 gst_plugins_base/gst-libs/gst/sdp/gstsdpmessage.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/gst_plugins_base/gst-libs/gst/sdp/gstsdpmessage.c Thu Dec 17 08:53:32 2009 +0200 @@ -0,0 +1,1979 @@ +/* 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. + */ + +/** + * SECTION:gstsdpmessage + * @short_description: Helper methods for dealing with SDP messages + * + * + * + * The GstSDPMessage helper functions makes it easy to parse and create SDP + * messages. + * + * + * + * Last reviewed on 2007-07-24 (0.10.14) + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include + +#ifdef HAVE_SYS_TYPES_H +#include +#endif + +#include /* for G_OS_WIN32 */ +#include /* For GST_STR_NULL */ + +#ifdef G_OS_WIN32 +#ifdef _MSC_VER +#include +#endif +#include +#else +#include +#include +#include +#endif + +#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 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; i < (field)->len; 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 FREE_ARRAY(field) \ +G_STMT_START { \ + if (field) \ + g_array_free ((field), TRUE); \ + (field) = NULL; \ +} G_STMT_END + +#define INIT_PTR_ARRAY(field, type, init_func) \ +G_STMT_START { \ + if (field) { \ + guint i; \ + for(i = 0; i < (field)->len; 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 FREE_PTR_ARRAY(field) FREE_ARRAY(field) + +#define DEFINE_STRING_SETTER(field) \ +GstSDPResult gst_sdp_message_set_##field (GstSDPMessage *msg, const gchar *val) { \ + g_free (msg->field); \ + msg->field = g_strdup (val); \ + return GST_SDP_OK; \ +} +#define DEFINE_STRING_GETTER(field) \ +const gchar* gst_sdp_message_get_##field (const GstSDPMessage *msg) { \ + return msg->field; \ +} + +#define DEFINE_ARRAY_LEN(field) \ +guint gst_sdp_message_##field##_len (const GstSDPMessage *msg) { \ + return msg->field->len; \ +} +#define DEFINE_ARRAY_GETTER(method, field, type) \ +type * gst_sdp_message_get_##method (const GstSDPMessage *msg, guint idx) { \ + return &g_array_index (msg->field, type, idx); \ +} + +#define DEFINE_PTR_ARRAY_LEN(field) DEFINE_ARRAY_LEN(field) +#define DEFINE_PTR_ARRAY_GETTER(method, field, type) \ +type gst_sdp_message_get_##method (const GstSDPMessage *msg, guint idx) { \ + return g_array_index (msg->field, type, idx); \ +} +#define DEFINE_PTR_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; \ +} +#ifdef __SYMBIAN32__ +EXPORT_C +#endif + + +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 * t) +{ + FREE_STRING (t->start); + FREE_STRING (t->stop); + INIT_PTR_ARRAY (t->repeat, gchar *, g_free); + FREE_PTR_ARRAY (t->repeat); +} + +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: a #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_PTR_ARRAY (msg->emails, gchar *, g_free); + INIT_PTR_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: a #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_PTR_ARRAY (msg->emails); + FREE_PTR_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: a #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; +} + +static gboolean +is_multicast_address (const gchar * host_name, guint * family) +{ + struct addrinfo hints; + struct addrinfo *ai; + struct addrinfo *res; + gboolean ret = FALSE; + int err; + + memset (&hints, 0, sizeof (hints)); + hints.ai_socktype = SOCK_DGRAM; + + g_return_val_if_fail (host_name, FALSE); + + if ((err = getaddrinfo (host_name, NULL, &hints, &res)) < 0) + return FALSE; + + for (ai = res; !ret && ai; ai = ai->ai_next) { + if (ai->ai_family == AF_INET) + ret = + IN_MULTICAST (ntohl (((struct sockaddr_in *) ai->ai_addr)->sin_addr. + s_addr)); + else + ret = + IN6_IS_ADDR_MULTICAST (&((struct sockaddr_in6 *) ai->ai_addr)-> + sin6_addr); + if (ret && family) + *family = ai->ai_family; + } + + freeaddrinfo (res); + + return ret; +} + +/** + * gst_sdp_message_as_text: + * @msg: a #GstSDPMessage + * + * Convert the contents of @msg to a text string. + * + * Returns: A dynamically allocated string representing the SDP description. + */ +gchar * +gst_sdp_message_as_text (const GstSDPMessage * msg) +{ + /* change all vars so they match rfc? */ + GString *lines; + guint i; + + g_return_val_if_fail (msg != NULL, NULL); + + lines = g_string_new (""); + + if (msg->version) + g_string_append_printf (lines, "v=%s\r\n", msg->version); + + if (msg->origin.sess_id && msg->origin.sess_version && msg->origin.nettype && + msg->origin.addrtype && msg->origin.addr) + g_string_append_printf (lines, "o=%s %s %s %s %s %s\r\n", + msg->origin.username ? msg->origin.username : "-", msg->origin.sess_id, + msg->origin.sess_version, msg->origin.nettype, msg->origin.addrtype, + msg->origin.addr); + + if (msg->session_name) + g_string_append_printf (lines, "s=%s\r\n", msg->session_name); + + if (msg->information) + g_string_append_printf (lines, "i=%s\r\n", msg->information); + + if (msg->uri) + g_string_append_printf (lines, "u=%s\r\n", msg->uri); + + for (i = 0; i < gst_sdp_message_emails_len (msg); i++) + g_string_append_printf (lines, "e=%s\r\n", + gst_sdp_message_get_email (msg, i)); + + for (i = 0; i < gst_sdp_message_phones_len (msg); i++) + g_string_append_printf (lines, "p=%s\r\n", + gst_sdp_message_get_phone (msg, i)); + + if (gst_sdp_message_emails_len (msg) == 0 && + gst_sdp_message_phones_len (msg) == 0) + g_string_append_printf (lines, "e=NONE\r\n"); + + if (msg->connection.nettype && msg->connection.addrtype && + msg->connection.address) { + guint family; + + g_string_append_printf (lines, "c=%s %s %s", msg->connection.nettype, + msg->connection.addrtype, msg->connection.address); + if (is_multicast_address (msg->connection.address, &family)) { + if (family == AF_INET) + g_string_append_printf (lines, "/%u", msg->connection.ttl); + if (msg->connection.addr_number > 1) + g_string_append_printf (lines, "/%u", msg->connection.addr_number); + } + g_string_append_printf (lines, "\r\n"); + } + + for (i = 0; i < gst_sdp_message_bandwidths_len (msg); i++) { + const GstSDPBandwidth *bandwidth = gst_sdp_message_get_bandwidth (msg, i); + + g_string_append_printf (lines, "b=%s:%u\r\n", bandwidth->bwtype, + bandwidth->bandwidth); + } + + for (i = 0; i < gst_sdp_message_times_len (msg); i++) { + const GstSDPTime *times = gst_sdp_message_get_time (msg, i); + + g_string_append_printf (lines, "t=%s %s\r\n", times->start, times->stop); + + if (times->repeat != NULL) { + guint j; + + g_string_append_printf (lines, "r=%s", + g_array_index (times->repeat, gchar *, 0)); + for (j = 1; j < times->repeat->len; j++) + g_string_append_printf (lines, " %s", + g_array_index (times->repeat, gchar *, j)); + g_string_append_printf (lines, "\r\n"); + } + } + + if (gst_sdp_message_zones_len (msg) > 0) { + const GstSDPZone *zone = gst_sdp_message_get_zone (msg, 0); + + g_string_append_printf (lines, "z=%s %s", zone->time, zone->typed_time); + for (i = 1; i < gst_sdp_message_zones_len (msg); i++) { + zone = gst_sdp_message_get_zone (msg, i); + g_string_append_printf (lines, " %s %s", zone->time, zone->typed_time); + } + g_string_append_printf (lines, "\r\n"); + } + + if (msg->key.type) { + g_string_append_printf (lines, "k=%s", msg->key.type); + if (msg->key.data) + g_string_append_printf (lines, ":%s", msg->key.data); + g_string_append_printf (lines, "\r\n"); + } + + for (i = 0; i < gst_sdp_message_attributes_len (msg); i++) { + const GstSDPAttribute *attr = gst_sdp_message_get_attribute (msg, i); + + if (attr->key) { + g_string_append_printf (lines, "a=%s", attr->key); + if (attr->value) + g_string_append_printf (lines, ":%s", attr->value); + g_string_append_printf (lines, "\r\n"); + } + } + + for (i = 0; i < gst_sdp_message_medias_len (msg); i++) { + const GstSDPMedia *media = gst_sdp_message_get_media (msg, i); + gchar *sdp_media_str; + + sdp_media_str = gst_sdp_media_as_text (media); + g_string_append_printf (lines, "%s", sdp_media_str); + g_free (sdp_media_str); + } + + return g_string_free (lines, FALSE); +} + +/** + * gst_sdp_message_set_version: + * @msg: a #GstSDPMessage + * @version: the version + * + * Set the version in @msg. + * + * Returns: a #GstSDPResult. + */ +DEFINE_STRING_SETTER (version); +/** + * gst_sdp_message_get_version: + * @msg: a #GstSDPMessage + * + * Get the version in @msg. + * + * Returns: a #GstSDPResult. + */ +DEFINE_STRING_GETTER (version); + +/** + * gst_sdp_message_set_origin: + * @msg: a #GstSDPMessage + * @username: the user name + * @sess_id: a session id + * @sess_version: a session version + * @nettype: a network type + * @addrtype: an address type + * @addr: an address + * + * Configure the SDP origin in @msg with the given parameters. + * + * Returns: #GST_SDP_OK. + */ +GstSDPResult +gst_sdp_message_set_origin (GstSDPMessage * msg, const gchar * username, + const gchar * sess_id, const gchar * sess_version, const gchar * nettype, + const gchar * addrtype, const 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; +} + +/** + * gst_sdp_message_get_origin: + * @msg: a #GstSDPMessage + * + * Get the origin of @msg. + * + * Returns: a #GstSDPOrigin. The result remains valid as long as @msg is valid. + */ +const GstSDPOrigin * +gst_sdp_message_get_origin (const GstSDPMessage * msg) +{ + return &msg->origin; +} + +/** + * gst_sdp_message_set_session_name: + * @msg: a #GstSDPMessage + * @session_name: the session name + * + * Set the session name in @msg. + * + * Returns: a #GstSDPResult. + */ +DEFINE_STRING_SETTER (session_name); +/** + * gst_sdp_message_get_session_name: + * @msg: a #GstSDPMessage + * + * Get the session name in @msg. + * + * Returns: a #GstSDPResult. + */ +DEFINE_STRING_GETTER (session_name); +/** + * gst_sdp_message_set_information: + * @msg: a #GstSDPMessage + * @information: the information + * + * Set the information in @msg. + * + * Returns: a #GstSDPResult. + */ +DEFINE_STRING_SETTER (information); +/** + * gst_sdp_message_get_information: + * @msg: a #GstSDPMessage + * + * Get the information in @msg. + * + * Returns: a #GstSDPResult. + */ +DEFINE_STRING_GETTER (information); +/** + * gst_sdp_message_set_uri: + * @msg: a #GstSDPMessage + * @uri: the URI + * + * Set the URI in @msg. + * + * Returns: a #GstSDPResult. + */ +DEFINE_STRING_SETTER (uri); +/** + * gst_sdp_message_get_uri: + * @msg: a #GstSDPMessage + * + * Get the URI in @msg. + * + * Returns: a #GstSDPResult. + */ +DEFINE_STRING_GETTER (uri); + +/** + * gst_sdp_message_emails_len: + * @msg: a #GstSDPMessage + * + * Get the number of emails in @msg. + * + * Returns: the number of emails in @msg. + */ +DEFINE_ARRAY_LEN (emails); +/** + * gst_sdp_message_get_email: + * @msg: a #GstSDPMessage + * @idx: an email index + * + * Get the email with number @idx from @msg. + * + * Returns: the email at position @idx. + */ +DEFINE_PTR_ARRAY_GETTER (email, emails, const gchar *); + +/** + * gst_sdp_message_add_email: + * @msg: a #GstSDPMessage + * @email: an email + * + * Add @email to the list of emails in @msg. + * + * Returns: a #GstSDPResult. + */ +DEFINE_PTR_ARRAY_ADDER (email, emails, const gchar *, g_strdup); + +/** + * gst_sdp_message_phones_len: + * @msg: a #GstSDPMessage + * + * Get the number of phones in @msg. + * + * Returns: the number of phones in @msg. + */ +DEFINE_ARRAY_LEN (phones); +/** + * gst_sdp_message_get_phone: + * @msg: a #GstSDPMessage + * @idx: a phone index + * + * Get the phone with number @idx from @msg. + * + * Returns: the phone at position @idx. + */ +DEFINE_PTR_ARRAY_GETTER (phone, phones, const gchar *); + +/** + * gst_sdp_message_add_phone: + * @msg: a #GstSDPMessage + * @phone: a phone + * + * Add @phone to the list of phones in @msg. + * + * Returns: a #GstSDPResult. + */ +DEFINE_PTR_ARRAY_ADDER (phone, phones, const gchar *, g_strdup); + +/** + * gst_sdp_message_set_connection: + * @msg: a #GstSDPMessage + * @nettype: the type of network. "IN" is defined to have the meaning + * "Internet". + * @addrtype: the type of address. + * @address: the address + * @ttl: the time to live of the address + * @addr_number: the number of layers + * + * Configure the SDP connection in @msg with the given parameters. + * + * Returns: a #GstSDPResult. + */ +GstSDPResult +gst_sdp_message_set_connection (GstSDPMessage * msg, const gchar * nettype, + const gchar * addrtype, const gchar * address, guint ttl, guint 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; +} + +/** + * gst_sdp_message_get_connection: + * @msg: a #GstSDPMessage + * + * Get the connection of @msg. + * + * Returns: a #GstSDPConnection. The result remains valid as long as @msg is valid. + */ +const GstSDPConnection * +gst_sdp_message_get_connection (const GstSDPMessage * msg) +{ + return &msg->connection; +} + +/** + * gst_sdp_message_bandwidths_len: + * @msg: a #GstSDPMessage + * + * Get the number of bandwidth information in @msg. + * + * Returns: the number of bandwidth information in @msg. + */ +DEFINE_ARRAY_LEN (bandwidths); +/** + * gst_sdp_message_get_bandwidth: + * @msg: a #GstSDPMessage + * @idx: the bandwidth index + * + * Get the bandwidth at index @idx from @msg. + * + * Returns: a #GstSDPBandwidth. + */ +DEFINE_ARRAY_GETTER (bandwidth, bandwidths, const GstSDPBandwidth); + +/** + * gst_sdp_message_add_bandwidth: + * @msg: a #GstSDPMessage + * @bwtype: the bandwidth modifier type + * @bandwidth: the bandwidth in kilobits per second + * + * Add the specified bandwidth information to @msg. + * + * Returns: a #GstSDPResult. + */ + +GstSDPResult +gst_sdp_message_add_bandwidth (GstSDPMessage * msg, const gchar * bwtype, + guint bandwidth) +{ + GstSDPBandwidth bw; + + bw.bwtype = g_strdup (bwtype); + bw.bandwidth = bandwidth; + + g_array_append_val (msg->bandwidths, bw); + + return GST_SDP_OK; +} + +/** + * gst_sdp_message_times_len: + * @msg: a #GstSDPMessage + * + * Get the number of time information entries in @msg. + * + * Returns: the number of time information entries in @msg. + */ +DEFINE_ARRAY_LEN (times); + +/** + * gst_sdp_message_get_time: + * @msg: a #GstSDPMessage + * @idx: the time index + * + * Get time information with index @idx from @msg. + * + * Returns: a #GstSDPTime. + */ +DEFINE_ARRAY_GETTER (time, times, const GstSDPTime); + +/** + * gst_sdp_message_add_time: + * @msg: a #GstSDPMessage + * @start: the start time + * @stop: the stop time + * @repeat: the repeat times + * + * Add time information @start and @stop to @msg. + * + * Returns: a #GstSDPResult. + */ +GstSDPResult +gst_sdp_message_add_time (GstSDPMessage * msg, const gchar * start, + const gchar * stop, const gchar ** repeat) +{ + GstSDPTime times; + + times.start = g_strdup (start); + times.stop = g_strdup (stop); + if (repeat) { + times.repeat = g_array_new (FALSE, TRUE, sizeof (gchar *)); + for (; *repeat; repeat++) { + gchar *r = g_strdup (*repeat); + + g_array_append_val (times.repeat, r); + } + } else + times.repeat = NULL; + g_array_append_val (msg->times, times); + + return GST_SDP_OK; +} + +/** + * gst_sdp_message_zones_len: + * @msg: a #GstSDPMessage + * + * Get the number of time zone information entries in @msg. + * + * Returns: the number of time zone information entries in @msg. + */ +DEFINE_ARRAY_LEN (zones); +/** + * gst_sdp_message_get_zone: + * @msg: a #GstSDPMessage + * @idx: the zone index + * + * Get time zone information with index @idx from @msg. + * + * Returns: a #GstSDPZone. + */ +DEFINE_ARRAY_GETTER (zone, zones, const GstSDPZone); + +/** + * gst_sdp_message_add_zone: + * @msg: a #GstSDPMessage + * @adj_time: the NTP time that a time zone adjustment happens + * @typed_time: the offset from the time when the session was first scheduled + * + * Add time zone information to @msg. + * + * Returns: a #GstSDPResult. + */ +GstSDPResult +gst_sdp_message_add_zone (GstSDPMessage * msg, const gchar * adj_time, + const gchar * typed_time) +{ + GstSDPZone zone; + + zone.time = g_strdup (adj_time); + zone.typed_time = g_strdup (typed_time); + + g_array_append_val (msg->zones, zone); + + return GST_SDP_OK; +} + +/** + * gst_sdp_message_set_key: + * @msg: a #GstSDPMessage + * @type: the encryption type + * @data: the encryption data + * + * Adds the encryption information to @msg. + * + * Returns: a #GstSDPResult. + */ +GstSDPResult +gst_sdp_message_set_key (GstSDPMessage * msg, const gchar * type, + const gchar * data) +{ + REPLACE_STRING (msg->key.type, type); + REPLACE_STRING (msg->key.data, data); + + return GST_SDP_OK; +} + +/** + * gst_sdp_message_get_key: + * @msg: a #GstSDPMessage + * + * Get the encryption information from @msg. + * + * Returns: a #GstSDPKey. + */ +const GstSDPKey * +gst_sdp_message_get_key (const GstSDPMessage * msg) +{ + return &msg->key; +} + +/** + * gst_sdp_message_attributes_len: + * @msg: a #GstSDPMessage + * + * Get the number of attributes in @msg. + * + * Returns: the number of attributes in @msg. + */ +DEFINE_ARRAY_LEN (attributes); + +/** + * gst_sdp_message_get_attribute: + * @msg: a #GstSDPMessage + * @idx: the index + * + * Get the attribute at position @idx in @msg. + * + * Returns: the #GstSDPAttribute at position @idx. + */ +DEFINE_ARRAY_GETTER (attribute, attributes, const GstSDPAttribute); + +/** + * gst_sdp_message_get_attribute_val_n: + * @msg: a #GstSDPMessage + * @key: the key + * @nth: the index + * + * Get the @nth attribute with key @key in @msg. + * + * Returns: the attribute value of the @nth attribute with @key. + */ +const gchar * +gst_sdp_message_get_attribute_val_n (const GstSDPMessage * msg, + const 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; +} + +/** + * gst_sdp_message_get_attribute_val: + * @msg: a #GstSDPMessage + * @key: the key + * + * Get the first attribute with key @key in @msg. + * + * Returns: the attribute value of the first attribute with @key. + */ +const gchar * +gst_sdp_message_get_attribute_val (const GstSDPMessage * msg, const gchar * key) +{ + return gst_sdp_message_get_attribute_val_n (msg, key, 0); +} + +/** + * gst_sdp_message_add_attribute: + * @msg: a #GstSDPMessage + * @key: the key + * @value: the value + * + * Add the attribute with @key and @value to @msg. + * + * Returns: @GST_SDP_OK. + */ +GstSDPResult +gst_sdp_message_add_attribute (GstSDPMessage * msg, const gchar * key, + const 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; +} + +/** + * gst_sdp_message_medias_len: + * @msg: a #GstSDPMessage + * + * Get the number of media descriptions in @msg. + * + * Returns: the number of media descriptions in @msg. + */ +DEFINE_ARRAY_LEN (medias); +/** + * gst_sdp_message_get_media: + * @msg: a #GstSDPMessage + * @idx: the index + * + * Get the media description at index @idx in @msg. + * + * Returns: a #GstSDPMedia. + */ +DEFINE_ARRAY_GETTER (media, medias, const GstSDPMedia); + +/** + * gst_sdp_message_add_media: + * @msg: a #GstSDPMessage + * @media: a #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: a #GstSDPResult. + */ +GstSDPResult +gst_sdp_message_add_media (GstSDPMessage * msg, GstSDPMedia * media) +{ + guint 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 */ + +/** + * 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_PTR_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: a #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_PTR_ARRAY (media->fmts); + FREE_ARRAY (media->connections); + FREE_ARRAY (media->bandwidths); + FREE_ARRAY (media->attributes); + + return GST_SDP_OK; +} + +/** + * gst_sdp_media_free: + * @media: a #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; +} + +/** + * gst_sdp_media_as_text: + * @media: a #GstSDPMedia + * + * Convert the contents of @media to a text string. + * + * Returns: A dynamically allocated string representing the media. + */ +gchar * +gst_sdp_media_as_text (const GstSDPMedia * media) +{ + GString *lines; + guint i; + + g_return_val_if_fail (media != NULL, NULL); + + lines = g_string_new (""); + + if (media->media) + g_string_append_printf (lines, "m=%s", media->media); + + g_string_append_printf (lines, " %u", media->port); + + if (media->num_ports > 1) + g_string_append_printf (lines, "/%u", media->num_ports); + + g_string_append_printf (lines, " %s", media->proto); + + for (i = 0; i < gst_sdp_media_formats_len (media); i++) + g_string_append_printf (lines, " %s", gst_sdp_media_get_format (media, i)); + g_string_append_printf (lines, "\r\n"); + + if (media->information) + g_string_append_printf (lines, "i=%s", media->information); + + for (i = 0; i < gst_sdp_media_connections_len (media); i++) { + const GstSDPConnection *conn = gst_sdp_media_get_connection (media, i); + + if (conn->nettype && conn->addrtype && conn->address) { + guint family; + + g_string_append_printf (lines, "c=%s %s %s", conn->nettype, + conn->addrtype, conn->address); + if (is_multicast_address (conn->address, &family)) { + if (family == AF_INET) + g_string_append_printf (lines, "/%u", conn->ttl); + if (conn->addr_number > 1) + g_string_append_printf (lines, "/%u", conn->addr_number); + } + g_string_append_printf (lines, "\r\n"); + } + } + + for (i = 0; i < gst_sdp_media_bandwidths_len (media); i++) { + const GstSDPBandwidth *bandwidth = gst_sdp_media_get_bandwidth (media, i); + + g_string_append_printf (lines, "b=%s:%u\r\n", bandwidth->bwtype, + bandwidth->bandwidth); + } + + if (media->key.type) { + g_string_append_printf (lines, "k=%s", media->key.type); + if (media->key.data) + g_string_append_printf (lines, ":%s", media->key.data); + g_string_append_printf (lines, "\r\n"); + } + + for (i = 0; i < gst_sdp_media_attributes_len (media); i++) { + const GstSDPAttribute *attr = gst_sdp_media_get_attribute (media, i); + + if (attr->key) { + g_string_append_printf (lines, "a=%s", attr->key); + if (attr->value) + g_string_append_printf (lines, ":%s", attr->value); + g_string_append_printf (lines, "\r\n"); + } + } + + return g_string_free (lines, FALSE); +} + +/** + * gst_sdp_media_get_media: + * @media: a #GstSDPMedia + * + * Get the media description of @media. + * + * Returns: the media description. + */ +const gchar * +gst_sdp_media_get_media (const GstSDPMedia * media) +{ + return media->media; +} + +/** + * gst_sdp_media_set_media: + * @media: a #GstSDPMedia + * @med: the media description + * + * Set the media description of @media to @med. + * + * Returns: #GST_SDP_OK. + */ +GstSDPResult +gst_sdp_media_set_media (GstSDPMedia * media, const gchar * med) +{ + g_free (media->media); + media->media = g_strdup (med); + + return GST_SDP_OK; +} + +/** + * gst_sdp_media_get_port: + * @media: a #GstSDPMedia + * + * Get the port number for @media. + * + * Returns: the port number of @media. + */ +guint +gst_sdp_media_get_port (const GstSDPMedia * media) +{ + return media->port; +} + +/** + * gst_sdp_media_get_num_ports: + * @media: a #GstSDPMedia + * + * Get the number of ports for @media. + * + * Returns: the number of ports for @media. + */ +guint +gst_sdp_media_get_num_ports (const GstSDPMedia * media) +{ + return media->num_ports; +} + +/** + * gst_sdp_media_set_port_info: + * @media: a #GstSDPMedia + * @port: the port number + * @num_ports: the number of ports + * + * Set the port information in @media. + * + * Returns: #GST_SDP_OK. + */ +GstSDPResult +gst_sdp_media_set_port_info (GstSDPMedia * media, guint port, guint num_ports) +{ + media->port = port; + media->num_ports = num_ports; + + return GST_SDP_OK; +} + +/** + * gst_sdp_media_get_proto: + * @media: a #GstSDPMedia + * + * Get the transport protocol of @media + * + * Returns: the transport protocol of @media. + */ +const gchar * +gst_sdp_media_get_proto (const GstSDPMedia * media) +{ + return media->proto; +} + +/** + * gst_sdp_media_set_proto: + * @media: a #GstSDPMedia + * @proto: the media transport protocol + * + * Set the media transport protocol of @media to @proto. + * + * Returns: #GST_SDP_OK. + */ +GstSDPResult +gst_sdp_media_set_proto (GstSDPMedia * media, const gchar * proto) +{ + g_free (media->proto); + media->proto = g_strdup (proto); + + return GST_SDP_OK; +} + +/** + * gst_sdp_media_formats_len: + * @media: a #GstSDPMedia + * + * Get the number of formats in @media. + * + * Returns: the number of formats in @media. + */ +guint +gst_sdp_media_formats_len (const GstSDPMedia * media) +{ + return media->fmts->len; +} + +/** + * gst_sdp_media_get_format: + * @media: a #GstSDPMedia + * @idx: an index + * + * Get the format information at position @idx in @media. + * + * Returns: the format at position @idx. + */ +const gchar * +gst_sdp_media_get_format (const GstSDPMedia * media, guint idx) +{ + if (idx >= media->fmts->len) + return NULL; + return g_array_index (media->fmts, gchar *, idx); +} + +/** + * gst_sdp_media_add_format: + * @media: a #GstSDPMedia + * @format: the format + * + * Add the format information to @media. + * + * Returns: #GST_SDP_OK. + */ +GstSDPResult +gst_sdp_media_add_format (GstSDPMedia * media, const gchar * format) +{ + gchar *fmt; + + fmt = g_strdup (format); + + g_array_append_val (media->fmts, fmt); + + return GST_SDP_OK; +} + +/** + * gst_sdp_media_get_information: + * @media: a #GstSDPMedia + * + * Get the information of @media + * + * Returns: the information of @media. + */ +const gchar * +gst_sdp_media_get_information (const GstSDPMedia * media) +{ + return media->information; +} + +/** + * gst_sdp_media_set_information: + * @media: a #GstSDPMedia + * @information: the media information + * + * Set the media information of @media to @information. + * + * Returns: #GST_SDP_OK. + */ +GstSDPResult +gst_sdp_media_set_information (GstSDPMedia * media, const gchar * information) +{ + g_free (media->information); + media->information = g_strdup (information); + + return GST_SDP_OK; +} + +/** + * gst_sdp_media_connections_len: + * @media: a #GstSDPMedia + * + * Get the number of connection fields in @media. + * + * Returns: the number of connections in @media. + */ +guint +gst_sdp_media_connections_len (const GstSDPMedia * media) +{ + return media->connections->len; +} + +/** + * gst_sdp_media_get_connection: + * @media: a #GstSDPMedia + * @idx: an index + * + * Get the connection at position @idx in @media. + * + * Returns: the #GstSDPConnection at position @idx. + */ +const GstSDPConnection * +gst_sdp_media_get_connection (const GstSDPMedia * media, guint idx) +{ + return &g_array_index (media->connections, GstSDPConnection, idx); +} + +/** + * gst_sdp_media_add_connection: + * @media: a #GstSDPMedia + * @nettype: the type of network. "IN" is defined to have the meaning + * "Internet". + * @addrtype: the type of address. + * @address: the address + * @ttl: the time to live of the address + * @addr_number: the number of layers + * + * Add the given connection parameters to @media. + * + * Returns: a #GstSDPResult. + */ +GstSDPResult +gst_sdp_media_add_connection (GstSDPMedia * media, const gchar * nettype, + const gchar * addrtype, const gchar * address, guint ttl, guint addr_number) +{ + GstSDPConnection conn; + + conn.nettype = g_strdup (nettype); + conn.addrtype = g_strdup (addrtype); + conn.address = g_strdup (address); + conn.ttl = ttl; + conn.addr_number = addr_number; + + g_array_append_val (media->connections, conn); + + return GST_SDP_OK; +} + +/** + * gst_sdp_media_bandwidths_len: + * @media: a #GstSDPMedia + * + * Get the number of bandwidth fields in @media. + * + * Returns: the number of bandwidths in @media. + */ +guint +gst_sdp_media_bandwidths_len (const GstSDPMedia * media) +{ + return media->bandwidths->len; +} + +/** + * gst_sdp_media_get_bandwidth: + * @media: a #GstSDPMedia + * @idx: an index + * + * Get the bandwidth at position @idx in @media. + * + * Returns: the #GstSDPBandwidth at position @idx. + */ +const GstSDPBandwidth * +gst_sdp_media_get_bandwidth (const GstSDPMedia * media, guint idx) +{ + return &g_array_index (media->bandwidths, GstSDPBandwidth, idx); +} + +/** + * gst_sdp_media_add_bandwidth: + * @media: a #GstSDPMedia + * @bwtype: the bandwidth modifier type + * @bandwidth: the bandwidth in kilobits per second + * + * Add the bandwidth information with @bwtype and @bandwidth to @media. + * + * Returns: #GST_SDP_OK. + */ +GstSDPResult +gst_sdp_media_add_bandwidth (GstSDPMedia * media, const gchar * bwtype, + guint bandwidth) +{ + GstSDPBandwidth bw; + + bw.bwtype = g_strdup (bwtype); + bw.bandwidth = bandwidth; + + g_array_append_val (media->bandwidths, bw); + + return GST_SDP_OK; +} + +/** + * gst_sdp_media_set_key: + * @media: a #GstSDPMedia + * @type: the encryption type + * @data: the encryption data + * + * Adds the encryption information to @media. + * + * Returns: a #GstSDPResult. + */ +GstSDPResult +gst_sdp_media_set_key (GstSDPMedia * media, const gchar * type, + const gchar * data) +{ + g_free (media->key.type); + media->key.type = g_strdup (type); + g_free (media->key.data); + media->key.data = g_strdup (data); + + return GST_SDP_OK; +} + +/** + * gst_sdp_media_get_key: + * @media: a #GstSDPMedia + * + * Get the encryption information from @media. + * + * Returns: a #GstSDPKey. + */ +const GstSDPKey * +gst_sdp_media_get_key (const GstSDPMedia * media) +{ + return &media->key; +} + +/** + * gst_sdp_media_attributes_len: + * @media: a #GstSDPMedia + * + * Get the number of attribute fields in @media. + * + * Returns: the number of attributes in @media. + */ +guint +gst_sdp_media_attributes_len (const GstSDPMedia * media) +{ + return media->attributes->len; +} + +/** + * gst_sdp_media_add_attribute: + * @media: a #GstSDPMedia + * @key: a key + * @value: a value + * + * Add the attribute with @key and @value to @media. + * + * Returns: #GST_SDP_OK. + */ +GstSDPResult +gst_sdp_media_add_attribute (GstSDPMedia * media, const gchar * key, + const 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; +} + +/** + * gst_sdp_media_get_attribute: + * @media: a #GstSDPMedia + * @idx: an index + * + * Get the attribute at position @idx in @media. + * + * Returns: the #GstSDPAttribute at position @idx. + */ +const GstSDPAttribute * +gst_sdp_media_get_attribute (const GstSDPMedia * media, guint idx) +{ + return &g_array_index (media->attributes, GstSDPAttribute, idx); +} + +/** + * gst_sdp_media_get_attribute_val_n: + * @media: a #GstSDPMedia + * @key: a key + * @nth: an index + * + * Get the @nth attribute value for @key in @media. + * + * Returns: the @nth attribute value. + */ +const gchar * +gst_sdp_media_get_attribute_val_n (const GstSDPMedia * media, const 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; +} + +/** + * gst_sdp_media_get_attribute_val: + * @media: a #GstSDPMedia + * @key: a key + * + * Get the first attribute value for @key in @media. + * + * Returns: the first attribute value for @key. + */ +const gchar * +gst_sdp_media_get_attribute_val (const GstSDPMedia * media, const gchar * key) +{ + return gst_sdp_media_get_attribute_val_n (media, key, 0); +} + +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 +{ + guint 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_UINT(field) read_string (str, sizeof (str), &p); field = strtoul (str, NULL, 10) + + 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_UINT (c->msg->connection.ttl); + READ_UINT (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); + + /* m= / ... */ + 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; +} + +/** + * gst_sdp_message_parse_buffer: + * @data: the start of the buffer + * @size: the size of the buffer + * @msg: the result #GstSDPMessage + * + * Parse the contents of @size bytes pointed to by @data and store the result in + * @msg. + * + * Returns: #GST_SDP_OK on success. + */ +GstSDPResult +gst_sdp_message_parse_buffer (const 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: '%u'\n", media->port); + g_print (" num_ports: '%u'\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); + } + } +} + +/** + * gst_sdp_message_dump: + * @msg: a #GstSDPMessage + * + * Dump the parsed contents of @msg to stdout. + * + * Returns: a #GstSDPResult. + */ +GstSDPResult +gst_sdp_message_dump (const GstSDPMessage * msg) +{ + g_return_val_if_fail (msg != NULL, GST_SDP_EINVAL); + + g_print ("sdp packet %p:\n", msg); + g_print (" version: '%s'\n", GST_STR_NULL (msg->version)); + g_print (" origin:\n"); + g_print (" username: '%s'\n", GST_STR_NULL (msg->origin.username)); + g_print (" sess_id: '%s'\n", GST_STR_NULL (msg->origin.sess_id)); + g_print (" sess_version: '%s'\n", GST_STR_NULL (msg->origin.sess_version)); + g_print (" nettype: '%s'\n", GST_STR_NULL (msg->origin.nettype)); + g_print (" addrtype: '%s'\n", GST_STR_NULL (msg->origin.addrtype)); + g_print (" addr: '%s'\n", GST_STR_NULL (msg->origin.addr)); + g_print (" session_name: '%s'\n", GST_STR_NULL (msg->session_name)); + g_print (" information: '%s'\n", GST_STR_NULL (msg->information)); + g_print (" uri: '%s'\n", GST_STR_NULL (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", GST_STR_NULL (msg->connection.nettype)); + g_print (" addrtype: '%s'\n", GST_STR_NULL (msg->connection.addrtype)); + g_print (" address: '%s'\n", GST_STR_NULL (msg->connection.address)); + g_print (" ttl: '%u'\n", msg->connection.ttl); + g_print (" addr_number: '%u'\n", msg->connection.addr_number); + g_print (" key:\n"); + g_print (" type: '%s'\n", GST_STR_NULL (msg->key.type)); + g_print (" data: '%s'\n", GST_STR_NULL (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 %u:\n", i); + print_media (&g_array_index (msg->medias, GstSDPMedia, i)); + } + } + return GST_SDP_OK; +}