Commits

Anonymous committed 2a1fe97 Merge

Merge branch 'master' into cross-test

Comments (0)

Files changed (38)

 					<folderInfo id="0.2079208171." name="/" resourcePath="">
 						<toolChain id="org.eclipse.cdt.build.core.prefbase.toolchain.2084203071" name="No ToolChain" resourceTypeBasedDiscovery="false" superClass="org.eclipse.cdt.build.core.prefbase.toolchain">
 							<targetPlatform binaryParser="org.eclipse.cdt.core.MachO64;org.eclipse.cdt.core.ELF" id="org.eclipse.cdt.build.core.prefbase.toolchain.2084203071.81924294" name=""/>
-							<builder id="org.eclipse.cdt.build.core.settings.default.builder.731584538" keepEnvironmentInBuildfile="false" managedBuildOn="false" name="Gnu Make Builder" superClass="org.eclipse.cdt.build.core.settings.default.builder"/>
+							<builder arguments="CFLAGS=&quot;-g -Werror -Wall&quot;" command="make" id="org.eclipse.cdt.build.core.settings.default.builder.731584538" keepEnvironmentInBuildfile="false" managedBuildOn="false" name="Gnu Make Builder" superClass="org.eclipse.cdt.build.core.settings.default.builder"/>
 							<tool id="org.eclipse.cdt.build.core.settings.holder.libs.1252970003" name="holder for library settings" superClass="org.eclipse.cdt.build.core.settings.holder.libs"/>
 							<tool id="org.eclipse.cdt.build.core.settings.holder.1371414073" name="GNU C++" superClass="org.eclipse.cdt.build.core.settings.holder">
 								<inputType id="org.eclipse.cdt.build.core.settings.holder.inType.306286573" languageId="org.eclipse.cdt.core.g++" languageName="GNU C++" sourceContentType="org.eclipse.cdt.core.cxxSource,org.eclipse.cdt.core.cxxHeader" superClass="org.eclipse.cdt.build.core.settings.holder.inType"/>
 			<storageModule moduleId="org.eclipse.cdt.core.externalSettings"/>
 			<storageModule moduleId="org.eclipse.cdt.internal.ui.text.commentOwnerProjectMappings"/>
 			<storageModule moduleId="org.eclipse.cdt.core.language.mapping"/>
-			<storageModule moduleId="org.eclipse.cdt.make.core.buildtargets">
-				<buildTargets>
-					<target name="test" path="mediastreamer2/tests" targetID="org.eclipse.cdt.build.MakeTargetBuilder">
-						<buildCommand>make</buildCommand>
-						<buildArguments/>
-						<buildTarget>all</buildTarget>
-						<stopOnError>true</stopOnError>
-						<useDefaultCommand>true</useDefaultCommand>
-						<runAllBuilders>true</runAllBuilders>
-					</target>
-					<target name="all" path="" targetID="org.eclipse.cdt.build.MakeTargetBuilder">
-						<buildCommand>make</buildCommand>
-						<buildTarget>install</buildTarget>
-						<stopOnError>true</stopOnError>
-						<useDefaultCommand>false</useDefaultCommand>
-						<runAllBuilders>true</runAllBuilders>
-					</target>
-					<target name="install" path="" targetID="org.eclipse.cdt.build.MakeTargetBuilder">
-						<buildCommand>make</buildCommand>
-						<buildTarget>install</buildTarget>
-						<stopOnError>true</stopOnError>
-						<useDefaultCommand>true</useDefaultCommand>
-						<runAllBuilders>true</runAllBuilders>
-					</target>
-					<target name="doc" path="coreapi/help" targetID="org.eclipse.cdt.build.MakeTargetBuilder">
-						<buildCommand>make</buildCommand>
-						<buildArguments/>
-						<buildTarget>all</buildTarget>
-						<stopOnError>true</stopOnError>
-						<useDefaultCommand>true</useDefaultCommand>
-						<runAllBuilders>true</runAllBuilders>
-					</target>
-					<target name="doc" path="mediastreamer2/help" targetID="org.eclipse.cdt.build.MakeTargetBuilder">
-						<buildCommand>make</buildCommand>
-						<buildArguments/>
-						<buildTarget>all</buildTarget>
-						<stopOnError>true</stopOnError>
-						<useDefaultCommand>true</useDefaultCommand>
-						<runAllBuilders>true</runAllBuilders>
-					</target>
-				</buildTargets>
-			</storageModule>
 			<storageModule moduleId="scannerConfiguration">
 				<autodiscovery enabled="true" problemReportingEnabled="true" selectedProfileId=""/>
 				<profile id="org.eclipse.cdt.make.core.GCCStandardMakePerProjectProfile">
 					</profile>
 				</scannerConfigBuildInfo>
 			</storageModule>
+			<storageModule moduleId="org.eclipse.cdt.make.core.buildtargets">
+				<buildTargets>
+					<target name="test" path="mediastreamer2/tests" targetID="org.eclipse.cdt.build.MakeTargetBuilder">
+						<buildCommand>make</buildCommand>
+						<buildTarget>all</buildTarget>
+						<stopOnError>true</stopOnError>
+						<useDefaultCommand>true</useDefaultCommand>
+						<runAllBuilders>true</runAllBuilders>
+					</target>
+					<target name="all" path="" targetID="org.eclipse.cdt.build.MakeTargetBuilder">
+						<buildCommand>make</buildCommand>
+						<buildArguments>CFLAGS="-g"</buildArguments>
+						<buildTarget>install</buildTarget>
+						<stopOnError>true</stopOnError>
+						<useDefaultCommand>true</useDefaultCommand>
+						<runAllBuilders>true</runAllBuilders>
+					</target>
+					<target name="install" path="" targetID="org.eclipse.cdt.build.MakeTargetBuilder">
+						<buildCommand>make</buildCommand>
+						<buildArguments>CFLAGS="-g"</buildArguments>
+						<buildTarget>install</buildTarget>
+						<stopOnError>true</stopOnError>
+						<useDefaultCommand>true</useDefaultCommand>
+						<runAllBuilders>true</runAllBuilders>
+					</target>
+					<target name="doc" path="coreapi/help" targetID="org.eclipse.cdt.build.MakeTargetBuilder">
+						<buildCommand>make</buildCommand>
+						<buildTarget>all</buildTarget>
+						<stopOnError>true</stopOnError>
+						<useDefaultCommand>true</useDefaultCommand>
+						<runAllBuilders>true</runAllBuilders>
+					</target>
+					<target name="doc" path="mediastreamer2/help" targetID="org.eclipse.cdt.build.MakeTargetBuilder">
+						<buildCommand>make</buildCommand>
+						<buildTarget>all</buildTarget>
+						<stopOnError>true</stopOnError>
+						<useDefaultCommand>true</useDefaultCommand>
+						<runAllBuilders>true</runAllBuilders>
+					</target>
+				</buildTargets>
+			</storageModule>
 		</cconfiguration>
 	</storageModule>
 	<storageModule moduleId="cdtBuildSystem" version="4.0.0">
 				</dictionary>
 				<dictionary>
 					<key>org.eclipse.cdt.make.core.buildArguments</key>
-					<value></value>
+					<value>CFLAGS=&quot;-g -Werror -Wall&quot;</value>
 				</dictionary>
 				<dictionary>
 					<key>org.eclipse.cdt.make.core.buildCommand</key>
 				</dictionary>
 				<dictionary>
 					<key>org.eclipse.cdt.make.core.useDefaultBuildCmd</key>
-					<value>true</value>
+					<value>false</value>
 				</dictionary>
 			</arguments>
 		</buildCommand>
 	rm -rf $(BUNDLEDIR)
 discovery:
 	touch specs.cpp
-	$(CC) $(CFLAGS) $(ANTLR_CFLAGS) $(CUNIT_CFLAGS) -E -P -v -dD specs.cpp
+	$(CC) $(CFLAGS) $(MEDIASTREAMER2_CFLAGS) $(ORTP_CFLAGS) -E -P -v -dD specs.cpp
-linphone-3.5.1 -- ??
+linphone-3.xxx --
+	* fix bug in zRTP support (upgrade required)
+	*
+
+linphone-3.5.2 -- February 22, 2012
+	* updated oRTP to 0.20.0
+	* updated mediastreamer2 to 2.8.2
+	* added ZRTP media encryption
+	* added SILK audio codec
+
+linphone-3.5.1 -- February 17, 2012
 	* gtk - implement friend search by typing into the friendlist, and friend sorting
 
 linphone-3.5.0 -- December 22, 2011
 
 Then or otherwise, do:
 	
- $ ./configure --prefix=/opt/local --with-readline=/opt/local --disable-strict --disable-x11 --with-srtp=/opt/local --with-gsm=/opt/local --enable-zrtp && make
+ $ ./configure --prefix=/opt/local --with-readline=/opt/local --disable-x11 --with-srtp=/opt/local --with-gsm=/opt/local --enable-zrtp && make
 
 Install to /opt/local
 

build/macos/linphone.bundle

   <binary >
     ${prefix:ms2plugins}/lib/mediastreamer/plugins/*.so
   </binary>
+  
+  <binary >
+    ${prefix:linphone}/lib/*.dylib
+  </binary>
 
   <!-- Copy in GTK+ modules.  Note the ${gtkdir} macro, which expands
        to the correct library subdirectory for the specified gtk
 dnl Process this file with autoconf to produce a configure script.
 
-AC_INIT([linphone],[3.5.1],[linphone-developers@nongnu.org])
+AC_INIT([linphone],[3.5.2],[linphone-developers@nongnu.org])
 AC_CANONICAL_SYSTEM
 AC_CONFIG_SRCDIR([coreapi/linphonecore.c])
 
 	LINPHONE_VERSION=$LINPHONE_VERSION.${LINPHONE_EXTRA_VERSION}
 fi
 
-LIBLINPHONE_SO_CURRENT=4 dnl increment this number when you add/change/remove an interface
+LIBLINPHONE_SO_CURRENT=5 dnl increment this number when you add/change/remove an interface
 LIBLINPHONE_SO_REVISION=0 dnl increment this number when you change source code, without changing interfaces; set to 0 when incrementing CURRENT
 LIBLINPHONE_SO_AGE=0 dnl increment this number when you add an interface, set to 0 if you remove an interface
 
                 ios_found=yes
         ;;
         x86_64-apple-darwin*|i686-apple-darwin*)
-                MSPLUGINS_CFLAGS=""
+        MSPLUGINS_CFLAGS=""
 		dnl use macport installation
 		ACLOCAL_MACOS_FLAGS="-I /opt/local/share/aclocal"
 		build_macos=yes
 AC_DEFINE_UNQUOTED(LINPHONE_ALL_LANGS, "$ALL_LINGUAS", [All supported languages])
 
 if test "$mingw_found" != "yes" ; then
-dnl gettext macro does not work properly under mingw. And we want to use the one provided by GTK.
-AM_GNU_GETTEXT([external])
-LIBS="$LIBS $LIBINTL"
+	dnl gettext macro does not work properly under mingw. And we want to use the one provided by GTK.
+	
+	dnl AM_GNU_GETTEXT pollutes CPPFLAGS: workaround this.
+	CPPFLAGS_save=$CPPFLAGS
+	AM_GNU_GETTEXT([external])
+	CPPFLAGS=$CPPFLAGS_save
+	LIBS="$LIBS $LIBINTL"
 else
 	AC_DEFINE(ENABLE_NLS,1,[Tells whether localisation is possible])
 	AC_DEFINE(HAVE_GETTEXT,1,[Tells wheter localisation is possible])
 
 AC_WORDS_BIGENDIAN
 
-dnl normaly this should only by done by mediastreamer2/configure.ac
-dnl but to workaround bugs when cross-compiling for arm-linux,
-dnl we need to have SPEEX_LIBS defined
-dnl Furthermore it is good to repeat here all mediastreamer2 toggles
-dnl since top-level configure --help will not print them.
-
-PKG_CHECK_MODULES(SPEEX, speex >= 1.1.6, build_speex=yes)
+AC_ARG_ENABLE([speex],
+	      AS_HELP_STRING([--disable-speex], [Disable speex support]),
+	      [], [enable_speex=yes])
+if test "x$enable_speex" = "xyes"; then
+	dnl normaly this should only by done by mediastreamer2/configure.ac
+	dnl but to workaround bugs when cross-compiling for arm-linux,
+	dnl we need to have SPEEX_LIBS defined
+	dnl Furthermore it is good to repeat here all mediastreamer2 toggles
+	dnl since top-level configure --help will not print them.
+	PKG_CHECK_MODULES(SPEEX, speex >= 1.1.6, build_speex=yes)
+fi
 
 dnl conditionnal build of video support
 AC_ARG_ENABLE(video,
     AC_DEFINE( HAVE_SIGHANDLER_T, 1, [Define if sighandler_t available] )
 fi
 
+AC_ARG_ENABLE(assistant,
+      [  --enable-assistant    Turn on assistant compiling],
+      [case "${enableval}" in
+        yes) build_wizard=true ;;
+        no)  build_wizard=false ;;
+        *) AC_MSG_ERROR(bad value ${enableval} for --enable-assistant) ;;
+      esac],[build_wizard=check])
+
 dnl check libsoup (needed for wizard)
-PKG_CHECK_MODULES(LIBSOUP, [libsoup-2.4 >= 2.26],[build_wizard=true],foo=bar)
+if test "$build_wizard" != "false" ; then
+	PKG_CHECK_MODULES(LIBSOUP, [libsoup-2.4 >= 2.26],[build_wizard=true],
+	[
+		if test "$build_wizard" = "true" ; then
+			AC_MSG_ERROR([Could not found libsoup, assistant cannot be compiled.])
+		else
+			build_wizard=false
+		fi
+	])
+fi
 AC_SUBST(LIBSOUP_CFLAGS)
 AC_SUBST(LIBSOUP_LIBS)
 AM_CONDITIONAL(BUILD_WIZARD, test x$build_wizard = xtrue)
    AC_DEFINE( BUILD_WIZARD, 1, [Define if wizard enabled] ) 
 fi
 
+AC_CHECK_HEADERS(libudev.h)
+AC_CHECK_LIB(udev,udev_new)
+
 ##################################################
 # Stricter build options (after external packages)
 ##################################################
 
 STRICT_OPTIONS="-Wall "
 
-if test "$GCC$strictness" = "yesyes" ; then
+if test "$strictness" = "yes" ; then
 	STRICT_OPTIONS="$STRICT_OPTIONS -Werror"
 	CFLAGS="$CFLAGS -fno-strict-aliasing"
 fi
 AC_OUTPUT
 
 echo "Linphone build configuration ended."
-
-if test x$gtk_ui = xtrue ; then
-	echo "* GTK interface will be compiled."
-fi
-if test x$console_ui = xtrue ; then
-	echo "* Console interface will be compiled."
+echo "Summary of build options:"
+printf "* Video support\t\t\t%s\n" $video
+printf "* GTK interface\t\t\t%s\n" $gtk_ui
+printf "* Account assistant\t\t%s\n" $build_wizard
+printf "* Console interface\t\t%s\n" $console_ui
+printf "* zRTP encryption (GPLv3)\t%s\n" $zrtp
+
+if test "$enable_tunnel" = "true" ; then
+	printf "* Tunnel support\t\ttrue\n"
 fi
 
 echo "Now type 'make' to compile, and then 'make install' as root to install it."

console/commands.c

 		char arg1[256]={0};
 		char arg2[266]={0};
 		long id2=0;
-		int n=sscanf(args,"%s %s %li",arg1,arg2,&id2);
+		int n=sscanf(args,"%255s %265s %li",arg1,arg2,&id2);
 		if (n==1 || isalpha(*arg1)){
 			call=linphone_core_get_current_call(lc);
 			if (call==NULL && ms_list_size(linphone_core_get_calls(lc))==1){
     		return 1;
     	}
 	passwd[0]=proxy[0]=identity[0]='\0';
-	sscanf(args,"%s %s %s",identity,proxy,passwd);
+	sscanf(args,"%511s %511s %511s",identity,proxy,passwd);
 	if (proxy[0]=='\0' || identity[0]=='\0'){
 		linphonec_out("Missing parameters, see help register\n");
 		return 1;
 	if (args == NULL) {
 		return 0;
 	}
-	switch (sscanf(args,"%s %s %s",section,param,value)) {
+	switch (sscanf(args,"%19s %19s %49s",section,param,value)) {
 		// case 1 might show all current settings under a section
 		case 2:
 			string = lp_config_get_string(linphone_core_get_config(lc), section, param, "(undef)");
 	
     if (!args) return 0;
 	memset(voice,0,sizeof(voice));
-	sscanf(args,"%s63",voice);
+	sscanf(args,"%63s",voice);
 	sentence=args+strlen(voice);
 	wavfile=tempnam("/tmp/","linphonec-espeak-");
 	snprintf(cl,sizeof(cl),"espeak -v %s -s 100 -w %s --stdin",voice,wavfile);
 	VideoParams *params=is_preview ? &lpc_preview_params : &lpc_video_params;
 
 	if (!args) return 0;
-	err=sscanf(args,"%s %i %i",subcommand,&a,&b);
+	err=sscanf(args,"%63s %i %i",subcommand,&a,&b);
 	if (err>=1){
 		if (strcmp(subcommand,"pos")==0){
 			if (err<3) return 0;

coreapi/TunnelManager.cc

 	if (!mTunnelClient) {
 		mTunnelClient = new TunnelClient();
 		mTunnelClient->setCallback((StateCallback)tunnelCallback,this);
-		std::list<ServerAddr>::iterator it;
+		list<ServerAddr>::iterator it;
 		for(it=mServerAddrs.begin();it!=mServerAddrs.end();++it){
 			const ServerAddr &addr=*it;
 			mTunnelClient->addServer(addr.mAddr.c_str(), addr.mPort);

coreapi/callbacks.c

 
 		if (propose_early_media || ringback_tone!=NULL){
 			linphone_call_set_state(call,LinphoneCallIncomingEarlyMedia,"Incoming call early media");
+			md=sal_call_get_final_media_description(h);
 			linphone_core_update_streams(lc,call,md);
 		}
 		if (sal_call_get_replaces(call->op)!=NULL && lp_config_get_int(lc->config,"sip","auto_answer_replacing_calls",1)){
 		if (lc->ringstream!=NULL) return;	/*already ringing !*/
 		if (lc->sound_conf.play_sndcard!=NULL){
 			MSSndCard *ringcard=lc->sound_conf.lsd_card ? lc->sound_conf.lsd_card : lc->sound_conf.play_sndcard;
+			if (call->localdesc->streams[0].max_rate>0) ms_snd_card_set_preferred_sample_rate(ringcard, call->localdesc->streams[0].max_rate);
 			lc->ringstream=ring_start(lc->sound_conf.remote_ring,2000,ringcard);
 		}
 		ms_message("Remote ringing...");
 	    call->state==LinphoneCallOutgoingRinging ||
 	    call->state==LinphoneCallOutgoingEarlyMedia){
 		linphone_call_set_state(call,LinphoneCallConnected,"Connected");
+		if (call->referer) linphone_core_notify_refer_state(lc,call->referer,call);
 	}
 	if (md && !sal_media_description_empty(md)){
 		if (sal_media_description_has_dir(md,SalStreamSendOnly) ||
 				}
 			}
 			linphone_core_update_streams (lc,call,md);
-			linphone_call_set_state(call, LinphoneCallStreamsRunning, "Streams running");
 			if (!call->current_params.in_conference)
 				lc->current_call=call;
+			linphone_call_set_state(call, LinphoneCallStreamsRunning, "Streams running");
 		}
 	}else{
 		/*send a bye*/
 	}
 }
 
