* #36737: switch back to svn repo, remove assert in sip_transaction.c
diff --git a/jni/pjproject-android/.svn/pristine/bc/bc215ce630b314f608def09eea14a565321b709f.svn-base b/jni/pjproject-android/.svn/pristine/bc/bc215ce630b314f608def09eea14a565321b709f.svn-base
new file mode 100644
index 0000000..f56545c
--- /dev/null
+++ b/jni/pjproject-android/.svn/pristine/bc/bc215ce630b314f608def09eea14a565321b709f.svn-base
@@ -0,0 +1,90 @@
+;
+; Copyright 1992 by Jutta Degener and Carsten Bormann, Technische
+; Universitaet Berlin.  See the accompanying file "COPYRIGHT" for
+; details.  THERE IS ABSOLUTELY NO WARRANTY FOR THIS SOFTWARE.
+;
+;
+; Variable     Size
+
+g->chain	4
+
+LARc[0]		6
+LARc[1]		6
+LARc[2]		5
+LARc[3]		5
+LARc[4]		4
+LARc[5]		4
+LARc[6]		3
+LARc[7]		3
+
+Nc[0]		7
+bc[0]		2
+Mc[0]		2
+xmaxc[0]	6
+xmc[0]		3
+xmc[1]		3
+xmc[2]		3
+xmc[3]		3
+xmc[4]		3
+xmc[5]		3
+xmc[6]		3
+xmc[7]		3
+xmc[8]		3
+xmc[9]		3
+xmc[10]		3
+xmc[11]		3
+xmc[12]		3
+
+Nc[1]		7
+bc[1]		2
+Mc[1]		2
+xmaxc[1]	6
+xmc[13]		3
+xmc[14]		3
+xmc[15]		3
+xmc[16]		3
+xmc[17]		3
+xmc[18]		3
+xmc[19]		3
+xmc[20]		3
+xmc[21]		3
+xmc[22]		3
+xmc[23]		3
+xmc[24]		3
+xmc[25]		3
+
+Nc[2]		7
+bc[2]		2
+Mc[2]		2
+xmaxc[2]	6
+xmc[26]		3
+xmc[27]		3
+xmc[28]		3
+xmc[29]		3
+xmc[30]		3
+xmc[31]		3
+xmc[32]		3
+xmc[33]		3
+xmc[34]		3
+xmc[35]		3
+xmc[36]		3
+xmc[37]		3
+xmc[38]		3
+
+Nc[3]		7
+bc[3]		2
+Mc[3]		2
+xmaxc[3]	6
+xmc[39]		3
+xmc[40]		3
+xmc[41]		3
+xmc[42]		3
+xmc[43]		3
+xmc[44]		3
+xmc[45]		3
+xmc[46]		3
+xmc[47]		3
+xmc[48]		3
+xmc[49]		3
+xmc[50]		3
+xmc[51]		3
diff --git a/jni/pjproject-android/.svn/pristine/bc/bc25059154604abe64522b4f4f519788cbc6bbea.svn-base b/jni/pjproject-android/.svn/pristine/bc/bc25059154604abe64522b4f4f519788cbc6bbea.svn-base
new file mode 100644
index 0000000..0c94255
--- /dev/null
+++ b/jni/pjproject-android/.svn/pristine/bc/bc25059154604abe64522b4f4f519788cbc6bbea.svn-base
@@ -0,0 +1,269 @@
+/*
+ * Copyright 1992 by Jutta Degener and Carsten Bormann, Technische
+ * Universitaet Berlin.  See the accompanying file "COPYRIGHT" for
+ * details.  THERE IS ABSOLUTELY NO WARRANTY FOR THIS SOFTWARE.
+ */
+
+/*$Header: /tmp_amd/presto/export/kbs/jutta/src/gsm/RCS/private.h,v 1.6 1996/07/02 10:15:26 jutta Exp $*/
+
+#ifndef	PRIVATE_H
+#define	PRIVATE_H
+
+typedef short			word;		/* 16 bit signed int	*/
+typedef long			longword;	/* 32 bit signed int	*/
+
+typedef unsigned short		uword;		/* unsigned word	*/
+typedef unsigned long		ulongword;	/* unsigned longword	*/
+
+struct gsm_state {
+
+	word		dp0[ 280 ];
+	word		e[ 50 ];	/* code.c 			*/
+
+	word		z1;		/* preprocessing.c, Offset_com. */
+	longword	L_z2;		/*                  Offset_com. */
+	int		mp;		/*                  Preemphasis	*/
+
+	word		u[8];		/* short_term_aly_filter.c	*/
+	word		LARpp[2][8]; 	/*                              */
+	word		j;		/*                              */
+
+	word            ltp_cut;        /* long_term.c, LTP crosscorr.  */
+	word		nrp; /* 40 */	/* long_term.c, synthesis	*/
+	word		v[9];		/* short_term.c, synthesis	*/
+	word		msr;		/* decoder.c,	Postprocessing	*/
+
+	char		verbose;	/* only used if !NDEBUG		*/
+	char		fast;		/* only used if FAST		*/
+
+	char		wav_fmt;	/* only used if WAV49 defined	*/
+	unsigned char	frame_index;	/*            odd/even chaining	*/
+	unsigned char	frame_chain;	/*   half-byte to carry forward	*/
+};
+
+
+#define	MIN_WORD	(-32767 - 1)
+#define	MAX_WORD	  32767
+
+#define	MIN_LONGWORD	(-2147483647 - 1)
+#define	MAX_LONGWORD	  2147483647
+
+#ifdef	SASR		/* flag: >> is a signed arithmetic shift right */
+#undef	SASR
+#define	SASR(x, by)	((x) >> (by))
+#else
+#define	SASR(x, by)	((x) >= 0 ? (x) >> (by) : (~(-((x) + 1) >> (by))))
+#endif	/* SASR */
+
+#include "proto.h"
+
+/*
+ *	Prototypes from add.c
+ */
+extern word	gsm_mult 	P((word a, word b));
+extern longword gsm_L_mult 	P((word a, word b));
+extern word	gsm_mult_r	P((word a, word b));
+
+extern word	gsm_div  	P((word num, word denum));
+
+extern word	gsm_add 	P(( word a, word b ));
+extern longword gsm_L_add 	P(( longword a, longword b ));
+
+extern word	gsm_sub 	P((word a, word b));
+extern longword gsm_L_sub 	P((longword a, longword b));
+
+extern word	gsm_abs 	P((word a));
+
+extern word	gsm_norm 	P(( longword a ));
+
+extern longword gsm_L_asl  	P((longword a, int n));
+extern word	gsm_asl 	P((word a, int n));
+
+extern longword gsm_L_asr  	P((longword a, int n));
+extern word	gsm_asr  	P((word a, int n));
+
+/*
+ *  Inlined functions from add.h 
+ */
+
+/* 
+ * #define GSM_MULT_R(a, b) (* word a, word b, !(a == b == MIN_WORD) *)	\
+ *	(0x0FFFF & SASR(((longword)(a) * (longword)(b) + 16384), 15))
+ */
+#define GSM_MULT_R(a, b) /* word a, word b, !(a == b == MIN_WORD) */	\
+	(SASR( ((longword)(a) * (longword)(b) + 16384), 15 ))
+
+# define GSM_MULT(a,b)	 /* word a, word b, !(a == b == MIN_WORD) */	\
+	(SASR( ((longword)(a) * (longword)(b)), 15 ))
+
+# define GSM_L_MULT(a, b) /* word a, word b */	\
+	(((longword)(a) * (longword)(b)) << 1)
+
+# define GSM_L_ADD(a, b)	\
+	( (a) <  0 ? ( (b) >= 0 ? (a) + (b)	\
+		 : (utmp = (ulongword)-((a) + 1) + (ulongword)-((b) + 1)) \
+		   >= MAX_LONGWORD ? MIN_LONGWORD : -(longword)utmp-2 )   \
+	: ((b) <= 0 ? (a) + (b)   \
+	          : (utmp = (ulongword)(a) + (ulongword)(b)) >= MAX_LONGWORD \
+		    ? MAX_LONGWORD : utmp))
+
+/*
+ * # define GSM_ADD(a, b)	\
+ * 	((ltmp = (longword)(a) + (longword)(b)) >= MAX_WORD \
+ * 	? MAX_WORD : ltmp <= MIN_WORD ? MIN_WORD : ltmp)
+ */
+/* Nonportable, but faster: */
+
+#define	GSM_ADD(a, b)	\
+	((ulongword)((ltmp = (longword)(a) + (longword)(b)) - MIN_WORD) > \
+		MAX_WORD - MIN_WORD ? (ltmp > 0 ? MAX_WORD : MIN_WORD) : ltmp)
+
+# define GSM_SUB(a, b)	\
+	((ltmp = (longword)(a) - (longword)(b)) >= MAX_WORD \
+	? MAX_WORD : ltmp <= MIN_WORD ? MIN_WORD : ltmp)
+
+# define GSM_ABS(a)	((a) < 0 ? ((a) == MIN_WORD ? MAX_WORD : -(a)) : (a))
+
+/* Use these if necessary:
+
+# define GSM_MULT_R(a, b)	gsm_mult_r(a, b)
+# define GSM_MULT(a, b)		gsm_mult(a, b)
+# define GSM_L_MULT(a, b)	gsm_L_mult(a, b)
+
+# define GSM_L_ADD(a, b)	gsm_L_add(a, b)
+# define GSM_ADD(a, b)		gsm_add(a, b)
+# define GSM_SUB(a, b)		gsm_sub(a, b)
+
+# define GSM_ABS(a)		gsm_abs(a)
+
+*/
+
+/*
+ *  More prototypes from implementations..
+ */
+extern void Gsm_Coder P((
+		struct gsm_state	* S,
+		word	* s,	/* [0..159] samples		IN	*/
+		word	* LARc,	/* [0..7] LAR coefficients	OUT	*/
+		word	* Nc,	/* [0..3] LTP lag		OUT 	*/
+		word	* bc,	/* [0..3] coded LTP gain	OUT 	*/
+		word	* Mc,	/* [0..3] RPE grid selection	OUT     */
+		word	* xmaxc,/* [0..3] Coded maximum amplitude OUT	*/
+		word	* xMc	/* [13*4] normalized RPE samples OUT	*/));
+
+extern void Gsm_Long_Term_Predictor P((		/* 4x for 160 samples */
+		struct gsm_state * S,
+		word	* d,	/* [0..39]   residual signal	IN	*/
+		word	* dp,	/* [-120..-1] d'		IN	*/
+		word	* e,	/* [0..40] 			OUT	*/
+		word	* dpp,	/* [0..40] 			OUT	*/
+		word	* Nc,	/* correlation lag		OUT	*/
+		word	* bc	/* gain factor			OUT	*/));
+
+extern void Gsm_LPC_Analysis P((
+		struct gsm_state * S,
+		word * s,	 /* 0..159 signals	IN/OUT	*/
+	        word * LARc));   /* 0..7   LARc's	OUT	*/
+
+extern void Gsm_Preprocess P((
+		struct gsm_state * S,
+		word * s, word * so));
+
+extern void Gsm_Encoding P((
+		struct gsm_state * S,
+		word	* e,	
+		word	* ep,	
+		word	* xmaxc,
+		word	* Mc,	
+		word	* xMc));
+
+extern void Gsm_Short_Term_Analysis_Filter P((
+		struct gsm_state * S,
+		word	* LARc,	/* coded log area ratio [0..7]  IN	*/
+		word	* d	/* st res. signal [0..159]	IN/OUT	*/));
+
+extern void Gsm_Decoder P((
+		struct gsm_state * S,
+		word	* LARcr,	/* [0..7]		IN	*/
+		word	* Ncr,		/* [0..3] 		IN 	*/
+		word	* bcr,		/* [0..3]		IN	*/
+		word	* Mcr,		/* [0..3] 		IN 	*/
+		word	* xmaxcr,	/* [0..3]		IN 	*/
+		word	* xMcr,		/* [0..13*4]		IN	*/
+		word	* s));		/* [0..159]		OUT 	*/
+
+extern void Gsm_Decoding P((
+		struct gsm_state * S,
+		word 	xmaxcr,
+		word	Mcr,
+		word	* xMcr,  	/* [0..12]		IN	*/
+		word	* erp)); 	/* [0..39]		OUT 	*/
+
+extern void Gsm_Long_Term_Synthesis_Filtering P((
+		struct gsm_state* S,
+		word	Ncr,
+		word	bcr,
+		word	* erp,		/* [0..39]		  IN 	*/
+		word	* drp)); 	/* [-120..-1] IN, [0..40] OUT 	*/
+
+void Gsm_RPE_Decoding P((
+	struct gsm_state *S,
+		word xmaxcr,
+		word Mcr,
+		word * xMcr,  /* [0..12], 3 bits             IN      */
+		word * erp)); /* [0..39]                     OUT     */
+
+void Gsm_RPE_Encoding P((
+		struct gsm_state * S,
+		word    * e,            /* -5..-1][0..39][40..44     IN/OUT  */
+		word    * xmaxc,        /*                              OUT */
+		word    * Mc,           /*                              OUT */
+		word    * xMc));        /* [0..12]                      OUT */
+
+extern void Gsm_Short_Term_Synthesis_Filter P((
+		struct gsm_state * S,
+		word	* LARcr, 	/* log area ratios [0..7]  IN	*/
+		word	* drp,		/* received d [0...39]	   IN	*/
+		word	* s));		/* signal   s [0..159]	  OUT	*/
+
+extern void Gsm_Update_of_reconstructed_short_time_residual_signal P((
+		word	* dpp,		/* [0...39]	IN	*/
+		word	* ep,		/* [0...39]	IN	*/
+		word	* dp));		/* [-120...-1]  IN/OUT 	*/
+
+/*
+ *  Tables from table.c
+ */
+#ifndef	GSM_TABLE_C
+
+extern word gsm_A[8], gsm_B[8], gsm_MIC[8], gsm_MAC[8];
+extern word gsm_INVA[8];
+extern word gsm_DLB[4], gsm_QLB[4];
+extern word gsm_H[11];
+extern word gsm_NRFAC[8];
+extern word gsm_FAC[8];
+
+#endif	/* GSM_TABLE_C */
+
+/*
+ *  Debugging
+ */
+#ifdef NDEBUG
+
+#	define	gsm_debug_words(a, b, c, d)		/* nil */
+#	define	gsm_debug_longwords(a, b, c, d)		/* nil */
+#	define	gsm_debug_word(a, b)			/* nil */
+#	define	gsm_debug_longword(a, b)		/* nil */
+
+#else	/* !NDEBUG => DEBUG */
+
+	extern void  gsm_debug_words     P((char * name, int, int, word *));
+	extern void  gsm_debug_longwords P((char * name, int, int, longword *));
+	extern void  gsm_debug_longword  P((char * name, longword));
+	extern void  gsm_debug_word      P((char * name, word));
+
+#endif /* !NDEBUG */
+
+#include "unproto.h"
+
+#endif	/* PRIVATE_H */
diff --git a/jni/pjproject-android/.svn/pristine/bc/bc5f00adc6918639438d78f5c54fcbdee49a8dd6.svn-base b/jni/pjproject-android/.svn/pristine/bc/bc5f00adc6918639438d78f5c54fcbdee49a8dd6.svn-base
new file mode 100644
index 0000000..d3eefe2
--- /dev/null
+++ b/jni/pjproject-android/.svn/pristine/bc/bc5f00adc6918639438d78f5c54fcbdee49a8dd6.svn-base
@@ -0,0 +1,1185 @@
+/*******************************************************

+ 

+ bdimad_dev.c

+

+ Author: bdSound Development Team (techsupport@bdsound.com)

+ 

+ Date: 30/10/2012

+ Version 1.0.206

+

+ Copyright (c) 2012 bdSound s.r.l. (www.bdsound.com)

+ All Rights Reserved.

+ 

+ *******************************************************/

+

+#include <pjmedia-audiodev/audiodev_imp.h>

+#include <pj/assert.h>

+#include <pj/log.h>

+#include <pj/os.h>

+#include <pj/string.h>

+#include <pj/unicode.h>

+#include <pjmedia/errno.h>

+

+#if defined(PJMEDIA_AUDIO_DEV_HAS_BDIMAD) && PJMEDIA_AUDIO_DEV_HAS_BDIMAD != 0

+

+#include <math.h>

+#include <wchar.h>

+

+/**************************

+*           bdIMAD

+***************************/

+#include  "../../third_party/bdsound/include/bdimad.h"

+

+/* Maximum supported audio devices */

+#define BD_IMAD_MAX_DEV_COUNT               100                                

+/* Maximum supported device name */

+#define BD_IMAD_MAX_DEV_LENGTH_NAME         256                                

+/* Only mono mode */

+#define BD_IMAD_MAX_CHANNELS                1                                  

+/* Frequency default value (admitted 8000 Hz, 16000 Hz, 32000 Hz and 48000Hz) */

+#define BD_IMAD_DEFAULT_FREQ                48000                              

+/* Default milliseconds per buffer */

+#define BD_IMAD_MSECOND_PER_BUFFER          16                                 

+/* Only for supported systems */

+#define BD_IMAD_STARTING_INPUT_VOLUME       50                                 

+/* Only for supported systems */

+#define BD_IMAD_STARTING_OUTPUT_VOLUME      100                                

+/* Diagnostic Enable/Disable */

+#define BD_IMAD_DIAGNOSTIC                  BD_IMAD_DIAGNOSTIC_DISABLE         

+

+/* Diagnostic folder path */ 

+wchar_t * bdImadPjDiagnosticFolderPath   = L"";                                

+

+#define THIS_FILE            "bdimad_dev.c"

+

+/* BD device info */

+struct bddev_info

+{

+    pjmedia_aud_dev_info    info;

+    unsigned            deviceId;

+};

+

+/* BD factory */

+struct bd_factory

+{

+    pjmedia_aud_dev_factory  base;

+    pj_pool_t                *base_pool;

+    pj_pool_t                *pool;

+    pj_pool_factory          *pf;

+    unsigned                 dev_count;

+    struct bddev_info        *dev_info;

+};

+

+/* Sound stream. */

+struct bd_stream

+{

+    /** Base stream.                    */

+    pjmedia_aud_stream   base;                

+    /** Settings.                       */

+    pjmedia_aud_param    param;               

+    /** Memory pool.                    */

+    pj_pool_t           *pool;                

+					

+    /** Capture callback.               */

+    pjmedia_aud_rec_cb   rec_cb;              

+    /** Playback callback.              */

+    pjmedia_aud_play_cb  play_cb;             

+    /** Application data.               */

+    void                *user_data;           

+					

+    /** Frame format                    */

+    pjmedia_format_id    fmt_id;              

+    /** Silence pattern                 */

+    pj_uint8_t           silence_char;    

+    /** Bytes per frame                 */

+    unsigned             bytes_per_frame;

+    /** Samples per frame               */

+    unsigned		 samples_per_frame;

+    /** Channel count	                 */

+    int			 channel_count;

+					

+    /** Extended frame buffer           */

+    pjmedia_frame_ext   *xfrm;                

+    /** Total ext frm size              */

+    unsigned             xfrm_size;           

+       

+    /** Check running variable          */

+    int                  go;                

+

+    /** Timestamp iterator for capture  */

+    pj_timestamp         timestampCapture;    

+    /** Timestamp iterator for playback */

+    pj_timestamp         timestampPlayback;   

+

+    /** bdIMAD current session instance */

+    bdIMADpj             bdIMADpjInstance;    

+    /** bdIMAD current session settings */

+    bdIMADpj_Setting_t  *bdIMADpjSettingsPtr; 

+    /** bdIMAD current session warnings */

+    bdIMADpj_Warnings_t *bdIMADpjWarningPtr;

+

+    pj_bool_t		 quit_flag;

+

+    pj_bool_t		 rec_thread_exited;

+    pj_bool_t		 rec_thread_initialized;

+    pj_thread_desc	 rec_thread_desc;

+    pj_thread_t		*rec_thread;

+

+    pj_bool_t		 play_thread_exited;

+    pj_bool_t		 play_thread_initialized;

+    pj_thread_desc	 play_thread_desc;

+    pj_thread_t		*play_thread;

+

+    /* Sometime the record callback does not return framesize as configured

+     * (e.g: in OSS), while this module must guarantee returning framesize

+     * as configured in the creation settings. In this case, we need a buffer 

+     * for the recorded samples.

+     */

+    pj_int16_t		*rec_buf;

+    unsigned		 rec_buf_count;

+

+    /* Sometime the player callback does not request framesize as configured

+     * (e.g: in Linux OSS) while sound device will always get samples from 

+     * the other component as many as configured samples_per_frame. 

+     */

+    pj_int16_t		*play_buf;

+    unsigned		 play_buf_count;

+};

