audio: extract SDP formatting functionality into sdpmsg.c
[siplcs.git] / src / core / sdpmsg.c
blobe17f61ee96fa4468ecd33fdbf4cc9b70e56ab580
1 /**
2 * @file sdpmsg.c
4 * pidgin-sipe
6 * Copyright (C) 2010 Jakub Adam <jakub.adam@tieto.com>
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, write to the Free Software
20 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
23 #include <stdio.h>
24 #include <stdlib.h>
26 #include <glib.h>
28 #include "sipe-backend.h"
29 #include "sdpmsg.h"
30 #include "sipe-utils.h"
32 static gboolean
33 parse_attributes(struct sdpmsg *smsg, gchar *msg) {
34 gchar **lines = g_strsplit(msg, "\r\n", 0);
35 GSList *attributes = NULL;
36 gchar **ptr;
38 for (ptr = lines; *ptr != NULL; ++ptr) {
39 if (g_str_has_prefix(*ptr, "a=")) {
40 gchar **parts = g_strsplit(*ptr + 2, ":", 2);
41 if(!parts[0]) {
42 g_strfreev(parts);
43 g_strfreev(lines);
44 sipe_utils_nameval_free(attributes);
45 return FALSE;
46 break;
48 attributes = sipe_utils_nameval_add(attributes, parts[0], parts[1]);
49 g_strfreev(parts);
51 } else if (g_str_has_prefix(*ptr, "o=")) {
52 gchar **parts = g_strsplit(*ptr + 2, " ", 6);
53 smsg->ip = g_strdup(parts[5]);
54 g_strfreev(parts);
55 } else if (g_str_has_prefix(*ptr, "m=")) {
56 gchar **parts = g_strsplit(*ptr + 2, " ", 3);
57 smsg->port = atoi(parts[1]);
58 g_strfreev(parts);
62 g_strfreev(lines);
64 smsg->attributes = attributes;
65 return TRUE;
68 static void sdpcandidate_free(struct sdpcandidate *candidate);
70 static GSList *
71 parse_candidates(GSList *attrs)
73 GSList *candidates = NULL;
74 const gchar *attr;
75 int i = 0;
77 while ((attr = sipe_utils_nameval_find_instance(attrs, "candidate", i++))) {
78 struct sdpcandidate *candidate = g_new0(struct sdpcandidate, 1);
79 gchar **tokens = g_strsplit_set(attr, " ", 0);
81 candidate->foundation = g_strdup(tokens[0]);
83 switch (atoi(tokens[1])) {
84 case 1:
85 candidate->component = SIPE_COMPONENT_RTP;
86 break;
87 case 2:
88 candidate->component = SIPE_COMPONENT_RTCP;
89 break;
90 default:
91 candidate->component = SIPE_COMPONENT_NONE;
94 if (sipe_strequal(tokens[2], "UDP"))
95 candidate->protocol = SIPE_NETWORK_PROTOCOL_UDP;
96 else {
97 // Ignore TCP candidates, at least for now...
98 // Also, if this is ICEv6 candidate list, candidates are dropped here
99 g_strfreev(tokens);
100 sdpcandidate_free(candidate);
101 continue;
104 candidate->priority = atoi(tokens[3]);
105 candidate->ip = g_strdup(tokens[4]);
106 candidate->port = atoi(tokens[5]);
108 if (sipe_strequal(tokens[7], "host"))
109 candidate->type = SIPE_CANDIDATE_TYPE_HOST;
110 else if (sipe_strequal(tokens[7], "relay"))
111 candidate->type = SIPE_CANDIDATE_TYPE_RELAY;
112 else if (sipe_strequal(tokens[7], "srflx"))
113 candidate->type = SIPE_CANDIDATE_TYPE_SRFLX;
114 else if (sipe_strequal(tokens[7], "prflx"))
115 candidate->type = SIPE_CANDIDATE_TYPE_PRFLX;
116 else {
117 g_strfreev(tokens);
118 sdpcandidate_free(candidate);
119 continue;
122 candidates = g_slist_append(candidates, candidate);
124 g_strfreev(tokens);
127 return candidates;
130 static GSList *
131 create_legacy_candidates(gchar *ip, guint16 port)
133 struct sdpcandidate *candidate;
134 GSList *candidates = NULL;
136 candidate = g_new0(struct sdpcandidate, 1);
137 candidate->foundation = g_strdup("1");
138 candidate->component = SIPE_COMPONENT_RTP;
139 candidate->type = SIPE_CANDIDATE_TYPE_HOST;
140 candidate->protocol = SIPE_NETWORK_PROTOCOL_UDP;
141 candidate->ip = g_strdup(ip);
142 candidate->port = port;
144 candidates = g_slist_append(candidates, candidate);
146 candidate = g_new0(struct sdpcandidate, 1);
147 candidate->foundation = g_strdup("1");
148 candidate->component = SIPE_COMPONENT_RTCP;
149 candidate->type = SIPE_CANDIDATE_TYPE_HOST;
150 candidate->protocol = SIPE_NETWORK_PROTOCOL_UDP;
151 candidate->ip = g_strdup(ip);
152 candidate->port = port + 1;
154 candidates = g_slist_append(candidates, candidate);
156 return candidates;
159 static GSList *
160 parse_codecs(GSList *attrs)
162 int i = 0;
163 const gchar *attr;
164 GSList *codecs = NULL;
166 while ((attr = sipe_utils_nameval_find_instance(attrs, "rtpmap", i++))) {
167 struct sdpcodec *codec = g_new0(struct sdpcodec, 1);
168 gchar **tokens = g_strsplit_set(attr, " /", 3);
170 int j = 0;
171 const gchar* params;
173 codec->id = atoi(tokens[0]);
174 codec->name = g_strdup(tokens[1]);
175 codec->clock_rate = atoi(tokens[2]);
176 codec->type = SIPE_MEDIA_AUDIO;
178 // TODO: more secure and effective implementation
179 while((params = sipe_utils_nameval_find_instance(attrs, "fmtp", j++))) {
180 gchar **tokens = g_strsplit_set(params, " ", 0);
181 gchar **next = tokens + 1;
183 if (atoi(tokens[0]) == codec->id) {
184 while (*next) {
185 gchar name[50];
186 gchar value[50];
188 if (sscanf(*next, "%[a-zA-Z0-9]=%s", name, value) == 2)
189 codec->parameters = sipe_utils_nameval_add(codec->parameters, name, value);
191 ++next;
195 g_strfreev(tokens);
198 codecs = g_slist_append(codecs, codec);
199 g_strfreev(tokens);
202 return codecs;
205 struct sdpmsg *
206 sdpmsg_parse_msg(gchar *msg)
208 struct sdpmsg *smsg = g_new0(struct sdpmsg, 1);
209 smsg->legacy = FALSE;
211 if (!parse_attributes(smsg, msg)) {
212 sdpmsg_free(smsg);
213 return NULL;
216 smsg->candidates = parse_candidates(smsg->attributes);
217 if (!smsg->candidates) {
218 // No a=candidate in SDP message, this seems to be pre-OC2007 R2 UAC
219 smsg->candidates = create_legacy_candidates(smsg->ip, smsg->port);
220 smsg->legacy = TRUE;
223 smsg->codecs = parse_codecs(smsg->attributes);
225 return smsg;
228 static gchar *
229 codecs_to_string(GSList *codecs)
231 GString *result = g_string_new(NULL);
233 for (; codecs; codecs = codecs->next) {
234 struct sdpcodec *c = codecs->data;
235 GSList *params = c->parameters;
237 g_string_append_printf(result,
238 "a=rtpmap:%d %s/%d\r\n",
239 c->id,
240 c->name,
241 c->clock_rate);
243 if (params) {
244 g_string_append_printf(result, "a=fmtp:%d", c->id);
246 for (; params; params = params->next) {
247 struct sipnameval* par = params->data;
248 g_string_append_printf(result, " %s=%s",
249 par->name, par->value);
252 g_string_append(result, "\r\n");
256 return g_string_free(result, FALSE);
259 static gchar *
260 codec_ids_to_string(GSList *codecs)
262 GString *result = g_string_new(NULL);
264 for (; codecs; codecs = codecs->next) {
265 struct sdpcodec *c = codecs->data;
266 g_string_append_printf(result, " %d", c->id);
269 return g_string_free(result, FALSE);
272 static gchar *
273 candidates_to_string(GSList *candidates)
275 GString *result = g_string_new("");
277 for (; candidates; candidates = candidates->next) {
278 struct sdpcandidate *c = candidates->data;
279 const gchar *protocol;
280 const gchar *type;
281 gchar *related = NULL;
283 switch (c->protocol) {
284 case SIPE_NETWORK_PROTOCOL_TCP:
285 protocol = "TCP";
286 break;
287 case SIPE_NETWORK_PROTOCOL_UDP:
288 protocol = "UDP";
289 break;
292 switch (c->type) {
293 case SIPE_CANDIDATE_TYPE_HOST:
294 type = "host";
295 break;
296 case SIPE_CANDIDATE_TYPE_RELAY:
297 type = "relay";
298 break;
299 case SIPE_CANDIDATE_TYPE_SRFLX:
300 type = "srflx";
301 related = g_strdup_printf("raddr %s rport %d",
302 c->base_ip,
303 c->base_port);
304 break;
305 case SIPE_CANDIDATE_TYPE_PRFLX:
306 type = "prflx";
307 break;
308 default:
309 // TODO: error unknown/unsupported type
310 break;
313 g_string_append_printf(result,
314 "a=candidate:%s %u %s %u %s %d typ %s %s\r\n",
315 c->foundation,
316 c->component,
317 protocol,
318 c->priority,
319 c->ip,
320 c->port,
321 type,
322 related ? related : "");
324 g_free(related);
327 return g_string_free(result, FALSE);
330 static gint
331 candidate_compare_by_component_id(struct sdpcandidate *c1,
332 struct sdpcandidate *c2)
334 return c1->component - c2->component;
337 static gchar *
338 remote_candidates_to_string(GSList *candidates)
340 GString *result = g_string_new("");
342 candidates = g_slist_copy(candidates);
343 candidates = g_slist_sort(candidates,
344 (GCompareFunc)candidate_compare_by_component_id);
346 if (candidates) {
347 GSList *i;
348 g_string_append(result, "a=remote-candidates:");
350 for (i = candidates; i; i = i->next) {
351 struct sdpcandidate *c = i->data;
352 g_string_append_printf(result, "%u %s %u ",
353 c->component, c->ip, c->port);
356 g_string_append(result, "\r\n");
359 g_slist_free(candidates);
361 return g_string_free(result, FALSE);
364 static gchar *
365 attributes_to_string(GSList *attributes)
367 GString *result = g_string_new("");
369 for (; attributes; attributes = attributes->next) {
370 struct sipnameval *a = attributes->data;
371 g_string_append_printf(result, "a=%s", a->name);
372 if (a->value)
373 g_string_append_printf(result, ":%s", a->value);
374 g_string_append(result, "\r\n");
377 return g_string_free(result, FALSE);
380 gchar *
381 sdpmsg_to_string(const struct sdpmsg *msg)
383 gchar *body = NULL;
385 gchar *codecs_str = codecs_to_string(msg->codecs);
386 gchar *codec_ids_str = codec_ids_to_string(msg->codecs);
388 gchar *candidates_str = msg->legacy ? g_strdup("")
389 : candidates_to_string(msg->candidates);
390 gchar *remote_candidates_str = remote_candidates_to_string(msg->remote_candidates);
392 gchar *attributes_str = attributes_to_string(msg->attributes);
394 body = g_strdup_printf(
395 "v=0\r\n"
396 "o=- 0 0 IN IP4 %s\r\n"
397 "s=session\r\n"
398 "c=IN IP4 %s\r\n"
399 "b=CT:99980\r\n"
400 "t=0 0\r\n"
401 "m=audio %d RTP/AVP%s\r\n"
402 "%s"
403 "%s"
404 "%s"
405 "%s",
406 msg->ip, msg->ip, msg->port, codec_ids_str,
407 candidates_str, remote_candidates_str,
408 codecs_str,
409 attributes_str);
411 g_free(codecs_str);
412 g_free(codec_ids_str);
413 g_free(candidates_str);
414 g_free(remote_candidates_str);
415 g_free(attributes_str);
417 return body;
420 static void
421 sdpcandidate_free(struct sdpcandidate *candidate)
423 if (candidate) {
424 g_free(candidate->foundation);
425 g_free(candidate->ip);
426 g_free(candidate->base_ip);
427 g_free(candidate);
431 static void
432 sdpcodec_free(struct sdpcodec *codec)
434 if (codec) {
435 g_free(codec->name);
436 sipe_utils_nameval_free(codec->parameters);
437 g_free(codec);
441 void
442 sdpmsg_free(struct sdpmsg *msg)
444 if (msg) {
445 GSList *item;
447 sipe_utils_nameval_free(msg->attributes);
449 for (item = msg->candidates; item; item = item->next)
450 sdpcandidate_free(item->data);
451 g_slist_free(msg->candidates);
453 for (item = msg->remote_candidates; item; item = item->next)
454 sdpcandidate_free(item->data);
455 g_slist_free(msg->remote_candidates);
457 for (item = msg->codecs; item; item = item->next)
458 sdpcodec_free(item->data);
459 g_slist_free(msg->codecs);
461 g_free(msg->ip);
462 g_free(msg);
467 Local Variables:
468 mode: c
469 c-file-style: "bsd"
470 indent-tabs-mode: t
471 tab-width: 8
472 End: