audio: start of removing purple specific code from core
[siplcs.git] / src / core / sipe-media.c
blob20c5c71194790cad3e592c85a26312c03fcefc1c
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 <string.h>
29 #include <libpurple/mediamanager.h>
30 #include <nice/agent.h>
32 #include "sipe-core.h"
33 #include "sip-sec.h"
34 #include "sipe.h"
35 #include "sipmsg.h"
36 #include "sipe-session.h"
37 #include "sipe-media.h"
38 #include "sipe-dialog.h"
39 #include "sipe-utils.h"
40 #include "sipe-common.h"
42 gchar *
43 sipe_media_get_callid(sipe_media_call *call)
45 return call->dialog->callid;
48 void sipe_media_codec_list_free(GList *codecs)
50 for (; codecs; codecs = g_list_delete_link(codecs, codecs)) {
51 sipe_backend_codec_free(codecs->data);
55 static void
56 sipe_media_call_free(sipe_media_call *call)
58 if (call) {
59 sipe_utils_nameval_free(call->sdp_attrs);
60 if (call->invitation)
61 sipmsg_free(call->invitation);
62 sipe_media_codec_list_free(call->remote_codecs);
63 purple_media_candidate_list_free(call->remote_candidates);
64 g_free(call);
68 static GList *
69 sipe_media_parse_remote_codecs(const sipe_media_call *call)
71 int i = 0;
72 const gchar *attr;
73 GList *codecs = NULL;
75 while ((attr = sipe_utils_nameval_find_instance(call->sdp_attrs, "rtpmap", i++))) {
76 gchar **tokens = g_strsplit_set(attr, " /", 3);
78 int id = atoi(tokens[0]);
79 gchar *name = tokens[1];
80 int clock_rate = atoi(tokens[2]);
81 SipeMediaType type = SIPE_MEDIA_AUDIO;
83 sipe_codec *codec = sipe_backend_codec_new(id, name, clock_rate, type);
85 codecs = g_list_append(codecs, codec);
86 g_strfreev(tokens);
89 return codecs;
92 static gint
93 codec_name_compare(sipe_codec* codec1, sipe_codec* codec2)
95 gchar *name1 = sipe_backend_codec_get_name(codec1);
96 gchar *name2 = sipe_backend_codec_get_name(codec2);
98 return g_strcmp0(name1, name2);
101 static GList *
102 sipe_media_prune_remote_codecs(sipe_media_call *call, GList *codecs)
104 GList *remote_codecs = codecs;
105 GList *local_codecs = sipe_backend_get_local_codecs(call);
106 GList *pruned_codecs = NULL;
108 while (remote_codecs) {
109 sipe_codec *c = remote_codecs->data;
111 if (g_list_find_custom(local_codecs, c, (GCompareFunc)codec_name_compare)) {
112 pruned_codecs = g_list_append(pruned_codecs, c);
113 remote_codecs->data = NULL;
114 } else {
115 printf("Pruned codec %s\n", sipe_backend_codec_get_name(c));
118 remote_codecs = remote_codecs->next;
121 sipe_media_codec_list_free(codecs);
123 return pruned_codecs;
126 static GList *
127 sipe_media_parse_remote_candidates(sipe_media_call *call)
129 GSList *sdp_attrs = call->sdp_attrs;
130 PurpleMediaCandidate *candidate;
131 GList *candidates = NULL;
132 const gchar *attr;
133 int i = 0;
135 const gchar* username = sipe_utils_nameval_find(sdp_attrs, "ice-ufrag");
136 const gchar* password = sipe_utils_nameval_find(sdp_attrs, "ice-pwd");
138 while ((attr = sipe_utils_nameval_find_instance(sdp_attrs, "candidate", i++))) {
139 gchar **tokens;
140 gchar *foundation;
141 PurpleMediaComponentType component;
142 PurpleMediaNetworkProtocol protocol;
143 guint32 priority;
144 gchar* ip;
145 guint16 port;
146 PurpleMediaCandidateType type;
148 tokens = g_strsplit_set(attr, " ", 0);
150 foundation = tokens[0];
152 switch (atoi(tokens[1])) {
153 case 1:
154 component = PURPLE_MEDIA_COMPONENT_RTP;
155 break;
156 case 2:
157 component = PURPLE_MEDIA_COMPONENT_RTCP;
158 break;
159 default:
160 component = PURPLE_MEDIA_COMPONENT_NONE;
163 if (sipe_strequal(tokens[2], "UDP"))
164 protocol = PURPLE_MEDIA_NETWORK_PROTOCOL_UDP;
165 else {
166 // Ignore TCP candidates, at least for now...
167 g_strfreev(tokens);
168 continue;
171 priority = atoi(tokens[3]);
172 ip = tokens[4];
173 port = atoi(tokens[5]);
175 if (sipe_strequal(tokens[7], "host"))
176 type = PURPLE_MEDIA_CANDIDATE_TYPE_HOST;
177 else if (sipe_strequal(tokens[7], "relay"))
178 type = PURPLE_MEDIA_CANDIDATE_TYPE_RELAY;
179 else if (sipe_strequal(tokens[7], "srflx"))
180 type = PURPLE_MEDIA_CANDIDATE_TYPE_SRFLX;
181 else {
182 g_strfreev(tokens);
183 continue;
186 candidate = purple_media_candidate_new(foundation, component,
187 type, protocol, ip, port);
188 g_object_set(candidate, "priority", priority, NULL);
189 candidates = g_list_append(candidates, candidate);
191 g_strfreev(tokens);
194 if (!candidates) {
195 // No a=candidate in SDP message, revert to OC2005 behaviour
196 candidate = purple_media_candidate_new("foundation",
197 PURPLE_MEDIA_COMPONENT_RTP,
198 PURPLE_MEDIA_CANDIDATE_TYPE_HOST,
199 PURPLE_MEDIA_NETWORK_PROTOCOL_UDP,
200 call->remote_ip, call->remote_port);
201 candidates = g_list_append(candidates, candidate);
203 candidate = purple_media_candidate_new("foundation",
204 PURPLE_MEDIA_COMPONENT_RTCP,
205 PURPLE_MEDIA_CANDIDATE_TYPE_HOST,
206 PURPLE_MEDIA_NETWORK_PROTOCOL_UDP,
207 call->remote_ip, call->remote_port + 1);
208 candidates = g_list_append(candidates, candidate);
210 // This seems to be pre-OC2007 R2 UAC
211 call->legacy_mode = TRUE;
214 if (username) {
215 GList *it = candidates;
216 while (it) {
217 g_object_set(it->data, "username", username, "password", password, NULL);
218 it = it->next;
222 return candidates;
225 static gchar *
226 sipe_media_sdp_codec_ids_format(GList *codecs)
228 GString *result = g_string_new(NULL);
230 while (codecs) {
231 PurpleMediaCodec *c = codecs->data;
233 gchar *tmp = g_strdup_printf(" %d", purple_media_codec_get_id(c));
234 g_string_append(result,tmp);
235 g_free(tmp);
237 codecs = codecs->next;
240 return g_string_free(result, FALSE);
243 static gchar *
244 sipe_media_sdp_codecs_format(GList *codecs)
246 GString *result = g_string_new(NULL);
248 while (codecs) {
249 PurpleMediaCodec *c = codecs->data;
250 GList *params = NULL;
252 gchar *tmp = g_strdup_printf("a=rtpmap:%d %s/%d\r\n",
253 purple_media_codec_get_id(c),
254 purple_media_codec_get_encoding_name(c),
255 purple_media_codec_get_clock_rate(c));
257 g_string_append(result, tmp);
258 g_free(tmp);
260 if ((params = purple_media_codec_get_optional_parameters(c))) {
261 tmp = g_strdup_printf("a=fmtp:%d",purple_media_codec_get_id(c));
262 g_string_append(result, tmp);
263 g_free(tmp);
265 while (params) {
266 PurpleKeyValuePair* par = params->data;
267 tmp = g_strdup_printf(" %s=%s", par->key, (gchar*) par->value);
268 g_string_append(result, tmp);
269 g_free(tmp);
270 params = params->next;
272 g_string_append(result, "\r\n");
275 codecs = codecs->next;
278 return g_string_free(result, FALSE);
281 static gchar *
282 sipe_media_sdp_candidates_format(GList *candidates, sipe_media_call* call, gboolean remote_candidate)
284 GString *result = g_string_new("");
285 gchar *tmp;
286 gchar *username = purple_media_candidate_get_username(candidates->data);
287 gchar *password = purple_media_candidate_get_password(candidates->data);
288 guint16 rtcp_port = 0;
290 if (call->legacy_mode)
291 return g_string_free(result, FALSE);
293 tmp = g_strdup_printf("a=ice-ufrag:%s\r\na=ice-pwd:%s\r\n",username, password);
294 g_string_append(result, tmp);
295 g_free(tmp);
297 while (candidates) {
298 PurpleMediaCandidate *c = candidates->data;
300 guint16 port;
301 guint16 component;
302 gchar *protocol;
303 gchar *type;
305 port = purple_media_candidate_get_port(c);
307 switch (purple_media_candidate_get_component_id(c)) {
308 case PURPLE_MEDIA_COMPONENT_RTP:
309 component = 1;
310 break;
311 case PURPLE_MEDIA_COMPONENT_RTCP:
312 component = 2;
313 if (rtcp_port == 0)
314 rtcp_port = port;
315 break;
318 switch (purple_media_candidate_get_protocol(c)) {
319 case PURPLE_MEDIA_NETWORK_PROTOCOL_TCP:
320 protocol = "TCP";
321 break;
322 case PURPLE_MEDIA_NETWORK_PROTOCOL_UDP:
323 protocol = "UDP";
324 break;
327 switch (purple_media_candidate_get_candidate_type(c)) {
328 case PURPLE_MEDIA_CANDIDATE_TYPE_HOST:
329 type = "host";
330 break;
331 case PURPLE_MEDIA_CANDIDATE_TYPE_RELAY:
332 type = "relay";
333 break;
334 case PURPLE_MEDIA_CANDIDATE_TYPE_SRFLX:
335 type = "srflx";
336 break;
337 default:
338 // TODO: error unknown/unsupported type
339 break;
342 tmp = g_strdup_printf("a=candidate:%s %u %s %u %s %d typ %s \r\n",
343 purple_media_candidate_get_foundation(c),
344 component,
345 protocol,
346 purple_media_candidate_get_priority(c),
347 purple_media_candidate_get_ip(c),
348 port,
349 type);
351 g_string_append(result, tmp);
352 g_free(tmp);
354 candidates = candidates->next;
357 if (remote_candidate) {
358 PurpleMediaCandidate *first = call->remote_candidates->data;
359 PurpleMediaCandidate *second = call->remote_candidates->next->data;
360 tmp = g_strdup_printf("a=remote-candidates:1 %s %u 2 %s %u\r\n",
361 purple_media_candidate_get_ip(first), purple_media_candidate_get_port(first),
362 purple_media_candidate_get_ip(second), purple_media_candidate_get_port(second));
364 g_string_append(result, tmp);
365 g_free(tmp);
369 if (rtcp_port != 0) {
370 tmp = g_strdup_printf("a=maxptime:200\r\na=rtcp:%u\r\n", rtcp_port);
371 g_string_append(result, tmp);
372 g_free(tmp);
375 return g_string_free(result, FALSE);
378 static gchar*
379 sipe_media_create_sdp(sipe_media_call *call, gboolean remote_candidate) {
380 PurpleMedia *media = call->media;
381 GList *local_codecs = purple_media_get_codecs(media, "sipe-voice");
382 GList *local_candidates = purple_media_get_local_candidates(media, "sipe-voice", call->dialog->with);
384 // TODO: more sophisticated
385 guint16 local_port = purple_media_candidate_get_port(local_candidates->data);
386 const char *ip = sipe_utils_get_suitable_local_ip(-1);
388 gchar *sdp_codecs = sipe_media_sdp_codecs_format(local_codecs);
389 gchar *sdp_codec_ids = sipe_media_sdp_codec_ids_format(local_codecs);
390 gchar *sdp_candidates = sipe_media_sdp_candidates_format(local_candidates, call, remote_candidate);
391 gchar *inactive = call->state == SIPE_CALL_HELD ? "a=inactive\r\n" : "";
393 gchar *body = g_strdup_printf(
394 "v=0\r\n"
395 "o=- 0 0 IN IP4 %s\r\n"
396 "s=session\r\n"
397 "c=IN IP4 %s\r\n"
398 "b=CT:99980\r\n"
399 "t=0 0\r\n"
400 "m=audio %d RTP/AVP%s\r\n"
401 "%s"
402 "%s"
403 "%s"
404 "a=encryption:rejected\r\n"
405 ,ip, ip, local_port, sdp_codec_ids, sdp_candidates, inactive, sdp_codecs);
407 g_free(sdp_codecs);
408 g_free(sdp_codec_ids);
409 g_free(sdp_candidates);
411 return body;
414 static void
415 sipe_media_session_ready_cb(sipe_media_call *call)
417 PurpleMedia *media = call->media;
418 PurpleAccount *account = purple_media_get_account(media);
420 if (!purple_media_candidates_prepared(media, NULL, NULL))
421 return;
423 if (!call->sdp_response)
424 call->sdp_response = sipe_media_create_sdp(call, FALSE);
426 if (!purple_media_accepted(media, NULL, NULL)) {
427 if (!call->legacy_mode)
428 send_sip_response(account->gc, call->invitation, 183, "Session Progress", call->sdp_response);
429 } else {
430 send_sip_response(account->gc, call->invitation, 200, "OK", call->sdp_response);
431 call->state = SIPE_CALL_RUNNING;
435 static void
436 sipe_invite_call(struct sipe_account_data *sip)
438 gchar *hdr;
439 gchar *contact;
440 gchar *body;
441 sipe_media_call *call = sip->media_call;
442 struct sip_dialog *dialog = call->dialog;
444 contact = get_contact(sip);
445 hdr = g_strdup_printf(
446 "Supported: ms-sender\r\n"
447 "ms-keep-alive: UAC;hop-hop=yes\r\n"
448 "Contact: %s%s\r\n"
449 "Supported: Replaces\r\n"
450 "Content-Type: application/sdp\r\n",
451 contact,
452 call->state == SIPE_CALL_HELD ? ";+sip.rendering=\"no\"" : "");
453 g_free(contact);
455 body = sipe_media_create_sdp(call, TRUE);
457 send_sip_request(sip->gc, "INVITE", dialog->with, dialog->with, hdr, body,
458 dialog, NULL);
460 g_free(body);
461 g_free(hdr);
464 static void
465 notify_state_change(struct sipe_account_data *sip, gboolean local) {
466 if (local) {
467 sipe_invite_call(sip);
468 } else {
469 gchar* body = sipe_media_create_sdp(sip->media_call, TRUE);
470 send_sip_response(sip->gc, sip->media_call->invitation, 200, "OK", body);
471 g_free(body);
475 static void
476 sipe_media_stream_info_cb(PurpleMedia *media,
477 PurpleMediaInfoType type,
478 SIPE_UNUSED_PARAMETER gchar *sid,
479 SIPE_UNUSED_PARAMETER gchar *name,
480 gboolean local, struct sipe_account_data *sip)
482 sipe_media_call *call = sip->media_call;
484 if (type == PURPLE_MEDIA_INFO_ACCEPT)
485 sipe_media_session_ready_cb(call);
486 else if (type == PURPLE_MEDIA_INFO_REJECT) {
487 PurpleAccount *account = purple_media_get_account(media);
488 send_sip_response(account->gc, call->invitation, 603, "Decline", NULL);
489 sipe_media_call_free(call);
490 sip->media_call = NULL;
491 } else if (type == PURPLE_MEDIA_INFO_HOLD) {
492 if (call->state == SIPE_CALL_HELD)
493 return;
495 call->state = SIPE_CALL_HELD;
496 notify_state_change(sip, local);
497 purple_media_stream_info(media, PURPLE_MEDIA_INFO_HOLD, NULL, NULL, TRUE);
499 } else if (type == PURPLE_MEDIA_INFO_UNHOLD) {
500 if (call->state == SIPE_CALL_RUNNING)
501 return;
503 call->state = SIPE_CALL_RUNNING;
504 notify_state_change(sip, local);
506 purple_media_stream_info(media, PURPLE_MEDIA_INFO_UNHOLD, NULL, NULL, TRUE);
507 } else if (type == PURPLE_MEDIA_INFO_HANGUP) {
508 call->state = SIPE_CALL_FINISHED;
509 if (local)
510 send_sip_request(sip->gc, "BYE", call->dialog->with, call->dialog->with,
511 NULL, NULL, call->dialog, NULL);
512 sipe_media_call_free(call);
513 sip->media_call = NULL;
517 static gboolean
518 sipe_media_parse_sdp_frame(sipe_media_call* call, gchar *frame) {
519 gchar **lines = g_strsplit(frame, "\r\n", 0);
520 GSList *sdp_attrs = NULL;
521 gchar *remote_ip = NULL;
522 guint16 remote_port = 0;
523 gchar **ptr;
524 gboolean no_error = TRUE;
526 for (ptr = lines; *ptr != NULL; ++ptr) {
527 if (g_str_has_prefix(*ptr, "a=")) {
528 gchar **parts = g_strsplit(*ptr + 2, ":", 2);
529 if(!parts[0]) {
530 g_strfreev(parts);
531 sipe_utils_nameval_free(sdp_attrs);
532 sdp_attrs = NULL;
533 no_error = FALSE;
534 break;
536 sdp_attrs = sipe_utils_nameval_add(sdp_attrs, parts[0], parts[1]);
537 g_strfreev(parts);
539 } else if (g_str_has_prefix(*ptr, "o=")) {
540 gchar **parts = g_strsplit(*ptr + 2, " ", 6);
541 remote_ip = g_strdup(parts[5]);
542 g_strfreev(parts);
543 } else if (g_str_has_prefix(*ptr, "m=")) {
544 gchar **parts = g_strsplit(*ptr + 2, " ", 3);
545 remote_port = atoi(parts[1]);
546 g_strfreev(parts);
550 g_strfreev(lines);
552 if (no_error) {
553 sipe_utils_nameval_free(call->sdp_attrs);
554 call->sdp_attrs = sdp_attrs;
555 call->remote_ip = remote_ip;
556 call->remote_port = remote_port;
559 return no_error;
562 static struct sip_dialog *
563 sipe_media_dialog_init(struct sip_session* session, struct sipmsg *msg)
565 gchar *newTag = gentag();
566 const gchar *oldHeader;
567 gchar *newHeader;
568 struct sip_dialog *dialog;
570 oldHeader = sipmsg_find_header(msg, "To");
571 newHeader = g_strdup_printf("%s;tag=%s", oldHeader, newTag);
572 sipmsg_remove_header_now(msg, "To");
573 sipmsg_add_header_now(msg, "To", newHeader);
574 g_free(newHeader);
576 dialog = sipe_dialog_add(session);
577 dialog->callid = g_strdup(session->callid);
578 dialog->with = parse_from(sipmsg_find_header(msg, "From"));
579 sipe_dialog_parse(dialog, msg, FALSE);
581 return dialog;
584 static sipe_media_call *
585 sipe_media_call_init(struct sipmsg *msg)
587 sipe_media_call *call;
589 call = g_new0(sipe_media_call, 1);
591 if (sipe_media_parse_sdp_frame(call, msg->body) == FALSE) {
592 g_free(call);
593 return NULL;
596 call->invitation = msg;
597 call->legacy_mode = FALSE;
598 call->state = SIPE_CALL_CONNECTING;
599 call->remote_candidates = sipe_media_parse_remote_candidates(call);
600 return call;
603 void sipe_media_hold(struct sipe_account_data *sip) {
604 if (sip->media_call) {
605 purple_media_stream_info(sip->media_call->media, PURPLE_MEDIA_INFO_HOLD,
606 NULL, NULL, FALSE);
610 void sipe_media_unhold(struct sipe_account_data *sip) {
611 if (sip->media_call) {
612 purple_media_stream_info(sip->media_call->media, PURPLE_MEDIA_INFO_UNHOLD,
613 NULL, NULL, FALSE);
617 void sipe_media_incoming_invite(struct sipe_account_data *sip, struct sipmsg *msg)
619 PurpleMediaManager *manager = purple_media_manager_get();
620 PurpleMedia *media;
622 const gchar *callid = sipmsg_find_header(msg, "Call-ID");
624 sipe_media_call *call;
625 struct sip_session *session;
626 struct sip_dialog *dialog;
628 GParameter *params;
630 if (sip->media_call) {
631 if (sipe_strequal(sip->media_call->dialog->callid, callid)) {
632 gchar *rsp;
634 call = sip->media_call;
636 sipmsg_free(call->invitation);
637 msg->dont_free = TRUE;
638 call->invitation = msg;
640 sipmsg_add_header(msg, "Supported", "Replaces");
642 sipe_utils_nameval_free(call->sdp_attrs);
643 call->sdp_attrs = NULL;
644 if (!sipe_media_parse_sdp_frame(call, msg->body)) {
645 // TODO: handle error
648 if (call->legacy_mode && call->state == SIPE_CALL_RUNNING) {
649 sipe_media_hold(sip);
650 return;
653 if (sipe_utils_nameval_find(call->sdp_attrs, "inactive")) {
654 sipe_media_hold(sip);
655 return;
658 if (call->state == SIPE_CALL_HELD) {
659 sipe_media_unhold(sip);
660 return;
663 call->remote_codecs = sipe_media_parse_remote_codecs(call);
664 call->remote_codecs = sipe_media_prune_remote_codecs(call->media, call->remote_codecs);
665 if (!call->remote_codecs) {
666 // TODO: error no remote codecs
668 if (sipe_backend_set_remote_codecs(call, call->dialog->with) == FALSE)
669 printf("ERROR SET REMOTE CODECS"); // TODO
671 rsp = sipe_media_create_sdp(sip->media_call, TRUE);
672 send_sip_response(sip->gc, msg, 200, "OK", rsp);
673 g_free(rsp);
674 } else {
675 // TODO: send Busy Here
676 printf("MEDIA SESSION ALREADY IN PROGRESS");
678 return;
681 call = sipe_media_call_init(msg);
683 session = sipe_session_find_or_add_chat_by_callid(sip, callid);
684 dialog = sipe_media_dialog_init(session, msg);
686 media = purple_media_manager_create_media(manager, sip->account,
687 "fsrtpconference", dialog->with, FALSE);
689 g_signal_connect(G_OBJECT(media), "stream-info",
690 G_CALLBACK(sipe_media_stream_info_cb), sip);
691 g_signal_connect_swapped(G_OBJECT(media), "candidates-prepared",
692 G_CALLBACK(sipe_media_session_ready_cb), call);
695 call->session = session;
696 call->dialog = dialog;
697 call->media = media;
699 if (call->legacy_mode) {
700 purple_media_add_stream(media, "sipe-voice", dialog->with,
701 PURPLE_MEDIA_AUDIO, FALSE, "rawudp", 0, NULL);
702 } else {
703 params = g_new0(GParameter, 2);
704 params[0].name = "controlling-mode";
705 g_value_init(&params[0].value, G_TYPE_BOOLEAN);
706 g_value_set_boolean(&params[0].value, FALSE);
707 params[1].name = "compatibility-mode";
708 g_value_init(&params[1].value, G_TYPE_UINT);
709 g_value_set_uint(&params[1].value, NICE_COMPATIBILITY_OC2007R2);
712 purple_media_add_stream(media, "sipe-voice", dialog->with,
713 PURPLE_MEDIA_AUDIO, FALSE, "nice", 2, params);
716 purple_media_add_remote_candidates(media, "sipe-voice", dialog->with,
717 call->remote_candidates);
719 call->remote_codecs = sipe_media_parse_remote_codecs(call);
720 call->remote_codecs = sipe_media_prune_remote_codecs(call, call->remote_codecs);
721 if (!call->remote_candidates || !call->remote_codecs) {
722 sipe_media_call_free(call);
723 sip->media_call = NULL;
724 printf("ERROR NO CANDIDATES OR CODECS");
725 return;
727 if (sipe_backend_set_remote_codecs(call, dialog->with) == FALSE)
728 printf("ERROR SET REMOTE CODECS"); // TODO
730 sip->media_call = call;
732 // TODO: copy message instead of this don't free thing
733 msg->dont_free = TRUE;
734 send_sip_response(sip->gc, msg, 180, "Ringing", NULL);
737 void sipe_media_hangup(struct sipe_account_data *sip)
739 if (sip->media_call) {
740 purple_media_stream_info(sip->media_call->media, PURPLE_MEDIA_INFO_HANGUP,
741 NULL, NULL, FALSE);
746 Local Variables:
747 mode: c
748 c-file-style: "bsd"
749 indent-tabs-mode: t
750 tab-width: 8
751 End: