From 9567e2bf62e4957ec110d5227f908bdb30f51027 Mon Sep 17 00:00:00 2001 From: Yann Diorcet Date: Thu, 3 Jan 2013 15:38:03 +0100 Subject: [PATCH] Working sip upnp --- coreapi/callbacks.c | 5 + coreapi/linphonecall.c | 11 +- coreapi/linphonecore.c | 44 ++- coreapi/private.h | 5 +- coreapi/proxy.c | 8 +- coreapi/upnp.c | 750 ++++++++++++++++++++++++++++++++--------- coreapi/upnp.h | 41 ++- mediastreamer2 | 2 +- 8 files changed, 679 insertions(+), 187 deletions(-) diff --git a/coreapi/callbacks.c b/coreapi/callbacks.c index 134c336ab..b3d1a6bb7 100644 --- a/coreapi/callbacks.c +++ b/coreapi/callbacks.c @@ -416,6 +416,11 @@ static void call_accept_update(LinphoneCore *lc, LinphoneCall *call){ linphone_core_update_ice_from_remote_media_description(call,rmd); linphone_core_update_local_media_description_from_ice(call->localdesc,call->ice_session); } +#ifdef BUILD_UPNP + if(call->upnp_session != NULL) { + linphone_core_update_local_media_description_from_upnp(call->localdesc,call->upnp_session); + } +#endif sal_call_accept(call->op); md=sal_call_get_final_media_description(call->op); if (md && !sal_media_description_empty(md)) diff --git a/coreapi/linphonecall.c b/coreapi/linphonecall.c index 8af948df5..2ab398ba5 100644 --- a/coreapi/linphonecall.c +++ b/coreapi/linphonecall.c @@ -283,6 +283,11 @@ void linphone_call_make_local_media_description(LinphoneCore *lc, LinphoneCall * linphone_core_update_local_media_description_from_ice(md, call->ice_session); linphone_core_update_ice_state_in_call_stats(call); } +#ifdef BUILD_UPNP + if(call->upnp_session != NULL) { + linphone_core_update_local_media_description_from_upnp(md, call->upnp_session); + } +#endif linphone_address_destroy(addr); call->localdesc=md; if (old_md) sal_media_description_unref(old_md); @@ -439,7 +444,7 @@ LinphoneCall * linphone_call_new_outgoing(struct _LinphoneCore *lc, LinphoneAddr call->op=sal_op_new(lc->sal); sal_op_set_user_pointer(call->op,call); call->core=lc; - linphone_core_get_local_ip(lc,linphone_address_get_domain(to),call->localip); + linphone_core_get_public_ip(lc,linphone_address_get_domain(to),call->localip); linphone_call_init_common(call,from,to); call->params=*params; if (linphone_core_get_firewall_policy(call->core) == LinphonePolicyUseIce) { @@ -486,7 +491,7 @@ LinphoneCall * linphone_call_new_incoming(LinphoneCore *lc, LinphoneAddress *fro } linphone_address_clean(from); - linphone_core_get_local_ip(lc,linphone_address_get_domain(from),call->localip); + linphone_core_get_public_ip(lc,linphone_address_get_domain(from),call->localip); linphone_call_init_common(call, from, to); call->log->call_id=ms_strdup(sal_op_get_call_id(op)); /*must be known at that time*/ linphone_core_init_default_params(lc, &call->params); @@ -1693,7 +1698,7 @@ void linphone_call_delete_ice_session(LinphoneCall *call){ #ifdef BUILD_UPNP void linphone_call_delete_upnp_session(LinphoneCall *call){ if(call->upnp_session!=NULL) { - upnp_session_destroy(call->upnp_session); + upnp_session_destroy(call); call->upnp_session=NULL; } } diff --git a/coreapi/linphonecore.c b/coreapi/linphonecore.c index 82e39685f..ddd9eaea6 100644 --- a/coreapi/linphonecore.c +++ b/coreapi/linphonecore.c @@ -66,7 +66,6 @@ static void linphone_core_free_hooks(LinphoneCore *lc); #include "enum.h" const char *linphone_core_get_nat_address_resolved(LinphoneCore *lc); -void linphone_core_get_local_ip(LinphoneCore *lc, const char *dest, char *result); static void toggle_video_preview(LinphoneCore *lc, bool_t val); /* relative path where is stored local ring*/ @@ -1307,13 +1306,20 @@ int linphone_core_set_primary_contact(LinphoneCore *lc, const char *contact) /*result must be an array of chars at least LINPHONE_IPADDR_SIZE */ -void linphone_core_get_local_ip(LinphoneCore *lc, const char *dest, char *result){ +void linphone_core_get_public_ip(LinphoneCore *lc, const char *dest, char *result){ const char *ip; if (linphone_core_get_firewall_policy(lc)==LinphonePolicyUseNatAddress && (ip=linphone_core_get_nat_address_resolved(lc))!=NULL){ strncpy(result,ip,LINPHONE_IPADDR_SIZE); return; } +#ifdef BUILD_UPNP + else if (linphone_core_get_firewall_policy(lc)==LinphonePolicyUseUpnp + && lc->upnp.state == LinphoneUpnpStateOk) { + ip = upnp_igd_get_external_ipaddress(lc->upnp.upnp_igd_ctxt); + strncpy(result,ip,LINPHONE_IPADDR_SIZE); + } +#endif if (linphone_core_get_local_ip_for(lc->sip_conf.ipv6_enabled ? AF_INET6 : AF_INET,dest,result)==0) return; /*else fallback to SAL routine that will attempt to find the most realistic interface */ @@ -1334,7 +1340,7 @@ static void update_primary_contact(LinphoneCore *lc){ ms_error("Could not parse identity contact !"); url=linphone_address_new("sip:unknown@unkwownhost"); } - linphone_core_get_local_ip(lc, NULL, tmp); + linphone_core_get_public_ip(lc, NULL, tmp); if (strcmp(tmp,"127.0.0.1")==0 || strcmp(tmp,"::1")==0 ){ ms_warning("Local loopback network only !"); lc->sip_conf.loopback_only=TRUE; @@ -2006,7 +2012,7 @@ void linphone_core_iterate(LinphoneCore *lc){ 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."); + ms_warning("uPnP mapping has not finished yet, proceeded with the call without uPnP anyway."); linphone_call_delete_upnp_session(call); } linphone_core_start_invite(lc,call); @@ -2639,9 +2645,14 @@ void linphone_core_notify_incoming_call(LinphoneCore *lc, LinphoneCall *call){ int linphone_core_start_update_call(LinphoneCore *lc, LinphoneCall *call){ const char *subject; call->camera_active=call->params.has_video; - if (call->ice_session != NULL) + if (call->ice_session != NULL) { linphone_core_update_local_media_description_from_ice(call->localdesc, call->ice_session); - + } +#ifdef BUILD_UPNP + if(call->upnp_session != NULL) { + linphone_core_update_local_media_description_from_upnp(call->localdesc, call->upnp_session); + } +#endif if (call->params.in_conference){ subject="Conference"; }else{ @@ -2756,6 +2767,11 @@ int linphone_core_start_accept_call_update(LinphoneCore *lc, LinphoneCall *call) } linphone_core_update_local_media_description_from_ice(call->localdesc, call->ice_session); } +#ifdef BUILD_UPNP + if(call->upnp_session != NULL) { + linphone_core_update_local_media_description_from_upnp(call->localdesc, call->upnp_session); + } +#endif sal_call_set_local_media_description(call->op,call->localdesc); sal_call_accept(call->op); md=sal_call_get_final_media_description(call->op); @@ -3124,8 +3140,14 @@ int linphone_core_pause_call(LinphoneCore *lc, LinphoneCall *call) return -1; } linphone_call_make_local_media_description(lc,call); - if (call->ice_session != NULL) + if (call->ice_session != NULL) { linphone_core_update_local_media_description_from_ice(call->localdesc, call->ice_session); + } +#ifdef BUILD_UPNP + if(call->upnp_session != NULL) { + linphone_core_update_local_media_description_from_upnp(call->localdesc, call->upnp_session); + } +#endif if (sal_media_description_has_dir(call->resultdesc,SalStreamSendRecv)){ sal_media_description_set_dir(call->localdesc,SalStreamSendOnly); subject="Call on hold"; @@ -3203,8 +3225,14 @@ int linphone_core_resume_call(LinphoneCore *lc, LinphoneCall *the_call) if (call->audiostream) audio_stream_play(call->audiostream, NULL); linphone_call_make_local_media_description(lc,the_call); - if (call->ice_session != NULL) + if (call->ice_session != NULL) { linphone_core_update_local_media_description_from_ice(call->localdesc, call->ice_session); + } +#ifdef BUILD_UPNP + if(call->upnp_session != NULL) { + linphone_core_update_local_media_description_from_upnp(call->localdesc, call->upnp_session); + } +#endif sal_call_set_local_media_description(call->op,call->localdesc); sal_media_description_set_dir(call->localdesc,SalStreamSendRecv); if (call->params.in_conference && !call->current_params.in_conference) subject="Conference"; diff --git a/coreapi/private.h b/coreapi/private.h index d4abf16ca..e110a03e3 100644 --- a/coreapi/private.h +++ b/coreapi/private.h @@ -206,7 +206,7 @@ int set_lock_file(); int get_lock_file(); int remove_lock_file(); void check_sound_device(LinphoneCore *lc); -void linphone_core_get_local_ip(LinphoneCore *lc, const char *to, char *result); +void linphone_core_get_public_ip(LinphoneCore *lc, const char *to, char *result); bool_t host_has_ipv6_network(); bool_t lp_spawn_command_line_sync(const char *command, char **result,int *command_ret); @@ -593,6 +593,9 @@ int linphone_core_del_call( LinphoneCore *lc, LinphoneCall *call); int linphone_core_set_as_current_call(LinphoneCore *lc, LinphoneCall *call); int linphone_core_get_calls_nb(const LinphoneCore *lc); +void linphone_core_add_iterate_hook(LinphoneCore *lc, LinphoneCoreIterateHook hook, void *hook_data); +void linphone_core_remove_iterate_hook(LinphoneCore *lc, LinphoneCoreIterateHook hook, void *hook_data); + void linphone_core_set_state(LinphoneCore *lc, LinphoneGlobalState gstate, const char *message); void linphone_call_make_local_media_description(LinphoneCore *lc, LinphoneCall *call); void linphone_core_update_streams(LinphoneCore *lc, LinphoneCall *call, SalMediaDescription *new_md); diff --git a/coreapi/proxy.c b/coreapi/proxy.c index 3b47af42c..ff046f92e 100644 --- a/coreapi/proxy.c +++ b/coreapi/proxy.c @@ -268,7 +268,7 @@ static char *guess_contact_for_register(LinphoneProxyConfig *obj){ LCSipTransports tr; LinphoneAddress *contact; - linphone_core_get_local_ip(obj->lc,host,localip); + linphone_core_get_public_ip(obj->lc,host,localip); contact=linphone_address_new(obj->reg_identity); linphone_address_set_domain (contact,localip); linphone_address_set_port_int(contact,linphone_core_get_sip_port(obj->lc)); @@ -427,7 +427,7 @@ static dial_plan_t const dial_plans[]={ {"Congo Democratic Republic" ,"CD" , "243" , 9 , "00" }, {"Cook Islands" ,"CK" , "682" , 5 , "00" }, {"Costa Rica" ,"CR" , "506" , 8 , "00" }, - {"C™te d'Ivoire" ,"AD" , "225" , 8 , "00" }, + {"C�te d'Ivoire" ,"AD" , "225" , 8 , "00" }, {"Croatia" ,"HR" , "385" , 9 , "00" }, {"Cuba" ,"CU" , "53" , 8 , "119" }, {"Cyprus" ,"CY" , "357" , 8 , "00" }, @@ -545,7 +545,7 @@ static dial_plan_t const dial_plans[]={ {"Portugal" ,"PT" , "351" , 9 , "00" }, {"Puerto Rico" ,"PR" , "1" , 10 , "011" }, {"Qatar" ,"QA" , "974" , 8 , "00" }, - {"RŽunion Island" ,"RE" , "262" , 9 , "011" }, + {"R�union Island" ,"RE" , "262" , 9 , "011" }, {"Romania" ,"RO" , "40" , 9 , "00" }, {"Russian Federation" ,"RU" , "7" , 10 , "8" }, {"Rwanda" ,"RW" , "250" , 9 , "00" }, @@ -556,7 +556,7 @@ static dial_plan_t const dial_plans[]={ {"Saint Vincent and the Grenadines","VC" , "1" , 10 , "011" }, {"Samoa" ,"WS" , "685" , 7 , "0" }, {"San Marino" ,"SM" , "378" , 10 , "00" }, - {"S‹o TomŽ and Pr’ncipe" ,"ST" , "239" , 7 , "00" }, + {"S�o Tom� and Pr�ncipe" ,"ST" , "239" , 7 , "00" }, {"Saudi Arabia" ,"SA" , "966" , 9 , "00" }, {"Senegal" ,"SN" , "221" , 9 , "00" }, {"Serbia" ,"RS" , "381" , 9 , "00" }, diff --git a/coreapi/upnp.c b/coreapi/upnp.c index d0b71b3e4..0de861210 100644 --- a/coreapi/upnp.c +++ b/coreapi/upnp.c @@ -21,14 +21,51 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. #include "private.h" #define UPNP_MAX_RETRY 4 +#define UPNP_SECTION_NAME "uPnP" + +/* Define private types */ +typedef struct _LpItem{ + char *key; + char *value; +} LpItem; + +typedef struct _LpSection{ + char *name; + MSList *items; +} LpSection; + +typedef struct _LpConfig{ + FILE *file; + char *filename; + MSList *sections; + int modified; + int readonly; +} LpConfig; + +/* Declare private functions */ +LpSection *lp_config_find_section(LpConfig *lpconfig, const char *name); +void lp_section_remove_item(LpSection *sec, LpItem *item); +void lp_config_set_string(LpConfig *lpconfig,const char *section, const char *key, const char *value); + +bool_t linphone_core_upnp_hook(void *data); UpnpPortBinding *upnp_port_binding_new(); -UpnpPortBinding * upnp_port_binding_retain(UpnpPortBinding *port); +UpnpPortBinding *upnp_port_binding_copy(const UpnpPortBinding *port); +bool_t upnp_port_binding_equal(const UpnpPortBinding *port1, const UpnpPortBinding *port2); +UpnpPortBinding *upnp_port_binding_retain(UpnpPortBinding *port); void upnp_port_binding_release(UpnpPortBinding *port); +MSList *upnp_config_list_port_bindings(struct _LpConfig *lpc); +int upnp_config_add_port_binding(LinphoneCore *lc, const UpnpPortBinding *port); +int upnp_config_remove_port_binding(LinphoneCore *lc, const UpnpPortBinding *port); + int upnp_context_send_remove_port_binding(LinphoneCore *lc, UpnpPortBinding *port); int upnp_context_send_add_port_binding(LinphoneCore *lc, UpnpPortBinding *port); +/** + * uPnP Callbacks + */ + /* 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; @@ -56,7 +93,6 @@ void linphone_upnp_igd_callback(void *cookie, upnp_igd_event event, void *arg) { const char *ip_address = NULL; const char *connection_status = NULL; bool_t nat_enabled = FALSE; - ms_mutex_lock(&lupnp->mutex); switch(event) { @@ -68,13 +104,14 @@ void linphone_upnp_igd_callback(void *cookie, upnp_igd_event event, void *arg) { nat_enabled = upnp_igd_get_nat_enabled(lupnp->upnp_igd_ctxt); if(ip_address == NULL || connection_status == NULL) { - lupnp->state = UPNP_Pending; + ms_message("uPnP IGD: Pending"); + lupnp->state = LinphoneUpnpStatePending; } else if(strcasecmp(connection_status, "Connected") || !nat_enabled) { - lupnp->state = UPNP_Ko; + ms_message("uPnP IGD: Not Available"); + lupnp->state = LinphoneUpnpStateNotAvailable; } else { - // Emit add port binding - // Emit remove old port binding - lupnp->state = UPNP_Ok; + ms_message("uPnP IGD: Connected"); + lupnp->state = LinphoneUpnpStateOk; } break; @@ -82,33 +119,56 @@ void linphone_upnp_igd_callback(void *cookie, upnp_igd_event event, void *arg) { 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 + port_mapping->external_port = mapping->remote_port; + port_mapping->state = LinphoneUpnpStateOk; + ms_message("uPnP IGD: Added port binding %s|%d->%s:%d", + (port_mapping->protocol == UPNP_IGD_IP_PROTOCOL_TCP)? "TCP":"UDP", + port_mapping->external_port, + port_mapping->local_addr, + port_mapping->local_port); + upnp_config_add_port_binding(lc, port_mapping); + 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); + if(upnp_context_send_add_port_binding(lc, port_mapping) != 0) { + ms_error("uPnP IGD: Can't add port binding %s|%d->%s:%d", + (port_mapping->protocol == UPNP_IGD_IP_PROTOCOL_TCP)? "TCP":"UDP", + port_mapping->external_port, + port_mapping->local_addr, + port_mapping->local_port); + } + 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 + port_mapping->state = LinphoneUpnpStateIdle; + ms_message("uPnP IGD: Removed port binding %s|%d->%d", + (port_mapping->protocol == UPNP_IGD_IP_PROTOCOL_TCP)? "TCP":"UDP", + port_mapping->external_port, + port_mapping->local_port); + upnp_config_remove_port_binding(lc, port_mapping); + 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) + if(upnp_context_send_remove_port_binding(lc, port_mapping) != 0) { + ms_error("uPnP IGD: Can't remove port binding %s|%d->%d", + (port_mapping->protocol == UPNP_IGD_IP_PROTOCOL_TCP)? "TCP":"UDP", + port_mapping->external_port, + port_mapping->local_port); + upnp_config_remove_port_binding(lc, port_mapping); + } + upnp_port_binding_release(port_mapping); break; @@ -119,161 +179,74 @@ void linphone_upnp_igd_callback(void *cookie, upnp_igd_event event, void *arg) { 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); -} +/** + * uPnP Context + */ int upnp_context_init(LinphoneCore *lc) { LCSipTransports transport; UpnpContext *lupnp = &lc->upnp; + const char *ip_address; + ms_mutex_init(&lupnp->mutex, NULL); - lupnp->state = UPNP_Idle; + lupnp->pending_configs = NULL; + lupnp->state = LinphoneUpnpStateIdle; + lupnp->old_state = LinphoneUpnpStateIdle; + ms_message("uPnP IGD: Init"); 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; + lupnp->sip_udp->local_port = transport.udp_port; } 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; + lupnp->sip_tcp->local_port = transport.tcp_port; } 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; + lupnp->sip_tls->local_port = transport.tls_port; } else { lupnp->sip_tls = NULL; } + + linphone_core_add_iterate_hook(lc, linphone_core_upnp_hook, lc); + 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 ) { - lupnp->state = UPNP_Ko; + if(lupnp->upnp_igd_ctxt == NULL) { + lupnp->state = LinphoneUpnpStateKo; ms_error("Can't create uPnP IGD context"); return -1; } - lupnp->state = UPNP_Pending; + + ip_address = upnp_igd_get_local_ipaddress(lupnp->upnp_igd_ctxt); + if(lupnp->sip_udp != NULL) { + strncpy(lupnp->sip_udp->local_addr, ip_address, sizeof(lupnp->sip_udp->local_addr)); + } + if(lupnp->sip_tcp != NULL) { + strncpy(lupnp->sip_tcp->local_addr, ip_address, sizeof(lupnp->sip_tcp->local_addr)); + } + if(lupnp->sip_tls != NULL) { + strncpy(lupnp->sip_tls->local_addr, ip_address, sizeof(lupnp->sip_tls->local_addr)); + } + + lupnp->state = LinphoneUpnpStatePending; return 0; } void upnp_context_uninit(LinphoneCore *lc) { - // Emit remove port (sip & saved) UpnpContext *lupnp = &lc->upnp; + linphone_core_remove_iterate_hook(lc, linphone_core_upnp_hook, lc); + if(lupnp->sip_udp != NULL) { upnp_port_binding_release(lupnp->sip_udp); } @@ -287,19 +260,334 @@ void upnp_context_uninit(LinphoneCore *lc) { upnp_igd_destroy(lupnp->upnp_igd_ctxt); } ms_mutex_destroy(&lupnp->mutex); + + ms_message("uPnP IGD: Uninit"); } +int upnp_context_send_add_port_binding(LinphoneCore *lc, UpnpPortBinding *port) { + UpnpContext *lupnp = &lc->upnp; + upnp_igd_port_mapping mapping; + int ret; + if(port->state == LinphoneUpnpStateIdle) { + port->external_port = -1; + port->retry = 0; + port->state = LinphoneUpnpStateAdding; + } else if(port->state != LinphoneUpnpStateAdding) { + ms_error("uPnP: try to add a port binding in wrong state: %d", port->state); + return -2; + } + + if(port->retry >= UPNP_MAX_RETRY) { + ret = -1; + } else { + mapping.cookie = upnp_port_binding_retain(port); + mapping.local_port = port->local_port; + mapping.local_host = port->local_addr; + if(port->external_port == -1) + mapping.remote_port = rand()%1024 + 1024; // TODO: use better method + else + mapping.remote_port = port->external_port; + 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(ret != 0) { + port->state = LinphoneUpnpStateKo; + } + 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 == LinphoneUpnpStateIdle) { + port->retry = 0; + port->state = LinphoneUpnpStateRemoving; + } else if(port->state != LinphoneUpnpStateRemoving) { + ms_error("uPnP: try to remove a port binding in wrong state: %d", port->state); + return -2; + } + + if(port->retry >= UPNP_MAX_RETRY) { + ret = -1; + } else { + mapping.cookie = upnp_port_binding_retain(port); + mapping.remote_port = port->external_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 = LinphoneUpnpStateKo; + } + return ret; +} + +/* + * uPnP Core interfaces + */ + +int upnp_call_process(LinphoneCall *call) { + LinphoneCore *lc = call->core; + UpnpContext *lupnp = &lc->upnp; + int ret = -1; + UpnpState oldState; + const char *local_addr, *external_addr; + + ms_mutex_lock(&lupnp->mutex); + // Don't handle when the call + if(lupnp->state == LinphoneUpnpStateOk && call->upnp_session != NULL) { + ret = 0; + local_addr = upnp_igd_get_local_ipaddress(lupnp->upnp_igd_ctxt); + external_addr = upnp_igd_get_external_ipaddress(lupnp->upnp_igd_ctxt); + + /* + * Audio part + */ + strncpy(call->upnp_session->audio->rtp->local_addr, local_addr, LINPHONE_IPADDR_SIZE); + strncpy(call->upnp_session->audio->rtp->external_addr, external_addr, LINPHONE_IPADDR_SIZE); + call->upnp_session->audio->rtp->local_port = call->audio_port; + strncpy(call->upnp_session->audio->rtcp->local_addr, local_addr, LINPHONE_IPADDR_SIZE); + strncpy(call->upnp_session->audio->rtcp->external_addr, external_addr, LINPHONE_IPADDR_SIZE); + call->upnp_session->audio->rtcp->local_port = call->audio_port+1; + if(call->upnp_session->audio->rtp->state == LinphoneUpnpStateIdle && 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 == LinphoneUpnpStateOk && 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 == LinphoneUpnpStateIdle && 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 == LinphoneUpnpStateOk && call->audiostream == NULL) { + // Remove audio port binding + upnp_context_send_remove_port_binding(lc, call->upnp_session->audio->rtcp); + } + if((call->upnp_session->audio->rtp->state == LinphoneUpnpStateOk || call->upnp_session->audio->rtp->state == LinphoneUpnpStateIdle) && + (call->upnp_session->audio->rtcp->state == LinphoneUpnpStateOk || call->upnp_session->audio->rtcp->state == LinphoneUpnpStateIdle)) { + call->upnp_session->audio->state = LinphoneUpnpStateOk; + } else if(call->upnp_session->audio->rtp->state == LinphoneUpnpStateAdding || + call->upnp_session->audio->rtp->state == LinphoneUpnpStateRemoving || + call->upnp_session->audio->rtcp->state == LinphoneUpnpStateAdding || + call->upnp_session->audio->rtcp->state == LinphoneUpnpStateRemoving) { + call->upnp_session->audio->state = LinphoneUpnpStatePending; + } else if(call->upnp_session->audio->rtcp->state == LinphoneUpnpStateKo || + call->upnp_session->audio->rtp->state == LinphoneUpnpStateKo) { + call->upnp_session->audio->state = LinphoneUpnpStateKo; + } else { + call->upnp_session->audio->state = LinphoneUpnpStateIdle; + } + + /* + * Video part + */ + strncpy(call->upnp_session->video->rtp->local_addr, local_addr, LINPHONE_IPADDR_SIZE); + strncpy(call->upnp_session->video->rtp->external_addr, external_addr, LINPHONE_IPADDR_SIZE); + call->upnp_session->video->rtp->local_port = call->video_port; + strncpy(call->upnp_session->video->rtcp->local_addr, local_addr, LINPHONE_IPADDR_SIZE); + strncpy(call->upnp_session->video->rtcp->external_addr, external_addr, LINPHONE_IPADDR_SIZE); + call->upnp_session->video->rtcp->local_port = call->video_port+1; + if(call->upnp_session->video->rtp->state == LinphoneUpnpStateIdle && 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 == LinphoneUpnpStateOk && 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 == LinphoneUpnpStateIdle && 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 == LinphoneUpnpStateOk && call->videostream == NULL) { + // Remove video port binding + upnp_context_send_remove_port_binding(lc, call->upnp_session->video->rtcp); + } + if((call->upnp_session->video->rtp->state == LinphoneUpnpStateOk || call->upnp_session->video->rtp->state == LinphoneUpnpStateIdle) && + (call->upnp_session->video->rtcp->state == LinphoneUpnpStateOk || call->upnp_session->video->rtcp->state == LinphoneUpnpStateIdle)) { + call->upnp_session->video->state = LinphoneUpnpStateOk; + } else if(call->upnp_session->video->rtp->state == LinphoneUpnpStateAdding || + call->upnp_session->video->rtp->state == LinphoneUpnpStateRemoving || + call->upnp_session->video->rtcp->state == LinphoneUpnpStateAdding || + call->upnp_session->video->rtcp->state == LinphoneUpnpStateRemoving) { + call->upnp_session->video->state = LinphoneUpnpStatePending; + } else if(call->upnp_session->video->rtcp->state == LinphoneUpnpStateKo || + call->upnp_session->video->rtp->state == LinphoneUpnpStateKo) { + call->upnp_session->video->state = LinphoneUpnpStateKo; + } else { + call->upnp_session->video->state = LinphoneUpnpStateIdle; + } + + /* + * Update session state + */ + oldState = call->upnp_session->state; + if(call->upnp_session->audio->state == LinphoneUpnpStateOk && + call->upnp_session->video->state == LinphoneUpnpStateOk) { + call->upnp_session->state = LinphoneUpnpStateOk; + } else if(call->upnp_session->audio->state == LinphoneUpnpStatePending || + call->upnp_session->video->state == LinphoneUpnpStatePending) { + call->upnp_session->state = LinphoneUpnpStatePending; + } else if(call->upnp_session->audio->state == LinphoneUpnpStateKo || + call->upnp_session->video->state == LinphoneUpnpStateKo) { + call->upnp_session->state = LinphoneUpnpStateKo; + } else { + call->upnp_session->state = LinphoneUpnpStateIdle; + } + + /* When change is done proceed update */ + if(oldState != LinphoneUpnpStateOk && oldState != LinphoneUpnpStateKo && + (call->upnp_session->state == LinphoneUpnpStateOk || call->upnp_session->state == LinphoneUpnpStateKo)) { + switch (call->state) { + case LinphoneCallUpdating: + linphone_core_start_update_call(call->core, call); + break; + case LinphoneCallUpdatedByRemote: + linphone_core_start_accept_call_update(call->core, call); + break; + case LinphoneCallOutgoingInit: + linphone_core_proceed_with_invite_if_ready(call->core, call, NULL); + break; + case LinphoneCallIdle: + linphone_core_notify_incoming_call(call->core, call); + break; + default: + break; + } + } + } + + ms_mutex_unlock(&lupnp->mutex); + return ret; +} + +int linphone_core_update_upnp(LinphoneCore *lc, LinphoneCall *call) { + return upnp_call_process(call); +} + +bool_t linphone_core_upnp_hook(void *data) { + char key[64]; + MSList *port_bindings = NULL; + MSList *port_bindings_item; + UpnpPortBinding *port_mapping; + LinphoneCore *lc = (LinphoneCore *)data; + UpnpContext *lupnp = &lc->upnp; + ms_mutex_lock(&lupnp->mutex); + + if(lupnp->state == LinphoneUpnpStateOk && lupnp->old_state != LinphoneUpnpStateOk) { + // Remove old mapping + port_bindings = upnp_config_list_port_bindings(lc->config); + if(port_bindings != NULL) { + for(port_bindings_item = port_bindings;port_bindings_item!=NULL;port_bindings_item=port_bindings_item->next) { + port_mapping = (UpnpPortBinding *)port_bindings_item->data; + upnp_context_send_remove_port_binding(lc, port_mapping); + } + ms_list_for_each(port_bindings,(void (*)(void*))upnp_port_binding_release); + port_bindings = ms_list_free(port_bindings); + } + } + + if(lupnp->state == LinphoneUpnpStateOk && lupnp->old_state != LinphoneUpnpStateOk) { + // Add port bindings + if(lupnp->sip_udp != NULL) { + upnp_context_send_add_port_binding(lc, lupnp->sip_udp); + } + if(lupnp->sip_tcp != NULL) { + upnp_context_send_add_port_binding(lc, lupnp->sip_tcp); + } + if(lupnp->sip_tls != NULL) { + upnp_context_send_add_port_binding(lc, lupnp->sip_tls); + } + } + + /* Update configs */ + for(port_bindings_item = lupnp->pending_configs;port_bindings_item!=NULL;port_bindings_item=port_bindings_item->next) { + port_mapping = (UpnpPortBinding *)port_bindings_item->data; + if(port_mapping->state == LinphoneUpnpStateAdding) { + snprintf(key, sizeof(key), "%s-%d-%d", + (port_mapping->protocol == UPNP_IGD_IP_PROTOCOL_TCP)? "TCP":"UDP", + port_mapping->external_port, + port_mapping->local_port); + lp_config_set_string(lc->config, UPNP_SECTION_NAME, key, ""); + } + if(port_mapping->state == LinphoneUpnpStateRemoving) { + snprintf(key, sizeof(key), "%s-%d-%d", + (port_mapping->protocol == UPNP_IGD_IP_PROTOCOL_TCP)? "TCP":"UDP", + port_mapping->external_port, + port_mapping->local_port); + lp_config_set_string(lc->config, UPNP_SECTION_NAME, key, NULL); + } + } + ms_list_for_each(lupnp->pending_configs,(void (*)(void*))upnp_port_binding_release); + lupnp->pending_configs = ms_list_free(lupnp->pending_configs); + + lupnp->old_state = lupnp->state; + ms_mutex_unlock(&lupnp->mutex); + return TRUE; +} + +void linphone_core_update_local_media_description_from_upnp(SalMediaDescription *desc, UpnpSession *session) { + int i; + SalStreamDescription *stream; + UpnpStream *upnpStream; + + for (i = 0; i < desc->nstreams; i++) { + stream = &desc->streams[i]; + upnpStream = NULL; + if(stream->type == SalAudio) { + upnpStream = session->audio; + } else if(stream->type == SalVideo) { + upnpStream = session->video; + } + if(upnpStream != NULL) { + if(upnpStream->rtp->state == LinphoneUpnpStateOk) { + strncpy(stream->rtp_addr, upnpStream->rtp->external_addr, LINPHONE_IPADDR_SIZE); + stream->rtp_port = upnpStream->rtp->external_port; + } + if(upnpStream->rtcp->state == LinphoneUpnpStateOk) { + strncpy(stream->rtcp_addr, upnpStream->rtcp->external_addr, LINPHONE_IPADDR_SIZE); + stream->rtcp_port = upnpStream->rtcp->external_port; + } + } + } +} + + +/* + * uPnP Port Binding + */ + UpnpPortBinding *upnp_port_binding_new() { UpnpPortBinding *port = NULL; port = ms_new0(UpnpPortBinding,1); ms_mutex_init(&port->mutex, NULL); - port->state = UPNP_Idle; + port->state = LinphoneUpnpStateIdle; + port->local_addr[0] = '\0'; port->local_port = -1; - port->remote_port = -1; + port->external_addr[0] = '\0'; + port->external_port = -1; port->ref = 1; return port; } +UpnpPortBinding *upnp_port_binding_copy(const UpnpPortBinding *port) { + UpnpPortBinding *new_port = NULL; + new_port = ms_new0(UpnpPortBinding,1); + memcpy(new_port, port, sizeof(UpnpPortBinding)); + ms_mutex_init(&new_port->mutex, NULL); + new_port->ref = 1; + return new_port; +} + +bool_t upnp_port_binding_equal(const UpnpPortBinding *port1, const UpnpPortBinding *port2) { + return port1->protocol == port2->protocol && port1->local_port == port2->local_port && + port1->external_port && port2->external_port; +} + UpnpPortBinding *upnp_port_binding_retain(UpnpPortBinding *port) { ms_mutex_lock(&port->mutex); port->ref++; @@ -318,25 +606,177 @@ void upnp_port_binding_release(UpnpPortBinding *port) { 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; +/* + * uPnP Stream + */ + +UpnpStream* upnp_stream_new() { + UpnpStream *stream = ms_new0(UpnpStream,1); + stream->state = LinphoneUpnpStateIdle; + stream->rtp = upnp_port_binding_new(); + stream->rtp->protocol = UPNP_IGD_IP_PROTOCOL_UDP; + stream->rtcp = upnp_port_binding_new(); + stream->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); +void upnp_stream_destroy(UpnpStream* stream) { + upnp_port_binding_release(stream->rtp); + upnp_port_binding_release(stream->rtcp); + ms_free(stream); +} + + +/* + * uPnP Session + */ + +UpnpSession* upnp_session_new() { + UpnpSession *session = ms_new0(UpnpSession,1); + session->state = LinphoneUpnpStateIdle; + session->audio = upnp_stream_new(); + session->video = upnp_stream_new(); + return NULL; +} + +void upnp_session_destroy(LinphoneCall* call) { + LinphoneCore *lc = call->core; + + /* Remove bindings */ + if(call->upnp_session->audio->rtp->state != LinphoneUpnpStateKo && call->upnp_session->audio->rtp->state != LinphoneUpnpStateIdle) { + upnp_context_send_remove_port_binding(lc, call->upnp_session->audio->rtp); + } + if(call->upnp_session->audio->rtcp->state != LinphoneUpnpStateKo && call->upnp_session->audio->rtcp->state != LinphoneUpnpStateIdle) { + upnp_context_send_remove_port_binding(lc, call->upnp_session->audio->rtcp); + } + if(call->upnp_session->video->rtp->state != LinphoneUpnpStateKo && call->upnp_session->video->rtp->state != LinphoneUpnpStateIdle) { + upnp_context_send_remove_port_binding(lc, call->upnp_session->video->rtp); + } + if(call->upnp_session->video->rtcp->state != LinphoneUpnpStateKo && call->upnp_session->video->rtcp->state != LinphoneUpnpStateIdle) { + upnp_context_send_remove_port_binding(lc, call->upnp_session->video->rtcp); + } + + upnp_stream_destroy(call->upnp_session->audio); + upnp_stream_destroy(call->upnp_session->video); + ms_free(call->upnp_session); + call->upnp_session = NULL; +} + + +/* + * uPnP Config + */ + +MSList *upnp_config_list_port_bindings(struct _LpConfig *lpc) { + char protocol_str[4]; // TCP or UDP + upnp_igd_ip_protocol protocol; + int external_port; + int local_port; + MSList *retList = NULL; + UpnpPortBinding *port; + bool_t valid; + MSList *elem; + MSList *prev_elem; + LpItem *item; + LpSection *sec=lp_config_find_section(lpc, UPNP_SECTION_NAME); + if(sec == NULL) + return retList; + + elem = sec->items; + while(elem != NULL) { + item=(LpItem*)elem->data; + valid = TRUE; + if(sscanf(item->key, "%3s-%i-%i", protocol_str, &external_port, &local_port) == 3) { + if(strcasecmp(protocol_str, "TCP") == 0) { + protocol = UPNP_IGD_IP_PROTOCOL_TCP; + } else if(strcasecmp(protocol_str, "UDP") == 0) { + protocol = UPNP_IGD_IP_PROTOCOL_UDP; + } else { + valid = FALSE; + } + if(valid) { + port = upnp_port_binding_new(); + port->protocol = protocol; + port->external_port = external_port; + port->local_port = local_port; + retList = ms_list_append(retList, port); + } + } else { + valid = FALSE; + } + prev_elem = elem; + elem = ms_list_next(elem); + if(!valid) { + ms_warning("uPnP configuration invalid line: %s", item->key); + lp_section_remove_item(sec, item); + } + } + + return retList; +} + +int upnp_config_add_port_binding(LinphoneCore *lc, const UpnpPortBinding *port) { + UpnpContext *lupnp = &lc->upnp; + MSList *list = lupnp->pending_configs; + UpnpPortBinding *list_port; + bool_t remove; + bool_t add = TRUE; + while(list != NULL) { + remove = FALSE; + list_port = (UpnpPortBinding *)list->data; + if(upnp_port_binding_equal(list_port, port) == TRUE) { + if(list_port->state == LinphoneUpnpStateAdding) { + add = FALSE; + break; + } + if(list_port->state == LinphoneUpnpStateRemoving) { + remove = TRUE; + break; + } + } + list = ms_list_next(list); + } + + if(remove) { + lupnp->pending_configs = ms_list_remove(list, list_port); + } else if(add) { + list_port = upnp_port_binding_copy(port); + list_port->state = LinphoneUpnpStateAdding; + lupnp->pending_configs = ms_list_append(list, list_port); + } + + return 0; +} + +int upnp_config_remove_port_binding(LinphoneCore *lc, const UpnpPortBinding *port) { + UpnpContext *lupnp = &lc->upnp; + MSList *list = lupnp->pending_configs; + UpnpPortBinding *list_port; + bool_t remove; + bool_t add = TRUE; + while(list != NULL) { + remove = FALSE; + list_port = (UpnpPortBinding *)list->data; + if(upnp_port_binding_equal(list_port, port)) { + if(list_port->state == LinphoneUpnpStateRemoving) { + add = FALSE; + break; + } + if(list_port->state == LinphoneUpnpStateAdding) { + remove = TRUE; + break; + } + } + list = ms_list_next(list); + } + + if(remove) { + lupnp->pending_configs = ms_list_remove(list, list_port); + } else if(add) { + list_port = upnp_port_binding_copy(port); + list_port->state = LinphoneUpnpStateRemoving; + lupnp->pending_configs = ms_list_append(list, list_port); + } + + return 0; } diff --git a/coreapi/upnp.h b/coreapi/upnp.h index f0a93d1d6..492b35234 100644 --- a/coreapi/upnp.h +++ b/coreapi/upnp.h @@ -22,33 +22,42 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. #include "mediastreamer2/upnp_igd.h" #include "linphonecore.h" +#include "sal.h" typedef enum { - UPNP_Idle, - UPNP_Pending, - UPNP_Ok, - UPNP_Ko, + LinphoneUpnpStateIdle, + LinphoneUpnpStatePending, + LinphoneUpnpStateAdding, // Only used by port binding + LinphoneUpnpStateRemoving, // Only used by port binding + LinphoneUpnpStateNotAvailable, // Only used by uPnP context + LinphoneUpnpStateOk, + LinphoneUpnpStateKo, } UpnpState; -typedef struct _UpnpSession UpnpSession; typedef struct _UpnpPortBinding { ms_mutex_t mutex; UpnpState state; upnp_igd_ip_protocol protocol; + char local_addr[LINPHONE_IPADDR_SIZE]; int local_port; - int remote_port; + char external_addr[LINPHONE_IPADDR_SIZE]; + int external_port; int retry; int ref; } UpnpPortBinding; -struct _UpnpSession { - UpnpPortBinding *audio_rtp; - UpnpPortBinding *audio_rtcp; - UpnpPortBinding *video_rtp; - UpnpPortBinding *video_rtcp; +typedef struct _UpnpStream { + UpnpPortBinding *rtp; + UpnpPortBinding *rtcp; UpnpState state; -}; +} UpnpStream; + +typedef struct _UpnpSession { + UpnpStream *audio; + UpnpStream *video; + UpnpState state; +} UpnpSession; typedef struct _UpnpContext { upnp_igd_context *upnp_igd_ctxt; @@ -56,15 +65,17 @@ typedef struct _UpnpContext { UpnpPortBinding *sip_tls; UpnpPortBinding *sip_udp; UpnpState state; - MSList *pending_bindinds; + UpnpState old_state; + MSList *pending_configs; + ms_mutex_t mutex; } UpnpContext; - +void linphone_core_update_local_media_description_from_upnp(SalMediaDescription *desc, UpnpSession *session); int linphone_core_update_upnp(LinphoneCore *lc, LinphoneCall *call); int upnp_call_process(LinphoneCall *call); UpnpSession* upnp_session_new(); -void upnp_session_destroy(UpnpSession* session); +void upnp_session_destroy(LinphoneCall* call); int upnp_context_init(LinphoneCore *lc); void upnp_context_uninit(LinphoneCore *lc); diff --git a/mediastreamer2 b/mediastreamer2 index 39998cb24..34de96d6b 160000 --- a/mediastreamer2 +++ b/mediastreamer2 @@ -1 +1 @@ -Subproject commit 39998cb245606b904a77e093db168057f87bf8b0 +Subproject commit 34de96d6b33f58b248f7d46e9c25edb11e6a5426