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
31 #include "sipe-common.h"
33 #include "sip-transport.h"
34 #include "sipe-backend.h"
36 #include "sipe-core.h"
37 #include "sipe-core-private.h"
38 #include "sipe-dialog.h"
39 #include "sipe-media.h"
40 #include "sipe-session.h"
41 #include "sipe-utils.h"
44 struct sipe_media_call_private
{
45 struct sipe_media_call
public;
47 /* private part starts here */
48 struct sipe_core_private
*sipe_private
;
49 struct sip_session
*session
;
50 struct sip_dialog
*dialog
;
52 struct sipe_backend_stream
*voice_stream
;
54 struct sipmsg
*invitation
;
57 gboolean encryption_compatible
;
61 #define SIPE_MEDIA_CALL ((struct sipe_media_call *) call_private)
62 #define SIPE_MEDIA_CALL_PRIVATE ((struct sipe_media_call_private *) call)
65 sipe_media_get_callid(struct sipe_media_call_private
*call
)
67 return call
->dialog
->callid
;
70 static void sipe_media_codec_list_free(GList
*codecs
)
72 for (; codecs
; codecs
= g_list_delete_link(codecs
, codecs
))
73 sipe_backend_codec_free(codecs
->data
);
76 static void sipe_media_candidate_list_free(GList
*candidates
)
78 for (; candidates
; candidates
= g_list_delete_link(candidates
, candidates
))
79 sipe_backend_candidate_free(candidates
->data
);
83 sipe_media_call_free(struct sipe_media_call_private
*call_private
)
86 sipe_backend_media_free(call_private
->public.backend_private
);
88 if (call_private
->session
)
89 sipe_session_remove(call_private
->sipe_private
,
90 call_private
->session
);
92 if (call_private
->invitation
)
93 sipmsg_free(call_private
->invitation
);
95 sdpmsg_free(call_private
->smsg
);
101 sipe_media_sdp_codec_ids_format(GList
*codecs
)
103 GString
*result
= g_string_new(NULL
);
106 struct sipe_backend_codec
*c
= codecs
->data
;
108 g_string_append_printf(result
,
110 sipe_backend_codec_get_id(c
));
112 codecs
= codecs
->next
;
115 return g_string_free(result
, FALSE
);
119 sipe_media_sdp_codecs_format(GList
*codecs
)
121 GString
*result
= g_string_new(NULL
);
124 struct sipe_backend_codec
*c
= codecs
->data
;
125 GList
*params
= NULL
;
126 gchar
*name
= sipe_backend_codec_get_name(c
);
128 g_string_append_printf(result
,
129 "a=rtpmap:%d %s/%d\r\n",
130 sipe_backend_codec_get_id(c
),
132 sipe_backend_codec_get_clock_rate(c
));
135 if ((params
= sipe_backend_codec_get_optional_parameters(c
))) {
136 g_string_append_printf(result
,
138 sipe_backend_codec_get_id(c
));
141 struct sipnameval
* par
= params
->data
;
142 g_string_append_printf(result
,
144 par
->name
, par
->value
);
145 params
= params
->next
;
147 g_string_append(result
, "\r\n");
150 codecs
= codecs
->next
;
153 return g_string_free(result
, FALSE
);
157 candidate_compare_by_component_id(struct sipe_backend_candidate
*c1
, struct sipe_backend_candidate
*c2
)
159 return sipe_backend_candidate_get_component_type(c1
)
160 - sipe_backend_candidate_get_component_type(c2
);
164 sipe_media_sdp_candidates_format(struct sipe_media_call_private
*call_private
, guint16
*local_port
)
166 struct sipe_backend_media
*backend_media
= call_private
->public.backend_private
;
167 struct sipe_backend_stream
*voice_stream
= call_private
->voice_stream
;
173 GString
*result
= g_string_new("");
174 guint16 rtcp_port
= 0;
176 // If we have established candidate pairs, send them in SDP response.
177 // Otherwise send all available local candidates.
178 l_candidates
= sipe_backend_media_get_active_local_candidates(backend_media
, voice_stream
);
180 l_candidates
= sipe_backend_get_local_candidates(backend_media
, voice_stream
);
182 // If in legacy mode, just fill local_port variable with local host's RTP
183 // component port and return empty string.
184 if (call_private
->legacy_mode
) {
185 for (cand
= l_candidates
; cand
; cand
= cand
->next
) {
186 struct sipe_backend_candidate
*c
= cand
->data
;
187 SipeCandidateType type
= sipe_backend_candidate_get_type(c
);
188 SipeComponentType component
= sipe_backend_candidate_get_component_type(c
);
190 if (type
== SIPE_CANDIDATE_TYPE_HOST
&& component
== SIPE_COMPONENT_RTP
) {
191 *local_port
= sipe_backend_candidate_get_port(c
);
196 sipe_media_candidate_list_free(l_candidates
);
198 return g_string_free(result
, FALSE
);
201 username
= sipe_backend_candidate_get_username(l_candidates
->data
);
202 password
= sipe_backend_candidate_get_password(l_candidates
->data
);
204 g_string_append_printf(result
,
205 "a=ice-ufrag:%s\r\na=ice-pwd:%s\r\n",
211 for (cand
= l_candidates
; cand
; cand
= cand
->next
) {
212 struct sipe_backend_candidate
*c
= cand
->data
;
215 SipeComponentType component
;
216 const gchar
*protocol
;
218 gchar
*related
= NULL
;
219 gchar
*tmp_foundation
;
222 component
= sipe_backend_candidate_get_component_type(c
);
223 port
= sipe_backend_candidate_get_port(c
);
225 switch (sipe_backend_candidate_get_protocol(c
)) {
226 case SIPE_NETWORK_PROTOCOL_TCP
:
229 case SIPE_NETWORK_PROTOCOL_UDP
:
234 switch (sipe_backend_candidate_get_type(c
)) {
235 case SIPE_CANDIDATE_TYPE_HOST
:
237 if (component
== SIPE_COMPONENT_RTP
)
239 else if (component
== SIPE_COMPONENT_RTCP
)
242 case SIPE_CANDIDATE_TYPE_RELAY
:
245 case SIPE_CANDIDATE_TYPE_SRFLX
:
250 related
= g_strdup_printf("raddr %s rport %d",
251 tmp
= sipe_backend_candidate_get_base_ip(c
),
252 sipe_backend_candidate_get_base_port(c
));
256 case SIPE_CANDIDATE_TYPE_PRFLX
:
260 // TODO: error unknown/unsupported type
264 g_string_append_printf(result
,
265 "a=candidate:%s %u %s %u %s %d typ %s %s\r\n",
266 tmp_foundation
= sipe_backend_candidate_get_foundation(c
),
269 sipe_backend_candidate_get_priority(c
),
270 tmp_ip
= sipe_backend_candidate_get_ip(c
),
273 related
? related
: "");
275 g_free(tmp_foundation
);
279 r_candidates
= sipe_backend_media_get_active_remote_candidates(backend_media
, voice_stream
);
280 r_candidates
= g_list_sort(r_candidates
, (GCompareFunc
)candidate_compare_by_component_id
);
283 g_string_append(result
, "a=remote-candidates:");
284 for (cand
= r_candidates
; cand
; cand
= cand
->next
) {
285 struct sipe_backend_candidate
*candidate
= cand
->data
;
287 g_string_append_printf(result
,
289 sipe_backend_candidate_get_component_type(candidate
),
290 tmp
= sipe_backend_candidate_get_ip(candidate
),
291 sipe_backend_candidate_get_port(candidate
));
294 g_string_append(result
, "\r\n");
297 sipe_media_candidate_list_free(l_candidates
);
298 sipe_media_candidate_list_free(r_candidates
);
300 if (rtcp_port
!= 0) {
301 g_string_append_printf(result
,
302 "a=maxptime:200\r\na=rtcp:%u\r\n",
306 return g_string_free(result
, FALSE
);
310 sipe_media_create_sdp(struct sipe_media_call_private
*call_private
) {
311 GList
*usable_codecs
= sipe_backend_get_local_codecs(SIPE_MEDIA_CALL
,
312 call_private
->voice_stream
);
315 const char *ip
= sipe_utils_get_suitable_local_ip(-1);
316 guint16 local_port
= 0;
318 gchar
*sdp_codecs
= sipe_media_sdp_codecs_format(usable_codecs
);
319 gchar
*sdp_codec_ids
= sipe_media_sdp_codec_ids_format(usable_codecs
);
320 gchar
*sdp_candidates
= sipe_media_sdp_candidates_format(call_private
, &local_port
);
321 gchar
*inactive
= (call_private
->public.local_on_hold
||
322 call_private
->public.remote_on_hold
) ? "a=inactive\r\n" : "";
324 body
= g_strdup_printf(
326 "o=- 0 0 IN IP4 %s\r\n"
331 "m=audio %d RTP/AVP%s\r\n"
335 "a=encryption:rejected\r\n"
336 ,ip
, ip
, local_port
, sdp_codec_ids
, sdp_candidates
, inactive
, sdp_codecs
);
339 g_free(sdp_codec_ids
);
340 g_free(sdp_candidates
);
342 sipe_media_codec_list_free(usable_codecs
);
348 sipe_invite_call(struct sipe_core_private
*sipe_private
, TransCallback tc
)
353 struct sipe_media_call_private
*call_private
= sipe_private
->media_call
;
354 struct sip_dialog
*dialog
= call_private
->dialog
;
356 contact
= get_contact(sipe_private
);
357 hdr
= g_strdup_printf(
358 "Supported: ms-early-media\r\n"
359 "Supported: 100rel\r\n"
360 "ms-keep-alive: UAC;hop-hop=yes\r\n"
362 "Content-Type: application/sdp\r\n",
364 (call_private
->public.local_on_hold
|| call_private
->public.remote_on_hold
) ? ";+sip.rendering=\"no\"" : "");
367 body
= sipe_media_create_sdp(call_private
);
369 dialog
->outgoing_invite
= sip_transport_invite(sipe_private
,
379 static struct sip_dialog
*
380 sipe_media_dialog_init(struct sip_session
* session
, struct sipmsg
*msg
)
382 gchar
*newTag
= gentag();
383 const gchar
*oldHeader
;
385 struct sip_dialog
*dialog
;
387 oldHeader
= sipmsg_find_header(msg
, "To");
388 newHeader
= g_strdup_printf("%s;tag=%s", oldHeader
, newTag
);
389 sipmsg_remove_header_now(msg
, "To");
390 sipmsg_add_header_now(msg
, "To", newHeader
);
393 dialog
= sipe_dialog_add(session
);
394 dialog
->callid
= g_strdup(sipmsg_find_header(msg
, "Call-ID"));
395 dialog
->with
= parse_from(sipmsg_find_header(msg
, "From"));
396 sipe_dialog_parse(dialog
, msg
, FALSE
);
402 send_response_with_session_description(struct sipe_media_call_private
*call_private
, int code
, gchar
*text
)
404 gchar
*body
= sipe_media_create_sdp(call_private
);
405 sipmsg_add_header(call_private
->invitation
, "Content-Type", "application/sdp");
406 sip_transport_response(call_private
->sipe_private
, call_private
->invitation
, code
, text
, body
);
411 encryption_levels_compatible(struct sdpmsg
*smsg
)
413 const gchar
*encrypion_level
;
415 encrypion_level
= sipe_utils_nameval_find(smsg
->attributes
, "encryption");
417 // Decline call if peer requires encryption as we don't support it yet.
418 return !sipe_strequal(encrypion_level
, "required");
422 handle_incompatible_encryption_level(struct sipe_media_call_private
*call_private
)
424 sipmsg_add_header(call_private
->invitation
, "Warning",
425 "308 lcs.microsoft.com \"Encryption Levels not compatible\"");
426 sip_transport_response(call_private
->sipe_private
,
427 call_private
->invitation
,
428 488, "Encryption Levels not compatible",
430 sipe_backend_media_reject(call_private
->public.backend_private
, FALSE
);
431 sipe_backend_notify_error(_("Unable to establish a call"),
432 _("Encryption settings of peer are incompatible with ours."));
436 process_invite_call_response(struct sipe_core_private
*sipe_private
,
438 struct transaction
*trans
);
441 apply_remote_message(struct sipe_media_call_private
* call_private
,
444 struct sipe_backend_media
*backend_media
= SIPE_MEDIA_CALL
->backend_private
;
445 struct sipe_backend_stream
*backend_stream
= call_private
->voice_stream
;
446 GList
*backend_candidates
= NULL
;
447 GList
*backend_codecs
= NULL
;
450 const gchar
*username
= sipe_utils_nameval_find(msg
->attributes
, "ice-ufrag");
451 const gchar
*password
= sipe_utils_nameval_find(msg
->attributes
, "ice-pwd");
454 for (i
= msg
->candidates
; i
; i
= i
->next
) {
455 struct sdpcandidate
*c
= i
->data
;
456 struct sipe_backend_candidate
*candidate
;
457 candidate
= sipe_backend_candidate_new(c
->foundation
,
463 sipe_backend_candidate_set_priority(candidate
, c
->priority
);
466 sipe_backend_candidate_set_username_and_pwd(candidate
,
470 backend_candidates
= g_list_append(backend_candidates
, candidate
);
473 sipe_backend_media_add_remote_candidates(backend_media
,
476 sipe_media_candidate_list_free(backend_candidates
);
478 for (i
= msg
->codecs
; i
; i
= i
->next
) {
479 struct sdpcodec
*c
= i
->data
;
480 struct sipe_backend_codec
*codec
;
483 codec
= sipe_backend_codec_new(c
->id
,
488 for (j
= c
->attributes
; j
; j
= j
->next
) {
489 struct sipnameval
*attr
= j
->data
;
491 sipe_backend_codec_add_optional_parameter(codec
,
496 backend_codecs
= g_list_append(backend_codecs
, codec
);
499 sipe_backend_set_remote_codecs(backend_media
,
502 sipe_media_codec_list_free(backend_codecs
);
504 call_private
->legacy_mode
= msg
->legacy
;
505 call_private
->encryption_compatible
= encryption_levels_compatible(msg
);
510 static void candidates_prepared_cb(struct sipe_media_call
*call
)
512 struct sipe_media_call_private
*call_private
= SIPE_MEDIA_CALL_PRIVATE
;
514 if (sipe_backend_media_is_initiator(call_private
->public.backend_private
,
515 call_private
->voice_stream
)) {
516 sipe_invite_call(call_private
->sipe_private
,
517 process_invite_call_response
);
521 if (!apply_remote_message(call_private
, call_private
->smsg
)) {
522 sipe_media_hangup(call_private
->sipe_private
);
526 sdpmsg_free(call_private
->smsg
);
527 call_private
->smsg
= NULL
;
529 if (!call_private
->legacy_mode
&& call_private
->encryption_compatible
)
530 send_response_with_session_description(call_private
,
531 183, "Session Progress");
534 static void media_connected_cb(SIPE_UNUSED_PARAMETER
struct sipe_media_call_private
*call_private
)
538 static void call_accept_cb(struct sipe_media_call
*call
, gboolean local
)
541 struct sipe_media_call_private
*call_private
= SIPE_MEDIA_CALL_PRIVATE
;
543 if (!call_private
->encryption_compatible
) {
544 handle_incompatible_encryption_level(call_private
);
548 send_response_with_session_description(call_private
, 200, "OK");
552 static void call_reject_cb(struct sipe_media_call
*call
, gboolean local
)
554 struct sipe_media_call_private
*call_private
= SIPE_MEDIA_CALL_PRIVATE
;
557 sip_transport_response(call_private
->sipe_private
, call_private
->invitation
, 603, "Decline", NULL
);
559 call_private
->sipe_private
->media_call
= NULL
;
560 sipe_media_call_free(call_private
);
564 sipe_media_send_ack(struct sipe_core_private
*sipe_private
, struct sipmsg
*msg
,
565 struct transaction
*trans
);
567 static void call_hold_cb(struct sipe_media_call
*call
, gboolean local
, gboolean state
)
569 struct sipe_media_call_private
*call_private
= SIPE_MEDIA_CALL_PRIVATE
;
571 if (local
&& (call_private
->public.local_on_hold
!= state
)) {
572 call_private
->public.local_on_hold
= state
;
573 sipe_invite_call(call_private
->sipe_private
, sipe_media_send_ack
);
574 } else if (call_private
->public.remote_on_hold
!= state
) {
575 call_private
->public.remote_on_hold
= state
;
576 send_response_with_session_description(call_private
, 200, "OK");
580 static void call_hangup_cb(struct sipe_media_call
*call
, gboolean local
)
582 struct sipe_media_call_private
*call_private
= SIPE_MEDIA_CALL_PRIVATE
;
585 sip_transport_bye(call_private
->sipe_private
, call_private
->dialog
);
587 call_private
->sipe_private
->media_call
= NULL
;
588 sipe_media_call_free(call_private
);
591 static struct sipe_media_call_private
*
592 sipe_media_call_new(struct sipe_core_private
*sipe_private
,
593 const gchar
* with
, gboolean initiator
)
595 struct sipe_media_call_private
*call_private
= g_new0(struct sipe_media_call_private
, 1);
597 call_private
->sipe_private
= sipe_private
;
598 call_private
->public.backend_private
= sipe_backend_media_new(SIPE_CORE_PUBLIC
,
603 call_private
->legacy_mode
= FALSE
;
604 call_private
->using_nice
= TRUE
;
605 call_private
->encryption_compatible
= TRUE
;
607 call_private
->public.candidates_prepared_cb
= candidates_prepared_cb
;
608 call_private
->public.media_connected_cb
= media_connected_cb
;
609 call_private
->public.call_accept_cb
= call_accept_cb
;
610 call_private
->public.call_reject_cb
= call_reject_cb
;
611 call_private
->public.call_hold_cb
= call_hold_cb
;
612 call_private
->public.call_hangup_cb
= call_hangup_cb
;
614 call_private
->public.local_on_hold
= FALSE
;
615 call_private
->public.remote_on_hold
= FALSE
;
620 void sipe_media_hangup(struct sipe_core_private
*sipe_private
)
622 struct sipe_media_call_private
*call_private
= sipe_private
->media_call
;
624 sipe_backend_media_hangup(call_private
->public.backend_private
,
629 sipe_core_media_initiate_call(struct sipe_core_public
*sipe_public
,
632 struct sipe_core_private
*sipe_private
= SIPE_CORE_PRIVATE
;
633 struct sipe_media_call_private
*call_private
;
635 if (sipe_private
->media_call
)
638 call_private
= sipe_media_call_new(sipe_private
, with
, TRUE
);
640 call_private
->session
= sipe_session_add_call(sipe_private
, with
);
641 call_private
->dialog
= sipe_dialog_add(call_private
->session
);
642 call_private
->dialog
->callid
= gencallid();
643 call_private
->dialog
->with
= g_strdup(call_private
->session
->with
);
644 call_private
->dialog
->ourtag
= gentag();
646 call_private
->voice_stream
= sipe_backend_media_add_stream(
647 call_private
->public.backend_private
,
650 call_private
->using_nice
,
653 if (!call_private
->voice_stream
) {
654 sipe_backend_notify_error("Error occured",
655 "Error creating media stream");
656 sipe_media_call_free(call_private
);
660 sipe_private
->media_call
= call_private
;
662 // Processing continues in candidates_prepared_cb
666 sipe_media_incoming_invite(struct sipe_core_private
*sipe_private
,
669 const gchar
*callid
= sipmsg_find_header(msg
, "Call-ID");
671 struct sipe_media_call_private
*call_private
= sipe_private
->media_call
;
674 if (call_private
&& !sipe_strequal(call_private
->dialog
->callid
, callid
)) {
675 sip_transport_response(sipe_private
, msg
, 486, "Busy Here", NULL
);
679 smsg
= sdpmsg_parse_msg(msg
->body
);
681 sipe_media_hangup(sipe_private
);
686 if (call_private
->invitation
)
687 sipmsg_free(call_private
->invitation
);
688 call_private
->invitation
= sipmsg_copy(msg
);
690 apply_remote_message(call_private
, smsg
);
692 if (!call_private
->encryption_compatible
) {
693 handle_incompatible_encryption_level(call_private
);
694 } else if (call_private
->legacy_mode
&& !call_private
->public.remote_on_hold
) {
695 sipe_backend_media_hold(call_private
->public.backend_private
,
697 } else if (sipe_utils_nameval_find(smsg
->attributes
, "inactive")) {
698 sipe_backend_media_hold(call_private
->public.backend_private
, FALSE
);
699 } else if (call_private
->public.remote_on_hold
) {
700 sipe_backend_media_unhold(call_private
->public.backend_private
, FALSE
);
702 send_response_with_session_description(call_private
,
708 gchar
*with
= parse_from(sipmsg_find_header(msg
, "From"));
710 call_private
= sipe_media_call_new(sipe_private
, with
, FALSE
);
711 call_private
->session
= sipe_session_add_call(sipe_private
, with
);
712 call_private
->dialog
= sipe_media_dialog_init(call_private
->session
, msg
);
713 call_private
->invitation
= sipmsg_copy(msg
);
714 call_private
->smsg
= smsg
;
715 call_private
->voice_stream
=
716 sipe_backend_media_add_stream(call_private
->public.backend_private
,
719 !call_private
->legacy_mode
, FALSE
);
721 sipe_private
->media_call
= call_private
;
723 sip_transport_response(sipe_private
, call_private
->invitation
, 180, "Ringing", NULL
);
727 // Processing continues in candidates_prepared_cb
732 sipe_media_send_ack(struct sipe_core_private
*sipe_private
,
733 SIPE_UNUSED_PARAMETER
struct sipmsg
*msg
,
734 struct transaction
*trans
)
736 struct sipe_media_call_private
*call_private
= sipe_private
->media_call
;
737 struct sip_dialog
*dialog
;
741 if (!call_private
|| !call_private
->dialog
)
744 dialog
= call_private
->dialog
;
745 tmp_cseq
= dialog
->cseq
;
747 sscanf(trans
->key
, "<%*[a-zA-Z0-9]><%d INVITE>", &trans_cseq
);
748 dialog
->cseq
= trans_cseq
- 1;
749 sip_transport_ack(sipe_private
, dialog
);
750 dialog
->cseq
= tmp_cseq
;
752 dialog
->outgoing_invite
= NULL
;
758 process_invite_call_response(struct sipe_core_private
*sipe_private
,
760 struct transaction
*trans
)
762 const gchar
*callid
= sipmsg_find_header(msg
, "Call-ID");
764 struct sipe_media_call_private
*call_private
= sipe_private
->media_call
;
765 struct sipe_backend_media
*backend_private
;
769 !sipe_strequal(sipe_media_get_callid(call_private
), callid
))
772 backend_private
= call_private
->public.backend_private
;
773 with
= call_private
->dialog
->with
;
775 call_private
->dialog
->outgoing_invite
= NULL
;
777 if (msg
->response
>= 400) {
778 // Call rejected by remote peer or an error occurred
780 GString
*desc
= g_string_new("");
782 switch (msg
->response
) {
784 title
= _("User unavailable");
785 g_string_append_printf(desc
, _("User %s is not available"), with
);
789 title
= _("Call rejected");
790 g_string_append_printf(desc
, _("User %s rejected call"), with
);
793 title
= _("Error occured");
794 g_string_append(desc
, _("Unable to establish a call"));
798 g_string_append_printf(desc
, "\n%d %s", msg
->response
, msg
->responsestr
);
800 sipe_backend_notify_error(title
, desc
->str
);
801 g_string_free(desc
, TRUE
);
803 sipe_backend_media_reject(backend_private
, FALSE
);
804 sipe_media_send_ack(sipe_private
, msg
, trans
);
809 sipe_dialog_parse(call_private
->dialog
, msg
, TRUE
);
810 smsg
= sdpmsg_parse_msg(msg
->body
);
812 sipe_media_hangup(sipe_private
);
816 apply_remote_message(call_private
, smsg
);
818 if (msg
->response
== 183) {
819 // Session in progress
820 const gchar
*rseq
= sipmsg_find_header(msg
, "RSeq");
821 const gchar
*cseq
= sipmsg_find_header(msg
, "CSeq");
822 gchar
*rack
= g_strdup_printf("RAck: %s %s\r\n", rseq
, cseq
);
823 sip_transport_request(sipe_private
,
829 call_private
->dialog
,
833 sipe_media_send_ack(sipe_private
, msg
, trans
);
835 if (call_private
->legacy_mode
&& call_private
->using_nice
) {
836 // We created non-legacy stream as we don't know which version of
837 // client is on the other side until first SDP response is received.
838 // This client requires legacy mode, so we must remove current session
839 // (using ICE) and create new using raw UDP transport.
840 struct sipe_backend_stream
*new_stream
;
842 call_private
->using_nice
= FALSE
;
844 new_stream
= sipe_backend_media_add_stream(backend_private
,
850 sipe_backend_media_remove_stream(backend_private
,
851 call_private
->voice_stream
);
852 call_private
->voice_stream
= new_stream
;
854 apply_remote_message(call_private
, smsg
);
856 // New INVITE will be sent in candidates_prepared_cb
858 sipe_invite_call(sipe_private
, sipe_media_send_ack
);