diff --git a/coreapi/Makefile.am b/coreapi/Makefile.am
index 4835dc74cc30391fc2b9ae104aa97be6f361d243..8f88045f568a2c3993ffa01b6b705e5f03c1d3b8 100644
--- a/coreapi/Makefile.am
+++ b/coreapi/Makefile.am
@@ -35,7 +35,8 @@ liblinphone_la_SOURCES=\
 	linphonecall.c \
 	sipsetup.c sipsetup.h \
 	siplogin.c \
-	lsd.c linphonecore_utils.h
+	lsd.c linphonecore_utils.h \
+	ec-calibrator.c
 
 
 liblinphone_la_LDFLAGS= -version-info $(LIBLINPHONE_SO_VERSION) -no-undefined
@@ -49,7 +50,7 @@ if BUILD_WIN32
 liblinphone_la_LIBADD+=$(top_builddir)/oRTP/src/libortp.la
 endif
 
-noinst_PROGRAMS=test_lsd
+noinst_PROGRAMS=test_lsd test_ecc
 
 test_lsd_SOURCES=test_lsd.c
 
@@ -57,6 +58,14 @@ test_lsd_LDADD=liblinphone.la \
 							$(MEDIASTREAMER_LIBS) \
 							$(ORTP_LIBS)
 
+test_ecc_SOURCES=test_ecc.c
+
+test_ecc_LDADD=liblinphone.la \
+							$(MEDIASTREAMER_LIBS) \
+							$(ORTP_LIBS)
+
+
+
 AM_CFLAGS=$(STRICT_OPTIONS)  -DIN_LINPHONE \
 	$(ORTP_CFLAGS) \
 	$(OSIP_CFLAGS) \
