mirror of
https://gitlab.linphone.org/BC/public/linphone-iphone.git
synced 2026-01-29 00:59:20 +00:00
Working sip upnp
This commit is contained in:
parent
806203ca0a
commit
9567e2bf62
8 changed files with 679 additions and 187 deletions
|
|
@ -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))
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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";
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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<EFBFBD>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<EFBFBD>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<EFBFBD>o Tom<6F> and Pr<50>ncipe" ,"ST" , "239" , 7 , "00" },
|
||||
{"Saudi Arabia" ,"SA" , "966" , 9 , "00" },
|
||||
{"Senegal" ,"SN" , "221" , 9 , "00" },
|
||||
{"Serbia" ,"RS" , "381" , 9 , "00" },
|
||||
|
|
|
|||
750
coreapi/upnp.c
750
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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -1 +1 @@
|
|||
Subproject commit 39998cb245606b904a77e093db168057f87bf8b0
|
||||
Subproject commit 34de96d6b33f58b248f7d46e9c25edb11e6a5426
|
||||
Loading…
Add table
Reference in a new issue