+

+/* Prototypes */

+

+// pjmedia_aud_dev_factory_op

+static pj_status_t factory_init(pjmedia_aud_dev_factory *f);

+static pj_status_t factory_destroy(pjmedia_aud_dev_factory *f);

+static unsigned    factory_get_dev_count(pjmedia_aud_dev_factory *f);

+static pj_status_t factory_get_dev_info(pjmedia_aud_dev_factory *f, 

+					unsigned index,    

+					pjmedia_aud_dev_info *info);

+static pj_status_t factory_default_param(pjmedia_aud_dev_factory *f, 

+					 unsigned index, 

+					 pjmedia_aud_param *param);

+static pj_status_t factory_create_stream(pjmedia_aud_dev_factory *f, 

+					 const pjmedia_aud_param *param, 

+					 pjmedia_aud_rec_cb rec_cb, 

+					 pjmedia_aud_play_cb play_cb, 

+					 void *user_data, 

+					 pjmedia_aud_stream **p_aud_strm);

+static pj_status_t factory_refresh(pjmedia_aud_dev_factory *f);

+

+// pjmedia_aud_stream_op

+static pj_status_t stream_get_param(pjmedia_aud_stream *strm, 

+				    pjmedia_aud_param *param);

+static pj_status_t stream_get_cap(pjmedia_aud_stream *strm, 

+				  pjmedia_aud_dev_cap cap, 

+				  void *value);

+static pj_status_t stream_set_cap(pjmedia_aud_stream *strm, 

+				  pjmedia_aud_dev_cap cap, 

+				  const void *value);

+static pj_status_t stream_start(pjmedia_aud_stream *strm);

+static pj_status_t stream_stop(pjmedia_aud_stream *strm);

+static pj_status_t stream_destroy(pjmedia_aud_stream *strm);

+

+/* End Prototypes */

+

+/* Operations */

+static pjmedia_aud_dev_factory_op factory_op =

+{

+    &factory_init,

+    &factory_destroy,

+    &factory_get_dev_count,

+    &factory_get_dev_info,

+    &factory_default_param,

+    &factory_create_stream,

+    &factory_refresh

+};

+

+static pjmedia_aud_stream_op stream_op = 

+{

+    &stream_get_param,

+    &stream_get_cap,

+    &stream_set_cap,

+    &stream_start,

+    &stream_stop,

+    &stream_destroy

+};

+

+/* End Operations */

+

+/* Utility functions */

+

+char* BD_IMAD_PJ_WCHARtoCHAR(wchar_t *orig)

+{

+    size_t origsize = wcslen(orig)+1;

+    const size_t newsize = origsize*sizeof(wchar_t);

+    char *nstring = (char*)calloc(newsize, sizeof(char));

+    wcstombs(nstring, orig, newsize);

+    return nstring;

+}

+

+#ifdef __cplusplus

+extern "C" {

+#endif

+void manage_code(const unsigned char * pCode, unsigned char *pMsg1, 

+		 unsigned char * pMsg2);

+#ifdef __cplusplus

+}

+#endif

+

+/* End Utility functions */

+

+/****************************************************************************

+* Factory operations

+*/

+

+/* Init BDIMAD audio driver */

+pjmedia_aud_dev_factory* pjmedia_bdimad_factory(pj_pool_factory *pf)

+{

+    struct bd_factory *f;

+    pj_pool_t *pool;

+

+    pool = pj_pool_create(pf, "BDIMAD_DRIVER", 1000, 1000, NULL);

+    f = PJ_POOL_ZALLOC_T(pool, struct bd_factory);

+    f->pf = pf;

+    f->base_pool = pool;

+    f->base.op = &factory_op;

+    f->pool = NULL;

+    f->dev_count = 0;

+    f->dev_info = NULL;

+

+    return &f->base;

+}

+

+/* API: init factory */

+static pj_status_t factory_init(pjmedia_aud_dev_factory *f)

+{

+    pj_status_t ret = factory_refresh(f);

+    if (ret != PJ_SUCCESS) return ret;

+

+    PJ_LOG(4, (THIS_FILE, "BDIMAD initialized"));

+

+    return PJ_SUCCESS;

+}

+

+/* API: refresh the device list */

+static pj_status_t factory_refresh(pjmedia_aud_dev_factory *f)

+{    

+    struct bd_factory *wf = (struct bd_factory*)f;

+    unsigned int i = 0;

+    wchar_t *deviceNamep=NULL;

+    wchar_t captureDevName[BD_IMAD_MAX_DEV_COUNT][BD_IMAD_MAX_DEV_LENGTH_NAME];

+    unsigned int captureDeviceCount = 0;

+    wchar_t playbackDevName[BD_IMAD_MAX_DEV_COUNT][BD_IMAD_MAX_DEV_LENGTH_NAME];

+    unsigned int playbackDeviceCount = 0;

+

+

+    if(wf->pool != NULL) {

+        pj_pool_release(wf->pool);

+        wf->pool = NULL;

+    }

+

+    // Enumerate capture sound devices

+    while(bdIMADpj_getDeviceName(BD_IMAD_CAPTURE_DEVICES, &deviceNamep) != 

+	  BD_PJ_ERROR_IMAD_DEVICE_LIST_EMPTY)

+    {

+        wcscpy(captureDevName[captureDeviceCount], deviceNamep);

+        captureDeviceCount++;

+    }

+

+    // Enumerate playback sound devices

+    while(bdIMADpj_getDeviceName(BD_IMAD_PLAYBACK_DEVICES, &deviceNamep) != 

+	  BD_PJ_ERROR_IMAD_DEVICE_LIST_EMPTY)

+    {

+        wcscpy(playbackDevName[playbackDeviceCount], deviceNamep);

+        playbackDeviceCount++;

+    }

+    

+    // Set devices info

+    wf->dev_count = captureDeviceCount + playbackDeviceCount;

+    wf->pool = pj_pool_create(wf->pf, "BD_IMAD_DEVICES", 1000, 1000, NULL);

+    wf->dev_info = (struct bddev_info*)pj_pool_calloc(wf->pool, wf->dev_count, 

+						     sizeof(struct bddev_info));

+    

+    // Capture device properties

+    for(i=0;i<captureDeviceCount;i++) {

+        wf->dev_info[i].deviceId = i;

+        wf->dev_info[i].info.caps = PJMEDIA_AUD_DEV_CAP_INPUT_VOLUME_SETTING | 

+				    PJMEDIA_AUD_DEV_CAP_EC;

+        wf->dev_info[i].info.default_samples_per_sec = BD_IMAD_DEFAULT_FREQ;

+        strcpy(wf->dev_info[i].info.driver, "BD_IMAD");

+        wf->dev_info[i].info.ext_fmt_cnt = 0;

+        wf->dev_info[i].info.input_count = BD_IMAD_MAX_CHANNELS;

+        wf->dev_info[i].info.output_count = 0;

+        strcpy(wf->dev_info[i].info.name, 

+	       BD_IMAD_PJ_WCHARtoCHAR(captureDevName[i]));

+        wf->dev_info[i].info.routes = 0;

+    }

+

+    // Playback device properties

+    for(i=0;i<playbackDeviceCount;i++) {

+        wf->dev_info[captureDeviceCount+i].deviceId = captureDeviceCount+i;

+        wf->dev_info[captureDeviceCount+i].info.caps = 

+				PJMEDIA_AUD_DEV_CAP_OUTPUT_VOLUME_SETTING;

+        wf->dev_info[captureDeviceCount+i].info.default_samples_per_sec = 

+				BD_IMAD_DEFAULT_FREQ;

+        strcpy(wf->dev_info[captureDeviceCount+i].info.driver, "BD_IMAD");

+        wf->dev_info[captureDeviceCount+i].info.ext_fmt_cnt = 0;

+        wf->dev_info[captureDeviceCount+i].info.input_count = 0;

+        wf->dev_info[captureDeviceCount+i].info.output_count = 

+				BD_IMAD_MAX_CHANNELS;

+        strcpy(wf->dev_info[captureDeviceCount+i].info.name, 

+	       BD_IMAD_PJ_WCHARtoCHAR(playbackDevName[i]));

+        wf->dev_info[captureDeviceCount+i].info.routes = 0;

+    }

+

+    PJ_LOG(4, (THIS_FILE, "BDIMAD found %d devices:", wf->dev_count));

+    for(i=0; i<wf->dev_count; i++) {

+        PJ_LOG(4,   

+	       (THIS_FILE, " dev_id %d: %s  (in=%d, out=%d)", 

+               i,

+               wf->dev_info[i].info.name,

+               wf->dev_info[i].info.input_count,

+               wf->dev_info[i].info.output_count));

+    }    

+    return PJ_SUCCESS;

+}

+

+/* API: destroy factory */

+static pj_status_t factory_destroy(pjmedia_aud_dev_factory *f)

+{

+    struct bd_factory *wf = (struct bd_factory*)f;

+    pj_pool_t *pool = wf->base_pool;

+

+    pj_pool_release(wf->pool);

+    wf->base_pool = NULL;

+    pj_pool_release(pool);

+

+    return PJ_SUCCESS;

+}

+

+/* API: get number of devices */

+static unsigned factory_get_dev_count(pjmedia_aud_dev_factory *f)

+{

+    struct bd_factory *wf = (struct bd_factory*)f;

+    return wf->dev_count;

+}

+

+/* API: get device info */

+static pj_status_t factory_get_dev_info(pjmedia_aud_dev_factory *f, 

+                                        unsigned index,

+                                        pjmedia_aud_dev_info *info)

+{

+    struct bd_factory *wf = (struct bd_factory*)f;

+

+    PJ_ASSERT_RETURN(index < wf->dev_count, PJMEDIA_EAUD_INVDEV);

+

+    pj_memcpy(info, &wf->dev_info[index].info, sizeof(*info));

+

+    return PJ_SUCCESS;

+}

+

+/* API: create default device parameter */

+static pj_status_t factory_default_param(pjmedia_aud_dev_factory *f,

+                                         unsigned index,

+                                         pjmedia_aud_param *param)

+{

+    struct bd_factory *wf = (struct bd_factory*)f;

+    struct bddev_info *di = &wf->dev_info[index];

+

+    pj_bzero(param, sizeof(*param));

+    if (di->info.input_count && di->info.output_count) {

+        param->dir = PJMEDIA_DIR_CAPTURE_PLAYBACK;

+        param->rec_id = index;

+        param->play_id = index;

+        param->channel_count = di->info.output_count;

+    } else if(di->info.input_count) {

+        param->dir = PJMEDIA_DIR_CAPTURE;

+        param->rec_id = index;

+        param->play_id = PJMEDIA_AUD_INVALID_DEV;

+        param->channel_count = di->info.input_count;

+    } else if(di->info.output_count) {

+        param->dir = PJMEDIA_DIR_PLAYBACK;

+        param->play_id = index;

+        param->rec_id = PJMEDIA_AUD_INVALID_DEV;

+        param->channel_count = di->info.output_count;

+    } else {

+        return PJMEDIA_EAUD_INVDEV;

+    }

+

+    param->bits_per_sample = BD_IMAD_BITS_X_SAMPLE;

+    param->clock_rate = di->info.default_samples_per_sec;

+    param->flags = di->info.caps;

+    param->samples_per_frame = di->info.default_samples_per_sec * 

+			       param->channel_count * 

+			       BD_IMAD_MSECOND_PER_BUFFER / 

+			       1000;    

+

+    if(di->info.caps & PJMEDIA_AUD_DEV_CAP_INPUT_VOLUME_SETTING) {

+        param->input_vol = BD_IMAD_STARTING_INPUT_VOLUME;

+    }

+

+    if(di->info.caps & PJMEDIA_AUD_DEV_CAP_OUTPUT_VOLUME_SETTING) {

+        param->output_vol = BD_IMAD_STARTING_OUTPUT_VOLUME;

+    }

+

+    if(di->info.caps & PJMEDIA_AUD_DEV_CAP_EC) {

+        param->ec_enabled = PJ_TRUE;

+    }

+

+    return PJ_SUCCESS;

+}

+

+/* callbacks to set data */

+void bdimad_CaptureCallback(void *buffer, int samples, void *user_data)

+{

+    pj_status_t status = PJ_SUCCESS;

+    pjmedia_frame frame;

+    unsigned nsamples;    

+

+    struct bd_stream *strm = (struct bd_stream*)user_data;    

+

+    if(!strm->go) 

+	goto on_break;

+

+    /* Known cases of callback's thread:

+     * - The thread may be changed in the middle of a session, e.g: in MacOS 

+     *   it happens when plugging/unplugging headphone.

+     * - The same thread may be reused in consecutive sessions. The first

+     *   session will leave TLS set, but release the TLS data address,

+     *   so the second session must re-register the callback's thread.

+     */

+    if (strm->rec_thread_initialized == 0 || !pj_thread_is_registered()) 

+    {

+	pj_bzero(strm->rec_thread_desc, sizeof(pj_thread_desc));

+	status = pj_thread_register("bd_CaptureCallback", 

+				    strm->rec_thread_desc, 

+				    &strm->rec_thread);

+	if (status != PJ_SUCCESS)

+	    goto on_break;

+

+	strm->rec_thread_initialized = 1;

+	PJ_LOG(5,(THIS_FILE, "Recorder thread started"));

+    }

+    

+    /* Calculate number of samples we've got */

+    nsamples = samples * strm->channel_count + strm->rec_buf_count;

+

+    /*

+    RECORD

+    */

+    if (strm->fmt_id == PJMEDIA_FORMAT_L16) {

+	if (nsamples >= strm->samples_per_frame) {

+	    /* If buffer is not empty, combine the buffer with the just incoming

+	     * samples, then call put_frame.

+	     */

+	    if (strm->rec_buf_count) {

+		unsigned chunk_count = 0;		

+

+		chunk_count = strm->samples_per_frame - strm->rec_buf_count;

+		pjmedia_copy_samples(strm->rec_buf + strm->rec_buf_count,

+				     (pj_int16_t*)buffer, chunk_count);

+

+		frame.type = PJMEDIA_FRAME_TYPE_AUDIO;

+		frame.buf = (void*) strm->rec_buf;

+		frame.size = strm->bytes_per_frame;

+		frame.timestamp.u64 = strm->timestampCapture.u64;

+		frame.bit_info = 0;

+

+		status = (*strm->rec_cb)(strm->user_data, &frame);

+

+		buffer = (pj_int16_t*) buffer + chunk_count;

+		nsamples -= strm->samples_per_frame;

+		strm->rec_buf_count = 0;

+		strm->timestampCapture.u64 += strm->samples_per_frame /

+					      strm->channel_count;

+	    }

+

+	    /* Give all frames we have */

+	    while (nsamples >= strm->samples_per_frame && status == 0) {		

+		frame.type = PJMEDIA_FRAME_TYPE_AUDIO;

+		frame.buf = (void*) buffer;

+		frame.size = strm->bytes_per_frame;

+		frame.timestamp.u64 = strm->timestampCapture.u64;

+		frame.bit_info = 0;

+

+		status = (*strm->rec_cb)(strm->user_data, &frame);

+

+		buffer = (pj_int16_t*) buffer + strm->samples_per_frame;

+		nsamples -= strm->samples_per_frame;

+		strm->timestampCapture.u64 += strm->samples_per_frame /

+					      strm->channel_count;

+	    }

+

+	    /* Store the remaining samples into the buffer */

+	    if (nsamples && status == 0) {

+		strm->rec_buf_count = nsamples;

+		pjmedia_copy_samples(strm->rec_buf, (pj_int16_t*)buffer, 

+				     nsamples);

+	    }

+

+	} else {

+	    /* Not enough samples, let's just store them in the buffer */

+	    pjmedia_copy_samples(strm->rec_buf + strm->rec_buf_count,

+				 (pj_int16_t*)buffer, 

+				 samples * strm->channel_count);

+	    strm->rec_buf_count += samples * strm->channel_count;	

+	}

+    }  else {

+        pj_assert(!"Frame type not supported");

+    }

+    

+    strm->timestampCapture.u64 += strm->param.samples_per_frame / 

+				  strm->param.channel_count;

+

+    if (status==0)

+	return;

+

+on_break:

+    strm->rec_thread_exited = 1;    

+}

+

+/* callbacks to get data */

+int bdimad_PlaybackCallback(void *buffer, int samples, void *user_data)

+{

+    pj_status_t status = PJ_SUCCESS;

+    pjmedia_frame frame;

+    struct bd_stream *strm = (struct bd_stream*)user_data;

+    unsigned nsamples_req = samples * strm->channel_count;

+

+    if(!strm->go) 

+	goto on_break;

+

+    /* Known cases of callback's thread:

+     * - The thread may be changed in the middle of a session, e.g: in MacOS 

+     *   it happens when plugging/unplugging headphone.

+     * - The same thread may be reused in consecutive sessions. The first

+     *   session will leave TLS set, but release the TLS data address,

+     *   so the second session must re-register the callback's thread.

+     */

+    if (strm->play_thread_initialized == 0 || !pj_thread_is_registered()) 

+    {

+	pj_bzero(strm->play_thread_desc, sizeof(pj_thread_desc));

+	status = pj_thread_register("bd_PlaybackCallback", 

+				    strm->play_thread_desc,

+				    &strm->play_thread);

+	if (status != PJ_SUCCESS)

+	    goto on_break;

+

+	strm->play_thread_initialized = 1;

+	PJ_LOG(5,(THIS_FILE, "Player thread started"));

+    }

+

+    /*

+    PLAY

+    */

+    if(strm->fmt_id == PJMEDIA_FORMAT_L16) {

+	/* Check if any buffered samples */

+	if (strm->play_buf_count) {

+	    /* samples buffered >= requested by sound device */

+	    if (strm->play_buf_count >= nsamples_req) {

+		pjmedia_copy_samples((pj_int16_t*)buffer, strm->play_buf, 

+				     nsamples_req);

+		strm->play_buf_count -= nsamples_req;

+		pjmedia_move_samples(strm->play_buf, 

+				     strm->play_buf + nsamples_req,

+				     strm->play_buf_count);		

+

+		return nsamples_req;

+	    }

+

+	    /* samples buffered < requested by sound device */

+	    pjmedia_copy_samples((pj_int16_t*)buffer, strm->play_buf, 

+				 strm->play_buf_count);

+	    nsamples_req -= strm->play_buf_count;

+	    buffer = (pj_int16_t*)buffer + strm->play_buf_count;

+	    strm->play_buf_count = 0;

+	}

+

+	/* Fill output buffer as requested */

+	while (nsamples_req && status == 0) {

+	    if (nsamples_req >= strm->samples_per_frame) {		

+		frame.type = PJMEDIA_FRAME_TYPE_AUDIO;

+		frame.buf = buffer;

+		frame.size = strm->bytes_per_frame;

+		frame.timestamp.u64 = strm->timestampPlayback.u64;

+		frame.bit_info = 0;

+

+		status = (*strm->play_cb)(strm->user_data, &frame);

+		if (status != PJ_SUCCESS)

+		    return 0;

+

+		if (frame.type != PJMEDIA_FRAME_TYPE_AUDIO)

+		    pj_bzero(frame.buf, frame.size);

+

+		nsamples_req -= strm->samples_per_frame;

+		buffer = (pj_int16_t*)buffer + strm->samples_per_frame;

+	    } else {		

+		frame.type = PJMEDIA_FRAME_TYPE_AUDIO;

+		frame.buf = strm->play_buf;

+		frame.size = strm->bytes_per_frame;

+		frame.timestamp.u64 = strm->timestampPlayback.u64;

+		frame.bit_info = 0;

+

+		status = (*strm->play_cb)(strm->user_data, &frame);

+		if (status != PJ_SUCCESS)

+		    return 0;

+

+		if (frame.type != PJMEDIA_FRAME_TYPE_AUDIO)

+		    pj_bzero(frame.buf, frame.size);

+

+		pjmedia_copy_samples((pj_int16_t*)buffer, strm->play_buf, 

+				     nsamples_req);

+		strm->play_buf_count = strm->samples_per_frame - 

+				       nsamples_req;

+		pjmedia_move_samples(strm->play_buf, 

+				     strm->play_buf+nsamples_req,

+				     strm->play_buf_count);

+		nsamples_req = 0;

+	    }

+

+	    strm->timestampPlayback.u64 += strm->samples_per_frame /

+					   strm->channel_count;

+	}

+    } else {

+        pj_assert(!"Frame type not supported");

+    }

+

+    if(status != PJ_SUCCESS) {

+        return 0;

+    }

+

+    strm->timestampPlayback.u64 += strm->param.samples_per_frame / 

+				   strm->param.channel_count;

+

+    if (status == 0)

+	return samples;

+

+on_break:

+    strm->play_thread_exited = 1;

+    return 0;

+}

+

+/* Internal: Get format name */

+static const char *get_fmt_name(pj_uint32_t id)

+{

+    static char name[8];

+

+    if (id == PJMEDIA_FORMAT_L16)

+        return "PCM";

+    pj_memcpy(name, &id, 4);

+    name[4] = '\0';

+    return name;

+}

+

+/* Internal: create BD device. */

+static pj_status_t init_streams(struct bd_factory *wf,

+                                struct bd_stream *strm,

+                                const pjmedia_aud_param *prm)

+{

+    unsigned ptime;

+    enum bdIMADpj_Status errorInitAEC;

+    wchar_t *deviceNamep=NULL;

+    wchar_t captureDevName[BD_IMAD_MAX_DEV_COUNT][BD_IMAD_MAX_DEV_LENGTH_NAME];

+    int captureDeviceCount = 0;

+    wchar_t playbackDevName[BD_IMAD_MAX_DEV_COUNT][BD_IMAD_MAX_DEV_LENGTH_NAME];

+    int playbackDeviceCount = 0;

+

+    PJ_ASSERT_RETURN(prm->play_id < (int)wf->dev_count, PJ_EINVAL);

+    PJ_ASSERT_RETURN(prm->rec_id < (int)wf->dev_count, PJ_EINVAL);

+

+    ptime = prm->samples_per_frame * 

+	    1000 / 

+	    (prm->clock_rate * prm->channel_count);

+    strm->bytes_per_frame = (prm->clock_rate * 

+			    ((prm->channel_count * prm->bits_per_sample) / 8)) * 

+			     ptime / 

+			     1000;

+    strm->timestampCapture.u64 = 0;

+    strm->timestampPlayback.u64 = 0;

+

+    //BD_IMAD_PJ

+    bdIMADpj_CreateStructures(&strm->bdIMADpjSettingsPtr, 

+			      &strm->bdIMADpjWarningPtr);

+    

+    strm->bdIMADpjSettingsPtr->FrameSize_ms = ptime;

+    strm->bdIMADpjSettingsPtr->DiagnosticEnable = BD_IMAD_DIAGNOSTIC;

+    strm->bdIMADpjSettingsPtr->DiagnosticFolderPath = 

+					    bdImadPjDiagnosticFolderPath;

+    strm->bdIMADpjSettingsPtr->validate = (void *)manage_code;

+

+    if(prm->clock_rate != 8000 && prm->clock_rate != 16000 

+	   && prm->clock_rate != 32000 && prm->clock_rate != 48000) {

+        PJ_LOG(4, (THIS_FILE, 

+		   "BDIMAD support 8000 Hz, 16000 Hz, 32000 Hz and 48000 Hz "

+		   "frequency."));

+    }

+    strm->bdIMADpjSettingsPtr->SamplingFrequency = prm->clock_rate;

+

+    if(prm->channel_count > BD_IMAD_MAX_CHANNELS) {

+        PJ_LOG(4, (THIS_FILE, 

+		   "BDIMAD doesn't support a number of channels upper than %d.", 

+		   BD_IMAD_MAX_CHANNELS));

+    }

+    

+    // Enumerate capture sound devices

+    while(bdIMADpj_getDeviceName(BD_IMAD_CAPTURE_DEVICES, &deviceNamep) != 

+	  BD_PJ_ERROR_IMAD_DEVICE_LIST_EMPTY)

+    {

+        wcscpy(captureDevName[captureDeviceCount], deviceNamep);

+        captureDeviceCount++;

+    }

+    strm->bdIMADpjSettingsPtr->CaptureDevice = captureDevName[(int)prm->rec_id];

+

+    // Enumerate playback sound devices

+    while(bdIMADpj_getDeviceName(BD_IMAD_PLAYBACK_DEVICES, &deviceNamep) != 

+	  BD_PJ_ERROR_IMAD_DEVICE_LIST_EMPTY)

+    {

+        wcscpy(playbackDevName[playbackDeviceCount], deviceNamep);

+        playbackDeviceCount++;

+    }

+    strm->bdIMADpjSettingsPtr->PlayDevice = 

+		    playbackDevName[(int)(prm->play_id-captureDeviceCount)];

+

+    strm->bdIMADpjSettingsPtr->cb_emptyCaptureBuffer = &bdimad_CaptureCallback;

+    strm->bdIMADpjSettingsPtr->cb_emptyCaptureBuffer_user_data = (void*)strm;

+    strm->bdIMADpjSettingsPtr->cb_fillPlayBackBuffer = &bdimad_PlaybackCallback;

+    strm->bdIMADpjSettingsPtr->cb_fillPlayBackBuffer_user_data = (void*)strm;

+

+    if(strm->bdIMADpjInstance != NULL) 

+        bdIMADpj_FreeAEC(&strm->bdIMADpjInstance);

+    strm->bdIMADpjInstance = NULL;

+

+    errorInitAEC = bdIMADpj_InitAEC(&strm->bdIMADpjInstance, 

+				    &strm->bdIMADpjSettingsPtr, 

+				    &strm->bdIMADpjWarningPtr);

+

+    {

+	int auxInt = (prm->ec_enabled == PJ_TRUE ? 1 : 0);

+        bdIMADpj_setParameter(strm->bdIMADpjInstance, 

+			      BD_PARAM_IMAD_PJ_AEC_ENABLE, 

+			      &auxInt);

+        auxInt = 1;

+        //Mic control On by default

+        bdIMADpj_setParameter(strm->bdIMADpjInstance, 

+			      BD_PARAM_IMAD_PJ_MIC_CONTROL_ENABLE, 

+			      &auxInt);

+    }

+

+    if(errorInitAEC != BD_PJ_OK && 

+       errorInitAEC != BD_PJ_WARN_BDIMAD_WARNING_ASSERTED) 

+    {

+        return PJMEDIA_AUDIODEV_ERRNO_FROM_BDIMAD(errorInitAEC);

+    }

+    

+    return PJ_SUCCESS;

+}

+

+/**************************************** 

+            API: create stream 

+*****************************************/

+static pj_status_t stream_stopBDIMAD(pjmedia_aud_stream *s)

+{

+    struct bd_stream *strm = (struct bd_stream*)s;

+    pj_status_t status = PJ_SUCCESS;

+    int i, err = 0;

+

+    PJ_ASSERT_RETURN(strm != NULL, PJ_EINVAL);

+

+    strm->go = 0;    

+    

+    for (i=0; !strm->rec_thread_exited && i<100; ++i)

+	pj_thread_sleep(10);

+    for (i=0; !strm->play_thread_exited && i<100; ++i)

+	pj_thread_sleep(10);

+

+    pj_thread_sleep(1);

+

+    PJ_LOG(5,(THIS_FILE, "Stopping stream.."));

+

+    strm->play_thread_initialized = 0;

+    strm->rec_thread_initialized = 0;

+

+    PJ_LOG(5,(THIS_FILE, "Done, status=%d", err));

+

+    return status;

+}

+

+static pj_status_t stream_stop(pjmedia_aud_stream *s)

+{

+    struct bd_stream *strm = (struct bd_stream*)s;

+

+    PJ_ASSERT_RETURN(strm != NULL, PJ_EINVAL);

+

+    if(strm->bdIMADpjInstance != NULL) {

+        return stream_stopBDIMAD(s);

+    } else {

+        return PJMEDIA_EAUD_ERR;

+    }

+}

+

+static pj_status_t stream_destroyBDIMAD(pjmedia_aud_stream *s)

+{

+    struct bd_stream *strm = (struct bd_stream*)s;

+    int i = 0;

+

+    PJ_ASSERT_RETURN(strm != NULL, PJ_EINVAL);

+

+    stream_stopBDIMAD(s);

+

+    // DeInit BDIMAD

+    bdIMADpj_FreeAEC(&strm->bdIMADpjInstance); 

+	PJ_LOG(4, (THIS_FILE, "Free AEC"));

+

+    bdIMADpj_FreeStructures(&strm->bdIMADpjSettingsPtr, 

+			    &strm->bdIMADpjWarningPtr);

+    PJ_LOG(4, (THIS_FILE, "Free AEC Structure"));

+

+    strm->bdIMADpjInstance = NULL;

+    strm->bdIMADpjSettingsPtr = NULL;

+    strm->bdIMADpjWarningPtr = NULL;    

+

+    strm->quit_flag = 1;

+    for (i=0; !strm->rec_thread_exited && i<100; ++i) {

+	pj_thread_sleep(1);

+    }

+    for (i=0; !strm->play_thread_exited && i<100; ++i) {

+	pj_thread_sleep(1);

+    }

+

+    PJ_LOG(5,(THIS_FILE, "Destroying stream.."));

+

+    pj_pool_release(strm->pool);

+    return PJ_SUCCESS;

+}

+

+/* API: Destroy stream. */

+static pj_status_t stream_destroy(pjmedia_aud_stream *s)

+{

+    struct bd_stream *strm = (struct bd_stream*)s;

+

+    PJ_ASSERT_RETURN(strm != NULL, PJ_EINVAL);

+

+    if(strm->bdIMADpjInstance != NULL) {

+        return stream_destroyBDIMAD(s);

+    } else {

+        return PJMEDIA_EAUD_ERR;

+    }

+}

+

+/* API: set capability */

+static pj_status_t stream_set_capBDIMAD(pjmedia_aud_stream *s,

+					pjmedia_aud_dev_cap cap,

+					const void *pval)

+{

+    struct bd_stream *strm = (struct bd_stream*)s;

+    bdIMADpj_Status res = BD_PJ_OK;

+    PJ_ASSERT_RETURN(s && pval, PJ_EINVAL);

+

+    if(cap == PJMEDIA_AUD_DEV_CAP_OUTPUT_VOLUME_SETTING) {

+        /* Output volume setting */

+        float vol = (float)*(unsigned*)pval;

+

+        if(vol > 100.0f) vol = 100.0f;

+        if(vol < 0.0f) vol = 0.0f;

+

+        vol = vol / 100.0f;

+        res = bdIMADpj_setParameter(strm->bdIMADpjInstance, 

+				    BD_PARAM_IMAD_PJ_SPK_VOLUME, &vol);

+

+

+        if(res == BD_PJ_OK) {

+            strm->param.output_vol = *(unsigned*)pval;

+            return PJ_SUCCESS;

+        } else {

+            return PJMEDIA_AUDIODEV_ERRNO_FROM_BDIMAD(res);

+        }

+    }

+

+    if(cap == PJMEDIA_AUD_DEV_CAP_INPUT_VOLUME_SETTING) {

+        /* Input volume setting */

+        float vol = (float)*(unsigned*)pval;

+

+        if(vol > 100.0f) vol = 100.0f;

+        if(vol < 0.0f) vol = 0.0f;

+

+        vol = vol / 100.0f;

+        res = bdIMADpj_setParameter(strm->bdIMADpjInstance, 

+				    BD_PARAM_IMAD_PJ_MIC_VOLUME, &vol);

+        if(res == BD_PJ_OK) {

+            strm->param.input_vol = *(unsigned*)pval;

+            return PJ_SUCCESS;

+        } else {

+            return PJMEDIA_AUDIODEV_ERRNO_FROM_BDIMAD(res);

+        }

+    }

+

+    if(cap == PJMEDIA_AUD_DEV_CAP_EC) {

+        int aecOnOff = (*(pj_bool_t*)pval == PJ_TRUE ? 1 : 0);

+

+        /* AEC setting */

+        res = bdIMADpj_setParameter(strm->bdIMADpjInstance, 

+				    BD_PARAM_IMAD_PJ_AEC_ENABLE, 

+				    &aecOnOff);

+        if(res == BD_PJ_OK) {

+            strm->param.ec_enabled = (aecOnOff == 1 ? PJ_TRUE : PJ_FALSE);

+            return PJ_SUCCESS;

+        } else {

+            return PJMEDIA_AUDIODEV_ERRNO_FROM_BDIMAD(res);

+        }

+    }

+

+    return PJMEDIA_EAUD_INVCAP;

+}

+

+static pj_status_t factory_create_streamBDIMAD(pjmedia_aud_dev_factory *f,

+                                    const pjmedia_aud_param *param,

+                                    pjmedia_aud_rec_cb rec_cb,

+                                    pjmedia_aud_play_cb play_cb,

+                                    void *user_data,

+                                    pjmedia_aud_stream **p_aud_strm)

+{

+    struct bd_factory *wf = (struct bd_factory*)f;

+    pj_pool_t *pool;

+    struct bd_stream *strm;

+    pj_uint8_t silence_char;

+    pj_status_t status;

+

+    switch (param->ext_fmt.id) {

+    case PJMEDIA_FORMAT_L16:

+	silence_char = '\0';

+	break;

+    default:

+	return PJMEDIA_EAUD_BADFORMAT;

+    }

+

+    /* Create and Initialize stream descriptor */

+    pool = pj_pool_create(wf->pf, "BDIMAD_STREAM", 1000, 1000, NULL);

+    PJ_ASSERT_RETURN(pool != NULL, PJ_ENOMEM);

+

+    strm = PJ_POOL_ZALLOC_T(pool, struct bd_stream);

+    pj_memcpy(&strm->param, param, sizeof(*param));

+    strm->pool = pool;

+    strm->rec_cb = rec_cb;

+    strm->play_cb = play_cb;

+    strm->user_data = user_data;

+    strm->fmt_id = (pjmedia_format_id)param->ext_fmt.id;

+    strm->silence_char = silence_char;

+    strm->channel_count = param->channel_count;

+    strm->samples_per_frame = param->samples_per_frame;

+

+    if (param->dir & PJMEDIA_DIR_CAPTURE_PLAYBACK) {

+        status = init_streams(wf, strm, param);

+

+        if (status != PJ_SUCCESS) {

+            stream_destroyBDIMAD(&strm->base);

+            return status;

+        }

+    } else {

+        stream_destroyBDIMAD(&strm->base);

+        return PJMEDIA_EAUD_ERR;

+    }

+

+    strm->rec_buf = (pj_int16_t*)pj_pool_alloc(pool, 

+		    strm->bytes_per_frame);

+    if (!strm->rec_buf) {

+        pj_pool_release(pool);

+        return PJ_ENOMEM;

+    }

+    strm->rec_buf_count = 0;

+

+    strm->play_buf = (pj_int16_t*)pj_pool_alloc(pool, 

+		     strm->bytes_per_frame);

+    if (!strm->play_buf) {

+        pj_pool_release(pool);

+        return PJ_ENOMEM;

+    }

+    strm->play_buf_count = 0;

+

+    /* Apply the remaining settings */

+    if(param->flags & PJMEDIA_AUD_DEV_CAP_OUTPUT_VOLUME_SETTING) {

+        stream_set_capBDIMAD(&strm->base, 

+			     PJMEDIA_AUD_DEV_CAP_OUTPUT_VOLUME_SETTING, 

+			     &param->output_vol);

+    }

+    if(param->flags & PJMEDIA_AUD_DEV_CAP_INPUT_VOLUME_SETTING) {

+        stream_set_capBDIMAD(&strm->base, 

+			     PJMEDIA_AUD_DEV_CAP_INPUT_VOLUME_SETTING, 

+			     &param->input_vol);

+    }

+    if(param->flags & PJMEDIA_AUD_DEV_CAP_EC) {

+        stream_set_capBDIMAD(&strm->base, 

+			     PJMEDIA_AUD_DEV_CAP_EC, 

+			     &param->ec_enabled);

+    }

+

+    strm->base.op = &stream_op;

+    *p_aud_strm = &strm->base;

+

+    return PJ_SUCCESS;

+}

+

+static pj_status_t factory_create_stream(pjmedia_aud_dev_factory *f,

+					 const pjmedia_aud_param *param,

+					 pjmedia_aud_rec_cb rec_cb,

+					 pjmedia_aud_play_cb play_cb,

+					 void *user_data,

+					 pjmedia_aud_stream **p_aud_strm)

+{

+    return factory_create_streamBDIMAD(f, param, rec_cb, 

+				       play_cb, user_data, p_aud_strm);

+}

+// ----------------------------------------------------------------------

+// ----------------------------------------------------------------------

+// ----------------------------------------------------------------------

+

+/* API: Get stream info. */

+static pj_status_t stream_get_param(pjmedia_aud_stream *s,

+                                    pjmedia_aud_param *pi)

+{

+    struct bd_stream *strm = (struct bd_stream*)s;

+

+    PJ_ASSERT_RETURN(strm && pi, PJ_EINVAL);

+

+    pj_memcpy(pi, &strm->param, sizeof(*pi));

+

+    // Get the output volume setting

+    if(stream_get_cap(s, PJMEDIA_AUD_DEV_CAP_OUTPUT_VOLUME_SETTING, 

+		      &pi->output_vol) == PJ_SUCCESS)

+    {

+        pi->flags |= PJMEDIA_AUD_DEV_CAP_OUTPUT_VOLUME_SETTING;

+    }

+

+    // Get the input volume setting

+    if(stream_get_cap(s, PJMEDIA_AUD_DEV_CAP_INPUT_VOLUME_SETTING, 

+		      &pi->input_vol) == PJ_SUCCESS)

+    {

+        pi->flags |= PJMEDIA_AUD_DEV_CAP_INPUT_VOLUME_SETTING;

+    }

+

+    // Get the AEC setting

+    if(stream_get_cap(s, PJMEDIA_AUD_DEV_CAP_EC, &pi->ec_enabled) == PJ_SUCCESS)

+    {

+        pi->flags |= PJMEDIA_AUD_DEV_CAP_EC;

+    }

+

+    return PJ_SUCCESS;

+}

+

+static pj_status_t stream_get_capBDIMAD(pjmedia_aud_stream *s,

+                                        pjmedia_aud_dev_cap cap,

+                                        void *pval)

+{

+    struct bd_stream *strm = (struct bd_stream*)s;

+    bdIMADpj_Status res = BD_PJ_OK;

+

+    PJ_ASSERT_RETURN(s && pval, PJ_EINVAL);

+

+    if(cap == PJMEDIA_AUD_DEV_CAP_INPUT_VOLUME_SETTING)

+    {

+        /* Input volume setting */

+        float vol;

+        res = bdIMADpj_getParameter(strm->bdIMADpjInstance, 

+				    BD_PARAM_IMAD_PJ_MIC_VOLUME, &vol);

+        if(res == BD_PJ_OK) {

+	    vol = vol * 100;

+	    if(vol > 100.0f) vol = 100.0f;

+	    if(vol < 0.0f) vol = 0.0f;

+	    *(unsigned int *)pval = (unsigned int)vol;

+	    return PJ_SUCCESS;

+        } else{

+            return PJMEDIA_AUDIODEV_ERRNO_FROM_BDIMAD(res);

+        }

+    } else if(cap == PJMEDIA_AUD_DEV_CAP_OUTPUT_VOLUME_SETTING) {

+        /* Output volume setting */

+        float vol;

+        res = bdIMADpj_getParameter(strm->bdIMADpjInstance, 

+				    BD_PARAM_IMAD_PJ_SPK_VOLUME, &vol);

+        if(res == BD_PJ_OK) {

+			vol = vol * 100;

+            if(vol > 100.0f) vol = 100.0f;

+            if(vol < 0.0f) vol = 0.0f;

+            *(unsigned int *)pval = (unsigned int)vol;

+            return PJ_SUCCESS;

+        } else {

+            return PJMEDIA_AUDIODEV_ERRNO_FROM_BDIMAD(res);

+        }

+    }

+    else if(cap == PJMEDIA_AUD_DEV_CAP_EC) {

+        int aecIsOn;

+        res = bdIMADpj_getParameter(strm->bdIMADpjInstance, 

+				    BD_PARAM_IMAD_PJ_AEC_ENABLE, &aecIsOn);

+        if(res == BD_PJ_OK) {

+            *(pj_bool_t*)pval = (aecIsOn == 1 ? PJ_TRUE : PJ_FALSE);

+            return PJ_SUCCESS;

+        } else {

+            return PJMEDIA_AUDIODEV_ERRNO_FROM_BDIMAD(res);

+        }

+    } else {

+        return PJMEDIA_EAUD_INVCAP;

+    }

+}

+

+static pj_status_t stream_startBDIMAD(pjmedia_aud_stream *s)

+{

+    struct bd_stream *strm = (struct bd_stream*)s;

+

+    PJ_ASSERT_RETURN(strm != NULL, PJ_EINVAL);

+

+    strm->go = 1;

+

+    return PJ_SUCCESS;

+}

+

+/* API: get capability */

+static pj_status_t stream_get_cap(pjmedia_aud_stream *s,

+                                  pjmedia_aud_dev_cap cap,

+                                  void *pval)

+{

+    struct bd_stream *strm = (struct bd_stream*)s;

+

+    PJ_ASSERT_RETURN(strm != NULL, PJ_EINVAL);

+

+    if(strm->bdIMADpjInstance != NULL) {

+        return stream_get_capBDIMAD(s, cap, pval);

+    } else {

+        return PJMEDIA_EAUD_ERR;

+    }

+}

+

+/* API: set capability */

+static pj_status_t stream_set_cap(pjmedia_aud_stream *s,

+                                  pjmedia_aud_dev_cap cap,

+                                  const void *pval)

+{

+    struct bd_stream *strm = (struct bd_stream*)s;

+

+    PJ_ASSERT_RETURN(strm != NULL, PJ_EINVAL);

+

+    if(strm->bdIMADpjInstance != NULL) {

+        return stream_set_capBDIMAD(s, cap, pval);

+    } else {

+        return PJMEDIA_EAUD_ERR;

+    }

+}

+

+/* API: Start stream. */

+static pj_status_t stream_start(pjmedia_aud_stream *s)

+{

+    struct bd_stream *strm = (struct bd_stream*)s;

+

+    PJ_ASSERT_RETURN(strm != NULL, PJ_EINVAL);

+

+    if(strm->bdIMADpjInstance != NULL) {

+        return stream_startBDIMAD(s);

+    } else {

+        return PJMEDIA_EAUD_ERR;

+    }

+}

+

+#if defined (_MSC_VER)

+#pragma comment ( lib, "bdClientValidation.lib" )

+#pragma comment ( lib, "bdIMADpj.lib" )

+#endif

+

+

+#endif    /* PJMEDIA_AUDIO_DEV_HAS_BDIMAD */

+

