Index: .gitignore
===================================================================
--- .gitignore	(revision 957)
+++ .gitignore	(revision 957)
@@ -0,0 +1,29 @@
+INSTALL
+install-sh
+aclocal.m4
+*~
+configure
+Makefile.in
+Makefile
+build-aux
+m4
+autom4te.cache
+*.o
+*.lo
+*.la
+config.log
+config.guess
+config.sub
+config.status
+.deps
+.libs
+libtool
+ltmain.sh
+depcomp
+mediastreamer-config.h
+mediastreamer-config.h.in
+mediastreamer.pc
+mediastreamer2.spec
+missing
+stamp-h1
+
Index: NEWS
===================================================================
--- NEWS	(revision 856)
+++ NEWS	(revision 997)
@@ -1,5 +1,22 @@
-mediastreamer-2.3.2: ????????
+mediastreamer-2.5.1: ???
+
+mediastreamer-2.5.0: June 3, 2010
+	* fix regression with speex decoder at 16 and 32khz.
+	* uses less memory for speex decoding.
+	* added an event queue for notifications of MSFilters
+	* add stereo support to resampler
+	* add new MSFilter to convert from mono to stereo and vice versa
+	* add Inter Ticker Communication filter (ITC) so that graphs running
+		on different MSTicker can exchange data.
+	* add an audio mixer to mix down audio streams. This is not suitable
+		for conferencing, use MSConf instead.
+
+mediastreamer-2.4.0: May 19, 2010
 	* use libv4l2 when possible to benefit from hardware pixel conversion
 	* added jpeg over RTP support
+	* added PulseAudio support
+	* add a new MSDrawDibDisplay video output filter with new layout features.
+	* enhance performance of SDL video output
+	* improve MacOS sound support
 
 mediastreamer-2.3.1: October 5, 2009
Index: build/win32native/alldescs.h
===================================================================
--- build/win32native/alldescs.h	(revision 856)
+++ build/win32native/alldescs.h	(revision 993)
@@ -42,4 +42,9 @@
 extern MSFilterDesc ms_ice_desc;
 extern MSFilterDesc ms_equalizer_desc;
+extern MSFilterDesc ms_dd_display_desc;
+extern MSFilterDesc ms_itc_source_desc;
+extern MSFilterDesc ms_itc_sink_desc;
+extern MSFilterDesc ms_audio_mixer_desc;
+
 MSFilterDesc * ms_filter_descs[]={
 &ms_alaw_dec_desc,
@@ -86,4 +91,8 @@
 &ms_ice_desc,
 &ms_equalizer_desc,
+&ms_dd_display_desc,
+&ms_itc_source_desc,
+&ms_itc_sink_desc,
+&ms_audio_mixer_desc,
 NULL
 };
Index: build/win32native/mediastreamer2.vcproj
===================================================================
--- build/win32native/mediastreamer2.vcproj	(revision 856)
+++ build/win32native/mediastreamer2.vcproj	(revision 993)
@@ -199,8 +199,20 @@
 			</File>
 			<File
+				RelativePath="..\..\src\audiomixer.c"
+				>
+			</File>
+			<File
 				RelativePath="..\..\src\audiostream.c"
 				>
 			</File>
 			<File
+				RelativePath="..\..\src\chanadapt.c"
+				>
+			</File>
+			<File
+				RelativePath="..\..\src\drawdib-display.c"
+				>
+			</File>
+			<File
 				RelativePath="..\..\src\dsptools.c"
 				>
@@ -219,4 +231,8 @@
 			</File>
 			<File
+				RelativePath="..\..\src\eventqueue.c"
+				>
+			</File>
+			<File
 				RelativePath="..\..\src\gsm.c"
 				>
@@ -224,4 +240,8 @@
 			<File
 				RelativePath="..\..\src\ice.c"
+				>
+			</File>
+			<File
+				RelativePath="..\..\src\itc.c"
 				>
 			</File>
@@ -421,8 +441,20 @@
 			</File>
 			<File
+				RelativePath="..\..\include\mediastreamer2\msaudiomixer.h"
+				>
+			</File>
+			<File
+				RelativePath="..\..\include\mediastreamer2\mschanadapter.h"
+				>
+			</File>
+			<File
 				RelativePath="..\..\include\mediastreamer2\mscommon.h"
 				>
 			</File>
 			<File
+				RelativePath="..\..\include\mediastreamer2\mseventqueue.h"
+				>
+			</File>
+			<File
 				RelativePath="..\..\include\mediastreamer2\msfileplayer.h"
 				>
@@ -434,4 +466,12 @@
 			<File
 				RelativePath="..\..\include\mediastreamer2\msfilter.h"
+				>
+			</File>
+			<File
+				RelativePath="..\..\include\mediastreamer2\msinterfaces.h"
+				>
+			</File>
+			<File
+				RelativePath="..\..\include\mediastreamer2\msitc.h"
 				>
 			</File>
Index: configure.ac
===================================================================
--- configure.ac	(revision 933)
+++ configure.ac	(revision 997)
@@ -1,4 +1,4 @@
 dnl Process this file with autoconf to produce a configure script.
-AC_INIT([mediastreamer],[2.3.1])
+AC_INIT([mediastreamer],[2.5.0.1])
 
 AC_MSG_NOTICE([$PACKAGE_NAME-$PACKAGE_VERSION		A mediastreaming library for telephony application.])
@@ -408,4 +408,22 @@
 fi
 
+AC_ARG_ENABLE(pulseaudio,
+      [  --disable-pulseaudio    Disable pulseaudio support],
+      [case "${enableval}" in
+        yes) pulseaudio=true ;;
+        no)  pulseaudio=false ;;
+        *) AC_MSG_ERROR(bad value ${enableval} for --disable-pulseaudio) ;;
+      esac],[pulseaudio=true])
+
+
+if test x$pulseaudio = xtrue ; then
+	PKG_CHECK_MODULES(LIBPULSE, libpulse >= 0.9.21,
+		[AC_DEFINE(__PULSEAUDIO_ENABLED__,1,[Pulse audio support])],
+		[pulseaudio=false])
+fi
+
+AM_CONDITIONAL(BUILD_PULSEAUDIO,test x$pulseaudio = xtrue)
+
+
 if test "$found_sound" = "no"; then
 	AC_MSG_WARN([Could not find a support sound driver API])
@@ -580,5 +598,5 @@
 
 if test "$found_v4l" = "yes" && test "$have_libv4l2" != "yes" ; then
-	if test "$libv4l" = "yes" ; then
+	if test "$video" = "true" && test "$libv4l" = "yes" ; then
 		AC_MSG_ERROR(
 [
Index: include/mediastreamer2/Makefile.am
===================================================================
--- include/mediastreamer2/Makefile.am	(revision 856)
+++ include/mediastreamer2/Makefile.am	(revision 1002)
@@ -6,4 +6,5 @@
 				msqueue.h \
 				mscommon.h \
+				mseventqueue.h \
 				allfilters.h \
 				msticker.h \
@@ -24,5 +25,9 @@
 				dsptools.h \
 				msequalizer.h \
-				msspeexec.h
+				msinterfaces.h \
+				mschanadapter.h \
+				msaudiomixer.h \
+				msitc.h \
+				msextdisplay.h
 
 EXTRA_DIST=$(mediastreamer2_include_HEADERS)
Index: include/mediastreamer2/allfilters.h
===================================================================
--- include/mediastreamer2/allfilters.h	(revision 856)
+++ include/mediastreamer2/allfilters.h	(revision 997)
@@ -90,4 +90,12 @@
 	MS_JPEG_DEC_ID,
 	MS_JPEG_ENC_ID,
+	MS_PULSE_READ_ID,
+	MS_PULSE_WRITE_ID,
+	MS_DRAWDIB_DISPLAY_ID,
+	MS_CHANNEL_ADAPTER_ID,
+	MS_AUDIO_MIXER_ID,
+	MS_ITC_SINK_ID,
+	MS_ITC_SOURCE_ID,
+	MS_EXT_DISPLAY_ID
 } MSFilterId;
 
Index: include/mediastreamer2/mediastream.h
===================================================================
--- include/mediastreamer2/mediastream.h	(revision 856)
+++ include/mediastreamer2/mediastream.h	(revision 997)
@@ -130,4 +130,7 @@
 void audio_stream_set_mic_gain(AudioStream *stream, float gain);
 
+/* enable/disable rtp stream */ 
+void audio_stream_mute_rtp(AudioStream *stream, bool_t val);
+
 /*enable noise gate, must be done before start()*/
 void audio_stream_enable_noise_gate(AudioStream *stream, bool_t val);
@@ -151,4 +154,7 @@
 void audio_stream_set_default_card(int cardindex);
 
+/* retrieve RTP statistics*/
+void audio_stream_get_local_rtp_stats(AudioStream *stream, rtp_stats_t *stats);
+
 
 /*****************
@@ -156,4 +162,5 @@
  *****************/
 
+typedef void (*VideoStreamRenderCallback)(void *user_pointer, const MSPicture *local_view, const MSPicture *remote_view);
 
 struct _VideoStream
@@ -173,4 +180,6 @@
 	MSVideoSize sent_vsize;
 	int corner; /*for selfview*/
+	VideoStreamRenderCallback rendercb;
+	void *render_pointer;
 	bool_t adapt_bitrate;
 };
@@ -180,4 +189,5 @@
 VideoStream *video_stream_new(int locport, bool_t use_ipv6);
 void video_stream_enable_adaptive_bitrate_control(VideoStream *s, bool_t yesno);
+void video_stream_set_render_callback(VideoStream *s, VideoStreamRenderCallback cb, void *user_pointer);
 int video_stream_start(VideoStream * stream, RtpProfile *profile, const char *remip, int remport, int rem_rtcp_port,
 		int payload, int jitt_comp, MSWebCam *device);
Index: include/mediastreamer2/msaudiomixer.h
===================================================================
--- include/mediastreamer2/msaudiomixer.h	(revision 997)
+++ include/mediastreamer2/msaudiomixer.h	(revision 997)
@@ -0,0 +1,31 @@
+/*
+mediastreamer2 library - modular sound and video processing and streaming
+Copyright (C) 2010  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.
+*/
+#ifndef msaudiomixer_h
+#define msaudiomixer_h
+
+#include "mediastreamer2/msfilter.h"
+
+typedef struct MSAudioMixerCtl{
+	int pin;
+	float gain;
+} MSAudioMixerCtl;
+
+#define MS_AUDIO_MIXER_SET_INPUT_GAIN	MS_FILTER_METHOD(MS_AUDIO_MIXER_ID,0,MSAudioMixerCtl)
+
+#endif
Index: include/mediastreamer2/mschanadapter.h
===================================================================
--- include/mediastreamer2/mschanadapter.h	(revision 981)
+++ include/mediastreamer2/mschanadapter.h	(revision 981)
@@ -0,0 +1,27 @@
+/*
+mediastreamer2 library - modular sound and video processing and streaming
+Copyright (C) 2010 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.
+*/
+#ifndef mchanadapter_h
+#define mchanadapter_h
+
+#include "msfilter.h"
+
+#define MS_CHANNEL_ADAPTER_SET_OUTPUT_NCHANNELS	MS_FILTER_METHOD(MS_CHANNEL_ADAPTER_ID,0,int)
+#define MS_CHANNEL_ADAPTER_GET_OUTPUT_NCHANNELS	MS_FILTER_METHOD(MS_CHANNEL_ADAPTER_ID,1,int)
+
+#endif
Index: include/mediastreamer2/mscommon.h
===================================================================
--- include/mediastreamer2/mscommon.h	(revision 909)
+++ include/mediastreamer2/mscommon.h	(revision 982)
@@ -170,4 +170,6 @@
 void ms_sleep(int seconds);
 
+void ms_usleep(uint64_t usec);
+
 /**
  * The max payload size allowed.
Index: include/mediastreamer2/mseventqueue.h
===================================================================
--- include/mediastreamer2/mseventqueue.h	(revision 986)
+++ include/mediastreamer2/mseventqueue.h	(revision 986)
@@ -0,0 +1,57 @@
+/*
+mediastreamer2 library - modular sound and video processing and streaming
+Copyright (C) 2010  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.
+*/
+
+#ifndef mseventqueue_h
+#define mseventqueue_h
+
+
+typedef struct _MSEventQueue MSEventQueue;
+
+/**
+ * Creates an event queue to receive notifications from MSFilters.
+ *
+ * The queue can be installed to be global with ms_set_global_event_queue().
+ * The application can then schedule the callbacks for the events
+ * received by the queue by calling ms_event_queue_pump()
+**/ 
+MSEventQueue *ms_event_queue_new();
+
+/**
+ * Install a global event queue.
+ *
+ * All filters currently scheduled by MSTickers will send events (notifications)
+ * to the event queue.
+ *
+**/
+void ms_set_global_event_queue(MSEventQueue *q);
+
+/**
+ * Run callbacks associated to the events received.
+ * The user can register a notify callback per filter using
+ * ms_filter_set_notify_callback() in order to be informed 
+ * of various events generated by a MSFilter.
+**/
+void ms_event_queue_pump(MSEventQueue *q);
+
+/**
+ * Destroys an event queue.
+**/
+void ms_event_queue_destroy(MSEventQueue *q);
+
+#endif
Index: include/mediastreamer2/msextdisplay.h
===================================================================
--- include/mediastreamer2/msextdisplay.h	(revision 997)
+++ include/mediastreamer2/msextdisplay.h	(revision 997)
@@ -0,0 +1,33 @@
+/*
+mediastreamer2 library - modular sound and video processing and streaming
+Copyright (C) 2010  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.
+*/
+#ifndef msextdisplay_h
+#define msextdisplay_h
+
+#include "mediastreamer2/msfilter.h"
+#include "mediastreamer2/msvideo.h"
+
+typedef struct _MSExtDisplayOutput{
+	MSPicture remote_view;
+	MSPicture local_view;
+}MSExtDisplayOutput;
+
+/* a synchronous event generated by the filter when a new picture is to be drawn */
+#define MS_EXT_DISPLAY_ON_DRAW MS_FILTER_EVENT(MS_EXT_DISPLAY_ID,0,MSExtDisplayOutput)
+
+#endif
Index: include/mediastreamer2/msfilter.h
===================================================================
--- include/mediastreamer2/msfilter.h	(revision 856)
+++ include/mediastreamer2/msfilter.h	(revision 1002)
@@ -132,4 +132,5 @@
 	uint32_t last_tick;
 	bool_t seen;
+	bool_t synchronous_notifies;
 };
 
@@ -335,4 +336,7 @@
 /**
  * Set a callback on filter's to be informed of private filter's event.
+ * This callback is called from the filter's MSTicker, unless a global event queue
+ * is created to receive all filter's notification asynchronously.
+ * See ms_event_queue_new() for details.
  *
  * @param f        A MSFilter object.
@@ -340,7 +344,14 @@
  * @param userdata A pointer to private data.
  *
- * Returns: 0 if successfull, -1 otherwise.
+ *
  */
 void ms_filter_set_notify_callback(MSFilter *f, MSFilterNotifyFunc fn, void *userdata);
+
+/**
+ * Forces the filter to synchronously send notifications, that is
+ * the notify callback will be called from MSTicker thread instead of being
+ * run by a MSEventQueue.
+ */
+void ms_filter_enable_synchronous_notifcations(MSFilter *f, bool_t yesno);
 
 /**
@@ -461,4 +472,15 @@
 
 
+enum _MSFilterInterfaceId{
+	MSFilterInterfaceBegin=16384,
+	MSFilterPlayerInterface,
+	MSFilterRecorderInterface,
+	MSFilterVideoDisplayInterface,
+	MSFilterEchoCancellerInterface
+};
+
+typedef enum _MSFilterInterfaceId MSFilterInterfaceId;
+
+
 /* more specific methods: to be moved into implementation specific header files*/
 #define MS_FILTER_SET_FRAMESIZE 	MS_FILTER_BASE_METHOD(11,int)
@@ -481,9 +503,7 @@
 #define MS_CONF_CHANNEL_VOLUME	MS_FILTER_EVENT(MS_CONF_ID, 3, void*)
 
-#define MS_SPEEX_EC_PREPROCESS_MIC	MS_FILTER_EVENT(MS_SPEEX_EC_ID, 1, void*)
-#define MS_SPEEX_EC_ECHO_STATE	MS_FILTER_EVENT(MS_SPEEX_EC_ID, 2, void*)
 /** @} */
 
-/*private methods*/
+/*protected/ private methods*/
 void ms_filter_process(MSFilter *f);
 void ms_filter_preprocess(MSFilter *f, struct _MSTicker *t);
@@ -500,4 +520,6 @@
 #endif
 
+#include "msinterfaces.h"
+
 /* used by awk script in Makefile.am to generate alldescs.c */
 #define MS_FILTER_DESC_EXPORT(desc)
Index: include/mediastreamer2/msinterfaces.h
===================================================================
--- include/mediastreamer2/msinterfaces.h	(revision 1002)
+++ include/mediastreamer2/msinterfaces.h	(revision 1002)
@@ -0,0 +1,111 @@
+/*
+mediastreamer2 library - modular sound and video processing and streaming
+Copyright (C) 2010  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.
+*/
+
+#ifndef msinterfaces_h
+#define msinterfaces_h
+
+/**
+ * Interface definition for video display filters.
+**/
+
+/** whether the video window should be resized to the stream's resolution*/
+#define MS_VIDEO_DISPLAY_ENABLE_AUTOFIT \
+	MS_FILTER_METHOD(MSFilterVideoDisplayInterface,0,int)
+
+/**position of the local view */
+#define MS_VIDEO_DISPLAY_SET_LOCAL_VIEW_MODE \
+	MS_FILTER_METHOD(MSFilterVideoDisplayInterface,1,int)
+
+/**whether the video should be reversed as in mirror */
+#define MS_VIDEO_DISPLAY_ENABLE_MIRRORING \
+	MS_FILTER_METHOD(MSFilterVideoDisplayInterface,2,int)
+
+/**returns a platform dependant window id where the video is drawn */
+#define MS_VIDEO_DISPLAY_GET_NATIVE_WINDOW_ID \
+	MS_FILTER_METHOD(MSFilterVideoDisplayInterface,3,long)
+
+
+/**Sets an external native window id where the video is to be drawn */
+#define MS_VIDEO_DISPLAY_SET_NATIVE_WINDOW_ID \
+	MS_FILTER_METHOD(MSFilterVideoDisplayInterface,4,long)
+
+
+/**scale factor of the local view */
+#define MS_VIDEO_DISPLAY_SET_LOCAL_VIEW_SCALEFACTOR \
+	MS_FILTER_METHOD(MSFilterVideoDisplayInterface,5,float)
+
+/**scale factor of the local view */
+#define MS_VIDEO_DISPLAY_SET_SELFVIEW_POS \
+	MS_FILTER_METHOD(MSFilterVideoDisplayInterface,6,float[3])
+
+/**scale factor of the local view */
+#define MS_VIDEO_DISPLAY_GET_SELFVIEW_POS \
+	MS_FILTER_METHOD(MSFilterVideoDisplayInterface,7,float[3])
+
+/**scale factor of the local view */
+#define MS_VIDEO_DISPLAY_SET_BACKGROUND_COLOR \
+	MS_FILTER_METHOD(MSFilterVideoDisplayInterface,8,int[3])
+
+
+/**
+  * Interface definitions for players
+**/
+
+enum _MSPlayerState{
+	MSPlayerClosed,
+	MSPlayerPaused,
+	MSPlayerPlaying
+};
+
+typedef enum _MSPlayerState MSPlayerState;
+
+/**open a media file*/
+#define MS_PLAYER_OPEN \
+	MS_FILTER_METHOD(MSFilterPlayerInterface,0,const char *)
+
+#define MS_PLAYER_START \
+	MS_FILTER_METHOD_NO_ARG(MSFilterPlayerInterface,1)
+
+#define MS_PLAYER_PAUSE \
+	MS_FILTER_METHOD_NO_ARG(MSFilterPlayerInterface,2)
+
+#define MS_PLAYER_CLOSE \
+	MS_FILTER_METHOD_NO_ARG(MSFilterPlayerInterface,3)
+
+#define MS_PLAYER_SEEK_MS \
+	MS_FILTER_METHOD(MSFilterPlayerInterface,4,int)
+
+#define MS_PLAYER_GET_STATE \
+	MS_FILTER_METHOD(MSFilterPlayerInterface,5,int)
+
+
+/** Interface definitions for echo cancellers*/
+
+/** sets the echo delay in milliseconds*/
+#define MS_ECHO_CANCELLER_SET_DELAY \
+	MS_FILTER_METHOD(MSFilterEchoCancellerInterface,0,int)
+
+#define MS_ECHO_CANCELLER_SET_FRAMESIZE \
+	MS_FILTER_METHOD(MSFilterEchoCancellerInterface,1,int)
+
+/** sets tail length in milliseconds */
+#define MS_ECHO_CANCELLER_SET_TAIL_LENGTH \
+	MS_FILTER_METHOD(MSFilterEchoCancellerInterface,2,int)
+
+#endif
Index: include/mediastreamer2/msitc.h
===================================================================
--- include/mediastreamer2/msitc.h	(revision 986)
+++ include/mediastreamer2/msitc.h	(revision 986)
@@ -0,0 +1,28 @@
+/*
+mediastreamer2 library - modular sound and video processing and streaming
+Copyright (C) 2010  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.
+*/
+#ifndef msitc_h
+#define msitc_h
+
+#include "msfilter.h"
+
+#define MS_ITC_SINK_CONNECT MS_FILTER_METHOD(MS_ITC_SINK_ID,0,MSFilter)
+
+#define MS_ITC_SOURCE_UPDATED MS_FILTER_EVENT_NO_ARG(MS_ITC_SOURCE_ID,0)
+
+#endif
Index: include/mediastreamer2/msqueue.h
===================================================================
--- include/mediastreamer2/msqueue.h	(revision 856)
+++ include/mediastreamer2/msqueue.h	(revision 963)
@@ -50,4 +50,8 @@
 static inline mblk_t * ms_queue_peek_last(MSQueue *q){
 	return qlast(&q->q);
+}
+
+static inline void ms_queue_remove(MSQueue *q, mblk_t *m){
+	remq(&q->q,m);
 }
 
Index: nclude/mediastreamer2/msspeexec.h
===================================================================
--- include/mediastreamer2/msspeexec.h	(revision 856)
+++ 	(revision )
@@ -1,38 +1,0 @@
-/*
-mediastreamer2 library - modular sound and video processing and streaming
-Copyright (C) 2006-2009  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.
-
-msspeexec.h : interface of the speex echo canceler integration in mediastreamer2
-
-*/
-
-#ifndef msspeexec_h
-#define msspeexec_h
-
-#include <mediastreamer2/msfilter.h>
-
-/** sets the tail length in milliseconds*/
-#define MS_SPEEX_EC_SET_TAIL_LENGTH	MS_FILTER_METHOD(MS_SPEEX_EC_ID,0,int)
-
-/** sets the minimum delay of the echo if known. This optimizes the convergence*/
-#define MS_SPEEX_EC_SET_DELAY		MS_FILTER_METHOD(MS_SPEEX_EC_ID,1,int)
-
-/** sets the frame size for the AU-MDF algorithm, in number of fft points*/
-#define MS_SPEEX_EC_SET_FRAME_SIZE	MS_FILTER_METHOD(MS_SPEEX_EC_ID,2,int)
-
-
-#endif
Index: include/mediastreamer2/msvideo.h
===================================================================
--- include/mediastreamer2/msvideo.h	(revision 903)
+++ include/mediastreamer2/msvideo.h	(revision 969)
@@ -26,4 +26,6 @@
 #define MS_VIDEO_SIZE_SQCIF_W 128
 #define MS_VIDEO_SIZE_SQCIF_H 96
+#define MS_VIDEO_SIZE_WQCIF_W 256
+#define MS_VIDEO_SIZE_WQCIF_H 144
 #define MS_VIDEO_SIZE_QCIF_W 176
 #define MS_VIDEO_SIZE_QCIF_H 144
@@ -34,4 +36,6 @@
 #define MS_VIDEO_SIZE_4CIF_W 704
 #define MS_VIDEO_SIZE_4CIF_H 576
+#define MS_VIDEO_SIZE_W4CIF_W 1024
+#define MS_VIDEO_SIZE_W4CIF_H 576
 
 #define MS_VIDEO_SIZE_QQVGA_W 160
@@ -58,6 +62,10 @@
 #define MS_VIDEO_SIZE_288P_W 512
 #define MS_VIDEO_SIZE_288P_H 288
+#define MS_VIDEO_SIZE_432P_W 768
+#define MS_VIDEO_SIZE_432P_H 432
 #define MS_VIDEO_SIZE_448P_W 768
 #define MS_VIDEO_SIZE_448P_H 448
+#define MS_VIDEO_SIZE_480P_W 848
+#define MS_VIDEO_SIZE_480P_H 480
 #define MS_VIDEO_SIZE_576P_W 1024
 #define MS_VIDEO_SIZE_576P_H 576
@@ -150,4 +158,5 @@
 		uint8_t *dst_planes[], const int dst_strides[3], MSVideoSize roi);
 void ms_yuv_buf_mirror(YuvBuf *buf);
+void rgb24_mirror(uint8_t *buf, int w, int h, int linesize);
 void rgb24_revert(uint8_t *buf, int w, int h, int linesize);
 void rgb24_copy_revert(uint8_t *dstbuf, int dstlsz,
Index: include/mediastreamer2/msvolume.h
===================================================================
--- include/mediastreamer2/msvolume.h	(revision 923)
+++ include/mediastreamer2/msvolume.h	(revision 999)
@@ -65,4 +65,8 @@
 #define MS_VOLUME_GET_GAIN		MS_FILTER_METHOD(MS_VOLUME_ID,14,float)
 
+/* get the gain in db*/
+#define MS_VOLUME_GET_GAIN_DB		MS_FILTER_METHOD(MS_VOLUME_ID,15,float)
+
+
 extern MSFilterDesc ms_volume_desc;
 
Index: src/Makefile.am
===================================================================
--- src/Makefile.am	(revision 933)
+++ src/Makefile.am	(revision 997)
@@ -17,4 +17,5 @@
 				msqueue.c      \
 				msticker.c     \
+				eventqueue.c \
 				alaw.c 	       \
 				ulaw.c         \
@@ -37,5 +38,8 @@
 				kiss_fftr.c \
 				kiss_fftr.h \
-				equalizer.c
+				equalizer.c \
+				chanadapt.c \
+				audiomixer.c \
+				itc.c
 
 #dummy c++ file to force libtool to use c++ linking (because of msdscap-mingw.cc)
@@ -89,4 +93,8 @@
 endif
 
+if BUILD_PULSEAUDIO
+libmediastreamer_la_SOURCES+=pulseaudio.c
+endif
+
 
 if BUILD_VIDEO
@@ -101,5 +109,5 @@
 
 if BUILD_WIN32
-libmediastreamer_la_SOURCES+=msdscap-mingw.cc
+libmediastreamer_la_SOURCES+=msdscap-mingw.cc drawdib-display.c
 endif
 
@@ -121,5 +129,6 @@
 				msvideo.c \
 				rfc3984.c \
-				mire.c
+				mire.c \
+				extdisplay.c
 
 libmediastreamer_la_SOURCES+=videostream.c
@@ -141,4 +150,5 @@
 				$(ALSA_LIBS) \
 				$(ARTS_LIBS) \
+				$(LIBPULSE_LIBS) \
 				$(SPEEX_LIBS) \
 				$(GSM_LIBS) \
@@ -173,5 +183,6 @@
 		$(SPEEX_CFLAGS)  \
 		$(GSM_CFLAGS)    \
-		$(STRICT_OPTIONS)
+		$(STRICT_OPTIONS) \
+		$(LIBPULSE_CFLAGS)
 
 AM_CXXFLAGS= 	-I$(top_srcdir)  \
Index: src/alsa.c
===================================================================
--- src/alsa.c	(revision 914)
+++ src/alsa.c	(revision 992)
@@ -246,4 +246,6 @@
 {
 	snd_pcm_t *pcm_handle;
+
+	ms_message("alsa_open_r: opening %s at %iHz, bits=%i, stereo=%i",pcmdev,rate,bits,stereo);
 	
 	if (snd_pcm_open(&pcm_handle, pcmdev,SND_PCM_STREAM_PLAYBACK,SND_PCM_NONBLOCK) < 0) {
Index: src/audiomixer.c
===================================================================
--- src/audiomixer.c	(revision 996)
+++ src/audiomixer.c	(revision 996)
@@ -0,0 +1,226 @@
+/*
+mediastreamer2 library - modular sound and video processing and streaming
+Copyright (C) 2010  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 "mediastreamer2/msaudiomixer.h"
+#include "mediastreamer2/msticker.h"
+
+#ifdef _MSC_VER
+#include <malloc.h>
+#define alloca _alloca
+#endif
+
+#define MIXER_MAX_CHANNELS 20
+#define MAX_LATENCY 0.08
+#define ALWAYS_STREAMOUT 1
+
+typedef struct MixerState{
+	int nchannels;
+	int rate;
+	int purgeoffset;
+	int bytespertick;
+	MSBufferizer channels[MIXER_MAX_CHANNELS];
+	float gains[MIXER_MAX_CHANNELS];
+	int32_t *sum;
+} MixerState;
+
+static void mixer_init(MSFilter *f){
+	MixerState *s=ms_new0(MixerState,1);
+	int i;
+	
+	s->nchannels=1;
+	s->rate=44100;
+	for(i=0;i<MIXER_MAX_CHANNELS;++i){
+		ms_bufferizer_init(&s->channels[i]);
+		s->gains[i]=1;
+	}
+	f->data=s;
+}
+
+static void mixer_uninit(MSFilter *f){
+	int i;
+	MixerState *s=(MixerState *)f->data;
+	for(i=0;i<MIXER_MAX_CHANNELS;++i){
+		ms_bufferizer_uninit(&s->channels[i]);
+	}
+	ms_free(s);
+}
+
+static void mixer_preprocess(MSFilter *f){
+	MixerState *s=(MixerState *)f->data;
+	s->purgeoffset=(int)(MAX_LATENCY*(float)(2*s->nchannels*s->rate));
+	s->bytespertick=(2*s->nchannels*s->rate*f->ticker->interval)/1000;
+	s->sum=(int32_t*)ms_malloc0((s->bytespertick/2)*sizeof(int32_t));
+	/*ms_message("bytespertick=%i, purgeoffset=%i",s->bytespertick,s->purgeoffset);*/
+}
+
+static void mixer_postprocess(MSFilter *f){
+	MixerState *s=(MixerState *)f->data;
+	ms_free(s->sum);
+	s->sum=NULL;
+}
+
+static void accumulate(int32_t *sum, int16_t* contrib, int nwords){
+	int i;
+	for(i=0;i<nwords;++i){
+		sum[i]+=contrib[i];
+	}
+}
+
+static void accumulate_mpy(int32_t *sum, int16_t* contrib, int nwords, float gain){
+	int i;
+	for(i=0;i<nwords;++i){
+		sum[i]+=(int32_t)(gain*(float)contrib[i]);
+	}
+}
+
+static inline int16_t saturate(int32_t s){
+	if (s>32767) return 32767;
+	if (s<-32767) return -32767;
+	return (int16_t)s;
+}
+
+static mblk_t *make_output(int32_t *sum, int nwords){
+	mblk_t *om=allocb(nwords*2,0);
+	int i;
+	for(i=0;i<nwords;++i,om->b_wptr+=2){
+		*(int16_t*)om->b_wptr=saturate(sum[i]);
+	}
+	return om;
+}
+
+static void mixer_process(MSFilter *f){
+	MixerState *s=(MixerState *)f->data;
+	int i;
+	int nwords=s->bytespertick/2;
+	uint8_t *tmpbuf=(uint8_t *)alloca(s->bytespertick);
+	bool_t got_something=FALSE;
+
+	memset(s->sum,0,nwords*sizeof(int32_t));
+
+	for(i=0;i<MIXER_MAX_CHANNELS;++i){
+		MSQueue *q=f->inputs[i];
+		if (q){
+			ms_bufferizer_put_from_queue(&s->channels[i],q);
+			if (ms_bufferizer_get_avail(&s->channels[i])>=s->bytespertick){
+				ms_bufferizer_read(&s->channels[i],tmpbuf,s->bytespertick);
+				if (s->gains[i]==1)
+					accumulate(s->sum,(int16_t*)tmpbuf,nwords);
+				else
+					accumulate_mpy(s->sum,(int16_t*)tmpbuf,nwords,s->gains[i]);
+				got_something=TRUE;
+			}
+			if (ms_bufferizer_get_avail(&s->channels[i])>s->purgeoffset){
+				ms_warning("Too much data in channel %i",i);
+				ms_bufferizer_flush (&s->channels[i]);
+			}
+		}
+	}
+#ifdef ALWAYS_STREAMOUT
+	got_something=TRUE;
+#endif
+	
+	if (got_something){
+		ms_queue_put(f->outputs[0],make_output(s->sum,nwords));
+	}
+}
+
+static int mixer_set_rate(MSFilter *f, void *data){
+	MixerState *s=(MixerState *)f->data;
+	s->rate=*(int*)data;
+	return 0;
+}
+
+static int mixer_get_rate(MSFilter *f, void *data){
+	MixerState *s=(MixerState *)f->data;
+	*(int*)data=s->rate;
+	return 0;
+}
+
+static int mixer_set_nchannels(MSFilter *f, void *data){
+	MixerState *s=(MixerState *)f->data;
+	s->nchannels=*(int*)data;
+	return 0;
+}
+
+static int mixer_get_nchannels(MSFilter *f, void *data){
+	MixerState *s=(MixerState *)f->data;
+	*(int*)data=s->nchannels;
+	return 0;
+}
+
+static int mixer_set_input_gain(MSFilter *f, void *data){
+	MixerState *s=(MixerState *)f->data;
+	MSAudioMixerCtl *ctl=(MSAudioMixerCtl*)data;
+	if (ctl->pin<0 || ctl->pin>=MIXER_MAX_CHANNELS){
+		ms_warning("mixer_set_input_gain: invalid pin number %i",ctl->pin);
+		return -1;
+	}
+	s->gains[ctl->pin]=ctl->gain;
+	return 0;
+}
+
+static MSFilterMethod methods[]={
+	{	MS_FILTER_SET_NCHANNELS , mixer_set_nchannels },
+	{	MS_FILTER_GET_NCHANNELS , mixer_get_nchannels },
+	{	MS_FILTER_SET_SAMPLE_RATE, mixer_set_rate },
+	{	MS_FILTER_GET_SAMPLE_RATE, mixer_get_rate },
+	{	MS_AUDIO_MIXER_SET_INPUT_GAIN , mixer_set_input_gain },
+	{0,NULL}
+};
+
+#ifdef _MSC_VER
+
+MSFilterDesc ms_audio_mixer_desc={
+	MS_AUDIO_MIXER_ID,
+	"MSAudioMixer",
+	N_("A filter that mixes down 16 bit sample audio streams"),
+	MS_FILTER_OTHER,
+	NULL,
+	MIXER_MAX_CHANNELS,
+	1,
+	mixer_init,
+	mixer_preprocess,
+	mixer_process,
+	mixer_postprocess,
+	mixer_uninit,
+	methods,
+	MS_FILTER_IS_PUMP
+};
+
+#else
+
+MSFilterDesc ms_audio_mixer_desc={
+	.id=MS_AUDIO_MIXER_ID,
+	.name="MSAudioMixer",
+	.text=N_("A filter that mixes down 16 bit sample audio streams"),
+	.category=MS_FILTER_OTHER,
+	.ninputs=MIXER_MAX_CHANNELS,
+	.noutputs=1,
+	.init=mixer_init,
+	.preprocess=mixer_preprocess,
+	.process=mixer_process,
+	.postprocess=mixer_postprocess,
+	.uninit=mixer_uninit,
+	.methods=methods,
+	.flags=MS_FILTER_IS_PUMP
+};
+
+#endif
+
+MS_FILTER_DESC_EXPORT(ms_audio_mixer_desc)
Index: src/audiostream.c
===================================================================
--- src/audiostream.c	(revision 858)
+++ src/audiostream.c	(revision 1002)
@@ -251,9 +251,9 @@
 		ms_filter_call_method(stream->ec,MS_FILTER_SET_SAMPLE_RATE,&pt->clock_rate);
 		if (stream->ec_tail_len!=0)
-			ms_filter_call_method(stream->ec,MS_SPEEX_EC_SET_TAIL_LENGTH,&stream->ec_tail_len);
+			ms_filter_call_method(stream->ec,MS_ECHO_CANCELLER_SET_TAIL_LENGTH,&stream->ec_tail_len);
 		if (stream->ec_delay!=0)
-			ms_filter_call_method(stream->ec,MS_SPEEX_EC_SET_DELAY,&stream->ec_delay);
+			ms_filter_call_method(stream->ec,MS_ECHO_CANCELLER_SET_DELAY,&stream->ec_delay);
 		if (stream->ec_framesize!=0)
-			ms_filter_call_method(stream->ec,MS_SPEEX_EC_SET_FRAME_SIZE,&stream->ec_framesize);
+			ms_filter_call_method(stream->ec,MS_ECHO_CANCELLER_SET_FRAMESIZE,&stream->ec_framesize);
 	}
 
@@ -592,2 +592,20 @@
 	return 0;
 }
+
+void audio_stream_get_local_rtp_stats(AudioStream *stream, rtp_stats_t *lstats){
+	if (stream->session){
+		const rtp_stats_t *stats=rtp_session_get_stats(stream->session);
+		memcpy(lstats,stats,sizeof(*stats));
+	}else memset(lstats,0,sizeof(rtp_stats_t));
+}
+
+
+void audio_stream_mute_rtp(AudioStream *stream, bool_t val) 
+{
+  if (stream->rtpsend){
+    if (val)
+      ms_filter_call_method(stream->rtpsend,MS_RTP_SEND_MUTE_MIC,&val);
+    else
+      ms_filter_call_method(stream->rtpsend,MS_RTP_SEND_UNMUTE_MIC,&val);
+  }
+}
Index: src/chanadapt.c
===================================================================
--- src/chanadapt.c	(revision 981)
+++ src/chanadapt.c	(revision 981)
@@ -0,0 +1,139 @@
+/*
+mediastreamer2 library - modular sound and video processing and streaming
+Copyright (C) 2010  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 "mediastreamer2/msfilter.h"
+#include "mediastreamer2/mschanadapter.h"
+
+/*
+ This filter transforms stereo buffers to mono and vice versa.
+*/
+
+typedef struct AdapterState{
+	int inputchans;
+	int outputchans;
+}AdapterState;
+
+static void adapter_init(MSFilter *f){
+	AdapterState *s=ms_new(AdapterState,1);
+	s->inputchans=1;
+	s->outputchans=1;
+	f->data=s;
+}
+
+static void adapter_uninit(MSFilter *f){
+	ms_free(f->data);
+}
+
+static void adapter_process(MSFilter *f){
+	AdapterState *s=(AdapterState*)f->data;
+	mblk_t *im,*om;
+	int msgsize;
+	
+	while((im=ms_queue_get(f->inputs[0]))!=NULL){
+		if (s->inputchans==s->outputchans){
+			ms_queue_put(f->outputs[0],im);
+		}else if (s->inputchans==2){
+			msgsize=msgdsize(im)/2;
+			om=allocb(msgsize,0);
+			for (;im->b_rptr<im->b_wptr;im->b_rptr+=4,om->b_wptr+=2){
+				*(int16_t*)om->b_wptr=*(int16_t*)im->b_rptr;
+			}
+			ms_queue_put(f->outputs[0],om);
+			freemsg(im);
+		}else if (s->outputchans==2){
+			msgsize=msgdsize(im)*2;
+			om=allocb(msgsize,0);
+			for (;im->b_rptr<im->b_wptr;im->b_rptr+=2,om->b_wptr+=4){
+				((int16_t*)om->b_wptr)[0]=*(int16_t*)im->b_rptr;
+				((int16_t*)om->b_wptr)[1]=*(int16_t*)im->b_rptr;
+			}
+			ms_queue_put(f->outputs[0],om);
+			freemsg(im);
+		}
+	}
+}
+
+static int adapter_set_nchannels(MSFilter *f, void *data){
+	AdapterState *s=(AdapterState*)f->data;
+	s->inputchans=*(int*)data;
+	return 0;
+}
+
+static int adapter_get_nchannels(MSFilter *f, void *data){
+	AdapterState *s=(AdapterState*)f->data;
+	*(int*)data=s->inputchans;
+	return 0;
+}
+
+static int adapter_set_out_nchannels(MSFilter *f, void *data){
+	AdapterState *s=(AdapterState*)f->data;
+	s->outputchans=*(int*)data;
+	return 0;
+}
+
+static int adapter_get_out_nchannels(MSFilter *f, void *data){
+	AdapterState *s=(AdapterState*)f->data;
+	*(int*)data=s->outputchans;
+	return 0;
+}
+
+static MSFilterMethod methods[]={
+	{	MS_FILTER_SET_NCHANNELS , adapter_set_nchannels },
+	{	MS_FILTER_GET_NCHANNELS, adapter_get_nchannels },
+	{	MS_CHANNEL_ADAPTER_SET_OUTPUT_NCHANNELS, adapter_set_out_nchannels },
+	{  MS_CHANNEL_ADAPTER_GET_OUTPUT_NCHANNELS, adapter_get_out_nchannels },
+	{ 0,	NULL }
+};
+
+
+#ifdef _MSC_VER
+
+MSFilterDesc ms_channel_adapter_desc={
+	MS_CHANNEL_ADAPTER_ID,
+	"MSChannelAdapter",
+	N_("A filter that converts from mono to stereo and vice versa."),
+	MS_FILTER_OTHER,
+	NULL,
+	1,
+	1,
+	adapter_init,
+	NULL,
+	adapter_process,
+	NULL,
+	adapter_uninit,
+	methods
+};
+
+#else
+
+MSFilterDesc ms_channel_adapter_desc={
+	.id=MS_CHANNEL_ADAPTER_ID,
+	.name="MSChannelAdapter",
+	.text=N_("A filter that converts from mono to stereo and vice versa."),
+	.category=MS_FILTER_OTHER,
+	.ninputs=1,
+	.noutputs=1,
+	.init=adapter_init,
+	.process=adapter_process,
+	.uninit=adapter_uninit,
+	.methods=methods
+};
+#endif
+
+MS_FILTER_DESC_EXPORT(ms_channel_adapter_desc)
Index: src/drawdib-display.c
===================================================================
--- src/drawdib-display.c	(revision 1000)
+++ src/drawdib-display.c	(revision 1000)
@@ -0,0 +1,708 @@
+/*
+mediastreamer2 library - modular sound and video processing and streaming
+Copyright (C) 2006  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.
+*/
+
+#ifdef HAVE_CONFIG_H
+#include "mediastreamer-config.h"
+#endif
+
+#include "mediastreamer2/msfilter.h"
+#include "mediastreamer2/msvideo.h"
+
+#include "ffmpeg-priv.h"
+
+#define SCALE_FACTOR 4.0f
+#define SELVIEW_POS_INACTIVE -100.0
+#include <Vfw.h>
+
+typedef struct Yuv2RgbCtx{
+	uint8_t *rgb;
+	size_t rgblen;
+	MSVideoSize dsize;
+	MSVideoSize ssize;
+	struct ms_SwsContext *sws;
+}Yuv2RgbCtx;
+
+static void yuv2rgb_init(Yuv2RgbCtx *ctx){
+	ctx->rgb=NULL;
+	ctx->rgblen=0;
+	ctx->dsize.width=0;
+	ctx->dsize.height=0;
+	ctx->ssize.width=0;
+	ctx->ssize.height=0;
+	ctx->sws=NULL;
+}
+
+static void yuv2rgb_uninit(Yuv2RgbCtx *ctx){
+	if (ctx->rgb){
+		ms_free(ctx->rgb);
+		ctx->rgb=NULL;
+		ctx->rgblen=0;
+	}
+	if (ctx->sws){
+		ms_sws_freeContext(ctx->sws);
+		ctx->sws=NULL;
+	}
+	ctx->dsize.width=0;
+	ctx->dsize.height=0;
+	ctx->ssize.width=0;
+	ctx->ssize.height=0;
+}
+
+static void yuv2rgb_prepare(Yuv2RgbCtx *ctx, MSVideoSize src, MSVideoSize dst){
+	if (ctx->sws!=NULL) yuv2rgb_uninit(ctx);
+	ctx->sws=ms_sws_getContext(src.width,src.height,PIX_FMT_YUV420P,
+			dst.width,dst.height, PIX_FMT_BGR24,
+			SWS_FAST_BILINEAR, NULL, NULL, NULL);
+	ctx->dsize=dst;
+	ctx->ssize=src;
+	ctx->rgblen=dst.width*dst.height*3;
+	ctx->rgb=(uint8_t*)ms_malloc0(ctx->rgblen+dst.width);
+}
+
+
+/*
+ this function resizes the original pictures to the destination size and converts to rgb.
+ It takes care of reallocating a new SwsContext and rgb buffer if the source/destination sizes have 
+ changed.
+*/
+static void yuv2rgb_process(Yuv2RgbCtx *ctx, MSPicture *src, MSVideoSize dstsize, bool_t mirroring){
+	MSVideoSize srcsize;
+	
+	srcsize.width=src->w;
+	srcsize.height=src->h;
+	if (!ms_video_size_equal(dstsize,ctx->dsize) || !ms_video_size_equal(srcsize,ctx->ssize)){	
+		yuv2rgb_prepare(ctx,srcsize,dstsize);
+	}
+	{
+		int rgb_stride=-dstsize.width*3;
+		uint8_t *p;
+
+		p=ctx->rgb+(dstsize.width*3*(dstsize.height-1));
+		if (ms_sws_scale(ctx->sws,src->planes,src->strides, 0,
+           				src->h, &p, &rgb_stride)<0){
+			ms_error("Error in 420->rgb ms_sws_scale().");
+		}
+		if (mirroring) rgb24_mirror(ctx->rgb,dstsize.width,dstsize.height,dstsize.width*3);
+	}
+}
+
+static void yuv2rgb_draw(Yuv2RgbCtx *ctx, HDRAWDIB ddh, HDC hdc, int dstx, int dsty){
+	if (ctx->rgb){
+		BITMAPINFOHEADER bi;
+		memset(&bi,0,sizeof(bi));
+		bi.biSize=sizeof(bi);
+		bi.biWidth=ctx->dsize.width;
+		bi.biHeight=ctx->dsize.height;
+		bi.biPlanes=1;
+		bi.biBitCount=24;
+		bi.biCompression=BI_RGB;
+		bi.biSizeImage=ctx->rgblen;
+
+		DrawDibDraw(ddh,hdc,dstx,dsty,-1,-1,&bi,ctx->rgb,
+			0,0,ctx->dsize.width,ctx->dsize.height,0);
+	}
+}
+
+typedef struct _DDDisplay{
+	HWND window;
+	HDRAWDIB ddh;
+	MSVideoSize wsize; /*the initial requested window size*/
+	MSVideoSize vsize; /*the video size received for main input*/
+	MSVideoSize lsize; /*the video size received for local display */
+	Yuv2RgbCtx mainview;
+	Yuv2RgbCtx locview;
+	int sv_corner;
+	float sv_scalefactor;
+	float sv_posx,sv_posy;
+	int background_color[3];
+	bool_t need_repaint;
+	bool_t autofit;
+	bool_t mirroring;
+	bool_t own_window;
+}DDDisplay;
+
+static LRESULT CALLBACK window_proc(
+    HWND hwnd,        // handle to window
+    UINT uMsg,        // message identifier
+    WPARAM wParam,    // first message parameter
+    LPARAM lParam)    // second message parameter
+{
+	DDDisplay *wd=(DDDisplay*)GetWindowLongPtr(hwnd,GWLP_USERDATA);
+	switch(uMsg){
+		case WM_DESTROY:
+			if (wd){
+				wd->window=NULL;
+			}
+		break;
+		case WM_SIZE:
+			if (wParam==SIZE_RESTORED){
+				int h=(lParam>>16) & 0xffff;
+				int w=lParam & 0xffff;
+				
+				ms_message("Resized to %i,%i",w,h);
+				
+				if (wd!=NULL){
+					wd->need_repaint=TRUE;
+					//wd->window_size.width=w;
+					//wd->window_size.height=h;
+				}else{
+					ms_error("Could not retrieve DDDisplay from window !");
+				}
+			}
+		break;
+		case WM_PAINT:
+			if (wd!=NULL){
+				wd->need_repaint=TRUE;
+			}
+		default:
+			return DefWindowProc(hwnd, uMsg, wParam, lParam);
+	}
+	return 0;
+}
+
+static HWND create_window(int w, int h)
+{
+	WNDCLASS wc;
+	HINSTANCE hInstance = GetModuleHandle(NULL);
+	HWND hwnd;
+	RECT rect;
+	wc.style = 0 ;
+	wc.lpfnWndProc = window_proc;
+	wc.cbClsExtra = 0;
+	wc.cbWndExtra = 0;
+	wc.hInstance = NULL;
+	wc.hIcon = NULL;
+	wc.hCursor = LoadCursor(hInstance, IDC_ARROW);
+	wc.hbrBackground = NULL;
+	wc.lpszMenuName =  NULL;
+	wc.lpszClassName = "Video Window";
+	
+	if(!RegisterClass(&wc))
+	{
+		/* already registred! */
+	}
+	rect.left=100;
+	rect.top=100;
+	rect.right=rect.left+w;
+	rect.bottom=rect.top+h;
+	if (!AdjustWindowRect(&rect,WS_OVERLAPPEDWINDOW|WS_VISIBLE /*WS_CAPTION WS_TILED|WS_BORDER*/,FALSE)){
+		ms_error("AdjustWindowRect failed.");
+	}
+	ms_message("AdjustWindowRect: %li,%li %li,%li",rect.left,rect.top,rect.right,rect.bottom);
+	hwnd=CreateWindow("Video Window", "Video window", 
+		WS_OVERLAPPEDWINDOW /*WS_THICKFRAME*/ | WS_VISIBLE ,
+		CW_USEDEFAULT, CW_USEDEFAULT, rect.right-rect.left,rect.bottom-rect.top,
+													NULL, NULL, hInstance, NULL);
+	if (hwnd==NULL){
+		ms_error("Fail to create video window");
+	}
+	return hwnd;
+}
+
+static void dd_display_init(MSFilter  *f){
+	DDDisplay *obj=(DDDisplay*)ms_new0(DDDisplay,1);
+	obj->wsize.width=MS_VIDEO_SIZE_CIF_W;
+	obj->wsize.height=MS_VIDEO_SIZE_CIF_H;
+	obj->vsize.width=MS_VIDEO_SIZE_CIF_W;
+	obj->vsize.height=MS_VIDEO_SIZE_CIF_H;
+	obj->lsize.width=MS_VIDEO_SIZE_CIF_W;
+	obj->lsize.height=MS_VIDEO_SIZE_CIF_H;
+	yuv2rgb_init(&obj->mainview);
+	yuv2rgb_init(&obj->locview);
+	obj->sv_corner=0; /* bottom right*/
+	obj->sv_scalefactor=SCALE_FACTOR;
+	obj->sv_posx=obj->sv_posy=SELVIEW_POS_INACTIVE;
+	obj->background_color[0]=obj->background_color[1]=obj->background_color[2]=0;
+	obj->need_repaint=FALSE;
+	obj->autofit=TRUE;
+	obj->mirroring=FALSE;
+	obj->own_window=TRUE;
+	f->data=obj;
+}
+
+static void dd_display_prepare(MSFilter *f){
+	DDDisplay *dd=(DDDisplay*)f->data;
+	if (dd->window==NULL){
+		dd->window=create_window(dd->wsize.width,dd->wsize.height);
+		SetWindowLong(dd->window,GWL_USERDATA,(long)dd);
+	}
+	if (dd->ddh==NULL)
+		dd->ddh=DrawDibOpen();
+}
+
+static void dd_display_unprepare(MSFilter *f){
+	DDDisplay *dd=(DDDisplay*)f->data;
+	if (dd->own_window && dd->window!=NULL){
+		DestroyWindow(dd->window);
+		dd->window=NULL;
+	}
+	if (dd->ddh!=NULL){
+		DrawDibClose(dd->ddh);
+		dd->ddh=NULL;
+	}
+}
+
+static void dd_display_uninit(MSFilter *f){
+	DDDisplay *obj=(DDDisplay*)f->data;
+	dd_display_unprepare(f);
+	yuv2rgb_uninit(&obj->mainview);
+	yuv2rgb_uninit(&obj->locview);
+	ms_free(obj);
+}
+
+static void dd_display_preprocess(MSFilter *f){
+	dd_display_prepare(f);
+}
+
+
+/* compute the ideal placement of the video within a window of size wsize,
+given that the original video has size vsize. Put the result in rect*/
+static void center_with_ratio(MSVideoSize wsize, MSVideoSize vsize, MSRect *rect){
+	int w,h;
+	w=wsize.width & ~0x3;
+	h=((w*vsize.height)/vsize.width) & ~0x1;
+	if (h>wsize.height){
+		/*the height doesn't fit, so compute the width*/
+		h=wsize.height & ~0x1;
+		w=((h*vsize.width)/vsize.height) & ~0x3;
+	}
+	rect->x=(wsize.width-w)/2;
+	rect->y=(wsize.height-h)/2;
+	rect->w=w;
+	rect->h=h;
+}
+
+#define LOCAL_BORDER_SIZE 2
+#define LOCAL_POS_OFFSET 10
+
+static void compute_layout(MSVideoSize wsize, MSVideoSize vsize, MSVideoSize orig_psize, MSRect *mainrect, MSRect *localrect, int localrect_pos, float scalefactor){
+	MSVideoSize psize;
+
+	center_with_ratio(wsize,vsize,mainrect);
+	if (localrect_pos!=-1){
+		psize.width=(int)(wsize.width/scalefactor);
+		psize.height=(int)(wsize.height/scalefactor);
+		center_with_ratio(psize,orig_psize,localrect);
+		if ((wsize.height - mainrect->h < mainrect->h/scalefactor && wsize.width - mainrect->w < mainrect->w/scalefactor) || localrect_pos<=3)
+		{
+			int x_sv;
+			int y_sv;
+			if (localrect_pos%4==1)
+			{
+				/* top left corner */
+				x_sv = LOCAL_POS_OFFSET;
+				y_sv = LOCAL_POS_OFFSET;
+			}
+			else if (localrect_pos%4==2)
+			{
+				/* top right corner */
+				x_sv = (wsize.width-localrect->w-LOCAL_POS_OFFSET);
+				y_sv = LOCAL_POS_OFFSET;
+			}
+			else if (localrect_pos%4==3)
+			{
+				/* bottom left corner */
+				x_sv = LOCAL_POS_OFFSET;
+				y_sv = (wsize.height-localrect->h-LOCAL_POS_OFFSET);
+			}
+			else /* corner = 0: default */
+			{
+				/* bottom right corner */
+				x_sv = (wsize.width-localrect->w-LOCAL_POS_OFFSET);
+				y_sv = (wsize.height-localrect->h-LOCAL_POS_OFFSET);
+			}
+			localrect->x=x_sv; //wsize.width-localrect->w-LOCAL_POS_OFFSET;
+			localrect->y=y_sv; //wsize.height-localrect->h-LOCAL_POS_OFFSET;
+		}
+		else
+		{
+			int x_sv;
+			int y_sv;
+
+			if (wsize.width - mainrect->w < mainrect->w/scalefactor)
+			{
+				// recalculate so we have a selfview taking as
+				// much available space as possible
+				psize.width=wsize.width;
+				psize.height=wsize.height-mainrect->h;
+				center_with_ratio(psize,orig_psize,localrect);
+
+				if (localrect_pos%4==1 || localrect_pos%4==2)
+				{
+					//Self View on Top
+					x_sv = (wsize.width-localrect->w)/2;
+					y_sv = LOCAL_POS_OFFSET;
+
+					mainrect->y = wsize.height-mainrect->h-LOCAL_POS_OFFSET;
+				}
+				else
+				{
+					//Self View on Bottom
+					x_sv = (wsize.width-localrect->w)/2;
+					y_sv = (wsize.height-localrect->h-LOCAL_POS_OFFSET);
+
+					mainrect->y = LOCAL_POS_OFFSET;
+				}
+			}
+			else
+			{
+				// recalculate so we have a selfview taking as
+				// much available space as possible
+				psize.width=wsize.width-mainrect->w;
+				psize.height=wsize.height;
+				center_with_ratio(psize,orig_psize,localrect);
+
+				if (localrect_pos%4==1 || localrect_pos%4==3)
+				{
+					//Self View on left
+					x_sv = LOCAL_POS_OFFSET;
+					y_sv = (wsize.height-localrect->h)/2;
+
+					mainrect->x = wsize.width-mainrect->w-LOCAL_POS_OFFSET;
+				}
+				else
+				{
+					//Self View on right
+					x_sv = (wsize.width-localrect->w-LOCAL_POS_OFFSET);
+					y_sv = (wsize.height-localrect->h)/2;
+
+					mainrect->x = LOCAL_POS_OFFSET;
+				}
+			}
+
+			localrect->x=x_sv; //wsize.width-localrect->w-LOCAL_POS_OFFSET;
+			localrect->y=y_sv; //wsize.height-localrect->h-LOCAL_POS_OFFSET;
+		}
+	}
+/*
+	ms_message("Compute layout result for\nwindow size=%ix%i\nvideo orig size=%ix%i\nlocal size=%ix%i\nlocal orig size=%ix%i\n"
+		"mainrect=%i,%i,%i,%i\tlocalrect=%i,%i,%i,%i",
+		wsize.width,wsize.height,vsize.width,vsize.height,psize.width,psize.height,orig_psize.width,orig_psize.height,
+		mainrect->x,mainrect->y,mainrect->w,mainrect->h,
+		localrect->x,localrect->y,localrect->w,localrect->h);
+*/
+}
+
+static void draw_local_view_frame(HDC hdc, MSVideoSize wsize, MSRect localrect){
+	Rectangle(hdc, localrect.x-LOCAL_BORDER_SIZE, localrect.y-LOCAL_BORDER_SIZE,
+		localrect.x+localrect.w+LOCAL_BORDER_SIZE, localrect.y+localrect.h+LOCAL_BORDER_SIZE);
+}
+
+/*
+* Draws a background, that is the black rectangles at top, bottom or left right sides of the video display.
+* It is normally invoked only when a full redraw is needed (notified by Windows).
+*/
+static void draw_background(HDC hdc, MSVideoSize wsize, MSRect mainrect, int color[3]){
+	HBRUSH brush;
+	RECT brect;
+
+	brush = CreateSolidBrush(RGB(color[0],color[1],color[2]));
+	if (mainrect.x>0){	
+		brect.left=0;
+		brect.top=0;
+		brect.right=mainrect.x;
+		brect.bottom=wsize.height;
+		FillRect(hdc, &brect, brush);
+		brect.left=mainrect.x+mainrect.w;
+		brect.top=0;
+		brect.right=wsize.width;
+		brect.bottom=wsize.height;
+		FillRect(hdc, &brect, brush);
+	}
+	if (mainrect.y>0){
+		brect.left=0;
+		brect.top=0;
+		brect.right=wsize.width;
+		brect.bottom=mainrect.y;
+		FillRect(hdc, &brect, brush);
+		brect.left=0;
+		brect.top=mainrect.y+mainrect.h;
+		brect.right=wsize.width;
+		brect.bottom=wsize.height;
+		FillRect(hdc, &brect, brush);
+	}
+	if (mainrect.w==0 && mainrect.h==0){
+		/*no image yet, black everything*/
+		brect.left=brect.top=0;
+		brect.right=wsize.width;
+		brect.bottom=wsize.height;
+		FillRect(hdc,&brect,brush);
+	}
+	DeleteObject(brush);
+}
+
+static void dd_display_process(MSFilter *f){
+	DDDisplay *obj=(DDDisplay*)f->data;
+	RECT rect;
+	MSVideoSize wsize; /* the window size*/
+	MSVideoSize vsize;
+	MSVideoSize lsize; /*local preview size*/
+	HDC hdc;
+	MSRect mainrect;
+	MSRect localrect;
+	MSPicture mainpic;
+	MSPicture localpic;
+	mblk_t *main_im=NULL;
+	mblk_t *local_im=NULL;
+	HDC hdc2;
+	HBITMAP tmp_bmp=NULL;
+	HGDIOBJ old_object=NULL;
+	bool_t repainted=FALSE;
+	int corner=obj->sv_corner;
+	float scalefactor=obj->sv_scalefactor;
+
+	if (wd->window==NULL){
+		goto end;
+	}
+
+	GetClientRect(obj->window,&rect);
+	wsize.width=rect.right;
+	wsize.height=rect.bottom;
+	obj->wsize=wsize;
+	/*get most recent message and draw it*/
+	if (corner!=-1 && f->inputs[1]!=NULL && (local_im=ms_queue_peek_last(f->inputs[1]))!=NULL) {
+		if (yuv_buf_init_from_mblk(&localpic,local_im)==0){
+			obj->lsize.width=localpic.w;
+			obj->lsize.height=localpic.h;
+		}
+	}
+	
+	if (f->inputs[0]!=NULL && (main_im=ms_queue_peek_last(f->inputs[0]))!=NULL) {
+		if (yuv_buf_init_from_mblk(&mainpic,main_im)==0){
+			if (obj->autofit && (obj->vsize.width!=mainpic.w || obj->vsize.height!=mainpic.h)
+				&& (mainpic.w>wsize.width || mainpic.h>wsize.height)){
+				RECT cur;
+				ms_message("Detected video resolution changed, resizing window");
+				GetWindowRect(obj->window,&cur);
+				wsize.width=mainpic.w;
+				wsize.height=mainpic.h;
+				MoveWindow(obj->window,cur.left, cur.top, wsize.width, wsize.height,TRUE);
+				obj->need_repaint=TRUE;
+			}
+			obj->vsize.width=mainpic.w;
+			obj->vsize.height=mainpic.h;
+		}
+	}
+
+	if (main_im!=NULL || local_im!=NULL || obj->need_repaint){
+		compute_layout(wsize,obj->vsize,obj->lsize,&mainrect,&localrect,corner, scalefactor);
+		vsize.width=mainrect.w;
+		vsize.height=mainrect.h;
+		lsize.width=localrect.w;
+		lsize.height=localrect.h;
+		
+		if (local_im!=NULL)
+			yuv2rgb_process(&obj->locview,&localpic,lsize,!mblk_get_precious_flag(local_im));
+	
+		if (main_im!=NULL)
+			yuv2rgb_process(&obj->mainview,&mainpic,vsize,obj->mirroring && !mblk_get_precious_flag(main_im));
+	
+		hdc=GetDC(obj->window);
+		if (hdc==NULL) {
+			ms_error("Could not get window dc");
+			return;
+		}
+		/*handle the case where local view is disabled*/
+		if (corner==-1 && obj->locview.rgb!=NULL){
+			yuv2rgb_uninit(&obj->locview);
+		}
+		if (obj->locview.rgb==NULL){
+			 /*One layer: we can draw directly on the displayed surface*/
+			hdc2=hdc;
+			if (obj->need_repaint)
+				draw_background(hdc2,wsize,mainrect, obj->background_color);
+		}else{
+			/* in this case we need to stack several layers*/
+			/*Create a second DC and bitmap to draw to a buffer that will be blitted to screen
+			once all drawing is finished. This avoids some blinking while composing the image*/
+			hdc2=CreateCompatibleDC(hdc);
+			tmp_bmp=CreateCompatibleBitmap(hdc,wsize.width,wsize.height);
+			old_object = SelectObject(hdc2, tmp_bmp);
+			draw_background(hdc2,wsize,mainrect, obj->background_color);
+		}
+
+		if (obj->need_repaint){
+			repainted=TRUE;
+			obj->need_repaint=FALSE;
+		}
+		if (main_im!=NULL || obj->locview.rgb!=NULL){
+			yuv2rgb_draw(&obj->mainview,obj->ddh,hdc2,mainrect.x,mainrect.y);
+		}
+		if (obj->locview.rgb!=NULL){
+			draw_local_view_frame(hdc2,wsize,localrect);
+			yuv2rgb_draw(&obj->locview,obj->ddh,hdc2,localrect.x,localrect.y);
+		}
+		if (hdc!=hdc2){
+			if (main_im==NULL && !repainted){
+				/* Blitting local rect only */
+				BitBlt(hdc,localrect.x-LOCAL_BORDER_SIZE,localrect.y-LOCAL_BORDER_SIZE,
+					localrect.w+LOCAL_BORDER_SIZE,localrect.h+LOCAL_BORDER_SIZE,hdc2,
+					localrect.x-LOCAL_BORDER_SIZE,localrect.y-LOCAL_BORDER_SIZE,SRCCOPY);
+			}else{
+				/*Blitting the entire window */
+				BitBlt(hdc, 0, 0, wsize.width, wsize.height, hdc2, 0, 0, SRCCOPY);
+			}
+			SelectObject(hdc2,old_object);
+			DeleteObject(tmp_bmp);
+			DeleteDC(hdc2);
+		}
+		/*else using direct blitting to screen*/
+
+		ReleaseDC(NULL,hdc);
+	}
+	
+	end:
+		
+	if (f->inputs[0]!=NULL)
+		ms_queue_flush(f->inputs[0]);
+	if (f->inputs[1]!=NULL)
+		ms_queue_flush(f->inputs[1]);
+}
+
+static int get_native_window_id(MSFilter *f, void *data){
+	DDDisplay *obj=(DDDisplay*)f->data;
+	*(long*)data=(long)obj->window;
+	return 0;
+}
+
+static int set_native_window_id(MSFilter *f, void *data){
+	DDDisplay *obj=(DDDisplay*)f->data;
+	obj->window=(HWND)(*(long*)data);
+	obj->own_window=FALSE;
+	return 0;
+}
+
+static int enable_autofit(MSFilter *f, void *data){
+	DDDisplay *obj=(DDDisplay*)f->data;
+	obj->autofit=*(int*)data;
+	return 0;
+}
+
+static int enable_mirroring(MSFilter *f, void *data){
+	DDDisplay *obj=(DDDisplay*)f->data;
+	obj->mirroring=*(int*)data;
+	return 0;
+}
+
+static int set_corner(MSFilter *f, void *data){
+	DDDisplay *obj=(DDDisplay*)f->data;
+	obj->sv_corner=*(int*)data;
+	obj->need_repaint=TRUE;
+	return 0;
+}
+
+static int get_vsize(MSFilter *f, void *data){
+	DDDisplay *obj=(DDDisplay*)f->data;
+	*(MSVideoSize*)data=obj->wsize;
+	return 0;
+}
+
+static int set_vsize(MSFilter *f, void *data){
+	DDDisplay *obj=(DDDisplay*)f->data;
+	obj->wsize=*(MSVideoSize*)data;
+	return 0;
+}
+
+static int set_scalefactor(MSFilter *f,void *arg){
+	DDDisplay *obj=(DDDisplay*)f->data;
+	ms_filter_lock(f);
+	obj->sv_scalefactor = *(float*)arg;
+	if (obj->sv_scalefactor<0.5f)
+		obj->sv_scalefactor = 0.5f;
+	ms_filter_unlock(f);
+	return 0;
+}
+
+
+static int set_selfview_pos(MSFilter *f,void *arg){
+	DDDisplay *s=(DDDisplay*)f->data;
+	s->sv_posx=((float*)arg)[0];
+	s->sv_posy=((float*)arg)[1];
+	s->sv_scalefactor=(float)100.0/((float*)arg)[2];
+	return 0;
+}
+
+static int get_selfview_pos(MSFilter *f,void *arg){
+	DDDisplay *s=(DDDisplay*)f->data;
+	((float*)arg)[0]=s->sv_posx;
+	((float*)arg)[1]=s->sv_posy;
+	((float*)arg)[2]=(float)100.0/s->sv_scalefactor;
+	return 0;
+}
+
+static int set_background_color(MSFilter *f,void *arg){
+	DDDisplay *s=(DDDisplay*)f->data;
+	s->background_color[0]=((int*)arg)[0];
+	s->background_color[1]=((int*)arg)[1];
+	s->background_color[2]=((int*)arg)[2];
+	return 0;
+}
+
+static MSFilterMethod methods[]={
+	{	MS_FILTER_GET_VIDEO_SIZE			, get_vsize	},
+	{	MS_FILTER_SET_VIDEO_SIZE			, set_vsize	},
+	{	MS_VIDEO_DISPLAY_GET_NATIVE_WINDOW_ID, get_native_window_id },
+	{	MS_VIDEO_DISPLAY_SET_NATIVE_WINDOW_ID, set_native_window_id },
+	{	MS_VIDEO_DISPLAY_ENABLE_AUTOFIT		,	enable_autofit	},
+	{	MS_VIDEO_DISPLAY_ENABLE_MIRRORING	,	enable_mirroring},
+	{	MS_VIDEO_DISPLAY_SET_LOCAL_VIEW_MODE	, set_corner },
+	{	MS_VIDEO_DISPLAY_SET_LOCAL_VIEW_SCALEFACTOR	, set_scalefactor },
+	{	MS_VIDEO_DISPLAY_SET_SELFVIEW_POS 	 ,	set_selfview_pos},
+	{	MS_VIDEO_DISPLAY_GET_SELFVIEW_POS    ,  get_selfview_pos},
+	{	MS_VIDEO_DISPLAY_SET_BACKGROUND_COLOR    ,  set_background_color},
+	{	0	,NULL}
+};
+
+#ifdef _MSC_VER
+
+MSFilterDesc ms_dd_display_desc={
+	MS_DRAWDIB_DISPLAY_ID,
+	"MSDrawDibDisplay",
+	N_("A video display based on windows DrawDib api"),
+	MS_FILTER_OTHER,
+	NULL,
+	2,
+	0,
+	dd_display_init,
+	dd_display_preprocess,
+	dd_display_process,
+	NULL,
+	dd_display_uninit,
+	methods
+};
+
+#else
+
+MSFilterDesc ms_dd_display_desc={
+	.id=MS_DRAWDIB_DISPLAY_ID,
+	.name="MSDrawDibDisplay",
+	.text=N_("A video display based on windows DrawDib api"),
+	.category=MS_FILTER_OTHER,
+	.ninputs=2,
+	.noutputs=0,
+	.init=dd_display_init,
+	.preprocess=dd_display_preprocess,
+	.process=dd_display_process,
+	.uninit=dd_display_uninit,
+	.methods=methods
+};
+
+#endif
+
+MS_FILTER_DESC_EXPORT(ms_dd_display_desc)
Index: src/eventqueue.c
===================================================================
--- src/eventqueue.c	(revision 987)
+++ src/eventqueue.c	(revision 987)
@@ -0,0 +1,133 @@
+/*
+mediastreamer2 library - modular sound and video processing and streaming
+Copyright (C) 2010  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 "mediastreamer2/mseventqueue.h"
+#include "mediastreamer2/msfilter.h"
+
+#ifndef MS_EVENT_BUF_SIZE
+#define MS_EVENT_BUF_SIZE 8192
+#endif
+
+struct _MSEventQueue{
+	ms_mutex_t mutex; /*could be replaced by an atomic counter for freeroom*/
+	uint8_t *rptr;
+	uint8_t *wptr;
+	uint8_t *endptr;
+	uint8_t *lim;
+	int freeroom;
+	int size;
+	uint8_t buffer[MS_EVENT_BUF_SIZE];
+};
+
+static void write_event(MSEventQueue *q, MSFilter *f, unsigned int ev_id, void *arg){
+	int argsize=ev_id & 0xffff;
+	int size=argsize+16;
+	uint8_t *nextpos=q->wptr+size;
+
+	if (q->freeroom<size){
+		ms_error("Dropped event, no more free space in event buffer !");
+		return;
+	}
+	
+	if (nextpos>q->lim){
+		/* need to wrap around */
+		q->endptr=q->wptr;
+		q->wptr=q->buffer;
+		nextpos=q->wptr+size;
+	}
+	*(long*)q->wptr=(long)f;
+	*(long*)(q->wptr+8)=(long)ev_id;
+	if (argsize>0) memcpy(q->wptr+16,arg,argsize);
+	q->wptr=nextpos;
+	ms_mutex_lock(&q->mutex);
+	q->freeroom-=size;
+	ms_mutex_unlock(&q->mutex);
+}
+
+static bool_t read_event(MSEventQueue *q){
+	int available=q->size-q->freeroom;
+	if (available>0){
+		MSFilter *f;
+		unsigned int id;
+		void *data;
+		int argsize;
+		int evsize;
+		
+		f=(MSFilter *)*(long*)(q->rptr);
+		id=(unsigned int)*(long*)(q->rptr+8);
+		argsize=id & 0xffff;
+		evsize=argsize+16;
+		data=q->rptr+16;
+		if (f->notify!=NULL)
+			f->notify(f->notify_ud,id,argsize>0 ? data : NULL);
+		q->rptr+=evsize;
+		if (q->rptr>=q->endptr){
+			q->rptr=q->buffer;
+		}
+		ms_mutex_lock(&q->mutex);
+		q->freeroom+=evsize;
+		ms_mutex_unlock(&q->mutex);
+		return TRUE;
+	}
+	return FALSE;
+}
+
+MSEventQueue *ms_event_queue_new(){
+	MSEventQueue *q=ms_new0(MSEventQueue,1);
+	int bufsize=MS_EVENT_BUF_SIZE;
+	ms_mutex_init(&q->mutex,NULL);
+	q->lim=q->buffer+bufsize;
+	q->freeroom=bufsize;
+	q->wptr=q->rptr=q->buffer;
+	q->endptr=q->lim;
+	q->size=bufsize;
+	return q;
+}
+
+void ms_event_queue_destroy(MSEventQueue *q){
+	ms_mutex_destroy(&q->mutex);
+	ms_free(q);
+}
+
+static MSEventQueue *ms_global_event_queue=NULL;
+
+void ms_set_global_event_queue(MSEventQueue *q){
+	ms_global_event_queue=q;
+}
+
+void ms_event_queue_pump(MSEventQueue *q){
+	while(read_event(q)){
+	}
+}
+
+
+void ms_filter_notify(MSFilter *f, unsigned int id, void *arg){
+	if (f->notify!=NULL){
+		if (ms_global_event_queue==NULL){
+			/* synchronous notification */
+			f->notify(f->notify_ud,id,arg);			
+		}else{
+			write_event(ms_global_event_queue,f,id,arg);
+		}
+	}
+}
+
+void ms_filter_notify_no_arg(MSFilter *f, unsigned int id){
+	ms_filter_notify(f,id,NULL);
+}
Index: src/extdisplay.c
===================================================================
--- src/extdisplay.c	(revision 997)
+++ src/extdisplay.c	(revision 997)
@@ -0,0 +1,90 @@
+/*
+mediastreamer2 library - modular sound and video processing and streaming
+Copyright (C) 2010  Belledonne Communications SARL <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 "mediastreamer2/msextdisplay.h"
+
+static void ext_display_init(MSFilter *f){
+	ms_filter_enable_synchronous_notifcations (f,TRUE);
+}
+
+static void ext_display_uninit(MSFilter *f){
+}
+
+static void ext_display_process(MSFilter *f){
+	MSExtDisplayOutput output;
+	mblk_t *main_im=NULL;
+	mblk_t *local_im=NULL;
+
+	memset(&output,0,sizeof(output));
+	
+	/*get most recent message and draw it*/
+	if ( f->inputs[1]!=NULL && (local_im=ms_queue_peek_last(f->inputs[1]))!=NULL) {
+		if (yuv_buf_init_from_mblk(&output.local_view,local_im)==0){
+		}
+	}
+	
+	if (f->inputs[0]!=NULL && (main_im=ms_queue_peek_last(f->inputs[0]))!=NULL) {
+		if (yuv_buf_init_from_mblk(&output.remote_view,main_im)==0){
+		}
+	}
+
+	ms_filter_notify(f,MS_EXT_DISPLAY_ON_DRAW,&output);
+	
+	if (f->inputs[0]!=NULL)
+		ms_queue_flush(f->inputs[0]);
+	if (f->inputs[1]!=NULL)
+		ms_queue_flush(f->inputs[1]);
+}
+
+
+#ifdef _MSC_VER
+
+MSFilterDesc ms_ext_display_desc={
+	MS_EXT_DISPLAY_ID,
+	"MSExtDisplay",
+	N_("A display filter sending the buffers to draw to the upper layer"),
+	MS_FILTER_OTHER,
+	NULL,
+	2,
+	0,
+	ext_display_init,
+	NULL,
+	ext_display_process,
+	NULL,
+	ext_display_uninit,
+};
+
+#else
+
+MSFilterDesc ms_ext_display_desc={
+	.id=MS_EXT_DISPLAY_ID,
+	.name="MSExtDisplay",
+	.text=N_("A display filter sending the buffers to draw to the upper layer"),
+	.category=MS_FILTER_OTHER,
+	.ninputs=2,
+	.noutputs=0,
+	.init=ext_display_init,
+	.process=ext_display_process,
+	.uninit=ext_display_uninit,
+};
+
+#endif
+
+MS_FILTER_DESC_EXPORT(ms_ext_display_desc)
Index: src/itc.c
===================================================================
--- src/itc.c	(revision 993)
+++ src/itc.c	(revision 993)
@@ -0,0 +1,227 @@
+/*
+mediastreamer2 library - modular sound and video processing and streaming
+Copyright (C) 2010  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 "mediastreamer2/msitc.h"
+
+typedef struct SourceState{
+	ms_mutex_t mutex;
+	int rate;
+	int nchannels;
+	MSQueue q;
+}SourceState;
+
+static void itc_source_init(MSFilter *f){
+	SourceState *s=ms_new(SourceState,1);
+	ms_mutex_init(&s->mutex,NULL);
+	ms_queue_init(&s->q);
+	s->rate=44100;
+	s->nchannels=1;
+	f->data=s;
+}
+
+static void itc_source_uninit(MSFilter *f){
+	SourceState *s=(SourceState *)f->data;
+	ms_mutex_destroy(&s->mutex);
+	ms_queue_flush (&s->q);
+	ms_free(s);
+}
+
+static void itc_source_queue_packet(MSFilter *f, mblk_t *m){
+	SourceState *s=(SourceState *)f->data;
+	ms_mutex_lock(&s->mutex);
+	ms_queue_put(&s->q,m);
+	ms_mutex_unlock(&s->mutex);
+}
+
+static void itc_source_set_nchannels(MSFilter *f, int chans){
+	SourceState *s=(SourceState *)f->data;
+	s->nchannels=chans;
+}
+
+static void itc_source_set_rate(MSFilter *f, int rate){
+	SourceState *s=(SourceState *)f->data;
+	s->rate=rate;
+}
+
+static int itc_source_get_nchannels(MSFilter *f, void *data){
+	SourceState *s=(SourceState *)f->data;
+	*(int*)data=s->nchannels;
+	return 0;
+}
+
+static int itc_source_get_rate(MSFilter *f, void *data){
+	SourceState *s=(SourceState *)f->data;
+	*(int*)data=s->rate;
+	return 0;
+}
+
+static void itc_source_process(MSFilter *f){
+	SourceState *s=(SourceState *)f->data;
+	mblk_t *m;
+	ms_mutex_lock(&s->mutex);
+	while((m=ms_queue_get(&s->q))!=NULL){
+		ms_mutex_unlock(&s->mutex);
+		ms_queue_put(f->outputs[0],m);
+		ms_mutex_lock(&s->mutex);
+	}
+	ms_mutex_unlock(&s->mutex);
+}
+
+static MSFilterMethod source_methods[]={
+	{	MS_FILTER_GET_SAMPLE_RATE , itc_source_get_rate },
+	{	MS_FILTER_GET_NCHANNELS , itc_source_get_nchannels },
+	{ 0,NULL}
+};
+
+#ifdef _MSC_VER
+
+MSFilterDesc ms_itc_source_desc={
+	MS_ITC_SOURCE_ID,
+	"MSItcSource",
+	N_("Inter ticker communication filter."),
+	MS_FILTER_OTHER,
+	NULL,
+	0,
+	1,
+	itc_source_init,
+	NULL,
+	itc_source_process,
+	NULL,
+	itc_source_uninit,
+	source_methods
+};
+
+#else
+
+MSFilterDesc ms_itc_source_desc={
+	.id=MS_ITC_SOURCE_ID,
+	.name="MSItcSource",
+	.text=N_("Inter ticker communication filter."),
+	.category=MS_FILTER_OTHER,
+	.ninputs=0,
+	.noutputs=1,
+	.init=itc_source_init,
+	.process=itc_source_process,
+	.uninit=itc_source_uninit,
+	.methods=source_methods
+};
+
+#endif
+
+static void itc_sink_preprocess(MSFilter *f){
+	MSFilter *other=(MSFilter *)f->data;
+	ms_filter_notify(other,MS_ITC_SOURCE_UPDATED,NULL);
+}
+
+static void itc_sink_process(MSFilter *f){
+	MSFilter *other=(MSFilter *)f->data;
+	mblk_t *im;
+	while((im=ms_queue_get(f->inputs[0]))!=NULL){
+		itc_source_queue_packet(other,im);
+	}
+}
+
+static int itc_sink_connect(MSFilter *f, void *data){
+	f->data=data;
+	return 0;
+}
+
+static int itc_sink_set_nchannels(MSFilter *f , void *data){
+	MSFilter *other=(MSFilter *)f->data;
+	if (other==NULL){
+		ms_error("MSItcSink not connected to any source !");
+		return -1;
+	}
+	itc_source_set_nchannels (other,*(int*)data);
+	return 0;
+}
+
+static int itc_sink_set_sr(MSFilter *f , void *data){
+	MSFilter *other=(MSFilter *)f->data;
+	if (other==NULL){
+		ms_error("MSItcSink not connected to any source !");
+		return -1;
+	}
+	itc_source_set_rate (other,*(int*)data);
+	return 0;
+}
+
+static int itc_sink_get_nchannels(MSFilter *f , void *data){
+	MSFilter *other=(MSFilter *)f->data;
+	if (other==NULL){
+		ms_error("MSItcSink not connected to any source !");
+		return -1;
+	}
+	return itc_source_get_nchannels (other,data);
+}
+
+static int itc_sink_get_sr(MSFilter *f , void *data){
+	MSFilter *other=(MSFilter *)f->data;
+	if (other==NULL){
+		ms_error("MSItcSink not connected to any source !");
+		return -1;
+	}
+	return itc_source_get_rate (other,data);
+}
+
+static MSFilterMethod sink_methods[]={
+	{	MS_ITC_SINK_CONNECT , itc_sink_connect },
+	{  MS_FILTER_SET_NCHANNELS , itc_sink_set_nchannels },
+	{  MS_FILTER_SET_SAMPLE_RATE , itc_sink_set_sr },
+	{	MS_FILTER_GET_NCHANNELS, itc_sink_get_nchannels },
+	{	MS_FILTER_GET_SAMPLE_RATE, itc_sink_get_sr },
+	{ 0, NULL }
+};
+
+#ifdef _MSC_VER
+
+MSFilterDesc ms_itc_sink_desc={
+	MS_ITC_SINK_ID,
+	"MSItcSink",
+	N_("Inter ticker communication filter."),
+	MS_FILTER_OTHER,
+	NULL,
+	1,
+	0,
+	NULL,
+	itc_sink_preprocess,
+	itc_sink_process,
+	NULL,
+	NULL,
+	sink_methods
+};
+
+#else
+
+MSFilterDesc ms_itc_sink_desc={
+	.id=MS_ITC_SINK_ID,
+	.name="MSItcSink",
+	.text=N_("Inter ticker communication filter."),
+	.category=MS_FILTER_OTHER,
+	.ninputs=1,
+	.noutputs=0,
+	.preprocess=itc_sink_preprocess,
+	.process=itc_sink_process,
+	.methods=sink_methods
+};
+
+#endif
+
+MS_FILTER_DESC_EXPORT(ms_itc_source_desc)
+MS_FILTER_DESC_EXPORT(ms_itc_sink_desc)
Index: src/mscommon.c
===================================================================
--- src/mscommon.c	(revision 861)
+++ src/mscommon.c	(revision 993)
@@ -432,4 +432,8 @@
 #endif
 
+#ifdef __PULSEAUDIO_ENABLED__
+extern MSSndCardDesc pulse_card_desc;
+#endif
+
 static MSSndCardDesc * ms_snd_card_descs[]={
 #ifdef __ALSA_ENABLED__
@@ -456,4 +460,7 @@
 #ifdef __MAC_AQ_ENABLED__
 	&aq_card_desc,
+#endif
+#ifdef __PULSEAUDIO_ENABLED__
+	&pulse_card_desc,
 #endif
 	NULL
@@ -600,4 +607,19 @@
 }
 
+void ms_usleep(uint64_t usec){
+#ifdef WIN32
+	Sleep((DWORD)(usec/1000));
+#else
+	struct timespec ts,rem;
+	int err;
+	ts.tv_sec=usec/1000000LL;
+	ts.tv_nsec=(usec%1000000LL)*1000;
+	do {
+		err=nanosleep(&ts,&rem);
+		ts=rem;
+	}while(err==-1 && errno==EINTR);
+#endif
+}
+
 #define DEFAULT_MAX_PAYLOAD_SIZE 1440
 
Index: src/msfileplayer.c
===================================================================
--- src/msfileplayer.c	(revision 856)
+++ src/msfileplayer.c	(revision 980)
@@ -25,13 +25,7 @@
 static int player_close(MSFilter *f, void *arg);
 
-typedef enum {
-	CLOSED,
-	STARTED,
-	STOPPED
-} PlayerState;
-
 struct _PlayerData{
 	int fd;
-	PlayerState state;
+	MSPlayerState state;
 	int rate;
 	int nchannels;
@@ -48,5 +42,5 @@
 	PlayerData *d=ms_new(PlayerData,1);
 	d->fd=-1;
-	d->state=CLOSED;
+	d->state=MSPlayerClosed;
 	d->swap=FALSE;
 	d->rate=8000;
@@ -140,5 +134,5 @@
 		return -1;
 	}
-	d->state=STOPPED;
+	d->state=MSPlayerPaused;
 	d->fd=fd;
 	if (read_wav_header(d)!=0 && strstr(file,".wav")){
@@ -151,6 +145,6 @@
 static int player_start(MSFilter *f, void *arg){
 	PlayerData *d=(PlayerData*)f->data;
-	if (d->state==STOPPED)
-		d->state=STARTED;
+	if (d->state==MSPlayerPaused)
+		d->state=MSPlayerPlaying;
 	return 0;
 }
@@ -159,7 +153,17 @@
 	PlayerData *d=(PlayerData*)f->data;
 	ms_filter_lock(f);
-	if (d->state==STARTED){
-		d->state=STOPPED;
+	if (d->state!=MSPlayerClosed){
+		d->state=MSPlayerPaused;
 		lseek(d->fd,d->hsize,SEEK_SET);
+	}
+	ms_filter_unlock(f);
+	return 0;
+}
+
+static int player_pause(MSFilter *f, void *arg){
+	PlayerData *d=(PlayerData*)f->data;
+	ms_filter_lock(f);
+	if (d->state==MSPlayerPlaying){
+		d->state=MSPlayerPaused;
 	}
 	ms_filter_unlock(f);
@@ -172,5 +176,11 @@
 	if (d->fd>=0)	close(d->fd);
 	d->fd=-1;
-	d->state=CLOSED;
+	d->state=MSPlayerClosed;
+	return 0;
+}
+
+static int player_get_state(MSFilter *f, void *arg){
+	PlayerData *d=(PlayerData*)f->data;
+	*(int*)arg=d->state;
 	return 0;
 }
@@ -208,5 +218,5 @@
 	d->count++;
 	ms_filter_lock(f);
-	if (d->state==STARTED){
+	if (d->state==MSPlayerPlaying){
 		int err;
 		mblk_t *om=allocb(bytes,0);
@@ -231,7 +241,7 @@
 
 				/* special value for playing file only once */
