Commit 30abe669 authored by Youness Alaoui's avatar Youness Alaoui

Adding new SDP parsing and generation API.

This adds nice_agent_set_stream_name, nice_agent_get_stream_name,
nice_agent_generate_local_sdp, nice_agent_parse_remote_sdp.
parent b1528dae
......@@ -2996,3 +2996,342 @@ nice_agent_set_software (NiceAgent *agent, const gchar *software)
agent_unlock ();
}
NICEAPI_EXPORT gboolean
nice_agent_set_stream_name (NiceAgent *agent, guint stream_id,
const gchar *name)
{
Stream *stream_to_name = NULL;
GSList *i;
gboolean ret = FALSE;
agent_lock();
if (name != NULL) {
for (i = agent->streams; i; i = i->next) {
Stream *stream = i->data;
if (stream->id != stream_id &&
g_strcmp0 (stream->name, name) == 0)
goto done;
else if (stream->id == stream_id)
stream_to_name = stream;
}
}
if (stream_to_name == NULL)
goto done;
if (stream_to_name->name)
g_free (stream_to_name->name);
stream_to_name->name = g_strdup (name);
ret = TRUE;
done:
agent_unlock();
return ret;
}
NICEAPI_EXPORT const gchar *
nice_agent_get_stream_name (NiceAgent *agent, guint stream_id)
{
Stream *stream;
gchar *name = NULL;
agent_lock();
stream = agent_find_stream (agent, stream_id);
if (stream == NULL)
goto done;
name = stream->name;
done:
agent_unlock();
return name;
}
static const gchar *
_cand_type_to_sdp (NiceCandidateType type) {
switch(type) {
case NICE_CANDIDATE_TYPE_SERVER_REFLEXIVE:
return "srflx";
case NICE_CANDIDATE_TYPE_PEER_REFLEXIVE:
return "prflx";
case NICE_CANDIDATE_TYPE_RELAYED:
return "relay";
case NICE_CANDIDATE_TYPE_HOST:
default:
return "host";
}
}
NICEAPI_EXPORT gchar *
nice_agent_generate_local_sdp (NiceAgent *agent)
{
GString * sdp = g_string_new (NULL);
GSList *i, *j, *k;
agent_lock();
for (i = agent->streams; i; i = i->next) {
Stream *stream = i->data;
NiceCandidate *default_candidate = NULL;
NiceAddress rtp, rtcp;
gchar ip4[INET6_ADDRSTRLEN];
nice_address_init (&rtp);
nice_address_set_ipv4 (&rtp, 0);
nice_address_init (&rtcp);
nice_address_set_ipv4 (&rtcp, 0);
/* Find default candidates */
for (j = stream->components; j; j = j->next) {
Component *component = j->data;
for (k = component->local_candidates; k; k = k->next) {
NiceCandidate *local_candidate = k->data;
if (local_candidate->component_id > 2)
continue;
/* Only check for ipv4 candidates */
if (nice_address_ip_version (&local_candidate->addr) != 4)
continue;
if (component->id == NICE_COMPONENT_TYPE_RTP) {
if (default_candidate == NULL ||
local_candidate->priority < default_candidate->priority) {
default_candidate = local_candidate;
rtp = local_candidate->addr;
}
} else if (component->id == NICE_COMPONENT_TYPE_RTCP &&
default_candidate != NULL &&
strncmp (local_candidate->foundation, default_candidate->foundation,
NICE_CANDIDATE_MAX_FOUNDATION) == 0) {
rtcp = local_candidate->addr;
break;
}
}
}
nice_address_to_string (&rtp, ip4);
g_string_append_printf (sdp, "m=%s %d RTP/AVP\n",
stream->name ? stream->name : "-", nice_address_get_port (&rtp));
g_string_append_printf (sdp, "c=IN IP4 %s\n", ip4);
if (nice_address_get_port (&rtcp) != 0)
g_string_append_printf (sdp, "a=rtcp:%d\n",
nice_address_get_port (&rtcp));
g_string_append_printf (sdp, "a=ice-ufrag:%s\n", stream->local_ufrag);
g_string_append_printf (sdp, "a=ice-pwd:%s\n", stream->local_password);
for (j = stream->components; j; j = j->next) {
Component *component = j->data;
for (k = component->local_candidates; k; k = k->next) {
NiceCandidate *cand = k->data;
nice_address_to_string (&cand->addr, ip4);
g_string_append_printf (sdp, "a=candidate:%.*s %d %s %d %s %d",
NICE_CANDIDATE_MAX_FOUNDATION, cand->foundation, cand->component_id,
cand->transport == NICE_CANDIDATE_TRANSPORT_UDP ? "UDP" : "???",
cand->priority, ip4, nice_address_get_port (&cand->addr));
g_string_append_printf (sdp, " typ %s", _cand_type_to_sdp (cand->type));
if (nice_address_is_valid (&cand->base_addr) &&
!nice_address_equal (&cand->addr, &cand->base_addr)) {
nice_address_to_string (&cand->base_addr, ip4);
g_string_append_printf (sdp, " raddr %s rport %d", ip4,
nice_address_get_port (&cand->base_addr));
}
g_string_append (sdp, "\n");
}
}
}
agent_unlock();
return g_string_free (sdp, FALSE);
}
NICEAPI_EXPORT gint
nice_agent_parse_remote_sdp (NiceAgent *agent, const gchar *sdp)
{
Stream *current_stream = NULL;
gchar **sdp_lines = NULL;
GSList *l;
gint i;
gint ret = 0;
agent_lock();
for (l = agent->streams; l; l = l->next) {
Stream *stream = l->data;
if (stream->name == NULL) {
ret = -1;
goto done;
}
}
sdp_lines = g_strsplit (sdp, "\n", 0);
for (i = 0; sdp_lines && sdp_lines[i]; i++) {
if (g_str_has_prefix (sdp_lines[i], "m=")) {
gchar *name = g_strdup (sdp_lines[i] + 2);
gchar *ptr = name;
while (*ptr != ' ' && *ptr != '\0') ptr++;
*ptr = 0;
current_stream = NULL;
for (l = agent->streams; l; l = l->next) {
Stream *stream = l->data;
if (g_strcmp0 (stream->name, name) == 0) {
current_stream = stream;
break;
}
}
g_free (name);
} else if (g_str_has_prefix (sdp_lines[i], "a=ice-ufrag:")) {
const gchar *ufrag = sdp_lines[i] + 12;
if (current_stream == NULL) {
ret = -1;
goto done;
}
g_strlcpy (current_stream->remote_ufrag, ufrag, NICE_STREAM_MAX_UFRAG);
} else if (g_str_has_prefix (sdp_lines[i], "a=ice-pwd:")) {
const gchar *pwd = sdp_lines[i] + 10;
if (current_stream == NULL) {
ret = -1;
goto done;
}
g_strlcpy (current_stream->remote_password, pwd, NICE_STREAM_MAX_PWD);
} else if (g_str_has_prefix (sdp_lines[i], "a=candidate:")) {
const gchar *candidate = sdp_lines[i] + 12;
int ntype = -1;
gchar **tokens = NULL;
const gchar *foundation = NULL;
guint component_id;
const gchar *transport = NULL;
guint32 priority;
const gchar *addr = NULL;
guint16 port;
const gchar *type = NULL;
const gchar *raddr = NULL;
guint16 rport;
static const gchar *type_names[] = {"host", "srflx", "prflx", "relay"};
guint j;
if (current_stream == NULL) {
ret = -1;
goto done;
}
tokens = g_strsplit (candidate, " ", 0);
for (j = 0; tokens && tokens[j]; j++) {
switch (j) {
case 0:
foundation = tokens[j];
break;
case 1:
component_id = (guint) g_ascii_strtoull (tokens[j], NULL, 10);
break;
case 2:
transport = tokens[j];
break;
case 3:
priority = (guint32) g_ascii_strtoull (tokens[j], NULL, 10);
break;
case 4:
addr = tokens[j];
break;
case 5:
port = (guint16) g_ascii_strtoull (tokens[j], NULL, 10);
break;
default:
if (tokens[j + 1] == NULL) {
g_strfreev(tokens);
ret = -1;
goto done;
}
if (g_strcmp0 (tokens[j], "typ") == 0) {
type = tokens[j + 1];
} else if (g_strcmp0 (tokens[j], "raddr") == 0) {
raddr = tokens[j + 1];
} else if (g_strcmp0 (tokens[j], "rport") == 0) {
rport = (guint16) g_ascii_strtoull (tokens[j + 1], NULL, 10);
}
j++;
break;
}
}
if (type == NULL) {
g_strfreev(tokens);
ret = -1;
goto done;
}
ntype = -1;
for (j = 0; j < G_N_ELEMENTS (type_names); j++) {
if (g_strcmp0 (type, type_names[j]) == 0) {
ntype = j;
break;
}
}
if (ntype == -1) {
g_strfreev(tokens);
ret = -1;
goto done;
}
if (g_strcmp0 (transport, "UDP") == 0) {
NiceCandidate *cand = NULL;
GSList *cands = NULL;
gint added;
cand = nice_candidate_new(ntype);
cand->component_id = component_id;
cand->stream_id = current_stream->id;
cand->transport = NICE_CANDIDATE_TRANSPORT_UDP;
g_strlcpy(cand->foundation, foundation, NICE_CANDIDATE_MAX_FOUNDATION);
cand->priority = priority;
if (!nice_address_set_from_string (&cand->addr, addr)) {
nice_candidate_free (cand);
g_strfreev(tokens);
ret = -1;
goto done;
}
nice_address_set_port (&cand->addr, port);
if (raddr && rport) {
if (!nice_address_set_from_string (&cand->base_addr, raddr)) {
nice_candidate_free (cand);
g_strfreev(tokens);
ret = -1;
goto done;
}
nice_address_set_port (&cand->base_addr, rport);
}
cands = g_slist_prepend (cands, cand);
added = nice_agent_set_remote_candidates (agent, current_stream->id,
component_id, cands);
g_slist_free_full(cands, (GDestroyNotify)&nice_candidate_free);
if (added > 0)
ret++;
}
g_strfreev(tokens);
}
}
done:
if (sdp_lines)
g_strfreev(sdp_lines);
agent_unlock();
return ret;
}
......@@ -778,7 +778,115 @@ void nice_agent_set_stream_tos (
* Since: 0.0.10
*
*/
void nice_agent_set_software (NiceAgent *agent, const gchar *software);
void nice_agent_set_software (
NiceAgent *agent,
const gchar *software);
/**
* nice_agent_set_stream_name:
* @agent: The #NiceAgent Object
* @stream_id: The ID of the stream to change
* @name: The new name of the stream or %NULL
*
* This function will assign a unique name to a stream.
* This is only useful when parsing and generating an SDP of the candidates.
*
* <para>See also: nice_agent_generate_local_sdp()</para>
* <para>See also: nice_agent_parse_remote_sdp()</para>
* <para>See also: nice_agent_get_stream_name()</para>
*
* Returns: %TRUE if the name has been set. %FALSE in case of error
* (invalid stream or duplicate name).
* Since: 0.1.4
*/
gboolean nice_agent_set_stream_name (
NiceAgent *agent,
guint stream_id,
const gchar *name);
/**
* nice_agent_get_stream_name:
* @agent: The #NiceAgent Object
* @stream_id: The ID of the stream to change
*
* This function will return the name assigned to a stream.
* <para>See also: nice_agent_set_stream_name()</para>
*
* Returns: The name of the stream. The name is only valid while the stream
* exists or until it changes through a call to nice_agent_set_stream_name().
*
*
* Since: 0.1.4
*/
const gchar *nice_agent_get_stream_name (
NiceAgent *agent,
guint stream_id);
/**
* nice_agent_generate_local_sdp:
* @agent: The #NiceAgent Object
*
* Generate an SDP string containing the local candidates and credentials.
*
<note>
<para>
The SDP will not contain any codec lines and the 'm' line will not list
any payload types.
</para>
<para>
It is highly recommended to set names on the streams prior to calling this
function. Unnamed streams will show up as '-' in the 'm' line, but the SDP
will not be parseable with nice_agent_parse_remote_sdp() if a stream is
unnamed.
</para>
<para>
The default candidate in the SDP will be selected based on the lowest
priority candidate.
</para>
</note>
*
* <para>See also: nice_agent_set_stream_name() </para>
* <para>See also: nice_agent_parse_remote_sdp() </para>
*
* Returns: A string representing the local SDP. Must be freed with g_free()
* once done.
*
* Since: 0.1.4
**/
gchar *
nice_agent_generate_local_sdp (
NiceAgent *agent);
/**
* nice_agent_parse_remote_sdp:
* @agent: The #NiceAgent Object
* @sdp: The remote SDP to parse
*
* Parse an SDP string and extracts candidates and credentials from it and sets
* them on the agent.
*
<note>
<para>
This function will return an error if a stream has not been assigned a name
with nice_agent_set_stream_name() as it becomes troublesome to assign the
streams from the agent to the streams in the SDP.
</para>
</note>
*
*
* <para>See also: nice_agent_set_stream_name() </para>
* <para>See also: nice_agent_generate_local_sdp() </para>
*
* Returns: The number of candidates added, negative on errors
*
* Since: 0.1.4
**/
int
nice_agent_parse_remote_sdp (
NiceAgent *agent,
const gchar *sdp);
G_END_DECLS
......
......@@ -71,6 +71,8 @@ stream_free (Stream *stream)
{
GSList *i;
if (stream->name)
g_free (stream->name);
for (i = stream->components; i; i = i->next) {
Component *component = i->data;
component_free (component);
......
......@@ -61,6 +61,7 @@ G_BEGIN_DECLS
struct _Stream
{
gchar *name;
guint id;
guint n_components;
gboolean initial_binding_request_received;
......
......@@ -28,6 +28,10 @@ nice_agent_set_selected_remote_candidate
nice_agent_set_stream_tos
nice_agent_set_software
nice_agent_restart
nice_agent_set_stream_name
nice_agent_get_stream_name
nice_agent_generate_local_sdp
nice_agent_parse_remote_sdp
<SUBSECTION Standard>
NICE_AGENT
NICE_IS_AGENT
......
......@@ -18,13 +18,16 @@ nice_agent_add_local_address
nice_agent_add_stream
nice_agent_attach_recv
nice_agent_gather_candidates
nice_agent_generate_local_sdp
nice_agent_get_local_candidates
nice_agent_get_local_credentials
nice_agent_get_remote_candidates
nice_agent_get_selected_pair
nice_agent_get_stream_name
nice_agent_get_type
nice_agent_new
nice_agent_new_reliable
nice_agent_parse_remote_sdp
nice_agent_remove_stream
nice_agent_restart
nice_agent_send
......@@ -35,6 +38,7 @@ nice_agent_set_remote_credentials
nice_agent_set_selected_pair
nice_agent_set_selected_remote_candidate
nice_agent_set_software
nice_agent_set_stream_name
nice_agent_set_stream_tos
nice_candidate_copy
nice_candidate_free
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment