Adding new SDP parsing and generation API.
authorYouness Alaoui <youness.alaoui@collabora.co.uk>
Tue, 5 Feb 2013 04:15:00 +0000 (23:15 -0500)
committerYouness Alaoui <youness.alaoui@collabora.co.uk>
Tue, 5 Feb 2013 04:21:30 +0000 (23:21 -0500)
This adds nice_agent_set_stream_name, nice_agent_get_stream_name,
nice_agent_generate_local_sdp, nice_agent_parse_remote_sdp.

agent/agent.c
agent/agent.h
agent/stream.c
agent/stream.h
docs/reference/libnice/libnice-sections.txt
nice/libnice.sym

index df7f4be..a25bc9a 100644 (file)
@@ -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;
+}
index 0b2847d..082d598 100644 (file)
@@ -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
 
index 1276ae2..870cdda 100644 (file)
@@ -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);
index e2f5e39..5808ba5 100644 (file)
@@ -61,6 +61,7 @@ G_BEGIN_DECLS
 
 struct _Stream
 {
+  gchar *name;
   guint id;
   guint n_components;
   gboolean initial_binding_request_received;
index 03241ff..043e8d8 100644 (file)
@@ -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
index 79cd303..ebbf570 100644 (file)
@@ -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