diff --git a/coreapi/ec-calibrator.c b/coreapi/ec-calibrator.c
new file mode 100644
index 0000000000000000000000000000000000000000..5d0851add4801b582e1d404cf50c1b5b1e7a640b
--- /dev/null
+++ b/coreapi/ec-calibrator.c
@@ -0,0 +1,171 @@
+/*
+linphone
+Copyright (C) 2011 Belledonne Communications SARL
+Author: Simon MORLAT (simon.morlat@linphone.org)
+
+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, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+*/
+
+#include "private.h"
+
+#include "mediastreamer2/mstonedetector.h"
+#include "mediastreamer2/dtmfgen.h"
+
+
+
+
+
+static void ecc_init_filters(EcCalibrator *ecc){
+	ecc->ticker=ms_ticker_new();
+
+	ecc->sndread=ms_snd_card_create_reader(ecc->play_card);
+	ecc->det=ms_filter_new(MS_TONE_DETECTOR_ID);
+	ecc->rec=ms_filter_new(MS_FILE_REC_ID);
+
+	ms_filter_link(ecc->sndread,0,ecc->det,0);
+	ms_filter_link(ecc->det,0,ecc->rec,0);
+
+	ecc->play=ms_filter_new(MS_FILE_PLAYER_ID);
+	ecc->gen=ms_filter_new(MS_DTMF_GEN_ID);
+	ecc->sndwrite=ms_snd_card_create_writer(ecc->capt_card);
+
+	ms_filter_link(ecc->play,0,ecc->gen,0);
+	ms_filter_link(ecc->gen,0,ecc->sndwrite,0);
+
+	ms_ticker_attach(ecc->ticker,ecc->play);
+	ms_ticker_attach(ecc->ticker,ecc->sndread);
+}
+
+static void ecc_deinit_filters(EcCalibrator *ecc){
+	ms_ticker_detach(ecc->ticker,ecc->play);
+	ms_ticker_detach(ecc->ticker,ecc->sndread);
+
+	ms_filter_unlink(ecc->play,0,ecc->gen,0);
+	ms_filter_unlink(ecc->gen,0,ecc->sndwrite,0);
+
+	ms_filter_unlink(ecc->sndread,0,ecc->det,0);
+	ms_filter_unlink(ecc->det,0,ecc->rec,0);
+
+	ms_filter_destroy(ecc->sndread);
+	ms_filter_destroy(ecc->det);
+	ms_filter_destroy(ecc->rec);
+	ms_filter_destroy(ecc->play);
+	ms_filter_destroy(ecc->gen);
+	ms_filter_destroy(ecc->sndwrite);
+
+	ms_ticker_destroy(ecc->ticker);
+}
+
+static void on_tone_sent(void *data, MSFilter *f, unsigned int event_id, void *arg){
+	MSDtmfGenEvent *ev=(MSDtmfGenEvent*)arg;
+	EcCalibrator *ecc=(EcCalibrator*)data;
+	ecc->sent_count++;
+	ecc->acc-=ev->tone_start_time;
+	ms_message("Sent tone at %u",(unsigned int)ev->tone_start_time);
+}
+
+static void on_tone_received(void *data, MSFilter *f, unsigned int event_id, void *arg){
+	MSToneDetectorEvent *ev=(MSToneDetectorEvent*)arg;
+	EcCalibrator *ecc=(EcCalibrator*)data;
+	ecc->recv_count++;
+	ecc->acc+=ev->tone_start_time;
+	ms_message("Received tone at %u",(unsigned int)ev->tone_start_time);
+}
+
+static void ecc_play_tones(EcCalibrator *ecc){
+	MSDtmfGenCustomTone tone;
+	MSToneDetectorDef expected_tone;
+
+	
+	ms_filter_set_notify_callback(ecc->det,on_tone_received,ecc);
+
+	expected_tone.frequency=2000;
+	expected_tone.min_duration=40;
+	expected_tone.min_amplitude=0.02;
+
+	ms_filter_call_method (ecc->det,MS_TONE_DETECTOR_ADD_SCAN,&expected_tone);
+	
+	tone.frequency=1000;
+	tone.duration=1000;
+	tone.amplitude=1.0;
+
+	/*play an initial tone to startup the audio playback/capture*/
+	ms_filter_call_method(ecc->gen,MS_DTMF_GEN_PLAY_CUSTOM,&tone);
+	sleep(2);
+
+	ms_filter_set_notify_callback(ecc->gen,on_tone_sent,ecc);
+	tone.frequency=2000;
+	tone.duration=100;
+
+	ms_filter_call_method(ecc->gen,MS_DTMF_GEN_PLAY_CUSTOM,&tone);
+	sleep(1);
+	ms_filter_call_method(ecc->gen,MS_DTMF_GEN_PLAY_CUSTOM,&tone);
+	sleep(1);
+	ms_filter_call_method(ecc->gen,MS_DTMF_GEN_PLAY_CUSTOM,&tone);
+	sleep(1);
+
+	if (ecc->sent_count==3 && ecc->recv_count==3){
+		int delay=ecc->acc/3;
+		if (delay<0){
+			ms_error("Quite surprising calibration result, delay=%i",delay);
+			ecc->status=LinphoneEcCalibratorFailed;
+		}else{ms_message("Echo calibration estimated delay to be %i ms",delay);
+			ecc->delay=delay;
+			ecc->status=LinphoneEcCalibratorDone;
+		}
+	}else{
+		ms_error("Echo calibration failed, tones received = %i",ecc->recv_count);
+		ecc->status=LinphoneEcCalibratorFailed;
+	}
+	
+}
+
+static void  * ecc_thread(void *p){
+	EcCalibrator *ecc=(EcCalibrator*)p;
+	
+	ecc_init_filters(ecc);
+	ecc_play_tones(ecc);
+	ecc_deinit_filters(ecc);
+	return NULL;
+}
+
+EcCalibrator * ec_calibrator_new(MSSndCard *play_card, MSSndCard *capt_card, LinphoneEcCalibrationCallback cb, void *cb_data ){
+	EcCalibrator *ecc=ms_new0(EcCalibrator,1);
+
+	ecc->cb=cb;
+	ecc->cb_data=cb_data;
+	ecc->capt_card=capt_card;
+	ecc->play_card=play_card;
+	ms_thread_create(&ecc->thread,NULL,ecc_thread,ecc);
+	return ecc;
+}
+
+LinphoneEcCalibratorStatus ec_calibrator_get_status(EcCalibrator *ecc){
+	return ecc->status;
+}
+
+void ec_calibrator_destroy(EcCalibrator *ecc){
+	ms_thread_join(ecc->thread,NULL);
+	ms_free(ecc);
+}
+
+int linphone_core_start_echo_calibration(LinphoneCore *lc, LinphoneEcCalibrationCallback cb, void *cb_data){
+	if (lc->ecc!=NULL){
+		ms_error("Echo calibration is still on going !");
+		return -1;
+	}
+	lc->ecc=ec_calibrator_new(lc->sound_conf.play_sndcard,lc->sound_conf.capt_sndcard,cb,cb_data);
+	return 0;
+}
diff --git a/coreapi/linphonecore.c b/coreapi/linphonecore.c
index 7252a5490d4b6a3f0bd384554060e3353f545576..7861632cfa96564261e6f9ad961307a16273edc0 100644
--- a/coreapi/linphonecore.c
+++ b/coreapi/linphonecore.c
@@ -1635,6 +1635,19 @@ void linphone_core_iterate(LinphoneCore *lc){
 		one_second_elapsed=TRUE;
 	}
 
