* #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,
+ ¶m->output_vol);
+ }
+ if(param->flags & PJMEDIA_AUD_DEV_CAP_INPUT_VOLUME_SETTING) {
+ stream_set_capBDIMAD(&strm->base,
+ PJMEDIA_AUD_DEV_CAP_INPUT_VOLUME_SETTING,
+ ¶m->input_vol);
+ }
+ if(param->flags & PJMEDIA_AUD_DEV_CAP_EC) {
+ stream_set_capBDIMAD(&strm->base,
+ PJMEDIA_AUD_DEV_CAP_EC,
+ ¶m->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__ */