-				if (d->loop_after==-2)
+				if (d->loop_after<0)
 				{
-					d->state=STOPPED;
+					d->state=MSPlayerPaused;
 					ms_filter_unlock(f);
 					return;
@@ -263,5 +273,5 @@
 static int player_eof(MSFilter *f, void *arg){
 	PlayerData *d=(PlayerData*)f->data;
-	if (d->fd<0 && d->state==CLOSED)
+	if (d->fd<0 && d->state==MSPlayerClosed)
 		*((int*)arg) = TRUE; /* 1 */
 	else
@@ -285,4 +295,10 @@
 	{	MS_FILE_PLAYER_LOOP,	player_loop	},
 	{	MS_FILE_PLAYER_DONE,	player_eof	},
+	/* this wav file player implements the MSFilterPlayerInterface*/
+	{ MS_PLAYER_OPEN , player_open },
+	{ MS_PLAYER_START , player_start },
+	{ MS_PLAYER_PAUSE, player_pause },
+	{ MS_PLAYER_CLOSE, player_close },
+	{ MS_PLAYER_GET_STATE, player_get_state },
 	{	0,			NULL		}
 };
Index: src/msfilter.c
===================================================================
--- src/msfilter.c	(revision 856)
+++ src/msfilter.c	(revision 997)
@@ -124,4 +124,5 @@
 int ms_filter_link(MSFilter *f1, int pin1, MSFilter *f2, int pin2){
 	MSQueue *q;
+	ms_message("ms_filter_link: %s:%p,%i-->%s:%p,%i",f1->desc->name,f1,pin1,f2->desc->name,f2,pin2);
 	ms_return_val_if_fail(pin1<f1->desc->noutputs, -1);
 	ms_return_val_if_fail(pin2<f2->desc->ninputs, -1);
@@ -131,5 +132,4 @@
 	f1->outputs[pin1]=q;
 	f2->inputs[pin2]=q;
-	ms_message("ms_filter_link: %s:%p,%i-->%s:%p,%i",f1->desc->name,f1,pin1,f2->desc->name,f2,pin2);
 	return 0;
 }
@@ -139,4 +139,5 @@
 	ms_return_val_if_fail(f1, -1);
 	ms_return_val_if_fail(f2, -1);
+	ms_message("ms_filter_unlink: %s:%p,%i-->%s:%p,%i",f1->desc->name,f1,pin1,f2->desc->name,f2,pin2);
 	ms_return_val_if_fail(pin1<f1->desc->noutputs, -1);
 	ms_return_val_if_fail(pin2<f2->desc->ninputs, -1);
@@ -147,9 +148,13 @@
 	f1->outputs[pin1]=f2->inputs[pin2]=0;
 	ms_queue_destroy(q);
-	ms_message("ms_filter_unlink: %s:%p,%i-->%s:%p,%i",f1->desc->name,f1,pin1,f2->desc->name,f2,pin2);
 	return 0;
 }
 
 #define MS_FILTER_METHOD_GET_FID(id)	(((id)>>16) & 0xFFFF)
+#define MS_FILTER_METHOD_GET_INDEX(id) ( ((id)>>8) & 0XFF) 
+
+static inline bool_t is_interface_method(unsigned int magic){
+	return magic==MS_FILTER_BASE_ID || magic>MSFilterInterfaceBegin;
+}
 
 int ms_filter_call_method(MSFilter *f, unsigned int id, void *arg){
@@ -157,5 +162,5 @@
 	int i;
 	unsigned int magic=MS_FILTER_METHOD_GET_FID(id);
-	if (magic!=MS_FILTER_BASE_ID && magic!=f->desc->id) {
+	if (!is_interface_method(magic) && magic!=f->desc->id) {
 		ms_fatal("Method type checking failed when calling %u on filter %s",id,f->desc->name);
 		return -1;
@@ -163,5 +168,5 @@
 	for(i=0;methods!=NULL && methods[i].method!=NULL; i++){
 		unsigned int mm=MS_FILTER_METHOD_GET_FID(methods[i].id);
-		if (mm!=f->desc->id && mm!=MS_FILTER_BASE_ID) {
+		if (mm!=f->desc->id && !is_interface_method(mm)) {
 			ms_fatal("Bad method definition on filter %s. fid=%u , mm=%u",f->desc->name,f->desc->id,mm);
 			return -1;
@@ -171,5 +176,6 @@
 		}
 	}
-	if (magic!=MS_FILTER_BASE_ID) ms_error("no such method on filter %s",f->desc->name);
+	if (magic!=MS_FILTER_BASE_ID) ms_error("no such method on filter %s, fid=%i method index=%i",f->desc->name,magic,
+	                           MS_FILTER_METHOD_GET_INDEX(id) );
 	return -1;
 }
@@ -182,4 +188,8 @@
 	f->notify=fn;
 	f->notify_ud=ud;
+}
+
+void ms_filter_enable_synchronous_notifcations(MSFilter *f, bool_t yesno){
+	f->synchronous_notifies=yesno;
 }
 
@@ -221,13 +231,4 @@
 }
 
-void ms_filter_notify(MSFilter *f, unsigned int id, void *arg){
-	if (f->notify!=NULL)
-		f->notify(f->notify_ud,id,arg);
-}
-
-void ms_filter_notify_no_arg(MSFilter *f, unsigned int id){
-	if (f->notify!=NULL)
-		f->notify(f->notify_ud,id,NULL);
-}
 
 
Index: src/msresample.c
===================================================================
--- src/msresample.c	(revision 856)
+++ src/msresample.c	(revision 986)
@@ -32,7 +32,6 @@
 	uint32_t input_rate;
 	uint32_t output_rate;
-
+	int nchannels;
 	SpeexResamplerState *handle;
-	int nb_unprocessed;
 } ResampleData;
 
@@ -44,6 +43,5 @@
 	obj->output_rate=16000;
 	obj->handle=NULL;
-
-	obj->nb_unprocessed=0;
+	obj->nchannels=1;
 	return obj;
 }
@@ -61,87 +59,7 @@
 
 static void resample_uninit(MSFilter *obj){
-	resample_data_destroy((ResampleData*)obj->data);
- 
+	resample_data_destroy((ResampleData*)obj->data); 
 }
 
-#if 0
-static void resample_process_ms2(MSFilter *obj){
-	ResampleData *dt=(ResampleData*)obj->data;
-	MSBufferizer *bz=dt->bz;
-	uint8_t buffer[2240];
-	int size_of_input;
-	int size_of_output;
-
-	mblk_t *m;
-	
-	if (dt->output_rate==dt->input_rate)
-	  {
-	    while((m=ms_queue_get(obj->inputs[0]))!=NULL){
-	      ms_queue_put(obj->outputs[0],m);
-	    }
-	    return;
-	  }
-        if (dt->handle!=NULL){
-		unsigned int inrate=0, outrate=0;
-		speex_resampler_get_rate(dt->handle,&inrate,&outrate);
-		if (inrate!=dt->input_rate || outrate!=dt->output_rate){
-			speex_resampler_destroy(dt->handle);
-			dt->handle=0;
-		}
-	}
-	if (dt->handle==NULL){
-		int err=0;
-		dt->handle=speex_resampler_init(1, dt->input_rate, dt->output_rate, SPEEX_RESAMPLER_QUALITY_VOIP, &err);
-	}
-
-
-	if (dt->input_rate<dt->output_rate)
-	    size_of_input=320*dt->input_rate/8000;
-	else
-	    size_of_input=320*dt->input_rate/8000;
-	size_of_output = (size_of_input * dt->output_rate)/dt->input_rate;
-
-	while((m=ms_queue_get(obj->inputs[0]))!=NULL){
-		ms_bufferizer_put(bz,m);
-	}
-	while (ms_bufferizer_read(bz,buffer,size_of_input)==size_of_input){
-		mblk_t *obl=allocb(size_of_output,0);
-
-		float *in;
-		float *out;
-		spx_uint32_t in_len;
-		spx_uint32_t out_len;
-		int err;
-
-		short *data = (short*)buffer;
-		short *data_out = (short*)obl->b_wptr;
-
-		int i;
-		spx_uint32_t idx;
-    
-		in = (float*) alloca((size_of_input/2)*sizeof(float));
-		out = (float*) alloca((size_of_output/2)*sizeof(float));
-
-		/* Convert the samples to floats */
-		for (i = 0; i < size_of_input/2; i++)
-			in[i] = (float) data[i];
-
-		in_len = size_of_input/2;
-		out_len = size_of_output/2;
-		err = speex_resampler_process_float(dt->handle, 0, in, &in_len, out, &out_len);
-
-		/* ms_message("resampling info: err=%i in_len=%i, out_len=%i", err, in_len, out_len); */
-
-		for (idx=0;idx<out_len;idx++)
-		  data_out[idx]=(short)floor(.5+out[idx]);
-		obl->b_wptr=obl->b_wptr+(out_len*2); /* size_of_output; */
-
-		mblk_set_timestamp_info(obl,dt->ts);
-		dt->ts+=160;
-		ms_queue_put(obj->outputs[0],obl);
-	}
-}
-
-#else
 static void resample_process_ms2(MSFilter *obj){
 	ResampleData *dt=(ResampleData*)obj->data;
@@ -152,7 +70,7 @@
 			ms_queue_put(obj->outputs[0],m);
 		}
-	    	return;
+		return;
 	}
