From 92c9faec6e9fce601a995b76b001a1a451adbdb0 Mon Sep 17 00:00:00 2001 From: Yann Diorcet Date: Tue, 8 Jan 2013 17:06:27 +0100 Subject: [PATCH] Improve uPnP --- coreapi/linphonecore.c | 36 ++--- coreapi/upnp.c | 289 +++++++++++++++++++++++++++-------------- coreapi/upnp.h | 11 +- 3 files changed, 222 insertions(+), 114 deletions(-) diff --git a/coreapi/linphonecore.c b/coreapi/linphonecore.c index b277d8ae4..17b1c84b9 100644 --- a/coreapi/linphonecore.c +++ b/coreapi/linphonecore.c @@ -2714,20 +2714,20 @@ int linphone_core_update_call(LinphoneCore *lc, LinphoneCall *call, const Linpho 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_update_upnp(lc, call)<0) { - /* uPnP port mappings failed, proceed with the call anyway. */ - linphone_call_delete_upnp_session(call); - } else { - return err; + 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_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{ @@ -3000,6 +3000,11 @@ int linphone_core_abort_call(LinphoneCore *lc, LinphoneCall *call, const char *e lc->ringstream=NULL; } linphone_call_stop_media_streams(call); + +#ifdef BUILD_UPNP + linphone_call_delete_upnp_session(call); +#endif //BUILD_UPNP + if (lc->vtable.display_status!=NULL) lc->vtable.display_status(lc,_("Call aborted") ); linphone_call_set_state(call,LinphoneCallError,error); @@ -4958,6 +4963,11 @@ static void linphone_core_uninit(LinphoneCore *lc) usleep(50000); #endif } + +#ifdef BUILD_UPNP + upnp_context_uninit(lc); +#endif //BUILD_UPNP + if (lc->friends) ms_list_for_each(lc->friends,(void (*)(void *))linphone_friend_close_subscriptions); linphone_core_set_state(lc,LinphoneGlobalShutdown,"Shutting down"); @@ -4983,10 +4993,6 @@ static void linphone_core_uninit(LinphoneCore *lc) lc->config = NULL; /* Mark the config as NULL to block further calls */ sip_setup_unregister_all(); -#ifdef BUILD_UPNP - upnp_context_uninit(lc); -#endif //BUILD_UPNP - ms_list_for_each(lc->call_logs,(void (*)(void*))linphone_call_log_destroy); lc->call_logs=ms_list_free(lc->call_logs); diff --git a/coreapi/upnp.c b/coreapi/upnp.c index 336e0b78f..3a68cbcbe 100644 --- a/coreapi/upnp.c +++ b/coreapi/upnp.c @@ -20,7 +20,8 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. #include "upnp.h" #include "private.h" -#define UPNP_MAX_RETRY 4 +#define UPNP_ADD_MAX_RETRY 4 +#define UPNP_REMOVE_MAX_RETRY 4 #define UPNP_SECTION_NAME "uPnP" /* Define private types */ @@ -57,8 +58,8 @@ void upnp_port_binding_log(int level, const char *msg, const UpnpPortBinding *po 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); +void upnp_config_add_port_binding(LinphoneCore *lc, const UpnpPortBinding *port); +void 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); @@ -113,6 +114,9 @@ void linphone_upnp_igd_callback(void *cookie, upnp_igd_event event, void *arg) { lupnp->state = LinphoneUpnpStateNotAvailable; } else { ms_message("uPnP IGD: Connected"); + if(lupnp->state != LinphoneUpnpStateOk) { + lupnp->clean = TRUE; // Remove saved port mapping configurations + } lupnp->state = LinphoneUpnpStateOk; } @@ -126,17 +130,18 @@ void linphone_upnp_igd_callback(void *cookie, upnp_igd_event event, void *arg) { upnp_port_binding_log(ORTP_MESSAGE, "Added port binding", port_mapping); upnp_config_add_port_binding(lc, port_mapping); + lupnp->pending_bindings = ms_list_remove(lupnp->pending_bindings, 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; - port_mapping->external_port = -1; //Force a new random port if(upnp_context_send_add_port_binding(lc, port_mapping) != 0) { upnp_port_binding_log(ORTP_ERROR, "Can't add port binding", port_mapping); } + lupnp->pending_bindings = ms_list_remove(lupnp->pending_bindings, port_mapping); upnp_port_binding_release(port_mapping); break; @@ -147,6 +152,7 @@ void linphone_upnp_igd_callback(void *cookie, upnp_igd_event event, void *arg) { upnp_port_binding_log(ORTP_MESSAGE, "Removed port binding", port_mapping); upnp_config_remove_port_binding(lc, port_mapping); + lupnp->pending_bindings = ms_list_remove(lupnp->pending_bindings, port_mapping); upnp_port_binding_release(port_mapping); break; @@ -158,6 +164,7 @@ void linphone_upnp_igd_callback(void *cookie, upnp_igd_event event, void *arg) { upnp_config_remove_port_binding(lc, port_mapping); } + lupnp->pending_bindings = ms_list_remove(lupnp->pending_bindings, port_mapping); upnp_port_binding_release(port_mapping); break; @@ -165,6 +172,13 @@ void linphone_upnp_igd_callback(void *cookie, upnp_igd_event event, void *arg) { break; } + if(lupnp->pending_bindings == NULL) { + if(lupnp->cleaning == TRUE) { + lupnp->emit = TRUE; // Emit port bindings + lupnp->cleaning = FALSE; + } + pthread_cond_signal(&lupnp->cond); + } ms_mutex_unlock(&lupnp->mutex); } @@ -179,9 +193,15 @@ int upnp_context_init(LinphoneCore *lc) { const char *ip_address; ms_mutex_init(&lupnp->mutex, NULL); - lupnp->pending_configs = NULL; + ms_cond_init(&lupnp->cond, NULL); + + lupnp->pending_bindings = NULL; + lupnp->adding_configs = NULL; + lupnp->removing_configs = NULL; + lupnp->clean = FALSE; + lupnp->cleaning = FALSE; + lupnp->emit = FALSE; lupnp->state = LinphoneUpnpStateIdle; - lupnp->old_state = LinphoneUpnpStateIdle; ms_message("uPnP IGD: Init"); linphone_core_get_sip_transports(lc, &transport); @@ -238,22 +258,62 @@ int upnp_context_init(LinphoneCore *lc) { void upnp_context_uninit(LinphoneCore *lc) { UpnpContext *lupnp = &lc->upnp; - // Not need, all hooks are removed before - //linphone_core_remove_iterate_hook(lc, linphone_core_upnp_hook, lc); + /* + * Not need, all hooks are removed before + * linphone_core_remove_iterate_hook(lc, linphone_core_upnp_hook, lc); + */ + /* Send port binding removes */ if(lupnp->sip_udp != NULL) { - upnp_port_binding_release(lupnp->sip_udp); + upnp_context_send_remove_port_binding(lc, lupnp->sip_udp); + lupnp->sip_udp = NULL; } if(lupnp->sip_tcp != NULL) { - upnp_port_binding_release(lupnp->sip_tcp); + upnp_context_send_remove_port_binding(lc, lupnp->sip_tcp); + lupnp->sip_tcp = NULL; } if(lupnp->sip_tls != NULL) { - upnp_port_binding_release(lupnp->sip_tls); + upnp_context_send_remove_port_binding(lc, lupnp->sip_tls); + lupnp->sip_tcp = NULL; } + + /* Wait all pending bindings are done */ + ms_message("uPnP IGD: Wait all pending port bindings ..."); + ms_mutex_lock(&lupnp->mutex); + ms_cond_wait(&lupnp->cond, &lupnp->mutex); + ms_mutex_unlock(&lupnp->mutex); + if(lupnp->upnp_igd_ctxt != NULL) { upnp_igd_destroy(lupnp->upnp_igd_ctxt); } + + /* Run one time the hook for configuration update */ + linphone_core_upnp_hook(lc); + + /* Release port bindings */ + if(lupnp->sip_udp != NULL) { + upnp_port_binding_release(lupnp->sip_udp); + lupnp->sip_udp = NULL; + } + if(lupnp->sip_tcp != NULL) { + upnp_port_binding_release(lupnp->sip_tcp); + lupnp->sip_tcp = NULL; + } + if(lupnp->sip_tls != NULL) { + upnp_port_binding_release(lupnp->sip_tls); + lupnp->sip_tcp = NULL; + } + + /* Release lists */ + ms_list_for_each(lupnp->adding_configs,(void (*)(void*))upnp_port_binding_release); + lupnp->adding_configs = ms_list_free(lupnp->adding_configs); + ms_list_for_each(lupnp->removing_configs,(void (*)(void*))upnp_port_binding_release); + lupnp->removing_configs = ms_list_free(lupnp->removing_configs); + ms_list_for_each(lupnp->pending_bindings,(void (*)(void*))upnp_port_binding_release); + lupnp->pending_bindings = ms_list_free(lupnp->pending_bindings); + ms_mutex_destroy(&lupnp->mutex); + ms_cond_destroy(&lupnp->cond); ms_message("uPnP IGD: Uninit"); } @@ -270,10 +330,12 @@ int upnp_context_send_add_port_binding(LinphoneCore *lc, UpnpPortBinding *port) return -2; } - if(port->retry >= UPNP_MAX_RETRY) { + if(port->retry >= UPNP_ADD_MAX_RETRY) { ret = -1; } else { mapping.cookie = upnp_port_binding_retain(port); + lupnp->pending_bindings = ms_list_append(lupnp->pending_bindings, mapping.cookie); + mapping.local_port = port->local_port; mapping.local_host = port->local_addr; if(port->external_port == -1) @@ -305,10 +367,12 @@ int upnp_context_send_remove_port_binding(LinphoneCore *lc, UpnpPortBinding *por return -2; } - if(port->retry >= UPNP_MAX_RETRY) { + if(port->retry >= UPNP_REMOVE_MAX_RETRY) { ret = -1; } else { mapping.cookie = upnp_port_binding_retain(port); + lupnp->pending_bindings = ms_list_append(lupnp->pending_bindings, mapping.cookie); + mapping.remote_port = port->external_port; mapping.remote_host = ""; mapping.protocol = port->protocol; @@ -344,11 +408,15 @@ int linphone_core_update_upnp_audio_video(LinphoneCall *call, bool_t audio, bool 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; - call->upnp_session->audio->rtp->external_port = call->audio_port; + if(call->upnp_session->audio->rtp->external_port == -1) { + call->upnp_session->audio->rtp->external_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; - call->upnp_session->audio->rtcp->external_port = call->audio_port+1; + if(call->upnp_session->audio->rtcp->external_port == -1) { + call->upnp_session->audio->rtcp->external_port = call->audio_port+1; + } if(call->upnp_session->audio->rtp->state == LinphoneUpnpStateIdle && audio) { // Add audio port binding upnp_context_send_add_port_binding(lc, call->upnp_session->audio->rtp); @@ -370,11 +438,15 @@ int linphone_core_update_upnp_audio_video(LinphoneCall *call, bool_t audio, bool 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; - call->upnp_session->video->rtp->external_port = call->video_port; + if(call->upnp_session->video->rtp->external_port == -1) { + call->upnp_session->video->rtp->external_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; - call->upnp_session->video->rtcp->external_port = call->video_port+1; + if(call->upnp_session->video->rtcp->external_port == -1) { + call->upnp_session->video->rtcp->external_port = call->video_port+1; + } if(call->upnp_session->video->rtp->state == LinphoneUpnpStateIdle && video) { // Add video port binding upnp_context_send_add_port_binding(lc, call->upnp_session->video->rtp); @@ -392,6 +464,12 @@ int linphone_core_update_upnp_audio_video(LinphoneCall *call, bool_t audio, bool } ms_mutex_unlock(&lupnp->mutex); + + /* + * Update uPnP call state + */ + upnp_call_process(call); + return ret; } @@ -516,64 +594,85 @@ int upnp_call_process(LinphoneCall *call) { bool_t linphone_core_upnp_hook(void *data) { char key[64]; - MSList *port_bindings = NULL; - MSList *port_bindings_item; + MSList *list = NULL; + MSList *item; UpnpPortBinding *port_mapping; LinphoneCore *lc = (LinphoneCore *)data; + LinphoneCall *call; UpnpContext *lupnp = &lc->upnp; ms_mutex_lock(&lupnp->mutex); - if(lupnp->state == LinphoneUpnpStateOk && lupnp->old_state != LinphoneUpnpStateOk) { + if(lupnp->clean && !lupnp->cleaning) { + lupnp->clean = FALSE; // 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; - //TODO: Don't send id it's udp/tcp/tls port binding + list = upnp_config_list_port_bindings(lc->config); + if(list == NULL) { + lupnp->emit = TRUE; + } else { + lupnp->cleaning = TRUE; + for(item = list;item != NULL; item = item->next) { + port_mapping = (UpnpPortBinding *)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); + ms_list_for_each(list,(void (*)(void*))upnp_port_binding_release); + list = ms_list_free(list); } } - if(lupnp->state == LinphoneUpnpStateOk && lupnp->old_state != LinphoneUpnpStateOk) { - // Add port bindings + if(lupnp->emit) { + lupnp->emit = FALSE; + + /* Force port bindings */ if(lupnp->sip_udp != NULL) { + lupnp->sip_udp->state = LinphoneUpnpStateIdle; upnp_context_send_add_port_binding(lc, lupnp->sip_udp); } if(lupnp->sip_tcp != NULL) { + lupnp->sip_udp->state = LinphoneUpnpStateIdle; upnp_context_send_add_port_binding(lc, lupnp->sip_tcp); } if(lupnp->sip_tls != NULL) { + lupnp->sip_udp->state = LinphoneUpnpStateIdle; 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, "uPnP"); - upnp_port_binding_log(ORTP_DEBUG, "Configuration: Added port binding", port_mapping); - } - 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); - upnp_port_binding_log(ORTP_DEBUG, "Configuration: Removed port binding", port_mapping); + list = lc->calls; + while(list != NULL) { + call = (LinphoneCall *)list->data; + call->upnp_session->audio->rtp->state = LinphoneUpnpStateIdle; + call->upnp_session->audio->rtcp->state = LinphoneUpnpStateIdle; + call->upnp_session->video->rtp->state = LinphoneUpnpStateIdle; + call->upnp_session->video->rtcp->state = LinphoneUpnpStateIdle; + linphone_core_update_upnp_audio_video(call, call->audiostream!=NULL, call->videostream!=NULL); + list = list->next; } } - 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; + /* Add configs */ + for(item = lupnp->adding_configs;item!=NULL;item=item->next) { + port_mapping = (UpnpPortBinding *)item->data; + 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, "uPnP"); + upnp_port_binding_log(ORTP_DEBUG, "Configuration: Added port binding", port_mapping); + } + ms_list_for_each(lupnp->adding_configs,(void (*)(void*))upnp_port_binding_release); + lupnp->adding_configs = ms_list_free(lupnp->adding_configs); + + /* Remove configs */ + for(item = lupnp->removing_configs;item!=NULL;item=item->next) { + port_mapping = (UpnpPortBinding *)item->data; + 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); + upnp_port_binding_log(ORTP_DEBUG, "Configuration: Removed port binding", port_mapping); + } + ms_list_for_each(lupnp->removing_configs,(void (*)(void*))upnp_port_binding_release); + lupnp->removing_configs = ms_list_free(lupnp->removing_configs); + ms_mutex_unlock(&lupnp->mutex); return TRUE; } @@ -687,7 +786,9 @@ UpnpStream* upnp_stream_new() { void upnp_stream_destroy(UpnpStream* stream) { upnp_port_binding_release(stream->rtp); + stream->rtp = NULL; upnp_port_binding_release(stream->rtcp); + stream->rtcp = NULL; ms_free(stream); } @@ -779,66 +880,60 @@ MSList *upnp_config_list_port_bindings(struct _LpConfig *lpc) { return retList; } -int upnp_config_add_port_binding(LinphoneCore *lc, const UpnpPortBinding *port) { +void upnp_config_add_port_binding(LinphoneCore *lc, const UpnpPortBinding *port) { UpnpContext *lupnp = &lc->upnp; - MSList *list = lupnp->pending_configs; + MSList *list; UpnpPortBinding *list_port; - bool_t remove = FALSE; - bool_t add = TRUE; + + list = lupnp->removing_configs; while(list != NULL) { 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; - } + lupnp->removing_configs = ms_list_remove(lupnp->removing_configs, list_port); + upnp_port_binding_release(list_port); + return; } 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 = FALSE; - bool_t add = TRUE; + list = lupnp->adding_configs; while(list != NULL) { 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; - } + if(upnp_port_binding_equal(list_port, port) == TRUE) { + return; } 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); + list_port = upnp_port_binding_copy(port); + lupnp->adding_configs = ms_list_append(lupnp->adding_configs, list_port); +} + +void upnp_config_remove_port_binding(LinphoneCore *lc, const UpnpPortBinding *port) { + UpnpContext *lupnp = &lc->upnp; + MSList *list; + UpnpPortBinding *list_port; + + list = lupnp->adding_configs; + while(list != NULL) { + list_port = (UpnpPortBinding *)list->data; + if(upnp_port_binding_equal(list_port, port) == TRUE) { + lupnp->adding_configs = ms_list_remove(lupnp->adding_configs, list_port); + upnp_port_binding_release(list_port); + return; + } + list = ms_list_next(list); } - return 0; + list = lupnp->removing_configs; + while(list != NULL) { + list_port = (UpnpPortBinding *)list->data; + if(upnp_port_binding_equal(list_port, port) == TRUE) { + return; + } + list = ms_list_next(list); + } + + list_port = upnp_port_binding_copy(port); + lupnp->removing_configs = ms_list_append(lupnp->removing_configs, list_port); } diff --git a/coreapi/upnp.h b/coreapi/upnp.h index b515d0410..04281c737 100644 --- a/coreapi/upnp.h +++ b/coreapi/upnp.h @@ -65,10 +65,17 @@ typedef struct _UpnpContext { UpnpPortBinding *sip_tls; UpnpPortBinding *sip_udp; UpnpState state; - UpnpState old_state; - MSList *pending_configs; + MSList *removing_configs; + MSList *adding_configs; + MSList *pending_bindings; + + bool_t clean; // True if at the next loop clean the port bindings + bool_t cleaning; // True if the cleaning processing; + bool_t emit; // True if at the next loop emit the port bindings ms_mutex_t mutex; + ms_cond_t cond; + } UpnpContext; void linphone_core_update_local_media_description_from_upnp(SalMediaDescription *desc, UpnpSession *session);