diff -aurN a/build/os-android.mak b/build/os-android.mak --- a/build/os-android.mak 1970-01-01 01:00:00.000000000 +0100 +++ b/build/os-android.mak 2009-04-06 21:21:05.000000000 +0100 @@ -0,0 +1,10 @@ + +export OS_CFLAGS := @ANDROID_CFLAGS@ + +export OS_CXXFLAGS := @ANDROID_CFLAGS@ + +export OS_LDFLAGS := @ANDROID_LDFLAGS@ + +export OS_SOURCES := + + diff -aurN a/build.mak.in b/build.mak.in --- a/build.mak.in 2008-08-21 21:59:58.000000000 +0100 +++ b/build.mak.in 2009-04-06 21:21:05.000000000 +0100 @@ -1,6 +1,6 @@ # @configure_input@ export MACHINE_NAME := auto -export OS_NAME := auto +export OS_NAME := android export HOST_NAME := unix export CC_NAME := gcc export TARGET_NAME := @target@ diff -aurN a/pjlib/build/os-android.mak b/pjlib/build/os-android.mak --- a/pjlib/build/os-android.mak 1970-01-01 01:00:00.000000000 +0100 +++ b/pjlib/build/os-android.mak 2009-04-06 21:21:05.000000000 +0100 @@ -0,0 +1,39 @@ +# +# OS specific configuration for Linux OS target. +# + +# +# PJLIB_OBJS specified here are object files to be included in PJLIB +# (the library) for this specific operating system. Object files common +# to all operating systems should go in Makefile instead. +# +export PJLIB_OBJS += addr_resolv_sock.o file_access_unistd.o \ + file_io_ansi.o guid_simple.o \ + log_writer_stdout.o os_core_android.o \ + os_error_unix.o os_time_unix.o \ + os_timestamp_common.o os_timestamp_posix.o \ + pool_policy_malloc.o sock_bsd.o sock_select.o + +ifeq (epoll,$(LINUX_POLL)) +export PJLIB_OBJS += ioqueue_epoll.o +else +export PJLIB_OBJS += ioqueue_select.o +endif + +# +# TEST_OBJS are operating system specific object files to be included in +# the test application. +# +export TEST_OBJS += main.o + +# +# Additional LDFLAGS for pjlib-test +# +export TEST_LDFLAGS += -lm + +# +# TARGETS are make targets in the Makefile, to be executed for this given +# operating system. +# +export TARGETS = pjlib pjlib-test + diff -aurN a/pjlib/include/pj/compat/os_android.h b/pjlib/include/pj/compat/os_android.h --- a/pjlib/include/pj/compat/os_android.h 1970-01-01 01:00:00.000000000 +0100 +++ b/pjlib/include/pj/compat/os_android.h 2009-04-06 21:21:05.000000000 +0100 @@ -0,0 +1,134 @@ +/* $Id: os_linux.h 2394 2008-12-23 17:27:53Z bennylp $ */ +/* + * Copyright (C) 2008-2009 Teluu Inc. (http://www.teluu.com) + * Copyright (C) 2003-2008 Benny Prijono + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +#ifndef __PJ_COMPAT_OS_ANDROID_H__ +#define __PJ_COMPAT_OS_ANDROID_H__ + +/** + * @file os_linux.h + * @brief Describes Linux operating system specifics. + */ + +#define PJ_OS_NAME "android" + +#define PJ_HAS_ARPA_INET_H 1 +#define PJ_HAS_ASSERT_H 1 +#define PJ_HAS_CTYPE_H 1 +#define PJ_HAS_ERRNO_H 1 +#define PJ_HAS_LINUX_SOCKET_H 0 +#define PJ_HAS_MALLOC_H 1 +#define PJ_HAS_NETDB_H 1 +#define PJ_HAS_NETINET_IN_H 1 +#define PJ_HAS_SETJMP_H 1 +#define PJ_HAS_STDARG_H 1 +#define PJ_HAS_STDDEF_H 1 +#define PJ_HAS_STDIO_H 1 +#define PJ_HAS_STDLIB_H 1 +#define PJ_HAS_STDINT_H 1 +#define PJ_HAS_STRING_H 1 +#define PJ_HAS_SYS_IOCTL_H 1 +#define PJ_HAS_SYS_SELECT_H 1 +#define PJ_HAS_SYS_SOCKET_H 1 +#define PJ_HAS_SYS_TIME_H 1 +/* timeb.h is present in cupcake, but not in the 1.1 release */ +#define PJ_HAS_SYS_TIMEB_H 0 +#define PJ_HAS_SYS_TYPES_H 1 +#define PJ_HAS_TIME_H 1 +#define PJ_HAS_UNISTD_H 1 +#define PJ_HAS_SEMAPHORE_H 1 + +#define PJ_HAS_MSWSOCK_H 0 +#define PJ_HAS_WINSOCK_H 0 +#define PJ_HAS_WINSOCK2_H 0 + +#define PJ_SOCK_HAS_INET_ATON 1 +#define PJ_SOCK_HAS_INET_NTOP 1 + +/* Set 1 if native sockaddr_in has sin_len member. + * Default: 0 + */ +#define PJ_SOCKADDR_HAS_LEN 0 +#define PJ_SOCKADDR_HAS_PAD 1 + + +/** + * If this macro is set, it tells select I/O Queue that select() needs to + * be given correct value of nfds (i.e. largest fd + 1). This requires + * select ioqueue to re-scan the descriptors on each registration and + * unregistration. + * If this macro is not set, then ioqueue will always give FD_SETSIZE for + * nfds argument when calling select(). + * + * Default: 0 + */ +#define PJ_SELECT_NEEDS_NFDS 0 + +/* Is errno a good way to retrieve OS errors? + */ +#define PJ_HAS_ERRNO_VAR 1 + +/* When this macro is set, getsockopt(SOL_SOCKET, SO_ERROR) will return + * the status of non-blocking connect() operation. + */ +#define PJ_HAS_SO_ERROR 1 + +/* This value specifies the value set in errno by the OS when a non-blocking + * socket recv() can not return immediate daata. + */ +#define PJ_BLOCKING_ERROR_VAL EAGAIN + +/* This value specifies the value set in errno by the OS when a non-blocking + * socket connect() can not get connected immediately. + */ +#define PJ_BLOCKING_CONNECT_ERROR_VAL EINPROGRESS + +/* Default threading is enabled, unless it's overridden. */ +#ifndef PJ_HAS_THREADS +# define PJ_HAS_THREADS (1) +#endif + +#define PJ_HAS_HIGH_RES_TIMER 1 +#define PJ_HAS_MALLOC 1 +#ifndef PJ_OS_HAS_CHECK_STACK +# define PJ_OS_HAS_CHECK_STACK 0 +#endif +#define PJ_NATIVE_STRING_IS_UNICODE 0 + +#define PJ_ATOMIC_VALUE_TYPE long + +/* pthread_rwlock_t does not appear to be defined on Android */ +#define PJ_EMULATE_RWMUTEX 1 + +/* If 1, pj_thread_create() should enforce the stack size when creating + * threads. + * Default: 0 (let OS decide the thread's stack size). + */ +#define PJ_THREAD_SET_STACK_SIZE 0 + +/* If 1, pj_thread_create() should allocate stack from the pool supplied. + * Default: 0 (let OS allocate memory for thread's stack). + */ +#define PJ_THREAD_ALLOCATE_STACK 0 + +/* Linux has socklen_t */ +#define PJ_HAS_SOCKLEN_T 1 + + +#endif /* __PJ_COMPAT_OS_ANDROID_H__ */ + diff -aurN a/pjlib/include/pj/config.h b/pjlib/include/pj/config.h --- a/pjlib/include/pj/config.h 2009-03-23 11:57:55.000000000 +0000 +++ b/pjlib/include/pj/config.h 2009-04-06 21:21:05.000000000 +0100 @@ -1,4 +1,4 @@ -/* $Id: config.h 2527 2009-03-23 11:57:55Z bennylp $ */ +/* $Id: config.h 2394 2008-12-23 17:27:53Z bennylp $ */ /* * Copyright (C) 2008-2009 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2003-2008 Benny Prijono @@ -94,6 +94,14 @@ # define PJ_LINUX 1 # include +#elif defined(PJ_ANDROID) && PJ_ANDROID!=0 + /* + * Android + */ +# undef PJ_ANDROID +# define PJ_ANDROID 1 +# include + #elif defined(PJ_PALMOS) && PJ_PALMOS!=0 /* * Palm @@ -108,8 +116,7 @@ # define PJ_SUNOS 1 # include -#elif defined(PJ_DARWINOS) || defined(__MACOSX__) || \ - defined (__APPLE__) || defined (__MACH__) +#elif defined(PJ_DARWINOS) || defined(__MACOSX__) /* * MacOS X */ @@ -364,7 +371,7 @@ * Default: 4 */ #ifndef PJ_LOG_MAX_LEVEL -# define PJ_LOG_MAX_LEVEL 5 +# define PJ_LOG_MAX_LEVEL 6 #endif /** diff -aurN a/pjlib/include/pj/sock.h b/pjlib/include/pj/sock.h --- a/pjlib/include/pj/sock.h 2008-12-23 17:27:53.000000000 +0000 +++ b/pjlib/include/pj/sock.h 2009-04-06 21:21:05.000000000 +0100 @@ -474,7 +474,11 @@ #endif pj_uint16_t sin_port; /**< Transport layer port number. */ pj_in_addr sin_addr; /**< IP address. */ +#if defined(PJ_SOCKADDR_HAS_PAD) && PJ_SOCKADDR_HAS_PAD!=0 + char __pad[8]; /**< Padding. */ +#else char sin_zero[8]; /**< Padding. */ +#endif }; diff -aurN a/pjlib/src/pj/os_core_android.c b/pjlib/src/pj/os_core_android.c --- a/pjlib/src/pj/os_core_android.c 1970-01-01 01:00:00.000000000 +0100 +++ b/pjlib/src/pj/os_core_android.c 2009-04-06 21:21:05.000000000 +0100 @@ -0,0 +1,1800 @@ +/* $Id: os_core_unix.c 2395 2008-12-24 09:17:08Z bennylp $ */ +/* + * Copyright (C) 2008-2009 Teluu Inc. (http://www.teluu.com) + * Copyright (C) 2003-2008 Benny Prijono + * + * 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 + */ +/* + * Contributors: + * - Thanks for Zetron, Inc. (Phil Torre, ptorre@zetron.com) for donating + * the RTEMS port. + */ +#ifndef _GNU_SOURCE +# define _GNU_SOURCE +#endif +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#if defined(PJ_HAS_SEMAPHORE_H) && PJ_HAS_SEMAPHORE_H != 0 +# include +#endif + +#include // getpid() +#include // errno + +#include + +#define THIS_FILE "os_core_unix.c" + +#define SIGNATURE1 0xDEAFBEEF +#define SIGNATURE2 0xDEADC0DE + +struct pj_thread_t +{ + char obj_name[PJ_MAX_OBJ_NAME]; + pthread_t thread; + pj_thread_proc *proc; + void *arg; + pj_uint32_t signature1; + pj_uint32_t signature2; + + pj_mutex_t *suspended_mutex; + +#if defined(PJ_OS_HAS_CHECK_STACK) && PJ_OS_HAS_CHECK_STACK!=0 + pj_uint32_t stk_size; + pj_uint32_t stk_max_usage; + char *stk_start; + const char *caller_file; + int caller_line; +#endif +}; + +struct pj_atomic_t +{ + pj_mutex_t *mutex; + pj_atomic_value_t value; +}; + +struct pj_mutex_t +{ + pthread_mutex_t mutex; + char obj_name[PJ_MAX_OBJ_NAME]; +#if PJ_DEBUG + int nesting_level; + pj_thread_t *owner; + char owner_name[PJ_MAX_OBJ_NAME]; +#endif +}; + +#if defined(PJ_HAS_SEMAPHORE) && PJ_HAS_SEMAPHORE != 0 +struct pj_sem_t +{ + sem_t *sem; + char obj_name[PJ_MAX_OBJ_NAME]; +}; +#endif /* PJ_HAS_SEMAPHORE */ + +#if defined(PJ_HAS_EVENT_OBJ) && PJ_HAS_EVENT_OBJ != 0 +struct pj_event_t +{ + char obj_name[PJ_MAX_OBJ_NAME]; +}; +#endif /* PJ_HAS_EVENT_OBJ */ + + +#if PJ_HAS_THREADS + static pj_thread_t main_thread; + static long thread_tls_id; + static pj_mutex_t critical_section; +#else +# define MAX_THREADS 32 + static int tls_flag[MAX_THREADS]; + static void *tls[MAX_THREADS]; +#endif + +static unsigned atexit_count; +static void (*atexit_func[32])(void); + +static pj_status_t init_mutex(pj_mutex_t *mutex, const char *name, int type); + +/* + * pj_init(void). + * Init PJLIB! + */ +PJ_DEF(pj_status_t) pj_init(void) +{ + char dummy_guid[PJ_GUID_MAX_LENGTH]; + pj_str_t guid; + pj_status_t rc; + +#if PJ_HAS_THREADS + /* Init this thread's TLS. */ + if ((rc=pj_thread_init()) != 0) { + return rc; + } + + /* Critical section. */ + if ((rc=init_mutex(&critical_section, "critsec", PJ_MUTEX_RECURSE)) != 0) + return rc; + +#endif + + /* Initialize exception ID for the pool. + * Must do so after critical section is configured. + */ + rc = pj_exception_id_alloc("PJLIB/No memory", &PJ_NO_MEMORY_EXCEPTION); + if (rc != PJ_SUCCESS) + return rc; + + /* Init random seed. */ + /* Or probably not. Let application in charge of this */ + /* pj_srand( clock() ); */ + + /* Startup GUID. */ + guid.ptr = dummy_guid; + pj_generate_unique_string( &guid ); + + /* Startup timestamp */ +#if defined(PJ_HAS_HIGH_RES_TIMER) && PJ_HAS_HIGH_RES_TIMER != 0 + { + pj_timestamp dummy_ts; + if ((rc=pj_get_timestamp(&dummy_ts)) != 0) { + return rc; + } + } +#endif + + PJ_LOG(4,(THIS_FILE, "pjlib %s for POSIX initialized", + PJ_VERSION)); + + return PJ_SUCCESS; +} + +/* + * pj_atexit() + */ +PJ_DEF(pj_status_t) pj_atexit(void (*func)(void)) +{ + if (atexit_count >= PJ_ARRAY_SIZE(atexit_func)) + return PJ_ETOOMANY; + + atexit_func[atexit_count++] = func; + return PJ_SUCCESS; +} + +/* + * pj_shutdown(void) + */ +PJ_DEF(void) pj_shutdown() +{ + int i; + + /* Call atexit() functions */ + for (i=atexit_count-1; i>=0; --i) { + (*atexit_func[i])(); + } + atexit_count = 0; + + /* Free exception ID */ + if (PJ_NO_MEMORY_EXCEPTION != -1) { + pj_exception_id_free(PJ_NO_MEMORY_EXCEPTION); + PJ_NO_MEMORY_EXCEPTION = -1; + } + +#if PJ_HAS_THREADS + /* Destroy PJLIB critical section */ + pj_mutex_destroy(&critical_section); + + /* Free PJLIB TLS */ + if (thread_tls_id != -1) { + pj_thread_local_free(thread_tls_id); + thread_tls_id = -1; + } +#endif + + /* Clear static variables */ + pj_errno_clear_handlers(); +} + + +/* + * pj_getpid(void) + */ +PJ_DEF(pj_uint32_t) pj_getpid(void) +{ + PJ_CHECK_STACK(); + return getpid(); +} + +/* + * Check if this thread has been registered to PJLIB. + */ +PJ_DEF(pj_bool_t) pj_thread_is_registered(void) +{ +#if PJ_HAS_THREADS + return pj_thread_local_get(thread_tls_id) != 0; +#else + pj_assert("pj_thread_is_registered() called in non-threading mode!"); + return PJ_TRUE; +#endif +} + + +/* + * Get thread priority value for the thread. + */ +PJ_DEF(int) pj_thread_get_prio(pj_thread_t *thread) +{ +#if PJ_HAS_THREADS + struct sched_param param; + int policy; + int rc; + + rc = pthread_getschedparam (thread->thread, &policy, ¶m); + if (rc != 0) + return -1; + + return param.sched_priority; +#else + PJ_UNUSED_ARG(thread); + return 1; +#endif +} + + +/* + * Set the thread priority. + */ +PJ_DEF(pj_status_t) pj_thread_set_prio(pj_thread_t *thread, int prio) +{ +#if PJ_HAS_THREADS + struct sched_param param; + int policy; + int rc; + + rc = pthread_getschedparam (thread->thread, &policy, ¶m); + if (rc != 0) + return PJ_RETURN_OS_ERROR(rc); + + param.sched_priority = prio; + + rc = pthread_setschedparam(thread->thread, policy, ¶m); + if (rc != 0) + return PJ_RETURN_OS_ERROR(rc); + + return PJ_SUCCESS; +#else + PJ_UNUSED_ARG(thread); + PJ_UNUSED_ARG(prio); + pj_assert("pj_thread_set_prio() called in non-threading mode!"); + return 1; +#endif +} + + +/* + * Get the lowest priority value available on this system. + */ +PJ_DEF(int) pj_thread_get_prio_min(pj_thread_t *thread) +{ + struct sched_param param; + int policy; + int rc; + + rc = pthread_getschedparam(thread->thread, &policy, ¶m); + if (rc != 0) + return -1; + + return sched_get_priority_min(policy); +} + + +/* + * Get the highest priority value available on this system. + */ +PJ_DEF(int) pj_thread_get_prio_max(pj_thread_t *thread) +{ + struct sched_param param; + int policy; + int rc; + + rc = pthread_getschedparam(thread->thread, &policy, ¶m); + if (rc != 0) + return -1; + + return sched_get_priority_max(policy); +} + + +/* + * Get native thread handle + */ +PJ_DEF(void*) pj_thread_get_os_handle(pj_thread_t *thread) +{ + PJ_ASSERT_RETURN(thread, NULL); + +#if PJ_HAS_THREADS + return &thread->thread; +#else + pj_assert("pj_thread_is_registered() called in non-threading mode!"); + return NULL; +#endif +} + +/* + * pj_thread_register(..) + */ +PJ_DEF(pj_status_t) pj_thread_register ( const char *cstr_thread_name, + pj_thread_desc desc, + pj_thread_t **ptr_thread) +{ +#if PJ_HAS_THREADS + char stack_ptr; + pj_status_t rc; + pj_thread_t *thread = (pj_thread_t *)desc; + pj_str_t thread_name = pj_str((char*)cstr_thread_name); + + /* Size sanity check. */ + if (sizeof(pj_thread_desc) < sizeof(pj_thread_t)) { + pj_assert(!"Not enough pj_thread_desc size!"); + return PJ_EBUG; + } + + /* Warn if this thread has been registered before */ + if (pj_thread_local_get (thread_tls_id) != 0) { + // 2006-02-26 bennylp: + // This wouldn't work in all cases!. + // If thread is created by external module (e.g. sound thread), + // thread may be reused while the pool used for the thread descriptor + // has been deleted by application. + //*thread_ptr = (pj_thread_t*)pj_thread_local_get (thread_tls_id); + //return PJ_SUCCESS; + PJ_LOG(4,(THIS_FILE, "Info: possibly re-registering existing " + "thread")); + } + + /* On the other hand, also warn if the thread descriptor buffer seem to + * have been used to register other threads. + */ + pj_assert(thread->signature1 != SIGNATURE1 || + thread->signature2 != SIGNATURE2 || + (thread->thread == pthread_self())); + + /* Initialize and set the thread entry. */ + pj_bzero(desc, sizeof(struct pj_thread_t)); + thread->thread = pthread_self(); + thread->signature1 = SIGNATURE1; + thread->signature2 = SIGNATURE2; + + if(cstr_thread_name && pj_strlen(&thread_name) < sizeof(thread->obj_name)-1) + pj_ansi_snprintf(thread->obj_name, sizeof(thread->obj_name), + cstr_thread_name, thread->thread); + else + pj_ansi_snprintf(thread->obj_name, sizeof(thread->obj_name), + "thr%p", (void*)thread->thread); + + rc = pj_thread_local_set(thread_tls_id, thread); + if (rc != PJ_SUCCESS) { + pj_bzero(desc, sizeof(struct pj_thread_t)); + return rc; + } + +#if defined(PJ_OS_HAS_CHECK_STACK) && PJ_OS_HAS_CHECK_STACK!=0 + thread->stk_start = &stack_ptr; + thread->stk_size = 0xFFFFFFFFUL; + thread->stk_max_usage = 0; +#else + stack_ptr = '\0'; +#endif + + *ptr_thread = thread; + return PJ_SUCCESS; +#else + pj_thread_t *thread = (pj_thread_t*)desc; + *ptr_thread = thread; + return PJ_SUCCESS; +#endif +} + +/* + * pj_thread_init(void) + */ +pj_status_t pj_thread_init(void) +{ +#if PJ_HAS_THREADS + pj_status_t rc; + pj_thread_t *dummy; + + rc = pj_thread_local_alloc(&thread_tls_id ); + if (rc != PJ_SUCCESS) { + return rc; + } + return pj_thread_register("thr%p", (long*)&main_thread, &dummy); +#else + PJ_LOG(2,(THIS_FILE, "Thread init error. Threading is not enabled!")); + return PJ_EINVALIDOP; +#endif +} + +#if PJ_HAS_THREADS +/* + * thread_main() + * + * This is the main entry for all threads. + */ +static void *thread_main(void *param) +{ + pj_thread_t *rec = (pj_thread_t*)param; + void *result; + pj_status_t rc; + +#if defined(PJ_OS_HAS_CHECK_STACK) && PJ_OS_HAS_CHECK_STACK!=0 + rec->stk_start = (char*)&rec; +#endif + + /* Set current thread id. */ + rc = pj_thread_local_set(thread_tls_id, rec); + if (rc != PJ_SUCCESS) { + pj_assert(!"Thread TLS ID is not set (pj_init() error?)"); + } + + /* Check if suspension is required. */ + if (rec->suspended_mutex) { + pj_mutex_lock(rec->suspended_mutex); + pj_mutex_unlock(rec->suspended_mutex); + } + + PJ_LOG(6,(rec->obj_name, "Thread started")); + + /* Call user's entry! */ + result = (void*)(long)(*rec->proc)(rec->arg); + + /* Done. */ + PJ_LOG(6,(rec->obj_name, "Thread quitting")); + + return result; +} +#endif + +/* + * pj_thread_create(...) + */ +PJ_DEF(pj_status_t) pj_thread_create( pj_pool_t *pool, + const char *thread_name, + pj_thread_proc *proc, + void *arg, + pj_size_t stack_size, + unsigned flags, + pj_thread_t **ptr_thread) +{ +#if PJ_HAS_THREADS + pj_thread_t *rec; + pthread_attr_t thread_attr; + void *stack_addr; + int rc; + + PJ_UNUSED_ARG(stack_addr); + + PJ_CHECK_STACK(); + PJ_ASSERT_RETURN(pool && proc && ptr_thread, PJ_EINVAL); + + /* Create thread record and assign name for the thread */ + rec = (struct pj_thread_t*) pj_pool_zalloc(pool, sizeof(pj_thread_t)); + PJ_ASSERT_RETURN(rec, PJ_ENOMEM); + + /* Set name. */ + if (!thread_name) + thread_name = "thr%p"; + + if (strchr(thread_name, '%')) { + pj_ansi_snprintf(rec->obj_name, PJ_MAX_OBJ_NAME, thread_name, rec); + } else { + strncpy(rec->obj_name, thread_name, PJ_MAX_OBJ_NAME); + rec->obj_name[PJ_MAX_OBJ_NAME-1] = '\0'; + } + + /* Set default stack size */ + if (stack_size == 0) + stack_size = PJ_THREAD_DEFAULT_STACK_SIZE; + +#if defined(PJ_OS_HAS_CHECK_STACK) && PJ_OS_HAS_CHECK_STACK!=0 + rec->stk_size = stack_size; + rec->stk_max_usage = 0; +#endif + + /* Emulate suspended thread with mutex. */ + if (flags & PJ_THREAD_SUSPENDED) { + rc = pj_mutex_create_simple(pool, NULL, &rec->suspended_mutex); + if (rc != PJ_SUCCESS) { + return rc; + } + + pj_mutex_lock(rec->suspended_mutex); + } else { + pj_assert(rec->suspended_mutex == NULL); + } + + + /* Init thread attributes */ + pthread_attr_init(&thread_attr); + +#if defined(PJ_THREAD_SET_STACK_SIZE) && PJ_THREAD_SET_STACK_SIZE!=0 + /* Set thread's stack size */ + rc = pthread_attr_setstacksize(&thread_attr, stack_size); + if (rc != 0) + return PJ_RETURN_OS_ERROR(rc); +#endif /* PJ_THREAD_SET_STACK_SIZE */ + + +#if defined(PJ_THREAD_ALLOCATE_STACK) && PJ_THREAD_ALLOCATE_STACK!=0 + /* Allocate memory for the stack */ + stack_addr = pj_pool_alloc(pool, stack_size); + PJ_ASSERT_RETURN(stack_addr, PJ_ENOMEM); + + rc = pthread_attr_setstackaddr(&thread_attr, stack_addr); + if (rc != 0) + return PJ_RETURN_OS_ERROR(rc); +#endif /* PJ_THREAD_ALLOCATE_STACK */ + + + /* Create the thread. */ + rec->proc = proc; + rec->arg = arg; + rc = pthread_create( &rec->thread, &thread_attr, &thread_main, rec); + if (rc != 0) { + return PJ_RETURN_OS_ERROR(rc); + } + + *ptr_thread = rec; + + PJ_LOG(6, (rec->obj_name, "Thread created")); + return PJ_SUCCESS; +#else + pj_assert(!"Threading is disabled!"); + return PJ_EINVALIDOP; +#endif +} + +/* + * pj_thread-get_name() + */ +PJ_DEF(const char*) pj_thread_get_name(pj_thread_t *p) +{ +#if PJ_HAS_THREADS + pj_thread_t *rec = (pj_thread_t*)p; + + PJ_CHECK_STACK(); + PJ_ASSERT_RETURN(p, ""); + + return rec->obj_name; +#else + return ""; +#endif +} + +/* + * pj_thread_resume() + */ +PJ_DEF(pj_status_t) pj_thread_resume(pj_thread_t *p) +{ + pj_status_t rc; + + PJ_CHECK_STACK(); + PJ_ASSERT_RETURN(p, PJ_EINVAL); + + rc = pj_mutex_unlock(p->suspended_mutex); + + return rc; +} + +/* + * pj_thread_this() + */ +PJ_DEF(pj_thread_t*) pj_thread_this(void) +{ +#if PJ_HAS_THREADS + pj_thread_t *rec = (pj_thread_t*)pj_thread_local_get(thread_tls_id); + + if (rec == NULL) { + pj_assert(!"Calling pjlib from unknown/external thread. You must " + "register external threads with pj_thread_register() " + "before calling any pjlib functions."); + } + + /* + * MUST NOT check stack because this function is called + * by PJ_CHECK_STACK() itself!!! + * + */ + + return rec; +#else + pj_assert(!"Threading is not enabled!"); + return NULL; +#endif +} + +/* + * pj_thread_join() + */ +PJ_DEF(pj_status_t) pj_thread_join(pj_thread_t *p) +{ +#if PJ_HAS_THREADS + pj_thread_t *rec = (pj_thread_t *)p; + void *ret; + int result; + + PJ_CHECK_STACK(); + + PJ_LOG(6, (pj_thread_this()->obj_name, "Joining thread %s", p->obj_name)); + result = pthread_join( rec->thread, &ret); + + if (result == 0) + return PJ_SUCCESS; + else { + /* Calling pthread_join() on a thread that no longer exists and + * getting back ESRCH isn't an error (in this context). + * Thanks Phil Torre . + */ + return result==ESRCH ? PJ_SUCCESS : PJ_RETURN_OS_ERROR(result); + } +#else + PJ_CHECK_STACK(); + pj_assert(!"No multithreading support!"); + return PJ_EINVALIDOP; +#endif +} + +/* + * pj_thread_destroy() + */ +PJ_DEF(pj_status_t) pj_thread_destroy(pj_thread_t *p) +{ + PJ_CHECK_STACK(); + + /* Destroy mutex used to suspend thread */ + if (p->suspended_mutex) { + pj_mutex_destroy(p->suspended_mutex); + p->suspended_mutex = NULL; + } + + return PJ_SUCCESS; +} + +/* + * pj_thread_sleep() + */ +PJ_DEF(pj_status_t) pj_thread_sleep(unsigned msec) +{ +/* TODO: should change this to something like PJ_OS_HAS_NANOSLEEP */ +#if defined(PJ_RTEMS) && PJ_RTEMS!=0 + enum { NANOSEC_PER_MSEC = 1000000 }; + struct timespec req; + + PJ_CHECK_STACK(); + req.tv_sec = msec / 1000; + req.tv_nsec = (msec % 1000) * NANOSEC_PER_MSEC; + + if (nanosleep(&req, NULL) == 0) + return PJ_SUCCESS; + + return PJ_RETURN_OS_ERROR(pj_get_native_os_error()); +#else + PJ_CHECK_STACK(); + + pj_set_os_error(0); + + usleep(msec * 1000); + + /* MacOS X (reported on 10.5) seems to always set errno to ETIMEDOUT. + * It does so because usleep() is declared to return int, and we're + * supposed to check for errno only when usleep() returns non-zero. + * Unfortunately, usleep() is declared to return void in other platforms + * so it's not possible to always check for the return value (unless + * we add a detection routine in autoconf). + * + * As a workaround, here we check if ETIMEDOUT is returned and + * return successfully if it is. + */ + if (pj_get_native_os_error() == ETIMEDOUT) + return PJ_SUCCESS; + + return pj_get_os_error(); + +#endif /* PJ_RTEMS */ +} + +#if defined(PJ_OS_HAS_CHECK_STACK) && PJ_OS_HAS_CHECK_STACK!=0 +/* + * pj_thread_check_stack() + * Implementation for PJ_CHECK_STACK() + */ +PJ_DEF(void) pj_thread_check_stack(const char *file, int line) +{ + char stk_ptr; + pj_uint32_t usage; + pj_thread_t *thread = pj_thread_this(); + + /* Calculate current usage. */ + usage = (&stk_ptr > thread->stk_start) ? &stk_ptr - thread->stk_start : + thread->stk_start - &stk_ptr; + + /* Assert if stack usage is dangerously high. */ + pj_assert("STACK OVERFLOW!! " && (usage <= thread->stk_size - 128)); + + /* Keep statistic. */ + if (usage > thread->stk_max_usage) { + thread->stk_max_usage = usage; + thread->caller_file = file; + thread->caller_line = line; + } +} + +/* + * pj_thread_get_stack_max_usage() + */ +PJ_DEF(pj_uint32_t) pj_thread_get_stack_max_usage(pj_thread_t *thread) +{ + return thread->stk_max_usage; +} + +/* + * pj_thread_get_stack_info() + */ +PJ_DEF(pj_status_t) pj_thread_get_stack_info( pj_thread_t *thread, + const char **file, + int *line ) +{ + pj_assert(thread); + + *file = thread->caller_file; + *line = thread->caller_line; + return 0; +} + +#endif /* PJ_OS_HAS_CHECK_STACK */ + +/////////////////////////////////////////////////////////////////////////////// +/* + * pj_atomic_create() + */ +PJ_DEF(pj_status_t) pj_atomic_create( pj_pool_t *pool, + pj_atomic_value_t initial, + pj_atomic_t **ptr_atomic) +{ + pj_status_t rc; + pj_atomic_t *atomic_var; + + atomic_var = PJ_POOL_ZALLOC_T(pool, pj_atomic_t); + + PJ_ASSERT_RETURN(atomic_var, PJ_ENOMEM); + +#if PJ_HAS_THREADS + rc = pj_mutex_create(pool, "atm%p", PJ_MUTEX_SIMPLE, &atomic_var->mutex); + if (rc != PJ_SUCCESS) + return rc; +#endif + atomic_var->value = initial; + + *ptr_atomic = atomic_var; + return PJ_SUCCESS; +} + +/* + * pj_atomic_destroy() + */ +PJ_DEF(pj_status_t) pj_atomic_destroy( pj_atomic_t *atomic_var ) +{ + PJ_ASSERT_RETURN(atomic_var, PJ_EINVAL); +#if PJ_HAS_THREADS + return pj_mutex_destroy( atomic_var->mutex ); +#else + return 0; +#endif +} + +/* + * pj_atomic_set() + */ +PJ_DEF(void) pj_atomic_set(pj_atomic_t *atomic_var, pj_atomic_value_t value) +{ + PJ_CHECK_STACK(); + +#if PJ_HAS_THREADS + pj_mutex_lock( atomic_var->mutex ); +#endif + atomic_var->value = value; +#if PJ_HAS_THREADS + pj_mutex_unlock( atomic_var->mutex); +#endif +} + +/* + * pj_atomic_get() + */ +PJ_DEF(pj_atomic_value_t) pj_atomic_get(pj_atomic_t *atomic_var) +{ + pj_atomic_value_t oldval; + + PJ_CHECK_STACK(); + +#if PJ_HAS_THREADS + pj_mutex_lock( atomic_var->mutex ); +#endif + oldval = atomic_var->value; +#if PJ_HAS_THREADS + pj_mutex_unlock( atomic_var->mutex); +#endif + return oldval; +} + +/* + * pj_atomic_inc_and_get() + */ +PJ_DEF(pj_atomic_value_t) pj_atomic_inc_and_get(pj_atomic_t *atomic_var) +{ + pj_atomic_value_t new_value; + + PJ_CHECK_STACK(); + +#if PJ_HAS_THREADS + pj_mutex_lock( atomic_var->mutex ); +#endif + new_value = ++atomic_var->value; +#if PJ_HAS_THREADS + pj_mutex_unlock( atomic_var->mutex); +#endif + + return new_value; +} +/* + * pj_atomic_inc() + */ +PJ_DEF(void) pj_atomic_inc(pj_atomic_t *atomic_var) +{ + pj_atomic_inc_and_get(atomic_var); +} + +/* + * pj_atomic_dec_and_get() + */ +PJ_DEF(pj_atomic_value_t) pj_atomic_dec_and_get(pj_atomic_t *atomic_var) +{ + pj_atomic_value_t new_value; + + PJ_CHECK_STACK(); + +#if PJ_HAS_THREADS + pj_mutex_lock( atomic_var->mutex ); +#endif + new_value = --atomic_var->value; +#if PJ_HAS_THREADS + pj_mutex_unlock( atomic_var->mutex); +#endif + + return new_value; +} + +/* + * pj_atomic_dec() + */ +PJ_DEF(void) pj_atomic_dec(pj_atomic_t *atomic_var) +{ + pj_atomic_dec_and_get(atomic_var); +} + +/* + * pj_atomic_add_and_get() + */ +PJ_DEF(pj_atomic_value_t) pj_atomic_add_and_get( pj_atomic_t *atomic_var, + pj_atomic_value_t value ) +{ + pj_atomic_value_t new_value; + +#if PJ_HAS_THREADS + pj_mutex_lock(atomic_var->mutex); +#endif + + atomic_var->value += value; + new_value = atomic_var->value; + +#if PJ_HAS_THREADS + pj_mutex_unlock(atomic_var->mutex); +#endif + + return new_value; +} + +/* + * pj_atomic_add() + */ +PJ_DEF(void) pj_atomic_add( pj_atomic_t *atomic_var, + pj_atomic_value_t value ) +{ + pj_atomic_add_and_get(atomic_var, value); +} + +/////////////////////////////////////////////////////////////////////////////// +/* + * pj_thread_local_alloc() + */ +PJ_DEF(pj_status_t) pj_thread_local_alloc(long *p_index) +{ +#if PJ_HAS_THREADS + pthread_key_t key; + int rc; + + PJ_ASSERT_RETURN(p_index != NULL, PJ_EINVAL); + + pj_assert( sizeof(pthread_key_t) <= sizeof(long)); + if ((rc=pthread_key_create(&key, NULL)) != 0) + return PJ_RETURN_OS_ERROR(rc); + + *p_index = key; + return PJ_SUCCESS; +#else + int i; + for (i=0; i= 0 && index < MAX_THREADS); + tls[index] = value; + return PJ_SUCCESS; +#endif +} + +PJ_DEF(void*) pj_thread_local_get(long index) +{ + //Can't check stack because this function is called + //by PJ_CHECK_STACK() itself!!! + //PJ_CHECK_STACK(); +#if PJ_HAS_THREADS + return pthread_getspecific(index); +#else + pj_assert(index >= 0 && index < MAX_THREADS); + return tls[index]; +#endif +} + +/////////////////////////////////////////////////////////////////////////////// +PJ_DEF(void) pj_enter_critical_section(void) +{ +#if PJ_HAS_THREADS + pj_mutex_lock(&critical_section); +#endif +} + +PJ_DEF(void) pj_leave_critical_section(void) +{ +#if PJ_HAS_THREADS + pj_mutex_unlock(&critical_section); +#endif +} + + +/////////////////////////////////////////////////////////////////////////////// +#if defined(PJ_LINUX) && PJ_LINUX!=0 +PJ_BEGIN_DECL +PJ_DECL(int) pthread_mutexattr_settype(pthread_mutexattr_t*,int); +PJ_END_DECL +#endif + +static pj_status_t init_mutex(pj_mutex_t *mutex, const char *name, int type) +{ +#if PJ_HAS_THREADS + pthread_mutexattr_t attr; + int rc; + + PJ_CHECK_STACK(); + + rc = pthread_mutexattr_init(&attr); + if (rc != 0) + return PJ_RETURN_OS_ERROR(rc); + + if (type == PJ_MUTEX_SIMPLE) { +#if (defined(PJ_LINUX) && PJ_LINUX!=0) || \ + defined(PJ_HAS_PTHREAD_MUTEXATTR_SETTYPE) + rc = pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_FAST_NP); +#elif (defined(PJ_RTEMS) && PJ_RTEMS!=0) || \ + defined(PJ_PTHREAD_MUTEXATTR_T_HAS_RECURSIVE) + /* Nothing to do, default is simple */ +#else + rc = pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_NORMAL); +#endif + } else { +#if (defined(PJ_LINUX) && PJ_LINUX!=0) || \ + defined(PJ_HAS_PTHREAD_MUTEXATTR_SETTYPE) + rc = pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE_NP); +#elif (defined(PJ_RTEMS) && PJ_RTEMS!=0) || \ + defined(PJ_PTHREAD_MUTEXATTR_T_HAS_RECURSIVE) + // Phil Torre : + // The RTEMS implementation of POSIX mutexes doesn't include + // pthread_mutexattr_settype(), so what follows is a hack + // until I get RTEMS patched to support the set/get functions. + // + // More info: + // newlib's pthread also lacks pthread_mutexattr_settype(), + // but it seems to have mutexattr.recursive. + PJ_TODO(FIX_RTEMS_RECURSIVE_MUTEX_TYPE) + attr.recursive = 1; +#else + rc = pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE); +#endif + } + + if (rc != 0) { + return PJ_RETURN_OS_ERROR(rc); + } + + rc = pthread_mutex_init(&mutex->mutex, &attr); + if (rc != 0) { + return PJ_RETURN_OS_ERROR(rc); + } + + rc = pthread_mutexattr_destroy(&attr); + if (rc != 0) { + pj_status_t status = PJ_RETURN_OS_ERROR(rc); + pthread_mutex_destroy(&mutex->mutex); + return status; + } + +#if PJ_DEBUG + /* Set owner. */ + mutex->nesting_level = 0; + mutex->owner = NULL; +#endif + + /* Set name. */ + if (!name) { + name = "mtx%p"; + } + if (strchr(name, '%')) { + pj_ansi_snprintf(mutex->obj_name, PJ_MAX_OBJ_NAME, name, mutex); + } else { + strncpy(mutex->obj_name, name, PJ_MAX_OBJ_NAME); + mutex->obj_name[PJ_MAX_OBJ_NAME-1] = '\0'; + } + + PJ_LOG(6, (mutex->obj_name, "Mutex created")); + return PJ_SUCCESS; +#else /* PJ_HAS_THREADS */ + return PJ_SUCCESS; +#endif +} + +/* + * pj_mutex_create() + */ +PJ_DEF(pj_status_t) pj_mutex_create(pj_pool_t *pool, + const char *name, + int type, + pj_mutex_t **ptr_mutex) +{ +#if PJ_HAS_THREADS + pj_status_t rc; + pj_mutex_t *mutex; + + PJ_ASSERT_RETURN(pool && ptr_mutex, PJ_EINVAL); + + mutex = PJ_POOL_ALLOC_T(pool, pj_mutex_t); + PJ_ASSERT_RETURN(mutex, PJ_ENOMEM); + + if ((rc=init_mutex(mutex, name, type)) != PJ_SUCCESS) + return rc; + + *ptr_mutex = mutex; + return PJ_SUCCESS; +#else /* PJ_HAS_THREADS */ + *ptr_mutex = (pj_mutex_t*)1; + return PJ_SUCCESS; +#endif +} + +/* + * pj_mutex_create_simple() + */ +PJ_DEF(pj_status_t) pj_mutex_create_simple( pj_pool_t *pool, + const char *name, + pj_mutex_t **mutex ) +{ + return pj_mutex_create(pool, name, PJ_MUTEX_SIMPLE, mutex); +} + +/* + * pj_mutex_create_recursive() + */ +PJ_DEF(pj_status_t) pj_mutex_create_recursive( pj_pool_t *pool, + const char *name, + pj_mutex_t **mutex ) +{ + return pj_mutex_create(pool, name, PJ_MUTEX_RECURSE, mutex); +} + +/* + * pj_mutex_lock() + */ +PJ_DEF(pj_status_t) pj_mutex_lock(pj_mutex_t *mutex) +{ +#if PJ_HAS_THREADS + pj_status_t status; + + PJ_CHECK_STACK(); + PJ_ASSERT_RETURN(mutex, PJ_EINVAL); + +#if PJ_DEBUG + PJ_LOG(6,(mutex->obj_name, "Mutex: thread %s is waiting (mutex owner=%s)", + pj_thread_this()->obj_name, + mutex->owner_name)); +#else + PJ_LOG(6,(mutex->obj_name, "Mutex: thread %s is waiting", + pj_thread_this()->obj_name)); +#endif + + status = pthread_mutex_lock( &mutex->mutex ); + + +#if PJ_DEBUG + if (status == PJ_SUCCESS) { + mutex->owner = pj_thread_this(); + pj_ansi_strcpy(mutex->owner_name, mutex->owner->obj_name); + ++mutex->nesting_level; + } + + PJ_LOG(6,(mutex->obj_name, + (status==0 ? + "Mutex acquired by thread %s (level=%d)" : + "Mutex acquisition FAILED by %s (level=%d)"), + pj_thread_this()->obj_name, + mutex->nesting_level)); +#else + PJ_LOG(6,(mutex->obj_name, + (status==0 ? "Mutex acquired by thread %s" : "FAILED by %s"), + pj_thread_this()->obj_name)); +#endif + + if (status == 0) + return PJ_SUCCESS; + else + return PJ_RETURN_OS_ERROR(status); +#else /* PJ_HAS_THREADS */ + pj_assert( mutex == (pj_mutex_t*)1 ); + return PJ_SUCCESS; +#endif +} + +/* + * pj_mutex_unlock() + */ +PJ_DEF(pj_status_t) pj_mutex_unlock(pj_mutex_t *mutex) +{ +#if PJ_HAS_THREADS + pj_status_t status; + + PJ_CHECK_STACK(); + PJ_ASSERT_RETURN(mutex, PJ_EINVAL); + +#if PJ_DEBUG + pj_assert(mutex->owner == pj_thread_this()); + if (--mutex->nesting_level == 0) { + mutex->owner = NULL; + mutex->owner_name[0] = '\0'; + } + + PJ_LOG(6,(mutex->obj_name, "Mutex released by thread %s (level=%d)", + pj_thread_this()->obj_name, + mutex->nesting_level)); +#else + PJ_LOG(6,(mutex->obj_name, "Mutex released by thread %s", + pj_thread_this()->obj_name)); +#endif + + status = pthread_mutex_unlock( &mutex->mutex ); + if (status == 0) + return PJ_SUCCESS; + else + return PJ_RETURN_OS_ERROR(status); + +#else /* PJ_HAS_THREADS */ + pj_assert( mutex == (pj_mutex_t*)1 ); + return PJ_SUCCESS; +#endif +} + +/* + * pj_mutex_trylock() + */ +PJ_DEF(pj_status_t) pj_mutex_trylock(pj_mutex_t *mutex) +{ +#if PJ_HAS_THREADS + int status; + + PJ_CHECK_STACK(); + PJ_ASSERT_RETURN(mutex, PJ_EINVAL); + + PJ_LOG(6,(mutex->obj_name, "Mutex: thread %s is trying", + pj_thread_this()->obj_name)); + + status = pthread_mutex_trylock( &mutex->mutex ); + + if (status==0) { +#if PJ_DEBUG + mutex->owner = pj_thread_this(); + pj_ansi_strcpy(mutex->owner_name, mutex->owner->obj_name); + ++mutex->nesting_level; + + PJ_LOG(6,(mutex->obj_name, "Mutex acquired by thread %s (level=%d)", + pj_thread_this()->obj_name, + mutex->nesting_level)); +#else + PJ_LOG(6,(mutex->obj_name, "Mutex acquired by thread %s", + pj_thread_this()->obj_name)); +#endif + } else { + PJ_LOG(6,(mutex->obj_name, "Mutex: thread %s's trylock() failed", + pj_thread_this()->obj_name)); + } + + if (status==0) + return PJ_SUCCESS; + else + return PJ_RETURN_OS_ERROR(status); +#else /* PJ_HAS_THREADS */ + pj_assert( mutex == (pj_mutex_t*)1); + return PJ_SUCCESS; +#endif +} + +/* + * pj_mutex_destroy() + */ +PJ_DEF(pj_status_t) pj_mutex_destroy(pj_mutex_t *mutex) +{ + enum { RETRY = 4 }; + int status = 0; + unsigned retry; + + PJ_CHECK_STACK(); + PJ_ASSERT_RETURN(mutex, PJ_EINVAL); + +#if PJ_HAS_THREADS + PJ_LOG(6,(mutex->obj_name, "Mutex destroyed by thread %s", + pj_thread_this()->obj_name)); + + for (retry=0; retrymutex ); + if (status == PJ_SUCCESS) + break; + else if (retrymutex); + } + + if (status == 0) + return PJ_SUCCESS; + else { + pj_assert(!"Error destroying pthread_mutex"); + return PJ_RETURN_OS_ERROR(status); + } +#else + pj_assert( mutex == (pj_mutex_t*)1 ); + status = PJ_SUCCESS; + return status; +#endif +} + +#if PJ_DEBUG +PJ_DEF(pj_bool_t) pj_mutex_is_locked(pj_mutex_t *mutex) +{ +#if PJ_HAS_THREADS + return mutex->owner == pj_thread_this(); +#else + return 1; +#endif +} +#endif + +/////////////////////////////////////////////////////////////////////////////// +/* + * Include Read/Write mutex emulation for POSIX platforms that lack it (e.g. + * RTEMS). Otherwise use POSIX rwlock. + */ +#if defined(PJ_EMULATE_RWMUTEX) && PJ_EMULATE_RWMUTEX!=0 + /* We need semaphore functionality to emulate rwmutex */ +# if !defined(PJ_HAS_SEMAPHORE) || PJ_HAS_SEMAPHORE==0 +# error "Semaphore support needs to be enabled to emulate rwmutex" +# endif +# include "os_rwmutex.c" +#else +struct pj_rwmutex_t +{ + pthread_rwlock_t rwlock; +}; + +PJ_DEF(pj_status_t) pj_rwmutex_create(pj_pool_t *pool, const char *name, + pj_rwmutex_t **p_mutex) +{ + pj_rwmutex_t *rwm; + pj_status_t status; + + PJ_UNUSED_ARG(name); + + rwm = PJ_POOL_ALLOC_T(pool, pj_rwmutex_t); + PJ_ASSERT_RETURN(rwm, PJ_ENOMEM); + + status = pthread_rwlock_init(&rwm->rwlock, NULL); + if (status != 0) + return PJ_RETURN_OS_ERROR(status); + + *p_mutex = rwm; + return PJ_SUCCESS; +} + +/* + * Lock the mutex for reading. + * + */ +PJ_DEF(pj_status_t) pj_rwmutex_lock_read(pj_rwmutex_t *mutex) +{ + pj_status_t status; + + status = pthread_rwlock_rdlock(&mutex->rwlock); + if (status != 0) + return PJ_RETURN_OS_ERROR(status); + + return PJ_SUCCESS; +} + +/* + * Lock the mutex for writing. + * + */ +PJ_DEF(pj_status_t) pj_rwmutex_lock_write(pj_rwmutex_t *mutex) +{ + pj_status_t status; + + status = pthread_rwlock_wrlock(&mutex->rwlock); + if (status != 0) + return PJ_RETURN_OS_ERROR(status); + + return PJ_SUCCESS; +} + +/* + * Release read lock. + * + */ +PJ_DEF(pj_status_t) pj_rwmutex_unlock_read(pj_rwmutex_t *mutex) +{ + return pj_rwmutex_unlock_write(mutex); +} + +/* + * Release write lock. + * + */ +PJ_DEF(pj_status_t) pj_rwmutex_unlock_write(pj_rwmutex_t *mutex) +{ + pj_status_t status; + + status = pthread_rwlock_unlock(&mutex->rwlock); + if (status != 0) + return PJ_RETURN_OS_ERROR(status); + + return PJ_SUCCESS; +} + +/* + * Destroy reader/writer mutex. + * + */ +PJ_DEF(pj_status_t) pj_rwmutex_destroy(pj_rwmutex_t *mutex) +{ + pj_status_t status; + + status = pthread_rwlock_destroy(&mutex->rwlock); + if (status != 0) + return PJ_RETURN_OS_ERROR(status); + + return PJ_SUCCESS; +} + +#endif /* PJ_EMULATE_RWMUTEX */ + + +/////////////////////////////////////////////////////////////////////////////// +#if defined(PJ_HAS_SEMAPHORE) && PJ_HAS_SEMAPHORE != 0 + +/* + * pj_sem_create() + */ +PJ_DEF(pj_status_t) pj_sem_create( pj_pool_t *pool, + const char *name, + unsigned initial, + unsigned max, + pj_sem_t **ptr_sem) +{ +#if PJ_HAS_THREADS + pj_sem_t *sem; + + PJ_CHECK_STACK(); + PJ_ASSERT_RETURN(pool != NULL && ptr_sem != NULL, PJ_EINVAL); + + sem = PJ_POOL_ALLOC_T(pool, pj_sem_t); + PJ_ASSERT_RETURN(sem, PJ_ENOMEM); + +#if defined(PJ_DARWINOS) && PJ_DARWINOS!=0 + /* MacOS X doesn't support anonymous semaphore */ + { + char sem_name[PJ_GUID_MAX_LENGTH+1]; + pj_str_t nam; + + /* We should use SEM_NAME_LEN, but this doesn't seem to be + * declared anywhere? The value here is just from trial and error + * to get the longest name supported. + */ +# define MAX_SEM_NAME_LEN 23 + + /* Create a unique name for the semaphore. */ + if (PJ_GUID_STRING_LENGTH <= MAX_SEM_NAME_LEN) { + nam.ptr = sem_name; + pj_generate_unique_string(&nam); + sem_name[nam.slen] = '\0'; + } else { + pj_create_random_string(sem_name, MAX_SEM_NAME_LEN); + sem_name[MAX_SEM_NAME_LEN] = '\0'; + } + + /* Create semaphore */ + sem->sem = sem_open(sem_name, O_CREAT|O_EXCL, S_IRUSR|S_IWUSR, + initial); + if (sem->sem == SEM_FAILED) + return PJ_RETURN_OS_ERROR(pj_get_native_os_error()); + + /* And immediately release the name as we don't need it */ + sem_unlink(sem_name); + } +#else + sem->sem = PJ_POOL_ALLOC_T(pool, sem_t); + if (sem_init( sem->sem, 0, initial) != 0) + return PJ_RETURN_OS_ERROR(pj_get_native_os_error()); +#endif + + /* Set name. */ + if (!name) { + name = "sem%p"; + } + if (strchr(name, '%')) { + pj_ansi_snprintf(sem->obj_name, PJ_MAX_OBJ_NAME, name, sem); + } else { + strncpy(sem->obj_name, name, PJ_MAX_OBJ_NAME); + sem->obj_name[PJ_MAX_OBJ_NAME-1] = '\0'; + } + + PJ_LOG(6, (sem->obj_name, "Semaphore created")); + + *ptr_sem = sem; + return PJ_SUCCESS; +#else + *ptr_sem = (pj_sem_t*)1; + return PJ_SUCCESS; +#endif +} + +/* + * pj_sem_wait() + */ +PJ_DEF(pj_status_t) pj_sem_wait(pj_sem_t *sem) +{ +#if PJ_HAS_THREADS + int result; + + PJ_CHECK_STACK(); + PJ_ASSERT_RETURN(sem, PJ_EINVAL); + + PJ_LOG(6, (sem->obj_name, "Semaphore: thread %s is waiting", + pj_thread_this()->obj_name)); + + result = sem_wait( sem->sem ); + + if (result == 0) { + PJ_LOG(6, (sem->obj_name, "Semaphore acquired by thread %s", + pj_thread_this()->obj_name)); + } else { + PJ_LOG(6, (sem->obj_name, "Semaphore: thread %s FAILED to acquire", + pj_thread_this()->obj_name)); + } + + if (result == 0) + return PJ_SUCCESS; + else + return PJ_RETURN_OS_ERROR(pj_get_native_os_error()); +#else + pj_assert( sem == (pj_sem_t*) 1 ); + return PJ_SUCCESS; +#endif +} + +/* + * pj_sem_trywait() + */ +PJ_DEF(pj_status_t) pj_sem_trywait(pj_sem_t *sem) +{ +#if PJ_HAS_THREADS + int result; + + PJ_CHECK_STACK(); + PJ_ASSERT_RETURN(sem, PJ_EINVAL); + + result = sem_trywait( sem->sem ); + + if (result == 0) { + PJ_LOG(6, (sem->obj_name, "Semaphore acquired by thread %s", + pj_thread_this()->obj_name)); + } + if (result == 0) + return PJ_SUCCESS; + else + return PJ_RETURN_OS_ERROR(pj_get_native_os_error()); +#else + pj_assert( sem == (pj_sem_t*)1 ); + return PJ_SUCCESS; +#endif +} + +/* + * pj_sem_post() + */ +PJ_DEF(pj_status_t) pj_sem_post(pj_sem_t *sem) +{ +#if PJ_HAS_THREADS + int result; + PJ_LOG(6, (sem->obj_name, "Semaphore released by thread %s", + pj_thread_this()->obj_name)); + result = sem_post( sem->sem ); + + if (result == 0) + return PJ_SUCCESS; + else + return PJ_RETURN_OS_ERROR(pj_get_native_os_error()); +#else + pj_assert( sem == (pj_sem_t*) 1); + return PJ_SUCCESS; +#endif +} + +/* + * pj_sem_destroy() + */ +PJ_DEF(pj_status_t) pj_sem_destroy(pj_sem_t *sem) +{ +#if PJ_HAS_THREADS + int result; + + PJ_CHECK_STACK(); + PJ_ASSERT_RETURN(sem, PJ_EINVAL); + + PJ_LOG(6, (sem->obj_name, "Semaphore destroyed by thread %s", + pj_thread_this()->obj_name)); +#if defined(PJ_DARWINOS) && PJ_DARWINOS!=0 + result = sem_close( sem->sem ); +#else + result = sem_destroy( sem->sem ); +#endif + + if (result == 0) + return PJ_SUCCESS; + else + return PJ_RETURN_OS_ERROR(pj_get_native_os_error()); +#else + pj_assert( sem == (pj_sem_t*) 1 ); + return PJ_SUCCESS; +#endif +} + +#endif /* PJ_HAS_SEMAPHORE */ + +/////////////////////////////////////////////////////////////////////////////// +#if defined(PJ_HAS_EVENT_OBJ) && PJ_HAS_EVENT_OBJ != 0 + +/* + * pj_event_create() + */ +PJ_DEF(pj_status_t) pj_event_create(pj_pool_t *pool, const char *name, + pj_bool_t manual_reset, pj_bool_t initial, + pj_event_t **ptr_event) +{ + pj_assert(!"Not supported!"); + PJ_UNUSED_ARG(pool); + PJ_UNUSED_ARG(name); + PJ_UNUSED_ARG(manual_reset); + PJ_UNUSED_ARG(initial); + PJ_UNUSED_ARG(ptr_event); + return PJ_EINVALIDOP; +} + +/* + * pj_event_wait() + */ +PJ_DEF(pj_status_t) pj_event_wait(pj_event_t *event) +{ + PJ_UNUSED_ARG(event); + return PJ_EINVALIDOP; +} + +/* + * pj_event_trywait() + */ +PJ_DEF(pj_status_t) pj_event_trywait(pj_event_t *event) +{ + PJ_UNUSED_ARG(event); + return PJ_EINVALIDOP; +} + +/* + * pj_event_set() + */ +PJ_DEF(pj_status_t) pj_event_set(pj_event_t *event) +{ + PJ_UNUSED_ARG(event); + return PJ_EINVALIDOP; +} + +/* + * pj_event_pulse() + */ +PJ_DEF(pj_status_t) pj_event_pulse(pj_event_t *event) +{ + PJ_UNUSED_ARG(event); + return PJ_EINVALIDOP; +} + +/* + * pj_event_reset() + */ +PJ_DEF(pj_status_t) pj_event_reset(pj_event_t *event) +{ + PJ_UNUSED_ARG(event); + return PJ_EINVALIDOP; +} + +/* + * pj_event_destroy() + */ +PJ_DEF(pj_status_t) pj_event_destroy(pj_event_t *event) +{ + PJ_UNUSED_ARG(event); + return PJ_EINVALIDOP; +} + +#endif /* PJ_HAS_EVENT_OBJ */ + +/////////////////////////////////////////////////////////////////////////////// +#if defined(PJ_TERM_HAS_COLOR) && PJ_TERM_HAS_COLOR != 0 +/* + * Terminal + */ + +/** + * Set terminal color. + */ +PJ_DEF(pj_status_t) pj_term_set_color(pj_color_t color) +{ + /* put bright prefix to ansi_color */ + char ansi_color[12] = "\033[01;3"; + + if (color & PJ_TERM_COLOR_BRIGHT) { + color ^= PJ_TERM_COLOR_BRIGHT; + } else { + strcpy(ansi_color, "\033[00;3"); + } + + switch (color) { + case 0: + /* black color */ + strcat(ansi_color, "0m"); + break; + case PJ_TERM_COLOR_R: + /* red color */ + strcat(ansi_color, "1m"); + break; + case PJ_TERM_COLOR_G: + /* green color */ + strcat(ansi_color, "2m"); + break; + case PJ_TERM_COLOR_B: + /* blue color */ + strcat(ansi_color, "4m"); + break; + case PJ_TERM_COLOR_R | PJ_TERM_COLOR_G: + /* yellow color */ + strcat(ansi_color, "3m"); + break; + case PJ_TERM_COLOR_R | PJ_TERM_COLOR_B: + /* magenta color */ + strcat(ansi_color, "5m"); + break; + case PJ_TERM_COLOR_G | PJ_TERM_COLOR_B: + /* cyan color */ + strcat(ansi_color, "6m"); + break; + case PJ_TERM_COLOR_R | PJ_TERM_COLOR_G | PJ_TERM_COLOR_B: + /* white color */ + strcat(ansi_color, "7m"); + break; + default: + /* default console color */ + strcpy(ansi_color, "\033[00m"); + break; + } + + fputs(ansi_color, stdout); + + return PJ_SUCCESS; +} + +/** + * Get current terminal foreground color. + */ +PJ_DEF(pj_color_t) pj_term_get_color(void) +{ + return 0; +} + +#endif /* PJ_TERM_HAS_COLOR */ + diff -aurN a/pjlib/src/pjlib-test/sock.c b/pjlib/src/pjlib-test/sock.c --- a/pjlib/src/pjlib-test/sock.c 2008-12-23 17:27:53.000000000 +0000 +++ b/pjlib/src/pjlib-test/sock.c 2009-04-06 21:21:05.000000000 +0100 @@ -154,7 +154,11 @@ */ pj_sockaddr_in_init(&addr2, 0, 1000); pj_bzero(zero, sizeof(zero)); +#if defined(PJ_SOCKADDR_HAS_PAD) && PJ_SOCKADDR_HAS_PAD!=0 + if (pj_memcmp(addr2.__pad, zero, sizeof(addr2.__pad)) != 0) +#else if (pj_memcmp(addr2.sin_zero, zero, sizeof(addr2.sin_zero)) != 0) +#endif return -35; /* pj_gethostname() */ diff -aurN a/pjmedia/build/Makefile b/pjmedia/build/Makefile --- a/pjmedia/build/Makefile 2008-10-07 21:23:01.000000000 +0100 +++ b/pjmedia/build/Makefile 2009-04-06 21:21:05.000000000 +0100 @@ -64,6 +64,7 @@ wsola.o $(SOUND_OBJS) $(NULLSOUND_OBJS) export PJMEDIA_CFLAGS += $(_CFLAGS) +export PJMEDIA_CXXFLAGS += $(_CFLAGS) ############################################################################### diff -aurN a/pjmedia/build/os-android.mak b/pjmedia/build/os-android.mak --- a/pjmedia/build/os-android.mak 1970-01-01 01:00:00.000000000 +0100 +++ b/pjmedia/build/os-android.mak 2009-04-06 21:21:05.000000000 +0100 @@ -0,0 +1,101 @@ +# Linux + +# Define the desired sound device backend +# Valid values are: +# - pa_unix: PortAudio on Unix (OSS or ALSA) +# - pa_darwinos: PortAudio on MacOSX (CoreAudio) +# - pa_old_darwinos: PortAudio on MacOSX (old CoreAudio, for OSX 10.2) +# - pa_win32: PortAudio on Win32 (WMME) +# - ds: Win32 DirectSound (dsound.c) +# - android: Android audio (AudioFlinger interface) +# - null: Null sound device (nullsound.c) +AC_PJMEDIA_SND=android + +# +# Codecs +# +AC_NO_G711_CODEC=0 +AC_NO_L16_CODEC=0 +AC_NO_GSM_CODEC=0 +AC_NO_SPEEX_CODEC=0 +AC_NO_ILBC_CODEC=0 +AC_NO_G722_CODEC=0 + +export CODEC_OBJS= + +ifeq ($(AC_NO_G711_CODEC),1) +export CFLAGS += -DPJMEDIA_HAS_G711_CODEC=0 +else +export CODEC_OBJS += +endif + +ifeq ($(AC_NO_L16_CODEC),1) +export CFLAGS += -DPJMEDIA_HAS_L16_CODEC=0 +else +export CODEC_OBJS += l16.o +endif + +ifeq ($(AC_NO_GSM_CODEC),1) +export CFLAGS += -DPJMEDIA_HAS_GSM_CODEC=0 +else +export CODEC_OBJS += gsm.o +endif + +ifeq ($(AC_NO_SPEEX_CODEC),1) +export CFLAGS += -DPJMEDIA_HAS_SPEEX_CODEC=0 +else +export CFLAGS += -I$(THIRD_PARTY)/build/speex -I$(THIRD_PARTY)/speex/include +export CODEC_OBJS += speex_codec.o + +ifeq (1,1) +export PJMEDIA_OBJS += echo_speex.o +endif + +endif + +ifeq ($(AC_NO_ILBC_CODEC),1) +export CFLAGS += -DPJMEDIA_HAS_ILBC_CODEC=0 +else +export CODEC_OBJS += ilbc.o +endif + +ifeq ($(AC_NO_G722_CODEC),1) +export CFLAGS += -DPJMEDIA_HAS_G722_CODEC=0 +else +export CODEC_OBJS += g722.o g722/g722_enc.o g722/g722_dec.o +endif + + +# +# PortAudio +# +ifneq ($(findstring pa,$(AC_PJMEDIA_SND)),) +export CFLAGS += -I$(THIRD_PARTY)/build/portaudio -I$(THIRD_PARTY)/portaudio/include -DPJMEDIA_SOUND_IMPLEMENTATION=PJMEDIA_SOUND_PORTAUDIO_SOUND +export SOUND_OBJS = pasound.o +endif + +# +# Win32 DirectSound +# +ifeq ($(AC_PJMEDIA_SND),ds) +export SOUND_OBJS = dsound.o +export CFLAGS += -DPJMEDIA_SOUND_IMPLEMENTATION=PJMEDIA_SOUND_WIN32_DIRECT_SOUND +endif + +# +# Android (AudioFlinger interface) +# +ifeq ($(AC_PJMEDIA_SND),android) +export SOUND_OBJS = android_sound.o +export CFLAGS += -DPJMEDIA_SOUND_IMPLEMENTATION=PJMEDIA_SOUND_ANDROID_DIRECT_SOUND +endif + +# +# Last resort, null sound device +# +ifeq ($(AC_PJMEDIA_SND),null) +export SOUND_OBJS = nullsound.o +export CFLAGS += -DPJMEDIA_SOUND_IMPLEMENTATION=PJMEDIA_SOUND_NULL_SOUND +endif + + diff -aurN a/pjmedia/include/pjmedia/config.h b/pjmedia/include/pjmedia/config.h --- a/pjmedia/include/pjmedia/config.h 2008-12-23 17:27:53.000000000 +0000 +++ b/pjmedia/include/pjmedia/config.h 2009-04-06 21:21:05.000000000 +0100 @@ -60,6 +60,8 @@ /** Constant for Win32 MME sound backend. */ #define PJMEDIA_SOUND_WIN32_MME_SOUND 3 +#define PJMEDIA_SOUND_ANDROID_DIRECT_SOUND 4 + /** When this is set, pjmedia will not provide any sound device backend. * Application will have to provide its own sound device backend * and link the application with it. @@ -175,7 +177,7 @@ * Default: 6 */ #ifndef PJMEDIA_SOUND_BUFFER_COUNT -# define PJMEDIA_SOUND_BUFFER_COUNT 6 +# define PJMEDIA_SOUND_BUFFER_COUNT 12 #endif diff -aurN a/pjmedia/src/pjmedia/android_sound.cpp b/pjmedia/src/pjmedia/android_sound.cpp --- a/pjmedia/src/pjmedia/android_sound.cpp 1970-01-01 01:00:00.000000000 +0100 +++ b/pjmedia/src/pjmedia/android_sound.cpp 2009-04-06 21:21:27.000000000 +0100 @@ -0,0 +1,546 @@ +/* + * Copyright (C) 2009 Jurij Smakov + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#if PJMEDIA_SOUND_IMPLEMENTATION==PJMEDIA_SOUND_ANDROID_DIRECT_SOUND + +#define THIS_FILE "android_sound.cpp" +#define POOL_NAME "AndroidSound" +#define POOL_SIZE 8192 +#define POOL_INC 4096 + +#define ANDROID_CHANNELS 1 +#define ANDROID_BUFFERS 2 +#define ANDROID_FLAGS 0 +#define ANDROID_BYTES_PER_SAMPLE 2 +#define ANDROID_BITS_PER_SAMPLE (ANDROID_BYTES_PER_SAMPLE * 8) +#define ANDROID_CLOCK_RATE 8000 + +static pjmedia_snd_dev_info android_snd_dev_info = +{ + "Android Sound Device", + ANDROID_CHANNELS, /* input */ + ANDROID_CHANNELS, /* output */ + ANDROID_CLOCK_RATE +}; + +/* + * PJMEDIA Sound Stream instance + */ +struct pjmedia_snd_stream +{ + pj_pool_t *pool; + + pj_lock_t *lock; + + pjmedia_dir dir; + unsigned clock_rate; + unsigned channel_count; + unsigned samples_per_frame; + unsigned bytes_per_frame; + void *user_data; + + int saved_audio_mode; + unsigned saved_audio_routing; + + pjmedia_snd_rec_cb rec_cb; + pjmedia_snd_play_cb play_cb; + + pj_uint32_t rec_timestamp; + pj_uint32_t play_timestamp; + + pj_int16_t *play_saved_buf; + pj_int16_t *rec_saved_buf; + + unsigned play_saved_samples; + unsigned rec_saved_samples; + + pj_sem_t *play_sem; + pj_sem_t *rec_sem; + + android::AudioRecord *ar; + android::AudioTrack *at; +}; + +static pj_pool_factory *snd_pool_factory; + +/* + * AudioRecord callback. + */ +static bool android_rec_cb(void* user, const android::AudioRecord::Buffer& info) { + pj_status_t status; + unsigned src_pos = 0; + unsigned samples_to_copy = 0; + unsigned count = 0; + pjmedia_snd_stream *strm = (pjmedia_snd_stream*) user; + + pj_int16_t *src = (pj_int16_t *) info.raw; + /* + * Apparently, the Buffer we are getting in this function contains + * inconsistent information, for example info.frameCount may be 1200 + * while info.size is only 2048 (corresponding to 1024 samples). To + * avoid problems we calculate the number of input samples ourselves + * based on buffer size, instead of using info.frameCount. + */ + unsigned input_samples = info.size / ANDROID_BYTES_PER_SAMPLE; + + /* + * Copy the samples from AudioRecord buffer, appending them to the ones + * left over from last time, to fill the rec_saved_buffer completely. + */ + // PJ_LOG(4, (THIS_FILE, "android_rec_cb: entry, samples %d, size %d", input_samples, info.size)); + count = strm->rec_timestamp; + + samples_to_copy = strm->samples_per_frame - strm->rec_saved_samples; + if(samples_to_copy) { + pjmedia_copy_samples(strm->rec_saved_buf + strm->rec_saved_samples, + src, samples_to_copy); + strm->rec_timestamp += strm->samples_per_frame; + status = (*strm->rec_cb)(strm->user_data, + strm->rec_timestamp, + (void *) strm->rec_saved_buf, + strm->samples_per_frame * ANDROID_BYTES_PER_SAMPLE); + src_pos += samples_to_copy; + } + + /* Continue copying until we have data to fill a pjmedia frame. */ + while(src_pos + strm->samples_per_frame <= input_samples) { + strm->rec_timestamp += strm->samples_per_frame; + status = (*strm->rec_cb)(strm->user_data, + strm->rec_timestamp, + (void *) (src + src_pos), + strm->samples_per_frame * ANDROID_BYTES_PER_SAMPLE); + src_pos += strm->samples_per_frame; + } + + /* Save the rest of the samples in the buffer for next time. */ + samples_to_copy = input_samples - src_pos; + if (samples_to_copy) { + pjmedia_copy_samples(strm->rec_saved_buf, src + src_pos, samples_to_copy); + strm->rec_saved_samples = samples_to_copy; + } else { + strm->rec_saved_samples = 0; + } + // PJ_LOG(4, (THIS_FILE, "android_rec_cb: exit, %d samples", strm->rec_timestamp - count)); + return true; +} + +/* + * AudioTrack callback. + * + * This function is called by the audio subsystem, whenever an output buffer + * becomes available. Currently the size of each buffer is 2400 bytes per + * channel, which translates to 150ms of latency for a single channel, 16-bit + * sample size, and 8kHz sampling frequency. There are two buffers allocated, + * so whenever we are called, we should fill the given buffer with samples, + * while the other one is being played back. New data is obtained by executing + * pjlib callback, which returns strm->samples_per_frame samples at a time. In + * Android terminology "frame" means a 2-byte "sample", so info.frameCount is + * simply a number of samples which will fit into the buffer. + * + */ + +static void android_play_cb(void* user, const android::AudioTrack::Buffer& info) { + pj_status_t status; + unsigned dst_pos = 0; + unsigned samples_to_copy = 0; + pjmedia_snd_stream *strm = (pjmedia_snd_stream *) user; + + + pj_int16_t *dst = (pj_int16_t*) info.raw; + + // PJ_LOG(4, (THIS_FILE, "android_play_cb: entry, framecount %d, size %d", info.frameCount, info.size)); + /* Copy the stuff left from the previous invocation into the buffer */ + if(strm->play_saved_samples) { + strm->play_timestamp += strm->play_saved_samples; + pjmedia_copy_samples(dst, strm->play_saved_buf, strm->play_saved_samples); + dst_pos += strm->play_saved_samples; + } + + /* Write directly into buffer while we can fit complete frames there */ + while(dst_pos + strm->samples_per_frame <= info.frameCount) { + strm->play_timestamp += strm->samples_per_frame; + status = (*strm->play_cb)(strm->user_data, + strm->play_timestamp, + (void *) (dst + dst_pos), + strm->samples_per_frame * ANDROID_BYTES_PER_SAMPLE); + dst_pos += strm->samples_per_frame; + } + + /* How many samples do we need to fill the buffer up? */ + samples_to_copy = info.frameCount - dst_pos; + if(samples_to_copy) { + status = (*strm->play_cb)(strm->user_data, + strm->play_timestamp, + (void *) strm->play_saved_buf, + strm->samples_per_frame * ANDROID_BYTES_PER_SAMPLE); + /* Fill the buffer completely */ + strm->play_timestamp += samples_to_copy; + pjmedia_copy_samples(dst + dst_pos, strm->play_saved_buf, samples_to_copy); + /* Save remaining ones for the next iteration */ + strm->play_saved_samples = strm->samples_per_frame - samples_to_copy; + pjmedia_move_samples(strm->play_saved_buf, + strm->play_saved_buf + samples_to_copy, + strm->play_saved_samples); + } else { + /* Nothing left to copy */ + strm->play_saved_samples = 0; + } + // PJ_LOG(4, (THIS_FILE, "android_play_cb: exit")); +} + + +/* + * Initialize sound subsystem. + */ +PJ_DEF(pj_status_t) pjmedia_snd_init(pj_pool_factory *factory) +{ + snd_pool_factory = factory; + return PJ_SUCCESS; +} + +/* + * Get device count. + */ +PJ_DEF(int) pjmedia_snd_get_dev_count(void) +{ + /* Always return 1 */ + return 1; +} + +/* + * Get device info. + */ +PJ_DEF(const pjmedia_snd_dev_info*) pjmedia_snd_get_dev_info(unsigned index) +{ + /* Always return the default sound device */ + if (index == (unsigned)-1) + index = 0; + + PJ_ASSERT_RETURN(index==0, NULL); + return &android_snd_dev_info; +} + + + +/* + * Open sound recorder stream. + */ +PJ_DEF(pj_status_t) android_pjmedia_snd_open( + pjmedia_dir dir, + int rec_id, + int play_id, + unsigned clock_rate, + unsigned channel_count, + unsigned samples_per_frame, + unsigned bits_per_sample, + pjmedia_snd_rec_cb rec_cb, + pjmedia_snd_play_cb play_cb, + void *user_data, + pjmedia_snd_stream **p_snd_strm) +{ + pj_status_t status; + pj_pool_t *pool; + pjmedia_snd_stream *strm; + + PJ_LOG(4, (THIS_FILE, "snd_open: clock_rate=%d", clock_rate)); + PJ_LOG(4, (THIS_FILE, "snd_open: channel_count=%d", channel_count)); + PJ_LOG(4, (THIS_FILE, "snd_open: bits_per_sample=%d", bits_per_sample)); + PJ_LOG(4, (THIS_FILE, "snd_open: samples_per_frame=%d", samples_per_frame)); + if (clock_rate != ANDROID_CLOCK_RATE + || channel_count != ANDROID_CHANNELS + || bits_per_sample != ANDROID_BITS_PER_SAMPLE + || !samples_per_frame + || !p_snd_strm) + return PJ_EINVAL; + + pool = pj_pool_create(snd_pool_factory, POOL_NAME, POOL_SIZE, POOL_INC, NULL); + if (!pool) + return PJ_ENOMEM; + + strm = (pjmedia_snd_stream*) pj_pool_zalloc(pool, sizeof(pjmedia_snd_stream)); + strm->dir = dir; + strm->pool = pool; + strm->clock_rate = clock_rate; + strm->channel_count = channel_count; + strm->samples_per_frame = samples_per_frame; + strm->bytes_per_frame = ANDROID_BYTES_PER_SAMPLE * samples_per_frame; + strm->user_data = user_data; + strm->rec_timestamp = 0; + strm->play_timestamp = 0; + strm->rec_saved_buf = (pj_int16_t*) pj_pool_zalloc(pool, strm->bytes_per_frame); + strm->play_saved_buf = (pj_int16_t*) pj_pool_zalloc(pool, strm->bytes_per_frame); + strm->play_saved_samples = 0; + strm->rec_saved_samples = 0; + pj_lock_create_simple_mutex(pool, "android_callback_lock", &strm->lock); + + if(dir & PJMEDIA_DIR_CAPTURE) { + if (rec_id == -1) rec_id = 0; + PJ_ASSERT_RETURN(rec_id == 0, PJ_EINVAL); + strm->rec_cb = rec_cb; + strm->ar = new android::AudioRecord(android::AudioRecord::MIC_INPUT, + clock_rate, + android::AudioSystem::PCM_16_BIT, + channel_count, + ANDROID_BUFFERS, + ANDROID_FLAGS, + &android_rec_cb, + (void *) strm); + if(!strm->ar) { + pj_pool_release(pool); + return PJ_ENOMEM; + } + status = pj_sem_create (pool, "android_rec_sem", 1, 1, &strm->rec_sem); + if(PJ_SUCCESS != status) { + pj_pool_release(pool); + return PJ_ENOMEM; + } + } + + if(dir & PJMEDIA_DIR_PLAYBACK) { + if (play_id == -1) play_id = 0; + PJ_ASSERT_RETURN(play_id == 0, PJ_EINVAL); + strm->play_cb = play_cb; + strm->at = new android::AudioTrack(android::AudioTrack::SYSTEM, + clock_rate, + android::AudioSystem::PCM_16_BIT, + channel_count, + ANDROID_BUFFERS, + ANDROID_FLAGS, + &android_play_cb, + (void *) strm); + if(!strm->at) { + pj_pool_release(pool); + return PJ_ENOMEM; + } + status = pj_sem_create (pool, "android_play_sem", 1, 1, &strm->play_sem); + if(PJ_SUCCESS != status) { + pj_pool_release(pool); + return PJ_ENOMEM; + } + } + + /* Save the current audio routing setting, then switch it to earpiece. */ + android::AudioSystem::getMode(&strm->saved_audio_mode); + android::AudioSystem::getRouting(strm->saved_audio_mode, &strm->saved_audio_routing); + android::AudioSystem::setRouting(strm->saved_audio_mode, + android::AudioSystem::ROUTE_EARPIECE, + android::AudioSystem::ROUTE_ALL); + + + *p_snd_strm = strm; + return PJ_SUCCESS; +} + +PJ_DEF(pj_status_t) pjmedia_snd_open_rec(int index, + unsigned clock_rate, + unsigned channel_count, + unsigned samples_per_frame, + unsigned bits_per_sample, + pjmedia_snd_rec_cb rec_cb, + void *user_data, + pjmedia_snd_stream **p_snd_strm) +{ + PJ_LOG(4, (THIS_FILE, "pjmedia_snd_open_rec called")); + return android_pjmedia_snd_open(PJMEDIA_DIR_CAPTURE, + index, + 0, + clock_rate, + channel_count, + samples_per_frame, + bits_per_sample, + rec_cb, + NULL, + user_data, + p_snd_strm); +} + +PJ_DEF(pj_status_t) pjmedia_snd_open_player(int index, + unsigned clock_rate, + unsigned channel_count, + unsigned samples_per_frame, + unsigned bits_per_sample, + pjmedia_snd_play_cb play_cb, + void *user_data, + pjmedia_snd_stream **p_snd_strm) +{ + PJ_LOG(4, (THIS_FILE, "pjmedia_snd_open_player called")); + return android_pjmedia_snd_open(PJMEDIA_DIR_PLAYBACK, + 0, + index, + clock_rate, + channel_count, + samples_per_frame, + bits_per_sample, + NULL, + play_cb, + user_data, + p_snd_strm); +} + +PJ_DEF(pj_status_t) pjmedia_snd_open( + int rec_id, + int play_id, + unsigned clock_rate, + unsigned channel_count, + unsigned samples_per_frame, + unsigned bits_per_sample, + pjmedia_snd_rec_cb rec_cb, + pjmedia_snd_play_cb play_cb, + void *user_data, + pjmedia_snd_stream **p_snd_strm) +{ + PJ_LOG(4, (THIS_FILE, "pjmedia_snd_open called")); + return android_pjmedia_snd_open(PJMEDIA_DIR_CAPTURE_PLAYBACK, + rec_id, + play_id, + clock_rate, + channel_count, + samples_per_frame, + bits_per_sample, + rec_cb, + play_cb, + user_data, + p_snd_strm); +} + + +/* + * Get stream info. + */ + +PJ_DEF(pj_status_t) pjmedia_snd_stream_get_info(pjmedia_snd_stream *strm, + pjmedia_snd_stream_info *pi) +{ + PJ_ASSERT_RETURN(strm && pi, PJ_EINVAL); + + pj_bzero(pi, sizeof(*pi)); + pi->dir = strm->dir; + pi->play_id = 0; + pi->rec_id = 0; + pi->clock_rate = strm->clock_rate; + pi->channel_count = strm->channel_count; + pi->samples_per_frame = strm->samples_per_frame; + pi->bits_per_sample = ANDROID_BITS_PER_SAMPLE; + pi->rec_latency = 1280; + pi->play_latency = 1280; + + return PJ_SUCCESS; +} + +PJ_DEF(pj_status_t) pjmedia_snd_stream_start(pjmedia_snd_stream *stream) +{ + PJ_ASSERT_RETURN(stream != NULL, PJ_EINVAL); + + if (stream->at) { + PJ_LOG(4, (THIS_FILE, "starting playback stream.")); + stream->at->start(); + } + + if (stream->ar) { + PJ_LOG(4, (THIS_FILE, "starting recording stream.")); + stream->ar->start(); + } + + return PJ_SUCCESS; +} + + +PJ_DEF(pj_status_t) pjmedia_snd_stream_stop(pjmedia_snd_stream *stream) +{ + PJ_ASSERT_RETURN(stream != NULL, PJ_EINVAL); + + if (stream->ar) { + PJ_LOG(4, (THIS_FILE, "stopping recording stream.")); + stream->ar->stop(); + } + + if (stream->at) { + PJ_LOG(4, (THIS_FILE, "stopping playback stream.")); + stream->at->stop(); + } + + return PJ_SUCCESS; +} + + +PJ_DEF(pj_status_t) pjmedia_snd_stream_close(pjmedia_snd_stream *stream) +{ + pj_pool_t *pool; + + PJ_ASSERT_RETURN(stream != NULL, PJ_EINVAL); + + if (stream->ar) { + delete stream->ar; + stream->ar = NULL; + } + + if (stream->at) { + delete stream->at; + stream->at = NULL; + } + + pool = stream->pool; + if (pool) { + stream->pool = NULL; + pj_pool_release(pool); + } + + /* Restore the audio routing setting */ + android::AudioSystem::setRouting(stream->saved_audio_mode, + stream->saved_audio_routing, + android::AudioSystem::ROUTE_ALL); + + + return PJ_SUCCESS; +} + +PJ_DEF(pj_status_t) pjmedia_snd_deinit(void) +{ + /* Nothing to do */ + return PJ_SUCCESS; +} + + +/* + * Set sound latency. + */ +PJ_DEF(pj_status_t) pjmedia_snd_set_latency(unsigned input_latency, + unsigned output_latency) +{ + /* Nothing to do */ + PJ_UNUSED_ARG(input_latency); + PJ_UNUSED_ARG(output_latency); + return PJ_EINVAL; +} +#endif diff -aurN a/third_party/build/os-android.mak b/third_party/build/os-android.mak --- a/third_party/build/os-android.mak 1970-01-01 01:00:00.000000000 +0100 +++ b/third_party/build/os-android.mak 2009-04-06 21:21:05.000000000 +0100 @@ -0,0 +1,6 @@ +DIRS += gsm +DIRS += ilbc +DIRS += speex +DIRS += portaudio + + diff -aurN a/third_party/build/speex/config.h b/third_party/build/speex/config.h --- a/third_party/build/speex/config.h 2008-06-10 15:35:50.000000000 +0100 +++ b/third_party/build/speex/config.h 2009-04-06 21:21:05.000000000 +0100 @@ -19,6 +19,13 @@ #define restrict #endif +#if defined(PJ_ANDROID) && PJ_ANDROID!=0 +#undef FLOATING_POINT +#define FIXED_POINT +#define USE_KISS_FFT +#define ARM5E_ASM +#endif + #ifdef _MSC_VER # pragma warning(disable: 4100) // unreferenced formal parameter # pragma warning(disable: 4101) // unreferenced local variable