mirror of
https://gitlab.freedesktop.org/gstreamer/gstreamer.git
synced 2024-12-28 19:20:35 +00:00
1381e3e2be
The extmap attribute allows mapping RTP extension header IDs to well-known RTP extension header specifications. See RFC8285 for details. We store the extmap attribute either as string in the caps extmap-X=extensionname where X is the integer extension header ID, or as 3-tuple of strings extmap-X=<direction,extensionname,extensionattributes> where direction or extensionattributes are allowed to be the empty string. Both formats are allowed because usually only the extension name is given and it's much simpler to handle in caps.
4327 lines
108 KiB
C
4327 lines
108 KiB
C
/* 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_new_from_text:
|
|
* @msg: (out) (transfer full): pointer to new #GstSDPMessage
|
|
* @text: A dynamically allocated string representing the SDP description
|
|
*
|
|
* Parse @text and create a new SDPMessage from these.
|
|
*
|
|
* Returns: a #GstSDPResult.
|
|
* Since: 1.16
|
|
*/
|
|
GstSDPResult
|
|
gst_sdp_message_new_from_text (const gchar * text, GstSDPMessage ** msg)
|
|
{
|
|
GstSDPResult res;
|
|
|
|
if ((res = gst_sdp_message_new (msg)) != GST_SDP_OK)
|
|
return res;
|
|
|
|
res =
|
|
gst_sdp_message_parse_buffer ((const guint8 *) text, strlen (text), *msg);
|
|
|
|
return res;
|
|
}
|
|
|
|
/**
|
|
* 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);
|
|
|
|
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)];...
|
|
*
|
|
* Note that the extmap attribute is set only by gst_sdp_media_attributes_to_caps().
|
|
*
|
|
* 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]...
|
|
*
|
|
* a=extmap:(id)[/direction] (extensionname) (extensionattributes)
|
|
*
|
|
* 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, extmap 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;
|
|
}
|
|
|
|
/* extmap */
|
|
if (g_str_has_prefix (fname, "extmap-")) {
|
|
gchar *endptr;
|
|
guint id = strtoull (fname + 7, &endptr, 10);
|
|
const GValue *arr;
|
|
|
|
if (*endptr != '\0' || id == 0 || id == 15 || id > 9999)
|
|
continue;
|
|
|
|
if ((fval = gst_structure_get_string (s, fname))) {
|
|
gchar *extmap = g_strdup_printf ("%u %s", id, fval);
|
|
gst_sdp_media_add_attribute (media, "extmap", extmap);
|
|
g_free (extmap);
|
|
} else if ((arr = gst_structure_get_value (s, fname))
|
|
&& G_VALUE_HOLDS (arr, GST_TYPE_ARRAY)
|
|
&& gst_value_array_get_size (arr) == 3) {
|
|
const GValue *val;
|
|
const gchar *direction, *extensionname, *extensionattributes;
|
|
|
|
val = gst_value_array_get_value (arr, 0);
|
|
direction = g_value_get_string (val);
|
|
|
|
val = gst_value_array_get_value (arr, 1);
|
|
extensionname = g_value_get_string (val);
|
|
|
|
val = gst_value_array_get_value (arr, 2);
|
|
extensionattributes = g_value_get_string (val);
|
|
|
|
if (!extensionname || *extensionname == '\0')
|
|
continue;
|
|
|
|
if (direction && *direction != '\0' && extensionattributes
|
|
&& *extensionattributes != '\0') {
|
|
gchar *extmap =
|
|
g_strdup_printf ("%u/%s %s %s", id, direction, extensionname,
|
|
extensionattributes);
|
|
gst_sdp_media_add_attribute (media, "extmap", extmap);
|
|
g_free (extmap);
|
|
} else if (direction && *direction != '\0') {
|
|
gchar *extmap =
|
|
g_strdup_printf ("%u/%s %s", id, direction, extensionname);
|
|
gst_sdp_media_add_attribute (media, "extmap", extmap);
|
|
g_free (extmap);
|
|
} else if (extensionattributes && *extensionattributes != '\0') {
|
|
gchar *extmap = g_strdup_printf ("%u %s %s", id, extensionname,
|
|
extensionattributes);
|
|
gst_sdp_media_add_attribute (media, "extmap", extmap);
|
|
g_free (extmap);
|
|
} else {
|
|
gchar *extmap = g_strdup_printf ("%u %s", id, extensionname);
|
|
gst_sdp_media_add_attribute (media, "extmap", extmap);
|
|
g_free (extmap);
|
|
}
|
|
}
|
|
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;
|
|
if (!strcmp (key, "extmap"))
|
|
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;
|
|
}
|
|
|
|
static GstSDPResult
|
|
gst_sdp_media_add_extmap_attributes (GArray * attributes, GstCaps * caps)
|
|
{
|
|
const gchar *extmap;
|
|
gchar *p, *tmp, *to_free;
|
|
guint id, i;
|
|
GstStructure *s;
|
|
|
|
g_return_val_if_fail (attributes != 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 < attributes->len; i++) {
|
|
GstSDPAttribute *attr;
|
|
const gchar *direction, *extensionname, *extensionattributes;
|
|
|
|
attr = &g_array_index (attributes, GstSDPAttribute, i);
|
|
if (strcmp (attr->key, "extmap") != 0)
|
|
continue;
|
|
|
|
extmap = attr->value;
|
|
|
|
/* p is now of the format id[/direction] extensionname [extensionattributes] */
|
|
to_free = p = g_strdup (extmap);
|
|
|
|
id = strtoul (p, &tmp, 10);
|
|
if (id == 0 || id == 15 || id > 9999 || (*tmp != ' ' && *tmp != '/')) {
|
|
GST_ERROR ("Invalid extmap '%s'", to_free);
|
|
goto next;
|
|
} else if (*tmp == '/') {
|
|
p = tmp;
|
|
p++;
|
|
|
|
PARSE_STRING (p, " ", direction);
|
|
|
|
/* Invalid format */
|
|
if (direction == NULL || *direction == '\0') {
|
|
GST_ERROR ("Invalid extmap '%s'", to_free);
|
|
goto next;
|
|
}
|
|
} else {
|
|
/* At the space */
|
|
p = tmp;
|
|
direction = "";
|
|
}
|
|
|
|
SKIP_SPACES (p);
|
|
|
|
tmp = strstr (p, " ");
|
|
if (tmp == NULL) {
|
|
extensionname = p;
|
|
extensionattributes = "";
|
|
} else {
|
|
extensionname = p;
|
|
*tmp = '\0';
|
|
p = tmp + 1;
|
|
SKIP_SPACES (p);
|
|
extensionattributes = p;
|
|
}
|
|
|
|
if (extensionname == NULL || *extensionname == '\0') {
|
|
GST_ERROR ("Invalid extmap '%s'", to_free);
|
|
goto next;
|
|
}
|
|
|
|
if (*direction != '\0' || *extensionattributes != '\0') {
|
|
GValue arr = G_VALUE_INIT;
|
|
GValue val = G_VALUE_INIT;
|
|
gchar *key;
|
|
|
|
key = g_strdup_printf ("extmap-%u", id);
|
|
|
|
g_value_init (&arr, GST_TYPE_ARRAY);
|
|
g_value_init (&val, G_TYPE_STRING);
|
|
|
|
g_value_set_string (&val, direction);
|
|
gst_value_array_append_value (&arr, &val);
|
|
|
|
g_value_set_string (&val, extensionname);
|
|
gst_value_array_append_value (&arr, &val);
|
|
|
|
g_value_set_string (&val, extensionattributes);
|
|
gst_value_array_append_value (&arr, &val);
|
|
|
|
gst_structure_set_value (s, key, &arr);
|
|
g_value_unset (&val);
|
|
g_value_unset (&arr);
|
|
GST_DEBUG ("adding caps: %s=<%s,%s,%s>", key, direction, extensionname,
|
|
extensionattributes);
|
|
g_free (key);
|
|
} else {
|
|
gchar *key;
|
|
|
|
key = g_strdup_printf ("extmap-%u", id);
|
|
gst_structure_set (s, key, G_TYPE_STRING, extensionname, NULL);
|
|
GST_DEBUG ("adding caps: %s=%s", key, extensionname);
|
|
g_free (key);
|
|
}
|
|
|
|
next:
|
|
g_free (to_free);
|
|
}
|
|
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);
|
|
|
|
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);
|
|
|
|
if (res == GST_SDP_OK) {
|
|
/* parse global extmap field */
|
|
res = gst_sdp_media_add_extmap_attributes (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);
|
|
|
|
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);
|
|
|
|
if (res == GST_SDP_OK) {
|
|
/* parse media extmap field */
|
|
res = gst_sdp_media_add_extmap_attributes (media->attributes, caps);
|
|
}
|
|
|
|
done:
|
|
if (mikey)
|
|
gst_mikey_message_unref (mikey);
|
|
return res;
|
|
}
|