+static void call_accept_update(LinphoneCore *lc, LinphoneCall *call){
+	SalMediaDescription *md;
+	sal_call_accept(call->op);
+	md=sal_call_get_final_media_description(call->op);
+	if (md && !sal_media_description_empty(md))
+		linphone_core_update_streams(lc,call,md);
+}
+
+static void call_resumed(LinphoneCore *lc, LinphoneCall *call){
+	call_accept_update(lc,call);
+	if(lc->vtable.display_status)
+		lc->vtable.display_status(lc,_("We have been resumed."));
+	linphone_call_set_state(call,LinphoneCallStreamsRunning,"Connected (streams running)");
+	linphone_call_set_transfer_state(call, LinphoneCallIdle);
+}
+
+static void call_paused_by_remote(LinphoneCore *lc, LinphoneCall *call){
+	call_accept_update(lc,call);
+	/* we are being paused */
+	if(lc->vtable.display_status)
+		lc->vtable.display_status(lc,_("We are paused by other party."));
+	linphone_call_set_state (call,LinphoneCallPausedByRemote,"Call paused by remote");
+}
+
+static void call_updated_by_remote(LinphoneCore *lc, LinphoneCall *call){
+	if(lc->vtable.display_status)
+		lc->vtable.display_status(lc,_("Call is updated by remote."));
+	call->defer_update=FALSE;
+	linphone_call_set_state(call, LinphoneCallUpdatedByRemote,"Call updated by remote");
+	if (call->defer_update==FALSE){
+		linphone_core_accept_call_update(lc,call,NULL);
+	}
+}
 
 /* this callback is called when an incoming re-INVITE modifies the session*/
 static void call_updating(SalOp *op){
 	LinphoneCore *lc=(LinphoneCore *)sal_get_user_pointer(sal_op_get_sal(op));
 	LinphoneCall *call=(LinphoneCall*)sal_op_get_user_pointer(op);
-	LinphoneCallState prevstate=LinphoneCallIdle;
-	SalMediaDescription *md;
-	SalMediaDescription *old_md=call->resultdesc;
-
-	sal_media_description_ref(old_md);
-	
-	md=sal_call_get_final_media_description(op);
+	SalMediaDescription *rmd=sal_call_get_remote_media_description(op);
 
-	/*accept the modification (sends a 200Ok)*/
-	sal_call_accept(op);
-	
-	if (md && !sal_media_description_empty(md))
-	{
-		linphone_core_update_streams (lc,call,md);
+	if (rmd==NULL){
+		/* case of a reINVITE without SDP */
+		call_accept_update(lc,call);
+		call->media_pending=TRUE;
+		return;
+	}
 
-		if (sal_media_description_has_dir(call->localdesc,SalStreamSendRecv)){
-			ms_message("Our local status is SalStreamSendRecv");
-			if (sal_media_description_has_dir (md,SalStreamRecvOnly) || sal_media_description_has_dir(md,SalStreamInactive)){
-				/* we are being paused */
-				if(lc->vtable.display_status)
-					lc->vtable.display_status(lc,_("We are being paused..."));
-				linphone_call_set_state (call,LinphoneCallPausedByRemote,"Call paused by remote");
-			}else if (!sal_media_description_has_dir(old_md,SalStreamSendRecv) && sal_media_description_has_dir(md,SalStreamSendRecv)){
-				if(lc->vtable.display_status)
-					lc->vtable.display_status(lc,_("We have been resumed..."));
-				linphone_call_set_state (call,LinphoneCallStreamsRunning,"Connected (streams running)");
+	switch(call->state){
+		case LinphoneCallPausedByRemote:
+			if (sal_media_description_has_dir(rmd,SalStreamSendRecv) || sal_media_description_has_dir(rmd,SalStreamRecvOnly)){
+				call_resumed(lc,call);
+			}
+		break;
+		case LinphoneCallStreamsRunning:
+		case LinphoneCallConnected:
+			if (sal_media_description_has_dir(rmd,SalStreamSendOnly) || sal_media_description_has_dir(rmd,SalStreamInactive)){
+				call_paused_by_remote(lc,call);
 			}else{
-				prevstate=call->state;
-				if(lc->vtable.display_status)
-					lc->vtable.display_status(lc,_("Call has been updated by remote..."));
-				linphone_call_set_state(call, LinphoneCallUpdatedByRemote,"Call updated by remote");
+				call_updated_by_remote(lc,call);
 			}
-		}
-
-		if (prevstate!=LinphoneCallIdle){
-			linphone_call_set_state (call,prevstate,"Connected (streams running)");
-		}
+		break;
+		default:
+			call_accept_update(lc,call);
 	}
-	sal_media_description_unref(old_md);
 }
 
 static void call_terminated(SalOp *op, const char *from){
 					int i;
 					ms_message("Outgoing call failed with SRTP (SAVP) enabled - retrying with AVP");
 					linphone_call_stop_media_streams(call);
-					/* clear SRTP local params */
-					call->params.media_encryption = LinphoneMediaEncryptionNone;
-					for(i=0; i<call->localdesc->nstreams; i++) {
-						call->localdesc->streams[i].proto = SalProtoRtpAvp;
-						memset(call->localdesc->streams[i].crypto, 0, sizeof(call->localdesc->streams[i].crypto));
+					if (call->state==LinphoneCallOutgoingInit || call->state==LinphoneCallOutgoingProgress){
+						/* clear SRTP local params */
+						call->params.media_encryption = LinphoneMediaEncryptionNone;
+						for(i=0; i<call->localdesc->nstreams; i++) {
+							call->localdesc->streams[i].proto = SalProtoRtpAvp;
+							memset(call->localdesc->streams[i].crypto, 0, sizeof(call->localdesc->streams[i].crypto));
+						}
+						linphone_core_start_invite(lc, call, NULL);
 					}
-					linphone_core_start_invite(lc, call, NULL);
 					return;
 				}
-				msg=_("No common codecs");
+				msg=_("Incompatible media parameters.");
 				if (lc->vtable.display_status)
 					lc->vtable.display_status(lc,msg);
 			break;
 		lc->ringstream=NULL;
 	}
 	linphone_call_stop_media_streams (call);
+	if (call->referer && linphone_call_get_state(call->referer)==LinphoneCallPaused && call->referer->was_automatically_paused){
+		/*resume to the call that send us the refer automatically*/
+		linphone_core_resume_call(lc,call->referer);
+	}
 	if (sr == SalReasonDeclined) {
 		call->reason=LinphoneReasonDeclined;
 		linphone_call_set_state(call,LinphoneCallEnd,"Call declined.");
 static void auth_requested(SalOp *h, const char *realm, const char *username){
 	LinphoneCore *lc=(LinphoneCore *)sal_get_user_pointer(sal_op_get_sal(h));
 	LinphoneAuthInfo *ai=(LinphoneAuthInfo*)linphone_core_find_auth_info(lc,realm,username);
+	LinphoneCall *call=is_a_linphone_call(sal_op_get_user_pointer(h));
+
+	if (call && call->ping_op==h){
+		/*don't request authentication for ping requests. Their purpose is just to get any
+		 * answer to get the Via's received and rport parameters.
+		 */
+		ms_message("auth_requested(): ignored for ping request.");
+		return;
+	}
+	
 	ms_message("auth_requested() for realm=%s, username=%s",realm,username);
 
 	if (ai && ai->works==FALSE && ai->usecount>=3){
 	LinphoneProxyConfig *cfg=(LinphoneProxyConfig*)sal_op_get_user_pointer(op);
 	char *msg;
 	
+	if (cfg->deletion_date!=0){
+		ms_message("Registration success for removed proxy config, ignored");
+		return;
+	}
 	linphone_proxy_config_set_error(cfg,LinphoneReasonNone);
 	linphone_proxy_config_set_state(cfg, registered ? LinphoneRegistrationOk : LinphoneRegistrationCleared ,
 	                                registered ? "Registration sucessful" : "Unregistration done");
 		ms_warning("Registration failed for unknown proxy config.");
 		return ;
 	}
+	if (cfg->deletion_date!=0){
+		ms_message("Registration failed for removed proxy config, ignored");
+		return;
+	}
 	if (details==NULL)
 		details=_("no response timeout");
 	
 		if (call->state!=LinphoneCallPaused){
 			ms_message("Automatically pausing current call to accept transfer.");
 			linphone_core_pause_call(lc,call);
+			call->was_automatically_paused=TRUE;
 		}
 		linphone_core_start_refered_call(lc,call);
-		sal_call_accept_refer(op);
 	}else if (lc->vtable.refer_received){
 		lc->vtable.refer_received(lc,referto);
-		sal_call_accept_refer(op);
 	}
 }
 
 		lc->vtable.notify_recv(lc,call,from,msg);
 }
 
-static void notify_presence(SalOp *op, SalSubscribeState ss, SalPresenceStatus status, const char *msg){
+static void notify_presence(SalOp *op, SalSubscribeStatus ss, SalPresenceStatus status, const char *msg){
 	LinphoneCore *lc=(LinphoneCore *)sal_get_user_pointer(sal_op_get_sal(op));
 	linphone_notify_recv(lc,op,ss,status);
 }
 	}
 }
 
