linphone_call_delete_ice_session(call);
linphone_call_stop_media_streams_for_ice_gathering(call);
}
+ if (call->upnp_session != NULL) {
+ ms_warning("uPnP mapping has not finished yet, proceeded with the call withoutt uPnP anyway.");
+ linphone_call_delete_upnp_session(call);
+ }
linphone_core_start_invite(lc,call);
}
if (call->state==LinphoneCallIncomingReceived){
LinphoneAddress *parsed_url2=NULL;
char *real_url=NULL;
LinphoneCall *call;
- bool_t use_ice = FALSE;
+ bool_t defer = FALSE;
linphone_core_preempt_sound_resources(lc);
linphone_call_delete_ice_session(call);
linphone_call_stop_media_streams_for_ice_gathering(call);
} else {
- use_ice = TRUE;
+ defer = TRUE;
+ }
+ }
+ else if (linphone_core_get_firewall_policy(call->core) == LinphonePolicyUseUpnp) {
+#ifdef BUILD_UPNP
+ linphone_call_init_media_streams(call);
+ call->start_time=time(NULL);
+ if (linphone_core_update_upnp(lc,call)<0) {
+ /* uPnP port mappings failed, proceed with the call anyway. */
+ linphone_call_delete_upnp_session(call);
+ } else {
+ defer = TRUE;
}
+#endif
}
if (call->dest_proxy==NULL && lc->sip_conf.ping_with_options==TRUE){
sal_op_set_user_pointer(call->ping_op,call);
call->start_time=time(NULL);
}else{
- if (use_ice==FALSE) linphone_core_start_invite(lc,call);
+ if (defer==FALSE) linphone_core_start_invite(lc,call);
}
if (real_url!=NULL) ms_free(real_url);
linphone_call_set_state(call,LinphoneCallUpdating,"Updating call");
#ifdef VIDEO_ENABLED
bool_t has_video = call->params.has_video;
- if ((call->ice_session != NULL) && (call->videostream != NULL) && !params->has_video) {
- ice_session_remove_check_list(call->ice_session, call->videostream->ice_check_list);
- call->videostream->ice_check_list = NULL;
+ if(call->videostream != NULL && !params->has_video) {
+ if ((call->ice_session != NULL)) {
+ ice_session_remove_check_list(call->ice_session, call->videostream->ice_check_list);
+ call->videostream->ice_check_list = NULL;
+ }
}
call->params = *params;
linphone_call_make_local_media_description(lc, call);
- if ((call->ice_session != NULL) && !has_video && call->params.has_video) {
- /* Defer call update until the ICE candidates gathering process has finished. */
- ms_message("Defer call update to gather ICE candidates");
+ if (!has_video && call->params.has_video) {
+ if (call->ice_session != NULL) {
+ /* Defer call update until the ICE candidates gathering process has finished. */
+ ms_message("Defer call update to gather ICE candidates");
+ linphone_call_init_video_stream(call);
+ video_stream_prepare_video(call->videostream);
+ if (linphone_core_gather_ice_candidates(lc,call)<0) {
+ /* Ice candidates gathering failed, proceed with the call anyway. */
+ linphone_call_delete_ice_session(call);
+ } else {
+ return err;
+ }
+ }
+ }
+#ifdef BUILD_UPNP
+ if(call->upnp_session != NULL) {
+ ms_message("Defer call update to add uPnP port mappings");
linphone_call_init_video_stream(call);
video_stream_prepare_video(call->videostream);
- if (linphone_core_gather_ice_candidates(lc,call)<0) {
- /* Ice candidates gathering failed, proceed with the call anyway. */
- linphone_call_delete_ice_session(call);
- } else return err;
+ if (linphone_core_update_upnp(lc, call)<0) {
+ /* uPnP port mappings failed, proceed with the call anyway. */
+ linphone_call_delete_upnp_session(call);
+ } else {
+ return err;
+ }
}
+#endif //BUILD_UPNP
#endif
err = linphone_core_start_update_call(lc, call);
}else{
}
#endif
}
+
+#if BUILD_UPNP
+ if(call->upnp_session != NULL) {
+ }
+#endif //BUILD_UPNP
+
linphone_core_start_accept_call_update(lc, call);
return 0;
}
#include "upnp.h"
#include "private.h"
+#define UPNP_MAX_RETRY 4
+
+UpnpPortBinding *upnp_port_binding_new();
+UpnpPortBinding * upnp_port_binding_retain(UpnpPortBinding *port);
+void upnp_port_binding_release(UpnpPortBinding *port);
+
+int upnp_context_send_remove_port_binding(LinphoneCore *lc, UpnpPortBinding *port);
+int upnp_context_send_add_port_binding(LinphoneCore *lc, UpnpPortBinding *port);
+
/* Convert uPnP IGD logs to ortp logs */
void linphone_upnp_igd_print(void *cookie, upnp_igd_print_level level, const char *fmt, va_list list) {
int ortp_level = ORTP_DEBUG;
void linphone_upnp_igd_callback(void *cookie, upnp_igd_event event, void *arg) {
LinphoneCore *lc = (LinphoneCore *)cookie;
UpnpContext *lupnp = &lc->upnp;
+ upnp_igd_port_mapping *mapping = NULL;
+ UpnpPortBinding *port_mapping = NULL;
+ const char *ip_address = NULL;
+ const char *connection_status = NULL;
+ bool_t nat_enabled = FALSE;
+
+ ms_mutex_lock(&lupnp->mutex);
+
switch(event) {
case UPNP_IGD_EXTERNAL_IPADDRESS_CHANGED:
case UPNP_IGD_NAT_ENABLED_CHANGED:
case UPNP_IGD_CONNECTION_STATUS_CHANGED:
+ ip_address = upnp_igd_get_external_ipaddress(lupnp->upnp_igd_ctxt);
+ connection_status = upnp_igd_get_connection_status(lupnp->upnp_igd_ctxt);
+ nat_enabled = upnp_igd_get_nat_enabled(lupnp->upnp_igd_ctxt);
+
+ if(ip_address == NULL || connection_status == NULL) {
+ lupnp->state = UPNP_Pending;
+ } else if(strcasecmp(connection_status, "Connected") || !nat_enabled) {
+ lupnp->state = UPNP_Ko;
+ } else {
+ // Emit add port binding
+ // Emit remove old port binding
+ lupnp->state = UPNP_Ok;
+ }
+
+ break;
+
+ case UPNP_IGD_PORT_MAPPING_ADD_SUCCESS:
+ mapping = (upnp_igd_port_mapping *) arg;
+ port_mapping = (UpnpPortBinding*) mapping->cookie;
+ port_mapping->remote_port = mapping->remote_port;
+ port_mapping->state = UPNP_Ok;
+ // TODO: SAVE IN CONFIG THE PORT
+ upnp_port_binding_release(port_mapping);
+ break;
+
+ case UPNP_IGD_PORT_MAPPING_ADD_FAILURE:
+ mapping = (upnp_igd_port_mapping *) arg;
+ port_mapping = (UpnpPortBinding*) mapping->cookie;
+ upnp_context_send_add_port_binding(lc, port_mapping);
+ upnp_port_binding_release(port_mapping);
+ break;
+
+ case UPNP_IGD_PORT_MAPPING_REMOVE_SUCCESS:
+ mapping = (upnp_igd_port_mapping *) arg;
+ port_mapping = (UpnpPortBinding*) mapping->cookie;
+ port_mapping->remote_port = -1;
+ port_mapping->state = UPNP_Idle;
+ // TODO: REMOVE FROM CONFIG THE PORT
+ upnp_port_binding_release(port_mapping);
+ break;
+
+ case UPNP_IGD_PORT_MAPPING_REMOVE_FAILURE:
+ mapping = (upnp_igd_port_mapping *) arg;
+ port_mapping = (UpnpPortBinding*) mapping->cookie;
+ upnp_context_send_remove_port_binding(lc, port_mapping);
+ // TODO: REMOVE FROM CONFIG THE PORT (DON'T TRY ANYMORE)
+ upnp_port_binding_release(port_mapping);
break;
default:
break;
}
+
+ ms_mutex_unlock(&lupnp->mutex);
+}
+
+int upnp_context_send_add_port_binding(LinphoneCore *lc, UpnpPortBinding *port) {
+ UpnpContext *lupnp = &lc->upnp;
+ upnp_igd_port_mapping mapping;
+ char * local_host = NULL;
+ int ret;
+ if(port->state == UPNP_Idle) {
+ port->remote_port = -1;
+ port->retry = 0;
+ port->state = UPNP_Pending;
+ }
+ if(port->retry >= UPNP_MAX_RETRY) {
+ ret = -1;
+ } else {
+ local_host = upnp_igd_get_local_ipaddress(lupnp->upnp_igd_ctxt);
+ mapping.cookie = upnp_port_binding_retain(port);
+ mapping.local_port = port->local_port;
+ mapping.local_host = local_host;
+ mapping.remote_port = rand()%1024 + 1024;
+ mapping.remote_host = "";
+ mapping.description = PACKAGE_NAME;
+ mapping.protocol = port->protocol;
+
+ port->retry++;
+ ret = upnp_igd_add_port_mapping(lupnp->upnp_igd_ctxt, &mapping);
+ if(local_host != NULL) {
+ free(local_host);
+ }
+ }
+ if(ret != 0) {
+ port->state = UPNP_Ko;
+ }
+ return ret;
+}
+
+int upnp_context_send_remove_port_binding(LinphoneCore *lc, UpnpPortBinding *port) {
+ UpnpContext *lupnp = &lc->upnp;
+ upnp_igd_port_mapping mapping;
+ int ret;
+ if(port->state == UPNP_Idle) {
+ port->retry = 0;
+ port->state = UPNP_Pending;
+ }
+ if(port->retry >= UPNP_MAX_RETRY) {
+ ret = -1;
+ } else {
+ mapping.cookie = upnp_port_binding_retain(port);
+ mapping.remote_port = port->remote_port;
+ mapping.remote_host = "";
+ mapping.protocol = port->protocol;
+ port->retry++;
+ ret = upnp_igd_delete_port_mapping(lupnp->upnp_igd_ctxt, &mapping);
+ }
+ if(ret != 0) {
+ port->state = UPNP_Ko;
+ }
+ return ret;
+}
+
+int upnp_call_process(LinphoneCall *call) {
+ LinphoneCore *lc = call->core;
+ UpnpContext *lupnp = &lc->upnp;
+ int ret = -1;
+
+ ms_mutex_lock(&lupnp->mutex);
+ // Don't handle when the call
+ if(lupnp->state != UPNP_Ko && call->upnp_session != NULL) {
+ ret = 0;
+
+ /*
+ * Audio part
+ */
+ call->upnp_session->audio_rtp->local_port = call->audio_port;
+ call->upnp_session->audio_rtcp->local_port = call->audio_port+1;
+ if(call->upnp_session->audio_rtp->state == UPNP_Idle && call->audiostream != NULL) {
+ // Add audio port binding
+ upnp_context_send_add_port_binding(lc, call->upnp_session->audio_rtp);
+ } else if(call->upnp_session->audio_rtp->state == UPNP_Ok && call->audiostream == NULL) {
+ // Remove audio port binding
+ upnp_context_send_remove_port_binding(lc, call->upnp_session->audio_rtp);
+ }
+ if(call->upnp_session->audio_rtcp->state == UPNP_Idle && call->audiostream != NULL) {
+ // Add audio port binding
+ upnp_context_send_add_port_binding(lc, call->upnp_session->audio_rtcp);
+ } else if(call->upnp_session->audio_rtcp->state == UPNP_Ok && call->audiostream == NULL) {
+ // Remove audio port binding
+ upnp_context_send_remove_port_binding(lc, call->upnp_session->audio_rtcp);
+ }
+
+ /*
+ * Video part
+ */
+ call->upnp_session->video_rtp->local_port = call->video_port;
+ call->upnp_session->video_rtcp->local_port = call->video_port+1;
+ if(call->upnp_session->video_rtp->state == UPNP_Idle && call->videostream != NULL) {
+ // Add video port binding
+ upnp_context_send_add_port_binding(lc, call->upnp_session->video_rtp);
+ } else if(call->upnp_session->video_rtp->state == UPNP_Ok && call->videostream == NULL) {
+ // Remove video port binding
+ upnp_context_send_remove_port_binding(lc, call->upnp_session->video_rtp);
+ }
+ if(call->upnp_session->video_rtcp->state == UPNP_Idle && call->videostream != NULL) {
+ // Add video port binding
+ upnp_context_send_add_port_binding(lc, call->upnp_session->video_rtcp);
+ } else if(call->upnp_session->video_rtcp->state == UPNP_Ok && call->videostream == NULL) {
+ // Remove video port binding
+ upnp_context_send_remove_port_binding(lc, call->upnp_session->video_rtcp);
+ }
+ }
+ ms_mutex_unlock(&lupnp->mutex);
+ return ret;
+}
+
+int linphone_core_update_upnp(LinphoneCore *lc, LinphoneCall *call) {
+ return upnp_call_process(call);
}
int upnp_context_init(LinphoneCore *lc) {
+ LCSipTransports transport;
UpnpContext *lupnp = &lc->upnp;
+ ms_mutex_init(&lupnp->mutex, NULL);
+ lupnp->state = UPNP_Idle;
+
+ linphone_core_get_sip_transports(lc, &transport);
+ if(transport.udp_port != 0) {
+ lupnp->sip_udp = upnp_port_binding_new();
+ lupnp->sip_udp->protocol = UPNP_IGD_IP_PROTOCOL_UDP;
+ } else {
+ lupnp->sip_udp = NULL;
+ }
+ if(transport.tcp_port != 0) {
+ lupnp->sip_tcp = upnp_port_binding_new();
+ lupnp->sip_tcp->protocol = UPNP_IGD_IP_PROTOCOL_TCP;
+ } else {
+ lupnp->sip_tcp = NULL;
+ }
+ if(transport.tls_port != 0) {
+ lupnp->sip_tls = upnp_port_binding_new();
+ lupnp->sip_tls->protocol = UPNP_IGD_IP_PROTOCOL_TCP;
+ } else {
+ lupnp->sip_tls = NULL;
+ }
lupnp->upnp_igd_ctxt = NULL;
lupnp->upnp_igd_ctxt = upnp_igd_create(linphone_upnp_igd_callback, linphone_upnp_igd_print, lc);
- if(lupnp->upnp_igd_ctxt == NULL) {
+ if(lupnp->upnp_igd_ctxt == NULL ) {
+ lupnp->state = UPNP_Ko;
ms_error("Can't create uPnP IGD context");
return -1;
}
+ lupnp->state = UPNP_Pending;
return 0;
}
void upnp_context_uninit(LinphoneCore *lc) {
+ // Emit remove port (sip & saved)
UpnpContext *lupnp = &lc->upnp;
+ if(lupnp->sip_udp != NULL) {
+ upnp_port_binding_release(lupnp->sip_udp);
+ }
+ if(lupnp->sip_tcp != NULL) {
+ upnp_port_binding_release(lupnp->sip_tcp);
+ }
+ if(lupnp->sip_tls != NULL) {
+ upnp_port_binding_release(lupnp->sip_tls);
+ }
if(lupnp->upnp_igd_ctxt != NULL) {
upnp_igd_destroy(lupnp->upnp_igd_ctxt);
}
+ ms_mutex_destroy(&lupnp->mutex);
+}
+
+UpnpPortBinding *upnp_port_binding_new() {
+ UpnpPortBinding *port = NULL;
+ port = ms_new0(UpnpPortBinding,1);
+ ms_mutex_init(&port->mutex, NULL);
+ port->state = UPNP_Idle;
+ port->local_port = -1;
+ port->remote_port = -1;
+ port->ref = 1;
+ return port;
+}
+
+UpnpPortBinding *upnp_port_binding_retain(UpnpPortBinding *port) {
+ ms_mutex_lock(&port->mutex);
+ port->ref++;
+ ms_mutex_unlock(&port->mutex);
+ return port;
+}
+
+void upnp_port_binding_release(UpnpPortBinding *port) {
+ ms_mutex_lock(&port->mutex);
+ if(--port->ref == 0) {
+ ms_mutex_unlock(&port->mutex);
+ ms_mutex_destroy(&port->mutex);
+ ms_free(port);
+ return;
+ }
+ ms_mutex_unlock(&port->mutex);
}
UpnpSession* upnp_session_new() {
+ UpnpSession *session = ms_new0(UpnpSession,1);
+ session->state = UPNP_Idle;
+ session->audio_rtp = upnp_port_binding_new();
+ session->audio_rtp->protocol = UPNP_IGD_IP_PROTOCOL_UDP;
+ session->audio_rtcp = upnp_port_binding_new();
+ session->audio_rtcp->protocol = UPNP_IGD_IP_PROTOCOL_UDP;
+ session->video_rtp = upnp_port_binding_new();
+ session->video_rtp->protocol = UPNP_IGD_IP_PROTOCOL_UDP;
+ session->video_rtcp = upnp_port_binding_new();
+ session->video_rtcp->protocol = UPNP_IGD_IP_PROTOCOL_UDP;
return NULL;
}
+
+void upnp_session_destroy(UpnpSession* session) {
+ upnp_port_binding_release(session->audio_rtp);
+ upnp_port_binding_release(session->audio_rtcp);
+ upnp_port_binding_release(session->video_rtp);
+ upnp_port_binding_release(session->video_rtp);
+ // TODO: send remove
+ ms_free(session);
+}