static int tsip_dialog_invite_ice_create_ctx(tsip_dialog_invite_t * self, tmedia_type_t media_type)
{
	int32_t transport_idx;
	int ret = 0;
	if(!self){
		TSK_DEBUG_ERROR("Invalid parameter");
		return -1;
	}
	transport_idx = TSIP_DIALOG_GET_STACK(self)->network.transport_idx_default;
	if (!self->ice.ctx_audio && (media_type & tmedia_audio)) {
		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);
		if (!self->ice.ctx_audio) {
			TSK_DEBUG_ERROR("Failed to create ICE audio context");
			return -2;
		}
		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);
		ret = tnet_ice_ctx_set_stun_enabled(self->ice.ctx_audio, TSIP_DIALOG_GET_SS(self)->media.enable_icestun);
		ret = tnet_ice_ctx_set_turn_enabled(self->ice.ctx_audio, TSIP_DIALOG_GET_SS(self)->media.enable_iceturn);
		ret = tnet_ice_ctx_set_rtcpmux(self->ice.ctx_audio, self->use_rtcpmux);
	}
	if (!self->ice.ctx_video && (media_type & tmedia_video)) {
		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);
		if (!self->ice.ctx_video) {
			TSK_DEBUG_ERROR("Failed to create ICE video context");
			return -2;
		}
		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);
		ret = tnet_ice_ctx_set_stun_enabled(self->ice.ctx_video, TSIP_DIALOG_GET_SS(self)->media.enable_icestun);
		ret = tnet_ice_ctx_set_turn_enabled(self->ice.ctx_video, TSIP_DIALOG_GET_SS(self)->media.enable_iceturn);
		ret = tnet_ice_ctx_set_rtcpmux(self->ice.ctx_video, self->use_rtcpmux);
	}

	// set media type
	ret = tsip_dialog_invite_ice_set_media_type(self, media_type);

	// update session manager with the right ICE contexts
	if (self->msession_mgr) {
		ret = tmedia_session_mgr_set_ice_ctx(self->msession_mgr, self->ice.ctx_audio, self->ice.ctx_video);
	}

	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;
	const tsdp_header_O_t *O;
	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;
	}

	// 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;
	}	

	// 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;
}
static int tsip_dialog_invite_ice_create_ctx(tsip_dialog_invite_t * self, tmedia_type_t media_type)
{
	int32_t transport_idx;
	if(!self){
		TSK_DEBUG_ERROR("Invalid parameter");
		return -1;
	}
	transport_idx = TSIP_DIALOG_GET_STACK(self)->network.transport_idx_default;
	if(!self->ice.ctx_audio && (media_type & tmedia_audio)){
		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);
		if(!self->ice.ctx_audio){
			TSK_DEBUG_ERROR("Failed to create ICE audio context");
			return -2;
		}
		tnet_ice_ctx_set_stun(self->ice.ctx_audio, "stun.l.google.com", 19302, "Doubango", "*****@*****.**", "stun-password"); //FIXME
		tnet_ice_ctx_set_rtcpmux(self->ice.ctx_audio, self->use_rtcpmux);
	}
	if(!self->ice.ctx_video && (media_type & tmedia_video)){
		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);
		if(!self->ice.ctx_video){
			TSK_DEBUG_ERROR("Failed to create ICE video context");
			return -2;
		}
		tnet_ice_ctx_set_stun(self->ice.ctx_video, "stun.l.google.com", 19302, "Doubango", "*****@*****.**", "stun-password"); // FIXME
		tnet_ice_ctx_set_rtcpmux(self->ice.ctx_video, self->use_rtcpmux);
	}

	// "none" comparison is used to exclude the "first call"
	if(self->ice.media_type != tmedia_none && self->ice.media_type != media_type){
		// cancels contexts associated to old medias
		if(self->ice.ctx_audio && !(media_type & tmedia_audio)){
			tnet_ice_ctx_cancel(self->ice.ctx_audio);
		}
		if(self->ice.ctx_video && !(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 && (!(media_type & tmedia_audio) && (self->ice.media_type & tmedia_audio))){
			//tnet_ice_ctx_cancel(self->ice.ctx_audio);
		}
		if(self->ice.ctx_video && (!(media_type & tmedia_video) && (self->ice.media_type & tmedia_video))){
			//tnet_ice_ctx_cancel(self->ice.ctx_video);
		}
	}

	self->ice.media_type = media_type;
	

	// For now disable timers until both parties get candidates
	// (RECV ACK) or RECV (200 OK)
	tsip_dialog_invite_ice_timers_set(self, -1);

	// update session manager with the right ICE contexts
	if(self->msession_mgr){
		tmedia_session_mgr_set_ice_ctx(self->msession_mgr, self->ice.ctx_audio, self->ice.ctx_video);
	}

	return 0;
}