+static void notify_refer(SalOp *op, SalReferStatus status){
+	LinphoneCore *lc=(LinphoneCore *)sal_get_user_pointer(sal_op_get_sal(op));
+	LinphoneCall *call=(LinphoneCall*) sal_op_get_user_pointer(op);
+	LinphoneCallState cstate;
+	if (call==NULL) {
+		ms_warning("Receiving notify_refer for unknown call.");
+		return ;
+	}
+	switch(status){
+		case SalReferTrying:
+			cstate=LinphoneCallOutgoingProgress;
+		break;
+		case SalReferSuccess:
+			cstate=LinphoneCallConnected;
+		break;
+		case SalReferFailed:
+			cstate=LinphoneCallError;
+		break;
+		default:
+			cstate=LinphoneCallError;
+	}
+	linphone_call_set_transfer_state(call, cstate);
+	if (cstate==LinphoneCallConnected){
+		/*automatically terminate the call as the transfer is complete.*/
+		linphone_core_terminate_call(lc,call);
+	}
+}
+
 SalCallbacks linphone_sal_callbacks={
 	call_received,
 	call_ringing,
 	text_received,
 	notify,
 	notify_presence,
+	notify_refer,
 	subscribe_received,
 	subscribe_closed,
 	ping_reply
 void linphone_chat_room_send_message(LinphoneChatRoom *cr, const char *msg){
 	const char *route=NULL;
 	const char *identity=linphone_core_find_best_identity(cr->lc,cr->peer_url,&route);
-	SalOp *op;
+	SalOp *op=NULL;
 	LinphoneCall *call;
-	if((call = linphone_core_get_call_by_remote_address(cr->lc,cr->peer))!=NULL)
-	{
-		ms_message("send SIP message into the call\n");
-		op = call->op;
+	if((call = linphone_core_get_call_by_remote_address(cr->lc,cr->peer))!=NULL){
+		if (call->state==LinphoneCallConnected ||
+		    call->state==LinphoneCallStreamsRunning ||
+		    call->state==LinphoneCallPaused ||
+		    call->state==LinphoneCallPausing ||
+		    call->state==LinphoneCallPausedByRemote){
+			ms_message("send SIP message through the existing call.");
+			op = call->op;
+		}
 	}
-	else
-	{
+	if (op==NULL){
+		/*sending out of calls*/
 		op = sal_op_new(cr->lc->sal);
 		sal_op_set_route(op,route);
 		if (cr->op!=NULL){

coreapi/help/doxygen.dox

  * video calls into any applications. All variants of linphone are directly based
  * on it:
  * - linphone (gtk interface)
- *
  * - linphonec (console interface)
+ * - linphone for iOS
+ * - linphone for Android
  *
  * Liblinphone is GPL (see COPYING file). Please understand the licencing details
  * before using it!

coreapi/linphonecall.c

 #include "mediastreamer2/msfileplayer.h"
 #include "mediastreamer2/msjpegwriter.h"
 #include "mediastreamer2/mseventqueue.h"
+#include "mediastreamer2/mssndcard.h"
 
 #ifdef VIDEO_ENABLED
 static MSWebCam *get_nowebcam_device(){
 	propagate_encryption_changed(call);
 }
 
-static MSList *make_codec_list(LinphoneCore *lc, const MSList *codecs, int bandwidth_limit){
+static MSList *make_codec_list(LinphoneCore *lc, const MSList *codecs, int bandwidth_limit,int* max_sample_rate){
 	MSList *l=NULL;
 	const MSList *it;
+	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){
 			}
 			if (linphone_core_check_payload_type_usability(lc,pt)){
 				l=ms_list_append(l,payload_type_clone(pt));
+				if (max_sample_rate && payload_type_get_rate(pt)>*max_sample_rate) *max_sample_rate=payload_type_get_rate(pt);
 			}
 		}
 	}
 	const char *username=linphone_address_get_username (addr);
 	SalMediaDescription *md=sal_media_description_new();
 
+
 	md->session_id=session_id;
 	md->session_ver=session_ver;
 	md->nstreams=1;
 		SalProtoRtpSavp : SalProtoRtpAvp;
 	md->streams[0].type=SalAudio;
 	md->streams[0].ptime=lc->net_conf.down_ptime;
-	l=make_codec_list(lc,lc->codecs_conf.audio_codecs,call->params.audio_bw);
+	l=make_codec_list(lc,lc->codecs_conf.audio_codecs,call->params.audio_bw,&md->streams[0].max_rate);
 	pt=payload_type_clone(rtp_profile_get_payload_from_mime(&av_profile,"telephone-event"));
 	l=ms_list_append(l,pt);
 	md->streams[0].payloads=l;
+	
 
 
 	if (call->params.has_video){
 		md->streams[1].port=call->video_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);
+		l=make_codec_list(lc,lc->codecs_conf.video_codecs,0,NULL);
 		md->streams[1].payloads=l;
 	}
 	
 	return md;
 }
 
-void update_local_media_description(LinphoneCore *lc, LinphoneCall *call, SalMediaDescription **md){
-	if (*md == NULL) {
-		*md = _create_local_media_description(lc,call,0,0);
+void update_local_media_description(LinphoneCore *lc, LinphoneCall *call){
+	SalMediaDescription *md=call->localdesc;
+	if (md== NULL) {
+		call->localdesc = create_local_media_description(lc,call);
 	} else {
-		unsigned int id = (*md)->session_id;
-		unsigned int ver = (*md)->session_ver+1;
-		sal_media_description_unref(*md);
-		*md = _create_local_media_description(lc,call,id,ver);
+		call->localdesc = _create_local_media_description(lc,call,md->session_id,md->session_ver+1);
+		sal_media_description_unref(md);
 	}
 }
 
 
 static void linphone_call_init_common(LinphoneCall *call, LinphoneAddress *from, LinphoneAddress *to){
 	int port_offset;
+	call->magic=linphone_call_magic;
 	call->refcnt=1;
 	call->state=LinphoneCallIdle;
+	call->transfer_state = LinphoneCallIdle;
 	call->start_time=time(NULL);
 	call->media_start_time=0;
 	call->log=linphone_call_log_new(call, from, to);
 	discover_mtu(lc,linphone_address_get_domain (to));
 	if (params->referer){
 		sal_call_set_referer(call->op,params->referer->op);
+		call->referer=linphone_call_ref(params->referer);
 	}
 	return call;
 }
 	linphone_core_get_local_ip(lc,linphone_address_get_domain(from),call->localip);
 	linphone_call_init_common(call, from, to);
 	linphone_core_init_default_params(lc, &call->params);
+	call->params.has_video &= !!lc->video_policy.automatically_accept;
 	call->localdesc=create_local_media_description (lc,call);
 	call->camera_active=call->params.has_video;
 	if (linphone_core_get_firewall_policy(call->core)==LinphonePolicyUseStun)
 		linphone_core_stop_dtmf(lc);
 		call->ringing_beep=FALSE;
 	}
+	if (call->referer){
+		linphone_call_unref(call->referer);
+		call->referer=NULL;
+	}
+}
+
+void linphone_call_fix_call_parameters(LinphoneCall *call){
+	call->params.has_video=call->current_params.has_video;
+	call->params.media_encryption=call->current_params.media_encryption;
 }
 
 const char *linphone_call_state_to_string(LinphoneCallState cs){
 	return &call->current_params;
 }
 
+static bool_t is_video_active(const SalStreamDescription *sd){
+	return sd->port!=0 && sd->dir!=SalStreamInactive;
+}
+
+/**
+ * Returns call parameters proposed by remote.
+ * 
+ * This is useful when receiving an incoming call, to know whether the remote party
+ * supports video, encryption or whatever.
+**/
+const LinphoneCallParams * linphone_call_get_remote_params(LinphoneCall *call){
+	LinphoneCallParams *cp=&call->remote_params;
+	memset(cp,0,sizeof(*cp));
+	if (call->op){
+		SalMediaDescription *md=sal_call_get_remote_media_description(call->op);
+		if (md){
+			SalStreamDescription *asd,*vsd,*secure_asd,*secure_vsd;
+
+			asd=sal_media_description_find_stream(md,SalProtoRtpAvp,SalAudio);
+			vsd=sal_media_description_find_stream(md,SalProtoRtpAvp,SalVideo);
+			secure_asd=sal_media_description_find_stream(md,SalProtoRtpSavp,SalAudio);
+			secure_vsd=sal_media_description_find_stream(md,SalProtoRtpSavp,SalVideo);
+			if (secure_vsd){
+				cp->has_video=is_video_active(secure_vsd);
+				if (secure_asd || asd==NULL)
+					cp->media_encryption=LinphoneMediaEncryptionSRTP;
+			}else if (vsd){
+				cp->has_video=is_video_active(vsd);
+			}
+			return cp;
+		}
+	}
+	return NULL;
+}
+
 /**
  * Returns the remote address associated to this call
  *
 
 #ifdef VIDEO_ENABLED
 static void video_stream_event_cb(void *user_pointer, const MSFilter *f, const unsigned int event_id, const void *args){
+    LinphoneCall* call = (LinphoneCall*) user_pointer;
 	ms_warning("In linphonecall.c: video_stream_event_cb");
 	switch (event_id) {
 		case MS_VIDEO_DECODER_DECODING_ERRORS:
 			ms_warning("Case is MS_VIDEO_DECODER_DECODING_ERRORS");
-			linphone_call_send_vfu_request((LinphoneCall*) user_pointer);
+			linphone_call_send_vfu_request(call);
 			break;
+        case MS_VIDEO_DECODER_FIRST_IMAGE_DECODED:
+            ms_message("First video frame decoded successfully");
+            if (call->nextVideoFrameDecoded._func != NULL)
+                call->nextVideoFrameDecoded._func(call, call->nextVideoFrameDecoded._user_data);
+            break;
 		default:
 			ms_warning("Unhandled event %i", event_id);
 			break;
 }
 #endif
 
+void linphone_call_set_next_video_frame_decoded_callback(LinphoneCall *call, LinphoneCallCbFunc cb, void* user_data) {
+    call->nextVideoFrameDecoded._func = cb;
+    call->nextVideoFrameDecoded._user_data = user_data;
+#ifdef VIDEO_ENABLED
+    ms_filter_call_method_noarg(call->videostream->decoder, MS_VIDEO_DECODER_RESET_FIRST_IMAGE_NOTIFICATION);
+#endif
+}
+
 void linphone_call_init_media_streams(LinphoneCall *call){
 	LinphoneCore *lc=call->core;
 	SalMediaDescription *md=call->localdesc;
 #ifdef VIDEO_ENABLED
 
 	if ((lc->video_conf.display || lc->video_conf.capture) && md->streams[1].port>0){
+		int video_recv_buf_size=lp_config_get_int(lc->config,"video","recv_buf_size",0);
 		call->videostream=video_stream_new(md->streams[1].port,linphone_core_ipv6_enabled(lc));
-	if( lc->video_conf.displaytype != NULL)
-		video_stream_set_display_filter_name(call->videostream,lc->video_conf.displaytype);
-	video_stream_set_event_callback(call->videostream,video_stream_event_cb, call);
-	if (lc->rtptf){
-		RtpTransport *vrtp=lc->rtptf->video_rtp_func(lc->rtptf->video_rtp_func_data, call->video_port);
-		RtpTransport *vrtcp=lc->rtptf->video_rtcp_func(lc->rtptf->video_rtcp_func_data, call->video_port+1);
-		rtp_session_set_transports(call->videostream->session,vrtp,vrtcp);
-	}
-	call->videostream_app_evq = ortp_ev_queue_new();
-	rtp_session_register_event_queue(call->videostream->session,call->videostream_app_evq);
+		video_stream_enable_display_filter_auto_rotate(call->videostream, lp_config_get_int(lc->config,"video","display_filter_auto_rotate",0));
+		if (video_recv_buf_size>0) rtp_session_set_recv_buf_size(call->videostream->session,video_recv_buf_size);
+          
+		if( lc->video_conf.displaytype != NULL)
+			video_stream_set_display_filter_name(call->videostream,lc->video_conf.displaytype);
+		video_stream_set_event_callback(call->videostream,video_stream_event_cb, call);
+		if (lc->rtptf){
+			RtpTransport *vrtp=lc->rtptf->video_rtp_func(lc->rtptf->video_rtp_func_data, call->video_port);
+			RtpTransport *vrtcp=lc->rtptf->video_rtcp_func(lc->rtptf->video_rtcp_func_data, call->video_port+1);
+			rtp_session_set_transports(call->videostream->session,vrtp,vrtcp);
+		}
+		call->videostream_app_evq = ortp_ev_queue_new();
+		rtp_session_register_event_queue(call->videostream->session,call->videostream_app_evq);
 #ifdef TEST_EXT_RENDERER
 		video_stream_set_render_callback(call->videostream,rendercb,NULL);
 #endif
 		(current==NULL || current==call);
 }
 static int find_crypto_index_from_tag(const SalSrtpCryptoAlgo crypto[],unsigned char tag) {
-	int i;
-	for(i=0; i<SAL_CRYPTO_ALGO_MAX; i++) {
-		if (crypto[i].tag == tag) {
-			return i;
-		}
-	}
-	return -1;
+    int i;
+    for(i=0; i<SAL_CRYPTO_ALGO_MAX; i++) {
+        if (crypto[i].tag == tag) {
+            return i;
+        }
+    }
+    return -1;
 }
 static void linphone_call_start_audio_stream(LinphoneCall *call, const char *cname, bool_t muted, bool_t send_ringbacktone, bool_t use_arc){
 	LinphoneCore *lc=call->core;
 				captcard=playcard=NULL;
 			}
 			use_ec=captcard==NULL ? FALSE : linphone_core_echo_cancellation_enabled(lc);
-
+			if (playcard &&  stream->max_rate>0) ms_snd_card_set_preferred_sample_rate(playcard, stream->max_rate);
+			if (captcard &&  stream->max_rate>0) ms_snd_card_set_preferred_sample_rate(captcard, stream->max_rate);
 			audio_stream_enable_adaptive_bitrate_control(call->audiostream,use_arc);
 			audio_stream_start_full(
 				call->audiostream,
 			}
 			audio_stream_set_rtcp_information(call->audiostream, cname, LINPHONE_RTCP_SDES_TOOL);
 			
+            /* valid local tags are > 0 */
 			if (stream->proto == SalProtoRtpSavp) {
-				const SalStreamDescription *local_st_desc=sal_media_description_find_stream(call->localdesc,
-	    					SalProtoRtpSavp,SalAudio);
-				audio_stream_enable_strp(
-					call->audiostream, 
-					stream->crypto[0].algo,
-					local_st_desc->crypto[find_crypto_index_from_tag(local_st_desc->crypto,stream->crypto[0].tag)].master_key,
-					stream->crypto[0].master_key);
-				call->audiostream_encrypted=TRUE;
+                const SalStreamDescription *local_st_desc=sal_media_description_find_stream(call->localdesc,
+                                                                                            SalProtoRtpSavp,SalAudio);
+                int crypto_idx = find_crypto_index_from_tag(local_st_desc->crypto, stream->crypto_local_tag);
+                
+                if (crypto_idx >= 0) {
+                    audio_stream_enable_strp(
+                                             call->audiostream, 
+                                             stream->crypto[0].algo,
+                                             local_st_desc->crypto[crypto_idx].master_key,
+                                             stream->crypto[0].master_key);
+                    call->audiostream_encrypted=TRUE;
+                } else {
+                    ms_warning("Failed to find local crypto algo with tag: %d", stream->crypto_local_tag);
+                    call->audiostream_encrypted=FALSE;
+                }
 			}else call->audiostream_encrypted=FALSE;
 			if (call->params.in_conference){
 				/*transform the graph to connect it to the conference filter */
 		video_preview_stop(lc->previewstream);
 		lc->previewstream=NULL;
 	}
-	call->current_params.has_video=FALSE;
+	
 	if (vstream!=NULL && vstream->dir!=SalStreamInactive && vstream->port!=0) {
 		const char *addr=vstream->addr[0]!='\0' ? vstream->addr : call->resultdesc->addr;
 		call->video_profile=make_profile(call,call->resultdesc,vstream,&used_pt);
 				cam=get_nowebcam_device();
 			}
 			if (!is_inactive){
+                call->log->video_enabled = TRUE;
 				video_stream_set_direction (call->videostream, dir);
 				ms_message("%s lc rotation:%d\n", __FUNCTION__, lc->device_rotation);
 				video_stream_set_device_rotation(call->videostream, lc->device_rotation);
 	}
 #endif
 	linphone_call_start_audio_stream(call,cname,all_inputs_muted,send_ringbacktone,use_arc);
+	call->current_params.has_video=FALSE;
 	if (call->videostream!=NULL) {
 		linphone_call_start_video_stream(call,cname,all_inputs_muted);
 	}
 	}else if (call->params.media_encryption==LinphoneMediaEncryptionSRTP){
 		call->current_params.media_encryption=linphone_call_are_all_streams_encrypted(call) ?
 			LinphoneMediaEncryptionSRTP : LinphoneMediaEncryptionNone;
-		/*also reflect the change if the "wished" params, in order to avoid to propose SAVP again
-		 * further in the call, for example during pause,resume, conferencing reINVITEs*/
-		call->params.media_encryption=call->current_params.media_encryption;
 	}
 
+	/*also reflect the change if the "wished" params, in order to avoid to propose SAVP or video again
+	 * further in the call, for example during pause,resume, conferencing reINVITEs*/
+	linphone_call_fix_call_parameters(call);
+
 	goto end;
 	end:
 		ms_free(cname);
 	call_logs_write_to_config_file(lc);
 }
 
+LinphoneCallState linphone_call_get_transfer_state(LinphoneCall *call) {
+	return call->transfer_state;
+}
+
+void linphone_call_set_transfer_state(LinphoneCall* call, LinphoneCallState state) {
+	if (state != call->transfer_state) {
+		LinphoneCore* lc = call->core;
+		call->transfer_state = state;
+		if (lc->vtable.transfer_state_changed)
+			lc->vtable.transfer_state_changed(lc, call, state);
+	}
+}
 

coreapi/linphonecore.c

 		lp_config_set_int(cfg,logsection,"duration",cl->duration);
 		if (cl->refkey) lp_config_set_string(cfg,logsection,"refkey",cl->refkey);
 		lp_config_set_float(cfg,logsection,"quality",cl->quality);
+        lp_config_set_int(cfg,logsection,"video_enabled", cl->video_enabled);
 	}
 	for(;i<lc->max_call_logs;++i){
 		snprintf(logsection,sizeof(logsection),"call_log_%i",i);
 			tmp=lp_config_get_string(cfg,logsection,"refkey",NULL);
 			if (tmp) cl->refkey=ms_strdup(tmp);
 			cl->quality=lp_config_get_float(cfg,logsection,"quality",-1);
+            cl->video_enabled=lp_config_get_int(cfg,logsection,"video_enabled",0);
 			lc->call_logs=ms_list_append(lc->call_logs,cl);
 		}else break;
 	}
 
 #define RANK_END 10000
 static const char *codec_pref_order[]={
+	"SILK",
 	"speex",
 	"iLBC",
 	"amr",
 	linphone_core_update_allocated_audio_bandwidth(lc);
 }
 
