* #27232: jni: added pjproject checkout as regular git content
We will remove it once the next release of pjsip (with Android support)
comes out and is merged into SFLphone.
diff --git a/jni/pjproject-android/.svn/pristine/ad/ad2dafb3d167ceefba0d9a2efd56524ae4dfec6c.svn-base b/jni/pjproject-android/.svn/pristine/ad/ad2dafb3d167ceefba0d9a2efd56524ae4dfec6c.svn-base
new file mode 100644
index 0000000..07d4973
--- /dev/null
+++ b/jni/pjproject-android/.svn/pristine/ad/ad2dafb3d167ceefba0d9a2efd56524ae4dfec6c.svn-base
@@ -0,0 +1,2109 @@
+/* $Id$ */
+/*
+ * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
+ *
+ * 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 <pj/assert.h>
+#include <pj/log.h>
+#include <pj/os.h>
+
+#if PJMEDIA_AUDIO_DEV_HAS_COREAUDIO
+
+#include "TargetConditionals.h"
+#if TARGET_OS_IPHONE
+ #define COREAUDIO_MAC 0
+#else
+ #define COREAUDIO_MAC 1
+#endif
+
+#include <AudioUnit/AudioUnit.h>
+#include <AudioToolbox/AudioConverter.h>
+#if COREAUDIO_MAC
+ #include <CoreAudio/CoreAudio.h>
+#else
+ #include <AudioToolbox/AudioServices.h>
+
+ #define AudioDeviceID unsigned
+
+ /**
+ * As in iOS SDK 4 or later, audio route change property listener is
+ * no longer necessary. Just make surethat your application can receive
+ * remote control events by adding the code:
+ * [[UIApplication sharedApplication]
+ * beginReceivingRemoteControlEvents];
+ * Otherwise audio route change (such as headset plug/unplug) will not be
+ * processed while your application is in the background mode.
+ */
+ #define USE_AUDIO_ROUTE_CHANGE_PROP_LISTENER 0
+
+#endif
+
+/* For Mac OS 10.5.x and earlier */
+#if AUDIO_UNIT_VERSION < 1060
+ #define AudioComponent Component
+ #define AudioComponentDescription ComponentDescription
+ #define AudioComponentInstance ComponentInstance
+ #define AudioComponentFindNext FindNextComponent
+ #define AudioComponentInstanceNew OpenAComponent
+ #define AudioComponentInstanceDispose CloseComponent
+#endif
+
+
+#define THIS_FILE "coreaudio_dev.c"
+
+/* coreaudio device info */
+struct coreaudio_dev_info
+{
+ pjmedia_aud_dev_info info;
+ AudioDeviceID dev_id;
+};
+
+/* linked list of streams */
+struct stream_list
+{
+ PJ_DECL_LIST_MEMBER(struct stream_list);
+ struct coreaudio_stream *stream;
+};
+
+/* coreaudio factory */
+struct coreaudio_factory
+{
+ pjmedia_aud_dev_factory base;
+ pj_pool_t *base_pool;
+ pj_pool_t *pool;
+ pj_pool_factory *pf;
+ pj_mutex_t *mutex;
+
+ unsigned dev_count;
+ struct coreaudio_dev_info *dev_info;
+
+ AudioComponent io_comp;
+ struct stream_list streams;
+};
+
+/* Sound stream. */
+struct coreaudio_stream
+{
+ pjmedia_aud_stream base; /**< Base stream */
+ pjmedia_aud_param param; /**< Settings */
+ pj_pool_t *pool; /**< Memory pool. */
+ struct coreaudio_factory *cf;
+ struct stream_list list_entry;
+
+ pjmedia_aud_rec_cb rec_cb; /**< Capture callback. */
+ pjmedia_aud_play_cb play_cb; /**< Playback callback. */
+ void *user_data; /**< Application data. */
+
+ pj_timestamp play_timestamp;
+ pj_timestamp rec_timestamp;
+
+ pj_int16_t *rec_buf;
+ unsigned rec_buf_count;
+ pj_int16_t *play_buf;
+ unsigned play_buf_count;
+
+ pj_bool_t interrupted;
+ pj_bool_t quit_flag;
+ pj_bool_t running;
+
+ pj_bool_t rec_thread_initialized;
+ pj_thread_desc rec_thread_desc;
+ pj_thread_t *rec_thread;
+
+ pj_bool_t play_thread_initialized;
+ pj_thread_desc play_thread_desc;
+ pj_thread_t *play_thread;
+
+ AudioUnit io_units[2];
+ AudioStreamBasicDescription streamFormat;
+ AudioBufferList *audio_buf;
+
+ AudioConverterRef resample;
+ pj_int16_t *resample_buf;
+ void *resample_buf_ptr;
+ unsigned resample_buf_count;
+ unsigned resample_buf_size;
+};
+
+/* Static variable */
+static struct coreaudio_factory *cf_instance = NULL;
+
+/* Prototypes */
+static pj_status_t ca_factory_init(pjmedia_aud_dev_factory *f);
+static pj_status_t ca_factory_destroy(pjmedia_aud_dev_factory *f);
+static pj_status_t ca_factory_refresh(pjmedia_aud_dev_factory *f);
+static unsigned ca_factory_get_dev_count(pjmedia_aud_dev_factory *f);
+static pj_status_t ca_factory_get_dev_info(pjmedia_aud_dev_factory *f,
+ unsigned index,
+ pjmedia_aud_dev_info *info);
+static pj_status_t ca_factory_default_param(pjmedia_aud_dev_factory *f,
+ unsigned index,
+ pjmedia_aud_param *param);
+static pj_status_t ca_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 ca_stream_get_param(pjmedia_aud_stream *strm,
+ pjmedia_aud_param *param);
+static pj_status_t ca_stream_get_cap(pjmedia_aud_stream *strm,
+ pjmedia_aud_dev_cap cap,
+ void *value);
+static pj_status_t ca_stream_set_cap(pjmedia_aud_stream *strm,
+ pjmedia_aud_dev_cap cap,
+ const void *value);
+static pj_status_t ca_stream_start(pjmedia_aud_stream *strm);
+static pj_status_t ca_stream_stop(pjmedia_aud_stream *strm);
+static pj_status_t ca_stream_destroy(pjmedia_aud_stream *strm);
+static pj_status_t create_audio_unit(AudioComponent io_comp,
+ AudioDeviceID dev_id,
+ pjmedia_dir dir,
+ struct coreaudio_stream *strm,
+ AudioUnit *io_unit);
+#if !COREAUDIO_MAC
+static void interruptionListener(void *inClientData, UInt32 inInterruption);
+static void propListener(void * inClientData,
+ AudioSessionPropertyID inID,
+ UInt32 inDataSize,
+ const void * inData);
+#endif
+
+/* Operations */
+static pjmedia_aud_dev_factory_op factory_op =
+{
+ &ca_factory_init,
+ &ca_factory_destroy,
+ &ca_factory_get_dev_count,
+ &ca_factory_get_dev_info,
+ &ca_factory_default_param,
+ &ca_factory_create_stream,
+ &ca_factory_refresh
+};
+
+static pjmedia_aud_stream_op stream_op =
+{
+ &ca_stream_get_param,
+ &ca_stream_get_cap,
+ &ca_stream_set_cap,
+ &ca_stream_start,
+ &ca_stream_stop,
+ &ca_stream_destroy
+};
+
+
+/****************************************************************************
+ * Factory operations
+ */
+/*
+ * Init coreaudio audio driver.
+ */
+pjmedia_aud_dev_factory* pjmedia_coreaudio_factory(pj_pool_factory *pf)
+{
+ struct coreaudio_factory *f;
+ pj_pool_t *pool;
+
+ pool = pj_pool_create(pf, "core audio base", 1000, 1000, NULL);
+ f = PJ_POOL_ZALLOC_T(pool, struct coreaudio_factory);
+ f->pf = pf;
+ f->base_pool = pool;
+ f->base.op = &factory_op;
+
+ return &f->base;
+}
+
+
+/* API: init factory */
+static pj_status_t ca_factory_init(pjmedia_aud_dev_factory *f)
+{
+ struct coreaudio_factory *cf = (struct coreaudio_factory*)f;
+ AudioComponentDescription desc;
+ pj_status_t status;
+#if !COREAUDIO_MAC
+ unsigned i;
+ OSStatus ostatus;
+#endif
+
+ pj_list_init(&cf->streams);
+ status = pj_mutex_create_recursive(cf->base_pool,
+ "coreaudio",
+ &cf->mutex);
+ if (status != PJ_SUCCESS)
+ return status;
+
+ desc.componentType = kAudioUnitType_Output;
+#if COREAUDIO_MAC
+ desc.componentSubType = kAudioUnitSubType_HALOutput;
+#else
+ desc.componentSubType = kAudioUnitSubType_RemoteIO;
+#endif
+ desc.componentManufacturer = kAudioUnitManufacturer_Apple;
+ desc.componentFlags = 0;
+ desc.componentFlagsMask = 0;
+
+ cf->io_comp = AudioComponentFindNext(NULL, &desc);
+ if (cf->io_comp == NULL)
+ return PJMEDIA_EAUD_INIT; // cannot find IO unit;
+
+ status = ca_factory_refresh(f);
+ if (status != PJ_SUCCESS)
+ return status;
+
+#if !COREAUDIO_MAC
+ cf->pool = pj_pool_create(cf->pf, "core audio", 1000, 1000, NULL);
+ cf->dev_count = 1;
+ cf->dev_info = (struct coreaudio_dev_info*)
+ pj_pool_calloc(cf->pool, cf->dev_count,
+ sizeof(struct coreaudio_dev_info));
+ for (i = 0; i < cf->dev_count; i++) {
+ struct coreaudio_dev_info *cdi;
+
+ cdi = &cf->dev_info[i];
+ pj_bzero(cdi, sizeof(*cdi));
+ cdi->dev_id = 0;
+ strcpy(cdi->info.name, "iPhone IO device");
+ strcpy(cdi->info.driver, "apple");
+ cdi->info.input_count = 1;
+ cdi->info.output_count = 1;
+ cdi->info.default_samples_per_sec = 8000;
+
+ /* Set the device capabilities here */
+ cdi->info.caps = PJMEDIA_AUD_DEV_CAP_INPUT_LATENCY |
+ PJMEDIA_AUD_DEV_CAP_OUTPUT_LATENCY |
+ PJMEDIA_AUD_DEV_CAP_OUTPUT_VOLUME_SETTING |
+ PJMEDIA_AUD_DEV_CAP_INPUT_ROUTE |
+ PJMEDIA_AUD_DEV_CAP_OUTPUT_ROUTE |
+ PJMEDIA_AUD_DEV_CAP_EC;
+ cdi->info.routes = PJMEDIA_AUD_DEV_ROUTE_LOUDSPEAKER |
+ PJMEDIA_AUD_DEV_ROUTE_EARPIECE |
+ PJMEDIA_AUD_DEV_ROUTE_BLUETOOTH;
+
+ PJ_LOG(4, (THIS_FILE, " dev_id %d: %s (in=%d, out=%d) %dHz",
+ i,
+ cdi->info.name,
+ cdi->info.input_count,
+ cdi->info.output_count,
+ cdi->info.default_samples_per_sec));
+ }
+
+ /* Initialize the Audio Session */
+ ostatus = AudioSessionInitialize(NULL, NULL, interruptionListener, NULL);
+ if (ostatus != kAudioSessionNoError) {
+ PJ_LOG(4, (THIS_FILE,
+ "Warning: cannot initialize audio session services (%i)",
+ ostatus));
+ }
+
+ /* Listen for audio routing change notifications. */
+#if USE_AUDIO_ROUTE_CHANGE_PROP_LISTENER != 0
+ ostatus = AudioSessionAddPropertyListener(
+ kAudioSessionProperty_AudioRouteChange,
+ propListener, cf);
+ if (ostatus != kAudioSessionNoError) {
+ PJ_LOG(4, (THIS_FILE,
+ "Warning: cannot listen for audio route change "
+ "notifications (%i)", ostatus));
+ }
+#endif
+
+ cf_instance = cf;
+#endif
+
+ PJ_LOG(4, (THIS_FILE, "core audio initialized"));
+
+ return PJ_SUCCESS;
+}
+
+/* API: destroy factory */
+static pj_status_t ca_factory_destroy(pjmedia_aud_dev_factory *f)
+{
+ struct coreaudio_factory *cf = (struct coreaudio_factory*)f;
+ pj_pool_t *pool;
+
+ pj_assert(cf);
+ pj_assert(cf->base_pool);
+ pj_assert(pj_list_empty(&cf->streams));
+
+#if !COREAUDIO_MAC
+#if USE_AUDIO_ROUTE_CHANGE_PROP_LISTENER != 0
+ AudioSessionRemovePropertyListenerWithUserData(
+ kAudioSessionProperty_AudioRouteChange, propListener, cf);
+#endif
+#endif
+
+ if (cf->pool) {
+ pj_pool_release(cf->pool);
+ cf->pool = NULL;
+ }
+
+ if (cf->mutex) {
+ pj_mutex_lock(cf->mutex);
+ cf_instance = NULL;
+ pj_mutex_unlock(cf->mutex);
+ pj_mutex_destroy(cf->mutex);
+ cf->mutex = NULL;
+ }
+
+ pool = cf->base_pool;
+ cf->base_pool = NULL;
+ pj_pool_release(pool);
+
+ return PJ_SUCCESS;
+}
+
+/* API: refresh the device list */
+static pj_status_t ca_factory_refresh(pjmedia_aud_dev_factory *f)
+{
+#if !COREAUDIO_MAC
+ /* iPhone doesn't support refreshing the device list */
+ PJ_UNUSED_ARG(f);
+ return PJ_SUCCESS;
+#else
+ struct coreaudio_factory *cf = (struct coreaudio_factory*)f;
+ unsigned i;
+ unsigned dev_count;
+ AudioObjectPropertyAddress addr;
+ AudioDeviceID *dev_ids;
+ UInt32 buf_size, dev_size, size = sizeof(AudioDeviceID);
+ AudioBufferList *buf = NULL;
+ OSStatus ostatus;
+
+ if (cf->pool != NULL) {
+ pj_pool_release(cf->pool);
+ cf->pool = NULL;
+ }
+
+ cf->dev_count = 0;
+ cf->pool = pj_pool_create(cf->pf, "core audio", 1000, 1000, NULL);
+
+ /* Find out how many audio devices there are */
+ addr.mSelector = kAudioHardwarePropertyDevices;
+ addr.mScope = kAudioObjectPropertyScopeGlobal;
+ addr.mElement = kAudioObjectPropertyElementMaster;
+ ostatus = AudioObjectGetPropertyDataSize(kAudioObjectSystemObject, &addr,
+ 0, NULL, &dev_size);
+ if (ostatus != noErr) {
+ dev_size = 0;
+ }
+
+ /* Calculate the number of audio devices available */
+ dev_count = dev_size / size;
+ if (dev_count==0) {
+ PJ_LOG(4,(THIS_FILE, "core audio found no sound devices"));
+ /* Enabling this will cause pjsua-lib initialization to fail when
+ * there is no sound device installed in the system, even when pjsua
+ * has been run with --null-audio. Moreover, it might be better to
+ * think that the core audio backend initialization is successful,
+ * regardless there is no audio device installed, as later application
+ * can check it using get_dev_count().
+ return PJMEDIA_EAUD_NODEV;
+ */
+ return PJ_SUCCESS;
+ }
+ PJ_LOG(4, (THIS_FILE, "core audio detected %d devices",
+ dev_count));
+
+ /* Get all the audio device IDs */
+ dev_ids = (AudioDeviceID *)pj_pool_calloc(cf->pool, dev_size, size);
+ if (!dev_ids)
+ return PJ_ENOMEM;
+ pj_bzero(dev_ids, dev_count);
+ ostatus = AudioObjectGetPropertyData(kAudioObjectSystemObject, &addr,
+ 0, NULL,
+ &dev_size, (void *)dev_ids);
+ if (ostatus != noErr ) {
+ /* This should not happen since we have successfully retrieved
+ * the property data size before
+ */
+ return PJMEDIA_EAUD_INIT;
+ }
+
+ if (dev_size > 1) {
+ AudioDeviceID dev_id = kAudioObjectUnknown;
+ unsigned idx = 0;
+
+ /* Find default audio input device */
+ addr.mSelector = kAudioHardwarePropertyDefaultInputDevice;
+ addr.mScope = kAudioObjectPropertyScopeGlobal;
+ addr.mElement = kAudioObjectPropertyElementMaster;
+ size = sizeof(dev_id);
+
+ ostatus = AudioObjectGetPropertyData(kAudioObjectSystemObject,
+ &addr, 0, NULL,
+ &size, (void *)&dev_id);
+ if (ostatus == noErr && dev_id != dev_ids[idx]) {
+ AudioDeviceID temp_id = dev_ids[idx];
+
+ for (i = idx + 1; i < dev_size; i++) {
+ if (dev_ids[i] == dev_id) {
+ dev_ids[idx++] = dev_id;
+ dev_ids[i] = temp_id;
+ break;
+ }
+ }
+ }
+
+ /* Find default audio output device */
+ addr.mSelector = kAudioHardwarePropertyDefaultOutputDevice;
+ ostatus = AudioObjectGetPropertyData(kAudioObjectSystemObject,
+ &addr, 0, NULL,
+ &size, (void *)&dev_id);
+ if (ostatus == noErr && dev_id != dev_ids[idx]) {
+ AudioDeviceID temp_id = dev_ids[idx];
+
+ for (i = idx + 1; i < dev_size; i++) {
+ if (dev_ids[i] == dev_id) {
+ dev_ids[idx] = dev_id;
+ dev_ids[i] = temp_id;
+ break;
+ }
+ }
+ }
+ }
+
+ /* Build the devices' info */
+ cf->dev_info = (struct coreaudio_dev_info*)
+ pj_pool_calloc(cf->pool, dev_count,
+ sizeof(struct coreaudio_dev_info));
+ buf_size = 0;
+ for (i = 0; i < dev_count; i++) {
+ struct coreaudio_dev_info *cdi;
+ Float64 sampleRate;
+
+ cdi = &cf->dev_info[i];
+ pj_bzero(cdi, sizeof(*cdi));
+ cdi->dev_id = dev_ids[i];
+
+ /* Get device name */
+ addr.mSelector = kAudioDevicePropertyDeviceName;
+ addr.mScope = kAudioObjectPropertyScopeGlobal;
+ addr.mElement = kAudioObjectPropertyElementMaster;
+ size = sizeof(cdi->info.name);
+ AudioObjectGetPropertyData(cdi->dev_id, &addr,
+ 0, NULL,
+ &size, (void *)cdi->info.name);
+
+ strcpy(cdi->info.driver, "core audio");
+
+ /* Get the number of input channels */
+ addr.mSelector = kAudioDevicePropertyStreamConfiguration;
+ addr.mScope = kAudioDevicePropertyScopeInput;
+ size = 0;
+ ostatus = AudioObjectGetPropertyDataSize(cdi->dev_id, &addr,
+ 0, NULL, &size);
+ if (ostatus == noErr && size > 0) {
+
+ if (size > buf_size) {
+ buf = pj_pool_alloc(cf->pool, size);
+ buf_size = size;
+ }
+ if (buf) {
+ UInt32 idx;
+
+ /* Get the input stream configuration */
+ ostatus = AudioObjectGetPropertyData(cdi->dev_id, &addr,
+ 0, NULL,
+ &size, buf);
+ if (ostatus == noErr) {
+ /* Count the total number of input channels in
+ * the stream
+ */
+ for (idx = 0; idx < buf->mNumberBuffers; idx++) {
+ cdi->info.input_count +=
+ buf->mBuffers[idx].mNumberChannels;
+ }
+ }
+ }
+ }
+
+ /* Get the number of output channels */
+ addr.mScope = kAudioDevicePropertyScopeOutput;
+ size = 0;
+ ostatus = AudioObjectGetPropertyDataSize(cdi->dev_id, &addr,
+ 0, NULL, &size);
+ if (ostatus == noErr && size > 0) {
+
+ if (size > buf_size) {
+ buf = pj_pool_alloc(cf->pool, size);
+ buf_size = size;
+ }
+ if (buf) {
+ UInt32 idx;
+
+ /* Get the output stream configuration */
+ ostatus = AudioObjectGetPropertyData(cdi->dev_id, &addr,
+ 0, NULL,
+ &size, buf);
+ if (ostatus == noErr) {
+ /* Count the total number of output channels in
+ * the stream
+ */
+ for (idx = 0; idx < buf->mNumberBuffers; idx++) {
+ cdi->info.output_count +=
+ buf->mBuffers[idx].mNumberChannels;
+ }
+ }
+ }
+ }
+
+ /* Get default sample rate */
+ addr.mSelector = kAudioDevicePropertyNominalSampleRate;
+ addr.mScope = kAudioObjectPropertyScopeGlobal;
+ size = sizeof(Float64);
+ ostatus = AudioObjectGetPropertyData (cdi->dev_id, &addr,
+ 0, NULL,
+ &size, &sampleRate);
+ cdi->info.default_samples_per_sec = (ostatus == noErr ?
+ sampleRate:
+ 16000);
+
+ /* Set device capabilities here */
+ if (cdi->info.input_count > 0) {
+ cdi->info.caps |= PJMEDIA_AUD_DEV_CAP_INPUT_LATENCY;
+ }
+ if (cdi->info.output_count > 0) {
+ cdi->info.caps |= PJMEDIA_AUD_DEV_CAP_OUTPUT_LATENCY;
+ addr.mSelector = kAudioDevicePropertyVolumeScalar;
+ addr.mScope = kAudioDevicePropertyScopeOutput;
+ if (AudioObjectHasProperty(cdi->dev_id, &addr)) {
+ cdi->info.caps |= PJMEDIA_AUD_DEV_CAP_OUTPUT_VOLUME_SETTING;
+ }
+ }
+
+ cf->dev_count++;
+
+ PJ_LOG(4, (THIS_FILE, " dev_id %d: %s (in=%d, out=%d) %dHz",
+ i,
+ cdi->info.name,
+ cdi->info.input_count,
+ cdi->info.output_count,
+ cdi->info.default_samples_per_sec));
+ }
+
+ return PJ_SUCCESS;
+#endif
+}
+
+/* API: get number of devices */
+static unsigned ca_factory_get_dev_count(pjmedia_aud_dev_factory *f)
+{
+ struct coreaudio_factory *cf = (struct coreaudio_factory*)f;
+ return cf->dev_count;
+}
+
+/* API: get device info */
+static pj_status_t ca_factory_get_dev_info(pjmedia_aud_dev_factory *f,
+ unsigned index,
+ pjmedia_aud_dev_info *info)
+{
+ struct coreaudio_factory *cf = (struct coreaudio_factory*)f;
+
+ PJ_ASSERT_RETURN(index < cf->dev_count, PJMEDIA_EAUD_INVDEV);
+
+ pj_memcpy(info, &cf->dev_info[index].info, sizeof(*info));
+
+ return PJ_SUCCESS;
+}
+
+/* API: create default device parameter */
+static pj_status_t ca_factory_default_param(pjmedia_aud_dev_factory *f,
+ unsigned index,
+ pjmedia_aud_param *param)
+{
+ struct coreaudio_factory *cf = (struct coreaudio_factory*)f;
+ struct coreaudio_dev_info *di = &cf->dev_info[index];
+
+ PJ_ASSERT_RETURN(index < cf->dev_count, PJMEDIA_EAUD_INVDEV);
+
+ 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;
+ } else if (di->info.input_count) {
+ param->dir = PJMEDIA_DIR_CAPTURE;
+ param->rec_id = index;
+ param->play_id = PJMEDIA_AUD_INVALID_DEV;
+ } else if (di->info.output_count) {
+ param->dir = PJMEDIA_DIR_PLAYBACK;
+ param->play_id = index;
+ param->rec_id = PJMEDIA_AUD_INVALID_DEV;
+ } else {
+ return PJMEDIA_EAUD_INVDEV;
+ }
+
+ /* Set the mandatory settings here */
+ param->clock_rate = di->info.default_samples_per_sec;
+ param->channel_count = 1;
+ param->samples_per_frame = di->info.default_samples_per_sec * 20 / 1000;
+ param->bits_per_sample = 16;
+
+ /* Set the param for device capabilities here */
+ param->flags = PJMEDIA_AUD_DEV_CAP_INPUT_LATENCY |
+ PJMEDIA_AUD_DEV_CAP_OUTPUT_LATENCY;
+ param->input_latency_ms = PJMEDIA_SND_DEFAULT_REC_LATENCY;
+ param->output_latency_ms = PJMEDIA_SND_DEFAULT_PLAY_LATENCY;
+
+ return PJ_SUCCESS;
+}
+
+OSStatus resampleProc(AudioConverterRef inAudioConverter,
+ UInt32 *ioNumberDataPackets,
+ AudioBufferList *ioData,
+ AudioStreamPacketDescription **outDataPacketDescription,
+ void *inUserData)
+{
+ struct coreaudio_stream *strm = (struct coreaudio_stream*)inUserData;
+
+ if (*ioNumberDataPackets > strm->resample_buf_size)
+ *ioNumberDataPackets = strm->resample_buf_size;
+
+ ioData->mNumberBuffers = 1;
+ ioData->mBuffers[0].mNumberChannels = strm->streamFormat.mChannelsPerFrame;
+ ioData->mBuffers[0].mData = strm->resample_buf_ptr;
+ ioData->mBuffers[0].mDataByteSize = *ioNumberDataPackets *
+ strm->streamFormat.mChannelsPerFrame *
+ strm->param.bits_per_sample >> 3;
+
+ return noErr;
+}
+
+static OSStatus resample_callback(void *inRefCon,
+ AudioUnitRenderActionFlags *ioActionFlags,
+ const AudioTimeStamp *inTimeStamp,
+ UInt32 inBusNumber,
+ UInt32 inNumberFrames,
+ AudioBufferList *ioData)
+{
+ struct coreaudio_stream *strm = (struct coreaudio_stream*)inRefCon;
+ OSStatus ostatus;
+ pj_status_t status = 0;
+ unsigned nsamples;
+ AudioBufferList *buf = strm->audio_buf;
+ pj_int16_t *input;
+ UInt32 resampleSize;
+
+ pj_assert(!strm->quit_flag);
+
+ /* Known cases of callback's thread:
+ * - The thread may be changed in the middle of a session
+ * 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("ca_rec", strm->rec_thread_desc,
+ &strm->rec_thread);
+ strm->rec_thread_initialized = 1;
+ PJ_LOG(5,(THIS_FILE, "Recorder thread started, (%i frames)",
+ inNumberFrames));
+ }
+
+ buf->mBuffers[0].mData = NULL;
+ buf->mBuffers[0].mDataByteSize = inNumberFrames *
+ strm->streamFormat.mChannelsPerFrame;
+ /* Render the unit to get input data */
+ ostatus = AudioUnitRender(strm->io_units[0],
+ ioActionFlags,
+ inTimeStamp,
+ inBusNumber,
+ inNumberFrames,
+ buf);
+
+ if (ostatus != noErr) {
+ PJ_LOG(5, (THIS_FILE, "Core audio unit render error %i", ostatus));
+ goto on_break;
+ }
+ input = (pj_int16_t *)buf->mBuffers[0].mData;
+
+ resampleSize = strm->resample_buf_size;
+ nsamples = inNumberFrames * strm->param.channel_count +
+ strm->resample_buf_count;
+
+ if (nsamples >= resampleSize) {
+ pjmedia_frame frame;
+ UInt32 resampleOutput = strm->param.samples_per_frame /
+ strm->streamFormat.mChannelsPerFrame;
+ AudioBufferList ab;
+
+ frame.type = PJMEDIA_FRAME_TYPE_AUDIO;
+ frame.buf = (void*) strm->rec_buf;
+ frame.size = strm->param.samples_per_frame *
+ strm->param.bits_per_sample >> 3;
+ frame.bit_info = 0;
+
+ ab.mNumberBuffers = 1;
+ ab.mBuffers[0].mNumberChannels = strm->streamFormat.mChannelsPerFrame;
+ ab.mBuffers[0].mData = strm->rec_buf;
+ ab.mBuffers[0].mDataByteSize = frame.size;
+
+ /* If buffer is not empty, combine the buffer with the just incoming
+ * samples, then call put_frame.
+ */
+ if (strm->resample_buf_count) {
+ unsigned chunk_count = resampleSize - strm->resample_buf_count;
+ pjmedia_copy_samples(strm->resample_buf + strm->resample_buf_count,
+ input, chunk_count);
+
+ /* Do the resample */
+
+ strm->resample_buf_ptr = strm->resample_buf;
+ ostatus = AudioConverterFillComplexBuffer(strm->resample,
+ resampleProc,
+ strm,
+ &resampleOutput,
+ &ab,
+ NULL);
+ if (ostatus != noErr) {
+ goto on_break;
+ }
+ frame.timestamp.u64 = strm->rec_timestamp.u64;
+
+ status = (*strm->rec_cb)(strm->user_data, &frame);
+
+ input = input + chunk_count;
+ nsamples -= resampleSize;
+ strm->resample_buf_count = 0;
+ strm->rec_timestamp.u64 += strm->param.samples_per_frame /
+ strm->param.channel_count;
+ }
+
+
+ /* Give all frames we have */
+ while (nsamples >= resampleSize && status == 0) {
+ frame.timestamp.u64 = strm->rec_timestamp.u64;
+
+ /* Do the resample */
+ strm->resample_buf_ptr = input;
+ ab.mBuffers[0].mDataByteSize = frame.size;
+ resampleOutput = strm->param.samples_per_frame /
+ strm->streamFormat.mChannelsPerFrame;
+ ostatus = AudioConverterFillComplexBuffer(strm->resample,
+ resampleProc,
+ strm,
+ &resampleOutput,
+ &ab,
+ NULL);
+ if (ostatus != noErr) {
+ goto on_break;
+ }
+
+ status = (*strm->rec_cb)(strm->user_data, &frame);
+
+ input = (pj_int16_t*) input + resampleSize;
+ nsamples -= resampleSize;
+ strm->rec_timestamp.u64 += strm->param.samples_per_frame /
+ strm->param.channel_count;
+ }
+
+ /* Store the remaining samples into the buffer */
+ if (nsamples && status == 0) {
+ strm->resample_buf_count = nsamples;
+ pjmedia_copy_samples(strm->resample_buf, input,
+ nsamples);
+ }
+
+ } else {
+ /* Not enough samples, let's just store them in the buffer */
+ pjmedia_copy_samples(strm->resample_buf + strm->resample_buf_count,
+ input,
+ inNumberFrames * strm->param.channel_count);
+ strm->resample_buf_count += inNumberFrames *
+ strm->param.channel_count;
+ }
+
+ return noErr;
+
+on_break:
+ return -1;
+}
+
+static OSStatus input_callback(void *inRefCon,
+ AudioUnitRenderActionFlags *ioActionFlags,
+ const AudioTimeStamp *inTimeStamp,
+ UInt32 inBusNumber,
+ UInt32 inNumberFrames,
+ AudioBufferList *ioData)
+{
+ struct coreaudio_stream *strm = (struct coreaudio_stream*)inRefCon;
+ OSStatus ostatus;
+ pj_status_t status = 0;
+ unsigned nsamples;
+ AudioBufferList *buf = strm->audio_buf;
+ pj_int16_t *input;
+
+ pj_assert(!strm->quit_flag);
+
+ /* Known cases of callback's thread:
+ * - The thread may be changed in the middle of a session
+ * 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("ca_rec", strm->rec_thread_desc,
+ &strm->rec_thread);
+ strm->rec_thread_initialized = 1;
+ PJ_LOG(5,(THIS_FILE, "Recorder thread started, (%i frames)",
+ inNumberFrames));
+ }
+
+ buf->mBuffers[0].mData = NULL;
+ buf->mBuffers[0].mDataByteSize = inNumberFrames *
+ strm->streamFormat.mChannelsPerFrame;
+ /* Render the unit to get input data */
+ ostatus = AudioUnitRender(strm->io_units[0],
+ ioActionFlags,
+ inTimeStamp,
+ inBusNumber,
+ inNumberFrames,
+ buf);
+
+ if (ostatus != noErr) {
+ PJ_LOG(5, (THIS_FILE, "Core audio unit render error %i", ostatus));
+ goto on_break;
+ }
+ input = (pj_int16_t *)buf->mBuffers[0].mData;
+
+ /* Calculate number of samples we've got */
+ nsamples = inNumberFrames * strm->param.channel_count +
+ strm->rec_buf_count;
+ if (nsamples >= strm->param.samples_per_frame) {
+ pjmedia_frame frame;
+
+ frame.type = PJMEDIA_FRAME_TYPE_AUDIO;
+ frame.size = strm->param.samples_per_frame *
+ strm->param.bits_per_sample >> 3;
+ frame.bit_info = 0;
+
+ /* 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->param.samples_per_frame - strm->rec_buf_count;
+ pjmedia_copy_samples(strm->rec_buf + strm->rec_buf_count,
+ input, chunk_count);
+
+ frame.buf = (void*) strm->rec_buf;
+ frame.timestamp.u64 = strm->rec_timestamp.u64;
+
+ status = (*strm->rec_cb)(strm->user_data, &frame);
+
+ input = input + chunk_count;
+ nsamples -= strm->param.samples_per_frame;
+ strm->rec_buf_count = 0;
+ strm->rec_timestamp.u64 += strm->param.samples_per_frame /
+ strm->param.channel_count;
+ }
+
+ /* Give all frames we have */
+ while (nsamples >= strm->param.samples_per_frame && status == 0) {
+ frame.buf = (void*) input;
+ frame.timestamp.u64 = strm->rec_timestamp.u64;
+
+ status = (*strm->rec_cb)(strm->user_data, &frame);
+
+ input = (pj_int16_t*) input + strm->param.samples_per_frame;
+ nsamples -= strm->param.samples_per_frame;
+ strm->rec_timestamp.u64 += strm->param.samples_per_frame /
+ strm->param.channel_count;
+ }
+
+ /* Store the remaining samples into the buffer */
+ if (nsamples && status == 0) {
+ strm->rec_buf_count = nsamples;
+ pjmedia_copy_samples(strm->rec_buf, input,
+ nsamples);
+ }
+
+ } else {
+ /* Not enough samples, let's just store them in the buffer */
+ pjmedia_copy_samples(strm->rec_buf + strm->rec_buf_count,
+ input,
+ inNumberFrames * strm->param.channel_count);
+ strm->rec_buf_count += inNumberFrames * strm->param.channel_count;
+ }
+
+ return noErr;
+
+on_break:
+ return -1;
+}
+
+static OSStatus output_renderer(void *inRefCon,
+ AudioUnitRenderActionFlags *ioActionFlags,
+ const AudioTimeStamp *inTimeStamp,
+ UInt32 inBusNumber,
+ UInt32 inNumberFrames,
+ AudioBufferList *ioData)
+{
+ struct coreaudio_stream *stream = (struct coreaudio_stream*)inRefCon;
+ pj_status_t status = 0;
+ unsigned nsamples_req = inNumberFrames * stream->param.channel_count;
+ pj_int16_t *output = ioData->mBuffers[0].mData;
+
+ pj_assert(!stream->quit_flag);
+
+ /* Known cases of callback's thread:
+ * - The thread may be changed in the middle of a session
+ * 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 (stream->play_thread_initialized == 0 || !pj_thread_is_registered())
+ {
+ pj_bzero(stream->play_thread_desc, sizeof(pj_thread_desc));
+ status = pj_thread_register("coreaudio", stream->play_thread_desc,
+ &stream->play_thread);
+ stream->play_thread_initialized = 1;
+ PJ_LOG(5,(THIS_FILE, "Player thread started, (%i frames)",
+ inNumberFrames));
+ }
+
+
+ /* Check if any buffered samples */
+ if (stream->play_buf_count) {
+ /* samples buffered >= requested by sound device */
+ if (stream->play_buf_count >= nsamples_req) {
+ pjmedia_copy_samples((pj_int16_t*)output, stream->play_buf,
+ nsamples_req);
+ stream->play_buf_count -= nsamples_req;
+ pjmedia_move_samples(stream->play_buf,
+ stream->play_buf + nsamples_req,
+ stream->play_buf_count);
+ nsamples_req = 0;
+
+ return noErr;
+ }
+
+ /* samples buffered < requested by sound device */
+ pjmedia_copy_samples((pj_int16_t*)output, stream->play_buf,
+ stream->play_buf_count);
+ nsamples_req -= stream->play_buf_count;
+ output = (pj_int16_t*)output + stream->play_buf_count;
+ stream->play_buf_count = 0;
+ }
+
+ /* Fill output buffer as requested */
+ while (nsamples_req && status == 0) {
+ pjmedia_frame frame;
+
+ frame.type = PJMEDIA_FRAME_TYPE_AUDIO;
+ frame.size = stream->param.samples_per_frame *
+ stream->param.bits_per_sample >> 3;
+ frame.timestamp.u64 = stream->play_timestamp.u64;
+ frame.bit_info = 0;
+
+ if (nsamples_req >= stream->param.samples_per_frame) {
+ frame.buf = output;
+ status = (*stream->play_cb)(stream->user_data, &frame);
+ if (status != PJ_SUCCESS)
+ goto on_break;
+
+ if (frame.type != PJMEDIA_FRAME_TYPE_AUDIO)
+ pj_bzero(frame.buf, frame.size);
+
+ nsamples_req -= stream->param.samples_per_frame;
+ output = (pj_int16_t*)output + stream->param.samples_per_frame;
+ } else {
+ frame.buf = stream->play_buf;
+ status = (*stream->play_cb)(stream->user_data, &frame);
+ if (status != PJ_SUCCESS)
+ goto on_break;
+
+ if (frame.type != PJMEDIA_FRAME_TYPE_AUDIO)
+ pj_bzero(frame.buf, frame.size);
+
+ pjmedia_copy_samples((pj_int16_t*)output, stream->play_buf,
+ nsamples_req);
+ stream->play_buf_count = stream->param.samples_per_frame -
+ nsamples_req;
+ pjmedia_move_samples(stream->play_buf,
+ stream->play_buf+nsamples_req,
+ stream->play_buf_count);
+ nsamples_req = 0;
+ }
+
+ stream->play_timestamp.u64 += stream->param.samples_per_frame /
+ stream->param.channel_count;
+ }
+
+ return noErr;
+
+on_break:
+ return -1;
+}
+
+#if !COREAUDIO_MAC
+static void propListener(void *inClientData,
+ AudioSessionPropertyID inID,
+ UInt32 inDataSize,
+ const void * inData)
+{
+ struct coreaudio_factory *cf = (struct coreaudio_factory*)inClientData;
+ struct stream_list *it, *itBegin;
+ CFDictionaryRef routeDictionary;
+ CFNumberRef reason;
+ SInt32 reasonVal;
+ pj_assert(cf);
+
+ if (inID != kAudioSessionProperty_AudioRouteChange)
+ return;
+
+ routeDictionary = (CFDictionaryRef)inData;
+ reason = (CFNumberRef)
+ CFDictionaryGetValue(
+ routeDictionary,
+ CFSTR(kAudioSession_AudioRouteChangeKey_Reason));
+ CFNumberGetValue(reason, kCFNumberSInt32Type, &reasonVal);
+
+ if (reasonVal != kAudioSessionRouteChangeReason_OldDeviceUnavailable) {
+ PJ_LOG(3, (THIS_FILE, "ignoring audio route change..."));
+ return;
+ }
+
+ PJ_LOG(3, (THIS_FILE, "audio route changed"));
+
+ pj_mutex_lock(cf->mutex);
+ itBegin = &cf->streams;
+ for (it = itBegin->next; it != itBegin; it = it->next) {
+ if (it->stream->interrupted)
+ continue;
+
+ /*
+ status = ca_stream_stop((pjmedia_aud_stream *)it->stream);
+ status = ca_stream_start((pjmedia_aud_stream *)it->stream);
+ if (status != PJ_SUCCESS) {
+ PJ_LOG(3, (THIS_FILE,
+ "Error: failed to restart the audio unit (%i)",
+ status));
+ continue;
+ }
+ PJ_LOG(3, (THIS_FILE, "core audio unit successfully restarted"));
+ */
+ }
+ pj_mutex_unlock(cf->mutex);
+}
+
+static void interruptionListener(void *inClientData, UInt32 inInterruption)
+{
+ struct stream_list *it, *itBegin;
+ pj_status_t status;
+ pj_thread_desc thread_desc;
+ pj_thread_t *thread;
+
+ /* Register the thread with PJLIB, this is must for any external threads
+ * which need to use the PJLIB framework.
+ */
+ if (!pj_thread_is_registered()) {
+ pj_bzero(thread_desc, sizeof(pj_thread_desc));
+ status = pj_thread_register("intListener", thread_desc, &thread);
+ }
+
+ PJ_LOG(3, (THIS_FILE, "Session interrupted! --- %s ---",
+ inInterruption == kAudioSessionBeginInterruption ?
+ "Begin Interruption" : "End Interruption"));
+
+ if (!cf_instance)
+ return;
+
+ pj_mutex_lock(cf_instance->mutex);
+ itBegin = &cf_instance->streams;
+ for (it = itBegin->next; it != itBegin; it = it->next) {
+ if (inInterruption == kAudioSessionEndInterruption &&
+ it->stream->interrupted == PJ_TRUE)
+ {
+ UInt32 audioCategory;
+ OSStatus ostatus;
+
+ /* Make sure that your application can receive remote control
+ * events by adding the code:
+ * [[UIApplication sharedApplication]
+ * beginReceivingRemoteControlEvents];
+ * Otherwise audio unit will fail to restart while your
+ * application is in the background mode.
+ */
+ /* Make sure we set the correct audio category before restarting */
+ audioCategory = kAudioSessionCategory_PlayAndRecord;
+ ostatus = AudioSessionSetProperty(kAudioSessionProperty_AudioCategory,
+ sizeof(audioCategory),
+ &audioCategory);
+ if (ostatus != kAudioSessionNoError) {
+ PJ_LOG(4, (THIS_FILE,
+ "Warning: cannot set the audio session category (%i)",
+ ostatus));
+ }
+
+ /* Restart the stream */
+ status = ca_stream_start((pjmedia_aud_stream*)it->stream);
+ if (status != PJ_SUCCESS) {
+ PJ_LOG(3, (THIS_FILE,
+ "Error: failed to restart the audio unit (%i)",
+ status));
+ continue;
+ }
+ PJ_LOG(3, (THIS_FILE, "core audio unit successfully resumed"
+ " after interruption"));
+ } else if (inInterruption == kAudioSessionBeginInterruption &&
+ it->stream->running == PJ_TRUE)
+ {
+ status = ca_stream_stop((pjmedia_aud_stream*)it->stream);
+ it->stream->interrupted = PJ_TRUE;
+ }
+ }
+ pj_mutex_unlock(cf_instance->mutex);
+}
+
+#endif
+
+#if COREAUDIO_MAC
+/* Internal: create audio converter for resampling the recorder device */
+static pj_status_t create_audio_resample(struct coreaudio_stream *strm,
+ AudioStreamBasicDescription *desc)
+{
+ OSStatus ostatus;
+
+ pj_assert(strm->streamFormat.mSampleRate != desc->mSampleRate);
+ pj_assert(NULL == strm->resample);
+ pj_assert(NULL == strm->resample_buf);
+
+ /* Create the audio converter */
+ ostatus = AudioConverterNew(desc, &strm->streamFormat, &strm->resample);
+ if (ostatus != noErr) {
+ return PJMEDIA_AUDIODEV_ERRNO_FROM_COREAUDIO(ostatus);
+ }
+
+ /*
+ * Allocate the buffer required to hold enough input data
+ */
+ strm->resample_buf_size = (unsigned)(desc->mSampleRate *
+ strm->param.samples_per_frame /
+ strm->param.clock_rate);
+ strm->resample_buf = (pj_int16_t*)
+ pj_pool_alloc(strm->pool,
+ strm->resample_buf_size *
+ strm->param.bits_per_sample >> 3);
+ if (!strm->resample_buf)
+ return PJ_ENOMEM;
+ strm->resample_buf_count = 0;
+
+ return PJ_SUCCESS;
+}
+#endif
+
+/* Internal: create audio unit for recorder/playback device */
+static pj_status_t create_audio_unit(AudioComponent io_comp,
+ AudioDeviceID dev_id,
+ pjmedia_dir dir,
+ struct coreaudio_stream *strm,
+ AudioUnit *io_unit)
+{
+ OSStatus ostatus;
+#if !COREAUDIO_MAC
+ UInt32 audioCategory = kAudioSessionCategory_PlayAndRecord;
+ /* We want to be able to open playback and recording streams */
+ ostatus = AudioSessionSetProperty(kAudioSessionProperty_AudioCategory,
+ sizeof(audioCategory),
+ &audioCategory);
+ if (ostatus != kAudioSessionNoError) {
+ PJ_LOG(4, (THIS_FILE,
+ "Warning: cannot set the audio session category (%i)",
+ ostatus));
+ }
+#endif
+
+ /* Create an audio unit to interface with the device */
+ ostatus = AudioComponentInstanceNew(io_comp, io_unit);
+ if (ostatus != noErr) {
+ return PJMEDIA_AUDIODEV_ERRNO_FROM_COREAUDIO(ostatus);
+ }
+
+ /* Set audio unit's properties for capture device */
+ if (dir & PJMEDIA_DIR_CAPTURE) {
+ UInt32 enable = 1;
+
+ /* Enable input */
+ ostatus = AudioUnitSetProperty(*io_unit,
+ kAudioOutputUnitProperty_EnableIO,
+ kAudioUnitScope_Input,
+ 1,
+ &enable,
+ sizeof(enable));
+ if (ostatus != noErr) {
+ PJ_LOG(4, (THIS_FILE,
+ "Warning: cannot enable IO of capture device %d",
+ dev_id));
+ }
+
+ /* Disable output */
+ if (!(dir & PJMEDIA_DIR_PLAYBACK)) {
+ enable = 0;
+ ostatus = AudioUnitSetProperty(*io_unit,
+ kAudioOutputUnitProperty_EnableIO,
+ kAudioUnitScope_Output,
+ 0,
+ &enable,
+ sizeof(enable));
+ if (ostatus != noErr) {
+ PJ_LOG(4, (THIS_FILE,
+ "Warning: cannot disable IO of capture device %d",
+ dev_id));
+ }
+ }
+ }
+
+ /* Set audio unit's properties for playback device */
+ if (dir & PJMEDIA_DIR_PLAYBACK) {
+ UInt32 enable = 1;
+
+ /* Enable output */
+ ostatus = AudioUnitSetProperty(*io_unit,
+ kAudioOutputUnitProperty_EnableIO,
+ kAudioUnitScope_Output,
+ 0,
+ &enable,
+ sizeof(enable));
+ if (ostatus != noErr) {
+ PJ_LOG(4, (THIS_FILE,
+ "Warning: cannot enable IO of playback device %d",
+ dev_id));
+ }
+
+ }
+
+#if COREAUDIO_MAC
+ PJ_LOG(5, (THIS_FILE, "Opening device %d", dev_id));
+ ostatus = AudioUnitSetProperty(*io_unit,
+ kAudioOutputUnitProperty_CurrentDevice,
+ kAudioUnitScope_Global,
+ 0,
+ &dev_id,
+ sizeof(dev_id));
+ if (ostatus != noErr) {
+ return PJMEDIA_AUDIODEV_ERRNO_FROM_COREAUDIO(ostatus);
+ }
+#endif
+
+ if (dir & PJMEDIA_DIR_CAPTURE) {
+#if COREAUDIO_MAC
+ AudioStreamBasicDescription deviceFormat;
+ UInt32 size;
+
+ /*
+ * Keep the sample rate from the device, otherwise we will confuse
+ * AUHAL
+ */
+ size = sizeof(AudioStreamBasicDescription);
+ ostatus = AudioUnitGetProperty(*io_unit,
+ kAudioUnitProperty_StreamFormat,
+ kAudioUnitScope_Input,
+ 1,
+ &deviceFormat,
+ &size);
+ if (ostatus != noErr) {
+ return PJMEDIA_AUDIODEV_ERRNO_FROM_COREAUDIO(ostatus);
+ }
+ strm->streamFormat.mSampleRate = deviceFormat.mSampleRate;
+#endif
+
+ /* When setting the stream format, we have to make sure the sample
+ * rate is supported. Setting an unsupported sample rate will cause
+ * AudioUnitRender() to fail later.
+ */
+ ostatus = AudioUnitSetProperty(*io_unit,
+ kAudioUnitProperty_StreamFormat,
+ kAudioUnitScope_Output,
+ 1,
+ &strm->streamFormat,
+ sizeof(strm->streamFormat));
+ if (ostatus != noErr) {
+ return PJMEDIA_AUDIODEV_ERRNO_FROM_COREAUDIO(ostatus);
+ }
+
+#if COREAUDIO_MAC
+ strm->streamFormat.mSampleRate = strm->param.clock_rate;
+ size = sizeof(AudioStreamBasicDescription);
+ ostatus = AudioUnitGetProperty (*io_unit,
+ kAudioUnitProperty_StreamFormat,
+ kAudioUnitScope_Output,
+ 1,
+ &deviceFormat,
+ &size);
+ if (ostatus == noErr) {
+ if (strm->streamFormat.mSampleRate != deviceFormat.mSampleRate) {
+ pj_status_t rc = create_audio_resample(strm, &deviceFormat);
+ if (PJ_SUCCESS != rc)
+ return rc;
+ }
+ } else {
+ return PJMEDIA_AUDIODEV_ERRNO_FROM_COREAUDIO(ostatus);
+ }
+#endif
+ }
+
+ if (dir & PJMEDIA_DIR_PLAYBACK) {
+ AURenderCallbackStruct output_cb;
+
+ /* Set the stream format */
+ ostatus = AudioUnitSetProperty(*io_unit,
+ kAudioUnitProperty_StreamFormat,
+ kAudioUnitScope_Input,
+ 0,
+ &strm->streamFormat,
+ sizeof(strm->streamFormat));
+ if (ostatus != noErr) {
+ PJ_LOG(4, (THIS_FILE,
+ "Warning: cannot set playback stream format of dev %d",
+ dev_id));
+ }
+
+ /* Set render callback */
+ output_cb.inputProc = output_renderer;
+ output_cb.inputProcRefCon = strm;
+ ostatus = AudioUnitSetProperty(*io_unit,
+ kAudioUnitProperty_SetRenderCallback,
+ kAudioUnitScope_Input,
+ 0,
+ &output_cb,
+ sizeof(output_cb));
+ if (ostatus != noErr) {
+ return PJMEDIA_AUDIODEV_ERRNO_FROM_COREAUDIO(ostatus);
+ }
+
+ /* Allocate playback buffer */
+ strm->play_buf = (pj_int16_t*)pj_pool_alloc(strm->pool,
+ strm->param.samples_per_frame *
+ strm->param.bits_per_sample >> 3);
+ if (!strm->play_buf)
+ return PJ_ENOMEM;
+ strm->play_buf_count = 0;
+ }
+
+ if (dir & PJMEDIA_DIR_CAPTURE) {
+ AURenderCallbackStruct input_cb;
+#if COREAUDIO_MAC
+ AudioBuffer *ab;
+ UInt32 size, buf_size;
+#endif
+
+ /* Set input callback */
+ input_cb.inputProc = strm->resample ? resample_callback :
+ input_callback;
+ input_cb.inputProcRefCon = strm;
+ ostatus = AudioUnitSetProperty(
+ *io_unit,
+ kAudioOutputUnitProperty_SetInputCallback,
+ kAudioUnitScope_Global,
+ 0,
+ &input_cb,
+ sizeof(input_cb));
+ if (ostatus != noErr) {
+ return PJMEDIA_AUDIODEV_ERRNO_FROM_COREAUDIO(ostatus);
+ }
+
+#if COREAUDIO_MAC
+ /* Get device's buffer frame size */
+ size = sizeof(UInt32);
+ ostatus = AudioUnitGetProperty(*io_unit,
+ kAudioDevicePropertyBufferFrameSize,
+ kAudioUnitScope_Global,
+ 0,
+ &buf_size,
+ &size);
+ if (ostatus != noErr)
+ {
+ return PJMEDIA_AUDIODEV_ERRNO_FROM_COREAUDIO(ostatus);
+ }
+
+ /* Allocate audio buffer */
+ strm->audio_buf = (AudioBufferList*)pj_pool_alloc(strm->pool,
+ sizeof(AudioBufferList) + sizeof(AudioBuffer));
+ if (!strm->audio_buf)
+ return PJ_ENOMEM;
+
+ strm->audio_buf->mNumberBuffers = 1;
+ ab = &strm->audio_buf->mBuffers[0];
+ ab->mNumberChannels = strm->streamFormat.mChannelsPerFrame;
+ ab->mDataByteSize = buf_size * ab->mNumberChannels *
+ strm->param.bits_per_sample >> 3;
+ ab->mData = pj_pool_alloc(strm->pool,
+ ab->mDataByteSize);
+ if (!ab->mData)
+ return PJ_ENOMEM;
+
+#else
+ /* We will let AudioUnitRender() to allocate the buffer
+ * for us later
+ */
+ strm->audio_buf = (AudioBufferList*)pj_pool_alloc(strm->pool,
+ sizeof(AudioBufferList) + sizeof(AudioBuffer));
+ if (!strm->audio_buf)
+ return PJ_ENOMEM;
+
+ strm->audio_buf->mNumberBuffers = 1;
+ strm->audio_buf->mBuffers[0].mNumberChannels =
+ strm->streamFormat.mChannelsPerFrame;
+
+#endif
+
+ /* Allocate recording buffer */
+ strm->rec_buf = (pj_int16_t*)pj_pool_alloc(strm->pool,
+ strm->param.samples_per_frame *
+ strm->param.bits_per_sample >> 3);
+ if (!strm->rec_buf)
+ return PJ_ENOMEM;
+ strm->rec_buf_count = 0;
+ }
+
+ /* Initialize the audio unit */
+ ostatus = AudioUnitInitialize(*io_unit);
+ if (ostatus != noErr) {
+ return PJMEDIA_AUDIODEV_ERRNO_FROM_COREAUDIO(ostatus);
+ }
+
+ return PJ_SUCCESS;
+}
+
+/* API: create stream */
+static pj_status_t ca_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 coreaudio_factory *cf = (struct coreaudio_factory*)f;
+ pj_pool_t *pool;
+ struct coreaudio_stream *strm;
+ pj_status_t status;
+
+ /* Create and Initialize stream descriptor */
+ pool = pj_pool_create(cf->pf, "coreaudio-dev", 1000, 1000, NULL);
+ PJ_ASSERT_RETURN(pool != NULL, PJ_ENOMEM);
+
+ strm = PJ_POOL_ZALLOC_T(pool, struct coreaudio_stream);
+ pj_list_init(&strm->list_entry);
+ strm->list_entry.stream = strm;
+ strm->cf = cf;
+ 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;
+
+ /* Set the stream format */
+ strm->streamFormat.mSampleRate = param->clock_rate;
+ strm->streamFormat.mFormatID = kAudioFormatLinearPCM;
+ strm->streamFormat.mFormatFlags = kLinearPCMFormatFlagIsSignedInteger
+ | kLinearPCMFormatFlagIsPacked;
+ strm->streamFormat.mBitsPerChannel = strm->param.bits_per_sample;
+ strm->streamFormat.mChannelsPerFrame = param->channel_count;
+ strm->streamFormat.mBytesPerFrame = strm->streamFormat.mChannelsPerFrame
+ * strm->param.bits_per_sample >> 3;
+ strm->streamFormat.mFramesPerPacket = 1;
+ strm->streamFormat.mBytesPerPacket = strm->streamFormat.mBytesPerFrame *
+ strm->streamFormat.mFramesPerPacket;
+
+ /* Apply input/output routes settings before we create the audio units */
+ if (param->flags & PJMEDIA_AUD_DEV_CAP_INPUT_ROUTE) {
+ ca_stream_set_cap(&strm->base,
+ PJMEDIA_AUD_DEV_CAP_INPUT_ROUTE,
+ ¶m->input_route);
+ }
+ if (param->flags & PJMEDIA_AUD_DEV_CAP_OUTPUT_ROUTE) {
+ ca_stream_set_cap(&strm->base,
+ PJMEDIA_AUD_DEV_CAP_OUTPUT_ROUTE,
+ ¶m->output_route);
+ }
+ if (param->flags & PJMEDIA_AUD_DEV_CAP_EC) {
+ ca_stream_set_cap(&strm->base,
+ PJMEDIA_AUD_DEV_CAP_EC,
+ ¶m->ec_enabled);
+ } else {
+ pj_bool_t ec = PJ_FALSE;
+ ca_stream_set_cap(&strm->base,
+ PJMEDIA_AUD_DEV_CAP_EC, &ec);
+ }
+
+ strm->io_units[0] = strm->io_units[1] = NULL;
+ if (param->dir == PJMEDIA_DIR_CAPTURE_PLAYBACK &&
+ param->rec_id == param->play_id)
+ {
+ /* If both input and output are on the same device, only create
+ * one audio unit to interface with the device.
+ */
+ status = create_audio_unit(cf->io_comp,
+ cf->dev_info[param->rec_id].dev_id,
+ param->dir, strm, &strm->io_units[0]);
+ if (status != PJ_SUCCESS)
+ goto on_error;
+ } else {
+ unsigned nunits = 0;
+
+ if (param->dir & PJMEDIA_DIR_CAPTURE) {
+ status = create_audio_unit(cf->io_comp,
+ cf->dev_info[param->rec_id].dev_id,
+ PJMEDIA_DIR_CAPTURE,
+ strm, &strm->io_units[nunits++]);
+ if (status != PJ_SUCCESS)
+ goto on_error;
+ }
+ if (param->dir & PJMEDIA_DIR_PLAYBACK) {
+
+ status = create_audio_unit(cf->io_comp,
+ cf->dev_info[param->play_id].dev_id,
+ PJMEDIA_DIR_PLAYBACK,
+ strm, &strm->io_units[nunits++]);
+ if (status != PJ_SUCCESS)
+ goto on_error;
+ }
+ }
+
+ /* Apply the remaining settings */
+ if (param->flags & PJMEDIA_AUD_DEV_CAP_INPUT_LATENCY) {
+ ca_stream_get_cap(&strm->base,
+ PJMEDIA_AUD_DEV_CAP_INPUT_LATENCY,
+ &strm->param.input_latency_ms);
+ }
+ if (param->flags & PJMEDIA_AUD_DEV_CAP_OUTPUT_LATENCY) {
+ ca_stream_get_cap(&strm->base,
+ PJMEDIA_AUD_DEV_CAP_OUTPUT_LATENCY,
+ &strm->param.output_latency_ms);
+ }
+ if (param->flags & PJMEDIA_AUD_DEV_CAP_OUTPUT_VOLUME_SETTING) {
+ ca_stream_set_cap(&strm->base,
+ PJMEDIA_AUD_DEV_CAP_OUTPUT_VOLUME_SETTING,
+ ¶m->output_vol);
+ }
+
+ pj_mutex_lock(strm->cf->mutex);
+ pj_assert(pj_list_empty(&strm->list_entry));
+ pj_list_insert_after(&strm->cf->streams, &strm->list_entry);
+ pj_mutex_unlock(strm->cf->mutex);
+
+ /* Done */
+ strm->base.op = &stream_op;
+ *p_aud_strm = &strm->base;
+
+ return PJ_SUCCESS;
+
+ on_error:
+ ca_stream_destroy((pjmedia_aud_stream *)strm);
+ return status;
+}
+
+/* API: Get stream info. */
+static pj_status_t ca_stream_get_param(pjmedia_aud_stream *s,
+ pjmedia_aud_param *pi)
+{
+ struct coreaudio_stream *strm = (struct coreaudio_stream*)s;
+
+ PJ_ASSERT_RETURN(strm && pi, PJ_EINVAL);
+
+ pj_memcpy(pi, &strm->param, sizeof(*pi));
+
+ /* Update the device capabilities' values */
+ if (ca_stream_get_cap(s, PJMEDIA_AUD_DEV_CAP_INPUT_LATENCY,
+ &pi->input_latency_ms) == PJ_SUCCESS)
+ {
+ pi->flags |= PJMEDIA_AUD_DEV_CAP_INPUT_LATENCY;
+ }
+ if (ca_stream_get_cap(s, PJMEDIA_AUD_DEV_CAP_OUTPUT_LATENCY,
+ &pi->output_latency_ms) == PJ_SUCCESS)
+ {
+ pi->flags |= PJMEDIA_AUD_DEV_CAP_OUTPUT_LATENCY;
+ }
+ if (ca_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;
+ }
+ if (ca_stream_get_cap(s, PJMEDIA_AUD_DEV_CAP_INPUT_ROUTE,
+ &pi->input_route) == PJ_SUCCESS)
+ {
+ pi->flags |= PJMEDIA_AUD_DEV_CAP_INPUT_ROUTE;
+ }
+ if (ca_stream_get_cap(s, PJMEDIA_AUD_DEV_CAP_OUTPUT_ROUTE,
+ &pi->output_route) == PJ_SUCCESS)
+ {
+ pi->flags |= PJMEDIA_AUD_DEV_CAP_OUTPUT_ROUTE;
+ }
+ if (ca_stream_get_cap(s, PJMEDIA_AUD_DEV_CAP_EC,
+ &pi->ec_enabled) == PJ_SUCCESS)
+ {
+ pi->flags |= PJMEDIA_AUD_DEV_CAP_EC;
+ }
+
+ return PJ_SUCCESS;
+}
+
+/* API: get capability */
+static pj_status_t ca_stream_get_cap(pjmedia_aud_stream *s,
+ pjmedia_aud_dev_cap cap,
+ void *pval)
+{
+ struct coreaudio_stream *strm = (struct coreaudio_stream*)s;
+
+ PJ_UNUSED_ARG(strm);
+
+ PJ_ASSERT_RETURN(s && pval, PJ_EINVAL);
+
+ if (cap==PJMEDIA_AUD_DEV_CAP_INPUT_LATENCY &&
+ (strm->param.dir & PJMEDIA_DIR_CAPTURE))
+ {
+#if COREAUDIO_MAC
+ UInt32 latency, size = sizeof(UInt32);
+
+ /* Recording latency */
+ if (AudioUnitGetProperty (strm->io_units[0],
+ kAudioDevicePropertyLatency,
+ kAudioUnitScope_Input,
+ 1,
+ &latency,
+ &size) == noErr)
+ {
+ UInt32 latency2;
+ if (AudioUnitGetProperty (strm->io_units[0],
+ kAudioDevicePropertyBufferFrameSize,
+ kAudioUnitScope_Input,
+ 1,
+ &latency2,
+ &size) == noErr)
+ {
+ strm->param.input_latency_ms = (latency + latency2) * 1000 /
+ strm->param.clock_rate;
+ strm->param.input_latency_ms++;
+ }
+ }
+#else
+ Float32 latency, latency2;
+ UInt32 size = sizeof(Float32);
+
+ if ((AudioSessionGetProperty(
+ kAudioSessionProperty_CurrentHardwareInputLatency,
+ &size, &latency) == kAudioSessionNoError) &&
+ (AudioSessionGetProperty(
+ kAudioSessionProperty_CurrentHardwareIOBufferDuration,
+ &size, &latency2) == kAudioSessionNoError))
+ {
+ strm->param.input_latency_ms = (unsigned)
+ ((latency + latency2) * 1000);
+ strm->param.input_latency_ms++;
+ }
+#endif
+
+ *(unsigned*)pval = strm->param.input_latency_ms;
+ return PJ_SUCCESS;
+ } else if (cap==PJMEDIA_AUD_DEV_CAP_OUTPUT_LATENCY &&
+ (strm->param.dir & PJMEDIA_DIR_PLAYBACK))
+ {
+#if COREAUDIO_MAC
+ UInt32 latency, size = sizeof(UInt32);
+ AudioUnit *io_unit = strm->io_units[1] ? &strm->io_units[1] :
+ &strm->io_units[0];
+
+ /* Playback latency */
+ if (AudioUnitGetProperty (*io_unit,
+ kAudioDevicePropertyLatency,
+ kAudioUnitScope_Output,
+ 0,
+ &latency,
+ &size) == noErr)
+ {
+ UInt32 latency2;
+ if (AudioUnitGetProperty (*io_unit,
+ kAudioDevicePropertyBufferFrameSize,
+ kAudioUnitScope_Output,
+ 0,
+ &latency2,
+ &size) == noErr)
+ {
+ strm->param.output_latency_ms = (latency + latency2) * 1000 /
+ strm->param.clock_rate;
+ strm->param.output_latency_ms++;
+ }
+ }
+#else
+ Float32 latency, latency2;
+ UInt32 size = sizeof(Float32);
+
+ if ((AudioSessionGetProperty(
+ kAudioSessionProperty_CurrentHardwareOutputLatency,
+ &size, &latency) == kAudioSessionNoError) &&
+ (AudioSessionGetProperty(
+ kAudioSessionProperty_CurrentHardwareIOBufferDuration,
+ &size, &latency2) == kAudioSessionNoError))
+ {
+ strm->param.output_latency_ms = (unsigned)
+ ((latency + latency2) * 1000);
+ strm->param.output_latency_ms++;
+ }
+#endif
+ *(unsigned*)pval = (++strm->param.output_latency_ms * 2);
+ return PJ_SUCCESS;
+ } else if (cap==PJMEDIA_AUD_DEV_CAP_OUTPUT_VOLUME_SETTING &&
+ (strm->param.dir & PJMEDIA_DIR_PLAYBACK))
+ {
+ OSStatus ostatus;
+ Float32 volume;
+ UInt32 size = sizeof(Float32);
+
+ /* Output volume setting */
+#if COREAUDIO_MAC
+ ostatus = AudioUnitGetProperty (strm->io_units[1] ? strm->io_units[1] :
+ strm->io_units[0],
+ kAudioDevicePropertyVolumeScalar,
+ kAudioUnitScope_Output,
+ 0,
+ &volume,
+ &size);
+ if (ostatus != noErr)
+ return PJMEDIA_AUDIODEV_ERRNO_FROM_COREAUDIO(ostatus);
+#else
+ ostatus = AudioSessionGetProperty(
+ kAudioSessionProperty_CurrentHardwareOutputVolume,
+ &size, &volume);
+ if (ostatus != kAudioSessionNoError) {
+ return PJMEDIA_AUDIODEV_ERRNO_FROM_COREAUDIO(ostatus);
+ }
+#endif
+
+ *(unsigned*)pval = (unsigned)(volume * 100);
+ return PJ_SUCCESS;
+#if !COREAUDIO_MAC
+ } else if (cap==PJMEDIA_AUD_DEV_CAP_INPUT_ROUTE &&
+ (strm->param.dir & PJMEDIA_DIR_CAPTURE))
+ {
+ UInt32 btooth, size = sizeof(UInt32);
+ OSStatus ostatus;
+
+ ostatus = AudioSessionGetProperty (
+ kAudioSessionProperty_OverrideCategoryEnableBluetoothInput,
+ &size, &btooth);
+ if (ostatus != kAudioSessionNoError) {
+ return PJMEDIA_AUDIODEV_ERRNO_FROM_COREAUDIO(ostatus);
+ }
+
+ *(pjmedia_aud_dev_route*)pval = btooth?
+ PJMEDIA_AUD_DEV_ROUTE_BLUETOOTH:
+ PJMEDIA_AUD_DEV_ROUTE_DEFAULT;
+ return PJ_SUCCESS;
+ } else if (cap==PJMEDIA_AUD_DEV_CAP_OUTPUT_ROUTE &&
+ (strm->param.dir & PJMEDIA_DIR_PLAYBACK))
+ {
+ CFStringRef route;
+ UInt32 size = sizeof(CFStringRef);
+ OSStatus ostatus;
+
+ ostatus = AudioSessionGetProperty (kAudioSessionProperty_AudioRoute,
+ &size, &route);
+ if (ostatus != kAudioSessionNoError) {
+ return PJMEDIA_AUDIODEV_ERRNO_FROM_COREAUDIO(ostatus);
+ }
+
+ if (!route) {
+ *(pjmedia_aud_dev_route*)pval = PJMEDIA_AUD_DEV_ROUTE_DEFAULT;
+ } else if (CFStringHasPrefix(route, CFSTR("Headset"))) {
+ *(pjmedia_aud_dev_route*)pval = PJMEDIA_AUD_DEV_ROUTE_EARPIECE;
+ } else {
+ *(pjmedia_aud_dev_route*)pval = PJMEDIA_AUD_DEV_ROUTE_DEFAULT;
+ }
+
+ CFRelease(route);
+
+ return PJ_SUCCESS;
+ } else if (cap==PJMEDIA_AUD_DEV_CAP_EC) {
+ AudioComponentDescription desc;
+ OSStatus ostatus;
+
+ ostatus = AudioComponentGetDescription(strm->cf->io_comp, &desc);
+ if (ostatus != noErr) {
+ return PJMEDIA_AUDIODEV_ERRNO_FROM_COREAUDIO(ostatus);
+ }
+
+ *(pj_bool_t*)pval = (desc.componentSubType ==
+ kAudioUnitSubType_VoiceProcessingIO);
+ return PJ_SUCCESS;
+#endif
+ } else {
+ return PJMEDIA_EAUD_INVCAP;
+ }
+}
+
+/* API: set capability */
+static pj_status_t ca_stream_set_cap(pjmedia_aud_stream *s,
+ pjmedia_aud_dev_cap cap,
+ const void *pval)
+{
+ struct coreaudio_stream *strm = (struct coreaudio_stream*)s;
+
+ PJ_ASSERT_RETURN(s && pval, PJ_EINVAL);
+
+#if COREAUDIO_MAC
+ if (cap==PJMEDIA_AUD_DEV_CAP_OUTPUT_VOLUME_SETTING &&
+ (strm->param.dir & PJMEDIA_DIR_PLAYBACK))
+ {
+ OSStatus ostatus;
+ Float32 volume = *(unsigned*)pval;
+
+ /* Output volume setting */
+ volume /= 100.0;
+ ostatus = AudioUnitSetProperty (strm->io_units[1] ? strm->io_units[1] :
+ strm->io_units[0],
+ kAudioDevicePropertyVolumeScalar,
+ kAudioUnitScope_Output,
+ 0,
+ &volume,
+ sizeof(Float32));
+ if (ostatus != noErr) {
+ return PJMEDIA_AUDIODEV_ERRNO_FROM_COREAUDIO(ostatus);
+ }
+ strm->param.output_vol = *(unsigned*)pval;
+ return PJ_SUCCESS;
+ }
+
+#else
+
+ if ((cap==PJMEDIA_AUD_DEV_CAP_INPUT_LATENCY &&
+ (strm->param.dir & PJMEDIA_DIR_CAPTURE)) ||
+ (cap==PJMEDIA_AUD_DEV_CAP_OUTPUT_LATENCY &&
+ (strm->param.dir & PJMEDIA_DIR_PLAYBACK)))
+ {
+ Float32 bufferDuration = *(unsigned *)pval;
+ OSStatus ostatus;
+ unsigned latency;
+
+ /* For low-latency audio streaming, you can set this value to
+ * as low as 5 ms (the default is 23ms). However, lowering the
+ * latency may cause a decrease in audio quality.
+ */
+ bufferDuration /= 1000;
+ ostatus = AudioSessionSetProperty(
+ kAudioSessionProperty_PreferredHardwareIOBufferDuration,
+ sizeof(bufferDuration), &bufferDuration);
+ if (ostatus != kAudioSessionNoError) {
+ PJ_LOG(4, (THIS_FILE,
+ "Error: cannot set the preferred buffer duration (%i)",
+ ostatus));
+ return PJMEDIA_AUDIODEV_ERRNO_FROM_COREAUDIO(ostatus);
+ }
+
+ ca_stream_get_cap(s, PJMEDIA_AUD_DEV_CAP_INPUT_LATENCY, &latency);
+ ca_stream_get_cap(s, PJMEDIA_AUD_DEV_CAP_OUTPUT_LATENCY, &latency);
+
+ return PJ_SUCCESS;
+ } else if (cap==PJMEDIA_AUD_DEV_CAP_INPUT_ROUTE &&
+ (strm->param.dir & PJMEDIA_DIR_CAPTURE))
+ {
+ UInt32 btooth = *(pjmedia_aud_dev_route*)pval ==
+ PJMEDIA_AUD_DEV_ROUTE_BLUETOOTH ? 1 : 0;
+ OSStatus ostatus;
+
+ ostatus = AudioSessionSetProperty (
+ kAudioSessionProperty_OverrideCategoryEnableBluetoothInput,
+ sizeof(btooth), &btooth);
+ if (ostatus != kAudioSessionNoError) {
+ return PJMEDIA_AUDIODEV_ERRNO_FROM_COREAUDIO(ostatus);
+ }
+ strm->param.input_route = *(pjmedia_aud_dev_route*)pval;
+ return PJ_SUCCESS;
+ } else if (cap==PJMEDIA_AUD_DEV_CAP_OUTPUT_ROUTE &&
+ (strm->param.dir & PJMEDIA_DIR_PLAYBACK))
+ {
+ OSStatus ostatus;
+ UInt32 route = *(pjmedia_aud_dev_route*)pval ==
+ PJMEDIA_AUD_DEV_ROUTE_LOUDSPEAKER ?
+ kAudioSessionOverrideAudioRoute_Speaker :
+ kAudioSessionOverrideAudioRoute_None;
+
+ ostatus = AudioSessionSetProperty (
+ kAudioSessionProperty_OverrideAudioRoute,
+ sizeof(route), &route);
+ if (ostatus != kAudioSessionNoError) {
+ return PJMEDIA_AUDIODEV_ERRNO_FROM_COREAUDIO(ostatus);
+ }
+ strm->param.output_route = *(pjmedia_aud_dev_route*)pval;
+ return PJ_SUCCESS;
+ } else if (cap==PJMEDIA_AUD_DEV_CAP_EC) {
+ AudioComponentDescription desc;
+ AudioComponent io_comp;
+
+ desc.componentType = kAudioUnitType_Output;
+ desc.componentSubType = (*(pj_bool_t*)pval)?
+ kAudioUnitSubType_VoiceProcessingIO :
+ kAudioUnitSubType_RemoteIO;
+ desc.componentManufacturer = kAudioUnitManufacturer_Apple;
+ desc.componentFlags = 0;
+ desc.componentFlagsMask = 0;
+
+ io_comp = AudioComponentFindNext(NULL, &desc);
+ if (io_comp == NULL)
+ return PJMEDIA_AUDIODEV_ERRNO_FROM_COREAUDIO(-1);
+ strm->cf->io_comp = io_comp;
+ strm->param.ec_enabled = *(pj_bool_t*)pval;
+
+ PJ_LOG(4, (THIS_FILE, "Using %s audio unit",
+ (desc.componentSubType ==
+ kAudioUnitSubType_RemoteIO? "RemoteIO":
+ "VoiceProcessingIO")));
+
+ return PJ_SUCCESS;
+ }
+#endif
+
+ return PJMEDIA_EAUD_INVCAP;
+}
+
+/* API: Start stream. */
+static pj_status_t ca_stream_start(pjmedia_aud_stream *strm)
+{
+ struct coreaudio_stream *stream = (struct coreaudio_stream*)strm;
+ OSStatus ostatus;
+ UInt32 i;
+
+ if (stream->running)
+ return PJ_SUCCESS;
+
+ stream->quit_flag = 0;
+ stream->interrupted = PJ_FALSE;
+ stream->rec_buf_count = 0;
+ stream->play_buf_count = 0;
+ stream->resample_buf_count = 0;
+
+ if (stream->resample) {
+ ostatus = AudioConverterReset(stream->resample);
+ if (ostatus != noErr)
+ return PJMEDIA_AUDIODEV_ERRNO_FROM_COREAUDIO(ostatus);
+ }
+
+#if !COREAUDIO_MAC
+ AudioSessionSetActive(true);
+#endif
+
+ for (i = 0; i < 2; i++) {
+ if (stream->io_units[i] == NULL) break;
+ ostatus = AudioOutputUnitStart(stream->io_units[i]);
+ if (ostatus != noErr) {
+ if (i == 1)
+ AudioOutputUnitStop(stream->io_units[0]);
+ return PJMEDIA_AUDIODEV_ERRNO_FROM_COREAUDIO(ostatus);
+ }
+ }
+
+ stream->running = PJ_TRUE;
+
+ PJ_LOG(4, (THIS_FILE, "core audio stream started"));
+
+ return PJ_SUCCESS;
+}
+
+/* API: Stop stream. */
+static pj_status_t ca_stream_stop(pjmedia_aud_stream *strm)
+{
+ struct coreaudio_stream *stream = (struct coreaudio_stream*)strm;
+ OSStatus ostatus;
+ unsigned i;
+ int should_deactivate;
+ struct stream_list *it, *itBegin;
+
+ if (!stream->running)
+ return PJ_SUCCESS;
+
+ for (i = 0; i < 2; i++) {
+ if (stream->io_units[i] == NULL) break;
+ ostatus = AudioOutputUnitStop(stream->io_units[i]);
+ if (ostatus != noErr) {
+ if (i == 0 && stream->io_units[1])
+ AudioOutputUnitStop(stream->io_units[1]);
+ return PJMEDIA_AUDIODEV_ERRNO_FROM_COREAUDIO(ostatus);
+ }
+ }
+
+ /* Check whether we need to deactivate the audio session. */
+ pj_mutex_lock(stream->cf->mutex);
+ pj_assert(!pj_list_empty(&stream->cf->streams));
+ pj_assert(!pj_list_empty(&stream->list_entry));
+ stream->running = PJ_FALSE;
+ should_deactivate = PJ_TRUE;
+ itBegin = &stream->cf->streams;
+ for (it = itBegin->next; it != itBegin; it = it->next) {
+ if (it->stream->running) {
+ should_deactivate = PJ_FALSE;
+ break;
+ }
+ }
+ pj_mutex_unlock(stream->cf->mutex);
+
+#if !COREAUDIO_MAC
+ if (should_deactivate)
+ AudioSessionSetActive(false);
+#endif
+
+ stream->quit_flag = 1;
+ stream->play_thread_initialized = 0;
+ stream->rec_thread_initialized = 0;
+ pj_bzero(stream->rec_thread_desc, sizeof(pj_thread_desc));
+ pj_bzero(stream->play_thread_desc, sizeof(pj_thread_desc));
+
+ PJ_LOG(4, (THIS_FILE, "core audio stream stopped"));
+
+ return PJ_SUCCESS;
+}
+
+
+/* API: Destroy stream. */
+static pj_status_t ca_stream_destroy(pjmedia_aud_stream *strm)
+{
+ struct coreaudio_stream *stream = (struct coreaudio_stream*)strm;
+ unsigned i;
+
+ PJ_ASSERT_RETURN(stream != NULL, PJ_EINVAL);
+
+ ca_stream_stop(strm);
+
+ for (i = 0; i < 2; i++) {
+ if (stream->io_units[i]) {
+ AudioUnitUninitialize(stream->io_units[i]);
+ AudioComponentInstanceDispose(stream->io_units[i]);
+ stream->io_units[i] = NULL;
+ }
+ }
+
+ if (stream->resample)
+ AudioConverterDispose(stream->resample);
+
+ pj_mutex_lock(stream->cf->mutex);
+ if (!pj_list_empty(&stream->list_entry))
+ pj_list_erase(&stream->list_entry);
+ pj_mutex_unlock(stream->cf->mutex);
+
+ pj_pool_release(stream->pool);
+
+ return PJ_SUCCESS;
+}
+
+#endif /* PJMEDIA_AUDIO_DEV_HAS_COREAUDIO */
diff --git a/jni/pjproject-android/.svn/pristine/ad/ad7b4ac1916178ae5dea50d0869d867971c6ca94.svn-base b/jni/pjproject-android/.svn/pristine/ad/ad7b4ac1916178ae5dea50d0869d867971c6ca94.svn-base
new file mode 100644
index 0000000..6428d97
--- /dev/null
+++ b/jni/pjproject-android/.svn/pristine/ad/ad7b4ac1916178ae5dea50d0869d867971c6ca94.svn-base
@@ -0,0 +1,298 @@
+/* $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 <pjnath/stun_msg.h>
+#include <pjnath/errno.h>
+#include <pj/assert.h>
+#include <pj/os.h>
+#include <pj/string.h>
+
+#if PJ_LOG_MAX_LEVEL > 0
+
+
+#define APPLY() if (len < 1 || len >= (end-p)) \
+ goto on_return; \
+ p += len
+
+static int print_binary(char *buffer, unsigned length,
+ const pj_uint8_t *data, unsigned data_len)
+{
+ unsigned i;
+
+ if (length < data_len * 2 + 8)
+ return -1;
+
+ pj_ansi_sprintf(buffer, ", data=");
+ buffer += 7;
+
+ for (i=0; i<data_len; ++i) {
+ pj_ansi_sprintf(buffer, "%02x", (*data) & 0xFF);
+ buffer += 2;
+ data++;
+ }
+
+ pj_ansi_sprintf(buffer, "\n");
+ buffer++;
+
+ return data_len * 2 + 8;
+}
+
+static int print_attr(char *buffer, unsigned length,
+ const pj_stun_attr_hdr *ahdr)
+{
+ char *p = buffer, *end = buffer + length;
+ const char *attr_name = pj_stun_get_attr_name(ahdr->type);
+ char attr_buf[32];
+ int len;
+
+ if (*attr_name == '?') {
+ pj_ansi_snprintf(attr_buf, sizeof(attr_buf), "Attr 0x%x",
+ ahdr->type);
+ attr_name = attr_buf;
+ }
+
+ len = pj_ansi_snprintf(p, end-p,
+ " %s: length=%d",
+ attr_name,
+ (int)ahdr->length);
+ APPLY();
+
+
+ switch (ahdr->type) {
+ case PJ_STUN_ATTR_MAPPED_ADDR:
+ case PJ_STUN_ATTR_RESPONSE_ADDR:
+ case PJ_STUN_ATTR_SOURCE_ADDR:
+ case PJ_STUN_ATTR_CHANGED_ADDR:
+ case PJ_STUN_ATTR_REFLECTED_FROM:
+ case PJ_STUN_ATTR_XOR_PEER_ADDR:
+ case PJ_STUN_ATTR_XOR_RELAYED_ADDR:
+ case PJ_STUN_ATTR_XOR_MAPPED_ADDR:
+ case PJ_STUN_ATTR_XOR_REFLECTED_FROM:
+ case PJ_STUN_ATTR_ALTERNATE_SERVER:
+ {
+ const pj_stun_sockaddr_attr *attr;
+
+ attr = (const pj_stun_sockaddr_attr*)ahdr;
+
+ if (attr->sockaddr.addr.sa_family == pj_AF_INET()) {
+ len = pj_ansi_snprintf(p, end-p,
+ ", IPv4 addr=%s:%d\n",
+ pj_inet_ntoa(attr->sockaddr.ipv4.sin_addr),
+ pj_ntohs(attr->sockaddr.ipv4.sin_port));
+
+ } else if (attr->sockaddr.addr.sa_family == pj_AF_INET6()) {
+ len = pj_ansi_snprintf(p, end-p,
+ ", IPv6 addr present\n");
+ } else {
+ len = pj_ansi_snprintf(p, end-p,
+ ", INVALID ADDRESS FAMILY!\n");
+ }
+ APPLY();
+ }
+ break;
+
+ case PJ_STUN_ATTR_CHANNEL_NUMBER:
+ {
+ const pj_stun_uint_attr *attr;
+
+ attr = (const pj_stun_uint_attr*)ahdr;
+ len = pj_ansi_snprintf(p, end-p,
+ ", chnum=%u (0x%x)\n",
+ (int)PJ_STUN_GET_CH_NB(attr->value),
+ (int)PJ_STUN_GET_CH_NB(attr->value));
+ APPLY();
+ }
+ break;
+
+ case PJ_STUN_ATTR_CHANGE_REQUEST:
+ case PJ_STUN_ATTR_LIFETIME:
+ case PJ_STUN_ATTR_BANDWIDTH:
+ case PJ_STUN_ATTR_REQ_ADDR_TYPE:
+ case PJ_STUN_ATTR_EVEN_PORT:
+ case PJ_STUN_ATTR_REQ_TRANSPORT:
+ case PJ_STUN_ATTR_TIMER_VAL:
+ case PJ_STUN_ATTR_PRIORITY:
+ case PJ_STUN_ATTR_FINGERPRINT:
+ case PJ_STUN_ATTR_REFRESH_INTERVAL:
+ case PJ_STUN_ATTR_ICMP:
+ {
+ const pj_stun_uint_attr *attr;
+
+ attr = (const pj_stun_uint_attr*)ahdr;
+ len = pj_ansi_snprintf(p, end-p,
+ ", value=%u (0x%x)\n",
+ (pj_uint32_t)attr->value,
+ (pj_uint32_t)attr->value);
+ APPLY();
+ }
+ break;
+
+ case PJ_STUN_ATTR_USERNAME:
+ case PJ_STUN_ATTR_PASSWORD:
+ case PJ_STUN_ATTR_REALM:
+ case PJ_STUN_ATTR_NONCE:
+ case PJ_STUN_ATTR_SOFTWARE:
+ {
+ const pj_stun_string_attr *attr;
+
+ attr = (pj_stun_string_attr*)ahdr;
+ len = pj_ansi_snprintf(p, end-p,
+ ", value=\"%.*s\"\n",
+ (int)attr->value.slen,
+ attr->value.ptr);
+ APPLY();
+ }
+ break;
+
+ case PJ_STUN_ATTR_ERROR_CODE:
+ {
+ const pj_stun_errcode_attr *attr;
+
+ attr = (const pj_stun_errcode_attr*) ahdr;
+ len = pj_ansi_snprintf(p, end-p,
+ ", err_code=%d, reason=\"%.*s\"\n",
+ attr->err_code,
+ (int)attr->reason.slen,
+ attr->reason.ptr);
+ APPLY();
+ }
+ break;
+
+ case PJ_STUN_ATTR_UNKNOWN_ATTRIBUTES:
+ {
+ const pj_stun_unknown_attr *attr;
+ unsigned j;
+
+ attr = (const pj_stun_unknown_attr*) ahdr;
+
+ len = pj_ansi_snprintf(p, end-p,
+ ", unknown list:");
+ APPLY();
+
+ for (j=0; j<attr->attr_count; ++j) {
+ len = pj_ansi_snprintf(p, end-p,
+ " %d",
+ (int)attr->attrs[j]);
+ APPLY();
+ }
+ }
+ break;
+
+ case PJ_STUN_ATTR_MESSAGE_INTEGRITY:
+ {
+ const pj_stun_msgint_attr *attr;
+
+ attr = (const pj_stun_msgint_attr*) ahdr;
+ len = print_binary(p, (unsigned)(end-p), attr->hmac, 20);
+ APPLY();
+ }
+ break;
+
+ case PJ_STUN_ATTR_DATA:
+ {
+ const pj_stun_binary_attr *attr;
+
+ attr = (const pj_stun_binary_attr*) ahdr;
+ len = print_binary(p, (unsigned)(end-p), attr->data, attr->length);
+ APPLY();
+ }
+ break;
+ case PJ_STUN_ATTR_ICE_CONTROLLED:
+ case PJ_STUN_ATTR_ICE_CONTROLLING:
+ case PJ_STUN_ATTR_RESERVATION_TOKEN:
+ {
+ const pj_stun_uint64_attr *attr;
+ pj_uint8_t data[8];
+ int i;
+
+ attr = (const pj_stun_uint64_attr*) ahdr;
+
+ for (i=0; i<8; ++i)
+ data[i] = ((const pj_uint8_t*)&attr->value)[7-i];
+
+ len = print_binary(p, (unsigned)(end-p), data, 8);
+ APPLY();
+ }
+ break;
+ case PJ_STUN_ATTR_USE_CANDIDATE:
+ case PJ_STUN_ATTR_DONT_FRAGMENT:
+ default:
+ len = pj_ansi_snprintf(p, end-p, "\n");
+ APPLY();
+ break;
+ }
+
+ return (int)(p-buffer);
+
+on_return:
+ return len;
+}
+
+
+/*
+ * Dump STUN message to a printable string output.
+ */
+PJ_DEF(char*) pj_stun_msg_dump(const pj_stun_msg *msg,
+ char *buffer,
+ unsigned length,
+ unsigned *printed_len)
+{
+ char *p, *end;
+ int len;
+ unsigned i;
+
+ PJ_ASSERT_RETURN(msg && buffer && length, NULL);
+
+ PJ_CHECK_STACK();
+
+ p = buffer;
+ end = buffer + length;
+
+ len = pj_ansi_snprintf(p, end-p, "STUN %s %s\n",
+ pj_stun_get_method_name(msg->hdr.type),
+ pj_stun_get_class_name(msg->hdr.type));
+ APPLY();
+
+ len = pj_ansi_snprintf(p, end-p,
+ " Hdr: length=%d, magic=%08x, tsx_id=%08x%08x%08x\n"
+ " Attributes:\n",
+ msg->hdr.length,
+ msg->hdr.magic,
+ *(pj_uint32_t*)&msg->hdr.tsx_id[0],
+ *(pj_uint32_t*)&msg->hdr.tsx_id[4],
+ *(pj_uint32_t*)&msg->hdr.tsx_id[8]);
+ APPLY();
+
+ for (i=0; i<msg->attr_count; ++i) {
+ len = print_attr(p, (unsigned)(end-p), msg->attr[i]);
+ APPLY();
+ }
+
+on_return:
+ *p = '\0';
+ if (printed_len)
+ *printed_len = (unsigned)(p-buffer);
+ return buffer;
+
+#undef APPLY
+}
+
+
+#endif /* PJ_LOG_MAX_LEVEL > 0 */
+
diff --git a/jni/pjproject-android/.svn/pristine/ad/ad90a2706a74f2155fe59e9a2fea062e86e81447.svn-base b/jni/pjproject-android/.svn/pristine/ad/ad90a2706a74f2155fe59e9a2fea062e86e81447.svn-base
new file mode 100644
index 0000000..bc45da8
--- /dev/null
+++ b/jni/pjproject-android/.svn/pristine/ad/ad90a2706a74f2155fe59e9a2fea062e86e81447.svn-base
@@ -0,0 +1,964 @@
+ Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org>
+ Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
+
+ 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, see http://www.gnu.org/licenses/.
+
+
+Getting Started: Building and Using PJSIP and PJMEDIA
+
+ [Last Update: $Date: 2007-02-02 20:42:44 +0000 (Fri, 02 Feb 2007) $]
+
+ Print Friendly Page
+ _________________________________________________________________
+
+ This article describes how to download, customize, build, and use the open
+ source PJSIP and PJMEDIA SIP and media stack. The online (and HTML) version
+ of this file can be downloaded from http://www.pjsip.org/using.htm
+
+
+Quick Info
+ _________________________________________________________________
+
+ Building with GNU tools (Linux, *BSD, MacOS X, mingw, etc.)
+ Generally these should be all that are needed to build the libraries,
+ applications, and samples:
+
+ $ ./configure
+ $ make dep && make clean && make
+
+ Building Win32 Target with Microsoft Visual Studio
+ Generally we can just do these steps:
+
+ 1. Visual Studio 6: open pjproject.dsw workspace,
+ 2. Visual Studio 2005: open pjproject-vs8.sln solution,
+ 3. Create an empty pjlib/include/pj/config_site.h, and
+ 4. build the pjsua application.
+
+ Building for Windows Mobile
+ Generally these are all that are needed:
+
+ 1. Open pjsip-apps/build/wince-evc4/wince_demos.vcw EVC4 workspace,
+ 2. Create an empty pjlib/include/pj/config_site.h, and
+ 3. build the pjsua_wince application.
+
+ Invoking Older Build System (e.g. for RTEMS)
+ Generally these should be all that are needed to build the libraries,
+ applications, and samples:
+
+ $ ./configure-legacy
+ $ make dep && make clean && make
+
+ Locating Output Binaries/Libraries
+ Libraries will be put in lib directory, and binaries will be put in
+ bin directory, under each projects.
+
+ Running the Applications
+ After successful build, you can try running pjsua application on
+ pjsip-apps/bin directory. PJSUA manual can be found in
+ http://www.pjsip.org/pjsua.htm page.
+
+
+Table of Contents:
+ _________________________________________________________________
+
+ 1. Getting the Source Distribution
+
+ 1.1 Getting the Release tarball
+
+ 1.2 Getting from Subversion trunk
+
+ 1.3 Source Directories Layout
+
+ 2. Build Preparation
+
+ 2.1 config_site.h file
+
+ 2.2 Disk Space Requirements
+
+ 3. Building Linux, *nix, *BSD, and MacOS X Targets with GNU Build
+ Systems
+
+ 3.1 Supported Targets
+
+ 3.2 Requirements
+
+ 3.3 Running configure
+
+ 3.4 Running make
+
+ 3.5 Cross Compilation
+
+ 3.6 Build Customizations
+
+ 4. Building for Windows Targets with Microsoft Visual Studio
+
+ 4.1 Requirements
+
+ 4.2 Building the Projects
+
+ 4.3 Debugging the Sample Application
+
+ 5. Building for Windows Mobile Targets (Windows CE/WinCE/PDA/SmartPhone)
+
+ 5.1 Requirements
+
+ 5.2 Building the Projects
+
+ 6. Older PJLIB Build System for Non-Autoconf Targets (e.g. RTEMS)
+
+ 6.1 Supported Targets
+
+ 6.2 Invoking the Build System
+
+ 7. Running the Applications
+
+ 7.1 pjsua
+
+ 7.2 Sample Applications
+
+ 7.3 pjlib-test
+
+ 7.4 pjsip-test
+
+ 8. Using PJPROJECT with Applications
+
+
+ Appendix I: Common Problems/Frequently Asked Question (FAQ)
+
+ I.1 fatal error C1083: Cannot open include file: 'pj/config_site.h':
+ No such file or directory
+
+
+1. Getting the Source Code Distribution
+ _________________________________________________________________
+
+ All libraries (PJLIB, PJLIB-UTIL, PJSIP, PJMEDIA, and PJMEDIA-CODEC) are
+ currently distributed under a single source tree, collectively named as
+ PJPROJECT or just PJ libraries. These libraries can be obtained by either
+ downloading the release tarball or getting them from the Subversion trunk.
+
+
+1.1 Getting the Release tarball
+ _________________________________________________________________
+
+ Getting the released tarball is a convenient way to obtain stable version of
+ PJPROJECT. The tarball may not contain the latest features or bug-fixes, but
+ normally it is considered more stable as each will be tested more rigorously
+ before released.
+
+ The latest released tarball can be downloaded from the
+ http://www.pjsip.org/download.htm.
+
+
+1.2 Getting from Subversion trunk
+ _________________________________________________________________
+
+ PJPROJECT Subversion repository will always contain the latest/most
+ up-to-date version of the sources. Normally the Subversion repository is
+ always kept in a "good" state. However, there's always a chance that things
+ break and the tree doesn't build correctly (particularly for the
+ "not-so-popular" targets), so please consult the mailing list should there
+ be any problems.
+
+ Using Subversion also has benefits of keeping the local copy of the source
+ up to date with the main PJ source tree and to easily track the changes made
+ to the local copy, if any.
+
+
+What is Subversion
+
+ Subversion (SVN) is Open Source version control system similar to CVS.
+ Subversion homepage is in http://subversion.tigris.org/
+
+
+Getting Subversion Client
+
+ A Subversion (SVN) client is needed to download the PJ source files from
+ pjsip.org SVN tree. SVN client binaries can be downloaded from
+ http://subversion.tigris.org/, and the program should be available for
+ Windows, Linux, MacOS X, and many more platforms.
+
+
+Getting the Source for The First Time
+
+ Once Subversion client is installed, we can use these commands to initially
+ retrieve the latest sources from the Subversion trunk:
+
+
+
+ $ svn co http://svn.pjproject.net/repos/pjproject/trunk pjproject
+ $ cd pjproject
+
+
+Keeping The Local Copy Up-to-Date
+
+ Once sources have been downloaded, we can keep the local copy up to date by
+ periodically synchronizing the local source with the latest revision from
+ the PJ's Subversion trunk. The mailing list provides best source of
+ information about the availability of new updates in the trunk.
+
+ To update the local copy with the latest changes in the main PJ's
+ repository:
+
+
+
+ $ cd pjproject
+ $ svn update
+
+
+Tracking Local and Remote Changes
+
+ To see what files have been changed locally:
+
+
+
+ $ cd pjproject
+ $ svn status
+
+ The above command only compares local file against the original local copy,
+ so it doesn't require Internet connection while performing the check.
+
+ To see both what files have been changed locally and what files have been
+ updated in the PJ's Subversion repository:
+
+
+
+ $ cd pjproject
+ $ svn status -u
+
+ Note that this command requires active Internet connection to query the
+ status of PJPROJECT's source repository.
+
+
+1.3 Source Directories Layout
+ _________________________________________________________________
+
+Top-Level Directory Layout
+
+ The top-level directories (denoted as $TOP here) in the source distribution
+ contains the following sub-directories:
+
+ $TOP/build
+ Contains makefiles that are common for all projects.
+
+ $TOP/pjlib
+ Contains header and source files of PJLIB. PJLIB is the base
+ portability and framework library which is used by all other
+ libraries
+
+ $TOP/pjlib-util
+ Contains PJLIB-UTIL header and source files. PJLIB-UTIL is an
+ auxiliary library that contains utility functions such as scanner,
+ XML, STUN, MD5 algorithm, getopt() implementation, etc.
+
+ $TOP/pjmedia
+ Contains PJMEDIA and PJMEDIA-CODEC header and source files. The
+ sources of various codecs (such as GSM, Speex, and iLBC) can be found
+ under this directory.
+
+ $TOP/pjsip
+ Contains PJSIP header and source files.
+
+ $TOP/pjsip-apps
+ Contains source code for PJSUA and various sample applications.
+
+
+Individual Directory Inside Each Project
+
+ Each library directory further contains these sub-directories:
+
+ bin
+ Contains binaries produced by the build process.
+
+ build
+ Contains build scripts/makefiles, project files, project workspace,
+ etc. to build the project. In particular, it contains one Makefile
+ file to build the project with GNU build systems, and a *.dsw
+ workspace file to build the library with Microsoft Visual Studio 6 or
+ later.
+
+ build/output
+ The build/output directory contains the object files and other files
+ generated by the build process. To support building multiple targets
+ with a single source tree, each build target will occupy a different
+ subdirectory under this directory.
+
+ build/wince-evc4
+ This directory contains the project/workspace files to build Windows
+ CE/WinCE version of the project using Microsoft Embedded Visual C++
+ 4.
+
+ build/wince-evc4/output
+ This directory contains the library, executable, and object files
+ generated by Windows Mobile build process.
+
+ docs
+ Contains Doxygen configuration file (doxygen.cfg) to generate online
+ documentation from the source files. The output documentation will be
+ put in this directory as well (for example, docs/html directory for
+ the HTML files).
+
+ (to generate Doxygen documentation from the source tree, just run
+ "doxygen docs/doxygen.cfg" in the individual project directory. The
+ generated files will reside in docs directory).
+
+ include
+ Contains the header files for the project.
+
+ lib
+ Contains libraries produced by the build process.
+
+ src
+ Contains the source files of the project.
+
+
+2. Build Preparation
+ _________________________________________________________________
+
+2.1 Create config_site.h file
+ _________________________________________________________________
+
+ Before source files can be built, the pjlib/include/pj/config_site.h file
+ must be created (it can just be an empty file).
+
+ Note:
+ When the Makefile based build system is used, this process is taken
+ care by the Makefiles. But when non-Makefile based build system (such
+ as Visual Studio) is used, the config_site.h file must be created
+ manually.
+
+
+What is config_site.h File
+
+ The pjlib/include/pj/config_site.h contains local customizations to the
+ libraries.
+
+ All customizations should be put in this file instead of modifying PJ's
+ files, because if PJ's files get modified, then those modified files will
+ not be updated the next time the source is synchronized. Or in other case,
+ the local modification may be overwritten with the fresh copy from the SVN.
+
+ Putting the local customization to the config_site.h solves this problem,
+ because this file is not included in the version control, so it will never
+ be overwritten by "svn update" command.
+
+ Please find list of configuration macros that can be overriden from these
+ files:
+ * PJLIB Configuration (the pjlib/config.h file)
+ * PJLIB-UTIL Configuration (the pjlib-util/config.h file)
+ * PJMEDIA Configuration (the pjmedia/config.h file)
+ * PJSIP Configuration (the pjsip/sip_config.h file)
+
+ A sample config_site.h file is also available in
+ pjlib/include/config_site_sample.h.
+
+
+Creating config_site.h file
+
+ The simplest way is just to create an empty file, to use whetever default
+ values set by the libraries.
+
+ Another way to create the config_site.h file is to write something like the
+ following:
+
+
+ // Uncomment to get minimum footprint (suitable for 1-2 concurrent calls
+ only)
+ //#define PJ_CONFIG_MINIMAL_SIZE
+ // Uncomment to get maximum performance
+ //#define PJ_CONFIG_MAXIMUM_SPEED
+ #include <pj/config_site_sample.h>
+
+
+2.2 Disk Space Requirements
+ _________________________________________________________________
+
+ The building process needs:
+ about 50-60 MB of disk space to store the uncompressed source files, and
+ * about 30-50 MB of additional space for building each target
+
+ (Visual Studio Debug and Release are considered as separate targets)
+
+
+3. Building Linux, *nix, *BSD, and MacOS X Targets with GNU Build Systems
+ _________________________________________________________________
+
+3.1 Supported Targets
+ _________________________________________________________________
+
+ The new, autoconf based GNU build system can be used to build the
+ libraries/applications for the following targets:
+ * Linux/uC-Linux (i386, Opteron, Itanium, MIPS, PowerPC, etc.),
+ * MacOS X (PowerPC),
+ * mingw (i386),
+ * FreeBSD and maybe other BSD's (i386, Opteron, etc.),
+ * RTEMS with cross compilation (ARM, powerpc),
+ * etc.
+
+
+3.2 Requirements
+ _________________________________________________________________
+
+ In order to use PJ's GNU build system, these typical GNU tools are needed:
+ * GNU make (other make will not work),
+ * GNU binutils for the target, and
+ * GNU gcc for the target.
+ * OpenSSL header files/libraries (optional) if TLS support is wanted.
+
+ In addition, the appropriate "SDK" must be installed for the particular
+ target (this could just be a libc and the appropriate system abstraction
+ library such as Posix).
+
+ The build system is known to work on the following hosts:
+ * Linux, many types of distributions.
+ * MacOS X 10.2
+ * mingw (Win2K, XP)
+ * FreeBSD (must use gmake instead of make)
+
+ Building Win32 applications with Cygwin is currently not supported by the
+ autoconf script (there is some Windows header conflicts), but one can still
+ use the old configure script by calling ./configure-legacy. More over,
+ cross-compilations might also work with Cygwin.
+
+
+3.3 Running configure
+ _________________________________________________________________
+
+Using Default Settings
+
+ Run "./configure" without any options to let the script detect the
+ appropriate settings for the host:
+
+
+
+ $ cd pjproject
+ $ ./configure
+ ...
+
+ Notes:
+ The default settings build the libraries in "release" mode, with
+ default CFLAGS set to "-O2 -DNDEBUG". To change the default CFLAGS,
+ we can use the usual "./configure CFLAGS='-g'" construct.
+
+ Features Customization
+
+ With the new autoconf based build system, most configuration/customization
+ can be specified as configure arguments. The list of customizable features
+ can be viewed by running "./configure --help" command:
+
+
+
+ $ cd pjproject
+ $ ./configure --help
+ ...
+ Optional Features:
+ --disable-floating-point Disable floating point where possible
+ --disable-sound Exclude sound (i.e. use null sound)
+ --disable-small-filter Exclude small filter in resampling
+ --disable-large-filter Exclude large filter in resampling
+ --disable-g711-plc Exclude G.711 Annex A PLC
+ --disable-speex-aec Exclude Speex Acoustic Echo Canceller/AEC
+ --disable-g711-codec Exclude G.711 codecs from the build
+ --disable-l16-codec Exclude Linear/L16 codec family from the build
+ --disable-gsm-codec Exclude GSM codec in the build
+ --disable-speex-codec Exclude Speex codecs in the build
+ --disable-ilbc-codec Exclude iLBC codec in the build
+ --disable-tls Force excluding TLS support (default is autodetected based on
+ OpenSSL availability)
+ ...
+
+ Configuring Debug Version and Other Customizations
+
+ The configure script accepts standard customization, which details can be
+ obtained by executing ./configure --help.
+
+ Below is an example of specifying CFLAGS in configure:
+
+
+
+ $ ./configure CFLAGS="-O3 -DNDEBUG -msoft-float -fno-builtin"
+ ...
+
+ Configuring TLS Support
+
+ By default, TLS support is configured based on the availability of OpenSSL
+ header files and libraries. If OpenSSL is available at the default include
+ and library path locations, TLS will be enabled by the configure script.
+
+ You can explicitly disable TLS support by giving the configure script
+ --disable-tls option.
+
+
+ 3.4 Cross Compilation
+ _________________________________________________________________
+
+ Cross compilation should be supported, using the usual autoconf syntax:
+
+
+
+ $ ./configure --host=arm-elf-linux
+ ...
+
+ Since cross-compilation is not tested as often as the "normal" build, please
+ watch for the ./configure output for incorrect settings (well ideally this
+ should be done for normal build too).
+
+ Please refer to Porting Guide for further information about porting PJ
+ software.
+
+
+ 3.5 Running make
+ _________________________________________________________________
+
+ Once the configure script completes successfully, start the build process by
+ invoking these commands:
+
+
+
+ $ cd pjproject
+ $ make dep
+ $ make
+
+ Note:
+ gmake may need to be specified instead of make for some hosts, to
+ invoke GNU make instead of the native make.
+
+
+ Description of all make targets supported by the Makefile's:
+
+ all
+ The default (or first) target to build the libraries/binaries.
+
+ dep, depend
+ Build dependencies rule from the source files.
+
+ clean
+ Clean the object files for current target, but keep the output
+ library/binary files intact.
+
+ distclean, realclean
+ Remove all generated files (object, libraries, binaries, and
+ dependency files) for current target.
+
+
+ Note:
+ make can be invoked either in the top-level PJ directory or in build
+ directory under each project to build only the particular project.
+
+
+ 3.6 Build Customizations
+ _________________________________________________________________
+
+ Build features can be customized by specifying the options when running
+ ./configure as described in Running Configure above.
+
+ In addition, additional CFLAGS and LDFLAGS options can be put in user.mak
+ file in PJ root directory (this file may need to be created if it doesn't
+ exist). Below is a sample of user.mak file contents:
+
+
+
+ export CFLAGS += -msoft-float -fno-builtin
+ export LDFLAGS +=
+
+
+4. Building for Windows Targets with Microsoft Visual Studio
+ _________________________________________________________________
+
+ 4.1 Requirements
+ _________________________________________________________________
+
+ The Microsoft Visual Studio based project files can be used with one of the
+ following:
+
+ * Microsoft Visual Studio 6,
+ * Microsoft Visual Studio .NET 2002,
+ * Microsoft Visual Studio .NET 2003,
+ * Microsoft Visual C++ 2005 (including Express edition),
+
+ In addition, the following SDK's are needed:
+ * Platform SDK, if you're using Visual Studio 2005 Express (tested with
+ Platform SDK for Windows Server 2003 SP1),
+ * DirectX SDK (tested with DirectX version 8 and 9),
+ * OpenSSL development kit would be needed if TLS support is wanted, or
+ otherwise this is optional.
+
+ For the host, the following are required:
+ * Windows NT, 2000, XP, 2003, or later ,
+ * Windows 95/98 should work too, but this has not been tested,
+ * Sufficient amount of RAM for the build process (at least 256MB).
+
+
+ Enabling TLS Support with OpenSSL
+
+ If TLS support is wanted, then OpenSSL SDK must be installed in the
+ development host.
+
+ To install OpenSSL SDK from the Win32 binary distribution:
+ 1. Install OpenSSL SDK to any folder (e.g. C:\OpenSSL)
+ 2. Add OpenSSL DLL location to the system PATH.
+ 3. Add OpenSSL include path to Visual Studio includes search directory.
+ Make sure that OpenSSL header files can be accessed from the program
+ with #include <openssl/ssl.h> construct.
+ 4. Add OpenSSL library path to Visual Studio library search directory. Make
+ sure the following libraries are accessible:
+ + For Debug build: libeay32MTd and ssleay32MTd.
+ + For Release build: libeay32MT and ssleay32MT.
+
+ Then to enable TLS transport support in PJSIP, just add
+
+ #define PJSIP_HAS_TLS_TRANSPORT 1
+
+ in your pj/config_site.h. When this macro is defined, OpenSSL libraries will
+ be automatically linked to the application via the #pragma construct in
+ sip_transport_tls_ossl.c file.
+
+
+ 4.2 Building the Projects
+ _________________________________________________________________
+
+ Follow the steps below to build the libraries/application using Visual
+ Studio:
+ 1. For Visual Studio 6: open pjproject.dsw workspace file.
+ 2. For Visual Studio 8 (VS 2005): open pjproject-vs8.sln solution file.
+ 3. Set pjsua as Active Project.
+ 4. Select Debug or Release build as appropriate.
+ 5. Build the project. This will build pjsua application and all libraries
+ needed by pjsua.
+ 6. After successful build, the pjsua application will be placed in
+ pjsip-apps/bin directory, and the libraries in lib directory under each
+ projects.
+
+ To build the samples:
+ 1. (Still using the same workspace)
+ 2. Set samples project as Active Project
+ 3. Select Debug or Release build as appropriate.
+ 4. Build the project. This will build all sample applications and all
+ libraries needed.
+ 5. After successful build, the sample applications will be placed in
+ pjsip-apps/bin/samples directory, and the libraries in lib directory
+ under each projects.
+
+ 4.3 Debugging the Sample Application
+ _________________________________________________________________
+
+ The sample applications are build using Samples.mak makefile, therefore it
+ is difficult to setup debugging session in Visual Studio for these
+ applications. To solve this issue, the pjsip_apps workspace contain one
+ project called sample_debug which can be used to debug the sample
+ application.
+
+ To setup debugging using sample_debug project:
+ 1. (Still using pjsip_apps workspace)
+ 2. Set sample_debug project as Active Project
+ 3. Edit debug.c file inside this project.
+ 4. Modify the #include line to include the particular sample application to
+ debug
+ 5. Select Debug build.
+ 6. Build and debug the project.
+
+
+5. Building for Windows Mobile Targets (Windows CE/WinCE/PDA/SmartPhone)
+ _________________________________________________________________
+
+ PJ supports building SIP and media stacks and applications for Windows
+ Mobile targets. A very simple WinCE SIP user agent (with media) application
+ is provided just as proof of concept that the port works.
+
+ 5.1 Requirements
+ _________________________________________________________________
+
+ One of the following development tools is needed to build SIP and media
+ components for Windows Mobile:
+ * Microsoft Embedded Visual C++ 4 with appropriate SDKs, or
+ * Microsoft Visual Studio 2005 for Windows Mobile with appropriate SDKs.
+
+ Note that VS2005 is not directly supported (as I don't have the tools), but
+ it is reported to work (I assumed that VS2005 for Windows Mobile can import
+ EVC4 workspace file).
+
+ 5.2 Building the Projects
+ _________________________________________________________________
+
+ The Windows Mobile port is included in the main source distribution. Please
+ follow the following steps to build the WinCE libraries and sample
+ application:
+ 1. Open pjsip-apps/build/wince-evc4/wince_demos.vcw workspace file. If
+ later version of EVC4 is being used, this may cause the workspace file
+ to be converted to the appropriate format.
+ 2. Select pjsua_wince project as the Active Project.
+ 3. Select the appropriate SDK (for example Pocket PC 2003 SDK or SmartPhone
+ 2003 SDK)
+ 4. Select the appropriate configuration (for example, Win32 (WCE Emulator
+ Debug) to debug the program in emulator, or other configurations such as
+ ARMV4, MIPS, SH3, SH4, or whatever suitable for the device)
+ 5. Select the appropriate device (Emulator or the actual Device).
+ 6. Build the project. This will build the sample WinCE application and all
+ libraries (SIP, Media, etc.) needed by this application.
+
+ Notes
+
+ + If the config_site.h includes config_site_sample.h file, then
+ there are certain configuration in config_site_sample.h that get
+ activated for Windows CE targets. Please make sure that these
+ configurations are suitable for the application.
+ + The libraries, binaries and object files produced by the build
+ process are located under build/wince-evc4/output directory of each
+ projects.
+
+
+6. Older PJLIB Build System for Non-Autoconf Targets (e.g. RTEMS)
+ _________________________________________________________________
+
+ The old PJLIB build system can still be used for building PJ libraries, for
+ example for RTEMS target. Please see the Porting PJLIB page in PJLIB
+ Reference documentation for information on how to support new target using
+ this build system.
+
+ 6.1 Supported Targets
+ _________________________________________________________________
+
+ The older build system supports building PJ libraries for the following
+ operating systems:
+ * RTEMS
+ * Linux
+ * MacOS X
+ * Cygwin and Mingw
+
+ And it supports the following target architectures:
+ * i386, x86_64, itanium
+ * ARM
+ * mips
+ * powerpc
+ * mpc860
+ * etc.
+
+ For other targets, specific files need to be added to the build system,
+ please see the Porting PJLIB page in PJLIB Reference documentation for
+ details.
+
+ 6.2 Invoking the Build System
+ _________________________________________________________________
+
+ To invoke the older build system, run the following:
+
+
+
+ $ cd pjproject
+ $ ./configure-legacy
+ $ make dep && make clean && make
+
+
+
+7. Running the Applications
+ _________________________________________________________________
+
+ Upon successful build, the output libraries (PJLIB, PJLIB-UTIL, PJMEDIA,
+ PJSIP, etc.) are put under ./lib sub-directory under each project directory.
+ In addition, some applications may also be built, and such applications will
+ be put in ./bin sub-directory under each project directory.
+
+
+ 7.1 pjsua
+ _________________________________________________________________
+
+ pjsua is the reference implementation for both PJSIP and PJMEDIA stack, and
+ is the main target of the build system. Upon successful build, pjsua
+ application will be put in pjsip-apps/bin directory.
+
+ pjsua manual can be found in pjsua Manual Page.
+
+
+ 7.2 Sample Applications
+ _________________________________________________________________
+
+ Sample applications will be built with the Makefile build system. For Visual
+ Studio, you have to build the samples manually by selecting and building the
+ Samples project inside pjsip-apps/build/pjsip_apps.dsw project workspace.
+
+ Upon successful build, the sample applications are put in
+ pjsip-apps/bin/samples directory.
+
+ The sample applications are described in PJMEDIA Samples Page and
+ PJSIP Samples Page in the website.
+
+
+ 7.3 pjlib-test
+ _________________________________________________________________
+
+ pjlib-test contains comprehensive tests for testing PJLIB functionality.
+ This application will only be built when the Makefile build system is used;
+ with Visual Studio, one has to open pjlib.dsw project in pjlib/build
+ directory to build this application.
+
+ If you're porting PJLIB to new target, it is recommended to run this
+ application to make sure that all functionalities works as expected.
+
+
+ 7.4 pjsip-test
+ _________________________________________________________________
+
+ pjsip-test contains codes for testing various SIP functionalities in PJSIP
+ and also to benchmark static performance metrics such as message parsing per
+ second.
+
+
+
+8. Using PJPROJECT with Applications
+ _________________________________________________________________
+
+ Regardless of the build system being used, the following tasks are normally
+ needed to be done in order to build application to use PJSIP and PJMEDIA:
+ 1. Put these include directories in the include search path:
+ + pjlib/include
+ + pjlib-util/include
+ + pjmedia/include
+ + pjsip/include
+ 2. Put these library directories in the library search path:
+ + pjlib/lib
+ + pjlib-util/lib
+ + pjmedia/lib
+ + pjsip/lib
+ 3. Include the relevant PJ header files in the application source file. For
+ example, using these would include ALL APIs exported by PJ:
+
+ #include <pjlib.h>
+ #include <pjlib-util.h>
+ #include <pjsip.h>
+ #include <pjsip_ua.h>
+ #include <pjsip_simple.h>
+ #include <pjsua.h>
+ #include <pjmedia.h>
+ #include <pjmedia-codec.h>
+ (Note: the documentation of the relevant libraries should say which
+ header files should be included to get the declaration of the APIs).
+ 4. Declare the OS macros.
+ + For Windows applications built with Visual Studio, we need to
+ declare PJ_WIN32=1 macro in the project settings (declaring the
+ macro in the source file may not be sufficient).
+ + For Windows Mobile applications build with Visual C++, we need to
+ declare PJ_WIN32_WINCE=1 macro in the project settings.
+ + For GNU build system/autoconf based build system, we need to
+ declare PJ_AUTOCONF=1 macro when compiling the applications.
+ (Note: the old PJ build system requires declaring the target processor
+ with PJ_M_XXX=1 macro, but this has been made obsolete. The target
+ processor will be detected from compiler's predefined macro by
+ pjlib/config.h file).
+ 5. Link with the appropriate PJ libraries. The following libraries will
+ need to be included in the library link specifications:
+
+ pjlib
+ Base library used by all libraries.
+
+ pjlib-util
+ Auxiliary library containing scanner, XML, STUN, MD5, getopt,
+ etc, used by the SIP and media stack.
+
+ pjsip
+ SIP core stack library.
+
+ pjsip-ua
+ SIP user agent library containing INVITE session, call
+ transfer, client registration, etc.
+
+ pjsip-simple
+ SIP SIMPLE library for base event framework, presence, instant
+ messaging, etc.
+
+ pjsua
+ High level SIP UA library, combining SIP and media stack into
+ high-level easy to use API.
+
+ pjmedia
+ The media framework.
+
+ pjmedia-codec
+ Container library for various codecs such as GSM, Speex, and
+ iLBC.
+
+
+ Note: the actual library names will be appended with the target name and the
+ build configuration. For example:
+
+ For Visual Studio builds
+ The actual library names will look like
+ pjlib-i386-win32-vc6-debug.lib,
+ pjlib-i386-win32-vc6-release.lib, etc., depending on whether we
+ are building the Debug or Release version of the library.
+
+ An easier way to link with the libraries is to include PJ
+ project files in the workspace, and to configure project
+ dependencies so that the application depends on the PJ
+ libraries. This way, we don't need to manually add each PJ
+ libraries to the input library file specification, since VS
+ will automatically link the dependency libraries with the
+ application.
+
+ For Windows Mobile builds
+ Unfortunately the PJ libraries built for Windows Mobile will
+ not be placed in the usual lib directory, but rather under the
+ output directory under build/wince-evc4 project directory.
+
+ An easier way to link with the libraries is to include PJ
+ project files in the workspace, and to configure project
+ dependencies so that the application depends on the PJ
+ libraries. This way, we don't need to manually add each PJ
+ libraries to the input library file specification, since VS
+ will automatically link the dependency libraries with the
+ application.
+
+ For GNU builds
+ Application's Makefile can get the PJ library suffix by
+ including PJ's build.mak file from the root PJ directory (the
+ suffix is contained in TARGET_NAME variable). For example, to
+ link with PJLIB and PJMEDIA, we can use this syntax in the
+ LDFLAGS: "-lpj-$(TARGET_NAME) -lpjmedia-$(TARGET_NAME)"
+
+
+ 6. Link with system spesific libraries:
+
+ Windows
+ Add (among other things): wsock32.lib, ws2_32.lib, ole32.lib,
+ dsound.lib
+
+ Linux, *nix, *BSD
+ Add (among other things): '-lpthread -lm' (at least).
+
+ MacOS X
+ Add (among other things): '-framework CoreAudio -lpthread -lm'.
+
+
+Appendix I: Common Problems/Frequently Asked Question (FAQ)
+ _________________________________________________________________
+
+ I.1 fatal error C1083: Cannot open include file: 'pj/config_site.h': No such
+ file or directory
+
+ This error normally occurs when the config_site.h file has not been created.
+ This file needs to be created manually (an empty file is sufficient). Please
+ follow the Build Preparation instructions above to create this file.
+
+
+
+
+
+
+
+
+ _________________________________________________________________
+
+ Feedback:
+ Thanks for using PJ libraries and for reading this document. Please
+ send feedbacks or general comments to <bennylp at pjsip dot org>.
+
diff --git a/jni/pjproject-android/.svn/pristine/ad/ad9ca3f4b9c198a02c701f6ddd5bb9322fac4bf6.svn-base b/jni/pjproject-android/.svn/pristine/ad/ad9ca3f4b9c198a02c701f6ddd5bb9322fac4bf6.svn-base
new file mode 100644
index 0000000..c95561f
--- /dev/null
+++ b/jni/pjproject-android/.svn/pristine/ad/ad9ca3f4b9c198a02c701f6ddd5bb9322fac4bf6.svn-base
@@ -0,0 +1,24 @@
+/* $Id$ */
+/*
+ * Copyright (C) 2009-2011 Teluu Inc. (http://www.teluu.com)
+ *
+ * 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
+ */
+
+/*
+ * This file is a C++ wrapper, see ticket #886 for details.
+ */
+
+#include "pool.c"
diff --git a/jni/pjproject-android/.svn/pristine/ad/adc83b19e793491b1c6ea0fd8b46cd9f32e592fc.svn-base b/jni/pjproject-android/.svn/pristine/ad/adc83b19e793491b1c6ea0fd8b46cd9f32e592fc.svn-base
new file mode 100644
index 0000000..8b13789
--- /dev/null
+++ b/jni/pjproject-android/.svn/pristine/ad/adc83b19e793491b1c6ea0fd8b46cd9f32e592fc.svn-base
@@ -0,0 +1 @@
+