diff --git a/jni/pjproject-android/.svn/pristine/bc/bc836780581b68470191b2a678d0013ad75e9456.svn-base b/jni/pjproject-android/.svn/pristine/bc/bc836780581b68470191b2a678d0013ad75e9456.svn-base
new file mode 100644
index 0000000..3f4338d
--- /dev/null
+++ b/jni/pjproject-android/.svn/pristine/bc/bc836780581b68470191b2a678d0013ad75e9456.svn-base
@@ -0,0 +1,20 @@
+# $Id$
+#
+from inc_cfg import *
+
+# Basic registration
+test_param = TestParam(
+		"Basic registration",
+		[
+			InstanceParam(	"client", 
+					"--null-audio"+
+						" --id=\"<sip:test1@pjsip.org>\""+
+						" --registrar=sip:sip.pjsip.org" +
+						" --username=test1" +
+						" --password=test1" +
+						" --realm=*",
+					uri="sip:test1@pjsip.org",
+					have_reg=True),
+		]
+		)
+
diff --git a/jni/pjproject-android/.svn/pristine/bc/bc857ce8d6993927ff77c88263eb987b083a0d1b.svn-base b/jni/pjproject-android/.svn/pristine/bc/bc857ce8d6993927ff77c88263eb987b083a0d1b.svn-base
new file mode 100644
index 0000000..9521aa9
--- /dev/null
+++ b/jni/pjproject-android/.svn/pristine/bc/bc857ce8d6993927ff77c88263eb987b083a0d1b.svn-base
@@ -0,0 +1,197 @@
+/* $Id$ */
+/* 
+ * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
+ * Copyright (C) 2003-2008 Benny Prijono <benny@prijono.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 "test.h"
+
+/**
+ * \page page_pjlib_timer_test Test: Timer
+ *
+ * This file provides implementation of \b timer_test(). It tests the
+ * functionality of the timer heap.
+ *
+ *
+ * This file is <b>pjlib-test/timer.c</b>
+ *
+ * \include pjlib-test/timer.c
+ */
+
+
+#if INCLUDE_TIMER_TEST
+
+#include <pjlib.h>
+
+#define LOOP		16
+#define MIN_COUNT	250
+#define MAX_COUNT	(LOOP * MIN_COUNT)
+#define MIN_DELAY	2
+#define	D		(MAX_COUNT / 32000)
+#define DELAY		(D < MIN_DELAY ? MIN_DELAY : D)
+#define THIS_FILE	"timer_test"
+
+
+static void timer_callback(pj_timer_heap_t *ht, pj_timer_entry *e)
+{
+    PJ_UNUSED_ARG(ht);
+    PJ_UNUSED_ARG(e);
+}
+
+static int test_timer_heap(void)
+{
+    int i, j;
+    pj_timer_entry *entry;
+    pj_pool_t *pool;
+    pj_timer_heap_t *timer;
+    pj_time_val delay;
+    pj_status_t rc;    int err=0;
+    pj_size_t size;
+    unsigned count;
+
+    size = pj_timer_heap_mem_size(MAX_COUNT)+MAX_COUNT*sizeof(pj_timer_entry);
+    pool = pj_pool_create( mem, NULL, size, 4000, NULL);
+    if (!pool) {
+	PJ_LOG(3,("test", "...error: unable to create pool of %u bytes",
+		  size));
+	return -10;
+    }
+
+    entry = (pj_timer_entry*)pj_pool_calloc(pool, MAX_COUNT, sizeof(*entry));
+    if (!entry)
+	return -20;
+
+    for (i=0; i<MAX_COUNT; ++i) {
+	entry[i].cb = &timer_callback;
+    }
+    rc = pj_timer_heap_create(pool, MAX_COUNT, &timer);
+    if (rc != PJ_SUCCESS) {
+        app_perror("...error: unable to create timer heap", rc);
+	return -30;
+    }
+
+    count = MIN_COUNT;
+    for (i=0; i<LOOP; ++i) {
+	int early = 0;
+	int done=0;
+	int cancelled=0;
+	int rc;
+	pj_timestamp t1, t2, t_sched, t_cancel, t_poll;
+	pj_time_val now, expire;
+
+	pj_gettimeofday(&now);
+	pj_srand(now.sec);
+	t_sched.u32.lo = t_cancel.u32.lo = t_poll.u32.lo = 0;
+
+	// Register timers
+	for (j=0; j<(int)count; ++j) {
+	    delay.sec = pj_rand() % DELAY;
+	    delay.msec = pj_rand() % 1000;
+
+	    // Schedule timer
+	    pj_get_timestamp(&t1);
+	    rc = pj_timer_heap_schedule(timer, &entry[j], &delay);
+	    if (rc != 0)
+		return -40;
+	    pj_get_timestamp(&t2);
+
+	    t_sched.u32.lo += (t2.u32.lo - t1.u32.lo);
+
+	    // Poll timers.
+	    pj_get_timestamp(&t1);
+	    rc = pj_timer_heap_poll(timer, NULL);
+	    pj_get_timestamp(&t2);
+	    if (rc > 0) {
+		t_poll.u32.lo += (t2.u32.lo - t1.u32.lo);
+		early += rc;
+	    }
+	}
+
+	// Set the time where all timers should finish
+	pj_gettimeofday(&expire);
+	delay.sec = DELAY; 
+	delay.msec = 0;
+	PJ_TIME_VAL_ADD(expire, delay);
+
+	// Wait unfil all timers finish, cancel some of them.
+	do {
+	    int index = pj_rand() % count;
+	    pj_get_timestamp(&t1);
+	    rc = pj_timer_heap_cancel(timer, &entry[index]);
+	    pj_get_timestamp(&t2);
+	    if (rc > 0) {
+		cancelled += rc;
+		t_cancel.u32.lo += (t2.u32.lo - t1.u32.lo);
+	    }
+
+	    pj_gettimeofday(&now);
+
+	    pj_get_timestamp(&t1);
+#if defined(PJ_SYMBIAN) && PJ_SYMBIAN!=0
+	    /* On Symbian, we must use OS poll (Active Scheduler poll) since 
+	     * timer is implemented using Active Object.
+	     */
+	    rc = 0;
+	    while (pj_symbianos_poll(-1, 0))
+		++rc;
+#else
+	    rc = pj_timer_heap_poll(timer, NULL);
+#endif
+	    pj_get_timestamp(&t2);
+	    if (rc > 0) {
+		done += rc;
+		t_poll.u32.lo += (t2.u32.lo - t1.u32.lo);
+	    }
+
+	} while (PJ_TIME_VAL_LTE(now, expire)&&pj_timer_heap_count(timer) > 0);
+
+	if (pj_timer_heap_count(timer)) {
+	    PJ_LOG(3, (THIS_FILE, "ERROR: %d timers left", 
+		       pj_timer_heap_count(timer)));
+	    ++err;
+	}
+	t_sched.u32.lo /= count; 
+	t_cancel.u32.lo /= count;
+	t_poll.u32.lo /= count;
+	PJ_LOG(4, (THIS_FILE, 
+	        "...ok (count:%d, early:%d, cancelled:%d, "
+		"sched:%d, cancel:%d poll:%d)", 
+		count, early, cancelled, t_sched.u32.lo, t_cancel.u32.lo,
+		t_poll.u32.lo));
+
+	count = count * 2;
+	if (count > MAX_COUNT)
+	    break;
+    }
+
+    pj_pool_release(pool);
+    return err;
+}
+
+
+int timer_test()
+{
+    return test_timer_heap();
+}
+
+#else
+/* To prevent warning about "translation unit is empty"
+ * when this test is disabled. 
+ */
+int dummy_timer_test;
+#endif	/* INCLUDE_TIMER_TEST */
+
+
diff --git a/jni/pjproject-android/.svn/pristine/bc/bcebc930e7f35185192a9cee5c1867be809ae480.svn-base b/jni/pjproject-android/.svn/pristine/bc/bcebc930e7f35185192a9cee5c1867be809ae480.svn-base
new file mode 100644
index 0000000..60ca4e4
--- /dev/null
+++ b/jni/pjproject-android/.svn/pristine/bc/bcebc930e7f35185192a9cee5c1867be809ae480.svn-base
@@ -0,0 +1,202 @@
+/* $Id$ */
+/* 
+ * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
+ * Copyright (C) 2003-2008 Benny Prijono <benny@prijono.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 
+ */
+
+/**
+ * \page page_pjmedia_samples_recfile_c Samples: Capturing Audio to WAV File
+ *
+ * In this example, we capture audio from the sound device and save it to
+ * WAVE file.
+ *
+ * @see page_pjmedia_samples_playfile_c
+ *
+ * This file is pjsip-apps/src/samples/recfile.c
+ *
+ * \includelineno recfile.c
+ */
+
+#include <pjmedia.h>
+#include <pjlib.h>
+
+#include <stdio.h>
+
+/* For logging purpose. */
+#define THIS_FILE   "recfile.c"
+
+
+/* Configs */
+#define CLOCK_RATE	    44100
+#define NCHANNELS	    2
+#define SAMPLES_PER_FRAME   (NCHANNELS * (CLOCK_RATE * 10 / 1000))
+#define BITS_PER_SAMPLE	    16
+
+
+static const char *desc = 
+ " FILE					    \n"
+ "  recfile.c				    \n"
+ "					    \n"
+ " PURPOSE:				    \n"
+ "  Record microphone to WAVE file.	    \n"
+ "					    \n"
+ " USAGE:				    \n"
+ "  recfile FILE.WAV			    \n"
+ "";
+
+
+/* Util to display the error message for the specified error code  */
+static int app_perror( const char *sender, const char *title, 
+		       pj_status_t status)
+{
+    char errmsg[PJ_ERR_MSG_SIZE];
+
+    PJ_UNUSED_ARG(sender);
+
+    pj_strerror(status, errmsg, sizeof(errmsg));
+
+    printf("%s: %s [code=%d]\n", title, errmsg, status);
+    return 1;
+}
+
+
+/*
+ * main()
+ */
+int main(int argc, char *argv[])
+{
+    pj_caching_pool cp;
+    pjmedia_endpt *med_endpt;
+    pj_pool_t *pool;
+    pjmedia_port *file_port;
+    pjmedia_snd_port *snd_port;
+    char tmp[10];
+    pj_status_t status;
+
+
+    /* Verify cmd line arguments. */
+    if (argc != 2) {
+	puts("");
+	puts(desc);
+	return 0;
+    }
+
+    /* Must init PJLIB first: */
+    status = pj_init();
+    PJ_ASSERT_RETURN(status == PJ_SUCCESS, 1);
+
+    /* Must create a pool factory before we can allocate any memory. */
+    pj_caching_pool_init(&cp, &pj_pool_factory_default_policy, 0);
+
+    /* 
+     * Initialize media endpoint.
+     * This will implicitly initialize PJMEDIA too.
+     */
+    status = pjmedia_endpt_create(&cp.factory, NULL, 1, &med_endpt);
+    PJ_ASSERT_RETURN(status == PJ_SUCCESS, 1);
+
+    /* Create memory pool for our file player */
+    pool = pj_pool_create( &cp.factory,	    /* pool factory	    */
+			   "app",	    /* pool name.	    */
+			   4000,	    /* init size	    */
+			   4000,	    /* increment size	    */
+			   NULL		    /* callback on error    */
+			   );
+
+    /* Create WAVE file writer port. */
+    status = pjmedia_wav_writer_port_create(  pool, argv[1],
+					      CLOCK_RATE,
+					      NCHANNELS,
+					      SAMPLES_PER_FRAME,
+					      BITS_PER_SAMPLE,
+					      0, 0, 
+					      &file_port);
+    if (status != PJ_SUCCESS) {
+	app_perror(THIS_FILE, "Unable to open WAV file for writing", status);
+	return 1;
+    }
+
+    /* Create sound player port. */
+    status = pjmedia_snd_port_create_rec( 
+		 pool,				    /* pool		    */
+		 -1,				    /* use default dev.	    */
+		 PJMEDIA_PIA_SRATE(&file_port->info),/* clock rate.	    */
+		 PJMEDIA_PIA_CCNT(&file_port->info),/* # of channels.	    */
+		 PJMEDIA_PIA_SPF(&file_port->info), /* samples per frame.   */
+		 PJMEDIA_PIA_BITS(&file_port->info),/* bits per sample.	    */
+		 0,				    /* options		    */
+		 &snd_port			    /* returned port	    */
+		 );
+    if (status != PJ_SUCCESS) {
+	app_perror(THIS_FILE, "Unable to open sound device", status);
+	return 1;
+    }
+
+    /* Connect file port to the sound player.
+     * Stream playing will commence immediately.
+     */
+    status = pjmedia_snd_port_connect( snd_port, file_port);
+    PJ_ASSERT_RETURN(status == PJ_SUCCESS, 1);
+
+
+
+    /* 
+     * Recording should be started now. 
+     */
+
+
+    /* Sleep to allow log messages to flush */
+    pj_thread_sleep(10);
+
+
+    printf("Recodring %s..\n", argv[1]);
+    puts("");
+    puts("Press <ENTER> to stop recording and quit");
+
+    if (fgets(tmp, sizeof(tmp), stdin) == NULL) {
+	puts("EOF while reading stdin, will quit now..");
+    }
+
+    
+    /* Start deinitialization: */
+
+    /* Destroy sound device */
+    status = pjmedia_snd_port_destroy( snd_port );
+    PJ_ASSERT_RETURN(status == PJ_SUCCESS, 1);
+
+
+    /* Destroy file port */
+    status = pjmedia_port_destroy( file_port );
+    PJ_ASSERT_RETURN(status == PJ_SUCCESS, 1);
+
+
+    /* Release application pool */
+    pj_pool_release( pool );
+
+    /* Destroy media endpoint. */
+    pjmedia_endpt_destroy( med_endpt );
+
+    /* Destroy pool factory */
+    pj_caching_pool_destroy( &cp );
+
+    /* Shutdown PJLIB */
+    pj_shutdown();
+
+
+    /* Done. */
+    return 0;
+}
diff --git a/jni/pjproject-android/.svn/pristine/bc/bcf59f3ad2c20931cef9a7e28fb31cdcef241fee.svn-base b/jni/pjproject-android/.svn/pristine/bc/bcf59f3ad2c20931cef9a7e28fb31cdcef241fee.svn-base
new file mode 100644
index 0000000..4851be5
--- /dev/null
+++ b/jni/pjproject-android/.svn/pristine/bc/bcf59f3ad2c20931cef9a7e28fb31cdcef241fee.svn-base
@@ -0,0 +1,2186 @@
+/* $Id$ */
+/* 
+ * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
+ * Copyright (C) 2003-2008 Benny Prijono <benny@prijono.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 <pjsip/sip_transport.h>
+#include <pjsip/sip_endpoint.h>
+#include <pjsip/sip_parser.h>
+#include <pjsip/sip_msg.h>
+#include <pjsip/sip_private.h>
+#include <pjsip/sip_errno.h>
+#include <pjsip/sip_module.h>
+#include <pj/addr_resolv.h>
+#include <pj/except.h>
+#include <pj/os.h>
+#include <pj/log.h>
+#include <pj/ioqueue.h>
+#include <pj/hash.h>
+#include <pj/string.h>
+#include <pj/pool.h>
+#include <pj/assert.h>
+#include <pj/lock.h>
+#include <pj/list.h>
+
+
+#define THIS_FILE    "sip_transport.c"
+
+#if 0
+#   define TRACE_(x)	PJ_LOG(5,x)
+
+static const char *addr_string(const pj_sockaddr_t *addr)
+{
+    static char str[PJ_INET6_ADDRSTRLEN];
+    pj_inet_ntop(((const pj_sockaddr*)addr)->addr.sa_family, 
+		 pj_sockaddr_get_addr(addr),
+		 str, sizeof(str));
+    return str;
+}
+#else
+#   define TRACE_(x)
+#endif
+
+/* Prototype. */
+static pj_status_t mod_on_tx_msg(pjsip_tx_data *tdata);
+
+/* This module has sole purpose to print transmit data to contigous buffer
+ * before actually transmitted to the wire. 
+ */
+static pjsip_module mod_msg_print = 
+{
+    NULL, NULL,				/* prev and next		    */
+    { "mod-msg-print", 13},		/* Name.			    */
+    -1,					/* Id				    */
+    PJSIP_MOD_PRIORITY_TRANSPORT_LAYER,	/* Priority			    */
+    NULL,				/* load()			    */
+    NULL,				/* start()			    */
+    NULL,				/* stop()			    */
+    NULL,				/* unload()			    */
+    NULL,				/* on_rx_request()		    */
+    NULL,				/* on_rx_response()		    */
+    &mod_on_tx_msg,			/* on_tx_request()		    */
+    &mod_on_tx_msg,			/* on_tx_response()		    */
+    NULL,				/* on_tsx_state()		    */
+};
+
+/*
+ * Transport manager.
+ */
+struct pjsip_tpmgr 
+{
+    pj_hash_table_t *table;
+    pj_lock_t	    *lock;
+    pjsip_endpoint  *endpt;
+    pjsip_tpfactory  factory_list;
+#if defined(PJ_DEBUG) && PJ_DEBUG!=0
+    pj_atomic_t	    *tdata_counter;
+#endif
+    void           (*on_rx_msg)(pjsip_endpoint*, pj_status_t, pjsip_rx_data*);
+    pj_status_t	   (*on_tx_msg)(pjsip_endpoint*, pjsip_tx_data*);
+    pjsip_tp_state_callback tp_state_cb;
+
+    /* Transmit data list, for transmit data cleanup when transport manager
+     * is destroyed.
+     */
+    pjsip_tx_data    tdata_list;
+};
+
+
+/* Transport state listener list type */
+typedef struct tp_state_listener
+{
+    PJ_DECL_LIST_MEMBER(struct tp_state_listener);
+
+    pjsip_tp_state_callback  cb;
+    void *user_data;
+} tp_state_listener;
+
+
+/*
+ * Transport data.
+ */
+typedef struct transport_data
+{
+    /* Transport listeners */
+    tp_state_listener	    st_listeners;
+    tp_state_listener	    st_listeners_empty;
+} transport_data;
+
+
+/*****************************************************************************
+ *
+ * GENERAL TRANSPORT (NAMES, TYPES, ETC.)
+ *
+ *****************************************************************************/
+
+/*
+ * Transport names.
+ */
+struct transport_names_t
+{
+    pjsip_transport_type_e type;	    /* Transport type	    */
+    pj_uint16_t		   port;	    /* Default port number  */
+    pj_str_t		   name;	    /* Id tag		    */
+    const char		  *description;	    /* Longer description   */
+    unsigned		   flag;	    /* Flags		    */
+    char		   name_buf[16];    /* For user's transport */
+} transport_names[16] = 
+{
+    { 
+	PJSIP_TRANSPORT_UNSPECIFIED, 
+	0, 
+	{"Unspecified", 11}, 
+	"Unspecified", 
+	0
+    },
+    { 
+	PJSIP_TRANSPORT_UDP, 
+	5060, 
+	{"UDP", 3}, 
+	"UDP transport", 
+	PJSIP_TRANSPORT_DATAGRAM
+    },
+    { 
+	PJSIP_TRANSPORT_TCP, 
+	5060, 
+	{"TCP", 3}, 
+	"TCP transport", 
+	PJSIP_TRANSPORT_RELIABLE
+    },
+    { 
+	PJSIP_TRANSPORT_TLS, 
+	5061, 
+	{"TLS", 3}, 
+	"TLS transport", 
+	PJSIP_TRANSPORT_RELIABLE | PJSIP_TRANSPORT_SECURE
+    },
+    { 
+	PJSIP_TRANSPORT_SCTP, 
+	5060, 
+	{"SCTP", 4}, 
+	"SCTP transport", 
+	PJSIP_TRANSPORT_RELIABLE
+    },
+    { 
+	PJSIP_TRANSPORT_LOOP, 
+	15060, 
+	{"LOOP", 4}, 
+	"Loopback transport", 
+	PJSIP_TRANSPORT_RELIABLE
+    }, 
+    { 
+	PJSIP_TRANSPORT_LOOP_DGRAM, 
+	15060, 
+	{"LOOP-DGRAM", 10}, 
+	"Loopback datagram transport", 
+	PJSIP_TRANSPORT_DATAGRAM
+    },
+    { 
+	PJSIP_TRANSPORT_UDP6, 
+	5060, 
+	{"UDP", 3}, 
+	"UDP IPv6 transport", 
+	PJSIP_TRANSPORT_DATAGRAM
+    },
+    { 
+	PJSIP_TRANSPORT_TCP6, 
+	5060, 
+	{"TCP", 3}, 
+	"TCP IPv6 transport", 
+	PJSIP_TRANSPORT_RELIABLE
+    },
+    {
+	PJSIP_TRANSPORT_TLS6,
+	5061,
+	{"TLS", 3},
+	"TLS IPv6 transport",
+	PJSIP_TRANSPORT_RELIABLE | PJSIP_TRANSPORT_SECURE
+    },
+};
+
+static void tp_state_callback(pjsip_transport *tp,
+			      pjsip_transport_state state,
+			      const pjsip_transport_state_info *info);
+
+
+struct transport_names_t *get_tpname(pjsip_transport_type_e type)
+{
+    unsigned i;
+    for (i=0; i<PJ_ARRAY_SIZE(transport_names); ++i) {
+	if (transport_names[i].type == type)
+	    return &transport_names[i];
+    }
+    pj_assert(!"Invalid transport type!");
+    return NULL;
+}
+
+
+
+/*
+ * Register new transport type to PJSIP.
+ */
+PJ_DEF(pj_status_t) pjsip_transport_register_type( unsigned tp_flag,
+						   const char *tp_name,
+						   int def_port,
+						   int *p_tp_type)
+{
+    unsigned i;
+
+    PJ_ASSERT_RETURN(tp_flag && tp_name && def_port, PJ_EINVAL);
+    PJ_ASSERT_RETURN(pj_ansi_strlen(tp_name) < 
+			PJ_ARRAY_SIZE(transport_names[0].name_buf), 
+		     PJ_ENAMETOOLONG);
+
+    for (i=1; i<PJ_ARRAY_SIZE(transport_names); ++i) {
+	if (transport_names[i].type == 0)
+	    break;
+    }
+
+    if (i == PJ_ARRAY_SIZE(transport_names))
+	return PJ_ETOOMANY;
+
+    transport_names[i].type = (pjsip_transport_type_e)i;
+    transport_names[i].port = (pj_uint16_t)def_port;
+    pj_ansi_strcpy(transport_names[i].name_buf, tp_name);
+    transport_names[i].name = pj_str(transport_names[i].name_buf);
+    transport_names[i].flag = tp_flag;
+
+    if (p_tp_type)
+	*p_tp_type = i;
+
+    return PJ_SUCCESS;
+}
+
+
+/*
+ * Get transport type from name.
+ */
+PJ_DEF(pjsip_transport_type_e) pjsip_transport_get_type_from_name(const pj_str_t *name)
+{
+    unsigned i;
+
+    if (name->slen == 0)
+	return PJSIP_TRANSPORT_UNSPECIFIED;
+
+    /* Get transport type from name. */
+    for (i=0; i<PJ_ARRAY_SIZE(transport_names); ++i) {
+	if (pj_stricmp(name, &transport_names[i].name) == 0) {
+	    return transport_names[i].type;
+	}
+    }
+
+    pj_assert(!"Invalid transport name");
+    return PJSIP_TRANSPORT_UNSPECIFIED;
+}
+
+
+/*
+ * Get the transport type for the specified flags.
+ */
+PJ_DEF(pjsip_transport_type_e) pjsip_transport_get_type_from_flag(unsigned flag)
+{
+    unsigned i;
+
+    /* Get the transport type for the specified flags. */
+    for (i=0; i<PJ_ARRAY_SIZE(transport_names); ++i) {
+	if (transport_names[i].flag == flag) {
+	    return transport_names[i].type;
+	}
+    }
+
+    pj_assert(!"Invalid transport type");
+    return PJSIP_TRANSPORT_UNSPECIFIED;
+}
+
+/*
+ * Get the socket address family of a given transport type.
+ */
+PJ_DEF(int) pjsip_transport_type_get_af(pjsip_transport_type_e type)
+{
+    if (type & PJSIP_TRANSPORT_IPV6)
+	return pj_AF_INET6();
+    else
+	return pj_AF_INET();
+}
+
+PJ_DEF(unsigned) pjsip_transport_get_flag_from_type(pjsip_transport_type_e type)
+{
+    /* Return transport flag. */
+    return get_tpname(type)->flag;
+}
+
+/*
+ * Get the default SIP port number for the specified type.
+ */
+PJ_DEF(int) pjsip_transport_get_default_port_for_type(pjsip_transport_type_e type)
+{
+    /* Return the port. */
+    return get_tpname(type)->port;
+}
+
+/*
+ * Get transport name.
+ */
+PJ_DEF(const char*) pjsip_transport_get_type_name(pjsip_transport_type_e type)
+{
+    /* Return the name. */
+    return get_tpname(type)->name.ptr;
+}
+
+/*
+ * Get transport description.
+ */
+PJ_DEF(const char*) pjsip_transport_get_type_desc(pjsip_transport_type_e type)
+{
+    /* Return the description. */
+    return get_tpname(type)->description;
+}
+
+
+/*****************************************************************************
+ *
+ * TRANSPORT SELECTOR
+ *
+ *****************************************************************************/
+
+/*
+ * Add transport/listener reference in the selector.
+ */
+PJ_DEF(void) pjsip_tpselector_add_ref(pjsip_tpselector *sel)
+{
+    if (sel->type == PJSIP_TPSELECTOR_TRANSPORT && sel->u.transport != NULL)
+	pjsip_transport_add_ref(sel->u.transport);
+    else if (sel->type == PJSIP_TPSELECTOR_LISTENER && sel->u.listener != NULL)
+	; /* Hmm.. looks like we don't have reference counter for listener */
+}
+
+
+/*
+ * Decrement transport/listener reference in the selector.
+ */
+PJ_DEF(void) pjsip_tpselector_dec_ref(pjsip_tpselector *sel)
+{
+    if (sel->type == PJSIP_TPSELECTOR_TRANSPORT && sel->u.transport != NULL)
+	pjsip_transport_dec_ref(sel->u.transport);
+    else if (sel->type == PJSIP_TPSELECTOR_LISTENER && sel->u.listener != NULL)
+	; /* Hmm.. looks like we don't have reference counter for listener */
+}
+
+
+/*****************************************************************************
+ *
+ * TRANSMIT DATA BUFFER MANIPULATION.
+ *
+ *****************************************************************************/
+
+/*
+ * Create new transmit buffer.
+ */
+PJ_DEF(pj_status_t) pjsip_tx_data_create( pjsip_tpmgr *mgr,
+					  pjsip_tx_data **p_tdata )
+{
+    pj_pool_t *pool;
+    pjsip_tx_data *tdata;
+    pj_status_t status;
+
+    PJ_ASSERT_RETURN(mgr && p_tdata, PJ_EINVAL);
+
+    pool = pjsip_endpt_create_pool( mgr->endpt, "tdta%p",
+				    PJSIP_POOL_LEN_TDATA,
+				    PJSIP_POOL_INC_TDATA );
+    if (!pool)
+	return PJ_ENOMEM;
+
+    tdata = PJ_POOL_ZALLOC_T(pool, pjsip_tx_data);
+    tdata->pool = pool;
+    tdata->mgr = mgr;
+    pj_memcpy(tdata->obj_name, pool->obj_name, PJ_MAX_OBJ_NAME);
+
+    status = pj_atomic_create(tdata->pool, 0, &tdata->ref_cnt);
+    if (status != PJ_SUCCESS) {
+	pjsip_endpt_release_pool( mgr->endpt, tdata->pool );
+	return status;
+    }
+    
+    //status = pj_lock_create_simple_mutex(pool, "tdta%p", &tdata->lock);
+    status = pj_lock_create_null_mutex(pool, "tdta%p", &tdata->lock);
+    if (status != PJ_SUCCESS) {
+	pjsip_endpt_release_pool( mgr->endpt, tdata->pool );
+	return status;
+    }
+
+    pj_ioqueue_op_key_init(&tdata->op_key.key, sizeof(tdata->op_key.key));
+    pj_list_init(tdata);
+
+#if defined(PJSIP_HAS_TX_DATA_LIST) && PJSIP_HAS_TX_DATA_LIST!=0
+    /* Append this just created tdata to transmit buffer list */
+    pj_lock_acquire(mgr->lock);
+    pj_list_push_back(&mgr->tdata_list, tdata);
+    pj_lock_release(mgr->lock);
+#endif
+
+#if defined(PJ_DEBUG) && PJ_DEBUG!=0
+    pj_atomic_inc( tdata->mgr->tdata_counter );
+#endif
+
+    *p_tdata = tdata;
+    return PJ_SUCCESS;
+}
+
+
+/*
+ * Add reference to tx buffer.
+ */
+PJ_DEF(void) pjsip_tx_data_add_ref( pjsip_tx_data *tdata )
+{
+    pj_atomic_inc(tdata->ref_cnt);
+}
+
+static void tx_data_destroy(pjsip_tx_data *tdata)
+{
+    PJ_LOG(5,(tdata->obj_name, "Destroying txdata %s",
+	      pjsip_tx_data_get_info(tdata)));
+    pjsip_tpselector_dec_ref(&tdata->tp_sel);
+#if defined(PJ_DEBUG) && PJ_DEBUG!=0
+    pj_atomic_dec( tdata->mgr->tdata_counter );
+#endif
+
+#if defined(PJSIP_HAS_TX_DATA_LIST) && PJSIP_HAS_TX_DATA_LIST!=0
+    /* Remove this tdata from transmit buffer list */
+    pj_lock_acquire(tdata->mgr->lock);
+    pj_list_erase(tdata);
+    pj_lock_release(tdata->mgr->lock);
+#endif
+
+    pj_atomic_destroy( tdata->ref_cnt );
+    pj_lock_destroy( tdata->lock );
+    pjsip_endpt_release_pool( tdata->mgr->endpt, tdata->pool );
+}
+
+/*
+ * Decrease transport data reference, destroy it when the reference count
+ * reaches zero.
+ */
+PJ_DEF(pj_status_t) pjsip_tx_data_dec_ref( pjsip_tx_data *tdata )
+{
+    pj_assert( pj_atomic_get(tdata->ref_cnt) > 0);
+    if (pj_atomic_dec_and_get(tdata->ref_cnt) <= 0) {
+	tx_data_destroy(tdata);
+	return PJSIP_EBUFDESTROYED;
+    } else {
+	return PJ_SUCCESS;
+    }
+}
+
+/*
+ * Invalidate the content of the print buffer to force the message to be
+ * re-printed when sent.
+ */
+PJ_DEF(void) pjsip_tx_data_invalidate_msg( pjsip_tx_data *tdata )
+{
+    tdata->buf.cur = tdata->buf.start;
+    tdata->info = NULL;
+}
+
+/*
+ * Print the SIP message to transmit data buffer's internal buffer.
+ */
+PJ_DEF(pj_status_t) pjsip_tx_data_encode(pjsip_tx_data *tdata)
+{
+    /* Allocate buffer if necessary. */
+    if (tdata->buf.start == NULL) {
+	PJ_USE_EXCEPTION;
+
+	PJ_TRY {
+	    tdata->buf.start = (char*) 
+			       pj_pool_alloc(tdata->pool, PJSIP_MAX_PKT_LEN);
+	}
+	PJ_CATCH_ANY {
+	    return PJ_ENOMEM;
+	}
+	PJ_END
+
+	tdata->buf.cur = tdata->buf.start;
+	tdata->buf.end = tdata->buf.start + PJSIP_MAX_PKT_LEN;
+    }
+
+    /* Do we need to reprint? */
+    if (!pjsip_tx_data_is_valid(tdata)) {
+	pj_ssize_t size;
+
+	size = pjsip_msg_print( tdata->msg, tdata->buf.start, 
+			        tdata->buf.end - tdata->buf.start);
+	if (size < 0) {
+	    return PJSIP_EMSGTOOLONG;
+	}
+	pj_assert(size != 0);
+	tdata->buf.cur[size] = '\0';
+	tdata->buf.cur += size;
+    }
+
+    return PJ_SUCCESS;
+}
+
+PJ_DEF(pj_bool_t) pjsip_tx_data_is_valid( pjsip_tx_data *tdata )
+{
+    return tdata->buf.cur != tdata->buf.start;
+}
+
+static char *get_msg_info(pj_pool_t *pool, const char *obj_name,
+			  const pjsip_msg *msg)
+{
+    char info_buf[128], *info;
+    const pjsip_cseq_hdr *cseq;
+    int len;
+
+    cseq = (const pjsip_cseq_hdr*) pjsip_msg_find_hdr(msg, PJSIP_H_CSEQ, NULL);
+    PJ_ASSERT_RETURN(cseq != NULL, "INVALID MSG");
+
+    if (msg->type == PJSIP_REQUEST_MSG) {
+	len = pj_ansi_snprintf(info_buf, sizeof(info_buf), 
+			       "Request msg %.*s/cseq=%d (%s)",
+			       (int)msg->line.req.method.name.slen,
+			       msg->line.req.method.name.ptr,
+			       cseq->cseq, obj_name);
+    } else {
+	len = pj_ansi_snprintf(info_buf, sizeof(info_buf),
+			       "Response msg %d/%.*s/cseq=%d (%s)",
+			       msg->line.status.code,
+			       (int)cseq->method.name.slen,
+			       cseq->method.name.ptr,
+			       cseq->cseq, obj_name);
+    }
+
+    if (len < 1 || len >= (int)sizeof(info_buf)) {
+	return (char*)obj_name;
+    }
+
+    info = (char*) pj_pool_alloc(pool, len+1);
+    pj_memcpy(info, info_buf, len+1);
+
+    return info;
+}
+
+PJ_DEF(char*) pjsip_tx_data_get_info( pjsip_tx_data *tdata )
+{
+    PJ_ASSERT_RETURN(tdata, "NULL");
+
+    /* tdata->info may be assigned by application so if it exists
+     * just return it.
+     */
+    if (tdata->info)
+	return tdata->info;
+
+    if (tdata->msg==NULL)
+	return "NULL";
+
+    pj_lock_acquire(tdata->lock);
+    tdata->info = get_msg_info(tdata->pool, tdata->obj_name, tdata->msg);
+    pj_lock_release(tdata->lock);
+
+    return tdata->info;
+}
+
+PJ_DEF(pj_status_t) pjsip_tx_data_set_transport(pjsip_tx_data *tdata,
+						const pjsip_tpselector *sel)
+{
+    PJ_ASSERT_RETURN(tdata && sel, PJ_EINVAL);
+
+    pj_lock_acquire(tdata->lock);
+
+    pjsip_tpselector_dec_ref(&tdata->tp_sel);
+
+    pj_memcpy(&tdata->tp_sel, sel, sizeof(*sel));
+    pjsip_tpselector_add_ref(&tdata->tp_sel);
+
+    pj_lock_release(tdata->lock);
+
+    return PJ_SUCCESS;
+}
+
+
+PJ_DEF(char*) pjsip_rx_data_get_info(pjsip_rx_data *rdata)
+{
+    char obj_name[PJ_MAX_OBJ_NAME];
+
+    PJ_ASSERT_RETURN(rdata->msg_info.msg, "INVALID MSG");
+
+    if (rdata->msg_info.info)
+	return rdata->msg_info.info;
+
+    pj_ansi_strcpy(obj_name, "rdata");
+    pj_ansi_snprintf(obj_name+5, sizeof(obj_name)-5, "%p", rdata);
+
+    rdata->msg_info.info = get_msg_info(rdata->tp_info.pool, obj_name,
+					rdata->msg_info.msg);
+    return rdata->msg_info.info;
+}
+
+/* Clone pjsip_rx_data. */
+PJ_DEF(pj_status_t) pjsip_rx_data_clone( const pjsip_rx_data *src,
+                                         unsigned flags,
+                                         pjsip_rx_data **p_rdata)
+{
+    pj_pool_t *pool;
+    pjsip_rx_data *dst;
+    pjsip_hdr *hdr;
+
+    PJ_ASSERT_RETURN(src && flags==0 && p_rdata, PJ_EINVAL);
+
+    pool = pj_pool_create(src->tp_info.pool->factory,
+                          "rtd%p",
+                          PJSIP_POOL_RDATA_LEN,
+                          PJSIP_POOL_RDATA_INC,
+                          NULL);
+    if (!pool)
+	return PJ_ENOMEM;
+
+    dst = PJ_POOL_ZALLOC_T(pool, pjsip_rx_data);
+
+    /* Parts of tp_info */
+    dst->tp_info.pool = pool;
+    dst->tp_info.transport = (pjsip_transport*)src->tp_info.transport;
+
+    /* pkt_info can be memcopied */
+    pj_memcpy(&dst->pkt_info, &src->pkt_info, sizeof(src->pkt_info));
+
+    /* msg_info needs deep clone */
+    dst->msg_info.msg_buf = dst->pkt_info.packet;
+    dst->msg_info.len = src->msg_info.len;
+    dst->msg_info.msg = pjsip_msg_clone(pool, src->msg_info.msg);
+    pj_list_init(&dst->msg_info.parse_err);
+
+#define GET_MSG_HDR2(TYPE, type, var)	\
+			case PJSIP_H_##TYPE: \
+			    if (!dst->msg_info.var) { \
+				dst->msg_info.var = (pjsip_##type##_hdr*)hdr; \
+			    } \
+			    break
+#define GET_MSG_HDR(TYPE, var_type)	GET_MSG_HDR2(TYPE, var_type, var_type)
+
+    hdr = dst->msg_info.msg->hdr.next;
+    while (hdr != &dst->msg_info.msg->hdr) {
+	switch (hdr->type) {
+	GET_MSG_HDR(CALL_ID, cid);
+	GET_MSG_HDR(FROM, from);
+	GET_MSG_HDR(TO, to);
+	GET_MSG_HDR(VIA, via);
+	GET_MSG_HDR(CSEQ, cseq);
+	GET_MSG_HDR(MAX_FORWARDS, max_fwd);
+	GET_MSG_HDR(ROUTE, route);
+	GET_MSG_HDR2(RECORD_ROUTE, rr, record_route);
+	GET_MSG_HDR(CONTENT_TYPE, ctype);
+	GET_MSG_HDR(CONTENT_LENGTH, clen);
+	GET_MSG_HDR(REQUIRE, require);
+	GET_MSG_HDR(SUPPORTED, supported);
+	default:
+	    break;
+	}
+	hdr = hdr->next;
+    }
+
+#undef GET_MSG_HDR
+#undef GET_MSG_HDR2
+
+    *p_rdata = dst;
+
+    /* Finally add transport ref */
+    return pjsip_transport_add_ref(dst->tp_info.transport);
+}
+
+/* Free previously cloned pjsip_rx_data. */
+PJ_DEF(pj_status_t) pjsip_rx_data_free_cloned(pjsip_rx_data *rdata)
+{
+    PJ_ASSERT_RETURN(rdata, PJ_EINVAL);
+
+    pjsip_transport_dec_ref(rdata->tp_info.transport);
+    pj_pool_release(rdata->tp_info.pool);
+
+    return PJ_SUCCESS;
+}
+
+/*****************************************************************************
+ *
+ * TRANSPORT KEY
+ *
+ *****************************************************************************/
+
+
+/*****************************************************************************
+ *
+ * TRANSPORT
+ *
+ *****************************************************************************/
+
+static void transport_send_callback(pjsip_transport *transport,
+				    void *token,
+				    pj_ssize_t size)
+{
+    pjsip_tx_data *tdata = (pjsip_tx_data*) token;
+
+    PJ_UNUSED_ARG(transport);
+
+    /* Mark pending off so that app can resend/reuse txdata from inside
+     * the callback.
+     */
+    tdata->is_pending = 0;
+
+    /* Call callback, if any. */
+    if (tdata->cb) {
+	(*tdata->cb)(tdata->token, tdata, size);
+    }
+
+    /* Decrement reference count. */
+    pjsip_tx_data_dec_ref(tdata);
+}
+
+/* This function is called by endpoint for on_tx_request() and on_tx_response()
+ * notification.
+ */
+static pj_status_t mod_on_tx_msg(pjsip_tx_data *tdata)
+{
+    return pjsip_tx_data_encode(tdata);
+}
+
+/*
+ * Send a SIP message using the specified transport.
+ */
+PJ_DEF(pj_status_t) pjsip_transport_send(  pjsip_transport *tr, 
+					   pjsip_tx_data *tdata,
+					   const pj_sockaddr_t *addr,
+					   int addr_len,
+					   void *token,
+					   pjsip_tp_send_callback cb)
+{
+    pj_status_t status;
+
+    PJ_ASSERT_RETURN(tr && tdata && addr, PJ_EINVAL);
+
+    /* Is it currently being sent? */
+    if (tdata->is_pending) {
+	pj_assert(!"Invalid operation step!");
+	PJ_LOG(2,(THIS_FILE, "Unable to send %s: message is pending", 
+			     pjsip_tx_data_get_info(tdata)));
+	return PJSIP_EPENDINGTX;
+    }
+
+    /* Add reference to prevent deletion, and to cancel idle timer if
+     * it's running.
+     */
+    pjsip_transport_add_ref(tr);
+
+    /* Fill in tp_info. */
+    tdata->tp_info.transport = tr;
+    pj_memcpy(&tdata->tp_info.dst_addr, addr, addr_len);
+    tdata->tp_info.dst_addr_len = addr_len;
+
+    pj_inet_ntop(((pj_sockaddr*)addr)->addr.sa_family,
+		 pj_sockaddr_get_addr(addr),
+		 tdata->tp_info.dst_name,
+		 sizeof(tdata->tp_info.dst_name));
+    tdata->tp_info.dst_port = pj_sockaddr_get_port(addr);
+
+    /* Distribute to modules. 
+     * When the message reach mod_msg_print, the contents of the message will
+     * be "printed" to contiguous buffer.
+     */
+    if (tr->tpmgr->on_tx_msg) {
+	status = (*tr->tpmgr->on_tx_msg)(tr->endpt, tdata);
+	if (status != PJ_SUCCESS) {
+	    pjsip_transport_dec_ref(tr);
+	    return status;
+	}
+    }
+
+    /* Save callback data. */
+    tdata->token = token;
+    tdata->cb = cb;
+
+    /* Add reference counter. */
+    pjsip_tx_data_add_ref(tdata);
+
+    /* Mark as pending. */
+    tdata->is_pending = 1;
+
+    /* Send to transport. */
+    status = (*tr->send_msg)(tr, tdata,  addr, addr_len, (void*)tdata, 
+			     &transport_send_callback);
+
+    if (status != PJ_EPENDING) {
+	tdata->is_pending = 0;
+	pjsip_tx_data_dec_ref(tdata);
+    }
+
+    pjsip_transport_dec_ref(tr);
+    return status;
+}
+
+
+/* send_raw() callback */
+static void send_raw_callback(pjsip_transport *transport,
+			      void *token,
+			      pj_ssize_t size)
+{
+    pjsip_tx_data *tdata = (pjsip_tx_data*) token;
+
+    /* Mark pending off so that app can resend/reuse txdata from inside
+     * the callback.
+     */
+    tdata->is_pending = 0;
+
+    /* Call callback, if any. */
+    if (tdata->cb) {
+	(*tdata->cb)(tdata->token, tdata, size);
+    }
+
+    /* Decrement tdata reference count. */
+    pjsip_tx_data_dec_ref(tdata);
+
+    /* Decrement transport reference count */
+    pjsip_transport_dec_ref(transport);
+}
+
+
+/* Send raw data */
+PJ_DEF(pj_status_t) pjsip_tpmgr_send_raw(pjsip_tpmgr *mgr,
+					 pjsip_transport_type_e tp_type,
+					 const pjsip_tpselector *sel,
+					 pjsip_tx_data *tdata,
+					 const void *raw_data,
+					 pj_size_t data_len,
+					 const pj_sockaddr_t *addr,
+					 int addr_len,
+					 void *token,
+					 pjsip_tp_send_callback cb)
+{
+    pjsip_transport *tr;
+    pj_status_t status;
+ 
+    /* Acquire the transport */
+    status = pjsip_tpmgr_acquire_transport(mgr, tp_type, addr, addr_len,
+					   sel, &tr);
+    if (status != PJ_SUCCESS)
+	return status;
+
+    /* Create transmit data buffer if one is not specified */
+    if (tdata == NULL) {
+	status = pjsip_endpt_create_tdata(tr->endpt, &tdata);
+	if (status != PJ_SUCCESS) {
+	    pjsip_transport_dec_ref(tr);
+	    return status;
+	}
+
+	tdata->info = "raw";
+
+	/* Add reference counter. */
+	pjsip_tx_data_add_ref(tdata);
+    }
+
+    /* Allocate buffer */
+    if (tdata->buf.start == NULL ||
+	(tdata->buf.end - tdata->buf.start) < (int)data_len)
+    {
+	/* Note: data_len may be zero, so allocate +1 */
+	tdata->buf.start = (char*) pj_pool_alloc(tdata->pool, data_len+1);
+	tdata->buf.end = tdata->buf.start + data_len + 1;
+    }
+ 
+    /* Copy data, if any! (application may send zero len packet) */
+    if (data_len) {
+	pj_memcpy(tdata->buf.start, raw_data, data_len);
+    }
+    tdata->buf.cur = tdata->buf.start + data_len;
+ 
+    /* Save callback data. */
+    tdata->token = token;
+    tdata->cb = cb;
+
+    /* Mark as pending. */
+    tdata->is_pending = 1;
+
+    /* Send to transport */
+    status = tr->send_msg(tr, tdata, addr, addr_len,
+			  tdata, &send_raw_callback);
+ 
+    if (status != PJ_EPENDING) {
+	/* callback will not be called, so destroy tdata now. */
+	pjsip_tx_data_dec_ref(tdata);
+	pjsip_transport_dec_ref(tr);
+    }
+
+    return status;
+}
+
+
+static void transport_idle_callback(pj_timer_heap_t *timer_heap,
+				    struct pj_timer_entry *entry)
+{
+    pjsip_transport *tp = (pjsip_transport*) entry->user_data;
+    pj_assert(tp != NULL);
+
+    PJ_UNUSED_ARG(timer_heap);
+
+    entry->id = PJ_FALSE;
+    pjsip_transport_destroy(tp);
+}
+
+/*
+ * Add ref.
+ */
+PJ_DEF(pj_status_t) pjsip_transport_add_ref( pjsip_transport *tp )
+{
+    PJ_ASSERT_RETURN(tp != NULL, PJ_EINVAL);
+
+    if (pj_atomic_inc_and_get(tp->ref_cnt) == 1) {
+	pj_lock_acquire(tp->tpmgr->lock);
+	/* Verify again. */
+	if (pj_atomic_get(tp->ref_cnt) == 1) {
+	    if (tp->idle_timer.id != PJ_FALSE) {
+		pjsip_endpt_cancel_timer(tp->tpmgr->endpt, &tp->idle_timer);
+		tp->idle_timer.id = PJ_FALSE;
+	    }
+	}
+	pj_lock_release(tp->tpmgr->lock);
+    }
+
+    return PJ_SUCCESS;
+}
+
+/*
+ * Dec ref.
+ */
+PJ_DEF(pj_status_t) pjsip_transport_dec_ref( pjsip_transport *tp )
+{
+    PJ_ASSERT_RETURN(tp != NULL, PJ_EINVAL);
+
+    pj_assert(pj_atomic_get(tp->ref_cnt) > 0);
+
+    if (pj_atomic_dec_and_get(tp->ref_cnt) == 0) {
+	pj_lock_acquire(tp->tpmgr->lock);
+	/* Verify again. Do not register timer if the transport is
+	 * being destroyed.
+	 */
+	if (pj_atomic_get(tp->ref_cnt) == 0 && !tp->is_destroying) {
+	    pj_time_val delay;
+	    
+	    /* If transport is in graceful shutdown, then this is the
+	     * last user who uses the transport. Schedule to destroy the
+	     * transport immediately. Otherwise schedule idle timer.
+	     */
+	    if (tp->is_shutdown) {
+		delay.sec = delay.msec = 0;
+	    } else {
+		delay.sec = (tp->dir==PJSIP_TP_DIR_OUTGOING) ?
+				PJSIP_TRANSPORT_IDLE_TIME :
+				PJSIP_TRANSPORT_SERVER_IDLE_TIME;
+		delay.msec = 0;
+	    }
+
+	    pj_assert(tp->idle_timer.id == 0);
+	    tp->idle_timer.id = PJ_TRUE;
+	    pjsip_endpt_schedule_timer(tp->tpmgr->endpt, &tp->idle_timer, 
+				       &delay);
+	}
+	pj_lock_release(tp->tpmgr->lock);
+    }
+
+    return PJ_SUCCESS;
+}
+
+
+/**
+ * Register a transport.
+ */
+PJ_DEF(pj_status_t) pjsip_transport_register( pjsip_tpmgr *mgr,
+					      pjsip_transport *tp )
+{
+    int key_len;
+    pj_uint32_t hval;
+    void *entry;
+
+    /* Init. */
+    tp->tpmgr = mgr;
+    pj_bzero(&tp->idle_timer, sizeof(tp->idle_timer));
+    tp->idle_timer.user_data = tp;
+    tp->idle_timer.cb = &transport_idle_callback;
+
+    /* 
+     * Register to hash table (see Trac ticket #42).
+     */
+    key_len = sizeof(tp->key.type) + tp->addr_len;
+    pj_lock_acquire(mgr->lock);
+
+    /* If entry already occupied, unregister previous entry */
+    hval = 0;
+    entry = pj_hash_get(mgr->table, &tp->key, key_len, &hval);
+    if (entry != NULL)
+	pj_hash_set(NULL, mgr->table, &tp->key, key_len, hval, NULL);
+
+    /* Register new entry */
+    pj_hash_set(tp->pool, mgr->table, &tp->key, key_len, hval, tp);
+
+    pj_lock_release(mgr->lock);
+
+    TRACE_((THIS_FILE,"Transport %s registered: type=%s, remote=%s:%d",
+		       tp->obj_name,
+		       pjsip_transport_get_type_name(tp->key.type),
+		       addr_string(&tp->key.rem_addr),
+		       pj_sockaddr_get_port(&tp->key.rem_addr)));
+
+    return PJ_SUCCESS;
+}
+
+/* Force destroy transport (e.g. during transport manager shutdown. */
+static pj_status_t destroy_transport( pjsip_tpmgr *mgr,
+				      pjsip_transport *tp )
+{
+    int key_len;
+    pj_uint32_t hval;
+    void *entry;
+
+    TRACE_((THIS_FILE, "Transport %s is being destroyed", tp->obj_name));
+
+    pj_lock_acquire(tp->lock);
+    pj_lock_acquire(mgr->lock);
+
+    tp->is_destroying = PJ_TRUE;
+
+    /*
+     * Unregister timer, if any.
+     */
+    //pj_assert(tp->idle_timer.id == PJ_FALSE);
+    if (tp->idle_timer.id != PJ_FALSE) {
+	pjsip_endpt_cancel_timer(mgr->endpt, &tp->idle_timer);
+	tp->idle_timer.id = PJ_FALSE;
+    }
+
+    /*
+     * Unregister from hash table (see Trac ticket #42).
+     */
+    key_len = sizeof(tp->key.type) + tp->addr_len;
+    hval = 0;
+    entry = pj_hash_get(mgr->table, &tp->key, key_len, &hval);
+    if (entry == (void*)tp)
+	pj_hash_set(NULL, mgr->table, &tp->key, key_len, hval, NULL);
+
+    pj_lock_release(mgr->lock);
+
+    /* Destroy. */
+    return tp->destroy(tp);
+}
+
+
+/*
+ * Start graceful shutdown procedure for this transport. 
+ */
+PJ_DEF(pj_status_t) pjsip_transport_shutdown(pjsip_transport *tp)
+{
+    pjsip_tpmgr *mgr;
+    pj_status_t status;
+
+    TRACE_((THIS_FILE, "Transport %s shutting down", tp->obj_name));
+
+    pj_lock_acquire(tp->lock);
+
+    mgr = tp->tpmgr;
+    pj_lock_acquire(mgr->lock);
+
+    /* Do nothing if transport is being shutdown already */
+    if (tp->is_shutdown) {
+	pj_lock_release(tp->lock);
+	pj_lock_release(mgr->lock);
+	return PJ_SUCCESS;
+    }
+
+    status = PJ_SUCCESS;
+
+    /* Instruct transport to shutdown itself */
+    if (tp->do_shutdown)
+	status = tp->do_shutdown(tp);
+    
+    if (status == PJ_SUCCESS)
+	tp->is_shutdown = PJ_TRUE;
+
+    /* If transport reference count is zero, start timer count-down */
+    if (pj_atomic_get(tp->ref_cnt) == 0) {
+	pjsip_transport_add_ref(tp);
+	pjsip_transport_dec_ref(tp);
+    }
+
+    pj_lock_release(tp->lock);
+    pj_lock_release(mgr->lock);
+
+    return status;
+}
+
+
+/**
+ * Unregister transport.
+ */
+PJ_DEF(pj_status_t) pjsip_transport_destroy( pjsip_transport *tp)
+{
+    /* Must have no user. */
+    PJ_ASSERT_RETURN(pj_atomic_get(tp->ref_cnt) == 0, PJSIP_EBUSY);
+
+    /* Destroy. */
+    return destroy_transport(tp->tpmgr, tp);
+}
+
+
+
+/*****************************************************************************
+ *
+ * TRANSPORT FACTORY
+ *
+ *****************************************************************************/
+
+
+PJ_DEF(pj_status_t) pjsip_tpmgr_register_tpfactory( pjsip_tpmgr *mgr,
+						    pjsip_tpfactory *tpf)
+{
+    pjsip_tpfactory *p;
+    pj_status_t status;
+
+    pj_lock_acquire(mgr->lock);
+
+    /* Check that no factory with the same type has been registered. */
+    status = PJ_SUCCESS;
+    for (p=mgr->factory_list.next; p!=&mgr->factory_list; p=p->next) {
+	if (p->type == tpf->type) {
+	    status = PJSIP_ETYPEEXISTS;
+	    break;
+	}
+	if (p == tpf) {
+	    status = PJ_EEXISTS;
+	    break;
+	}
+    }
+
+    if (status != PJ_SUCCESS) {
+	pj_lock_release(mgr->lock);
+	return status;
+    }
+
+    pj_list_insert_before(&mgr->factory_list, tpf);
+
+    pj_lock_release(mgr->lock);
+
+    return PJ_SUCCESS;
+}
+
+
+/**
+ * Unregister factory.
+ */
+PJ_DEF(pj_status_t) pjsip_tpmgr_unregister_tpfactory( pjsip_tpmgr *mgr,
+						      pjsip_tpfactory *tpf)
+{
+    pj_lock_acquire(mgr->lock);
+
+    pj_assert(pj_list_find_node(&mgr->factory_list, tpf) == tpf);
+    pj_list_erase(tpf);
+
+    pj_lock_release(mgr->lock);
+
+    return PJ_SUCCESS;
+}
+
+PJ_DECL(void) pjsip_tpmgr_fla2_param_default(pjsip_tpmgr_fla2_param *prm)
+{
+    pj_bzero(prm, sizeof(*prm));
+}
+
+/*****************************************************************************
+ *
+ * TRANSPORT MANAGER
+ *
+ *****************************************************************************/
+
+/*
+ * Create a new transport manager.
+ */
+PJ_DEF(pj_status_t) pjsip_tpmgr_create( pj_pool_t *pool,
+					pjsip_endpoint *endpt,
+					pjsip_rx_callback rx_cb,
+					pjsip_tx_callback tx_cb,
+					pjsip_tpmgr **p_mgr)
+{
+    pjsip_tpmgr *mgr;
+    pj_status_t status;
+
+    PJ_ASSERT_RETURN(pool && endpt && rx_cb && p_mgr, PJ_EINVAL);
+
+    /* Register mod_msg_print module. */
+    status = pjsip_endpt_register_module(endpt, &mod_msg_print);
+    if (status != PJ_SUCCESS)
+	return status;
+
+    /* Create and initialize transport manager. */
+    mgr = PJ_POOL_ZALLOC_T(pool, pjsip_tpmgr);
+    mgr->endpt = endpt;
+    mgr->on_rx_msg = rx_cb;
+    mgr->on_tx_msg = tx_cb;
+    pj_list_init(&mgr->factory_list);
+    pj_list_init(&mgr->tdata_list);
+
+    mgr->table = pj_hash_create(pool, PJSIP_TPMGR_HTABLE_SIZE);
+    if (!mgr->table)
+	return PJ_ENOMEM;
+
+    status = pj_lock_create_recursive_mutex(pool, "tmgr%p", &mgr->lock);
+    if (status != PJ_SUCCESS)
+	return status;
+
+#if defined(PJ_DEBUG) && PJ_DEBUG!=0
+    status = pj_atomic_create(pool, 0, &mgr->tdata_counter);
+    if (status != PJ_SUCCESS) {
+    	pj_lock_destroy(mgr->lock);
+    	return status;
+    }
+#endif
+
+    /* Set transport state callback */
+    pjsip_tpmgr_set_state_cb(mgr, &tp_state_callback);
+
+    PJ_LOG(5, (THIS_FILE, "Transport manager created."));
+
+    *p_mgr = mgr;
+    return PJ_SUCCESS;
+}
+
+/* Get the interface to send packet to the specified address */
+static pj_status_t get_net_interface(pjsip_transport_type_e tp_type,
+				     const pj_str_t *dst,
+                                     pj_str_t *itf_str_addr)
+{
+    int af;
+    pj_sockaddr itf_addr;
+    pj_status_t status;
+
+    af = (tp_type & PJSIP_TRANSPORT_IPV6)? PJ_AF_INET6 : PJ_AF_INET;
+    status = pj_getipinterface(af, dst, &itf_addr, PJ_FALSE, NULL);
+    if (status != PJ_SUCCESS) {
+	/* If it fails, e.g: on WM6 (http://support.microsoft.com/kb/129065),
+	 * just fallback using pj_gethostip(), see ticket #1660.
+	 */
+	PJ_LOG(5,(THIS_FILE,"Warning: unable to determine local "
+			    "interface, fallback to default interface!"));
+	status = pj_gethostip(af, &itf_addr);
+	if (status != PJ_SUCCESS)
+	    return status;
+    }
+
+    /* Print address */
+    pj_sockaddr_print(&itf_addr, itf_str_addr->ptr,
+		      PJ_INET6_ADDRSTRLEN, 0);
+    itf_str_addr->slen = pj_ansi_strlen(itf_str_addr->ptr);
+
+    return PJ_SUCCESS;
+}
+
+/*
+ * Find out the appropriate local address info (IP address and port) to
+ * advertise in Contact header based on the remote address to be 
+ * contacted. The local address info would be the address name of the
+ * transport or listener which will be used to send the request.
+ *
+ * In this implementation, it will only select the transport based on
+ * the transport type in the request.
+ */
+PJ_DEF(pj_status_t) pjsip_tpmgr_find_local_addr2(pjsip_tpmgr *tpmgr,
+						 pj_pool_t *pool,
+						 pjsip_tpmgr_fla2_param *prm)
+{
+    char tmp_buf[PJ_INET6_ADDRSTRLEN+10];
+    pj_str_t tmp_str;
+    pj_status_t status = PJSIP_EUNSUPTRANSPORT;
+    unsigned flag;
+
+    /* Sanity checks */
+    PJ_ASSERT_RETURN(tpmgr && pool && prm, PJ_EINVAL);
+
+    pj_strset(&tmp_str, tmp_buf, 0);
+    prm->ret_addr.slen = 0;
+    prm->ret_port = 0;
+    prm->ret_tp = NULL;
+
+    flag = pjsip_transport_get_flag_from_type(prm->tp_type);
+
+    if (prm->tp_sel && prm->tp_sel->type == PJSIP_TPSELECTOR_TRANSPORT &&
+	prm->tp_sel->u.transport)
+    {
+	const pjsip_transport *tp = prm->tp_sel->u.transport;
+	if (prm->local_if) {
+	    status = get_net_interface((pjsip_transport_type_e)tp->key.type,
+				       &prm->dst_host, &tmp_str);
+	    if (status != PJ_SUCCESS)
+		goto on_return;
+	    pj_strdup(pool, &prm->ret_addr, &tmp_str);
+	    prm->ret_port = pj_sockaddr_get_port(&tp->local_addr);
+	    prm->ret_tp = tp;
+	} else {
+	    pj_strdup(pool, &prm->ret_addr, &tp->local_name.host);
+	    prm->ret_port = (pj_uint16_t)tp->local_name.port;
+	}
+	status = PJ_SUCCESS;
+
+    } else if (prm->tp_sel && prm->tp_sel->type == PJSIP_TPSELECTOR_LISTENER &&
+	       prm->tp_sel->u.listener)
+    {
+	if (prm->local_if) {
+	    status = get_net_interface(prm->tp_sel->u.listener->type,
+	                               &prm->dst_host, &tmp_str);
+	    if (status != PJ_SUCCESS)
+		goto on_return;
+	    pj_strdup(pool, &prm->ret_addr, &tmp_str);
+	} else {
+	    pj_strdup(pool, &prm->ret_addr,
+		      &prm->tp_sel->u.listener->addr_name.host);
+	}
+	prm->ret_port = (pj_uint16_t)prm->tp_sel->u.listener->addr_name.port;
+	status = PJ_SUCCESS;
+
+    } else if ((flag & PJSIP_TRANSPORT_DATAGRAM) != 0) {
+	pj_sockaddr remote;
+	int addr_len;
+	pjsip_transport *tp;
+
+	pj_bzero(&remote, sizeof(remote));
+	if (prm->tp_type & PJSIP_TRANSPORT_IPV6) {
+	    addr_len = sizeof(pj_sockaddr_in6);
+	    remote.addr.sa_family = pj_AF_INET6();
+	} else {
+	    addr_len = sizeof(pj_sockaddr_in);
+	    remote.addr.sa_family = pj_AF_INET();
+	}
+
+	status = pjsip_tpmgr_acquire_transport(tpmgr, prm->tp_type, &remote,
+					       addr_len, NULL, &tp);
+
+	if (status == PJ_SUCCESS) {
+	    if (prm->local_if) {
+		status = get_net_interface((pjsip_transport_type_e)
+					   tp->key.type,
+					   &prm->dst_host, &tmp_str);
+		if (status != PJ_SUCCESS)
+		    goto on_return;
+		pj_strdup(pool, &prm->ret_addr, &tmp_str);
+		prm->ret_port = pj_sockaddr_get_port(&tp->local_addr);
+		prm->ret_tp = tp;
+	    } else {
+		pj_strdup(pool, &prm->ret_addr, &tp->local_name.host);
+		prm->ret_port = (pj_uint16_t)tp->local_name.port;
+	    }
+
+	    pjsip_transport_dec_ref(tp);
+	}
+
+    } else {
+	/* For connection oriented transport, enum the factories */
+	pjsip_tpfactory *f;
+
+	pj_lock_acquire(tpmgr->lock);
+
+	f = tpmgr->factory_list.next;
+	while (f != &tpmgr->factory_list) {
+	    if (f->type == prm->tp_type)
+		break;
+	    f = f->next;
+	}
+
+	if (f != &tpmgr->factory_list) {
+	    if (prm->local_if) {
+		status = get_net_interface(f->type, &prm->dst_host,
+					   &tmp_str);
+		if (status == PJ_SUCCESS) {
+		    pj_strdup(pool, &prm->ret_addr, &tmp_str);
+		} else {
+		    /* It could fail "normally" on certain cases, e.g.
+		     * when connecting to IPv6 link local address, it
+		     * will wail with EINVAL.
+		     * In this case, fallback to use the default interface
+		     * rather than failing the call.
+		     */
+		    PJ_PERROR(5,(THIS_FILE, status, "Warning: unable to "
+			         "determine local interface"));
+		    pj_strdup(pool, &prm->ret_addr, &f->addr_name.host);
+		    status = PJ_SUCCESS;
+		}
+	    } else {
+		pj_strdup(pool, &prm->ret_addr, &f->addr_name.host);
+	    }
+	    prm->ret_port = (pj_uint16_t)f->addr_name.port;
+	    status = PJ_SUCCESS;
+	}
+	pj_lock_release(tpmgr->lock);
+    }
+
+on_return:
+    return status;
+}
+
+PJ_DEF(pj_status_t) pjsip_tpmgr_find_local_addr( pjsip_tpmgr *tpmgr,
+						 pj_pool_t *pool,
+						 pjsip_transport_type_e type,
+						 const pjsip_tpselector *sel,
+						 pj_str_t *ip_addr,
+						 int *port)
+{
+    pjsip_tpmgr_fla2_param prm;
+    pj_status_t status;
+
+    pjsip_tpmgr_fla2_param_default(&prm);
+    prm.tp_type = type;
+    prm.tp_sel = sel;
+
+    status = pjsip_tpmgr_find_local_addr2(tpmgr, pool, &prm);
+    if (status != PJ_SUCCESS)
+	return status;
+
+    *ip_addr = prm.ret_addr;
+    *port = prm.ret_port;
+
+    return PJ_SUCCESS;
+}
+
+/*
+ * Return number of transports currently registered to the transport
+ * manager.
+ */
+PJ_DEF(unsigned) pjsip_tpmgr_get_transport_count(pjsip_tpmgr *mgr)
+{
+    pj_hash_iterator_t itr_val;
+    pj_hash_iterator_t *itr;
+    int nr_of_transports = 0;
+    
+    pj_lock_acquire(mgr->lock);
+    
+    itr = pj_hash_first(mgr->table, &itr_val);
+    while (itr) {
+	nr_of_transports++;
+	itr = pj_hash_next(mgr->table, itr);
+    }
+    
+    pj_lock_release(mgr->lock);
+
+    return nr_of_transports;
+}
+
+/*
+ * pjsip_tpmgr_destroy()
+ *
+ * Destroy transport manager.
+ */
+PJ_DEF(pj_status_t) pjsip_tpmgr_destroy( pjsip_tpmgr *mgr )
+{
+    pj_hash_iterator_t itr_val;
+    pj_hash_iterator_t *itr;
+    pjsip_tpfactory *factory;
+    pjsip_endpoint *endpt = mgr->endpt;
+    
+    PJ_LOG(5, (THIS_FILE, "Destroying transport manager"));
+
+    pj_lock_acquire(mgr->lock);
+
+    /*
+     * Destroy all transports.
+     */
+    itr = pj_hash_first(mgr->table, &itr_val);
+    while (itr != NULL) {
+	pj_hash_iterator_t *next;
+	pjsip_transport *transport;
+	
+	transport = (pjsip_transport*) pj_hash_this(mgr->table, itr);
+
+	next = pj_hash_next(mgr->table, itr);
+
+	destroy_transport(mgr, transport);
+
+	itr = next;
+    }
+
+    /*
+     * Destroy all factories/listeners.
+     */
+    factory = mgr->factory_list.next;
+    while (factory != &mgr->factory_list) {
+	pjsip_tpfactory *next = factory->next;
+	
+	factory->destroy(factory);
+
+	factory = next;
+    }
+
+    pj_lock_release(mgr->lock);
+
+#if defined(PJ_DEBUG) && PJ_DEBUG!=0
+    /* If you encounter assert error on this line, it means there are
+     * leakings in transmit data (i.e. some transmit data have not been
+     * destroyed).
+     */
+    //pj_assert(pj_atomic_get(mgr->tdata_counter) == 0);
+    if (pj_atomic_get(mgr->tdata_counter) != 0) {
+	PJ_LOG(3,(THIS_FILE, "Warning: %d transmit buffer(s) not freed!",
+		  pj_atomic_get(mgr->tdata_counter)));
+    }
+#endif
+
+    /*
+     * Destroy any dangling transmit buffer.
+     */
+    if (!pj_list_empty(&mgr->tdata_list)) {
+	pjsip_tx_data *tdata = mgr->tdata_list.next;
+	while (tdata != &mgr->tdata_list) {
+	    pjsip_tx_data *next = tdata->next;
+	    tx_data_destroy(tdata);
+	    tdata = next;
+	}
+	PJ_LOG(3,(THIS_FILE, "Cleaned up dangling transmit buffer(s)."));
+    }
+
+#if defined(PJ_DEBUG) && PJ_DEBUG!=0
+    pj_atomic_destroy(mgr->tdata_counter);
+#endif
+
+    pj_lock_destroy(mgr->lock);
+
+    /* Unregister mod_msg_print. */
+    if (mod_msg_print.id != -1) {
+	pjsip_endpt_unregister_module(endpt, &mod_msg_print);
+    }
+
+    return PJ_SUCCESS;
+}
+
+
+/*
+ * pjsip_tpmgr_receive_packet()
+ *
+ * Called by tranports when they receive a new packet.
+ */
+PJ_DEF(pj_ssize_t) pjsip_tpmgr_receive_packet( pjsip_tpmgr *mgr,
+					       pjsip_rx_data *rdata)
+{
+    pjsip_transport *tr = rdata->tp_info.transport;
+
+    char *current_pkt;
+    pj_size_t remaining_len;
+    pj_size_t total_processed = 0;
+
+    /* Check size. */
+    pj_assert(rdata->pkt_info.len > 0);
+    if (rdata->pkt_info.len <= 0)
+	return -1;
+
+    current_pkt = rdata->pkt_info.packet;
+    remaining_len = rdata->pkt_info.len;
+    
+    /* Must NULL terminate buffer. This is the requirement of the 
+     * parser etc. 
+     */
+    current_pkt[remaining_len] = '\0';
+
+    /* Process all message fragments. */
+    while (remaining_len > 0) {
+
+	pjsip_msg *msg;
+	char *p, *end;
+	char saved;
+	pj_size_t msg_fragment_size;
+
+	/* Skip leading newlines as pjsip_find_msg() currently can't
+	 * handle leading newlines.
+	 */
+	for (p=current_pkt, end=p+remaining_len; p!=end; ++p) {
+	    if (*p != '\r' && *p != '\n')
+		break;
+	}
+	if (p!=current_pkt) {
+	    remaining_len -= (p - current_pkt);
+	    total_processed += (p - current_pkt);
+	    current_pkt = p;
+	    if (remaining_len == 0) {
+		return total_processed;
+	    }
+	}
+
+	/* Initialize default fragment size. */
+	msg_fragment_size = remaining_len;
+
+	/* Clear and init msg_info in rdata. 
+	 * Endpoint might inspect the values there when we call the callback
+	 * to report some errors.
+	 */
+	pj_bzero(&rdata->msg_info, sizeof(rdata->msg_info));
+	pj_list_init(&rdata->msg_info.parse_err);
+	rdata->msg_info.msg_buf = current_pkt;
+	rdata->msg_info.len = (int)remaining_len;
+
+	/* For TCP transport, check if the whole message has been received. */
+	if ((tr->flag & PJSIP_TRANSPORT_DATAGRAM) == 0) {
+	    pj_status_t msg_status;
+	    msg_status = pjsip_find_msg(current_pkt, remaining_len, PJ_FALSE, 
+                                        &msg_fragment_size);
+	    if (msg_status != PJ_SUCCESS) {
+		if (remaining_len == PJSIP_MAX_PKT_LEN) {
+		    mgr->on_rx_msg(mgr->endpt, PJSIP_ERXOVERFLOW, rdata);
+		    /* Exhaust all data. */
+		    return rdata->pkt_info.len;
+		} else {
+		    /* Not enough data in packet. */
+		    return total_processed;
+		}
+	    }
+	}
+
+	/* Update msg_info. */
+	rdata->msg_info.len = (int)msg_fragment_size;
+
+	/* Null terminate packet */
+	saved = current_pkt[msg_fragment_size];
+	current_pkt[msg_fragment_size] = '\0';
+
+	/* Parse the message. */
+	rdata->msg_info.msg = msg = 
+	    pjsip_parse_rdata( current_pkt, msg_fragment_size, rdata);
+
+	/* Restore null termination */
+	current_pkt[msg_fragment_size] = saved;
+
+	/* Check for parsing syntax error */
+	if (msg==NULL || !pj_list_empty(&rdata->msg_info.parse_err)) {
+	    pjsip_parser_err_report *err;
+	    char buf[128];
+	    pj_str_t tmp;
+
+	    /* Gather syntax error information */
+	    tmp.ptr = buf; tmp.slen = 0;
+	    err = rdata->msg_info.parse_err.next;
+	    while (err != &rdata->msg_info.parse_err) {
+		int len;
+		len = pj_ansi_snprintf(tmp.ptr+tmp.slen, sizeof(buf)-tmp.slen,
+				       ": %s exception when parsing '%.*s' "
+				       "header on line %d col %d",
+				       pj_exception_id_name(err->except_code),
+				       (int)err->hname.slen, err->hname.ptr,
+				       err->line, err->col);
+		if (len > 0 && len < (int) (sizeof(buf)-tmp.slen)) {
+		    tmp.slen += len;
+		}
+		err = err->next;
+	    }
+
+	    /* Only print error message if there's error.
+	     * Sometimes we receive blank packets (packets with only CRLF)
+	     * which were sent to keep NAT bindings.
+	     */
+	    if (tmp.slen) {
+		PJ_LOG(1, (THIS_FILE, 
+		      "Error processing %d bytes packet from %s %s:%d %.*s:\n"
+		      "%.*s\n"
+		      "-- end of packet.",
+		      msg_fragment_size,
+		      rdata->tp_info.transport->type_name,
+		      rdata->pkt_info.src_name, 
+		      rdata->pkt_info.src_port,
+		      (int)tmp.slen, tmp.ptr,
+		      (int)msg_fragment_size,
+		      rdata->msg_info.msg_buf));
+	    }
+
+	    goto finish_process_fragment;
+	}
+
+	/* Perform basic header checking. */
+	if (rdata->msg_info.cid == NULL ||
+	    rdata->msg_info.cid->id.slen == 0 || 
+	    rdata->msg_info.from == NULL || 
+	    rdata->msg_info.to == NULL || 
+	    rdata->msg_info.via == NULL || 
+	    rdata->msg_info.cseq == NULL) 
+	{
+	    mgr->on_rx_msg(mgr->endpt, PJSIP_EMISSINGHDR, rdata);
+	    goto finish_process_fragment;
+	}
+
+	/* For request: */
+	if (rdata->msg_info.msg->type == PJSIP_REQUEST_MSG) {
+	    /* always add received parameter to the via. */
+	    pj_strdup2(rdata->tp_info.pool, 
+		       &rdata->msg_info.via->recvd_param, 
+		       rdata->pkt_info.src_name);
+
+	    /* RFC 3581:
+	     * If message contains "rport" param, put the received port there.
+	     */
+	    if (rdata->msg_info.via->rport_param == 0) {
+		rdata->msg_info.via->rport_param = rdata->pkt_info.src_port;
+	    }
+	} else {
+	    /* Drop malformed responses */
+	    if (rdata->msg_info.msg->line.status.code < 100 ||
+		rdata->msg_info.msg->line.status.code >= 700)
+	    {
+		mgr->on_rx_msg(mgr->endpt, PJSIP_EINVALIDSTATUS, rdata);
+		goto finish_process_fragment;
+	    }
+	}
+
+	/* Drop response message if it has more than one Via.
+	*/
+	/* This is wrong. Proxy DOES receive responses with multiple
+	 * Via headers! Thanks Aldo <acampi at deis.unibo.it> for pointing
+	 * this out.
+
+	if (msg->type == PJSIP_RESPONSE_MSG) {
+	    pjsip_hdr *hdr;
+	    hdr = (pjsip_hdr*)rdata->msg_info.via->next;
+	    if (hdr != &msg->hdr) {
+		hdr = pjsip_msg_find_hdr(msg, PJSIP_H_VIA, hdr);
+		if (hdr) {
+		    mgr->on_rx_msg(mgr->endpt, PJSIP_EMULTIPLEVIA, rdata);
+		    goto finish_process_fragment;
+		}
+	    }
+	}
+	*/
+
+	/* Call the transport manager's upstream message callback.
+	 */
+	mgr->on_rx_msg(mgr->endpt, PJ_SUCCESS, rdata);
+
+
+finish_process_fragment:
+	total_processed += msg_fragment_size;
+	current_pkt += msg_fragment_size;
+	remaining_len -= msg_fragment_size;
+
+    }	/* while (rdata->pkt_info.len > 0) */
+
+
+    return total_processed;
+}
+
+
+/*
+ * pjsip_tpmgr_acquire_transport()
+ *
+ * Get transport suitable to communicate to remote. Create a new one
+ * if necessary.
+ */
+PJ_DEF(pj_status_t) pjsip_tpmgr_acquire_transport(pjsip_tpmgr *mgr,
+						  pjsip_transport_type_e type,
+						  const pj_sockaddr_t *remote,
+						  int addr_len,
+						  const pjsip_tpselector *sel,
+						  pjsip_transport **tp)
+{
+    return pjsip_tpmgr_acquire_transport2(mgr, type, remote, addr_len, sel,
+					  NULL, tp);
+}
+
+/*
+ * pjsip_tpmgr_acquire_transport2()
+ *
+ * Get transport suitable to communicate to remote. Create a new one
+ * if necessary.
+ */
+PJ_DEF(pj_status_t) pjsip_tpmgr_acquire_transport2(pjsip_tpmgr *mgr,
+						   pjsip_transport_type_e type,
+						   const pj_sockaddr_t *remote,
+						   int addr_len,
+						   const pjsip_tpselector *sel,
+						   pjsip_tx_data *tdata,
+						   pjsip_transport **tp)
+{
+    pjsip_tpfactory *factory;
+    pj_status_t status;
+
+    TRACE_((THIS_FILE,"Acquiring transport type=%s, remote=%s:%d",
+		       pjsip_transport_get_type_name(type),
+		       addr_string(remote),
+		       pj_sockaddr_get_port(remote)));
+
+    pj_lock_acquire(mgr->lock);
+
+    /* If transport is specified, then just use it if it is suitable
+     * for the destination.
+     */
+    if (sel && sel->type == PJSIP_TPSELECTOR_TRANSPORT &&
+	sel->u.transport) 
+    {
+	pjsip_transport *seltp = sel->u.transport;
+
+	/* See if the transport is (not) suitable */
+	if (seltp->key.type != type) {
+	    pj_lock_release(mgr->lock);
+	    return PJSIP_ETPNOTSUITABLE;
+	}
+
+	/* We could also verify that the destination address is reachable
+	 * from this transport (i.e. both are equal), but if application
+	 * has requested a specific transport to be used, assume that
+	 * it knows what to do.
+	 *
+	 * In other words, I don't think destination verification is a good
+	 * idea for now.
+	 */
+
+	/* Transport looks to be suitable to use, so just use it. */
+	pjsip_transport_add_ref(seltp);
+	pj_lock_release(mgr->lock);
+	*tp = seltp;
+
+	TRACE_((THIS_FILE, "Transport %s acquired", seltp->obj_name));
+	return PJ_SUCCESS;
+
+
+    } else if (sel && sel->type == PJSIP_TPSELECTOR_LISTENER &&
+	       sel->u.listener)
+    {
+	/* Application has requested that a specific listener is to
+	 * be used. In this case, skip transport hash table lookup.
+	 */
+
+	/* Verify that the listener type matches the destination type */
+	if (sel->u.listener->type != type) {
+	    pj_lock_release(mgr->lock);
+	    return PJSIP_ETPNOTSUITABLE;
+	}
+
+	/* We'll use this listener to create transport */
+	factory = sel->u.listener;
+
+    } else {
+
+	/*
+	 * This is the "normal" flow, where application doesn't specify
+	 * specific transport/listener to be used to send message to.
+	 * In this case, lookup the transport from the hash table.
+	 */
+	pjsip_transport_key key;
+	int key_len;
+	pjsip_transport *transport;
+
+	pj_bzero(&key, sizeof(key));
+	key_len = sizeof(key.type) + addr_len;
+
+	/* First try to get exact destination. */
+	key.type = type;
+	pj_memcpy(&key.rem_addr, remote, addr_len);
+
+	transport = (pjsip_transport*)
+		    pj_hash_get(mgr->table, &key, key_len, NULL);
+
+	if (transport == NULL) {
+	    unsigned flag = pjsip_transport_get_flag_from_type(type);
+	    const pj_sockaddr *remote_addr = (const pj_sockaddr*)remote;
+
+
+	    /* Ignore address for loop transports. */
+	    if (type == PJSIP_TRANSPORT_LOOP ||
+		     type == PJSIP_TRANSPORT_LOOP_DGRAM)
+	    {
+		pj_sockaddr *addr = &key.rem_addr;
+
+		pj_bzero(addr, addr_len);
+		key_len = sizeof(key.type) + addr_len;
+		transport = (pjsip_transport*) 
+			    pj_hash_get(mgr->table, &key, key_len, NULL);
+	    }
+	    /* For datagram transports, try lookup with zero address.
+	     */
+	    else if (flag & PJSIP_TRANSPORT_DATAGRAM)
+	    {
+		pj_sockaddr *addr = &key.rem_addr;
+
+		pj_bzero(addr, addr_len);
+		addr->addr.sa_family = remote_addr->addr.sa_family;
+
+		key_len = sizeof(key.type) + addr_len;
+		transport = (pjsip_transport*)
+			    pj_hash_get(mgr->table, &key, key_len, NULL);
+	    }
+	}
+
+	if (transport!=NULL && !transport->is_shutdown) {
+	    /*
+	     * Transport found!
+	     */
+	    pjsip_transport_add_ref(transport);
+	    pj_lock_release(mgr->lock);
+	    *tp = transport;
+
+	    TRACE_((THIS_FILE, "Transport %s acquired", transport->obj_name));
+	    return PJ_SUCCESS;
+	}
+
+	/*
+	 * Transport not found!
+	 * Find factory that can create such transport.
+	 */
+	factory = mgr->factory_list.next;
+	while (factory != &mgr->factory_list) {
+	    if (factory->type == type)
+		break;
+	    factory = factory->next;
+	}
+
+	if (factory == &mgr->factory_list) {
+	    /* No factory can create the transport! */
+	    pj_lock_release(mgr->lock);
+	    TRACE_((THIS_FILE, "No suitable factory was found either"));
+	    return PJSIP_EUNSUPTRANSPORT;
+	}
+    }
+
+    TRACE_((THIS_FILE, "Creating new transport from factory"));
+
+    /* Request factory to create transport. */
+    if (factory->create_transport2) {
+	status = factory->create_transport2(factory, mgr, mgr->endpt,
+					    (const pj_sockaddr*) remote, 
+					    addr_len, tdata, tp);
+    } else {
+	status = factory->create_transport(factory, mgr, mgr->endpt,
+					   (const pj_sockaddr*) remote, 
+					   addr_len, tp);
+    }
+    if (status == PJ_SUCCESS) {
+	PJ_ASSERT_ON_FAIL(tp!=NULL, 
+	    {pj_lock_release(mgr->lock); return PJ_EBUG;});
+	pjsip_transport_add_ref(*tp);
+    }
+    pj_lock_release(mgr->lock);
+    return status;
+}
+
+/**
+ * Dump transport info.
+ */
+PJ_DEF(void) pjsip_tpmgr_dump_transports(pjsip_tpmgr *mgr)
+{
+#if PJ_LOG_MAX_LEVEL >= 3
+    pj_hash_iterator_t itr_val;
+    pj_hash_iterator_t *itr;
+    pjsip_tpfactory *factory;
+
+    pj_lock_acquire(mgr->lock);
+
+#if defined(PJ_DEBUG) && PJ_DEBUG!=0
+    PJ_LOG(3,(THIS_FILE, " Outstanding transmit buffers: %d",
+	      pj_atomic_get(mgr->tdata_counter)));
+#endif
+
+    PJ_LOG(3, (THIS_FILE, " Dumping listeners:"));
+    factory = mgr->factory_list.next;
+    while (factory != &mgr->factory_list) {
+	PJ_LOG(3, (THIS_FILE, "  %s %s:%.*s:%d", 
+		   factory->obj_name,
+		   factory->type_name,
+		   (int)factory->addr_name.host.slen,
+		   factory->addr_name.host.ptr,
+		   (int)factory->addr_name.port));
+	factory = factory->next;
+    }
+
+    itr = pj_hash_first(mgr->table, &itr_val);
+    if (itr) {
+	PJ_LOG(3, (THIS_FILE, " Dumping transports:"));
+
+	do {
+	    pjsip_transport *t = (pjsip_transport*) 
+	    			 pj_hash_this(mgr->table, itr);
+
+	    PJ_LOG(3, (THIS_FILE, "  %s %s (refcnt=%d%s)", 
+		       t->obj_name,
+		       t->info,
+		       pj_atomic_get(t->ref_cnt),
+		       (t->idle_timer.id ? " [idle]" : "")));
+
+	    itr = pj_hash_next(mgr->table, itr);
+	} while (itr);
+    }
+
+    pj_lock_release(mgr->lock);
+#else
+    PJ_UNUSED_ARG(mgr);
+#endif
+}
+
+/**
+ * Set callback of global transport state notification.
+ */
+PJ_DEF(pj_status_t) pjsip_tpmgr_set_state_cb(pjsip_tpmgr *mgr,
+					     pjsip_tp_state_callback cb)
+{
+    PJ_ASSERT_RETURN(mgr, PJ_EINVAL);
+
+    mgr->tp_state_cb = cb;
+
+    return PJ_SUCCESS;
+}
+
+/**
+ * Get callback of global transport state notification.
+ */
+PJ_DEF(pjsip_tp_state_callback) pjsip_tpmgr_get_state_cb(
+					     const pjsip_tpmgr *mgr)
+{
+    PJ_ASSERT_RETURN(mgr, NULL);
+
+    return mgr->tp_state_cb;
+}
+
+
+/**
+ * Allocate and init transport data.
+ */
+static void init_tp_data(pjsip_transport *tp)
+{
+    transport_data *tp_data;
+
+    pj_assert(tp && !tp->data);
+
+    tp_data = PJ_POOL_ZALLOC_T(tp->pool, transport_data);
+    pj_list_init(&tp_data->st_listeners);
+    pj_list_init(&tp_data->st_listeners_empty);
+    tp->data = tp_data;
+}
+
+
+static void tp_state_callback(pjsip_transport *tp,
+			      pjsip_transport_state state,
+			      const pjsip_transport_state_info *info)
+{
+    transport_data *tp_data;
+
+    pj_lock_acquire(tp->lock);
+
+    tp_data = (transport_data*)tp->data;
+
+    /* Notify the transport state listeners, if any. */
+    if (!tp_data || pj_list_empty(&tp_data->st_listeners)) {
+	goto on_return;
+    } else {
+	pjsip_transport_state_info st_info;
+	tp_state_listener *st_listener = tp_data->st_listeners.next;
+
+	/* As we need to put the user data into the transport state info,
+	 * let's use a copy of transport state info.
+	 */
+	pj_memcpy(&st_info, info, sizeof(st_info));
+	while (st_listener != &tp_data->st_listeners) {
+	    st_info.user_data = st_listener->user_data;
+	    (*st_listener->cb)(tp, state, &st_info);
+
+	    st_listener = st_listener->next;
+	}
+    }
+
+on_return:
+    pj_lock_release(tp->lock);
+}
+
+
+/**
+ * Add a listener to the specified transport for transport state notification.
+ */
+PJ_DEF(pj_status_t) pjsip_transport_add_state_listener (
+					    pjsip_transport *tp,
+					    pjsip_tp_state_callback cb,
+					    void *user_data,
+					    pjsip_tp_state_listener_key **key)
+{
+    transport_data *tp_data;
+    tp_state_listener *entry;
+
+    PJ_ASSERT_RETURN(tp && cb && key, PJ_EINVAL);
+
+    pj_lock_acquire(tp->lock);
+
+    /* Init transport data, if it hasn't */
+    if (!tp->data)
+	init_tp_data(tp);
+
+    tp_data = (transport_data*)tp->data;
+
+    /* Init the new listener entry. Use available empty slot, if any,
+     * otherwise allocate it using the transport pool.
+     */
+    if (!pj_list_empty(&tp_data->st_listeners_empty)) {
+	entry = tp_data->st_listeners_empty.next;
+	pj_list_erase(entry);
+    } else {
+	entry = PJ_POOL_ZALLOC_T(tp->pool, tp_state_listener);
+    }
+    entry->cb = cb;
+    entry->user_data = user_data;
+
+    /* Add the new listener entry to the listeners list */
+    pj_list_push_back(&tp_data->st_listeners, entry);
+
+    *key = entry;
+
+    pj_lock_release(tp->lock);
+
+    return PJ_SUCCESS;
+}
+
+/**
+ * Remove a listener from the specified transport for transport state 
+ * notification.
+ */
+PJ_DEF(pj_status_t) pjsip_transport_remove_state_listener (
+				    pjsip_transport *tp,
+				    pjsip_tp_state_listener_key *key,
+				    const void *user_data)
+{
+    transport_data *tp_data;
+    tp_state_listener *entry;
+
+    PJ_ASSERT_RETURN(tp && key, PJ_EINVAL);
+
+    pj_lock_acquire(tp->lock);
+
+    tp_data = (transport_data*)tp->data;
+
+    /* Transport data is NULL or no registered listener? */
+    if (!tp_data || pj_list_empty(&tp_data->st_listeners)) {
+	pj_lock_release(tp->lock);
+	return PJ_ENOTFOUND;
+    }
+
+    entry = (tp_state_listener*)key;
+
+    /* Validate the user data */
+    if (entry->user_data != user_data) {
+	pj_assert(!"Invalid transport state listener key");
+	pj_lock_release(tp->lock);
+	return PJ_EBUG;
+    }
+
+    /* Reset the entry and move it to the empty list */
+    entry->cb = NULL;
+    entry->user_data = NULL;
+    pj_list_erase(entry);
+    pj_list_push_back(&tp_data->st_listeners_empty, entry);
+
+    pj_lock_release(tp->lock);
+
+    return PJ_SUCCESS;
+}
diff --git a/jni/pjproject-android/.svn/pristine/bc/bcf659da2a804ef195c952e32f30aae31a6b1eba.svn-base b/jni/pjproject-android/.svn/pristine/bc/bcf659da2a804ef195c952e32f30aae31a6b1eba.svn-base
new file mode 100644
index 0000000..88e3a99
--- /dev/null
+++ b/jni/pjproject-android/.svn/pristine/bc/bcf659da2a804ef195c952e32f30aae31a6b1eba.svn-base
@@ -0,0 +1,70 @@
+/* $Id$ */
+/* 
+ * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
+ * Copyright (C) 2003-2008 Benny Prijono <benny@prijono.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 __PJMEDIA_NULL_PORT_H__
+#define __PJMEDIA_NULL_PORT_H__
+
+/**
+ * @file null_port.h
+ * @brief Null media port.
+ */
+#include <pjmedia/port.h>
+
+
+
+/**
+ * @defgroup PJMEDIA_NULL_PORT Null Port
+ * @ingroup PJMEDIA_PORT
+ * @brief The simplest type of media port which does nothing.
+ * @{
+ */
+
+
+PJ_BEGIN_DECL
+
+
+/**
+ * Create Null port. 
+ *
+ * @param pool			Pool to allocate memory.
+ * @param sampling_rate		Sampling rate of the port.
+ * @param channel_count		Number of channels.
+ * @param samples_per_frame	Number of samples per frame.
+ * @param bits_per_sample	Number of bits per sample.
+ * @param p_port		Pointer to receive the port instance.
+ *
+ * @return			PJ_SUCCESS on success.
+ */
+PJ_DECL(pj_status_t) pjmedia_null_port_create( pj_pool_t *pool,
+					       unsigned sampling_rate,
+					       unsigned channel_count,
+					       unsigned samples_per_frame,
+					       unsigned bits_per_sample,
+					       pjmedia_port **p_port );
+
+
+
+PJ_END_DECL
+
+/**
+ * @}
+ */
+
+
+#endif	/* __PJMEDIA_NULL_PORT_H__ */