2012-12-03 03:11:21 +00:00
/*
* Copyright ( C ) 2012 Doubango Telecom < http : //www.doubango.org>
*
* Contact : Mamadou Diop < diopmamadou ( at ) doubango [ dot ] org >
*
* This file is part of Open Source Doubango Framework .
*
* DOUBANGO is free software : you can redistribute it and / or modify
* it under the terms of the GNU General Public License as publishd by
* the Free Software Foundation , either version 3 of the License , or
* ( at your option ) any later version .
*
* DOUBANGO is distributed in the hope that it will be useful ,
* but WITHOUT ANY WARRANTY ; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the
* GNU General Public License for more details .
*
* You should have received a copy of the GNU General Public License
* along with DOUBANGO .
*
*/
# include "tinysip/dialogs/tsip_dialog_invite.h"
# include "tinysip/dialogs/tsip_dialog_invite.common.h"
# include "tinysdp/parsers/tsdp_parser_message.h"
# include "tinysdp/tsdp_message.h"
# include "tinysdp/headers/tsdp_header_S.h"
2013-07-31 19:25:16 +00:00
# include "tinysdp/headers/tsdp_header_O.h"
2012-12-03 03:11:21 +00:00
2014-05-11 22:25:35 +00:00
# include "stun/tnet_stun_types.h"
2012-12-03 03:11:21 +00:00
# include "ice/tnet_ice_ctx.h"
# include "tsk_debug.h"
extern int tsip_dialog_invite_msession_start ( tsip_dialog_invite_t * self ) ;
static int tsip_dialog_invite_ice_create_ctx ( tsip_dialog_invite_t * self , tmedia_type_t media_type ) ;
static int tsip_dialog_invite_ice_audio_callback ( const tnet_ice_event_t * e ) ;
static int tsip_dialog_invite_ice_video_callback ( const tnet_ice_event_t * e ) ;
2013-05-24 19:33:16 +00:00
int tsip_dialog_invite_ice_set_media_type ( tsip_dialog_invite_t * self , tmedia_type_t media_type ) ;
2012-12-03 03:11:21 +00:00
tsk_bool_t tsip_dialog_invite_ice_got_local_candidates ( const tsip_dialog_invite_t * self ) ;
int tsip_dialog_invite_ice_process_ro ( tsip_dialog_invite_t * self , const tsdp_message_t * sdp_ro , tsk_bool_t is_remote_offer ) ;
2013-07-31 23:58:51 +00:00
# define tsip_dialog_invite_ice_cancel_silent_and_sync_ctx(_self) \
tsip_dialog_invite_ice_set_sync_mode_ctx ( ( _self ) , tsk_true ) ; \
tsip_dialog_invite_ice_set_silent_mode_ctx ( ( _self ) , tsk_true ) ; \
tsip_dialog_invite_ice_cancel_ctx ( ( _self ) ) ; /* "cancelled" event will not be sent and we're sure that cancel operation will be done when the function exit */ \
tsip_dialog_invite_ice_set_sync_mode_ctx ( ( _self ) , tsk_false ) ; \
tsip_dialog_invite_ice_set_silent_mode_ctx ( ( _self ) , tsk_false ) ; \
2012-12-03 03:11:21 +00:00
/* ======================== transitions ======================== */
// Use "Current" instead of "Any" to avoid priority reordering
static int x0500_Current_2_Current_X_oINVITE ( va_list * app ) ;
static int x0500_Current_2_Current_X_iINVITE ( va_list * app ) ;
/* ======================== conds ======================== */
static tsk_bool_t _fsm_cond_get_local_candidates ( tsip_dialog_invite_t * self , tsip_message_t * message )
{
if ( self - > supported . ice ) {
tsk_bool_t use_ice = tsk_false ;
// "action->media.type" will be defined for locally initiated media update
tmedia_type_t new_media = TSIP_DIALOG ( self ) - > curr_action ? TSIP_DIALOG ( self ) - > curr_action - > media . type : tmedia_none ;
if ( message & & TSIP_MESSAGE_HAS_CONTENT ( message ) & & tsk_striequals ( " application/sdp " , TSIP_MESSAGE_CONTENT_TYPE ( message ) ) ) {
// If this code is called this means that we are the "answerer"
// only gets the candidates if ICE is enabled and the remote peer supports ICE
tsdp_message_t * sdp_ro ;
const tsdp_header_M_t * M ;
2012-12-13 22:15:40 +00:00
int index ;
2012-12-03 03:11:21 +00:00
if ( ! ( sdp_ro = tsdp_message_parse ( TSIP_MESSAGE_CONTENT_DATA ( message ) , TSIP_MESSAGE_CONTENT_DATA_LENGTH ( message ) ) ) ) {
TSK_DEBUG_ERROR ( " Failed to parse remote sdp message " ) ;
return tsk_false ;
}
2012-12-13 22:15:40 +00:00
index = 0 ;
while ( ( M = ( const tsdp_header_M_t * ) tsdp_message_get_headerAt ( sdp_ro , tsdp_htype_M , index + + ) ) ) {
if ( ! tsdp_header_M_findA ( M , " candidate " ) ) {
2013-07-31 17:52:20 +00:00
use_ice = tsk_false ; // do not use ICE if at least on media is ICE-less (e.g. MSRP)
2012-12-13 22:15:40 +00:00
break ;
}
use_ice = tsk_true ; // only use ICE if there is a least one media line
}
2012-12-03 03:11:21 +00:00
new_media = tmedia_type_from_sdp ( sdp_ro ) ;
TSK_OBJECT_SAFE_FREE ( sdp_ro ) ;
}
else if ( ! message ) {
2012-12-13 22:15:40 +00:00
// we are the "offerer" -> use ICE only for audio or video medias (ignore ice for MSRP)
use_ice = ( new_media & tmedia_audio ) | | ( new_media & tmedia_video ) ;
2012-12-03 03:11:21 +00:00
}
if ( use_ice ) {
if ( ! self - > ice . ctx_audio & & ! self - > ice . ctx_video ) { // First time
return tsk_true ;
}
else {
if ( self - > ice . media_type ! = new_media ) {
return tsk_true ;
}
return ! tsip_dialog_invite_ice_got_local_candidates ( self ) ;
}
}
}
return tsk_false ;
}
int tsip_dialog_invite_ice_init ( tsip_dialog_invite_t * self )
{
tsk_fsm_set ( TSIP_DIALOG_GET_FSM ( self ) ,
// Current -> (oINVITE) -> Current
TSK_FSM_ADD ( tsk_fsm_state_current , _fsm_action_oINVITE , _fsm_cond_get_local_candidates , tsk_fsm_state_current , x0500_Current_2_Current_X_oINVITE , " x0500_Current_2_Current_X_oINVITE " ) ,
// Current -> (iINVITE) -> Current
TSK_FSM_ADD ( tsk_fsm_state_current , _fsm_action_iINVITE , _fsm_cond_get_local_candidates , tsk_fsm_state_current , x0500_Current_2_Current_X_iINVITE , " x0500_Current_2_Current_X_iINVITE " )
) ;
return 0 ;
}
int tsip_dialog_invite_ice_timers_set ( tsip_dialog_invite_t * self , int64_t timeout )
{
2013-07-31 21:44:48 +00:00
if ( /*tnet_ice_ctx_is_active*/ ( self - > ice . ctx_audio ) ) {
2012-12-03 03:11:21 +00:00
tnet_ice_ctx_set_concheck_timeout ( self - > ice . ctx_audio , timeout ) ;
}
2013-07-31 21:44:48 +00:00
if ( /*tnet_ice_ctx_is_active*/ ( self - > ice . ctx_video ) ) {
2012-12-03 03:11:21 +00:00
tnet_ice_ctx_set_concheck_timeout ( self - > ice . ctx_video , timeout ) ;
}
return 0 ;
}
static int tsip_dialog_invite_ice_create_ctx ( tsip_dialog_invite_t * self , tmedia_type_t media_type )
{
int32_t transport_idx ;
2014-05-11 22:25:35 +00:00
int ret = 0 ;
2012-12-03 03:11:21 +00:00
if ( ! self ) {
TSK_DEBUG_ERROR ( " Invalid parameter " ) ;
return - 1 ;
}
transport_idx = TSIP_DIALOG_GET_STACK ( self ) - > network . transport_idx_default ;
2014-05-11 22:25:35 +00:00
if ( ! self - > ice . ctx_audio & & ( media_type & tmedia_audio ) ) {
2012-12-03 03:11:21 +00:00
self - > ice . ctx_audio = tnet_ice_ctx_create ( self - > ice . is_jingle , TNET_SOCKET_TYPE_IS_IPV6 ( TSIP_DIALOG_GET_STACK ( self ) - > network . proxy_cscf_type [ transport_idx ] ) ,
self - > use_rtcp , tsk_false , tsip_dialog_invite_ice_audio_callback , self ) ;
2014-05-11 22:25:35 +00:00
if ( ! self - > ice . ctx_audio ) {
2012-12-03 03:11:21 +00:00
TSK_DEBUG_ERROR ( " Failed to create ICE audio context " ) ;
return - 2 ;
}
2015-06-05 00:45:47 +00:00
#if 0 // @deprecated
2014-05-11 23:11:40 +00:00
ret = tnet_ice_ctx_set_stun ( self - > ice . ctx_audio , TSIP_DIALOG_GET_SS ( self ) - > media . stun . hostname , TSIP_DIALOG_GET_SS ( self ) - > media . stun . port , kStunSoftware , TSIP_DIALOG_GET_SS ( self ) - > media . stun . username , TSIP_DIALOG_GET_SS ( self ) - > media . stun . password ) ;
2014-12-01 23:42:34 +00:00
# else
ret = tnet_ice_ctx_add_server (
self - > ice . ctx_audio ,
" udp " , // "tcp", "tls", "ws", "wss"...
TSIP_DIALOG_GET_SS ( self ) - > media . stun . hostname ,
TSIP_DIALOG_GET_SS ( self ) - > media . stun . port ,
TSIP_DIALOG_GET_SS ( self ) - > media . enable_iceturn ,
TSIP_DIALOG_GET_SS ( self ) - > media . enable_icestun ,
TSIP_DIALOG_GET_SS ( self ) - > media . stun . username ,
TSIP_DIALOG_GET_SS ( self ) - > media . stun . password ) ;
# endif
2014-05-11 22:25:35 +00:00
ret = tnet_ice_ctx_set_turn_enabled ( self - > ice . ctx_audio , TSIP_DIALOG_GET_SS ( self ) - > media . enable_iceturn ) ;
2014-12-01 23:42:34 +00:00
ret = tnet_ice_ctx_set_stun_enabled ( self - > ice . ctx_audio , TSIP_DIALOG_GET_SS ( self ) - > media . enable_icestun ) ;
2014-05-11 22:25:35 +00:00
ret = tnet_ice_ctx_set_rtcpmux ( self - > ice . ctx_audio , self - > use_rtcpmux ) ;
2012-12-03 03:11:21 +00:00
}
2014-05-11 22:25:35 +00:00
if ( ! self - > ice . ctx_video & & ( media_type & tmedia_video ) ) {
2012-12-03 03:11:21 +00:00
self - > ice . ctx_video = tnet_ice_ctx_create ( self - > ice . is_jingle , TNET_SOCKET_TYPE_IS_IPV6 ( TSIP_DIALOG_GET_STACK ( self ) - > network . proxy_cscf_type [ transport_idx ] ) ,
self - > use_rtcp , tsk_true , tsip_dialog_invite_ice_video_callback , self ) ;
2014-05-11 22:25:35 +00:00
if ( ! self - > ice . ctx_video ) {
2012-12-03 03:11:21 +00:00
TSK_DEBUG_ERROR ( " Failed to create ICE video context " ) ;
return - 2 ;
}
2014-12-01 23:42:34 +00:00
# if 1 // @deprecated
2014-05-11 23:11:40 +00:00
ret = tnet_ice_ctx_set_stun ( self - > ice . ctx_video , TSIP_DIALOG_GET_SS ( self ) - > media . stun . hostname , TSIP_DIALOG_GET_SS ( self ) - > media . stun . port , kStunSoftware , TSIP_DIALOG_GET_SS ( self ) - > media . stun . username , TSIP_DIALOG_GET_SS ( self ) - > media . stun . password ) ;
2014-12-01 23:42:34 +00:00
# else
ret = tnet_ice_ctx_add_server (
self - > ice . ctx_video ,
" udp " , // "tcp", "tls", "ws", "wss"...
TSIP_DIALOG_GET_SS ( self ) - > media . stun . hostname ,
TSIP_DIALOG_GET_SS ( self ) - > media . stun . port ,
TSIP_DIALOG_GET_SS ( self ) - > media . enable_iceturn ,
TSIP_DIALOG_GET_SS ( self ) - > media . enable_icestun ,
TSIP_DIALOG_GET_SS ( self ) - > media . stun . username ,
TSIP_DIALOG_GET_SS ( self ) - > media . stun . password ) ;
# endif
2014-05-11 22:25:35 +00:00
ret = tnet_ice_ctx_set_turn_enabled ( self - > ice . ctx_video , TSIP_DIALOG_GET_SS ( self ) - > media . enable_iceturn ) ;
2014-12-01 23:42:34 +00:00
ret = tnet_ice_ctx_set_stun_enabled ( self - > ice . ctx_video , TSIP_DIALOG_GET_SS ( self ) - > media . enable_icestun ) ;
2014-05-11 22:25:35 +00:00
ret = tnet_ice_ctx_set_rtcpmux ( self - > ice . ctx_video , self - > use_rtcpmux ) ;
2012-12-03 03:11:21 +00:00
}
2013-05-24 19:33:16 +00:00
// set media type
2014-05-11 22:25:35 +00:00
ret = tsip_dialog_invite_ice_set_media_type ( self , media_type ) ;
2012-12-03 03:11:21 +00:00
// update session manager with the right ICE contexts
2014-05-11 22:25:35 +00:00
if ( self - > msession_mgr ) {
ret = tmedia_session_mgr_set_ice_ctx ( self - > msession_mgr , self - > ice . ctx_audio , self - > ice . ctx_video ) ;
2012-12-03 03:11:21 +00:00
}
2014-05-11 22:25:35 +00:00
return ret ;
2012-12-03 03:11:21 +00:00
}
2013-05-24 19:33:16 +00:00
int tsip_dialog_invite_ice_set_media_type ( tsip_dialog_invite_t * self , tmedia_type_t _media_type )
{
if ( self ) {
tmedia_type_t av_media_type = ( _media_type & tmedia_audiovideo ) ; // filter to keep audio and video only
// "none" comparison is used to exclude the "first call"
if ( self - > ice . media_type ! = tmedia_none & & self - > ice . media_type ! = av_media_type ) {
// cancels contexts associated to old medias
if ( self - > ice . ctx_audio & & ! ( av_media_type & tmedia_audio ) ) {
tnet_ice_ctx_cancel ( self - > ice . ctx_audio ) ;
}
if ( self - > ice . ctx_video & & ! ( av_media_type & tmedia_video ) ) {
tnet_ice_ctx_cancel ( self - > ice . ctx_video ) ;
}
// cancels contexts associated to new medias (e.g. session "remove" then "add")
// cancel() on newly created contexts don't have any effect
if ( self - > ice . ctx_audio & & ( ! ( av_media_type & tmedia_audio ) & & ( self - > ice . media_type & tmedia_audio ) ) ) {
//tnet_ice_ctx_cancel(self->ice.ctx_audio);
}
if ( self - > ice . ctx_video & & ( ! ( av_media_type & tmedia_video ) & & ( self - > ice . media_type & tmedia_video ) ) ) {
//tnet_ice_ctx_cancel(self->ice.ctx_video);
}
}
self - > ice . media_type = av_media_type ;
}
return 0 ;
}
2013-07-31 23:58:51 +00:00
static int tsip_dialog_invite_ice_start_ctx ( tsip_dialog_invite_t * self )
2012-12-03 03:11:21 +00:00
{
int ret = 0 ;
if ( self ) {
if ( ( self - > ice . media_type & tmedia_audio ) ) {
2013-07-31 23:58:51 +00:00
if ( self - > ice . ctx_audio & & ( ret = tnet_ice_ctx_start ( self - > ice . ctx_audio ) ) ! = 0 ) {
2012-12-03 03:11:21 +00:00
return ret ;
}
}
if ( ( self - > ice . media_type & tmedia_video ) ) {
2013-07-31 23:58:51 +00:00
if ( self - > ice . ctx_video & & ( ret = tnet_ice_ctx_start ( self - > ice . ctx_video ) ) ! = 0 ) {
2012-12-03 03:11:21 +00:00
return ret ;
}
}
}
return 0 ;
}
2013-07-31 23:58:51 +00:00
static int tsip_dialog_invite_ice_cancel_ctx ( tsip_dialog_invite_t * self )
2013-07-31 22:58:53 +00:00
{
2013-07-31 23:58:51 +00:00
int ret = 0 ;
if ( self ) {
if ( ( self - > ice . media_type & tmedia_audio ) ) {
if ( self - > ice . ctx_audio & & ( ret = tnet_ice_ctx_cancel ( self - > ice . ctx_audio ) ) ! = 0 ) {
return ret ;
}
}
if ( ( self - > ice . media_type & tmedia_video ) ) {
if ( self - > ice . ctx_video & & ( ret = tnet_ice_ctx_cancel ( self - > ice . ctx_video ) ) ! = 0 ) {
return ret ;
}
}
}
return 0 ;
2013-07-31 22:58:53 +00:00
}
2013-07-31 23:58:51 +00:00
static int tsip_dialog_invite_ice_set_sync_mode_ctx ( tsip_dialog_invite_t * self , tsk_bool_t sync_mode )
2013-07-31 17:52:20 +00:00
{
int ret = 0 ;
if ( self ) {
if ( ( self - > ice . media_type & tmedia_audio ) ) {
2013-07-31 23:58:51 +00:00
if ( self - > ice . ctx_audio & & ( ret = tnet_ice_ctx_set_sync_mode ( self - > ice . ctx_audio , sync_mode ) ) ! = 0 ) {
2013-07-31 17:52:20 +00:00
return ret ;
}
}
if ( ( self - > ice . media_type & tmedia_video ) ) {
2013-07-31 23:58:51 +00:00
if ( self - > ice . ctx_video & & ( ret = tnet_ice_ctx_set_sync_mode ( self - > ice . ctx_video , sync_mode ) ) ! = 0 ) {
2013-07-31 17:52:20 +00:00
return ret ;
}
}
}
return 0 ;
}
2013-07-31 23:58:51 +00:00
static int tsip_dialog_invite_ice_set_silent_mode_ctx ( tsip_dialog_invite_t * self , tsk_bool_t silent_mode )
2013-07-31 22:58:53 +00:00
{
2013-07-31 23:58:51 +00:00
int ret = 0 ;
if ( self ) {
if ( ( self - > ice . media_type & tmedia_audio ) ) {
if ( self - > ice . ctx_audio & & ( ret = tnet_ice_ctx_set_silent_mode ( self - > ice . ctx_audio , silent_mode ) ) ! = 0 ) {
return ret ;
}
}
if ( ( self - > ice . media_type & tmedia_video ) ) {
if ( self - > ice . ctx_video & & ( ret = tnet_ice_ctx_set_silent_mode ( self - > ice . ctx_video , silent_mode ) ) ! = 0 ) {
return ret ;
}
}
}
return 0 ;
2013-07-31 22:58:53 +00:00
}
2013-07-31 17:52:20 +00:00
2012-12-03 03:11:21 +00:00
tsk_bool_t tsip_dialog_invite_ice_is_enabled ( const tsip_dialog_invite_t * self )
{
if ( self ) {
return ( self - > supported . ice & & ( tnet_ice_ctx_is_active ( self - > ice . ctx_audio ) | | tnet_ice_ctx_is_active ( self - > ice . ctx_video ) ) ) ;
}
return tsk_false ;
}
tsk_bool_t tsip_dialog_invite_ice_is_connected ( const tsip_dialog_invite_t * self )
{
if ( self ) {
return ( ! tnet_ice_ctx_is_active ( self - > ice . ctx_audio ) | | tnet_ice_ctx_is_connected ( self - > ice . ctx_audio ) )
& & ( ! tnet_ice_ctx_is_active ( self - > ice . ctx_video ) | | tnet_ice_ctx_is_connected ( self - > ice . ctx_video ) ) ;
}
return tsk_false ;
}
tsk_bool_t tsip_dialog_invite_ice_got_local_candidates ( const tsip_dialog_invite_t * self )
{
if ( self ) {
return ( ! tnet_ice_ctx_is_active ( self - > ice . ctx_audio ) | | tnet_ice_ctx_got_local_candidates ( self - > ice . ctx_audio ) )
& & ( ! tnet_ice_ctx_is_active ( self - > ice . ctx_video ) | | tnet_ice_ctx_got_local_candidates ( self - > ice . ctx_video ) ) ;
}
return tsk_false ;
}
int tsip_dialog_invite_ice_save_action ( tsip_dialog_invite_t * self , tsk_fsm_action_id action_id , const tsip_action_t * action , const tsip_message_t * message )
{
if ( ! self ) {
TSK_DEBUG_ERROR ( " Invalid parameter " ) ;
return - 1 ;
}
// There are good reasons to ref() the action and message before safe_free()
// /!\ do not change
self - > ice . last_action_id = action_id ;
action = tsk_object_ref ( ( tsk_object_t * ) action ) ;
TSK_OBJECT_SAFE_FREE ( self - > ice . last_action ) ;
self - > ice . last_action = ( tsip_action_t * ) action ;
message = tsk_object_ref ( ( tsk_object_t * ) message ) ;
TSK_OBJECT_SAFE_FREE ( self - > ice . last_message ) ;
self - > ice . last_message = ( tsip_message_t * ) message ;
return 0 ;
}
int tsip_dialog_invite_ice_process_lo ( tsip_dialog_invite_t * self , const tsdp_message_t * sdp_lo )
{
const tsdp_header_M_t * M ;
const tsdp_header_A_t * A ;
int ret = 0 , i ;
if ( ! self | | ! sdp_lo ) {
TSK_DEBUG_ERROR ( " Invalid parameter " ) ;
return - 1 ;
}
// cancels all ICE contexts without candidates
// this happens if codecs negotiations mismatch for one media out of two or three
for ( i = 0 ; i < 2 ; + + i ) {
struct tnet_ice_ctx_s * ctx = i = = 0 ? self - > ice . ctx_audio : self - > ice . ctx_video ;
const char * media = i = = 0 ? " audio " : " video " ;
if ( tnet_ice_ctx_is_active ( ctx ) ) {
tsk_bool_t cancel = tsk_true ;
if ( ( M = tsdp_message_find_media ( sdp_lo , media ) ) ) {
if ( ( A = tsdp_header_M_findA ( M , " candidate " ) ) ) {
cancel = tsk_false ;
}
}
if ( cancel ) {
ret = tnet_ice_ctx_cancel ( ctx ) ;
}
}
}
return ret ;
}
int tsip_dialog_invite_ice_process_ro ( tsip_dialog_invite_t * self , const tsdp_message_t * sdp_ro , tsk_bool_t is_remote_offer )
{
char * ice_remote_candidates ;
const tsdp_header_M_t * M ;
tsk_size_t index ;
const tsdp_header_A_t * A ;
2013-07-31 19:25:16 +00:00
const tsdp_header_O_t * O ;
2012-12-03 03:11:21 +00:00
const char * sess_ufrag = tsk_null ;
const char * sess_pwd = tsk_null ;
int ret = 0 , i ;
struct tnet_ice_ctx_s * ctx ;
if ( ! self | | ! sdp_ro ) {
TSK_DEBUG_ERROR ( " Invalid parameter " ) ;
return - 1 ;
}
if ( ! self - > ice . ctx_audio & & ! self - > ice . ctx_video ) {
return 0 ;
}
2013-07-31 19:25:16 +00:00
// make sure this is different SDP
if ( ( O = ( const tsdp_header_O_t * ) tsdp_message_get_header ( sdp_ro , tsdp_htype_O ) ) ) {
if ( self - > ice . last_sdp_ro_ver = = ( int32_t ) O - > sess_version ) {
TSK_DEBUG_INFO ( " ICE: ignore processing SDP RO because version haven't changed " ) ;
return 0 ;
}
self - > ice . last_sdp_ro_ver = ( int32_t ) O - > sess_version ;
}
2012-12-03 03:11:21 +00:00
// session level attributes
if ( ( A = tsdp_message_get_headerA ( sdp_ro , " ice-ufrag " ) ) ) {
sess_ufrag = A - > value ;
}
if ( ( A = tsdp_message_get_headerA ( sdp_ro , " ice-pwd " ) ) ) {
sess_pwd = A - > value ;
}
#if 0 // Use RTCWeb Profile (tmedia_profile_rtcweb)
{
const tsdp_header_S_t * S ;
if ( ( S = ( const tsdp_header_S_t * ) tsdp_message_get_header ( sdp_ro , tsdp_htype_S ) ) & & S - > value ) {
self - > ice . is_jingle = tsk_strcontains ( S - > value , tsk_strlen ( S - > value ) , " webrtc " ) ;
}
}
# endif
for ( i = 0 ; i < 2 ; + + i ) {
if ( ( M = tsdp_message_find_media ( sdp_ro , i = = 0 ? " audio " : " video " ) ) ) {
const char * ufrag = sess_ufrag , * pwd = sess_pwd ;
tsk_bool_t remote_use_rtcpmux = ( tsdp_header_M_findA ( M , " rtcp-mux " ) ! = tsk_null ) ;
ctx = ( i = = 0 ? self - > ice . ctx_audio : self - > ice . ctx_video ) ;
ice_remote_candidates = tsk_null ;
index = 0 ;
if ( ( A = tsdp_header_M_findA ( M , " ice-ufrag " ) ) ) {
ufrag = A - > value ;
}
if ( ( A = tsdp_header_M_findA ( M , " ice-pwd " ) ) ) {
pwd = A - > value ;
}
while ( ( A = tsdp_header_M_findA_at ( M , " candidate " , index + + ) ) ) {
tsk_strcat_2 ( & ice_remote_candidates , " %s \r \n " , A - > value ) ;
}
// ICE processing will be automatically stopped if the remote candidates are not valid
// ICE-CONTROLLING role if we are the offerer
ret = tnet_ice_ctx_set_remote_candidates ( ctx , ice_remote_candidates , ufrag , pwd , ! is_remote_offer , self - > ice . is_jingle ) ;
TSK_SAFE_FREE ( ice_remote_candidates ) ;
// Now that 'rtcp-mux' option have been updated by the session pass the value to the ICE ctx
ret = tnet_ice_ctx_set_rtcpmux ( ctx , ( self - > use_rtcpmux & & remote_use_rtcpmux ) ) ;
}
}
return ret ;
}
//--------------------------------------------------------
// == STATE MACHINE BEGIN ==
//--------------------------------------------------------
2013-07-31 17:52:20 +00:00
// Current -> (oINVITE) -> Current
2012-12-03 03:11:21 +00:00
static int x0500_Current_2_Current_X_oINVITE ( va_list * app )
{
int ret ;
tsip_dialog_invite_t * self ;
const tsip_action_t * action ;
const tsip_message_t * message ;
tmedia_type_t media_type ;
2013-07-31 22:58:53 +00:00
static const tsk_bool_t __force_restart_is_yes = tsk_true ;
2012-12-03 03:11:21 +00:00
self = va_arg ( * app , tsip_dialog_invite_t * ) ;
message = va_arg ( * app , const tsip_message_t * ) ;
action = va_arg ( * app , const tsip_action_t * ) ;
media_type = ( action & & action - > media . type ! = tmedia_none ) ? action - > media . type : TSIP_DIALOG_GET_SS ( self ) - > media . type ;
self - > is_client = tsk_true ;
tsip_dialog_invite_ice_save_action ( self , _fsm_action_oINVITE , action , message ) ;
2013-07-31 23:58:51 +00:00
// Cancel without notifying ("silent mode") and perform the operation right now ("sync mode")
tsip_dialog_invite_ice_cancel_silent_and_sync_ctx ( self ) ;
2013-07-31 19:25:16 +00:00
2012-12-03 03:11:21 +00:00
// create ICE context
if ( ( ret = tsip_dialog_invite_ice_create_ctx ( self , media_type ) ) ) {
TSK_DEBUG_ERROR ( " tsip_dialog_invite_ice_create_ctx() failed " ) ;
return ret ;
}
2013-07-31 17:52:20 +00:00
// For now disable ICE timers until we receive the 2xx
ret = tsip_dialog_invite_ice_timers_set ( self , - 1 ) ;
2013-07-31 23:58:51 +00:00
// Start ICE
ret = tsip_dialog_invite_ice_start_ctx ( self ) ;
2012-12-03 03:11:21 +00:00
// alert the user only if we are in initial state which means that it's not media update
if ( TSIP_DIALOG ( self ) - > state = = tsip_initial ) {
TSIP_DIALOG_SIGNAL ( self , tsip_event_code_dialog_connecting , " Dialog connecting " ) ;
}
return ret ;
}
2013-07-31 17:52:20 +00:00
// Current -> (iINVITE) -> Current
2012-12-03 03:11:21 +00:00
static int x0500_Current_2_Current_X_iINVITE ( va_list * app )
{
int ret ;
tsip_dialog_invite_t * self ;
const tsip_action_t * action ;
const tsip_message_t * message ;
self = va_arg ( * app , tsip_dialog_invite_t * ) ;
message = va_arg ( * app , const tsip_message_t * ) ;
action = va_arg ( * app , const tsip_action_t * ) ;
self - > is_client = tsk_false ;
ret = tsip_dialog_invite_ice_save_action ( self , _fsm_action_iINVITE , action , message ) ;
2013-07-31 23:58:51 +00:00
// Cancel without notifying ("silent mode") and perform the operation right now ("sync mode")
tsip_dialog_invite_ice_cancel_silent_and_sync_ctx ( self ) ;
2013-07-31 22:58:53 +00:00
2012-12-03 03:11:21 +00:00
// set remote candidates
if ( TSIP_MESSAGE_HAS_CONTENT ( message ) ) {
if ( tsk_striequals ( " application/sdp " , TSIP_MESSAGE_CONTENT_TYPE ( message ) ) ) {
tsdp_message_t * sdp_ro ;
if ( ! ( sdp_ro = tsdp_message_parse ( TSIP_MESSAGE_CONTENT_DATA ( message ) , TSIP_MESSAGE_CONTENT_DATA_LENGTH ( message ) ) ) ) {
TSK_DEBUG_ERROR ( " Failed to parse remote sdp message " ) ;
return - 2 ;
}
// create ICE context
if ( ( ret = tsip_dialog_invite_ice_create_ctx ( self , tmedia_type_from_sdp ( sdp_ro ) ) ) ) {
TSK_DEBUG_ERROR ( " tsip_dialog_invite_ice_create_ctx() failed " ) ;
return ret ;
}
ret = tsip_dialog_invite_ice_process_ro ( self , sdp_ro , tsk_true ) ;
TSK_OBJECT_SAFE_FREE ( sdp_ro ) ;
}
else {
TSK_DEBUG_ERROR ( " [%s] content-type is not supportted " , TSIP_MESSAGE_CONTENT_TYPE ( message ) ) ;
return - 3 ;
}
}
2013-07-31 17:52:20 +00:00
// For now disable ICE timers until we send the 2xx and receive the ACK
ret = tsip_dialog_invite_ice_timers_set ( self , - 1 ) ;
2013-07-31 23:58:51 +00:00
// Start ICE
ret = tsip_dialog_invite_ice_start_ctx ( self ) ;
2012-12-03 03:11:21 +00:00
return ret ;
}
//++++++++++++++++++++++++++++++++++++++++++++++++++++++++
// == STATE MACHINE END ==
//++++++++++++++++++++++++++++++++++++++++++++++++++++++++
static int tsip_dialog_invite_ice_callback ( const tnet_ice_event_t * e )
{
int ret = 0 ;
tsip_dialog_invite_t * dialog ;
TSK_DEBUG_INFO ( " ICE callback: %s " , e - > phrase ) ;
dialog = tsk_object_ref ( TSK_OBJECT ( e - > userdata ) ) ;
// Do not lock: caller is thread safe
switch ( e - > type ) {
case tnet_ice_event_type_gathering_completed :
case tnet_ice_event_type_conncheck_succeed :
case tnet_ice_event_type_conncheck_failed :
case tnet_ice_event_type_cancelled :
{
if ( dialog - > ice . last_action_id ! = tsk_fsm_state_none ) {
if ( tsip_dialog_invite_ice_got_local_candidates ( dialog ) ) {
2014-05-11 22:25:35 +00:00
ret = tsip_dialog_fsm_act ( TSIP_DIALOG ( dialog ) , dialog - > ice . last_action_id , dialog - > ice . last_message , dialog - > ice . last_action ) ;
2012-12-03 03:11:21 +00:00
dialog - > ice . last_action_id = tsk_fsm_state_none ;
}
}
if ( dialog - > ice . start_smgr ) {
ret = tsip_dialog_invite_msession_start ( dialog ) ;
}
break ;
}
// fatal errors which discard ICE process
case tnet_ice_event_type_gathering_host_candidates_failed :
case tnet_ice_event_type_gathering_reflexive_candidates_failed :
2014-05-11 22:25:35 +00:00
case tnet_ice_event_type_gathering_relay_candidates_failed :
2012-12-03 03:11:21 +00:00
{
2014-05-11 22:25:35 +00:00
if ( dialog - > ice . last_action_id ! = tsk_fsm_state_none ) {
ret = tsip_dialog_fsm_act ( TSIP_DIALOG ( dialog ) , dialog - > ice . last_action_id , dialog - > ice . last_message , dialog - > ice . last_action ) ;
2012-12-03 03:11:21 +00:00
dialog - > ice . last_action_id = tsk_fsm_state_none ;
}
break ;
}
2014-05-11 22:25:35 +00:00
// TURN session disconnected while we're in call
case tnet_ice_event_type_turn_connection_broken :
{
ret = tsip_dialog_fsm_act_2 ( TSIP_DIALOG ( dialog ) , _fsm_action_oBYE ) ;
break ;
}
2012-12-05 06:35:45 +00:00
default : break ;
2012-12-03 03:11:21 +00:00
}
TSK_OBJECT_SAFE_FREE ( dialog ) ;
return ret ;
}
static int tsip_dialog_invite_ice_audio_callback ( const tnet_ice_event_t * e )
{
return tsip_dialog_invite_ice_callback ( e ) ;
}
static int tsip_dialog_invite_ice_video_callback ( const tnet_ice_event_t * e )
{
return tsip_dialog_invite_ice_callback ( e ) ;
2012-05-02 10:42:55 +00:00
}