+	if (lc->ecc!=NULL){
+		LinphoneEcCalibratorStatus ecs=ec_calibrator_get_status(lc->ecc);
+		if (ecs!=LinphoneEcCalibratorInProgress){
+			if (lc->ecc->cb)
+				lc->ecc->cb(lc,ecs,lc->ecc->delay,lc->ecc->cb_data);
+			if (ecs==LinphoneEcCalibratorDone){
+				lp_config_set_int(lc->config, "sound", "ec_delay",MAX(lc->ecc->delay-10,0));
+			}
+			ec_calibrator_destroy(lc->ecc);
+			lc->ecc=NULL;
+		}
+	}
+
 	if (lc->preview_finished){
 		lc->preview_finished=0;
 		ring_stop(lc->ringstream);
diff --git a/coreapi/linphonecore_utils.h b/coreapi/linphonecore_utils.h
index 8c29ef1afe5883f7c7d6e434d6cda5e63ed43d88..b2ede96496cbd0186cc660ca4f0be41e8935caec 100644
--- a/coreapi/linphonecore_utils.h
+++ b/coreapi/linphonecore_utils.h
@@ -49,4 +49,21 @@ void linphone_sound_daemon_release_all_players(LinphoneSoundDaemon *obj);
 void linphone_core_use_sound_daemon(LinphoneCore *lc, LinphoneSoundDaemon *lsd);
 void linphone_sound_daemon_destroy(LinphoneSoundDaemon *obj);
 
+/**
+ * Enum describing the result of the echo canceller calibration process.
+**/
+typedef enum {
+	LinphoneEcCalibratorInProgress,
+	LinphoneEcCalibratorDone,
+	LinphoneEcCalibratorFailed
+}LinphoneEcCalibratorStatus;
+
+
+typedef void (*LinphoneEcCalibrationCallback)(LinphoneCore *lc, LinphoneEcCalibratorStatus status, int delay_ms, void *data);
+
+/**
+ * Start an echo calibration of the sound devices, in order to find adequate settings for the echo canceller automatically.
+**/
+int linphone_core_start_echo_calibration(LinphoneCore *lc, LinphoneEcCalibrationCallback cb, void *cb_data);
+
 #endif
diff --git a/coreapi/private.h b/coreapi/private.h
index 5759bbc19a67778a2e65a94516965f06a8ffec35..a960acbd8130adb94a28cb811fd5734d7a82a2d1 100644
--- a/coreapi/private.h
+++ b/coreapi/private.h
@@ -26,6 +26,7 @@
 #define _PRIVATE_H
 
 #include "linphonecore.h"
+#include "linphonecore_utils.h"
 #include "sal.h"
 
 #ifdef HAVE_CONFIG_H
@@ -424,6 +425,7 @@ struct _LinphoneCore
 	unsigned long video_window_id;
 	unsigned long preview_window_id;
 	time_t netup_time; /*time when network went reachable */
+	struct _EcCalibrator *ecc;
 	bool_t use_files;
 	bool_t apply_nat_settings;
 	bool_t initial_subscribes_sent;
@@ -451,6 +453,27 @@ bool_t linphone_core_is_payload_type_usable_for_bandwidth(LinphoneCore *lc, Payl
 #define linphone_core_ready(lc) ((lc)->state!=LinphoneGlobalStartup)
 void _linphone_core_configure_resolver();
 
+struct _EcCalibrator{
+	ms_thread_t thread;
+	MSSndCard *play_card,*capt_card;
+	MSFilter *sndread,*det,*rec;
+	MSFilter *play, *gen, *sndwrite;
+	MSTicker *ticker;
+	LinphoneEcCalibrationCallback cb;
+	void *cb_data;
+	int recv_count;
+	int sent_count;
+	int64_t acc;
+	int delay;
+	LinphoneEcCalibratorStatus status;
+};
+
+typedef struct _EcCalibrator EcCalibrator;
+
+LinphoneEcCalibratorStatus ec_calibrator_get_status(EcCalibrator *ecc);
+
+void ec_calibrator_destroy(EcCalibrator *ecc);
+
 #define HOLD_OFF	(0)
 #define HOLD_ON		(1)
 
diff --git a/coreapi/test_ecc.c b/coreapi/test_ecc.c
new file mode 100644
index 0000000000000000000000000000000000000000..7553c20189ad021056aaea880d0821176b11d68b
--- /dev/null
+++ b/coreapi/test_ecc.c
@@ -0,0 +1,46 @@
+/*
+linphone
+Copyright (C) 2011 Belledonne Communications SARL
+Author: Simon MORLAT (simon.morlat@linphone.org)
+
+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, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+*/
+
+
+#include "linphonecore.h"
+#include "linphonecore_utils.h"
+
+static void calibration_finished(LinphoneCore *lc, LinphoneEcCalibratorStatus status, int delay, void *data){
+	ms_message("echo calibration finished %s.",status==LinphoneEcCalibratorDone ? "successfully" : "with faillure");
+	if (status==LinphoneEcCalibratorDone) ms_message("Measured delay is %i",delay);
+}
+
+int main(int argc, char *argv[]){
+	int count=0;
+	LinphoneCoreVTable vtable={0};
+	LinphoneCore *lc=linphone_core_new(&vtable,NULL,NULL,NULL);
+	
+	linphone_core_enable_logs(NULL);
+
+	linphone_core_start_echo_calibration(lc,calibration_finished,NULL);
+	
+	while(count++<1000){
+		linphone_core_iterate(lc);
+		ms_usleep(10000);
+	}
+	linphone_core_destroy(lc);
+	return 0;
+}
+
diff --git a/mediastreamer2 b/mediastreamer2
index c5959fa4520005013cd14d2f773116f24f2eb7b7..434b627405d46c50cb109c001e44891551d0efab 160000
--- a/mediastreamer2
+++ b/mediastreamer2
@@ -1 +1 @@
-Subproject commit c5959fa4520005013cd14d2f773116f24f2eb7b7
+Subproject commit 434b627405d46c50cb109c001e44891551d0efab
diff --git a/oRTP b/oRTP
index c8b487f32fe225f8b1961754db9140eb282a0d28..37c60a638fd108404ca437e2bbef78f227178450 160000
--- a/oRTP
+++ b/oRTP
@@ -1 +1 @@
-Subproject commit c8b487f32fe225f8b1961754db9140eb282a0d28
+Subproject commit 37c60a638fd108404ca437e2bbef78f227178450