-
+	ms_filter_lock(obj);
 	if (dt->handle!=NULL){
 		unsigned int inrate=0, outrate=0;
@@ -165,24 +83,32 @@
 	if (dt->handle==NULL){
 		int err=0;
-		dt->handle=speex_resampler_init(1, dt->input_rate, dt->output_rate, SPEEX_RESAMPLER_QUALITY_VOIP, &err);
+		dt->handle=speex_resampler_init(dt->nchannels, dt->input_rate, dt->output_rate, SPEEX_RESAMPLER_QUALITY_VOIP, &err);
 	}
 
 	
 	while((m=ms_queue_get(obj->inputs[0]))!=NULL){
-		unsigned int inlen=(m->b_wptr-m->b_rptr)/2;
+		unsigned int inlen=(m->b_wptr-m->b_rptr)/(2*dt->nchannels);
 		unsigned int outlen=((inlen*dt->output_rate)/dt->input_rate)+1;
 		unsigned int inlen_orig=inlen;
-		mblk_t *om=allocb(outlen*2,0);
-		speex_resampler_process_int(dt->handle, 
-                                 0, 
-                                 (int16_t*)m->b_rptr, 
-                                 &inlen, 
-                                 (int16_t*)om->b_wptr, 
-                                 &outlen);
+		mblk_t *om=allocb(outlen*2*dt->nchannels,0);
+		if (dt->nchannels==1){
+			speex_resampler_process_int(dt->handle, 
+					0, 
+					(int16_t*)m->b_rptr, 
+					&inlen, 
+					(int16_t*)om->b_wptr, 
+					&outlen);
+		}else{
+			speex_resampler_process_interleaved_int(dt->handle, 
+					(int16_t*)m->b_rptr, 
+					&inlen, 
+					(int16_t*)om->b_wptr, 
+					&outlen);
+		}
 		if (inlen_orig!=inlen){
 			ms_error("Bug in resampler ! only %u samples consumed instead of %u, out=%u",
 				inlen,inlen_orig,outlen);
 		}
-		om->b_wptr+=outlen*2;
+		om->b_wptr+=outlen*2*dt->nchannels;
 		mblk_set_timestamp_info(om,dt->ts);
 		dt->ts+=outlen;
@@ -190,26 +116,38 @@
 		freemsg(m);
 	}
+	ms_filter_unlock(obj);
 }
 
-#endif
 
-
-
-int ms_resample_set_sr(MSFilter *obj, void *arg){
-  ResampleData *dt=(ResampleData*)obj->data;
-  dt->input_rate=((int*)arg)[0];
-  return 0;
+static int ms_resample_set_sr(MSFilter *obj, void *arg){
+	ResampleData *dt=(ResampleData*)obj->data;
+	dt->input_rate=((int*)arg)[0];
+	return 0;
 }
 
-int ms_resample_set_output_sr(MSFilter *obj, void *arg){
-  ResampleData *dt=(ResampleData*)obj->data;
-  dt->output_rate=((int*)arg)[0];
-  return 0;
+static int ms_resample_set_output_sr(MSFilter *obj, void *arg){
+	ResampleData *dt=(ResampleData*)obj->data;
+	dt->output_rate=((int*)arg)[0];
+	return 0;
 }
 
-static MSFilterMethod enc_methods[]={
-  {	MS_FILTER_SET_SAMPLE_RATE	 ,	ms_resample_set_sr		},
-  {	MS_FILTER_SET_OUTPUT_SAMPLE_RATE ,	ms_resample_set_output_sr	},
-  {	0				 ,	NULL	}
+static int set_nchannels(MSFilter *f, void *arg){
+	ResampleData *dt=(ResampleData*)f->data;
+	int chans=*(int*)arg;
+	ms_filter_lock(f);
+	if (dt->nchannels!=chans && dt->handle!=NULL){
+		speex_resampler_destroy(dt->handle);
+		dt->handle=NULL;
+	}
+	dt->nchannels=*(int*)arg;
+	ms_filter_unlock(f);
+	return 0;
+}
+
+static MSFilterMethod methods[]={
+	{	MS_FILTER_SET_SAMPLE_RATE	 ,	ms_resample_set_sr		},
+	{	MS_FILTER_SET_OUTPUT_SAMPLE_RATE ,	ms_resample_set_output_sr	},
+	{ MS_FILTER_SET_NCHANNELS, set_nchannels },
+	{	0				 ,	NULL	}
 };
 
@@ -219,5 +157,5 @@
 	MS_RESAMPLE_ID,
 	"MSResample",
-	N_("frequency resampler"),
+	N_("Audio resampler"),
 	MS_FILTER_OTHER,
 	NULL,
@@ -229,5 +167,5 @@
 	NULL,
 	resample_uninit,
-	enc_methods
+	methods
 };
 
@@ -237,5 +175,5 @@
 	.id=MS_RESAMPLE_ID,
 	.name="MSResample",
-	.text=N_("frequency resampler"),
+	.text=N_("Audio resampler"),
 	.category=MS_FILTER_OTHER,
 	.ninputs=1,
@@ -244,5 +182,5 @@
 	.process=resample_process_ms2,
 	.uninit=resample_uninit,
-	.methods=enc_methods
+	.methods=methods
 };
 
Index: src/msspeex.c
===================================================================
--- src/msspeex.c	(revision 856)
+++ src/msspeex.c	(revision 984)
@@ -394,4 +394,5 @@
 	int penh;
 	int frsz;
+	uint32_t last_ts;
 	void *state;
 } DecState;
@@ -403,4 +404,5 @@
 	s->state=NULL;
 	s->penh=1;
+	s->last_ts=0;
 	f->data=s;
 }
@@ -458,33 +460,46 @@
 	mblk_t *im;
 	mblk_t *om;
-	int err;
-	int frame_per_packet;
+	int err=-2;
 	SpeexBits bits;
 	int bytes=s->frsz*2;
+	
 	speex_bits_init(&bits);
 	while((im=ms_queue_get(f->inputs[0]))!=NULL){
+		uint32_t curts=mblk_get_timestamp_info(im);
+		int rem_bits=(im->b_wptr-im->b_rptr)*8;
+		
 		speex_bits_reset(&bits);
 		speex_bits_read_from(&bits,(char*)im->b_rptr,im->b_wptr-im->b_rptr);
-		om=allocb(bytes*7,0);
-		/* support for multiple frame (max=7 frames???) in one RTP packet */
-        for (frame_per_packet=0;frame_per_packet<7;frame_per_packet++)
-        {
-            int i;
-			err=speex_decode_int(s->state,&bits,(int16_t*)(om->b_wptr+(frame_per_packet*320)));
-            
-            i = speex_bits_remaining(&bits);
-			if (i<10) /* this seems to work: don't know why. */
-                break;
-        }
-		if (err==0){
-			om->b_wptr+=bytes*(frame_per_packet+1);
-			ms_queue_put(f->outputs[0],om);
-		}else {
-			if (err==-1)
-				ms_warning("speex end of stream");
-			else if (err==-2)
-				ms_warning("speex corrupted stream");
-			freemsg(om);
+		
+
+#if 0 /* does not work very well, revisit later*/
+		/* packet loss concealment stuff, for one packet lost 
+			A better algorithm would be needed to accomodate more
+		*/
+		if (s->last_ts!=0){
+			int ts_diff=(int)(curts-s->last_ts);
+			if ( ts_diff>160){
+				ms_message("Packet missing, trying loss concealment, ts_diff=%i",ts_diff);
+				err=speex_decode_int(s->state,NULL,(int16_t*)om->b_wptr);
+				om->b_wptr+=320;
+			}
 		}
+#endif
+		/* support for multiple frame  in one RTP packet */
+ 		do{
+			om=allocb(bytes,0);
+			err=speex_decode_int(s->state,&bits,(int16_t*)om->b_wptr);
+			om->b_wptr+=bytes;
+			s->last_ts=curts+s->frsz;
+			if (err==0){
+				ms_queue_put(f->outputs[0],om);
+			}else {
+				if (err==-1)
+					ms_warning("speex end of stream");
+				else if (err==-2)
+					ms_warning("speex corrupted stream");
+				freemsg(om);
+			}
+		}while((rem_bits= speex_bits_remaining(&bits))>10);
 		freemsg(im);
 	}
Index: src/msvideo.c
===================================================================
--- src/msvideo.c	(revision 933)
+++ src/msvideo.c	(revision 969)
@@ -57,4 +57,7 @@
 		w=MS_VIDEO_SIZE_4CIF_W;
 		h=MS_VIDEO_SIZE_4CIF_H;
+	}else if (size==(MS_VIDEO_SIZE_W4CIF_W*MS_VIDEO_SIZE_W4CIF_H*3)/2){
+		w=MS_VIDEO_SIZE_W4CIF_W;
+		h=MS_VIDEO_SIZE_W4CIF_H;
 	}else if (size==(MS_VIDEO_SIZE_SVGA_W*MS_VIDEO_SIZE_SVGA_H*3)/2){
 		w=MS_VIDEO_SIZE_SVGA_W;
@@ -81,7 +84,13 @@
 		w=MS_VIDEO_SIZE_288P_W;
 		h=MS_VIDEO_SIZE_288P_H;
+	}else if (size==(MS_VIDEO_SIZE_432P_W*MS_VIDEO_SIZE_432P_H*3)/2){
+		w=MS_VIDEO_SIZE_432P_W;
+		h=MS_VIDEO_SIZE_432P_H;
 	}else if (size==(MS_VIDEO_SIZE_448P_W*MS_VIDEO_SIZE_448P_H*3)/2){
 		w=MS_VIDEO_SIZE_448P_W;
 		h=MS_VIDEO_SIZE_448P_H;
+	}else if (size==(MS_VIDEO_SIZE_480P_W*MS_VIDEO_SIZE_480P_H*3)/2){
+		w=MS_VIDEO_SIZE_480P_W;
+		h=MS_VIDEO_SIZE_480P_H;
 	}else if (size==(MS_VIDEO_SIZE_576P_W*MS_VIDEO_SIZE_576P_H*3)/2){
 		w=MS_VIDEO_SIZE_576P_W;
@@ -105,4 +114,7 @@
 		w=MS_VIDEO_SIZE_WXGA_W;
 		h=MS_VIDEO_SIZE_WXGA_H;
+	}else if (size==(MS_VIDEO_SIZE_WQCIF_W*MS_VIDEO_SIZE_WQCIF_H*3)/2){
+		w=MS_VIDEO_SIZE_WQCIF_W;
+		h=MS_VIDEO_SIZE_WQCIF_H;
 	}else if (size==(160*112*3)/2){/*format used by econf*/
 		w=160;
@@ -196,4 +208,24 @@
 	}
 	return ret;
+}
+
+void rgb24_mirror(uint8_t *buf, int w, int h, int linesize){
+	int i,j;
+	int r,g,b;
+	int end=w*3;
+	for(i=0;i<h;++i){
+		for(j=0;j<end/2;j+=3){
+			r=buf[j];
+			g=buf[j+1];
+			b=buf[j+2];
+			buf[j]=buf[end-j-3];
+			buf[j+1]=buf[end-j-2];
+			buf[j+2]=buf[end-j-1];
+			buf[end-j-3]=r;
+			buf[end-j-2]=g;
+			buf[end-j-1]=b;
+		}
+		buf+=linesize;
+	}
 }
 
Index: src/msvolume.c
===================================================================
--- src/msvolume.c	(revision 923)
+++ src/msvolume.c	(revision 999)
@@ -103,8 +103,12 @@
 }
 
+static float linear_to_db(float linear){
+	return 10*ortp_log10f(linear);
+}
+
 static int volume_get(MSFilter *f, void *arg){
 	float *farg=(float*)arg;
 	Volume *v=(Volume*)f->data;
-	*farg=10*ortp_log10f((v->energy+1)/max_e);
+	*farg=linear_to_db((v->energy+1)/max_e);
 	return 0;
 }
@@ -221,4 +225,11 @@
 }
 
+static int volume_get_gain_db(MSFilter *f, void *arg){
+	float *farg=(float*)arg;
+	Volume *v=(Volume*)f->data;
+	*farg = linear_to_db (v->gain);
+	return 0;
+}
+
 static int volume_get_ea_state(MSFilter *f, void *arg){
 	int *barg=(int*)arg;
@@ -393,5 +404,4 @@
 	{	MS_VOLUME_GET		,	volume_get		},
 	{	MS_VOLUME_GET_LINEAR	, 	volume_get_linear	},
-	{	MS_VOLUME_SET_GAIN	,	volume_set_gain		},
 	{	MS_VOLUME_GET_EA_STATE	, 	volume_get_ea_state	},
 	{	MS_VOLUME_SET_PEER	,	volume_set_peer		},
@@ -405,6 +415,8 @@
 	{	MS_VOLUME_SET_NOISE_GATE_THRESHOLD,	volume_set_noise_gate_threshold},
 	{	MS_VOLUME_SET_NOISE_GATE_FLOORGAIN,	volume_set_noise_gate_floorgain},
+	{	MS_VOLUME_SET_GAIN	,	volume_set_gain		},
 	{	MS_VOLUME_SET_DB_GAIN	,	volume_set_db_gain		},
 	{	MS_VOLUME_GET_GAIN	,	volume_get_gain		},
+	{	MS_VOLUME_GET_GAIN_DB , volume_get_gain_db },
 	{	0			,	NULL			}
 };
Index: src/mtu.c
===================================================================
--- src/mtu.c	(revision 856)
+++ src/mtu.c	(revision 971)
@@ -162,5 +162,4 @@
 	int err,mtu=0,new_mtu;
 	socklen_t optlen;
-	char buf[1500-28]={0};
 	char port[10];
 	struct addrinfo hints,*ai=NULL;
@@ -184,5 +183,9 @@
 	}
 	sock=socket(PF_INET,SOCK_DGRAM,0);
-
+	if(sock < 0)
+	{
+		ms_error("socket(): %s",strerror(errno));
+		return sock;
+	}
 	mtu=IP_PMTUDISC_DO;
 	optlen=sizeof(mtu);
@@ -204,7 +207,26 @@
 		return -1;
 	}
-	mtu=sizeof(buf);
+	mtu=1500-28;//was the size of the inital buf
 	do{
-		send(sock,buf,mtu,0);
+		int send_returned;
+		char *buf=ms_malloc(mtu);//avoid mtu greater than the beginning
+		if(buf == NULL)
+		{
+			ms_error("malloc(): %s",strerror(errno));
+			err = close(sock);
+			if (err!=0)
+				ms_error("close(): %s", strerror(errno));
+			return -1;
+		}
+		send_returned = send(sock,buf,mtu,0);
+		ms_free(buf);
+		if( send_returned < 0)//mtu actually change...
+		{
+			ms_error("send(): %s",strerror(errno));
+			err = close(sock);
+			if (err!=0)
+				ms_error("close(): %s", strerror(errno));
+			return -1;
+		}
 		usleep(500000);/*wait for an icmp message come back */
 		err=getsockopt(sock,IPPROTO_IP,IP_MTU,&new_mtu,&optlen);
Index: src/nowebcam.c
===================================================================
--- src/nowebcam.c	(revision 940)
+++ src/nowebcam.c	(revision 964)
@@ -1649,5 +1649,9 @@
 	uint8_t *jpgbuf;
 	int err;
+#ifndef WIN32
 	int fd=open(jpgpath,O_RDONLY);
+#else
+	int fd=open(jpgpath,O_RDONLY|O_BINARY);
+#endif
 	if (fd!=-1){
 		fstat(fd,&statbuf);
@@ -1669,5 +1673,5 @@
 		err=read(fd,jpgbuf,statbuf.st_size);
 		if (err!=statbuf.st_size){
-			  ms_error("Could not read as much as wanted !");
+			ms_error("Could not read as much as wanted: %i<>%i !",err,statbuf.st_size);
 		}
 		m=jpeg2yuv(jpgbuf,statbuf.st_size,reqsize);
Index: src/pulseaudio.c
===================================================================
--- src/pulseaudio.c	(revision 955)
+++ src/pulseaudio.c	(revision 955)
@@ -0,0 +1,333 @@
+/*
+mediastreamer2 library - modular sound and video processing and streaming
+Copyright (C) 2010  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 "mediastreamer2/msfilter.h"
+#include "mediastreamer2/mssndcard.h"
+
+#include <pulse/pulseaudio.h>
+
+static const float latency_req=0.02;
+
+static void init_pulse_context();
+static void pulse_card_detect(MSSndCardManager *m);
+static MSFilter *pulse_card_create_reader(MSSndCard *card);
+static MSFilter *pulse_card_create_writer(MSSndCard *card);
+
+
+static void pulse_card_init(MSSndCard *obj){
+	
+}
+
+static void pulse_card_uninit(MSSndCard *obj){
+	
+}
+
+
+MSSndCardDesc pulse_card_desc={
+	.driver_type="PulseAudio",
+	.detect=pulse_card_detect,
+	.init=pulse_card_init,
+	.create_reader=pulse_card_create_reader,
+	.create_writer=pulse_card_create_writer,
+	.uninit=pulse_card_uninit
+};
+
+
+static void pulse_card_detect(MSSndCardManager *m){
+	MSSndCard *card=ms_snd_card_new(&pulse_card_desc);
+	if (card!=NULL){
+		card->name=ms_strdup("default");
+		card->capabilities=MS_SND_CARD_CAP_CAPTURE|MS_SND_CARD_CAP_PLAYBACK;
+		ms_snd_card_manager_add_card(m,card);
+		init_pulse_context();
+	}
+}
+
+static pa_context *context=NULL;
+static pa_threaded_mainloop *pa_loop=NULL;
+static bool_t pa_ready=FALSE;
+
+static void uninit_pulse_context(){
+	pa_context_disconnect(context);
+	pa_context_unref(context);
+	pa_threaded_mainloop_stop(pa_loop);
+}
+
+static void state_notify(pa_context *ctx, void *userdata){
+	pa_context_state_t state=pa_context_get_state(ctx);
+	const char *sname="";
+	switch (state){
+		case PA_CONTEXT_UNCONNECTED:
+			sname="PA_CONTEXT_UNCONNECTED";
+		break;
+		case PA_CONTEXT_CONNECTING:
+			sname="PA_CONTEXT_CONNECTING";
+		break;
+		case PA_CONTEXT_AUTHORIZING:
+			sname="PA_CONTEXT_AUTHORIZING";
+		break;
+		case PA_CONTEXT_SETTING_NAME:
+			sname="PA_CONTEXT_SETTING_NAME";
+		break;
+		case PA_CONTEXT_READY:
+			sname="PA_CONTEXT_READY";
+			pa_ready=TRUE;
+		break;
+		case PA_CONTEXT_FAILED:
+			sname="PA_CONTEXT_FAILED";
+		break;
+		case PA_CONTEXT_TERMINATED:
+			sname="PA_CONTEXT_TERMINATED";
+		break;
+	}
+	ms_message("New PulseAudio context state: %s",sname);
+}
+
+static void init_pulse_context(){
+	if (context==NULL){
+		pa_loop=pa_threaded_mainloop_new();
+		context=pa_context_new(pa_threaded_mainloop_get_api(pa_loop),NULL);
+		pa_context_set_state_callback(context,state_notify,NULL);	
+		pa_context_connect(context,NULL,0,NULL);
+		pa_threaded_mainloop_start(pa_loop);
+		atexit(uninit_pulse_context);
+	}
+}
+
+static void check_pulse_ready(){
+	int i=0;
+	while(pa_ready==FALSE && i<10){
+		usleep(20000);
+	}
+}
+
+typedef struct _PulseReadState{
+	int channels;
+	int rate;
+	pa_stream *stream;
+	int fragsize;
+}PulseReadState;
+
+static void pulse_read_init(MSFilter *f){
+	PulseReadState *s=ms_new0(PulseReadState,1);
+	
+	s->channels=1;
+	s->rate=8000;
+	f->data=s;
+	check_pulse_ready();
+}
+
+static void pulse_read_preprocess(MSFilter *f){
+	PulseReadState *s=(PulseReadState *)f->data;
+	int err;
+	pa_sample_spec pss;
+	pa_buffer_attr attr;
+
+	if (context==NULL) return;
+	
+	pss.format=PA_SAMPLE_S16LE;
+	pss.channels=s->channels;
+	pss.rate=s->rate;
+	
+	attr.maxlength=-1;
+	attr.tlength=-1;
+	attr.prebuf=-1;
+	attr.minreq=-1;
+	attr.fragsize=s->fragsize=latency_req*(float)s->channels*(float)s->rate*2;
+	
+	s->stream=pa_stream_new(context,"phone",&pss,NULL);
+	if (s->stream==NULL){
+		ms_error("pa_stream_new() failed: %s",pa_strerror(pa_context_errno(context)));
+		return;
+	}
+	pa_threaded_mainloop_lock(pa_loop);
+	err=pa_stream_connect_record(s->stream,NULL,&attr, PA_STREAM_ADJUST_LATENCY);
+	pa_threaded_mainloop_unlock(pa_loop);
+	if (err!=0){
+		ms_error("pa_stream_connect_record() failed");
+	}
+}
+
+
+static void pulse_read_process(MSFilter *f){
+	PulseReadState *s=(PulseReadState *)f->data;
+	const void *buffer=NULL;
+	size_t nbytes=0;
+	
+	if (s->stream!=NULL){
+		pa_threaded_mainloop_lock(pa_loop);
+		while (pa_stream_peek(s->stream,&buffer,&nbytes)==0 && nbytes>0){
+			mblk_t *om;
+			om=allocb(nbytes,0);
+			memcpy(om->b_wptr,buffer,nbytes);
+			om->b_wptr+=nbytes;
+			ms_queue_put(f->outputs[0],om);
+			nbytes=0;
+			pa_stream_drop(s->stream);
+		}
+		pa_threaded_mainloop_unlock(pa_loop);
+	}
+}
+
+
+static void pulse_read_postprocess(MSFilter *f){
+	PulseReadState *s=(PulseReadState *)f->data;
+	
+	if (s->stream) {
+		pa_threaded_mainloop_lock(pa_loop);
+		pa_stream_disconnect(s->stream);
+		pa_stream_unref(s->stream);
+		pa_threaded_mainloop_unlock(pa_loop);
+	}
+}
+
+static int pulse_read_set_sr(MSFilter *f, void *arg){
+	PulseReadState *s=(PulseReadState *)f->data;
+	s->rate=*(int*)arg;
+	return 0;
+}
+
+static int pulse_read_get_sr(MSFilter *f, void *arg){
+	PulseReadState *s=(PulseReadState *)f->data;
+	*(int*)arg=s->rate;
+	return 0;
+}
+
+static int pulse_read_set_nchannels(MSFilter *f, void *arg){
+	PulseReadState *s=(PulseReadState *)f->data;
+	s->channels=*(int*)arg;
+	return 0;
+}
+
+static MSFilterMethod pulse_read_methods[]={
+	{	MS_FILTER_SET_SAMPLE_RATE , pulse_read_set_sr },
+	{	MS_FILTER_GET_SAMPLE_RATE , pulse_read_get_sr },
+	{	MS_FILTER_SET_NCHANNELS	, pulse_read_set_nchannels },
+	{	0	, 0 }
+};
+
+static MSFilterDesc pulse_read_desc={
+	.id=MS_PULSE_READ_ID,
+	.name="MSPulseRead",
+	.text="Sound input plugin based on PulseAudio",
+	.ninputs=0,
+	.noutputs=1,
+	.category=MS_FILTER_OTHER,
+	.init=pulse_read_init,
+	.preprocess=pulse_read_preprocess,
+	.process=pulse_read_process,
+	.postprocess=pulse_read_postprocess,
+	.methods=pulse_read_methods
+};
+
+
+typedef struct _PulseReadState PulseWriteState;
+
+static void pulse_write_init(MSFilter *f){
+	PulseWriteState *s=ms_new0(PulseWriteState,1);
+	s->rate=8000;
+	s->channels=1;
+	f->data=s;
+	check_pulse_ready();
+}
+
+static void pulse_write_preprocess(MSFilter *f){
+	PulseWriteState *s=(PulseWriteState*)f->data;
+	int err;
+	pa_sample_spec pss;
+	pa_buffer_attr attr;
+
+	if (context==NULL) return;
+	
+	pss.format=PA_SAMPLE_S16LE;
+	pss.channels=s->channels;
+	pss.rate=s->rate;
+
+	s->fragsize=latency_req*(float)s->channels*(float)s->rate*2;
+	
+	attr.maxlength=-1;
+	attr.tlength=s->fragsize;
+	attr.prebuf=-1;
+	attr.minreq=-1;
+	attr.fragsize=-1;
+	
+	s->stream=pa_stream_new(context,"phone",&pss,NULL);
+	if (s->stream==NULL){
+		ms_error("pa_stream_new() failed: %s",pa_strerror(pa_context_errno(context)));
+		return;
+	}
+	pa_threaded_mainloop_lock(pa_loop);
+	err=pa_stream_connect_playback(s->stream,NULL,&attr, PA_STREAM_ADJUST_LATENCY,NULL,NULL);
+	pa_threaded_mainloop_unlock(pa_loop);
+	if (err!=0){
+		ms_error("pa_stream_connect_playback() failed");
+	}
+}
+
+static void pulse_write_process(MSFilter *f){
+	PulseWriteState *s=(PulseWriteState*)f->data;
+	mblk_t *im;
+	while((im=ms_queue_get(f->inputs[0]))!=NULL){
+		int bsize=msgdsize(im);
+		if (s->stream){
+			pa_threaded_mainloop_lock(pa_loop);
+			if (pa_stream_writable_size(s->stream)>=bsize){
+				//ms_message("Pushing data to pulseaudio");
+				pa_stream_write(s->stream,im->b_rptr,bsize,NULL,0,PA_SEEK_RELATIVE);
+			}
+			pa_threaded_mainloop_unlock(pa_loop);
+		}
+		freemsg(im);
+	}
+}
+
+static void pulse_write_postprocess(MSFilter *f){
+	PulseWriteState *s=(PulseWriteState*)f->data;
+	if (s->stream) {
+		pa_threaded_mainloop_lock(pa_loop);
+		pa_stream_disconnect(s->stream);
+		pa_stream_unref(s->stream);
+		pa_threaded_mainloop_unlock(pa_loop);
+	}
+}
+
+static MSFilterDesc pulse_write_desc={
+	.id=MS_PULSE_WRITE_ID,
+	.name="MSPulseWrite",
+	.text="Sound output plugin based on PulseAudio",
+	.ninputs=1,
+	.noutputs=0,
+	.category=MS_FILTER_OTHER,
+	.init=pulse_write_init,
+	.preprocess=pulse_write_preprocess,
+	.process=pulse_write_process,
+	.postprocess=pulse_write_postprocess,
+	.methods=pulse_read_methods
+};
+
+static MSFilter *pulse_card_create_reader(MSSndCard *card)
+{
+	return ms_filter_new_from_desc (&pulse_read_desc);
+}
+
+static MSFilter *pulse_card_create_writer(MSSndCard *card)
+{
+	return ms_filter_new_from_desc (&pulse_write_desc);
+}
+
Index: src/speexec.c
===================================================================
--- src/speexec.c	(revision 856)
+++ src/speexec.c	(revision 1003)
@@ -1,5 +1,6 @@
 /*
 mediastreamer2 library - modular sound and video processing and streaming
-Copyright (C) 2006  Simon MORLAT (simon.morlat@linphone.org)
+Copyright (C) 2010  Belledonne Communications SARL 
+Author: Simon Morlat <simon.morlat@linphone.org>
 
 This program is free software; you can redistribute it and/or
@@ -18,6 +19,5 @@
 */
 
-#include "mediastreamer2/msspeexec.h"
-
+#include "mediastreamer2/msfilter.h"
 #include <speex/speex_echo.h>
 #include <speex/speex_preprocess.h>
@@ -32,19 +32,16 @@
 
 static const int framesize=128;
-static const int filter_length=2048; /*250 ms*/
+static const int ref_max_delay=80;
 
 typedef struct SpeexECState{
 	SpeexEchoState *ecstate;
-	MSBufferizer speak_delay;
-	int size_delay;
-	int playback_delay;
-	MSBufferizer in[2];
+	SpeexPreprocessState *den;
+	MSBufferizer ref;
+	MSBufferizer delayed_ref;
+	MSBufferizer echo;
+	int ref_bytes_limit;
 	int framesize;
 	int filterlength;
 	int samplerate;
-	SpeexPreprocessState *den;
-        int ref;
-        int echo;
-        int out;
 	int delay_ms;
 	int tail_length_ms;
@@ -55,16 +52,11 @@
 
 	s->samplerate=8000;
+	ms_bufferizer_init(&s->ref);
+	ms_bufferizer_init(&s->delayed_ref);
+	ms_bufferizer_init(&s->echo);
+	s->delay_ms=0;
+	s->tail_length_ms=250;
+	s->ecstate=NULL;
 	s->framesize=framesize;
-	s->filterlength=filter_length;
-
-	ms_bufferizer_init(&s->speak_delay);
-	s->size_delay=0;
-	s->delay_ms=0;
-	s->playback_delay=0;
-	s->tail_length_ms=250;
-
-	ms_bufferizer_init(&s->in[0]);
-	ms_bufferizer_init(&s->in[1]);
-	s->ecstate=NULL;
 	s->den = NULL;
 
@@ -74,11 +66,7 @@
 static void speex_ec_uninit(MSFilter *f){
 	SpeexECState *s=(SpeexECState*)f->data;
-	ms_bufferizer_uninit(&s->speak_delay);
-	ms_bufferizer_uninit(&s->in[0]);
-	ms_bufferizer_uninit(&s->in[1]);
-	if (s->ecstate!=NULL)
-		speex_echo_state_destroy(s->ecstate);
-	if (s->den!=NULL)
-		speex_preprocess_state_destroy(s->den);
+	ms_bufferizer_uninit(&s->ref);
+	ms_bufferizer_uninit(&s->delayed_ref);
+	ms_bufferizer_uninit(&s->delayed_ref);
 
 	ms_free(s);
@@ -88,19 +76,19 @@
 static void speex_ec_preprocess(MSFilter *f){
 	SpeexECState *s=(SpeexECState*)f->data;
-	if (s->ecstate!=NULL)
-		speex_echo_state_destroy(s->ecstate);
-	if (s->den!=NULL)
-		speex_preprocess_state_destroy(s->den);
-
-	if (s->tail_length_ms!=0)
-		s->filterlength=(s->tail_length_ms*s->samplerate)/1000;
-	if (s->delay_ms!=0)
-		s->playback_delay=s->delay_ms*s->samplerate/1000;
-	ms_message("Initializing speex echo canceler with framesize=%i, filterlength=%i, playback_delay=%i",
-		s->framesize,s->filterlength,s->playback_delay);
+	int delay_samples=0;
+	mblk_t *m;
+
+	s->filterlength=(s->tail_length_ms*s->samplerate)/1000;
+	delay_samples=s->delay_ms*s->samplerate/1000;
+	ms_message("Initializing speex echo canceler with framesize=%i, filterlength=%i, delay_samples=%i",
+		s->framesize,s->filterlength,delay_samples);
+	s->ref_bytes_limit=(2*ref_max_delay*s->samplerate)/1000;
 	s->ecstate=speex_echo_state_init(s->framesize,s->filterlength);
 	s->den = speex_preprocess_state_init(s->framesize, s->samplerate);
 	speex_echo_ctl(s->ecstate, SPEEX_ECHO_SET_SAMPLING_RATE, &s->samplerate);
 	speex_preprocess_ctl(s->den, SPEEX_PREPROCESS_SET_ECHO_STATE, s->ecstate);
+	/* fill with zeroes for the time of the delay*/
+	m=allocb(delay_samples*2,0);
+	ms_bufferizer_put (&s->delayed_ref,m);
 }
 
@@ -112,134 +100,54 @@
 	SpeexECState *s=(SpeexECState*)f->data;
 	int nbytes=s->framesize*2;
-	uint8_t *in1;
-	mblk_t *om0,*om1;
-#ifdef AMD_WIN32_HACK
-	static int count=0;
-#endif
-	mblk_t *m;
-	mblk_t *md;	
-
-	/* first fill delayed buffer until playback delay is reached (only in first n calls) */
-	if (s->size_delay<s->playback_delay){
-		while((m=ms_queue_get(f->inputs[0]))!=NULL && s->size_delay<s->playback_delay){
-			// Duplicate queue : one to write to the output speaker, the other will be delayed for AEC
-			int size=msgdsize(m);
-			md = copyb(m);
-			s->size_delay = s->size_delay + size;
-			ms_bufferizer_put(&s->speak_delay,md);
-			ms_bufferizer_put(&s->in[0],m);
+	mblk_t *refm;
+	int ref_samples=0;
+	uint8_t *ref,*echo;
+	
+	if (f->inputs[0]!=NULL){
+		while((refm=ms_queue_get(f->inputs[0]))!=NULL){
+			mblk_t *cp=copymsg(refm);
+			ref_samples+=msgdsize(refm)/2;
+			ms_bufferizer_put(&s->ref,refm);
+			ms_bufferizer_put(&s->delayed_ref,cp);
 		}
-
-		if (s->size_delay<=s->playback_delay)
-		{
-			/* make sure we always send block with same size */
-			while (ms_bufferizer_get_avail(&s->speak_delay)>=nbytes)
-			{
-				om0=allocb(nbytes,0);
-				ms_bufferizer_read(&s->speak_delay,(uint8_t*)om0->b_wptr,nbytes);
-				om0->b_wptr+=nbytes;
-				ms_queue_put(f->outputs[0],om0);
-			}
-
-			/* make sure we always send block with same size */
-			ms_bufferizer_put_from_queue(&s->in[1],f->inputs[1]);
-			while (ms_bufferizer_get_avail(&s->in[1])>=nbytes)
-			{
-				om0=allocb(nbytes,0);
-				ms_bufferizer_read(&s->in[1],(uint8_t*)om0->b_wptr,nbytes);
-				om0->b_wptr+=nbytes;
-    				ms_queue_put(f->outputs[1],om0);
-			}
-			/* we are now equal and speaker is delayed */
-			return;
+	}
+	if (f->inputs[1]!=NULL){
+		ms_bufferizer_put_from_queue (&s->echo,f->inputs[1]);
+	}
+	
+	ref=(uint8_t*)alloca(nbytes);
+	echo=(uint8_t*)alloca(nbytes);
+	while (ms_bufferizer_read(&s->echo,echo,nbytes)>=nbytes){
+		mblk_t *oref=allocb(nbytes,0);
+		mblk_t *oecho=allocb(nbytes,0);
+		if (ms_bufferizer_read(&s->ref,oref->b_wptr,nbytes)==0){
+			memset(ref,0,nbytes);
+			memset(oref->b_wptr,0,nbytes);
+			/*missing data, use silence instead*/
+			ms_warning("No ref samples, using silence instead");
+		}else{
+			ms_bufferizer_read(&s->delayed_ref,ref,nbytes);
 		}
-	}
-
-	ms_bufferizer_put_from_queue(&s->in[1],f->inputs[1]);
-
-	/*read input and put in bufferizers*/
-	while((m=ms_queue_get(f->inputs[0]))!=NULL){
-		md = copyb(m);
-		// Duplicate queue : one to write to the output speaker, the other will be delayed for AEC
-		ms_bufferizer_put(&s->in[0],m);
-		ms_bufferizer_put(&s->speak_delay,md);	
-	}
-	
-
-	in1=(uint8_t*)alloca(nbytes);
-
-	//ms_debug("speexec:  in0=%i, in1=%i",ms_bufferizer_get_avail(&s->in[0]),ms_bufferizer_get_avail(&s->in[1]));
-
-	while (ms_bufferizer_get_avail(&s->speak_delay)>=nbytes && ms_bufferizer_get_avail(&s->in[1])>=nbytes){
-		om0=allocb(nbytes,0);
-		ms_bufferizer_read(&s->speak_delay,(uint8_t*)om0->b_wptr,nbytes);
-		om0->b_wptr+=nbytes;
-		ms_queue_put(f->outputs[0],om0);
-
-		om0=allocb(nbytes,0);
-		ms_bufferizer_read(&s->in[0],(uint8_t*)om0->b_wptr,nbytes);
-		/* we have reference signal */
-		/* the reference signal is sent through outputs[0]*/
-		
-		om0->b_wptr+=nbytes;
-		//ms_queue_put(f->outputs[0],om0);
-
-		ms_bufferizer_read(&s->in[1],in1,nbytes);
-		/* we have echo signal */
-		om1=allocb(nbytes,0);
-		speex_echo_cancellation(s->ecstate,(short*)in1,(short*)om0->b_rptr,(short*)om1->b_wptr);
-		speex_preprocess_run(s->den, (short*)om1->b_wptr);
-		ms_filter_notify(f, MS_SPEEX_EC_ECHO_STATE, (void*)s->ecstate);
-		ms_filter_notify(f, MS_SPEEX_EC_PREPROCESS_MIC, (void*)s->den);
-
-		om1->b_wptr+=nbytes;
-		ms_queue_put(f->outputs[1],om1);
-#ifdef AMD_WIN32_HACK
-		count++;
-		if (count==100*3)
-		{
-			ms_message("periodic reset of echo canceller.");
-			speex_echo_state_reset(s->ecstate);
-			count=0;
-		}		
-#endif
-		freeb(om0);
-	}
-
-	if (ms_bufferizer_get_avail(&s->speak_delay)> 5*320*(s->samplerate/8000)) /* above 4*20ms -> useless */
-	{
+		oref->b_wptr+=nbytes;
+		ms_queue_put(f->outputs[0],oref);
+		speex_echo_cancellation(s->ecstate,(short*)echo,(short*)ref,(short*)oecho->b_wptr);
+		speex_preprocess_run(s->den, (short*)oecho->b_wptr);
+		oecho->b_wptr+=nbytes;
+		ms_queue_put(f->outputs[1],oecho);
+	}
+	/* do not accumulate too much reference signal */
+	if (ms_bufferizer_get_avail(&s->ref)> s->ref_bytes_limit) {
 		/* reset evrything */
-		ms_warning("speexec: -reset of echo canceller- in0=%i, in1=%i",ms_bufferizer_get_avail(&s->in[0]),ms_bufferizer_get_avail(&s->in[1]));
-		flushq(&s->in[1].q,0);
-		flushq(&s->in[0].q,0);
-		flushq(&s->speak_delay.q,0);
-		ms_bufferizer_init(&s->in[0]);
-		ms_bufferizer_init(&s->in[1]);
-		ms_bufferizer_init(&s->speak_delay);
-		s->size_delay=0;
-		speex_echo_state_reset(s->ecstate);
-	}
-
-	while (ms_bufferizer_get_avail(&s->in[1])> 5*320*(s->samplerate/8000)){
-		om1=allocb(nbytes,0);
-		ms_bufferizer_read(&s->in[1],(uint8_t*)om1->b_wptr,nbytes);
-		om1->b_wptr+=nbytes;
-		ms_queue_put(f->outputs[1],om1);
-		ms_message("too much echo signal, sending anyway.");
-		speex_echo_state_reset(s->ecstate);
-	}
-	
+		ms_warning("purging ref signal");
+		ms_bufferizer_flush(&s->ref);
+		ms_bufferizer_flush(&s->delayed_ref);
+	}
 }
 
 static void speex_ec_postprocess(MSFilter *f){
 	SpeexECState *s=(SpeexECState*)f->data;
-	ms_bufferizer_uninit(&s->in[0]);
-	ms_bufferizer_uninit(&s->in[1]);
-	ms_bufferizer_uninit(&s->speak_delay);
-	ms_bufferizer_init(&s->in[0]);
-	ms_bufferizer_init(&s->in[1]);
-	ms_bufferizer_init(&s->speak_delay);
-	s->size_delay=0;
-
+	ms_bufferizer_flush (&s->ref);
+	ms_bufferizer_flush (&s->delayed_ref);
+	ms_bufferizer_flush (&s->echo);
 	if (s->ecstate!=NULL){
 		speex_echo_state_destroy(s->ecstate);
@@ -254,17 +162,5 @@
 static int speex_ec_set_sr(MSFilter *f, void *arg){
 	SpeexECState *s=(SpeexECState*)f->data;
-
 	s->samplerate = *(int*)arg;
-
-	if (s->ecstate!=NULL){
-		speex_echo_state_destroy(s->ecstate);
-		if (s->den!=NULL)
-			speex_preprocess_state_destroy(s->den);
-	
-		s->ecstate=speex_echo_state_init(s->framesize,s->filterlength);
-		s->den = speex_preprocess_state_init(s->framesize, s->samplerate);
-		speex_echo_ctl(s->ecstate, SPEEX_ECHO_SET_SAMPLING_RATE, &s->samplerate);
-		speex_preprocess_ctl(s->den, SPEEX_PREPROCESS_SET_ECHO_STATE, s->ecstate);
-	}
 	return 0;
 }
@@ -273,35 +169,8 @@
 	SpeexECState *s=(SpeexECState*)f->data;
 	s->framesize = *(int*)arg;
-
-	if (s->ecstate!=NULL){
-		speex_echo_state_destroy(s->ecstate);
-		if (s->den!=NULL)
-			speex_preprocess_state_destroy(s->den);
-	
-		s->ecstate=speex_echo_state_init(s->framesize,s->filterlength);
-		s->den = speex_preprocess_state_init(s->framesize, s->samplerate);
-		speex_echo_ctl(s->ecstate, SPEEX_ECHO_SET_SAMPLING_RATE, &s->samplerate);
-		speex_preprocess_ctl(s->den, SPEEX_PREPROCESS_SET_ECHO_STATE, s->ecstate);
-	}
-	return 0;
-}
-
-static int speex_ec_set_filterlength(MSFilter *f, void *arg){
-	SpeexECState *s=(SpeexECState*)f->data;
-	s->filterlength = (*(int*)arg)*(s->samplerate/8000);
-	s->tail_length_ms=0;/*trust the length in sample, not the length in milliseconds*/
-	if (s->ecstate!=NULL)
-		speex_echo_state_destroy(s->ecstate);
-	if (s->den!=NULL)
-	  speex_preprocess_state_destroy(s->den);
-
-	s->ecstate=speex_echo_state_init(s->framesize,s->filterlength);
-	s->den = speex_preprocess_state_init(s->framesize, s->samplerate);
-	speex_echo_ctl(s->ecstate, SPEEX_ECHO_SET_SAMPLING_RATE, &s->samplerate);
-	speex_preprocess_ctl(s->den, SPEEX_PREPROCESS_SET_ECHO_STATE, s->ecstate);
-	return 0;
-}
-
-static int speex_ec_set_delay2(MSFilter *f, void *arg){
+	return 0;
+}
+
+static int speex_ec_set_delay(MSFilter *f, void *arg){
 	SpeexECState *s=(SpeexECState*)f->data;
 	s->delay_ms = *(int*)arg;
@@ -309,5 +178,5 @@
 }
 
-static int speex_ec_set_tail_length2(MSFilter *f, void *arg){
+static int speex_ec_set_tail_length(MSFilter *f, void *arg){
 	SpeexECState *s=(SpeexECState*)f->data;
 	s->tail_length_ms=*(int*)arg;
@@ -315,29 +184,10 @@
 }
 
-static int speex_ec_set_playbackdelay(MSFilter *f, void *arg){
-	SpeexECState *s=(SpeexECState*)f->data;	
-	s->playback_delay = *(int*)arg;
-
-	flushq(&s->in[1].q,0);
-	flushq(&s->in[0].q,0);
-	flushq(&s->speak_delay.q,0);
-	ms_bufferizer_init(&s->in[0]);
-	ms_bufferizer_init(&s->in[1]);
-	ms_bufferizer_init(&s->speak_delay);
-	s->size_delay=0;
-	speex_echo_state_reset(s->ecstate);
-	return 0;
-}
 
 static MSFilterMethod speex_ec_methods[]={
 	{	MS_FILTER_SET_SAMPLE_RATE, speex_ec_set_sr },
-	{	MS_SPEEX_EC_SET_TAIL_LENGTH	,	speex_ec_set_tail_length2	},
-	{	MS_SPEEX_EC_SET_DELAY		,	speex_ec_set_delay2		},
-	{	MS_SPEEX_EC_SET_FRAME_SIZE	,	speex_ec_set_framesize		},
-/*these are kept for backward compatibility */
-	{	MS_FILTER_SET_FRAMESIZE, speex_ec_set_framesize },
-	{	MS_FILTER_SET_FILTERLENGTH, speex_ec_set_filterlength },
-	{	MS_FILTER_SET_PLAYBACKDELAY, speex_ec_set_playbackdelay },
-	{	0			, NULL}
+	{	MS_ECHO_CANCELLER_SET_TAIL_LENGTH	,	speex_ec_set_tail_length	},
+	{	MS_ECHO_CANCELLER_SET_DELAY		,	speex_ec_set_delay		},
+	{	MS_ECHO_CANCELLER_SET_FRAMESIZE	,	speex_ec_set_framesize		},
 };
 
Index: src/videoout.c
===================================================================
--- src/videoout.c	(revision 931)
+++ src/videoout.c	(revision 976)
@@ -1801,4 +1801,13 @@
 	{	MS_VIDEO_OUT_SET_BACKGROUND_COLOR    ,  video_out_set_background_color},
 	{	MS_VIDEO_OUT_GET_BACKGROUND_COLOR    ,  video_out_get_background_color},
+/* methods for compatibility with the MSVideoDisplay interface*/
+	{	MS_VIDEO_DISPLAY_SET_LOCAL_VIEW_MODE , video_out_set_corner },
+	{	MS_VIDEO_DISPLAY_ENABLE_AUTOFIT			, video_out_auto_fit },
+	{	MS_VIDEO_DISPLAY_ENABLE_MIRRORING		, video_out_enable_mirroring },
+	{	MS_VIDEO_DISPLAY_GET_NATIVE_WINDOW_ID	, video_out_get_native_window_id },
+	{	MS_VIDEO_DISPLAY_SET_LOCAL_VIEW_SCALEFACTOR	, video_out_set_scalefactor },
+	{	MS_VIDEO_DISPLAY_SET_SELFVIEW_POS 	 ,	video_out_set_selfview_pos},
+	{	MS_VIDEO_DISPLAY_GET_SELFVIEW_POS    ,  video_out_get_selfview_pos},
+	{	MS_VIDEO_DISPLAY_SET_BACKGROUND_COLOR    ,  video_out_set_background_color},
 	
 	{	0	,NULL}
Index: src/videostream.c
===================================================================
--- src/videostream.c	(revision 935)
+++ src/videostream.c	(revision 997)
@@ -23,5 +23,5 @@
 #include "mediastreamer2/msrtp.h"
 #include "mediastreamer2/msvideoout.h"
-
+#include "mediastreamer2/msextdisplay.h"
 
 #ifdef HAVE_CONFIG_H
@@ -48,4 +48,6 @@
 	if (stream->output != NULL)
 		ms_filter_destroy (stream->output);
+	if (stream->encoder != NULL)
+		ms_filter_destroy (stream->encoder);
 	if (stream->decoder != NULL)
 		ms_filter_destroy (stream->decoder);
@@ -138,9 +140,9 @@
 
 void video_stream_iterate(VideoStream *stream){
-	
+	/*
 	if (stream->output!=NULL)
 		ms_filter_call_method_noarg(stream->output,
 			MS_VIDEO_OUT_HANDLE_RESIZING);
-	
+	*/
 	if (stream->evq){
 		OrtpEvent *ev=ortp_ev_queue_get(stream->evq);
@@ -185,5 +187,5 @@
 	stream->corner=val ? 0 : -1;
 	if (out){
-		ms_filter_call_method(out,MS_VIDEO_OUT_SET_CORNER,&stream->corner);
+		ms_filter_call_method(out,MS_VIDEO_DISPLAY_SET_LOCAL_VIEW_MODE,&stream->corner);
 	}
 }
@@ -191,4 +193,19 @@
 void video_stream_enable_adaptive_bitrate_control(VideoStream *s, bool_t yesno){
 	s->adapt_bitrate=yesno;
+}
+
+void video_stream_set_render_callback (VideoStream *s, VideoStreamRenderCallback cb, void *user_pointer){
+	s->rendercb=cb;
+	s->render_pointer=user_pointer;
+}
+
+static void ext_display_cb(void *ud, unsigned int event, void *eventdata){
+	MSExtDisplayOutput *output=(MSExtDisplayOutput*)eventdata;
+	VideoStream *st=(VideoStream*)ud;
+	if (st->rendercb!=NULL){
+		st->rendercb(st->render_pointer,
+		            output->local_view.w!=0 ? &output->local_view : NULL,
+		            output->remote_view.w!=0 ? &output->remote_view : NULL);
+	}
 }
 
@@ -245,7 +262,18 @@
 	stream->source = ms_web_cam_create_reader(cam);
 	stream->tee = ms_filter_new(MS_TEE_ID);
-	stream->output=ms_filter_new(MS_VIDEO_OUT_ID);
+
+	if (stream->rendercb!=NULL){
+		stream->output=ms_filter_new(MS_EXT_DISPLAY_ID);
+		ms_filter_set_notify_callback (stream->output,ext_display_cb,stream);
+	}else{
+#ifndef WIN32
+		stream->output=ms_filter_new(MS_VIDEO_OUT_ID);
+#else
+		stream->output=ms_filter_new(MS_DRAWDIB_DISPLAY_ID);
+#endif
+	}
+
 	stream->sizeconv=ms_filter_new(MS_SIZE_CONV_ID);
-	
+
 	if (pt->normal_bitrate>0){
 		ms_message("Limiting bitrate of video encoder to %i bits/s",pt->normal_bitrate);
@@ -263,5 +291,6 @@
 	ms_message("Setting vsize=%ix%i, fps=%f",vsize.width,vsize.height,fps);
 	/* configure the filters */
-	ms_filter_call_method(stream->source,MS_FILTER_SET_FPS,&fps);
+	if (ms_filter_get_id(stream->source)!=MS_STATIC_IMAGE_ID)
+		ms_filter_call_method(stream->source,MS_FILTER_SET_FPS,&fps);
 	ms_filter_call_method(stream->source,MS_FILTER_SET_VIDEO_SIZE,&vsize);
 
@@ -290,7 +319,7 @@
 	tmp=1;
 	ms_filter_call_method(stream->output,MS_FILTER_SET_VIDEO_SIZE,&disp_size);
-	ms_filter_call_method(stream->output,MS_VIDEO_OUT_AUTO_FIT,&tmp);
+	ms_filter_call_method(stream->output,MS_VIDEO_DISPLAY_ENABLE_AUTOFIT,&tmp);
 	ms_filter_call_method(stream->output,MS_FILTER_SET_PIX_FMT,&format);
-	ms_filter_call_method(stream->output,MS_VIDEO_OUT_SET_CORNER,&stream->corner);
+	ms_filter_call_method(stream->output,MS_VIDEO_DISPLAY_SET_LOCAL_VIEW_MODE,&stream->corner);
 
 	if (pt->recv_fmtp!=NULL)
@@ -353,5 +382,5 @@
 	unsigned long id;
 	if (stream->output){
-		if (ms_filter_call_method(stream->output,MS_VIDEO_OUT_GET_NATIVE_WINDOW_ID,&id)==0)
+		if (ms_filter_call_method(stream->output,MS_VIDEO_DISPLAY_GET_NATIVE_WINDOW_ID,&id)==0)
 			return id;
 	}
@@ -370,10 +399,16 @@
 	/* creates the filters */
 	stream->source = ms_web_cam_create_reader(device);
+
+#ifndef WIN32
 	stream->output = ms_filter_new(MS_VIDEO_OUT_ID);
+#else
+	stream->output = ms_filter_new(MS_DRAWDIB_DISPLAY_ID);
+#endif
 
 
 	/* configure the filters */
 	ms_filter_call_method(stream->source,MS_FILTER_SET_VIDEO_SIZE,&vsize);
-	ms_filter_call_method(stream->source,MS_FILTER_SET_FPS,&fps);
+	if (ms_filter_get_id(stream->source)!=MS_STATIC_IMAGE_ID)
+		ms_filter_call_method(stream->source,MS_FILTER_SET_FPS,&fps);
 	ms_filter_call_method(stream->source,MS_FILTER_GET_PIX_FMT,&format);
 	ms_filter_call_method(stream->source,MS_FILTER_GET_VIDEO_SIZE,&vsize);
@@ -389,6 +424,6 @@
 	ms_filter_call_method(stream->output,MS_FILTER_SET_PIX_FMT,&format);
 	ms_filter_call_method(stream->output,MS_FILTER_SET_VIDEO_SIZE,&disp_size);
-	ms_filter_call_method(stream->output,MS_VIDEO_OUT_ENABLE_MIRRORING,&mirroring);
-	ms_filter_call_method(stream->output,MS_VIDEO_OUT_SET_CORNER,&corner);
+	ms_filter_call_method(stream->output,MS_VIDEO_DISPLAY_ENABLE_MIRRORING,&mirroring);
+	ms_filter_call_method(stream->output,MS_VIDEO_DISPLAY_SET_LOCAL_VIEW_MODE,&corner);
 	/* and then connect all */
 
@@ -545,6 +580,9 @@
 		return -1;
 	}
+#ifndef WIN32
 	stream->output=ms_filter_new(MS_VIDEO_OUT_ID);
-
+#else
+	stream->output=ms_filter_new(MS_DRAWDIB_DISPLAY_ID);
+#endif
 	/*force the decoder to output YUV420P */
 	format=MS_YUV420P;
Index: src/winsndds.cpp
===================================================================
--- src/winsndds.cpp	(revision 856)
+++ src/winsndds.cpp	(revision 949)
@@ -1337,4 +1337,7 @@
 	ms_DirectSoundCaptureCreate( &d->in_guid, &d->lpDirectSoundCapture, NULL );
 
+	if (d->lpDirectSoundCapture==NULL)
+		return;
+
 	ZeroMemory(&captureDesc, sizeof(DSCBUFFERDESC));
 	captureDesc.dwSize = sizeof(DSCBUFFERDESC);
@@ -1368,13 +1371,16 @@
 	WinSndDs *d=(WinSndDs*)f->data;
 
-	ms_mutex_lock(&d->thread_lock);
-	d->thread_running=FALSE;
-	ms_cond_wait(&d->thread_cond,&d->thread_lock);
-	ms_mutex_unlock(&d->thread_lock);
-	ms_thread_join(d->thread,NULL);
-
-	ms_mutex_lock(&f->ticker->lock);
-	ms_ticker_set_time_func(f->ticker,NULL,NULL);
-	ms_mutex_unlock(&f->ticker->lock);
+	if (d->thread_running==TRUE)
+	{
+		ms_mutex_lock(&d->thread_lock);
+		d->thread_running=FALSE;
+		ms_cond_wait(&d->thread_cond,&d->thread_lock);
+		ms_mutex_unlock(&d->thread_lock);
+		ms_thread_join(d->thread,NULL);
+
+		ms_mutex_lock(&f->ticker->lock);
+		ms_ticker_set_time_func(f->ticker,NULL,NULL);
+		ms_mutex_unlock(&f->ticker->lock);
+	}
 
 	if( d->lpDirectSoundInputBuffer )
@@ -1539,4 +1545,8 @@
 		ms_DirectSoundCreate( &d->out_guid, &d->lpDirectSound, NULL );
 
+		if (d->lpDirectSound==NULL)
+		{
+			return ;
+		}
 
 		hWnd = GetDesktopWindow();
Index: tests/mediastream.c
===================================================================
--- tests/mediastream.c	(revision 870)
+++ tests/mediastream.c	(revision 954)
@@ -43,4 +43,5 @@
 
 static const char * capture_card=NULL;
+static const char * playback_card=NULL;
 static float ng_threshold=-1;
 static bool_t use_ng=FALSE;
@@ -140,5 +141,6 @@
 								"[ --ng (enable noise gate)]\n"
 								"[ --ng-threshold <(float) [0-1]> (noise gate threshold)]\n"
-								"[ --capture-card <index>] \n";
+								"[ --capture-card <name>] \n"
+								"[ --playback-card <name>] \n";
 
 static void run_media_streams(int localport, const char *remote_ip, int remoteport, int payload, const char *fmtp,
@@ -164,4 +166,5 @@
 	rtp_profile_set_payload(&av_profile,111,&payload_type_speex_wb);
 	rtp_profile_set_payload(&av_profile,112,&payload_type_ilbc);
+	rtp_profile_set_payload(&av_profile,113,&payload_type_amr);
 #ifdef VIDEO_ENABLED
 	rtp_profile_set_payload(&av_profile,26,&payload_type_jpeg);
@@ -211,4 +214,7 @@
 			i++;
 			capture_card=argv[i];
+		}else if (strcmp(argv[i],"--playback-card")==0){
+			i++;
+			playback_card=argv[i];
 		}else if (strcmp(argv[i],"--ec")==0){
 			ec=TRUE;
@@ -255,11 +261,13 @@
 		MSSndCard *capt= capture_card==NULL ? ms_snd_card_manager_get_default_capture_card(manager) :
 				ms_snd_card_manager_get_card(manager,capture_card);
+		MSSndCard *play= playback_card==NULL ? ms_snd_card_manager_get_default_playback_card(manager) :
+				ms_snd_card_manager_get_card(manager,playback_card);
 		audio=audio_stream_new(localport,ms_is_ipv6(remote_ip));
 		audio_stream_enable_automatic_gain_control(audio,agc);
 		audio_stream_enable_noise_gate(audio,use_ng);
-    audio_stream_set_echo_canceller_params(audio,ec_len_ms,ec_delay_ms,ec_framesize);
-    printf("Starting audio stream.\n");
+		audio_stream_set_echo_canceller_params(audio,ec_len_ms,ec_delay_ms,ec_framesize);
+		printf("Starting audio stream.\n");
 		audio_stream_start_now(audio,profile,remote_ip,remoteport,remoteport+1,payload,jitter,
-			ms_snd_card_manager_get_default_playback_card(manager),
+			play,
 			capt,
 			 ec);
Index: tests/videodisplay.c
===================================================================
--- tests/videodisplay.c	(revision 856)
+++ tests/videodisplay.c	(revision 975)
@@ -77,20 +77,20 @@
 			  {
 			    int corner=1;
-			    ms_filter_call_method(vs->output,MS_VIDEO_OUT_SET_CORNER,&corner);
+			    ms_filter_call_method(vs->output,MS_VIDEO_DISPLAY_SET_LOCAL_VIEW_MODE,&corner);
 			  }
 			if (n==600)
 			  {
 			    int corner=2;
-			    ms_filter_call_method(vs->output,MS_VIDEO_OUT_SET_CORNER,&corner);
+			    ms_filter_call_method(vs->output,MS_VIDEO_DISPLAY_SET_LOCAL_VIEW_MODE,&corner);
 			  }
 			if (n==700)
 			  {
 			    int corner=3;
-			    ms_filter_call_method(vs->output,MS_VIDEO_OUT_SET_CORNER,&corner);
+			    ms_filter_call_method(vs->output,MS_VIDEO_DISPLAY_SET_LOCAL_VIEW_MODE,&corner);
 			  }
 			if (n==800)
 			  {
 			    int corner=-1;
-			    ms_filter_call_method(vs->output,MS_VIDEO_OUT_SET_CORNER,&corner);
+			    ms_filter_call_method(vs->output,MS_VIDEO_DISPLAY_SET_LOCAL_VIEW_MODE,&corner);
 			  }
 			if (n==900)
