| /* GStreamer |
| * Copyright (C) <2005,2006> Wim Taymans <wim@fluendo.com> |
| * |
| * This library is free software; you can redistribute it and/or |
| * modify it under the terms of the GNU Library General Public |
| * License as published by the Free Software Foundation; either |
| * version 2 of the License, or (at your option) any later version. |
| * |
| * This library is distributed in the hope that it will be useful, |
| * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
| * Library General Public License for more details. |
| * |
| * You should have received a copy of the GNU Library General Public |
| * License along with this library; if not, write to the |
| * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, |
| * Boston, MA 02110-1301, 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 |
| * @title: GstSDPMessage |
| * @short_description: Helper methods for dealing with SDP messages |
| * |
| * The GstSDPMessage helper functions makes it easy to parse and create SDP |
| * messages. |
| * |
| */ |
| |
| #ifdef HAVE_CONFIG_H |
| #include "config.h" |
| #endif |
| |
| #include <stdio.h> |
| #include <stdlib.h> |
| #include <string.h> |
| |
| #include <gio/gio.h> |
| |
| #include <gst/rtp/gstrtppayloads.h> |
| #include "gstsdpmessage.h" |
| |
| #define FREE_STRING(field) g_free (field); (field) = NULL |
| #define REPLACE_STRING(field, val) FREE_STRING(field); (field) = g_strdup (val) |
| |
| static void |
| free_string (gchar ** str) |
| { |
| FREE_STRING (*str); |
| } |
| |
| #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 DEFINE_STRING_SETTER(field) \ |
| GstSDPResult gst_sdp_message_set_##field (GstSDPMessage *msg, const gchar *val) { \ |
| g_return_val_if_fail (msg != NULL, GST_SDP_EINVAL); \ |
| 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) { \ |
| g_return_val_if_fail (msg != NULL, NULL); \ |
| return msg->field; \ |
| } |
| |
| #define DEFINE_ARRAY_LEN(field) \ |
| guint gst_sdp_message_##field##_len (const GstSDPMessage *msg) { \ |
| g_return_val_if_fail (msg != NULL, 0); \ |
| return msg->field->len; \ |
| } |
| #define DEFINE_ARRAY_GETTER(method, field, type) \ |
| const type * gst_sdp_message_get_##method (const GstSDPMessage *msg, guint idx) { \ |
| g_return_val_if_fail (msg != NULL, NULL); \ |
| return &g_array_index (msg->field, type, idx); \ |
| } |
| #define DEFINE_PTR_ARRAY_GETTER(method, field, type) \ |
| const type gst_sdp_message_get_##method (const GstSDPMessage *msg, guint idx) { \ |
| g_return_val_if_fail (msg != NULL, (type) 0); \ |
| return g_array_index (msg->field, type, idx); \ |
| } |
| #define DEFINE_ARRAY_INSERT(method, field, intype, dup_method, type) \ |
| GstSDPResult gst_sdp_message_insert_##method (GstSDPMessage *msg, gint idx, intype val) { \ |
| type vt; \ |
| type* v = &vt; \ |
| g_return_val_if_fail (msg != NULL, GST_SDP_EINVAL); \ |
| dup_method (v, val); \ |
| if (idx == -1) \ |
| g_array_append_val (msg->field, vt); \ |
| else \ |
| g_array_insert_val (msg->field, idx, vt); \ |
| return GST_SDP_OK; \ |
| } |
| |
| #define DEFINE_ARRAY_REPLACE(method, field, intype, free_method, dup_method, type) \ |
| GstSDPResult gst_sdp_message_replace_##method (GstSDPMessage *msg, guint idx, intype val) { \ |
| type *v; \ |
| g_return_val_if_fail (msg != NULL, GST_SDP_EINVAL); \ |
| v = &g_array_index (msg->field, type, idx); \ |
| free_method (v); \ |
| dup_method (v, val); \ |
| return GST_SDP_OK; \ |
| } |
| #define DEFINE_ARRAY_REMOVE(method, field, type, free_method) \ |
| GstSDPResult gst_sdp_message_remove_##method (GstSDPMessage *msg, guint idx) { \ |
| type *v; \ |
| g_return_val_if_fail (msg != NULL, GST_SDP_EINVAL); \ |
| v = &g_array_index (msg->field, type, idx); \ |
| free_method (v); \ |
| g_array_remove_index (msg->field, idx); \ |
| return GST_SDP_OK; \ |
| } |
| #define DEFINE_ARRAY_ADDER(method, type) \ |
| GstSDPResult gst_sdp_message_add_##method (GstSDPMessage *msg, const type val) { \ |
| g_return_val_if_fail (msg != NULL, GST_SDP_EINVAL); \ |
| return gst_sdp_message_insert_##method (msg, -1, val); \ |
| } |
| |
| #define dup_string(v,val) ((*v) = g_strdup (val)) |
| #define INIT_STR_ARRAY(field) \ |
| INIT_ARRAY (field, gchar *, free_string) |
| #define DEFINE_STR_ARRAY_GETTER(method, field) \ |
| DEFINE_PTR_ARRAY_GETTER(method, field, gchar *) |
| #define DEFINE_STR_ARRAY_INSERT(method, field) \ |
| DEFINE_ARRAY_INSERT (method, field, const gchar *, dup_string, gchar *) |
| #define DEFINE_STR_ARRAY_ADDER(method, field) \ |
| DEFINE_ARRAY_ADDER (method, gchar *) |
| #define DEFINE_STR_ARRAY_REPLACE(method, field) \ |
| DEFINE_ARRAY_REPLACE (method, field, const gchar *, free_string, dup_string, gchar *) |
| #define DEFINE_STR_ARRAY_REMOVE(method, field) \ |
| DEFINE_ARRAY_REMOVE (method, field, gchar *, free_string) |
| |
| static GstSDPMessage *gst_sdp_message_boxed_copy (GstSDPMessage * orig); |
| static void gst_sdp_message_boxed_free (GstSDPMessage * msg); |
| |
| G_DEFINE_BOXED_TYPE (GstSDPMessage, gst_sdp_message, gst_sdp_message_boxed_copy, |
| gst_sdp_message_boxed_free); |
| |
| static GstSDPMessage * |
| gst_sdp_message_boxed_copy (GstSDPMessage * orig) |
| { |
| GstSDPMessage *copy; |
| |
| if (!orig) |
| return NULL; |
| |
| if (gst_sdp_message_copy (orig, ©) == GST_SDP_OK) |
| return copy; |
| |
| return NULL; |
| } |
| |
| static void |
| gst_sdp_message_boxed_free (GstSDPMessage * msg) |
| { |
| gst_sdp_message_free (msg); |
| } |
| |
| 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_key_init (GstSDPKey * key) |
| { |
| FREE_STRING (key->type); |
| FREE_STRING (key->data); |
| } |
| |
| /** |
| * gst_sdp_message_new: |
| * @msg: (out) (transfer full): 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_STR_ARRAY (msg->emails); |
| INIT_STR_ARRAY (msg->phones); |
| gst_sdp_connection_clear (&msg->connection); |
| INIT_ARRAY (msg->bandwidths, GstSDPBandwidth, gst_sdp_bandwidth_clear); |
| INIT_ARRAY (msg->times, GstSDPTime, gst_sdp_time_clear); |
| INIT_ARRAY (msg->zones, GstSDPZone, gst_sdp_zone_clear); |
| gst_sdp_key_init (&msg->key); |
| INIT_ARRAY (msg->attributes, GstSDPAttribute, gst_sdp_attribute_clear); |
| 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_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_copy: |
| * @msg: a #GstSDPMessage |
| * @copy: (out) (transfer full): pointer to new #GstSDPMessage |
| * |
| * Allocate a new copy of @msg and store the result in @copy. The value in |
| * @copy should be release with gst_sdp_message_free function. |
| * |
| * Returns: a #GstSDPResult |
| * |
| * Since: 1.2 |
| */ |
| GstSDPResult |
| gst_sdp_message_copy (const GstSDPMessage * msg, GstSDPMessage ** copy) |
| { |
| GstSDPResult ret; |
| GstSDPMessage *cp; |
| guint i, len; |
| |
| if (msg == NULL) |
| return GST_SDP_EINVAL; |
| |
| ret = gst_sdp_message_new (copy); |
| if (ret != GST_SDP_OK) |
| return ret; |
| |
| cp = *copy; |
| |
| REPLACE_STRING (cp->version, msg->version); |
| gst_sdp_message_set_origin (cp, msg->origin.username, msg->origin.sess_id, |
| msg->origin.sess_version, msg->origin.nettype, msg->origin.addrtype, |
| msg->origin.addr); |
| REPLACE_STRING (cp->session_name, msg->session_name); |
| REPLACE_STRING (cp->information, msg->information); |
| REPLACE_STRING (cp->uri, msg->uri); |
| |
| len = gst_sdp_message_emails_len (msg); |
| for (i = 0; i < len; i++) { |
| gst_sdp_message_add_email (cp, gst_sdp_message_get_email (msg, i)); |
| } |
| |
| len = gst_sdp_message_phones_len (msg); |
| for (i = 0; i < len; i++) { |
| gst_sdp_message_add_phone (cp, gst_sdp_message_get_phone (msg, i)); |
| } |
| |
| gst_sdp_message_set_connection (cp, msg->connection.nettype, |
| msg->connection.addrtype, msg->connection.address, msg->connection.ttl, |
| msg->connection.addr_number); |
| |
| len = gst_sdp_message_bandwidths_len (msg); |
| for (i = 0; i < len; i++) { |
| const GstSDPBandwidth *bw = gst_sdp_message_get_bandwidth (msg, i); |
| gst_sdp_message_add_bandwidth (cp, bw->bwtype, bw->bandwidth); |
| } |
| |
| len = gst_sdp_message_times_len (msg); |
| for (i = 0; i < len; i++) { |
| const gchar **repeat = NULL; |
| const GstSDPTime *time = gst_sdp_message_get_time (msg, i); |
| |
| if (time->repeat != NULL) { |
| guint j; |
| |
| repeat = g_malloc0 ((time->repeat->len + 1) * sizeof (gchar *)); |
| for (j = 0; j < time->repeat->len; j++) { |
| repeat[j] = g_array_index (time->repeat, char *, j); |
| } |
| repeat[j] = NULL; |
| } |
| |
| gst_sdp_message_add_time (cp, time->start, time->stop, repeat); |
| |
| g_free ((gchar **) repeat); |
| } |
| |
| len = gst_sdp_message_zones_len (msg); |
| for (i = 0; i < len; i++) { |
| const GstSDPZone *zone = gst_sdp_message_get_zone (msg, i); |
| gst_sdp_message_add_zone (cp, zone->time, zone->typed_time); |
| } |
| |
| gst_sdp_message_set_key (cp, msg->key.type, msg->key.data); |
| |
| len = gst_sdp_message_attributes_len (msg); |
| for (i = 0; i < len; i++) { |
| const GstSDPAttribute *attr = gst_sdp_message_get_attribute (msg, i); |
| gst_sdp_message_add_attribute (cp, attr->key, attr->value); |
| } |
| |
| len = gst_sdp_message_medias_len (msg); |
| for (i = 0; i < len; i++) { |
| GstSDPMedia *media_copy; |
| const GstSDPMedia *media = gst_sdp_message_get_media (msg, i); |
| |
| if (gst_sdp_media_copy (media, &media_copy) == GST_SDP_OK) { |
| gst_sdp_message_add_media (cp, media_copy); |
| gst_sdp_media_free (media_copy); |
| } |
| } |
| |
| 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; |
| } |
| |
| /** |
| * gst_sdp_address_is_multicast: |
| * @nettype: a network type |
| * @addrtype: an address type |
| * @addr: an address |
| * |
| * Check if the given @addr is a multicast address. |
| * |
| * Returns: TRUE when @addr is multicast. |
| */ |
| gboolean |
| gst_sdp_address_is_multicast (const gchar * nettype, const gchar * addrtype, |
| const gchar * addr) |
| { |
| gboolean ret = FALSE; |
| GInetAddress *iaddr; |
| |
| g_return_val_if_fail (addr, FALSE); |
| |
| /* we only support IN */ |
| if (nettype && strcmp (nettype, "IN") != 0) |
| return FALSE; |
| |
| /* guard against parse failures */ |
| if ((iaddr = g_inet_address_new_from_string (addr)) == NULL) |
| return FALSE; |
| |
| ret = g_inet_address_get_is_multicast (iaddr); |
| g_object_unref (iaddr); |
| |
| 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 (msg->connection.nettype && msg->connection.addrtype && |
| msg->connection.address) { |
| g_string_append_printf (lines, "c=%s %s %s", msg->connection.nettype, |
| msg->connection.addrtype, msg->connection.address); |
| if (gst_sdp_address_is_multicast (msg->connection.nettype, |
| msg->connection.addrtype, msg->connection.address)) { |
| /* only add ttl for IP4 */ |
| if (strcmp (msg->connection.addrtype, "IP4") == 0) |
| 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); |
| } |
| |
| if (gst_sdp_message_times_len (msg) == 0) { |
| g_string_append_printf (lines, "t=0 0\r\n"); |
| } else { |
| 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 && attr->value[0] != '\0') |
| 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); |
| } |
| |
| static int |
| hex_to_int (gchar c) |
| { |
| return c >= '0' && c <= '9' ? c - '0' |
| : c >= 'A' && c <= 'F' ? c - 'A' + 10 |
| : c >= 'a' && c <= 'f' ? c - 'a' + 10 : 0; |
| } |
| |
| /** |
| * gst_sdp_message_parse_uri: |
| * @uri: the start of the uri |
| * @msg: the result #GstSDPMessage |
| * |
| * Parse the null-terminated @uri and store the result in @msg. |
| * |
| * The uri should be of the form: |
| * |
| * scheme://[address[:ttl=ttl][:noa=noa]]/[sessionname] |
| * [#type=value *[&type=value]] |
| * |
| * where value is url encoded. This looslely resembles |
| * http://tools.ietf.org/html/draft-fujikawa-sdp-url-01 |
| * |
| * Returns: #GST_SDP_OK on success. |
| */ |
| GstSDPResult |
| gst_sdp_message_parse_uri (const gchar * uri, GstSDPMessage * msg) |
| { |
| GstSDPResult res; |
| gchar *message; |
| const gchar *colon, *slash, *hash, *p; |
| GString *lines; |
| |
| g_return_val_if_fail (uri != NULL, GST_SDP_EINVAL); |
| g_return_val_if_fail (msg != NULL, GST_SDP_EINVAL); |
| |
| colon = strstr (uri, "://"); |
| if (!colon) |
| goto no_colon; |
| |
| /* FIXME connection info goes here */ |
| |
| slash = strstr (colon + 3, "/"); |
| if (!slash) |
| goto no_slash; |
| |
| /* FIXME session name goes here */ |
| |
| hash = strstr (slash + 1, "#"); |
| if (!hash) |
| goto no_hash; |
| |
| lines = g_string_new (""); |
| |
| /* unescape */ |
| for (p = hash + 1; *p; p++) { |
| if (*p == '&') |
| g_string_append_printf (lines, "\r\n"); |
| else if (*p == '+') |
| g_string_append_c (lines, ' '); |
| else if (*p == '%') { |
| gchar a, b; |
| |
| if ((a = p[1])) { |
| if ((b = p[2])) { |
| g_string_append_c (lines, (hex_to_int (a) << 4) | hex_to_int (b)); |
| p += 2; |
| } |
| } else { |
| p++; |
| } |
| } else |
| g_string_append_c (lines, *p); |
| } |
| |
| message = g_string_free (lines, FALSE); |
| res = |
| gst_sdp_message_parse_buffer ((const guint8 *) message, strlen (message), |
| msg); |
| g_free (message); |
| |
| return res; |
| |
| /* ERRORS */ |
| no_colon: |
| { |
| return GST_SDP_EINVAL; |
| } |
| no_slash: |
| { |
| return GST_SDP_EINVAL; |
| } |
| no_hash: |
| { |
| return GST_SDP_EINVAL; |
| } |
| } |
| |
| static const guchar acceptable[96] = { |
| /* X0 X1 X2 X3 X4 X5 X6 X7 X8 X9 XA XB XC XD XE XF */ |
| 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x00, 0x01, 0x01, 0x01, 0x00, /* 2X !"#$%&'()*+,-./ */ |
| 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 3X 0123456789:;<=>? */ |
| 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, /* 4X @ABCDEFGHIJKLMNO */ |
| 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x01, /* 5X PQRSTUVWXYZ[\]^_ */ |
| 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, /* 6X `abcdefghijklmno */ |
| 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00 /* 7X pqrstuvwxyz{|}~DEL */ |
| }; |
| |
| static const gchar hex[16] = "0123456789ABCDEF"; |
| |
| #define ACCEPTABLE_CHAR(a) (((guchar)(a))>=32 && ((guchar)(a))<128 && acceptable[(((guchar)a))-32]) |
| |
| /** |
| * gst_sdp_message_as_uri: |
| * @scheme: the uri scheme |
| * @msg: the #GstSDPMessage |
| * |
| * Creates a uri from @msg with the given @scheme. The uri has the format: |
| * |
| * \@scheme:///[#type=value *[&type=value]] |
| * |
| * Where each value is url encoded. |
| * |
| * Returns: a uri for @msg. |
| */ |
| gchar * |
| gst_sdp_message_as_uri (const gchar * scheme, const GstSDPMessage * msg) |
| { |
| gchar *serialized, *p; |
| gchar *res; |
| GString *lines; |
| gboolean first; |
| |
| g_return_val_if_fail (scheme != NULL, NULL); |
| g_return_val_if_fail (msg != NULL, NULL); |
| |
| p = serialized = gst_sdp_message_as_text (msg); |
| |
| lines = g_string_new (""); |
| g_string_append_printf (lines, "%s:///#", scheme); |
| |
| /* now escape */ |
| first = TRUE; |
| for (p = serialized; *p; p++) { |
| if (first) { |
| g_string_append_printf (lines, "%c=", *p); |
| if (*(p + 1)) |
| p++; |
| first = FALSE; |
| continue; |
| } |
| if (*p == '\r') |
| continue; |
| else if (*p == '\n') { |
| if (*(p + 1)) |
| g_string_append_c (lines, '&'); |
| first = TRUE; |
| } else if (*p == ' ') |
| g_string_append_c (lines, '+'); |
| else if (ACCEPTABLE_CHAR (*p)) |
| g_string_append_c (lines, *p); |
| else { |
| /* escape */ |
| g_string_append_printf (lines, "%%%c%c", hex[*p >> 4], hex[*p & 0xf]); |
| } |
| } |
| |
| res = g_string_free (lines, FALSE); |
| g_free (serialized); |
| |
| return res; |
| } |
| |
| /** |
| * 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) |
| { |
| g_return_val_if_fail (msg != NULL, GST_SDP_EINVAL); |
| |
| 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) |
| { |
| g_return_val_if_fail (msg != NULL, NULL); |
| |
| 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_STR_ARRAY_GETTER (email, emails); |
| |
| /** |
| * gst_sdp_message_insert_email: |
| * @msg: a #GstSDPMessage |
| * @idx: an index |
| * @email: an email |
| * |
| * Insert @email into the array of emails in @msg at index @idx. |
| * When -1 is given as @idx, the email is inserted at the end. |
| * |
| * Returns: a #GstSDPResult. |
| * |
| * Since: 1.2 |
| */ |
| DEFINE_STR_ARRAY_INSERT (email, emails); |
| |
| /** |
| * gst_sdp_message_replace_email: |
| * @msg: a #GstSDPMessage |
| * @idx: an email index |
| * @email: an email |
| * |
| * Replace the email in @msg at index @idx with @email. |
| * |
| * Returns: a #GstSDPResult. |
| * |
| * Since: 1.2 |
| */ |
| DEFINE_STR_ARRAY_REPLACE (email, emails); |
| |
| /** |
| * gst_sdp_message_remove_email: |
| * @msg: a #GstSDPMessage |
| * @idx: an email index |
| * |
| * Remove the email in @msg at index @idx. |
| * |
| * Returns: a #GstSDPResult. |
| * |
| * Since: 1.2 |
| */ |
| DEFINE_STR_ARRAY_REMOVE (email, emails); |
| |
| /** |
| * gst_sdp_message_add_email: |
| * @msg: a #GstSDPMessage |
| * @email: an email |
| * |
| * Add @email to the list of emails in @msg. |
| * |
| * Returns: a #GstSDPResult. |
| */ |
| DEFINE_STR_ARRAY_ADDER (email, emails); |
| |
| /** |
| * 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_STR_ARRAY_GETTER (phone, phones); |
| |
| /** |
| * gst_sdp_message_insert_phone: |
| * @msg: a #GstSDPMessage |
| * @idx: a phone index |
| * @phone: a phone |
| * |
| * Insert @phone into the array of phone numbers in @msg at index @idx. |
| * When -1 is given as @idx, the phone is inserted at the end. |
| * |
| * Returns: a #GstSDPResult. |
| * |
| * Since: 1.2 |
| */ |
| DEFINE_STR_ARRAY_INSERT (phone, phones); |
| |
| /** |
| * gst_sdp_message_replace_phone: |
| * @msg: a #GstSDPMessage |
| * @idx: a phone index |
| * @phone: a phone |
| * |
| * Replace the phone number in @msg at index @idx with @phone. |
| * |
| * Returns: a #GstSDPResult. |
| * |
| * Since: 1.2 |
| */ |
| DEFINE_STR_ARRAY_REPLACE (phone, phones); |
| |
| /** |
| * gst_sdp_message_remove_phone: |
| * @msg: a #GstSDPMessage |
| * @idx: a phone index |
| * |
| * Remove the phone number in @msg at index @idx. |
| * |
| * Returns: a #GstSDPResult. |
| * |
| * Since: 1.2 |
| */ |
| DEFINE_STR_ARRAY_REMOVE (phone, phones); |
| |
| /** |
| * gst_sdp_message_add_phone: |
| * @msg: a #GstSDPMessage |
| * @phone: a phone |
| * |
| * Add @phone to the list of phones in @msg. |
| * |
| * Returns: a #GstSDPResult. |
| */ |
| DEFINE_STR_ARRAY_ADDER (phone, phones); |
| |
| |
| /** |
| * 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) |
| { |
| g_return_val_if_fail (msg != NULL, GST_SDP_EINVAL); |
| |
| 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) |
| { |
| g_return_val_if_fail (msg != NULL, NULL); |
| |
| return &msg->connection; |
| } |
| |
| /** |
| * gst_sdp_bandwidth_set: |
| * @bw: a #GstSDPBandwidth |
| * @bwtype: the bandwidth modifier type |
| * @bandwidth: the bandwidth in kilobits per second |
| * |
| * Set bandwidth information in @bw. |
| * |
| * Returns: a #GstSDPResult. |
| * |
| * Since: 1.2 |
| */ |
| GstSDPResult |
| gst_sdp_bandwidth_set (GstSDPBandwidth * bw, const gchar * bwtype, |
| guint bandwidth) |
| { |
| g_return_val_if_fail (bw != NULL, GST_SDP_EINVAL); |
| |
| bw->bwtype = g_strdup (bwtype); |
| bw->bandwidth = bandwidth; |
| return GST_SDP_OK; |
| } |
| |
| /** |
| * gst_sdp_bandwidth_clear: |
| * @bw: a #GstSDPBandwidth |
| * |
| * Reset the bandwidth information in @bw. |
| * |
| * Returns: a #GstSDPResult. |
| * |
| * Since: 1.2 |
| */ |
| GstSDPResult |
| gst_sdp_bandwidth_clear (GstSDPBandwidth * bw) |
| { |
| g_return_val_if_fail (bw != NULL, GST_SDP_EINVAL); |
| |
| FREE_STRING (bw->bwtype); |
| bw->bandwidth = 0; |
| return GST_SDP_OK; |
| } |
| |
| /** |
| * 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, GstSDPBandwidth); |
| |
| #define DUP_BANDWIDTH(v, val) memcpy (v, val, sizeof (GstSDPBandwidth)) |
| #define FREE_BANDWIDTH(v) gst_sdp_bandwidth_clear(v) |
| |
| /** |
| * gst_sdp_message_insert_bandwidth: |
| * @msg: a #GstSDPMessage |
| * @idx: an index |
| * @bw: the bandwidth |
| * |
| * Insert bandwidth parameters into the array of bandwidths in @msg |
| * at index @idx. |
| * When -1 is given as @idx, the bandwidth is inserted at the end. |
| * |
| * Returns: a #GstSDPResult. |
| * |
| * Since: 1.2 |
| */ |
| DEFINE_ARRAY_INSERT (bandwidth, bandwidths, GstSDPBandwidth *, DUP_BANDWIDTH, |
| GstSDPBandwidth); |
| |
| /** |
| * gst_sdp_message_replace_bandwidth: |
| * @msg: a #GstSDPMessage |
| * @idx: the bandwidth index |
| * @bw: the bandwidth |
| * |
| * Replace the bandwidth information in @msg at index @idx with @bw. |
| * |
| * Returns: a #GstSDPResult. |
| * |
| * Since: 1.2 |
| */ |
| DEFINE_ARRAY_REPLACE (bandwidth, bandwidths, GstSDPBandwidth *, FREE_BANDWIDTH, |
| DUP_BANDWIDTH, GstSDPBandwidth); |
| |
| /** |
| * gst_sdp_message_remove_bandwidth: |
| * @msg: a #GstSDPMessage |
| * @idx: the bandwidth index |
| * |
| * Remove the bandwidth information in @msg at index @idx. |
| * |
| * Returns: a #GstSDPResult. |
| * |
| * Since: 1.2 |
| */ |
| DEFINE_ARRAY_REMOVE (bandwidth, bandwidths, GstSDPBandwidth, FREE_BANDWIDTH); |
| |
| /** |
| * 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; |
| |
| g_return_val_if_fail (msg != NULL, GST_SDP_EINVAL); |
| |
| gst_sdp_bandwidth_set (&bw, bwtype, bandwidth); |
| return gst_sdp_message_insert_bandwidth (msg, -1, &bw); |
| } |
| |
| /** |
| * gst_sdp_time_set: |
| * @t: a #GstSDPTime |
| * @start: the start time |
| * @stop: the stop time |
| * @repeat: (array zero-terminated=1): the repeat times |
| * |
| * Set time information @start, @stop and @repeat in @t. |
| * |
| * Returns: a #GstSDPResult. |
| * |
| * Since: 1.2 |
| */ |
| GstSDPResult |
| gst_sdp_time_set (GstSDPTime * t, const gchar * start, |
| const gchar * stop, const gchar ** repeat) |
| { |
| g_return_val_if_fail (t != NULL, GST_SDP_EINVAL); |
| |
| t->start = g_strdup (start); |
| t->stop = g_strdup (stop); |
| if (repeat) { |
| t->repeat = g_array_new (FALSE, TRUE, sizeof (gchar *)); |
| for (; *repeat; repeat++) { |
| gchar *r = g_strdup (*repeat); |
| |
| g_array_append_val (t->repeat, r); |
| } |
| } else |
| t->repeat = NULL; |
| |
| return GST_SDP_OK; |
| } |
| |
| /** |
| * gst_sdp_time_clear: |
| * @t: a #GstSDPTime |
| * |
| * Reset the time information in @t. |
| * |
| * Returns: a #GstSDPResult. |
| * |
| * Since: 1.2 |
| */ |
| GstSDPResult |
| gst_sdp_time_clear (GstSDPTime * t) |
| { |
| g_return_val_if_fail (t != NULL, GST_SDP_EINVAL); |
| |
| FREE_STRING (t->start); |
| FREE_STRING (t->stop); |
| INIT_STR_ARRAY (t->repeat); |
| FREE_ARRAY (t->repeat); |
| |
| 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, GstSDPTime); |
| |
| #define DUP_TIME(v, val) memcpy (v, val, sizeof (GstSDPTime)) |
| #define FREE_TIME(v) gst_sdp_time_clear(v) |
| |
| /** |
| * gst_sdp_message_insert_time: |
| * @msg: a #GstSDPMessage |
| * @idx: an index |
| * @t: a #GstSDPTime |
| * |
| * Insert time parameters into the array of times in @msg |
| * at index @idx. |
| * When -1 is given as @idx, the times are inserted at the end. |
| * |
| * Returns: a #GstSDPResult. |
| * |
| * Since: 1.2 |
| */ |
| DEFINE_ARRAY_INSERT (time, times, GstSDPTime *, DUP_TIME, GstSDPTime); |
| |
| /** |
| * gst_sdp_message_replace_time: |
| * @msg: a #GstSDPMessage |
| * @idx: the index |
| * @t: a #GstSDPTime |
| * |
| * Replace the time information in @msg at index @idx with @t. |
| * |
| * Returns: a #GstSDPResult. |
| * |
| * Since: 1.2 |
| */ |
| DEFINE_ARRAY_REPLACE (time, times, GstSDPTime *, FREE_TIME, |
| DUP_TIME, GstSDPTime); |
| |
| /** |
| * gst_sdp_message_remove_time: |
| * @msg: a #GstSDPMessage |
| * @idx: the index |
| * |
| * Remove the time information in @msg at index @idx. |
| * |
| * Returns: a #GstSDPResult. |
| * |
| * Since: 1.2 |
| */ |
| DEFINE_ARRAY_REMOVE (time, times, GstSDPTime, FREE_TIME); |
| |
| /** |
| * gst_sdp_message_add_time: |
| * @msg: a #GstSDPMessage |
| * @start: the start time |
| * @stop: the stop time |
| * @repeat: (array zero-terminated=1): 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; |
| |
| g_return_val_if_fail (msg != NULL, GST_SDP_EINVAL); |
| |
| gst_sdp_time_set (×, start, stop, repeat); |
| g_array_append_val (msg->times, times); |
| |
| return GST_SDP_OK; |
| } |
| |
| /** |
| * gst_sdp_zone_set: |
| * @zone: a #GstSDPZone |
| * @adj_time: the NTP time that a time zone adjustment happens |
| * @typed_time: the offset from the time when the session was first scheduled |
| * |
| * Set zone information in @zone. |
| * |
| * Returns: a #GstSDPResult. |
| * |
| * Since: 1.2 |
| */ |
| GstSDPResult |
| gst_sdp_zone_set (GstSDPZone * zone, const gchar * adj_time, |
| const gchar * typed_time) |
| { |
| g_return_val_if_fail (zone != NULL, GST_SDP_EINVAL); |
| |
| zone->time = g_strdup (adj_time); |
| zone->typed_time = g_strdup (typed_time); |
| return GST_SDP_OK; |
| } |
| |
| /** |
| * gst_sdp_zone_clear: |
| * @zone: a #GstSDPZone |
| * |
| * Reset the zone information in @zone. |
| * |
| * Returns: a #GstSDPResult. |
| * |
| * Since: 1.2 |
| */ |
| GstSDPResult |
| gst_sdp_zone_clear (GstSDPZone * zone) |
| { |
| g_return_val_if_fail (zone != NULL, GST_SDP_EINVAL); |
| |
| FREE_STRING (zone->time); |
| FREE_STRING (zone->typed_time); |
| 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, GstSDPZone); |
| |
| #define DUP_ZONE(v, val) memcpy (v, val, sizeof (GstSDPZone)) |
| #define FREE_ZONE(v) gst_sdp_zone_clear(v) |
| |
| /** |
| * gst_sdp_message_insert_zone: |
| * @msg: a #GstSDPMessage |
| * @idx: an index |
| * @zone a #GstSDPZone |
| * |
| * Insert zone parameters into the array of zones in @msg |
| * at index @idx. |
| * When -1 is given as @idx, the zone is inserted at the end. |
| * |
| * Returns: a #GstSDPResult. |
| * |
| * Since: 1.2 |
| */ |
| DEFINE_ARRAY_INSERT (zone, zones, GstSDPZone *, DUP_ZONE, GstSDPZone); |
| |
| /** |
| * gst_sdp_message_replace_zone: |
| * @msg: a #GstSDPMessage |
| * @idx: the index |
| * @zone: a #GstSDPZone |
| * |
| * Replace the zone information in @msg at index @idx with @zone. |
| * |
| * Returns: a #GstSDPResult. |
| * |
| * Since: 1.2 |
| */ |
| DEFINE_ARRAY_REPLACE (zone, zones, GstSDPZone *, FREE_ZONE, |
| DUP_ZONE, GstSDPZone); |
| |
| /** |
| * gst_sdp_message_remove_zone: |
| * @msg: a #GstSDPMessage |
| * @idx: the index |
| * |
| * Remove the zone information in @msg at index @idx. |
| * |
| * Returns: a #GstSDPResult. |
| * |
| * Since: 1.2 |
| */ |
| DEFINE_ARRAY_REMOVE (zone, zones, GstSDPZone, FREE_ZONE); |
| |
| /** |
| * 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; |
| |
| g_return_val_if_fail (msg != NULL, GST_SDP_EINVAL); |
| |
| gst_sdp_zone_set (&zone, adj_time, 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) |
| { |
| g_return_val_if_fail (msg != NULL, GST_SDP_EINVAL); |
| |
| 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) |
| { |
| g_return_val_if_fail (msg != NULL, NULL); |
| |
| return &msg->key; |
| } |
| |
| /** |
| * gst_sdp_attribute_set: |
| * @attr: a #GstSDPAttribute |
| * @key: the key |
| * @value: (nullable): the value |
| * |
| * Set the attribute with @key and @value. |
| * |
| * Returns: @GST_SDP_OK. |
| * |
| * Since: 1.2 |
| */ |
| GstSDPResult |
| gst_sdp_attribute_set (GstSDPAttribute * attr, const gchar * key, |
| const gchar * value) |
| { |
| g_return_val_if_fail (attr != NULL, GST_SDP_EINVAL); |
| g_return_val_if_fail (key != NULL, GST_SDP_EINVAL); |
| |
| attr->key = g_strdup (key); |
| attr->value = g_strdup (value); |
| return GST_SDP_OK; |
| } |
| |
| /** |
| * gst_sdp_attribute_clear: |
| * @attr: a #GstSDPAttribute |
| * |
| * Clear the attribute. |
| * |
| * Returns: @GST_SDP_OK. |
| * |
| * Since: 1.2 |
| */ |
| GstSDPResult |
| gst_sdp_attribute_clear (GstSDPAttribute * attr) |
| { |
| g_return_val_if_fail (attr != NULL, GST_SDP_EINVAL); |
| |
| FREE_STRING (attr->key); |
| FREE_STRING (attr->value); |
| return GST_SDP_OK; |
| } |
| |
| /** |
| * 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, 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; |
| |
| g_return_val_if_fail (msg != NULL, NULL); |
| g_return_val_if_fail (key != NULL, NULL); |
| |
| 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); |
| } |
| |
| #define DUP_ATTRIBUTE(v, val) memcpy (v, val, sizeof (GstSDPAttribute)) |
| #define FREE_ATTRIBUTE(v) gst_sdp_attribute_clear(v) |
| |
| /** |
| * gst_sdp_message_insert_attribute: |
| * @msg: a #GstSDPMessage |
| * @idx: an index |
| * @attr: a #GstSDPAttribute |
| * |
| * Insert attribute into the array of attributes in @msg |
| * at index @idx. |
| * When -1 is given as @idx, the attribute is inserted at the end. |
| * |
| * Returns: a #GstSDPResult. |
| * |
| * Since: 1.2 |
| */ |
| DEFINE_ARRAY_INSERT (attribute, attributes, GstSDPAttribute *, DUP_ATTRIBUTE, |
| GstSDPAttribute); |
| |
| /** |
| * gst_sdp_message_replace_attribute: |
| * @msg: a #GstSDPMessage |
| * @idx: the index |
| * @attr: a #GstSDPAttribute |
| * |
| * Replace the attribute in @msg at index @idx with @attr. |
| * |
| * Returns: a #GstSDPResult. |
| * |
| * Since: 1.2 |
| */ |
| DEFINE_ARRAY_REPLACE (attribute, attributes, GstSDPAttribute *, FREE_ATTRIBUTE, |
| DUP_ATTRIBUTE, GstSDPAttribute); |
| |
| /** |
| * gst_sdp_message_remove_attribute: |
| * @msg: a #GstSDPMessage |
| * @idx: the index |
| * |
| * Remove the attribute in @msg at index @idx. |
| * |
| * Returns: a #GstSDPResult. |
| * |
| * Since: 1.2 |
| */ |
| DEFINE_ARRAY_REMOVE (attribute, attributes, GstSDPAttribute, FREE_ATTRIBUTE); |
| |
| /** |
| * gst_sdp_message_add_attribute: |
| * @msg: a #GstSDPMessage |
| * @key: the key |
| * @value: (nullable): 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; |
| |
| g_return_val_if_fail (msg != NULL, GST_SDP_EINVAL); |
| g_return_val_if_fail (key != NULL, GST_SDP_EINVAL); |
| |
| gst_sdp_attribute_set (&attr, key, 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, 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_sdp_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; |
| |
| g_return_val_if_fail (msg != NULL, GST_SDP_EINVAL); |
| g_return_val_if_fail (media != NULL, GST_SDP_EINVAL); |
| |
| 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: (out) (transfer full): 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_STR_ARRAY (media->fmts); |
| FREE_STRING (media->information); |
| INIT_ARRAY (media->connections, GstSDPConnection, gst_sdp_connection_clear); |
| INIT_ARRAY (media->bandwidths, GstSDPBandwidth, gst_sdp_bandwidth_clear); |
| gst_sdp_key_init (&media->key); |
| INIT_ARRAY (media->attributes, GstSDPAttribute, gst_sdp_attribute_clear); |
| |
| 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_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_copy: |
| * @media: a #GstSDPMedia |
| * @copy: (out) (transfer full): pointer to new #GstSDPMedia |
| * |
| * Allocate a new copy of @media and store the result in @copy. The value in |
| * @copy should be release with gst_sdp_media_free function. |
| * |
| * Returns: a #GstSDPResult |
| * |
| * Since: 1.2 |
| */ |
| GstSDPResult |
| gst_sdp_media_copy (const GstSDPMedia * media, GstSDPMedia ** copy) |
| { |
| GstSDPResult ret; |
| GstSDPMedia *cp; |
| guint i, len; |
| |
| if (media == NULL) |
| return GST_SDP_EINVAL; |
| |
| ret = gst_sdp_media_new (copy); |
| if (ret != GST_SDP_OK) |
| return ret; |
| |
| cp = *copy; |
| |
| REPLACE_STRING (cp->media, media->media); |
| cp->port = media->port; |
| cp->num_ports = media->num_ports; |
| REPLACE_STRING (cp->proto, media->proto); |
| |
| len = gst_sdp_media_formats_len (media); |
| for (i = 0; i < len; i++) { |
| gst_sdp_media_add_format (cp, gst_sdp_media_get_format (media, i)); |
| } |
| |
| REPLACE_STRING (cp->information, media->information); |
| |
| len = gst_sdp_media_connections_len (media); |
| for (i = 0; i < len; i++) { |
| const GstSDPConnection *connection = |
| gst_sdp_media_get_connection (media, i); |
| gst_sdp_media_add_connection (cp, connection->nettype, connection->addrtype, |
| connection->address, connection->ttl, connection->addr_number); |
| } |
| |
| len = gst_sdp_media_bandwidths_len (media); |
| for (i = 0; i < len; i++) { |
| const GstSDPBandwidth *bw = gst_sdp_media_get_bandwidth (media, i); |
| gst_sdp_media_add_bandwidth (cp, bw->bwtype, bw->bandwidth); |
| } |
| |
| gst_sdp_media_set_key (cp, media->key.type, media->key.data); |
| |
| len = gst_sdp_media_attributes_len (media); |
| for (i = 0; i < len; i++) { |
| const GstSDPAttribute *att = gst_sdp_media_get_attribute (media, i); |
| gst_sdp_media_add_attribute (cp, att->key, att->value); |
| } |
| |
| 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) { |
| g_string_append_printf (lines, "c=%s %s %s", conn->nettype, |
| conn->addrtype, conn->address); |
| if (gst_sdp_address_is_multicast (conn->nettype, conn->addrtype, |
| conn->address)) { |
| /* only add TTL for IP4 multicast */ |
| if (strcmp (conn->addrtype, "IP4") == 0) |
| 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 && attr->value[0] != '\0') |
| 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) |
| { |
| g_return_val_if_fail (media != NULL, NULL); |
| |
| 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_return_val_if_fail (media != NULL, GST_SDP_EINVAL); |
| g_return_val_if_fail (med != NULL, GST_SDP_EINVAL); |
| |
| 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) |
| { |
| g_return_val_if_fail (media != NULL, 0); |
| |
| 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) |
| { |
| g_return_val_if_fail (media != NULL, 0); |
| |
| 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) |
| { |
| g_return_val_if_fail (media != NULL, GST_SDP_EINVAL); |
| |
| 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) |
| { |
| g_return_val_if_fail (media != NULL, NULL); |
| |
| 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_return_val_if_fail (media != NULL, GST_SDP_EINVAL); |
| |
| 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) |
| { |
| g_return_val_if_fail (media != NULL, 0); |
| |
| 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) |
| { |
| g_return_val_if_fail (media != NULL, NULL); |
| |
| if (idx >= media->fmts->len) |
| return NULL; |
| return g_array_index (media->fmts, gchar *, idx); |
| } |
| |
| /** |
| * gst_sdp_media_insert_format: |
| * @media: a #GstSDPMedia |
| * @idx: an index |
| * @format: the format |
| * |
| * Insert the format information to @media at @idx. When @idx is -1, |
| * the format is appended. |
| * |
| * Returns: #GST_SDP_OK. |
| * |
| * Since: 1.2 |
| */ |
| GstSDPResult |
| gst_sdp_media_insert_format (GstSDPMedia * media, gint idx, |
| const gchar * format) |
| { |
| gchar *fmt; |
| |
| g_return_val_if_fail (media != NULL, GST_SDP_EINVAL); |
| g_return_val_if_fail (format != NULL, GST_SDP_EINVAL); |
| |
| fmt = g_strdup (format); |
| |
| if (idx == -1) |
| g_array_append_val (media->fmts, fmt); |
| else |
| g_array_insert_val (media->fmts, idx, fmt); |
| |
| return GST_SDP_OK; |
| } |
| |
| /** |
| * gst_sdp_media_replace_format: |
| * @media: a #GstSDPMedia |
| * @idx: an index |
| * @format: the format |
| * |
| * Replace the format information in @media at @idx with @format. |
| * |
| * Returns: #GST_SDP_OK. |
| * |
| * Since: 1.2 |
| */ |
| GstSDPResult |
| gst_sdp_media_replace_format (GstSDPMedia * media, guint idx, |
| const gchar * format) |
| { |
| gchar **old; |
| |
| g_return_val_if_fail (media != NULL, GST_SDP_EINVAL); |
| g_return_val_if_fail (format != NULL, GST_SDP_EINVAL); |
| |
| old = &g_array_index (media->fmts, gchar *, idx); |
| g_free (*old); |
| *old = g_strdup (format); |
| |
| return GST_SDP_OK; |
| } |
| |
| /** |
| * gst_sdp_media_remove_format: |
| * @media: a #GstSDPMedia |
| * @idx: an index |
| * |
| * Remove the format information in @media at @idx. |
| * |
| * Returns: #GST_SDP_OK. |
| * |
| * Since: 1.2 |
| */ |
| GstSDPResult |
| gst_sdp_media_remove_format (GstSDPMedia * media, guint idx) |
| { |
| gchar **old; |
| |
| g_return_val_if_fail (media != NULL, GST_SDP_EINVAL); |
| |
| old = &g_array_index (media->fmts, gchar *, idx); |
| g_free (*old); |
| g_array_remove_index (media->fmts, idx); |
| |
| return GST_SDP_OK; |
| } |
| |
| /** |
| * 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; |
| |
| g_return_val_if_fail (media != NULL, GST_SDP_EINVAL); |
| g_return_val_if_fail (format != NULL, GST_SDP_EINVAL); |
| |
| 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) |
| { |
| g_return_val_if_fail (media != NULL, NULL); |
| |
| 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_return_val_if_fail (media != NULL, GST_SDP_EINVAL); |
| |
| g_free (media->information); |
| media->information = g_strdup (information); |
| |
| return GST_SDP_OK; |
| } |
| |
| /** |
| * gst_sdp_connection_set: |
| * @conn: a #GstSDPConnection |
| * @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 |
| * |
| * Set the connection with the given parameters. |
| * |
| * Returns: @GST_SDP_OK. |
| * |
| * Since: 1.2 |
| */ |
| GstSDPResult |
| gst_sdp_connection_set (GstSDPConnection * conn, const gchar * nettype, |
| const gchar * addrtype, const gchar * address, guint ttl, guint addr_number) |
| { |
| g_return_val_if_fail (conn != NULL, GST_SDP_EINVAL); |
| g_return_val_if_fail (nettype != NULL, GST_SDP_EINVAL); |
| g_return_val_if_fail (addrtype != NULL, GST_SDP_EINVAL); |
| g_return_val_if_fail (address != NULL, GST_SDP_EINVAL); |
| |
| conn->nettype = g_strdup (nettype); |
| conn->addrtype = g_strdup (addrtype); |
| conn->address = g_strdup (address); |
| conn->ttl = ttl; |
| conn->addr_number = addr_number; |
| return GST_SDP_OK; |
| } |
| |
| /** |
| * gst_sdp_connection_clear: |
| * @conn: a #GstSDPConnection |
| * |
| * Clear the connection. |
| * |
| * Returns: @GST_SDP_OK. |
| * |
| * Since: 1.2 |
| */ |
| GstSDPResult |
| gst_sdp_connection_clear (GstSDPConnection * conn) |
| { |
| g_return_val_if_fail (conn != NULL, GST_SDP_EINVAL); |
| |
| FREE_STRING (conn->nettype); |
| FREE_STRING (conn->addrtype); |
| FREE_STRING (conn->address); |
| conn->ttl = 0; |
| conn->addr_number = 0; |
| 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) |
| { |
| g_return_val_if_fail (media != NULL, 0); |
| |
| 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) |
| { |
| g_return_val_if_fail (media != NULL, NULL); |
| g_return_val_if_fail (idx < media->connections->len, NULL); |
| |
| return &g_array_index (media->connections, GstSDPConnection, idx); |
| } |
| |
| /** |
| * gst_sdp_media_insert_connection: |
| * @media: a #GstSDPMedia |
| * @idx: an index |
| * @conn: a #GstSDPConnection |
| * |
| * Insert the connection information to @media at @idx. When @idx is -1, |
| * the connection is appended. |
| * |
| * Returns: #GST_SDP_OK. |
| * |
| * Since: 1.2 |
| */ |
| GstSDPResult |
| gst_sdp_media_insert_connection (GstSDPMedia * media, gint idx, |
| GstSDPConnection * conn) |
| { |
| g_return_val_if_fail (media != NULL, GST_SDP_EINVAL); |
| g_return_val_if_fail (conn != NULL, GST_SDP_EINVAL); |
| g_return_val_if_fail (idx == -1 |
| || idx < media->connections->len, GST_SDP_EINVAL); |
| |
| if (idx == -1) |
| g_array_append_val (media->connections, *conn); |
| else |
| g_array_insert_val (media->connections, idx, *conn); |
| |
| return GST_SDP_OK; |
| } |
| |
| /** |
| * gst_sdp_media_replace_connection: |
| * @media: a #GstSDPMedia |
| * @idx: an index |
| * @conn: a #GstSDPConnection |
| * |
| * Replace the connection information in @media at @idx with @conn. |
| * |
| * Returns: #GST_SDP_OK. |
| * |
| * Since: 1.2 |
| */ |
| GstSDPResult |
| gst_sdp_media_replace_connection (GstSDPMedia * media, guint idx, |
| GstSDPConnection * conn) |
| { |
| GstSDPConnection *old; |
| |
| g_return_val_if_fail (media != NULL, GST_SDP_EINVAL); |
| g_return_val_if_fail (conn != NULL, GST_SDP_EINVAL); |
| g_return_val_if_fail (idx < media->connections->len, GST_SDP_EINVAL); |
| |
| old = &g_array_index (media->connections, GstSDPConnection, idx); |
| gst_sdp_connection_clear (old); |
| *old = *conn; |
| |
| return GST_SDP_OK; |
| } |
| |
| /** |
| * gst_sdp_media_remove_connection: |
| * @media: a #GstSDPMedia |
| * @idx: an index |
| * |
| * Remove the connection information in @media at @idx. |
| * |
| * Returns: #GST_SDP_OK. |
| * |
| * Since: 1.2 |
| */ |
| GstSDPResult |
| gst_sdp_media_remove_connection (GstSDPMedia * media, guint idx) |
| { |
| GstSDPConnection *old; |
| |
| g_return_val_if_fail (media != NULL, GST_SDP_EINVAL); |
| g_return_val_if_fail (idx < media->connections->len, GST_SDP_EINVAL); |
| |
| old = &g_array_index (media->connections, GstSDPConnection, idx); |
| gst_sdp_connection_clear (old); |
| g_array_remove_index (media->connections, idx); |
| |
| return GST_SDP_OK; |
| } |
| |
| /** |
| * 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; |
| |
| g_return_val_if_fail (media != NULL, GST_SDP_EINVAL); |
| g_return_val_if_fail (nettype != NULL, GST_SDP_EINVAL); |
| g_return_val_if_fail (addrtype != NULL, GST_SDP_EINVAL); |
| g_return_val_if_fail (address != NULL, GST_SDP_EINVAL); |
| |
| gst_sdp_connection_set (&conn, nettype, addrtype, address, ttl, 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) |
| { |
| g_return_val_if_fail (media != NULL, 0); |
| |
| 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) |
| { |
| g_return_val_if_fail (media != NULL, NULL); |
| |
| return &g_array_index (media->bandwidths, GstSDPBandwidth, idx); |
| } |
| |
| /** |
| * gst_sdp_media_insert_bandwidth: |
| * @media: a #GstSDPMedia |
| * @idx: an index |
| * @bw: a #GstSDPBandwidth |
| * |
| * Insert the bandwidth information to @media at @idx. When @idx is -1, |
| * the bandwidth is appended. |
| * |
| * Returns: #GST_SDP_OK. |
| * |
| * Since: 1.2 |
| */ |
| GstSDPResult |
| gst_sdp_media_insert_bandwidth (GstSDPMedia * media, gint idx, |
| GstSDPBandwidth * bw) |
| { |
| g_return_val_if_fail (media != NULL, GST_SDP_EINVAL); |
| g_return_val_if_fail (bw != NULL, GST_SDP_EINVAL); |
| g_return_val_if_fail (idx == -1 |
| || idx < media->bandwidths->len, GST_SDP_EINVAL); |
| |
| if (idx == -1) |
| g_array_append_val (media->bandwidths, *bw); |
| else |
| g_array_insert_val (media->bandwidths, idx, *bw); |
| |
| return GST_SDP_OK; |
| } |
| |
| /** |
| * gst_sdp_media_replace_bandwidth: |
| * @media: a #GstSDPMedia |
| * @idx: an index |
| * @bw: a #GstSDPBandwidth |
| * |
| * Replace the bandwidth information in @media at @idx with @bw. |
| * |
| * Returns: #GST_SDP_OK. |
| * |
| * Since: 1.2 |
| */ |
| GstSDPResult |
| gst_sdp_media_replace_bandwidth (GstSDPMedia * media, guint idx, |
| GstSDPBandwidth * bw) |
| { |
| GstSDPBandwidth *old; |
| g_return_val_if_fail (media != NULL, GST_SDP_EINVAL); |
| g_return_val_if_fail (bw != NULL, GST_SDP_EINVAL); |
| g_return_val_if_fail (idx < media->bandwidths->len, GST_SDP_EINVAL); |
| |
| old = &g_array_index (media->bandwidths, GstSDPBandwidth, idx); |
| gst_sdp_bandwidth_clear (old); |
| *old = *bw; |
| |
| return GST_SDP_OK; |
| } |
| |
| /** |
| * gst_sdp_media_remove_bandwidth: |
| * @media: a #GstSDPMedia |
| * @idx: an index |
| * |
| * Remove the bandwidth information in @media at @idx. |
| * |
| * Returns: #GST_SDP_OK. |
| * |
| * Since: 1.2 |
| */ |
| GstSDPResult |
| gst_sdp_media_remove_bandwidth (GstSDPMedia * media, guint idx) |
| { |
| GstSDPBandwidth *old; |
| |
| g_return_val_if_fail (media != NULL, GST_SDP_EINVAL); |
| g_return_val_if_fail (idx < media->bandwidths->len, GST_SDP_EINVAL); |
| |
| old = &g_array_index (media->bandwidths, GstSDPBandwidth, idx); |
| gst_sdp_bandwidth_clear (old); |
| g_array_remove_index (media->bandwidths, idx); |
| |
| return GST_SDP_OK; |
| } |
| |
| /** |
| * 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; |
| |
| g_return_val_if_fail (media != NULL, GST_SDP_EINVAL); |
| g_return_val_if_fail (bwtype != NULL, GST_SDP_EINVAL); |
| |
| gst_sdp_bandwidth_set (&bw, bwtype, 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_return_val_if_fail (media != NULL, GST_SDP_EINVAL); |
| |
| 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) |
| { |
| g_return_val_if_fail (media != NULL, NULL); |
| |
| 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) |
| { |
| g_return_val_if_fail (media != NULL, 0); |
| |
| return media->attributes->len; |
| } |
| |
| /** |
| * gst_sdp_media_add_attribute: |
| * @media: a #GstSDPMedia |
| * @key: a key |
| * @value: (nullable): 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; |
| |
| g_return_val_if_fail (media != NULL, GST_SDP_EINVAL); |
| g_return_val_if_fail (key != NULL, GST_SDP_EINVAL); |
| |
| gst_sdp_attribute_set (&attr, key, 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) |
| { |
| g_return_val_if_fail (media != NULL, NULL); |
| g_return_val_if_fail (idx < media->attributes->len, NULL); |
| |
| 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; |
| |
| g_return_val_if_fail (media != NULL, NULL); |
| g_return_val_if_fail (key != NULL, NULL); |
| |
| 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) |
| { |
| g_return_val_if_fail (media != NULL, NULL); |
| g_return_val_if_fail (key != NULL, NULL); |
| |
| return gst_sdp_media_get_attribute_val_n (media, key, 0); |
| } |
| |
| /** |
| * gst_sdp_media_insert_attribute: |
| * @media: a #GstSDPMedia |
| * @idx: an index |
| * @attr: a #GstSDPAttribute |
| * |
| * Insert the attribute to @media at @idx. When @idx is -1, |
| * the attribute is appended. |
| * |
| * Returns: #GST_SDP_OK. |
| * |
| * Since: 1.2 |
| */ |
| GstSDPResult |
| gst_sdp_media_insert_attribute (GstSDPMedia * media, gint idx, |
| GstSDPAttribute * attr) |
| { |
| g_return_val_if_fail (media != NULL, GST_SDP_EINVAL); |
| g_return_val_if_fail (attr != NULL, GST_SDP_EINVAL); |
| g_return_val_if_fail (idx == -1 |
| || idx < media->attributes->len, GST_SDP_EINVAL); |
| |
| if (idx == -1) |
| g_array_append_val (media->attributes, *attr); |
| else |
| g_array_insert_val (media->attributes, idx, *attr); |
| |
| return GST_SDP_OK; |
| } |
| |
| /** |
| * gst_sdp_media_replace_attribute: |
| * @media: a #GstSDPMedia |
| * @idx: an index |
| * @attr: a #GstSDPAttribute |
| * |
| * Replace the attribute in @media at @idx with @attr. |
| * |
| * Returns: #GST_SDP_OK. |
| * |
| * Since: 1.2 |
| */ |
| GstSDPResult |
| gst_sdp_media_replace_attribute (GstSDPMedia * media, guint idx, |
| GstSDPAttribute * attr) |
| { |
| GstSDPAttribute *old; |
| |
| g_return_val_if_fail (media != NULL, GST_SDP_EINVAL); |
| g_return_val_if_fail (attr != NULL, GST_SDP_EINVAL); |
| g_return_val_if_fail (idx < media->attributes->len, GST_SDP_EINVAL); |
| |
| old = &g_array_index (media->attributes, GstSDPAttribute, idx); |
| gst_sdp_attribute_clear (old); |
| *old = *attr; |
| |
| return GST_SDP_OK; |
| } |
| |
| /** |
| * gst_sdp_media_remove_attribute: |
| * @media: a #GstSDPMedia |
| * @idx: an index |
| * |
| * Remove the attribute in @media at @idx. |
| * |
| * Returns: #GST_SDP_OK. |
| * |
| * Since: 1.2 |
| */ |
| GstSDPResult |
| gst_sdp_media_remove_attribute (GstSDPMedia * media, guint idx) |
| { |
| GstSDPAttribute *old; |
| g_return_val_if_fail (media != NULL, GST_SDP_EINVAL); |
| g_return_val_if_fail (idx < media->attributes->len, GST_SDP_EINVAL); |
| |
| old = &g_array_index (media->attributes, GstSDPAttribute, idx); |
| gst_sdp_attribute_clear (old); |
| g_array_remove_index (media->attributes, idx); |
| |
| return GST_SDP_OK; |
| } |
| |
| 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) \ |
| do { read_string (str, sizeof (str), &p); REPLACE_STRING (field, str); } while (0) |
| #define READ_UINT(field) \ |
| do { read_string (str, sizeof (str), &p); field = strtoul (str, NULL, 10); } while (0) |
| |
| switch (type) { |
| case 'v': |
| if (buffer[0] != '0') |
| GST_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': |
| { |
| GstSDPConnection conn; |
| gchar *str2; |
| |
| memset (&conn, 0, sizeof (conn)); |
| |
| str2 = p; |
| while ((str2 = strchr (str2, '/'))) |
| *str2++ = ' '; |
| READ_STRING (conn.nettype); |
| READ_STRING (conn.addrtype); |
| READ_STRING (conn.address); |
| /* only read TTL for IP4 */ |
| if (strcmp (conn.addrtype, "IP4") == 0) |
| READ_UINT (conn.ttl); |
| READ_UINT (conn.addr_number); |
| |
| if (c->state == SDP_SESSION) { |
| gst_sdp_message_set_connection (c->msg, conn.nettype, conn.addrtype, |
| conn.address, conn.ttl, conn.addr_number); |
| } else { |
| gst_sdp_media_add_connection (c->media, conn.nettype, conn.addrtype, |
| conn.address, conn.ttl, conn.addr_number); |
| } |
| gst_sdp_connection_clear (&conn); |
| break; |
| } |
| case 'b': |
| { |
| gchar str2[32]; |
| |
| read_string_del (str, sizeof (str), ':', &p); |
| if (*p != '\0') |
| 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': |
| read_string_del (str, sizeof (str), ':', &p); |
| if (*p != '\0') |
| p++; |
| if (c->state == SDP_SESSION) |
| gst_sdp_message_set_key (c->msg, str, p); |
| else |
| gst_sdp_media_set_key (c->media, str, p); |
| 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=<media> <port>/<number of ports> <proto> <fmt> ... */ |
| 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 = 0; |
| } |
| 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: (array length=size): 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, *s; |
| SDPContext c; |
| gchar type; |
| gchar *buffer = NULL; |
| guint bufsize = 0; |
| guint len = 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; |
| |
| #define SIZE_CHECK_GUARD \ |
| G_STMT_START { \ |
| if (p - (gchar *) data >= size) \ |
| goto out; \ |
| } G_STMT_END |
| |
| p = (gchar *) data; |
| while (TRUE) { |
| while (p - (gchar *) data < size && g_ascii_isspace (*p)) |
| p++; |
| |
| SIZE_CHECK_GUARD; |
| |
| type = *p++; |
| if (type == '\0') |
| break; |
| |
| SIZE_CHECK_GUARD; |
| |
| if (*p != '=') |
| goto line_done; |
| p++; |
| |
| SIZE_CHECK_GUARD; |
| |
| s = p; |
| while (p - (gchar *) data < size && *p != '\n' && *p != '\r' && *p != '\0') |
| p++; |
| |
| len = p - s; |
| if (bufsize <= len) { |
| buffer = g_realloc (buffer, len + 1); |
| bufsize = len + 1; |
| } |
| memcpy (buffer, s, len); |
| buffer[len] = '\0'; |
| |
| gst_sdp_parse_line (&c, type, buffer); |
| |
| SIZE_CHECK_GUARD; |
| |
| line_done: |
| while (p - (gchar *) data < size && *p != '\n' && *p != '\0') |
| p++; |
| |
| SIZE_CHECK_GUARD; |
| |
| if (*p == '\n') |
| p++; |
| } |
| |
| #undef SIZE_CHECK_GUARD |
| |
| out: |
| g_free (buffer); |
| |
| return GST_SDP_OK; |
| } |
| |
| static void |
| print_media (GstSDPMedia * media) |
| { |
| g_print (" media: '%s'\n", GST_STR_NULL (media->media)); |
| g_print (" port: '%u'\n", media->port); |
| g_print (" num_ports: '%u'\n", media->num_ports); |
| g_print (" proto: '%s'\n", GST_STR_NULL (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", GST_STR_NULL (media->information)); |
| if (media->connections->len > 0) { |
| guint i; |
| |
| g_print (" connections:\n"); |
| for (i = 0; i < media->connections->len; i++) { |
| GstSDPConnection *conn = |
| &g_array_index (media->connections, GstSDPConnection, i); |
| |
| g_print (" nettype: '%s'\n", GST_STR_NULL (conn->nettype)); |
| g_print (" addrtype: '%s'\n", GST_STR_NULL (conn->addrtype)); |
| g_print (" address: '%s'\n", GST_STR_NULL (conn->address)); |
| g_print (" ttl: '%u'\n", conn->ttl); |
| g_print (" addr_number: '%u'\n", conn->addr_number); |
| } |
| } |
| if (media->bandwidths->len > 0) { |
| guint i; |
| |
| g_print (" bandwidths:\n"); |
| for (i = 0; i < media->bandwidths->len; i++) { |
| GstSDPBandwidth *bw = |
| &g_array_index (media->bandwidths, GstSDPBandwidth, i); |
| |
| g_print (" type: '%s'\n", GST_STR_NULL (bw->bwtype)); |
| g_print (" bandwidth: '%u'\n", bw->bandwidth); |
| } |
| } |
| g_print (" key:\n"); |
| g_print (" type: '%s'\n", GST_STR_NULL (media->key.type)); |
| g_print (" data: '%s'\n", GST_STR_NULL (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); |
| if (msg->bandwidths->len > 0) { |
| guint i; |
| |
| g_print (" bandwidths:\n"); |
| for (i = 0; i < msg->bandwidths->len; i++) { |
| GstSDPBandwidth *bw = |
| &g_array_index (msg->bandwidths, GstSDPBandwidth, i); |
| |
| g_print (" type: '%s'\n", GST_STR_NULL (bw->bwtype)); |
| g_print (" bandwidth: '%u'\n", bw->bandwidth); |
| } |
| } |
| 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; |
| } |
| |
| static const gchar * |
| gst_sdp_get_attribute_for_pt (const GstSDPMedia * media, const gchar * name, |
| gint pt) |
| { |
| guint i; |
| |
| for (i = 0;; i++) { |
| const gchar *attr; |
| gint val; |
| |
| if ((attr = gst_sdp_media_get_attribute_val_n (media, name, i)) == NULL) |
| break; |
| |
| if (sscanf (attr, "%d ", &val) != 1) |
| continue; |
| |
| if (val == pt) |
| return attr; |
| } |
| return NULL; |
| } |
| |
| /* this may modify the input string (and resets) */ |
| #define PARSE_INT(p, del, res) \ |
| G_STMT_START { \ |
| gchar *t = p; \ |
| p = strstr (p, del); \ |
| if (p == NULL) \ |
| res = -1; \ |
| else { \ |
| char prev = *p; \ |
| *p = '\0'; \ |
| res = atoi (t); \ |
| *p = prev; \ |
| p++; \ |
| } \ |
| } G_STMT_END |
| |
| /* this may modify the string without reset */ |
| #define PARSE_STRING(p, del, res) \ |
| G_STMT_START { \ |
| gchar *t = p; \ |
| p = strstr (p, del); \ |
| if (p == NULL) { \ |
| res = NULL; \ |
| p = t; \ |
| } \ |
| else { \ |
| *p = '\0'; \ |
| p++; \ |
| res = t; \ |
| } \ |
| } G_STMT_END |
| |
| #define SKIP_SPACES(p) \ |
| while (*p && g_ascii_isspace (*p)) \ |
| p++; |
| |
| /* rtpmap contains: |
| * |
| * <payload> <encoding_name>/<clock_rate>[/<encoding_params>] |
| */ |
| static gboolean |
| gst_sdp_parse_rtpmap (const gchar * rtpmap, gint * payload, gchar ** name, |
| gint * rate, gchar ** params) |
| { |
| gchar *p, *t, *orig_value; |
| |
| p = orig_value = g_strdup (rtpmap); |
| |
| PARSE_INT (p, " ", *payload); |
| if (*payload == -1) |
| goto fail; |
| |
| SKIP_SPACES (p); |
| if (*p == '\0') |
| goto fail; |
| |
| PARSE_STRING (p, "/", *name); |
| if (*name == NULL) { |
| GST_DEBUG ("no rate, name %s", p); |
| /* no rate, assume -1 then, this is not supposed to happen but RealMedia |
| * streams seem to omit the rate. */ |
| *name = g_strdup (p); |
| *rate = -1; |
| *params = NULL; |
| goto out; |
| } else { |
| *name = strdup (*name); |
| } |
| |
| t = p; |
| p = strstr (p, "/"); |
| if (p == NULL) { |
| *rate = atoi (t); |
| *params = NULL; |
| goto out; |
| } |
| p++; |
| *rate = atoi (t); |
| |
| if (*p == '\0') |
| *params = NULL; |
| else |
| *params = g_strdup (p); |
| |
| out: |
| g_free (orig_value); |
| return TRUE; |
| |
| fail: |
| g_free (orig_value); |
| return FALSE; |
| } |
| |
| /** |
| * gst_sdp_media_add_rtcp_fb_attributes_from_media: |
| * @media: a #GstSDPMedia |
| * @pt: payload type |
| * @caps: a #GstCaps |
| * |
| * Parse given @media for "rtcp-fb" attributes and add it to the @caps. |
| * |
| * Mapping of caps from SDP fields: |
| * |
| * a=rtcp-fb:(payload) (param1) [param2]... |
| * |
| * Returns: a #GstSDPResult. |
| */ |
| static GstSDPResult |
| gst_sdp_media_add_rtcp_fb_attributes_from_media (const GstSDPMedia * media, |
| gint pt, GstCaps * caps) |
| { |
| const gchar *rtcp_fb; |
| gchar *p, *to_free; |
| gint payload, i; |
| GstStructure *s; |
| |
| g_return_val_if_fail (media != NULL, GST_SDP_EINVAL); |
| g_return_val_if_fail (caps != NULL && GST_IS_CAPS (caps), GST_SDP_EINVAL); |
| |
| s = gst_caps_get_structure (caps, 0); |
| |
| for (i = 0;; i++) { |
| gboolean all_formats = FALSE; |
| |
| if ((rtcp_fb = gst_sdp_media_get_attribute_val_n (media, |
| "rtcp-fb", i)) == NULL) |
| break; |
| |
| /* p is now of the format <payload> attr... */ |
| to_free = p = g_strdup (rtcp_fb); |
| |
| /* check if it applies to all formats */ |
| if (*p == '*') { |
| p++; |
| all_formats = TRUE; |
| } else { |
| PARSE_INT (p, " ", payload); |
| } |
| |
| if (all_formats || (payload != -1 && payload == pt)) { |
| gchar *tmp, *key; |
| |
| SKIP_SPACES (p); |
| |
| /* replace space with '-' */ |
| key = g_strdup_printf ("rtcp-fb-%s", p); |
| |
| for (tmp = key; (tmp = strchr (tmp, ' ')); ++tmp) { |
| *tmp = '-'; |
| } |
| |
| gst_structure_set (s, key, G_TYPE_BOOLEAN, TRUE, NULL); |
| GST_DEBUG ("adding caps: %s=TRUE", key); |
| g_free (key); |
| } |
| g_free (to_free); |
| } |
| return GST_SDP_OK; |
| } |
| |
| /** |
| * gst_sdp_media_get_caps_from_media: |
| * @media: a #GstSDPMedia |
| * @pt: a payload type |
| * |
| * Mapping of caps from SDP fields: |
| * |
| * a=rtpmap:(payload) (encoding_name)/(clock_rate)[/(encoding_params)] |
| * |
| * a=framesize:(payload) (width)-(height) |
| * |
| * a=fmtp:(payload) (param)[=(value)];... |
| * |
| * Returns: a #GstCaps, or %NULL if an error happened |
| * |
| * Since: 1.8 |
| */ |
| GstCaps * |
| gst_sdp_media_get_caps_from_media (const GstSDPMedia * media, gint pt) |
| { |
| GstCaps *caps; |
| const gchar *rtpmap; |
| gchar *fmtp = NULL; |
| gchar *framesize = NULL; |
| gchar *name = NULL; |
| gint rate = -1; |
| gchar *params = NULL; |
| gchar *tmp; |
| GstStructure *s; |
| gint payload = 0; |
| gboolean ret; |
| |
| g_return_val_if_fail (media != NULL, NULL); |
| |
| /* get and parse rtpmap */ |
| rtpmap = gst_sdp_get_attribute_for_pt (media, "rtpmap", pt); |
| |
| if (rtpmap) { |
| ret = gst_sdp_parse_rtpmap (rtpmap, &payload, &name, &rate, ¶ms); |
| if (!ret) { |
| GST_ERROR ("error parsing rtpmap, ignoring"); |
| rtpmap = NULL; |
| } |
| } |
| /* dynamic payloads need rtpmap or we fail */ |
| if (rtpmap == NULL && pt >= 96) |
| goto no_rtpmap; |
| |
| /* check if we have a rate, if not, we need to look up the rate from the |
| * default rates based on the payload types. */ |
| if (rate == -1) { |
| const GstRTPPayloadInfo *info; |
| |
| if (GST_RTP_PAYLOAD_IS_DYNAMIC (pt)) { |
| /* dynamic types, use media and encoding_name */ |
| tmp = g_ascii_strdown (media->media, -1); |
| info = gst_rtp_payload_info_for_name (tmp, name); |
| g_free (tmp); |
| } else { |
| /* static types, use payload type */ |
| info = gst_rtp_payload_info_for_pt (pt); |
| } |
| |
| if (info) { |
| if ((rate = info->clock_rate) == 0) |
| rate = -1; |
| } |
| /* we fail if we cannot find one */ |
| if (rate == -1) |
| goto no_rate; |
| } |
| |
| tmp = g_ascii_strdown (media->media, -1); |
| caps = gst_caps_new_simple ("application/x-unknown", |
| "media", G_TYPE_STRING, tmp, "payload", G_TYPE_INT, pt, NULL); |
| g_free (tmp); |
| s = gst_caps_get_structure (caps, 0); |
| |
| gst_structure_set (s, "clock-rate", G_TYPE_INT, rate, NULL); |
| |
| /* encoding name must be upper case */ |
| if (name != NULL) { |
| tmp = g_ascii_strup (name, -1); |
| gst_structure_set (s, "encoding-name", G_TYPE_STRING, tmp, NULL); |
| g_free (tmp); |
| } |
| |
| /* params must be lower case */ |
| if (params != NULL) { |
| tmp = g_ascii_strdown (params, -1); |
| gst_structure_set (s, "encoding-params", G_TYPE_STRING, tmp, NULL); |
| g_free (tmp); |
| } |
| |
| /* parse optional fmtp: field */ |
| if ((fmtp = g_strdup (gst_sdp_get_attribute_for_pt (media, "fmtp", pt)))) { |
| gchar *p; |
| gint payload = 0; |
| |
| p = fmtp; |
| |
| /* p is now of the format <payload> <param>[=<value>];... */ |
| PARSE_INT (p, " ", payload); |
| if (payload != -1 && payload == pt) { |
| gchar **pairs; |
| gint i; |
| |
| /* <param>[=<value>] are separated with ';' */ |
| pairs = g_strsplit (p, ";", 0); |
| for (i = 0; pairs[i]; i++) { |
| gchar *valpos; |
| const gchar *val, *key; |
| gint j; |
| const gchar *reserved_keys[] = |
| { "media", "payload", "clock-rate", "encoding-name", |
| "encoding-params" |
| }; |
| |
| /* the key may not have a '=', the value can have other '='s */ |
| valpos = strstr (pairs[i], "="); |
| if (valpos) { |
| /* we have a '=' and thus a value, remove the '=' with \0 */ |
| *valpos = '\0'; |
| /* value is everything between '=' and ';'. We split the pairs at ; |
| * boundaries so we can take the remainder of the value. Some servers |
| * put spaces around the value which we strip off here. Alternatively |
| * we could strip those spaces in the depayloaders should these spaces |
| * actually carry any meaning in the future. */ |
| val = g_strstrip (valpos + 1); |
| } else { |
| /* simple <param>;.. is translated into <param>=1;... */ |
| val = "1"; |
| } |
| /* strip the key of spaces, convert key to lowercase but not the value. */ |
| key = g_strstrip (pairs[i]); |
| |
| /* skip keys from the fmtp, which we already use ourselves for the |
| * caps. Some software is adding random things like clock-rate into |
| * the fmtp, and we would otherwise here set a string-typed clock-rate |
| * in the caps... and thus fail to create valid RTP caps |
| */ |
| for (j = 0; j < G_N_ELEMENTS (reserved_keys); j++) { |
| if (g_ascii_strcasecmp (reserved_keys[j], key) == 0) { |
| key = ""; |
| break; |
| } |
| } |
| |
| if (strlen (key) > 1) { |
| tmp = g_ascii_strdown (key, -1); |
| gst_structure_set (s, tmp, G_TYPE_STRING, val, NULL); |
| g_free (tmp); |
| } |
| } |
| g_strfreev (pairs); |
| } |
| } |
| |
| /* parse framesize: field */ |
| if ((framesize = |
| g_strdup (gst_sdp_media_get_attribute_val (media, "framesize")))) { |
| gchar *p; |
| |
| /* p is now of the format <payload> <width>-<height> */ |
| p = framesize; |
| |
| PARSE_INT (p, " ", payload); |
| if (payload != -1 && payload == pt) { |
| gst_structure_set (s, "a-framesize", G_TYPE_STRING, p, NULL); |
| } |
| } |
| |
| /* parse rtcp-fb: field */ |
| gst_sdp_media_add_rtcp_fb_attributes_from_media (media, pt, caps); |
| |
| out: |
| g_free (framesize); |
| g_free (fmtp); |
| g_free (name); |
| g_free (params); |
| return caps; |
| |
| /* ERRORS */ |
| no_rtpmap: |
| { |
| GST_ERROR ("rtpmap type not given for dynamic payload %d", pt); |
| caps = NULL; |
| goto out; |
| } |
| no_rate: |
| { |
| GST_ERROR ("rate unknown for payload type %d", pt); |
| caps = NULL; |
| goto out; |
| } |
| } |
| |
| /** |
| * gst_sdp_media_set_media_from_caps: |
| * @caps: a #GstCaps |
| * @media: a #GstSDPMedia |
| * |
| * Mapping of caps to SDP fields: |
| * |
| * a=rtpmap:(payload) (encoding_name) or (clock_rate)[or (encoding_params)] |
| * |
| * a=framesize:(payload) (width)-(height) |
| * |
| * a=fmtp:(payload) (param)[=(value)];... |
| * |
| * a=rtcp-fb:(payload) (param1) [param2]... |
| * |
| * Returns: a #GstSDPResult. |
| * |
| * Since: 1.8 |
| */ |
| GstSDPResult |
| gst_sdp_media_set_media_from_caps (const GstCaps * caps, GstSDPMedia * media) |
| { |
| const gchar *caps_str, *caps_enc, *caps_params; |
| gchar *tmp; |
| gint caps_pt, caps_rate; |
| guint n_fields, j; |
| gboolean first, nack, nack_pli, ccm_fir; |
| GString *fmtp; |
| GstStructure *s; |
| |
| g_return_val_if_fail (media != NULL, GST_SDP_EINVAL); |
| g_return_val_if_fail (caps != NULL && GST_IS_CAPS (caps), GST_SDP_EINVAL); |
| |
| s = gst_caps_get_structure (caps, 0); |
| if (s == NULL) { |
| GST_ERROR ("ignoring stream without media type"); |
| goto error; |
| } |
| |
| /* get media type and payload for the m= line */ |
| caps_str = gst_structure_get_string (s, "media"); |
| gst_sdp_media_set_media (media, caps_str); |
| |
| gst_structure_get_int (s, "payload", &caps_pt); |
| tmp = g_strdup_printf ("%d", caps_pt); |
| gst_sdp_media_add_format (media, tmp); |
| g_free (tmp); |
| |
| /* get clock-rate, media type and params for the rtpmap attribute */ |
| gst_structure_get_int (s, "clock-rate", &caps_rate); |
| caps_enc = gst_structure_get_string (s, "encoding-name"); |
| caps_params = gst_structure_get_string (s, "encoding-params"); |
| |
| if (caps_enc) { |
| if (caps_params) |
| tmp = g_strdup_printf ("%d %s/%d/%s", caps_pt, caps_enc, caps_rate, |
| caps_params); |
| else |
| tmp = g_strdup_printf ("%d %s/%d", caps_pt, caps_enc, caps_rate); |
| |
| gst_sdp_media_add_attribute (media, "rtpmap", tmp); |
| g_free (tmp); |
| } |
| |
| /* get rtcp-fb attributes */ |
| if (gst_structure_get_boolean (s, "rtcp-fb-nack", &nack)) { |
| if (nack) { |
| tmp = g_strdup_printf ("%d nack", caps_pt); |
| gst_sdp_media_add_attribute (media, "rtcp-fb", tmp); |
| g_free (tmp); |
| GST_DEBUG ("adding rtcp-fb-nack to pt=%d", caps_pt); |
| } |
| } |
| |
| if (gst_structure_get_boolean (s, "rtcp-fb-nack-pli", &nack_pli)) { |
| if (nack_pli) { |
| tmp = g_strdup_printf ("%d nack pli", caps_pt); |
| gst_sdp_media_add_attribute (media, "rtcp-fb", tmp); |
| g_free (tmp); |
| GST_DEBUG ("adding rtcp-fb-nack-pli to pt=%d", caps_pt); |
| } |
| } |
| |
| if (gst_structure_get_boolean (s, "rtcp-fb-ccm-fir", &ccm_fir)) { |
| if (ccm_fir) { |
| tmp = g_strdup_printf ("%d ccm fir", caps_pt); |
| gst_sdp_media_add_attribute (media, "rtcp-fb", tmp); |
| g_free (tmp); |
| GST_DEBUG ("adding rtcp-fb-ccm-fir to pt=%d", caps_pt); |
| } |
| } |
| |
| /* collect all other properties and add them to fmtp or attributes */ |
| fmtp = g_string_new (""); |
| g_string_append_printf (fmtp, "%d ", caps_pt); |
| first = TRUE; |
| n_fields = gst_structure_n_fields (s); |
| for (j = 0; j < n_fields; j++) { |
| const gchar *fname, *fval; |
| |
| fname = gst_structure_nth_field_name (s, j); |
| |
| /* filter out standard properties */ |
| if (!strcmp (fname, "media")) |
| continue; |
| if (!strcmp (fname, "payload")) |
| continue; |
| if (!strcmp (fname, "clock-rate")) |
| continue; |
| if (!strcmp (fname, "encoding-name")) |
| continue; |
| if (!strcmp (fname, "encoding-params")) |
| continue; |
| if (!strcmp (fname, "ssrc")) |
| continue; |
| if (!strcmp (fname, "timestamp-offset")) |
| continue; |
| if (!strcmp (fname, "seqnum-offset")) |
| continue; |
| if (g_str_has_prefix (fname, "srtp-")) |
| continue; |
| if (g_str_has_prefix (fname, "srtcp-")) |
| continue; |
| /* handled later */ |
| if (g_str_has_prefix (fname, "x-gst-rtsp-server-rtx-time")) |
| continue; |
| if (g_str_has_prefix (fname, "rtcp-fb-")) |
| continue; |
| |
| if (!strcmp (fname, "a-framesize")) { |
| /* a-framesize attribute */ |
| if ((fval = gst_structure_get_string (s, fname))) { |
| tmp = g_strdup_printf ("%d %s", caps_pt, fval); |
| gst_sdp_media_add_attribute (media, fname + 2, tmp); |
| g_free (tmp); |
| } |
| continue; |
| } |
| |
| if (g_str_has_prefix (fname, "a-")) { |
| /* attribute */ |
| if ((fval = gst_structure_get_string (s, fname))) |
| gst_sdp_media_add_attribute (media, fname + 2, fval); |
| continue; |
| } |
| if (g_str_has_prefix (fname, "x-")) { |
| /* attribute */ |
| if ((fval = gst_structure_get_string (s, fname))) |
| gst_sdp_media_add_attribute (media, fname, fval); |
| continue; |
| } |
| |
| if ((fval = gst_structure_get_string (s, fname))) { |
| g_string_append_printf (fmtp, "%s%s=%s", first ? "" : ";", fname, fval); |
| first = FALSE; |
| } |
| } |
| |
| if (!first) { |
| tmp = g_string_free (fmtp, FALSE); |
| gst_sdp_media_add_attribute (media, "fmtp", tmp); |
| g_free (tmp); |
| } else { |
| g_string_free (fmtp, TRUE); |
| } |
| |
| return GST_SDP_OK; |
| |
| /* ERRORS */ |
| error: |
| { |
| GST_DEBUG ("ignoring stream"); |
| return GST_SDP_EINVAL; |
| } |
| } |
| |
| /** |
| * gst_sdp_make_keymgmt: |
| * @uri: a #gchar URI |
| * @base64: a #gchar base64-encoded key data |
| * |
| * Makes key management data |
| * |
| * Returns: (transfer full): a #gchar key-mgmt data, |
| * |
| * Since: 1.8 |
| */ |
| gchar * |
| gst_sdp_make_keymgmt (const gchar * uri, const gchar * base64) |
| { |
| g_return_val_if_fail (uri != NULL, NULL); |
| g_return_val_if_fail (base64 != NULL, NULL); |
| |
| return g_strdup_printf ("prot=mikey;uri=\"%s\";data=\"%s\"", uri, base64); |
| } |
| |
| static gboolean |
| gst_sdp_parse_keymgmt (const gchar * keymgmt, GstMIKEYMessage ** mikey) |
| { |
| gsize size; |
| guchar *data; |
| gchar *orig_value; |
| gchar *p, *kmpid; |
| |
| p = orig_value = g_strdup (keymgmt); |
| |
| SKIP_SPACES (p); |
| if (*p == '\0') { |
| g_free (orig_value); |
| return FALSE; |
| } |
| |
| PARSE_STRING (p, " ", kmpid); |
| if (kmpid == NULL || !g_str_equal (kmpid, "mikey")) { |
| g_free (orig_value); |
| return FALSE; |
| } |
| data = g_base64_decode (p, &size); |
| g_free (orig_value); /* Don't need this any more */ |
| |
| if (data == NULL) |
| return FALSE; |
| |
| *mikey = gst_mikey_message_new_from_data (data, size, NULL, NULL); |
| g_free (data); |
| |
| return (*mikey != NULL); |
| } |
| |
| static GstSDPResult |
| sdp_add_attributes_to_keymgmt (GArray * attributes, GstMIKEYMessage ** mikey) |
| { |
| GstSDPResult res = GST_SDP_OK; |
| |
| if (attributes->len > 0) { |
| guint i; |
| for (i = 0; i < attributes->len; i++) { |
| GstSDPAttribute *attr = &g_array_index (attributes, GstSDPAttribute, i); |
| |
| if (g_str_equal (attr->key, "key-mgmt")) { |
| res = gst_sdp_parse_keymgmt (attr->value, mikey); |
| break; |
| } |
| } |
| } |
| |
| return res; |
| } |
| |
| /** |
| * gst_sdp_message_parse_keymgmt: |
| * @msg: a #GstSDPMessage |
| * @mikey: (out) (transfer full): pointer to new #GstMIKEYMessage |
| * |
| * Creates a new #GstMIKEYMessage after parsing the key-mgmt attribute |
| * from a #GstSDPMessage. |
| * |
| * Returns: a #GstSDPResult. |
| * |
| * Since: 1.8.1 |
| */ |
| GstSDPResult |
| gst_sdp_message_parse_keymgmt (const GstSDPMessage * msg, |
| GstMIKEYMessage ** mikey) |
| { |
| g_return_val_if_fail (msg != NULL, GST_SDP_EINVAL); |
| |
| return sdp_add_attributes_to_keymgmt (msg->attributes, mikey); |
| } |
| |
| /** |
| * gst_sdp_media_parse_keymgmt: |
| * @media: a #GstSDPMedia |
| * @mikey: (out) (transfer full): pointer to new #GstMIKEYMessage |
| * |
| * Creates a new #GstMIKEYMessage after parsing the key-mgmt attribute |
| * from a #GstSDPMedia. |
| * |
| * Returns: a #GstSDPResult. |
| * |
| * Since: 1.8.1 |
| */ |
| GstSDPResult |
| gst_sdp_media_parse_keymgmt (const GstSDPMedia * media, |
| GstMIKEYMessage ** mikey) |
| { |
| g_return_val_if_fail (media != NULL, GST_SDP_EINVAL); |
| |
| return sdp_add_attributes_to_keymgmt (media->attributes, mikey); |
| } |
| |
| static GstSDPResult |
| sdp_add_attributes_to_caps (GArray * attributes, GstCaps * caps) |
| { |
| if (attributes->len > 0) { |
| GstStructure *s; |
| guint i; |
| |
| s = gst_caps_get_structure (caps, 0); |
| |
| for (i = 0; i < attributes->len; i++) { |
| GstSDPAttribute *attr = &g_array_index (attributes, GstSDPAttribute, i); |
| gchar *tofree, *key; |
| |
| key = attr->key; |
| |
| /* skip some of the attribute we already handle */ |
| if (!strcmp (key, "fmtp")) |
| continue; |
| if (!strcmp (key, "rtpmap")) |
| continue; |
| if (!strcmp (key, "control")) |
| continue; |
| if (!strcmp (key, "range")) |
| continue; |
| if (!strcmp (key, "framesize")) |
| continue; |
| if (!strcmp (key, "key-mgmt")) |
| continue; |
| |
| /* string must be valid UTF8 */ |
| if (!g_utf8_validate (attr->value, -1, NULL)) |
| continue; |
| |
| if (!g_str_has_prefix (key, "x-")) |
| tofree = key = g_strdup_printf ("a-%s", key); |
| else |
| tofree = NULL; |
| |
| GST_DEBUG ("adding caps: %s=%s", key, attr->value); |
| gst_structure_set (s, key, G_TYPE_STRING, attr->value, NULL); |
| g_free (tofree); |
| } |
| } |
| |
| return GST_SDP_OK; |
| } |
| |
| /** |
| * gst_sdp_message_attributes_to_caps: |
| * @msg: a #GstSDPMessage |
| * @caps: a #GstCaps |
| * |
| * Mapping of attributes of #GstSDPMessage to #GstCaps |
| * |
| * Returns: a #GstSDPResult. |
| * |
| * Since: 1.8 |
| */ |
| GstSDPResult |
| gst_sdp_message_attributes_to_caps (const GstSDPMessage * msg, GstCaps * caps) |
| { |
| GstSDPResult res; |
| GstMIKEYMessage *mikey = NULL; |
| |
| g_return_val_if_fail (msg != NULL, GST_SDP_EINVAL); |
| g_return_val_if_fail (caps != NULL && GST_IS_CAPS (caps), GST_SDP_EINVAL); |
| |
| res = gst_sdp_message_parse_keymgmt (msg, &mikey); |
| if (mikey) { |
| if (gst_mikey_message_to_caps (mikey, caps)) { |
| res = GST_SDP_EINVAL; |
| goto done; |
| } |
| } |
| |
| res = sdp_add_attributes_to_caps (msg->attributes, caps); |
| |
| done: |
| if (mikey) |
| gst_mikey_message_unref (mikey); |
| return res; |
| } |
| |
| /** |
| * gst_sdp_media_attributes_to_caps: |
| * @media: a #GstSDPMedia |
| * @caps: a #GstCaps |
| * |
| * Mapping of attributes of #GstSDPMedia to #GstCaps |
| * |
| * Returns: a #GstSDPResult. |
| * |
| * Since: 1.8 |
| */ |
| GstSDPResult |
| gst_sdp_media_attributes_to_caps (const GstSDPMedia * media, GstCaps * caps) |
| { |
| GstSDPResult res; |
| GstMIKEYMessage *mikey = NULL; |
| |
| g_return_val_if_fail (media != NULL, GST_SDP_EINVAL); |
| g_return_val_if_fail (caps != NULL && GST_IS_CAPS (caps), GST_SDP_EINVAL); |
| |
| res = gst_sdp_media_parse_keymgmt (media, &mikey); |
| if (mikey) { |
| if (!gst_mikey_message_to_caps (mikey, caps)) { |
| res = GST_SDP_EINVAL; |
| goto done; |
| } |
| } |
| |
| res = sdp_add_attributes_to_caps (media->attributes, caps); |
| |
| done: |
| if (mikey) |
| gst_mikey_message_unref (mikey); |
| return res; |
| } |