* #36737: switch back to svn repo, remove assert in sip_transaction.c
diff --git a/jni/pjproject-android/.svn/pristine/49/4974e03b1fe9d2535d13852c5c2d31dddecd8fd2.svn-base b/jni/pjproject-android/.svn/pristine/49/4974e03b1fe9d2535d13852c5c2d31dddecd8fd2.svn-base
new file mode 100644
index 0000000..2c741e4
--- /dev/null
+++ b/jni/pjproject-android/.svn/pristine/49/4974e03b1fe9d2535d13852c5c2d31dddecd8fd2.svn-base
@@ -0,0 +1,35 @@
+# $Id$
+import inc_sip as sip
+import inc_sdp as sdp
+
+sdp = \
+"""
+v=0
+o=- 0 0 IN IP4 127.0.0.1
+s=pjmedia
+c=IN IP4 127.0.0.1
+t=0 0
+m=audio 4000 RTP/AVP 0 101
+a=ice-ufrag:1234
+a=ice-pwd:5678
+a=rtpmap:0 PCMU/8000
+a=sendrecv
+a=rtpmap:101 telephone-event/8000
+a=fmtp:101 0-15
+a=candidate:XX 1 UDP 1234 127.0.0.1 4000 typ host
+a=candidate:YY 2 UDP 1234 127.0.0.1 4001 typ host
+"""
+
+args = "--null-audio --use-ice --auto-answer 200 --max-calls 1"
+include = ["a=ice-ufrag", # must have ICE
+ "a=candidate:[0-9a-zA-Z]+ 2 UDP" # must have RTCP component
+ ]
+exclude = [
+ "ice-mismatch" # must not mismatch
+ ]
+
+sendto_cfg = sip.SendtoCfg( "caller sends two components without a=rtcp line",
+ pjsua_args=args, sdp=sdp, resp_code=200,
+ resp_inc=include, resp_exc=exclude,
+ enable_buffer = True)
+
diff --git a/jni/pjproject-android/.svn/pristine/49/4995bd2698d43a8dcd2f041a39573fd8d8565668.svn-base b/jni/pjproject-android/.svn/pristine/49/4995bd2698d43a8dcd2f041a39573fd8d8565668.svn-base
new file mode 100644
index 0000000..929fc21
--- /dev/null
+++ b/jni/pjproject-android/.svn/pristine/49/4995bd2698d43a8dcd2f041a39573fd8d8565668.svn-base
@@ -0,0 +1,547 @@
+ /**
+ * @file bdIMADpj.h
+ * @brief bdSound IMproved Audio Device for PJSIP.
+ */
+
+/**
+ * @defgroup bd_IMAD bdIMADpj bdSound IMproved Audio Device for PJSIP.
+ * @ingroup audio_device_api
+ *
+ * <b>bdSound IMproved Audio Device</b> is a multiplatform audio interface
+ * created to integrate in <b>PJSIP</b> library with no effort.
+ * \n Porting <b>bdIMADpj</b> across the main operating systems is
+ * straightforward, without the need of change a single line of code.
+ *
+ * - <b>Features</b>
+ * - Echo cancellation (Full Duplex)
+ * - Noise reduction
+ * - Automatic Gain Control
+ * - Audio Enhancement
+ *
+ * - <b>Supported operating systems</b>
+ * - Windows
+ * - Android
+ * - MacOS X
+ * - iOS
+ * - Linux / Alsa
+ *
+ * - <b>Supported platforms</b>
+ * - x86
+ * - x64
+ * - ARM Cortex-A8/A9/A15 with NEON
+ *
+ * Visit <a href="http:/www.bdsound.com" target="new">bdSound</a> for updated
+ * features, supported operating systems and platforms.
+ *
+ * <b>Using PJSIP with bdIMAD audio device</b>
+ *
+ * - <b>Integration</b>
+ * \n Using <b>bdIMAD</b> within <b>PJSIP</b> is simple:
+ * -# Request for bdIMADpj library to
+ * <a href="http:/www.bdsound.com" target="new">bdSound</a>:
+ * bdSound will provide instruction to integrate the library depending on
+ * the platform / O.S. / toolchain;
+ * -# Add the <code>bdimad_dev.c</code> file to
+ * <code>pjmedia/src/pjmedia-audiodev</code> folder;
+ * -# Enable the bdIMAD audio device defining the periferal in the
+ * <code>pj/config_site.h</code> and disabling all other devices:
+ * <pre>
+ * #define PJMEDIA_AUDIO_DEV_HAS_BDIMAD 1
+ * </pre>
+ *
+ * - <b>Usage</b>
+ * \n There are only a couple of things the customer have to pay attention on
+ * §when using bdIMAD library.
+ *
+ * - <b>Initialization</b>
+ * \n Since the bdIMAD library provide itself the echo cancellation
+ * and the latency management, is necessary to disable these features
+ * in the PJSIP librariy applications.
+ * \n For example in PJSUA sample application there is the need
+ * to provide the following commands:
+ * <pre>
+ * --ec-tail=0
+ * --no-vad
+ * --capture-lat=0
+ * --playback-lat=0
+ * </pre>
+ *
+ * - <b>Supported set capability</b>
+ * - <code>PJMEDIA_AUD_DEV_CAP_OUTPUT_VOLUME_SETTING</code>
+ * \n Setting speaker volume.
+ * - <code>PJMEDIA_AUD_DEV_CAP_INPUT_VOLUME_SETTING</code>
+ * \n Setting michrophone volume.
+ * - <code>PJMEDIA_AUD_DEV_CAP_EC</code>
+ * \n Enable/disable echo cancellation.
+ *
+ * For additional information visit
+ * <a href="http:/www.bdsound.com" target="new">www.bdsound.com</a>
+ * or write to info@bdsound.com
+ *
+ * @author bdSound
+ * @version 1.0.1
+ * @copyright 2012 bdSound srl. All rights reserved.
+ *
+ */
+
+/**
+ * @defgroup groupFunction Functions
+ * @ingroup bd_IMAD
+ *
+ * Functions defined in bdIMAD.
+ */
+
+/**
+ * @defgroup groupCallback Callbacks
+ * @ingroup bd_IMAD
+ *
+ * Callbacks defined in bdIMAD.
+ */
+
+/**
+ * @defgroup groupStructEnum Structs and Enums
+ * @ingroup bd_IMAD
+ *
+ * Struct and Enum defined in bdIMAD.
+ */
+
+#ifndef BD_IMAD_PJ_H__
+#define BD_IMAD_PJ_H__
+
+/**
+ * @brief Macro for Windows DLL Support.
+ */
+
+#ifdef _BDIMADPJ_EXPORTDLL
+ #ifdef WIN32
+ #define BDIMADPJ_API __declspec(dllexport)
+ #else
+ #define BDIMADPJ_API __attribute__((visibility("default")))
+ #endif
+#else
+ #define BDIMADPJ_API
+#endif
+
+#define BD_IMAD_CAPTURE_DEVICES 1
+#define BD_IMAD_PLAYBACK_DEVICES 0
+#define BD_IMAD_DIAGNOSTIC_ENABLE 1
+#define BD_IMAD_DIAGNOSTIC_DISABLE 0
+
+#define BD_IMAD_BITS_X_SAMPLE 16 /**< Bits per sample */
+
+typedef void* bdIMADpj;
+
+/**
+ * @addtogroup groupCallback
+ * @{
+ */
+
+/**
+ * @brief Callback used to fill the playback buffer of bdIMAD.
+ * The function is called by bdIMAD each time are required sample to be played.
+ * @param[in] *buffer pointer to the buffer with the audio
+ * samples to be played(short type).
+ * @param[in] nSamples number of samples required.
+ * @param[in] user_data pointer to the user data structure
+ * defined in the bdIMADpj_Setting_t
+ * structure.
+ * @return none.
+ */
+
+typedef int (* cb_fillPlayBackB_t) (void *buffer, int nSamples,
+ void *user_data);
+
+/**
+ * @brief Callback used to retrive the caputre buffer of bdIMAD. The function
+ * is called by bdIMAD each time processed mic samples are available.
+ * @param[out] *buffer pointer to the buffer with the audio
+ * samples to download(short type).
+ * @param[in] nSamples number of samples processed to download.
+ * @param[in] user_data pointer to the user data structure
+ * defined in the MainSet structure.
+ * @return none.
+ */
+
+typedef void (* cb_emptyCaptureB_t) (void *buffer, int nSamples,
+ void *user_data);
+/**
+ * @}
+ */
+
+/**
+ * @addtogroup groupStructEnum
+ * @{
+ */
+
+/**
+ * @brief Error status returned by some functions in the library.
+ */
+
+typedef enum bdIMADpj_Status{
+ /**< No error. */
+ BD_PJ_OK = 0,
+ /**< Watch bdIMADpj_Warnings_t structure to find the warnings. */
+ BD_PJ_WARN_BDIMAD_WARNING_ASSERTED = 1,
+ /**< Error not identified. */
+ BD_PJ_ERROR_GENERIC = 2,
+ /**< The pointer passed is NULL. */
+ BD_PJ_ERROR_NULL_POINTER = 3,
+ /**< Allocation procedure failed. */
+ BD_PJ_ERROR_ALLOCATION = 4,
+ /**< The parameter is not existent or the set/get function is not active. */
+ BD_PJ_ERROR_PARAMETER_NOT_FOUND = 5,
+ /**< No capture device found. */
+ BD_PJ_ERROR_IMAD_NONE_CAPTURE_DEV = 10,
+ /**< No play device found. */
+ BD_PJ_ERROR_IMAD_NONE_PLAY_DEV = 11,
+ /**< Frame size not allowed. */
+ BD_PJ_ERROR_IMAD_FRAME_SIZE = 12,
+ /**< Sample frequency not allowed. */
+ BD_PJ_ERROR_IMAD_SAMPLE_FREQ = 13,
+ /**< Samples missing. */
+ BD_PJ_ERROR_IMAD_MISSING_SAMPLES = 14,
+ /**< Device list is empty. */
+ BD_PJ_ERROR_IMAD_DEVICE_LIST_EMPTY = 15,
+ /**< Library not authorized, entering demo mode. */
+ BD_PJ_ERROR_IMAD_LIB_NOT_AUTHORIZED = 16,
+ /**< The input channel memory has not been allocated. */
+ BD_PJ_ERROR_IMAD_INPUT_CH_NOT_ALLOCATED = 17,
+ /**< The library has expired, entering demo mode. */
+ BD_PJ_ERROR_IMAD_LICENSE_EXPIRED = 18,
+ /**< Open of capture device failed. */
+ BD_PJ_ERROR_IMAD_OPEN_CAPTURE_DEV_FAILED = 19,
+ /**< Open of play device failed. */
+ BD_PJ_ERROR_IMAD_OPEN_PLAY_DEV_FAILED = 20,
+ /**< Start of play device failed. */
+ BD_PJ_ERROR_IMAD_START_PLAY_DEV_FAILED = 21,
+ /**< Start of capture device failed. */
+ BD_PJ_ERROR_IMAD_START_CAPTURE_DEV_FAILED = 22,
+ /**< Start of time process failed. */
+ BD_PJ_ERROR_IMAD_START_TIME_PROCESS_FAILED = 23,
+ /**< Start of thread process failed. */
+ BD_PJ_ERROR_IMAD_THREAD_PROCESS_FAILED = 24,
+ /**< No volume control available. */
+ BD_PJ_ERROR_IMAD_NO_VOL_CONTROL_AVAILABLE = 25,
+} bdIMADpj_Status;
+
+/**
+ * @brief Parameter to pass to set and get parameter functions.
+ *
+ * For each enumeration are defined the data type and the supported operations
+ * on that parameter (set and get).
+ */
+
+typedef enum bdIMADpj_Parameter{
+ /**< int* \n set/get \n 1 enable / 0 disable echo cancellation. */
+ BD_PARAM_IMAD_PJ_AEC_ENABLE = 1,
+ /**< int* \n set/get \n 1 enable / 0 disable microphone control
+ * (when possible). */
+ BD_PARAM_IMAD_PJ_MIC_CONTROL_ENABLE = 2,
+ /**< int* \n set/get \n 1 ebable / 0 disable noise reduction. */
+ BD_PARAM_IMAD_PJ_NOISE_REDUCTION_ENABLE = 3,
+ /**< int* \n set \n number of channel to reset. Used to reset
+ * the input channel statistics. To be used when the same channel
+ * is assigned to another partecipant. */
+ BD_PARAM_IMAD_PJ_RESET_STATISTIC_IN_CH = 4,
+ /**< float* \n set/get \n 0.0f -> 1.0f volume of
+ * the microphone(when possible). */
+ BD_PARAM_IMAD_PJ_MIC_VOLUME = 5,
+ /**< int* \n set/get \n 0 mute / 1 not mute on microphone
+ * (when possible). */
+ BD_PARAM_IMAD_PJ_MIC_MUTE = 6,
+ /**< float* \n set/get \n 0.0f -> 1.0f volume of the speaker. */
+ BD_PARAM_IMAD_PJ_SPK_VOLUME = 7,
+ /**< int* \n set/get \n 0 mute / 1 not mute on speaker. */
+ BD_PARAM_IMAD_PJ_SPK_MUTE = 8,
+} bdIMADpj_Parameter;
+
+
+/**
+ * @brief Instance structure for the information regarding the aec engine.
+ */
+
+typedef struct bdIMADpj_Setting_t{
+ /**< Sample frequency (8kHz or 16kHz). */
+ int SamplingFrequency;
+ /**< Audio buffer managed by the aec bdIMAD functions.
+ * (from 16ms to 80ms, 16ms recommended). */
+ int FrameSize_ms;
+ /**< Points to the validation functions in the validation library. */
+ void *validate;
+ /**< Points to the the callback function used for filling
+ * the playback buffer of bdIMAD. */
+ cb_fillPlayBackB_t cb_fillPlayBackBuffer;
+ /**< Points to user data to pass to the callback. */
+ void *cb_fillPlayBackBuffer_user_data;
+ /**< Points to the callback function used for retreive the processed
+ * audio present in the capture buffer of bdIMAD. */
+ cb_emptyCaptureB_t cb_emptyCaptureBuffer;
+ /**< Points to user data to pass to the callback. */
+ void *cb_emptyCaptureBuffer_user_data;
+ /**< Is a wide char pointer to the capture device name. */
+ wchar_t *CaptureDevice;
+ /**< Is a wide char pointer to the play device name. */
+ wchar_t *PlayDevice;
+ /**< True to enable diagnostic, false to disable. */
+ int DiagnosticEnable;
+ /**< Directory which will contains the files generated for diagnostic. */
+ wchar_t *DiagnosticFolderPath;
+ /**< Is an auxiliary settings pointer used internally by bdIMAD. */
+ void *bdIMADwr_SettingsData;
+} bdIMADpj_Setting_t;
+
+/**
+ * @brief Instance structure for the warnings generated by the initialization
+ * functions.
+ */
+
+typedef struct bdIMADpj_Warnings_t{
+ /**< The capture device indicated can't be opened, has been selected
+ * the default capture device. */
+ int DefaultCaptureDeviceAutomaticallySelected;
+ /**< The capture device opened has not volume control. */
+ int CaptureDeviceWithoutVolumeControl;
+ /**< The play device indicated can't be opened, has been selected
+ * the default play device. */
+ int DefaultPlayDeviceAutomaticallySelected;
+ /**< The number of channel requested is out of range. The number of
+ * channel opened is equal to the maximum. */
+ int NumberOfChannelsOutOfRange;
+ /**< The diagnostic files could not be saved. */
+ int DiagnosticSaveNotAllowed;
+ /**< The nlp level requested is not allowed, it has been automatically
+ * changed to the default value. */
+ int nlpLevelChangeSettting;
+ /**< No capture device is present. Anyway the bdSES has been
+ * istantiated only for playback. */
+ int NoCaptureDevicePresent;
+ /**< The cpu is not adapt to run the aec engine, the aec has been disabled.
+ * This appens for very old cpu like pentium III. */
+ int oldCPUdetected_AECdisable;
+ /**< Windows Direct Sound error. */
+ long directSoundError;
+ /**< Windows Direct Sound volume error. */
+ long directSoundLevel;
+ /**< No play device is present. Anyway the bdSES has been istantiated
+ * only for capture. */
+ int NoPlayDevicePresent;
+} bdIMADpj_Warnings_t;
+
+/**
+ * @brief Instance structure for the library version
+ */
+
+typedef struct bdIMADpj_libVersion_t{
+ int major; /**< major version. */
+ int minor; /**< minor version. */
+ int build; /**< build number. */
+ char *name; /**< name "bdIMADpj ver.X". */
+ char *version; /**< beta, RC, release. */
+ char *buildDate; /**< build date. */
+} bdIMADpj_libVersion_t;
+
+/**
+ * @}
+ */
+
+
+
+/**
+ * @addtogroup groupFunction
+ * @{
+ */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * @brief Must be used to allocate and set to default parameter the memory
+ * for the bdIMAD.
+ *
+ * The function generate a structure bdIMADpj_Setting_t filled with the
+ * default settings.
+ * \n The user can change this settings according to the need and then
+ * launch the ::bdIMADpj_InitAEC.
+ * \n The function generate also a warning structure (::bdIMADpj_Warnings_t)
+ * that could be used in ::bdIMADpj_InitAEC to handle eventual warnings.
+ * @param[out] **ppSettings Points to the pointer of the
+ * allocated ::bdIMADpj_Setting_t.
+ * @param[out] **ppWarningMessages Points to the pointer of the
+ * allocated ::bdIMADpj_Warnings_t.
+ * @return ::BD_PJ_OK if the function has been
+ * performed successfully, otherwise return
+ * an error (refer to ::bdIMADpj_Status).
+ */
+BDIMADPJ_API bdIMADpj_Status bdIMADpj_CreateStructures(
+ bdIMADpj_Setting_t **ppSettings,
+ bdIMADpj_Warnings_t **ppWarningMessages);
+
+/**
+ * @brief Is used to free the memory for the ::bdIMADpj_Setting_t structure and
+ * ::bdIMADpj_Warnings_t structure allocated with
+ * the ::bdIMADpj_CreateStructures.
+ * @param[in] **ppSettings Pointer to a memory location filled
+ * with the address of the
+ * ::bdIMADpj_Setting_t structure to free.
+ * This address will be set to NULL.
+ * @param[in] **ppWarningMessages Pointer to a memory location filled
+ * with the address of the allocated
+ * ::bdIMADpj_Warnings_t structure to free.
+ * This address will be set to NULL.
+ * @return ::BD_PJ_OK if the function has been
+ * performed successfully, otherwise return
+ * an error (refer to ::bdIMADpj_Status).
+ */
+BDIMADPJ_API bdIMADpj_Status bdIMADpj_FreeStructures(
+ bdIMADpj_Setting_t **ppSettings,
+ bdIMADpj_Warnings_t **ppWarningMessages);
+
+/**
+ * @brief Is used to initialize the memory for bdIMAD with the settings
+ * contained in the <code>ppSettings</code>.
+ * @param[out] *pBdIMADInstance Is the pointer to the bdIMAD object.
+ * @param[in] **ppSettings Pointer to pointer to a
+ * ::bdIMADpj_Setting_t structure, filled
+ * with initialization settings to be
+ * applied to the bdIMAD.
+ * \n Note, the <code>pBdIMADInstance</code>
+ * is modified with the applied settings.
+ * @param[out] **ppWarningMessages Pointer to pointer to a
+ * ::bdIMADpj_Warnings_t sructure,
+ * which reports the warnings after the
+ * initialization.
+ * @return ::BD_PJ_OK if the function has been
+ * performed successfully, otherwise return
+ * an error (refer to ::bdIMADpj_Status).
+ * \n If the error is
+ * ::BD_PJ_WARN_BDIMAD_WARNING_ASSERTED
+ * the init has been performed with success,
+ * but with a different settings
+ * respect to the ones required.
+ * This mainly happens if the audio
+ * device opened is different to the
+ * one requested.
+ */
+BDIMADPJ_API bdIMADpj_Status bdIMADpj_InitAEC(bdIMADpj *pBdIMADInstance,
+ bdIMADpj_Setting_t **ppSettings,
+ bdIMADpj_Warnings_t **ppWarningMessages);
+
+/**
+ * @brief Is used to free the bdIMAD object pointed by the
+ * <code>pBdIMADInstance</code>.
+ * @param[in] *pBdIMADInstance Pointer to the bdIMAD object to free.
+ * @return ::BD_PJ_OK if the function has been
+ * performed successfully, otherwise return
+ * an error (refer to ::bdIMADpj_Status).
+ */
+BDIMADPJ_API bdIMADpj_Status bdIMADpj_FreeAEC(bdIMADpj *pBdIMADInstance);
+
+/**
+ * @brief Is used to make a list of capure and play devices available
+ * on the system.
+ * @param[in] captureDevice Set to 1 to get the list of capture
+ * devices. Set to 0 to get the list of
+ * play devices.
+ * @param[in] **deviceName Pointer to pointer to a wide char
+ * containing the names of capture/play
+ * devices.
+ * @return ::BD_PJ_OK if the function has been
+ * performed successfully, otherwise return
+ * an error (refer to ::bdIMADpj_Status).
+ */
+BDIMADPJ_API bdIMADpj_Status bdIMADpj_getDeviceName(int captureDevice,
+ wchar_t **deviceName);
+
+/**
+ * @brief Is used to freeze the bdIMAD, stopping the audio playback
+ * and recording.
+ * @param[in] bdIMADInstance bdIMAD object.
+ * @return ::BD_PJ_OK if the function has been
+ * performed successfully, otherwise
+ * return an error (refer to
+ * ::bdIMADpj_Status).
+ */
+BDIMADPJ_API bdIMADpj_Status bdIMADpj_stop(bdIMADpj bdIMADInstance);
+
+/**
+ * @brief Is used to put back in play the audio after it has been stopped by the
+ * ::bdIMADpj_stop functions.
+ * @param[in] bdIMADInstance bdIMAD object.
+ * @return ::BD_PJ_OK if the function has been
+ * performed successfully, otherwise
+ * return an error (refer to
+ * ::bdIMADpj_Status).
+ */
+BDIMADPJ_API bdIMADpj_Status bdIMADpj_run(bdIMADpj bdIMADInstance);
+
+/**
+ * @brief Print on a standard output the warning messages.
+ * @param[in] *pWarningMessages Pointer to the warning structure
+ * to be printed.
+ * @return ::BD_PJ_OK if the function has been
+ * performed successfully, otherwise
+ * return an error
+ * (refer to ::bdIMADpj_Status).
+ */
+BDIMADPJ_API bdIMADpj_Status bdIMADpj_DisplayWarnings(
+ bdIMADpj_Warnings_t *pWarningMessages);
+
+/**
+ * @brief Clear the warning structure after being read.
+ * @param[out] **ppWarningMessages Pointer to pointer to the warning
+ * structure to be cleared.
+ * @return ::BD_PJ_OK if the function has been
+ * performed successfully, otherwise
+ * return an error (refer to
+ * ::bdIMADpj_Status).
+ */
+BDIMADPJ_API bdIMADpj_Status bdIMADpj_ClearAllWarnings(
+ bdIMADpj_Warnings_t **ppWarningMessages);
+
+/**
+ * @brief Is used to set a parameter of the bdIMAD object pointed by the
+ * <code>pBdIMADInstance</code>.
+ * @param[in] bdIMADInstance bdIMAD object.
+ * @param[in] parameterName Indicate the parameter to set.
+ * @param[in] *pValue Is a pointer to the value to set
+ * cast to void.
+ * \n In the ::bdIMADpj_Parameter
+ * declaration is indicated the real type of
+ * the value, depending on the
+ * <code>parameterName</code>.
+ * @return ::BD_PJ_OK if the function has been
+ * performed successfully, otherwise
+ * return an error (refer to
+ * §::bdIMADpj_Status).
+ */
+BDIMADPJ_API bdIMADpj_Status bdIMADpj_setParameter(bdIMADpj bdIMADInstance,
+ bdIMADpj_Parameter parameterName, void *pValue);
+
+/**
+ * @brief Is used to get a parameter of the bdIMAD object pointed by the
+ * <code>pBdIMADInstance</code>.
+ * @param[in] bdIMADInstance bdIMAD object.
+ * @param[in] parameterName Indicate the parameter to get.
+ * @param[out] *pValue Is a pointer to the value to get cast
+ * to void. \n In the
+ * ::bdIMADpj_Parameter declaration is
+ * indicated the real type of the value,
+ * depending on the
+ * <code>parameterName</code>.
+ * @return ::BD_PJ_OK if the function has been
+ * performed successfully, otherwise return
+ * an error (refer to ::bdIMADpj_Status).
+ */
+BDIMADPJ_API bdIMADpj_Status bdIMADpj_getParameter(bdIMADpj bdIMADInstance,
+ bdIMADpj_Parameter parameterName, void *pValue);
+
+
+#ifdef __cplusplus
+}
+#endif
+/**
+ * @}
+ */
+
+#endif //BD_IMAD_PJ_H__
diff --git a/jni/pjproject-android/.svn/pristine/49/499d59e0e9859840f9f6361ec06f87181e5ef446.svn-base b/jni/pjproject-android/.svn/pristine/49/499d59e0e9859840f9f6361ec06f87181e5ef446.svn-base
new file mode 100644
index 0000000..645ed39
--- /dev/null
+++ b/jni/pjproject-android/.svn/pristine/49/499d59e0e9859840f9f6361ec06f87181e5ef446.svn-base
@@ -0,0 +1,1196 @@
+/* $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 <pjmedia-audiodev/audiodev_imp.h>
+#include <pjmedia-audiodev/errno.h>
+#include <pjmedia/alaw_ulaw.h>
+#include <pj/assert.h>
+#include <pj/log.h>
+#include <pj/math.h>
+#include <pj/os.h>
+#include <pj/string.h>
+
+#if PJMEDIA_AUDIO_DEV_HAS_SYMB_MDA
+
+/*
+ * This file provides sound implementation for Symbian Audio Streaming
+ * device. Application using this sound abstraction must link with:
+ * - mediaclientaudiostream.lib, and
+ * - mediaclientaudioinputstream.lib
+ */
+#include <mda/common/audio.h>
+#include <mdaaudiooutputstream.h>
+#include <mdaaudioinputstream.h>
+
+
+#define THIS_FILE "symb_mda_dev.c"
+#define BITS_PER_SAMPLE 16
+#define BYTES_PER_SAMPLE (BITS_PER_SAMPLE/8)
+
+
+#if 1
+# define TRACE_(st) PJ_LOG(3, st)
+#else
+# define TRACE_(st)
+#endif
+
+
+/* MDA factory */
+struct mda_factory
+{
+ pjmedia_aud_dev_factory base;
+ pj_pool_t *pool;
+ pj_pool_factory *pf;
+ pjmedia_aud_dev_info dev_info;
+};
+
+/* Forward declaration of internal engine. */
+class CPjAudioInputEngine;
+class CPjAudioOutputEngine;
+
+/* MDA stream. */
+struct mda_stream
+{
+ // Base
+ pjmedia_aud_stream base; /**< Base class. */
+
+ // Pool
+ pj_pool_t *pool; /**< Memory pool. */
+
+ // Common settings.
+ pjmedia_aud_param param; /**< Stream param. */
+
+ // Audio engine
+ CPjAudioInputEngine *in_engine; /**< Record engine. */
+ CPjAudioOutputEngine *out_engine; /**< Playback engine. */
+};
+
+
+/* Prototypes */
+static pj_status_t factory_init(pjmedia_aud_dev_factory *f);
+static pj_status_t factory_destroy(pjmedia_aud_dev_factory *f);
+static pj_status_t factory_refresh(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 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);
+
+
+/* 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
+};
+
+
+/*
+ * Convert clock rate to Symbian's TMdaAudioDataSettings capability.
+ */
+static TInt get_clock_rate_cap(unsigned clock_rate)
+{
+ switch (clock_rate) {
+ case 8000: return TMdaAudioDataSettings::ESampleRate8000Hz;
+ case 11025: return TMdaAudioDataSettings::ESampleRate11025Hz;
+ case 12000: return TMdaAudioDataSettings::ESampleRate12000Hz;
+ case 16000: return TMdaAudioDataSettings::ESampleRate16000Hz;
+ case 22050: return TMdaAudioDataSettings::ESampleRate22050Hz;
+ case 24000: return TMdaAudioDataSettings::ESampleRate24000Hz;
+ case 32000: return TMdaAudioDataSettings::ESampleRate32000Hz;
+ case 44100: return TMdaAudioDataSettings::ESampleRate44100Hz;
+ case 48000: return TMdaAudioDataSettings::ESampleRate48000Hz;
+ case 64000: return TMdaAudioDataSettings::ESampleRate64000Hz;
+ case 96000: return TMdaAudioDataSettings::ESampleRate96000Hz;
+ default:
+ return 0;
+ }
+}
+
+/*
+ * Convert number of channels into Symbian's TMdaAudioDataSettings capability.
+ */
+static TInt get_channel_cap(unsigned channel_count)
+{
+ switch (channel_count) {
+ case 1: return TMdaAudioDataSettings::EChannelsMono;
+ case 2: return TMdaAudioDataSettings::EChannelsStereo;
+ default:
+ return 0;
+ }
+}
+
+/*
+ * Utility: print sound device error
+ */
+static void snd_perror(const char *title, TInt rc)
+{
+ PJ_LOG(1,(THIS_FILE, "%s: error code %d", title, rc));
+}
+
+//////////////////////////////////////////////////////////////////////////////
+//
+
+/*
+ * Implementation: Symbian Input Stream.
+ */
+class CPjAudioInputEngine : public CBase, MMdaAudioInputStreamCallback
+{
+public:
+ enum State
+ {
+ STATE_INACTIVE,
+ STATE_ACTIVE,
+ };
+
+ ~CPjAudioInputEngine();
+
+ static CPjAudioInputEngine *NewL(struct mda_stream *parent_strm,
+ pjmedia_aud_rec_cb rec_cb,
+ void *user_data);
+
+ static CPjAudioInputEngine *NewLC(struct mda_stream *parent_strm,
+ pjmedia_aud_rec_cb rec_cb,
+ void *user_data);
+
+ pj_status_t StartRecord();
+ void Stop();
+
+ pj_status_t SetGain(TInt gain) {
+ if (iInputStream_) {
+ iInputStream_->SetGain(gain);
+ return PJ_SUCCESS;
+ } else
+ return PJ_EINVALIDOP;
+ }
+
+ TInt GetGain() {
+ if (iInputStream_) {
+ return iInputStream_->Gain();
+ } else
+ return PJ_EINVALIDOP;
+ }
+
+ TInt GetMaxGain() {
+ if (iInputStream_) {
+ return iInputStream_->MaxGain();
+ } else
+ return PJ_EINVALIDOP;
+ }
+
+private:
+ State state_;
+ struct mda_stream *parentStrm_;
+ pjmedia_aud_rec_cb recCb_;
+ void *userData_;
+ CMdaAudioInputStream *iInputStream_;
+ HBufC8 *iStreamBuffer_;
+ TPtr8 iFramePtr_;
+ TInt lastError_;
+ pj_uint32_t timeStamp_;
+ CActiveSchedulerWait startAsw_;
+
+ // cache variable
+ // to avoid calculating frame length repeatedly
+ TInt frameLen_;
+
+ // sometimes recorded size != requested framesize, so let's
+ // provide a buffer to make sure the rec callback returning
+ // framesize as requested.
+ TUint8 *frameRecBuf_;
+ TInt frameRecBufLen_;
+
+ CPjAudioInputEngine(struct mda_stream *parent_strm,
+ pjmedia_aud_rec_cb rec_cb,
+ void *user_data);
+ void ConstructL();
+ TPtr8 & GetFrame();
+
+public:
+ virtual void MaiscOpenComplete(TInt aError);
+ virtual void MaiscBufferCopied(TInt aError, const TDesC8 &aBuffer);
+ virtual void MaiscRecordComplete(TInt aError);
+
+};
+
+
+CPjAudioInputEngine::CPjAudioInputEngine(struct mda_stream *parent_strm,
+ pjmedia_aud_rec_cb rec_cb,
+ void *user_data)
+ : state_(STATE_INACTIVE), parentStrm_(parent_strm),
+ recCb_(rec_cb), userData_(user_data),
+ iInputStream_(NULL), iStreamBuffer_(NULL), iFramePtr_(0, 0),
+ lastError_(KErrNone), timeStamp_(0),
+ frameLen_(parent_strm->param.samples_per_frame *
+ BYTES_PER_SAMPLE),
+ frameRecBuf_(NULL), frameRecBufLen_(0)
+{
+}
+
+CPjAudioInputEngine::~CPjAudioInputEngine()
+{
+ Stop();
+
+ delete iStreamBuffer_;
+ iStreamBuffer_ = NULL;
+
+ delete [] frameRecBuf_;
+ frameRecBuf_ = NULL;
+ frameRecBufLen_ = 0;
+}
+
+void CPjAudioInputEngine::ConstructL()
+{
+ iStreamBuffer_ = HBufC8::NewL(frameLen_);
+ CleanupStack::PushL(iStreamBuffer_);
+
+ frameRecBuf_ = new TUint8[frameLen_*2];
+ CleanupStack::PushL(frameRecBuf_);
+}
+
+CPjAudioInputEngine *CPjAudioInputEngine::NewLC(struct mda_stream *parent,
+ pjmedia_aud_rec_cb rec_cb,
+ void *user_data)
+{
+ CPjAudioInputEngine* self = new (ELeave) CPjAudioInputEngine(parent,
+ rec_cb,
+ user_data);
+ CleanupStack::PushL(self);
+ self->ConstructL();
+ return self;
+}
+
+CPjAudioInputEngine *CPjAudioInputEngine::NewL(struct mda_stream *parent,
+ pjmedia_aud_rec_cb rec_cb,
+ void *user_data)
+{
+ CPjAudioInputEngine *self = NewLC(parent, rec_cb, user_data);
+ CleanupStack::Pop(self->frameRecBuf_);
+ CleanupStack::Pop(self->iStreamBuffer_);
+ CleanupStack::Pop(self);
+ return self;
+}
+
+
+pj_status_t CPjAudioInputEngine::StartRecord()
+{
+
+ // Ignore command if recording is in progress.
+ if (state_ == STATE_ACTIVE)
+ return PJ_SUCCESS;
+
+ // According to Nokia's AudioStream example, some 2nd Edition, FP2 devices
+ // (such as Nokia 6630) require the stream to be reconstructed each time
+ // before calling Open() - otherwise the callback never gets called.
+ // For uniform behavior, lets just delete/re-create the stream for all
+ // devices.
+
+ // Destroy existing stream.
+ if (iInputStream_) delete iInputStream_;
+ iInputStream_ = NULL;
+
+ // Create the stream.
+ TRAPD(err, iInputStream_ = CMdaAudioInputStream::NewL(*this));
+ if (err != KErrNone)
+ return PJ_RETURN_OS_ERROR(err);
+
+ // Initialize settings.
+ TMdaAudioDataSettings iStreamSettings;
+ iStreamSettings.iChannels =
+ get_channel_cap(parentStrm_->param.channel_count);
+ iStreamSettings.iSampleRate =
+ get_clock_rate_cap(parentStrm_->param.clock_rate);
+
+ pj_assert(iStreamSettings.iChannels != 0 &&
+ iStreamSettings.iSampleRate != 0);
+
+ PJ_LOG(4,(THIS_FILE, "Opening sound device for capture, "
+ "clock rate=%d, channel count=%d..",
+ parentStrm_->param.clock_rate,
+ parentStrm_->param.channel_count));
+
+ // Open stream.
+ lastError_ = KRequestPending;
+ iInputStream_->Open(&iStreamSettings);
+
+#if defined(PJMEDIA_AUDIO_DEV_MDA_USE_SYNC_START) && \
+ PJMEDIA_AUDIO_DEV_MDA_USE_SYNC_START != 0
+
+ startAsw_.Start();
+
+#endif
+
+ // Success
+ PJ_LOG(4,(THIS_FILE, "Sound capture started."));
+ return PJ_SUCCESS;
+}
+
+
+void CPjAudioInputEngine::Stop()
+{
+ // If capture is in progress, stop it.
+ if (iInputStream_ && state_ == STATE_ACTIVE) {
+ lastError_ = KRequestPending;
+ iInputStream_->Stop();
+
+ // Wait until it's actually stopped
+ while (lastError_ == KRequestPending)
+ pj_symbianos_poll(-1, 100);
+ }
+
+ if (iInputStream_) {
+ delete iInputStream_;
+ iInputStream_ = NULL;
+ }
+
+ if (startAsw_.IsStarted()) {
+ startAsw_.AsyncStop();
+ }
+
+ state_ = STATE_INACTIVE;
+}
+
+
+TPtr8 & CPjAudioInputEngine::GetFrame()
+{
+ //iStreamBuffer_->Des().FillZ(frameLen_);
+ iFramePtr_.Set((TUint8*)(iStreamBuffer_->Ptr()), frameLen_, frameLen_);
+ return iFramePtr_;
+}
+
+void CPjAudioInputEngine::MaiscOpenComplete(TInt aError)
+{
+ if (startAsw_.IsStarted()) {
+ startAsw_.AsyncStop();
+ }
+
+ lastError_ = aError;
+ if (aError != KErrNone) {
+ snd_perror("Error in MaiscOpenComplete()", aError);
+ return;
+ }
+
+ /* Apply input volume setting if specified */
+ if (parentStrm_->param.flags &
+ PJMEDIA_AUD_DEV_CAP_INPUT_VOLUME_SETTING)
+ {
+ stream_set_cap(&parentStrm_->base,
+ PJMEDIA_AUD_DEV_CAP_INPUT_VOLUME_SETTING,
+ &parentStrm_->param.input_vol);
+ }
+
+ // set stream priority to normal and time sensitive
+ iInputStream_->SetPriority(EPriorityNormal,
+ EMdaPriorityPreferenceTime);
+
+ // Read the first frame.
+ TPtr8 & frm = GetFrame();
+ TRAPD(err2, iInputStream_->ReadL(frm));
+ if (err2) {
+ PJ_LOG(4,(THIS_FILE, "Exception in iInputStream_->ReadL()"));
+ lastError_ = err2;
+ return;
+ }
+
+ // input stream opened succesfully, set status to Active
+ state_ = STATE_ACTIVE;
+}
+
+void CPjAudioInputEngine::MaiscBufferCopied(TInt aError,
+ const TDesC8 &aBuffer)
+{
+ lastError_ = aError;
+ if (aError != KErrNone) {
+ snd_perror("Error in MaiscBufferCopied()", aError);
+ return;
+ }
+
+ if (frameRecBufLen_ || aBuffer.Length() < frameLen_) {
+ pj_memcpy(frameRecBuf_ + frameRecBufLen_, (void*) aBuffer.Ptr(), aBuffer.Length());
+ frameRecBufLen_ += aBuffer.Length();
+ }
+
+ if (frameRecBufLen_) {
+ while (frameRecBufLen_ >= frameLen_) {
+ pjmedia_frame f;
+
+ f.type = PJMEDIA_FRAME_TYPE_AUDIO;
+ f.buf = frameRecBuf_;
+ f.size = frameLen_;
+ f.timestamp.u32.lo = timeStamp_;
+ f.bit_info = 0;
+
+ // Call the callback.
+ recCb_(userData_, &f);
+ // Increment timestamp.
+ timeStamp_ += parentStrm_->param.samples_per_frame;
+
+ frameRecBufLen_ -= frameLen_;
+ pj_memmove(frameRecBuf_, frameRecBuf_+frameLen_, frameRecBufLen_);
+ }
+ } else {
+ pjmedia_frame f;
+
+ f.type = PJMEDIA_FRAME_TYPE_AUDIO;
+ f.buf = (void*)aBuffer.Ptr();
+ f.size = aBuffer.Length();
+ f.timestamp.u32.lo = timeStamp_;
+ f.bit_info = 0;
+
+ // Call the callback.
+ recCb_(userData_, &f);
+
+ // Increment timestamp.
+ timeStamp_ += parentStrm_->param.samples_per_frame;
+ }
+
+ // Record next frame
+ TPtr8 & frm = GetFrame();
+ TRAPD(err2, iInputStream_->ReadL(frm));
+ if (err2) {
+ PJ_LOG(4,(THIS_FILE, "Exception in iInputStream_->ReadL()"));
+ }
+}
+
+
+void CPjAudioInputEngine::MaiscRecordComplete(TInt aError)
+{
+ lastError_ = aError;
+ state_ = STATE_INACTIVE;
+ if (aError != KErrNone && aError != KErrCancel) {
+ snd_perror("Error in MaiscRecordComplete()", aError);
+ }
+}
+
+
+
+//////////////////////////////////////////////////////////////////////////////
+//
+
+/*
+ * Implementation: Symbian Output Stream.
+ */
+
+class CPjAudioOutputEngine : public CBase, MMdaAudioOutputStreamCallback
+{
+public:
+ enum State
+ {
+ STATE_INACTIVE,
+ STATE_ACTIVE,
+ };
+
+ ~CPjAudioOutputEngine();
+
+ static CPjAudioOutputEngine *NewL(struct mda_stream *parent_strm,
+ pjmedia_aud_play_cb play_cb,
+ void *user_data);
+
+ static CPjAudioOutputEngine *NewLC(struct mda_stream *parent_strm,
+ pjmedia_aud_play_cb rec_cb,
+ void *user_data);
+
+ pj_status_t StartPlay();
+ void Stop();
+
+ pj_status_t SetVolume(TInt vol) {
+ if (iOutputStream_) {
+ iOutputStream_->SetVolume(vol);
+ return PJ_SUCCESS;
+ } else
+ return PJ_EINVALIDOP;
+ }
+
+ TInt GetVolume() {
+ if (iOutputStream_) {
+ return iOutputStream_->Volume();
+ } else
+ return PJ_EINVALIDOP;
+ }
+
+ TInt GetMaxVolume() {
+ if (iOutputStream_) {
+ return iOutputStream_->MaxVolume();
+ } else
+ return PJ_EINVALIDOP;
+ }
+
+private:
+ State state_;
+ struct mda_stream *parentStrm_;
+ pjmedia_aud_play_cb playCb_;
+ void *userData_;
+ CMdaAudioOutputStream *iOutputStream_;
+ TUint8 *frameBuf_;
+ unsigned frameBufSize_;
+ TPtrC8 frame_;
+ TInt lastError_;
+ unsigned timestamp_;
+ CActiveSchedulerWait startAsw_;
+
+ CPjAudioOutputEngine(struct mda_stream *parent_strm,
+ pjmedia_aud_play_cb play_cb,
+ void *user_data);
+ void ConstructL();
+
+ virtual void MaoscOpenComplete(TInt aError);
+ virtual void MaoscBufferCopied(TInt aError, const TDesC8& aBuffer);
+ virtual void MaoscPlayComplete(TInt aError);
+};
+
+
+CPjAudioOutputEngine::CPjAudioOutputEngine(struct mda_stream *parent_strm,
+ pjmedia_aud_play_cb play_cb,
+ void *user_data)
+: state_(STATE_INACTIVE), parentStrm_(parent_strm), playCb_(play_cb),
+ userData_(user_data), iOutputStream_(NULL), frameBuf_(NULL),
+ lastError_(KErrNone), timestamp_(0)
+{
+}
+
+
+void CPjAudioOutputEngine::ConstructL()
+{
+ frameBufSize_ = parentStrm_->param.samples_per_frame *
+ BYTES_PER_SAMPLE;
+ frameBuf_ = new TUint8[frameBufSize_];
+}
+
+CPjAudioOutputEngine::~CPjAudioOutputEngine()
+{
+ Stop();
+ delete [] frameBuf_;
+}
+
+CPjAudioOutputEngine *
+CPjAudioOutputEngine::NewLC(struct mda_stream *parent_strm,
+ pjmedia_aud_play_cb play_cb,
+ void *user_data)
+{
+ CPjAudioOutputEngine* self = new (ELeave) CPjAudioOutputEngine(parent_strm,
+ play_cb,
+ user_data);
+ CleanupStack::PushL(self);
+ self->ConstructL();
+ return self;
+}
+
+CPjAudioOutputEngine *
+CPjAudioOutputEngine::NewL(struct mda_stream *parent_strm,
+ pjmedia_aud_play_cb play_cb,
+ void *user_data)
+{
+ CPjAudioOutputEngine *self = NewLC(parent_strm, play_cb, user_data);
+ CleanupStack::Pop(self);
+ return self;
+}
+
+pj_status_t CPjAudioOutputEngine::StartPlay()
+{
+ // Ignore command if playing is in progress.
+ if (state_ == STATE_ACTIVE)
+ return PJ_SUCCESS;
+
+ // Destroy existing stream.
+ if (iOutputStream_) delete iOutputStream_;
+ iOutputStream_ = NULL;
+
+ // Create the stream
+ TRAPD(err, iOutputStream_ = CMdaAudioOutputStream::NewL(*this));
+ if (err != KErrNone)
+ return PJ_RETURN_OS_ERROR(err);
+
+ // Initialize settings.
+ TMdaAudioDataSettings iStreamSettings;
+ iStreamSettings.iChannels =
+ get_channel_cap(parentStrm_->param.channel_count);
+ iStreamSettings.iSampleRate =
+ get_clock_rate_cap(parentStrm_->param.clock_rate);
+
+ pj_assert(iStreamSettings.iChannels != 0 &&
+ iStreamSettings.iSampleRate != 0);
+
+ PJ_LOG(4,(THIS_FILE, "Opening sound device for playback, "
+ "clock rate=%d, channel count=%d..",
+ parentStrm_->param.clock_rate,
+ parentStrm_->param.channel_count));
+
+ // Open stream.
+ lastError_ = KRequestPending;
+ iOutputStream_->Open(&iStreamSettings);
+
+#if defined(PJMEDIA_AUDIO_DEV_MDA_USE_SYNC_START) && \
+ PJMEDIA_AUDIO_DEV_MDA_USE_SYNC_START != 0
+
+ startAsw_.Start();
+
+#endif
+
+ // Success
+ PJ_LOG(4,(THIS_FILE, "Sound playback started"));
+ return PJ_SUCCESS;
+
+}
+
+void CPjAudioOutputEngine::Stop()
+{
+ // Stop stream if it's playing
+ if (iOutputStream_ && state_ != STATE_INACTIVE) {
+ lastError_ = KRequestPending;
+ iOutputStream_->Stop();
+
+ // Wait until it's actually stopped
+ while (lastError_ == KRequestPending)
+ pj_symbianos_poll(-1, 100);
+ }
+
+ if (iOutputStream_) {
+ delete iOutputStream_;
+ iOutputStream_ = NULL;
+ }
+
+ if (startAsw_.IsStarted()) {
+ startAsw_.AsyncStop();
+ }
+
+ state_ = STATE_INACTIVE;
+}
+
+void CPjAudioOutputEngine::MaoscOpenComplete(TInt aError)
+{
+ if (startAsw_.IsStarted()) {
+ startAsw_.AsyncStop();
+ }
+
+ lastError_ = aError;
+
+ if (aError==KErrNone) {
+ // set stream properties, 16bit 8KHz mono
+ TMdaAudioDataSettings iSettings;
+ iSettings.iChannels =
+ get_channel_cap(parentStrm_->param.channel_count);
+ iSettings.iSampleRate =
+ get_clock_rate_cap(parentStrm_->param.clock_rate);
+
+ iOutputStream_->SetAudioPropertiesL(iSettings.iSampleRate,
+ iSettings.iChannels);
+
+ /* Apply output volume setting if specified */
+ if (parentStrm_->param.flags &
+ PJMEDIA_AUD_DEV_CAP_OUTPUT_VOLUME_SETTING)
+ {
+ stream_set_cap(&parentStrm_->base,
+ PJMEDIA_AUD_DEV_CAP_OUTPUT_VOLUME_SETTING,
+ &parentStrm_->param.output_vol);
+ } else {
+ // set volume to 1/2th of stream max volume
+ iOutputStream_->SetVolume(iOutputStream_->MaxVolume()/2);
+ }
+
+ // set stream priority to normal and time sensitive
+ iOutputStream_->SetPriority(EPriorityNormal,
+ EMdaPriorityPreferenceTime);
+
+ // Call callback to retrieve frame from upstream.
+ pjmedia_frame f;
+ pj_status_t status;
+
+ f.type = PJMEDIA_FRAME_TYPE_AUDIO;
+ f.buf = frameBuf_;
+ f.size = frameBufSize_;
+ f.timestamp.u32.lo = timestamp_;
+ f.bit_info = 0;
+
+ status = playCb_(this->userData_, &f);
+ if (status != PJ_SUCCESS) {
+ this->Stop();
+ return;
+ }
+
+ if (f.type != PJMEDIA_FRAME_TYPE_AUDIO)
+ pj_bzero(frameBuf_, frameBufSize_);
+
+ // Increment timestamp.
+ timestamp_ += (frameBufSize_ / BYTES_PER_SAMPLE);
+
+ // issue WriteL() to write the first audio data block,
+ // subsequent calls to WriteL() will be issued in
+ // MMdaAudioOutputStreamCallback::MaoscBufferCopied()
+ // until whole data buffer is written.
+ frame_.Set(frameBuf_, frameBufSize_);
+ iOutputStream_->WriteL(frame_);
+
+ // output stream opened succesfully, set status to Active
+ state_ = STATE_ACTIVE;
+ } else {
+ snd_perror("Error in MaoscOpenComplete()", aError);
+ }
+}
+
+void CPjAudioOutputEngine::MaoscBufferCopied(TInt aError,
+ const TDesC8& aBuffer)
+{
+ PJ_UNUSED_ARG(aBuffer);
+
+ if (aError==KErrNone) {
+ // Buffer successfully written, feed another one.
+
+ // Call callback to retrieve frame from upstream.
+ pjmedia_frame f;
+ pj_status_t status;
+
+ f.type = PJMEDIA_FRAME_TYPE_AUDIO;
+ f.buf = frameBuf_;
+ f.size = frameBufSize_;
+ f.timestamp.u32.lo = timestamp_;
+ f.bit_info = 0;
+
+ status = playCb_(this->userData_, &f);
+ if (status != PJ_SUCCESS) {
+ this->Stop();
+ return;
+ }
+
+ if (f.type != PJMEDIA_FRAME_TYPE_AUDIO)
+ pj_bzero(frameBuf_, frameBufSize_);
+
+ // Increment timestamp.
+ timestamp_ += (frameBufSize_ / BYTES_PER_SAMPLE);
+
+ // Write to playback stream.
+ frame_.Set(frameBuf_, frameBufSize_);
+ iOutputStream_->WriteL(frame_);
+
+ } else if (aError==KErrAbort) {
+ // playing was aborted, due to call to CMdaAudioOutputStream::Stop()
+ state_ = STATE_INACTIVE;
+ } else {
+ // error writing data to output
+ lastError_ = aError;
+ state_ = STATE_INACTIVE;
+ snd_perror("Error in MaoscBufferCopied()", aError);
+ }
+}
+
+void CPjAudioOutputEngine::MaoscPlayComplete(TInt aError)
+{
+ lastError_ = aError;
+ state_ = STATE_INACTIVE;
+ if (aError != KErrNone && aError != KErrCancel) {
+ snd_perror("Error in MaoscPlayComplete()", aError);
+ }
+}
+
+/****************************************************************************
+ * Factory operations
+ */
+
+/*
+ * C compatible declaration of MDA factory.
+ */
+PJ_BEGIN_DECL
+PJ_DECL(pjmedia_aud_dev_factory*) pjmedia_symb_mda_factory(pj_pool_factory *pf);
+PJ_END_DECL
+
+/*
+ * Init Symbian audio driver.
+ */
+pjmedia_aud_dev_factory* pjmedia_symb_mda_factory(pj_pool_factory *pf)
+{
+ struct mda_factory *f;
+ pj_pool_t *pool;
+
+ pool = pj_pool_create(pf, "symb_aud", 1000, 1000, NULL);
+ f = PJ_POOL_ZALLOC_T(pool, struct mda_factory);
+ f->pf = pf;
+ f->pool = pool;
+ f->base.op = &factory_op;
+
+ return &f->base;
+}
+
+/* API: init factory */
+static pj_status_t factory_init(pjmedia_aud_dev_factory *f)
+{
+ struct mda_factory *af = (struct mda_factory*)f;
+
+ pj_ansi_strcpy(af->dev_info.name, "Symbian Audio");
+ af->dev_info.default_samples_per_sec = 8000;
+ af->dev_info.caps = PJMEDIA_AUD_DEV_CAP_INPUT_VOLUME_SETTING |
+ PJMEDIA_AUD_DEV_CAP_OUTPUT_VOLUME_SETTING;
+ af->dev_info.input_count = 1;
+ af->dev_info.output_count = 1;
+
+ PJ_LOG(4, (THIS_FILE, "Symb Mda initialized"));
+
+ return PJ_SUCCESS;
+}
+
+/* API: destroy factory */
+static pj_status_t factory_destroy(pjmedia_aud_dev_factory *f)
+{
+ struct mda_factory *af = (struct mda_factory*)f;
+ pj_pool_t *pool = af->pool;
+
+ af->pool = NULL;
+ pj_pool_release(pool);
+
+ PJ_LOG(4, (THIS_FILE, "Symbian Mda destroyed"));
+
+ return PJ_SUCCESS;
+}
+
+/* API: refresh the device list */
+static pj_status_t factory_refresh(pjmedia_aud_dev_factory *f)
+{
+ PJ_UNUSED_ARG(f);
+ return PJ_ENOTSUP;
+}
+
+/* API: get number of devices */
+static unsigned factory_get_dev_count(pjmedia_aud_dev_factory *f)
+{
+ PJ_UNUSED_ARG(f);
+ return 1;
+}
+
+/* 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 mda_factory *af = (struct mda_factory*)f;
+
+ PJ_ASSERT_RETURN(index == 0, PJMEDIA_EAUD_INVDEV);
+
+ pj_memcpy(info, &af->dev_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 mda_factory *af = (struct mda_factory*)f;
+
+ PJ_ASSERT_RETURN(index == 0, PJMEDIA_EAUD_INVDEV);
+
+ pj_bzero(param, sizeof(*param));
+ param->dir = PJMEDIA_DIR_CAPTURE_PLAYBACK;
+ param->rec_id = index;
+ param->play_id = index;
+ param->clock_rate = af->dev_info.default_samples_per_sec;
+ param->channel_count = 1;
+ param->samples_per_frame = af->dev_info.default_samples_per_sec * 20 / 1000;
+ param->bits_per_sample = BITS_PER_SAMPLE;
+ // Don't set the flags without specifying the flags value.
+ //param->flags = af->dev_info.caps;
+
+ return PJ_SUCCESS;
+}
+
+
+/* API: create stream */
+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)
+{
+ struct mda_factory *mf = (struct mda_factory*)f;
+ pj_pool_t *pool;
+ struct mda_stream *strm;
+
+ /* Can only support 16bits per sample raw PCM format. */
+ PJ_ASSERT_RETURN(param->bits_per_sample == BITS_PER_SAMPLE, PJ_EINVAL);
+ PJ_ASSERT_RETURN((param->flags & PJMEDIA_AUD_DEV_CAP_EXT_FORMAT)==0 ||
+ param->ext_fmt.id == PJMEDIA_FORMAT_L16,
+ PJ_ENOTSUP);
+
+ /* It seems that MDA recorder only supports for mono channel. */
+ PJ_ASSERT_RETURN(param->channel_count == 1, PJ_EINVAL);
+
+ /* Create and Initialize stream descriptor */
+ pool = pj_pool_create(mf->pf, "symb_aud_dev", 1000, 1000, NULL);
+ PJ_ASSERT_RETURN(pool, PJ_ENOMEM);
+
+ strm = PJ_POOL_ZALLOC_T(pool, struct mda_stream);
+ strm->pool = pool;
+ strm->param = *param;
+
+ // Create the output stream.
+ if (strm->param.dir & PJMEDIA_DIR_PLAYBACK) {
+ TRAPD(err, strm->out_engine = CPjAudioOutputEngine::NewL(strm, play_cb,
+ user_data));
+ if (err != KErrNone) {
+ pj_pool_release(pool);
+ return PJ_RETURN_OS_ERROR(err);
+ }
+ }
+
+ // Create the input stream.
+ if (strm->param.dir & PJMEDIA_DIR_CAPTURE) {
+ TRAPD(err, strm->in_engine = CPjAudioInputEngine::NewL(strm, rec_cb,
+ user_data));
+ if (err != KErrNone) {
+ strm->in_engine = NULL;
+ delete strm->out_engine;
+ strm->out_engine = NULL;
+ pj_pool_release(pool);
+ return PJ_RETURN_OS_ERROR(err);
+ }
+ }
+
+ /* Done */
+ strm->base.op = &stream_op;
+ *p_aud_strm = &strm->base;
+
+ return PJ_SUCCESS;
+}
+
+/* API: Get stream info. */
+static pj_status_t stream_get_param(pjmedia_aud_stream *s,
+ pjmedia_aud_param *pi)
+{
+ struct mda_stream *strm = (struct mda_stream*)s;
+
+ PJ_ASSERT_RETURN(strm && pi, PJ_EINVAL);
+
+ pj_memcpy(pi, &strm->param, sizeof(*pi));
+
+ /* Update 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;
+ }
+
+ /* Update 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;
+ }
+
+ 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 mda_stream *strm = (struct mda_stream*)s;
+ pj_status_t status = PJ_ENOTSUP;
+
+ PJ_ASSERT_RETURN(s && pval, PJ_EINVAL);
+
+ switch (cap) {
+ case PJMEDIA_AUD_DEV_CAP_INPUT_VOLUME_SETTING:
+ if (strm->param.dir & PJMEDIA_DIR_CAPTURE) {
+ PJ_ASSERT_RETURN(strm->in_engine, PJ_EINVAL);
+
+ TInt max_gain = strm->in_engine->GetMaxGain();
+ TInt gain = strm->in_engine->GetGain();
+
+ if (max_gain > 0 && gain >= 0) {
+ *(unsigned*)pval = gain * 100 / max_gain;
+ status = PJ_SUCCESS;
+ } else {
+ status = PJMEDIA_EAUD_NOTREADY;
+ }
+ }
+ break;
+ case PJMEDIA_AUD_DEV_CAP_OUTPUT_VOLUME_SETTING:
+ if (strm->param.dir & PJMEDIA_DIR_PLAYBACK) {
+ PJ_ASSERT_RETURN(strm->out_engine, PJ_EINVAL);
+
+ TInt max_vol = strm->out_engine->GetMaxVolume();
+ TInt vol = strm->out_engine->GetVolume();
+
+ if (max_vol > 0 && vol >= 0) {
+ *(unsigned*)pval = vol * 100 / max_vol;
+ status = PJ_SUCCESS;
+ } else {
+ status = PJMEDIA_EAUD_NOTREADY;
+ }
+ }
+ break;
+ default:
+ break;
+ }
+
+ return status;
+}
+
+/* API: set capability */
+static pj_status_t stream_set_cap(pjmedia_aud_stream *s,
+ pjmedia_aud_dev_cap cap,
+ const void *pval)
+{
+ struct mda_stream *strm = (struct mda_stream*)s;
+ pj_status_t status = PJ_ENOTSUP;
+
+ PJ_ASSERT_RETURN(s && pval, PJ_EINVAL);
+
+ switch (cap) {
+ case PJMEDIA_AUD_DEV_CAP_INPUT_VOLUME_SETTING:
+ if (strm->param.dir & PJMEDIA_DIR_CAPTURE) {
+ PJ_ASSERT_RETURN(strm->in_engine, PJ_EINVAL);
+
+ TInt max_gain = strm->in_engine->GetMaxGain();
+ if (max_gain > 0) {
+ TInt gain;
+
+ gain = *(unsigned*)pval * max_gain / 100;
+ status = strm->in_engine->SetGain(gain);
+ } else {
+ status = PJMEDIA_EAUD_NOTREADY;
+ }
+ }
+ break;
+ case PJMEDIA_AUD_DEV_CAP_OUTPUT_VOLUME_SETTING:
+ if (strm->param.dir & PJMEDIA_DIR_PLAYBACK) {
+ PJ_ASSERT_RETURN(strm->out_engine, PJ_EINVAL);
+
+ TInt max_vol = strm->out_engine->GetMaxVolume();
+ if (max_vol > 0) {
+ TInt vol;
+
+ vol = *(unsigned*)pval * max_vol / 100;
+ status = strm->out_engine->SetVolume(vol);
+ } else {
+ status = PJMEDIA_EAUD_NOTREADY;
+ }
+ }
+ break;
+ default:
+ break;
+ }
+
+ return status;
+}
+
+/* API: Start stream. */
+static pj_status_t stream_start(pjmedia_aud_stream *strm)
+{
+ struct mda_stream *stream = (struct mda_stream*)strm;
+
+ PJ_ASSERT_RETURN(stream, PJ_EINVAL);
+
+ if (stream->out_engine) {
+ pj_status_t status;
+ status = stream->out_engine->StartPlay();
+ if (status != PJ_SUCCESS)
+ return status;
+ }
+
+ if (stream->in_engine) {
+ pj_status_t status;
+ status = stream->in_engine->StartRecord();
+ if (status != PJ_SUCCESS)
+ return status;
+ }
+
+ return PJ_SUCCESS;
+}
+
+/* API: Stop stream. */
+static pj_status_t stream_stop(pjmedia_aud_stream *strm)
+{
+ struct mda_stream *stream = (struct mda_stream*)strm;
+
+ PJ_ASSERT_RETURN(stream, PJ_EINVAL);
+
+ if (stream->in_engine) {
+ stream->in_engine->Stop();
+ }
+
+ if (stream->out_engine) {
+ stream->out_engine->Stop();
+ }
+
+ return PJ_SUCCESS;
+}
+
+
+/* API: Destroy stream. */
+static pj_status_t stream_destroy(pjmedia_aud_stream *strm)
+{
+ struct mda_stream *stream = (struct mda_stream*)strm;
+
+ PJ_ASSERT_RETURN(stream, PJ_EINVAL);
+
+ stream_stop(strm);
+
+ delete stream->in_engine;
+ stream->in_engine = NULL;
+
+ delete stream->out_engine;
+ stream->out_engine = NULL;
+
+ pj_pool_t *pool;
+ pool = stream->pool;
+ if (pool) {
+ stream->pool = NULL;
+ pj_pool_release(pool);
+ }
+
+ return PJ_SUCCESS;
+}
+
+#endif /* PJMEDIA_AUDIO_DEV_HAS_SYMB_MDA */
diff --git a/jni/pjproject-android/.svn/pristine/49/499d6eeec0a295cb60c54eea1df8be9f397918d8.svn-base b/jni/pjproject-android/.svn/pristine/49/499d6eeec0a295cb60c54eea1df8be9f397918d8.svn-base
new file mode 100644
index 0000000..ca907ba
--- /dev/null
+++ b/jni/pjproject-android/.svn/pristine/49/499d6eeec0a295cb60c54eea1df8be9f397918d8.svn-base
@@ -0,0 +1,1447 @@
+/* $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-ua/sip_regc.h>
+#include <pjsip/sip_endpoint.h>
+#include <pjsip/sip_parser.h>
+#include <pjsip/sip_module.h>
+#include <pjsip/sip_transaction.h>
+#include <pjsip/sip_event.h>
+#include <pjsip/sip_util.h>
+#include <pjsip/sip_auth_msg.h>
+#include <pjsip/sip_errno.h>
+#include <pj/assert.h>
+#include <pj/guid.h>
+#include <pj/lock.h>
+#include <pj/os.h>
+#include <pj/pool.h>
+#include <pj/log.h>
+#include <pj/rand.h>
+#include <pj/string.h>
+
+
+#define REFRESH_TIMER 1
+#define DELAY_BEFORE_REFRESH PJSIP_REGISTER_CLIENT_DELAY_BEFORE_REFRESH
+#define THIS_FILE "sip_reg.c"
+
+/* Outgoing transaction timeout when server sends 100 but never replies
+ * with final response. Value is in MILISECONDS!
+ */
+#define REGC_TSX_TIMEOUT 33000
+
+enum { NOEXP = 0x1FFFFFFF };
+
+static const pj_str_t XUID_PARAM_NAME = { "x-uid", 5 };
+
+
+/* Current/pending operation */
+enum regc_op
+{
+ REGC_IDLE,
+ REGC_REGISTERING,
+ REGC_UNREGISTERING
+};
+
+/**
+ * SIP client registration structure.
+ */
+struct pjsip_regc
+{
+ pj_pool_t *pool;
+ pjsip_endpoint *endpt;
+ pj_lock_t *lock;
+ pj_bool_t _delete_flag;
+ pj_bool_t has_tsx;
+ pj_atomic_t *busy_ctr;
+ enum regc_op current_op;
+
+ pj_bool_t add_xuid_param;
+
+ void *token;
+ pjsip_regc_cb *cb;
+ pjsip_regc_tsx_cb *tsx_cb;
+
+ pj_str_t str_srv_url;
+ pjsip_uri *srv_url;
+ pjsip_cid_hdr *cid_hdr;
+ pjsip_cseq_hdr *cseq_hdr;
+ pj_str_t from_uri;
+ pjsip_from_hdr *from_hdr;
+ pjsip_to_hdr *to_hdr;
+ pjsip_contact_hdr contact_hdr_list;
+ pjsip_contact_hdr removed_contact_hdr_list;
+ pjsip_expires_hdr *expires_hdr;
+ pj_uint32_t expires;
+ pj_uint32_t delay_before_refresh;
+ pjsip_route_hdr route_set;
+ pjsip_hdr hdr_list;
+ pjsip_host_port via_addr;
+ const void *via_tp;
+
+ /* Authorization sessions. */
+ pjsip_auth_clt_sess auth_sess;
+
+ /* Auto refresh registration. */
+ pj_bool_t auto_reg;
+ pj_time_val last_reg;
+ pj_time_val next_reg;
+ pj_timer_entry timer;
+
+ /* Transport selector */
+ pjsip_tpselector tp_sel;
+
+ /* Last transport used. We acquire the transport to keep
+ * it open.
+ */
+ pjsip_transport *last_transport;
+};
+
+
+PJ_DEF(pj_status_t) pjsip_regc_create( pjsip_endpoint *endpt, void *token,
+ pjsip_regc_cb *cb,
+ pjsip_regc **p_regc)
+{
+ pj_pool_t *pool;
+ pjsip_regc *regc;
+ pj_status_t status;
+
+ /* Verify arguments. */
+ PJ_ASSERT_RETURN(endpt && cb && p_regc, PJ_EINVAL);
+
+ pool = pjsip_endpt_create_pool(endpt, "regc%p", 1024, 1024);
+ PJ_ASSERT_RETURN(pool != NULL, PJ_ENOMEM);
+
+ regc = PJ_POOL_ZALLOC_T(pool, pjsip_regc);
+
+ regc->pool = pool;
+ regc->endpt = endpt;
+ regc->token = token;
+ regc->cb = cb;
+ regc->expires = PJSIP_REGC_EXPIRATION_NOT_SPECIFIED;
+ regc->add_xuid_param = pjsip_cfg()->regc.add_xuid_param;
+
+ status = pj_lock_create_recursive_mutex(pool, pool->obj_name,
+ ®c->lock);
+ if (status != PJ_SUCCESS) {
+ pj_pool_release(pool);
+ return status;
+ }
+
+ status = pj_atomic_create(pool, 0, ®c->busy_ctr);
+ if (status != PJ_SUCCESS) {
+ pj_lock_destroy(regc->lock);
+ pj_pool_release(pool);
+ return status;
+ }
+
+ status = pjsip_auth_clt_init(®c->auth_sess, endpt, regc->pool, 0);
+ if (status != PJ_SUCCESS)
+ return status;
+
+ pj_list_init(®c->route_set);
+ pj_list_init(®c->hdr_list);
+ pj_list_init(®c->contact_hdr_list);
+ pj_list_init(®c->removed_contact_hdr_list);
+
+ /* Done */
+ *p_regc = regc;
+ return PJ_SUCCESS;
+}
+
+
+PJ_DEF(pj_status_t) pjsip_regc_destroy(pjsip_regc *regc)
+{
+ PJ_ASSERT_RETURN(regc, PJ_EINVAL);
+
+ pj_lock_acquire(regc->lock);
+ if (regc->has_tsx || pj_atomic_get(regc->busy_ctr) != 0) {
+ regc->_delete_flag = 1;
+ regc->cb = NULL;
+ pj_lock_release(regc->lock);
+ } else {
+ pjsip_tpselector_dec_ref(®c->tp_sel);
+ if (regc->last_transport) {
+ pjsip_transport_dec_ref(regc->last_transport);
+ regc->last_transport = NULL;
+ }
+ if (regc->timer.id != 0) {
+ pjsip_endpt_cancel_timer(regc->endpt, ®c->timer);
+ regc->timer.id = 0;
+ }
+ pj_atomic_destroy(regc->busy_ctr);
+ pj_lock_release(regc->lock);
+ pj_lock_destroy(regc->lock);
+ regc->lock = NULL;
+ pjsip_endpt_release_pool(regc->endpt, regc->pool);
+ }
+
+ return PJ_SUCCESS;
+}
+
+
+PJ_DEF(pj_status_t) pjsip_regc_get_info( pjsip_regc *regc,
+ pjsip_regc_info *info )
+{
+ PJ_ASSERT_RETURN(regc && info, PJ_EINVAL);
+
+ pj_lock_acquire(regc->lock);
+
+ info->server_uri = regc->str_srv_url;
+ info->client_uri = regc->from_uri;
+ info->is_busy = (pj_atomic_get(regc->busy_ctr) || regc->has_tsx);
+ info->auto_reg = regc->auto_reg;
+ info->interval = regc->expires;
+ info->transport = regc->last_transport;
+
+ if (regc->has_tsx)
+ info->next_reg = 0;
+ else if (regc->auto_reg == 0)
+ info->next_reg = 0;
+ else if (regc->expires < 0)
+ info->next_reg = regc->expires;
+ else {
+ pj_time_val now, next_reg;
+
+ next_reg = regc->next_reg;
+ pj_gettimeofday(&now);
+ PJ_TIME_VAL_SUB(next_reg, now);
+ info->next_reg = next_reg.sec;
+ }
+
+ pj_lock_release(regc->lock);
+
+ return PJ_SUCCESS;
+}
+
+
+PJ_DEF(pj_pool_t*) pjsip_regc_get_pool(pjsip_regc *regc)
+{
+ return regc->pool;
+}
+
+static void set_expires( pjsip_regc *regc, pj_uint32_t expires)
+{
+ if (expires != regc->expires) {
+ regc->expires_hdr = pjsip_expires_hdr_create(regc->pool, expires);
+ } else {
+ regc->expires_hdr = NULL;
+ }
+}
+
+
+static pj_status_t set_contact( pjsip_regc *regc,
+ int contact_cnt,
+ const pj_str_t contact[] )
+{
+ const pj_str_t CONTACT = { "Contact", 7 };
+ pjsip_contact_hdr *h;
+ int i;
+
+ /* Save existing contact list to removed_contact_hdr_list and
+ * clear contact_hdr_list.
+ */
+ pj_list_merge_last(®c->removed_contact_hdr_list,
+ ®c->contact_hdr_list);
+
+ /* Set the expiration of Contacts in to removed_contact_hdr_list
+ * zero.
+ */
+ h = regc->removed_contact_hdr_list.next;
+ while (h != ®c->removed_contact_hdr_list) {
+ h->expires = 0;
+ h = h->next;
+ }
+
+ /* Process new contacts */
+ for (i=0; i<contact_cnt; ++i) {
+ pjsip_contact_hdr *hdr;
+ pj_str_t tmp;
+
+ pj_strdup_with_null(regc->pool, &tmp, &contact[i]);
+ hdr = (pjsip_contact_hdr*)
+ pjsip_parse_hdr(regc->pool, &CONTACT, tmp.ptr, tmp.slen, NULL);
+ if (hdr == NULL) {
+ PJ_LOG(4,(THIS_FILE, "Invalid Contact: \"%.*s\"",
+ (int)tmp.slen, tmp.ptr));
+ return PJSIP_EINVALIDURI;
+ }
+
+ /* Find the new contact in old contact list. If found, remove
+ * the old header from the old header list.
+ */
+ h = regc->removed_contact_hdr_list.next;
+ while (h != ®c->removed_contact_hdr_list) {
+ int rc;
+
+ rc = pjsip_uri_cmp(PJSIP_URI_IN_CONTACT_HDR,
+ h->uri, hdr->uri);
+ if (rc == 0) {
+ /* Match */
+ pj_list_erase(h);
+ break;
+ }
+
+ h = h->next;
+ }
+
+ /* If add_xuid_param option is enabled and Contact URI is sip/sips,
+ * add xuid parameter to assist matching the Contact URI in the
+ * REGISTER response later.
+ */
+ if (regc->add_xuid_param && (PJSIP_URI_SCHEME_IS_SIP(hdr->uri) ||
+ PJSIP_URI_SCHEME_IS_SIPS(hdr->uri)))
+ {
+ pjsip_param *xuid_param;
+ pjsip_sip_uri *sip_uri;
+
+ xuid_param = PJ_POOL_ZALLOC_T(regc->pool, pjsip_param);
+ xuid_param->name = XUID_PARAM_NAME;
+ pj_create_unique_string(regc->pool, &xuid_param->value);
+
+ sip_uri = (pjsip_sip_uri*) pjsip_uri_get_uri(hdr->uri);
+ pj_list_push_back(&sip_uri->other_param, xuid_param);
+ }
+
+ pj_list_push_back(®c->contact_hdr_list, hdr);
+ }
+
+ return PJ_SUCCESS;
+}
+
+
+PJ_DEF(pj_status_t) pjsip_regc_init( pjsip_regc *regc,
+ const pj_str_t *srv_url,
+ const pj_str_t *from_url,
+ const pj_str_t *to_url,
+ int contact_cnt,
+ const pj_str_t contact[],
+ pj_uint32_t expires)
+{
+ pj_str_t tmp;
+ pj_status_t status;
+
+ PJ_ASSERT_RETURN(regc && srv_url && from_url && to_url &&
+ expires, PJ_EINVAL);
+
+ /* Copy server URL. */
+ pj_strdup_with_null(regc->pool, ®c->str_srv_url, srv_url);
+
+ /* Set server URL. */
+ tmp = regc->str_srv_url;
+ regc->srv_url = pjsip_parse_uri( regc->pool, tmp.ptr, tmp.slen, 0);
+ if (regc->srv_url == NULL) {
+ return PJSIP_EINVALIDURI;
+ }
+
+ /* Set "From" header. */
+ pj_strdup_with_null(regc->pool, ®c->from_uri, from_url);
+ tmp = regc->from_uri;
+ regc->from_hdr = pjsip_from_hdr_create(regc->pool);
+ regc->from_hdr->uri = pjsip_parse_uri(regc->pool, tmp.ptr, tmp.slen,
+ PJSIP_PARSE_URI_AS_NAMEADDR);
+ if (!regc->from_hdr->uri) {
+ PJ_LOG(4,(THIS_FILE, "regc: invalid source URI %.*s",
+ from_url->slen, from_url->ptr));
+ return PJSIP_EINVALIDURI;
+ }
+
+ /* Set "To" header. */
+ pj_strdup_with_null(regc->pool, &tmp, to_url);
+ regc->to_hdr = pjsip_to_hdr_create(regc->pool);
+ regc->to_hdr->uri = pjsip_parse_uri(regc->pool, tmp.ptr, tmp.slen,
+ PJSIP_PARSE_URI_AS_NAMEADDR);
+ if (!regc->to_hdr->uri) {
+ PJ_LOG(4,(THIS_FILE, "regc: invalid target URI %.*s", to_url->slen, to_url->ptr));
+ return PJSIP_EINVALIDURI;
+ }
+
+
+ /* Set "Contact" header. */
+ status = set_contact( regc, contact_cnt, contact);
+ if (status != PJ_SUCCESS)
+ return status;
+
+ /* Set "Expires" header, if required. */
+ set_expires( regc, expires);
+ regc->delay_before_refresh = DELAY_BEFORE_REFRESH;
+
+ /* Set "Call-ID" header. */
+ regc->cid_hdr = pjsip_cid_hdr_create(regc->pool);
+ pj_create_unique_string(regc->pool, ®c->cid_hdr->id);
+
+ /* Set "CSeq" header. */
+ regc->cseq_hdr = pjsip_cseq_hdr_create(regc->pool);
+ regc->cseq_hdr->cseq = pj_rand() % 0xFFFF;
+ pjsip_method_set( ®c->cseq_hdr->method, PJSIP_REGISTER_METHOD);
+
+ /* Done. */
+ return PJ_SUCCESS;
+}
+
+PJ_DEF(pj_status_t) pjsip_regc_set_credentials( pjsip_regc *regc,
+ int count,
+ const pjsip_cred_info cred[] )
+{
+ PJ_ASSERT_RETURN(regc && count && cred, PJ_EINVAL);
+ return pjsip_auth_clt_set_credentials(®c->auth_sess, count, cred);
+}
+
+PJ_DEF(pj_status_t) pjsip_regc_set_prefs( pjsip_regc *regc,
+ const pjsip_auth_clt_pref *pref)
+{
+ PJ_ASSERT_RETURN(regc && pref, PJ_EINVAL);
+ return pjsip_auth_clt_set_prefs(®c->auth_sess, pref);
+}
+
+PJ_DEF(pj_status_t) pjsip_regc_set_route_set( pjsip_regc *regc,
+ const pjsip_route_hdr *route_set)
+{
+ const pjsip_route_hdr *chdr;
+
+ PJ_ASSERT_RETURN(regc && route_set, PJ_EINVAL);
+
+ pj_list_init(®c->route_set);
+
+ chdr = route_set->next;
+ while (chdr != route_set) {
+ pj_list_push_back(®c->route_set, pjsip_hdr_clone(regc->pool, chdr));
+ chdr = chdr->next;
+ }
+
+ return PJ_SUCCESS;
+}
+
+
+/*
+ * Bind client registration to a specific transport/listener.
+ */
+PJ_DEF(pj_status_t) pjsip_regc_set_transport( pjsip_regc *regc,
+ const pjsip_tpselector *sel)
+{
+ PJ_ASSERT_RETURN(regc && sel, PJ_EINVAL);
+
+ pjsip_tpselector_dec_ref(®c->tp_sel);
+ pj_memcpy(®c->tp_sel, sel, sizeof(*sel));
+ pjsip_tpselector_add_ref(®c->tp_sel);
+
+ return PJ_SUCCESS;
+}
+
+/* Release transport */
+PJ_DEF(pj_status_t) pjsip_regc_release_transport(pjsip_regc *regc)
+{
+ PJ_ASSERT_RETURN(regc, PJ_EINVAL);
+ if (regc->last_transport) {
+ pjsip_transport_dec_ref(regc->last_transport);
+ regc->last_transport = NULL;
+ }
+ return PJ_SUCCESS;
+}
+
+
+PJ_DEF(pj_status_t) pjsip_regc_add_headers( pjsip_regc *regc,
+ const pjsip_hdr *hdr_list)
+{
+ const pjsip_hdr *hdr;
+
+ PJ_ASSERT_RETURN(regc && hdr_list, PJ_EINVAL);
+
+ //This is "add" operation, so don't remove headers.
+ //pj_list_init(®c->hdr_list);
+
+ hdr = hdr_list->next;
+ while (hdr != hdr_list) {
+ pj_list_push_back(®c->hdr_list, pjsip_hdr_clone(regc->pool, hdr));
+ hdr = hdr->next;
+ }
+
+ return PJ_SUCCESS;
+}
+
+static pj_status_t create_request(pjsip_regc *regc,
+ pjsip_tx_data **p_tdata)
+{
+ pj_status_t status;
+ pjsip_tx_data *tdata;
+
+ PJ_ASSERT_RETURN(regc && p_tdata, PJ_EINVAL);
+
+ /* Create the request. */
+ status = pjsip_endpt_create_request_from_hdr( regc->endpt,
+ pjsip_get_register_method(),
+ regc->srv_url,
+ regc->from_hdr,
+ regc->to_hdr,
+ NULL,
+ regc->cid_hdr,
+ regc->cseq_hdr->cseq,
+ NULL,
+ &tdata);
+ if (status != PJ_SUCCESS)
+ return status;
+
+ /* Add cached authorization headers. */
+ pjsip_auth_clt_init_req( ®c->auth_sess, tdata );
+
+ /* Add Route headers from route set, ideally after Via header */
+ if (!pj_list_empty(®c->route_set)) {
+ pjsip_hdr *route_pos;
+ const pjsip_route_hdr *route;
+
+ route_pos = (pjsip_hdr*)
+ pjsip_msg_find_hdr(tdata->msg, PJSIP_H_VIA, NULL);
+ if (!route_pos)
+ route_pos = &tdata->msg->hdr;
+
+ route = regc->route_set.next;
+ while (route != ®c->route_set) {
+ pjsip_hdr *new_hdr = (pjsip_hdr*)
+ pjsip_hdr_shallow_clone(tdata->pool, route);
+ pj_list_insert_after(route_pos, new_hdr);
+ route_pos = new_hdr;
+ route = route->next;
+ }
+ }
+
+ /* Add additional request headers */
+ if (!pj_list_empty(®c->hdr_list)) {
+ const pjsip_hdr *hdr;
+
+ hdr = regc->hdr_list.next;
+ while (hdr != ®c->hdr_list) {
+ pjsip_hdr *new_hdr = (pjsip_hdr*)
+ pjsip_hdr_shallow_clone(tdata->pool, hdr);
+ pjsip_msg_add_hdr(tdata->msg, new_hdr);
+ hdr = hdr->next;
+ }
+ }
+
+ /* Done. */
+ *p_tdata = tdata;
+ return PJ_SUCCESS;
+}
+
+
+PJ_DEF(pj_status_t) pjsip_regc_register(pjsip_regc *regc, pj_bool_t autoreg,
+ pjsip_tx_data **p_tdata)
+{
+ pjsip_msg *msg;
+ pjsip_contact_hdr *hdr;
+ const pjsip_hdr *h_allow;
+ pj_status_t status;
+ pjsip_tx_data *tdata;
+
+ PJ_ASSERT_RETURN(regc && p_tdata, PJ_EINVAL);
+
+ pj_lock_acquire(regc->lock);
+
+ status = create_request(regc, &tdata);
+ if (status != PJ_SUCCESS) {
+ pj_lock_release(regc->lock);
+ return status;
+ }
+
+ msg = tdata->msg;
+
+ /* Add Contact headers. */
+ hdr = regc->contact_hdr_list.next;
+ while (hdr != ®c->contact_hdr_list) {
+ pjsip_msg_add_hdr(msg, (pjsip_hdr*)
+ pjsip_hdr_shallow_clone(tdata->pool, hdr));
+ hdr = hdr->next;
+ }
+
+ /* Also add bindings which are to be removed */
+ while (!pj_list_empty(®c->removed_contact_hdr_list)) {
+ hdr = regc->removed_contact_hdr_list.next;
+ pjsip_msg_add_hdr(msg, (pjsip_hdr*)
+ pjsip_hdr_clone(tdata->pool, hdr));
+ pj_list_erase(hdr);
+ }
+
+
+ if (regc->expires_hdr)
+ pjsip_msg_add_hdr(msg, (pjsip_hdr*)
+ pjsip_hdr_shallow_clone(tdata->pool,
+ regc->expires_hdr));
+
+ if (regc->timer.id != 0) {
+ pjsip_endpt_cancel_timer(regc->endpt, ®c->timer);
+ regc->timer.id = 0;
+ }
+
+ /* Add Allow header (http://trac.pjsip.org/repos/ticket/1039) */
+ h_allow = pjsip_endpt_get_capability(regc->endpt, PJSIP_H_ALLOW, NULL);
+ if (h_allow) {
+ pjsip_msg_add_hdr(msg, (pjsip_hdr*)
+ pjsip_hdr_shallow_clone(tdata->pool, h_allow));
+
+ }
+
+ regc->auto_reg = autoreg;
+
+ pj_lock_release(regc->lock);
+
+ /* Done */
+ *p_tdata = tdata;
+ return PJ_SUCCESS;
+}
+
+
+PJ_DEF(pj_status_t) pjsip_regc_unregister(pjsip_regc *regc,
+ pjsip_tx_data **p_tdata)
+{
+ pjsip_tx_data *tdata;
+ pjsip_msg *msg;
+ pjsip_hdr *hdr;
+ pj_status_t status;
+
+ PJ_ASSERT_RETURN(regc && p_tdata, PJ_EINVAL);
+
+ pj_lock_acquire(regc->lock);
+
+ if (regc->timer.id != 0) {
+ pjsip_endpt_cancel_timer(regc->endpt, ®c->timer);
+ regc->timer.id = 0;
+ }
+
+ status = create_request(regc, &tdata);
+ if (status != PJ_SUCCESS) {
+ pj_lock_release(regc->lock);
+ return status;
+ }
+
+ msg = tdata->msg;
+
+ /* Add Contact headers. */
+ hdr = (pjsip_hdr*)regc->contact_hdr_list.next;
+ while ((void*)hdr != (void*)®c->contact_hdr_list) {
+ pjsip_msg_add_hdr(msg, (pjsip_hdr*)
+ pjsip_hdr_shallow_clone(tdata->pool, hdr));
+ hdr = hdr->next;
+ }
+
+ /* Also add bindings which are to be removed */
+ while (!pj_list_empty(®c->removed_contact_hdr_list)) {
+ hdr = (pjsip_hdr*)regc->removed_contact_hdr_list.next;
+ pjsip_msg_add_hdr(msg, (pjsip_hdr*)
+ pjsip_hdr_clone(tdata->pool, hdr));
+ pj_list_erase(hdr);
+ }
+
+ /* Add Expires:0 header */
+ hdr = (pjsip_hdr*) pjsip_expires_hdr_create(tdata->pool, 0);
+ pjsip_msg_add_hdr(msg, hdr);
+
+ pj_lock_release(regc->lock);
+
+ *p_tdata = tdata;
+ return PJ_SUCCESS;
+}
+
+PJ_DEF(pj_status_t) pjsip_regc_unregister_all(pjsip_regc *regc,
+ pjsip_tx_data **p_tdata)
+{
+ pjsip_tx_data *tdata;
+ pjsip_contact_hdr *hcontact;
+ pjsip_hdr *hdr;
+ pjsip_msg *msg;
+ pj_status_t status;
+
+ PJ_ASSERT_RETURN(regc && p_tdata, PJ_EINVAL);
+
+ pj_lock_acquire(regc->lock);
+
+ if (regc->timer.id != 0) {
+ pjsip_endpt_cancel_timer(regc->endpt, ®c->timer);
+ regc->timer.id = 0;
+ }
+
+ status = create_request(regc, &tdata);
+ if (status != PJ_SUCCESS) {
+ pj_lock_release(regc->lock);
+ return status;
+ }
+
+ msg = tdata->msg;
+
+ /* Clear removed_contact_hdr_list */
+ pj_list_init(®c->removed_contact_hdr_list);
+
+ /* Add Contact:* header */
+ hcontact = pjsip_contact_hdr_create(tdata->pool);
+ hcontact->star = 1;
+ pjsip_msg_add_hdr(msg, (pjsip_hdr*)hcontact);
+
+ /* Add Expires:0 header */
+ hdr = (pjsip_hdr*) pjsip_expires_hdr_create(tdata->pool, 0);
+ pjsip_msg_add_hdr(msg, hdr);
+
+ pj_lock_release(regc->lock);
+
+ *p_tdata = tdata;
+ return PJ_SUCCESS;
+}
+
+
+PJ_DEF(pj_status_t) pjsip_regc_update_contact( pjsip_regc *regc,
+ int contact_cnt,
+ const pj_str_t contact[] )
+{
+ pj_status_t status;
+
+ PJ_ASSERT_RETURN(regc, PJ_EINVAL);
+
+ pj_lock_acquire(regc->lock);
+ status = set_contact( regc, contact_cnt, contact );
+ pj_lock_release(regc->lock);
+
+ return status;
+}
+
+
+PJ_DEF(pj_status_t) pjsip_regc_update_expires( pjsip_regc *regc,
+ pj_uint32_t expires )
+{
+ PJ_ASSERT_RETURN(regc, PJ_EINVAL);
+
+ pj_lock_acquire(regc->lock);
+ set_expires( regc, expires );
+ pj_lock_release(regc->lock);
+
+ return PJ_SUCCESS;
+}
+
+static void cbparam_init( struct pjsip_regc_cbparam *cbparam,
+ pjsip_regc *regc,
+ pj_status_t status, int st_code,
+ const pj_str_t *reason,
+ pjsip_rx_data *rdata, pj_int32_t expiration,
+ int contact_cnt, pjsip_contact_hdr *contact[])
+{
+ cbparam->regc = regc;
+ cbparam->token = regc->token;
+ cbparam->status = status;
+ cbparam->code = st_code;
+ cbparam->reason = *reason;
+ cbparam->rdata = rdata;
+ cbparam->contact_cnt = contact_cnt;
+ cbparam->expiration = expiration;
+ if (contact_cnt) {
+ pj_memcpy( cbparam->contact, contact,
+ contact_cnt*sizeof(pjsip_contact_hdr*));
+ }
+}
+
+static void call_callback(pjsip_regc *regc, pj_status_t status, int st_code,
+ const pj_str_t *reason,
+ pjsip_rx_data *rdata, pj_int32_t expiration,
+ int contact_cnt, pjsip_contact_hdr *contact[])
+{
+ struct pjsip_regc_cbparam cbparam;
+
+ if (!regc->cb)
+ return;
+
+ cbparam_init(&cbparam, regc, status, st_code, reason, rdata, expiration,
+ contact_cnt, contact);
+ (*regc->cb)(&cbparam);
+}
+
+static void regc_refresh_timer_cb( pj_timer_heap_t *timer_heap,
+ struct pj_timer_entry *entry)
+{
+ pjsip_regc *regc = (pjsip_regc*) entry->user_data;
+ pjsip_tx_data *tdata;
+ pj_status_t status;
+
+ PJ_UNUSED_ARG(timer_heap);
+
+ /* Temporarily increase busy flag to prevent regc from being deleted
+ * in pjsip_regc_send() or in the callback
+ */
+ pj_atomic_inc(regc->busy_ctr);
+
+ entry->id = 0;
+ status = pjsip_regc_register(regc, 1, &tdata);
+ if (status == PJ_SUCCESS) {
+ status = pjsip_regc_send(regc, tdata);
+ }
+
+ if (status != PJ_SUCCESS && regc->cb) {
+ char errmsg[PJ_ERR_MSG_SIZE];
+ pj_str_t reason = pj_strerror(status, errmsg, sizeof(errmsg));
+ call_callback(regc, status, 400, &reason, NULL, -1, 0, NULL);
+ }
+
+ /* Delete the record if user destroy regc during the callback. */
+ if (pj_atomic_dec_and_get(regc->busy_ctr)==0 && regc->_delete_flag) {
+ pjsip_regc_destroy(regc);
+ }
+}
+
+static void schedule_registration ( pjsip_regc *regc, pj_int32_t expiration )
+{
+ if (regc->auto_reg && expiration > 0) {
+ pj_time_val delay = { 0, 0};
+
+ pj_timer_heap_cancel_if_active(pjsip_endpt_get_timer_heap(regc->endpt),
+ ®c->timer, 0);
+
+ delay.sec = expiration - regc->delay_before_refresh;
+ if (regc->expires != PJSIP_REGC_EXPIRATION_NOT_SPECIFIED &&
+ delay.sec > (pj_int32_t)regc->expires)
+ {
+ delay.sec = regc->expires;
+ }
+ if (delay.sec < DELAY_BEFORE_REFRESH)
+ delay.sec = DELAY_BEFORE_REFRESH;
+ regc->timer.cb = ®c_refresh_timer_cb;
+ regc->timer.id = REFRESH_TIMER;
+ regc->timer.user_data = regc;
+ pjsip_endpt_schedule_timer( regc->endpt, ®c->timer, &delay);
+ pj_gettimeofday(®c->last_reg);
+ regc->next_reg = regc->last_reg;
+ regc->next_reg.sec += delay.sec;
+ }
+}
+
+PJ_DEF(pj_status_t) pjsip_regc_set_reg_tsx_cb( pjsip_regc *regc,
+ pjsip_regc_tsx_cb *tsx_cb)
+{
+ PJ_ASSERT_RETURN(regc, PJ_EINVAL);
+ regc->tsx_cb = tsx_cb;
+ return PJ_SUCCESS;
+}
+
+
+PJ_DEF(pj_status_t) pjsip_regc_set_via_sent_by( pjsip_regc *regc,
+ pjsip_host_port *via_addr,
+ pjsip_transport *via_tp)
+{
+ PJ_ASSERT_RETURN(regc, PJ_EINVAL);
+
+ if (!via_addr)
+ pj_bzero(®c->via_addr, sizeof(regc->via_addr));
+ else {
+ if (pj_strcmp(®c->via_addr.host, &via_addr->host))
+ pj_strdup(regc->pool, ®c->via_addr.host, &via_addr->host);
+ regc->via_addr.port = via_addr->port;
+ }
+ regc->via_tp = via_tp;
+
+ return PJ_SUCCESS;
+}
+
+PJ_DEF(pj_status_t)
+pjsip_regc_set_delay_before_refresh( pjsip_regc *regc,
+ pj_uint32_t delay )
+{
+ PJ_ASSERT_RETURN(regc, PJ_EINVAL);
+
+ if (delay > regc->expires)
+ return PJ_ETOOBIG;
+
+ pj_lock_acquire(regc->lock);
+
+ if (regc->delay_before_refresh != delay)
+ {
+ regc->delay_before_refresh = delay;
+
+ if (regc->timer.id != 0) {
+ /* Cancel registration timer */
+ pjsip_endpt_cancel_timer(regc->endpt, ®c->timer);
+ regc->timer.id = 0;
+
+ /* Schedule next registration */
+ schedule_registration(regc, regc->expires);
+ }
+ }
+
+ pj_lock_release(regc->lock);
+
+ return PJ_SUCCESS;
+}
+
+
+static pj_int32_t calculate_response_expiration(const pjsip_regc *regc,
+ const pjsip_rx_data *rdata,
+ unsigned *contact_cnt,
+ unsigned max_contact,
+ pjsip_contact_hdr *contacts[])
+{
+ pj_int32_t expiration = NOEXP;
+ const pjsip_msg *msg = rdata->msg_info.msg;
+ const pjsip_hdr *hdr;
+
+ /* Enumerate all Contact headers in the response */
+ *contact_cnt = 0;
+ for (hdr=msg->hdr.next; hdr!=&msg->hdr; hdr=hdr->next) {
+ if (hdr->type == PJSIP_H_CONTACT &&
+ *contact_cnt < max_contact)
+ {
+ contacts[*contact_cnt] = (pjsip_contact_hdr*)hdr;
+ ++(*contact_cnt);
+ }
+ }
+
+ if (regc->current_op == REGC_REGISTERING) {
+ pj_bool_t has_our_contact = PJ_FALSE;
+ const pjsip_expires_hdr *expires;
+
+ /* Get Expires header */
+ expires = (const pjsip_expires_hdr*)
+ pjsip_msg_find_hdr(msg, PJSIP_H_EXPIRES, NULL);
+
+ /* Try to find the Contact URIs that we register, in the response
+ * to get the expires value. We'll try both with comparing the URI
+ * and comparing the extension param only.
+ */
+ if (pjsip_cfg()->regc.check_contact || regc->add_xuid_param) {
+ unsigned i;
+ for (i=0; i<*contact_cnt; ++i) {
+ const pjsip_contact_hdr *our_hdr;
+
+ our_hdr = (const pjsip_contact_hdr*)
+ regc->contact_hdr_list.next;
+
+ /* Match with our Contact header(s) */
+ while ((void*)our_hdr != (void*)®c->contact_hdr_list) {
+
+ const pjsip_uri *uri1, *uri2;
+ pj_bool_t matched = PJ_FALSE;
+
+ /* Exclude the display name when comparing the URI
+ * since server may not return it.
+ */
+ uri1 = (const pjsip_uri*)
+ pjsip_uri_get_uri(contacts[i]->uri);
+ uri2 = (const pjsip_uri*)
+ pjsip_uri_get_uri(our_hdr->uri);
+
+ /* First try with exact matching, according to RFC 3261
+ * Section 19.1.4 URI Comparison
+ */
+ if (pjsip_cfg()->regc.check_contact) {
+ matched = pjsip_uri_cmp(PJSIP_URI_IN_CONTACT_HDR,
+ uri1, uri2)==0;
+ }
+
+ /* If no match is found, try with matching the extension
+ * parameter only if extension parameter was added.
+ */
+ if (!matched && regc->add_xuid_param &&
+ (PJSIP_URI_SCHEME_IS_SIP(uri1) ||
+ PJSIP_URI_SCHEME_IS_SIPS(uri1)) &&
+ (PJSIP_URI_SCHEME_IS_SIP(uri2) ||
+ PJSIP_URI_SCHEME_IS_SIPS(uri2)))
+ {
+ const pjsip_sip_uri *sip_uri1, *sip_uri2;
+ const pjsip_param *p1, *p2;
+
+ sip_uri1 = (const pjsip_sip_uri*)uri1;
+ sip_uri2 = (const pjsip_sip_uri*)uri2;
+
+ p1 = pjsip_param_cfind(&sip_uri1->other_param,
+ &XUID_PARAM_NAME);
+ p2 = pjsip_param_cfind(&sip_uri2->other_param,
+ &XUID_PARAM_NAME);
+ matched = p1 && p2 &&
+ pj_strcmp(&p1->value, &p2->value)==0;
+
+ }
+
+ if (matched) {
+ has_our_contact = PJ_TRUE;
+
+ if (contacts[i]->expires >= 0 &&
+ contacts[i]->expires < expiration)
+ {
+ /* Get the lowest expiration time. */
+ expiration = contacts[i]->expires;
+ }
+
+ break;
+ }
+
+ our_hdr = our_hdr->next;
+
+ } /* while ((void.. */
+
+ } /* for (i=.. */
+
+ /* If matching Contact header(s) are found but the
+ * header doesn't contain expires parameter, get the
+ * expiration value from the Expires header. And
+ * if Expires header is not present, get the expiration
+ * value from the request.
+ */
+ if (has_our_contact && expiration == NOEXP) {
+ if (expires) {
+ expiration = expires->ivalue;
+ } else if (regc->expires_hdr) {
+ expiration = regc->expires_hdr->ivalue;
+ } else {
+ /* We didn't request explicit expiration value,
+ * and server doesn't specify it either. This
+ * shouldn't happen unless we have a broken
+ * registrar.
+ */
+ expiration = 3600;
+ }
+ }
+
+ }
+
+ /* If we still couldn't get matching Contact header(s), it means
+ * there must be something wrong with the registrar (e.g. it may
+ * have modified the URI's in the response, which is prohibited).
+ */
+ if (expiration==NOEXP) {
+ /* If the number of Contact headers in the response matches
+ * ours, they're all probably ours. Get the expiration
+ * from there if this is the case, or from Expires header
+ * if we don't have exact Contact header count, or
+ * from the request as the last resort.
+ */
+ pj_size_t our_contact_cnt;
+
+ our_contact_cnt = pj_list_size(®c->contact_hdr_list);
+
+ if (*contact_cnt == our_contact_cnt && *contact_cnt &&
+ contacts[0]->expires >= 0)
+ {
+ expiration = contacts[0]->expires;
+ } else if (expires)
+ expiration = expires->ivalue;
+ else if (regc->expires_hdr)
+ expiration = regc->expires_hdr->ivalue;
+ else
+ expiration = 3600;
+ }
+
+ } else {
+ /* Just assume that the unregistration has been successful. */
+ expiration = 0;
+ }
+
+ /* Must have expiration value by now */
+ pj_assert(expiration != NOEXP);
+
+ return expiration;
+}
+
+static void regc_tsx_callback(void *token, pjsip_event *event)
+{
+ pj_status_t status;
+ pjsip_regc *regc = (pjsip_regc*) token;
+ pjsip_transaction *tsx = event->body.tsx_state.tsx;
+ pj_bool_t handled = PJ_TRUE;
+ pj_bool_t update_contact = PJ_FALSE;
+
+ pj_atomic_inc(regc->busy_ctr);
+ pj_lock_acquire(regc->lock);
+
+ /* Decrement pending transaction counter. */
+ pj_assert(regc->has_tsx);
+ regc->has_tsx = PJ_FALSE;
+
+ /* Add reference to the transport */
+ if (tsx->transport != regc->last_transport) {
+ if (regc->last_transport) {
+ pjsip_transport_dec_ref(regc->last_transport);
+ regc->last_transport = NULL;
+ }
+
+ if (tsx->transport) {
+ regc->last_transport = tsx->transport;
+ pjsip_transport_add_ref(regc->last_transport);
+ }
+ }
+
+ if (regc->_delete_flag == 0 && regc->tsx_cb &&
+ regc->current_op == REGC_REGISTERING)
+ {
+ struct pjsip_regc_tsx_cb_param param;
+
+ param.contact_cnt = -1;
+ cbparam_init(¶m.cbparam, regc, PJ_SUCCESS, tsx->status_code,
+ &tsx->status_text,
+ (event->body.tsx_state.type==PJSIP_EVENT_RX_MSG) ?
+ event->body.tsx_state.src.rdata : NULL,
+ -1, 0, NULL);
+
+ /* Call regc tsx callback before handling any response */
+ pj_lock_release(regc->lock);
+ (*regc->tsx_cb)(¶m);
+ pj_lock_acquire(regc->lock);
+
+ if (param.contact_cnt >= 0) {
+ /* Since we receive non-2xx response, it means that (some) contact
+ * bindings haven't been established so we can safely remove these
+ * contact headers. This is to avoid removing non-existent contact
+ * bindings later.
+ */
+ if (tsx->status_code/100 != 2) {
+ pjsip_contact_hdr *h;
+
+ h = regc->contact_hdr_list.next;
+ while (h != ®c->contact_hdr_list) {
+ pjsip_contact_hdr *next = h->next;
+
+ if (h->expires == -1) {
+ pj_list_erase(h);
+ }
+ h = next;
+ }
+ }
+
+ /* Update contact address */
+ pjsip_regc_update_contact(regc, param.contact_cnt, param.contact);
+ update_contact = PJ_TRUE;
+ }
+ }
+
+ /* Handle 401/407 challenge (even when _delete_flag is set) */
+ if (tsx->status_code == PJSIP_SC_PROXY_AUTHENTICATION_REQUIRED ||
+ tsx->status_code == PJSIP_SC_UNAUTHORIZED)
+ {
+ pjsip_rx_data *rdata = event->body.tsx_state.src.rdata;
+ pjsip_tx_data *tdata;
+
+ /* reset current op */
+ regc->current_op = REGC_IDLE;
+
+ if (update_contact) {
+ pjsip_msg *msg;
+ pjsip_hdr *hdr, *ins_hdr;
+ pjsip_contact_hdr *chdr;
+
+ /* Delete Contact headers, but we shouldn't delete headers
+ * which are supposed to remove contact bindings since
+ * we cannot reconstruct those headers.
+ */
+ msg = tsx->last_tx->msg;
+ hdr = msg->hdr.next;
+ ins_hdr = &msg->hdr;
+ while (hdr != &msg->hdr) {
+ pjsip_hdr *next = hdr->next;
+
+ if (hdr->type == PJSIP_H_CONTACT) {
+ chdr = (pjsip_contact_hdr *)hdr;
+ if (chdr->expires != 0) {
+ pj_list_erase(hdr);
+ ins_hdr = next;
+ }
+ }
+ hdr = next;
+ }
+
+ /* Add Contact headers. */
+ chdr = regc->contact_hdr_list.next;
+ while (chdr != ®c->contact_hdr_list) {
+ pj_list_insert_before(ins_hdr, (pjsip_hdr*)
+ pjsip_hdr_shallow_clone(tsx->last_tx->pool, chdr));
+ chdr = chdr->next;
+ }
+
+ /* Also add bindings which are to be removed */
+ while (!pj_list_empty(®c->removed_contact_hdr_list)) {
+ chdr = regc->removed_contact_hdr_list.next;
+ pj_list_insert_before(ins_hdr, (pjsip_hdr*)
+ pjsip_hdr_clone(tsx->last_tx->pool, chdr));
+ pj_list_erase(chdr);
+ }
+ }
+
+ status = pjsip_auth_clt_reinit_req( ®c->auth_sess,
+ rdata,
+ tsx->last_tx,
+ &tdata);
+
+ if (status == PJ_SUCCESS) {
+ status = pjsip_regc_send(regc, tdata);
+ }
+
+ if (status != PJ_SUCCESS) {
+
+ /* Only call callback if application is still interested
+ * in it.
+ */
+ if (regc->_delete_flag == 0) {
+ /* Should be safe to release the lock temporarily.
+ * We do this to avoid deadlock.
+ */
+ pj_lock_release(regc->lock);
+ call_callback(regc, status, tsx->status_code,
+ &rdata->msg_info.msg->line.status.reason,
+ rdata, -1, 0, NULL);
+ pj_lock_acquire(regc->lock);
+ }
+ }
+
+ } else if (regc->_delete_flag) {
+
+ /* User has called pjsip_regc_destroy(), so don't call callback.
+ * This regc will be destroyed later in this function.
+ */
+
+ /* Just reset current op */
+ regc->current_op = REGC_IDLE;
+
+ } else if (tsx->status_code == PJSIP_SC_INTERVAL_TOO_BRIEF &&
+ regc->current_op == REGC_REGISTERING)
+ {
+ /* Handle 423 response automatically:
+ * - set requested expiration to Min-Expires header, ONLY IF
+ * the original request is a registration (as opposed to
+ * unregistration) and the requested expiration was indeed
+ * lower than Min-Expires)
+ * - resend the request
+ */
+ pjsip_rx_data *rdata = event->body.tsx_state.src.rdata;
+ pjsip_min_expires_hdr *me_hdr;
+ pjsip_tx_data *tdata;
+ pj_int32_t min_exp;
+
+ /* reset current op */
+ regc->current_op = REGC_IDLE;
+
+ /* Update requested expiration */
+ me_hdr = (pjsip_min_expires_hdr*)
+ pjsip_msg_find_hdr(rdata->msg_info.msg,
+ PJSIP_H_MIN_EXPIRES, NULL);
+ if (me_hdr) {
+ min_exp = me_hdr->ivalue;
+ } else {
+ /* Broken server, Min-Expires doesn't exist.
+ * Just guestimate then, BUT ONLY if if this is the
+ * first time we received such response.
+ */
+ enum {
+ /* Note: changing this value would require changing couple of
+ * Python test scripts.
+ */
+ UNSPECIFIED_MIN_EXPIRES = 3601
+ };
+ if (!regc->expires_hdr ||
+ regc->expires_hdr->ivalue != UNSPECIFIED_MIN_EXPIRES)
+ {
+ min_exp = UNSPECIFIED_MIN_EXPIRES;
+ } else {
+ handled = PJ_FALSE;
+ PJ_LOG(4,(THIS_FILE, "Registration failed: 423 response "
+ "without Min-Expires header is invalid"));
+ goto handle_err;
+ }
+ }
+
+ if (regc->expires_hdr && regc->expires_hdr->ivalue >= min_exp) {
+ /* But we already send with greater expiration time, why does
+ * the server send us with 423? Oh well, just fail the request.
+ */
+ handled = PJ_FALSE;
+ PJ_LOG(4,(THIS_FILE, "Registration failed: invalid "
+ "Min-Expires header value in response"));
+ goto handle_err;
+ }
+
+ set_expires(regc, min_exp);
+
+ status = pjsip_regc_register(regc, regc->auto_reg, &tdata);
+ if (status == PJ_SUCCESS) {
+ status = pjsip_regc_send(regc, tdata);
+ }
+
+ if (status != PJ_SUCCESS) {
+ /* Only call callback if application is still interested
+ * in it.
+ */
+ if (!regc->_delete_flag) {
+ /* Should be safe to release the lock temporarily.
+ * We do this to avoid deadlock.
+ */
+ pj_lock_release(regc->lock);
+ call_callback(regc, status, tsx->status_code,
+ &rdata->msg_info.msg->line.status.reason,
+ rdata, -1, 0, NULL);
+ pj_lock_acquire(regc->lock);
+ }
+ }
+
+ } else {
+ handled = PJ_FALSE;
+ }
+
+handle_err:
+ if (!handled) {
+ pjsip_rx_data *rdata;
+ pj_int32_t expiration = NOEXP;
+ unsigned contact_cnt = 0;
+ pjsip_contact_hdr *contact[PJSIP_REGC_MAX_CONTACT];
+
+ if (tsx->status_code/100 == 2) {
+
+ rdata = event->body.tsx_state.src.rdata;
+
+ /* Calculate expiration */
+ expiration = calculate_response_expiration(regc, rdata,
+ &contact_cnt,
+ PJSIP_REGC_MAX_CONTACT,
+ contact);
+
+ /* Schedule next registration */
+ schedule_registration(regc, expiration);
+
+ } else {
+ rdata = (event->body.tsx_state.type==PJSIP_EVENT_RX_MSG) ?
+ event->body.tsx_state.src.rdata : NULL;
+ }
+
+ /* Update registration */
+ if (expiration==NOEXP) expiration=-1;
+ regc->expires = expiration;
+
+ /* Mark operation as complete */
+ regc->current_op = REGC_IDLE;
+
+ /* Call callback. */
+ /* Should be safe to release the lock temporarily.
+ * We do this to avoid deadlock.
+ */
+ pj_lock_release(regc->lock);
+ call_callback(regc, PJ_SUCCESS, tsx->status_code,
+ (rdata ? &rdata->msg_info.msg->line.status.reason
+ : &tsx->status_text),
+ rdata, expiration,
+ contact_cnt, contact);
+ pj_lock_acquire(regc->lock);
+ }
+
+ pj_lock_release(regc->lock);
+
+ /* Delete the record if user destroy regc during the callback. */
+ if (pj_atomic_dec_and_get(regc->busy_ctr)==0 && regc->_delete_flag) {
+ pjsip_regc_destroy(regc);
+ }
+}
+
+PJ_DEF(pj_status_t) pjsip_regc_send(pjsip_regc *regc, pjsip_tx_data *tdata)
+{
+ pj_status_t status;
+ pjsip_cseq_hdr *cseq_hdr;
+ pjsip_expires_hdr *expires_hdr;
+ pj_uint32_t cseq;
+
+ pj_atomic_inc(regc->busy_ctr);
+ pj_lock_acquire(regc->lock);
+
+ /* Make sure we don't have pending transaction. */
+ if (regc->has_tsx) {
+ PJ_LOG(4,(THIS_FILE, "Unable to send request, regc has another "
+ "transaction pending"));
+ pjsip_tx_data_dec_ref( tdata );
+ pj_lock_release(regc->lock);
+ pj_atomic_dec(regc->busy_ctr);
+ return PJSIP_EBUSY;
+ }
+
+ pj_assert(regc->current_op == REGC_IDLE);
+
+ /* Invalidate message buffer. */
+ pjsip_tx_data_invalidate_msg(tdata);
+
+ /* Increment CSeq */
+ cseq = ++regc->cseq_hdr->cseq;
+ cseq_hdr = (pjsip_cseq_hdr*)
+ pjsip_msg_find_hdr(tdata->msg, PJSIP_H_CSEQ, NULL);
+ cseq_hdr->cseq = cseq;
+
+ /* Find Expires header */
+ expires_hdr = (pjsip_expires_hdr*)
+ pjsip_msg_find_hdr(tdata->msg, PJSIP_H_EXPIRES, NULL);
+
+ /* Bind to transport selector */
+ pjsip_tx_data_set_transport(tdata, ®c->tp_sel);
+
+ regc->has_tsx = PJ_TRUE;
+
+ /* Set current operation based on the value of Expires header */
+ if (expires_hdr && expires_hdr->ivalue==0)
+ regc->current_op = REGC_UNREGISTERING;
+ else
+ regc->current_op = REGC_REGISTERING;
+
+ /* Prevent deletion of tdata, e.g: when something wrong in sending,
+ * we need tdata to retrieve the transport.
+ */
+ pjsip_tx_data_add_ref(tdata);
+
+ /* If via_addr is set, use this address for the Via header. */
+ if (regc->via_addr.host.slen > 0) {
+ tdata->via_addr = regc->via_addr;
+ tdata->via_tp = regc->via_tp;
+ }
+
+ /* Need to unlock the regc temporarily while sending the message to
+ * prevent deadlock (https://trac.pjsip.org/repos/ticket/1247).
+ * It should be safe to do this since the regc's refcount has been
+ * incremented.
+ */
+ pj_lock_release(regc->lock);
+
+ /* Now send the message */
+ status = pjsip_endpt_send_request(regc->endpt, tdata, REGC_TSX_TIMEOUT,
+ regc, ®c_tsx_callback);
+ if (status!=PJ_SUCCESS) {
+ PJ_LOG(4,(THIS_FILE, "Error sending request, status=%d", status));
+ }
+
+ /* Reacquire the lock */
+ pj_lock_acquire(regc->lock);
+
+ /* Get last transport used and add reference to it */
+ if (tdata->tp_info.transport != regc->last_transport &&
+ status==PJ_SUCCESS)
+ {
+ if (regc->last_transport) {
+ pjsip_transport_dec_ref(regc->last_transport);
+ regc->last_transport = NULL;
+ }
+
+ if (tdata->tp_info.transport) {
+ regc->last_transport = tdata->tp_info.transport;
+ pjsip_transport_add_ref(regc->last_transport);
+ }
+ }
+
+ /* Release tdata */
+ pjsip_tx_data_dec_ref(tdata);
+
+ pj_lock_release(regc->lock);
+
+ /* Delete the record if user destroy regc during the callback. */
+ if (pj_atomic_dec_and_get(regc->busy_ctr)==0 && regc->_delete_flag) {
+ pjsip_regc_destroy(regc);
+ }
+
+ return status;
+}
+
+
diff --git a/jni/pjproject-android/.svn/pristine/49/49baa624266f6ebb133c25569a86b2881dc915aa.svn-base b/jni/pjproject-android/.svn/pristine/49/49baa624266f6ebb133c25569a86b2881dc915aa.svn-base
new file mode 100644
index 0000000..9dbb118
--- /dev/null
+++ b/jni/pjproject-android/.svn/pristine/49/49baa624266f6ebb133c25569a86b2881dc915aa.svn-base
@@ -0,0 +1,19 @@
+; symsndtest.pkg
+
+; Languages
+&EN
+
+; Header
+#{"symsndtest"},(0xA000000E), 0, 1, 1
+
+; Platform compatibility
+[0x101F7961], *, *, *, {"Series60ProductID"}
+
+; vendor
+%{"PJSIP"}
+:"PJSIP"
+
+; Target
+"$(EPOCROOT)Epoc32\release\$(PLATFORM)\$(TARGET)\symsndtest.exe"-"!:\sys\bin\symsndtest.exe"
+"$(EPOCROOT)Epoc32\data\z\private\10003a3f\apps\symsndtest_reg.rSC"-"!:\private\10003a3f\import\apps\symsndtest_reg.rSC"
+
diff --git a/jni/pjproject-android/.svn/pristine/49/49f03cdcb2c809e5d7ad1bfb9a5511ff968018b8.svn-base b/jni/pjproject-android/.svn/pristine/49/49f03cdcb2c809e5d7ad1bfb9a5511ff968018b8.svn-base
new file mode 100644
index 0000000..255ac44
--- /dev/null
+++ b/jni/pjproject-android/.svn/pristine/49/49f03cdcb2c809e5d7ad1bfb9a5511ff968018b8.svn-base
@@ -0,0 +1 @@
+#include "../../../portaudio/src/hostapi/oss/pa_unix_oss.c"