audio: call hangup/resume support
[siplcs.git] / src / core / sipe-media.c
blobf1bd8bdbff7ac090205360f0ccb9847f3e9d4f1e
1 /**
2 * @file sipe-media.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 #ifdef HAVE_CONFIG_H
24 #include "config.h"
25 #endif
27 #include <glib.h>
28 #include <libpurple/mediamanager.h>
29 #include <nice/agent.h>
31 #include "sip-sec.h"
32 #include "sipe.h"
33 #include "sipmsg.h"
34 #include "sipe-session.h"
35 #include "sipe-media.h"
36 #include "sipe-dialog.h"
37 #include "sipe-utils.h"
38 #include "sipe-common.h"
40 typedef enum sipe_call_state {
41 SIPE_CALL_CONNECTING,
42 SIPE_CALL_RUNNING,
43 SIPE_CALL_HELD,
44 SIPE_CALL_FINISHED
45 } SipeCallState;
47 struct _sipe_media_call {
48 PurpleMedia *media;
49 struct sip_session *session;
50 struct sip_dialog *dialog;
51 GSList *sdp_attrs;
52 struct sipmsg *invitation;
53 GList *remote_candidates;
54 GList *remote_codecs;
55 gchar *sdp_response;
56 gboolean legacy_mode;
57 SipeCallState state;
60 gchar *
61 sipe_media_get_callid(sipe_media_call *call)
63 return call->dialog->callid;
66 static void
67 sipe_media_call_free(sipe_media_call *call)
69 if (call) {
70 sipe_utils_nameval_free(call->sdp_attrs);
71 if (call->invitation)
72 sipmsg_free(call->invitation);
73 purple_media_codec_list_free(call->remote_codecs);
74 purple_media_candidate_list_free(call->remote_candidates);
75 g_free(call);
79 static GList *
80 sipe_media_parse_remote_codecs(const sipe_media_call *call)
82 int i = 0;
83 const gchar *attr;
84 GList *codecs = NULL;
86 while ((attr = sipe_utils_nameval_find_instance(call->sdp_attrs, "a", i++))) {
87 gchar **tokens;
88 int id;
89 int clock_rate;
90 gchar *codec_name;
91 PurpleMediaCodec *codec;
93 if (!g_str_has_prefix(attr, "rtpmap:"))
94 continue;
96 tokens = g_strsplit_set(attr + 7, " /", 3);
98 id = atoi(tokens[0]);
99 codec_name = tokens[1];
100 clock_rate = atoi(tokens[2]);
102 codec = purple_media_codec_new(id, codec_name, PURPLE_MEDIA_AUDIO, clock_rate);
103 codecs = g_list_append(codecs, codec);
105 g_strfreev(tokens);
107 printf("REMOTE CODEC: %s\n",purple_media_codec_to_string(codec));
110 return codecs;
113 static gint
114 codec_name_compare(PurpleMediaCodec* codec1, PurpleMediaCodec* codec2)
116 gchar *name1 = purple_media_codec_get_encoding_name(codec1);
117 gchar *name2 = purple_media_codec_get_encoding_name(codec2);
119 return g_strcmp0(name1, name2);
122 static GList *
123 sipe_media_prune_remote_codecs(PurpleMedia *media, GList *codecs)
125 GList *remote_codecs = codecs;
126 GList *local_codecs = purple_media_get_codecs(media, "sipe-voice");
127 GList *pruned_codecs = NULL;
129 while (remote_codecs) {
130 PurpleMediaCodec *c = remote_codecs->data;
132 if (g_list_find_custom(local_codecs, c, (GCompareFunc)codec_name_compare)) {
133 pruned_codecs = g_list_append(pruned_codecs, c);
134 remote_codecs->data = NULL;
135 } else {
136 printf("Pruned codec %s\n", purple_media_codec_get_encoding_name(c));
139 remote_codecs = remote_codecs->next;
142 purple_media_codec_list_free(codecs);
144 return pruned_codecs;
147 static GList *
148 sipe_media_parse_remote_candidates(sipe_media_call *call)
150 GSList *sdp_attrs = call->sdp_attrs;
151 PurpleMediaCandidate *candidate;
152 GList *candidates = NULL;
153 const gchar *attr;
154 int i = 0;
156 gchar* username = NULL;
157 gchar* password = NULL;
159 while ((attr = sipe_utils_nameval_find_instance(sdp_attrs, "a", i++))) {
160 const char ICE_UFRAG[] = "ice-ufrag:";
161 const char ICE_PWD[] = "ice-pwd:";
162 const char CANDIDATE[] = "candidate:";
164 if (g_str_has_prefix(attr, ICE_UFRAG) && !username) {
165 username = g_strdup(attr + sizeof (ICE_UFRAG) - 1);
166 } else if (g_str_has_prefix(attr, ICE_PWD) && !password) {
167 password = g_strdup(attr + sizeof (ICE_PWD) - 1);
168 } else if (g_str_has_prefix(attr, CANDIDATE)) {
169 gchar **tokens;
170 gchar *foundation;
171 PurpleMediaComponentType component;
172 PurpleMediaNetworkProtocol protocol;
173 guint32 priority;
174 gchar* ip;
175 guint16 port;
176 PurpleMediaCandidateType type;
178 tokens = g_strsplit_set(attr + sizeof (CANDIDATE) - 1, " ", 0);
180 foundation = tokens[0];
182 switch (atoi(tokens[1])) {
183 case 1:
184 component = PURPLE_MEDIA_COMPONENT_RTP;
185 break;
186 case 2:
187 component = PURPLE_MEDIA_COMPONENT_RTCP;
188 break;
189 default:
190 component = PURPLE_MEDIA_COMPONENT_NONE;
193 if (sipe_strequal(tokens[2], "UDP"))
194 protocol = PURPLE_MEDIA_NETWORK_PROTOCOL_UDP;
195 else {
196 // Ignore TCP candidates, at least for now...
197 g_strfreev(tokens);
198 continue;
201 priority = atoi(tokens[3]);
202 ip = tokens[4];
203 port = atoi(tokens[5]);
205 if (sipe_strequal(tokens[7], "host"))
206 type = PURPLE_MEDIA_CANDIDATE_TYPE_HOST;
207 else if (sipe_strequal(tokens[7], "relay"))
208 type = PURPLE_MEDIA_CANDIDATE_TYPE_RELAY;
209 else if (sipe_strequal(tokens[7], "srflx"))
210 type = PURPLE_MEDIA_CANDIDATE_TYPE_SRFLX;
211 else {
212 g_strfreev(tokens);
213 continue;
216 candidate = purple_media_candidate_new(foundation, component,
217 type, protocol, ip, port);
218 g_object_set(candidate, "priority", priority, NULL);
219 candidates = g_list_append(candidates, candidate);
221 g_strfreev(tokens);
225 if (!candidates) {
226 // No a=candidate in SDP message, revert to OC2005 behaviour
227 gchar **tokens = g_strsplit(sipe_utils_nameval_find(sdp_attrs, "o"), " ", 6);
228 gchar *ip = g_strdup(tokens[5]);
229 guint port;
231 g_strfreev(tokens);
233 tokens = g_strsplit(sipe_utils_nameval_find(sdp_attrs, "m"), " ", 3);
234 port = atoi(tokens[1]);
235 g_strfreev(tokens);
237 candidate = purple_media_candidate_new("foundation",
238 PURPLE_MEDIA_COMPONENT_RTP,
239 PURPLE_MEDIA_CANDIDATE_TYPE_HOST,
240 PURPLE_MEDIA_NETWORK_PROTOCOL_UDP, ip, port);
241 candidates = g_list_append(candidates, candidate);
243 candidate = purple_media_candidate_new("foundation",
244 PURPLE_MEDIA_COMPONENT_RTCP,
245 PURPLE_MEDIA_CANDIDATE_TYPE_HOST,
246 PURPLE_MEDIA_NETWORK_PROTOCOL_UDP, ip, port + 1);
247 candidates = g_list_append(candidates, candidate);
249 // This seems to be pre-OC2007 R2 UAC
250 call->legacy_mode = TRUE;
253 if (username) {
254 GList *it = candidates;
255 while (it) {
256 g_object_set(it->data, "username", username, "password", password, NULL);
257 it = it->next;
261 g_free(username);
262 g_free(password);
264 return candidates;
267 static gchar *
268 sipe_media_sdp_codec_ids_format(GList *codecs)
270 GString *result = g_string_new(NULL);
272 while (codecs) {
273 PurpleMediaCodec *c = codecs->data;
275 gchar *tmp = g_strdup_printf(" %d", purple_media_codec_get_id(c));
276 g_string_append(result,tmp);
277 g_free(tmp);
279 codecs = codecs->next;
282 return g_string_free(result, FALSE);
285 static gchar *
286 sipe_media_sdp_codecs_format(GList *codecs)
288 GString *result = g_string_new(NULL);
290 while (codecs) {
291 PurpleMediaCodec *c = codecs->data;
292 GList *params = NULL;
294 gchar *tmp = g_strdup_printf("a=rtpmap:%d %s/%d\r\n",
295 purple_media_codec_get_id(c),
296 purple_media_codec_get_encoding_name(c),
297 purple_media_codec_get_clock_rate(c));
299 g_string_append(result, tmp);
300 g_free(tmp);
302 if ((params = purple_media_codec_get_optional_parameters(c))) {
303 tmp = g_strdup_printf("a=fmtp:%d",purple_media_codec_get_id(c));
304 g_string_append(result, tmp);
305 g_free(tmp);
307 while (params) {
308 PurpleKeyValuePair* par = params->data;
309 tmp = g_strdup_printf(" %s=%s", par->key, (gchar*) par->value);
310 g_string_append(result, tmp);
311 g_free(tmp);
312 params = params->next;
314 g_string_append(result, "\r\n");
317 codecs = codecs->next;
320 return g_string_free(result, FALSE);
323 static gchar *
324 sipe_media_sdp_candidates_format(GList *candidates, sipe_media_call* call, gboolean remote_candidate)
326 GString *result = g_string_new("");
327 gchar *tmp;
328 gchar *username = purple_media_candidate_get_username(candidates->data);
329 gchar *password = purple_media_candidate_get_password(candidates->data);
330 guint16 rtcp_port = 0;
332 if (call->legacy_mode)
333 return g_string_free(result, FALSE);
335 tmp = g_strdup_printf("a=ice-ufrag:%s\r\na=ice-pwd:%s\r\n",username, password);
336 g_string_append(result, tmp);
337 g_free(tmp);
339 while (candidates) {
340 PurpleMediaCandidate *c = candidates->data;
342 guint16 port;
343 guint16 component;
344 gchar *protocol;
345 gchar *type;
347 port = purple_media_candidate_get_port(c);
349 switch (purple_media_candidate_get_component_id(c)) {
350 case PURPLE_MEDIA_COMPONENT_RTP:
351 component = 1;
352 break;
353 case PURPLE_MEDIA_COMPONENT_RTCP:
354 component = 2;
355 if (rtcp_port == 0)
356 rtcp_port = port;
357 break;
360 switch (purple_media_candidate_get_protocol(c)) {
361 case PURPLE_MEDIA_NETWORK_PROTOCOL_TCP:
362 protocol = "TCP";
363 break;
364 case PURPLE_MEDIA_NETWORK_PROTOCOL_UDP:
365 protocol = "UDP";
366 break;
369 switch (purple_media_candidate_get_candidate_type(c)) {
370 case PURPLE_MEDIA_CANDIDATE_TYPE_HOST:
371 type = "host";
372 break;
373 case PURPLE_MEDIA_CANDIDATE_TYPE_RELAY:
374 type = "relay";
375 break;
376 case PURPLE_MEDIA_CANDIDATE_TYPE_SRFLX:
377 type = "srflx";
378 break;
379 default:
380 // TODO: error unknown/unsupported type
381 break;
384 tmp = g_strdup_printf("a=candidate:%s %u %s %u %s %d typ %s \r\n",
385 purple_media_candidate_get_foundation(c),
386 component,
387 protocol,
388 purple_media_candidate_get_priority(c),
389 purple_media_candidate_get_ip(c),
390 port,
391 type);
393 g_string_append(result, tmp);
394 g_free(tmp);
396 candidates = candidates->next;
399 if (remote_candidate) {
400 PurpleMediaCandidate *first = call->remote_candidates->data;
401 PurpleMediaCandidate *second = call->remote_candidates->next->data;
402 tmp = g_strdup_printf("a=remote-candidates:1 %s %u 2 %s %u\r\n",
403 purple_media_candidate_get_ip(first), purple_media_candidate_get_port(first),
404 purple_media_candidate_get_ip(second), purple_media_candidate_get_port(second));
406 g_string_append(result, tmp);
407 g_free(tmp);
411 if (rtcp_port != 0) {
412 tmp = g_strdup_printf("a=maxptime:200\r\na=rtcp:%u\r\n", rtcp_port);
413 g_string_append(result, tmp);
414 g_free(tmp);
417 return g_string_free(result, FALSE);
420 static gchar*
421 sipe_media_create_sdp(sipe_media_call *call, gboolean remote_candidate) {
422 PurpleMedia *media = call->media;
423 GList *local_codecs = purple_media_get_codecs(media, "sipe-voice");
424 GList *local_candidates = purple_media_get_local_candidates(media, "sipe-voice", call->dialog->with);
426 // TODO: more sophisticated
427 guint16 local_port = purple_media_candidate_get_port(local_candidates->data);
428 const char *ip = sipe_utils_get_suitable_local_ip(-1);
430 gchar *sdp_codecs = sipe_media_sdp_codecs_format(local_codecs);
431 gchar *sdp_codec_ids = sipe_media_sdp_codec_ids_format(local_codecs);
432 gchar *sdp_candidates = sipe_media_sdp_candidates_format(local_candidates, call, remote_candidate);
433 gchar *inactive = call->state == SIPE_CALL_HELD ? "a=inactive\r\n" : "";
435 gchar *body = g_strdup_printf(
436 "v=0\r\n"
437 "o=- 0 0 IN IP4 %s\r\n"
438 "s=session\r\n"
439 "c=IN IP4 %s\r\n"
440 "b=CT:99980\r\n"
441 "t=0 0\r\n"
442 "m=audio %d RTP/AVP%s\r\n"
443 "%s"
444 "%s"
445 "%s"
446 "a=encryption:rejected\r\n"
447 ,ip, ip, local_port, sdp_codec_ids, sdp_candidates, inactive, sdp_codecs);
449 g_free(sdp_codecs);
450 g_free(sdp_codec_ids);
451 g_free(sdp_candidates);
453 return body;
456 static void
457 sipe_media_session_ready_cb(sipe_media_call *call)
459 PurpleMedia *media = call->media;
460 PurpleAccount *account = purple_media_get_account(media);
462 if (!purple_media_candidates_prepared(media, NULL, NULL))
463 return;
465 if (!call->sdp_response)
466 call->sdp_response = sipe_media_create_sdp(call, FALSE);
468 if (!purple_media_accepted(media, NULL, NULL)) {
469 if (!call->legacy_mode)
470 send_sip_response(account->gc, call->invitation, 183, "Session Progress", call->sdp_response);
471 } else {
472 send_sip_response(account->gc, call->invitation, 200, "OK", call->sdp_response);
473 call->state = SIPE_CALL_RUNNING;
477 static void
478 sipe_invite_call(struct sipe_account_data *sip)
480 gchar *hdr;
481 gchar *contact;
482 gchar *body;
483 sipe_media_call *call = sip->media_call;
484 struct sip_dialog *dialog = call->dialog;
486 contact = get_contact(sip);
487 hdr = g_strdup_printf(
488 "Supported: ms-sender\r\n"
489 "ms-keep-alive: UAC;hop-hop=yes\r\n"
490 "Contact: %s%s\r\n"
491 "Supported: Replaces\r\n"
492 "Content-Type: application/sdp\r\n",
493 contact,
494 call->state == SIPE_CALL_HELD ? ";+sip.rendering=\"no\"" : "");
495 g_free(contact);
497 body = sipe_media_create_sdp(call, TRUE);
499 send_sip_request(sip->gc, "INVITE", dialog->with, dialog->with, hdr, body,
500 dialog, NULL);
502 g_free(body);
503 g_free(hdr);
506 static void
507 notify_state_change(struct sipe_account_data *sip, gboolean local) {
508 if (local) {
509 sipe_invite_call(sip);
510 } else {
511 gchar* body = sipe_media_create_sdp(sip->media_call, TRUE);
512 send_sip_response(sip->gc, sip->media_call->invitation, 200, "OK", body);
513 g_free(body);
517 static void
518 sipe_media_stream_info_cb(PurpleMedia *media,
519 PurpleMediaInfoType type,
520 SIPE_UNUSED_PARAMETER gchar *sid,
521 SIPE_UNUSED_PARAMETER gchar *name,
522 gboolean local, struct sipe_account_data *sip)
524 sipe_media_call *call = sip->media_call;
526 if (type == PURPLE_MEDIA_INFO_ACCEPT)
527 sipe_media_session_ready_cb(call);
528 else if (type == PURPLE_MEDIA_INFO_REJECT) {
529 PurpleAccount *account = purple_media_get_account(media);
530 send_sip_response(account->gc, call->invitation, 603, "Decline", NULL);
531 sipe_media_call_free(call);
532 sip->media_call = NULL;
533 } else if (type == PURPLE_MEDIA_INFO_HOLD) {
534 if (call->state == SIPE_CALL_HELD)
535 return;
537 call->state = SIPE_CALL_HELD;
538 notify_state_change(sip, local);
539 purple_media_stream_info(media, PURPLE_MEDIA_INFO_HOLD, NULL, NULL, TRUE);
541 } else if (type == PURPLE_MEDIA_INFO_UNHOLD) {
542 if (call->state == SIPE_CALL_RUNNING)
543 return;
545 call->state = SIPE_CALL_RUNNING;
546 notify_state_change(sip, local);
548 purple_media_stream_info(media, PURPLE_MEDIA_INFO_UNHOLD, NULL, NULL, TRUE);
549 } else if (type == PURPLE_MEDIA_INFO_HANGUP) {
550 call->state = SIPE_CALL_FINISHED;
551 if (local)
552 send_sip_request(sip->gc, "BYE", call->dialog->with, call->dialog->with,
553 NULL, NULL, call->dialog, NULL);
554 sipe_media_call_free(call);
555 sip->media_call = NULL;
559 static GSList *
560 sipe_media_parse_sdp_frame(gchar *frame)
562 gchar **lines = g_strsplit(frame, "\r\n", 0);
563 GSList *sdp_attrs = NULL;
565 gboolean result = sipe_utils_parse_lines(&sdp_attrs, lines, "=");
566 g_strfreev(lines);
568 if (result == FALSE) {
569 sipe_utils_nameval_free(sdp_attrs);
570 return NULL;
573 return sdp_attrs;
576 static struct sip_dialog *
577 sipe_media_dialog_init(struct sip_session* session, struct sipmsg *msg)
579 gchar *newTag = gentag();
580 const gchar *oldHeader;
581 gchar *newHeader;
582 struct sip_dialog *dialog;
584 oldHeader = sipmsg_find_header(msg, "To");
585 newHeader = g_strdup_printf("%s;tag=%s", oldHeader, newTag);
586 sipmsg_remove_header_now(msg, "To");
587 sipmsg_add_header_now(msg, "To", newHeader);
588 g_free(newHeader);
590 dialog = sipe_dialog_add(session);
591 dialog->callid = g_strdup(session->callid);
592 dialog->with = parse_from(sipmsg_find_header(msg, "From"));
593 sipe_dialog_parse(dialog, msg, FALSE);
595 return dialog;
598 static sipe_media_call *
599 sipe_media_call_init(struct sipmsg *msg)
601 sipe_media_call *call;
603 call = g_new0(sipe_media_call, 1);
604 call->sdp_attrs = sipe_media_parse_sdp_frame(msg->body);
605 call->invitation = msg;
606 call->legacy_mode = FALSE;
607 call->state = SIPE_CALL_CONNECTING;
608 call->remote_candidates = sipe_media_parse_remote_candidates(call);
609 return call;
612 void sipe_media_hold(struct sipe_account_data *sip) {
613 if (sip->media_call) {
614 purple_media_stream_info(sip->media_call->media, PURPLE_MEDIA_INFO_HOLD,
615 NULL, NULL, FALSE);
619 void sipe_media_unhold(struct sipe_account_data *sip) {
620 if (sip->media_call) {
621 purple_media_stream_info(sip->media_call->media, PURPLE_MEDIA_INFO_UNHOLD,
622 NULL, NULL, FALSE);
626 void sipe_media_incoming_invite(struct sipe_account_data *sip, struct sipmsg *msg)
628 PurpleMediaManager *manager = purple_media_manager_get();
629 PurpleMedia *media;
631 const gchar *callid = sipmsg_find_header(msg, "Call-ID");
633 sipe_media_call *call;
634 struct sip_session *session;
635 struct sip_dialog *dialog;
637 GParameter *params;
639 if (sip->media_call) {
640 if (sipe_strequal(sip->media_call->dialog->callid, callid)) {
641 gchar *rsp;
642 int i = 0;
643 const gchar *attr;
645 call = sip->media_call;
647 sipmsg_free(call->invitation);
648 msg->dont_free = TRUE;
649 call->invitation = msg;
651 sipmsg_add_header(msg, "Supported", "Replaces");
653 sipe_utils_nameval_free(call->sdp_attrs);
654 call->sdp_attrs = NULL;
655 call->sdp_attrs = sipe_media_parse_sdp_frame(msg->body);
657 if (call->legacy_mode && call->state == SIPE_CALL_RUNNING) {
658 sipe_media_hold(sip);
659 return;
662 while ((attr = sipe_utils_nameval_find_instance(call->sdp_attrs, "a", i++))) {
663 if (g_str_has_prefix("inactive", attr)) {
664 sipe_media_hold(sip);
665 return;
669 if (call->state == SIPE_CALL_HELD) {
670 sipe_media_unhold(sip);
671 return;
674 call->remote_codecs = sipe_media_parse_remote_codecs(call);
675 call->remote_codecs = sipe_media_prune_remote_codecs(call->media, call->remote_codecs);
676 if (!call->remote_codecs) {
677 // TODO: error no remote codecs
679 if (purple_media_set_remote_codecs(call->media, "sipe-voice", call->dialog->with,
680 call->remote_codecs) == FALSE)
681 printf("ERROR SET REMOTE CODECS"); // TODO
683 rsp = sipe_media_create_sdp(sip->media_call, TRUE);
684 send_sip_response(sip->gc, msg, 200, "OK", rsp);
685 g_free(rsp);
686 } else {
687 // TODO: send Busy Here
688 printf("MEDIA SESSION ALREADY IN PROGRESS");
690 return;
693 call = sipe_media_call_init(msg);
695 session = sipe_session_find_or_add_chat_by_callid(sip, callid);
696 dialog = sipe_media_dialog_init(session, msg);
698 media = purple_media_manager_create_media(manager, sip->account,
699 "fsrtpconference", dialog->with, FALSE);
701 g_signal_connect(G_OBJECT(media), "stream-info",
702 G_CALLBACK(sipe_media_stream_info_cb), sip);
703 g_signal_connect_swapped(G_OBJECT(media), "candidates-prepared",
704 G_CALLBACK(sipe_media_session_ready_cb), call);
707 call->session = session;
708 call->dialog = dialog;
709 call->media = media;
711 if (call->legacy_mode) {
712 purple_media_add_stream(media, "sipe-voice", dialog->with,
713 PURPLE_MEDIA_AUDIO, FALSE, "rawudp", 0, NULL);
714 } else {
715 params = g_new0(GParameter, 2);
716 params[0].name = "controlling-mode";
717 g_value_init(&params[0].value, G_TYPE_BOOLEAN);
718 g_value_set_boolean(&params[0].value, FALSE);
719 params[1].name = "compatibility-mode";
720 g_value_init(&params[1].value, G_TYPE_UINT);
721 g_value_set_uint(&params[1].value, NICE_COMPATIBILITY_OC2007R2);
724 purple_media_add_stream(media, "sipe-voice", dialog->with,
725 PURPLE_MEDIA_AUDIO, FALSE, "nice", 2, params);
728 purple_media_add_remote_candidates(media, "sipe-voice", dialog->with,
729 call->remote_candidates);
731 call->remote_codecs = sipe_media_parse_remote_codecs(call);
732 call->remote_codecs = sipe_media_prune_remote_codecs(media, call->remote_codecs);
733 if (!call->remote_candidates || !call->remote_codecs) {
734 sipe_media_call_free(call);
735 sip->media_call = NULL;
736 printf("ERROR NO CANDIDATES OR CODECS");
737 return;
739 if (purple_media_set_remote_codecs(media, "sipe-voice", dialog->with,
740 call->remote_codecs) == FALSE)
741 printf("ERROR SET REMOTE CODECS"); // TODO
743 sip->media_call = call;
745 // TODO: copy message instead of this don't free thing
746 msg->dont_free = TRUE;
747 send_sip_response(sip->gc, msg, 180, "Ringing", NULL);
750 void sipe_media_hangup(struct sipe_account_data *sip)
752 if (sip->media_call) {
753 purple_media_stream_info(sip->media_call->media, PURPLE_MEDIA_INFO_HANGUP,
754 NULL, NULL, FALSE);
759 Local Variables:
760 mode: c
761 c-file-style: "bsd"
762 indent-tabs-mode: t
763 tab-width: 8
764 End: