diff --git a/Makefile.am b/Makefile.am index 2fd425a0d..4f3f46860 100644 --- a/Makefile.am +++ b/Makefile.am @@ -7,6 +7,8 @@ SUBDIRS = build m4 pixmaps po @ORTP_DIR@ @MS2_DIR@ \ coreapi console gtk share scripts tools tester include +GITVERSION=`cd $(top_srcdir) && git describe --always || echo $(VERSION)` + ACLOCAL_FLAGS=-I$(top_srcdir)/m4 @@ -20,11 +22,11 @@ OPTIONAL_SOUNDS=\ INSTALLDIR=$(abs_top_builddir)/linphone-install INSTALLDIR_WITH_PREFIX=$(INSTALLDIR)/$(prefix) -ZIPFILE=$(abs_top_builddir)/$(PACKAGE)-win32-$(VERSION).zip +ZIPFILE=$(abs_top_builddir)/$(PACKAGE)-win32-$(GITVERSION).zip ZIP_EXCLUDED=include lib \ $(OPTIONAL_SOUNDS) -SDK_ZIPFILE=$(abs_top_builddir)/lib$(PACKAGE)-win32-$(VERSION).zip +SDK_ZIPFILE=$(abs_top_builddir)/lib$(PACKAGE)-win32-sdk-$(GITVERSION).zip SDK_EXCLUDED= \ bin/linphone.exe \ lib/*.la \ @@ -173,7 +175,7 @@ setup.exe: filelist cp $(ISS_SCRIPT) $(INSTALLDIR_WITH_PREFIX)/. cd $(INSTALLDIR_WITH_PREFIX) && \ $(ISCC) $(ISS_SCRIPT) - mv $(INSTALLDIR_WITH_PREFIX)/Output/setup.exe $(PACKAGE)-$(VERSION)-setup.exe + mv $(INSTALLDIR_WITH_PREFIX)/Output/setup.exe $(PACKAGE)-setup-$(GITVERSION).exe rm -rf $(INSTALLDIR_WITH_PREFIX)/Output rm -f $(INSTALLDIR_WITH_PREFIX)/$(PACKAGE_WIN32_FILELIST) rm -f $(INSTALLDIR_WITH_PREFIX)/$(ISS_SCRIPT) @@ -201,8 +203,8 @@ Portfile-devel: $(top_srcdir)/scripts/Portfile-devel.tmpl dist ### MAC MACAPPNAME=Linphone.app -MACAPPZIP=$(PACKAGE)-$(VERSION).app.zip -MACAPPDMG=$(PACKAGE)-$(VERSION).dmg +MACAPPZIP=$(PACKAGE)-$(GITVERSION).app.zip +MACAPPDMG=$(PACKAGE)-$(GITVERSION).dmg BUNDLEPREFIX=./ BUNDLEDIR=$(BUNDLEPREFIX)$(MACAPPNAME) #a path prefix where additional libs can be cherry-picked by the bundler. diff --git a/README.macos.md b/README.macos.md index 381efcf2f..4028bb40a 100644 --- a/README.macos.md +++ b/README.macos.md @@ -53,7 +53,7 @@ Install `GTK`. It is recommended to use the `quartz` backend for better integrat brew install cairo --without-x11 brew install gtk+ --without-x11 - brew install gettext gtk-mac-integration libsoup + brew install gettext gtk-mac-integration libsoup hicolor-icon-theme #readline is required from linphonec.c otherwise compilation will fail brew link readline --force @@ -148,6 +148,15 @@ If you don't need plugins, remove or comment out this line from the bundler file ${prefix:ms2plugins}/lib/mediastreamer/plugins/*.*.so +If using HomeBrew, this is not working yet. However you will at least need to: + + brew install shared-mime-info glib-networking hicolor-icon-theme + update-mime-database /usr/local/share/mime + + And modify also: + + /usr/local + Then run, inside Linphone source tree configure as told before but with `--enable-relativeprefix` appended. make && make bundle diff --git a/autogen.sh b/autogen.sh index 9cedbf164..d5ec9ea80 100755 --- a/autogen.sh +++ b/autogen.sh @@ -33,6 +33,11 @@ fi INTLTOOLIZE=$(which intltoolize) +#workaround for mingw bug in intltoolize script. +if test "$INTLTOOLIZE" = "/bin/intltoolize" ; then + INTLTOOLIZE=/usr/bin/intltoolize +fi + echo "Generating build scripts in linphone..." set -x $LIBTOOLIZE --copy --force diff --git a/build/android/liblinphone_tester.mk b/build/android/liblinphone_tester.mk index ee2018621..d92bb60b5 100644 --- a/build/android/liblinphone_tester.mk +++ b/build/android/liblinphone_tester.mk @@ -18,7 +18,8 @@ common_SRC_FILES := \ transport_tester.c \ player_tester.c \ dtmf_tester.c \ - accountmanager.c + accountmanager.c \ + offeranswer_tester.c common_C_INCLUDES += \ $(LOCAL_PATH) \ diff --git a/build/macos/Info-linphone.plist.in b/build/macos/Info-linphone.plist.in index b9cde78a9..7d20d4ac6 100644 --- a/build/macos/Info-linphone.plist.in +++ b/build/macos/Info-linphone.plist.in @@ -26,6 +26,8 @@ Copyright 2011 Belledonne Communications LSMinimumSystemVersion 10.4 + NSAppSleepDisabled + YES diff --git a/configure.ac b/configure.ac index 40d93a208..6edb22499 100644 --- a/configure.ac +++ b/configure.ac @@ -62,7 +62,10 @@ case $target in *mingw*) CFLAGS="$CFLAGS -DORTP_STATIC -D_WIN32_WINNT=0x0501 " CXXFLAGS="$CXXFLAGS -DORTP_STATIC -D_WIN32_WINNT=0x0501" - LIBS="$LIBS -L/lib -lws2_32" + dnl Workaround for mingw, whose compiler does not check in /usr/include ... + CPPFLAGS="$CPPFLAGS -I/usr/include" + LDFLAGS="$LDFLAGS -L/usr/lib" + LIBS="$LIBS -lws2_32" GUI_FLAGS="-mwindows" CONSOLE_FLAGS="-mconsole" mingw_found=yes @@ -298,7 +301,7 @@ if test "$build_zlib" != "false" ; then AC_MSG_NOTICE([zlib library and headers not found]) else AC_DEFINE( HAVE_ZLIB, 1, [ZLIB support] ) - ZLIBS_LIBS='-z' + ZLIB_LIBS='-lz' AC_SUBST(ZLIB_LIBS) fi else diff --git a/coreapi/address.c b/coreapi/address.c index 764a8fb24..d9d772337 100644 --- a/coreapi/address.c +++ b/coreapi/address.c @@ -159,6 +159,13 @@ bool_t linphone_address_is_secure(const LinphoneAddress *uri){ return sal_address_is_secure(uri); } +/** + * returns true if address is a routable sip address + */ +bool_t linphone_address_is_sip(const LinphoneAddress *uri){ + return sal_address_is_sip(uri); +} + static bool_t strings_equals(const char *s1, const char *s2){ if (s1==NULL && s2==NULL) return TRUE; if (s1!=NULL && s2!=NULL && strcmp(s1,s2)==0) return TRUE; diff --git a/coreapi/bellesip_sal/sal_address_impl.c b/coreapi/bellesip_sal/sal_address_impl.c index c4ff234d7..034135f72 100644 --- a/coreapi/bellesip_sal/sal_address_impl.c +++ b/coreapi/bellesip_sal/sal_address_impl.c @@ -151,6 +151,11 @@ char *sal_address_as_string(const SalAddress *addr){ return ms_strdup(tmp); } +bool_t sal_address_is_sip(const SalAddress *addr){ + belle_sip_header_address_t* header_addr = BELLE_SIP_HEADER_ADDRESS(addr); + return belle_sip_header_address_get_uri(header_addr) != NULL; +} + char *sal_address_as_string_uri_only(const SalAddress *addr){ belle_sip_header_address_t* header_addr = BELLE_SIP_HEADER_ADDRESS(addr); belle_sip_uri_t* sip_uri = belle_sip_header_address_get_uri(header_addr); @@ -210,7 +215,7 @@ void sal_address_unref(SalAddress *addr){ belle_sip_object_unref(BELLE_SIP_HEADER_ADDRESS(addr)); } -bool_t sal_address_is_ipv6(SalAddress *addr){ +bool_t sal_address_is_ipv6(const SalAddress *addr){ belle_sip_header_address_t* header_addr = BELLE_SIP_HEADER_ADDRESS(addr); belle_sip_uri_t* uri = belle_sip_header_address_get_uri(header_addr); if (uri){ diff --git a/coreapi/bellesip_sal/sal_op_call.c b/coreapi/bellesip_sal/sal_op_call.c index 921184cf6..4aca82a81 100644 --- a/coreapi/bellesip_sal/sal_op_call.c +++ b/coreapi/bellesip_sal/sal_op_call.c @@ -97,6 +97,7 @@ static int set_sdp(belle_sip_message_t *msg,belle_sdp_session_description_t* ses error = belle_sip_object_marshal(BELLE_SIP_OBJECT(session_desc),buff,bufLen,&length); if( error != BELLE_SIP_OK ){ bufLen *= 2; + length = 0; buff = belle_sip_realloc(buff,bufLen); } } @@ -687,6 +688,11 @@ int sal_call(SalOp *op, const char *from, const char *to){ ms_message("[%s] calling [%s] on op [%p]", from, to, op); invite=sal_op_build_request(op,"INVITE"); + if( invite == NULL ){ + /* can happen if the op has an invalid address */ + return -1; + } + sal_op_fill_invite(op,invite); sal_op_call_fill_cbs(op); diff --git a/coreapi/bellesip_sal/sal_op_events.c b/coreapi/bellesip_sal/sal_op_events.c index 4409c73a3..d4a24bd9b 100644 --- a/coreapi/bellesip_sal/sal_op_events.c +++ b/coreapi/bellesip_sal/sal_op_events.c @@ -198,6 +198,9 @@ int sal_subscribe(SalOp *op, const char *from, const char *to, const char *event sal_op_subscribe_fill_cbs(op); /*???sal_exosip_fix_route(op); make sure to ha ;lr*/ req=sal_op_build_request(op,"SUBSCRIBE"); + if( req == NULL ) { + return -1; + } if (eventname){ if (op->event) belle_sip_object_unref(op->event); op->event=belle_sip_header_create("Event",eventname); diff --git a/coreapi/bellesip_sal/sal_op_impl.c b/coreapi/bellesip_sal/sal_op_impl.c index 2e68146a0..5c953ffc5 100644 --- a/coreapi/bellesip_sal/sal_op_impl.c +++ b/coreapi/bellesip_sal/sal_op_impl.c @@ -150,20 +150,37 @@ belle_sip_request_t* sal_op_build_request(SalOp *op,const char* method) { belle_sip_provider_t* prov=op->base.root->prov; belle_sip_request_t *req; belle_sip_uri_t* req_uri; + belle_sip_uri_t* to_uri; + + const SalAddress* to_address; const MSList *elem=sal_op_get_route_addresses(op); char token[10]; + /* check that the op has a correct to address */ + to_address = sal_op_get_to_address(op); + if( to_address == NULL ){ + ms_error("No To: address, cannot build request"); + return NULL; + } + + to_uri = belle_sip_header_address_get_uri(BELLE_SIP_HEADER_ADDRESS(to_address)); + if( to_uri == NULL ){ + ms_error("To: address is invalid, cannot build request"); + return NULL; + } + if (strcmp("REGISTER",method)==0 || op->privacy==SalPrivacyNone) { from_header = belle_sip_header_from_create(BELLE_SIP_HEADER_ADDRESS(sal_op_get_from_address(op)) - ,belle_sip_random_token(token,sizeof(token))); + ,belle_sip_random_token(token,sizeof(token))); } else { from_header=belle_sip_header_from_create2("Anonymous ",belle_sip_random_token(token,sizeof(token))); } /*make sure to preserve components like headers or port*/ - req_uri = (belle_sip_uri_t*)belle_sip_object_clone((belle_sip_object_t*)belle_sip_header_address_get_uri(BELLE_SIP_HEADER_ADDRESS(sal_op_get_to_address(op)))); + + req_uri = (belle_sip_uri_t*)belle_sip_object_clone((belle_sip_object_t*)to_uri); belle_sip_uri_set_secure(req_uri,sal_op_is_secure(op)); - to_header = belle_sip_header_to_create(BELLE_SIP_HEADER_ADDRESS(sal_op_get_to_address(op)),NULL); + to_header = belle_sip_header_to_create(BELLE_SIP_HEADER_ADDRESS(to_address),NULL); req=belle_sip_request_create( req_uri, @@ -218,7 +235,10 @@ int sal_ping(SalOp *op, const char *from, const char *to){ void sal_op_set_remote_ua(SalOp*op,belle_sip_message_t* message) { belle_sip_header_user_agent_t* user_agent=belle_sip_message_get_header_by_type(message,belle_sip_header_user_agent_t); char user_agent_string[256]; - if(user_agent && belle_sip_header_user_agent_get_products_as_string(user_agent,user_agent_string,sizeof(user_agent_string))>0) { + if (user_agent && belle_sip_header_user_agent_get_products_as_string(user_agent,user_agent_string,sizeof(user_agent_string))>0) { + if (op->base.remote_ua!=NULL){ + ms_free(op->base.remote_ua); + } op->base.remote_ua=ms_strdup(user_agent_string); } } diff --git a/coreapi/bellesip_sal/sal_op_message.c b/coreapi/bellesip_sal/sal_op_message.c index 3324cefb7..017845833 100644 --- a/coreapi/bellesip_sal/sal_op_message.c +++ b/coreapi/bellesip_sal/sal_op_message.c @@ -172,6 +172,9 @@ int sal_message_send(SalOp *op, const char *from, const char *to, const char* co op->dir=SalOpDirOutgoing; req=sal_op_build_request(op,"MESSAGE"); + if (req == NULL ){ + return -1; + } if (sal_op_get_contact_address(op)){ belle_sip_message_add_header(BELLE_SIP_MESSAGE(req),BELLE_SIP_HEADER(sal_op_create_contact(op))); } diff --git a/coreapi/bellesip_sal/sal_op_presence.c b/coreapi/bellesip_sal/sal_op_presence.c index 23db49e6f..6de0a0fbf 100644 --- a/coreapi/bellesip_sal/sal_op_presence.c +++ b/coreapi/bellesip_sal/sal_op_presence.c @@ -306,8 +306,10 @@ int sal_subscribe_presence(SalOp *op, const char *from, const char *to, int expi belle_sip_parameters_remove_parameter(BELLE_SIP_PARAMETERS(op->base.from_address),"tag"); belle_sip_parameters_remove_parameter(BELLE_SIP_PARAMETERS(op->base.to_address),"tag"); req=sal_op_build_request(op,"SUBSCRIBE"); - belle_sip_message_add_header(BELLE_SIP_MESSAGE(req),op->event); - belle_sip_message_add_header(BELLE_SIP_MESSAGE(req),BELLE_SIP_HEADER(belle_sip_header_expires_create(expires))); + if( req ){ + belle_sip_message_add_header(BELLE_SIP_MESSAGE(req),op->event); + belle_sip_message_add_header(BELLE_SIP_MESSAGE(req),BELLE_SIP_HEADER(belle_sip_header_expires_create(expires))); + } return sal_op_send_request(op,req); } diff --git a/coreapi/bellesip_sal/sal_op_publish.c b/coreapi/bellesip_sal/sal_op_publish.c index 2655c58d2..17ef8e8db 100644 --- a/coreapi/bellesip_sal/sal_op_publish.c +++ b/coreapi/bellesip_sal/sal_op_publish.c @@ -76,6 +76,11 @@ int sal_publish_presence(SalOp *op, const char *from, const char *to, int expire op->type=SalOpPublish; req=sal_op_build_request(op,"PUBLISH"); + + if( req == NULL ){ + return -1; + } + if (sal_op_get_contact_address(op)){ belle_sip_message_add_header(BELLE_SIP_MESSAGE(req),BELLE_SIP_HEADER(sal_op_create_contact(op))); } @@ -101,6 +106,10 @@ int sal_publish(SalOp *op, const char *from, const char *to, const char *eventna sal_op_publish_fill_cbs(op); req=sal_op_build_request(op,"PUBLISH"); + if( req == NULL ){ + return -1; + } + if (sal_op_get_contact_address(op)){ belle_sip_message_add_header(BELLE_SIP_MESSAGE(req),BELLE_SIP_HEADER(sal_op_create_contact(op))); } diff --git a/coreapi/callbacks.c b/coreapi/callbacks.c index fe65a4282..ad425b21c 100644 --- a/coreapi/callbacks.c +++ b/coreapi/callbacks.c @@ -96,7 +96,23 @@ static void prepare_early_media_forking(LinphoneCall *call){ if (call->videostream){ rtp_session_set_symmetric_rtp(call->videostream->ms.sessions.rtp_session,FALSE); } +} +void linphone_call_update_frozen_payloads(LinphoneCall *call, SalMediaDescription *result_desc){ + SalMediaDescription *local=call->localdesc; + int i; + for(i=0;inb_streams;++i){ + MSList *elem; + for (elem=result_desc->streams[i].payloads;elem!=NULL;elem=elem->next){ + PayloadType *pt=(PayloadType*)elem->data; + if (is_payload_type_number_available(local->streams[i].already_assigned_payloads, payload_type_get_number(pt), NULL)){ + /*new codec, needs to be added to the list*/ + local->streams[i].already_assigned_payloads=ms_list_append(local->streams[i].already_assigned_payloads, payload_type_clone(pt)); + ms_message("LinphoneCall[%p] : payload type %i %s/%i fmtp=%s added to frozen list.", + call, payload_type_get_number(pt), pt->mime_type, pt->clock_rate, pt->recv_fmtp ? pt->recv_fmtp : NULL); + } + } + } } void linphone_core_update_streams(LinphoneCore *lc, LinphoneCall *call, SalMediaDescription *new_md){ @@ -181,6 +197,7 @@ void linphone_core_update_streams(LinphoneCore *lc, LinphoneCall *call, SalMedia if (call->state==LinphoneCallPausing && call->paused_by_app && ms_list_size(lc->calls)==1){ linphone_core_play_named_tone(lc,LinphoneToneCallOnHold); } + linphone_call_update_frozen_payloads(call, new_md); end: if (oldmd) sal_media_description_unref(oldmd); @@ -597,7 +614,7 @@ static void call_updated_by_remote(LinphoneCore *lc, LinphoneCall *call, bool_t } } - if (call->state==LinphoneCallStreamsRunning) { + if ( call->state == LinphoneCallStreamsRunning) { /*reINVITE and in-dialogs UPDATE go here*/ linphone_core_notify_display_status(lc,_("Call is updated by remote.")); call->defer_update=FALSE; @@ -605,8 +622,21 @@ static void call_updated_by_remote(LinphoneCore *lc, LinphoneCall *call, bool_t if (call->defer_update==FALSE){ linphone_core_accept_call_update(lc,call,NULL); } - if (rmd==NULL) + if (rmd==NULL){ call->expect_media_in_ack=TRUE; + } + + } else if( call->state == LinphoneCallPausedByRemote ){ + /* Case where no SDP is present and we were paused by remote. + * We send back an ACK with our SDP and expect the remote to send its own. + * No state change here until an answer is received. */ + call->defer_update=FALSE; + if (call->defer_update==FALSE){ + _linphone_core_accept_call_update(lc,call,NULL,call->state,linphone_call_state_to_string(call->state)); + } + if (rmd==NULL){ + call->expect_media_in_ack=TRUE; + } } else if (is_update){ /*SIP UPDATE case, can occur in early states*/ linphone_call_set_state(call, LinphoneCallEarlyUpdatedByRemote, "EarlyUpdatedByRemote"); _linphone_core_accept_call_update(lc,call,NULL,call->prevstate,linphone_call_state_to_string(call->prevstate)); diff --git a/coreapi/linphonecall.c b/coreapi/linphonecall.c index 8923b804a..d3766c174 100644 --- a/coreapi/linphonecall.c +++ b/coreapi/linphonecall.c @@ -234,35 +234,154 @@ void linphone_call_set_authentication_token_verified(LinphoneCall *call, bool_t propagate_encryption_changed(call); } -static MSList *make_codec_list(LinphoneCore *lc, const MSList *codecs, int bandwidth_limit,int* max_sample_rate, int nb_codecs_limit){ - MSList *l=NULL; +static int get_max_codec_sample_rate(const MSList *codecs){ + int max_sample_rate=0; const MSList *it; - int nb = 0; - if (max_sample_rate) *max_sample_rate=0; for(it=codecs;it!=NULL;it=it->next){ PayloadType *pt=(PayloadType*)it->data; - if (pt->flags & PAYLOAD_TYPE_ENABLED){ - int sample_rate = payload_type_get_rate(pt); + int sample_rate; + + if( strcasecmp("G722",pt->mime_type) == 0 ){ + /* G722 spec says 8000 but the codec actually requires 16000 */ + sample_rate = 16000; + }else sample_rate=pt->clock_rate; + if (sample_rate>max_sample_rate) max_sample_rate=sample_rate; + } + return max_sample_rate; +} - if( strcasecmp("G722",pt->mime_type) == 0 ){ - /* G722 spec says 8000 but the codec actually requires 16000 */ - ms_debug("Correcting sample rate for G722"); - sample_rate = 16000; - } - - if (bandwidth_limit>0 && !linphone_core_is_payload_type_usable_for_bandwidth(lc,pt,bandwidth_limit)){ - ms_message("Codec %s/%i eliminated because of audio bandwidth constraint of %i kbit/s", - pt->mime_type,pt->clock_rate,bandwidth_limit); - continue; - } - if (linphone_core_check_payload_type_usability(lc,pt)){ - l=ms_list_append(l,payload_type_clone(pt)); - nb++; - if (max_sample_rate && sample_rate>*max_sample_rate) *max_sample_rate=sample_rate; +static int find_payload_type_number(const MSList *assigned, const PayloadType *pt){ + const MSList *elem; + const PayloadType *candidate=NULL; + for(elem=assigned;elem!=NULL;elem=elem->next){ + const PayloadType *it=(const PayloadType*)elem->data; + if ((strcasecmp(pt->mime_type, payload_type_get_mime(it)) == 0) + && (it->clock_rate==pt->clock_rate) + && (it->channels==pt->channels || pt->channels<=0)) { + candidate=it; + if ((it->recv_fmtp!=NULL && pt->recv_fmtp!=NULL && strcasecmp(it->recv_fmtp, pt->recv_fmtp)==0) + || (it->recv_fmtp==NULL && pt->recv_fmtp==NULL)){ + break;/*exact match*/ } } - if ((nb_codecs_limit > 0) && (nb >= nb_codecs_limit)) break; } + return candidate ? payload_type_get_number(candidate) : -1; +} + +bool_t is_payload_type_number_available(const MSList *l, int number, const PayloadType *ignore){ + const MSList *elem; + for (elem=l; elem!=NULL; elem=elem->next){ + const PayloadType *pt=(PayloadType*)elem->data; + if (pt!=ignore && payload_type_get_number(pt)==number) return FALSE; + } + return TRUE; +} + +static void linphone_core_assign_payload_type_numbers(LinphoneCore *lc, MSList *codecs){ + MSList *elem; + int dyn_number=lc->codecs_conf.dyn_pt; + for (elem=codecs; elem!=NULL; elem=elem->next){ + PayloadType *pt=(PayloadType*)elem->data; + int number=payload_type_get_number(pt); + + /*check if number is duplicated: it could be the case if the remote forced us to use a mapping with a previous offer*/ + if (number!=-1 && !(pt->flags & PAYLOAD_TYPE_FROZEN_NUMBER)){ + if (!is_payload_type_number_available(codecs, number, pt)){ + ms_message("Reassigning payload type %i %s/%i because already offered.", number, pt->mime_type, pt->clock_rate); + number=-1; /*need to be re-assigned*/ + } + } + + if (number==-1){ + while(dyn_number<127){ + if (is_payload_type_number_available(codecs, dyn_number, NULL)){ + payload_type_set_number(pt, dyn_number); + dyn_number++; + break; + } + dyn_number++; + } + if (dyn_number==127){ + ms_error("Too many payload types configured ! codec %s/%i is disabled.", pt->mime_type, pt->clock_rate); + payload_type_set_enable(pt, FALSE); + } + } + } +} + +static bool_t has_telephone_event_at_rate(const MSList *tev, int rate){ + const MSList *it; + for(it=tev;it!=NULL;it=it->next){ + const PayloadType *pt=(PayloadType*)it->data; + if (pt->clock_rate==rate) return TRUE; + } + return FALSE; +} + +static MSList * create_telephone_events(LinphoneCore *lc, const MSList *codecs){ + const MSList *it; + MSList *ret=NULL; + for(it=codecs;it!=NULL;it=it->next){ + const PayloadType *pt=(PayloadType*)it->data; + if (!has_telephone_event_at_rate(ret,pt->clock_rate)){ + PayloadType *tev=payload_type_clone(&payload_type_telephone_event); + tev->clock_rate=pt->clock_rate; + /*let it choose the number dynamically as for normal codecs*/ + payload_type_set_number(tev, -1); + if (ret==NULL){ + /*But for first telephone-event, prefer the number that was configured in the core*/ + if (is_payload_type_number_available(codecs, lc->codecs_conf.telephone_event_pt, NULL)){ + payload_type_set_number(tev, lc->codecs_conf.telephone_event_pt); + } + } + ret=ms_list_append(ret,tev); + } + } + return ret; +} + +typedef struct _CodecConstraints{ + int bandwidth_limit; + int max_codecs; + MSList *previously_used; +}CodecConstraints; + +static MSList *make_codec_list(LinphoneCore *lc, CodecConstraints * hints, const MSList *codecs){ + MSList *l=NULL; + MSList *tevs=NULL; + const MSList *it; + int nb = 0; + + for(it=codecs;it!=NULL;it=it->next){ + PayloadType *pt=(PayloadType*)it->data; + int num; + + if (!(pt->flags & PAYLOAD_TYPE_ENABLED)) + continue; + if (hints->bandwidth_limit>0 && !linphone_core_is_payload_type_usable_for_bandwidth(lc,pt,hints->bandwidth_limit)){ + ms_message("Codec %s/%i eliminated because of audio bandwidth constraint of %i kbit/s", + pt->mime_type,pt->clock_rate,hints->bandwidth_limit); + continue; + } + if (!linphone_core_check_payload_type_usability(lc,pt)){ + continue; + } + pt=payload_type_clone(pt); + + /*look for a previously assigned number for this codec*/ + num=find_payload_type_number(hints->previously_used, pt); + if (num!=-1){ + payload_type_set_number(pt,num); + payload_type_set_flag(pt, PAYLOAD_TYPE_FROZEN_NUMBER); + } + + l=ms_list_append(l, pt); + nb++; + if ((hints->max_codecs > 0) && (nb >= hints->max_codecs)) break; + } + tevs=create_telephone_events(lc,l); + l=ms_list_concat(l,tevs); + linphone_core_assign_payload_type_numbers(lc, l); return l; } @@ -401,9 +520,16 @@ void linphone_call_update_local_media_description_from_ice_or_upnp(LinphoneCall #endif //BUILD_UPNP } +static void transfer_already_assigned_payload_types(SalMediaDescription *old, SalMediaDescription *md){ + int i; + for(i=0;inb_streams;++i){ + md->streams[i].already_assigned_payloads=old->streams[i].already_assigned_payloads; + old->streams[i].already_assigned_payloads=NULL; + } +} + void linphone_call_make_local_media_description(LinphoneCore *lc, LinphoneCall *call){ MSList *l; - PayloadType *pt; SalMediaDescription *old_md=call->localdesc; int i; int nb_active_streams = 0; @@ -412,6 +538,7 @@ void linphone_call_make_local_media_description(LinphoneCore *lc, LinphoneCall * LinphoneAddress *addr; char* local_ip=call->localip; const char *subject=linphone_call_params_get_session_name(call->params); + CodecConstraints codec_hints={0}; linphone_core_adapt_to_network(lc,call->ping_time,call->params); @@ -445,9 +572,11 @@ void linphone_call_make_local_media_description(LinphoneCore *lc, LinphoneCall * md->streams[0].ptime=call->params->down_ptime; else md->streams[0].ptime=linphone_core_get_download_ptime(lc); - l=make_codec_list(lc,lc->codecs_conf.audio_codecs,call->params->audio_bw,&md->streams[0].max_rate,-1); - pt=payload_type_clone(rtp_profile_get_payload_from_mime(lc->default_profile,"telephone-event")); - l=ms_list_append(l,pt); + codec_hints.bandwidth_limit=call->params->audio_bw; + codec_hints.max_codecs=-1; + codec_hints.previously_used=old_md ? old_md->streams[0].already_assigned_payloads : NULL; + l=make_codec_list(lc, &codec_hints, lc->codecs_conf.audio_codecs); + md->streams[0].max_rate=get_max_codec_sample_rate(l); md->streams[0].payloads=l; if (call->audiostream && call->audiostream->ms.sessions.rtp_session) { char* me = linphone_address_as_string_uri_only(call->me); @@ -467,7 +596,10 @@ void linphone_call_make_local_media_description(LinphoneCore *lc, LinphoneCall * md->streams[1].rtcp_port=call->media_ports[1].rtcp_port; md->streams[1].proto=md->streams[0].proto; md->streams[1].type=SalVideo; - l=make_codec_list(lc,lc->codecs_conf.video_codecs,0,NULL,-1); + codec_hints.bandwidth_limit=0; + codec_hints.max_codecs=-1; + codec_hints.previously_used=old_md ? old_md->streams[1].already_assigned_payloads : NULL; + l=make_codec_list(lc, &codec_hints, lc->codecs_conf.video_codecs); md->streams[1].payloads=l; if (call->videostream && call->videostream->ms.sessions.rtp_session) { char* me = linphone_address_as_string_uri_only(call->me); @@ -490,7 +622,10 @@ void linphone_call_make_local_media_description(LinphoneCore *lc, LinphoneCall * md->streams[i].proto = call->biggestdesc->streams[i].proto; md->streams[i].type = call->biggestdesc->streams[i].type; md->streams[i].dir = SalStreamInactive; - l = make_codec_list(lc, lc->codecs_conf.video_codecs, 0, NULL, 1); + codec_hints.bandwidth_limit=0; + codec_hints.max_codecs=1; + codec_hints.previously_used=NULL; + l = make_codec_list(lc, &codec_hints, lc->codecs_conf.video_codecs); md->streams[i].payloads = l; } @@ -511,6 +646,7 @@ void linphone_call_make_local_media_description(LinphoneCore *lc, LinphoneCall * linphone_call_update_local_media_description_from_ice_or_upnp(call); linphone_address_destroy(addr); if (old_md){ + transfer_already_assigned_payload_types(old_md,md); call->localdesc_changed=sal_media_description_equals(md,old_md); sal_media_description_unref(old_md); } diff --git a/coreapi/linphonecore.c b/coreapi/linphonecore.c index 678d0ca53..393f405ea 100644 --- a/coreapi/linphonecore.c +++ b/coreapi/linphonecore.c @@ -983,12 +983,13 @@ static void rtp_config_read(LinphoneCore *lc) linphone_core_set_avpf_mode(lc,lp_config_get_int(lc->config,"rtp","avpf",0)); } -static PayloadType * find_payload(RtpProfile *prof, const char *mime_type, int clock_rate, int channels, const char *recv_fmtp){ +static PayloadType * find_payload(const MSList *default_list, const char *mime_type, int clock_rate, int channels, const char *recv_fmtp){ PayloadType *candidate=NULL; - int i; PayloadType *it; - for(i=0;inext){ + it=(PayloadType*)elem->data; if (it!=NULL && strcasecmp(mime_type,it->mime_type)==0 && (clock_rate==it->clock_rate || clock_rate<=0) && (channels==it->channels || channels<=0) ){ @@ -1010,7 +1011,20 @@ static PayloadType * find_payload(RtpProfile *prof, const char *mime_type, int c return candidate; } -static bool_t get_codec(LinphoneCore *lc, const char* type, int index, PayloadType **ret){ +static PayloadType* find_payload_type_from_list(const char* type, int rate, int channels, const MSList* from) { + const MSList *elem; + for(elem=from;elem!=NULL;elem=elem->next){ + PayloadType *pt=(PayloadType*)elem->data; + if ((strcasecmp(type, payload_type_get_mime(pt)) == 0) + && (rate == LINPHONE_FIND_PAYLOAD_IGNORE_RATE || rate==pt->clock_rate) + && (channels == LINPHONE_FIND_PAYLOAD_IGNORE_CHANNELS || channels==pt->channels)) { + return pt; + } + } + return NULL; +} + +static bool_t get_codec(LinphoneCore *lc, SalStreamType type, int index, PayloadType **ret){ char codeckey[50]; const char *mime,*fmtp; int rate,channels,enabled; @@ -1018,7 +1032,7 @@ static bool_t get_codec(LinphoneCore *lc, const char* type, int index, PayloadTy LpConfig *config=lc->config; *ret=NULL; - snprintf(codeckey,50,"%s_%i",type,index); + snprintf(codeckey,50,"%s_codec_%i",type==SalAudio ? "audio" : "video", index); mime=lp_config_get_string(config,codeckey,"mime",NULL); if (mime==NULL || strlen(mime)==0 ) return FALSE; @@ -1026,85 +1040,52 @@ static bool_t get_codec(LinphoneCore *lc, const char* type, int index, PayloadTy fmtp=lp_config_get_string(config,codeckey,"recv_fmtp",NULL); channels=lp_config_get_int(config,codeckey,"channels",0); enabled=lp_config_get_int(config,codeckey,"enabled",1); - pt=find_payload(lc->default_profile,mime,rate,channels,fmtp); - if (pt && enabled ) pt->flags|=PAYLOAD_TYPE_ENABLED; - //ms_message("Found codec %s/%i",pt->mime_type,pt->clock_rate); - if (pt==NULL) ms_warning("Ignoring codec config %s/%i with fmtp=%s because unsupported", - mime,rate,fmtp ? fmtp : ""); + if (!ms_filter_codec_supported(mime)){ + ms_warning("Codec %s/%i read from conf is not supported by mediastreamer2, ignored.",mime,rate); + return TRUE; + } + pt=find_payload(type==SalAudio ? lc->default_audio_codecs : lc->default_video_codecs,mime,rate,channels,fmtp); + if (!pt){ + MSList **default_list=(type==SalAudio) ? &lc->default_audio_codecs : &lc->default_video_codecs; + ms_warning("Codec %s/%i read from conf is not in the default list.",mime,rate); + pt=payload_type_new(); + pt->type=(type==SalAudio) ? PAYLOAD_AUDIO_PACKETIZED : PAYLOAD_VIDEO; + pt->mime_type=ortp_strdup(mime); + pt->clock_rate=rate; + pt->channels=channels; + payload_type_set_recv_fmtp(pt,fmtp); + *default_list=ms_list_append(*default_list, pt); + } + if (enabled ) pt->flags|=PAYLOAD_TYPE_ENABLED; *ret=pt; return TRUE; } -#define RANK_END 10000 - -typedef struct codec_desc{ - const char *name; - int rate; -}codec_desc_t; - -static codec_desc_t codec_pref_order[]={ - {"opus", 48000}, - {"SILK", 16000}, - {"speex", 16000}, - {"speex", 8000}, - {"pcmu",8000}, - {"pcma",8000}, - {"VP8",90000}, - {"H264",90000}, - {"MP4V-ES",90000}, - {NULL,0} -}; - -static int find_codec_rank(const char *mime, int clock_rate){ - int i; - -#ifdef __arm__ - /*hack for opus, that needs to be disabed by default on ARM single processor, otherwise there is no cpu left for video processing*/ - if (strcasecmp(mime,"opus")==0){ - if (ms_get_cpu_count()==1) return RANK_END; - } -#endif - for(i=0;codec_pref_order[i].name!=NULL;++i){ - if (strcasecmp(codec_pref_order[i].name,mime)==0 && clock_rate==codec_pref_order[i].rate) - return i; - } - return RANK_END; -} - -static int codec_compare(const PayloadType *a, const PayloadType *b){ - int ra,rb; - ra=find_codec_rank(a->mime_type,a->clock_rate); - rb=find_codec_rank(b->mime_type,b->clock_rate); - if (ra>rb) return 1; - if (radefault_profile,i); - if (pt){ - if (mtype==SalVideo && pt->type!=PAYLOAD_VIDEO) - pt=NULL; - else if (mtype==SalAudio && (pt->type!=PAYLOAD_AUDIO_PACKETIZED - && pt->type!=PAYLOAD_AUDIO_CONTINUOUS)){ - pt=NULL; - } - if (pt && ms_filter_codec_supported(pt->mime_type)){ - if (ms_list_find(l,pt)==NULL){ - /*unranked codecs are disabled by default*/ - if (find_codec_rank(pt->mime_type, pt->clock_rate)!=RANK_END){ - payload_type_set_flag(pt,PAYLOAD_TYPE_ENABLED); - } - ms_message("Adding new codec %s/%i with fmtp %s", - pt->mime_type,pt->clock_rate,pt->recv_fmtp ? pt->recv_fmtp : ""); - l=ms_list_insert_sorted(l,pt,(int (*)(const void *, const void *))codec_compare); - } +/*this function merges the payload types from the codec default list with the list read from configuration file. + * If a new codec becomes supported in Liblinphone or if the list from configuration file is empty or incomplete, all the supported codecs are added + * automatically. This 'l' list is entirely destroyed and rewritten.*/ +static MSList *add_missing_codecs(const MSList *default_list, MSList *l){ + const MSList *elem; + MSList *newlist; + + for(elem=default_list; elem!=NULL; elem=elem->next){ + MSList *elem2=ms_list_find(l,elem->data); + if (!elem2){ + PayloadType *pt=(PayloadType*)elem->data; + /*this codec from default list should be inserted in the list*/ + if (!elem->prev){ + l=ms_list_prepend(l,pt); + }else{ + const MSList *after=ms_list_find(l,elem->prev->data); + l=ms_list_insert(l, after->next, pt); } + ms_message("Supported codec %s/%i fmtp=%s automatically added to codec list.", pt->mime_type, + pt->clock_rate, pt->recv_fmtp ? pt->recv_fmtp : ""); } } - return l; + newlist=ms_list_copy_with_data(l,(void *(*)(void*))payload_type_clone); + ms_list_free(l); + return newlist; } static MSList *codec_append_if_new(MSList *l, PayloadType *pt){ @@ -1124,23 +1105,23 @@ static void codecs_config_read(LinphoneCore *lc) PayloadType *pt; MSList *audio_codecs=NULL; MSList *video_codecs=NULL; - for (i=0;get_codec(lc,"audio_codec",i,&pt);i++){ + + lc->codecs_conf.dyn_pt=96; + lc->codecs_conf.telephone_event_pt=lp_config_get_int(lc->config,"misc","telephone_event_pt",101); + + for (i=0;get_codec(lc,SalAudio,i,&pt);i++){ if (pt){ - if (!ms_filter_codec_supported(pt->mime_type)){ - ms_warning("Codec %s is not supported by mediastreamer2, removed.",pt->mime_type); - }else audio_codecs=codec_append_if_new(audio_codecs,pt); + audio_codecs=codec_append_if_new(audio_codecs, pt); } } - audio_codecs=add_missing_codecs(lc,SalAudio,audio_codecs); + audio_codecs=add_missing_codecs(lc->default_audio_codecs,audio_codecs); - for (i=0;get_codec(lc,"video_codec",i,&pt);i++){ + for (i=0;get_codec(lc,SalVideo,i,&pt);i++){ if (pt){ - if (!ms_filter_codec_supported(pt->mime_type)){ - ms_warning("Codec %s is not supported by mediastreamer2, removed.",pt->mime_type); - }else video_codecs=codec_append_if_new(video_codecs,(void *)pt); + video_codecs=codec_append_if_new(video_codecs, pt); } } - video_codecs=add_missing_codecs(lc,SalVideo,video_codecs); + video_codecs=add_missing_codecs(lc->default_video_codecs,video_codecs); linphone_core_set_audio_codecs(lc,audio_codecs); linphone_core_set_video_codecs(lc,video_codecs); linphone_core_update_allocated_audio_bandwidth(lc); @@ -1413,60 +1394,45 @@ const char * linphone_core_get_version(void){ return liblinphone_version; } -static void linphone_core_assign_payload_type(LinphoneCore *lc, PayloadType *const_pt, int number, const char *recv_fmtp){ - PayloadType *pt; - - pt=payload_type_clone(const_pt); - if (number==-1){ - /*look for a free number */ - MSList *elem; - int i; - for(i=lc->dyn_pt;ipayload_types;elem!=NULL;elem=elem->next){ - PayloadType *it=(PayloadType*)elem->data; - if (payload_type_get_number(it)==i){ - already_assigned=TRUE; - break; - } - } - if (!already_assigned){ - number=i; - lc->dyn_pt=i+1; - break; - } - } - if (number==-1){ - ms_fatal("FIXME: too many codecs, no more free numbers."); - } +static void linphone_core_register_payload_type(LinphoneCore *lc, const PayloadType *const_pt, const char *recv_fmtp, bool_t enabled){ + MSList **codec_list=const_pt->type==PAYLOAD_VIDEO ? &lc->default_video_codecs : &lc->default_audio_codecs; + if (ms_filter_codec_supported(const_pt->mime_type)){ + PayloadType *pt=payload_type_clone(const_pt); + int number=-1; + payload_type_set_enable(pt,enabled); + if (recv_fmtp!=NULL) payload_type_set_recv_fmtp(pt,recv_fmtp); + /*Set a number to the payload type from the statically defined (RFC3551) profile, if not static, -1 is returned + and the payload type number will be determined dynamically later, at call time.*/ + payload_type_set_number(pt, + (number=rtp_profile_find_payload_number(&av_profile, pt->mime_type, pt->clock_rate, pt->channels)) + ); + ms_message("Codec %s/%i fmtp=[%s] number=%i, enabled=%i) added to default capabilities.", pt->mime_type, pt->clock_rate, + pt->recv_fmtp ? pt->recv_fmtp : "", number, (int)payload_type_enabled(pt)); + *codec_list=ms_list_append(*codec_list,pt); } - ms_message("assigning %s/%i payload type number %i",pt->mime_type,pt->clock_rate,number); - payload_type_set_number(pt,number); - if (recv_fmtp!=NULL) payload_type_set_recv_fmtp(pt,recv_fmtp); - rtp_profile_set_payload(lc->default_profile,number,pt); - lc->payload_types=ms_list_append(lc->payload_types,pt); } -static void linphone_core_handle_static_payloads(LinphoneCore *lc){ +static void linphone_core_register_static_payloads(LinphoneCore *lc){ RtpProfile *prof=&av_profile; int i; for(i=0;idefault_profile,i) == NULL){ - linphone_core_assign_payload_type(lc,pt,i,NULL); +#ifndef VIDEO_ENABLED + if (pt->type==PAYLOAD_VIDEO) continue; +#endif + if (find_payload_type_from_list( + pt->mime_type, pt->clock_rate, pt->type!=PAYLOAD_VIDEO ? pt->channels : LINPHONE_FIND_PAYLOAD_IGNORE_CHANNELS, + pt->type==PAYLOAD_VIDEO ? lc->default_video_codecs : lc->default_audio_codecs)==NULL){ + linphone_core_register_payload_type(lc,pt,NULL,FALSE); } } } } static void linphone_core_free_payload_types(LinphoneCore *lc){ - rtp_profile_clear_all(lc->default_profile); - rtp_profile_destroy(lc->default_profile); - ms_list_for_each(lc->payload_types,(void (*)(void*))payload_type_destroy); - ms_list_free(lc->payload_types); - lc->payload_types=NULL; + ms_list_free_with_data(lc->default_audio_codecs, (void (*)(void*))payload_type_destroy); + ms_list_free_with_data(lc->default_video_codecs, (void (*)(void*))payload_type_destroy); } void linphone_core_set_state(LinphoneCore *lc, LinphoneGlobalState gstate, const char *message){ @@ -1548,10 +1514,73 @@ static void linphone_core_deactivate_log_serialization_if_needed(void) { } } -static void linphone_core_init(LinphoneCore * lc, const LinphoneCoreVTable *vtable, LpConfig *config, void * userdata) -{ - const char *remote_provisioning_uri = NULL; +static void linphone_core_register_default_codecs(LinphoneCore *lc){ const char *aac_fmtp162248, *aac_fmtp3244; + bool_t opus_enabled=TRUE; + /*default enabled audio codecs, in order of preference*/ +#ifdef __arm__ + /*hack for opus, that needs to be disabed by default on ARM single processor, otherwise there is no cpu left for video processing*/ + if (ms_get_cpu_count()==1) opus_enabled=FALSE; +#endif + linphone_core_register_payload_type(lc,&payload_type_opus,"useinbandfec=1; stereo=0; sprop-stereo=0",opus_enabled); + linphone_core_register_payload_type(lc,&payload_type_silk_wb,NULL,TRUE); + linphone_core_register_payload_type(lc,&payload_type_speex_wb,"vbr=on",TRUE); + linphone_core_register_payload_type(lc,&payload_type_speex_nb,"vbr=on",TRUE); + linphone_core_register_payload_type(lc,&payload_type_pcmu8000,NULL,TRUE); + linphone_core_register_payload_type(lc,&payload_type_pcma8000,NULL,TRUE); + + /*other audio codecs, not enabled by default, in order of preference*/ + linphone_core_register_payload_type(lc,&payload_type_gsm,NULL,FALSE); + linphone_core_register_payload_type(lc,&payload_type_g722,NULL,FALSE); + linphone_core_register_payload_type(lc,&payload_type_ilbc,"mode=30",FALSE); + linphone_core_register_payload_type(lc,&payload_type_amr,"octet-align=1",FALSE); + linphone_core_register_payload_type(lc,&payload_type_amrwb,"octet-align=1",FALSE); + linphone_core_register_payload_type(lc,&payload_type_g729,"annexb=no",FALSE); + /* For AAC, we use a config value to determine if we ought to support SBR. Since it is not offically supported + * for the mpeg4-generic mime type, setting this flag to 1 will break compatibility with other clients. */ + if( lp_config_get_int(lc->config, "misc", "aac_use_sbr", FALSE) ) { + ms_message("Using SBR for AAC"); + aac_fmtp162248 = "config=F8EE2000; constantDuration=512; indexDeltaLength=3; indexLength=3; mode=AAC-hbr; profile-level-id=76; sizeLength=13; streamType=5; SBR-enabled=1"; + aac_fmtp3244 = "config=F8E82000; constantDuration=512; indexDeltaLength=3; indexLength=3; mode=AAC-hbr; profile-level-id=76; sizeLength=13; streamType=5; SBR-enabled=1"; + } else { + aac_fmtp162248 = "config=F8EE2000; constantDuration=512; indexDeltaLength=3; indexLength=3; mode=AAC-hbr; profile-level-id=76; sizeLength=13; streamType=5"; + aac_fmtp3244 = "config=F8E82000; constantDuration=512; indexDeltaLength=3; indexLength=3; mode=AAC-hbr; profile-level-id=76; sizeLength=13; streamType=5"; + } + linphone_core_register_payload_type(lc,&payload_type_aaceld_16k,aac_fmtp162248,FALSE); + linphone_core_register_payload_type(lc,&payload_type_aaceld_22k,aac_fmtp162248,FALSE); + linphone_core_register_payload_type(lc,&payload_type_aaceld_32k,aac_fmtp3244,FALSE); + linphone_core_register_payload_type(lc,&payload_type_aaceld_44k,aac_fmtp3244,FALSE); + linphone_core_register_payload_type(lc,&payload_type_aaceld_48k,aac_fmtp162248,FALSE); + linphone_core_register_payload_type(lc,&payload_type_isac,NULL,FALSE); + linphone_core_register_payload_type(lc,&payload_type_speex_uwb,"vbr=on",FALSE); + linphone_core_register_payload_type(lc,&payload_type_silk_nb,NULL,FALSE); + linphone_core_register_payload_type(lc,&payload_type_silk_mb,NULL,FALSE); + linphone_core_register_payload_type(lc,&payload_type_silk_swb,NULL,FALSE); + linphone_core_register_payload_type(lc,&payload_type_g726_16,NULL,FALSE); + linphone_core_register_payload_type(lc,&payload_type_g726_24,NULL,FALSE); + linphone_core_register_payload_type(lc,&payload_type_g726_32,NULL,FALSE); + linphone_core_register_payload_type(lc,&payload_type_g726_40,NULL,FALSE); + linphone_core_register_payload_type(lc,&payload_type_aal2_g726_16,NULL,FALSE); + linphone_core_register_payload_type(lc,&payload_type_aal2_g726_24,NULL,FALSE); + linphone_core_register_payload_type(lc,&payload_type_aal2_g726_32,NULL,FALSE); + linphone_core_register_payload_type(lc,&payload_type_aal2_g726_40,NULL,FALSE); + + + +#ifdef VIDEO_ENABLED + /*default enabled video codecs, in order of preference*/ + linphone_core_register_payload_type(lc,&payload_type_vp8,NULL,TRUE); + linphone_core_register_payload_type(lc,&payload_type_h264,"profile-level-id=42801F",TRUE); + linphone_core_register_payload_type(lc,&payload_type_mp4v,"profile-level-id=3",TRUE); + linphone_core_register_payload_type(lc,&payload_type_h263_1998,"CIF=1;QCIF=1",FALSE); + linphone_core_register_payload_type(lc,&payload_type_h263,NULL,FALSE); +#endif + /*register all static payload types declared in av_profile of oRTP, if not already declared above*/ + linphone_core_register_static_payloads(lc); +} + +static void linphone_core_init(LinphoneCore * lc, const LinphoneCoreVTable *vtable, LpConfig *config, void * userdata){ + const char *remote_provisioning_uri = NULL; LinphoneCoreVTable* local_vtable= linphone_core_v_table_new(); ms_message("Initializing LinphoneCore %s", linphone_core_get_version()); @@ -1565,83 +1594,10 @@ static void linphone_core_init(LinphoneCore * lc, const LinphoneCoreVTable *vtab linphone_core_set_state(lc,LinphoneGlobalStartup,"Starting up"); ortp_init(); linphone_core_activate_log_serialization_if_needed(); - lc->dyn_pt=96; - lc->default_profile=rtp_profile_new("default profile"); - linphone_core_assign_payload_type(lc,&payload_type_pcmu8000,0,NULL); - linphone_core_assign_payload_type(lc,&payload_type_gsm,3,NULL); - linphone_core_assign_payload_type(lc,&payload_type_pcma8000,8,NULL); - linphone_core_assign_payload_type(lc,&payload_type_speex_nb,110,"vbr=on"); - linphone_core_assign_payload_type(lc,&payload_type_speex_wb,111,"vbr=on"); - linphone_core_assign_payload_type(lc,&payload_type_speex_uwb,112,"vbr=on"); - linphone_core_assign_payload_type(lc,&payload_type_telephone_event,101,"0-15"); - linphone_core_assign_payload_type(lc,&payload_type_g722,9,NULL); - -#ifdef ENABLE_NONSTANDARD_GSM - { - PayloadType *pt; - pt=payload_type_clone(&payload_type_gsm); - pt->clock_rate=11025; - linphone_core_assign_payload_type(lc,pt,-1,NULL); - pt->clock_rate=22050; - linphone_core_assign_payload_type(lc,pt,-1,NULL); - payload_type_destroy(pt); - } -#endif - -#ifdef VIDEO_ENABLED - - linphone_core_assign_payload_type(lc,&payload_type_h263,34,NULL); - linphone_core_assign_payload_type(lc,&payload_type_h263_1998,98,"CIF=1;QCIF=1"); - linphone_core_assign_payload_type(lc,&payload_type_mp4v,99,"profile-level-id=3"); - linphone_core_assign_payload_type(lc,&payload_type_h264,102,"profile-level-id=42801F"); - linphone_core_assign_payload_type(lc,&payload_type_vp8,103,NULL); - - /* linphone_core_assign_payload_type(lc,&payload_type_theora,97,NULL); commented out to free 1 slot */ - /* linphone_core_assign_payload_type(lc,&payload_type_x_snow,-1,NULL); commented out to free 1 slot */ - /* due to limited space in SDP, we have to disable this h264 line which is normally no more necessary */ - /* linphone_core_assign_payload_type(&payload_type_h264,-1,"packetization-mode=1;profile-level-id=428014");*/ -#endif - - /* For AAC, we use a config value to determine if we ought to support SBR. Since it is not offically supported - * for the mpeg4-generic mime type, setting this flag to 1 will break compatibility with other clients. */ - if( lp_config_get_int(lc->config, "misc", "aac_use_sbr", FALSE) ) { - ms_message("Using SBR for AAC"); - aac_fmtp162248 = "config=F8EE2000; constantDuration=512; indexDeltaLength=3; indexLength=3; mode=AAC-hbr; profile-level-id=76; sizeLength=13; streamType=5; SBR-enabled=1"; - aac_fmtp3244 = "config=F8E82000; constantDuration=512; indexDeltaLength=3; indexLength=3; mode=AAC-hbr; profile-level-id=76; sizeLength=13; streamType=5; SBR-enabled=1"; - } else { - aac_fmtp162248 = "config=F8EE2000; constantDuration=512; indexDeltaLength=3; indexLength=3; mode=AAC-hbr; profile-level-id=76; sizeLength=13; streamType=5"; - aac_fmtp3244 = "config=F8E82000; constantDuration=512; indexDeltaLength=3; indexLength=3; mode=AAC-hbr; profile-level-id=76; sizeLength=13; streamType=5"; - } - - - /*add all payload type for which we don't care about the number */ - linphone_core_assign_payload_type(lc,&payload_type_ilbc,-1,"mode=30"); - linphone_core_assign_payload_type(lc,&payload_type_amr,-1,"octet-align=1"); - linphone_core_assign_payload_type(lc,&payload_type_amrwb,-1,"octet-align=1"); - /* linphone_core_assign_payload_type(lc,&payload_type_lpc1015,-1,NULL); commented out to free 1 slot */ - linphone_core_assign_payload_type(lc,&payload_type_g726_16,-1,NULL); - linphone_core_assign_payload_type(lc,&payload_type_g726_24,-1,NULL); - linphone_core_assign_payload_type(lc,&payload_type_g726_32,-1,NULL); - linphone_core_assign_payload_type(lc,&payload_type_g726_40,-1,NULL); - linphone_core_assign_payload_type(lc,&payload_type_aal2_g726_16,-1,NULL); - linphone_core_assign_payload_type(lc,&payload_type_aal2_g726_24,-1,NULL); - linphone_core_assign_payload_type(lc,&payload_type_aal2_g726_32,-1,NULL); - linphone_core_assign_payload_type(lc,&payload_type_aal2_g726_40,-1,NULL); - linphone_core_assign_payload_type(lc,&payload_type_silk_nb,-1,NULL); - linphone_core_assign_payload_type(lc,&payload_type_silk_mb,-1,NULL); - linphone_core_assign_payload_type(lc,&payload_type_silk_wb,-1,NULL); - linphone_core_assign_payload_type(lc,&payload_type_silk_swb,-1,NULL); - linphone_core_assign_payload_type(lc,&payload_type_g729,18,"annexb=no"); - linphone_core_assign_payload_type(lc,&payload_type_aaceld_16k,-1,aac_fmtp162248); - linphone_core_assign_payload_type(lc,&payload_type_aaceld_22k,-1,aac_fmtp162248); - linphone_core_assign_payload_type(lc,&payload_type_aaceld_32k,-1,aac_fmtp3244); - linphone_core_assign_payload_type(lc,&payload_type_aaceld_44k,-1,aac_fmtp3244); - linphone_core_assign_payload_type(lc,&payload_type_aaceld_48k,-1,aac_fmtp162248); - linphone_core_assign_payload_type(lc,&payload_type_opus,-1,"useinbandfec=1; stereo=0; sprop-stereo=0"); - linphone_core_assign_payload_type(lc,&payload_type_isac,-1,NULL); - linphone_core_handle_static_payloads(lc); - + ms_init(); + + linphone_core_register_default_codecs(lc); /* create a mediastreamer2 event queue and set it as global */ /* This allows to run event's callback in linphone_core_iterate() */ lc->msevq=ms_event_queue_new(); @@ -1848,8 +1804,7 @@ LinphoneAddress *linphone_core_get_primary_contact_parsed(LinphoneCore *lc){ * The list is taken by the LinphoneCore thus the application should not free it. * This list is made of struct PayloadType describing the codec parameters. **/ -int linphone_core_set_audio_codecs(LinphoneCore *lc, MSList *codecs) -{ +int linphone_core_set_audio_codecs(LinphoneCore *lc, MSList *codecs){ if (lc->codecs_conf.audio_codecs!=NULL) ms_list_free(lc->codecs_conf.audio_codecs); lc->codecs_conf.audio_codecs=codecs; _linphone_core_codec_config_write(lc); @@ -1867,8 +1822,7 @@ int linphone_core_set_audio_codecs(LinphoneCore *lc, MSList *codecs) * The list is taken by the LinphoneCore thus the application should not free it. * This list is made of struct PayloadType describing the codec parameters. **/ -int linphone_core_set_video_codecs(LinphoneCore *lc, MSList *codecs) -{ +int linphone_core_set_video_codecs(LinphoneCore *lc, MSList *codecs){ if (lc->codecs_conf.video_codecs!=NULL) ms_list_free(lc->codecs_conf.video_codecs); lc->codecs_conf.video_codecs=codecs; _linphone_core_codec_config_write(lc); @@ -2632,6 +2586,15 @@ void linphone_core_iterate(LinphoneCore *lc){ } } +static LinphoneAddress* _linphone_core_destroy_addr_if_not_sip( LinphoneAddress* addr ){ + if( linphone_address_is_sip(addr) ) { + return addr; + } else { + linphone_address_destroy(addr); + return NULL; + } +} + /** * Interpret a call destination as supplied by the user, and returns a fully qualified * LinphoneAddress. @@ -2668,7 +2631,7 @@ LinphoneAddress * linphone_core_interpret_url(LinphoneCore *lc, const char *url) tmpurl=enumres->sip_address[0]; uri=linphone_address_new(tmpurl); enum_lookup_res_free(enumres); - return uri; + return _linphone_core_destroy_addr_if_not_sip(uri); } /* check if we have a "sip:" or a "sips:" */ if ( (strstr(url,"sip:")==NULL) && (strstr(url,"sips:")==NULL) ){ @@ -2679,7 +2642,7 @@ LinphoneAddress * linphone_core_interpret_url(LinphoneCore *lc, const char *url) uri=linphone_address_new(tmpurl); ms_free(tmpurl); if (uri){ - return uri; + return _linphone_core_destroy_addr_if_not_sip(uri); } } @@ -2695,12 +2658,12 @@ LinphoneAddress * linphone_core_interpret_url(LinphoneCore *lc, const char *url) linphone_proxy_config_normalize_number(proxy,url,normalized_username, sizeof(normalized_username)); linphone_address_set_username(uri,normalized_username); - return uri; + return _linphone_core_destroy_addr_if_not_sip(uri); }else return NULL; } uri=linphone_address_new(url); if (uri!=NULL){ - return uri; + return _linphone_core_destroy_addr_if_not_sip(uri); } return NULL; @@ -4021,7 +3984,11 @@ int linphone_core_resume_call(LinphoneCore *lc, LinphoneCall *call){ linphone_core_update_local_media_description_from_upnp(call->localdesc, call->upnp_session); } #endif //BUILD_UPNP - sal_call_set_local_media_description(call->op,call->localdesc); + if (!lc->sip_conf.sdp_200_ack){ + sal_call_set_local_media_description(call->op,call->localdesc); + } else { + sal_call_set_local_media_description(call->op,NULL); + } sal_media_description_set_dir(call->localdesc,SalStreamSendRecv); if (call->params->in_conference && !call->current_params->in_conference) subject="Conference"; if ( sal_call_update(call->op,subject,FALSE) != 0){ @@ -4032,6 +3999,12 @@ int linphone_core_resume_call(LinphoneCore *lc, LinphoneCall *call){ lc->current_call=call; snprintf(temp,sizeof(temp)-1,"Resuming the call with %s",linphone_call_get_remote_address_as_string(call)); linphone_core_notify_display_status(lc,temp); + + if (lc->sip_conf.sdp_200_ack){ + /*we are NOT offering, set local media description after sending the call so that we are ready to + process the remote offer when it will arrive*/ + sal_call_set_local_media_description(call->op,call->localdesc); + } return 0; } @@ -6195,8 +6168,8 @@ void _linphone_core_codec_config_write(LinphoneCore *lc){ static void codecs_config_uninit(LinphoneCore *lc) { _linphone_core_codec_config_write(lc); - ms_list_free(lc->codecs_conf.audio_codecs); - ms_list_free(lc->codecs_conf.video_codecs); + ms_list_free_with_data(lc->codecs_conf.audio_codecs, (void (*)(void*))payload_type_destroy); + ms_list_free_with_data(lc->codecs_conf.video_codecs, (void (*)(void*))payload_type_destroy); } void ui_config_uninit(LinphoneCore* lc) @@ -6505,20 +6478,6 @@ const char *linphone_core_get_remote_ringback_tone(const LinphoneCore *lc){ return lc->sound_conf.ringback_tone; } -static PayloadType* find_payload_type_from_list(const char* type, int rate, int channels, const MSList* from) { - const MSList *elem; - for(elem=from;elem!=NULL;elem=elem->next){ - PayloadType *pt=(PayloadType*)elem->data; - if ((strcasecmp((char*)type, payload_type_get_mime(pt)) == 0) - && (rate == LINPHONE_FIND_PAYLOAD_IGNORE_RATE || rate==pt->clock_rate) - && (channels == LINPHONE_FIND_PAYLOAD_IGNORE_CHANNELS || channels==pt->channels)) { - return pt; - } - } - return NULL; -} - - LinphonePayloadType* linphone_core_find_payload_type(LinphoneCore* lc, const char* type, int rate, int channels) { LinphonePayloadType* result = find_payload_type_from_list(type, rate, channels, linphone_core_get_audio_codecs(lc)); if (result) { diff --git a/coreapi/linphonecore.h b/coreapi/linphonecore.h index 3ec93dc5d..f7d8af700 100644 --- a/coreapi/linphonecore.h +++ b/coreapi/linphonecore.h @@ -398,6 +398,7 @@ LINPHONE_PUBLIC void linphone_address_set_port(LinphoneAddress *uri, int port); /*remove tags, params etc... so that it is displayable to the user*/ LINPHONE_PUBLIC void linphone_address_clean(LinphoneAddress *uri); LINPHONE_PUBLIC bool_t linphone_address_is_secure(const LinphoneAddress *uri); +LINPHONE_PUBLIC bool_t linphone_address_is_sip(const LinphoneAddress *uri); LINPHONE_PUBLIC LinphoneTransportType linphone_address_get_transport(const LinphoneAddress *uri); LINPHONE_PUBLIC void linphone_address_set_transport(LinphoneAddress *uri,LinphoneTransportType type); LINPHONE_PUBLIC char *linphone_address_as_string(const LinphoneAddress *u); @@ -2345,8 +2346,19 @@ LINPHONE_PUBLIC int linphone_core_enable_payload_type(LinphoneCore *lc, Linphone */ LINPHONE_PUBLIC LinphonePayloadType* linphone_core_find_payload_type(LinphoneCore* lc, const char* type, int rate, int channels) ; +/** + * @ingroup media_parameters + * Returns the payload type number assigned for this codec. +**/ LINPHONE_PUBLIC int linphone_core_get_payload_type_number(LinphoneCore *lc, const PayloadType *pt); +/** + * @ingroup media_parameters + * Force a number for a payload type. The LinphoneCore does payload type number assignment automatically. THis function is to be used mainly for tests, in order + * to override the automatic assignment mechanism. +**/ +LINPHONE_PUBLIC void linphone_core_set_payload_type_number(LinphoneCore *lc, PayloadType *pt, int number); + LINPHONE_PUBLIC const char *linphone_core_get_payload_type_description(LinphoneCore *lc, PayloadType *pt); LINPHONE_PUBLIC bool_t linphone_core_check_payload_type_usability(LinphoneCore *lc, const PayloadType *pt); diff --git a/coreapi/misc.c b/coreapi/misc.c index 99ea9182d..5912acc8f 100644 --- a/coreapi/misc.c +++ b/coreapi/misc.c @@ -59,15 +59,6 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. #define RTP_HDR_SZ 12 #define IP4_HDR_SZ 20 /*20 is the minimum, but there may be some options*/ -static void payload_type_set_enable(PayloadType *pt,int value) -{ - if ((value)!=0) payload_type_set_flag(pt,PAYLOAD_TYPE_ENABLED); \ - else payload_type_unset_flag(pt,PAYLOAD_TYPE_ENABLED); -} - -static bool_t payload_type_enabled(const PayloadType *pt) { - return (((pt)->flags & PAYLOAD_TYPE_ENABLED)!=0); -} bool_t linphone_core_payload_type_enabled(LinphoneCore *lc, const LinphonePayloadType *pt){ if (ms_list_find(lc->codecs_conf.audio_codecs, (PayloadType*) pt) || ms_list_find(lc->codecs_conf.video_codecs, (PayloadType*)pt)){ @@ -97,6 +88,10 @@ int linphone_core_get_payload_type_number(LinphoneCore *lc, const PayloadType *p return payload_type_get_number(pt); } +void linphone_core_set_payload_type_number(LinphoneCore *lc, PayloadType *pt, int number){ + payload_type_set_number(pt,number); +} + const char *linphone_core_get_payload_type_description(LinphoneCore *lc, PayloadType *pt){ if (ms_filter_codec_supported(pt->mime_type)){ MSFilterDesc *desc=ms_filter_get_encoder(pt->mime_type); diff --git a/coreapi/offeranswer.c b/coreapi/offeranswer.c index 755c6bcd7..ef14fad81 100644 --- a/coreapi/offeranswer.c +++ b/coreapi/offeranswer.c @@ -31,7 +31,49 @@ static bool_t only_telephone_event(const MSList *l){ return TRUE; } -static PayloadType * find_payload_type_best_match(const MSList *l, const PayloadType *refpt){ +typedef struct _PayloadTypeMatcher{ + const char *mime_type; + PayloadType *(*match_func)(const MSList *l, const PayloadType *refpt); +}PayloadTypeMatcher; + +static PayloadType * opus_match(const MSList *l, const PayloadType *refpt){ + PayloadType *pt; + const MSList *elem; + PayloadType *candidate=NULL; + + for (elem=l;elem!=NULL;elem=elem->next){ + pt=(PayloadType*)elem->data; + + /*workaround a bug in earlier versions of linphone where opus/48000/1 is offered, which is uncompliant with opus rtp draft*/ + if (strcasecmp(pt->mime_type,"opus")==0 ){ + if (refpt->channels==1){ + pt->channels=1; /*so that we respond with same number of channels */ + candidate=pt; + }else if (refpt->channels==2){ + return pt; + } + } + } + return candidate; +} + +/* the reason for this matcher is for some stupid uncompliant phone that offer G729a mime type !*/ +static PayloadType * g729A_match(const MSList *l, const PayloadType *refpt){ + PayloadType *pt; + const MSList *elem; + PayloadType *candidate=NULL; + + for (elem=l;elem!=NULL;elem=elem->next){ + pt=(PayloadType*)elem->data; + + if (strcasecmp(pt->mime_type,"G729")==0 && refpt->channels==pt->channels){ + candidate=pt; + } + } + return candidate; +} + +static PayloadType * amr_match(const MSList *l, const PayloadType *refpt){ PayloadType *pt; char value[10]; const MSList *elem; @@ -40,39 +82,63 @@ static PayloadType * find_payload_type_best_match(const MSList *l, const Payload for (elem=l;elem!=NULL;elem=elem->next){ pt=(PayloadType*)elem->data; - /*workaround a bug in earlier versions of linphone where opus/48000/1 is offered, which is uncompliant with opus rtp draft*/ - if (refpt->mime_type && strcasecmp(refpt->mime_type,"opus")==0 && refpt->channels==1 - && strcasecmp(pt->mime_type,refpt->mime_type)==0){ - pt->channels=1; /*so that we respond with same number of channels */ - candidate=pt; - break; - } - - /* the compare between G729 and G729A is for some stupid uncompliant phone*/ - if ( pt->mime_type && refpt->mime_type && - (strcasecmp(pt->mime_type,refpt->mime_type)==0 || - (strcasecmp(pt->mime_type, "G729") == 0 && strcasecmp(refpt->mime_type, "G729A") == 0 )) - && pt->clock_rate==refpt->clock_rate && pt->channels==refpt->channels){ - candidate=pt; - /*good candidate, check fmtp for H264 */ - if (strcasecmp(pt->mime_type,"H264")==0){ - if (pt->recv_fmtp!=NULL && refpt->recv_fmtp!=NULL){ - int mode1=0,mode2=0; - if (fmtp_get_value(pt->recv_fmtp,"packetization-mode",value,sizeof(value))){ - mode1=atoi(value); - } - if (fmtp_get_value(refpt->recv_fmtp,"packetization-mode",value,sizeof(value))){ - mode2=atoi(value); - } - if (mode1==mode2) - break; /*exact match */ - } - }else break; + if ( pt->mime_type && refpt->mime_type + && strcasecmp(pt->mime_type, refpt->mime_type)==0 + && pt->clock_rate==refpt->clock_rate + && pt->channels==refpt->channels) { + int octedalign1=0,octedalign2=0; + if (pt->recv_fmtp!=NULL && fmtp_get_value(pt->recv_fmtp,"octet-align",value,sizeof(value))){ + octedalign1=atoi(value); + } + if (refpt->send_fmtp!=NULL && fmtp_get_value(refpt->send_fmtp,"octet-align",value,sizeof(value))){ + octedalign2=atoi(value); + } + if (octedalign1==octedalign2) { + candidate=pt; + break; /*exact match */ + } } } return candidate; } +static PayloadType * generic_match(const MSList *l, const PayloadType *refpt){ + PayloadType *pt; + const MSList *elem; + + for (elem=l;elem!=NULL;elem=elem->next){ + pt=(PayloadType*)elem->data; + + if ( pt->mime_type && refpt->mime_type + && strcasecmp(pt->mime_type, refpt->mime_type)==0 + && pt->clock_rate==refpt->clock_rate + && pt->channels==refpt->channels) + return pt; + } + return NULL; +} + +static PayloadTypeMatcher matchers[]={ + {"opus", opus_match}, + {"G729A", g729A_match}, + {"AMR", amr_match}, + {"AMR-WB", amr_match}, + {NULL, NULL} +}; + + + +static PayloadType * find_payload_type_best_match(const MSList *l, const PayloadType *refpt){ + PayloadTypeMatcher *m; + for(m=matchers;m->mime_type!=NULL;++m){ + if (refpt->mime_type && strcasecmp(m->mime_type,refpt->mime_type)==0){ + return m->match_func(l,refpt); + } + } + return generic_match(l,refpt); +} + + static MSList *match_payloads(const MSList *local, const MSList *remote, bool_t reading_response, bool_t one_matching_codec){ const MSList *e2,*e1; MSList *res=NULL; @@ -110,6 +176,7 @@ static MSList *match_payloads(const MSList *local, const MSList *remote, bool_t res=ms_list_append(res,newp); /* we should use the remote numbering even when parsing a response */ payload_type_set_number(newp,remote_number); + payload_type_set_flag(newp, PAYLOAD_TYPE_FROZEN_NUMBER); if (reading_response && remote_number!=local_number){ ms_warning("For payload type %s, proposed number was %i but the remote phone answered %i", newp->mime_type, local_number, remote_number); @@ -120,6 +187,7 @@ static MSList *match_payloads(const MSList *local, const MSList *remote, bool_t */ newp=payload_type_clone(newp); payload_type_set_number(newp,local_number); + payload_type_set_flag(newp, PAYLOAD_TYPE_FROZEN_NUMBER); res=ms_list_append(res,newp); } }else{ @@ -143,7 +211,8 @@ static MSList *match_payloads(const MSList *local, const MSList *remote, bool_t if (!found){ ms_message("Adding %s/%i for compatibility, just in case.",p1->mime_type,p1->clock_rate); p1=payload_type_clone(p1); - p1->flags|=PAYLOAD_TYPE_FLAG_CAN_RECV; + payload_type_set_flag(p1, PAYLOAD_TYPE_FLAG_CAN_RECV); + payload_type_set_flag(p1, PAYLOAD_TYPE_FROZEN_NUMBER); res=ms_list_append(res,p1); } } diff --git a/coreapi/private.h b/coreapi/private.h index 9978252fa..8778f1020 100644 --- a/coreapi/private.h +++ b/coreapi/private.h @@ -353,6 +353,7 @@ static MS2_INLINE void set_string(char **dest, const char *src){ #define PAYLOAD_TYPE_ENABLED PAYLOAD_TYPE_USER_FLAG_0 #define PAYLOAD_TYPE_BITRATE_OVERRIDE PAYLOAD_TYPE_USER_FLAG_3 +#define PAYLOAD_TYPE_FROZEN_NUMBER PAYLOAD_TYPE_USER_FLAG_4 void linphone_process_authentication(LinphoneCore* lc, SalOp *op); void linphone_authentication_ok(LinphoneCore *lc, SalOp *op); @@ -631,7 +632,9 @@ typedef struct sound_config typedef struct codecs_config { MSList *audio_codecs; /* list of audio codecs in order of preference*/ - MSList *video_codecs; /* for later use*/ + MSList *video_codecs; + int dyn_pt; + int telephone_event_pt; }codecs_config_t; typedef struct video_config{ @@ -698,7 +701,8 @@ struct _LinphoneCore Sal *sal; LinphoneGlobalState state; struct _LpConfig *config; - RtpProfile *default_profile; + MSList *default_audio_codecs; + MSList *default_video_codecs; net_config_t net_conf; sip_config_t sip_conf; rtp_config_t rtp_conf; @@ -707,8 +711,6 @@ struct _LinphoneCore codecs_config_t codecs_conf; ui_config_t ui_conf; autoreplier_config_t autoreplier_conf; - MSList *payload_types; - int dyn_pt; LinphoneProxyConfig *default_proxy; MSList *friends; MSList *auth_info; @@ -1003,6 +1005,18 @@ static MS2_INLINE const LinphoneErrorInfo *linphone_error_info_from_sal_op(const return (const LinphoneErrorInfo*)sal_op_get_error_info(op); } +static MS2_INLINE void payload_type_set_enable(PayloadType *pt,int value) +{ + if ((value)!=0) payload_type_set_flag(pt,PAYLOAD_TYPE_ENABLED); \ + else payload_type_unset_flag(pt,PAYLOAD_TYPE_ENABLED); +} + +static MS2_INLINE bool_t payload_type_enabled(const PayloadType *pt) { + return (((pt)->flags & PAYLOAD_TYPE_ENABLED)!=0); +} + +bool_t is_payload_type_number_available(const MSList *l, int number, const PayloadType *ignore); + const MSCryptoSuite * linphone_core_get_srtp_crypto_suites(LinphoneCore *lc); /** Belle Sip-based objects need unique ids diff --git a/coreapi/quality_reporting.c b/coreapi/quality_reporting.c index bb1d443bf..e22f7229c 100644 --- a/coreapi/quality_reporting.c +++ b/coreapi/quality_reporting.c @@ -332,6 +332,7 @@ static int send_report(LinphoneCall* call, reporting_session_report_t * report, } linphone_content_set_buffer(content, buffer, strlen(buffer)); + ms_free(buffer); if (call->log->reporting.on_report_sent != NULL){ call->log->reporting.on_report_sent( diff --git a/coreapi/sal.c b/coreapi/sal.c index 6aa444947..ea26b25d8 100644 --- a/coreapi/sal.c +++ b/coreapi/sal.c @@ -62,9 +62,10 @@ SalMediaDescription *sal_media_description_new(){ static void sal_media_description_destroy(SalMediaDescription *md){ int i; for(i=0;istreams[i].payloads,(void (*)(void *))payload_type_destroy); - ms_list_free(md->streams[i].payloads); + ms_list_free_with_data(md->streams[i].payloads,(void (*)(void *))payload_type_destroy); + ms_list_free_with_data(md->streams[i].already_assigned_payloads,(void (*)(void *))payload_type_destroy); md->streams[i].payloads=NULL; + md->streams[i].already_assigned_payloads=NULL; } ms_free(md); } diff --git a/include/sal/sal.h b/include/sal/sal.h index e2ce603cc..f765bf349 100644 --- a/include/sal/sal.h +++ b/include/sal/sal.h @@ -109,7 +109,8 @@ void sal_address_set_transport(SalAddress* addr,SalTransport transport); void sal_address_set_transport_name(SalAddress* addr,const char* transport); void sal_address_set_params(SalAddress *addr, const char *params); void sal_address_set_uri_params(SalAddress *addr, const char *params); -bool_t sal_address_is_ipv6(SalAddress *addr); +bool_t sal_address_is_ipv6(const SalAddress *addr); +bool_t sal_address_is_sip(const SalAddress *addr); void sal_address_set_password(SalAddress *addr, const char *passwd); const char *sal_address_get_password(const SalAddress *addr); void sal_address_set_header(SalAddress *addr, const char *header_name, const char *header_value); @@ -206,7 +207,8 @@ typedef struct SalStreamDescription{ char rtcp_cname[255]; int rtp_port; int rtcp_port; - MSList *payloads; //\n" "Language-Team: Arabic (http://www.transifex.com/projects/p/linphone-gtk/" "language/ar/)\n" diff --git a/po/de.po b/po/de.po index 2446bd02c..229f60c71 100644 --- a/po/de.po +++ b/po/de.po @@ -12,7 +12,7 @@ msgstr "" "Project-Id-Version: linphone-gtk\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2015-01-13 16:18+0100\n" -"PO-Revision-Date: 2015-01-06 11:29+0000\n" +"PO-Revision-Date: 2015-01-13 16:34+0000\n" "Last-Translator: Gautier Pelloux-Prayer \n" "Language-Team: German (http://www.transifex.com/projects/p/linphone-gtk/" "language/de/)\n" diff --git a/po/fr.po b/po/fr.po index 23a2060fa..aff7882d6 100644 --- a/po/fr.po +++ b/po/fr.po @@ -12,7 +12,7 @@ msgstr "" "Project-Id-Version: linphone-gtk\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2015-01-13 16:18+0100\n" -"PO-Revision-Date: 2015-01-06 11:41+0000\n" +"PO-Revision-Date: 2015-01-13 16:34+0000\n" "Last-Translator: Gautier Pelloux-Prayer \n" "Language-Team: French (http://www.transifex.com/projects/p/linphone-gtk/" "language/fr/)\n" diff --git a/po/ja.po b/po/ja.po index 0ebeaa016..e998ee25c 100644 --- a/po/ja.po +++ b/po/ja.po @@ -10,7 +10,7 @@ msgstr "" "Project-Id-Version: linphone-gtk\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2015-01-13 16:18+0100\n" -"PO-Revision-Date: 2015-01-09 16:31+0000\n" +"PO-Revision-Date: 2015-01-13 16:34+0000\n" "Last-Translator: Alexander\n" "Language-Team: Japanese (http://www.transifex.com/projects/p/linphone-gtk/" "language/ja/)\n" diff --git a/po/ru.po b/po/ru.po index fd7b8f5b9..b3e312070 100644 --- a/po/ru.po +++ b/po/ru.po @@ -13,7 +13,7 @@ msgstr "" "Project-Id-Version: linphone-gtk\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2015-01-13 16:18+0100\n" -"PO-Revision-Date: 2015-01-07 08:31+0000\n" +"PO-Revision-Date: 2015-01-14 09:11+0000\n" "Last-Translator: AlexL \n" "Language-Team: Russian (http://www.transifex.com/projects/p/linphone-gtk/" "language/ru/)\n" diff --git a/po/sr.po b/po/sr.po index ae6f5da59..56f9e5f19 100644 --- a/po/sr.po +++ b/po/sr.po @@ -9,7 +9,7 @@ msgstr "" "Project-Id-Version: linphone-gtk\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2015-01-13 16:18+0100\n" -"PO-Revision-Date: 2015-01-07 17:21+0000\n" +"PO-Revision-Date: 2015-01-13 16:34+0000\n" "Last-Translator: Мирослав Николић \n" "Language-Team: Serbian (http://www.transifex.com/projects/p/linphone-gtk/" "language/sr/)\n" diff --git a/tester/Makefile.am b/tester/Makefile.am index d296841ad..6c5ba79bc 100644 --- a/tester/Makefile.am +++ b/tester/Makefile.am @@ -26,7 +26,8 @@ liblinphonetester_la_SOURCES = tester.c \ transport_tester.c \ player_tester.c \ dtmf_tester.c \ - accountmanager.c + accountmanager.c \ + offeranswer_tester.c liblinphonetester_la_LDFLAGS= -no-undefined liblinphonetester_la_LIBADD= ../coreapi/liblinphone.la $(CUNIT_LIBS) diff --git a/tester/accountmanager.c b/tester/accountmanager.c index 97cf8803b..2236b2039 100644 --- a/tester/accountmanager.c +++ b/tester/accountmanager.c @@ -77,6 +77,7 @@ void account_manager_destroy(void){ ms_free(the_am); } the_am=NULL; + ms_message("Test account manager destroyed."); } Account *account_manager_get_account(AccountManager *m, const LinphoneAddress *identity){ @@ -211,10 +212,6 @@ void linphone_core_manager_check_accounts(LinphoneCoreManager *m){ for(it=linphone_core_get_proxy_config_list(m->lc);it!=NULL;it=it->next){ LinphoneProxyConfig *cfg=(LinphoneProxyConfig *)it->data; - LinphoneAddress *modified_identity=account_manager_check_account(am,cfg); - if (m->identity){ - linphone_address_unref(m->identity); - } - m->identity=linphone_address_ref(modified_identity); + account_manager_check_account(am,cfg); } } diff --git a/tester/call_tester.c b/tester/call_tester.c index ca02f9ef3..5103ea15e 100644 --- a/tester/call_tester.c +++ b/tester/call_tester.c @@ -33,8 +33,6 @@ #endif static void srtp_call(void); - -static void disable_all_audio_codecs_except_one(LinphoneCore *lc, const char *mime, int rate); static char *create_filepath(const char *dir, const char *filename, const char *ext); // prototype definition for call_recording() @@ -728,7 +726,7 @@ static void cancelled_call(void) { linphone_core_manager_destroy(pauline); } -static void disable_all_audio_codecs_except_one(LinphoneCore *lc, const char *mime, int rate){ +void disable_all_audio_codecs_except_one(LinphoneCore *lc, const char *mime, int rate){ const MSList *elem=linphone_core_get_audio_codecs(lc); PayloadType *pt; @@ -755,40 +753,6 @@ static void disable_all_video_codecs_except_one(LinphoneCore *lc, const char *mi } #endif -static void call_failed_because_of_codecs(void) { - int begin,leaked_objects; - - belle_sip_object_enable_leak_detector(TRUE); - begin=belle_sip_object_get_object_count(); - - { - LinphoneCoreManager* marie = linphone_core_manager_new( "marie_rc"); - LinphoneCoreManager* pauline = linphone_core_manager_new( "pauline_rc"); - LinphoneCall* out_call; - - disable_all_audio_codecs_except_one(marie->lc,"pcmu",-1); - disable_all_audio_codecs_except_one(pauline->lc,"pcma",-1); - out_call = linphone_core_invite_address(pauline->lc,marie->identity); - linphone_call_ref(out_call); - CU_ASSERT_TRUE(wait_for(pauline->lc,marie->lc,&pauline->stat.number_of_LinphoneCallOutgoingInit,1)); - - /*flexisip will retain the 488 until the "urgent reply" timeout (I.E 5s) arrives.*/ - CU_ASSERT_TRUE(wait_for_until(pauline->lc,marie->lc,&pauline->stat.number_of_LinphoneCallError,1,7000)); - CU_ASSERT_EQUAL(linphone_call_get_reason(out_call),LinphoneReasonNotAcceptable); - CU_ASSERT_EQUAL(marie->stat.number_of_LinphoneCallIncomingReceived,0); - CU_ASSERT_EQUAL(marie->stat.number_of_LinphoneCallReleased,0); - - linphone_call_unref(out_call); - linphone_core_manager_destroy(marie); - linphone_core_manager_destroy(pauline); - } - leaked_objects=belle_sip_object_get_object_count()-begin; - CU_ASSERT_TRUE(leaked_objects==0); - if (leaked_objects>0){ - belle_sip_object_dump_active_objects(); - } -} - static void call_with_dns_time_out(void) { LinphoneCoreManager* marie = linphone_core_manager_new2( "empty_rc", FALSE); LCSipTransports transport = {9773,0,0,0}; @@ -2017,15 +1981,13 @@ static void simple_conference_base(LinphoneCoreManager* marie, LinphoneCoreManag CU_ASSERT_PTR_NOT_NULL_FATAL(marie_call_laure); linphone_core_add_to_conference(marie->lc,marie_call_laure); - CU_ASSERT_TRUE(wait_for(marie->lc,laure->lc,&marie->stat.number_of_LinphoneCallUpdating,initial_marie_stat.number_of_LinphoneCallUpdating+1)); - - + CU_ASSERT_TRUE(wait_for_list(lcs,&marie->stat.number_of_LinphoneCallUpdating,initial_marie_stat.number_of_LinphoneCallUpdating+1,5000)); linphone_core_add_to_conference(marie->lc,marie_call_pauline); CU_ASSERT_TRUE(wait_for_list(lcs,&marie->stat.number_of_LinphoneCallResuming,initial_marie_stat.number_of_LinphoneCallResuming+1,2000)); - CU_ASSERT_TRUE(wait_for_list(lcs,&pauline->stat.number_of_LinphoneCallStreamsRunning,initial_pauline_stat.number_of_LinphoneCallStreamsRunning+1,2000)); + CU_ASSERT_TRUE(wait_for_list(lcs,&pauline->stat.number_of_LinphoneCallStreamsRunning,initial_pauline_stat.number_of_LinphoneCallStreamsRunning+1,5000)); CU_ASSERT_TRUE(wait_for_list(lcs,&laure->stat.number_of_LinphoneCallStreamsRunning,initial_laure_stat.number_of_LinphoneCallStreamsRunning+1,2000)); CU_ASSERT_TRUE(wait_for_list(lcs,&marie->stat.number_of_LinphoneCallStreamsRunning,initial_marie_stat.number_of_LinphoneCallStreamsRunning+2,3000)); @@ -2160,12 +2122,16 @@ static void call_with_file_player(void) { linphone_core_terminate_all_calls(marie->lc); CU_ASSERT_TRUE(wait_for(pauline->lc,marie->lc,&pauline->stat.number_of_LinphoneCallEnd,1)); CU_ASSERT_TRUE(wait_for(pauline->lc,marie->lc,&marie->stat.number_of_LinphoneCallEnd,1)); +#ifndef __arm__ CU_ASSERT_TRUE(ms_audio_diff(hellopath,recordpath,&similar,NULL,NULL)==0); CU_ASSERT_TRUE(similar>threshold); CU_ASSERT_TRUE(similar<=1.0); if(similar > threshold && similar <=1.0) { remove(recordpath); } +#else + remove(recordpath); +#endif linphone_core_manager_destroy(marie); linphone_core_manager_destroy(pauline); ms_free(recordpath); @@ -2228,16 +2194,16 @@ static void call_with_mkv_file_player(void) { linphone_core_terminate_all_calls(marie->lc); CU_ASSERT_TRUE(wait_for(pauline->lc,marie->lc,&pauline->stat.number_of_LinphoneCallEnd,1)); CU_ASSERT_TRUE(wait_for(pauline->lc,marie->lc,&marie->stat.number_of_LinphoneCallEnd,1)); -#ifdef ANDROID - /*inter-correlation process is too much CPU consuming ending in a 20 minutes test on Android...*/ - remove(recordpath); -#else +#ifndef __arm__ CU_ASSERT_TRUE(ms_audio_diff(hellowav,recordpath,&similar,NULL,NULL)==0); CU_ASSERT_TRUE(similar>threshold); CU_ASSERT_TRUE(similar<=1.0); if(similar>threshold && similar<=1.0) { remove(recordpath); } +#else + /*inter-correlation process is too much CPU consuming ending in a 20 minutes test on arm...*/ + remove(recordpath); #endif ms_free(recordpath); @@ -3061,115 +3027,6 @@ static void multiple_early_media(void) { } #endif -static void profile_call(bool_t avpf1, bool_t srtp1, bool_t avpf2, bool_t srtp2, const char *expected_profile) { - LinphoneCoreManager *marie = linphone_core_manager_new("marie_rc"); - LinphoneCoreManager *pauline = linphone_core_manager_new("pauline_rc"); - LinphoneProxyConfig *lpc; - const LinphoneCallParams *params; - - if (avpf1) { - linphone_core_get_default_proxy(marie->lc, &lpc); - linphone_proxy_config_enable_avpf(lpc, TRUE); - linphone_proxy_config_set_avpf_rr_interval(lpc, 3); - } - if (avpf2) { - linphone_core_get_default_proxy(pauline->lc, &lpc); - linphone_proxy_config_enable_avpf(lpc, TRUE); - linphone_proxy_config_set_avpf_rr_interval(lpc, 3); - } - if (srtp1) { - if (linphone_core_media_encryption_supported(marie->lc, LinphoneMediaEncryptionSRTP)) { - linphone_core_set_media_encryption(marie->lc, LinphoneMediaEncryptionSRTP); - } - } - if (srtp2) { - if (linphone_core_media_encryption_supported(pauline->lc, LinphoneMediaEncryptionSRTP)) { - linphone_core_set_media_encryption(pauline->lc, LinphoneMediaEncryptionSRTP); - } - } - - CU_ASSERT_TRUE(call(marie, pauline)); - CU_ASSERT_TRUE(wait_for(marie->lc, pauline->lc, &marie->stat.number_of_LinphoneCallStreamsRunning, 1)); - CU_ASSERT_TRUE(wait_for(marie->lc, pauline->lc, &pauline->stat.number_of_LinphoneCallStreamsRunning, 1)); - params = linphone_call_get_current_params(linphone_core_get_current_call(marie->lc)); - CU_ASSERT_STRING_EQUAL(linphone_call_params_get_rtp_profile(params), expected_profile); - params = linphone_call_get_current_params(linphone_core_get_current_call(pauline->lc)); - CU_ASSERT_STRING_EQUAL(linphone_call_params_get_rtp_profile(params), expected_profile); - - linphone_core_terminate_all_calls(marie->lc); - CU_ASSERT_TRUE(wait_for(marie->lc, pauline->lc, &marie->stat.number_of_LinphoneCallEnd, 1)); - CU_ASSERT_TRUE(wait_for(marie->lc, pauline->lc, &pauline->stat.number_of_LinphoneCallEnd, 1)); - CU_ASSERT_EQUAL(marie->stat.number_of_LinphoneCallConnected, 1); - CU_ASSERT_EQUAL(pauline->stat.number_of_LinphoneCallConnected, 1); - - linphone_core_manager_destroy(pauline); - linphone_core_manager_destroy(marie); -} - -static void avp_to_avp_call(void) { - profile_call(FALSE, FALSE, FALSE, FALSE, "RTP/AVP"); -} - -static void avp_to_avpf_call(void) { - profile_call(FALSE, FALSE, TRUE, FALSE, "RTP/AVP"); -} - -static void avp_to_savp_call(void) { - profile_call(FALSE, FALSE, FALSE, TRUE, "RTP/AVP"); -} - -static void avp_to_savpf_call(void) { - profile_call(FALSE, FALSE, TRUE, TRUE, "RTP/AVP"); -} - -static void avpf_to_avp_call(void) { - profile_call(TRUE, FALSE, FALSE, FALSE, "RTP/AVPF"); -} - -static void avpf_to_avpf_call(void) { - profile_call(TRUE, FALSE, TRUE, FALSE, "RTP/AVPF"); -} - -static void avpf_to_savp_call(void) { - profile_call(TRUE, FALSE, FALSE, TRUE, "RTP/AVPF"); -} - -static void avpf_to_savpf_call(void) { - profile_call(TRUE, FALSE, TRUE, TRUE, "RTP/AVPF"); -} - -static void savp_to_avp_call(void) { - profile_call(FALSE, TRUE, FALSE, FALSE, "RTP/SAVP"); -} - -static void savp_to_avpf_call(void) { - profile_call(FALSE, TRUE, TRUE, FALSE, "RTP/SAVP"); -} - -static void savp_to_savp_call(void) { - profile_call(FALSE, TRUE, FALSE, TRUE, "RTP/SAVP"); -} - -static void savp_to_savpf_call(void) { - profile_call(FALSE, TRUE, TRUE, TRUE, "RTP/SAVP"); -} - -static void savpf_to_avp_call(void) { - profile_call(TRUE, TRUE, FALSE, FALSE, "RTP/SAVPF"); -} - -static void savpf_to_avpf_call(void) { - profile_call(TRUE, TRUE, TRUE, FALSE, "RTP/SAVPF"); -} - -static void savpf_to_savp_call(void) { - profile_call(TRUE, TRUE, FALSE, TRUE, "RTP/SAVPF"); -} - -static void savpf_to_savpf_call(void) { - profile_call(TRUE, TRUE, TRUE, TRUE, "RTP/SAVPF"); -} - static char *create_filepath(const char *dir, const char *filename, const char *ext) { return ms_strdup_printf("%s/%s.%s",dir,filename,ext); } @@ -3548,6 +3405,65 @@ static void outgoing_reinvite_without_ack_sdp() { #endif } + +static void call_with_paused_no_sdp_on_resume() { + int begin; + int leaked_objects; + int dummy=0; + LinphoneCoreManager* marie; + LinphoneCoreManager* pauline; + LinphoneCall* call_marie = NULL; + + belle_sip_object_enable_leak_detector(TRUE); + begin=belle_sip_object_get_object_count(); + + marie = linphone_core_manager_new( "marie_rc"); + pauline = linphone_core_manager_new( "pauline_rc"); + CU_ASSERT_TRUE(call(pauline,marie)); + liblinphone_tester_check_rtcp(marie,pauline); + + call_marie = linphone_core_get_current_call(marie->lc); + CU_ASSERT_PTR_NOT_NULL(call_marie); + + ms_message("== Call is OK =="); + + /* the called party pause the call */ + wait_for_until(pauline->lc, marie->lc, NULL, 5, 3000); + + linphone_core_pause_call(marie->lc,call_marie); + ms_message("== Call pausing =="); + CU_ASSERT_TRUE(wait_for(pauline->lc,marie->lc,&marie->stat.number_of_LinphoneCallPausing,1)); + CU_ASSERT_TRUE(wait_for(pauline->lc,marie->lc,&pauline->stat.number_of_LinphoneCallPausedByRemote,1)); + CU_ASSERT_TRUE(wait_for(pauline->lc,marie->lc,&marie->stat.number_of_LinphoneCallPaused,1)); + + /*stay in pause a little while in order to generate traffic*/ + wait_for_until(pauline->lc, marie->lc, NULL, 5, 2000); + + ms_message("== Call paused, marie call: %p ==", call_marie); + + linphone_core_enable_sdp_200_ack(marie->lc,TRUE); + + linphone_core_resume_call(marie->lc,call_marie); + + CU_ASSERT_TRUE(wait_for(marie->lc,pauline->lc,&marie->stat.number_of_LinphoneCallStreamsRunning,2)); + CU_ASSERT_TRUE(wait_for(marie->lc,pauline->lc,&pauline->stat.number_of_LinphoneCallStreamsRunning,2)); + + wait_for_until(marie->lc, pauline->lc, &dummy, 1, 3000); + CU_ASSERT_TRUE(linphone_call_get_audio_stats(call_marie)->download_bandwidth>70); + CU_ASSERT_TRUE(linphone_call_get_audio_stats(linphone_core_get_current_call(pauline->lc))->download_bandwidth>70); + + end_call(marie,pauline); + linphone_core_manager_destroy(marie); + linphone_core_manager_destroy(pauline); + + leaked_objects=belle_sip_object_get_object_count()-begin; + CU_ASSERT_TRUE(leaked_objects==0); + if (leaked_objects>0){ + belle_sip_object_dump_active_objects(); + } +} + + test_t call_tests[] = { { "Early declined call", early_declined_call }, { "Call declined", call_declined }, @@ -3555,7 +3471,6 @@ test_t call_tests[] = { { "Early cancelled call", early_cancelled_call}, { "Call with DNS timeout", call_with_dns_time_out }, { "Cancelled ringing call", cancelled_ringing_call }, - { "Call failed because of codecs", call_failed_because_of_codecs }, { "Simple call", simple_call }, { "Call with timeouted bye", call_with_timeouted_bye }, { "Direct call over IPv6", direct_call_over_ipv6}, @@ -3633,25 +3548,10 @@ test_t call_tests[] = { { "Call established with rejected RE-INVITE in error", call_established_with_rejected_reinvite_with_error}, { "Call redirected by callee", call_redirect}, { "Call with specified codec bitrate", call_with_specified_codec_bitrate}, - { "AVP to AVP call", avp_to_avp_call }, - { "AVP to AVPF call", avp_to_avpf_call }, - { "AVP to SAVP call", avp_to_savp_call }, - { "AVP to SAVPF call", avp_to_savpf_call }, - { "AVPF to AVP call", avpf_to_avp_call }, - { "AVPF to AVPF call", avpf_to_avpf_call }, - { "AVPF to SAVP call", avpf_to_savp_call }, - { "AVPF to SAVPF call", avpf_to_savpf_call }, - { "SAVP to AVP call", savp_to_avp_call }, - { "SAVP to AVPF call", savp_to_avpf_call }, - { "SAVP to SAVP call", savp_to_savp_call }, - { "SAVP to SAVPF call", savp_to_savpf_call }, - { "SAVPF to AVP call", savpf_to_avp_call }, - { "SAVPF to AVPF call", savpf_to_avpf_call }, - { "SAVPF to SAVP call", savpf_to_savp_call }, - { "SAVPF to SAVPF call", savpf_to_savpf_call }, { "Call with in-dialog UPDATE request", call_with_in_dialog_update }, { "Call with in-dialog codec change", call_with_in_dialog_codec_change }, { "Call with in-dialog codec change no sdp", call_with_in_dialog_codec_change_no_sdp }, + { "Call with pause no SDP on resume", call_with_paused_no_sdp_on_resume }, { "Call with custom supported tags", call_with_custom_supported_tags }, { "Call log from taken from asserted id",call_log_from_taken_from_p_asserted_id}, { "Incoming INVITE without SDP",incoming_invite_without_sdp}, diff --git a/tester/liblinphone_tester.c b/tester/liblinphone_tester.c index aa4f78f29..27b9672b0 100644 --- a/tester/liblinphone_tester.c +++ b/tester/liblinphone_tester.c @@ -109,6 +109,14 @@ JNIEXPORT jint JNICALL Java_org_linphone_tester_Tester_run(JNIEnv *env, jobject return ret; } +JNIEXPORT void JNICALL Java_org_linphone_tester_Tester_keepAccounts(JNIEnv *env, jclass c, jboolean keep) { + liblinphone_tester_keep_accounts((int)keep); +} + +JNIEXPORT void JNICALL Java_org_linphone_tester_Tester_clearAccounts(JNIEnv *env, jclass c) { + liblinphone_tester_clear_accounts(); +} + #endif /* ANDROID */ #ifdef __QNX__ diff --git a/tester/liblinphone_tester.h b/tester/liblinphone_tester.h index dded33942..b6aca2626 100644 --- a/tester/liblinphone_tester.h +++ b/tester/liblinphone_tester.h @@ -62,6 +62,7 @@ extern test_suite_t log_collection_test_suite; extern test_suite_t transport_test_suite; extern test_suite_t player_test_suite; extern test_suite_t dtmf_test_suite; +extern test_suite_t offeranswer_test_suite; extern int liblinphone_tester_nb_test_suites(void); @@ -293,6 +294,7 @@ bool_t call_with_test_params(LinphoneCoreManager* caller_mgr bool_t call(LinphoneCoreManager* caller_mgr,LinphoneCoreManager* callee_mgr); void end_call(LinphoneCoreManager *m1, LinphoneCoreManager *m2); +void disable_all_audio_codecs_except_one(LinphoneCore *lc, const char *mime, int rate); stats * get_stats(LinphoneCore *lc); LinphoneCoreManager *get_manager(LinphoneCore *lc); const char *liblinphone_tester_get_subscribe_content(void); diff --git a/tester/log_collection_tester.c b/tester/log_collection_tester.c index 35cf8d1f2..3260e3078 100644 --- a/tester/log_collection_tester.c +++ b/tester/log_collection_tester.c @@ -32,8 +32,8 @@ #endif -/*getline is not available on android...*/ -#ifdef ANDROID +/*getline is POSIX 2008, not available on many systems.*/ +#if defined(ANDROID) || defined(WIN32) /* This code is public domain -- Will Hartung 4/9/09 */ size_t getline(char **lineptr, size_t *n, FILE *stream) { char *bufptr = NULL; @@ -153,8 +153,10 @@ time_t check_file(LinphoneCoreManager* mgr) { int line_count = 0; char *line = NULL; size_t line_size = 256; +#ifndef WIN32 struct tm tm_curr; time_t time_prev = -1; +#endif #if HAVE_ZLIB // 0) if zlib is enabled, we must decompress the file first @@ -170,6 +172,7 @@ time_t check_file(LinphoneCoreManager* mgr) { while (getline(&line, &line_size, file) != -1) { // a) there should be at least 25 lines ++line_count; +#ifndef WIN32 // b) logs should be ordered by date (format: 2014-11-04 15:22:12:606) if (strlen(line) > 24) { char date[24] = {'\0'}; @@ -180,6 +183,9 @@ time_t check_file(LinphoneCoreManager* mgr) { time_prev = time_curr; } } +#else + ms_warning("strptime() not available for this platform, test is incomplete."); +#endif } CU_ASSERT_TRUE(line_count > 25); free(line); diff --git a/tester/offeranswer_tester.c b/tester/offeranswer_tester.c new file mode 100644 index 000000000..b9de6a879 --- /dev/null +++ b/tester/offeranswer_tester.c @@ -0,0 +1,297 @@ +/* + liblinphone_tester - liblinphone test suite + Copyright (C) 2013 Belledonne Communications SARL + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + + +#include +#include +#include +#include "CUnit/Basic.h" +#include "linphonecore.h" +#include "lpconfig.h" +#include "private.h" +#include "liblinphone_tester.h" + +static int get_codec_position(const MSList *l, const char *mime_type, int rate){ + const MSList *elem; + int i; + for (elem=l, i=0; elem!=NULL; elem=elem->next,i++){ + PayloadType *pt=(PayloadType*)elem->data; + if (strcasecmp(pt->mime_type, mime_type)==0 && pt->clock_rate==rate) return i; + } + return -1; +} + +/*check basic things about codecs at startup: order and enablement*/ +static void start_with_no_config(void){ + LinphoneCoreVTable vtable={0}; + LinphoneCore *lc=linphone_core_new(&vtable, NULL, NULL, NULL); + const MSList *codecs=linphone_core_get_audio_codecs(lc); + int opus_codec_pos; + int speex_codec_pos=get_codec_position(codecs, "speex", 8000); + int speex16_codec_pos=get_codec_position(codecs, "speex", 16000); + PayloadType *pt; + opus_codec_pos=get_codec_position(codecs, "opus", 48000); + if (opus_codec_pos!=-1) CU_ASSERT_TRUE(opus_codec_pos==0); + CU_ASSERT_TRUE(speex16_codec_poslc,"pcmu",-1); + disable_all_audio_codecs_except_one(pauline->lc,"pcmu",-1); + + /*marie set a fantasy number to PCMU*/ + linphone_core_set_payload_type_number(marie->lc, + linphone_core_find_payload_type(marie->lc, "PCMU", 8000, -1), + 104); + + CU_ASSERT_TRUE(call(marie,pauline)); + pauline_call=linphone_core_get_current_call(pauline->lc); + CU_ASSERT_PTR_NOT_NULL(pauline_call); + if (pauline_call){ + LinphoneCallParams *params; + check_payload_type_numbers(linphone_core_get_current_call(marie->lc), pauline_call, 104); + /*make a reinvite in the other direction*/ + linphone_core_update_call(pauline->lc, pauline_call, + params=linphone_core_create_call_params(pauline->lc, pauline_call)); + linphone_call_params_unref(params); + CU_ASSERT_TRUE(wait_for(pauline->lc,marie->lc,&pauline->stat.number_of_LinphoneCallUpdating,1)); + CU_ASSERT_TRUE(wait_for(pauline->lc,marie->lc,&marie->stat.number_of_LinphoneCallUpdatedByRemote,1)); + CU_ASSERT_TRUE(wait_for(pauline->lc,marie->lc,&pauline->stat.number_of_LinphoneCallStreamsRunning,2)); + CU_ASSERT_TRUE(wait_for(pauline->lc,marie->lc,&marie->stat.number_of_LinphoneCallStreamsRunning,2)); + /*payload type numbers shall remain the same*/ + check_payload_type_numbers(linphone_core_get_current_call(marie->lc), pauline_call, 104); + } + + end_call(marie,pauline); + linphone_core_manager_destroy(marie); + linphone_core_manager_destroy(pauline); + + leaked_objects=belle_sip_object_get_object_count()-begin; + CU_ASSERT_TRUE(leaked_objects==0); + if (leaked_objects>0){ + belle_sip_object_dump_active_objects(); + } +} + +static void call_failed_because_of_codecs(void) { + int begin,leaked_objects; + + belle_sip_object_enable_leak_detector(TRUE); + begin=belle_sip_object_get_object_count(); + + { + LinphoneCoreManager* marie = linphone_core_manager_new( "marie_rc"); + LinphoneCoreManager* pauline = linphone_core_manager_new( "pauline_rc"); + LinphoneCall* out_call; + + disable_all_audio_codecs_except_one(marie->lc,"pcmu",-1); + disable_all_audio_codecs_except_one(pauline->lc,"pcma",-1); + out_call = linphone_core_invite_address(pauline->lc,marie->identity); + linphone_call_ref(out_call); + CU_ASSERT_TRUE(wait_for(pauline->lc,marie->lc,&pauline->stat.number_of_LinphoneCallOutgoingInit,1)); + + /*flexisip will retain the 488 until the "urgent reply" timeout (I.E 5s) arrives.*/ + CU_ASSERT_TRUE(wait_for_until(pauline->lc,marie->lc,&pauline->stat.number_of_LinphoneCallError,1,7000)); + CU_ASSERT_EQUAL(linphone_call_get_reason(out_call),LinphoneReasonNotAcceptable); + CU_ASSERT_EQUAL(marie->stat.number_of_LinphoneCallIncomingReceived,0); + CU_ASSERT_EQUAL(marie->stat.number_of_LinphoneCallReleased,0); + + linphone_call_unref(out_call); + linphone_core_manager_destroy(marie); + linphone_core_manager_destroy(pauline); + } + leaked_objects=belle_sip_object_get_object_count()-begin; + CU_ASSERT_TRUE(leaked_objects==0); + if (leaked_objects>0){ + belle_sip_object_dump_active_objects(); + } +} + + +static void profile_call(bool_t avpf1, bool_t srtp1, bool_t avpf2, bool_t srtp2, const char *expected_profile) { + LinphoneCoreManager *marie = linphone_core_manager_new("marie_rc"); + LinphoneCoreManager *pauline = linphone_core_manager_new("pauline_rc"); + LinphoneProxyConfig *lpc; + const LinphoneCallParams *params; + + if (avpf1) { + linphone_core_get_default_proxy(marie->lc, &lpc); + linphone_proxy_config_enable_avpf(lpc, TRUE); + linphone_proxy_config_set_avpf_rr_interval(lpc, 3); + } + if (avpf2) { + linphone_core_get_default_proxy(pauline->lc, &lpc); + linphone_proxy_config_enable_avpf(lpc, TRUE); + linphone_proxy_config_set_avpf_rr_interval(lpc, 3); + } + if (srtp1) { + if (linphone_core_media_encryption_supported(marie->lc, LinphoneMediaEncryptionSRTP)) { + linphone_core_set_media_encryption(marie->lc, LinphoneMediaEncryptionSRTP); + } + } + if (srtp2) { + if (linphone_core_media_encryption_supported(pauline->lc, LinphoneMediaEncryptionSRTP)) { + linphone_core_set_media_encryption(pauline->lc, LinphoneMediaEncryptionSRTP); + } + } + + CU_ASSERT_TRUE(call(marie, pauline)); + CU_ASSERT_TRUE(wait_for(marie->lc, pauline->lc, &marie->stat.number_of_LinphoneCallStreamsRunning, 1)); + CU_ASSERT_TRUE(wait_for(marie->lc, pauline->lc, &pauline->stat.number_of_LinphoneCallStreamsRunning, 1)); + params = linphone_call_get_current_params(linphone_core_get_current_call(marie->lc)); + CU_ASSERT_STRING_EQUAL(linphone_call_params_get_rtp_profile(params), expected_profile); + params = linphone_call_get_current_params(linphone_core_get_current_call(pauline->lc)); + CU_ASSERT_STRING_EQUAL(linphone_call_params_get_rtp_profile(params), expected_profile); + + linphone_core_terminate_all_calls(marie->lc); + CU_ASSERT_TRUE(wait_for(marie->lc, pauline->lc, &marie->stat.number_of_LinphoneCallEnd, 1)); + CU_ASSERT_TRUE(wait_for(marie->lc, pauline->lc, &pauline->stat.number_of_LinphoneCallEnd, 1)); + CU_ASSERT_EQUAL(marie->stat.number_of_LinphoneCallConnected, 1); + CU_ASSERT_EQUAL(pauline->stat.number_of_LinphoneCallConnected, 1); + + linphone_core_manager_destroy(pauline); + linphone_core_manager_destroy(marie); +} + +static void avp_to_avp_call(void) { + profile_call(FALSE, FALSE, FALSE, FALSE, "RTP/AVP"); +} + +static void avp_to_avpf_call(void) { + profile_call(FALSE, FALSE, TRUE, FALSE, "RTP/AVP"); +} + +static void avp_to_savp_call(void) { + profile_call(FALSE, FALSE, FALSE, TRUE, "RTP/AVP"); +} + +static void avp_to_savpf_call(void) { + profile_call(FALSE, FALSE, TRUE, TRUE, "RTP/AVP"); +} + +static void avpf_to_avp_call(void) { + profile_call(TRUE, FALSE, FALSE, FALSE, "RTP/AVPF"); +} + +static void avpf_to_avpf_call(void) { + profile_call(TRUE, FALSE, TRUE, FALSE, "RTP/AVPF"); +} + +static void avpf_to_savp_call(void) { + profile_call(TRUE, FALSE, FALSE, TRUE, "RTP/AVPF"); +} + +static void avpf_to_savpf_call(void) { + profile_call(TRUE, FALSE, TRUE, TRUE, "RTP/AVPF"); +} + +static void savp_to_avp_call(void) { + profile_call(FALSE, TRUE, FALSE, FALSE, "RTP/SAVP"); +} + +static void savp_to_avpf_call(void) { + profile_call(FALSE, TRUE, TRUE, FALSE, "RTP/SAVP"); +} + +static void savp_to_savp_call(void) { + profile_call(FALSE, TRUE, FALSE, TRUE, "RTP/SAVP"); +} + +static void savp_to_savpf_call(void) { + profile_call(FALSE, TRUE, TRUE, TRUE, "RTP/SAVP"); +} + +static void savpf_to_avp_call(void) { + profile_call(TRUE, TRUE, FALSE, FALSE, "RTP/SAVPF"); +} + +static void savpf_to_avpf_call(void) { + profile_call(TRUE, TRUE, TRUE, FALSE, "RTP/SAVPF"); +} + +static void savpf_to_savp_call(void) { + profile_call(TRUE, TRUE, FALSE, TRUE, "RTP/SAVPF"); +} + +static void savpf_to_savpf_call(void) { + profile_call(TRUE, TRUE, TRUE, TRUE, "RTP/SAVPF"); +} + +static test_t offeranswer_tests[] = { + { "Start with no config", start_with_no_config }, + { "Call failed because of codecs", call_failed_because_of_codecs }, + { "Simple call with different codec mappings", simple_call_with_different_codec_mappings}, + { "AVP to AVP call", avp_to_avp_call }, + { "AVP to AVPF call", avp_to_avpf_call }, + { "AVP to SAVP call", avp_to_savp_call }, + { "AVP to SAVPF call", avp_to_savpf_call }, + { "AVPF to AVP call", avpf_to_avp_call }, + { "AVPF to AVPF call", avpf_to_avpf_call }, + { "AVPF to SAVP call", avpf_to_savp_call }, + { "AVPF to SAVPF call", avpf_to_savpf_call }, + { "SAVP to AVP call", savp_to_avp_call }, + { "SAVP to AVPF call", savp_to_avpf_call }, + { "SAVP to SAVP call", savp_to_savp_call }, + { "SAVP to SAVPF call", savp_to_savpf_call }, + { "SAVPF to AVP call", savpf_to_avp_call }, + { "SAVPF to AVPF call", savpf_to_avpf_call }, + { "SAVPF to SAVP call", savpf_to_savp_call }, + { "SAVPF to SAVPF call", savpf_to_savpf_call }, +}; + +test_suite_t offeranswer_test_suite = { + "Offer-answer", + NULL, + NULL, + sizeof(offeranswer_tests) / sizeof(offeranswer_tests[0]), + offeranswer_tests +}; diff --git a/tester/tester.c b/tester/tester.c index 8c09b90f1..d3a34a375 100644 --- a/tester/tester.c +++ b/tester/tester.c @@ -420,6 +420,7 @@ void liblinphone_tester_set_writable_dir_prefix(const char* writable_dir_prefix) void liblinphone_tester_init(void) { add_test_suite(&setup_test_suite); add_test_suite(®ister_test_suite); + add_test_suite(&offeranswer_test_suite); add_test_suite(&call_test_suite); add_test_suite(&message_test_suite); add_test_suite(&presence_test_suite); diff --git a/tools/python/unittests/test_register.py b/tools/python/unittests/test_register.py index f62da77e0..abfa6fa17 100644 --- a/tools/python/unittests/test_register.py +++ b/tools/python/unittests/test_register.py @@ -181,11 +181,11 @@ class TestRegister: cm = CoreManager('multi_account_rc', False) number_of_udp_proxies = reduce(lambda x, y: x + int(y.transport == "udp"), cm.lc.proxy_config_list, 0) total_number_of_proxies = len(cm.lc.proxy_config_list) + assert_equals(CoreManager.wait_for(cm, cm, lambda cm1, cm2: cm1.stats.number_of_LinphoneRegistrationOk == total_number_of_proxies), True) + register_ok = cm.stats.number_of_LinphoneRegistrationOk # Keep only UDP - tr = cm.lc.sip_transports - tr.tcp_port = 0 - tr.tls_port = 0 - tr.dtls_port = 0 + tr = linphone.SipTransports(0, 0, 0, 0) + tr.udp_port = cm.lc.sip_transports.udp_port cm.lc.sip_transports = tr - assert_equals(CoreManager.wait_for(cm, cm, lambda cm1, cm2: cm1.stats.number_of_LinphoneRegistrationOk == number_of_udp_proxies), True) + assert_equals(CoreManager.wait_for(cm, cm, lambda cm1, cm2: cm1.stats.number_of_LinphoneRegistrationOk == (register_ok + number_of_udp_proxies)), True) assert_equals(CoreManager.wait_for(cm, cm, lambda cm1, cm2: cm1.stats.number_of_LinphoneRegistrationFailed == (total_number_of_proxies - number_of_udp_proxies)), True)