-static void video_config_read(LinphoneCore *lc){
-#ifdef VIDEO_ENABLED
-	int capture, display, self_view;
-#endif
-	const char *str;
-	int ndev;
-	const char **devices;
+static void build_video_devices_table(LinphoneCore *lc){
 	const MSList *elem;
 	int i;
-
+	int ndev;
+	const char **devices;
+	if (lc->video_conf.cams)
+		ms_free(lc->video_conf.cams);
 	/* retrieve all video devices */
 	elem=ms_web_cam_manager_get_list(ms_web_cam_manager_get());
 	ndev=ms_list_size(elem);
 	}
 	devices[ndev]=NULL;
 	lc->video_conf.cams=devices;
+}
+
+static void video_config_read(LinphoneCore *lc){
+#ifdef VIDEO_ENABLED
+	int capture, display, self_view;
+#endif
+	const char *str;	
+	LinphoneVideoPolicy vpol;
+
+	build_video_devices_table(lc);
 
 	str=lp_config_get_string(lc->config,"video","device",NULL);
 	if (str && str[0]==0) str=NULL;
 	capture=lp_config_get_int(lc->config,"video","capture",1);
 	display=lp_config_get_int(lc->config,"video","display",1);
 	self_view=lp_config_get_int(lc->config,"video","self_view",1);
+	vpol.automatically_initiate=lp_config_get_int(lc->config,"video","automatically_initiate",1);
+	vpol.automatically_accept=lp_config_get_int(lc->config,"video","automatically_accept",1);
 	lc->video_conf.displaytype=lp_config_get_string(lc->config,"video","displaytype",NULL);
 	if(lc->video_conf.displaytype)
 		ms_message("we are using a specific display:%s\n",lc->video_conf.displaytype);
 
 	linphone_core_enable_video(lc,capture,display);
 	linphone_core_enable_self_view(lc,self_view);
+	linphone_core_set_video_policy(lc,&vpol);
 #endif
 }
 
 	lc->payload_types=ms_list_append(lc->payload_types,pt);
 }
 
+static void linphone_core_handle_static_payloads(LinphoneCore *lc){
+	RtpProfile *prof=&av_profile;
+	int i;
+	for(i=0;i<128;++i){
+		PayloadType *pt=rtp_profile_get_payload(prof,i);
+		if (pt){
+			if (payload_type_get_number(pt)!=i){
+				linphone_core_assign_payload_type(lc,pt,i,NULL);
+			}
+		}
+	}
+}
+
 static void linphone_core_free_payload_types(LinphoneCore *lc){
 	ms_list_for_each(lc->payload_types,(void (*)(void*))payload_type_destroy);
 	ms_list_free(lc->payload_types);
 	/*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_amrwb,-1,"octet-align=1");
 	linphone_core_assign_payload_type(lc,&payload_type_lpc1015,-1,NULL);
 	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_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_handle_static_payloads(lc);
 	
 	ms_init();
 	/* create a mediastreamer2 event queue and set it as global */
 			/*start the call even if the OPTIONS reply did not arrive*/
 			linphone_core_start_invite(lc,call,NULL);
 		}
-		if (call->dir==LinphoneCallIncoming && call->state==LinphoneCallOutgoingRinging){
+		if (call->state==LinphoneCallIncomingReceived){
 			elapsed=curtime-call->start_time;
 			ms_message("incoming call ringing for %i seconds",elapsed);
 			if (elapsed>lc->sip_conf.inc_timeout){
 void linphone_core_start_refered_call(LinphoneCore *lc, LinphoneCall *call){
 	if (call->refer_pending){
 		LinphoneCallParams *cp=linphone_core_create_default_call_parameters(lc);
+		LinphoneCall *newcall;
+		cp->has_video &= !!lc->video_policy.automatically_initiate;
 		cp->referer=call;
 		ms_message("Starting new call to refered address %s",call->refer_to);
 		call->refer_pending=FALSE;
-		linphone_core_invite_with_params(lc,call->refer_to,cp);
+		newcall=linphone_core_invite_with_params(lc,call->refer_to,cp);
 		linphone_call_params_destroy(cp);
+		if (newcall) linphone_core_notify_refer_state(lc,call,newcall);
+	}
+}
+
+void linphone_core_notify_refer_state(LinphoneCore *lc, LinphoneCall *referer, LinphoneCall *newcall){
+	if (referer->op!=NULL){
+		sal_call_notify_refer_state(referer->op,newcall ? newcall->op : NULL);
 	}
 }
 
 LinphoneCall * linphone_core_invite(LinphoneCore *lc, const char *url){
 	LinphoneCall *call;
 	LinphoneCallParams *p=linphone_core_create_default_call_parameters (lc);
+	p->has_video &= !!lc->video_policy.automatically_initiate;
 	call=linphone_core_invite_with_params(lc,url,p);
 	linphone_call_params_destroy(p);
 	return call;
 **/
 LinphoneCall * linphone_core_invite_address(LinphoneCore *lc, const LinphoneAddress *addr){
 	LinphoneCall *call;
-	LinphoneCallParams *p=linphone_core_create_default_call_parameters (lc);
+	LinphoneCallParams *p=linphone_core_create_default_call_parameters(lc);
+	p->has_video &= !!lc->video_policy.automatically_initiate;
 	call=linphone_core_invite_address_with_params (lc,addr,p);
 	linphone_call_params_destroy(p);
 	return call;
 	sal_call_refer(call->op,real_url);
 	ms_free(real_url);
 	linphone_address_destroy(real_parsed_url);
+	linphone_call_set_transfer_state(call, LinphoneCallOutgoingInit);
 	return 0;
 }
 
  * close the call with us (the 'dest' call).
 **/
 int linphone_core_transfer_call_to_another(LinphoneCore *lc, LinphoneCall *call, LinphoneCall *dest){
-	return sal_call_refer_with_replaces (call->op,dest->op);
+	int result = sal_call_refer_with_replaces (call->op,dest->op);
+	linphone_call_set_transfer_state(call, LinphoneCallOutgoingInit);
+	return result;
 }
 
 bool_t linphone_core_inc_invite_pending(LinphoneCore*lc){
  * - changing the size of the transmitted video after calling linphone_core_set_preferred_video_size()
  *
  * In case no changes are requested through the LinphoneCallParams argument, then this argument can be omitted and set to NULL.
- *
+ * @param lc the core
+ * @param call the call to be updated
+ * @param params the new call parameters to use. (may be NULL)
  * @return 0 if successful, -1 otherwise.
 **/
 int linphone_core_update_call(LinphoneCore *lc, LinphoneCall *call, const LinphoneCallParams *params){
 	if (params!=NULL){
 		const char *subject;
 		call->params=*params;
-		update_local_media_description(lc,call,&call->localdesc);
-		call->camera_active=params->has_video;
-
+		call->camera_active=call->params.has_video;
+		update_local_media_description(lc,call);
+		
 		if (params->in_conference){
 			subject="Conference";
 		}else{
 	return err;
 }
 
+/**
+ * @ingroup call_control
+ * When receiving a #LinphoneCallUpdatedByRemote state notification, prevent LinphoneCore from performing an automatic answer.
+ * 
+ * When receiving a #LinphoneCallUpdatedByRemote state notification (ie an incoming reINVITE), the default behaviour of
+ * LinphoneCore is to automatically answer the reINIVTE with call parameters unchanged.
+ * However when for example when the remote party updated the call to propose a video stream, it can be useful
+ * to prompt the user before answering. This can be achieved by calling linphone_core_defer_call_update() during 
+ * the call state notifiacation, to deactivate the automatic answer that would just confirm the audio but reject the video.
+ * Then, when the user responds to dialog prompt, it becomes possible to call linphone_core_accept_call_update() to answer
+ * the reINVITE, with eventually video enabled in the LinphoneCallParams argument.
+ * 
+ * @Returns 0 if successful, -1 if the linphone_core_defer_call_update() was done outside a #LinphoneCallUpdatedByRemote notification, which is illegal.
+**/
+int linphone_core_defer_call_update(LinphoneCore *lc, LinphoneCall *call){
+	if (call->state==LinphoneCallUpdatedByRemote){
+		call->defer_update=TRUE;
+		return 0;
+	}
+	ms_error("linphone_core_defer_call_update() not done in state LinphoneCallUpdatedByRemote");
+	return -1;
+}
+
+/**
+ * @ingroup call_control
+ * Accept call modifications initiated by other end.
+ * 
+ * This call may be performed in response to a #LinphoneCallUpdatedByRemote state notification.
+ * When such notification arrives, the application can decide to call linphone_core_defer_update_call() so that it can
+ * have the time to prompt the user. linphone_call_get_remote_params() can be used to get information about the call parameters
+ * requested by the other party, such as whether a video stream is requested.
+ * 
+ * When the user accepts or refuse the change, linphone_core_accept_call_update() can be done to answer to the other party.
+ * If params is NULL, then the same call parameters established before the update request will continue to be used (no change).
+ * If params is not NULL, then the update will be accepted according to the parameters passed.
+ * Typical example is when a user accepts to start video, then params should indicate that video stream should be used 
+ * (see linphone_call_params_enable_video()).
+ * @param lc the linphone core object.
+ * @param call the LinphoneCall object
+ * @param params a LinphoneCallParams object describing the call parameters to accept.
+ * @Returns 0 if sucessful, -1 otherwise (actually when this function call is performed outside ot #LinphoneCallUpdatedByRemote state).
+**/
+int linphone_core_accept_call_update(LinphoneCore *lc, LinphoneCall *call, const LinphoneCallParams *params){
+	SalMediaDescription *md;
+	if (call->state!=LinphoneCallUpdatedByRemote){
+		ms_error("linphone_core_accept_update(): invalid state %s to call this function.",
+		         linphone_call_state_to_string(call->state));
+		return -1;
+	}
+	if (params==NULL){
+		call->params.has_video=lc->video_policy.automatically_accept;
+	}else
+		call->params=*params;
+
+	if (call->current_params.in_conference) {
+		ms_warning("Video isn't supported in conference");
+		call->params.has_video = FALSE;
+	}
+	call->camera_active=call->params.has_video;
+	update_local_media_description(lc,call);
+	sal_call_set_local_media_description(call->op,call->localdesc);
+	sal_call_accept(call->op);
+	md=sal_call_get_final_media_description(call->op);
+	if (md && !sal_media_description_empty(md))
+		linphone_core_update_streams (lc,call,md);
+	linphone_call_set_state(call,LinphoneCallStreamsRunning,"Connected (streams running)");
+	return 0;
+}
 
 /**
  * Accept an incoming call.
  * Basically the application is notified of incoming calls within the
  * call_state_changed callback of the #LinphoneCoreVTable structure, where it will receive
  * a LinphoneCallIncoming event with the associated LinphoneCall object.
+ * The application can later accept the call using this method.
+ * @param lc the LinphoneCore object
+ * @param call the LinphoneCall object representing the call to be answered.
+ *
+**/
+int linphone_core_accept_call(LinphoneCore *lc, LinphoneCall *call){
+	return linphone_core_accept_call_with_params(lc,call,NULL);
+}
+
+/**
+ * Accept an incoming call, with parameters.
+ *
+ * @ingroup call_control
+ * Basically the application is notified of incoming calls within the
+ * call_state_changed callback of the #LinphoneCoreVTable structure, where it will receive
+ * a LinphoneCallIncoming event with the associated LinphoneCall object.
  * The application can later accept the call using
  * this method.
  * @param lc the LinphoneCore object
  * @param call the LinphoneCall object representing the call to be answered.
+ * @param params the specific parameters for this call, for example whether video is accepted or not. Use NULL to use default parameters.
  *
 **/
-int linphone_core_accept_call(LinphoneCore *lc, LinphoneCall *call)
+int linphone_core_accept_call_with_params(LinphoneCore *lc, LinphoneCall *call, const LinphoneCallParams *params)
 {
 	LinphoneProxyConfig *cfg=NULL,*dest_proxy=NULL;
 	const char *contact=NULL;
 	if (call->audiostream==NULL)
 		linphone_call_init_media_streams(call);
 
+	if (params){
+		call->params=*params;
+		call->camera_active=call->params.has_video;
+		update_local_media_description(lc,call);
+		sal_call_set_local_media_description(call->op,call->localdesc);
+	}
+	
 	sal_call_accept(call->op);
 	if (lc->vtable.display_status!=NULL)
 		lc->vtable.display_status(lc,_("Connected."));
 		ms_warning("Cannot pause this call, it is not active.");
 		return -1;
 	}
-	update_local_media_description(lc,call,&call->localdesc);
+	update_local_media_description(lc,call);
 	if (sal_media_description_has_dir(call->resultdesc,SalStreamSendRecv)){
 		sal_media_description_set_dir(call->localdesc,SalStreamSendOnly);
 		subject="Call on hold";
 		ms_message("Resuming call %p",call);
 	}
 
-	// Stop playing music immediately. If remote side is a conference it
-	// prevents the participants to hear it while the 200OK comes back.
-	audio_stream_play(call->audiostream, NULL);
+	/* Stop playing music immediately. If remote side is a conference it
+	 prevents the participants to hear it while the 200OK comes back.*/
+	if (call->audiostream) audio_stream_play(call->audiostream, NULL);
 
-	update_local_media_description(lc,the_call,&call->localdesc);
+	update_local_media_description(lc,the_call);
 	sal_call_set_local_media_description(call->op,call->localdesc);
 	sal_media_description_set_dir(call->localdesc,SalStreamSendRecv);
 	if (call->params.in_conference && !call->current_params.in_conference) subject="Conference";
  * @param lc The LinphoneCore object
 **/
 const char**  linphone_core_get_sound_devices(LinphoneCore *lc){
-	build_sound_devices_table(lc);
 	return lc->sound_conf.cards;
 }
 
 	return lc->video_conf.cams;
 }
 
+/**
+ * Update detection of sound devices.
+ * 
+ * Use this function when the application is notified of USB plug events, so that
+ * list of available hardwares for sound playback and capture is updated.
+ **/
+void linphone_core_reload_sound_devices(LinphoneCore *lc){
+	const char *ringer,*playback,*capture;
+	ringer=linphone_core_get_ringer_device(lc);
+	playback=linphone_core_get_playback_device(lc);
+	capture=linphone_core_get_capture_device(lc);
+	ms_snd_card_manager_reload(ms_snd_card_manager_get());
+	build_sound_devices_table(lc);
+	linphone_core_set_ringer_device(lc,ringer);
+	linphone_core_set_playback_device(lc,playback);
+	linphone_core_set_capture_device(lc,capture);
+}
+
+/**
+ * Update detection of camera devices.
+ * 
+ * Use this function when the application is notified of USB plug events, so that
+ * list of available hardwares for video capture is updated.
+ **/
+void linphone_core_reload_video_devices(LinphoneCore *lc){
+	const char *devid;
+	devid=linphone_core_get_video_device(lc);
+	ms_web_cam_manager_reload(ms_web_cam_manager_get());
+	build_video_devices_table(lc);
+	linphone_core_set_video_device(lc,devid);
+}
+
 char linphone_core_get_sound_source(LinphoneCore *lc)
 {
 	return lc->sound_conf.source;
 		linphone_core_get_upload_bandwidth(lc));
 }
 
+bool_t linphone_core_video_supported(LinphoneCore *lc){
+#ifdef VIDEO_ENABLED
+	return TRUE;
+#else
+	return FALSE;
+#endif
+}
+
 /**
  * Returns TRUE if video is enabled, FALSE otherwise.
  * @ingroup media_parameters
 }
 
 /**
+ * Sets the default policy for video.
+ * This policy defines whether:
+ * - video shall be initiated by default for outgoing calls
+ * - video shall be accepter by default for incoming calls
+**/
+void linphone_core_set_video_policy(LinphoneCore *lc, const LinphoneVideoPolicy *policy){
+	lc->video_policy=*policy;
+	if (linphone_core_ready(lc)){
+		lp_config_set_int(lc->config,"video","automatically_initiate",policy->automatically_initiate);
+		lp_config_set_int(lc->config,"video","automatically_accept",policy->automatically_accept);
+	}
+}
+
+/**
+ * Get the default policy for video.
+ * See linphone_core_set_video_policy() for more details.
+**/
+const LinphoneVideoPolicy *linphone_core_get_video_policy(LinphoneCore *lc){
+	return &lc->video_policy;
+}
+
+/**
  * Controls video preview enablement.
  *
  * @ingroup media_parameters
 	return lc->data;
 }
 
+void linphone_core_set_user_data(LinphoneCore *lc, void *userdata){
+	lc->data=userdata;
+}
+
 int linphone_core_get_mtu(const LinphoneCore *lc){
 	return lc->net_conf.mtu;
 }
 }
 
 void linphone_core_stop_dtmf_stream(LinphoneCore* lc) {
-	if (lc->ringstream) ring_stop(lc->ringstream);
-	lc->ringstream=NULL;
+	if (lc->ringstream && lc->dmfs_playing_start_time!=0) {
+		ring_stop(lc->ringstream);
+		lc->ringstream=NULL;
+	}
 }
 
 int linphone_core_get_max_calls(LinphoneCore *lc) {
 }
 
 void linphone_core_init_default_params(LinphoneCore*lc, LinphoneCallParams *params) {
-	params->has_video=linphone_core_video_enabled(lc);
+	params->has_video=linphone_core_video_enabled(lc) && lc->video_policy.automatically_initiate;
 	params->media_encryption=linphone_core_get_media_encryption(lc);	
 	params->in_conference=FALSE;
 }

coreapi/linphonecore.h

 	rtp_stats_t local_stats;
 	rtp_stats_t remote_stats;
 	float quality;
+    int video_enabled;
 	struct _LinphoneCore *lc;
 } LinphoneCallLog;
 
 const char *linphone_reason_to_string(LinphoneReason err);
 
 /**
+ * Structure describing policy regarding video streams establishments.
+**/
+struct _LinphoneVideoPolicy{
+	bool_t automatically_initiate; /**<Whether video shall be automatically proposed for outgoing calls.*/ 
+	bool_t automatically_accept; /**<Whether video shall be automatically accepted for incoming calls*/
+	bool_t unused[2];
+};
+
+typedef struct _LinphoneVideoPolicy LinphoneVideoPolicy;
+
+/**
  * The LinphoneCall object represents a call issued or received by the LinphoneCore
 **/
 struct _LinphoneCall;
 typedef struct _LinphoneCall LinphoneCall;
+    
+/** Callback prototype */
+typedef void (*LinphoneCallCbFunc)(struct _LinphoneCall *call,void * user_data);
 
 /**
  * LinphoneCallState enum represents the different state a call can reach into.
 	LinphoneCallError, /**<The call encountered an error*/
 	LinphoneCallEnd, /**<The call ended normally*/
 	LinphoneCallPausedByRemote, /**<The call is paused by remote end*/
-	LinphoneCallUpdatedByRemote, /**<The call's parameters are updated, used for example when video is asked by remote */
+	LinphoneCallUpdatedByRemote, /**<The call's parameters change is requested by remote end, used for example when video is added by remote */
 	LinphoneCallIncomingEarlyMedia, /**<We are proposing early media to an incoming call */
 	LinphoneCallUpdated, /**<The remote accepted the call update initiated by us */
 	LinphoneCallReleased /**< The call object is no more retained by the core */
 LinphoneCall *linphone_call_get_replaced_call(LinphoneCall *call);
 int linphone_call_get_duration(const LinphoneCall *call);
 const LinphoneCallParams * linphone_call_get_current_params(const LinphoneCall *call);
+const LinphoneCallParams * linphone_call_get_remote_params(LinphoneCall *call);
 void linphone_call_enable_camera(LinphoneCall *lc, bool_t enabled);
 bool_t linphone_call_camera_enabled(const LinphoneCall *lc);
 int linphone_call_take_video_snapshot(LinphoneCall *call, const char *file);
 void linphone_call_send_vfu_request(LinphoneCall *call);
 void *linphone_call_get_user_pointer(LinphoneCall *call);
 void linphone_call_set_user_pointer(LinphoneCall *call, void *user_pointer);
+void linphone_call_set_next_video_frame_decoded_callback(LinphoneCall *call, LinphoneCallCbFunc cb, void* user_data);
+LinphoneCallState linphone_call_get_transfer_state(LinphoneCall *call);
+    
 /**
  * Enables or disable echo cancellation for this call
  * @param call
 typedef void (*ReferReceived)(struct _LinphoneCore *lc, const char *refer_to);
 /** Callback prototype */
 typedef void (*BuddyInfoUpdated)(struct _LinphoneCore *lc, LinphoneFriend *lf);
+/** Callback prototype for in progress transfers. The new_call_state is the state of the call resulting of the transfer, at the other party. */
+typedef void (*LinphoneTransferStateChanged)(struct _LinphoneCore *lc, LinphoneCall *transfered, LinphoneCallState new_call_state);
 
 /**
  * This structure holds all callbacks that the application should implement.
 	TextMessageReceived text_received; /**< A text message has been received */
 	DtmfReceived dtmf_received; /**< A dtmf has been received received */
 	ReferReceived refer_received; /**< An out of call refer was received */
+	CallEncryptionChangedCb call_encryption_changed; /**<Notifies on change in the encryption of call streams */
+    LinphoneTransferStateChanged transfer_state_changed; /**<Notifies when a transfer is in progress */
 	BuddyInfoUpdated buddy_info_updated; /**< a LinphoneFriend's BuddyInfo has changed*/
 	NotifyReceivedCb notify_recv; /**< Other notifications*/
 	DisplayStatusCb display_status; /**< Callback that notifies various events with human readable text.*/
 	DisplayMessageCb display_warning;/** Callback to display a warning to the user */
 	DisplayUrlCb display_url;
 	ShowInterfaceCb show; /**< Notifies the application that it should show up*/
-	CallEncryptionChangedCb call_encryption_changed; /**<Notifies on change in the encryption of call streams */
 } LinphoneCoreVTable;
 
 /**
 
 int linphone_core_accept_call(LinphoneCore *lc, LinphoneCall *call);
 
+int linphone_core_accept_call_with_params(LinphoneCore *lc, LinphoneCall *call, const LinphoneCallParams *params);
+
 int linphone_core_terminate_call(LinphoneCore *lc, LinphoneCall *call);
 
 int linphone_core_redirect_call(LinphoneCore *lc, LinphoneCall *call, const char *redirect_uri);
 
 int linphone_core_update_call(LinphoneCore *lc, LinphoneCall *call, const LinphoneCallParams *params);
 
+int linphone_core_defer_call_update(LinphoneCore *lc, LinphoneCall *call);
+
+int linphone_core_accept_call_update(LinphoneCore *lc, LinphoneCall *call, const LinphoneCallParams *params);
+
 LinphoneCallParams *linphone_core_create_default_call_parameters(LinphoneCore *lc);
 
 LinphoneCall *linphone_core_get_call_by_remote_address(LinphoneCore *lc, const char *remote_address);
 
 PayloadType* linphone_core_find_payload_type(LinphoneCore* lc, const char* type, int rate) ;
 
+int linphone_core_get_payload_type_number(LinphoneCore *lc, PayloadType *pt);
+
 const char *linphone_core_get_payload_type_description(LinphoneCore *lc, PayloadType *pt);
 
 bool_t linphone_core_check_payload_type_usability(LinphoneCore *lc, PayloadType *pt);
 /* sound functions */
 /* returns a null terminated static array of string describing the sound devices */
 const char**  linphone_core_get_sound_devices(LinphoneCore *lc);
+void linphone_core_reload_sound_devices(LinphoneCore *lc);
 bool_t linphone_core_sound_device_can_capture(LinphoneCore *lc, const char *device);
 bool_t linphone_core_sound_device_can_playback(LinphoneCore *lc, const char *device);
 int linphone_core_get_ring_level(LinphoneCore *lc);
 void linphone_core_clear_call_logs(LinphoneCore *lc);
 
 /* video support */
+bool_t linphone_core_video_supported(LinphoneCore *lc);
 void linphone_core_enable_video(LinphoneCore *lc, bool_t vcap_enabled, bool_t display_enabled);
 bool_t linphone_core_video_enabled(LinphoneCore *lc);
+void linphone_core_set_video_policy(LinphoneCore *lc, const LinphoneVideoPolicy *policy);
+const LinphoneVideoPolicy *linphone_core_get_video_policy(LinphoneCore *lc);
 
 typedef struct MSVideoSizeDef{
 	MSVideoSize vsize;
 
 
 /* returns a null terminated static array of string describing the webcams */
+void linphone_core_reload_video_devices(LinphoneCore *lc);
 const char**  linphone_core_get_video_devices(const LinphoneCore *lc);
 int linphone_core_set_video_device(LinphoneCore *lc, const char *id);
 const char *linphone_core_get_video_device(const LinphoneCore *lc);
 bool_t linphone_core_keep_alive_enabled(LinphoneCore* lc);
 
 void *linphone_core_get_user_data(LinphoneCore *lc);
+void linphone_core_set_user_data(LinphoneCore *lc, void *userdata);
 
 /* returns LpConfig object to read/write to the config file: usefull if you wish to extend
 the config file with your own sections */

coreapi/linphonecore_jni.cc

 	linphone_core_accept_call((LinphoneCore*)lc,(LinphoneCall*)call);
 }
 
+extern "C" void Java_org_linphone_core_LinphoneCoreImpl_acceptCallWithParams(JNIEnv *env,
+		jobject thiz,
+		jlong lc,
+		jlong call,
+		jlong params){
+	linphone_core_accept_call_with_params((LinphoneCore*)lc,(LinphoneCall*)call, (LinphoneCallParams*)params);
+}
+
+extern "C" void Java_org_linphone_core_LinphoneCoreImpl_acceptCallUpdate(JNIEnv *env,
+		jobject thiz,
+		jlong lc,
+		jlong call,
+		jlong params){
+	linphone_core_accept_call_update((LinphoneCore*)lc,(LinphoneCall*)call, (LinphoneCallParams*)params);
+}
+
+extern "C" void Java_org_linphone_core_LinphoneCoreImpl_deferCallUpdate(JNIEnv *env,
+		jobject thiz,
+		jlong lc,
+		jlong call){
+	linphone_core_defer_call_update((LinphoneCore*)lc,(LinphoneCall*)call);
+}
+
 extern "C" jlong Java_org_linphone_core_LinphoneCoreImpl_getCallLog(	JNIEnv*  env
 		,jobject  thiz
 		,jlong lc
 	return (jlong) linphone_core_create_default_call_parameters((LinphoneCore*)lc);
 }
 
+extern "C" jlong Java_org_linphone_core_LinphoneCallImpl_getRemoteParams(JNIEnv *env, jobject thiz, jlong lc){
+	return (jlong) linphone_call_params_copy(linphone_call_get_remote_params((LinphoneCall*)lc));
+}
+
 extern "C" jlong Java_org_linphone_core_LinphoneCallImpl_getCurrentParamsCopy(JNIEnv *env, jobject thiz, jlong lc){
 	return (jlong) linphone_call_params_copy(linphone_call_get_current_params((LinphoneCall*)lc));
 }
 #endif
 }
 
+extern "C" void Java_org_linphone_core_LinphoneCoreImpl_setUserAgent(JNIEnv *env,jobject thiz,jlong pCore, jstring name, jstring version){
+	const char* cname=env->GetStringUTFChars(name, NULL);
+	const char* cversion=env->GetStringUTFChars(version, NULL);
+	linphone_core_set_user_agent(cname,cversion);
+	env->ReleaseStringUTFChars(name, cname);
+	env->ReleaseStringUTFChars(version, cversion);
+}
+
 extern "C" jboolean Java_org_linphone_core_LinphoneCoreImpl_isTunnelAvailable(JNIEnv *env,jobject thiz){
 	return linphone_core_tunnel_available();
 }
+
+extern "C" void Java_org_linphone_core_LinphoneCoreImpl_setVideoPolicy(JNIEnv *env, jobject thiz, jlong lc, jboolean autoInitiate, jboolean autoAccept){
+	LinphoneVideoPolicy vpol;
+	vpol.automatically_initiate = autoInitiate;
+	vpol.automatically_accept = autoAccept;
+	linphone_core_set_video_policy((LinphoneCore *)lc, &vpol);
+}
+
+extern "C" void Java_org_linphone_core_LinphoneCoreImpl_setCpuCountNative(JNIEnv *env, jobject thiz, jint count) {
+	ms_set_cpu_count(count);
+}
 	return -1;
 }
 
+int linphone_core_get_payload_type_number(LinphoneCore *lc, PayloadType *pt){
+       return payload_type_get_number(pt);
+}
+
 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);
 	}
 }
 
+LinphoneCall * is_a_linphone_call(void *user_pointer){
+	LinphoneCall *call=(LinphoneCall*)user_pointer;
+	if (call==NULL) return NULL;
+	return call->magic==linphone_call_magic ? call : NULL;
+}
+
+LinphoneProxyConfig * is_a_linphone_proxy_config(void *user_pointer){
+	LinphoneProxyConfig *cfg=(LinphoneProxyConfig*)user_pointer;
+	if (cfg==NULL) return NULL;
+	return cfg->magic==linphone_proxy_config_magic ? cfg : NULL;
+}
+
+
 #ifdef HAVE_GETIFADDRS
 
 #include <ifaddrs.h>

coreapi/offeranswer.c

 }
 
 static bool_t match_crypto_algo(const SalSrtpCryptoAlgo* local, const SalSrtpCryptoAlgo* remote, 
-	SalSrtpCryptoAlgo* result, bool_t use_local_key) {
+	SalSrtpCryptoAlgo* result, unsigned int* choosen_local_tag, bool_t use_local_key) {
 	int i,j;
 	for(i=0; i<SAL_CRYPTO_ALGO_MAX; i++) {
 		if (remote[i].algo == 0)
 			break;
-			
+
+        /* Look for a local enabled crypto algo that matches one of the proposed by remote */
 		for(j=0; j<SAL_CRYPTO_ALGO_MAX; j++) {
 			if (remote[i].algo == local[j].algo) {
 				result->algo = remote[i].algo;
+            /* We're answering an SDP offer. Supply our master key, associated with the remote supplied tag */
 				if (use_local_key) {
 					strncpy(result->master_key, local[j].master_key, 41);
-					result->tag = local[j].tag;
-				} else {
-					strncpy(result->master_key, remote[i].master_key, 41);
 					result->tag = remote[i].tag;
+                    *choosen_local_tag = local[j].tag;
+				}
+				/* We received an answer to our SDP crypto proposal. Copy matching algo remote master key to result, and memorize local tag */
+            else {
+					strncpy(result->master_key, remote[i].master_key, 41);
+					result->tag = local[j].tag;
+                    *choosen_local_tag = local[j].tag;
 				}
 				result->master_key[40] = '\0';
 				return TRUE;
 	if (result->proto == SalProtoRtpSavp) {
 		/* verify crypto algo */
 		memset(result->crypto, 0, sizeof(result->crypto));
-		if (!match_crypto_algo(local_offer->crypto, remote_answer->crypto, &result->crypto[0], FALSE))
+		if (!match_crypto_algo(local_offer->crypto, remote_answer->crypto, &result->crypto[0], &result->crypto_local_tag, FALSE))
 			result->port = 0;
 	}
 }
 	if (result->proto == SalProtoRtpSavp) {
 		/* select crypto algo */
 		memset(result->crypto, 0, sizeof(result->crypto));
-		if (!match_crypto_algo(local_cap->crypto, remote_offer->crypto, &result->crypto[0], TRUE))
+		if (!match_crypto_algo(local_cap->crypto, remote_offer->crypto, &result->crypto[0], &result->crypto_local_tag, TRUE))
 			result->port = 0; 
 		
 	}
 int offer_answer_initiate_outgoing(const SalMediaDescription *local_offer,
 									const SalMediaDescription *remote_answer,
     							SalMediaDescription *result){
-    int i,j;
+    	int i,j;
     
 	const SalStreamDescription *ls,*rs;
-    for(i=0,j=0;i<local_offer->nstreams;++i){
+	for(i=0,j=0;i<local_offer->nstreams;++i){
 		ms_message("Processing for stream %i",i);
 		ls=&local_offer->streams[i];
 		rs=sal_media_description_find_stream((SalMediaDescription*)remote_answer,ls->proto,ls->type);
-    	if (rs) {
+	if (rs) {
 			initiate_outgoing(ls,rs,&result->streams[j]);
 			++j;
 		}
 		else ms_warning("No matching stream for %i",i);
-    }
+	}
 	result->nstreams=j;
 	result->bandwidth=remote_answer->bandwidth;
 	strcpy(result->addr,remote_answer->addr);
 int offer_answer_initiate_incoming(const SalMediaDescription *local_capabilities,
 						const SalMediaDescription *remote_offer,
     					SalMediaDescription *result, bool_t one_matching_codec){
-    int i;
-	const SalStreamDescription *ls,*rs;
+	int i;
+	const SalStreamDescription *ls=NULL,*rs;
 							
-    for(i=0;i<remote_offer->nstreams;++i){
+	for(i=0;i<remote_offer->nstreams;++i){
 		rs=&remote_offer->streams[i];
-		ms_message("Processing for stream %i",i);
-		
-		ls=sal_media_description_find_stream((SalMediaDescription*)local_capabilities,rs->proto,rs->type);
-		ms_message("remote proto: %s => %p", (rs->proto == SalProtoRtpAvp)?"AVP":"SAVP", ls);
-		/* if matching failed, and remote proposes Avp only, ask for local Savp streams */ 
-		if (!ls && rs->proto == SalProtoRtpAvp) {
-			ls=sal_media_description_find_stream((SalMediaDescription*)local_capabilities,SalProtoRtpSavp,rs->type);
-			ms_message("retry with AVP => %p", ls);
-		}
+		if (rs->proto!=SalProtoUnknown){
+			ls=sal_media_description_find_stream((SalMediaDescription*)local_capabilities,rs->proto,rs->type);
+			/* if matching failed, and remote proposes Avp only, ask for local Savp streams */ 
+			if (!ls && rs->proto == SalProtoRtpAvp) {
+				ls=sal_media_description_find_stream((SalMediaDescription*)local_capabilities,SalProtoRtpSavp,rs->type);
+			}
+		}else ms_warning("Unknown protocol for mline %i, declining",i);
 		if (ls){
-    		initiate_incoming(ls,rs,&result->streams[i],one_matching_codec);
+			initiate_incoming(ls,rs,&result->streams[i],one_matching_codec);
 		}
 		else {
 			/* create an inactive stream for the answer, as there where no matching stream a local capability */
 				strncpy(result->streams[i].typeother,rs->typeother,sizeof(rs->typeother)-1);
 			}
 		}
-    }
+	}
 	result->nstreams=i;
 	strcpy(result->username, local_capabilities->username);
 	strcpy(result->addr,local_capabilities->addr);

coreapi/presence.c

 	ms_free(tmp);
 }
 
-void linphone_notify_recv(LinphoneCore *lc, SalOp *op, SalSubscribeState ss, SalPresenceStatus sal_status){
+void linphone_notify_recv(LinphoneCore *lc, SalOp *op, SalSubscribeStatus ss, SalPresenceStatus sal_status){
 	char *tmp;
 	LinphoneFriend *lf;
 	LinphoneAddress *friend=NULL;

coreapi/private.h

 	bool_t pad;
 	
 };
+    
+typedef struct _CallCallbackObj
+{
+    LinphoneCallCbFunc _func;
+    void * _user_data;
+}CallCallbackObj;
+
+static const int linphone_call_magic=0x3343;
 
 struct _LinphoneCall
 {
+	int magic; /*used to distinguish from proxy config*/
 	struct _LinphoneCore *core;
 	SalMediaDescription *localdesc;
 	SalMediaDescription *resultdesc;
 	LinphoneCallDir dir;
+	LinphoneCall *referer; /*when this call is the result of a transfer, referer is set to the original call that caused the transfer*/
 	struct _RtpProfile *audio_profile;
 	struct _RtpProfile *video_profile;
 	struct _LinphoneCallLog *log;
 	time_t start_time; /*time at which the call was initiated*/
 	time_t media_start_time; /*time at which it was accepted, media streams established*/
 	LinphoneCallState	state;
+	LinphoneCallState transfer_state; /*idle if no transfer*/
 	LinphoneReason reason;
 	int refcnt;
 	void * user_pointer;
 	char *refer_to;
 	LinphoneCallParams params;
 	LinphoneCallParams current_params;
+	LinphoneCallParams remote_params;
 	int up_bw; /*upload bandwidth setting at the time the call is started. Used to detect if it changes during a call */
 	int audio_bw;	/*upload bandwidth used by audio */
 	bool_t refer_pending;
 	bool_t videostream_encrypted;
 	bool_t audiostream_encrypted;
 	bool_t auth_token_verified;
+	bool_t defer_update;
+	bool_t was_automatically_paused;
+    CallCallbackObj nextVideoFrameDecoded;
 };
 
 
 LinphoneCallLog * linphone_call_log_new(LinphoneCall *call, LinphoneAddress *local, LinphoneAddress * remote);
 void linphone_call_log_completed(LinphoneCall *call);
 void linphone_call_log_destroy(LinphoneCallLog *cl);
+void linphone_call_set_transfer_state(LinphoneCall* call, LinphoneCallState state);
 
 void linphone_auth_info_write_config(struct _LpConfig *config, LinphoneAuthInfo *obj, int pos);
 
 void linphone_process_authentication(LinphoneCore* lc, SalOp *op);
 void linphone_authentication_ok(LinphoneCore *lc, SalOp *op);
 void linphone_subscription_new(LinphoneCore *lc, SalOp *op, const char *from);
-void linphone_notify_recv(LinphoneCore *lc, SalOp *op, SalSubscribeState ss, SalPresenceStatus status);
+void linphone_notify_recv(LinphoneCore *lc, SalOp *op, SalSubscribeStatus ss, SalPresenceStatus status);
 void linphone_proxy_config_process_authentication_failure(LinphoneCore *lc, SalOp *op);
 
 void linphone_subscription_answered(LinphoneCore *lc, SalOp *op);
 void linphone_proxy_config_set_error(LinphoneProxyConfig *cfg, LinphoneReason error);
 bool_t linphone_core_rtcp_enabled(const LinphoneCore *lc);
 
+LinphoneCall * is_a_linphone_call(void *user_pointer);
+LinphoneProxyConfig * is_a_linphone_proxy_config(void *user_pointer);
+
+static const int linphone_proxy_config_magic=0x7979;
 
 struct _LinphoneProxyConfig
 {
+	int magic;
 	struct _LinphoneCore *lc;
 	char *reg_proxy;
 	char *reg_identity;
 	MSList *hooks;
 	LinphoneConference conf_ctx;
 	char* zrtp_secrets_cache;
+	LinphoneVideoPolicy video_policy;
 	bool_t use_files;
 	bool_t apply_nat_settings;
 	bool_t initial_subscribes_sent;
 	bool_t bl_refresh;
+	
 	bool_t preview_finished;
 	bool_t auto_net_state_mon;
 	bool_t network_reachable;
 	bool_t use_preview_window;
-	int device_rotation;
+	
 	bool_t ringstream_autorelease;
+	bool_t pad[3];
+	int device_rotation;
 	int max_calls;
 	LinphoneTunnel *tunnel;
 };
 void linphone_core_set_state(LinphoneCore *lc, LinphoneGlobalState gstate, const char *message);
 
 SalMediaDescription *create_local_media_description(LinphoneCore *lc, LinphoneCall *call);
-void update_local_media_description(LinphoneCore *lc, LinphoneCall *call, SalMediaDescription **md);
+void update_local_media_description(LinphoneCore *lc, LinphoneCall *call);
 
 void linphone_core_update_streams(LinphoneCore *lc, LinphoneCall *call, SalMediaDescription *new_md);
 
 void linphone_call_remove_from_conf(LinphoneCall *call);
 void linphone_core_conference_check_uninit(LinphoneCore *lc);
 bool_t linphone_core_sound_resources_available(LinphoneCore *lc);
+void linphone_core_notify_refer_state(LinphoneCore *lc, LinphoneCall *referer, LinphoneCall *newcall);
 
 void __linphone_core_invalidate_registers(LinphoneCore* lc);
 
 
 void linphone_proxy_config_init(LinphoneProxyConfig *obj){
 	memset(obj,0,sizeof(LinphoneProxyConfig));
+	obj->magic=linphone_proxy_config_magic;
 	obj->expires=3600;
 }
 
 	/* add to the list of destroyed proxies, so that the possible unREGISTER request can succeed authentication */
 	lc->sip_conf.deleted_proxies=ms_list_append(lc->sip_conf.deleted_proxies,(void *)cfg);
 	cfg->deletion_date=ms_time(NULL);
-	/* this will unREGISTER */
-	linphone_proxy_config_edit(cfg);
+	if (cfg->state==LinphoneRegistrationOk){
+		/* this will unREGISTER */
+		linphone_proxy_config_edit(cfg);
+	}
 	if (lc->default_proxy==cfg){
 		lc->default_proxy=NULL;
 	}
 	SalEndpointCandidate candidates[SAL_ENDPOINT_CANDIDATE_MAX];
 	SalStreamDir dir;
 	SalSrtpCryptoAlgo crypto[SAL_CRYPTO_ALGO_MAX];
+	unsigned int crypto_local_tag;
+	int max_rate;
 } SalStreamDescription;
 
 #define SAL_MEDIA_DESCRIPTION_MAX_STREAMS 4
 	SalPresenceAltService,
 }SalPresenceStatus;
 
-typedef enum SalSubscribeState{
+typedef enum SalReferStatus{
+	SalReferTrying,
+	SalReferSuccess,
+	SalReferFailed
+}SalReferStatus;
+
+typedef enum SalSubscribeStatus{
 	SalSubscribeActive,
 	SalSubscribeTerminated
-}SalSubscribeState;
+}SalSubscribeStatus;
 
 typedef void (*SalOnCallReceived)(SalOp *op);
 typedef void (*SalOnCallRinging)(SalOp *op);
 typedef void (*SalOnDtmfReceived)(SalOp *op, char dtmf);
 typedef void (*SalOnRefer)(Sal *sal, SalOp *op, const char *referto);
 typedef void (*SalOnTextReceived)(Sal *sal, const char *from, const char *msg);
-typedef void (*SalOnNotify)(SalOp *op, const char *from, const char *value);
-typedef void (*SalOnNotifyPresence)(SalOp *op, SalSubscribeState ss, SalPresenceStatus status, const char *msg);
+typedef void (*SalOnNotify)(SalOp *op, const char *from, const char *event);
+typedef void (*SalOnNotifyRefer)(SalOp *op, SalReferStatus state);
+typedef void (*SalOnNotifyPresence)(SalOp *op, SalSubscribeStatus ss, SalPresenceStatus status, const char *msg);
 typedef void (*SalOnSubscribeReceived)(SalOp *salop, const char *from);
 typedef void (*SalOnSubscribeClosed)(SalOp *salop, const char *from);
 typedef void (*SalOnPingReply)(SalOp *salop);
 	SalOnTextReceived text_received;
 	SalOnNotify notify;
 	SalOnNotifyPresence notify_presence;
+	SalOnNotifyRefer notify_refer;
 	SalOnSubscribeReceived subscribe_received;
 	SalOnSubscribeClosed subscribe_closed;
 	SalOnPingReply ping_reply;
 int sal_call_accept(SalOp*h);
 int sal_call_decline(SalOp *h, SalReason reason, const char *redirection /*optional*/);
 int sal_call_update(SalOp *h, const char *subject);
+SalMediaDescription * sal_call_get_remote_media_description(SalOp *h);
 SalMediaDescription * sal_call_get_final_media_description(SalOp *h);