summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--build/common.mak108
-rw-r--r--pjlib-util/include/pjlib-util/md5.h110
-rw-r--r--pjlib-util/include/pjlib-util/scanner.h1058
-rw-r--r--pjlib-util/include/pjlib-util/string.h175
-rw-r--r--pjlib-util/include/pjlib-util/stun.h282
-rw-r--r--pjlib-util/include/pjlib-util/xml.h342
-rw-r--r--pjlib-util/src/pjlib-util-test/main.c106
-rw-r--r--pjlib-util/src/pjlib-util-test/test.c172
-rw-r--r--pjlib-util/src/pjlib-util-test/test.h56
-rw-r--r--pjlib-util/src/pjlib-util-test/xml.c290
-rw-r--r--pjlib-util/src/pjlib-util/md5.c558
-rw-r--r--pjlib-util/src/pjlib-util/scanner.c1190
-rw-r--r--pjlib-util/src/pjlib-util/string.c206
-rw-r--r--pjlib-util/src/pjlib-util/stun.c258
-rw-r--r--pjlib-util/src/pjlib-util/stun_client.c554
-rw-r--r--pjlib-util/src/pjlib-util/symbols.c164
-rw-r--r--pjlib-util/src/pjlib-util/xml.c792
-rw-r--r--pjlib/include/pj++/file.hpp374
-rw-r--r--pjlib/include/pj++/hash.hpp310
-rw-r--r--pjlib/include/pj++/list.hpp654
-rw-r--r--pjlib/include/pj++/lock.hpp296
-rw-r--r--pjlib/include/pj++/os.hpp1608
-rw-r--r--pjlib/include/pj++/pool.hpp540
-rw-r--r--pjlib/include/pj++/proactor.hpp1036
-rw-r--r--pjlib/include/pj++/scanner.hpp378
-rw-r--r--pjlib/include/pj++/sock.hpp886
-rw-r--r--pjlib/include/pj++/string.hpp850
-rw-r--r--pjlib/include/pj++/timer.hpp394
-rw-r--r--pjlib/include/pj++/tree.hpp256
-rw-r--r--pjlib/include/pj++/types.hpp320
-rw-r--r--pjlib/include/pj/addr_resolv.h184
-rw-r--r--pjlib/include/pj/array.h190
-rw-r--r--pjlib/include/pj/assert.h146
-rw-r--r--pjlib/include/pj/compat/assert.h86
-rw-r--r--pjlib/include/pj/compat/cc_gcc.h86
-rw-r--r--pjlib/include/pj/compat/cc_msvc.h94
-rw-r--r--pjlib/include/pj/compat/ctype.h92
-rw-r--r--pjlib/include/pj/compat/errno.h88
-rw-r--r--pjlib/include/pj/compat/high_precision.h204
-rw-r--r--pjlib/include/pj/compat/m_alpha.h66
-rw-r--r--pjlib/include/pj/compat/m_i386.h68
-rw-r--r--pjlib/include/pj/compat/m_m68k.h64
-rw-r--r--pjlib/include/pj/compat/m_sparc.h68
-rw-r--r--pjlib/include/pj/compat/malloc.h66
-rw-r--r--pjlib/include/pj/compat/os_linux.h164
-rw-r--r--pjlib/include/pj/compat/os_linux_kernel.h204
-rw-r--r--pjlib/include/pj/compat/os_palmos.h154
-rw-r--r--pjlib/include/pj/compat/os_sunos.h170
-rw-r--r--pjlib/include/pj/compat/os_win32.h170
-rw-r--r--pjlib/include/pj/compat/rand.h138
-rw-r--r--pjlib/include/pj/compat/setjmp.h180
-rw-r--r--pjlib/include/pj/compat/size_t.h62
-rw-r--r--pjlib/include/pj/compat/socket.h260
-rw-r--r--pjlib/include/pj/compat/sprintf.h76
-rw-r--r--pjlib/include/pj/compat/stdarg.h62
-rw-r--r--pjlib/include/pj/compat/stdfileio.h62
-rw-r--r--pjlib/include/pj/compat/string.h114
-rw-r--r--pjlib/include/pj/compat/time.h72
-rw-r--r--pjlib/include/pj/compat/vsprintf.h74
-rw-r--r--pjlib/include/pj/os.h38
-rw-r--r--pjlib/include/pj/string.h2
-rw-r--r--pjlib/src/pj/addr_resolv_linux_kernel.c50
-rw-r--r--pjlib/src/pj/addr_resolv_sock.c102
-rw-r--r--pjlib/src/pj/array.c140
-rw-r--r--pjlib/src/pj/compat/sigjmp.c78
-rw-r--r--pjlib/src/pj/compat/string.c88
-rw-r--r--pjlib/src/pjlib++-test/main.cpp122
-rw-r--r--pjlib/src/pjlib-samples/except.c170
-rw-r--r--pjlib/src/pjlib-samples/list.c142
-rw-r--r--pjlib/src/pjlib-samples/log.c84
-rw-r--r--pjlib/src/pjlib-test/atomic.c216
-rw-r--r--pjlib/src/pjlib-test/echo_clt.c536
-rw-r--r--pjlib/src/pjlib-test/errno.c324
-rw-r--r--pjlib/src/pjlib-test/exception.c354
-rw-r--r--pjlib/src/pjlib-test/fifobuf.c232
-rw-r--r--pjlib/src/pjlib-test/file.c424
-rw-r--r--pjlib/src/pjlib-test/ioq_perf.c1004
-rw-r--r--pjlib/src/pjlib-test/ioq_tcp.c1080
-rw-r--r--pjlib/src/pjlib-test/ioq_udp.c1326
-rw-r--r--pjlib/src/pjlib-test/list.c450
-rw-r--r--pjlib/src/pjlib-test/main.c186
-rw-r--r--pjlib/src/pjlib-test/main_mod.c78
-rw-r--r--pjlib/src/pjlib-test/mutex.c348
-rw-r--r--pjlib/src/pjlib-test/os.c38
-rw-r--r--pjlib/src/pjlib-test/pool.c344
-rw-r--r--pjlib/src/pjlib-test/pool_perf.c280
-rw-r--r--pjlib/src/pjlib-test/rand.c106
-rw-r--r--pjlib/src/pjlib-test/rbtree.c334
-rw-r--r--pjlib/src/pjlib-test/select.c434
-rw-r--r--pjlib/src/pjlib-test/sleep.c400
-rw-r--r--pjlib/src/pjlib-test/sock.c912
-rw-r--r--pjlib/src/pjlib-test/sock_perf.c366
-rw-r--r--pjlib/src/pjlib-test/string.c348
-rw-r--r--pjlib/src/pjlib-test/test.c398
-rw-r--r--pjlib/src/pjlib-test/test.h222
-rw-r--r--pjlib/src/pjlib-test/thread.c578
-rw-r--r--pjlib/src/pjlib-test/timer.c372
-rw-r--r--pjlib/src/pjlib-test/timestamp.c286
-rw-r--r--pjlib/src/pjlib-test/udp_echo_srv_ioqueue.c422
-rw-r--r--pjlib/src/pjlib-test/udp_echo_srv_sync.c326
-rw-r--r--pjlib/src/pjlib-test/util.c262
-rw-r--r--pjmedia/build/Jbtest.dat124
-rw-r--r--pjmedia/src/pjmedia/codec.c212
-rw-r--r--pjmedia/src/pjmedia/codec.h706
-rw-r--r--pjmedia/src/pjmedia/config.h54
-rw-r--r--pjmedia/src/pjmedia/dsound.c1754
-rw-r--r--pjmedia/src/pjmedia/g711.c1234
-rw-r--r--pjmedia/src/pjmedia/jbuf.c840
-rw-r--r--pjmedia/src/pjmedia/jbuf.h284
-rw-r--r--pjmedia/src/pjmedia/mediamgr.c224
-rw-r--r--pjmedia/src/pjmedia/mediamgr.h200
-rw-r--r--pjmedia/src/pjmedia/nullsound.c254
-rw-r--r--pjmedia/src/pjmedia/pasound.c808
-rw-r--r--pjmedia/src/pjmedia/portaudio/LICENSE.txt128
-rw-r--r--pjmedia/src/pjmedia/portaudio/README.txt162
-rw-r--r--pjmedia/src/pjmedia/portaudio/V19-devel-readme.txt444
-rw-r--r--pjmedia/src/pjmedia/portaudio/dsound_wrapper.c1232
-rw-r--r--pjmedia/src/pjmedia/portaudio/dsound_wrapper.h260
-rw-r--r--pjmedia/src/pjmedia/portaudio/pa_allocation.c468
-rw-r--r--pjmedia/src/pjmedia/portaudio/pa_allocation.h190
-rw-r--r--pjmedia/src/pjmedia/portaudio/pa_converters.c3852
-rw-r--r--pjmedia/src/pjmedia/portaudio/pa_converters.h508
-rw-r--r--pjmedia/src/pjmedia/portaudio/pa_cpuload.c192
-rw-r--r--pjmedia/src/pjmedia/portaudio/pa_cpuload.h126
-rw-r--r--pjmedia/src/pjmedia/portaudio/pa_dither.c408
-rw-r--r--pjmedia/src/pjmedia/portaudio/pa_dither.h182
-rw-r--r--pjmedia/src/pjmedia/portaudio/pa_endianness.h222
-rw-r--r--pjmedia/src/pjmedia/portaudio/pa_front.c3952
-rw-r--r--pjmedia/src/pjmedia/portaudio/pa_hostapi.h488
-rw-r--r--pjmedia/src/pjmedia/portaudio/pa_linux_alsa.c6544
-rw-r--r--pjmedia/src/pjmedia/portaudio/pa_linux_alsa.h128
-rw-r--r--pjmedia/src/pjmedia/portaudio/pa_process.c3512
-rw-r--r--pjmedia/src/pjmedia/portaudio/pa_process.h1482
-rw-r--r--pjmedia/src/pjmedia/portaudio/pa_skeleton.c1614
-rw-r--r--pjmedia/src/pjmedia/portaudio/pa_stream.c282
-rw-r--r--pjmedia/src/pjmedia/portaudio/pa_stream.h392
-rw-r--r--pjmedia/src/pjmedia/portaudio/pa_trace.c176
-rw-r--r--pjmedia/src/pjmedia/portaudio/pa_trace.h140
-rw-r--r--pjmedia/src/pjmedia/portaudio/pa_types.h130
-rw-r--r--pjmedia/src/pjmedia/portaudio/pa_unix_hostapis.c128
-rw-r--r--pjmedia/src/pjmedia/portaudio/pa_unix_oss.c3834
-rw-r--r--pjmedia/src/pjmedia/portaudio/pa_unix_util.c346
-rw-r--r--pjmedia/src/pjmedia/portaudio/pa_unix_util.h146
-rw-r--r--pjmedia/src/pjmedia/portaudio/pa_util.h334
-rw-r--r--pjmedia/src/pjmedia/portaudio/pa_win_ds.c3656
-rw-r--r--pjmedia/src/pjmedia/portaudio/pa_win_hostapis.c172
-rw-r--r--pjmedia/src/pjmedia/portaudio/pa_win_util.c266
-rw-r--r--pjmedia/src/pjmedia/portaudio/pa_win_wmme.c7246
-rw-r--r--pjmedia/src/pjmedia/portaudio/pa_win_wmme.h320
-rw-r--r--pjmedia/src/pjmedia/portaudio/pa_x86_plain_converters.c2334
-rw-r--r--pjmedia/src/pjmedia/portaudio/pa_x86_plain_converters.h38
-rw-r--r--pjmedia/src/pjmedia/portaudio/portaudio.h2244
-rw-r--r--pjmedia/src/test/audio_tool.c818
-rw-r--r--pjmedia/src/test/jbuf_test.c308
-rw-r--r--pjmedia/src/test/main.c84
-rw-r--r--pjmedia/src/test/rtp_test.c74
-rw-r--r--pjmedia/src/test/sdptest.c242
-rw-r--r--pjmedia/src/test/session_test.c260
-rw-r--r--pjsip/include/pjsip-simple/event_notify.h660
-rw-r--r--pjsip/include/pjsip-simple/event_notify_msg.h234
-rw-r--r--pjsip/include/pjsip-simple/messaging.h536
-rw-r--r--pjsip/include/pjsip-simple/pidf.h352
-rw-r--r--pjsip/include/pjsip-simple/presence.h458
-rw-r--r--pjsip/include/pjsip-simple/xpidf.h266
-rw-r--r--pjsip/include/pjsip-ua/sip_dialog.h1270
-rw-r--r--pjsip/include/pjsip-ua/sip_regc.h420
-rw-r--r--pjsip/include/pjsip-ua/sip_ua.h194
-rw-r--r--pjsip/include/pjsip/print_util.h288
-rw-r--r--pjsip/include/pjsip/sip_auth.h494
-rw-r--r--pjsip/include/pjsip/sip_auth_msg.h422
-rw-r--r--pjsip/include/pjsip/sip_auth_parser.h174
-rw-r--r--pjsip/include/pjsip/sip_config.h326
-rw-r--r--pjsip/include/pjsip/sip_endpoint.h772
-rw-r--r--pjsip/include/pjsip/sip_errno.h456
-rw-r--r--pjsip/include/pjsip/sip_event.h620
-rw-r--r--pjsip/include/pjsip/sip_module.h280
-rw-r--r--pjsip/include/pjsip/sip_msg.h3054
-rw-r--r--pjsip/include/pjsip/sip_msg_i.h58
-rw-r--r--pjsip/include/pjsip/sip_parser.h706
-rw-r--r--pjsip/include/pjsip/sip_private.h92
-rw-r--r--pjsip/include/pjsip/sip_resolve.h240
-rw-r--r--pjsip/include/pjsip/sip_transaction.h542
-rw-r--r--pjsip/include/pjsip/sip_transport.h1270
-rw-r--r--pjsip/include/pjsip/sip_transport_udp.h136
-rw-r--r--pjsip/include/pjsip/sip_types.h350
-rw-r--r--pjsip/include/pjsip/sip_uri.h743
-rw-r--r--pjsip/include/pjsip/sip_util.h384
-rw-r--r--pjsip/src/pjsip-simple/event_notify.c3288
-rw-r--r--pjsip/src/pjsip-simple/event_notify_msg.c644
-rw-r--r--pjsip/src/pjsip-simple/messaging.c704
-rw-r--r--pjsip/src/pjsip-simple/pidf.c700
-rw-r--r--pjsip/src/pjsip-simple/presence.c798
-rw-r--r--pjsip/src/pjsip-simple/xpidf.c588
-rw-r--r--pjsip/src/pjsip-ua/sip_dialog.c3604
-rw-r--r--pjsip/src/pjsip-ua/sip_reg.c1022
-rw-r--r--pjsip/src/pjsip-ua/sip_ua.c1012
-rw-r--r--pjsip/src/pjsip-ua/sip_ua_private.h72
-rw-r--r--pjsip/src/pjsip/sip_auth.c1608
-rw-r--r--pjsip/src/pjsip/sip_auth_msg.c637
-rw-r--r--pjsip/src/pjsip/sip_auth_parser.c594
-rw-r--r--pjsip/src/pjsip/sip_endpoint.c2224
-rw-r--r--pjsip/src/pjsip/sip_errno.c250
-rw-r--r--pjsip/src/pjsip/sip_msg.c2884
-rw-r--r--pjsip/src/pjsip/sip_parser.c3564
-rw-r--r--pjsip/src/pjsip/sip_resolve.c250
-rw-r--r--pjsip/src/pjsip/sip_transaction.c3990
-rw-r--r--pjsip/src/pjsip/sip_transport.c1686
-rw-r--r--pjsip/src/pjsip/sip_transport_udp.c820
-rw-r--r--pjsip/src/pjsip/sip_uri.c1105
-rw-r--r--pjsip/src/pjsip/sip_util.c1448
-rw-r--r--pjsip/src/pjsua/getopt.c2122
-rw-r--r--pjsip/src/pjsua/getopt.h282
-rw-r--r--pjsip/src/pjsua/main.c3654
-rw-r--r--pjsip/src/pjsua/misc.c970
-rw-r--r--pjsip/src/test-pjsip/main.c48
-rw-r--r--pjsip/src/test-pjsip/msg.c738
-rw-r--r--pjsip/src/test-pjsip/test.c208
-rw-r--r--pjsip/src/test-pjsip/test.h66
-rw-r--r--pjsip/src/test-pjsip/uri.c1196
219 files changed, 73688 insertions, 73432 deletions
diff --git a/build/common.mak b/build/common.mak
index 608390cb..3d84338b 100644
--- a/build/common.mak
+++ b/build/common.mak
@@ -1,54 +1,54 @@
-#
-# Include host/target/compiler selection.
-# This will export CC_NAME, MACHINE_NAME, OS_NAME, and HOST_NAME variables.
-#
-include ../../build.mak
-
-#
-# Include global compiler specific definitions
-#
-include ../../build/cc-$(CC_NAME).mak
-
-#
-# (Optionally) Include compiler specific configuration that is
-# specific to this project. This configuration file is
-# located in this directory.
-#
--include cc-$(CC_NAME).mak
-
-#
-# Include global machine specific definitions
-#
-include ../../build/m-$(MACHINE_NAME).mak
--include m-$(MACHINE_NAME).mak
-
-#
-# Include target OS specific definitions
-#
-include ../../build/os-$(OS_NAME).mak
-
-#
-# (Optionally) Include target OS specific configuration that is
-# specific to this project. This configuration file is
-# located in this directory.
-#
--include os-$(OS_NAME).mak
-
-#
-# Include host specific definitions
-#
-include ../../build/host-$(HOST_NAME).mak
-
-#
-# (Optionally) Include host specific configuration that is
-# specific to this project. This configuration file is
-# located in this directory.
-#
--include host-$(HOST_NAME).mak
-
-#
-# Include global user configuration, if any
-#
--include ../../user.mak
-
-
+#
+# Include host/target/compiler selection.
+# This will export CC_NAME, MACHINE_NAME, OS_NAME, and HOST_NAME variables.
+#
+include ../../build.mak
+
+#
+# Include global compiler specific definitions
+#
+include ../../build/cc-$(CC_NAME).mak
+
+#
+# (Optionally) Include compiler specific configuration that is
+# specific to this project. This configuration file is
+# located in this directory.
+#
+-include cc-$(CC_NAME).mak
+
+#
+# Include global machine specific definitions
+#
+include ../../build/m-$(MACHINE_NAME).mak
+-include m-$(MACHINE_NAME).mak
+
+#
+# Include target OS specific definitions
+#
+include ../../build/os-$(OS_NAME).mak
+
+#
+# (Optionally) Include target OS specific configuration that is
+# specific to this project. This configuration file is
+# located in this directory.
+#
+-include os-$(OS_NAME).mak
+
+#
+# Include host specific definitions
+#
+include ../../build/host-$(HOST_NAME).mak
+
+#
+# (Optionally) Include host specific configuration that is
+# specific to this project. This configuration file is
+# located in this directory.
+#
+-include host-$(HOST_NAME).mak
+
+#
+# Include global user configuration, if any
+#
+-include ../../user.mak
+
+
diff --git a/pjlib-util/include/pjlib-util/md5.h b/pjlib-util/include/pjlib-util/md5.h
index 24e504b0..92c55fc8 100644
--- a/pjlib-util/include/pjlib-util/md5.h
+++ b/pjlib-util/include/pjlib-util/md5.h
@@ -1,55 +1,55 @@
-/* $Id$ */
-/*
- * Copyright (C) 2003-2005 Benny Prijono <benny@prijono.org>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
-#ifndef __PJLIB_UTIL_MD5_H__
-#define __PJLIB_UTIL_MD5_H__
-
-#include <pj/types.h>
-
-PJ_BEGIN_DECL
-
-/** MD5 context. */
-typedef struct pj_md5_context
-{
- pj_uint32_t buf[4];
- pj_uint32_t bits[2];
- pj_uint8_t in[64];
-} pj_md5_context;
-
-/** Initialize the algorithm.
- * @param pms MD5 context.
- */
-PJ_DECL(void) pj_md5_init(pj_md5_context *pms);
-
-/** Append a string to the message.
- * @param pms MD5 context.
- * @param data Data.
- * @param nbytes Length of data.
- */
-PJ_DECL(void) pj_md5_update( pj_md5_context *pms,
- const pj_uint8_t *data, unsigned nbytes);
-
-/** Finish the message and return the digest.
- * @param pms MD5 context.
- * @param digest 16 byte digest.
- */
-PJ_DECL(void) pj_md5_final(pj_md5_context *pms, pj_uint8_t digest[16]);
-
-PJ_END_DECL
-
-#endif /* __PJLIB_UTIL_MD5_H__ */
+/* $Id$ */
+/*
+ * Copyright (C) 2003-2005 Benny Prijono <benny@prijono.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#ifndef __PJLIB_UTIL_MD5_H__
+#define __PJLIB_UTIL_MD5_H__
+
+#include <pj/types.h>
+
+PJ_BEGIN_DECL
+
+/** MD5 context. */
+typedef struct pj_md5_context
+{
+ pj_uint32_t buf[4];
+ pj_uint32_t bits[2];
+ pj_uint8_t in[64];
+} pj_md5_context;
+
+/** Initialize the algorithm.
+ * @param pms MD5 context.
+ */
+PJ_DECL(void) pj_md5_init(pj_md5_context *pms);
+
+/** Append a string to the message.
+ * @param pms MD5 context.
+ * @param data Data.
+ * @param nbytes Length of data.
+ */
+PJ_DECL(void) pj_md5_update( pj_md5_context *pms,
+ const pj_uint8_t *data, unsigned nbytes);
+
+/** Finish the message and return the digest.
+ * @param pms MD5 context.
+ * @param digest 16 byte digest.
+ */
+PJ_DECL(void) pj_md5_final(pj_md5_context *pms, pj_uint8_t digest[16]);
+
+PJ_END_DECL
+
+#endif /* __PJLIB_UTIL_MD5_H__ */
diff --git a/pjlib-util/include/pjlib-util/scanner.h b/pjlib-util/include/pjlib-util/scanner.h
index 209b2d36..d7db4a92 100644
--- a/pjlib-util/include/pjlib-util/scanner.h
+++ b/pjlib-util/include/pjlib-util/scanner.h
@@ -1,529 +1,529 @@
-/* $Id$ */
-/*
- * Copyright (C) 2003-2005 Benny Prijono <benny@prijono.org>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
-#ifndef __PJ_SCANNER_H__
-#define __PJ_SCANNER_H__
-
-/**
- * @file scanner.h
- * @brief Text Scanning.
- */
-
-#include <pj/types.h>
-
-PJ_BEGIN_DECL
-
-/**
- * @defgroup PJ_SCAN Text Scanning
- * @ingroup PJ_MISC
- * @brief
- * Text scanning utility.
- *
- * @{
- */
-
-/**
- * This describes the type of individual character specification in
- * #pj_cis_buf_t. Basicly the number of bits here
- */
-#ifndef PJ_CIS_ELEM_TYPE
-# define PJ_CIS_ELEM_TYPE pj_uint32_t
-#endif
-
-/**
- * This describes the type of individual character specification in
- * #pj_cis_buf_t.
- */
-typedef PJ_CIS_ELEM_TYPE pj_cis_elem_t;
-
-/**
- * Maximum number of input specification in a buffer.
- * Effectively this means the number of bits in pj_cis_elem_t.
- */
-#define PJ_CIS_MAX_INDEX (sizeof(pj_cis_elem_t) << 3)
-
-/**
- * The scanner input specification buffer.
- */
-typedef struct pj_cis_buf_t
-{
- pj_cis_elem_t cis_buf[256]; /**< Must be 256 (not 128)! */
- pj_cis_elem_t use_mask; /**< To keep used indexes. */
-} pj_cis_buf_t;
-
-/**
- * Character input specification.
- */
-typedef struct pj_cis_t
-{
- pj_cis_elem_t *cis_buf; /**< Pointer to buffer. */
- int cis_id; /**< Id. */
-} pj_cis_t;
-
-/**
- * Initialize scanner input specification buffer.
- *
- * @param cs_buf The scanner character specification.
- */
-PJ_DECL(void) pj_cis_buf_init(pj_cis_buf_t *cs_buf);
-
-/**
- * Create a new input specification.
- *
- * @param cs_buf Specification buffer.
- * @param cis Character input specification to be initialized.
- *
- * @return PJ_SUCCESS if new specification has been successfully
- * created, or PJ_ETOOMANY if there are already too many
- * specifications in the buffer.
- */
-PJ_DECL(pj_status_t) pj_cis_init(pj_cis_buf_t *cs_buf, pj_cis_t *cis);
-
-/**
- * Create a new input specification based on an existing specification.
- *
- * @param new_cis The new specification to be initialized.
- * @param existing The existing specification, from which the input
- * bitmask will be copied to the new specification.
- *
- * @return PJ_SUCCESS if new specification has been successfully
- * created, or PJ_ETOOMANY if there are already too many
- * specifications in the buffer.
- */
-PJ_DECL(pj_status_t) pj_cis_dup(pj_cis_t *new_cis, pj_cis_t *existing);
-
-/**
- * Set the membership of the specified character.
- * Note that this is a macro, and arguments may be evaluated more than once.
- *
- * @param cis Pointer to character input specification.
- * @param c The character.
- */
-#define PJ_CIS_SET(cis,c) ((cis)->cis_buf[(c)] |= (1 << (cis)->cis_id))
-
-/**
- * Remove the membership of the specified character.
- * Note that this is a macro, and arguments may be evaluated more than once.
- *
- * @param cis Pointer to character input specification.
- * @param c The character to be removed from the membership.
- */
-#define PJ_CIS_CLR(cis,c) ((cis)->cis_buf[c] &= ~(1 << (cis)->cis_id))
-
-/**
- * Check the membership of the specified character.
- * Note that this is a macro, and arguments may be evaluated more than once.
- *
- * @param cis Pointer to character input specification.
- * @param c The character.
- */
-#define PJ_CIS_ISSET(cis,c) ((cis)->cis_buf[c] & (1 << (cis)->cis_id))
-
-/**
- * Add the characters in the specified range '[cstart, cend)' to the
- * specification (the last character itself ('cend') is not added).
- *
- * @param cis The scanner character specification.
- * @param cstart The first character in the range.
- * @param cend The next character after the last character in the range.
- */
-PJ_DECL(void) pj_cis_add_range( pj_cis_t *cis, int cstart, int cend);
-
-/**
- * Add alphabetic characters to the specification.
- *
- * @param cis The scanner character specification.
- */
-PJ_DECL(void) pj_cis_add_alpha( pj_cis_t *cis);
-
-/**
- * Add numeric characters to the specification.
- *
- * @param cis The scanner character specification.
- */
-PJ_DECL(void) pj_cis_add_num( pj_cis_t *cis);
-
-/**
- * Add the characters in the string to the specification.
- *
- * @param cis The scanner character specification.
- * @param str The string.
- */
-PJ_DECL(void) pj_cis_add_str( pj_cis_t *cis, const char *str);
-
-/**
- * Delete characters in the specified range from the specification.
- *
- * @param cis The scanner character specification.
- * @param cstart The first character in the range.
- * @param cend The next character after the last character in the range.
- */
-PJ_DECL(void) pj_cis_del_range( pj_cis_t *cis, int cstart, int cend);
-
-/**
- * Delete characters in the specified string from the specification.
- *
- * @param cis The scanner character specification.
- * @param str The string.
- */
-PJ_DECL(void) pj_cis_del_str( pj_cis_t *cis, const char *str);
-
-/**
- * Invert specification.
- *
- * @param cis The scanner character specification.
- */
-PJ_DECL(void) pj_cis_invert( pj_cis_t *cis );
-
-/**
- * Check whether the specified character belongs to the specification.
- *
- * @param cis The scanner character specification.
- * @param c The character to check for matching.
- *
- * @return Non-zero if match (not necessarily one).
- */
-PJ_INLINE(int) pj_cis_match( const pj_cis_t *cis, int c )
-{
- return PJ_CIS_ISSET(cis, c);
-}
-
-
-/**
- * Flags for scanner.
- */
-enum
-{
- /** This flags specifies that the scanner should automatically skip
- whitespaces
- */
- PJ_SCAN_AUTOSKIP_WS = 1,
-
- /** This flags specifies that the scanner should automatically skip
- SIP header continuation. This flag implies PJ_SCAN_AUTOSKIP_WS.
- */
- PJ_SCAN_AUTOSKIP_WS_HEADER = 3,
-
- /** Auto-skip new lines.
- */
- PJ_SCAN_AUTOSKIP_NEWLINE = 4,
-};
-
-
-/* Forward decl. */
-struct pj_scanner;
-
-
-/**
- * The callback function type to be called by the scanner when it encounters
- * syntax error.
- *
- * @param scanner The scanner instance that calls the callback .
- */
-typedef void (*pj_syn_err_func_ptr)(struct pj_scanner *scanner);
-
-
-/**
- * The text scanner structure.
- */
-typedef struct pj_scanner
-{
- char *begin; /**< Start of input buffer. */
- char *end; /**< End of input buffer. */
- char *curptr; /**< Current pointer. */
- int line; /**< Current line. */
- int col; /**< Current column. */
- int skip_ws; /**< Skip whitespace flag. */
- pj_syn_err_func_ptr callback; /**< Syntax error callback. */
-} pj_scanner;
-
-
-/**
- * This structure can be used by application to store the state of the parser,
- * so that the scanner state can be rollback to this state when necessary.
- */
-typedef struct pj_scan_state
-{
- char *curptr; /**< Current scanner's pointer. */
- int line; /**< Current line. */
- int col; /**< Current column. */
-} pj_scan_state;
-
-
-/**
- * Initialize the scanner. Note that the input string buffer must have
- * length at least buflen+1 because the scanner will NULL terminate the
- * string during initialization.
- *
- * @param scanner The scanner to be initialized.
- * @param bufstart The input buffer to scan. Note that buffer[buflen] will be
- * filled with NULL char until scanner is destroyed, so
- * the actual buffer length must be at least buflen+1.
- * @param buflen The length of the input buffer, which normally is
- * strlen(bufstart).
- * @param options Zero, or combination of PJ_SCAN_AUTOSKIP_WS or
- * PJ_SCAN_AUTOSKIP_WS_HEADER
- * @param callback Callback to be called when the scanner encounters syntax
- * error condition.
- */
-PJ_DECL(void) pj_scan_init( pj_scanner *scanner, char *bufstart, int buflen,
- unsigned options,
- pj_syn_err_func_ptr callback );
-
-
-/**
- * Call this function when application has finished using the scanner.
- *
- * @param scanner The scanner.
- */
-PJ_DECL(void) pj_scan_fini( pj_scanner *scanner );
-
-
-/**
- * Determine whether the EOF condition for the scanner has been met.
- *
- * @param scanner The scanner.
- *
- * @return Non-zero if scanner is EOF.
- */
-PJ_INLINE(int) pj_scan_is_eof( const pj_scanner *scanner)
-{
- return scanner->curptr >= scanner->end;
-}
-
-
-/**
- * Peek strings in current position according to parameter spec, and return
- * the strings in parameter out. The current scanner position will not be
- * moved. If the scanner is already in EOF state, syntax error callback will
- * be called thrown.
- *
- * @param scanner The scanner.
- * @param spec The spec to match input string.
- * @param out String to store the result.
- *
- * @return the character right after the peek-ed position or zero if there's
- * no more characters.
- */
-PJ_DECL(int) pj_scan_peek( pj_scanner *scanner,
- const pj_cis_t *spec, pj_str_t *out);
-
-
-/**
- * Peek len characters in current position, and return them in out parameter.
- * Note that whitespaces or newlines will be returned as it is, regardless
- * of PJ_SCAN_AUTOSKIP_WS settings. If the character left is less than len,
- * syntax error callback will be called.
- *
- * @param scanner The scanner.
- * @param len Length to peek.
- * @param out String to store the result.
- *
- * @return the character right after the peek-ed position or zero if there's
- * no more characters.
- */
-PJ_DECL(int) pj_scan_peek_n( pj_scanner *scanner,
- pj_size_t len, pj_str_t *out);
-
-
-/**
- * Peek strings in current position until spec is matched, and return
- * the strings in parameter out. The current scanner position will not be
- * moved. If the scanner is already in EOF state, syntax error callback will
- * be called.
- *
- * @param scanner The scanner.
- * @param spec The peeking will stop when the input match this spec.
- * @param out String to store the result.
- *
- * @return the character right after the peek-ed position.
- */
-PJ_DECL(int) pj_scan_peek_until( pj_scanner *scanner,
- const pj_cis_t *spec,
- pj_str_t *out);
-
-
-/**
- * Get characters from the buffer according to the spec, and return them
- * in out parameter. The scanner will attempt to get as many characters as
- * possible as long as the spec matches. If the first character doesn't
- * match the spec, or scanner is already in EOF when this function is called,
- * an exception will be thrown.
- *
- * @param scanner The scanner.
- * @param spec The spec to match input string.
- * @param out String to store the result.
- */
-PJ_DECL(void) pj_scan_get( pj_scanner *scanner,
- const pj_cis_t *spec, pj_str_t *out);
-
-
-/**
- * Get characters between quotes. If current input doesn't match begin_quote,
- * syntax error will be thrown.
- *
- * @param scanner The scanner.
- * @param begin_quote The character to begin the quote.
- * @param end_quote The character to end the quote.
- * @param out String to store the result.
- */
-PJ_DECL(void) pj_scan_get_quote( pj_scanner *scanner,
- int begin_quote, int end_quote,
- pj_str_t *out);
-
-/**
- * Get N characters from the scanner.
- *
- * @param scanner The scanner.
- * @param N Number of characters to get.
- * @param out String to store the result.
- */
-PJ_DECL(void) pj_scan_get_n( pj_scanner *scanner,
- unsigned N, pj_str_t *out);
-
-
-/**
- * Get one character from the scanner.
- *
- * @param scanner The scanner.
- *
- * @return The character.
- */
-PJ_DECL(int) pj_scan_get_char( pj_scanner *scanner );
-
-
-/**
- * Get a newline from the scanner. A newline is defined as '\\n', or '\\r', or
- * "\\r\\n". If current input is not newline, syntax error will be thrown.
- *
- * @param scanner The scanner.
- */
-PJ_DECL(void) pj_scan_get_newline( pj_scanner *scanner );
-
-
-/**
- * Get characters from the scanner and move the scanner position until the
- * current character matches the spec.
- *
- * @param scanner The scanner.
- * @param spec Get until the input match this spec.
- * @param out String to store the result.
- */
-PJ_DECL(void) pj_scan_get_until( pj_scanner *scanner,
- const pj_cis_t *spec, pj_str_t *out);
-
-
-/**
- * Get characters from the scanner and move the scanner position until the
- * current character matches until_char.
- *
- * @param scanner The scanner.
- * @param until_char Get until the input match this character.
- * @param out String to store the result.
- */
-PJ_DECL(void) pj_scan_get_until_ch( pj_scanner *scanner,
- int until_char, pj_str_t *out);
-
-
-/**
- * Get characters from the scanner and move the scanner position until the
- * current character matches until_char.
- *
- * @param scanner The scanner.
- * @param until_spec Get until the input match any of these characters.
- * @param out String to store the result.
- */
-PJ_DECL(void) pj_scan_get_until_chr( pj_scanner *scanner,
- const char *until_spec, pj_str_t *out);
-
-/**
- * Advance the scanner N characters, and skip whitespace
- * if necessary.
- *
- * @param scanner The scanner.
- * @param N Number of characters to skip.
- * @param skip Flag to specify whether whitespace should be skipped
- * after skipping the characters.
- */
-PJ_DECL(void) pj_scan_advance_n( pj_scanner *scanner,
- unsigned N, pj_bool_t skip);
-
-
-/**
- * Compare string in current position with the specified string.
- *
- * @param scanner The scanner.
- * @param s The string to compare with.
- * @param len Length of the string to compare.
- *
- * @return zero, <0, or >0 (just like strcmp()).
- */
-PJ_DECL(int) pj_scan_strcmp( pj_scanner *scanner, const char *s, int len);
-
-
-/**
- * Case-less string comparison of current position with the specified
- * string.
- *
- * @param scanner The scanner.
- * @param s The string to compare with.
- * @param len Length of the string to compare with.
- *
- * @return zero, <0, or >0 (just like strcmp()).
- */
-PJ_DECL(int) pj_scan_stricmp( pj_scanner *scanner, const char *s, int len);
-
-
-/**
- * Manually skip whitespaces according to flag that was specified when
- * the scanner was initialized.
- *
- * @param scanner The scanner.
- */
-PJ_DECL(void) pj_scan_skip_whitespace( pj_scanner *scanner );
-
-
-/**
- * Save the full scanner state.
- *
- * @param scanner The scanner.
- * @param state Variable to store scanner's state.
- */
-PJ_DECL(void) pj_scan_save_state( pj_scanner *scanner, pj_scan_state *state);
-
-
-/**
- * Restore the full scanner state.
- * Note that this would not restore the string if application has modified
- * it. This will only restore the scanner scanning position.
- *
- * @param scanner The scanner.
- * @param state State of the scanner.
- */
-PJ_DECL(void) pj_scan_restore_state( pj_scanner *scanner,
- pj_scan_state *state);
-
-/**
- * @}
- */
-
-
-PJ_END_DECL
-
-#endif
-
+/* $Id$ */
+/*
+ * Copyright (C) 2003-2005 Benny Prijono <benny@prijono.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#ifndef __PJ_SCANNER_H__
+#define __PJ_SCANNER_H__
+
+/**
+ * @file scanner.h
+ * @brief Text Scanning.
+ */
+
+#include <pj/types.h>
+
+PJ_BEGIN_DECL
+
+/**
+ * @defgroup PJ_SCAN Text Scanning
+ * @ingroup PJ_MISC
+ * @brief
+ * Text scanning utility.
+ *
+ * @{
+ */
+
+/**
+ * This describes the type of individual character specification in
+ * #pj_cis_buf_t. Basicly the number of bits here
+ */
+#ifndef PJ_CIS_ELEM_TYPE
+# define PJ_CIS_ELEM_TYPE pj_uint32_t
+#endif
+
+/**
+ * This describes the type of individual character specification in
+ * #pj_cis_buf_t.
+ */
+typedef PJ_CIS_ELEM_TYPE pj_cis_elem_t;
+
+/**
+ * Maximum number of input specification in a buffer.
+ * Effectively this means the number of bits in pj_cis_elem_t.
+ */
+#define PJ_CIS_MAX_INDEX (sizeof(pj_cis_elem_t) << 3)
+
+/**
+ * The scanner input specification buffer.
+ */
+typedef struct pj_cis_buf_t
+{
+ pj_cis_elem_t cis_buf[256]; /**< Must be 256 (not 128)! */
+ pj_cis_elem_t use_mask; /**< To keep used indexes. */
+} pj_cis_buf_t;
+
+/**
+ * Character input specification.
+ */
+typedef struct pj_cis_t
+{
+ pj_cis_elem_t *cis_buf; /**< Pointer to buffer. */
+ int cis_id; /**< Id. */
+} pj_cis_t;
+
+/**
+ * Initialize scanner input specification buffer.
+ *
+ * @param cs_buf The scanner character specification.
+ */
+PJ_DECL(void) pj_cis_buf_init(pj_cis_buf_t *cs_buf);
+
+/**
+ * Create a new input specification.
+ *
+ * @param cs_buf Specification buffer.
+ * @param cis Character input specification to be initialized.
+ *
+ * @return PJ_SUCCESS if new specification has been successfully
+ * created, or PJ_ETOOMANY if there are already too many
+ * specifications in the buffer.
+ */
+PJ_DECL(pj_status_t) pj_cis_init(pj_cis_buf_t *cs_buf, pj_cis_t *cis);
+
+/**
+ * Create a new input specification based on an existing specification.
+ *
+ * @param new_cis The new specification to be initialized.
+ * @param existing The existing specification, from which the input
+ * bitmask will be copied to the new specification.
+ *
+ * @return PJ_SUCCESS if new specification has been successfully
+ * created, or PJ_ETOOMANY if there are already too many
+ * specifications in the buffer.
+ */
+PJ_DECL(pj_status_t) pj_cis_dup(pj_cis_t *new_cis, pj_cis_t *existing);
+
+/**
+ * Set the membership of the specified character.
+ * Note that this is a macro, and arguments may be evaluated more than once.
+ *
+ * @param cis Pointer to character input specification.
+ * @param c The character.
+ */
+#define PJ_CIS_SET(cis,c) ((cis)->cis_buf[(c)] |= (1 << (cis)->cis_id))
+
+/**
+ * Remove the membership of the specified character.
+ * Note that this is a macro, and arguments may be evaluated more than once.
+ *
+ * @param cis Pointer to character input specification.
+ * @param c The character to be removed from the membership.
+ */
+#define PJ_CIS_CLR(cis,c) ((cis)->cis_buf[c] &= ~(1 << (cis)->cis_id))
+
+/**
+ * Check the membership of the specified character.
+ * Note that this is a macro, and arguments may be evaluated more than once.
+ *
+ * @param cis Pointer to character input specification.
+ * @param c The character.
+ */
+#define PJ_CIS_ISSET(cis,c) ((cis)->cis_buf[c] & (1 << (cis)->cis_id))
+
+/**
+ * Add the characters in the specified range '[cstart, cend)' to the
+ * specification (the last character itself ('cend') is not added).
+ *
+ * @param cis The scanner character specification.
+ * @param cstart The first character in the range.
+ * @param cend The next character after the last character in the range.
+ */
+PJ_DECL(void) pj_cis_add_range( pj_cis_t *cis, int cstart, int cend);
+
+/**
+ * Add alphabetic characters to the specification.
+ *
+ * @param cis The scanner character specification.
+ */
+PJ_DECL(void) pj_cis_add_alpha( pj_cis_t *cis);
+
+/**
+ * Add numeric characters to the specification.
+ *
+ * @param cis The scanner character specification.
+ */
+PJ_DECL(void) pj_cis_add_num( pj_cis_t *cis);
+
+/**
+ * Add the characters in the string to the specification.
+ *
+ * @param cis The scanner character specification.
+ * @param str The string.
+ */
+PJ_DECL(void) pj_cis_add_str( pj_cis_t *cis, const char *str);
+
+/**
+ * Delete characters in the specified range from the specification.
+ *
+ * @param cis The scanner character specification.
+ * @param cstart The first character in the range.
+ * @param cend The next character after the last character in the range.
+ */
+PJ_DECL(void) pj_cis_del_range( pj_cis_t *cis, int cstart, int cend);
+
+/**
+ * Delete characters in the specified string from the specification.
+ *
+ * @param cis The scanner character specification.
+ * @param str The string.
+ */
+PJ_DECL(void) pj_cis_del_str( pj_cis_t *cis, const char *str);
+
+/**
+ * Invert specification.
+ *
+ * @param cis The scanner character specification.
+ */
+PJ_DECL(void) pj_cis_invert( pj_cis_t *cis );
+
+/**
+ * Check whether the specified character belongs to the specification.
+ *
+ * @param cis The scanner character specification.
+ * @param c The character to check for matching.
+ *
+ * @return Non-zero if match (not necessarily one).
+ */
+PJ_INLINE(int) pj_cis_match( const pj_cis_t *cis, int c )
+{
+ return PJ_CIS_ISSET(cis, c);
+}
+
+
+/**
+ * Flags for scanner.
+ */
+enum
+{
+ /** This flags specifies that the scanner should automatically skip
+ whitespaces
+ */
+ PJ_SCAN_AUTOSKIP_WS = 1,
+
+ /** This flags specifies that the scanner should automatically skip
+ SIP header continuation. This flag implies PJ_SCAN_AUTOSKIP_WS.
+ */
+ PJ_SCAN_AUTOSKIP_WS_HEADER = 3,
+
+ /** Auto-skip new lines.
+ */
+ PJ_SCAN_AUTOSKIP_NEWLINE = 4,
+};
+
+
+/* Forward decl. */
+struct pj_scanner;
+
+
+/**
+ * The callback function type to be called by the scanner when it encounters
+ * syntax error.
+ *
+ * @param scanner The scanner instance that calls the callback .
+ */
+typedef void (*pj_syn_err_func_ptr)(struct pj_scanner *scanner);
+
+
+/**
+ * The text scanner structure.
+ */
+typedef struct pj_scanner
+{
+ char *begin; /**< Start of input buffer. */
+ char *end; /**< End of input buffer. */
+ char *curptr; /**< Current pointer. */
+ int line; /**< Current line. */
+ int col; /**< Current column. */
+ int skip_ws; /**< Skip whitespace flag. */
+ pj_syn_err_func_ptr callback; /**< Syntax error callback. */
+} pj_scanner;
+
+
+/**
+ * This structure can be used by application to store the state of the parser,
+ * so that the scanner state can be rollback to this state when necessary.
+ */
+typedef struct pj_scan_state
+{
+ char *curptr; /**< Current scanner's pointer. */
+ int line; /**< Current line. */
+ int col; /**< Current column. */
+} pj_scan_state;
+
+
+/**
+ * Initialize the scanner. Note that the input string buffer must have
+ * length at least buflen+1 because the scanner will NULL terminate the
+ * string during initialization.
+ *
+ * @param scanner The scanner to be initialized.
+ * @param bufstart The input buffer to scan. Note that buffer[buflen] will be
+ * filled with NULL char until scanner is destroyed, so
+ * the actual buffer length must be at least buflen+1.
+ * @param buflen The length of the input buffer, which normally is
+ * strlen(bufstart).
+ * @param options Zero, or combination of PJ_SCAN_AUTOSKIP_WS or
+ * PJ_SCAN_AUTOSKIP_WS_HEADER
+ * @param callback Callback to be called when the scanner encounters syntax
+ * error condition.
+ */
+PJ_DECL(void) pj_scan_init( pj_scanner *scanner, char *bufstart, int buflen,
+ unsigned options,
+ pj_syn_err_func_ptr callback );
+
+
+/**
+ * Call this function when application has finished using the scanner.
+ *
+ * @param scanner The scanner.
+ */
+PJ_DECL(void) pj_scan_fini( pj_scanner *scanner );
+
+
+/**
+ * Determine whether the EOF condition for the scanner has been met.
+ *
+ * @param scanner The scanner.
+ *
+ * @return Non-zero if scanner is EOF.
+ */
+PJ_INLINE(int) pj_scan_is_eof( const pj_scanner *scanner)
+{
+ return scanner->curptr >= scanner->end;
+}
+
+
+/**
+ * Peek strings in current position according to parameter spec, and return
+ * the strings in parameter out. The current scanner position will not be
+ * moved. If the scanner is already in EOF state, syntax error callback will
+ * be called thrown.
+ *
+ * @param scanner The scanner.
+ * @param spec The spec to match input string.
+ * @param out String to store the result.
+ *
+ * @return the character right after the peek-ed position or zero if there's
+ * no more characters.
+ */
+PJ_DECL(int) pj_scan_peek( pj_scanner *scanner,
+ const pj_cis_t *spec, pj_str_t *out);
+
+
+/**
+ * Peek len characters in current position, and return them in out parameter.
+ * Note that whitespaces or newlines will be returned as it is, regardless
+ * of PJ_SCAN_AUTOSKIP_WS settings. If the character left is less than len,
+ * syntax error callback will be called.
+ *
+ * @param scanner The scanner.
+ * @param len Length to peek.
+ * @param out String to store the result.
+ *
+ * @return the character right after the peek-ed position or zero if there's
+ * no more characters.
+ */
+PJ_DECL(int) pj_scan_peek_n( pj_scanner *scanner,
+ pj_size_t len, pj_str_t *out);
+
+
+/**
+ * Peek strings in current position until spec is matched, and return
+ * the strings in parameter out. The current scanner position will not be
+ * moved. If the scanner is already in EOF state, syntax error callback will
+ * be called.
+ *
+ * @param scanner The scanner.
+ * @param spec The peeking will stop when the input match this spec.
+ * @param out String to store the result.
+ *
+ * @return the character right after the peek-ed position.
+ */
+PJ_DECL(int) pj_scan_peek_until( pj_scanner *scanner,
+ const pj_cis_t *spec,
+ pj_str_t *out);
+
+
+/**
+ * Get characters from the buffer according to the spec, and return them
+ * in out parameter. The scanner will attempt to get as many characters as
+ * possible as long as the spec matches. If the first character doesn't
+ * match the spec, or scanner is already in EOF when this function is called,
+ * an exception will be thrown.
+ *
+ * @param scanner The scanner.
+ * @param spec The spec to match input string.
+ * @param out String to store the result.
+ */
+PJ_DECL(void) pj_scan_get( pj_scanner *scanner,
+ const pj_cis_t *spec, pj_str_t *out);
+
+
+/**
+ * Get characters between quotes. If current input doesn't match begin_quote,
+ * syntax error will be thrown.
+ *
+ * @param scanner The scanner.
+ * @param begin_quote The character to begin the quote.
+ * @param end_quote The character to end the quote.
+ * @param out String to store the result.
+ */
+PJ_DECL(void) pj_scan_get_quote( pj_scanner *scanner,
+ int begin_quote, int end_quote,
+ pj_str_t *out);
+
+/**
+ * Get N characters from the scanner.
+ *
+ * @param scanner The scanner.
+ * @param N Number of characters to get.
+ * @param out String to store the result.
+ */
+PJ_DECL(void) pj_scan_get_n( pj_scanner *scanner,
+ unsigned N, pj_str_t *out);
+
+
+/**
+ * Get one character from the scanner.
+ *
+ * @param scanner The scanner.
+ *
+ * @return The character.
+ */
+PJ_DECL(int) pj_scan_get_char( pj_scanner *scanner );
+
+
+/**
+ * Get a newline from the scanner. A newline is defined as '\\n', or '\\r', or
+ * "\\r\\n". If current input is not newline, syntax error will be thrown.
+ *
+ * @param scanner The scanner.
+ */
+PJ_DECL(void) pj_scan_get_newline( pj_scanner *scanner );
+
+
+/**
+ * Get characters from the scanner and move the scanner position until the
+ * current character matches the spec.
+ *
+ * @param scanner The scanner.
+ * @param spec Get until the input match this spec.
+ * @param out String to store the result.
+ */
+PJ_DECL(void) pj_scan_get_until( pj_scanner *scanner,
+ const pj_cis_t *spec, pj_str_t *out);
+
+
+/**
+ * Get characters from the scanner and move the scanner position until the
+ * current character matches until_char.
+ *
+ * @param scanner The scanner.
+ * @param until_char Get until the input match this character.
+ * @param out String to store the result.
+ */
+PJ_DECL(void) pj_scan_get_until_ch( pj_scanner *scanner,
+ int until_char, pj_str_t *out);
+
+
+/**
+ * Get characters from the scanner and move the scanner position until the
+ * current character matches until_char.
+ *
+ * @param scanner The scanner.
+ * @param until_spec Get until the input match any of these characters.
+ * @param out String to store the result.
+ */
+PJ_DECL(void) pj_scan_get_until_chr( pj_scanner *scanner,
+ const char *until_spec, pj_str_t *out);
+
+/**
+ * Advance the scanner N characters, and skip whitespace
+ * if necessary.
+ *
+ * @param scanner The scanner.
+ * @param N Number of characters to skip.
+ * @param skip Flag to specify whether whitespace should be skipped
+ * after skipping the characters.
+ */
+PJ_DECL(void) pj_scan_advance_n( pj_scanner *scanner,
+ unsigned N, pj_bool_t skip);
+
+
+/**
+ * Compare string in current position with the specified string.
+ *
+ * @param scanner The scanner.
+ * @param s The string to compare with.
+ * @param len Length of the string to compare.
+ *
+ * @return zero, <0, or >0 (just like strcmp()).
+ */
+PJ_DECL(int) pj_scan_strcmp( pj_scanner *scanner, const char *s, int len);
+
+
+/**
+ * Case-less string comparison of current position with the specified
+ * string.
+ *
+ * @param scanner The scanner.
+ * @param s The string to compare with.
+ * @param len Length of the string to compare with.
+ *
+ * @return zero, <0, or >0 (just like strcmp()).
+ */
+PJ_DECL(int) pj_scan_stricmp( pj_scanner *scanner, const char *s, int len);
+
+
+/**
+ * Manually skip whitespaces according to flag that was specified when
+ * the scanner was initialized.
+ *
+ * @param scanner The scanner.
+ */
+PJ_DECL(void) pj_scan_skip_whitespace( pj_scanner *scanner );
+
+
+/**
+ * Save the full scanner state.
+ *
+ * @param scanner The scanner.
+ * @param state Variable to store scanner's state.
+ */
+PJ_DECL(void) pj_scan_save_state( pj_scanner *scanner, pj_scan_state *state);
+
+
+/**
+ * Restore the full scanner state.
+ * Note that this would not restore the string if application has modified
+ * it. This will only restore the scanner scanning position.
+ *
+ * @param scanner The scanner.
+ * @param state State of the scanner.
+ */
+PJ_DECL(void) pj_scan_restore_state( pj_scanner *scanner,
+ pj_scan_state *state);
+
+/**
+ * @}
+ */
+
+
+PJ_END_DECL
+
+#endif
+
diff --git a/pjlib-util/include/pjlib-util/string.h b/pjlib-util/include/pjlib-util/string.h
index bde48e28..617e6bd7 100644
--- a/pjlib-util/include/pjlib-util/string.h
+++ b/pjlib-util/include/pjlib-util/string.h
@@ -1,85 +1,90 @@
-/* $Id: $ */
-/*
- * Copyright (C)2003-2006 Benny Prijono <benny@prijono.org>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
-#ifndef __PJLIB_UTIL_STRING_H__
-#define __PJLIB_UTIL_STRING_H__
-
-/**
- * @file string.h
- * @brief More string functions.
- */
-
-#include <pj/types.h>
-#include <pjlib-util/scanner.h>
-
-PJ_BEGIN_DECL
-
-/**
- * Unescape string.
- *
- * @param str The string to unescape.
- */
-PJ_DECL(void) pj_str_unescape(pj_str_t *str);
-
-/**
- * Unescape string to destination.
- *
- * @param dst Target string.
- * @param src Source string.
- *
- * @return Target string.
- */
-PJ_DECL(pj_str_t*) pj_strcpy_unescape(pj_str_t *dst, const pj_str_t *src);
-
-/**
- * Copy string to destination while escaping reserved characters, up to
- * the specified maximum length.
- *
- * @param dst Target string.
- * @param src Source string.
- * @param max Maximum length to copy to target string.
- * @param unres Unreserved characters, which are allowed to appear
- * unescaped.
- *
- * @return The target string if all characters have been copied
- * successfully, or NULL if there's not enough buffer to
- * escape the strings.
- */
-PJ_DECL(pj_str_t*) pj_strncpy_escape(pj_str_t *dst, const pj_str_t *src,
- pj_ssize_t max, const pj_cis_t *unres);
-
-
-/**
- * Copy string to destination while escaping reserved characters, up to
- * the specified maximum length.
- *
- * @param dst Target string.
- * @param src Source string.
- * @param max Maximum length to copy to target string.
- * @param unres Unreserved characters, which are allowed to appear
- * unescaped.
- *
- * @return The length of the destination, or -1 if there's not
- * enough buffer.
- */
-PJ_DECL(pj_ssize_t) pj_strncpy2_escape(char *dst, const pj_str_t *src,
- pj_ssize_t max, const pj_cis_t *unres);
-
-PJ_END_DECL
-
-#endif /* __PJLIB_UTIL_STRING_H__ */
+/* $Id$ */
+/*
+ * Copyright (C)2003-2006 Benny Prijono <benny@prijono.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#ifndef __PJLIB_UTIL_STRING_H__
+#define __PJLIB_UTIL_STRING_H__
+
+/**
+ * @file string.h
+ * @brief More string functions.
+ */
+
+#include <pj/types.h>
+#include <pjlib-util/scanner.h>
+
+PJ_BEGIN_DECL
+
+/**
+ * Unescape string. If source string does not contain any escaped
+ * characters, the function would simply return the original string.
+ * Otherwise a new string will be allocated.
+ *
+ * @param pool Pool to allocate the string.
+ * @param src Source string to unescape.
+ *
+ * @return String with no escaped characters.
+ */
+PJ_DECL(pj_str_t) pj_str_unescape( pj_pool_t *pool, const pj_str_t *src);
+
+/**
+ * Unescape string to destination.
+ *
+ * @param dst Target string.
+ * @param src Source string.
+ *
+ * @return Target string.
+ */
+PJ_DECL(pj_str_t*) pj_strcpy_unescape(pj_str_t *dst, const pj_str_t *src);
+
+/**
+ * Copy string to destination while escaping reserved characters, up to
+ * the specified maximum length.
+ *
+ * @param dst Target string.
+ * @param src Source string.
+ * @param max Maximum length to copy to target string.
+ * @param unres Unreserved characters, which are allowed to appear
+ * unescaped.
+ *
+ * @return The target string if all characters have been copied
+ * successfully, or NULL if there's not enough buffer to
+ * escape the strings.
+ */
+PJ_DECL(pj_str_t*) pj_strncpy_escape(pj_str_t *dst, const pj_str_t *src,
+ pj_ssize_t max, const pj_cis_t *unres);
+
+
+/**
+ * Copy string to destination while escaping reserved characters, up to
+ * the specified maximum length.
+ *
+ * @param dst Target string.
+ * @param src Source string.
+ * @param max Maximum length to copy to target string.
+ * @param unres Unreserved characters, which are allowed to appear
+ * unescaped.
+ *
+ * @return The length of the destination, or -1 if there's not
+ * enough buffer.
+ */
+PJ_DECL(pj_ssize_t) pj_strncpy2_escape(char *dst, const pj_str_t *src,
+ pj_ssize_t max, const pj_cis_t *unres);
+
+PJ_END_DECL
+
+#endif /* __PJLIB_UTIL_STRING_H__ */
diff --git a/pjlib-util/include/pjlib-util/stun.h b/pjlib-util/include/pjlib-util/stun.h
index b2af6d35..353e07f6 100644
--- a/pjlib-util/include/pjlib-util/stun.h
+++ b/pjlib-util/include/pjlib-util/stun.h
@@ -1,141 +1,141 @@
-/* $Id */
-/*
- * Copyright (C) 2003-2005 Benny Prijono <benny@prijono.org>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
-#ifndef __PJ_STUN_H__
-#define __PJ_STUN_H__
-
-#include <pj/types.h>
-#include <pj/sock.h>
-
-PJ_BEGIN_DECL
-
-#define PJ_STUN_MAX_ATTR 16
-
-typedef enum pj_stun_msg_type
-{
- PJ_STUN_BINDING_REQUEST = 0x0001,
- PJ_STUN_BINDING_RESPONSE = 0x0101,
- PJ_STUN_BINDING_ERROR_RESPONSE = 0x0111,
- PJ_STUN_SHARED_SECRET_REQUEST = 0x0002,
- PJ_STUN_SHARED_SECRET_RESPONSE = 0x0102,
- PJ_STUN_SHARED_SECRET_ERROR_RESPONSE = 0x0112
-} pj_stun_msg_type;
-
-typedef enum pj_stun_attr_type
-{
- PJ_STUN_ATTR_MAPPED_ADDR = 1,
- PJ_STUN_ATTR_RESPONSE_ADDR,
- PJ_STUN_ATTR_CHANGE_REQUEST,
- PJ_STUN_ATTR_SOURCE_ADDR,
- PJ_STUN_ATTR_CHANGED_ADDR,
- PJ_STUN_ATTR_USERNAME,
- PJ_STUN_ATTR_PASSWORD,
- PJ_STUN_ATTR_MESSAGE_INTEGRITY,
- PJ_STUN_ATTR_ERROR_CODE,
- PJ_STUN_ATTR_UNKNOWN_ATTRIBUTES,
- PJ_STUN_ATTR_REFLECTED_FORM
-} pj_stun_attr_type;
-
-typedef struct pj_stun_msg_hdr
-{
- pj_uint16_t type;
- pj_uint16_t length;
- pj_uint32_t tsx[4];
-} pj_stun_msg_hdr;
-
-typedef struct pj_stun_attr_hdr
-{
- pj_uint16_t type;
- pj_uint16_t length;
-} pj_stun_attr_hdr;
-
-typedef struct pj_stun_mapped_addr_attr
-{
- pj_stun_attr_hdr hdr;
- pj_uint8_t ignored;
- pj_uint8_t family;
- pj_uint16_t port;
- pj_uint32_t addr;
-} pj_stun_mapped_addr_attr;
-
-typedef pj_stun_mapped_addr_attr pj_stun_response_addr_attr;
-typedef pj_stun_mapped_addr_attr pj_stun_changed_addr_attr;
-typedef pj_stun_mapped_addr_attr pj_stun_src_addr_attr;
-typedef pj_stun_mapped_addr_attr pj_stun_reflected_form_attr;
-
-typedef struct pj_stun_change_request_attr
-{
- pj_stun_attr_hdr hdr;
- pj_uint32_t value;
-} pj_stun_change_request_attr;
-
-typedef struct pj_stun_username_attr
-{
- pj_stun_attr_hdr hdr;
- pj_uint32_t value[1];
-} pj_stun_username_attr;
-
-typedef pj_stun_username_attr pj_stun_password_attr;
-
-typedef struct pj_stun_error_code_attr
-{
- pj_stun_attr_hdr hdr;
- pj_uint16_t ignored;
- pj_uint8_t err_class;
- pj_uint8_t number;
- char reason[4];
-} pj_stun_error_code_attr;
-
-typedef struct pj_stun_msg
-{
- pj_stun_msg_hdr *hdr;
- int attr_count;
- pj_stun_attr_hdr *attr[PJ_STUN_MAX_ATTR];
-} pj_stun_msg;
-
-/* STUN message API (stun.c). */
-
-PJ_DECL(pj_status_t) pj_stun_create_bind_req( pj_pool_t *pool,
- void **msg, pj_size_t *len,
- pj_uint32_t id_hi,
- pj_uint32_t id_lo);
-PJ_DECL(pj_status_t) pj_stun_parse_msg( void *buf, pj_size_t len,
- pj_stun_msg *msg);
-PJ_DECL(void*) pj_stun_msg_find_attr( pj_stun_msg *msg, pj_stun_attr_type t);
-
-/* STUN simple client API (stun_client.c) */
-enum pj_stun_err_code {
- PJ_STUN_ERR_MEMORY = (-2),
- PJ_STUN_ERR_RESOLVE = (-3),
- PJ_STUN_ERR_TRANSPORT = (-4),
- PJ_STUN_ERR_INVALID_MSG = (-5),
- PJ_STUN_ERR_NO_RESPONSE = (-6),
- PJ_STUN_ERR_SYMETRIC = (-7),
-};
-
-PJ_DECL(pj_status_t) pj_stun_get_mapped_addr( pj_pool_factory *pf,
- int sock_cnt, pj_sock_t sock[],
- const pj_str_t *srv1, int port1,
- const pj_str_t *srv2, int port2,
- pj_sockaddr_in mapped_addr[]);
-PJ_DECL(const char*) pj_stun_get_err_msg(pj_status_t status);
-
-PJ_END_DECL
-
-#endif /* __PJ_STUN_H__ */
-
+/* $Id */
+/*
+ * Copyright (C) 2003-2005 Benny Prijono <benny@prijono.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#ifndef __PJ_STUN_H__
+#define __PJ_STUN_H__
+
+#include <pj/types.h>
+#include <pj/sock.h>
+
+PJ_BEGIN_DECL
+
+#define PJ_STUN_MAX_ATTR 16
+
+typedef enum pj_stun_msg_type
+{
+ PJ_STUN_BINDING_REQUEST = 0x0001,
+ PJ_STUN_BINDING_RESPONSE = 0x0101,
+ PJ_STUN_BINDING_ERROR_RESPONSE = 0x0111,
+ PJ_STUN_SHARED_SECRET_REQUEST = 0x0002,
+ PJ_STUN_SHARED_SECRET_RESPONSE = 0x0102,
+ PJ_STUN_SHARED_SECRET_ERROR_RESPONSE = 0x0112
+} pj_stun_msg_type;
+
+typedef enum pj_stun_attr_type
+{
+ PJ_STUN_ATTR_MAPPED_ADDR = 1,
+ PJ_STUN_ATTR_RESPONSE_ADDR,
+ PJ_STUN_ATTR_CHANGE_REQUEST,
+ PJ_STUN_ATTR_SOURCE_ADDR,
+ PJ_STUN_ATTR_CHANGED_ADDR,
+ PJ_STUN_ATTR_USERNAME,
+ PJ_STUN_ATTR_PASSWORD,
+ PJ_STUN_ATTR_MESSAGE_INTEGRITY,
+ PJ_STUN_ATTR_ERROR_CODE,
+ PJ_STUN_ATTR_UNKNOWN_ATTRIBUTES,
+ PJ_STUN_ATTR_REFLECTED_FORM
+} pj_stun_attr_type;
+
+typedef struct pj_stun_msg_hdr
+{
+ pj_uint16_t type;
+ pj_uint16_t length;
+ pj_uint32_t tsx[4];
+} pj_stun_msg_hdr;
+
+typedef struct pj_stun_attr_hdr
+{
+ pj_uint16_t type;
+ pj_uint16_t length;
+} pj_stun_attr_hdr;
+
+typedef struct pj_stun_mapped_addr_attr
+{
+ pj_stun_attr_hdr hdr;
+ pj_uint8_t ignored;
+ pj_uint8_t family;
+ pj_uint16_t port;
+ pj_uint32_t addr;
+} pj_stun_mapped_addr_attr;
+
+typedef pj_stun_mapped_addr_attr pj_stun_response_addr_attr;
+typedef pj_stun_mapped_addr_attr pj_stun_changed_addr_attr;
+typedef pj_stun_mapped_addr_attr pj_stun_src_addr_attr;
+typedef pj_stun_mapped_addr_attr pj_stun_reflected_form_attr;
+
+typedef struct pj_stun_change_request_attr
+{
+ pj_stun_attr_hdr hdr;
+ pj_uint32_t value;
+} pj_stun_change_request_attr;
+
+typedef struct pj_stun_username_attr
+{
+ pj_stun_attr_hdr hdr;
+ pj_uint32_t value[1];
+} pj_stun_username_attr;
+
+typedef pj_stun_username_attr pj_stun_password_attr;
+
+typedef struct pj_stun_error_code_attr
+{
+ pj_stun_attr_hdr hdr;
+ pj_uint16_t ignored;
+ pj_uint8_t err_class;
+ pj_uint8_t number;
+ char reason[4];
+} pj_stun_error_code_attr;
+
+typedef struct pj_stun_msg
+{
+ pj_stun_msg_hdr *hdr;
+ int attr_count;
+ pj_stun_attr_hdr *attr[PJ_STUN_MAX_ATTR];
+} pj_stun_msg;
+
+/* STUN message API (stun.c). */
+
+PJ_DECL(pj_status_t) pj_stun_create_bind_req( pj_pool_t *pool,
+ void **msg, pj_size_t *len,
+ pj_uint32_t id_hi,
+ pj_uint32_t id_lo);
+PJ_DECL(pj_status_t) pj_stun_parse_msg( void *buf, pj_size_t len,
+ pj_stun_msg *msg);
+PJ_DECL(void*) pj_stun_msg_find_attr( pj_stun_msg *msg, pj_stun_attr_type t);
+
+/* STUN simple client API (stun_client.c) */
+enum pj_stun_err_code {
+ PJ_STUN_ERR_MEMORY = (-2),
+ PJ_STUN_ERR_RESOLVE = (-3),
+ PJ_STUN_ERR_TRANSPORT = (-4),
+ PJ_STUN_ERR_INVALID_MSG = (-5),
+ PJ_STUN_ERR_NO_RESPONSE = (-6),
+ PJ_STUN_ERR_SYMETRIC = (-7),
+};
+
+PJ_DECL(pj_status_t) pj_stun_get_mapped_addr( pj_pool_factory *pf,
+ int sock_cnt, pj_sock_t sock[],
+ const pj_str_t *srv1, int port1,
+ const pj_str_t *srv2, int port2,
+ pj_sockaddr_in mapped_addr[]);
+PJ_DECL(const char*) pj_stun_get_err_msg(pj_status_t status);
+
+PJ_END_DECL
+
+#endif /* __PJ_STUN_H__ */
+
diff --git a/pjlib-util/include/pjlib-util/xml.h b/pjlib-util/include/pjlib-util/xml.h
index 05f67bbf..d54fc2d5 100644
--- a/pjlib-util/include/pjlib-util/xml.h
+++ b/pjlib-util/include/pjlib-util/xml.h
@@ -1,171 +1,171 @@
-/* $Id$ */
-/*
- * Copyright (C) 2003-2005 Benny Prijono <benny@prijono.org>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
-#ifndef __PJ_XML_H__
-#define __PJ_XML_H__
-
-/**
- * @file xml.h
- * @brief PJLIB XML Parser/Helper.
- */
-
-#include <pj/types.h>
-#include <pj/list.h>
-
-PJ_BEGIN_DECL
-
-/**
- * @defgroup PJ_XML XML Parser/Helper.
- * @ingroup PJ
- * @{
- */
-
-/** Typedef for XML attribute. */
-typedef struct pj_xml_attr pj_xml_attr;
-
-/** Typedef for XML nodes. */
-typedef struct pj_xml_node pj_xml_node;
-
-/** This structure declares XML attribute. */
-struct pj_xml_attr
-{
- PJ_DECL_LIST_MEMBER(pj_xml_attr); /**< Standard list elements. */
- pj_str_t name; /**< Attribute name. */
- pj_str_t value; /**< Attribute value. */
-};
-
-/** This structure describes XML node head inside XML node structure.
- */
-typedef struct pj_xml_node_head
-{
- PJ_DECL_LIST_MEMBER(pj_xml_node); /**< Standard list elements. */
-} pj_xml_node_head;
-
-/** This structure describes XML node. */
-struct pj_xml_node
-{
- PJ_DECL_LIST_MEMBER(pj_xml_node); /**< List @a prev and @a next member */
- pj_str_t name; /**< Node name. */
- pj_xml_attr attr_head; /**< Attribute list. */
- pj_xml_node_head node_head; /**< Node list. */
- pj_str_t content; /**< Node content. */
-};
-
-/**
- * Parse XML message into XML document with a single root node. The parser
- * is capable of parsing XML processing instruction construct ("<?") and
- * XML comments ("<!--"), however such constructs will be ignored and will not
- * be included in the resulted XML node tree.
- *
- * @param pool Pool to allocate memory from.
- * @param msg The XML message to parse.
- * @param len The length of the message.
- *
- * @return XML root node, or NULL if the XML document can not be parsed.
- */
-PJ_DECL(pj_xml_node*) pj_xml_parse( pj_pool_t *pool, char *msg, pj_size_t len);
-
-
-/**
- * Print XML into XML message. Note that the function WILL NOT NULL terminate
- * the output.
- *
- * @param node The XML node to print.
- * @param buf Buffer to hold the output message.
- * @param len The length of the buffer.
- * @param prolog If set to nonzero, will print XML prolog ("<?xml..")
- *
- * @return The size of the printed message, or -1 if there is not
- * sufficient space in the buffer to print the message.
- */
-PJ_DECL(int) pj_xml_print( const pj_xml_node *node, char *buf, pj_size_t len,
- pj_bool_t include_prolog);
-
-/**
- * Add node to another node.
- *
- * @param parent Parent node.
- * @param node Node to be added to parent.
- */
-PJ_DECL(void) pj_xml_add_node( pj_xml_node *parent, pj_xml_node *node );
-
-
-/**
- * Add attribute to a node.
- *
- * @param node Node.
- * @param attr Attribute to add to node.
- */
-PJ_DECL(void) pj_xml_add_attr( pj_xml_node *node, pj_xml_attr *attr );
-
-/**
- * Find first node with the specified name.
- *
- * @param parent Parent node.
- * @param name Node name to find.
- *
- * @return XML node found or NULL.
- */
-PJ_DECL(pj_xml_node*) pj_xml_find_node(pj_xml_node *parent, const pj_str_t *name);
-
-/**
- * Find first node with the specified name.
- *
- * @param parent Parent node.
- * @param name Node name to find.
- *
- * @return XML node found or NULL.
- */
-PJ_DECL(pj_xml_node*) pj_xml_find_next_node(pj_xml_node *parent, pj_xml_node *node,
- const pj_str_t *name);
-
-/**
- * Find first attribute within a node with the specified name and optional value.
- *
- * @param node XML Node.
- * @param name Attribute name to find.
- * @param value Optional value to match.
- *
- * @return XML attribute found, or NULL.
- */
-PJ_DECL(pj_xml_attr*) pj_xml_find_attr(pj_xml_node *node, const pj_str_t *name,
- const pj_str_t *value);
-
-
-/**
- * Find a direct child node with the specified name and match the function.
- *
- * @param node Parent node.
- * @param name Optional name.
- * @param data Data to be passed to matching function.
- * @param match Optional matching function.
- *
- * @return The first matched node, or NULL.
- */
-PJ_DECL(pj_xml_node*) pj_xml_find( pj_xml_node *parent, const pj_str_t *name,
- const void *data,
- pj_bool_t (*match)(pj_xml_node *, const void*));
-
-
-/**
- * @}
- */
-
-PJ_END_DECL
-
-#endif /* __PJ_XML_H__ */
+/* $Id$ */
+/*
+ * Copyright (C) 2003-2005 Benny Prijono <benny@prijono.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#ifndef __PJ_XML_H__
+#define __PJ_XML_H__
+
+/**
+ * @file xml.h
+ * @brief PJLIB XML Parser/Helper.
+ */
+
+#include <pj/types.h>
+#include <pj/list.h>
+
+PJ_BEGIN_DECL
+
+/**
+ * @defgroup PJ_XML XML Parser/Helper.
+ * @ingroup PJ
+ * @{
+ */
+
+/** Typedef for XML attribute. */
+typedef struct pj_xml_attr pj_xml_attr;
+
+/** Typedef for XML nodes. */
+typedef struct pj_xml_node pj_xml_node;
+
+/** This structure declares XML attribute. */
+struct pj_xml_attr
+{
+ PJ_DECL_LIST_MEMBER(pj_xml_attr); /**< Standard list elements. */
+ pj_str_t name; /**< Attribute name. */
+ pj_str_t value; /**< Attribute value. */
+};
+
+/** This structure describes XML node head inside XML node structure.
+ */
+typedef struct pj_xml_node_head
+{
+ PJ_DECL_LIST_MEMBER(pj_xml_node); /**< Standard list elements. */
+} pj_xml_node_head;
+
+/** This structure describes XML node. */
+struct pj_xml_node
+{
+ PJ_DECL_LIST_MEMBER(pj_xml_node); /**< List @a prev and @a next member */
+ pj_str_t name; /**< Node name. */
+ pj_xml_attr attr_head; /**< Attribute list. */
+ pj_xml_node_head node_head; /**< Node list. */
+ pj_str_t content; /**< Node content. */
+};
+
+/**
+ * Parse XML message into XML document with a single root node. The parser
+ * is capable of parsing XML processing instruction construct ("<?") and
+ * XML comments ("<!--"), however such constructs will be ignored and will not
+ * be included in the resulted XML node tree.
+ *
+ * @param pool Pool to allocate memory from.
+ * @param msg The XML message to parse.
+ * @param len The length of the message.
+ *
+ * @return XML root node, or NULL if the XML document can not be parsed.
+ */
+PJ_DECL(pj_xml_node*) pj_xml_parse( pj_pool_t *pool, char *msg, pj_size_t len);
+
+
+/**
+ * Print XML into XML message. Note that the function WILL NOT NULL terminate
+ * the output.
+ *
+ * @param node The XML node to print.
+ * @param buf Buffer to hold the output message.
+ * @param len The length of the buffer.
+ * @param prolog If set to nonzero, will print XML prolog ("<?xml..")
+ *
+ * @return The size of the printed message, or -1 if there is not
+ * sufficient space in the buffer to print the message.
+ */
+PJ_DECL(int) pj_xml_print( const pj_xml_node *node, char *buf, pj_size_t len,
+ pj_bool_t include_prolog);
+
+/**
+ * Add node to another node.
+ *
+ * @param parent Parent node.
+ * @param node Node to be added to parent.
+ */
+PJ_DECL(void) pj_xml_add_node( pj_xml_node *parent, pj_xml_node *node );
+
+
+/**
+ * Add attribute to a node.
+ *
+ * @param node Node.
+ * @param attr Attribute to add to node.
+ */
+PJ_DECL(void) pj_xml_add_attr( pj_xml_node *node, pj_xml_attr *attr );
+
+/**
+ * Find first node with the specified name.
+ *
+ * @param parent Parent node.
+ * @param name Node name to find.
+ *
+ * @return XML node found or NULL.
+ */
+PJ_DECL(pj_xml_node*) pj_xml_find_node(pj_xml_node *parent, const pj_str_t *name);
+
+/**
+ * Find first node with the specified name.
+ *
+ * @param parent Parent node.
+ * @param name Node name to find.
+ *
+ * @return XML node found or NULL.
+ */
+PJ_DECL(pj_xml_node*) pj_xml_find_next_node(pj_xml_node *parent, pj_xml_node *node,
+ const pj_str_t *name);
+
+/**
+ * Find first attribute within a node with the specified name and optional value.
+ *
+ * @param node XML Node.
+ * @param name Attribute name to find.
+ * @param value Optional value to match.
+ *
+ * @return XML attribute found, or NULL.
+ */
+PJ_DECL(pj_xml_attr*) pj_xml_find_attr(pj_xml_node *node, const pj_str_t *name,
+ const pj_str_t *value);
+
+
+/**
+ * Find a direct child node with the specified name and match the function.
+ *
+ * @param node Parent node.
+ * @param name Optional name.
+ * @param data Data to be passed to matching function.
+ * @param match Optional matching function.
+ *
+ * @return The first matched node, or NULL.
+ */
+PJ_DECL(pj_xml_node*) pj_xml_find( pj_xml_node *parent, const pj_str_t *name,
+ const void *data,
+ pj_bool_t (*match)(pj_xml_node *, const void*));
+
+
+/**
+ * @}
+ */
+
+PJ_END_DECL
+
+#endif /* __PJ_XML_H__ */
diff --git a/pjlib-util/src/pjlib-util-test/main.c b/pjlib-util/src/pjlib-util-test/main.c
index 2fc23a79..9925b710 100644
--- a/pjlib-util/src/pjlib-util-test/main.c
+++ b/pjlib-util/src/pjlib-util-test/main.c
@@ -1,53 +1,53 @@
-/* $Id */
-/*
- * Copyright (C) 2003-2006 Benny Prijono <benny@prijono.org>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
-#include "test.h"
-
-#if defined(PJ_SUNOS) && PJ_SUNOS!=0
-#include <signal.h>
-static void init_signals()
-{
- struct sigaction act;
-
- memset(&act, 0, sizeof(act));
- act.sa_handler = SIG_IGN;
-
- sigaction(SIGALRM, &act, NULL);
-}
-
-#else
-#define init_signals()
-#endif
-
-#define boost()
-
-int main(int argc, char *argv[])
-{
- int rc;
-
- PJ_UNUSED_ARG(argc);
- PJ_UNUSED_ARG(argv);
-
- boost();
- init_signals();
-
- rc = test_main();
-
- return rc;
-}
-
+/* $Id */
+/*
+ * Copyright (C) 2003-2006 Benny Prijono <benny@prijono.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#include "test.h"
+
+#if defined(PJ_SUNOS) && PJ_SUNOS!=0
+#include <signal.h>
+static void init_signals()
+{
+ struct sigaction act;
+
+ memset(&act, 0, sizeof(act));
+ act.sa_handler = SIG_IGN;
+
+ sigaction(SIGALRM, &act, NULL);
+}
+
+#else
+#define init_signals()
+#endif
+
+#define boost()
+
+int main(int argc, char *argv[])
+{
+ int rc;
+
+ PJ_UNUSED_ARG(argc);
+ PJ_UNUSED_ARG(argv);
+
+ boost();
+ init_signals();
+
+ rc = test_main();
+
+ return rc;
+}
+
diff --git a/pjlib-util/src/pjlib-util-test/test.c b/pjlib-util/src/pjlib-util-test/test.c
index 511e7a12..991c1c39 100644
--- a/pjlib-util/src/pjlib-util-test/test.c
+++ b/pjlib-util/src/pjlib-util-test/test.c
@@ -1,86 +1,86 @@
-/* $Id */
-/*
- * Copyright (C) 2003-2006 Benny Prijono <benny@prijono.org>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
-#include "test.h"
-#include <pjlib.h>
-
-void app_perror(const char *msg, pj_status_t rc)
-{
- char errbuf[256];
-
- PJ_CHECK_STACK();
-
- pj_strerror(rc, errbuf, sizeof(errbuf));
- PJ_LOG(1,("test", "%s: [pj_status_t=%d] %s", msg, rc, errbuf));
-}
-
-#define DO_TEST(test) do { \
- PJ_LOG(3, ("test", "Running %s...", #test)); \
- rc = test; \
- PJ_LOG(3, ("test", \
- "%s(%d)", \
- (char*)(rc ? "..ERROR" : "..success"), rc)); \
- if (rc!=0) goto on_return; \
- } while (0)
-
-
-pj_pool_factory *mem;
-
-
-static int test_inner(void)
-{
- pj_caching_pool caching_pool;
- int rc = 0;
-
- mem = &caching_pool.factory;
-
- pj_log_set_level(3);
- pj_log_set_decor(PJ_LOG_HAS_NEWLINE | PJ_LOG_HAS_TIME |
- PJ_LOG_HAS_MICRO_SEC);
-
- rc = pj_init();
- if (rc != 0) {
- app_perror("pj_init() error!!", rc);
- return rc;
- }
-
- pj_dump_config();
- pj_caching_pool_init( &caching_pool, &pj_pool_factory_default_policy, 0 );
- DO_TEST(xml_test());
-
-on_return:
- return rc;
-}
-
-int test_main(void)
-{
- PJ_USE_EXCEPTION;
-
- PJ_TRY {
- return test_inner();
- }
- PJ_DEFAULT {
- int id = PJ_GET_EXCEPTION();
- PJ_LOG(3,("test", "FATAL: unhandled exception id %d (%s)",
- id, pj_exception_id_name(id)));
- }
- PJ_END;
-
- return -1;
-}
-
+/* $Id */
+/*
+ * Copyright (C) 2003-2006 Benny Prijono <benny@prijono.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#include "test.h"
+#include <pjlib.h>
+
+void app_perror(const char *msg, pj_status_t rc)
+{
+ char errbuf[256];
+
+ PJ_CHECK_STACK();
+
+ pj_strerror(rc, errbuf, sizeof(errbuf));
+ PJ_LOG(1,("test", "%s: [pj_status_t=%d] %s", msg, rc, errbuf));
+}
+
+#define DO_TEST(test) do { \
+ PJ_LOG(3, ("test", "Running %s...", #test)); \
+ rc = test; \
+ PJ_LOG(3, ("test", \
+ "%s(%d)", \
+ (char*)(rc ? "..ERROR" : "..success"), rc)); \
+ if (rc!=0) goto on_return; \
+ } while (0)
+
+
+pj_pool_factory *mem;
+
+
+static int test_inner(void)
+{
+ pj_caching_pool caching_pool;
+ int rc = 0;
+
+ mem = &caching_pool.factory;
+
+ pj_log_set_level(3);
+ pj_log_set_decor(PJ_LOG_HAS_NEWLINE | PJ_LOG_HAS_TIME |
+ PJ_LOG_HAS_MICRO_SEC);
+
+ rc = pj_init();
+ if (rc != 0) {
+ app_perror("pj_init() error!!", rc);
+ return rc;
+ }
+
+ pj_dump_config();
+ pj_caching_pool_init( &caching_pool, &pj_pool_factory_default_policy, 0 );
+ DO_TEST(xml_test());
+
+on_return:
+ return rc;
+}
+
+int test_main(void)
+{
+ PJ_USE_EXCEPTION;
+
+ PJ_TRY {
+ return test_inner();
+ }
+ PJ_DEFAULT {
+ int id = PJ_GET_EXCEPTION();
+ PJ_LOG(3,("test", "FATAL: unhandled exception id %d (%s)",
+ id, pj_exception_id_name(id)));
+ }
+ PJ_END;
+
+ return -1;
+}
+
diff --git a/pjlib-util/src/pjlib-util-test/test.h b/pjlib-util/src/pjlib-util-test/test.h
index c4467290..c3ef3e4c 100644
--- a/pjlib-util/src/pjlib-util-test/test.h
+++ b/pjlib-util/src/pjlib-util-test/test.h
@@ -1,28 +1,28 @@
-/* $Id */
-/*
- * Copyright (C) 2003-2006 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 <pj/types.h>
-
-#define INCLUDE_XML_TEST 1
-
-extern int xml_test(void);
-extern int test_main(void);
-
-extern void app_perror(const char *title, pj_status_t rc);
-extern pj_pool_factory *mem;
-
+/* $Id */
+/*
+ * Copyright (C) 2003-2006 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 <pj/types.h>
+
+#define INCLUDE_XML_TEST 1
+
+extern int xml_test(void);
+extern int test_main(void);
+
+extern void app_perror(const char *title, pj_status_t rc);
+extern pj_pool_factory *mem;
+
diff --git a/pjlib-util/src/pjlib-util-test/xml.c b/pjlib-util/src/pjlib-util-test/xml.c
index 9ad4a73e..e20a68ff 100644
--- a/pjlib-util/src/pjlib-util-test/xml.c
+++ b/pjlib-util/src/pjlib-util-test/xml.c
@@ -1,145 +1,145 @@
-/* $Id$ */
-/*
- * Copyright (C) 2003-2006 Benny Prijono <benny@prijono.org>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
-#include "test.h"
-
-
-#if INCLUDE_XML_TEST
-
-#include <pjlib-util/xml.h>
-#include <pjlib.h>
-
-#define THIS_FILE "xml_test"
-
-static const char *xml_doc[] =
-{
-" <?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
-" <p:pidf-full xmlns=\"urn:ietf:params:xml:ns:pidf\"\n"
-" xmlns:p=\"urn:ietf:params:xml:ns:pidf-diff\"\n"
-" xmlns:r=\"urn:ietf:params:xml:ns:pidf:rpid\"\n"
-" xmlns:c=\"urn:ietf:params:xml:ns:pidf:caps\"\n"
-" entity=\"pres:someone@example.com\"\n"
-" version=\"567\">\n"
-"\n"
-" <tuple id=\"sg89ae\">\n"
-" <status>\n"
-" <basic>open</basic>\n"
-" <r:relationship>assistant</r:relationship>\n"
-" </status>\n"
-" <c:servcaps>\n"
-" <c:audio>true</c:audio>\n"
-" <c:video>false</c:video>\n"
-" <c:message>true</c:message>\n"
-" </c:servcaps>\n"
-" <contact priority=\"0.8\">tel:09012345678</contact>\n"
-" </tuple>\n"
-"\n"
-" <tuple id=\"cg231jcr\">\n"
-" <status>\n"
-" <basic>open</basic>\n"
-" </status>\n"
-" <contact priority=\"1.0\">im:pep@example.com</contact>\n"
-" </tuple>\n"
-"\n"
-" <tuple id=\"r1230d\">\n"
-" <status>\n"
-" <basic>closed</basic>\n"
-" <r:activity>meeting</r:activity>\n"
-" </status>\n"
-" <r:homepage>http://example.com/~pep/</r:homepage>\n"
-" <r:icon>http://example.com/~pep/icon.gif</r:icon>\n"
-" <r:card>http://example.com/~pep/card.vcd</r:card>\n"
-" <contact priority=\"0.9\">sip:pep@example.com</contact>\n"
-" </tuple>\n"
-"\n"
-" <note xml:lang=\"en\">Full state presence document</note>\n"
-"\n"
-" <r:person>\n"
-" <r:status>\n"
-" <r:activities>\n"
-" <r:on-the-phone/>\n"
-" <r:busy/>\n"
-" </r:activities>\n"
-" </r:status>\n"
-" </r:person>\n"
-"\n"
-" <r:device id=\"urn:esn:600b40c7\">\n"
-" <r:status>\n"
-" <c:devcaps>\n"
-" <c:mobility>\n"
-" <c:supported>\n"
-" <c:mobile/>\n"
-" </c:supported>\n"
-" </c:mobility>\n"
-" </c:devcaps>\n"
-" </r:status>\n"
-" </r:device>\n"
-"\n"
-" </p:pidf-full>\n"
-}
-;
-
-static int xml_parse_print_test(const char *doc)
-{
- pj_str_t msg;
- pj_pool_t *pool;
- pj_xml_node *root;
- char *output;
- int output_len;
-
- pool = pj_pool_create(mem, "xml", 4096, 1024, NULL);
- pj_strdup2(pool, &msg, doc);
- root = pj_xml_parse(pool, msg.ptr, msg.slen);
- if (!root) {
- PJ_LOG(1, (THIS_FILE, " Error: unable to parse XML"));
- return -10;
- }
-
- output = (char*)pj_pool_alloc(pool, msg.slen + 512);
- pj_memset(output, 0, msg.slen+512);
- output_len = pj_xml_print(root, output, msg.slen+512, PJ_TRUE);
- if (output_len < 1) {
- PJ_LOG(1, (THIS_FILE, " Error: buffer too small to print XML file"));
- return -20;
- }
- output[output_len] = '\0';
-
-
- pj_pool_release(pool);
- return 0;
-}
-
-int xml_test()
-{
- unsigned i;
- for (i=0; i<sizeof(xml_doc)/sizeof(xml_doc[0]); ++i) {
- int status;
- if ((status=xml_parse_print_test(xml_doc[i])) != 0)
- return status;
- }
- return 0;
-}
-
-#else
-/* To prevent warning about "translation unit is empty"
- * when this test is disabled.
- */
-int dummy_xml_test;
-#endif /* INCLUDE_XML_TEST */
-
-
+/* $Id$ */
+/*
+ * Copyright (C) 2003-2006 Benny Prijono <benny@prijono.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#include "test.h"
+
+
+#if INCLUDE_XML_TEST
+
+#include <pjlib-util/xml.h>
+#include <pjlib.h>
+
+#define THIS_FILE "xml_test"
+
+static const char *xml_doc[] =
+{
+" <?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
+" <p:pidf-full xmlns=\"urn:ietf:params:xml:ns:pidf\"\n"
+" xmlns:p=\"urn:ietf:params:xml:ns:pidf-diff\"\n"
+" xmlns:r=\"urn:ietf:params:xml:ns:pidf:rpid\"\n"
+" xmlns:c=\"urn:ietf:params:xml:ns:pidf:caps\"\n"
+" entity=\"pres:someone@example.com\"\n"
+" version=\"567\">\n"
+"\n"
+" <tuple id=\"sg89ae\">\n"
+" <status>\n"
+" <basic>open</basic>\n"
+" <r:relationship>assistant</r:relationship>\n"
+" </status>\n"
+" <c:servcaps>\n"
+" <c:audio>true</c:audio>\n"
+" <c:video>false</c:video>\n"
+" <c:message>true</c:message>\n"
+" </c:servcaps>\n"
+" <contact priority=\"0.8\">tel:09012345678</contact>\n"
+" </tuple>\n"
+"\n"
+" <tuple id=\"cg231jcr\">\n"
+" <status>\n"
+" <basic>open</basic>\n"
+" </status>\n"
+" <contact priority=\"1.0\">im:pep@example.com</contact>\n"
+" </tuple>\n"
+"\n"
+" <tuple id=\"r1230d\">\n"
+" <status>\n"
+" <basic>closed</basic>\n"
+" <r:activity>meeting</r:activity>\n"
+" </status>\n"
+" <r:homepage>http://example.com/~pep/</r:homepage>\n"
+" <r:icon>http://example.com/~pep/icon.gif</r:icon>\n"
+" <r:card>http://example.com/~pep/card.vcd</r:card>\n"
+" <contact priority=\"0.9\">sip:pep@example.com</contact>\n"
+" </tuple>\n"
+"\n"
+" <note xml:lang=\"en\">Full state presence document</note>\n"
+"\n"
+" <r:person>\n"
+" <r:status>\n"
+" <r:activities>\n"
+" <r:on-the-phone/>\n"
+" <r:busy/>\n"
+" </r:activities>\n"
+" </r:status>\n"
+" </r:person>\n"
+"\n"
+" <r:device id=\"urn:esn:600b40c7\">\n"
+" <r:status>\n"
+" <c:devcaps>\n"
+" <c:mobility>\n"
+" <c:supported>\n"
+" <c:mobile/>\n"
+" </c:supported>\n"
+" </c:mobility>\n"
+" </c:devcaps>\n"
+" </r:status>\n"
+" </r:device>\n"
+"\n"
+" </p:pidf-full>\n"
+}
+;
+
+static int xml_parse_print_test(const char *doc)
+{
+ pj_str_t msg;
+ pj_pool_t *pool;
+ pj_xml_node *root;
+ char *output;
+ int output_len;
+
+ pool = pj_pool_create(mem, "xml", 4096, 1024, NULL);
+ pj_strdup2(pool, &msg, doc);
+ root = pj_xml_parse(pool, msg.ptr, msg.slen);
+ if (!root) {
+ PJ_LOG(1, (THIS_FILE, " Error: unable to parse XML"));
+ return -10;
+ }
+
+ output = (char*)pj_pool_alloc(pool, msg.slen + 512);
+ pj_memset(output, 0, msg.slen+512);
+ output_len = pj_xml_print(root, output, msg.slen+512, PJ_TRUE);
+ if (output_len < 1) {
+ PJ_LOG(1, (THIS_FILE, " Error: buffer too small to print XML file"));
+ return -20;
+ }
+ output[output_len] = '\0';
+
+
+ pj_pool_release(pool);
+ return 0;
+}
+
+int xml_test()
+{
+ unsigned i;
+ for (i=0; i<sizeof(xml_doc)/sizeof(xml_doc[0]); ++i) {
+ int status;
+ if ((status=xml_parse_print_test(xml_doc[i])) != 0)
+ return status;
+ }
+ return 0;
+}
+
+#else
+/* To prevent warning about "translation unit is empty"
+ * when this test is disabled.
+ */
+int dummy_xml_test;
+#endif /* INCLUDE_XML_TEST */
+
+
diff --git a/pjlib-util/src/pjlib-util/md5.c b/pjlib-util/src/pjlib-util/md5.c
index 80aed531..233a1da9 100644
--- a/pjlib-util/src/pjlib-util/md5.c
+++ b/pjlib-util/src/pjlib-util/md5.c
@@ -1,279 +1,279 @@
-/* $Id$ */
-/*
- * Copyright (C) 2003-2006 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 <pjlib-util/md5.h>
-#include <pj/string.h> /* pj_memcpy */
-/*
- * This code implements the MD5 message-digest algorithm.
- * The algorithm is due to Ron Rivest. This code was
- * written by Colin Plumb in 1993, no copyright is claimed.
- * This code is in the public domain; do with it what you wish.
- *
- * Equivalent code is available from RSA Data Security, Inc.
- * This code has been tested against that, and is equivalent,
- * except that you don't need to include two pages of legalese
- * with every copy.
- *
- * To compute the message digest of a chunk of bytes, declare an
- * MD5Context structure, pass it to MD5Init, call MD5Update as
- * needed on buffers full of bytes, and then call MD5Final, which
- * will fill a supplied 16-byte array with the digest.
- */
-
-#if defined(PJ_IS_BIG_ENDIAN) && PJ_IS_BIG_ENDIAN != 0
-#define HIGHFIRST 1
-#endif
-
-#ifndef HIGHFIRST
-#define byteReverse(buf, len) /* Nothing */
-#else
-void byteReverse(unsigned char *buf, unsigned longs);
-
-#ifndef ASM_MD5
-/*
- * Note: this code is harmless on little-endian machines.
- */
-void byteReverse(unsigned char *buf, unsigned longs)
-{
- pj_uint32_t t;
- do {
- t = (pj_uint32_t) ((unsigned) buf[3] << 8 | buf[2]) << 16 |
- ((unsigned) buf[1] << 8 | buf[0]);
- *(pj_uint32_t *) buf = t;
- buf += 4;
- } while (--longs);
-}
-#endif
-#endif
-
-static void MD5Transform(pj_uint32_t buf[4], pj_uint32_t const in[16]);
-
-
-/*
- * Start MD5 accumulation. Set bit count to 0 and buffer to mysterious
- * initialization constants.
- */
-PJ_DEF(void) pj_md5_init(pj_md5_context *ctx)
-{
- ctx->buf[0] = 0x67452301;
- ctx->buf[1] = 0xefcdab89;
- ctx->buf[2] = 0x98badcfe;
- ctx->buf[3] = 0x10325476;
-
- ctx->bits[0] = 0;
- ctx->bits[1] = 0;
-}
-
-/*
- * Update context to reflect the concatenation of another buffer full
- * of bytes.
- */
-PJ_DEF(void) pj_md5_update( pj_md5_context *ctx,
- unsigned char const *buf, unsigned len)
-{
- pj_uint32_t t;
-
- /* Update bitcount */
-
- t = ctx->bits[0];
- if ((ctx->bits[0] = t + ((pj_uint32_t) len << 3)) < t)
- ctx->bits[1]++; /* Carry from low to high */
- ctx->bits[1] += len >> 29;
-
- t = (t >> 3) & 0x3f; /* Bytes already in shsInfo->data */
-
- /* Handle any leading odd-sized chunks */
-
- if (t) {
- unsigned char *p = (unsigned char *) ctx->in + t;
-
- t = 64 - t;
- if (len < t) {
- pj_memcpy(p, buf, len);
- return;
- }
- pj_memcpy(p, buf, t);
- byteReverse(ctx->in, 16);
- MD5Transform(ctx->buf, (pj_uint32_t *) ctx->in);
- buf += t;
- len -= t;
- }
- /* Process data in 64-byte chunks */
-
- while (len >= 64) {
- pj_memcpy(ctx->in, buf, 64);
- byteReverse(ctx->in, 16);
- MD5Transform(ctx->buf, (pj_uint32_t *) ctx->in);
- buf += 64;
- len -= 64;
- }
-
- /* Handle any remaining bytes of data. */
-
- pj_memcpy(ctx->in, buf, len);
-}
-
-/*
- * Final wrapup - pad to 64-byte boundary with the bit pattern
- * 1 0* (64-bit count of bits processed, MSB-first)
- */
-PJ_DEF(void) pj_md5_final(pj_md5_context *ctx, unsigned char digest[16])
-{
- unsigned count;
- unsigned char *p;
-
- /* Compute number of bytes mod 64 */
- count = (ctx->bits[0] >> 3) & 0x3F;
-
- /* Set the first char of padding to 0x80. This is safe since there is
- always at least one byte free */
- p = ctx->in + count;
- *p++ = 0x80;
-
- /* Bytes of padding needed to make 64 bytes */
- count = 64 - 1 - count;
-
- /* Pad out to 56 mod 64 */
- if (count < 8) {
- /* Two lots of padding: Pad the first block to 64 bytes */
- pj_memset(p, 0, count);
- byteReverse(ctx->in, 16);
- MD5Transform(ctx->buf, (pj_uint32_t *) ctx->in);
-
- /* Now fill the next block with 56 bytes */
- pj_memset(ctx->in, 0, 56);
- } else {
- /* Pad block to 56 bytes */
- pj_memset(p, 0, count - 8);
- }
- byteReverse(ctx->in, 14);
-
- /* Append length in bits and transform */
- ((pj_uint32_t *) ctx->in)[14] = ctx->bits[0];
- ((pj_uint32_t *) ctx->in)[15] = ctx->bits[1];
-
- MD5Transform(ctx->buf, (pj_uint32_t *) ctx->in);
- byteReverse((unsigned char *) ctx->buf, 4);
- pj_memcpy(digest, ctx->buf, 16);
- pj_memset(ctx, 0, sizeof(ctx)); /* In case it's sensitive */
-}
-
-#ifndef ASM_MD5
-
-/* The four core functions - F1 is optimized somewhat */
-
-/* #define F1(x, y, z) (x & y | ~x & z) */
-#define F1(x, y, z) (z ^ (x & (y ^ z)))
-#define F2(x, y, z) F1(z, x, y)
-#define F3(x, y, z) (x ^ y ^ z)
-#define F4(x, y, z) (y ^ (x | ~z))
-
-/* This is the central step in the MD5 algorithm. */
-#define MD5STEP(f, w, x, y, z, data, s) \
- ( w += f(x, y, z) + data, w = w<<s | w>>(32-s), w += x )
-
-/*
- * The core of the MD5 algorithm, this alters an existing MD5 hash to
- * reflect the addition of 16 longwords of new data. MD5Update blocks
- * the data and converts bytes into longwords for this routine.
- */
-static void MD5Transform(pj_uint32_t buf[4], pj_uint32_t const in[16])
-{
- register pj_uint32_t a, b, c, d;
-
- a = buf[0];
- b = buf[1];
- c = buf[2];
- d = buf[3];
-
- MD5STEP(F1, a, b, c, d, in[0] + 0xd76aa478, 7);
- MD5STEP(F1, d, a, b, c, in[1] + 0xe8c7b756, 12);
- MD5STEP(F1, c, d, a, b, in[2] + 0x242070db, 17);
- MD5STEP(F1, b, c, d, a, in[3] + 0xc1bdceee, 22);
- MD5STEP(F1, a, b, c, d, in[4] + 0xf57c0faf, 7);
- MD5STEP(F1, d, a, b, c, in[5] + 0x4787c62a, 12);
- MD5STEP(F1, c, d, a, b, in[6] + 0xa8304613, 17);
- MD5STEP(F1, b, c, d, a, in[7] + 0xfd469501, 22);
- MD5STEP(F1, a, b, c, d, in[8] + 0x698098d8, 7);
- MD5STEP(F1, d, a, b, c, in[9] + 0x8b44f7af, 12);
- MD5STEP(F1, c, d, a, b, in[10] + 0xffff5bb1, 17);
- MD5STEP(F1, b, c, d, a, in[11] + 0x895cd7be, 22);
- MD5STEP(F1, a, b, c, d, in[12] + 0x6b901122, 7);
- MD5STEP(F1, d, a, b, c, in[13] + 0xfd987193, 12);
- MD5STEP(F1, c, d, a, b, in[14] + 0xa679438e, 17);
- MD5STEP(F1, b, c, d, a, in[15] + 0x49b40821, 22);
-
- MD5STEP(F2, a, b, c, d, in[1] + 0xf61e2562, 5);
- MD5STEP(F2, d, a, b, c, in[6] + 0xc040b340, 9);
- MD5STEP(F2, c, d, a, b, in[11] + 0x265e5a51, 14);
- MD5STEP(F2, b, c, d, a, in[0] + 0xe9b6c7aa, 20);
- MD5STEP(F2, a, b, c, d, in[5] + 0xd62f105d, 5);
- MD5STEP(F2, d, a, b, c, in[10] + 0x02441453, 9);
- MD5STEP(F2, c, d, a, b, in[15] + 0xd8a1e681, 14);
- MD5STEP(F2, b, c, d, a, in[4] + 0xe7d3fbc8, 20);
- MD5STEP(F2, a, b, c, d, in[9] + 0x21e1cde6, 5);
- MD5STEP(F2, d, a, b, c, in[14] + 0xc33707d6, 9);
- MD5STEP(F2, c, d, a, b, in[3] + 0xf4d50d87, 14);
- MD5STEP(F2, b, c, d, a, in[8] + 0x455a14ed, 20);
- MD5STEP(F2, a, b, c, d, in[13] + 0xa9e3e905, 5);
- MD5STEP(F2, d, a, b, c, in[2] + 0xfcefa3f8, 9);
- MD5STEP(F2, c, d, a, b, in[7] + 0x676f02d9, 14);
- MD5STEP(F2, b, c, d, a, in[12] + 0x8d2a4c8a, 20);
-
- MD5STEP(F3, a, b, c, d, in[5] + 0xfffa3942, 4);
- MD5STEP(F3, d, a, b, c, in[8] + 0x8771f681, 11);
- MD5STEP(F3, c, d, a, b, in[11] + 0x6d9d6122, 16);
- MD5STEP(F3, b, c, d, a, in[14] + 0xfde5380c, 23);
- MD5STEP(F3, a, b, c, d, in[1] + 0xa4beea44, 4);
- MD5STEP(F3, d, a, b, c, in[4] + 0x4bdecfa9, 11);
- MD5STEP(F3, c, d, a, b, in[7] + 0xf6bb4b60, 16);
- MD5STEP(F3, b, c, d, a, in[10] + 0xbebfbc70, 23);
- MD5STEP(F3, a, b, c, d, in[13] + 0x289b7ec6, 4);
- MD5STEP(F3, d, a, b, c, in[0] + 0xeaa127fa, 11);
- MD5STEP(F3, c, d, a, b, in[3] + 0xd4ef3085, 16);
- MD5STEP(F3, b, c, d, a, in[6] + 0x04881d05, 23);
- MD5STEP(F3, a, b, c, d, in[9] + 0xd9d4d039, 4);
- MD5STEP(F3, d, a, b, c, in[12] + 0xe6db99e5, 11);
- MD5STEP(F3, c, d, a, b, in[15] + 0x1fa27cf8, 16);
- MD5STEP(F3, b, c, d, a, in[2] + 0xc4ac5665, 23);
-
- MD5STEP(F4, a, b, c, d, in[0] + 0xf4292244, 6);
- MD5STEP(F4, d, a, b, c, in[7] + 0x432aff97, 10);
- MD5STEP(F4, c, d, a, b, in[14] + 0xab9423a7, 15);
- MD5STEP(F4, b, c, d, a, in[5] + 0xfc93a039, 21);
- MD5STEP(F4, a, b, c, d, in[12] + 0x655b59c3, 6);
- MD5STEP(F4, d, a, b, c, in[3] + 0x8f0ccc92, 10);
- MD5STEP(F4, c, d, a, b, in[10] + 0xffeff47d, 15);
- MD5STEP(F4, b, c, d, a, in[1] + 0x85845dd1, 21);
- MD5STEP(F4, a, b, c, d, in[8] + 0x6fa87e4f, 6);
- MD5STEP(F4, d, a, b, c, in[15] + 0xfe2ce6e0, 10);
- MD5STEP(F4, c, d, a, b, in[6] + 0xa3014314, 15);
- MD5STEP(F4, b, c, d, a, in[13] + 0x4e0811a1, 21);
- MD5STEP(F4, a, b, c, d, in[4] + 0xf7537e82, 6);
- MD5STEP(F4, d, a, b, c, in[11] + 0xbd3af235, 10);
- MD5STEP(F4, c, d, a, b, in[2] + 0x2ad7d2bb, 15);
- MD5STEP(F4, b, c, d, a, in[9] + 0xeb86d391, 21);
-
- buf[0] += a;
- buf[1] += b;
- buf[2] += c;
- buf[3] += d;
-}
-
-#endif
-
+/* $Id$ */
+/*
+ * Copyright (C) 2003-2006 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 <pjlib-util/md5.h>
+#include <pj/string.h> /* pj_memcpy */
+/*
+ * This code implements the MD5 message-digest algorithm.
+ * The algorithm is due to Ron Rivest. This code was
+ * written by Colin Plumb in 1993, no copyright is claimed.
+ * This code is in the public domain; do with it what you wish.
+ *
+ * Equivalent code is available from RSA Data Security, Inc.
+ * This code has been tested against that, and is equivalent,
+ * except that you don't need to include two pages of legalese
+ * with every copy.
+ *
+ * To compute the message digest of a chunk of bytes, declare an
+ * MD5Context structure, pass it to MD5Init, call MD5Update as
+ * needed on buffers full of bytes, and then call MD5Final, which
+ * will fill a supplied 16-byte array with the digest.
+ */
+
+#if defined(PJ_IS_BIG_ENDIAN) && PJ_IS_BIG_ENDIAN != 0
+#define HIGHFIRST 1
+#endif
+
+#ifndef HIGHFIRST
+#define byteReverse(buf, len) /* Nothing */
+#else
+void byteReverse(unsigned char *buf, unsigned longs);
+
+#ifndef ASM_MD5
+/*
+ * Note: this code is harmless on little-endian machines.
+ */
+void byteReverse(unsigned char *buf, unsigned longs)
+{
+ pj_uint32_t t;
+ do {
+ t = (pj_uint32_t) ((unsigned) buf[3] << 8 | buf[2]) << 16 |
+ ((unsigned) buf[1] << 8 | buf[0]);
+ *(pj_uint32_t *) buf = t;
+ buf += 4;
+ } while (--longs);
+}
+#endif
+#endif
+
+static void MD5Transform(pj_uint32_t buf[4], pj_uint32_t const in[16]);
+
+
+/*
+ * Start MD5 accumulation. Set bit count to 0 and buffer to mysterious
+ * initialization constants.
+ */
+PJ_DEF(void) pj_md5_init(pj_md5_context *ctx)
+{
+ ctx->buf[0] = 0x67452301;
+ ctx->buf[1] = 0xefcdab89;
+ ctx->buf[2] = 0x98badcfe;
+ ctx->buf[3] = 0x10325476;
+
+ ctx->bits[0] = 0;
+ ctx->bits[1] = 0;
+}
+
+/*
+ * Update context to reflect the concatenation of another buffer full
+ * of bytes.
+ */
+PJ_DEF(void) pj_md5_update( pj_md5_context *ctx,
+ unsigned char const *buf, unsigned len)
+{
+ pj_uint32_t t;
+
+ /* Update bitcount */
+
+ t = ctx->bits[0];
+ if ((ctx->bits[0] = t + ((pj_uint32_t) len << 3)) < t)
+ ctx->bits[1]++; /* Carry from low to high */
+ ctx->bits[1] += len >> 29;
+
+ t = (t >> 3) & 0x3f; /* Bytes already in shsInfo->data */
+
+ /* Handle any leading odd-sized chunks */
+
+ if (t) {
+ unsigned char *p = (unsigned char *) ctx->in + t;
+
+ t = 64 - t;
+ if (len < t) {
+ pj_memcpy(p, buf, len);
+ return;
+ }
+ pj_memcpy(p, buf, t);
+ byteReverse(ctx->in, 16);
+ MD5Transform(ctx->buf, (pj_uint32_t *) ctx->in);
+ buf += t;
+ len -= t;
+ }
+ /* Process data in 64-byte chunks */
+
+ while (len >= 64) {
+ pj_memcpy(ctx->in, buf, 64);
+ byteReverse(ctx->in, 16);
+ MD5Transform(ctx->buf, (pj_uint32_t *) ctx->in);
+ buf += 64;
+ len -= 64;
+ }
+
+ /* Handle any remaining bytes of data. */
+
+ pj_memcpy(ctx->in, buf, len);
+}
+
+/*
+ * Final wrapup - pad to 64-byte boundary with the bit pattern
+ * 1 0* (64-bit count of bits processed, MSB-first)
+ */
+PJ_DEF(void) pj_md5_final(pj_md5_context *ctx, unsigned char digest[16])
+{
+ unsigned count;
+ unsigned char *p;
+
+ /* Compute number of bytes mod 64 */
+ count = (ctx->bits[0] >> 3) & 0x3F;
+
+ /* Set the first char of padding to 0x80. This is safe since there is
+ always at least one byte free */
+ p = ctx->in + count;
+ *p++ = 0x80;
+
+ /* Bytes of padding needed to make 64 bytes */
+ count = 64 - 1 - count;
+
+ /* Pad out to 56 mod 64 */
+ if (count < 8) {
+ /* Two lots of padding: Pad the first block to 64 bytes */
+ pj_memset(p, 0, count);
+ byteReverse(ctx->in, 16);
+ MD5Transform(ctx->buf, (pj_uint32_t *) ctx->in);
+
+ /* Now fill the next block with 56 bytes */
+ pj_memset(ctx->in, 0, 56);
+ } else {
+ /* Pad block to 56 bytes */
+ pj_memset(p, 0, count - 8);
+ }
+ byteReverse(ctx->in, 14);
+
+ /* Append length in bits and transform */
+ ((pj_uint32_t *) ctx->in)[14] = ctx->bits[0];
+ ((pj_uint32_t *) ctx->in)[15] = ctx->bits[1];
+
+ MD5Transform(ctx->buf, (pj_uint32_t *) ctx->in);
+ byteReverse((unsigned char *) ctx->buf, 4);
+ pj_memcpy(digest, ctx->buf, 16);
+ pj_memset(ctx, 0, sizeof(ctx)); /* In case it's sensitive */
+}
+
+#ifndef ASM_MD5
+
+/* The four core functions - F1 is optimized somewhat */
+
+/* #define F1(x, y, z) (x & y | ~x & z) */
+#define F1(x, y, z) (z ^ (x & (y ^ z)))
+#define F2(x, y, z) F1(z, x, y)
+#define F3(x, y, z) (x ^ y ^ z)
+#define F4(x, y, z) (y ^ (x | ~z))
+
+/* This is the central step in the MD5 algorithm. */
+#define MD5STEP(f, w, x, y, z, data, s) \
+ ( w += f(x, y, z) + data, w = w<<s | w>>(32-s), w += x )
+
+/*
+ * The core of the MD5 algorithm, this alters an existing MD5 hash to
+ * reflect the addition of 16 longwords of new data. MD5Update blocks
+ * the data and converts bytes into longwords for this routine.
+ */
+static void MD5Transform(pj_uint32_t buf[4], pj_uint32_t const in[16])
+{
+ register pj_uint32_t a, b, c, d;
+
+ a = buf[0];
+ b = buf[1];
+ c = buf[2];
+ d = buf[3];
+
+ MD5STEP(F1, a, b, c, d, in[0] + 0xd76aa478, 7);
+ MD5STEP(F1, d, a, b, c, in[1] + 0xe8c7b756, 12);
+ MD5STEP(F1, c, d, a, b, in[2] + 0x242070db, 17);
+ MD5STEP(F1, b, c, d, a, in[3] + 0xc1bdceee, 22);
+ MD5STEP(F1, a, b, c, d, in[4] + 0xf57c0faf, 7);
+ MD5STEP(F1, d, a, b, c, in[5] + 0x4787c62a, 12);
+ MD5STEP(F1, c, d, a, b, in[6] + 0xa8304613, 17);
+ MD5STEP(F1, b, c, d, a, in[7] + 0xfd469501, 22);
+ MD5STEP(F1, a, b, c, d, in[8] + 0x698098d8, 7);
+ MD5STEP(F1, d, a, b, c, in[9] + 0x8b44f7af, 12);
+ MD5STEP(F1, c, d, a, b, in[10] + 0xffff5bb1, 17);
+ MD5STEP(F1, b, c, d, a, in[11] + 0x895cd7be, 22);
+ MD5STEP(F1, a, b, c, d, in[12] + 0x6b901122, 7);
+ MD5STEP(F1, d, a, b, c, in[13] + 0xfd987193, 12);
+ MD5STEP(F1, c, d, a, b, in[14] + 0xa679438e, 17);
+ MD5STEP(F1, b, c, d, a, in[15] + 0x49b40821, 22);
+
+ MD5STEP(F2, a, b, c, d, in[1] + 0xf61e2562, 5);
+ MD5STEP(F2, d, a, b, c, in[6] + 0xc040b340, 9);
+ MD5STEP(F2, c, d, a, b, in[11] + 0x265e5a51, 14);
+ MD5STEP(F2, b, c, d, a, in[0] + 0xe9b6c7aa, 20);
+ MD5STEP(F2, a, b, c, d, in[5] + 0xd62f105d, 5);
+ MD5STEP(F2, d, a, b, c, in[10] + 0x02441453, 9);
+ MD5STEP(F2, c, d, a, b, in[15] + 0xd8a1e681, 14);
+ MD5STEP(F2, b, c, d, a, in[4] + 0xe7d3fbc8, 20);
+ MD5STEP(F2, a, b, c, d, in[9] + 0x21e1cde6, 5);
+ MD5STEP(F2, d, a, b, c, in[14] + 0xc33707d6, 9);
+ MD5STEP(F2, c, d, a, b, in[3] + 0xf4d50d87, 14);
+ MD5STEP(F2, b, c, d, a, in[8] + 0x455a14ed, 20);
+ MD5STEP(F2, a, b, c, d, in[13] + 0xa9e3e905, 5);
+ MD5STEP(F2, d, a, b, c, in[2] + 0xfcefa3f8, 9);
+ MD5STEP(F2, c, d, a, b, in[7] + 0x676f02d9, 14);
+ MD5STEP(F2, b, c, d, a, in[12] + 0x8d2a4c8a, 20);
+
+ MD5STEP(F3, a, b, c, d, in[5] + 0xfffa3942, 4);
+ MD5STEP(F3, d, a, b, c, in[8] + 0x8771f681, 11);
+ MD5STEP(F3, c, d, a, b, in[11] + 0x6d9d6122, 16);
+ MD5STEP(F3, b, c, d, a, in[14] + 0xfde5380c, 23);
+ MD5STEP(F3, a, b, c, d, in[1] + 0xa4beea44, 4);
+ MD5STEP(F3, d, a, b, c, in[4] + 0x4bdecfa9, 11);
+ MD5STEP(F3, c, d, a, b, in[7] + 0xf6bb4b60, 16);
+ MD5STEP(F3, b, c, d, a, in[10] + 0xbebfbc70, 23);
+ MD5STEP(F3, a, b, c, d, in[13] + 0x289b7ec6, 4);
+ MD5STEP(F3, d, a, b, c, in[0] + 0xeaa127fa, 11);
+ MD5STEP(F3, c, d, a, b, in[3] + 0xd4ef3085, 16);
+ MD5STEP(F3, b, c, d, a, in[6] + 0x04881d05, 23);
+ MD5STEP(F3, a, b, c, d, in[9] + 0xd9d4d039, 4);
+ MD5STEP(F3, d, a, b, c, in[12] + 0xe6db99e5, 11);
+ MD5STEP(F3, c, d, a, b, in[15] + 0x1fa27cf8, 16);
+ MD5STEP(F3, b, c, d, a, in[2] + 0xc4ac5665, 23);
+
+ MD5STEP(F4, a, b, c, d, in[0] + 0xf4292244, 6);
+ MD5STEP(F4, d, a, b, c, in[7] + 0x432aff97, 10);
+ MD5STEP(F4, c, d, a, b, in[14] + 0xab9423a7, 15);
+ MD5STEP(F4, b, c, d, a, in[5] + 0xfc93a039, 21);
+ MD5STEP(F4, a, b, c, d, in[12] + 0x655b59c3, 6);
+ MD5STEP(F4, d, a, b, c, in[3] + 0x8f0ccc92, 10);
+ MD5STEP(F4, c, d, a, b, in[10] + 0xffeff47d, 15);
+ MD5STEP(F4, b, c, d, a, in[1] + 0x85845dd1, 21);
+ MD5STEP(F4, a, b, c, d, in[8] + 0x6fa87e4f, 6);
+ MD5STEP(F4, d, a, b, c, in[15] + 0xfe2ce6e0, 10);
+ MD5STEP(F4, c, d, a, b, in[6] + 0xa3014314, 15);
+ MD5STEP(F4, b, c, d, a, in[13] + 0x4e0811a1, 21);
+ MD5STEP(F4, a, b, c, d, in[4] + 0xf7537e82, 6);
+ MD5STEP(F4, d, a, b, c, in[11] + 0xbd3af235, 10);
+ MD5STEP(F4, c, d, a, b, in[2] + 0x2ad7d2bb, 15);
+ MD5STEP(F4, b, c, d, a, in[9] + 0xeb86d391, 21);
+
+ buf[0] += a;
+ buf[1] += b;
+ buf[2] += c;
+ buf[3] += d;
+}
+
+#endif
+
diff --git a/pjlib-util/src/pjlib-util/scanner.c b/pjlib-util/src/pjlib-util/scanner.c
index 27a1d4d0..3a19e9fd 100644
--- a/pjlib-util/src/pjlib-util/scanner.c
+++ b/pjlib-util/src/pjlib-util/scanner.c
@@ -1,595 +1,595 @@
-/* $Id$ */
-/*
- * Copyright (C) 2003-2006 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 <pjlib-util/scanner.h>
-#include <pj/string.h>
-#include <pj/except.h>
-#include <pj/os.h>
-#include <pj/errno.h>
-
-#define PJ_SCAN_IS_SPACE(c) ((c)==' ' || (c)=='\t')
-#define PJ_SCAN_IS_NEWLINE(c) ((c)=='\r' || (c)=='\n')
-#define PJ_SCAN_CHECK_EOF(s) (s != end)
-
-
-static void pj_scan_syntax_err(pj_scanner *scanner)
-{
- (*scanner->callback)(scanner);
-}
-
-PJ_DEF(void) pj_cis_buf_init( pj_cis_buf_t *cis_buf)
-{
- pj_memset(cis_buf->cis_buf, 0, sizeof(cis_buf->cis_buf));
- cis_buf->use_mask = 0;
-}
-
-PJ_DEF(pj_status_t) pj_cis_init(pj_cis_buf_t *cis_buf, pj_cis_t *cis)
-{
- unsigned i;
-
- cis->cis_buf = cis_buf->cis_buf;
-
- for (i=0; i<PJ_CIS_MAX_INDEX; ++i) {
- if ((cis_buf->use_mask & (1 << i)) == 0) {
- cis->cis_id = i;
- cis_buf->use_mask |= (1 << i);
- return PJ_SUCCESS;
- }
- }
-
- cis->cis_id = PJ_CIS_MAX_INDEX;
- return PJ_ETOOMANY;
-}
-
-PJ_DEF(pj_status_t) pj_cis_dup( pj_cis_t *new_cis, pj_cis_t *existing)
-{
- pj_status_t status;
- unsigned i;
-
- /* Warning: typecasting here! */
- status = pj_cis_init((pj_cis_buf_t*)existing->cis_buf, new_cis);
- if (status != PJ_SUCCESS)
- return status;
-
- for (i=0; i<256; ++i) {
- if (PJ_CIS_ISSET(existing, i))
- PJ_CIS_SET(new_cis, i);
- else
- PJ_CIS_CLR(new_cis, i);
- }
-
- return PJ_SUCCESS;
-}
-
-PJ_DEF(void) pj_cis_add_range(pj_cis_t *cis, int cstart, int cend)
-{
- while (cstart != cend) {
- PJ_CIS_SET(cis, cstart);
- ++cstart;
- }
-}
-
-PJ_DEF(void) pj_cis_add_alpha(pj_cis_t *cis)
-{
- pj_cis_add_range( cis, 'a', 'z'+1);
- pj_cis_add_range( cis, 'A', 'Z'+1);
-}
-
-PJ_DEF(void) pj_cis_add_num(pj_cis_t *cis)
-{
- pj_cis_add_range( cis, '0', '9'+1);
-}
-
-PJ_DEF(void) pj_cis_add_str( pj_cis_t *cis, const char *str)
-{
- while (*str) {
- PJ_CIS_SET(cis, *str);
- ++str;
- }
-}
-
-PJ_DEF(void) pj_cis_del_range( pj_cis_t *cis, int cstart, int cend)
-{
- while (cstart != cend) {
- PJ_CIS_CLR(cis, cstart);
- cstart++;
- }
-}
-
-PJ_DEF(void) pj_cis_del_str( pj_cis_t *cis, const char *str)
-{
- while (*str) {
- PJ_CIS_CLR(cis, *str);
- ++str;
- }
-}
-
-PJ_DEF(void) pj_cis_invert( pj_cis_t *cis )
-{
- unsigned i;
- for (i=0; i<256; ++i) {
- if (PJ_CIS_ISSET(cis,i))
- PJ_CIS_CLR(cis,i);
- else
- PJ_CIS_SET(cis,i);
- }
-}
-
-PJ_DEF(void) pj_scan_init( pj_scanner *scanner, char *bufstart, int buflen,
- unsigned options, pj_syn_err_func_ptr callback )
-{
- PJ_CHECK_STACK();
-
- scanner->begin = scanner->curptr = bufstart;
- scanner->end = bufstart + buflen;
- scanner->line = 1;
- scanner->col = 1;
- scanner->callback = callback;
- scanner->skip_ws = options;
-
- if (scanner->skip_ws)
- pj_scan_skip_whitespace(scanner);
-
- scanner->col = scanner->curptr - scanner->begin + 1;
-}
-
-
-PJ_DEF(void) pj_scan_fini( pj_scanner *scanner )
-{
- PJ_CHECK_STACK();
- PJ_UNUSED_ARG(scanner);
-}
-
-PJ_DEF(void) pj_scan_skip_whitespace( pj_scanner *scanner )
-{
- register char *s = scanner->curptr;
-
- PJ_CHECK_STACK();
-
- while (PJ_SCAN_IS_SPACE(*s)) {
- ++s;
- }
-
- if ((scanner->skip_ws & PJ_SCAN_AUTOSKIP_NEWLINE) && PJ_SCAN_IS_NEWLINE(*s)) {
- for (;;) {
- if (*s == '\r') {
- ++s;
- if (*s == '\n') ++s;
- ++scanner->line;
- scanner->col = 1;
- scanner->curptr = s;
- } else if (*s == '\n') {
- ++s;
- ++scanner->line;
- scanner->col = 1;
- scanner->curptr = s;
- } else if (PJ_SCAN_IS_SPACE(*s)) {
- do {
- ++s;
- } while (PJ_SCAN_IS_SPACE(*s));
- } else {
- break;
- }
- }
- }
-
- if (PJ_SCAN_IS_NEWLINE(*s) && (scanner->skip_ws & PJ_SCAN_AUTOSKIP_WS_HEADER)==PJ_SCAN_AUTOSKIP_WS_HEADER) {
- /* Check for header continuation. */
- scanner->col += s - scanner->curptr;
- scanner->curptr = s;
-
- if (*s == '\r') {
- ++s;
- }
- if (*s == '\n') {
- ++s;
- }
- if (PJ_SCAN_IS_SPACE(*s)) {
- register char *t = s;
- do {
- ++t;
- } while (PJ_SCAN_IS_SPACE(*t));
-
- ++scanner->line;
- scanner->col = t-s;
- scanner->curptr = t;
- }
- } else {
- scanner->col += s - scanner->curptr;
- scanner->curptr = s;
- }
-}
-
-PJ_DEF(int) pj_scan_peek( pj_scanner *scanner,
- const pj_cis_t *spec, pj_str_t *out)
-{
- register char *s = scanner->curptr;
- register char *end = scanner->end;
-
- PJ_CHECK_STACK();
-
- if (pj_scan_is_eof(scanner)) {
- pj_scan_syntax_err(scanner);
- return -1;
- }
-
- while (PJ_SCAN_CHECK_EOF(s) && pj_cis_match(spec, *s))
- ++s;
-
- pj_strset3(out, scanner->curptr, s);
- return s < scanner->end ? *s : 0;
-}
-
-
-PJ_DEF(int) pj_scan_peek_n( pj_scanner *scanner,
- pj_size_t len, pj_str_t *out)
-{
- char *endpos = scanner->curptr + len;
-
- PJ_CHECK_STACK();
-
- if (endpos > scanner->end) {
- pj_scan_syntax_err(scanner);
- return -1;
- }
-
- pj_strset(out, scanner->curptr, len);
- return *endpos;
-}
-
-
-PJ_DEF(int) pj_scan_peek_until( pj_scanner *scanner,
- const pj_cis_t *spec,
- pj_str_t *out)
-{
- register char *s = scanner->curptr;
- register char *end = scanner->end;
-
- PJ_CHECK_STACK();
-
- if (pj_scan_is_eof(scanner)) {
- pj_scan_syntax_err(scanner);
- return -1;
- }
-
- while (PJ_SCAN_CHECK_EOF(s) && !pj_cis_match( spec, *s))
- ++s;
-
- pj_strset3(out, scanner->curptr, s);
- return s!=scanner->end ? *s : 0;
-}
-
-
-PJ_DEF(void) pj_scan_get( pj_scanner *scanner,
- const pj_cis_t *spec, pj_str_t *out)
-{
- register char *s = scanner->curptr;
- register char *end = scanner->end;
- char *start = s;
-
- PJ_CHECK_STACK();
-
- if (pj_scan_is_eof(scanner) || !pj_cis_match(spec, *s)) {
- pj_scan_syntax_err(scanner);
- return;
- }
-
- do {
- ++s;
- } while (PJ_SCAN_CHECK_EOF(s) && pj_cis_match(spec, *s));
-
- pj_strset3(out, scanner->curptr, s);
-
- scanner->col += (s - start);
- scanner->curptr = s;
-
- if (scanner->skip_ws) {
- pj_scan_skip_whitespace(scanner);
- }
-}
-
-
-PJ_DEF(void) pj_scan_get_quote( pj_scanner *scanner,
- int begin_quote, int end_quote,
- pj_str_t *out)
-{
- register char *s = scanner->curptr;
- register char *end = scanner->end;
- char *start = s;
-
- PJ_CHECK_STACK();
-
- /* Check and eat the begin_quote. */
- if (*s != begin_quote) {
- pj_scan_syntax_err(scanner);
- return;
- }
- ++s;
-
- /* Loop until end_quote is found.
- */
- do {
- /* loop until end_quote is found. */
- do {
- ++s;
- } while (s != end && *s != '\n' && *s != end_quote);
-
- /* check that no backslash character precedes the end_quote. */
- if (*s == end_quote) {
- if (*(s-1) == '\\') {
- if (s-2 == scanner->begin) {
- break;
- } else {
- char *q = s-2;
- char *r = s-2;
-
- while (r != scanner->begin && *r == '\\') {
- --r;
- }
- /* break from main loop if we have odd number of backslashes */
- if (((unsigned)(q-r) & 0x01) == 1) {
- break;
- }
- }
- } else {
- /* end_quote is not preceeded by backslash. break now. */
- break;
- }
- } else {
- /* loop ended by non-end_quote character. break now. */
- break;
- }
- } while (1);
-
- /* Check and eat the end quote. */
- if (*s != end_quote) {
- pj_scan_syntax_err(scanner);
- return;
- }
- ++s;
-
- pj_strset3(out, scanner->curptr, s);
-
- scanner->col += (s - start);
- scanner->curptr = s;
-
- if (scanner->skip_ws) {
- pj_scan_skip_whitespace(scanner);
- }
-}
-
-PJ_DEF(void) pj_scan_get_n( pj_scanner *scanner,
- unsigned N, pj_str_t *out)
-{
- register char *s = scanner->curptr;
- char *start = scanner->curptr;
-
- PJ_CHECK_STACK();
-
- if (scanner->curptr + N > scanner->end) {
- pj_scan_syntax_err(scanner);
- return;
- }
-
- pj_strset(out, s, N);
-
- s += N;
- scanner->col += (s - start);
- scanner->curptr = s;
-
- if (scanner->skip_ws) {
- pj_scan_skip_whitespace(scanner);
- }
-}
-
-
-PJ_DEF(int) pj_scan_get_char( pj_scanner *scanner )
-{
- char *start = scanner->curptr;
- int chr = *start;
-
- PJ_CHECK_STACK();
-
- if (pj_scan_is_eof(scanner)) {
- pj_scan_syntax_err(scanner);
- return 0;
- }
-
- ++scanner->curptr;
- scanner->col += (scanner->curptr - start);
-
- if (scanner->skip_ws) {
- pj_scan_skip_whitespace(scanner);
- }
- return chr;
-}
-
-
-PJ_DEF(void) pj_scan_get_newline( pj_scanner *scanner )
-{
- PJ_CHECK_STACK();
-
- if (!PJ_SCAN_IS_NEWLINE(*scanner->curptr)) {
- pj_scan_syntax_err(scanner);
- return;
- }
-
- if (*scanner->curptr == '\r') {
- ++scanner->curptr;
- }
- if (*scanner->curptr == '\n') {
- ++scanner->curptr;
- }
-
- ++scanner->line;
- scanner->col = 1;
-
- if (scanner->skip_ws) {
- pj_scan_skip_whitespace(scanner);
- }
-}
-
-
-PJ_DEF(void) pj_scan_get_until( pj_scanner *scanner,
- const pj_cis_t *spec, pj_str_t *out)
-{
- register char *s = scanner->curptr;
- register char *end = scanner->end;
- char *start = s;
-
- PJ_CHECK_STACK();
-
- if (pj_scan_is_eof(scanner)) {
- pj_scan_syntax_err(scanner);
- return;
- }
-
- while (PJ_SCAN_CHECK_EOF(s) && !pj_cis_match(spec, *s)) {
- ++s;
- }
-
- pj_strset3(out, scanner->curptr, s);
-
- scanner->col += (s - start);
- scanner->curptr = s;
-
- if (scanner->skip_ws) {
- pj_scan_skip_whitespace(scanner);
- }
-}
-
-
-PJ_DEF(void) pj_scan_get_until_ch( pj_scanner *scanner,
- int until_char, pj_str_t *out)
-{
- register char *s = scanner->curptr;
- register char *end = scanner->end;
- char *start = s;
-
- PJ_CHECK_STACK();
-
- if (pj_scan_is_eof(scanner)) {
- pj_scan_syntax_err(scanner);
- return;
- }
-
- while (PJ_SCAN_CHECK_EOF(s) && *s != until_char) {
- ++s;
- }
-
- pj_strset3(out, scanner->curptr, s);
-
- scanner->col += (s - start);
- scanner->curptr = s;
-
- if (scanner->skip_ws) {
- pj_scan_skip_whitespace(scanner);
- }
-}
-
-
-PJ_DEF(void) pj_scan_get_until_chr( pj_scanner *scanner,
- const char *until_spec, pj_str_t *out)
-{
- register char *s = scanner->curptr;
- register char *end = scanner->end;
- char *start = scanner->curptr;
-
- PJ_CHECK_STACK();
-
- if (pj_scan_is_eof(scanner)) {
- pj_scan_syntax_err(scanner);
- return;
- }
-
- while (PJ_SCAN_CHECK_EOF(s) && !strchr(until_spec, *s)) {
- ++s;
- }
-
- pj_strset3(out, scanner->curptr, s);
-
- scanner->col += (s - start);
- scanner->curptr = s;
-
- if (scanner->skip_ws) {
- pj_scan_skip_whitespace(scanner);
- }
-}
-
-PJ_DEF(void) pj_scan_advance_n( pj_scanner *scanner,
- unsigned N, pj_bool_t skip_ws)
-{
- char *start = scanner->curptr;
-
- PJ_CHECK_STACK();
-
- if (scanner->curptr + N > scanner->end) {
- pj_scan_syntax_err(scanner);
- return;
- }
-
- scanner->curptr += N;
- scanner->col += (scanner->curptr - start);
-
- if (skip_ws) {
- pj_scan_skip_whitespace(scanner);
- }
-}
-
-
-PJ_DEF(int) pj_scan_strcmp( pj_scanner *scanner, const char *s, int len)
-{
- if (scanner->curptr + len > scanner->end) {
- pj_scan_syntax_err(scanner);
- return -1;
- }
- return strncmp(scanner->curptr, s, len);
-}
-
-
-PJ_DEF(int) pj_scan_stricmp( pj_scanner *scanner, const char *s, int len)
-{
- if (scanner->curptr + len > scanner->end) {
- pj_scan_syntax_err(scanner);
- return -1;
- }
- return strnicmp(scanner->curptr, s, len);
-}
-
-
-PJ_DEF(void) pj_scan_save_state( pj_scanner *scanner, pj_scan_state *state)
-{
- PJ_CHECK_STACK();
-
- state->curptr = scanner->curptr;
- state->line = scanner->line;
- state->col = scanner->col;
-}
-
-
-PJ_DEF(void) pj_scan_restore_state( pj_scanner *scanner,
- pj_scan_state *state)
-{
- PJ_CHECK_STACK();
-
- scanner->curptr = state->curptr;
- scanner->line = state->line;
- scanner->col = state->col;
-}
-
-
+/* $Id$ */
+/*
+ * Copyright (C) 2003-2006 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 <pjlib-util/scanner.h>
+#include <pj/string.h>
+#include <pj/except.h>
+#include <pj/os.h>
+#include <pj/errno.h>
+
+#define PJ_SCAN_IS_SPACE(c) ((c)==' ' || (c)=='\t')
+#define PJ_SCAN_IS_NEWLINE(c) ((c)=='\r' || (c)=='\n')
+#define PJ_SCAN_CHECK_EOF(s) (s != end)
+
+
+static void pj_scan_syntax_err(pj_scanner *scanner)
+{
+ (*scanner->callback)(scanner);
+}
+
+PJ_DEF(void) pj_cis_buf_init( pj_cis_buf_t *cis_buf)
+{
+ pj_memset(cis_buf->cis_buf, 0, sizeof(cis_buf->cis_buf));
+ cis_buf->use_mask = 0;
+}
+
+PJ_DEF(pj_status_t) pj_cis_init(pj_cis_buf_t *cis_buf, pj_cis_t *cis)
+{
+ unsigned i;
+
+ cis->cis_buf = cis_buf->cis_buf;
+
+ for (i=0; i<PJ_CIS_MAX_INDEX; ++i) {
+ if ((cis_buf->use_mask & (1 << i)) == 0) {
+ cis->cis_id = i;
+ cis_buf->use_mask |= (1 << i);
+ return PJ_SUCCESS;
+ }
+ }
+
+ cis->cis_id = PJ_CIS_MAX_INDEX;
+ return PJ_ETOOMANY;
+}
+
+PJ_DEF(pj_status_t) pj_cis_dup( pj_cis_t *new_cis, pj_cis_t *existing)
+{
+ pj_status_t status;
+ unsigned i;
+
+ /* Warning: typecasting here! */
+ status = pj_cis_init((pj_cis_buf_t*)existing->cis_buf, new_cis);
+ if (status != PJ_SUCCESS)
+ return status;
+
+ for (i=0; i<256; ++i) {
+ if (PJ_CIS_ISSET(existing, i))
+ PJ_CIS_SET(new_cis, i);
+ else
+ PJ_CIS_CLR(new_cis, i);
+ }
+
+ return PJ_SUCCESS;
+}
+
+PJ_DEF(void) pj_cis_add_range(pj_cis_t *cis, int cstart, int cend)
+{
+ while (cstart != cend) {
+ PJ_CIS_SET(cis, cstart);
+ ++cstart;
+ }
+}
+
+PJ_DEF(void) pj_cis_add_alpha(pj_cis_t *cis)
+{
+ pj_cis_add_range( cis, 'a', 'z'+1);
+ pj_cis_add_range( cis, 'A', 'Z'+1);
+}
+
+PJ_DEF(void) pj_cis_add_num(pj_cis_t *cis)
+{
+ pj_cis_add_range( cis, '0', '9'+1);
+}
+
+PJ_DEF(void) pj_cis_add_str( pj_cis_t *cis, const char *str)
+{
+ while (*str) {
+ PJ_CIS_SET(cis, *str);
+ ++str;
+ }
+}
+
+PJ_DEF(void) pj_cis_del_range( pj_cis_t *cis, int cstart, int cend)
+{
+ while (cstart != cend) {
+ PJ_CIS_CLR(cis, cstart);
+ cstart++;
+ }
+}
+
+PJ_DEF(void) pj_cis_del_str( pj_cis_t *cis, const char *str)
+{
+ while (*str) {
+ PJ_CIS_CLR(cis, *str);
+ ++str;
+ }
+}
+
+PJ_DEF(void) pj_cis_invert( pj_cis_t *cis )
+{
+ unsigned i;
+ for (i=0; i<256; ++i) {
+ if (PJ_CIS_ISSET(cis,i))
+ PJ_CIS_CLR(cis,i);
+ else
+ PJ_CIS_SET(cis,i);
+ }
+}
+
+PJ_DEF(void) pj_scan_init( pj_scanner *scanner, char *bufstart, int buflen,
+ unsigned options, pj_syn_err_func_ptr callback )
+{
+ PJ_CHECK_STACK();
+
+ scanner->begin = scanner->curptr = bufstart;
+ scanner->end = bufstart + buflen;
+ scanner->line = 1;
+ scanner->col = 1;
+ scanner->callback = callback;
+ scanner->skip_ws = options;
+
+ if (scanner->skip_ws)
+ pj_scan_skip_whitespace(scanner);
+
+ scanner->col = scanner->curptr - scanner->begin + 1;
+}
+
+
+PJ_DEF(void) pj_scan_fini( pj_scanner *scanner )
+{
+ PJ_CHECK_STACK();
+ PJ_UNUSED_ARG(scanner);
+}
+
+PJ_DEF(void) pj_scan_skip_whitespace( pj_scanner *scanner )
+{
+ register char *s = scanner->curptr;
+
+ PJ_CHECK_STACK();
+
+ while (PJ_SCAN_IS_SPACE(*s)) {
+ ++s;
+ }
+
+ if ((scanner->skip_ws & PJ_SCAN_AUTOSKIP_NEWLINE) && PJ_SCAN_IS_NEWLINE(*s)) {
+ for (;;) {
+ if (*s == '\r') {
+ ++s;
+ if (*s == '\n') ++s;
+ ++scanner->line;
+ scanner->col = 1;
+ scanner->curptr = s;
+ } else if (*s == '\n') {
+ ++s;
+ ++scanner->line;
+ scanner->col = 1;
+ scanner->curptr = s;
+ } else if (PJ_SCAN_IS_SPACE(*s)) {
+ do {
+ ++s;
+ } while (PJ_SCAN_IS_SPACE(*s));
+ } else {
+ break;
+ }
+ }
+ }
+
+ if (PJ_SCAN_IS_NEWLINE(*s) && (scanner->skip_ws & PJ_SCAN_AUTOSKIP_WS_HEADER)==PJ_SCAN_AUTOSKIP_WS_HEADER) {
+ /* Check for header continuation. */
+ scanner->col += s - scanner->curptr;
+ scanner->curptr = s;
+
+ if (*s == '\r') {
+ ++s;
+ }
+ if (*s == '\n') {
+ ++s;
+ }
+ if (PJ_SCAN_IS_SPACE(*s)) {
+ register char *t = s;
+ do {
+ ++t;
+ } while (PJ_SCAN_IS_SPACE(*t));
+
+ ++scanner->line;
+ scanner->col = t-s;
+ scanner->curptr = t;
+ }
+ } else {
+ scanner->col += s - scanner->curptr;
+ scanner->curptr = s;
+ }
+}
+
+PJ_DEF(int) pj_scan_peek( pj_scanner *scanner,
+ const pj_cis_t *spec, pj_str_t *out)
+{
+ register char *s = scanner->curptr;
+ register char *end = scanner->end;
+
+ PJ_CHECK_STACK();
+
+ if (pj_scan_is_eof(scanner)) {
+ pj_scan_syntax_err(scanner);
+ return -1;
+ }
+
+ while (PJ_SCAN_CHECK_EOF(s) && pj_cis_match(spec, *s))
+ ++s;
+
+ pj_strset3(out, scanner->curptr, s);
+ return s < scanner->end ? *s : 0;
+}
+
+
+PJ_DEF(int) pj_scan_peek_n( pj_scanner *scanner,
+ pj_size_t len, pj_str_t *out)
+{
+ char *endpos = scanner->curptr + len;
+
+ PJ_CHECK_STACK();
+
+ if (endpos > scanner->end) {
+ pj_scan_syntax_err(scanner);
+ return -1;
+ }
+
+ pj_strset(out, scanner->curptr, len);
+ return *endpos;
+}
+
+
+PJ_DEF(int) pj_scan_peek_until( pj_scanner *scanner,
+ const pj_cis_t *spec,
+ pj_str_t *out)
+{
+ register char *s = scanner->curptr;
+ register char *end = scanner->end;
+
+ PJ_CHECK_STACK();
+
+ if (pj_scan_is_eof(scanner)) {
+ pj_scan_syntax_err(scanner);
+ return -1;
+ }
+
+ while (PJ_SCAN_CHECK_EOF(s) && !pj_cis_match( spec, *s))
+ ++s;
+
+ pj_strset3(out, scanner->curptr, s);
+ return s!=scanner->end ? *s : 0;
+}
+
+
+PJ_DEF(void) pj_scan_get( pj_scanner *scanner,
+ const pj_cis_t *spec, pj_str_t *out)
+{
+ register char *s = scanner->curptr;
+ register char *end = scanner->end;
+ char *start = s;
+
+ PJ_CHECK_STACK();
+
+ if (pj_scan_is_eof(scanner) || !pj_cis_match(spec, *s)) {
+ pj_scan_syntax_err(scanner);
+ return;
+ }
+
+ do {
+ ++s;
+ } while (PJ_SCAN_CHECK_EOF(s) && pj_cis_match(spec, *s));
+
+ pj_strset3(out, scanner->curptr, s);
+
+ scanner->col += (s - start);
+ scanner->curptr = s;
+
+ if (scanner->skip_ws) {
+ pj_scan_skip_whitespace(scanner);
+ }
+}
+
+
+PJ_DEF(void) pj_scan_get_quote( pj_scanner *scanner,
+ int begin_quote, int end_quote,
+ pj_str_t *out)
+{
+ register char *s = scanner->curptr;
+ register char *end = scanner->end;
+ char *start = s;
+
+ PJ_CHECK_STACK();
+
+ /* Check and eat the begin_quote. */
+ if (*s != begin_quote) {
+ pj_scan_syntax_err(scanner);
+ return;
+ }
+ ++s;
+
+ /* Loop until end_quote is found.
+ */
+ do {
+ /* loop until end_quote is found. */
+ do {
+ ++s;
+ } while (s != end && *s != '\n' && *s != end_quote);
+
+ /* check that no backslash character precedes the end_quote. */
+ if (*s == end_quote) {
+ if (*(s-1) == '\\') {
+ if (s-2 == scanner->begin) {
+ break;
+ } else {
+ char *q = s-2;
+ char *r = s-2;
+
+ while (r != scanner->begin && *r == '\\') {
+ --r;
+ }
+ /* break from main loop if we have odd number of backslashes */
+ if (((unsigned)(q-r) & 0x01) == 1) {
+ break;
+ }
+ }
+ } else {
+ /* end_quote is not preceeded by backslash. break now. */
+ break;
+ }
+ } else {
+ /* loop ended by non-end_quote character. break now. */
+ break;
+ }
+ } while (1);
+
+ /* Check and eat the end quote. */
+ if (*s != end_quote) {
+ pj_scan_syntax_err(scanner);
+ return;
+ }
+ ++s;
+
+ pj_strset3(out, scanner->curptr, s);
+
+ scanner->col += (s - start);
+ scanner->curptr = s;
+
+ if (scanner->skip_ws) {
+ pj_scan_skip_whitespace(scanner);
+ }
+}
+
+PJ_DEF(void) pj_scan_get_n( pj_scanner *scanner,
+ unsigned N, pj_str_t *out)
+{
+ register char *s = scanner->curptr;
+ char *start = scanner->curptr;
+
+ PJ_CHECK_STACK();
+
+ if (scanner->curptr + N > scanner->end) {
+ pj_scan_syntax_err(scanner);
+ return;
+ }
+
+ pj_strset(out, s, N);
+
+ s += N;
+ scanner->col += (s - start);
+ scanner->curptr = s;
+
+ if (scanner->skip_ws) {
+ pj_scan_skip_whitespace(scanner);
+ }
+}
+
+
+PJ_DEF(int) pj_scan_get_char( pj_scanner *scanner )
+{
+ char *start = scanner->curptr;
+ int chr = *start;
+
+ PJ_CHECK_STACK();
+
+ if (pj_scan_is_eof(scanner)) {
+ pj_scan_syntax_err(scanner);
+ return 0;
+ }
+
+ ++scanner->curptr;
+ scanner->col += (scanner->curptr - start);
+
+ if (scanner->skip_ws) {
+ pj_scan_skip_whitespace(scanner);
+ }
+ return chr;
+}
+
+
+PJ_DEF(void) pj_scan_get_newline( pj_scanner *scanner )
+{
+ PJ_CHECK_STACK();
+
+ if (!PJ_SCAN_IS_NEWLINE(*scanner->curptr)) {
+ pj_scan_syntax_err(scanner);
+ return;
+ }
+
+ if (*scanner->curptr == '\r') {
+ ++scanner->curptr;
+ }
+ if (*scanner->curptr == '\n') {
+ ++scanner->curptr;
+ }
+
+ ++scanner->line;
+ scanner->col = 1;
+
+ if (scanner->skip_ws) {
+ pj_scan_skip_whitespace(scanner);
+ }
+}
+
+
+PJ_DEF(void) pj_scan_get_until( pj_scanner *scanner,
+ const pj_cis_t *spec, pj_str_t *out)
+{
+ register char *s = scanner->curptr;
+ register char *end = scanner->end;
+ char *start = s;
+
+ PJ_CHECK_STACK();
+
+ if (pj_scan_is_eof(scanner)) {
+ pj_scan_syntax_err(scanner);
+ return;
+ }
+
+ while (PJ_SCAN_CHECK_EOF(s) && !pj_cis_match(spec, *s)) {
+ ++s;
+ }
+
+ pj_strset3(out, scanner->curptr, s);
+
+ scanner->col += (s - start);
+ scanner->curptr = s;
+
+ if (scanner->skip_ws) {
+ pj_scan_skip_whitespace(scanner);
+ }
+}
+
+
+PJ_DEF(void) pj_scan_get_until_ch( pj_scanner *scanner,
+ int until_char, pj_str_t *out)
+{
+ register char *s = scanner->curptr;
+ register char *end = scanner->end;
+ char *start = s;
+
+ PJ_CHECK_STACK();
+
+ if (pj_scan_is_eof(scanner)) {
+ pj_scan_syntax_err(scanner);
+ return;
+ }
+
+ while (PJ_SCAN_CHECK_EOF(s) && *s != until_char) {
+ ++s;
+ }
+
+ pj_strset3(out, scanner->curptr, s);
+
+ scanner->col += (s - start);
+ scanner->curptr = s;
+
+ if (scanner->skip_ws) {
+ pj_scan_skip_whitespace(scanner);
+ }
+}
+
+
+PJ_DEF(void) pj_scan_get_until_chr( pj_scanner *scanner,
+ const char *until_spec, pj_str_t *out)
+{
+ register char *s = scanner->curptr;
+ register char *end = scanner->end;
+ char *start = scanner->curptr;
+
+ PJ_CHECK_STACK();
+
+ if (pj_scan_is_eof(scanner)) {
+ pj_scan_syntax_err(scanner);
+ return;
+ }
+
+ while (PJ_SCAN_CHECK_EOF(s) && !strchr(until_spec, *s)) {
+ ++s;
+ }
+
+ pj_strset3(out, scanner->curptr, s);
+
+ scanner->col += (s - start);
+ scanner->curptr = s;
+
+ if (scanner->skip_ws) {
+ pj_scan_skip_whitespace(scanner);
+ }
+}
+
+PJ_DEF(void) pj_scan_advance_n( pj_scanner *scanner,
+ unsigned N, pj_bool_t skip_ws)
+{
+ char *start = scanner->curptr;
+
+ PJ_CHECK_STACK();
+
+ if (scanner->curptr + N > scanner->end) {
+ pj_scan_syntax_err(scanner);
+ return;
+ }
+
+ scanner->curptr += N;
+ scanner->col += (scanner->curptr - start);
+
+ if (skip_ws) {
+ pj_scan_skip_whitespace(scanner);
+ }
+}
+
+
+PJ_DEF(int) pj_scan_strcmp( pj_scanner *scanner, const char *s, int len)
+{
+ if (scanner->curptr + len > scanner->end) {
+ pj_scan_syntax_err(scanner);
+ return -1;
+ }
+ return strncmp(scanner->curptr, s, len);
+}
+
+
+PJ_DEF(int) pj_scan_stricmp( pj_scanner *scanner, const char *s, int len)
+{
+ if (scanner->curptr + len > scanner->end) {
+ pj_scan_syntax_err(scanner);
+ return -1;
+ }
+ return strnicmp(scanner->curptr, s, len);
+}
+
+
+PJ_DEF(void) pj_scan_save_state( pj_scanner *scanner, pj_scan_state *state)
+{
+ PJ_CHECK_STACK();
+
+ state->curptr = scanner->curptr;
+ state->line = scanner->line;
+ state->col = scanner->col;
+}
+
+
+PJ_DEF(void) pj_scan_restore_state( pj_scanner *scanner,
+ pj_scan_state *state)
+{
+ PJ_CHECK_STACK();
+
+ scanner->curptr = state->curptr;
+ scanner->line = state->line;
+ scanner->col = state->col;
+}
+
+
diff --git a/pjlib-util/src/pjlib-util/string.c b/pjlib-util/src/pjlib-util/string.c
index 55859eb6..8115bc8f 100644
--- a/pjlib-util/src/pjlib-util/string.c
+++ b/pjlib-util/src/pjlib-util/string.c
@@ -1,99 +1,107 @@
-/* $Id: $ */
-/*
- * Copyright (C)2003-2006 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 <pjlib-util/string.h>
-#include <pj/ctype.h>
-
-PJ_DEF(void) pj_str_unescape(pj_str_t *str)
-{
- char *src = str->ptr;
- char *dst = str->ptr;
- char *end = src + str->slen;
-
- while (src != end) {
- if (*src == '%' && src < end-2) {
- *dst = (pj_uint8_t) ((pj_hex_digit_to_val(*(src+1)) << 4) +
- pj_hex_digit_to_val(*(src+2)));
- ++dst;
- src += 3;
- } else {
- ++src;
- ++dst;
- }
- }
- str->slen = dst - str->ptr;
-}
-
-PJ_DEF(pj_str_t*) pj_strcpy_unescape(pj_str_t *dst_str,
- const pj_str_t *src_str)
-{
- const char *src = src_str->ptr;
- const char *end = src + src_str->slen;
- char *dst = dst_str->ptr;
-
- while (src != end) {
- if (*src == '%' && src < end-2) {
- *dst = (pj_uint8_t) ((pj_hex_digit_to_val(*(src+1)) << 4) +
- pj_hex_digit_to_val(*(src+2)));
- ++dst;
- src += 3;
- } else {
- *dst++ = *src++;
- }
- }
- dst_str->slen = dst - dst_str->ptr;
- return dst_str;
-}
-
-PJ_DEF(pj_ssize_t) pj_strncpy2_escape( char *dst_str, const pj_str_t *src_str,
- pj_ssize_t max, const pj_cis_t *unres)
-{
- const char *src = src_str->ptr;
- const char *src_end = src + src_str->slen;
- char *dst = dst_str;
- char *dst_end = dst + max;
-
- if (max < src_str->slen)
- return -1;
-
- while (src != src_end && dst != dst_end) {
- if (pj_cis_match(unres, *src)) {
- *dst++ = *src++;
- } else {
- if (dst < dst_end-2) {
- *dst++ = '%';
- pj_val_to_hex_digit(*src, dst);
- dst+=2;
- ++src;
- } else {
- break;
- }
- }
- }
-
- return src==src_end ? dst-dst_str : -1;
-}
-
-PJ_DEF(pj_str_t*) pj_strncpy_escape(pj_str_t *dst_str,
- const pj_str_t *src_str,
- pj_ssize_t max, const pj_cis_t *unres)
-{
- dst_str->slen = pj_strncpy2_escape(dst_str->ptr, src_str, max, unres);
- return dst_str->slen < 0 ? NULL : dst_str;
-}
-
+/* $Id$ */
+/*
+ * Copyright (C)2003-2006 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 <pjlib-util/string.h>
+#include <pj/ctype.h>
+#include <pj/string.h>
+#include <pj/pool.h>
+
+PJ_DEF(pj_str_t) pj_str_unescape( pj_pool_t *pool, const pj_str_t *src_str)
+{
+ char *src = src_str->ptr;
+ char *end = src + src_str->slen;
+ pj_str_t dst_str;
+ char *dst;
+
+ if (pj_strchr(src_str, '%')==NULL)
+ return *src_str;
+
+ dst = dst_str.ptr = pj_pool_alloc(pool, src_str->slen);
+
+ while (src != end) {
+ if (*src == '%' && src < end-2) {
+ *dst = (pj_uint8_t) ((pj_hex_digit_to_val(*(src+1)) << 4) +
+ pj_hex_digit_to_val(*(src+2)));
+ ++dst;
+ src += 3;
+ } else {
+ *dst++ = *src++;
+ }
+ }
+ dst_str.slen = dst - dst_str.ptr;
+ return dst_str;
+}
+
+PJ_DEF(pj_str_t*) pj_strcpy_unescape(pj_str_t *dst_str,
+ const pj_str_t *src_str)
+{
+ const char *src = src_str->ptr;
+ const char *end = src + src_str->slen;
+ char *dst = dst_str->ptr;
+
+ while (src != end) {
+ if (*src == '%' && src < end-2) {
+ *dst = (pj_uint8_t) ((pj_hex_digit_to_val(*(src+1)) << 4) +
+ pj_hex_digit_to_val(*(src+2)));
+ ++dst;
+ src += 3;
+ } else {
+ *dst++ = *src++;
+ }
+ }
+ dst_str->slen = dst - dst_str->ptr;
+ return dst_str;
+}
+
+PJ_DEF(pj_ssize_t) pj_strncpy2_escape( char *dst_str, const pj_str_t *src_str,
+ pj_ssize_t max, const pj_cis_t *unres)
+{
+ const char *src = src_str->ptr;
+ const char *src_end = src + src_str->slen;
+ char *dst = dst_str;
+ char *dst_end = dst + max;
+
+ if (max < src_str->slen)
+ return -1;
+
+ while (src != src_end && dst != dst_end) {
+ if (pj_cis_match(unres, *src)) {
+ *dst++ = *src++;
+ } else {
+ if (dst < dst_end-2) {
+ *dst++ = '%';
+ pj_val_to_hex_digit(*src, dst);
+ dst+=2;
+ ++src;
+ } else {
+ break;
+ }
+ }
+ }
+
+ return src==src_end ? dst-dst_str : -1;
+}
+
+PJ_DEF(pj_str_t*) pj_strncpy_escape(pj_str_t *dst_str,
+ const pj_str_t *src_str,
+ pj_ssize_t max, const pj_cis_t *unres)
+{
+ dst_str->slen = pj_strncpy2_escape(dst_str->ptr, src_str, max, unres);
+ return dst_str->slen < 0 ? NULL : dst_str;
+}
+
diff --git a/pjlib-util/src/pjlib-util/stun.c b/pjlib-util/src/pjlib-util/stun.c
index f4287691..52c5bf55 100644
--- a/pjlib-util/src/pjlib-util/stun.c
+++ b/pjlib-util/src/pjlib-util/stun.c
@@ -1,129 +1,129 @@
-/* $Id$ */
-/*
- * Copyright (C) 2003-2006 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 <pjlib-util/stun.h>
-#include <pj/pool.h>
-#include <pj/log.h>
-#include <pj/sock.h>
-#include <pj/os.h>
-
-#define THIS_FILE "stun"
-
-PJ_DEF(pj_status_t) pj_stun_create_bind_req( pj_pool_t *pool,
- void **msg, pj_size_t *len,
- pj_uint32_t id_hi,
- pj_uint32_t id_lo)
-{
- pj_stun_msg_hdr *hdr;
-
- PJ_CHECK_STACK();
-
- PJ_LOG(5,(THIS_FILE, "pj_stun_create_bind_req"));
-
- hdr = pj_pool_calloc(pool, 1, sizeof(pj_stun_msg_hdr));
- if (!hdr) {
- PJ_LOG(5,(THIS_FILE, "Error allocating memory!"));
- return -1;
- }
-
- hdr->type = pj_htons(PJ_STUN_BINDING_REQUEST);
- hdr->tsx[2] = pj_htonl(id_hi);
- hdr->tsx[3] = pj_htonl(id_lo);
- *msg = hdr;
- *len = sizeof(pj_stun_msg_hdr);
-
- return 0;
-}
-
-PJ_DEF(pj_status_t) pj_stun_parse_msg( void *buf, pj_size_t len,
- pj_stun_msg *msg)
-{
- pj_uint16_t msg_type, msg_len;
- char *p_attr;
-
- PJ_CHECK_STACK();
-
- PJ_LOG(5,(THIS_FILE, "pj_stun_parse_msg %p, len=%d", buf, len));
-
- msg->hdr = (pj_stun_msg_hdr*)buf;
- msg_type = pj_ntohs(msg->hdr->type);
-
- switch (msg_type) {
- case PJ_STUN_BINDING_REQUEST:
- case PJ_STUN_BINDING_RESPONSE:
- case PJ_STUN_BINDING_ERROR_RESPONSE:
- case PJ_STUN_SHARED_SECRET_REQUEST:
- case PJ_STUN_SHARED_SECRET_RESPONSE:
- case PJ_STUN_SHARED_SECRET_ERROR_RESPONSE:
- break;
- default:
- PJ_LOG(5,(THIS_FILE, "Error: unknown msg type %d", msg_type));
- return -1;
- }
-
- msg_len = pj_ntohs(msg->hdr->length);
- if (msg_len != len - sizeof(pj_stun_msg_hdr)) {
- PJ_LOG(5,(THIS_FILE, "Error: invalid msg_len %d (expecting %d)",
- msg_len, len - sizeof(pj_stun_msg_hdr)));
- return -1;
- }
-
- msg->attr_count = 0;
- p_attr = (char*)buf + sizeof(pj_stun_msg_hdr);
-
- while (msg_len > 0) {
- pj_stun_attr_hdr **attr = &msg->attr[msg->attr_count];
- pj_uint32_t len;
-
- *attr = (pj_stun_attr_hdr*)p_attr;
- len = pj_ntohs((pj_uint16_t) ((*attr)->length)) + sizeof(pj_stun_attr_hdr);
-
- if (msg_len < len) {
- PJ_LOG(5,(THIS_FILE, "Error: length mismatch in attr %d",
- msg->attr_count));
- return -1;
- }
-
- if (pj_ntohs((*attr)->type) > PJ_STUN_ATTR_REFLECTED_FORM) {
- PJ_LOG(5,(THIS_FILE, "Error: invalid attr type %d in attr %d",
- pj_ntohs((*attr)->type), msg->attr_count));
- return -1;
- }
-
- msg_len = (pj_uint16_t)(msg_len - len);
- p_attr += len;
- ++msg->attr_count;
- }
-
- return 0;
-}
-
-PJ_DEF(void*) pj_stun_msg_find_attr( pj_stun_msg *msg, pj_stun_attr_type t)
-{
- int i;
-
- PJ_CHECK_STACK();
-
- for (i=0; i<msg->attr_count; ++i) {
- pj_stun_attr_hdr *attr = msg->attr[i];
- if (pj_ntohs(attr->type) == t)
- return attr;
- }
-
- return 0;
-}
+/* $Id$ */
+/*
+ * Copyright (C) 2003-2006 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 <pjlib-util/stun.h>
+#include <pj/pool.h>
+#include <pj/log.h>
+#include <pj/sock.h>
+#include <pj/os.h>
+
+#define THIS_FILE "stun"
+
+PJ_DEF(pj_status_t) pj_stun_create_bind_req( pj_pool_t *pool,
+ void **msg, pj_size_t *len,
+ pj_uint32_t id_hi,
+ pj_uint32_t id_lo)
+{
+ pj_stun_msg_hdr *hdr;
+
+ PJ_CHECK_STACK();
+
+ PJ_LOG(5,(THIS_FILE, "pj_stun_create_bind_req"));
+
+ hdr = pj_pool_calloc(pool, 1, sizeof(pj_stun_msg_hdr));
+ if (!hdr) {
+ PJ_LOG(5,(THIS_FILE, "Error allocating memory!"));
+ return -1;
+ }
+
+ hdr->type = pj_htons(PJ_STUN_BINDING_REQUEST);
+ hdr->tsx[2] = pj_htonl(id_hi);
+ hdr->tsx[3] = pj_htonl(id_lo);
+ *msg = hdr;
+ *len = sizeof(pj_stun_msg_hdr);
+
+ return 0;
+}
+
+PJ_DEF(pj_status_t) pj_stun_parse_msg( void *buf, pj_size_t len,
+ pj_stun_msg *msg)
+{
+ pj_uint16_t msg_type, msg_len;
+ char *p_attr;
+
+ PJ_CHECK_STACK();
+
+ PJ_LOG(5,(THIS_FILE, "pj_stun_parse_msg %p, len=%d", buf, len));
+
+ msg->hdr = (pj_stun_msg_hdr*)buf;
+ msg_type = pj_ntohs(msg->hdr->type);
+
+ switch (msg_type) {
+ case PJ_STUN_BINDING_REQUEST:
+ case PJ_STUN_BINDING_RESPONSE:
+ case PJ_STUN_BINDING_ERROR_RESPONSE:
+ case PJ_STUN_SHARED_SECRET_REQUEST:
+ case PJ_STUN_SHARED_SECRET_RESPONSE:
+ case PJ_STUN_SHARED_SECRET_ERROR_RESPONSE:
+ break;
+ default:
+ PJ_LOG(5,(THIS_FILE, "Error: unknown msg type %d", msg_type));
+ return -1;
+ }
+
+ msg_len = pj_ntohs(msg->hdr->length);
+ if (msg_len != len - sizeof(pj_stun_msg_hdr)) {
+ PJ_LOG(5,(THIS_FILE, "Error: invalid msg_len %d (expecting %d)",
+ msg_len, len - sizeof(pj_stun_msg_hdr)));
+ return -1;
+ }
+
+ msg->attr_count = 0;
+ p_attr = (char*)buf + sizeof(pj_stun_msg_hdr);
+
+ while (msg_len > 0) {
+ pj_stun_attr_hdr **attr = &msg->attr[msg->attr_count];
+ pj_uint32_t len;
+
+ *attr = (pj_stun_attr_hdr*)p_attr;
+ len = pj_ntohs((pj_uint16_t) ((*attr)->length)) + sizeof(pj_stun_attr_hdr);
+
+ if (msg_len < len) {
+ PJ_LOG(5,(THIS_FILE, "Error: length mismatch in attr %d",
+ msg->attr_count));
+ return -1;
+ }
+
+ if (pj_ntohs((*attr)->type) > PJ_STUN_ATTR_REFLECTED_FORM) {
+ PJ_LOG(5,(THIS_FILE, "Error: invalid attr type %d in attr %d",
+ pj_ntohs((*attr)->type), msg->attr_count));
+ return -1;
+ }
+
+ msg_len = (pj_uint16_t)(msg_len - len);
+ p_attr += len;
+ ++msg->attr_count;
+ }
+
+ return 0;
+}
+
+PJ_DEF(void*) pj_stun_msg_find_attr( pj_stun_msg *msg, pj_stun_attr_type t)
+{
+ int i;
+
+ PJ_CHECK_STACK();
+
+ for (i=0; i<msg->attr_count; ++i) {
+ pj_stun_attr_hdr *attr = msg->attr[i];
+ if (pj_ntohs(attr->type) == t)
+ return attr;
+ }
+
+ return 0;
+}
diff --git a/pjlib-util/src/pjlib-util/stun_client.c b/pjlib-util/src/pjlib-util/stun_client.c
index e0633ccc..69153f6d 100644
--- a/pjlib-util/src/pjlib-util/stun_client.c
+++ b/pjlib-util/src/pjlib-util/stun_client.c
@@ -1,277 +1,277 @@
-/* $Id$ */
-/*
- * Copyright (C) 2003-2006 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 <pjlib-util/stun.h>
-#include <pj/pool.h>
-#include <pj/log.h>
-#include <pj/string.h>
-#include <pj/os.h>
-#include <pj/sock_select.h>
-
-enum { MAX_REQUEST = 3 };
-static int stun_timer[] = {1600, 1600, 1600 };
-
-#define THIS_FILE "stunclient"
-#define LOG_ADDR(addr) pj_inet_ntoa(addr.sin_addr), pj_ntohs(addr.sin_port)
-
-
-PJ_DECL(pj_status_t) pj_stun_get_mapped_addr( pj_pool_factory *pf,
- int sock_cnt, pj_sock_t sock[],
- const pj_str_t *srv1, int port1,
- const pj_str_t *srv2, int port2,
- pj_sockaddr_in mapped_addr[])
-{
- pj_sockaddr_in srv_addr[2];
- int i, j, rc, send_cnt = 0;
- pj_pool_t *pool;
- struct {
- struct {
- pj_uint32_t mapped_addr;
- pj_uint32_t mapped_port;
- } srv[2];
- } *rec;
- void *out_msg;
- pj_size_t out_msg_len;
- int wait_resp = 0;
- int mapped_status = 0;
-
- PJ_CHECK_STACK();
-
- /* Create pool. */
- pool = pj_pool_create(pf, "stun%p", 1024, 1024, NULL);
- if (!pool) {
- mapped_status = PJ_STUN_ERR_MEMORY;
- return -1;
- }
-
- /* Allocate client records */
- rec = pj_pool_calloc(pool, sock_cnt, sizeof(*rec));
- if (!rec) {
- mapped_status = PJ_STUN_ERR_MEMORY;
- goto on_error;
- }
-
- /* Create the outgoing BIND REQUEST message template */
- rc = pj_stun_create_bind_req( pool, &out_msg, &out_msg_len, 0, 0);
- if (rc != 0) {
- mapped_status = -1;
- goto on_error;
- }
-
- /* Resolve servers. */
- if (pj_sockaddr_in_init(&srv_addr[0], srv1, (pj_uint16_t)port1) != 0) {
- mapped_status = PJ_STUN_ERR_RESOLVE;
- goto on_error;
- }
- if (pj_sockaddr_in_init(&srv_addr[1], srv2, (pj_uint16_t)port2) != 0) {
- mapped_status = PJ_STUN_ERR_RESOLVE;
- goto on_error;
- }
-
- /* Init mapped addresses to zero */
- pj_memset(mapped_addr, 0, sock_cnt * sizeof(pj_sockaddr_in));
-
- /* Main retransmission loop. */
- for (send_cnt=0; send_cnt<MAX_REQUEST; ++send_cnt) {
- pj_time_val next_tx, now;
- pj_fd_set_t r;
- int select_rc;
-
- PJ_LOG(4,(THIS_FILE, "STUN retransmit %d, wait_resp=%d",
- send_cnt, wait_resp));
-
- PJ_FD_ZERO(&r);
-
- /* Send messages to servers that has not given us response. */
- for (i=0; i<sock_cnt && mapped_status==0; ++i) {
- for (j=0; j<2 && mapped_status==0; ++j) {
- pj_stun_msg_hdr *msg_hdr = out_msg;
- pj_ssize_t sent_len;
-
- if (rec[i].srv[j].mapped_port != 0)
- continue;
-
- /* Modify message so that we can distinguish response. */
- msg_hdr->tsx[2] = pj_htonl(i);
- msg_hdr->tsx[3] = pj_htonl(j);
-
- /* Send! */
- sent_len = out_msg_len;
- rc = pj_sock_sendto(sock[i], out_msg, &sent_len, 0,
- (pj_sockaddr_t*)&srv_addr[j],
- sizeof(pj_sockaddr_in));
- if (sent_len != (int)out_msg_len) {
- PJ_LOG(4,(THIS_FILE,
- "Error sending STUN request to %s:%d",
- LOG_ADDR(srv_addr[j])));
- mapped_status = PJ_STUN_ERR_TRANSPORT;
- } else {
- ++wait_resp;
- }
- }
- }
-
- /* All requests sent.
- * The loop below will wait for responses until all responses have
- * been received (i.e. wait_resp==0) or timeout occurs, which then
- * we'll go to the next retransmission iteration.
- */
-
- /* Calculate time of next retransmission. */
- pj_gettimeofday(&next_tx);
- next_tx.sec += (stun_timer[send_cnt]/1000);
- next_tx.msec += (stun_timer[send_cnt]%1000);
- pj_time_val_normalize(&next_tx);
-
- for (pj_gettimeofday(&now), select_rc=1;
- mapped_status==0 && select_rc==1 && wait_resp>0 && PJ_TIME_VAL_LT(now, next_tx);
- pj_gettimeofday(&now))
- {
- pj_time_val timeout;
-
- timeout = next_tx;
- PJ_TIME_VAL_SUB(timeout, now);
-
- for (i=0; i<sock_cnt; ++i) {
- PJ_FD_SET(sock[i], &r);
- }
-
- select_rc = pj_sock_select(FD_SETSIZE, &r, NULL, NULL, &timeout);
- if (select_rc < 1)
- continue;
-
- for (i=0; i<sock_cnt; ++i) {
- int sock_idx, srv_idx;
- pj_ssize_t len;
- pj_stun_msg msg;
- pj_sockaddr_in addr;
- int addrlen = sizeof(addr);
- pj_stun_mapped_addr_attr *attr;
- char recv_buf[128];
-
- if (!PJ_FD_ISSET(sock[i], &r))
- continue;
-
- len = sizeof(recv_buf);
- pj_sock_recvfrom( sock[i], recv_buf,
- &len, 0,
- (pj_sockaddr_t*)&addr,
- &addrlen);
-
- --wait_resp;
-
- if (len < 1) {
- mapped_status = PJ_STUN_ERR_TRANSPORT;
- continue;
- }
-
- if (pj_stun_parse_msg(recv_buf, len, &msg) != 0) {
- PJ_LOG(4,(THIS_FILE,
- "Error parsing STUN response from %s:%d",
- LOG_ADDR(addr)));
- mapped_status = PJ_STUN_ERR_INVALID_MSG;
- continue;
- }
-
- sock_idx = pj_ntohl(msg.hdr->tsx[2]);
- srv_idx = pj_ntohl(msg.hdr->tsx[3]);
-
- if (sock_idx<0 || sock_idx>=sock_cnt || srv_idx<0 || srv_idx>=2) {
- PJ_LOG(4,(THIS_FILE,
- "Invalid transaction ID from %s:%d",
- LOG_ADDR(addr)));
- mapped_status = PJ_STUN_ERR_INVALID_MSG;
- continue;
- }
-
- if (pj_ntohs(msg.hdr->type) != PJ_STUN_BINDING_RESPONSE) {
- PJ_LOG(4,(THIS_FILE,
- "Non binding response %d from %s:%d",
- pj_ntohs(msg.hdr->type), LOG_ADDR(addr)));
- mapped_status = PJ_STUN_ERR_INVALID_MSG;
- continue;
- }
-
- if (pj_stun_msg_find_attr(&msg, PJ_STUN_ATTR_ERROR_CODE) != NULL) {
- PJ_LOG(4,(THIS_FILE,
- "Got STUN error attribute from %s:%d",
- LOG_ADDR(addr)));
- mapped_status = PJ_STUN_ERR_INVALID_MSG;
- continue;
- }
-
- attr = (void*)pj_stun_msg_find_attr(&msg, PJ_STUN_ATTR_MAPPED_ADDR);
- if (!attr) {
- PJ_LOG(4,(THIS_FILE,
- "No mapped address in response from %s:%d",
- LOG_ADDR(addr)));
- mapped_status = PJ_STUN_ERR_INVALID_MSG;
- continue;
- }
-
- rec[sock_idx].srv[srv_idx].mapped_addr = attr->addr;
- rec[sock_idx].srv[srv_idx].mapped_port = attr->port;
- }
- }
-
- /* The best scenario is if all requests have been replied.
- * Then we don't need to go to the next retransmission iteration.
- */
- if (wait_resp <= 0)
- break;
- }
-
- for (i=0; i<sock_cnt && mapped_status==0; ++i) {
- if (rec[i].srv[0].mapped_addr == rec[i].srv[1].mapped_addr &&
- rec[i].srv[0].mapped_port == rec[i].srv[1].mapped_port)
- {
- mapped_addr[i].sin_family = PJ_AF_INET;
- mapped_addr[i].sin_addr.s_addr = rec[i].srv[0].mapped_addr;
- mapped_addr[i].sin_port = (pj_uint16_t)rec[i].srv[0].mapped_port;
-
- if (rec[i].srv[0].mapped_addr == 0 || rec[i].srv[0].mapped_port == 0) {
- mapped_status = PJ_STUN_ERR_NO_RESPONSE;
- }
- } else {
- mapped_status = PJ_STUN_ERR_SYMETRIC;
- }
- }
-
- pj_pool_release(pool);
-
- return mapped_status;
-
-on_error:
- if (pool) pj_pool_release(pool);
- return -1;
-}
-
-PJ_DEF(const char*) pj_stun_get_err_msg(pj_status_t status)
-{
- switch (status) {
- case 0: return "No error";
- case -1: return "General error";
- case PJ_STUN_ERR_MEMORY: return "Memory allocation failed";
- case PJ_STUN_ERR_RESOLVE: return "Invalid IP or unable to resolve STUN server";
- case PJ_STUN_ERR_TRANSPORT: return "Unable to contact STUN server";
- case PJ_STUN_ERR_INVALID_MSG: return "Invalid response from STUN server";
- case PJ_STUN_ERR_NO_RESPONSE: return "No response from STUN server";
- case PJ_STUN_ERR_SYMETRIC: return "Different mappings are returned from servers";
- }
- return "Unknown error";
-}
+/* $Id$ */
+/*
+ * Copyright (C) 2003-2006 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 <pjlib-util/stun.h>
+#include <pj/pool.h>
+#include <pj/log.h>
+#include <pj/string.h>
+#include <pj/os.h>
+#include <pj/sock_select.h>
+
+enum { MAX_REQUEST = 3 };
+static int stun_timer[] = {1600, 1600, 1600 };
+
+#define THIS_FILE "stunclient"
+#define LOG_ADDR(addr) pj_inet_ntoa(addr.sin_addr), pj_ntohs(addr.sin_port)
+
+
+PJ_DECL(pj_status_t) pj_stun_get_mapped_addr( pj_pool_factory *pf,
+ int sock_cnt, pj_sock_t sock[],
+ const pj_str_t *srv1, int port1,
+ const pj_str_t *srv2, int port2,
+ pj_sockaddr_in mapped_addr[])
+{
+ pj_sockaddr_in srv_addr[2];
+ int i, j, rc, send_cnt = 0;
+ pj_pool_t *pool;
+ struct {
+ struct {
+ pj_uint32_t mapped_addr;
+ pj_uint32_t mapped_port;
+ } srv[2];
+ } *rec;
+ void *out_msg;
+ pj_size_t out_msg_len;
+ int wait_resp = 0;
+ int mapped_status = 0;
+
+ PJ_CHECK_STACK();
+
+ /* Create pool. */
+ pool = pj_pool_create(pf, "stun%p", 1024, 1024, NULL);
+ if (!pool) {
+ mapped_status = PJ_STUN_ERR_MEMORY;
+ return -1;
+ }
+
+ /* Allocate client records */
+ rec = pj_pool_calloc(pool, sock_cnt, sizeof(*rec));
+ if (!rec) {
+ mapped_status = PJ_STUN_ERR_MEMORY;
+ goto on_error;
+ }
+
+ /* Create the outgoing BIND REQUEST message template */
+ rc = pj_stun_create_bind_req( pool, &out_msg, &out_msg_len, 0, 0);
+ if (rc != 0) {
+ mapped_status = -1;
+ goto on_error;
+ }
+
+ /* Resolve servers. */
+ if (pj_sockaddr_in_init(&srv_addr[0], srv1, (pj_uint16_t)port1) != 0) {
+ mapped_status = PJ_STUN_ERR_RESOLVE;
+ goto on_error;
+ }
+ if (pj_sockaddr_in_init(&srv_addr[1], srv2, (pj_uint16_t)port2) != 0) {
+ mapped_status = PJ_STUN_ERR_RESOLVE;
+ goto on_error;
+ }
+
+ /* Init mapped addresses to zero */
+ pj_memset(mapped_addr, 0, sock_cnt * sizeof(pj_sockaddr_in));
+
+ /* Main retransmission loop. */
+ for (send_cnt=0; send_cnt<MAX_REQUEST; ++send_cnt) {
+ pj_time_val next_tx, now;
+ pj_fd_set_t r;
+ int select_rc;
+
+ PJ_LOG(4,(THIS_FILE, "STUN retransmit %d, wait_resp=%d",
+ send_cnt, wait_resp));
+
+ PJ_FD_ZERO(&r);
+
+ /* Send messages to servers that has not given us response. */
+ for (i=0; i<sock_cnt && mapped_status==0; ++i) {
+ for (j=0; j<2 && mapped_status==0; ++j) {
+ pj_stun_msg_hdr *msg_hdr = out_msg;
+ pj_ssize_t sent_len;
+
+ if (rec[i].srv[j].mapped_port != 0)
+ continue;
+
+ /* Modify message so that we can distinguish response. */
+ msg_hdr->tsx[2] = pj_htonl(i);
+ msg_hdr->tsx[3] = pj_htonl(j);
+
+ /* Send! */
+ sent_len = out_msg_len;
+ rc = pj_sock_sendto(sock[i], out_msg, &sent_len, 0,
+ (pj_sockaddr_t*)&srv_addr[j],
+ sizeof(pj_sockaddr_in));
+ if (sent_len != (int)out_msg_len) {
+ PJ_LOG(4,(THIS_FILE,
+ "Error sending STUN request to %s:%d",
+ LOG_ADDR(srv_addr[j])));
+ mapped_status = PJ_STUN_ERR_TRANSPORT;
+ } else {
+ ++wait_resp;
+ }
+ }
+ }
+
+ /* All requests sent.
+ * The loop below will wait for responses until all responses have
+ * been received (i.e. wait_resp==0) or timeout occurs, which then
+ * we'll go to the next retransmission iteration.
+ */
+
+ /* Calculate time of next retransmission. */
+ pj_gettimeofday(&next_tx);
+ next_tx.sec += (stun_timer[send_cnt]/1000);
+ next_tx.msec += (stun_timer[send_cnt]%1000);
+ pj_time_val_normalize(&next_tx);
+
+ for (pj_gettimeofday(&now), select_rc=1;
+ mapped_status==0 && select_rc==1 && wait_resp>0 && PJ_TIME_VAL_LT(now, next_tx);
+ pj_gettimeofday(&now))
+ {
+ pj_time_val timeout;
+
+ timeout = next_tx;
+ PJ_TIME_VAL_SUB(timeout, now);
+
+ for (i=0; i<sock_cnt; ++i) {
+ PJ_FD_SET(sock[i], &r);
+ }
+
+ select_rc = pj_sock_select(FD_SETSIZE, &r, NULL, NULL, &timeout);
+ if (select_rc < 1)
+ continue;
+
+ for (i=0; i<sock_cnt; ++i) {
+ int sock_idx, srv_idx;
+ pj_ssize_t len;
+ pj_stun_msg msg;
+ pj_sockaddr_in addr;
+ int addrlen = sizeof(addr);
+ pj_stun_mapped_addr_attr *attr;
+ char recv_buf[128];
+
+ if (!PJ_FD_ISSET(sock[i], &r))
+ continue;
+
+ len = sizeof(recv_buf);
+ pj_sock_recvfrom( sock[i], recv_buf,
+ &len, 0,
+ (pj_sockaddr_t*)&addr,
+ &addrlen);
+
+ --wait_resp;
+
+ if (len < 1) {
+ mapped_status = PJ_STUN_ERR_TRANSPORT;
+ continue;
+ }
+
+ if (pj_stun_parse_msg(recv_buf, len, &msg) != 0) {
+ PJ_LOG(4,(THIS_FILE,
+ "Error parsing STUN response from %s:%d",
+ LOG_ADDR(addr)));
+ mapped_status = PJ_STUN_ERR_INVALID_MSG;
+ continue;
+ }
+
+ sock_idx = pj_ntohl(msg.hdr->tsx[2]);
+ srv_idx = pj_ntohl(msg.hdr->tsx[3]);
+
+ if (sock_idx<0 || sock_idx>=sock_cnt || srv_idx<0 || srv_idx>=2) {
+ PJ_LOG(4,(THIS_FILE,
+ "Invalid transaction ID from %s:%d",
+ LOG_ADDR(addr)));
+ mapped_status = PJ_STUN_ERR_INVALID_MSG;
+ continue;
+ }
+
+ if (pj_ntohs(msg.hdr->type) != PJ_STUN_BINDING_RESPONSE) {
+ PJ_LOG(4,(THIS_FILE,
+ "Non binding response %d from %s:%d",
+ pj_ntohs(msg.hdr->type), LOG_ADDR(addr)));
+ mapped_status = PJ_STUN_ERR_INVALID_MSG;
+ continue;
+ }
+
+ if (pj_stun_msg_find_attr(&msg, PJ_STUN_ATTR_ERROR_CODE) != NULL) {
+ PJ_LOG(4,(THIS_FILE,
+ "Got STUN error attribute from %s:%d",
+ LOG_ADDR(addr)));
+ mapped_status = PJ_STUN_ERR_INVALID_MSG;
+ continue;
+ }
+
+ attr = (void*)pj_stun_msg_find_attr(&msg, PJ_STUN_ATTR_MAPPED_ADDR);
+ if (!attr) {
+ PJ_LOG(4,(THIS_FILE,
+ "No mapped address in response from %s:%d",
+ LOG_ADDR(addr)));
+ mapped_status = PJ_STUN_ERR_INVALID_MSG;
+ continue;
+ }
+
+ rec[sock_idx].srv[srv_idx].mapped_addr = attr->addr;
+ rec[sock_idx].srv[srv_idx].mapped_port = attr->port;
+ }
+ }
+
+ /* The best scenario is if all requests have been replied.
+ * Then we don't need to go to the next retransmission iteration.
+ */
+ if (wait_resp <= 0)
+ break;
+ }
+
+ for (i=0; i<sock_cnt && mapped_status==0; ++i) {
+ if (rec[i].srv[0].mapped_addr == rec[i].srv[1].mapped_addr &&
+ rec[i].srv[0].mapped_port == rec[i].srv[1].mapped_port)
+ {
+ mapped_addr[i].sin_family = PJ_AF_INET;
+ mapped_addr[i].sin_addr.s_addr = rec[i].srv[0].mapped_addr;
+ mapped_addr[i].sin_port = (pj_uint16_t)rec[i].srv[0].mapped_port;
+
+ if (rec[i].srv[0].mapped_addr == 0 || rec[i].srv[0].mapped_port == 0) {
+ mapped_status = PJ_STUN_ERR_NO_RESPONSE;
+ }
+ } else {
+ mapped_status = PJ_STUN_ERR_SYMETRIC;
+ }
+ }
+
+ pj_pool_release(pool);
+
+ return mapped_status;
+
+on_error:
+ if (pool) pj_pool_release(pool);
+ return -1;
+}
+
+PJ_DEF(const char*) pj_stun_get_err_msg(pj_status_t status)
+{
+ switch (status) {
+ case 0: return "No error";
+ case -1: return "General error";
+ case PJ_STUN_ERR_MEMORY: return "Memory allocation failed";
+ case PJ_STUN_ERR_RESOLVE: return "Invalid IP or unable to resolve STUN server";
+ case PJ_STUN_ERR_TRANSPORT: return "Unable to contact STUN server";
+ case PJ_STUN_ERR_INVALID_MSG: return "Invalid response from STUN server";
+ case PJ_STUN_ERR_NO_RESPONSE: return "No response from STUN server";
+ case PJ_STUN_ERR_SYMETRIC: return "Different mappings are returned from servers";
+ }
+ return "Unknown error";
+}
diff --git a/pjlib-util/src/pjlib-util/symbols.c b/pjlib-util/src/pjlib-util/symbols.c
index 123d401c..00558a04 100644
--- a/pjlib-util/src/pjlib-util/symbols.c
+++ b/pjlib-util/src/pjlib-util/symbols.c
@@ -1,82 +1,82 @@
-/* $Id$ */
-/*
- * Copyright (C) 2003-2006 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 <pjlib.h>
-#include <pjlib-util.h>
-
-/*
- * md5.h
- */
-PJ_EXPORT_SYMBOL(md5_init)
-PJ_EXPORT_SYMBOL(md5_append)
-PJ_EXPORT_SYMBOL(md5_finish)
-
-/*
- * scanner.h
- */
-PJ_EXPORT_SYMBOL(pj_cs_init)
-PJ_EXPORT_SYMBOL(pj_cs_set)
-PJ_EXPORT_SYMBOL(pj_cs_add_range)
-PJ_EXPORT_SYMBOL(pj_cs_add_alpha)
-PJ_EXPORT_SYMBOL(pj_cs_add_num)
-PJ_EXPORT_SYMBOL(pj_cs_add_str)
-PJ_EXPORT_SYMBOL(pj_cs_del_range)
-PJ_EXPORT_SYMBOL(pj_cs_del_str)
-PJ_EXPORT_SYMBOL(pj_cs_invert)
-PJ_EXPORT_SYMBOL(pj_scan_init)
-PJ_EXPORT_SYMBOL(pj_scan_fini)
-PJ_EXPORT_SYMBOL(pj_scan_peek)
-PJ_EXPORT_SYMBOL(pj_scan_peek_n)
-PJ_EXPORT_SYMBOL(pj_scan_peek_until)
-PJ_EXPORT_SYMBOL(pj_scan_get)
-PJ_EXPORT_SYMBOL(pj_scan_get_quote)
-PJ_EXPORT_SYMBOL(pj_scan_get_n)
-PJ_EXPORT_SYMBOL(pj_scan_get_char)
-PJ_EXPORT_SYMBOL(pj_scan_get_newline)
-PJ_EXPORT_SYMBOL(pj_scan_get_until)
-PJ_EXPORT_SYMBOL(pj_scan_get_until_ch)
-PJ_EXPORT_SYMBOL(pj_scan_get_until_chr)
-PJ_EXPORT_SYMBOL(pj_scan_advance_n)
-PJ_EXPORT_SYMBOL(pj_scan_strcmp)
-PJ_EXPORT_SYMBOL(pj_scan_stricmp)
-PJ_EXPORT_SYMBOL(pj_scan_skip_whitespace)
-PJ_EXPORT_SYMBOL(pj_scan_save_state)
-PJ_EXPORT_SYMBOL(pj_scan_restore_state)
-
-/*
- * stun.h
- */
-PJ_EXPORT_SYMBOL(pj_stun_create_bind_req)
-PJ_EXPORT_SYMBOL(pj_stun_parse_msg)
-PJ_EXPORT_SYMBOL(pj_stun_msg_find_attr)
-PJ_EXPORT_SYMBOL(pj_stun_get_mapped_addr)
-PJ_EXPORT_SYMBOL(pj_stun_get_err_msg)
-
-/*
- * xml.h
- */
-PJ_EXPORT_SYMBOL(pj_xml_parse)
-PJ_EXPORT_SYMBOL(pj_xml_print)
-PJ_EXPORT_SYMBOL(pj_xml_add_node)
-PJ_EXPORT_SYMBOL(pj_xml_add_attr)
-PJ_EXPORT_SYMBOL(pj_xml_find_node)
-PJ_EXPORT_SYMBOL(pj_xml_find_next_node)
-PJ_EXPORT_SYMBOL(pj_xml_find_attr)
-PJ_EXPORT_SYMBOL(pj_xml_find)
-
-
+/* $Id$ */
+/*
+ * Copyright (C) 2003-2006 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 <pjlib.h>
+#include <pjlib-util.h>
+
+/*
+ * md5.h
+ */
+PJ_EXPORT_SYMBOL(md5_init)
+PJ_EXPORT_SYMBOL(md5_append)
+PJ_EXPORT_SYMBOL(md5_finish)
+
+/*
+ * scanner.h
+ */
+PJ_EXPORT_SYMBOL(pj_cs_init)
+PJ_EXPORT_SYMBOL(pj_cs_set)
+PJ_EXPORT_SYMBOL(pj_cs_add_range)
+PJ_EXPORT_SYMBOL(pj_cs_add_alpha)
+PJ_EXPORT_SYMBOL(pj_cs_add_num)
+PJ_EXPORT_SYMBOL(pj_cs_add_str)
+PJ_EXPORT_SYMBOL(pj_cs_del_range)
+PJ_EXPORT_SYMBOL(pj_cs_del_str)
+PJ_EXPORT_SYMBOL(pj_cs_invert)
+PJ_EXPORT_SYMBOL(pj_scan_init)
+PJ_EXPORT_SYMBOL(pj_scan_fini)
+PJ_EXPORT_SYMBOL(pj_scan_peek)
+PJ_EXPORT_SYMBOL(pj_scan_peek_n)
+PJ_EXPORT_SYMBOL(pj_scan_peek_until)
+PJ_EXPORT_SYMBOL(pj_scan_get)
+PJ_EXPORT_SYMBOL(pj_scan_get_quote)
+PJ_EXPORT_SYMBOL(pj_scan_get_n)
+PJ_EXPORT_SYMBOL(pj_scan_get_char)
+PJ_EXPORT_SYMBOL(pj_scan_get_newline)
+PJ_EXPORT_SYMBOL(pj_scan_get_until)
+PJ_EXPORT_SYMBOL(pj_scan_get_until_ch)
+PJ_EXPORT_SYMBOL(pj_scan_get_until_chr)
+PJ_EXPORT_SYMBOL(pj_scan_advance_n)
+PJ_EXPORT_SYMBOL(pj_scan_strcmp)
+PJ_EXPORT_SYMBOL(pj_scan_stricmp)
+PJ_EXPORT_SYMBOL(pj_scan_skip_whitespace)
+PJ_EXPORT_SYMBOL(pj_scan_save_state)
+PJ_EXPORT_SYMBOL(pj_scan_restore_state)
+
+/*
+ * stun.h
+ */
+PJ_EXPORT_SYMBOL(pj_stun_create_bind_req)
+PJ_EXPORT_SYMBOL(pj_stun_parse_msg)
+PJ_EXPORT_SYMBOL(pj_stun_msg_find_attr)
+PJ_EXPORT_SYMBOL(pj_stun_get_mapped_addr)
+PJ_EXPORT_SYMBOL(pj_stun_get_err_msg)
+
+/*
+ * xml.h
+ */
+PJ_EXPORT_SYMBOL(pj_xml_parse)
+PJ_EXPORT_SYMBOL(pj_xml_print)
+PJ_EXPORT_SYMBOL(pj_xml_add_node)
+PJ_EXPORT_SYMBOL(pj_xml_add_attr)
+PJ_EXPORT_SYMBOL(pj_xml_find_node)
+PJ_EXPORT_SYMBOL(pj_xml_find_next_node)
+PJ_EXPORT_SYMBOL(pj_xml_find_attr)
+PJ_EXPORT_SYMBOL(pj_xml_find)
+
+
diff --git a/pjlib-util/src/pjlib-util/xml.c b/pjlib-util/src/pjlib-util/xml.c
index 3be7bee4..140c64c7 100644
--- a/pjlib-util/src/pjlib-util/xml.c
+++ b/pjlib-util/src/pjlib-util/xml.c
@@ -1,396 +1,396 @@
-/* $Id$ */
-/*
- * Copyright (C) 2003-2006 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 <pjlib-util/xml.h>
-#include <pjlib-util/scanner.h>
-#include <pj/except.h>
-#include <pj/pool.h>
-#include <pj/string.h>
-#include <pj/log.h>
-#include <pj/os.h>
-
-#define EX_SYNTAX_ERROR 12
-#define THIS_FILE "xml.c"
-
-static void on_syntax_error(struct pj_scanner *scanner)
-{
- PJ_UNUSED_ARG(scanner);
- PJ_THROW(EX_SYNTAX_ERROR);
-}
-
-static pj_xml_node *alloc_node( pj_pool_t *pool )
-{
- pj_xml_node *node;
-
- node = pj_pool_calloc(pool, 1, sizeof(pj_xml_node));
- pj_list_init( &node->attr_head );
- pj_list_init( &node->node_head );
-
- return node;
-}
-
-static pj_xml_attr *alloc_attr( pj_pool_t *pool )
-{
- return pj_pool_calloc(pool, 1, sizeof(pj_xml_attr));
-}
-
-/* This is a recursive function! */
-static pj_xml_node *xml_parse_node( pj_pool_t *pool, pj_scanner *scanner)
-{
- pj_xml_node *node;
- pj_str_t end_name;
-
- PJ_CHECK_STACK();
-
- if (*scanner->curptr != '<')
- on_syntax_error(scanner);
-
- /* Handle Processing Instructino (PI) construct (i.e. "<?") */
- if (*scanner->curptr == '<' && *(scanner->curptr+1) == '?') {
- pj_scan_advance_n(scanner, 2, PJ_FALSE);
- for (;;) {
- pj_str_t dummy;
- pj_scan_get_until_ch(scanner, '?', &dummy);
- if (*scanner->curptr=='?' && *(scanner->curptr+1)=='>') {
- pj_scan_advance_n(scanner, 2, PJ_TRUE);
- break;
- } else {
- pj_scan_advance_n(scanner, 1, PJ_FALSE);
- }
- }
- return xml_parse_node(pool, scanner);
- }
-
- /* Handle comments construct (i.e. "<!--") */
- if (pj_scan_strcmp(scanner, "<!--", 4) == 0) {
- pj_scan_advance_n(scanner, 4, PJ_FALSE);
- for (;;) {
- pj_str_t dummy;
- pj_scan_get_until_ch(scanner, '-', &dummy);
- if (pj_scan_strcmp(scanner, "-->", 3) == 0) {
- pj_scan_advance_n(scanner, 3, PJ_TRUE);
- break;
- } else {
- pj_scan_advance_n(scanner, 1, PJ_FALSE);
- }
- }
- return xml_parse_node(pool, scanner);
- }
-
- /* Alloc node. */
- node = alloc_node(pool);
-
- /* Get '<' */
- pj_scan_get_char(scanner);
-
- /* Get node name. */
- pj_scan_get_until_chr( scanner, " />\t", &node->name);
-
- /* Get attributes. */
- while (*scanner->curptr != '>' && *scanner->curptr != '/') {
- pj_xml_attr *attr = alloc_attr(pool);
-
- pj_scan_get_until_chr( scanner, "=> \t", &attr->name);
- if (*scanner->curptr == '=') {
- pj_scan_get_char( scanner );
- pj_scan_get_quote(scanner, '"', '"', &attr->value);
- /* remove quote characters */
- ++attr->value.ptr;
- attr->value.slen -= 2;
- }
-
- pj_list_insert_before( &node->attr_head, attr );
- }
-
- if (*scanner->curptr == '/') {
- pj_scan_get_char(scanner);
- if (pj_scan_get_char(scanner) != '>')
- on_syntax_error(scanner);
- return node;
- }
-
- /* Enclosing bracket. */
- if (pj_scan_get_char(scanner) != '>')
- on_syntax_error(scanner);
-
- /* Sub nodes. */
- while (*scanner->curptr == '<' && *(scanner->curptr+1) != '/') {
- pj_xml_node *sub_node = xml_parse_node(pool, scanner);
- pj_list_insert_before( &node->node_head, sub_node );
- }
-
- /* Content. */
- if (!pj_scan_is_eof(scanner) && *scanner->curptr != '<') {
- pj_scan_get_until_ch(scanner, '<', &node->content);
- }
-
- /* Enclosing node. */
- if (pj_scan_get_char(scanner) != '<' || pj_scan_get_char(scanner) != '/')
- on_syntax_error(scanner);
-
- pj_scan_get_until_chr(scanner, " \t>", &end_name);
-
- /* Compare name. */
- if (pj_stricmp(&node->name, &end_name) != 0)
- on_syntax_error(scanner);
-
- /* Enclosing '>' */
- if (pj_scan_get_char(scanner) != '>')
- on_syntax_error(scanner);
-
- return node;
-}
-
-PJ_DEF(pj_xml_node*) pj_xml_parse( pj_pool_t *pool, char *msg, pj_size_t len)
-{
- pj_xml_node *node = NULL;
- pj_scanner scanner;
- PJ_USE_EXCEPTION;
-
- if (!msg || !len || !pool)
- return NULL;
-
- pj_scan_init( &scanner, msg, len,
- PJ_SCAN_AUTOSKIP_WS|PJ_SCAN_AUTOSKIP_NEWLINE,
- &on_syntax_error);
- PJ_TRY {
- node = xml_parse_node(pool, &scanner);
- }
- PJ_DEFAULT {
- PJ_LOG(4,(THIS_FILE, "Syntax error parsing XML in line %d column %d",
- scanner.line, scanner.col));
- }
- PJ_END;
- pj_scan_fini( &scanner );
- return node;
-}
-
-/* This is a recursive function. */
-static int xml_print_node( const pj_xml_node *node, int indent,
- char *buf, pj_size_t len )
-{
- int i;
- char *p = buf;
- pj_xml_attr *attr;
- pj_xml_node *sub_node;
-
-#define SIZE_LEFT() ((int)(len - (p-buf)))
-
- PJ_CHECK_STACK();
-
- /* Print name. */
- if (SIZE_LEFT() < node->name.slen + indent + 5)
- return -1;
- for (i=0; i<indent; ++i)
- *p++ = ' ';
- *p++ = '<';
- pj_memcpy(p, node->name.ptr, node->name.slen);
- p += node->name.slen;
-
- /* Print attributes. */
- attr = node->attr_head.next;
- while (attr != &node->attr_head) {
-
- if (SIZE_LEFT() < attr->name.slen + attr->value.slen + 4)
- return -1;
-
- *p++ = ' ';
-
- /* Attribute name. */
- pj_memcpy(p, attr->name.ptr, attr->name.slen);
- p += attr->name.slen;
-
- /* Attribute value. */
- if (attr->value.slen) {
- *p++ = '=';
- *p++ = '"';
- pj_memcpy(p, attr->value.ptr, attr->value.slen);
- p += attr->value.slen;
- *p++ = '"';
- }
-
- attr = attr->next;
- }
-
- /* Check for empty node. */
- if (node->content.slen==0 &&
- node->node_head.next==(pj_xml_node*)&node->node_head)
- {
- *p++ = ' ';
- *p++ = '/';
- *p++ = '>';
- return p-buf;
- }
-
- /* Enclosing '>' */
- if (SIZE_LEFT() < 1) return -1;
- *p++ = '>';
-
- /* Print sub nodes. */
- sub_node = node->node_head.next;
- while (sub_node != (pj_xml_node*)&node->node_head) {
- int printed;
-
- if (SIZE_LEFT() < indent + 3)
- return -1;
- //*p++ = '\r';
- *p++ = '\n';
-
- printed = xml_print_node(sub_node, indent + 1, p, SIZE_LEFT());
- if (printed < 0)
- return -1;
-
- p += printed;
- sub_node = sub_node->next;
- }
-
- /* Content. */
- if (node->content.slen) {
- if (SIZE_LEFT() < node->content.slen) return -1;
- pj_memcpy(p, node->content.ptr, node->content.slen);
- p += node->content.slen;
- }
-
- /* Enclosing node. */
- if (node->node_head.next != (pj_xml_node*)&node->node_head) {
- if (SIZE_LEFT() < node->name.slen + 5 + indent)
- return -1;
- //*p++ = '\r';
- *p++ = '\n';
- for (i=0; i<indent; ++i)
- *p++ = ' ';
- } else {
- if (SIZE_LEFT() < node->name.slen + 3)
- return -1;
- }
- *p++ = '<';
- *p++ = '/';
- pj_memcpy(p, node->name.ptr, node->name.slen);
- p += node->name.slen;
- *p++ = '>';
-
-#undef SIZE_LEFT
-
- return p - buf;
-}
-
-PJ_DEF(int) pj_xml_print(const pj_xml_node *node, char *buf, pj_size_t len,
- pj_bool_t include_prolog)
-{
- int prolog_len = 0;
- int printed;
-
- if (!node || !buf || !len)
- return 0;
-
- if (include_prolog) {
- pj_str_t prolog = {"<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n", 39};
- if ((int)len < prolog.slen)
- return -1;
- pj_memcpy(buf, prolog.ptr, prolog.slen);
- prolog_len = prolog.slen;
- }
-
- printed = xml_print_node(node, 0, buf+prolog_len, len-prolog_len) + prolog_len;
- if (printed > 0 && len-printed >= 1) {
- buf[printed++] = '\n';
- }
- return printed;
-}
-
-
-PJ_DEF(void) pj_xml_add_node( pj_xml_node *parent, pj_xml_node *node )
-{
- pj_list_insert_before(&parent->node_head, node);
-}
-
-PJ_DEF(void) pj_xml_add_attr( pj_xml_node *node, pj_xml_attr *attr )
-{
- pj_list_insert_before(&node->attr_head, attr);
-}
-
-PJ_DEF(pj_xml_node*) pj_xml_find_node(pj_xml_node *parent, const pj_str_t *name)
-{
- pj_xml_node *node = parent->node_head.next;
-
- PJ_CHECK_STACK();
-
- while (node != (void*)&parent->node_head) {
- if (pj_stricmp(&node->name, name) == 0)
- return node;
- node = node->next;
- }
- return NULL;
-}
-
-
-PJ_DEF(pj_xml_node*) pj_xml_find_next_node( pj_xml_node *parent, pj_xml_node *node,
- const pj_str_t *name)
-{
- PJ_CHECK_STACK();
-
- node = node->next;
- while (node != (void*)&parent->node_head) {
- if (pj_stricmp(&node->name, name) == 0)
- return node;
- node = node->next;
- }
- return NULL;
-}
-
-
-PJ_DEF(pj_xml_attr*) pj_xml_find_attr( pj_xml_node *node, const pj_str_t *name,
- const pj_str_t *value)
-{
- pj_xml_attr *attr = node->attr_head.next;
- while (attr != (void*)&node->attr_head) {
- if (pj_stricmp(&attr->name, name)==0) {
- if (value) {
- if (pj_stricmp(&attr->value, value)==0)
- return attr;
- } else {
- return attr;
- }
- }
- attr = attr->next;
- }
- return NULL;
-}
-
-
-
-PJ_DEF(pj_xml_node*) pj_xml_find( pj_xml_node *parent, const pj_str_t *name,
- const void *data,
- pj_bool_t (*match)(pj_xml_node *, const void*))
-{
- pj_xml_node *head = (void*)&parent->node_head, *node = head->next;
-
- while (node != (void*)head) {
- if (name && pj_stricmp(&node->name, name)==0) {
- if (match) {
- if (match(node, data))
- return node;
- } else {
- return node;
- }
- }
- node = node->next;
- }
- return NULL;
-}
-
+/* $Id$ */
+/*
+ * Copyright (C) 2003-2006 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 <pjlib-util/xml.h>
+#include <pjlib-util/scanner.h>
+#include <pj/except.h>
+#include <pj/pool.h>
+#include <pj/string.h>
+#include <pj/log.h>
+#include <pj/os.h>
+
+#define EX_SYNTAX_ERROR 12
+#define THIS_FILE "xml.c"
+
+static void on_syntax_error(struct pj_scanner *scanner)
+{
+ PJ_UNUSED_ARG(scanner);
+ PJ_THROW(EX_SYNTAX_ERROR);
+}
+
+static pj_xml_node *alloc_node( pj_pool_t *pool )
+{
+ pj_xml_node *node;
+
+ node = pj_pool_calloc(pool, 1, sizeof(pj_xml_node));
+ pj_list_init( &node->attr_head );
+ pj_list_init( &node->node_head );
+
+ return node;
+}
+
+static pj_xml_attr *alloc_attr( pj_pool_t *pool )
+{
+ return pj_pool_calloc(pool, 1, sizeof(pj_xml_attr));
+}
+
+/* This is a recursive function! */
+static pj_xml_node *xml_parse_node( pj_pool_t *pool, pj_scanner *scanner)
+{
+ pj_xml_node *node;
+ pj_str_t end_name;
+
+ PJ_CHECK_STACK();
+
+ if (*scanner->curptr != '<')
+ on_syntax_error(scanner);
+
+ /* Handle Processing Instructino (PI) construct (i.e. "<?") */
+ if (*scanner->curptr == '<' && *(scanner->curptr+1) == '?') {
+ pj_scan_advance_n(scanner, 2, PJ_FALSE);
+ for (;;) {
+ pj_str_t dummy;
+ pj_scan_get_until_ch(scanner, '?', &dummy);
+ if (*scanner->curptr=='?' && *(scanner->curptr+1)=='>') {
+ pj_scan_advance_n(scanner, 2, PJ_TRUE);
+ break;
+ } else {
+ pj_scan_advance_n(scanner, 1, PJ_FALSE);
+ }
+ }
+ return xml_parse_node(pool, scanner);
+ }
+
+ /* Handle comments construct (i.e. "<!--") */
+ if (pj_scan_strcmp(scanner, "<!--", 4) == 0) {
+ pj_scan_advance_n(scanner, 4, PJ_FALSE);
+ for (;;) {
+ pj_str_t dummy;
+ pj_scan_get_until_ch(scanner, '-', &dummy);
+ if (pj_scan_strcmp(scanner, "-->", 3) == 0) {
+ pj_scan_advance_n(scanner, 3, PJ_TRUE);
+ break;
+ } else {
+ pj_scan_advance_n(scanner, 1, PJ_FALSE);
+ }
+ }
+ return xml_parse_node(pool, scanner);
+ }
+
+ /* Alloc node. */
+ node = alloc_node(pool);
+
+ /* Get '<' */
+ pj_scan_get_char(scanner);
+
+ /* Get node name. */
+ pj_scan_get_until_chr( scanner, " />\t", &node->name);
+
+ /* Get attributes. */
+ while (*scanner->curptr != '>' && *scanner->curptr != '/') {
+ pj_xml_attr *attr = alloc_attr(pool);
+
+ pj_scan_get_until_chr( scanner, "=> \t", &attr->name);
+ if (*scanner->curptr == '=') {
+ pj_scan_get_char( scanner );
+ pj_scan_get_quote(scanner, '"', '"', &attr->value);
+ /* remove quote characters */
+ ++attr->value.ptr;
+ attr->value.slen -= 2;
+ }
+
+ pj_list_insert_before( &node->attr_head, attr );
+ }
+
+ if (*scanner->curptr == '/') {
+ pj_scan_get_char(scanner);
+ if (pj_scan_get_char(scanner) != '>')
+ on_syntax_error(scanner);
+ return node;
+ }
+
+ /* Enclosing bracket. */
+ if (pj_scan_get_char(scanner) != '>')
+ on_syntax_error(scanner);
+
+ /* Sub nodes. */
+ while (*scanner->curptr == '<' && *(scanner->curptr+1) != '/') {
+ pj_xml_node *sub_node = xml_parse_node(pool, scanner);
+ pj_list_insert_before( &node->node_head, sub_node );
+ }
+
+ /* Content. */
+ if (!pj_scan_is_eof(scanner) && *scanner->curptr != '<') {
+ pj_scan_get_until_ch(scanner, '<', &node->content);
+ }
+
+ /* Enclosing node. */
+ if (pj_scan_get_char(scanner) != '<' || pj_scan_get_char(scanner) != '/')
+ on_syntax_error(scanner);
+
+ pj_scan_get_until_chr(scanner, " \t>", &end_name);
+
+ /* Compare name. */
+ if (pj_stricmp(&node->name, &end_name) != 0)
+ on_syntax_error(scanner);
+
+ /* Enclosing '>' */
+ if (pj_scan_get_char(scanner) != '>')
+ on_syntax_error(scanner);
+
+ return node;
+}
+
+PJ_DEF(pj_xml_node*) pj_xml_parse( pj_pool_t *pool, char *msg, pj_size_t len)
+{
+ pj_xml_node *node = NULL;
+ pj_scanner scanner;
+ PJ_USE_EXCEPTION;
+
+ if (!msg || !len || !pool)
+ return NULL;
+
+ pj_scan_init( &scanner, msg, len,
+ PJ_SCAN_AUTOSKIP_WS|PJ_SCAN_AUTOSKIP_NEWLINE,
+ &on_syntax_error);
+ PJ_TRY {
+ node = xml_parse_node(pool, &scanner);
+ }
+ PJ_DEFAULT {
+ PJ_LOG(4,(THIS_FILE, "Syntax error parsing XML in line %d column %d",
+ scanner.line, scanner.col));
+ }
+ PJ_END;
+ pj_scan_fini( &scanner );
+ return node;
+}
+
+/* This is a recursive function. */
+static int xml_print_node( const pj_xml_node *node, int indent,
+ char *buf, pj_size_t len )
+{
+ int i;
+ char *p = buf;
+ pj_xml_attr *attr;
+ pj_xml_node *sub_node;
+
+#define SIZE_LEFT() ((int)(len - (p-buf)))
+
+ PJ_CHECK_STACK();
+
+ /* Print name. */
+ if (SIZE_LEFT() < node->name.slen + indent + 5)
+ return -1;
+ for (i=0; i<indent; ++i)
+ *p++ = ' ';
+ *p++ = '<';
+ pj_memcpy(p, node->name.ptr, node->name.slen);
+ p += node->name.slen;
+
+ /* Print attributes. */
+ attr = node->attr_head.next;
+ while (attr != &node->attr_head) {
+
+ if (SIZE_LEFT() < attr->name.slen + attr->value.slen + 4)
+ return -1;
+
+ *p++ = ' ';
+
+ /* Attribute name. */
+ pj_memcpy(p, attr->name.ptr, attr->name.slen);
+ p += attr->name.slen;
+
+ /* Attribute value. */
+ if (attr->value.slen) {
+ *p++ = '=';
+ *p++ = '"';
+ pj_memcpy(p, attr->value.ptr, attr->value.slen);
+ p += attr->value.slen;
+ *p++ = '"';
+ }
+
+ attr = attr->next;
+ }
+
+ /* Check for empty node. */
+ if (node->content.slen==0 &&
+ node->node_head.next==(pj_xml_node*)&node->node_head)
+ {
+ *p++ = ' ';
+ *p++ = '/';
+ *p++ = '>';
+ return p-buf;
+ }
+
+ /* Enclosing '>' */
+ if (SIZE_LEFT() < 1) return -1;
+ *p++ = '>';
+
+ /* Print sub nodes. */
+ sub_node = node->node_head.next;
+ while (sub_node != (pj_xml_node*)&node->node_head) {
+ int printed;
+
+ if (SIZE_LEFT() < indent + 3)
+ return -1;
+ //*p++ = '\r';
+ *p++ = '\n';
+
+ printed = xml_print_node(sub_node, indent + 1, p, SIZE_LEFT());
+ if (printed < 0)
+ return -1;
+
+ p += printed;
+ sub_node = sub_node->next;
+ }
+
+ /* Content. */
+ if (node->content.slen) {
+ if (SIZE_LEFT() < node->content.slen) return -1;
+ pj_memcpy(p, node->content.ptr, node->content.slen);
+ p += node->content.slen;
+ }
+
+ /* Enclosing node. */
+ if (node->node_head.next != (pj_xml_node*)&node->node_head) {
+ if (SIZE_LEFT() < node->name.slen + 5 + indent)
+ return -1;
+ //*p++ = '\r';
+ *p++ = '\n';
+ for (i=0; i<indent; ++i)
+ *p++ = ' ';
+ } else {
+ if (SIZE_LEFT() < node->name.slen + 3)
+ return -1;
+ }
+ *p++ = '<';
+ *p++ = '/';
+ pj_memcpy(p, node->name.ptr, node->name.slen);
+ p += node->name.slen;
+ *p++ = '>';
+
+#undef SIZE_LEFT
+
+ return p - buf;
+}
+
+PJ_DEF(int) pj_xml_print(const pj_xml_node *node, char *buf, pj_size_t len,
+ pj_bool_t include_prolog)
+{
+ int prolog_len = 0;
+ int printed;
+
+ if (!node || !buf || !len)
+ return 0;
+
+ if (include_prolog) {
+ pj_str_t prolog = {"<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n", 39};
+ if ((int)len < prolog.slen)
+ return -1;
+ pj_memcpy(buf, prolog.ptr, prolog.slen);
+ prolog_len = prolog.slen;
+ }
+
+ printed = xml_print_node(node, 0, buf+prolog_len, len-prolog_len) + prolog_len;
+ if (printed > 0 && len-printed >= 1) {
+ buf[printed++] = '\n';
+ }
+ return printed;
+}
+
+
+PJ_DEF(void) pj_xml_add_node( pj_xml_node *parent, pj_xml_node *node )
+{
+ pj_list_insert_before(&parent->node_head, node);
+}
+
+PJ_DEF(void) pj_xml_add_attr( pj_xml_node *node, pj_xml_attr *attr )
+{
+ pj_list_insert_before(&node->attr_head, attr);
+}
+
+PJ_DEF(pj_xml_node*) pj_xml_find_node(pj_xml_node *parent, const pj_str_t *name)
+{
+ pj_xml_node *node = parent->node_head.next;
+
+ PJ_CHECK_STACK();
+
+ while (node != (void*)&parent->node_head) {
+ if (pj_stricmp(&node->name, name) == 0)
+ return node;
+ node = node->next;
+ }
+ return NULL;
+}
+
+
+PJ_DEF(pj_xml_node*) pj_xml_find_next_node( pj_xml_node *parent, pj_xml_node *node,
+ const pj_str_t *name)
+{
+ PJ_CHECK_STACK();
+
+ node = node->next;
+ while (node != (void*)&parent->node_head) {
+ if (pj_stricmp(&node->name, name) == 0)
+ return node;
+ node = node->next;
+ }
+ return NULL;
+}
+
+
+PJ_DEF(pj_xml_attr*) pj_xml_find_attr( pj_xml_node *node, const pj_str_t *name,
+ const pj_str_t *value)
+{
+ pj_xml_attr *attr = node->attr_head.next;
+ while (attr != (void*)&node->attr_head) {
+ if (pj_stricmp(&attr->name, name)==0) {
+ if (value) {
+ if (pj_stricmp(&attr->value, value)==0)
+ return attr;
+ } else {
+ return attr;
+ }
+ }
+ attr = attr->next;
+ }
+ return NULL;
+}
+
+
+
+PJ_DEF(pj_xml_node*) pj_xml_find( pj_xml_node *parent, const pj_str_t *name,
+ const void *data,
+ pj_bool_t (*match)(pj_xml_node *, const void*))
+{
+ pj_xml_node *head = (void*)&parent->node_head, *node = head->next;
+
+ while (node != (void*)head) {
+ if (name && pj_stricmp(&node->name, name)==0) {
+ if (match) {
+ if (match(node, data))
+ return node;
+ } else {
+ return node;
+ }
+ }
+ node = node->next;
+ }
+ return NULL;
+}
+
diff --git a/pjlib/include/pj++/file.hpp b/pjlib/include/pj++/file.hpp
index 3fed646e..9efe53f5 100644
--- a/pjlib/include/pj++/file.hpp
+++ b/pjlib/include/pj++/file.hpp
@@ -1,187 +1,187 @@
-/* $Id$ */
-/*
- * Copyright (C)2003-2006 Benny Prijono <benny@prijono.org>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
-#ifndef __PJPP_FILE_HPP__
-#define __PJPP_FILE_HPP__
-
-#include <pj/file_io.h>
-#include <pj/file_access.h>
-#include <pj++/types.hpp>
-#include <pj++/pool.hpp>
-
-//
-// File API.
-//
-class Pj_File_API
-{
-public:
- //
- // Check file existance.
- //
- static bool file_exists(const char *filename)
- {
- return pj_file_exists(filename) != 0;
- }
-
- //
- // Get file size.
- //
- static pj_off_t file_size(const char *filename)
- {
- return pj_file_size(filename);
- }
-
- //
- // Delete file.
- //
- static pj_status_t file_delete(const char *filename)
- {
- return pj_file_delete(filename);
- }
-
- //
- // Move/rename file.
- //
- static pj_status_t file_move(const char *oldname, const char *newname)
- {
- return pj_file_move(oldname, newname);
- }
-
- //
- // Get stat.
- //
- static pj_status_t file_stat(const char *filename, pj_file_stat *buf)
- {
- return pj_file_getstat(filename, buf);
- }
-};
-
-
-//
-// File.
-//
-class Pj_File : public Pj_Object
-{
-public:
- //
- // Offset type to be used in setpos.
- //
- enum Offset_Type
- {
- SEEK_SET = PJ_SEEK_SET,
- SEEK_CUR = PJ_SEEK_CUR,
- SEEK_END = PJ_SEEK_END,
- };
-
- //
- // Default constructor.
- //
- Pj_File()
- : hnd_(0)
- {
- }
-
- //
- // Construct and open a file.
- //
- Pj_File(Pj_Pool *pool, const char *filename,
- unsigned access = PJ_O_RDONLY)
- : hnd_(NULL)
- {
- open(pool, filename, access);
- }
-
- //
- // Destructor closes the file.
- //
- ~Pj_File()
- {
- close();
- }
-
- //
- // Open a file.
- //
- pj_status_t open(Pj_Pool *pool, const char *filename,
- unsigned access = PJ_O_RDONLY )
- {
- close();
- return pj_file_open(pool->pool_(), filename, access, &hnd_);
- }
-
- //
- // Close a file.
- //
- void close()
- {
- if (hnd_ != 0) {
- pj_file_close(hnd_);
- hnd_ = 0;
- }
- }
-
- //
- // Write data.
- //
- pj_ssize_t write(const void *buff, pj_size_t size)
- {
- pj_ssize_t bytes = size;
- if (pj_file_write(hnd_, buff, &bytes) != PJ_SUCCESS)
- return -1;
- return bytes;
- }
-
- //
- // Read data.
- //
- pj_ssize_t read(void *buf, pj_size_t size)
- {
- pj_ssize_t bytes = size;
- if (pj_file_read(hnd_, buf, &bytes) != PJ_SUCCESS)
- return -1;
- return bytes;
- }
-
- //
- // Set file position.
- //
- pj_status_t setpos(pj_off_t offset, Offset_Type whence)
- {
- return pj_file_setpos(hnd_, offset,
- (enum pj_file_seek_type)whence);
- }
-
- //
- // Get file position.
- //
- pj_off_t getpos()
- {
- pj_off_t pos;
- if (pj_file_getpos(hnd_, &pos) != PJ_SUCCESS)
- return -1;
- return pos;
- }
-
-private:
- pj_oshandle_t hnd_;
-};
-
-
-
-#endif /* __PJPP_FILE_HPP__ */
-
+/* $Id$ */
+/*
+ * Copyright (C)2003-2006 Benny Prijono <benny@prijono.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#ifndef __PJPP_FILE_HPP__
+#define __PJPP_FILE_HPP__
+
+#include <pj/file_io.h>
+#include <pj/file_access.h>
+#include <pj++/types.hpp>
+#include <pj++/pool.hpp>
+
+//
+// File API.
+//
+class Pj_File_API
+{
+public:
+ //
+ // Check file existance.
+ //
+ static bool file_exists(const char *filename)
+ {
+ return pj_file_exists(filename) != 0;
+ }
+
+ //
+ // Get file size.
+ //
+ static pj_off_t file_size(const char *filename)
+ {
+ return pj_file_size(filename);
+ }
+
+ //
+ // Delete file.
+ //
+ static pj_status_t file_delete(const char *filename)
+ {
+ return pj_file_delete(filename);
+ }
+
+ //
+ // Move/rename file.
+ //
+ static pj_status_t file_move(const char *oldname, const char *newname)
+ {
+ return pj_file_move(oldname, newname);
+ }
+
+ //
+ // Get stat.
+ //
+ static pj_status_t file_stat(const char *filename, pj_file_stat *buf)
+ {
+ return pj_file_getstat(filename, buf);
+ }
+};
+
+
+//
+// File.
+//
+class Pj_File : public Pj_Object
+{
+public:
+ //
+ // Offset type to be used in setpos.
+ //
+ enum Offset_Type
+ {
+ SEEK_SET = PJ_SEEK_SET,
+ SEEK_CUR = PJ_SEEK_CUR,
+ SEEK_END = PJ_SEEK_END,
+ };
+
+ //
+ // Default constructor.
+ //
+ Pj_File()
+ : hnd_(0)
+ {
+ }
+
+ //
+ // Construct and open a file.
+ //
+ Pj_File(Pj_Pool *pool, const char *filename,
+ unsigned access = PJ_O_RDONLY)
+ : hnd_(NULL)
+ {
+ open(pool, filename, access);
+ }
+
+ //
+ // Destructor closes the file.
+ //
+ ~Pj_File()
+ {
+ close();
+ }
+
+ //
+ // Open a file.
+ //
+ pj_status_t open(Pj_Pool *pool, const char *filename,
+ unsigned access = PJ_O_RDONLY )
+ {
+ close();
+ return pj_file_open(pool->pool_(), filename, access, &hnd_);
+ }
+
+ //
+ // Close a file.
+ //
+ void close()
+ {
+ if (hnd_ != 0) {
+ pj_file_close(hnd_);
+ hnd_ = 0;
+ }
+ }
+
+ //
+ // Write data.
+ //
+ pj_ssize_t write(const void *buff, pj_size_t size)
+ {
+ pj_ssize_t bytes = size;
+ if (pj_file_write(hnd_, buff, &bytes) != PJ_SUCCESS)
+ return -1;
+ return bytes;
+ }
+
+ //
+ // Read data.
+ //
+ pj_ssize_t read(void *buf, pj_size_t size)
+ {
+ pj_ssize_t bytes = size;
+ if (pj_file_read(hnd_, buf, &bytes) != PJ_SUCCESS)
+ return -1;
+ return bytes;
+ }
+
+ //
+ // Set file position.
+ //
+ pj_status_t setpos(pj_off_t offset, Offset_Type whence)
+ {
+ return pj_file_setpos(hnd_, offset,
+ (enum pj_file_seek_type)whence);
+ }
+
+ //
+ // Get file position.
+ //
+ pj_off_t getpos()
+ {
+ pj_off_t pos;
+ if (pj_file_getpos(hnd_, &pos) != PJ_SUCCESS)
+ return -1;
+ return pos;
+ }
+
+private:
+ pj_oshandle_t hnd_;
+};
+
+
+
+#endif /* __PJPP_FILE_HPP__ */
+
diff --git a/pjlib/include/pj++/hash.hpp b/pjlib/include/pj++/hash.hpp
index 5d85a6e2..1be7f45e 100644
--- a/pjlib/include/pj++/hash.hpp
+++ b/pjlib/include/pj++/hash.hpp
@@ -1,155 +1,155 @@
-/* $Id$ */
-/*
- * Copyright (C)2003-2006 Benny Prijono <benny@prijono.org>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
-#ifndef __PJPP_HASH_HPP__
-#define __PJPP_HASH_HPP__
-
-#include <pj++/types.hpp>
-#include <pj++/pool.hpp>
-#include <pj/hash.h>
-
-//
-// Hash table.
-//
-class Pj_Hash_Table : public Pj_Object
-{
-public:
- //
- // Hash table iterator.
- //
- class iterator
- {
- public:
- iterator()
- {
- }
- explicit iterator(pj_hash_table_t *h, pj_hash_iterator_t *i)
- : ht_(h), it_(i)
- {
- }
- iterator(const iterator &rhs)
- : ht_(rhs.ht_), it_(rhs.it_)
- {
- }
- void operator++()
- {
- it_ = pj_hash_next(ht_, it_);
- }
- bool operator==(const iterator &rhs)
- {
- return ht_ == rhs.ht_ && it_ == rhs.it_;
- }
- iterator & operator=(const iterator &rhs)
- {
- ht_=rhs.ht_; it_=rhs.it_;
- return *this;
- }
- private:
- pj_hash_table_t *ht_;
- pj_hash_iterator_t it_val_;
- pj_hash_iterator_t *it_;
-
- friend class Pj_Hash_Table;
- };
-
- //
- // Construct hash table.
- //
- Pj_Hash_Table(Pj_Pool *pool, unsigned size)
- {
- table_ = pj_hash_create(pool->pool_(), size);
- }
-
- //
- // Destroy hash table.
- //
- ~Pj_Hash_Table()
- {
- }
-
- //
- // Calculate hash value.
- //
- static pj_uint32_t calc( pj_uint32_t initial_hval,
- const void *key,
- unsigned keylen = PJ_HASH_KEY_STRING)
- {
- return pj_hash_calc(initial_hval, key, keylen);
- }
-
- //
- // Return pjlib compatible hash table object.
- //
- pj_hash_table_t *pj_hash_table_t_()
- {
- return table_;
- }
-
- //
- // Get the value associated with the specified key.
- //
- void *get(const void *key, unsigned keylen = PJ_HASH_KEY_STRING)
- {
- return pj_hash_get(table_, key, keylen);
- }
-
- //
- // Associate a value with a key.
- // Set the value to NULL to delete the key from the hash table.
- //
- void set(Pj_Pool *pool,
- const void *key,
- void *value,
- unsigned keylen = PJ_HASH_KEY_STRING)
- {
- pj_hash_set(pool->pool_(), table_, key, keylen, value);
- }
-
- //
- // Get number of items in the hash table.
- //
- unsigned count()
- {
- return pj_hash_count(table_);
- }
-
- //
- // Iterate hash table.
- //
- iterator begin()
- {
- iterator it(table_, NULL);
- it.it_ = pj_hash_first(table_, &it.it_val_);
- return it;
- }
-
- //
- // End of items.
- //
- iterator end()
- {
- return iterator(table_, NULL);
- }
-
-private:
- pj_hash_table_t *table_;
-};
-
-
-#endif /* __PJPP_HASH_HPP__ */
-
+/* $Id$ */
+/*
+ * Copyright (C)2003-2006 Benny Prijono <benny@prijono.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#ifndef __PJPP_HASH_HPP__
+#define __PJPP_HASH_HPP__
+
+#include <pj++/types.hpp>
+#include <pj++/pool.hpp>
+#include <pj/hash.h>
+
+//
+// Hash table.
+//
+class Pj_Hash_Table : public Pj_Object
+{
+public:
+ //
+ // Hash table iterator.
+ //
+ class iterator
+ {
+ public:
+ iterator()
+ {
+ }
+ explicit iterator(pj_hash_table_t *h, pj_hash_iterator_t *i)
+ : ht_(h), it_(i)
+ {
+ }
+ iterator(const iterator &rhs)
+ : ht_(rhs.ht_), it_(rhs.it_)
+ {
+ }
+ void operator++()
+ {
+ it_ = pj_hash_next(ht_, it_);
+ }
+ bool operator==(const iterator &rhs)
+ {
+ return ht_ == rhs.ht_ && it_ == rhs.it_;
+ }
+ iterator & operator=(const iterator &rhs)
+ {
+ ht_=rhs.ht_; it_=rhs.it_;
+ return *this;
+ }
+ private:
+ pj_hash_table_t *ht_;
+ pj_hash_iterator_t it_val_;
+ pj_hash_iterator_t *it_;
+
+ friend class Pj_Hash_Table;
+ };
+
+ //
+ // Construct hash table.
+ //
+ Pj_Hash_Table(Pj_Pool *pool, unsigned size)
+ {
+ table_ = pj_hash_create(pool->pool_(), size);
+ }
+
+ //
+ // Destroy hash table.
+ //
+ ~Pj_Hash_Table()
+ {
+ }
+
+ //
+ // Calculate hash value.
+ //
+ static pj_uint32_t calc( pj_uint32_t initial_hval,
+ const void *key,
+ unsigned keylen = PJ_HASH_KEY_STRING)
+ {
+ return pj_hash_calc(initial_hval, key, keylen);
+ }
+
+ //
+ // Return pjlib compatible hash table object.
+ //
+ pj_hash_table_t *pj_hash_table_t_()
+ {
+ return table_;
+ }
+
+ //
+ // Get the value associated with the specified key.
+ //
+ void *get(const void *key, unsigned keylen = PJ_HASH_KEY_STRING)
+ {
+ return pj_hash_get(table_, key, keylen);
+ }
+
+ //
+ // Associate a value with a key.
+ // Set the value to NULL to delete the key from the hash table.
+ //
+ void set(Pj_Pool *pool,
+ const void *key,
+ void *value,
+ unsigned keylen = PJ_HASH_KEY_STRING)
+ {
+ pj_hash_set(pool->pool_(), table_, key, keylen, value);
+ }
+
+ //
+ // Get number of items in the hash table.
+ //
+ unsigned count()
+ {
+ return pj_hash_count(table_);
+ }
+
+ //
+ // Iterate hash table.
+ //
+ iterator begin()
+ {
+ iterator it(table_, NULL);
+ it.it_ = pj_hash_first(table_, &it.it_val_);
+ return it;
+ }
+
+ //
+ // End of items.
+ //
+ iterator end()
+ {
+ return iterator(table_, NULL);
+ }
+
+private:
+ pj_hash_table_t *table_;
+};
+
+
+#endif /* __PJPP_HASH_HPP__ */
+
diff --git a/pjlib/include/pj++/list.hpp b/pjlib/include/pj++/list.hpp
index 6e086ad2..433bd512 100644
--- a/pjlib/include/pj++/list.hpp
+++ b/pjlib/include/pj++/list.hpp
@@ -1,327 +1,327 @@
-/* $Id$ */
-/*
- * Copyright (C)2003-2006 Benny Prijono <benny@prijono.org>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
-#ifndef __PJPP_LIST_HPP__
-#define __PJPP_LIST_HPP__
-
-#include <pj/list.h>
-#include <pj++/pool.hpp>
-
-
-//
-// Linked-list.
-//
-// Note:
-// List_Node must have public member next and prev. Normally
-// it will be declared like:
-//
-// struct my_node
-// {
-// PJ_DECL_LIST_MEMBER(struct my_node);
-// ..
-// };
-//
-//
-template <class List_Node>
-class Pj_List : public Pj_Object
-{
-public:
- //
- // List const_iterator.
- //
- class const_iterator
- {
- public:
- const_iterator()
- : node_(NULL)
- {}
- const_iterator(const List_Node *nd)
- : node_((List_Node*)nd)
- {}
- const List_Node * operator *()
- {
- return node_;
- }
- const List_Node * operator -> ()
- {
- return node_;
- }
- const_iterator operator++()
- {
- return const_iterator(node_->next);
- }
- bool operator==(const const_iterator &rhs)
- {
- return node_ == rhs.node_;
- }
- bool operator!=(const const_iterator &rhs)
- {
- return node_ != rhs.node_;
- }
-
- protected:
- List_Node *node_;
- };
-
- //
- // List iterator.
- //
- class iterator : public const_iterator
- {
- public:
- iterator()
- {}
- iterator(List_Node *nd)
- : const_iterator(nd)
- {}
- List_Node * operator *()
- {
- return node_;
- }
- List_Node * operator -> ()
- {
- return node_;
- }
- iterator operator++()
- {
- return iterator(node_->next);
- }
- bool operator==(const iterator &rhs)
- {
- return node_ == rhs.node_;
- }
- bool operator!=(const iterator &rhs)
- {
- return node_ != rhs.node_;
- }
- };
-
- //
- // Default constructor.
- //
- Pj_List()
- {
- pj_list_init(&root_);
- if (0) compiletest();
- }
-
- //
- // Check if list is empty.
- //
- bool empty() const
- {
- return pj_list_empty(&root_);
- }
-
- //
- // Get first element.
- //
- iterator begin()
- {
- return iterator(root_.next);
- }
-
- //
- // Get first element.
- //
- const_iterator begin() const
- {
- return const_iterator(root_.next);
- }
-
- //
- // Get end-of-element
- //
- const_iterator end() const
- {
- return const_iterator((List_Node*)&root_);
- }
-
- //
- // Get end-of-element
- //
- iterator end()
- {
- return iterator((List_Node*)&root_);
- }
-
- //
- // Insert node.
- //
- void insert_before (iterator &pos, List_Node *node)
- {
- pj_list_insert_before( *pos, node );
- }
-
- //
- // Insert node.
- //
- void insert_after(iterator &pos, List_Node *node)
- {
- pj_list_insert_after(*pos, node);
- }
-
- //
- // Merge list.
- //
- void merge_first(List_Node *list2)
- {
- pj_list_merge_first(&root_, list2);
- }
-
- //
- // Merge list.
- //
- void merge_last(Pj_List *list)
- {
- pj_list_merge_last(&root_, &list->root_);
- }
-
- //
- // Insert list.
- //
- void insert_nodes_before(iterator &pos, Pj_List *list2)
- {
- pj_list_insert_nodes_before(*pos, &list2->root_);
- }
-
- //
- // Insert list.
- //
- void insert_nodes_after(iterator &pos, Pj_List *list2)
- {
- pj_list_insert_nodes_after(*pos, &list2->root_);
- }
-
- //
- // Erase an element.
- //
- void erase(iterator &it)
- {
- pj_list_erase(*it);
- }
-
- //
- // Get first element.
- //
- List_Node *front()
- {
- return root_.next;
- }
-
- //
- // Get first element.
- //
- const List_Node *front() const
- {
- return root_.next;
- }
-
- //
- // Remove first element.
- //
- void pop_front()
- {
- pj_list_erase(root_.next);
- }
-
- //
- // Get last element.
- //
- List_Node *back()
- {
- return root_.prev;
- }
-
- //
- // Get last element.
- //
- const List_Node *back() const
- {
- return root_.prev;
- }
-
- //
- // Remove last element.
- //
- void pop_back()
- {
- pj_list_erase(root_.prev);
- }
-
- //
- // Find a node.
- //
- iterator find(List_Node *node)
- {
- List_Node *n = pj_list_find_node(&root_, node);
- return n ? iterator(n) : end();
- }
-
- //
- // Find a node.
- //
- const_iterator find(List_Node *node) const
- {
- List_Node *n = pj_list_find_node(&root_, node);
- return n ? const_iterator(n) : end();
- }
-
- //
- // Insert a node in the back.
- //
- void push_back(List_Node *node)
- {
- pj_list_insert_after(root_.prev, node);
- }
-
- //
- // Insert a node in the front.
- //
- void push_front(List_Node *node)
- {
- pj_list_insert_before(root_.next, node);
- }
-
- //
- // Remove all elements.
- //
- void clear()
- {
- root_.next = &root_;
- root_.prev = &root_;
- }
-
-private:
- struct RootNode
- {
- PJ_DECL_LIST_MEMBER(List_Node);
- } root_;
-
- void compiletest()
- {
- // If you see error in this line,
- // it's because List_Node is not derived from Pj_List_Node.
- List_Node *n = (List_Node*)0;
- n = n->next; n = n->prev;
- }
-};
-
-
-#endif /* __PJPP_LIST_HPP__ */
-
+/* $Id$ */
+/*
+ * Copyright (C)2003-2006 Benny Prijono <benny@prijono.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#ifndef __PJPP_LIST_HPP__
+#define __PJPP_LIST_HPP__
+
+#include <pj/list.h>
+#include <pj++/pool.hpp>
+
+
+//
+// Linked-list.
+//
+// Note:
+// List_Node must have public member next and prev. Normally
+// it will be declared like:
+//
+// struct my_node
+// {
+// PJ_DECL_LIST_MEMBER(struct my_node);
+// ..
+// };
+//
+//
+template <class List_Node>
+class Pj_List : public Pj_Object
+{
+public:
+ //
+ // List const_iterator.
+ //
+ class const_iterator
+ {
+ public:
+ const_iterator()
+ : node_(NULL)
+ {}
+ const_iterator(const List_Node *nd)
+ : node_((List_Node*)nd)
+ {}
+ const List_Node * operator *()
+ {
+ return node_;
+ }
+ const List_Node * operator -> ()
+ {
+ return node_;
+ }
+ const_iterator operator++()
+ {
+ return const_iterator(node_->next);
+ }
+ bool operator==(const const_iterator &rhs)
+ {
+ return node_ == rhs.node_;
+ }
+ bool operator!=(const const_iterator &rhs)
+ {
+ return node_ != rhs.node_;
+ }
+
+ protected:
+ List_Node *node_;
+ };
+
+ //
+ // List iterator.
+ //
+ class iterator : public const_iterator
+ {
+ public:
+ iterator()
+ {}
+ iterator(List_Node *nd)
+ : const_iterator(nd)
+ {}
+ List_Node * operator *()
+ {
+ return node_;
+ }
+ List_Node * operator -> ()
+ {
+ return node_;
+ }
+ iterator operator++()
+ {
+ return iterator(node_->next);
+ }
+ bool operator==(const iterator &rhs)
+ {
+ return node_ == rhs.node_;
+ }
+ bool operator!=(const iterator &rhs)
+ {
+ return node_ != rhs.node_;
+ }
+ };
+
+ //
+ // Default constructor.
+ //
+ Pj_List()
+ {
+ pj_list_init(&root_);
+ if (0) compiletest();
+ }
+
+ //
+ // Check if list is empty.
+ //
+ bool empty() const
+ {
+ return pj_list_empty(&root_);
+ }
+
+ //
+ // Get first element.
+ //
+ iterator begin()
+ {
+ return iterator(root_.next);
+ }
+
+ //
+ // Get first element.
+ //
+ const_iterator begin() const
+ {
+ return const_iterator(root_.next);
+ }
+
+ //
+ // Get end-of-element
+ //
+ const_iterator end() const
+ {
+ return const_iterator((List_Node*)&root_);
+ }
+
+ //
+ // Get end-of-element
+ //
+ iterator end()
+ {
+ return iterator((List_Node*)&root_);
+ }
+
+ //
+ // Insert node.
+ //
+ void insert_before (iterator &pos, List_Node *node)
+ {
+ pj_list_insert_before( *pos, node );
+ }
+
+ //
+ // Insert node.
+ //
+ void insert_after(iterator &pos, List_Node *node)
+ {
+ pj_list_insert_after(*pos, node);
+ }
+
+ //
+ // Merge list.
+ //
+ void merge_first(List_Node *list2)
+ {
+ pj_list_merge_first(&root_, list2);
+ }
+
+ //
+ // Merge list.
+ //
+ void merge_last(Pj_List *list)
+ {
+ pj_list_merge_last(&root_, &list->root_);
+ }
+
+ //
+ // Insert list.
+ //
+ void insert_nodes_before(iterator &pos, Pj_List *list2)
+ {
+ pj_list_insert_nodes_before(*pos, &list2->root_);
+ }
+
+ //
+ // Insert list.
+ //
+ void insert_nodes_after(iterator &pos, Pj_List *list2)
+ {
+ pj_list_insert_nodes_after(*pos, &list2->root_);
+ }
+
+ //
+ // Erase an element.
+ //
+ void erase(iterator &it)
+ {
+ pj_list_erase(*it);
+ }
+
+ //
+ // Get first element.
+ //
+ List_Node *front()
+ {
+ return root_.next;
+ }
+
+ //
+ // Get first element.
+ //
+ const List_Node *front() const
+ {
+ return root_.next;
+ }
+
+ //
+ // Remove first element.
+ //
+ void pop_front()
+ {
+ pj_list_erase(root_.next);
+ }
+
+ //
+ // Get last element.
+ //
+ List_Node *back()
+ {
+ return root_.prev;
+ }
+
+ //
+ // Get last element.
+ //
+ const List_Node *back() const
+ {
+ return root_.prev;
+ }
+
+ //
+ // Remove last element.
+ //
+ void pop_back()
+ {
+ pj_list_erase(root_.prev);
+ }
+
+ //
+ // Find a node.
+ //
+ iterator find(List_Node *node)
+ {
+ List_Node *n = pj_list_find_node(&root_, node);
+ return n ? iterator(n) : end();
+ }
+
+ //
+ // Find a node.
+ //
+ const_iterator find(List_Node *node) const
+ {
+ List_Node *n = pj_list_find_node(&root_, node);
+ return n ? const_iterator(n) : end();
+ }
+
+ //
+ // Insert a node in the back.
+ //
+ void push_back(List_Node *node)
+ {
+ pj_list_insert_after(root_.prev, node);
+ }
+
+ //
+ // Insert a node in the front.
+ //
+ void push_front(List_Node *node)
+ {
+ pj_list_insert_before(root_.next, node);
+ }
+
+ //
+ // Remove all elements.
+ //
+ void clear()
+ {
+ root_.next = &root_;
+ root_.prev = &root_;
+ }
+
+private:
+ struct RootNode
+ {
+ PJ_DECL_LIST_MEMBER(List_Node);
+ } root_;
+
+ void compiletest()
+ {
+ // If you see error in this line,
+ // it's because List_Node is not derived from Pj_List_Node.
+ List_Node *n = (List_Node*)0;
+ n = n->next; n = n->prev;
+ }
+};
+
+
+#endif /* __PJPP_LIST_HPP__ */
+
diff --git a/pjlib/include/pj++/lock.hpp b/pjlib/include/pj++/lock.hpp
index 8473c01f..17c3a534 100644
--- a/pjlib/include/pj++/lock.hpp
+++ b/pjlib/include/pj++/lock.hpp
@@ -1,148 +1,148 @@
-/* $Id$ */
-/*
- * Copyright (C)2003-2006 Benny Prijono <benny@prijono.org>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
-#ifndef __PJPP_LOCK_HPP__
-#define __PJPP_LOCK_HPP__
-
-#include <pj++/types.hpp>
-#include <pj/lock.h>
-#include <pj++/pool.hpp>
-
-//////////////////////////////////////////////////////////////////////////////
-// Lock object.
-//
-class Pj_Lock : public Pj_Object
-{
-public:
- //
- // Constructor.
- //
- explicit Pj_Lock(pj_lock_t *lock)
- : lock_(lock)
- {
- }
-
- //
- // Destructor.
- //
- ~Pj_Lock()
- {
- if (lock_)
- pj_lock_destroy(lock_);
- }
-
- //
- // Get pjlib compatible lock object.
- //
- pj_lock_t *pj_lock_t_()
- {
- return lock_;
- }
-
- //
- // acquire lock.
- //
- pj_status_t acquire()
- {
- return pj_lock_acquire(lock_);
- }
-
- //
- // release lock,.
- //
- pj_status_t release()
- {
- return pj_lock_release(lock_);
- }
-
-protected:
- pj_lock_t *lock_;
-};
-
-
-//////////////////////////////////////////////////////////////////////////////
-// Null lock object.
-//
-class Pj_Null_Lock : public Pj_Lock
-{
-public:
- //
- // Default constructor.
- //
- explicit Pj_Null_Lock(Pj_Pool *pool, const char *name = NULL)
- : Pj_Lock(NULL)
- {
- pj_lock_create_null_mutex(pool->pool_(), name, &lock_);
- }
-};
-
-//////////////////////////////////////////////////////////////////////////////
-// Simple mutex lock object.
-//
-class Pj_Simple_Mutex_Lock : public Pj_Lock
-{
-public:
- //
- // Default constructor.
- //
- explicit Pj_Simple_Mutex_Lock(Pj_Pool *pool, const char *name = NULL)
- : Pj_Lock(NULL)
- {
- pj_lock_create_simple_mutex(pool->pool_(), name, &lock_);
- }
-};
-
-//////////////////////////////////////////////////////////////////////////////
-// Recursive mutex lock object.
-//
-class Pj_Recursive_Mutex_Lock : public Pj_Lock
-{
-public:
- //
- // Default constructor.
- //
- explicit Pj_Recursive_Mutex_Lock(Pj_Pool *pool, const char *name = NULL)
- : Pj_Lock(NULL)
- {
- pj_lock_create_recursive_mutex(pool->pool_(), name, &lock_);
- }
-};
-
-//////////////////////////////////////////////////////////////////////////////
-// Semaphore lock object.
-//
-class Pj_Semaphore_Lock : public Pj_Lock
-{
-public:
- //
- // Default constructor.
- //
- explicit Pj_Semaphore_Lock(Pj_Pool *pool,
- unsigned max=PJ_MAXINT32,
- unsigned initial=0,
- const char *name=NULL)
- : Pj_Lock(NULL)
- {
- pj_lock_create_semaphore(pool->pool_(), name, initial, max, &lock_);
- }
-};
-
-
-
-#endif /* __PJPP_LOCK_HPP__ */
-
+/* $Id$ */
+/*
+ * Copyright (C)2003-2006 Benny Prijono <benny@prijono.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#ifndef __PJPP_LOCK_HPP__
+#define __PJPP_LOCK_HPP__
+
+#include <pj++/types.hpp>
+#include <pj/lock.h>
+#include <pj++/pool.hpp>
+
+//////////////////////////////////////////////////////////////////////////////
+// Lock object.
+//
+class Pj_Lock : public Pj_Object
+{
+public:
+ //
+ // Constructor.
+ //
+ explicit Pj_Lock(pj_lock_t *lock)
+ : lock_(lock)
+ {
+ }
+
+ //
+ // Destructor.
+ //
+ ~Pj_Lock()
+ {
+ if (lock_)
+ pj_lock_destroy(lock_);
+ }
+
+ //
+ // Get pjlib compatible lock object.
+ //
+ pj_lock_t *pj_lock_t_()
+ {
+ return lock_;
+ }
+
+ //
+ // acquire lock.
+ //
+ pj_status_t acquire()
+ {
+ return pj_lock_acquire(lock_);
+ }
+
+ //
+ // release lock,.
+ //
+ pj_status_t release()
+ {
+ return pj_lock_release(lock_);
+ }
+
+protected:
+ pj_lock_t *lock_;
+};
+
+
+//////////////////////////////////////////////////////////////////////////////
+// Null lock object.
+//
+class Pj_Null_Lock : public Pj_Lock
+{
+public:
+ //
+ // Default constructor.
+ //
+ explicit Pj_Null_Lock(Pj_Pool *pool, const char *name = NULL)
+ : Pj_Lock(NULL)
+ {
+ pj_lock_create_null_mutex(pool->pool_(), name, &lock_);
+ }
+};
+
+//////////////////////////////////////////////////////////////////////////////
+// Simple mutex lock object.
+//
+class Pj_Simple_Mutex_Lock : public Pj_Lock
+{
+public:
+ //
+ // Default constructor.
+ //
+ explicit Pj_Simple_Mutex_Lock(Pj_Pool *pool, const char *name = NULL)
+ : Pj_Lock(NULL)
+ {
+ pj_lock_create_simple_mutex(pool->pool_(), name, &lock_);
+ }
+};
+
+//////////////////////////////////////////////////////////////////////////////
+// Recursive mutex lock object.
+//
+class Pj_Recursive_Mutex_Lock : public Pj_Lock
+{
+public:
+ //
+ // Default constructor.
+ //
+ explicit Pj_Recursive_Mutex_Lock(Pj_Pool *pool, const char *name = NULL)
+ : Pj_Lock(NULL)
+ {
+ pj_lock_create_recursive_mutex(pool->pool_(), name, &lock_);
+ }
+};
+
+//////////////////////////////////////////////////////////////////////////////
+// Semaphore lock object.
+//
+class Pj_Semaphore_Lock : public Pj_Lock
+{
+public:
+ //
+ // Default constructor.
+ //
+ explicit Pj_Semaphore_Lock(Pj_Pool *pool,
+ unsigned max=PJ_MAXINT32,
+ unsigned initial=0,
+ const char *name=NULL)
+ : Pj_Lock(NULL)
+ {
+ pj_lock_create_semaphore(pool->pool_(), name, initial, max, &lock_);
+ }
+};
+
+
+
+#endif /* __PJPP_LOCK_HPP__ */
+
diff --git a/pjlib/include/pj++/os.hpp b/pjlib/include/pj++/os.hpp
index 523c1c7d..2484e1bd 100644
--- a/pjlib/include/pj++/os.hpp
+++ b/pjlib/include/pj++/os.hpp
@@ -1,804 +1,804 @@
-/* $Id$ */
-/*
- * Copyright (C)2003-2006 Benny Prijono <benny@prijono.org>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
-#ifndef __PJPP_OS_HPP__
-#define __PJPP_OS_HPP__
-
-#include <pj/os.h>
-#include <pj++/types.hpp>
-#include <pj++/pool.hpp>
-
-class Pj_Thread;
-
-//
-// Thread API.
-//
-class Pj_Thread_API
-{
-public:
- //
- // Create a thread.
- //
- static pj_status_t create( Pj_Pool *pool, pj_thread_t **thread,
- pj_thread_proc *proc, void *arg,
- unsigned flags = 0,
- const char *name = NULL,
- pj_size_t stack_size = 0 )
- {
- return pj_thread_create(pool->pool_(), name, proc, arg, stack_size,
- flags, thread);
- }
-
- //
- // Register a thread.
- //
- static pj_status_t register_this_thread( pj_thread_desc desc,
- pj_thread_t **thread,
- const char *name = NULL )
- {
- return pj_thread_register( name, desc, thread );
- }
-
- //
- // Get current thread.
- // Will return pj_thread_t (sorry folks, not Pj_Thread).
- //
- static pj_thread_t *this_thread()
- {
- return pj_thread_this();
- }
-
- //
- // Get thread name.
- //
- static const char *get_name(pj_thread_t *thread)
- {
- return pj_thread_get_name(thread);
- }
-
- //
- // Resume thread.
- //
- static pj_status_t resume(pj_thread_t *thread)
- {
- return pj_thread_resume(thread);
- }
-
- //
- // Sleep.
- //
- static pj_status_t sleep(unsigned msec)
- {
- return pj_thread_sleep(msec);
- }
-
- //
- // Join the specified thread.
- //
- static pj_status_t join(pj_thread_t *thread)
- {
- return pj_thread_join(thread);
- }
-
- //
- // Destroy thread
- //
- static pj_status_t destroy(pj_thread_t *thread)
- {
- return pj_thread_destroy(thread);
- }
-};
-
-
-
-//
-// Thread object.
-//
-// How to use:
-// Derive a class from this class, then override main().
-//
-class Pj_Thread : public Pj_Object
-{
-public:
- enum Flags
- {
- FLAG_SUSPENDED = PJ_THREAD_SUSPENDED
- };
-
- //
- // Default constructor.
- //
- Pj_Thread()
- : thread_(NULL)
- {
- }
-
- //
- // Destroy thread.
- //
- ~Pj_Thread()
- {
- destroy();
- }
-
- //
- // This is the main thread function.
- //
- virtual int main() = 0;
-
- //
- // Start a thread.
- //
- pj_status_t create( Pj_Pool *pool,
- unsigned flags = 0,
- const char *thread_name = NULL,
- pj_size_t stack_size = PJ_THREAD_DEFAULT_STACK_SIZE)
- {
- destroy();
- return Pj_Thread_API::create( pool, &thread_, &thread_proc, this,
- flags, thread_name);
- }
-
- //
- // Get pjlib compatible thread object.
- //
- pj_thread_t *pj_thread_t_()
- {
- return thread_;
- }
-
- //
- // Get thread name.
- //
- const char *get_name()
- {
- return Pj_Thread_API::get_name(thread_);
- }
-
- //
- // Resume a suspended thread.
- //
- pj_status_t resume()
- {
- return Pj_Thread_API::resume(thread_);
- }
-
- //
- // Join this thread.
- //
- pj_status_t join()
- {
- return Pj_Thread_API::join(thread_);
- }
-
- //
- // Destroy thread.
- //
- pj_status_t destroy()
- {
- if (thread_) {
- Pj_Thread_API::destroy(thread_);
- thread_ = NULL;
- }
- }
-
-protected:
- pj_thread_t *thread_;
-
- static int PJ_THREAD_FUNC thread_proc(void *obj)
- {
- Pj_Thread *thread_class = (Pj_Thread*)obj;
- return thread_class->main();
- }
-};
-
-
-//
-// External Thread
-// (threads that were started by external means, i.e. not
-// with Pj_Thread::create).
-//
-// This class will normally be defined as local variable in
-// external thread's stack, normally inside thread's main proc.
-// But be aware that the handle will be destroyed on destructor!
-//
-class Pj_External_Thread : public Pj_Thread
-{
-public:
- Pj_External_Thread()
- {
- }
-
- //
- // Register external thread so that pjlib functions can work
- // in that thread.
- //
- pj_status_t register_this_thread( const char *name=NULL )
- {
- return Pj_Thread_API::register_this_thread(desc_, &thread_,name);
- }
-
-private:
- pj_thread_desc desc_;
-};
-
-
-//
-// Thread specific data/thread local storage/TLS.
-//
-class Pj_Thread_Local_API
-{
-public:
- //
- // Allocate thread local storage (TLS) index.
- //
- static pj_status_t alloc(long *index)
- {
- return pj_thread_local_alloc(index);
- }
-
- //
- // Free TLS index.
- //
- static void free(long index)
- {
- pj_thread_local_free(index);
- }
-
- //
- // Set thread specific data.
- //
- static pj_status_t set(long index, void *value)
- {
- return pj_thread_local_set(index, value);
- }
-
- //
- // Get thread specific data.
- //
- static void *get(long index)
- {
- return pj_thread_local_get(index);
- }
-
-};
-
-//
-// Atomic variable
-//
-// How to use:
-// Pj_Atomic_Var var(pool, 0);
-// var.set(..);
-//
-class Pj_Atomic_Var : public Pj_Object
-{
-public:
- //
- // Default constructor, initialize variable with NULL.
- //
- Pj_Atomic_Var()
- : var_(NULL)
- {
- }
-
- //
- // Construct atomic variable.
- //
- Pj_Atomic_Var(Pj_Pool *pool, pj_atomic_value_t value)
- : var_(NULL)
- {
- create(pool, value);
- }
-
- //
- // Destructor.
- //
- ~Pj_Atomic_Var()
- {
- destroy();
- }
-
- //
- // Create atomic variable.
- //
- pj_status_t create( Pj_Pool *pool, pj_atomic_value_t value)
- {
- destroy();
- return pj_atomic_create(pool->pool_(), value, &var_);
- }
-
- //
- // Destroy.
- //
- void destroy()
- {
- if (var_) {
- pj_atomic_destroy(var_);
- var_ = NULL;
- }
- }
-
- //
- // Get pjlib compatible atomic variable.
- //
- pj_atomic_t *pj_atomic_t_()
- {
- return var_;
- }
-
- //
- // Set the value.
- //
- void set(pj_atomic_value_t val)
- {
- pj_atomic_set(var_, val);
- }
-
- //
- // Get the value.
- //
- pj_atomic_value_t get()
- {
- return pj_atomic_get(var_);
- }
-
- //
- // Increment.
- //
- void inc()
- {
- pj_atomic_inc(var_);
- }
-
- //
- // Increment and get the result.
- //
- pj_atomic_value_t inc_and_get()
- {
- return pj_atomic_inc_and_get(var_);
- }
-
- //
- // Decrement.
- //
- void dec()
- {
- pj_atomic_dec(var_);
- }
-
- //
- // Decrement and get the result.
- //
- pj_atomic_value_t dec_and_get()
- {
- return pj_atomic_dec_and_get(var_);
- }
-
- //
- // Add the variable.
- //
- void add(pj_atomic_value_t value)
- {
- pj_atomic_add(var_, value);
- }
-
- //
- // Add the variable and get the value.
- //
- pj_atomic_value_t add_and_get(pj_atomic_value_t value)
- {
- return pj_atomic_add_and_get(var_, value );
- }
-
-private:
- pj_atomic_t *var_;
-};
-
-
-//
-// Mutex
-//
-class Pj_Mutex : public Pj_Object
-{
-public:
- //
- // Mutex type.
- //
- enum Type
- {
- DEFAULT = PJ_MUTEX_DEFAULT,
- SIMPLE = PJ_MUTEX_SIMPLE,
- RECURSE = PJ_MUTEX_RECURSE,
- };
-
- //
- // Default constructor will create default mutex.
- //
- explicit Pj_Mutex(Pj_Pool *pool, Type type = DEFAULT,
- const char *name = NULL)
- : mutex_(NULL)
- {
- create(pool, type, name);
- }
-
- //
- // Destructor.
- //
- ~Pj_Mutex()
- {
- destroy();
- }
-
- //
- // Create mutex.
- //
- pj_status_t create( Pj_Pool *pool, Type type, const char *name = NULL)
- {
- destroy();
- return pj_mutex_create( pool->pool_(), name, type,
- &mutex_ );
- }
-
- //
- // Create simple mutex.
- //
- pj_status_t create_simple( Pj_Pool *pool,const char *name = NULL)
- {
- return create(pool, SIMPLE, name);
- }
-
- //
- // Create recursive mutex.
- //
- pj_status_t create_recursive( Pj_Pool *pool, const char *name = NULL )
- {
- return create(pool, RECURSE, name);
- }
-
- //
- // Get pjlib compatible mutex object.
- //
- pj_mutex_t *pj_mutex_t_()
- {
- return mutex_;
- }
-
- //
- // Destroy mutex.
- //
- void destroy()
- {
- if (mutex_) {
- pj_mutex_destroy(mutex_);
- mutex_ = NULL;
- }
- }
-
- //
- // Lock mutex.
- //
- pj_status_t acquire()
- {
- return pj_mutex_lock(mutex_);
- }
-
- //
- // Unlock mutex.
- //
- pj_status_t release()
- {
- return pj_mutex_unlock(mutex_);
- }
-
- //
- // Try locking the mutex.
- //
- pj_status_t tryacquire()
- {
- return pj_mutex_trylock(mutex_);
- }
-
-private:
- pj_mutex_t *mutex_;
-};
-
-
-//
-// Semaphore
-//
-class Pj_Semaphore : public Pj_Object
-{
-public:
- //
- // Construct semaphore
- //
- Pj_Semaphore(Pj_Pool *pool, unsigned max,
- unsigned initial = 0, const char *name = NULL)
- : sem_(NULL)
- {
- }
-
- //
- // Destructor.
- //
- ~Pj_Semaphore()
- {
- destroy();
- }
-
- //
- // Create semaphore
- //
- pj_status_t create( Pj_Pool *pool, unsigned max,
- unsigned initial = 0, const char *name = NULL )
- {
- destroy();
- return pj_sem_create( pool->pool_(), name, initial, max, &sem_);
- }
-
- //
- // Destroy semaphore.
- //
- void destroy()
- {
- if (sem_) {
- pj_sem_destroy(sem_);
- sem_ = NULL;
- }
- }
-
- //
- // Get pjlib compatible semaphore object.
- //
- pj_sem_t *pj_sem_t_()
- {
- return (pj_sem_t*)this;
- }
-
- //
- // Wait semaphore.
- //
- pj_status_t wait()
- {
- return pj_sem_wait(this->pj_sem_t_());
- }
-
- //
- // Wait semaphore.
- //
- pj_status_t acquire()
- {
- return wait();
- }
-
- //
- // Try wait semaphore.
- //
- pj_status_t trywait()
- {
- return pj_sem_trywait(this->pj_sem_t_());
- }
-
- //
- // Try wait semaphore.
- //
- pj_status_t tryacquire()
- {
- return trywait();
- }
-
- //
- // Post semaphore.
- //
- pj_status_t post()
- {
- return pj_sem_post(this->pj_sem_t_());
- }
-
- //
- // Post semaphore.
- //
- pj_status_t release()
- {
- return post();
- }
-
-private:
- pj_sem_t *sem_;
-};
-
-
-//
-// Event object.
-//
-class Pj_Event
-{
-public:
- //
- // Construct event object.
- //
- Pj_Event( Pj_Pool *pool, bool manual_reset = false,
- bool initial = false, const char *name = NULL )
- : event_(NULL)
- {
- create(pool, manual_reset, initial, name);
- }
-
- //
- // Destructor.
- //
- ~Pj_Event()
- {
- destroy();
- }
-
- //
- // Create event object.
- //
- pj_status_t create( Pj_Pool *pool, bool manual_reset = false,
- bool initial = false, const char *name = NULL)
- {
- destroy();
- return pj_event_create(pool->pool_(), name, manual_reset, initial,
- &event_);
- }
-
- //
- // Get pjlib compatible event object.
- //
- pj_event_t *pj_event_t_()
- {
- return event_;
- }
-
- //
- // Destroy event object.
- //
- void destroy()
- {
- if (event_) {
- pj_event_destroy(event_);
- event_ = NULL;
- }
- }
-
- //
- // Wait.
- //
- pj_status_t wait()
- {
- return pj_event_wait(event_);
- }
-
- //
- // Try wait.
- //
- pj_status_t trywait()
- {
- return pj_event_trywait(event_);
- }
-
- //
- // Set event state to signalled.
- //
- pj_status_t set()
- {
- return pj_event_set(this->pj_event_t_());
- }
-
- //
- // Release one waiting thread.
- //
- pj_status_t pulse()
- {
- return pj_event_pulse(this->pj_event_t_());
- }
-
- //
- // Set a non-signalled.
- //
- pj_status_t reset()
- {
- return pj_event_reset(this->pj_event_t_());
- }
-
-private:
- pj_event_t *event_;
-};
-
-//
-// OS abstraction.
-//
-class Pj_OS_API
-{
-public:
- //
- // Get current time.
- //
- static pj_status_t gettimeofday( Pj_Time_Val *tv )
- {
- return pj_gettimeofday(tv);
- }
-
- //
- // Parse to time of day.
- //
- static pj_status_t time_decode( const Pj_Time_Val *tv,
- pj_parsed_time *pt )
- {
- return pj_time_decode(tv, pt);
- }
-
- //
- // Parse from time of day.
- //
- static pj_status_t time_encode( const pj_parsed_time *pt,
- Pj_Time_Val *tv)
- {
- return pj_time_encode(pt, tv);
- }
-
- //
- // Convert to GMT.
- //
- static pj_status_t time_local_to_gmt( Pj_Time_Val *tv )
- {
- return pj_time_local_to_gmt( tv );
- }
-
- //
- // Convert time to local.
- //
- static pj_status_t time_gmt_to_local( Pj_Time_Val *tv)
- {
- return pj_time_gmt_to_local( tv );
- }
-};
-
-//
-// Timeval inlines.
-//
-inline pj_status_t Pj_Time_Val::gettimeofday()
-{
- return Pj_OS_API::gettimeofday(this);
-}
-
-inline pj_parsed_time Pj_Time_Val::decode()
-{
- pj_parsed_time pt;
- Pj_OS_API::time_decode(this, &pt);
- return pt;
-}
-
-inline pj_status_t Pj_Time_Val::encode(const pj_parsed_time *pt)
-{
- return Pj_OS_API::time_encode(pt, this);
-}
-
-inline pj_status_t Pj_Time_Val::to_gmt()
-{
- return Pj_OS_API::time_local_to_gmt(this);
-}
-
-inline pj_status_t Pj_Time_Val::to_local()
-{
- return Pj_OS_API::time_gmt_to_local(this);
-}
-
-#endif /* __PJPP_OS_HPP__ */
-
+/* $Id$ */
+/*
+ * Copyright (C)2003-2006 Benny Prijono <benny@prijono.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#ifndef __PJPP_OS_HPP__
+#define __PJPP_OS_HPP__
+
+#include <pj/os.h>
+#include <pj++/types.hpp>
+#include <pj++/pool.hpp>
+
+class Pj_Thread;
+
+//
+// Thread API.
+//
+class Pj_Thread_API
+{
+public:
+ //
+ // Create a thread.
+ //
+ static pj_status_t create( Pj_Pool *pool, pj_thread_t **thread,
+ pj_thread_proc *proc, void *arg,
+ unsigned flags = 0,
+ const char *name = NULL,
+ pj_size_t stack_size = 0 )
+ {
+ return pj_thread_create(pool->pool_(), name, proc, arg, stack_size,
+ flags, thread);
+ }
+
+ //
+ // Register a thread.
+ //
+ static pj_status_t register_this_thread( pj_thread_desc desc,
+ pj_thread_t **thread,
+ const char *name = NULL )
+ {
+ return pj_thread_register( name, desc, thread );
+ }
+
+ //
+ // Get current thread.
+ // Will return pj_thread_t (sorry folks, not Pj_Thread).
+ //
+ static pj_thread_t *this_thread()
+ {
+ return pj_thread_this();
+ }
+
+ //
+ // Get thread name.
+ //
+ static const char *get_name(pj_thread_t *thread)
+ {
+ return pj_thread_get_name(thread);
+ }
+
+ //
+ // Resume thread.
+ //
+ static pj_status_t resume(pj_thread_t *thread)
+ {
+ return pj_thread_resume(thread);
+ }
+
+ //
+ // Sleep.
+ //
+ static pj_status_t sleep(unsigned msec)
+ {
+ return pj_thread_sleep(msec);
+ }
+
+ //
+ // Join the specified thread.
+ //
+ static pj_status_t join(pj_thread_t *thread)
+ {
+ return pj_thread_join(thread);
+ }
+
+ //
+ // Destroy thread
+ //
+ static pj_status_t destroy(pj_thread_t *thread)
+ {
+ return pj_thread_destroy(thread);
+ }
+};
+
+
+
+//
+// Thread object.
+//
+// How to use:
+// Derive a class from this class, then override main().
+//
+class Pj_Thread : public Pj_Object
+{
+public:
+ enum Flags
+ {
+ FLAG_SUSPENDED = PJ_THREAD_SUSPENDED
+ };
+
+ //
+ // Default constructor.
+ //
+ Pj_Thread()
+ : thread_(NULL)
+ {
+ }
+
+ //
+ // Destroy thread.
+ //
+ ~Pj_Thread()
+ {
+ destroy();
+ }
+
+ //
+ // This is the main thread function.
+ //
+ virtual int main() = 0;
+
+ //
+ // Start a thread.
+ //
+ pj_status_t create( Pj_Pool *pool,
+ unsigned flags = 0,
+ const char *thread_name = NULL,
+ pj_size_t stack_size = PJ_THREAD_DEFAULT_STACK_SIZE)
+ {
+ destroy();
+ return Pj_Thread_API::create( pool, &thread_, &thread_proc, this,
+ flags, thread_name);
+ }
+
+ //
+ // Get pjlib compatible thread object.
+ //
+ pj_thread_t *pj_thread_t_()
+ {
+ return thread_;
+ }
+
+ //
+ // Get thread name.
+ //
+ const char *get_name()
+ {
+ return Pj_Thread_API::get_name(thread_);
+ }
+
+ //
+ // Resume a suspended thread.
+ //
+ pj_status_t resume()
+ {
+ return Pj_Thread_API::resume(thread_);
+ }
+
+ //
+ // Join this thread.
+ //
+ pj_status_t join()
+ {
+ return Pj_Thread_API::join(thread_);
+ }
+
+ //
+ // Destroy thread.
+ //
+ pj_status_t destroy()
+ {
+ if (thread_) {
+ Pj_Thread_API::destroy(thread_);
+ thread_ = NULL;
+ }
+ }
+
+protected:
+ pj_thread_t *thread_;
+
+ static int PJ_THREAD_FUNC thread_proc(void *obj)
+ {
+ Pj_Thread *thread_class = (Pj_Thread*)obj;
+ return thread_class->main();
+ }
+};
+
+
+//
+// External Thread
+// (threads that were started by external means, i.e. not
+// with Pj_Thread::create).
+//
+// This class will normally be defined as local variable in
+// external thread's stack, normally inside thread's main proc.
+// But be aware that the handle will be destroyed on destructor!
+//
+class Pj_External_Thread : public Pj_Thread
+{
+public:
+ Pj_External_Thread()
+ {
+ }
+
+ //
+ // Register external thread so that pjlib functions can work
+ // in that thread.
+ //
+ pj_status_t register_this_thread( const char *name=NULL )
+ {
+ return Pj_Thread_API::register_this_thread(desc_, &thread_,name);
+ }
+
+private:
+ pj_thread_desc desc_;
+};
+
+
+//
+// Thread specific data/thread local storage/TLS.
+//
+class Pj_Thread_Local_API
+{
+public:
+ //
+ // Allocate thread local storage (TLS) index.
+ //
+ static pj_status_t alloc(long *index)
+ {
+ return pj_thread_local_alloc(index);
+ }
+
+ //
+ // Free TLS index.
+ //
+ static void free(long index)
+ {
+ pj_thread_local_free(index);
+ }
+
+ //
+ // Set thread specific data.
+ //
+ static pj_status_t set(long index, void *value)
+ {
+ return pj_thread_local_set(index, value);
+ }
+
+ //
+ // Get thread specific data.
+ //
+ static void *get(long index)
+ {
+ return pj_thread_local_get(index);
+ }
+
+};
+
+//
+// Atomic variable
+//
+// How to use:
+// Pj_Atomic_Var var(pool, 0);
+// var.set(..);
+//
+class Pj_Atomic_Var : public Pj_Object
+{
+public:
+ //
+ // Default constructor, initialize variable with NULL.
+ //
+ Pj_Atomic_Var()
+ : var_(NULL)
+ {
+ }
+
+ //
+ // Construct atomic variable.
+ //
+ Pj_Atomic_Var(Pj_Pool *pool, pj_atomic_value_t value)
+ : var_(NULL)
+ {
+ create(pool, value);
+ }
+
+ //
+ // Destructor.
+ //
+ ~Pj_Atomic_Var()
+ {
+ destroy();
+ }
+
+ //
+ // Create atomic variable.
+ //
+ pj_status_t create( Pj_Pool *pool, pj_atomic_value_t value)
+ {
+ destroy();
+ return pj_atomic_create(pool->pool_(), value, &var_);
+ }
+
+ //
+ // Destroy.
+ //
+ void destroy()
+ {
+ if (var_) {
+ pj_atomic_destroy(var_);
+ var_ = NULL;
+ }
+ }
+
+ //
+ // Get pjlib compatible atomic variable.
+ //
+ pj_atomic_t *pj_atomic_t_()
+ {
+ return var_;
+ }
+
+ //
+ // Set the value.
+ //
+ void set(pj_atomic_value_t val)
+ {
+ pj_atomic_set(var_, val);
+ }
+
+ //
+ // Get the value.
+ //
+ pj_atomic_value_t get()
+ {
+ return pj_atomic_get(var_);
+ }
+
+ //
+ // Increment.
+ //
+ void inc()
+ {
+ pj_atomic_inc(var_);
+ }
+
+ //
+ // Increment and get the result.
+ //
+ pj_atomic_value_t inc_and_get()
+ {
+ return pj_atomic_inc_and_get(var_);
+ }
+
+ //
+ // Decrement.
+ //
+ void dec()
+ {
+ pj_atomic_dec(var_);
+ }
+
+ //
+ // Decrement and get the result.
+ //
+ pj_atomic_value_t dec_and_get()
+ {
+ return pj_atomic_dec_and_get(var_);
+ }
+
+ //
+ // Add the variable.
+ //
+ void add(pj_atomic_value_t value)
+ {
+ pj_atomic_add(var_, value);
+ }
+
+ //
+ // Add the variable and get the value.
+ //
+ pj_atomic_value_t add_and_get(pj_atomic_value_t value)
+ {
+ return pj_atomic_add_and_get(var_, value );
+ }
+
+private:
+ pj_atomic_t *var_;
+};
+
+
+//
+// Mutex
+//
+class Pj_Mutex : public Pj_Object
+{
+public:
+ //
+ // Mutex type.
+ //
+ enum Type
+ {
+ DEFAULT = PJ_MUTEX_DEFAULT,
+ SIMPLE = PJ_MUTEX_SIMPLE,
+ RECURSE = PJ_MUTEX_RECURSE,
+ };
+
+ //
+ // Default constructor will create default mutex.
+ //
+ explicit Pj_Mutex(Pj_Pool *pool, Type type = DEFAULT,
+ const char *name = NULL)
+ : mutex_(NULL)
+ {
+ create(pool, type, name);
+ }
+
+ //
+ // Destructor.
+ //
+ ~Pj_Mutex()
+ {
+ destroy();
+ }
+
+ //
+ // Create mutex.
+ //
+ pj_status_t create( Pj_Pool *pool, Type type, const char *name = NULL)
+ {
+ destroy();
+ return pj_mutex_create( pool->pool_(), name, type,
+ &mutex_ );
+ }
+
+ //
+ // Create simple mutex.
+ //
+ pj_status_t create_simple( Pj_Pool *pool,const char *name = NULL)
+ {
+ return create(pool, SIMPLE, name);
+ }
+
+ //
+ // Create recursive mutex.
+ //
+ pj_status_t create_recursive( Pj_Pool *pool, const char *name = NULL )
+ {
+ return create(pool, RECURSE, name);
+ }
+
+ //
+ // Get pjlib compatible mutex object.
+ //
+ pj_mutex_t *pj_mutex_t_()
+ {
+ return mutex_;
+ }
+
+ //
+ // Destroy mutex.
+ //
+ void destroy()
+ {
+ if (mutex_) {
+ pj_mutex_destroy(mutex_);
+ mutex_ = NULL;
+ }
+ }
+
+ //
+ // Lock mutex.
+ //
+ pj_status_t acquire()
+ {
+ return pj_mutex_lock(mutex_);
+ }
+
+ //
+ // Unlock mutex.
+ //
+ pj_status_t release()
+ {
+ return pj_mutex_unlock(mutex_);
+ }
+
+ //
+ // Try locking the mutex.
+ //
+ pj_status_t tryacquire()
+ {
+ return pj_mutex_trylock(mutex_);
+ }
+
+private:
+ pj_mutex_t *mutex_;
+};
+
+
+//
+// Semaphore
+//
+class Pj_Semaphore : public Pj_Object
+{
+public:
+ //
+ // Construct semaphore
+ //
+ Pj_Semaphore(Pj_Pool *pool, unsigned max,
+ unsigned initial = 0, const char *name = NULL)
+ : sem_(NULL)
+ {
+ }
+
+ //
+ // Destructor.
+ //
+ ~Pj_Semaphore()
+ {
+ destroy();
+ }
+
+ //
+ // Create semaphore
+ //
+ pj_status_t create( Pj_Pool *pool, unsigned max,
+ unsigned initial = 0, const char *name = NULL )
+ {
+ destroy();
+ return pj_sem_create( pool->pool_(), name, initial, max, &sem_);
+ }
+
+ //
+ // Destroy semaphore.
+ //
+ void destroy()
+ {
+ if (sem_) {
+ pj_sem_destroy(sem_);
+ sem_ = NULL;
+ }
+ }
+
+ //
+ // Get pjlib compatible semaphore object.
+ //
+ pj_sem_t *pj_sem_t_()
+ {
+ return (pj_sem_t*)this;
+ }
+
+ //
+ // Wait semaphore.
+ //
+ pj_status_t wait()
+ {
+ return pj_sem_wait(this->pj_sem_t_());
+ }
+
+ //
+ // Wait semaphore.
+ //
+ pj_status_t acquire()
+ {
+ return wait();
+ }
+
+ //
+ // Try wait semaphore.
+ //
+ pj_status_t trywait()
+ {
+ return pj_sem_trywait(this->pj_sem_t_());
+ }
+
+ //
+ // Try wait semaphore.
+ //
+ pj_status_t tryacquire()
+ {
+ return trywait();
+ }
+
+ //
+ // Post semaphore.
+ //
+ pj_status_t post()
+ {
+ return pj_sem_post(this->pj_sem_t_());
+ }
+
+ //
+ // Post semaphore.
+ //
+ pj_status_t release()
+ {
+ return post();
+ }
+
+private:
+ pj_sem_t *sem_;
+};
+
+
+//
+// Event object.
+//
+class Pj_Event
+{
+public:
+ //
+ // Construct event object.
+ //
+ Pj_Event( Pj_Pool *pool, bool manual_reset = false,
+ bool initial = false, const char *name = NULL )
+ : event_(NULL)
+ {
+ create(pool, manual_reset, initial, name);
+ }
+
+ //
+ // Destructor.
+ //
+ ~Pj_Event()
+ {
+ destroy();
+ }
+
+ //
+ // Create event object.
+ //
+ pj_status_t create( Pj_Pool *pool, bool manual_reset = false,
+ bool initial = false, const char *name = NULL)
+ {
+ destroy();
+ return pj_event_create(pool->pool_(), name, manual_reset, initial,
+ &event_);
+ }
+
+ //
+ // Get pjlib compatible event object.
+ //
+ pj_event_t *pj_event_t_()
+ {
+ return event_;
+ }
+
+ //
+ // Destroy event object.
+ //
+ void destroy()
+ {
+ if (event_) {
+ pj_event_destroy(event_);
+ event_ = NULL;
+ }
+ }
+
+ //
+ // Wait.
+ //
+ pj_status_t wait()
+ {
+ return pj_event_wait(event_);
+ }
+
+ //
+ // Try wait.
+ //
+ pj_status_t trywait()
+ {
+ return pj_event_trywait(event_);
+ }
+
+ //
+ // Set event state to signalled.
+ //
+ pj_status_t set()
+ {
+ return pj_event_set(this->pj_event_t_());
+ }
+
+ //
+ // Release one waiting thread.
+ //
+ pj_status_t pulse()
+ {
+ return pj_event_pulse(this->pj_event_t_());
+ }
+
+ //
+ // Set a non-signalled.
+ //
+ pj_status_t reset()
+ {
+ return pj_event_reset(this->pj_event_t_());
+ }
+
+private:
+ pj_event_t *event_;
+};
+
+//
+// OS abstraction.
+//
+class Pj_OS_API
+{
+public:
+ //
+ // Get current time.
+ //
+ static pj_status_t gettimeofday( Pj_Time_Val *tv )
+ {
+ return pj_gettimeofday(tv);
+ }
+
+ //
+ // Parse to time of day.
+ //
+ static pj_status_t time_decode( const Pj_Time_Val *tv,
+ pj_parsed_time *pt )
+ {
+ return pj_time_decode(tv, pt);
+ }
+
+ //
+ // Parse from time of day.
+ //
+ static pj_status_t time_encode( const pj_parsed_time *pt,
+ Pj_Time_Val *tv)
+ {
+ return pj_time_encode(pt, tv);
+ }
+
+ //
+ // Convert to GMT.
+ //
+ static pj_status_t time_local_to_gmt( Pj_Time_Val *tv )
+ {
+ return pj_time_local_to_gmt( tv );
+ }
+
+ //
+ // Convert time to local.
+ //
+ static pj_status_t time_gmt_to_local( Pj_Time_Val *tv)
+ {
+ return pj_time_gmt_to_local( tv );
+ }
+};
+
+//
+// Timeval inlines.
+//
+inline pj_status_t Pj_Time_Val::gettimeofday()
+{
+ return Pj_OS_API::gettimeofday(this);
+}
+
+inline pj_parsed_time Pj_Time_Val::decode()
+{
+ pj_parsed_time pt;
+ Pj_OS_API::time_decode(this, &pt);
+ return pt;
+}
+
+inline pj_status_t Pj_Time_Val::encode(const pj_parsed_time *pt)
+{
+ return Pj_OS_API::time_encode(pt, this);
+}
+
+inline pj_status_t Pj_Time_Val::to_gmt()
+{
+ return Pj_OS_API::time_local_to_gmt(this);
+}
+
+inline pj_status_t Pj_Time_Val::to_local()
+{
+ return Pj_OS_API::time_gmt_to_local(this);
+}
+
+#endif /* __PJPP_OS_HPP__ */
+
diff --git a/pjlib/include/pj++/pool.hpp b/pjlib/include/pj++/pool.hpp
index 1be7ed04..02b54336 100644
--- a/pjlib/include/pj++/pool.hpp
+++ b/pjlib/include/pj++/pool.hpp
@@ -1,270 +1,270 @@
-/* $Id$ */
-/*
- * Copyright (C)2003-2006 Benny Prijono <benny@prijono.org>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
-#ifndef __PJPP_POOL_HPP__
-#define __PJPP_POOL_HPP__
-
-#include <pj/pool.h>
-
-class Pj_Pool;
-class Pj_Caching_Pool;
-
-//
-// Base class for all Pjlib objects
-//
-class Pj_Object
-{
-public:
- void *operator new(unsigned int class_size, Pj_Pool *pool);
- void *operator new(unsigned int class_size, Pj_Pool &pool);
-
- void operator delete(void*)
- {
- }
-
- void operator delete(void*, Pj_Pool*)
- {
- }
-
- void operator delete(void*, Pj_Pool&)
- {
- }
-
- //
- // Inline implementations at the end of this file.
- //
-
-private:
- // Can not use normal new operator; must use pool.
- // e.g.:
- // obj = new(pool) Pj_The_Object(pool, ...);
- //
- void *operator new(unsigned int)
- {}
-};
-
-
-//
-// Pool.
-//
-class Pj_Pool : public Pj_Object
-{
-public:
- //
- // Default constructor, initializes internal pool to NULL.
- // Application must call attach() some time later.
- //
- Pj_Pool()
- : p_(NULL)
- {
- }
-
- //
- // Create pool.
- //
- Pj_Pool(Pj_Caching_Pool &caching_pool,
- pj_size_t initial_size,
- pj_size_t increment_size,
- const char *name = NULL,
- pj_pool_callback *callback = NULL);
-
- //
- // Construct from existing pool.
- //
- explicit Pj_Pool(pj_pool_t *pool)
- : p_(pool)
- {
- }
-
- //
- // Attach existing pool.
- //
- void attach(pj_pool_t *pool)
- {
- p_ = pool;
- }
-
- //
- // Destructor.
- //
- // Release pool back to factory. Remember: if you delete pool, then
- // make sure that all objects that have been allocated from this pool
- // have been properly destroyed.
- //
- // This is where C++ is trickier than plain C!!
- //
- ~Pj_Pool()
- {
- if (p_)
- pj_pool_release(p_);
- }
-
- //
- // Get name.
- //
- const char *getobjname() const
- {
- return pj_pool_getobjname(p_);
- }
-
- //
- // Get pjlib compatible pool object.
- //
- pj_pool_t *pool_()
- {
- return p_;
- }
-
- //
- // Get pjlib compatible pool object.
- //
- const pj_pool_t *pool_() const
- {
- return p_;
- }
-
- //
- // Get pjlib compatible pool object.
- //
- pj_pool_t *pj_pool_t_()
- {
- return p_;
- }
-
- //
- // Reset pool.
- //
- void reset()
- {
- pj_pool_reset(p_);
- }
-
- //
- // Get current capacity.
- //
- pj_size_t get_capacity()
- {
- pj_pool_get_capacity(p_);
- }
-
- //
- // Get current total bytes allocated from the pool.
- //
- pj_size_t get_used_size()
- {
- pj_pool_get_used_size(p_);
- }
-
- //
- // Allocate.
- //
- void *alloc(pj_size_t size)
- {
- return pj_pool_alloc(p_, size);
- }
-
- //
- // Allocate elements and zero fill the memory.
- //
- void *calloc(pj_size_t count, pj_size_t elem)
- {
- return pj_pool_calloc(p_, count, elem);
- }
-
- //
- // Allocate and zero fill memory.
- //
- void *zalloc(pj_size_t size)
- {
- return pj_pool_zalloc(p_, size);
- }
-
-private:
- pj_pool_t *p_;
-};
-
-
-//
-// Caching pool.
-//
-class Pj_Caching_Pool
-{
-public:
- //
- // Construct caching pool.
- //
- Pj_Caching_Pool( pj_size_t cache_capacity = 0,
- const pj_pool_factory_policy *pol=&pj_pool_factory_default_policy)
- {
- pj_caching_pool_init(&cp_, pol, cache_capacity);
- }
-
- //
- // Destroy caching pool.
- //
- ~Pj_Caching_Pool()
- {
- pj_caching_pool_destroy(&cp_);
- }
-
- //
- // Create pool.
- //
- pj_pool_t *create_pool( pj_size_t initial_size,
- pj_size_t increment_size,
- const char *name = NULL,
- pj_pool_callback *callback = NULL)
- {
- return (pj_pool_t*)(*cp_.factory.create_pool)(&cp_.factory, name,
- initial_size,
- increment_size,
- callback);
- }
-
-private:
- pj_caching_pool cp_;
-};
-
-//
-// Inlines for Pj_Object
-//
-inline void *Pj_Object::operator new(unsigned int class_size, Pj_Pool *pool)
-{
- return pool->alloc(class_size);
-}
-inline void *Pj_Object::operator new(unsigned int class_size, Pj_Pool &pool)
-{
- return pool.alloc(class_size);
-}
-
-//
-// Inlines for Pj_Pool
-//
-inline Pj_Pool::Pj_Pool( Pj_Caching_Pool &caching_pool,
- pj_size_t initial_size,
- pj_size_t increment_size,
- const char *name,
- pj_pool_callback *callback)
-{
- p_ = caching_pool.create_pool(initial_size, increment_size, name,
- callback);
-}
-
-
-#endif /* __PJPP_POOL_HPP__ */
-
+/* $Id$ */
+/*
+ * Copyright (C)2003-2006 Benny Prijono <benny@prijono.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#ifndef __PJPP_POOL_HPP__
+#define __PJPP_POOL_HPP__
+
+#include <pj/pool.h>
+
+class Pj_Pool;
+class Pj_Caching_Pool;
+
+//
+// Base class for all Pjlib objects
+//
+class Pj_Object
+{
+public:
+ void *operator new(unsigned int class_size, Pj_Pool *pool);
+ void *operator new(unsigned int class_size, Pj_Pool &pool);
+
+ void operator delete(void*)
+ {
+ }
+
+ void operator delete(void*, Pj_Pool*)
+ {
+ }
+
+ void operator delete(void*, Pj_Pool&)
+ {
+ }
+
+ //
+ // Inline implementations at the end of this file.
+ //
+
+private:
+ // Can not use normal new operator; must use pool.
+ // e.g.:
+ // obj = new(pool) Pj_The_Object(pool, ...);
+ //
+ void *operator new(unsigned int)
+ {}
+};
+
+
+//
+// Pool.
+//
+class Pj_Pool : public Pj_Object
+{
+public:
+ //
+ // Default constructor, initializes internal pool to NULL.
+ // Application must call attach() some time later.
+ //
+ Pj_Pool()
+ : p_(NULL)
+ {
+ }
+
+ //
+ // Create pool.
+ //
+ Pj_Pool(Pj_Caching_Pool &caching_pool,
+ pj_size_t initial_size,
+ pj_size_t increment_size,
+ const char *name = NULL,
+ pj_pool_callback *callback = NULL);
+
+ //
+ // Construct from existing pool.
+ //
+ explicit Pj_Pool(pj_pool_t *pool)
+ : p_(pool)
+ {
+ }
+
+ //
+ // Attach existing pool.
+ //
+ void attach(pj_pool_t *pool)
+ {
+ p_ = pool;
+ }
+
+ //
+ // Destructor.
+ //
+ // Release pool back to factory. Remember: if you delete pool, then
+ // make sure that all objects that have been allocated from this pool
+ // have been properly destroyed.
+ //
+ // This is where C++ is trickier than plain C!!
+ //
+ ~Pj_Pool()
+ {
+ if (p_)
+ pj_pool_release(p_);
+ }
+
+ //
+ // Get name.
+ //
+ const char *getobjname() const
+ {
+ return pj_pool_getobjname(p_);
+ }
+
+ //
+ // Get pjlib compatible pool object.
+ //
+ pj_pool_t *pool_()
+ {
+ return p_;
+ }
+
+ //
+ // Get pjlib compatible pool object.
+ //
+ const pj_pool_t *pool_() const
+ {
+ return p_;
+ }
+
+ //
+ // Get pjlib compatible pool object.
+ //
+ pj_pool_t *pj_pool_t_()
+ {
+ return p_;
+ }
+
+ //
+ // Reset pool.
+ //
+ void reset()
+ {
+ pj_pool_reset(p_);
+ }
+
+ //
+ // Get current capacity.
+ //
+ pj_size_t get_capacity()
+ {
+ pj_pool_get_capacity(p_);
+ }
+
+ //
+ // Get current total bytes allocated from the pool.
+ //
+ pj_size_t get_used_size()
+ {
+ pj_pool_get_used_size(p_);
+ }
+
+ //
+ // Allocate.
+ //
+ void *alloc(pj_size_t size)
+ {
+ return pj_pool_alloc(p_, size);
+ }
+
+ //
+ // Allocate elements and zero fill the memory.
+ //
+ void *calloc(pj_size_t count, pj_size_t elem)
+ {
+ return pj_pool_calloc(p_, count, elem);
+ }
+
+ //
+ // Allocate and zero fill memory.
+ //
+ void *zalloc(pj_size_t size)
+ {
+ return pj_pool_zalloc(p_, size);
+ }
+
+private:
+ pj_pool_t *p_;
+};
+
+
+//
+// Caching pool.
+//
+class Pj_Caching_Pool
+{
+public:
+ //
+ // Construct caching pool.
+ //
+ Pj_Caching_Pool( pj_size_t cache_capacity = 0,
+ const pj_pool_factory_policy *pol=&pj_pool_factory_default_policy)
+ {
+ pj_caching_pool_init(&cp_, pol, cache_capacity);
+ }
+
+ //
+ // Destroy caching pool.
+ //
+ ~Pj_Caching_Pool()
+ {
+ pj_caching_pool_destroy(&cp_);
+ }
+
+ //
+ // Create pool.
+ //
+ pj_pool_t *create_pool( pj_size_t initial_size,
+ pj_size_t increment_size,
+ const char *name = NULL,
+ pj_pool_callback *callback = NULL)
+ {
+ return (pj_pool_t*)(*cp_.factory.create_pool)(&cp_.factory, name,
+ initial_size,
+ increment_size,
+ callback);
+ }
+
+private:
+ pj_caching_pool cp_;
+};
+
+//
+// Inlines for Pj_Object
+//
+inline void *Pj_Object::operator new(unsigned int class_size, Pj_Pool *pool)
+{
+ return pool->alloc(class_size);
+}
+inline void *Pj_Object::operator new(unsigned int class_size, Pj_Pool &pool)
+{
+ return pool.alloc(class_size);
+}
+
+//
+// Inlines for Pj_Pool
+//
+inline Pj_Pool::Pj_Pool( Pj_Caching_Pool &caching_pool,
+ pj_size_t initial_size,
+ pj_size_t increment_size,
+ const char *name,
+ pj_pool_callback *callback)
+{
+ p_ = caching_pool.create_pool(initial_size, increment_size, name,
+ callback);
+}
+
+
+#endif /* __PJPP_POOL_HPP__ */
+
diff --git a/pjlib/include/pj++/proactor.hpp b/pjlib/include/pj++/proactor.hpp
index a8061d64..92309ea6 100644
--- a/pjlib/include/pj++/proactor.hpp
+++ b/pjlib/include/pj++/proactor.hpp
@@ -1,518 +1,518 @@
-/* $Id$ */
-/*
- * Copyright (C)2003-2006 Benny Prijono <benny@prijono.org>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
-#ifndef __PJPP_PROACTOR_HPP__
-#define __PJPP_PROACTOR_HPP__
-
-#include <pj/ioqueue.h>
-#include <pj++/pool.hpp>
-#include <pj++/sock.hpp>
-#include <pj++/timer.hpp>
-#include <pj/errno.h>
-
-class Pj_Proactor;
-class Pj_Event_Handler;
-
-
-//////////////////////////////////////////////////////////////////////////////
-// Asynchronous operation key.
-//
-// Applications may inheric this class to put their application
-// specific data.
-//
-class Pj_Async_Op : public pj_ioqueue_op_key_t
-{
-public:
- //
- // Construct with null handler.
- // App must call set_handler() before use.
- //
- Pj_Async_Op()
- : handler_(NULL)
- {
- pj_ioqueue_op_key_init(this, sizeof(*this));
- }
-
- //
- // Constructor.
- //
- explicit Pj_Async_Op(Pj_Event_Handler *handler)
- : handler_(handler)
- {
- pj_ioqueue_op_key_init(this, sizeof(*this));
- }
-
- //
- // Set handler.
- //
- void set_handler(Pj_Event_Handler *handler)
- {
- handler_ = handler;
- }
-
- //
- // Check whether operation is still pending for this key.
- //
- bool is_pending();
-
- //
- // Cancel the operation.
- //
- bool cancel(pj_ssize_t bytes_status=-PJ_ECANCELLED);
-
-protected:
- Pj_Event_Handler *handler_;
-};
-
-
-//////////////////////////////////////////////////////////////////////////////
-// Event handler.
-//
-// Applications should inherit this class to receive various event
-// notifications.
-//
-// Applications should implement get_socket_handle().
-//
-class Pj_Event_Handler : public Pj_Object
-{
- friend class Pj_Proactor;
-public:
- //
- // Default constructor.
- //
- Pj_Event_Handler()
- : key_(NULL)
- {
- pj_memset(&timer_, 0, sizeof(timer_));
- timer_.user_data = this;
- timer_.cb = &timer_callback;
- }
-
- //
- // Destroy.
- //
- virtual ~Pj_Event_Handler()
- {
- unregister();
- }
-
- //
- // Unregister this handler from the ioqueue.
- //
- void unregister()
- {
- if (key_) {
- pj_ioqueue_unregister(key_);
- key_ = NULL;
- }
- }
-
- //
- // Get socket handle associated with this.
- //
- virtual pj_sock_t get_socket_handle()
- {
- return PJ_INVALID_SOCKET;
- }
-
- //
- // Start async receive.
- //
- pj_status_t recv( Pj_Async_Op *op_key,
- void *buf, pj_ssize_t *len,
- unsigned flags)
- {
- return pj_ioqueue_recv( key_, op_key,
- buf, len, flags);
- }
-
- //
- // Start async recvfrom()
- //
- pj_status_t recvfrom( Pj_Async_Op *op_key,
- void *buf, pj_ssize_t *len, unsigned flags,
- Pj_Inet_Addr *addr)
- {
- addr->addrlen_ = sizeof(Pj_Inet_Addr);
- return pj_ioqueue_recvfrom( key_, op_key, buf, len, flags,
- addr, &addr->addrlen_ );
- }
-
- //
- // Start async send()
- //
- pj_status_t send( Pj_Async_Op *op_key,
- const void *data, pj_ssize_t *len,
- unsigned flags)
- {
- return pj_ioqueue_send( key_, op_key, data, len, flags);
- }
-
- //
- // Start async sendto()
- //
- pj_status_t sendto( Pj_Async_Op *op_key,
- const void *data, pj_ssize_t *len, unsigned flags,
- const Pj_Inet_Addr &addr)
- {
- return pj_ioqueue_sendto(key_, op_key, data, len, flags,
- &addr, sizeof(addr));
- }
-
-#if PJ_HAS_TCP
- //
- // Start async connect()
- //
- pj_status_t connect(const Pj_Inet_Addr &addr)
- {
- return pj_ioqueue_connect(key_, &addr, sizeof(addr));
- }
-
- //
- // Start async accept().
- //
- pj_status_t accept( Pj_Async_Op *op_key,
- Pj_Socket *sock,
- Pj_Inet_Addr *local = NULL,
- Pj_Inet_Addr *remote = NULL)
- {
- int *addrlen = local ? &local->addrlen_ : NULL;
- return pj_ioqueue_accept( key_, op_key, &sock->sock_,
- local, remote, addrlen );
- }
-
-#endif
-
-protected:
- //////////////////
- // Overridables
- //////////////////
-
- //
- // Timeout callback.
- //
- virtual void on_timeout(int data)
- {
- }
-
- //
- // On read complete callback.
- //
- virtual void on_read_complete( Pj_Async_Op *op_key,
- pj_ssize_t bytes_read)
- {
- }
-
- //
- // On write complete callback.
- //
- virtual void on_write_complete( Pj_Async_Op *op_key,
- pj_ssize_t bytes_sent)
- {
- }
-
-#if PJ_HAS_TCP
- //
- // On connect complete callback.
- //
- virtual void on_connect_complete(pj_status_t status)
- {
- }
-
- //
- // On new connection callback.
- //
- virtual void on_accept_complete( Pj_Async_Op *op_key,
- pj_sock_t new_sock,
- pj_status_t status)
- {
- }
-
-#endif
-
-
-private:
- pj_ioqueue_key_t *key_;
- pj_timer_entry timer_;
-
- friend class Pj_Proactor;
- friend class Pj_Async_Op;
-
- //
- // Static timer callback.
- //
- static void timer_callback( pj_timer_heap_t *timer_heap,
- struct pj_timer_entry *entry)
- {
- Pj_Event_Handler *handler =
- (Pj_Event_Handler*) entry->user_data;
-
- handler->on_timeout(entry->id);
- }
-};
-
-inline bool Pj_Async_Op::is_pending()
-{
- return pj_ioqueue_is_pending(handler_->key_, this) != 0;
-}
-
-inline bool Pj_Async_Op::cancel(pj_ssize_t bytes_status)
-{
- return pj_ioqueue_post_completion(handler_->key_, this,
- bytes_status) == PJ_SUCCESS;
-}
-
-//////////////////////////////////////////////////////////////////////////////
-// Proactor
-//
-class Pj_Proactor : public Pj_Object
-{
-public:
- //
- // Default constructor, initializes to NULL.
- //
- Pj_Proactor()
- : ioq_(NULL), th_(NULL)
- {
- cb_.on_read_complete = &read_complete_cb;
- cb_.on_write_complete = &write_complete_cb;
- cb_.on_accept_complete = &accept_complete_cb;
- cb_.on_connect_complete = &connect_complete_cb;
- }
-
- //
- // Construct proactor.
- //
- Pj_Proactor( Pj_Pool *pool, pj_size_t max_fd,
- pj_size_t max_timer_entries )
- : ioq_(NULL), th_(NULL)
- {
- cb_.on_read_complete = &read_complete_cb;
- cb_.on_write_complete = &write_complete_cb;
- cb_.on_accept_complete = &accept_complete_cb;
- cb_.on_connect_complete = &connect_complete_cb;
-
- create(pool, max_fd, max_timer_entries);
- }
-
- //
- // Destructor.
- //
- ~Pj_Proactor()
- {
- destroy();
- }
-
- //
- // Create proactor.
- //
- pj_status_t create( Pj_Pool *pool, pj_size_t max_fd,
- pj_size_t timer_entry_count)
- {
- pj_status_t status;
-
- destroy();
-
- status = pj_ioqueue_create(pool->pool_(), max_fd, &ioq_);
- if (status != PJ_SUCCESS)
- return status;
-
- status = pj_timer_heap_create(pool->pool_(),
- timer_entry_count, &th_);
- if (status != PJ_SUCCESS) {
- pj_ioqueue_destroy(ioq_);
- ioq_ = NULL;
- return NULL;
- }
-
- return status;
- }
-
- //
- // Destroy proactor.
- //
- void destroy()
- {
- if (ioq_) {
- pj_ioqueue_destroy(ioq_);
- ioq_ = NULL;
- }
- if (th_) {
- pj_timer_heap_destroy(th_);
- th_ = NULL;
- }
- }
-
- //
- // Register handler.
- // This will call handler->get_socket_handle()
- //
- pj_status_t register_socket_handler(Pj_Pool *pool,
- Pj_Event_Handler *handler)
- {
- return pj_ioqueue_register_sock( pool->pool_(), ioq_,
- handler->get_socket_handle(),
- handler, &cb_, &handler->key_ );
- }
-
- //
- // Unregister handler.
- //
- static void unregister_handler(Pj_Event_Handler *handler)
- {
- if (handler->key_) {
- pj_ioqueue_unregister( handler->key_ );
- handler->key_ = NULL;
- }
- }
-
- //
- // Scheduler timer.
- //
- bool schedule_timer( Pj_Event_Handler *handler,
- const Pj_Time_Val &delay,
- int id=-1)
- {
- return schedule_timer(th_, handler, delay, id);
- }
-
- //
- // Cancel timer.
- //
- bool cancel_timer(Pj_Event_Handler *handler)
- {
- return pj_timer_heap_cancel(th_, &handler->timer_) == 1;
- }
-
- //
- // Handle events.
- //
- int handle_events(Pj_Time_Val *max_timeout)
- {
- Pj_Time_Val timeout(0, 0);
- int timer_count;
-
- timer_count = pj_timer_heap_poll( th_, &timeout );
-
- if (timeout.get_sec() < 0)
- timeout.sec = PJ_MAXINT32;
-
- /* If caller specifies maximum time to wait, then compare the value
- * with the timeout to wait from timer, and use the minimum value.
- */
- if (max_timeout && timeout >= *max_timeout) {
- timeout = *max_timeout;
- }
-
- /* Poll events in ioqueue. */
- int ioqueue_count;
-
- ioqueue_count = pj_ioqueue_poll(ioq_, &timeout);
- if (ioqueue_count < 0)
- return ioqueue_count;
-
- return ioqueue_count + timer_count;
- }
-
- //
- // Get the internal ioqueue object.
- //
- pj_ioqueue_t *get_io_queue()
- {
- return ioq_;
- }
-
- //
- // Get the internal timer heap object.
- //
- pj_timer_heap_t *get_timer_heap()
- {
- return th_;
- }
-
-private:
- pj_ioqueue_t *ioq_;
- pj_timer_heap_t *th_;
- pj_ioqueue_callback cb_;
-
- static bool schedule_timer( pj_timer_heap_t *timer,
- Pj_Event_Handler *handler,
- const Pj_Time_Val &delay,
- int id=-1)
- {
- handler->timer_.id = id;
- return pj_timer_heap_schedule(timer, &handler->timer_, &delay) == 0;
- }
-
-
- //
- // Static read completion callback.
- //
- static void read_complete_cb( pj_ioqueue_key_t *key,
- pj_ioqueue_op_key_t *op_key,
- pj_ssize_t bytes_read)
- {
- Pj_Event_Handler *handler =
- (Pj_Event_Handler*) pj_ioqueue_get_user_data(key);
-
- handler->on_read_complete((Pj_Async_Op*)op_key, bytes_read);
- }
-
- //
- // Static write completion callback.
- //
- static void write_complete_cb(pj_ioqueue_key_t *key,
- pj_ioqueue_op_key_t *op_key,
- pj_ssize_t bytes_sent)
- {
- Pj_Event_Handler *handler =
- (Pj_Event_Handler*) pj_ioqueue_get_user_data(key);
-
- handler->on_write_complete((Pj_Async_Op*)op_key, bytes_sent);
- }
-
- //
- // Static accept completion callback.
- //
- static void accept_complete_cb(pj_ioqueue_key_t *key,
- pj_ioqueue_op_key_t *op_key,
- pj_sock_t new_sock,
- pj_status_t status)
- {
- Pj_Event_Handler *handler =
- (Pj_Event_Handler*) pj_ioqueue_get_user_data(key);
-
- handler->on_accept_complete((Pj_Async_Op*)op_key, new_sock, status);
- }
-
- //
- // Static connect completion callback.
- //
- static void connect_complete_cb(pj_ioqueue_key_t *key,
- pj_status_t status)
- {
- Pj_Event_Handler *handler =
- (Pj_Event_Handler*) pj_ioqueue_get_user_data(key);
-
- handler->on_connect_complete(status);
- }
-
-};
-
-#endif /* __PJPP_PROACTOR_HPP__ */
-
+/* $Id$ */
+/*
+ * Copyright (C)2003-2006 Benny Prijono <benny@prijono.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#ifndef __PJPP_PROACTOR_HPP__
+#define __PJPP_PROACTOR_HPP__
+
+#include <pj/ioqueue.h>
+#include <pj++/pool.hpp>
+#include <pj++/sock.hpp>
+#include <pj++/timer.hpp>
+#include <pj/errno.h>
+
+class Pj_Proactor;
+class Pj_Event_Handler;
+
+
+//////////////////////////////////////////////////////////////////////////////
+// Asynchronous operation key.
+//
+// Applications may inheric this class to put their application
+// specific data.
+//
+class Pj_Async_Op : public pj_ioqueue_op_key_t
+{
+public:
+ //
+ // Construct with null handler.
+ // App must call set_handler() before use.
+ //
+ Pj_Async_Op()
+ : handler_(NULL)
+ {
+ pj_ioqueue_op_key_init(this, sizeof(*this));
+ }
+
+ //
+ // Constructor.
+ //
+ explicit Pj_Async_Op(Pj_Event_Handler *handler)
+ : handler_(handler)
+ {
+ pj_ioqueue_op_key_init(this, sizeof(*this));
+ }
+
+ //
+ // Set handler.
+ //
+ void set_handler(Pj_Event_Handler *handler)
+ {
+ handler_ = handler;
+ }
+
+ //
+ // Check whether operation is still pending for this key.
+ //
+ bool is_pending();
+
+ //
+ // Cancel the operation.
+ //
+ bool cancel(pj_ssize_t bytes_status=-PJ_ECANCELLED);
+
+protected:
+ Pj_Event_Handler *handler_;
+};
+
+
+//////////////////////////////////////////////////////////////////////////////
+// Event handler.
+//
+// Applications should inherit this class to receive various event
+// notifications.
+//
+// Applications should implement get_socket_handle().
+//
+class Pj_Event_Handler : public Pj_Object
+{
+ friend class Pj_Proactor;
+public:
+ //
+ // Default constructor.
+ //
+ Pj_Event_Handler()
+ : key_(NULL)
+ {
+ pj_memset(&timer_, 0, sizeof(timer_));
+ timer_.user_data = this;
+ timer_.cb = &timer_callback;
+ }
+
+ //
+ // Destroy.
+ //
+ virtual ~Pj_Event_Handler()
+ {
+ unregister();
+ }
+
+ //
+ // Unregister this handler from the ioqueue.
+ //
+ void unregister()
+ {
+ if (key_) {
+ pj_ioqueue_unregister(key_);
+ key_ = NULL;
+ }
+ }
+
+ //
+ // Get socket handle associated with this.
+ //
+ virtual pj_sock_t get_socket_handle()
+ {
+ return PJ_INVALID_SOCKET;
+ }
+
+ //
+ // Start async receive.
+ //
+ pj_status_t recv( Pj_Async_Op *op_key,
+ void *buf, pj_ssize_t *len,
+ unsigned flags)
+ {
+ return pj_ioqueue_recv( key_, op_key,
+ buf, len, flags);
+ }
+
+ //
+ // Start async recvfrom()
+ //
+ pj_status_t recvfrom( Pj_Async_Op *op_key,
+ void *buf, pj_ssize_t *len, unsigned flags,
+ Pj_Inet_Addr *addr)
+ {
+ addr->addrlen_ = sizeof(Pj_Inet_Addr);
+ return pj_ioqueue_recvfrom( key_, op_key, buf, len, flags,
+ addr, &addr->addrlen_ );
+ }
+
+ //
+ // Start async send()
+ //
+ pj_status_t send( Pj_Async_Op *op_key,
+ const void *data, pj_ssize_t *len,
+ unsigned flags)
+ {
+ return pj_ioqueue_send( key_, op_key, data, len, flags);
+ }
+
+ //
+ // Start async sendto()
+ //
+ pj_status_t sendto( Pj_Async_Op *op_key,
+ const void *data, pj_ssize_t *len, unsigned flags,
+ const Pj_Inet_Addr &addr)
+ {
+ return pj_ioqueue_sendto(key_, op_key, data, len, flags,
+ &addr, sizeof(addr));
+ }
+
+#if PJ_HAS_TCP
+ //
+ // Start async connect()
+ //
+ pj_status_t connect(const Pj_Inet_Addr &addr)
+ {
+ return pj_ioqueue_connect(key_, &addr, sizeof(addr));
+ }
+
+ //
+ // Start async accept().
+ //
+ pj_status_t accept( Pj_Async_Op *op_key,
+ Pj_Socket *sock,
+ Pj_Inet_Addr *local = NULL,
+ Pj_Inet_Addr *remote = NULL)
+ {
+ int *addrlen = local ? &local->addrlen_ : NULL;
+ return pj_ioqueue_accept( key_, op_key, &sock->sock_,
+ local, remote, addrlen );
+ }
+
+#endif
+
+protected:
+ //////////////////
+ // Overridables
+ //////////////////
+
+ //
+ // Timeout callback.
+ //
+ virtual void on_timeout(int data)
+ {
+ }
+
+ //
+ // On read complete callback.
+ //
+ virtual void on_read_complete( Pj_Async_Op *op_key,
+ pj_ssize_t bytes_read)
+ {
+ }
+
+ //
+ // On write complete callback.
+ //
+ virtual void on_write_complete( Pj_Async_Op *op_key,
+ pj_ssize_t bytes_sent)
+ {
+ }
+
+#if PJ_HAS_TCP
+ //
+ // On connect complete callback.
+ //
+ virtual void on_connect_complete(pj_status_t status)
+ {
+ }
+
+ //
+ // On new connection callback.
+ //
+ virtual void on_accept_complete( Pj_Async_Op *op_key,
+ pj_sock_t new_sock,
+ pj_status_t status)
+ {
+ }
+
+#endif
+
+
+private:
+ pj_ioqueue_key_t *key_;
+ pj_timer_entry timer_;
+
+ friend class Pj_Proactor;
+ friend class Pj_Async_Op;
+
+ //
+ // Static timer callback.
+ //
+ static void timer_callback( pj_timer_heap_t *timer_heap,
+ struct pj_timer_entry *entry)
+ {
+ Pj_Event_Handler *handler =
+ (Pj_Event_Handler*) entry->user_data;
+
+ handler->on_timeout(entry->id);
+ }
+};
+
+inline bool Pj_Async_Op::is_pending()
+{
+ return pj_ioqueue_is_pending(handler_->key_, this) != 0;
+}
+
+inline bool Pj_Async_Op::cancel(pj_ssize_t bytes_status)
+{
+ return pj_ioqueue_post_completion(handler_->key_, this,
+ bytes_status) == PJ_SUCCESS;
+}
+
+//////////////////////////////////////////////////////////////////////////////
+// Proactor
+//
+class Pj_Proactor : public Pj_Object
+{
+public:
+ //
+ // Default constructor, initializes to NULL.
+ //
+ Pj_Proactor()
+ : ioq_(NULL), th_(NULL)
+ {
+ cb_.on_read_complete = &read_complete_cb;
+ cb_.on_write_complete = &write_complete_cb;
+ cb_.on_accept_complete = &accept_complete_cb;
+ cb_.on_connect_complete = &connect_complete_cb;
+ }
+
+ //
+ // Construct proactor.
+ //
+ Pj_Proactor( Pj_Pool *pool, pj_size_t max_fd,
+ pj_size_t max_timer_entries )
+ : ioq_(NULL), th_(NULL)
+ {
+ cb_.on_read_complete = &read_complete_cb;
+ cb_.on_write_complete = &write_complete_cb;
+ cb_.on_accept_complete = &accept_complete_cb;
+ cb_.on_connect_complete = &connect_complete_cb;
+
+ create(pool, max_fd, max_timer_entries);
+ }
+
+ //
+ // Destructor.
+ //
+ ~Pj_Proactor()
+ {
+ destroy();
+ }
+
+ //
+ // Create proactor.
+ //
+ pj_status_t create( Pj_Pool *pool, pj_size_t max_fd,
+ pj_size_t timer_entry_count)
+ {
+ pj_status_t status;
+
+ destroy();
+
+ status = pj_ioqueue_create(pool->pool_(), max_fd, &ioq_);
+ if (status != PJ_SUCCESS)
+ return status;
+
+ status = pj_timer_heap_create(pool->pool_(),
+ timer_entry_count, &th_);
+ if (status != PJ_SUCCESS) {
+ pj_ioqueue_destroy(ioq_);
+ ioq_ = NULL;
+ return NULL;
+ }
+
+ return status;
+ }
+
+ //
+ // Destroy proactor.
+ //
+ void destroy()
+ {
+ if (ioq_) {
+ pj_ioqueue_destroy(ioq_);
+ ioq_ = NULL;
+ }
+ if (th_) {
+ pj_timer_heap_destroy(th_);
+ th_ = NULL;
+ }
+ }
+
+ //
+ // Register handler.
+ // This will call handler->get_socket_handle()
+ //
+ pj_status_t register_socket_handler(Pj_Pool *pool,
+ Pj_Event_Handler *handler)
+ {
+ return pj_ioqueue_register_sock( pool->pool_(), ioq_,
+ handler->get_socket_handle(),
+ handler, &cb_, &handler->key_ );
+ }
+
+ //
+ // Unregister handler.
+ //
+ static void unregister_handler(Pj_Event_Handler *handler)
+ {
+ if (handler->key_) {
+ pj_ioqueue_unregister( handler->key_ );
+ handler->key_ = NULL;
+ }
+ }
+
+ //
+ // Scheduler timer.
+ //
+ bool schedule_timer( Pj_Event_Handler *handler,
+ const Pj_Time_Val &delay,
+ int id=-1)
+ {
+ return schedule_timer(th_, handler, delay, id);
+ }
+
+ //
+ // Cancel timer.
+ //
+ bool cancel_timer(Pj_Event_Handler *handler)
+ {
+ return pj_timer_heap_cancel(th_, &handler->timer_) == 1;
+ }
+
+ //
+ // Handle events.
+ //
+ int handle_events(Pj_Time_Val *max_timeout)
+ {
+ Pj_Time_Val timeout(0, 0);
+ int timer_count;
+
+ timer_count = pj_timer_heap_poll( th_, &timeout );
+
+ if (timeout.get_sec() < 0)
+ timeout.sec = PJ_MAXINT32;
+
+ /* If caller specifies maximum time to wait, then compare the value
+ * with the timeout to wait from timer, and use the minimum value.
+ */
+ if (max_timeout && timeout >= *max_timeout) {
+ timeout = *max_timeout;
+ }
+
+ /* Poll events in ioqueue. */
+ int ioqueue_count;
+
+ ioqueue_count = pj_ioqueue_poll(ioq_, &timeout);
+ if (ioqueue_count < 0)
+ return ioqueue_count;
+
+ return ioqueue_count + timer_count;
+ }
+
+ //
+ // Get the internal ioqueue object.
+ //
+ pj_ioqueue_t *get_io_queue()
+ {
+ return ioq_;
+ }
+
+ //
+ // Get the internal timer heap object.
+ //
+ pj_timer_heap_t *get_timer_heap()
+ {
+ return th_;
+ }
+
+private:
+ pj_ioqueue_t *ioq_;
+ pj_timer_heap_t *th_;
+ pj_ioqueue_callback cb_;
+
+ static bool schedule_timer( pj_timer_heap_t *timer,
+ Pj_Event_Handler *handler,
+ const Pj_Time_Val &delay,
+ int id=-1)
+ {
+ handler->timer_.id = id;
+ return pj_timer_heap_schedule(timer, &handler->timer_, &delay) == 0;
+ }
+
+
+ //
+ // Static read completion callback.
+ //
+ static void read_complete_cb( pj_ioqueue_key_t *key,
+ pj_ioqueue_op_key_t *op_key,
+ pj_ssize_t bytes_read)
+ {
+ Pj_Event_Handler *handler =
+ (Pj_Event_Handler*) pj_ioqueue_get_user_data(key);
+
+ handler->on_read_complete((Pj_Async_Op*)op_key, bytes_read);
+ }
+
+ //
+ // Static write completion callback.
+ //
+ static void write_complete_cb(pj_ioqueue_key_t *key,
+ pj_ioqueue_op_key_t *op_key,
+ pj_ssize_t bytes_sent)
+ {
+ Pj_Event_Handler *handler =
+ (Pj_Event_Handler*) pj_ioqueue_get_user_data(key);
+
+ handler->on_write_complete((Pj_Async_Op*)op_key, bytes_sent);
+ }
+
+ //
+ // Static accept completion callback.
+ //
+ static void accept_complete_cb(pj_ioqueue_key_t *key,
+ pj_ioqueue_op_key_t *op_key,
+ pj_sock_t new_sock,
+ pj_status_t status)
+ {
+ Pj_Event_Handler *handler =
+ (Pj_Event_Handler*) pj_ioqueue_get_user_data(key);
+
+ handler->on_accept_complete((Pj_Async_Op*)op_key, new_sock, status);
+ }
+
+ //
+ // Static connect completion callback.
+ //
+ static void connect_complete_cb(pj_ioqueue_key_t *key,
+ pj_status_t status)
+ {
+ Pj_Event_Handler *handler =
+ (Pj_Event_Handler*) pj_ioqueue_get_user_data(key);
+
+ handler->on_connect_complete(status);
+ }
+
+};
+
+#endif /* __PJPP_PROACTOR_HPP__ */
+
diff --git a/pjlib/include/pj++/scanner.hpp b/pjlib/include/pj++/scanner.hpp
index 9e2d528f..e9ffe304 100644
--- a/pjlib/include/pj++/scanner.hpp
+++ b/pjlib/include/pj++/scanner.hpp
@@ -1,189 +1,189 @@
-/* $Id$ */
-/*
- * Copyright (C)2003-2006 Benny Prijono <benny@prijono.org>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
-#ifndef __PJPP_SCANNER_HPP__
-#define __PJPP_SCANNER_HPP__
-
-#include <pjlib-util/scanner.h>
-#include <pj++/string.hpp>
-
-class Pj_Char_Spec
-{
-public:
- Pj_Char_Spec() { pj_cs_init(cs__); }
-
- void set(int c) { pj_cs_set(cs__, c); }
- void add_range(int begin, int end) { pj_cs_add_range(cs__, begin, end); }
- void add_alpha() { pj_cs_add_alpha(cs__); }
- void add_num() { pj_cs_add_num(cs__); }
- void add_str(const char *str) { pj_cs_add_str(cs__, str); }
- void del_range(int begin, int end) { pj_cs_del_range(cs__, begin, end); }
- void del_str(const char *str) { pj_cs_del_str(cs__, str); }
- void invert() { pj_cs_invert(cs__); }
- int match(int c) { return pj_cs_match(cs__, c); }
-
- pj_char_spec_element_t *cs_()
- {
- return cs__;
- }
-
- const pj_char_spec_element_t *cs_() const
- {
- return cs__;
- }
-
-private:
- pj_char_spec cs__;
-};
-
-class Pj_Scanner
-{
-public:
- Pj_Scanner() {}
-
- enum
- {
- SYNTAX_ERROR = 101
- };
- static void syntax_error_handler_throw_pj(pj_scanner *);
-
- typedef pj_scan_state State;
-
- void init(char *buf, int len, unsigned options=PJ_SCAN_AUTOSKIP_WS,
- pj_syn_err_func_ptr callback = &syntax_error_handler_throw_pj)
- {
- pj_scan_init(&scanner_, buf, len, options, callback);
- }
-
- void fini()
- {
- pj_scan_fini(&scanner_);
- }
-
- int eof() const
- {
- return pj_scan_is_eof(&scanner_);
- }
-
- int peek_char() const
- {
- return *scanner_.curptr;
- }
-
- int peek(const Pj_Char_Spec *cs, Pj_String *out)
- {
- return pj_scan_peek(&scanner_, cs->cs_(), out);
- }
-
- int peek_n(pj_size_t len, Pj_String *out)
- {
- return pj_scan_peek_n(&scanner_, len, out);
- }
-
- int peek_until(const Pj_Char_Spec *cs, Pj_String *out)
- {
- return pj_scan_peek_until(&scanner_, cs->cs_(), out);
- }
-
- void get(const Pj_Char_Spec *cs, Pj_String *out)
- {
- pj_scan_get(&scanner_, cs->cs_(), out);
- }
-
- void get_n(unsigned N, Pj_String *out)
- {
- pj_scan_get_n(&scanner_, N, out);
- }
-
- int get_char()
- {
- return pj_scan_get_char(&scanner_);
- }
-
- void get_quote(int begin_quote, int end_quote, Pj_String *out)
- {
- pj_scan_get_quote(&scanner_, begin_quote, end_quote, out);
- }
-
- void get_newline()
- {
- pj_scan_get_newline(&scanner_);
- }
-
- void get_until(const Pj_Char_Spec *cs, Pj_String *out)
- {
- pj_scan_get_until(&scanner_, cs->cs_(), out);
- }
-
- void get_until_ch(int until_ch, Pj_String *out)
- {
- pj_scan_get_until_ch(&scanner_, until_ch, out);
- }
-
- void get_until_chr(const char *spec, Pj_String *out)
- {
- pj_scan_get_until_chr(&scanner_, spec, out);
- }
-
- void advance_n(unsigned N, bool skip_ws=true)
- {
- pj_scan_advance_n(&scanner_, N, skip_ws);
- }
-
- int strcmp(const char *s, int len)
- {
- return pj_scan_strcmp(&scanner_, s, len);
- }
-
- int stricmp(const char *s, int len)
- {
- return pj_scan_stricmp(&scanner_, s, len);
- }
-
- void skip_ws()
- {
- pj_scan_skip_whitespace(&scanner_);
- }
-
- void save_state(State *state)
- {
- pj_scan_save_state(&scanner_, state);
- }
-
- void restore_state(State *state)
- {
- pj_scan_restore_state(&scanner_, state);
- }
-
- int get_pos_line() const
- {
- return scanner_.line;
- }
-
- int get_pos_col() const
- {
- return scanner_.col;
- }
-
-
-private:
- pj_scanner scanner_;
-};
-
-#endif /* __PJPP_SCANNER_HPP__ */
-
+/* $Id$ */
+/*
+ * Copyright (C)2003-2006 Benny Prijono <benny@prijono.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#ifndef __PJPP_SCANNER_HPP__
+#define __PJPP_SCANNER_HPP__
+
+#include <pjlib-util/scanner.h>
+#include <pj++/string.hpp>
+
+class Pj_Char_Spec
+{
+public:
+ Pj_Char_Spec() { pj_cs_init(cs__); }
+
+ void set(int c) { pj_cs_set(cs__, c); }
+ void add_range(int begin, int end) { pj_cs_add_range(cs__, begin, end); }
+ void add_alpha() { pj_cs_add_alpha(cs__); }
+ void add_num() { pj_cs_add_num(cs__); }
+ void add_str(const char *str) { pj_cs_add_str(cs__, str); }
+ void del_range(int begin, int end) { pj_cs_del_range(cs__, begin, end); }
+ void del_str(const char *str) { pj_cs_del_str(cs__, str); }
+ void invert() { pj_cs_invert(cs__); }
+ int match(int c) { return pj_cs_match(cs__, c); }
+
+ pj_char_spec_element_t *cs_()
+ {
+ return cs__;
+ }
+
+ const pj_char_spec_element_t *cs_() const
+ {
+ return cs__;
+ }
+
+private:
+ pj_char_spec cs__;
+};
+
+class Pj_Scanner
+{
+public:
+ Pj_Scanner() {}
+
+ enum
+ {
+ SYNTAX_ERROR = 101
+ };
+ static void syntax_error_handler_throw_pj(pj_scanner *);
+
+ typedef pj_scan_state State;
+
+ void init(char *buf, int len, unsigned options=PJ_SCAN_AUTOSKIP_WS,
+ pj_syn_err_func_ptr callback = &syntax_error_handler_throw_pj)
+ {
+ pj_scan_init(&scanner_, buf, len, options, callback);
+ }
+
+ void fini()
+ {
+ pj_scan_fini(&scanner_);
+ }
+
+ int eof() const
+ {
+ return pj_scan_is_eof(&scanner_);
+ }
+
+ int peek_char() const
+ {
+ return *scanner_.curptr;
+ }
+
+ int peek(const Pj_Char_Spec *cs, Pj_String *out)
+ {
+ return pj_scan_peek(&scanner_, cs->cs_(), out);
+ }
+
+ int peek_n(pj_size_t len, Pj_String *out)
+ {
+ return pj_scan_peek_n(&scanner_, len, out);
+ }
+
+ int peek_until(const Pj_Char_Spec *cs, Pj_String *out)
+ {
+ return pj_scan_peek_until(&scanner_, cs->cs_(), out);
+ }
+
+ void get(const Pj_Char_Spec *cs, Pj_String *out)
+ {
+ pj_scan_get(&scanner_, cs->cs_(), out);
+ }
+
+ void get_n(unsigned N, Pj_String *out)
+ {
+ pj_scan_get_n(&scanner_, N, out);
+ }
+
+ int get_char()
+ {
+ return pj_scan_get_char(&scanner_);
+ }
+
+ void get_quote(int begin_quote, int end_quote, Pj_String *out)
+ {
+ pj_scan_get_quote(&scanner_, begin_quote, end_quote, out);
+ }
+
+ void get_newline()
+ {
+ pj_scan_get_newline(&scanner_);
+ }
+
+ void get_until(const Pj_Char_Spec *cs, Pj_String *out)
+ {
+ pj_scan_get_until(&scanner_, cs->cs_(), out);
+ }
+
+ void get_until_ch(int until_ch, Pj_String *out)
+ {
+ pj_scan_get_until_ch(&scanner_, until_ch, out);
+ }
+
+ void get_until_chr(const char *spec, Pj_String *out)
+ {
+ pj_scan_get_until_chr(&scanner_, spec, out);
+ }
+
+ void advance_n(unsigned N, bool skip_ws=true)
+ {
+ pj_scan_advance_n(&scanner_, N, skip_ws);
+ }
+
+ int strcmp(const char *s, int len)
+ {
+ return pj_scan_strcmp(&scanner_, s, len);
+ }
+
+ int stricmp(const char *s, int len)
+ {
+ return pj_scan_stricmp(&scanner_, s, len);
+ }
+
+ void skip_ws()
+ {
+ pj_scan_skip_whitespace(&scanner_);
+ }
+
+ void save_state(State *state)
+ {
+ pj_scan_save_state(&scanner_, state);
+ }
+
+ void restore_state(State *state)
+ {
+ pj_scan_restore_state(&scanner_, state);
+ }
+
+ int get_pos_line() const
+ {
+ return scanner_.line;
+ }
+
+ int get_pos_col() const
+ {
+ return scanner_.col;
+ }
+
+
+private:
+ pj_scanner scanner_;
+};
+
+#endif /* __PJPP_SCANNER_HPP__ */
+
diff --git a/pjlib/include/pj++/sock.hpp b/pjlib/include/pj++/sock.hpp
index c0813e1e..ab86f77a 100644
--- a/pjlib/include/pj++/sock.hpp
+++ b/pjlib/include/pj++/sock.hpp
@@ -1,443 +1,443 @@
-/* $Id$ */
-/*
- * Copyright (C)2003-2006 Benny Prijono <benny@prijono.org>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
-#ifndef __PJPP_SOCK_HPP__
-#define __PJPP_SOCK_HPP__
-
-#include <pj/sock.h>
-#include <pj/string.h>
-
-class Pj_Event_Handler;
-
-//
-// Base class for address.
-//
-class Pj_Addr
-{
-};
-
-//
-// Internet address.
-//
-class Pj_Inet_Addr : public pj_sockaddr_in, public Pj_Addr
-{
-public:
- //
- // Get port number.
- //
- pj_uint16_t get_port_number() const
- {
- return pj_sockaddr_in_get_port(this);
- }
-
- //
- // Set port number.
- //
- void set_port_number(pj_uint16_t port)
- {
- sin_family = PJ_AF_INET;
- pj_sockaddr_in_set_port(this, port);
- }
-
- //
- // Get IP address.
- //
- pj_uint32_t get_ip_address() const
- {
- return pj_sockaddr_in_get_addr(this).s_addr;
- }
-
- //
- // Get address string.
- //
- const char *get_address() const
- {
- return pj_inet_ntoa(sin_addr);
- }
-
- //
- // Set IP address.
- //
- void set_ip_address(pj_uint32_t addr)
- {
- sin_family = PJ_AF_INET;
- pj_sockaddr_in_set_addr(this, addr);
- }
-
- //
- // Set address.
- //
- pj_status_t set_address(const pj_str_t *addr)
- {
- return pj_sockaddr_in_set_str_addr(this, addr);
- }
-
- //
- // Set address.
- //
- pj_status_t set_address(const char *addr)
- {
- pj_str_t s;
- return pj_sockaddr_in_set_str_addr(this, pj_cstr(&s, addr));
- }
-
- //
- // Compare for equality.
- //
- bool operator==(const Pj_Inet_Addr &rhs) const
- {
- return sin_family == rhs.sin_family &&
- sin_addr.s_addr == rhs.sin_addr.s_addr &&
- sin_port == rhs.sin_port;
- }
-
-private:
- //
- // Dummy length used in pj_ioqueue_recvfrom() etc
- //
- friend class Pj_Event_Handler;
- friend class Pj_Socket;
- friend class Pj_Sock_Stream;
- friend class Pj_Sock_Dgram;
-
- int addrlen_;
-};
-
-
-//
-// Socket base class.
-//
-// Note:
-// socket will not automatically be closed on destructor.
-//
-class Pj_Socket
-{
-public:
- //
- // Default constructor.
- //
- Pj_Socket()
- : sock_(PJ_INVALID_SOCKET)
- {
- }
-
- //
- // Initialize from a socket handle.
- //
- explicit Pj_Socket(pj_sock_t sock)
- : sock_(sock)
- {
- }
-
- //
- // Copy constructor.
- //
- Pj_Socket(const Pj_Socket &rhs)
- : sock_(rhs.sock_)
- {
- }
-
- //
- // Destructor will not close the socket.
- // You must call close() explicitly.
- //
- ~Pj_Socket()
- {
- }
-
- //
- // Set socket handle.
- //
- void set_handle(pj_sock_t sock)
- {
- sock_ = sock;
- }
-
- //
- // Get socket handle.
- //
- pj_sock_t get_handle() const
- {
- return sock_;
- }
-
- //
- // Get socket handle.
- //
- pj_sock_t& get_handle()
- {
- return sock_;
- }
-
- //
- // See if the socket is valid.
- //
- bool is_valid() const
- {
- return sock_ != PJ_INVALID_SOCKET;
- }
-
- //
- // Create the socket.
- //
- pj_status_t create(int af, int type, int proto)
- {
- return pj_sock_socket(af, type, proto, &sock_);
- }
-
- //
- // Bind socket.
- //
- pj_status_t bind(const Pj_Inet_Addr &addr)
- {
- return pj_sock_bind(sock_, &addr, sizeof(Pj_Inet_Addr));
- }
-
- //
- // Close socket.
- //
- pj_status_t close()
- {
- pj_sock_close(sock_);
- }
-
- //
- // Get peer socket name.
- //
- pj_status_t getpeername(Pj_Inet_Addr *addr)
- {
- return pj_sock_getpeername(sock_, addr, &addr->addrlen_);
- }
-
- //
- // getsockname
- //
- pj_status_t getsockname(Pj_Inet_Addr *addr)
- {
- return pj_sock_getsockname(sock_, addr, &addr->addrlen_);
- }
-
- //
- // getsockopt.
- //
- pj_status_t getsockopt(int level, int optname,
- void *optval, int *optlen)
- {
- return pj_sock_getsockopt(sock_, level, optname, optval, optlen);
- }
-
- //
- // setsockopt
- //
- pj_status_t setsockopt(int level, int optname,
- const void *optval, int optlen)
- {
- return pj_sock_setsockopt(sock_, level, optname, optval, optlen);
- }
-
- //
- // receive data.
- //
- pj_ssize_t recv(void *buf, pj_size_t len, int flag = 0)
- {
- pj_ssize_t bytes = len;
- if (pj_sock_recv(sock_, buf, &bytes, flag) != PJ_SUCCESS)
- return -1;
- return bytes;
- }
-
- //
- // send data.
- //
- pj_ssize_t send(const void *buf, pj_ssize_t len, int flag = 0)
- {
- pj_ssize_t bytes = len;
- if (pj_sock_send(sock_, buf, &bytes, flag) != PJ_SUCCESS)
- return -1;
- return bytes;
- }
-
- //
- // connect.
- //
- pj_status_t connect(const Pj_Inet_Addr &addr)
- {
- return pj_sock_connect(sock_, &addr, sizeof(Pj_Inet_Addr));
- }
-
- //
- // assignment.
- //
- Pj_Socket &operator=(const Pj_Socket &rhs)
- {
- sock_ = rhs.sock_;
- return *this;
- }
-
-protected:
- friend class Pj_Event_Handler;
- pj_sock_t sock_;
-};
-
-
-#if PJ_HAS_TCP
-//
-// Stream socket.
-//
-class Pj_Sock_Stream : public Pj_Socket
-{
-public:
- //
- // Default constructor.
- //
- Pj_Sock_Stream()
- {
- }
-
- //
- // Initialize from a socket handle.
- //
- explicit Pj_Sock_Stream(pj_sock_t sock)
- : Pj_Socket(sock)
- {
- }
-
- //
- // Copy constructor.
- //
- Pj_Sock_Stream(const Pj_Sock_Stream &rhs) : Pj_Socket(rhs)
- {
- }
-
- //
- // Assignment.
- //
- Pj_Sock_Stream &operator=(const Pj_Sock_Stream &rhs)
- {
- sock_ = rhs.sock_;
- return *this;
- }
-
- //
- // listen()
- //
- pj_status_t listen(int backlog = 5)
- {
- return pj_sock_listen(sock_, backlog);
- }
-
- //
- // blocking accept()
- //
- Pj_Sock_Stream accept(Pj_Inet_Addr *remote_addr = NULL)
- {
- pj_sock_t newsock;
- int *addrlen = remote_addr ? &remote_addr->addrlen_ : NULL;
- pj_status_t status;
-
- status = pj_sock_accept(sock_, &newsock, remote_addr, addrlen);
- if (status != PJ_SUCCESS)
- return Pj_Sock_Stream(-1);
-
- return Pj_Sock_Stream(newsock);
- }
-
- //
- // shutdown()
- //
- pj_status_t shutdown(int how = PJ_SHUT_RDWR)
- {
- return pj_sock_shutdown(sock_, how);
- }
-
-};
-#endif
-
-//
-// Datagram socket.
-//
-class Pj_Sock_Dgram : public Pj_Socket
-{
-public:
- //
- // Default constructor.
- //
- Pj_Sock_Dgram()
- {
- }
-
- //
- // Initialize from a socket handle.
- //
- explicit Pj_Sock_Dgram(pj_sock_t sock)
- : Pj_Socket(sock)
- {
- }
-
- //
- // Copy constructor.
- //
- Pj_Sock_Dgram(const Pj_Sock_Dgram &rhs)
- : Pj_Socket(rhs)
- {
- }
-
- //
- // Assignment.
- //
- Pj_Sock_Dgram &operator=(const Pj_Sock_Dgram &rhs)
- {
- Pj_Socket::operator =(rhs);
- return *this;
- }
-
- //
- // recvfrom()
- //
- pj_ssize_t recvfrom( void *buf, pj_size_t len, int flag = 0,
- Pj_Inet_Addr *fromaddr = NULL)
- {
- pj_ssize_t bytes = len;
- int *addrlen = fromaddr ? &fromaddr->addrlen_ : NULL;
- if (pj_sock_recvfrom( sock_, buf, &bytes, flag,
- fromaddr, addrlen) != PJ_SUCCESS)
- {
- return -1;
- }
- return bytes;
- }
-
- //
- // sendto()
- //
- pj_ssize_t sendto( const void *buf, pj_size_t len, int flag,
- const Pj_Inet_Addr &addr)
- {
- pj_ssize_t bytes = len;
- if (pj_sock_sendto( sock_, buf, &bytes, flag,
- &addr, sizeof(pj_sockaddr_in)) != PJ_SUCCESS)
- {
- return -1;
- }
- return bytes;
- }
-};
-
-
-#endif /* __PJPP_SOCK_HPP__ */
-
+/* $Id$ */
+/*
+ * Copyright (C)2003-2006 Benny Prijono <benny@prijono.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#ifndef __PJPP_SOCK_HPP__
+#define __PJPP_SOCK_HPP__
+
+#include <pj/sock.h>
+#include <pj/string.h>
+
+class Pj_Event_Handler;
+
+//
+// Base class for address.
+//
+class Pj_Addr
+{
+};
+
+//
+// Internet address.
+//
+class Pj_Inet_Addr : public pj_sockaddr_in, public Pj_Addr
+{
+public:
+ //
+ // Get port number.
+ //
+ pj_uint16_t get_port_number() const
+ {
+ return pj_sockaddr_in_get_port(this);
+ }
+
+ //
+ // Set port number.
+ //
+ void set_port_number(pj_uint16_t port)
+ {
+ sin_family = PJ_AF_INET;
+ pj_sockaddr_in_set_port(this, port);
+ }
+
+ //
+ // Get IP address.
+ //
+ pj_uint32_t get_ip_address() const
+ {
+ return pj_sockaddr_in_get_addr(this).s_addr;
+ }
+
+ //
+ // Get address string.
+ //
+ const char *get_address() const
+ {
+ return pj_inet_ntoa(sin_addr);
+ }
+
+ //
+ // Set IP address.
+ //
+ void set_ip_address(pj_uint32_t addr)
+ {
+ sin_family = PJ_AF_INET;
+ pj_sockaddr_in_set_addr(this, addr);
+ }
+
+ //
+ // Set address.
+ //
+ pj_status_t set_address(const pj_str_t *addr)
+ {
+ return pj_sockaddr_in_set_str_addr(this, addr);
+ }
+
+ //
+ // Set address.
+ //
+ pj_status_t set_address(const char *addr)
+ {
+ pj_str_t s;
+ return pj_sockaddr_in_set_str_addr(this, pj_cstr(&s, addr));
+ }
+
+ //
+ // Compare for equality.
+ //
+ bool operator==(const Pj_Inet_Addr &rhs) const
+ {
+ return sin_family == rhs.sin_family &&
+ sin_addr.s_addr == rhs.sin_addr.s_addr &&
+ sin_port == rhs.sin_port;
+ }
+
+private:
+ //
+ // Dummy length used in pj_ioqueue_recvfrom() etc
+ //
+ friend class Pj_Event_Handler;
+ friend class Pj_Socket;
+ friend class Pj_Sock_Stream;
+ friend class Pj_Sock_Dgram;
+
+ int addrlen_;
+};
+
+
+//
+// Socket base class.
+//
+// Note:
+// socket will not automatically be closed on destructor.
+//
+class Pj_Socket
+{
+public:
+ //
+ // Default constructor.
+ //
+ Pj_Socket()
+ : sock_(PJ_INVALID_SOCKET)
+ {
+ }
+
+ //
+ // Initialize from a socket handle.
+ //
+ explicit Pj_Socket(pj_sock_t sock)
+ : sock_(sock)
+ {
+ }
+
+ //
+ // Copy constructor.
+ //
+ Pj_Socket(const Pj_Socket &rhs)
+ : sock_(rhs.sock_)
+ {
+ }
+
+ //
+ // Destructor will not close the socket.
+ // You must call close() explicitly.
+ //
+ ~Pj_Socket()
+ {
+ }
+
+ //
+ // Set socket handle.
+ //
+ void set_handle(pj_sock_t sock)
+ {
+ sock_ = sock;
+ }
+
+ //
+ // Get socket handle.
+ //
+ pj_sock_t get_handle() const
+ {
+ return sock_;
+ }
+
+ //
+ // Get socket handle.
+ //
+ pj_sock_t& get_handle()
+ {
+ return sock_;
+ }
+
+ //
+ // See if the socket is valid.
+ //
+ bool is_valid() const
+ {
+ return sock_ != PJ_INVALID_SOCKET;
+ }
+
+ //
+ // Create the socket.
+ //
+ pj_status_t create(int af, int type, int proto)
+ {
+ return pj_sock_socket(af, type, proto, &sock_);
+ }
+
+ //
+ // Bind socket.
+ //
+ pj_status_t bind(const Pj_Inet_Addr &addr)
+ {
+ return pj_sock_bind(sock_, &addr, sizeof(Pj_Inet_Addr));
+ }
+
+ //
+ // Close socket.
+ //
+ pj_status_t close()
+ {
+ pj_sock_close(sock_);
+ }
+
+ //
+ // Get peer socket name.
+ //
+ pj_status_t getpeername(Pj_Inet_Addr *addr)
+ {
+ return pj_sock_getpeername(sock_, addr, &addr->addrlen_);
+ }
+
+ //
+ // getsockname
+ //
+ pj_status_t getsockname(Pj_Inet_Addr *addr)
+ {
+ return pj_sock_getsockname(sock_, addr, &addr->addrlen_);
+ }
+
+ //
+ // getsockopt.
+ //
+ pj_status_t getsockopt(int level, int optname,
+ void *optval, int *optlen)
+ {
+ return pj_sock_getsockopt(sock_, level, optname, optval, optlen);
+ }
+
+ //
+ // setsockopt
+ //
+ pj_status_t setsockopt(int level, int optname,
+ const void *optval, int optlen)
+ {
+ return pj_sock_setsockopt(sock_, level, optname, optval, optlen);
+ }
+
+ //
+ // receive data.
+ //
+ pj_ssize_t recv(void *buf, pj_size_t len, int flag = 0)
+ {
+ pj_ssize_t bytes = len;
+ if (pj_sock_recv(sock_, buf, &bytes, flag) != PJ_SUCCESS)
+ return -1;
+ return bytes;
+ }
+
+ //
+ // send data.
+ //
+ pj_ssize_t send(const void *buf, pj_ssize_t len, int flag = 0)
+ {
+ pj_ssize_t bytes = len;
+ if (pj_sock_send(sock_, buf, &bytes, flag) != PJ_SUCCESS)
+ return -1;
+ return bytes;
+ }
+
+ //
+ // connect.
+ //
+ pj_status_t connect(const Pj_Inet_Addr &addr)
+ {
+ return pj_sock_connect(sock_, &addr, sizeof(Pj_Inet_Addr));
+ }
+
+ //
+ // assignment.
+ //
+ Pj_Socket &operator=(const Pj_Socket &rhs)
+ {
+ sock_ = rhs.sock_;
+ return *this;
+ }
+
+protected:
+ friend class Pj_Event_Handler;
+ pj_sock_t sock_;
+};
+
+
+#if PJ_HAS_TCP
+//
+// Stream socket.
+//
+class Pj_Sock_Stream : public Pj_Socket
+{
+public:
+ //
+ // Default constructor.
+ //
+ Pj_Sock_Stream()
+ {
+ }
+
+ //
+ // Initialize from a socket handle.
+ //
+ explicit Pj_Sock_Stream(pj_sock_t sock)
+ : Pj_Socket(sock)
+ {
+ }
+
+ //
+ // Copy constructor.
+ //
+ Pj_Sock_Stream(const Pj_Sock_Stream &rhs) : Pj_Socket(rhs)
+ {
+ }
+
+ //
+ // Assignment.
+ //
+ Pj_Sock_Stream &operator=(const Pj_Sock_Stream &rhs)
+ {
+ sock_ = rhs.sock_;
+ return *this;
+ }
+
+ //
+ // listen()
+ //
+ pj_status_t listen(int backlog = 5)
+ {
+ return pj_sock_listen(sock_, backlog);
+ }
+
+ //
+ // blocking accept()
+ //
+ Pj_Sock_Stream accept(Pj_Inet_Addr *remote_addr = NULL)
+ {
+ pj_sock_t newsock;
+ int *addrlen = remote_addr ? &remote_addr->addrlen_ : NULL;
+ pj_status_t status;
+
+ status = pj_sock_accept(sock_, &newsock, remote_addr, addrlen);
+ if (status != PJ_SUCCESS)
+ return Pj_Sock_Stream(-1);
+
+ return Pj_Sock_Stream(newsock);
+ }
+
+ //
+ // shutdown()
+ //
+ pj_status_t shutdown(int how = PJ_SHUT_RDWR)
+ {
+ return pj_sock_shutdown(sock_, how);
+ }
+
+};
+#endif
+
+//
+// Datagram socket.
+//
+class Pj_Sock_Dgram : public Pj_Socket
+{
+public:
+ //
+ // Default constructor.
+ //
+ Pj_Sock_Dgram()
+ {
+ }
+
+ //
+ // Initialize from a socket handle.
+ //
+ explicit Pj_Sock_Dgram(pj_sock_t sock)
+ : Pj_Socket(sock)
+ {
+ }
+
+ //
+ // Copy constructor.
+ //
+ Pj_Sock_Dgram(const Pj_Sock_Dgram &rhs)
+ : Pj_Socket(rhs)
+ {
+ }
+
+ //
+ // Assignment.
+ //
+ Pj_Sock_Dgram &operator=(const Pj_Sock_Dgram &rhs)
+ {
+ Pj_Socket::operator =(rhs);
+ return *this;
+ }
+
+ //
+ // recvfrom()
+ //
+ pj_ssize_t recvfrom( void *buf, pj_size_t len, int flag = 0,
+ Pj_Inet_Addr *fromaddr = NULL)
+ {
+ pj_ssize_t bytes = len;
+ int *addrlen = fromaddr ? &fromaddr->addrlen_ : NULL;
+ if (pj_sock_recvfrom( sock_, buf, &bytes, flag,
+ fromaddr, addrlen) != PJ_SUCCESS)
+ {
+ return -1;
+ }
+ return bytes;
+ }
+
+ //
+ // sendto()
+ //
+ pj_ssize_t sendto( const void *buf, pj_size_t len, int flag,
+ const Pj_Inet_Addr &addr)
+ {
+ pj_ssize_t bytes = len;
+ if (pj_sock_sendto( sock_, buf, &bytes, flag,
+ &addr, sizeof(pj_sockaddr_in)) != PJ_SUCCESS)
+ {
+ return -1;
+ }
+ return bytes;
+ }
+};
+
+
+#endif /* __PJPP_SOCK_HPP__ */
+
diff --git a/pjlib/include/pj++/string.hpp b/pjlib/include/pj++/string.hpp
index c656aadc..aa196c8f 100644
--- a/pjlib/include/pj++/string.hpp
+++ b/pjlib/include/pj++/string.hpp
@@ -1,425 +1,425 @@
-/* $Id$ */
-/*
- * Copyright (C)2003-2006 Benny Prijono <benny@prijono.org>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
-#ifndef __PJPP_STRING_HPP__
-#define __PJPP_STRING_HPP__
-
-#include <pj/string.h>
-#include <pj++/pool.hpp>
-#include <pj/assert.h>
-
-//
-// String wrapper class for pj_str_t.
-//
-class Pj_String : public pj_str_t
-{
-public:
- //
- // Default constructor.
- //
- Pj_String()
- {
- pj_assert(sizeof(Pj_String) == sizeof(pj_str_t));
- ptr=NULL;
- slen=0;
- }
-
- //
- // Construct the buffer from a char*.
- //
- explicit Pj_String(char *str)
- {
- set(str);
- }
-
- //
- // Construct from a const char*.
- //
- Pj_String(Pj_Pool *pool, const char *src)
- {
- set(pool, src);
- }
-
- //
- // Construct from pj_str_t*.
- //
- explicit Pj_String(pj_str_t *s)
- {
- set(s);
- }
-
- //
- // Construct by copying from const pj_str_t*.
- //
- Pj_String(Pj_Pool *pool, const pj_str_t *s)
- {
- set(pool, s);
- }
-
- //
- // Construct from another Pj_String
- //
- explicit Pj_String(Pj_String &rhs)
- {
- set(rhs);
- }
-
- //
- // Construct by copying from Pj_String
- //
- Pj_String(Pj_Pool *pool, const Pj_String &rhs)
- {
- set(pool, rhs);
- }
-
- //
- // Construct from a char* and a length.
- //
- Pj_String(char *str, pj_size_t len)
- {
- set(str, len);
- }
-
- //
- // Construct from pair of pointer.
- //
- Pj_String(char *begin, char *end)
- {
- pj_strset3(this, begin, end);
- }
-
- //
- // Get the length of the string.
- //
- pj_size_t length() const
- {
- return pj_strlen(this);
- }
-
- //
- // Get the length of the string.
- //
- pj_size_t size() const
- {
- return length();
- }
-
- //
- // Get the string buffer.
- //
- const char *buf() const
- {
- return ptr;
- }
-
- //
- // Initialize buffer from char*.
- //
- void set(char *str)
- {
- pj_strset2(this, str);
- }
-
- //
- // Initialize by copying from a const char*.
- //
- void set(Pj_Pool *pool, const char *s)
- {
- pj_strdup2(pool->pool_(), this, s);
- }
-
- //
- // Initialize from pj_str_t*.
- //
- void set(pj_str_t *s)
- {
- pj_strassign(this, s);
- }
-
- //
- // Initialize by copying from const pj_str_t*.
- //
- void set(Pj_Pool *pool, const pj_str_t *s)
- {
- pj_strdup(pool->pool_(), this, s);
- }
-
- //
- // Initialize from char* and length.
- //
- void set(char *str, pj_size_t len)
- {
- pj_strset(this, str, len);
- }
-
- //
- // Initialize from pair of pointers.
- //
- void set(char *begin, char *end)
- {
- pj_strset3(this, begin, end);
- }
-
- //
- // Initialize from other Pj_String.
- //
- void set(Pj_String &rhs)
- {
- pj_strassign(this, &rhs);
- }
-
- //
- // Initialize by copying from a Pj_String*.
- //
- void set(Pj_Pool *pool, const Pj_String *s)
- {
- pj_strdup(pool->pool_(), this, s);
- }
-
- //
- // Initialize by copying from other Pj_String.
- //
- void set(Pj_Pool *pool, const Pj_String &s)
- {
- pj_strdup(pool->pool_(), this, &s);
- }
-
- //
- // Copy the contents of other string.
- //
- void strcpy(const pj_str_t *s)
- {
- pj_strcpy(this, s);
- }
-
- //
- // Copy the contents of other string.
- //
- void strcpy(const Pj_String &rhs)
- {
- pj_strcpy(this, &rhs);
- }
-
- //
- // Copy the contents of other string.
- //
- void strcpy(const char *s)
- {
- pj_strcpy2(this, s);
- }
-
- //
- // Compare string.
- //
- int strcmp(const char *s) const
- {
- return pj_strcmp2(this, s);
- }
-
- //
- // Compare string.
- //
- int strcmp(const pj_str_t *s) const
- {
- return pj_strcmp(this, s);
- }
-
- //
- // Compare string.
- //
- int strcmp(const Pj_String &rhs) const
- {
- return pj_strcmp(this, &rhs);
- }
-
- //
- // Compare string.
- //
- int strncmp(const char *s, pj_size_t len) const
- {
- return pj_strncmp2(this, s, len);
- }
-
- //
- // Compare string.
- //
- int strncmp(const pj_str_t *s, pj_size_t len) const
- {
- return pj_strncmp(this, s, len);
- }
-
- //
- // Compare string.
- //
- int strncmp(const Pj_String &rhs, pj_size_t len) const
- {
- return pj_strncmp(this, &rhs, len);
- }
-
- //
- // Compare string.
- //
- int stricmp(const char *s) const
- {
- return pj_stricmp2(this, s);
- }
-
- //
- // Compare string.
- //
- int stricmp(const pj_str_t *s) const
- {
- return pj_stricmp(this, s);
- }
-
- //
- // Compare string.
- //
- int stricmp(const Pj_String &rhs) const
- {
- return stricmp(&rhs);
- }
-
- //
- // Compare string.
- //
- int strnicmp(const char *s, pj_size_t len) const
- {
- return pj_strnicmp2(this, s, len);
- }
-
- //
- // Compare string.
- //
- int strnicmp(const pj_str_t *s, pj_size_t len) const
- {
- return pj_strnicmp(this, s, len);
- }
-
- //
- // Compare string.
- //
- int strnicmp(const Pj_String &rhs, pj_size_t len) const
- {
- return strnicmp(&rhs, len);
- }
-
- //
- // Compare contents for equality.
- //
- bool operator==(const char *s) const
- {
- return strcmp(s) == 0;
- }
-
- //
- // Compare contents for equality.
- //
- bool operator==(const pj_str_t *s) const
- {
- return strcmp(s) == 0;
- }
-
- //
- // Compare contents for equality.
- //
- bool operator==(const Pj_String &rhs) const
- {
- return pj_strcmp(this, &rhs) == 0;
- }
-
- //
- // Find a character in the string.
- //
- char *strchr(int chr)
- {
- return pj_strchr(this, chr);
- }
-
- //
- // Find a character in the string.
- //
- char *find(int chr)
- {
- return strchr(chr);
- }
-
- //
- // Concatenate string.
- //
- void strcat(const Pj_String &rhs)
- {
- pj_strcat(this, &rhs);
- }
-
- //
- // Left trim.
- //
- void ltrim()
- {
- pj_strltrim(this);
- }
-
- //
- // Right trim.
- //
- void rtrim()
- {
- pj_strrtrim(this);
- }
-
- //
- // Left and right trim.
- //
- void trim()
- {
- pj_strtrim(this);
- }
-
- //
- // Convert to unsigned long.
- //
- unsigned long to_ulong() const
- {
- return pj_strtoul(this);
- }
-
- //
- // Convert from unsigned long.
- //
- void from_ulong(unsigned long value)
- {
- slen = pj_utoa(value, ptr);
- }
-
- //
- // Convert from unsigned long with padding.
- //
- void from_ulong_with_pad(unsigned long value, int min_dig=0, int pad=' ')
- {
- slen = pj_utoa_pad(value, ptr, min_dig, pad);
- }
-
-
-private:
- //Pj_String(const Pj_String &rhs) {}
- void operator=(const Pj_String &rhs) { pj_assert(false); }
-};
-
-#endif /* __PJPP_STRING_HPP__ */
-
+/* $Id$ */
+/*
+ * Copyright (C)2003-2006 Benny Prijono <benny@prijono.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#ifndef __PJPP_STRING_HPP__
+#define __PJPP_STRING_HPP__
+
+#include <pj/string.h>
+#include <pj++/pool.hpp>
+#include <pj/assert.h>
+
+//
+// String wrapper class for pj_str_t.
+//
+class Pj_String : public pj_str_t
+{
+public:
+ //
+ // Default constructor.
+ //
+ Pj_String()
+ {
+ pj_assert(sizeof(Pj_String) == sizeof(pj_str_t));
+ ptr=NULL;
+ slen=0;
+ }
+
+ //
+ // Construct the buffer from a char*.
+ //
+ explicit Pj_String(char *str)
+ {
+ set(str);
+ }
+
+ //
+ // Construct from a const char*.
+ //
+ Pj_String(Pj_Pool *pool, const char *src)
+ {
+ set(pool, src);
+ }
+
+ //
+ // Construct from pj_str_t*.
+ //
+ explicit Pj_String(pj_str_t *s)
+ {
+ set(s);
+ }
+
+ //
+ // Construct by copying from const pj_str_t*.
+ //
+ Pj_String(Pj_Pool *pool, const pj_str_t *s)
+ {
+ set(pool, s);
+ }
+
+ //
+ // Construct from another Pj_String
+ //
+ explicit Pj_String(Pj_String &rhs)
+ {
+ set(rhs);
+ }
+
+ //
+ // Construct by copying from Pj_String
+ //
+ Pj_String(Pj_Pool *pool, const Pj_String &rhs)
+ {
+ set(pool, rhs);
+ }
+
+ //
+ // Construct from a char* and a length.
+ //
+ Pj_String(char *str, pj_size_t len)
+ {
+ set(str, len);
+ }
+
+ //
+ // Construct from pair of pointer.
+ //
+ Pj_String(char *begin, char *end)
+ {
+ pj_strset3(this, begin, end);
+ }
+
+ //
+ // Get the length of the string.
+ //
+ pj_size_t length() const
+ {
+ return pj_strlen(this);
+ }
+
+ //
+ // Get the length of the string.
+ //
+ pj_size_t size() const
+ {
+ return length();
+ }
+
+ //
+ // Get the string buffer.
+ //
+ const char *buf() const
+ {
+ return ptr;
+ }
+
+ //
+ // Initialize buffer from char*.
+ //
+ void set(char *str)
+ {
+ pj_strset2(this, str);
+ }
+
+ //
+ // Initialize by copying from a const char*.
+ //
+ void set(Pj_Pool *pool, const char *s)
+ {
+ pj_strdup2(pool->pool_(), this, s);
+ }
+
+ //
+ // Initialize from pj_str_t*.
+ //
+ void set(pj_str_t *s)
+ {
+ pj_strassign(this, s);
+ }
+
+ //
+ // Initialize by copying from const pj_str_t*.
+ //
+ void set(Pj_Pool *pool, const pj_str_t *s)
+ {
+ pj_strdup(pool->pool_(), this, s);
+ }
+
+ //
+ // Initialize from char* and length.
+ //
+ void set(char *str, pj_size_t len)
+ {
+ pj_strset(this, str, len);
+ }
+
+ //
+ // Initialize from pair of pointers.
+ //
+ void set(char *begin, char *end)
+ {
+ pj_strset3(this, begin, end);
+ }
+
+ //
+ // Initialize from other Pj_String.
+ //
+ void set(Pj_String &rhs)
+ {
+ pj_strassign(this, &rhs);
+ }
+
+ //
+ // Initialize by copying from a Pj_String*.
+ //
+ void set(Pj_Pool *pool, const Pj_String *s)
+ {
+ pj_strdup(pool->pool_(), this, s);
+ }
+
+ //
+ // Initialize by copying from other Pj_String.
+ //
+ void set(Pj_Pool *pool, const Pj_String &s)
+ {
+ pj_strdup(pool->pool_(), this, &s);
+ }
+
+ //
+ // Copy the contents of other string.
+ //
+ void strcpy(const pj_str_t *s)
+ {
+ pj_strcpy(this, s);
+ }
+
+ //
+ // Copy the contents of other string.
+ //
+ void strcpy(const Pj_String &rhs)
+ {
+ pj_strcpy(this, &rhs);
+ }
+
+ //
+ // Copy the contents of other string.
+ //
+ void strcpy(const char *s)
+ {
+ pj_strcpy2(this, s);
+ }
+
+ //
+ // Compare string.
+ //
+ int strcmp(const char *s) const
+ {
+ return pj_strcmp2(this, s);
+ }
+
+ //
+ // Compare string.
+ //
+ int strcmp(const pj_str_t *s) const
+ {
+ return pj_strcmp(this, s);
+ }
+
+ //
+ // Compare string.
+ //
+ int strcmp(const Pj_String &rhs) const
+ {
+ return pj_strcmp(this, &rhs);
+ }
+
+ //
+ // Compare string.
+ //
+ int strncmp(const char *s, pj_size_t len) const
+ {
+ return pj_strncmp2(this, s, len);
+ }
+
+ //
+ // Compare string.
+ //
+ int strncmp(const pj_str_t *s, pj_size_t len) const
+ {
+ return pj_strncmp(this, s, len);
+ }
+
+ //
+ // Compare string.
+ //
+ int strncmp(const Pj_String &rhs, pj_size_t len) const
+ {
+ return pj_strncmp(this, &rhs, len);
+ }
+
+ //
+ // Compare string.
+ //
+ int stricmp(const char *s) const
+ {
+ return pj_stricmp2(this, s);
+ }
+
+ //
+ // Compare string.
+ //
+ int stricmp(const pj_str_t *s) const
+ {
+ return pj_stricmp(this, s);
+ }
+
+ //
+ // Compare string.
+ //
+ int stricmp(const Pj_String &rhs) const
+ {
+ return stricmp(&rhs);
+ }
+
+ //
+ // Compare string.
+ //
+ int strnicmp(const char *s, pj_size_t len) const
+ {
+ return pj_strnicmp2(this, s, len);
+ }
+
+ //
+ // Compare string.
+ //
+ int strnicmp(const pj_str_t *s, pj_size_t len) const
+ {
+ return pj_strnicmp(this, s, len);
+ }
+
+ //
+ // Compare string.
+ //
+ int strnicmp(const Pj_String &rhs, pj_size_t len) const
+ {
+ return strnicmp(&rhs, len);
+ }
+
+ //
+ // Compare contents for equality.
+ //
+ bool operator==(const char *s) const
+ {
+ return strcmp(s) == 0;
+ }
+
+ //
+ // Compare contents for equality.
+ //
+ bool operator==(const pj_str_t *s) const
+ {
+ return strcmp(s) == 0;
+ }
+
+ //
+ // Compare contents for equality.
+ //
+ bool operator==(const Pj_String &rhs) const
+ {
+ return pj_strcmp(this, &rhs) == 0;
+ }
+
+ //
+ // Find a character in the string.
+ //
+ char *strchr(int chr)
+ {
+ return pj_strchr(this, chr);
+ }
+
+ //
+ // Find a character in the string.
+ //
+ char *find(int chr)
+ {
+ return strchr(chr);
+ }
+
+ //
+ // Concatenate string.
+ //
+ void strcat(const Pj_String &rhs)
+ {
+ pj_strcat(this, &rhs);
+ }
+
+ //
+ // Left trim.
+ //
+ void ltrim()
+ {
+ pj_strltrim(this);
+ }
+
+ //
+ // Right trim.
+ //
+ void rtrim()
+ {
+ pj_strrtrim(this);
+ }
+
+ //
+ // Left and right trim.
+ //
+ void trim()
+ {
+ pj_strtrim(this);
+ }
+
+ //
+ // Convert to unsigned long.
+ //
+ unsigned long to_ulong() const
+ {
+ return pj_strtoul(this);
+ }
+
+ //
+ // Convert from unsigned long.
+ //
+ void from_ulong(unsigned long value)
+ {
+ slen = pj_utoa(value, ptr);
+ }
+
+ //
+ // Convert from unsigned long with padding.
+ //
+ void from_ulong_with_pad(unsigned long value, int min_dig=0, int pad=' ')
+ {
+ slen = pj_utoa_pad(value, ptr, min_dig, pad);
+ }
+
+
+private:
+ //Pj_String(const Pj_String &rhs) {}
+ void operator=(const Pj_String &rhs) { pj_assert(false); }
+};
+
+#endif /* __PJPP_STRING_HPP__ */
+
diff --git a/pjlib/include/pj++/timer.hpp b/pjlib/include/pj++/timer.hpp
index 09510c50..927de13c 100644
--- a/pjlib/include/pj++/timer.hpp
+++ b/pjlib/include/pj++/timer.hpp
@@ -1,197 +1,197 @@
-/* $Id$ */
-/*
- * Copyright (C)2003-2006 Benny Prijono <benny@prijono.org>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
-#ifndef __PJPP_TIMER_HPP__
-#define __PJPP_TIMER_HPP__
-
-#include <pj/timer.h>
-#include <pj++/types.hpp>
-#include <pj/assert.h>
-#include <pj++/lock.hpp>
-
-class Pj_Timer_Heap;
-
-//////////////////////////////////////////////////////////////////////////////
-// Timer entry.
-//
-// How to use:
-// Derive class from Pj_Timer_Entry and override on_timeout().
-// Scheduler timer in Pj_Timer_Heap.
-//
-class Pj_Timer_Entry : public Pj_Object
-{
- friend class Pj_Timer_Heap;
-
-public:
- //
- // Default constructor.
- //
- Pj_Timer_Entry()
- {
- entry_.user_data = this;
- entry_.cb = &timer_heap_callback;
- }
-
- //
- // Destructor, do nothing.
- //
- ~Pj_Timer_Entry()
- {
- }
-
- //
- // Override this to get the timeout notification.
- //
- virtual void on_timeout(int id) = 0;
-
-private:
- pj_timer_entry entry_;
-
- static void timer_heap_callback(pj_timer_heap_t *th, pj_timer_entry *e)
- {
- Pj_Timer_Entry *entry = (Pj_Timer_Entry*) e->user_data;
- entry->on_timeout(e->id);
- }
-
-};
-
-//////////////////////////////////////////////////////////////////////////////
-// Timer heap.
-//
-class Pj_Timer_Heap : public Pj_Object
-{
-public:
- //
- // Default constructor.
- //
- Pj_Timer_Heap()
- : ht_(NULL)
- {
- }
-
- //
- // Construct timer heap.
- //
- Pj_Timer_Heap(Pj_Pool *pool, pj_size_t initial_count)
- : ht_(NULL)
- {
- create(pool, initial_count);
- }
-
- //
- // Destructor.
- //
- ~Pj_Timer_Heap()
- {
- destroy();
- }
-
- //
- // Create
- //
- pj_status_t create(Pj_Pool *pool, pj_size_t initial_count)
- {
- destroy();
- return pj_timer_heap_create(pool->pool_(), initial_count, &ht_);
- }
-
- //
- // Destroy
- //
- void destroy()
- {
- if (ht_) {
- pj_timer_heap_destroy(ht_);
- ht_ = NULL;
- }
- }
-
- //
- // Get pjlib compatible timer heap object.
- //
- pj_timer_heap_t *get_timer_heap()
- {
- return ht_;
- }
-
- //
- // Set the lock object.
- //
- void set_lock( Pj_Lock *lock, bool auto_delete )
- {
- pj_timer_heap_set_lock( ht_, lock->pj_lock_t_(), auto_delete);
- }
-
- //
- // Set maximum number of timed out entries to be processed per poll.
- //
- unsigned set_max_timed_out_per_poll(unsigned count)
- {
- return pj_timer_heap_set_max_timed_out_per_poll(ht_, count);
- }
-
- //
- // Schedule a timer.
- //
- bool schedule( Pj_Timer_Entry *ent, const Pj_Time_Val &delay,
- int id)
- {
- ent->entry_.id = id;
- return pj_timer_heap_schedule(ht_, &ent->entry_, &delay) == 0;
- }
-
- //
- // Cancel a timer.
- //
- bool cancel(Pj_Timer_Entry *ent)
- {
- return pj_timer_heap_cancel(ht_, &ent->entry_) == 1;
- }
-
- //
- // Get current number of timers
- //
- pj_size_t count()
- {
- return pj_timer_heap_count(ht_);
- }
-
- //
- // Get the earliest time.
- // Return false if no timer is found.
- //
- bool earliest_time(Pj_Time_Val *t)
- {
- return pj_timer_heap_earliest_time(ht_, t) == PJ_SUCCESS;
- }
-
- //
- // Poll the timer.
- // Return number of timed out entries has been called.
- //
- unsigned poll(Pj_Time_Val *next_delay = NULL)
- {
- return pj_timer_heap_poll(ht_, next_delay);
- }
-
-private:
- pj_timer_heap_t *ht_;
-};
-
-#endif /* __PJPP_TIMER_HPP__ */
-
+/* $Id$ */
+/*
+ * Copyright (C)2003-2006 Benny Prijono <benny@prijono.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#ifndef __PJPP_TIMER_HPP__
+#define __PJPP_TIMER_HPP__
+
+#include <pj/timer.h>
+#include <pj++/types.hpp>
+#include <pj/assert.h>
+#include <pj++/lock.hpp>
+
+class Pj_Timer_Heap;
+
+//////////////////////////////////////////////////////////////////////////////
+// Timer entry.
+//
+// How to use:
+// Derive class from Pj_Timer_Entry and override on_timeout().
+// Scheduler timer in Pj_Timer_Heap.
+//
+class Pj_Timer_Entry : public Pj_Object
+{
+ friend class Pj_Timer_Heap;
+
+public:
+ //
+ // Default constructor.
+ //
+ Pj_Timer_Entry()
+ {
+ entry_.user_data = this;
+ entry_.cb = &timer_heap_callback;
+ }
+
+ //
+ // Destructor, do nothing.
+ //
+ ~Pj_Timer_Entry()
+ {
+ }
+
+ //
+ // Override this to get the timeout notification.
+ //
+ virtual void on_timeout(int id) = 0;
+
+private:
+ pj_timer_entry entry_;
+
+ static void timer_heap_callback(pj_timer_heap_t *th, pj_timer_entry *e)
+ {
+ Pj_Timer_Entry *entry = (Pj_Timer_Entry*) e->user_data;
+ entry->on_timeout(e->id);
+ }
+
+};
+
+//////////////////////////////////////////////////////////////////////////////
+// Timer heap.
+//
+class Pj_Timer_Heap : public Pj_Object
+{
+public:
+ //
+ // Default constructor.
+ //
+ Pj_Timer_Heap()
+ : ht_(NULL)
+ {
+ }
+
+ //
+ // Construct timer heap.
+ //
+ Pj_Timer_Heap(Pj_Pool *pool, pj_size_t initial_count)
+ : ht_(NULL)
+ {
+ create(pool, initial_count);
+ }
+
+ //
+ // Destructor.
+ //
+ ~Pj_Timer_Heap()
+ {
+ destroy();
+ }
+
+ //
+ // Create
+ //
+ pj_status_t create(Pj_Pool *pool, pj_size_t initial_count)
+ {
+ destroy();
+ return pj_timer_heap_create(pool->pool_(), initial_count, &ht_);
+ }
+
+ //
+ // Destroy
+ //
+ void destroy()
+ {
+ if (ht_) {
+ pj_timer_heap_destroy(ht_);
+ ht_ = NULL;
+ }
+ }
+
+ //
+ // Get pjlib compatible timer heap object.
+ //
+ pj_timer_heap_t *get_timer_heap()
+ {
+ return ht_;
+ }
+
+ //
+ // Set the lock object.
+ //
+ void set_lock( Pj_Lock *lock, bool auto_delete )
+ {
+ pj_timer_heap_set_lock( ht_, lock->pj_lock_t_(), auto_delete);
+ }
+
+ //
+ // Set maximum number of timed out entries to be processed per poll.
+ //
+ unsigned set_max_timed_out_per_poll(unsigned count)
+ {
+ return pj_timer_heap_set_max_timed_out_per_poll(ht_, count);
+ }
+
+ //
+ // Schedule a timer.
+ //
+ bool schedule( Pj_Timer_Entry *ent, const Pj_Time_Val &delay,
+ int id)
+ {
+ ent->entry_.id = id;
+ return pj_timer_heap_schedule(ht_, &ent->entry_, &delay) == 0;
+ }
+
+ //
+ // Cancel a timer.
+ //
+ bool cancel(Pj_Timer_Entry *ent)
+ {
+ return pj_timer_heap_cancel(ht_, &ent->entry_) == 1;
+ }
+
+ //
+ // Get current number of timers
+ //
+ pj_size_t count()
+ {
+ return pj_timer_heap_count(ht_);
+ }
+
+ //
+ // Get the earliest time.
+ // Return false if no timer is found.
+ //
+ bool earliest_time(Pj_Time_Val *t)
+ {
+ return pj_timer_heap_earliest_time(ht_, t) == PJ_SUCCESS;
+ }
+
+ //
+ // Poll the timer.
+ // Return number of timed out entries has been called.
+ //
+ unsigned poll(Pj_Time_Val *next_delay = NULL)
+ {
+ return pj_timer_heap_poll(ht_, next_delay);
+ }
+
+private:
+ pj_timer_heap_t *ht_;
+};
+
+#endif /* __PJPP_TIMER_HPP__ */
+
diff --git a/pjlib/include/pj++/tree.hpp b/pjlib/include/pj++/tree.hpp
index 3dd5bfa9..fe2ff6e3 100644
--- a/pjlib/include/pj++/tree.hpp
+++ b/pjlib/include/pj++/tree.hpp
@@ -1,128 +1,128 @@
-/* $Id$ */
-/*
- * Copyright (C)2003-2006 Benny Prijono <benny@prijono.org>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
-#ifndef __PJPP_TREE_HPP__
-#define __PJPP_TREE_HPP__
-
-#include <pj/rbtree.h>
-
-//
-// Tree.
-//
-class PJ_Tree
-{
-public:
- typedef pj_rbtree_comp Comp;
- class iterator;
- class reverse_iterator;
-
- class Node : private pj_rbtree_node
- {
- friend class PJ_Tree;
- friend class iterator;
- friend class reverse_iterator;
-
- public:
- Node() {}
- explicit Node(void *data) { user_data = data; }
- void set_user_data(void *data) { user_data = data; }
- void *get_user_data() const { return user_data; }
- };
-
- class iterator
- {
- public:
- iterator() {}
- iterator(const iterator &rhs) : tr_(rhs.tr_), nd_(rhs.nd_) {}
- iterator(pj_rbtree *tr, pj_rbtree_node *nd) : tr_(tr), nd_(nd) {}
- Node *operator*() { return (Node*)nd_; }
- bool operator==(const iterator &rhs) const { return tr_==rhs.tr_ && nd_==rhs.nd_; }
- iterator &operator=(const iterator &rhs) { tr_=rhs.tr_; nd_=rhs.nd_; return *this; }
- void operator++() { nd_=pj_rbtree_next(tr_, nd_); }
- void operator--() { nd_=pj_rbtree_prev(tr_, nd_); }
- protected:
- pj_rbtree *tr_;
- pj_rbtree_node *nd_;
- };
-
- class reverse_iterator : public iterator
- {
- public:
- reverse_iterator() {}
- reverse_iterator(const reverse_iterator &it) : iterator(it) {}
- reverse_iterator(pj_rbtree *t, pj_rbtree_node *n) : iterator(t, n) {}
- reverse_iterator &operator=(const reverse_iterator &rhs) { iterator::operator=(rhs); return *this; }
- Node *operator*() { return (Node*)nd_; }
- bool operator==(const reverse_iterator &rhs) const { return iterator::operator==(rhs); }
- void operator++() { nd_=pj_rbtree_prev(tr_, nd_); }
- void operator--() { nd_=pj_rbtree_next(tr_, nd_); }
- };
-
- explicit PJ_Tree(Comp *comp) { pj_rbtree_init(&t_, comp); }
-
- iterator begin()
- {
- return iterator(&t_, pj_rbtree_first(&t_));
- }
-
- iterator end()
- {
- return iterator(&t_, NULL);
- }
-
- reverse_iterator rbegin()
- {
- return reverse_iterator(&t_, pj_rbtree_last(&t_));
- }
-
- reverse_iterator rend()
- {
- return reverse_iterator(&t_, NULL);
- }
-
- bool insert(Node *node)
- {
- return pj_rbtree_insert(&t_, node)==0 ? true : false;
- }
-
- Node *find(const void *key)
- {
- return (Node*)pj_rbtree_find(&t_, key);
- }
-
- Node *erase(Node *node)
- {
- return (Node*)pj_rbtree_erase(&t_, node);
- }
-
- unsigned max_height(Node *node=NULL)
- {
- return pj_rbtree_max_height(&t_, node);
- }
-
- unsigned min_height(Node *node=NULL)
- {
- return pj_rbtree_min_height(&t_, node);
- }
-
-private:
- pj_rbtree t_;
-};
-
-#endif /* __PJPP_TREE_HPP__ */
-
+/* $Id$ */
+/*
+ * Copyright (C)2003-2006 Benny Prijono <benny@prijono.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#ifndef __PJPP_TREE_HPP__
+#define __PJPP_TREE_HPP__
+
+#include <pj/rbtree.h>
+
+//
+// Tree.
+//
+class PJ_Tree
+{
+public:
+ typedef pj_rbtree_comp Comp;
+ class iterator;
+ class reverse_iterator;
+
+ class Node : private pj_rbtree_node
+ {
+ friend class PJ_Tree;
+ friend class iterator;
+ friend class reverse_iterator;
+
+ public:
+ Node() {}
+ explicit Node(void *data) { user_data = data; }
+ void set_user_data(void *data) { user_data = data; }
+ void *get_user_data() const { return user_data; }
+ };
+
+ class iterator
+ {
+ public:
+ iterator() {}
+ iterator(const iterator &rhs) : tr_(rhs.tr_), nd_(rhs.nd_) {}
+ iterator(pj_rbtree *tr, pj_rbtree_node *nd) : tr_(tr), nd_(nd) {}
+ Node *operator*() { return (Node*)nd_; }
+ bool operator==(const iterator &rhs) const { return tr_==rhs.tr_ && nd_==rhs.nd_; }
+ iterator &operator=(const iterator &rhs) { tr_=rhs.tr_; nd_=rhs.nd_; return *this; }
+ void operator++() { nd_=pj_rbtree_next(tr_, nd_); }
+ void operator--() { nd_=pj_rbtree_prev(tr_, nd_); }
+ protected:
+ pj_rbtree *tr_;
+ pj_rbtree_node *nd_;
+ };
+
+ class reverse_iterator : public iterator
+ {
+ public:
+ reverse_iterator() {}
+ reverse_iterator(const reverse_iterator &it) : iterator(it) {}
+ reverse_iterator(pj_rbtree *t, pj_rbtree_node *n) : iterator(t, n) {}
+ reverse_iterator &operator=(const reverse_iterator &rhs) { iterator::operator=(rhs); return *this; }
+ Node *operator*() { return (Node*)nd_; }
+ bool operator==(const reverse_iterator &rhs) const { return iterator::operator==(rhs); }
+ void operator++() { nd_=pj_rbtree_prev(tr_, nd_); }
+ void operator--() { nd_=pj_rbtree_next(tr_, nd_); }
+ };
+
+ explicit PJ_Tree(Comp *comp) { pj_rbtree_init(&t_, comp); }
+
+ iterator begin()
+ {
+ return iterator(&t_, pj_rbtree_first(&t_));
+ }
+
+ iterator end()
+ {
+ return iterator(&t_, NULL);
+ }
+
+ reverse_iterator rbegin()
+ {
+ return reverse_iterator(&t_, pj_rbtree_last(&t_));
+ }
+
+ reverse_iterator rend()
+ {
+ return reverse_iterator(&t_, NULL);
+ }
+
+ bool insert(Node *node)
+ {
+ return pj_rbtree_insert(&t_, node)==0 ? true : false;
+ }
+
+ Node *find(const void *key)
+ {
+ return (Node*)pj_rbtree_find(&t_, key);
+ }
+
+ Node *erase(Node *node)
+ {
+ return (Node*)pj_rbtree_erase(&t_, node);
+ }
+
+ unsigned max_height(Node *node=NULL)
+ {
+ return pj_rbtree_max_height(&t_, node);
+ }
+
+ unsigned min_height(Node *node=NULL)
+ {
+ return pj_rbtree_min_height(&t_, node);
+ }
+
+private:
+ pj_rbtree t_;
+};
+
+#endif /* __PJPP_TREE_HPP__ */
+
diff --git a/pjlib/include/pj++/types.hpp b/pjlib/include/pj++/types.hpp
index a9669fa3..e0c2e230 100644
--- a/pjlib/include/pj++/types.hpp
+++ b/pjlib/include/pj++/types.hpp
@@ -1,160 +1,160 @@
-/* $Id$ */
-/*
- * Copyright (C)2003-2006 Benny Prijono <benny@prijono.org>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
-#ifndef __PJPP_TYPES_HPP__
-#define __PJPP_TYPES_HPP__
-
-#include <pj/types.h>
-
-class Pj_Pool;
-class Pj_Socket ;
-class Pj_Lock;
-
-
-//
-// PJLIB initializer.
-//
-class Pjlib
-{
-public:
- Pjlib()
- {
- pj_init();
- }
-};
-
-//
-// Class Pj_Object is declared in pool.hpp
-//
-
-//
-// Time value wrapper.
-//
-class Pj_Time_Val : public pj_time_val
-{
-public:
- Pj_Time_Val()
- {
- }
-
- Pj_Time_Val(long init_sec, long init_msec)
- {
- sec = init_sec;
- msec = init_msec;
- }
-
- Pj_Time_Val(const Pj_Time_Val &rhs)
- {
- sec=rhs.sec;
- msec=rhs.msec;
- }
-
- explicit Pj_Time_Val(const pj_time_val &tv)
- {
- sec = tv.sec;
- msec = tv.msec;
- }
-
- long get_sec() const
- {
- return sec;
- }
-
- long get_msec() const
- {
- return msec;
- }
-
- void set_sec (long s)
- {
- sec = s;
- }
-
- void set_msec(long ms)
- {
- msec = ms;
- normalize();
- }
-
- long to_msec() const
- {
- return PJ_TIME_VAL_MSEC((*this));
- }
-
- bool operator == (const Pj_Time_Val &rhs) const
- {
- return PJ_TIME_VAL_EQ((*this), rhs);
- }
-
- bool operator > (const Pj_Time_Val &rhs) const
- {
- return PJ_TIME_VAL_GT((*this), rhs);
- }
-
- bool operator >= (const Pj_Time_Val &rhs) const
- {
- return PJ_TIME_VAL_GTE((*this), rhs);
- }
-
- bool operator < (const Pj_Time_Val &rhs) const
- {
- return PJ_TIME_VAL_LT((*this), rhs);
- }
-
- bool operator <= (const Pj_Time_Val &rhs) const
- {
- return PJ_TIME_VAL_LTE((*this), rhs);
- }
-
- Pj_Time_Val & operator = (const Pj_Time_Val &rhs)
- {
- sec = rhs.sec;
- msec = rhs.msec;
- return *this;
- }
-
- Pj_Time_Val & operator += (const Pj_Time_Val &rhs)
- {
- PJ_TIME_VAL_ADD((*this), rhs);
- return *this;
- }
-
- Pj_Time_Val & operator -= (const Pj_Time_Val &rhs)
- {
- PJ_TIME_VAL_SUB((*this), rhs);
- return *this;
- }
-
- /* Must include os.hpp to use these, otherwise unresolved in linking */
- inline pj_status_t gettimeofday();
- inline pj_parsed_time decode();
- inline pj_status_t encode(const pj_parsed_time *pt);
- inline pj_status_t to_gmt();
- inline pj_status_t to_local();
-
-
-private:
- void normalize()
- {
- pj_time_val_normalize(this);
- }
-
-};
-
-#endif /* __PJPP_TYPES_HPP__ */
-
+/* $Id$ */
+/*
+ * Copyright (C)2003-2006 Benny Prijono <benny@prijono.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#ifndef __PJPP_TYPES_HPP__
+#define __PJPP_TYPES_HPP__
+
+#include <pj/types.h>
+
+class Pj_Pool;
+class Pj_Socket ;
+class Pj_Lock;
+
+
+//
+// PJLIB initializer.
+//
+class Pjlib
+{
+public:
+ Pjlib()
+ {
+ pj_init();
+ }
+};
+
+//
+// Class Pj_Object is declared in pool.hpp
+//
+
+//
+// Time value wrapper.
+//
+class Pj_Time_Val : public pj_time_val
+{
+public:
+ Pj_Time_Val()
+ {
+ }
+
+ Pj_Time_Val(long init_sec, long init_msec)
+ {
+ sec = init_sec;
+ msec = init_msec;
+ }
+
+ Pj_Time_Val(const Pj_Time_Val &rhs)
+ {
+ sec=rhs.sec;
+ msec=rhs.msec;
+ }
+
+ explicit Pj_Time_Val(const pj_time_val &tv)
+ {
+ sec = tv.sec;
+ msec = tv.msec;
+ }
+
+ long get_sec() const
+ {
+ return sec;
+ }
+
+ long get_msec() const
+ {
+ return msec;
+ }
+
+ void set_sec (long s)
+ {
+ sec = s;
+ }
+
+ void set_msec(long ms)
+ {
+ msec = ms;
+ normalize();
+ }
+
+ long to_msec() const
+ {
+ return PJ_TIME_VAL_MSEC((*this));
+ }
+
+ bool operator == (const Pj_Time_Val &rhs) const
+ {
+ return PJ_TIME_VAL_EQ((*this), rhs);
+ }
+
+ bool operator > (const Pj_Time_Val &rhs) const
+ {
+ return PJ_TIME_VAL_GT((*this), rhs);
+ }
+
+ bool operator >= (const Pj_Time_Val &rhs) const
+ {
+ return PJ_TIME_VAL_GTE((*this), rhs);
+ }
+
+ bool operator < (const Pj_Time_Val &rhs) const
+ {
+ return PJ_TIME_VAL_LT((*this), rhs);
+ }
+
+ bool operator <= (const Pj_Time_Val &rhs) const
+ {
+ return PJ_TIME_VAL_LTE((*this), rhs);
+ }
+
+ Pj_Time_Val & operator = (const Pj_Time_Val &rhs)
+ {
+ sec = rhs.sec;
+ msec = rhs.msec;
+ return *this;
+ }
+
+ Pj_Time_Val & operator += (const Pj_Time_Val &rhs)
+ {
+ PJ_TIME_VAL_ADD((*this), rhs);
+ return *this;
+ }
+
+ Pj_Time_Val & operator -= (const Pj_Time_Val &rhs)
+ {
+ PJ_TIME_VAL_SUB((*this), rhs);
+ return *this;
+ }
+
+ /* Must include os.hpp to use these, otherwise unresolved in linking */
+ inline pj_status_t gettimeofday();
+ inline pj_parsed_time decode();
+ inline pj_status_t encode(const pj_parsed_time *pt);
+ inline pj_status_t to_gmt();
+ inline pj_status_t to_local();
+
+
+private:
+ void normalize()
+ {
+ pj_time_val_normalize(this);
+ }
+
+};
+
+#endif /* __PJPP_TYPES_HPP__ */
+
diff --git a/pjlib/include/pj/addr_resolv.h b/pjlib/include/pj/addr_resolv.h
index ec64b401..3d75798c 100644
--- a/pjlib/include/pj/addr_resolv.h
+++ b/pjlib/include/pj/addr_resolv.h
@@ -1,92 +1,92 @@
-/* $Id$ */
-/*
- * Copyright (C)2003-2006 Benny Prijono <benny@prijono.org>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
-#ifndef __PJ_ADDR_RESOLV_H__
-#define __PJ_ADDR_RESOLV_H__
-
-/**
- * @file addr_resolv.h
- * @brief Address resolve (pj_gethostbyname()).
- */
-
-#include <pj/types.h>
-
-PJ_BEGIN_DECL
-
-/**
- * @defgroup pj_addr_resolve Network Address Resolution
- * @ingroup PJ_IO
- * @{
- *
- * This module provides function to resolve Internet address of the
- * specified host name. To resolve a particular host name, application
- * can just call #pj_gethostbyname().
- *
- * Example:
- * <pre>
- * ...
- * pj_hostent he;
- * pj_status_t rc;
- * pj_str_t host = pj_str("host.example.com");
- *
- * rc = pj_gethostbyname( &host, &he);
- * if (rc != PJ_SUCCESS) {
- * char errbuf[80];
- * pj_strerror( rc, errbuf, sizeof(errbuf));
- * PJ_LOG(2,("sample", "Unable to resolve host, error=%s", errbuf));
- * return rc;
- * }
- *
- * // process address...
- * addr.sin_addr.s_addr = *(pj_uint32_t*)he.h_addr;
- * ...
- * </pre>
- *
- * It's pretty simple really...
- */
-
-/** This structure describes an Internet host address. */
-typedef struct pj_hostent
-{
- char *h_name; /**< The official name of the host. */
- char **h_aliases; /**< Aliases list. */
- int h_addrtype; /**< Host address type. */
- int h_length; /**< Length of address. */
- char **h_addr_list; /**< List of addresses. */
-} pj_hostent;
-
-/** Shortcut to h_addr_list[0] */
-#define h_addr h_addr_list[0]
-
-/**
- * This function fills the structure of type pj_hostent for a given host name.
- *
- * @param name Host name, or IPv4 or IPv6 address in standard dot notation.
- * @param he The pj_hostent structure to be filled.
- *
- * @return PJ_SUCCESS, or the appropriate error codes.
- */
-PJ_DECL(pj_status_t) pj_gethostbyname(const pj_str_t *name, pj_hostent *he);
-
-
-/** @} */
-
-PJ_END_DECL
-
-#endif /* __PJ_ADDR_RESOLV_H__ */
-
+/* $Id$ */
+/*
+ * Copyright (C)2003-2006 Benny Prijono <benny@prijono.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#ifndef __PJ_ADDR_RESOLV_H__
+#define __PJ_ADDR_RESOLV_H__
+
+/**
+ * @file addr_resolv.h
+ * @brief Address resolve (pj_gethostbyname()).
+ */
+
+#include <pj/types.h>
+
+PJ_BEGIN_DECL
+
+/**
+ * @defgroup pj_addr_resolve Network Address Resolution
+ * @ingroup PJ_IO
+ * @{
+ *
+ * This module provides function to resolve Internet address of the
+ * specified host name. To resolve a particular host name, application
+ * can just call #pj_gethostbyname().
+ *
+ * Example:
+ * <pre>
+ * ...
+ * pj_hostent he;
+ * pj_status_t rc;
+ * pj_str_t host = pj_str("host.example.com");
+ *
+ * rc = pj_gethostbyname( &host, &he);
+ * if (rc != PJ_SUCCESS) {
+ * char errbuf[80];
+ * pj_strerror( rc, errbuf, sizeof(errbuf));
+ * PJ_LOG(2,("sample", "Unable to resolve host, error=%s", errbuf));
+ * return rc;
+ * }
+ *
+ * // process address...
+ * addr.sin_addr.s_addr = *(pj_uint32_t*)he.h_addr;
+ * ...
+ * </pre>
+ *
+ * It's pretty simple really...
+ */
+
+/** This structure describes an Internet host address. */
+typedef struct pj_hostent
+{
+ char *h_name; /**< The official name of the host. */
+ char **h_aliases; /**< Aliases list. */
+ int h_addrtype; /**< Host address type. */
+ int h_length; /**< Length of address. */
+ char **h_addr_list; /**< List of addresses. */
+} pj_hostent;
+
+/** Shortcut to h_addr_list[0] */
+#define h_addr h_addr_list[0]
+
+/**
+ * This function fills the structure of type pj_hostent for a given host name.
+ *
+ * @param name Host name, or IPv4 or IPv6 address in standard dot notation.
+ * @param he The pj_hostent structure to be filled.
+ *
+ * @return PJ_SUCCESS, or the appropriate error codes.
+ */
+PJ_DECL(pj_status_t) pj_gethostbyname(const pj_str_t *name, pj_hostent *he);
+
+
+/** @} */
+
+PJ_END_DECL
+
+#endif /* __PJ_ADDR_RESOLV_H__ */
+
diff --git a/pjlib/include/pj/array.h b/pjlib/include/pj/array.h
index e03bf050..d7321116 100644
--- a/pjlib/include/pj/array.h
+++ b/pjlib/include/pj/array.h
@@ -1,95 +1,95 @@
-/* $Id$ */
-/*
- * Copyright (C)2003-2006 Benny Prijono <benny@prijono.org>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
-#ifndef __PJ_ARRAY_H__
-#define __PJ_ARRAY_H__
-
-/**
- * @file array.h
- * @brief PJLIB Array helper.
- */
-#include <pj/types.h>
-
-PJ_BEGIN_DECL
-
-/**
- * @defgroup PJ_ARRAY Array helper.
- * @ingroup PJ_DS
- * @{
- *
- * This module provides helper to manipulate array of elements of any size.
- * It provides most used array operations such as insert, erase, and search.
- */
-
-/**
- * Insert value to the array at the given position, and rearrange the
- * remaining nodes after the position.
- *
- * @param array the array.
- * @param elem_size the size of the individual element.
- * @param count the current number of elements in the array.
- * @param pos the position where the new element is put.
- * @param value the value to copy to the new element.
- */
-PJ_DECL(void) pj_array_insert( void *array,
- unsigned elem_size,
- unsigned count,
- unsigned pos,
- const void *value);
-
-/**
- * Erase a value from the array at given position, and rearrange the remaining
- * elements post the erased element.
- *
- * @param array the array.
- * @param elem_size the size of the individual element.
- * @param count the current number of elements in the array.
- * @param pos the index/position to delete.
- */
-PJ_DECL(void) pj_array_erase( void *array,
- unsigned elem_size,
- unsigned count,
- unsigned pos);
-
-/**
- * Search the first value in the array according to matching function.
- *
- * @param array the array.
- * @param elem_size the individual size of the element.
- * @param count the number of elements.
- * @param matching the matching function, which MUST return PJ_SUCCESS if
- * the specified element match.
- * @param result the pointer to the value found.
- *
- * @return PJ_SUCCESS if value is found, otherwise the error code.
- */
-PJ_DECL(pj_status_t) pj_array_find( const void *array,
- unsigned elem_size,
- unsigned count,
- pj_status_t (*matching)(const void *value),
- void **result);
-
-/**
- * @}
- */
-
-PJ_END_DECL
-
-
-#endif /* __PJ_ARRAY_H__ */
-
+/* $Id$ */
+/*
+ * Copyright (C)2003-2006 Benny Prijono <benny@prijono.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#ifndef __PJ_ARRAY_H__
+#define __PJ_ARRAY_H__
+
+/**
+ * @file array.h
+ * @brief PJLIB Array helper.
+ */
+#include <pj/types.h>
+
+PJ_BEGIN_DECL
+
+/**
+ * @defgroup PJ_ARRAY Array helper.
+ * @ingroup PJ_DS
+ * @{
+ *
+ * This module provides helper to manipulate array of elements of any size.
+ * It provides most used array operations such as insert, erase, and search.
+ */
+
+/**
+ * Insert value to the array at the given position, and rearrange the
+ * remaining nodes after the position.
+ *
+ * @param array the array.
+ * @param elem_size the size of the individual element.
+ * @param count the current number of elements in the array.
+ * @param pos the position where the new element is put.
+ * @param value the value to copy to the new element.
+ */
+PJ_DECL(void) pj_array_insert( void *array,
+ unsigned elem_size,
+ unsigned count,
+ unsigned pos,
+ const void *value);
+
+/**
+ * Erase a value from the array at given position, and rearrange the remaining
+ * elements post the erased element.
+ *
+ * @param array the array.
+ * @param elem_size the size of the individual element.
+ * @param count the current number of elements in the array.
+ * @param pos the index/position to delete.
+ */
+PJ_DECL(void) pj_array_erase( void *array,
+ unsigned elem_size,
+ unsigned count,
+ unsigned pos);
+
+/**
+ * Search the first value in the array according to matching function.
+ *
+ * @param array the array.
+ * @param elem_size the individual size of the element.
+ * @param count the number of elements.
+ * @param matching the matching function, which MUST return PJ_SUCCESS if
+ * the specified element match.
+ * @param result the pointer to the value found.
+ *
+ * @return PJ_SUCCESS if value is found, otherwise the error code.
+ */
+PJ_DECL(pj_status_t) pj_array_find( const void *array,
+ unsigned elem_size,
+ unsigned count,
+ pj_status_t (*matching)(const void *value),
+ void **result);
+
+/**
+ * @}
+ */
+
+PJ_END_DECL
+
+
+#endif /* __PJ_ARRAY_H__ */
+
diff --git a/pjlib/include/pj/assert.h b/pjlib/include/pj/assert.h
index 2206b40a..5a842477 100644
--- a/pjlib/include/pj/assert.h
+++ b/pjlib/include/pj/assert.h
@@ -1,73 +1,73 @@
-/* $Id$ */
-/*
- * Copyright (C)2003-2006 Benny Prijono <benny@prijono.org>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
-#ifndef __PJ_ASSERT_H__
-#define __PJ_ASSERT_H__
-
-/**
- * @file assert.h
- * @brief Assertion macro pj_assert().
- */
-
-#include <pj/config.h>
-#include <pj/compat/assert.h>
-
-/**
- * @defgroup pj_assert Assertion Macro
- * @ingroup PJ_MISC
- * @{
- *
- * Assertion and other helper macros for sanity checking.
- */
-
-/**
- * @hideinitializer
- * Check during debug build that an expression is true. If the expression
- * computes to false during run-time, then the program will stop at the
- * offending statements.
- * For release build, this macro will not do anything.
- *
- * @param expr The expression to be evaluated.
- */
-#define pj_assert(expr) assert(expr)
-
-
-/**
- * @hideinitializer
- * If #PJ_ENABLE_EXTRA_CHECK is declared and non-zero, then
- * #PJ_ASSERT_RETURN macro will evaluate the expression in @a expr during
- * run-time. If the expression yields false, assertion will be triggered
- * and the current function will return with the specified return value.
- *
- * If #PJ_ENABLE_EXTRA_CHECK is not declared or is zero, then no run-time
- * checking will be performed. The macro simply evaluates to pj_assert(expr).
- */
-#if defined(PJ_ENABLE_EXTRA_CHECK) && PJ_ENABLE_EXTRA_CHECK != 0
-# define PJ_ASSERT_RETURN(expr,retval) \
- do { \
- pj_assert(expr); \
- if (!(expr)) return retval; \
- } while (0)
-#else
-# define PJ_ASSERT_RETURN(expr,retval) pj_assert(expr)
-#endif
-
-/** @} */
-
-#endif /* __PJ_ASSERT_H__ */
-
+/* $Id$ */
+/*
+ * Copyright (C)2003-2006 Benny Prijono <benny@prijono.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#ifndef __PJ_ASSERT_H__
+#define __PJ_ASSERT_H__
+
+/**
+ * @file assert.h
+ * @brief Assertion macro pj_assert().
+ */
+
+#include <pj/config.h>
+#include <pj/compat/assert.h>
+
+/**
+ * @defgroup pj_assert Assertion Macro
+ * @ingroup PJ_MISC
+ * @{
+ *
+ * Assertion and other helper macros for sanity checking.
+ */
+
+/**
+ * @hideinitializer
+ * Check during debug build that an expression is true. If the expression
+ * computes to false during run-time, then the program will stop at the
+ * offending statements.
+ * For release build, this macro will not do anything.
+ *
+ * @param expr The expression to be evaluated.
+ */
+#define pj_assert(expr) assert(expr)
+
+
+/**
+ * @hideinitializer
+ * If #PJ_ENABLE_EXTRA_CHECK is declared and non-zero, then
+ * #PJ_ASSERT_RETURN macro will evaluate the expression in @a expr during
+ * run-time. If the expression yields false, assertion will be triggered
+ * and the current function will return with the specified return value.
+ *
+ * If #PJ_ENABLE_EXTRA_CHECK is not declared or is zero, then no run-time
+ * checking will be performed. The macro simply evaluates to pj_assert(expr).
+ */
+#if defined(PJ_ENABLE_EXTRA_CHECK) && PJ_ENABLE_EXTRA_CHECK != 0
+# define PJ_ASSERT_RETURN(expr,retval) \
+ do { \
+ pj_assert(expr); \
+ if (!(expr)) return retval; \
+ } while (0)
+#else
+# define PJ_ASSERT_RETURN(expr,retval) pj_assert(expr)
+#endif
+
+/** @} */
+
+#endif /* __PJ_ASSERT_H__ */
+
diff --git a/pjlib/include/pj/compat/assert.h b/pjlib/include/pj/compat/assert.h
index e7d6fe70..fa63c701 100644
--- a/pjlib/include/pj/compat/assert.h
+++ b/pjlib/include/pj/compat/assert.h
@@ -1,43 +1,43 @@
-/* $Id$ */
-/*
- * Copyright (C)2003-2006 Benny Prijono <benny@prijono.org>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
-#ifndef __PJ_COMPAT_ASSERT_H__
-#define __PJ_COMPAT_ASSERT_H__
-
-/**
- * @file assert.h
- * @brief Provides assert() macro.
- */
-
-#if defined(PJ_HAS_ASSERT_H) && PJ_HAS_ASSERT_H != 0
-# include <assert.h>
-
-#elif defined(PJ_LINUX_KERNEL) && PJ_LINUX_KERNEL != 0
-# define assert(expr) do { \
- if (!(expr)) \
- printk("!!ASSERTION FAILED: [%s:%d] \"" #expr "\"\n",\
- __FILE__, __LINE__); \
- } while (0)
-
-#else
-# warning "assert() is not implemented"
-# define assert(expr)
-#endif
-
-#endif /* __PJ_COMPAT_ASSERT_H__ */
-
+/* $Id$ */
+/*
+ * Copyright (C)2003-2006 Benny Prijono <benny@prijono.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#ifndef __PJ_COMPAT_ASSERT_H__
+#define __PJ_COMPAT_ASSERT_H__
+
+/**
+ * @file assert.h
+ * @brief Provides assert() macro.
+ */
+
+#if defined(PJ_HAS_ASSERT_H) && PJ_HAS_ASSERT_H != 0
+# include <assert.h>
+
+#elif defined(PJ_LINUX_KERNEL) && PJ_LINUX_KERNEL != 0
+# define assert(expr) do { \
+ if (!(expr)) \
+ printk("!!ASSERTION FAILED: [%s:%d] \"" #expr "\"\n",\
+ __FILE__, __LINE__); \
+ } while (0)
+
+#else
+# warning "assert() is not implemented"
+# define assert(expr)
+#endif
+
+#endif /* __PJ_COMPAT_ASSERT_H__ */
+
diff --git a/pjlib/include/pj/compat/cc_gcc.h b/pjlib/include/pj/compat/cc_gcc.h
index 10c78e00..eeca7e48 100644
--- a/pjlib/include/pj/compat/cc_gcc.h
+++ b/pjlib/include/pj/compat/cc_gcc.h
@@ -1,43 +1,43 @@
-/* $Id$ */
-/*
- * Copyright (C)2003-2006 Benny Prijono <benny@prijono.org>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
-#ifndef __PJ_COMPAT_CC_GCC_H__
-#define __PJ_COMPAT_CC_GCC_H__
-
-/**
- * @file cc_gcc.h
- * @brief Describes GCC compiler specifics.
- */
-
-#ifndef __GNUC__
-# error "This file is only for gcc!"
-#endif
-
-#define PJ_INLINE_SPECIFIER static inline
-#define PJ_THREAD_FUNC
-#define PJ_NORETURN
-#define PJ_ATTR_NORETURN __attribute__ ((noreturn))
-
-#define PJ_HAS_INT64 1
-
-typedef long long pj_int64_t;
-typedef unsigned long long pj_uint64_t;
-
-
-#endif /* __PJ_COMPAT_CC_GCC_H__ */
-
+/* $Id$ */
+/*
+ * Copyright (C)2003-2006 Benny Prijono <benny@prijono.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#ifndef __PJ_COMPAT_CC_GCC_H__
+#define __PJ_COMPAT_CC_GCC_H__
+
+/**
+ * @file cc_gcc.h
+ * @brief Describes GCC compiler specifics.
+ */
+
+#ifndef __GNUC__
+# error "This file is only for gcc!"
+#endif
+
+#define PJ_INLINE_SPECIFIER static inline
+#define PJ_THREAD_FUNC
+#define PJ_NORETURN
+#define PJ_ATTR_NORETURN __attribute__ ((noreturn))
+
+#define PJ_HAS_INT64 1
+
+typedef long long pj_int64_t;
+typedef unsigned long long pj_uint64_t;
+
+
+#endif /* __PJ_COMPAT_CC_GCC_H__ */
+
diff --git a/pjlib/include/pj/compat/cc_msvc.h b/pjlib/include/pj/compat/cc_msvc.h
index 08a9d76b..010d7caa 100644
--- a/pjlib/include/pj/compat/cc_msvc.h
+++ b/pjlib/include/pj/compat/cc_msvc.h
@@ -1,47 +1,47 @@
-/* $Id$ */
-/*
- * Copyright (C)2003-2006 Benny Prijono <benny@prijono.org>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
-#ifndef __PJ_COMPAT_CC_MSVC_H__
-#define __PJ_COMPAT_CC_MSVC_H__
-
-/**
- * @file cc_msvc.h
- * @brief Describes Microsoft Visual C compiler specifics.
- */
-
-#ifndef _MSC_VER
-# error "This header file is only for Visual C compiler!"
-#endif
-
-# pragma warning(disable: 4127) // conditional expression is constant
-# pragma warning(disable: 4611) // not wise to mix setjmp with C++
-# pragma warning(disable: 4514) // unreferenced inline function has been removed
-# ifdef __cplusplus
-# define PJ_INLINE_SPECIFIER inline
-# else
-# define PJ_INLINE_SPECIFIER static __inline
-# endif
-# define PJ_THREAD_FUNC
-# define PJ_NORETURN __declspec(noreturn)
-# define PJ_ATTR_NORETURN
-
-# define PJ_HAS_INT64 1
-typedef __int64 pj_int64_t;
-typedef unsigned __int64 pj_uint64_t;
-
-#endif /* __PJ_COMPAT_CC_MSVC_H__ */
+/* $Id$ */
+/*
+ * Copyright (C)2003-2006 Benny Prijono <benny@prijono.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#ifndef __PJ_COMPAT_CC_MSVC_H__
+#define __PJ_COMPAT_CC_MSVC_H__
+
+/**
+ * @file cc_msvc.h
+ * @brief Describes Microsoft Visual C compiler specifics.
+ */
+
+#ifndef _MSC_VER
+# error "This header file is only for Visual C compiler!"
+#endif
+
+# pragma warning(disable: 4127) // conditional expression is constant
+# pragma warning(disable: 4611) // not wise to mix setjmp with C++
+# pragma warning(disable: 4514) // unreferenced inline function has been removed
+# ifdef __cplusplus
+# define PJ_INLINE_SPECIFIER inline
+# else
+# define PJ_INLINE_SPECIFIER static __inline
+# endif
+# define PJ_THREAD_FUNC
+# define PJ_NORETURN __declspec(noreturn)
+# define PJ_ATTR_NORETURN
+
+# define PJ_HAS_INT64 1
+typedef __int64 pj_int64_t;
+typedef unsigned __int64 pj_uint64_t;
+
+#endif /* __PJ_COMPAT_CC_MSVC_H__ */
diff --git a/pjlib/include/pj/compat/ctype.h b/pjlib/include/pj/compat/ctype.h
index 8ab19c0f..b5326f86 100644
--- a/pjlib/include/pj/compat/ctype.h
+++ b/pjlib/include/pj/compat/ctype.h
@@ -1,46 +1,46 @@
-/* $Id$ */
-/*
- * Copyright (C)2003-2006 Benny Prijono <benny@prijono.org>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
-#ifndef __PJ_COMPAT_CTYPE_H__
-#define __PJ_COMPAT_CTYPE_H__
-
-/**
- * @file ctype.h
- * @brief Provides ctype function family.
- */
-
-#if defined(PJ_HAS_CTYPE_H) && PJ_HAS_CTYPE_H != 0
-# include <ctype.h>
-#else
-# define isalnum(c) (isalpha(c) || isdigit(c))
-# define isalpha(c) (islower(c) || isupper(c))
-# define isascii(c) (((unsigned char)(c))<=0x7f)
-# define isdigit(c) ((c)>='0' && (c)<='9')
-# define isspace(c) ((c)==' ' || (c)=='\t' ||\
- (c)=='\n' || (c)=='\r' || (c)=='\v')
-# define islower(c) ((c)>='a' && (c)<='z')
-# define isupper(c) ((c)>='A' && (c)<='Z')
-# define isxdigit(c) (isdigit(c) || (tolower(c)>='a'&&tolower(c)<='f'))
-# define tolower(c) (((c) >= 'A' && (c) <= 'Z') ? (c)+('a'-'A') : (c))
-# define toupper(c) (((c) >= 'a' && (c) <= 'z') ? (c)-('a'-'A') : (c))
-#endif
-
-#define isblank(c) (c==' ' || c=='\t')
-
-
-#endif /* __PJ_COMPAT_CTYPE_H__ */
+/* $Id$ */
+/*
+ * Copyright (C)2003-2006 Benny Prijono <benny@prijono.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#ifndef __PJ_COMPAT_CTYPE_H__
+#define __PJ_COMPAT_CTYPE_H__
+
+/**
+ * @file ctype.h
+ * @brief Provides ctype function family.
+ */
+
+#if defined(PJ_HAS_CTYPE_H) && PJ_HAS_CTYPE_H != 0
+# include <ctype.h>
+#else
+# define isalnum(c) (isalpha(c) || isdigit(c))
+# define isalpha(c) (islower(c) || isupper(c))
+# define isascii(c) (((unsigned char)(c))<=0x7f)
+# define isdigit(c) ((c)>='0' && (c)<='9')
+# define isspace(c) ((c)==' ' || (c)=='\t' ||\
+ (c)=='\n' || (c)=='\r' || (c)=='\v')
+# define islower(c) ((c)>='a' && (c)<='z')
+# define isupper(c) ((c)>='A' && (c)<='Z')
+# define isxdigit(c) (isdigit(c) || (tolower(c)>='a'&&tolower(c)<='f'))
+# define tolower(c) (((c) >= 'A' && (c) <= 'Z') ? (c)+('a'-'A') : (c))
+# define toupper(c) (((c) >= 'a' && (c) <= 'z') ? (c)-('a'-'A') : (c))
+#endif
+
+#define isblank(c) (c==' ' || c=='\t')
+
+
+#endif /* __PJ_COMPAT_CTYPE_H__ */
diff --git a/pjlib/include/pj/compat/errno.h b/pjlib/include/pj/compat/errno.h
index 0df08162..a4246d72 100644
--- a/pjlib/include/pj/compat/errno.h
+++ b/pjlib/include/pj/compat/errno.h
@@ -1,44 +1,44 @@
-/* $Id$ */
-/*
- * Copyright (C)2003-2006 Benny Prijono <benny@prijono.org>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
-#ifndef __PJ_COMPAT_ERRNO_H__
-#define __PJ_COMPAT_ERRNO_H__
-
-#if defined(PJ_WIN32) && PJ_WIN32 != 0
-
- typedef unsigned long pj_os_err_type;
-# define pj_get_native_os_error() GetLastError()
-# define pj_get_native_netos_error() WSAGetLastError()
-
-#elif (defined(PJ_LINUX) && PJ_LINUX != 0) || \
- (defined(PJ_LINUX_KERNEL) && PJ_LINUX_KERNEL != 0) || \
- (defined(PJ_SUNOS) && PJ_SUNOS != 0)
-
- typedef int pj_os_err_type;
-# define pj_get_native_os_error() (errno)
-# define pj_get_native_netos_error() (errno)
-
-#else
-
-# error "Please define pj_os_err_type for this platform here!"
-
-#endif
-
-
-#endif /* __PJ_COMPAT_ERRNO_H__ */
-
+/* $Id$ */
+/*
+ * Copyright (C)2003-2006 Benny Prijono <benny@prijono.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#ifndef __PJ_COMPAT_ERRNO_H__
+#define __PJ_COMPAT_ERRNO_H__
+
+#if defined(PJ_WIN32) && PJ_WIN32 != 0
+
+ typedef unsigned long pj_os_err_type;
+# define pj_get_native_os_error() GetLastError()
+# define pj_get_native_netos_error() WSAGetLastError()
+
+#elif (defined(PJ_LINUX) && PJ_LINUX != 0) || \
+ (defined(PJ_LINUX_KERNEL) && PJ_LINUX_KERNEL != 0) || \
+ (defined(PJ_SUNOS) && PJ_SUNOS != 0)
+
+ typedef int pj_os_err_type;
+# define pj_get_native_os_error() (errno)
+# define pj_get_native_netos_error() (errno)
+
+#else
+
+# error "Please define pj_os_err_type for this platform here!"
+
+#endif
+
+
+#endif /* __PJ_COMPAT_ERRNO_H__ */
+
diff --git a/pjlib/include/pj/compat/high_precision.h b/pjlib/include/pj/compat/high_precision.h
index 141452ce..b854cb28 100644
--- a/pjlib/include/pj/compat/high_precision.h
+++ b/pjlib/include/pj/compat/high_precision.h
@@ -1,102 +1,102 @@
-/* $Id$ */
-/*
- * Copyright (C)2003-2006 Benny Prijono <benny@prijono.org>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
-#ifndef __PJ_COMPAT_HIGH_PRECISION_H__
-#define __PJ_COMPAT_HIGH_PRECISION_H__
-
-
-#if defined(PJ_HAS_FLOATING_POINT) && PJ_HAS_FLOATING_POINT != 0
- /*
- * The first choice for high precision math is to use double.
- */
-# include <math.h>
- typedef double pj_highprec_t;
-
-# define PJ_HIGHPREC_VALUE_IS_ZERO(a) (a==0)
-# define pj_highprec_mod(a,b) (a=fmod(a,b))
-
-#elif defined(PJ_LINUX_KERNEL) && PJ_LINUX_KERNEL != 0
-
-# include <asm/div64.h>
-
- typedef pj_int64_t pj_highprec_t;
-
-# define pj_highprec_div(a1,a2) do_div(a1,a2)
-# define pj_highprec_mod(a1,a2) (a1=do_mod(a1, a2))
-
- PJ_INLINE(pj_int64_t) do_mod( pj_int64_t a1, pj_int64_t a2)
- {
- return do_div(a1,a2);
- }
-
-
-#elif defined(PJ_HAS_INT64) && PJ_HAS_INT64 != 0
- /*
- * Next choice is to use 64-bit arithmatics.
- */
- typedef pj_int64_t pj_highprec_t;
-
-#else
-# warning "High precision math is not available"
-
- /*
- * Last, fallback to 32-bit arithmetics.
- */
- typedef pj_int32_t pj_highprec_t;
-
-#endif
-
-/**
- * @def pj_highprec_mul
- * pj_highprec_mul(a1, a2) - High Precision Multiplication
- * Multiply a1 and a2, and store the result in a1.
- */
-#ifndef pj_highprec_mul
-# define pj_highprec_mul(a1,a2) (a1 = a1 * a2)
-#endif
-
-/**
- * @def pj_highprec_div
- * pj_highprec_div(a1, a2) - High Precision Division
- * Divide a2 from a1, and store the result in a1.
- */
-#ifndef pj_highprec_div
-# define pj_highprec_div(a1,a2) (a1 = a1 / a2)
-#endif
-
-/**
- * @def pj_highprec_mod
- * pj_highprec_mod(a1, a2) - High Precision Modulus
- * Get the modulus a2 from a1, and store the result in a1.
- */
-#ifndef pj_highprec_mod
-# define pj_highprec_mod(a1,a2) (a1 = a1 % a2)
-#endif
-
-
-/**
- * @def PJ_HIGHPREC_VALUE_IS_ZERO(a)
- * Test if the specified high precision value is zero.
- */
-#ifndef PJ_HIGHPREC_VALUE_IS_ZERO
-# define PJ_HIGHPREC_VALUE_IS_ZERO(a) (a==0)
-#endif
-
-
-#endif /* __PJ_COMPAT_HIGH_PRECISION_H__ */
-
+/* $Id$ */
+/*
+ * Copyright (C)2003-2006 Benny Prijono <benny@prijono.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#ifndef __PJ_COMPAT_HIGH_PRECISION_H__
+#define __PJ_COMPAT_HIGH_PRECISION_H__
+
+
+#if defined(PJ_HAS_FLOATING_POINT) && PJ_HAS_FLOATING_POINT != 0
+ /*
+ * The first choice for high precision math is to use double.
+ */
+# include <math.h>
+ typedef double pj_highprec_t;
+
+# define PJ_HIGHPREC_VALUE_IS_ZERO(a) (a==0)
+# define pj_highprec_mod(a,b) (a=fmod(a,b))
+
+#elif defined(PJ_LINUX_KERNEL) && PJ_LINUX_KERNEL != 0
+
+# include <asm/div64.h>
+
+ typedef pj_int64_t pj_highprec_t;
+
+# define pj_highprec_div(a1,a2) do_div(a1,a2)
+# define pj_highprec_mod(a1,a2) (a1=do_mod(a1, a2))
+
+ PJ_INLINE(pj_int64_t) do_mod( pj_int64_t a1, pj_int64_t a2)
+ {
+ return do_div(a1,a2);
+ }
+
+
+#elif defined(PJ_HAS_INT64) && PJ_HAS_INT64 != 0
+ /*
+ * Next choice is to use 64-bit arithmatics.
+ */
+ typedef pj_int64_t pj_highprec_t;
+
+#else
+# warning "High precision math is not available"
+
+ /*
+ * Last, fallback to 32-bit arithmetics.
+ */
+ typedef pj_int32_t pj_highprec_t;
+
+#endif
+
+/**
+ * @def pj_highprec_mul
+ * pj_highprec_mul(a1, a2) - High Precision Multiplication
+ * Multiply a1 and a2, and store the result in a1.
+ */
+#ifndef pj_highprec_mul
+# define pj_highprec_mul(a1,a2) (a1 = a1 * a2)
+#endif
+
+/**
+ * @def pj_highprec_div
+ * pj_highprec_div(a1, a2) - High Precision Division
+ * Divide a2 from a1, and store the result in a1.
+ */
+#ifndef pj_highprec_div
+# define pj_highprec_div(a1,a2) (a1 = a1 / a2)
+#endif
+
+/**
+ * @def pj_highprec_mod
+ * pj_highprec_mod(a1, a2) - High Precision Modulus
+ * Get the modulus a2 from a1, and store the result in a1.
+ */
+#ifndef pj_highprec_mod
+# define pj_highprec_mod(a1,a2) (a1 = a1 % a2)
+#endif
+
+
+/**
+ * @def PJ_HIGHPREC_VALUE_IS_ZERO(a)
+ * Test if the specified high precision value is zero.
+ */
+#ifndef PJ_HIGHPREC_VALUE_IS_ZERO
+# define PJ_HIGHPREC_VALUE_IS_ZERO(a) (a==0)
+#endif
+
+
+#endif /* __PJ_COMPAT_HIGH_PRECISION_H__ */
+
diff --git a/pjlib/include/pj/compat/m_alpha.h b/pjlib/include/pj/compat/m_alpha.h
index 852696ab..89423e9c 100644
--- a/pjlib/include/pj/compat/m_alpha.h
+++ b/pjlib/include/pj/compat/m_alpha.h
@@ -1,33 +1,33 @@
-/* $Id$ */
-/*
- * Copyright (C)2003-2006 Benny Prijono <benny@prijono.org>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
-#ifndef __PJ_COMPAT_M_ALPHA_H__
-#define __PJ_COMPAT_M_ALPHA_H__
-
-/**
- * @file m_alpha.h
- * @brief Describes Alpha processor family specifics.
- */
-
-#define PJ_HAS_PENTIUM 0
-#define PJ_IS_LITTLE_ENDIAN 1
-#define PJ_IS_BIG_ENDIAN 0
-
-
-#endif /* __PJ_COMPAT_M_ALPHA_H__ */
-
+/* $Id$ */
+/*
+ * Copyright (C)2003-2006 Benny Prijono <benny@prijono.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#ifndef __PJ_COMPAT_M_ALPHA_H__
+#define __PJ_COMPAT_M_ALPHA_H__
+
+/**
+ * @file m_alpha.h
+ * @brief Describes Alpha processor family specifics.
+ */
+
+#define PJ_HAS_PENTIUM 0
+#define PJ_IS_LITTLE_ENDIAN 1
+#define PJ_IS_BIG_ENDIAN 0
+
+
+#endif /* __PJ_COMPAT_M_ALPHA_H__ */
+
diff --git a/pjlib/include/pj/compat/m_i386.h b/pjlib/include/pj/compat/m_i386.h
index eaf81536..51288066 100644
--- a/pjlib/include/pj/compat/m_i386.h
+++ b/pjlib/include/pj/compat/m_i386.h
@@ -1,34 +1,34 @@
-/* $Id$ */
-/*
- * Copyright (C)2003-2006 Benny Prijono <benny@prijono.org>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
-#ifndef __PJ_COMPAT_M_i386_H__
-#define __PJ_COMPAT_M_i386_H__
-
-/**
- * @file m_i386.h
- * @brief Describes Intel i386 family processor specifics.
- */
-
-#define PJ_M_I386 1
-
-#define PJ_HAS_PENTIUM 1
-#define PJ_IS_LITTLE_ENDIAN 1
-#define PJ_IS_BIG_ENDIAN 0
-
-
-#endif /* __PJ_COMPAT_M_i386_H__ */
+/* $Id$ */
+/*
+ * Copyright (C)2003-2006 Benny Prijono <benny@prijono.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#ifndef __PJ_COMPAT_M_i386_H__
+#define __PJ_COMPAT_M_i386_H__
+
+/**
+ * @file m_i386.h
+ * @brief Describes Intel i386 family processor specifics.
+ */
+
+#define PJ_M_I386 1
+
+#define PJ_HAS_PENTIUM 1
+#define PJ_IS_LITTLE_ENDIAN 1
+#define PJ_IS_BIG_ENDIAN 0
+
+
+#endif /* __PJ_COMPAT_M_i386_H__ */
diff --git a/pjlib/include/pj/compat/m_m68k.h b/pjlib/include/pj/compat/m_m68k.h
index 314f3736..f3e83ee0 100644
--- a/pjlib/include/pj/compat/m_m68k.h
+++ b/pjlib/include/pj/compat/m_m68k.h
@@ -1,32 +1,32 @@
-/* $Id$ */
-/*
- * Copyright (C)2003-2006 Benny Prijono <benny@prijono.org>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
-#ifndef __PJ_COMPAT_M_M68K_H__
-#define __PJ_COMPAT_M_M68K_H__
-
-/**
- * @file m_m68k.h
- * @brief Describes Motorola m68k family processor specifics.
- */
-
-#define PJ_HAS_PENTIUM 0
-#define PJ_IS_LITTLE_ENDIAN 1
-#define PJ_IS_BIG_ENDIAN 0
-
-
-#endif /* __PJ_COMPAT_M_M68K_H__ */
+/* $Id$ */
+/*
+ * Copyright (C)2003-2006 Benny Prijono <benny@prijono.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#ifndef __PJ_COMPAT_M_M68K_H__
+#define __PJ_COMPAT_M_M68K_H__
+
+/**
+ * @file m_m68k.h
+ * @brief Describes Motorola m68k family processor specifics.
+ */
+
+#define PJ_HAS_PENTIUM 0
+#define PJ_IS_LITTLE_ENDIAN 1
+#define PJ_IS_BIG_ENDIAN 0
+
+
+#endif /* __PJ_COMPAT_M_M68K_H__ */
diff --git a/pjlib/include/pj/compat/m_sparc.h b/pjlib/include/pj/compat/m_sparc.h
index 07df35ab..b2a230e5 100644
--- a/pjlib/include/pj/compat/m_sparc.h
+++ b/pjlib/include/pj/compat/m_sparc.h
@@ -1,34 +1,34 @@
-/* $Id$ */
-/*
- * Copyright (C)2003-2006 Benny Prijono <benny@prijono.org>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
-#ifndef __PJ_COMPAT_M_SPARC_H__
-#define __PJ_COMPAT_M_SPARC_H__
-
-/**
- * @file m_sparc.h
- * @brief Describes SPARC family processor specifics.
- */
-
-#define PJ_SPARC 1
-
-#define PJ_HAS_PENTIUM 0
-#define PJ_IS_LITTLE_ENDIAN 0
-#define PJ_IS_BIG_ENDIAN 1
-
-
-#endif /* __PJ_COMPAT_M_SPARC_H__ */
+/* $Id$ */
+/*
+ * Copyright (C)2003-2006 Benny Prijono <benny@prijono.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#ifndef __PJ_COMPAT_M_SPARC_H__
+#define __PJ_COMPAT_M_SPARC_H__
+
+/**
+ * @file m_sparc.h
+ * @brief Describes SPARC family processor specifics.
+ */
+
+#define PJ_SPARC 1
+
+#define PJ_HAS_PENTIUM 0
+#define PJ_IS_LITTLE_ENDIAN 0
+#define PJ_IS_BIG_ENDIAN 1
+
+
+#endif /* __PJ_COMPAT_M_SPARC_H__ */
diff --git a/pjlib/include/pj/compat/malloc.h b/pjlib/include/pj/compat/malloc.h
index 5843ad78..8b93a81a 100644
--- a/pjlib/include/pj/compat/malloc.h
+++ b/pjlib/include/pj/compat/malloc.h
@@ -1,33 +1,33 @@
-/* $Id$ */
-/*
- * Copyright (C)2003-2006 Benny Prijono <benny@prijono.org>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
-#ifndef __PJ_COMPAT_MALLOC_H__
-#define __PJ_COMPAT_MALLOC_H__
-
-/**
- * @file malloc.h
- * @brief Provides malloc() and free() functions.
- */
-
-#if defined(PJ_HAS_MALLOC_H) && PJ_HAS_MALLOC_H != 0
-# include <malloc.h>
-#elif defined(PJ_HAS_STDLIB_H) && PJ_HAS_STDLIB_H != 0
-# include <stdlib.h>
-#endif
-
-#endif /* __PJ_COMPAT_MALLOC_H__ */
+/* $Id$ */
+/*
+ * Copyright (C)2003-2006 Benny Prijono <benny@prijono.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#ifndef __PJ_COMPAT_MALLOC_H__
+#define __PJ_COMPAT_MALLOC_H__
+
+/**
+ * @file malloc.h
+ * @brief Provides malloc() and free() functions.
+ */
+
+#if defined(PJ_HAS_MALLOC_H) && PJ_HAS_MALLOC_H != 0
+# include <malloc.h>
+#elif defined(PJ_HAS_STDLIB_H) && PJ_HAS_STDLIB_H != 0
+# include <stdlib.h>
+#endif
+
+#endif /* __PJ_COMPAT_MALLOC_H__ */
diff --git a/pjlib/include/pj/compat/os_linux.h b/pjlib/include/pj/compat/os_linux.h
index 1236628d..21d6a302 100644
--- a/pjlib/include/pj/compat/os_linux.h
+++ b/pjlib/include/pj/compat/os_linux.h
@@ -1,82 +1,82 @@
-/* $Id$ */
-/*
- * Copyright (C)2003-2006 Benny Prijono <benny@prijono.org>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
-#ifndef __PJ_COMPAT_OS_LINUX_H__
-#define __PJ_COMPAT_OS_LINUX_H__
-
-/**
- * @file os_linux.h
- * @brief Describes Linux operating system specifics.
- */
-
-#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_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_TIMEB_H 1
-#define PJ_HAS_SYS_TYPES_H 1
-#define PJ_HAS_TIME_H 1
-#define PJ_HAS_UNISTD_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
-
-/* 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
-#define PJ_OS_HAS_CHECK_STACK 0
-
-#define PJ_ATOMIC_VALUE_TYPE long
-
-#endif /* __PJ_COMPAT_OS_LINUX_H__ */
-
+/* $Id$ */
+/*
+ * Copyright (C)2003-2006 Benny Prijono <benny@prijono.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#ifndef __PJ_COMPAT_OS_LINUX_H__
+#define __PJ_COMPAT_OS_LINUX_H__
+
+/**
+ * @file os_linux.h
+ * @brief Describes Linux operating system specifics.
+ */
+
+#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_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_TIMEB_H 1
+#define PJ_HAS_SYS_TYPES_H 1
+#define PJ_HAS_TIME_H 1
+#define PJ_HAS_UNISTD_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
+
+/* 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
+#define PJ_OS_HAS_CHECK_STACK 0
+
+#define PJ_ATOMIC_VALUE_TYPE long
+
+#endif /* __PJ_COMPAT_OS_LINUX_H__ */
+
diff --git a/pjlib/include/pj/compat/os_linux_kernel.h b/pjlib/include/pj/compat/os_linux_kernel.h
index 698bea3f..06cd6bd1 100644
--- a/pjlib/include/pj/compat/os_linux_kernel.h
+++ b/pjlib/include/pj/compat/os_linux_kernel.h
@@ -1,102 +1,102 @@
-/* $Id$ */
-/*
- * Copyright (C)2003-2006 Benny Prijono <benny@prijono.org>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
-#ifndef __PJ_COMPAT_OS_LINUX_KERNEL_H__
-#define __PJ_COMPAT_OS_LINUX_KERNEL_H__
-
-/**
- * @file os_linux.h
- * @brief Describes Linux operating system specifics.
- */
-
-#define PJ_HAS_ARPA_INET_H 0
-#define PJ_HAS_ASSERT_H 0
-#define PJ_HAS_CTYPE_H 0
-#define PJ_HAS_ERRNO_H 0
-#define PJ_HAS_LINUX_SOCKET_H 1
-#define PJ_HAS_MALLOC_H 0
-#define PJ_HAS_NETDB_H 0
-#define PJ_HAS_NETINET_IN_H 0
-#define PJ_HAS_SETJMP_H 0
-#define PJ_HAS_STDARG_H 1
-#define PJ_HAS_STDDEF_H 0
-#define PJ_HAS_STDIO_H 0
-#define PJ_HAS_STDLIB_H 0
-#define PJ_HAS_STRING_H 0
-#define PJ_HAS_SYS_IOCTL_H 0
-#define PJ_HAS_SYS_SELECT_H 0
-#define PJ_HAS_SYS_SOCKET_H 0
-#define PJ_HAS_SYS_TIMEB_H 0
-#define PJ_HAS_SYS_TYPES_H 0
-#define PJ_HAS_TIME_H 0
-#define PJ_HAS_UNISTD_H 0
-
-#define PJ_HAS_MSWSOCK_H 0
-#define PJ_HAS_WINSOCK_H 0
-#define PJ_HAS_WINSOCK2_H 0
-
-#define PJ_SOCK_HAS_INET_ATON 0
-
-/* 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
-
-#ifndef PJ_HAS_THREADS
-# define PJ_HAS_THREADS (1)
-#endif
-
-
-/*
- * Declare __FD_SETSIZE now before including <linux*>.
- */
-#define __FD_SETSIZE PJ_IOQUEUE_MAX_HANDLES
-
-#define NULL ((void*)0)
-
-#include <linux/module.h> /* Needed by all modules */
-#include <linux/kernel.h> /* Needed for KERN_INFO */
-
-#define __PJ_EXPORT_SYMBOL(a) EXPORT_SYMBOL(a);
-
-/*
- * Override features.
- */
-#define PJ_HAS_FLOATING_POINT 0
-#define PJ_HAS_MALLOC 0
-#define PJ_HAS_SEMAPHORE 0
-#define PJ_HAS_EVENT_OBJ 0
-#define PJ_HAS_HIGH_RES_TIMER 1
-#define PJ_OS_HAS_CHECK_STACK 0
-#define PJ_TERM_HAS_COLOR 0
-
-#define PJ_ATOMIC_VALUE_TYPE int
-#define PJ_THREAD_DESC_SIZE 128
-
-#endif /* __PJ_COMPAT_OS_LINUX_KERNEL_H__ */
-
+/* $Id$ */
+/*
+ * Copyright (C)2003-2006 Benny Prijono <benny@prijono.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#ifndef __PJ_COMPAT_OS_LINUX_KERNEL_H__
+#define __PJ_COMPAT_OS_LINUX_KERNEL_H__
+
+/**
+ * @file os_linux.h
+ * @brief Describes Linux operating system specifics.
+ */
+
+#define PJ_HAS_ARPA_INET_H 0
+#define PJ_HAS_ASSERT_H 0
+#define PJ_HAS_CTYPE_H 0
+#define PJ_HAS_ERRNO_H 0
+#define PJ_HAS_LINUX_SOCKET_H 1
+#define PJ_HAS_MALLOC_H 0
+#define PJ_HAS_NETDB_H 0
+#define PJ_HAS_NETINET_IN_H 0
+#define PJ_HAS_SETJMP_H 0
+#define PJ_HAS_STDARG_H 1
+#define PJ_HAS_STDDEF_H 0
+#define PJ_HAS_STDIO_H 0
+#define PJ_HAS_STDLIB_H 0
+#define PJ_HAS_STRING_H 0
+#define PJ_HAS_SYS_IOCTL_H 0
+#define PJ_HAS_SYS_SELECT_H 0
+#define PJ_HAS_SYS_SOCKET_H 0
+#define PJ_HAS_SYS_TIMEB_H 0
+#define PJ_HAS_SYS_TYPES_H 0
+#define PJ_HAS_TIME_H 0
+#define PJ_HAS_UNISTD_H 0
+
+#define PJ_HAS_MSWSOCK_H 0
+#define PJ_HAS_WINSOCK_H 0
+#define PJ_HAS_WINSOCK2_H 0
+
+#define PJ_SOCK_HAS_INET_ATON 0
+
+/* 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
+
+#ifndef PJ_HAS_THREADS
+# define PJ_HAS_THREADS (1)
+#endif
+
+
+/*
+ * Declare __FD_SETSIZE now before including <linux*>.
+ */
+#define __FD_SETSIZE PJ_IOQUEUE_MAX_HANDLES
+
+#define NULL ((void*)0)
+
+#include <linux/module.h> /* Needed by all modules */
+#include <linux/kernel.h> /* Needed for KERN_INFO */
+
+#define __PJ_EXPORT_SYMBOL(a) EXPORT_SYMBOL(a);
+
+/*
+ * Override features.
+ */
+#define PJ_HAS_FLOATING_POINT 0
+#define PJ_HAS_MALLOC 0
+#define PJ_HAS_SEMAPHORE 0
+#define PJ_HAS_EVENT_OBJ 0
+#define PJ_HAS_HIGH_RES_TIMER 1
+#define PJ_OS_HAS_CHECK_STACK 0
+#define PJ_TERM_HAS_COLOR 0
+
+#define PJ_ATOMIC_VALUE_TYPE int
+#define PJ_THREAD_DESC_SIZE 128
+
+#endif /* __PJ_COMPAT_OS_LINUX_KERNEL_H__ */
+
diff --git a/pjlib/include/pj/compat/os_palmos.h b/pjlib/include/pj/compat/os_palmos.h
index e890d747..54dea228 100644
--- a/pjlib/include/pj/compat/os_palmos.h
+++ b/pjlib/include/pj/compat/os_palmos.h
@@ -1,77 +1,77 @@
-/* $Id$ */
-/*
- * Copyright (C)2003-2006 Benny Prijono <benny@prijono.org>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
-#ifndef __PJ_COMPAT_OS_PALMOS_H__
-#define __PJ_COMPAT_OS_PALMOS_H__
-
-/**
- * @file os_palmos.h
- * @brief Describes PalmOS operating system specifics.
- */
-
-#define PJ_HAS_ARPA_INET_H 0
-#define PJ_HAS_ASSERT_H 1
-#define PJ_HAS_CTYPE_H 1
-#define PJ_HAS_ERRNO_H 0
-#define PJ_HAS_MALLOC_H 1
-#define PJ_HAS_NETDB_H 0
-#define PJ_HAS_NETINET_IN_H 0
-#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_STRING_H 1
-#define PJ_HAS_SYS_IOCTL_H 0
-#define PJ_HAS_SYS_SELECT_H 0
-#define PJ_HAS_SYS_SOCKET_H 0
-#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 0
-
-#define PJ_HAS_MSWSOCK_H 0
-#define PJ_HAS_WINSOCK_H 0
-#define PJ_HAS_WINSOCK2_H 0
-
-#define PJ_SOCK_HAS_INET_ATON 0
-
-/* When this macro is set, getsockopt(SOL_SOCKET, SO_ERROR) will return
- * the status of non-blocking connect() operation.
- */
-#define PJ_HAS_SO_ERROR 0
-
-/* 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 xxx
-
-/* 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 xxx
-
-/* 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_OS_HAS_CHECK_STACK 0
-
-#endif /* __PJ_COMPAT_OS_PALMOS_H__ */
+/* $Id$ */
+/*
+ * Copyright (C)2003-2006 Benny Prijono <benny@prijono.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#ifndef __PJ_COMPAT_OS_PALMOS_H__
+#define __PJ_COMPAT_OS_PALMOS_H__
+
+/**
+ * @file os_palmos.h
+ * @brief Describes PalmOS operating system specifics.
+ */
+
+#define PJ_HAS_ARPA_INET_H 0
+#define PJ_HAS_ASSERT_H 1
+#define PJ_HAS_CTYPE_H 1
+#define PJ_HAS_ERRNO_H 0
+#define PJ_HAS_MALLOC_H 1
+#define PJ_HAS_NETDB_H 0
+#define PJ_HAS_NETINET_IN_H 0
+#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_STRING_H 1
+#define PJ_HAS_SYS_IOCTL_H 0
+#define PJ_HAS_SYS_SELECT_H 0
+#define PJ_HAS_SYS_SOCKET_H 0
+#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 0
+
+#define PJ_HAS_MSWSOCK_H 0
+#define PJ_HAS_WINSOCK_H 0
+#define PJ_HAS_WINSOCK2_H 0
+
+#define PJ_SOCK_HAS_INET_ATON 0
+
+/* When this macro is set, getsockopt(SOL_SOCKET, SO_ERROR) will return
+ * the status of non-blocking connect() operation.
+ */
+#define PJ_HAS_SO_ERROR 0
+
+/* 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 xxx
+
+/* 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 xxx
+
+/* 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_OS_HAS_CHECK_STACK 0
+
+#endif /* __PJ_COMPAT_OS_PALMOS_H__ */
diff --git a/pjlib/include/pj/compat/os_sunos.h b/pjlib/include/pj/compat/os_sunos.h
index 18269504..68827ecc 100644
--- a/pjlib/include/pj/compat/os_sunos.h
+++ b/pjlib/include/pj/compat/os_sunos.h
@@ -1,85 +1,85 @@
-/* $Id$ */
-/*
- * Copyright (C)2003-2006 Benny Prijono <benny@prijono.org>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
-#ifndef __PJ_COMPAT_OS_SUNOS_H__
-#define __PJ_COMPAT_OS_SUNOS_H__
-
-/**
- * @file os_sunos.h
- * @brief Describes SunOS/Solaris operating system specifics.
- */
-
-#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_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_TIMEB_H 1
-#define PJ_HAS_SYS_TYPES_H 1
-#define PJ_HAS_TIME_H 1
-#define PJ_HAS_UNISTD_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 0
-
-/* When this macro is set, getsockopt(SOL_SOCKET, SO_ERROR) will return
- * the status of non-blocking connect() operation.
- */
-#define PJ_HAS_SO_ERROR 0
-
-/* 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 EWOULDBLOCK
-
-/* 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
-#define PJ_OS_HAS_CHECK_STACK 0
-
-#define PJ_ATOMIC_VALUE_TYPE long
-
-/* Get BSD related identifers in Sun's include files */
-#define BSD_COMP
-
-#endif /* __PJ_COMPAT_OS_SUNOS_H__ */
-
+/* $Id$ */
+/*
+ * Copyright (C)2003-2006 Benny Prijono <benny@prijono.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#ifndef __PJ_COMPAT_OS_SUNOS_H__
+#define __PJ_COMPAT_OS_SUNOS_H__
+
+/**
+ * @file os_sunos.h
+ * @brief Describes SunOS/Solaris operating system specifics.
+ */
+
+#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_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_TIMEB_H 1
+#define PJ_HAS_SYS_TYPES_H 1
+#define PJ_HAS_TIME_H 1
+#define PJ_HAS_UNISTD_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 0
+
+/* When this macro is set, getsockopt(SOL_SOCKET, SO_ERROR) will return
+ * the status of non-blocking connect() operation.
+ */
+#define PJ_HAS_SO_ERROR 0
+
+/* 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 EWOULDBLOCK
+
+/* 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
+#define PJ_OS_HAS_CHECK_STACK 0
+
+#define PJ_ATOMIC_VALUE_TYPE long
+
+/* Get BSD related identifers in Sun's include files */
+#define BSD_COMP
+
+#endif /* __PJ_COMPAT_OS_SUNOS_H__ */
+
diff --git a/pjlib/include/pj/compat/os_win32.h b/pjlib/include/pj/compat/os_win32.h
index e0736ad7..ac6eaff0 100644
--- a/pjlib/include/pj/compat/os_win32.h
+++ b/pjlib/include/pj/compat/os_win32.h
@@ -1,85 +1,85 @@
-/* $Id$ */
-/*
- * Copyright (C)2003-2006 Benny Prijono <benny@prijono.org>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
-#ifndef __PJ_COMPAT_OS_WIN32_H__
-#define __PJ_COMPAT_OS_WIN32_H__
-
-/**
- * @file os_win32.h
- * @brief Describes Win32 operating system family specifics.
- */
-
-#define WIN32_LEAN_AND_MEAN
-#define PJ_WIN32_WINNT 0x0400
-#define _WIN32_WINNT PJ_WIN32_WINNT
-
-#define PJ_HAS_ARPA_INET_H 0
-#define PJ_HAS_ASSERT_H 1
-#define PJ_HAS_CTYPE_H 1
-#define PJ_HAS_ERRNO_H 0 /* Must be zero, otherwise errno_test() fails. */
-#define PJ_HAS_LINUX_SOCKET_H 0
-#define PJ_HAS_MALLOC_H 1
-#define PJ_HAS_NETDB_H 0
-#define PJ_HAS_NETINET_IN_H 0
-#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_STRING_H 1
-#define PJ_HAS_SYS_IOCTL_H 0
-#define PJ_HAS_SYS_SELECT_H 0
-#define PJ_HAS_SYS_SOCKET_H 0
-#define PJ_HAS_SYS_TIMEB_H 1
-#define PJ_HAS_SYS_TYPES_H 1
-#define PJ_HAS_TIME_H 1
-#define PJ_HAS_UNISTD_H 0
-
-#define PJ_HAS_MSWSOCK_H 1
-#define PJ_HAS_WINSOCK_H 0
-#define PJ_HAS_WINSOCK2_H 1
-
-#define PJ_SOCK_HAS_INET_ATON 0
-
-/* When this macro is set, getsockopt(SOL_SOCKET, SO_ERROR) will return
- * the status of non-blocking connect() operation.
- */
-#define PJ_HAS_SO_ERROR 0
-
-/* This value specifies the value set in errno by the OS when a non-blocking
- * socket recv() or send() can not return immediately.
- */
-#define PJ_BLOCKING_ERROR_VAL WSAEWOULDBLOCK
-
-/* 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 WSAEWOULDBLOCK
-
-/* 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
-#define PJ_OS_HAS_CHECK_STACK 1
-
-#define PJ_ATOMIC_VALUE_TYPE long
-
-#endif /* __PJ_COMPAT_OS_WIN32_H__ */
+/* $Id$ */
+/*
+ * Copyright (C)2003-2006 Benny Prijono <benny@prijono.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#ifndef __PJ_COMPAT_OS_WIN32_H__
+#define __PJ_COMPAT_OS_WIN32_H__
+
+/**
+ * @file os_win32.h
+ * @brief Describes Win32 operating system family specifics.
+ */
+
+#define WIN32_LEAN_AND_MEAN
+#define PJ_WIN32_WINNT 0x0400
+#define _WIN32_WINNT PJ_WIN32_WINNT
+
+#define PJ_HAS_ARPA_INET_H 0
+#define PJ_HAS_ASSERT_H 1
+#define PJ_HAS_CTYPE_H 1
+#define PJ_HAS_ERRNO_H 0 /* Must be zero, otherwise errno_test() fails. */
+#define PJ_HAS_LINUX_SOCKET_H 0
+#define PJ_HAS_MALLOC_H 1
+#define PJ_HAS_NETDB_H 0
+#define PJ_HAS_NETINET_IN_H 0
+#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_STRING_H 1
+#define PJ_HAS_SYS_IOCTL_H 0
+#define PJ_HAS_SYS_SELECT_H 0
+#define PJ_HAS_SYS_SOCKET_H 0
+#define PJ_HAS_SYS_TIMEB_H 1
+#define PJ_HAS_SYS_TYPES_H 1
+#define PJ_HAS_TIME_H 1
+#define PJ_HAS_UNISTD_H 0
+
+#define PJ_HAS_MSWSOCK_H 1
+#define PJ_HAS_WINSOCK_H 0
+#define PJ_HAS_WINSOCK2_H 1
+
+#define PJ_SOCK_HAS_INET_ATON 0
+
+/* When this macro is set, getsockopt(SOL_SOCKET, SO_ERROR) will return
+ * the status of non-blocking connect() operation.
+ */
+#define PJ_HAS_SO_ERROR 0
+
+/* This value specifies the value set in errno by the OS when a non-blocking
+ * socket recv() or send() can not return immediately.
+ */
+#define PJ_BLOCKING_ERROR_VAL WSAEWOULDBLOCK
+
+/* 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 WSAEWOULDBLOCK
+
+/* 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
+#define PJ_OS_HAS_CHECK_STACK 1
+
+#define PJ_ATOMIC_VALUE_TYPE long
+
+#endif /* __PJ_COMPAT_OS_WIN32_H__ */
diff --git a/pjlib/include/pj/compat/rand.h b/pjlib/include/pj/compat/rand.h
index dc54b2a4..d5804c9d 100644
--- a/pjlib/include/pj/compat/rand.h
+++ b/pjlib/include/pj/compat/rand.h
@@ -1,69 +1,69 @@
-/* $Id$ */
-/*
- * Copyright (C)2003-2006 Benny Prijono <benny@prijono.org>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
-#ifndef __PJ_COMPAT_RAND_H__
-#define __PJ_COMPAT_RAND_H__
-
-/**
- * @file rand.h
- * @brief Provides platform_rand() and platform_srand() functions.
- */
-
-#if defined(PJ_HAS_STDLIB_H) && PJ_HAS_STDLIB_H != 0
- /*
- * Use stdlib based rand() and srand().
- */
-# include <stdlib.h>
-# define platform_srand srand
-# if defined(RAND_MAX) && RAND_MAX <= 0xFFFF
- /*
- * When rand() is only 16 bit strong, double the strength
- * by calling it twice!
- */
- PJ_INLINE(int) platform_rand(void)
- {
- return ((rand() & 0xFFFF) << 16) | (rand() & 0xFFFF);
- }
-# else
-# define platform_rand rand
-# endif
-
-#elif defined(PJ_LINUX_KERNEL) && PJ_LINUX_KERNEL != 0
- /*
- * Linux kernel mode random number generator.
- */
-# include <linux/random.h>
-# define platform_srand(seed)
-
- PJ_INLINE(int) platform_rand(void)
- {
- int value;
- get_random_bytes((void*)&value, sizeof(value));
- return value;
- }
-
-#else
-# warning "platform_rand() is not implemented"
-# define platform_rand() 1
-# define platform_srand(seed)
-
-#endif
-
-
-#endif /* __PJ_COMPAT_RAND_H__ */
-
+/* $Id$ */
+/*
+ * Copyright (C)2003-2006 Benny Prijono <benny@prijono.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#ifndef __PJ_COMPAT_RAND_H__
+#define __PJ_COMPAT_RAND_H__
+
+/**
+ * @file rand.h
+ * @brief Provides platform_rand() and platform_srand() functions.
+ */
+
+#if defined(PJ_HAS_STDLIB_H) && PJ_HAS_STDLIB_H != 0
+ /*
+ * Use stdlib based rand() and srand().
+ */
+# include <stdlib.h>
+# define platform_srand srand
+# if defined(RAND_MAX) && RAND_MAX <= 0xFFFF
+ /*
+ * When rand() is only 16 bit strong, double the strength
+ * by calling it twice!
+ */
+ PJ_INLINE(int) platform_rand(void)
+ {
+ return ((rand() & 0xFFFF) << 16) | (rand() & 0xFFFF);
+ }
+# else
+# define platform_rand rand
+# endif
+
+#elif defined(PJ_LINUX_KERNEL) && PJ_LINUX_KERNEL != 0
+ /*
+ * Linux kernel mode random number generator.
+ */
+# include <linux/random.h>
+# define platform_srand(seed)
+
+ PJ_INLINE(int) platform_rand(void)
+ {
+ int value;
+ get_random_bytes((void*)&value, sizeof(value));
+ return value;
+ }
+
+#else
+# warning "platform_rand() is not implemented"
+# define platform_rand() 1
+# define platform_srand(seed)
+
+#endif
+
+
+#endif /* __PJ_COMPAT_RAND_H__ */
+
diff --git a/pjlib/include/pj/compat/setjmp.h b/pjlib/include/pj/compat/setjmp.h
index c0a7af31..577956d9 100644
--- a/pjlib/include/pj/compat/setjmp.h
+++ b/pjlib/include/pj/compat/setjmp.h
@@ -1,90 +1,90 @@
-/* $Id$ */
-/*
- * Copyright (C)2003-2006 Benny Prijono <benny@prijono.org>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
-#ifndef __PJ_COMPAT_SETJMP_H__
-#define __PJ_COMPAT_SETJMP_H__
-
-/**
- * @file setjmp.h
- * @brief Provides setjmp.h functionality.
- */
-
-#if defined(PJ_HAS_SETJMP_H) && PJ_HAS_SETJMP_H != 0
-# include <setjmp.h>
- typedef jmp_buf pj_jmp_buf;
-# define pj_setjmp(buf) setjmp(buf)
-# define pj_longjmp(buf,d) longjmp(buf,d)
-
-#elif defined(PJ_LINUX_KERNEL) && PJ_LINUX_KERNEL != 0 && \
- defined(PJ_M_I386) && PJ_M_I386 != 0
-
- /*
- * These are taken from uClibc.
- * Copyright (C) 2000-2003 Erik Andersen <andersen@uclibc.org>
- */
-# if defined __USE_MISC || defined _ASM
-# define JB_BX 0
-# define JB_SI 1
-# define JB_DI 2
-# define JB_BP 3
-# define JB_SP 4
-# define JB_PC 5
-# define JB_SIZE 24
-# endif
-
-# ifndef _ASM
- typedef int __jmp_buf[6];
-
- /* A `sigset_t' has a bit for each signal. */
-# define _SIGSET_NWORDS (1024 / (8 * sizeof (unsigned long int)))
- typedef struct __sigset_t_tag
- {
- unsigned long int __val[_SIGSET_NWORDS];
- } __sigset_t;
-
- /* Calling environment, plus possibly a saved signal mask. */
- typedef struct __jmp_buf_tag /* C++ doesn't like tagless structs. */
- {
- /* NOTE: The machine-dependent definitions of `__sigsetjmp'
- assume that a `jmp_buf' begins with a `__jmp_buf' and that
- `__mask_was_saved' follows it. Do not move these members
- or add others before it. */
- __jmp_buf __jmpbuf; /* Calling environment. */
- int __mask_was_saved; /* Saved the signal mask? */
- // we never saved the mask.
- __sigset_t __saved_mask; /* Saved signal mask. */
- } jmp_buf[1];
-
- typedef jmp_buf sigjmp_buf;
- typedef jmp_buf pj_jmp_buf;
-
- PJ_DECL(int) pj_setjmp(pj_jmp_buf env);
- PJ_DECL(void) pj_longjmp(pj_jmp_buf env, int val) __attribute__((noreturn));
-
-# endif /* _ASM */
-
-#else
-# warning "setjmp()/longjmp() is not implemented"
- typedef int pj_jmp_buf[1];
-# define pj_setjmp(buf) 0
-# define pj_longjmp(buf,d) 0
-#endif
-
-
-#endif /* __PJ_COMPAT_SETJMP_H__ */
-
+/* $Id$ */
+/*
+ * Copyright (C)2003-2006 Benny Prijono <benny@prijono.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#ifndef __PJ_COMPAT_SETJMP_H__
+#define __PJ_COMPAT_SETJMP_H__
+
+/**
+ * @file setjmp.h
+ * @brief Provides setjmp.h functionality.
+ */
+
+#if defined(PJ_HAS_SETJMP_H) && PJ_HAS_SETJMP_H != 0
+# include <setjmp.h>
+ typedef jmp_buf pj_jmp_buf;
+# define pj_setjmp(buf) setjmp(buf)
+# define pj_longjmp(buf,d) longjmp(buf,d)
+
+#elif defined(PJ_LINUX_KERNEL) && PJ_LINUX_KERNEL != 0 && \
+ defined(PJ_M_I386) && PJ_M_I386 != 0
+
+ /*
+ * These are taken from uClibc.
+ * Copyright (C) 2000-2003 Erik Andersen <andersen@uclibc.org>
+ */
+# if defined __USE_MISC || defined _ASM
+# define JB_BX 0
+# define JB_SI 1
+# define JB_DI 2
+# define JB_BP 3
+# define JB_SP 4
+# define JB_PC 5
+# define JB_SIZE 24
+# endif
+
+# ifndef _ASM
+ typedef int __jmp_buf[6];
+
+ /* A `sigset_t' has a bit for each signal. */
+# define _SIGSET_NWORDS (1024 / (8 * sizeof (unsigned long int)))
+ typedef struct __sigset_t_tag
+ {
+ unsigned long int __val[_SIGSET_NWORDS];
+ } __sigset_t;
+
+ /* Calling environment, plus possibly a saved signal mask. */
+ typedef struct __jmp_buf_tag /* C++ doesn't like tagless structs. */
+ {
+ /* NOTE: The machine-dependent definitions of `__sigsetjmp'
+ assume that a `jmp_buf' begins with a `__jmp_buf' and that
+ `__mask_was_saved' follows it. Do not move these members
+ or add others before it. */
+ __jmp_buf __jmpbuf; /* Calling environment. */
+ int __mask_was_saved; /* Saved the signal mask? */
+ // we never saved the mask.
+ __sigset_t __saved_mask; /* Saved signal mask. */
+ } jmp_buf[1];
+
+ typedef jmp_buf sigjmp_buf;
+ typedef jmp_buf pj_jmp_buf;
+
+ PJ_DECL(int) pj_setjmp(pj_jmp_buf env);
+ PJ_DECL(void) pj_longjmp(pj_jmp_buf env, int val) __attribute__((noreturn));
+
+# endif /* _ASM */
+
+#else
+# warning "setjmp()/longjmp() is not implemented"
+ typedef int pj_jmp_buf[1];
+# define pj_setjmp(buf) 0
+# define pj_longjmp(buf,d) 0
+#endif
+
+
+#endif /* __PJ_COMPAT_SETJMP_H__ */
+
diff --git a/pjlib/include/pj/compat/size_t.h b/pjlib/include/pj/compat/size_t.h
index d164d994..fb5126cd 100644
--- a/pjlib/include/pj/compat/size_t.h
+++ b/pjlib/include/pj/compat/size_t.h
@@ -1,31 +1,31 @@
-/* $Id$ */
-/*
- * Copyright (C)2003-2006 Benny Prijono <benny@prijono.org>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
-#ifndef __PJ_COMPAT_SIZE_T_H__
-#define __PJ_COMPAT_SIZE_T_H__
-
-/**
- * @file size_t.h
- * @brief Provides size_t type.
- */
-#if PJ_HAS_STDDEF_H
-# include <stddef.h>
-#endif
-
-#endif /* __PJ_COMPAT_SIZE_T_H__ */
-
+/* $Id$ */
+/*
+ * Copyright (C)2003-2006 Benny Prijono <benny@prijono.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#ifndef __PJ_COMPAT_SIZE_T_H__
+#define __PJ_COMPAT_SIZE_T_H__
+
+/**
+ * @file size_t.h
+ * @brief Provides size_t type.
+ */
+#if PJ_HAS_STDDEF_H
+# include <stddef.h>
+#endif
+
+#endif /* __PJ_COMPAT_SIZE_T_H__ */
+
diff --git a/pjlib/include/pj/compat/socket.h b/pjlib/include/pj/compat/socket.h
index 12a13752..c45cd3f2 100644
--- a/pjlib/include/pj/compat/socket.h
+++ b/pjlib/include/pj/compat/socket.h
@@ -1,130 +1,130 @@
-/* $Id$ */
-/*
- * Copyright (C)2003-2006 Benny Prijono <benny@prijono.org>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
-#ifndef __PJ_COMPAT_SOCKET_H__
-#define __PJ_COMPAT_SOCKET_H__
-
-/**
- * @file socket.h
- * @brief Provides all socket related functions,data types, error codes, etc.
- */
-
-#if defined(PJ_HAS_WINSOCK_H) && PJ_HAS_WINSOCK_H != 0
-# include <winsock.h>
-#endif
-
-#if defined(PJ_HAS_WINSOCK2_H) && PJ_HAS_WINSOCK2_H != 0
-# include <winsock2.h>
-#endif
-
-#if defined(PJ_HAS_SYS_TYPES_H) && PJ_HAS_SYS_TYPES_H != 0
-# include <sys/types.h>
-#endif
-
-#if defined(PJ_HAS_SYS_SOCKET_H) && PJ_HAS_SYS_SOCKET_H != 0
-# include <sys/socket.h>
-#endif
-
-#if defined(PJ_HAS_LINUX_SOCKET_H) && PJ_HAS_LINUX_SOCKET_H != 0
-# include <linux/socket.h>
-#endif
-
-#if defined(PJ_HAS_SYS_SELECT_H) && PJ_HAS_SYS_SELECT_H != 0
-# include <sys/select.h>
-#endif
-
-#if defined(PJ_HAS_NETINET_IN_H) && PJ_HAS_NETINET_IN_H != 0
-# include <netinet/in.h>
-#endif
-
-#if defined(PJ_HAS_ARPA_INET_H) && PJ_HAS_ARPA_INET_H != 0
-# include <arpa/inet.h>
-#endif
-
-#if defined(PJ_HAS_SYS_IOCTL_H) && PJ_HAS_SYS_IOCTL_H != 0
-# include <sys/ioctl.h> /* FBIONBIO */
-#endif
-
-#if defined(PJ_HAS_ERRNO_H) && PJ_HAS_ERRNO_H != 0
-# include <errno.h>
-#endif
-
-#if defined(PJ_HAS_NETDB_H) && PJ_HAS_NETDB_H != 0
-# include <netdb.h>
-#endif
-
-#if defined(PJ_HAS_UNISTD_H) && PJ_HAS_UNISTD_H != 0
-# include <unistd.h>
-#endif
-
-
-/*
- * Define common errors.
- */
-#ifdef PJ_WIN32
-# define OSERR_EWOULDBLOCK WSAEWOULDBLOCK
-# define OSERR_EINPROGRESS WSAEINPROGRESS
-#else
-# define OSERR_EWOULDBLOCK EWOULDBLOCK
-# define OSERR_EINPROGRESS EINPROGRESS
-#endif
-
-
-/*
- * And undefine this..
- */
-#undef s_addr
-
-/*
- * Linux kernel specifics
- */
-#ifdef PJ_LINUX_KERNEL
-# include <linux/net.h>
-# include <asm/ioctls.h> /* FIONBIO */
-# include <linux/syscalls.h> /* sys_select() */
-# include <asm/uaccess.h> /* set/get_fs() */
-
- typedef int socklen_t;
-# define getsockopt sys_getsockopt
-
- /*
- * Wrapper for select() in Linux kernel.
- */
- PJ_INLINE(int) select(int n, fd_set *inp, fd_set *outp, fd_set *exp,
- struct timeval *tvp)
- {
- int count;
- mm_segment_t oldfs = get_fs();
- set_fs(KERNEL_DS);
- count = sys_select(n, inp, outp, exp, tvp);
- set_fs(oldfs);
- return count;
- }
-#endif /* PJ_LINUX_KERNEL */
-
-
-/*
- * Windows specific
- */
-#ifdef PJ_WIN32
- typedef int socklen_t;;
-#endif
-
-
-#endif /* __PJ_COMPAT_SOCKET_H__ */
-
+/* $Id$ */
+/*
+ * Copyright (C)2003-2006 Benny Prijono <benny@prijono.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#ifndef __PJ_COMPAT_SOCKET_H__
+#define __PJ_COMPAT_SOCKET_H__
+
+/**
+ * @file socket.h
+ * @brief Provides all socket related functions,data types, error codes, etc.
+ */
+
+#if defined(PJ_HAS_WINSOCK_H) && PJ_HAS_WINSOCK_H != 0
+# include <winsock.h>
+#endif
+
+#if defined(PJ_HAS_WINSOCK2_H) && PJ_HAS_WINSOCK2_H != 0
+# include <winsock2.h>
+#endif
+
+#if defined(PJ_HAS_SYS_TYPES_H) && PJ_HAS_SYS_TYPES_H != 0
+# include <sys/types.h>
+#endif
+
+#if defined(PJ_HAS_SYS_SOCKET_H) && PJ_HAS_SYS_SOCKET_H != 0
+# include <sys/socket.h>
+#endif
+
+#if defined(PJ_HAS_LINUX_SOCKET_H) && PJ_HAS_LINUX_SOCKET_H != 0
+# include <linux/socket.h>
+#endif
+
+#if defined(PJ_HAS_SYS_SELECT_H) && PJ_HAS_SYS_SELECT_H != 0
+# include <sys/select.h>
+#endif
+
+#if defined(PJ_HAS_NETINET_IN_H) && PJ_HAS_NETINET_IN_H != 0
+# include <netinet/in.h>
+#endif
+
+#if defined(PJ_HAS_ARPA_INET_H) && PJ_HAS_ARPA_INET_H != 0
+# include <arpa/inet.h>
+#endif
+
+#if defined(PJ_HAS_SYS_IOCTL_H) && PJ_HAS_SYS_IOCTL_H != 0
+# include <sys/ioctl.h> /* FBIONBIO */
+#endif
+
+#if defined(PJ_HAS_ERRNO_H) && PJ_HAS_ERRNO_H != 0
+# include <errno.h>
+#endif
+
+#if defined(PJ_HAS_NETDB_H) && PJ_HAS_NETDB_H != 0
+# include <netdb.h>
+#endif
+
+#if defined(PJ_HAS_UNISTD_H) && PJ_HAS_UNISTD_H != 0
+# include <unistd.h>
+#endif
+
+
+/*
+ * Define common errors.
+ */
+#ifdef PJ_WIN32
+# define OSERR_EWOULDBLOCK WSAEWOULDBLOCK
+# define OSERR_EINPROGRESS WSAEINPROGRESS
+#else
+# define OSERR_EWOULDBLOCK EWOULDBLOCK
+# define OSERR_EINPROGRESS EINPROGRESS
+#endif
+
+
+/*
+ * And undefine this..
+ */
+#undef s_addr
+
+/*
+ * Linux kernel specifics
+ */
+#ifdef PJ_LINUX_KERNEL
+# include <linux/net.h>
+# include <asm/ioctls.h> /* FIONBIO */
+# include <linux/syscalls.h> /* sys_select() */
+# include <asm/uaccess.h> /* set/get_fs() */
+
+ typedef int socklen_t;
+# define getsockopt sys_getsockopt
+
+ /*
+ * Wrapper for select() in Linux kernel.
+ */
+ PJ_INLINE(int) select(int n, fd_set *inp, fd_set *outp, fd_set *exp,
+ struct timeval *tvp)
+ {
+ int count;
+ mm_segment_t oldfs = get_fs();
+ set_fs(KERNEL_DS);
+ count = sys_select(n, inp, outp, exp, tvp);
+ set_fs(oldfs);
+ return count;
+ }
+#endif /* PJ_LINUX_KERNEL */
+
+
+/*
+ * Windows specific
+ */
+#ifdef PJ_WIN32
+ typedef int socklen_t;;
+#endif
+
+
+#endif /* __PJ_COMPAT_SOCKET_H__ */
+
diff --git a/pjlib/include/pj/compat/sprintf.h b/pjlib/include/pj/compat/sprintf.h
index ffb4d42e..a780a5fc 100644
--- a/pjlib/include/pj/compat/sprintf.h
+++ b/pjlib/include/pj/compat/sprintf.h
@@ -1,38 +1,38 @@
-/* $Id$ */
-/*
- * Copyright (C)2003-2006 Benny Prijono <benny@prijono.org>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
-#ifndef __PJ_COMPAT_SPRINTF_H__
-#define __PJ_COMPAT_SPRINTF_H__
-
-/**
- * @file sprintf.h
- * @brief Provides sprintf() and snprintf() functions.
- */
-
-#if defined(PJ_HAS_STDIO_H) && PJ_HAS_STDIO_H != 0
-# include <stdio.h>
-#endif
-
-#if defined(_MSC_VER)
-# define snprintf _snprintf
-#endif
-
-#define pj_sprintf sprintf
-#define pj_snprintf snprintf
-
-#endif /* __PJ_COMPAT_SPRINTF_H__ */
+/* $Id$ */
+/*
+ * Copyright (C)2003-2006 Benny Prijono <benny@prijono.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#ifndef __PJ_COMPAT_SPRINTF_H__
+#define __PJ_COMPAT_SPRINTF_H__
+
+/**
+ * @file sprintf.h
+ * @brief Provides sprintf() and snprintf() functions.
+ */
+
+#if defined(PJ_HAS_STDIO_H) && PJ_HAS_STDIO_H != 0
+# include <stdio.h>
+#endif
+
+#if defined(_MSC_VER)
+# define snprintf _snprintf
+#endif
+
+#define pj_sprintf sprintf
+#define pj_snprintf snprintf
+
+#endif /* __PJ_COMPAT_SPRINTF_H__ */
diff --git a/pjlib/include/pj/compat/stdarg.h b/pjlib/include/pj/compat/stdarg.h
index a85e707f..72c83cdb 100644
--- a/pjlib/include/pj/compat/stdarg.h
+++ b/pjlib/include/pj/compat/stdarg.h
@@ -1,31 +1,31 @@
-/* $Id$ */
-/*
- * Copyright (C)2003-2006 Benny Prijono <benny@prijono.org>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
-#ifndef __PJ_COMPAT_STDARG_H__
-#define __PJ_COMPAT_STDARG_H__
-
-/**
- * @file stdarg.h
- * @brief Provides stdarg functionality.
- */
-
-#if defined(PJ_HAS_STDARG_H) && PJ_HAS_STDARG_H != 0
-# include <stdarg.h>
-#endif
-
-#endif /* __PJ_COMPAT_STDARG_H__ */
+/* $Id$ */
+/*
+ * Copyright (C)2003-2006 Benny Prijono <benny@prijono.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#ifndef __PJ_COMPAT_STDARG_H__
+#define __PJ_COMPAT_STDARG_H__
+
+/**
+ * @file stdarg.h
+ * @brief Provides stdarg functionality.
+ */
+
+#if defined(PJ_HAS_STDARG_H) && PJ_HAS_STDARG_H != 0
+# include <stdarg.h>
+#endif
+
+#endif /* __PJ_COMPAT_STDARG_H__ */
diff --git a/pjlib/include/pj/compat/stdfileio.h b/pjlib/include/pj/compat/stdfileio.h
index a8895db0..7521da8a 100644
--- a/pjlib/include/pj/compat/stdfileio.h
+++ b/pjlib/include/pj/compat/stdfileio.h
@@ -1,31 +1,31 @@
-/* $Id$ */
-/*
- * Copyright (C)2003-2006 Benny Prijono <benny@prijono.org>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
-#ifndef __PJ_COMPAT_STDFILEIO_H__
-#define __PJ_COMPAT_STDFILEIO_H__
-
-/**
- * @file stdfileio.h
- * @brief Compatibility for ANSI file I/O like fputs, fflush, etc.
- */
-
-#if defined(PJ_HAS_STDIO_H) && PJ_HAS_STDIO_H != 0
-# include <stdio.h>
-#endif
-
-#endif /* __PJ_COMPAT_STDFILEIO_H__ */
+/* $Id$ */
+/*
+ * Copyright (C)2003-2006 Benny Prijono <benny@prijono.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#ifndef __PJ_COMPAT_STDFILEIO_H__
+#define __PJ_COMPAT_STDFILEIO_H__
+
+/**
+ * @file stdfileio.h
+ * @brief Compatibility for ANSI file I/O like fputs, fflush, etc.
+ */
+
+#if defined(PJ_HAS_STDIO_H) && PJ_HAS_STDIO_H != 0
+# include <stdio.h>
+#endif
+
+#endif /* __PJ_COMPAT_STDFILEIO_H__ */
diff --git a/pjlib/include/pj/compat/string.h b/pjlib/include/pj/compat/string.h
index df739819..56ae5c2f 100644
--- a/pjlib/include/pj/compat/string.h
+++ b/pjlib/include/pj/compat/string.h
@@ -1,57 +1,57 @@
-/* $Id$ */
-/*
- * Copyright (C)2003-2006 Benny Prijono <benny@prijono.org>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
-#ifndef __PJ_COMPAT_STRING_H__
-#define __PJ_COMPAT_STRING_H__
-
-/**
- * @file string.h
- * @brief Provides string manipulation functions found in ANSI string.h.
- */
-
-#if defined(PJ_HAS_STRING_H) && PJ_HAS_STRING_H != 0
-# include <string.h>
-#else
-
- PJ_DECL(int) strcasecmp(const char *s1, const char *s2);
- PJ_DECL(int) strncasecmp(const char *s1, const char *s2, int len);
-
-#endif
-
-#if defined(_MSC_VER)
-# define strcasecmp stricmp
-# define strncasecmp strnicmp
-# define snprintf _snprintf
-#else
-# define stricmp strcasecmp
-# define strnicmp strncasecmp
-#endif
-
-
-#define pj_native_strcmp strcmp
-#define pj_native_strncmp strncmp
-#define pj_native_strlen strlen
-#define pj_native_strcpy strcpy
-#define pj_native_strstr strstr
-#define pj_native_strchr strchr
-#define pj_native_strcasecmp strcasecmp
-#define pj_native_stricmp strcasecmp
-#define pj_native_strncasecmp strncasecmp
-#define pj_native_strnicmp strncasecmp
-
-#endif /* __PJ_COMPAT_STRING_H__ */
+/* $Id$ */
+/*
+ * Copyright (C)2003-2006 Benny Prijono <benny@prijono.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#ifndef __PJ_COMPAT_STRING_H__
+#define __PJ_COMPAT_STRING_H__
+
+/**
+ * @file string.h
+ * @brief Provides string manipulation functions found in ANSI string.h.
+ */
+
+#if defined(PJ_HAS_STRING_H) && PJ_HAS_STRING_H != 0
+# include <string.h>
+#else
+
+ PJ_DECL(int) strcasecmp(const char *s1, const char *s2);
+ PJ_DECL(int) strncasecmp(const char *s1, const char *s2, int len);
+
+#endif
+
+#if defined(_MSC_VER)
+# define strcasecmp stricmp
+# define strncasecmp strnicmp
+# define snprintf _snprintf
+#else
+# define stricmp strcasecmp
+# define strnicmp strncasecmp
+#endif
+
+
+#define pj_native_strcmp strcmp
+#define pj_native_strncmp strncmp
+#define pj_native_strlen strlen
+#define pj_native_strcpy strcpy
+#define pj_native_strstr strstr
+#define pj_native_strchr strchr
+#define pj_native_strcasecmp strcasecmp
+#define pj_native_stricmp strcasecmp
+#define pj_native_strncasecmp strncasecmp
+#define pj_native_strnicmp strncasecmp
+
+#endif /* __PJ_COMPAT_STRING_H__ */
diff --git a/pjlib/include/pj/compat/time.h b/pjlib/include/pj/compat/time.h
index 9932f902..2a2bc397 100644
--- a/pjlib/include/pj/compat/time.h
+++ b/pjlib/include/pj/compat/time.h
@@ -1,36 +1,36 @@
-/* $Id$ */
-/*
- * Copyright (C)2003-2006 Benny Prijono <benny@prijono.org>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
-#ifndef __PJ_COMPAT_TIME_H__
-#define __PJ_COMPAT_TIME_H__
-
-/**
- * @file time.h
- * @brief Provides ftime() and localtime() etc functions.
- */
-
-#if defined(PJ_HAS_TIME_H) && PJ_HAS_TIME_H != 0
-# include <time.h>
-#endif
-
-#if defined(PJ_HAS_SYS_TIMEB_H) && PJ_HAS_SYS_TIMEB_H != 0
-# include <sys/timeb.h>
-#endif
-
-
-#endif /* __PJ_COMPAT_TIME_H__ */
+/* $Id$ */
+/*
+ * Copyright (C)2003-2006 Benny Prijono <benny@prijono.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#ifndef __PJ_COMPAT_TIME_H__
+#define __PJ_COMPAT_TIME_H__
+
+/**
+ * @file time.h
+ * @brief Provides ftime() and localtime() etc functions.
+ */
+
+#if defined(PJ_HAS_TIME_H) && PJ_HAS_TIME_H != 0
+# include <time.h>
+#endif
+
+#if defined(PJ_HAS_SYS_TIMEB_H) && PJ_HAS_SYS_TIMEB_H != 0
+# include <sys/timeb.h>
+#endif
+
+
+#endif /* __PJ_COMPAT_TIME_H__ */
diff --git a/pjlib/include/pj/compat/vsprintf.h b/pjlib/include/pj/compat/vsprintf.h
index 85a9814e..788465fd 100644
--- a/pjlib/include/pj/compat/vsprintf.h
+++ b/pjlib/include/pj/compat/vsprintf.h
@@ -1,37 +1,37 @@
-/* $Id$ */
-/*
- * Copyright (C)2003-2006 Benny Prijono <benny@prijono.org>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
-#ifndef __PJ_COMPAT_VSPRINTF_H__
-#define __PJ_COMPAT_VSPRINTF_H__
-
-/**
- * @file vsprintf.h
- * @brief Provides vsprintf and vsnprintf function.
- */
-
-#if defined(PJ_HAS_STDIO_H) && PJ_HAS_STDIO_H != 0
-# include <stdio.h>
-#endif
-
-#if defined(_MSC_VER)
-# define vsnprintf _vsnprintf
-#endif
-
-#define pj_vsnprintf vsnprintf
-
-#endif /* __PJ_COMPAT_VSPRINTF_H__ */
+/* $Id$ */
+/*
+ * Copyright (C)2003-2006 Benny Prijono <benny@prijono.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#ifndef __PJ_COMPAT_VSPRINTF_H__
+#define __PJ_COMPAT_VSPRINTF_H__
+
+/**
+ * @file vsprintf.h
+ * @brief Provides vsprintf and vsnprintf function.
+ */
+
+#if defined(PJ_HAS_STDIO_H) && PJ_HAS_STDIO_H != 0
+# include <stdio.h>
+#endif
+
+#if defined(_MSC_VER)
+# define vsnprintf _vsnprintf
+#endif
+
+#define pj_vsnprintf vsnprintf
+
+#endif /* __PJ_COMPAT_VSPRINTF_H__ */
diff --git a/pjlib/include/pj/os.h b/pjlib/include/pj/os.h
index 0c87cad8..edd5d1d4 100644
--- a/pjlib/include/pj/os.h
+++ b/pjlib/include/pj/os.h
@@ -866,6 +866,44 @@ PJ_DECL(pj_status_t) pj_get_timestamp(pj_timestamp *ts);
PJ_DECL(pj_status_t) pj_get_timestamp_freq(pj_timestamp *freq);
/**
+ * Add timestamp t2 to t1.
+ * @param t1 t1.
+ * @param t2 t2.
+ */
+PJ_INLINE(void) pj_add_timestamp(pj_timestamp *t1, const pj_timestamp *t2)
+{
+#if PJ_HAS_INT64
+ t1->u64 += t2->u64;
+#else
+ pj_uint32_t old = t1->u32.lo;
+ t1->u32.hi += t2->u32.hi;
+ t1->u32.lo += t2->u32.lo;
+ if (t1->u32.lo < old)
+ ++t1->u32.hi;
+#endif
+}
+
+/**
+ * Substract timestamp t2 from t1.
+ * @param t1 t1.
+ * @param t2 t2.
+ */
+PJ_INLINE(void) pj_sub_timestamp(pj_timestamp *t1, const pj_timestamp *t2)
+{
+#if PJ_HAS_INT64
+ t1->u64 -= t2->u64;
+#else
+ t1->u32.hi -= t2->u32.hi;
+ if (t1->u32.lo >= t2->u32.lo)
+ t1->u32.lo -= t2->u32.lo;
+ else {
+ t1->u32.lo -= t2->u32.lo;
+ --t1->u32.hi;
+ }
+#endif
+}
+
+/**
* Calculate the elapsed time, and store it in pj_time_val.
* This function calculates the elapsed time using highest precision
* calculation that is available for current platform, considering
diff --git a/pjlib/include/pj/string.h b/pjlib/include/pj/string.h
index 44688cbe..8edcba8d 100644
--- a/pjlib/include/pj/string.h
+++ b/pjlib/include/pj/string.h
@@ -398,7 +398,7 @@ PJ_IDECL(void) pj_strcat(pj_str_t *dst, const pj_str_t *src);
*
* @return the pointer to first character found, or NULL.
*/
-PJ_INLINE(char*) pj_strchr( pj_str_t *str, int chr)
+PJ_INLINE(char*) pj_strchr( const pj_str_t *str, int chr)
{
return (char*) memchr(str->ptr, chr, str->slen);
}
diff --git a/pjlib/src/pj/addr_resolv_linux_kernel.c b/pjlib/src/pj/addr_resolv_linux_kernel.c
index fe70acbb..d6a4dcc3 100644
--- a/pjlib/src/pj/addr_resolv_linux_kernel.c
+++ b/pjlib/src/pj/addr_resolv_linux_kernel.c
@@ -1,25 +1,25 @@
-/* $Id$ */
-/*
- * Copyright (C)2003-2006 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 <pj/addr_resolv.h>
-
-PJ_DEF(pj_status_t) pj_gethostbyname(const pj_str_t *hostname, pj_hostent *phe)
-{
- return -1;
-}
-
+/* $Id$ */
+/*
+ * Copyright (C)2003-2006 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 <pj/addr_resolv.h>
+
+PJ_DEF(pj_status_t) pj_gethostbyname(const pj_str_t *hostname, pj_hostent *phe)
+{
+ return -1;
+}
+
diff --git a/pjlib/src/pj/addr_resolv_sock.c b/pjlib/src/pj/addr_resolv_sock.c
index e4493b23..9b9eb6dc 100644
--- a/pjlib/src/pj/addr_resolv_sock.c
+++ b/pjlib/src/pj/addr_resolv_sock.c
@@ -1,51 +1,51 @@
-/* $Id$ */
-/*
- * Copyright (C)2003-2006 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 <pj/addr_resolv.h>
-#include <pj/assert.h>
-#include <pj/string.h>
-#include <pj/compat/socket.h>
-#include <pj/errno.h>
-
-
-PJ_DEF(pj_status_t) pj_gethostbyname(const pj_str_t *hostname, pj_hostent *phe)
-{
- struct hostent *he;
- char copy[PJ_MAX_HOSTNAME];
-
- pj_assert(hostname && hostname ->slen < PJ_MAX_HOSTNAME);
-
- if (hostname->slen >= PJ_MAX_HOSTNAME)
- return PJ_ENAMETOOLONG;
-
- pj_memcpy(copy, hostname->ptr, hostname->slen);
- copy[ hostname->slen ] = '\0';
-
- he = gethostbyname(copy);
- if (!he)
- return PJ_RETURN_OS_ERROR(pj_get_native_netos_error());
-
- phe->h_name = he->h_name;
- phe->h_aliases = he->h_aliases;
- phe->h_addrtype = he->h_addrtype;
- phe->h_length = he->h_length;
- phe->h_addr_list = he->h_addr_list;
-
- return PJ_SUCCESS;
-}
-
+/* $Id$ */
+/*
+ * Copyright (C)2003-2006 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 <pj/addr_resolv.h>
+#include <pj/assert.h>
+#include <pj/string.h>
+#include <pj/compat/socket.h>
+#include <pj/errno.h>
+
+
+PJ_DEF(pj_status_t) pj_gethostbyname(const pj_str_t *hostname, pj_hostent *phe)
+{
+ struct hostent *he;
+ char copy[PJ_MAX_HOSTNAME];
+
+ pj_assert(hostname && hostname ->slen < PJ_MAX_HOSTNAME);
+
+ if (hostname->slen >= PJ_MAX_HOSTNAME)
+ return PJ_ENAMETOOLONG;
+
+ pj_memcpy(copy, hostname->ptr, hostname->slen);
+ copy[ hostname->slen ] = '\0';
+
+ he = gethostbyname(copy);
+ if (!he)
+ return PJ_RETURN_OS_ERROR(pj_get_native_netos_error());
+
+ phe->h_name = he->h_name;
+ phe->h_aliases = he->h_aliases;
+ phe->h_addrtype = he->h_addrtype;
+ phe->h_length = he->h_length;
+ phe->h_addr_list = he->h_addr_list;
+
+ return PJ_SUCCESS;
+}
+
diff --git a/pjlib/src/pj/array.c b/pjlib/src/pj/array.c
index 59854a1c..2afe8fe6 100644
--- a/pjlib/src/pj/array.c
+++ b/pjlib/src/pj/array.c
@@ -1,70 +1,70 @@
-/* $Id$ */
-/*
- * Copyright (C)2003-2006 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 <pj/array.h>
-#include <pj/string.h>
-#include <pj/assert.h>
-#include <pj/errno.h>
-
-PJ_DEF(void) pj_array_insert( void *array,
- unsigned elem_size,
- unsigned count,
- unsigned pos,
- const void *value)
-{
- if (count && pos < count-1) {
- pj_memmove( (char*)array + (pos+1)*elem_size,
- (char*)array + pos*elem_size,
- (count-pos)*elem_size);
- }
- pj_memmove((char*)array + pos*elem_size, value, elem_size);
-}
-
-PJ_DEF(void) pj_array_erase( void *array,
- unsigned elem_size,
- unsigned count,
- unsigned pos)
-{
- pj_assert(count != 0);
- if (pos < count-1) {
- pj_memmove( (char*)array + pos*elem_size,
- (char*)array + (pos+1)*elem_size,
- (count-pos-1)*elem_size);
- }
-}
-
-PJ_DEF(pj_status_t) pj_array_find( const void *array,
- unsigned elem_size,
- unsigned count,
- pj_status_t (*matching)(const void *value),
- void **result)
-{
- unsigned i;
- const char *char_array = array;
- for (i=0; i<count; ++i) {
- if ( (*matching)(char_array) == PJ_SUCCESS) {
- if (result) {
- *result = (void*)char_array;
- }
- return PJ_SUCCESS;
- }
- char_array += elem_size;
- }
- return PJ_ENOTFOUND;
-}
-
+/* $Id$ */
+/*
+ * Copyright (C)2003-2006 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 <pj/array.h>
+#include <pj/string.h>
+#include <pj/assert.h>
+#include <pj/errno.h>
+
+PJ_DEF(void) pj_array_insert( void *array,
+ unsigned elem_size,
+ unsigned count,
+ unsigned pos,
+ const void *value)
+{
+ if (count && pos < count-1) {
+ pj_memmove( (char*)array + (pos+1)*elem_size,
+ (char*)array + pos*elem_size,
+ (count-pos)*elem_size);
+ }
+ pj_memmove((char*)array + pos*elem_size, value, elem_size);
+}
+
+PJ_DEF(void) pj_array_erase( void *array,
+ unsigned elem_size,
+ unsigned count,
+ unsigned pos)
+{
+ pj_assert(count != 0);
+ if (pos < count-1) {
+ pj_memmove( (char*)array + pos*elem_size,
+ (char*)array + (pos+1)*elem_size,
+ (count-pos-1)*elem_size);
+ }
+}
+
+PJ_DEF(pj_status_t) pj_array_find( const void *array,
+ unsigned elem_size,
+ unsigned count,
+ pj_status_t (*matching)(const void *value),
+ void **result)
+{
+ unsigned i;
+ const char *char_array = array;
+ for (i=0; i<count; ++i) {
+ if ( (*matching)(char_array) == PJ_SUCCESS) {
+ if (result) {
+ *result = (void*)char_array;
+ }
+ return PJ_SUCCESS;
+ }
+ char_array += elem_size;
+ }
+ return PJ_ENOTFOUND;
+}
+
diff --git a/pjlib/src/pj/compat/sigjmp.c b/pjlib/src/pj/compat/sigjmp.c
index 90161763..6b071547 100644
--- a/pjlib/src/pj/compat/sigjmp.c
+++ b/pjlib/src/pj/compat/sigjmp.c
@@ -1,39 +1,39 @@
-/* $Id$ */
-/*
- * Copyright (C)2003-2006 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 <pj/config.h>
-#include <pj/compat/setjmp.h>
-
-int __sigjmp_save(sigjmp_buf env, int savemask)
-{
- return 0;
-}
-
-extern int __sigsetjmp(pj_jmp_buf env, int savemask);
-extern void __longjmp(pj_jmp_buf env, int val) __attribute__((noreturn));
-
-PJ_DEF(int) pj_setjmp(pj_jmp_buf env)
-{
- return __sigsetjmp(env, 0);
-}
-
-PJ_DEF(void) pj_longjmp(pj_jmp_buf env, int val)
-{
- __longjmp(env, val);
-}
-
+/* $Id$ */
+/*
+ * Copyright (C)2003-2006 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 <pj/config.h>
+#include <pj/compat/setjmp.h>
+
+int __sigjmp_save(sigjmp_buf env, int savemask)
+{
+ return 0;
+}
+
+extern int __sigsetjmp(pj_jmp_buf env, int savemask);
+extern void __longjmp(pj_jmp_buf env, int val) __attribute__((noreturn));
+
+PJ_DEF(int) pj_setjmp(pj_jmp_buf env)
+{
+ return __sigsetjmp(env, 0);
+}
+
+PJ_DEF(void) pj_longjmp(pj_jmp_buf env, int val)
+{
+ __longjmp(env, val);
+}
+
diff --git a/pjlib/src/pj/compat/string.c b/pjlib/src/pj/compat/string.c
index 77fb1a57..bb2362ca 100644
--- a/pjlib/src/pj/compat/string.c
+++ b/pjlib/src/pj/compat/string.c
@@ -1,44 +1,44 @@
-/* $Id$ */
-/*
- * Copyright (C)2003-2006 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 <pj/types.h>
-#include <pj/compat/string.h>
-#include <pj/ctype.h>
-
-PJ_DEF(int) strcasecmp(const char *s1, const char *s2)
-{
- while ((*s1==*s2) || (pj_tolower(*s1)==pj_tolower(*s2))) {
- if (!*s1++)
- return 0;
- ++s2;
- }
- return (pj_tolower(*s1) < pj_tolower(*s2)) ? -1 : 1;
-}
-
-PJ_DEF(int) strncasecmp(const char *s1, const char *s2, int len)
-{
- if (!len) return 0;
-
- while ((*s1==*s2) || (pj_tolower(*s1)==pj_tolower(*s2))) {
- if (!*s1++ || --len <= 0)
- return 0;
- ++s2;
- }
- return (pj_tolower(*s1) < pj_tolower(*s2)) ? -1 : 1;
-}
-
+/* $Id$ */
+/*
+ * Copyright (C)2003-2006 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 <pj/types.h>
+#include <pj/compat/string.h>
+#include <pj/ctype.h>
+
+PJ_DEF(int) strcasecmp(const char *s1, const char *s2)
+{
+ while ((*s1==*s2) || (pj_tolower(*s1)==pj_tolower(*s2))) {
+ if (!*s1++)
+ return 0;
+ ++s2;
+ }
+ return (pj_tolower(*s1) < pj_tolower(*s2)) ? -1 : 1;
+}
+
+PJ_DEF(int) strncasecmp(const char *s1, const char *s2, int len)
+{
+ if (!len) return 0;
+
+ while ((*s1==*s2) || (pj_tolower(*s1)==pj_tolower(*s2))) {
+ if (!*s1++ || --len <= 0)
+ return 0;
+ ++s2;
+ }
+ return (pj_tolower(*s1) < pj_tolower(*s2)) ? -1 : 1;
+}
+
diff --git a/pjlib/src/pjlib++-test/main.cpp b/pjlib/src/pjlib++-test/main.cpp
index 86858a1d..dc3b17ed 100644
--- a/pjlib/src/pjlib++-test/main.cpp
+++ b/pjlib/src/pjlib++-test/main.cpp
@@ -1,61 +1,61 @@
-/* $Id */
-/*
- * Copyright (C)2003-2006 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 <pj++/file.hpp>
-#include <pj++/list.hpp>
-#include <pj++/lock.hpp>
-#include <pj++/hash.hpp>
-#include <pj++/os.hpp>
-#include <pj++/proactor.hpp>
-#include <pj++/sock.hpp>
-#include <pj++/string.hpp>
-#include <pj++/timer.hpp>
-#include <pj++/tree.hpp>
-
-class My_Async_Op : public Pj_Async_Op
-{
-};
-
-class My_Event_Handler : public Pj_Event_Handler
-{
-};
-
-int main()
-{
- Pjlib lib;
- Pj_Caching_Pool mem;
- Pj_Pool the_pool;
- Pj_Pool *pool = &the_pool;
-
- the_pool.attach(mem.create_pool(4000,4000));
-
- Pj_Semaphore_Lock lsem(pool);
- Pj_Semaphore_Lock *plsem;
-
- plsem = new(pool) Pj_Semaphore_Lock(pool);
- delete plsem;
-
- Pj_Proactor proactor(pool, 100, 100);
-
- My_Event_Handler *event_handler = new(the_pool) My_Event_Handler;
- proactor.register_socket_handler(pool, event_handler);
- proactor.unregister_handler(event_handler);
-
- return 0;
-}
-
+/* $Id */
+/*
+ * Copyright (C)2003-2006 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 <pj++/file.hpp>
+#include <pj++/list.hpp>
+#include <pj++/lock.hpp>
+#include <pj++/hash.hpp>
+#include <pj++/os.hpp>
+#include <pj++/proactor.hpp>
+#include <pj++/sock.hpp>
+#include <pj++/string.hpp>
+#include <pj++/timer.hpp>
+#include <pj++/tree.hpp>
+
+class My_Async_Op : public Pj_Async_Op
+{
+};
+
+class My_Event_Handler : public Pj_Event_Handler
+{
+};
+
+int main()
+{
+ Pjlib lib;
+ Pj_Caching_Pool mem;
+ Pj_Pool the_pool;
+ Pj_Pool *pool = &the_pool;
+
+ the_pool.attach(mem.create_pool(4000,4000));
+
+ Pj_Semaphore_Lock lsem(pool);
+ Pj_Semaphore_Lock *plsem;
+
+ plsem = new(pool) Pj_Semaphore_Lock(pool);
+ delete plsem;
+
+ Pj_Proactor proactor(pool, 100, 100);
+
+ My_Event_Handler *event_handler = new(the_pool) My_Event_Handler;
+ proactor.register_socket_handler(pool, event_handler);
+ proactor.unregister_handler(event_handler);
+
+ return 0;
+}
+
diff --git a/pjlib/src/pjlib-samples/except.c b/pjlib/src/pjlib-samples/except.c
index 6611a9da..3bbfa544 100644
--- a/pjlib/src/pjlib-samples/except.c
+++ b/pjlib/src/pjlib-samples/except.c
@@ -1,85 +1,85 @@
-/* $Id$ */
-/*
- * Copyright (C)2003-2006 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 <pj/except.h>
-#include <pj/rand.h>
-#include <stdio.h>
-#include <stdlib.h>
-
-/**
- * \page page_pjlib_samples_except_c Example: Exception Handling
- *
- * Below is sample program to demonstrate how to use exception handling.
- *
- * \includelineno pjlib-samples/except.c
- */
-
-static pj_exception_id_t NO_MEMORY, OTHER_EXCEPTION;
-
-static void randomly_throw_exception()
-{
- if (pj_rand() % 2)
- PJ_THROW(OTHER_EXCEPTION);
-}
-
-static void *my_malloc(size_t size)
-{
- void *ptr = malloc(size);
- if (!ptr)
- PJ_THROW(NO_MEMORY);
- return ptr;
-}
-
-static int test_exception()
-{
- PJ_USE_EXCEPTION;
-
- PJ_TRY {
- void *data = my_malloc(200);
- free(data);
- randomly_throw_exception();
- }
- PJ_CATCH( NO_MEMORY ) {
- puts("Can't allocate memory");
- return 0;
- }
- PJ_DEFAULT {
- pj_exception_id_t x_id;
-
- x_id = PJ_GET_EXCEPTION();
- printf("Caught exception %d (%s)\n",
- x_id, pj_exception_id_name(x_id));
- }
- PJ_END
- return 1;
-}
-
-int main()
-{
- pj_status_t rc;
-
- // Error handling is omited for clarity.
-
- rc = pj_init();
-
- rc = pj_exception_id_alloc("No Memory", &NO_MEMORY);
- rc = pj_exception_id_alloc("Other Exception", &OTHER_EXCEPTION);
-
- return test_exception();
-}
-
+/* $Id$ */
+/*
+ * Copyright (C)2003-2006 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 <pj/except.h>
+#include <pj/rand.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+/**
+ * \page page_pjlib_samples_except_c Example: Exception Handling
+ *
+ * Below is sample program to demonstrate how to use exception handling.
+ *
+ * \includelineno pjlib-samples/except.c
+ */
+
+static pj_exception_id_t NO_MEMORY, OTHER_EXCEPTION;
+
+static void randomly_throw_exception()
+{
+ if (pj_rand() % 2)
+ PJ_THROW(OTHER_EXCEPTION);
+}
+
+static void *my_malloc(size_t size)
+{
+ void *ptr = malloc(size);
+ if (!ptr)
+ PJ_THROW(NO_MEMORY);
+ return ptr;
+}
+
+static int test_exception()
+{
+ PJ_USE_EXCEPTION;
+
+ PJ_TRY {
+ void *data = my_malloc(200);
+ free(data);
+ randomly_throw_exception();
+ }
+ PJ_CATCH( NO_MEMORY ) {
+ puts("Can't allocate memory");
+ return 0;
+ }
+ PJ_DEFAULT {
+ pj_exception_id_t x_id;
+
+ x_id = PJ_GET_EXCEPTION();
+ printf("Caught exception %d (%s)\n",
+ x_id, pj_exception_id_name(x_id));
+ }
+ PJ_END
+ return 1;
+}
+
+int main()
+{
+ pj_status_t rc;
+
+ // Error handling is omited for clarity.
+
+ rc = pj_init();
+
+ rc = pj_exception_id_alloc("No Memory", &NO_MEMORY);
+ rc = pj_exception_id_alloc("Other Exception", &OTHER_EXCEPTION);
+
+ return test_exception();
+}
+
diff --git a/pjlib/src/pjlib-samples/list.c b/pjlib/src/pjlib-samples/list.c
index 6f089dac..72e514e6 100644
--- a/pjlib/src/pjlib-samples/list.c
+++ b/pjlib/src/pjlib-samples/list.c
@@ -1,71 +1,71 @@
-/* $Id$ */
-/*
- * Copyright (C)2003-2006 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 <pj/list.h>
-#include <pj/assert.h>
-#include <pj/log.h>
-
-/**
- * \page page_pjlib_samples_list_c Example: List Manipulation
- *
- * Below is sample program to demonstrate how to manipulate linked list.
- *
- * \includelineno pjlib-samples/list.c
- */
-
-struct my_node
-{
- // This must be the first member declared in the struct!
- PJ_DECL_LIST_MEMBER(struct my_node);
- int value;
-};
-
-
-int main()
-{
- struct my_node nodes[10];
- struct my_node list;
- struct my_node *it;
- int i;
-
- // Initialize the list as empty.
- pj_list_init(&list);
-
- // Insert nodes.
- for (i=0; i<10; ++i) {
- nodes[i].value = i;
- pj_list_insert_before(&list, &nodes[i]);
- }
-
- // Iterate list nodes.
- it = list.next;
- while (it != &list) {
- PJ_LOG(3,("list", "value = %d", it->value));
- it = it->next;
- }
-
- // Erase all nodes.
- for (i=0; i<10; ++i) {
- pj_list_erase(&nodes[i]);
- }
-
- // List must be empty by now.
- pj_assert( pj_list_empty(&list) );
-
- return 0;
-};
+/* $Id$ */
+/*
+ * Copyright (C)2003-2006 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 <pj/list.h>
+#include <pj/assert.h>
+#include <pj/log.h>
+
+/**
+ * \page page_pjlib_samples_list_c Example: List Manipulation
+ *
+ * Below is sample program to demonstrate how to manipulate linked list.
+ *
+ * \includelineno pjlib-samples/list.c
+ */
+
+struct my_node
+{
+ // This must be the first member declared in the struct!
+ PJ_DECL_LIST_MEMBER(struct my_node);
+ int value;
+};
+
+
+int main()
+{
+ struct my_node nodes[10];
+ struct my_node list;
+ struct my_node *it;
+ int i;
+
+ // Initialize the list as empty.
+ pj_list_init(&list);
+
+ // Insert nodes.
+ for (i=0; i<10; ++i) {
+ nodes[i].value = i;
+ pj_list_insert_before(&list, &nodes[i]);
+ }
+
+ // Iterate list nodes.
+ it = list.next;
+ while (it != &list) {
+ PJ_LOG(3,("list", "value = %d", it->value));
+ it = it->next;
+ }
+
+ // Erase all nodes.
+ for (i=0; i<10; ++i) {
+ pj_list_erase(&nodes[i]);
+ }
+
+ // List must be empty by now.
+ pj_assert( pj_list_empty(&list) );
+
+ return 0;
+};
diff --git a/pjlib/src/pjlib-samples/log.c b/pjlib/src/pjlib-samples/log.c
index e6712641..dffcb72e 100644
--- a/pjlib/src/pjlib-samples/log.c
+++ b/pjlib/src/pjlib-samples/log.c
@@ -1,42 +1,42 @@
-/* $Id$ */
-/*
- * Copyright (C)2003-2006 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 <pj/log.h>
-
-/**
- * \page page_pjlib_samples_log_c Example: Log, Hello World
- *
- * Very simple program to write log.
- *
- * \includelineno pjlib-samples/log.c
- */
-
-int main()
-{
- pj_status_t rc;
-
- // Error handling omited for clarity
-
- // Must initialize PJLIB first!
- rc = pj_init();
-
- PJ_LOG(3, ("main.c", "Hello world!"));
-
- return 0;
-}
-
+/* $Id$ */
+/*
+ * Copyright (C)2003-2006 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 <pj/log.h>
+
+/**
+ * \page page_pjlib_samples_log_c Example: Log, Hello World
+ *
+ * Very simple program to write log.
+ *
+ * \includelineno pjlib-samples/log.c
+ */
+
+int main()
+{
+ pj_status_t rc;
+
+ // Error handling omited for clarity
+
+ // Must initialize PJLIB first!
+ rc = pj_init();
+
+ PJ_LOG(3, ("main.c", "Hello world!"));
+
+ return 0;
+}
+
diff --git a/pjlib/src/pjlib-test/atomic.c b/pjlib/src/pjlib-test/atomic.c
index 2f6acc5e..a7fbe60f 100644
--- a/pjlib/src/pjlib-test/atomic.c
+++ b/pjlib/src/pjlib-test/atomic.c
@@ -1,108 +1,108 @@
-/* $Id$ */
-/*
- * Copyright (C)2003-2006 Benny Prijono <benny@prijono.org>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
-#include "test.h"
-#include <pjlib.h>
-
-/**
- * \page page_pjlib_atomic_test Test: Atomic Variable
- *
- * This file provides implementation of \b atomic_test(). It tests the
- * functionality of the atomic variable API.
- *
- * \section atomic_test_sec Scope of the Test
- *
- * API tested:
- * - pj_atomic_create()
- * - pj_atomic_get()
- * - pj_atomic_inc()
- * - pj_atomic_dec()
- * - pj_atomic_set()
- * - pj_atomic_destroy()
- *
- *
- * This file is <b>pjlib-test/atomic.c</b>
- *
- * \include pjlib-test/atomic.c
- */
-
-
-#if INCLUDE_ATOMIC_TEST
-
-int atomic_test(void)
-{
- pj_pool_t *pool;
- pj_atomic_t *atomic_var;
- pj_status_t rc;
-
- pool = pj_pool_create(mem, NULL, 4096, 0, NULL);
- if (!pool)
- return -10;
-
- /* create() */
- rc = pj_atomic_create(pool, 111, &atomic_var);
- if (rc != 0) {
- return -20;
- }
-
- /* get: check the value. */
- if (pj_atomic_get(atomic_var) != 111)
- return -30;
-
- /* increment. */
- pj_atomic_inc(atomic_var);
- if (pj_atomic_get(atomic_var) != 112)
- return -40;
-
- /* decrement. */
- pj_atomic_dec(atomic_var);
- if (pj_atomic_get(atomic_var) != 111)
- return -50;
-
- /* set */
- pj_atomic_set(atomic_var, 211);
- if (pj_atomic_get(atomic_var) != 211)
- return -60;
-
- /* add */
- pj_atomic_add(atomic_var, 10);
- if (pj_atomic_get(atomic_var) != 221)
- return -60;
-
- /* check the value again. */
- if (pj_atomic_get(atomic_var) != 221)
- return -70;
-
- /* destroy */
- rc = pj_atomic_destroy(atomic_var);
- if (rc != 0)
- return -80;
-
- pj_pool_release(pool);
-
- return 0;
-}
-
-
-#else
-/* To prevent warning about "translation unit is empty"
- * when this test is disabled.
- */
-int dummy_atomic_test;
-#endif /* INCLUDE_ATOMIC_TEST */
-
+/* $Id$ */
+/*
+ * Copyright (C)2003-2006 Benny Prijono <benny@prijono.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#include "test.h"
+#include <pjlib.h>
+
+/**
+ * \page page_pjlib_atomic_test Test: Atomic Variable
+ *
+ * This file provides implementation of \b atomic_test(). It tests the
+ * functionality of the atomic variable API.
+ *
+ * \section atomic_test_sec Scope of the Test
+ *
+ * API tested:
+ * - pj_atomic_create()
+ * - pj_atomic_get()
+ * - pj_atomic_inc()
+ * - pj_atomic_dec()
+ * - pj_atomic_set()
+ * - pj_atomic_destroy()
+ *
+ *
+ * This file is <b>pjlib-test/atomic.c</b>
+ *
+ * \include pjlib-test/atomic.c
+ */
+
+
+#if INCLUDE_ATOMIC_TEST
+
+int atomic_test(void)
+{
+ pj_pool_t *pool;
+ pj_atomic_t *atomic_var;
+ pj_status_t rc;
+
+ pool = pj_pool_create(mem, NULL, 4096, 0, NULL);
+ if (!pool)
+ return -10;
+
+ /* create() */
+ rc = pj_atomic_create(pool, 111, &atomic_var);
+ if (rc != 0) {
+ return -20;
+ }
+
+ /* get: check the value. */
+ if (pj_atomic_get(atomic_var) != 111)
+ return -30;
+
+ /* increment. */
+ pj_atomic_inc(atomic_var);
+ if (pj_atomic_get(atomic_var) != 112)
+ return -40;
+
+ /* decrement. */
+ pj_atomic_dec(atomic_var);
+ if (pj_atomic_get(atomic_var) != 111)
+ return -50;
+
+ /* set */
+ pj_atomic_set(atomic_var, 211);
+ if (pj_atomic_get(atomic_var) != 211)
+ return -60;
+
+ /* add */
+ pj_atomic_add(atomic_var, 10);
+ if (pj_atomic_get(atomic_var) != 221)
+ return -60;
+
+ /* check the value again. */
+ if (pj_atomic_get(atomic_var) != 221)
+ return -70;
+
+ /* destroy */
+ rc = pj_atomic_destroy(atomic_var);
+ if (rc != 0)
+ return -80;
+
+ pj_pool_release(pool);
+
+ return 0;
+}
+
+
+#else
+/* To prevent warning about "translation unit is empty"
+ * when this test is disabled.
+ */
+int dummy_atomic_test;
+#endif /* INCLUDE_ATOMIC_TEST */
+
diff --git a/pjlib/src/pjlib-test/echo_clt.c b/pjlib/src/pjlib-test/echo_clt.c
index a9d8183c..2453d120 100644
--- a/pjlib/src/pjlib-test/echo_clt.c
+++ b/pjlib/src/pjlib-test/echo_clt.c
@@ -1,268 +1,268 @@
-/* $Id$ */
-/*
- * Copyright (C)2003-2006 Benny Prijono <benny@prijono.org>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
-#include "test.h"
-#include <pjlib.h>
-
-#if INCLUDE_ECHO_CLIENT
-
-enum { BUF_SIZE = 512 };
-
-struct client
-{
- int sock_type;
- const char *server;
- int port;
-};
-
-static pj_atomic_t *totalBytes;
-static pj_atomic_t *timeout_counter;
-static pj_atomic_t *invalid_counter;
-
-#define MSEC_PRINT_DURATION 1000
-
-static int wait_socket(pj_sock_t sock, unsigned msec_timeout)
-{
- pj_fd_set_t fdset;
- pj_time_val timeout;
-
- timeout.sec = 0;
- timeout.msec = msec_timeout;
- pj_time_val_normalize(&timeout);
-
- PJ_FD_ZERO(&fdset);
- PJ_FD_SET(sock, &fdset);
-
- return pj_sock_select(FD_SETSIZE, &fdset, NULL, NULL, &timeout);
-}
-
-static int echo_client_thread(void *arg)
-{
- pj_sock_t sock;
- char send_buf[BUF_SIZE];
- char recv_buf[BUF_SIZE];
- pj_sockaddr_in addr;
- pj_str_t s;
- pj_status_t rc;
- pj_uint32_t buffer_id;
- pj_uint32_t buffer_counter;
- struct client *client = arg;
- pj_status_t last_recv_err = PJ_SUCCESS, last_send_err = PJ_SUCCESS;
- unsigned counter = 0;
-
- rc = app_socket(PJ_AF_INET, client->sock_type, 0, -1, &sock);
- if (rc != PJ_SUCCESS) {
- app_perror("...unable to create socket", rc);
- return -10;
- }
-
- rc = pj_sockaddr_in_init( &addr, pj_cstr(&s, client->server),
- (pj_uint16_t)client->port);
- if (rc != PJ_SUCCESS) {
- app_perror("...unable to resolve server", rc);
- return -15;
- }
-
- rc = pj_sock_connect(sock, &addr, sizeof(addr));
- if (rc != PJ_SUCCESS) {
- app_perror("...connect() error", rc);
- pj_sock_close(sock);
- return -20;
- }
-
- PJ_LOG(3,("", "...socket connected to %s:%d",
- pj_inet_ntoa(addr.sin_addr),
- pj_ntohs(addr.sin_port)));
-
- pj_memset(send_buf, 'A', BUF_SIZE);
- send_buf[BUF_SIZE-1]='\0';
-
- /* Give other thread chance to initialize themselves! */
- pj_thread_sleep(200);
-
- //PJ_LOG(3,("", "...thread %p running", pj_thread_this()));
-
- buffer_id = (pj_uint32_t) pj_thread_this();
- buffer_counter = 0;
-
- *(pj_uint32_t*)send_buf = buffer_id;
-
- for (;;) {
- int rc;
- pj_ssize_t bytes;
-
- ++counter;
-
- //while (wait_socket(sock,0) > 0)
- // ;
-
- /* Send a packet. */
- bytes = BUF_SIZE;
- *(pj_uint32_t*)(send_buf+4) = ++buffer_counter;
- rc = pj_sock_send(sock, send_buf, &bytes, 0);
- if (rc != PJ_SUCCESS || bytes != BUF_SIZE) {
- if (rc != last_send_err) {
- app_perror("...send() error", rc);
- PJ_LOG(3,("", "...ignoring subsequent error.."));
- last_send_err = rc;
- pj_thread_sleep(100);
- }
- continue;
- }
-
- rc = wait_socket(sock, 500);
- if (rc == 0) {
- PJ_LOG(3,("", "...timeout"));
- bytes = 0;
- pj_atomic_inc(timeout_counter);
- } else if (rc < 0) {
- rc = pj_get_netos_error();
- app_perror("...select() error", rc);
- break;
- } else {
- /* Receive back the original packet. */
- bytes = 0;
- do {
- pj_ssize_t received = BUF_SIZE - bytes;
- rc = pj_sock_recv(sock, recv_buf+bytes, &received, 0);
- if (rc != PJ_SUCCESS || received == 0) {
- if (rc != last_recv_err) {
- app_perror("...recv() error", rc);
- PJ_LOG(3,("", "...ignoring subsequent error.."));
- last_recv_err = rc;
- pj_thread_sleep(100);
- }
- bytes = 0;
- received = 0;
- break;
- }
- bytes += received;
- } while (bytes != BUF_SIZE && bytes != 0);
- }
-
- if (bytes == 0)
- continue;
-
- if (pj_memcmp(send_buf, recv_buf, BUF_SIZE) != 0) {
- recv_buf[BUF_SIZE-1] = '\0';
- PJ_LOG(3,("", "...error: buffer %u has changed!\n"
- "send_buf=%s\n"
- "recv_buf=%s\n",
- counter, send_buf, recv_buf));
- pj_atomic_inc(invalid_counter);
- }
-
- /* Accumulate total received. */
- pj_atomic_add(totalBytes, bytes);
- }
-
- pj_sock_close(sock);
- return 0;
-}
-
-int echo_client(int sock_type, const char *server, int port)
-{
- pj_pool_t *pool;
- pj_thread_t *thread[ECHO_CLIENT_MAX_THREADS];
- pj_status_t rc;
- struct client client;
- int i;
- pj_atomic_value_t last_received;
- pj_timestamp last_report;
-
- client.sock_type = sock_type;
- client.server = server;
- client.port = port;
-
- pool = pj_pool_create( mem, NULL, 4000, 4000, NULL );
-
- rc = pj_atomic_create(pool, 0, &totalBytes);
- if (rc != PJ_SUCCESS) {
- PJ_LOG(3,("", "...error: unable to create atomic variable", rc));
- return -30;
- }
- rc = pj_atomic_create(pool, 0, &invalid_counter);
- rc = pj_atomic_create(pool, 0, &timeout_counter);
-
- PJ_LOG(3,("", "Echo client started"));
- PJ_LOG(3,("", " Destination: %s:%d",
- ECHO_SERVER_ADDRESS, ECHO_SERVER_START_PORT));
- PJ_LOG(3,("", " Press Ctrl-C to exit"));
-
- for (i=0; i<ECHO_CLIENT_MAX_THREADS; ++i) {
- rc = pj_thread_create( pool, NULL, &echo_client_thread, &client,
- PJ_THREAD_DEFAULT_STACK_SIZE, 0,
- &thread[i]);
- if (rc != PJ_SUCCESS) {
- app_perror("...error: unable to create thread", rc);
- return -10;
- }
- }
-
- last_received = 0;
- pj_get_timestamp(&last_report);
-
- for (;;) {
- pj_timestamp now;
- unsigned long received, cur_received;
- unsigned msec;
- pj_highprec_t bw;
- pj_time_val elapsed;
- unsigned bw32;
- pj_uint32_t timeout, invalid;
-
- pj_thread_sleep(1000);
-
- pj_get_timestamp(&now);
- elapsed = pj_elapsed_time(&last_report, &now);
- msec = PJ_TIME_VAL_MSEC(elapsed);
-
- received = pj_atomic_get(totalBytes);
- cur_received = received - last_received;
-
- bw = cur_received;
- pj_highprec_mul(bw, 1000);
- pj_highprec_div(bw, msec);
-
- bw32 = (unsigned)bw;
-
- last_report = now;
- last_received = received;
-
- timeout = pj_atomic_get(timeout_counter);
- invalid = pj_atomic_get(invalid_counter);
-
- PJ_LOG(3,("",
- "...%d threads, total bandwidth: %d KB/s, "
- "timeout=%d, invalid=%d",
- ECHO_CLIENT_MAX_THREADS, bw32/1000,
- timeout, invalid));
- }
-
- for (i=0; i<ECHO_CLIENT_MAX_THREADS; ++i) {
- pj_thread_join( thread[i] );
- }
-
- pj_pool_release(pool);
- return 0;
-}
-
-
-#else
-int dummy_echo_client;
-#endif /* INCLUDE_ECHO_CLIENT */
+/* $Id$ */
+/*
+ * Copyright (C)2003-2006 Benny Prijono <benny@prijono.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#include "test.h"
+#include <pjlib.h>
+
+#if INCLUDE_ECHO_CLIENT
+
+enum { BUF_SIZE = 512 };
+
+struct client
+{
+ int sock_type;
+ const char *server;
+ int port;
+};
+
+static pj_atomic_t *totalBytes;
+static pj_atomic_t *timeout_counter;
+static pj_atomic_t *invalid_counter;
+
+#define MSEC_PRINT_DURATION 1000
+
+static int wait_socket(pj_sock_t sock, unsigned msec_timeout)
+{
+ pj_fd_set_t fdset;
+ pj_time_val timeout;
+
+ timeout.sec = 0;
+ timeout.msec = msec_timeout;
+ pj_time_val_normalize(&timeout);
+
+ PJ_FD_ZERO(&fdset);
+ PJ_FD_SET(sock, &fdset);
+
+ return pj_sock_select(FD_SETSIZE, &fdset, NULL, NULL, &timeout);
+}
+
+static int echo_client_thread(void *arg)
+{
+ pj_sock_t sock;
+ char send_buf[BUF_SIZE];
+ char recv_buf[BUF_SIZE];
+ pj_sockaddr_in addr;
+ pj_str_t s;
+ pj_status_t rc;
+ pj_uint32_t buffer_id;
+ pj_uint32_t buffer_counter;
+ struct client *client = arg;
+ pj_status_t last_recv_err = PJ_SUCCESS, last_send_err = PJ_SUCCESS;
+ unsigned counter = 0;
+
+ rc = app_socket(PJ_AF_INET, client->sock_type, 0, -1, &sock);
+ if (rc != PJ_SUCCESS) {
+ app_perror("...unable to create socket", rc);
+ return -10;
+ }
+
+ rc = pj_sockaddr_in_init( &addr, pj_cstr(&s, client->server),
+ (pj_uint16_t)client->port);
+ if (rc != PJ_SUCCESS) {
+ app_perror("...unable to resolve server", rc);
+ return -15;
+ }
+
+ rc = pj_sock_connect(sock, &addr, sizeof(addr));
+ if (rc != PJ_SUCCESS) {
+ app_perror("...connect() error", rc);
+ pj_sock_close(sock);
+ return -20;
+ }
+
+ PJ_LOG(3,("", "...socket connected to %s:%d",
+ pj_inet_ntoa(addr.sin_addr),
+ pj_ntohs(addr.sin_port)));
+
+ pj_memset(send_buf, 'A', BUF_SIZE);
+ send_buf[BUF_SIZE-1]='\0';
+
+ /* Give other thread chance to initialize themselves! */
+ pj_thread_sleep(200);
+
+ //PJ_LOG(3,("", "...thread %p running", pj_thread_this()));
+
+ buffer_id = (pj_uint32_t) pj_thread_this();
+ buffer_counter = 0;
+
+ *(pj_uint32_t*)send_buf = buffer_id;
+
+ for (;;) {
+ int rc;
+ pj_ssize_t bytes;
+
+ ++counter;
+
+ //while (wait_socket(sock,0) > 0)
+ // ;
+
+ /* Send a packet. */
+ bytes = BUF_SIZE;
+ *(pj_uint32_t*)(send_buf+4) = ++buffer_counter;
+ rc = pj_sock_send(sock, send_buf, &bytes, 0);
+ if (rc != PJ_SUCCESS || bytes != BUF_SIZE) {
+ if (rc != last_send_err) {
+ app_perror("...send() error", rc);
+ PJ_LOG(3,("", "...ignoring subsequent error.."));
+ last_send_err = rc;
+ pj_thread_sleep(100);
+ }
+ continue;
+ }
+
+ rc = wait_socket(sock, 500);
+ if (rc == 0) {
+ PJ_LOG(3,("", "...timeout"));
+ bytes = 0;
+ pj_atomic_inc(timeout_counter);
+ } else if (rc < 0) {
+ rc = pj_get_netos_error();
+ app_perror("...select() error", rc);
+ break;
+ } else {
+ /* Receive back the original packet. */
+ bytes = 0;
+ do {
+ pj_ssize_t received = BUF_SIZE - bytes;
+ rc = pj_sock_recv(sock, recv_buf+bytes, &received, 0);
+ if (rc != PJ_SUCCESS || received == 0) {
+ if (rc != last_recv_err) {
+ app_perror("...recv() error", rc);
+ PJ_LOG(3,("", "...ignoring subsequent error.."));
+ last_recv_err = rc;
+ pj_thread_sleep(100);
+ }
+ bytes = 0;
+ received = 0;
+ break;
+ }
+ bytes += received;
+ } while (bytes != BUF_SIZE && bytes != 0);
+ }
+
+ if (bytes == 0)
+ continue;
+
+ if (pj_memcmp(send_buf, recv_buf, BUF_SIZE) != 0) {
+ recv_buf[BUF_SIZE-1] = '\0';
+ PJ_LOG(3,("", "...error: buffer %u has changed!\n"
+ "send_buf=%s\n"
+ "recv_buf=%s\n",
+ counter, send_buf, recv_buf));
+ pj_atomic_inc(invalid_counter);
+ }
+
+ /* Accumulate total received. */
+ pj_atomic_add(totalBytes, bytes);
+ }
+
+ pj_sock_close(sock);
+ return 0;
+}
+
+int echo_client(int sock_type, const char *server, int port)
+{
+ pj_pool_t *pool;
+ pj_thread_t *thread[ECHO_CLIENT_MAX_THREADS];
+ pj_status_t rc;
+ struct client client;
+ int i;
+ pj_atomic_value_t last_received;
+ pj_timestamp last_report;
+
+ client.sock_type = sock_type;
+ client.server = server;
+ client.port = port;
+
+ pool = pj_pool_create( mem, NULL, 4000, 4000, NULL );
+
+ rc = pj_atomic_create(pool, 0, &totalBytes);
+ if (rc != PJ_SUCCESS) {
+ PJ_LOG(3,("", "...error: unable to create atomic variable", rc));
+ return -30;
+ }
+ rc = pj_atomic_create(pool, 0, &invalid_counter);
+ rc = pj_atomic_create(pool, 0, &timeout_counter);
+
+ PJ_LOG(3,("", "Echo client started"));
+ PJ_LOG(3,("", " Destination: %s:%d",
+ ECHO_SERVER_ADDRESS, ECHO_SERVER_START_PORT));
+ PJ_LOG(3,("", " Press Ctrl-C to exit"));
+
+ for (i=0; i<ECHO_CLIENT_MAX_THREADS; ++i) {
+ rc = pj_thread_create( pool, NULL, &echo_client_thread, &client,
+ PJ_THREAD_DEFAULT_STACK_SIZE, 0,
+ &thread[i]);
+ if (rc != PJ_SUCCESS) {
+ app_perror("...error: unable to create thread", rc);
+ return -10;
+ }
+ }
+
+ last_received = 0;
+ pj_get_timestamp(&last_report);
+
+ for (;;) {
+ pj_timestamp now;
+ unsigned long received, cur_received;
+ unsigned msec;
+ pj_highprec_t bw;
+ pj_time_val elapsed;
+ unsigned bw32;
+ pj_uint32_t timeout, invalid;
+
+ pj_thread_sleep(1000);
+
+ pj_get_timestamp(&now);
+ elapsed = pj_elapsed_time(&last_report, &now);
+ msec = PJ_TIME_VAL_MSEC(elapsed);
+
+ received = pj_atomic_get(totalBytes);
+ cur_received = received - last_received;
+
+ bw = cur_received;
+ pj_highprec_mul(bw, 1000);
+ pj_highprec_div(bw, msec);
+
+ bw32 = (unsigned)bw;
+
+ last_report = now;
+ last_received = received;
+
+ timeout = pj_atomic_get(timeout_counter);
+ invalid = pj_atomic_get(invalid_counter);
+
+ PJ_LOG(3,("",
+ "...%d threads, total bandwidth: %d KB/s, "
+ "timeout=%d, invalid=%d",
+ ECHO_CLIENT_MAX_THREADS, bw32/1000,
+ timeout, invalid));
+ }
+
+ for (i=0; i<ECHO_CLIENT_MAX_THREADS; ++i) {
+ pj_thread_join( thread[i] );
+ }
+
+ pj_pool_release(pool);
+ return 0;
+}
+
+
+#else
+int dummy_echo_client;
+#endif /* INCLUDE_ECHO_CLIENT */
diff --git a/pjlib/src/pjlib-test/errno.c b/pjlib/src/pjlib-test/errno.c
index 1a2560c2..999f3525 100644
--- a/pjlib/src/pjlib-test/errno.c
+++ b/pjlib/src/pjlib-test/errno.c
@@ -1,162 +1,162 @@
-/* $Id$ */
-/*
- * Copyright (C)2003-2006 Benny Prijono <benny@prijono.org>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
-#include "test.h"
-#include <pj/errno.h>
-#include <pj/log.h>
-#include <pj/ctype.h>
-#include <pj/compat/socket.h>
-#include <pj/string.h>
-
-#if INCLUDE_ERRNO_TEST
-
-#define THIS_FILE "errno"
-
-#if defined(PJ_WIN32) && PJ_WIN32 != 0
-# include <windows.h>
-#endif
-
-#if defined(PJ_HAS_ERRNO_H) && PJ_HAS_ERRNO_H != 0
-# include <errno.h>
-#endif
-
-static void trim_newlines(char *s)
-{
- while (*s) {
- if (*s == '\r' || *s == '\n')
- *s = ' ';
- ++s;
- }
-}
-
-int my_strncasecmp(const char *s1, const char *s2, int max_len)
-{
- while (*s1 && *s2 && max_len > 0) {
- if (pj_tolower(*s1) != pj_tolower(*s2))
- return -1;
- ++s1;
- ++s2;
- --max_len;
- }
- return 0;
-}
-
-const char *my_stristr(const char *whole, const char *part)
-{
- int part_len = strlen(part);
- while (*whole) {
- if (my_strncasecmp(whole, part, part_len) == 0)
- return whole;
- ++whole;
- }
- return NULL;
-}
-
-int errno_test(void)
-{
- enum { CUT = 6 };
- pj_status_t rc;
- char errbuf[256];
-
- PJ_LOG(3,(THIS_FILE, "...errno test: check the msg carefully"));
-
- /*
- * Windows platform error.
- */
-# ifdef ERROR_INVALID_DATA
- rc = PJ_STATUS_FROM_OS(ERROR_INVALID_DATA);
- pj_set_os_error(rc);
-
- /* Whole */
- pj_strerror(rc, errbuf, sizeof(errbuf));
- trim_newlines(errbuf);
- PJ_LOG(3,(THIS_FILE, "...msg for rc=ERROR_INVALID_DATA: '%s'", errbuf));
- if (my_stristr(errbuf, "invalid") == NULL) {
- PJ_LOG(3, (THIS_FILE,
- "...error: expecting \"invalid\" string in the msg"));
- return -20;
- }
-
- /* Cut version. */
- pj_strerror(rc, errbuf, CUT);
- PJ_LOG(3,(THIS_FILE, "...msg for rc=ERROR_INVALID_DATA (cut): '%s'", errbuf));
-# endif
-
- /*
- * Unix errors
- */
-# ifdef EINVAL
- rc = PJ_STATUS_FROM_OS(EINVAL);
- pj_set_os_error(rc);
-
- /* Whole */
- pj_strerror(rc, errbuf, sizeof(errbuf));
- trim_newlines(errbuf);
- PJ_LOG(3,(THIS_FILE, "...msg for rc=EINVAL: '%s'", errbuf));
- if (my_stristr(errbuf, "invalid") == NULL) {
- PJ_LOG(3, (THIS_FILE,
- "...error: expecting \"invalid\" string in the msg"));
- return -30;
- }
-
- /* Cut */
- pj_strerror(rc, errbuf, CUT);
- PJ_LOG(3,(THIS_FILE, "...msg for rc=EINVAL (cut): '%s'", errbuf));
-# endif
-
- /*
- * Windows WSA errors
- */
-# ifdef WSAEINVAL
- rc = PJ_STATUS_FROM_OS(WSAEINVAL);
- pj_set_os_error(rc);
-
- /* Whole */
- pj_strerror(rc, errbuf, sizeof(errbuf));
- trim_newlines(errbuf);
- PJ_LOG(3,(THIS_FILE, "...msg for rc=WSAEINVAL: '%s'", errbuf));
- if (my_stristr(errbuf, "invalid") == NULL) {
- PJ_LOG(3, (THIS_FILE,
- "...error: expecting \"invalid\" string in the msg"));
- return -40;
- }
-
- /* Cut */
- pj_strerror(rc, errbuf, CUT);
- PJ_LOG(3,(THIS_FILE, "...msg for rc=WSAEINVAL (cut): '%s'", errbuf));
-# endif
-
- pj_strerror(PJ_EBUG, errbuf, sizeof(errbuf));
- PJ_LOG(3,(THIS_FILE, "...msg for rc=PJ_EBUG: '%s'", errbuf));
- if (my_stristr(errbuf, "BUG") == NULL) {
- PJ_LOG(3, (THIS_FILE,
- "...error: expecting \"BUG\" string in the msg"));
- return -20;
- }
-
- pj_strerror(PJ_EBUG, errbuf, CUT);
- PJ_LOG(3,(THIS_FILE, "...msg for rc=PJ_EBUG, cut at %d chars: '%s'",
- CUT, errbuf));
-
- return 0;
-}
-
-
-#endif /* INCLUDE_ERRNO_TEST */
-
-
+/* $Id$ */
+/*
+ * Copyright (C)2003-2006 Benny Prijono <benny@prijono.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#include "test.h"
+#include <pj/errno.h>
+#include <pj/log.h>
+#include <pj/ctype.h>
+#include <pj/compat/socket.h>
+#include <pj/string.h>
+
+#if INCLUDE_ERRNO_TEST
+
+#define THIS_FILE "errno"
+
+#if defined(PJ_WIN32) && PJ_WIN32 != 0
+# include <windows.h>
+#endif
+
+#if defined(PJ_HAS_ERRNO_H) && PJ_HAS_ERRNO_H != 0
+# include <errno.h>
+#endif
+
+static void trim_newlines(char *s)
+{
+ while (*s) {
+ if (*s == '\r' || *s == '\n')
+ *s = ' ';
+ ++s;
+ }
+}
+
+int my_strncasecmp(const char *s1, const char *s2, int max_len)
+{
+ while (*s1 && *s2 && max_len > 0) {
+ if (pj_tolower(*s1) != pj_tolower(*s2))
+ return -1;
+ ++s1;
+ ++s2;
+ --max_len;
+ }
+ return 0;
+}
+
+const char *my_stristr(const char *whole, const char *part)
+{
+ int part_len = strlen(part);
+ while (*whole) {
+ if (my_strncasecmp(whole, part, part_len) == 0)
+ return whole;
+ ++whole;
+ }
+ return NULL;
+}
+
+int errno_test(void)
+{
+ enum { CUT = 6 };
+ pj_status_t rc;
+ char errbuf[256];
+
+ PJ_LOG(3,(THIS_FILE, "...errno test: check the msg carefully"));
+
+ /*
+ * Windows platform error.
+ */
+# ifdef ERROR_INVALID_DATA
+ rc = PJ_STATUS_FROM_OS(ERROR_INVALID_DATA);
+ pj_set_os_error(rc);
+
+ /* Whole */
+ pj_strerror(rc, errbuf, sizeof(errbuf));
+ trim_newlines(errbuf);
+ PJ_LOG(3,(THIS_FILE, "...msg for rc=ERROR_INVALID_DATA: '%s'", errbuf));
+ if (my_stristr(errbuf, "invalid") == NULL) {
+ PJ_LOG(3, (THIS_FILE,
+ "...error: expecting \"invalid\" string in the msg"));
+ return -20;
+ }
+
+ /* Cut version. */
+ pj_strerror(rc, errbuf, CUT);
+ PJ_LOG(3,(THIS_FILE, "...msg for rc=ERROR_INVALID_DATA (cut): '%s'", errbuf));
+# endif
+
+ /*
+ * Unix errors
+ */
+# ifdef EINVAL
+ rc = PJ_STATUS_FROM_OS(EINVAL);
+ pj_set_os_error(rc);
+
+ /* Whole */
+ pj_strerror(rc, errbuf, sizeof(errbuf));
+ trim_newlines(errbuf);
+ PJ_LOG(3,(THIS_FILE, "...msg for rc=EINVAL: '%s'", errbuf));
+ if (my_stristr(errbuf, "invalid") == NULL) {
+ PJ_LOG(3, (THIS_FILE,
+ "...error: expecting \"invalid\" string in the msg"));
+ return -30;
+ }
+
+ /* Cut */
+ pj_strerror(rc, errbuf, CUT);
+ PJ_LOG(3,(THIS_FILE, "...msg for rc=EINVAL (cut): '%s'", errbuf));
+# endif
+
+ /*
+ * Windows WSA errors
+ */
+# ifdef WSAEINVAL
+ rc = PJ_STATUS_FROM_OS(WSAEINVAL);
+ pj_set_os_error(rc);
+
+ /* Whole */
+ pj_strerror(rc, errbuf, sizeof(errbuf));
+ trim_newlines(errbuf);
+ PJ_LOG(3,(THIS_FILE, "...msg for rc=WSAEINVAL: '%s'", errbuf));
+ if (my_stristr(errbuf, "invalid") == NULL) {
+ PJ_LOG(3, (THIS_FILE,
+ "...error: expecting \"invalid\" string in the msg"));
+ return -40;
+ }
+
+ /* Cut */
+ pj_strerror(rc, errbuf, CUT);
+ PJ_LOG(3,(THIS_FILE, "...msg for rc=WSAEINVAL (cut): '%s'", errbuf));
+# endif
+
+ pj_strerror(PJ_EBUG, errbuf, sizeof(errbuf));
+ PJ_LOG(3,(THIS_FILE, "...msg for rc=PJ_EBUG: '%s'", errbuf));
+ if (my_stristr(errbuf, "BUG") == NULL) {
+ PJ_LOG(3, (THIS_FILE,
+ "...error: expecting \"BUG\" string in the msg"));
+ return -20;
+ }
+
+ pj_strerror(PJ_EBUG, errbuf, CUT);
+ PJ_LOG(3,(THIS_FILE, "...msg for rc=PJ_EBUG, cut at %d chars: '%s'",
+ CUT, errbuf));
+
+ return 0;
+}
+
+
+#endif /* INCLUDE_ERRNO_TEST */
+
+
diff --git a/pjlib/src/pjlib-test/exception.c b/pjlib/src/pjlib-test/exception.c
index b09ba11c..ef4bf4a2 100644
--- a/pjlib/src/pjlib-test/exception.c
+++ b/pjlib/src/pjlib-test/exception.c
@@ -1,177 +1,177 @@
-/* $Id$ */
-/*
- * Copyright (C)2003-2006 Benny Prijono <benny@prijono.org>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
-#include "test.h"
-
-
-/**
- * \page page_pjlib_exception_test Test: Exception Handling
- *
- * This file provides implementation of \b exception_test(). It tests the
- * functionality of the exception handling API.
- *
- * @note This test use static ID not acquired through proper registration.
- * This is not recommended, since it may create ID collissions.
- *
- * \section exception_test_sec Scope of the Test
- *
- * Some scenarios tested:
- * - no exception situation
- * - basic TRY/CATCH
- * - multiple exception handlers
- * - default handlers
- *
- *
- * This file is <b>pjlib-test/exception.c</b>
- *
- * \include pjlib-test/exception.c
- */
-
-
-#if INCLUDE_EXCEPTION_TEST
-
-#include <pjlib.h>
-
-#define ID_1 1
-#define ID_2 2
-
-static int throw_id_1(void)
-{
- PJ_THROW( ID_1 );
- return -1;
-}
-
-static int throw_id_2(void)
-{
- PJ_THROW( ID_2 );
- return -1;
-}
-
-
-static int test(void)
-{
- PJ_USE_EXCEPTION;
- int rc = 0;
-
- /*
- * No exception situation.
- */
- PJ_TRY {
- rc = rc;
- }
- PJ_CATCH( ID_1 ) {
- rc = -2;
- }
- PJ_DEFAULT {
- rc = -3;
- }
- PJ_END;
-
- if (rc != 0)
- return rc;
-
-
- /*
- * Basic TRY/CATCH
- */
- PJ_TRY {
- rc = throw_id_1();
-
- // should not reach here.
- rc = -10;
- }
- PJ_CATCH( ID_1 ) {
- if (!rc) rc = 0;
- }
- PJ_DEFAULT {
- int id = PJ_GET_EXCEPTION();
- PJ_LOG(3,("", "...error: got unexpected exception %d (%s)",
- id, pj_exception_id_name(id)));
- if (!rc) rc = -20;
- }
- PJ_END;
-
- if (rc != 0)
- return rc;
-
- /*
- * Multiple exceptions handlers
- */
- PJ_TRY {
- rc = throw_id_2();
- // should not reach here.
- rc = -25;
- }
- PJ_CATCH( ID_1 ) {
- if (!rc) rc = -30;
- }
- PJ_CATCH( ID_2 ) {
- if (!rc) rc = 0;
- }
- PJ_DEFAULT {
- if (!rc) rc = -40;
- }
- PJ_END;
-
- if (rc != 0)
- return rc;
-
- /*
- * Test default handler.
- */
- PJ_TRY {
- rc = throw_id_1();
- // should not reach here
- rc = -50;
- }
- PJ_CATCH( ID_2 ) {
- if (!rc) rc = -60;
- }
- PJ_DEFAULT {
- if (!rc) rc = 0;
- }
- PJ_END;
-
- if (rc != 0)
- return rc;
-
- return 0;
-}
-
-int exception_test(void)
-{
- int i, rc;
- enum { LOOP = 10 };
-
- for (i=0; i<LOOP; ++i) {
- if ((rc=test()) != 0) {
- PJ_LOG(3,("", "...failed at i=%d (rc=%d)", i, rc));
- return rc;
- }
- }
- return 0;
-}
-
-#else
-/* To prevent warning about "translation unit is empty"
- * when this test is disabled.
- */
-int dummy_exception_test;
-#endif /* INCLUDE_EXCEPTION_TEST */
-
-
+/* $Id$ */
+/*
+ * Copyright (C)2003-2006 Benny Prijono <benny@prijono.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#include "test.h"
+
+
+/**
+ * \page page_pjlib_exception_test Test: Exception Handling
+ *
+ * This file provides implementation of \b exception_test(). It tests the
+ * functionality of the exception handling API.
+ *
+ * @note This test use static ID not acquired through proper registration.
+ * This is not recommended, since it may create ID collissions.
+ *
+ * \section exception_test_sec Scope of the Test
+ *
+ * Some scenarios tested:
+ * - no exception situation
+ * - basic TRY/CATCH
+ * - multiple exception handlers
+ * - default handlers
+ *
+ *
+ * This file is <b>pjlib-test/exception.c</b>
+ *
+ * \include pjlib-test/exception.c
+ */
+
+
+#if INCLUDE_EXCEPTION_TEST
+
+#include <pjlib.h>
+
+#define ID_1 1
+#define ID_2 2
+
+static int throw_id_1(void)
+{
+ PJ_THROW( ID_1 );
+ return -1;
+}
+
+static int throw_id_2(void)
+{
+ PJ_THROW( ID_2 );
+ return -1;
+}
+
+
+static int test(void)
+{
+ PJ_USE_EXCEPTION;
+ int rc = 0;
+
+ /*
+ * No exception situation.
+ */
+ PJ_TRY {
+ rc = rc;
+ }
+ PJ_CATCH( ID_1 ) {
+ rc = -2;
+ }
+ PJ_DEFAULT {
+ rc = -3;
+ }
+ PJ_END;
+
+ if (rc != 0)
+ return rc;
+
+
+ /*
+ * Basic TRY/CATCH
+ */
+ PJ_TRY {
+ rc = throw_id_1();
+
+ // should not reach here.
+ rc = -10;
+ }
+ PJ_CATCH( ID_1 ) {
+ if (!rc) rc = 0;
+ }
+ PJ_DEFAULT {
+ int id = PJ_GET_EXCEPTION();
+ PJ_LOG(3,("", "...error: got unexpected exception %d (%s)",
+ id, pj_exception_id_name(id)));
+ if (!rc) rc = -20;
+ }
+ PJ_END;
+
+ if (rc != 0)
+ return rc;
+
+ /*
+ * Multiple exceptions handlers
+ */
+ PJ_TRY {
+ rc = throw_id_2();
+ // should not reach here.
+ rc = -25;
+ }
+ PJ_CATCH( ID_1 ) {
+ if (!rc) rc = -30;
+ }
+ PJ_CATCH( ID_2 ) {
+ if (!rc) rc = 0;
+ }
+ PJ_DEFAULT {
+ if (!rc) rc = -40;
+ }
+ PJ_END;
+
+ if (rc != 0)
+ return rc;
+
+ /*
+ * Test default handler.
+ */
+ PJ_TRY {
+ rc = throw_id_1();
+ // should not reach here
+ rc = -50;
+ }
+ PJ_CATCH( ID_2 ) {
+ if (!rc) rc = -60;
+ }
+ PJ_DEFAULT {
+ if (!rc) rc = 0;
+ }
+ PJ_END;
+
+ if (rc != 0)
+ return rc;
+
+ return 0;
+}
+
+int exception_test(void)
+{
+ int i, rc;
+ enum { LOOP = 10 };
+
+ for (i=0; i<LOOP; ++i) {
+ if ((rc=test()) != 0) {
+ PJ_LOG(3,("", "...failed at i=%d (rc=%d)", i, rc));
+ return rc;
+ }
+ }
+ return 0;
+}
+
+#else
+/* To prevent warning about "translation unit is empty"
+ * when this test is disabled.
+ */
+int dummy_exception_test;
+#endif /* INCLUDE_EXCEPTION_TEST */
+
+
diff --git a/pjlib/src/pjlib-test/fifobuf.c b/pjlib/src/pjlib-test/fifobuf.c
index 03917de6..753ae251 100644
--- a/pjlib/src/pjlib-test/fifobuf.c
+++ b/pjlib/src/pjlib-test/fifobuf.c
@@ -1,116 +1,116 @@
-/* $Id$ */
-/*
- * Copyright (C)2003-2006 Benny Prijono <benny@prijono.org>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
-#include "test.h"
-
-/* To prevent warning about "translation unit is empty"
- * when this test is disabled.
- */
-int dummy_fifobuf_test;
-
-#if INCLUDE_FIFOBUF_TEST
-
-#include <pjlib.h>
-
-int fifobuf_test()
-{
- enum { SIZE = 1024, MAX_ENTRIES = 128,
- MIN_SIZE = 4, MAX_SIZE = 64,
- LOOP=10000 };
- pj_pool_t *pool;
- pj_fifobuf_t fifo;
- unsigned available = SIZE;
- void *entries[MAX_ENTRIES];
- void *buffer;
- int i;
-
- pool = pj_pool_create(mem, NULL, SIZE+256, 0, NULL);
- if (!pool)
- return -10;
-
- buffer = pj_pool_alloc(pool, SIZE);
- if (!buffer)
- return -20;
-
- pj_fifobuf_init (&fifo, buffer, SIZE);
-
- // Test 1
- for (i=0; i<LOOP*MAX_ENTRIES; ++i) {
- int size;
- int c, f;
- c = i%2;
- f = (i+1)%2;
- do {
- size = MIN_SIZE+(pj_rand() % MAX_SIZE);
- entries[c] = pj_fifobuf_alloc (&fifo, size);
- } while (entries[c] == 0);
- if ( i!=0) {
- pj_fifobuf_free(&fifo, entries[f]);
- }
- }
- if (entries[(i+1)%2])
- pj_fifobuf_free(&fifo, entries[(i+1)%2]);
-
- if (pj_fifobuf_max_size(&fifo) < SIZE-4) {
- pj_assert(0);
- return -1;
- }
-
- // Test 2
- entries[0] = pj_fifobuf_alloc (&fifo, MIN_SIZE);
- if (!entries[0]) return -1;
- for (i=0; i<LOOP*MAX_ENTRIES; ++i) {
- int size = MIN_SIZE+(pj_rand() % MAX_SIZE);
- entries[1] = pj_fifobuf_alloc (&fifo, size);
- if (entries[1])
- pj_fifobuf_unalloc(&fifo, entries[1]);
- }
- pj_fifobuf_unalloc(&fifo, entries[0]);
- if (pj_fifobuf_max_size(&fifo) < SIZE-4) {
- pj_assert(0);
- return -2;
- }
-
- // Test 3
- for (i=0; i<LOOP; ++i) {
- int count, j;
- for (count=0; available>=MIN_SIZE+4 && count < MAX_ENTRIES;) {
- int size = MIN_SIZE+(pj_rand() % MAX_SIZE);
- entries[count] = pj_fifobuf_alloc (&fifo, size);
- if (entries[count]) {
- available -= (size+4);
- ++count;
- }
- }
- for (j=0; j<count; ++j) {
- pj_fifobuf_free (&fifo, entries[j]);
- }
- available = SIZE;
- }
-
- if (pj_fifobuf_max_size(&fifo) < SIZE-4) {
- pj_assert(0);
- return -3;
- }
- pj_pool_release(pool);
- return 0;
-}
-
-#endif /* INCLUDE_FIFOBUF_TEST */
-
-
+/* $Id$ */
+/*
+ * Copyright (C)2003-2006 Benny Prijono <benny@prijono.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#include "test.h"
+
+/* To prevent warning about "translation unit is empty"
+ * when this test is disabled.
+ */
+int dummy_fifobuf_test;
+
+#if INCLUDE_FIFOBUF_TEST
+
+#include <pjlib.h>
+
+int fifobuf_test()
+{
+ enum { SIZE = 1024, MAX_ENTRIES = 128,
+ MIN_SIZE = 4, MAX_SIZE = 64,
+ LOOP=10000 };
+ pj_pool_t *pool;
+ pj_fifobuf_t fifo;
+ unsigned available = SIZE;
+ void *entries[MAX_ENTRIES];
+ void *buffer;
+ int i;
+
+ pool = pj_pool_create(mem, NULL, SIZE+256, 0, NULL);
+ if (!pool)
+ return -10;
+
+ buffer = pj_pool_alloc(pool, SIZE);
+ if (!buffer)
+ return -20;
+
+ pj_fifobuf_init (&fifo, buffer, SIZE);
+
+ // Test 1
+ for (i=0; i<LOOP*MAX_ENTRIES; ++i) {
+ int size;
+ int c, f;
+ c = i%2;
+ f = (i+1)%2;
+ do {
+ size = MIN_SIZE+(pj_rand() % MAX_SIZE);
+ entries[c] = pj_fifobuf_alloc (&fifo, size);
+ } while (entries[c] == 0);
+ if ( i!=0) {
+ pj_fifobuf_free(&fifo, entries[f]);
+ }
+ }
+ if (entries[(i+1)%2])
+ pj_fifobuf_free(&fifo, entries[(i+1)%2]);
+
+ if (pj_fifobuf_max_size(&fifo) < SIZE-4) {
+ pj_assert(0);
+ return -1;
+ }
+
+ // Test 2
+ entries[0] = pj_fifobuf_alloc (&fifo, MIN_SIZE);
+ if (!entries[0]) return -1;
+ for (i=0; i<LOOP*MAX_ENTRIES; ++i) {
+ int size = MIN_SIZE+(pj_rand() % MAX_SIZE);
+ entries[1] = pj_fifobuf_alloc (&fifo, size);
+ if (entries[1])
+ pj_fifobuf_unalloc(&fifo, entries[1]);
+ }
+ pj_fifobuf_unalloc(&fifo, entries[0]);
+ if (pj_fifobuf_max_size(&fifo) < SIZE-4) {
+ pj_assert(0);
+ return -2;
+ }
+
+ // Test 3
+ for (i=0; i<LOOP; ++i) {
+ int count, j;
+ for (count=0; available>=MIN_SIZE+4 && count < MAX_ENTRIES;) {
+ int size = MIN_SIZE+(pj_rand() % MAX_SIZE);
+ entries[count] = pj_fifobuf_alloc (&fifo, size);
+ if (entries[count]) {
+ available -= (size+4);
+ ++count;
+ }
+ }
+ for (j=0; j<count; ++j) {
+ pj_fifobuf_free (&fifo, entries[j]);
+ }
+ available = SIZE;
+ }
+
+ if (pj_fifobuf_max_size(&fifo) < SIZE-4) {
+ pj_assert(0);
+ return -3;
+ }
+ pj_pool_release(pool);
+ return 0;
+}
+
+#endif /* INCLUDE_FIFOBUF_TEST */
+
+
diff --git a/pjlib/src/pjlib-test/file.c b/pjlib/src/pjlib-test/file.c
index ffa72efd..d22c3956 100644
--- a/pjlib/src/pjlib-test/file.c
+++ b/pjlib/src/pjlib-test/file.c
@@ -1,212 +1,212 @@
-/* $Id$ */
-/*
- * Copyright (C)2003-2006 Benny Prijono <benny@prijono.org>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
-#include "test.h"
-#include <pjlib.h>
-
-#if INCLUDE_FILE_TEST
-
-#define FILENAME "testfil1.txt"
-#define NEWNAME "testfil2.txt"
-#define INCLUDE_FILE_TIME_TEST 0
-
-static char buffer[11] = {'H', 'e', 'l', 'l', 'o', ' ',
- 'W', 'o', 'r', 'l', 'd' };
-
-int file_test(void)
-{
- enum { FILE_MAX_AGE = 1000 };
- pj_oshandle_t fd = 0;
- pj_status_t status;
- char readbuf[sizeof(buffer)+16];
- pj_file_stat stat;
- pj_time_val start_time;
- pj_ssize_t size;
- pj_off_t pos;
-
- PJ_LOG(3,("", "..file io test.."));
-
- /* Get time. */
- pj_gettimeofday(&start_time);
-
- /* Delete original file if exists. */
- if (pj_file_exists(FILENAME))
- pj_file_delete(FILENAME);
-
- /*
- * Write data to the file.
- */
- status = pj_file_open(NULL, FILENAME, PJ_O_WRONLY, &fd);
- if (status != PJ_SUCCESS) {
- app_perror("...file_open() error", status);
- return -10;
- }
-
- size = sizeof(buffer);
- status = pj_file_write(fd, buffer, &size);
- if (status != PJ_SUCCESS) {
- app_perror("...file_write() error", status);
- pj_file_close(fd);
- return -20;
- }
- if (size != sizeof(buffer))
- return -25;
-
- status = pj_file_close(fd);
- if (status != PJ_SUCCESS) {
- app_perror("...file_close() error", status);
- return -30;
- }
-
- /* Check the file existance and size. */
- if (!pj_file_exists(FILENAME))
- return -40;
-
- if (pj_file_size(FILENAME) != sizeof(buffer))
- return -50;
-
- /* Get file stat. */
- status = pj_file_getstat(FILENAME, &stat);
- if (status != PJ_SUCCESS)
- return -60;
-
- /* Check stat size. */
- if (stat.size != sizeof(buffer))
- return -70;
-
- /* Check file creation time >= start_time. */
- if (!PJ_TIME_VAL_GTE(stat.ctime, start_time))
-#if INCLUDE_FILE_TIME_TEST
- return -80;
- /* Check file creation time is not much later. */
- PJ_TIME_VAL_SUB(stat.ctime, start_time);
- if (stat.ctime.sec > FILE_MAX_AGE)
- return -90;
-
- /* Check file modification time >= start_time. */
- if (!PJ_TIME_VAL_GTE(stat.mtime, start_time))
- return -80;
- /* Check file modification time is not much later. */
- PJ_TIME_VAL_SUB(stat.mtime, start_time);
- if (stat.mtime.sec > FILE_MAX_AGE)
- return -90;
-
- /* Check file access time >= start_time. */
- if (!PJ_TIME_VAL_GTE(stat.atime, start_time))
- return -80;
- /* Check file access time is not much later. */
- PJ_TIME_VAL_SUB(stat.atime, start_time);
- if (stat.atime.sec > FILE_MAX_AGE)
- return -90;
-#endif
-
- /*
- * Re-open the file and read data.
- */
- status = pj_file_open(NULL, FILENAME, PJ_O_RDONLY, &fd);
- if (status != PJ_SUCCESS) {
- app_perror("...file_open() error", status);
- return -100;
- }
-
- size = 0;
- while (size < sizeof(readbuf)) {
- pj_ssize_t read;
- read = 1;
- status = pj_file_read(fd, &readbuf[size], &read);
- if (status != PJ_SUCCESS) {
- PJ_LOG(3,("", "...error reading file after %d bytes (error follows)",
- size));
- app_perror("...error", status);
- return -110;
- }
- if (read == 0) {
- // EOF
- break;
- }
- size += read;
- }
-
- if (size != sizeof(buffer))
- return -120;
-
- /*
- if (!pj_file_eof(fd, PJ_O_RDONLY))
- return -130;
- */
-
- if (pj_memcmp(readbuf, buffer, size) != 0)
- return -140;
-
- /* Seek test. */
- status = pj_file_setpos(fd, 4, PJ_SEEK_SET);
- if (status != PJ_SUCCESS) {
- app_perror("...file_setpos() error", status);
- return -141;
- }
-
- /* getpos test. */
- status = pj_file_getpos(fd, &pos);
- if (status != PJ_SUCCESS) {
- app_perror("...file_getpos() error", status);
- return -142;
- }
- if (pos != 4)
- return -143;
-
- status = pj_file_close(fd);
- if (status != PJ_SUCCESS) {
- app_perror("...file_close() error", status);
- return -150;
- }
-
- /*
- * Rename test.
- */
- status = pj_file_move(FILENAME, NEWNAME);
- if (status != PJ_SUCCESS) {
- app_perror("...file_move() error", status);
- return -160;
- }
-
- if (pj_file_exists(FILENAME))
- return -170;
- if (!pj_file_exists(NEWNAME))
- return -180;
-
- if (pj_file_size(NEWNAME) != sizeof(buffer))
- return -190;
-
- /* Delete test. */
- status = pj_file_delete(NEWNAME);
- if (status != PJ_SUCCESS) {
- app_perror("...file_delete() error", status);
- return -200;
- }
-
- if (pj_file_exists(NEWNAME))
- return -210;
-
- PJ_LOG(3,("", "...success"));
- return PJ_SUCCESS;
-}
-
-#else
-int dummy_file_test;
-#endif
-
+/* $Id$ */
+/*
+ * Copyright (C)2003-2006 Benny Prijono <benny@prijono.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#include "test.h"
+#include <pjlib.h>
+
+#if INCLUDE_FILE_TEST
+
+#define FILENAME "testfil1.txt"
+#define NEWNAME "testfil2.txt"
+#define INCLUDE_FILE_TIME_TEST 0
+
+static char buffer[11] = {'H', 'e', 'l', 'l', 'o', ' ',
+ 'W', 'o', 'r', 'l', 'd' };
+
+int file_test(void)
+{
+ enum { FILE_MAX_AGE = 1000 };
+ pj_oshandle_t fd = 0;
+ pj_status_t status;
+ char readbuf[sizeof(buffer)+16];
+ pj_file_stat stat;
+ pj_time_val start_time;
+ pj_ssize_t size;
+ pj_off_t pos;
+
+ PJ_LOG(3,("", "..file io test.."));
+
+ /* Get time. */
+ pj_gettimeofday(&start_time);
+
+ /* Delete original file if exists. */
+ if (pj_file_exists(FILENAME))
+ pj_file_delete(FILENAME);
+
+ /*
+ * Write data to the file.
+ */
+ status = pj_file_open(NULL, FILENAME, PJ_O_WRONLY, &fd);
+ if (status != PJ_SUCCESS) {
+ app_perror("...file_open() error", status);
+ return -10;
+ }
+
+ size = sizeof(buffer);
+ status = pj_file_write(fd, buffer, &size);
+ if (status != PJ_SUCCESS) {
+ app_perror("...file_write() error", status);
+ pj_file_close(fd);
+ return -20;
+ }
+ if (size != sizeof(buffer))
+ return -25;
+
+ status = pj_file_close(fd);
+ if (status != PJ_SUCCESS) {
+ app_perror("...file_close() error", status);
+ return -30;
+ }
+
+ /* Check the file existance and size. */
+ if (!pj_file_exists(FILENAME))
+ return -40;
+
+ if (pj_file_size(FILENAME) != sizeof(buffer))
+ return -50;
+
+ /* Get file stat. */
+ status = pj_file_getstat(FILENAME, &stat);
+ if (status != PJ_SUCCESS)
+ return -60;
+
+ /* Check stat size. */
+ if (stat.size != sizeof(buffer))
+ return -70;
+
+ /* Check file creation time >= start_time. */
+ if (!PJ_TIME_VAL_GTE(stat.ctime, start_time))
+#if INCLUDE_FILE_TIME_TEST
+ return -80;
+ /* Check file creation time is not much later. */
+ PJ_TIME_VAL_SUB(stat.ctime, start_time);
+ if (stat.ctime.sec > FILE_MAX_AGE)
+ return -90;
+
+ /* Check file modification time >= start_time. */
+ if (!PJ_TIME_VAL_GTE(stat.mtime, start_time))
+ return -80;
+ /* Check file modification time is not much later. */
+ PJ_TIME_VAL_SUB(stat.mtime, start_time);
+ if (stat.mtime.sec > FILE_MAX_AGE)
+ return -90;
+
+ /* Check file access time >= start_time. */
+ if (!PJ_TIME_VAL_GTE(stat.atime, start_time))
+ return -80;
+ /* Check file access time is not much later. */
+ PJ_TIME_VAL_SUB(stat.atime, start_time);
+ if (stat.atime.sec > FILE_MAX_AGE)
+ return -90;
+#endif
+
+ /*
+ * Re-open the file and read data.
+ */
+ status = pj_file_open(NULL, FILENAME, PJ_O_RDONLY, &fd);
+ if (status != PJ_SUCCESS) {
+ app_perror("...file_open() error", status);
+ return -100;
+ }
+
+ size = 0;
+ while (size < sizeof(readbuf)) {
+ pj_ssize_t read;
+ read = 1;
+ status = pj_file_read(fd, &readbuf[size], &read);
+ if (status != PJ_SUCCESS) {
+ PJ_LOG(3,("", "...error reading file after %d bytes (error follows)",
+ size));
+ app_perror("...error", status);
+ return -110;
+ }
+ if (read == 0) {
+ // EOF
+ break;
+ }
+ size += read;
+ }
+
+ if (size != sizeof(buffer))
+ return -120;
+
+ /*
+ if (!pj_file_eof(fd, PJ_O_RDONLY))
+ return -130;
+ */
+
+ if (pj_memcmp(readbuf, buffer, size) != 0)
+ return -140;
+
+ /* Seek test. */
+ status = pj_file_setpos(fd, 4, PJ_SEEK_SET);
+ if (status != PJ_SUCCESS) {
+ app_perror("...file_setpos() error", status);
+ return -141;
+ }
+
+ /* getpos test. */
+ status = pj_file_getpos(fd, &pos);
+ if (status != PJ_SUCCESS) {
+ app_perror("...file_getpos() error", status);
+ return -142;
+ }
+ if (pos != 4)
+ return -143;
+
+ status = pj_file_close(fd);
+ if (status != PJ_SUCCESS) {
+ app_perror("...file_close() error", status);
+ return -150;
+ }
+
+ /*
+ * Rename test.
+ */
+ status = pj_file_move(FILENAME, NEWNAME);
+ if (status != PJ_SUCCESS) {
+ app_perror("...file_move() error", status);
+ return -160;
+ }
+
+ if (pj_file_exists(FILENAME))
+ return -170;
+ if (!pj_file_exists(NEWNAME))
+ return -180;
+
+ if (pj_file_size(NEWNAME) != sizeof(buffer))
+ return -190;
+
+ /* Delete test. */
+ status = pj_file_delete(NEWNAME);
+ if (status != PJ_SUCCESS) {
+ app_perror("...file_delete() error", status);
+ return -200;
+ }
+
+ if (pj_file_exists(NEWNAME))
+ return -210;
+
+ PJ_LOG(3,("", "...success"));
+ return PJ_SUCCESS;
+}
+
+#else
+int dummy_file_test;
+#endif
+
diff --git a/pjlib/src/pjlib-test/ioq_perf.c b/pjlib/src/pjlib-test/ioq_perf.c
index b10decc3..3a8de8e1 100644
--- a/pjlib/src/pjlib-test/ioq_perf.c
+++ b/pjlib/src/pjlib-test/ioq_perf.c
@@ -1,502 +1,502 @@
-/* $Id$ */
-/*
- * Copyright (C)2003-2006 Benny Prijono <benny@prijono.org>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
-#include "test.h"
-#include <pjlib.h>
-#include <pj/compat/high_precision.h>
-
-/**
- * \page page_pjlib_ioqueue_perf_test Test: I/O Queue Performance
- *
- * Test the performance of the I/O queue, using typical producer
- * consumer test. The test should examine the effect of using multiple
- * threads on the performance.
- *
- * This file is <b>pjlib-test/ioq_perf.c</b>
- *
- * \include pjlib-test/ioq_perf.c
- */
-
-#if INCLUDE_IOQUEUE_PERF_TEST
-
-#ifdef _MSC_VER
-# pragma warning ( disable: 4204) // non-constant aggregate initializer
-#endif
-
-#define THIS_FILE "ioq_perf"
-//#define TRACE_(expr) PJ_LOG(3,expr)
-#define TRACE_(expr)
-
-
-static pj_bool_t thread_quit_flag;
-static pj_status_t last_error;
-static unsigned last_error_counter;
-
-/* Descriptor for each producer/consumer pair. */
-typedef struct test_item
-{
- pj_sock_t server_fd,
- client_fd;
- pj_ioqueue_t *ioqueue;
- pj_ioqueue_key_t *server_key,
- *client_key;
- pj_ioqueue_op_key_t recv_op,
- send_op;
- int has_pending_send;
- pj_size_t buffer_size;
- char *outgoing_buffer;
- char *incoming_buffer;
- pj_size_t bytes_sent,
- bytes_recv;
-} test_item;
-
-/* Callback when data has been read.
- * Increment item->bytes_recv and ready to read the next data.
- */
-static void on_read_complete(pj_ioqueue_key_t *key,
- pj_ioqueue_op_key_t *op_key,
- pj_ssize_t bytes_read)
-{
- test_item *item = pj_ioqueue_get_user_data(key);
- pj_status_t rc;
- int data_is_available = 1;
-
- //TRACE_((THIS_FILE, " read complete, bytes_read=%d", bytes_read));
-
- do {
- if (thread_quit_flag)
- return;
-
- if (bytes_read < 0) {
- pj_status_t rc = -bytes_read;
- char errmsg[128];
-
- if (rc != last_error) {
- //last_error = rc;
- pj_strerror(rc, errmsg, sizeof(errmsg));
- PJ_LOG(3,(THIS_FILE, "...error: read error, bytes_read=%d (%s)",
- bytes_read, errmsg));
- PJ_LOG(3,(THIS_FILE,
- ".....additional info: total read=%u, total sent=%u",
- item->bytes_recv, item->bytes_sent));
- } else {
- last_error_counter++;
- }
- bytes_read = 0;
-
- } else if (bytes_read == 0) {
- PJ_LOG(3,(THIS_FILE, "...socket has closed!"));
- }
-
- item->bytes_recv += bytes_read;
-
- /* To assure that the test quits, even if main thread
- * doesn't have time to run.
- */
- if (item->bytes_recv > item->buffer_size * 10000)
- thread_quit_flag = 1;
-
- bytes_read = item->buffer_size;
- rc = pj_ioqueue_recv( key, op_key,
- item->incoming_buffer, &bytes_read, 0 );
-
- if (rc == PJ_SUCCESS) {
- data_is_available = 1;
- } else if (rc == PJ_EPENDING) {
- data_is_available = 0;
- } else {
- data_is_available = 0;
- if (rc != last_error) {
- last_error = rc;
- app_perror("...error: read error(1)", rc);
- } else {
- last_error_counter++;
- }
- }
-
- if (!item->has_pending_send) {
- pj_ssize_t sent = item->buffer_size;
- rc = pj_ioqueue_send(item->client_key, &item->send_op,
- item->outgoing_buffer, &sent, 0);
- if (rc != PJ_SUCCESS && rc != PJ_EPENDING) {
- app_perror("...error: write error", rc);
- }
-
- item->has_pending_send = (rc==PJ_EPENDING);
- }
-
- } while (data_is_available);
-}
-
-/* Callback when data has been written.
- * Increment item->bytes_sent and write the next data.
- */
-static void on_write_complete(pj_ioqueue_key_t *key,
- pj_ioqueue_op_key_t *op_key,
- pj_ssize_t bytes_sent)
-{
- test_item *item = pj_ioqueue_get_user_data(key);
-
- //TRACE_((THIS_FILE, " write complete: sent = %d", bytes_sent));
-
- if (thread_quit_flag)
- return;
-
- item->has_pending_send = 0;
- item->bytes_sent += bytes_sent;
-
- if (bytes_sent <= 0) {
- PJ_LOG(3,(THIS_FILE, "...error: sending stopped. bytes_sent=%d",
- bytes_sent));
- }
- else {
- pj_status_t rc;
-
- bytes_sent = item->buffer_size;
- rc = pj_ioqueue_send( item->client_key, op_key,
- item->outgoing_buffer, &bytes_sent, 0);
- if (rc != PJ_SUCCESS && rc != PJ_EPENDING) {
- app_perror("...error: write error", rc);
- }
-
- item->has_pending_send = (rc==PJ_EPENDING);
- }
-}
-
-/* The worker thread. */
-static int worker_thread(void *arg)
-{
- pj_ioqueue_t *ioqueue = arg;
- const pj_time_val timeout = {0, 100};
- int rc;
-
- while (!thread_quit_flag) {
- rc = pj_ioqueue_poll(ioqueue, &timeout);
- //TRACE_((THIS_FILE, " thread: poll returned rc=%d", rc));
- if (rc < 0) {
- app_perror("...error in pj_ioqueue_poll()", pj_get_netos_error());
- return -1;
- }
- }
- return 0;
-}
-
-/* Calculate the bandwidth for the specific test configuration.
- * The test is simple:
- * - create sockpair_cnt number of producer-consumer socket pair.
- * - create thread_cnt number of worker threads.
- * - each producer will send buffer_size bytes data as fast and
- * as soon as it can.
- * - each consumer will read buffer_size bytes of data as fast
- * as it could.
- * - measure the total bytes received by all consumers during a
- * period of time.
- */
-static int perform_test(int sock_type, const char *type_name,
- unsigned thread_cnt, unsigned sockpair_cnt,
- pj_size_t buffer_size,
- pj_size_t *p_bandwidth)
-{
- enum { MSEC_DURATION = 5000 };
- pj_pool_t *pool;
- test_item *items;
- pj_thread_t **thread;
- pj_ioqueue_t *ioqueue;
- pj_status_t rc;
- pj_ioqueue_callback ioqueue_callback;
- pj_uint32_t total_elapsed_usec, total_received;
- pj_highprec_t bandwidth;
- pj_timestamp start, stop;
- unsigned i;
-
- TRACE_((THIS_FILE, " starting test.."));
-
- ioqueue_callback.on_read_complete = &on_read_complete;
- ioqueue_callback.on_write_complete = &on_write_complete;
-
- thread_quit_flag = 0;
-
- pool = pj_pool_create(mem, NULL, 4096, 4096, NULL);
- if (!pool)
- return -10;
-
- items = pj_pool_alloc(pool, sockpair_cnt*sizeof(test_item));
- thread = pj_pool_alloc(pool, thread_cnt*sizeof(pj_thread_t*));
-
- TRACE_((THIS_FILE, " creating ioqueue.."));
- rc = pj_ioqueue_create(pool, sockpair_cnt*2, &ioqueue);
- if (rc != PJ_SUCCESS) {
- app_perror("...error: unable to create ioqueue", rc);
- return -15;
- }
-
- /* Initialize each producer-consumer pair. */
- for (i=0; i<sockpair_cnt; ++i) {
- pj_ssize_t bytes;
-
- items[i].ioqueue = ioqueue;
- items[i].buffer_size = buffer_size;
- items[i].outgoing_buffer = pj_pool_alloc(pool, buffer_size);
- items[i].incoming_buffer = pj_pool_alloc(pool, buffer_size);
- items[i].bytes_recv = items[i].bytes_sent = 0;
-
- /* randomize outgoing buffer. */
- pj_create_random_string(items[i].outgoing_buffer, buffer_size);
-
- /* Create socket pair. */
- TRACE_((THIS_FILE, " calling socketpair.."));
- rc = app_socketpair(PJ_AF_INET, sock_type, 0,
- &items[i].server_fd, &items[i].client_fd);
- if (rc != PJ_SUCCESS) {
- app_perror("...error: unable to create socket pair", rc);
- return -20;
- }
-
- /* Register server socket to ioqueue. */
- TRACE_((THIS_FILE, " register(1).."));
- rc = pj_ioqueue_register_sock(pool, ioqueue,
- items[i].server_fd,
- &items[i], &ioqueue_callback,
- &items[i].server_key);
- if (rc != PJ_SUCCESS) {
- app_perror("...error: registering server socket to ioqueue", rc);
- return -60;
- }
-
- /* Register client socket to ioqueue. */
- TRACE_((THIS_FILE, " register(2).."));
- rc = pj_ioqueue_register_sock(pool, ioqueue,
- items[i].client_fd,
- &items[i], &ioqueue_callback,
- &items[i].client_key);
- if (rc != PJ_SUCCESS) {
- app_perror("...error: registering server socket to ioqueue", rc);
- return -70;
- }
-
- /* Start reading. */
- TRACE_((THIS_FILE, " pj_ioqueue_recv.."));
- bytes = items[i].buffer_size;
- rc = pj_ioqueue_recv(items[i].server_key, &items[i].recv_op,
- items[i].incoming_buffer, &bytes,
- 0);
- if (rc != PJ_EPENDING) {
- app_perror("...error: pj_ioqueue_recv", rc);
- return -73;
- }
-
- /* Start writing. */
- TRACE_((THIS_FILE, " pj_ioqueue_write.."));
- bytes = items[i].buffer_size;
- rc = pj_ioqueue_send(items[i].client_key, &items[i].recv_op,
- items[i].outgoing_buffer, &bytes, 0);
- if (rc != PJ_SUCCESS && rc != PJ_EPENDING) {
- app_perror("...error: pj_ioqueue_write", rc);
- return -76;
- }
-
- items[i].has_pending_send = (rc==PJ_EPENDING);
- }
-
- /* Create the threads. */
- for (i=0; i<thread_cnt; ++i) {
- rc = pj_thread_create( pool, NULL,
- &worker_thread,
- ioqueue,
- PJ_THREAD_DEFAULT_STACK_SIZE,
- PJ_THREAD_SUSPENDED, &thread[i] );
- if (rc != PJ_SUCCESS) {
- app_perror("...error: unable to create thread", rc);
- return -80;
- }
- }
-
- /* Mark start time. */
- rc = pj_get_timestamp(&start);
- if (rc != PJ_SUCCESS)
- return -90;
-
- /* Start the thread. */
- TRACE_((THIS_FILE, " resuming all threads.."));
- for (i=0; i<thread_cnt; ++i) {
- rc = pj_thread_resume(thread[i]);
- if (rc != 0)
- return -100;
- }
-
- /* Wait for MSEC_DURATION seconds.
- * This should be as simple as pj_thread_sleep(MSEC_DURATION) actually,
- * but unfortunately it doesn't work when system doesn't employ
- * timeslicing for threads.
- */
- TRACE_((THIS_FILE, " wait for few seconds.."));
- do {
- pj_thread_sleep(1);
-
- /* Mark end time. */
- rc = pj_get_timestamp(&stop);
-
- if (thread_quit_flag) {
- TRACE_((THIS_FILE, " transfer limit reached.."));
- break;
- }
-
- if (pj_elapsed_usec(&start,&stop)<MSEC_DURATION * 1000) {
- TRACE_((THIS_FILE, " time limit reached.."));
- break;
- }
-
- } while (1);
-
- /* Terminate all threads. */
- TRACE_((THIS_FILE, " terminating all threads.."));
- thread_quit_flag = 1;
-
- for (i=0; i<thread_cnt; ++i) {
- TRACE_((THIS_FILE, " join thread %d..", i));
- pj_thread_join(thread[i]);
- pj_thread_destroy(thread[i]);
- }
-
- /* Close all sockets. */
- TRACE_((THIS_FILE, " closing all sockets.."));
- for (i=0; i<sockpair_cnt; ++i) {
- pj_ioqueue_unregister(items[i].server_key);
- pj_ioqueue_unregister(items[i].client_key);
- pj_sock_close(items[i].server_fd);
- pj_sock_close(items[i].client_fd);
- }
-
- /* Destroy ioqueue. */
- TRACE_((THIS_FILE, " destroying ioqueue.."));
- pj_ioqueue_destroy(ioqueue);
-
- /* Calculate actual time in usec. */
- total_elapsed_usec = pj_elapsed_usec(&start, &stop);
-
- /* Calculate total bytes received. */
- total_received = 0;
- for (i=0; i<sockpair_cnt; ++i) {
- total_received = items[i].bytes_recv;
- }
-
- /* bandwidth = total_received*1000/total_elapsed_usec */
- bandwidth = total_received;
- pj_highprec_mul(bandwidth, 1000);
- pj_highprec_div(bandwidth, total_elapsed_usec);
-
- *p_bandwidth = (pj_uint32_t)bandwidth;
-
- PJ_LOG(3,(THIS_FILE, " %.4s %d %d %3d us %8d KB/s",
- type_name, thread_cnt, sockpair_cnt,
- -1 /*total_elapsed_usec/sockpair_cnt*/,
- *p_bandwidth));
-
- /* Done. */
- pj_pool_release(pool);
-
- TRACE_((THIS_FILE, " done.."));
- return 0;
-}
-
-/*
- * main test entry.
- */
-int ioqueue_perf_test(void)
-{
- enum { BUF_SIZE = 512 };
- int i, rc;
- struct {
- int type;
- const char *type_name;
- int thread_cnt;
- int sockpair_cnt;
- } test_param[] =
- {
- { PJ_SOCK_DGRAM, "udp", 1, 1},
- { PJ_SOCK_DGRAM, "udp", 1, 2},
- { PJ_SOCK_DGRAM, "udp", 1, 4},
- { PJ_SOCK_DGRAM, "udp", 1, 8},
- { PJ_SOCK_DGRAM, "udp", 2, 1},
- { PJ_SOCK_DGRAM, "udp", 2, 2},
- { PJ_SOCK_DGRAM, "udp", 2, 4},
- { PJ_SOCK_DGRAM, "udp", 2, 8},
- { PJ_SOCK_DGRAM, "udp", 4, 1},
- { PJ_SOCK_DGRAM, "udp", 4, 2},
- { PJ_SOCK_DGRAM, "udp", 4, 4},
- { PJ_SOCK_DGRAM, "udp", 4, 8},
- { PJ_SOCK_STREAM, "tcp", 1, 1},
- { PJ_SOCK_STREAM, "tcp", 1, 2},
- { PJ_SOCK_STREAM, "tcp", 1, 4},
- { PJ_SOCK_STREAM, "tcp", 1, 8},
- { PJ_SOCK_STREAM, "tcp", 2, 1},
- { PJ_SOCK_STREAM, "tcp", 2, 2},
- { PJ_SOCK_STREAM, "tcp", 2, 4},
- { PJ_SOCK_STREAM, "tcp", 2, 8},
- { PJ_SOCK_STREAM, "tcp", 4, 1},
- { PJ_SOCK_STREAM, "tcp", 4, 2},
- { PJ_SOCK_STREAM, "tcp", 4, 4},
- { PJ_SOCK_STREAM, "tcp", 4, 8},
- };
- pj_size_t best_bandwidth;
- int best_index = 0;
-
- PJ_LOG(3,(THIS_FILE, " Benchmarking %s ioqueue:", pj_ioqueue_name()));
- PJ_LOG(3,(THIS_FILE, " ==============================================="));
- PJ_LOG(3,(THIS_FILE, " Type Threads Skt.Pairs Avg.Time Bandwidth"));
- PJ_LOG(3,(THIS_FILE, " ==============================================="));
-
- best_bandwidth = 0;
- for (i=0; i<sizeof(test_param)/sizeof(test_param[0]); ++i) {
- pj_size_t bandwidth;
-
- rc = perform_test(test_param[i].type,
- test_param[i].type_name,
- test_param[i].thread_cnt,
- test_param[i].sockpair_cnt,
- BUF_SIZE,
- &bandwidth);
- if (rc != 0)
- return rc;
-
- if (bandwidth > best_bandwidth)
- best_bandwidth = bandwidth, best_index = i;
-
- /* Give it a rest before next test. */
- pj_thread_sleep(500);
- }
-
- PJ_LOG(3,(THIS_FILE,
- " Best: Type=%s Threads=%d, Skt.Pairs=%d, Bandwidth=%u KB/s",
- test_param[best_index].type_name,
- test_param[best_index].thread_cnt,
- test_param[best_index].sockpair_cnt,
- best_bandwidth));
- PJ_LOG(3,(THIS_FILE, " (Note: packet size=%d, total errors=%u)",
- BUF_SIZE, last_error_counter));
- return 0;
-}
-
-#else
-/* To prevent warning about "translation unit is empty"
- * when this test is disabled.
- */
-int dummy_uiq_perf_test;
-#endif /* INCLUDE_IOQUEUE_PERF_TEST */
-
-
+/* $Id$ */
+/*
+ * Copyright (C)2003-2006 Benny Prijono <benny@prijono.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#include "test.h"
+#include <pjlib.h>
+#include <pj/compat/high_precision.h>
+
+/**
+ * \page page_pjlib_ioqueue_perf_test Test: I/O Queue Performance
+ *
+ * Test the performance of the I/O queue, using typical producer
+ * consumer test. The test should examine the effect of using multiple
+ * threads on the performance.
+ *
+ * This file is <b>pjlib-test/ioq_perf.c</b>
+ *
+ * \include pjlib-test/ioq_perf.c
+ */
+
+#if INCLUDE_IOQUEUE_PERF_TEST
+
+#ifdef _MSC_VER
+# pragma warning ( disable: 4204) // non-constant aggregate initializer
+#endif
+
+#define THIS_FILE "ioq_perf"
+//#define TRACE_(expr) PJ_LOG(3,expr)
+#define TRACE_(expr)
+
+
+static pj_bool_t thread_quit_flag;
+static pj_status_t last_error;
+static unsigned last_error_counter;
+
+/* Descriptor for each producer/consumer pair. */
+typedef struct test_item
+{
+ pj_sock_t server_fd,
+ client_fd;
+ pj_ioqueue_t *ioqueue;
+ pj_ioqueue_key_t *server_key,
+ *client_key;
+ pj_ioqueue_op_key_t recv_op,
+ send_op;
+ int has_pending_send;
+ pj_size_t buffer_size;
+ char *outgoing_buffer;
+ char *incoming_buffer;
+ pj_size_t bytes_sent,
+ bytes_recv;
+} test_item;
+
+/* Callback when data has been read.
+ * Increment item->bytes_recv and ready to read the next data.
+ */
+static void on_read_complete(pj_ioqueue_key_t *key,
+ pj_ioqueue_op_key_t *op_key,
+ pj_ssize_t bytes_read)
+{
+ test_item *item = pj_ioqueue_get_user_data(key);
+ pj_status_t rc;
+ int data_is_available = 1;
+
+ //TRACE_((THIS_FILE, " read complete, bytes_read=%d", bytes_read));
+
+ do {
+ if (thread_quit_flag)
+ return;
+
+ if (bytes_read < 0) {
+ pj_status_t rc = -bytes_read;
+ char errmsg[128];
+
+ if (rc != last_error) {
+ //last_error = rc;
+ pj_strerror(rc, errmsg, sizeof(errmsg));
+ PJ_LOG(3,(THIS_FILE, "...error: read error, bytes_read=%d (%s)",
+ bytes_read, errmsg));
+ PJ_LOG(3,(THIS_FILE,
+ ".....additional info: total read=%u, total sent=%u",
+ item->bytes_recv, item->bytes_sent));
+ } else {
+ last_error_counter++;
+ }
+ bytes_read = 0;
+
+ } else if (bytes_read == 0) {
+ PJ_LOG(3,(THIS_FILE, "...socket has closed!"));
+ }
+
+ item->bytes_recv += bytes_read;
+
+ /* To assure that the test quits, even if main thread
+ * doesn't have time to run.
+ */
+ if (item->bytes_recv > item->buffer_size * 10000)
+ thread_quit_flag = 1;
+
+ bytes_read = item->buffer_size;
+ rc = pj_ioqueue_recv( key, op_key,
+ item->incoming_buffer, &bytes_read, 0 );
+
+ if (rc == PJ_SUCCESS) {
+ data_is_available = 1;
+ } else if (rc == PJ_EPENDING) {
+ data_is_available = 0;
+ } else {
+ data_is_available = 0;
+ if (rc != last_error) {
+ last_error = rc;
+ app_perror("...error: read error(1)", rc);
+ } else {
+ last_error_counter++;
+ }
+ }
+
+ if (!item->has_pending_send) {
+ pj_ssize_t sent = item->buffer_size;
+ rc = pj_ioqueue_send(item->client_key, &item->send_op,
+ item->outgoing_buffer, &sent, 0);
+ if (rc != PJ_SUCCESS && rc != PJ_EPENDING) {
+ app_perror("...error: write error", rc);
+ }
+
+ item->has_pending_send = (rc==PJ_EPENDING);
+ }
+
+ } while (data_is_available);
+}
+
+/* Callback when data has been written.
+ * Increment item->bytes_sent and write the next data.
+ */
+static void on_write_complete(pj_ioqueue_key_t *key,
+ pj_ioqueue_op_key_t *op_key,
+ pj_ssize_t bytes_sent)
+{
+ test_item *item = pj_ioqueue_get_user_data(key);
+
+ //TRACE_((THIS_FILE, " write complete: sent = %d", bytes_sent));
+
+ if (thread_quit_flag)
+ return;
+
+ item->has_pending_send = 0;
+ item->bytes_sent += bytes_sent;
+
+ if (bytes_sent <= 0) {
+ PJ_LOG(3,(THIS_FILE, "...error: sending stopped. bytes_sent=%d",
+ bytes_sent));
+ }
+ else {
+ pj_status_t rc;
+
+ bytes_sent = item->buffer_size;
+ rc = pj_ioqueue_send( item->client_key, op_key,
+ item->outgoing_buffer, &bytes_sent, 0);
+ if (rc != PJ_SUCCESS && rc != PJ_EPENDING) {
+ app_perror("...error: write error", rc);
+ }
+
+ item->has_pending_send = (rc==PJ_EPENDING);
+ }
+}
+
+/* The worker thread. */
+static int worker_thread(void *arg)
+{
+ pj_ioqueue_t *ioqueue = arg;
+ const pj_time_val timeout = {0, 100};
+ int rc;
+
+ while (!thread_quit_flag) {
+ rc = pj_ioqueue_poll(ioqueue, &timeout);
+ //TRACE_((THIS_FILE, " thread: poll returned rc=%d", rc));
+ if (rc < 0) {
+ app_perror("...error in pj_ioqueue_poll()", pj_get_netos_error());
+ return -1;
+ }
+ }
+ return 0;
+}
+
+/* Calculate the bandwidth for the specific test configuration.
+ * The test is simple:
+ * - create sockpair_cnt number of producer-consumer socket pair.
+ * - create thread_cnt number of worker threads.
+ * - each producer will send buffer_size bytes data as fast and
+ * as soon as it can.
+ * - each consumer will read buffer_size bytes of data as fast
+ * as it could.
+ * - measure the total bytes received by all consumers during a
+ * period of time.
+ */
+static int perform_test(int sock_type, const char *type_name,
+ unsigned thread_cnt, unsigned sockpair_cnt,
+ pj_size_t buffer_size,
+ pj_size_t *p_bandwidth)
+{
+ enum { MSEC_DURATION = 5000 };
+ pj_pool_t *pool;
+ test_item *items;
+ pj_thread_t **thread;
+ pj_ioqueue_t *ioqueue;
+ pj_status_t rc;
+ pj_ioqueue_callback ioqueue_callback;
+ pj_uint32_t total_elapsed_usec, total_received;
+ pj_highprec_t bandwidth;
+ pj_timestamp start, stop;
+ unsigned i;
+
+ TRACE_((THIS_FILE, " starting test.."));
+
+ ioqueue_callback.on_read_complete = &on_read_complete;
+ ioqueue_callback.on_write_complete = &on_write_complete;
+
+ thread_quit_flag = 0;
+
+ pool = pj_pool_create(mem, NULL, 4096, 4096, NULL);
+ if (!pool)
+ return -10;
+
+ items = pj_pool_alloc(pool, sockpair_cnt*sizeof(test_item));
+ thread = pj_pool_alloc(pool, thread_cnt*sizeof(pj_thread_t*));
+
+ TRACE_((THIS_FILE, " creating ioqueue.."));
+ rc = pj_ioqueue_create(pool, sockpair_cnt*2, &ioqueue);
+ if (rc != PJ_SUCCESS) {
+ app_perror("...error: unable to create ioqueue", rc);
+ return -15;
+ }
+
+ /* Initialize each producer-consumer pair. */
+ for (i=0; i<sockpair_cnt; ++i) {
+ pj_ssize_t bytes;
+
+ items[i].ioqueue = ioqueue;
+ items[i].buffer_size = buffer_size;
+ items[i].outgoing_buffer = pj_pool_alloc(pool, buffer_size);
+ items[i].incoming_buffer = pj_pool_alloc(pool, buffer_size);
+ items[i].bytes_recv = items[i].bytes_sent = 0;
+
+ /* randomize outgoing buffer. */
+ pj_create_random_string(items[i].outgoing_buffer, buffer_size);
+
+ /* Create socket pair. */
+ TRACE_((THIS_FILE, " calling socketpair.."));
+ rc = app_socketpair(PJ_AF_INET, sock_type, 0,
+ &items[i].server_fd, &items[i].client_fd);
+ if (rc != PJ_SUCCESS) {
+ app_perror("...error: unable to create socket pair", rc);
+ return -20;
+ }
+
+ /* Register server socket to ioqueue. */
+ TRACE_((THIS_FILE, " register(1).."));
+ rc = pj_ioqueue_register_sock(pool, ioqueue,
+ items[i].server_fd,
+ &items[i], &ioqueue_callback,
+ &items[i].server_key);
+ if (rc != PJ_SUCCESS) {
+ app_perror("...error: registering server socket to ioqueue", rc);
+ return -60;
+ }
+
+ /* Register client socket to ioqueue. */
+ TRACE_((THIS_FILE, " register(2).."));
+ rc = pj_ioqueue_register_sock(pool, ioqueue,
+ items[i].client_fd,
+ &items[i], &ioqueue_callback,
+ &items[i].client_key);
+ if (rc != PJ_SUCCESS) {
+ app_perror("...error: registering server socket to ioqueue", rc);
+ return -70;
+ }
+
+ /* Start reading. */
+ TRACE_((THIS_FILE, " pj_ioqueue_recv.."));
+ bytes = items[i].buffer_size;
+ rc = pj_ioqueue_recv(items[i].server_key, &items[i].recv_op,
+ items[i].incoming_buffer, &bytes,
+ 0);
+ if (rc != PJ_EPENDING) {
+ app_perror("...error: pj_ioqueue_recv", rc);
+ return -73;
+ }
+
+ /* Start writing. */
+ TRACE_((THIS_FILE, " pj_ioqueue_write.."));
+ bytes = items[i].buffer_size;
+ rc = pj_ioqueue_send(items[i].client_key, &items[i].recv_op,
+ items[i].outgoing_buffer, &bytes, 0);
+ if (rc != PJ_SUCCESS && rc != PJ_EPENDING) {
+ app_perror("...error: pj_ioqueue_write", rc);
+ return -76;
+ }
+
+ items[i].has_pending_send = (rc==PJ_EPENDING);
+ }
+
+ /* Create the threads. */
+ for (i=0; i<thread_cnt; ++i) {
+ rc = pj_thread_create( pool, NULL,
+ &worker_thread,
+ ioqueue,
+ PJ_THREAD_DEFAULT_STACK_SIZE,
+ PJ_THREAD_SUSPENDED, &thread[i] );
+ if (rc != PJ_SUCCESS) {
+ app_perror("...error: unable to create thread", rc);
+ return -80;
+ }
+ }
+
+ /* Mark start time. */
+ rc = pj_get_timestamp(&start);
+ if (rc != PJ_SUCCESS)
+ return -90;
+
+ /* Start the thread. */
+ TRACE_((THIS_FILE, " resuming all threads.."));
+ for (i=0; i<thread_cnt; ++i) {
+ rc = pj_thread_resume(thread[i]);
+ if (rc != 0)
+ return -100;
+ }
+
+ /* Wait for MSEC_DURATION seconds.
+ * This should be as simple as pj_thread_sleep(MSEC_DURATION) actually,
+ * but unfortunately it doesn't work when system doesn't employ
+ * timeslicing for threads.
+ */
+ TRACE_((THIS_FILE, " wait for few seconds.."));
+ do {
+ pj_thread_sleep(1);
+
+ /* Mark end time. */
+ rc = pj_get_timestamp(&stop);
+
+ if (thread_quit_flag) {
+ TRACE_((THIS_FILE, " transfer limit reached.."));
+ break;
+ }
+
+ if (pj_elapsed_usec(&start,&stop)<MSEC_DURATION * 1000) {
+ TRACE_((THIS_FILE, " time limit reached.."));
+ break;
+ }
+
+ } while (1);
+
+ /* Terminate all threads. */
+ TRACE_((THIS_FILE, " terminating all threads.."));
+ thread_quit_flag = 1;
+
+ for (i=0; i<thread_cnt; ++i) {
+ TRACE_((THIS_FILE, " join thread %d..", i));
+ pj_thread_join(thread[i]);
+ pj_thread_destroy(thread[i]);
+ }
+
+ /* Close all sockets. */
+ TRACE_((THIS_FILE, " closing all sockets.."));
+ for (i=0; i<sockpair_cnt; ++i) {
+ pj_ioqueue_unregister(items[i].server_key);
+ pj_ioqueue_unregister(items[i].client_key);
+ pj_sock_close(items[i].server_fd);
+ pj_sock_close(items[i].client_fd);
+ }
+
+ /* Destroy ioqueue. */
+ TRACE_((THIS_FILE, " destroying ioqueue.."));
+ pj_ioqueue_destroy(ioqueue);
+
+ /* Calculate actual time in usec. */
+ total_elapsed_usec = pj_elapsed_usec(&start, &stop);
+
+ /* Calculate total bytes received. */
+ total_received = 0;
+ for (i=0; i<sockpair_cnt; ++i) {
+ total_received = items[i].bytes_recv;
+ }
+
+ /* bandwidth = total_received*1000/total_elapsed_usec */
+ bandwidth = total_received;
+ pj_highprec_mul(bandwidth, 1000);
+ pj_highprec_div(bandwidth, total_elapsed_usec);
+
+ *p_bandwidth = (pj_uint32_t)bandwidth;
+
+ PJ_LOG(3,(THIS_FILE, " %.4s %d %d %3d us %8d KB/s",
+ type_name, thread_cnt, sockpair_cnt,
+ -1 /*total_elapsed_usec/sockpair_cnt*/,
+ *p_bandwidth));
+
+ /* Done. */
+ pj_pool_release(pool);
+
+ TRACE_((THIS_FILE, " done.."));
+ return 0;
+}
+
+/*
+ * main test entry.
+ */
+int ioqueue_perf_test(void)
+{
+ enum { BUF_SIZE = 512 };
+ int i, rc;
+ struct {
+ int type;
+ const char *type_name;
+ int thread_cnt;
+ int sockpair_cnt;
+ } test_param[] =
+ {
+ { PJ_SOCK_DGRAM, "udp", 1, 1},
+ { PJ_SOCK_DGRAM, "udp", 1, 2},
+ { PJ_SOCK_DGRAM, "udp", 1, 4},
+ { PJ_SOCK_DGRAM, "udp", 1, 8},
+ { PJ_SOCK_DGRAM, "udp", 2, 1},
+ { PJ_SOCK_DGRAM, "udp", 2, 2},
+ { PJ_SOCK_DGRAM, "udp", 2, 4},
+ { PJ_SOCK_DGRAM, "udp", 2, 8},
+ { PJ_SOCK_DGRAM, "udp", 4, 1},
+ { PJ_SOCK_DGRAM, "udp", 4, 2},
+ { PJ_SOCK_DGRAM, "udp", 4, 4},
+ { PJ_SOCK_DGRAM, "udp", 4, 8},
+ { PJ_SOCK_STREAM, "tcp", 1, 1},
+ { PJ_SOCK_STREAM, "tcp", 1, 2},
+ { PJ_SOCK_STREAM, "tcp", 1, 4},
+ { PJ_SOCK_STREAM, "tcp", 1, 8},
+ { PJ_SOCK_STREAM, "tcp", 2, 1},
+ { PJ_SOCK_STREAM, "tcp", 2, 2},
+ { PJ_SOCK_STREAM, "tcp", 2, 4},
+ { PJ_SOCK_STREAM, "tcp", 2, 8},
+ { PJ_SOCK_STREAM, "tcp", 4, 1},
+ { PJ_SOCK_STREAM, "tcp", 4, 2},
+ { PJ_SOCK_STREAM, "tcp", 4, 4},
+ { PJ_SOCK_STREAM, "tcp", 4, 8},
+ };
+ pj_size_t best_bandwidth;
+ int best_index = 0;
+
+ PJ_LOG(3,(THIS_FILE, " Benchmarking %s ioqueue:", pj_ioqueue_name()));
+ PJ_LOG(3,(THIS_FILE, " ==============================================="));
+ PJ_LOG(3,(THIS_FILE, " Type Threads Skt.Pairs Avg.Time Bandwidth"));
+ PJ_LOG(3,(THIS_FILE, " ==============================================="));
+
+ best_bandwidth = 0;
+ for (i=0; i<sizeof(test_param)/sizeof(test_param[0]); ++i) {
+ pj_size_t bandwidth;
+
+ rc = perform_test(test_param[i].type,
+ test_param[i].type_name,
+ test_param[i].thread_cnt,
+ test_param[i].sockpair_cnt,
+ BUF_SIZE,
+ &bandwidth);
+ if (rc != 0)
+ return rc;
+
+ if (bandwidth > best_bandwidth)
+ best_bandwidth = bandwidth, best_index = i;
+
+ /* Give it a rest before next test. */
+ pj_thread_sleep(500);
+ }
+
+ PJ_LOG(3,(THIS_FILE,
+ " Best: Type=%s Threads=%d, Skt.Pairs=%d, Bandwidth=%u KB/s",
+ test_param[best_index].type_name,
+ test_param[best_index].thread_cnt,
+ test_param[best_index].sockpair_cnt,
+ best_bandwidth));
+ PJ_LOG(3,(THIS_FILE, " (Note: packet size=%d, total errors=%u)",
+ BUF_SIZE, last_error_counter));
+ return 0;
+}
+
+#else
+/* To prevent warning about "translation unit is empty"
+ * when this test is disabled.
+ */
+int dummy_uiq_perf_test;
+#endif /* INCLUDE_IOQUEUE_PERF_TEST */
+
+
diff --git a/pjlib/src/pjlib-test/ioq_tcp.c b/pjlib/src/pjlib-test/ioq_tcp.c
index ade37ec2..c60d614f 100644
--- a/pjlib/src/pjlib-test/ioq_tcp.c
+++ b/pjlib/src/pjlib-test/ioq_tcp.c
@@ -1,540 +1,540 @@
-/* $Id$ */
-/*
- * Copyright (C)2003-2006 Benny Prijono <benny@prijono.org>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
-#include "test.h"
-
-/**
- * \page page_pjlib_ioqueue_tcp_test Test: I/O Queue (TCP)
- *
- * This file provides implementation to test the
- * functionality of the I/O queue when TCP socket is used.
- *
- *
- * This file is <b>pjlib-test/ioq_tcp.c</b>
- *
- * \include pjlib-test/ioq_tcp.c
- */
-
-
-#if INCLUDE_TCP_IOQUEUE_TEST
-
-#include <pjlib.h>
-
-#if PJ_HAS_TCP
-
-#define THIS_FILE "test_tcp"
-#define PORT 50000
-#define NON_EXISTANT_PORT 50123
-#define LOOP 100
-#define BUF_MIN_SIZE 32
-#define BUF_MAX_SIZE 2048
-#define SOCK_INACTIVE_MIN (4-2)
-#define SOCK_INACTIVE_MAX (PJ_IOQUEUE_MAX_HANDLES - 2)
-#define POOL_SIZE (2*BUF_MAX_SIZE + SOCK_INACTIVE_MAX*128 + 2048)
-
-static pj_ssize_t callback_read_size,
- callback_write_size,
- callback_accept_status,
- callback_connect_status;
-static pj_ioqueue_key_t *callback_read_key,
- *callback_write_key,
- *callback_accept_key,
- *callback_connect_key;
-static pj_ioqueue_op_key_t *callback_read_op,
- *callback_write_op,
- *callback_accept_op;
-
-static void on_ioqueue_read(pj_ioqueue_key_t *key,
- pj_ioqueue_op_key_t *op_key,
- pj_ssize_t bytes_read)
-{
- callback_read_key = key;
- callback_read_op = op_key;
- callback_read_size = bytes_read;
-}
-
-static void on_ioqueue_write(pj_ioqueue_key_t *key,
- pj_ioqueue_op_key_t *op_key,
- pj_ssize_t bytes_written)
-{
- callback_write_key = key;
- callback_write_op = op_key;
- callback_write_size = bytes_written;
-}
-
-static void on_ioqueue_accept(pj_ioqueue_key_t *key,
- pj_ioqueue_op_key_t *op_key,
- pj_sock_t sock,
- int status)
-{
- PJ_UNUSED_ARG(sock);
-
- callback_accept_key = key;
- callback_accept_op = op_key;
- callback_accept_status = status;
-}
-
-static void on_ioqueue_connect(pj_ioqueue_key_t *key, int status)
-{
- callback_connect_key = key;
- callback_connect_status = status;
-}
-
-static pj_ioqueue_callback test_cb =
-{
- &on_ioqueue_read,
- &on_ioqueue_write,
- &on_ioqueue_accept,
- &on_ioqueue_connect,
-};
-
-static int send_recv_test(pj_ioqueue_t *ioque,
- pj_ioqueue_key_t *skey,
- pj_ioqueue_key_t *ckey,
- void *send_buf,
- void *recv_buf,
- pj_ssize_t bufsize,
- pj_timestamp *t_elapsed)
-{
- pj_status_t status;
- pj_ssize_t bytes;
- pj_time_val timeout;
- pj_timestamp t1, t2;
- int pending_op = 0;
- pj_ioqueue_op_key_t read_op, write_op;
-
- // Start reading on the server side.
- bytes = bufsize;
- status = pj_ioqueue_recv(skey, &read_op, recv_buf, &bytes, 0);
- if (status != PJ_SUCCESS && status != PJ_EPENDING) {
- app_perror("...pj_ioqueue_recv error", status);
- return -100;
- }
-
- if (status == PJ_EPENDING)
- ++pending_op;
- else {
- /* Does not expect to return error or immediate data. */
- return -115;
- }
-
- // Randomize send buffer.
- pj_create_random_string((char*)send_buf, bufsize);
-
- // Starts send on the client side.
- bytes = bufsize;
- status = pj_ioqueue_send(ckey, &write_op, send_buf, &bytes, 0);
- if (status != PJ_SUCCESS && bytes != PJ_EPENDING) {
- return -120;
- }
- if (status == PJ_EPENDING) {
- ++pending_op;
- }
-
- // Begin time.
- pj_get_timestamp(&t1);
-
- // Reset indicators
- callback_read_size = callback_write_size = 0;
- callback_read_key = callback_write_key = NULL;
- callback_read_op = callback_write_op = NULL;
-
- // Poll the queue until we've got completion event in the server side.
- status = 0;
- while (pending_op > 0) {
- timeout.sec = 1; timeout.msec = 0;
- status = pj_ioqueue_poll(ioque, &timeout);
- if (status > 0) {
- if (callback_read_size) {
- if (callback_read_size != bufsize)
- return -160;
- if (callback_read_key != skey)
- return -161;
- if (callback_read_op != &read_op)
- return -162;
- }
- if (callback_write_size) {
- if (callback_write_key != ckey)
- return -163;
- if (callback_write_op != &write_op)
- return -164;
- }
- pending_op -= status;
- }
- if (status == 0) {
- PJ_LOG(3,("", "...error: timed out"));
- }
- if (status < 0) {
- return -170;
- }
- }
-
- // Pending op is zero.
- // Subsequent poll should yield zero too.
- timeout.sec = timeout.msec = 0;
- status = pj_ioqueue_poll(ioque, &timeout);
- if (status != 0)
- return -173;
-
- // End time.
- pj_get_timestamp(&t2);
- t_elapsed->u32.lo += (t2.u32.lo - t1.u32.lo);
-
- // Compare recv buffer with send buffer.
- if (pj_memcmp(send_buf, recv_buf, bufsize) != 0) {
- return -180;
- }
-
- // Success
- return 0;
-}
-
-
-/*
- * Compliance test for success scenario.
- */
-static int compliance_test_0(void)
-{
- pj_sock_t ssock=-1, csock0=-1, csock1=-1;
- pj_sockaddr_in addr, client_addr, rmt_addr;
- int client_addr_len;
- pj_pool_t *pool = NULL;
- char *send_buf, *recv_buf;
- pj_ioqueue_t *ioque = NULL;
- pj_ioqueue_key_t *skey, *ckey0, *ckey1;
- pj_ioqueue_op_key_t accept_op;
- int bufsize = BUF_MIN_SIZE;
- pj_ssize_t status = -1;
- int pending_op = 0;
- pj_timestamp t_elapsed;
- pj_str_t s;
- pj_status_t rc;
-
- // Create pool.
- pool = pj_pool_create(mem, NULL, POOL_SIZE, 4000, NULL);
-
- // Allocate buffers for send and receive.
- send_buf = (char*)pj_pool_alloc(pool, bufsize);
- recv_buf = (char*)pj_pool_alloc(pool, bufsize);
-
- // Create server socket and client socket for connecting
- rc = pj_sock_socket(PJ_AF_INET, PJ_SOCK_STREAM, 0, &ssock);
- if (rc != PJ_SUCCESS) {
- app_perror("...error creating socket", rc);
- status=-1; goto on_error;
- }
-
- rc = pj_sock_socket(PJ_AF_INET, PJ_SOCK_STREAM, 0, &csock1);
- if (rc != PJ_SUCCESS) {
- app_perror("...error creating socket", rc);
- status=-1; goto on_error;
- }
-
- // Bind server socket.
- memset(&addr, 0, sizeof(addr));
- addr.sin_family = PJ_AF_INET;
- addr.sin_port = pj_htons(PORT);
- if (pj_sock_bind(ssock, &addr, sizeof(addr))) {
- app_perror("...bind error", rc);
- status=-10; goto on_error;
- }
-
- // Create I/O Queue.
- rc = pj_ioqueue_create(pool, PJ_IOQUEUE_MAX_HANDLES, &ioque);
- if (rc != PJ_SUCCESS) {
- app_perror("...ERROR in pj_ioqueue_create()", rc);
- status=-20; goto on_error;
- }
-
- // Register server socket and client socket.
- rc = pj_ioqueue_register_sock(pool, ioque, ssock, NULL, &test_cb, &skey);
- if (rc == PJ_SUCCESS)
- rc = pj_ioqueue_register_sock(pool, ioque, csock1, NULL, &test_cb,
- &ckey1);
- else
- ckey1 = NULL;
- if (rc != PJ_SUCCESS) {
- app_perror("...ERROR in pj_ioqueue_register_sock()", rc);
- status=-23; goto on_error;
- }
-
- // Server socket listen().
- if (pj_sock_listen(ssock, 5)) {
- app_perror("...ERROR in pj_sock_listen()", rc);
- status=-25; goto on_error;
- }
-
- // Server socket accept()
- client_addr_len = sizeof(pj_sockaddr_in);
- status = pj_ioqueue_accept(skey, &accept_op, &csock0,
- &client_addr, &rmt_addr, &client_addr_len);
- if (status != PJ_EPENDING) {
- app_perror("...ERROR in pj_ioqueue_accept()", rc);
- status=-30; goto on_error;
- }
- if (status==PJ_EPENDING) {
- ++pending_op;
- }
-
- // Initialize remote address.
- memset(&addr, 0, sizeof(addr));
- addr.sin_family = PJ_AF_INET;
- addr.sin_port = pj_htons(PORT);
- addr.sin_addr = pj_inet_addr(pj_cstr(&s, "127.0.0.1"));
-
- // Client socket connect()
- status = pj_ioqueue_connect(ckey1, &addr, sizeof(addr));
- if (status!=PJ_SUCCESS && status != PJ_EPENDING) {
- app_perror("...ERROR in pj_ioqueue_connect()", rc);
- status=-40; goto on_error;
- }
- if (status==PJ_EPENDING) {
- ++pending_op;
- }
-
- // Poll until connected
- callback_read_size = callback_write_size = 0;
- callback_accept_status = callback_connect_status = -2;
-
- callback_read_key = callback_write_key =
- callback_accept_key = callback_connect_key = NULL;
- callback_accept_op = callback_read_op = callback_write_op = NULL;
-
- while (pending_op) {
- pj_time_val timeout = {1, 0};
-
- status=pj_ioqueue_poll(ioque, &timeout);
- if (status > 0) {
- if (callback_accept_status != -2) {
- if (callback_accept_status != 0) {
- status=-41; goto on_error;
- }
- if (callback_accept_key != skey) {
- status=-42; goto on_error;
- }
- if (callback_accept_op != &accept_op) {
- status=-43; goto on_error;
- }
- callback_accept_status = -2;
- }
-
- if (callback_connect_status != -2) {
- if (callback_connect_status != 0) {
- status=-50; goto on_error;
- }
- if (callback_connect_key != ckey1) {
- status=-51; goto on_error;
- }
- callback_connect_status = -2;
- }
-
- pending_op -= status;
-
- if (pending_op == 0) {
- status = 0;
- }
- }
- }
-
- // There's no pending operation.
- // When we poll the ioqueue, there must not be events.
- if (pending_op == 0) {
- pj_time_val timeout = {1, 0};
- status = pj_ioqueue_poll(ioque, &timeout);
- if (status != 0) {
- status=-60; goto on_error;
- }
- }
-
- // Check accepted socket.
- if (csock0 == PJ_INVALID_SOCKET) {
- status = -69;
- app_perror("...accept() error", pj_get_os_error());
- goto on_error;
- }
-
- // Register newly accepted socket.
- rc = pj_ioqueue_register_sock(pool, ioque, csock0, NULL,
- &test_cb, &ckey0);
- if (rc != PJ_SUCCESS) {
- app_perror("...ERROR in pj_ioqueue_register_sock", rc);
- status = -70;
- goto on_error;
- }
-
- // Test send and receive.
- t_elapsed.u32.lo = 0;
- status = send_recv_test(ioque, ckey0, ckey1, send_buf,
- recv_buf, bufsize, &t_elapsed);
- if (status != 0) {
- goto on_error;
- }
-
- // Success
- status = 0;
-
-on_error:
- if (ssock != PJ_INVALID_SOCKET)
- pj_sock_close(ssock);
- if (csock1 != PJ_INVALID_SOCKET)
- pj_sock_close(csock1);
- if (csock0 != PJ_INVALID_SOCKET)
- pj_sock_close(csock0);
- if (ioque != NULL)
- pj_ioqueue_destroy(ioque);
- pj_pool_release(pool);
- return status;
-
-}
-
-/*
- * Compliance test for failed scenario.
- * In this case, the client connects to a non-existant service.
- */
-static int compliance_test_1(void)
-{
- pj_sock_t csock1=-1;
- pj_sockaddr_in addr;
- pj_pool_t *pool = NULL;
- pj_ioqueue_t *ioque = NULL;
- pj_ioqueue_key_t *ckey1;
- pj_ssize_t status = -1;
- int pending_op = 0;
- pj_str_t s;
- pj_status_t rc;
-
- // Create pool.
- pool = pj_pool_create(mem, NULL, POOL_SIZE, 4000, NULL);
-
- // Create I/O Queue.
- rc = pj_ioqueue_create(pool, PJ_IOQUEUE_MAX_HANDLES, &ioque);
- if (!ioque) {
- status=-20; goto on_error;
- }
-
- // Create client socket
- rc = pj_sock_socket(PJ_AF_INET, PJ_SOCK_STREAM, 0, &csock1);
- if (rc != PJ_SUCCESS) {
- app_perror("...ERROR in pj_sock_socket()", rc);
- status=-1; goto on_error;
- }
-
- // Register client socket.
- rc = pj_ioqueue_register_sock(pool, ioque, csock1, NULL,
- &test_cb, &ckey1);
- if (rc != PJ_SUCCESS) {
- app_perror("...ERROR in pj_ioqueue_register_sock()", rc);
- status=-23; goto on_error;
- }
-
- // Initialize remote address.
- memset(&addr, 0, sizeof(addr));
- addr.sin_family = PJ_AF_INET;
- addr.sin_port = pj_htons(NON_EXISTANT_PORT);
- addr.sin_addr = pj_inet_addr(pj_cstr(&s, "127.0.0.1"));
-
- // Client socket connect()
- status = pj_ioqueue_connect(ckey1, &addr, sizeof(addr));
- if (status==PJ_SUCCESS) {
- // unexpectedly success!
- status = -30;
- goto on_error;
- }
- if (status != PJ_EPENDING) {
- // success
- } else {
- ++pending_op;
- }
-
- callback_connect_status = -2;
- callback_connect_key = NULL;
-
- // Poll until we've got result
- while (pending_op) {
- pj_time_val timeout = {1, 0};
-
- status=pj_ioqueue_poll(ioque, &timeout);
- if (status > 0) {
- if (callback_connect_key==ckey1) {
- if (callback_connect_status == 0) {
- // unexpectedly connected!
- status = -50;
- goto on_error;
- }
- }
-
- pending_op -= status;
- if (pending_op == 0) {
- status = 0;
- }
- }
- }
-
- // There's no pending operation.
- // When we poll the ioqueue, there must not be events.
- if (pending_op == 0) {
- pj_time_val timeout = {1, 0};
- status = pj_ioqueue_poll(ioque, &timeout);
- if (status != 0) {
- status=-60; goto on_error;
- }
- }
-
- // Success
- status = 0;
-
-on_error:
- if (csock1 != PJ_INVALID_SOCKET)
- pj_sock_close(csock1);
- if (ioque != NULL)
- pj_ioqueue_destroy(ioque);
- pj_pool_release(pool);
- return status;
-}
-
-int tcp_ioqueue_test()
-{
- int status;
-
- PJ_LOG(3, (THIS_FILE, "..%s compliance test 0 (success scenario)",
- pj_ioqueue_name()));
- if ((status=compliance_test_0()) != 0) {
- PJ_LOG(1, (THIS_FILE, "....FAILED (status=%d)\n", status));
- return status;
- }
- PJ_LOG(3, (THIS_FILE, "..%s compliance test 1 (failed scenario)",
- pj_ioqueue_name()));
- if ((status=compliance_test_1()) != 0) {
- PJ_LOG(1, (THIS_FILE, "....FAILED (status=%d)\n", status));
- return status;
- }
-
- return 0;
-}
-
-#endif /* PJ_HAS_TCP */
-
-
-#else
-/* To prevent warning about "translation unit is empty"
- * when this test is disabled.
- */
-int dummy_uiq_tcp;
-#endif /* INCLUDE_TCP_IOQUEUE_TEST */
-
-
+/* $Id$ */
+/*
+ * Copyright (C)2003-2006 Benny Prijono <benny@prijono.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#include "test.h"
+
+/**
+ * \page page_pjlib_ioqueue_tcp_test Test: I/O Queue (TCP)
+ *
+ * This file provides implementation to test the
+ * functionality of the I/O queue when TCP socket is used.
+ *
+ *
+ * This file is <b>pjlib-test/ioq_tcp.c</b>
+ *
+ * \include pjlib-test/ioq_tcp.c
+ */
+
+
+#if INCLUDE_TCP_IOQUEUE_TEST
+
+#include <pjlib.h>
+
+#if PJ_HAS_TCP
+
+#define THIS_FILE "test_tcp"
+#define PORT 50000
+#define NON_EXISTANT_PORT 50123
+#define LOOP 100
+#define BUF_MIN_SIZE 32
+#define BUF_MAX_SIZE 2048
+#define SOCK_INACTIVE_MIN (4-2)
+#define SOCK_INACTIVE_MAX (PJ_IOQUEUE_MAX_HANDLES - 2)
+#define POOL_SIZE (2*BUF_MAX_SIZE + SOCK_INACTIVE_MAX*128 + 2048)
+
+static pj_ssize_t callback_read_size,
+ callback_write_size,
+ callback_accept_status,
+ callback_connect_status;
+static pj_ioqueue_key_t *callback_read_key,
+ *callback_write_key,
+ *callback_accept_key,
+ *callback_connect_key;
+static pj_ioqueue_op_key_t *callback_read_op,
+ *callback_write_op,
+ *callback_accept_op;
+
+static void on_ioqueue_read(pj_ioqueue_key_t *key,
+ pj_ioqueue_op_key_t *op_key,
+ pj_ssize_t bytes_read)
+{
+ callback_read_key = key;
+ callback_read_op = op_key;
+ callback_read_size = bytes_read;
+}
+
+static void on_ioqueue_write(pj_ioqueue_key_t *key,
+ pj_ioqueue_op_key_t *op_key,
+ pj_ssize_t bytes_written)
+{
+ callback_write_key = key;
+ callback_write_op = op_key;
+ callback_write_size = bytes_written;
+}
+
+static void on_ioqueue_accept(pj_ioqueue_key_t *key,
+ pj_ioqueue_op_key_t *op_key,
+ pj_sock_t sock,
+ int status)
+{
+ PJ_UNUSED_ARG(sock);
+
+ callback_accept_key = key;
+ callback_accept_op = op_key;
+ callback_accept_status = status;
+}
+
+static void on_ioqueue_connect(pj_ioqueue_key_t *key, int status)
+{
+ callback_connect_key = key;
+ callback_connect_status = status;
+}
+
+static pj_ioqueue_callback test_cb =
+{
+ &on_ioqueue_read,
+ &on_ioqueue_write,
+ &on_ioqueue_accept,
+ &on_ioqueue_connect,
+};
+
+static int send_recv_test(pj_ioqueue_t *ioque,
+ pj_ioqueue_key_t *skey,
+ pj_ioqueue_key_t *ckey,
+ void *send_buf,
+ void *recv_buf,
+ pj_ssize_t bufsize,
+ pj_timestamp *t_elapsed)
+{
+ pj_status_t status;
+ pj_ssize_t bytes;
+ pj_time_val timeout;
+ pj_timestamp t1, t2;
+ int pending_op = 0;
+ pj_ioqueue_op_key_t read_op, write_op;
+
+ // Start reading on the server side.
+ bytes = bufsize;
+ status = pj_ioqueue_recv(skey, &read_op, recv_buf, &bytes, 0);
+ if (status != PJ_SUCCESS && status != PJ_EPENDING) {
+ app_perror("...pj_ioqueue_recv error", status);
+ return -100;
+ }
+
+ if (status == PJ_EPENDING)
+ ++pending_op;
+ else {
+ /* Does not expect to return error or immediate data. */
+ return -115;
+ }
+
+ // Randomize send buffer.
+ pj_create_random_string((char*)send_buf, bufsize);
+
+ // Starts send on the client side.
+ bytes = bufsize;
+ status = pj_ioqueue_send(ckey, &write_op, send_buf, &bytes, 0);
+ if (status != PJ_SUCCESS && bytes != PJ_EPENDING) {
+ return -120;
+ }
+ if (status == PJ_EPENDING) {
+ ++pending_op;
+ }
+
+ // Begin time.
+ pj_get_timestamp(&t1);
+
+ // Reset indicators
+ callback_read_size = callback_write_size = 0;
+ callback_read_key = callback_write_key = NULL;
+ callback_read_op = callback_write_op = NULL;
+
+ // Poll the queue until we've got completion event in the server side.
+ status = 0;
+ while (pending_op > 0) {
+ timeout.sec = 1; timeout.msec = 0;
+ status = pj_ioqueue_poll(ioque, &timeout);
+ if (status > 0) {
+ if (callback_read_size) {
+ if (callback_read_size != bufsize)
+ return -160;
+ if (callback_read_key != skey)
+ return -161;
+ if (callback_read_op != &read_op)
+ return -162;
+ }
+ if (callback_write_size) {
+ if (callback_write_key != ckey)
+ return -163;
+ if (callback_write_op != &write_op)
+ return -164;
+ }
+ pending_op -= status;
+ }
+ if (status == 0) {
+ PJ_LOG(3,("", "...error: timed out"));
+ }
+ if (status < 0) {
+ return -170;
+ }
+ }
+
+ // Pending op is zero.
+ // Subsequent poll should yield zero too.
+ timeout.sec = timeout.msec = 0;
+ status = pj_ioqueue_poll(ioque, &timeout);
+ if (status != 0)
+ return -173;
+
+ // End time.
+ pj_get_timestamp(&t2);
+ t_elapsed->u32.lo += (t2.u32.lo - t1.u32.lo);
+
+ // Compare recv buffer with send buffer.
+ if (pj_memcmp(send_buf, recv_buf, bufsize) != 0) {
+ return -180;
+ }
+
+ // Success
+ return 0;
+}
+
+
+/*
+ * Compliance test for success scenario.
+ */
+static int compliance_test_0(void)
+{
+ pj_sock_t ssock=-1, csock0=-1, csock1=-1;
+ pj_sockaddr_in addr, client_addr, rmt_addr;
+ int client_addr_len;
+ pj_pool_t *pool = NULL;
+ char *send_buf, *recv_buf;
+ pj_ioqueue_t *ioque = NULL;
+ pj_ioqueue_key_t *skey, *ckey0, *ckey1;
+ pj_ioqueue_op_key_t accept_op;
+ int bufsize = BUF_MIN_SIZE;
+ pj_ssize_t status = -1;
+ int pending_op = 0;
+ pj_timestamp t_elapsed;
+ pj_str_t s;
+ pj_status_t rc;
+
+ // Create pool.
+ pool = pj_pool_create(mem, NULL, POOL_SIZE, 4000, NULL);
+
+ // Allocate buffers for send and receive.
+ send_buf = (char*)pj_pool_alloc(pool, bufsize);
+ recv_buf = (char*)pj_pool_alloc(pool, bufsize);
+
+ // Create server socket and client socket for connecting
+ rc = pj_sock_socket(PJ_AF_INET, PJ_SOCK_STREAM, 0, &ssock);
+ if (rc != PJ_SUCCESS) {
+ app_perror("...error creating socket", rc);
+ status=-1; goto on_error;
+ }
+
+ rc = pj_sock_socket(PJ_AF_INET, PJ_SOCK_STREAM, 0, &csock1);
+ if (rc != PJ_SUCCESS) {
+ app_perror("...error creating socket", rc);
+ status=-1; goto on_error;
+ }
+
+ // Bind server socket.
+ memset(&addr, 0, sizeof(addr));
+ addr.sin_family = PJ_AF_INET;
+ addr.sin_port = pj_htons(PORT);
+ if (pj_sock_bind(ssock, &addr, sizeof(addr))) {
+ app_perror("...bind error", rc);
+ status=-10; goto on_error;
+ }
+
+ // Create I/O Queue.
+ rc = pj_ioqueue_create(pool, PJ_IOQUEUE_MAX_HANDLES, &ioque);
+ if (rc != PJ_SUCCESS) {
+ app_perror("...ERROR in pj_ioqueue_create()", rc);
+ status=-20; goto on_error;
+ }
+
+ // Register server socket and client socket.
+ rc = pj_ioqueue_register_sock(pool, ioque, ssock, NULL, &test_cb, &skey);
+ if (rc == PJ_SUCCESS)
+ rc = pj_ioqueue_register_sock(pool, ioque, csock1, NULL, &test_cb,
+ &ckey1);
+ else
+ ckey1 = NULL;
+ if (rc != PJ_SUCCESS) {
+ app_perror("...ERROR in pj_ioqueue_register_sock()", rc);
+ status=-23; goto on_error;
+ }
+
+ // Server socket listen().
+ if (pj_sock_listen(ssock, 5)) {
+ app_perror("...ERROR in pj_sock_listen()", rc);
+ status=-25; goto on_error;
+ }
+
+ // Server socket accept()
+ client_addr_len = sizeof(pj_sockaddr_in);
+ status = pj_ioqueue_accept(skey, &accept_op, &csock0,
+ &client_addr, &rmt_addr, &client_addr_len);
+ if (status != PJ_EPENDING) {
+ app_perror("...ERROR in pj_ioqueue_accept()", rc);
+ status=-30; goto on_error;
+ }
+ if (status==PJ_EPENDING) {
+ ++pending_op;
+ }
+
+ // Initialize remote address.
+ memset(&addr, 0, sizeof(addr));
+ addr.sin_family = PJ_AF_INET;
+ addr.sin_port = pj_htons(PORT);
+ addr.sin_addr = pj_inet_addr(pj_cstr(&s, "127.0.0.1"));
+
+ // Client socket connect()
+ status = pj_ioqueue_connect(ckey1, &addr, sizeof(addr));
+ if (status!=PJ_SUCCESS && status != PJ_EPENDING) {
+ app_perror("...ERROR in pj_ioqueue_connect()", rc);
+ status=-40; goto on_error;
+ }
+ if (status==PJ_EPENDING) {
+ ++pending_op;
+ }
+
+ // Poll until connected
+ callback_read_size = callback_write_size = 0;
+ callback_accept_status = callback_connect_status = -2;
+
+ callback_read_key = callback_write_key =
+ callback_accept_key = callback_connect_key = NULL;
+ callback_accept_op = callback_read_op = callback_write_op = NULL;
+
+ while (pending_op) {
+ pj_time_val timeout = {1, 0};
+
+ status=pj_ioqueue_poll(ioque, &timeout);
+ if (status > 0) {
+ if (callback_accept_status != -2) {
+ if (callback_accept_status != 0) {
+ status=-41; goto on_error;
+ }
+ if (callback_accept_key != skey) {
+ status=-42; goto on_error;
+ }
+ if (callback_accept_op != &accept_op) {
+ status=-43; goto on_error;
+ }
+ callback_accept_status = -2;
+ }
+
+ if (callback_connect_status != -2) {
+ if (callback_connect_status != 0) {
+ status=-50; goto on_error;
+ }
+ if (callback_connect_key != ckey1) {
+ status=-51; goto on_error;
+ }
+ callback_connect_status = -2;
+ }
+
+ pending_op -= status;
+
+ if (pending_op == 0) {
+ status = 0;
+ }
+ }
+ }
+
+ // There's no pending operation.
+ // When we poll the ioqueue, there must not be events.
+ if (pending_op == 0) {
+ pj_time_val timeout = {1, 0};
+ status = pj_ioqueue_poll(ioque, &timeout);
+ if (status != 0) {
+ status=-60; goto on_error;
+ }
+ }
+
+ // Check accepted socket.
+ if (csock0 == PJ_INVALID_SOCKET) {
+ status = -69;
+ app_perror("...accept() error", pj_get_os_error());
+ goto on_error;
+ }
+
+ // Register newly accepted socket.
+ rc = pj_ioqueue_register_sock(pool, ioque, csock0, NULL,
+ &test_cb, &ckey0);
+ if (rc != PJ_SUCCESS) {
+ app_perror("...ERROR in pj_ioqueue_register_sock", rc);
+ status = -70;
+ goto on_error;
+ }
+
+ // Test send and receive.
+ t_elapsed.u32.lo = 0;
+ status = send_recv_test(ioque, ckey0, ckey1, send_buf,
+ recv_buf, bufsize, &t_elapsed);
+ if (status != 0) {
+ goto on_error;
+ }
+
+ // Success
+ status = 0;
+
+on_error:
+ if (ssock != PJ_INVALID_SOCKET)
+ pj_sock_close(ssock);
+ if (csock1 != PJ_INVALID_SOCKET)
+ pj_sock_close(csock1);
+ if (csock0 != PJ_INVALID_SOCKET)
+ pj_sock_close(csock0);
+ if (ioque != NULL)
+ pj_ioqueue_destroy(ioque);
+ pj_pool_release(pool);
+ return status;
+
+}
+
+/*
+ * Compliance test for failed scenario.
+ * In this case, the client connects to a non-existant service.
+ */
+static int compliance_test_1(void)
+{
+ pj_sock_t csock1=-1;
+ pj_sockaddr_in addr;
+ pj_pool_t *pool = NULL;
+ pj_ioqueue_t *ioque = NULL;
+ pj_ioqueue_key_t *ckey1;
+ pj_ssize_t status = -1;
+ int pending_op = 0;
+ pj_str_t s;
+ pj_status_t rc;
+
+ // Create pool.
+ pool = pj_pool_create(mem, NULL, POOL_SIZE, 4000, NULL);
+
+ // Create I/O Queue.
+ rc = pj_ioqueue_create(pool, PJ_IOQUEUE_MAX_HANDLES, &ioque);
+ if (!ioque) {
+ status=-20; goto on_error;
+ }
+
+ // Create client socket
+ rc = pj_sock_socket(PJ_AF_INET, PJ_SOCK_STREAM, 0, &csock1);
+ if (rc != PJ_SUCCESS) {
+ app_perror("...ERROR in pj_sock_socket()", rc);
+ status=-1; goto on_error;
+ }
+
+ // Register client socket.
+ rc = pj_ioqueue_register_sock(pool, ioque, csock1, NULL,
+ &test_cb, &ckey1);
+ if (rc != PJ_SUCCESS) {
+ app_perror("...ERROR in pj_ioqueue_register_sock()", rc);
+ status=-23; goto on_error;
+ }
+
+ // Initialize remote address.
+ memset(&addr, 0, sizeof(addr));
+ addr.sin_family = PJ_AF_INET;
+ addr.sin_port = pj_htons(NON_EXISTANT_PORT);
+ addr.sin_addr = pj_inet_addr(pj_cstr(&s, "127.0.0.1"));
+
+ // Client socket connect()
+ status = pj_ioqueue_connect(ckey1, &addr, sizeof(addr));
+ if (status==PJ_SUCCESS) {
+ // unexpectedly success!
+ status = -30;
+ goto on_error;
+ }
+ if (status != PJ_EPENDING) {
+ // success
+ } else {
+ ++pending_op;
+ }
+
+ callback_connect_status = -2;
+ callback_connect_key = NULL;
+
+ // Poll until we've got result
+ while (pending_op) {
+ pj_time_val timeout = {1, 0};
+
+ status=pj_ioqueue_poll(ioque, &timeout);
+ if (status > 0) {
+ if (callback_connect_key==ckey1) {
+ if (callback_connect_status == 0) {
+ // unexpectedly connected!
+ status = -50;
+ goto on_error;
+ }
+ }
+
+ pending_op -= status;
+ if (pending_op == 0) {
+ status = 0;
+ }
+ }
+ }
+
+ // There's no pending operation.
+ // When we poll the ioqueue, there must not be events.
+ if (pending_op == 0) {
+ pj_time_val timeout = {1, 0};
+ status = pj_ioqueue_poll(ioque, &timeout);
+ if (status != 0) {
+ status=-60; goto on_error;
+ }
+ }
+
+ // Success
+ status = 0;
+
+on_error:
+ if (csock1 != PJ_INVALID_SOCKET)
+ pj_sock_close(csock1);
+ if (ioque != NULL)
+ pj_ioqueue_destroy(ioque);
+ pj_pool_release(pool);
+ return status;
+}
+
+int tcp_ioqueue_test()
+{
+ int status;
+
+ PJ_LOG(3, (THIS_FILE, "..%s compliance test 0 (success scenario)",
+ pj_ioqueue_name()));
+ if ((status=compliance_test_0()) != 0) {
+ PJ_LOG(1, (THIS_FILE, "....FAILED (status=%d)\n", status));
+ return status;
+ }
+ PJ_LOG(3, (THIS_FILE, "..%s compliance test 1 (failed scenario)",
+ pj_ioqueue_name()));
+ if ((status=compliance_test_1()) != 0) {
+ PJ_LOG(1, (THIS_FILE, "....FAILED (status=%d)\n", status));
+ return status;
+ }
+
+ return 0;
+}
+
+#endif /* PJ_HAS_TCP */
+
+
+#else
+/* To prevent warning about "translation unit is empty"
+ * when this test is disabled.
+ */
+int dummy_uiq_tcp;
+#endif /* INCLUDE_TCP_IOQUEUE_TEST */
+
+
diff --git a/pjlib/src/pjlib-test/ioq_udp.c b/pjlib/src/pjlib-test/ioq_udp.c
index 1e3b6dc8..7db2a8eb 100644
--- a/pjlib/src/pjlib-test/ioq_udp.c
+++ b/pjlib/src/pjlib-test/ioq_udp.c
@@ -1,663 +1,663 @@
-/* $Id$ */
-/*
- * Copyright (C)2003-2006 Benny Prijono <benny@prijono.org>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
-#include "test.h"
-
-
-/**
- * \page page_pjlib_ioqueue_udp_test Test: I/O Queue (UDP)
- *
- * This file provides implementation to test the
- * functionality of the I/O queue when UDP socket is used.
- *
- *
- * This file is <b>pjlib-test/ioq_udp.c</b>
- *
- * \include pjlib-test/ioq_udp.c
- */
-
-
-#if INCLUDE_UDP_IOQUEUE_TEST
-
-#include <pjlib.h>
-
-#include <pj/compat/socket.h>
-
-#define THIS_FILE "test_udp"
-#define PORT 51233
-#define LOOP 100
-#define BUF_MIN_SIZE 32
-#define BUF_MAX_SIZE 2048
-#define SOCK_INACTIVE_MIN (1)
-#define SOCK_INACTIVE_MAX (PJ_IOQUEUE_MAX_HANDLES - 2)
-#define POOL_SIZE (2*BUF_MAX_SIZE + SOCK_INACTIVE_MAX*128 + 2048)
-
-#undef TRACE_
-#define TRACE_(msg) PJ_LOG(3,(THIS_FILE,"....." msg))
-
-static pj_ssize_t callback_read_size,
- callback_write_size,
- callback_accept_status,
- callback_connect_status;
-static pj_ioqueue_key_t *callback_read_key,
- *callback_write_key,
- *callback_accept_key,
- *callback_connect_key;
-static pj_ioqueue_op_key_t *callback_read_op,
- *callback_write_op,
- *callback_accept_op;
-
-static void on_ioqueue_read(pj_ioqueue_key_t *key,
- pj_ioqueue_op_key_t *op_key,
- pj_ssize_t bytes_read)
-{
- callback_read_key = key;
- callback_read_op = op_key;
- callback_read_size = bytes_read;
-}
-
-static void on_ioqueue_write(pj_ioqueue_key_t *key,
- pj_ioqueue_op_key_t *op_key,
- pj_ssize_t bytes_written)
-{
- callback_write_key = key;
- callback_write_op = op_key;
- callback_write_size = bytes_written;
-}
-
-static void on_ioqueue_accept(pj_ioqueue_key_t *key,
- pj_ioqueue_op_key_t *op_key,
- pj_sock_t sock, int status)
-{
- PJ_UNUSED_ARG(sock);
- callback_accept_key = key;
- callback_accept_op = op_key;
- callback_accept_status = status;
-}
-
-static void on_ioqueue_connect(pj_ioqueue_key_t *key, int status)
-{
- callback_connect_key = key;
- callback_connect_status = status;
-}
-
-static pj_ioqueue_callback test_cb =
-{
- &on_ioqueue_read,
- &on_ioqueue_write,
- &on_ioqueue_accept,
- &on_ioqueue_connect,
-};
-
-#ifdef PJ_WIN32
-# define S_ADDR S_un.S_addr
-#else
-# define S_ADDR s_addr
-#endif
-
-/*
- * compliance_test()
- * To test that the basic IOQueue functionality works. It will just exchange
- * data between two sockets.
- */
-static int compliance_test(void)
-{
- pj_sock_t ssock=-1, csock=-1;
- pj_sockaddr_in addr;
- int addrlen;
- pj_pool_t *pool = NULL;
- char *send_buf, *recv_buf;
- pj_ioqueue_t *ioque = NULL;
- pj_ioqueue_key_t *skey, *ckey;
- pj_ioqueue_op_key_t read_op, write_op;
- int bufsize = BUF_MIN_SIZE;
- pj_ssize_t bytes, status = -1;
- pj_str_t temp;
- pj_bool_t send_pending, recv_pending;
- pj_status_t rc;
-
- pj_set_os_error(PJ_SUCCESS);
-
- // Create pool.
- pool = pj_pool_create(mem, NULL, POOL_SIZE, 4000, NULL);
-
- // Allocate buffers for send and receive.
- send_buf = (char*)pj_pool_alloc(pool, bufsize);
- recv_buf = (char*)pj_pool_alloc(pool, bufsize);
-
- // Allocate sockets for sending and receiving.
- TRACE_("creating sockets...");
- rc = pj_sock_socket(PJ_AF_INET, PJ_SOCK_DGRAM, 0, &ssock);
- if (rc==PJ_SUCCESS)
- rc = pj_sock_socket(PJ_AF_INET, PJ_SOCK_DGRAM, 0, &csock);
- else
- csock = PJ_INVALID_SOCKET;
- if (rc != PJ_SUCCESS) {
- app_perror("...ERROR in pj_sock_socket()", rc);
- status=-1; goto on_error;
- }
-
- // Bind server socket.
- TRACE_("bind socket...");
- memset(&addr, 0, sizeof(addr));
- addr.sin_family = PJ_AF_INET;
- addr.sin_port = pj_htons(PORT);
- if (pj_sock_bind(ssock, &addr, sizeof(addr))) {
- status=-10; goto on_error;
- }
-
- // Create I/O Queue.
- TRACE_("create ioqueue...");
- rc = pj_ioqueue_create(pool, PJ_IOQUEUE_MAX_HANDLES, &ioque);
- if (rc != PJ_SUCCESS) {
- status=-20; goto on_error;
- }
-
- // Register server and client socket.
- // We put this after inactivity socket, hopefully this can represent the
- // worst waiting time.
- TRACE_("registering first sockets...");
- rc = pj_ioqueue_register_sock(pool, ioque, ssock, NULL,
- &test_cb, &skey);
- if (rc != PJ_SUCCESS) {
- app_perror("...error(10): ioqueue_register error", rc);
- status=-25; goto on_error;
- }
- TRACE_("registering second sockets...");
- rc = pj_ioqueue_register_sock( pool, ioque, csock, NULL,
- &test_cb, &ckey);
- if (rc != PJ_SUCCESS) {
- app_perror("...error(11): ioqueue_register error", rc);
- status=-26; goto on_error;
- }
-
- // Set destination address to send the packet.
- TRACE_("set destination address...");
- temp = pj_str("127.0.0.1");
- if ((rc=pj_sockaddr_in_init(&addr, &temp, PORT)) != 0) {
- app_perror("...error: unable to resolve 127.0.0.1", rc);
- status=-26; goto on_error;
- }
-
- // Randomize send_buf.
- pj_create_random_string(send_buf, bufsize);
-
- // Register reading from ioqueue.
- TRACE_("start recvfrom...");
- addrlen = sizeof(addr);
- bytes = bufsize;
- rc = pj_ioqueue_recvfrom(skey, &read_op, recv_buf, &bytes, 0,
- &addr, &addrlen);
- if (rc != PJ_SUCCESS && rc != PJ_EPENDING) {
- app_perror("...error: pj_ioqueue_recvfrom", rc);
- status=-28; goto on_error;
- } else if (rc == PJ_EPENDING) {
- recv_pending = 1;
- PJ_LOG(3, (THIS_FILE,
- "......ok: recvfrom returned pending"));
- } else {
- PJ_LOG(3, (THIS_FILE,
- "......error: recvfrom returned immediate ok!"));
- status=-29; goto on_error;
- }
-
- // Write must return the number of bytes.
- TRACE_("start sendto...");
- bytes = bufsize;
- rc = pj_ioqueue_sendto(ckey, &write_op, send_buf, &bytes, 0, &addr,
- sizeof(addr));
- if (rc != PJ_SUCCESS && rc != PJ_EPENDING) {
- app_perror("...error: pj_ioqueue_sendto", rc);
- status=-30; goto on_error;
- } else if (rc == PJ_EPENDING) {
- send_pending = 1;
- PJ_LOG(3, (THIS_FILE,
- "......ok: sendto returned pending"));
- } else {
- send_pending = 0;
- PJ_LOG(3, (THIS_FILE,
- "......ok: sendto returned immediate success"));
- }
-
- // reset callback variables.
- callback_read_size = callback_write_size = 0;
- callback_accept_status = callback_connect_status = -2;
- callback_read_key = callback_write_key =
- callback_accept_key = callback_connect_key = NULL;
- callback_read_op = callback_write_op = NULL;
-
- // Poll if pending.
- while (send_pending || recv_pending) {
- int rc;
- pj_time_val timeout = { 5, 0 };
-
- TRACE_("poll...");
- rc = pj_ioqueue_poll(ioque, &timeout);
-
- if (rc == 0) {
- PJ_LOG(1,(THIS_FILE, "...ERROR: timed out..."));
- status=-45; goto on_error;
- } else if (rc < 0) {
- app_perror("...ERROR in ioqueue_poll()", rc);
- status=-50; goto on_error;
- }
-
- if (callback_read_key != NULL) {
- if (callback_read_size != bufsize) {
- status=-61; goto on_error;
- }
- if (callback_read_key != skey) {
- status=-65; goto on_error;
- }
- if (callback_read_op != &read_op) {
- status=-66; goto on_error;
- }
-
- if (memcmp(send_buf, recv_buf, bufsize) != 0) {
- status=-70; goto on_error;
- }
-
-
- recv_pending = 0;
- }
-
- if (callback_write_key != NULL) {
- if (callback_write_size != bufsize) {
- status=-73; goto on_error;
- }
- if (callback_write_key != ckey) {
- status=-75; goto on_error;
- }
- if (callback_write_op != &write_op) {
- status=-76; goto on_error;
- }
-
- send_pending = 0;
- }
- }
-
- // Success
- status = 0;
-
-on_error:
- if (status != 0) {
- char errbuf[128];
- PJ_LOG(1, (THIS_FILE,
- "...compliance test error: status=%d, os_err=%d (%s)",
- status, pj_get_netos_error(),
- pj_strerror(pj_get_netos_error(), errbuf, sizeof(errbuf))));
- }
- if (ssock)
- pj_sock_close(ssock);
- if (csock)
- pj_sock_close(csock);
- if (ioque != NULL)
- pj_ioqueue_destroy(ioque);
- pj_pool_release(pool);
- return status;
-
-}
-
-/*
- * Testing with many handles.
- * This will just test registering PJ_IOQUEUE_MAX_HANDLES count
- * of sockets to the ioqueue.
- */
-static int many_handles_test(void)
-{
- enum { MAX = PJ_IOQUEUE_MAX_HANDLES };
- pj_pool_t *pool;
- pj_ioqueue_t *ioqueue;
- pj_sock_t *sock;
- pj_ioqueue_key_t **key;
- pj_status_t rc;
- int count, i;
-
- PJ_LOG(3,(THIS_FILE,"...testing with so many handles"));
-
- pool = pj_pool_create(mem, NULL, 4000, 4000, NULL);
- if (!pool)
- return PJ_ENOMEM;
-
- key = pj_pool_alloc(pool, MAX*sizeof(pj_ioqueue_key_t*));
- sock = pj_pool_alloc(pool, MAX*sizeof(pj_sock_t));
-
- /* Create IOQueue */
- rc = pj_ioqueue_create(pool, MAX, &ioqueue);
- if (rc != PJ_SUCCESS || ioqueue == NULL) {
- app_perror("...error in pj_ioqueue_create", rc);
- return -10;
- }
-
- /* Register as many sockets. */
- for (count=0; count<MAX; ++count) {
- sock[count] = PJ_INVALID_SOCKET;
- rc = pj_sock_socket(PJ_AF_INET, PJ_SOCK_DGRAM, 0, &sock[count]);
- if (rc != PJ_SUCCESS || sock[count] == PJ_INVALID_SOCKET) {
- PJ_LOG(3,(THIS_FILE, "....unable to create %d-th socket, rc=%d",
- count, rc));
- break;
- }
- key[count] = NULL;
- rc = pj_ioqueue_register_sock(pool, ioqueue, sock[count],
- NULL, &test_cb, &key[count]);
- if (rc != PJ_SUCCESS || key[count] == NULL) {
- PJ_LOG(3,(THIS_FILE, "....unable to register %d-th socket, rc=%d",
- count, rc));
- return -30;
- }
- }
-
- /* Test complete. */
-
- /* Now deregister and close all handles. */
-
- for (i=0; i<count; ++i) {
- rc = pj_ioqueue_unregister(key[i]);
- if (rc != PJ_SUCCESS) {
- app_perror("...error in pj_ioqueue_unregister", rc);
- }
- rc = pj_sock_close(sock[i]);
- if (rc != PJ_SUCCESS) {
- app_perror("...error in pj_sock_close", rc);
- }
- }
-
- rc = pj_ioqueue_destroy(ioqueue);
- if (rc != PJ_SUCCESS) {
- app_perror("...error in pj_ioqueue_destroy", rc);
- }
-
- pj_pool_release(pool);
-
- PJ_LOG(3,(THIS_FILE,"....many_handles_test() ok"));
-
- return 0;
-}
-
-/*
- * Multi-operation test.
- */
-
-/*
- * Benchmarking IOQueue
- */
-static int bench_test(int bufsize, int inactive_sock_count)
-{
- pj_sock_t ssock=-1, csock=-1;
- pj_sockaddr_in addr;
- pj_pool_t *pool = NULL;
- pj_sock_t *inactive_sock=NULL;
- pj_ioqueue_op_key_t *inactive_read_op;
- char *send_buf, *recv_buf;
- pj_ioqueue_t *ioque = NULL;
- pj_ioqueue_key_t *skey, *ckey, *key;
- pj_timestamp t1, t2, t_elapsed;
- int rc=0, i;
- pj_str_t temp;
- char errbuf[128];
-
- // Create pool.
- pool = pj_pool_create(mem, NULL, POOL_SIZE, 4000, NULL);
-
- // Allocate buffers for send and receive.
- send_buf = (char*)pj_pool_alloc(pool, bufsize);
- recv_buf = (char*)pj_pool_alloc(pool, bufsize);
-
- // Allocate sockets for sending and receiving.
- rc = pj_sock_socket(PJ_AF_INET, PJ_SOCK_DGRAM, 0, &ssock);
- if (rc == PJ_SUCCESS) {
- rc = pj_sock_socket(PJ_AF_INET, PJ_SOCK_DGRAM, 0, &csock);
- } else
- csock = PJ_INVALID_SOCKET;
- if (rc != PJ_SUCCESS) {
- app_perror("...error: pj_sock_socket()", rc);
- goto on_error;
- }
-
- // Bind server socket.
- memset(&addr, 0, sizeof(addr));
- addr.sin_family = PJ_AF_INET;
- addr.sin_port = pj_htons(PORT);
- if (pj_sock_bind(ssock, &addr, sizeof(addr)))
- goto on_error;
-
- pj_assert(inactive_sock_count+2 <= PJ_IOQUEUE_MAX_HANDLES);
-
- // Create I/O Queue.
- rc = pj_ioqueue_create(pool, PJ_IOQUEUE_MAX_HANDLES, &ioque);
- if (rc != PJ_SUCCESS) {
- app_perror("...error: pj_ioqueue_create()", rc);
- goto on_error;
- }
-
- // Allocate inactive sockets, and bind them to some arbitrary address.
- // Then register them to the I/O queue, and start a read operation.
- inactive_sock = (pj_sock_t*)pj_pool_alloc(pool,
- inactive_sock_count*sizeof(pj_sock_t));
- inactive_read_op = (pj_ioqueue_op_key_t*)pj_pool_alloc(pool,
- inactive_sock_count*sizeof(pj_ioqueue_op_key_t));
- memset(&addr, 0, sizeof(addr));
- addr.sin_family = PJ_AF_INET;
- for (i=0; i<inactive_sock_count; ++i) {
- pj_ssize_t bytes;
-
- rc = pj_sock_socket(PJ_AF_INET, PJ_SOCK_DGRAM, 0, &inactive_sock[i]);
- if (rc != PJ_SUCCESS || inactive_sock[i] < 0) {
- app_perror("...error: pj_sock_socket()", rc);
- goto on_error;
- }
- if ((rc=pj_sock_bind(inactive_sock[i], &addr, sizeof(addr))) != 0) {
- pj_sock_close(inactive_sock[i]);
- inactive_sock[i] = PJ_INVALID_SOCKET;
- app_perror("...error: pj_sock_bind()", rc);
- goto on_error;
- }
- rc = pj_ioqueue_register_sock(pool, ioque, inactive_sock[i],
- NULL, &test_cb, &key);
- if (rc != PJ_SUCCESS) {
- pj_sock_close(inactive_sock[i]);
- inactive_sock[i] = PJ_INVALID_SOCKET;
- app_perror("...error(1): pj_ioqueue_register_sock()", rc);
- PJ_LOG(3,(THIS_FILE, "....i=%d", i));
- goto on_error;
- }
- bytes = bufsize;
- rc = pj_ioqueue_recv(key, &inactive_read_op[i], recv_buf, &bytes, 0);
- if ( rc < 0 && rc != PJ_EPENDING) {
- pj_sock_close(inactive_sock[i]);
- inactive_sock[i] = PJ_INVALID_SOCKET;
- app_perror("...error: pj_ioqueue_read()", rc);
- goto on_error;
- }
- }
-
- // Register server and client socket.
- // We put this after inactivity socket, hopefully this can represent the
- // worst waiting time.
- rc = pj_ioqueue_register_sock(pool, ioque, ssock, NULL,
- &test_cb, &skey);
- if (rc != PJ_SUCCESS) {
- app_perror("...error(2): pj_ioqueue_register_sock()", rc);
- goto on_error;
- }
-
- rc = pj_ioqueue_register_sock(pool, ioque, csock, NULL,
- &test_cb, &ckey);
- if (rc != PJ_SUCCESS) {
- app_perror("...error(3): pj_ioqueue_register_sock()", rc);
- goto on_error;
- }
-
- // Set destination address to send the packet.
- pj_sockaddr_in_init(&addr, pj_cstr(&temp, "127.0.0.1"), PORT);
-
- // Test loop.
- t_elapsed.u64 = 0;
- for (i=0; i<LOOP; ++i) {
- pj_ssize_t bytes;
- pj_ioqueue_op_key_t read_op, write_op;
-
- // Randomize send buffer.
- pj_create_random_string(send_buf, bufsize);
-
- // Start reading on the server side.
- bytes = bufsize;
- rc = pj_ioqueue_recv(skey, &read_op, recv_buf, &bytes, 0);
- if (rc < 0 && rc != PJ_EPENDING) {
- app_perror("...error: pj_ioqueue_read()", rc);
- break;
- }
-
- // Starts send on the client side.
- bytes = bufsize;
- rc = pj_ioqueue_sendto(ckey, &write_op, send_buf, &bytes, 0,
- &addr, sizeof(addr));
- if (rc != PJ_SUCCESS && rc != PJ_EPENDING) {
- app_perror("...error: pj_ioqueue_write()", bytes);
- rc = -1;
- break;
- }
-
- // Begin time.
- pj_get_timestamp(&t1);
-
- // Poll the queue until we've got completion event in the server side.
- callback_read_key = NULL;
- callback_read_size = 0;
- do {
- rc = pj_ioqueue_poll(ioque, NULL);
- } while (rc >= 0 && callback_read_key != skey);
-
- // End time.
- pj_get_timestamp(&t2);
- t_elapsed.u64 += (t2.u64 - t1.u64);
-
- if (rc < 0)
- break;
-
- // Compare recv buffer with send buffer.
- if (callback_read_size != bufsize ||
- memcmp(send_buf, recv_buf, bufsize))
- {
- rc = -1;
- break;
- }
-
- // Poll until all events are exhausted, before we start the next loop.
- do {
- pj_time_val timeout = { 0, 10 };
- rc = pj_ioqueue_poll(ioque, &timeout);
- } while (rc>0);
-
- rc = 0;
- }
-
- // Print results
- if (rc == 0) {
- pj_timestamp tzero;
- pj_uint32_t usec_delay;
-
- tzero.u32.hi = tzero.u32.lo = 0;
- usec_delay = pj_elapsed_usec( &tzero, &t_elapsed);
-
- PJ_LOG(3, (THIS_FILE, "...%10d %15d % 9d",
- bufsize, inactive_sock_count, usec_delay));
-
- } else {
- PJ_LOG(2, (THIS_FILE, "...ERROR (buf:%d, fds:%d)",
- bufsize, inactive_sock_count+2));
- }
-
- // Cleaning up.
- for (i=0; i<inactive_sock_count; ++i)
- pj_sock_close(inactive_sock[i]);
- pj_sock_close(ssock);
- pj_sock_close(csock);
-
- pj_ioqueue_destroy(ioque);
- pj_pool_release( pool);
- return 0;
-
-on_error:
- PJ_LOG(1,(THIS_FILE, "...ERROR: %s",
- pj_strerror(pj_get_netos_error(), errbuf, sizeof(errbuf))));
- if (ssock)
- pj_sock_close(ssock);
- if (csock)
- pj_sock_close(csock);
- for (i=0; i<inactive_sock_count && inactive_sock &&
- inactive_sock[i]!=PJ_INVALID_SOCKET; ++i)
- {
- pj_sock_close(inactive_sock[i]);
- }
- if (ioque != NULL)
- pj_ioqueue_destroy(ioque);
- pj_pool_release( pool);
- return -1;
-}
-
-int udp_ioqueue_test()
-{
- int status;
- int bufsize, sock_count;
-
- PJ_LOG(3, (THIS_FILE, "...compliance test (%s)", pj_ioqueue_name()));
- if ((status=compliance_test()) != 0) {
- return status;
- }
- PJ_LOG(3, (THIS_FILE, "....compliance test ok"));
-
- if ((status=many_handles_test()) != 0) {
- return status;
- }
-
- PJ_LOG(4, (THIS_FILE, "...benchmarking different buffer size:"));
- PJ_LOG(4, (THIS_FILE, "... note: buf=bytes sent, fds=# of fds, "
- "elapsed=in timer ticks"));
-
- PJ_LOG(3, (THIS_FILE, "...Benchmarking poll times for %s:", pj_ioqueue_name()));
- PJ_LOG(3, (THIS_FILE, "...====================================="));
- PJ_LOG(3, (THIS_FILE, "...Buf.size #inactive-socks Time/poll"));
- PJ_LOG(3, (THIS_FILE, "... (bytes) (nanosec)"));
- PJ_LOG(3, (THIS_FILE, "...====================================="));
-
- for (bufsize=BUF_MIN_SIZE; bufsize <= BUF_MAX_SIZE; bufsize *= 2) {
- if (bench_test(bufsize, SOCK_INACTIVE_MIN))
- return -1;
- }
- bufsize = 512;
- for (sock_count=SOCK_INACTIVE_MIN+2;
- sock_count<=SOCK_INACTIVE_MAX+2;
- sock_count *= 2)
- {
- //PJ_LOG(3,(THIS_FILE, "...testing with %d fds", sock_count));
- if (bench_test(bufsize, sock_count-2))
- return -1;
- }
- return 0;
-}
-
-#else
-/* To prevent warning about "translation unit is empty"
- * when this test is disabled.
- */
-int dummy_uiq_udp;
-#endif /* INCLUDE_UDP_IOQUEUE_TEST */
-
-
+/* $Id$ */
+/*
+ * Copyright (C)2003-2006 Benny Prijono <benny@prijono.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#include "test.h"
+
+
+/**
+ * \page page_pjlib_ioqueue_udp_test Test: I/O Queue (UDP)
+ *
+ * This file provides implementation to test the
+ * functionality of the I/O queue when UDP socket is used.
+ *
+ *
+ * This file is <b>pjlib-test/ioq_udp.c</b>
+ *
+ * \include pjlib-test/ioq_udp.c
+ */
+
+
+#if INCLUDE_UDP_IOQUEUE_TEST
+
+#include <pjlib.h>
+
+#include <pj/compat/socket.h>
+
+#define THIS_FILE "test_udp"
+#define PORT 51233
+#define LOOP 100
+#define BUF_MIN_SIZE 32
+#define BUF_MAX_SIZE 2048
+#define SOCK_INACTIVE_MIN (1)
+#define SOCK_INACTIVE_MAX (PJ_IOQUEUE_MAX_HANDLES - 2)
+#define POOL_SIZE (2*BUF_MAX_SIZE + SOCK_INACTIVE_MAX*128 + 2048)
+
+#undef TRACE_
+#define TRACE_(msg) PJ_LOG(3,(THIS_FILE,"....." msg))
+
+static pj_ssize_t callback_read_size,
+ callback_write_size,
+ callback_accept_status,
+ callback_connect_status;
+static pj_ioqueue_key_t *callback_read_key,
+ *callback_write_key,
+ *callback_accept_key,
+ *callback_connect_key;
+static pj_ioqueue_op_key_t *callback_read_op,
+ *callback_write_op,
+ *callback_accept_op;
+
+static void on_ioqueue_read(pj_ioqueue_key_t *key,
+ pj_ioqueue_op_key_t *op_key,
+ pj_ssize_t bytes_read)
+{
+ callback_read_key = key;
+ callback_read_op = op_key;
+ callback_read_size = bytes_read;
+}
+
+static void on_ioqueue_write(pj_ioqueue_key_t *key,
+ pj_ioqueue_op_key_t *op_key,
+ pj_ssize_t bytes_written)
+{
+ callback_write_key = key;
+ callback_write_op = op_key;
+ callback_write_size = bytes_written;
+}
+
+static void on_ioqueue_accept(pj_ioqueue_key_t *key,
+ pj_ioqueue_op_key_t *op_key,
+ pj_sock_t sock, int status)
+{
+ PJ_UNUSED_ARG(sock);
+ callback_accept_key = key;
+ callback_accept_op = op_key;
+ callback_accept_status = status;
+}
+
+static void on_ioqueue_connect(pj_ioqueue_key_t *key, int status)
+{
+ callback_connect_key = key;
+ callback_connect_status = status;
+}
+
+static pj_ioqueue_callback test_cb =
+{
+ &on_ioqueue_read,
+ &on_ioqueue_write,
+ &on_ioqueue_accept,
+ &on_ioqueue_connect,
+};
+
+#ifdef PJ_WIN32
+# define S_ADDR S_un.S_addr
+#else
+# define S_ADDR s_addr
+#endif
+
+/*
+ * compliance_test()
+ * To test that the basic IOQueue functionality works. It will just exchange
+ * data between two sockets.
+ */
+static int compliance_test(void)
+{
+ pj_sock_t ssock=-1, csock=-1;
+ pj_sockaddr_in addr;
+ int addrlen;
+ pj_pool_t *pool = NULL;
+ char *send_buf, *recv_buf;
+ pj_ioqueue_t *ioque = NULL;
+ pj_ioqueue_key_t *skey, *ckey;
+ pj_ioqueue_op_key_t read_op, write_op;
+ int bufsize = BUF_MIN_SIZE;
+ pj_ssize_t bytes, status = -1;
+ pj_str_t temp;
+ pj_bool_t send_pending, recv_pending;
+ pj_status_t rc;
+
+ pj_set_os_error(PJ_SUCCESS);
+
+ // Create pool.
+ pool = pj_pool_create(mem, NULL, POOL_SIZE, 4000, NULL);
+
+ // Allocate buffers for send and receive.
+ send_buf = (char*)pj_pool_alloc(pool, bufsize);
+ recv_buf = (char*)pj_pool_alloc(pool, bufsize);
+
+ // Allocate sockets for sending and receiving.
+ TRACE_("creating sockets...");
+ rc = pj_sock_socket(PJ_AF_INET, PJ_SOCK_DGRAM, 0, &ssock);
+ if (rc==PJ_SUCCESS)
+ rc = pj_sock_socket(PJ_AF_INET, PJ_SOCK_DGRAM, 0, &csock);
+ else
+ csock = PJ_INVALID_SOCKET;
+ if (rc != PJ_SUCCESS) {
+ app_perror("...ERROR in pj_sock_socket()", rc);
+ status=-1; goto on_error;
+ }
+
+ // Bind server socket.
+ TRACE_("bind socket...");
+ memset(&addr, 0, sizeof(addr));
+ addr.sin_family = PJ_AF_INET;
+ addr.sin_port = pj_htons(PORT);
+ if (pj_sock_bind(ssock, &addr, sizeof(addr))) {
+ status=-10; goto on_error;
+ }
+
+ // Create I/O Queue.
+ TRACE_("create ioqueue...");
+ rc = pj_ioqueue_create(pool, PJ_IOQUEUE_MAX_HANDLES, &ioque);
+ if (rc != PJ_SUCCESS) {
+ status=-20; goto on_error;
+ }
+
+ // Register server and client socket.
+ // We put this after inactivity socket, hopefully this can represent the
+ // worst waiting time.
+ TRACE_("registering first sockets...");
+ rc = pj_ioqueue_register_sock(pool, ioque, ssock, NULL,
+ &test_cb, &skey);
+ if (rc != PJ_SUCCESS) {
+ app_perror("...error(10): ioqueue_register error", rc);
+ status=-25; goto on_error;
+ }
+ TRACE_("registering second sockets...");
+ rc = pj_ioqueue_register_sock( pool, ioque, csock, NULL,
+ &test_cb, &ckey);
+ if (rc != PJ_SUCCESS) {
+ app_perror("...error(11): ioqueue_register error", rc);
+ status=-26; goto on_error;
+ }
+
+ // Set destination address to send the packet.
+ TRACE_("set destination address...");
+ temp = pj_str("127.0.0.1");
+ if ((rc=pj_sockaddr_in_init(&addr, &temp, PORT)) != 0) {
+ app_perror("...error: unable to resolve 127.0.0.1", rc);
+ status=-26; goto on_error;
+ }
+
+ // Randomize send_buf.
+ pj_create_random_string(send_buf, bufsize);
+
+ // Register reading from ioqueue.
+ TRACE_("start recvfrom...");
+ addrlen = sizeof(addr);
+ bytes = bufsize;
+ rc = pj_ioqueue_recvfrom(skey, &read_op, recv_buf, &bytes, 0,
+ &addr, &addrlen);
+ if (rc != PJ_SUCCESS && rc != PJ_EPENDING) {
+ app_perror("...error: pj_ioqueue_recvfrom", rc);
+ status=-28; goto on_error;
+ } else if (rc == PJ_EPENDING) {
+ recv_pending = 1;
+ PJ_LOG(3, (THIS_FILE,
+ "......ok: recvfrom returned pending"));
+ } else {
+ PJ_LOG(3, (THIS_FILE,
+ "......error: recvfrom returned immediate ok!"));
+ status=-29; goto on_error;
+ }
+
+ // Write must return the number of bytes.
+ TRACE_("start sendto...");
+ bytes = bufsize;
+ rc = pj_ioqueue_sendto(ckey, &write_op, send_buf, &bytes, 0, &addr,
+ sizeof(addr));
+ if (rc != PJ_SUCCESS && rc != PJ_EPENDING) {
+ app_perror("...error: pj_ioqueue_sendto", rc);
+ status=-30; goto on_error;
+ } else if (rc == PJ_EPENDING) {
+ send_pending = 1;
+ PJ_LOG(3, (THIS_FILE,
+ "......ok: sendto returned pending"));
+ } else {
+ send_pending = 0;
+ PJ_LOG(3, (THIS_FILE,
+ "......ok: sendto returned immediate success"));
+ }
+
+ // reset callback variables.
+ callback_read_size = callback_write_size = 0;
+ callback_accept_status = callback_connect_status = -2;
+ callback_read_key = callback_write_key =
+ callback_accept_key = callback_connect_key = NULL;
+ callback_read_op = callback_write_op = NULL;
+
+ // Poll if pending.
+ while (send_pending || recv_pending) {
+ int rc;
+ pj_time_val timeout = { 5, 0 };
+
+ TRACE_("poll...");
+ rc = pj_ioqueue_poll(ioque, &timeout);
+
+ if (rc == 0) {
+ PJ_LOG(1,(THIS_FILE, "...ERROR: timed out..."));
+ status=-45; goto on_error;
+ } else if (rc < 0) {
+ app_perror("...ERROR in ioqueue_poll()", rc);
+ status=-50; goto on_error;
+ }
+
+ if (callback_read_key != NULL) {
+ if (callback_read_size != bufsize) {
+ status=-61; goto on_error;
+ }
+ if (callback_read_key != skey) {
+ status=-65; goto on_error;
+ }
+ if (callback_read_op != &read_op) {
+ status=-66; goto on_error;
+ }
+
+ if (memcmp(send_buf, recv_buf, bufsize) != 0) {
+ status=-70; goto on_error;
+ }
+
+
+ recv_pending = 0;
+ }
+
+ if (callback_write_key != NULL) {
+ if (callback_write_size != bufsize) {
+ status=-73; goto on_error;
+ }
+ if (callback_write_key != ckey) {
+ status=-75; goto on_error;
+ }
+ if (callback_write_op != &write_op) {
+ status=-76; goto on_error;
+ }
+
+ send_pending = 0;
+ }
+ }
+
+ // Success
+ status = 0;
+
+on_error:
+ if (status != 0) {
+ char errbuf[128];
+ PJ_LOG(1, (THIS_FILE,
+ "...compliance test error: status=%d, os_err=%d (%s)",
+ status, pj_get_netos_error(),
+ pj_strerror(pj_get_netos_error(), errbuf, sizeof(errbuf))));
+ }
+ if (ssock)
+ pj_sock_close(ssock);
+ if (csock)
+ pj_sock_close(csock);
+ if (ioque != NULL)
+ pj_ioqueue_destroy(ioque);
+ pj_pool_release(pool);
+ return status;
+
+}
+
+/*
+ * Testing with many handles.
+ * This will just test registering PJ_IOQUEUE_MAX_HANDLES count
+ * of sockets to the ioqueue.
+ */
+static int many_handles_test(void)
+{
+ enum { MAX = PJ_IOQUEUE_MAX_HANDLES };
+ pj_pool_t *pool;
+ pj_ioqueue_t *ioqueue;
+ pj_sock_t *sock;
+ pj_ioqueue_key_t **key;
+ pj_status_t rc;
+ int count, i;
+
+ PJ_LOG(3,(THIS_FILE,"...testing with so many handles"));
+
+ pool = pj_pool_create(mem, NULL, 4000, 4000, NULL);
+ if (!pool)
+ return PJ_ENOMEM;
+
+ key = pj_pool_alloc(pool, MAX*sizeof(pj_ioqueue_key_t*));
+ sock = pj_pool_alloc(pool, MAX*sizeof(pj_sock_t));
+
+ /* Create IOQueue */
+ rc = pj_ioqueue_create(pool, MAX, &ioqueue);
+ if (rc != PJ_SUCCESS || ioqueue == NULL) {
+ app_perror("...error in pj_ioqueue_create", rc);
+ return -10;
+ }
+
+ /* Register as many sockets. */
+ for (count=0; count<MAX; ++count) {
+ sock[count] = PJ_INVALID_SOCKET;
+ rc = pj_sock_socket(PJ_AF_INET, PJ_SOCK_DGRAM, 0, &sock[count]);
+ if (rc != PJ_SUCCESS || sock[count] == PJ_INVALID_SOCKET) {
+ PJ_LOG(3,(THIS_FILE, "....unable to create %d-th socket, rc=%d",
+ count, rc));
+ break;
+ }
+ key[count] = NULL;
+ rc = pj_ioqueue_register_sock(pool, ioqueue, sock[count],
+ NULL, &test_cb, &key[count]);
+ if (rc != PJ_SUCCESS || key[count] == NULL) {
+ PJ_LOG(3,(THIS_FILE, "....unable to register %d-th socket, rc=%d",
+ count, rc));
+ return -30;
+ }
+ }
+
+ /* Test complete. */
+
+ /* Now deregister and close all handles. */
+
+ for (i=0; i<count; ++i) {
+ rc = pj_ioqueue_unregister(key[i]);
+ if (rc != PJ_SUCCESS) {
+ app_perror("...error in pj_ioqueue_unregister", rc);
+ }
+ rc = pj_sock_close(sock[i]);
+ if (rc != PJ_SUCCESS) {
+ app_perror("...error in pj_sock_close", rc);
+ }
+ }
+
+ rc = pj_ioqueue_destroy(ioqueue);
+ if (rc != PJ_SUCCESS) {
+ app_perror("...error in pj_ioqueue_destroy", rc);
+ }
+
+ pj_pool_release(pool);
+
+ PJ_LOG(3,(THIS_FILE,"....many_handles_test() ok"));
+
+ return 0;
+}
+
+/*
+ * Multi-operation test.
+ */
+
+/*
+ * Benchmarking IOQueue
+ */
+static int bench_test(int bufsize, int inactive_sock_count)
+{
+ pj_sock_t ssock=-1, csock=-1;
+ pj_sockaddr_in addr;
+ pj_pool_t *pool = NULL;
+ pj_sock_t *inactive_sock=NULL;
+ pj_ioqueue_op_key_t *inactive_read_op;
+ char *send_buf, *recv_buf;
+ pj_ioqueue_t *ioque = NULL;
+ pj_ioqueue_key_t *skey, *ckey, *key;
+ pj_timestamp t1, t2, t_elapsed;
+ int rc=0, i;
+ pj_str_t temp;
+ char errbuf[128];
+
+ // Create pool.
+ pool = pj_pool_create(mem, NULL, POOL_SIZE, 4000, NULL);
+
+ // Allocate buffers for send and receive.
+ send_buf = (char*)pj_pool_alloc(pool, bufsize);
+ recv_buf = (char*)pj_pool_alloc(pool, bufsize);
+
+ // Allocate sockets for sending and receiving.
+ rc = pj_sock_socket(PJ_AF_INET, PJ_SOCK_DGRAM, 0, &ssock);
+ if (rc == PJ_SUCCESS) {
+ rc = pj_sock_socket(PJ_AF_INET, PJ_SOCK_DGRAM, 0, &csock);
+ } else
+ csock = PJ_INVALID_SOCKET;
+ if (rc != PJ_SUCCESS) {
+ app_perror("...error: pj_sock_socket()", rc);
+ goto on_error;
+ }
+
+ // Bind server socket.
+ memset(&addr, 0, sizeof(addr));
+ addr.sin_family = PJ_AF_INET;
+ addr.sin_port = pj_htons(PORT);
+ if (pj_sock_bind(ssock, &addr, sizeof(addr)))
+ goto on_error;
+
+ pj_assert(inactive_sock_count+2 <= PJ_IOQUEUE_MAX_HANDLES);
+
+ // Create I/O Queue.
+ rc = pj_ioqueue_create(pool, PJ_IOQUEUE_MAX_HANDLES, &ioque);
+ if (rc != PJ_SUCCESS) {
+ app_perror("...error: pj_ioqueue_create()", rc);
+ goto on_error;
+ }
+
+ // Allocate inactive sockets, and bind them to some arbitrary address.
+ // Then register them to the I/O queue, and start a read operation.
+ inactive_sock = (pj_sock_t*)pj_pool_alloc(pool,
+ inactive_sock_count*sizeof(pj_sock_t));
+ inactive_read_op = (pj_ioqueue_op_key_t*)pj_pool_alloc(pool,
+ inactive_sock_count*sizeof(pj_ioqueue_op_key_t));
+ memset(&addr, 0, sizeof(addr));
+ addr.sin_family = PJ_AF_INET;
+ for (i=0; i<inactive_sock_count; ++i) {
+ pj_ssize_t bytes;
+
+ rc = pj_sock_socket(PJ_AF_INET, PJ_SOCK_DGRAM, 0, &inactive_sock[i]);
+ if (rc != PJ_SUCCESS || inactive_sock[i] < 0) {
+ app_perror("...error: pj_sock_socket()", rc);
+ goto on_error;
+ }
+ if ((rc=pj_sock_bind(inactive_sock[i], &addr, sizeof(addr))) != 0) {
+ pj_sock_close(inactive_sock[i]);
+ inactive_sock[i] = PJ_INVALID_SOCKET;
+ app_perror("...error: pj_sock_bind()", rc);
+ goto on_error;
+ }
+ rc = pj_ioqueue_register_sock(pool, ioque, inactive_sock[i],
+ NULL, &test_cb, &key);
+ if (rc != PJ_SUCCESS) {
+ pj_sock_close(inactive_sock[i]);
+ inactive_sock[i] = PJ_INVALID_SOCKET;
+ app_perror("...error(1): pj_ioqueue_register_sock()", rc);
+ PJ_LOG(3,(THIS_FILE, "....i=%d", i));
+ goto on_error;
+ }
+ bytes = bufsize;
+ rc = pj_ioqueue_recv(key, &inactive_read_op[i], recv_buf, &bytes, 0);
+ if ( rc < 0 && rc != PJ_EPENDING) {
+ pj_sock_close(inactive_sock[i]);
+ inactive_sock[i] = PJ_INVALID_SOCKET;
+ app_perror("...error: pj_ioqueue_read()", rc);
+ goto on_error;
+ }
+ }
+
+ // Register server and client socket.
+ // We put this after inactivity socket, hopefully this can represent the
+ // worst waiting time.
+ rc = pj_ioqueue_register_sock(pool, ioque, ssock, NULL,
+ &test_cb, &skey);
+ if (rc != PJ_SUCCESS) {
+ app_perror("...error(2): pj_ioqueue_register_sock()", rc);
+ goto on_error;
+ }
+
+ rc = pj_ioqueue_register_sock(pool, ioque, csock, NULL,
+ &test_cb, &ckey);
+ if (rc != PJ_SUCCESS) {
+ app_perror("...error(3): pj_ioqueue_register_sock()", rc);
+ goto on_error;
+ }
+
+ // Set destination address to send the packet.
+ pj_sockaddr_in_init(&addr, pj_cstr(&temp, "127.0.0.1"), PORT);
+
+ // Test loop.
+ t_elapsed.u64 = 0;
+ for (i=0; i<LOOP; ++i) {
+ pj_ssize_t bytes;
+ pj_ioqueue_op_key_t read_op, write_op;
+
+ // Randomize send buffer.
+ pj_create_random_string(send_buf, bufsize);
+
+ // Start reading on the server side.
+ bytes = bufsize;
+ rc = pj_ioqueue_recv(skey, &read_op, recv_buf, &bytes, 0);
+ if (rc < 0 && rc != PJ_EPENDING) {
+ app_perror("...error: pj_ioqueue_read()", rc);
+ break;
+ }
+
+ // Starts send on the client side.
+ bytes = bufsize;
+ rc = pj_ioqueue_sendto(ckey, &write_op, send_buf, &bytes, 0,
+ &addr, sizeof(addr));
+ if (rc != PJ_SUCCESS && rc != PJ_EPENDING) {
+ app_perror("...error: pj_ioqueue_write()", bytes);
+ rc = -1;
+ break;
+ }
+
+ // Begin time.
+ pj_get_timestamp(&t1);
+
+ // Poll the queue until we've got completion event in the server side.
+ callback_read_key = NULL;
+ callback_read_size = 0;
+ do {
+ rc = pj_ioqueue_poll(ioque, NULL);
+ } while (rc >= 0 && callback_read_key != skey);
+
+ // End time.
+ pj_get_timestamp(&t2);
+ t_elapsed.u64 += (t2.u64 - t1.u64);
+
+ if (rc < 0)
+ break;
+
+ // Compare recv buffer with send buffer.
+ if (callback_read_size != bufsize ||
+ memcmp(send_buf, recv_buf, bufsize))
+ {
+ rc = -1;
+ break;
+ }
+
+ // Poll until all events are exhausted, before we start the next loop.
+ do {
+ pj_time_val timeout = { 0, 10 };
+ rc = pj_ioqueue_poll(ioque, &timeout);
+ } while (rc>0);
+
+ rc = 0;
+ }
+
+ // Print results
+ if (rc == 0) {
+ pj_timestamp tzero;
+ pj_uint32_t usec_delay;
+
+ tzero.u32.hi = tzero.u32.lo = 0;
+ usec_delay = pj_elapsed_usec( &tzero, &t_elapsed);
+
+ PJ_LOG(3, (THIS_FILE, "...%10d %15d % 9d",
+ bufsize, inactive_sock_count, usec_delay));
+
+ } else {
+ PJ_LOG(2, (THIS_FILE, "...ERROR (buf:%d, fds:%d)",
+ bufsize, inactive_sock_count+2));
+ }
+
+ // Cleaning up.
+ for (i=0; i<inactive_sock_count; ++i)
+ pj_sock_close(inactive_sock[i]);
+ pj_sock_close(ssock);
+ pj_sock_close(csock);
+
+ pj_ioqueue_destroy(ioque);
+ pj_pool_release( pool);
+ return 0;
+
+on_error:
+ PJ_LOG(1,(THIS_FILE, "...ERROR: %s",
+ pj_strerror(pj_get_netos_error(), errbuf, sizeof(errbuf))));
+ if (ssock)
+ pj_sock_close(ssock);
+ if (csock)
+ pj_sock_close(csock);
+ for (i=0; i<inactive_sock_count && inactive_sock &&
+ inactive_sock[i]!=PJ_INVALID_SOCKET; ++i)
+ {
+ pj_sock_close(inactive_sock[i]);
+ }
+ if (ioque != NULL)
+ pj_ioqueue_destroy(ioque);
+ pj_pool_release( pool);
+ return -1;
+}
+
+int udp_ioqueue_test()
+{
+ int status;
+ int bufsize, sock_count;
+
+ PJ_LOG(3, (THIS_FILE, "...compliance test (%s)", pj_ioqueue_name()));
+ if ((status=compliance_test()) != 0) {
+ return status;
+ }
+ PJ_LOG(3, (THIS_FILE, "....compliance test ok"));
+
+ if ((status=many_handles_test()) != 0) {
+ return status;
+ }
+
+ PJ_LOG(4, (THIS_FILE, "...benchmarking different buffer size:"));
+ PJ_LOG(4, (THIS_FILE, "... note: buf=bytes sent, fds=# of fds, "
+ "elapsed=in timer ticks"));
+
+ PJ_LOG(3, (THIS_FILE, "...Benchmarking poll times for %s:", pj_ioqueue_name()));
+ PJ_LOG(3, (THIS_FILE, "...====================================="));
+ PJ_LOG(3, (THIS_FILE, "...Buf.size #inactive-socks Time/poll"));
+ PJ_LOG(3, (THIS_FILE, "... (bytes) (nanosec)"));
+ PJ_LOG(3, (THIS_FILE, "...====================================="));
+
+ for (bufsize=BUF_MIN_SIZE; bufsize <= BUF_MAX_SIZE; bufsize *= 2) {
+ if (bench_test(bufsize, SOCK_INACTIVE_MIN))
+ return -1;
+ }
+ bufsize = 512;
+ for (sock_count=SOCK_INACTIVE_MIN+2;
+ sock_count<=SOCK_INACTIVE_MAX+2;
+ sock_count *= 2)
+ {
+ //PJ_LOG(3,(THIS_FILE, "...testing with %d fds", sock_count));
+ if (bench_test(bufsize, sock_count-2))
+ return -1;
+ }
+ return 0;
+}
+
+#else
+/* To prevent warning about "translation unit is empty"
+ * when this test is disabled.
+ */
+int dummy_uiq_udp;
+#endif /* INCLUDE_UDP_IOQUEUE_TEST */
+
+
diff --git a/pjlib/src/pjlib-test/list.c b/pjlib/src/pjlib-test/list.c
index 4777a9fa..7aa638b0 100644
--- a/pjlib/src/pjlib-test/list.c
+++ b/pjlib/src/pjlib-test/list.c
@@ -1,225 +1,225 @@
-/* $Id$ */
-/*
- * Copyright (C)2003-2006 Benny Prijono <benny@prijono.org>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
-#include "test.h"
-
-/**
- * \page page_pjlib_list_test Test: Linked List
- *
- * This file provides implementation of \b list_test(). It tests the
- * functionality of the linked-list API.
- *
- * \section list_test_sec Scope of the Test
- *
- * API tested:
- * - pj_list_init()
- * - pj_list_insert_before()
- * - pj_list_insert_after()
- * - pj_list_merge_last()
- * - pj_list_empty()
- * - pj_list_insert_nodes_before()
- * - pj_list_erase()
- * - pj_list_find_node()
- * - pj_list_search()
- *
- *
- * This file is <b>pjlib-test/list.c</b>
- *
- * \include pjlib-test/list.c
- */
-
-#if INCLUDE_LIST_TEST
-
-#include <pjlib.h>
-
-typedef struct list_node
-{
- PJ_DECL_LIST_MEMBER(struct list_node);
- int value;
-} list_node;
-
-static int compare_node(void *value, const pj_list_type *nd)
-{
- list_node *node = (list_node*)nd;
- return ((long)value == node->value) ? 0 : -1;
-}
-
-#define PJ_SIGNED_ARRAY_SIZE(a) ((int)PJ_ARRAY_SIZE(a))
-
-int list_test()
-{
- list_node nodes[4]; // must be even number of nodes
- list_node list;
- list_node list2;
- list_node *p;
- int i; // don't change to unsigned!
-
- //
- // Test insert_before().
- //
- list.value = (unsigned)-1;
- pj_list_init(&list);
- for (i=0; i<PJ_SIGNED_ARRAY_SIZE(nodes); ++i) {
- nodes[i].value = i;
- pj_list_insert_before(&list, &nodes[i]);
- }
- // check.
- for (i=0, p=list.next; i<PJ_SIGNED_ARRAY_SIZE(nodes); ++i, p=p->next) {
- pj_assert(p->value == i);
- if (p->value != i) {
- return -1;
- }
- }
-
- //
- // Test insert_after()
- //
- pj_list_init(&list);
- for (i=PJ_SIGNED_ARRAY_SIZE(nodes)-1; i>=0; --i) {
- pj_list_insert_after(&list, &nodes[i]);
- }
- // check.
- for (i=0, p=list.next; i<PJ_SIGNED_ARRAY_SIZE(nodes); ++i, p=p->next) {
- pj_assert(p->value == i);
- if (p->value != i) {
- return -1;
- }
- }
-
- //
- // Test merge_last()
- //
- // Init lists
- pj_list_init(&list);
- pj_list_init(&list2);
- for (i=0; i<PJ_SIGNED_ARRAY_SIZE(nodes)/2; ++i) {
- pj_list_insert_before(&list, &nodes[i]);
- }
- for (i=PJ_SIGNED_ARRAY_SIZE(nodes)/2; i<PJ_SIGNED_ARRAY_SIZE(nodes); ++i) {
- pj_list_insert_before(&list2, &nodes[i]);
- }
- // merge
- pj_list_merge_last(&list, &list2);
- // check.
- for (i=0, p=list.next; i<PJ_SIGNED_ARRAY_SIZE(nodes); ++i, p=p->next) {
- pj_assert(p->value == i);
- if (p->value != i) {
- return -1;
- }
- }
- // check list is empty
- pj_assert( pj_list_empty(&list2) );
- if (!pj_list_empty(&list2)) {
- return -1;
- }
-
- //
- // Check merge_first()
- //
- pj_list_init(&list);
- pj_list_init(&list2);
- for (i=0; i<PJ_SIGNED_ARRAY_SIZE(nodes)/2; ++i) {
- pj_list_insert_before(&list, &nodes[i]);
- }
- for (i=PJ_SIGNED_ARRAY_SIZE(nodes)/2; i<PJ_SIGNED_ARRAY_SIZE(nodes); ++i) {
- pj_list_insert_before(&list2, &nodes[i]);
- }
- // merge
- pj_list_merge_first(&list2, &list);
- // check (list2).
- for (i=0, p=list2.next; i<PJ_SIGNED_ARRAY_SIZE(nodes); ++i, p=p->next) {
- pj_assert(p->value == i);
- if (p->value != i) {
- return -1;
- }
- }
- // check list is empty
- pj_assert( pj_list_empty(&list) );
- if (!pj_list_empty(&list)) {
- return -1;
- }
-
- //
- // Test insert_nodes_before()
- //
- // init list
- pj_list_init(&list);
- for (i=0; i<PJ_SIGNED_ARRAY_SIZE(nodes)/2; ++i) {
- pj_list_insert_before(&list, &nodes[i]);
- }
- // chain remaining nodes
- pj_list_init(&nodes[PJ_SIGNED_ARRAY_SIZE(nodes)/2]);
- for (i=PJ_SIGNED_ARRAY_SIZE(nodes)/2+1; i<PJ_SIGNED_ARRAY_SIZE(nodes); ++i) {
- pj_list_insert_before(&nodes[PJ_SIGNED_ARRAY_SIZE(nodes)/2], &nodes[i]);
- }
- // insert nodes
- pj_list_insert_nodes_before(&list, &nodes[PJ_SIGNED_ARRAY_SIZE(nodes)/2]);
- // check
- for (i=0, p=list.next; i<PJ_SIGNED_ARRAY_SIZE(nodes); ++i, p=p->next) {
- pj_assert(p->value == i);
- if (p->value != i) {
- return -1;
- }
- }
-
- // erase test.
- pj_list_init(&list);
- for (i=0; i<PJ_SIGNED_ARRAY_SIZE(nodes); ++i) {
- nodes[i].value = i;
- pj_list_insert_before(&list, &nodes[i]);
- }
- for (i=PJ_SIGNED_ARRAY_SIZE(nodes)-1; i>=0; --i) {
- int j;
- pj_list_erase(&nodes[i]);
- for (j=0, p=list.next; j<i; ++j, p=p->next) {
- pj_assert(p->value == j);
- if (p->value != j) {
- return -1;
- }
- }
- }
-
- // find and search
- pj_list_init(&list);
- for (i=0; i<PJ_SIGNED_ARRAY_SIZE(nodes); ++i) {
- nodes[i].value = i;
- pj_list_insert_before(&list, &nodes[i]);
- }
- for (i=0; i<PJ_SIGNED_ARRAY_SIZE(nodes); ++i) {
- p = (list_node*) pj_list_find_node(&list, &nodes[i]);
- pj_assert( p == &nodes[i] );
- if (p != &nodes[i]) {
- return -1;
- }
- p = (list_node*) pj_list_search(&list, (void*)(long)i, &compare_node);
- pj_assert( p == &nodes[i] );
- if (p != &nodes[i]) {
- return -1;
- }
- }
- return 0;
-}
-
-#else
-/* To prevent warning about "translation unit is empty"
- * when this test is disabled.
- */
-int dummy_list_test;
-#endif /* INCLUDE_LIST_TEST */
-
-
+/* $Id$ */
+/*
+ * Copyright (C)2003-2006 Benny Prijono <benny@prijono.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#include "test.h"
+
+/**
+ * \page page_pjlib_list_test Test: Linked List
+ *
+ * This file provides implementation of \b list_test(). It tests the
+ * functionality of the linked-list API.
+ *
+ * \section list_test_sec Scope of the Test
+ *
+ * API tested:
+ * - pj_list_init()
+ * - pj_list_insert_before()
+ * - pj_list_insert_after()
+ * - pj_list_merge_last()
+ * - pj_list_empty()
+ * - pj_list_insert_nodes_before()
+ * - pj_list_erase()
+ * - pj_list_find_node()
+ * - pj_list_search()
+ *
+ *
+ * This file is <b>pjlib-test/list.c</b>
+ *
+ * \include pjlib-test/list.c
+ */
+
+#if INCLUDE_LIST_TEST
+
+#include <pjlib.h>
+
+typedef struct list_node
+{
+ PJ_DECL_LIST_MEMBER(struct list_node);
+ int value;
+} list_node;
+
+static int compare_node(void *value, const pj_list_type *nd)
+{
+ list_node *node = (list_node*)nd;
+ return ((long)value == node->value) ? 0 : -1;
+}
+
+#define PJ_SIGNED_ARRAY_SIZE(a) ((int)PJ_ARRAY_SIZE(a))
+
+int list_test()
+{
+ list_node nodes[4]; // must be even number of nodes
+ list_node list;
+ list_node list2;
+ list_node *p;
+ int i; // don't change to unsigned!
+
+ //
+ // Test insert_before().
+ //
+ list.value = (unsigned)-1;
+ pj_list_init(&list);
+ for (i=0; i<PJ_SIGNED_ARRAY_SIZE(nodes); ++i) {
+ nodes[i].value = i;
+ pj_list_insert_before(&list, &nodes[i]);
+ }
+ // check.
+ for (i=0, p=list.next; i<PJ_SIGNED_ARRAY_SIZE(nodes); ++i, p=p->next) {
+ pj_assert(p->value == i);
+ if (p->value != i) {
+ return -1;
+ }
+ }
+
+ //
+ // Test insert_after()
+ //
+ pj_list_init(&list);
+ for (i=PJ_SIGNED_ARRAY_SIZE(nodes)-1; i>=0; --i) {
+ pj_list_insert_after(&list, &nodes[i]);
+ }
+ // check.
+ for (i=0, p=list.next; i<PJ_SIGNED_ARRAY_SIZE(nodes); ++i, p=p->next) {
+ pj_assert(p->value == i);
+ if (p->value != i) {
+ return -1;
+ }
+ }
+
+ //
+ // Test merge_last()
+ //
+ // Init lists
+ pj_list_init(&list);
+ pj_list_init(&list2);
+ for (i=0; i<PJ_SIGNED_ARRAY_SIZE(nodes)/2; ++i) {
+ pj_list_insert_before(&list, &nodes[i]);
+ }
+ for (i=PJ_SIGNED_ARRAY_SIZE(nodes)/2; i<PJ_SIGNED_ARRAY_SIZE(nodes); ++i) {
+ pj_list_insert_before(&list2, &nodes[i]);
+ }
+ // merge
+ pj_list_merge_last(&list, &list2);
+ // check.
+ for (i=0, p=list.next; i<PJ_SIGNED_ARRAY_SIZE(nodes); ++i, p=p->next) {
+ pj_assert(p->value == i);
+ if (p->value != i) {
+ return -1;
+ }
+ }
+ // check list is empty
+ pj_assert( pj_list_empty(&list2) );
+ if (!pj_list_empty(&list2)) {
+ return -1;
+ }
+
+ //
+ // Check merge_first()
+ //
+ pj_list_init(&list);
+ pj_list_init(&list2);
+ for (i=0; i<PJ_SIGNED_ARRAY_SIZE(nodes)/2; ++i) {
+ pj_list_insert_before(&list, &nodes[i]);
+ }
+ for (i=PJ_SIGNED_ARRAY_SIZE(nodes)/2; i<PJ_SIGNED_ARRAY_SIZE(nodes); ++i) {
+ pj_list_insert_before(&list2, &nodes[i]);
+ }
+ // merge
+ pj_list_merge_first(&list2, &list);
+ // check (list2).
+ for (i=0, p=list2.next; i<PJ_SIGNED_ARRAY_SIZE(nodes); ++i, p=p->next) {
+ pj_assert(p->value == i);
+ if (p->value != i) {
+ return -1;
+ }
+ }
+ // check list is empty
+ pj_assert( pj_list_empty(&list) );
+ if (!pj_list_empty(&list)) {
+ return -1;
+ }
+
+ //
+ // Test insert_nodes_before()
+ //
+ // init list
+ pj_list_init(&list);
+ for (i=0; i<PJ_SIGNED_ARRAY_SIZE(nodes)/2; ++i) {
+ pj_list_insert_before(&list, &nodes[i]);
+ }
+ // chain remaining nodes
+ pj_list_init(&nodes[PJ_SIGNED_ARRAY_SIZE(nodes)/2]);
+ for (i=PJ_SIGNED_ARRAY_SIZE(nodes)/2+1; i<PJ_SIGNED_ARRAY_SIZE(nodes); ++i) {
+ pj_list_insert_before(&nodes[PJ_SIGNED_ARRAY_SIZE(nodes)/2], &nodes[i]);
+ }
+ // insert nodes
+ pj_list_insert_nodes_before(&list, &nodes[PJ_SIGNED_ARRAY_SIZE(nodes)/2]);
+ // check
+ for (i=0, p=list.next; i<PJ_SIGNED_ARRAY_SIZE(nodes); ++i, p=p->next) {
+ pj_assert(p->value == i);
+ if (p->value != i) {
+ return -1;
+ }
+ }
+
+ // erase test.
+ pj_list_init(&list);
+ for (i=0; i<PJ_SIGNED_ARRAY_SIZE(nodes); ++i) {
+ nodes[i].value = i;
+ pj_list_insert_before(&list, &nodes[i]);
+ }
+ for (i=PJ_SIGNED_ARRAY_SIZE(nodes)-1; i>=0; --i) {
+ int j;
+ pj_list_erase(&nodes[i]);
+ for (j=0, p=list.next; j<i; ++j, p=p->next) {
+ pj_assert(p->value == j);
+ if (p->value != j) {
+ return -1;
+ }
+ }
+ }
+
+ // find and search
+ pj_list_init(&list);
+ for (i=0; i<PJ_SIGNED_ARRAY_SIZE(nodes); ++i) {
+ nodes[i].value = i;
+ pj_list_insert_before(&list, &nodes[i]);
+ }
+ for (i=0; i<PJ_SIGNED_ARRAY_SIZE(nodes); ++i) {
+ p = (list_node*) pj_list_find_node(&list, &nodes[i]);
+ pj_assert( p == &nodes[i] );
+ if (p != &nodes[i]) {
+ return -1;
+ }
+ p = (list_node*) pj_list_search(&list, (void*)(long)i, &compare_node);
+ pj_assert( p == &nodes[i] );
+ if (p != &nodes[i]) {
+ return -1;
+ }
+ }
+ return 0;
+}
+
+#else
+/* To prevent warning about "translation unit is empty"
+ * when this test is disabled.
+ */
+int dummy_list_test;
+#endif /* INCLUDE_LIST_TEST */
+
+
diff --git a/pjlib/src/pjlib-test/main.c b/pjlib/src/pjlib-test/main.c
index 89c0853c..fea4cd6f 100644
--- a/pjlib/src/pjlib-test/main.c
+++ b/pjlib/src/pjlib-test/main.c
@@ -1,93 +1,93 @@
-/* $Id$ */
-/*
- * Copyright (C)2003-2006 Benny Prijono <benny@prijono.org>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
-#include "test.h"
-
-#include <pj/string.h>
-#include <pj/sock.h>
-#include <pj/log.h>
-
-extern int param_echo_sock_type;
-extern const char *param_echo_server;
-extern int param_echo_port;
-
-
-//#if defined(PJ_WIN32) && PJ_WIN32!=0
-#if 0
-#include <windows.h>
-static void boost(void)
-{
- SetPriorityClass(GetCurrentProcess(), REALTIME_PRIORITY_CLASS);
-}
-#else
-#define boost()
-#endif
-
-#if defined(PJ_SUNOS) && PJ_SUNOS!=0
-#include <signal.h>
-static void init_signals()
-{
- struct sigaction act;
-
- memset(&act, 0, sizeof(act));
- act.sa_handler = SIG_IGN;
-
- sigaction(SIGALRM, &act, NULL);
-}
-
-#else
-#define init_signals()
-#endif
-
-int main(int argc, char *argv[])
-{
- int rc;
-
- boost();
- init_signals();
-
- while (argc > 1) {
- char *arg = argv[--argc];
-
- if (*arg=='-' && *(arg+1)=='p') {
- pj_str_t port = pj_str(argv[--argc]);
-
- param_echo_port = pj_strtoul(&port);
-
- } else if (*arg=='-' && *(arg+1)=='s') {
- param_echo_server = argv[--argc];
-
- } else if (*arg=='-' && *(arg+1)=='t') {
- pj_str_t type = pj_str(argv[--argc]);
-
- if (pj_stricmp2(&type, "tcp")==0)
- param_echo_sock_type = PJ_SOCK_STREAM;
- else if (pj_stricmp2(&type, "udp")==0)
- param_echo_sock_type = PJ_SOCK_DGRAM;
- else {
- PJ_LOG(3,("", "error: unknown socket type %s", type.ptr));
- return 1;
- }
- }
- }
-
- rc = test_main();
-
- return rc;
-}
-
+/* $Id$ */
+/*
+ * Copyright (C)2003-2006 Benny Prijono <benny@prijono.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#include "test.h"
+
+#include <pj/string.h>
+#include <pj/sock.h>
+#include <pj/log.h>
+
+extern int param_echo_sock_type;
+extern const char *param_echo_server;
+extern int param_echo_port;
+
+
+//#if defined(PJ_WIN32) && PJ_WIN32!=0
+#if 0
+#include <windows.h>
+static void boost(void)
+{
+ SetPriorityClass(GetCurrentProcess(), REALTIME_PRIORITY_CLASS);
+}
+#else
+#define boost()
+#endif
+
+#if defined(PJ_SUNOS) && PJ_SUNOS!=0
+#include <signal.h>
+static void init_signals()
+{
+ struct sigaction act;
+
+ memset(&act, 0, sizeof(act));
+ act.sa_handler = SIG_IGN;
+
+ sigaction(SIGALRM, &act, NULL);
+}
+
+#else
+#define init_signals()
+#endif
+
+int main(int argc, char *argv[])
+{
+ int rc;
+
+ boost();
+ init_signals();
+
+ while (argc > 1) {
+ char *arg = argv[--argc];
+
+ if (*arg=='-' && *(arg+1)=='p') {
+ pj_str_t port = pj_str(argv[--argc]);
+
+ param_echo_port = pj_strtoul(&port);
+
+ } else if (*arg=='-' && *(arg+1)=='s') {
+ param_echo_server = argv[--argc];
+
+ } else if (*arg=='-' && *(arg+1)=='t') {
+ pj_str_t type = pj_str(argv[--argc]);
+
+ if (pj_stricmp2(&type, "tcp")==0)
+ param_echo_sock_type = PJ_SOCK_STREAM;
+ else if (pj_stricmp2(&type, "udp")==0)
+ param_echo_sock_type = PJ_SOCK_DGRAM;
+ else {
+ PJ_LOG(3,("", "error: unknown socket type %s", type.ptr));
+ return 1;
+ }
+ }
+ }
+
+ rc = test_main();
+
+ return rc;
+}
+
diff --git a/pjlib/src/pjlib-test/main_mod.c b/pjlib/src/pjlib-test/main_mod.c
index 2e7d8607..2dada340 100644
--- a/pjlib/src/pjlib-test/main_mod.c
+++ b/pjlib/src/pjlib-test/main_mod.c
@@ -1,39 +1,39 @@
-/* $Id$ */
-/*
- * Copyright (C)2003-2006 Benny Prijono <benny@prijono.org>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
-#include "test.h"
-#include <linux/module.h>
-#include <linux/kernel.h>
-
-int init_module(void)
-{
- printk(KERN_INFO "PJLIB test module loaded. Starting tests...\n");
-
- test_main();
-
- /* Prevent module from loading. We've finished test anyway.. */
- return 1;
-}
-
-void cleanup_module(void)
-{
- printk(KERN_INFO "PJLIB test module unloading...\n");
-}
-
-MODULE_LICENSE("GPL");
-
+/* $Id$ */
+/*
+ * Copyright (C)2003-2006 Benny Prijono <benny@prijono.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#include "test.h"
+#include <linux/module.h>
+#include <linux/kernel.h>
+
+int init_module(void)
+{
+ printk(KERN_INFO "PJLIB test module loaded. Starting tests...\n");
+
+ test_main();
+
+ /* Prevent module from loading. We've finished test anyway.. */
+ return 1;
+}
+
+void cleanup_module(void)
+{
+ printk(KERN_INFO "PJLIB test module unloading...\n");
+}
+
+MODULE_LICENSE("GPL");
+
diff --git a/pjlib/src/pjlib-test/mutex.c b/pjlib/src/pjlib-test/mutex.c
index 29fabd4c..877712d7 100644
--- a/pjlib/src/pjlib-test/mutex.c
+++ b/pjlib/src/pjlib-test/mutex.c
@@ -1,174 +1,174 @@
-/* $Id$ */
-/*
- * Copyright (C)2003-2006 Benny Prijono <benny@prijono.org>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
-#include "test.h"
-#include <pjlib.h>
-
-#if INCLUDE_MUTEX_TEST
-
-#undef TRACE_
-//#define TRACE_(x) PJ_LOG(3,x)
-#define TRACE_(x)
-
-/* Test witn non-recursive mutex. */
-static int simple_mutex_test(pj_pool_t *pool)
-{
- pj_status_t rc;
- pj_mutex_t *mutex;
-
- PJ_LOG(3,("", "...testing simple mutex"));
-
- /* Create mutex. */
- TRACE_(("", "....create mutex"));
- rc = pj_mutex_create( pool, "", PJ_MUTEX_SIMPLE, &mutex);
- if (rc != PJ_SUCCESS) {
- app_perror("...error: pj_mutex_create", rc);
- return -10;
- }
-
- /* Normal lock/unlock cycle. */
- TRACE_(("", "....lock mutex"));
- rc = pj_mutex_lock(mutex);
- if (rc != PJ_SUCCESS) {
- app_perror("...error: pj_mutex_lock", rc);
- return -20;
- }
- TRACE_(("", "....unlock mutex"));
- rc = pj_mutex_unlock(mutex);
- if (rc != PJ_SUCCESS) {
- app_perror("...error: pj_mutex_unlock", rc);
- return -30;
- }
-
- /* Lock again. */
- TRACE_(("", "....lock mutex"));
- rc = pj_mutex_lock(mutex);
- if (rc != PJ_SUCCESS) return -40;
-
- /* Try-lock should fail. It should not deadlocked. */
- TRACE_(("", "....trylock mutex"));
- rc = pj_mutex_trylock(mutex);
- if (rc == PJ_SUCCESS)
- PJ_LOG(3,("", "...info: looks like simple mutex is recursive"));
-
- /* Unlock and done. */
- TRACE_(("", "....unlock mutex"));
- rc = pj_mutex_unlock(mutex);
- if (rc != PJ_SUCCESS) return -50;
-
- TRACE_(("", "....destroy mutex"));
- rc = pj_mutex_destroy(mutex);
- if (rc != PJ_SUCCESS) return -60;
-
- TRACE_(("", "....done"));
- return PJ_SUCCESS;
-}
-
-
-/* Test with recursive mutex. */
-static int recursive_mutex_test(pj_pool_t *pool)
-{
- pj_status_t rc;
- pj_mutex_t *mutex;
-
- PJ_LOG(3,("", "...testing recursive mutex"));
-
- /* Create mutex. */
- TRACE_(("", "....create mutex"));
- rc = pj_mutex_create( pool, "", PJ_MUTEX_RECURSE, &mutex);
- if (rc != PJ_SUCCESS) {
- app_perror("...error: pj_mutex_create", rc);
- return -10;
- }
-
- /* Normal lock/unlock cycle. */
- TRACE_(("", "....lock mutex"));
- rc = pj_mutex_lock(mutex);
- if (rc != PJ_SUCCESS) {
- app_perror("...error: pj_mutex_lock", rc);
- return -20;
- }
- TRACE_(("", "....unlock mutex"));
- rc = pj_mutex_unlock(mutex);
- if (rc != PJ_SUCCESS) {
- app_perror("...error: pj_mutex_unlock", rc);
- return -30;
- }
-
- /* Lock again. */
- TRACE_(("", "....lock mutex"));
- rc = pj_mutex_lock(mutex);
- if (rc != PJ_SUCCESS) return -40;
-
- /* Try-lock should NOT fail. . */
- TRACE_(("", "....trylock mutex"));
- rc = pj_mutex_trylock(mutex);
- if (rc != PJ_SUCCESS) {
- app_perror("...error: recursive mutex is not recursive!", rc);
- return -40;
- }
-
- /* Locking again should not fail. */
- TRACE_(("", "....lock mutex"));
- rc = pj_mutex_lock(mutex);
- if (rc != PJ_SUCCESS) {
- app_perror("...error: recursive mutex is not recursive!", rc);
- return -45;
- }
-
- /* Unlock several times and done. */
- TRACE_(("", "....unlock mutex 3x"));
- rc = pj_mutex_unlock(mutex);
- if (rc != PJ_SUCCESS) return -50;
- rc = pj_mutex_unlock(mutex);
- if (rc != PJ_SUCCESS) return -51;
- rc = pj_mutex_unlock(mutex);
- if (rc != PJ_SUCCESS) return -52;
-
- TRACE_(("", "....destroy mutex"));
- rc = pj_mutex_destroy(mutex);
- if (rc != PJ_SUCCESS) return -60;
-
- TRACE_(("", "....done"));
- return PJ_SUCCESS;
-}
-
-int mutex_test(void)
-{
- pj_pool_t *pool;
- int rc;
-
- pool = pj_pool_create(mem, "", 4000, 4000, NULL);
-
- rc = simple_mutex_test(pool);
- if (rc != 0)
- return rc;
-
- rc = recursive_mutex_test(pool);
- if (rc != 0)
- return rc;
-
- pj_pool_release(pool);
-
- return 0;
-}
-
-#else
-int dummy_mutex_test;
-#endif
-
+/* $Id$ */
+/*
+ * Copyright (C)2003-2006 Benny Prijono <benny@prijono.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#include "test.h"
+#include <pjlib.h>
+
+#if INCLUDE_MUTEX_TEST
+
+#undef TRACE_
+//#define TRACE_(x) PJ_LOG(3,x)
+#define TRACE_(x)
+
+/* Test witn non-recursive mutex. */
+static int simple_mutex_test(pj_pool_t *pool)
+{
+ pj_status_t rc;
+ pj_mutex_t *mutex;
+
+ PJ_LOG(3,("", "...testing simple mutex"));
+
+ /* Create mutex. */
+ TRACE_(("", "....create mutex"));
+ rc = pj_mutex_create( pool, "", PJ_MUTEX_SIMPLE, &mutex);
+ if (rc != PJ_SUCCESS) {
+ app_perror("...error: pj_mutex_create", rc);
+ return -10;
+ }
+
+ /* Normal lock/unlock cycle. */
+ TRACE_(("", "....lock mutex"));
+ rc = pj_mutex_lock(mutex);
+ if (rc != PJ_SUCCESS) {
+ app_perror("...error: pj_mutex_lock", rc);
+ return -20;
+ }
+ TRACE_(("", "....unlock mutex"));
+ rc = pj_mutex_unlock(mutex);
+ if (rc != PJ_SUCCESS) {
+ app_perror("...error: pj_mutex_unlock", rc);
+ return -30;
+ }
+
+ /* Lock again. */
+ TRACE_(("", "....lock mutex"));
+ rc = pj_mutex_lock(mutex);
+ if (rc != PJ_SUCCESS) return -40;
+
+ /* Try-lock should fail. It should not deadlocked. */
+ TRACE_(("", "....trylock mutex"));
+ rc = pj_mutex_trylock(mutex);
+ if (rc == PJ_SUCCESS)
+ PJ_LOG(3,("", "...info: looks like simple mutex is recursive"));
+
+ /* Unlock and done. */
+ TRACE_(("", "....unlock mutex"));
+ rc = pj_mutex_unlock(mutex);
+ if (rc != PJ_SUCCESS) return -50;
+
+ TRACE_(("", "....destroy mutex"));
+ rc = pj_mutex_destroy(mutex);
+ if (rc != PJ_SUCCESS) return -60;
+
+ TRACE_(("", "....done"));
+ return PJ_SUCCESS;
+}
+
+
+/* Test with recursive mutex. */
+static int recursive_mutex_test(pj_pool_t *pool)
+{
+ pj_status_t rc;
+ pj_mutex_t *mutex;
+
+ PJ_LOG(3,("", "...testing recursive mutex"));
+
+ /* Create mutex. */
+ TRACE_(("", "....create mutex"));
+ rc = pj_mutex_create( pool, "", PJ_MUTEX_RECURSE, &mutex);
+ if (rc != PJ_SUCCESS) {
+ app_perror("...error: pj_mutex_create", rc);
+ return -10;
+ }
+
+ /* Normal lock/unlock cycle. */
+ TRACE_(("", "....lock mutex"));
+ rc = pj_mutex_lock(mutex);
+ if (rc != PJ_SUCCESS) {
+ app_perror("...error: pj_mutex_lock", rc);
+ return -20;
+ }
+ TRACE_(("", "....unlock mutex"));
+ rc = pj_mutex_unlock(mutex);
+ if (rc != PJ_SUCCESS) {
+ app_perror("...error: pj_mutex_unlock", rc);
+ return -30;
+ }
+
+ /* Lock again. */
+ TRACE_(("", "....lock mutex"));
+ rc = pj_mutex_lock(mutex);
+ if (rc != PJ_SUCCESS) return -40;
+
+ /* Try-lock should NOT fail. . */
+ TRACE_(("", "....trylock mutex"));
+ rc = pj_mutex_trylock(mutex);
+ if (rc != PJ_SUCCESS) {
+ app_perror("...error: recursive mutex is not recursive!", rc);
+ return -40;
+ }
+
+ /* Locking again should not fail. */
+ TRACE_(("", "....lock mutex"));
+ rc = pj_mutex_lock(mutex);
+ if (rc != PJ_SUCCESS) {
+ app_perror("...error: recursive mutex is not recursive!", rc);
+ return -45;
+ }
+
+ /* Unlock several times and done. */
+ TRACE_(("", "....unlock mutex 3x"));
+ rc = pj_mutex_unlock(mutex);
+ if (rc != PJ_SUCCESS) return -50;
+ rc = pj_mutex_unlock(mutex);
+ if (rc != PJ_SUCCESS) return -51;
+ rc = pj_mutex_unlock(mutex);
+ if (rc != PJ_SUCCESS) return -52;
+
+ TRACE_(("", "....destroy mutex"));
+ rc = pj_mutex_destroy(mutex);
+ if (rc != PJ_SUCCESS) return -60;
+
+ TRACE_(("", "....done"));
+ return PJ_SUCCESS;
+}
+
+int mutex_test(void)
+{
+ pj_pool_t *pool;
+ int rc;
+
+ pool = pj_pool_create(mem, "", 4000, 4000, NULL);
+
+ rc = simple_mutex_test(pool);
+ if (rc != 0)
+ return rc;
+
+ rc = recursive_mutex_test(pool);
+ if (rc != 0)
+ return rc;
+
+ pj_pool_release(pool);
+
+ return 0;
+}
+
+#else
+int dummy_mutex_test;
+#endif
+
diff --git a/pjlib/src/pjlib-test/os.c b/pjlib/src/pjlib-test/os.c
index 2d1b2693..7f3c1614 100644
--- a/pjlib/src/pjlib-test/os.c
+++ b/pjlib/src/pjlib-test/os.c
@@ -1,19 +1,19 @@
-/* $Id$ */
-/*
- * Copyright (C)2003-2006 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
- */
-int dummy_os_var;
+/* $Id$ */
+/*
+ * Copyright (C)2003-2006 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
+ */
+int dummy_os_var;
diff --git a/pjlib/src/pjlib-test/pool.c b/pjlib/src/pjlib-test/pool.c
index b20e63a5..d48a3589 100644
--- a/pjlib/src/pjlib-test/pool.c
+++ b/pjlib/src/pjlib-test/pool.c
@@ -1,172 +1,172 @@
-/* $Id$ */
-/*
- * Copyright (C)2003-2006 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 <pj/pool.h>
-#include <pj/rand.h>
-#include <pj/log.h>
-#include "test.h"
-
-/**
- * \page page_pjlib_pool_test Test: Pool
- *
- * This file provides implementation of \b pool_test(). It tests the
- * functionality of the memory pool.
- *
- *
- * This file is <b>pjlib-test/pool.c</b>
- *
- * \include pjlib-test/pool.c
- */
-
-
-#if INCLUDE_POOL_TEST
-
-#define SIZE 4096
-
-/* Normally we should throw exception when memory alloc fails.
- * Here we do nothing so that the flow will go back to original caller,
- * which will test the result using NULL comparison. Normally caller will
- * catch the exception instead of checking for NULLs.
- */
-static void null_callback(pj_pool_t *pool, pj_size_t size)
-{
- PJ_UNUSED_ARG(pool);
- PJ_UNUSED_ARG(size);
-}
-
-#define GET_FREE(p) (pj_pool_get_capacity(p)-pj_pool_get_used_size(p))
-
-/* Test that the capacity and used size reported by the pool is correct.
- */
-static int capacity_test(void)
-{
- pj_pool_t *pool = pj_pool_create(mem, NULL, SIZE, 0, &null_callback);
- pj_size_t freesize;
-
- PJ_LOG(3,("test", "...capacity_test()"));
-
- if (!pool)
- return -200;
-
- freesize = GET_FREE(pool);
-
- if (pj_pool_alloc(pool, freesize) == NULL) {
- PJ_LOG(3,("test", "...error: wrong freesize %u reported",
- freesize));
- pj_pool_release(pool);
- return -210;
- }
-
- pj_pool_release(pool);
- return 0;
-}
-
-/* Test function to drain the pool's space.
- */
-static int drain_test(pj_size_t size, pj_size_t increment)
-{
- pj_pool_t *pool = pj_pool_create(mem, NULL, size, increment,
- &null_callback);
- pj_size_t freesize;
- void *p;
- int status = 0;
-
- PJ_LOG(3,("test", "...drain_test(%d,%d)", size, increment));
-
- if (!pool)
- return -10;
-
- /* Get free size */
- freesize = GET_FREE(pool);
- if (freesize < 1) {
- status=-15;
- goto on_error;
- }
-
- /* Drain the pool until there's nothing left. */
- while (freesize > 0) {
- int size;
-
- if (freesize > 255)
- size = ((pj_rand() & 0x000000FF) + 4) & ~0x03L;
- else
- size = freesize;
-
- p = pj_pool_alloc(pool, size);
- if (!p) {
- status=-20; goto on_error;
- }
-
- freesize -= size;
- }
-
- /* Check that capacity is zero. */
- if (GET_FREE(pool) != 0) {
- PJ_LOG(3,("test", "....error: returned free=%u (expecting 0)",
- GET_FREE(pool)));
- status=-30; goto on_error;
- }
-
- /* Try to allocate once more */
- p = pj_pool_alloc(pool, 257);
- if (!p) {
- status=-40; goto on_error;
- }
-
- /* Check that capacity is NOT zero. */
- if (GET_FREE(pool) == 0) {
- status=-50; goto on_error;
- }
-
-
-on_error:
- pj_pool_release(pool);
- return status;
-}
-
-int pool_test(void)
-{
- enum { LOOP = 2 };
- int loop;
- int rc;
-
- rc = capacity_test();
- if (rc) return rc;
-
- for (loop=0; loop<LOOP; ++loop) {
- /* Test that the pool should grow automaticly. */
- rc = drain_test(SIZE, SIZE);
- if (rc != 0) return rc;
-
- /* Test situation where pool is not allowed to grow.
- * We expect the test to return correct error.
- */
- rc = drain_test(SIZE, 0);
- if (rc != -40) return rc;
- }
-
- return 0;
-}
-
-#else
-/* To prevent warning about "translation unit is empty"
- * when this test is disabled.
- */
-int dummy_pool_test;
-#endif /* INCLUDE_POOL_TEST */
-
+/* $Id$ */
+/*
+ * Copyright (C)2003-2006 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 <pj/pool.h>
+#include <pj/rand.h>
+#include <pj/log.h>
+#include "test.h"
+
+/**
+ * \page page_pjlib_pool_test Test: Pool
+ *
+ * This file provides implementation of \b pool_test(). It tests the
+ * functionality of the memory pool.
+ *
+ *
+ * This file is <b>pjlib-test/pool.c</b>
+ *
+ * \include pjlib-test/pool.c
+ */
+
+
+#if INCLUDE_POOL_TEST
+
+#define SIZE 4096
+
+/* Normally we should throw exception when memory alloc fails.
+ * Here we do nothing so that the flow will go back to original caller,
+ * which will test the result using NULL comparison. Normally caller will
+ * catch the exception instead of checking for NULLs.
+ */
+static void null_callback(pj_pool_t *pool, pj_size_t size)
+{
+ PJ_UNUSED_ARG(pool);
+ PJ_UNUSED_ARG(size);
+}
+
+#define GET_FREE(p) (pj_pool_get_capacity(p)-pj_pool_get_used_size(p))
+
+/* Test that the capacity and used size reported by the pool is correct.
+ */
+static int capacity_test(void)
+{
+ pj_pool_t *pool = pj_pool_create(mem, NULL, SIZE, 0, &null_callback);
+ pj_size_t freesize;
+
+ PJ_LOG(3,("test", "...capacity_test()"));
+
+ if (!pool)
+ return -200;
+
+ freesize = GET_FREE(pool);
+
+ if (pj_pool_alloc(pool, freesize) == NULL) {
+ PJ_LOG(3,("test", "...error: wrong freesize %u reported",
+ freesize));
+ pj_pool_release(pool);
+ return -210;
+ }
+
+ pj_pool_release(pool);
+ return 0;
+}
+
+/* Test function to drain the pool's space.
+ */
+static int drain_test(pj_size_t size, pj_size_t increment)
+{
+ pj_pool_t *pool = pj_pool_create(mem, NULL, size, increment,
+ &null_callback);
+ pj_size_t freesize;
+ void *p;
+ int status = 0;
+
+ PJ_LOG(3,("test", "...drain_test(%d,%d)", size, increment));
+
+ if (!pool)
+ return -10;
+
+ /* Get free size */
+ freesize = GET_FREE(pool);
+ if (freesize < 1) {
+ status=-15;
+ goto on_error;
+ }
+
+ /* Drain the pool until there's nothing left. */
+ while (freesize > 0) {
+ int size;
+
+ if (freesize > 255)
+ size = ((pj_rand() & 0x000000FF) + 4) & ~0x03L;
+ else
+ size = freesize;
+
+ p = pj_pool_alloc(pool, size);
+ if (!p) {
+ status=-20; goto on_error;
+ }
+
+ freesize -= size;
+ }
+
+ /* Check that capacity is zero. */
+ if (GET_FREE(pool) != 0) {
+ PJ_LOG(3,("test", "....error: returned free=%u (expecting 0)",
+ GET_FREE(pool)));
+ status=-30; goto on_error;
+ }
+
+ /* Try to allocate once more */
+ p = pj_pool_alloc(pool, 257);
+ if (!p) {
+ status=-40; goto on_error;
+ }
+
+ /* Check that capacity is NOT zero. */
+ if (GET_FREE(pool) == 0) {
+ status=-50; goto on_error;
+ }
+
+
+on_error:
+ pj_pool_release(pool);
+ return status;
+}
+
+int pool_test(void)
+{
+ enum { LOOP = 2 };
+ int loop;
+ int rc;
+
+ rc = capacity_test();
+ if (rc) return rc;
+
+ for (loop=0; loop<LOOP; ++loop) {
+ /* Test that the pool should grow automaticly. */
+ rc = drain_test(SIZE, SIZE);
+ if (rc != 0) return rc;
+
+ /* Test situation where pool is not allowed to grow.
+ * We expect the test to return correct error.
+ */
+ rc = drain_test(SIZE, 0);
+ if (rc != -40) return rc;
+ }
+
+ return 0;
+}
+
+#else
+/* To prevent warning about "translation unit is empty"
+ * when this test is disabled.
+ */
+int dummy_pool_test;
+#endif /* INCLUDE_POOL_TEST */
+
diff --git a/pjlib/src/pjlib-test/pool_perf.c b/pjlib/src/pjlib-test/pool_perf.c
index a7c8fba9..6bc6dcfb 100644
--- a/pjlib/src/pjlib-test/pool_perf.c
+++ b/pjlib/src/pjlib-test/pool_perf.c
@@ -1,140 +1,140 @@
-/* $Id$ */
-/*
- * Copyright (C)2003-2006 Benny Prijono <benny@prijono.org>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
-#include "test.h"
-
-#if INCLUDE_POOL_PERF_TEST
-
-#include <pjlib.h>
-#include <pj/compat/malloc.h>
-
-#if !PJ_HAS_HIGH_RES_TIMER
-# error Need high resolution timer for this test.
-#endif
-
-#define THIS_FILE "test"
-
-#define LOOP 10
-#define COUNT 1024
-static unsigned sizes[COUNT];
-#define MIN_SIZE 4
-#define MAX_SIZE 512
-static unsigned total_size;
-
-static int pool_test_pool()
-{
- int i;
- pj_pool_t *pool = pj_pool_create(mem, NULL, total_size + 4*COUNT, 0, NULL);
- if (!pool)
- return -1;
-
- for (i=0; i<COUNT; ++i) {
- char *p;
- if ( (p=(char*)pj_pool_alloc(pool, sizes[i])) == NULL)
- return -1;
- *p = '\0';
- }
-
- pj_pool_release(pool);
- return 0;
-}
-
-static int pool_test_malloc_free()
-{
- char *p[COUNT];
- int i;
-
- for (i=0; i<COUNT; ++i) {
- p[i] = (char*)malloc(sizes[i]);
- if (!p[i]) {
- // Don't care for memory leak in this test
- return -1;
- }
- *p[i] = '\0';
- }
-
- for (i=0; i<COUNT; ++i) {
- free(p[i]);
- }
-
- return 0;
-}
-
-int pool_perf_test()
-{
- unsigned i;
- pj_uint32_t pool_time=0, malloc_time=0, pool_time2=0;
- pj_timestamp start, end;
- pj_uint32_t best, worst;
-
- // Initialize sizes.
- for (i=0; i<COUNT; ++i) {
- sizes[i] = MIN_SIZE + pj_rand() % MAX_SIZE;
- total_size += sizes[i];
- }
-
- PJ_LOG(3, (THIS_FILE, "Benchmarking pool.."));
-
- // Warmup
- pool_test_pool();
- pool_test_malloc_free();
-
- for (i=0; i<LOOP; ++i) {
- pj_get_timestamp(&start);
- if (pool_test_pool()) {
- return 1;
- }
- pj_get_timestamp(&end);
- pool_time += (end.u32.lo - start.u32.lo);
-
- pj_get_timestamp(&start);
- if (pool_test_malloc_free()) {
- return 2;
- }
- pj_get_timestamp(&end);
- malloc_time += (end.u32.lo - start.u32.lo);
-
- pj_get_timestamp(&start);
- if (pool_test_pool()) {
- return 4;
- }
- pj_get_timestamp(&end);
- pool_time2 += (end.u32.lo - start.u32.lo);
- }
-
- PJ_LOG(4, (THIS_FILE, "..LOOP count: %u", LOOP));
- PJ_LOG(4, (THIS_FILE, "..number of alloc/dealloc per loop: %u", COUNT));
- PJ_LOG(4, (THIS_FILE, "..pool allocation/deallocation time: %u", pool_time));
- PJ_LOG(4, (THIS_FILE, "..malloc/free time: %u", malloc_time));
- PJ_LOG(4, (THIS_FILE, "..pool again, second invocation: %u", pool_time2));
-
- if (pool_time2==0) pool_time2=1;
- if (pool_time < pool_time2)
- best = pool_time, worst = pool_time2;
- else
- best = pool_time2, worst = pool_time;
-
- PJ_LOG(3, (THIS_FILE, "..malloc Speedup best=%dx, worst=%dx",
- (int)(malloc_time/best),
- (int)(malloc_time/worst)));
- return 0;
-}
-
-
-#endif /* INCLUDE_POOL_PERF_TEST */
-
+/* $Id$ */
+/*
+ * Copyright (C)2003-2006 Benny Prijono <benny@prijono.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#include "test.h"
+
+#if INCLUDE_POOL_PERF_TEST
+
+#include <pjlib.h>
+#include <pj/compat/malloc.h>
+
+#if !PJ_HAS_HIGH_RES_TIMER
+# error Need high resolution timer for this test.
+#endif
+
+#define THIS_FILE "test"
+
+#define LOOP 10
+#define COUNT 1024
+static unsigned sizes[COUNT];
+#define MIN_SIZE 4
+#define MAX_SIZE 512
+static unsigned total_size;
+
+static int pool_test_pool()
+{
+ int i;
+ pj_pool_t *pool = pj_pool_create(mem, NULL, total_size + 4*COUNT, 0, NULL);
+ if (!pool)
+ return -1;
+
+ for (i=0; i<COUNT; ++i) {
+ char *p;
+ if ( (p=(char*)pj_pool_alloc(pool, sizes[i])) == NULL)
+ return -1;
+ *p = '\0';
+ }
+
+ pj_pool_release(pool);
+ return 0;
+}
+
+static int pool_test_malloc_free()
+{
+ char *p[COUNT];
+ int i;
+
+ for (i=0; i<COUNT; ++i) {
+ p[i] = (char*)malloc(sizes[i]);
+ if (!p[i]) {
+ // Don't care for memory leak in this test
+ return -1;
+ }
+ *p[i] = '\0';
+ }
+
+ for (i=0; i<COUNT; ++i) {
+ free(p[i]);
+ }
+
+ return 0;
+}
+
+int pool_perf_test()
+{
+ unsigned i;
+ pj_uint32_t pool_time=0, malloc_time=0, pool_time2=0;
+ pj_timestamp start, end;
+ pj_uint32_t best, worst;
+
+ // Initialize sizes.
+ for (i=0; i<COUNT; ++i) {
+ sizes[i] = MIN_SIZE + pj_rand() % MAX_SIZE;
+ total_size += sizes[i];
+ }
+
+ PJ_LOG(3, (THIS_FILE, "Benchmarking pool.."));
+
+ // Warmup
+ pool_test_pool();
+ pool_test_malloc_free();
+
+ for (i=0; i<LOOP; ++i) {
+ pj_get_timestamp(&start);
+ if (pool_test_pool()) {
+ return 1;
+ }
+ pj_get_timestamp(&end);
+ pool_time += (end.u32.lo - start.u32.lo);
+
+ pj_get_timestamp(&start);
+ if (pool_test_malloc_free()) {
+ return 2;
+ }
+ pj_get_timestamp(&end);
+ malloc_time += (end.u32.lo - start.u32.lo);
+
+ pj_get_timestamp(&start);
+ if (pool_test_pool()) {
+ return 4;
+ }
+ pj_get_timestamp(&end);
+ pool_time2 += (end.u32.lo - start.u32.lo);
+ }
+
+ PJ_LOG(4, (THIS_FILE, "..LOOP count: %u", LOOP));
+ PJ_LOG(4, (THIS_FILE, "..number of alloc/dealloc per loop: %u", COUNT));
+ PJ_LOG(4, (THIS_FILE, "..pool allocation/deallocation time: %u", pool_time));
+ PJ_LOG(4, (THIS_FILE, "..malloc/free time: %u", malloc_time));
+ PJ_LOG(4, (THIS_FILE, "..pool again, second invocation: %u", pool_time2));
+
+ if (pool_time2==0) pool_time2=1;
+ if (pool_time < pool_time2)
+ best = pool_time, worst = pool_time2;
+ else
+ best = pool_time2, worst = pool_time;
+
+ PJ_LOG(3, (THIS_FILE, "..malloc Speedup best=%dx, worst=%dx",
+ (int)(malloc_time/best),
+ (int)(malloc_time/worst)));
+ return 0;
+}
+
+
+#endif /* INCLUDE_POOL_PERF_TEST */
+
diff --git a/pjlib/src/pjlib-test/rand.c b/pjlib/src/pjlib-test/rand.c
index 33e81075..6edfbd21 100644
--- a/pjlib/src/pjlib-test/rand.c
+++ b/pjlib/src/pjlib-test/rand.c
@@ -1,53 +1,53 @@
-/* $Id$ */
-/*
- * Copyright (C)2003-2006 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 <pj/rand.h>
-#include <pj/log.h>
-#include "test.h"
-
-#if INCLUDE_RAND_TEST
-
-#define COUNT 1024
-static int values[COUNT];
-
-/*
- * rand_test(), simply generates COUNT number of random number and
- * check that there's no duplicate numbers.
- */
-int rand_test(void)
-{
- int i;
-
- for (i=0; i<COUNT; ++i) {
- int j;
-
- values[i] = pj_rand();
- for (j=0; j<i; ++j) {
- if (values[i] == values[j]) {
- PJ_LOG(3,("test", "error: duplicate value %d at %d-th index",
- values[i], i));
- return -10;
- }
- }
- }
-
- return 0;
-}
-
-#endif /* INCLUDE_RAND_TEST */
-
+/* $Id$ */
+/*
+ * Copyright (C)2003-2006 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 <pj/rand.h>
+#include <pj/log.h>
+#include "test.h"
+
+#if INCLUDE_RAND_TEST
+
+#define COUNT 1024
+static int values[COUNT];
+
+/*
+ * rand_test(), simply generates COUNT number of random number and
+ * check that there's no duplicate numbers.
+ */
+int rand_test(void)
+{
+ int i;
+
+ for (i=0; i<COUNT; ++i) {
+ int j;
+
+ values[i] = pj_rand();
+ for (j=0; j<i; ++j) {
+ if (values[i] == values[j]) {
+ PJ_LOG(3,("test", "error: duplicate value %d at %d-th index",
+ values[i], i));
+ return -10;
+ }
+ }
+ }
+
+ return 0;
+}
+
+#endif /* INCLUDE_RAND_TEST */
+
diff --git a/pjlib/src/pjlib-test/rbtree.c b/pjlib/src/pjlib-test/rbtree.c
index 588e58a7..dc10218d 100644
--- a/pjlib/src/pjlib-test/rbtree.c
+++ b/pjlib/src/pjlib-test/rbtree.c
@@ -1,167 +1,167 @@
-/* $Id$ */
-/*
- * Copyright (C)2003-2006 Benny Prijono <benny@prijono.org>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
-#include "test.h"
-
-#if INCLUDE_RBTREE_TEST
-
-#include <pjlib.h>
-
-#define LOOP 32
-#define MIN_COUNT 64
-#define MAX_COUNT (LOOP * MIN_COUNT)
-#define STRSIZE 16
-#define THIS_FILE "rbtree_test"
-
-typedef struct node_key
-{
- pj_uint32_t hash;
- char str[STRSIZE];
-} node_key;
-
-static int compare_node(const node_key *k1, const node_key *k2)
-{
- if (k1->hash == k2->hash) {
- return strcmp(k1->str, k2->str);
- } else {
- return k1->hash < k2->hash ? -1 : 1;
- }
-}
-
-void randomize_string(char *str, int len)
-{
- int i;
- for (i=0; i<len-1; ++i)
- str[i] = (char)('a' + pj_rand() % 26);
- str[len-1] = '\0';
-}
-
-static int test(void)
-{
- pj_rbtree rb;
- node_key *key;
- pj_rbtree_node *node;
- pj_pool_t *pool;
- int err=0;
- int count = MIN_COUNT;
- int i;
- unsigned size;
-
- pj_rbtree_init(&rb, (pj_rbtree_comp*)&compare_node);
- size = MAX_COUNT*(sizeof(*key)+PJ_RBTREE_NODE_SIZE) +
- PJ_RBTREE_SIZE + PJ_POOL_SIZE;
- pool = pj_pool_create( mem, "pool", size, 0, NULL);
- if (!pool) {
- PJ_LOG(3,("test", "...error: creating pool of %u bytes", size));
- return -10;
- }
-
- key = (node_key *)pj_pool_alloc(pool, MAX_COUNT*sizeof(*key));
- if (!key)
- return -20;
-
- node = (pj_rbtree_node*)pj_pool_alloc(pool, MAX_COUNT*sizeof(*node));
- if (!node)
- return -30;
-
- for (i=0; i<LOOP; ++i) {
- int j;
- pj_rbtree_node *prev, *it;
- pj_timestamp t1, t2, t_setup, t_insert, t_search, t_erase;
-
- pj_assert(rb.size == 0);
-
- t_setup.u32.lo = t_insert.u32.lo = t_search.u32.lo = t_erase.u32.lo = 0;
-
- for (j=0; j<count; j++) {
- randomize_string(key[j].str, STRSIZE);
-
- pj_get_timestamp(&t1);
- node[j].key = &key[j];
- node[j].user_data = key[j].str;
- key[j].hash = pj_hash_calc(0, key[j].str, PJ_HASH_KEY_STRING);
- pj_get_timestamp(&t2);
- t_setup.u32.lo += (t2.u32.lo - t1.u32.lo);
-
- pj_get_timestamp(&t1);
- pj_rbtree_insert(&rb, &node[j]);
- pj_get_timestamp(&t2);
- t_insert.u32.lo += (t2.u32.lo - t1.u32.lo);
- }
-
- pj_assert(rb.size == (unsigned)count);
-
- // Iterate key, make sure they're sorted.
- prev = NULL;
- it = pj_rbtree_first(&rb);
- while (it) {
- if (prev) {
- if (compare_node((node_key*)prev->key,(node_key*)it->key)>=0) {
- ++err;
- PJ_LOG(3, (THIS_FILE, "Error: %s >= %s",
- (char*)prev->user_data, (char*)it->user_data));
- }
- }
- prev = it;
- it = pj_rbtree_next(&rb, it);
- }
-
- // Search.
- for (j=0; j<count; j++) {
- pj_get_timestamp(&t1);
- it = pj_rbtree_find(&rb, &key[j]);
- pj_get_timestamp(&t2);
- t_search.u32.lo += (t2.u32.lo - t1.u32.lo);
-
- pj_assert(it != NULL);
- if (it == NULL)
- ++err;
- }
-
- // Erase node.
- for (j=0; j<count; j++) {
- pj_get_timestamp(&t1);
- it = pj_rbtree_erase(&rb, &node[j]);
- pj_get_timestamp(&t2);
- t_erase.u32.lo += (t2.u32.lo - t1.u32.lo);
- }
-
- PJ_LOG(4, (THIS_FILE,
- "...count:%d, setup:%d, insert:%d, search:%d, erase:%d",
- count,
- t_setup.u32.lo / count, t_insert.u32.lo / count,
- t_search.u32.lo / count, t_erase.u32.lo / count));
-
- count = 2 * count;
- if (count > MAX_COUNT)
- break;
- }
-
- pj_pool_release(pool);
- return err;
-}
-
-
-int rbtree_test()
-{
- return test();
-}
-
-#endif /* INCLUDE_RBTREE_TEST */
-
-
+/* $Id$ */
+/*
+ * Copyright (C)2003-2006 Benny Prijono <benny@prijono.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#include "test.h"
+
+#if INCLUDE_RBTREE_TEST
+
+#include <pjlib.h>
+
+#define LOOP 32
+#define MIN_COUNT 64
+#define MAX_COUNT (LOOP * MIN_COUNT)
+#define STRSIZE 16
+#define THIS_FILE "rbtree_test"
+
+typedef struct node_key
+{
+ pj_uint32_t hash;
+ char str[STRSIZE];
+} node_key;
+
+static int compare_node(const node_key *k1, const node_key *k2)
+{
+ if (k1->hash == k2->hash) {
+ return strcmp(k1->str, k2->str);
+ } else {
+ return k1->hash < k2->hash ? -1 : 1;
+ }
+}
+
+void randomize_string(char *str, int len)
+{
+ int i;
+ for (i=0; i<len-1; ++i)
+ str[i] = (char)('a' + pj_rand() % 26);
+ str[len-1] = '\0';
+}
+
+static int test(void)
+{
+ pj_rbtree rb;
+ node_key *key;
+ pj_rbtree_node *node;
+ pj_pool_t *pool;
+ int err=0;
+ int count = MIN_COUNT;
+ int i;
+ unsigned size;
+
+ pj_rbtree_init(&rb, (pj_rbtree_comp*)&compare_node);
+ size = MAX_COUNT*(sizeof(*key)+PJ_RBTREE_NODE_SIZE) +
+ PJ_RBTREE_SIZE + PJ_POOL_SIZE;
+ pool = pj_pool_create( mem, "pool", size, 0, NULL);
+ if (!pool) {
+ PJ_LOG(3,("test", "...error: creating pool of %u bytes", size));
+ return -10;
+ }
+
+ key = (node_key *)pj_pool_alloc(pool, MAX_COUNT*sizeof(*key));
+ if (!key)
+ return -20;
+
+ node = (pj_rbtree_node*)pj_pool_alloc(pool, MAX_COUNT*sizeof(*node));
+ if (!node)
+ return -30;
+
+ for (i=0; i<LOOP; ++i) {
+ int j;
+ pj_rbtree_node *prev, *it;
+ pj_timestamp t1, t2, t_setup, t_insert, t_search, t_erase;
+
+ pj_assert(rb.size == 0);
+
+ t_setup.u32.lo = t_insert.u32.lo = t_search.u32.lo = t_erase.u32.lo = 0;
+
+ for (j=0; j<count; j++) {
+ randomize_string(key[j].str, STRSIZE);
+
+ pj_get_timestamp(&t1);
+ node[j].key = &key[j];
+ node[j].user_data = key[j].str;
+ key[j].hash = pj_hash_calc(0, key[j].str, PJ_HASH_KEY_STRING);
+ pj_get_timestamp(&t2);
+ t_setup.u32.lo += (t2.u32.lo - t1.u32.lo);
+
+ pj_get_timestamp(&t1);
+ pj_rbtree_insert(&rb, &node[j]);
+ pj_get_timestamp(&t2);
+ t_insert.u32.lo += (t2.u32.lo - t1.u32.lo);
+ }
+
+ pj_assert(rb.size == (unsigned)count);
+
+ // Iterate key, make sure they're sorted.
+ prev = NULL;
+ it = pj_rbtree_first(&rb);
+ while (it) {
+ if (prev) {
+ if (compare_node((node_key*)prev->key,(node_key*)it->key)>=0) {
+ ++err;
+ PJ_LOG(3, (THIS_FILE, "Error: %s >= %s",
+ (char*)prev->user_data, (char*)it->user_data));
+ }
+ }
+ prev = it;
+ it = pj_rbtree_next(&rb, it);
+ }
+
+ // Search.
+ for (j=0; j<count; j++) {
+ pj_get_timestamp(&t1);
+ it = pj_rbtree_find(&rb, &key[j]);
+ pj_get_timestamp(&t2);
+ t_search.u32.lo += (t2.u32.lo - t1.u32.lo);
+
+ pj_assert(it != NULL);
+ if (it == NULL)
+ ++err;
+ }
+
+ // Erase node.
+ for (j=0; j<count; j++) {
+ pj_get_timestamp(&t1);
+ it = pj_rbtree_erase(&rb, &node[j]);
+ pj_get_timestamp(&t2);
+ t_erase.u32.lo += (t2.u32.lo - t1.u32.lo);
+ }
+
+ PJ_LOG(4, (THIS_FILE,
+ "...count:%d, setup:%d, insert:%d, search:%d, erase:%d",
+ count,
+ t_setup.u32.lo / count, t_insert.u32.lo / count,
+ t_search.u32.lo / count, t_erase.u32.lo / count));
+
+ count = 2 * count;
+ if (count > MAX_COUNT)
+ break;
+ }
+
+ pj_pool_release(pool);
+ return err;
+}
+
+
+int rbtree_test()
+{
+ return test();
+}
+
+#endif /* INCLUDE_RBTREE_TEST */
+
+
diff --git a/pjlib/src/pjlib-test/select.c b/pjlib/src/pjlib-test/select.c
index 44184730..ed2edd40 100644
--- a/pjlib/src/pjlib-test/select.c
+++ b/pjlib/src/pjlib-test/select.c
@@ -1,217 +1,217 @@
-/* $Id$ */
-/*
- * Copyright (C)2003-2006 Benny Prijono <benny@prijono.org>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
-#include "test.h"
-
-/**
- * \page page_pjlib_select_test Test: Socket Select()
- *
- * This file provides implementation of \b select_test(). It tests the
- * functionality of the pj_sock_select() API.
- *
- *
- * This file is <b>pjlib-test/select.c</b>
- *
- * \include pjlib-test/select.c
- */
-
-
-#if INCLUDE_SELECT_TEST
-
-#include <pj/sock.h>
-#include <pj/sock_select.h>
-#include <pj/log.h>
-#include <pj/string.h>
-#include <pj/assert.h>
-#include <pj/os.h>
-#include <pj/errno.h>
-
-enum
-{
- READ_FDS,
- WRITE_FDS,
- EXCEPT_FDS
-};
-
-#define UDP_PORT 51232
-#define THIS_FILE "select_test"
-
-/*
- * do_select()
- *
- * Perform pj_sock_select() and find out which sockets
- * are signalled.
- */
-static int do_select( pj_sock_t sock1, pj_sock_t sock2,
- int setcount[])
-{
- pj_fd_set_t fds[3];
- pj_time_val timeout;
- int i, n;
-
- for (i=0; i<3; ++i) {
- PJ_FD_ZERO(&fds[i]);
- PJ_FD_SET(sock1, &fds[i]);
- PJ_FD_SET(sock2, &fds[i]);
- setcount[i] = 0;
- }
-
- timeout.sec = 1;
- timeout.msec = 0;
-
- n = pj_sock_select(FD_SETSIZE, &fds[0], &fds[1], &fds[2],
- &timeout);
- if (n < 0)
- return n;
- if (n == 0)
- return 0;
-
- for (i=0; i<3; ++i) {
- if (PJ_FD_ISSET(sock1, &fds[i]))
- setcount[i]++;
- if (PJ_FD_ISSET(sock2, &fds[i]))
- setcount[i]++;
- }
-
- return n;
-}
-
-/*
- * select_test()
- *
- * Test main entry.
- */
-int select_test()
-{
- pj_sock_t udp1=PJ_INVALID_SOCKET, udp2=PJ_INVALID_SOCKET;
- pj_sockaddr_in udp_addr;
- int status;
- int setcount[3];
- pj_str_t s;
- const char data[] = "hello";
- const int datalen = 5;
- pj_ssize_t sent, received;
- char buf[10];
- pj_status_t rc;
-
- PJ_LOG(3, (THIS_FILE, "...Testing simple UDP select()"));
-
- // Create two UDP sockets.
- rc = pj_sock_socket( PJ_AF_INET, PJ_SOCK_DGRAM, 0, &udp1);
- if (rc != PJ_SUCCESS) {
- app_perror("...error: unable to create socket", rc);
- status=-10; goto on_return;
- }
- rc = pj_sock_socket( PJ_AF_INET, PJ_SOCK_DGRAM, 0, &udp2);
- if (udp2 == PJ_INVALID_SOCKET) {
- app_perror("...error: unable to create socket", rc);
- status=-20; goto on_return;
- }
-
- // Bind one of the UDP socket.
- pj_memset(&udp_addr, 0, sizeof(udp_addr));
- udp_addr.sin_family = PJ_AF_INET;
- udp_addr.sin_port = UDP_PORT;
- udp_addr.sin_addr = pj_inet_addr(pj_cstr(&s, "127.0.0.1"));
-
- if (pj_sock_bind(udp2, &udp_addr, sizeof(udp_addr))) {
- status=-30; goto on_return;
- }
-
- // Send data.
- sent = datalen;
- rc = pj_sock_sendto(udp1, data, &sent, 0, &udp_addr, sizeof(udp_addr));
- if (rc != PJ_SUCCESS || sent != datalen) {
- app_perror("...error: sendto() error", rc);
- status=-40; goto on_return;
- }
-
- // Check that socket is marked as reable.
- // Note that select() may also report that sockets are writable.
- status = do_select(udp1, udp2, setcount);
- if (status < 0) {
- char errbuf[128];
- pj_strerror(pj_get_netos_error(), errbuf, sizeof(errbuf));
- PJ_LOG(1,(THIS_FILE, "...error: %s", errbuf));
- status=-50; goto on_return;
- }
- if (status == 0) {
- status=-60; goto on_return;
- }
-
- if (setcount[READ_FDS] != 1) {
- status=-70; goto on_return;
- }
- if (setcount[WRITE_FDS] != 0) {
- if (setcount[WRITE_FDS] == 2) {
- PJ_LOG(3,(THIS_FILE, "...info: system reports writable sockets"));
- } else {
- status=-80; goto on_return;
- }
- } else {
- PJ_LOG(3,(THIS_FILE,
- "...info: system doesn't report writable sockets"));
- }
- if (setcount[EXCEPT_FDS] != 0) {
- status=-90; goto on_return;
- }
-
- // Read the socket to clear readable sockets.
- received = sizeof(buf);
- rc = pj_sock_recv(udp2, buf, &received, 0);
- if (rc != PJ_SUCCESS || received != 5) {
- status=-100; goto on_return;
- }
-
- status = 0;
-
- // Test timeout on the read part.
- // This won't necessarily return zero, as select() may report that
- // sockets are writable.
- setcount[0] = setcount[1] = setcount[2] = 0;
- status = do_select(udp1, udp2, setcount);
- if (status != 0 && status != setcount[WRITE_FDS]) {
- PJ_LOG(3,(THIS_FILE, "...error: expecting timeout but got %d sks set",
- status));
- PJ_LOG(3,(THIS_FILE, " rdset: %d, wrset: %d, exset: %d",
- setcount[0], setcount[1], setcount[2]));
- status = -110; goto on_return;
- }
- if (setcount[READ_FDS] != 0) {
- PJ_LOG(3,(THIS_FILE, "...error: readable socket not expected"));
- status = -120; goto on_return;
- }
-
- status = 0;
-
-on_return:
- if (udp1 != PJ_INVALID_SOCKET)
- pj_sock_close(udp1);
- if (udp2 != PJ_INVALID_SOCKET)
- pj_sock_close(udp2);
- return status;
-}
-
-#else
-/* To prevent warning about "translation unit is empty"
- * when this test is disabled.
- */
-int dummy_select_test;
-#endif /* INCLUDE_SELECT_TEST */
-
-
+/* $Id$ */
+/*
+ * Copyright (C)2003-2006 Benny Prijono <benny@prijono.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#include "test.h"
+
+/**
+ * \page page_pjlib_select_test Test: Socket Select()
+ *
+ * This file provides implementation of \b select_test(). It tests the
+ * functionality of the pj_sock_select() API.
+ *
+ *
+ * This file is <b>pjlib-test/select.c</b>
+ *
+ * \include pjlib-test/select.c
+ */
+
+
+#if INCLUDE_SELECT_TEST
+
+#include <pj/sock.h>
+#include <pj/sock_select.h>
+#include <pj/log.h>
+#include <pj/string.h>
+#include <pj/assert.h>
+#include <pj/os.h>
+#include <pj/errno.h>
+
+enum
+{
+ READ_FDS,
+ WRITE_FDS,
+ EXCEPT_FDS
+};
+
+#define UDP_PORT 51232
+#define THIS_FILE "select_test"
+
+/*
+ * do_select()
+ *
+ * Perform pj_sock_select() and find out which sockets
+ * are signalled.
+ */
+static int do_select( pj_sock_t sock1, pj_sock_t sock2,
+ int setcount[])
+{
+ pj_fd_set_t fds[3];
+ pj_time_val timeout;
+ int i, n;
+
+ for (i=0; i<3; ++i) {
+ PJ_FD_ZERO(&fds[i]);
+ PJ_FD_SET(sock1, &fds[i]);
+ PJ_FD_SET(sock2, &fds[i]);
+ setcount[i] = 0;
+ }
+
+ timeout.sec = 1;
+ timeout.msec = 0;
+
+ n = pj_sock_select(FD_SETSIZE, &fds[0], &fds[1], &fds[2],
+ &timeout);
+ if (n < 0)
+ return n;
+ if (n == 0)
+ return 0;
+
+ for (i=0; i<3; ++i) {
+ if (PJ_FD_ISSET(sock1, &fds[i]))
+ setcount[i]++;
+ if (PJ_FD_ISSET(sock2, &fds[i]))
+ setcount[i]++;
+ }
+
+ return n;
+}
+
+/*
+ * select_test()
+ *
+ * Test main entry.
+ */
+int select_test()
+{
+ pj_sock_t udp1=PJ_INVALID_SOCKET, udp2=PJ_INVALID_SOCKET;
+ pj_sockaddr_in udp_addr;
+ int status;
+ int setcount[3];
+ pj_str_t s;
+ const char data[] = "hello";
+ const int datalen = 5;
+ pj_ssize_t sent, received;
+ char buf[10];
+ pj_status_t rc;
+
+ PJ_LOG(3, (THIS_FILE, "...Testing simple UDP select()"));
+
+ // Create two UDP sockets.
+ rc = pj_sock_socket( PJ_AF_INET, PJ_SOCK_DGRAM, 0, &udp1);
+ if (rc != PJ_SUCCESS) {
+ app_perror("...error: unable to create socket", rc);
+ status=-10; goto on_return;
+ }
+ rc = pj_sock_socket( PJ_AF_INET, PJ_SOCK_DGRAM, 0, &udp2);
+ if (udp2 == PJ_INVALID_SOCKET) {
+ app_perror("...error: unable to create socket", rc);
+ status=-20; goto on_return;
+ }
+
+ // Bind one of the UDP socket.
+ pj_memset(&udp_addr, 0, sizeof(udp_addr));
+ udp_addr.sin_family = PJ_AF_INET;
+ udp_addr.sin_port = UDP_PORT;
+ udp_addr.sin_addr = pj_inet_addr(pj_cstr(&s, "127.0.0.1"));
+
+ if (pj_sock_bind(udp2, &udp_addr, sizeof(udp_addr))) {
+ status=-30; goto on_return;
+ }
+
+ // Send data.
+ sent = datalen;
+ rc = pj_sock_sendto(udp1, data, &sent, 0, &udp_addr, sizeof(udp_addr));
+ if (rc != PJ_SUCCESS || sent != datalen) {
+ app_perror("...error: sendto() error", rc);
+ status=-40; goto on_return;
+ }
+
+ // Check that socket is marked as reable.
+ // Note that select() may also report that sockets are writable.
+ status = do_select(udp1, udp2, setcount);
+ if (status < 0) {
+ char errbuf[128];
+ pj_strerror(pj_get_netos_error(), errbuf, sizeof(errbuf));
+ PJ_LOG(1,(THIS_FILE, "...error: %s", errbuf));
+ status=-50; goto on_return;
+ }
+ if (status == 0) {
+ status=-60; goto on_return;
+ }
+
+ if (setcount[READ_FDS] != 1) {
+ status=-70; goto on_return;
+ }
+ if (setcount[WRITE_FDS] != 0) {
+ if (setcount[WRITE_FDS] == 2) {
+ PJ_LOG(3,(THIS_FILE, "...info: system reports writable sockets"));
+ } else {
+ status=-80; goto on_return;
+ }
+ } else {
+ PJ_LOG(3,(THIS_FILE,
+ "...info: system doesn't report writable sockets"));
+ }
+ if (setcount[EXCEPT_FDS] != 0) {
+ status=-90; goto on_return;
+ }
+
+ // Read the socket to clear readable sockets.
+ received = sizeof(buf);
+ rc = pj_sock_recv(udp2, buf, &received, 0);
+ if (rc != PJ_SUCCESS || received != 5) {
+ status=-100; goto on_return;
+ }
+
+ status = 0;
+
+ // Test timeout on the read part.
+ // This won't necessarily return zero, as select() may report that
+ // sockets are writable.
+ setcount[0] = setcount[1] = setcount[2] = 0;
+ status = do_select(udp1, udp2, setcount);
+ if (status != 0 && status != setcount[WRITE_FDS]) {
+ PJ_LOG(3,(THIS_FILE, "...error: expecting timeout but got %d sks set",
+ status));
+ PJ_LOG(3,(THIS_FILE, " rdset: %d, wrset: %d, exset: %d",
+ setcount[0], setcount[1], setcount[2]));
+ status = -110; goto on_return;
+ }
+ if (setcount[READ_FDS] != 0) {
+ PJ_LOG(3,(THIS_FILE, "...error: readable socket not expected"));
+ status = -120; goto on_return;
+ }
+
+ status = 0;
+
+on_return:
+ if (udp1 != PJ_INVALID_SOCKET)
+ pj_sock_close(udp1);
+ if (udp2 != PJ_INVALID_SOCKET)
+ pj_sock_close(udp2);
+ return status;
+}
+
+#else
+/* To prevent warning about "translation unit is empty"
+ * when this test is disabled.
+ */
+int dummy_select_test;
+#endif /* INCLUDE_SELECT_TEST */
+
+
diff --git a/pjlib/src/pjlib-test/sleep.c b/pjlib/src/pjlib-test/sleep.c
index 1af794e1..5732a165 100644
--- a/pjlib/src/pjlib-test/sleep.c
+++ b/pjlib/src/pjlib-test/sleep.c
@@ -1,200 +1,200 @@
-/* $Id$ */
-/*
- * Copyright (C)2003-2006 Benny Prijono <benny@prijono.org>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
-#include "test.h"
-
-/**
- * \page page_pjlib_sleep_test Test: Sleep, Time, and Timestamp
- *
- * This file provides implementation of \b sleep_test().
- *
- * \section sleep_test_sec Scope of the Test
- *
- * This tests:
- * - whether pj_thread_sleep() works.
- * - whether pj_gettimeofday() works.
- * - whether pj_get_timestamp() and friends works.
- *
- * API tested:
- * - pj_thread_sleep()
- * - pj_gettimeofday()
- * - PJ_TIME_VAL_SUB()
- * - PJ_TIME_VAL_LTE()
- * - pj_get_timestamp()
- * - pj_get_timestamp_freq() (implicitly)
- * - pj_elapsed_time()
- * - pj_elapsed_usec()
- *
- *
- * This file is <b>pjlib-test/sleep.c</b>
- *
- * \include pjlib-test/sleep.c
- */
-
-#if INCLUDE_SLEEP_TEST
-
-#include <pjlib.h>
-
-#define THIS_FILE "sleep_test"
-
-static int simple_sleep_test(void)
-{
- enum { COUNT = 5 };
- int i;
- pj_status_t rc;
-
- PJ_LOG(3,(THIS_FILE, "..will write messages every 1 second:"));
-
- for (i=0; i<COUNT; ++i) {
- rc = pj_thread_sleep(1000);
- if (rc != PJ_SUCCESS) {
- app_perror("...error: pj_thread_sleep()", rc);
- return -10;
- }
- PJ_LOG(3,(THIS_FILE, "...wake up.."));
- }
-
- return 0;
-}
-
-static int sleep_duration_test(void)
-{
- enum { MIS = 20, DURATION = 1000, DURATION2 = 500 };
- pj_status_t rc;
-
- PJ_LOG(3,(THIS_FILE, "..running sleep duration test"));
-
- /* Test pj_thread_sleep() and pj_gettimeofday() */
- {
- pj_time_val start, stop;
- pj_uint32_t msec;
-
- /* Mark start of test. */
- rc = pj_gettimeofday(&start);
- if (rc != PJ_SUCCESS) {
- app_perror("...error: pj_gettimeofday()", rc);
- return -10;
- }
-
- /* Sleep */
- rc = pj_thread_sleep(DURATION);
- if (rc != PJ_SUCCESS) {
- app_perror("...error: pj_thread_sleep()", rc);
- return -20;
- }
-
- /* Mark end of test. */
- rc = pj_gettimeofday(&stop);
-
- /* Calculate duration (store in stop). */
- PJ_TIME_VAL_SUB(stop, start);
-
- /* Convert to msec. */
- msec = PJ_TIME_VAL_MSEC(stop);
-
- /* Check if it's within range. */
- if (msec < DURATION * (100-MIS)/100 ||
- msec > DURATION * (100+MIS)/100)
- {
- PJ_LOG(3,(THIS_FILE,
- "...error: slept for %d ms instead of %d ms "
- "(outside %d%% err window)",
- msec, DURATION, MIS));
- return -30;
- }
- }
-
-
- /* Test pj_thread_sleep() and pj_get_timestamp() and friends */
- {
- pj_time_val t1, t2;
- pj_timestamp start, stop;
- pj_time_val elapsed;
- pj_uint32_t msec;
-
- /* Mark start of test. */
- rc = pj_get_timestamp(&start);
- if (rc != PJ_SUCCESS) {
- app_perror("...error: pj_get_timestamp()", rc);
- return -60;
- }
-
- /* ..also with gettimeofday() */
- pj_gettimeofday(&t1);
-
- /* Sleep */
- rc = pj_thread_sleep(DURATION2);
- if (rc != PJ_SUCCESS) {
- app_perror("...error: pj_thread_sleep()", rc);
- return -70;
- }
-
- /* Mark end of test. */
- pj_get_timestamp(&stop);
-
- /* ..also with gettimeofday() */
- pj_gettimeofday(&t2);
-
- /* Compare t1 and t2. */
- if (PJ_TIME_VAL_LTE(t2, t1)) {
- PJ_LOG(3,(THIS_FILE, "...error: t2 is less than t1!!"));
- return -75;
- }
-
- /* Get elapsed time in time_val */
- elapsed = pj_elapsed_time(&start, &stop);
-
- msec = PJ_TIME_VAL_MSEC(elapsed);
-
- /* Check if it's within range. */
- if (msec < DURATION2 * (100-MIS)/100 ||
- msec > DURATION2 * (100+MIS)/100)
- {
- PJ_LOG(3,(THIS_FILE,
- "...error: slept for %d ms instead of %d ms "
- "(outside %d%% err window)",
- msec, DURATION2, MIS));
- return -30;
- }
- }
-
- /* All done. */
- return 0;
-}
-
-int sleep_test()
-{
- int rc;
-
- rc = simple_sleep_test();
- if (rc != PJ_SUCCESS)
- return rc;
-
- rc = sleep_duration_test();
- if (rc != PJ_SUCCESS)
- return rc;
-
- return 0;
-}
-
-#else
-/* To prevent warning about "translation unit is empty"
- * when this test is disabled.
- */
-int dummy_sleep_test;
-#endif /* INCLUDE_SLEEP_TEST */
+/* $Id$ */
+/*
+ * Copyright (C)2003-2006 Benny Prijono <benny@prijono.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#include "test.h"
+
+/**
+ * \page page_pjlib_sleep_test Test: Sleep, Time, and Timestamp
+ *
+ * This file provides implementation of \b sleep_test().
+ *
+ * \section sleep_test_sec Scope of the Test
+ *
+ * This tests:
+ * - whether pj_thread_sleep() works.
+ * - whether pj_gettimeofday() works.
+ * - whether pj_get_timestamp() and friends works.
+ *
+ * API tested:
+ * - pj_thread_sleep()
+ * - pj_gettimeofday()
+ * - PJ_TIME_VAL_SUB()
+ * - PJ_TIME_VAL_LTE()
+ * - pj_get_timestamp()
+ * - pj_get_timestamp_freq() (implicitly)
+ * - pj_elapsed_time()
+ * - pj_elapsed_usec()
+ *
+ *
+ * This file is <b>pjlib-test/sleep.c</b>
+ *
+ * \include pjlib-test/sleep.c
+ */
+
+#if INCLUDE_SLEEP_TEST
+
+#include <pjlib.h>
+
+#define THIS_FILE "sleep_test"
+
+static int simple_sleep_test(void)
+{
+ enum { COUNT = 5 };
+ int i;
+ pj_status_t rc;
+
+ PJ_LOG(3,(THIS_FILE, "..will write messages every 1 second:"));
+
+ for (i=0; i<COUNT; ++i) {
+ rc = pj_thread_sleep(1000);
+ if (rc != PJ_SUCCESS) {
+ app_perror("...error: pj_thread_sleep()", rc);
+ return -10;
+ }
+ PJ_LOG(3,(THIS_FILE, "...wake up.."));
+ }
+
+ return 0;
+}
+
+static int sleep_duration_test(void)
+{
+ enum { MIS = 20, DURATION = 1000, DURATION2 = 500 };
+ pj_status_t rc;
+
+ PJ_LOG(3,(THIS_FILE, "..running sleep duration test"));
+
+ /* Test pj_thread_sleep() and pj_gettimeofday() */
+ {
+ pj_time_val start, stop;
+ pj_uint32_t msec;
+
+ /* Mark start of test. */
+ rc = pj_gettimeofday(&start);
+ if (rc != PJ_SUCCESS) {
+ app_perror("...error: pj_gettimeofday()", rc);
+ return -10;
+ }
+
+ /* Sleep */
+ rc = pj_thread_sleep(DURATION);
+ if (rc != PJ_SUCCESS) {
+ app_perror("...error: pj_thread_sleep()", rc);
+ return -20;
+ }
+
+ /* Mark end of test. */
+ rc = pj_gettimeofday(&stop);
+
+ /* Calculate duration (store in stop). */
+ PJ_TIME_VAL_SUB(stop, start);
+
+ /* Convert to msec. */
+ msec = PJ_TIME_VAL_MSEC(stop);
+
+ /* Check if it's within range. */
+ if (msec < DURATION * (100-MIS)/100 ||
+ msec > DURATION * (100+MIS)/100)
+ {
+ PJ_LOG(3,(THIS_FILE,
+ "...error: slept for %d ms instead of %d ms "
+ "(outside %d%% err window)",
+ msec, DURATION, MIS));
+ return -30;
+ }
+ }
+
+
+ /* Test pj_thread_sleep() and pj_get_timestamp() and friends */
+ {
+ pj_time_val t1, t2;
+ pj_timestamp start, stop;
+ pj_time_val elapsed;
+ pj_uint32_t msec;
+
+ /* Mark start of test. */
+ rc = pj_get_timestamp(&start);
+ if (rc != PJ_SUCCESS) {
+ app_perror("...error: pj_get_timestamp()", rc);
+ return -60;
+ }
+
+ /* ..also with gettimeofday() */
+ pj_gettimeofday(&t1);
+
+ /* Sleep */
+ rc = pj_thread_sleep(DURATION2);
+ if (rc != PJ_SUCCESS) {
+ app_perror("...error: pj_thread_sleep()", rc);
+ return -70;
+ }
+
+ /* Mark end of test. */
+ pj_get_timestamp(&stop);
+
+ /* ..also with gettimeofday() */
+ pj_gettimeofday(&t2);
+
+ /* Compare t1 and t2. */
+ if (PJ_TIME_VAL_LTE(t2, t1)) {
+ PJ_LOG(3,(THIS_FILE, "...error: t2 is less than t1!!"));
+ return -75;
+ }
+
+ /* Get elapsed time in time_val */
+ elapsed = pj_elapsed_time(&start, &stop);
+
+ msec = PJ_TIME_VAL_MSEC(elapsed);
+
+ /* Check if it's within range. */
+ if (msec < DURATION2 * (100-MIS)/100 ||
+ msec > DURATION2 * (100+MIS)/100)
+ {
+ PJ_LOG(3,(THIS_FILE,
+ "...error: slept for %d ms instead of %d ms "
+ "(outside %d%% err window)",
+ msec, DURATION2, MIS));
+ return -30;
+ }
+ }
+
+ /* All done. */
+ return 0;
+}
+
+int sleep_test()
+{
+ int rc;
+
+ rc = simple_sleep_test();
+ if (rc != PJ_SUCCESS)
+ return rc;
+
+ rc = sleep_duration_test();
+ if (rc != PJ_SUCCESS)
+ return rc;
+
+ return 0;
+}
+
+#else
+/* To prevent warning about "translation unit is empty"
+ * when this test is disabled.
+ */
+int dummy_sleep_test;
+#endif /* INCLUDE_SLEEP_TEST */
diff --git a/pjlib/src/pjlib-test/sock.c b/pjlib/src/pjlib-test/sock.c
index 2961b788..b8af3cd8 100644
--- a/pjlib/src/pjlib-test/sock.c
+++ b/pjlib/src/pjlib-test/sock.c
@@ -1,456 +1,456 @@
-/* $Id$ */
-/*
- * Copyright (C)2003-2006 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 <pjlib.h>
-#include "test.h"
-
-
-/**
- * \page page_pjlib_sock_test Test: Socket
- *
- * This file provides implementation of \b sock_test(). It tests the
- * various aspects of the socket API.
- *
- * \section sock_test_scope_sec Scope of the Test
- *
- * The scope of the test:
- * - verify the validity of the address structs.
- * - verify that address manipulation API works.
- * - simple socket creation and destruction.
- * - simple socket send/recv and sendto/recvfrom.
- * - UDP connect()
- * - send/recv big data.
- * - all for both UDP and TCP.
- *
- * The APIs tested in this test:
- * - pj_inet_aton()
- * - pj_inet_ntoa()
- * - pj_gethostname()
- * - pj_sock_socket()
- * - pj_sock_close()
- * - pj_sock_send()
- * - pj_sock_sendto()
- * - pj_sock_recv()
- * - pj_sock_recvfrom()
- * - pj_sock_bind()
- * - pj_sock_connect()
- * - pj_sock_listen()
- * - pj_sock_accept()
- *
- *
- * This file is <b>pjlib-test/sock.c</b>
- *
- * \include pjlib-test/sock.c
- */
-
-#if INCLUDE_SOCK_TEST
-
-#define UDP_PORT 51234
-#define TCP_PORT (UDP_PORT+10)
-#define BIG_DATA_LEN 9000
-
-static char bigdata[BIG_DATA_LEN];
-static char bigbuffer[BIG_DATA_LEN];
-
-static int format_test(void)
-{
- pj_str_t s = pj_str("127.0.0.1");
- char *p;
- pj_in_addr addr;
- const pj_str_t *hostname;
-
- PJ_LOG(3,("test", "...format_test()"));
-
- /* pj_inet_aton() */
- if (pj_inet_aton(&s, &addr) != 1)
- return -10;
-
- /* Check the result. */
- p = (char*)&addr;
- if (p[0]!=127 || p[1]!=0 || p[2]!=0 || p[3]!=1)
- return -15;
-
- /* pj_inet_ntoa() */
- p = pj_inet_ntoa(addr);
- if (!p)
- return -20;
-
- if (pj_strcmp2(&s, p) != 0)
- return -30;
-
- /* pj_gethostname() */
- hostname = pj_gethostname();
- if (!hostname || !hostname->ptr || !hostname->slen)
- return -40;
-
- /* pj_gethostaddr() */
-
- return 0;
-}
-
-static int simple_sock_test(void)
-{
- int types[2];
- pj_sock_t sock;
- int i;
- pj_status_t rc = PJ_SUCCESS;
-
- types[0] = PJ_SOCK_STREAM;
- types[1] = PJ_SOCK_DGRAM;
-
- PJ_LOG(3,("test", "...simple_sock_test()"));
-
- for (i=0; i<sizeof(types)/sizeof(types[0]); ++i) {
-
- rc = pj_sock_socket(PJ_AF_INET, types[i], 0, &sock);
- if (rc != PJ_SUCCESS) {
- app_perror("...error: unable to create socket type %d", rc);
- break;
- } else {
- rc = pj_sock_close(sock);
- if (rc != 0) {
- app_perror("...error: close socket", rc);
- break;
- }
- }
- }
- return rc;
-}
-
-
-static int send_recv_test(int sock_type,
- pj_sock_t ss, pj_sock_t cs,
- pj_sockaddr_in *dstaddr, pj_sockaddr_in *srcaddr,
- int addrlen)
-{
- enum { DATA_LEN = 16 };
- char senddata[DATA_LEN+4], recvdata[DATA_LEN+4];
- pj_ssize_t sent, received, total_received;
- pj_status_t rc;
-
- TRACE_(("test", "....create_random_string()"));
- pj_create_random_string(senddata, DATA_LEN);
- senddata[DATA_LEN-1] = '\0';
-
- /*
- * Test send/recv small data.
- */
- TRACE_(("test", "....sendto()"));
- if (dstaddr) {
- sent = DATA_LEN;
- rc = pj_sock_sendto(cs, senddata, &sent, 0, dstaddr, addrlen);
- if (rc != PJ_SUCCESS || sent != DATA_LEN) {
- app_perror("...sendto error", rc);
- rc = -140; goto on_error;
- }
- } else {
- sent = DATA_LEN;
- rc = pj_sock_send(cs, senddata, &sent, 0);
- if (rc != PJ_SUCCESS || sent != DATA_LEN) {
- app_perror("...send error", rc);
- rc = -145; goto on_error;
- }
- }
-
- TRACE_(("test", "....recv()"));
- if (srcaddr) {
- pj_sockaddr_in addr;
- int srclen = sizeof(addr);
-
- pj_memset(&addr, 0, sizeof(addr));
-
- received = DATA_LEN;
- rc = pj_sock_recvfrom(ss, recvdata, &received, 0, &addr, &srclen);
- if (rc != PJ_SUCCESS || received != DATA_LEN) {
- app_perror("...recvfrom error", rc);
- rc = -150; goto on_error;
- }
- if (srclen != addrlen)
- return -151;
- if (pj_memcmp(&addr, srcaddr, srclen) != 0) {
- char srcaddr_str[32], addr_str[32];
- strcpy(srcaddr_str, pj_inet_ntoa(srcaddr->sin_addr));
- strcpy(addr_str, pj_inet_ntoa(addr.sin_addr));
- PJ_LOG(3,("test", "...error: src address mismatch (original=%s, "
- "recvfrom addr=%s)",
- srcaddr_str, addr_str));
- return -152;
- }
-
- } else {
- /* Repeat recv() until all data is received.
- * This applies only for non-UDP of course, since for UDP
- * we would expect all data to be received in one packet.
- */
- total_received = 0;
- do {
- received = DATA_LEN-total_received;
- rc = pj_sock_recv(ss, recvdata+total_received, &received, 0);
- if (rc != PJ_SUCCESS) {
- app_perror("...recv error", rc);
- rc = -155; goto on_error;
- }
- if (received <= 0) {
- PJ_LOG(3,("", "...error: socket has closed! (received=%d)",
- received));
- rc = -156; goto on_error;
- }
- if (received != DATA_LEN-total_received) {
- if (sock_type != PJ_SOCK_STREAM) {
- PJ_LOG(3,("", "...error: expecting %u bytes, got %u bytes",
- DATA_LEN-total_received, received));
- rc = -157; goto on_error;
- }
- }
- total_received += received;
- } while (total_received < DATA_LEN);
- }
-
- TRACE_(("test", "....memcmp()"));
- if (pj_memcmp(senddata, recvdata, DATA_LEN) != 0) {
- PJ_LOG(3,("","...error: received data mismatch "
- "(got:'%s' expecting:'%s'",
- recvdata, senddata));
- rc = -160; goto on_error;
- }
-
- /*
- * Test send/recv big data.
- */
- TRACE_(("test", "....sendto()"));
- if (dstaddr) {
- sent = BIG_DATA_LEN;
- rc = pj_sock_sendto(cs, bigdata, &sent, 0, dstaddr, addrlen);
- if (rc != PJ_SUCCESS || sent != BIG_DATA_LEN) {
- app_perror("...sendto error", rc);
- rc = -161; goto on_error;
- }
- } else {
- sent = BIG_DATA_LEN;
- rc = pj_sock_send(cs, bigdata, &sent, 0);
- if (rc != PJ_SUCCESS || sent != BIG_DATA_LEN) {
- app_perror("...send error", rc);
- rc = -165; goto on_error;
- }
- }
-
- TRACE_(("test", "....recv()"));
-
- /* Repeat recv() until all data is received.
- * This applies only for non-UDP of course, since for UDP
- * we would expect all data to be received in one packet.
- */
- total_received = 0;
- do {
- received = BIG_DATA_LEN-total_received;
- rc = pj_sock_recv(ss, bigbuffer+total_received, &received, 0);
- if (rc != PJ_SUCCESS) {
- app_perror("...recv error", rc);
- rc = -170; goto on_error;
- }
- if (received <= 0) {
- PJ_LOG(3,("", "...error: socket has closed! (received=%d)",
- received));
- rc = -173; goto on_error;
- }
- if (received != BIG_DATA_LEN-total_received) {
- if (sock_type != PJ_SOCK_STREAM) {
- PJ_LOG(3,("", "...error: expecting %u bytes, got %u bytes",
- BIG_DATA_LEN-total_received, received));
- rc = -176; goto on_error;
- }
- }
- total_received += received;
- } while (total_received < BIG_DATA_LEN);
-
- TRACE_(("test", "....memcmp()"));
- if (pj_memcmp(bigdata, bigbuffer, BIG_DATA_LEN) != 0) {
- PJ_LOG(3,("", "...error: received data has been altered!"));
- rc = -180; goto on_error;
- }
-
- rc = 0;
-
-on_error:
- return rc;
-}
-
-static int udp_test(void)
-{
- pj_sock_t cs = PJ_INVALID_SOCKET, ss = PJ_INVALID_SOCKET;
- pj_sockaddr_in dstaddr, srcaddr;
- pj_str_t s;
- pj_status_t rc = 0, retval;
-
- PJ_LOG(3,("test", "...udp_test()"));
-
- rc = pj_sock_socket(PJ_AF_INET, PJ_SOCK_DGRAM, 0, &ss);
- if (rc != 0) {
- app_perror("...error: unable to create socket", rc);
- return -100;
- }
-
- rc = pj_sock_socket(PJ_AF_INET, PJ_SOCK_DGRAM, 0, &cs);
- if (rc != 0)
- return -110;
-
- /* Bind server socket. */
- pj_memset(&dstaddr, 0, sizeof(dstaddr));
- dstaddr.sin_family = PJ_AF_INET;
- dstaddr.sin_port = pj_htons(UDP_PORT);
- dstaddr.sin_addr = pj_inet_addr(pj_cstr(&s, "127.0.0.1"));
-
- if ((rc=pj_sock_bind(ss, &dstaddr, sizeof(dstaddr))) != 0) {
- app_perror("...bind error", rc);
- rc = -120; goto on_error;
- }
-
- /* Bind client socket. */
- pj_memset(&srcaddr, 0, sizeof(srcaddr));
- srcaddr.sin_family = PJ_AF_INET;
- srcaddr.sin_port = pj_htons(UDP_PORT-1);
- srcaddr.sin_addr = pj_inet_addr(pj_cstr(&s, "127.0.0.1"));
-
- if ((rc=pj_sock_bind(cs, &srcaddr, sizeof(srcaddr))) != 0) {
- app_perror("...bind error", rc);
- rc = -121; goto on_error;
- }
-
- /* Test send/recv, with sendto */
- rc = send_recv_test(PJ_SOCK_DGRAM, ss, cs, &dstaddr, NULL,
- sizeof(dstaddr));
- if (rc != 0)
- goto on_error;
-
- /* Test send/recv, with sendto and recvfrom */
- rc = send_recv_test(PJ_SOCK_DGRAM, ss, cs, &dstaddr,
- &srcaddr, sizeof(dstaddr));
- if (rc != 0)
- goto on_error;
-
- /* connect() the sockets. */
- rc = pj_sock_connect(cs, &dstaddr, sizeof(dstaddr));
- if (rc != 0) {
- app_perror("...connect() error", rc);
- rc = -122; goto on_error;
- }
-
- /* Test send/recv with send() */
- rc = send_recv_test(PJ_SOCK_DGRAM, ss, cs, NULL, NULL, 0);
- if (rc != 0)
- goto on_error;
-
- /* Test send/recv with send() and recvfrom */
- rc = send_recv_test(PJ_SOCK_DGRAM, ss, cs, NULL, &srcaddr,
- sizeof(srcaddr));
- if (rc != 0)
- goto on_error;
-
-on_error:
- retval = rc;
- if (cs != PJ_INVALID_SOCKET) {
- rc = pj_sock_close(cs);
- if (rc != PJ_SUCCESS) {
- app_perror("...error in closing socket", rc);
- return -1000;
- }
- }
- if (ss != PJ_INVALID_SOCKET) {
- rc = pj_sock_close(ss);
- if (rc != PJ_SUCCESS) {
- app_perror("...error in closing socket", rc);
- return -1010;
- }
- }
-
- return retval;
-}
-
-static int tcp_test(void)
-{
- pj_sock_t cs, ss;
- pj_status_t rc = 0, retval;
-
- PJ_LOG(3,("test", "...tcp_test()"));
-
- rc = app_socketpair(PJ_AF_INET, PJ_SOCK_STREAM, 0, &ss, &cs);
- if (rc != PJ_SUCCESS) {
- app_perror("...error: app_socketpair():", rc);
- return -2000;
- }
-
- /* Test send/recv with send() and recv() */
- retval = send_recv_test(PJ_SOCK_STREAM, ss, cs, NULL, NULL, 0);
-
- rc = pj_sock_close(cs);
- if (rc != PJ_SUCCESS) {
- app_perror("...error in closing socket", rc);
- return -2000;
- }
-
- rc = pj_sock_close(ss);
- if (rc != PJ_SUCCESS) {
- app_perror("...error in closing socket", rc);
- return -2010;
- }
-
- return retval;
-}
-
-static int ioctl_test(void)
-{
- return 0;
-}
-
-int sock_test()
-{
- int rc;
-
- pj_create_random_string(bigdata, BIG_DATA_LEN);
-
- rc = format_test();
- if (rc != 0)
- return rc;
-
- rc = simple_sock_test();
- if (rc != 0)
- return rc;
-
- rc = ioctl_test();
- if (rc != 0)
- return rc;
-
- rc = udp_test();
- if (rc != 0)
- return rc;
-
- rc = tcp_test();
- if (rc != 0)
- return rc;
-
- return 0;
-}
-
-
-#else
-/* To prevent warning about "translation unit is empty"
- * when this test is disabled.
- */
-int dummy_sock_test;
-#endif /* INCLUDE_SOCK_TEST */
-
+/* $Id$ */
+/*
+ * Copyright (C)2003-2006 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 <pjlib.h>
+#include "test.h"
+
+
+/**
+ * \page page_pjlib_sock_test Test: Socket
+ *
+ * This file provides implementation of \b sock_test(). It tests the
+ * various aspects of the socket API.
+ *
+ * \section sock_test_scope_sec Scope of the Test
+ *
+ * The scope of the test:
+ * - verify the validity of the address structs.
+ * - verify that address manipulation API works.
+ * - simple socket creation and destruction.
+ * - simple socket send/recv and sendto/recvfrom.
+ * - UDP connect()
+ * - send/recv big data.
+ * - all for both UDP and TCP.
+ *
+ * The APIs tested in this test:
+ * - pj_inet_aton()
+ * - pj_inet_ntoa()
+ * - pj_gethostname()
+ * - pj_sock_socket()
+ * - pj_sock_close()
+ * - pj_sock_send()
+ * - pj_sock_sendto()
+ * - pj_sock_recv()
+ * - pj_sock_recvfrom()
+ * - pj_sock_bind()
+ * - pj_sock_connect()
+ * - pj_sock_listen()
+ * - pj_sock_accept()
+ *
+ *
+ * This file is <b>pjlib-test/sock.c</b>
+ *
+ * \include pjlib-test/sock.c
+ */
+
+#if INCLUDE_SOCK_TEST
+
+#define UDP_PORT 51234
+#define TCP_PORT (UDP_PORT+10)
+#define BIG_DATA_LEN 9000
+
+static char bigdata[BIG_DATA_LEN];
+static char bigbuffer[BIG_DATA_LEN];
+
+static int format_test(void)
+{
+ pj_str_t s = pj_str("127.0.0.1");
+ char *p;
+ pj_in_addr addr;
+ const pj_str_t *hostname;
+
+ PJ_LOG(3,("test", "...format_test()"));
+
+ /* pj_inet_aton() */
+ if (pj_inet_aton(&s, &addr) != 1)
+ return -10;
+
+ /* Check the result. */
+ p = (char*)&addr;
+ if (p[0]!=127 || p[1]!=0 || p[2]!=0 || p[3]!=1)
+ return -15;
+
+ /* pj_inet_ntoa() */
+ p = pj_inet_ntoa(addr);
+ if (!p)
+ return -20;
+
+ if (pj_strcmp2(&s, p) != 0)
+ return -30;
+
+ /* pj_gethostname() */
+ hostname = pj_gethostname();
+ if (!hostname || !hostname->ptr || !hostname->slen)
+ return -40;
+
+ /* pj_gethostaddr() */
+
+ return 0;
+}
+
+static int simple_sock_test(void)
+{
+ int types[2];
+ pj_sock_t sock;
+ int i;
+ pj_status_t rc = PJ_SUCCESS;
+
+ types[0] = PJ_SOCK_STREAM;
+ types[1] = PJ_SOCK_DGRAM;
+
+ PJ_LOG(3,("test", "...simple_sock_test()"));
+
+ for (i=0; i<sizeof(types)/sizeof(types[0]); ++i) {
+
+ rc = pj_sock_socket(PJ_AF_INET, types[i], 0, &sock);
+ if (rc != PJ_SUCCESS) {
+ app_perror("...error: unable to create socket type %d", rc);
+ break;
+ } else {
+ rc = pj_sock_close(sock);
+ if (rc != 0) {
+ app_perror("...error: close socket", rc);
+ break;
+ }
+ }
+ }
+ return rc;
+}
+
+
+static int send_recv_test(int sock_type,
+ pj_sock_t ss, pj_sock_t cs,
+ pj_sockaddr_in *dstaddr, pj_sockaddr_in *srcaddr,
+ int addrlen)
+{
+ enum { DATA_LEN = 16 };
+ char senddata[DATA_LEN+4], recvdata[DATA_LEN+4];
+ pj_ssize_t sent, received, total_received;
+ pj_status_t rc;
+
+ TRACE_(("test", "....create_random_string()"));
+ pj_create_random_string(senddata, DATA_LEN);
+ senddata[DATA_LEN-1] = '\0';
+
+ /*
+ * Test send/recv small data.
+ */
+ TRACE_(("test", "....sendto()"));
+ if (dstaddr) {
+ sent = DATA_LEN;
+ rc = pj_sock_sendto(cs, senddata, &sent, 0, dstaddr, addrlen);
+ if (rc != PJ_SUCCESS || sent != DATA_LEN) {
+ app_perror("...sendto error", rc);
+ rc = -140; goto on_error;
+ }
+ } else {
+ sent = DATA_LEN;
+ rc = pj_sock_send(cs, senddata, &sent, 0);
+ if (rc != PJ_SUCCESS || sent != DATA_LEN) {
+ app_perror("...send error", rc);
+ rc = -145; goto on_error;
+ }
+ }
+
+ TRACE_(("test", "....recv()"));
+ if (srcaddr) {
+ pj_sockaddr_in addr;
+ int srclen = sizeof(addr);
+
+ pj_memset(&addr, 0, sizeof(addr));
+
+ received = DATA_LEN;
+ rc = pj_sock_recvfrom(ss, recvdata, &received, 0, &addr, &srclen);
+ if (rc != PJ_SUCCESS || received != DATA_LEN) {
+ app_perror("...recvfrom error", rc);
+ rc = -150; goto on_error;
+ }
+ if (srclen != addrlen)
+ return -151;
+ if (pj_memcmp(&addr, srcaddr, srclen) != 0) {
+ char srcaddr_str[32], addr_str[32];
+ strcpy(srcaddr_str, pj_inet_ntoa(srcaddr->sin_addr));
+ strcpy(addr_str, pj_inet_ntoa(addr.sin_addr));
+ PJ_LOG(3,("test", "...error: src address mismatch (original=%s, "
+ "recvfrom addr=%s)",
+ srcaddr_str, addr_str));
+ return -152;
+ }
+
+ } else {
+ /* Repeat recv() until all data is received.
+ * This applies only for non-UDP of course, since for UDP
+ * we would expect all data to be received in one packet.
+ */
+ total_received = 0;
+ do {
+ received = DATA_LEN-total_received;
+ rc = pj_sock_recv(ss, recvdata+total_received, &received, 0);
+ if (rc != PJ_SUCCESS) {
+ app_perror("...recv error", rc);
+ rc = -155; goto on_error;
+ }
+ if (received <= 0) {
+ PJ_LOG(3,("", "...error: socket has closed! (received=%d)",
+ received));
+ rc = -156; goto on_error;
+ }
+ if (received != DATA_LEN-total_received) {
+ if (sock_type != PJ_SOCK_STREAM) {
+ PJ_LOG(3,("", "...error: expecting %u bytes, got %u bytes",
+ DATA_LEN-total_received, received));
+ rc = -157; goto on_error;
+ }
+ }
+ total_received += received;
+ } while (total_received < DATA_LEN);
+ }
+
+ TRACE_(("test", "....memcmp()"));
+ if (pj_memcmp(senddata, recvdata, DATA_LEN) != 0) {
+ PJ_LOG(3,("","...error: received data mismatch "
+ "(got:'%s' expecting:'%s'",
+ recvdata, senddata));
+ rc = -160; goto on_error;
+ }
+
+ /*
+ * Test send/recv big data.
+ */
+ TRACE_(("test", "....sendto()"));
+ if (dstaddr) {
+ sent = BIG_DATA_LEN;
+ rc = pj_sock_sendto(cs, bigdata, &sent, 0, dstaddr, addrlen);
+ if (rc != PJ_SUCCESS || sent != BIG_DATA_LEN) {
+ app_perror("...sendto error", rc);
+ rc = -161; goto on_error;
+ }
+ } else {
+ sent = BIG_DATA_LEN;
+ rc = pj_sock_send(cs, bigdata, &sent, 0);
+ if (rc != PJ_SUCCESS || sent != BIG_DATA_LEN) {
+ app_perror("...send error", rc);
+ rc = -165; goto on_error;
+ }
+ }
+
+ TRACE_(("test", "....recv()"));
+
+ /* Repeat recv() until all data is received.
+ * This applies only for non-UDP of course, since for UDP
+ * we would expect all data to be received in one packet.
+ */
+ total_received = 0;
+ do {
+ received = BIG_DATA_LEN-total_received;
+ rc = pj_sock_recv(ss, bigbuffer+total_received, &received, 0);
+ if (rc != PJ_SUCCESS) {
+ app_perror("...recv error", rc);
+ rc = -170; goto on_error;
+ }
+ if (received <= 0) {
+ PJ_LOG(3,("", "...error: socket has closed! (received=%d)",
+ received));
+ rc = -173; goto on_error;
+ }
+ if (received != BIG_DATA_LEN-total_received) {
+ if (sock_type != PJ_SOCK_STREAM) {
+ PJ_LOG(3,("", "...error: expecting %u bytes, got %u bytes",
+ BIG_DATA_LEN-total_received, received));
+ rc = -176; goto on_error;
+ }
+ }
+ total_received += received;
+ } while (total_received < BIG_DATA_LEN);
+
+ TRACE_(("test", "....memcmp()"));
+ if (pj_memcmp(bigdata, bigbuffer, BIG_DATA_LEN) != 0) {
+ PJ_LOG(3,("", "...error: received data has been altered!"));
+ rc = -180; goto on_error;
+ }
+
+ rc = 0;
+
+on_error:
+ return rc;
+}
+
+static int udp_test(void)
+{
+ pj_sock_t cs = PJ_INVALID_SOCKET, ss = PJ_INVALID_SOCKET;
+ pj_sockaddr_in dstaddr, srcaddr;
+ pj_str_t s;
+ pj_status_t rc = 0, retval;
+
+ PJ_LOG(3,("test", "...udp_test()"));
+
+ rc = pj_sock_socket(PJ_AF_INET, PJ_SOCK_DGRAM, 0, &ss);
+ if (rc != 0) {
+ app_perror("...error: unable to create socket", rc);
+ return -100;
+ }
+
+ rc = pj_sock_socket(PJ_AF_INET, PJ_SOCK_DGRAM, 0, &cs);
+ if (rc != 0)
+ return -110;
+
+ /* Bind server socket. */
+ pj_memset(&dstaddr, 0, sizeof(dstaddr));
+ dstaddr.sin_family = PJ_AF_INET;
+ dstaddr.sin_port = pj_htons(UDP_PORT);
+ dstaddr.sin_addr = pj_inet_addr(pj_cstr(&s, "127.0.0.1"));
+
+ if ((rc=pj_sock_bind(ss, &dstaddr, sizeof(dstaddr))) != 0) {
+ app_perror("...bind error", rc);
+ rc = -120; goto on_error;
+ }
+
+ /* Bind client socket. */
+ pj_memset(&srcaddr, 0, sizeof(srcaddr));
+ srcaddr.sin_family = PJ_AF_INET;
+ srcaddr.sin_port = pj_htons(UDP_PORT-1);
+ srcaddr.sin_addr = pj_inet_addr(pj_cstr(&s, "127.0.0.1"));
+
+ if ((rc=pj_sock_bind(cs, &srcaddr, sizeof(srcaddr))) != 0) {
+ app_perror("...bind error", rc);
+ rc = -121; goto on_error;
+ }
+
+ /* Test send/recv, with sendto */
+ rc = send_recv_test(PJ_SOCK_DGRAM, ss, cs, &dstaddr, NULL,
+ sizeof(dstaddr));
+ if (rc != 0)
+ goto on_error;
+
+ /* Test send/recv, with sendto and recvfrom */
+ rc = send_recv_test(PJ_SOCK_DGRAM, ss, cs, &dstaddr,
+ &srcaddr, sizeof(dstaddr));
+ if (rc != 0)
+ goto on_error;
+
+ /* connect() the sockets. */
+ rc = pj_sock_connect(cs, &dstaddr, sizeof(dstaddr));
+ if (rc != 0) {
+ app_perror("...connect() error", rc);
+ rc = -122; goto on_error;
+ }
+
+ /* Test send/recv with send() */
+ rc = send_recv_test(PJ_SOCK_DGRAM, ss, cs, NULL, NULL, 0);
+ if (rc != 0)
+ goto on_error;
+
+ /* Test send/recv with send() and recvfrom */
+ rc = send_recv_test(PJ_SOCK_DGRAM, ss, cs, NULL, &srcaddr,
+ sizeof(srcaddr));
+ if (rc != 0)
+ goto on_error;
+
+on_error:
+ retval = rc;
+ if (cs != PJ_INVALID_SOCKET) {
+ rc = pj_sock_close(cs);
+ if (rc != PJ_SUCCESS) {
+ app_perror("...error in closing socket", rc);
+ return -1000;
+ }
+ }
+ if (ss != PJ_INVALID_SOCKET) {
+ rc = pj_sock_close(ss);
+ if (rc != PJ_SUCCESS) {
+ app_perror("...error in closing socket", rc);
+ return -1010;
+ }
+ }
+
+ return retval;
+}
+
+static int tcp_test(void)
+{
+ pj_sock_t cs, ss;
+ pj_status_t rc = 0, retval;
+
+ PJ_LOG(3,("test", "...tcp_test()"));
+
+ rc = app_socketpair(PJ_AF_INET, PJ_SOCK_STREAM, 0, &ss, &cs);
+ if (rc != PJ_SUCCESS) {
+ app_perror("...error: app_socketpair():", rc);
+ return -2000;
+ }
+
+ /* Test send/recv with send() and recv() */
+ retval = send_recv_test(PJ_SOCK_STREAM, ss, cs, NULL, NULL, 0);
+
+ rc = pj_sock_close(cs);
+ if (rc != PJ_SUCCESS) {
+ app_perror("...error in closing socket", rc);
+ return -2000;
+ }
+
+ rc = pj_sock_close(ss);
+ if (rc != PJ_SUCCESS) {
+ app_perror("...error in closing socket", rc);
+ return -2010;
+ }
+
+ return retval;
+}
+
+static int ioctl_test(void)
+{
+ return 0;
+}
+
+int sock_test()
+{
+ int rc;
+
+ pj_create_random_string(bigdata, BIG_DATA_LEN);
+
+ rc = format_test();
+ if (rc != 0)
+ return rc;
+
+ rc = simple_sock_test();
+ if (rc != 0)
+ return rc;
+
+ rc = ioctl_test();
+ if (rc != 0)
+ return rc;
+
+ rc = udp_test();
+ if (rc != 0)
+ return rc;
+
+ rc = tcp_test();
+ if (rc != 0)
+ return rc;
+
+ return 0;
+}
+
+
+#else
+/* To prevent warning about "translation unit is empty"
+ * when this test is disabled.
+ */
+int dummy_sock_test;
+#endif /* INCLUDE_SOCK_TEST */
+
diff --git a/pjlib/src/pjlib-test/sock_perf.c b/pjlib/src/pjlib-test/sock_perf.c
index bb20f8a7..a3b46b98 100644
--- a/pjlib/src/pjlib-test/sock_perf.c
+++ b/pjlib/src/pjlib-test/sock_perf.c
@@ -1,183 +1,183 @@
-/* $Id$ */
-/*
- * Copyright (C)2003-2006 Benny Prijono <benny@prijono.org>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
-#include "test.h"
-#include <pjlib.h>
-#include <pj/compat/high_precision.h>
-
-
-/**
- * \page page_pjlib_sock_perf_test Test: Socket Performance
- *
- * Test the performance of the socket communication. This will perform
- * simple producer-consumer type of test, where we calculate how long
- * does it take to send certain number of packets from producer to
- * consumer.
- *
- * This file is <b>pjlib-test/sock_perf.c</b>
- *
- * \include pjlib-test/sock_perf.c
- */
-
-#if INCLUDE_SOCK_PERF_TEST
-
-/*
- * sock_producer_consumer()
- *
- * Simple producer-consumer benchmarking. Send loop number of
- * buf_size size packets as fast as possible.
- */
-static int sock_producer_consumer(int sock_type,
- unsigned buf_size,
- unsigned loop,
- unsigned *p_bandwidth)
-{
- pj_sock_t consumer, producer;
- pj_pool_t *pool;
- char *outgoing_buffer, *incoming_buffer;
- pj_timestamp start, stop;
- unsigned i;
- pj_highprec_t elapsed, bandwidth;
- pj_size_t total_received;
- pj_status_t rc;
-
- /* Create pool. */
- pool = pj_pool_create(mem, NULL, 4096, 4096, NULL);
- if (!pool)
- return -10;
-
- /* Create producer-consumer pair. */
- rc = app_socketpair(PJ_AF_INET, sock_type, 0, &consumer, &producer);
- if (rc != PJ_SUCCESS) {
- app_perror("...error: create socket pair", rc);
- return -20;
- }
-
- /* Create buffers. */
- outgoing_buffer = pj_pool_alloc(pool, buf_size);
- incoming_buffer = pj_pool_alloc(pool, buf_size);
-
- /* Start loop. */
- pj_get_timestamp(&start);
- total_received = 0;
- for (i=0; i<loop; ++i) {
- pj_ssize_t sent, part_received, received;
- pj_time_val delay;
-
- sent = buf_size;
- rc = pj_sock_send(producer, outgoing_buffer, &sent, 0);
- if (rc != PJ_SUCCESS || sent != (pj_ssize_t)buf_size) {
- app_perror("...error: send()", rc);
- return -61;
- }
-
- /* Repeat recv() until all data is part_received.
- * This applies only for non-UDP of course, since for UDP
- * we would expect all data to be part_received in one packet.
- */
- received = 0;
- do {
- part_received = buf_size-received;
- rc = pj_sock_recv(consumer, incoming_buffer+received,
- &part_received, 0);
- if (rc != PJ_SUCCESS) {
- app_perror("...recv error", rc);
- return -70;
- }
- if (part_received <= 0) {
- PJ_LOG(3,("", "...error: socket has closed (part_received=%d)!",
- part_received));
- return -73;
- }
- if ((pj_size_t)part_received != buf_size-received) {
- if (sock_type != PJ_SOCK_STREAM) {
- PJ_LOG(3,("", "...error: expecting %u bytes, got %u bytes",
- buf_size-received, part_received));
- return -76;
- }
- }
- received += part_received;
- } while ((pj_size_t)received < buf_size);
-
- total_received += received;
-
- /* Stop test if it's been runnign for more than 10 secs. */
- pj_get_timestamp(&stop);
- delay = pj_elapsed_time(&start, &stop);
- if (delay.sec > 10)
- break;
- }
-
- /* Stop timer. */
- pj_get_timestamp(&stop);
-
- elapsed = pj_elapsed_usec(&start, &stop);
-
- /* bandwidth = total_received * 1000 / elapsed */
- bandwidth = total_received;
- pj_highprec_mul(bandwidth, 1000);
- pj_highprec_div(bandwidth, elapsed);
-
- *p_bandwidth = (pj_uint32_t)bandwidth;
-
- /* Close sockets. */
- pj_sock_close(consumer);
- pj_sock_close(producer);
-
- /* Done */
- pj_pool_release(pool);
-
- return 0;
-}
-
-/*
- * sock_perf_test()
- *
- * Main test entry.
- */
-int sock_perf_test(void)
-{
- enum { LOOP = 64 * 1024 };
- int rc;
- unsigned bandwidth;
-
- PJ_LOG(3,("", "...benchmarking socket "
- "(2 sockets, packet=512, single threaded):"));
-
- /* Benchmarking UDP */
- rc = sock_producer_consumer(PJ_SOCK_DGRAM, 512, LOOP, &bandwidth);
- if (rc != 0) return rc;
- PJ_LOG(3,("", "....bandwidth UDP = %d KB/s", bandwidth));
-
- /* Benchmarking TCP */
- rc = sock_producer_consumer(PJ_SOCK_STREAM, 512, LOOP, &bandwidth);
- if (rc != 0) return rc;
- PJ_LOG(3,("", "....bandwidth TCP = %d KB/s", bandwidth));
-
- return rc;
-}
-
-
-#else
-/* To prevent warning about "translation unit is empty"
- * when this test is disabled.
- */
-int dummy_sock_perf_test;
-#endif /* INCLUDE_SOCK_PERF_TEST */
-
-
+/* $Id$ */
+/*
+ * Copyright (C)2003-2006 Benny Prijono <benny@prijono.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#include "test.h"
+#include <pjlib.h>
+#include <pj/compat/high_precision.h>
+
+
+/**
+ * \page page_pjlib_sock_perf_test Test: Socket Performance
+ *
+ * Test the performance of the socket communication. This will perform
+ * simple producer-consumer type of test, where we calculate how long
+ * does it take to send certain number of packets from producer to
+ * consumer.
+ *
+ * This file is <b>pjlib-test/sock_perf.c</b>
+ *
+ * \include pjlib-test/sock_perf.c
+ */
+
+#if INCLUDE_SOCK_PERF_TEST
+
+/*
+ * sock_producer_consumer()
+ *
+ * Simple producer-consumer benchmarking. Send loop number of
+ * buf_size size packets as fast as possible.
+ */
+static int sock_producer_consumer(int sock_type,
+ unsigned buf_size,
+ unsigned loop,
+ unsigned *p_bandwidth)
+{
+ pj_sock_t consumer, producer;
+ pj_pool_t *pool;
+ char *outgoing_buffer, *incoming_buffer;
+ pj_timestamp start, stop;
+ unsigned i;
+ pj_highprec_t elapsed, bandwidth;
+ pj_size_t total_received;
+ pj_status_t rc;
+
+ /* Create pool. */
+ pool = pj_pool_create(mem, NULL, 4096, 4096, NULL);
+ if (!pool)
+ return -10;
+
+ /* Create producer-consumer pair. */
+ rc = app_socketpair(PJ_AF_INET, sock_type, 0, &consumer, &producer);
+ if (rc != PJ_SUCCESS) {
+ app_perror("...error: create socket pair", rc);
+ return -20;
+ }
+
+ /* Create buffers. */
+ outgoing_buffer = pj_pool_alloc(pool, buf_size);
+ incoming_buffer = pj_pool_alloc(pool, buf_size);
+
+ /* Start loop. */
+ pj_get_timestamp(&start);
+ total_received = 0;
+ for (i=0; i<loop; ++i) {
+ pj_ssize_t sent, part_received, received;
+ pj_time_val delay;
+
+ sent = buf_size;
+ rc = pj_sock_send(producer, outgoing_buffer, &sent, 0);
+ if (rc != PJ_SUCCESS || sent != (pj_ssize_t)buf_size) {
+ app_perror("...error: send()", rc);
+ return -61;
+ }
+
+ /* Repeat recv() until all data is part_received.
+ * This applies only for non-UDP of course, since for UDP
+ * we would expect all data to be part_received in one packet.
+ */
+ received = 0;
+ do {
+ part_received = buf_size-received;
+ rc = pj_sock_recv(consumer, incoming_buffer+received,
+ &part_received, 0);
+ if (rc != PJ_SUCCESS) {
+ app_perror("...recv error", rc);
+ return -70;
+ }
+ if (part_received <= 0) {
+ PJ_LOG(3,("", "...error: socket has closed (part_received=%d)!",
+ part_received));
+ return -73;
+ }
+ if ((pj_size_t)part_received != buf_size-received) {
+ if (sock_type != PJ_SOCK_STREAM) {
+ PJ_LOG(3,("", "...error: expecting %u bytes, got %u bytes",
+ buf_size-received, part_received));
+ return -76;
+ }
+ }
+ received += part_received;
+ } while ((pj_size_t)received < buf_size);
+
+ total_received += received;
+
+ /* Stop test if it's been runnign for more than 10 secs. */
+ pj_get_timestamp(&stop);
+ delay = pj_elapsed_time(&start, &stop);
+ if (delay.sec > 10)
+ break;
+ }
+
+ /* Stop timer. */
+ pj_get_timestamp(&stop);
+
+ elapsed = pj_elapsed_usec(&start, &stop);
+
+ /* bandwidth = total_received * 1000 / elapsed */
+ bandwidth = total_received;
+ pj_highprec_mul(bandwidth, 1000);
+ pj_highprec_div(bandwidth, elapsed);
+
+ *p_bandwidth = (pj_uint32_t)bandwidth;
+
+ /* Close sockets. */
+ pj_sock_close(consumer);
+ pj_sock_close(producer);
+
+ /* Done */
+ pj_pool_release(pool);
+
+ return 0;
+}
+
+/*
+ * sock_perf_test()
+ *
+ * Main test entry.
+ */
+int sock_perf_test(void)
+{
+ enum { LOOP = 64 * 1024 };
+ int rc;
+ unsigned bandwidth;
+
+ PJ_LOG(3,("", "...benchmarking socket "
+ "(2 sockets, packet=512, single threaded):"));
+
+ /* Benchmarking UDP */
+ rc = sock_producer_consumer(PJ_SOCK_DGRAM, 512, LOOP, &bandwidth);
+ if (rc != 0) return rc;
+ PJ_LOG(3,("", "....bandwidth UDP = %d KB/s", bandwidth));
+
+ /* Benchmarking TCP */
+ rc = sock_producer_consumer(PJ_SOCK_STREAM, 512, LOOP, &bandwidth);
+ if (rc != 0) return rc;
+ PJ_LOG(3,("", "....bandwidth TCP = %d KB/s", bandwidth));
+
+ return rc;
+}
+
+
+#else
+/* To prevent warning about "translation unit is empty"
+ * when this test is disabled.
+ */
+int dummy_sock_perf_test;
+#endif /* INCLUDE_SOCK_PERF_TEST */
+
+
diff --git a/pjlib/src/pjlib-test/string.c b/pjlib/src/pjlib-test/string.c
index 7199c565..96a4ade1 100644
--- a/pjlib/src/pjlib-test/string.c
+++ b/pjlib/src/pjlib-test/string.c
@@ -1,174 +1,174 @@
-/* $Id$ */
-/*
- * Copyright (C)2003-2006 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 <pj/string.h>
-#include <pj/pool.h>
-#include <pj/log.h>
-#include "test.h"
-
-/**
- * \page page_pjlib_string_test Test: String
- *
- * This file provides implementation of \b string_test(). It tests the
- * functionality of the string API.
- *
- * \section sleep_test_sec Scope of the Test
- *
- * API tested:
- * - pj_str()
- * - pj_strcmp()
- * - pj_strcmp2()
- * - pj_stricmp()
- * - pj_strlen()
- * - pj_strncmp()
- * - pj_strnicmp()
- * - pj_strchr()
- * - pj_strdup()
- * - pj_strdup2()
- * - pj_strcpy()
- * - pj_strcat()
- * - pj_strtrim()
- * - pj_utoa()
- * - pj_strtoul()
- * - pj_create_random_string()
- *
- *
- * This file is <b>pjlib-test/string.c</b>
- *
- * \include pjlib-test/string.c
- */
-
-#if INCLUDE_STRING_TEST
-
-#ifdef _MSC_VER
-# pragma warning(disable: 4204)
-#endif
-
-#define HELLO_WORLD "Hello World"
-#define JUST_HELLO "Hello"
-#define UL_VALUE 3456789012UL
-
-int string_test(void)
-{
- const pj_str_t hello_world = { HELLO_WORLD, strlen(HELLO_WORLD) };
- const pj_str_t just_hello = { JUST_HELLO, strlen(JUST_HELLO) };
- pj_str_t s1, s2, s3, s4, s5;
- enum { RCOUNT = 10, RLEN = 16 };
- pj_str_t random[RCOUNT];
- pj_pool_t *pool;
- int i;
-
- pool = pj_pool_create(mem, NULL, 4096, 0, NULL);
- if (!pool) return -5;
-
- /*
- * pj_str(), pj_strcmp(), pj_stricmp(), pj_strlen(),
- * pj_strncmp(), pj_strchr()
- */
- s1 = pj_str(HELLO_WORLD);
- if (pj_strcmp(&s1, &hello_world) != 0)
- return -10;
- if (pj_stricmp(&s1, &hello_world) != 0)
- return -20;
- if (pj_strcmp(&s1, &just_hello) <= 0)
- return -30;
- if (pj_stricmp(&s1, &just_hello) <= 0)
- return -40;
- if (pj_strlen(&s1) != strlen(HELLO_WORLD))
- return -50;
- if (pj_strncmp(&s1, &hello_world, 5) != 0)
- return -60;
- if (pj_strnicmp(&s1, &hello_world, 5) != 0)
- return -70;
- if (pj_strchr(&s1, HELLO_WORLD[1]) != s1.ptr+1)
- return -80;
-
- /*
- * pj_strdup()
- */
- if (!pj_strdup(pool, &s2, &s1))
- return -100;
- if (pj_strcmp(&s1, &s2) != 0)
- return -110;
-
- /*
- * pj_strcpy(), pj_strcat()
- */
- s3.ptr = pj_pool_alloc(pool, 256);
- if (!s3.ptr)
- return -200;
- pj_strcpy(&s3, &s2);
- pj_strcat(&s3, &just_hello);
-
- if (pj_strcmp2(&s3, HELLO_WORLD JUST_HELLO) != 0)
- return -210;
-
- /*
- * pj_strdup2(), pj_strtrim().
- */
- pj_strdup2(pool, &s4, " " HELLO_WORLD "\t ");
- pj_strtrim(&s4);
- if (pj_strcmp2(&s4, HELLO_WORLD) != 0)
- return -250;
-
- /*
- * pj_utoa()
- */
- s5.ptr = pj_pool_alloc(pool, 16);
- if (!s5.ptr)
- return -270;
- s5.slen = pj_utoa(UL_VALUE, s5.ptr);
-
- /*
- * pj_strtoul()
- */
- if (pj_strtoul(&s5) != UL_VALUE)
- return -280;
-
- /*
- * pj_create_random_string()
- * Check that no duplicate strings are returned.
- */
- for (i=0; i<RCOUNT; ++i) {
- int j;
-
- random[i].ptr = pj_pool_alloc(pool, RLEN);
- if (!random[i].ptr)
- return -320;
-
- random[i].slen = RLEN;
- pj_create_random_string(random[i].ptr, RLEN);
-
- for (j=0; j<i; ++j) {
- if (pj_strcmp(&random[i], &random[j])==0)
- return -330;
- }
- }
-
- /* Done. */
- pj_pool_release(pool);
- return 0;
-}
-
-#else
-/* To prevent warning about "translation unit is empty"
- * when this test is disabled.
- */
-int dummy_string_test;
-#endif /* INCLUDE_STRING_TEST */
-
+/* $Id$ */
+/*
+ * Copyright (C)2003-2006 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 <pj/string.h>
+#include <pj/pool.h>
+#include <pj/log.h>
+#include "test.h"
+
+/**
+ * \page page_pjlib_string_test Test: String
+ *
+ * This file provides implementation of \b string_test(). It tests the
+ * functionality of the string API.
+ *
+ * \section sleep_test_sec Scope of the Test
+ *
+ * API tested:
+ * - pj_str()
+ * - pj_strcmp()
+ * - pj_strcmp2()
+ * - pj_stricmp()
+ * - pj_strlen()
+ * - pj_strncmp()
+ * - pj_strnicmp()
+ * - pj_strchr()
+ * - pj_strdup()
+ * - pj_strdup2()
+ * - pj_strcpy()
+ * - pj_strcat()
+ * - pj_strtrim()
+ * - pj_utoa()
+ * - pj_strtoul()
+ * - pj_create_random_string()
+ *
+ *
+ * This file is <b>pjlib-test/string.c</b>
+ *
+ * \include pjlib-test/string.c
+ */
+
+#if INCLUDE_STRING_TEST
+
+#ifdef _MSC_VER
+# pragma warning(disable: 4204)
+#endif
+
+#define HELLO_WORLD "Hello World"
+#define JUST_HELLO "Hello"
+#define UL_VALUE 3456789012UL
+
+int string_test(void)
+{
+ const pj_str_t hello_world = { HELLO_WORLD, strlen(HELLO_WORLD) };
+ const pj_str_t just_hello = { JUST_HELLO, strlen(JUST_HELLO) };
+ pj_str_t s1, s2, s3, s4, s5;
+ enum { RCOUNT = 10, RLEN = 16 };
+ pj_str_t random[RCOUNT];
+ pj_pool_t *pool;
+ int i;
+
+ pool = pj_pool_create(mem, NULL, 4096, 0, NULL);
+ if (!pool) return -5;
+
+ /*
+ * pj_str(), pj_strcmp(), pj_stricmp(), pj_strlen(),
+ * pj_strncmp(), pj_strchr()
+ */
+ s1 = pj_str(HELLO_WORLD);
+ if (pj_strcmp(&s1, &hello_world) != 0)
+ return -10;
+ if (pj_stricmp(&s1, &hello_world) != 0)
+ return -20;
+ if (pj_strcmp(&s1, &just_hello) <= 0)
+ return -30;
+ if (pj_stricmp(&s1, &just_hello) <= 0)
+ return -40;
+ if (pj_strlen(&s1) != strlen(HELLO_WORLD))
+ return -50;
+ if (pj_strncmp(&s1, &hello_world, 5) != 0)
+ return -60;
+ if (pj_strnicmp(&s1, &hello_world, 5) != 0)
+ return -70;
+ if (pj_strchr(&s1, HELLO_WORLD[1]) != s1.ptr+1)
+ return -80;
+
+ /*
+ * pj_strdup()
+ */
+ if (!pj_strdup(pool, &s2, &s1))
+ return -100;
+ if (pj_strcmp(&s1, &s2) != 0)
+ return -110;
+
+ /*
+ * pj_strcpy(), pj_strcat()
+ */
+ s3.ptr = pj_pool_alloc(pool, 256);
+ if (!s3.ptr)
+ return -200;
+ pj_strcpy(&s3, &s2);
+ pj_strcat(&s3, &just_hello);
+
+ if (pj_strcmp2(&s3, HELLO_WORLD JUST_HELLO) != 0)
+ return -210;
+
+ /*
+ * pj_strdup2(), pj_strtrim().
+ */
+ pj_strdup2(pool, &s4, " " HELLO_WORLD "\t ");
+ pj_strtrim(&s4);
+ if (pj_strcmp2(&s4, HELLO_WORLD) != 0)
+ return -250;
+
+ /*
+ * pj_utoa()
+ */
+ s5.ptr = pj_pool_alloc(pool, 16);
+ if (!s5.ptr)
+ return -270;
+ s5.slen = pj_utoa(UL_VALUE, s5.ptr);
+
+ /*
+ * pj_strtoul()
+ */
+ if (pj_strtoul(&s5) != UL_VALUE)
+ return -280;
+
+ /*
+ * pj_create_random_string()
+ * Check that no duplicate strings are returned.
+ */
+ for (i=0; i<RCOUNT; ++i) {
+ int j;
+
+ random[i].ptr = pj_pool_alloc(pool, RLEN);
+ if (!random[i].ptr)
+ return -320;
+
+ random[i].slen = RLEN;
+ pj_create_random_string(random[i].ptr, RLEN);
+
+ for (j=0; j<i; ++j) {
+ if (pj_strcmp(&random[i], &random[j])==0)
+ return -330;
+ }
+ }
+
+ /* Done. */
+ pj_pool_release(pool);
+ return 0;
+}
+
+#else
+/* To prevent warning about "translation unit is empty"
+ * when this test is disabled.
+ */
+int dummy_string_test;
+#endif /* INCLUDE_STRING_TEST */
+
diff --git a/pjlib/src/pjlib-test/test.c b/pjlib/src/pjlib-test/test.c
index 5fb89afa..6a1342c9 100644
--- a/pjlib/src/pjlib-test/test.c
+++ b/pjlib/src/pjlib-test/test.c
@@ -1,199 +1,199 @@
-/* $Id$ */
-/*
- * Copyright (C)2003-2006 Benny Prijono <benny@prijono.org>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
-#include "test.h"
-#include <pjlib.h>
-#ifdef _MSC_VER
-# pragma warning(disable:4127)
-#endif
-
-#define DO_TEST(test) do { \
- PJ_LOG(3, ("test", "Running %s...", #test)); \
- rc = test; \
- PJ_LOG(3, ("test", \
- "%s(%d)", \
- (rc ? "..ERROR" : "..success"), rc)); \
- if (rc!=0) goto on_return; \
- } while (0)
-
-
-pj_pool_factory *mem;
-
-int param_echo_sock_type;
-const char *param_echo_server = ECHO_SERVER_ADDRESS;
-int param_echo_port = ECHO_SERVER_START_PORT;
-
-int test_inner(void)
-{
- pj_caching_pool caching_pool;
- const char *filename;
- int line;
- int rc = 0;
-
- mem = &caching_pool.factory;
-
- pj_log_set_level(3);
- pj_log_set_decor(PJ_LOG_HAS_NEWLINE | PJ_LOG_HAS_TIME |
- PJ_LOG_HAS_MICRO_SEC);
-
- rc = pj_init();
- if (rc != 0) {
- app_perror("pj_init() error!!", rc);
- return rc;
- }
-
- pj_dump_config();
- pj_caching_pool_init( &caching_pool, &pj_pool_factory_default_policy, 0 );
-
-#if INCLUDE_ERRNO_TEST
- DO_TEST( errno_test() );
-#endif
-
-#if INCLUDE_TIMESTAMP_TEST
- DO_TEST( timestamp_test() );
-#endif
-
-#if INCLUDE_EXCEPTION_TEST
- DO_TEST( exception_test() );
-#endif
-
-#if INCLUDE_RAND_TEST
- DO_TEST( rand_test() );
-#endif
-
-#if INCLUDE_LIST_TEST
- DO_TEST( list_test() );
-#endif
-
-#if INCLUDE_POOL_TEST
- DO_TEST( pool_test() );
-#endif
-
-#if INCLUDE_POOL_PERF_TEST
- DO_TEST( pool_perf_test() );
-#endif
-
-#if INCLUDE_STRING_TEST
- DO_TEST( string_test() );
-#endif
-
-#if INCLUDE_FIFOBUF_TEST
- DO_TEST( fifobuf_test() );
-#endif
-
-#if INCLUDE_RBTREE_TEST
- DO_TEST( rbtree_test() );
-#endif
-
-#if INCLUDE_ATOMIC_TEST
- DO_TEST( atomic_test() );
-#endif
-
-#if INCLUDE_MUTEX_TEST
- DO_TEST( mutex_test() );
-#endif
-
-#if INCLUDE_TIMER_TEST
- DO_TEST( timer_test() );
-#endif
-
-#if INCLUDE_SLEEP_TEST
- DO_TEST( sleep_test() );
-#endif
-
-#if INCLUDE_THREAD_TEST
- DO_TEST( thread_test() );
-#endif
-
-#if INCLUDE_SOCK_TEST
- DO_TEST( sock_test() );
-#endif
-
-#if INCLUDE_SOCK_PERF_TEST
- DO_TEST( sock_perf_test() );
-#endif
-
-#if INCLUDE_SELECT_TEST
- DO_TEST( select_test() );
-#endif
-
-#if INCLUDE_UDP_IOQUEUE_TEST
- DO_TEST( udp_ioqueue_test() );
-#endif
-
-#if PJ_HAS_TCP && INCLUDE_TCP_IOQUEUE_TEST
- DO_TEST( tcp_ioqueue_test() );
-#endif
-
-#if INCLUDE_IOQUEUE_PERF_TEST
- DO_TEST( ioqueue_perf_test() );
-#endif
-
-#if INCLUDE_FILE_TEST
- DO_TEST( file_test() );
-#endif
-
-#if INCLUDE_ECHO_SERVER
- //echo_server();
- //echo_srv_sync();
- udp_echo_srv_ioqueue();
-
-#elif INCLUDE_ECHO_CLIENT
- if (param_echo_sock_type == 0)
- param_echo_sock_type = PJ_SOCK_DGRAM;
-
- echo_client( param_echo_sock_type,
- param_echo_server,
- param_echo_port);
-#endif
-
- goto on_return;
-
-on_return:
-
- pj_caching_pool_destroy( &caching_pool );
-
- PJ_LOG(3,("test", ""));
-
- pj_thread_get_stack_info(pj_thread_this(), &filename, &line);
- PJ_LOG(3,("test", "Stack max usage: %u, deepest: %s:%u",
- pj_thread_get_stack_max_usage(pj_thread_this()),
- filename, line));
- if (rc == 0)
- PJ_LOG(3,("test", "Looks like everything is okay!.."));
- else
- PJ_LOG(3,("test", "Test completed with error(s)"));
- return 0;
-}
-
-int test_main(void)
-{
- PJ_USE_EXCEPTION;
-
- PJ_TRY {
- return test_inner();
- }
- PJ_DEFAULT {
- int id = PJ_GET_EXCEPTION();
- PJ_LOG(3,("test", "FATAL: unhandled exception id %d (%s)",
- id, pj_exception_id_name(id)));
- }
- PJ_END;
-
- return -1;
-}
+/* $Id$ */
+/*
+ * Copyright (C)2003-2006 Benny Prijono <benny@prijono.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#include "test.h"
+#include <pjlib.h>
+#ifdef _MSC_VER
+# pragma warning(disable:4127)
+#endif
+
+#define DO_TEST(test) do { \
+ PJ_LOG(3, ("test", "Running %s...", #test)); \
+ rc = test; \
+ PJ_LOG(3, ("test", \
+ "%s(%d)", \
+ (rc ? "..ERROR" : "..success"), rc)); \
+ if (rc!=0) goto on_return; \
+ } while (0)
+
+
+pj_pool_factory *mem;
+
+int param_echo_sock_type;
+const char *param_echo_server = ECHO_SERVER_ADDRESS;
+int param_echo_port = ECHO_SERVER_START_PORT;
+
+int test_inner(void)
+{
+ pj_caching_pool caching_pool;
+ const char *filename;
+ int line;
+ int rc = 0;
+
+ mem = &caching_pool.factory;
+
+ pj_log_set_level(3);
+ pj_log_set_decor(PJ_LOG_HAS_NEWLINE | PJ_LOG_HAS_TIME |
+ PJ_LOG_HAS_MICRO_SEC);
+
+ rc = pj_init();
+ if (rc != 0) {
+ app_perror("pj_init() error!!", rc);
+ return rc;
+ }
+
+ pj_dump_config();
+ pj_caching_pool_init( &caching_pool, &pj_pool_factory_default_policy, 0 );
+
+#if INCLUDE_ERRNO_TEST
+ DO_TEST( errno_test() );
+#endif
+
+#if INCLUDE_TIMESTAMP_TEST
+ DO_TEST( timestamp_test() );
+#endif
+
+#if INCLUDE_EXCEPTION_TEST
+ DO_TEST( exception_test() );
+#endif
+
+#if INCLUDE_RAND_TEST
+ DO_TEST( rand_test() );
+#endif
+
+#if INCLUDE_LIST_TEST
+ DO_TEST( list_test() );
+#endif
+
+#if INCLUDE_POOL_TEST
+ DO_TEST( pool_test() );
+#endif
+
+#if INCLUDE_POOL_PERF_TEST
+ DO_TEST( pool_perf_test() );
+#endif
+
+#if INCLUDE_STRING_TEST
+ DO_TEST( string_test() );
+#endif
+
+#if INCLUDE_FIFOBUF_TEST
+ DO_TEST( fifobuf_test() );
+#endif
+
+#if INCLUDE_RBTREE_TEST
+ DO_TEST( rbtree_test() );
+#endif
+
+#if INCLUDE_ATOMIC_TEST
+ DO_TEST( atomic_test() );
+#endif
+
+#if INCLUDE_MUTEX_TEST
+ DO_TEST( mutex_test() );
+#endif
+
+#if INCLUDE_TIMER_TEST
+ DO_TEST( timer_test() );
+#endif
+
+#if INCLUDE_SLEEP_TEST
+ DO_TEST( sleep_test() );
+#endif
+
+#if INCLUDE_THREAD_TEST
+ DO_TEST( thread_test() );
+#endif
+
+#if INCLUDE_SOCK_TEST
+ DO_TEST( sock_test() );
+#endif
+
+#if INCLUDE_SOCK_PERF_TEST
+ DO_TEST( sock_perf_test() );
+#endif
+
+#if INCLUDE_SELECT_TEST
+ DO_TEST( select_test() );
+#endif
+
+#if INCLUDE_UDP_IOQUEUE_TEST
+ DO_TEST( udp_ioqueue_test() );
+#endif
+
+#if PJ_HAS_TCP && INCLUDE_TCP_IOQUEUE_TEST
+ DO_TEST( tcp_ioqueue_test() );
+#endif
+
+#if INCLUDE_IOQUEUE_PERF_TEST
+ DO_TEST( ioqueue_perf_test() );
+#endif
+
+#if INCLUDE_FILE_TEST
+ DO_TEST( file_test() );
+#endif
+
+#if INCLUDE_ECHO_SERVER
+ //echo_server();
+ //echo_srv_sync();
+ udp_echo_srv_ioqueue();
+
+#elif INCLUDE_ECHO_CLIENT
+ if (param_echo_sock_type == 0)
+ param_echo_sock_type = PJ_SOCK_DGRAM;
+
+ echo_client( param_echo_sock_type,
+ param_echo_server,
+ param_echo_port);
+#endif
+
+ goto on_return;
+
+on_return:
+
+ pj_caching_pool_destroy( &caching_pool );
+
+ PJ_LOG(3,("test", ""));
+
+ pj_thread_get_stack_info(pj_thread_this(), &filename, &line);
+ PJ_LOG(3,("test", "Stack max usage: %u, deepest: %s:%u",
+ pj_thread_get_stack_max_usage(pj_thread_this()),
+ filename, line));
+ if (rc == 0)
+ PJ_LOG(3,("test", "Looks like everything is okay!.."));
+ else
+ PJ_LOG(3,("test", "Test completed with error(s)"));
+ return 0;
+}
+
+int test_main(void)
+{
+ PJ_USE_EXCEPTION;
+
+ PJ_TRY {
+ return test_inner();
+ }
+ PJ_DEFAULT {
+ int id = PJ_GET_EXCEPTION();
+ PJ_LOG(3,("test", "FATAL: unhandled exception id %d (%s)",
+ id, pj_exception_id_name(id)));
+ }
+ PJ_END;
+
+ return -1;
+}
diff --git a/pjlib/src/pjlib-test/test.h b/pjlib/src/pjlib-test/test.h
index 4f3267b0..8571d9dd 100644
--- a/pjlib/src/pjlib-test/test.h
+++ b/pjlib/src/pjlib-test/test.h
@@ -1,111 +1,111 @@
-/* $Id$ */
-/*
- * Copyright (C)2003-2006 Benny Prijono <benny@prijono.org>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
-#ifndef __PJLIB_TEST_H__
-#define __PJLIB_TEST_H__
-
-#include <pj/types.h>
-
-#define GROUP_LIBC 1
-#define GROUP_OS 1
-#define GROUP_DATA_STRUCTURE 1
-#define GROUP_NETWORK 1
-#define GROUP_FILE 1
-
-#define INCLUDE_ERRNO_TEST GROUP_LIBC
-#define INCLUDE_TIMESTAMP_TEST GROUP_OS
-#define INCLUDE_EXCEPTION_TEST GROUP_LIBC
-#define INCLUDE_RAND_TEST GROUP_LIBC
-#define INCLUDE_LIST_TEST GROUP_DATA_STRUCTURE
-#define INCLUDE_POOL_TEST GROUP_LIBC
-#define INCLUDE_POOL_PERF_TEST (PJ_HAS_MALLOC && GROUP_LIBC)
-#define INCLUDE_STRING_TEST GROUP_DATA_STRUCTURE
-#define INCLUDE_FIFOBUF_TEST 0 // GROUP_DATA_STRUCTURE
-#define INCLUDE_RBTREE_TEST GROUP_DATA_STRUCTURE
-#define INCLUDE_TIMER_TEST GROUP_DATA_STRUCTURE
-#define INCLUDE_ATOMIC_TEST GROUP_OS
-#define INCLUDE_MUTEX_TEST GROUP_OS
-#define INCLUDE_SLEEP_TEST GROUP_OS
-#define INCLUDE_THREAD_TEST GROUP_OS
-#define INCLUDE_SOCK_TEST GROUP_NETWORK
-#define INCLUDE_SOCK_PERF_TEST GROUP_NETWORK
-#define INCLUDE_SELECT_TEST GROUP_NETWORK
-#define INCLUDE_UDP_IOQUEUE_TEST GROUP_NETWORK
-#define INCLUDE_TCP_IOQUEUE_TEST GROUP_NETWORK
-#define INCLUDE_IOQUEUE_PERF_TEST GROUP_NETWORK
-#define INCLUDE_FILE_TEST GROUP_FILE
-
-#define INCLUDE_ECHO_SERVER 0
-#define INCLUDE_ECHO_CLIENT 0
-
-
-#define ECHO_SERVER_MAX_THREADS 2
-#define ECHO_SERVER_START_PORT 65000
-#define ECHO_SERVER_ADDRESS "compaq.home"
-#define ECHO_SERVER_DURATION_MSEC (60*60*1000)
-
-#define ECHO_CLIENT_MAX_THREADS 6
-
-PJ_BEGIN_DECL
-
-extern int errno_test(void);
-extern int timestamp_test(void);
-extern int exception_test(void);
-extern int rand_test(void);
-extern int list_test(void);
-extern int pool_test(void);
-extern int pool_perf_test(void);
-extern int string_test(void);
-extern int fifobuf_test(void);
-extern int timer_test(void);
-extern int rbtree_test(void);
-extern int atomic_test(void);
-extern int mutex_test(void);
-extern int sleep_test(void);
-extern int thread_test(void);
-extern int sock_test(void);
-extern int sock_perf_test(void);
-extern int select_test(void);
-extern int udp_ioqueue_test(void);
-extern int tcp_ioqueue_test(void);
-extern int ioqueue_perf_test(void);
-extern int file_test(void);
-
-extern int echo_server(void);
-extern int echo_client(int sock_type, const char *server, int port);
-
-extern int echo_srv_sync(void);
-extern int udp_echo_srv_ioqueue(void);
-extern int echo_srv_common_loop(pj_atomic_t *bytes_counter);
-
-extern pj_pool_factory *mem;
-
-extern int test_main(void);
-extern void app_perror(const char *msg, pj_status_t err);
-extern pj_status_t app_socket(int family, int type, int proto, int port,
- pj_sock_t *ptr_sock);
-extern pj_status_t app_socketpair(int family, int type, int protocol,
- pj_sock_t *server, pj_sock_t *client);
-
-//#define TRACE_(expr) PJ_LOG(3,expr)
-#define TRACE_(expr)
-
-PJ_END_DECL
-
-#endif /* __PJLIB_TEST_H__ */
-
+/* $Id$ */
+/*
+ * Copyright (C)2003-2006 Benny Prijono <benny@prijono.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#ifndef __PJLIB_TEST_H__
+#define __PJLIB_TEST_H__
+
+#include <pj/types.h>
+
+#define GROUP_LIBC 1
+#define GROUP_OS 1
+#define GROUP_DATA_STRUCTURE 1
+#define GROUP_NETWORK 1
+#define GROUP_FILE 1
+
+#define INCLUDE_ERRNO_TEST GROUP_LIBC
+#define INCLUDE_TIMESTAMP_TEST GROUP_OS
+#define INCLUDE_EXCEPTION_TEST GROUP_LIBC
+#define INCLUDE_RAND_TEST GROUP_LIBC
+#define INCLUDE_LIST_TEST GROUP_DATA_STRUCTURE
+#define INCLUDE_POOL_TEST GROUP_LIBC
+#define INCLUDE_POOL_PERF_TEST (PJ_HAS_MALLOC && GROUP_LIBC)
+#define INCLUDE_STRING_TEST GROUP_DATA_STRUCTURE
+#define INCLUDE_FIFOBUF_TEST 0 // GROUP_DATA_STRUCTURE
+#define INCLUDE_RBTREE_TEST GROUP_DATA_STRUCTURE
+#define INCLUDE_TIMER_TEST GROUP_DATA_STRUCTURE
+#define INCLUDE_ATOMIC_TEST GROUP_OS
+#define INCLUDE_MUTEX_TEST GROUP_OS
+#define INCLUDE_SLEEP_TEST GROUP_OS
+#define INCLUDE_THREAD_TEST GROUP_OS
+#define INCLUDE_SOCK_TEST GROUP_NETWORK
+#define INCLUDE_SOCK_PERF_TEST GROUP_NETWORK
+#define INCLUDE_SELECT_TEST GROUP_NETWORK
+#define INCLUDE_UDP_IOQUEUE_TEST GROUP_NETWORK
+#define INCLUDE_TCP_IOQUEUE_TEST GROUP_NETWORK
+#define INCLUDE_IOQUEUE_PERF_TEST GROUP_NETWORK
+#define INCLUDE_FILE_TEST GROUP_FILE
+
+#define INCLUDE_ECHO_SERVER 0
+#define INCLUDE_ECHO_CLIENT 0
+
+
+#define ECHO_SERVER_MAX_THREADS 2
+#define ECHO_SERVER_START_PORT 65000
+#define ECHO_SERVER_ADDRESS "compaq.home"
+#define ECHO_SERVER_DURATION_MSEC (60*60*1000)
+
+#define ECHO_CLIENT_MAX_THREADS 6
+
+PJ_BEGIN_DECL
+
+extern int errno_test(void);
+extern int timestamp_test(void);
+extern int exception_test(void);
+extern int rand_test(void);
+extern int list_test(void);
+extern int pool_test(void);
+extern int pool_perf_test(void);
+extern int string_test(void);
+extern int fifobuf_test(void);
+extern int timer_test(void);
+extern int rbtree_test(void);
+extern int atomic_test(void);
+extern int mutex_test(void);
+extern int sleep_test(void);
+extern int thread_test(void);
+extern int sock_test(void);
+extern int sock_perf_test(void);
+extern int select_test(void);
+extern int udp_ioqueue_test(void);
+extern int tcp_ioqueue_test(void);
+extern int ioqueue_perf_test(void);
+extern int file_test(void);
+
+extern int echo_server(void);
+extern int echo_client(int sock_type, const char *server, int port);
+
+extern int echo_srv_sync(void);
+extern int udp_echo_srv_ioqueue(void);
+extern int echo_srv_common_loop(pj_atomic_t *bytes_counter);
+
+extern pj_pool_factory *mem;
+
+extern int test_main(void);
+extern void app_perror(const char *msg, pj_status_t err);
+extern pj_status_t app_socket(int family, int type, int proto, int port,
+ pj_sock_t *ptr_sock);
+extern pj_status_t app_socketpair(int family, int type, int protocol,
+ pj_sock_t *server, pj_sock_t *client);
+
+//#define TRACE_(expr) PJ_LOG(3,expr)
+#define TRACE_(expr)
+
+PJ_END_DECL
+
+#endif /* __PJLIB_TEST_H__ */
+
diff --git a/pjlib/src/pjlib-test/thread.c b/pjlib/src/pjlib-test/thread.c
index 68264ea8..2c7c41f7 100644
--- a/pjlib/src/pjlib-test/thread.c
+++ b/pjlib/src/pjlib-test/thread.c
@@ -1,289 +1,289 @@
-/* $Id$ */
-/*
- * Copyright (C)2003-2006 Benny Prijono <benny@prijono.org>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
-#include "test.h"
-
-/**
- * \page page_pjlib_thread_test Test: Thread Test
- *
- * This file contains \a thread_test() definition.
- *
- * \section thread_test_scope_sec Scope of Test
- * This tests:
- * - whether PJ_THREAD_SUSPENDED flag works.
- * - whether multithreading works.
- * - whether thread timeslicing works, and threads have equal
- * time-slice proportion.
- *
- * APIs tested:
- * - pj_thread_create()
- * - pj_thread_register()
- * - pj_thread_this()
- * - pj_thread_get_name()
- * - pj_thread_destroy()
- * - pj_thread_resume()
- * - pj_thread_sleep()
- * - pj_thread_join()
- * - pj_thread_destroy()
- *
- *
- * This file is <b>pjlib-test/thread.c</b>
- *
- * \include pjlib-test/thread.c
- */
-#if INCLUDE_THREAD_TEST
-
-#include <pjlib.h>
-
-#define THIS_FILE "thread_test"
-
-static int quit_flag=0;
-
-/*
- * The thread's entry point.
- *
- * Each of the thread mainly will just execute the loop which
- * increments a variable.
- */
-static void* thread_proc(pj_uint32_t *pcounter)
-{
- /* Test that pj_thread_register() works. */
- pj_thread_desc desc;
- pj_thread_t *this_thread;
- pj_status_t rc;
-
- rc = pj_thread_register("thread", desc, &this_thread);
- if (rc != PJ_SUCCESS) {
- app_perror("...error in pj_thread_register", rc);
- return NULL;
- }
-
- /* Test that pj_thread_this() works */
- this_thread = pj_thread_this();
- if (this_thread == NULL) {
- PJ_LOG(3,(THIS_FILE, "...error: pj_thread_this() returns NULL!"));
- return NULL;
- }
-
- /* Test that pj_thread_get_name() works */
- if (pj_thread_get_name(this_thread) == NULL) {
- PJ_LOG(3,(THIS_FILE, "...error: pj_thread_get_name() returns NULL!"));
- return NULL;
- }
-
- /* Main loop */
- for (;!quit_flag;) {
- (*pcounter)++;
- //Must sleep if platform doesn't do time-slicing.
- pj_thread_sleep(0);
- }
-
- return NULL;
-}
-
-/*
- * simple_thread()
- */
-static int simple_thread(const char *title, unsigned flags)
-{
- pj_pool_t *pool;
- pj_thread_t *thread;
- pj_status_t rc;
- pj_uint32_t counter = 0;
-
- PJ_LOG(3,(THIS_FILE, "..%s", title));
-
- pool = pj_pool_create(mem, NULL, 4000, 4000, NULL);
- if (!pool)
- return -1000;
-
- quit_flag = 0;
-
- rc = pj_thread_create(pool, "thread", (pj_thread_proc*)&thread_proc,
- &counter,
- PJ_THREAD_DEFAULT_STACK_SIZE,
- flags,
- &thread);
-
- if (rc != PJ_SUCCESS) {
- app_perror("...error: unable to create thread", rc);
- return -1010;
- }
-
- if (flags & PJ_THREAD_SUSPENDED) {
- rc = pj_thread_resume(thread);
- if (rc != PJ_SUCCESS) {
- app_perror("...error: resume thread error", rc);
- return -1020;
- }
- }
-
- PJ_LOG(3,(THIS_FILE, "..waiting for thread to quit.."));
-
- quit_flag = 1;
- pj_thread_join(thread);
-
- pj_pool_release(pool);
-
- PJ_LOG(3,(THIS_FILE, "...%s success", title));
- return PJ_SUCCESS;
-}
-
-
-/*
- * timeslice_test()
- */
-static int timeslice_test(void)
-{
- enum { NUM_THREADS = 4 };
- pj_pool_t *pool;
- pj_uint32_t counter[NUM_THREADS], lowest, highest, diff;
- pj_thread_t *thread[NUM_THREADS];
- int i;
- pj_status_t rc;
-
- quit_flag = 0;
-
- pool = pj_pool_create(mem, NULL, 4096, 0, NULL);
- if (!pool)
- return -10;
-
- PJ_LOG(3,(THIS_FILE, "..timeslice testing with %d threads", NUM_THREADS));
-
- /* Create all threads in suspended mode. */
- for (i=0; i<NUM_THREADS; ++i) {
- counter[i] = 0;
- rc = pj_thread_create(pool, "thread", (pj_thread_proc*)&thread_proc,
- &counter[i],
- PJ_THREAD_DEFAULT_STACK_SIZE,
- PJ_THREAD_SUSPENDED,
- &thread[i]);
- if (rc!=PJ_SUCCESS) {
- app_perror("...ERROR in pj_thread_create()", rc);
- return -20;
- }
- }
-
- /* Sleep for 1 second.
- * The purpose of this is to test whether all threads are suspended.
- */
- pj_thread_sleep(1000);
-
- /* Check that all counters are still zero. */
- for (i=0; i<NUM_THREADS; ++i) {
- if (counter[i] != 0) {
- PJ_LOG(3,(THIS_FILE, "....ERROR! Thread %d-th is not suspended!",
- i));
- return -30;
- }
- }
-
- /* Now resume all threads. */
- for (i=0; i<NUM_THREADS; ++i) {
- rc = pj_thread_resume(thread[i]);
- if (rc != PJ_SUCCESS) {
- app_perror("...ERROR in pj_thread_resume()", rc);
- return -40;
- }
- }
-
- /* Main thread sleeps for some time to allow threads to run.
- * The longer we sleep, the more accurate the calculation will be,
- * but it'll make user waits for longer for the test to finish.
- */
- pj_thread_sleep(5000);
-
- /* Signal all threads to quit. */
- quit_flag = 1;
-
- /* Wait until all threads quit, then destroy. */
- for (i=0; i<NUM_THREADS; ++i) {
- rc = pj_thread_join(thread[i]);
- if (rc != PJ_SUCCESS) {
- app_perror("...ERROR in pj_thread_join()", rc);
- return -50;
- }
- rc = pj_thread_destroy(thread[i]);
- if (rc != PJ_SUCCESS) {
- app_perror("...ERROR in pj_thread_destroy()", rc);
- return -60;
- }
- }
-
- /* Now examine the value of the counters.
- * Check that all threads had equal proportion of processing.
- */
- lowest = 0xFFFFFFFF;
- highest = 0;
- for (i=0; i<NUM_THREADS; ++i) {
- if (counter[i] < lowest)
- lowest = counter[i];
- if (counter[i] > highest)
- highest = counter[i];
- }
-
- /* Check that all threads are running. */
- if (lowest < 2) {
- PJ_LOG(3,(THIS_FILE, "...ERROR: not all threads were running!"));
- return -70;
- }
-
- /* The difference between lowest and higest should be lower than 50%.
- */
- diff = (highest-lowest)*100 / ((highest+lowest)/2);
- if ( diff >= 50) {
- PJ_LOG(3,(THIS_FILE, "...ERROR: thread didn't have equal timeslice!"));
- PJ_LOG(3,(THIS_FILE, ".....lowest counter=%u, highest counter=%u, diff=%u%%",
- lowest, highest, diff));
- return -80;
- } else {
- PJ_LOG(3,(THIS_FILE,
- "...info: timeslice diff between lowest & highest=%u%%",
- diff));
- }
-
- return 0;
-}
-
-int thread_test(void)
-{
- int rc;
-
- rc = simple_thread("simple thread test", 0);
- if (rc != PJ_SUCCESS)
- return rc;
-
- rc = simple_thread("suspended thread test", PJ_THREAD_SUSPENDED);
- if (rc != PJ_SUCCESS)
- return rc;
-
- rc = timeslice_test();
- if (rc != PJ_SUCCESS)
- return rc;
-
- return rc;
-}
-
-#else
-/* To prevent warning about "translation unit is empty"
- * when this test is disabled.
- */
-int dummy_thread_test;
-#endif /* INCLUDE_THREAD_TEST */
-
-
+/* $Id$ */
+/*
+ * Copyright (C)2003-2006 Benny Prijono <benny@prijono.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#include "test.h"
+
+/**
+ * \page page_pjlib_thread_test Test: Thread Test
+ *
+ * This file contains \a thread_test() definition.
+ *
+ * \section thread_test_scope_sec Scope of Test
+ * This tests:
+ * - whether PJ_THREAD_SUSPENDED flag works.
+ * - whether multithreading works.
+ * - whether thread timeslicing works, and threads have equal
+ * time-slice proportion.
+ *
+ * APIs tested:
+ * - pj_thread_create()
+ * - pj_thread_register()
+ * - pj_thread_this()
+ * - pj_thread_get_name()
+ * - pj_thread_destroy()
+ * - pj_thread_resume()
+ * - pj_thread_sleep()
+ * - pj_thread_join()
+ * - pj_thread_destroy()
+ *
+ *
+ * This file is <b>pjlib-test/thread.c</b>
+ *
+ * \include pjlib-test/thread.c
+ */
+#if INCLUDE_THREAD_TEST
+
+#include <pjlib.h>
+
+#define THIS_FILE "thread_test"
+
+static int quit_flag=0;
+
+/*
+ * The thread's entry point.
+ *
+ * Each of the thread mainly will just execute the loop which
+ * increments a variable.
+ */
+static void* thread_proc(pj_uint32_t *pcounter)
+{
+ /* Test that pj_thread_register() works. */
+ pj_thread_desc desc;
+ pj_thread_t *this_thread;
+ pj_status_t rc;
+
+ rc = pj_thread_register("thread", desc, &this_thread);
+ if (rc != PJ_SUCCESS) {
+ app_perror("...error in pj_thread_register", rc);
+ return NULL;
+ }
+
+ /* Test that pj_thread_this() works */
+ this_thread = pj_thread_this();
+ if (this_thread == NULL) {
+ PJ_LOG(3,(THIS_FILE, "...error: pj_thread_this() returns NULL!"));
+ return NULL;
+ }
+
+ /* Test that pj_thread_get_name() works */
+ if (pj_thread_get_name(this_thread) == NULL) {
+ PJ_LOG(3,(THIS_FILE, "...error: pj_thread_get_name() returns NULL!"));
+ return NULL;
+ }
+
+ /* Main loop */
+ for (;!quit_flag;) {
+ (*pcounter)++;
+ //Must sleep if platform doesn't do time-slicing.
+ pj_thread_sleep(0);
+ }
+
+ return NULL;
+}
+
+/*
+ * simple_thread()
+ */
+static int simple_thread(const char *title, unsigned flags)
+{
+ pj_pool_t *pool;
+ pj_thread_t *thread;
+ pj_status_t rc;
+ pj_uint32_t counter = 0;
+
+ PJ_LOG(3,(THIS_FILE, "..%s", title));
+
+ pool = pj_pool_create(mem, NULL, 4000, 4000, NULL);
+ if (!pool)
+ return -1000;
+
+ quit_flag = 0;
+
+ rc = pj_thread_create(pool, "thread", (pj_thread_proc*)&thread_proc,
+ &counter,
+ PJ_THREAD_DEFAULT_STACK_SIZE,
+ flags,
+ &thread);
+
+ if (rc != PJ_SUCCESS) {
+ app_perror("...error: unable to create thread", rc);
+ return -1010;
+ }
+
+ if (flags & PJ_THREAD_SUSPENDED) {
+ rc = pj_thread_resume(thread);
+ if (rc != PJ_SUCCESS) {
+ app_perror("...error: resume thread error", rc);
+ return -1020;
+ }
+ }
+
+ PJ_LOG(3,(THIS_FILE, "..waiting for thread to quit.."));
+
+ quit_flag = 1;
+ pj_thread_join(thread);
+
+ pj_pool_release(pool);
+
+ PJ_LOG(3,(THIS_FILE, "...%s success", title));
+ return PJ_SUCCESS;
+}
+
+
+/*
+ * timeslice_test()
+ */
+static int timeslice_test(void)
+{
+ enum { NUM_THREADS = 4 };
+ pj_pool_t *pool;
+ pj_uint32_t counter[NUM_THREADS], lowest, highest, diff;
+ pj_thread_t *thread[NUM_THREADS];
+ int i;
+ pj_status_t rc;
+
+ quit_flag = 0;
+
+ pool = pj_pool_create(mem, NULL, 4096, 0, NULL);
+ if (!pool)
+ return -10;
+
+ PJ_LOG(3,(THIS_FILE, "..timeslice testing with %d threads", NUM_THREADS));
+
+ /* Create all threads in suspended mode. */
+ for (i=0; i<NUM_THREADS; ++i) {
+ counter[i] = 0;
+ rc = pj_thread_create(pool, "thread", (pj_thread_proc*)&thread_proc,
+ &counter[i],
+ PJ_THREAD_DEFAULT_STACK_SIZE,
+ PJ_THREAD_SUSPENDED,
+ &thread[i]);
+ if (rc!=PJ_SUCCESS) {
+ app_perror("...ERROR in pj_thread_create()", rc);
+ return -20;
+ }
+ }
+
+ /* Sleep for 1 second.
+ * The purpose of this is to test whether all threads are suspended.
+ */
+ pj_thread_sleep(1000);
+
+ /* Check that all counters are still zero. */
+ for (i=0; i<NUM_THREADS; ++i) {
+ if (counter[i] != 0) {
+ PJ_LOG(3,(THIS_FILE, "....ERROR! Thread %d-th is not suspended!",
+ i));
+ return -30;
+ }
+ }
+
+ /* Now resume all threads. */
+ for (i=0; i<NUM_THREADS; ++i) {
+ rc = pj_thread_resume(thread[i]);
+ if (rc != PJ_SUCCESS) {
+ app_perror("...ERROR in pj_thread_resume()", rc);
+ return -40;
+ }
+ }
+
+ /* Main thread sleeps for some time to allow threads to run.
+ * The longer we sleep, the more accurate the calculation will be,
+ * but it'll make user waits for longer for the test to finish.
+ */
+ pj_thread_sleep(5000);
+
+ /* Signal all threads to quit. */
+ quit_flag = 1;
+
+ /* Wait until all threads quit, then destroy. */
+ for (i=0; i<NUM_THREADS; ++i) {
+ rc = pj_thread_join(thread[i]);
+ if (rc != PJ_SUCCESS) {
+ app_perror("...ERROR in pj_thread_join()", rc);
+ return -50;
+ }
+ rc = pj_thread_destroy(thread[i]);
+ if (rc != PJ_SUCCESS) {
+ app_perror("...ERROR in pj_thread_destroy()", rc);
+ return -60;
+ }
+ }
+
+ /* Now examine the value of the counters.
+ * Check that all threads had equal proportion of processing.
+ */
+ lowest = 0xFFFFFFFF;
+ highest = 0;
+ for (i=0; i<NUM_THREADS; ++i) {
+ if (counter[i] < lowest)
+ lowest = counter[i];
+ if (counter[i] > highest)
+ highest = counter[i];
+ }
+
+ /* Check that all threads are running. */
+ if (lowest < 2) {
+ PJ_LOG(3,(THIS_FILE, "...ERROR: not all threads were running!"));
+ return -70;
+ }
+
+ /* The difference between lowest and higest should be lower than 50%.
+ */
+ diff = (highest-lowest)*100 / ((highest+lowest)/2);
+ if ( diff >= 50) {
+ PJ_LOG(3,(THIS_FILE, "...ERROR: thread didn't have equal timeslice!"));
+ PJ_LOG(3,(THIS_FILE, ".....lowest counter=%u, highest counter=%u, diff=%u%%",
+ lowest, highest, diff));
+ return -80;
+ } else {
+ PJ_LOG(3,(THIS_FILE,
+ "...info: timeslice diff between lowest & highest=%u%%",
+ diff));
+ }
+
+ return 0;
+}
+
+int thread_test(void)
+{
+ int rc;
+
+ rc = simple_thread("simple thread test", 0);
+ if (rc != PJ_SUCCESS)
+ return rc;
+
+ rc = simple_thread("suspended thread test", PJ_THREAD_SUSPENDED);
+ if (rc != PJ_SUCCESS)
+ return rc;
+
+ rc = timeslice_test();
+ if (rc != PJ_SUCCESS)
+ return rc;
+
+ return rc;
+}
+
+#else
+/* To prevent warning about "translation unit is empty"
+ * when this test is disabled.
+ */
+int dummy_thread_test;
+#endif /* INCLUDE_THREAD_TEST */
+
+
diff --git a/pjlib/src/pjlib-test/timer.c b/pjlib/src/pjlib-test/timer.c
index 2ae5520a..8b2ebe58 100644
--- a/pjlib/src/pjlib-test/timer.c
+++ b/pjlib/src/pjlib-test/timer.c
@@ -1,186 +1,186 @@
-/* $Id$ */
-/*
- * Copyright (C)2003-2006 Benny Prijono <benny@prijono.org>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
-#include "test.h"
-
-/**
- * \page page_pjlib_timer_test Test: Timer
- *
- * This file provides implementation of \b timer_test(). It tests the
- * functionality of the timer heap.
- *
- *
- * This file is <b>pjlib-test/timer.c</b>
- *
- * \include pjlib-test/timer.c
- */
-
-
-#if INCLUDE_TIMER_TEST
-
-#include <pjlib.h>
-
-#define LOOP 16
-#define MIN_COUNT 250
-#define MAX_COUNT (LOOP * MIN_COUNT)
-#define MIN_DELAY 2
-#define D (MAX_COUNT / 32000)
-#define DELAY (D < MIN_DELAY ? MIN_DELAY : D)
-#define THIS_FILE "timer_test"
-
-
-static void timer_callback(pj_timer_heap_t *ht, pj_timer_entry *e)
-{
- PJ_UNUSED_ARG(ht);
- PJ_UNUSED_ARG(e);
-}
-
-static int test_timer_heap(void)
-{
- int i, j;
- pj_timer_entry *entry;
- pj_pool_t *pool;
- pj_timer_heap_t *timer;
- pj_time_val delay;
- pj_status_t rc; int err=0;
- unsigned size, count;
-
- size = pj_timer_heap_mem_size(MAX_COUNT)+MAX_COUNT*sizeof(pj_timer_entry);
- pool = pj_pool_create( mem, NULL, size, 4000, NULL);
- if (!pool) {
- PJ_LOG(3,("test", "...error: unable to create pool of %u bytes",
- size));
- return -10;
- }
-
- entry = (pj_timer_entry*)pj_pool_calloc(pool, MAX_COUNT, sizeof(*entry));
- if (!entry)
- return -20;
-
- for (i=0; i<MAX_COUNT; ++i) {
- entry[i].cb = &timer_callback;
- }
- rc = pj_timer_heap_create(pool, MAX_COUNT, &timer);
- if (rc != PJ_SUCCESS) {
- app_perror("...error: unable to create timer heap", rc);
- return -30;
- }
-
- count = MIN_COUNT;
- for (i=0; i<LOOP; ++i) {
- int early = 0;
- int done=0;
- int cancelled=0;
- int rc;
- pj_timestamp t1, t2, t_sched, t_cancel, t_poll;
- pj_time_val now, expire;
-
- pj_gettimeofday(&now);
- pj_srand(now.sec);
- t_sched.u32.lo = t_cancel.u32.lo = t_poll.u32.lo = 0;
-
- // Register timers
- for (j=0; j<(int)count; ++j) {
- delay.sec = pj_rand() % DELAY;
- delay.msec = pj_rand() % 1000;
-
- // Schedule timer
- pj_get_timestamp(&t1);
- rc = pj_timer_heap_schedule(timer, &entry[j], &delay);
- if (rc != 0)
- return -40;
- pj_get_timestamp(&t2);
-
- t_sched.u32.lo += (t2.u32.lo - t1.u32.lo);
-
- // Poll timers.
- pj_get_timestamp(&t1);
- rc = pj_timer_heap_poll(timer, NULL);
- pj_get_timestamp(&t2);
- if (rc > 0) {
- t_poll.u32.lo += (t2.u32.lo - t1.u32.lo);
- early += rc;
- }
- }
-
- // Set the time where all timers should finish
- pj_gettimeofday(&expire);
- delay.sec = DELAY;
- delay.msec = 0;
- PJ_TIME_VAL_ADD(expire, delay);
-
- // Wait unfil all timers finish, cancel some of them.
- do {
- int index = pj_rand() % count;
- pj_get_timestamp(&t1);
- rc = pj_timer_heap_cancel(timer, &entry[index]);
- pj_get_timestamp(&t2);
- if (rc > 0) {
- cancelled += rc;
- t_cancel.u32.lo += (t2.u32.lo - t1.u32.lo);
- }
-
- pj_gettimeofday(&now);
-
- pj_get_timestamp(&t1);
- rc = pj_timer_heap_poll(timer, NULL);
- pj_get_timestamp(&t2);
- if (rc > 0) {
- done += rc;
- t_poll.u32.lo += (t2.u32.lo - t1.u32.lo);
- }
-
- } while (PJ_TIME_VAL_LTE(now, expire)&&pj_timer_heap_count(timer) > 0);
-
- if (pj_timer_heap_count(timer)) {
- PJ_LOG(3, (THIS_FILE, "ERROR: %d timers left",
- pj_timer_heap_count(timer)));
- ++err;
- }
- t_sched.u32.lo /= count;
- t_cancel.u32.lo /= count;
- t_poll.u32.lo /= count;
- PJ_LOG(4, (THIS_FILE,
- "...ok (count:%d, early:%d, cancelled:%d, "
- "sched:%d, cancel:%d poll:%d)",
- count, early, cancelled, t_sched.u32.lo, t_cancel.u32.lo,
- t_poll.u32.lo));
-
- count = count * 2;
- if (count > MAX_COUNT)
- break;
- }
-
- pj_pool_release(pool);
- return err;
-}
-
-
-int timer_test()
-{
- return test_timer_heap();
-}
-
-#else
-/* To prevent warning about "translation unit is empty"
- * when this test is disabled.
- */
-int dummy_timer_test;
-#endif /* INCLUDE_TIMER_TEST */
-
-
+/* $Id$ */
+/*
+ * Copyright (C)2003-2006 Benny Prijono <benny@prijono.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#include "test.h"
+
+/**
+ * \page page_pjlib_timer_test Test: Timer
+ *
+ * This file provides implementation of \b timer_test(). It tests the
+ * functionality of the timer heap.
+ *
+ *
+ * This file is <b>pjlib-test/timer.c</b>
+ *
+ * \include pjlib-test/timer.c
+ */
+
+
+#if INCLUDE_TIMER_TEST
+
+#include <pjlib.h>
+
+#define LOOP 16
+#define MIN_COUNT 250
+#define MAX_COUNT (LOOP * MIN_COUNT)
+#define MIN_DELAY 2
+#define D (MAX_COUNT / 32000)
+#define DELAY (D < MIN_DELAY ? MIN_DELAY : D)
+#define THIS_FILE "timer_test"
+
+
+static void timer_callback(pj_timer_heap_t *ht, pj_timer_entry *e)
+{
+ PJ_UNUSED_ARG(ht);
+ PJ_UNUSED_ARG(e);
+}
+
+static int test_timer_heap(void)
+{
+ int i, j;
+ pj_timer_entry *entry;
+ pj_pool_t *pool;
+ pj_timer_heap_t *timer;
+ pj_time_val delay;
+ pj_status_t rc; int err=0;
+ unsigned size, count;
+
+ size = pj_timer_heap_mem_size(MAX_COUNT)+MAX_COUNT*sizeof(pj_timer_entry);
+ pool = pj_pool_create( mem, NULL, size, 4000, NULL);
+ if (!pool) {
+ PJ_LOG(3,("test", "...error: unable to create pool of %u bytes",
+ size));
+ return -10;
+ }
+
+ entry = (pj_timer_entry*)pj_pool_calloc(pool, MAX_COUNT, sizeof(*entry));
+ if (!entry)
+ return -20;
+
+ for (i=0; i<MAX_COUNT; ++i) {
+ entry[i].cb = &timer_callback;
+ }
+ rc = pj_timer_heap_create(pool, MAX_COUNT, &timer);
+ if (rc != PJ_SUCCESS) {
+ app_perror("...error: unable to create timer heap", rc);
+ return -30;
+ }
+
+ count = MIN_COUNT;
+ for (i=0; i<LOOP; ++i) {
+ int early = 0;
+ int done=0;
+ int cancelled=0;
+ int rc;
+ pj_timestamp t1, t2, t_sched, t_cancel, t_poll;
+ pj_time_val now, expire;
+
+ pj_gettimeofday(&now);
+ pj_srand(now.sec);
+ t_sched.u32.lo = t_cancel.u32.lo = t_poll.u32.lo = 0;
+
+ // Register timers
+ for (j=0; j<(int)count; ++j) {
+ delay.sec = pj_rand() % DELAY;
+ delay.msec = pj_rand() % 1000;
+
+ // Schedule timer
+ pj_get_timestamp(&t1);
+ rc = pj_timer_heap_schedule(timer, &entry[j], &delay);
+ if (rc != 0)
+ return -40;
+ pj_get_timestamp(&t2);
+
+ t_sched.u32.lo += (t2.u32.lo - t1.u32.lo);
+
+ // Poll timers.
+ pj_get_timestamp(&t1);
+ rc = pj_timer_heap_poll(timer, NULL);
+ pj_get_timestamp(&t2);
+ if (rc > 0) {
+ t_poll.u32.lo += (t2.u32.lo - t1.u32.lo);
+ early += rc;
+ }
+ }
+
+ // Set the time where all timers should finish
+ pj_gettimeofday(&expire);
+ delay.sec = DELAY;
+ delay.msec = 0;
+ PJ_TIME_VAL_ADD(expire, delay);
+
+ // Wait unfil all timers finish, cancel some of them.
+ do {
+ int index = pj_rand() % count;
+ pj_get_timestamp(&t1);
+ rc = pj_timer_heap_cancel(timer, &entry[index]);
+ pj_get_timestamp(&t2);
+ if (rc > 0) {
+ cancelled += rc;
+ t_cancel.u32.lo += (t2.u32.lo - t1.u32.lo);
+ }
+
+ pj_gettimeofday(&now);
+
+ pj_get_timestamp(&t1);
+ rc = pj_timer_heap_poll(timer, NULL);
+ pj_get_timestamp(&t2);
+ if (rc > 0) {
+ done += rc;
+ t_poll.u32.lo += (t2.u32.lo - t1.u32.lo);
+ }
+
+ } while (PJ_TIME_VAL_LTE(now, expire)&&pj_timer_heap_count(timer) > 0);
+
+ if (pj_timer_heap_count(timer)) {
+ PJ_LOG(3, (THIS_FILE, "ERROR: %d timers left",
+ pj_timer_heap_count(timer)));
+ ++err;
+ }
+ t_sched.u32.lo /= count;
+ t_cancel.u32.lo /= count;
+ t_poll.u32.lo /= count;
+ PJ_LOG(4, (THIS_FILE,
+ "...ok (count:%d, early:%d, cancelled:%d, "
+ "sched:%d, cancel:%d poll:%d)",
+ count, early, cancelled, t_sched.u32.lo, t_cancel.u32.lo,
+ t_poll.u32.lo));
+
+ count = count * 2;
+ if (count > MAX_COUNT)
+ break;
+ }
+
+ pj_pool_release(pool);
+ return err;
+}
+
+
+int timer_test()
+{
+ return test_timer_heap();
+}
+
+#else
+/* To prevent warning about "translation unit is empty"
+ * when this test is disabled.
+ */
+int dummy_timer_test;
+#endif /* INCLUDE_TIMER_TEST */
+
+
diff --git a/pjlib/src/pjlib-test/timestamp.c b/pjlib/src/pjlib-test/timestamp.c
index ec103514..67e47c11 100644
--- a/pjlib/src/pjlib-test/timestamp.c
+++ b/pjlib/src/pjlib-test/timestamp.c
@@ -1,143 +1,143 @@
-/* $Id$ */
-/*
- * Copyright (C)2003-2006 Benny Prijono <benny@prijono.org>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
-#include "test.h"
-#include <pj/os.h>
-#include <pj/log.h>
-
-
-/**
- * \page page_pjlib_timestamp_test Test: Timestamp
- *
- * This file provides implementation of timestamp_test()
- *
- * \section timestamp_test_sec Scope of the Test
- *
- * This tests whether timestamp API works.
- *
- * API tested:
- * - pj_get_timestamp_freq()
- * - pj_get_timestamp()
- * - pj_elapsed_usec()
- * - PJ_LOG()
- *
- *
- * This file is <b>pjlib-test/timestamp.c</b>
- *
- * \include pjlib-test/timestamp.c
- */
-
-#if INCLUDE_TIMESTAMP_TEST
-
-#define THIS_FILE "timestamp"
-
-int timestamp_test(void)
-{
- enum { CONSECUTIVE_LOOP = 1000 };
- volatile unsigned i;
- pj_timestamp freq, t1, t2;
- unsigned elapsed;
- pj_status_t rc;
-
- PJ_LOG(3,(THIS_FILE, "...Testing timestamp (high res time)"));
-
- /* Get and display timestamp frequency. */
- if ((rc=pj_get_timestamp_freq(&freq)) != PJ_SUCCESS) {
- app_perror("...ERROR: get timestamp freq", rc);
- return -1000;
- }
-
- PJ_LOG(3,(THIS_FILE, "....frequency: hiword=%lu loword=%lu",
- freq.u32.hi, freq.u32.lo));
-
- PJ_LOG(3,(THIS_FILE, "...checking if time can run backwards (pls wait).."));
-
- /*
- * Check if consecutive readings should yield timestamp value
- * that is bigger than previous value.
- * First we get the first timestamp.
- */
- rc = pj_get_timestamp(&t1);
- if (rc != PJ_SUCCESS) {
- app_perror("...ERROR: get timestamp", rc);
- return -1001;
- }
- for (i=0; i<CONSECUTIVE_LOOP; ++i) {
- /*
- volatile unsigned j;
- for (j=0; j<1000; ++j)
- ;
- */
- pj_thread_sleep(1);
- rc = pj_get_timestamp(&t2);
- if (rc != PJ_SUCCESS) {
- app_perror("...ERROR: get timestamp", rc);
- return -1002;
- }
- /* compare t2 with t1, expecting t2 >= t1. */
- if (t2.u32.hi < t1.u32.hi ||
- (t2.u32.hi == t1.u32.hi && t2.u32.lo < t1.u32.lo))
- {
- PJ_LOG(3,(THIS_FILE, "...ERROR: timestamp runs backwards!"));
- return -1003;
- }
- }
-
- /*
- * Simple test to time some loop.
- */
- PJ_LOG(3,(THIS_FILE, "....testing simple 1000000 loop"));
-
-
- /* Mark start time. */
- if ((rc=pj_get_timestamp(&t1)) != PJ_SUCCESS) {
- app_perror("....error: cat't get timestamp", rc);
- return -1010;
- }
-
- /* Loop.. */
- for (i=0; i<1000000; ++i)
- ;
-
- /* Mark end time. */
- pj_get_timestamp(&t2);
-
- /* Get elapsed time in usec. */
- elapsed = pj_elapsed_usec(&t1, &t2);
- PJ_LOG(3,(THIS_FILE, "....elapsed: %u usec", (unsigned)elapsed));
-
- /* See if elapsed time is reasonable. */
- if (elapsed < 1 || elapsed > 100000) {
- PJ_LOG(3,(THIS_FILE, "....error: elapsed time outside window (%u, "
- "t1.u32.hi=%u, t1.u32.lo=%u, "
- "t2.u32.hi=%u, t2.u32.lo=%u)",
- elapsed,
- t1.u32.hi, t1.u32.lo, t2.u32.hi, t2.u32.lo));
- return -1030;
- }
- return 0;
-}
-
-
-#else
-/* To prevent warning about "translation unit is empty"
- * when this test is disabled.
- */
-int dummy_timestamp_test;
-#endif /* INCLUDE_TIMESTAMP_TEST */
-
+/* $Id$ */
+/*
+ * Copyright (C)2003-2006 Benny Prijono <benny@prijono.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#include "test.h"
+#include <pj/os.h>
+#include <pj/log.h>
+
+
+/**
+ * \page page_pjlib_timestamp_test Test: Timestamp
+ *
+ * This file provides implementation of timestamp_test()
+ *
+ * \section timestamp_test_sec Scope of the Test
+ *
+ * This tests whether timestamp API works.
+ *
+ * API tested:
+ * - pj_get_timestamp_freq()
+ * - pj_get_timestamp()
+ * - pj_elapsed_usec()
+ * - PJ_LOG()
+ *
+ *
+ * This file is <b>pjlib-test/timestamp.c</b>
+ *
+ * \include pjlib-test/timestamp.c
+ */
+
+#if INCLUDE_TIMESTAMP_TEST
+
+#define THIS_FILE "timestamp"
+
+int timestamp_test(void)
+{
+ enum { CONSECUTIVE_LOOP = 1000 };
+ volatile unsigned i;
+ pj_timestamp freq, t1, t2;
+ unsigned elapsed;
+ pj_status_t rc;
+
+ PJ_LOG(3,(THIS_FILE, "...Testing timestamp (high res time)"));
+
+ /* Get and display timestamp frequency. */
+ if ((rc=pj_get_timestamp_freq(&freq)) != PJ_SUCCESS) {
+ app_perror("...ERROR: get timestamp freq", rc);
+ return -1000;
+ }
+
+ PJ_LOG(3,(THIS_FILE, "....frequency: hiword=%lu loword=%lu",
+ freq.u32.hi, freq.u32.lo));
+
+ PJ_LOG(3,(THIS_FILE, "...checking if time can run backwards (pls wait).."));
+
+ /*
+ * Check if consecutive readings should yield timestamp value
+ * that is bigger than previous value.
+ * First we get the first timestamp.
+ */
+ rc = pj_get_timestamp(&t1);
+ if (rc != PJ_SUCCESS) {
+ app_perror("...ERROR: get timestamp", rc);
+ return -1001;
+ }
+ for (i=0; i<CONSECUTIVE_LOOP; ++i) {
+ /*
+ volatile unsigned j;
+ for (j=0; j<1000; ++j)
+ ;
+ */
+ pj_thread_sleep(1);
+ rc = pj_get_timestamp(&t2);
+ if (rc != PJ_SUCCESS) {
+ app_perror("...ERROR: get timestamp", rc);
+ return -1002;
+ }
+ /* compare t2 with t1, expecting t2 >= t1. */
+ if (t2.u32.hi < t1.u32.hi ||
+ (t2.u32.hi == t1.u32.hi && t2.u32.lo < t1.u32.lo))
+ {
+ PJ_LOG(3,(THIS_FILE, "...ERROR: timestamp runs backwards!"));
+ return -1003;
+ }
+ }
+
+ /*
+ * Simple test to time some loop.
+ */
+ PJ_LOG(3,(THIS_FILE, "....testing simple 1000000 loop"));
+
+
+ /* Mark start time. */
+ if ((rc=pj_get_timestamp(&t1)) != PJ_SUCCESS) {
+ app_perror("....error: cat't get timestamp", rc);
+ return -1010;
+ }
+
+ /* Loop.. */
+ for (i=0; i<1000000; ++i)
+ ;
+
+ /* Mark end time. */
+ pj_get_timestamp(&t2);
+
+ /* Get elapsed time in usec. */
+ elapsed = pj_elapsed_usec(&t1, &t2);
+ PJ_LOG(3,(THIS_FILE, "....elapsed: %u usec", (unsigned)elapsed));
+
+ /* See if elapsed time is reasonable. */
+ if (elapsed < 1 || elapsed > 100000) {
+ PJ_LOG(3,(THIS_FILE, "....error: elapsed time outside window (%u, "
+ "t1.u32.hi=%u, t1.u32.lo=%u, "
+ "t2.u32.hi=%u, t2.u32.lo=%u)",
+ elapsed,
+ t1.u32.hi, t1.u32.lo, t2.u32.hi, t2.u32.lo));
+ return -1030;
+ }
+ return 0;
+}
+
+
+#else
+/* To prevent warning about "translation unit is empty"
+ * when this test is disabled.
+ */
+int dummy_timestamp_test;
+#endif /* INCLUDE_TIMESTAMP_TEST */
+
diff --git a/pjlib/src/pjlib-test/udp_echo_srv_ioqueue.c b/pjlib/src/pjlib-test/udp_echo_srv_ioqueue.c
index 2dec91c4..8ede493b 100644
--- a/pjlib/src/pjlib-test/udp_echo_srv_ioqueue.c
+++ b/pjlib/src/pjlib-test/udp_echo_srv_ioqueue.c
@@ -1,211 +1,211 @@
-/* $Id$ */
-/*
- * Copyright (C)2003-2006 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 <pjlib.h>
-#include "test.h"
-
-static pj_ioqueue_key_t *key;
-static pj_atomic_t *total_bytes;
-
-struct op_key
-{
- pj_ioqueue_op_key_t op_key_;
- struct op_key *peer;
- char *buffer;
- pj_size_t size;
- int is_pending;
- pj_status_t last_err;
- pj_sockaddr_in addr;
- int addrlen;
-};
-
-static void on_read_complete(pj_ioqueue_key_t *key,
- pj_ioqueue_op_key_t *op_key,
- pj_ssize_t bytes_received)
-{
- pj_status_t rc;
- struct op_key *recv_rec = (struct op_key *)op_key;
-
- for (;;) {
- struct op_key *send_rec = recv_rec->peer;
- recv_rec->is_pending = 0;
-
- if (bytes_received < 0) {
- if (-bytes_received != recv_rec->last_err) {
- recv_rec->last_err = -bytes_received;
- app_perror("...error receiving data", -bytes_received);
- }
- } else if (bytes_received == 0) {
- /* note: previous error, or write callback */
- } else {
- pj_atomic_add(total_bytes, bytes_received);
-
- if (!send_rec->is_pending) {
- pj_ssize_t sent = bytes_received;
- pj_memcpy(send_rec->buffer, recv_rec->buffer, bytes_received);
- pj_memcpy(&send_rec->addr, &recv_rec->addr, recv_rec->addrlen);
- send_rec->addrlen = recv_rec->addrlen;
- rc = pj_ioqueue_sendto(key, &send_rec->op_key_,
- send_rec->buffer, &sent, 0,
- &send_rec->addr, send_rec->addrlen);
- send_rec->is_pending = (rc==PJ_EPENDING);
-
- if (rc!=PJ_SUCCESS && rc!=PJ_EPENDING) {
- app_perror("...send error(1)", rc);
- }
- }
- }
-
- if (!send_rec->is_pending) {
- bytes_received = recv_rec->size;
- rc = pj_ioqueue_recvfrom(key, &recv_rec->op_key_,
- recv_rec->buffer, &bytes_received, 0,
- &recv_rec->addr, &recv_rec->addrlen);
- recv_rec->is_pending = (rc==PJ_EPENDING);
- if (rc == PJ_SUCCESS) {
- /* fall through next loop. */
- } else if (rc == PJ_EPENDING) {
- /* quit callback. */
- break;
- } else {
- /* error */
- app_perror("...recv error", rc);
- recv_rec->last_err = rc;
-
- bytes_received = 0;
- /* fall through next loop. */
- }
- } else {
- /* recv will be done when write completion callback is called. */
- break;
- }
- }
-}
-
-static void on_write_complete(pj_ioqueue_key_t *key,
- pj_ioqueue_op_key_t *op_key,
- pj_ssize_t bytes_sent)
-{
- struct op_key *send_rec = (struct op_key*)op_key;
-
- if (bytes_sent <= 0) {
- pj_status_t rc = -bytes_sent;
- if (rc != send_rec->last_err) {
- send_rec->last_err = rc;
- app_perror("...send error(2)", rc);
- }
- }
-
- send_rec->is_pending = 0;
- on_read_complete(key, &send_rec->peer->op_key_, 0);
-}
-
-static int worker_thread(void *arg)
-{
- pj_ioqueue_t *ioqueue = arg;
- struct op_key read_op, write_op;
- char recv_buf[512], send_buf[512];
- pj_ssize_t length;
- pj_status_t rc;
-
- read_op.peer = &write_op;
- read_op.is_pending = 0;
- read_op.last_err = 0;
- read_op.buffer = recv_buf;
- read_op.size = sizeof(recv_buf);
- read_op.addrlen = sizeof(read_op.addr);
-
- write_op.peer = &read_op;
- write_op.is_pending = 0;
- write_op.last_err = 0;
- write_op.buffer = send_buf;
- write_op.size = sizeof(send_buf);
-
- length = sizeof(recv_buf);
- rc = pj_ioqueue_recvfrom(key, &read_op.op_key_, recv_buf, &length, 0,
- &read_op.addr, &read_op.addrlen);
- if (rc == PJ_SUCCESS) {
- read_op.is_pending = 1;
- on_read_complete(key, &read_op.op_key_, length);
- }
-
- for (;;) {
- pj_time_val timeout;
- timeout.sec = 0; timeout.msec = 10;
- rc = pj_ioqueue_poll(ioqueue, &timeout);
- }
-}
-
-int udp_echo_srv_ioqueue(void)
-{
- pj_pool_t *pool;
- pj_sock_t sock;
- pj_ioqueue_t *ioqueue;
- pj_ioqueue_callback callback;
- int i;
- pj_thread_t *thread[ECHO_SERVER_MAX_THREADS];
- pj_status_t rc;
-
- pj_memset(&callback, 0, sizeof(callback));
- callback.on_read_complete = &on_read_complete;
- callback.on_write_complete = &on_write_complete;
-
- pool = pj_pool_create(mem, NULL, 4000, 4000, NULL);
- if (!pool)
- return -10;
-
- rc = pj_ioqueue_create(pool, 2, &ioqueue);
- if (rc != PJ_SUCCESS) {
- app_perror("...pj_ioqueue_create error", rc);
- return -20;
- }
-
- rc = app_socket(PJ_AF_INET, PJ_SOCK_DGRAM, 0,
- ECHO_SERVER_START_PORT, &sock);
- if (rc != PJ_SUCCESS) {
- app_perror("...app_socket error", rc);
- return -30;
- }
-
- rc = pj_ioqueue_register_sock(pool, ioqueue, sock, NULL,
- &callback, &key);
- if (rc != PJ_SUCCESS) {
- app_perror("...error registering socket", rc);
- return -40;
- }
-
- rc = pj_atomic_create(pool, 0, &total_bytes);
- if (rc != PJ_SUCCESS) {
- app_perror("...error creating atomic variable", rc);
- return -45;
- }
-
- for (i=0; i<ECHO_SERVER_MAX_THREADS; ++i) {
- rc = pj_thread_create(pool, NULL, &worker_thread, ioqueue,
- PJ_THREAD_DEFAULT_STACK_SIZE, 0,
- &thread[i]);
- if (rc != PJ_SUCCESS) {
- app_perror("...create thread error", rc);
- return -50;
- }
- }
-
- echo_srv_common_loop(total_bytes);
-
- return 0;
-}
+/* $Id$ */
+/*
+ * Copyright (C)2003-2006 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 <pjlib.h>
+#include "test.h"
+
+static pj_ioqueue_key_t *key;
+static pj_atomic_t *total_bytes;
+
+struct op_key
+{
+ pj_ioqueue_op_key_t op_key_;
+ struct op_key *peer;
+ char *buffer;
+ pj_size_t size;
+ int is_pending;
+ pj_status_t last_err;
+ pj_sockaddr_in addr;
+ int addrlen;
+};
+
+static void on_read_complete(pj_ioqueue_key_t *key,
+ pj_ioqueue_op_key_t *op_key,
+ pj_ssize_t bytes_received)
+{
+ pj_status_t rc;
+ struct op_key *recv_rec = (struct op_key *)op_key;
+
+ for (;;) {
+ struct op_key *send_rec = recv_rec->peer;
+ recv_rec->is_pending = 0;
+
+ if (bytes_received < 0) {
+ if (-bytes_received != recv_rec->last_err) {
+ recv_rec->last_err = -bytes_received;
+ app_perror("...error receiving data", -bytes_received);
+ }
+ } else if (bytes_received == 0) {
+ /* note: previous error, or write callback */
+ } else {
+ pj_atomic_add(total_bytes, bytes_received);
+
+ if (!send_rec->is_pending) {
+ pj_ssize_t sent = bytes_received;
+ pj_memcpy(send_rec->buffer, recv_rec->buffer, bytes_received);
+ pj_memcpy(&send_rec->addr, &recv_rec->addr, recv_rec->addrlen);
+ send_rec->addrlen = recv_rec->addrlen;
+ rc = pj_ioqueue_sendto(key, &send_rec->op_key_,
+ send_rec->buffer, &sent, 0,
+ &send_rec->addr, send_rec->addrlen);
+ send_rec->is_pending = (rc==PJ_EPENDING);
+
+ if (rc!=PJ_SUCCESS && rc!=PJ_EPENDING) {
+ app_perror("...send error(1)", rc);
+ }
+ }
+ }
+
+ if (!send_rec->is_pending) {
+ bytes_received = recv_rec->size;
+ rc = pj_ioqueue_recvfrom(key, &recv_rec->op_key_,
+ recv_rec->buffer, &bytes_received, 0,
+ &recv_rec->addr, &recv_rec->addrlen);
+ recv_rec->is_pending = (rc==PJ_EPENDING);
+ if (rc == PJ_SUCCESS) {
+ /* fall through next loop. */
+ } else if (rc == PJ_EPENDING) {
+ /* quit callback. */
+ break;
+ } else {
+ /* error */
+ app_perror("...recv error", rc);
+ recv_rec->last_err = rc;
+
+ bytes_received = 0;
+ /* fall through next loop. */
+ }
+ } else {
+ /* recv will be done when write completion callback is called. */
+ break;
+ }
+ }
+}
+
+static void on_write_complete(pj_ioqueue_key_t *key,
+ pj_ioqueue_op_key_t *op_key,
+ pj_ssize_t bytes_sent)
+{
+ struct op_key *send_rec = (struct op_key*)op_key;
+
+ if (bytes_sent <= 0) {
+ pj_status_t rc = -bytes_sent;
+ if (rc != send_rec->last_err) {
+ send_rec->last_err = rc;
+ app_perror("...send error(2)", rc);
+ }
+ }
+
+ send_rec->is_pending = 0;
+ on_read_complete(key, &send_rec->peer->op_key_, 0);
+}
+
+static int worker_thread(void *arg)
+{
+ pj_ioqueue_t *ioqueue = arg;
+ struct op_key read_op, write_op;
+ char recv_buf[512], send_buf[512];
+ pj_ssize_t length;
+ pj_status_t rc;
+
+ read_op.peer = &write_op;
+ read_op.is_pending = 0;
+ read_op.last_err = 0;
+ read_op.buffer = recv_buf;
+ read_op.size = sizeof(recv_buf);
+ read_op.addrlen = sizeof(read_op.addr);
+
+ write_op.peer = &read_op;
+ write_op.is_pending = 0;
+ write_op.last_err = 0;
+ write_op.buffer = send_buf;
+ write_op.size = sizeof(send_buf);
+
+ length = sizeof(recv_buf);
+ rc = pj_ioqueue_recvfrom(key, &read_op.op_key_, recv_buf, &length, 0,
+ &read_op.addr, &read_op.addrlen);
+ if (rc == PJ_SUCCESS) {
+ read_op.is_pending = 1;
+ on_read_complete(key, &read_op.op_key_, length);
+ }
+
+ for (;;) {
+ pj_time_val timeout;
+ timeout.sec = 0; timeout.msec = 10;
+ rc = pj_ioqueue_poll(ioqueue, &timeout);
+ }
+}
+
+int udp_echo_srv_ioqueue(void)
+{
+ pj_pool_t *pool;
+ pj_sock_t sock;
+ pj_ioqueue_t *ioqueue;
+ pj_ioqueue_callback callback;
+ int i;
+ pj_thread_t *thread[ECHO_SERVER_MAX_THREADS];
+ pj_status_t rc;
+
+ pj_memset(&callback, 0, sizeof(callback));
+ callback.on_read_complete = &on_read_complete;
+ callback.on_write_complete = &on_write_complete;
+
+ pool = pj_pool_create(mem, NULL, 4000, 4000, NULL);
+ if (!pool)
+ return -10;
+
+ rc = pj_ioqueue_create(pool, 2, &ioqueue);
+ if (rc != PJ_SUCCESS) {
+ app_perror("...pj_ioqueue_create error", rc);
+ return -20;
+ }
+
+ rc = app_socket(PJ_AF_INET, PJ_SOCK_DGRAM, 0,
+ ECHO_SERVER_START_PORT, &sock);
+ if (rc != PJ_SUCCESS) {
+ app_perror("...app_socket error", rc);
+ return -30;
+ }
+
+ rc = pj_ioqueue_register_sock(pool, ioqueue, sock, NULL,
+ &callback, &key);
+ if (rc != PJ_SUCCESS) {
+ app_perror("...error registering socket", rc);
+ return -40;
+ }
+
+ rc = pj_atomic_create(pool, 0, &total_bytes);
+ if (rc != PJ_SUCCESS) {
+ app_perror("...error creating atomic variable", rc);
+ return -45;
+ }
+
+ for (i=0; i<ECHO_SERVER_MAX_THREADS; ++i) {
+ rc = pj_thread_create(pool, NULL, &worker_thread, ioqueue,
+ PJ_THREAD_DEFAULT_STACK_SIZE, 0,
+ &thread[i]);
+ if (rc != PJ_SUCCESS) {
+ app_perror("...create thread error", rc);
+ return -50;
+ }
+ }
+
+ echo_srv_common_loop(total_bytes);
+
+ return 0;
+}
diff --git a/pjlib/src/pjlib-test/udp_echo_srv_sync.c b/pjlib/src/pjlib-test/udp_echo_srv_sync.c
index 312fa8c6..34633de2 100644
--- a/pjlib/src/pjlib-test/udp_echo_srv_sync.c
+++ b/pjlib/src/pjlib-test/udp_echo_srv_sync.c
@@ -1,163 +1,163 @@
-/* $Id$ */
-/*
- * Copyright (C)2003-2006 Benny Prijono <benny@prijono.org>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
-#include "test.h"
-#include <pjlib.h>
-
-static pj_atomic_t *total_bytes;
-
-static int worker_thread(void *arg)
-{
- pj_sock_t sock = (pj_sock_t)arg;
- char buf[512];
- pj_status_t last_recv_err = PJ_SUCCESS, last_write_err = PJ_SUCCESS;
-
- for (;;) {
- pj_ssize_t len;
- pj_status_t rc;
- pj_sockaddr_in addr;
- int addrlen;
-
- len = sizeof(buf);
- addrlen = sizeof(addr);
- rc = pj_sock_recvfrom(sock, buf, &len, 0, &addr, &addrlen);
- if (rc != 0) {
- if (rc != last_recv_err) {
- app_perror("...recv error", rc);
- last_recv_err = rc;
- }
- continue;
- }
-
- pj_atomic_add(total_bytes, len);
-
- rc = pj_sock_sendto(sock, buf, &len, 0, &addr, addrlen);
- if (rc != PJ_SUCCESS) {
- if (rc != last_write_err) {
- app_perror("...send error", rc);
- last_write_err = rc;
- }
- continue;
- }
- }
-}
-
-
-int echo_srv_sync(void)
-{
- pj_pool_t *pool;
- pj_sock_t sock;
- pj_thread_t *thread[ECHO_SERVER_MAX_THREADS];
- pj_status_t rc;
- int i;
-
- pool = pj_pool_create(mem, NULL, 4000, 4000, NULL);
- if (!pool)
- return -5;
-
- rc = pj_atomic_create(pool, 0, &total_bytes);
- if (rc != PJ_SUCCESS) {
- app_perror("...unable to create atomic_var", rc);
- return -6;
- }
-
- rc = app_socket(PJ_AF_INET, PJ_SOCK_DGRAM,0, ECHO_SERVER_START_PORT, &sock);
- if (rc != PJ_SUCCESS) {
- app_perror("...socket error", rc);
- return -10;
- }
-
- for (i=0; i<ECHO_SERVER_MAX_THREADS; ++i) {
- rc = pj_thread_create(pool, NULL, &worker_thread, (void*)sock,
- PJ_THREAD_DEFAULT_STACK_SIZE, 0,
- &thread[i]);
- if (rc != PJ_SUCCESS) {
- app_perror("...unable to create thread", rc);
- return -20;
- }
- }
-
- PJ_LOG(3,("", "...UDP echo server running with %d threads at port %d",
- ECHO_SERVER_MAX_THREADS, ECHO_SERVER_START_PORT));
- PJ_LOG(3,("", "...Press Ctrl-C to abort"));
-
- echo_srv_common_loop(total_bytes);
- return 0;
-}
-
-
-int echo_srv_common_loop(pj_atomic_t *bytes_counter)
-{
- pj_highprec_t last_received, avg_bw, highest_bw;
- pj_time_val last_print;
- unsigned count;
- const char *ioqueue_name;
-
- last_received = 0;
- pj_gettimeofday(&last_print);
- avg_bw = highest_bw = 0;
- count = 0;
-
- ioqueue_name = pj_ioqueue_name();
-
- for (;;) {
- pj_highprec_t received, cur_received, bw;
- unsigned msec;
- pj_time_val now, duration;
-
- pj_thread_sleep(1000);
-
- received = cur_received = pj_atomic_get(bytes_counter);
- cur_received = cur_received - last_received;
-
- pj_gettimeofday(&now);
- duration = now;
- PJ_TIME_VAL_SUB(duration, last_print);
- msec = PJ_TIME_VAL_MSEC(duration);
-
- bw = cur_received;
- pj_highprec_mul(bw, 1000);
- pj_highprec_div(bw, msec);
-
- last_print = now;
- last_received = received;
-
- avg_bw = avg_bw + bw;
- count++;
-
- PJ_LOG(3,("", "%s UDP (%d threads): %u KB/s (avg=%u KB/s) %s",
- ioqueue_name,
- ECHO_SERVER_MAX_THREADS,
- (unsigned)(bw / 1000),
- (unsigned)(avg_bw / count / 1000),
- (count==20 ? "<ses avg>" : "")));
-
- if (count==20) {
- if (avg_bw/count > highest_bw)
- highest_bw = avg_bw/count;
-
- count = 0;
- avg_bw = 0;
-
- PJ_LOG(3,("", "Highest average bandwidth=%u KB/s",
- (unsigned)(highest_bw/1000)));
- }
- }
-}
-
-
+/* $Id$ */
+/*
+ * Copyright (C)2003-2006 Benny Prijono <benny@prijono.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#include "test.h"
+#include <pjlib.h>
+
+static pj_atomic_t *total_bytes;
+
+static int worker_thread(void *arg)
+{
+ pj_sock_t sock = (pj_sock_t)arg;
+ char buf[512];
+ pj_status_t last_recv_err = PJ_SUCCESS, last_write_err = PJ_SUCCESS;
+
+ for (;;) {
+ pj_ssize_t len;
+ pj_status_t rc;
+ pj_sockaddr_in addr;
+ int addrlen;
+
+ len = sizeof(buf);
+ addrlen = sizeof(addr);
+ rc = pj_sock_recvfrom(sock, buf, &len, 0, &addr, &addrlen);
+ if (rc != 0) {
+ if (rc != last_recv_err) {
+ app_perror("...recv error", rc);
+ last_recv_err = rc;
+ }
+ continue;
+ }
+
+ pj_atomic_add(total_bytes, len);
+
+ rc = pj_sock_sendto(sock, buf, &len, 0, &addr, addrlen);
+ if (rc != PJ_SUCCESS) {
+ if (rc != last_write_err) {
+ app_perror("...send error", rc);
+ last_write_err = rc;
+ }
+ continue;
+ }
+ }
+}
+
+
+int echo_srv_sync(void)
+{
+ pj_pool_t *pool;
+ pj_sock_t sock;
+ pj_thread_t *thread[ECHO_SERVER_MAX_THREADS];
+ pj_status_t rc;
+ int i;
+
+ pool = pj_pool_create(mem, NULL, 4000, 4000, NULL);
+ if (!pool)
+ return -5;
+
+ rc = pj_atomic_create(pool, 0, &total_bytes);
+ if (rc != PJ_SUCCESS) {
+ app_perror("...unable to create atomic_var", rc);
+ return -6;
+ }
+
+ rc = app_socket(PJ_AF_INET, PJ_SOCK_DGRAM,0, ECHO_SERVER_START_PORT, &sock);
+ if (rc != PJ_SUCCESS) {
+ app_perror("...socket error", rc);
+ return -10;
+ }
+
+ for (i=0; i<ECHO_SERVER_MAX_THREADS; ++i) {
+ rc = pj_thread_create(pool, NULL, &worker_thread, (void*)sock,
+ PJ_THREAD_DEFAULT_STACK_SIZE, 0,
+ &thread[i]);
+ if (rc != PJ_SUCCESS) {
+ app_perror("...unable to create thread", rc);
+ return -20;
+ }
+ }
+
+ PJ_LOG(3,("", "...UDP echo server running with %d threads at port %d",
+ ECHO_SERVER_MAX_THREADS, ECHO_SERVER_START_PORT));
+ PJ_LOG(3,("", "...Press Ctrl-C to abort"));
+
+ echo_srv_common_loop(total_bytes);
+ return 0;
+}
+
+
+int echo_srv_common_loop(pj_atomic_t *bytes_counter)
+{
+ pj_highprec_t last_received, avg_bw, highest_bw;
+ pj_time_val last_print;
+ unsigned count;
+ const char *ioqueue_name;
+
+ last_received = 0;
+ pj_gettimeofday(&last_print);
+ avg_bw = highest_bw = 0;
+ count = 0;
+
+ ioqueue_name = pj_ioqueue_name();
+
+ for (;;) {
+ pj_highprec_t received, cur_received, bw;
+ unsigned msec;
+ pj_time_val now, duration;
+
+ pj_thread_sleep(1000);
+
+ received = cur_received = pj_atomic_get(bytes_counter);
+ cur_received = cur_received - last_received;
+
+ pj_gettimeofday(&now);
+ duration = now;
+ PJ_TIME_VAL_SUB(duration, last_print);
+ msec = PJ_TIME_VAL_MSEC(duration);
+
+ bw = cur_received;
+ pj_highprec_mul(bw, 1000);
+ pj_highprec_div(bw, msec);
+
+ last_print = now;
+ last_received = received;
+
+ avg_bw = avg_bw + bw;
+ count++;
+
+ PJ_LOG(3,("", "%s UDP (%d threads): %u KB/s (avg=%u KB/s) %s",
+ ioqueue_name,
+ ECHO_SERVER_MAX_THREADS,
+ (unsigned)(bw / 1000),
+ (unsigned)(avg_bw / count / 1000),
+ (count==20 ? "<ses avg>" : "")));
+
+ if (count==20) {
+ if (avg_bw/count > highest_bw)
+ highest_bw = avg_bw/count;
+
+ count = 0;
+ avg_bw = 0;
+
+ PJ_LOG(3,("", "Highest average bandwidth=%u KB/s",
+ (unsigned)(highest_bw/1000)));
+ }
+ }
+}
+
+
diff --git a/pjlib/src/pjlib-test/util.c b/pjlib/src/pjlib-test/util.c
index b4d3a3f6..d5cded20 100644
--- a/pjlib/src/pjlib-test/util.c
+++ b/pjlib/src/pjlib-test/util.c
@@ -1,131 +1,131 @@
-/* $Id$ */
-/*
- * Copyright (C)2003-2006 Benny Prijono <benny@prijono.org>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
-#include "test.h"
-#include <pjlib.h>
-
-void app_perror(const char *msg, pj_status_t rc)
-{
- char errbuf[256];
-
- PJ_CHECK_STACK();
-
- pj_strerror(rc, errbuf, sizeof(errbuf));
- PJ_LOG(1,("test", "%s: [pj_status_t=%d] %s", msg, rc, errbuf));
-}
-
-#define SERVER 0
-#define CLIENT 1
-
-pj_status_t app_socket(int family, int type, int proto, int port,
- pj_sock_t *ptr_sock)
-{
- pj_sockaddr_in addr;
- pj_sock_t sock;
- pj_status_t rc;
-
- rc = pj_sock_socket(family, type, proto, &sock);
- if (rc != PJ_SUCCESS)
- return rc;
-
- pj_memset(&addr, 0, sizeof(addr));
- addr.sin_family = (pj_uint16_t)family;
- addr.sin_port = (short)(port!=-1 ? pj_htons((pj_uint16_t)port) : 0);
- rc = pj_sock_bind(sock, &addr, sizeof(addr));
- if (rc != PJ_SUCCESS)
- return rc;
-
- if (type == PJ_SOCK_STREAM) {
- rc = pj_sock_listen(sock, 5);
- if (rc != PJ_SUCCESS)
- return rc;
- }
-
- *ptr_sock = sock;
- return PJ_SUCCESS;
-}
-
-pj_status_t app_socketpair(int family, int type, int protocol,
- pj_sock_t *serverfd, pj_sock_t *clientfd)
-{
- int i;
- static unsigned short port = 11000;
- pj_sockaddr_in addr;
- pj_str_t s;
- pj_status_t rc = 0;
- pj_sock_t sock[2];
-
- /* Create both sockets. */
- for (i=0; i<2; ++i) {
- rc = pj_sock_socket(family, type, protocol, &sock[i]);
- if (rc != PJ_SUCCESS) {
- if (i==1)
- pj_sock_close(sock[0]);
- return rc;
- }
- }
-
- /* Retry bind */
- pj_memset(&addr, 0, sizeof(addr));
- addr.sin_family = PJ_AF_INET;
- for (i=0; i<5; ++i) {
- addr.sin_port = pj_htons(port++);
- rc = pj_sock_bind(sock[SERVER], &addr, sizeof(addr));
- if (rc == PJ_SUCCESS)
- break;
- }
-
- if (rc != PJ_SUCCESS)
- goto on_error;
-
- /* For TCP, listen the socket. */
- if (type == PJ_SOCK_STREAM) {
- rc = pj_sock_listen(sock[SERVER], PJ_SOMAXCONN);
- if (rc != PJ_SUCCESS)
- goto on_error;
- }
-
- /* Connect client socket. */
- addr.sin_addr = pj_inet_addr(pj_cstr(&s, "127.0.0.1"));
- rc = pj_sock_connect(sock[CLIENT], &addr, sizeof(addr));
- if (rc != PJ_SUCCESS)
- goto on_error;
-
- /* For TCP, must accept(), and get the new socket. */
- if (type == PJ_SOCK_STREAM) {
- pj_sock_t newserver;
-
- rc = pj_sock_accept(sock[SERVER], &newserver, NULL, NULL);
- if (rc != PJ_SUCCESS)
- goto on_error;
-
- /* Replace server socket with new socket. */
- pj_sock_close(sock[SERVER]);
- sock[SERVER] = newserver;
- }
-
- *serverfd = sock[SERVER];
- *clientfd = sock[CLIENT];
-
- return rc;
-
-on_error:
- for (i=0; i<2; ++i)
- pj_sock_close(sock[i]);
- return rc;
-}
+/* $Id$ */
+/*
+ * Copyright (C)2003-2006 Benny Prijono <benny@prijono.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#include "test.h"
+#include <pjlib.h>
+
+void app_perror(const char *msg, pj_status_t rc)
+{
+ char errbuf[256];
+
+ PJ_CHECK_STACK();
+
+ pj_strerror(rc, errbuf, sizeof(errbuf));
+ PJ_LOG(1,("test", "%s: [pj_status_t=%d] %s", msg, rc, errbuf));
+}
+
+#define SERVER 0
+#define CLIENT 1
+
+pj_status_t app_socket(int family, int type, int proto, int port,
+ pj_sock_t *ptr_sock)
+{
+ pj_sockaddr_in addr;
+ pj_sock_t sock;
+ pj_status_t rc;
+
+ rc = pj_sock_socket(family, type, proto, &sock);
+ if (rc != PJ_SUCCESS)
+ return rc;
+
+ pj_memset(&addr, 0, sizeof(addr));
+ addr.sin_family = (pj_uint16_t)family;
+ addr.sin_port = (short)(port!=-1 ? pj_htons((pj_uint16_t)port) : 0);
+ rc = pj_sock_bind(sock, &addr, sizeof(addr));
+ if (rc != PJ_SUCCESS)
+ return rc;
+
+ if (type == PJ_SOCK_STREAM) {
+ rc = pj_sock_listen(sock, 5);
+ if (rc != PJ_SUCCESS)
+ return rc;
+ }
+
+ *ptr_sock = sock;
+ return PJ_SUCCESS;
+}
+
+pj_status_t app_socketpair(int family, int type, int protocol,
+ pj_sock_t *serverfd, pj_sock_t *clientfd)
+{
+ int i;
+ static unsigned short port = 11000;
+ pj_sockaddr_in addr;
+ pj_str_t s;
+ pj_status_t rc = 0;
+ pj_sock_t sock[2];
+
+ /* Create both sockets. */
+ for (i=0; i<2; ++i) {
+ rc = pj_sock_socket(family, type, protocol, &sock[i]);
+ if (rc != PJ_SUCCESS) {
+ if (i==1)
+ pj_sock_close(sock[0]);
+ return rc;
+ }
+ }
+
+ /* Retry bind */
+ pj_memset(&addr, 0, sizeof(addr));
+ addr.sin_family = PJ_AF_INET;
+ for (i=0; i<5; ++i) {
+ addr.sin_port = pj_htons(port++);
+ rc = pj_sock_bind(sock[SERVER], &addr, sizeof(addr));
+ if (rc == PJ_SUCCESS)
+ break;
+ }
+
+ if (rc != PJ_SUCCESS)
+ goto on_error;
+
+ /* For TCP, listen the socket. */
+ if (type == PJ_SOCK_STREAM) {
+ rc = pj_sock_listen(sock[SERVER], PJ_SOMAXCONN);
+ if (rc != PJ_SUCCESS)
+ goto on_error;
+ }
+
+ /* Connect client socket. */
+ addr.sin_addr = pj_inet_addr(pj_cstr(&s, "127.0.0.1"));
+ rc = pj_sock_connect(sock[CLIENT], &addr, sizeof(addr));
+ if (rc != PJ_SUCCESS)
+ goto on_error;
+
+ /* For TCP, must accept(), and get the new socket. */
+ if (type == PJ_SOCK_STREAM) {
+ pj_sock_t newserver;
+
+ rc = pj_sock_accept(sock[SERVER], &newserver, NULL, NULL);
+ if (rc != PJ_SUCCESS)
+ goto on_error;
+
+ /* Replace server socket with new socket. */
+ pj_sock_close(sock[SERVER]);
+ sock[SERVER] = newserver;
+ }
+
+ *serverfd = sock[SERVER];
+ *clientfd = sock[CLIENT];
+
+ return rc;
+
+on_error:
+ for (i=0; i<2; ++i)
+ pj_sock_close(sock[i]);
+ return rc;
+}
diff --git a/pjmedia/build/Jbtest.dat b/pjmedia/build/Jbtest.dat
index aa7be7fe..7646b6f5 100644
--- a/pjmedia/build/Jbtest.dat
+++ b/pjmedia/build/Jbtest.dat
@@ -1,62 +1,62 @@
-#
-###############################################################################
-# This test demonstrates situation where there is no jitter.
-# Jitter should go the minimum configured value.
-###############################################################################
-#
-#PGPGPGPGPGPGPGPGPGPG PGPGPGPGPGPGPGPGPGPG PGPGPGPGPGPGPGPGPGPG PGPGPGPGPGPGPGPGPGPG PGPGPGPGPGPGPGPGPGPG PGPGPGPGPGPGPGPGPGPG PGPGPGPGPGPGPGPGPGPG PGPGPGPGPGPGPGPGPGPG
-
-#
-###############################################################################
-# This test demonstrates situation where there is no jitter, but with
-# addition of silence compression. The jitter value should also go
-# to the minimum.
-###############################################################################
-#
-#PGPGPGPGPGPGPGPGPGPG PGPGPGPGPGPGPGPGPGPG PGPGPGPGPGPGPGPGPGPG PGPGPGPGPGPGPGPGPGPG /* Start silence */ GGGGGGGGGGGGGGGGGGGGG /* End silence */ PGPGPGPGPGPGPGPGPGPG PGPGPGPGPGPGPGPGPGPG PGPGPGPGPGPGPGPGPGPG PGPGPGPGPGPGPGPGPGPG
-
-#
-###############################################################################
-# This test demonstrates situation where there's about one-three packets jitter
-# in the network, without packet lost.
-###############################################################################
-#
-#PGPGPPGGPPGGPPGGGGPG PGPGPPGGPPPGGPPGGGPG PGPGPPGGPPPGGPPGGGPG PGPGPPGGPPGGPPPGGGPG PGPGPPGGPPGGPPPGGGPG
-
-#
-###############################################################################
-# Two gets two puts, no jitter
-###############################################################################
-#
-#PPGGPPGGPPGGPPGGPPGG PPGGPPGGPPGGPPGGPPGG PPGGPPGGPPGGPPGGPPGG PPGGPPGGPPGGPPGGPPGG PPGGPPGGPPGGPPGGPPGG
-
-#
-###############################################################################
-# Three gets three puts, no packet lost
-###############################################################################
-#
-#PPPGGGPPPGGGPPPGGGPGPG PPPGGGPPPGGGPPPGGGPGPG PPPGGGPPPGGGPPPGGGPGPG PPPGGGPPPGGGPPPGGGPGPG PPPGGGPPPGGGPPPGGGPGPG PPPGGGPPPGGGPPPGGGPGPG PPPGGGPPPGGGPPPGGGPGPG PPPGGGPPPGGGPPPGGGPGPG PPPGGGPPPGGGPPPGGGPGPG PPPGGGPPPGGGPPPGGGPGPG PPPGGGPPPGGGPPPGGGPGPG PPPGGGPPPGGGPPPGGGPGPG PPPGGGPPPGGGPPPGGGPGPG PPPGGGPPPGGGPPPGGGPGPG PPPGGGPPPGGGPPPGGGPGPG PPPGGGPPPGGGPPPGGGPGPG PPPGGGPPPGGGPPPGGGPGPG PPPGGGPPPGGGPPPGGGPGPG PPPGGGPPPGGGPPPGGGPGPG PPPGGGPPPGGGPPPGGGPGPG PPPGGGPPPGGGPPPGGGPGPG PPPGGGPPPGGGPPPGGGPGPG PPPGGGPPPGGGPPPGGGPGPG PPPGGGPPPGGGPPPGGGPGPG PPPGGGPPPGGGPPPGGGPGPG PPPGGGPPPGGGPPPGGGPGPG PPPGGGPPPGGGPPPGGGPGPG PPPGGGPPPGGGPPPGGGPGPG PPPGGGPPPGGGPPPGGGPGPG PPPGGGPPPGGGPPPGGGPGPG PPPGGGPPPGGGPPPGGGPGPG PPPGGGPPPGGGPPPGGGPGPG PPPGGGPPPGGGPPPGGGPGPG PPPGGGPPPGGGPPPGGGPGPG PPPGGGPPPGGGPPPGGGPGPG PPPGGGPPPGGGPPPGGGPGPG PPPGGGPPPGGGPPPGGGPGPG PPPGGGPPPGGGPPPGGGPGPG PPPGGGPPPGGGPPPGGGPGPG PPPGGGPPPGGGPPPGGGPGPG PPPGGGPPPGGGPPPGGGPGPG PPPGGGPPPGGGPPPGGGPGPG
-
-
-#
-###############################################################################
-# Three gets three puts, with packet lost
-###############################################################################
-#
-#PPPGGGPPPGGGPPPGGGPGPG /* Lost */ GGGGGGGGGG PPPGGGPPPGGGPPPGGGPGPG /* Lost */ GGGGGGGGGG PPPGGGPPPGGGPPPGGGPGPG /* Lost */ GGGGGGGGGG PPPGGGPPPGGGPPPGGGPGPG /* Lost */ GGGGGGGGGG PPPGGGPPPGGGPPPGGGPGPG /* Lost */ GGGGGGGGGG PPPGGGPPPGGGPPPGGGPGPG /* Lost */ GGGGGGGGGG PPPGGGPPPGGGPPPGGGPGPG /* Lost */ GGGGGGGGGG PPPGGGPPPGGGPPPGGGPGPG /* Lost */ GGGGGGGGGG PPPGGGPPPGGGPPPGGGPGPG /* Lost */ GGGGGGGGGG PPPGGGPPPGGGPPPGGGPGPG /* Lost */ GGGGGGGGGG PPPGGGPPPGGGPPPGGGPGPG
-
-
-#
-###############################################################################
-# Three gets three puts, then stable
-###############################################################################
-#
-PPPGGGPPPGGGPPPGGGPGPG PPPGGGPPPGGGPPPGGGPGPG PPPGGGPPPGGGPPPGGGPGPG PPPGGGPPPGGGPPPGGGPGPG PPPGGGPPPGGGPPPGGGPGPG PPPGGGPPPGGGPPPGGGPGPG PPPGGGPPPGGGPPPGGGPGPG PPPGGGPPPGGGPPPGGGPGPG PPPGGGPPPGGGPPPGGGPGPG PPPGGGPPPGGGPPPGGGPGPG PGPGPGPGPGPGPGPGPGPG PGPGPGPGPGPGPGPGPGPG PGPGPGPGPGPGPGPGPGPG PGPGPGPGPGPGPGPGPGPG PGPGPGPGPGPGPGPGPGPG PGPGPGPGPGPGPGPGPGPG PGPGPGPGPGPGPGPGPGPG PGPGPGPGPGPGPGPGPGPG PGPGPGPGPGPGPGPGPGPG PGPGPGPGPGPGPGPGPGPG PGPGPGPGPGPGPGPGPGPG PGPGPGPGPGPGPGPGPGPG PGPGPGPGPGPGPGPGPGPG PGPGPGPGPGPGPGPGPGPG PGPGPGPGPGPGPGPGPGPG PGPGPGPGPGPGPGPGPGPG PGPGPGPGPGPGPGPGPGPG PGPGPGPGPGPGPGPGPGPG PGPGPGPGPGPGPGPGPGPG
-
-#
-###############################################################################
-# Some jitter
-###############################################################################
-#
-#PGPGPGPGPG /*Some frames missing here*/ GG /*Some frames arrive*/ PPPG /*Normal*/ PGPGPGPGPGPGPGPGPGPG PGPGPGPGPGPGPGPGPGPG PGPGPGPGPGPGPGPGPGPG PGPGPGPGPGPGPGPGPGPG
-
+#
+###############################################################################
+# This test demonstrates situation where there is no jitter.
+# Jitter should go the minimum configured value.
+###############################################################################
+#
+#PGPGPGPGPGPGPGPGPGPG PGPGPGPGPGPGPGPGPGPG PGPGPGPGPGPGPGPGPGPG PGPGPGPGPGPGPGPGPGPG PGPGPGPGPGPGPGPGPGPG PGPGPGPGPGPGPGPGPGPG PGPGPGPGPGPGPGPGPGPG PGPGPGPGPGPGPGPGPGPG
+
+#
+###############################################################################
+# This test demonstrates situation where there is no jitter, but with
+# addition of silence compression. The jitter value should also go
+# to the minimum.
+###############################################################################
+#
+#PGPGPGPGPGPGPGPGPGPG PGPGPGPGPGPGPGPGPGPG PGPGPGPGPGPGPGPGPGPG PGPGPGPGPGPGPGPGPGPG /* Start silence */ GGGGGGGGGGGGGGGGGGGGG /* End silence */ PGPGPGPGPGPGPGPGPGPG PGPGPGPGPGPGPGPGPGPG PGPGPGPGPGPGPGPGPGPG PGPGPGPGPGPGPGPGPGPG
+
+#
+###############################################################################
+# This test demonstrates situation where there's about one-three packets jitter
+# in the network, without packet lost.
+###############################################################################
+#
+#PGPGPPGGPPGGPPGGGGPG PGPGPPGGPPPGGPPGGGPG PGPGPPGGPPPGGPPGGGPG PGPGPPGGPPGGPPPGGGPG PGPGPPGGPPGGPPPGGGPG
+
+#
+###############################################################################
+# Two gets two puts, no jitter
+###############################################################################
+#
+#PPGGPPGGPPGGPPGGPPGG PPGGPPGGPPGGPPGGPPGG PPGGPPGGPPGGPPGGPPGG PPGGPPGGPPGGPPGGPPGG PPGGPPGGPPGGPPGGPPGG
+
+#
+###############################################################################
+# Three gets three puts, no packet lost
+###############################################################################
+#
+#PPPGGGPPPGGGPPPGGGPGPG PPPGGGPPPGGGPPPGGGPGPG PPPGGGPPPGGGPPPGGGPGPG PPPGGGPPPGGGPPPGGGPGPG PPPGGGPPPGGGPPPGGGPGPG PPPGGGPPPGGGPPPGGGPGPG PPPGGGPPPGGGPPPGGGPGPG PPPGGGPPPGGGPPPGGGPGPG PPPGGGPPPGGGPPPGGGPGPG PPPGGGPPPGGGPPPGGGPGPG PPPGGGPPPGGGPPPGGGPGPG PPPGGGPPPGGGPPPGGGPGPG PPPGGGPPPGGGPPPGGGPGPG PPPGGGPPPGGGPPPGGGPGPG PPPGGGPPPGGGPPPGGGPGPG PPPGGGPPPGGGPPPGGGPGPG PPPGGGPPPGGGPPPGGGPGPG PPPGGGPPPGGGPPPGGGPGPG PPPGGGPPPGGGPPPGGGPGPG PPPGGGPPPGGGPPPGGGPGPG PPPGGGPPPGGGPPPGGGPGPG PPPGGGPPPGGGPPPGGGPGPG PPPGGGPPPGGGPPPGGGPGPG PPPGGGPPPGGGPPPGGGPGPG PPPGGGPPPGGGPPPGGGPGPG PPPGGGPPPGGGPPPGGGPGPG PPPGGGPPPGGGPPPGGGPGPG PPPGGGPPPGGGPPPGGGPGPG PPPGGGPPPGGGPPPGGGPGPG PPPGGGPPPGGGPPPGGGPGPG PPPGGGPPPGGGPPPGGGPGPG PPPGGGPPPGGGPPPGGGPGPG PPPGGGPPPGGGPPPGGGPGPG PPPGGGPPPGGGPPPGGGPGPG PPPGGGPPPGGGPPPGGGPGPG PPPGGGPPPGGGPPPGGGPGPG PPPGGGPPPGGGPPPGGGPGPG PPPGGGPPPGGGPPPGGGPGPG PPPGGGPPPGGGPPPGGGPGPG PPPGGGPPPGGGPPPGGGPGPG PPPGGGPPPGGGPPPGGGPGPG PPPGGGPPPGGGPPPGGGPGPG
+
+
+#
+###############################################################################
+# Three gets three puts, with packet lost
+###############################################################################
+#
+#PPPGGGPPPGGGPPPGGGPGPG /* Lost */ GGGGGGGGGG PPPGGGPPPGGGPPPGGGPGPG /* Lost */ GGGGGGGGGG PPPGGGPPPGGGPPPGGGPGPG /* Lost */ GGGGGGGGGG PPPGGGPPPGGGPPPGGGPGPG /* Lost */ GGGGGGGGGG PPPGGGPPPGGGPPPGGGPGPG /* Lost */ GGGGGGGGGG PPPGGGPPPGGGPPPGGGPGPG /* Lost */ GGGGGGGGGG PPPGGGPPPGGGPPPGGGPGPG /* Lost */ GGGGGGGGGG PPPGGGPPPGGGPPPGGGPGPG /* Lost */ GGGGGGGGGG PPPGGGPPPGGGPPPGGGPGPG /* Lost */ GGGGGGGGGG PPPGGGPPPGGGPPPGGGPGPG /* Lost */ GGGGGGGGGG PPPGGGPPPGGGPPPGGGPGPG
+
+
+#
+###############################################################################
+# Three gets three puts, then stable
+###############################################################################
+#
+PPPGGGPPPGGGPPPGGGPGPG PPPGGGPPPGGGPPPGGGPGPG PPPGGGPPPGGGPPPGGGPGPG PPPGGGPPPGGGPPPGGGPGPG PPPGGGPPPGGGPPPGGGPGPG PPPGGGPPPGGGPPPGGGPGPG PPPGGGPPPGGGPPPGGGPGPG PPPGGGPPPGGGPPPGGGPGPG PPPGGGPPPGGGPPPGGGPGPG PPPGGGPPPGGGPPPGGGPGPG PGPGPGPGPGPGPGPGPGPG PGPGPGPGPGPGPGPGPGPG PGPGPGPGPGPGPGPGPGPG PGPGPGPGPGPGPGPGPGPG PGPGPGPGPGPGPGPGPGPG PGPGPGPGPGPGPGPGPGPG PGPGPGPGPGPGPGPGPGPG PGPGPGPGPGPGPGPGPGPG PGPGPGPGPGPGPGPGPGPG PGPGPGPGPGPGPGPGPGPG PGPGPGPGPGPGPGPGPGPG PGPGPGPGPGPGPGPGPGPG PGPGPGPGPGPGPGPGPGPG PGPGPGPGPGPGPGPGPGPG PGPGPGPGPGPGPGPGPGPG PGPGPGPGPGPGPGPGPGPG PGPGPGPGPGPGPGPGPGPG PGPGPGPGPGPGPGPGPGPG PGPGPGPGPGPGPGPGPGPG
+
+#
+###############################################################################
+# Some jitter
+###############################################################################
+#
+#PGPGPGPGPG /*Some frames missing here*/ GG /*Some frames arrive*/ PPPG /*Normal*/ PGPGPGPGPGPGPGPGPGPG PGPGPGPGPGPGPGPGPGPG PGPGPGPGPGPGPGPGPGPG PGPGPGPGPGPGPGPGPGPG
+
diff --git a/pjmedia/src/pjmedia/codec.c b/pjmedia/src/pjmedia/codec.c
index 690a5a3b..b4b64d58 100644
--- a/pjmedia/src/pjmedia/codec.c
+++ b/pjmedia/src/pjmedia/codec.c
@@ -1,106 +1,106 @@
-/* $Id$ */
-/*
- * Copyright (C) 2003-2006 Benny Prijono <benny@prijono.org>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
-#include <pjmedia/codec.h>
-#include <pj/pool.h>
-#include <pj/string.h>
-#include <pj/log.h>
-
-#define THIS_FILE "codec.c"
-
-static void enum_all_codecs (pj_codec_mgr *cm)
-{
- pj_codec_factory *cf;
-
- cf = cm->factory_list.next;
- cm->codec_cnt = 0;
- while (cf != &cm->factory_list) {
- pj_codec_id temp[PJ_CODEC_MGR_MAX_CODECS];
- int i, cnt;
-
- cnt = cf->op->enum_codecs (cf, PJ_CODEC_MGR_MAX_CODECS, temp);
- if (cnt > PJ_CODEC_MGR_MAX_CODECS) {
- pj_assert(0);
- PJ_LOG(4, (THIS_FILE, "Too many codecs reported by factory"));
- cnt = PJ_CODEC_MGR_MAX_CODECS;
- }
-
- for (i=0; i<cnt && cm->codec_cnt < PJ_CODEC_MGR_MAX_CODECS; ++i) {
- cm->codecs[cm->codec_cnt++] = temp[i];
- }
-
- cf = cf->next;
- }
-}
-
-PJ_DEF(pj_status_t) pj_codec_mgr_init (pj_codec_mgr *mgr)
-{
- pj_list_init (&mgr->factory_list);
- mgr->codec_cnt = 0;
- return 0;
-}
-
-PJ_DEF(pj_status_t) pj_codec_mgr_register_factory (pj_codec_mgr *mgr,
- pj_codec_factory *factory)
-{
- pj_list_insert_before (&mgr->factory_list, factory);
- enum_all_codecs (mgr);
- return 0;
-}
-
-PJ_DEF(void) pj_codec_mgr_unregister_factory (pj_codec_mgr *mgr, pj_codec_factory *factory)
-{
- PJ_UNUSED_ARG(mgr)
- pj_list_erase(factory);
- enum_all_codecs (mgr);
-}
-
-PJ_DEF(unsigned)
-pj_codec_mgr_enum_codecs (pj_codec_mgr *mgr, unsigned count, const pj_codec_id *codecs[])
-{
- unsigned i;
-
- if (count > mgr->codec_cnt)
- count = mgr->codec_cnt;
-
- for (i=0; i<count; ++i)
- codecs[i] = &mgr->codecs[i];
-
- return mgr->codec_cnt;
-}
-
-PJ_DEF(pj_codec*) pj_codec_mgr_alloc_codec (pj_codec_mgr *mgr, const struct pj_codec_id *id)
-{
- pj_codec_factory *factory = mgr->factory_list.next;
- while (factory != &mgr->factory_list) {
- if ( (*factory->op->match_id)(factory, id) == 0 ) {
- pj_codec *codec = (*factory->op->alloc_codec)(factory, id);
- if (codec != NULL)
- return codec;
- }
- factory = factory->next;
- }
- return NULL;
-}
-
-PJ_DEF(void) pj_codec_mgr_dealloc_codec (pj_codec_mgr *mgr, pj_codec *codec)
-{
- PJ_UNUSED_ARG(mgr)
- (*codec->factory->op->dealloc_codec)(codec->factory, codec);
-}
-
+/* $Id$ */
+/*
+ * Copyright (C) 2003-2006 Benny Prijono <benny@prijono.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#include <pjmedia/codec.h>
+#include <pj/pool.h>
+#include <pj/string.h>
+#include <pj/log.h>
+
+#define THIS_FILE "codec.c"
+
+static void enum_all_codecs (pj_codec_mgr *cm)
+{
+ pj_codec_factory *cf;
+
+ cf = cm->factory_list.next;
+ cm->codec_cnt = 0;
+ while (cf != &cm->factory_list) {
+ pj_codec_id temp[PJ_CODEC_MGR_MAX_CODECS];
+ int i, cnt;
+
+ cnt = cf->op->enum_codecs (cf, PJ_CODEC_MGR_MAX_CODECS, temp);
+ if (cnt > PJ_CODEC_MGR_MAX_CODECS) {
+ pj_assert(0);
+ PJ_LOG(4, (THIS_FILE, "Too many codecs reported by factory"));
+ cnt = PJ_CODEC_MGR_MAX_CODECS;
+ }
+
+ for (i=0; i<cnt && cm->codec_cnt < PJ_CODEC_MGR_MAX_CODECS; ++i) {
+ cm->codecs[cm->codec_cnt++] = temp[i];
+ }
+
+ cf = cf->next;
+ }
+}
+
+PJ_DEF(pj_status_t) pj_codec_mgr_init (pj_codec_mgr *mgr)
+{
+ pj_list_init (&mgr->factory_list);
+ mgr->codec_cnt = 0;
+ return 0;
+}
+
+PJ_DEF(pj_status_t) pj_codec_mgr_register_factory (pj_codec_mgr *mgr,
+ pj_codec_factory *factory)
+{
+ pj_list_insert_before (&mgr->factory_list, factory);
+ enum_all_codecs (mgr);
+ return 0;
+}
+
+PJ_DEF(void) pj_codec_mgr_unregister_factory (pj_codec_mgr *mgr, pj_codec_factory *factory)
+{
+ PJ_UNUSED_ARG(mgr)
+ pj_list_erase(factory);
+ enum_all_codecs (mgr);
+}
+
+PJ_DEF(unsigned)
+pj_codec_mgr_enum_codecs (pj_codec_mgr *mgr, unsigned count, const pj_codec_id *codecs[])
+{
+ unsigned i;
+
+ if (count > mgr->codec_cnt)
+ count = mgr->codec_cnt;
+
+ for (i=0; i<count; ++i)
+ codecs[i] = &mgr->codecs[i];
+
+ return mgr->codec_cnt;
+}
+
+PJ_DEF(pj_codec*) pj_codec_mgr_alloc_codec (pj_codec_mgr *mgr, const struct pj_codec_id *id)
+{
+ pj_codec_factory *factory = mgr->factory_list.next;
+ while (factory != &mgr->factory_list) {
+ if ( (*factory->op->match_id)(factory, id) == 0 ) {
+ pj_codec *codec = (*factory->op->alloc_codec)(factory, id);
+ if (codec != NULL)
+ return codec;
+ }
+ factory = factory->next;
+ }
+ return NULL;
+}
+
+PJ_DEF(void) pj_codec_mgr_dealloc_codec (pj_codec_mgr *mgr, pj_codec *codec)
+{
+ PJ_UNUSED_ARG(mgr)
+ (*codec->factory->op->dealloc_codec)(codec->factory, codec);
+}
+
diff --git a/pjmedia/src/pjmedia/codec.h b/pjmedia/src/pjmedia/codec.h
index efb3368e..50a2513b 100644
--- a/pjmedia/src/pjmedia/codec.h
+++ b/pjmedia/src/pjmedia/codec.h
@@ -1,353 +1,353 @@
-/* $Id$ */
-/*
- * Copyright (C) 2003-2006 Benny Prijono <benny@prijono.org>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
-#ifndef __PJMEDIA_CODEC_H__
-#define __PJMEDIA_CODEC_H__
-
-
-/**
- * @file codec.h
- * @brief Codec framework.
- */
-
-#include <pj/list.h>
-
-PJ_BEGIN_DECL
-
-
-/**
- * @defgroup PJMED_CODEC Codec framework.
- * @ingroup PJMEDIA
- * @{
- */
-
-/** Top most media type. */
-typedef enum pj_media_type
-{
- /** No type. */
- PJ_MEDIA_TYPE_NONE = 0,
-
- /** The media is audio */
- PJ_MEDIA_TYPE_AUDIO = 1,
-
- /** The media is video. */
- PJ_MEDIA_TYPE_VIDEO = 2,
-
- /** Unknown media type, in this case the name will be specified in
- * encoding_name.
- */
- PJ_MEDIA_TYPE_UNKNOWN = 3,
-
-} pj_media_type;
-
-
-/** Media direction. */
-typedef enum pj_media_dir_t
-{
- /** None */
- PJ_MEDIA_DIR_NONE = 0,
-
- /** Encoding (outgoing to network) stream */
- PJ_MEDIA_DIR_ENCODING = 1,
-
- /** Decoding (incoming from network) stream. */
- PJ_MEDIA_DIR_DECODING = 2,
-
- /** Incoming and outgoing stream. */
- PJ_MEDIA_DIR_ENCODING_DECODING = 3,
-
-} pj_media_dir_t;
-
-
-/** Standard RTP paylist types. */
-typedef enum pj_rtp_pt
-{
- PJ_RTP_PT_PCMU = 0, /* audio PCMU */
- PJ_RTP_PT_GSM = 3, /* audio GSM */
- PJ_RTP_PT_G723 = 4, /* audio G723 */
- PJ_RTP_PT_DVI4_8K = 5, /* audio DVI4 8KHz */
- PJ_RTP_PT_DVI4_16K = 6, /* audio DVI4 16Khz */
- PJ_RTP_PT_LPC = 7, /* audio LPC */
- PJ_RTP_PT_PCMA = 8, /* audio PCMA */
- PJ_RTP_PT_G722 = 9, /* audio G722 */
- PJ_RTP_PT_L16_2 = 10, /* audio 16bit linear 44.1KHz stereo */
- PJ_RTP_PT_L16_1 = 11, /* audio 16bit linear 44.1KHz mono */
- PJ_RTP_PT_QCELP = 12, /* audio QCELP */
- PJ_RTP_PT_CN = 13, /* audio Comfort Noise */
- PJ_RTP_PT_MPA = 14, /* audio MPEG1 or MPEG2 as elementary streams */
- PJ_RTP_PT_G728 = 15, /* audio G728 */
- PJ_RTP_PT_DVI4_11K = 16, /* audio DVI4 11.025KHz mono */
- PJ_RTP_PT_DVI4_22K = 17, /* audio DVI4 22.050KHz mono */
- PJ_RTP_PT_G729 = 18, /* audio G729 */
- PJ_RTP_PT_CELB = 25, /* video/comb Cell-B by Sun Microsystems (RFC 2029) */
- PJ_RTP_PT_JPEG = 26, /* video JPEG */
- PJ_RTP_PT_NV = 28, /* video NV implemented by nv program by Xerox */
- PJ_RTP_PT_H261 = 31, /* video H261 */
- PJ_RTP_PT_MPV = 32, /* video MPEG1 or MPEG2 elementary streams */
- PJ_RTP_PT_MP2T = 33, /* video MPEG2 transport */
- PJ_RTP_PT_H263 = 34, /* video H263 */
-
- PJ_RTP_PT_DYNAMIC = 96, /* start of dynamic RTP payload */
-} pj_rtp_pt;
-
-
-/** Identification used to search for codec factory that supports specific
- * codec specification.
- */
-typedef struct pj_codec_id
-{
- /** Media type. */
- pj_media_type type;
-
- /** Payload type (can be dynamic). */
- unsigned pt;
-
- /** Encoding name, must be present if the payload type is dynamic. */
- pj_str_t encoding_name;
-
- /** Sampling rate. */
- unsigned sample_rate;
-} pj_codec_id;
-
-
-/** Detailed codec attributes used both to configure a codec and to query
- * the capability of codec factories.
- */
-typedef struct pj_codec_attr
-{
- pj_uint32_t sample_rate; /* Sampling rate in Hz */
- pj_uint32_t avg_bps; /* Average bandwidth in bits per second */
-
- pj_uint8_t pcm_bits_per_sample;/* Bits per sample in the PCM side */
- pj_uint16_t ptime; /* Packet time in miliseconds */
-
- unsigned pt:8; /* Payload type. */
- unsigned vad_enabled:1; /* Voice Activity Detector. */
- unsigned cng_enabled:1; /* Comfort Noise Generator. */
- unsigned lpf_enabled:1; /* Low pass filter */
- unsigned hpf_enabled:1; /* High pass filter */
- unsigned penh_enabled:1; /* Perceptual Enhancement */
- unsigned concl_enabled:1; /* Packet loss concealment */
- unsigned reserved_bit:1;
-
-} pj_codec_attr;
-
-/** Types of audio frame. */
-typedef enum pj_audio_frame_type
-{
- /** The frame is a silence audio frame. */
- PJ_AUDIO_FRAME_SILENCE,
-
- /** The frame is a non-silence audio frame. */
- PJ_AUDIO_FRAME_AUDIO,
-
-} pj_audio_frame_type;
-
-typedef struct pj_codec pj_codec;
-typedef struct pj_codec_factory pj_codec_factory;
-
-
-/** This structure describes an audio frame. */
-struct pj_audio_frame
-{
- /** Type: silence or non-silence. */
- pj_audio_frame_type type;
-
- /** Pointer to buffer. */
- void *buf;
-
- /** Frame size in bytes. */
- unsigned size;
-};
-
-/**
- * Operations that must be supported by the codec.
- */
-typedef struct pj_codec_op
-{
- /** Get default attributes. */
- pj_status_t (*default_attr) (pj_codec *codec, pj_codec_attr *attr);
-
- /** Open and initialize codec using the specified attribute.
- * @return zero on success.
- */
- pj_status_t (*init)( pj_codec *codec, pj_pool_t *pool );
-
- /** Close and shutdown codec.
- */
- pj_status_t (*open)( pj_codec *codec, pj_codec_attr *attr );
-
- /** Close and shutdown codec.
- */
- pj_status_t (*close)( pj_codec *codec );
-
- /** Encode frame.
- */
- pj_status_t (*encode)( pj_codec *codec, const struct pj_audio_frame *input,
- unsigned output_buf_len, struct pj_audio_frame *output);
-
- /** Decode frame.
- */
- pj_status_t (*decode)( pj_codec *codec, const struct pj_audio_frame *input,
- unsigned output_buf_len, struct pj_audio_frame *output);
-
-} pj_codec_op;
-
-/**
- * A codec describes an instance to encode or decode media frames.
- */
-struct pj_codec
-{
- /** Entries to put this codec instance in codec factory's list. */
- PJ_DECL_LIST_MEMBER(struct pj_codec)
-
- /** Codec's private data. */
- void *codec_data;
-
- /** Codec factory where this codec was allocated. */
- pj_codec_factory *factory;
-
- /** Operations to codec. */
- pj_codec_op *op;
-};
-
-/**
- * This structure describes operations that must be supported by codec factories.
- */
-typedef struct pj_codec_factory_op
-{
- /** Check whether the factory can create codec with the specified ID.
- * @param factory The codec factory.
- * @param id The codec ID.
- * @return zero it matches.
- */
- pj_status_t (*match_id)( pj_codec_factory *factory, const pj_codec_id *id );
-
- /** Create default attributes for the specified codec ID. This function can
- * be called by application to get the capability of the codec.
- * @param factory The codec factory.
- * @param id The codec ID.
- * @param attr The attribute to be initialized.
- * @return zero if success.
- */
- pj_status_t (*default_attr)( pj_codec_factory *factory, const pj_codec_id *id,
- pj_codec_attr *attr );
-
- /** Enumerate supported codecs.
- * @param factory The codec factory.
- * @param count Number of entries in the array.
- * @param codecs The codec array.
- * @return the total number of supported codecs, which can be less or
- * greater than requested.
- */
- unsigned (*enum_codecs) (pj_codec_factory *factory, unsigned count, pj_codec_id codecs[]);
-
- /** This function is called by codec manager to instantiate one codec
- * instance.
- * @param factory The codec factory.
- * @param id The codec ID.
- * @return the instance of the codec, or NULL if codec can not be created.
- */
- pj_codec* (*alloc_codec)( pj_codec_factory *factory, const pj_codec_id *id);
-
- /** This function is called by codec manager to return a particular instance
- * of codec back to the codec factory.
- * @param factory The codec factory.
- * @param codec The codec instance to be returned.
- */
- void (*dealloc_codec)( pj_codec_factory *factory, pj_codec *codec );
-
-} pj_codec_factory_op;
-
-/**
- * Codec factory describes a module that is able to create codec with specific
- * capabilities. These capabilities can be queried by codec manager to create
- * instances of codec.
- */
-struct pj_codec_factory
-{
- /** Entries to put this structure in the codec manager list. */
- PJ_DECL_LIST_MEMBER(struct pj_codec_factory)
-
- /** The factory's private data. */
- void *factory_data;
-
- /** Operations to the factory. */
- pj_codec_factory_op *op;
-
-};
-
-/**
- * Declare maximum codecs
- */
-#define PJ_CODEC_MGR_MAX_CODECS 32
-
-/**
- * Codec manager maintains codec factory etc.
- */
-typedef struct pj_codec_mgr
-{
- pj_codec_factory factory_list;
- unsigned codec_cnt;
- pj_codec_id codecs[PJ_CODEC_MGR_MAX_CODECS];
-} pj_codec_mgr;
-
-/**
- * Init codec manager.
- */
-PJ_DECL(pj_status_t)
-pj_codec_mgr_init (pj_codec_mgr *mgr);
-
-/**
- * Register codec to codec manager.
- */
-PJ_DECL(pj_status_t)
-pj_codec_mgr_register_factory (pj_codec_mgr *mgr, pj_codec_factory *factory);
-
-/**
- * Unregister codec.
- */
-PJ_DECL(void)
-pj_codec_mgr_unregister_factory (pj_codec_mgr *mgr, pj_codec_factory *factory);
-
-/**
- * Enumerate codecs.
- */
-PJ_DECL(unsigned)
-pj_codec_mgr_enum_codecs (pj_codec_mgr *mgr, unsigned count, const pj_codec_id *codecs[]);
-
-/**
- * Open codec.
- */
-PJ_DECL(pj_codec*)
-pj_codec_mgr_alloc_codec (pj_codec_mgr *mgr, const struct pj_codec_id *id);
-
-/**
- * Close codec.
- */
-PJ_DECL(void)
-pj_codec_mgr_dealloc_codec (pj_codec_mgr *mgr, pj_codec *codec);
-
-/**
- * @}
- */
-
-PJ_END_DECL
-
-
-#endif /* __PJMEDIA_CODEC_H__ */
+/* $Id$ */
+/*
+ * Copyright (C) 2003-2006 Benny Prijono <benny@prijono.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#ifndef __PJMEDIA_CODEC_H__
+#define __PJMEDIA_CODEC_H__
+
+
+/**
+ * @file codec.h
+ * @brief Codec framework.
+ */
+
+#include <pj/list.h>
+
+PJ_BEGIN_DECL
+
+
+/**
+ * @defgroup PJMED_CODEC Codec framework.
+ * @ingroup PJMEDIA
+ * @{
+ */
+
+/** Top most media type. */
+typedef enum pj_media_type
+{
+ /** No type. */
+ PJ_MEDIA_TYPE_NONE = 0,
+
+ /** The media is audio */
+ PJ_MEDIA_TYPE_AUDIO = 1,
+
+ /** The media is video. */
+ PJ_MEDIA_TYPE_VIDEO = 2,
+
+ /** Unknown media type, in this case the name will be specified in
+ * encoding_name.
+ */
+ PJ_MEDIA_TYPE_UNKNOWN = 3,
+
+} pj_media_type;
+
+
+/** Media direction. */
+typedef enum pj_media_dir_t
+{
+ /** None */
+ PJ_MEDIA_DIR_NONE = 0,
+
+ /** Encoding (outgoing to network) stream */
+ PJ_MEDIA_DIR_ENCODING = 1,
+
+ /** Decoding (incoming from network) stream. */
+ PJ_MEDIA_DIR_DECODING = 2,
+
+ /** Incoming and outgoing stream. */
+ PJ_MEDIA_DIR_ENCODING_DECODING = 3,
+
+} pj_media_dir_t;
+
+
+/** Standard RTP paylist types. */
+typedef enum pj_rtp_pt
+{
+ PJ_RTP_PT_PCMU = 0, /* audio PCMU */
+ PJ_RTP_PT_GSM = 3, /* audio GSM */
+ PJ_RTP_PT_G723 = 4, /* audio G723 */
+ PJ_RTP_PT_DVI4_8K = 5, /* audio DVI4 8KHz */
+ PJ_RTP_PT_DVI4_16K = 6, /* audio DVI4 16Khz */
+ PJ_RTP_PT_LPC = 7, /* audio LPC */
+ PJ_RTP_PT_PCMA = 8, /* audio PCMA */
+ PJ_RTP_PT_G722 = 9, /* audio G722 */
+ PJ_RTP_PT_L16_2 = 10, /* audio 16bit linear 44.1KHz stereo */
+ PJ_RTP_PT_L16_1 = 11, /* audio 16bit linear 44.1KHz mono */
+ PJ_RTP_PT_QCELP = 12, /* audio QCELP */
+ PJ_RTP_PT_CN = 13, /* audio Comfort Noise */
+ PJ_RTP_PT_MPA = 14, /* audio MPEG1 or MPEG2 as elementary streams */
+ PJ_RTP_PT_G728 = 15, /* audio G728 */
+ PJ_RTP_PT_DVI4_11K = 16, /* audio DVI4 11.025KHz mono */
+ PJ_RTP_PT_DVI4_22K = 17, /* audio DVI4 22.050KHz mono */
+ PJ_RTP_PT_G729 = 18, /* audio G729 */
+ PJ_RTP_PT_CELB = 25, /* video/comb Cell-B by Sun Microsystems (RFC 2029) */
+ PJ_RTP_PT_JPEG = 26, /* video JPEG */
+ PJ_RTP_PT_NV = 28, /* video NV implemented by nv program by Xerox */
+ PJ_RTP_PT_H261 = 31, /* video H261 */
+ PJ_RTP_PT_MPV = 32, /* video MPEG1 or MPEG2 elementary streams */
+ PJ_RTP_PT_MP2T = 33, /* video MPEG2 transport */
+ PJ_RTP_PT_H263 = 34, /* video H263 */
+
+ PJ_RTP_PT_DYNAMIC = 96, /* start of dynamic RTP payload */
+} pj_rtp_pt;
+
+
+/** Identification used to search for codec factory that supports specific
+ * codec specification.
+ */
+typedef struct pj_codec_id
+{
+ /** Media type. */
+ pj_media_type type;
+
+ /** Payload type (can be dynamic). */
+ unsigned pt;
+
+ /** Encoding name, must be present if the payload type is dynamic. */
+ pj_str_t encoding_name;
+
+ /** Sampling rate. */
+ unsigned sample_rate;
+} pj_codec_id;
+
+
+/** Detailed codec attributes used both to configure a codec and to query
+ * the capability of codec factories.
+ */
+typedef struct pj_codec_attr
+{
+ pj_uint32_t sample_rate; /* Sampling rate in Hz */
+ pj_uint32_t avg_bps; /* Average bandwidth in bits per second */
+
+ pj_uint8_t pcm_bits_per_sample;/* Bits per sample in the PCM side */
+ pj_uint16_t ptime; /* Packet time in miliseconds */
+
+ unsigned pt:8; /* Payload type. */
+ unsigned vad_enabled:1; /* Voice Activity Detector. */
+ unsigned cng_enabled:1; /* Comfort Noise Generator. */
+ unsigned lpf_enabled:1; /* Low pass filter */
+ unsigned hpf_enabled:1; /* High pass filter */
+ unsigned penh_enabled:1; /* Perceptual Enhancement */
+ unsigned concl_enabled:1; /* Packet loss concealment */
+ unsigned reserved_bit:1;
+
+} pj_codec_attr;
+
+/** Types of audio frame. */
+typedef enum pj_audio_frame_type
+{
+ /** The frame is a silence audio frame. */
+ PJ_AUDIO_FRAME_SILENCE,
+
+ /** The frame is a non-silence audio frame. */
+ PJ_AUDIO_FRAME_AUDIO,
+
+} pj_audio_frame_type;
+
+typedef struct pj_codec pj_codec;
+typedef struct pj_codec_factory pj_codec_factory;
+
+
+/** This structure describes an audio frame. */
+struct pj_audio_frame
+{
+ /** Type: silence or non-silence. */
+ pj_audio_frame_type type;
+
+ /** Pointer to buffer. */
+ void *buf;
+
+ /** Frame size in bytes. */
+ unsigned size;
+};
+
+/**
+ * Operations that must be supported by the codec.
+ */
+typedef struct pj_codec_op
+{
+ /** Get default attributes. */
+ pj_status_t (*default_attr) (pj_codec *codec, pj_codec_attr *attr);
+
+ /** Open and initialize codec using the specified attribute.
+ * @return zero on success.
+ */
+ pj_status_t (*init)( pj_codec *codec, pj_pool_t *pool );
+
+ /** Close and shutdown codec.
+ */
+ pj_status_t (*open)( pj_codec *codec, pj_codec_attr *attr );
+
+ /** Close and shutdown codec.
+ */
+ pj_status_t (*close)( pj_codec *codec );
+
+ /** Encode frame.
+ */
+ pj_status_t (*encode)( pj_codec *codec, const struct pj_audio_frame *input,
+ unsigned output_buf_len, struct pj_audio_frame *output);
+
+ /** Decode frame.
+ */
+ pj_status_t (*decode)( pj_codec *codec, const struct pj_audio_frame *input,
+ unsigned output_buf_len, struct pj_audio_frame *output);
+
+} pj_codec_op;
+
+/**
+ * A codec describes an instance to encode or decode media frames.
+ */
+struct pj_codec
+{
+ /** Entries to put this codec instance in codec factory's list. */
+ PJ_DECL_LIST_MEMBER(struct pj_codec)
+
+ /** Codec's private data. */
+ void *codec_data;
+
+ /** Codec factory where this codec was allocated. */
+ pj_codec_factory *factory;
+
+ /** Operations to codec. */
+ pj_codec_op *op;
+};
+
+/**
+ * This structure describes operations that must be supported by codec factories.
+ */
+typedef struct pj_codec_factory_op
+{
+ /** Check whether the factory can create codec with the specified ID.
+ * @param factory The codec factory.
+ * @param id The codec ID.
+ * @return zero it matches.
+ */
+ pj_status_t (*match_id)( pj_codec_factory *factory, const pj_codec_id *id );
+
+ /** Create default attributes for the specified codec ID. This function can
+ * be called by application to get the capability of the codec.
+ * @param factory The codec factory.
+ * @param id The codec ID.
+ * @param attr The attribute to be initialized.
+ * @return zero if success.
+ */
+ pj_status_t (*default_attr)( pj_codec_factory *factory, const pj_codec_id *id,
+ pj_codec_attr *attr );
+
+ /** Enumerate supported codecs.
+ * @param factory The codec factory.
+ * @param count Number of entries in the array.
+ * @param codecs The codec array.
+ * @return the total number of supported codecs, which can be less or
+ * greater than requested.
+ */
+ unsigned (*enum_codecs) (pj_codec_factory *factory, unsigned count, pj_codec_id codecs[]);
+
+ /** This function is called by codec manager to instantiate one codec
+ * instance.
+ * @param factory The codec factory.
+ * @param id The codec ID.
+ * @return the instance of the codec, or NULL if codec can not be created.
+ */
+ pj_codec* (*alloc_codec)( pj_codec_factory *factory, const pj_codec_id *id);
+
+ /** This function is called by codec manager to return a particular instance
+ * of codec back to the codec factory.
+ * @param factory The codec factory.
+ * @param codec The codec instance to be returned.
+ */
+ void (*dealloc_codec)( pj_codec_factory *factory, pj_codec *codec );
+
+} pj_codec_factory_op;
+
+/**
+ * Codec factory describes a module that is able to create codec with specific
+ * capabilities. These capabilities can be queried by codec manager to create
+ * instances of codec.
+ */
+struct pj_codec_factory
+{
+ /** Entries to put this structure in the codec manager list. */
+ PJ_DECL_LIST_MEMBER(struct pj_codec_factory)
+
+ /** The factory's private data. */
+ void *factory_data;
+
+ /** Operations to the factory. */
+ pj_codec_factory_op *op;
+
+};
+
+/**
+ * Declare maximum codecs
+ */
+#define PJ_CODEC_MGR_MAX_CODECS 32
+
+/**
+ * Codec manager maintains codec factory etc.
+ */
+typedef struct pj_codec_mgr
+{
+ pj_codec_factory factory_list;
+ unsigned codec_cnt;
+ pj_codec_id codecs[PJ_CODEC_MGR_MAX_CODECS];
+} pj_codec_mgr;
+
+/**
+ * Init codec manager.
+ */
+PJ_DECL(pj_status_t)
+pj_codec_mgr_init (pj_codec_mgr *mgr);
+
+/**
+ * Register codec to codec manager.
+ */
+PJ_DECL(pj_status_t)
+pj_codec_mgr_register_factory (pj_codec_mgr *mgr, pj_codec_factory *factory);
+
+/**
+ * Unregister codec.
+ */
+PJ_DECL(void)
+pj_codec_mgr_unregister_factory (pj_codec_mgr *mgr, pj_codec_factory *factory);
+
+/**
+ * Enumerate codecs.
+ */
+PJ_DECL(unsigned)
+pj_codec_mgr_enum_codecs (pj_codec_mgr *mgr, unsigned count, const pj_codec_id *codecs[]);
+
+/**
+ * Open codec.
+ */
+PJ_DECL(pj_codec*)
+pj_codec_mgr_alloc_codec (pj_codec_mgr *mgr, const struct pj_codec_id *id);
+
+/**
+ * Close codec.
+ */
+PJ_DECL(void)
+pj_codec_mgr_dealloc_codec (pj_codec_mgr *mgr, pj_codec *codec);
+
+/**
+ * @}
+ */
+
+PJ_END_DECL
+
+
+#endif /* __PJMEDIA_CODEC_H__ */
diff --git a/pjmedia/src/pjmedia/config.h b/pjmedia/src/pjmedia/config.h
index 0fc85d9f..489d5b31 100644
--- a/pjmedia/src/pjmedia/config.h
+++ b/pjmedia/src/pjmedia/config.h
@@ -1,27 +1,27 @@
-/* $Id$ */
-/*
- * Copyright (C) 2003-2006 Benny Prijono <benny@prijono.org>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
-#ifndef __PJMED_CONFIG_H__
-#define __PJMED_CONFIG_H__
-
-/**
- * @defgroup PJMEDIA Media Stack
- */
-
-
-#endif /* __PJMED_CONFIG_H__ */
+/* $Id$ */
+/*
+ * Copyright (C) 2003-2006 Benny Prijono <benny@prijono.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#ifndef __PJMED_CONFIG_H__
+#define __PJMED_CONFIG_H__
+
+/**
+ * @defgroup PJMEDIA Media Stack
+ */
+
+
+#endif /* __PJMED_CONFIG_H__ */
diff --git a/pjmedia/src/pjmedia/dsound.c b/pjmedia/src/pjmedia/dsound.c
index 36f4f732..55aeef83 100644
--- a/pjmedia/src/pjmedia/dsound.c
+++ b/pjmedia/src/pjmedia/dsound.c
@@ -1,877 +1,877 @@
-/* $Id$ */
-/*
- * Copyright (C) 2003-2006 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
- */
-#ifdef _MSC_VER
-//# pragma warning(disable: 4201) // non-standard extension: nameless struct/union
-# pragma warning(push, 3)
-#endif
-#include <pj/config.h>
-#include <pj/os.h>
-#include <pj/log.h>
-
-#include <dsound.h>
-#include <stdio.h>
-#include <assert.h>
-#include <pjmedia/sound.h>
-
-#define THIS_FILE "dsound.c"
-
-/*
- * Constants
- */
-#define PACKET_BUFFER_COUNT 4
-
-typedef struct PJ_Direct_Sound_Device PJ_Direct_Sound_Device;
-
-
-/*
- * DirectSound Factory Operations
- */
-static pj_status_t dsound_init(void);
-static const char *dsound_get_name(void);
-static pj_status_t dsound_destroy(void);
-static pj_status_t dsound_enum_devices(int *count, char *dev_names[]);
-static pj_status_t dsound_create_dev(const char *dev_name, pj_snd_dev *dev);
-static pj_status_t dsound_destroy_dev(pj_snd_dev *dev);
-
-
-/*
- * DirectSound Device Operations
- */
-static pj_status_t dsound_dev_open( pj_snd_dev *dev, pj_snd_role_t role );
-static pj_status_t dsound_dev_close( pj_snd_dev *dev );
-static pj_status_t dsound_dev_play( pj_snd_dev *dev );
-static pj_status_t dsound_dev_record( pj_snd_dev *dev );
-
-/*
- * Utils.
- */
-static pj_status_t dsound_destroy_dsound_dev( PJ_Direct_Sound_Device *dsDev );
-
-
-static pj_snd_dev_factory dsound_factory =
-{
- &dsound_init,
- &dsound_get_name,
- &dsound_destroy,
- &dsound_enum_devices,
- &dsound_create_dev,
- &dsound_destroy_dev
-};
-
-static struct pj_snd_dev_op dsound_dev_op =
-{
- &dsound_dev_open,
- &dsound_dev_close,
- &dsound_dev_play,
- &dsound_dev_record
-};
-
-#define DSOUND_TYPE_PLAYER 1
-#define DSOUND_TYPE_RECORDER 2
-
-typedef struct Direct_Sound_Descriptor
-{
- int type;
-
- LPDIRECTSOUND lpDsPlay;
- LPDIRECTSOUNDBUFFER lpDsPlayBuffer;
-
- LPDIRECTSOUNDCAPTURE lpDsCapture;
- LPDIRECTSOUNDCAPTUREBUFFER lpDsCaptureBuffer;
-
- LPDIRECTSOUNDNOTIFY lpDsNotify;
- HANDLE hEvent;
- HANDLE hThread;
- HANDLE hStartEvent;
- DWORD dwThreadQuitFlag;
- pj_thread_desc thread_desc;
- pj_thread_t *thread;
-} Direct_Sound_Descriptor;
-
-struct PJ_Direct_Sound_Device
-{
- Direct_Sound_Descriptor playDesc;
- Direct_Sound_Descriptor recDesc;
-};
-
-struct Thread_Param
-{
- pj_snd_dev *dev;
- Direct_Sound_Descriptor *desc;
-};
-
-PJ_DEF(pj_snd_dev_factory*) pj_dsound_get_factory()
-{
- return &dsound_factory;
-}
-
-/*
- * Init DirectSound.
- */
-static pj_status_t dsound_init(void)
-{
- /* Nothing to do. */
- return 0;
-}
-
-/*
- * Get the name of the factory.
- */
-static const char *dsound_get_name(void)
-{
- return "DirectSound";
-}
-
-/*
- * Destroy DirectSound.
- */
-static pj_status_t dsound_destroy(void)
-{
- /* TODO: clean up devices in case application haven't done it. */
- return 0;
-}
-
-/*
- * Enum devices in the system.
- */
-static pj_status_t dsound_enum_devices(int *count, char *dev_names[])
-{
- dev_names[0] = "DirectSound Default Device";
- *count = 1;
- return 0;
-}
-
-/*
- * Create DirectSound device.
- */
-static pj_status_t dsound_create_dev(const char *dev_name, pj_snd_dev *dev)
-{
- PJ_Direct_Sound_Device *dsDev;
-
- /* TODO: create based on the name. */
- PJ_TODO(DSOUND_CREATE_DEVICE_BY_NAME);
-
- /* Create DirectSound structure. */
- dsDev = malloc(sizeof(*dsDev));
- if (!dsDev) {
- PJ_LOG(1,(THIS_FILE, "No memory to allocate device!"));
- return -1;
- }
- memset(dsDev, 0, sizeof(*dsDev));
-
- /* Associate DirectSound device with device. */
- dev->device = dsDev;
- dev->op = &dsound_dev_op;
-
- return 0;
-}
-
-/*
- * Destroy DirectSound device.
- */
-static pj_status_t dsound_destroy_dev( pj_snd_dev *dev )
-{
- if (dev->device) {
- free(dev->device);
- dev->device = NULL;
- }
- return 0;
-}
-
-static void dsound_release_descriptor(Direct_Sound_Descriptor *desc)
-{
- if (desc->lpDsNotify)
- IDirectSoundNotify_Release( desc->lpDsNotify );
-
- if (desc->hEvent)
- CloseHandle(desc->hEvent);
-
- if (desc->lpDsPlayBuffer)
- IDirectSoundBuffer_Release( desc->lpDsPlayBuffer );
-
- if (desc->lpDsPlay)
- IDirectSound_Release( desc->lpDsPlay );
-
- if (desc->lpDsCaptureBuffer)
- IDirectSoundCaptureBuffer_Release(desc->lpDsCaptureBuffer);
-
- if (desc->lpDsCapture)
- IDirectSoundCapture_Release(desc->lpDsCapture);
-}
-
-/*
- * Destroy DirectSound resources.
- */
-static pj_status_t dsound_destroy_dsound_dev( PJ_Direct_Sound_Device *dsDev )
-{
- dsound_release_descriptor( &dsDev->playDesc );
- dsound_release_descriptor( &dsDev->recDesc );
- memset(dsDev, 0, sizeof(*dsDev));
- return 0;
-}
-
-static void init_waveformatex (PCMWAVEFORMAT *pcmwf, pj_snd_dev *dev)
-{
- memset(pcmwf, 0, sizeof(PCMWAVEFORMAT));
- pcmwf->wf.wFormatTag = WAVE_FORMAT_PCM;
- pcmwf->wf.nChannels = 1;
- pcmwf->wf.nSamplesPerSec = dev->param.samples_per_sec;
- pcmwf->wf.nBlockAlign = dev->param.bytes_per_frame;
- pcmwf->wf.nAvgBytesPerSec =
- dev->param.samples_per_sec * dev->param.bytes_per_frame;
- pcmwf->wBitsPerSample = dev->param.bits_per_sample;
-}
-
-/*
- * Initialize DirectSound player device.
- */
-static pj_status_t dsound_init_player (pj_snd_dev *dev)
-{
- HRESULT hr;
- HWND hwnd;
- PCMWAVEFORMAT pcmwf;
- DSBUFFERDESC dsbdesc;
- DSBPOSITIONNOTIFY dsPosNotify[PACKET_BUFFER_COUNT];
- unsigned i;
- PJ_Direct_Sound_Device *dsDev = dev->device;
-
- /*
- * Check parameters.
- */
- if (dev->play_cb == NULL) {
- assert(0);
- return -1;
- }
- if (dev->device == NULL) {
- assert(0);
- return -1;
- }
-
- PJ_LOG(4,(THIS_FILE, "Creating DirectSound player device"));
-
- /*
- * Create DirectSound device.
- */
- hr = DirectSoundCreate(NULL, &dsDev->playDesc.lpDsPlay, NULL);
- if (FAILED(hr))
- goto on_error;
-
- hwnd = GetForegroundWindow();
- if (hwnd == NULL) {
- hwnd = GetDesktopWindow();
- }
- hr = IDirectSound_SetCooperativeLevel( dsDev->playDesc.lpDsPlay, hwnd,
- DSSCL_PRIORITY);
- if FAILED(hr)
- goto on_error;
-
- /*
- * Create DirectSound play buffer.
- */
- // Set up wave format structure.
- init_waveformatex (&pcmwf, dev);
-
- // Set up DSBUFFERDESC structure.
- memset(&dsbdesc, 0, sizeof(DSBUFFERDESC));
- dsbdesc.dwSize = sizeof(DSBUFFERDESC);
- dsbdesc.dwFlags = DSBCAPS_CTRLVOLUME | DSBCAPS_CTRLPOSITIONNOTIFY |
- DSBCAPS_GETCURRENTPOSITION2;
-
- dsbdesc.dwBufferBytes =
- (PACKET_BUFFER_COUNT * dev->param.bytes_per_frame *
- dev->param.frames_per_packet);
- dsbdesc.lpwfxFormat = (LPWAVEFORMATEX)&pcmwf;
-
- // Create buffer.
- hr = IDirectSound_CreateSoundBuffer(dsDev->playDesc.lpDsPlay, &dsbdesc,
- &dsDev->playDesc.lpDsPlayBuffer, NULL);
- if (FAILED(hr) )
- goto on_error;
-
- /*
- * Create event for play notification.
- */
- dsDev->playDesc.hEvent = CreateEvent( NULL, FALSE, FALSE, NULL);
- if (dsDev->playDesc.hEvent == NULL)
- goto on_error;
-
- /*
- * Setup notification for play.
- */
- hr = IDirectSoundBuffer_QueryInterface( dsDev->playDesc.lpDsPlayBuffer,
- &IID_IDirectSoundNotify,
- (LPVOID *)&dsDev->playDesc.lpDsNotify);
- if (FAILED(hr))
- goto on_error;
-
-
- for (i=0; i<PACKET_BUFFER_COUNT; ++i) {
- dsPosNotify[i].dwOffset = i * dev->param.bytes_per_frame *
- dev->param.frames_per_packet;
- dsPosNotify[i].hEventNotify = dsDev->playDesc.hEvent;
- }
-
- hr = IDirectSoundNotify_SetNotificationPositions( dsDev->playDesc.lpDsNotify,
- PACKET_BUFFER_COUNT,
- dsPosNotify);
- if (FAILED(hr))
- goto on_error;
-
- /* Done setting up play device. */
- PJ_LOG(4,(THIS_FILE, "DirectSound player device created"));
-
- return 0;
-
-on_error:
- PJ_LOG(2,(THIS_FILE, "Error creating player device, hresult=0x%x", hr));
- dsound_destroy_dsound_dev(dsDev);
- return -1;
-}
-
-/*
- * Initialize DirectSound recorder device
- */
-static pj_status_t dsound_init_recorder (pj_snd_dev *dev)
-{
- HRESULT hr;
- PCMWAVEFORMAT pcmwf;
- DSCBUFFERDESC dscbdesc;
- DSBPOSITIONNOTIFY dsPosNotify[PACKET_BUFFER_COUNT];
- unsigned i;
- PJ_Direct_Sound_Device *dsDev = dev->device;
-
- /*
- * Check parameters.
- */
- if (dev->rec_cb == NULL) {
- assert(0);
- return -1;
- }
- if (dev->device == NULL) {
- assert(0);
- return -1;
- }
-
- PJ_LOG(4,(THIS_FILE, "Creating DirectSound recorder device"));
-
- /*
- * Creating recorder device.
- */
- hr = DirectSoundCaptureCreate(NULL, &dsDev->recDesc.lpDsCapture, NULL);
- if (FAILED(hr))
- goto on_error;
-
- /* Init wave format */
- init_waveformatex (&pcmwf, dev);
-
- /*
- * Setup capture buffer using sound buffer structure that was passed
- * to play buffer creation earlier.
- */
- memset(&dscbdesc, 0, sizeof(DSCBUFFERDESC));
- dscbdesc.dwSize = sizeof(DSCBUFFERDESC);
- dscbdesc.dwFlags = DSCBCAPS_WAVEMAPPED ;
- dscbdesc.dwBufferBytes =
- (PACKET_BUFFER_COUNT * dev->param.bytes_per_frame *
- dev->param.frames_per_packet);
- dscbdesc.lpwfxFormat = (LPWAVEFORMATEX)&pcmwf;
-
- hr = IDirectSoundCapture_CreateCaptureBuffer( dsDev->recDesc.lpDsCapture,
- &dscbdesc,
- &dsDev->recDesc.lpDsCaptureBuffer,
- NULL);
- if (FAILED(hr))
- goto on_error;
-
- /*
- * Create event for play notification.
- */
- dsDev->recDesc.hEvent = CreateEvent( NULL, FALSE, FALSE, NULL);
- if (dsDev->recDesc.hEvent == NULL)
- goto on_error;
-
- /*
- * Setup notifications for recording.
- */
- hr = IDirectSoundCaptureBuffer_QueryInterface( dsDev->recDesc.lpDsCaptureBuffer,
- &IID_IDirectSoundNotify,
- (LPVOID *)&dsDev->recDesc.lpDsNotify);
- if (FAILED(hr))
- goto on_error;
-
-
- for (i=0; i<PACKET_BUFFER_COUNT; ++i) {
- dsPosNotify[i].dwOffset = i * dev->param.bytes_per_frame *
- dev->param.frames_per_packet;
- dsPosNotify[i].hEventNotify = dsDev->recDesc.hEvent;
- }
-
- hr = IDirectSoundNotify_SetNotificationPositions( dsDev->recDesc.lpDsNotify,
- PACKET_BUFFER_COUNT,
- dsPosNotify);
- if (FAILED(hr))
- goto on_error;
-
- /* Done setting up recorder device. */
- PJ_LOG(4,(THIS_FILE, "DirectSound recorder device created"));
-
- return 0;
-
-on_error:
- PJ_LOG(4,(THIS_FILE, "Error creating device, hresult=%d", hr));
- dsound_destroy_dsound_dev(dsDev);
- return -1;
-}
-
-/*
- * Initialize DirectSound device.
- */
-static pj_status_t dsound_dev_open( pj_snd_dev *dev, pj_snd_role_t role )
-{
- PJ_Direct_Sound_Device *dsDev = dev->device;
- pj_status_t status;
-
- dsDev->playDesc.type = DSOUND_TYPE_PLAYER;
- dsDev->recDesc.type = DSOUND_TYPE_RECORDER;
-
- if (role & PJ_SOUND_PLAYER) {
- status = dsound_init_player (dev);
- if (status != 0)
- return status;
- }
-
- if (role & PJ_SOUND_RECORDER) {
- status = dsound_init_recorder (dev);
- if (status != 0)
- return status;
- }
-
- return 0;
-}
-
-/*
- * Close DirectSound device.
- */
-static pj_status_t dsound_dev_close( pj_snd_dev *dev )
-{
- PJ_LOG(4,(THIS_FILE, "Closing DirectSound device"));
-
- if (dev->device) {
- PJ_Direct_Sound_Device *dsDev = dev->device;
-
- if (dsDev->playDesc.hThread) {
- PJ_LOG(4,(THIS_FILE, "Stopping DirectSound player"));
- dsDev->playDesc.dwThreadQuitFlag = 1;
- SetEvent(dsDev->playDesc.hEvent);
- if (WaitForSingleObject(dsDev->playDesc.hThread, 1000) != WAIT_OBJECT_0) {
- PJ_LOG(4,(THIS_FILE, "Timed out waiting player thread to quit"));
- TerminateThread(dsDev->playDesc.hThread, -1);
- IDirectSoundBuffer_Stop( dsDev->playDesc.lpDsPlayBuffer );
- }
-
- pj_thread_destroy (dsDev->playDesc.thread);
- }
-
- if (dsDev->recDesc.hThread) {
- PJ_LOG(4,(THIS_FILE, "Stopping DirectSound recorder"));
- dsDev->recDesc.dwThreadQuitFlag = 1;
- SetEvent(dsDev->recDesc.hEvent);
- if (WaitForSingleObject(dsDev->recDesc.hThread, 1000) != WAIT_OBJECT_0) {
- PJ_LOG(4,(THIS_FILE, "Timed out waiting recorder thread to quit"));
- TerminateThread(dsDev->recDesc.hThread, -1);
- IDirectSoundCaptureBuffer_Stop( dsDev->recDesc.lpDsCaptureBuffer );
- }
-
- pj_thread_destroy (dsDev->recDesc.thread);
- }
-
- dsound_destroy_dsound_dev( dev->device );
- dev->op = NULL;
- }
-
- PJ_LOG(4,(THIS_FILE, "DirectSound device closed"));
- return 0;
-}
-
-static BOOL AppReadDataFromBuffer(LPDIRECTSOUNDCAPTUREBUFFER lpDsb, // The buffer.
- DWORD dwOffset, // Our own write cursor.
- LPBYTE lpbSoundData, // Start of our data.
- DWORD dwSoundBytes) // Size of block to copy.
-{
- LPVOID lpvPtr1;
- DWORD dwBytes1;
- LPVOID lpvPtr2;
- DWORD dwBytes2;
- HRESULT hr;
-
- // Obtain memory address of write block. This will be in two parts
- // if the block wraps around.
-
- hr = IDirectSoundCaptureBuffer_Lock( lpDsb, dwOffset, dwSoundBytes, &lpvPtr1,
- &dwBytes1, &lpvPtr2, &dwBytes2, 0);
-
- if SUCCEEDED(hr) {
- // Read from pointers.
- CopyMemory(lpbSoundData, lpvPtr1, dwBytes1);
- if (lpvPtr2 != NULL)
- CopyMemory(lpbSoundData+dwBytes1, lpvPtr2, dwBytes2);
-
- // Release the data back to DirectSound.
- hr = IDirectSoundCaptureBuffer_Unlock(lpDsb, lpvPtr1, dwBytes1, lpvPtr2, dwBytes2);
- if SUCCEEDED(hr)
- return TRUE;
- }
-
- // Lock, Unlock, or Restore failed.
- return FALSE;
-}
-
-
-static BOOL AppWriteDataToBuffer(LPDIRECTSOUNDBUFFER lpDsb, // The buffer.
- DWORD dwOffset, // Our own write cursor.
- LPBYTE lpbSoundData, // Start of our data.
- DWORD dwSoundBytes) // Size of block to copy.
-{
- LPVOID lpvPtr1;
- DWORD dwBytes1;
- LPVOID lpvPtr2;
- DWORD dwBytes2;
- HRESULT hr;
-
- // Obtain memory address of write block. This will be in two parts
- // if the block wraps around.
-
- hr = IDirectSoundBuffer_Lock( lpDsb, dwOffset, dwSoundBytes, &lpvPtr1,
- &dwBytes1, &lpvPtr2, &dwBytes2, 0);
-
- // If the buffer was lost, restore and retry lock.
- if (DSERR_BUFFERLOST == hr) {
- IDirectSoundBuffer_Restore(lpDsb);
- hr = IDirectSoundBuffer_Lock( lpDsb, dwOffset, dwSoundBytes,
- &lpvPtr1, &dwBytes1, &lpvPtr2, &dwBytes2, 0);
- }
- if SUCCEEDED(hr) {
- CopyMemory(lpvPtr1, lpbSoundData, dwBytes1);
- if (NULL != lpvPtr2)
- CopyMemory(lpvPtr2, lpbSoundData+dwBytes1, dwBytes2);
-
- hr = IDirectSoundBuffer_Unlock(lpDsb, lpvPtr1, dwBytes1, lpvPtr2, dwBytes2);
- if SUCCEEDED(hr)
- return TRUE;
- }
-
- return FALSE;
-}
-
-
-/*
- * Player thread.
- */
-static DWORD WINAPI dsound_dev_thread(void *arg)
-{
- struct Thread_Param *param = arg;
- pj_snd_dev *dev = param->dev;
- Direct_Sound_Descriptor *desc = param->desc;
- unsigned bytes_per_pkt = dev->param.bytes_per_frame *
- dev->param.frames_per_packet;
- unsigned samples_per_pkt = dev->param.samples_per_frame *
- dev->param.frames_per_packet;
- void *buffer = NULL;
- DWORD size;
- pj_status_t status;
- DWORD sample_pos;
- DWORD byte_pos;
- DWORD buffer_size =
- (PACKET_BUFFER_COUNT * dev->param.bytes_per_frame *
- dev->param.frames_per_packet);
- HRESULT hr;
-
- PJ_LOG(4,(THIS_FILE, "DirectSound thread starting"));
-
- /* Allocate buffer for sound data */
- buffer = malloc(bytes_per_pkt);
- if (!buffer) {
- PJ_LOG(1,(THIS_FILE, "Unable to allocate packet buffer!"));
- return (DWORD)-1;
- }
-
- desc->thread = pj_thread_register ("dsound", desc->thread_desc);
- if (desc->thread == NULL)
- return (DWORD)-1;
-
- /*
- * Start playing or recording!
- */
- if (desc->type == DSOUND_TYPE_PLAYER) {
- hr = IDirectSoundBuffer_SetCurrentPosition( desc->lpDsPlayBuffer, 0);
- if (FAILED(hr)) {
- PJ_LOG(1,(THIS_FILE, "DirectSound play: SetCurrentPosition() error %d", hr));
- goto on_error;
- }
-
- hr = IDirectSoundBuffer_Play(desc->lpDsPlayBuffer, 0, 0, DSBPLAY_LOOPING);
- if (FAILED(hr)) {
- PJ_LOG(1,(THIS_FILE, "DirectSound: Play() error %d", hr));
- goto on_error;
- }
- } else {
- hr = IDirectSoundCaptureBuffer_Start(desc->lpDsCaptureBuffer, DSCBSTART_LOOPING );
- if (FAILED(hr)) {
- PJ_LOG(1,(THIS_FILE, "DirectSound: Record() error %d", hr));
- goto on_error;
- }
- }
-
- /*
- * Reset initial positions.
- */
- byte_pos = 0xFFFFFFFF;
- sample_pos = 0;
-
- /*
- * Wait to get the first notification.
- */
- if (WaitForSingleObject(desc->hEvent, 100) != WAIT_OBJECT_0) {
- PJ_LOG(1,(THIS_FILE, "DirectSound: error getting notification"));
- goto on_error;
- }
-
-
- /* Get initial byte position. */
- if (desc->type == DSOUND_TYPE_PLAYER) {
- hr = IDirectSoundBuffer_GetCurrentPosition( desc->lpDsPlayBuffer,
- NULL, &byte_pos );
- if (FAILED(hr)) {
- PJ_LOG(1,(THIS_FILE, "DirectSound: unable to get "
- "position, err %d", hr));
- goto on_error;
- }
- } else {
- hr = IDirectSoundCaptureBuffer_GetCurrentPosition( desc->lpDsCaptureBuffer,
- NULL, &byte_pos );
- if (FAILED(hr)) {
- PJ_LOG(1,(THIS_FILE, "DirectSound: unable to get "
- "position, err %d", hr));
- goto on_error;
- }
- }
-
- /* Signal main thread that we're running. */
- assert( desc->hStartEvent );
- SetEvent( desc->hStartEvent );
-
- /*
- * Loop while not signalled to quit, wait for event object to be signalled
- * by DirectSound play buffer, then request for sound data from the
- * application and write it to DirectSound buffer.
- */
- do {
-
- /* Call callback to get sound data */
- if (desc->type == DSOUND_TYPE_PLAYER) {
- size = bytes_per_pkt;
- status = (*dev->play_cb)(dev, sample_pos, buffer, &size);
-
- /* Quit thread on error. */
- if (status != 0)
- break;
-
- /* Write zeroes when we've got nothing from application. */
- if (size == 0) {
- memset(buffer, 0, bytes_per_pkt);
- size = bytes_per_pkt;
- }
-
- /* Write to DirectSound buffer. */
- AppWriteDataToBuffer( desc->lpDsPlayBuffer, byte_pos,
- (LPBYTE)buffer, size);
-
- } else {
- /* Capture from DirectSound buffer. */
- size = bytes_per_pkt;
- if (AppReadDataFromBuffer( desc->lpDsCaptureBuffer, byte_pos,
- (LPBYTE)buffer, size)) {
-
- } else {
- memset(buffer, 0, size);
- }
-
- /* Call callback */
- status = (*dev->rec_cb)(dev, sample_pos, buffer, size);
-
- /* Quit thread on error. */
- if (status != 0)
- break;
- }
-
- /* Increment position. */
- byte_pos += size;
- if (byte_pos >= buffer_size)
- byte_pos -= buffer_size;
- sample_pos += samples_per_pkt;
-
- while (WaitForSingleObject(desc->hEvent, 500) != WAIT_OBJECT_0 &&
- (!desc->dwThreadQuitFlag))
- {
- Sleep(1);
- }
- } while (!desc->dwThreadQuitFlag);
-
-
- PJ_LOG(4,(THIS_FILE, "DirectSound: stopping.."));
-
- free(buffer);
- if (desc->type == DSOUND_TYPE_PLAYER) {
- IDirectSoundBuffer_Stop( desc->lpDsPlayBuffer );
- } else {
- IDirectSoundCaptureBuffer_Stop( desc->lpDsCaptureBuffer );
- }
- return 0;
-
-on_error:
- PJ_LOG(4,(THIS_FILE, "DirectSound play stopping"));
-
- if (buffer)
- free(buffer);
- if (desc->type == DSOUND_TYPE_PLAYER) {
- IDirectSoundBuffer_Stop( desc->lpDsPlayBuffer );
- } else {
- IDirectSoundCaptureBuffer_Stop( desc->lpDsCaptureBuffer );
- }
- desc->dwThreadQuitFlag = 1;
-
- /* Signal main thread that we failed to initialize */
- assert( desc->hStartEvent );
- SetEvent( desc->hStartEvent );
- return -1;
-}
-
-
-/*
- * Generic starter for play/record.
- */
-static pj_status_t dsound_dev_play_record( pj_snd_dev *dev,
- Direct_Sound_Descriptor *desc )
-{
- DWORD threadId;
- int op_type = desc->type;
- const char *op_name = (op_type == DSOUND_TYPE_PLAYER) ? "play" : "record";
- struct Thread_Param param;
-
- PJ_LOG(4,(THIS_FILE, "DirectSound %s()", op_name));
-
- /*
- * Create event for the thread to signal us that it is starting or
- * quitting during startup.
- */
- desc->hStartEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
- if (desc->hStartEvent == NULL) {
- PJ_LOG(1,(THIS_FILE, "DirectSound %s: unable to create event", op_name));
- return -1;
- }
-
- param.dev = dev;
- param.desc = desc;
-
- /*
- * Create thread to handle feeding up data to player/recorder.
- */
- desc->hThread = NULL;
- desc->dwThreadQuitFlag = 0;
- desc->hThread = CreateThread( NULL, 0, &dsound_dev_thread, &param,
- CREATE_SUSPENDED, &threadId);
- if (!desc->hThread) {
- PJ_LOG(1,(THIS_FILE, "DirectSound %s(): unable to create thread", op_name));
- return -1;
- }
-
- SetThreadPriority( desc->hThread, THREAD_PRIORITY_HIGHEST);
-
- /*
- * Resume thread.
- */
- if (ResumeThread(desc->hThread) == (DWORD)-1) {
- PJ_LOG(1,(THIS_FILE, "DirectSound %s(): unable to resume thread", op_name));
- goto on_error;
- }
-
- /*
- * Wait until we've got signal from the thread that it has successfully
- * started, or when it is quitting.
- */
- WaitForSingleObject( desc->hStartEvent, INFINITE);
-
- /* We can destroy the event now. */
- CloseHandle( desc->hStartEvent );
- desc->hStartEvent = NULL;
-
- /* Examine thread status. */
- if (desc->dwThreadQuitFlag != 0) {
- /* Thread failed to initialize */
- WaitForSingleObject(desc->hThread, INFINITE);
- CloseHandle(desc->hThread);
- desc->hThread = NULL;
- return -1;
- }
-
- return 0;
-
-on_error:
- TerminateThread(desc->hThread, -1);
- CloseHandle(desc->hThread);
- desc->hThread = NULL;
- return -1;
-}
-
-/*
- * Start playing.
- */
-static pj_status_t dsound_dev_play( pj_snd_dev *dev )
-{
- PJ_Direct_Sound_Device *dsDev = dev->device;
-
- assert(dsDev);
- if (!dsDev) {
- assert(0);
- return -1;
- }
-
- return dsound_dev_play_record( dev, &dsDev->playDesc );
-}
-
-/*
- * Start recording.
- */
-static pj_status_t dsound_dev_record( pj_snd_dev *dev )
-{
- PJ_Direct_Sound_Device *dsDev = dev->device;
-
- assert(dsDev);
- if (!dsDev) {
- assert(0);
- return -1;
- }
-
- return dsound_dev_play_record( dev, &dsDev->recDesc );
-}
-
-#ifdef _MSC_VER
-# pragma warning(pop)
-# pragma warning(disable: 4514) // unreferenced inline function has been removed
-#endif
+/* $Id$ */
+/*
+ * Copyright (C) 2003-2006 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
+ */
+#ifdef _MSC_VER
+//# pragma warning(disable: 4201) // non-standard extension: nameless struct/union
+# pragma warning(push, 3)
+#endif
+#include <pj/config.h>
+#include <pj/os.h>
+#include <pj/log.h>
+
+#include <dsound.h>
+#include <stdio.h>
+#include <assert.h>
+#include <pjmedia/sound.h>
+
+#define THIS_FILE "dsound.c"
+
+/*
+ * Constants
+ */
+#define PACKET_BUFFER_COUNT 4
+
+typedef struct PJ_Direct_Sound_Device PJ_Direct_Sound_Device;
+
+
+/*
+ * DirectSound Factory Operations
+ */
+static pj_status_t dsound_init(void);
+static const char *dsound_get_name(void);
+static pj_status_t dsound_destroy(void);
+static pj_status_t dsound_enum_devices(int *count, char *dev_names[]);
+static pj_status_t dsound_create_dev(const char *dev_name, pj_snd_dev *dev);
+static pj_status_t dsound_destroy_dev(pj_snd_dev *dev);
+
+
+/*
+ * DirectSound Device Operations
+ */
+static pj_status_t dsound_dev_open( pj_snd_dev *dev, pj_snd_role_t role );
+static pj_status_t dsound_dev_close( pj_snd_dev *dev );
+static pj_status_t dsound_dev_play( pj_snd_dev *dev );
+static pj_status_t dsound_dev_record( pj_snd_dev *dev );
+
+/*
+ * Utils.
+ */
+static pj_status_t dsound_destroy_dsound_dev( PJ_Direct_Sound_Device *dsDev );
+
+
+static pj_snd_dev_factory dsound_factory =
+{
+ &dsound_init,
+ &dsound_get_name,
+ &dsound_destroy,
+ &dsound_enum_devices,
+ &dsound_create_dev,
+ &dsound_destroy_dev
+};
+
+static struct pj_snd_dev_op dsound_dev_op =
+{
+ &dsound_dev_open,
+ &dsound_dev_close,
+ &dsound_dev_play,
+ &dsound_dev_record
+};
+
+#define DSOUND_TYPE_PLAYER 1
+#define DSOUND_TYPE_RECORDER 2
+
+typedef struct Direct_Sound_Descriptor
+{
+ int type;
+
+ LPDIRECTSOUND lpDsPlay;
+ LPDIRECTSOUNDBUFFER lpDsPlayBuffer;
+
+ LPDIRECTSOUNDCAPTURE lpDsCapture;
+ LPDIRECTSOUNDCAPTUREBUFFER lpDsCaptureBuffer;
+
+ LPDIRECTSOUNDNOTIFY lpDsNotify;
+ HANDLE hEvent;
+ HANDLE hThread;
+ HANDLE hStartEvent;
+ DWORD dwThreadQuitFlag;
+ pj_thread_desc thread_desc;
+ pj_thread_t *thread;
+} Direct_Sound_Descriptor;
+
+struct PJ_Direct_Sound_Device
+{
+ Direct_Sound_Descriptor playDesc;
+ Direct_Sound_Descriptor recDesc;
+};
+
+struct Thread_Param
+{
+ pj_snd_dev *dev;
+ Direct_Sound_Descriptor *desc;
+};
+
+PJ_DEF(pj_snd_dev_factory*) pj_dsound_get_factory()
+{
+ return &dsound_factory;
+}
+
+/*
+ * Init DirectSound.
+ */
+static pj_status_t dsound_init(void)
+{
+ /* Nothing to do. */
+ return 0;
+}
+
+/*
+ * Get the name of the factory.
+ */
+static const char *dsound_get_name(void)
+{
+ return "DirectSound";
+}
+
+/*
+ * Destroy DirectSound.
+ */
+static pj_status_t dsound_destroy(void)
+{
+ /* TODO: clean up devices in case application haven't done it. */
+ return 0;
+}
+
+/*
+ * Enum devices in the system.
+ */
+static pj_status_t dsound_enum_devices(int *count, char *dev_names[])
+{
+ dev_names[0] = "DirectSound Default Device";
+ *count = 1;
+ return 0;
+}
+
+/*
+ * Create DirectSound device.
+ */
+static pj_status_t dsound_create_dev(const char *dev_name, pj_snd_dev *dev)
+{
+ PJ_Direct_Sound_Device *dsDev;
+
+ /* TODO: create based on the name. */
+ PJ_TODO(DSOUND_CREATE_DEVICE_BY_NAME);
+
+ /* Create DirectSound structure. */
+ dsDev = malloc(sizeof(*dsDev));
+ if (!dsDev) {
+ PJ_LOG(1,(THIS_FILE, "No memory to allocate device!"));
+ return -1;
+ }
+ memset(dsDev, 0, sizeof(*dsDev));
+
+ /* Associate DirectSound device with device. */
+ dev->device = dsDev;
+ dev->op = &dsound_dev_op;
+
+ return 0;
+}
+
+/*
+ * Destroy DirectSound device.
+ */
+static pj_status_t dsound_destroy_dev( pj_snd_dev *dev )
+{
+ if (dev->device) {
+ free(dev->device);
+ dev->device = NULL;
+ }
+ return 0;
+}
+
+static void dsound_release_descriptor(Direct_Sound_Descriptor *desc)
+{
+ if (desc->lpDsNotify)
+ IDirectSoundNotify_Release( desc->lpDsNotify );
+
+ if (desc->hEvent)
+ CloseHandle(desc->hEvent);
+
+ if (desc->lpDsPlayBuffer)
+ IDirectSoundBuffer_Release( desc->lpDsPlayBuffer );
+
+ if (desc->lpDsPlay)
+ IDirectSound_Release( desc->lpDsPlay );
+
+ if (desc->lpDsCaptureBuffer)
+ IDirectSoundCaptureBuffer_Release(desc->lpDsCaptureBuffer);
+
+ if (desc->lpDsCapture)
+ IDirectSoundCapture_Release(desc->lpDsCapture);
+}
+
+/*
+ * Destroy DirectSound resources.
+ */
+static pj_status_t dsound_destroy_dsound_dev( PJ_Direct_Sound_Device *dsDev )
+{
+ dsound_release_descriptor( &dsDev->playDesc );
+ dsound_release_descriptor( &dsDev->recDesc );
+ memset(dsDev, 0, sizeof(*dsDev));
+ return 0;
+}
+
+static void init_waveformatex (PCMWAVEFORMAT *pcmwf, pj_snd_dev *dev)
+{
+ memset(pcmwf, 0, sizeof(PCMWAVEFORMAT));
+ pcmwf->wf.wFormatTag = WAVE_FORMAT_PCM;
+ pcmwf->wf.nChannels = 1;
+ pcmwf->wf.nSamplesPerSec = dev->param.samples_per_sec;
+ pcmwf->wf.nBlockAlign = dev->param.bytes_per_frame;
+ pcmwf->wf.nAvgBytesPerSec =
+ dev->param.samples_per_sec * dev->param.bytes_per_frame;
+ pcmwf->wBitsPerSample = dev->param.bits_per_sample;
+}
+
+/*
+ * Initialize DirectSound player device.
+ */
+static pj_status_t dsound_init_player (pj_snd_dev *dev)
+{
+ HRESULT hr;
+ HWND hwnd;
+ PCMWAVEFORMAT pcmwf;
+ DSBUFFERDESC dsbdesc;
+ DSBPOSITIONNOTIFY dsPosNotify[PACKET_BUFFER_COUNT];
+ unsigned i;
+ PJ_Direct_Sound_Device *dsDev = dev->device;
+
+ /*
+ * Check parameters.
+ */
+ if (dev->play_cb == NULL) {
+ assert(0);
+ return -1;
+ }
+ if (dev->device == NULL) {
+ assert(0);
+ return -1;
+ }
+
+ PJ_LOG(4,(THIS_FILE, "Creating DirectSound player device"));
+
+ /*
+ * Create DirectSound device.
+ */
+ hr = DirectSoundCreate(NULL, &dsDev->playDesc.lpDsPlay, NULL);
+ if (FAILED(hr))
+ goto on_error;
+
+ hwnd = GetForegroundWindow();
+ if (hwnd == NULL) {
+ hwnd = GetDesktopWindow();
+ }
+ hr = IDirectSound_SetCooperativeLevel( dsDev->playDesc.lpDsPlay, hwnd,
+ DSSCL_PRIORITY);
+ if FAILED(hr)
+ goto on_error;
+
+ /*
+ * Create DirectSound play buffer.
+ */
+ // Set up wave format structure.
+ init_waveformatex (&pcmwf, dev);
+
+ // Set up DSBUFFERDESC structure.
+ memset(&dsbdesc, 0, sizeof(DSBUFFERDESC));
+ dsbdesc.dwSize = sizeof(DSBUFFERDESC);
+ dsbdesc.dwFlags = DSBCAPS_CTRLVOLUME | DSBCAPS_CTRLPOSITIONNOTIFY |
+ DSBCAPS_GETCURRENTPOSITION2;
+
+ dsbdesc.dwBufferBytes =
+ (PACKET_BUFFER_COUNT * dev->param.bytes_per_frame *
+ dev->param.frames_per_packet);
+ dsbdesc.lpwfxFormat = (LPWAVEFORMATEX)&pcmwf;
+
+ // Create buffer.
+ hr = IDirectSound_CreateSoundBuffer(dsDev->playDesc.lpDsPlay, &dsbdesc,
+ &dsDev->playDesc.lpDsPlayBuffer, NULL);
+ if (FAILED(hr) )
+ goto on_error;
+
+ /*
+ * Create event for play notification.
+ */
+ dsDev->playDesc.hEvent = CreateEvent( NULL, FALSE, FALSE, NULL);
+ if (dsDev->playDesc.hEvent == NULL)
+ goto on_error;
+
+ /*
+ * Setup notification for play.
+ */
+ hr = IDirectSoundBuffer_QueryInterface( dsDev->playDesc.lpDsPlayBuffer,
+ &IID_IDirectSoundNotify,
+ (LPVOID *)&dsDev->playDesc.lpDsNotify);
+ if (FAILED(hr))
+ goto on_error;
+
+
+ for (i=0; i<PACKET_BUFFER_COUNT; ++i) {
+ dsPosNotify[i].dwOffset = i * dev->param.bytes_per_frame *
+ dev->param.frames_per_packet;
+ dsPosNotify[i].hEventNotify = dsDev->playDesc.hEvent;
+ }
+
+ hr = IDirectSoundNotify_SetNotificationPositions( dsDev->playDesc.lpDsNotify,
+ PACKET_BUFFER_COUNT,
+ dsPosNotify);
+ if (FAILED(hr))
+ goto on_error;
+
+ /* Done setting up play device. */
+ PJ_LOG(4,(THIS_FILE, "DirectSound player device created"));
+
+ return 0;
+
+on_error:
+ PJ_LOG(2,(THIS_FILE, "Error creating player device, hresult=0x%x", hr));
+ dsound_destroy_dsound_dev(dsDev);
+ return -1;
+}
+
+/*
+ * Initialize DirectSound recorder device
+ */
+static pj_status_t dsound_init_recorder (pj_snd_dev *dev)
+{
+ HRESULT hr;
+ PCMWAVEFORMAT pcmwf;
+ DSCBUFFERDESC dscbdesc;
+ DSBPOSITIONNOTIFY dsPosNotify[PACKET_BUFFER_COUNT];
+ unsigned i;
+ PJ_Direct_Sound_Device *dsDev = dev->device;
+
+ /*
+ * Check parameters.
+ */
+ if (dev->rec_cb == NULL) {
+ assert(0);
+ return -1;
+ }
+ if (dev->device == NULL) {
+ assert(0);
+ return -1;
+ }
+
+ PJ_LOG(4,(THIS_FILE, "Creating DirectSound recorder device"));
+
+ /*
+ * Creating recorder device.
+ */
+ hr = DirectSoundCaptureCreate(NULL, &dsDev->recDesc.lpDsCapture, NULL);
+ if (FAILED(hr))
+ goto on_error;
+
+ /* Init wave format */
+ init_waveformatex (&pcmwf, dev);
+
+ /*
+ * Setup capture buffer using sound buffer structure that was passed
+ * to play buffer creation earlier.
+ */
+ memset(&dscbdesc, 0, sizeof(DSCBUFFERDESC));
+ dscbdesc.dwSize = sizeof(DSCBUFFERDESC);
+ dscbdesc.dwFlags = DSCBCAPS_WAVEMAPPED ;
+ dscbdesc.dwBufferBytes =
+ (PACKET_BUFFER_COUNT * dev->param.bytes_per_frame *
+ dev->param.frames_per_packet);
+ dscbdesc.lpwfxFormat = (LPWAVEFORMATEX)&pcmwf;
+
+ hr = IDirectSoundCapture_CreateCaptureBuffer( dsDev->recDesc.lpDsCapture,
+ &dscbdesc,
+ &dsDev->recDesc.lpDsCaptureBuffer,
+ NULL);
+ if (FAILED(hr))
+ goto on_error;
+
+ /*
+ * Create event for play notification.
+ */
+ dsDev->recDesc.hEvent = CreateEvent( NULL, FALSE, FALSE, NULL);
+ if (dsDev->recDesc.hEvent == NULL)
+ goto on_error;
+
+ /*
+ * Setup notifications for recording.
+ */
+ hr = IDirectSoundCaptureBuffer_QueryInterface( dsDev->recDesc.lpDsCaptureBuffer,
+ &IID_IDirectSoundNotify,
+ (LPVOID *)&dsDev->recDesc.lpDsNotify);
+ if (FAILED(hr))
+ goto on_error;
+
+
+ for (i=0; i<PACKET_BUFFER_COUNT; ++i) {
+ dsPosNotify[i].dwOffset = i * dev->param.bytes_per_frame *
+ dev->param.frames_per_packet;
+ dsPosNotify[i].hEventNotify = dsDev->recDesc.hEvent;
+ }
+
+ hr = IDirectSoundNotify_SetNotificationPositions( dsDev->recDesc.lpDsNotify,
+ PACKET_BUFFER_COUNT,
+ dsPosNotify);
+ if (FAILED(hr))
+ goto on_error;
+
+ /* Done setting up recorder device. */
+ PJ_LOG(4,(THIS_FILE, "DirectSound recorder device created"));
+
+ return 0;
+
+on_error:
+ PJ_LOG(4,(THIS_FILE, "Error creating device, hresult=%d", hr));
+ dsound_destroy_dsound_dev(dsDev);
+ return -1;
+}
+
+/*
+ * Initialize DirectSound device.
+ */
+static pj_status_t dsound_dev_open( pj_snd_dev *dev, pj_snd_role_t role )
+{
+ PJ_Direct_Sound_Device *dsDev = dev->device;
+ pj_status_t status;
+
+ dsDev->playDesc.type = DSOUND_TYPE_PLAYER;
+ dsDev->recDesc.type = DSOUND_TYPE_RECORDER;
+
+ if (role & PJ_SOUND_PLAYER) {
+ status = dsound_init_player (dev);
+ if (status != 0)
+ return status;
+ }
+
+ if (role & PJ_SOUND_RECORDER) {
+ status = dsound_init_recorder (dev);
+ if (status != 0)
+ return status;
+ }
+
+ return 0;
+}
+
+/*
+ * Close DirectSound device.
+ */
+static pj_status_t dsound_dev_close( pj_snd_dev *dev )
+{
+ PJ_LOG(4,(THIS_FILE, "Closing DirectSound device"));
+
+ if (dev->device) {
+ PJ_Direct_Sound_Device *dsDev = dev->device;
+
+ if (dsDev->playDesc.hThread) {
+ PJ_LOG(4,(THIS_FILE, "Stopping DirectSound player"));
+ dsDev->playDesc.dwThreadQuitFlag = 1;
+ SetEvent(dsDev->playDesc.hEvent);
+ if (WaitForSingleObject(dsDev->playDesc.hThread, 1000) != WAIT_OBJECT_0) {
+ PJ_LOG(4,(THIS_FILE, "Timed out waiting player thread to quit"));
+ TerminateThread(dsDev->playDesc.hThread, -1);
+ IDirectSoundBuffer_Stop( dsDev->playDesc.lpDsPlayBuffer );
+ }
+
+ pj_thread_destroy (dsDev->playDesc.thread);
+ }
+
+ if (dsDev->recDesc.hThread) {
+ PJ_LOG(4,(THIS_FILE, "Stopping DirectSound recorder"));
+ dsDev->recDesc.dwThreadQuitFlag = 1;
+ SetEvent(dsDev->recDesc.hEvent);
+ if (WaitForSingleObject(dsDev->recDesc.hThread, 1000) != WAIT_OBJECT_0) {
+ PJ_LOG(4,(THIS_FILE, "Timed out waiting recorder thread to quit"));
+ TerminateThread(dsDev->recDesc.hThread, -1);
+ IDirectSoundCaptureBuffer_Stop( dsDev->recDesc.lpDsCaptureBuffer );
+ }
+
+ pj_thread_destroy (dsDev->recDesc.thread);
+ }
+
+ dsound_destroy_dsound_dev( dev->device );
+ dev->op = NULL;
+ }
+
+ PJ_LOG(4,(THIS_FILE, "DirectSound device closed"));
+ return 0;
+}
+
+static BOOL AppReadDataFromBuffer(LPDIRECTSOUNDCAPTUREBUFFER lpDsb, // The buffer.
+ DWORD dwOffset, // Our own write cursor.
+ LPBYTE lpbSoundData, // Start of our data.
+ DWORD dwSoundBytes) // Size of block to copy.
+{
+ LPVOID lpvPtr1;
+ DWORD dwBytes1;
+ LPVOID lpvPtr2;
+ DWORD dwBytes2;
+ HRESULT hr;
+
+ // Obtain memory address of write block. This will be in two parts
+ // if the block wraps around.
+
+ hr = IDirectSoundCaptureBuffer_Lock( lpDsb, dwOffset, dwSoundBytes, &lpvPtr1,
+ &dwBytes1, &lpvPtr2, &dwBytes2, 0);
+
+ if SUCCEEDED(hr) {
+ // Read from pointers.
+ CopyMemory(lpbSoundData, lpvPtr1, dwBytes1);
+ if (lpvPtr2 != NULL)
+ CopyMemory(lpbSoundData+dwBytes1, lpvPtr2, dwBytes2);
+
+ // Release the data back to DirectSound.
+ hr = IDirectSoundCaptureBuffer_Unlock(lpDsb, lpvPtr1, dwBytes1, lpvPtr2, dwBytes2);
+ if SUCCEEDED(hr)
+ return TRUE;
+ }
+
+ // Lock, Unlock, or Restore failed.
+ return FALSE;
+}
+
+
+static BOOL AppWriteDataToBuffer(LPDIRECTSOUNDBUFFER lpDsb, // The buffer.
+ DWORD dwOffset, // Our own write cursor.
+ LPBYTE lpbSoundData, // Start of our data.
+ DWORD dwSoundBytes) // Size of block to copy.
+{
+ LPVOID lpvPtr1;
+ DWORD dwBytes1;
+ LPVOID lpvPtr2;
+ DWORD dwBytes2;
+ HRESULT hr;
+
+ // Obtain memory address of write block. This will be in two parts
+ // if the block wraps around.
+
+ hr = IDirectSoundBuffer_Lock( lpDsb, dwOffset, dwSoundBytes, &lpvPtr1,
+ &dwBytes1, &lpvPtr2, &dwBytes2, 0);
+
+ // If the buffer was lost, restore and retry lock.
+ if (DSERR_BUFFERLOST == hr) {
+ IDirectSoundBuffer_Restore(lpDsb);
+ hr = IDirectSoundBuffer_Lock( lpDsb, dwOffset, dwSoundBytes,
+ &lpvPtr1, &dwBytes1, &lpvPtr2, &dwBytes2, 0);
+ }
+ if SUCCEEDED(hr) {
+ CopyMemory(lpvPtr1, lpbSoundData, dwBytes1);
+ if (NULL != lpvPtr2)
+ CopyMemory(lpvPtr2, lpbSoundData+dwBytes1, dwBytes2);
+
+ hr = IDirectSoundBuffer_Unlock(lpDsb, lpvPtr1, dwBytes1, lpvPtr2, dwBytes2);
+ if SUCCEEDED(hr)
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+
+/*
+ * Player thread.
+ */
+static DWORD WINAPI dsound_dev_thread(void *arg)
+{
+ struct Thread_Param *param = arg;
+ pj_snd_dev *dev = param->dev;
+ Direct_Sound_Descriptor *desc = param->desc;
+ unsigned bytes_per_pkt = dev->param.bytes_per_frame *
+ dev->param.frames_per_packet;
+ unsigned samples_per_pkt = dev->param.samples_per_frame *
+ dev->param.frames_per_packet;
+ void *buffer = NULL;
+ DWORD size;
+ pj_status_t status;
+ DWORD sample_pos;
+ DWORD byte_pos;
+ DWORD buffer_size =
+ (PACKET_BUFFER_COUNT * dev->param.bytes_per_frame *
+ dev->param.frames_per_packet);
+ HRESULT hr;
+
+ PJ_LOG(4,(THIS_FILE, "DirectSound thread starting"));
+
+ /* Allocate buffer for sound data */
+ buffer = malloc(bytes_per_pkt);
+ if (!buffer) {
+ PJ_LOG(1,(THIS_FILE, "Unable to allocate packet buffer!"));
+ return (DWORD)-1;
+ }
+
+ desc->thread = pj_thread_register ("dsound", desc->thread_desc);
+ if (desc->thread == NULL)
+ return (DWORD)-1;
+
+ /*
+ * Start playing or recording!
+ */
+ if (desc->type == DSOUND_TYPE_PLAYER) {
+ hr = IDirectSoundBuffer_SetCurrentPosition( desc->lpDsPlayBuffer, 0);
+ if (FAILED(hr)) {
+ PJ_LOG(1,(THIS_FILE, "DirectSound play: SetCurrentPosition() error %d", hr));
+ goto on_error;
+ }
+
+ hr = IDirectSoundBuffer_Play(desc->lpDsPlayBuffer, 0, 0, DSBPLAY_LOOPING);
+ if (FAILED(hr)) {
+ PJ_LOG(1,(THIS_FILE, "DirectSound: Play() error %d", hr));
+ goto on_error;
+ }
+ } else {
+ hr = IDirectSoundCaptureBuffer_Start(desc->lpDsCaptureBuffer, DSCBSTART_LOOPING );
+ if (FAILED(hr)) {
+ PJ_LOG(1,(THIS_FILE, "DirectSound: Record() error %d", hr));
+ goto on_error;
+ }
+ }
+
+ /*
+ * Reset initial positions.
+ */
+ byte_pos = 0xFFFFFFFF;
+ sample_pos = 0;
+
+ /*
+ * Wait to get the first notification.
+ */
+ if (WaitForSingleObject(desc->hEvent, 100) != WAIT_OBJECT_0) {
+ PJ_LOG(1,(THIS_FILE, "DirectSound: error getting notification"));
+ goto on_error;
+ }
+
+
+ /* Get initial byte position. */
+ if (desc->type == DSOUND_TYPE_PLAYER) {
+ hr = IDirectSoundBuffer_GetCurrentPosition( desc->lpDsPlayBuffer,
+ NULL, &byte_pos );
+ if (FAILED(hr)) {
+ PJ_LOG(1,(THIS_FILE, "DirectSound: unable to get "
+ "position, err %d", hr));
+ goto on_error;
+ }
+ } else {
+ hr = IDirectSoundCaptureBuffer_GetCurrentPosition( desc->lpDsCaptureBuffer,
+ NULL, &byte_pos );
+ if (FAILED(hr)) {
+ PJ_LOG(1,(THIS_FILE, "DirectSound: unable to get "
+ "position, err %d", hr));
+ goto on_error;
+ }
+ }
+
+ /* Signal main thread that we're running. */
+ assert( desc->hStartEvent );
+ SetEvent( desc->hStartEvent );
+
+ /*
+ * Loop while not signalled to quit, wait for event object to be signalled
+ * by DirectSound play buffer, then request for sound data from the
+ * application and write it to DirectSound buffer.
+ */
+ do {
+
+ /* Call callback to get sound data */
+ if (desc->type == DSOUND_TYPE_PLAYER) {
+ size = bytes_per_pkt;
+ status = (*dev->play_cb)(dev, sample_pos, buffer, &size);
+
+ /* Quit thread on error. */
+ if (status != 0)
+ break;
+
+ /* Write zeroes when we've got nothing from application. */
+ if (size == 0) {
+ memset(buffer, 0, bytes_per_pkt);
+ size = bytes_per_pkt;
+ }
+
+ /* Write to DirectSound buffer. */
+ AppWriteDataToBuffer( desc->lpDsPlayBuffer, byte_pos,
+ (LPBYTE)buffer, size);
+
+ } else {
+ /* Capture from DirectSound buffer. */
+ size = bytes_per_pkt;
+ if (AppReadDataFromBuffer( desc->lpDsCaptureBuffer, byte_pos,
+ (LPBYTE)buffer, size)) {
+
+ } else {
+ memset(buffer, 0, size);
+ }
+
+ /* Call callback */
+ status = (*dev->rec_cb)(dev, sample_pos, buffer, size);
+
+ /* Quit thread on error. */
+ if (status != 0)
+ break;
+ }
+
+ /* Increment position. */
+ byte_pos += size;
+ if (byte_pos >= buffer_size)
+ byte_pos -= buffer_size;
+ sample_pos += samples_per_pkt;
+
+ while (WaitForSingleObject(desc->hEvent, 500) != WAIT_OBJECT_0 &&
+ (!desc->dwThreadQuitFlag))
+ {
+ Sleep(1);
+ }
+ } while (!desc->dwThreadQuitFlag);
+
+
+ PJ_LOG(4,(THIS_FILE, "DirectSound: stopping.."));
+
+ free(buffer);
+ if (desc->type == DSOUND_TYPE_PLAYER) {
+ IDirectSoundBuffer_Stop( desc->lpDsPlayBuffer );
+ } else {
+ IDirectSoundCaptureBuffer_Stop( desc->lpDsCaptureBuffer );
+ }
+ return 0;
+
+on_error:
+ PJ_LOG(4,(THIS_FILE, "DirectSound play stopping"));
+
+ if (buffer)
+ free(buffer);
+ if (desc->type == DSOUND_TYPE_PLAYER) {
+ IDirectSoundBuffer_Stop( desc->lpDsPlayBuffer );
+ } else {
+ IDirectSoundCaptureBuffer_Stop( desc->lpDsCaptureBuffer );
+ }
+ desc->dwThreadQuitFlag = 1;
+
+ /* Signal main thread that we failed to initialize */
+ assert( desc->hStartEvent );
+ SetEvent( desc->hStartEvent );
+ return -1;
+}
+
+
+/*
+ * Generic starter for play/record.
+ */
+static pj_status_t dsound_dev_play_record( pj_snd_dev *dev,
+ Direct_Sound_Descriptor *desc )
+{
+ DWORD threadId;
+ int op_type = desc->type;
+ const char *op_name = (op_type == DSOUND_TYPE_PLAYER) ? "play" : "record";
+ struct Thread_Param param;
+
+ PJ_LOG(4,(THIS_FILE, "DirectSound %s()", op_name));
+
+ /*
+ * Create event for the thread to signal us that it is starting or
+ * quitting during startup.
+ */
+ desc->hStartEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
+ if (desc->hStartEvent == NULL) {
+ PJ_LOG(1,(THIS_FILE, "DirectSound %s: unable to create event", op_name));
+ return -1;
+ }
+
+ param.dev = dev;
+ param.desc = desc;
+
+ /*
+ * Create thread to handle feeding up data to player/recorder.
+ */
+ desc->hThread = NULL;
+ desc->dwThreadQuitFlag = 0;
+ desc->hThread = CreateThread( NULL, 0, &dsound_dev_thread, &param,
+ CREATE_SUSPENDED, &threadId);
+ if (!desc->hThread) {
+ PJ_LOG(1,(THIS_FILE, "DirectSound %s(): unable to create thread", op_name));
+ return -1;
+ }
+
+ SetThreadPriority( desc->hThread, THREAD_PRIORITY_HIGHEST);
+
+ /*
+ * Resume thread.
+ */
+ if (ResumeThread(desc->hThread) == (DWORD)-1) {
+ PJ_LOG(1,(THIS_FILE, "DirectSound %s(): unable to resume thread", op_name));
+ goto on_error;
+ }
+
+ /*
+ * Wait until we've got signal from the thread that it has successfully
+ * started, or when it is quitting.
+ */
+ WaitForSingleObject( desc->hStartEvent, INFINITE);
+
+ /* We can destroy the event now. */
+ CloseHandle( desc->hStartEvent );
+ desc->hStartEvent = NULL;
+
+ /* Examine thread status. */
+ if (desc->dwThreadQuitFlag != 0) {
+ /* Thread failed to initialize */
+ WaitForSingleObject(desc->hThread, INFINITE);
+ CloseHandle(desc->hThread);
+ desc->hThread = NULL;
+ return -1;
+ }
+
+ return 0;
+
+on_error:
+ TerminateThread(desc->hThread, -1);
+ CloseHandle(desc->hThread);
+ desc->hThread = NULL;
+ return -1;
+}
+
+/*
+ * Start playing.
+ */
+static pj_status_t dsound_dev_play( pj_snd_dev *dev )
+{
+ PJ_Direct_Sound_Device *dsDev = dev->device;
+
+ assert(dsDev);
+ if (!dsDev) {
+ assert(0);
+ return -1;
+ }
+
+ return dsound_dev_play_record( dev, &dsDev->playDesc );
+}
+
+/*
+ * Start recording.
+ */
+static pj_status_t dsound_dev_record( pj_snd_dev *dev )
+{
+ PJ_Direct_Sound_Device *dsDev = dev->device;
+
+ assert(dsDev);
+ if (!dsDev) {
+ assert(0);
+ return -1;
+ }
+
+ return dsound_dev_play_record( dev, &dsDev->recDesc );
+}
+
+#ifdef _MSC_VER
+# pragma warning(pop)
+# pragma warning(disable: 4514) // unreferenced inline function has been removed
+#endif
diff --git a/pjmedia/src/pjmedia/g711.c b/pjmedia/src/pjmedia/g711.c
index 708922ac..4275943f 100644
--- a/pjmedia/src/pjmedia/g711.c
+++ b/pjmedia/src/pjmedia/g711.c
@@ -1,617 +1,617 @@
-/* $Id$ */
-/*
- * Copyright (C) 2003-2006 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
- */
-/* This file contains file from Sun Microsystems, Inc, with the complete
- * notice in the second half of this file.
- */
-#include <pjmedia/codec.h>
-#include <pj/pool.h>
-#include <pj/string.h>
-#include <string.h> /* memset */
-
-#define G711_BPS 64000
-#define G711_CODEC_CNT 0 /* number of codec to preallocate in memory */
-
-/* These are the only public functions exported to applications */
-PJ_DECL(pj_status_t) g711_init_factory (pj_codec_factory *factory, pj_pool_t *pool);
-PJ_DECL(pj_status_t) g711_deinit_factory (pj_codec_factory *factory);
-
-/* Algorithm prototypes. */
-static unsigned char linear2alaw(int pcm_val); /* 2's complement (16-bit range) */
-static int alaw2linear(unsigned char a_val);
-static unsigned char linear2ulaw(int pcm_val);
-static int ulaw2linear(unsigned char u_val);
-
-/* Prototypes for G711 factory */
-static pj_status_t g711_match_id( pj_codec_factory *factory, const pj_codec_id *id );
-static pj_status_t g711_default_attr( pj_codec_factory *factory, const pj_codec_id *id, pj_codec_attr *attr );
-static unsigned g711_enum_codecs (pj_codec_factory *factory, unsigned count, pj_codec_id codecs[]);
-static pj_codec* g711_alloc_codec( pj_codec_factory *factory, const pj_codec_id *id);
-static void g711_dealloc_codec( pj_codec_factory *factory, pj_codec *codec );
-
-/* Prototypes for G711 implementation. */
-static pj_status_t g711_codec_default_attr (pj_codec *codec, pj_codec_attr *attr);
-static pj_status_t g711_init( pj_codec *codec, pj_pool_t *pool );
-static pj_status_t g711_open( pj_codec *codec, pj_codec_attr *attr );
-static pj_status_t g711_close( pj_codec *codec );
-static pj_status_t g711_encode( pj_codec *codec, const struct pj_audio_frame *input,
- unsigned output_buf_len, struct pj_audio_frame *output);
-static pj_status_t g711_decode( pj_codec *codec, const struct pj_audio_frame *input,
- unsigned output_buf_len, struct pj_audio_frame *output);
-
-/* Definition for G711 codec operations. */
-static pj_codec_op g711_op =
-{
- &g711_codec_default_attr ,
- &g711_init,
- &g711_open,
- &g711_close,
- &g711_encode,
- &g711_decode
-};
-
-/* Definition for G711 codec factory operations. */
-static pj_codec_factory_op g711_factory_op =
-{
- &g711_match_id,
- &g711_default_attr,
- &g711_enum_codecs,
- &g711_alloc_codec,
- &g711_dealloc_codec
-};
-
-/* G711 factory private data */
-struct g711_factory_private
-{
- pj_pool_t *pool;
- pj_codec codec_list;
-};
-
-/* G711 codec private data. */
-struct g711_private
-{
- unsigned pt;
-};
-
-
-PJ_DEF(pj_status_t) g711_init_factory (pj_codec_factory *factory, pj_pool_t *pool)
-{
- struct g711_factory_private *priv;
- //enum { CODEC_MEM_SIZE = sizeof(pj_codec) + sizeof(struct g711_private) + 4 };
-
- /* Create pool. */
- /*
- pool = pj_pool_pool_create_pool(pp, "g711ftry",
- G711_CODEC_CNT*CODEC_MEM_SIZE +
- sizeof(struct g711_factory_private),
- CODEC_MEM_SIZE, NULL);
- if (!pool)
- return -1;
- */
-
- priv = pj_pool_alloc(pool, sizeof(struct g711_factory_private));
- if (!priv)
- return -1;
-
- factory->factory_data = priv;
- factory->op = &g711_factory_op;
-
- priv->pool = pool;
- pj_list_init(&priv->codec_list);
- return 0;
-}
-
-PJ_DEF(pj_status_t) g711_deinit_factory (pj_codec_factory *factory)
-{
- struct g711_factory_private *priv = factory->factory_data;
-
- /* Invalidate member to help detect errors */
- priv->pool = NULL;
- priv->codec_list.next = priv->codec_list.prev = NULL;
- return 0;
-}
-
-static pj_status_t g711_match_id( pj_codec_factory *factory, const pj_codec_id *id )
-{
- PJ_UNUSED_ARG(factory)
-
- /* It's sufficient to check payload type only. */
- return (id->pt==PJ_RTP_PT_PCMU || id->pt==PJ_RTP_PT_PCMA) ? 0 : -1;
-}
-
-static pj_status_t g711_default_attr (pj_codec_factory *factory,
- const pj_codec_id *id,
- pj_codec_attr *attr )
-{
- PJ_UNUSED_ARG(factory)
-
- memset(attr, 0, sizeof(pj_codec_attr));
- attr->sample_rate = 8000;
- attr->avg_bps = G711_BPS;
- attr->pcm_bits_per_sample = 16;
- attr->ptime = 20;
- attr->pt = id->pt;
-
- /* Default all flag bits disabled. */
-
- return PJ_SUCCESS;
-}
-
-static unsigned g711_enum_codecs (pj_codec_factory *factory,
- unsigned count, pj_codec_id codecs[])
-{
- PJ_UNUSED_ARG(factory)
-
- if (count > 0) {
- codecs[0].type = PJ_MEDIA_TYPE_AUDIO;
- codecs[0].pt = PJ_RTP_PT_PCMU;
- codecs[0].encoding_name = pj_str("PCMU");
- codecs[0].sample_rate = 8000;
- }
- if (count > 1) {
- codecs[1].type = PJ_MEDIA_TYPE_AUDIO;
- codecs[1].pt = PJ_RTP_PT_PCMA;
- codecs[1].encoding_name = pj_str("PCMA");
- codecs[1].sample_rate = 8000;
- }
-
- return 2;
-}
-
-static pj_codec *g711_alloc_codec( pj_codec_factory *factory, const pj_codec_id *id)
-{
- struct g711_factory_private *priv = factory->factory_data;
- pj_codec *codec = NULL;
-
- /* Allocate new codec if no more is available */
- if (pj_list_empty(&priv->codec_list)) {
- struct g711_private *codec_priv;
-
- codec = pj_pool_alloc(priv->pool, sizeof(pj_codec));
- codec_priv = pj_pool_alloc(priv->pool, sizeof(struct g711_private));
- if (!codec || !codec_priv)
- return NULL;
-
- codec_priv->pt = id->pt;
-
- codec->factory = factory;
- codec->op = &g711_op;
- codec->codec_data = codec_priv;
- } else {
- codec = priv->codec_list.next;
- pj_list_erase(codec);
- }
-
- /* Zero the list, for error detection in g711_dealloc_codec */
- codec->next = codec->prev = NULL;
-
- return codec;
-}
-
-static void g711_dealloc_codec( pj_codec_factory *factory, pj_codec *codec )
-{
- struct g711_factory_private *priv = factory->factory_data;
-
- /* Check that this node has not been deallocated before */
- pj_assert (codec->next==NULL && codec->prev==NULL);
- if (codec->next!=NULL || codec->prev!=NULL) {
- return;
- }
-
- /* Insert at the back of the list */
- pj_list_insert_before(&priv->codec_list, codec);
-}
-
-static pj_status_t g711_codec_default_attr (pj_codec *codec, pj_codec_attr *attr)
-{
- struct g711_private *priv = codec->codec_data;
- pj_codec_id id;
-
- id.pt = priv->pt;
- return g711_default_attr (NULL, &id, attr);
-}
-
-static pj_status_t g711_init( pj_codec *codec, pj_pool_t *pool )
-{
- /* There's nothing to do here really */
- PJ_UNUSED_ARG(codec)
- PJ_UNUSED_ARG(pool)
-
- return PJ_SUCCESS;
-}
-
-static pj_status_t g711_open( pj_codec *codec, pj_codec_attr *attr )
-{
- struct g711_private *priv = codec->codec_data;
- priv->pt = attr->pt;
- return PJ_SUCCESS;
-}
-
-static pj_status_t g711_close( pj_codec *codec )
-{
- PJ_UNUSED_ARG(codec);
- /* Nothing to do */
- return PJ_SUCCESS;
-}
-
-static pj_status_t g711_encode( pj_codec *codec, const struct pj_audio_frame *input,
- unsigned output_buf_len, struct pj_audio_frame *output)
-{
- pj_int16_t *samples = (pj_int16_t*) input->buf;
- struct g711_private *priv = codec->codec_data;
-
- /* Check output buffer length */
- if (output_buf_len < input->size / 2)
- return -1;
-
- /* Encode */
- if (priv->pt == PJ_RTP_PT_PCMA) {
- unsigned i;
- pj_uint8_t *dst = output->buf;
-
- for (i=0; i!=input->size/2; ++i, ++dst) {
- *dst = linear2alaw(samples[i]);
- }
- } else if (priv->pt == PJ_RTP_PT_PCMU) {
- unsigned i;
- pj_uint8_t *dst = output->buf;
-
- for (i=0; i!=input->size/2; ++i, ++dst) {
- *dst = linear2ulaw(samples[i]);
- }
-
- } else {
- return -1;
- }
-
- output->type = PJ_AUDIO_FRAME_AUDIO;
- output->size = input->size / 2;
-
- return 0;
-}
-
-static pj_status_t g711_decode( pj_codec *codec, const struct pj_audio_frame *input,
- unsigned output_buf_len, struct pj_audio_frame *output)
-{
- struct g711_private *priv = codec->codec_data;
-
- /* Check output buffer length */
- if (output_buf_len < input->size * 2)
- return -1;
-
- /* Decode */
- if (priv->pt == PJ_RTP_PT_PCMA) {
- unsigned i;
- pj_uint8_t *src = input->buf;
- pj_uint16_t *dst = output->buf;
-
- for (i=0; i!=input->size; ++i) {
- *dst++ = (pj_uint16_t) alaw2linear(*src++);
- }
- } else if (priv->pt == PJ_RTP_PT_PCMU) {
- unsigned i;
- pj_uint8_t *src = input->buf;
- pj_uint16_t *dst = output->buf;
-
- for (i=0; i!=input->size; ++i) {
- *dst++ = (pj_uint16_t) ulaw2linear(*src++);
- }
-
- } else {
- return -1;
- }
-
- output->type = PJ_AUDIO_FRAME_AUDIO;
- output->size = input->size * 2;
-
- return 0;
-}
-
-
-/*
- * This source code is a product of Sun Microsystems, Inc. and is provided
- * for unrestricted use. Users may copy or modify this source code without
- * charge.
- *
- * SUN SOURCE CODE IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING
- * THE WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR
- * PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE.
- *
- * Sun source code is provided with no support and without any obligation on
- * the part of Sun Microsystems, Inc. to assist in its use, correction,
- * modification or enhancement.
- *
- * SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE
- * INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY THIS SOFTWARE
- * OR ANY PART THEREOF.
- *
- * In no event will Sun Microsystems, Inc. be liable for any lost revenue
- * or profits or other special, indirect and consequential damages, even if
- * Sun has been advised of the possibility of such damages.
- *
- * Sun Microsystems, Inc.
- * 2550 Garcia Avenue
- * Mountain View, California 94043
- */
-
-
-
-#ifdef _MSC_VER
-# pragma warning ( disable: 4244 ) /* Conversion from int to char etc */
-#endif
-
-/*
- * g711.c
- *
- * u-law, A-law and linear PCM conversions.
- */
-#define SIGN_BIT (0x80) /* Sign bit for a A-law byte. */
-#define QUANT_MASK (0xf) /* Quantization field mask. */
-#define NSEGS (8) /* Number of A-law segments. */
-#define SEG_SHIFT (4) /* Left shift for segment number. */
-#define SEG_MASK (0x70) /* Segment field mask. */
-
-static short seg_end[8] = {0xFF, 0x1FF, 0x3FF, 0x7FF,
- 0xFFF, 0x1FFF, 0x3FFF, 0x7FFF};
-
-/* copy from CCITT G.711 specifications */
-static unsigned char _u2a[128] = { /* u- to A-law conversions */
- 1, 1, 2, 2, 3, 3, 4, 4,
- 5, 5, 6, 6, 7, 7, 8, 8,
- 9, 10, 11, 12, 13, 14, 15, 16,
- 17, 18, 19, 20, 21, 22, 23, 24,
- 25, 27, 29, 31, 33, 34, 35, 36,
- 37, 38, 39, 40, 41, 42, 43, 44,
- 46, 48, 49, 50, 51, 52, 53, 54,
- 55, 56, 57, 58, 59, 60, 61, 62,
- 64, 65, 66, 67, 68, 69, 70, 71,
- 72, 73, 74, 75, 76, 77, 78, 79,
- 81, 82, 83, 84, 85, 86, 87, 88,
- 89, 90, 91, 92, 93, 94, 95, 96,
- 97, 98, 99, 100, 101, 102, 103, 104,
- 105, 106, 107, 108, 109, 110, 111, 112,
- 113, 114, 115, 116, 117, 118, 119, 120,
- 121, 122, 123, 124, 125, 126, 127, 128};
-
-static unsigned char _a2u[128] = { /* A- to u-law conversions */
- 1, 3, 5, 7, 9, 11, 13, 15,
- 16, 17, 18, 19, 20, 21, 22, 23,
- 24, 25, 26, 27, 28, 29, 30, 31,
- 32, 32, 33, 33, 34, 34, 35, 35,
- 36, 37, 38, 39, 40, 41, 42, 43,
- 44, 45, 46, 47, 48, 48, 49, 49,
- 50, 51, 52, 53, 54, 55, 56, 57,
- 58, 59, 60, 61, 62, 63, 64, 64,
- 65, 66, 67, 68, 69, 70, 71, 72,
- 73, 74, 75, 76, 77, 78, 79, 79,
- 80, 81, 82, 83, 84, 85, 86, 87,
- 88, 89, 90, 91, 92, 93, 94, 95,
- 96, 97, 98, 99, 100, 101, 102, 103,
- 104, 105, 106, 107, 108, 109, 110, 111,
- 112, 113, 114, 115, 116, 117, 118, 119,
- 120, 121, 122, 123, 124, 125, 126, 127};
-
-static int
-search(
- int val,
- short *table,
- int size)
-{
- int i;
-
- for (i = 0; i < size; i++) {
- if (val <= *table++)
- return (i);
- }
- return (size);
-}
-
-/*
- * linear2alaw() - Convert a 16-bit linear PCM value to 8-bit A-law
- *
- * linear2alaw() accepts an 16-bit integer and encodes it as A-law data.
- *
- * Linear Input Code Compressed Code
- * ------------------------ ---------------
- * 0000000wxyza 000wxyz
- * 0000001wxyza 001wxyz
- * 000001wxyzab 010wxyz
- * 00001wxyzabc 011wxyz
- * 0001wxyzabcd 100wxyz
- * 001wxyzabcde 101wxyz
- * 01wxyzabcdef 110wxyz
- * 1wxyzabcdefg 111wxyz
- *
- * For further information see John C. Bellamy's Digital Telephony, 1982,
- * John Wiley & Sons, pps 98-111 and 472-476.
- */
-static unsigned char
-linear2alaw(
- int pcm_val) /* 2's complement (16-bit range) */
-{
- int mask;
- int seg;
- unsigned char aval;
-
- if (pcm_val >= 0) {
- mask = 0xD5; /* sign (7th) bit = 1 */
- } else {
- mask = 0x55; /* sign bit = 0 */
- pcm_val = -pcm_val - 8;
- }
-
- /* Convert the scaled magnitude to segment number. */
- seg = search(pcm_val, seg_end, 8);
-
- /* Combine the sign, segment, and quantization bits. */
-
- if (seg >= 8) /* out of range, return maximum value. */
- return (0x7F ^ mask);
- else {
- aval = seg << SEG_SHIFT;
- if (seg < 2)
- aval |= (pcm_val >> 4) & QUANT_MASK;
- else
- aval |= (pcm_val >> (seg + 3)) & QUANT_MASK;
- return (aval ^ mask);
- }
-}
-
-/*
- * alaw2linear() - Convert an A-law value to 16-bit linear PCM
- *
- */
-static int
-alaw2linear(
- unsigned char a_val)
-{
- int t;
- int seg;
-
- a_val ^= 0x55;
-
- t = (a_val & QUANT_MASK) << 4;
- seg = ((unsigned)a_val & SEG_MASK) >> SEG_SHIFT;
- switch (seg) {
- case 0:
- t += 8;
- break;
- case 1:
- t += 0x108;
- break;
- default:
- t += 0x108;
- t <<= seg - 1;
- }
- return ((a_val & SIGN_BIT) ? t : -t);
-}
-
-#define BIAS (0x84) /* Bias for linear code. */
-
-/*
- * linear2ulaw() - Convert a linear PCM value to u-law
- *
- * In order to simplify the encoding process, the original linear magnitude
- * is biased by adding 33 which shifts the encoding range from (0 - 8158) to
- * (33 - 8191). The result can be seen in the following encoding table:
- *
- * Biased Linear Input Code Compressed Code
- * ------------------------ ---------------
- * 00000001wxyza 000wxyz
- * 0000001wxyzab 001wxyz
- * 000001wxyzabc 010wxyz
- * 00001wxyzabcd 011wxyz
- * 0001wxyzabcde 100wxyz
- * 001wxyzabcdef 101wxyz
- * 01wxyzabcdefg 110wxyz
- * 1wxyzabcdefgh 111wxyz
- *
- * Each biased linear code has a leading 1 which identifies the segment
- * number. The value of the segment number is equal to 7 minus the number
- * of leading 0's. The quantization interval is directly available as the
- * four bits wxyz. * The trailing bits (a - h) are ignored.
- *
- * Ordinarily the complement of the resulting code word is used for
- * transmission, and so the code word is complemented before it is returned.
- *
- * For further information see John C. Bellamy's Digital Telephony, 1982,
- * John Wiley & Sons, pps 98-111 and 472-476.
- */
-static unsigned char
-linear2ulaw(
- int pcm_val) /* 2's complement (16-bit range) */
-{
- int mask;
- int seg;
- unsigned char uval;
-
- /* Get the sign and the magnitude of the value. */
- if (pcm_val < 0) {
- pcm_val = BIAS - pcm_val;
- mask = 0x7F;
- } else {
- pcm_val += BIAS;
- mask = 0xFF;
- }
-
- /* Convert the scaled magnitude to segment number. */
- seg = search(pcm_val, seg_end, 8);
-
- /*
- * Combine the sign, segment, quantization bits;
- * and complement the code word.
- */
- if (seg >= 8) /* out of range, return maximum value. */
- return (0x7F ^ mask);
- else {
- uval = (seg << 4) | ((pcm_val >> (seg + 3)) & 0xF);
- return (uval ^ mask);
- }
-
-}
-
-/*
- * ulaw2linear() - Convert a u-law value to 16-bit linear PCM
- *
- * First, a biased linear code is derived from the code word. An unbiased
- * output can then be obtained by subtracting 33 from the biased code.
- *
- * Note that this function expects to be passed the complement of the
- * original code word. This is in keeping with ISDN conventions.
- */
-static int
-ulaw2linear(
- unsigned char u_val)
-{
- int t;
-
- /* Complement to obtain normal u-law value. */
- u_val = ~u_val;
-
- /*
- * Extract and bias the quantization bits. Then
- * shift up by the segment number and subtract out the bias.
- */
- t = ((u_val & QUANT_MASK) << 3) + BIAS;
- t <<= ((unsigned)u_val & SEG_MASK) >> SEG_SHIFT;
-
- return ((u_val & SIGN_BIT) ? (BIAS - t) : (t - BIAS));
-}
-
-/* A-law to u-law conversion */
-unsigned char
-alaw2ulaw(
- unsigned char aval)
-{
- aval &= 0xff;
- return ((aval & 0x80) ? (0xFF ^ _a2u[aval ^ 0xD5]) :
- (0x7F ^ _a2u[aval ^ 0x55]));
-}
-
-/* u-law to A-law conversion */
-unsigned char
-ulaw2alaw(
- unsigned char uval)
-{
- uval &= 0xff;
- return ((uval & 0x80) ? (0xD5 ^ (_u2a[0xFF ^ uval] - 1)) :
- (0x55 ^ (_u2a[0x7F ^ uval] - 1)));
-}
-
-
-
+/* $Id$ */
+/*
+ * Copyright (C) 2003-2006 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
+ */
+/* This file contains file from Sun Microsystems, Inc, with the complete
+ * notice in the second half of this file.
+ */
+#include <pjmedia/codec.h>
+#include <pj/pool.h>
+#include <pj/string.h>
+#include <string.h> /* memset */
+
+#define G711_BPS 64000
+#define G711_CODEC_CNT 0 /* number of codec to preallocate in memory */
+
+/* These are the only public functions exported to applications */
+PJ_DECL(pj_status_t) g711_init_factory (pj_codec_factory *factory, pj_pool_t *pool);
+PJ_DECL(pj_status_t) g711_deinit_factory (pj_codec_factory *factory);
+
+/* Algorithm prototypes. */
+static unsigned char linear2alaw(int pcm_val); /* 2's complement (16-bit range) */
+static int alaw2linear(unsigned char a_val);
+static unsigned char linear2ulaw(int pcm_val);
+static int ulaw2linear(unsigned char u_val);
+
+/* Prototypes for G711 factory */
+static pj_status_t g711_match_id( pj_codec_factory *factory, const pj_codec_id *id );
+static pj_status_t g711_default_attr( pj_codec_factory *factory, const pj_codec_id *id, pj_codec_attr *attr );
+static unsigned g711_enum_codecs (pj_codec_factory *factory, unsigned count, pj_codec_id codecs[]);
+static pj_codec* g711_alloc_codec( pj_codec_factory *factory, const pj_codec_id *id);
+static void g711_dealloc_codec( pj_codec_factory *factory, pj_codec *codec );
+
+/* Prototypes for G711 implementation. */
+static pj_status_t g711_codec_default_attr (pj_codec *codec, pj_codec_attr *attr);
+static pj_status_t g711_init( pj_codec *codec, pj_pool_t *pool );
+static pj_status_t g711_open( pj_codec *codec, pj_codec_attr *attr );
+static pj_status_t g711_close( pj_codec *codec );
+static pj_status_t g711_encode( pj_codec *codec, const struct pj_audio_frame *input,
+ unsigned output_buf_len, struct pj_audio_frame *output);
+static pj_status_t g711_decode( pj_codec *codec, const struct pj_audio_frame *input,
+ unsigned output_buf_len, struct pj_audio_frame *output);
+
+/* Definition for G711 codec operations. */
+static pj_codec_op g711_op =
+{
+ &g711_codec_default_attr ,
+ &g711_init,
+ &g711_open,
+ &g711_close,
+ &g711_encode,
+ &g711_decode
+};
+
+/* Definition for G711 codec factory operations. */
+static pj_codec_factory_op g711_factory_op =
+{
+ &g711_match_id,
+ &g711_default_attr,
+ &g711_enum_codecs,
+ &g711_alloc_codec,
+ &g711_dealloc_codec
+};
+
+/* G711 factory private data */
+struct g711_factory_private
+{
+ pj_pool_t *pool;
+ pj_codec codec_list;
+};
+
+/* G711 codec private data. */
+struct g711_private
+{
+ unsigned pt;
+};
+
+
+PJ_DEF(pj_status_t) g711_init_factory (pj_codec_factory *factory, pj_pool_t *pool)
+{
+ struct g711_factory_private *priv;
+ //enum { CODEC_MEM_SIZE = sizeof(pj_codec) + sizeof(struct g711_private) + 4 };
+
+ /* Create pool. */
+ /*
+ pool = pj_pool_pool_create_pool(pp, "g711ftry",
+ G711_CODEC_CNT*CODEC_MEM_SIZE +
+ sizeof(struct g711_factory_private),
+ CODEC_MEM_SIZE, NULL);
+ if (!pool)
+ return -1;
+ */
+
+ priv = pj_pool_alloc(pool, sizeof(struct g711_factory_private));
+ if (!priv)
+ return -1;
+
+ factory->factory_data = priv;
+ factory->op = &g711_factory_op;
+
+ priv->pool = pool;
+ pj_list_init(&priv->codec_list);
+ return 0;
+}
+
+PJ_DEF(pj_status_t) g711_deinit_factory (pj_codec_factory *factory)
+{
+ struct g711_factory_private *priv = factory->factory_data;
+
+ /* Invalidate member to help detect errors */
+ priv->pool = NULL;
+ priv->codec_list.next = priv->codec_list.prev = NULL;
+ return 0;
+}
+
+static pj_status_t g711_match_id( pj_codec_factory *factory, const pj_codec_id *id )
+{
+ PJ_UNUSED_ARG(factory)
+
+ /* It's sufficient to check payload type only. */
+ return (id->pt==PJ_RTP_PT_PCMU || id->pt==PJ_RTP_PT_PCMA) ? 0 : -1;
+}
+
+static pj_status_t g711_default_attr (pj_codec_factory *factory,
+ const pj_codec_id *id,
+ pj_codec_attr *attr )
+{
+ PJ_UNUSED_ARG(factory)
+
+ memset(attr, 0, sizeof(pj_codec_attr));
+ attr->sample_rate = 8000;
+ attr->avg_bps = G711_BPS;
+ attr->pcm_bits_per_sample = 16;
+ attr->ptime = 20;
+ attr->pt = id->pt;
+
+ /* Default all flag bits disabled. */
+
+ return PJ_SUCCESS;
+}
+
+static unsigned g711_enum_codecs (pj_codec_factory *factory,
+ unsigned count, pj_codec_id codecs[])
+{
+ PJ_UNUSED_ARG(factory)
+
+ if (count > 0) {
+ codecs[0].type = PJ_MEDIA_TYPE_AUDIO;
+ codecs[0].pt = PJ_RTP_PT_PCMU;
+ codecs[0].encoding_name = pj_str("PCMU");
+ codecs[0].sample_rate = 8000;
+ }
+ if (count > 1) {
+ codecs[1].type = PJ_MEDIA_TYPE_AUDIO;
+ codecs[1].pt = PJ_RTP_PT_PCMA;
+ codecs[1].encoding_name = pj_str("PCMA");
+ codecs[1].sample_rate = 8000;
+ }
+
+ return 2;
+}
+
+static pj_codec *g711_alloc_codec( pj_codec_factory *factory, const pj_codec_id *id)
+{
+ struct g711_factory_private *priv = factory->factory_data;
+ pj_codec *codec = NULL;
+
+ /* Allocate new codec if no more is available */
+ if (pj_list_empty(&priv->codec_list)) {
+ struct g711_private *codec_priv;
+
+ codec = pj_pool_alloc(priv->pool, sizeof(pj_codec));
+ codec_priv = pj_pool_alloc(priv->pool, sizeof(struct g711_private));
+ if (!codec || !codec_priv)
+ return NULL;
+
+ codec_priv->pt = id->pt;
+
+ codec->factory = factory;
+ codec->op = &g711_op;
+ codec->codec_data = codec_priv;
+ } else {
+ codec = priv->codec_list.next;
+ pj_list_erase(codec);
+ }
+
+ /* Zero the list, for error detection in g711_dealloc_codec */
+ codec->next = codec->prev = NULL;
+
+ return codec;
+}
+
+static void g711_dealloc_codec( pj_codec_factory *factory, pj_codec *codec )
+{
+ struct g711_factory_private *priv = factory->factory_data;
+
+ /* Check that this node has not been deallocated before */
+ pj_assert (codec->next==NULL && codec->prev==NULL);
+ if (codec->next!=NULL || codec->prev!=NULL) {
+ return;
+ }
+
+ /* Insert at the back of the list */
+ pj_list_insert_before(&priv->codec_list, codec);
+}
+
+static pj_status_t g711_codec_default_attr (pj_codec *codec, pj_codec_attr *attr)
+{
+ struct g711_private *priv = codec->codec_data;
+ pj_codec_id id;
+
+ id.pt = priv->pt;
+ return g711_default_attr (NULL, &id, attr);
+}
+
+static pj_status_t g711_init( pj_codec *codec, pj_pool_t *pool )
+{
+ /* There's nothing to do here really */
+ PJ_UNUSED_ARG(codec)
+ PJ_UNUSED_ARG(pool)
+
+ return PJ_SUCCESS;
+}
+
+static pj_status_t g711_open( pj_codec *codec, pj_codec_attr *attr )
+{
+ struct g711_private *priv = codec->codec_data;
+ priv->pt = attr->pt;
+ return PJ_SUCCESS;
+}
+
+static pj_status_t g711_close( pj_codec *codec )
+{
+ PJ_UNUSED_ARG(codec);
+ /* Nothing to do */
+ return PJ_SUCCESS;
+}
+
+static pj_status_t g711_encode( pj_codec *codec, const struct pj_audio_frame *input,
+ unsigned output_buf_len, struct pj_audio_frame *output)
+{
+ pj_int16_t *samples = (pj_int16_t*) input->buf;
+ struct g711_private *priv = codec->codec_data;
+
+ /* Check output buffer length */
+ if (output_buf_len < input->size / 2)
+ return -1;
+
+ /* Encode */
+ if (priv->pt == PJ_RTP_PT_PCMA) {
+ unsigned i;
+ pj_uint8_t *dst = output->buf;
+
+ for (i=0; i!=input->size/2; ++i, ++dst) {
+ *dst = linear2alaw(samples[i]);
+ }
+ } else if (priv->pt == PJ_RTP_PT_PCMU) {
+ unsigned i;
+ pj_uint8_t *dst = output->buf;
+
+ for (i=0; i!=input->size/2; ++i, ++dst) {
+ *dst = linear2ulaw(samples[i]);
+ }
+
+ } else {
+ return -1;
+ }
+
+ output->type = PJ_AUDIO_FRAME_AUDIO;
+ output->size = input->size / 2;
+
+ return 0;
+}
+
+static pj_status_t g711_decode( pj_codec *codec, const struct pj_audio_frame *input,
+ unsigned output_buf_len, struct pj_audio_frame *output)
+{
+ struct g711_private *priv = codec->codec_data;
+
+ /* Check output buffer length */
+ if (output_buf_len < input->size * 2)
+ return -1;
+
+ /* Decode */
+ if (priv->pt == PJ_RTP_PT_PCMA) {
+ unsigned i;
+ pj_uint8_t *src = input->buf;
+ pj_uint16_t *dst = output->buf;
+
+ for (i=0; i!=input->size; ++i) {
+ *dst++ = (pj_uint16_t) alaw2linear(*src++);
+ }
+ } else if (priv->pt == PJ_RTP_PT_PCMU) {
+ unsigned i;
+ pj_uint8_t *src = input->buf;
+ pj_uint16_t *dst = output->buf;
+
+ for (i=0; i!=input->size; ++i) {
+ *dst++ = (pj_uint16_t) ulaw2linear(*src++);
+ }
+
+ } else {
+ return -1;
+ }
+
+ output->type = PJ_AUDIO_FRAME_AUDIO;
+ output->size = input->size * 2;
+
+ return 0;
+}
+
+
+/*
+ * This source code is a product of Sun Microsystems, Inc. and is provided
+ * for unrestricted use. Users may copy or modify this source code without
+ * charge.
+ *
+ * SUN SOURCE CODE IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING
+ * THE WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE.
+ *
+ * Sun source code is provided with no support and without any obligation on
+ * the part of Sun Microsystems, Inc. to assist in its use, correction,
+ * modification or enhancement.
+ *
+ * SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE
+ * INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY THIS SOFTWARE
+ * OR ANY PART THEREOF.
+ *
+ * In no event will Sun Microsystems, Inc. be liable for any lost revenue
+ * or profits or other special, indirect and consequential damages, even if
+ * Sun has been advised of the possibility of such damages.
+ *
+ * Sun Microsystems, Inc.
+ * 2550 Garcia Avenue
+ * Mountain View, California 94043
+ */
+
+
+
+#ifdef _MSC_VER
+# pragma warning ( disable: 4244 ) /* Conversion from int to char etc */
+#endif
+
+/*
+ * g711.c
+ *
+ * u-law, A-law and linear PCM conversions.
+ */
+#define SIGN_BIT (0x80) /* Sign bit for a A-law byte. */
+#define QUANT_MASK (0xf) /* Quantization field mask. */
+#define NSEGS (8) /* Number of A-law segments. */
+#define SEG_SHIFT (4) /* Left shift for segment number. */
+#define SEG_MASK (0x70) /* Segment field mask. */
+
+static short seg_end[8] = {0xFF, 0x1FF, 0x3FF, 0x7FF,
+ 0xFFF, 0x1FFF, 0x3FFF, 0x7FFF};
+
+/* copy from CCITT G.711 specifications */
+static unsigned char _u2a[128] = { /* u- to A-law conversions */
+ 1, 1, 2, 2, 3, 3, 4, 4,
+ 5, 5, 6, 6, 7, 7, 8, 8,
+ 9, 10, 11, 12, 13, 14, 15, 16,
+ 17, 18, 19, 20, 21, 22, 23, 24,
+ 25, 27, 29, 31, 33, 34, 35, 36,
+ 37, 38, 39, 40, 41, 42, 43, 44,
+ 46, 48, 49, 50, 51, 52, 53, 54,
+ 55, 56, 57, 58, 59, 60, 61, 62,
+ 64, 65, 66, 67, 68, 69, 70, 71,
+ 72, 73, 74, 75, 76, 77, 78, 79,
+ 81, 82, 83, 84, 85, 86, 87, 88,
+ 89, 90, 91, 92, 93, 94, 95, 96,
+ 97, 98, 99, 100, 101, 102, 103, 104,
+ 105, 106, 107, 108, 109, 110, 111, 112,
+ 113, 114, 115, 116, 117, 118, 119, 120,
+ 121, 122, 123, 124, 125, 126, 127, 128};
+
+static unsigned char _a2u[128] = { /* A- to u-law conversions */
+ 1, 3, 5, 7, 9, 11, 13, 15,
+ 16, 17, 18, 19, 20, 21, 22, 23,
+ 24, 25, 26, 27, 28, 29, 30, 31,
+ 32, 32, 33, 33, 34, 34, 35, 35,
+ 36, 37, 38, 39, 40, 41, 42, 43,
+ 44, 45, 46, 47, 48, 48, 49, 49,
+ 50, 51, 52, 53, 54, 55, 56, 57,
+ 58, 59, 60, 61, 62, 63, 64, 64,
+ 65, 66, 67, 68, 69, 70, 71, 72,
+ 73, 74, 75, 76, 77, 78, 79, 79,
+ 80, 81, 82, 83, 84, 85, 86, 87,
+ 88, 89, 90, 91, 92, 93, 94, 95,
+ 96, 97, 98, 99, 100, 101, 102, 103,
+ 104, 105, 106, 107, 108, 109, 110, 111,
+ 112, 113, 114, 115, 116, 117, 118, 119,
+ 120, 121, 122, 123, 124, 125, 126, 127};
+
+static int
+search(
+ int val,
+ short *table,
+ int size)
+{
+ int i;
+
+ for (i = 0; i < size; i++) {
+ if (val <= *table++)
+ return (i);
+ }
+ return (size);
+}
+
+/*
+ * linear2alaw() - Convert a 16-bit linear PCM value to 8-bit A-law
+ *
+ * linear2alaw() accepts an 16-bit integer and encodes it as A-law data.
+ *
+ * Linear Input Code Compressed Code
+ * ------------------------ ---------------
+ * 0000000wxyza 000wxyz
+ * 0000001wxyza 001wxyz
+ * 000001wxyzab 010wxyz
+ * 00001wxyzabc 011wxyz
+ * 0001wxyzabcd 100wxyz
+ * 001wxyzabcde 101wxyz
+ * 01wxyzabcdef 110wxyz
+ * 1wxyzabcdefg 111wxyz
+ *
+ * For further information see John C. Bellamy's Digital Telephony, 1982,
+ * John Wiley & Sons, pps 98-111 and 472-476.
+ */
+static unsigned char
+linear2alaw(
+ int pcm_val) /* 2's complement (16-bit range) */
+{
+ int mask;
+ int seg;
+ unsigned char aval;
+
+ if (pcm_val >= 0) {
+ mask = 0xD5; /* sign (7th) bit = 1 */
+ } else {
+ mask = 0x55; /* sign bit = 0 */
+ pcm_val = -pcm_val - 8;
+ }
+
+ /* Convert the scaled magnitude to segment number. */
+ seg = search(pcm_val, seg_end, 8);
+
+ /* Combine the sign, segment, and quantization bits. */
+
+ if (seg >= 8) /* out of range, return maximum value. */
+ return (0x7F ^ mask);
+ else {
+ aval = seg << SEG_SHIFT;
+ if (seg < 2)
+ aval |= (pcm_val >> 4) & QUANT_MASK;
+ else
+ aval |= (pcm_val >> (seg + 3)) & QUANT_MASK;
+ return (aval ^ mask);
+ }
+}
+
+/*
+ * alaw2linear() - Convert an A-law value to 16-bit linear PCM
+ *
+ */
+static int
+alaw2linear(
+ unsigned char a_val)
+{
+ int t;
+ int seg;
+
+ a_val ^= 0x55;
+
+ t = (a_val & QUANT_MASK) << 4;
+ seg = ((unsigned)a_val & SEG_MASK) >> SEG_SHIFT;
+ switch (seg) {
+ case 0:
+ t += 8;
+ break;
+ case 1:
+ t += 0x108;
+ break;
+ default:
+ t += 0x108;
+ t <<= seg - 1;
+ }
+ return ((a_val & SIGN_BIT) ? t : -t);
+}
+
+#define BIAS (0x84) /* Bias for linear code. */
+
+/*
+ * linear2ulaw() - Convert a linear PCM value to u-law
+ *
+ * In order to simplify the encoding process, the original linear magnitude
+ * is biased by adding 33 which shifts the encoding range from (0 - 8158) to
+ * (33 - 8191). The result can be seen in the following encoding table:
+ *
+ * Biased Linear Input Code Compressed Code
+ * ------------------------ ---------------
+ * 00000001wxyza 000wxyz
+ * 0000001wxyzab 001wxyz
+ * 000001wxyzabc 010wxyz
+ * 00001wxyzabcd 011wxyz
+ * 0001wxyzabcde 100wxyz
+ * 001wxyzabcdef 101wxyz
+ * 01wxyzabcdefg 110wxyz
+ * 1wxyzabcdefgh 111wxyz
+ *
+ * Each biased linear code has a leading 1 which identifies the segment
+ * number. The value of the segment number is equal to 7 minus the number
+ * of leading 0's. The quantization interval is directly available as the
+ * four bits wxyz. * The trailing bits (a - h) are ignored.
+ *
+ * Ordinarily the complement of the resulting code word is used for
+ * transmission, and so the code word is complemented before it is returned.
+ *
+ * For further information see John C. Bellamy's Digital Telephony, 1982,
+ * John Wiley & Sons, pps 98-111 and 472-476.
+ */
+static unsigned char
+linear2ulaw(
+ int pcm_val) /* 2's complement (16-bit range) */
+{
+ int mask;
+ int seg;
+ unsigned char uval;
+
+ /* Get the sign and the magnitude of the value. */
+ if (pcm_val < 0) {
+ pcm_val = BIAS - pcm_val;
+ mask = 0x7F;
+ } else {
+ pcm_val += BIAS;
+ mask = 0xFF;
+ }
+
+ /* Convert the scaled magnitude to segment number. */
+ seg = search(pcm_val, seg_end, 8);
+
+ /*
+ * Combine the sign, segment, quantization bits;
+ * and complement the code word.
+ */
+ if (seg >= 8) /* out of range, return maximum value. */
+ return (0x7F ^ mask);
+ else {
+ uval = (seg << 4) | ((pcm_val >> (seg + 3)) & 0xF);
+ return (uval ^ mask);
+ }
+
+}
+
+/*
+ * ulaw2linear() - Convert a u-law value to 16-bit linear PCM
+ *
+ * First, a biased linear code is derived from the code word. An unbiased
+ * output can then be obtained by subtracting 33 from the biased code.
+ *
+ * Note that this function expects to be passed the complement of the
+ * original code word. This is in keeping with ISDN conventions.
+ */
+static int
+ulaw2linear(
+ unsigned char u_val)
+{
+ int t;
+
+ /* Complement to obtain normal u-law value. */
+ u_val = ~u_val;
+
+ /*
+ * Extract and bias the quantization bits. Then
+ * shift up by the segment number and subtract out the bias.
+ */
+ t = ((u_val & QUANT_MASK) << 3) + BIAS;
+ t <<= ((unsigned)u_val & SEG_MASK) >> SEG_SHIFT;
+
+ return ((u_val & SIGN_BIT) ? (BIAS - t) : (t - BIAS));
+}
+
+/* A-law to u-law conversion */
+unsigned char
+alaw2ulaw(
+ unsigned char aval)
+{
+ aval &= 0xff;
+ return ((aval & 0x80) ? (0xFF ^ _a2u[aval ^ 0xD5]) :
+ (0x7F ^ _a2u[aval ^ 0x55]));
+}
+
+/* u-law to A-law conversion */
+unsigned char
+ulaw2alaw(
+ unsigned char uval)
+{
+ uval &= 0xff;
+ return ((uval & 0x80) ? (0xD5 ^ (_u2a[0xFF ^ uval] - 1)) :
+ (0x55 ^ (_u2a[0x7F ^ uval] - 1)));
+}
+
+
+
diff --git a/pjmedia/src/pjmedia/jbuf.c b/pjmedia/src/pjmedia/jbuf.c
index ebaa9159..b2e1b4da 100644
--- a/pjmedia/src/pjmedia/jbuf.c
+++ b/pjmedia/src/pjmedia/jbuf.c
@@ -1,420 +1,420 @@
-/* $Id$ */
-/*
- * Copyright (C) 2003-2006 Benny Prijono <benny@prijono.org>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
-#include <pjmedia/jbuf.h>
-#include <pj/log.h>
-#include <pj/pool.h>
-#include <string.h> /* memset() */
-
-/*
- * At the current state, this is basicly an ugly jitter buffer.
- * It worked before by observing level, bit it doesn't work.
- * Then I used the size, which makes the level obsolete.
- * That's why it's ugly!
- */
-
-#define MAX_SEQ_RANGE 1000 /* Range in which sequence is considered still within session */
-#define UPDATE_DURATION 20 /* Number of frames retrieved before jitter is updated */
-
-#define THIS_FILE "jbuf"
-
-/* Individual frame in the frame list. */
-struct pj_jbframe
-{
- pj_uint32_t extseq;
- void *buf;
-};
-
-
-/* Jitter buffer state. */
-typedef enum jb_state_t
-{
- JB_PREFETCH,
- JB_NORMAL,
-} jb_state_t;
-
-
-/* Jitter buffer last operation. */
-typedef enum jb_op_t
-{
- JB_PUT,
- JB_GET,
-} jb_op_t;
-
-
-/* Short name for convenience. */
-typedef struct pj_jitter_buffer JB;
-
-
-/* Initialize framelist. */
-static pj_status_t
-pj_framelist_init( pj_jbframelist *lst, pj_pool_t *pool, unsigned maxcount )
-{
- PJ_LOG(5, (THIS_FILE, "..pj_frame_list_init [lst=%p], maxcount=%d", lst, maxcount));
-
- memset(lst, 0, sizeof(*lst));
- lst->maxcount = maxcount;
- lst->frames = pj_pool_calloc( pool, maxcount, sizeof(*lst->frames) );
- if (lst->frames == NULL) {
- PJ_LOG(1,(THIS_FILE, "Unable to allocate frame list!"));
- return -1;
- }
- return 0;
-}
-
-/* Reset framelist. */
-static void
-pj_framelist_reset( pj_jbframelist *lst )
-{
- PJ_LOG(6, (THIS_FILE, "..pj_frame_list_reset [lst=%p]", lst));
-
- lst->count = 0;
- lst->head = 0;
- lst->frames[0].extseq = 0;
-}
-
-/* Put a buffer with the specified sequence into the ordered list. */
-static int
-pj_framelist_put( pj_jbframelist *lst, pj_uint32_t extseq, void *buf )
-{
- unsigned pos = (unsigned)-1;
- pj_uint32_t startseq = lst->frames[lst->head].extseq;
-
- if (lst->count == 0) {
- /* Empty list. Initialize frame list. */
- PJ_LOG(6, (THIS_FILE, " ..pj_frame_list_put [lst=%p], empty, seq=%u@pos=%d",
- lst, extseq, lst->head));
-
- lst->head = 0;
- lst->count = 1;
- lst->frames[0].buf = buf;
- lst->frames[0].extseq = extseq;
- return 0;
-
- } else if (extseq < startseq) {
- /* The sequence number is lower than our oldest packet. This can mean
- two things:
- - old packet has been receieved, or
- - the sequence number has wrapped around.
- */
- if (startseq + lst->maxcount <= extseq) {
- /* The sequence number has wrapped around, but it is beyond
- the capacity of the list (i.e. too soon).
- */
- PJ_LOG(5, (THIS_FILE, " ..pj_frame_list_put TOO_SOON! [lst=%p] seq=%u, startseq=%d",
- lst, extseq, startseq));
- return PJ_JB_STATUS_TOO_SOON;
-
- } else if (startseq-extseq > lst->maxcount && startseq+lst->maxcount > extseq) {
- /* The sequence number has wrapped around, and it is still inside
- the 'window' of the framelist.
- */
- pos = extseq - startseq;
- } else {
- /* The new frame is too old. */
- PJ_LOG(5, (THIS_FILE, " ..pj_frame_list_put TOO_OLD! [lst=%p] seq=%u, startseq=%d",
- lst, extseq, startseq));
- return PJ_JB_STATUS_TOO_OLD;
- }
-
- } else if (extseq > startseq + lst->maxcount) {
- /* Two possibilities here. Either:
- - packet is really too soon, or
- - sequence number of startseq has just wrapped around, and old packet
- which hasn't wrapped is received.
- */
- if (extseq < MAX_SEQ_RANGE /*approx 20 seconds with 50 fps*/) {
- PJ_LOG(5, (THIS_FILE, " ..pj_frame_list_put TOO_SOON! [lst=%p] seq=%u, startseq=%d",
- lst, extseq, startseq));
- return PJ_JB_STATUS_TOO_SOON;
- } else {
- PJ_LOG(5, (THIS_FILE, " ..pj_frame_list_put TOO_OLD! [lst=%p] seq=%u, startseq=%d",
- lst, extseq, startseq));
- return PJ_JB_STATUS_TOO_OLD;
- }
- }
-
- /* The new frame is within the framelist capacity.
- Calculate position where to put it in the list.
- */
- if (pos == (unsigned)-1)
- pos = ((extseq - startseq) + lst->head) % lst->maxcount;
-
- pj_assert(pos < lst->maxcount);
-
- /* Update count only if we're not overwriting existing frame. */
- if (lst->frames[pos].buf == NULL)
- ++lst->count;
-
- lst->frames[pos].buf = buf;
- lst->frames[pos].extseq = extseq;
-
- PJ_LOG(6, (THIS_FILE, " ..pj_frame_list_put [lst=%p] seq=%u, startseq=%d, head=%d, pos=%d",
- lst, extseq, startseq, lst->head, pos));
- return 0;
-}
-
-/* Get the first element of the list. */
-static int
-pj_framelist_get( pj_jbframelist *lst, pj_uint32_t *extseq, void **buf )
-{
- if (lst->count == 0) {
- /* Empty. */
- *buf = NULL;
- *extseq = 0;
- PJ_LOG(6, (THIS_FILE, " ..pj_frame_list_get [lst=%p], empty!", lst));
- return -1;
-
- } else {
- *buf = lst->frames[lst->head].buf;
- *extseq = lst->frames[lst->head].extseq;
- lst->frames[lst->head].buf = NULL;
-
- PJ_LOG(6, (THIS_FILE, " ..pj_frame_list_get [lst=%p] seq=%u, head=%d",
- lst, *extseq, lst->head));
-
- lst->head = (lst->head + 1) % lst->maxcount;
- --lst->count;
- return 0;
- }
-}
-
-
-/*****************************************************************************
- * Reset jitter buffer.
- ****************************************************************************
-*/
-PJ_DEF(void) pj_jb_reset(JB *jb)
-{
- PJ_LOG(6, (THIS_FILE, "pj_jb_reset [jb=%p]", jb));
-
- jb->level = jb->max_level = 1;
- jb->prefetch = jb->min;
- jb->get_cnt = 0;
- jb->lastseq = 0;
- jb->state = JB_PREFETCH;
- jb->upd_count = 0;
- jb->last_op = -1;
- pj_framelist_reset( &jb->lst );
-}
-
-
-/*****************************************************************************
- * Create jitter buffer.
- *****************************************************************************
- */
-PJ_DEF(pj_status_t) pj_jb_init( pj_jitter_buffer *jb, pj_pool_t *pool,
- unsigned min, unsigned max, unsigned maxcount)
-{
- pj_status_t status;
-
- if (maxcount <= max) {
- maxcount = max * 5 / 4;
- PJ_LOG(3,(THIS_FILE, "Jitter buffer maximum count was adjusted."));
- }
-
- jb->min = min;
- jb->max = max;
-
- status = pj_framelist_init( &jb->lst, pool, maxcount );
- if (status != PJ_SUCCESS)
- return status;
-
- pj_jb_reset(jb);
-
- PJ_LOG(4, (THIS_FILE, "pj_jb_init success [jb=%p], min=%d, max=%d, maxcount=%d",
- jb, min, max, maxcount));
- return PJ_SUCCESS;
-}
-
-
-/*****************************************************************************
- * Put a packet to the jitter buffer.
- *****************************************************************************
- */
-PJ_DEF(pj_status_t) pj_jb_put( JB *jb, pj_uint32_t extseq, void *buf )
-{
- unsigned distance;
- int status;
-
- PJ_LOG(6, (THIS_FILE, "==> pj_jb_put [jb=%p], seq=%u, buf=%p", jb, extseq, buf));
-
- if (jb->lastseq == 0)
- jb->lastseq = extseq - 1;
-
- /* Calculate distance between this packet and last received packet
- to detect long jump (indicating probably remote has just been
- restarted.
- */
- distance = (extseq > jb->lastseq) ? extseq - jb->lastseq : jb->lastseq - extseq;
- if (distance > MAX_SEQ_RANGE) {
- /* Distance is out of range, reset jitter while maintaining current jitter
- level.
- */
- int old_level = jb->level;
- int old_prefetch = jb->prefetch;
-
- PJ_LOG(4, (THIS_FILE, " ..[jb=%p] distance out of range, resetting", jb));
-
- pj_jb_reset(jb);
- jb->level = old_level;
- jb->prefetch = old_prefetch;
- distance = 1;
- jb->lastseq = extseq - 1;
- }
-
- jb->lastseq = extseq;
-
- status = pj_framelist_put( &jb->lst, extseq, buf );
- if (status == PJ_JB_STATUS_TOO_OLD)
- return -1;
-
- if (status == PJ_JB_STATUS_TOO_SOON) {
- /* TODO: discard old packets.. */
- /* No, don't do it without putting a way to inform application so that
- it can free the memory */
- }
-
-
- if (jb->last_op != JB_PUT) {
- if (jb->state != JB_PREFETCH)
- jb->level--;
- } else {
- jb->level++;
- }
-
- if (jb->lst.count > jb->max_level)
- jb->max_level++;
-
- jb->last_op = JB_PUT;
- return 0;
-}
-
-
-/*
- * Update jitter buffer algorithm.
- */
-static void jb_update(JB *jb, int apply, int log_info)
-{
- unsigned abs_level = jb->max_level > 0 ? jb->max_level : -jb->max_level;
- unsigned new_prefetch;
-
- /* Update prefetch count */
- if (abs_level > jb->prefetch)
- new_prefetch = (jb->prefetch + abs_level*9 + 1) / 10;
- else {
- new_prefetch = (jb->prefetch*4 + abs_level) / 5;
- pj_assert(new_prefetch <= jb->prefetch);
- }
-
- if (log_info) {
- PJ_LOG(5, (THIS_FILE, " ..jb_update [jb=%p], level=%d, max_level=%d, old_prefetch=%d, new_prefetch=%d",
- jb, jb->level, jb->max_level, jb->prefetch, new_prefetch));
- } else {
- PJ_LOG(6, (THIS_FILE, " ..jb_update [jb=%p], level=%d, max_level=%d, old_prefetch=%d, new_prefetch=%d",
- jb, jb->level, jb->max_level, jb->prefetch, new_prefetch));
- }
-
- if (new_prefetch < jb->min) new_prefetch = jb->min;
- if (new_prefetch > jb->max) new_prefetch = jb->max;
-
- /* If jitter buffer is empty, set state to JB_PREFETCH, taking care of the
- new prefetch setting.
- */
- if (jb->lst.count == 0) {
- jb->state = JB_PREFETCH;
- jb->get_cnt = 0;
- } else {
- /* Check if delay is too long, which in this case probably better to
- discard some frames..
- */
- /* No, don't do it without putting a way to inform application so that
- it can free the memory */
- }
-
-
- if (apply) {
- jb->prefetch = new_prefetch;
- if (jb->max_level > 0)
- jb->max_level--;
- } else {
- jb->level = new_prefetch;
- }
-}
-
-
-/*****************************************************************************
- * Get the oldest frame from jitter buffer.
- *****************************************************************************
- */
-PJ_DEF(pj_status_t) pj_jb_get( JB *jb, pj_uint32_t *extseq, void **buf )
-{
- pj_status_t status;
-
- PJ_LOG(6, (THIS_FILE, "<== pj_jb_get [jb=%p]", jb));
-
- /*
- * Check whether we're ready to give frame. When we're in JB_PREFETCH state,
- * only give frames only when:
- * - the buffer has enough frames in it (jb->list.count > jb->prefetch), OR
- * - after 'prefetch' attempts, there's still no frame, which in this
- * case PJ_JB_STATUS_FRAME_NULL will be returned by the next check.
- */
- if (jb->state == JB_PREFETCH && jb->lst.count <= jb->prefetch && jb->get_cnt < jb->prefetch) {
- jb->get_cnt++;
- jb->last_op = JB_GET;
- PJ_LOG(5, (THIS_FILE, " ..[jb=%p] bufferring...", jb));
- return PJ_JB_STATUS_FRAME_NULL;
- }
-
- /* Attempt to get one frame from the list. */
- status = pj_framelist_get( &jb->lst, extseq, buf );
- if (status != 0) {
- PJ_LOG(6, (THIS_FILE, " ..[jb=%p] no packet!", jb));
- status = jb->lst.count ? PJ_JB_STATUS_FRAME_MISSING : PJ_JB_STATUS_FRAME_NULL;
- jb_update(jb, 1, 0);
- return status;
- }
-
- /* Force state to NORMAL */
- jb->state = JB_NORMAL;
-
- /* Increase level only when last operation is GET.
- * This is to prevent level from increasing during silence period, which
- * no packets is receieved.
- */
- if (jb->last_op != JB_GET) {
- int apply;
-
- //jb->level++;
- jb->last_op = JB_GET;
-
- apply = (++jb->upd_count > UPDATE_DURATION);
- if (apply)
- jb->upd_count = 0;
-
- jb_update(jb, apply, apply);
- }
-
- PJ_LOG(6, (THIS_FILE, " ..[jb=%p] seq=%u, level=%d, prefetch=%d, size=%u, delay=%d",
- jb, *extseq, jb->level, jb->prefetch, jb->lst.count,
- jb->lastseq - *extseq));
- return 0;
-}
-
-
+/* $Id$ */
+/*
+ * Copyright (C) 2003-2006 Benny Prijono <benny@prijono.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#include <pjmedia/jbuf.h>
+#include <pj/log.h>
+#include <pj/pool.h>
+#include <string.h> /* memset() */
+
+/*
+ * At the current state, this is basicly an ugly jitter buffer.
+ * It worked before by observing level, bit it doesn't work.
+ * Then I used the size, which makes the level obsolete.
+ * That's why it's ugly!
+ */
+
+#define MAX_SEQ_RANGE 1000 /* Range in which sequence is considered still within session */
+#define UPDATE_DURATION 20 /* Number of frames retrieved before jitter is updated */
+
+#define THIS_FILE "jbuf"
+
+/* Individual frame in the frame list. */
+struct pj_jbframe
+{
+ pj_uint32_t extseq;
+ void *buf;
+};
+
+
+/* Jitter buffer state. */
+typedef enum jb_state_t
+{
+ JB_PREFETCH,
+ JB_NORMAL,
+} jb_state_t;
+
+
+/* Jitter buffer last operation. */
+typedef enum jb_op_t
+{
+ JB_PUT,
+ JB_GET,
+} jb_op_t;
+
+
+/* Short name for convenience. */
+typedef struct pj_jitter_buffer JB;
+
+
+/* Initialize framelist. */
+static pj_status_t
+pj_framelist_init( pj_jbframelist *lst, pj_pool_t *pool, unsigned maxcount )
+{
+ PJ_LOG(5, (THIS_FILE, "..pj_frame_list_init [lst=%p], maxcount=%d", lst, maxcount));
+
+ memset(lst, 0, sizeof(*lst));
+ lst->maxcount = maxcount;
+ lst->frames = pj_pool_calloc( pool, maxcount, sizeof(*lst->frames) );
+ if (lst->frames == NULL) {
+ PJ_LOG(1,(THIS_FILE, "Unable to allocate frame list!"));
+ return -1;
+ }
+ return 0;
+}
+
+/* Reset framelist. */
+static void
+pj_framelist_reset( pj_jbframelist *lst )
+{
+ PJ_LOG(6, (THIS_FILE, "..pj_frame_list_reset [lst=%p]", lst));
+
+ lst->count = 0;
+ lst->head = 0;
+ lst->frames[0].extseq = 0;
+}
+
+/* Put a buffer with the specified sequence into the ordered list. */
+static int
+pj_framelist_put( pj_jbframelist *lst, pj_uint32_t extseq, void *buf )
+{
+ unsigned pos = (unsigned)-1;
+ pj_uint32_t startseq = lst->frames[lst->head].extseq;
+
+ if (lst->count == 0) {
+ /* Empty list. Initialize frame list. */
+ PJ_LOG(6, (THIS_FILE, " ..pj_frame_list_put [lst=%p], empty, seq=%u@pos=%d",
+ lst, extseq, lst->head));
+
+ lst->head = 0;
+ lst->count = 1;
+ lst->frames[0].buf = buf;
+ lst->frames[0].extseq = extseq;
+ return 0;
+
+ } else if (extseq < startseq) {
+ /* The sequence number is lower than our oldest packet. This can mean
+ two things:
+ - old packet has been receieved, or
+ - the sequence number has wrapped around.
+ */
+ if (startseq + lst->maxcount <= extseq) {
+ /* The sequence number has wrapped around, but it is beyond
+ the capacity of the list (i.e. too soon).
+ */
+ PJ_LOG(5, (THIS_FILE, " ..pj_frame_list_put TOO_SOON! [lst=%p] seq=%u, startseq=%d",
+ lst, extseq, startseq));
+ return PJ_JB_STATUS_TOO_SOON;
+
+ } else if (startseq-extseq > lst->maxcount && startseq+lst->maxcount > extseq) {
+ /* The sequence number has wrapped around, and it is still inside
+ the 'window' of the framelist.
+ */
+ pos = extseq - startseq;
+ } else {
+ /* The new frame is too old. */
+ PJ_LOG(5, (THIS_FILE, " ..pj_frame_list_put TOO_OLD! [lst=%p] seq=%u, startseq=%d",
+ lst, extseq, startseq));
+ return PJ_JB_STATUS_TOO_OLD;
+ }
+
+ } else if (extseq > startseq + lst->maxcount) {
+ /* Two possibilities here. Either:
+ - packet is really too soon, or
+ - sequence number of startseq has just wrapped around, and old packet
+ which hasn't wrapped is received.
+ */
+ if (extseq < MAX_SEQ_RANGE /*approx 20 seconds with 50 fps*/) {
+ PJ_LOG(5, (THIS_FILE, " ..pj_frame_list_put TOO_SOON! [lst=%p] seq=%u, startseq=%d",
+ lst, extseq, startseq));
+ return PJ_JB_STATUS_TOO_SOON;
+ } else {
+ PJ_LOG(5, (THIS_FILE, " ..pj_frame_list_put TOO_OLD! [lst=%p] seq=%u, startseq=%d",
+ lst, extseq, startseq));
+ return PJ_JB_STATUS_TOO_OLD;
+ }
+ }
+
+ /* The new frame is within the framelist capacity.
+ Calculate position where to put it in the list.
+ */
+ if (pos == (unsigned)-1)
+ pos = ((extseq - startseq) + lst->head) % lst->maxcount;
+
+ pj_assert(pos < lst->maxcount);
+
+ /* Update count only if we're not overwriting existing frame. */
+ if (lst->frames[pos].buf == NULL)
+ ++lst->count;
+
+ lst->frames[pos].buf = buf;
+ lst->frames[pos].extseq = extseq;
+
+ PJ_LOG(6, (THIS_FILE, " ..pj_frame_list_put [lst=%p] seq=%u, startseq=%d, head=%d, pos=%d",
+ lst, extseq, startseq, lst->head, pos));
+ return 0;
+}
+
+/* Get the first element of the list. */
+static int
+pj_framelist_get( pj_jbframelist *lst, pj_uint32_t *extseq, void **buf )
+{
+ if (lst->count == 0) {
+ /* Empty. */
+ *buf = NULL;
+ *extseq = 0;
+ PJ_LOG(6, (THIS_FILE, " ..pj_frame_list_get [lst=%p], empty!", lst));
+ return -1;
+
+ } else {
+ *buf = lst->frames[lst->head].buf;
+ *extseq = lst->frames[lst->head].extseq;
+ lst->frames[lst->head].buf = NULL;
+
+ PJ_LOG(6, (THIS_FILE, " ..pj_frame_list_get [lst=%p] seq=%u, head=%d",
+ lst, *extseq, lst->head));
+
+ lst->head = (lst->head + 1) % lst->maxcount;
+ --lst->count;
+ return 0;
+ }
+}
+
+
+/*****************************************************************************
+ * Reset jitter buffer.
+ ****************************************************************************
+*/
+PJ_DEF(void) pj_jb_reset(JB *jb)
+{
+ PJ_LOG(6, (THIS_FILE, "pj_jb_reset [jb=%p]", jb));
+
+ jb->level = jb->max_level = 1;
+ jb->prefetch = jb->min;
+ jb->get_cnt = 0;
+ jb->lastseq = 0;
+ jb->state = JB_PREFETCH;
+ jb->upd_count = 0;
+ jb->last_op = -1;
+ pj_framelist_reset( &jb->lst );
+}
+
+
+/*****************************************************************************
+ * Create jitter buffer.
+ *****************************************************************************
+ */
+PJ_DEF(pj_status_t) pj_jb_init( pj_jitter_buffer *jb, pj_pool_t *pool,
+ unsigned min, unsigned max, unsigned maxcount)
+{
+ pj_status_t status;
+
+ if (maxcount <= max) {
+ maxcount = max * 5 / 4;
+ PJ_LOG(3,(THIS_FILE, "Jitter buffer maximum count was adjusted."));
+ }
+
+ jb->min = min;
+ jb->max = max;
+
+ status = pj_framelist_init( &jb->lst, pool, maxcount );
+ if (status != PJ_SUCCESS)
+ return status;
+
+ pj_jb_reset(jb);
+
+ PJ_LOG(4, (THIS_FILE, "pj_jb_init success [jb=%p], min=%d, max=%d, maxcount=%d",
+ jb, min, max, maxcount));
+ return PJ_SUCCESS;
+}
+
+
+/*****************************************************************************
+ * Put a packet to the jitter buffer.
+ *****************************************************************************
+ */
+PJ_DEF(pj_status_t) pj_jb_put( JB *jb, pj_uint32_t extseq, void *buf )
+{
+ unsigned distance;
+ int status;
+
+ PJ_LOG(6, (THIS_FILE, "==> pj_jb_put [jb=%p], seq=%u, buf=%p", jb, extseq, buf));
+
+ if (jb->lastseq == 0)
+ jb->lastseq = extseq - 1;
+
+ /* Calculate distance between this packet and last received packet
+ to detect long jump (indicating probably remote has just been
+ restarted.
+ */
+ distance = (extseq > jb->lastseq) ? extseq - jb->lastseq : jb->lastseq - extseq;
+ if (distance > MAX_SEQ_RANGE) {
+ /* Distance is out of range, reset jitter while maintaining current jitter
+ level.
+ */
+ int old_level = jb->level;
+ int old_prefetch = jb->prefetch;
+
+ PJ_LOG(4, (THIS_FILE, " ..[jb=%p] distance out of range, resetting", jb));
+
+ pj_jb_reset(jb);
+ jb->level = old_level;
+ jb->prefetch = old_prefetch;
+ distance = 1;
+ jb->lastseq = extseq - 1;
+ }
+
+ jb->lastseq = extseq;
+
+ status = pj_framelist_put( &jb->lst, extseq, buf );
+ if (status == PJ_JB_STATUS_TOO_OLD)
+ return -1;
+
+ if (status == PJ_JB_STATUS_TOO_SOON) {
+ /* TODO: discard old packets.. */
+ /* No, don't do it without putting a way to inform application so that
+ it can free the memory */
+ }
+
+
+ if (jb->last_op != JB_PUT) {
+ if (jb->state != JB_PREFETCH)
+ jb->level--;
+ } else {
+ jb->level++;
+ }
+
+ if (jb->lst.count > jb->max_level)
+ jb->max_level++;
+
+ jb->last_op = JB_PUT;
+ return 0;
+}
+
+
+/*
+ * Update jitter buffer algorithm.
+ */
+static void jb_update(JB *jb, int apply, int log_info)
+{
+ unsigned abs_level = jb->max_level > 0 ? jb->max_level : -jb->max_level;
+ unsigned new_prefetch;
+
+ /* Update prefetch count */
+ if (abs_level > jb->prefetch)
+ new_prefetch = (jb->prefetch + abs_level*9 + 1) / 10;
+ else {
+ new_prefetch = (jb->prefetch*4 + abs_level) / 5;
+ pj_assert(new_prefetch <= jb->prefetch);
+ }
+
+ if (log_info) {
+ PJ_LOG(5, (THIS_FILE, " ..jb_update [jb=%p], level=%d, max_level=%d, old_prefetch=%d, new_prefetch=%d",
+ jb, jb->level, jb->max_level, jb->prefetch, new_prefetch));
+ } else {
+ PJ_LOG(6, (THIS_FILE, " ..jb_update [jb=%p], level=%d, max_level=%d, old_prefetch=%d, new_prefetch=%d",
+ jb, jb->level, jb->max_level, jb->prefetch, new_prefetch));
+ }
+
+ if (new_prefetch < jb->min) new_prefetch = jb->min;
+ if (new_prefetch > jb->max) new_prefetch = jb->max;
+
+ /* If jitter buffer is empty, set state to JB_PREFETCH, taking care of the
+ new prefetch setting.
+ */
+ if (jb->lst.count == 0) {
+ jb->state = JB_PREFETCH;
+ jb->get_cnt = 0;
+ } else {
+ /* Check if delay is too long, which in this case probably better to
+ discard some frames..
+ */
+ /* No, don't do it without putting a way to inform application so that
+ it can free the memory */
+ }
+
+
+ if (apply) {
+ jb->prefetch = new_prefetch;
+ if (jb->max_level > 0)
+ jb->max_level--;
+ } else {
+ jb->level = new_prefetch;
+ }
+}
+
+
+/*****************************************************************************
+ * Get the oldest frame from jitter buffer.
+ *****************************************************************************
+ */
+PJ_DEF(pj_status_t) pj_jb_get( JB *jb, pj_uint32_t *extseq, void **buf )
+{
+ pj_status_t status;
+
+ PJ_LOG(6, (THIS_FILE, "<== pj_jb_get [jb=%p]", jb));
+
+ /*
+ * Check whether we're ready to give frame. When we're in JB_PREFETCH state,
+ * only give frames only when:
+ * - the buffer has enough frames in it (jb->list.count > jb->prefetch), OR
+ * - after 'prefetch' attempts, there's still no frame, which in this
+ * case PJ_JB_STATUS_FRAME_NULL will be returned by the next check.
+ */
+ if (jb->state == JB_PREFETCH && jb->lst.count <= jb->prefetch && jb->get_cnt < jb->prefetch) {
+ jb->get_cnt++;
+ jb->last_op = JB_GET;
+ PJ_LOG(5, (THIS_FILE, " ..[jb=%p] bufferring...", jb));
+ return PJ_JB_STATUS_FRAME_NULL;
+ }
+
+ /* Attempt to get one frame from the list. */
+ status = pj_framelist_get( &jb->lst, extseq, buf );
+ if (status != 0) {
+ PJ_LOG(6, (THIS_FILE, " ..[jb=%p] no packet!", jb));
+ status = jb->lst.count ? PJ_JB_STATUS_FRAME_MISSING : PJ_JB_STATUS_FRAME_NULL;
+ jb_update(jb, 1, 0);
+ return status;
+ }
+
+ /* Force state to NORMAL */
+ jb->state = JB_NORMAL;
+
+ /* Increase level only when last operation is GET.
+ * This is to prevent level from increasing during silence period, which
+ * no packets is receieved.
+ */
+ if (jb->last_op != JB_GET) {
+ int apply;
+
+ //jb->level++;
+ jb->last_op = JB_GET;
+
+ apply = (++jb->upd_count > UPDATE_DURATION);
+ if (apply)
+ jb->upd_count = 0;
+
+ jb_update(jb, apply, apply);
+ }
+
+ PJ_LOG(6, (THIS_FILE, " ..[jb=%p] seq=%u, level=%d, prefetch=%d, size=%u, delay=%d",
+ jb, *extseq, jb->level, jb->prefetch, jb->lst.count,
+ jb->lastseq - *extseq));
+ return 0;
+}
+
+
diff --git a/pjmedia/src/pjmedia/jbuf.h b/pjmedia/src/pjmedia/jbuf.h
index a36137ee..8ad89f38 100644
--- a/pjmedia/src/pjmedia/jbuf.h
+++ b/pjmedia/src/pjmedia/jbuf.h
@@ -1,142 +1,142 @@
-/* $Id$ */
-/*
- * Copyright (C) 2003-2006 Benny Prijono <benny@prijono.org>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
-#ifndef __PJMEDIA_JBUF_H__
-#define __PJMEDIA_JBUF_H__
-
-
-/**
- * @file jbuf.h
- * @brief Adaptive jitter buffer implementation.
- */
-/**
- * @defgroup PJMED_JBUF Adaptive jitter buffer
- * @ingroup PJMEDIA
- * @{
- */
-
-#include <pj/types.h>
-
-
-PJ_BEGIN_DECL
-
-
-/**
- * Opaque declaration of internal frame type used by jitter buffer.
- */
-struct pj_jbframe;
-
-
-/**
- * Miscelaneous operation result/status.
- */
-typedef enum jb_op_status
-{
- PJ_JB_STATUS_TOO_OLD = -2, /** The packet is too old. */
- PJ_JB_STATUS_TOO_SOON = -3, /** The packet is too soon. */
- PJ_JB_STATUS_FRAME_NULL = -4, /** No packet can be retrieved */
- PJ_JB_STATUS_FRAME_MISSING = -5, /** The specified packet is missing/lost */
-} jb_op_status;
-
-
-/*
- * Frame list, container abstraction for ordered list with fixed maximum
- * size. It is used internally by the jitter buffer.
- */
-typedef struct pj_jbframelist
-{
- unsigned head, count, maxcount;
- struct pj_jbframe *frames;
-} pj_jbframelist;
-
-
-/**
- * Jitter buffer implementation.
- */
-typedef struct pj_jitter_buffer
-{
- pj_jbframelist lst; /** The frame list. */
- int level; /** Current, real-time jitter level. */
- int max_level; /** Maximum level for the period. */
- unsigned prefetch; /** Prefetch count currently used. */
- unsigned get_cnt; /** Number of get operation during prefetch state. */
- unsigned min; /** Minimum jitter size, in packets. */
- unsigned max; /** Maximum jitter size, in packets. */
- pj_uint32_t lastseq; /** Last sequence number put to jitter buffer. */
- unsigned upd_count; /** Internal counter to manage update interval. */
- int state; /** Jitter buffer state (1==operational) */
- int last_op; /** Last jitter buffer operation. */
-} pj_jitter_buffer;
-
-
-/**
- * Initialize jitter buffer with the specified parameters.
- * This function will allocate internal frame buffer from the specified pool.
- * @param jb The jitter buffer to be initialized.
- * @param pool Pool where memory will be allocated for the frame buffer.
- * @param min The minimum value of jitter buffer, in packets.
- * @param max The maximum value of jitter buffer, in packets.
- * @param maxcount The maximum number of delay, in packets, which must be
- * greater than max.
- * @return PJ_SUCCESS on success.
- */
-PJ_DECL(pj_status_t) pj_jb_init(pj_jitter_buffer *jb, pj_pool_t *pool,
- unsigned min, unsigned max, unsigned maxcount);
-
-/**
- * Reset jitter buffer according to the parameters specified when the jitter
- * buffer was initialized. Any packets currently in the buffer will be
- * discarded.
- */
-PJ_DECL(void) pj_jb_reset(pj_jitter_buffer *jb);
-
-/**
- * Put a pointer to the buffer with the specified sequence number. The pointer
- * normally points to a buffer held by application, and this pointer will be
- * returned later on when pj_jb_get() is called. The jitter buffer will not try
- * to interpret the content of this pointer.
- * @return:
- * - PJ_SUCCESS on success.
- * - PJ_JB_STATUS_TOO_OLD when the packet is too old.
- * - PJ_JB_STATUS_TOO_SOON when the packet is too soon.
- */
-PJ_DECL(pj_status_t) pj_jb_put( pj_jitter_buffer *jb, pj_uint32_t extseq, void *buf );
-
-/**
- * Get the earliest data from the jitter buffer. ONLY when the operation succeeds,
- * the function returns both sequence number and a pointer in the parameters.
- * This returned data corresponds to sequence number and pointer that were
- * given to jitter buffer in the previous pj_jb_put operation.
- * @return
- * - PJ_SUCCESS on success
- * - PJ_JB_STATUS_FRAME_NULL when there is no frames to be returned.
- * - PJ_JB_STATUS_FRAME_MISSING if the jitter buffer detects that the packet
- * is lost. Application may run packet concealment algorithm when it
- * receives this status.
- */
-PJ_DECL(pj_status_t) pj_jb_get( pj_jitter_buffer *jb, pj_uint32_t *extseq, void **buf );
-
-
-
-PJ_END_DECL
-
-/**
- * @}
- */
-
-#endif /* __PJMEDIA_JBUF_H__ */
+/* $Id$ */
+/*
+ * Copyright (C) 2003-2006 Benny Prijono <benny@prijono.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#ifndef __PJMEDIA_JBUF_H__
+#define __PJMEDIA_JBUF_H__
+
+
+/**
+ * @file jbuf.h
+ * @brief Adaptive jitter buffer implementation.
+ */
+/**
+ * @defgroup PJMED_JBUF Adaptive jitter buffer
+ * @ingroup PJMEDIA
+ * @{
+ */
+
+#include <pj/types.h>
+
+
+PJ_BEGIN_DECL
+
+
+/**
+ * Opaque declaration of internal frame type used by jitter buffer.
+ */
+struct pj_jbframe;
+
+
+/**
+ * Miscelaneous operation result/status.
+ */
+typedef enum jb_op_status
+{
+ PJ_JB_STATUS_TOO_OLD = -2, /** The packet is too old. */
+ PJ_JB_STATUS_TOO_SOON = -3, /** The packet is too soon. */
+ PJ_JB_STATUS_FRAME_NULL = -4, /** No packet can be retrieved */
+ PJ_JB_STATUS_FRAME_MISSING = -5, /** The specified packet is missing/lost */
+} jb_op_status;
+
+
+/*
+ * Frame list, container abstraction for ordered list with fixed maximum
+ * size. It is used internally by the jitter buffer.
+ */
+typedef struct pj_jbframelist
+{
+ unsigned head, count, maxcount;
+ struct pj_jbframe *frames;
+} pj_jbframelist;
+
+
+/**
+ * Jitter buffer implementation.
+ */
+typedef struct pj_jitter_buffer
+{
+ pj_jbframelist lst; /** The frame list. */
+ int level; /** Current, real-time jitter level. */
+ int max_level; /** Maximum level for the period. */
+ unsigned prefetch; /** Prefetch count currently used. */
+ unsigned get_cnt; /** Number of get operation during prefetch state. */
+ unsigned min; /** Minimum jitter size, in packets. */
+ unsigned max; /** Maximum jitter size, in packets. */
+ pj_uint32_t lastseq; /** Last sequence number put to jitter buffer. */
+ unsigned upd_count; /** Internal counter to manage update interval. */
+ int state; /** Jitter buffer state (1==operational) */
+ int last_op; /** Last jitter buffer operation. */
+} pj_jitter_buffer;
+
+
+/**
+ * Initialize jitter buffer with the specified parameters.
+ * This function will allocate internal frame buffer from the specified pool.
+ * @param jb The jitter buffer to be initialized.
+ * @param pool Pool where memory will be allocated for the frame buffer.
+ * @param min The minimum value of jitter buffer, in packets.
+ * @param max The maximum value of jitter buffer, in packets.
+ * @param maxcount The maximum number of delay, in packets, which must be
+ * greater than max.
+ * @return PJ_SUCCESS on success.
+ */
+PJ_DECL(pj_status_t) pj_jb_init(pj_jitter_buffer *jb, pj_pool_t *pool,
+ unsigned min, unsigned max, unsigned maxcount);
+
+/**
+ * Reset jitter buffer according to the parameters specified when the jitter
+ * buffer was initialized. Any packets currently in the buffer will be
+ * discarded.
+ */
+PJ_DECL(void) pj_jb_reset(pj_jitter_buffer *jb);
+
+/**
+ * Put a pointer to the buffer with the specified sequence number. The pointer
+ * normally points to a buffer held by application, and this pointer will be
+ * returned later on when pj_jb_get() is called. The jitter buffer will not try
+ * to interpret the content of this pointer.
+ * @return:
+ * - PJ_SUCCESS on success.
+ * - PJ_JB_STATUS_TOO_OLD when the packet is too old.
+ * - PJ_JB_STATUS_TOO_SOON when the packet is too soon.
+ */
+PJ_DECL(pj_status_t) pj_jb_put( pj_jitter_buffer *jb, pj_uint32_t extseq, void *buf );
+
+/**
+ * Get the earliest data from the jitter buffer. ONLY when the operation succeeds,
+ * the function returns both sequence number and a pointer in the parameters.
+ * This returned data corresponds to sequence number and pointer that were
+ * given to jitter buffer in the previous pj_jb_put operation.
+ * @return
+ * - PJ_SUCCESS on success
+ * - PJ_JB_STATUS_FRAME_NULL when there is no frames to be returned.
+ * - PJ_JB_STATUS_FRAME_MISSING if the jitter buffer detects that the packet
+ * is lost. Application may run packet concealment algorithm when it
+ * receives this status.
+ */
+PJ_DECL(pj_status_t) pj_jb_get( pj_jitter_buffer *jb, pj_uint32_t *extseq, void **buf );
+
+
+
+PJ_END_DECL
+
+/**
+ * @}
+ */
+
+#endif /* __PJMEDIA_JBUF_H__ */
diff --git a/pjmedia/src/pjmedia/mediamgr.c b/pjmedia/src/pjmedia/mediamgr.c
index 80eefb44..e43e742d 100644
--- a/pjmedia/src/pjmedia/mediamgr.c
+++ b/pjmedia/src/pjmedia/mediamgr.c
@@ -1,112 +1,112 @@
-/* $Id$ */
-/*
- * Copyright (C) 2003-2006 Benny Prijono <benny@prijono.org>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
-#include <pjmedia/mediamgr.h>
-#include <pj/sock.h>
-#include <pj/pool.h>
-#include <pj/string.h>
-
-PJ_DECL(pj_status_t) g711_init_factory (pj_codec_factory *factory, pj_pool_t *pool);
-PJ_DECL(pj_status_t) g711_deinit_factory (pj_codec_factory *factory);
-
-/** Concrete declaration of media manager. */
-struct pj_med_mgr_t
-{
- /** Pool. */
- pj_pool_t *pool;
-
- /** Pool factory. */
- pj_pool_factory *pf;
-
- /** Codec manager. */
- pj_codec_mgr codec_mgr;
-};
-
-/**
- * Initialize and get the instance of media manager.
- */
-PJ_DEF(pj_med_mgr_t*) pj_med_mgr_create ( pj_pool_factory *pf)
-{
- pj_pool_t *pool;
- pj_med_mgr_t *mm;
- pj_codec_factory *cf;
- pj_status_t status;
-
- pool = pj_pool_create(pf, "mediamgr", 512, 512, NULL);
- if (!pool)
- return NULL;
-
- mm = pj_pool_calloc(pool, 1, sizeof(struct pj_med_mgr_t));
- mm->pool = pool;
- mm->pf = pf;
-
- /* Sound */
- pj_snd_init(pf);
-
- /* Init codec manager. */
- status = pj_codec_mgr_init(&mm->codec_mgr);
- if (status != 0) {
- pj_snd_deinit();
- goto on_error;
- }
-
- /* Init and register G.711 codec. */
- cf = pj_pool_alloc (mm->pool, sizeof(pj_codec_factory));
-
- status = g711_init_factory (cf, mm->pool);
- if (status != 0) {
- pj_snd_deinit();
- return NULL;
- }
-
- status = pj_codec_mgr_register_factory (&mm->codec_mgr, cf);
- if (status != 0)
- return NULL;
-
- return mm;
-
-on_error:
- pj_pool_release(pool);
- return NULL;
-}
-
-/**
- * Get the codec manager instance.
- */
-PJ_DEF(pj_codec_mgr*) pj_med_mgr_get_codec_mgr (pj_med_mgr_t *mgr)
-{
- return &mgr->codec_mgr;
-}
-
-/**
- * Deinitialize media manager.
- */
-PJ_DEF(pj_status_t) pj_med_mgr_destroy (pj_med_mgr_t *mgr)
-{
- pj_snd_deinit();
- pj_pool_release (mgr->pool);
- return 0;
-}
-
-/**
- * Get pool factory.
- */
-PJ_DEF(pj_pool_factory*) pj_med_mgr_get_pool_factory (pj_med_mgr_t *mgr)
-{
- return mgr->pf;
-}
+/* $Id$ */
+/*
+ * Copyright (C) 2003-2006 Benny Prijono <benny@prijono.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#include <pjmedia/mediamgr.h>
+#include <pj/sock.h>
+#include <pj/pool.h>
+#include <pj/string.h>
+
+PJ_DECL(pj_status_t) g711_init_factory (pj_codec_factory *factory, pj_pool_t *pool);
+PJ_DECL(pj_status_t) g711_deinit_factory (pj_codec_factory *factory);
+
+/** Concrete declaration of media manager. */
+struct pj_med_mgr_t
+{
+ /** Pool. */
+ pj_pool_t *pool;
+
+ /** Pool factory. */
+ pj_pool_factory *pf;
+
+ /** Codec manager. */
+ pj_codec_mgr codec_mgr;
+};
+
+/**
+ * Initialize and get the instance of media manager.
+ */
+PJ_DEF(pj_med_mgr_t*) pj_med_mgr_create ( pj_pool_factory *pf)
+{
+ pj_pool_t *pool;
+ pj_med_mgr_t *mm;
+ pj_codec_factory *cf;
+ pj_status_t status;
+
+ pool = pj_pool_create(pf, "mediamgr", 512, 512, NULL);
+ if (!pool)
+ return NULL;
+
+ mm = pj_pool_calloc(pool, 1, sizeof(struct pj_med_mgr_t));
+ mm->pool = pool;
+ mm->pf = pf;
+
+ /* Sound */
+ pj_snd_init(pf);
+
+ /* Init codec manager. */
+ status = pj_codec_mgr_init(&mm->codec_mgr);
+ if (status != 0) {
+ pj_snd_deinit();
+ goto on_error;
+ }
+
+ /* Init and register G.711 codec. */
+ cf = pj_pool_alloc (mm->pool, sizeof(pj_codec_factory));
+
+ status = g711_init_factory (cf, mm->pool);
+ if (status != 0) {
+ pj_snd_deinit();
+ return NULL;
+ }
+
+ status = pj_codec_mgr_register_factory (&mm->codec_mgr, cf);
+ if (status != 0)
+ return NULL;
+
+ return mm;
+
+on_error:
+ pj_pool_release(pool);
+ return NULL;
+}
+
+/**
+ * Get the codec manager instance.
+ */
+PJ_DEF(pj_codec_mgr*) pj_med_mgr_get_codec_mgr (pj_med_mgr_t *mgr)
+{
+ return &mgr->codec_mgr;
+}
+
+/**
+ * Deinitialize media manager.
+ */
+PJ_DEF(pj_status_t) pj_med_mgr_destroy (pj_med_mgr_t *mgr)
+{
+ pj_snd_deinit();
+ pj_pool_release (mgr->pool);
+ return 0;
+}
+
+/**
+ * Get pool factory.
+ */
+PJ_DEF(pj_pool_factory*) pj_med_mgr_get_pool_factory (pj_med_mgr_t *mgr)
+{
+ return mgr->pf;
+}
diff --git a/pjmedia/src/pjmedia/mediamgr.h b/pjmedia/src/pjmedia/mediamgr.h
index cf52e41f..148038ec 100644
--- a/pjmedia/src/pjmedia/mediamgr.h
+++ b/pjmedia/src/pjmedia/mediamgr.h
@@ -1,100 +1,100 @@
-/* $Id$ */
-/*
- * Copyright (C) 2003-2006 Benny Prijono <benny@prijono.org>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
-#ifndef __PJMEDIA_MEDIAMGR_H__
-#define __PJMEDIA_MEDIAMGR_H__
-
-
-/**
- * @file mediamgr.h
- * @brief Media Manager.
- */
-/**
- * @defgroup PJMED_MGR Media Manager
- * @ingroup PJMEDIA
- * @{
- *
- * The media manager acts as placeholder for endpoint capabilities. Each
- * media manager will have a codec manager to manage list of codecs installed
- * in the endpoint and a sound device factory.
- *
- * A reference to media manager instance is required when application wants
- * to create a media session (#pj_media_session_create or
- * #pj_media_session_create_from_sdp).
- */
-
-#include <pjmedia/sound.h>
-#include <pjmedia/codec.h>
-
-
-PJ_BEGIN_DECL
-
-
-/** Opague declaration of media manager. */
-typedef struct pj_med_mgr_t pj_med_mgr_t;
-
-/**
- * Create an instance of media manager.
- *
- * @param pf Pool factory.
- * @param conn_addr Connection address to be used by this media manager.
- *
- * @return A new instance of media manager, or NULL if failed.
- */
-PJ_DECL(pj_med_mgr_t*) pj_med_mgr_create (pj_pool_factory *pf);
-
-/**
- * Destroy media manager instance.
- *
- * @param mgr Media manager instance.
- *
- * @return zero on success.
- */
-PJ_DECL(pj_status_t) pj_med_mgr_destroy (pj_med_mgr_t *mgr);
-
-/**
- * Get pool factory of the media manager as specified when the media
- * manager was created.
- *
- * @param mgr The media manager instance.
- *
- * @return Pool factory instance of the media manager.
- */
-PJ_DECL(pj_pool_factory*) pj_med_mgr_get_pool_factory (pj_med_mgr_t *mgr);
-
-/**
- * Get the codec manager instance.
- *
- * @param mgr The media manager instance.
- *
- * @return The instance of codec manager.
- */
-PJ_DECL(pj_codec_mgr*) pj_med_mgr_get_codec_mgr (pj_med_mgr_t *mgr);
-
-
-
-PJ_END_DECL
-
-
-/**
- * @}
- */
-
-
-
-#endif /* __PJMEDIA_MEDIAMGR_H__ */
+/* $Id$ */
+/*
+ * Copyright (C) 2003-2006 Benny Prijono <benny@prijono.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#ifndef __PJMEDIA_MEDIAMGR_H__
+#define __PJMEDIA_MEDIAMGR_H__
+
+
+/**
+ * @file mediamgr.h
+ * @brief Media Manager.
+ */
+/**
+ * @defgroup PJMED_MGR Media Manager
+ * @ingroup PJMEDIA
+ * @{
+ *
+ * The media manager acts as placeholder for endpoint capabilities. Each
+ * media manager will have a codec manager to manage list of codecs installed
+ * in the endpoint and a sound device factory.
+ *
+ * A reference to media manager instance is required when application wants
+ * to create a media session (#pj_media_session_create or
+ * #pj_media_session_create_from_sdp).
+ */
+
+#include <pjmedia/sound.h>
+#include <pjmedia/codec.h>
+
+
+PJ_BEGIN_DECL
+
+
+/** Opague declaration of media manager. */
+typedef struct pj_med_mgr_t pj_med_mgr_t;
+
+/**
+ * Create an instance of media manager.
+ *
+ * @param pf Pool factory.
+ * @param conn_addr Connection address to be used by this media manager.
+ *
+ * @return A new instance of media manager, or NULL if failed.
+ */
+PJ_DECL(pj_med_mgr_t*) pj_med_mgr_create (pj_pool_factory *pf);
+
+/**
+ * Destroy media manager instance.
+ *
+ * @param mgr Media manager instance.
+ *
+ * @return zero on success.
+ */
+PJ_DECL(pj_status_t) pj_med_mgr_destroy (pj_med_mgr_t *mgr);
+
+/**
+ * Get pool factory of the media manager as specified when the media
+ * manager was created.
+ *
+ * @param mgr The media manager instance.
+ *
+ * @return Pool factory instance of the media manager.
+ */
+PJ_DECL(pj_pool_factory*) pj_med_mgr_get_pool_factory (pj_med_mgr_t *mgr);
+
+/**
+ * Get the codec manager instance.
+ *
+ * @param mgr The media manager instance.
+ *
+ * @return The instance of codec manager.
+ */
+PJ_DECL(pj_codec_mgr*) pj_med_mgr_get_codec_mgr (pj_med_mgr_t *mgr);
+
+
+
+PJ_END_DECL
+
+
+/**
+ * @}
+ */
+
+
+
+#endif /* __PJMEDIA_MEDIAMGR_H__ */
diff --git a/pjmedia/src/pjmedia/nullsound.c b/pjmedia/src/pjmedia/nullsound.c
index 6f3059ea..dc509a7a 100644
--- a/pjmedia/src/pjmedia/nullsound.c
+++ b/pjmedia/src/pjmedia/nullsound.c
@@ -1,127 +1,127 @@
-/* $Id$ */
-/*
- * Copyright (C) 2003-2006 Benny Prijono <benny@prijono.org>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
-#include <pjmedia/sound.h>
-
-/*
- * Null Factory Operations
- */
-static pj_status_t null_sound_init(void);
-static const char *null_sound_get_name(void);
-static pj_status_t null_sound_destroy(void);
-static pj_status_t null_sound_enum_devices(int *count, char *dev_names[]);
-static pj_status_t null_sound_create_dev(const char *dev_name, pj_snd_dev *dev);
-static pj_status_t null_sound_destroy_dev(pj_snd_dev *dev);
-
-
-/*
- * Null Device Operations
- */
-static pj_status_t null_sound_dev_open( pj_snd_dev *dev, pj_snd_role_t role );
-static pj_status_t null_sound_dev_close( pj_snd_dev *dev );
-static pj_status_t null_sound_dev_play( pj_snd_dev *dev );
-static pj_status_t null_sound_dev_record( pj_snd_dev *dev );
-
-
-static pj_snd_dev_factory null_sound_factory =
-{
- &null_sound_init,
- &null_sound_get_name,
- &null_sound_destroy,
- &null_sound_enum_devices,
- &null_sound_create_dev,
- &null_sound_destroy_dev
-};
-
-static struct pj_snd_dev_op null_sound_dev_op =
-{
- &null_sound_dev_open,
- &null_sound_dev_close,
- &null_sound_dev_play,
- &null_sound_dev_record
-};
-
-PJ_DEF(pj_snd_dev_factory*) pj_nullsound_get_factory()
-{
- return &null_sound_factory;
-}
-
-static pj_status_t null_sound_init(void)
-{
- return 0;
-}
-
-static const char *null_sound_get_name(void)
-{
- return "nullsound";
-}
-
-static pj_status_t null_sound_destroy(void)
-{
- return 0;
-}
-
-static pj_status_t null_sound_enum_devices(int *count, char *dev_names[])
-{
- *count = 1;
- dev_names[0] = "nullsound";
- return 0;
-}
-
-static pj_status_t null_sound_create_dev(const char *dev_name, pj_snd_dev *dev)
-{
- PJ_UNUSED_ARG(dev_name);
- dev->op = &null_sound_dev_op;
- return 0;
-}
-
-static pj_status_t null_sound_destroy_dev(pj_snd_dev *dev)
-{
- PJ_UNUSED_ARG(dev);
- return 0;
-}
-
-
-/*
- * Null Device Operations
- */
-static pj_status_t null_sound_dev_open( pj_snd_dev *dev, pj_snd_role_t role )
-{
- PJ_UNUSED_ARG(dev);
- PJ_UNUSED_ARG(role);
- return 0;
-}
-
-static pj_status_t null_sound_dev_close( pj_snd_dev *dev )
-{
- PJ_UNUSED_ARG(dev);
- return 0;
-}
-
-static pj_status_t null_sound_dev_play( pj_snd_dev *dev )
-{
- PJ_UNUSED_ARG(dev);
- return 0;
-}
-
-static pj_status_t null_sound_dev_record( pj_snd_dev *dev )
-{
- PJ_UNUSED_ARG(dev);
- return 0;
-}
-
+/* $Id$ */
+/*
+ * Copyright (C) 2003-2006 Benny Prijono <benny@prijono.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#include <pjmedia/sound.h>
+
+/*
+ * Null Factory Operations
+ */
+static pj_status_t null_sound_init(void);
+static const char *null_sound_get_name(void);
+static pj_status_t null_sound_destroy(void);
+static pj_status_t null_sound_enum_devices(int *count, char *dev_names[]);
+static pj_status_t null_sound_create_dev(const char *dev_name, pj_snd_dev *dev);
+static pj_status_t null_sound_destroy_dev(pj_snd_dev *dev);
+
+
+/*
+ * Null Device Operations
+ */
+static pj_status_t null_sound_dev_open( pj_snd_dev *dev, pj_snd_role_t role );
+static pj_status_t null_sound_dev_close( pj_snd_dev *dev );
+static pj_status_t null_sound_dev_play( pj_snd_dev *dev );
+static pj_status_t null_sound_dev_record( pj_snd_dev *dev );
+
+
+static pj_snd_dev_factory null_sound_factory =
+{
+ &null_sound_init,
+ &null_sound_get_name,
+ &null_sound_destroy,
+ &null_sound_enum_devices,
+ &null_sound_create_dev,
+ &null_sound_destroy_dev
+};
+
+static struct pj_snd_dev_op null_sound_dev_op =
+{
+ &null_sound_dev_open,
+ &null_sound_dev_close,
+ &null_sound_dev_play,
+ &null_sound_dev_record
+};
+
+PJ_DEF(pj_snd_dev_factory*) pj_nullsound_get_factory()
+{
+ return &null_sound_factory;
+}
+
+static pj_status_t null_sound_init(void)
+{
+ return 0;
+}
+
+static const char *null_sound_get_name(void)
+{
+ return "nullsound";
+}
+
+static pj_status_t null_sound_destroy(void)
+{
+ return 0;
+}
+
+static pj_status_t null_sound_enum_devices(int *count, char *dev_names[])
+{
+ *count = 1;
+ dev_names[0] = "nullsound";
+ return 0;
+}
+
+static pj_status_t null_sound_create_dev(const char *dev_name, pj_snd_dev *dev)
+{
+ PJ_UNUSED_ARG(dev_name);
+ dev->op = &null_sound_dev_op;
+ return 0;
+}
+
+static pj_status_t null_sound_destroy_dev(pj_snd_dev *dev)
+{
+ PJ_UNUSED_ARG(dev);
+ return 0;
+}
+
+
+/*
+ * Null Device Operations
+ */
+static pj_status_t null_sound_dev_open( pj_snd_dev *dev, pj_snd_role_t role )
+{
+ PJ_UNUSED_ARG(dev);
+ PJ_UNUSED_ARG(role);
+ return 0;
+}
+
+static pj_status_t null_sound_dev_close( pj_snd_dev *dev )
+{
+ PJ_UNUSED_ARG(dev);
+ return 0;
+}
+
+static pj_status_t null_sound_dev_play( pj_snd_dev *dev )
+{
+ PJ_UNUSED_ARG(dev);
+ return 0;
+}
+
+static pj_status_t null_sound_dev_record( pj_snd_dev *dev )
+{
+ PJ_UNUSED_ARG(dev);
+ return 0;
+}
+
diff --git a/pjmedia/src/pjmedia/pasound.c b/pjmedia/src/pjmedia/pasound.c
index 4d9d05af..d3184a7d 100644
--- a/pjmedia/src/pjmedia/pasound.c
+++ b/pjmedia/src/pjmedia/pasound.c
@@ -1,404 +1,404 @@
-/* $Id$ */
-/*
- * Copyright (C) 2003-2006 Benny Prijono <benny@prijono.org>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
-#include <pjmedia/sound.h>
-#include <pj/string.h>
-#include <pj/os.h>
-#include <pj/log.h>
-#include <portaudio.h>
-
-#define THIS_FILE "pasound.c"
-
-static struct snd_mgr
-{
- pj_pool_factory *factory;
-} snd_mgr;
-
-struct pj_snd_stream
-{
- pj_pool_t *pool;
- PaStream *stream;
- int dev_index;
- int bytes_per_sample;
- pj_uint32_t samples_per_sec;
- pj_uint32_t timestamp;
- pj_uint32_t underflow;
- pj_uint32_t overflow;
- void *user_data;
- pj_snd_rec_cb rec_cb;
- pj_snd_play_cb play_cb;
- pj_bool_t quit_flag;
- pj_bool_t thread_has_exited;
- pj_bool_t thread_initialized;
- pj_thread_desc thread_desc;
- pj_thread_t *thread;
-};
-
-
-static int PaRecorderCallback(const void *input,
- void *output,
- unsigned long frameCount,
- const PaStreamCallbackTimeInfo* timeInfo,
- PaStreamCallbackFlags statusFlags,
- void *userData )
-{
- pj_snd_stream *stream = userData;
- pj_status_t status;
-
- PJ_UNUSED_ARG(output)
- PJ_UNUSED_ARG(timeInfo)
-
- if (stream->quit_flag)
- goto on_break;
-
- if (stream->thread_initialized == 0) {
- stream->thread = pj_thread_register("pa_rec", stream->thread_desc);
- stream->thread_initialized = 1;
- }
-
- if (statusFlags & paInputUnderflow)
- ++stream->underflow;
- if (statusFlags & paInputOverflow)
- ++stream->overflow;
-
- stream->timestamp += frameCount;
-
- status = (*stream->rec_cb)(stream->user_data, stream->timestamp,
- input, frameCount * stream->bytes_per_sample);
-
- if (status==0)
- return paContinue;
-
-on_break:
- stream->thread_has_exited = 1;
- return paAbort;
-}
-
-static int PaPlayerCallback( const void *input,
- void *output,
- unsigned long frameCount,
- const PaStreamCallbackTimeInfo* timeInfo,
- PaStreamCallbackFlags statusFlags,
- void *userData )
-{
- pj_snd_stream *stream = userData;
- pj_status_t status;
- unsigned size = frameCount * stream->bytes_per_sample;
-
- PJ_UNUSED_ARG(input)
- PJ_UNUSED_ARG(timeInfo)
-
- if (stream->quit_flag)
- goto on_break;
-
- if (stream->thread_initialized == 0) {
- stream->thread = pj_thread_register("pa_rec", stream->thread_desc);
- stream->thread_initialized = 1;
- }
-
- if (statusFlags & paInputUnderflow)
- ++stream->underflow;
- if (statusFlags & paInputOverflow)
- ++stream->overflow;
-
- stream->timestamp += frameCount;
-
- status = (*stream->play_cb)(stream->user_data, stream->timestamp,
- output, size);
-
- if (status==0)
- return paContinue;
-
-on_break:
- stream->thread_has_exited = 1;
- return paAbort;
-}
-
-
-/*
- * Init sound library.
- */
-PJ_DEF(pj_status_t) pj_snd_init(pj_pool_factory *factory)
-{
- int err;
-
- snd_mgr.factory = factory;
- err = Pa_Initialize();
-
- PJ_LOG(4,(THIS_FILE, "PortAudio sound library initialized, status=%d", err));
-
- return err;
-}
-
-
-/*
- * Get device count.
- */
-PJ_DEF(int) pj_snd_get_dev_count(void)
-{
- return Pa_GetDeviceCount();
-}
-
-
-/*
- * Get device info.
- */
-PJ_DEF(const pj_snd_dev_info*) pj_snd_get_dev_info(unsigned index)
-{
- static pj_snd_dev_info info;
- const PaDeviceInfo *pa_info;
-
- pa_info = Pa_GetDeviceInfo(index);
- if (!pa_info)
- return NULL;
-
- pj_memset(&info, 0, sizeof(info));
- strncpy(info.name, pa_info->name, sizeof(info.name));
- info.name[sizeof(info.name)-1] = '\0';
- info.input_count = pa_info->maxInputChannels;
- info.output_count = pa_info->maxOutputChannels;
- info.default_samples_per_sec = (unsigned)pa_info->defaultSampleRate;
-
- return &info;
-}
-
-
-/*
- * Open stream.
- */
-PJ_DEF(pj_snd_stream*) pj_snd_open_recorder( int index,
- const pj_snd_stream_info *param,
- pj_snd_rec_cb rec_cb,
- void *user_data)
-{
- pj_pool_t *pool;
- pj_snd_stream *stream;
- PaStreamParameters inputParam;
- int sampleFormat;
- const PaDeviceInfo *paDevInfo = NULL;
- PaError err;
-
- if (index == -1) {
- int count = Pa_GetDeviceCount();
- for (index=0; index<count; ++index) {
- paDevInfo = Pa_GetDeviceInfo(index);
- if (paDevInfo->maxInputChannels > 0)
- break;
- }
- if (index == count) {
- PJ_LOG(2,(THIS_FILE, "Error: unable to find recorder device"));
- return NULL;
- }
- } else {
- paDevInfo = Pa_GetDeviceInfo(index);
- if (!paDevInfo)
- return NULL;
- }
-
- if (param->bits_per_sample == 8)
- sampleFormat = paUInt8;
- else if (param->bits_per_sample == 16)
- sampleFormat = paInt16;
- else if (param->bits_per_sample == 32)
- sampleFormat = paInt32;
- else
- return NULL;
-
- pool = pj_pool_create( snd_mgr.factory, "sndstream", 1024, 1024, NULL);
- if (!pool)
- return NULL;
-
- stream = pj_pool_calloc(pool, 1, sizeof(*stream));
- stream->pool = pool;
- stream->user_data = user_data;
- stream->dev_index = index;
- stream->samples_per_sec = param->samples_per_frame;
- stream->bytes_per_sample = param->bits_per_sample / 8;
- stream->rec_cb = rec_cb;
-
- pj_memset(&inputParam, 0, sizeof(inputParam));
- inputParam.device = index;
- inputParam.channelCount = 1;
- inputParam.hostApiSpecificStreamInfo = NULL;
- inputParam.sampleFormat = sampleFormat;
- inputParam.suggestedLatency = paDevInfo->defaultLowInputLatency;
-
- err = Pa_OpenStream( &stream->stream, &inputParam, NULL,
- param->samples_per_sec,
- param->samples_per_frame * param->frames_per_packet,
- 0,
- &PaRecorderCallback, stream );
- if (err != paNoError) {
- pj_pool_release(pool);
- PJ_LOG(2,(THIS_FILE, "Error opening player: %s", Pa_GetErrorText(err)));
- return NULL;
- }
-
- PJ_LOG(4,(THIS_FILE, "%s opening device %s for recording, sample rate=%d, "
- "%d bits per sample, %d samples per buffer",
- (err==0 ? "Success" : "Error"),
- paDevInfo->name, param->samples_per_sec,
- param->bits_per_sample,
- param->samples_per_frame * param->frames_per_packet));
-
- return stream;
-}
-
-
-PJ_DEF(pj_snd_stream*) pj_snd_open_player(int index,
- const pj_snd_stream_info *param,
- pj_snd_play_cb play_cb,
- void *user_data)
-{
- pj_pool_t *pool;
- pj_snd_stream *stream;
- PaStreamParameters outputParam;
- int sampleFormat;
- const PaDeviceInfo *paDevInfo = NULL;
- PaError err;
-
- if (index == -1) {
- int count = Pa_GetDeviceCount();
- for (index=0; index<count; ++index) {
- paDevInfo = Pa_GetDeviceInfo(index);
- if (paDevInfo->maxOutputChannels > 0)
- break;
- }
- if (index == count) {
- PJ_LOG(2,(THIS_FILE, "Error: unable to find player device"));
- return NULL;
- }
- } else {
- paDevInfo = Pa_GetDeviceInfo(index);
- if (!paDevInfo)
- return NULL;
- }
-
- if (param->bits_per_sample == 8)
- sampleFormat = paUInt8;
- else if (param->bits_per_sample == 16)
- sampleFormat = paInt16;
- else if (param->bits_per_sample == 32)
- sampleFormat = paInt32;
- else
- return NULL;
-
- pool = pj_pool_create( snd_mgr.factory, "sndstream", 1024, 1024, NULL);
- if (!pool)
- return NULL;
-
- stream = pj_pool_calloc(pool, 1, sizeof(*stream));
- stream->pool = pool;
- stream->user_data = user_data;
- stream->samples_per_sec = param->samples_per_frame;
- stream->bytes_per_sample = param->bits_per_sample / 8;
- stream->dev_index = index;
- stream->play_cb = play_cb;
-
- pj_memset(&outputParam, 0, sizeof(outputParam));
- outputParam.device = index;
- outputParam.channelCount = 1;
- outputParam.hostApiSpecificStreamInfo = NULL;
- outputParam.sampleFormat = sampleFormat;
- outputParam.suggestedLatency = paDevInfo->defaultLowInputLatency;
-
- err = Pa_OpenStream( &stream->stream, NULL, &outputParam,
- param->samples_per_sec,
- param->samples_per_frame * param->frames_per_packet,
- 0,
- &PaPlayerCallback, stream );
- if (err != paNoError) {
- pj_pool_release(pool);
- PJ_LOG(2,(THIS_FILE, "Error opening player: %s", Pa_GetErrorText(err)));
- return NULL;
- }
-
- PJ_LOG(4,(THIS_FILE, "%s opening device %s for playing, sample rate=%d, "
- "%d bits per sample, %d samples per buffer",
- (err==0 ? "Success" : "Error"),
- paDevInfo->name, param->samples_per_sec,
- param->bits_per_sample,
- param->samples_per_frame * param->frames_per_packet));
-
- return stream;
-}
-
-
-/*
- * Start stream.
- */
-PJ_DEF(pj_status_t) pj_snd_stream_start(pj_snd_stream *stream)
-{
- PJ_LOG(4,(THIS_FILE, "Starting stream.."));
- return Pa_StartStream(stream->stream);
-}
-
-/*
- * Stop stream.
- */
-PJ_DEF(pj_status_t) pj_snd_stream_stop(pj_snd_stream *stream)
-{
- int i, err;
-
- stream->quit_flag = 1;
- for (i=0; !stream->thread_has_exited && i<100; ++i)
- pj_thread_sleep(10);
-
- pj_thread_sleep(1);
-
- PJ_LOG(4,(THIS_FILE, "Stopping stream.."));
-
- err = Pa_StopStream(stream->stream);
- return err;
-}
-
-/*
- * Destroy stream.
- */
-PJ_DEF(pj_status_t) pj_snd_stream_close(pj_snd_stream *stream)
-{
- int i, err;
- const PaDeviceInfo *paDevInfo;
-
- stream->quit_flag = 1;
- for (i=0; !stream->thread_has_exited && i<100; ++i)
- pj_thread_sleep(10);
-
- pj_thread_sleep(1);
-
- paDevInfo = Pa_GetDeviceInfo(stream->dev_index);
-
- PJ_LOG(4,(THIS_FILE, "Closing %s: %lu underflow, %lu overflow",
- paDevInfo->name,
- stream->underflow, stream->overflow));
-
- err = Pa_CloseStream(stream->stream);
- pj_pool_release(stream->pool);
- return err;
-}
-
-/*
- * Deinitialize sound library.
- */
-PJ_DEF(pj_status_t) pj_snd_deinit(void)
-{
- PJ_LOG(4,(THIS_FILE, "PortAudio sound library shutting down.."));
-
- return Pa_Terminate();
-}
-
+/* $Id$ */
+/*
+ * Copyright (C) 2003-2006 Benny Prijono <benny@prijono.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#include <pjmedia/sound.h>
+#include <pj/string.h>
+#include <pj/os.h>
+#include <pj/log.h>
+#include <portaudio.h>
+
+#define THIS_FILE "pasound.c"
+
+static struct snd_mgr
+{
+ pj_pool_factory *factory;
+} snd_mgr;
+
+struct pj_snd_stream
+{
+ pj_pool_t *pool;
+ PaStream *stream;
+ int dev_index;
+ int bytes_per_sample;
+ pj_uint32_t samples_per_sec;
+ pj_uint32_t timestamp;
+ pj_uint32_t underflow;
+ pj_uint32_t overflow;
+ void *user_data;
+ pj_snd_rec_cb rec_cb;
+ pj_snd_play_cb play_cb;
+ pj_bool_t quit_flag;
+ pj_bool_t thread_has_exited;
+ pj_bool_t thread_initialized;
+ pj_thread_desc thread_desc;
+ pj_thread_t *thread;
+};
+
+
+static int PaRecorderCallback(const void *input,
+ void *output,
+ unsigned long frameCount,
+ const PaStreamCallbackTimeInfo* timeInfo,
+ PaStreamCallbackFlags statusFlags,
+ void *userData )
+{
+ pj_snd_stream *stream = userData;
+ pj_status_t status;
+
+ PJ_UNUSED_ARG(output)
+ PJ_UNUSED_ARG(timeInfo)
+
+ if (stream->quit_flag)
+ goto on_break;
+
+ if (stream->thread_initialized == 0) {
+ stream->thread = pj_thread_register("pa_rec", stream->thread_desc);
+ stream->thread_initialized = 1;
+ }
+
+ if (statusFlags & paInputUnderflow)
+ ++stream->underflow;
+ if (statusFlags & paInputOverflow)
+ ++stream->overflow;
+
+ stream->timestamp += frameCount;
+
+ status = (*stream->rec_cb)(stream->user_data, stream->timestamp,
+ input, frameCount * stream->bytes_per_sample);
+
+ if (status==0)
+ return paContinue;
+
+on_break:
+ stream->thread_has_exited = 1;
+ return paAbort;
+}
+
+static int PaPlayerCallback( const void *input,
+ void *output,
+ unsigned long frameCount,
+ const PaStreamCallbackTimeInfo* timeInfo,
+ PaStreamCallbackFlags statusFlags,
+ void *userData )
+{
+ pj_snd_stream *stream = userData;
+ pj_status_t status;
+ unsigned size = frameCount * stream->bytes_per_sample;
+
+ PJ_UNUSED_ARG(input)
+ PJ_UNUSED_ARG(timeInfo)
+
+ if (stream->quit_flag)
+ goto on_break;
+
+ if (stream->thread_initialized == 0) {
+ stream->thread = pj_thread_register("pa_rec", stream->thread_desc);
+ stream->thread_initialized = 1;
+ }
+
+ if (statusFlags & paInputUnderflow)
+ ++stream->underflow;
+ if (statusFlags & paInputOverflow)
+ ++stream->overflow;
+
+ stream->timestamp += frameCount;
+
+ status = (*stream->play_cb)(stream->user_data, stream->timestamp,
+ output, size);
+
+ if (status==0)
+ return paContinue;
+
+on_break:
+ stream->thread_has_exited = 1;
+ return paAbort;
+}
+
+
+/*
+ * Init sound library.
+ */
+PJ_DEF(pj_status_t) pj_snd_init(pj_pool_factory *factory)
+{
+ int err;
+
+ snd_mgr.factory = factory;
+ err = Pa_Initialize();
+
+ PJ_LOG(4,(THIS_FILE, "PortAudio sound library initialized, status=%d", err));
+
+ return err;
+}
+
+
+/*
+ * Get device count.
+ */
+PJ_DEF(int) pj_snd_get_dev_count(void)
+{
+ return Pa_GetDeviceCount();
+}
+
+
+/*
+ * Get device info.
+ */
+PJ_DEF(const pj_snd_dev_info*) pj_snd_get_dev_info(unsigned index)
+{
+ static pj_snd_dev_info info;
+ const PaDeviceInfo *pa_info;
+
+ pa_info = Pa_GetDeviceInfo(index);
+ if (!pa_info)
+ return NULL;
+
+ pj_memset(&info, 0, sizeof(info));
+ strncpy(info.name, pa_info->name, sizeof(info.name));
+ info.name[sizeof(info.name)-1] = '\0';
+ info.input_count = pa_info->maxInputChannels;
+ info.output_count = pa_info->maxOutputChannels;
+ info.default_samples_per_sec = (unsigned)pa_info->defaultSampleRate;
+
+ return &info;
+}
+
+
+/*
+ * Open stream.
+ */
+PJ_DEF(pj_snd_stream*) pj_snd_open_recorder( int index,
+ const pj_snd_stream_info *param,
+ pj_snd_rec_cb rec_cb,
+ void *user_data)
+{
+ pj_pool_t *pool;
+ pj_snd_stream *stream;
+ PaStreamParameters inputParam;
+ int sampleFormat;
+ const PaDeviceInfo *paDevInfo = NULL;
+ PaError err;
+
+ if (index == -1) {
+ int count = Pa_GetDeviceCount();
+ for (index=0; index<count; ++index) {
+ paDevInfo = Pa_GetDeviceInfo(index);
+ if (paDevInfo->maxInputChannels > 0)
+ break;
+ }
+ if (index == count) {
+ PJ_LOG(2,(THIS_FILE, "Error: unable to find recorder device"));
+ return NULL;
+ }
+ } else {
+ paDevInfo = Pa_GetDeviceInfo(index);
+ if (!paDevInfo)
+ return NULL;
+ }
+
+ if (param->bits_per_sample == 8)
+ sampleFormat = paUInt8;
+ else if (param->bits_per_sample == 16)
+ sampleFormat = paInt16;
+ else if (param->bits_per_sample == 32)
+ sampleFormat = paInt32;
+ else
+ return NULL;
+
+ pool = pj_pool_create( snd_mgr.factory, "sndstream", 1024, 1024, NULL);
+ if (!pool)
+ return NULL;
+
+ stream = pj_pool_calloc(pool, 1, sizeof(*stream));
+ stream->pool = pool;
+ stream->user_data = user_data;
+ stream->dev_index = index;
+ stream->samples_per_sec = param->samples_per_frame;
+ stream->bytes_per_sample = param->bits_per_sample / 8;
+ stream->rec_cb = rec_cb;
+
+ pj_memset(&inputParam, 0, sizeof(inputParam));
+ inputParam.device = index;
+ inputParam.channelCount = 1;
+ inputParam.hostApiSpecificStreamInfo = NULL;
+ inputParam.sampleFormat = sampleFormat;
+ inputParam.suggestedLatency = paDevInfo->defaultLowInputLatency;
+
+ err = Pa_OpenStream( &stream->stream, &inputParam, NULL,
+ param->samples_per_sec,
+ param->samples_per_frame * param->frames_per_packet,
+ 0,
+ &PaRecorderCallback, stream );
+ if (err != paNoError) {
+ pj_pool_release(pool);
+ PJ_LOG(2,(THIS_FILE, "Error opening player: %s", Pa_GetErrorText(err)));
+ return NULL;
+ }
+
+ PJ_LOG(4,(THIS_FILE, "%s opening device %s for recording, sample rate=%d, "
+ "%d bits per sample, %d samples per buffer",
+ (err==0 ? "Success" : "Error"),
+ paDevInfo->name, param->samples_per_sec,
+ param->bits_per_sample,
+ param->samples_per_frame * param->frames_per_packet));
+
+ return stream;
+}
+
+
+PJ_DEF(pj_snd_stream*) pj_snd_open_player(int index,
+ const pj_snd_stream_info *param,
+ pj_snd_play_cb play_cb,
+ void *user_data)
+{
+ pj_pool_t *pool;
+ pj_snd_stream *stream;
+ PaStreamParameters outputParam;
+ int sampleFormat;
+ const PaDeviceInfo *paDevInfo = NULL;
+ PaError err;
+
+ if (index == -1) {
+ int count = Pa_GetDeviceCount();
+ for (index=0; index<count; ++index) {
+ paDevInfo = Pa_GetDeviceInfo(index);
+ if (paDevInfo->maxOutputChannels > 0)
+ break;
+ }
+ if (index == count) {
+ PJ_LOG(2,(THIS_FILE, "Error: unable to find player device"));
+ return NULL;
+ }
+ } else {
+ paDevInfo = Pa_GetDeviceInfo(index);
+ if (!paDevInfo)
+ return NULL;
+ }
+
+ if (param->bits_per_sample == 8)
+ sampleFormat = paUInt8;
+ else if (param->bits_per_sample == 16)
+ sampleFormat = paInt16;
+ else if (param->bits_per_sample == 32)
+ sampleFormat = paInt32;
+ else
+ return NULL;
+
+ pool = pj_pool_create( snd_mgr.factory, "sndstream", 1024, 1024, NULL);
+ if (!pool)
+ return NULL;
+
+ stream = pj_pool_calloc(pool, 1, sizeof(*stream));
+ stream->pool = pool;
+ stream->user_data = user_data;
+ stream->samples_per_sec = param->samples_per_frame;
+ stream->bytes_per_sample = param->bits_per_sample / 8;
+ stream->dev_index = index;
+ stream->play_cb = play_cb;
+
+ pj_memset(&outputParam, 0, sizeof(outputParam));
+ outputParam.device = index;
+ outputParam.channelCount = 1;
+ outputParam.hostApiSpecificStreamInfo = NULL;
+ outputParam.sampleFormat = sampleFormat;
+ outputParam.suggestedLatency = paDevInfo->defaultLowInputLatency;
+
+ err = Pa_OpenStream( &stream->stream, NULL, &outputParam,
+ param->samples_per_sec,
+ param->samples_per_frame * param->frames_per_packet,
+ 0,
+ &PaPlayerCallback, stream );
+ if (err != paNoError) {
+ pj_pool_release(pool);
+ PJ_LOG(2,(THIS_FILE, "Error opening player: %s", Pa_GetErrorText(err)));
+ return NULL;
+ }
+
+ PJ_LOG(4,(THIS_FILE, "%s opening device %s for playing, sample rate=%d, "
+ "%d bits per sample, %d samples per buffer",
+ (err==0 ? "Success" : "Error"),
+ paDevInfo->name, param->samples_per_sec,
+ param->bits_per_sample,
+ param->samples_per_frame * param->frames_per_packet));
+
+ return stream;
+}
+
+
+/*
+ * Start stream.
+ */
+PJ_DEF(pj_status_t) pj_snd_stream_start(pj_snd_stream *stream)
+{
+ PJ_LOG(4,(THIS_FILE, "Starting stream.."));
+ return Pa_StartStream(stream->stream);
+}
+
+/*
+ * Stop stream.
+ */
+PJ_DEF(pj_status_t) pj_snd_stream_stop(pj_snd_stream *stream)
+{
+ int i, err;
+
+ stream->quit_flag = 1;
+ for (i=0; !stream->thread_has_exited && i<100; ++i)
+ pj_thread_sleep(10);
+
+ pj_thread_sleep(1);
+
+ PJ_LOG(4,(THIS_FILE, "Stopping stream.."));
+
+ err = Pa_StopStream(stream->stream);
+ return err;
+}
+
+/*
+ * Destroy stream.
+ */
+PJ_DEF(pj_status_t) pj_snd_stream_close(pj_snd_stream *stream)
+{
+ int i, err;
+ const PaDeviceInfo *paDevInfo;
+
+ stream->quit_flag = 1;
+ for (i=0; !stream->thread_has_exited && i<100; ++i)
+ pj_thread_sleep(10);
+
+ pj_thread_sleep(1);
+
+ paDevInfo = Pa_GetDeviceInfo(stream->dev_index);
+
+ PJ_LOG(4,(THIS_FILE, "Closing %s: %lu underflow, %lu overflow",
+ paDevInfo->name,
+ stream->underflow, stream->overflow));
+
+ err = Pa_CloseStream(stream->stream);
+ pj_pool_release(stream->pool);
+ return err;
+}
+
+/*
+ * Deinitialize sound library.
+ */
+PJ_DEF(pj_status_t) pj_snd_deinit(void)
+{
+ PJ_LOG(4,(THIS_FILE, "PortAudio sound library shutting down.."));
+
+ return Pa_Terminate();
+}
+
diff --git a/pjmedia/src/pjmedia/portaudio/LICENSE.txt b/pjmedia/src/pjmedia/portaudio/LICENSE.txt
index 7a95bee3..105da3f7 100644
--- a/pjmedia/src/pjmedia/portaudio/LICENSE.txt
+++ b/pjmedia/src/pjmedia/portaudio/LICENSE.txt
@@ -1,65 +1,65 @@
-Portable header file to contain:
-/*
- * PortAudio Portable Real-Time Audio Library
- * PortAudio API Header File
- * Latest version available at: http://www.audiomulch.com/portaudio/
- *
- * Copyright (c) 1999-2000 Ross Bencina and Phil Burk
- *
- * 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.
- *
- * Any person wishing to distribute modifications to the Software is
- * requested to send the modifications to the original developer so that
- * they can be incorporated into the canonical version.
- *
- * 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.
- *
- */
-
-
-Implementation files to contain:
-/*
- * PortAudio Portable Real-Time Audio Library
- * Latest version at: http://www.audiomulch.com/portaudio/
- * <platform> Implementation
- * Copyright (c) 1999-2000 <author(s)>
- *
- * 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.
- *
- * Any person wishing to distribute modifications to the Software is
- * requested to send the modifications to the original developer so that
- * they can be incorporated into the canonical version.
- *
- * 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.
- *
+Portable header file to contain:
+/*
+ * PortAudio Portable Real-Time Audio Library
+ * PortAudio API Header File
+ * Latest version available at: http://www.audiomulch.com/portaudio/
+ *
+ * Copyright (c) 1999-2000 Ross Bencina and Phil Burk
+ *
+ * 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.
+ *
+ * Any person wishing to distribute modifications to the Software is
+ * requested to send the modifications to the original developer so that
+ * they can be incorporated into the canonical version.
+ *
+ * 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.
+ *
+ */
+
+
+Implementation files to contain:
+/*
+ * PortAudio Portable Real-Time Audio Library
+ * Latest version at: http://www.audiomulch.com/portaudio/
+ * <platform> Implementation
+ * Copyright (c) 1999-2000 <author(s)>
+ *
+ * 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.
+ *
+ * Any person wishing to distribute modifications to the Software is
+ * requested to send the modifications to the original developer so that
+ * they can be incorporated into the canonical version.
+ *
+ * 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.
+ *
*/ \ No newline at end of file
diff --git a/pjmedia/src/pjmedia/portaudio/README.txt b/pjmedia/src/pjmedia/portaudio/README.txt
index ccbf8a01..4cfc6166 100644
--- a/pjmedia/src/pjmedia/portaudio/README.txt
+++ b/pjmedia/src/pjmedia/portaudio/README.txt
@@ -1,81 +1,81 @@
-README for PortAudio
-Implementations for PC DirectSound and Mac SoundManager
-
-/*
- * PortAudio Portable Real-Time Audio Library
- * Latest Version at: http://www.portaudio.com//
- *
- * Copyright (c) 1999-2000 Phil Burk and Ross Bencina
- *
- * 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.
- *
- * Any person wishing to distribute modifications to the Software is
- * requested to send the modifications to the original developer so that
- * they can be incorporated into the canonical version.
- *
- * 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.
- *
- */
-
-PortAudio is a portable audio I/O library designed for cross-platform
-support of audio. It uses a callback mechanism to request audio processing.
-Audio can be generated in various formats, including 32 bit floating point,
-and will be converted to the native format internally.
-
-Documentation:
- See "pa_common/portaudio.h" for API spec.
- See docs folder for a tutorial.
- Also see http://www.portaudio.com/docs/
- And see "pa_tests/patest_saw.c" for an example.
-
-For information on compiling programs with PortAudio, please see the
-tutorial at:
-
- http://www.portaudio.com/docs/pa_tutorial.html
-
-Important Files and Folders:
- pa_common/ = platform independant code
- pa_common/portaudio.h = header file for PortAudio API. Specifies API.
- pa_common/pa_lib.c = host independant code for all implementations.
-
- pablio = simple blocking read/write interface
-
-Platform Implementations
- pa_asio = ASIO for Windows and Macintosh
- pa_beos = BeOS
- pa_mac_sm = Macintosh Sound Manager for OS 8,9 and Carbon
- pa_mac_core = Macintosh Core Audio for OS X
- pa_sgi = Silicon Graphics AL
- pa_unix_oss = OSS implementation for various Unixes
- pa_win_ds = Windows Direct Sound
- pa_win_wmme = Windows MME (most widely supported)
-
-Test Programs
- pa_tests/pa_fuzz.c = guitar fuzz box
- pa_tests/pa_devs.c = print a list of available devices
- pa_tests/pa_minlat.c = determine minimum latency for your machine
- pa_tests/paqa_devs.c = self test that opens all devices
- pa_tests/paqa_errs.c = test error detection and reporting
- pa_tests/patest_clip.c = hear a sine wave clipped and unclipped
- pa_tests/patest_dither.c = hear effects of dithering (extremely subtle)
- pa_tests/patest_pink.c = fun with pink noise
- pa_tests/patest_record.c = record and playback some audio
- pa_tests/patest_maxsines.c = how many sine waves can we play? Tests Pa_GetCPULoad().
- pa_tests/patest_sine.c = output a sine wave in a simple PA app
- pa_tests/patest_sync.c = test syncronization of audio and video
- pa_tests/patest_wire.c = pass input to output, wire simulator
+README for PortAudio
+Implementations for PC DirectSound and Mac SoundManager
+
+/*
+ * PortAudio Portable Real-Time Audio Library
+ * Latest Version at: http://www.portaudio.com//
+ *
+ * Copyright (c) 1999-2000 Phil Burk and Ross Bencina
+ *
+ * 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.
+ *
+ * Any person wishing to distribute modifications to the Software is
+ * requested to send the modifications to the original developer so that
+ * they can be incorporated into the canonical version.
+ *
+ * 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.
+ *
+ */
+
+PortAudio is a portable audio I/O library designed for cross-platform
+support of audio. It uses a callback mechanism to request audio processing.
+Audio can be generated in various formats, including 32 bit floating point,
+and will be converted to the native format internally.
+
+Documentation:
+ See "pa_common/portaudio.h" for API spec.
+ See docs folder for a tutorial.
+ Also see http://www.portaudio.com/docs/
+ And see "pa_tests/patest_saw.c" for an example.
+
+For information on compiling programs with PortAudio, please see the
+tutorial at:
+
+ http://www.portaudio.com/docs/pa_tutorial.html
+
+Important Files and Folders:
+ pa_common/ = platform independant code
+ pa_common/portaudio.h = header file for PortAudio API. Specifies API.
+ pa_common/pa_lib.c = host independant code for all implementations.
+
+ pablio = simple blocking read/write interface
+
+Platform Implementations
+ pa_asio = ASIO for Windows and Macintosh
+ pa_beos = BeOS
+ pa_mac_sm = Macintosh Sound Manager for OS 8,9 and Carbon
+ pa_mac_core = Macintosh Core Audio for OS X
+ pa_sgi = Silicon Graphics AL
+ pa_unix_oss = OSS implementation for various Unixes
+ pa_win_ds = Windows Direct Sound
+ pa_win_wmme = Windows MME (most widely supported)
+
+Test Programs
+ pa_tests/pa_fuzz.c = guitar fuzz box
+ pa_tests/pa_devs.c = print a list of available devices
+ pa_tests/pa_minlat.c = determine minimum latency for your machine
+ pa_tests/paqa_devs.c = self test that opens all devices
+ pa_tests/paqa_errs.c = test error detection and reporting
+ pa_tests/patest_clip.c = hear a sine wave clipped and unclipped
+ pa_tests/patest_dither.c = hear effects of dithering (extremely subtle)
+ pa_tests/patest_pink.c = fun with pink noise
+ pa_tests/patest_record.c = record and playback some audio
+ pa_tests/patest_maxsines.c = how many sine waves can we play? Tests Pa_GetCPULoad().
+ pa_tests/patest_sine.c = output a sine wave in a simple PA app
+ pa_tests/patest_sync.c = test syncronization of audio and video
+ pa_tests/patest_wire.c = pass input to output, wire simulator
diff --git a/pjmedia/src/pjmedia/portaudio/V19-devel-readme.txt b/pjmedia/src/pjmedia/portaudio/V19-devel-readme.txt
index 0b18fde8..ae5570eb 100644
--- a/pjmedia/src/pjmedia/portaudio/V19-devel-readme.txt
+++ b/pjmedia/src/pjmedia/portaudio/V19-devel-readme.txt
@@ -1,222 +1,222 @@
-STATUS:
-
-MME, DirectSound and ASIO versions are more-or-less working. See FIXMEs @todos
-and the proposals matrix at portaudio.com for further status.
-
-The pa_tests directory contains tests. pa_tests/README.txt notes which tests
-currently build.
-
-The PaUtil support code is finished enough for other implementations to be
-ported. No changes are expected to be made to the definition of the PaUtil
-functions.
-
-Note that it's not yet 100% clear how the current support functions
-will interact with blocking read/write streams.
-
-BUILD INSTRUCTIONS
-
-to build tests/patest_sine.c you will need to compile and link the following
-files (MME)
-pa_common\pa_process.c
-pa_common\pa_skeleton.c
-pa_common\pa_stream.c
-pa_common\pa_trace.c
-pa_common\pa_converters.c
-pa_common\pa_cpuload.c
-pa_common\pa_dither.c
-pa_common\pa_front.c
-pa_common\pa_allocation.h
-pa_win\pa_win_util.c
-pa_win\pa_win_hostapis.c
-pa_win_wmme\pa_win_wmme.c
-
-see below for a description of these files.
-
-
-FILES:
-
-portaudio.h
- public api header file
-
-pa_front.c
- implements the interface defined in portaudio.h. manages multiple host apis.
- validates function parameters before calling through to host apis. tracks
- open streams and closes them at Pa_Terminate().
-
-pa_util.h
- declares utility functions for use my implementations. including utility
- functions which must be implemented separately for each platform.
-
-pa_hostapi.h
- hostapi representation structure used to interface between pa_front.c
- and implementations
-
-pa_stream.c/h
- stream interface and representation structures and helper functions
- used to interface between pa_front.c and implementations
-
-pa_cpuload.c/h
- source and header for cpu load calculation facility
-
-pa_trace.c/h
- source and header for debug trace log facility
-
-pa_converters.c/h
- sample buffer conversion facility
-
-pa_dither.c/h
- dither noise generator
-
-pa_process.c/h
- callback buffer processing facility including interleave and block adaption
-
-pa_allocation.c/h
- allocation context for tracking groups of allocations
-
-pa_skeleton.c
- an skeleton implementation showing how the common code can be used.
-
-pa_win_util.c
- Win32 implementation of platform specific PaUtil functions (memory allocation,
- usec clock, Pa_Sleep().) The file will be used with all Win32 host APIs.
-
-pa_win_hostapis.c
- contains the paHostApiInitializers array and an implementation of
- Pa_GetDefaultHostApi() for win32 builds.
-
-pa_win_wmme.c
- Win32 host api implementation for the windows multimedia extensions audio API.
-
-pa_win_wmme.h
- public header file containing interfaces to mme-specific functions and the
- deviceInfo data structure.
-
-
-CODING GUIDELINES:
-
-naming conventions:
- #defines begin with PA_
- #defines local to a file end with _
- global utility variables begin with paUtil
- global utility types begin with PaUtil (including function types)
- global utility functions begin with PaUtil_
- static variables end with _
- static constants begin with const and end with _
- static funtions have no special prefix/suffix
-
-In general, implementations should declare all of their members static,
-except for their initializer which should be exported. All exported names
-should be preceeded by Pa<MN>_ where MN is the module name, for example
-the windows mme initializer should be named PaWinWmme_Initialize().
-
-Every host api should define an initializer which returns an error code
-and a PaHostApiInterface*. The initializer should only return an error other
-than paNoError if it encounters an unexpected and fatal error (memory allocation
-error for example). In general, there may be conditions under which it returns
-a NULL interface pointer and also returns paNoError. For example, if the ASIO
-implementation detects that ASIO is not installed, it should return a
-NULL interface, and paNoError.
-
-Platform-specific shared functions should begin with Pa<PN>_ where PN is the
-platform name. eg. PaWin_ for windows, PaUnix_ for unix.
-
-The above two conventions should also be followed whenever it is necessary to
-share functions accross multiple source files.
-
-Two utilities for debug messages are provided. The PA_DEBUG macro defined in
-pa_implementation.h provides a simple way to print debug messages to stderr.
-Due to real-time performance issues, PA_DEBUG may not be suitable for use
-within the portaudio processing callback, or in other threads. In such cases
-the event tracing facility provided in pa_trace.h may be more appropriate.
-
-If PA_LOG_API_CALLS is defined, all calls to the public PortAudio API
-will be logged to stderr along with parameter and return values.
-
-
-TODO:
- (this list is totally out of date)
-
- finish coding converter functions in pa_converters.c (anyone?)
-
- implement block adaption in pa_process.c (phil?)
-
- fix all current tests to work with new code. this should mostly involve
- changing PortAudioStream to PaStream, and GetDefaultDeviceID to GetDefaultDevice etc.
-
- write some new tests to exercise the multi-api functions
-
- write (doxygen) documentation for pa_trace (phil?)
-
- remove unused typeids from PaHostAPITypeID
-
- create a global configuration file which documents which PA_ defines can be
- used for configuration
-
- need a coding standard for comment formatting
-
- migrate directx (phil)
-
- migrate asio (ross?, stephane?)
-
- see top of pa_win_wmme.c for MME todo items (ross)
-
- write style guide document (ross)
-
-
-DESIGN ISSUES:
- (this list is totally out of date)
-
- consider removing Pa_ConvertHostApiDeviceIndexToGlobalDeviceIndex() from the API
-
- switch to new latency parameter mechanism now (?)
-
- question: if input or outputDriverInfo structures are passed for a different
- hostApi from the one being called, do we return an error or just ignore
- them? (i think return error)
-
- consider renaming PortAudioCallback to PaStreamCallback
-
- consider renaming PaError, PaResult
-
-
-ASSORTED DISORGANISED NOTES:
-
- NOTE:
- pa_lib.c performs the following validations for Pa_OpenStream() which we do not currently do:
- - checks the device info to make sure that the device supports the requested sample rate,
- it may also change the sample rate to the "closest available" sample rate if it
- is within a particular error margin
-
- rationale for breaking up internalPortAudioStream:
- each implementation has its own requirements and behavior, and should be
- able to choose the best way to operate without being limited by the
- constraints imposed by a common infrastructure. in other words the
- implementations should be able to pick and choose services from the
- common infrastructure. currently identified services include:
-
- - cpu load tracking
- - buffering and conversion service (same code works for input and output)
- - should support buffer multiplexing (non-integer length input and output buffers)
- - in-place conversion where possible (only for callback, read/write always copies)
- - should manage allocation of temporary buffers if necessary
- - instrumentation (should be able to be disabled): callback count, framesProcessed
- - common data: magic, streamInterface, callback, userdata
-
-
-- conversion functions:
- - should handle temp buffer allocation
- - dithering (random number state per-stream)
- - buffer size mismatches
- - with new buffer slip rules, temp buffers may always be needed
- - we should aim for in-place conversion wherever possible
- - does phil's code support in-place conversion? (yes)
-
-- dicuss relationship between user and host buffer sizes
- - completely independent.. individual implementations may constrain
- host buffer sizes if necessary
-
-
-- discuss device capabilities:
- - i'd like to be able to request certain information:
- - channel count for example
-
+STATUS:
+
+MME, DirectSound and ASIO versions are more-or-less working. See FIXMEs @todos
+and the proposals matrix at portaudio.com for further status.
+
+The pa_tests directory contains tests. pa_tests/README.txt notes which tests
+currently build.
+
+The PaUtil support code is finished enough for other implementations to be
+ported. No changes are expected to be made to the definition of the PaUtil
+functions.
+
+Note that it's not yet 100% clear how the current support functions
+will interact with blocking read/write streams.
+
+BUILD INSTRUCTIONS
+
+to build tests/patest_sine.c you will need to compile and link the following
+files (MME)
+pa_common\pa_process.c
+pa_common\pa_skeleton.c
+pa_common\pa_stream.c
+pa_common\pa_trace.c
+pa_common\pa_converters.c
+pa_common\pa_cpuload.c
+pa_common\pa_dither.c
+pa_common\pa_front.c
+pa_common\pa_allocation.h
+pa_win\pa_win_util.c
+pa_win\pa_win_hostapis.c
+pa_win_wmme\pa_win_wmme.c
+
+see below for a description of these files.
+
+
+FILES:
+
+portaudio.h
+ public api header file
+
+pa_front.c
+ implements the interface defined in portaudio.h. manages multiple host apis.
+ validates function parameters before calling through to host apis. tracks
+ open streams and closes them at Pa_Terminate().
+
+pa_util.h
+ declares utility functions for use my implementations. including utility
+ functions which must be implemented separately for each platform.
+
+pa_hostapi.h
+ hostapi representation structure used to interface between pa_front.c
+ and implementations
+
+pa_stream.c/h
+ stream interface and representation structures and helper functions
+ used to interface between pa_front.c and implementations
+
+pa_cpuload.c/h
+ source and header for cpu load calculation facility
+
+pa_trace.c/h
+ source and header for debug trace log facility
+
+pa_converters.c/h
+ sample buffer conversion facility
+
+pa_dither.c/h
+ dither noise generator
+
+pa_process.c/h
+ callback buffer processing facility including interleave and block adaption
+
+pa_allocation.c/h
+ allocation context for tracking groups of allocations
+
+pa_skeleton.c
+ an skeleton implementation showing how the common code can be used.
+
+pa_win_util.c
+ Win32 implementation of platform specific PaUtil functions (memory allocation,
+ usec clock, Pa_Sleep().) The file will be used with all Win32 host APIs.
+
+pa_win_hostapis.c
+ contains the paHostApiInitializers array and an implementation of
+ Pa_GetDefaultHostApi() for win32 builds.
+
+pa_win_wmme.c
+ Win32 host api implementation for the windows multimedia extensions audio API.
+
+pa_win_wmme.h
+ public header file containing interfaces to mme-specific functions and the
+ deviceInfo data structure.
+
+
+CODING GUIDELINES:
+
+naming conventions:
+ #defines begin with PA_
+ #defines local to a file end with _
+ global utility variables begin with paUtil
+ global utility types begin with PaUtil (including function types)
+ global utility functions begin with PaUtil_
+ static variables end with _
+ static constants begin with const and end with _
+ static funtions have no special prefix/suffix
+
+In general, implementations should declare all of their members static,
+except for their initializer which should be exported. All exported names
+should be preceeded by Pa<MN>_ where MN is the module name, for example
+the windows mme initializer should be named PaWinWmme_Initialize().
+
+Every host api should define an initializer which returns an error code
+and a PaHostApiInterface*. The initializer should only return an error other
+than paNoError if it encounters an unexpected and fatal error (memory allocation
+error for example). In general, there may be conditions under which it returns
+a NULL interface pointer and also returns paNoError. For example, if the ASIO
+implementation detects that ASIO is not installed, it should return a
+NULL interface, and paNoError.
+
+Platform-specific shared functions should begin with Pa<PN>_ where PN is the
+platform name. eg. PaWin_ for windows, PaUnix_ for unix.
+
+The above two conventions should also be followed whenever it is necessary to
+share functions accross multiple source files.
+
+Two utilities for debug messages are provided. The PA_DEBUG macro defined in
+pa_implementation.h provides a simple way to print debug messages to stderr.
+Due to real-time performance issues, PA_DEBUG may not be suitable for use
+within the portaudio processing callback, or in other threads. In such cases
+the event tracing facility provided in pa_trace.h may be more appropriate.
+
+If PA_LOG_API_CALLS is defined, all calls to the public PortAudio API
+will be logged to stderr along with parameter and return values.
+
+
+TODO:
+ (this list is totally out of date)
+
+ finish coding converter functions in pa_converters.c (anyone?)
+
+ implement block adaption in pa_process.c (phil?)
+
+ fix all current tests to work with new code. this should mostly involve
+ changing PortAudioStream to PaStream, and GetDefaultDeviceID to GetDefaultDevice etc.
+
+ write some new tests to exercise the multi-api functions
+
+ write (doxygen) documentation for pa_trace (phil?)
+
+ remove unused typeids from PaHostAPITypeID
+
+ create a global configuration file which documents which PA_ defines can be
+ used for configuration
+
+ need a coding standard for comment formatting
+
+ migrate directx (phil)
+
+ migrate asio (ross?, stephane?)
+
+ see top of pa_win_wmme.c for MME todo items (ross)
+
+ write style guide document (ross)
+
+
+DESIGN ISSUES:
+ (this list is totally out of date)
+
+ consider removing Pa_ConvertHostApiDeviceIndexToGlobalDeviceIndex() from the API
+
+ switch to new latency parameter mechanism now (?)
+
+ question: if input or outputDriverInfo structures are passed for a different
+ hostApi from the one being called, do we return an error or just ignore
+ them? (i think return error)
+
+ consider renaming PortAudioCallback to PaStreamCallback
+
+ consider renaming PaError, PaResult
+
+
+ASSORTED DISORGANISED NOTES:
+
+ NOTE:
+ pa_lib.c performs the following validations for Pa_OpenStream() which we do not currently do:
+ - checks the device info to make sure that the device supports the requested sample rate,
+ it may also change the sample rate to the "closest available" sample rate if it
+ is within a particular error margin
+
+ rationale for breaking up internalPortAudioStream:
+ each implementation has its own requirements and behavior, and should be
+ able to choose the best way to operate without being limited by the
+ constraints imposed by a common infrastructure. in other words the
+ implementations should be able to pick and choose services from the
+ common infrastructure. currently identified services include:
+
+ - cpu load tracking
+ - buffering and conversion service (same code works for input and output)
+ - should support buffer multiplexing (non-integer length input and output buffers)
+ - in-place conversion where possible (only for callback, read/write always copies)
+ - should manage allocation of temporary buffers if necessary
+ - instrumentation (should be able to be disabled): callback count, framesProcessed
+ - common data: magic, streamInterface, callback, userdata
+
+
+- conversion functions:
+ - should handle temp buffer allocation
+ - dithering (random number state per-stream)
+ - buffer size mismatches
+ - with new buffer slip rules, temp buffers may always be needed
+ - we should aim for in-place conversion wherever possible
+ - does phil's code support in-place conversion? (yes)
+
+- dicuss relationship between user and host buffer sizes
+ - completely independent.. individual implementations may constrain
+ host buffer sizes if necessary
+
+
+- discuss device capabilities:
+ - i'd like to be able to request certain information:
+ - channel count for example
+
diff --git a/pjmedia/src/pjmedia/portaudio/dsound_wrapper.c b/pjmedia/src/pjmedia/portaudio/dsound_wrapper.c
index 767145b7..207d2873 100644
--- a/pjmedia/src/pjmedia/portaudio/dsound_wrapper.c
+++ b/pjmedia/src/pjmedia/portaudio/dsound_wrapper.c
@@ -1,616 +1,616 @@
-/*
- * $Id: dsound_wrapper.c,v 1.1.1.1.2.11 2003/09/07 13:04:53 rossbencina Exp $
- * Simplified DirectSound interface.
- *
- * Author: Phil Burk & Robert Marsanyi
- *
- * PortAudio Portable Real-Time Audio Library
- * For more information see: http://www.softsynth.com/portaudio/
- * DirectSound Implementation
- * Copyright (c) 1999-2000 Phil Burk & Robert Marsanyi
- *
- * 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.
- *
- * Any person wishing to distribute modifications to the Software is
- * requested to send the modifications to the original developer so that
- * they can be incorporated into the canonical version.
- *
- * 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 <stdio.h>
-#include <stdlib.h>
-#include <math.h>
-
-#include "dsound_wrapper.h"
-#include "pa_trace.h"
-
-/*
- Rather than linking with dxguid.a or using "#define INITGUID" to force a
- header file to instantiate the required GUID(s), we define them directly
- below.
-*/
-#include <initguid.h> // needed for the DEFINE_GUID macro
-DEFINE_GUID(IID_IDirectSoundNotify, 0xb0210783, 0x89cd, 0x11d0, 0xaf, 0x8, 0x0, 0xa0, 0xc9, 0x25, 0xcd, 0x16);
-
-
-/************************************************************************************/
-DSoundEntryPoints dswDSoundEntryPoints = { 0, 0, 0, 0, 0, 0, 0 };
-/************************************************************************************/
-static HRESULT WINAPI DummyDirectSoundCreate(LPGUID lpcGuidDevice, LPDIRECTSOUND *ppDS, LPUNKNOWN pUnkOuter)
-{
- (void)lpcGuidDevice; /* unused parameter */
- (void)ppDS; /* unused parameter */
- (void)pUnkOuter; /* unused parameter */
- return E_NOTIMPL;
-}
-
-static HRESULT WINAPI DummyDirectSoundEnumerateW(LPDSENUMCALLBACKW lpDSEnumCallback, LPVOID lpContext)
-{
- (void)lpDSEnumCallback; /* unused parameter */
- (void)lpContext; /* unused parameter */
- return E_NOTIMPL;
-}
-
-static HRESULT WINAPI DummyDirectSoundEnumerateA(LPDSENUMCALLBACKA lpDSEnumCallback, LPVOID lpContext)
-{
- (void)lpDSEnumCallback; /* unused parameter */
- (void)lpContext; /* unused parameter */
- return E_NOTIMPL;
-}
-
-static HRESULT WINAPI DummyDirectSoundCaptureCreate(LPGUID lpcGUID, LPDIRECTSOUNDCAPTURE *lplpDSC, LPUNKNOWN pUnkOuter)
-{
- (void)lpcGUID; /* unused parameter */
- (void)lplpDSC; /* unused parameter */
- (void)pUnkOuter; /* unused parameter */
- return E_NOTIMPL;
-}
-
-static HRESULT WINAPI DummyDirectSoundCaptureEnumerateW(LPDSENUMCALLBACKW lpDSCEnumCallback, LPVOID lpContext)
-{
- (void)lpDSCEnumCallback; /* unused parameter */
- (void)lpContext; /* unused parameter */
- return E_NOTIMPL;
-}
-
-static HRESULT WINAPI DummyDirectSoundCaptureEnumerateA(LPDSENUMCALLBACKA lpDSCEnumCallback, LPVOID lpContext)
-{
- (void)lpDSCEnumCallback; /* unused parameter */
- (void)lpContext; /* unused parameter */
- return E_NOTIMPL;
-}
-/************************************************************************************/
-void DSW_InitializeDSoundEntryPoints(void)
-{
- dswDSoundEntryPoints.hInstance_ = LoadLibrary("dsound.dll");
- if( dswDSoundEntryPoints.hInstance_ != NULL )
- {
- dswDSoundEntryPoints.DirectSoundCreate =
- (HRESULT (WINAPI *)(LPGUID, LPDIRECTSOUND *, LPUNKNOWN))
- GetProcAddress( dswDSoundEntryPoints.hInstance_, "DirectSoundCreate" );
- if( dswDSoundEntryPoints.DirectSoundCreate == NULL )
- dswDSoundEntryPoints.DirectSoundCreate = DummyDirectSoundCreate;
-
- dswDSoundEntryPoints.DirectSoundEnumerateW =
- (HRESULT (WINAPI *)(LPDSENUMCALLBACKW, LPVOID))
- GetProcAddress( dswDSoundEntryPoints.hInstance_, "DirectSoundEnumerateW" );
- if( dswDSoundEntryPoints.DirectSoundEnumerateW == NULL )
- dswDSoundEntryPoints.DirectSoundEnumerateW = DummyDirectSoundEnumerateW;
-
- dswDSoundEntryPoints.DirectSoundEnumerateA =
- (HRESULT (WINAPI *)(LPDSENUMCALLBACKA, LPVOID))
- GetProcAddress( dswDSoundEntryPoints.hInstance_, "DirectSoundEnumerateA" );
- if( dswDSoundEntryPoints.DirectSoundEnumerateA == NULL )
- dswDSoundEntryPoints.DirectSoundEnumerateA = DummyDirectSoundEnumerateA;
-
- dswDSoundEntryPoints.DirectSoundCaptureCreate =
- (HRESULT (WINAPI *)(LPGUID, LPDIRECTSOUNDCAPTURE *, LPUNKNOWN))
- GetProcAddress( dswDSoundEntryPoints.hInstance_, "DirectSoundCaptureCreate" );
- if( dswDSoundEntryPoints.DirectSoundCaptureCreate == NULL )
- dswDSoundEntryPoints.DirectSoundCaptureCreate = DummyDirectSoundCaptureCreate;
-
- dswDSoundEntryPoints.DirectSoundCaptureEnumerateW =
- (HRESULT (WINAPI *)(LPDSENUMCALLBACKW, LPVOID))
- GetProcAddress( dswDSoundEntryPoints.hInstance_, "DirectSoundCaptureEnumerateW" );
- if( dswDSoundEntryPoints.DirectSoundCaptureEnumerateW == NULL )
- dswDSoundEntryPoints.DirectSoundCaptureEnumerateW = DummyDirectSoundCaptureEnumerateW;
-
- dswDSoundEntryPoints.DirectSoundCaptureEnumerateA =
- (HRESULT (WINAPI *)(LPDSENUMCALLBACKA, LPVOID))
- GetProcAddress( dswDSoundEntryPoints.hInstance_, "DirectSoundCaptureEnumerateA" );
- if( dswDSoundEntryPoints.DirectSoundCaptureEnumerateA == NULL )
- dswDSoundEntryPoints.DirectSoundCaptureEnumerateA = DummyDirectSoundCaptureEnumerateA;
- }
- else
- {
- /* initialize with dummy entry points to make live easy when ds isn't present */
- dswDSoundEntryPoints.DirectSoundCreate = DummyDirectSoundCreate;
- dswDSoundEntryPoints.DirectSoundEnumerateW = DummyDirectSoundEnumerateW;
- dswDSoundEntryPoints.DirectSoundEnumerateA = DummyDirectSoundEnumerateA;
- dswDSoundEntryPoints.DirectSoundCaptureCreate = DummyDirectSoundCaptureCreate;
- dswDSoundEntryPoints.DirectSoundCaptureEnumerateW = DummyDirectSoundCaptureEnumerateW;
- dswDSoundEntryPoints.DirectSoundCaptureEnumerateA = DummyDirectSoundCaptureEnumerateA;
- }
-}
-/************************************************************************************/
-void DSW_TerminateDSoundEntryPoints(void)
-{
- if( dswDSoundEntryPoints.hInstance_ != NULL )
- {
- FreeLibrary( dswDSoundEntryPoints.hInstance_ );
- dswDSoundEntryPoints.hInstance_ = NULL;
- /* ensure that we crash reliably if the entry points arent initialised */
- dswDSoundEntryPoints.DirectSoundCreate = 0;
- dswDSoundEntryPoints.DirectSoundEnumerateW = 0;
- dswDSoundEntryPoints.DirectSoundEnumerateA = 0;
- dswDSoundEntryPoints.DirectSoundCaptureCreate = 0;
- dswDSoundEntryPoints.DirectSoundCaptureEnumerateW = 0;
- dswDSoundEntryPoints.DirectSoundCaptureEnumerateA = 0;
- }
-}
-/************************************************************************************/
-void DSW_Term( DSoundWrapper *dsw )
-{
- // Cleanup the sound buffers
- if (dsw->dsw_OutputBuffer)
- {
- IDirectSoundBuffer_Stop( dsw->dsw_OutputBuffer );
- IDirectSoundBuffer_Release( dsw->dsw_OutputBuffer );
- dsw->dsw_OutputBuffer = NULL;
- }
-
- if (dsw->dsw_InputBuffer)
- {
- IDirectSoundCaptureBuffer_Stop( dsw->dsw_InputBuffer );
- IDirectSoundCaptureBuffer_Release( dsw->dsw_InputBuffer );
- dsw->dsw_InputBuffer = NULL;
- }
-
- if (dsw->dsw_pDirectSoundCapture)
- {
- IDirectSoundCapture_Release( dsw->dsw_pDirectSoundCapture );
- dsw->dsw_pDirectSoundCapture = NULL;
- }
-
- if (dsw->dsw_pDirectSound)
- {
- IDirectSound_Release( dsw->dsw_pDirectSound );
- dsw->dsw_pDirectSound = NULL;
- }
-}
-/************************************************************************************/
-HRESULT DSW_Init( DSoundWrapper *dsw )
-{
- memset( dsw, 0, sizeof(DSoundWrapper) );
- return 0;
-}
-/************************************************************************************/
-HRESULT DSW_InitOutputDevice( DSoundWrapper *dsw, LPGUID lpGUID )
-{
- // Create the DS object
- HRESULT hr = dswDSoundEntryPoints.DirectSoundCreate( lpGUID, &dsw->dsw_pDirectSound, NULL );
- if( hr != DS_OK ) return hr;
- return hr;
-}
-
-/************************************************************************************/
-HRESULT DSW_InitOutputBuffer( DSoundWrapper *dsw, unsigned long nFrameRate, WORD nChannels, int bytesPerBuffer )
-{
- DWORD dwDataLen;
- DWORD playCursor;
- HRESULT result;
- LPDIRECTSOUNDBUFFER pPrimaryBuffer;
- HWND hWnd;
- HRESULT hr;
- WAVEFORMATEX wfFormat;
- DSBUFFERDESC primaryDesc;
- DSBUFFERDESC secondaryDesc;
- unsigned char* pDSBuffData;
- LARGE_INTEGER counterFrequency;
-
- dsw->dsw_OutputSize = bytesPerBuffer;
- dsw->dsw_OutputRunning = FALSE;
- dsw->dsw_OutputUnderflows = 0;
- dsw->dsw_FramesWritten = 0;
- dsw->dsw_BytesPerOutputFrame = nChannels * sizeof(short);
-
- // We were using getForegroundWindow() but sometimes the ForegroundWindow may not be the
- // applications's window. Also if that window is closed before the Buffer is closed
- // then DirectSound can crash. (Thanks for Scott Patterson for reporting this.)
- // So we will use GetDesktopWindow() which was suggested by Miller Puckette.
- // hWnd = GetForegroundWindow();
- //
- // FIXME: The example code I have on the net creates a hidden window that
- // is managed by our code - I think we should do that - one hidden
- // window for the whole of Pa_DS
- //
- hWnd = GetDesktopWindow();
-
- // Set cooperative level to DSSCL_EXCLUSIVE so that we can get 16 bit output, 44.1 KHz.
- // Exclusize also prevents unexpected sounds from other apps during a performance.
- if ((hr = IDirectSound_SetCooperativeLevel( dsw->dsw_pDirectSound,
- hWnd, DSSCL_EXCLUSIVE)) != DS_OK)
- {
- return hr;
- }
-
- // -----------------------------------------------------------------------
- // Create primary buffer and set format just so we can specify our custom format.
- // Otherwise we would be stuck with the default which might be 8 bit or 22050 Hz.
- // Setup the primary buffer description
- ZeroMemory(&primaryDesc, sizeof(DSBUFFERDESC));
- primaryDesc.dwSize = sizeof(DSBUFFERDESC);
- primaryDesc.dwFlags = DSBCAPS_PRIMARYBUFFER; // all panning, mixing, etc done by synth
- primaryDesc.dwBufferBytes = 0;
- primaryDesc.lpwfxFormat = NULL;
- // Create the buffer
- if ((result = IDirectSound_CreateSoundBuffer( dsw->dsw_pDirectSound,
- &primaryDesc, &pPrimaryBuffer, NULL)) != DS_OK) return result;
- // Define the buffer format
- wfFormat.wFormatTag = WAVE_FORMAT_PCM;
- wfFormat.nChannels = nChannels;
- wfFormat.nSamplesPerSec = nFrameRate;
- wfFormat.wBitsPerSample = 8 * sizeof(short);
- wfFormat.nBlockAlign = (WORD)(wfFormat.nChannels * (wfFormat.wBitsPerSample / 8));
- wfFormat.nAvgBytesPerSec = wfFormat.nSamplesPerSec * wfFormat.nBlockAlign;
- wfFormat.cbSize = 0; /* No extended format info. */
- // Set the primary buffer's format
- if((result = IDirectSoundBuffer_SetFormat( pPrimaryBuffer, &wfFormat)) != DS_OK) return result;
-
- // ----------------------------------------------------------------------
- // Setup the secondary buffer description
- ZeroMemory(&secondaryDesc, sizeof(DSBUFFERDESC));
- secondaryDesc.dwSize = sizeof(DSBUFFERDESC);
- secondaryDesc.dwFlags = DSBCAPS_GLOBALFOCUS | DSBCAPS_GETCURRENTPOSITION2;
- secondaryDesc.dwBufferBytes = bytesPerBuffer;
- secondaryDesc.lpwfxFormat = &wfFormat;
- // Create the secondary buffer
- if ((result = IDirectSound_CreateSoundBuffer( dsw->dsw_pDirectSound,
- &secondaryDesc, &dsw->dsw_OutputBuffer, NULL)) != DS_OK) return result;
- // Lock the DS buffer
- if ((result = IDirectSoundBuffer_Lock( dsw->dsw_OutputBuffer, 0, dsw->dsw_OutputSize, (LPVOID*)&pDSBuffData,
- &dwDataLen, NULL, 0, 0)) != DS_OK) return result;
- // Zero the DS buffer
- ZeroMemory(pDSBuffData, dwDataLen);
- // Unlock the DS buffer
- if ((result = IDirectSoundBuffer_Unlock( dsw->dsw_OutputBuffer, pDSBuffData, dwDataLen, NULL, 0)) != DS_OK) return result;
- if( QueryPerformanceFrequency( &counterFrequency ) )
- {
- int framesInBuffer = bytesPerBuffer / (nChannels * sizeof(short));
- dsw->dsw_CounterTicksPerBuffer.QuadPart = (counterFrequency.QuadPart * framesInBuffer) / nFrameRate;
- }
- else
- {
- dsw->dsw_CounterTicksPerBuffer.QuadPart = 0;
- }
- // Let DSound set the starting write position because if we set it to zero, it looks like the
- // buffer is full to begin with. This causes a long pause before sound starts when using large buffers.
- hr = IDirectSoundBuffer_GetCurrentPosition( dsw->dsw_OutputBuffer, &playCursor, &dsw->dsw_WriteOffset );
- if( hr != DS_OK )
- {
- return hr;
- }
- dsw->dsw_FramesWritten = dsw->dsw_WriteOffset / dsw->dsw_BytesPerOutputFrame;
- /* printf("DSW_InitOutputBuffer: playCursor = %d, writeCursor = %d\n", playCursor, dsw->dsw_WriteOffset ); */
- return DS_OK;
-}
-
-/************************************************************************************/
-HRESULT DSW_StartOutput( DSoundWrapper *dsw )
-{
- HRESULT hr;
- QueryPerformanceCounter( &dsw->dsw_LastPlayTime );
- dsw->dsw_LastPlayCursor = 0;
- dsw->dsw_FramesPlayed = 0;
- hr = IDirectSoundBuffer_SetCurrentPosition( dsw->dsw_OutputBuffer, 0 );
- if( hr != DS_OK )
- {
- return hr;
- }
- // Start the buffer playback in a loop.
- if( dsw->dsw_OutputBuffer != NULL )
- {
- hr = IDirectSoundBuffer_Play( dsw->dsw_OutputBuffer, 0, 0, DSBPLAY_LOOPING );
- if( hr != DS_OK )
- {
- return hr;
- }
- dsw->dsw_OutputRunning = TRUE;
- }
-
- return 0;
-}
-/************************************************************************************/
-HRESULT DSW_StopOutput( DSoundWrapper *dsw )
-{
- // Stop the buffer playback
- if( dsw->dsw_OutputBuffer != NULL )
- {
- dsw->dsw_OutputRunning = FALSE;
- return IDirectSoundBuffer_Stop( dsw->dsw_OutputBuffer );
- }
- else return 0;
-}
-
-/************************************************************************************/
-HRESULT DSW_QueryOutputFilled( DSoundWrapper *dsw, long *bytesFilledPtr )
-{
- HRESULT hr;
- DWORD playCursor;
- DWORD writeCursor;
- long bytesFilled;
- // Query to see where play position is.
- // We don't need the writeCursor but sometimes DirectSound doesn't handle NULLS correctly
- // so let's pass a pointer just to be safe.
- hr = IDirectSoundBuffer_GetCurrentPosition( dsw->dsw_OutputBuffer, &playCursor, &writeCursor );
- if( hr != DS_OK )
- {
- return hr;
- }
- bytesFilled = dsw->dsw_WriteOffset - playCursor;
- if( bytesFilled < 0 ) bytesFilled += dsw->dsw_OutputSize; // unwrap offset
- *bytesFilledPtr = bytesFilled;
- return hr;
-}
-
-/************************************************************************************
- * Determine how much space can be safely written to in DS buffer.
- * Detect underflows and overflows.
- * Does not allow writing into safety gap maintained by DirectSound.
- */
-HRESULT DSW_QueryOutputSpace( DSoundWrapper *dsw, long *bytesEmpty )
-{
- HRESULT hr;
- DWORD playCursor;
- DWORD writeCursor;
- long numBytesEmpty;
- long playWriteGap;
- // Query to see how much room is in buffer.
- hr = IDirectSoundBuffer_GetCurrentPosition( dsw->dsw_OutputBuffer, &playCursor, &writeCursor );
- if( hr != DS_OK )
- {
- return hr;
- }
- // Determine size of gap between playIndex and WriteIndex that we cannot write into.
- playWriteGap = writeCursor - playCursor;
- if( playWriteGap < 0 ) playWriteGap += dsw->dsw_OutputSize; // unwrap
- /* DirectSound doesn't have a large enough playCursor so we cannot detect wrap-around. */
- /* Attempt to detect playCursor wrap-around and correct it. */
- if( dsw->dsw_OutputRunning && (dsw->dsw_CounterTicksPerBuffer.QuadPart != 0) )
- {
- /* How much time has elapsed since last check. */
- LARGE_INTEGER currentTime;
- LARGE_INTEGER elapsedTime;
- long bytesPlayed;
- long bytesExpected;
- long buffersWrapped;
- QueryPerformanceCounter( &currentTime );
- elapsedTime.QuadPart = currentTime.QuadPart - dsw->dsw_LastPlayTime.QuadPart;
- dsw->dsw_LastPlayTime = currentTime;
- /* How many bytes does DirectSound say have been played. */
- bytesPlayed = playCursor - dsw->dsw_LastPlayCursor;
- if( bytesPlayed < 0 ) bytesPlayed += dsw->dsw_OutputSize; // unwrap
- dsw->dsw_LastPlayCursor = playCursor;
- /* Calculate how many bytes we would have expected to been played by now. */
- bytesExpected = (long) ((elapsedTime.QuadPart * dsw->dsw_OutputSize) / dsw->dsw_CounterTicksPerBuffer.QuadPart);
- buffersWrapped = (bytesExpected - bytesPlayed) / dsw->dsw_OutputSize;
- if( buffersWrapped > 0 )
- {
- playCursor += (buffersWrapped * dsw->dsw_OutputSize);
- bytesPlayed += (buffersWrapped * dsw->dsw_OutputSize);
- }
- /* Maintain frame output cursor. */
- dsw->dsw_FramesPlayed += (bytesPlayed / dsw->dsw_BytesPerOutputFrame);
- }
- numBytesEmpty = playCursor - dsw->dsw_WriteOffset;
- if( numBytesEmpty < 0 ) numBytesEmpty += dsw->dsw_OutputSize; // unwrap offset
- /* Have we underflowed? */
- if( numBytesEmpty > (dsw->dsw_OutputSize - playWriteGap) )
- {
- if( dsw->dsw_OutputRunning )
- {
- dsw->dsw_OutputUnderflows += 1;
- }
- dsw->dsw_WriteOffset = writeCursor;
- numBytesEmpty = dsw->dsw_OutputSize - playWriteGap;
- }
- *bytesEmpty = numBytesEmpty;
- return hr;
-}
-
-/************************************************************************************/
-HRESULT DSW_ZeroEmptySpace( DSoundWrapper *dsw )
-{
- HRESULT hr;
- LPBYTE lpbuf1 = NULL;
- LPBYTE lpbuf2 = NULL;
- DWORD dwsize1 = 0;
- DWORD dwsize2 = 0;
- long bytesEmpty;
- hr = DSW_QueryOutputSpace( dsw, &bytesEmpty ); // updates dsw_FramesPlayed
- if (hr != DS_OK) return hr;
- if( bytesEmpty == 0 ) return DS_OK;
- // Lock free space in the DS
- hr = IDirectSoundBuffer_Lock( dsw->dsw_OutputBuffer, dsw->dsw_WriteOffset, bytesEmpty, (void **) &lpbuf1, &dwsize1,
- (void **) &lpbuf2, &dwsize2, 0);
- if (hr == DS_OK)
- {
- // Copy the buffer into the DS
- ZeroMemory(lpbuf1, dwsize1);
- if(lpbuf2 != NULL)
- {
- ZeroMemory(lpbuf2, dwsize2);
- }
- // Update our buffer offset and unlock sound buffer
- dsw->dsw_WriteOffset = (dsw->dsw_WriteOffset + dwsize1 + dwsize2) % dsw->dsw_OutputSize;
- IDirectSoundBuffer_Unlock( dsw->dsw_OutputBuffer, lpbuf1, dwsize1, lpbuf2, dwsize2);
- dsw->dsw_FramesWritten += bytesEmpty / dsw->dsw_BytesPerOutputFrame;
- }
- return hr;
-}
-
-/************************************************************************************/
-HRESULT DSW_WriteBlock( DSoundWrapper *dsw, char *buf, long numBytes )
-{
- HRESULT hr;
- LPBYTE lpbuf1 = NULL;
- LPBYTE lpbuf2 = NULL;
- DWORD dwsize1 = 0;
- DWORD dwsize2 = 0;
- // Lock free space in the DS
- hr = IDirectSoundBuffer_Lock( dsw->dsw_OutputBuffer, dsw->dsw_WriteOffset, numBytes, (void **) &lpbuf1, &dwsize1,
- (void **) &lpbuf2, &dwsize2, 0);
- if (hr == DS_OK)
- {
- // Copy the buffer into the DS
- CopyMemory(lpbuf1, buf, dwsize1);
- if(lpbuf2 != NULL)
- {
- CopyMemory(lpbuf2, buf+dwsize1, dwsize2);
- }
- // Update our buffer offset and unlock sound buffer
- dsw->dsw_WriteOffset = (dsw->dsw_WriteOffset + dwsize1 + dwsize2) % dsw->dsw_OutputSize;
- IDirectSoundBuffer_Unlock( dsw->dsw_OutputBuffer, lpbuf1, dwsize1, lpbuf2, dwsize2);
- dsw->dsw_FramesWritten += numBytes / dsw->dsw_BytesPerOutputFrame;
- }
- return hr;
-}
-
-/************************************************************************************/
-DWORD DSW_GetOutputStatus( DSoundWrapper *dsw )
-{
- DWORD status;
- if (IDirectSoundBuffer_GetStatus( dsw->dsw_OutputBuffer, &status ) != DS_OK)
- return( DSERR_INVALIDPARAM );
- else
- return( status );
-}
-
-/* These routines are used to support audio input.
- * Do NOT compile these calls when using NT4 because it does
- * not support the entry points.
- */
-/************************************************************************************/
-HRESULT DSW_InitInputDevice( DSoundWrapper *dsw, LPGUID lpGUID )
-{
- HRESULT hr = dswDSoundEntryPoints.DirectSoundCaptureCreate( lpGUID, &dsw->dsw_pDirectSoundCapture, NULL );
- if( hr != DS_OK ) return hr;
- return hr;
-}
-/************************************************************************************/
-HRESULT DSW_InitInputBuffer( DSoundWrapper *dsw, unsigned long nFrameRate, WORD nChannels, int bytesPerBuffer )
-{
- DSCBUFFERDESC captureDesc;
- WAVEFORMATEX wfFormat;
- HRESULT result;
-
- dsw->dsw_BytesPerInputFrame = nChannels * sizeof(short);
-
- // Define the buffer format
- wfFormat.wFormatTag = WAVE_FORMAT_PCM;
- wfFormat.nChannels = nChannels;
- wfFormat.nSamplesPerSec = nFrameRate;
- wfFormat.wBitsPerSample = 8 * sizeof(short);
- wfFormat.nBlockAlign = (WORD)(wfFormat.nChannels * (wfFormat.wBitsPerSample / 8));
- wfFormat.nAvgBytesPerSec = wfFormat.nSamplesPerSec * wfFormat.nBlockAlign;
- wfFormat.cbSize = 0; /* No extended format info. */
- dsw->dsw_InputSize = bytesPerBuffer;
- // ----------------------------------------------------------------------
- // Setup the secondary buffer description
- ZeroMemory(&captureDesc, sizeof(DSCBUFFERDESC));
- captureDesc.dwSize = sizeof(DSCBUFFERDESC);
- captureDesc.dwFlags = 0;
- captureDesc.dwBufferBytes = bytesPerBuffer;
- captureDesc.lpwfxFormat = &wfFormat;
- // Create the capture buffer
- if ((result = IDirectSoundCapture_CreateCaptureBuffer( dsw->dsw_pDirectSoundCapture,
- &captureDesc, &dsw->dsw_InputBuffer, NULL)) != DS_OK) return result;
- dsw->dsw_ReadOffset = 0; // reset last read position to start of buffer
- return DS_OK;
-}
-
-/************************************************************************************/
-HRESULT DSW_StartInput( DSoundWrapper *dsw )
-{
- // Start the buffer playback
- if( dsw->dsw_InputBuffer != NULL )
- {
- return IDirectSoundCaptureBuffer_Start( dsw->dsw_InputBuffer, DSCBSTART_LOOPING );
- }
- else return 0;
-}
-
-/************************************************************************************/
-HRESULT DSW_StopInput( DSoundWrapper *dsw )
-{
- // Stop the buffer playback
- if( dsw->dsw_InputBuffer != NULL )
- {
- return IDirectSoundCaptureBuffer_Stop( dsw->dsw_InputBuffer );
- }
- else return 0;
-}
-
-/************************************************************************************/
-HRESULT DSW_QueryInputFilled( DSoundWrapper *dsw, long *bytesFilled )
-{
- HRESULT hr;
- DWORD capturePos;
- DWORD readPos;
- long filled;
- // Query to see how much data is in buffer.
- // We don't need the capture position but sometimes DirectSound doesn't handle NULLS correctly
- // so let's pass a pointer just to be safe.
- hr = IDirectSoundCaptureBuffer_GetCurrentPosition( dsw->dsw_InputBuffer, &capturePos, &readPos );
- if( hr != DS_OK )
- {
- return hr;
- }
- filled = readPos - dsw->dsw_ReadOffset;
- if( filled < 0 ) filled += dsw->dsw_InputSize; // unwrap offset
- *bytesFilled = filled;
- return hr;
-}
-
-/************************************************************************************/
-HRESULT DSW_ReadBlock( DSoundWrapper *dsw, char *buf, long numBytes )
-{
- HRESULT hr;
- LPBYTE lpbuf1 = NULL;
- LPBYTE lpbuf2 = NULL;
- DWORD dwsize1 = 0;
- DWORD dwsize2 = 0;
- // Lock free space in the DS
- hr = IDirectSoundCaptureBuffer_Lock ( dsw->dsw_InputBuffer, dsw->dsw_ReadOffset, numBytes, (void **) &lpbuf1, &dwsize1,
- (void **) &lpbuf2, &dwsize2, 0);
- if (hr == DS_OK)
- {
- // Copy from DS to the buffer
- CopyMemory( buf, lpbuf1, dwsize1);
- if(lpbuf2 != NULL)
- {
- CopyMemory( buf+dwsize1, lpbuf2, dwsize2);
- }
- // Update our buffer offset and unlock sound buffer
- dsw->dsw_ReadOffset = (dsw->dsw_ReadOffset + dwsize1 + dwsize2) % dsw->dsw_InputSize;
- IDirectSoundCaptureBuffer_Unlock ( dsw->dsw_InputBuffer, lpbuf1, dwsize1, lpbuf2, dwsize2);
- }
- return hr;
-}
-
+/*
+ * $Id: dsound_wrapper.c,v 1.1.1.1.2.11 2003/09/07 13:04:53 rossbencina Exp $
+ * Simplified DirectSound interface.
+ *
+ * Author: Phil Burk & Robert Marsanyi
+ *
+ * PortAudio Portable Real-Time Audio Library
+ * For more information see: http://www.softsynth.com/portaudio/
+ * DirectSound Implementation
+ * Copyright (c) 1999-2000 Phil Burk & Robert Marsanyi
+ *
+ * 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.
+ *
+ * Any person wishing to distribute modifications to the Software is
+ * requested to send the modifications to the original developer so that
+ * they can be incorporated into the canonical version.
+ *
+ * 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 <stdio.h>
+#include <stdlib.h>
+#include <math.h>
+
+#include "dsound_wrapper.h"
+#include "pa_trace.h"
+
+/*
+ Rather than linking with dxguid.a or using "#define INITGUID" to force a
+ header file to instantiate the required GUID(s), we define them directly
+ below.
+*/
+#include <initguid.h> // needed for the DEFINE_GUID macro
+DEFINE_GUID(IID_IDirectSoundNotify, 0xb0210783, 0x89cd, 0x11d0, 0xaf, 0x8, 0x0, 0xa0, 0xc9, 0x25, 0xcd, 0x16);
+
+
+/************************************************************************************/
+DSoundEntryPoints dswDSoundEntryPoints = { 0, 0, 0, 0, 0, 0, 0 };
+/************************************************************************************/
+static HRESULT WINAPI DummyDirectSoundCreate(LPGUID lpcGuidDevice, LPDIRECTSOUND *ppDS, LPUNKNOWN pUnkOuter)
+{
+ (void)lpcGuidDevice; /* unused parameter */
+ (void)ppDS; /* unused parameter */
+ (void)pUnkOuter; /* unused parameter */
+ return E_NOTIMPL;
+}
+
+static HRESULT WINAPI DummyDirectSoundEnumerateW(LPDSENUMCALLBACKW lpDSEnumCallback, LPVOID lpContext)
+{
+ (void)lpDSEnumCallback; /* unused parameter */
+ (void)lpContext; /* unused parameter */
+ return E_NOTIMPL;
+}
+
+static HRESULT WINAPI DummyDirectSoundEnumerateA(LPDSENUMCALLBACKA lpDSEnumCallback, LPVOID lpContext)
+{
+ (void)lpDSEnumCallback; /* unused parameter */
+ (void)lpContext; /* unused parameter */
+ return E_NOTIMPL;
+}
+
+static HRESULT WINAPI DummyDirectSoundCaptureCreate(LPGUID lpcGUID, LPDIRECTSOUNDCAPTURE *lplpDSC, LPUNKNOWN pUnkOuter)
+{
+ (void)lpcGUID; /* unused parameter */
+ (void)lplpDSC; /* unused parameter */
+ (void)pUnkOuter; /* unused parameter */
+ return E_NOTIMPL;
+}
+
+static HRESULT WINAPI DummyDirectSoundCaptureEnumerateW(LPDSENUMCALLBACKW lpDSCEnumCallback, LPVOID lpContext)
+{
+ (void)lpDSCEnumCallback; /* unused parameter */
+ (void)lpContext; /* unused parameter */
+ return E_NOTIMPL;
+}
+
+static HRESULT WINAPI DummyDirectSoundCaptureEnumerateA(LPDSENUMCALLBACKA lpDSCEnumCallback, LPVOID lpContext)
+{
+ (void)lpDSCEnumCallback; /* unused parameter */
+ (void)lpContext; /* unused parameter */
+ return E_NOTIMPL;
+}
+/************************************************************************************/
+void DSW_InitializeDSoundEntryPoints(void)
+{
+ dswDSoundEntryPoints.hInstance_ = LoadLibrary("dsound.dll");
+ if( dswDSoundEntryPoints.hInstance_ != NULL )
+ {
+ dswDSoundEntryPoints.DirectSoundCreate =
+ (HRESULT (WINAPI *)(LPGUID, LPDIRECTSOUND *, LPUNKNOWN))
+ GetProcAddress( dswDSoundEntryPoints.hInstance_, "DirectSoundCreate" );
+ if( dswDSoundEntryPoints.DirectSoundCreate == NULL )
+ dswDSoundEntryPoints.DirectSoundCreate = DummyDirectSoundCreate;
+
+ dswDSoundEntryPoints.DirectSoundEnumerateW =
+ (HRESULT (WINAPI *)(LPDSENUMCALLBACKW, LPVOID))
+ GetProcAddress( dswDSoundEntryPoints.hInstance_, "DirectSoundEnumerateW" );
+ if( dswDSoundEntryPoints.DirectSoundEnumerateW == NULL )
+ dswDSoundEntryPoints.DirectSoundEnumerateW = DummyDirectSoundEnumerateW;
+
+ dswDSoundEntryPoints.DirectSoundEnumerateA =
+ (HRESULT (WINAPI *)(LPDSENUMCALLBACKA, LPVOID))
+ GetProcAddress( dswDSoundEntryPoints.hInstance_, "DirectSoundEnumerateA" );
+ if( dswDSoundEntryPoints.DirectSoundEnumerateA == NULL )
+ dswDSoundEntryPoints.DirectSoundEnumerateA = DummyDirectSoundEnumerateA;
+
+ dswDSoundEntryPoints.DirectSoundCaptureCreate =
+ (HRESULT (WINAPI *)(LPGUID, LPDIRECTSOUNDCAPTURE *, LPUNKNOWN))
+ GetProcAddress( dswDSoundEntryPoints.hInstance_, "DirectSoundCaptureCreate" );
+ if( dswDSoundEntryPoints.DirectSoundCaptureCreate == NULL )
+ dswDSoundEntryPoints.DirectSoundCaptureCreate = DummyDirectSoundCaptureCreate;
+
+ dswDSoundEntryPoints.DirectSoundCaptureEnumerateW =
+ (HRESULT (WINAPI *)(LPDSENUMCALLBACKW, LPVOID))
+ GetProcAddress( dswDSoundEntryPoints.hInstance_, "DirectSoundCaptureEnumerateW" );
+ if( dswDSoundEntryPoints.DirectSoundCaptureEnumerateW == NULL )
+ dswDSoundEntryPoints.DirectSoundCaptureEnumerateW = DummyDirectSoundCaptureEnumerateW;
+
+ dswDSoundEntryPoints.DirectSoundCaptureEnumerateA =
+ (HRESULT (WINAPI *)(LPDSENUMCALLBACKA, LPVOID))
+ GetProcAddress( dswDSoundEntryPoints.hInstance_, "DirectSoundCaptureEnumerateA" );
+ if( dswDSoundEntryPoints.DirectSoundCaptureEnumerateA == NULL )
+ dswDSoundEntryPoints.DirectSoundCaptureEnumerateA = DummyDirectSoundCaptureEnumerateA;
+ }
+ else
+ {
+ /* initialize with dummy entry points to make live easy when ds isn't present */
+ dswDSoundEntryPoints.DirectSoundCreate = DummyDirectSoundCreate;
+ dswDSoundEntryPoints.DirectSoundEnumerateW = DummyDirectSoundEnumerateW;
+ dswDSoundEntryPoints.DirectSoundEnumerateA = DummyDirectSoundEnumerateA;
+ dswDSoundEntryPoints.DirectSoundCaptureCreate = DummyDirectSoundCaptureCreate;
+ dswDSoundEntryPoints.DirectSoundCaptureEnumerateW = DummyDirectSoundCaptureEnumerateW;
+ dswDSoundEntryPoints.DirectSoundCaptureEnumerateA = DummyDirectSoundCaptureEnumerateA;
+ }
+}
+/************************************************************************************/
+void DSW_TerminateDSoundEntryPoints(void)
+{
+ if( dswDSoundEntryPoints.hInstance_ != NULL )
+ {
+ FreeLibrary( dswDSoundEntryPoints.hInstance_ );
+ dswDSoundEntryPoints.hInstance_ = NULL;
+ /* ensure that we crash reliably if the entry points arent initialised */
+ dswDSoundEntryPoints.DirectSoundCreate = 0;
+ dswDSoundEntryPoints.DirectSoundEnumerateW = 0;
+ dswDSoundEntryPoints.DirectSoundEnumerateA = 0;
+ dswDSoundEntryPoints.DirectSoundCaptureCreate = 0;
+ dswDSoundEntryPoints.DirectSoundCaptureEnumerateW = 0;
+ dswDSoundEntryPoints.DirectSoundCaptureEnumerateA = 0;
+ }
+}
+/************************************************************************************/
+void DSW_Term( DSoundWrapper *dsw )
+{
+ // Cleanup the sound buffers
+ if (dsw->dsw_OutputBuffer)
+ {
+ IDirectSoundBuffer_Stop( dsw->dsw_OutputBuffer );
+ IDirectSoundBuffer_Release( dsw->dsw_OutputBuffer );
+ dsw->dsw_OutputBuffer = NULL;
+ }
+
+ if (dsw->dsw_InputBuffer)
+ {
+ IDirectSoundCaptureBuffer_Stop( dsw->dsw_InputBuffer );
+ IDirectSoundCaptureBuffer_Release( dsw->dsw_InputBuffer );
+ dsw->dsw_InputBuffer = NULL;
+ }
+
+ if (dsw->dsw_pDirectSoundCapture)
+ {
+ IDirectSoundCapture_Release( dsw->dsw_pDirectSoundCapture );
+ dsw->dsw_pDirectSoundCapture = NULL;
+ }
+
+ if (dsw->dsw_pDirectSound)
+ {
+ IDirectSound_Release( dsw->dsw_pDirectSound );
+ dsw->dsw_pDirectSound = NULL;
+ }
+}
+/************************************************************************************/
+HRESULT DSW_Init( DSoundWrapper *dsw )
+{
+ memset( dsw, 0, sizeof(DSoundWrapper) );
+ return 0;
+}
+/************************************************************************************/
+HRESULT DSW_InitOutputDevice( DSoundWrapper *dsw, LPGUID lpGUID )
+{
+ // Create the DS object
+ HRESULT hr = dswDSoundEntryPoints.DirectSoundCreate( lpGUID, &dsw->dsw_pDirectSound, NULL );
+ if( hr != DS_OK ) return hr;
+ return hr;
+}
+
+/************************************************************************************/
+HRESULT DSW_InitOutputBuffer( DSoundWrapper *dsw, unsigned long nFrameRate, WORD nChannels, int bytesPerBuffer )
+{
+ DWORD dwDataLen;
+ DWORD playCursor;
+ HRESULT result;
+ LPDIRECTSOUNDBUFFER pPrimaryBuffer;
+ HWND hWnd;
+ HRESULT hr;
+ WAVEFORMATEX wfFormat;
+ DSBUFFERDESC primaryDesc;
+ DSBUFFERDESC secondaryDesc;
+ unsigned char* pDSBuffData;
+ LARGE_INTEGER counterFrequency;
+
+ dsw->dsw_OutputSize = bytesPerBuffer;
+ dsw->dsw_OutputRunning = FALSE;
+ dsw->dsw_OutputUnderflows = 0;
+ dsw->dsw_FramesWritten = 0;
+ dsw->dsw_BytesPerOutputFrame = nChannels * sizeof(short);
+
+ // We were using getForegroundWindow() but sometimes the ForegroundWindow may not be the
+ // applications's window. Also if that window is closed before the Buffer is closed
+ // then DirectSound can crash. (Thanks for Scott Patterson for reporting this.)
+ // So we will use GetDesktopWindow() which was suggested by Miller Puckette.
+ // hWnd = GetForegroundWindow();
+ //
+ // FIXME: The example code I have on the net creates a hidden window that
+ // is managed by our code - I think we should do that - one hidden
+ // window for the whole of Pa_DS
+ //
+ hWnd = GetDesktopWindow();
+
+ // Set cooperative level to DSSCL_EXCLUSIVE so that we can get 16 bit output, 44.1 KHz.
+ // Exclusize also prevents unexpected sounds from other apps during a performance.
+ if ((hr = IDirectSound_SetCooperativeLevel( dsw->dsw_pDirectSound,
+ hWnd, DSSCL_EXCLUSIVE)) != DS_OK)
+ {
+ return hr;
+ }
+
+ // -----------------------------------------------------------------------
+ // Create primary buffer and set format just so we can specify our custom format.
+ // Otherwise we would be stuck with the default which might be 8 bit or 22050 Hz.
+ // Setup the primary buffer description
+ ZeroMemory(&primaryDesc, sizeof(DSBUFFERDESC));
+ primaryDesc.dwSize = sizeof(DSBUFFERDESC);
+ primaryDesc.dwFlags = DSBCAPS_PRIMARYBUFFER; // all panning, mixing, etc done by synth
+ primaryDesc.dwBufferBytes = 0;
+ primaryDesc.lpwfxFormat = NULL;
+ // Create the buffer
+ if ((result = IDirectSound_CreateSoundBuffer( dsw->dsw_pDirectSound,
+ &primaryDesc, &pPrimaryBuffer, NULL)) != DS_OK) return result;
+ // Define the buffer format
+ wfFormat.wFormatTag = WAVE_FORMAT_PCM;
+ wfFormat.nChannels = nChannels;
+ wfFormat.nSamplesPerSec = nFrameRate;
+ wfFormat.wBitsPerSample = 8 * sizeof(short);
+ wfFormat.nBlockAlign = (WORD)(wfFormat.nChannels * (wfFormat.wBitsPerSample / 8));
+ wfFormat.nAvgBytesPerSec = wfFormat.nSamplesPerSec * wfFormat.nBlockAlign;
+ wfFormat.cbSize = 0; /* No extended format info. */
+ // Set the primary buffer's format
+ if((result = IDirectSoundBuffer_SetFormat( pPrimaryBuffer, &wfFormat)) != DS_OK) return result;
+
+ // ----------------------------------------------------------------------
+ // Setup the secondary buffer description
+ ZeroMemory(&secondaryDesc, sizeof(DSBUFFERDESC));
+ secondaryDesc.dwSize = sizeof(DSBUFFERDESC);
+ secondaryDesc.dwFlags = DSBCAPS_GLOBALFOCUS | DSBCAPS_GETCURRENTPOSITION2;
+ secondaryDesc.dwBufferBytes = bytesPerBuffer;
+ secondaryDesc.lpwfxFormat = &wfFormat;
+ // Create the secondary buffer
+ if ((result = IDirectSound_CreateSoundBuffer( dsw->dsw_pDirectSound,
+ &secondaryDesc, &dsw->dsw_OutputBuffer, NULL)) != DS_OK) return result;
+ // Lock the DS buffer
+ if ((result = IDirectSoundBuffer_Lock( dsw->dsw_OutputBuffer, 0, dsw->dsw_OutputSize, (LPVOID*)&pDSBuffData,
+ &dwDataLen, NULL, 0, 0)) != DS_OK) return result;
+ // Zero the DS buffer
+ ZeroMemory(pDSBuffData, dwDataLen);
+ // Unlock the DS buffer
+ if ((result = IDirectSoundBuffer_Unlock( dsw->dsw_OutputBuffer, pDSBuffData, dwDataLen, NULL, 0)) != DS_OK) return result;
+ if( QueryPerformanceFrequency( &counterFrequency ) )
+ {
+ int framesInBuffer = bytesPerBuffer / (nChannels * sizeof(short));
+ dsw->dsw_CounterTicksPerBuffer.QuadPart = (counterFrequency.QuadPart * framesInBuffer) / nFrameRate;
+ }
+ else
+ {
+ dsw->dsw_CounterTicksPerBuffer.QuadPart = 0;
+ }
+ // Let DSound set the starting write position because if we set it to zero, it looks like the
+ // buffer is full to begin with. This causes a long pause before sound starts when using large buffers.
+ hr = IDirectSoundBuffer_GetCurrentPosition( dsw->dsw_OutputBuffer, &playCursor, &dsw->dsw_WriteOffset );
+ if( hr != DS_OK )
+ {
+ return hr;
+ }
+ dsw->dsw_FramesWritten = dsw->dsw_WriteOffset / dsw->dsw_BytesPerOutputFrame;
+ /* printf("DSW_InitOutputBuffer: playCursor = %d, writeCursor = %d\n", playCursor, dsw->dsw_WriteOffset ); */
+ return DS_OK;
+}
+
+/************************************************************************************/
+HRESULT DSW_StartOutput( DSoundWrapper *dsw )
+{
+ HRESULT hr;
+ QueryPerformanceCounter( &dsw->dsw_LastPlayTime );
+ dsw->dsw_LastPlayCursor = 0;
+ dsw->dsw_FramesPlayed = 0;
+ hr = IDirectSoundBuffer_SetCurrentPosition( dsw->dsw_OutputBuffer, 0 );
+ if( hr != DS_OK )
+ {
+ return hr;
+ }
+ // Start the buffer playback in a loop.
+ if( dsw->dsw_OutputBuffer != NULL )
+ {
+ hr = IDirectSoundBuffer_Play( dsw->dsw_OutputBuffer, 0, 0, DSBPLAY_LOOPING );
+ if( hr != DS_OK )
+ {
+ return hr;
+ }
+ dsw->dsw_OutputRunning = TRUE;
+ }
+
+ return 0;
+}
+/************************************************************************************/
+HRESULT DSW_StopOutput( DSoundWrapper *dsw )
+{
+ // Stop the buffer playback
+ if( dsw->dsw_OutputBuffer != NULL )
+ {
+ dsw->dsw_OutputRunning = FALSE;
+ return IDirectSoundBuffer_Stop( dsw->dsw_OutputBuffer );
+ }
+ else return 0;
+}
+
+/************************************************************************************/
+HRESULT DSW_QueryOutputFilled( DSoundWrapper *dsw, long *bytesFilledPtr )
+{
+ HRESULT hr;
+ DWORD playCursor;
+ DWORD writeCursor;
+ long bytesFilled;
+ // Query to see where play position is.
+ // We don't need the writeCursor but sometimes DirectSound doesn't handle NULLS correctly
+ // so let's pass a pointer just to be safe.
+ hr = IDirectSoundBuffer_GetCurrentPosition( dsw->dsw_OutputBuffer, &playCursor, &writeCursor );
+ if( hr != DS_OK )
+ {
+ return hr;
+ }
+ bytesFilled = dsw->dsw_WriteOffset - playCursor;
+ if( bytesFilled < 0 ) bytesFilled += dsw->dsw_OutputSize; // unwrap offset
+ *bytesFilledPtr = bytesFilled;
+ return hr;
+}
+
+/************************************************************************************
+ * Determine how much space can be safely written to in DS buffer.
+ * Detect underflows and overflows.
+ * Does not allow writing into safety gap maintained by DirectSound.
+ */
+HRESULT DSW_QueryOutputSpace( DSoundWrapper *dsw, long *bytesEmpty )
+{
+ HRESULT hr;
+ DWORD playCursor;
+ DWORD writeCursor;
+ long numBytesEmpty;
+ long playWriteGap;
+ // Query to see how much room is in buffer.
+ hr = IDirectSoundBuffer_GetCurrentPosition( dsw->dsw_OutputBuffer, &playCursor, &writeCursor );
+ if( hr != DS_OK )
+ {
+ return hr;
+ }
+ // Determine size of gap between playIndex and WriteIndex that we cannot write into.
+ playWriteGap = writeCursor - playCursor;
+ if( playWriteGap < 0 ) playWriteGap += dsw->dsw_OutputSize; // unwrap
+ /* DirectSound doesn't have a large enough playCursor so we cannot detect wrap-around. */
+ /* Attempt to detect playCursor wrap-around and correct it. */
+ if( dsw->dsw_OutputRunning && (dsw->dsw_CounterTicksPerBuffer.QuadPart != 0) )
+ {
+ /* How much time has elapsed since last check. */
+ LARGE_INTEGER currentTime;
+ LARGE_INTEGER elapsedTime;
+ long bytesPlayed;
+ long bytesExpected;
+ long buffersWrapped;
+ QueryPerformanceCounter( &currentTime );
+ elapsedTime.QuadPart = currentTime.QuadPart - dsw->dsw_LastPlayTime.QuadPart;
+ dsw->dsw_LastPlayTime = currentTime;
+ /* How many bytes does DirectSound say have been played. */
+ bytesPlayed = playCursor - dsw->dsw_LastPlayCursor;
+ if( bytesPlayed < 0 ) bytesPlayed += dsw->dsw_OutputSize; // unwrap
+ dsw->dsw_LastPlayCursor = playCursor;
+ /* Calculate how many bytes we would have expected to been played by now. */
+ bytesExpected = (long) ((elapsedTime.QuadPart * dsw->dsw_OutputSize) / dsw->dsw_CounterTicksPerBuffer.QuadPart);
+ buffersWrapped = (bytesExpected - bytesPlayed) / dsw->dsw_OutputSize;
+ if( buffersWrapped > 0 )
+ {
+ playCursor += (buffersWrapped * dsw->dsw_OutputSize);
+ bytesPlayed += (buffersWrapped * dsw->dsw_OutputSize);
+ }
+ /* Maintain frame output cursor. */
+ dsw->dsw_FramesPlayed += (bytesPlayed / dsw->dsw_BytesPerOutputFrame);
+ }
+ numBytesEmpty = playCursor - dsw->dsw_WriteOffset;
+ if( numBytesEmpty < 0 ) numBytesEmpty += dsw->dsw_OutputSize; // unwrap offset
+ /* Have we underflowed? */
+ if( numBytesEmpty > (dsw->dsw_OutputSize - playWriteGap) )
+ {
+ if( dsw->dsw_OutputRunning )
+ {
+ dsw->dsw_OutputUnderflows += 1;
+ }
+ dsw->dsw_WriteOffset = writeCursor;
+ numBytesEmpty = dsw->dsw_OutputSize - playWriteGap;
+ }
+ *bytesEmpty = numBytesEmpty;
+ return hr;
+}
+
+/************************************************************************************/
+HRESULT DSW_ZeroEmptySpace( DSoundWrapper *dsw )
+{
+ HRESULT hr;
+ LPBYTE lpbuf1 = NULL;
+ LPBYTE lpbuf2 = NULL;
+ DWORD dwsize1 = 0;
+ DWORD dwsize2 = 0;
+ long bytesEmpty;
+ hr = DSW_QueryOutputSpace( dsw, &bytesEmpty ); // updates dsw_FramesPlayed
+ if (hr != DS_OK) return hr;
+ if( bytesEmpty == 0 ) return DS_OK;
+ // Lock free space in the DS
+ hr = IDirectSoundBuffer_Lock( dsw->dsw_OutputBuffer, dsw->dsw_WriteOffset, bytesEmpty, (void **) &lpbuf1, &dwsize1,
+ (void **) &lpbuf2, &dwsize2, 0);
+ if (hr == DS_OK)
+ {
+ // Copy the buffer into the DS
+ ZeroMemory(lpbuf1, dwsize1);
+ if(lpbuf2 != NULL)
+ {
+ ZeroMemory(lpbuf2, dwsize2);
+ }
+ // Update our buffer offset and unlock sound buffer
+ dsw->dsw_WriteOffset = (dsw->dsw_WriteOffset + dwsize1 + dwsize2) % dsw->dsw_OutputSize;
+ IDirectSoundBuffer_Unlock( dsw->dsw_OutputBuffer, lpbuf1, dwsize1, lpbuf2, dwsize2);
+ dsw->dsw_FramesWritten += bytesEmpty / dsw->dsw_BytesPerOutputFrame;
+ }
+ return hr;
+}
+
+/************************************************************************************/
+HRESULT DSW_WriteBlock( DSoundWrapper *dsw, char *buf, long numBytes )
+{
+ HRESULT hr;
+ LPBYTE lpbuf1 = NULL;
+ LPBYTE lpbuf2 = NULL;
+ DWORD dwsize1 = 0;
+ DWORD dwsize2 = 0;
+ // Lock free space in the DS
+ hr = IDirectSoundBuffer_Lock( dsw->dsw_OutputBuffer, dsw->dsw_WriteOffset, numBytes, (void **) &lpbuf1, &dwsize1,
+ (void **) &lpbuf2, &dwsize2, 0);
+ if (hr == DS_OK)
+ {
+ // Copy the buffer into the DS
+ CopyMemory(lpbuf1, buf, dwsize1);
+ if(lpbuf2 != NULL)
+ {
+ CopyMemory(lpbuf2, buf+dwsize1, dwsize2);
+ }
+ // Update our buffer offset and unlock sound buffer
+ dsw->dsw_WriteOffset = (dsw->dsw_WriteOffset + dwsize1 + dwsize2) % dsw->dsw_OutputSize;
+ IDirectSoundBuffer_Unlock( dsw->dsw_OutputBuffer, lpbuf1, dwsize1, lpbuf2, dwsize2);
+ dsw->dsw_FramesWritten += numBytes / dsw->dsw_BytesPerOutputFrame;
+ }
+ return hr;
+}
+
+/************************************************************************************/
+DWORD DSW_GetOutputStatus( DSoundWrapper *dsw )
+{
+ DWORD status;
+ if (IDirectSoundBuffer_GetStatus( dsw->dsw_OutputBuffer, &status ) != DS_OK)
+ return( DSERR_INVALIDPARAM );
+ else
+ return( status );
+}
+
+/* These routines are used to support audio input.
+ * Do NOT compile these calls when using NT4 because it does
+ * not support the entry points.
+ */
+/************************************************************************************/
+HRESULT DSW_InitInputDevice( DSoundWrapper *dsw, LPGUID lpGUID )
+{
+ HRESULT hr = dswDSoundEntryPoints.DirectSoundCaptureCreate( lpGUID, &dsw->dsw_pDirectSoundCapture, NULL );
+ if( hr != DS_OK ) return hr;
+ return hr;
+}
+/************************************************************************************/
+HRESULT DSW_InitInputBuffer( DSoundWrapper *dsw, unsigned long nFrameRate, WORD nChannels, int bytesPerBuffer )
+{
+ DSCBUFFERDESC captureDesc;
+ WAVEFORMATEX wfFormat;
+ HRESULT result;
+
+ dsw->dsw_BytesPerInputFrame = nChannels * sizeof(short);
+
+ // Define the buffer format
+ wfFormat.wFormatTag = WAVE_FORMAT_PCM;
+ wfFormat.nChannels = nChannels;
+ wfFormat.nSamplesPerSec = nFrameRate;
+ wfFormat.wBitsPerSample = 8 * sizeof(short);
+ wfFormat.nBlockAlign = (WORD)(wfFormat.nChannels * (wfFormat.wBitsPerSample / 8));
+ wfFormat.nAvgBytesPerSec = wfFormat.nSamplesPerSec * wfFormat.nBlockAlign;
+ wfFormat.cbSize = 0; /* No extended format info. */
+ dsw->dsw_InputSize = bytesPerBuffer;
+ // ----------------------------------------------------------------------
+ // Setup the secondary buffer description
+ ZeroMemory(&captureDesc, sizeof(DSCBUFFERDESC));
+ captureDesc.dwSize = sizeof(DSCBUFFERDESC);
+ captureDesc.dwFlags = 0;
+ captureDesc.dwBufferBytes = bytesPerBuffer;
+ captureDesc.lpwfxFormat = &wfFormat;
+ // Create the capture buffer
+ if ((result = IDirectSoundCapture_CreateCaptureBuffer( dsw->dsw_pDirectSoundCapture,
+ &captureDesc, &dsw->dsw_InputBuffer, NULL)) != DS_OK) return result;
+ dsw->dsw_ReadOffset = 0; // reset last read position to start of buffer
+ return DS_OK;
+}
+
+/************************************************************************************/
+HRESULT DSW_StartInput( DSoundWrapper *dsw )
+{
+ // Start the buffer playback
+ if( dsw->dsw_InputBuffer != NULL )
+ {
+ return IDirectSoundCaptureBuffer_Start( dsw->dsw_InputBuffer, DSCBSTART_LOOPING );
+ }
+ else return 0;
+}
+
+/************************************************************************************/
+HRESULT DSW_StopInput( DSoundWrapper *dsw )
+{
+ // Stop the buffer playback
+ if( dsw->dsw_InputBuffer != NULL )
+ {
+ return IDirectSoundCaptureBuffer_Stop( dsw->dsw_InputBuffer );
+ }
+ else return 0;
+}
+
+/************************************************************************************/
+HRESULT DSW_QueryInputFilled( DSoundWrapper *dsw, long *bytesFilled )
+{
+ HRESULT hr;
+ DWORD capturePos;
+ DWORD readPos;
+ long filled;
+ // Query to see how much data is in buffer.
+ // We don't need the capture position but sometimes DirectSound doesn't handle NULLS correctly
+ // so let's pass a pointer just to be safe.
+ hr = IDirectSoundCaptureBuffer_GetCurrentPosition( dsw->dsw_InputBuffer, &capturePos, &readPos );
+ if( hr != DS_OK )
+ {
+ return hr;
+ }
+ filled = readPos - dsw->dsw_ReadOffset;
+ if( filled < 0 ) filled += dsw->dsw_InputSize; // unwrap offset
+ *bytesFilled = filled;
+ return hr;
+}
+
+/************************************************************************************/
+HRESULT DSW_ReadBlock( DSoundWrapper *dsw, char *buf, long numBytes )
+{
+ HRESULT hr;
+ LPBYTE lpbuf1 = NULL;
+ LPBYTE lpbuf2 = NULL;
+ DWORD dwsize1 = 0;
+ DWORD dwsize2 = 0;
+ // Lock free space in the DS
+ hr = IDirectSoundCaptureBuffer_Lock ( dsw->dsw_InputBuffer, dsw->dsw_ReadOffset, numBytes, (void **) &lpbuf1, &dwsize1,
+ (void **) &lpbuf2, &dwsize2, 0);
+ if (hr == DS_OK)
+ {
+ // Copy from DS to the buffer
+ CopyMemory( buf, lpbuf1, dwsize1);
+ if(lpbuf2 != NULL)
+ {
+ CopyMemory( buf+dwsize1, lpbuf2, dwsize2);
+ }
+ // Update our buffer offset and unlock sound buffer
+ dsw->dsw_ReadOffset = (dsw->dsw_ReadOffset + dwsize1 + dwsize2) % dsw->dsw_InputSize;
+ IDirectSoundCaptureBuffer_Unlock ( dsw->dsw_InputBuffer, lpbuf1, dwsize1, lpbuf2, dwsize2);
+ }
+ return hr;
+}
+
diff --git a/pjmedia/src/pjmedia/portaudio/dsound_wrapper.h b/pjmedia/src/pjmedia/portaudio/dsound_wrapper.h
index 76087908..9e3f565f 100644
--- a/pjmedia/src/pjmedia/portaudio/dsound_wrapper.h
+++ b/pjmedia/src/pjmedia/portaudio/dsound_wrapper.h
@@ -1,130 +1,130 @@
-#ifndef __DSOUND_WRAPPER_H
-#define __DSOUND_WRAPPER_H
-/*
- * $Id: dsound_wrapper.h,v 1.1.1.1.2.8 2005/01/16 20:48:37 rossbencina Exp $
- * Simplified DirectSound interface.
- *
- * Author: Phil Burk & Robert Marsanyi
- *
- * For PortAudio Portable Real-Time Audio Library
- * For more information see: http://www.softsynth.com/portaudio/
- * DirectSound Implementation
- * Copyright (c) 1999-2000 Phil Burk & Robert Marsanyi
- *
- * 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.
- *
- * Any person wishing to distribute modifications to the Software is
- * requested to send the modifications to the original developer so that
- * they can be incorporated into the canonical version.
- *
- * 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.
- *
- */
-
-/* on Borland compilers, WIN32 doesn't seem to be defined by default, which
- breaks DSound.h. Adding the define here fixes the problem. - rossb. */
-#ifdef __BORLANDC__
-#if !defined(WIN32)
-#define WIN32
-#endif
-#endif
-
-/*
- We are only using DX3 in here, no need to polute the namespace - davidv
-*/
-#define DIRECTSOUND_VERSION 0x0300
-
-#include <DSound.h>
-
-#ifdef __cplusplus
-extern "C"
-{
-#endif /* __cplusplus */
-
-
-typedef struct
-{
- HINSTANCE hInstance_;
-
- HRESULT (WINAPI *DirectSoundCreate)(LPGUID, LPDIRECTSOUND *, LPUNKNOWN);
- HRESULT (WINAPI *DirectSoundEnumerateW)(LPDSENUMCALLBACKW, LPVOID);
- HRESULT (WINAPI *DirectSoundEnumerateA)(LPDSENUMCALLBACKA, LPVOID);
-
- HRESULT (WINAPI *DirectSoundCaptureCreate)(LPGUID, LPDIRECTSOUNDCAPTURE *, LPUNKNOWN);
- HRESULT (WINAPI *DirectSoundCaptureEnumerateW)(LPDSENUMCALLBACKW, LPVOID);
- HRESULT (WINAPI *DirectSoundCaptureEnumerateA)(LPDSENUMCALLBACKA, LPVOID);
-}DSoundEntryPoints;
-
-extern DSoundEntryPoints dswDSoundEntryPoints;
-
-void DSW_InitializeDSoundEntryPoints(void);
-void DSW_TerminateDSoundEntryPoints(void);
-
-#define DSW_NUM_POSITIONS (4)
-#define DSW_NUM_EVENTS (5)
-#define DSW_TERMINATION_EVENT (DSW_NUM_POSITIONS)
-
-typedef struct
-{
-/* Output */
- LPDIRECTSOUND dsw_pDirectSound;
- LPDIRECTSOUNDBUFFER dsw_OutputBuffer;
- DWORD dsw_WriteOffset; /* last write position */
- INT dsw_OutputSize;
- INT dsw_BytesPerOutputFrame;
- /* Try to detect play buffer underflows. */
- LARGE_INTEGER dsw_CounterTicksPerBuffer; /* counter ticks it should take to play a full buffer */
- LARGE_INTEGER dsw_LastPlayTime;
- UINT dsw_LastPlayCursor;
- UINT dsw_OutputUnderflows;
- BOOL dsw_OutputRunning;
- /* use double which lets us can play for several thousand years with enough precision */
- double dsw_FramesWritten;
- double dsw_FramesPlayed;
-/* Input */
- INT dsw_BytesPerInputFrame;
- LPDIRECTSOUNDCAPTURE dsw_pDirectSoundCapture;
- LPDIRECTSOUNDCAPTUREBUFFER dsw_InputBuffer;
- UINT dsw_ReadOffset; /* last read position */
- UINT dsw_InputSize;
-} DSoundWrapper;
-
-HRESULT DSW_Init( DSoundWrapper *dsw );
-void DSW_Term( DSoundWrapper *dsw );
-HRESULT DSW_InitOutputBuffer( DSoundWrapper *dsw, unsigned long nFrameRate,
- WORD nChannels, int bufSize );
-HRESULT DSW_StartOutput( DSoundWrapper *dsw );
-HRESULT DSW_StopOutput( DSoundWrapper *dsw );
-DWORD DSW_GetOutputStatus( DSoundWrapper *dsw );
-HRESULT DSW_WriteBlock( DSoundWrapper *dsw, char *buf, long numBytes );
-HRESULT DSW_ZeroEmptySpace( DSoundWrapper *dsw );
-HRESULT DSW_QueryOutputSpace( DSoundWrapper *dsw, long *bytesEmpty );
-HRESULT DSW_Enumerate( DSoundWrapper *dsw );
-
-HRESULT DSW_InitInputBuffer( DSoundWrapper *dsw, unsigned long nFrameRate,
- WORD nChannels, int bufSize );
-HRESULT DSW_StartInput( DSoundWrapper *dsw );
-HRESULT DSW_StopInput( DSoundWrapper *dsw );
-HRESULT DSW_ReadBlock( DSoundWrapper *dsw, char *buf, long numBytes );
-HRESULT DSW_QueryInputFilled( DSoundWrapper *dsw, long *bytesFilled );
-HRESULT DSW_QueryOutputFilled( DSoundWrapper *dsw, long *bytesFilled );
-
-#ifdef __cplusplus
-}
-#endif /* __cplusplus */
-#endif /* __DSOUND_WRAPPER_H */
+#ifndef __DSOUND_WRAPPER_H
+#define __DSOUND_WRAPPER_H
+/*
+ * $Id: dsound_wrapper.h,v 1.1.1.1.2.8 2005/01/16 20:48:37 rossbencina Exp $
+ * Simplified DirectSound interface.
+ *
+ * Author: Phil Burk & Robert Marsanyi
+ *
+ * For PortAudio Portable Real-Time Audio Library
+ * For more information see: http://www.softsynth.com/portaudio/
+ * DirectSound Implementation
+ * Copyright (c) 1999-2000 Phil Burk & Robert Marsanyi
+ *
+ * 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.
+ *
+ * Any person wishing to distribute modifications to the Software is
+ * requested to send the modifications to the original developer so that
+ * they can be incorporated into the canonical version.
+ *
+ * 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.
+ *
+ */
+
+/* on Borland compilers, WIN32 doesn't seem to be defined by default, which
+ breaks DSound.h. Adding the define here fixes the problem. - rossb. */
+#ifdef __BORLANDC__
+#if !defined(WIN32)
+#define WIN32
+#endif
+#endif
+
+/*
+ We are only using DX3 in here, no need to polute the namespace - davidv
+*/
+#define DIRECTSOUND_VERSION 0x0300
+
+#include <DSound.h>
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif /* __cplusplus */
+
+
+typedef struct
+{
+ HINSTANCE hInstance_;
+
+ HRESULT (WINAPI *DirectSoundCreate)(LPGUID, LPDIRECTSOUND *, LPUNKNOWN);
+ HRESULT (WINAPI *DirectSoundEnumerateW)(LPDSENUMCALLBACKW, LPVOID);
+ HRESULT (WINAPI *DirectSoundEnumerateA)(LPDSENUMCALLBACKA, LPVOID);
+
+ HRESULT (WINAPI *DirectSoundCaptureCreate)(LPGUID, LPDIRECTSOUNDCAPTURE *, LPUNKNOWN);
+ HRESULT (WINAPI *DirectSoundCaptureEnumerateW)(LPDSENUMCALLBACKW, LPVOID);
+ HRESULT (WINAPI *DirectSoundCaptureEnumerateA)(LPDSENUMCALLBACKA, LPVOID);
+}DSoundEntryPoints;
+
+extern DSoundEntryPoints dswDSoundEntryPoints;
+
+void DSW_InitializeDSoundEntryPoints(void);
+void DSW_TerminateDSoundEntryPoints(void);
+
+#define DSW_NUM_POSITIONS (4)
+#define DSW_NUM_EVENTS (5)
+#define DSW_TERMINATION_EVENT (DSW_NUM_POSITIONS)
+
+typedef struct
+{
+/* Output */
+ LPDIRECTSOUND dsw_pDirectSound;
+ LPDIRECTSOUNDBUFFER dsw_OutputBuffer;
+ DWORD dsw_WriteOffset; /* last write position */
+ INT dsw_OutputSize;
+ INT dsw_BytesPerOutputFrame;
+ /* Try to detect play buffer underflows. */
+ LARGE_INTEGER dsw_CounterTicksPerBuffer; /* counter ticks it should take to play a full buffer */
+ LARGE_INTEGER dsw_LastPlayTime;
+ UINT dsw_LastPlayCursor;
+ UINT dsw_OutputUnderflows;
+ BOOL dsw_OutputRunning;
+ /* use double which lets us can play for several thousand years with enough precision */
+ double dsw_FramesWritten;
+ double dsw_FramesPlayed;
+/* Input */
+ INT dsw_BytesPerInputFrame;
+ LPDIRECTSOUNDCAPTURE dsw_pDirectSoundCapture;
+ LPDIRECTSOUNDCAPTUREBUFFER dsw_InputBuffer;
+ UINT dsw_ReadOffset; /* last read position */
+ UINT dsw_InputSize;
+} DSoundWrapper;
+
+HRESULT DSW_Init( DSoundWrapper *dsw );
+void DSW_Term( DSoundWrapper *dsw );
+HRESULT DSW_InitOutputBuffer( DSoundWrapper *dsw, unsigned long nFrameRate,
+ WORD nChannels, int bufSize );
+HRESULT DSW_StartOutput( DSoundWrapper *dsw );
+HRESULT DSW_StopOutput( DSoundWrapper *dsw );
+DWORD DSW_GetOutputStatus( DSoundWrapper *dsw );
+HRESULT DSW_WriteBlock( DSoundWrapper *dsw, char *buf, long numBytes );
+HRESULT DSW_ZeroEmptySpace( DSoundWrapper *dsw );
+HRESULT DSW_QueryOutputSpace( DSoundWrapper *dsw, long *bytesEmpty );
+HRESULT DSW_Enumerate( DSoundWrapper *dsw );
+
+HRESULT DSW_InitInputBuffer( DSoundWrapper *dsw, unsigned long nFrameRate,
+ WORD nChannels, int bufSize );
+HRESULT DSW_StartInput( DSoundWrapper *dsw );
+HRESULT DSW_StopInput( DSoundWrapper *dsw );
+HRESULT DSW_ReadBlock( DSoundWrapper *dsw, char *buf, long numBytes );
+HRESULT DSW_QueryInputFilled( DSoundWrapper *dsw, long *bytesFilled );
+HRESULT DSW_QueryOutputFilled( DSoundWrapper *dsw, long *bytesFilled );
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+#endif /* __DSOUND_WRAPPER_H */
diff --git a/pjmedia/src/pjmedia/portaudio/pa_allocation.c b/pjmedia/src/pjmedia/portaudio/pa_allocation.c
index 8d02e416..035b4d0b 100644
--- a/pjmedia/src/pjmedia/portaudio/pa_allocation.c
+++ b/pjmedia/src/pjmedia/portaudio/pa_allocation.c
@@ -1,234 +1,234 @@
-/*
- * $Id: pa_allocation.c,v 1.1.2.6 2004/12/20 12:07:51 rossbencina Exp $
- * Portable Audio I/O Library allocation group implementation
- * memory allocation group for tracking allocation groups
- *
- * Based on the Open Source API proposed by Ross Bencina
- * Copyright (c) 1999-2002 Ross Bencina, Phil Burk
- *
- * 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.
- *
- * Any person wishing to distribute modifications to the Software is
- * requested to send the modifications to the original developer so that
- * they can be incorporated into the canonical version.
- *
- * 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.
- */
-
-/** @file
- @brief Allocation Group implementation.
-*/
-
-
-#include "pa_allocation.h"
-#include "pa_util.h"
-
-
-/*
- Maintain 3 singly linked lists...
- linkBlocks: the buffers used to allocate the links
- spareLinks: links available for use in the allocations list
- allocations: the buffers currently allocated using PaUtil_ContextAllocateMemory()
-
- Link block size is doubled every time new links are allocated.
-*/
-
-
-#define PA_INITIAL_LINK_COUNT_ 16
-
-struct PaUtilAllocationGroupLink
-{
- struct PaUtilAllocationGroupLink *next;
- void *buffer;
-};
-
-/*
- Allocate a block of links. The first link will have it's buffer member
- pointing to the block, and it's next member set to <nextBlock>. The remaining
- links will have NULL buffer members, and each link will point to
- the next link except the last, which will point to <nextSpare>
-*/
-static struct PaUtilAllocationGroupLink *AllocateLinks( long count,
- struct PaUtilAllocationGroupLink *nextBlock,
- struct PaUtilAllocationGroupLink *nextSpare )
-{
- struct PaUtilAllocationGroupLink *result;
- int i;
-
- result = (struct PaUtilAllocationGroupLink *)PaUtil_AllocateMemory(
- sizeof(struct PaUtilAllocationGroupLink) * count );
- if( result )
- {
- /* the block link */
- result[0].buffer = result;
- result[0].next = nextBlock;
-
- /* the spare links */
- for( i=1; i<count; ++i )
- {
- result[i].buffer = 0;
- result[i].next = &result[i+1];
- }
- result[count-1].next = nextSpare;
- }
-
- return result;
-}
-
-
-PaUtilAllocationGroup* PaUtil_CreateAllocationGroup( void )
-{
- PaUtilAllocationGroup* result = 0;
- struct PaUtilAllocationGroupLink *links;
-
-
- links = AllocateLinks( PA_INITIAL_LINK_COUNT_, 0, 0 );
- if( links != 0 )
- {
- result = (PaUtilAllocationGroup*)PaUtil_AllocateMemory( sizeof(PaUtilAllocationGroup) );
- if( result )
- {
- result->linkCount = PA_INITIAL_LINK_COUNT_;
- result->linkBlocks = &links[0];
- result->spareLinks = &links[1];
- result->allocations = 0;
- }
- else
- {
- PaUtil_FreeMemory( links );
- }
- }
-
- return result;
-}
-
-
-void PaUtil_DestroyAllocationGroup( PaUtilAllocationGroup* group )
-{
- struct PaUtilAllocationGroupLink *current = group->linkBlocks;
- struct PaUtilAllocationGroupLink *next;
-
- while( current )
- {
- next = current->next;
- PaUtil_FreeMemory( current->buffer );
- current = next;
- }
-
- PaUtil_FreeMemory( group );
-}
-
-
-void* PaUtil_GroupAllocateMemory( PaUtilAllocationGroup* group, long size )
-{
- struct PaUtilAllocationGroupLink *links, *link;
- void *result = 0;
-
- /* allocate more links if necessary */
- if( !group->spareLinks )
- {
- /* double the link count on each block allocation */
- links = AllocateLinks( group->linkCount, group->linkBlocks, group->spareLinks );
- if( links )
- {
- group->linkCount += group->linkCount;
- group->linkBlocks = &links[0];
- group->spareLinks = &links[1];
- }
- }
-
- if( group->spareLinks )
- {
- result = PaUtil_AllocateMemory( size );
- if( result )
- {
- link = group->spareLinks;
- group->spareLinks = link->next;
-
- link->buffer = result;
- link->next = group->allocations;
-
- group->allocations = link;
- }
- }
-
- return result;
-}
-
-
-void PaUtil_GroupFreeMemory( PaUtilAllocationGroup* group, void *buffer )
-{
- struct PaUtilAllocationGroupLink *current = group->allocations;
- struct PaUtilAllocationGroupLink *previous = 0;
-
- if( buffer == 0 )
- return;
-
- /* find the right link and remove it */
- while( current )
- {
- if( current->buffer == buffer )
- {
- if( previous )
- {
- previous->next = current->next;
- }
- else
- {
- group->allocations = current->next;
- }
-
- current->buffer = 0;
- current->next = group->spareLinks;
- group->spareLinks = current;
-
- break;
- }
-
- previous = current;
- current = current->next;
- }
-
- PaUtil_FreeMemory( buffer ); /* free the memory whether we found it in the list or not */
-}
-
-
-void PaUtil_FreeAllAllocations( PaUtilAllocationGroup* group )
-{
- struct PaUtilAllocationGroupLink *current = group->allocations;
- struct PaUtilAllocationGroupLink *previous = 0;
-
- /* free all buffers in the allocations list */
- while( current )
- {
- PaUtil_FreeMemory( current->buffer );
- current->buffer = 0;
-
- previous = current;
- current = current->next;
- }
-
- /* link the former allocations list onto the front of the spareLinks list */
- if( previous )
- {
- previous->next = group->spareLinks;
- group->spareLinks = group->allocations;
- group->allocations = 0;
- }
-}
-
+/*
+ * $Id: pa_allocation.c,v 1.1.2.6 2004/12/20 12:07:51 rossbencina Exp $
+ * Portable Audio I/O Library allocation group implementation
+ * memory allocation group for tracking allocation groups
+ *
+ * Based on the Open Source API proposed by Ross Bencina
+ * Copyright (c) 1999-2002 Ross Bencina, Phil Burk
+ *
+ * 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.
+ *
+ * Any person wishing to distribute modifications to the Software is
+ * requested to send the modifications to the original developer so that
+ * they can be incorporated into the canonical version.
+ *
+ * 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.
+ */
+
+/** @file
+ @brief Allocation Group implementation.
+*/
+
+
+#include "pa_allocation.h"
+#include "pa_util.h"
+
+
+/*
+ Maintain 3 singly linked lists...
+ linkBlocks: the buffers used to allocate the links
+ spareLinks: links available for use in the allocations list
+ allocations: the buffers currently allocated using PaUtil_ContextAllocateMemory()
+
+ Link block size is doubled every time new links are allocated.
+*/
+
+
+#define PA_INITIAL_LINK_COUNT_ 16
+
+struct PaUtilAllocationGroupLink
+{
+ struct PaUtilAllocationGroupLink *next;
+ void *buffer;
+};
+
+/*
+ Allocate a block of links. The first link will have it's buffer member
+ pointing to the block, and it's next member set to <nextBlock>. The remaining
+ links will have NULL buffer members, and each link will point to
+ the next link except the last, which will point to <nextSpare>
+*/
+static struct PaUtilAllocationGroupLink *AllocateLinks( long count,
+ struct PaUtilAllocationGroupLink *nextBlock,
+ struct PaUtilAllocationGroupLink *nextSpare )
+{
+ struct PaUtilAllocationGroupLink *result;
+ int i;
+
+ result = (struct PaUtilAllocationGroupLink *)PaUtil_AllocateMemory(
+ sizeof(struct PaUtilAllocationGroupLink) * count );
+ if( result )
+ {
+ /* the block link */
+ result[0].buffer = result;
+ result[0].next = nextBlock;
+
+ /* the spare links */
+ for( i=1; i<count; ++i )
+ {
+ result[i].buffer = 0;
+ result[i].next = &result[i+1];
+ }
+ result[count-1].next = nextSpare;
+ }
+
+ return result;
+}
+
+
+PaUtilAllocationGroup* PaUtil_CreateAllocationGroup( void )
+{
+ PaUtilAllocationGroup* result = 0;
+ struct PaUtilAllocationGroupLink *links;
+
+
+ links = AllocateLinks( PA_INITIAL_LINK_COUNT_, 0, 0 );
+ if( links != 0 )
+ {
+ result = (PaUtilAllocationGroup*)PaUtil_AllocateMemory( sizeof(PaUtilAllocationGroup) );
+ if( result )
+ {
+ result->linkCount = PA_INITIAL_LINK_COUNT_;
+ result->linkBlocks = &links[0];
+ result->spareLinks = &links[1];
+ result->allocations = 0;
+ }
+ else
+ {
+ PaUtil_FreeMemory( links );
+ }
+ }
+
+ return result;
+}
+
+
+void PaUtil_DestroyAllocationGroup( PaUtilAllocationGroup* group )
+{
+ struct PaUtilAllocationGroupLink *current = group->linkBlocks;
+ struct PaUtilAllocationGroupLink *next;
+
+ while( current )
+ {
+ next = current->next;
+ PaUtil_FreeMemory( current->buffer );
+ current = next;
+ }
+
+ PaUtil_FreeMemory( group );
+}
+
+
+void* PaUtil_GroupAllocateMemory( PaUtilAllocationGroup* group, long size )
+{
+ struct PaUtilAllocationGroupLink *links, *link;
+ void *result = 0;
+
+ /* allocate more links if necessary */
+ if( !group->spareLinks )
+ {
+ /* double the link count on each block allocation */
+ links = AllocateLinks( group->linkCount, group->linkBlocks, group->spareLinks );
+ if( links )
+ {
+ group->linkCount += group->linkCount;
+ group->linkBlocks = &links[0];
+ group->spareLinks = &links[1];
+ }
+ }
+
+ if( group->spareLinks )
+ {
+ result = PaUtil_AllocateMemory( size );
+ if( result )
+ {
+ link = group->spareLinks;
+ group->spareLinks = link->next;
+
+ link->buffer = result;
+ link->next = group->allocations;
+
+ group->allocations = link;
+ }
+ }
+
+ return result;
+}
+
+
+void PaUtil_GroupFreeMemory( PaUtilAllocationGroup* group, void *buffer )
+{
+ struct PaUtilAllocationGroupLink *current = group->allocations;
+ struct PaUtilAllocationGroupLink *previous = 0;
+
+ if( buffer == 0 )
+ return;
+
+ /* find the right link and remove it */
+ while( current )
+ {
+ if( current->buffer == buffer )
+ {
+ if( previous )
+ {
+ previous->next = current->next;
+ }
+ else
+ {
+ group->allocations = current->next;
+ }
+
+ current->buffer = 0;
+ current->next = group->spareLinks;
+ group->spareLinks = current;
+
+ break;
+ }
+
+ previous = current;
+ current = current->next;
+ }
+
+ PaUtil_FreeMemory( buffer ); /* free the memory whether we found it in the list or not */
+}
+
+
+void PaUtil_FreeAllAllocations( PaUtilAllocationGroup* group )
+{
+ struct PaUtilAllocationGroupLink *current = group->allocations;
+ struct PaUtilAllocationGroupLink *previous = 0;
+
+ /* free all buffers in the allocations list */
+ while( current )
+ {
+ PaUtil_FreeMemory( current->buffer );
+ current->buffer = 0;
+
+ previous = current;
+ current = current->next;
+ }
+
+ /* link the former allocations list onto the front of the spareLinks list */
+ if( previous )
+ {
+ previous->next = group->spareLinks;
+ group->spareLinks = group->allocations;
+ group->allocations = 0;
+ }
+}
+
diff --git a/pjmedia/src/pjmedia/portaudio/pa_allocation.h b/pjmedia/src/pjmedia/portaudio/pa_allocation.h
index 13b9ee32..fb9321a0 100644
--- a/pjmedia/src/pjmedia/portaudio/pa_allocation.h
+++ b/pjmedia/src/pjmedia/portaudio/pa_allocation.h
@@ -1,95 +1,95 @@
-#ifndef PA_ALLOCATION_H
-#define PA_ALLOCATION_H
-/*
- * $Id: pa_allocation.h,v 1.1.2.4 2003/09/20 21:04:44 rossbencina Exp $
- * Portable Audio I/O Library allocation context header
- * memory allocation context for tracking allocation groups
- *
- * Based on the Open Source API proposed by Ross Bencina
- * Copyright (c) 1999-2002 Ross Bencina, Phil Burk
- *
- * 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.
- *
- * Any person wishing to distribute modifications to the Software is
- * requested to send the modifications to the original developer so that
- * they can be incorporated into the canonical version.
- *
- * 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.
- */
-
-/** @file
- @brief Allocation Group prototypes. An Allocation Group makes it easy to
- allocate multiple blocks of memory and free them all simultanously.
-
- An allocation group is useful for keeping track of multiple blocks
- of memory which are allocated at the same time (such as during initialization)
- and need to be deallocated at the same time. The allocation group maintains
- a list of allocated blocks, and can deallocate them all simultaneously which
- can be usefull for cleaning up after a partially initialized object fails.
-
- The allocation group implementation is built on top of the lower
- level allocation functions defined in pa_util.h
-*/
-
-
-#ifdef __cplusplus
-extern "C"
-{
-#endif /* __cplusplus */
-
-
-typedef struct
-{
- long linkCount;
- struct PaUtilAllocationGroupLink *linkBlocks;
- struct PaUtilAllocationGroupLink *spareLinks;
- struct PaUtilAllocationGroupLink *allocations;
-}PaUtilAllocationGroup;
-
-
-
-/** Create an allocation group.
-*/
-PaUtilAllocationGroup* PaUtil_CreateAllocationGroup( void );
-
-/** Destroy an allocation group, but not the memory allocated through the group.
-*/
-void PaUtil_DestroyAllocationGroup( PaUtilAllocationGroup* group );
-
-/** Allocate a block of memory though an allocation group.
-*/
-void* PaUtil_GroupAllocateMemory( PaUtilAllocationGroup* group, long size );
-
-/** Free a block of memory that was previously allocated though an allocation
- group. Calling this function is a relatively time consuming operation.
- Under normal circumstances clients should call PaUtil_FreeAllAllocations to
- free all allocated blocks simultaneously.
- @see PaUtil_FreeAllAllocations
-*/
-void PaUtil_GroupFreeMemory( PaUtilAllocationGroup* group, void *buffer );
-
-/** Free all blocks of memory which have been allocated through the allocation
- group. This function doesn't destroy the group itself.
-*/
-void PaUtil_FreeAllAllocations( PaUtilAllocationGroup* group );
-
-
-#ifdef __cplusplus
-}
-#endif /* __cplusplus */
-#endif /* PA_ALLOCATION_H */
+#ifndef PA_ALLOCATION_H
+#define PA_ALLOCATION_H
+/*
+ * $Id: pa_allocation.h,v 1.1.2.4 2003/09/20 21:04:44 rossbencina Exp $
+ * Portable Audio I/O Library allocation context header
+ * memory allocation context for tracking allocation groups
+ *
+ * Based on the Open Source API proposed by Ross Bencina
+ * Copyright (c) 1999-2002 Ross Bencina, Phil Burk
+ *
+ * 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.
+ *
+ * Any person wishing to distribute modifications to the Software is
+ * requested to send the modifications to the original developer so that
+ * they can be incorporated into the canonical version.
+ *
+ * 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.
+ */
+
+/** @file
+ @brief Allocation Group prototypes. An Allocation Group makes it easy to
+ allocate multiple blocks of memory and free them all simultanously.
+
+ An allocation group is useful for keeping track of multiple blocks
+ of memory which are allocated at the same time (such as during initialization)
+ and need to be deallocated at the same time. The allocation group maintains
+ a list of allocated blocks, and can deallocate them all simultaneously which
+ can be usefull for cleaning up after a partially initialized object fails.
+
+ The allocation group implementation is built on top of the lower
+ level allocation functions defined in pa_util.h
+*/
+
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif /* __cplusplus */
+
+
+typedef struct
+{
+ long linkCount;
+ struct PaUtilAllocationGroupLink *linkBlocks;
+ struct PaUtilAllocationGroupLink *spareLinks;
+ struct PaUtilAllocationGroupLink *allocations;
+}PaUtilAllocationGroup;
+
+
+
+/** Create an allocation group.
+*/
+PaUtilAllocationGroup* PaUtil_CreateAllocationGroup( void );
+
+/** Destroy an allocation group, but not the memory allocated through the group.
+*/
+void PaUtil_DestroyAllocationGroup( PaUtilAllocationGroup* group );
+
+/** Allocate a block of memory though an allocation group.
+*/
+void* PaUtil_GroupAllocateMemory( PaUtilAllocationGroup* group, long size );
+
+/** Free a block of memory that was previously allocated though an allocation
+ group. Calling this function is a relatively time consuming operation.
+ Under normal circumstances clients should call PaUtil_FreeAllAllocations to
+ free all allocated blocks simultaneously.
+ @see PaUtil_FreeAllAllocations
+*/
+void PaUtil_GroupFreeMemory( PaUtilAllocationGroup* group, void *buffer );
+
+/** Free all blocks of memory which have been allocated through the allocation
+ group. This function doesn't destroy the group itself.
+*/
+void PaUtil_FreeAllAllocations( PaUtilAllocationGroup* group );
+
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+#endif /* PA_ALLOCATION_H */
diff --git a/pjmedia/src/pjmedia/portaudio/pa_converters.c b/pjmedia/src/pjmedia/portaudio/pa_converters.c
index 36992823..4ee73c9a 100644
--- a/pjmedia/src/pjmedia/portaudio/pa_converters.c
+++ b/pjmedia/src/pjmedia/portaudio/pa_converters.c
@@ -1,1926 +1,1926 @@
-/*
- * $Id: pa_converters.c,v 1.1.2.26 2004/12/11 16:32:38 aknudsen Exp $
- * Portable Audio I/O Library sample conversion mechanism
- *
- * Based on the Open Source API proposed by Ross Bencina
- * Copyright (c) 1999-2002 Phil Burk, Ross Bencina
- *
- * 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.
- *
- * Any person wishing to distribute modifications to the Software is
- * requested to send the modifications to the original developer so that
- * they can be incorporated into the canonical version.
- *
- * 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.
- */
-
-/** @file
- @brief Conversion functions implementations.
-
- If the C9x function lrintf() is available, define PA_USE_C99_LRINTF to use it
-
- @todo Consider whether functions which dither but don't clip should exist,
- V18 automatically enabled clipping whenever dithering was selected. Perhaps
- we should do the same.
-
- @todo implement the converters marked IMPLEMENT ME: Float32_To_UInt8_Dither,
- Float32_To_UInt8_Clip, Float32_To_UInt8_DitherClip, Int32_To_Int24_Dither,
- Int32_To_UInt8_Dither, Int24_To_Int16_Dither, Int24_To_Int8_Dither,
- Int24_To_UInt8_Dither, Int16_To_Int8_Dither, Int16_To_UInt8_Dither,
-
- @todo review the converters marked REVIEW: Float32_To_Int32,
- Float32_To_Int32_Dither, Float32_To_Int32_Clip, Float32_To_Int32_DitherClip,
- Int32_To_Int16_Dither, Int32_To_Int8_Dither, Int16_To_Int32
-*/
-
-
-#include "pa_converters.h"
-#include "pa_dither.h"
-#include "pa_endianness.h"
-#include "pa_types.h"
-
-
-PaSampleFormat PaUtil_SelectClosestAvailableFormat(
- PaSampleFormat availableFormats, PaSampleFormat format )
-{
- PaSampleFormat result;
-
- format &= ~paNonInterleaved;
- availableFormats &= ~paNonInterleaved;
-
- if( (format & availableFormats) == 0 )
- {
- /* NOTE: this code depends on the sample format constants being in
- descending order of quality - ie best quality is 0
- FIXME: should write an assert which checks that all of the
- known constants conform to that requirement.
- */
-
- if( format != 0x01 )
- {
- /* scan for better formats */
- result = format;
- do
- {
- result >>= 1;
- }
- while( (result & availableFormats) == 0 && result != 0 );
- }
- else
- {
- result = 0;
- }
-
- if( result == 0 ){
- /* scan for worse formats */
- result = format;
- do
- {
- result <<= 1;
- }
- while( (result & availableFormats) == 0 && result != paCustomFormat );
-
- if( (result & availableFormats) == 0 )
- result = paSampleFormatNotSupported;
- }
-
- }else{
- result = format;
- }
-
- return result;
-}
-
-/* -------------------------------------------------------------------------- */
-
-#define PA_SELECT_FORMAT_( format, float32, int32, int24, int16, int8, uint8 ) \
- switch( format & ~paNonInterleaved ){ \
- case paFloat32: \
- float32 \
- case paInt32: \
- int32 \
- case paInt24: \
- int24 \
- case paInt16: \
- int16 \
- case paInt8: \
- int8 \
- case paUInt8: \
- uint8 \
- default: return 0; \
- }
-
-/* -------------------------------------------------------------------------- */
-
-#define PA_SELECT_CONVERTER_DITHER_CLIP_( flags, source, destination ) \
- if( flags & paClipOff ){ /* no clip */ \
- if( flags & paDitherOff ){ /* no dither */ \
- return paConverters. source ## _To_ ## destination; \
- }else{ /* dither */ \
- return paConverters. source ## _To_ ## destination ## _Dither; \
- } \
- }else{ /* clip */ \
- if( flags & paDitherOff ){ /* no dither */ \
- return paConverters. source ## _To_ ## destination ## _Clip; \
- }else{ /* dither */ \
- return paConverters. source ## _To_ ## destination ## _DitherClip; \
- } \
- }
-
-/* -------------------------------------------------------------------------- */
-
-#define PA_SELECT_CONVERTER_DITHER_( flags, source, destination ) \
- if( flags & paDitherOff ){ /* no dither */ \
- return paConverters. source ## _To_ ## destination; \
- }else{ /* dither */ \
- return paConverters. source ## _To_ ## destination ## _Dither; \
- }
-
-/* -------------------------------------------------------------------------- */
-
-#define PA_USE_CONVERTER_( source, destination )\
- return paConverters. source ## _To_ ## destination;
-
-/* -------------------------------------------------------------------------- */
-
-#define PA_UNITY_CONVERSION_( wordlength )\
- return paConverters. Copy_ ## wordlength ## _To_ ## wordlength;
-
-/* -------------------------------------------------------------------------- */
-
-PaUtilConverter* PaUtil_SelectConverter( PaSampleFormat sourceFormat,
- PaSampleFormat destinationFormat, PaStreamFlags flags )
-{
- PA_SELECT_FORMAT_( sourceFormat,
- /* paFloat32: */
- PA_SELECT_FORMAT_( destinationFormat,
- /* paFloat32: */ PA_UNITY_CONVERSION_( 32 ),
- /* paInt32: */ PA_SELECT_CONVERTER_DITHER_CLIP_( flags, Float32, Int32 ),
- /* paInt24: */ PA_SELECT_CONVERTER_DITHER_CLIP_( flags, Float32, Int24 ),
- /* paInt16: */ PA_SELECT_CONVERTER_DITHER_CLIP_( flags, Float32, Int16 ),
- /* paInt8: */ PA_SELECT_CONVERTER_DITHER_CLIP_( flags, Float32, Int8 ),
- /* paUInt8: */ PA_SELECT_CONVERTER_DITHER_CLIP_( flags, Float32, UInt8 )
- ),
- /* paInt32: */
- PA_SELECT_FORMAT_( destinationFormat,
- /* paFloat32: */ PA_USE_CONVERTER_( Int32, Float32 ),
- /* paInt32: */ PA_UNITY_CONVERSION_( 32 ),
- /* paInt24: */ PA_SELECT_CONVERTER_DITHER_( flags, Int32, Int24 ),
- /* paInt16: */ PA_SELECT_CONVERTER_DITHER_( flags, Int32, Int16 ),
- /* paInt8: */ PA_SELECT_CONVERTER_DITHER_( flags, Int32, Int8 ),
- /* paUInt8: */ PA_SELECT_CONVERTER_DITHER_( flags, Int32, UInt8 )
- ),
- /* paInt24: */
- PA_SELECT_FORMAT_( destinationFormat,
- /* paFloat32: */ PA_USE_CONVERTER_( Int24, Float32 ),
- /* paInt32: */ PA_USE_CONVERTER_( Int24, Int32 ),
- /* paInt24: */ PA_UNITY_CONVERSION_( 24 ),
- /* paInt16: */ PA_SELECT_CONVERTER_DITHER_( flags, Int24, Int16 ),
- /* paInt8: */ PA_SELECT_CONVERTER_DITHER_( flags, Int24, Int8 ),
- /* paUInt8: */ PA_SELECT_CONVERTER_DITHER_( flags, Int24, UInt8 )
- ),
- /* paInt16: */
- PA_SELECT_FORMAT_( destinationFormat,
- /* paFloat32: */ PA_USE_CONVERTER_( Int16, Float32 ),
- /* paInt32: */ PA_USE_CONVERTER_( Int16, Int32 ),
- /* paInt24: */ PA_USE_CONVERTER_( Int16, Int24 ),
- /* paInt16: */ PA_UNITY_CONVERSION_( 16 ),
- /* paInt8: */ PA_SELECT_CONVERTER_DITHER_( flags, Int16, Int8 ),
- /* paUInt8: */ PA_SELECT_CONVERTER_DITHER_( flags, Int16, UInt8 )
- ),
- /* paInt8: */
- PA_SELECT_FORMAT_( destinationFormat,
- /* paFloat32: */ PA_USE_CONVERTER_( Int8, Float32 ),
- /* paInt32: */ PA_USE_CONVERTER_( Int8, Int32 ),
- /* paInt24: */ PA_USE_CONVERTER_( Int8, Int24 ),
- /* paInt16: */ PA_USE_CONVERTER_( Int8, Int16 ),
- /* paInt8: */ PA_UNITY_CONVERSION_( 8 ),
- /* paUInt8: */ PA_USE_CONVERTER_( Int8, UInt8 )
- ),
- /* paUInt8: */
- PA_SELECT_FORMAT_( destinationFormat,
- /* paFloat32: */ PA_USE_CONVERTER_( UInt8, Float32 ),
- /* paInt32: */ PA_USE_CONVERTER_( UInt8, Int32 ),
- /* paInt24: */ PA_USE_CONVERTER_( UInt8, Int24 ),
- /* paInt16: */ PA_USE_CONVERTER_( UInt8, Int16 ),
- /* paInt8: */ PA_USE_CONVERTER_( UInt8, Int8 ),
- /* paUInt8: */ PA_UNITY_CONVERSION_( 8 )
- )
- )
-}
-
-/* -------------------------------------------------------------------------- */
-
-#ifdef PA_NO_STANDARD_CONVERTERS
-
-/* -------------------------------------------------------------------------- */
-
-PaUtilConverterTable paConverters = {
- 0, /* PaUtilConverter *Float32_To_Int32; */
- 0, /* PaUtilConverter *Float32_To_Int32_Dither; */
- 0, /* PaUtilConverter *Float32_To_Int32_Clip; */
- 0, /* PaUtilConverter *Float32_To_Int32_DitherClip; */
-
- 0, /* PaUtilConverter *Float32_To_Int24; */
- 0, /* PaUtilConverter *Float32_To_Int24_Dither; */
- 0, /* PaUtilConverter *Float32_To_Int24_Clip; */
- 0, /* PaUtilConverter *Float32_To_Int24_DitherClip; */
-
- 0, /* PaUtilConverter *Float32_To_Int16; */
- 0, /* PaUtilConverter *Float32_To_Int16_Dither; */
- 0, /* PaUtilConverter *Float32_To_Int16_Clip; */
- 0, /* PaUtilConverter *Float32_To_Int16_DitherClip; */
-
- 0, /* PaUtilConverter *Float32_To_Int8; */
- 0, /* PaUtilConverter *Float32_To_Int8_Dither; */
- 0, /* PaUtilConverter *Float32_To_Int8_Clip; */
- 0, /* PaUtilConverter *Float32_To_Int8_DitherClip; */
-
- 0, /* PaUtilConverter *Float32_To_UInt8; */
- 0, /* PaUtilConverter *Float32_To_UInt8_Dither; */
- 0, /* PaUtilConverter *Float32_To_UInt8_Clip; */
- 0, /* PaUtilConverter *Float32_To_UInt8_DitherClip; */
-
- 0, /* PaUtilConverter *Int32_To_Float32; */
- 0, /* PaUtilConverter *Int32_To_Int24; */
- 0, /* PaUtilConverter *Int32_To_Int24_Dither; */
- 0, /* PaUtilConverter *Int32_To_Int16; */
- 0, /* PaUtilConverter *Int32_To_Int16_Dither; */
- 0, /* PaUtilConverter *Int32_To_Int8; */
- 0, /* PaUtilConverter *Int32_To_Int8_Dither; */
- 0, /* PaUtilConverter *Int32_To_UInt8; */
- 0, /* PaUtilConverter *Int32_To_UInt8_Dither; */
-
- 0, /* PaUtilConverter *Int24_To_Float32; */
- 0, /* PaUtilConverter *Int24_To_Int32; */
- 0, /* PaUtilConverter *Int24_To_Int16; */
- 0, /* PaUtilConverter *Int24_To_Int16_Dither; */
- 0, /* PaUtilConverter *Int24_To_Int8; */
- 0, /* PaUtilConverter *Int24_To_Int8_Dither; */
- 0, /* PaUtilConverter *Int24_To_UInt8; */
- 0, /* PaUtilConverter *Int24_To_UInt8_Dither; */
-
- 0, /* PaUtilConverter *Int16_To_Float32; */
- 0, /* PaUtilConverter *Int16_To_Int32; */
- 0, /* PaUtilConverter *Int16_To_Int24; */
- 0, /* PaUtilConverter *Int16_To_Int8; */
- 0, /* PaUtilConverter *Int16_To_Int8_Dither; */
- 0, /* PaUtilConverter *Int16_To_UInt8; */
- 0, /* PaUtilConverter *Int16_To_UInt8_Dither; */
-
- 0, /* PaUtilConverter *Int8_To_Float32; */
- 0, /* PaUtilConverter *Int8_To_Int32; */
- 0, /* PaUtilConverter *Int8_To_Int24 */
- 0, /* PaUtilConverter *Int8_To_Int16; */
- 0, /* PaUtilConverter *Int8_To_UInt8; */
-
- 0, /* PaUtilConverter *UInt8_To_Float32; */
- 0, /* PaUtilConverter *UInt8_To_Int32; */
- 0, /* PaUtilConverter *UInt8_To_Int24; */
- 0, /* PaUtilConverter *UInt8_To_Int16; */
- 0, /* PaUtilConverter *UInt8_To_Int8; */
-
- 0, /* PaUtilConverter *Copy_8_To_8; */
- 0, /* PaUtilConverter *Copy_16_To_16; */
- 0, /* PaUtilConverter *Copy_24_To_24; */
- 0 /* PaUtilConverter *Copy_32_To_32; */
-};
-
-/* -------------------------------------------------------------------------- */
-
-#else /* PA_NO_STANDARD_CONVERTERS is not defined */
-
-/* -------------------------------------------------------------------------- */
-
-#define PA_CLIP_( val, min, max )\
- { val = ((val) < (min)) ? (min) : (((val) > (max)) ? (max) : (val)); }
-
-
-static const float const_1_div_128_ = 1.0f / 128.0f; /* 8 bit multiplier */
-
-static const float const_1_div_32768_ = 1.0f / 32768.f; /* 16 bit multiplier */
-
-static const double const_1_div_2147483648_ = 1.0 / 2147483648.0; /* 32 bit multiplier */
-
-/* -------------------------------------------------------------------------- */
-
-static void Float32_To_Int32(
- void *destinationBuffer, signed int destinationStride,
- void *sourceBuffer, signed int sourceStride,
- unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator )
-{
- float *src = (float*)sourceBuffer;
- signed long *dest = (signed long*)destinationBuffer;
- (void)ditherGenerator; /* unused parameter */
-
- while( count-- )
- {
- /* REVIEW */
-#ifdef PA_USE_C99_LRINTF
- float scaled = *src * 0x7FFFFFFF;
- *dest = lrintf(scaled-0.5f);
-#else
- double scaled = *src * 0x7FFFFFFF;
- *dest = (signed long) scaled;
-#endif
-
- src += sourceStride;
- dest += destinationStride;
- }
-}
-
-/* -------------------------------------------------------------------------- */
-
-static void Float32_To_Int32_Dither(
- void *destinationBuffer, signed int destinationStride,
- void *sourceBuffer, signed int sourceStride,
- unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator )
-{
- float *src = (float*)sourceBuffer;
- signed long *dest = (signed long*)destinationBuffer;
-
- while( count-- )
- {
- /* REVIEW */
-#ifdef PA_USE_C99_LRINTF
- float dither = PaUtil_GenerateFloatTriangularDither( ditherGenerator );
- /* use smaller scaler to prevent overflow when we add the dither */
- float dithered = ((float)*src * (2147483646.0f)) + dither;
- *dest = lrintf(dithered - 0.5f);
-#else
- double dither = PaUtil_GenerateFloatTriangularDither( ditherGenerator );
- /* use smaller scaler to prevent overflow when we add the dither */
- double dithered = ((double)*src * (2147483646.0)) + dither;
- *dest = (signed long) dithered;
-#endif
- src += sourceStride;
- dest += destinationStride;
- }
-}
-
-/* -------------------------------------------------------------------------- */
-
-static void Float32_To_Int32_Clip(
- void *destinationBuffer, signed int destinationStride,
- void *sourceBuffer, signed int sourceStride,
- unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator )
-{
- float *src = (float*)sourceBuffer;
- signed long *dest = (signed long*)destinationBuffer;
- (void) ditherGenerator; /* unused parameter */
-
- while( count-- )
- {
- /* REVIEW */
-#ifdef PA_USE_C99_LRINTF
- float scaled = *src * 0x7FFFFFFF;
- PA_CLIP_( scaled, -2147483648.f, 2147483647.f );
- *dest = lrintf(scaled-0.5f);
-#else
- double scaled = *src * 0x7FFFFFFF;
- PA_CLIP_( scaled, -2147483648., 2147483647. );
- *dest = (signed long) scaled;
-#endif
-
- src += sourceStride;
- dest += destinationStride;
- }
-}
-
-/* -------------------------------------------------------------------------- */
-
-static void Float32_To_Int32_DitherClip(
- void *destinationBuffer, signed int destinationStride,
- void *sourceBuffer, signed int sourceStride,
- unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator )
-{
- float *src = (float*)sourceBuffer;
- signed long *dest = (signed long*)destinationBuffer;
-
- while( count-- )
- {
- /* REVIEW */
-#ifdef PA_USE_C99_LRINTF
- float dither = PaUtil_GenerateFloatTriangularDither( ditherGenerator );
- /* use smaller scaler to prevent overflow when we add the dither */
- float dithered = ((float)*src * (2147483646.0f)) + dither;
- PA_CLIP_( dithered, -2147483648.f, 2147483647.f );
- *dest = lrintf(dithered-0.5f);
-#else
- double dither = PaUtil_GenerateFloatTriangularDither( ditherGenerator );
- /* use smaller scaler to prevent overflow when we add the dither */
- double dithered = ((double)*src * (2147483646.0)) + dither;
- PA_CLIP_( dithered, -2147483648., 2147483647. );
- *dest = (signed long) dithered;
-#endif
-
- src += sourceStride;
- dest += destinationStride;
- }
-}
-
-/* -------------------------------------------------------------------------- */
-
-static void Float32_To_Int24(
- void *destinationBuffer, signed int destinationStride,
- void *sourceBuffer, signed int sourceStride,
- unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator )
-{
- float *src = (float*)sourceBuffer;
- unsigned char *dest = (unsigned char*)destinationBuffer;
- signed long temp;
-
- (void) ditherGenerator; /* unused parameter */
-
- while( count-- )
- {
- /* convert to 32 bit and drop the low 8 bits */
- double scaled = *src * 0x7FFFFFFF;
- temp = (signed long) scaled;
-
-#if defined(PA_LITTLE_ENDIAN)
- dest[0] = (unsigned char)(temp >> 8);
- dest[1] = (unsigned char)(temp >> 16);
- dest[2] = (unsigned char)(temp >> 24);
-#elif defined(PA_BIG_ENDIAN)
- dest[0] = (unsigned char)(temp >> 24);
- dest[1] = (unsigned char)(temp >> 16);
- dest[2] = (unsigned char)(temp >> 8);
-#endif
-
- src += sourceStride;
- dest += destinationStride * 3;
- }
-}
-
-/* -------------------------------------------------------------------------- */
-
-static void Float32_To_Int24_Dither(
- void *destinationBuffer, signed int destinationStride,
- void *sourceBuffer, signed int sourceStride,
- unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator )
-{
- float *src = (float*)sourceBuffer;
- unsigned char *dest = (unsigned char*)destinationBuffer;
- signed long temp;
-
- while( count-- )
- {
- /* convert to 32 bit and drop the low 8 bits */
-
- double dither = PaUtil_GenerateFloatTriangularDither( ditherGenerator );
- /* use smaller scaler to prevent overflow when we add the dither */
- double dithered = ((double)*src * (2147483646.0)) + dither;
-
- temp = (signed long) dithered;
-
-#if defined(PA_LITTLE_ENDIAN)
- dest[0] = (unsigned char)(temp >> 8);
- dest[1] = (unsigned char)(temp >> 16);
- dest[2] = (unsigned char)(temp >> 24);
-#elif defined(PA_BIG_ENDIAN)
- dest[0] = (unsigned char)(temp >> 24);
- dest[1] = (unsigned char)(temp >> 16);
- dest[2] = (unsigned char)(temp >> 8);
-#endif
-
- src += sourceStride;
- dest += destinationStride * 3;
- }
-}
-
-/* -------------------------------------------------------------------------- */
-
-static void Float32_To_Int24_Clip(
- void *destinationBuffer, signed int destinationStride,
- void *sourceBuffer, signed int sourceStride,
- unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator )
-{
- float *src = (float*)sourceBuffer;
- unsigned char *dest = (unsigned char*)destinationBuffer;
- signed long temp;
-
- (void) ditherGenerator; /* unused parameter */
-
- while( count-- )
- {
- /* convert to 32 bit and drop the low 8 bits */
- double scaled = *src * 0x7FFFFFFF;
- PA_CLIP_( scaled, -2147483648., 2147483647. );
- temp = (signed long) scaled;
-
-#if defined(PA_LITTLE_ENDIAN)
- dest[0] = (unsigned char)(temp >> 8);
- dest[1] = (unsigned char)(temp >> 16);
- dest[2] = (unsigned char)(temp >> 24);
-#elif defined(PA_BIG_ENDIAN)
- dest[0] = (unsigned char)(temp >> 24);
- dest[1] = (unsigned char)(temp >> 16);
- dest[2] = (unsigned char)(temp >> 8);
-#endif
-
- src += sourceStride;
- dest += destinationStride * 3;
- }
-}
-
-/* -------------------------------------------------------------------------- */
-
-static void Float32_To_Int24_DitherClip(
- void *destinationBuffer, signed int destinationStride,
- void *sourceBuffer, signed int sourceStride,
- unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator )
-{
- float *src = (float*)sourceBuffer;
- unsigned char *dest = (unsigned char*)destinationBuffer;
- signed long temp;
-
- while( count-- )
- {
- /* convert to 32 bit and drop the low 8 bits */
-
- double dither = PaUtil_GenerateFloatTriangularDither( ditherGenerator );
- /* use smaller scaler to prevent overflow when we add the dither */
- double dithered = ((double)*src * (2147483646.0)) + dither;
- PA_CLIP_( dithered, -2147483648., 2147483647. );
-
- temp = (signed long) dithered;
-
-#if defined(PA_LITTLE_ENDIAN)
- dest[0] = (unsigned char)(temp >> 8);
- dest[1] = (unsigned char)(temp >> 16);
- dest[2] = (unsigned char)(temp >> 24);
-#elif defined(PA_BIG_ENDIAN)
- dest[0] = (unsigned char)(temp >> 24);
- dest[1] = (unsigned char)(temp >> 16);
- dest[2] = (unsigned char)(temp >> 8);
-#endif
-
- src += sourceStride;
- dest += destinationStride * 3;
- }
-}
-
-/* -------------------------------------------------------------------------- */
-
-static void Float32_To_Int16(
- void *destinationBuffer, signed int destinationStride,
- void *sourceBuffer, signed int sourceStride,
- unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator )
-{
- float *src = (float*)sourceBuffer;
- signed short *dest = (signed short*)destinationBuffer;
- (void)ditherGenerator; /* unused parameter */
-
- while( count-- )
- {
-#ifdef PA_USE_C99_LRINTF
- float tempf = (*src * (32767.0f)) ;
- *dest = lrintf(tempf-0.5f);
-#else
- short samp = (short) (*src * (32767.0f));
- *dest = samp;
-#endif
-
- src += sourceStride;
- dest += destinationStride;
- }
-}
-
-/* -------------------------------------------------------------------------- */
-
-static void Float32_To_Int16_Dither(
- void *destinationBuffer, signed int destinationStride,
- void *sourceBuffer, signed int sourceStride,
- unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator )
-{
- float *src = (float*)sourceBuffer;
- signed short *dest = (signed short*)destinationBuffer;
-
- while( count-- )
- {
-
- float dither = PaUtil_GenerateFloatTriangularDither( ditherGenerator );
- /* use smaller scaler to prevent overflow when we add the dither */
- float dithered = (*src * (32766.0f)) + dither;
-
-#ifdef PA_USE_C99_LRINTF
- *dest = lrintf(dithered-0.5f);
-#else
- *dest = (signed short) dithered;
-#endif
-
- src += sourceStride;
- dest += destinationStride;
- }
-}
-
-/* -------------------------------------------------------------------------- */
-
-static void Float32_To_Int16_Clip(
- void *destinationBuffer, signed int destinationStride,
- void *sourceBuffer, signed int sourceStride,
- unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator )
-{
- float *src = (float*)sourceBuffer;
- signed short *dest = (signed short*)destinationBuffer;
- (void)ditherGenerator; /* unused parameter */
-
- while( count-- )
- {
-#ifdef PA_USE_C99_LRINTF
- long samp = lrintf((*src * (32767.0f)) -0.5f);
-#else
- long samp = (signed long) (*src * (32767.0f));
-#endif
- PA_CLIP_( samp, -0x8000, 0x7FFF );
- *dest = (signed short) samp;
-
- src += sourceStride;
- dest += destinationStride;
- }
-}
-
-/* -------------------------------------------------------------------------- */
-
-static void Float32_To_Int16_DitherClip(
- void *destinationBuffer, signed int destinationStride,
- void *sourceBuffer, signed int sourceStride,
- unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator )
-{
- float *src = (float*)sourceBuffer;
- signed short *dest = (signed short*)destinationBuffer;
- (void)ditherGenerator; /* unused parameter */
-
- while( count-- )
- {
-
- float dither = PaUtil_GenerateFloatTriangularDither( ditherGenerator );
- /* use smaller scaler to prevent overflow when we add the dither */
- float dithered = (*src * (32766.0f)) + dither;
- signed long samp = (signed long) dithered;
- PA_CLIP_( samp, -0x8000, 0x7FFF );
-#ifdef PA_USE_C99_LRINTF
- *dest = lrintf(samp-0.5f);
-#else
- *dest = (signed short) samp;
-#endif
-
- src += sourceStride;
- dest += destinationStride;
- }
-}
-
-/* -------------------------------------------------------------------------- */
-
-static void Float32_To_Int8(
- void *destinationBuffer, signed int destinationStride,
- void *sourceBuffer, signed int sourceStride,
- unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator )
-{
- float *src = (float*)sourceBuffer;
- signed char *dest = (signed char*)destinationBuffer;
- (void)ditherGenerator; /* unused parameter */
-
- while( count-- )
- {
- signed char samp = (signed char) (*src * (127.0f));
- *dest = samp;
-
- src += sourceStride;
- dest += destinationStride;
- }
-}
-
-/* -------------------------------------------------------------------------- */
-
-static void Float32_To_Int8_Dither(
- void *destinationBuffer, signed int destinationStride,
- void *sourceBuffer, signed int sourceStride,
- unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator )
-{
- float *src = (float*)sourceBuffer;
- signed char *dest = (signed char*)destinationBuffer;
- (void)ditherGenerator; /* unused parameter */
-
- while( count-- )
- {
- float dither = PaUtil_GenerateFloatTriangularDither( ditherGenerator );
- /* use smaller scaler to prevent overflow when we add the dither */
- float dithered = (*src * (126.0f)) + dither;
- signed long samp = (signed long) dithered;
- *dest = (signed char) samp;
-
- src += sourceStride;
- dest += destinationStride;
- }
-}
-
-/* -------------------------------------------------------------------------- */
-
-static void Float32_To_Int8_Clip(
- void *destinationBuffer, signed int destinationStride,
- void *sourceBuffer, signed int sourceStride,
- unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator )
-{
- float *src = (float*)sourceBuffer;
- signed char *dest = (signed char*)destinationBuffer;
- (void)ditherGenerator; /* unused parameter */
-
- while( count-- )
- {
- signed long samp = (signed long)(*src * (127.0f));
- PA_CLIP_( samp, -0x80, 0x7F );
- *dest = (signed char) samp;
-
- src += sourceStride;
- dest += destinationStride;
- }
-}
-
-/* -------------------------------------------------------------------------- */
-
-static void Float32_To_Int8_DitherClip(
- void *destinationBuffer, signed int destinationStride,
- void *sourceBuffer, signed int sourceStride,
- unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator )
-{
- float *src = (float*)sourceBuffer;
- signed char *dest = (signed char*)destinationBuffer;
- (void)ditherGenerator; /* unused parameter */
-
- while( count-- )
- {
- float dither = PaUtil_GenerateFloatTriangularDither( ditherGenerator );
- /* use smaller scaler to prevent overflow when we add the dither */
- float dithered = (*src * (126.0f)) + dither;
- signed long samp = (signed long) dithered;
- PA_CLIP_( samp, -0x80, 0x7F );
- *dest = (signed char) samp;
-
- src += sourceStride;
- dest += destinationStride;
- }
-}
-
-/* -------------------------------------------------------------------------- */
-
-static void Float32_To_UInt8(
- void *destinationBuffer, signed int destinationStride,
- void *sourceBuffer, signed int sourceStride,
- unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator )
-{
- float *src = (float*)sourceBuffer;
- unsigned char *dest = (unsigned char*)destinationBuffer;
- (void)ditherGenerator; /* unused parameter */
-
- while( count-- )
- {
- unsigned char samp = (unsigned char)(128 + ((unsigned char) (*src * (127.0f))));
- *dest = samp;
-
- src += sourceStride;
- dest += destinationStride;
- }
-}
-
-/* -------------------------------------------------------------------------- */
-
-static void Float32_To_UInt8_Dither(
- void *destinationBuffer, signed int destinationStride,
- void *sourceBuffer, signed int sourceStride,
- unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator )
-{
- float *src = (float*)sourceBuffer;
- unsigned char *dest = (unsigned char*)destinationBuffer;
- (void)ditherGenerator; /* unused parameter */
-
- while( count-- )
- {
- /* IMPLEMENT ME */
-
- src += sourceStride;
- dest += destinationStride;
- }
-}
-
-/* -------------------------------------------------------------------------- */
-
-static void Float32_To_UInt8_Clip(
- void *destinationBuffer, signed int destinationStride,
- void *sourceBuffer, signed int sourceStride,
- unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator )
-{
- float *src = (float*)sourceBuffer;
- unsigned char *dest = (unsigned char*)destinationBuffer;
- (void)ditherGenerator; /* unused parameter */
-
- while( count-- )
- {
- /* IMPLEMENT ME */
-
- src += sourceStride;
- dest += destinationStride;
- }
-}
-
-/* -------------------------------------------------------------------------- */
-
-static void Float32_To_UInt8_DitherClip(
- void *destinationBuffer, signed int destinationStride,
- void *sourceBuffer, signed int sourceStride,
- unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator )
-{
- float *src = (float*)sourceBuffer;
- unsigned char *dest = (unsigned char*)destinationBuffer;
- (void)ditherGenerator; /* unused parameter */
-
- while( count-- )
- {
- /* IMPLEMENT ME */
-
- src += sourceStride;
- dest += destinationStride;
- }
-}
-
-/* -------------------------------------------------------------------------- */
-
-static void Int32_To_Float32(
- void *destinationBuffer, signed int destinationStride,
- void *sourceBuffer, signed int sourceStride,
- unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator )
-{
- signed long *src = (signed long*)sourceBuffer;
- float *dest = (float*)destinationBuffer;
- (void)ditherGenerator; /* unused parameter */
-
- while( count-- )
- {
- *dest = (float) ((double)*src * const_1_div_2147483648_);
-
- src += sourceStride;
- dest += destinationStride;
- }
-}
-
-/* -------------------------------------------------------------------------- */
-
-static void Int32_To_Int24(
- void *destinationBuffer, signed int destinationStride,
- void *sourceBuffer, signed int sourceStride,
- unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator )
-{
- signed long *src = (signed long*)sourceBuffer;
- unsigned char *dest = (unsigned char*)destinationBuffer;
- (void) ditherGenerator; /* unused parameter */
-
- while( count-- )
- {
- /* REVIEW */
-#if defined(PA_LITTLE_ENDIAN)
- dest[0] = (unsigned char)(*src >> 8);
- dest[1] = (unsigned char)(*src >> 16);
- dest[2] = (unsigned char)(*src >> 24);
-#elif defined(PA_BIG_ENDIAN)
- dest[0] = (unsigned char)(*src >> 24);
- dest[1] = (unsigned char)(*src >> 16);
- dest[2] = (unsigned char)(*src >> 8);
-#endif
- src += sourceStride;
- dest += destinationStride * 3;
- }
-}
-
-/* -------------------------------------------------------------------------- */
-
-static void Int32_To_Int24_Dither(
- void *destinationBuffer, signed int destinationStride,
- void *sourceBuffer, signed int sourceStride,
- unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator )
-{
- (void) destinationBuffer; /* unused parameters */
- (void) destinationStride; /* unused parameters */
- (void) sourceBuffer; /* unused parameters */
- (void) sourceStride; /* unused parameters */
- (void) count; /* unused parameters */
- (void) ditherGenerator; /* unused parameters */
- /* IMPLEMENT ME */
-}
-
-/* -------------------------------------------------------------------------- */
-
-static void Int32_To_Int16(
- void *destinationBuffer, signed int destinationStride,
- void *sourceBuffer, signed int sourceStride,
- unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator )
-{
- signed long *src = (signed long*)sourceBuffer;
- signed short *dest = (signed short*)destinationBuffer;
- (void)ditherGenerator; /* unused parameter */
-
- while( count-- )
- {
- *dest = (signed short) ((*src) >> 16);
-
- src += sourceStride;
- dest += destinationStride;
- }
-}
-
-/* -------------------------------------------------------------------------- */
-
-static void Int32_To_Int16_Dither(
- void *destinationBuffer, signed int destinationStride,
- void *sourceBuffer, signed int sourceStride,
- unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator )
-{
- signed long *src = (signed long*)sourceBuffer;
- signed short *dest = (signed short*)destinationBuffer;
- signed long dither;
-
- while( count-- )
- {
- /* REVIEW */
- dither = PaUtil_Generate16BitTriangularDither( ditherGenerator );
- *dest = (signed short) ((((*src)>>1) + dither) >> 15);
-
- src += sourceStride;
- dest += destinationStride;
- }
-}
-
-/* -------------------------------------------------------------------------- */
-
-static void Int32_To_Int8(
- void *destinationBuffer, signed int destinationStride,
- void *sourceBuffer, signed int sourceStride,
- unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator )
-{
- signed long *src = (signed long*)sourceBuffer;
- signed char *dest = (signed char*)destinationBuffer;
- (void)ditherGenerator; /* unused parameter */
-
- while( count-- )
- {
- *dest = (signed char) ((*src) >> 24);
-
- src += sourceStride;
- dest += destinationStride;
- }
-}
-
-/* -------------------------------------------------------------------------- */
-
-static void Int32_To_Int8_Dither(
- void *destinationBuffer, signed int destinationStride,
- void *sourceBuffer, signed int sourceStride,
- unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator )
-{
- signed long *src = (signed long*)sourceBuffer;
- signed char *dest = (signed char*)destinationBuffer;
- signed long dither;
-
- while( count-- )
- {
- /* REVIEW */
- dither = PaUtil_Generate16BitTriangularDither( ditherGenerator );
- *dest = (signed char) ((((*src)>>1) + dither) >> 23);
-
- src += sourceStride;
- dest += destinationStride;
- }
-}
-
-/* -------------------------------------------------------------------------- */
-
-static void Int32_To_UInt8(
- void *destinationBuffer, signed int destinationStride,
- void *sourceBuffer, signed int sourceStride,
- unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator )
-{
- signed long *src = (signed long*)sourceBuffer;
- unsigned char *dest = (unsigned char*)destinationBuffer;
- (void)ditherGenerator; /* unused parameter */
-
- while( count-- )
- {
- (*dest) = (unsigned char)(((*src) >> 24) + 128);
-
- src += sourceStride;
- dest += destinationStride;
- }
-}
-
-/* -------------------------------------------------------------------------- */
-
-static void Int32_To_UInt8_Dither(
- void *destinationBuffer, signed int destinationStride,
- void *sourceBuffer, signed int sourceStride,
- unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator )
-{
- signed long *src = (signed long*)sourceBuffer;
- unsigned char *dest = (unsigned char*)destinationBuffer;
- (void)ditherGenerator; /* unused parameter */
-
- while( count-- )
- {
- /* IMPLEMENT ME */
-
- src += sourceStride;
- dest += destinationStride;
- }
-}
-
-/* -------------------------------------------------------------------------- */
-
-static void Int24_To_Float32(
- void *destinationBuffer, signed int destinationStride,
- void *sourceBuffer, signed int sourceStride,
- unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator )
-{
- unsigned char *src = (unsigned char*)sourceBuffer;
- float *dest = (float*)destinationBuffer;
- signed long temp;
-
- (void) ditherGenerator; /* unused parameter */
-
- while( count-- )
- {
-
-#if defined(PA_LITTLE_ENDIAN)
- temp = (((long)src[0]) << 8);
- temp = temp | (((long)src[1]) << 16);
- temp = temp | (((long)src[2]) << 24);
-#elif defined(PA_BIG_ENDIAN)
- temp = (((long)src[0]) << 24);
- temp = temp | (((long)src[1]) << 16);
- temp = temp | (((long)src[2]) << 8);
-#endif
-
- *dest = (float) ((double)temp * const_1_div_2147483648_);
-
- src += sourceStride * 3;
- dest += destinationStride;
- }
-}
-
-/* -------------------------------------------------------------------------- */
-
-static void Int24_To_Int32(
- void *destinationBuffer, signed int destinationStride,
- void *sourceBuffer, signed int sourceStride,
- unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator )
-{
- unsigned char *src = (unsigned char*)sourceBuffer;
- signed long *dest = (signed long*) destinationBuffer;
- signed long temp;
-
- (void) ditherGenerator; /* unused parameter */
-
- while( count-- )
- {
-
-#if defined(PA_LITTLE_ENDIAN)
- temp = (((long)src[0]) << 8);
- temp = temp | (((long)src[1]) << 16);
- temp = temp | (((long)src[2]) << 24);
-#elif defined(PA_BIG_ENDIAN)
- temp = (((long)src[0]) << 24);
- temp = temp | (((long)src[1]) << 16);
- temp = temp | (((long)src[2]) << 8);
-#endif
-
- *dest = temp;
-
- src += sourceStride * 3;
- dest += destinationStride;
- }
-}
-
-/* -------------------------------------------------------------------------- */
-
-static void Int24_To_Int16(
- void *destinationBuffer, signed int destinationStride,
- void *sourceBuffer, signed int sourceStride,
- unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator )
-{
- unsigned char *src = (unsigned char*)sourceBuffer;
- signed short *dest = (signed short*)destinationBuffer;
-
- signed short temp;
-
- (void) ditherGenerator; /* unused parameter */
-
- while( count-- )
- {
-
-#if defined(PA_LITTLE_ENDIAN)
- /* src[0] is discarded */
- temp = (((signed short)src[1]));
- temp = temp | (signed short)(((signed short)src[2]) << 8);
-#elif defined(PA_BIG_ENDIAN)
- /* src[2] is discarded */
- temp = (signed short)(((signed short)src[0]) << 8);
- temp = temp | (((signed short)src[1]));
-#endif
-
- *dest = temp;
-
- src += sourceStride * 3;
- dest += destinationStride;
- }
-}
-
-/* -------------------------------------------------------------------------- */
-
-static void Int24_To_Int16_Dither(
- void *destinationBuffer, signed int destinationStride,
- void *sourceBuffer, signed int sourceStride,
- unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator )
-{
- (void) destinationBuffer; /* unused parameters */
- (void) destinationStride; /* unused parameters */
- (void) sourceBuffer; /* unused parameters */
- (void) sourceStride; /* unused parameters */
- (void) count; /* unused parameters */
- (void) ditherGenerator; /* unused parameters */
- /* IMPLEMENT ME */
-}
-
-/* -------------------------------------------------------------------------- */
-
-static void Int24_To_Int8(
- void *destinationBuffer, signed int destinationStride,
- void *sourceBuffer, signed int sourceStride,
- unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator )
-{
- unsigned char *src = (unsigned char*)sourceBuffer;
- signed char *dest = (signed char*)destinationBuffer;
-
- (void) ditherGenerator; /* unused parameter */
-
- while( count-- )
- {
-
-#if defined(PA_LITTLE_ENDIAN)
- /* src[0] is discarded */
- /* src[1] is discarded */
- *dest = src[2];
-#elif defined(PA_BIG_ENDIAN)
- /* src[2] is discarded */
- /* src[1] is discarded */
- *dest = src[0];
-#endif
-
- src += sourceStride * 3;
- dest += destinationStride;
- }
-}
-
-/* -------------------------------------------------------------------------- */
-
-static void Int24_To_Int8_Dither(
- void *destinationBuffer, signed int destinationStride,
- void *sourceBuffer, signed int sourceStride,
- unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator )
-{
- (void) destinationBuffer; /* unused parameters */
- (void) destinationStride; /* unused parameters */
- (void) sourceBuffer; /* unused parameters */
- (void) sourceStride; /* unused parameters */
- (void) count; /* unused parameters */
- (void) ditherGenerator; /* unused parameters */
- /* IMPLEMENT ME */
-}
-
-/* -------------------------------------------------------------------------- */
-
-static void Int24_To_UInt8(
- void *destinationBuffer, signed int destinationStride,
- void *sourceBuffer, signed int sourceStride,
- unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator )
-{
- unsigned char *src = (unsigned char*)sourceBuffer;
- unsigned char *dest = (unsigned char*)destinationBuffer;
-
- (void) ditherGenerator; /* unused parameter */
-
- while( count-- )
- {
-
-#if defined(PA_LITTLE_ENDIAN)
- /* src[0] is discarded */
- /* src[1] is discarded */
- *dest = (unsigned char)(src[2] + 128);
-#elif defined(PA_BIG_ENDIAN)
- *dest = (unsigned char)(src[0] + 128);
- /* src[1] is discarded */
- /* src[2] is discarded */
-#endif
-
- src += sourceStride * 3;
- dest += destinationStride;
- }
-}
-
-/* -------------------------------------------------------------------------- */
-
-static void Int24_To_UInt8_Dither(
- void *destinationBuffer, signed int destinationStride,
- void *sourceBuffer, signed int sourceStride,
- unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator )
-{
- (void) destinationBuffer; /* unused parameters */
- (void) destinationStride; /* unused parameters */
- (void) sourceBuffer; /* unused parameters */
- (void) sourceStride; /* unused parameters */
- (void) count; /* unused parameters */
- (void) ditherGenerator; /* unused parameters */
- /* IMPLEMENT ME */
-}
-
-/* -------------------------------------------------------------------------- */
-
-static void Int16_To_Float32(
- void *destinationBuffer, signed int destinationStride,
- void *sourceBuffer, signed int sourceStride,
- unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator )
-{
- signed short *src = (signed short*)sourceBuffer;
- float *dest = (float*)destinationBuffer;
- (void)ditherGenerator; /* unused parameter */
-
- while( count-- )
- {
- float samp = *src * const_1_div_32768_; /* FIXME: i'm concerned about this being asymetrical with float->int16 -rb */
- *dest = samp;
-
- src += sourceStride;
- dest += destinationStride;
- }
-}
-
-/* -------------------------------------------------------------------------- */
-
-static void Int16_To_Int32(
- void *destinationBuffer, signed int destinationStride,
- void *sourceBuffer, signed int sourceStride,
- unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator )
-{
- signed short *src = (signed short*)sourceBuffer;
- signed long *dest = (signed long*)destinationBuffer;
- (void)ditherGenerator; /* unused parameter */
-
- while( count-- )
- {
- /* REVIEW: we should consider something like
- (*src << 16) | (*src & 0xFFFF)
- */
-
- *dest = *src << 16;
-
- src += sourceStride;
- dest += destinationStride;
- }
-}
-
-/* -------------------------------------------------------------------------- */
-
-static void Int16_To_Int24(
- void *destinationBuffer, signed int destinationStride,
- void *sourceBuffer, signed int sourceStride,
- unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator )
-{
- signed short *src = (signed short*) sourceBuffer;
- unsigned char *dest = (unsigned char*)destinationBuffer;
- signed short temp;
-
- (void) ditherGenerator; /* unused parameter */
-
- while( count-- )
- {
- temp = *src;
-
-#if defined(PA_LITTLE_ENDIAN)
- dest[0] = 0;
- dest[1] = (unsigned char)(temp);
- dest[2] = (unsigned char)(temp >> 8);
-#elif defined(PA_BIG_ENDIAN)
- dest[0] = (unsigned char)(temp >> 8);
- dest[1] = (unsigned char)(temp);
- dest[2] = 0;
-#endif
-
- src += sourceStride;
- dest += destinationStride * 3;
- }
-}
-
-/* -------------------------------------------------------------------------- */
-
-static void Int16_To_Int8(
- void *destinationBuffer, signed int destinationStride,
- void *sourceBuffer, signed int sourceStride,
- unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator )
-{
- signed short *src = (signed short*)sourceBuffer;
- signed char *dest = (signed char*)destinationBuffer;
- (void)ditherGenerator; /* unused parameter */
-
- while( count-- )
- {
- (*dest) = (signed char)((*src) >> 8);
-
- src += sourceStride;
- dest += destinationStride;
- }
-}
-
-/* -------------------------------------------------------------------------- */
-
-static void Int16_To_Int8_Dither(
- void *destinationBuffer, signed int destinationStride,
- void *sourceBuffer, signed int sourceStride,
- unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator )
-{
- signed short *src = (signed short*)sourceBuffer;
- signed char *dest = (signed char*)destinationBuffer;
- (void)ditherGenerator; /* unused parameter */
-
- while( count-- )
- {
- /* IMPLEMENT ME */
-
- src += sourceStride;
- dest += destinationStride;
- }
-}
-
-/* -------------------------------------------------------------------------- */
-
-static void Int16_To_UInt8(
- void *destinationBuffer, signed int destinationStride,
- void *sourceBuffer, signed int sourceStride,
- unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator )
-{
- signed short *src = (signed short*)sourceBuffer;
- unsigned char *dest = (unsigned char*)destinationBuffer;
- (void)ditherGenerator; /* unused parameter */
-
- while( count-- )
- {
- (*dest) = (unsigned char)(((*src) >> 8) + 128);
-
- src += sourceStride;
- dest += destinationStride;
- }
-}
-
-/* -------------------------------------------------------------------------- */
-
-static void Int16_To_UInt8_Dither(
- void *destinationBuffer, signed int destinationStride,
- void *sourceBuffer, signed int sourceStride,
- unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator )
-{
- signed short *src = (signed short*)sourceBuffer;
- unsigned char *dest = (unsigned char*)destinationBuffer;
- (void)ditherGenerator; /* unused parameter */
-
- while( count-- )
- {
- /* IMPLEMENT ME */
-
- src += sourceStride;
- dest += destinationStride;
- }
-}
-
-/* -------------------------------------------------------------------------- */
-
-static void Int8_To_Float32(
- void *destinationBuffer, signed int destinationStride,
- void *sourceBuffer, signed int sourceStride,
- unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator )
-{
- signed char *src = (signed char*)sourceBuffer;
- float *dest = (float*)destinationBuffer;
- (void)ditherGenerator; /* unused parameter */
-
- while( count-- )
- {
- float samp = *src * const_1_div_128_;
- *dest = samp;
-
- src += sourceStride;
- dest += destinationStride;
- }
-}
-
-/* -------------------------------------------------------------------------- */
-
-static void Int8_To_Int32(
- void *destinationBuffer, signed int destinationStride,
- void *sourceBuffer, signed int sourceStride,
- unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator )
-{
- signed char *src = (signed char*)sourceBuffer;
- signed long *dest = (signed long*)destinationBuffer;
- (void)ditherGenerator; /* unused parameter */
-
- while( count-- )
- {
- (*dest) = (*src) << 24;
-
- src += sourceStride;
- dest += destinationStride;
- }
-}
-
-/* -------------------------------------------------------------------------- */
-
-static void Int8_To_Int24(
- void *destinationBuffer, signed int destinationStride,
- void *sourceBuffer, signed int sourceStride,
- unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator )
-{
- signed char *src = (signed char*)sourceBuffer;
- unsigned char *dest = (unsigned char*)destinationBuffer;
- (void)ditherGenerator; /* unused parameter */
-
- while( count-- )
- {
-
-#if defined(PA_LITTLE_ENDIAN)
- dest[0] = 0;
- dest[1] = 0;
- dest[2] = (*src);
-#elif defined(PA_BIG_ENDIAN)
- dest[0] = (*src);
- dest[1] = 0;
- dest[2] = 0;
-#endif
-
- src += sourceStride;
- dest += destinationStride * 3;
- }
-}
-
-/* -------------------------------------------------------------------------- */
-
-static void Int8_To_Int16(
- void *destinationBuffer, signed int destinationStride,
- void *sourceBuffer, signed int sourceStride,
- unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator )
-{
- signed char *src = (signed char*)sourceBuffer;
- signed short *dest = (signed short*)destinationBuffer;
- (void)ditherGenerator; /* unused parameter */
-
- while( count-- )
- {
- (*dest) = (signed short)((*src) << 8);
-
- src += sourceStride;
- dest += destinationStride;
- }
-}
-
-/* -------------------------------------------------------------------------- */
-
-static void Int8_To_UInt8(
- void *destinationBuffer, signed int destinationStride,
- void *sourceBuffer, signed int sourceStride,
- unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator )
-{
- signed char *src = (signed char*)sourceBuffer;
- unsigned char *dest = (unsigned char*)destinationBuffer;
- (void)ditherGenerator; /* unused parameter */
-
- while( count-- )
- {
- (*dest) = (unsigned char)(*src + 128);
-
- src += sourceStride;
- dest += destinationStride;
- }
-}
-
-/* -------------------------------------------------------------------------- */
-
-static void UInt8_To_Float32(
- void *destinationBuffer, signed int destinationStride,
- void *sourceBuffer, signed int sourceStride,
- unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator )
-{
- unsigned char *src = (unsigned char*)sourceBuffer;
- float *dest = (float*)destinationBuffer;
- (void)ditherGenerator; /* unused parameter */
-
- while( count-- )
- {
- float samp = (*src - 128) * const_1_div_128_;
- *dest = samp;
-
- src += sourceStride;
- dest += destinationStride;
- }
-}
-
-/* -------------------------------------------------------------------------- */
-
-static void UInt8_To_Int32(
- void *destinationBuffer, signed int destinationStride,
- void *sourceBuffer, signed int sourceStride,
- unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator )
-{
- unsigned char *src = (unsigned char*)sourceBuffer;
- signed long *dest = (signed long*)destinationBuffer;
- (void)ditherGenerator; /* unused parameter */
-
- while( count-- )
- {
- (*dest) = (*src - 128) << 24;
-
- src += sourceStride;
- dest += destinationStride;
- }
-}
-
-/* -------------------------------------------------------------------------- */
-
-static void UInt8_To_Int24(
- void *destinationBuffer, signed int destinationStride,
- void *sourceBuffer, signed int sourceStride,
- unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator )
-{
- unsigned char *src = (unsigned char*)sourceBuffer;
- unsigned char *dest = (unsigned char*)destinationBuffer;
- (void) ditherGenerator; /* unused parameters */
-
- while( count-- )
- {
-
-#if defined(PA_LITTLE_ENDIAN)
- dest[0] = 0;
- dest[1] = 0;
- dest[2] = (unsigned char)(*src - 128);
-#elif defined(PA_BIG_ENDIAN)
- dest[0] = (unsigned char)(*src - 128);
- dest[1] = 0;
- dest[2] = 0;
-#endif
-
- src += sourceStride;
- dest += destinationStride * 3;
- }
-}
-
-/* -------------------------------------------------------------------------- */
-
-static void UInt8_To_Int16(
- void *destinationBuffer, signed int destinationStride,
- void *sourceBuffer, signed int sourceStride,
- unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator )
-{
- unsigned char *src = (unsigned char*)sourceBuffer;
- signed short *dest = (signed short*)destinationBuffer;
- (void)ditherGenerator; /* unused parameter */
-
- while( count-- )
- {
- (*dest) = (signed short)((*src - 128) << 8);
-
- src += sourceStride;
- dest += destinationStride;
- }
-}
-
-/* -------------------------------------------------------------------------- */
-
-static void UInt8_To_Int8(
- void *destinationBuffer, signed int destinationStride,
- void *sourceBuffer, signed int sourceStride,
- unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator )
-{
- unsigned char *src = (unsigned char*)sourceBuffer;
- signed char *dest = (signed char*)destinationBuffer;
- (void)ditherGenerator; /* unused parameter */
-
- while( count-- )
- {
- (*dest) = (signed char)(*src - 128);
-
- src += sourceStride;
- dest += destinationStride;
- }
-}
-
-/* -------------------------------------------------------------------------- */
-
-static void Copy_8_To_8(
- void *destinationBuffer, signed int destinationStride,
- void *sourceBuffer, signed int sourceStride,
- unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator )
-{
- unsigned char *src = (unsigned char*)sourceBuffer;
- unsigned char *dest = (unsigned char*)destinationBuffer;
-
- (void) ditherGenerator; /* unused parameter */
-
- while( count-- )
- {
- *dest = *src;
-
- src += sourceStride;
- dest += destinationStride;
- }
-}
-
-/* -------------------------------------------------------------------------- */
-
-static void Copy_16_To_16(
- void *destinationBuffer, signed int destinationStride,
- void *sourceBuffer, signed int sourceStride,
- unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator )
-{
- PaUint16 *src = (PaUint16 *)sourceBuffer;
- PaUint16 *dest = (PaUint16 *)destinationBuffer;
-
- (void) ditherGenerator; /* unused parameter */
-
- while( count-- )
- {
- *dest = *src;
-
- src += sourceStride;
- dest += destinationStride;
- }
-}
-
-/* -------------------------------------------------------------------------- */
-
-static void Copy_24_To_24(
- void *destinationBuffer, signed int destinationStride,
- void *sourceBuffer, signed int sourceStride,
- unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator )
-{
- unsigned char *src = (unsigned char*)sourceBuffer;
- unsigned char *dest = (unsigned char*)destinationBuffer;
-
- (void) ditherGenerator; /* unused parameter */
-
- while( count-- )
- {
- dest[0] = src[0];
- dest[1] = src[1];
- dest[2] = src[2];
-
- src += sourceStride * 3;
- dest += destinationStride * 3;
- }
-}
-
-/* -------------------------------------------------------------------------- */
-
-static void Copy_32_To_32(
- void *destinationBuffer, signed int destinationStride,
- void *sourceBuffer, signed int sourceStride,
- unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator )
-{
- PaUint32 *dest = (PaUint32 *)destinationBuffer;
- PaUint32 *src = (PaUint32 *)sourceBuffer;
-
- (void) ditherGenerator; /* unused parameter */
-
- while( count-- )
- {
- *dest = *src;
-
- src += sourceStride;
- dest += destinationStride;
- }
-}
-
-/* -------------------------------------------------------------------------- */
-
-PaUtilConverterTable paConverters = {
- Float32_To_Int32, /* PaUtilConverter *Float32_To_Int32; */
- Float32_To_Int32_Dither, /* PaUtilConverter *Float32_To_Int32_Dither; */
- Float32_To_Int32_Clip, /* PaUtilConverter *Float32_To_Int32_Clip; */
- Float32_To_Int32_DitherClip, /* PaUtilConverter *Float32_To_Int32_DitherClip; */
-
- Float32_To_Int24, /* PaUtilConverter *Float32_To_Int24; */
- Float32_To_Int24_Dither, /* PaUtilConverter *Float32_To_Int24_Dither; */
- Float32_To_Int24_Clip, /* PaUtilConverter *Float32_To_Int24_Clip; */
- Float32_To_Int24_DitherClip, /* PaUtilConverter *Float32_To_Int24_DitherClip; */
-
- Float32_To_Int16, /* PaUtilConverter *Float32_To_Int16; */
- Float32_To_Int16_Dither, /* PaUtilConverter *Float32_To_Int16_Dither; */
- Float32_To_Int16_Clip, /* PaUtilConverter *Float32_To_Int16_Clip; */
- Float32_To_Int16_DitherClip, /* PaUtilConverter *Float32_To_Int16_DitherClip; */
-
- Float32_To_Int8, /* PaUtilConverter *Float32_To_Int8; */
- Float32_To_Int8_Dither, /* PaUtilConverter *Float32_To_Int8_Dither; */
- Float32_To_Int8_Clip, /* PaUtilConverter *Float32_To_Int8_Clip; */
- Float32_To_Int8_DitherClip, /* PaUtilConverter *Float32_To_Int8_DitherClip; */
-
- Float32_To_UInt8, /* PaUtilConverter *Float32_To_UInt8; */
- Float32_To_UInt8_Dither, /* PaUtilConverter *Float32_To_UInt8_Dither; */
- Float32_To_UInt8_Clip, /* PaUtilConverter *Float32_To_UInt8_Clip; */
- Float32_To_UInt8_DitherClip, /* PaUtilConverter *Float32_To_UInt8_DitherClip; */
-
- Int32_To_Float32, /* PaUtilConverter *Int32_To_Float32; */
- Int32_To_Int24, /* PaUtilConverter *Int32_To_Int24; */
- Int32_To_Int24_Dither, /* PaUtilConverter *Int32_To_Int24_Dither; */
- Int32_To_Int16, /* PaUtilConverter *Int32_To_Int16; */
- Int32_To_Int16_Dither, /* PaUtilConverter *Int32_To_Int16_Dither; */
- Int32_To_Int8, /* PaUtilConverter *Int32_To_Int8; */
- Int32_To_Int8_Dither, /* PaUtilConverter *Int32_To_Int8_Dither; */
- Int32_To_UInt8, /* PaUtilConverter *Int32_To_UInt8; */
- Int32_To_UInt8_Dither, /* PaUtilConverter *Int32_To_UInt8_Dither; */
-
- Int24_To_Float32, /* PaUtilConverter *Int24_To_Float32; */
- Int24_To_Int32, /* PaUtilConverter *Int24_To_Int32; */
- Int24_To_Int16, /* PaUtilConverter *Int24_To_Int16; */
- Int24_To_Int16_Dither, /* PaUtilConverter *Int24_To_Int16_Dither; */
- Int24_To_Int8, /* PaUtilConverter *Int24_To_Int8; */
- Int24_To_Int8_Dither, /* PaUtilConverter *Int24_To_Int8_Dither; */
- Int24_To_UInt8, /* PaUtilConverter *Int24_To_UInt8; */
- Int24_To_UInt8_Dither, /* PaUtilConverter *Int24_To_UInt8_Dither; */
-
- Int16_To_Float32, /* PaUtilConverter *Int16_To_Float32; */
- Int16_To_Int32, /* PaUtilConverter *Int16_To_Int32; */
- Int16_To_Int24, /* PaUtilConverter *Int16_To_Int24; */
- Int16_To_Int8, /* PaUtilConverter *Int16_To_Int8; */
- Int16_To_Int8_Dither, /* PaUtilConverter *Int16_To_Int8_Dither; */
- Int16_To_UInt8, /* PaUtilConverter *Int16_To_UInt8; */
- Int16_To_UInt8_Dither, /* PaUtilConverter *Int16_To_UInt8_Dither; */
-
- Int8_To_Float32, /* PaUtilConverter *Int8_To_Float32; */
- Int8_To_Int32, /* PaUtilConverter *Int8_To_Int32; */
- Int8_To_Int24, /* PaUtilConverter *Int8_To_Int24 */
- Int8_To_Int16, /* PaUtilConverter *Int8_To_Int16; */
- Int8_To_UInt8, /* PaUtilConverter *Int8_To_UInt8; */
-
- UInt8_To_Float32, /* PaUtilConverter *UInt8_To_Float32; */
- UInt8_To_Int32, /* PaUtilConverter *UInt8_To_Int32; */
- UInt8_To_Int24, /* PaUtilConverter *UInt8_To_Int24; */
- UInt8_To_Int16, /* PaUtilConverter *UInt8_To_Int16; */
- UInt8_To_Int8, /* PaUtilConverter *UInt8_To_Int8; */
-
- Copy_8_To_8, /* PaUtilConverter *Copy_8_To_8; */
- Copy_16_To_16, /* PaUtilConverter *Copy_16_To_16; */
- Copy_24_To_24, /* PaUtilConverter *Copy_24_To_24; */
- Copy_32_To_32 /* PaUtilConverter *Copy_32_To_32; */
-};
-
-/* -------------------------------------------------------------------------- */
-
-#endif /* PA_NO_STANDARD_CONVERTERS */
-
-/* -------------------------------------------------------------------------- */
-
-PaUtilZeroer* PaUtil_SelectZeroer( PaSampleFormat destinationFormat )
-{
- switch( destinationFormat & ~paNonInterleaved ){
- case paFloat32:
- return paZeroers.Zero32;
- case paInt32:
- return paZeroers.Zero32;
- case paInt24:
- return paZeroers.Zero24;
- case paInt16:
- return paZeroers.Zero16;
- case paInt8:
- return paZeroers.Zero8;
- case paUInt8:
- return paZeroers.ZeroU8;
- default: return 0;
- }
-}
-
-/* -------------------------------------------------------------------------- */
-
-#ifdef PA_NO_STANDARD_ZEROERS
-
-/* -------------------------------------------------------------------------- */
-
-PaUtilZeroerTable paZeroers = {
- 0, /* PaUtilZeroer *ZeroU8; */
- 0, /* PaUtilZeroer *Zero8; */
- 0, /* PaUtilZeroer *Zero16; */
- 0, /* PaUtilZeroer *Zero24; */
- 0, /* PaUtilZeroer *Zero32; */
-};
-
-/* -------------------------------------------------------------------------- */
-
-#else /* PA_NO_STANDARD_ZEROERS is not defined */
-
-/* -------------------------------------------------------------------------- */
-
-static void ZeroU8( void *destinationBuffer, signed int destinationStride,
- unsigned int count )
-{
- unsigned char *dest = (unsigned char*)destinationBuffer;
-
- while( count-- )
- {
- *dest = 128;
-
- dest += destinationStride;
- }
-}
-
-/* -------------------------------------------------------------------------- */
-
-static void Zero8( void *destinationBuffer, signed int destinationStride,
- unsigned int count )
-{
- unsigned char *dest = (unsigned char*)destinationBuffer;
-
- while( count-- )
- {
- *dest = 0;
-
- dest += destinationStride;
- }
-}
-
-/* -------------------------------------------------------------------------- */
-
-static void Zero16( void *destinationBuffer, signed int destinationStride,
- unsigned int count )
-{
- PaUint16 *dest = (PaUint16 *)destinationBuffer;
-
- while( count-- )
- {
- *dest = 0;
-
- dest += destinationStride;
- }
-}
-
-/* -------------------------------------------------------------------------- */
-
-static void Zero24( void *destinationBuffer, signed int destinationStride,
- unsigned int count )
-{
- unsigned char *dest = (unsigned char*)destinationBuffer;
-
- while( count-- )
- {
- dest[0] = 0;
- dest[1] = 0;
- dest[2] = 0;
-
- dest += destinationStride * 3;
- }
-}
-
-/* -------------------------------------------------------------------------- */
-
-static void Zero32( void *destinationBuffer, signed int destinationStride,
- unsigned int count )
-{
- PaUint32 *dest = (PaUint32 *)destinationBuffer;
-
- while( count-- )
- {
- *dest = 0;
-
- dest += destinationStride;
- }
-}
-
-/* -------------------------------------------------------------------------- */
-
-PaUtilZeroerTable paZeroers = {
- ZeroU8, /* PaUtilZeroer *ZeroU8; */
- Zero8, /* PaUtilZeroer *Zero8; */
- Zero16, /* PaUtilZeroer *Zero16; */
- Zero24, /* PaUtilZeroer *Zero24; */
- Zero32, /* PaUtilZeroer *Zero32; */
-};
-
-/* -------------------------------------------------------------------------- */
-
-#endif /* PA_NO_STANDARD_ZEROERS */
+/*
+ * $Id: pa_converters.c,v 1.1.2.26 2004/12/11 16:32:38 aknudsen Exp $
+ * Portable Audio I/O Library sample conversion mechanism
+ *
+ * Based on the Open Source API proposed by Ross Bencina
+ * Copyright (c) 1999-2002 Phil Burk, Ross Bencina
+ *
+ * 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.
+ *
+ * Any person wishing to distribute modifications to the Software is
+ * requested to send the modifications to the original developer so that
+ * they can be incorporated into the canonical version.
+ *
+ * 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.
+ */
+
+/** @file
+ @brief Conversion functions implementations.
+
+ If the C9x function lrintf() is available, define PA_USE_C99_LRINTF to use it
+
+ @todo Consider whether functions which dither but don't clip should exist,
+ V18 automatically enabled clipping whenever dithering was selected. Perhaps
+ we should do the same.
+
+ @todo implement the converters marked IMPLEMENT ME: Float32_To_UInt8_Dither,
+ Float32_To_UInt8_Clip, Float32_To_UInt8_DitherClip, Int32_To_Int24_Dither,
+ Int32_To_UInt8_Dither, Int24_To_Int16_Dither, Int24_To_Int8_Dither,
+ Int24_To_UInt8_Dither, Int16_To_Int8_Dither, Int16_To_UInt8_Dither,
+
+ @todo review the converters marked REVIEW: Float32_To_Int32,
+ Float32_To_Int32_Dither, Float32_To_Int32_Clip, Float32_To_Int32_DitherClip,
+ Int32_To_Int16_Dither, Int32_To_Int8_Dither, Int16_To_Int32
+*/
+
+
+#include "pa_converters.h"
+#include "pa_dither.h"
+#include "pa_endianness.h"
+#include "pa_types.h"
+
+
+PaSampleFormat PaUtil_SelectClosestAvailableFormat(
+ PaSampleFormat availableFormats, PaSampleFormat format )
+{
+ PaSampleFormat result;
+
+ format &= ~paNonInterleaved;
+ availableFormats &= ~paNonInterleaved;
+
+ if( (format & availableFormats) == 0 )
+ {
+ /* NOTE: this code depends on the sample format constants being in
+ descending order of quality - ie best quality is 0
+ FIXME: should write an assert which checks that all of the
+ known constants conform to that requirement.
+ */
+
+ if( format != 0x01 )
+ {
+ /* scan for better formats */
+ result = format;
+ do
+ {
+ result >>= 1;
+ }
+ while( (result & availableFormats) == 0 && result != 0 );
+ }
+ else
+ {
+ result = 0;
+ }
+
+ if( result == 0 ){
+ /* scan for worse formats */
+ result = format;
+ do
+ {
+ result <<= 1;
+ }
+ while( (result & availableFormats) == 0 && result != paCustomFormat );
+
+ if( (result & availableFormats) == 0 )
+ result = paSampleFormatNotSupported;
+ }
+
+ }else{
+ result = format;
+ }
+
+ return result;
+}
+
+/* -------------------------------------------------------------------------- */
+
+#define PA_SELECT_FORMAT_( format, float32, int32, int24, int16, int8, uint8 ) \
+ switch( format & ~paNonInterleaved ){ \
+ case paFloat32: \
+ float32 \
+ case paInt32: \
+ int32 \
+ case paInt24: \
+ int24 \
+ case paInt16: \
+ int16 \
+ case paInt8: \
+ int8 \
+ case paUInt8: \
+ uint8 \
+ default: return 0; \
+ }
+
+/* -------------------------------------------------------------------------- */
+
+#define PA_SELECT_CONVERTER_DITHER_CLIP_( flags, source, destination ) \
+ if( flags & paClipOff ){ /* no clip */ \
+ if( flags & paDitherOff ){ /* no dither */ \
+ return paConverters. source ## _To_ ## destination; \
+ }else{ /* dither */ \
+ return paConverters. source ## _To_ ## destination ## _Dither; \
+ } \
+ }else{ /* clip */ \
+ if( flags & paDitherOff ){ /* no dither */ \
+ return paConverters. source ## _To_ ## destination ## _Clip; \
+ }else{ /* dither */ \
+ return paConverters. source ## _To_ ## destination ## _DitherClip; \
+ } \
+ }
+
+/* -------------------------------------------------------------------------- */
+
+#define PA_SELECT_CONVERTER_DITHER_( flags, source, destination ) \
+ if( flags & paDitherOff ){ /* no dither */ \
+ return paConverters. source ## _To_ ## destination; \
+ }else{ /* dither */ \
+ return paConverters. source ## _To_ ## destination ## _Dither; \
+ }
+
+/* -------------------------------------------------------------------------- */
+
+#define PA_USE_CONVERTER_( source, destination )\
+ return paConverters. source ## _To_ ## destination;
+
+/* -------------------------------------------------------------------------- */
+
+#define PA_UNITY_CONVERSION_( wordlength )\
+ return paConverters. Copy_ ## wordlength ## _To_ ## wordlength;
+
+/* -------------------------------------------------------------------------- */
+
+PaUtilConverter* PaUtil_SelectConverter( PaSampleFormat sourceFormat,
+ PaSampleFormat destinationFormat, PaStreamFlags flags )
+{
+ PA_SELECT_FORMAT_( sourceFormat,
+ /* paFloat32: */
+ PA_SELECT_FORMAT_( destinationFormat,
+ /* paFloat32: */ PA_UNITY_CONVERSION_( 32 ),
+ /* paInt32: */ PA_SELECT_CONVERTER_DITHER_CLIP_( flags, Float32, Int32 ),
+ /* paInt24: */ PA_SELECT_CONVERTER_DITHER_CLIP_( flags, Float32, Int24 ),
+ /* paInt16: */ PA_SELECT_CONVERTER_DITHER_CLIP_( flags, Float32, Int16 ),
+ /* paInt8: */ PA_SELECT_CONVERTER_DITHER_CLIP_( flags, Float32, Int8 ),
+ /* paUInt8: */ PA_SELECT_CONVERTER_DITHER_CLIP_( flags, Float32, UInt8 )
+ ),
+ /* paInt32: */
+ PA_SELECT_FORMAT_( destinationFormat,
+ /* paFloat32: */ PA_USE_CONVERTER_( Int32, Float32 ),
+ /* paInt32: */ PA_UNITY_CONVERSION_( 32 ),
+ /* paInt24: */ PA_SELECT_CONVERTER_DITHER_( flags, Int32, Int24 ),
+ /* paInt16: */ PA_SELECT_CONVERTER_DITHER_( flags, Int32, Int16 ),
+ /* paInt8: */ PA_SELECT_CONVERTER_DITHER_( flags, Int32, Int8 ),
+ /* paUInt8: */ PA_SELECT_CONVERTER_DITHER_( flags, Int32, UInt8 )
+ ),
+ /* paInt24: */
+ PA_SELECT_FORMAT_( destinationFormat,
+ /* paFloat32: */ PA_USE_CONVERTER_( Int24, Float32 ),
+ /* paInt32: */ PA_USE_CONVERTER_( Int24, Int32 ),
+ /* paInt24: */ PA_UNITY_CONVERSION_( 24 ),
+ /* paInt16: */ PA_SELECT_CONVERTER_DITHER_( flags, Int24, Int16 ),
+ /* paInt8: */ PA_SELECT_CONVERTER_DITHER_( flags, Int24, Int8 ),
+ /* paUInt8: */ PA_SELECT_CONVERTER_DITHER_( flags, Int24, UInt8 )
+ ),
+ /* paInt16: */
+ PA_SELECT_FORMAT_( destinationFormat,
+ /* paFloat32: */ PA_USE_CONVERTER_( Int16, Float32 ),
+ /* paInt32: */ PA_USE_CONVERTER_( Int16, Int32 ),
+ /* paInt24: */ PA_USE_CONVERTER_( Int16, Int24 ),
+ /* paInt16: */ PA_UNITY_CONVERSION_( 16 ),
+ /* paInt8: */ PA_SELECT_CONVERTER_DITHER_( flags, Int16, Int8 ),
+ /* paUInt8: */ PA_SELECT_CONVERTER_DITHER_( flags, Int16, UInt8 )
+ ),
+ /* paInt8: */
+ PA_SELECT_FORMAT_( destinationFormat,
+ /* paFloat32: */ PA_USE_CONVERTER_( Int8, Float32 ),
+ /* paInt32: */ PA_USE_CONVERTER_( Int8, Int32 ),
+ /* paInt24: */ PA_USE_CONVERTER_( Int8, Int24 ),
+ /* paInt16: */ PA_USE_CONVERTER_( Int8, Int16 ),
+ /* paInt8: */ PA_UNITY_CONVERSION_( 8 ),
+ /* paUInt8: */ PA_USE_CONVERTER_( Int8, UInt8 )
+ ),
+ /* paUInt8: */
+ PA_SELECT_FORMAT_( destinationFormat,
+ /* paFloat32: */ PA_USE_CONVERTER_( UInt8, Float32 ),
+ /* paInt32: */ PA_USE_CONVERTER_( UInt8, Int32 ),
+ /* paInt24: */ PA_USE_CONVERTER_( UInt8, Int24 ),
+ /* paInt16: */ PA_USE_CONVERTER_( UInt8, Int16 ),
+ /* paInt8: */ PA_USE_CONVERTER_( UInt8, Int8 ),
+ /* paUInt8: */ PA_UNITY_CONVERSION_( 8 )
+ )
+ )
+}
+
+/* -------------------------------------------------------------------------- */
+
+#ifdef PA_NO_STANDARD_CONVERTERS
+
+/* -------------------------------------------------------------------------- */
+
+PaUtilConverterTable paConverters = {
+ 0, /* PaUtilConverter *Float32_To_Int32; */
+ 0, /* PaUtilConverter *Float32_To_Int32_Dither; */
+ 0, /* PaUtilConverter *Float32_To_Int32_Clip; */
+ 0, /* PaUtilConverter *Float32_To_Int32_DitherClip; */
+
+ 0, /* PaUtilConverter *Float32_To_Int24; */
+ 0, /* PaUtilConverter *Float32_To_Int24_Dither; */
+ 0, /* PaUtilConverter *Float32_To_Int24_Clip; */
+ 0, /* PaUtilConverter *Float32_To_Int24_DitherClip; */
+
+ 0, /* PaUtilConverter *Float32_To_Int16; */
+ 0, /* PaUtilConverter *Float32_To_Int16_Dither; */
+ 0, /* PaUtilConverter *Float32_To_Int16_Clip; */
+ 0, /* PaUtilConverter *Float32_To_Int16_DitherClip; */
+
+ 0, /* PaUtilConverter *Float32_To_Int8; */
+ 0, /* PaUtilConverter *Float32_To_Int8_Dither; */
+ 0, /* PaUtilConverter *Float32_To_Int8_Clip; */
+ 0, /* PaUtilConverter *Float32_To_Int8_DitherClip; */
+
+ 0, /* PaUtilConverter *Float32_To_UInt8; */
+ 0, /* PaUtilConverter *Float32_To_UInt8_Dither; */
+ 0, /* PaUtilConverter *Float32_To_UInt8_Clip; */
+ 0, /* PaUtilConverter *Float32_To_UInt8_DitherClip; */
+
+ 0, /* PaUtilConverter *Int32_To_Float32; */
+ 0, /* PaUtilConverter *Int32_To_Int24; */
+ 0, /* PaUtilConverter *Int32_To_Int24_Dither; */
+ 0, /* PaUtilConverter *Int32_To_Int16; */
+ 0, /* PaUtilConverter *Int32_To_Int16_Dither; */
+ 0, /* PaUtilConverter *Int32_To_Int8; */
+ 0, /* PaUtilConverter *Int32_To_Int8_Dither; */
+ 0, /* PaUtilConverter *Int32_To_UInt8; */
+ 0, /* PaUtilConverter *Int32_To_UInt8_Dither; */
+
+ 0, /* PaUtilConverter *Int24_To_Float32; */
+ 0, /* PaUtilConverter *Int24_To_Int32; */
+ 0, /* PaUtilConverter *Int24_To_Int16; */
+ 0, /* PaUtilConverter *Int24_To_Int16_Dither; */
+ 0, /* PaUtilConverter *Int24_To_Int8; */
+ 0, /* PaUtilConverter *Int24_To_Int8_Dither; */
+ 0, /* PaUtilConverter *Int24_To_UInt8; */
+ 0, /* PaUtilConverter *Int24_To_UInt8_Dither; */
+
+ 0, /* PaUtilConverter *Int16_To_Float32; */
+ 0, /* PaUtilConverter *Int16_To_Int32; */
+ 0, /* PaUtilConverter *Int16_To_Int24; */
+ 0, /* PaUtilConverter *Int16_To_Int8; */
+ 0, /* PaUtilConverter *Int16_To_Int8_Dither; */
+ 0, /* PaUtilConverter *Int16_To_UInt8; */
+ 0, /* PaUtilConverter *Int16_To_UInt8_Dither; */
+
+ 0, /* PaUtilConverter *Int8_To_Float32; */
+ 0, /* PaUtilConverter *Int8_To_Int32; */
+ 0, /* PaUtilConverter *Int8_To_Int24 */
+ 0, /* PaUtilConverter *Int8_To_Int16; */
+ 0, /* PaUtilConverter *Int8_To_UInt8; */
+
+ 0, /* PaUtilConverter *UInt8_To_Float32; */
+ 0, /* PaUtilConverter *UInt8_To_Int32; */
+ 0, /* PaUtilConverter *UInt8_To_Int24; */
+ 0, /* PaUtilConverter *UInt8_To_Int16; */
+ 0, /* PaUtilConverter *UInt8_To_Int8; */
+
+ 0, /* PaUtilConverter *Copy_8_To_8; */
+ 0, /* PaUtilConverter *Copy_16_To_16; */
+ 0, /* PaUtilConverter *Copy_24_To_24; */
+ 0 /* PaUtilConverter *Copy_32_To_32; */
+};
+
+/* -------------------------------------------------------------------------- */
+
+#else /* PA_NO_STANDARD_CONVERTERS is not defined */
+
+/* -------------------------------------------------------------------------- */
+
+#define PA_CLIP_( val, min, max )\
+ { val = ((val) < (min)) ? (min) : (((val) > (max)) ? (max) : (val)); }
+
+
+static const float const_1_div_128_ = 1.0f / 128.0f; /* 8 bit multiplier */
+
+static const float const_1_div_32768_ = 1.0f / 32768.f; /* 16 bit multiplier */
+
+static const double const_1_div_2147483648_ = 1.0 / 2147483648.0; /* 32 bit multiplier */
+
+/* -------------------------------------------------------------------------- */
+
+static void Float32_To_Int32(
+ void *destinationBuffer, signed int destinationStride,
+ void *sourceBuffer, signed int sourceStride,
+ unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator )
+{
+ float *src = (float*)sourceBuffer;
+ signed long *dest = (signed long*)destinationBuffer;
+ (void)ditherGenerator; /* unused parameter */
+
+ while( count-- )
+ {
+ /* REVIEW */
+#ifdef PA_USE_C99_LRINTF
+ float scaled = *src * 0x7FFFFFFF;
+ *dest = lrintf(scaled-0.5f);
+#else
+ double scaled = *src * 0x7FFFFFFF;
+ *dest = (signed long) scaled;
+#endif
+
+ src += sourceStride;
+ dest += destinationStride;
+ }
+}
+
+/* -------------------------------------------------------------------------- */
+
+static void Float32_To_Int32_Dither(
+ void *destinationBuffer, signed int destinationStride,
+ void *sourceBuffer, signed int sourceStride,
+ unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator )
+{
+ float *src = (float*)sourceBuffer;
+ signed long *dest = (signed long*)destinationBuffer;
+
+ while( count-- )
+ {
+ /* REVIEW */
+#ifdef PA_USE_C99_LRINTF
+ float dither = PaUtil_GenerateFloatTriangularDither( ditherGenerator );
+ /* use smaller scaler to prevent overflow when we add the dither */
+ float dithered = ((float)*src * (2147483646.0f)) + dither;
+ *dest = lrintf(dithered - 0.5f);
+#else
+ double dither = PaUtil_GenerateFloatTriangularDither( ditherGenerator );
+ /* use smaller scaler to prevent overflow when we add the dither */
+ double dithered = ((double)*src * (2147483646.0)) + dither;
+ *dest = (signed long) dithered;
+#endif
+ src += sourceStride;
+ dest += destinationStride;
+ }
+}
+
+/* -------------------------------------------------------------------------- */
+
+static void Float32_To_Int32_Clip(
+ void *destinationBuffer, signed int destinationStride,
+ void *sourceBuffer, signed int sourceStride,
+ unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator )
+{
+ float *src = (float*)sourceBuffer;
+ signed long *dest = (signed long*)destinationBuffer;
+ (void) ditherGenerator; /* unused parameter */
+
+ while( count-- )
+ {
+ /* REVIEW */
+#ifdef PA_USE_C99_LRINTF
+ float scaled = *src * 0x7FFFFFFF;
+ PA_CLIP_( scaled, -2147483648.f, 2147483647.f );
+ *dest = lrintf(scaled-0.5f);
+#else
+ double scaled = *src * 0x7FFFFFFF;
+ PA_CLIP_( scaled, -2147483648., 2147483647. );
+ *dest = (signed long) scaled;
+#endif
+
+ src += sourceStride;
+ dest += destinationStride;
+ }
+}
+
+/* -------------------------------------------------------------------------- */
+
+static void Float32_To_Int32_DitherClip(
+ void *destinationBuffer, signed int destinationStride,
+ void *sourceBuffer, signed int sourceStride,
+ unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator )
+{
+ float *src = (float*)sourceBuffer;
+ signed long *dest = (signed long*)destinationBuffer;
+
+ while( count-- )
+ {
+ /* REVIEW */
+#ifdef PA_USE_C99_LRINTF
+ float dither = PaUtil_GenerateFloatTriangularDither( ditherGenerator );
+ /* use smaller scaler to prevent overflow when we add the dither */
+ float dithered = ((float)*src * (2147483646.0f)) + dither;
+ PA_CLIP_( dithered, -2147483648.f, 2147483647.f );
+ *dest = lrintf(dithered-0.5f);
+#else
+ double dither = PaUtil_GenerateFloatTriangularDither( ditherGenerator );
+ /* use smaller scaler to prevent overflow when we add the dither */
+ double dithered = ((double)*src * (2147483646.0)) + dither;
+ PA_CLIP_( dithered, -2147483648., 2147483647. );
+ *dest = (signed long) dithered;
+#endif
+
+ src += sourceStride;
+ dest += destinationStride;
+ }
+}
+
+/* -------------------------------------------------------------------------- */
+
+static void Float32_To_Int24(
+ void *destinationBuffer, signed int destinationStride,
+ void *sourceBuffer, signed int sourceStride,
+ unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator )
+{
+ float *src = (float*)sourceBuffer;
+ unsigned char *dest = (unsigned char*)destinationBuffer;
+ signed long temp;
+
+ (void) ditherGenerator; /* unused parameter */
+
+ while( count-- )
+ {
+ /* convert to 32 bit and drop the low 8 bits */
+ double scaled = *src * 0x7FFFFFFF;
+ temp = (signed long) scaled;
+
+#if defined(PA_LITTLE_ENDIAN)
+ dest[0] = (unsigned char)(temp >> 8);
+ dest[1] = (unsigned char)(temp >> 16);
+ dest[2] = (unsigned char)(temp >> 24);
+#elif defined(PA_BIG_ENDIAN)
+ dest[0] = (unsigned char)(temp >> 24);
+ dest[1] = (unsigned char)(temp >> 16);
+ dest[2] = (unsigned char)(temp >> 8);
+#endif
+
+ src += sourceStride;
+ dest += destinationStride * 3;
+ }
+}
+
+/* -------------------------------------------------------------------------- */
+
+static void Float32_To_Int24_Dither(
+ void *destinationBuffer, signed int destinationStride,
+ void *sourceBuffer, signed int sourceStride,
+ unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator )
+{
+ float *src = (float*)sourceBuffer;
+ unsigned char *dest = (unsigned char*)destinationBuffer;
+ signed long temp;
+
+ while( count-- )
+ {
+ /* convert to 32 bit and drop the low 8 bits */
+
+ double dither = PaUtil_GenerateFloatTriangularDither( ditherGenerator );
+ /* use smaller scaler to prevent overflow when we add the dither */
+ double dithered = ((double)*src * (2147483646.0)) + dither;
+
+ temp = (signed long) dithered;
+
+#if defined(PA_LITTLE_ENDIAN)
+ dest[0] = (unsigned char)(temp >> 8);
+ dest[1] = (unsigned char)(temp >> 16);
+ dest[2] = (unsigned char)(temp >> 24);
+#elif defined(PA_BIG_ENDIAN)
+ dest[0] = (unsigned char)(temp >> 24);
+ dest[1] = (unsigned char)(temp >> 16);
+ dest[2] = (unsigned char)(temp >> 8);
+#endif
+
+ src += sourceStride;
+ dest += destinationStride * 3;
+ }
+}
+
+/* -------------------------------------------------------------------------- */
+
+static void Float32_To_Int24_Clip(
+ void *destinationBuffer, signed int destinationStride,
+ void *sourceBuffer, signed int sourceStride,
+ unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator )
+{
+ float *src = (float*)sourceBuffer;
+ unsigned char *dest = (unsigned char*)destinationBuffer;
+ signed long temp;
+
+ (void) ditherGenerator; /* unused parameter */
+
+ while( count-- )
+ {
+ /* convert to 32 bit and drop the low 8 bits */
+ double scaled = *src * 0x7FFFFFFF;
+ PA_CLIP_( scaled, -2147483648., 2147483647. );
+ temp = (signed long) scaled;
+
+#if defined(PA_LITTLE_ENDIAN)
+ dest[0] = (unsigned char)(temp >> 8);
+ dest[1] = (unsigned char)(temp >> 16);
+ dest[2] = (unsigned char)(temp >> 24);
+#elif defined(PA_BIG_ENDIAN)
+ dest[0] = (unsigned char)(temp >> 24);
+ dest[1] = (unsigned char)(temp >> 16);
+ dest[2] = (unsigned char)(temp >> 8);
+#endif
+
+ src += sourceStride;
+ dest += destinationStride * 3;
+ }
+}
+
+/* -------------------------------------------------------------------------- */
+
+static void Float32_To_Int24_DitherClip(
+ void *destinationBuffer, signed int destinationStride,
+ void *sourceBuffer, signed int sourceStride,
+ unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator )
+{
+ float *src = (float*)sourceBuffer;
+ unsigned char *dest = (unsigned char*)destinationBuffer;
+ signed long temp;
+
+ while( count-- )
+ {
+ /* convert to 32 bit and drop the low 8 bits */
+
+ double dither = PaUtil_GenerateFloatTriangularDither( ditherGenerator );
+ /* use smaller scaler to prevent overflow when we add the dither */
+ double dithered = ((double)*src * (2147483646.0)) + dither;
+ PA_CLIP_( dithered, -2147483648., 2147483647. );
+
+ temp = (signed long) dithered;
+
+#if defined(PA_LITTLE_ENDIAN)
+ dest[0] = (unsigned char)(temp >> 8);
+ dest[1] = (unsigned char)(temp >> 16);
+ dest[2] = (unsigned char)(temp >> 24);
+#elif defined(PA_BIG_ENDIAN)
+ dest[0] = (unsigned char)(temp >> 24);
+ dest[1] = (unsigned char)(temp >> 16);
+ dest[2] = (unsigned char)(temp >> 8);
+#endif
+
+ src += sourceStride;
+ dest += destinationStride * 3;
+ }
+}
+
+/* -------------------------------------------------------------------------- */
+
+static void Float32_To_Int16(
+ void *destinationBuffer, signed int destinationStride,
+ void *sourceBuffer, signed int sourceStride,
+ unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator )
+{
+ float *src = (float*)sourceBuffer;
+ signed short *dest = (signed short*)destinationBuffer;
+ (void)ditherGenerator; /* unused parameter */
+
+ while( count-- )
+ {
+#ifdef PA_USE_C99_LRINTF
+ float tempf = (*src * (32767.0f)) ;
+ *dest = lrintf(tempf-0.5f);
+#else
+ short samp = (short) (*src * (32767.0f));
+ *dest = samp;
+#endif
+
+ src += sourceStride;
+ dest += destinationStride;
+ }
+}
+
+/* -------------------------------------------------------------------------- */
+
+static void Float32_To_Int16_Dither(
+ void *destinationBuffer, signed int destinationStride,
+ void *sourceBuffer, signed int sourceStride,
+ unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator )
+{
+ float *src = (float*)sourceBuffer;
+ signed short *dest = (signed short*)destinationBuffer;
+
+ while( count-- )
+ {
+
+ float dither = PaUtil_GenerateFloatTriangularDither( ditherGenerator );
+ /* use smaller scaler to prevent overflow when we add the dither */
+ float dithered = (*src * (32766.0f)) + dither;
+
+#ifdef PA_USE_C99_LRINTF
+ *dest = lrintf(dithered-0.5f);
+#else
+ *dest = (signed short) dithered;
+#endif
+
+ src += sourceStride;
+ dest += destinationStride;
+ }
+}
+
+/* -------------------------------------------------------------------------- */
+
+static void Float32_To_Int16_Clip(
+ void *destinationBuffer, signed int destinationStride,
+ void *sourceBuffer, signed int sourceStride,
+ unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator )
+{
+ float *src = (float*)sourceBuffer;
+ signed short *dest = (signed short*)destinationBuffer;
+ (void)ditherGenerator; /* unused parameter */
+
+ while( count-- )
+ {
+#ifdef PA_USE_C99_LRINTF
+ long samp = lrintf((*src * (32767.0f)) -0.5f);
+#else
+ long samp = (signed long) (*src * (32767.0f));
+#endif
+ PA_CLIP_( samp, -0x8000, 0x7FFF );
+ *dest = (signed short) samp;
+
+ src += sourceStride;
+ dest += destinationStride;
+ }
+}
+
+/* -------------------------------------------------------------------------- */
+
+static void Float32_To_Int16_DitherClip(
+ void *destinationBuffer, signed int destinationStride,
+ void *sourceBuffer, signed int sourceStride,
+ unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator )
+{
+ float *src = (float*)sourceBuffer;
+ signed short *dest = (signed short*)destinationBuffer;
+ (void)ditherGenerator; /* unused parameter */
+
+ while( count-- )
+ {
+
+ float dither = PaUtil_GenerateFloatTriangularDither( ditherGenerator );
+ /* use smaller scaler to prevent overflow when we add the dither */
+ float dithered = (*src * (32766.0f)) + dither;
+ signed long samp = (signed long) dithered;
+ PA_CLIP_( samp, -0x8000, 0x7FFF );
+#ifdef PA_USE_C99_LRINTF
+ *dest = lrintf(samp-0.5f);
+#else
+ *dest = (signed short) samp;
+#endif
+
+ src += sourceStride;
+ dest += destinationStride;
+ }
+}
+
+/* -------------------------------------------------------------------------- */
+
+static void Float32_To_Int8(
+ void *destinationBuffer, signed int destinationStride,
+ void *sourceBuffer, signed int sourceStride,
+ unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator )
+{
+ float *src = (float*)sourceBuffer;
+ signed char *dest = (signed char*)destinationBuffer;
+ (void)ditherGenerator; /* unused parameter */
+
+ while( count-- )
+ {
+ signed char samp = (signed char) (*src * (127.0f));
+ *dest = samp;
+
+ src += sourceStride;
+ dest += destinationStride;
+ }
+}
+
+/* -------------------------------------------------------------------------- */
+
+static void Float32_To_Int8_Dither(
+ void *destinationBuffer, signed int destinationStride,
+ void *sourceBuffer, signed int sourceStride,
+ unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator )
+{
+ float *src = (float*)sourceBuffer;
+ signed char *dest = (signed char*)destinationBuffer;
+ (void)ditherGenerator; /* unused parameter */
+
+ while( count-- )
+ {
+ float dither = PaUtil_GenerateFloatTriangularDither( ditherGenerator );
+ /* use smaller scaler to prevent overflow when we add the dither */
+ float dithered = (*src * (126.0f)) + dither;
+ signed long samp = (signed long) dithered;
+ *dest = (signed char) samp;
+
+ src += sourceStride;
+ dest += destinationStride;
+ }
+}
+
+/* -------------------------------------------------------------------------- */
+
+static void Float32_To_Int8_Clip(
+ void *destinationBuffer, signed int destinationStride,
+ void *sourceBuffer, signed int sourceStride,
+ unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator )
+{
+ float *src = (float*)sourceBuffer;
+ signed char *dest = (signed char*)destinationBuffer;
+ (void)ditherGenerator; /* unused parameter */
+
+ while( count-- )
+ {
+ signed long samp = (signed long)(*src * (127.0f));
+ PA_CLIP_( samp, -0x80, 0x7F );
+ *dest = (signed char) samp;
+
+ src += sourceStride;
+ dest += destinationStride;
+ }
+}
+
+/* -------------------------------------------------------------------------- */
+
+static void Float32_To_Int8_DitherClip(
+ void *destinationBuffer, signed int destinationStride,
+ void *sourceBuffer, signed int sourceStride,
+ unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator )
+{
+ float *src = (float*)sourceBuffer;
+ signed char *dest = (signed char*)destinationBuffer;
+ (void)ditherGenerator; /* unused parameter */
+
+ while( count-- )
+ {
+ float dither = PaUtil_GenerateFloatTriangularDither( ditherGenerator );
+ /* use smaller scaler to prevent overflow when we add the dither */
+ float dithered = (*src * (126.0f)) + dither;
+ signed long samp = (signed long) dithered;
+ PA_CLIP_( samp, -0x80, 0x7F );
+ *dest = (signed char) samp;
+
+ src += sourceStride;
+ dest += destinationStride;
+ }
+}
+
+/* -------------------------------------------------------------------------- */
+
+static void Float32_To_UInt8(
+ void *destinationBuffer, signed int destinationStride,
+ void *sourceBuffer, signed int sourceStride,
+ unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator )
+{
+ float *src = (float*)sourceBuffer;
+ unsigned char *dest = (unsigned char*)destinationBuffer;
+ (void)ditherGenerator; /* unused parameter */
+
+ while( count-- )
+ {
+ unsigned char samp = (unsigned char)(128 + ((unsigned char) (*src * (127.0f))));
+ *dest = samp;
+
+ src += sourceStride;
+ dest += destinationStride;
+ }
+}
+
+/* -------------------------------------------------------------------------- */
+
+static void Float32_To_UInt8_Dither(
+ void *destinationBuffer, signed int destinationStride,
+ void *sourceBuffer, signed int sourceStride,
+ unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator )
+{
+ float *src = (float*)sourceBuffer;
+ unsigned char *dest = (unsigned char*)destinationBuffer;
+ (void)ditherGenerator; /* unused parameter */
+
+ while( count-- )
+ {
+ /* IMPLEMENT ME */
+
+ src += sourceStride;
+ dest += destinationStride;
+ }
+}
+
+/* -------------------------------------------------------------------------- */
+
+static void Float32_To_UInt8_Clip(
+ void *destinationBuffer, signed int destinationStride,
+ void *sourceBuffer, signed int sourceStride,
+ unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator )
+{
+ float *src = (float*)sourceBuffer;
+ unsigned char *dest = (unsigned char*)destinationBuffer;
+ (void)ditherGenerator; /* unused parameter */
+
+ while( count-- )
+ {
+ /* IMPLEMENT ME */
+
+ src += sourceStride;
+ dest += destinationStride;
+ }
+}
+
+/* -------------------------------------------------------------------------- */
+
+static void Float32_To_UInt8_DitherClip(
+ void *destinationBuffer, signed int destinationStride,
+ void *sourceBuffer, signed int sourceStride,
+ unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator )
+{
+ float *src = (float*)sourceBuffer;
+ unsigned char *dest = (unsigned char*)destinationBuffer;
+ (void)ditherGenerator; /* unused parameter */
+
+ while( count-- )
+ {
+ /* IMPLEMENT ME */
+
+ src += sourceStride;
+ dest += destinationStride;
+ }
+}
+
+/* -------------------------------------------------------------------------- */
+
+static void Int32_To_Float32(
+ void *destinationBuffer, signed int destinationStride,
+ void *sourceBuffer, signed int sourceStride,
+ unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator )
+{
+ signed long *src = (signed long*)sourceBuffer;
+ float *dest = (float*)destinationBuffer;
+ (void)ditherGenerator; /* unused parameter */
+
+ while( count-- )
+ {
+ *dest = (float) ((double)*src * const_1_div_2147483648_);
+
+ src += sourceStride;
+ dest += destinationStride;
+ }
+}
+
+/* -------------------------------------------------------------------------- */
+
+static void Int32_To_Int24(
+ void *destinationBuffer, signed int destinationStride,
+ void *sourceBuffer, signed int sourceStride,
+ unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator )
+{
+ signed long *src = (signed long*)sourceBuffer;
+ unsigned char *dest = (unsigned char*)destinationBuffer;
+ (void) ditherGenerator; /* unused parameter */
+
+ while( count-- )
+ {
+ /* REVIEW */
+#if defined(PA_LITTLE_ENDIAN)
+ dest[0] = (unsigned char)(*src >> 8);
+ dest[1] = (unsigned char)(*src >> 16);
+ dest[2] = (unsigned char)(*src >> 24);
+#elif defined(PA_BIG_ENDIAN)
+ dest[0] = (unsigned char)(*src >> 24);
+ dest[1] = (unsigned char)(*src >> 16);
+ dest[2] = (unsigned char)(*src >> 8);
+#endif
+ src += sourceStride;
+ dest += destinationStride * 3;
+ }
+}
+
+/* -------------------------------------------------------------------------- */
+
+static void Int32_To_Int24_Dither(
+ void *destinationBuffer, signed int destinationStride,
+ void *sourceBuffer, signed int sourceStride,
+ unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator )
+{
+ (void) destinationBuffer; /* unused parameters */
+ (void) destinationStride; /* unused parameters */
+ (void) sourceBuffer; /* unused parameters */
+ (void) sourceStride; /* unused parameters */
+ (void) count; /* unused parameters */
+ (void) ditherGenerator; /* unused parameters */
+ /* IMPLEMENT ME */
+}
+
+/* -------------------------------------------------------------------------- */
+
+static void Int32_To_Int16(
+ void *destinationBuffer, signed int destinationStride,
+ void *sourceBuffer, signed int sourceStride,
+ unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator )
+{
+ signed long *src = (signed long*)sourceBuffer;
+ signed short *dest = (signed short*)destinationBuffer;
+ (void)ditherGenerator; /* unused parameter */
+
+ while( count-- )
+ {
+ *dest = (signed short) ((*src) >> 16);
+
+ src += sourceStride;
+ dest += destinationStride;
+ }
+}
+
+/* -------------------------------------------------------------------------- */
+
+static void Int32_To_Int16_Dither(
+ void *destinationBuffer, signed int destinationStride,
+ void *sourceBuffer, signed int sourceStride,
+ unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator )
+{
+ signed long *src = (signed long*)sourceBuffer;
+ signed short *dest = (signed short*)destinationBuffer;
+ signed long dither;
+
+ while( count-- )
+ {
+ /* REVIEW */
+ dither = PaUtil_Generate16BitTriangularDither( ditherGenerator );
+ *dest = (signed short) ((((*src)>>1) + dither) >> 15);
+
+ src += sourceStride;
+ dest += destinationStride;
+ }
+}
+
+/* -------------------------------------------------------------------------- */
+
+static void Int32_To_Int8(
+ void *destinationBuffer, signed int destinationStride,
+ void *sourceBuffer, signed int sourceStride,
+ unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator )
+{
+ signed long *src = (signed long*)sourceBuffer;
+ signed char *dest = (signed char*)destinationBuffer;
+ (void)ditherGenerator; /* unused parameter */
+
+ while( count-- )
+ {
+ *dest = (signed char) ((*src) >> 24);
+
+ src += sourceStride;
+ dest += destinationStride;
+ }
+}
+
+/* -------------------------------------------------------------------------- */
+
+static void Int32_To_Int8_Dither(
+ void *destinationBuffer, signed int destinationStride,
+ void *sourceBuffer, signed int sourceStride,
+ unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator )
+{
+ signed long *src = (signed long*)sourceBuffer;
+ signed char *dest = (signed char*)destinationBuffer;
+ signed long dither;
+
+ while( count-- )
+ {
+ /* REVIEW */
+ dither = PaUtil_Generate16BitTriangularDither( ditherGenerator );
+ *dest = (signed char) ((((*src)>>1) + dither) >> 23);
+
+ src += sourceStride;
+ dest += destinationStride;
+ }
+}
+
+/* -------------------------------------------------------------------------- */
+
+static void Int32_To_UInt8(
+ void *destinationBuffer, signed int destinationStride,
+ void *sourceBuffer, signed int sourceStride,
+ unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator )
+{
+ signed long *src = (signed long*)sourceBuffer;
+ unsigned char *dest = (unsigned char*)destinationBuffer;
+ (void)ditherGenerator; /* unused parameter */
+
+ while( count-- )
+ {
+ (*dest) = (unsigned char)(((*src) >> 24) + 128);
+
+ src += sourceStride;
+ dest += destinationStride;
+ }
+}
+
+/* -------------------------------------------------------------------------- */
+
+static void Int32_To_UInt8_Dither(
+ void *destinationBuffer, signed int destinationStride,
+ void *sourceBuffer, signed int sourceStride,
+ unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator )
+{
+ signed long *src = (signed long*)sourceBuffer;
+ unsigned char *dest = (unsigned char*)destinationBuffer;
+ (void)ditherGenerator; /* unused parameter */
+
+ while( count-- )
+ {
+ /* IMPLEMENT ME */
+
+ src += sourceStride;
+ dest += destinationStride;
+ }
+}
+
+/* -------------------------------------------------------------------------- */
+
+static void Int24_To_Float32(
+ void *destinationBuffer, signed int destinationStride,
+ void *sourceBuffer, signed int sourceStride,
+ unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator )
+{
+ unsigned char *src = (unsigned char*)sourceBuffer;
+ float *dest = (float*)destinationBuffer;
+ signed long temp;
+
+ (void) ditherGenerator; /* unused parameter */
+
+ while( count-- )
+ {
+
+#if defined(PA_LITTLE_ENDIAN)
+ temp = (((long)src[0]) << 8);
+ temp = temp | (((long)src[1]) << 16);
+ temp = temp | (((long)src[2]) << 24);
+#elif defined(PA_BIG_ENDIAN)
+ temp = (((long)src[0]) << 24);
+ temp = temp | (((long)src[1]) << 16);
+ temp = temp | (((long)src[2]) << 8);
+#endif
+
+ *dest = (float) ((double)temp * const_1_div_2147483648_);
+
+ src += sourceStride * 3;
+ dest += destinationStride;
+ }
+}
+
+/* -------------------------------------------------------------------------- */
+
+static void Int24_To_Int32(
+ void *destinationBuffer, signed int destinationStride,
+ void *sourceBuffer, signed int sourceStride,
+ unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator )
+{
+ unsigned char *src = (unsigned char*)sourceBuffer;
+ signed long *dest = (signed long*) destinationBuffer;
+ signed long temp;
+
+ (void) ditherGenerator; /* unused parameter */
+
+ while( count-- )
+ {
+
+#if defined(PA_LITTLE_ENDIAN)
+ temp = (((long)src[0]) << 8);
+ temp = temp | (((long)src[1]) << 16);
+ temp = temp | (((long)src[2]) << 24);
+#elif defined(PA_BIG_ENDIAN)
+ temp = (((long)src[0]) << 24);
+ temp = temp | (((long)src[1]) << 16);
+ temp = temp | (((long)src[2]) << 8);
+#endif
+
+ *dest = temp;
+
+ src += sourceStride * 3;
+ dest += destinationStride;
+ }
+}
+
+/* -------------------------------------------------------------------------- */
+
+static void Int24_To_Int16(
+ void *destinationBuffer, signed int destinationStride,
+ void *sourceBuffer, signed int sourceStride,
+ unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator )
+{
+ unsigned char *src = (unsigned char*)sourceBuffer;
+ signed short *dest = (signed short*)destinationBuffer;
+
+ signed short temp;
+
+ (void) ditherGenerator; /* unused parameter */
+
+ while( count-- )
+ {
+
+#if defined(PA_LITTLE_ENDIAN)
+ /* src[0] is discarded */
+ temp = (((signed short)src[1]));
+ temp = temp | (signed short)(((signed short)src[2]) << 8);
+#elif defined(PA_BIG_ENDIAN)
+ /* src[2] is discarded */
+ temp = (signed short)(((signed short)src[0]) << 8);
+ temp = temp | (((signed short)src[1]));
+#endif
+
+ *dest = temp;
+
+ src += sourceStride * 3;
+ dest += destinationStride;
+ }
+}
+
+/* -------------------------------------------------------------------------- */
+
+static void Int24_To_Int16_Dither(
+ void *destinationBuffer, signed int destinationStride,
+ void *sourceBuffer, signed int sourceStride,
+ unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator )
+{
+ (void) destinationBuffer; /* unused parameters */
+ (void) destinationStride; /* unused parameters */
+ (void) sourceBuffer; /* unused parameters */
+ (void) sourceStride; /* unused parameters */
+ (void) count; /* unused parameters */
+ (void) ditherGenerator; /* unused parameters */
+ /* IMPLEMENT ME */
+}
+
+/* -------------------------------------------------------------------------- */
+
+static void Int24_To_Int8(
+ void *destinationBuffer, signed int destinationStride,
+ void *sourceBuffer, signed int sourceStride,
+ unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator )
+{
+ unsigned char *src = (unsigned char*)sourceBuffer;
+ signed char *dest = (signed char*)destinationBuffer;
+
+ (void) ditherGenerator; /* unused parameter */
+
+ while( count-- )
+ {
+
+#if defined(PA_LITTLE_ENDIAN)
+ /* src[0] is discarded */
+ /* src[1] is discarded */
+ *dest = src[2];
+#elif defined(PA_BIG_ENDIAN)
+ /* src[2] is discarded */
+ /* src[1] is discarded */
+ *dest = src[0];
+#endif
+
+ src += sourceStride * 3;
+ dest += destinationStride;
+ }
+}
+
+/* -------------------------------------------------------------------------- */
+
+static void Int24_To_Int8_Dither(
+ void *destinationBuffer, signed int destinationStride,
+ void *sourceBuffer, signed int sourceStride,
+ unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator )
+{
+ (void) destinationBuffer; /* unused parameters */
+ (void) destinationStride; /* unused parameters */
+ (void) sourceBuffer; /* unused parameters */
+ (void) sourceStride; /* unused parameters */
+ (void) count; /* unused parameters */
+ (void) ditherGenerator; /* unused parameters */
+ /* IMPLEMENT ME */
+}
+
+/* -------------------------------------------------------------------------- */
+
+static void Int24_To_UInt8(
+ void *destinationBuffer, signed int destinationStride,
+ void *sourceBuffer, signed int sourceStride,
+ unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator )
+{
+ unsigned char *src = (unsigned char*)sourceBuffer;
+ unsigned char *dest = (unsigned char*)destinationBuffer;
+
+ (void) ditherGenerator; /* unused parameter */
+
+ while( count-- )
+ {
+
+#if defined(PA_LITTLE_ENDIAN)
+ /* src[0] is discarded */
+ /* src[1] is discarded */
+ *dest = (unsigned char)(src[2] + 128);
+#elif defined(PA_BIG_ENDIAN)
+ *dest = (unsigned char)(src[0] + 128);
+ /* src[1] is discarded */
+ /* src[2] is discarded */
+#endif
+
+ src += sourceStride * 3;
+ dest += destinationStride;
+ }
+}
+
+/* -------------------------------------------------------------------------- */
+
+static void Int24_To_UInt8_Dither(
+ void *destinationBuffer, signed int destinationStride,
+ void *sourceBuffer, signed int sourceStride,
+ unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator )
+{
+ (void) destinationBuffer; /* unused parameters */
+ (void) destinationStride; /* unused parameters */
+ (void) sourceBuffer; /* unused parameters */
+ (void) sourceStride; /* unused parameters */
+ (void) count; /* unused parameters */
+ (void) ditherGenerator; /* unused parameters */
+ /* IMPLEMENT ME */
+}
+
+/* -------------------------------------------------------------------------- */
+
+static void Int16_To_Float32(
+ void *destinationBuffer, signed int destinationStride,
+ void *sourceBuffer, signed int sourceStride,
+ unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator )
+{
+ signed short *src = (signed short*)sourceBuffer;
+ float *dest = (float*)destinationBuffer;
+ (void)ditherGenerator; /* unused parameter */
+
+ while( count-- )
+ {
+ float samp = *src * const_1_div_32768_; /* FIXME: i'm concerned about this being asymetrical with float->int16 -rb */
+ *dest = samp;
+
+ src += sourceStride;
+ dest += destinationStride;
+ }
+}
+
+/* -------------------------------------------------------------------------- */
+
+static void Int16_To_Int32(
+ void *destinationBuffer, signed int destinationStride,
+ void *sourceBuffer, signed int sourceStride,
+ unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator )
+{
+ signed short *src = (signed short*)sourceBuffer;
+ signed long *dest = (signed long*)destinationBuffer;
+ (void)ditherGenerator; /* unused parameter */
+
+ while( count-- )
+ {
+ /* REVIEW: we should consider something like
+ (*src << 16) | (*src & 0xFFFF)
+ */
+
+ *dest = *src << 16;
+
+ src += sourceStride;
+ dest += destinationStride;
+ }
+}
+
+/* -------------------------------------------------------------------------- */
+
+static void Int16_To_Int24(
+ void *destinationBuffer, signed int destinationStride,
+ void *sourceBuffer, signed int sourceStride,
+ unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator )
+{
+ signed short *src = (signed short*) sourceBuffer;
+ unsigned char *dest = (unsigned char*)destinationBuffer;
+ signed short temp;
+
+ (void) ditherGenerator; /* unused parameter */
+
+ while( count-- )
+ {
+ temp = *src;
+
+#if defined(PA_LITTLE_ENDIAN)
+ dest[0] = 0;
+ dest[1] = (unsigned char)(temp);
+ dest[2] = (unsigned char)(temp >> 8);
+#elif defined(PA_BIG_ENDIAN)
+ dest[0] = (unsigned char)(temp >> 8);
+ dest[1] = (unsigned char)(temp);
+ dest[2] = 0;
+#endif
+
+ src += sourceStride;
+ dest += destinationStride * 3;
+ }
+}
+
+/* -------------------------------------------------------------------------- */
+
+static void Int16_To_Int8(
+ void *destinationBuffer, signed int destinationStride,
+ void *sourceBuffer, signed int sourceStride,
+ unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator )
+{
+ signed short *src = (signed short*)sourceBuffer;
+ signed char *dest = (signed char*)destinationBuffer;
+ (void)ditherGenerator; /* unused parameter */
+
+ while( count-- )
+ {
+ (*dest) = (signed char)((*src) >> 8);
+
+ src += sourceStride;
+ dest += destinationStride;
+ }
+}
+
+/* -------------------------------------------------------------------------- */
+
+static void Int16_To_Int8_Dither(
+ void *destinationBuffer, signed int destinationStride,
+ void *sourceBuffer, signed int sourceStride,
+ unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator )
+{
+ signed short *src = (signed short*)sourceBuffer;
+ signed char *dest = (signed char*)destinationBuffer;
+ (void)ditherGenerator; /* unused parameter */
+
+ while( count-- )
+ {
+ /* IMPLEMENT ME */
+
+ src += sourceStride;
+ dest += destinationStride;
+ }
+}
+
+/* -------------------------------------------------------------------------- */
+
+static void Int16_To_UInt8(
+ void *destinationBuffer, signed int destinationStride,
+ void *sourceBuffer, signed int sourceStride,
+ unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator )
+{
+ signed short *src = (signed short*)sourceBuffer;
+ unsigned char *dest = (unsigned char*)destinationBuffer;
+ (void)ditherGenerator; /* unused parameter */
+
+ while( count-- )
+ {
+ (*dest) = (unsigned char)(((*src) >> 8) + 128);
+
+ src += sourceStride;
+ dest += destinationStride;
+ }
+}
+
+/* -------------------------------------------------------------------------- */
+
+static void Int16_To_UInt8_Dither(
+ void *destinationBuffer, signed int destinationStride,
+ void *sourceBuffer, signed int sourceStride,
+ unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator )
+{
+ signed short *src = (signed short*)sourceBuffer;
+ unsigned char *dest = (unsigned char*)destinationBuffer;
+ (void)ditherGenerator; /* unused parameter */
+
+ while( count-- )
+ {
+ /* IMPLEMENT ME */
+
+ src += sourceStride;
+ dest += destinationStride;
+ }
+}
+
+/* -------------------------------------------------------------------------- */
+
+static void Int8_To_Float32(
+ void *destinationBuffer, signed int destinationStride,
+ void *sourceBuffer, signed int sourceStride,
+ unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator )
+{
+ signed char *src = (signed char*)sourceBuffer;
+ float *dest = (float*)destinationBuffer;
+ (void)ditherGenerator; /* unused parameter */
+
+ while( count-- )
+ {
+ float samp = *src * const_1_div_128_;
+ *dest = samp;
+
+ src += sourceStride;
+ dest += destinationStride;
+ }
+}
+
+/* -------------------------------------------------------------------------- */
+
+static void Int8_To_Int32(
+ void *destinationBuffer, signed int destinationStride,
+ void *sourceBuffer, signed int sourceStride,
+ unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator )
+{
+ signed char *src = (signed char*)sourceBuffer;
+ signed long *dest = (signed long*)destinationBuffer;
+ (void)ditherGenerator; /* unused parameter */
+
+ while( count-- )
+ {
+ (*dest) = (*src) << 24;
+
+ src += sourceStride;
+ dest += destinationStride;
+ }
+}
+
+/* -------------------------------------------------------------------------- */
+
+static void Int8_To_Int24(
+ void *destinationBuffer, signed int destinationStride,
+ void *sourceBuffer, signed int sourceStride,
+ unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator )
+{
+ signed char *src = (signed char*)sourceBuffer;
+ unsigned char *dest = (unsigned char*)destinationBuffer;
+ (void)ditherGenerator; /* unused parameter */
+
+ while( count-- )
+ {
+
+#if defined(PA_LITTLE_ENDIAN)
+ dest[0] = 0;
+ dest[1] = 0;
+ dest[2] = (*src);
+#elif defined(PA_BIG_ENDIAN)
+ dest[0] = (*src);
+ dest[1] = 0;
+ dest[2] = 0;
+#endif
+
+ src += sourceStride;
+ dest += destinationStride * 3;
+ }
+}
+
+/* -------------------------------------------------------------------------- */
+
+static void Int8_To_Int16(
+ void *destinationBuffer, signed int destinationStride,
+ void *sourceBuffer, signed int sourceStride,
+ unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator )
+{
+ signed char *src = (signed char*)sourceBuffer;
+ signed short *dest = (signed short*)destinationBuffer;
+ (void)ditherGenerator; /* unused parameter */
+
+ while( count-- )
+ {
+ (*dest) = (signed short)((*src) << 8);
+
+ src += sourceStride;
+ dest += destinationStride;
+ }
+}
+
+/* -------------------------------------------------------------------------- */
+
+static void Int8_To_UInt8(
+ void *destinationBuffer, signed int destinationStride,
+ void *sourceBuffer, signed int sourceStride,
+ unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator )
+{
+ signed char *src = (signed char*)sourceBuffer;
+ unsigned char *dest = (unsigned char*)destinationBuffer;
+ (void)ditherGenerator; /* unused parameter */
+
+ while( count-- )
+ {
+ (*dest) = (unsigned char)(*src + 128);
+
+ src += sourceStride;
+ dest += destinationStride;
+ }
+}
+
+/* -------------------------------------------------------------------------- */
+
+static void UInt8_To_Float32(
+ void *destinationBuffer, signed int destinationStride,
+ void *sourceBuffer, signed int sourceStride,
+ unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator )
+{
+ unsigned char *src = (unsigned char*)sourceBuffer;
+ float *dest = (float*)destinationBuffer;
+ (void)ditherGenerator; /* unused parameter */
+
+ while( count-- )
+ {
+ float samp = (*src - 128) * const_1_div_128_;
+ *dest = samp;
+
+ src += sourceStride;
+ dest += destinationStride;
+ }
+}
+
+/* -------------------------------------------------------------------------- */
+
+static void UInt8_To_Int32(
+ void *destinationBuffer, signed int destinationStride,
+ void *sourceBuffer, signed int sourceStride,
+ unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator )
+{
+ unsigned char *src = (unsigned char*)sourceBuffer;
+ signed long *dest = (signed long*)destinationBuffer;
+ (void)ditherGenerator; /* unused parameter */
+
+ while( count-- )
+ {
+ (*dest) = (*src - 128) << 24;
+
+ src += sourceStride;
+ dest += destinationStride;
+ }
+}
+
+/* -------------------------------------------------------------------------- */
+
+static void UInt8_To_Int24(
+ void *destinationBuffer, signed int destinationStride,
+ void *sourceBuffer, signed int sourceStride,
+ unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator )
+{
+ unsigned char *src = (unsigned char*)sourceBuffer;
+ unsigned char *dest = (unsigned char*)destinationBuffer;
+ (void) ditherGenerator; /* unused parameters */
+
+ while( count-- )
+ {
+
+#if defined(PA_LITTLE_ENDIAN)
+ dest[0] = 0;
+ dest[1] = 0;
+ dest[2] = (unsigned char)(*src - 128);
+#elif defined(PA_BIG_ENDIAN)
+ dest[0] = (unsigned char)(*src - 128);
+ dest[1] = 0;
+ dest[2] = 0;
+#endif
+
+ src += sourceStride;
+ dest += destinationStride * 3;
+ }
+}
+
+/* -------------------------------------------------------------------------- */
+
+static void UInt8_To_Int16(
+ void *destinationBuffer, signed int destinationStride,
+ void *sourceBuffer, signed int sourceStride,
+ unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator )
+{
+ unsigned char *src = (unsigned char*)sourceBuffer;
+ signed short *dest = (signed short*)destinationBuffer;
+ (void)ditherGenerator; /* unused parameter */
+
+ while( count-- )
+ {
+ (*dest) = (signed short)((*src - 128) << 8);
+
+ src += sourceStride;
+ dest += destinationStride;
+ }
+}
+
+/* -------------------------------------------------------------------------- */
+
+static void UInt8_To_Int8(
+ void *destinationBuffer, signed int destinationStride,
+ void *sourceBuffer, signed int sourceStride,
+ unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator )
+{
+ unsigned char *src = (unsigned char*)sourceBuffer;
+ signed char *dest = (signed char*)destinationBuffer;
+ (void)ditherGenerator; /* unused parameter */
+
+ while( count-- )
+ {
+ (*dest) = (signed char)(*src - 128);
+
+ src += sourceStride;
+ dest += destinationStride;
+ }
+}
+
+/* -------------------------------------------------------------------------- */
+
+static void Copy_8_To_8(
+ void *destinationBuffer, signed int destinationStride,
+ void *sourceBuffer, signed int sourceStride,
+ unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator )
+{
+ unsigned char *src = (unsigned char*)sourceBuffer;
+ unsigned char *dest = (unsigned char*)destinationBuffer;
+
+ (void) ditherGenerator; /* unused parameter */
+
+ while( count-- )
+ {
+ *dest = *src;
+
+ src += sourceStride;
+ dest += destinationStride;
+ }
+}
+
+/* -------------------------------------------------------------------------- */
+
+static void Copy_16_To_16(
+ void *destinationBuffer, signed int destinationStride,
+ void *sourceBuffer, signed int sourceStride,
+ unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator )
+{
+ PaUint16 *src = (PaUint16 *)sourceBuffer;
+ PaUint16 *dest = (PaUint16 *)destinationBuffer;
+
+ (void) ditherGenerator; /* unused parameter */
+
+ while( count-- )
+ {
+ *dest = *src;
+
+ src += sourceStride;
+ dest += destinationStride;
+ }
+}
+
+/* -------------------------------------------------------------------------- */
+
+static void Copy_24_To_24(
+ void *destinationBuffer, signed int destinationStride,
+ void *sourceBuffer, signed int sourceStride,
+ unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator )
+{
+ unsigned char *src = (unsigned char*)sourceBuffer;
+ unsigned char *dest = (unsigned char*)destinationBuffer;
+
+ (void) ditherGenerator; /* unused parameter */
+
+ while( count-- )
+ {
+ dest[0] = src[0];
+ dest[1] = src[1];
+ dest[2] = src[2];
+
+ src += sourceStride * 3;
+ dest += destinationStride * 3;
+ }
+}
+
+/* -------------------------------------------------------------------------- */
+
+static void Copy_32_To_32(
+ void *destinationBuffer, signed int destinationStride,
+ void *sourceBuffer, signed int sourceStride,
+ unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator )
+{
+ PaUint32 *dest = (PaUint32 *)destinationBuffer;
+ PaUint32 *src = (PaUint32 *)sourceBuffer;
+
+ (void) ditherGenerator; /* unused parameter */
+
+ while( count-- )
+ {
+ *dest = *src;
+
+ src += sourceStride;
+ dest += destinationStride;
+ }
+}
+
+/* -------------------------------------------------------------------------- */
+
+PaUtilConverterTable paConverters = {
+ Float32_To_Int32, /* PaUtilConverter *Float32_To_Int32; */
+ Float32_To_Int32_Dither, /* PaUtilConverter *Float32_To_Int32_Dither; */
+ Float32_To_Int32_Clip, /* PaUtilConverter *Float32_To_Int32_Clip; */
+ Float32_To_Int32_DitherClip, /* PaUtilConverter *Float32_To_Int32_DitherClip; */
+
+ Float32_To_Int24, /* PaUtilConverter *Float32_To_Int24; */
+ Float32_To_Int24_Dither, /* PaUtilConverter *Float32_To_Int24_Dither; */
+ Float32_To_Int24_Clip, /* PaUtilConverter *Float32_To_Int24_Clip; */
+ Float32_To_Int24_DitherClip, /* PaUtilConverter *Float32_To_Int24_DitherClip; */
+
+ Float32_To_Int16, /* PaUtilConverter *Float32_To_Int16; */
+ Float32_To_Int16_Dither, /* PaUtilConverter *Float32_To_Int16_Dither; */
+ Float32_To_Int16_Clip, /* PaUtilConverter *Float32_To_Int16_Clip; */
+ Float32_To_Int16_DitherClip, /* PaUtilConverter *Float32_To_Int16_DitherClip; */
+
+ Float32_To_Int8, /* PaUtilConverter *Float32_To_Int8; */
+ Float32_To_Int8_Dither, /* PaUtilConverter *Float32_To_Int8_Dither; */
+ Float32_To_Int8_Clip, /* PaUtilConverter *Float32_To_Int8_Clip; */
+ Float32_To_Int8_DitherClip, /* PaUtilConverter *Float32_To_Int8_DitherClip; */
+
+ Float32_To_UInt8, /* PaUtilConverter *Float32_To_UInt8; */
+ Float32_To_UInt8_Dither, /* PaUtilConverter *Float32_To_UInt8_Dither; */
+ Float32_To_UInt8_Clip, /* PaUtilConverter *Float32_To_UInt8_Clip; */
+ Float32_To_UInt8_DitherClip, /* PaUtilConverter *Float32_To_UInt8_DitherClip; */
+
+ Int32_To_Float32, /* PaUtilConverter *Int32_To_Float32; */
+ Int32_To_Int24, /* PaUtilConverter *Int32_To_Int24; */
+ Int32_To_Int24_Dither, /* PaUtilConverter *Int32_To_Int24_Dither; */
+ Int32_To_Int16, /* PaUtilConverter *Int32_To_Int16; */
+ Int32_To_Int16_Dither, /* PaUtilConverter *Int32_To_Int16_Dither; */
+ Int32_To_Int8, /* PaUtilConverter *Int32_To_Int8; */
+ Int32_To_Int8_Dither, /* PaUtilConverter *Int32_To_Int8_Dither; */
+ Int32_To_UInt8, /* PaUtilConverter *Int32_To_UInt8; */
+ Int32_To_UInt8_Dither, /* PaUtilConverter *Int32_To_UInt8_Dither; */
+
+ Int24_To_Float32, /* PaUtilConverter *Int24_To_Float32; */
+ Int24_To_Int32, /* PaUtilConverter *Int24_To_Int32; */
+ Int24_To_Int16, /* PaUtilConverter *Int24_To_Int16; */
+ Int24_To_Int16_Dither, /* PaUtilConverter *Int24_To_Int16_Dither; */
+ Int24_To_Int8, /* PaUtilConverter *Int24_To_Int8; */
+ Int24_To_Int8_Dither, /* PaUtilConverter *Int24_To_Int8_Dither; */
+ Int24_To_UInt8, /* PaUtilConverter *Int24_To_UInt8; */
+ Int24_To_UInt8_Dither, /* PaUtilConverter *Int24_To_UInt8_Dither; */
+
+ Int16_To_Float32, /* PaUtilConverter *Int16_To_Float32; */
+ Int16_To_Int32, /* PaUtilConverter *Int16_To_Int32; */
+ Int16_To_Int24, /* PaUtilConverter *Int16_To_Int24; */
+ Int16_To_Int8, /* PaUtilConverter *Int16_To_Int8; */
+ Int16_To_Int8_Dither, /* PaUtilConverter *Int16_To_Int8_Dither; */
+ Int16_To_UInt8, /* PaUtilConverter *Int16_To_UInt8; */
+ Int16_To_UInt8_Dither, /* PaUtilConverter *Int16_To_UInt8_Dither; */
+
+ Int8_To_Float32, /* PaUtilConverter *Int8_To_Float32; */
+ Int8_To_Int32, /* PaUtilConverter *Int8_To_Int32; */
+ Int8_To_Int24, /* PaUtilConverter *Int8_To_Int24 */
+ Int8_To_Int16, /* PaUtilConverter *Int8_To_Int16; */
+ Int8_To_UInt8, /* PaUtilConverter *Int8_To_UInt8; */
+
+ UInt8_To_Float32, /* PaUtilConverter *UInt8_To_Float32; */
+ UInt8_To_Int32, /* PaUtilConverter *UInt8_To_Int32; */
+ UInt8_To_Int24, /* PaUtilConverter *UInt8_To_Int24; */
+ UInt8_To_Int16, /* PaUtilConverter *UInt8_To_Int16; */
+ UInt8_To_Int8, /* PaUtilConverter *UInt8_To_Int8; */
+
+ Copy_8_To_8, /* PaUtilConverter *Copy_8_To_8; */
+ Copy_16_To_16, /* PaUtilConverter *Copy_16_To_16; */
+ Copy_24_To_24, /* PaUtilConverter *Copy_24_To_24; */
+ Copy_32_To_32 /* PaUtilConverter *Copy_32_To_32; */
+};
+
+/* -------------------------------------------------------------------------- */
+
+#endif /* PA_NO_STANDARD_CONVERTERS */
+
+/* -------------------------------------------------------------------------- */
+
+PaUtilZeroer* PaUtil_SelectZeroer( PaSampleFormat destinationFormat )
+{
+ switch( destinationFormat & ~paNonInterleaved ){
+ case paFloat32:
+ return paZeroers.Zero32;
+ case paInt32:
+ return paZeroers.Zero32;
+ case paInt24:
+ return paZeroers.Zero24;
+ case paInt16:
+ return paZeroers.Zero16;
+ case paInt8:
+ return paZeroers.Zero8;
+ case paUInt8:
+ return paZeroers.ZeroU8;
+ default: return 0;
+ }
+}
+
+/* -------------------------------------------------------------------------- */
+
+#ifdef PA_NO_STANDARD_ZEROERS
+
+/* -------------------------------------------------------------------------- */
+
+PaUtilZeroerTable paZeroers = {
+ 0, /* PaUtilZeroer *ZeroU8; */
+ 0, /* PaUtilZeroer *Zero8; */
+ 0, /* PaUtilZeroer *Zero16; */
+ 0, /* PaUtilZeroer *Zero24; */
+ 0, /* PaUtilZeroer *Zero32; */
+};
+
+/* -------------------------------------------------------------------------- */
+
+#else /* PA_NO_STANDARD_ZEROERS is not defined */
+
+/* -------------------------------------------------------------------------- */
+
+static void ZeroU8( void *destinationBuffer, signed int destinationStride,
+ unsigned int count )
+{
+ unsigned char *dest = (unsigned char*)destinationBuffer;
+
+ while( count-- )
+ {
+ *dest = 128;
+
+ dest += destinationStride;
+ }
+}
+
+/* -------------------------------------------------------------------------- */
+
+static void Zero8( void *destinationBuffer, signed int destinationStride,
+ unsigned int count )
+{
+ unsigned char *dest = (unsigned char*)destinationBuffer;
+
+ while( count-- )
+ {
+ *dest = 0;
+
+ dest += destinationStride;
+ }
+}
+
+/* -------------------------------------------------------------------------- */
+
+static void Zero16( void *destinationBuffer, signed int destinationStride,
+ unsigned int count )
+{
+ PaUint16 *dest = (PaUint16 *)destinationBuffer;
+
+ while( count-- )
+ {
+ *dest = 0;
+
+ dest += destinationStride;
+ }
+}
+
+/* -------------------------------------------------------------------------- */
+
+static void Zero24( void *destinationBuffer, signed int destinationStride,
+ unsigned int count )
+{
+ unsigned char *dest = (unsigned char*)destinationBuffer;
+
+ while( count-- )
+ {
+ dest[0] = 0;
+ dest[1] = 0;
+ dest[2] = 0;
+
+ dest += destinationStride * 3;
+ }
+}
+
+/* -------------------------------------------------------------------------- */
+
+static void Zero32( void *destinationBuffer, signed int destinationStride,
+ unsigned int count )
+{
+ PaUint32 *dest = (PaUint32 *)destinationBuffer;
+
+ while( count-- )
+ {
+ *dest = 0;
+
+ dest += destinationStride;
+ }
+}
+
+/* -------------------------------------------------------------------------- */
+
+PaUtilZeroerTable paZeroers = {
+ ZeroU8, /* PaUtilZeroer *ZeroU8; */
+ Zero8, /* PaUtilZeroer *Zero8; */
+ Zero16, /* PaUtilZeroer *Zero16; */
+ Zero24, /* PaUtilZeroer *Zero24; */
+ Zero32, /* PaUtilZeroer *Zero32; */
+};
+
+/* -------------------------------------------------------------------------- */
+
+#endif /* PA_NO_STANDARD_ZEROERS */
diff --git a/pjmedia/src/pjmedia/portaudio/pa_converters.h b/pjmedia/src/pjmedia/portaudio/pa_converters.h
index a328d9a9..831c9c64 100644
--- a/pjmedia/src/pjmedia/portaudio/pa_converters.h
+++ b/pjmedia/src/pjmedia/portaudio/pa_converters.h
@@ -1,254 +1,254 @@
-#ifndef PA_CONVERTERS_H
-#define PA_CONVERTERS_H
-/*
- * $Id: pa_converters.h,v 1.1.2.9 2003/09/20 21:05:14 rossbencina Exp $
- * Portable Audio I/O Library sample conversion mechanism
- *
- * Based on the Open Source API proposed by Ross Bencina
- * Copyright (c) 1999-2002 Phil Burk, Ross Bencina
- *
- * 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.
- *
- * Any person wishing to distribute modifications to the Software is
- * requested to send the modifications to the original developer so that
- * they can be incorporated into the canonical version.
- *
- * 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.
- */
-
-/** @file
- @brief Conversion functions used to convert buffers of samples from one
- format to another.
-*/
-
-
-#include "portaudio.h" /* for PaSampleFormat */
-
-#ifdef __cplusplus
-extern "C"
-{
-#endif /* __cplusplus */
-
-
-struct PaUtilTriangularDitherGenerator;
-
-
-/** Choose an available sample format which is most appropriate for
- representing the requested format. If the requested format is not available
- higher quality formats are considered before lower quality formates.
- @param availableFormats A variable containing the logical OR of all available
- formats.
- @param format The desired format.
- @return The most appropriate available format for representing the requested
- format.
-*/
-PaSampleFormat PaUtil_SelectClosestAvailableFormat(
- PaSampleFormat availableFormats, PaSampleFormat format );
-
-
-/* high level conversions functions for use by implementations */
-
-
-/** The generic sample converter prototype. Sample converters convert count
- samples from sourceBuffer to destinationBuffer. The actual type of the data
- pointed to by these parameters varys for different converter functions.
- @param destinationBuffer A pointer to the first sample of the destination.
- @param destinationStride An offset between successive destination samples
- expressed in samples (not bytes.) It may be negative.
- @param sourceBuffer A pointer to the first sample of the source.
- @param sourceStride An offset between successive source samples
- expressed in samples (not bytes.) It may be negative.
- @param count The number of samples to convert.
- @param ditherState State information used to calculate dither. Converters
- that do not perform dithering will ignore this parameter, in which case
- NULL or invalid dither state may be passed.
-*/
-typedef void PaUtilConverter(
- void *destinationBuffer, signed int destinationStride,
- void *sourceBuffer, signed int sourceStride,
- unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator );
-
-
-/** Find a sample converter function for the given source and destinations
- formats and flags (clip and dither.)
- @return
- A pointer to a PaUtilConverter which will perform the requested
- conversion, or NULL if the given format conversion is not supported.
- For conversions where clipping or dithering is not necessary, the
- clip and dither flags are ignored and a non-clipping or dithering
- version is returned.
- If the source and destination formats are the same, a function which
- copies data of the appropriate size will be returned.
-*/
-PaUtilConverter* PaUtil_SelectConverter( PaSampleFormat sourceFormat,
- PaSampleFormat destinationFormat, PaStreamFlags flags );
-
-
-/** The generic buffer zeroer prototype. Buffer zeroers copy count zeros to
- destinationBuffer. The actual type of the data pointed to varys for
- different zeroer functions.
- @param destinationBuffer A pointer to the first sample of the destination.
- @param destinationStride An offset between successive destination samples
- expressed in samples (not bytes.) It may be negative.
- @param count The number of samples to zero.
-*/
-typedef void PaUtilZeroer(
- void *destinationBuffer, signed int destinationStride, unsigned int count );
-
-
-/** Find a buffer zeroer function for the given destination format.
- @return
- A pointer to a PaUtilZeroer which will perform the requested
- zeroing.
-*/
-PaUtilZeroer* PaUtil_SelectZeroer( PaSampleFormat destinationFormat );
-
-/*----------------------------------------------------------------------------*/
-/* low level functions and data structures which may be used for
- substituting conversion functions */
-
-
-/** The type used to store all sample conversion functions.
- @see paConverters;
-*/
-typedef struct{
- PaUtilConverter *Float32_To_Int32;
- PaUtilConverter *Float32_To_Int32_Dither;
- PaUtilConverter *Float32_To_Int32_Clip;
- PaUtilConverter *Float32_To_Int32_DitherClip;
-
- PaUtilConverter *Float32_To_Int24;
- PaUtilConverter *Float32_To_Int24_Dither;
- PaUtilConverter *Float32_To_Int24_Clip;
- PaUtilConverter *Float32_To_Int24_DitherClip;
-
- PaUtilConverter *Float32_To_Int16;
- PaUtilConverter *Float32_To_Int16_Dither;
- PaUtilConverter *Float32_To_Int16_Clip;
- PaUtilConverter *Float32_To_Int16_DitherClip;
-
- PaUtilConverter *Float32_To_Int8;
- PaUtilConverter *Float32_To_Int8_Dither;
- PaUtilConverter *Float32_To_Int8_Clip;
- PaUtilConverter *Float32_To_Int8_DitherClip;
-
- PaUtilConverter *Float32_To_UInt8;
- PaUtilConverter *Float32_To_UInt8_Dither;
- PaUtilConverter *Float32_To_UInt8_Clip;
- PaUtilConverter *Float32_To_UInt8_DitherClip;
-
- PaUtilConverter *Int32_To_Float32;
- PaUtilConverter *Int32_To_Int24;
- PaUtilConverter *Int32_To_Int24_Dither;
- PaUtilConverter *Int32_To_Int16;
- PaUtilConverter *Int32_To_Int16_Dither;
- PaUtilConverter *Int32_To_Int8;
- PaUtilConverter *Int32_To_Int8_Dither;
- PaUtilConverter *Int32_To_UInt8;
- PaUtilConverter *Int32_To_UInt8_Dither;
-
- PaUtilConverter *Int24_To_Float32;
- PaUtilConverter *Int24_To_Int32;
- PaUtilConverter *Int24_To_Int16;
- PaUtilConverter *Int24_To_Int16_Dither;
- PaUtilConverter *Int24_To_Int8;
- PaUtilConverter *Int24_To_Int8_Dither;
- PaUtilConverter *Int24_To_UInt8;
- PaUtilConverter *Int24_To_UInt8_Dither;
-
- PaUtilConverter *Int16_To_Float32;
- PaUtilConverter *Int16_To_Int32;
- PaUtilConverter *Int16_To_Int24;
- PaUtilConverter *Int16_To_Int8;
- PaUtilConverter *Int16_To_Int8_Dither;
- PaUtilConverter *Int16_To_UInt8;
- PaUtilConverter *Int16_To_UInt8_Dither;
-
- PaUtilConverter *Int8_To_Float32;
- PaUtilConverter *Int8_To_Int32;
- PaUtilConverter *Int8_To_Int24;
- PaUtilConverter *Int8_To_Int16;
- PaUtilConverter *Int8_To_UInt8;
-
- PaUtilConverter *UInt8_To_Float32;
- PaUtilConverter *UInt8_To_Int32;
- PaUtilConverter *UInt8_To_Int24;
- PaUtilConverter *UInt8_To_Int16;
- PaUtilConverter *UInt8_To_Int8;
-
- PaUtilConverter *Copy_8_To_8; /* copy without any conversion */
- PaUtilConverter *Copy_16_To_16; /* copy without any conversion */
- PaUtilConverter *Copy_24_To_24; /* copy without any conversion */
- PaUtilConverter *Copy_32_To_32; /* copy without any conversion */
-} PaUtilConverterTable;
-
-
-/** A table of pointers to all required converter functions.
- PaUtil_SelectConverter() uses this table to lookup the appropriate
- conversion functions. The fields of this structure are initialized
- with default conversion functions. Fields may be NULL, indicating that
- no conversion function is available. User code may substitue optimised
- conversion functions by assigning different function pointers to
- these fields.
-
- @note
- If the PA_NO_STANDARD_CONVERTERS preprocessor variable is defined,
- PortAudio's standard converters will not be compiled, and all fields
- of this structure will be initialized to NULL. In such cases, users
- should supply their own conversion functions if the require PortAudio
- to open a stream that requires sample conversion.
-
- @see PaUtilConverterTable, PaUtilConverter, PaUtil_SelectConverter
-*/
-extern PaUtilConverterTable paConverters;
-
-
-/** The type used to store all buffer zeroing functions.
- @see paZeroers;
-*/
-typedef struct{
- PaUtilZeroer *ZeroU8; /* unsigned 8 bit, zero == 128 */
- PaUtilZeroer *Zero8;
- PaUtilZeroer *Zero16;
- PaUtilZeroer *Zero24;
- PaUtilZeroer *Zero32;
-} PaUtilZeroerTable;
-
-
-/** A table of pointers to all required zeroer functions.
- PaUtil_SelectZeroer() uses this table to lookup the appropriate
- conversion functions. The fields of this structure are initialized
- with default conversion functions. User code may substitue optimised
- conversion functions by assigning different function pointers to
- these fields.
-
- @note
- If the PA_NO_STANDARD_ZEROERS preprocessor variable is defined,
- PortAudio's standard zeroers will not be compiled, and all fields
- of this structure will be initialized to NULL. In such cases, users
- should supply their own zeroing functions for the sample sizes which
- they intend to use.
-
- @see PaUtilZeroerTable, PaUtilZeroer, PaUtil_SelectZeroer
-*/
-extern PaUtilZeroerTable paZeroers;
-
-#ifdef __cplusplus
-}
-#endif /* __cplusplus */
-#endif /* PA_CONVERTERS_H */
+#ifndef PA_CONVERTERS_H
+#define PA_CONVERTERS_H
+/*
+ * $Id: pa_converters.h,v 1.1.2.9 2003/09/20 21:05:14 rossbencina Exp $
+ * Portable Audio I/O Library sample conversion mechanism
+ *
+ * Based on the Open Source API proposed by Ross Bencina
+ * Copyright (c) 1999-2002 Phil Burk, Ross Bencina
+ *
+ * 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.
+ *
+ * Any person wishing to distribute modifications to the Software is
+ * requested to send the modifications to the original developer so that
+ * they can be incorporated into the canonical version.
+ *
+ * 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.
+ */
+
+/** @file
+ @brief Conversion functions used to convert buffers of samples from one
+ format to another.
+*/
+
+
+#include "portaudio.h" /* for PaSampleFormat */
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif /* __cplusplus */
+
+
+struct PaUtilTriangularDitherGenerator;
+
+
+/** Choose an available sample format which is most appropriate for
+ representing the requested format. If the requested format is not available
+ higher quality formats are considered before lower quality formates.
+ @param availableFormats A variable containing the logical OR of all available
+ formats.
+ @param format The desired format.
+ @return The most appropriate available format for representing the requested
+ format.
+*/
+PaSampleFormat PaUtil_SelectClosestAvailableFormat(
+ PaSampleFormat availableFormats, PaSampleFormat format );
+
+
+/* high level conversions functions for use by implementations */
+
+
+/** The generic sample converter prototype. Sample converters convert count
+ samples from sourceBuffer to destinationBuffer. The actual type of the data
+ pointed to by these parameters varys for different converter functions.
+ @param destinationBuffer A pointer to the first sample of the destination.
+ @param destinationStride An offset between successive destination samples
+ expressed in samples (not bytes.) It may be negative.
+ @param sourceBuffer A pointer to the first sample of the source.
+ @param sourceStride An offset between successive source samples
+ expressed in samples (not bytes.) It may be negative.
+ @param count The number of samples to convert.
+ @param ditherState State information used to calculate dither. Converters
+ that do not perform dithering will ignore this parameter, in which case
+ NULL or invalid dither state may be passed.
+*/
+typedef void PaUtilConverter(
+ void *destinationBuffer, signed int destinationStride,
+ void *sourceBuffer, signed int sourceStride,
+ unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator );
+
+
+/** Find a sample converter function for the given source and destinations
+ formats and flags (clip and dither.)
+ @return
+ A pointer to a PaUtilConverter which will perform the requested
+ conversion, or NULL if the given format conversion is not supported.
+ For conversions where clipping or dithering is not necessary, the
+ clip and dither flags are ignored and a non-clipping or dithering
+ version is returned.
+ If the source and destination formats are the same, a function which
+ copies data of the appropriate size will be returned.
+*/
+PaUtilConverter* PaUtil_SelectConverter( PaSampleFormat sourceFormat,
+ PaSampleFormat destinationFormat, PaStreamFlags flags );
+
+
+/** The generic buffer zeroer prototype. Buffer zeroers copy count zeros to
+ destinationBuffer. The actual type of the data pointed to varys for
+ different zeroer functions.
+ @param destinationBuffer A pointer to the first sample of the destination.
+ @param destinationStride An offset between successive destination samples
+ expressed in samples (not bytes.) It may be negative.
+ @param count The number of samples to zero.
+*/
+typedef void PaUtilZeroer(
+ void *destinationBuffer, signed int destinationStride, unsigned int count );
+
+
+/** Find a buffer zeroer function for the given destination format.
+ @return
+ A pointer to a PaUtilZeroer which will perform the requested
+ zeroing.
+*/
+PaUtilZeroer* PaUtil_SelectZeroer( PaSampleFormat destinationFormat );
+
+/*----------------------------------------------------------------------------*/
+/* low level functions and data structures which may be used for
+ substituting conversion functions */
+
+
+/** The type used to store all sample conversion functions.
+ @see paConverters;
+*/
+typedef struct{
+ PaUtilConverter *Float32_To_Int32;
+ PaUtilConverter *Float32_To_Int32_Dither;
+ PaUtilConverter *Float32_To_Int32_Clip;
+ PaUtilConverter *Float32_To_Int32_DitherClip;
+
+ PaUtilConverter *Float32_To_Int24;
+ PaUtilConverter *Float32_To_Int24_Dither;
+ PaUtilConverter *Float32_To_Int24_Clip;
+ PaUtilConverter *Float32_To_Int24_DitherClip;
+
+ PaUtilConverter *Float32_To_Int16;
+ PaUtilConverter *Float32_To_Int16_Dither;
+ PaUtilConverter *Float32_To_Int16_Clip;
+ PaUtilConverter *Float32_To_Int16_DitherClip;
+
+ PaUtilConverter *Float32_To_Int8;
+ PaUtilConverter *Float32_To_Int8_Dither;
+ PaUtilConverter *Float32_To_Int8_Clip;
+ PaUtilConverter *Float32_To_Int8_DitherClip;
+
+ PaUtilConverter *Float32_To_UInt8;
+ PaUtilConverter *Float32_To_UInt8_Dither;
+ PaUtilConverter *Float32_To_UInt8_Clip;
+ PaUtilConverter *Float32_To_UInt8_DitherClip;
+
+ PaUtilConverter *Int32_To_Float32;
+ PaUtilConverter *Int32_To_Int24;
+ PaUtilConverter *Int32_To_Int24_Dither;
+ PaUtilConverter *Int32_To_Int16;
+ PaUtilConverter *Int32_To_Int16_Dither;
+ PaUtilConverter *Int32_To_Int8;
+ PaUtilConverter *Int32_To_Int8_Dither;
+ PaUtilConverter *Int32_To_UInt8;
+ PaUtilConverter *Int32_To_UInt8_Dither;
+
+ PaUtilConverter *Int24_To_Float32;
+ PaUtilConverter *Int24_To_Int32;
+ PaUtilConverter *Int24_To_Int16;
+ PaUtilConverter *Int24_To_Int16_Dither;
+ PaUtilConverter *Int24_To_Int8;
+ PaUtilConverter *Int24_To_Int8_Dither;
+ PaUtilConverter *Int24_To_UInt8;
+ PaUtilConverter *Int24_To_UInt8_Dither;
+
+ PaUtilConverter *Int16_To_Float32;
+ PaUtilConverter *Int16_To_Int32;
+ PaUtilConverter *Int16_To_Int24;
+ PaUtilConverter *Int16_To_Int8;
+ PaUtilConverter *Int16_To_Int8_Dither;
+ PaUtilConverter *Int16_To_UInt8;
+ PaUtilConverter *Int16_To_UInt8_Dither;
+
+ PaUtilConverter *Int8_To_Float32;
+ PaUtilConverter *Int8_To_Int32;
+ PaUtilConverter *Int8_To_Int24;
+ PaUtilConverter *Int8_To_Int16;
+ PaUtilConverter *Int8_To_UInt8;
+
+ PaUtilConverter *UInt8_To_Float32;
+ PaUtilConverter *UInt8_To_Int32;
+ PaUtilConverter *UInt8_To_Int24;
+ PaUtilConverter *UInt8_To_Int16;
+ PaUtilConverter *UInt8_To_Int8;
+
+ PaUtilConverter *Copy_8_To_8; /* copy without any conversion */
+ PaUtilConverter *Copy_16_To_16; /* copy without any conversion */
+ PaUtilConverter *Copy_24_To_24; /* copy without any conversion */
+ PaUtilConverter *Copy_32_To_32; /* copy without any conversion */
+} PaUtilConverterTable;
+
+
+/** A table of pointers to all required converter functions.
+ PaUtil_SelectConverter() uses this table to lookup the appropriate
+ conversion functions. The fields of this structure are initialized
+ with default conversion functions. Fields may be NULL, indicating that
+ no conversion function is available. User code may substitue optimised
+ conversion functions by assigning different function pointers to
+ these fields.
+
+ @note
+ If the PA_NO_STANDARD_CONVERTERS preprocessor variable is defined,
+ PortAudio's standard converters will not be compiled, and all fields
+ of this structure will be initialized to NULL. In such cases, users
+ should supply their own conversion functions if the require PortAudio
+ to open a stream that requires sample conversion.
+
+ @see PaUtilConverterTable, PaUtilConverter, PaUtil_SelectConverter
+*/
+extern PaUtilConverterTable paConverters;
+
+
+/** The type used to store all buffer zeroing functions.
+ @see paZeroers;
+*/
+typedef struct{
+ PaUtilZeroer *ZeroU8; /* unsigned 8 bit, zero == 128 */
+ PaUtilZeroer *Zero8;
+ PaUtilZeroer *Zero16;
+ PaUtilZeroer *Zero24;
+ PaUtilZeroer *Zero32;
+} PaUtilZeroerTable;
+
+
+/** A table of pointers to all required zeroer functions.
+ PaUtil_SelectZeroer() uses this table to lookup the appropriate
+ conversion functions. The fields of this structure are initialized
+ with default conversion functions. User code may substitue optimised
+ conversion functions by assigning different function pointers to
+ these fields.
+
+ @note
+ If the PA_NO_STANDARD_ZEROERS preprocessor variable is defined,
+ PortAudio's standard zeroers will not be compiled, and all fields
+ of this structure will be initialized to NULL. In such cases, users
+ should supply their own zeroing functions for the sample sizes which
+ they intend to use.
+
+ @see PaUtilZeroerTable, PaUtilZeroer, PaUtil_SelectZeroer
+*/
+extern PaUtilZeroerTable paZeroers;
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+#endif /* PA_CONVERTERS_H */
diff --git a/pjmedia/src/pjmedia/portaudio/pa_cpuload.c b/pjmedia/src/pjmedia/portaudio/pa_cpuload.c
index f12ecbc7..e70fbf4e 100644
--- a/pjmedia/src/pjmedia/portaudio/pa_cpuload.c
+++ b/pjmedia/src/pjmedia/portaudio/pa_cpuload.c
@@ -1,96 +1,96 @@
-/*
- * $Id: pa_cpuload.c,v 1.1.2.14 2004/01/08 22:01:12 rossbencina Exp $
- * Portable Audio I/O Library CPU Load measurement functions
- * Portable CPU load measurement facility.
- *
- * Based on the Open Source API proposed by Ross Bencina
- * Copyright (c) 2002 Ross Bencina
- *
- * 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.
- *
- * Any person wishing to distribute modifications to the Software is
- * requested to send the modifications to the original developer so that
- * they can be incorporated into the canonical version.
- *
- * 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.
- */
-
-/** @file
- @brief Functions to assist in measuring the CPU utilization of a callback
- stream. Used to implement the Pa_GetStreamCpuLoad() function.
-
- @todo Dynamically calculate the coefficients used to smooth the CPU Load
- Measurements over time to provide a uniform characterisation of CPU Load
- independent of rate at which PaUtil_BeginCpuLoadMeasurement /
- PaUtil_EndCpuLoadMeasurement are called.
-*/
-
-
-#include "pa_cpuload.h"
-
-#include <assert.h>
-
-#include "pa_util.h" /* for PaUtil_GetTime() */
-
-
-void PaUtil_InitializeCpuLoadMeasurer( PaUtilCpuLoadMeasurer* measurer, double sampleRate )
-{
- assert( sampleRate > 0 );
-
- measurer->samplingPeriod = 1. / sampleRate;
- measurer->averageLoad = 0.;
-}
-
-void PaUtil_ResetCpuLoadMeasurer( PaUtilCpuLoadMeasurer* measurer )
-{
- measurer->averageLoad = 0.;
-}
-
-void PaUtil_BeginCpuLoadMeasurement( PaUtilCpuLoadMeasurer* measurer )
-{
- measurer->measurementStartTime = PaUtil_GetTime();
-}
-
-
-void PaUtil_EndCpuLoadMeasurement( PaUtilCpuLoadMeasurer* measurer, unsigned long framesProcessed )
-{
- double measurementEndTime, secondsFor100Percent, measuredLoad;
-
- if( framesProcessed > 0 ){
- measurementEndTime = PaUtil_GetTime();
-
- assert( framesProcessed > 0 );
- secondsFor100Percent = framesProcessed * measurer->samplingPeriod;
-
- measuredLoad = (measurementEndTime - measurer->measurementStartTime) / secondsFor100Percent;
-
- /* Low pass filter the calculated CPU load to reduce jitter using a simple IIR low pass filter. */
- /** FIXME @todo these coefficients shouldn't be hardwired */
-#define LOWPASS_COEFFICIENT_0 (0.9)
-#define LOWPASS_COEFFICIENT_1 (0.99999 - LOWPASS_COEFFICIENT_0)
-
- measurer->averageLoad = (LOWPASS_COEFFICIENT_0 * measurer->averageLoad) +
- (LOWPASS_COEFFICIENT_1 * measuredLoad);
- }
-}
-
-
-double PaUtil_GetCpuLoad( PaUtilCpuLoadMeasurer* measurer )
-{
- return measurer->averageLoad;
-}
+/*
+ * $Id: pa_cpuload.c,v 1.1.2.14 2004/01/08 22:01:12 rossbencina Exp $
+ * Portable Audio I/O Library CPU Load measurement functions
+ * Portable CPU load measurement facility.
+ *
+ * Based on the Open Source API proposed by Ross Bencina
+ * Copyright (c) 2002 Ross Bencina
+ *
+ * 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.
+ *
+ * Any person wishing to distribute modifications to the Software is
+ * requested to send the modifications to the original developer so that
+ * they can be incorporated into the canonical version.
+ *
+ * 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.
+ */
+
+/** @file
+ @brief Functions to assist in measuring the CPU utilization of a callback
+ stream. Used to implement the Pa_GetStreamCpuLoad() function.
+
+ @todo Dynamically calculate the coefficients used to smooth the CPU Load
+ Measurements over time to provide a uniform characterisation of CPU Load
+ independent of rate at which PaUtil_BeginCpuLoadMeasurement /
+ PaUtil_EndCpuLoadMeasurement are called.
+*/
+
+
+#include "pa_cpuload.h"
+
+#include <assert.h>
+
+#include "pa_util.h" /* for PaUtil_GetTime() */
+
+
+void PaUtil_InitializeCpuLoadMeasurer( PaUtilCpuLoadMeasurer* measurer, double sampleRate )
+{
+ assert( sampleRate > 0 );
+
+ measurer->samplingPeriod = 1. / sampleRate;
+ measurer->averageLoad = 0.;
+}
+
+void PaUtil_ResetCpuLoadMeasurer( PaUtilCpuLoadMeasurer* measurer )
+{
+ measurer->averageLoad = 0.;
+}
+
+void PaUtil_BeginCpuLoadMeasurement( PaUtilCpuLoadMeasurer* measurer )
+{
+ measurer->measurementStartTime = PaUtil_GetTime();
+}
+
+
+void PaUtil_EndCpuLoadMeasurement( PaUtilCpuLoadMeasurer* measurer, unsigned long framesProcessed )
+{
+ double measurementEndTime, secondsFor100Percent, measuredLoad;
+
+ if( framesProcessed > 0 ){
+ measurementEndTime = PaUtil_GetTime();
+
+ assert( framesProcessed > 0 );
+ secondsFor100Percent = framesProcessed * measurer->samplingPeriod;
+
+ measuredLoad = (measurementEndTime - measurer->measurementStartTime) / secondsFor100Percent;
+
+ /* Low pass filter the calculated CPU load to reduce jitter using a simple IIR low pass filter. */
+ /** FIXME @todo these coefficients shouldn't be hardwired */
+#define LOWPASS_COEFFICIENT_0 (0.9)
+#define LOWPASS_COEFFICIENT_1 (0.99999 - LOWPASS_COEFFICIENT_0)
+
+ measurer->averageLoad = (LOWPASS_COEFFICIENT_0 * measurer->averageLoad) +
+ (LOWPASS_COEFFICIENT_1 * measuredLoad);
+ }
+}
+
+
+double PaUtil_GetCpuLoad( PaUtilCpuLoadMeasurer* measurer )
+{
+ return measurer->averageLoad;
+}
diff --git a/pjmedia/src/pjmedia/portaudio/pa_cpuload.h b/pjmedia/src/pjmedia/portaudio/pa_cpuload.h
index 61420e46..f77d9199 100644
--- a/pjmedia/src/pjmedia/portaudio/pa_cpuload.h
+++ b/pjmedia/src/pjmedia/portaudio/pa_cpuload.h
@@ -1,63 +1,63 @@
-#ifndef PA_CPULOAD_H
-#define PA_CPULOAD_H
-/*
- * $Id: pa_cpuload.h,v 1.1.2.10 2004/01/08 22:01:12 rossbencina Exp $
- * Portable Audio I/O Library CPU Load measurement functions
- * Portable CPU load measurement facility.
- *
- * Based on the Open Source API proposed by Ross Bencina
- * Copyright (c) 2002 Ross Bencina
- *
- * 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.
- *
- * Any person wishing to distribute modifications to the Software is
- * requested to send the modifications to the original developer so that
- * they can be incorporated into the canonical version.
- *
- * 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.
- */
-
-/** @file
- @brief Functions to assist in measuring the CPU utilization of a callback
- stream. Used to implement the Pa_GetStreamCpuLoad() function.
-*/
-
-
-#ifdef __cplusplus
-extern "C"
-{
-#endif /* __cplusplus */
-
-
-typedef struct {
- double samplingPeriod;
- double measurementStartTime;
- double averageLoad;
-} PaUtilCpuLoadMeasurer; /**< @todo need better name than measurer */
-
-void PaUtil_InitializeCpuLoadMeasurer( PaUtilCpuLoadMeasurer* measurer, double sampleRate );
-void PaUtil_BeginCpuLoadMeasurement( PaUtilCpuLoadMeasurer* measurer );
-void PaUtil_EndCpuLoadMeasurement( PaUtilCpuLoadMeasurer* measurer, unsigned long framesProcessed );
-void PaUtil_ResetCpuLoadMeasurer( PaUtilCpuLoadMeasurer* measurer );
-double PaUtil_GetCpuLoad( PaUtilCpuLoadMeasurer* measurer );
-
-
-#ifdef __cplusplus
-}
-#endif /* __cplusplus */
-#endif /* PA_CPULOAD_H */
+#ifndef PA_CPULOAD_H
+#define PA_CPULOAD_H
+/*
+ * $Id: pa_cpuload.h,v 1.1.2.10 2004/01/08 22:01:12 rossbencina Exp $
+ * Portable Audio I/O Library CPU Load measurement functions
+ * Portable CPU load measurement facility.
+ *
+ * Based on the Open Source API proposed by Ross Bencina
+ * Copyright (c) 2002 Ross Bencina
+ *
+ * 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.
+ *
+ * Any person wishing to distribute modifications to the Software is
+ * requested to send the modifications to the original developer so that
+ * they can be incorporated into the canonical version.
+ *
+ * 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.
+ */
+
+/** @file
+ @brief Functions to assist in measuring the CPU utilization of a callback
+ stream. Used to implement the Pa_GetStreamCpuLoad() function.
+*/
+
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif /* __cplusplus */
+
+
+typedef struct {
+ double samplingPeriod;
+ double measurementStartTime;
+ double averageLoad;
+} PaUtilCpuLoadMeasurer; /**< @todo need better name than measurer */
+
+void PaUtil_InitializeCpuLoadMeasurer( PaUtilCpuLoadMeasurer* measurer, double sampleRate );
+void PaUtil_BeginCpuLoadMeasurement( PaUtilCpuLoadMeasurer* measurer );
+void PaUtil_EndCpuLoadMeasurement( PaUtilCpuLoadMeasurer* measurer, unsigned long framesProcessed );
+void PaUtil_ResetCpuLoadMeasurer( PaUtilCpuLoadMeasurer* measurer );
+double PaUtil_GetCpuLoad( PaUtilCpuLoadMeasurer* measurer );
+
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+#endif /* PA_CPULOAD_H */
diff --git a/pjmedia/src/pjmedia/portaudio/pa_dither.c b/pjmedia/src/pjmedia/portaudio/pa_dither.c
index 5181a8b9..0600db62 100644
--- a/pjmedia/src/pjmedia/portaudio/pa_dither.c
+++ b/pjmedia/src/pjmedia/portaudio/pa_dither.c
@@ -1,204 +1,204 @@
-/*
- * $Id: pa_dither.c,v 1.1.2.6 2005/05/28 22:49:02 rossbencina Exp $
- * Portable Audio I/O Library triangular dither generator
- *
- * Based on the Open Source API proposed by Ross Bencina
- * Copyright (c) 1999-2002 Phil Burk, Ross Bencina
- *
- * 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.
- *
- * Any person wishing to distribute modifications to the Software is
- * requested to send the modifications to the original developer so that
- * they can be incorporated into the canonical version.
- *
- * 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.
- */
-
-/** @file
- @brief Functions for generating dither noise
-*/
-
-
-#include "pa_dither.h"
-#include "pa_types.h"
-
-#define PA_DITHER_BITS_ (15)
-
-
-void PaUtil_InitializeTriangularDitherState( PaUtilTriangularDitherGenerator *state )
-{
- state->previous = 0;
- state->randSeed1 = 22222;
- state->randSeed2 = 5555555;
-}
-
-
-signed long PaUtil_Generate16BitTriangularDither( PaUtilTriangularDitherGenerator *state )
-{
- signed long current, highPass;
-
- /* Generate two random numbers. */
- state->randSeed1 = (state->randSeed1 * 196314165) + 907633515;
- state->randSeed2 = (state->randSeed2 * 196314165) + 907633515;
-
- /* Generate triangular distribution about 0.
- * Shift before adding to prevent overflow which would skew the distribution.
- * Also shift an extra bit for the high pass filter.
- */
-#define DITHER_SHIFT_ ((SIZEOF_LONG*8 - PA_DITHER_BITS_) + 1)
- current = (((signed long)state->randSeed1)>>DITHER_SHIFT_) +
- (((signed long)state->randSeed2)>>DITHER_SHIFT_);
-
- /* High pass filter to reduce audibility. */
- highPass = current - state->previous;
- state->previous = current;
- return highPass;
-}
-
-
-/* Multiply by PA_FLOAT_DITHER_SCALE_ to get a float between -2.0 and +1.99999 */
-#define PA_FLOAT_DITHER_SCALE_ (1.0f / ((1<<PA_DITHER_BITS_)-1))
-static const float const_float_dither_scale_ = PA_FLOAT_DITHER_SCALE_;
-
-float PaUtil_GenerateFloatTriangularDither( PaUtilTriangularDitherGenerator *state )
-{
- signed long current, highPass;
-
- /* Generate two random numbers. */
- state->randSeed1 = (state->randSeed1 * 196314165) + 907633515;
- state->randSeed2 = (state->randSeed2 * 196314165) + 907633515;
-
- /* Generate triangular distribution about 0.
- * Shift before adding to prevent overflow which would skew the distribution.
- * Also shift an extra bit for the high pass filter.
- */
-#define DITHER_SHIFT_ ((SIZEOF_LONG*8 - PA_DITHER_BITS_) + 1)
- current = (((signed long)state->randSeed1)>>DITHER_SHIFT_) +
- (((signed long)state->randSeed2)>>DITHER_SHIFT_);
-
- /* High pass filter to reduce audibility. */
- highPass = current - state->previous;
- state->previous = current;
- return ((float)highPass) * const_float_dither_scale_;
-}
-
-
-/*
-The following alternate dither algorithms (from musicdsp.org) could be
-considered
-*/
-
-/*Noise shaped dither (March 2000)
--------------------
-
-This is a simple implementation of highpass triangular-PDF dither with
-2nd-order noise shaping, for use when truncating floating point audio
-data to fixed point.
-
-The noise shaping lowers the noise floor by 11dB below 5kHz (@ 44100Hz
-sample rate) compared to triangular-PDF dither. The code below assumes
-input data is in the range +1 to -1 and doesn't check for overloads!
-
-To save time when generating dither for multiple channels you can do
-things like this: r3=(r1 & 0x7F)<<8; instead of calling rand() again.
-
-
-
- int r1, r2; //rectangular-PDF random numbers
- float s1, s2; //error feedback buffers
- float s = 0.5f; //set to 0.0f for no noise shaping
- float w = pow(2.0,bits-1); //word length (usually bits=16)
- float wi= 1.0f/w;
- float d = wi / RAND_MAX; //dither amplitude (2 lsb)
- float o = wi * 0.5f; //remove dc offset
- float in, tmp;
- int out;
-
-
-//for each sample...
-
- r2=r1; //can make HP-TRI dither by
- r1=rand(); //subtracting previous rand()
-
- in += s * (s1 + s1 - s2); //error feedback
- tmp = in + o + d * (float)(r1 - r2); //dc offset and dither
-
- out = (int)(w * tmp); //truncate downwards
- if(tmp<0.0f) out--; //this is faster than floor()
-
- s2 = s1;
- s1 = in - wi * (float)out; //error
-
-
-
---
-paul.kellett@maxim.abel.co.uk
-http://www.maxim.abel.co.uk
-*/
-
-
-/*
-16-to-8-bit first-order dither
-
-Type : First order error feedforward dithering code
-References : Posted by Jon Watte
-
-Notes :
-This is about as simple a dithering algorithm as you can implement, but it's
-likely to sound better than just truncating to N bits.
-
-Note that you might not want to carry forward the full difference for infinity.
-It's probably likely that the worst performance hit comes from the saturation
-conditionals, which can be avoided with appropriate instructions on many DSPs
-and integer SIMD type instructions, or CMOV.
-
-Last, if sound quality is paramount (such as when going from > 16 bits to 16
-bits) you probably want to use a higher-order dither function found elsewhere
-on this site.
-
-
-Code :
-// This code will down-convert and dither a 16-bit signed short
-// mono signal into an 8-bit unsigned char signal, using a first
-// order forward-feeding error term dither.
-
-#define uchar unsigned char
-
-void dither_one_channel_16_to_8( short * input, uchar * output, int count, int * memory )
-{
- int m = *memory;
- while( count-- > 0 ) {
- int i = *input++;
- i += m;
- int j = i + 32768 - 128;
- uchar o;
- if( j < 0 ) {
- o = 0;
- }
- else if( j > 65535 ) {
- o = 255;
- }
- else {
- o = (uchar)((j>>8)&0xff);
- }
- m = ((j-32768+128)-i);
- *output++ = o;
- }
- *memory = m;
-}
-*/
+/*
+ * $Id: pa_dither.c,v 1.1.2.6 2005/05/28 22:49:02 rossbencina Exp $
+ * Portable Audio I/O Library triangular dither generator
+ *
+ * Based on the Open Source API proposed by Ross Bencina
+ * Copyright (c) 1999-2002 Phil Burk, Ross Bencina
+ *
+ * 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.
+ *
+ * Any person wishing to distribute modifications to the Software is
+ * requested to send the modifications to the original developer so that
+ * they can be incorporated into the canonical version.
+ *
+ * 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.
+ */
+
+/** @file
+ @brief Functions for generating dither noise
+*/
+
+
+#include "pa_dither.h"
+#include "pa_types.h"
+
+#define PA_DITHER_BITS_ (15)
+
+
+void PaUtil_InitializeTriangularDitherState( PaUtilTriangularDitherGenerator *state )
+{
+ state->previous = 0;
+ state->randSeed1 = 22222;
+ state->randSeed2 = 5555555;
+}
+
+
+signed long PaUtil_Generate16BitTriangularDither( PaUtilTriangularDitherGenerator *state )
+{
+ signed long current, highPass;
+
+ /* Generate two random numbers. */
+ state->randSeed1 = (state->randSeed1 * 196314165) + 907633515;
+ state->randSeed2 = (state->randSeed2 * 196314165) + 907633515;
+
+ /* Generate triangular distribution about 0.
+ * Shift before adding to prevent overflow which would skew the distribution.
+ * Also shift an extra bit for the high pass filter.
+ */
+#define DITHER_SHIFT_ ((SIZEOF_LONG*8 - PA_DITHER_BITS_) + 1)
+ current = (((signed long)state->randSeed1)>>DITHER_SHIFT_) +
+ (((signed long)state->randSeed2)>>DITHER_SHIFT_);
+
+ /* High pass filter to reduce audibility. */
+ highPass = current - state->previous;
+ state->previous = current;
+ return highPass;
+}
+
+
+/* Multiply by PA_FLOAT_DITHER_SCALE_ to get a float between -2.0 and +1.99999 */
+#define PA_FLOAT_DITHER_SCALE_ (1.0f / ((1<<PA_DITHER_BITS_)-1))
+static const float const_float_dither_scale_ = PA_FLOAT_DITHER_SCALE_;
+
+float PaUtil_GenerateFloatTriangularDither( PaUtilTriangularDitherGenerator *state )
+{
+ signed long current, highPass;
+
+ /* Generate two random numbers. */
+ state->randSeed1 = (state->randSeed1 * 196314165) + 907633515;
+ state->randSeed2 = (state->randSeed2 * 196314165) + 907633515;
+
+ /* Generate triangular distribution about 0.
+ * Shift before adding to prevent overflow which would skew the distribution.
+ * Also shift an extra bit for the high pass filter.
+ */
+#define DITHER_SHIFT_ ((SIZEOF_LONG*8 - PA_DITHER_BITS_) + 1)
+ current = (((signed long)state->randSeed1)>>DITHER_SHIFT_) +
+ (((signed long)state->randSeed2)>>DITHER_SHIFT_);
+
+ /* High pass filter to reduce audibility. */
+ highPass = current - state->previous;
+ state->previous = current;
+ return ((float)highPass) * const_float_dither_scale_;
+}
+
+
+/*
+The following alternate dither algorithms (from musicdsp.org) could be
+considered
+*/
+
+/*Noise shaped dither (March 2000)
+-------------------
+
+This is a simple implementation of highpass triangular-PDF dither with
+2nd-order noise shaping, for use when truncating floating point audio
+data to fixed point.
+
+The noise shaping lowers the noise floor by 11dB below 5kHz (@ 44100Hz
+sample rate) compared to triangular-PDF dither. The code below assumes
+input data is in the range +1 to -1 and doesn't check for overloads!
+
+To save time when generating dither for multiple channels you can do
+things like this: r3=(r1 & 0x7F)<<8; instead of calling rand() again.
+
+
+
+ int r1, r2; //rectangular-PDF random numbers
+ float s1, s2; //error feedback buffers
+ float s = 0.5f; //set to 0.0f for no noise shaping
+ float w = pow(2.0,bits-1); //word length (usually bits=16)
+ float wi= 1.0f/w;
+ float d = wi / RAND_MAX; //dither amplitude (2 lsb)
+ float o = wi * 0.5f; //remove dc offset
+ float in, tmp;
+ int out;
+
+
+//for each sample...
+
+ r2=r1; //can make HP-TRI dither by
+ r1=rand(); //subtracting previous rand()
+
+ in += s * (s1 + s1 - s2); //error feedback
+ tmp = in + o + d * (float)(r1 - r2); //dc offset and dither
+
+ out = (int)(w * tmp); //truncate downwards
+ if(tmp<0.0f) out--; //this is faster than floor()
+
+ s2 = s1;
+ s1 = in - wi * (float)out; //error
+
+
+
+--
+paul.kellett@maxim.abel.co.uk
+http://www.maxim.abel.co.uk
+*/
+
+
+/*
+16-to-8-bit first-order dither
+
+Type : First order error feedforward dithering code
+References : Posted by Jon Watte
+
+Notes :
+This is about as simple a dithering algorithm as you can implement, but it's
+likely to sound better than just truncating to N bits.
+
+Note that you might not want to carry forward the full difference for infinity.
+It's probably likely that the worst performance hit comes from the saturation
+conditionals, which can be avoided with appropriate instructions on many DSPs
+and integer SIMD type instructions, or CMOV.
+
+Last, if sound quality is paramount (such as when going from > 16 bits to 16
+bits) you probably want to use a higher-order dither function found elsewhere
+on this site.
+
+
+Code :
+// This code will down-convert and dither a 16-bit signed short
+// mono signal into an 8-bit unsigned char signal, using a first
+// order forward-feeding error term dither.
+
+#define uchar unsigned char
+
+void dither_one_channel_16_to_8( short * input, uchar * output, int count, int * memory )
+{
+ int m = *memory;
+ while( count-- > 0 ) {
+ int i = *input++;
+ i += m;
+ int j = i + 32768 - 128;
+ uchar o;
+ if( j < 0 ) {
+ o = 0;
+ }
+ else if( j > 65535 ) {
+ o = 255;
+ }
+ else {
+ o = (uchar)((j>>8)&0xff);
+ }
+ m = ((j-32768+128)-i);
+ *output++ = o;
+ }
+ *memory = m;
+}
+*/
diff --git a/pjmedia/src/pjmedia/portaudio/pa_dither.h b/pjmedia/src/pjmedia/portaudio/pa_dither.h
index a76c8b6b..70369e18 100644
--- a/pjmedia/src/pjmedia/portaudio/pa_dither.h
+++ b/pjmedia/src/pjmedia/portaudio/pa_dither.h
@@ -1,91 +1,91 @@
-#ifndef PA_DITHER_H
-#define PA_DITHER_H
-/*
- * $Id: pa_dither.h,v 1.1.2.4 2003/09/20 21:06:19 rossbencina Exp $
- * Portable Audio I/O Library triangular dither generator
- *
- * Based on the Open Source API proposed by Ross Bencina
- * Copyright (c) 1999-2002 Phil Burk, Ross Bencina
- *
- * 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.
- *
- * Any person wishing to distribute modifications to the Software is
- * requested to send the modifications to the original developer so that
- * they can be incorporated into the canonical version.
- *
- * 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.
- */
-
-/** @file
- @brief Functions for generating dither noise
-*/
-
-
-#ifdef __cplusplus
-extern "C"
-{
-#endif /* __cplusplus */
-
-
-/** @brief State needed to generate a dither signal */
-typedef struct PaUtilTriangularDitherGenerator{
- unsigned long previous;
- unsigned long randSeed1;
- unsigned long randSeed2;
-} PaUtilTriangularDitherGenerator;
-
-
-/** @brief Initialize dither state */
-void PaUtil_InitializeTriangularDitherState( PaUtilTriangularDitherGenerator *ditherState );
-
-
-/**
- @brief Calculate 2 LSB dither signal with a triangular distribution.
- Ranged for adding to a 1 bit right-shifted 32 bit integer
- prior to >>15. eg:
-<pre>
- signed long in = *
- signed long dither = PaUtil_Generate16BitTriangularDither( ditherState );
- signed short out = (signed short)(((in>>1) + dither) >> 15);
-</pre>
- @return
- A signed long with a range of +32767 to -32768
-*/
-signed long PaUtil_Generate16BitTriangularDither( PaUtilTriangularDitherGenerator *ditherState );
-
-
-/**
- @brief Calculate 2 LSB dither signal with a triangular distribution.
- Ranged for adding to a pre-scaled float.
-<pre>
- float in = *
- float dither = PaUtil_GenerateFloatTriangularDither( ditherState );
- // use smaller scaler to prevent overflow when we add the dither
- signed short out = (signed short)(in*(32766.0f) + dither );
-</pre>
- @return
- A float with a range of -2.0 to +1.99999.
-*/
-float PaUtil_GenerateFloatTriangularDither( PaUtilTriangularDitherGenerator *ditherState );
-
-
-
-#ifdef __cplusplus
-}
-#endif /* __cplusplus */
-#endif /* PA_DITHER_H */
+#ifndef PA_DITHER_H
+#define PA_DITHER_H
+/*
+ * $Id: pa_dither.h,v 1.1.2.4 2003/09/20 21:06:19 rossbencina Exp $
+ * Portable Audio I/O Library triangular dither generator
+ *
+ * Based on the Open Source API proposed by Ross Bencina
+ * Copyright (c) 1999-2002 Phil Burk, Ross Bencina
+ *
+ * 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.
+ *
+ * Any person wishing to distribute modifications to the Software is
+ * requested to send the modifications to the original developer so that
+ * they can be incorporated into the canonical version.
+ *
+ * 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.
+ */
+
+/** @file
+ @brief Functions for generating dither noise
+*/
+
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif /* __cplusplus */
+
+
+/** @brief State needed to generate a dither signal */
+typedef struct PaUtilTriangularDitherGenerator{
+ unsigned long previous;
+ unsigned long randSeed1;
+ unsigned long randSeed2;
+} PaUtilTriangularDitherGenerator;
+
+
+/** @brief Initialize dither state */
+void PaUtil_InitializeTriangularDitherState( PaUtilTriangularDitherGenerator *ditherState );
+
+
+/**
+ @brief Calculate 2 LSB dither signal with a triangular distribution.
+ Ranged for adding to a 1 bit right-shifted 32 bit integer
+ prior to >>15. eg:
+<pre>
+ signed long in = *
+ signed long dither = PaUtil_Generate16BitTriangularDither( ditherState );
+ signed short out = (signed short)(((in>>1) + dither) >> 15);
+</pre>
+ @return
+ A signed long with a range of +32767 to -32768
+*/
+signed long PaUtil_Generate16BitTriangularDither( PaUtilTriangularDitherGenerator *ditherState );
+
+
+/**
+ @brief Calculate 2 LSB dither signal with a triangular distribution.
+ Ranged for adding to a pre-scaled float.
+<pre>
+ float in = *
+ float dither = PaUtil_GenerateFloatTriangularDither( ditherState );
+ // use smaller scaler to prevent overflow when we add the dither
+ signed short out = (signed short)(in*(32766.0f) + dither );
+</pre>
+ @return
+ A float with a range of -2.0 to +1.99999.
+*/
+float PaUtil_GenerateFloatTriangularDither( PaUtilTriangularDitherGenerator *ditherState );
+
+
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+#endif /* PA_DITHER_H */
diff --git a/pjmedia/src/pjmedia/portaudio/pa_endianness.h b/pjmedia/src/pjmedia/portaudio/pa_endianness.h
index 6faf6ec2..052bced6 100644
--- a/pjmedia/src/pjmedia/portaudio/pa_endianness.h
+++ b/pjmedia/src/pjmedia/portaudio/pa_endianness.h
@@ -1,111 +1,111 @@
-#ifndef PA_ENDIANNESS_H
-#define PA_ENDIANNESS_H
-/*
- * $Id: pa_endianness.h,v 1.1.2.3 2003/09/20 21:06:19 rossbencina Exp $
- * Portable Audio I/O Library current platform endianness macros
- *
- * Based on the Open Source API proposed by Ross Bencina
- * Copyright (c) 1999-2002 Phil Burk, Ross Bencina
- *
- * 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.
- *
- * Any person wishing to distribute modifications to the Software is
- * requested to send the modifications to the original developer so that
- * they can be incorporated into the canonical version.
- *
- * 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.
- */
-
-/** @file
- @brief Configure endianness symbols for the target processor.
-
- Arrange for either the PA_LITTLE_ENDIAN or PA_BIG_ENDIAN preprocessor symbols
- to be defined. The one that is defined reflects the endianness of the target
- platform and may be used to implement conditional compilation of byte-order
- dependent code.
-
- If either PA_LITTLE_ENDIAN or PA_BIG_ENDIAN is defined already, then no attempt
- is made to override that setting. This may be useful if you have a better way
- of determining the platform's endianness. The autoconf mechanism uses this for
- example.
-
- A PA_VALIDATE_ENDIANNESS macro is provided to compare the compile time
- and runtime endiannes and raise an assertion if they don't match.
-*/
-
-
-#ifdef __cplusplus
-extern "C"
-{
-#endif /* __cplusplus */
-
-
-#if defined(PA_LITTLE_ENDIAN) || defined(PA_BIG_ENDIAN)
- /* endianness define has been set externally, such as by autoconf */
-
- #if defined(PA_LITTLE_ENDIAN) && defined(PA_BIG_ENDIAN)
- #error both PA_LITTLE_ENDIAN and PA_BIG_ENDIAN have been defined externally to pa_endianness.h - only one endianness at a time please
- #endif
-
-#else
- /* endianness define has not been set externally */
-
- /* set PA_LITTLE_ENDIAN or PA_BIG_ENDIAN by testing well known platform specific defines */
-
- #if defined(WIN32) || defined(_WIN32) || defined(__WIN32__) || defined(__NT__)
-
- #define PA_LITTLE_ENDIAN /* win32, assume intel byte order */
-
- #else
-
-#endif
-
- #if !defined(PA_LITTLE_ENDIAN) && !defined(PA_BIG_ENDIAN)
- /*
- If the following error is raised, you either need to modify the code above
- to automatically determine the endianness from other symbols defined on your
- platform, or define either PA_LITTLE_ENDIAN or PA_BIG_ENDIAN externally.
- */
- #error pa_endianness.h was unable to automatically determine the endianness of the target platform
- #endif
-
-#endif
-
-/* PA_VALIDATE_ENDIANNESS compares the compile time and runtime endianness,
- and raises an assertion if they don't match. <assert.h> must be included in
- the context in which this macro is used.
-*/
-#if defined(PA_LITTLE_ENDIAN)
- #define PA_VALIDATE_ENDIANNESS \
- { \
- const long nativeOne = 1; \
- assert( "PortAudio: compile time and runtime endianness don't match" && (((char *)&nativeOne)[0]) == 1 ); \
- }
-#elif defined(PA_BIG_ENDIAN)
- #define PA_VALIDATE_ENDIANNESS \
- { \
- const long nativeOne = 1; \
- assert( "PortAudio: compile time and runtime endianness don't match" && (((char *)&nativeOne)[0]) == 0 ); \
- }
-#endif
-
-
-#ifdef __cplusplus
-}
-#endif /* __cplusplus */
-#endif /* PA_ENDIANNESS_H */
+#ifndef PA_ENDIANNESS_H
+#define PA_ENDIANNESS_H
+/*
+ * $Id: pa_endianness.h,v 1.1.2.3 2003/09/20 21:06:19 rossbencina Exp $
+ * Portable Audio I/O Library current platform endianness macros
+ *
+ * Based on the Open Source API proposed by Ross Bencina
+ * Copyright (c) 1999-2002 Phil Burk, Ross Bencina
+ *
+ * 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.
+ *
+ * Any person wishing to distribute modifications to the Software is
+ * requested to send the modifications to the original developer so that
+ * they can be incorporated into the canonical version.
+ *
+ * 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.
+ */
+
+/** @file
+ @brief Configure endianness symbols for the target processor.
+
+ Arrange for either the PA_LITTLE_ENDIAN or PA_BIG_ENDIAN preprocessor symbols
+ to be defined. The one that is defined reflects the endianness of the target
+ platform and may be used to implement conditional compilation of byte-order
+ dependent code.
+
+ If either PA_LITTLE_ENDIAN or PA_BIG_ENDIAN is defined already, then no attempt
+ is made to override that setting. This may be useful if you have a better way
+ of determining the platform's endianness. The autoconf mechanism uses this for
+ example.
+
+ A PA_VALIDATE_ENDIANNESS macro is provided to compare the compile time
+ and runtime endiannes and raise an assertion if they don't match.
+*/
+
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif /* __cplusplus */
+
+
+#if defined(PA_LITTLE_ENDIAN) || defined(PA_BIG_ENDIAN)
+ /* endianness define has been set externally, such as by autoconf */
+
+ #if defined(PA_LITTLE_ENDIAN) && defined(PA_BIG_ENDIAN)
+ #error both PA_LITTLE_ENDIAN and PA_BIG_ENDIAN have been defined externally to pa_endianness.h - only one endianness at a time please
+ #endif
+
+#else
+ /* endianness define has not been set externally */
+
+ /* set PA_LITTLE_ENDIAN or PA_BIG_ENDIAN by testing well known platform specific defines */
+
+ #if defined(WIN32) || defined(_WIN32) || defined(__WIN32__) || defined(__NT__)
+
+ #define PA_LITTLE_ENDIAN /* win32, assume intel byte order */
+
+ #else
+
+#endif
+
+ #if !defined(PA_LITTLE_ENDIAN) && !defined(PA_BIG_ENDIAN)
+ /*
+ If the following error is raised, you either need to modify the code above
+ to automatically determine the endianness from other symbols defined on your
+ platform, or define either PA_LITTLE_ENDIAN or PA_BIG_ENDIAN externally.
+ */
+ #error pa_endianness.h was unable to automatically determine the endianness of the target platform
+ #endif
+
+#endif
+
+/* PA_VALIDATE_ENDIANNESS compares the compile time and runtime endianness,
+ and raises an assertion if they don't match. <assert.h> must be included in
+ the context in which this macro is used.
+*/
+#if defined(PA_LITTLE_ENDIAN)
+ #define PA_VALIDATE_ENDIANNESS \
+ { \
+ const long nativeOne = 1; \
+ assert( "PortAudio: compile time and runtime endianness don't match" && (((char *)&nativeOne)[0]) == 1 ); \
+ }
+#elif defined(PA_BIG_ENDIAN)
+ #define PA_VALIDATE_ENDIANNESS \
+ { \
+ const long nativeOne = 1; \
+ assert( "PortAudio: compile time and runtime endianness don't match" && (((char *)&nativeOne)[0]) == 0 ); \
+ }
+#endif
+
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+#endif /* PA_ENDIANNESS_H */
diff --git a/pjmedia/src/pjmedia/portaudio/pa_front.c b/pjmedia/src/pjmedia/portaudio/pa_front.c
index 620610c3..3a267bbc 100644
--- a/pjmedia/src/pjmedia/portaudio/pa_front.c
+++ b/pjmedia/src/pjmedia/portaudio/pa_front.c
@@ -1,1976 +1,1976 @@
-/*
- * $Id: pa_front.c,v 1.1.2.51 2005/02/05 15:52:12 rossbencina Exp $
- * Portable Audio I/O Library Multi-Host API front end
- * Validate function parameters and manage multiple host APIs.
- *
- * Based on the Open Source API proposed by Ross Bencina
- * Copyright (c) 1999-2002 Ross Bencina, Phil Burk
- *
- * 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.
- *
- * Any person wishing to distribute modifications to the Software is
- * requested to send the modifications to the original developer so that
- * they can be incorporated into the canonical version.
- *
- * 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.
- */
-
-/* doxygen index page */
-/** @mainpage
-
-PortAudio is an open-source cross-platform †CË library for audio input
-and output. It is designed to simplify the porting of audio applications
-between various platforms, and also to simplify the development of audio
-software in general by hiding the complexities of device interfacing.
-
-See the PortAudio website for further information http://www.portaudio.com/
-
-This documentation pertains to PortAudio V19, API version 2.0 which is
-currently under development. API version 2.0 differs in a number of ways from
-previous versions, please consult the enhancement proposals for further details:
-http://www.portaudio.com/docs/proposals/index.html
-
-This documentation is under construction. Things you might be interested in
-include:
-
-- The PortAudio API 2.0, as documented in portaudio.h
-
-- The <a href="todo.html">TODO List</a>
-
-Feel free to pick an item off TODO list and fix/implement it. You may want to
-enquire about status on the PortAudio mailing list first.
-*/
-
-
-/** @file
- @brief Implements public PortAudio API, checks some errors, forwards to
- host API implementations.
-
- Implements the functions defined in the PortAudio API, checks for
- some parameter and state inconsistencies and forwards API requests to
- specific Host API implementations (via the interface declared in
- pa_hostapi.h), and Streams (via the interface declared in pa_stream.h).
-
- This file handles initialization and termination of Host API
- implementations via initializers stored in the paHostApiInitializers
- global variable.
-
- Some utility functions declared in pa_util.h are implemented in this file.
-
- All PortAudio API functions can be conditionally compiled with logging code.
- To compile with logging, define the PA_LOG_API_CALLS precompiler symbol.
-
- @todo Consider adding host API specific error text in Pa_GetErrorText() for
- paUnanticipatedHostError
-
- @todo Consider adding a new error code for when (inputParameters == NULL)
- && (outputParameters == NULL)
-
- @todo review whether Pa_CloseStream() should call the interface's
- CloseStream function if aborting the stream returns an error code.
-
- @todo Create new error codes if a NULL buffer pointer, or a
- zero frame count is passed to Pa_ReadStream or Pa_WriteStream.
-*/
-
-
-#include <stdio.h>
-#include <stdarg.h>
-#include <memory.h>
-#include <string.h>
-#include <assert.h> /* needed by PA_VALIDATE_ENDIANNESS */
-
-#include "portaudio.h"
-#include "pa_util.h"
-#include "pa_endianness.h"
-#include "pa_types.h"
-#include "pa_hostapi.h"
-#include "pa_stream.h"
-
-#include "pa_trace.h"
-
-
-#define PA_VERSION_ 1899
-#define PA_VERSION_TEXT_ "PortAudio V19-devel"
-
-
-
-/* #define PA_LOG_API_CALLS */
-
-/*
- The basic format for log messages is described below. If you need to
- add any log messages, please follow this format.
-
- Function entry (void function):
-
- "FunctionName called.\n"
-
- Function entry (non void function):
-
- "FunctionName called:\n"
- "\tParam1Type param1: param1Value\n"
- "\tParam2Type param2: param2Value\n" (etc...)
-
-
- Function exit (no return value):
-
- "FunctionName returned.\n"
-
- Function exit (simple return value):
-
- "FunctionName returned:\n"
- "\tReturnType: returnValue\n\n"
-
- If the return type is an error code, the error text is displayed in ()
-
- If the return type is not an error code, but has taken a special value
- because an error occurred, then the reason for the error is shown in []
-
- If the return type is a struct ptr, the struct is dumped.
-
- See the code below for examples
-*/
-
-
-int Pa_GetVersion( void )
-{
- return PA_VERSION_;
-}
-
-
-const char* Pa_GetVersionText( void )
-{
- return PA_VERSION_TEXT_;
-}
-
-
-
-#define PA_LAST_HOST_ERROR_TEXT_LENGTH_ 1024
-
-static char lastHostErrorText_[ PA_LAST_HOST_ERROR_TEXT_LENGTH_ + 1 ] = {0};
-
-static PaHostErrorInfo lastHostErrorInfo_ = { (PaHostApiTypeId)-1, 0, lastHostErrorText_ };
-
-
-void PaUtil_SetLastHostErrorInfo( PaHostApiTypeId hostApiType, long errorCode,
- const char *errorText )
-{
- lastHostErrorInfo_.hostApiType = hostApiType;
- lastHostErrorInfo_.errorCode = errorCode;
-
- strncpy( lastHostErrorText_, errorText, PA_LAST_HOST_ERROR_TEXT_LENGTH_ );
-}
-
-
-void PaUtil_DebugPrint( const char *format, ... )
-{
- va_list ap;
-
- va_start( ap, format );
- vfprintf( stderr, format, ap );
- va_end( ap );
-
- fflush( stderr );
-}
-
-
-static PaUtilHostApiRepresentation **hostApis_ = 0;
-static int hostApisCount_ = 0;
-static int initializationCount_ = 0;
-static int deviceCount_ = 0;
-
-PaUtilStreamRepresentation *firstOpenStream_ = NULL;
-
-
-#define PA_IS_INITIALISED_ (initializationCount_ != 0)
-
-
-static int CountHostApiInitializers( void )
-{
- int result = 0;
-
- while( paHostApiInitializers[ result ] != 0 )
- ++result;
- return result;
-}
-
-
-static void TerminateHostApis( void )
-{
- /* terminate in reverse order from initialization */
-
- while( hostApisCount_ > 0 )
- {
- --hostApisCount_;
- hostApis_[hostApisCount_]->Terminate( hostApis_[hostApisCount_] );
- }
- hostApisCount_ = 0;
- deviceCount_ = 0;
-
- if( hostApis_ != 0 )
- PaUtil_FreeMemory( hostApis_ );
- hostApis_ = 0;
-}
-
-
-static PaError InitializeHostApis( void )
-{
- PaError result = paNoError;
- int i, initializerCount, baseDeviceIndex;
-
- initializerCount = CountHostApiInitializers();
-
- hostApis_ = (PaUtilHostApiRepresentation**)PaUtil_AllocateMemory(
- sizeof(PaUtilHostApiRepresentation*) * initializerCount );
- if( !hostApis_ )
- {
- result = paInsufficientMemory;
- goto error;
- }
-
- hostApisCount_ = 0;
- deviceCount_ = 0;
- baseDeviceIndex = 0;
-
- for( i=0; i< initializerCount; ++i )
- {
- hostApis_[hostApisCount_] = NULL;
- result = paHostApiInitializers[i]( &hostApis_[hostApisCount_], hostApisCount_ );
- if( result != paNoError )
- goto error;
-
- if( hostApis_[hostApisCount_] )
- {
-
- hostApis_[hostApisCount_]->privatePaFrontInfo.baseDeviceIndex = baseDeviceIndex;
-
- if( hostApis_[hostApisCount_]->info.defaultInputDevice != paNoDevice )
- hostApis_[hostApisCount_]->info.defaultInputDevice += baseDeviceIndex;
-
- if( hostApis_[hostApisCount_]->info.defaultOutputDevice != paNoDevice )
- hostApis_[hostApisCount_]->info.defaultOutputDevice += baseDeviceIndex;
-
- baseDeviceIndex += hostApis_[hostApisCount_]->info.deviceCount;
- deviceCount_ += hostApis_[hostApisCount_]->info.deviceCount;
-
- ++hostApisCount_;
- }
- }
-
- return result;
-
-error:
- TerminateHostApis();
- return result;
-}
-
-
-/*
- FindHostApi() finds the index of the host api to which
- <device> belongs and returns it. if <hostSpecificDeviceIndex> is
- non-null, the host specific device index is returned in it.
- returns -1 if <device> is out of range.
-
-*/
-static int FindHostApi( PaDeviceIndex device, int *hostSpecificDeviceIndex )
-{
- int i=0;
-
- if( !PA_IS_INITIALISED_ )
- return -1;
-
- if( device < 0 )
- return -1;
-
- while( i < hostApisCount_
- && device >= hostApis_[i]->info.deviceCount )
- {
-
- device -= hostApis_[i]->info.deviceCount;
- ++i;
- }
-
- if( i >= hostApisCount_ )
- return -1;
-
- if( hostSpecificDeviceIndex )
- *hostSpecificDeviceIndex = device;
-
- return i;
-}
-
-
-static void AddOpenStream( PaStream* stream )
-{
- ((PaUtilStreamRepresentation*)stream)->nextOpenStream = firstOpenStream_;
- firstOpenStream_ = (PaUtilStreamRepresentation*)stream;
-}
-
-
-static void RemoveOpenStream( PaStream* stream )
-{
- PaUtilStreamRepresentation *previous = NULL;
- PaUtilStreamRepresentation *current = firstOpenStream_;
-
- while( current != NULL )
- {
- if( ((PaStream*)current) == stream )
- {
- if( previous == NULL )
- {
- firstOpenStream_ = current->nextOpenStream;
- }
- else
- {
- previous->nextOpenStream = current->nextOpenStream;
- }
- return;
- }
- else
- {
- previous = current;
- current = current->nextOpenStream;
- }
- }
-}
-
-
-static void CloseOpenStreams( void )
-{
- /* we call Pa_CloseStream() here to ensure that the same destruction
- logic is used for automatically closed streams */
-
- while( firstOpenStream_ != NULL )
- Pa_CloseStream( firstOpenStream_ );
-}
-
-
-PaError Pa_Initialize( void )
-{
- PaError result;
-
-#ifdef PA_LOG_API_CALLS
- PaUtil_DebugPrint( "Pa_Initialize called.\n" );
-#endif
-
- if( PA_IS_INITIALISED_ )
- {
- ++initializationCount_;
- result = paNoError;
- }
- else
- {
- PA_VALIDATE_TYPE_SIZES;
- PA_VALIDATE_ENDIANNESS;
-
- PaUtil_InitializeClock();
- PaUtil_ResetTraceMessages();
-
- result = InitializeHostApis();
- if( result == paNoError )
- ++initializationCount_;
- }
-
-#ifdef PA_LOG_API_CALLS
- PaUtil_DebugPrint( "Pa_Initialize returned:\n" );
- PaUtil_DebugPrint( "\tPaError: %d ( %s )\n\n", result, Pa_GetErrorText( result ) );
-#endif
-
- return result;
-}
-
-
-PaError Pa_Terminate( void )
-{
- PaError result;
-
-#ifdef PA_LOG_API_CALLS
- PaUtil_DebugPrint("Pa_Terminate called.\n" );
-#endif
-
- if( PA_IS_INITIALISED_ )
- {
- if( --initializationCount_ == 0 )
- {
- CloseOpenStreams();
-
- TerminateHostApis();
-
- PaUtil_DumpTraceMessages();
- }
- result = paNoError;
- }
- else
- {
- result= paNotInitialized;
- }
-
-#ifdef PA_LOG_API_CALLS
- PaUtil_DebugPrint("Pa_Terminate returned:\n" );
- PaUtil_DebugPrint("\tPaError: %d ( %s )\n\n", result, Pa_GetErrorText( result ) );
-#endif
-
- return result;
-}
-
-
-const PaHostErrorInfo* Pa_GetLastHostErrorInfo( void )
-{
- return &lastHostErrorInfo_;
-}
-
-
-const char *Pa_GetErrorText( PaError errorCode )
-{
- const char *result;
-
- switch( errorCode )
- {
- case paNoError: result = "Success"; break;
- case paNotInitialized: result = "PortAudio not initialized"; break;
- /** @todo could catenate the last host error text to result in the case of paUnanticipatedHostError */
- case paUnanticipatedHostError: result = "Unanticipated host error"; break;
- case paInvalidChannelCount: result = "Invalid number of channels"; break;
- case paInvalidSampleRate: result = "Invalid sample rate"; break;
- case paInvalidDevice: result = "Invalid device"; break;
- case paInvalidFlag: result = "Invalid flag"; break;
- case paSampleFormatNotSupported: result = "Sample format not supported"; break;
- case paBadIODeviceCombination: result = "Illegal combination of I/O devices"; break;
- case paInsufficientMemory: result = "Insufficient memory"; break;
- case paBufferTooBig: result = "Buffer too big"; break;
- case paBufferTooSmall: result = "Buffer too small"; break;
- case paNullCallback: result = "No callback routine specified"; break;
- case paBadStreamPtr: result = "Invalid stream pointer"; break;
- case paTimedOut: result = "Wait timed out"; break;
- case paInternalError: result = "Internal PortAudio error"; break;
- case paDeviceUnavailable: result = "Device unavailable"; break;
- case paIncompatibleHostApiSpecificStreamInfo: result = "Incompatible host API specific stream info"; break;
- case paStreamIsStopped: result = "Stream is stopped"; break;
- case paStreamIsNotStopped: result = "Stream is not stopped"; break;
- case paInputOverflowed: result = "Input overflowed"; break;
- case paOutputUnderflowed: result = "Output underflowed"; break;
- case paHostApiNotFound: result = "Host API not found"; break;
- case paInvalidHostApi: result = "Invalid host API"; break;
- case paCanNotReadFromACallbackStream: result = "Can't read from a callback stream"; break;
- case paCanNotWriteToACallbackStream: result = "Can't write to a callback stream"; break;
- case paCanNotReadFromAnOutputOnlyStream: result = "Can't read from an output only stream"; break;
- case paCanNotWriteToAnInputOnlyStream: result = "Can't write to an input only stream"; break;
- default: result = "Illegal error number"; break;
- }
- return result;
-}
-
-
-PaHostApiIndex Pa_HostApiTypeIdToHostApiIndex( PaHostApiTypeId type )
-{
- PaHostApiIndex result;
- int i;
-
-#ifdef PA_LOG_API_CALLS
- PaUtil_DebugPrint("Pa_HostApiTypeIdToHostApiIndex called:\n" );
- PaUtil_DebugPrint("\tPaHostApiTypeId type: %d\n", type );
-#endif
-
- if( !PA_IS_INITIALISED_ )
- {
- result = paNotInitialized;
- }
- else
- {
- result = paHostApiNotFound;
-
- for( i=0; i < hostApisCount_; ++i )
- {
- if( hostApis_[i]->info.type == type )
- {
- result = i;
- break;
- }
- }
- }
-
-#ifdef PA_LOG_API_CALLS
- PaUtil_DebugPrint("Pa_HostApiTypeIdToHostApiIndex returned:\n" );
- if( result < 0 )
- PaUtil_DebugPrint("\tPaError: %d ( %s )\n\n", result, Pa_GetErrorText( result ) );
- else
- PaUtil_DebugPrint("\tPaHostApiIndex: %d\n\n", result );
-#endif
-
- return result;
-}
-
-
-PaError PaUtil_GetHostApiRepresentation( struct PaUtilHostApiRepresentation **hostApi,
- PaHostApiTypeId type )
-{
- PaError result;
- int i;
-
- if( !PA_IS_INITIALISED_ )
- {
- result = paNotInitialized;
- }
- else
- {
- result = paHostApiNotFound;
-
- for( i=0; i < hostApisCount_; ++i )
- {
- if( hostApis_[i]->info.type == type )
- {
- *hostApi = hostApis_[i];
- result = paNoError;
- break;
- }
- }
- }
-
- return result;
-}
-
-
-PaError PaUtil_DeviceIndexToHostApiDeviceIndex(
- PaDeviceIndex *hostApiDevice, PaDeviceIndex device, struct PaUtilHostApiRepresentation *hostApi )
-{
- PaError result;
- PaDeviceIndex x;
-
- x = device - hostApi->privatePaFrontInfo.baseDeviceIndex;
-
- if( x < 0 || x >= hostApi->info.deviceCount )
- {
- result = paInvalidDevice;
- }
- else
- {
- *hostApiDevice = x;
- result = paNoError;
- }
-
- return result;
-}
-
-
-PaHostApiIndex Pa_GetHostApiCount( void )
-{
- int result;
-
-#ifdef PA_LOG_API_CALLS
- PaUtil_DebugPrint("Pa_GetHostApiCount called.\n" );
-#endif
-
- if( !PA_IS_INITIALISED_ )
- {
- result = paNotInitialized;
- }
- else
- {
- result = hostApisCount_;
- }
-
-#ifdef PA_LOG_API_CALLS
- PaUtil_DebugPrint("Pa_GetHostApiCount returned:\n" );
- if( result < 0 )
- PaUtil_DebugPrint("\tPaError: %d ( %s )\n\n", result, Pa_GetErrorText( result ) );
- else
- PaUtil_DebugPrint("\tPaHostApiIndex %d\n\n", result );
-#endif
-
- return result;
-}
-
-
-PaHostApiIndex Pa_GetDefaultHostApi( void )
-{
- int result;
-
-#ifdef PA_LOG_API_CALLS
- PaUtil_DebugPrint("Pa_GetDefaultHostApi called.\n" );
-#endif
-
- if( !PA_IS_INITIALISED_ )
- {
- result = paNotInitialized;
- }
- else
- {
- result = paDefaultHostApiIndex;
-
- /* internal consistency check: make sure that the default host api
- index is within range */
-
- if( result < 0 || result >= hostApisCount_ )
- {
- result = paInternalError;
- }
- }
-
-#ifdef PA_LOG_API_CALLS
- PaUtil_DebugPrint("Pa_GetDefaultHostApi returned:\n" );
- if( result < 0 )
- PaUtil_DebugPrint("\tPaError: %d ( %s )\n\n", result, Pa_GetErrorText( result ) );
- else
- PaUtil_DebugPrint("\tPaHostApiIndex %d\n\n", result );
-#endif
-
- return result;
-}
-
-
-const PaHostApiInfo* Pa_GetHostApiInfo( PaHostApiIndex hostApi )
-{
- PaHostApiInfo *info;
-
-#ifdef PA_LOG_API_CALLS
- PaUtil_DebugPrint("Pa_GetHostApiInfo called:\n" );
- PaUtil_DebugPrint("\tPaHostApiIndex hostApi: %d\n", hostApi );
-#endif
-
- if( !PA_IS_INITIALISED_ )
- {
- info = NULL;
-
-#ifdef PA_LOG_API_CALLS
- PaUtil_DebugPrint("Pa_GetHostApiInfo returned:\n" );
- PaUtil_DebugPrint("\tPaHostApiInfo*: NULL [ PortAudio not initialized ]\n\n" );
-#endif
-
- }
- else if( hostApi < 0 || hostApi >= hostApisCount_ )
- {
- info = NULL;
-
-#ifdef PA_LOG_API_CALLS
- PaUtil_DebugPrint("Pa_GetHostApiInfo returned:\n" );
- PaUtil_DebugPrint("\tPaHostApiInfo*: NULL [ hostApi out of range ]\n\n" );
-#endif
-
- }
- else
- {
- info = &hostApis_[hostApi]->info;
-
-#ifdef PA_LOG_API_CALLS
- PaUtil_DebugPrint("Pa_GetHostApiInfo returned:\n" );
- PaUtil_DebugPrint("\tPaHostApiInfo*: 0x%p\n", info );
- PaUtil_DebugPrint("\t{" );
- PaUtil_DebugPrint("\t\tint structVersion: %d\n", info->structVersion );
- PaUtil_DebugPrint("\t\tPaHostApiTypeId type: %d\n", info->type );
- PaUtil_DebugPrint("\t\tconst char *name: %s\n\n", info->name );
- PaUtil_DebugPrint("\t}\n\n" );
-#endif
-
- }
-
- return info;
-}
-
-
-PaDeviceIndex Pa_HostApiDeviceIndexToDeviceIndex( PaHostApiIndex hostApi, int hostApiDeviceIndex )
-{
- PaDeviceIndex result;
-
-#ifdef PA_LOG_API_CALLS
- PaUtil_DebugPrint("Pa_HostApiDeviceIndexToPaDeviceIndex called:\n" );
- PaUtil_DebugPrint("\tPaHostApiIndex hostApi: %d\n", hostApi );
- PaUtil_DebugPrint("\tint hostApiDeviceIndex: %d\n", hostApiDeviceIndex );
-#endif
-
- if( !PA_IS_INITIALISED_ )
- {
- result = paNotInitialized;
- }
- else
- {
- if( hostApi < 0 || hostApi >= hostApisCount_ )
- {
- result = paInvalidHostApi;
- }
- else
- {
- if( hostApiDeviceIndex < 0 ||
- hostApiDeviceIndex >= hostApis_[hostApi]->info.deviceCount )
- {
- result = paInvalidDevice;
- }
- else
- {
- result = hostApis_[hostApi]->privatePaFrontInfo.baseDeviceIndex + hostApiDeviceIndex;
- }
- }
- }
-
-#ifdef PA_LOG_API_CALLS
- PaUtil_DebugPrint("Pa_HostApiDeviceIndexToPaDeviceIndex returned:\n" );
- if( result < 0 )
- PaUtil_DebugPrint("\tPaError: %d ( %s )\n\n", result, Pa_GetErrorText( result ) );
- else
- PaUtil_DebugPrint("\tPaDeviceIndex: %d\n\n", result );
-#endif
-
- return result;
-}
-
-
-PaDeviceIndex Pa_GetDeviceCount( void )
-{
- PaDeviceIndex result;
-
-#ifdef PA_LOG_API_CALLS
- PaUtil_DebugPrint("Pa_GetDeviceCount called.\n" );
-#endif
-
- if( !PA_IS_INITIALISED_ )
- {
- result = paNotInitialized;
- }
- else
- {
- result = deviceCount_;
- }
-
-#ifdef PA_LOG_API_CALLS
- PaUtil_DebugPrint("Pa_GetDeviceCount returned:\n" );
- if( result < 0 )
- PaUtil_DebugPrint("\tPaError: %d ( %s )\n\n", result, Pa_GetErrorText( result ) );
- else
- PaUtil_DebugPrint("\tPaDeviceIndex: %d\n\n", result );
-#endif
-
- return result;
-}
-
-
-PaDeviceIndex Pa_GetDefaultInputDevice( void )
-{
- PaHostApiIndex hostApi;
- PaDeviceIndex result;
-
-#ifdef PA_LOG_API_CALLS
- PaUtil_DebugPrint("Pa_GetDefaultInputDevice called.\n" );
-#endif
-
- hostApi = Pa_GetDefaultHostApi();
- if( hostApi < 0 )
- {
- result = paNoDevice;
- }
- else
- {
- result = hostApis_[hostApi]->info.defaultInputDevice;
- }
-
-#ifdef PA_LOG_API_CALLS
- PaUtil_DebugPrint("Pa_GetDefaultInputDevice returned:\n" );
- PaUtil_DebugPrint("\tPaDeviceIndex: %d\n\n", result );
-#endif
-
- return result;
-}
-
-
-PaDeviceIndex Pa_GetDefaultOutputDevice( void )
-{
- PaHostApiIndex hostApi;
- PaDeviceIndex result;
-
-#ifdef PA_LOG_API_CALLS
- PaUtil_DebugPrint("Pa_GetDefaultOutputDevice called.\n" );
-#endif
-
- hostApi = Pa_GetDefaultHostApi();
- if( hostApi < 0 )
- {
- result = paNoDevice;
- }
- else
- {
- result = hostApis_[hostApi]->info.defaultOutputDevice;
- }
-
-#ifdef PA_LOG_API_CALLS
- PaUtil_DebugPrint("Pa_GetDefaultOutputDevice returned:\n" );
- PaUtil_DebugPrint("\tPaDeviceIndex: %d\n\n", result );
-#endif
-
- return result;
-}
-
-
-const PaDeviceInfo* Pa_GetDeviceInfo( PaDeviceIndex device )
-{
- int hostSpecificDeviceIndex;
- int hostApiIndex = FindHostApi( device, &hostSpecificDeviceIndex );
- PaDeviceInfo *result;
-
-
-#ifdef PA_LOG_API_CALLS
- PaUtil_DebugPrint("Pa_GetDeviceInfo called:\n" );
- PaUtil_DebugPrint("\tPaDeviceIndex device: %d\n", device );
-#endif
-
- if( hostApiIndex < 0 )
- {
- result = NULL;
-
-#ifdef PA_LOG_API_CALLS
- PaUtil_DebugPrint("Pa_GetDeviceInfo returned:\n" );
- PaUtil_DebugPrint("\tPaDeviceInfo* NULL [ invalid device index ]\n\n" );
-#endif
-
- }
- else
- {
- result = hostApis_[hostApiIndex]->deviceInfos[ hostSpecificDeviceIndex ];
-
-#ifdef PA_LOG_API_CALLS
- PaUtil_DebugPrint("Pa_GetDeviceInfo returned:\n" );
- PaUtil_DebugPrint("\tPaDeviceInfo*: 0x%p:\n", result );
- PaUtil_DebugPrint("\t{\n" );
-
- PaUtil_DebugPrint("\t\tint structVersion: %d\n", result->structVersion );
- PaUtil_DebugPrint("\t\tconst char *name: %s\n", result->name );
- PaUtil_DebugPrint("\t\tPaHostApiIndex hostApi: %d\n", result->hostApi );
- PaUtil_DebugPrint("\t\tint maxInputChannels: %d\n", result->maxInputChannels );
- PaUtil_DebugPrint("\t\tint maxOutputChannels: %d\n", result->maxOutputChannels );
- PaUtil_DebugPrint("\t}\n\n" );
-#endif
-
- }
-
- return result;
-}
-
-
-/*
- SampleFormatIsValid() returns 1 if sampleFormat is a sample format
- defined in portaudio.h, or 0 otherwise.
-*/
-static int SampleFormatIsValid( PaSampleFormat format )
-{
- switch( format & ~paNonInterleaved )
- {
- case paFloat32: return 1;
- case paInt16: return 1;
- case paInt32: return 1;
- case paInt24: return 1;
- case paInt8: return 1;
- case paUInt8: return 1;
- case paCustomFormat: return 1;
- default: return 0;
- }
-}
-
-/*
- NOTE: make sure this validation list is kept syncronised with the one in
- pa_hostapi.h
-
- ValidateOpenStreamParameters() checks that parameters to Pa_OpenStream()
- conform to the expected values as described below. This function is
- also designed to be used with the proposed Pa_IsFormatSupported() function.
-
- There are basically two types of validation that could be performed:
- Generic conformance validation, and device capability mismatch
- validation. This function performs only generic conformance validation.
- Validation that would require knowledge of device capabilities is
- not performed because of potentially complex relationships between
- combinations of parameters - for example, even if the sampleRate
- seems ok, it might not be for a duplex stream - we have no way of
- checking this in an API-neutral way, so we don't try.
-
- On success the function returns PaNoError and fills in hostApi,
- hostApiInputDeviceID, and hostApiOutputDeviceID fields. On failure
- the function returns an error code indicating the first encountered
- parameter error.
-
-
- If ValidateOpenStreamParameters() returns paNoError, the following
- assertions are guaranteed to be true.
-
- - at least one of inputParameters & outputParmeters is valid (not NULL)
-
- - if inputParameters & outputParmeters are both valid, that
- inputParameters->device & outputParmeters->device both use the same host api
-
- PaDeviceIndex inputParameters->device
- - is within range (0 to Pa_GetDeviceCount-1) Or:
- - is paUseHostApiSpecificDeviceSpecification and
- inputParameters->hostApiSpecificStreamInfo is non-NULL and refers
- to a valid host api
-
- int inputParameters->channelCount
- - if inputParameters->device is not paUseHostApiSpecificDeviceSpecification, channelCount is > 0
- - upper bound is NOT validated against device capabilities
-
- PaSampleFormat inputParameters->sampleFormat
- - is one of the sample formats defined in portaudio.h
-
- void *inputParameters->hostApiSpecificStreamInfo
- - if supplied its hostApi field matches the input device's host Api
-
- PaDeviceIndex outputParmeters->device
- - is within range (0 to Pa_GetDeviceCount-1)
-
- int outputParmeters->channelCount
- - if inputDevice is valid, channelCount is > 0
- - upper bound is NOT validated against device capabilities
-
- PaSampleFormat outputParmeters->sampleFormat
- - is one of the sample formats defined in portaudio.h
-
- void *outputParmeters->hostApiSpecificStreamInfo
- - if supplied its hostApi field matches the output device's host Api
-
- double sampleRate
- - is not an 'absurd' rate (less than 1000. or greater than 200000.)
- - sampleRate is NOT validated against device capabilities
-
- PaStreamFlags streamFlags
- - unused platform neutral flags are zero
- - paNeverDropInput is only used for full-duplex callback streams with
- variable buffer size (paFramesPerBufferUnspecified)
-*/
-static PaError ValidateOpenStreamParameters(
- const PaStreamParameters *inputParameters,
- const PaStreamParameters *outputParameters,
- double sampleRate,
- unsigned long framesPerBuffer,
- PaStreamFlags streamFlags,
- PaStreamCallback *streamCallback,
- PaUtilHostApiRepresentation **hostApi,
- PaDeviceIndex *hostApiInputDevice,
- PaDeviceIndex *hostApiOutputDevice )
-{
- int inputHostApiIndex = -1, /* Surpress uninitialised var warnings: compiler does */
- outputHostApiIndex = -1; /* not see that if inputParameters and outputParame- */
- /* ters are both nonzero, these indices are set. */
-
- if( (inputParameters == NULL) && (outputParameters == NULL) )
- {
- return paInvalidDevice; /** @todo should be a new error code "invalid device parameters" or something */
- }
- else
- {
- if( inputParameters == NULL )
- {
- *hostApiInputDevice = paNoDevice;
- }
- else if( inputParameters->device == paUseHostApiSpecificDeviceSpecification )
- {
- if( inputParameters->hostApiSpecificStreamInfo )
- {
- inputHostApiIndex = Pa_HostApiTypeIdToHostApiIndex(
- ((PaUtilHostApiSpecificStreamInfoHeader*)inputParameters->hostApiSpecificStreamInfo)->hostApiType );
-
- if( inputHostApiIndex != -1 )
- {
- *hostApiInputDevice = paUseHostApiSpecificDeviceSpecification;
- *hostApi = hostApis_[inputHostApiIndex];
- }
- else
- {
- return paInvalidDevice;
- }
- }
- else
- {
- return paInvalidDevice;
- }
- }
- else
- {
- if( inputParameters->device < 0 || inputParameters->device >= deviceCount_ )
- return paInvalidDevice;
-
- inputHostApiIndex = FindHostApi( inputParameters->device, hostApiInputDevice );
- if( inputHostApiIndex < 0 )
- return paInternalError;
-
- *hostApi = hostApis_[inputHostApiIndex];
-
- if( inputParameters->channelCount <= 0 )
- return paInvalidChannelCount;
-
- if( !SampleFormatIsValid( inputParameters->sampleFormat ) )
- return paSampleFormatNotSupported;
-
- if( inputParameters->hostApiSpecificStreamInfo != NULL )
- {
- if( ((PaUtilHostApiSpecificStreamInfoHeader*)inputParameters->hostApiSpecificStreamInfo)->hostApiType
- != (*hostApi)->info.type )
- return paIncompatibleHostApiSpecificStreamInfo;
- }
- }
-
- if( outputParameters == NULL )
- {
- *hostApiOutputDevice = paNoDevice;
- }
- else if( outputParameters->device == paUseHostApiSpecificDeviceSpecification )
- {
- if( outputParameters->hostApiSpecificStreamInfo )
- {
- outputHostApiIndex = Pa_HostApiTypeIdToHostApiIndex(
- ((PaUtilHostApiSpecificStreamInfoHeader*)outputParameters->hostApiSpecificStreamInfo)->hostApiType );
-
- if( outputHostApiIndex != -1 )
- {
- *hostApiOutputDevice = paUseHostApiSpecificDeviceSpecification;
- *hostApi = hostApis_[outputHostApiIndex];
- }
- else
- {
- return paInvalidDevice;
- }
- }
- else
- {
- return paInvalidDevice;
- }
- }
- else
- {
- if( outputParameters->device < 0 || outputParameters->device >= deviceCount_ )
- return paInvalidDevice;
-
- outputHostApiIndex = FindHostApi( outputParameters->device, hostApiOutputDevice );
- if( outputHostApiIndex < 0 )
- return paInternalError;
-
- *hostApi = hostApis_[outputHostApiIndex];
-
- if( outputParameters->channelCount <= 0 )
- return paInvalidChannelCount;
-
- if( !SampleFormatIsValid( outputParameters->sampleFormat ) )
- return paSampleFormatNotSupported;
-
- if( outputParameters->hostApiSpecificStreamInfo != NULL )
- {
- if( ((PaUtilHostApiSpecificStreamInfoHeader*)outputParameters->hostApiSpecificStreamInfo)->hostApiType
- != (*hostApi)->info.type )
- return paIncompatibleHostApiSpecificStreamInfo;
- }
- }
-
- if( (inputParameters != NULL) && (outputParameters != NULL) )
- {
- /* ensure that both devices use the same API */
- if( inputHostApiIndex != outputHostApiIndex )
- return paBadIODeviceCombination;
- }
- }
-
-
- /* Check for absurd sample rates. */
- if( (sampleRate < 1000.0) || (sampleRate > 200000.0) )
- return paInvalidSampleRate;
-
- if( ((streamFlags & ~paPlatformSpecificFlags) & ~(paClipOff | paDitherOff | paNeverDropInput | paPrimeOutputBuffersUsingStreamCallback ) ) != 0 )
- return paInvalidFlag;
-
- if( streamFlags & paNeverDropInput )
- {
- /* must be a callback stream */
- if( !streamCallback )
- return paInvalidFlag;
-
- /* must be a full duplex stream */
- if( (inputParameters == NULL) || (outputParameters == NULL) )
- return paInvalidFlag;
-
- /* must use paFramesPerBufferUnspecified */
- if( framesPerBuffer != paFramesPerBufferUnspecified )
- return paInvalidFlag;
- }
-
- return paNoError;
-}
-
-
-PaError Pa_IsFormatSupported( const PaStreamParameters *inputParameters,
- const PaStreamParameters *outputParameters,
- double sampleRate )
-{
- PaError result;
- PaUtilHostApiRepresentation *hostApi;
- PaDeviceIndex hostApiInputDevice, hostApiOutputDevice;
- PaStreamParameters hostApiInputParameters, hostApiOutputParameters;
- PaStreamParameters *hostApiInputParametersPtr, *hostApiOutputParametersPtr;
-
-
-#ifdef PA_LOG_API_CALLS
- PaUtil_DebugPrint("Pa_IsFormatSupported called:\n" );
-
- if( inputParameters == NULL ){
- PaUtil_DebugPrint("\tPaStreamParameters *inputParameters: NULL\n" );
- }else{
- PaUtil_DebugPrint("\tPaStreamParameters *inputParameters: 0x%p\n", inputParameters );
- PaUtil_DebugPrint("\tPaDeviceIndex inputParameters->device: %d\n", inputParameters->device );
- PaUtil_DebugPrint("\tint inputParameters->channelCount: %d\n", inputParameters->channelCount );
- PaUtil_DebugPrint("\tPaSampleFormat inputParameters->sampleFormat: %d\n", inputParameters->sampleFormat );
- PaUtil_DebugPrint("\tPaTime inputParameters->suggestedLatency: %f\n", inputParameters->suggestedLatency );
- PaUtil_DebugPrint("\tvoid *inputParameters->hostApiSpecificStreamInfo: 0x%p\n", inputParameters->hostApiSpecificStreamInfo );
- }
-
- if( outputParameters == NULL ){
- PaUtil_DebugPrint("\tPaStreamParameters *outputParameters: NULL\n" );
- }else{
- PaUtil_DebugPrint("\tPaStreamParameters *outputParameters: 0x%p\n", outputParameters );
- PaUtil_DebugPrint("\tPaDeviceIndex outputParameters->device: %d\n", outputParameters->device );
- PaUtil_DebugPrint("\tint outputParameters->channelCount: %d\n", outputParameters->channelCount );
- PaUtil_DebugPrint("\tPaSampleFormat outputParameters->sampleFormat: %d\n", outputParameters->sampleFormat );
- PaUtil_DebugPrint("\tPaTime outputParameters->suggestedLatency: %f\n", outputParameters->suggestedLatency );
- PaUtil_DebugPrint("\tvoid *outputParameters->hostApiSpecificStreamInfo: 0x%p\n", outputParameters->hostApiSpecificStreamInfo );
- }
-
- PaUtil_DebugPrint("\tdouble sampleRate: %g\n", sampleRate );
-#endif
-
- if( !PA_IS_INITIALISED_ )
- {
- result = paNotInitialized;
-
-#ifdef PA_LOG_API_CALLS
- PaUtil_DebugPrint("Pa_IsFormatSupported returned:\n" );
- PaUtil_DebugPrint("\tPaError: %d ( %s )\n\n", result, Pa_GetErrorText( result ) );
-#endif
- return result;
- }
-
- result = ValidateOpenStreamParameters( inputParameters,
- outputParameters,
- sampleRate, 0, paNoFlag, 0,
- &hostApi,
- &hostApiInputDevice,
- &hostApiOutputDevice );
- if( result != paNoError )
- {
-#ifdef PA_LOG_API_CALLS
- PaUtil_DebugPrint("Pa_IsFormatSupported returned:\n" );
- PaUtil_DebugPrint("\tPaError: %d ( %s )\n\n", result, Pa_GetErrorText( result ) );
-#endif
- return result;
- }
-
-
- if( inputParameters )
- {
- hostApiInputParameters.device = hostApiInputDevice;
- hostApiInputParameters.channelCount = inputParameters->channelCount;
- hostApiInputParameters.sampleFormat = inputParameters->sampleFormat;
- hostApiInputParameters.suggestedLatency = inputParameters->suggestedLatency;
- hostApiInputParameters.hostApiSpecificStreamInfo = inputParameters->hostApiSpecificStreamInfo;
- hostApiInputParametersPtr = &hostApiInputParameters;
- }
- else
- {
- hostApiInputParametersPtr = NULL;
- }
-
- if( outputParameters )
- {
- hostApiOutputParameters.device = hostApiOutputDevice;
- hostApiOutputParameters.channelCount = outputParameters->channelCount;
- hostApiOutputParameters.sampleFormat = outputParameters->sampleFormat;
- hostApiOutputParameters.suggestedLatency = outputParameters->suggestedLatency;
- hostApiOutputParameters.hostApiSpecificStreamInfo = outputParameters->hostApiSpecificStreamInfo;
- hostApiOutputParametersPtr = &hostApiOutputParameters;
- }
- else
- {
- hostApiOutputParametersPtr = NULL;
- }
-
- result = hostApi->IsFormatSupported( hostApi,
- hostApiInputParametersPtr, hostApiOutputParametersPtr,
- sampleRate );
-
-#ifdef PA_LOG_API_CALLS
- PaUtil_DebugPrint("Pa_OpenStream returned:\n" );
- if( result == paFormatIsSupported )
- PaUtil_DebugPrint("\tPaError: 0 [ paFormatIsSupported ]\n\n" );
- else
- PaUtil_DebugPrint("\tPaError: %d ( %s )\n\n", result, Pa_GetErrorText( result ) );
-#endif
-
- return result;
-}
-
-
-PaError Pa_OpenStream( PaStream** stream,
- const PaStreamParameters *inputParameters,
- const PaStreamParameters *outputParameters,
- double sampleRate,
- unsigned long framesPerBuffer,
- PaStreamFlags streamFlags,
- PaStreamCallback *streamCallback,
- void *userData )
-{
- PaError result;
- PaUtilHostApiRepresentation *hostApi;
- PaDeviceIndex hostApiInputDevice, hostApiOutputDevice;
- PaStreamParameters hostApiInputParameters, hostApiOutputParameters;
- PaStreamParameters *hostApiInputParametersPtr, *hostApiOutputParametersPtr;
-
-
-#ifdef PA_LOG_API_CALLS
- PaUtil_DebugPrint("Pa_OpenStream called:\n" );
- PaUtil_DebugPrint("\tPaStream** stream: 0x%p\n", stream );
-
- if( inputParameters == NULL ){
- PaUtil_DebugPrint("\tPaStreamParameters *inputParameters: NULL\n" );
- }else{
- PaUtil_DebugPrint("\tPaStreamParameters *inputParameters: 0x%p\n", inputParameters );
- PaUtil_DebugPrint("\tPaDeviceIndex inputParameters->device: %d\n", inputParameters->device );
- PaUtil_DebugPrint("\tint inputParameters->channelCount: %d\n", inputParameters->channelCount );
- PaUtil_DebugPrint("\tPaSampleFormat inputParameters->sampleFormat: %d\n", inputParameters->sampleFormat );
- PaUtil_DebugPrint("\tPaTime inputParameters->suggestedLatency: %f\n", inputParameters->suggestedLatency );
- PaUtil_DebugPrint("\tvoid *inputParameters->hostApiSpecificStreamInfo: 0x%p\n", inputParameters->hostApiSpecificStreamInfo );
- }
-
- if( outputParameters == NULL ){
- PaUtil_DebugPrint("\tPaStreamParameters *outputParameters: NULL\n" );
- }else{
- PaUtil_DebugPrint("\tPaStreamParameters *outputParameters: 0x%p\n", outputParameters );
- PaUtil_DebugPrint("\tPaDeviceIndex outputParameters->device: %d\n", outputParameters->device );
- PaUtil_DebugPrint("\tint outputParameters->channelCount: %d\n", outputParameters->channelCount );
- PaUtil_DebugPrint("\tPaSampleFormat outputParameters->sampleFormat: %d\n", outputParameters->sampleFormat );
- PaUtil_DebugPrint("\tPaTime outputParameters->suggestedLatency: %f\n", outputParameters->suggestedLatency );
- PaUtil_DebugPrint("\tvoid *outputParameters->hostApiSpecificStreamInfo: 0x%p\n", outputParameters->hostApiSpecificStreamInfo );
- }
-
- PaUtil_DebugPrint("\tdouble sampleRate: %g\n", sampleRate );
- PaUtil_DebugPrint("\tunsigned long framesPerBuffer: %d\n", framesPerBuffer );
- PaUtil_DebugPrint("\tPaStreamFlags streamFlags: 0x%x\n", streamFlags );
- PaUtil_DebugPrint("\tPaStreamCallback *streamCallback: 0x%p\n", streamCallback );
- PaUtil_DebugPrint("\tvoid *userData: 0x%p\n", userData );
-#endif
-
- if( !PA_IS_INITIALISED_ )
- {
- result = paNotInitialized;
-
-#ifdef PA_LOG_API_CALLS
- PaUtil_DebugPrint("Pa_OpenStream returned:\n" );
- PaUtil_DebugPrint("\t*(PaStream** stream): undefined\n" );
- PaUtil_DebugPrint("\tPaError: %d ( %s )\n\n", result, Pa_GetErrorText( result ) );
-#endif
- return result;
- }
-
- /* Check for parameter errors.
- NOTE: make sure this validation list is kept syncronised with the one
- in pa_hostapi.h
- */
-
- if( stream == NULL )
- {
- result = paBadStreamPtr;
-
-#ifdef PA_LOG_API_CALLS
- PaUtil_DebugPrint("Pa_OpenStream returned:\n" );
- PaUtil_DebugPrint("\t*(PaStream** stream): undefined\n" );
- PaUtil_DebugPrint("\tPaError: %d ( %s )\n\n", result, Pa_GetErrorText( result ) );
-#endif
- return result;
- }
-
- result = ValidateOpenStreamParameters( inputParameters,
- outputParameters,
- sampleRate, framesPerBuffer,
- streamFlags, streamCallback,
- &hostApi,
- &hostApiInputDevice,
- &hostApiOutputDevice );
- if( result != paNoError )
- {
-#ifdef PA_LOG_API_CALLS
- PaUtil_DebugPrint("Pa_OpenStream returned:\n" );
- PaUtil_DebugPrint("\t*(PaStream** stream): undefined\n" );
- PaUtil_DebugPrint("\tPaError: %d ( %s )\n\n", result, Pa_GetErrorText( result ) );
-#endif
- return result;
- }
-
-
- if( inputParameters )
- {
- hostApiInputParameters.device = hostApiInputDevice;
- hostApiInputParameters.channelCount = inputParameters->channelCount;
- hostApiInputParameters.sampleFormat = inputParameters->sampleFormat;
- hostApiInputParameters.suggestedLatency = inputParameters->suggestedLatency;
- hostApiInputParameters.hostApiSpecificStreamInfo = inputParameters->hostApiSpecificStreamInfo;
- hostApiInputParametersPtr = &hostApiInputParameters;
- }
- else
- {
- hostApiInputParametersPtr = NULL;
- }
-
- if( outputParameters )
- {
- hostApiOutputParameters.device = hostApiOutputDevice;
- hostApiOutputParameters.channelCount = outputParameters->channelCount;
- hostApiOutputParameters.sampleFormat = outputParameters->sampleFormat;
- hostApiOutputParameters.suggestedLatency = outputParameters->suggestedLatency;
- hostApiOutputParameters.hostApiSpecificStreamInfo = outputParameters->hostApiSpecificStreamInfo;
- hostApiOutputParametersPtr = &hostApiOutputParameters;
- }
- else
- {
- hostApiOutputParametersPtr = NULL;
- }
-
- result = hostApi->OpenStream( hostApi, stream,
- hostApiInputParametersPtr, hostApiOutputParametersPtr,
- sampleRate, framesPerBuffer, streamFlags, streamCallback, userData );
-
- if( result == paNoError )
- AddOpenStream( *stream );
-
-
-#ifdef PA_LOG_API_CALLS
- PaUtil_DebugPrint("Pa_OpenStream returned:\n" );
- PaUtil_DebugPrint("\t*(PaStream** stream): 0x%p\n", *stream );
- PaUtil_DebugPrint("\tPaError: %d ( %s )\n\n", result, Pa_GetErrorText( result ) );
-#endif
-
- return result;
-}
-
-
-PaError Pa_OpenDefaultStream( PaStream** stream,
- int inputChannelCount,
- int outputChannelCount,
- PaSampleFormat sampleFormat,
- double sampleRate,
- unsigned long framesPerBuffer,
- PaStreamCallback *streamCallback,
- void *userData )
-{
- PaError result;
- PaStreamParameters hostApiInputParameters, hostApiOutputParameters;
- PaStreamParameters *hostApiInputParametersPtr, *hostApiOutputParametersPtr;
-
-#ifdef PA_LOG_API_CALLS
- PaUtil_DebugPrint("Pa_OpenDefaultStream called:\n" );
- PaUtil_DebugPrint("\tPaStream** stream: 0x%p\n", stream );
- PaUtil_DebugPrint("\tint inputChannelCount: %d\n", inputChannelCount );
- PaUtil_DebugPrint("\tint outputChannelCount: %d\n", outputChannelCount );
- PaUtil_DebugPrint("\tPaSampleFormat sampleFormat: %d\n", sampleFormat );
- PaUtil_DebugPrint("\tdouble sampleRate: %g\n", sampleRate );
- PaUtil_DebugPrint("\tunsigned long framesPerBuffer: %d\n", framesPerBuffer );
- PaUtil_DebugPrint("\tPaStreamCallback *streamCallback: 0x%p\n", streamCallback );
- PaUtil_DebugPrint("\tvoid *userData: 0x%p\n", userData );
-#endif
-
-
- if( inputChannelCount > 0 )
- {
- hostApiInputParameters.device = Pa_GetDefaultInputDevice();
- hostApiInputParameters.channelCount = inputChannelCount;
- hostApiInputParameters.sampleFormat = sampleFormat;
- /* defaultHighInputLatency is used below instead of
- defaultLowInputLatency because it is more important for the default
- stream to work reliably than it is for it to work with the lowest
- latency.
- */
- hostApiInputParameters.suggestedLatency =
- Pa_GetDeviceInfo( hostApiInputParameters.device )->defaultHighInputLatency;
- hostApiInputParameters.hostApiSpecificStreamInfo = NULL;
- hostApiInputParametersPtr = &hostApiInputParameters;
- }
- else
- {
- hostApiInputParametersPtr = NULL;
- }
-
- if( outputChannelCount > 0 )
- {
- hostApiOutputParameters.device = Pa_GetDefaultOutputDevice();
- hostApiOutputParameters.channelCount = outputChannelCount;
- hostApiOutputParameters.sampleFormat = sampleFormat;
- /* defaultHighOutputLatency is used below instead of
- defaultLowOutputLatency because it is more important for the default
- stream to work reliably than it is for it to work with the lowest
- latency.
- */
- hostApiOutputParameters.suggestedLatency =
- Pa_GetDeviceInfo( hostApiOutputParameters.device )->defaultHighOutputLatency;
- hostApiOutputParameters.hostApiSpecificStreamInfo = NULL;
- hostApiOutputParametersPtr = &hostApiOutputParameters;
- }
- else
- {
- hostApiOutputParametersPtr = NULL;
- }
-
-
- result = Pa_OpenStream(
- stream, hostApiInputParametersPtr, hostApiOutputParametersPtr,
- sampleRate, framesPerBuffer, paNoFlag, streamCallback, userData );
-
-#ifdef PA_LOG_API_CALLS
- PaUtil_DebugPrint("Pa_OpenDefaultStream returned:\n" );
- PaUtil_DebugPrint("\t*(PaStream** stream): 0x%p", *stream );
- PaUtil_DebugPrint("\tPaError: %d ( %s )\n\n", result, Pa_GetErrorText( result ) );
-#endif
-
- return result;
-}
-
-
-PaError PaUtil_ValidateStreamPointer( PaStream* stream )
-{
- if( !PA_IS_INITIALISED_ ) return paNotInitialized;
-
- if( stream == NULL ) return paBadStreamPtr;
-
- if( ((PaUtilStreamRepresentation*)stream)->magic != PA_STREAM_MAGIC )
- return paBadStreamPtr;
-
- return paNoError;
-}
-
-
-PaError Pa_CloseStream( PaStream* stream )
-{
- PaUtilStreamInterface *interface;
- PaError result = PaUtil_ValidateStreamPointer( stream );
-
-#ifdef PA_LOG_API_CALLS
- PaUtil_DebugPrint("Pa_CloseStream called:\n" );
- PaUtil_DebugPrint("\tPaStream* stream: 0x%p\n", stream );
-#endif
-
- /* always remove the open stream from our list, even if this function
- eventually returns an error. Otherwise CloseOpenStreams() will
- get stuck in an infinite loop */
- RemoveOpenStream( stream ); /* be sure to call this _before_ closing the stream */
-
- if( result == paNoError )
- {
- interface = PA_STREAM_INTERFACE(stream);
-
- /* abort the stream if it isn't stopped */
- result = interface->IsStopped( stream );
- if( result == 1 )
- result = paNoError;
- else if( result == 0 )
- result = interface->Abort( stream );
-
- if( result == paNoError ) /** @todo REVIEW: shouldn't we close anyway? */
- result = interface->Close( stream );
- }
-
-#ifdef PA_LOG_API_CALLS
- PaUtil_DebugPrint("Pa_CloseStream returned:\n" );
- PaUtil_DebugPrint("\tPaError: %d ( %s )\n\n", result, Pa_GetErrorText( result ) );
-#endif
-
- return result;
-}
-
-
-PaError Pa_SetStreamFinishedCallback( PaStream *stream, PaStreamFinishedCallback* streamFinishedCallback )
-{
- PaError result = PaUtil_ValidateStreamPointer( stream );
-
-#ifdef PA_LOG_API_CALLS
- PaUtil_DebugPrint("Pa_SetStreamFinishedCallback called:\n" );
- PaUtil_DebugPrint("\tPaStream* stream: 0x%p\n", stream );
- PaUtil_DebugPrint("\tPaStreamFinishedCallback* streamFinishedCallback: 0x%p\n", streamFinishedCallback );
-#endif
-
- if( result == paNoError )
- {
- result = PA_STREAM_INTERFACE(stream)->IsStopped( stream );
- if( result == 0 )
- {
- result = paStreamIsNotStopped ;
- }
- if( result == 1 )
- {
- PA_STREAM_REP( stream )->streamFinishedCallback = streamFinishedCallback;
- result = paNoError;
- }
- }
-
-#ifdef PA_LOG_API_CALLS
- PaUtil_DebugPrint("Pa_SetStreamFinishedCallback returned:\n" );
- PaUtil_DebugPrint("\tPaError: %d ( %s )\n\n", result, Pa_GetErrorText( result ) );
-#endif
-
- return result;
-
-}
-
-
-PaError Pa_StartStream( PaStream *stream )
-{
- PaError result = PaUtil_ValidateStreamPointer( stream );
-
-#ifdef PA_LOG_API_CALLS
- PaUtil_DebugPrint("Pa_StartStream called:\n" );
- PaUtil_DebugPrint("\tPaStream* stream: 0x%p\n", stream );
-#endif
-
- if( result == paNoError )
- {
- result = PA_STREAM_INTERFACE(stream)->IsStopped( stream );
- if( result == 0 )
- {
- result = paStreamIsNotStopped ;
- }
- else if( result == 1 )
- {
- result = PA_STREAM_INTERFACE(stream)->Start( stream );
- }
- }
-
-#ifdef PA_LOG_API_CALLS
- PaUtil_DebugPrint("Pa_StartStream returned:\n" );
- PaUtil_DebugPrint("\tPaError: %d ( %s )\n\n", result, Pa_GetErrorText( result ) );
-#endif
-
- return result;
-}
-
-
-PaError Pa_StopStream( PaStream *stream )
-{
- PaError result = PaUtil_ValidateStreamPointer( stream );
-
-#ifdef PA_LOG_API_CALLS
- PaUtil_DebugPrint("Pa_StopStream called\n" );
- PaUtil_DebugPrint("\tPaStream* stream: 0x%p\n", stream );
-#endif
-
- if( result == paNoError )
- {
- result = PA_STREAM_INTERFACE(stream)->IsStopped( stream );
- if( result == 0 )
- {
- result = PA_STREAM_INTERFACE(stream)->Stop( stream );
- }
- else if( result == 1 )
- {
- result = paStreamIsStopped;
- }
- }
-
-#ifdef PA_LOG_API_CALLS
- PaUtil_DebugPrint("Pa_StopStream returned:\n" );
- PaUtil_DebugPrint("\tPaError: %d ( %s )\n\n", result, Pa_GetErrorText( result ) );
-#endif
-
- return result;
-}
-
-
-PaError Pa_AbortStream( PaStream *stream )
-{
- PaError result = PaUtil_ValidateStreamPointer( stream );
-
-#ifdef PA_LOG_API_CALLS
- PaUtil_DebugPrint("Pa_AbortStream called:\n" );
- PaUtil_DebugPrint("\tPaStream* stream: 0x%p\n", stream );
-#endif
-
- if( result == paNoError )
- {
- result = PA_STREAM_INTERFACE(stream)->IsStopped( stream );
- if( result == 0 )
- {
- result = PA_STREAM_INTERFACE(stream)->Abort( stream );
- }
- else if( result == 1 )
- {
- result = paStreamIsStopped;
- }
- }
-
-#ifdef PA_LOG_API_CALLS
- PaUtil_DebugPrint("Pa_AbortStream returned:\n" );
- PaUtil_DebugPrint("\tPaError: %d ( %s )\n\n", result, Pa_GetErrorText( result ) );
-#endif
-
- return result;
-}
-
-
-PaError Pa_IsStreamStopped( PaStream *stream )
-{
- PaError result = PaUtil_ValidateStreamPointer( stream );
-
-#ifdef PA_LOG_API_CALLS
- PaUtil_DebugPrint("Pa_IsStreamStopped called:\n" );
- PaUtil_DebugPrint("\tPaStream* stream: 0x%p\n", stream );
-#endif
-
- if( result == paNoError )
- result = PA_STREAM_INTERFACE(stream)->IsStopped( stream );
-
-#ifdef PA_LOG_API_CALLS
- PaUtil_DebugPrint("Pa_IsStreamStopped returned:\n" );
- PaUtil_DebugPrint("\tPaError: %d ( %s )\n\n", result, Pa_GetErrorText( result ) );
-#endif
-
- return result;
-}
-
-
-PaError Pa_IsStreamActive( PaStream *stream )
-{
- PaError result = PaUtil_ValidateStreamPointer( stream );
-
-#ifdef PA_LOG_API_CALLS
- PaUtil_DebugPrint("Pa_IsStreamActive called:\n" );
- PaUtil_DebugPrint("\tPaStream* stream: 0x%p\n", stream );
-#endif
-
- if( result == paNoError )
- result = PA_STREAM_INTERFACE(stream)->IsActive( stream );
-
-#ifdef PA_LOG_API_CALLS
- PaUtil_DebugPrint("Pa_IsStreamActive returned:\n" );
- PaUtil_DebugPrint("\tPaError: %d ( %s )\n\n", result, Pa_GetErrorText( result ) );
-#endif
-
- return result;
-}
-
-
-const PaStreamInfo* Pa_GetStreamInfo( PaStream *stream )
-{
- PaError error = PaUtil_ValidateStreamPointer( stream );
- const PaStreamInfo *result;
-
-#ifdef PA_LOG_API_CALLS
- PaUtil_DebugPrint("Pa_GetStreamInfo called:\n" );
- PaUtil_DebugPrint("\tPaStream* stream: 0x%p\n", stream );
-#endif
-
- if( error != paNoError )
- {
- result = 0;
-
-#ifdef PA_LOG_API_CALLS
- PaUtil_DebugPrint("Pa_GetStreamInfo returned:\n" );
- PaUtil_DebugPrint("\tconst PaStreamInfo*: 0 [PaError error:%d ( %s )]\n\n", result, error, Pa_GetErrorText( error ) );
-#endif
-
- }
- else
- {
- result = &PA_STREAM_REP( stream )->streamInfo;
-
-#ifdef PA_LOG_API_CALLS
- PaUtil_DebugPrint("Pa_GetStreamInfo returned:\n" );
- PaUtil_DebugPrint("\tconst PaStreamInfo*: 0x%p:\n", result );
- PaUtil_DebugPrint("\t{" );
-
- PaUtil_DebugPrint("\t\tint structVersion: %d\n", result->structVersion );
- PaUtil_DebugPrint("\t\tPaTime inputLatency: %f\n", result->inputLatency );
- PaUtil_DebugPrint("\t\tPaTime outputLatency: %f\n", result->outputLatency );
- PaUtil_DebugPrint("\t\tdouble sampleRate: %f\n", result->sampleRate );
- PaUtil_DebugPrint("\t}\n\n" );
-#endif
-
- }
-
- return result;
-}
-
-
-PaTime Pa_GetStreamTime( PaStream *stream )
-{
- PaError error = PaUtil_ValidateStreamPointer( stream );
- PaTime result;
-
-#ifdef PA_LOG_API_CALLS
- PaUtil_DebugPrint("Pa_GetStreamTime called:\n" );
- PaUtil_DebugPrint("\tPaStream* stream: 0x%p\n", stream );
-#endif
-
- if( error != paNoError )
- {
- result = 0;
-
-#ifdef PA_LOG_API_CALLS
- PaUtil_DebugPrint("Pa_GetStreamTime returned:\n" );
- PaUtil_DebugPrint("\tPaTime: 0 [PaError error:%d ( %s )]\n\n", result, error, Pa_GetErrorText( error ) );
-#endif
-
- }
- else
- {
- result = PA_STREAM_INTERFACE(stream)->GetTime( stream );
-
-#ifdef PA_LOG_API_CALLS
- PaUtil_DebugPrint("Pa_GetStreamTime returned:\n" );
- PaUtil_DebugPrint("\tPaTime: %g\n\n", result );
-#endif
-
- }
-
- return result;
-}
-
-
-double Pa_GetStreamCpuLoad( PaStream* stream )
-{
- PaError error = PaUtil_ValidateStreamPointer( stream );
- double result;
-
-#ifdef PA_LOG_API_CALLS
- PaUtil_DebugPrint("Pa_GetStreamCpuLoad called:\n" );
- PaUtil_DebugPrint("\tPaStream* stream: 0x%p\n", stream );
-#endif
-
- if( error != paNoError )
- {
-
- result = 0.0;
-
-#ifdef PA_LOG_API_CALLS
- PaUtil_DebugPrint("Pa_GetStreamCpuLoad returned:\n" );
- PaUtil_DebugPrint("\tdouble: 0.0 [PaError error: %d ( %s )]\n\n", error, Pa_GetErrorText( error ) );
-#endif
-
- }
- else
- {
- result = PA_STREAM_INTERFACE(stream)->GetCpuLoad( stream );
-
-#ifdef PA_LOG_API_CALLS
- PaUtil_DebugPrint("Pa_GetStreamCpuLoad returned:\n" );
- PaUtil_DebugPrint("\tdouble: %g\n\n", result );
-#endif
-
- }
-
- return result;
-}
-
-
-PaError Pa_ReadStream( PaStream* stream,
- void *buffer,
- unsigned long frames )
-{
- PaError result = PaUtil_ValidateStreamPointer( stream );
-
-#ifdef PA_LOG_API_CALLS
- PaUtil_DebugPrint("Pa_ReadStream called:\n" );
- PaUtil_DebugPrint("\tPaStream* stream: 0x%p\n", stream );
-#endif
-
- if( result == paNoError )
- {
- if( frames == 0 )
- {
- result = paInternalError; /** @todo should return a different error code */
- }
- else if( buffer == 0 )
- {
- result = paInternalError; /** @todo should return a different error code */
- }
- else
- {
- result = PA_STREAM_INTERFACE(stream)->IsStopped( stream );
- if( result == 0 )
- {
- result = PA_STREAM_INTERFACE(stream)->Read( stream, buffer, frames );
- }
- else if( result == 1 )
- {
- result = paStreamIsStopped;
- }
- }
- }
-
-#ifdef PA_LOG_API_CALLS
- PaUtil_DebugPrint("Pa_ReadStream returned:\n" );
- PaUtil_DebugPrint("\tPaError: %d ( %s )\n\n", result, Pa_GetErrorText( result ) );
-#endif
-
- return result;
-}
-
-
-PaError Pa_WriteStream( PaStream* stream,
- const void *buffer,
- unsigned long frames )
-{
- PaError result = PaUtil_ValidateStreamPointer( stream );
-
-#ifdef PA_LOG_API_CALLS
- PaUtil_DebugPrint("Pa_WriteStream called:\n" );
- PaUtil_DebugPrint("\tPaStream* stream: 0x%p\n", stream );
-#endif
-
- if( result == paNoError )
- {
- if( frames == 0 )
- {
- result = paInternalError; /** @todo should return a different error code */
- }
- else if( buffer == 0 )
- {
- result = paInternalError; /** @todo should return a different error code */
- }
- else
- {
- result = PA_STREAM_INTERFACE(stream)->IsStopped( stream );
- if( result == 0 )
- {
- result = PA_STREAM_INTERFACE(stream)->Write( stream, buffer, frames );
- }
- else if( result == 1 )
- {
- result = paStreamIsStopped;
- }
- }
- }
-
-#ifdef PA_LOG_API_CALLS
- PaUtil_DebugPrint("Pa_WriteStream returned:\n" );
- PaUtil_DebugPrint("\tPaError: %d ( %s )\n\n", result, Pa_GetErrorText( result ) );
-#endif
-
- return result;
-}
-
-signed long Pa_GetStreamReadAvailable( PaStream* stream )
-{
- PaError error = PaUtil_ValidateStreamPointer( stream );
- signed long result;
-
-#ifdef PA_LOG_API_CALLS
- PaUtil_DebugPrint("Pa_GetStreamReadAvailable called:\n" );
- PaUtil_DebugPrint("\tPaStream* stream: 0x%p\n", stream );
-#endif
-
- if( error != paNoError )
- {
- result = 0;
-
-#ifdef PA_LOG_API_CALLS
- PaUtil_DebugPrint("Pa_GetStreamReadAvailable returned:\n" );
- PaUtil_DebugPrint("\tunsigned long: 0 [ PaError error: %d ( %s ) ]\n\n", error, Pa_GetErrorText( error ) );
-#endif
-
- }
- else
- {
- result = PA_STREAM_INTERFACE(stream)->GetReadAvailable( stream );
-
-#ifdef PA_LOG_API_CALLS
- PaUtil_DebugPrint("Pa_GetStreamReadAvailable returned:\n" );
- PaUtil_DebugPrint("\tPaError: %d ( %s )\n\n", result, Pa_GetErrorText( result ) );
-#endif
-
- }
-
- return result;
-}
-
-
-signed long Pa_GetStreamWriteAvailable( PaStream* stream )
-{
- PaError error = PaUtil_ValidateStreamPointer( stream );
- signed long result;
-
-#ifdef PA_LOG_API_CALLS
- PaUtil_DebugPrint("Pa_GetStreamWriteAvailable called:\n" );
- PaUtil_DebugPrint("\tPaStream* stream: 0x%p\n", stream );
-#endif
-
- if( error != paNoError )
- {
- result = 0;
-
-#ifdef PA_LOG_API_CALLS
- PaUtil_DebugPrint("Pa_GetStreamWriteAvailable returned:\n" );
- PaUtil_DebugPrint("\tunsigned long: 0 [ PaError error: %d ( %s ) ]\n\n", error, Pa_GetErrorText( error ) );
-#endif
-
- }
- else
- {
- result = PA_STREAM_INTERFACE(stream)->GetWriteAvailable( stream );
-
-#ifdef PA_LOG_API_CALLS
- PaUtil_DebugPrint("Pa_GetStreamWriteAvailable returned:\n" );
- PaUtil_DebugPrint("\tPaError: %d ( %s )\n\n", result, Pa_GetErrorText( result ) );
-#endif
-
- }
-
- return result;
-}
-
-
-PaError Pa_GetSampleSize( PaSampleFormat format )
-{
- int result;
-
-#ifdef PA_LOG_API_CALLS
- PaUtil_DebugPrint("Pa_GetSampleSize called:\n" );
- PaUtil_DebugPrint("\tPaSampleFormat format: %d\n", format );
-#endif
-
- switch( format & ~paNonInterleaved )
- {
-
- case paUInt8:
- case paInt8:
- result = 1;
- break;
-
- case paInt16:
- result = 2;
- break;
-
- case paInt24:
- result = 3;
- break;
-
- case paFloat32:
- case paInt32:
- result = 4;
- break;
-
- default:
- result = paSampleFormatNotSupported;
- break;
- }
-
-#ifdef PA_LOG_API_CALLS
- PaUtil_DebugPrint("Pa_GetSampleSize returned:\n" );
- if( result > 0 )
- PaUtil_DebugPrint("\tint: %d\n\n", result );
- else
- PaUtil_DebugPrint("\tPaError: %d ( %s )\n\n", result, Pa_GetErrorText( result ) );
-#endif
-
- return (PaError) result;
-}
-
+/*
+ * $Id: pa_front.c,v 1.1.2.51 2005/02/05 15:52:12 rossbencina Exp $
+ * Portable Audio I/O Library Multi-Host API front end
+ * Validate function parameters and manage multiple host APIs.
+ *
+ * Based on the Open Source API proposed by Ross Bencina
+ * Copyright (c) 1999-2002 Ross Bencina, Phil Burk
+ *
+ * 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.
+ *
+ * Any person wishing to distribute modifications to the Software is
+ * requested to send the modifications to the original developer so that
+ * they can be incorporated into the canonical version.
+ *
+ * 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.
+ */
+
+/* doxygen index page */
+/** @mainpage
+
+PortAudio is an open-source cross-platform †CË library for audio input
+and output. It is designed to simplify the porting of audio applications
+between various platforms, and also to simplify the development of audio
+software in general by hiding the complexities of device interfacing.
+
+See the PortAudio website for further information http://www.portaudio.com/
+
+This documentation pertains to PortAudio V19, API version 2.0 which is
+currently under development. API version 2.0 differs in a number of ways from
+previous versions, please consult the enhancement proposals for further details:
+http://www.portaudio.com/docs/proposals/index.html
+
+This documentation is under construction. Things you might be interested in
+include:
+
+- The PortAudio API 2.0, as documented in portaudio.h
+
+- The <a href="todo.html">TODO List</a>
+
+Feel free to pick an item off TODO list and fix/implement it. You may want to
+enquire about status on the PortAudio mailing list first.
+*/
+
+
+/** @file
+ @brief Implements public PortAudio API, checks some errors, forwards to
+ host API implementations.
+
+ Implements the functions defined in the PortAudio API, checks for
+ some parameter and state inconsistencies and forwards API requests to
+ specific Host API implementations (via the interface declared in
+ pa_hostapi.h), and Streams (via the interface declared in pa_stream.h).
+
+ This file handles initialization and termination of Host API
+ implementations via initializers stored in the paHostApiInitializers
+ global variable.
+
+ Some utility functions declared in pa_util.h are implemented in this file.
+
+ All PortAudio API functions can be conditionally compiled with logging code.
+ To compile with logging, define the PA_LOG_API_CALLS precompiler symbol.
+
+ @todo Consider adding host API specific error text in Pa_GetErrorText() for
+ paUnanticipatedHostError
+
+ @todo Consider adding a new error code for when (inputParameters == NULL)
+ && (outputParameters == NULL)
+
+ @todo review whether Pa_CloseStream() should call the interface's
+ CloseStream function if aborting the stream returns an error code.
+
+ @todo Create new error codes if a NULL buffer pointer, or a
+ zero frame count is passed to Pa_ReadStream or Pa_WriteStream.
+*/
+
+
+#include <stdio.h>
+#include <stdarg.h>
+#include <memory.h>
+#include <string.h>
+#include <assert.h> /* needed by PA_VALIDATE_ENDIANNESS */
+
+#include "portaudio.h"
+#include "pa_util.h"
+#include "pa_endianness.h"
+#include "pa_types.h"
+#include "pa_hostapi.h"
+#include "pa_stream.h"
+
+#include "pa_trace.h"
+
+
+#define PA_VERSION_ 1899
+#define PA_VERSION_TEXT_ "PortAudio V19-devel"
+
+
+
+/* #define PA_LOG_API_CALLS */
+
+/*
+ The basic format for log messages is described below. If you need to
+ add any log messages, please follow this format.
+
+ Function entry (void function):
+
+ "FunctionName called.\n"
+
+ Function entry (non void function):
+
+ "FunctionName called:\n"
+ "\tParam1Type param1: param1Value\n"
+ "\tParam2Type param2: param2Value\n" (etc...)
+
+
+ Function exit (no return value):
+
+ "FunctionName returned.\n"
+
+ Function exit (simple return value):
+
+ "FunctionName returned:\n"
+ "\tReturnType: returnValue\n\n"
+
+ If the return type is an error code, the error text is displayed in ()
+
+ If the return type is not an error code, but has taken a special value
+ because an error occurred, then the reason for the error is shown in []
+
+ If the return type is a struct ptr, the struct is dumped.
+
+ See the code below for examples
+*/
+
+
+int Pa_GetVersion( void )
+{
+ return PA_VERSION_;
+}
+
+
+const char* Pa_GetVersionText( void )
+{
+ return PA_VERSION_TEXT_;
+}
+
+
+
+#define PA_LAST_HOST_ERROR_TEXT_LENGTH_ 1024
+
+static char lastHostErrorText_[ PA_LAST_HOST_ERROR_TEXT_LENGTH_ + 1 ] = {0};
+
+static PaHostErrorInfo lastHostErrorInfo_ = { (PaHostApiTypeId)-1, 0, lastHostErrorText_ };
+
+
+void PaUtil_SetLastHostErrorInfo( PaHostApiTypeId hostApiType, long errorCode,
+ const char *errorText )
+{
+ lastHostErrorInfo_.hostApiType = hostApiType;
+ lastHostErrorInfo_.errorCode = errorCode;
+
+ strncpy( lastHostErrorText_, errorText, PA_LAST_HOST_ERROR_TEXT_LENGTH_ );
+}
+
+
+void PaUtil_DebugPrint( const char *format, ... )
+{
+ va_list ap;
+
+ va_start( ap, format );
+ vfprintf( stderr, format, ap );
+ va_end( ap );
+
+ fflush( stderr );
+}
+
+
+static PaUtilHostApiRepresentation **hostApis_ = 0;
+static int hostApisCount_ = 0;
+static int initializationCount_ = 0;
+static int deviceCount_ = 0;
+
+PaUtilStreamRepresentation *firstOpenStream_ = NULL;
+
+
+#define PA_IS_INITIALISED_ (initializationCount_ != 0)
+
+
+static int CountHostApiInitializers( void )
+{
+ int result = 0;
+
+ while( paHostApiInitializers[ result ] != 0 )
+ ++result;
+ return result;
+}
+
+
+static void TerminateHostApis( void )
+{
+ /* terminate in reverse order from initialization */
+
+ while( hostApisCount_ > 0 )
+ {
+ --hostApisCount_;
+ hostApis_[hostApisCount_]->Terminate( hostApis_[hostApisCount_] );
+ }
+ hostApisCount_ = 0;
+ deviceCount_ = 0;
+
+ if( hostApis_ != 0 )
+ PaUtil_FreeMemory( hostApis_ );
+ hostApis_ = 0;
+}
+
+
+static PaError InitializeHostApis( void )
+{
+ PaError result = paNoError;
+ int i, initializerCount, baseDeviceIndex;
+
+ initializerCount = CountHostApiInitializers();
+
+ hostApis_ = (PaUtilHostApiRepresentation**)PaUtil_AllocateMemory(
+ sizeof(PaUtilHostApiRepresentation*) * initializerCount );
+ if( !hostApis_ )
+ {
+ result = paInsufficientMemory;
+ goto error;
+ }
+
+ hostApisCount_ = 0;
+ deviceCount_ = 0;
+ baseDeviceIndex = 0;
+
+ for( i=0; i< initializerCount; ++i )
+ {
+ hostApis_[hostApisCount_] = NULL;
+ result = paHostApiInitializers[i]( &hostApis_[hostApisCount_], hostApisCount_ );
+ if( result != paNoError )
+ goto error;
+
+ if( hostApis_[hostApisCount_] )
+ {
+
+ hostApis_[hostApisCount_]->privatePaFrontInfo.baseDeviceIndex = baseDeviceIndex;
+
+ if( hostApis_[hostApisCount_]->info.defaultInputDevice != paNoDevice )
+ hostApis_[hostApisCount_]->info.defaultInputDevice += baseDeviceIndex;
+
+ if( hostApis_[hostApisCount_]->info.defaultOutputDevice != paNoDevice )
+ hostApis_[hostApisCount_]->info.defaultOutputDevice += baseDeviceIndex;
+
+ baseDeviceIndex += hostApis_[hostApisCount_]->info.deviceCount;
+ deviceCount_ += hostApis_[hostApisCount_]->info.deviceCount;
+
+ ++hostApisCount_;
+ }
+ }
+
+ return result;
+
+error:
+ TerminateHostApis();
+ return result;
+}
+
+
+/*
+ FindHostApi() finds the index of the host api to which
+ <device> belongs and returns it. if <hostSpecificDeviceIndex> is
+ non-null, the host specific device index is returned in it.
+ returns -1 if <device> is out of range.
+
+*/
+static int FindHostApi( PaDeviceIndex device, int *hostSpecificDeviceIndex )
+{
+ int i=0;
+
+ if( !PA_IS_INITIALISED_ )
+ return -1;
+
+ if( device < 0 )
+ return -1;
+
+ while( i < hostApisCount_
+ && device >= hostApis_[i]->info.deviceCount )
+ {
+
+ device -= hostApis_[i]->info.deviceCount;
+ ++i;
+ }
+
+ if( i >= hostApisCount_ )
+ return -1;
+
+ if( hostSpecificDeviceIndex )
+ *hostSpecificDeviceIndex = device;
+
+ return i;
+}
+
+
+static void AddOpenStream( PaStream* stream )
+{
+ ((PaUtilStreamRepresentation*)stream)->nextOpenStream = firstOpenStream_;
+ firstOpenStream_ = (PaUtilStreamRepresentation*)stream;
+}
+
+
+static void RemoveOpenStream( PaStream* stream )
+{
+ PaUtilStreamRepresentation *previous = NULL;
+ PaUtilStreamRepresentation *current = firstOpenStream_;
+
+ while( current != NULL )
+ {
+ if( ((PaStream*)current) == stream )
+ {
+ if( previous == NULL )
+ {
+ firstOpenStream_ = current->nextOpenStream;
+ }
+ else
+ {
+ previous->nextOpenStream = current->nextOpenStream;
+ }
+ return;
+ }
+ else
+ {
+ previous = current;
+ current = current->nextOpenStream;
+ }
+ }
+}
+
+
+static void CloseOpenStreams( void )
+{
+ /* we call Pa_CloseStream() here to ensure that the same destruction
+ logic is used for automatically closed streams */
+
+ while( firstOpenStream_ != NULL )
+ Pa_CloseStream( firstOpenStream_ );
+}
+
+
+PaError Pa_Initialize( void )
+{
+ PaError result;
+
+#ifdef PA_LOG_API_CALLS
+ PaUtil_DebugPrint( "Pa_Initialize called.\n" );
+#endif
+
+ if( PA_IS_INITIALISED_ )
+ {
+ ++initializationCount_;
+ result = paNoError;
+ }
+ else
+ {
+ PA_VALIDATE_TYPE_SIZES;
+ PA_VALIDATE_ENDIANNESS;
+
+ PaUtil_InitializeClock();
+ PaUtil_ResetTraceMessages();
+
+ result = InitializeHostApis();
+ if( result == paNoError )
+ ++initializationCount_;
+ }
+
+#ifdef PA_LOG_API_CALLS
+ PaUtil_DebugPrint( "Pa_Initialize returned:\n" );
+ PaUtil_DebugPrint( "\tPaError: %d ( %s )\n\n", result, Pa_GetErrorText( result ) );
+#endif
+
+ return result;
+}
+
+
+PaError Pa_Terminate( void )
+{
+ PaError result;
+
+#ifdef PA_LOG_API_CALLS
+ PaUtil_DebugPrint("Pa_Terminate called.\n" );
+#endif
+
+ if( PA_IS_INITIALISED_ )
+ {
+ if( --initializationCount_ == 0 )
+ {
+ CloseOpenStreams();
+
+ TerminateHostApis();
+
+ PaUtil_DumpTraceMessages();
+ }
+ result = paNoError;
+ }
+ else
+ {
+ result= paNotInitialized;
+ }
+
+#ifdef PA_LOG_API_CALLS
+ PaUtil_DebugPrint("Pa_Terminate returned:\n" );
+ PaUtil_DebugPrint("\tPaError: %d ( %s )\n\n", result, Pa_GetErrorText( result ) );
+#endif
+
+ return result;
+}
+
+
+const PaHostErrorInfo* Pa_GetLastHostErrorInfo( void )
+{
+ return &lastHostErrorInfo_;
+}
+
+
+const char *Pa_GetErrorText( PaError errorCode )
+{
+ const char *result;
+
+ switch( errorCode )
+ {
+ case paNoError: result = "Success"; break;
+ case paNotInitialized: result = "PortAudio not initialized"; break;
+ /** @todo could catenate the last host error text to result in the case of paUnanticipatedHostError */
+ case paUnanticipatedHostError: result = "Unanticipated host error"; break;
+ case paInvalidChannelCount: result = "Invalid number of channels"; break;
+ case paInvalidSampleRate: result = "Invalid sample rate"; break;
+ case paInvalidDevice: result = "Invalid device"; break;
+ case paInvalidFlag: result = "Invalid flag"; break;
+ case paSampleFormatNotSupported: result = "Sample format not supported"; break;
+ case paBadIODeviceCombination: result = "Illegal combination of I/O devices"; break;
+ case paInsufficientMemory: result = "Insufficient memory"; break;
+ case paBufferTooBig: result = "Buffer too big"; break;
+ case paBufferTooSmall: result = "Buffer too small"; break;
+ case paNullCallback: result = "No callback routine specified"; break;
+ case paBadStreamPtr: result = "Invalid stream pointer"; break;
+ case paTimedOut: result = "Wait timed out"; break;
+ case paInternalError: result = "Internal PortAudio error"; break;
+ case paDeviceUnavailable: result = "Device unavailable"; break;
+ case paIncompatibleHostApiSpecificStreamInfo: result = "Incompatible host API specific stream info"; break;
+ case paStreamIsStopped: result = "Stream is stopped"; break;
+ case paStreamIsNotStopped: result = "Stream is not stopped"; break;
+ case paInputOverflowed: result = "Input overflowed"; break;
+ case paOutputUnderflowed: result = "Output underflowed"; break;
+ case paHostApiNotFound: result = "Host API not found"; break;
+ case paInvalidHostApi: result = "Invalid host API"; break;
+ case paCanNotReadFromACallbackStream: result = "Can't read from a callback stream"; break;
+ case paCanNotWriteToACallbackStream: result = "Can't write to a callback stream"; break;
+ case paCanNotReadFromAnOutputOnlyStream: result = "Can't read from an output only stream"; break;
+ case paCanNotWriteToAnInputOnlyStream: result = "Can't write to an input only stream"; break;
+ default: result = "Illegal error number"; break;
+ }
+ return result;
+}
+
+
+PaHostApiIndex Pa_HostApiTypeIdToHostApiIndex( PaHostApiTypeId type )
+{
+ PaHostApiIndex result;
+ int i;
+
+#ifdef PA_LOG_API_CALLS
+ PaUtil_DebugPrint("Pa_HostApiTypeIdToHostApiIndex called:\n" );
+ PaUtil_DebugPrint("\tPaHostApiTypeId type: %d\n", type );
+#endif
+
+ if( !PA_IS_INITIALISED_ )
+ {
+ result = paNotInitialized;
+ }
+ else
+ {
+ result = paHostApiNotFound;
+
+ for( i=0; i < hostApisCount_; ++i )
+ {
+ if( hostApis_[i]->info.type == type )
+ {
+ result = i;
+ break;
+ }
+ }
+ }
+
+#ifdef PA_LOG_API_CALLS
+ PaUtil_DebugPrint("Pa_HostApiTypeIdToHostApiIndex returned:\n" );
+ if( result < 0 )
+ PaUtil_DebugPrint("\tPaError: %d ( %s )\n\n", result, Pa_GetErrorText( result ) );
+ else
+ PaUtil_DebugPrint("\tPaHostApiIndex: %d\n\n", result );
+#endif
+
+ return result;
+}
+
+
+PaError PaUtil_GetHostApiRepresentation( struct PaUtilHostApiRepresentation **hostApi,
+ PaHostApiTypeId type )
+{
+ PaError result;
+ int i;
+
+ if( !PA_IS_INITIALISED_ )
+ {
+ result = paNotInitialized;
+ }
+ else
+ {
+ result = paHostApiNotFound;
+
+ for( i=0; i < hostApisCount_; ++i )
+ {
+ if( hostApis_[i]->info.type == type )
+ {
+ *hostApi = hostApis_[i];
+ result = paNoError;
+ break;
+ }
+ }
+ }
+
+ return result;
+}
+
+
+PaError PaUtil_DeviceIndexToHostApiDeviceIndex(
+ PaDeviceIndex *hostApiDevice, PaDeviceIndex device, struct PaUtilHostApiRepresentation *hostApi )
+{
+ PaError result;
+ PaDeviceIndex x;
+
+ x = device - hostApi->privatePaFrontInfo.baseDeviceIndex;
+
+ if( x < 0 || x >= hostApi->info.deviceCount )
+ {
+ result = paInvalidDevice;
+ }
+ else
+ {
+ *hostApiDevice = x;
+ result = paNoError;
+ }
+
+ return result;
+}
+
+
+PaHostApiIndex Pa_GetHostApiCount( void )
+{
+ int result;
+
+#ifdef PA_LOG_API_CALLS
+ PaUtil_DebugPrint("Pa_GetHostApiCount called.\n" );
+#endif
+
+ if( !PA_IS_INITIALISED_ )
+ {
+ result = paNotInitialized;
+ }
+ else
+ {
+ result = hostApisCount_;
+ }
+
+#ifdef PA_LOG_API_CALLS
+ PaUtil_DebugPrint("Pa_GetHostApiCount returned:\n" );
+ if( result < 0 )
+ PaUtil_DebugPrint("\tPaError: %d ( %s )\n\n", result, Pa_GetErrorText( result ) );
+ else
+ PaUtil_DebugPrint("\tPaHostApiIndex %d\n\n", result );
+#endif
+
+ return result;
+}
+
+
+PaHostApiIndex Pa_GetDefaultHostApi( void )
+{
+ int result;
+
+#ifdef PA_LOG_API_CALLS
+ PaUtil_DebugPrint("Pa_GetDefaultHostApi called.\n" );
+#endif
+
+ if( !PA_IS_INITIALISED_ )
+ {
+ result = paNotInitialized;
+ }
+ else
+ {
+ result = paDefaultHostApiIndex;
+
+ /* internal consistency check: make sure that the default host api
+ index is within range */
+
+ if( result < 0 || result >= hostApisCount_ )
+ {
+ result = paInternalError;
+ }
+ }
+
+#ifdef PA_LOG_API_CALLS
+ PaUtil_DebugPrint("Pa_GetDefaultHostApi returned:\n" );
+ if( result < 0 )
+ PaUtil_DebugPrint("\tPaError: %d ( %s )\n\n", result, Pa_GetErrorText( result ) );
+ else
+ PaUtil_DebugPrint("\tPaHostApiIndex %d\n\n", result );
+#endif
+
+ return result;
+}
+
+
+const PaHostApiInfo* Pa_GetHostApiInfo( PaHostApiIndex hostApi )
+{
+ PaHostApiInfo *info;
+
+#ifdef PA_LOG_API_CALLS
+ PaUtil_DebugPrint("Pa_GetHostApiInfo called:\n" );
+ PaUtil_DebugPrint("\tPaHostApiIndex hostApi: %d\n", hostApi );
+#endif
+
+ if( !PA_IS_INITIALISED_ )
+ {
+ info = NULL;
+
+#ifdef PA_LOG_API_CALLS
+ PaUtil_DebugPrint("Pa_GetHostApiInfo returned:\n" );
+ PaUtil_DebugPrint("\tPaHostApiInfo*: NULL [ PortAudio not initialized ]\n\n" );
+#endif
+
+ }
+ else if( hostApi < 0 || hostApi >= hostApisCount_ )
+ {
+ info = NULL;
+
+#ifdef PA_LOG_API_CALLS
+ PaUtil_DebugPrint("Pa_GetHostApiInfo returned:\n" );
+ PaUtil_DebugPrint("\tPaHostApiInfo*: NULL [ hostApi out of range ]\n\n" );
+#endif
+
+ }
+ else
+ {
+ info = &hostApis_[hostApi]->info;
+
+#ifdef PA_LOG_API_CALLS
+ PaUtil_DebugPrint("Pa_GetHostApiInfo returned:\n" );
+ PaUtil_DebugPrint("\tPaHostApiInfo*: 0x%p\n", info );
+ PaUtil_DebugPrint("\t{" );
+ PaUtil_DebugPrint("\t\tint structVersion: %d\n", info->structVersion );
+ PaUtil_DebugPrint("\t\tPaHostApiTypeId type: %d\n", info->type );
+ PaUtil_DebugPrint("\t\tconst char *name: %s\n\n", info->name );
+ PaUtil_DebugPrint("\t}\n\n" );
+#endif
+
+ }
+
+ return info;
+}
+
+
+PaDeviceIndex Pa_HostApiDeviceIndexToDeviceIndex( PaHostApiIndex hostApi, int hostApiDeviceIndex )
+{
+ PaDeviceIndex result;
+
+#ifdef PA_LOG_API_CALLS
+ PaUtil_DebugPrint("Pa_HostApiDeviceIndexToPaDeviceIndex called:\n" );
+ PaUtil_DebugPrint("\tPaHostApiIndex hostApi: %d\n", hostApi );
+ PaUtil_DebugPrint("\tint hostApiDeviceIndex: %d\n", hostApiDeviceIndex );
+#endif
+
+ if( !PA_IS_INITIALISED_ )
+ {
+ result = paNotInitialized;
+ }
+ else
+ {
+ if( hostApi < 0 || hostApi >= hostApisCount_ )
+ {
+ result = paInvalidHostApi;
+ }
+ else
+ {
+ if( hostApiDeviceIndex < 0 ||
+ hostApiDeviceIndex >= hostApis_[hostApi]->info.deviceCount )
+ {
+ result = paInvalidDevice;
+ }
+ else
+ {
+ result = hostApis_[hostApi]->privatePaFrontInfo.baseDeviceIndex + hostApiDeviceIndex;
+ }
+ }
+ }
+
+#ifdef PA_LOG_API_CALLS
+ PaUtil_DebugPrint("Pa_HostApiDeviceIndexToPaDeviceIndex returned:\n" );
+ if( result < 0 )
+ PaUtil_DebugPrint("\tPaError: %d ( %s )\n\n", result, Pa_GetErrorText( result ) );
+ else
+ PaUtil_DebugPrint("\tPaDeviceIndex: %d\n\n", result );
+#endif
+
+ return result;
+}
+
+
+PaDeviceIndex Pa_GetDeviceCount( void )
+{
+ PaDeviceIndex result;
+
+#ifdef PA_LOG_API_CALLS
+ PaUtil_DebugPrint("Pa_GetDeviceCount called.\n" );
+#endif
+
+ if( !PA_IS_INITIALISED_ )
+ {
+ result = paNotInitialized;
+ }
+ else
+ {
+ result = deviceCount_;
+ }
+
+#ifdef PA_LOG_API_CALLS
+ PaUtil_DebugPrint("Pa_GetDeviceCount returned:\n" );
+ if( result < 0 )
+ PaUtil_DebugPrint("\tPaError: %d ( %s )\n\n", result, Pa_GetErrorText( result ) );
+ else
+ PaUtil_DebugPrint("\tPaDeviceIndex: %d\n\n", result );
+#endif
+
+ return result;
+}
+
+
+PaDeviceIndex Pa_GetDefaultInputDevice( void )
+{
+ PaHostApiIndex hostApi;
+ PaDeviceIndex result;
+
+#ifdef PA_LOG_API_CALLS
+ PaUtil_DebugPrint("Pa_GetDefaultInputDevice called.\n" );
+#endif
+
+ hostApi = Pa_GetDefaultHostApi();
+ if( hostApi < 0 )
+ {
+ result = paNoDevice;
+ }
+ else
+ {
+ result = hostApis_[hostApi]->info.defaultInputDevice;
+ }
+
+#ifdef PA_LOG_API_CALLS
+ PaUtil_DebugPrint("Pa_GetDefaultInputDevice returned:\n" );
+ PaUtil_DebugPrint("\tPaDeviceIndex: %d\n\n", result );
+#endif
+
+ return result;
+}
+
+
+PaDeviceIndex Pa_GetDefaultOutputDevice( void )
+{
+ PaHostApiIndex hostApi;
+ PaDeviceIndex result;
+
+#ifdef PA_LOG_API_CALLS
+ PaUtil_DebugPrint("Pa_GetDefaultOutputDevice called.\n" );
+#endif
+
+ hostApi = Pa_GetDefaultHostApi();
+ if( hostApi < 0 )
+ {
+ result = paNoDevice;
+ }
+ else
+ {
+ result = hostApis_[hostApi]->info.defaultOutputDevice;
+ }
+
+#ifdef PA_LOG_API_CALLS
+ PaUtil_DebugPrint("Pa_GetDefaultOutputDevice returned:\n" );
+ PaUtil_DebugPrint("\tPaDeviceIndex: %d\n\n", result );
+#endif
+
+ return result;
+}
+
+
+const PaDeviceInfo* Pa_GetDeviceInfo( PaDeviceIndex device )
+{
+ int hostSpecificDeviceIndex;
+ int hostApiIndex = FindHostApi( device, &hostSpecificDeviceIndex );
+ PaDeviceInfo *result;
+
+
+#ifdef PA_LOG_API_CALLS
+ PaUtil_DebugPrint("Pa_GetDeviceInfo called:\n" );
+ PaUtil_DebugPrint("\tPaDeviceIndex device: %d\n", device );
+#endif
+
+ if( hostApiIndex < 0 )
+ {
+ result = NULL;
+
+#ifdef PA_LOG_API_CALLS
+ PaUtil_DebugPrint("Pa_GetDeviceInfo returned:\n" );
+ PaUtil_DebugPrint("\tPaDeviceInfo* NULL [ invalid device index ]\n\n" );
+#endif
+
+ }
+ else
+ {
+ result = hostApis_[hostApiIndex]->deviceInfos[ hostSpecificDeviceIndex ];
+
+#ifdef PA_LOG_API_CALLS
+ PaUtil_DebugPrint("Pa_GetDeviceInfo returned:\n" );
+ PaUtil_DebugPrint("\tPaDeviceInfo*: 0x%p:\n", result );
+ PaUtil_DebugPrint("\t{\n" );
+
+ PaUtil_DebugPrint("\t\tint structVersion: %d\n", result->structVersion );
+ PaUtil_DebugPrint("\t\tconst char *name: %s\n", result->name );
+ PaUtil_DebugPrint("\t\tPaHostApiIndex hostApi: %d\n", result->hostApi );
+ PaUtil_DebugPrint("\t\tint maxInputChannels: %d\n", result->maxInputChannels );
+ PaUtil_DebugPrint("\t\tint maxOutputChannels: %d\n", result->maxOutputChannels );
+ PaUtil_DebugPrint("\t}\n\n" );
+#endif
+
+ }
+
+ return result;
+}
+
+
+/*
+ SampleFormatIsValid() returns 1 if sampleFormat is a sample format
+ defined in portaudio.h, or 0 otherwise.
+*/
+static int SampleFormatIsValid( PaSampleFormat format )
+{
+ switch( format & ~paNonInterleaved )
+ {
+ case paFloat32: return 1;
+ case paInt16: return 1;
+ case paInt32: return 1;
+ case paInt24: return 1;
+ case paInt8: return 1;
+ case paUInt8: return 1;
+ case paCustomFormat: return 1;
+ default: return 0;
+ }
+}
+
+/*
+ NOTE: make sure this validation list is kept syncronised with the one in
+ pa_hostapi.h
+
+ ValidateOpenStreamParameters() checks that parameters to Pa_OpenStream()
+ conform to the expected values as described below. This function is
+ also designed to be used with the proposed Pa_IsFormatSupported() function.
+
+ There are basically two types of validation that could be performed:
+ Generic conformance validation, and device capability mismatch
+ validation. This function performs only generic conformance validation.
+ Validation that would require knowledge of device capabilities is
+ not performed because of potentially complex relationships between
+ combinations of parameters - for example, even if the sampleRate
+ seems ok, it might not be for a duplex stream - we have no way of
+ checking this in an API-neutral way, so we don't try.
+
+ On success the function returns PaNoError and fills in hostApi,
+ hostApiInputDeviceID, and hostApiOutputDeviceID fields. On failure
+ the function returns an error code indicating the first encountered
+ parameter error.
+
+
+ If ValidateOpenStreamParameters() returns paNoError, the following
+ assertions are guaranteed to be true.
+
+ - at least one of inputParameters & outputParmeters is valid (not NULL)
+
+ - if inputParameters & outputParmeters are both valid, that
+ inputParameters->device & outputParmeters->device both use the same host api
+
+ PaDeviceIndex inputParameters->device
+ - is within range (0 to Pa_GetDeviceCount-1) Or:
+ - is paUseHostApiSpecificDeviceSpecification and
+ inputParameters->hostApiSpecificStreamInfo is non-NULL and refers
+ to a valid host api
+
+ int inputParameters->channelCount
+ - if inputParameters->device is not paUseHostApiSpecificDeviceSpecification, channelCount is > 0
+ - upper bound is NOT validated against device capabilities
+
+ PaSampleFormat inputParameters->sampleFormat
+ - is one of the sample formats defined in portaudio.h
+
+ void *inputParameters->hostApiSpecificStreamInfo
+ - if supplied its hostApi field matches the input device's host Api
+
+ PaDeviceIndex outputParmeters->device
+ - is within range (0 to Pa_GetDeviceCount-1)
+
+ int outputParmeters->channelCount
+ - if inputDevice is valid, channelCount is > 0
+ - upper bound is NOT validated against device capabilities
+
+ PaSampleFormat outputParmeters->sampleFormat
+ - is one of the sample formats defined in portaudio.h
+
+ void *outputParmeters->hostApiSpecificStreamInfo
+ - if supplied its hostApi field matches the output device's host Api
+
+ double sampleRate
+ - is not an 'absurd' rate (less than 1000. or greater than 200000.)
+ - sampleRate is NOT validated against device capabilities
+
+ PaStreamFlags streamFlags
+ - unused platform neutral flags are zero
+ - paNeverDropInput is only used for full-duplex callback streams with
+ variable buffer size (paFramesPerBufferUnspecified)
+*/
+static PaError ValidateOpenStreamParameters(
+ const PaStreamParameters *inputParameters,
+ const PaStreamParameters *outputParameters,
+ double sampleRate,
+ unsigned long framesPerBuffer,
+ PaStreamFlags streamFlags,
+ PaStreamCallback *streamCallback,
+ PaUtilHostApiRepresentation **hostApi,
+ PaDeviceIndex *hostApiInputDevice,
+ PaDeviceIndex *hostApiOutputDevice )
+{
+ int inputHostApiIndex = -1, /* Surpress uninitialised var warnings: compiler does */
+ outputHostApiIndex = -1; /* not see that if inputParameters and outputParame- */
+ /* ters are both nonzero, these indices are set. */
+
+ if( (inputParameters == NULL) && (outputParameters == NULL) )
+ {
+ return paInvalidDevice; /** @todo should be a new error code "invalid device parameters" or something */
+ }
+ else
+ {
+ if( inputParameters == NULL )
+ {
+ *hostApiInputDevice = paNoDevice;
+ }
+ else if( inputParameters->device == paUseHostApiSpecificDeviceSpecification )
+ {
+ if( inputParameters->hostApiSpecificStreamInfo )
+ {
+ inputHostApiIndex = Pa_HostApiTypeIdToHostApiIndex(
+ ((PaUtilHostApiSpecificStreamInfoHeader*)inputParameters->hostApiSpecificStreamInfo)->hostApiType );
+
+ if( inputHostApiIndex != -1 )
+ {
+ *hostApiInputDevice = paUseHostApiSpecificDeviceSpecification;
+ *hostApi = hostApis_[inputHostApiIndex];
+ }
+ else
+ {
+ return paInvalidDevice;
+ }
+ }
+ else
+ {
+ return paInvalidDevice;
+ }
+ }
+ else
+ {
+ if( inputParameters->device < 0 || inputParameters->device >= deviceCount_ )
+ return paInvalidDevice;
+
+ inputHostApiIndex = FindHostApi( inputParameters->device, hostApiInputDevice );
+ if( inputHostApiIndex < 0 )
+ return paInternalError;
+
+ *hostApi = hostApis_[inputHostApiIndex];
+
+ if( inputParameters->channelCount <= 0 )
+ return paInvalidChannelCount;
+
+ if( !SampleFormatIsValid( inputParameters->sampleFormat ) )
+ return paSampleFormatNotSupported;
+
+ if( inputParameters->hostApiSpecificStreamInfo != NULL )
+ {
+ if( ((PaUtilHostApiSpecificStreamInfoHeader*)inputParameters->hostApiSpecificStreamInfo)->hostApiType
+ != (*hostApi)->info.type )
+ return paIncompatibleHostApiSpecificStreamInfo;
+ }
+ }
+
+ if( outputParameters == NULL )
+ {
+ *hostApiOutputDevice = paNoDevice;
+ }
+ else if( outputParameters->device == paUseHostApiSpecificDeviceSpecification )
+ {
+ if( outputParameters->hostApiSpecificStreamInfo )
+ {
+ outputHostApiIndex = Pa_HostApiTypeIdToHostApiIndex(
+ ((PaUtilHostApiSpecificStreamInfoHeader*)outputParameters->hostApiSpecificStreamInfo)->hostApiType );
+
+ if( outputHostApiIndex != -1 )
+ {
+ *hostApiOutputDevice = paUseHostApiSpecificDeviceSpecification;
+ *hostApi = hostApis_[outputHostApiIndex];
+ }
+ else
+ {
+ return paInvalidDevice;
+ }
+ }
+ else
+ {
+ return paInvalidDevice;
+ }
+ }
+ else
+ {
+ if( outputParameters->device < 0 || outputParameters->device >= deviceCount_ )
+ return paInvalidDevice;
+
+ outputHostApiIndex = FindHostApi( outputParameters->device, hostApiOutputDevice );
+ if( outputHostApiIndex < 0 )
+ return paInternalError;
+
+ *hostApi = hostApis_[outputHostApiIndex];
+
+ if( outputParameters->channelCount <= 0 )
+ return paInvalidChannelCount;
+
+ if( !SampleFormatIsValid( outputParameters->sampleFormat ) )
+ return paSampleFormatNotSupported;
+
+ if( outputParameters->hostApiSpecificStreamInfo != NULL )
+ {
+ if( ((PaUtilHostApiSpecificStreamInfoHeader*)outputParameters->hostApiSpecificStreamInfo)->hostApiType
+ != (*hostApi)->info.type )
+ return paIncompatibleHostApiSpecificStreamInfo;
+ }
+ }
+
+ if( (inputParameters != NULL) && (outputParameters != NULL) )
+ {
+ /* ensure that both devices use the same API */
+ if( inputHostApiIndex != outputHostApiIndex )
+ return paBadIODeviceCombination;
+ }
+ }
+
+
+ /* Check for absurd sample rates. */
+ if( (sampleRate < 1000.0) || (sampleRate > 200000.0) )
+ return paInvalidSampleRate;
+
+ if( ((streamFlags & ~paPlatformSpecificFlags) & ~(paClipOff | paDitherOff | paNeverDropInput | paPrimeOutputBuffersUsingStreamCallback ) ) != 0 )
+ return paInvalidFlag;
+
+ if( streamFlags & paNeverDropInput )
+ {
+ /* must be a callback stream */
+ if( !streamCallback )
+ return paInvalidFlag;
+
+ /* must be a full duplex stream */
+ if( (inputParameters == NULL) || (outputParameters == NULL) )
+ return paInvalidFlag;
+
+ /* must use paFramesPerBufferUnspecified */
+ if( framesPerBuffer != paFramesPerBufferUnspecified )
+ return paInvalidFlag;
+ }
+
+ return paNoError;
+}
+
+
+PaError Pa_IsFormatSupported( const PaStreamParameters *inputParameters,
+ const PaStreamParameters *outputParameters,
+ double sampleRate )
+{
+ PaError result;
+ PaUtilHostApiRepresentation *hostApi;
+ PaDeviceIndex hostApiInputDevice, hostApiOutputDevice;
+ PaStreamParameters hostApiInputParameters, hostApiOutputParameters;
+ PaStreamParameters *hostApiInputParametersPtr, *hostApiOutputParametersPtr;
+
+
+#ifdef PA_LOG_API_CALLS
+ PaUtil_DebugPrint("Pa_IsFormatSupported called:\n" );
+
+ if( inputParameters == NULL ){
+ PaUtil_DebugPrint("\tPaStreamParameters *inputParameters: NULL\n" );
+ }else{
+ PaUtil_DebugPrint("\tPaStreamParameters *inputParameters: 0x%p\n", inputParameters );
+ PaUtil_DebugPrint("\tPaDeviceIndex inputParameters->device: %d\n", inputParameters->device );
+ PaUtil_DebugPrint("\tint inputParameters->channelCount: %d\n", inputParameters->channelCount );
+ PaUtil_DebugPrint("\tPaSampleFormat inputParameters->sampleFormat: %d\n", inputParameters->sampleFormat );
+ PaUtil_DebugPrint("\tPaTime inputParameters->suggestedLatency: %f\n", inputParameters->suggestedLatency );
+ PaUtil_DebugPrint("\tvoid *inputParameters->hostApiSpecificStreamInfo: 0x%p\n", inputParameters->hostApiSpecificStreamInfo );
+ }
+
+ if( outputParameters == NULL ){
+ PaUtil_DebugPrint("\tPaStreamParameters *outputParameters: NULL\n" );
+ }else{
+ PaUtil_DebugPrint("\tPaStreamParameters *outputParameters: 0x%p\n", outputParameters );
+ PaUtil_DebugPrint("\tPaDeviceIndex outputParameters->device: %d\n", outputParameters->device );
+ PaUtil_DebugPrint("\tint outputParameters->channelCount: %d\n", outputParameters->channelCount );
+ PaUtil_DebugPrint("\tPaSampleFormat outputParameters->sampleFormat: %d\n", outputParameters->sampleFormat );
+ PaUtil_DebugPrint("\tPaTime outputParameters->suggestedLatency: %f\n", outputParameters->suggestedLatency );
+ PaUtil_DebugPrint("\tvoid *outputParameters->hostApiSpecificStreamInfo: 0x%p\n", outputParameters->hostApiSpecificStreamInfo );
+ }
+
+ PaUtil_DebugPrint("\tdouble sampleRate: %g\n", sampleRate );
+#endif
+
+ if( !PA_IS_INITIALISED_ )
+ {
+ result = paNotInitialized;
+
+#ifdef PA_LOG_API_CALLS
+ PaUtil_DebugPrint("Pa_IsFormatSupported returned:\n" );
+ PaUtil_DebugPrint("\tPaError: %d ( %s )\n\n", result, Pa_GetErrorText( result ) );
+#endif
+ return result;
+ }
+
+ result = ValidateOpenStreamParameters( inputParameters,
+ outputParameters,
+ sampleRate, 0, paNoFlag, 0,
+ &hostApi,
+ &hostApiInputDevice,
+ &hostApiOutputDevice );
+ if( result != paNoError )
+ {
+#ifdef PA_LOG_API_CALLS
+ PaUtil_DebugPrint("Pa_IsFormatSupported returned:\n" );
+ PaUtil_DebugPrint("\tPaError: %d ( %s )\n\n", result, Pa_GetErrorText( result ) );
+#endif
+ return result;
+ }
+
+
+ if( inputParameters )
+ {
+ hostApiInputParameters.device = hostApiInputDevice;
+ hostApiInputParameters.channelCount = inputParameters->channelCount;
+ hostApiInputParameters.sampleFormat = inputParameters->sampleFormat;
+ hostApiInputParameters.suggestedLatency = inputParameters->suggestedLatency;
+ hostApiInputParameters.hostApiSpecificStreamInfo = inputParameters->hostApiSpecificStreamInfo;
+ hostApiInputParametersPtr = &hostApiInputParameters;
+ }
+ else
+ {
+ hostApiInputParametersPtr = NULL;
+ }
+
+ if( outputParameters )
+ {
+ hostApiOutputParameters.device = hostApiOutputDevice;
+ hostApiOutputParameters.channelCount = outputParameters->channelCount;
+ hostApiOutputParameters.sampleFormat = outputParameters->sampleFormat;
+ hostApiOutputParameters.suggestedLatency = outputParameters->suggestedLatency;
+ hostApiOutputParameters.hostApiSpecificStreamInfo = outputParameters->hostApiSpecificStreamInfo;
+ hostApiOutputParametersPtr = &hostApiOutputParameters;
+ }
+ else
+ {
+ hostApiOutputParametersPtr = NULL;
+ }
+
+ result = hostApi->IsFormatSupported( hostApi,
+ hostApiInputParametersPtr, hostApiOutputParametersPtr,
+ sampleRate );
+
+#ifdef PA_LOG_API_CALLS
+ PaUtil_DebugPrint("Pa_OpenStream returned:\n" );
+ if( result == paFormatIsSupported )
+ PaUtil_DebugPrint("\tPaError: 0 [ paFormatIsSupported ]\n\n" );
+ else
+ PaUtil_DebugPrint("\tPaError: %d ( %s )\n\n", result, Pa_GetErrorText( result ) );
+#endif
+
+ return result;
+}
+
+
+PaError Pa_OpenStream( PaStream** stream,
+ const PaStreamParameters *inputParameters,
+ const PaStreamParameters *outputParameters,
+ double sampleRate,
+ unsigned long framesPerBuffer,
+ PaStreamFlags streamFlags,
+ PaStreamCallback *streamCallback,
+ void *userData )
+{
+ PaError result;
+ PaUtilHostApiRepresentation *hostApi;
+ PaDeviceIndex hostApiInputDevice, hostApiOutputDevice;
+ PaStreamParameters hostApiInputParameters, hostApiOutputParameters;
+ PaStreamParameters *hostApiInputParametersPtr, *hostApiOutputParametersPtr;
+
+
+#ifdef PA_LOG_API_CALLS
+ PaUtil_DebugPrint("Pa_OpenStream called:\n" );
+ PaUtil_DebugPrint("\tPaStream** stream: 0x%p\n", stream );
+
+ if( inputParameters == NULL ){
+ PaUtil_DebugPrint("\tPaStreamParameters *inputParameters: NULL\n" );
+ }else{
+ PaUtil_DebugPrint("\tPaStreamParameters *inputParameters: 0x%p\n", inputParameters );
+ PaUtil_DebugPrint("\tPaDeviceIndex inputParameters->device: %d\n", inputParameters->device );
+ PaUtil_DebugPrint("\tint inputParameters->channelCount: %d\n", inputParameters->channelCount );
+ PaUtil_DebugPrint("\tPaSampleFormat inputParameters->sampleFormat: %d\n", inputParameters->sampleFormat );
+ PaUtil_DebugPrint("\tPaTime inputParameters->suggestedLatency: %f\n", inputParameters->suggestedLatency );
+ PaUtil_DebugPrint("\tvoid *inputParameters->hostApiSpecificStreamInfo: 0x%p\n", inputParameters->hostApiSpecificStreamInfo );
+ }
+
+ if( outputParameters == NULL ){
+ PaUtil_DebugPrint("\tPaStreamParameters *outputParameters: NULL\n" );
+ }else{
+ PaUtil_DebugPrint("\tPaStreamParameters *outputParameters: 0x%p\n", outputParameters );
+ PaUtil_DebugPrint("\tPaDeviceIndex outputParameters->device: %d\n", outputParameters->device );
+ PaUtil_DebugPrint("\tint outputParameters->channelCount: %d\n", outputParameters->channelCount );
+ PaUtil_DebugPrint("\tPaSampleFormat outputParameters->sampleFormat: %d\n", outputParameters->sampleFormat );
+ PaUtil_DebugPrint("\tPaTime outputParameters->suggestedLatency: %f\n", outputParameters->suggestedLatency );
+ PaUtil_DebugPrint("\tvoid *outputParameters->hostApiSpecificStreamInfo: 0x%p\n", outputParameters->hostApiSpecificStreamInfo );
+ }
+
+ PaUtil_DebugPrint("\tdouble sampleRate: %g\n", sampleRate );
+ PaUtil_DebugPrint("\tunsigned long framesPerBuffer: %d\n", framesPerBuffer );
+ PaUtil_DebugPrint("\tPaStreamFlags streamFlags: 0x%x\n", streamFlags );
+ PaUtil_DebugPrint("\tPaStreamCallback *streamCallback: 0x%p\n", streamCallback );
+ PaUtil_DebugPrint("\tvoid *userData: 0x%p\n", userData );
+#endif
+
+ if( !PA_IS_INITIALISED_ )
+ {
+ result = paNotInitialized;
+
+#ifdef PA_LOG_API_CALLS
+ PaUtil_DebugPrint("Pa_OpenStream returned:\n" );
+ PaUtil_DebugPrint("\t*(PaStream** stream): undefined\n" );
+ PaUtil_DebugPrint("\tPaError: %d ( %s )\n\n", result, Pa_GetErrorText( result ) );
+#endif
+ return result;
+ }
+
+ /* Check for parameter errors.
+ NOTE: make sure this validation list is kept syncronised with the one
+ in pa_hostapi.h
+ */
+
+ if( stream == NULL )
+ {
+ result = paBadStreamPtr;
+
+#ifdef PA_LOG_API_CALLS
+ PaUtil_DebugPrint("Pa_OpenStream returned:\n" );
+ PaUtil_DebugPrint("\t*(PaStream** stream): undefined\n" );
+ PaUtil_DebugPrint("\tPaError: %d ( %s )\n\n", result, Pa_GetErrorText( result ) );
+#endif
+ return result;
+ }
+
+ result = ValidateOpenStreamParameters( inputParameters,
+ outputParameters,
+ sampleRate, framesPerBuffer,
+ streamFlags, streamCallback,
+ &hostApi,
+ &hostApiInputDevice,
+ &hostApiOutputDevice );
+ if( result != paNoError )
+ {
+#ifdef PA_LOG_API_CALLS
+ PaUtil_DebugPrint("Pa_OpenStream returned:\n" );
+ PaUtil_DebugPrint("\t*(PaStream** stream): undefined\n" );
+ PaUtil_DebugPrint("\tPaError: %d ( %s )\n\n", result, Pa_GetErrorText( result ) );
+#endif
+ return result;
+ }
+
+
+ if( inputParameters )
+ {
+ hostApiInputParameters.device = hostApiInputDevice;
+ hostApiInputParameters.channelCount = inputParameters->channelCount;
+ hostApiInputParameters.sampleFormat = inputParameters->sampleFormat;
+ hostApiInputParameters.suggestedLatency = inputParameters->suggestedLatency;
+ hostApiInputParameters.hostApiSpecificStreamInfo = inputParameters->hostApiSpecificStreamInfo;
+ hostApiInputParametersPtr = &hostApiInputParameters;
+ }
+ else
+ {
+ hostApiInputParametersPtr = NULL;
+ }
+
+ if( outputParameters )
+ {
+ hostApiOutputParameters.device = hostApiOutputDevice;
+ hostApiOutputParameters.channelCount = outputParameters->channelCount;
+ hostApiOutputParameters.sampleFormat = outputParameters->sampleFormat;
+ hostApiOutputParameters.suggestedLatency = outputParameters->suggestedLatency;
+ hostApiOutputParameters.hostApiSpecificStreamInfo = outputParameters->hostApiSpecificStreamInfo;
+ hostApiOutputParametersPtr = &hostApiOutputParameters;
+ }
+ else
+ {
+ hostApiOutputParametersPtr = NULL;
+ }
+
+ result = hostApi->OpenStream( hostApi, stream,
+ hostApiInputParametersPtr, hostApiOutputParametersPtr,
+ sampleRate, framesPerBuffer, streamFlags, streamCallback, userData );
+
+ if( result == paNoError )
+ AddOpenStream( *stream );
+
+
+#ifdef PA_LOG_API_CALLS
+ PaUtil_DebugPrint("Pa_OpenStream returned:\n" );
+ PaUtil_DebugPrint("\t*(PaStream** stream): 0x%p\n", *stream );
+ PaUtil_DebugPrint("\tPaError: %d ( %s )\n\n", result, Pa_GetErrorText( result ) );
+#endif
+
+ return result;
+}
+
+
+PaError Pa_OpenDefaultStream( PaStream** stream,
+ int inputChannelCount,
+ int outputChannelCount,
+ PaSampleFormat sampleFormat,
+ double sampleRate,
+ unsigned long framesPerBuffer,
+ PaStreamCallback *streamCallback,
+ void *userData )
+{
+ PaError result;
+ PaStreamParameters hostApiInputParameters, hostApiOutputParameters;
+ PaStreamParameters *hostApiInputParametersPtr, *hostApiOutputParametersPtr;
+
+#ifdef PA_LOG_API_CALLS
+ PaUtil_DebugPrint("Pa_OpenDefaultStream called:\n" );
+ PaUtil_DebugPrint("\tPaStream** stream: 0x%p\n", stream );
+ PaUtil_DebugPrint("\tint inputChannelCount: %d\n", inputChannelCount );
+ PaUtil_DebugPrint("\tint outputChannelCount: %d\n", outputChannelCount );
+ PaUtil_DebugPrint("\tPaSampleFormat sampleFormat: %d\n", sampleFormat );
+ PaUtil_DebugPrint("\tdouble sampleRate: %g\n", sampleRate );
+ PaUtil_DebugPrint("\tunsigned long framesPerBuffer: %d\n", framesPerBuffer );
+ PaUtil_DebugPrint("\tPaStreamCallback *streamCallback: 0x%p\n", streamCallback );
+ PaUtil_DebugPrint("\tvoid *userData: 0x%p\n", userData );
+#endif
+
+
+ if( inputChannelCount > 0 )
+ {
+ hostApiInputParameters.device = Pa_GetDefaultInputDevice();
+ hostApiInputParameters.channelCount = inputChannelCount;
+ hostApiInputParameters.sampleFormat = sampleFormat;
+ /* defaultHighInputLatency is used below instead of
+ defaultLowInputLatency because it is more important for the default
+ stream to work reliably than it is for it to work with the lowest
+ latency.
+ */
+ hostApiInputParameters.suggestedLatency =
+ Pa_GetDeviceInfo( hostApiInputParameters.device )->defaultHighInputLatency;
+ hostApiInputParameters.hostApiSpecificStreamInfo = NULL;
+ hostApiInputParametersPtr = &hostApiInputParameters;
+ }
+ else
+ {
+ hostApiInputParametersPtr = NULL;
+ }
+
+ if( outputChannelCount > 0 )
+ {
+ hostApiOutputParameters.device = Pa_GetDefaultOutputDevice();
+ hostApiOutputParameters.channelCount = outputChannelCount;
+ hostApiOutputParameters.sampleFormat = sampleFormat;
+ /* defaultHighOutputLatency is used below instead of
+ defaultLowOutputLatency because it is more important for the default
+ stream to work reliably than it is for it to work with the lowest
+ latency.
+ */
+ hostApiOutputParameters.suggestedLatency =
+ Pa_GetDeviceInfo( hostApiOutputParameters.device )->defaultHighOutputLatency;
+ hostApiOutputParameters.hostApiSpecificStreamInfo = NULL;
+ hostApiOutputParametersPtr = &hostApiOutputParameters;
+ }
+ else
+ {
+ hostApiOutputParametersPtr = NULL;
+ }
+
+
+ result = Pa_OpenStream(
+ stream, hostApiInputParametersPtr, hostApiOutputParametersPtr,
+ sampleRate, framesPerBuffer, paNoFlag, streamCallback, userData );
+
+#ifdef PA_LOG_API_CALLS
+ PaUtil_DebugPrint("Pa_OpenDefaultStream returned:\n" );
+ PaUtil_DebugPrint("\t*(PaStream** stream): 0x%p", *stream );
+ PaUtil_DebugPrint("\tPaError: %d ( %s )\n\n", result, Pa_GetErrorText( result ) );
+#endif
+
+ return result;
+}
+
+
+PaError PaUtil_ValidateStreamPointer( PaStream* stream )
+{
+ if( !PA_IS_INITIALISED_ ) return paNotInitialized;
+
+ if( stream == NULL ) return paBadStreamPtr;
+
+ if( ((PaUtilStreamRepresentation*)stream)->magic != PA_STREAM_MAGIC )
+ return paBadStreamPtr;
+
+ return paNoError;
+}
+
+
+PaError Pa_CloseStream( PaStream* stream )
+{
+ PaUtilStreamInterface *interface;
+ PaError result = PaUtil_ValidateStreamPointer( stream );
+
+#ifdef PA_LOG_API_CALLS
+ PaUtil_DebugPrint("Pa_CloseStream called:\n" );
+ PaUtil_DebugPrint("\tPaStream* stream: 0x%p\n", stream );
+#endif
+
+ /* always remove the open stream from our list, even if this function
+ eventually returns an error. Otherwise CloseOpenStreams() will
+ get stuck in an infinite loop */
+ RemoveOpenStream( stream ); /* be sure to call this _before_ closing the stream */
+
+ if( result == paNoError )
+ {
+ interface = PA_STREAM_INTERFACE(stream);
+
+ /* abort the stream if it isn't stopped */
+ result = interface->IsStopped( stream );
+ if( result == 1 )
+ result = paNoError;
+ else if( result == 0 )
+ result = interface->Abort( stream );
+
+ if( result == paNoError ) /** @todo REVIEW: shouldn't we close anyway? */
+ result = interface->Close( stream );
+ }
+
+#ifdef PA_LOG_API_CALLS
+ PaUtil_DebugPrint("Pa_CloseStream returned:\n" );
+ PaUtil_DebugPrint("\tPaError: %d ( %s )\n\n", result, Pa_GetErrorText( result ) );
+#endif
+
+ return result;
+}
+
+
+PaError Pa_SetStreamFinishedCallback( PaStream *stream, PaStreamFinishedCallback* streamFinishedCallback )
+{
+ PaError result = PaUtil_ValidateStreamPointer( stream );
+
+#ifdef PA_LOG_API_CALLS
+ PaUtil_DebugPrint("Pa_SetStreamFinishedCallback called:\n" );
+ PaUtil_DebugPrint("\tPaStream* stream: 0x%p\n", stream );
+ PaUtil_DebugPrint("\tPaStreamFinishedCallback* streamFinishedCallback: 0x%p\n", streamFinishedCallback );
+#endif
+
+ if( result == paNoError )
+ {
+ result = PA_STREAM_INTERFACE(stream)->IsStopped( stream );
+ if( result == 0 )
+ {
+ result = paStreamIsNotStopped ;
+ }
+ if( result == 1 )
+ {
+ PA_STREAM_REP( stream )->streamFinishedCallback = streamFinishedCallback;
+ result = paNoError;
+ }
+ }
+
+#ifdef PA_LOG_API_CALLS
+ PaUtil_DebugPrint("Pa_SetStreamFinishedCallback returned:\n" );
+ PaUtil_DebugPrint("\tPaError: %d ( %s )\n\n", result, Pa_GetErrorText( result ) );
+#endif
+
+ return result;
+
+}
+
+
+PaError Pa_StartStream( PaStream *stream )
+{
+ PaError result = PaUtil_ValidateStreamPointer( stream );
+
+#ifdef PA_LOG_API_CALLS
+ PaUtil_DebugPrint("Pa_StartStream called:\n" );
+ PaUtil_DebugPrint("\tPaStream* stream: 0x%p\n", stream );
+#endif
+
+ if( result == paNoError )
+ {
+ result = PA_STREAM_INTERFACE(stream)->IsStopped( stream );
+ if( result == 0 )
+ {
+ result = paStreamIsNotStopped ;
+ }
+ else if( result == 1 )
+ {
+ result = PA_STREAM_INTERFACE(stream)->Start( stream );
+ }
+ }
+
+#ifdef PA_LOG_API_CALLS
+ PaUtil_DebugPrint("Pa_StartStream returned:\n" );
+ PaUtil_DebugPrint("\tPaError: %d ( %s )\n\n", result, Pa_GetErrorText( result ) );
+#endif
+
+ return result;
+}
+
+
+PaError Pa_StopStream( PaStream *stream )
+{
+ PaError result = PaUtil_ValidateStreamPointer( stream );
+
+#ifdef PA_LOG_API_CALLS
+ PaUtil_DebugPrint("Pa_StopStream called\n" );
+ PaUtil_DebugPrint("\tPaStream* stream: 0x%p\n", stream );
+#endif
+
+ if( result == paNoError )
+ {
+ result = PA_STREAM_INTERFACE(stream)->IsStopped( stream );
+ if( result == 0 )
+ {
+ result = PA_STREAM_INTERFACE(stream)->Stop( stream );
+ }
+ else if( result == 1 )
+ {
+ result = paStreamIsStopped;
+ }
+ }
+
+#ifdef PA_LOG_API_CALLS
+ PaUtil_DebugPrint("Pa_StopStream returned:\n" );
+ PaUtil_DebugPrint("\tPaError: %d ( %s )\n\n", result, Pa_GetErrorText( result ) );
+#endif
+
+ return result;
+}
+
+
+PaError Pa_AbortStream( PaStream *stream )
+{
+ PaError result = PaUtil_ValidateStreamPointer( stream );
+
+#ifdef PA_LOG_API_CALLS
+ PaUtil_DebugPrint("Pa_AbortStream called:\n" );
+ PaUtil_DebugPrint("\tPaStream* stream: 0x%p\n", stream );
+#endif
+
+ if( result == paNoError )
+ {
+ result = PA_STREAM_INTERFACE(stream)->IsStopped( stream );
+ if( result == 0 )
+ {
+ result = PA_STREAM_INTERFACE(stream)->Abort( stream );
+ }
+ else if( result == 1 )
+ {
+ result = paStreamIsStopped;
+ }
+ }
+
+#ifdef PA_LOG_API_CALLS
+ PaUtil_DebugPrint("Pa_AbortStream returned:\n" );
+ PaUtil_DebugPrint("\tPaError: %d ( %s )\n\n", result, Pa_GetErrorText( result ) );
+#endif
+
+ return result;
+}
+
+
+PaError Pa_IsStreamStopped( PaStream *stream )
+{
+ PaError result = PaUtil_ValidateStreamPointer( stream );
+
+#ifdef PA_LOG_API_CALLS
+ PaUtil_DebugPrint("Pa_IsStreamStopped called:\n" );
+ PaUtil_DebugPrint("\tPaStream* stream: 0x%p\n", stream );
+#endif
+
+ if( result == paNoError )
+ result = PA_STREAM_INTERFACE(stream)->IsStopped( stream );
+
+#ifdef PA_LOG_API_CALLS
+ PaUtil_DebugPrint("Pa_IsStreamStopped returned:\n" );
+ PaUtil_DebugPrint("\tPaError: %d ( %s )\n\n", result, Pa_GetErrorText( result ) );
+#endif
+
+ return result;
+}
+
+
+PaError Pa_IsStreamActive( PaStream *stream )
+{
+ PaError result = PaUtil_ValidateStreamPointer( stream );
+
+#ifdef PA_LOG_API_CALLS
+ PaUtil_DebugPrint("Pa_IsStreamActive called:\n" );
+ PaUtil_DebugPrint("\tPaStream* stream: 0x%p\n", stream );
+#endif
+
+ if( result == paNoError )
+ result = PA_STREAM_INTERFACE(stream)->IsActive( stream );
+
+#ifdef PA_LOG_API_CALLS
+ PaUtil_DebugPrint("Pa_IsStreamActive returned:\n" );
+ PaUtil_DebugPrint("\tPaError: %d ( %s )\n\n", result, Pa_GetErrorText( result ) );
+#endif
+
+ return result;
+}
+
+
+const PaStreamInfo* Pa_GetStreamInfo( PaStream *stream )
+{
+ PaError error = PaUtil_ValidateStreamPointer( stream );
+ const PaStreamInfo *result;
+
+#ifdef PA_LOG_API_CALLS
+ PaUtil_DebugPrint("Pa_GetStreamInfo called:\n" );
+ PaUtil_DebugPrint("\tPaStream* stream: 0x%p\n", stream );
+#endif
+
+ if( error != paNoError )
+ {
+ result = 0;
+
+#ifdef PA_LOG_API_CALLS
+ PaUtil_DebugPrint("Pa_GetStreamInfo returned:\n" );
+ PaUtil_DebugPrint("\tconst PaStreamInfo*: 0 [PaError error:%d ( %s )]\n\n", result, error, Pa_GetErrorText( error ) );
+#endif
+
+ }
+ else
+ {
+ result = &PA_STREAM_REP( stream )->streamInfo;
+
+#ifdef PA_LOG_API_CALLS
+ PaUtil_DebugPrint("Pa_GetStreamInfo returned:\n" );
+ PaUtil_DebugPrint("\tconst PaStreamInfo*: 0x%p:\n", result );
+ PaUtil_DebugPrint("\t{" );
+
+ PaUtil_DebugPrint("\t\tint structVersion: %d\n", result->structVersion );
+ PaUtil_DebugPrint("\t\tPaTime inputLatency: %f\n", result->inputLatency );
+ PaUtil_DebugPrint("\t\tPaTime outputLatency: %f\n", result->outputLatency );
+ PaUtil_DebugPrint("\t\tdouble sampleRate: %f\n", result->sampleRate );
+ PaUtil_DebugPrint("\t}\n\n" );
+#endif
+
+ }
+
+ return result;
+}
+
+
+PaTime Pa_GetStreamTime( PaStream *stream )
+{
+ PaError error = PaUtil_ValidateStreamPointer( stream );
+ PaTime result;
+
+#ifdef PA_LOG_API_CALLS
+ PaUtil_DebugPrint("Pa_GetStreamTime called:\n" );
+ PaUtil_DebugPrint("\tPaStream* stream: 0x%p\n", stream );
+#endif
+
+ if( error != paNoError )
+ {
+ result = 0;
+
+#ifdef PA_LOG_API_CALLS
+ PaUtil_DebugPrint("Pa_GetStreamTime returned:\n" );
+ PaUtil_DebugPrint("\tPaTime: 0 [PaError error:%d ( %s )]\n\n", result, error, Pa_GetErrorText( error ) );
+#endif
+
+ }
+ else
+ {
+ result = PA_STREAM_INTERFACE(stream)->GetTime( stream );
+
+#ifdef PA_LOG_API_CALLS
+ PaUtil_DebugPrint("Pa_GetStreamTime returned:\n" );
+ PaUtil_DebugPrint("\tPaTime: %g\n\n", result );
+#endif
+
+ }
+
+ return result;
+}
+
+
+double Pa_GetStreamCpuLoad( PaStream* stream )
+{
+ PaError error = PaUtil_ValidateStreamPointer( stream );
+ double result;
+
+#ifdef PA_LOG_API_CALLS
+ PaUtil_DebugPrint("Pa_GetStreamCpuLoad called:\n" );
+ PaUtil_DebugPrint("\tPaStream* stream: 0x%p\n", stream );
+#endif
+
+ if( error != paNoError )
+ {
+
+ result = 0.0;
+
+#ifdef PA_LOG_API_CALLS
+ PaUtil_DebugPrint("Pa_GetStreamCpuLoad returned:\n" );
+ PaUtil_DebugPrint("\tdouble: 0.0 [PaError error: %d ( %s )]\n\n", error, Pa_GetErrorText( error ) );
+#endif
+
+ }
+ else
+ {
+ result = PA_STREAM_INTERFACE(stream)->GetCpuLoad( stream );
+
+#ifdef PA_LOG_API_CALLS
+ PaUtil_DebugPrint("Pa_GetStreamCpuLoad returned:\n" );
+ PaUtil_DebugPrint("\tdouble: %g\n\n", result );
+#endif
+
+ }
+
+ return result;
+}
+
+
+PaError Pa_ReadStream( PaStream* stream,
+ void *buffer,
+ unsigned long frames )
+{
+ PaError result = PaUtil_ValidateStreamPointer( stream );
+
+#ifdef PA_LOG_API_CALLS
+ PaUtil_DebugPrint("Pa_ReadStream called:\n" );
+ PaUtil_DebugPrint("\tPaStream* stream: 0x%p\n", stream );
+#endif
+
+ if( result == paNoError )
+ {
+ if( frames == 0 )
+ {
+ result = paInternalError; /** @todo should return a different error code */
+ }
+ else if( buffer == 0 )
+ {
+ result = paInternalError; /** @todo should return a different error code */
+ }
+ else
+ {
+ result = PA_STREAM_INTERFACE(stream)->IsStopped( stream );
+ if( result == 0 )
+ {
+ result = PA_STREAM_INTERFACE(stream)->Read( stream, buffer, frames );
+ }
+ else if( result == 1 )
+ {
+ result = paStreamIsStopped;
+ }
+ }
+ }
+
+#ifdef PA_LOG_API_CALLS
+ PaUtil_DebugPrint("Pa_ReadStream returned:\n" );
+ PaUtil_DebugPrint("\tPaError: %d ( %s )\n\n", result, Pa_GetErrorText( result ) );
+#endif
+
+ return result;
+}
+
+
+PaError Pa_WriteStream( PaStream* stream,
+ const void *buffer,
+ unsigned long frames )
+{
+ PaError result = PaUtil_ValidateStreamPointer( stream );
+
+#ifdef PA_LOG_API_CALLS
+ PaUtil_DebugPrint("Pa_WriteStream called:\n" );
+ PaUtil_DebugPrint("\tPaStream* stream: 0x%p\n", stream );
+#endif
+
+ if( result == paNoError )
+ {
+ if( frames == 0 )
+ {
+ result = paInternalError; /** @todo should return a different error code */
+ }
+ else if( buffer == 0 )
+ {
+ result = paInternalError; /** @todo should return a different error code */
+ }
+ else
+ {
+ result = PA_STREAM_INTERFACE(stream)->IsStopped( stream );
+ if( result == 0 )
+ {
+ result = PA_STREAM_INTERFACE(stream)->Write( stream, buffer, frames );
+ }
+ else if( result == 1 )
+ {
+ result = paStreamIsStopped;
+ }
+ }
+ }
+
+#ifdef PA_LOG_API_CALLS
+ PaUtil_DebugPrint("Pa_WriteStream returned:\n" );
+ PaUtil_DebugPrint("\tPaError: %d ( %s )\n\n", result, Pa_GetErrorText( result ) );
+#endif
+
+ return result;
+}
+
+signed long Pa_GetStreamReadAvailable( PaStream* stream )
+{
+ PaError error = PaUtil_ValidateStreamPointer( stream );
+ signed long result;
+
+#ifdef PA_LOG_API_CALLS
+ PaUtil_DebugPrint("Pa_GetStreamReadAvailable called:\n" );
+ PaUtil_DebugPrint("\tPaStream* stream: 0x%p\n", stream );
+#endif
+
+ if( error != paNoError )
+ {
+ result = 0;
+
+#ifdef PA_LOG_API_CALLS
+ PaUtil_DebugPrint("Pa_GetStreamReadAvailable returned:\n" );
+ PaUtil_DebugPrint("\tunsigned long: 0 [ PaError error: %d ( %s ) ]\n\n", error, Pa_GetErrorText( error ) );
+#endif
+
+ }
+ else
+ {
+ result = PA_STREAM_INTERFACE(stream)->GetReadAvailable( stream );
+
+#ifdef PA_LOG_API_CALLS
+ PaUtil_DebugPrint("Pa_GetStreamReadAvailable returned:\n" );
+ PaUtil_DebugPrint("\tPaError: %d ( %s )\n\n", result, Pa_GetErrorText( result ) );
+#endif
+
+ }
+
+ return result;
+}
+
+
+signed long Pa_GetStreamWriteAvailable( PaStream* stream )
+{
+ PaError error = PaUtil_ValidateStreamPointer( stream );
+ signed long result;
+
+#ifdef PA_LOG_API_CALLS
+ PaUtil_DebugPrint("Pa_GetStreamWriteAvailable called:\n" );
+ PaUtil_DebugPrint("\tPaStream* stream: 0x%p\n", stream );
+#endif
+
+ if( error != paNoError )
+ {
+ result = 0;
+
+#ifdef PA_LOG_API_CALLS
+ PaUtil_DebugPrint("Pa_GetStreamWriteAvailable returned:\n" );
+ PaUtil_DebugPrint("\tunsigned long: 0 [ PaError error: %d ( %s ) ]\n\n", error, Pa_GetErrorText( error ) );
+#endif
+
+ }
+ else
+ {
+ result = PA_STREAM_INTERFACE(stream)->GetWriteAvailable( stream );
+
+#ifdef PA_LOG_API_CALLS
+ PaUtil_DebugPrint("Pa_GetStreamWriteAvailable returned:\n" );
+ PaUtil_DebugPrint("\tPaError: %d ( %s )\n\n", result, Pa_GetErrorText( result ) );
+#endif
+
+ }
+
+ return result;
+}
+
+
+PaError Pa_GetSampleSize( PaSampleFormat format )
+{
+ int result;
+
+#ifdef PA_LOG_API_CALLS
+ PaUtil_DebugPrint("Pa_GetSampleSize called:\n" );
+ PaUtil_DebugPrint("\tPaSampleFormat format: %d\n", format );
+#endif
+
+ switch( format & ~paNonInterleaved )
+ {
+
+ case paUInt8:
+ case paInt8:
+ result = 1;
+ break;
+
+ case paInt16:
+ result = 2;
+ break;
+
+ case paInt24:
+ result = 3;
+ break;
+
+ case paFloat32:
+ case paInt32:
+ result = 4;
+ break;
+
+ default:
+ result = paSampleFormatNotSupported;
+ break;
+ }
+
+#ifdef PA_LOG_API_CALLS
+ PaUtil_DebugPrint("Pa_GetSampleSize returned:\n" );
+ if( result > 0 )
+ PaUtil_DebugPrint("\tint: %d\n\n", result );
+ else
+ PaUtil_DebugPrint("\tPaError: %d ( %s )\n\n", result, Pa_GetErrorText( result ) );
+#endif
+
+ return (PaError) result;
+}
+
diff --git a/pjmedia/src/pjmedia/portaudio/pa_hostapi.h b/pjmedia/src/pjmedia/portaudio/pa_hostapi.h
index a71ff319..d0550706 100644
--- a/pjmedia/src/pjmedia/portaudio/pa_hostapi.h
+++ b/pjmedia/src/pjmedia/portaudio/pa_hostapi.h
@@ -1,244 +1,244 @@
-#ifndef PA_HOSTAPI_H
-#define PA_HOSTAPI_H
-/*
- * $Id: pa_hostapi.h,v 1.1.2.14 2004/01/08 22:01:12 rossbencina Exp $
- * Portable Audio I/O Library
- * host api representation
- *
- * Based on the Open Source API proposed by Ross Bencina
- * Copyright (c) 1999-2002 Ross Bencina, Phil Burk
- *
- * 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.
- *
- * Any person wishing to distribute modifications to the Software is
- * requested to send the modifications to the original developer so that
- * they can be incorporated into the canonical version.
- *
- * 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.
- */
-
-/** @file
- @brief Interface used by pa_front to virtualize functions which operate on
- host APIs.
-*/
-
-
-#include "portaudio.h"
-
-#ifdef __cplusplus
-extern "C"
-{
-#endif /* __cplusplus */
-
-
-/** **FOR THE USE OF pa_front.c ONLY**
- Do NOT use fields in this structure, they my change at any time.
- Use functions defined in pa_util.h if you think you need functionality
- which can be derived from here.
-*/
-typedef struct PaUtilPrivatePaFrontHostApiInfo {
-
-
- unsigned long baseDeviceIndex;
-}PaUtilPrivatePaFrontHostApiInfo;
-
-
-/** The common header for all data structures whose pointers are passed through
- the hostApiSpecificStreamInfo field of the PaStreamParameters structure.
- Note that in order to keep the public PortAudio interface clean, this structure
- is not used explicitly when declaring hostApiSpecificStreamInfo data structures.
- However, some code in pa_front depends on the first 3 members being equivalent
- with this structure.
- @see PaStreamParameters
-*/
-typedef struct PaUtilHostApiSpecificStreamInfoHeader
-{
- unsigned long size; /**< size of whole structure including this header */
- PaHostApiTypeId hostApiType; /**< host API for which this data is intended */
- unsigned long version; /**< structure version */
-} PaUtilHostApiSpecificStreamInfoHeader;
-
-
-
-/** A structure representing the interface to a host API. Contains both
- concrete data and pointers to functions which implement the interface.
-*/
-typedef struct PaUtilHostApiRepresentation {
- PaUtilPrivatePaFrontHostApiInfo privatePaFrontInfo;
-
- /** The host api implementation should populate the info field. In the
- case of info.defaultInputDevice and info.defaultOutputDevice the
- values stored should be 0 based indices within the host api's own
- device index range (0 to deviceCount). These values will be converted
- to global device indices by pa_front after PaUtilHostApiInitializer()
- returns.
- */
- PaHostApiInfo info;
-
- PaDeviceInfo** deviceInfos;
-
- /**
- (*Terminate)() is guaranteed to be called with a valid <hostApi>
- parameter, which was previously returned from the same implementation's
- initializer.
- */
- void (*Terminate)( struct PaUtilHostApiRepresentation *hostApi );
-
- /**
- The inputParameters and outputParameters pointers should not be saved
- as they will not remain valid after OpenStream is called.
-
-
- The following guarantees are made about parameters to (*OpenStream)():
-
- [NOTE: the following list up to *END PA FRONT VALIDATIONS* should be
- kept in sync with the one for ValidateOpenStreamParameters and
- Pa_OpenStream in pa_front.c]
-
- PaHostApiRepresentation *hostApi
- - is valid for this implementation
-
- PaStream** stream
- - is non-null
-
- - at least one of inputParameters & outputParmeters is valid (not NULL)
-
- - if inputParameters & outputParmeters are both valid, that
- inputParameters->device & outputParmeters->device both use the same host api
-
- PaDeviceIndex inputParameters->device
- - is within range (0 to Pa_CountDevices-1) Or:
- - is paUseHostApiSpecificDeviceSpecification and
- inputParameters->hostApiSpecificStreamInfo is non-NULL and refers
- to a valid host api
-
- int inputParameters->numChannels
- - if inputParameters->device is not paUseHostApiSpecificDeviceSpecification, numInputChannels is > 0
- - upper bound is NOT validated against device capabilities
-
- PaSampleFormat inputParameters->sampleFormat
- - is one of the sample formats defined in portaudio.h
-
- void *inputParameters->hostApiSpecificStreamInfo
- - if supplied its hostApi field matches the input device's host Api
-
- PaDeviceIndex outputParmeters->device
- - is within range (0 to Pa_CountDevices-1)
-
- int outputParmeters->numChannels
- - if inputDevice is valid, numInputChannels is > 0
- - upper bound is NOT validated against device capabilities
-
- PaSampleFormat outputParmeters->sampleFormat
- - is one of the sample formats defined in portaudio.h
-
- void *outputParmeters->hostApiSpecificStreamInfo
- - if supplied its hostApi field matches the output device's host Api
-
- double sampleRate
- - is not an 'absurd' rate (less than 1000. or greater than 200000.)
- - sampleRate is NOT validated against device capabilities
-
- PaStreamFlags streamFlags
- - unused platform neutral flags are zero
- - paNeverDropInput is only used for full-duplex callback streams
- with variable buffer size (paFramesPerBufferUnspecified)
-
- [*END PA FRONT VALIDATIONS*]
-
-
- The following validations MUST be performed by (*OpenStream)():
-
- - check that input device can support numInputChannels
-
- - check that input device can support inputSampleFormat, or that
- we have the capability to convert from outputSampleFormat to
- a native format
-
- - if inputStreamInfo is supplied, validate its contents,
- or return an error if no inputStreamInfo is expected
-
- - check that output device can support numOutputChannels
-
- - check that output device can support outputSampleFormat, or that
- we have the capability to convert from outputSampleFormat to
- a native format
-
- - if outputStreamInfo is supplied, validate its contents,
- or return an error if no outputStreamInfo is expected
-
- - if a full duplex stream is requested, check that the combination
- of input and output parameters is supported
-
- - check that the device supports sampleRate
-
- - alter sampleRate to a close allowable rate if necessary
-
- - validate inputLatency and outputLatency
-
- - validate any platform specific flags, if flags are supplied they
- must be valid.
- */
- PaError (*OpenStream)( struct PaUtilHostApiRepresentation *hostApi,
- PaStream** stream,
- const PaStreamParameters *inputParameters,
- const PaStreamParameters *outputParameters,
- double sampleRate,
- unsigned long framesPerCallback,
- PaStreamFlags streamFlags,
- PaStreamCallback *streamCallback,
- void *userData );
-
-
- PaError (*IsFormatSupported)( struct PaUtilHostApiRepresentation *hostApi,
- const PaStreamParameters *inputParameters,
- const PaStreamParameters *outputParameters,
- double sampleRate );
-} PaUtilHostApiRepresentation;
-
-
-/** Prototype for the initialization function which must be implemented by every
- host API.
-
- @see paHostApiInitializers
-*/
-typedef PaError PaUtilHostApiInitializer( PaUtilHostApiRepresentation**, PaHostApiIndex );
-
-
-/** paHostApiInitializers is a NULL-terminated array of host API initialization
- functions. These functions are called by pa_front to initialize the host APIs
- when the client calls Pa_Initialize().
-
- There is a platform specific file which defines paHostApiInitializers for that
- platform, pa_win/pa_win_hostapis.c contains the Win32 definitions for example.
-*/
-extern PaUtilHostApiInitializer *paHostApiInitializers[];
-
-
-/** The index of the default host API in the paHostApiInitializers array.
-
- There is a platform specific file which defines paDefaultHostApiIndex for that
- platform, see pa_win/pa_win_hostapis.c for example.
-*/
-extern int paDefaultHostApiIndex;
-
-
-#ifdef __cplusplus
-}
-#endif /* __cplusplus */
-#endif /* PA_HOSTAPI_H */
+#ifndef PA_HOSTAPI_H
+#define PA_HOSTAPI_H
+/*
+ * $Id: pa_hostapi.h,v 1.1.2.14 2004/01/08 22:01:12 rossbencina Exp $
+ * Portable Audio I/O Library
+ * host api representation
+ *
+ * Based on the Open Source API proposed by Ross Bencina
+ * Copyright (c) 1999-2002 Ross Bencina, Phil Burk
+ *
+ * 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.
+ *
+ * Any person wishing to distribute modifications to the Software is
+ * requested to send the modifications to the original developer so that
+ * they can be incorporated into the canonical version.
+ *
+ * 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.
+ */
+
+/** @file
+ @brief Interface used by pa_front to virtualize functions which operate on
+ host APIs.
+*/
+
+
+#include "portaudio.h"
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif /* __cplusplus */
+
+
+/** **FOR THE USE OF pa_front.c ONLY**
+ Do NOT use fields in this structure, they my change at any time.
+ Use functions defined in pa_util.h if you think you need functionality
+ which can be derived from here.
+*/
+typedef struct PaUtilPrivatePaFrontHostApiInfo {
+
+
+ unsigned long baseDeviceIndex;
+}PaUtilPrivatePaFrontHostApiInfo;
+
+
+/** The common header for all data structures whose pointers are passed through
+ the hostApiSpecificStreamInfo field of the PaStreamParameters structure.
+ Note that in order to keep the public PortAudio interface clean, this structure
+ is not used explicitly when declaring hostApiSpecificStreamInfo data structures.
+ However, some code in pa_front depends on the first 3 members being equivalent
+ with this structure.
+ @see PaStreamParameters
+*/
+typedef struct PaUtilHostApiSpecificStreamInfoHeader
+{
+ unsigned long size; /**< size of whole structure including this header */
+ PaHostApiTypeId hostApiType; /**< host API for which this data is intended */
+ unsigned long version; /**< structure version */
+} PaUtilHostApiSpecificStreamInfoHeader;
+
+
+
+/** A structure representing the interface to a host API. Contains both
+ concrete data and pointers to functions which implement the interface.
+*/
+typedef struct PaUtilHostApiRepresentation {
+ PaUtilPrivatePaFrontHostApiInfo privatePaFrontInfo;
+
+ /** The host api implementation should populate the info field. In the
+ case of info.defaultInputDevice and info.defaultOutputDevice the
+ values stored should be 0 based indices within the host api's own
+ device index range (0 to deviceCount). These values will be converted
+ to global device indices by pa_front after PaUtilHostApiInitializer()
+ returns.
+ */
+ PaHostApiInfo info;
+
+ PaDeviceInfo** deviceInfos;
+
+ /**
+ (*Terminate)() is guaranteed to be called with a valid <hostApi>
+ parameter, which was previously returned from the same implementation's
+ initializer.
+ */
+ void (*Terminate)( struct PaUtilHostApiRepresentation *hostApi );
+
+ /**
+ The inputParameters and outputParameters pointers should not be saved
+ as they will not remain valid after OpenStream is called.
+
+
+ The following guarantees are made about parameters to (*OpenStream)():
+
+ [NOTE: the following list up to *END PA FRONT VALIDATIONS* should be
+ kept in sync with the one for ValidateOpenStreamParameters and
+ Pa_OpenStream in pa_front.c]
+
+ PaHostApiRepresentation *hostApi
+ - is valid for this implementation
+
+ PaStream** stream
+ - is non-null
+
+ - at least one of inputParameters & outputParmeters is valid (not NULL)
+
+ - if inputParameters & outputParmeters are both valid, that
+ inputParameters->device & outputParmeters->device both use the same host api
+
+ PaDeviceIndex inputParameters->device
+ - is within range (0 to Pa_CountDevices-1) Or:
+ - is paUseHostApiSpecificDeviceSpecification and
+ inputParameters->hostApiSpecificStreamInfo is non-NULL and refers
+ to a valid host api
+
+ int inputParameters->numChannels
+ - if inputParameters->device is not paUseHostApiSpecificDeviceSpecification, numInputChannels is > 0
+ - upper bound is NOT validated against device capabilities
+
+ PaSampleFormat inputParameters->sampleFormat
+ - is one of the sample formats defined in portaudio.h
+
+ void *inputParameters->hostApiSpecificStreamInfo
+ - if supplied its hostApi field matches the input device's host Api
+
+ PaDeviceIndex outputParmeters->device
+ - is within range (0 to Pa_CountDevices-1)
+
+ int outputParmeters->numChannels
+ - if inputDevice is valid, numInputChannels is > 0
+ - upper bound is NOT validated against device capabilities
+
+ PaSampleFormat outputParmeters->sampleFormat
+ - is one of the sample formats defined in portaudio.h
+
+ void *outputParmeters->hostApiSpecificStreamInfo
+ - if supplied its hostApi field matches the output device's host Api
+
+ double sampleRate
+ - is not an 'absurd' rate (less than 1000. or greater than 200000.)
+ - sampleRate is NOT validated against device capabilities
+
+ PaStreamFlags streamFlags
+ - unused platform neutral flags are zero
+ - paNeverDropInput is only used for full-duplex callback streams
+ with variable buffer size (paFramesPerBufferUnspecified)
+
+ [*END PA FRONT VALIDATIONS*]
+
+
+ The following validations MUST be performed by (*OpenStream)():
+
+ - check that input device can support numInputChannels
+
+ - check that input device can support inputSampleFormat, or that
+ we have the capability to convert from outputSampleFormat to
+ a native format
+
+ - if inputStreamInfo is supplied, validate its contents,
+ or return an error if no inputStreamInfo is expected
+
+ - check that output device can support numOutputChannels
+
+ - check that output device can support outputSampleFormat, or that
+ we have the capability to convert from outputSampleFormat to
+ a native format
+
+ - if outputStreamInfo is supplied, validate its contents,
+ or return an error if no outputStreamInfo is expected
+
+ - if a full duplex stream is requested, check that the combination
+ of input and output parameters is supported
+
+ - check that the device supports sampleRate
+
+ - alter sampleRate to a close allowable rate if necessary
+
+ - validate inputLatency and outputLatency
+
+ - validate any platform specific flags, if flags are supplied they
+ must be valid.
+ */
+ PaError (*OpenStream)( struct PaUtilHostApiRepresentation *hostApi,
+ PaStream** stream,
+ const PaStreamParameters *inputParameters,
+ const PaStreamParameters *outputParameters,
+ double sampleRate,
+ unsigned long framesPerCallback,
+ PaStreamFlags streamFlags,
+ PaStreamCallback *streamCallback,
+ void *userData );
+
+
+ PaError (*IsFormatSupported)( struct PaUtilHostApiRepresentation *hostApi,
+ const PaStreamParameters *inputParameters,
+ const PaStreamParameters *outputParameters,
+ double sampleRate );
+} PaUtilHostApiRepresentation;
+
+
+/** Prototype for the initialization function which must be implemented by every
+ host API.
+
+ @see paHostApiInitializers
+*/
+typedef PaError PaUtilHostApiInitializer( PaUtilHostApiRepresentation**, PaHostApiIndex );
+
+
+/** paHostApiInitializers is a NULL-terminated array of host API initialization
+ functions. These functions are called by pa_front to initialize the host APIs
+ when the client calls Pa_Initialize().
+
+ There is a platform specific file which defines paHostApiInitializers for that
+ platform, pa_win/pa_win_hostapis.c contains the Win32 definitions for example.
+*/
+extern PaUtilHostApiInitializer *paHostApiInitializers[];
+
+
+/** The index of the default host API in the paHostApiInitializers array.
+
+ There is a platform specific file which defines paDefaultHostApiIndex for that
+ platform, see pa_win/pa_win_hostapis.c for example.
+*/
+extern int paDefaultHostApiIndex;
+
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+#endif /* PA_HOSTAPI_H */
diff --git a/pjmedia/src/pjmedia/portaudio/pa_linux_alsa.c b/pjmedia/src/pjmedia/portaudio/pa_linux_alsa.c
index 8f115d0e..3b047163 100644
--- a/pjmedia/src/pjmedia/portaudio/pa_linux_alsa.c
+++ b/pjmedia/src/pjmedia/portaudio/pa_linux_alsa.c
@@ -1,3272 +1,3272 @@
-/*
- * $Id: pa_linux_alsa.c,v 1.1.2.71 2005/04/15 18:20:18 aknudsen Exp $
- * PortAudio Portable Real-Time Audio Library
- * Latest Version at: http://www.portaudio.com
- * ALSA implementation by Joshua Haberman and Arve Knudsen
- *
- * Copyright (c) 2002 Joshua Haberman <joshua@haberman.com>
- * Copyright (c) 2005 Arve Knudsen <aknuds-1@broadpark.no>
- *
- * Based on the Open Source API proposed by Ross Bencina
- * Copyright (c) 1999-2002 Ross Bencina, Phil Burk
- *
- * 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.
- *
- * Any person wishing to distribute modifications to the Software is
- * requested to send the modifications to the original developer so that
- * they can be incorporated into the canonical version.
- *
- * 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.
- */
-
-#define ALSA_PCM_NEW_HW_PARAMS_API
-#define ALSA_PCM_NEW_SW_PARAMS_API
-#include <alsa/asoundlib.h>
-#undef ALSA_PCM_NEW_HW_PARAMS_API
-#undef ALSA_PCM_NEW_SW_PARAMS_API
-
-#include <sys/poll.h>
-#include <string.h> /* strlen() */
-#include <limits.h>
-#include <math.h>
-#include <pthread.h>
-#include <signal.h>
-#include <time.h>
-#include <sys/mman.h>
-#include <signal.h> /* For sig_atomic_t */
-
-#include "portaudio.h"
-#include "pa_util.h"
-#include "pa_unix_util.h"
-#include "pa_allocation.h"
-#include "pa_hostapi.h"
-#include "pa_stream.h"
-#include "pa_cpuload.h"
-#include "pa_process.h"
-
-#include "pa_linux_alsa.h"
-
-/* Check return value of ALSA function, and map it to PaError */
-#define ENSURE_(expr, code) \
- do { \
- if( UNLIKELY( (aErr_ = (expr)) < 0 ) ) \
- { \
- /* PaUtil_SetLastHostErrorInfo should only be used in the main thread */ \
- if( (code) == paUnanticipatedHostError && pthread_self() != callbackThread_ ) \
- { \
- PaUtil_SetLastHostErrorInfo( paALSA, aErr_, snd_strerror( aErr_ ) ); \
- } \
- PaUtil_DebugPrint(( "Expression '" #expr "' failed in '" __FILE__ "', line: " STRINGIZE( __LINE__ ) "\n" )); \
- result = (code); \
- goto error; \
- } \
- } while( 0 );
-
-#define ASSERT_CALL_(expr, success) \
- aErr_ = (expr); \
- assert( aErr_ == success );
-
-static int aErr_; /* Used with ENSURE_ */
-static pthread_t callbackThread_;
-
-typedef enum
-{
- StreamDirection_In,
- StreamDirection_Out
-} StreamDirection;
-
-/* Threading utility struct */
-typedef struct PaAlsaThreading
-{
- pthread_t watchdogThread;
- pthread_t callbackThread;
- int watchdogRunning;
- int rtSched;
- int rtPrio;
- int useWatchdog;
- unsigned long throttledSleepTime;
- volatile PaTime callbackTime;
- volatile PaTime callbackCpuTime;
- PaUtilCpuLoadMeasurer *cpuLoadMeasurer;
-} PaAlsaThreading;
-
-typedef struct
-{
- PaSampleFormat hostSampleFormat;
- unsigned long framesPerBuffer;
- int numUserChannels, numHostChannels;
- int userInterleaved, hostInterleaved;
-
- snd_pcm_t *pcm;
- snd_pcm_uframes_t bufferSize;
- snd_pcm_format_t nativeFormat;
- unsigned int nfds;
- int ready; /* Marked ready from poll */
- void **userBuffers;
- snd_pcm_uframes_t offset;
- StreamDirection streamDir;
-
- snd_pcm_channel_area_t *channelAreas; /* Needed for channel adaption */
-} PaAlsaStreamComponent;
-
-/* Implementation specific stream structure */
-typedef struct PaAlsaStream
-{
- PaUtilStreamRepresentation streamRepresentation;
- PaUtilCpuLoadMeasurer cpuLoadMeasurer;
- PaUtilBufferProcessor bufferProcessor;
- PaAlsaThreading threading;
-
- unsigned long framesPerUserBuffer;
-
- int primeBuffers;
- int callbackMode; /* bool: are we running in callback mode? */
- int pcmsSynced; /* Have we successfully synced pcms */
-
- /* the callback thread uses these to poll the sound device(s), waiting
- * for data to be ready/available */
- struct pollfd *pfds;
- int pollTimeout;
-
- /* Used in communication between threads */
- volatile sig_atomic_t callback_finished; /* bool: are we in the "callback finished" state? */
- volatile sig_atomic_t callbackAbort; /* Drop frames? */
- volatile sig_atomic_t callbackStop; /* Signal a stop */
- volatile sig_atomic_t isActive; /* Is stream in active state? (Between StartStream and StopStream || !paContinue) */
- pthread_mutex_t stateMtx; /* Used to synchronize access to stream state */
- pthread_mutex_t startMtx; /* Used to synchronize stream start in callback mode */
- pthread_cond_t startCond; /* Wait untill audio is started in callback thread */
-
- int neverDropInput;
-
- PaTime underrun;
- PaTime overrun;
-
- PaAlsaStreamComponent capture, playback;
-}
-PaAlsaStream;
-
-/* PaAlsaHostApiRepresentation - host api datastructure specific to this implementation */
-
-typedef struct PaAlsaHostApiRepresentation
-{
- PaUtilHostApiRepresentation commonHostApiRep;
- PaUtilStreamInterface callbackStreamInterface;
- PaUtilStreamInterface blockingStreamInterface;
-
- PaUtilAllocationGroup *allocations;
-
- PaHostApiIndex hostApiIndex;
-}
-PaAlsaHostApiRepresentation;
-
-typedef struct PaAlsaDeviceInfo
-{
- PaDeviceInfo commonDeviceInfo;
- char *alsaName;
- int isPlug;
- int minInputChannels;
- int minOutputChannels;
-}
-PaAlsaDeviceInfo;
-
-/* Threading utilities */
-
-static void InitializeThreading( PaAlsaThreading *th, PaUtilCpuLoadMeasurer *clm )
-{
- th->watchdogRunning = 0;
- th->rtSched = 0;
- th->callbackTime = 0;
- th->callbackCpuTime = 0;
- th->useWatchdog = 1;
- th->throttledSleepTime = 0;
- th->cpuLoadMeasurer = clm;
-
- th->rtPrio = (sched_get_priority_max( SCHED_FIFO ) - sched_get_priority_min( SCHED_FIFO )) / 2
- + sched_get_priority_min( SCHED_FIFO );
-}
-
-static PaError KillCallbackThread( PaAlsaThreading *th, int wait, PaError *exitResult, PaError *watchdogExitResult )
-{
- PaError result = paNoError;
- void *pret;
-
- if( exitResult )
- *exitResult = paNoError;
- if( watchdogExitResult )
- *watchdogExitResult = paNoError;
-
- if( th->watchdogRunning )
- {
- pthread_cancel( th->watchdogThread );
- ASSERT_CALL_( pthread_join( th->watchdogThread, &pret ), 0 );
-
- if( pret && pret != PTHREAD_CANCELED )
- {
- if( watchdogExitResult )
- *watchdogExitResult = *(PaError *) pret;
- free( pret );
- }
- }
-
- /* Only kill the thread if it isn't in the process of stopping (flushing adaptation buffers) */
- /* TODO: Make join time out */
- if( !wait )
- pthread_cancel( th->callbackThread ); /* XXX: Safe to call this if the thread has exited on its own? */
- ASSERT_CALL_( pthread_join( th->callbackThread, &pret ), 0 );
-
- if( pret && pret != PTHREAD_CANCELED )
- {
- if( exitResult )
- *exitResult = *(PaError *) pret;
- free( pret );
- }
-
- return result;
-}
-
-static void OnWatchdogExit( void *userData )
-{
- PaAlsaThreading *th = (PaAlsaThreading *) userData;
- struct sched_param spm = { 0 };
- assert( th );
-
- ASSERT_CALL_( pthread_setschedparam( th->callbackThread, SCHED_OTHER, &spm ), 0 ); /* Lower before exiting */
- PA_DEBUG(( "Watchdog exiting\n" ));
-}
-
-static PaError BoostPriority( PaAlsaThreading *th )
-{
- PaError result = paNoError;
- struct sched_param spm = { 0 };
- spm.sched_priority = th->rtPrio;
-
- assert( th );
-
- if( pthread_setschedparam( th->callbackThread, SCHED_FIFO, &spm ) != 0 )
- {
- PA_UNLESS( errno == EPERM, paInternalError ); /* Lack permission to raise priority */
- PA_DEBUG(( "Failed bumping priority\n" ));
- result = 0;
- }
- else
- result = 1; /* Success */
-error:
- return result;
-}
-
-static void *WatchdogFunc( void *userData )
-{
- PaError result = paNoError, *pres = NULL;
- int err;
- PaAlsaThreading *th = (PaAlsaThreading *) userData;
- unsigned intervalMsec = 500;
- const PaTime maxSeconds = 3.; /* Max seconds between callbacks */
- PaTime timeThen = PaUtil_GetTime(), timeNow, timeElapsed, cpuTimeThen, cpuTimeNow, cpuTimeElapsed;
- double cpuLoad, avgCpuLoad = 0.;
- int throttled = 0;
-
- assert( th );
-
- pthread_cleanup_push( &OnWatchdogExit, th ); /* Execute OnWatchdogExit when exiting */
-
- /* Boost priority of callback thread */
- PA_ENSURE( result = BoostPriority( th ) );
- if( !result )
- {
- pthread_exit( NULL ); /* Boost failed, might as well exit */
- }
-
- cpuTimeThen = th->callbackCpuTime;
- {
- int policy;
- struct sched_param spm = { 0 };
- pthread_getschedparam( pthread_self(), &policy, &spm );
- PA_DEBUG(( "%s: Watchdog priority is %d\n", __FUNCTION__, spm.sched_priority ));
- }
-
- while( 1 )
- {
- double lowpassCoeff = 0.9, lowpassCoeff1 = 0.99999 - lowpassCoeff;
-
- /* Test before and after in case whatever underlying sleep call isn't interrupted by pthread_cancel */
- pthread_testcancel();
- Pa_Sleep( intervalMsec );
- pthread_testcancel();
-
- if( PaUtil_GetTime() - th->callbackTime > maxSeconds )
- {
- PA_DEBUG(( "Watchdog: Terminating callback thread\n" ));
- /* Tell thread to terminate */
- err = pthread_kill( th->callbackThread, SIGKILL );
- pthread_exit( NULL );
- }
-
- PA_DEBUG(( "%s: PortAudio reports CPU load: %g\n", __FUNCTION__, PaUtil_GetCpuLoad( th->cpuLoadMeasurer ) ));
-
- /* Check if we should throttle, or unthrottle :P */
- cpuTimeNow = th->callbackCpuTime;
- cpuTimeElapsed = cpuTimeNow - cpuTimeThen;
- cpuTimeThen = cpuTimeNow;
-
- timeNow = PaUtil_GetTime();
- timeElapsed = timeNow - timeThen;
- timeThen = timeNow;
- cpuLoad = cpuTimeElapsed / timeElapsed;
- avgCpuLoad = avgCpuLoad * lowpassCoeff + cpuLoad * lowpassCoeff1;
- /*
- if( throttled )
- PA_DEBUG(( "Watchdog: CPU load: %g, %g\n", avgCpuLoad, cpuTimeElapsed ));
- */
- if( PaUtil_GetCpuLoad( th->cpuLoadMeasurer ) > .925 )
- {
- static int policy;
- static struct sched_param spm = { 0 };
- static const struct sched_param defaultSpm = { 0 };
- PA_DEBUG(( "%s: Throttling audio thread, priority %d\n", __FUNCTION__, spm.sched_priority ));
-
- pthread_getschedparam( th->callbackThread, &policy, &spm );
- if( !pthread_setschedparam( th->callbackThread, SCHED_OTHER, &defaultSpm ) )
- {
- throttled = 1;
- }
- else
- PA_DEBUG(( "Watchdog: Couldn't lower priority of audio thread: %s\n", strerror( errno ) ));
-
- /* Give other processes a go, before raising priority again */
- PA_DEBUG(( "%s: Watchdog sleeping for %lu msecs before unthrottling\n", __FUNCTION__, th->throttledSleepTime ));
- Pa_Sleep( th->throttledSleepTime );
-
- /* Reset callback priority */
- if( pthread_setschedparam( th->callbackThread, SCHED_FIFO, &spm ) != 0 )
- {
- PA_DEBUG(( "%s: Couldn't raise priority of audio thread: %s\n", __FUNCTION__, strerror( errno ) ));
- }
-
- if( PaUtil_GetCpuLoad( th->cpuLoadMeasurer ) >= .99 )
- intervalMsec = 50;
- else
- intervalMsec = 100;
-
- /*
- lowpassCoeff = .97;
- lowpassCoeff1 = .99999 - lowpassCoeff;
- */
- }
- else if( throttled && avgCpuLoad < .8 )
- {
- intervalMsec = 500;
- throttled = 0;
-
- /*
- lowpassCoeff = .9;
- lowpassCoeff1 = .99999 - lowpassCoeff;
- */
- }
- }
-
- pthread_cleanup_pop( 1 ); /* Execute cleanup on exit */
-
-error:
- /* Shouldn't get here in the normal case */
-
- /* Pass on error code */
- pres = malloc( sizeof (PaError) );
- *pres = result;
-
- pthread_exit( pres );
-}
-
-static PaError CreateCallbackThread( PaAlsaThreading *th, void *(*callbackThreadFunc)( void * ), PaStream *s )
-{
- PaError result = paNoError;
- pthread_attr_t attr;
- int started = 0;
-
-#if defined _POSIX_MEMLOCK && (_POSIX_MEMLOCK != -1)
- if( th->rtSched )
- {
- if( mlockall( MCL_CURRENT | MCL_FUTURE ) < 0 )
- {
- int savedErrno = errno; /* In case errno gets overwritten */
- assert( savedErrno != EINVAL ); /* Most likely a programmer error */
- PA_UNLESS( (savedErrno == EPERM), paInternalError );
- PA_DEBUG(( "%s: Failed locking memory\n", __FUNCTION__ ));
- }
- else
- PA_DEBUG(( "%s: Successfully locked memory\n", __FUNCTION__ ));
- }
-#endif
-
- PA_UNLESS( !pthread_attr_init( &attr ), paInternalError );
- /* Priority relative to other processes */
- PA_UNLESS( !pthread_attr_setscope( &attr, PTHREAD_SCOPE_SYSTEM ), paInternalError );
-
- PA_UNLESS( !pthread_create( &th->callbackThread, &attr, callbackThreadFunc, s ), paInternalError );
- started = 1;
-
- if( th->rtSched )
- {
- if( th->useWatchdog )
- {
- int err;
- struct sched_param wdSpm = { 0 };
- /* Launch watchdog, watchdog sets callback thread priority */
- int prio = PA_MIN( th->rtPrio + 4, sched_get_priority_max( SCHED_FIFO ) );
- wdSpm.sched_priority = prio;
-
- PA_UNLESS( !pthread_attr_init( &attr ), paInternalError );
- PA_UNLESS( !pthread_attr_setinheritsched( &attr, PTHREAD_EXPLICIT_SCHED ), paInternalError );
- PA_UNLESS( !pthread_attr_setscope( &attr, PTHREAD_SCOPE_SYSTEM ), paInternalError );
- PA_UNLESS( !pthread_attr_setschedpolicy( &attr, SCHED_FIFO ), paInternalError );
- PA_UNLESS( !pthread_attr_setschedparam( &attr, &wdSpm ), paInternalError );
- if( (err = pthread_create( &th->watchdogThread, &attr, &WatchdogFunc, th )) )
- {
- PA_UNLESS( err == EPERM, paInternalError );
- /* Permission error, go on without realtime privileges */
- PA_DEBUG(( "Failed bumping priority\n" ));
- }
- else
- {
- int policy;
- th->watchdogRunning = 1;
- ASSERT_CALL_( pthread_getschedparam( th->watchdogThread, &policy, &wdSpm ), 0 );
- /* Check if priority is right, policy could potentially differ from SCHED_FIFO (but that's alright) */
- if( wdSpm.sched_priority != prio )
- {
- PA_DEBUG(( "Watchdog priority not set correctly (%d)\n", wdSpm.sched_priority ));
- PA_ENSURE( paInternalError );
- }
- }
- }
- else
- PA_ENSURE( BoostPriority( th ) );
- }
-
-end:
- return result;
-error:
- if( started )
- KillCallbackThread( th, 0, NULL, NULL );
-
- goto end;
-}
-
-static void CallbackUpdate( PaAlsaThreading *th )
-{
- th->callbackTime = PaUtil_GetTime();
- th->callbackCpuTime = PaUtil_GetCpuLoad( th->cpuLoadMeasurer );
-}
-
-/* prototypes for functions declared in this file */
-
-static void Terminate( struct PaUtilHostApiRepresentation *hostApi );
-static PaError IsFormatSupported( struct PaUtilHostApiRepresentation *hostApi,
- const PaStreamParameters *inputParameters,
- const PaStreamParameters *outputParameters,
- double sampleRate );
-static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi,
- PaStream** s,
- const PaStreamParameters *inputParameters,
- const PaStreamParameters *outputParameters,
- double sampleRate,
- unsigned long framesPerBuffer,
- PaStreamFlags streamFlags,
- PaStreamCallback *callback,
- void *userData );
-static PaError CloseStream( PaStream* stream );
-static PaError StartStream( PaStream *stream );
-static PaError StopStream( PaStream *stream );
-static PaError AbortStream( PaStream *stream );
-static PaError IsStreamStopped( PaStream *s );
-static PaError IsStreamActive( PaStream *stream );
-static PaTime GetStreamTime( PaStream *stream );
-static double GetStreamCpuLoad( PaStream* stream );
-static PaError BuildDeviceList( PaAlsaHostApiRepresentation *hostApi );
-static int SetApproximateSampleRate( snd_pcm_t *pcm, snd_pcm_hw_params_t *hwParams, double sampleRate );
-static int GetExactSampleRate( snd_pcm_hw_params_t *hwParams, double *sampleRate );
-
-/* Callback prototypes */
-static void *CallbackThreadFunc( void *userData );
-
-/* Blocking prototypes */
-static signed long GetStreamReadAvailable( PaStream* s );
-static signed long GetStreamWriteAvailable( PaStream* s );
-static PaError ReadStream( PaStream* stream, void *buffer, unsigned long frames );
-static PaError WriteStream( PaStream* stream, const void *buffer, unsigned long frames );
-
-
-static const PaAlsaDeviceInfo *GetDeviceInfo( const PaUtilHostApiRepresentation *hostApi, int device )
-{
- return (const PaAlsaDeviceInfo *)hostApi->deviceInfos[device];
-}
-
-PaError PaAlsa_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiIndex hostApiIndex )
-{
- PaError result = paNoError;
- PaAlsaHostApiRepresentation *alsaHostApi = NULL;
-
- PA_UNLESS( alsaHostApi = (PaAlsaHostApiRepresentation*) PaUtil_AllocateMemory(
- sizeof(PaAlsaHostApiRepresentation) ), paInsufficientMemory );
- PA_UNLESS( alsaHostApi->allocations = PaUtil_CreateAllocationGroup(), paInsufficientMemory );
- alsaHostApi->hostApiIndex = hostApiIndex;
-
- *hostApi = (PaUtilHostApiRepresentation*)alsaHostApi;
- (*hostApi)->info.structVersion = 1;
- (*hostApi)->info.type = paALSA;
- (*hostApi)->info.name = "ALSA";
-
- (*hostApi)->Terminate = Terminate;
- (*hostApi)->OpenStream = OpenStream;
- (*hostApi)->IsFormatSupported = IsFormatSupported;
-
- PA_ENSURE( BuildDeviceList( alsaHostApi ) );
-
- PaUtil_InitializeStreamInterface( &alsaHostApi->callbackStreamInterface,
- CloseStream, StartStream,
- StopStream, AbortStream,
- IsStreamStopped, IsStreamActive,
- GetStreamTime, GetStreamCpuLoad,
- PaUtil_DummyRead, PaUtil_DummyWrite,
- PaUtil_DummyGetReadAvailable,
- PaUtil_DummyGetWriteAvailable );
-
- PaUtil_InitializeStreamInterface( &alsaHostApi->blockingStreamInterface,
- CloseStream, StartStream,
- StopStream, AbortStream,
- IsStreamStopped, IsStreamActive,
- GetStreamTime, PaUtil_DummyGetCpuLoad,
- ReadStream, WriteStream,
- GetStreamReadAvailable,
- GetStreamWriteAvailable );
-
- return result;
-
-error:
- if( alsaHostApi )
- {
- if( alsaHostApi->allocations )
- {
- PaUtil_FreeAllAllocations( alsaHostApi->allocations );
- PaUtil_DestroyAllocationGroup( alsaHostApi->allocations );
- }
-
- PaUtil_FreeMemory( alsaHostApi );
- }
-
- return result;
-}
-
-static void Terminate( struct PaUtilHostApiRepresentation *hostApi )
-{
- PaAlsaHostApiRepresentation *alsaHostApi = (PaAlsaHostApiRepresentation*)hostApi;
-
- assert( hostApi );
-
- if( alsaHostApi->allocations )
- {
- PaUtil_FreeAllAllocations( alsaHostApi->allocations );
- PaUtil_DestroyAllocationGroup( alsaHostApi->allocations );
- }
-
- PaUtil_FreeMemory( alsaHostApi );
- snd_config_update_free_global();
-}
-
-/*! Determine max channels and default latencies.
- *
- * This function provides functionality to grope an opened (might be opened for capture or playback) pcm device for
- * traits like max channels, suitable default latencies and default sample rate. Upon error, max channels is set to zero,
- * and a suitable result returned. The device is closed before returning.
- */
-static PaError GropeDevice( snd_pcm_t *pcm, int *minChannels, int *maxChannels, double *defaultLowLatency,
- double *defaultHighLatency, double *defaultSampleRate, int isPlug )
-{
- PaError result = paNoError;
- snd_pcm_hw_params_t *hwParams;
- snd_pcm_uframes_t lowLatency = 512, highLatency = 2048;
- unsigned int minChans, maxChans;
- double defaultSr = *defaultSampleRate;
-
- assert( pcm );
-
- ENSURE_( snd_pcm_nonblock( pcm, 0 ), paUnanticipatedHostError );
-
- snd_pcm_hw_params_alloca( &hwParams );
- snd_pcm_hw_params_any( pcm, hwParams );
-
- if( defaultSr >= 0 )
- {
- /* Could be that the device opened in one mode supports samplerates that the other mode wont have,
- * so try again .. */
- if( SetApproximateSampleRate( pcm, hwParams, defaultSr ) < 0 )
- {
- defaultSr = -1.;
- PA_DEBUG(( "%s: Original default samplerate failed, trying again ..\n", __FUNCTION__ ));
- }
- }
-
- if( defaultSr < 0. ) /* Default sample rate not set */
- {
- unsigned int sampleRate = 44100; /* Will contain approximate rate returned by alsa-lib */
- ENSURE_( snd_pcm_hw_params_set_rate_near( pcm, hwParams, &sampleRate, NULL ), paUnanticipatedHostError );
- ENSURE_( GetExactSampleRate( hwParams, &defaultSr ), paUnanticipatedHostError );
- }
-
- ENSURE_( snd_pcm_hw_params_get_channels_min( hwParams, &minChans ), paUnanticipatedHostError );
- ENSURE_( snd_pcm_hw_params_get_channels_max( hwParams, &maxChans ), paUnanticipatedHostError );
- assert( maxChans <= INT_MAX );
- assert( maxChans > 0 ); /* Weird linking issue could cause wrong version of ALSA symbols to be called,
- resulting in zeroed values */
-
- /* XXX: Limit to sensible number (ALSA plugins accept a crazy amount of channels)? */
- if( isPlug && maxChans > 128 )
- {
- maxChans = 128;
- PA_DEBUG(( "%s: Limiting number of plugin channels to %u\n", __FUNCTION__, maxChans ));
- }
-
- /* TWEAKME:
- *
- * Giving values for default min and max latency is not
- * straightforward. Here are our objectives:
- *
- * * for low latency, we want to give the lowest value
- * that will work reliably. This varies based on the
- * sound card, kernel, CPU, etc. I think it is better
- * to give sub-optimal latency than to give a number
- * too low and cause dropouts. My conservative
- * estimate at this point is to base it on 4096-sample
- * latency at 44.1 kHz, which gives a latency of 23ms.
- * * for high latency we want to give a large enough
- * value that dropouts are basically impossible. This
- * doesn't really require as much tweaking, since
- * providing too large a number will just cause us to
- * select the nearest setting that will work at stream
- * config time.
- */
- ENSURE_( snd_pcm_hw_params_set_buffer_size_near( pcm, hwParams, &lowLatency ), paUnanticipatedHostError );
-
- /* Have to reset hwParams, to set new buffer size */
- ENSURE_( snd_pcm_hw_params_any( pcm, hwParams ), paUnanticipatedHostError );
- ENSURE_( snd_pcm_hw_params_set_buffer_size_near( pcm, hwParams, &highLatency ), paUnanticipatedHostError );
-
- *minChannels = (int)minChans;
- *maxChannels = (int)maxChans;
- *defaultSampleRate = defaultSr;
- *defaultLowLatency = (double) lowLatency / *defaultSampleRate;
- *defaultHighLatency = (double) highLatency / *defaultSampleRate;
-
-end:
- snd_pcm_close( pcm );
- return result;
-
-error:
- goto end;
-}
-
-/* Initialize device info with invalid values (maxInputChannels and maxOutputChannels are set to zero since these indicate
- * wether input/output is available) */
-static void InitializeDeviceInfo( PaDeviceInfo *deviceInfo )
-{
- deviceInfo->structVersion = -1;
- deviceInfo->name = NULL;
- deviceInfo->hostApi = -1;
- deviceInfo->maxInputChannels = 0;
- deviceInfo->maxOutputChannels = 0;
- deviceInfo->defaultLowInputLatency = -1.;
- deviceInfo->defaultLowOutputLatency = -1.;
- deviceInfo->defaultHighInputLatency = -1.;
- deviceInfo->defaultHighOutputLatency = -1.;
- deviceInfo->defaultSampleRate = -1.;
-}
-
-/* Helper struct */
-typedef struct
-{
- char *alsaName;
- char *name;
- int isPlug;
- int hasPlayback;
- int hasCapture;
-} DeviceNames;
-
-static PaError PaAlsa_StrDup( PaAlsaHostApiRepresentation *alsaApi,
- char **dst,
- const char *src)
-{
- PaError result = paNoError;
- int len = strlen( src ) + 1;
-
- /* PA_DEBUG(("PaStrDup %s %d\n", src, len)); */
-
- PA_UNLESS( *dst = (char *)PaUtil_GroupAllocateMemory( alsaApi->allocations, len ),
- paInsufficientMemory );
- strncpy( *dst, src, len );
-
-error:
- return result;
-}
-
-/* Disregard standard plugins
- * XXX: Might want to make the "default" plugin available, if we can make it work
- */
-static int IgnorePlugin( const char *pluginId )
-{
-#define numIgnored 10
- static const char *ignoredPlugins[numIgnored] = {"hw", "plughw", "plug", "default", "dsnoop", "dmix", "tee",
- "file", "null", "shm"};
- int i;
-
- for( i = 0; i < numIgnored; ++i )
- {
- if( !strcmp( pluginId, ignoredPlugins[i] ) )
- {
- return 1;
- }
- }
-
- return 0;
-}
-
-/* Build PaDeviceInfo list, ignore devices for which we cannot determine capabilities (possibly busy, sigh) */
-static PaError BuildDeviceList( PaAlsaHostApiRepresentation *alsaApi )
-{
- PaUtilHostApiRepresentation *commonApi = &alsaApi->commonHostApiRep;
- PaAlsaDeviceInfo *deviceInfoArray;
- int cardIdx = -1, devIdx = 0;
- snd_ctl_card_info_t *cardInfo;
- PaError result = paNoError;
- size_t numDeviceNames = 0, maxDeviceNames = 1, i;
- DeviceNames *deviceNames = NULL;
- snd_config_t *topNode = NULL;
- snd_pcm_info_t *pcmInfo;
- int res;
- int blocking = SND_PCM_NONBLOCK;
- char alsaCardName[50];
- if( getenv( "PA_ALSA_INITIALIZE_BLOCK" ) && atoi( getenv( "PA_ALSA_INITIALIZE_BLOCK" ) ) )
- blocking = 0;
-
- /* These two will be set to the first working input and output device, respectively */
- commonApi->info.defaultInputDevice = paNoDevice;
- commonApi->info.defaultOutputDevice = paNoDevice;
-
- /* count the devices by enumerating all the card numbers */
-
- /* snd_card_next() modifies the integer passed to it to be:
- * the index of the first card if the parameter is -1
- * the index of the next card if the parameter is the index of a card
- * -1 if there are no more cards
- *
- * The function itself returns 0 if it succeeded. */
- cardIdx = -1;
- snd_ctl_card_info_alloca( &cardInfo );
- snd_pcm_info_alloca( &pcmInfo );
- while( snd_card_next( &cardIdx ) == 0 && cardIdx >= 0 )
- {
- char *cardName;
- int devIdx = -1;
- snd_ctl_t *ctl;
- char buf[50];
-
- snprintf( alsaCardName, sizeof (alsaCardName), "hw:%d", cardIdx );
-
- /* Acquire name of card */
- if( snd_ctl_open( &ctl, alsaCardName, 0 ) < 0 )
- continue; /* Unable to open card :( */
- snd_ctl_card_info( ctl, cardInfo );
-
- PA_ENSURE( PaAlsa_StrDup( alsaApi, &cardName, snd_ctl_card_info_get_name( cardInfo )) );
-
- while( snd_ctl_pcm_next_device( ctl, &devIdx ) == 0 && devIdx >= 0 )
- {
- char *alsaDeviceName, *deviceName;
- size_t len;
- int hasPlayback = 0, hasCapture = 0;
- snprintf( buf, sizeof (buf), "%s:%d,%d", "hw", cardIdx, devIdx );
-
- /* Obtain info about this particular device */
- snd_pcm_info_set_device( pcmInfo, devIdx );
- snd_pcm_info_set_subdevice( pcmInfo, 0 );
- snd_pcm_info_set_stream( pcmInfo, SND_PCM_STREAM_CAPTURE );
- if( snd_ctl_pcm_info( ctl, pcmInfo ) >= 0 )
- hasCapture = 1;
-
- snd_pcm_info_set_stream( pcmInfo, SND_PCM_STREAM_PLAYBACK );
- if( snd_ctl_pcm_info( ctl, pcmInfo ) >= 0 )
- hasPlayback = 1;
-
- if( !hasPlayback && !hasCapture )
- {
- continue; /* Error */
- }
-
- /* The length of the string written by snprintf plus terminating 0 */
- len = snprintf( NULL, 0, "%s: %s (%s)", cardName, snd_pcm_info_get_name( pcmInfo ), buf ) + 1;
- PA_UNLESS( deviceName = (char *)PaUtil_GroupAllocateMemory( alsaApi->allocations, len ),
- paInsufficientMemory );
- snprintf( deviceName, len, "%s: %s (%s)", cardName,
- snd_pcm_info_get_name( pcmInfo ), buf );
-
- ++numDeviceNames;
- if( !deviceNames || numDeviceNames > maxDeviceNames )
- {
- maxDeviceNames *= 2;
- PA_UNLESS( deviceNames = (DeviceNames *) realloc( deviceNames, maxDeviceNames * sizeof (DeviceNames) ),
- paInsufficientMemory );
- }
-
- PA_ENSURE( PaAlsa_StrDup( alsaApi, &alsaDeviceName, buf ) );
-
- deviceNames[ numDeviceNames - 1 ].alsaName = alsaDeviceName;
- deviceNames[ numDeviceNames - 1 ].name = deviceName;
- deviceNames[ numDeviceNames - 1 ].isPlug = 0;
- deviceNames[ numDeviceNames - 1 ].hasPlayback = hasPlayback;
- deviceNames[ numDeviceNames - 1 ].hasCapture = hasCapture;
- }
- snd_ctl_close( ctl );
- }
-
- /* Iterate over plugin devices */
- snd_config_update();
- if( (res = snd_config_search( snd_config, "pcm", &topNode )) >= 0 )
- {
- snd_config_iterator_t i, next;
-
- snd_config_for_each( i, next, topNode )
- {
- const char *tpStr = NULL, *idStr = NULL;
- char *alsaDeviceName, *deviceName;
- snd_config_t *n = snd_config_iterator_entry( i ), *tp = NULL;
- if( snd_config_get_type( n ) != SND_CONFIG_TYPE_COMPOUND )
- continue;
-
- ENSURE_( snd_config_search( n, "type", &tp ), paUnanticipatedHostError );
- ENSURE_( snd_config_get_string( tp, &tpStr ), paUnanticipatedHostError );
-
- ENSURE_( snd_config_get_id( n, &idStr ), paUnanticipatedHostError );
- if( IgnorePlugin( idStr ) )
- {
- PA_DEBUG(( "%s: Ignoring ALSA plugin device %s of type %s\n", __FUNCTION__, idStr, tpStr ));
- continue;
- }
-
- PA_DEBUG(( "%s: Found plugin %s of type %s\n", __FUNCTION__, idStr, tpStr ));
-
- PA_UNLESS( alsaDeviceName = (char*)PaUtil_GroupAllocateMemory( alsaApi->allocations,
- strlen(idStr) + 6 ), paInsufficientMemory );
- strcpy( alsaDeviceName, idStr );
- PA_UNLESS( deviceName = (char*)PaUtil_GroupAllocateMemory( alsaApi->allocations,
- strlen(idStr) + 1 ), paInsufficientMemory );
- strcpy( deviceName, idStr );
-
- ++numDeviceNames;
- if( !deviceNames || numDeviceNames > maxDeviceNames )
- {
- maxDeviceNames *= 2;
- PA_UNLESS( deviceNames = (DeviceNames *) realloc( deviceNames, maxDeviceNames * sizeof (DeviceNames) ),
- paInsufficientMemory );
- }
-
- deviceNames[numDeviceNames - 1].alsaName = alsaDeviceName;
- deviceNames[numDeviceNames - 1].name = deviceName;
- deviceNames[numDeviceNames - 1].isPlug = 1;
- deviceNames[numDeviceNames - 1].hasPlayback = 1;
- deviceNames[numDeviceNames - 1].hasCapture = 1;
- }
- }
- else
- PA_DEBUG(( "%s: Iterating over ALSA plugins failed: %s\n", __FUNCTION__, snd_strerror( res ) ));
-
- /* allocate deviceInfo memory based on the number of devices */
- PA_UNLESS( commonApi->deviceInfos = (PaDeviceInfo**)PaUtil_GroupAllocateMemory(
- alsaApi->allocations, sizeof(PaDeviceInfo*) * (numDeviceNames) ), paInsufficientMemory );
-
- /* allocate all device info structs in a contiguous block */
- PA_UNLESS( deviceInfoArray = (PaAlsaDeviceInfo*)PaUtil_GroupAllocateMemory(
- alsaApi->allocations, sizeof(PaAlsaDeviceInfo) * numDeviceNames ), paInsufficientMemory );
-
- /* Loop over list of cards, filling in info, if a device is deemed unavailable (can't get name),
- * it's ignored.
- */
- /* while( snd_card_next( &cardIdx ) == 0 && cardIdx >= 0 ) */
- for( i = 0, devIdx = 0; i < numDeviceNames; ++i )
- {
- snd_pcm_t *pcm;
- PaAlsaDeviceInfo *deviceInfo = &deviceInfoArray[devIdx];
- PaDeviceInfo *commonDeviceInfo = &deviceInfo->commonDeviceInfo;
-
- /* Zero fields */
- InitializeDeviceInfo( commonDeviceInfo );
-
- /* to determine device capabilities, we must open the device and query the
- * hardware parameter configuration space */
-
- /* Query capture */
- if( deviceNames[i].hasCapture &&
- snd_pcm_open( &pcm, deviceNames[i].alsaName, SND_PCM_STREAM_CAPTURE, blocking ) >= 0 )
- {
- if( GropeDevice( pcm, &deviceInfo->minInputChannels, &commonDeviceInfo->maxInputChannels,
- &commonDeviceInfo->defaultLowInputLatency, &commonDeviceInfo->defaultHighInputLatency,
- &commonDeviceInfo->defaultSampleRate, deviceNames[i].isPlug ) != paNoError )
- continue; /* Error */
- }
-
- /* Query playback */
- if( deviceNames[i].hasPlayback &&
- snd_pcm_open( &pcm, deviceNames[i].alsaName, SND_PCM_STREAM_PLAYBACK, blocking ) >= 0 )
- {
- if( GropeDevice( pcm, &deviceInfo->minOutputChannels, &commonDeviceInfo->maxOutputChannels,
- &commonDeviceInfo->defaultLowOutputLatency, &commonDeviceInfo->defaultHighOutputLatency,
- &commonDeviceInfo->defaultSampleRate, deviceNames[i].isPlug ) != paNoError )
- continue; /* Error */
- }
-
- commonDeviceInfo->structVersion = 2;
- commonDeviceInfo->hostApi = alsaApi->hostApiIndex;
- commonDeviceInfo->name = deviceNames[i].name;
- deviceInfo->alsaName = deviceNames[i].alsaName;
- deviceInfo->isPlug = deviceNames[i].isPlug;
-
- /* A: Storing pointer to PaAlsaDeviceInfo object as pointer to PaDeviceInfo object.
- * Should now be safe to add device info, unless the device supports neither capture nor playback
- */
- if( commonDeviceInfo->maxInputChannels > 0 || commonDeviceInfo->maxOutputChannels > 0 )
- {
- if( commonApi->info.defaultInputDevice == paNoDevice && commonDeviceInfo->maxInputChannels > 0 )
- commonApi->info.defaultInputDevice = devIdx;
- if( commonApi->info.defaultOutputDevice == paNoDevice && commonDeviceInfo->maxOutputChannels > 0 )
- commonApi->info.defaultOutputDevice = devIdx;
-
- commonApi->deviceInfos[devIdx++] = (PaDeviceInfo *) deviceInfo;
- }
- }
- free( deviceNames );
-
- commonApi->info.deviceCount = devIdx; /* Number of successfully queried devices */
-
-end:
- return result;
-
-error:
- goto end; /* No particular action */
-}
-
-/* Check against known device capabilities */
-static PaError ValidateParameters( const PaStreamParameters *parameters, PaUtilHostApiRepresentation *hostApi, StreamDirection mode )
-{
- PaError result = paNoError;
- int maxChans;
- const PaAlsaDeviceInfo *deviceInfo = NULL;
- assert( parameters );
-
- if( parameters->device != paUseHostApiSpecificDeviceSpecification )
- {
- assert( parameters->device < hostApi->info.deviceCount );
- PA_UNLESS( parameters->hostApiSpecificStreamInfo == NULL, paBadIODeviceCombination );
- deviceInfo = GetDeviceInfo( hostApi, parameters->device );
- }
- else
- {
- const PaAlsaStreamInfo *streamInfo = parameters->hostApiSpecificStreamInfo;
-
- PA_UNLESS( parameters->device == paUseHostApiSpecificDeviceSpecification, paInvalidDevice );
- PA_UNLESS( streamInfo->size == sizeof (PaAlsaStreamInfo) && streamInfo->version == 1,
- paIncompatibleHostApiSpecificStreamInfo );
-
- return paNoError; /* Skip further checking */
- }
-
- assert( deviceInfo );
- assert( parameters->hostApiSpecificStreamInfo == NULL );
- maxChans = (StreamDirection_In == mode ? deviceInfo->commonDeviceInfo.maxInputChannels :
- deviceInfo->commonDeviceInfo.maxOutputChannels);
- PA_UNLESS( parameters->channelCount <= maxChans, paInvalidChannelCount );
-
-error:
- return result;
-}
-
-/* Given an open stream, what sample formats are available? */
-
-static PaSampleFormat GetAvailableFormats( snd_pcm_t *pcm )
-{
- PaSampleFormat available = 0;
- snd_pcm_hw_params_t *hwParams;
- snd_pcm_hw_params_alloca( &hwParams );
-
- snd_pcm_hw_params_any( pcm, hwParams );
-
- if( snd_pcm_hw_params_test_format( pcm, hwParams, SND_PCM_FORMAT_FLOAT ) >= 0)
- available |= paFloat32;
-
- if( snd_pcm_hw_params_test_format( pcm, hwParams, SND_PCM_FORMAT_S32 ) >= 0)
- available |= paInt32;
-
- if( snd_pcm_hw_params_test_format( pcm, hwParams, SND_PCM_FORMAT_S24 ) >= 0)
- available |= paInt24;
-
- if( snd_pcm_hw_params_test_format( pcm, hwParams, SND_PCM_FORMAT_S16 ) >= 0)
- available |= paInt16;
-
- if( snd_pcm_hw_params_test_format( pcm, hwParams, SND_PCM_FORMAT_U8 ) >= 0)
- available |= paUInt8;
-
- if( snd_pcm_hw_params_test_format( pcm, hwParams, SND_PCM_FORMAT_S8 ) >= 0)
- available |= paInt8;
-
- return available;
-}
-
-static snd_pcm_format_t Pa2AlsaFormat( PaSampleFormat paFormat )
-{
- switch( paFormat )
- {
- case paFloat32:
- return SND_PCM_FORMAT_FLOAT;
-
- case paInt16:
- return SND_PCM_FORMAT_S16;
-
- case paInt24:
- return SND_PCM_FORMAT_S24;
-
- case paInt32:
- return SND_PCM_FORMAT_S32;
-
- case paInt8:
- return SND_PCM_FORMAT_S8;
-
- case paUInt8:
- return SND_PCM_FORMAT_U8;
-
- default:
- return SND_PCM_FORMAT_UNKNOWN;
- }
-}
-
-/** Open an ALSA pcm handle.
- *
- * The device to be open can be specified in a custom PaAlsaStreamInfo struct, or it will be a device number. In case of a
- * device number, it maybe specified through an env variable (PA_ALSA_PLUGHW) that we should open the corresponding plugin
- * device.
- */
-static PaError AlsaOpen( const PaUtilHostApiRepresentation *hostApi, const PaStreamParameters *params, StreamDirection
- streamDir, snd_pcm_t **pcm )
-{
- PaError result = paNoError;
- int ret;
- const char *deviceName = alloca( 50 );
- const PaAlsaDeviceInfo *deviceInfo = NULL;
- PaAlsaStreamInfo *streamInfo = (PaAlsaStreamInfo *)params->hostApiSpecificStreamInfo;
-
- if( !streamInfo )
- {
- int usePlug = 0;
- deviceInfo = GetDeviceInfo( hostApi, params->device );
-
- /* If device name starts with hw: and PA_ALSA_PLUGHW is 1, we open the plughw device instead */
- if( !strncmp( "hw:", deviceInfo->alsaName, 3 ) && getenv( "PA_ALSA_PLUGHW" ) )
- usePlug = atoi( getenv( "PA_ALSA_PLUGHW" ) );
- if( usePlug )
- snprintf( (char *) deviceName, 50, "plug%s", deviceInfo->alsaName );
- else
- deviceName = deviceInfo->alsaName;
- }
- else
- deviceName = streamInfo->deviceString;
-
- if( (ret = snd_pcm_open( pcm, deviceName, streamDir == StreamDirection_In ? SND_PCM_STREAM_CAPTURE : SND_PCM_STREAM_PLAYBACK,
- SND_PCM_NONBLOCK )) < 0 )
- {
- *pcm = NULL; /* Not to be closed */
- ENSURE_( ret, ret == -EBUSY ? paDeviceUnavailable : paBadIODeviceCombination );
- }
- ENSURE_( snd_pcm_nonblock( *pcm, 0 ), paUnanticipatedHostError );
-
-end:
- return result;
-
-error:
- goto end;
-}
-
-static PaError TestParameters( const PaUtilHostApiRepresentation *hostApi, const PaStreamParameters *parameters,
- double sampleRate, StreamDirection streamDir )
-{
- PaError result = paNoError;
- snd_pcm_t *pcm = NULL;
- PaSampleFormat availableFormats;
- /* We are able to adapt to a number of channels less than what the device supports */
- unsigned int numHostChannels;
- PaSampleFormat hostFormat;
- snd_pcm_hw_params_t *hwParams;
- snd_pcm_hw_params_alloca( &hwParams );
-
- if( !parameters->hostApiSpecificStreamInfo )
- {
- const PaAlsaDeviceInfo *devInfo = GetDeviceInfo( hostApi, parameters->device );
- numHostChannels = PA_MAX( parameters->channelCount, StreamDirection_In == streamDir ?
- devInfo->minInputChannels : devInfo->minOutputChannels );
- }
- else
- numHostChannels = parameters->channelCount;
-
- PA_ENSURE( AlsaOpen( hostApi, parameters, streamDir, &pcm ) );
-
- snd_pcm_hw_params_any( pcm, hwParams );
-
- if( SetApproximateSampleRate( pcm, hwParams, sampleRate ) < 0 )
- {
- result = paInvalidSampleRate;
- goto error;
- }
-
- if( snd_pcm_hw_params_set_channels( pcm, hwParams, numHostChannels ) < 0 )
- {
- result = paInvalidChannelCount;
- goto error;
- }
-
- /* See if we can find a best possible match */
- availableFormats = GetAvailableFormats( pcm );
- PA_ENSURE( hostFormat = PaUtil_SelectClosestAvailableFormat( availableFormats, parameters->sampleFormat ) );
- ENSURE_( snd_pcm_hw_params_set_format( pcm, hwParams, Pa2AlsaFormat( hostFormat ) ), paUnanticipatedHostError );
-
- ENSURE_( snd_pcm_hw_params( pcm, hwParams ), paUnanticipatedHostError );
-
-end:
- if( pcm )
- snd_pcm_close( pcm );
- return result;
-
-error:
- goto end;
-}
-
-static PaError IsFormatSupported( struct PaUtilHostApiRepresentation *hostApi,
- const PaStreamParameters *inputParameters,
- const PaStreamParameters *outputParameters,
- double sampleRate )
-{
- int inputChannelCount = 0, outputChannelCount = 0;
- PaSampleFormat inputSampleFormat, outputSampleFormat;
- PaError result = paFormatIsSupported;
-
- if( inputParameters )
- {
- PA_ENSURE( ValidateParameters( inputParameters, hostApi, StreamDirection_In ) );
-
- inputChannelCount = inputParameters->channelCount;
- inputSampleFormat = inputParameters->sampleFormat;
- }
-
- if( outputParameters )
- {
- PA_ENSURE( ValidateParameters( outputParameters, hostApi, StreamDirection_Out ) );
-
- outputChannelCount = outputParameters->channelCount;
- outputSampleFormat = outputParameters->sampleFormat;
- }
-
- if( inputChannelCount )
- {
- if( (result = TestParameters( hostApi, inputParameters, sampleRate, StreamDirection_In ))
- != paNoError )
- goto error;
- }
- if ( outputChannelCount )
- {
- if( (result = TestParameters( hostApi, outputParameters, sampleRate, StreamDirection_Out ))
- != paNoError )
- goto error;
- }
-
- return paFormatIsSupported;
-
-error:
- return result;
-}
-
-static PaError PaAlsaStreamComponent_Initialize( PaAlsaStreamComponent *self, PaAlsaHostApiRepresentation *alsaApi,
- const PaStreamParameters *params, StreamDirection streamDir, int callbackMode )
-{
- PaError result = paNoError;
- PaSampleFormat userSampleFormat = params->sampleFormat, hostSampleFormat;
- assert( params->channelCount > 0 );
-
- /* Make sure things have an initial value */
- memset( self, 0, sizeof (PaAlsaStreamComponent) );
-
- if( NULL == params->hostApiSpecificStreamInfo )
- {
- const PaAlsaDeviceInfo *devInfo = GetDeviceInfo( &alsaApi->commonHostApiRep, params->device );
- self->numHostChannels = PA_MAX( params->channelCount, StreamDirection_In == streamDir ? devInfo->minInputChannels
- : devInfo->minOutputChannels );
- }
- else
- {
- /* We're blissfully unaware of the minimum channelCount */
- self->numHostChannels = params->channelCount;
- }
-
- PA_ENSURE( AlsaOpen( &alsaApi->commonHostApiRep, params, streamDir, &self->pcm ) );
- self->nfds = snd_pcm_poll_descriptors_count( self->pcm );
- hostSampleFormat = PaUtil_SelectClosestAvailableFormat( GetAvailableFormats( self->pcm ), userSampleFormat );
-
- self->hostSampleFormat = hostSampleFormat;
- self->nativeFormat = Pa2AlsaFormat( hostSampleFormat );
- self->hostInterleaved = self->userInterleaved = !(userSampleFormat & paNonInterleaved);
- self->numUserChannels = params->channelCount;
- self->streamDir = streamDir;
-
- if( !callbackMode && !self->userInterleaved )
- {
- /* Pre-allocate non-interleaved user provided buffers */
- PA_UNLESS( self->userBuffers = PaUtil_AllocateMemory( sizeof (void *) * self->numUserChannels ),
- paInsufficientMemory );
- }
-
-error:
- return result;
-}
-
-static void PaAlsaStreamComponent_Terminate( PaAlsaStreamComponent *self )
-{
- snd_pcm_close( self->pcm );
- if( self->userBuffers )
- PaUtil_FreeMemory( self->userBuffers );
-}
-
-/** Configure the associated ALSA pcm.
- *
- */
-static PaError PaAlsaStreamComponent_Configure( PaAlsaStreamComponent *self, const PaStreamParameters *params, unsigned long
- framesPerHostBuffer, int primeBuffers, int callbackMode, double *sampleRate, PaTime *returnedLatency )
-{
- /*
- int numPeriods;
-
- if( getenv("PA_NUMPERIODS") != NULL )
- numPeriods = atoi( getenv("PA_NUMPERIODS") );
- else
- numPeriods = ( (latency * sampleRate) / *framesPerBuffer ) + 1;
-
- PA_DEBUG(( "latency: %f, rate: %f, framesPerBuffer: %d\n", latency, sampleRate, *framesPerBuffer ));
- if( numPeriods <= 1 )
- numPeriods = 2;
- */
-
- /* Configuration consists of setting all of ALSA's parameters.
- * These parameters come in two flavors: hardware parameters
- * and software paramters. Hardware parameters will affect
- * the way the device is initialized, software parameters
- * affect the way ALSA interacts with me, the user-level client.
- */
-
- snd_pcm_hw_params_t *hwParams;
- snd_pcm_sw_params_t *swParams;
- PaError result = paNoError;
- snd_pcm_access_t accessMode, alternateAccessMode;
- unsigned int numPeriods, minPeriods = 2;
- int dir = 0;
- snd_pcm_t *pcm = self->pcm;
- PaTime latency = params->suggestedLatency;
- double sr = *sampleRate;
- *returnedLatency = -1.;
-
- snd_pcm_hw_params_alloca( &hwParams );
- snd_pcm_sw_params_alloca( &swParams );
-
- self->framesPerBuffer = framesPerHostBuffer;
-
- /* ... fill up the configuration space with all possibile
- * combinations of parameters this device will accept */
- ENSURE_( snd_pcm_hw_params_any( pcm, hwParams ), paUnanticipatedHostError );
-
- ENSURE_( snd_pcm_hw_params_set_periods_integer( pcm, hwParams ), paUnanticipatedHostError );
- ENSURE_( snd_pcm_hw_params_set_period_size_integer( pcm, hwParams ), paUnanticipatedHostError );
-
- if( self->userInterleaved )
- {
- accessMode = SND_PCM_ACCESS_MMAP_INTERLEAVED;
- alternateAccessMode = SND_PCM_ACCESS_MMAP_NONINTERLEAVED;
- }
- else
- {
- accessMode = SND_PCM_ACCESS_MMAP_NONINTERLEAVED;
- alternateAccessMode = SND_PCM_ACCESS_MMAP_INTERLEAVED;
- }
-
- /* If requested access mode fails, try alternate mode */
- if( snd_pcm_hw_params_set_access( pcm, hwParams, accessMode ) < 0 )
- {
- ENSURE_( snd_pcm_hw_params_set_access( pcm, hwParams, alternateAccessMode ), paUnanticipatedHostError );
- /* Flip mode */
- self->hostInterleaved = !self->userInterleaved;
- }
-
- ENSURE_( snd_pcm_hw_params_set_format( pcm, hwParams, self->nativeFormat ), paUnanticipatedHostError );
-
- ENSURE_( SetApproximateSampleRate( pcm, hwParams, sr ), paInvalidSampleRate );
- ENSURE_( GetExactSampleRate( hwParams, &sr ), paUnanticipatedHostError );
- /* reject if there's no sample rate within 1% of the one requested */
- if( (fabs( *sampleRate - sr ) / *sampleRate) > 0.01 )
- {
- PA_DEBUG(("%s: Wanted %f, closest sample rate was %d\n", __FUNCTION__, sampleRate, sr ));
- PA_ENSURE( paInvalidSampleRate );
- }
-
- ENSURE_( snd_pcm_hw_params_set_channels( pcm, hwParams, self->numHostChannels ), paInvalidChannelCount );
-
- /* I think there should be at least 2 periods (even though ALSA doesn't appear to enforce this) */
- dir = 0;
- ENSURE_( snd_pcm_hw_params_set_periods_min( pcm, hwParams, &minPeriods, &dir ), paUnanticipatedHostError );
- dir = 0;
- ENSURE_( snd_pcm_hw_params_set_period_size_near( pcm, hwParams, &self->framesPerBuffer, &dir ), paUnanticipatedHostError );
-
- /* Find an acceptable number of periods */
- numPeriods = (latency * sr) / self->framesPerBuffer + 1;
- dir = 0;
- ENSURE_( snd_pcm_hw_params_set_periods_near( pcm, hwParams, &numPeriods, &dir ), paUnanticipatedHostError );
- /* Minimum of periods should already be 2 */
- PA_UNLESS( numPeriods >= 2, paInternalError );
-
- /* Set the parameters! */
- ENSURE_( snd_pcm_hw_params( pcm, hwParams ), paUnanticipatedHostError );
- ENSURE_( snd_pcm_hw_params_get_buffer_size( hwParams, &self->bufferSize ), paUnanticipatedHostError );
-
- /* Latency in seconds, one period is not counted as latency */
- latency = (numPeriods - 1) * self->framesPerBuffer / sr;
-
- /* Now software parameters... */
- ENSURE_( snd_pcm_sw_params_current( pcm, swParams ), paUnanticipatedHostError );
-
- ENSURE_( snd_pcm_sw_params_set_start_threshold( pcm, swParams, self->framesPerBuffer ), paUnanticipatedHostError );
- ENSURE_( snd_pcm_sw_params_set_stop_threshold( pcm, swParams, self->bufferSize ), paUnanticipatedHostError );
-
- /* Silence buffer in the case of underrun */
- if( !primeBuffers ) /* XXX: Make sense? */
- {
- snd_pcm_uframes_t boundary;
- ENSURE_( snd_pcm_sw_params_get_boundary( swParams, &boundary ), paUnanticipatedHostError );
- ENSURE_( snd_pcm_sw_params_set_silence_threshold( pcm, swParams, 0 ), paUnanticipatedHostError );
- ENSURE_( snd_pcm_sw_params_set_silence_size( pcm, swParams, boundary ), paUnanticipatedHostError );
- }
-
- ENSURE_( snd_pcm_sw_params_set_avail_min( pcm, swParams, self->framesPerBuffer ), paUnanticipatedHostError );
- ENSURE_( snd_pcm_sw_params_set_xfer_align( pcm, swParams, 1 ), paUnanticipatedHostError );
- ENSURE_( snd_pcm_sw_params_set_tstamp_mode( pcm, swParams, SND_PCM_TSTAMP_MMAP ), paUnanticipatedHostError );
-
- /* Set the parameters! */
- ENSURE_( snd_pcm_sw_params( pcm, swParams ), paUnanticipatedHostError );
-
- *sampleRate = sr;
- *returnedLatency = latency;
-
-end:
- return result;
-
-error:
- goto end; /* No particular action */
-}
-
-static PaError PaAlsaStream_Initialize( PaAlsaStream *self, PaAlsaHostApiRepresentation *alsaApi, const PaStreamParameters *inParams,
- const PaStreamParameters *outParams, double sampleRate, unsigned long framesPerUserBuffer, PaStreamCallback callback,
- PaStreamFlags streamFlags, void *userData )
-{
- PaError result = paNoError;
- assert( self );
-
- memset( self, 0, sizeof (PaAlsaStream) );
-
- if( NULL != callback )
- {
- PaUtil_InitializeStreamRepresentation( &self->streamRepresentation,
- &alsaApi->callbackStreamInterface,
- callback, userData );
- self->callbackMode = 1;
- }
- else
- {
- PaUtil_InitializeStreamRepresentation( &self->streamRepresentation,
- &alsaApi->blockingStreamInterface,
- NULL, userData );
- }
-
- self->framesPerUserBuffer = framesPerUserBuffer;
- self->neverDropInput = streamFlags & paNeverDropInput;
- /* XXX: Ignore paPrimeOutputBuffersUsingStreamCallback untill buffer priming is fully supported in pa_process.c */
- /*
- if( outParams & streamFlags & paPrimeOutputBuffersUsingStreamCallback )
- self->primeBuffers = 1;
- */
- memset( &self->capture, 0, sizeof (PaAlsaStreamComponent) );
- memset( &self->playback, 0, sizeof (PaAlsaStreamComponent) );
- if( inParams )
- PA_ENSURE( PaAlsaStreamComponent_Initialize( &self->capture, alsaApi, inParams, StreamDirection_In, NULL != callback ) );
- if( outParams )
- PA_ENSURE( PaAlsaStreamComponent_Initialize( &self->playback, alsaApi, outParams, StreamDirection_Out, NULL != callback ) );
-
- assert( self->capture.nfds || self->playback.nfds );
-
- PA_UNLESS( self->pfds = (struct pollfd*)PaUtil_AllocateMemory( (self->capture.nfds +
- self->playback.nfds) * sizeof (struct pollfd) ), paInsufficientMemory );
-
- PaUtil_InitializeCpuLoadMeasurer( &self->cpuLoadMeasurer, sampleRate );
- InitializeThreading( &self->threading, &self->cpuLoadMeasurer );
- ASSERT_CALL_( pthread_mutex_init( &self->stateMtx, NULL ), 0 );
- ASSERT_CALL_( pthread_mutex_init( &self->startMtx, NULL ), 0 );
- ASSERT_CALL_( pthread_cond_init( &self->startCond, NULL ), 0 );
-
-error:
- return result;
-}
-
-/** Free resources associated with stream, and eventually stream itself.
- *
- * Frees allocated memory, and terminates individual StreamComponents.
- */
-static void PaAlsaStream_Terminate( PaAlsaStream *self )
-{
- assert( self );
-
- if( self->capture.pcm )
- {
- PaAlsaStreamComponent_Terminate( &self->capture );
- }
- if( self->playback.pcm )
- {
- PaAlsaStreamComponent_Terminate( &self->playback );
- }
-
- PaUtil_FreeMemory( self->pfds );
- ASSERT_CALL_( pthread_mutex_destroy( &self->stateMtx ), 0 );
- ASSERT_CALL_( pthread_mutex_destroy( &self->startMtx ), 0 );
- ASSERT_CALL_( pthread_cond_destroy( &self->startCond ), 0 );
-
- PaUtil_FreeMemory( self );
-}
-
-/** Calculate polling timeout
- *
- * @param frames Time to wait
- * @return Polling timeout in milliseconds
- */
-static int CalculatePollTimeout( const PaAlsaStream *stream, unsigned long frames )
-{
- assert( stream->streamRepresentation.streamInfo.sampleRate > 0.0 );
- /* Period in msecs, rounded up */
- return (int)ceil( 1000 * frames / stream->streamRepresentation.streamInfo.sampleRate );
-}
-
-/** Set up ALSA stream parameters.
- *
- */
-static PaError PaAlsaStream_Configure( PaAlsaStream *self, const PaStreamParameters *inParams, const PaStreamParameters
- *outParams, double sampleRate, unsigned long framesPerHostBuffer, double *inputLatency, double *outputLatency,
- unsigned long *maxHostBufferSize )
-{
- PaError result = paNoError;
- double realSr = sampleRate;
-
- if( self->capture.pcm )
- PA_ENSURE( PaAlsaStreamComponent_Configure( &self->capture, inParams, framesPerHostBuffer, self->primeBuffers,
- self->callbackMode, &realSr, inputLatency ) );
- if( self->playback.pcm )
- PA_ENSURE( PaAlsaStreamComponent_Configure( &self->playback, outParams, framesPerHostBuffer, self->primeBuffers,
- self->callbackMode, &realSr, outputLatency ) );
-
- /* Should be exact now */
- self->streamRepresentation.streamInfo.sampleRate = realSr;
-
- /* this will cause the two streams to automatically start/stop/prepare in sync.
- * We only need to execute these operations on one of the pair.
- * A: We don't want to do this on a blocking stream.
- */
- if( self->callbackMode && self->capture.pcm && self->playback.pcm )
- {
- int err = snd_pcm_link( self->capture.pcm, self->playback.pcm );
- if( err >= 0 )
- self->pcmsSynced = 1;
- else
- PA_DEBUG(( "%s: Unable to sync pcms: %s\n", __FUNCTION__, snd_strerror( err ) ));
- }
-
- /* Frames per host buffer for the stream is set as a compromise between the two directions */
- framesPerHostBuffer = PA_MIN( self->capture.pcm ? self->capture.framesPerBuffer : ULONG_MAX,
- self->playback.pcm ? self->playback.framesPerBuffer : ULONG_MAX );
- self->pollTimeout = CalculatePollTimeout( self, framesPerHostBuffer ); /* Period in msecs, rounded up */
-
- *maxHostBufferSize = PA_MAX( self->capture.pcm ? self->capture.bufferSize : 0,
- self->playback.pcm ? self->playback.bufferSize : 0 );
-
- /* Time before watchdog unthrottles realtime thread == 1/4 of period time in msecs */
- self->threading.throttledSleepTime = (unsigned long) (framesPerHostBuffer / sampleRate / 4 * 1000);
-
- if( self->callbackMode )
- {
- /* If the user expects a certain number of frames per callback we will either have to rely on block adaption
- * (framesPerHostBuffer is not an integer multiple of framesPerBuffer) or we can simply align the number
- * of host buffer frames with what the user specified */
- if( self->framesPerUserBuffer != paFramesPerBufferUnspecified )
- {
- /* self->alignFrames = 1; */
-
- /* Unless the ratio between number of host and user buffer frames is an integer we will have to rely
- * on block adaption */
- /*
- if( framesPerHostBuffer % framesPerBuffer != 0 || (self->capture.pcm && self->playback.pcm &&
- self->capture.framesPerBuffer != self->playback.framesPerBuffer) )
- self->useBlockAdaption = 1;
- else
- self->alignFrames = 1;
- */
- }
- }
-
-error:
- return result;
-}
-
-/* We need to determine how many frames per host buffer to use. Our
- * goals are to provide the best possible performance, but also to
- * most closely honor the requested latency settings. Therefore this
- * decision is based on:
- *
- * - the period sizes that playback and/or capture support. The
- * host buffer size has to be one of these.
- * - the number of periods that playback and/or capture support.
- *
- * We want to make period_size*(num_periods-1) to be as close as possible
- * to latency*rate for both playback and capture.
- *
- * This is one of those blocks of code that will just take a lot of
- * refinement to be any good.
- *
- * In the full-duplex case it is possible that the routine was unable
- * to find a number of frames per buffer acceptable to both devices
- * TODO: Implement an algorithm to find the value closest to acceptance
- * by both devices, to minimize difference between period sizes?
- */
-static PaError DetermineFramesPerBuffer( const PaAlsaStream *stream, double sampleRate, const PaStreamParameters *inputParameters,
- const PaStreamParameters *outputParameters, unsigned long *determinedFrames, const PaUtilHostApiRepresentation *hostApi )
-{
- PaError result = paNoError;
- unsigned long framesPerBuffer = 0;
- int numHostInputChannels = 0, numHostOutputChannels = 0;
-
- /* XXX: Clean this up */
- if( stream->capture.pcm )
- {
- const PaAlsaDeviceInfo *devInfo = GetDeviceInfo( hostApi, inputParameters->device );
- numHostInputChannels = PA_MAX( inputParameters->channelCount, devInfo->minInputChannels );
- }
- if( stream->playback.pcm )
- {
- const PaAlsaDeviceInfo *devInfo = GetDeviceInfo( hostApi, outputParameters->device );
- numHostOutputChannels = PA_MAX( outputParameters->channelCount, devInfo->minOutputChannels );
- }
-
- if( stream->capture.pcm && stream->playback.pcm )
- {
- snd_pcm_uframes_t desiredLatency, e;
- snd_pcm_uframes_t minPeriodSize, minPlayback, minCapture, maxPeriodSize, maxPlayback, maxCapture,
- optimalPeriodSize, periodSize;
- int dir = 0;
- unsigned int minPeriods = 2;
-
- snd_pcm_t *pcm;
- snd_pcm_hw_params_t *hwParamsPlayback, *hwParamsCapture;
-
- snd_pcm_hw_params_alloca( &hwParamsPlayback );
- snd_pcm_hw_params_alloca( &hwParamsCapture );
-
- /* Come up with a common desired latency */
- pcm = stream->playback.pcm;
- snd_pcm_hw_params_any( pcm, hwParamsPlayback );
- ENSURE_( SetApproximateSampleRate( pcm, hwParamsPlayback, sampleRate ), paInvalidSampleRate );
- ENSURE_( snd_pcm_hw_params_set_channels( pcm, hwParamsPlayback, numHostOutputChannels ),
- paBadIODeviceCombination );
-
- ENSURE_( snd_pcm_hw_params_set_period_size_integer( pcm, hwParamsPlayback ), paUnanticipatedHostError );
- ENSURE_( snd_pcm_hw_params_set_periods_integer( pcm, hwParamsPlayback ), paUnanticipatedHostError );
- ENSURE_( snd_pcm_hw_params_set_periods_min( pcm, hwParamsPlayback, &minPeriods, &dir ), paUnanticipatedHostError );
- ENSURE_( snd_pcm_hw_params_get_period_size_min( hwParamsPlayback, &minPlayback, &dir ), paUnanticipatedHostError );
- ENSURE_( snd_pcm_hw_params_get_period_size_max( hwParamsPlayback, &maxPlayback, &dir ), paUnanticipatedHostError );
-
- pcm = stream->capture.pcm;
- ENSURE_( snd_pcm_hw_params_any( pcm, hwParamsCapture ), paUnanticipatedHostError );
- ENSURE_( SetApproximateSampleRate( pcm, hwParamsCapture, sampleRate ), paBadIODeviceCombination );
- ENSURE_( snd_pcm_hw_params_set_channels( pcm, hwParamsCapture, numHostInputChannels ),
- paBadIODeviceCombination );
-
- ENSURE_( snd_pcm_hw_params_set_period_size_integer( pcm, hwParamsCapture ), paUnanticipatedHostError );
- ENSURE_( snd_pcm_hw_params_set_periods_integer( pcm, hwParamsCapture ), paUnanticipatedHostError );
- ENSURE_( snd_pcm_hw_params_set_periods_min( pcm, hwParamsCapture, &minPeriods, &dir ), paUnanticipatedHostError );
- ENSURE_( snd_pcm_hw_params_get_period_size_min( hwParamsCapture, &minCapture, &dir ), paUnanticipatedHostError );
- ENSURE_( snd_pcm_hw_params_get_period_size_max( hwParamsCapture, &maxCapture, &dir ), paUnanticipatedHostError );
-
- minPeriodSize = PA_MAX( minPlayback, minCapture );
- maxPeriodSize = PA_MIN( maxPlayback, maxCapture );
-
- desiredLatency = (snd_pcm_uframes_t) (PA_MIN( outputParameters->suggestedLatency, inputParameters->suggestedLatency )
- * sampleRate);
- /* Clamp desiredLatency */
- {
- snd_pcm_uframes_t tmp, maxBufferSize = ULONG_MAX;
- ENSURE_( snd_pcm_hw_params_get_buffer_size_max( hwParamsPlayback, &maxBufferSize ), paUnanticipatedHostError );
- ENSURE_( snd_pcm_hw_params_get_buffer_size_max( hwParamsCapture, &tmp ), paUnanticipatedHostError );
- maxBufferSize = PA_MIN( maxBufferSize, tmp );
-
- desiredLatency = PA_MIN( desiredLatency, maxBufferSize );
- }
-
- /* Find the closest power of 2 */
- e = ilogb( minPeriodSize );
- if( minPeriodSize & (minPeriodSize - 1) )
- e += 1;
- periodSize = (snd_pcm_uframes_t) pow( 2, e );
-
- while( periodSize <= maxPeriodSize )
- {
- if( snd_pcm_hw_params_test_period_size( stream->playback.pcm, hwParamsPlayback, periodSize, 0 ) >= 0 &&
- snd_pcm_hw_params_test_period_size( stream->capture.pcm, hwParamsCapture, periodSize, 0 ) >= 0 )
- break; /* Ok! */
-
- periodSize *= 2;
- }
-
- /* 4 periods considered optimal */
- optimalPeriodSize = PA_MAX( desiredLatency / 4, minPeriodSize );
- optimalPeriodSize = PA_MIN( optimalPeriodSize, maxPeriodSize );
-
- /* Find the closest power of 2 */
- e = ilogb( optimalPeriodSize );
- if( optimalPeriodSize & (optimalPeriodSize - 1) )
- e += 1;
- optimalPeriodSize = (snd_pcm_uframes_t) pow( 2, e );
-
- while( optimalPeriodSize >= periodSize )
- {
- pcm = stream->playback.pcm;
- if( snd_pcm_hw_params_test_period_size( pcm, hwParamsPlayback, optimalPeriodSize, 0 ) < 0 )
- continue;
-
- pcm = stream->capture.pcm;
- if( snd_pcm_hw_params_test_period_size( pcm, hwParamsCapture, optimalPeriodSize, 0 ) >= 0 )
- break;
-
- optimalPeriodSize /= 2;
- }
-
- if( optimalPeriodSize > periodSize )
- periodSize = optimalPeriodSize;
-
- if( periodSize <= maxPeriodSize )
- {
- /* Looks good */
- framesPerBuffer = periodSize;
- }
- else
- {
- /* Unable to find a common period size, oh well */
- optimalPeriodSize = PA_MAX( desiredLatency / 4, minPeriodSize );
- optimalPeriodSize = PA_MIN( optimalPeriodSize, maxPeriodSize );
-
- /* ConfigureStream should find individual period sizes acceptable for each device */
- framesPerBuffer = optimalPeriodSize;
- /* PA_ENSURE( paBadIODeviceCombination ); */
- }
- }
- else /* half-duplex is a slightly simpler case */
- {
- unsigned long bufferSize, channels;
- snd_pcm_t *pcm;
- snd_pcm_hw_params_t *hwParams;
-
- snd_pcm_hw_params_alloca( &hwParams );
-
- if( stream->capture.pcm )
- {
- pcm = stream->capture.pcm;
- bufferSize = inputParameters->suggestedLatency * sampleRate;
- channels = numHostInputChannels;
- }
- else
- {
- pcm = stream->playback.pcm;
- bufferSize = outputParameters->suggestedLatency * sampleRate;
- channels = numHostOutputChannels;
- }
-
- ENSURE_( snd_pcm_hw_params_any( pcm, hwParams ), paUnanticipatedHostError );
- ENSURE_( SetApproximateSampleRate( pcm, hwParams, sampleRate ), paInvalidSampleRate );
- ENSURE_( snd_pcm_hw_params_set_channels( pcm, hwParams, channels ), paBadIODeviceCombination );
-
- ENSURE_( snd_pcm_hw_params_set_period_size_integer( pcm, hwParams ), paUnanticipatedHostError );
- ENSURE_( snd_pcm_hw_params_set_periods_integer( pcm, hwParams ), paUnanticipatedHostError );
-
- /* Using 5 as a base number of periods, we try to approximate the suggested latency (+1 period),
- finding a combination of period/buffer size which best fits these constraints */
- framesPerBuffer = bufferSize / 4;
- bufferSize += framesPerBuffer; /* One period doesn't count as latency */
- ENSURE_( snd_pcm_hw_params_set_buffer_size_near( pcm, hwParams, &bufferSize ), paUnanticipatedHostError );
- ENSURE_( snd_pcm_hw_params_set_period_size_near( pcm, hwParams, &framesPerBuffer, NULL ), paUnanticipatedHostError );
- }
-
- PA_UNLESS( framesPerBuffer != 0, paInternalError );
- *determinedFrames = framesPerBuffer;
-
-error:
- return result;
-}
-
-static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi,
- PaStream** s,
- const PaStreamParameters *inputParameters,
- const PaStreamParameters *outputParameters,
- double sampleRate,
- unsigned long framesPerBuffer,
- PaStreamFlags streamFlags,
- PaStreamCallback *callback,
- void *userData )
-{
- PaError result = paNoError;
- PaAlsaHostApiRepresentation *alsaHostApi = (PaAlsaHostApiRepresentation*)hostApi;
- PaAlsaStream *stream = NULL;
- PaSampleFormat hostInputSampleFormat = 0, hostOutputSampleFormat = 0;
- PaSampleFormat inputSampleFormat = 0, outputSampleFormat = 0;
- int numInputChannels = 0, numOutputChannels = 0;
- PaTime inputLatency, outputLatency;
- unsigned long framesPerHostBuffer;
- PaUtilHostBufferSizeMode hostBufferSizeMode = paUtilBoundedHostBufferSize;
- unsigned long maxHostBufferSize; /* Upper bound of host buffer size */
-
- if( (streamFlags & paPlatformSpecificFlags) != 0 )
- return paInvalidFlag;
-
- if( inputParameters )
- {
- PA_ENSURE( ValidateParameters( inputParameters, hostApi, StreamDirection_In ) );
-
- numInputChannels = inputParameters->channelCount;
- inputSampleFormat = inputParameters->sampleFormat;
- }
- if( outputParameters )
- {
- PA_ENSURE( ValidateParameters( outputParameters, hostApi, StreamDirection_Out ) );
-
- numOutputChannels = outputParameters->channelCount;
- outputSampleFormat = outputParameters->sampleFormat;
- }
-
- /* XXX: Why do we support this anyway? */
- if( framesPerBuffer == paFramesPerBufferUnspecified && getenv( "PA_ALSA_PERIODSIZE" ) != NULL )
- {
- PA_DEBUG(( "%s: Getting framesPerBuffer from environment\n", __FUNCTION__ ));
- framesPerBuffer = atoi( getenv("PA_ALSA_PERIODSIZE") );
- }
- framesPerHostBuffer = framesPerBuffer;
-
- PA_UNLESS( stream = (PaAlsaStream*)PaUtil_AllocateMemory( sizeof(PaAlsaStream) ), paInsufficientMemory );
- PA_ENSURE( PaAlsaStream_Initialize( stream, alsaHostApi, inputParameters, outputParameters, sampleRate,
- framesPerBuffer, callback, streamFlags, userData ) );
-
- /* If the number of frames per buffer is unspecified, we have to come up with
- * one. This is both a blessing and a curse: a blessing because we can optimize
- * the number to best meet the requirements, but a curse because that's really
- * hard to do well. For this reason we also support an interface where the user
- * specifies these by setting environment variables. */
- if( framesPerBuffer == paFramesPerBufferUnspecified )
- {
- PA_ENSURE( DetermineFramesPerBuffer( stream, sampleRate, inputParameters, outputParameters, &framesPerHostBuffer,
- hostApi ) );
- }
-
- PA_ENSURE( PaAlsaStream_Configure( stream, inputParameters, outputParameters, sampleRate, framesPerHostBuffer,
- &inputLatency, &outputLatency, &maxHostBufferSize ) );
- hostInputSampleFormat = stream->capture.hostSampleFormat;
- hostOutputSampleFormat = stream->playback.hostSampleFormat;
-
- if( framesPerHostBuffer != framesPerBuffer )
- {
- PA_DEBUG(( "%s: Number of frames per user and host buffer differs\n", __FUNCTION__ ));
- }
-
- PA_ENSURE( PaUtil_InitializeBufferProcessor( &stream->bufferProcessor,
- numInputChannels, inputSampleFormat, hostInputSampleFormat,
- numOutputChannels, outputSampleFormat, hostOutputSampleFormat,
- sampleRate, streamFlags, framesPerBuffer, maxHostBufferSize,
- hostBufferSizeMode, callback, userData ) );
-
- /* Ok, buffer processor is initialized, now we can deduce it's latency */
- if( numInputChannels > 0 )
- stream->streamRepresentation.streamInfo.inputLatency = inputLatency + PaUtil_GetBufferProcessorInputLatency(
- &stream->bufferProcessor );
- if( numOutputChannels > 0 )
- stream->streamRepresentation.streamInfo.outputLatency = outputLatency + PaUtil_GetBufferProcessorOutputLatency(
- &stream->bufferProcessor );
-
- *s = (PaStream*)stream;
-
- return result;
-
-error:
- if( stream )
- PaAlsaStream_Terminate( stream );
-
- return result;
-}
-
-static PaError CloseStream( PaStream* s )
-{
- PaError result = paNoError;
- PaAlsaStream *stream = (PaAlsaStream*)s;
-
- PaUtil_TerminateBufferProcessor( &stream->bufferProcessor );
- PaUtil_TerminateStreamRepresentation( &stream->streamRepresentation );
-
- PaAlsaStream_Terminate( stream );
-
- return result;
-}
-
-static void SilenceBuffer( PaAlsaStream *stream )
-{
- const snd_pcm_channel_area_t *areas;
- snd_pcm_uframes_t frames = (snd_pcm_uframes_t)snd_pcm_avail_update( stream->playback.pcm ), offset;
-
- snd_pcm_mmap_begin( stream->playback.pcm, &areas, &offset, &frames );
- snd_pcm_areas_silence( areas, offset, stream->playback.numHostChannels, frames, stream->playback.nativeFormat );
- snd_pcm_mmap_commit( stream->playback.pcm, offset, frames );
-}
-
-/** Start/prepare pcm(s) for streaming.
- *
- * Depending on wether the stream is in callback or blocking mode, we will respectively start or simply
- * prepare the playback pcm. If the buffer has _not_ been primed, we will in callback mode prepare and
- * silence the buffer before starting playback. In blocking mode we simply prepare, as the playback will
- * be started automatically as the user writes to output.
- *
- * The capture pcm, however, will simply be prepared and started.
- *
- * PaAlsaStream::startMtx makes sure access is synchronized (useful in callback mode)
- */
-static PaError AlsaStart( PaAlsaStream *stream, int priming )
-{
- PaError result = paNoError;
-
- if( stream->playback.pcm )
- {
- if( stream->callbackMode )
- {
- if( !priming )
- {
- /* Buffer isn't primed, so prepare and silence */
- ENSURE_( snd_pcm_prepare( stream->playback.pcm ), paUnanticipatedHostError );
- SilenceBuffer( stream );
- }
- ENSURE_( snd_pcm_start( stream->playback.pcm ), paUnanticipatedHostError );
- }
- else
- ENSURE_( snd_pcm_prepare( stream->playback.pcm ), paUnanticipatedHostError );
- }
- if( stream->capture.pcm && !stream->pcmsSynced )
- {
- ENSURE_( snd_pcm_prepare( stream->capture.pcm ), paUnanticipatedHostError );
- /* For a blocking stream we want to start capture as well, since nothing will happen otherwise */
- ENSURE_( snd_pcm_start( stream->capture.pcm ), paUnanticipatedHostError );
- }
-
-end:
- return result;
-error:
- goto end;
-}
-
-/** Utility function for determining if pcms are in running state.
- *
- */
-static int IsRunning( PaAlsaStream *stream )
-{
- int result = 0;
-
- ASSERT_CALL_( pthread_mutex_lock( &stream->stateMtx ), 0 ); /* Synchronize access to pcm state */
- if( stream->capture.pcm )
- {
- snd_pcm_state_t capture_state = snd_pcm_state( stream->capture.pcm );
-
- if( capture_state == SND_PCM_STATE_RUNNING || capture_state == SND_PCM_STATE_XRUN
- || capture_state == SND_PCM_STATE_DRAINING )
- {
- result = 1;
- goto end;
- }
- }
-
- if( stream->playback.pcm )
- {
- snd_pcm_state_t playback_state = snd_pcm_state( stream->playback.pcm );
-
- if( playback_state == SND_PCM_STATE_RUNNING || playback_state == SND_PCM_STATE_XRUN
- || playback_state == SND_PCM_STATE_DRAINING )
- {
- result = 1;
- goto end;
- }
- }
-
-end:
- ASSERT_CALL_( pthread_mutex_unlock( &stream->stateMtx ), 0 );
-
- return result;
-}
-
-static PaError StartStream( PaStream *s )
-{
- PaError result = paNoError;
- PaAlsaStream *stream = (PaAlsaStream*)s;
- int streamStarted = 0; /* So we can know wether we need to take the stream down */
-
- /* Ready the processor */
- PaUtil_ResetBufferProcessor( &stream->bufferProcessor );
-
- /* Set now, so we can test for activity further down */
- stream->isActive = 1;
-
- if( stream->callbackMode )
- {
- int res = 0;
- PaTime pt = PaUtil_GetTime();
- struct timespec ts;
-
- PA_ENSURE( CreateCallbackThread( &stream->threading, &CallbackThreadFunc, stream ) );
- streamStarted = 1;
-
- /* Wait for stream to be started */
- ts.tv_sec = (time_t) floor( pt + 1 );
- ts.tv_nsec = (long) ((pt - floor( pt )) * 1000000000);
-
- /* Since we'll be holding a lock on the startMtx (when not waiting on the condition), IsRunning won't be checking
- * stream state at the same time as the callback thread affects it. We also check IsStreamActive, in the unlikely
- * case the callback thread exits in the meantime (the stream will be considered inactive after the thread exits) */
- ASSERT_CALL_( pthread_mutex_lock( &stream->startMtx ), 0 );
-
- /* Due to possible spurious wakeups, we enclose in a loop */
- while( !IsRunning( stream ) && IsStreamActive( s ) && !res )
- {
- res = pthread_cond_timedwait( &stream->startCond, &stream->startMtx, &ts );
- }
- ASSERT_CALL_( pthread_mutex_unlock( &stream->startMtx ), 0 );
-
- PA_UNLESS( !res || res == ETIMEDOUT, paInternalError );
- PA_DEBUG(( "%s: Waited for %g seconds for stream to start\n", __FUNCTION__, PaUtil_GetTime() - pt ));
-
- if( res == ETIMEDOUT )
- {
- PA_ENSURE( paTimedOut );
- }
- }
- else
- {
- PA_ENSURE( AlsaStart( stream, 0 ) );
- streamStarted = 1;
- }
-
-end:
- return result;
-error:
- if( streamStarted )
- AbortStream( stream );
- stream->isActive = 0;
-
- goto end;
-}
-
-static PaError AlsaStop( PaAlsaStream *stream, int abort )
-{
- PaError result = paNoError;
-
- if( abort )
- {
- if( stream->playback.pcm )
- ENSURE_( snd_pcm_drop( stream->playback.pcm ), paUnanticipatedHostError );
- if( stream->capture.pcm && !stream->pcmsSynced )
- ENSURE_( snd_pcm_drop( stream->capture.pcm ), paUnanticipatedHostError );
-
- PA_DEBUG(( "Dropped frames\n" ));
- }
- else
- {
- if( stream->playback.pcm )
- ENSURE_( snd_pcm_drain( stream->playback.pcm ), paUnanticipatedHostError );
- if( stream->capture.pcm && !stream->pcmsSynced )
- ENSURE_( snd_pcm_drain( stream->capture.pcm ), paUnanticipatedHostError );
- }
-
-end:
- return result;
-error:
- goto end;
-}
-
-/** Stop or abort stream.
- *
- * If a stream is in callback mode we will have to inspect wether the background thread has
- * finished, or we will have to take it out. In either case we join the thread before
- * returning. In blocking mode, we simply tell ALSA to stop abruptly (abort) or finish
- * buffers (drain)
- *
- * Stream will be considered inactive (!PaAlsaStream::isActive) after a call to this function
- */
-static PaError RealStop( PaAlsaStream *stream, int abort )
-{
- PaError result = paNoError;
-
- /* First deal with the callback thread, cancelling and/or joining
- * it if necessary
- */
- if( stream->callbackMode )
- {
- PaError threadRes, watchdogRes;
- stream->callbackAbort = abort;
-
- if( !abort )
- {
- PA_DEBUG(( "Stopping callback\n" ));
- stream->callbackStop = 1;
- }
- PA_ENSURE( KillCallbackThread( &stream->threading, !abort, &threadRes, &watchdogRes ) );
- if( threadRes != paNoError )
- PA_DEBUG(( "Callback thread returned: %d\n", threadRes ));
- if( watchdogRes != paNoError )
- PA_DEBUG(( "Watchdog thread returned: %d\n", watchdogRes ));
-
- stream->callbackStop = 0; /* The deed is done */
- stream->callback_finished = 0;
- }
- else
- {
- PA_ENSURE( AlsaStop( stream, abort ) );
- }
-
- stream->isActive = 0;
-
-end:
- return result;
-
-error:
- goto end;
-}
-
-static PaError StopStream( PaStream *s )
-{
- return RealStop( (PaAlsaStream *) s, 0 );
-}
-
-static PaError AbortStream( PaStream *s )
-{
- return RealStop( (PaAlsaStream * ) s, 1 );
-}
-
-/** The stream is considered stopped before StartStream, or AFTER a call to Abort/StopStream (callback
- * returning !paContinue is not considered)
- *
- */
-static PaError IsStreamStopped( PaStream *s )
-{
- PaAlsaStream *stream = (PaAlsaStream *)s;
-
- /* callback_finished indicates we need to join callback thread (ie. in Abort/StopStream) */
- return !IsStreamActive( s ) && !stream->callback_finished;
-}
-
-static PaError IsStreamActive( PaStream *s )
-{
- PaAlsaStream *stream = (PaAlsaStream*)s;
- return stream->isActive;
-}
-
-static PaTime GetStreamTime( PaStream *s )
-{
- PaAlsaStream *stream = (PaAlsaStream*)s;
-
- snd_timestamp_t timestamp;
- snd_pcm_status_t *status;
- snd_pcm_status_alloca( &status );
-
- /* TODO: what if we have both? does it really matter? */
-
- /* TODO: if running in callback mode, this will mean
- * libasound routines are being called from multiple threads.
- * need to verify that libasound is thread-safe. */
-
- if( stream->capture.pcm )
- {
- snd_pcm_status( stream->capture.pcm, status );
- }
- else if( stream->playback.pcm )
- {
- snd_pcm_status( stream->playback.pcm, status );
- }
-
- snd_pcm_status_get_tstamp( status, &timestamp );
- return timestamp.tv_sec + (PaTime)timestamp.tv_usec / 1000000.0;
-}
-
-static double GetStreamCpuLoad( PaStream* s )
-{
- PaAlsaStream *stream = (PaAlsaStream*)s;
-
- return PaUtil_GetCpuLoad( &stream->cpuLoadMeasurer );
-}
-
-static int SetApproximateSampleRate( snd_pcm_t *pcm, snd_pcm_hw_params_t *hwParams, double sampleRate )
-{
- unsigned long approx = (unsigned long) sampleRate;
- int dir = 0;
- double fraction = sampleRate - approx;
-
- assert( pcm && hwParams );
-
- if( fraction > 0.0 )
- {
- if( fraction > 0.5 )
- {
- ++approx;
- dir = -1;
- }
- else
- dir = 1;
- }
-
- return snd_pcm_hw_params_set_rate( pcm, hwParams, approx, dir );
-}
-
-/* Return exact sample rate in param sampleRate */
-static int GetExactSampleRate( snd_pcm_hw_params_t *hwParams, double *sampleRate )
-{
- unsigned int num, den;
- int err;
-
- assert( hwParams );
-
- err = snd_pcm_hw_params_get_rate_numden( hwParams, &num, &den );
- *sampleRate = (double) num / den;
-
- return err;
-}
-
-/* Utility functions for blocking/callback interfaces */
-
-/* Atomic restart of stream (we don't want the intermediate state visible) */
-static PaError AlsaRestart( PaAlsaStream *stream )
-{
- PaError result = paNoError;
-
- ASSERT_CALL_( pthread_mutex_lock( &stream->stateMtx ), 0 );
- PA_ENSURE( AlsaStop( stream, 0 ) );
- PA_ENSURE( AlsaStart( stream, 0 ) );
-
- PA_DEBUG(( "%s: Restarted audio\n", __FUNCTION__ ));
-
-error:
- ASSERT_CALL_( pthread_mutex_unlock( &stream->stateMtx ), 0 );
- return result;
-}
-
-/** Recover from xrun state.
- *
- */
-static PaError PaAlsaStream_HandleXrun( PaAlsaStream *self )
-{
- PaError result = paNoError;
- snd_pcm_status_t *st;
- PaTime now = PaUtil_GetTime();
- snd_timestamp_t t;
-
- snd_pcm_status_alloca( &st );
-
- if( self->playback.pcm )
- {
- snd_pcm_status( self->playback.pcm, st );
- if( snd_pcm_status_get_state( st ) == SND_PCM_STATE_XRUN )
- {
- snd_pcm_status_get_trigger_tstamp( st, &t );
- self->underrun = now * 1000 - ((PaTime) t.tv_sec * 1000 + (PaTime) t.tv_usec / 1000);
- }
- }
- if( self->capture.pcm )
- {
- snd_pcm_status( self->capture.pcm, st );
- if( snd_pcm_status_get_state( st ) == SND_PCM_STATE_XRUN )
- {
- snd_pcm_status_get_trigger_tstamp( st, &t );
- self->overrun = now * 1000 - ((PaTime) t.tv_sec * 1000 + (PaTime) t.tv_usec / 1000);
- }
- }
-
- PA_ENSURE( AlsaRestart( self ) );
-
-end:
- return result;
-error:
- goto end;
-}
-
-/** Decide if we should continue polling for specified direction, eventually adjust the poll timeout.
- *
- */
-static PaError ContinuePoll( const PaAlsaStream *stream, StreamDirection streamDir, int *pollTimeout, int *continuePoll )
-{
- PaError result = paNoError;
- snd_pcm_sframes_t delay, margin;
- int err;
- const PaAlsaStreamComponent *component = NULL, *otherComponent = NULL;
-
- *continuePoll = 1;
-
- if( StreamDirection_In == streamDir )
- {
- component = &stream->capture;
- otherComponent = &stream->playback;
- }
- else
- {
- component = &stream->playback;
- otherComponent = &stream->capture;
- }
-
- /* ALSA docs say that negative delay should indicate xrun, but in my experience snd_pcm_delay returns -EPIPE */
- if( (err = snd_pcm_delay( otherComponent->pcm, &delay )) < 0 )
- {
- if( err == -EPIPE )
- {
- /* Xrun */
- *continuePoll = 0;
- goto error;
- }
-
- ENSURE_( err, paUnanticipatedHostError );
- }
-
- if( StreamDirection_Out == streamDir )
- {
- /* Number of eligible frames before capture overrun */
- delay = otherComponent->bufferSize - delay;
- }
- margin = delay - otherComponent->framesPerBuffer / 2;
-
- if( margin < 0 )
- {
- PA_DEBUG(( "%s: Stopping poll for %s\n", __FUNCTION__, StreamDirection_In == streamDir ? "capture" : "playback" ));
- *continuePoll = 0;
- }
- else if( margin < otherComponent->framesPerBuffer )
- {
- *pollTimeout = CalculatePollTimeout( stream, margin );
- PA_DEBUG(( "%s: Trying to poll again for %s frames, pollTimeout: %d\n",
- __FUNCTION__, StreamDirection_In == streamDir ? "capture" : "playback", *pollTimeout ));
- }
-
-error:
- return result;
-}
-
-/* Callback interface */
-
-static void OnExit( void *data )
-{
- PaAlsaStream *stream = (PaAlsaStream *) data;
-
- assert( data );
-
- PaUtil_ResetCpuLoadMeasurer( &stream->cpuLoadMeasurer );
-
- stream->callback_finished = 1; /* Let the outside world know stream was stopped in callback */
- AlsaStop( stream, stream->callbackAbort );
- stream->callbackAbort = 0; /* Clear state */
-
- PA_DEBUG(( "OnExit: Stoppage\n" ));
-
- /* Eventually notify user all buffers have played */
- if( stream->streamRepresentation.streamFinishedCallback )
- stream->streamRepresentation.streamFinishedCallback( stream->streamRepresentation.userData );
- stream->isActive = 0;
-}
-
-static void CalculateTimeInfo( PaAlsaStream *stream, PaStreamCallbackTimeInfo *timeInfo )
-{
- snd_pcm_status_t *capture_status, *playback_status;
- snd_timestamp_t capture_timestamp, playback_timestamp;
- PaTime capture_time = 0., playback_time = 0.;
-
- snd_pcm_status_alloca( &capture_status );
- snd_pcm_status_alloca( &playback_status );
-
- if( stream->capture.pcm )
- {
- snd_pcm_sframes_t capture_delay;
-
- snd_pcm_status( stream->capture.pcm, capture_status );
- snd_pcm_status_get_tstamp( capture_status, &capture_timestamp );
-
- capture_time = capture_timestamp.tv_sec +
- ((PaTime)capture_timestamp.tv_usec / 1000000.0);
- timeInfo->currentTime = capture_time;
-
- capture_delay = snd_pcm_status_get_delay( capture_status );
- timeInfo->inputBufferAdcTime = timeInfo->currentTime -
- (PaTime)capture_delay / stream->streamRepresentation.streamInfo.sampleRate;
- }
- if( stream->playback.pcm )
- {
- snd_pcm_sframes_t playback_delay;
-
- snd_pcm_status( stream->playback.pcm, playback_status );
- snd_pcm_status_get_tstamp( playback_status, &playback_timestamp );
-
- playback_time = playback_timestamp.tv_sec +
- ((PaTime)playback_timestamp.tv_usec / 1000000.0);
-
- if( stream->capture.pcm ) /* Full duplex */
- {
- /* Hmm, we have both a playback and a capture timestamp.
- * Hopefully they are the same... */
- if( fabs( capture_time - playback_time ) > 0.01 )
- PA_DEBUG(("Capture time and playback time differ by %f\n", fabs(capture_time-playback_time)));
- }
- else
- timeInfo->currentTime = playback_time;
-
- playback_delay = snd_pcm_status_get_delay( playback_status );
- timeInfo->outputBufferDacTime = timeInfo->currentTime +
- (PaTime)playback_delay / stream->streamRepresentation.streamInfo.sampleRate;
- }
-}
-
-/** Called after buffer processing is finished.
- *
- * A number of mmapped frames is committed, it is possible that an xrun has occurred in the meantime.
- *
- * @param numFrames The number of frames that has been processed
- * @param xrun Return whether an xrun has occurred
- */
-static PaError PaAlsaStreamComponent_EndProcessing( PaAlsaStreamComponent *self, unsigned long numFrames, int *xrun )
-{
- PaError result = paNoError;
- int res;
-
- /* @concern FullDuplex It is possible that only one direction is marked ready after polling, and processed
- * afterwards
- */
- if( !self->ready )
- goto end;
-
- res = snd_pcm_mmap_commit( self->pcm, self->offset, numFrames );
- if( res == -EPIPE || res == -ESTRPIPE )
- {
- *xrun = 1;
- }
- else
- {
- ENSURE_( res, paUnanticipatedHostError );
- }
-
-end:
-error:
- return result;
-}
-
-/* Extract buffer from channel area */
-static unsigned char *ExtractAddress( const snd_pcm_channel_area_t *area, snd_pcm_uframes_t offset )
-{
- return (unsigned char *) area->addr + (area->first + offset * area->step) / 8;
-}
-
-/** Do necessary adaption between user and host channels.
- *
- @concern ChannelAdaption Adapting between user and host channels can involve silencing unused channels and
- duplicating mono information if host outputs come in pairs.
- */
-static PaError PaAlsaStreamComponent_DoChannelAdaption( PaAlsaStreamComponent *self, PaUtilBufferProcessor *bp, int numFrames )
-{
- PaError result = paNoError;
- unsigned char *p;
- int i;
- int unusedChans = self->numHostChannels - self->numUserChannels;
- unsigned char *src, *dst;
- int convertMono = (self->numHostChannels % 2) == 0 && (self->numUserChannels % 2) != 0;
-
- assert( StreamDirection_Out == self->streamDir );
-
- if( self->hostInterleaved )
- {
- int swidth = snd_pcm_format_size( self->nativeFormat, 1 );
- unsigned char *buffer = ExtractAddress( self->channelAreas, self->offset );
-
- /* Start after the last user channel */
- p = buffer + self->numUserChannels * swidth;
-
- if( convertMono )
- {
- /* Convert the last user channel into stereo pair */
- src = buffer + (self->numUserChannels - 1) * swidth;
- for( i = 0; i < numFrames; ++i )
- {
- dst = src + swidth;
- memcpy( dst, src, swidth );
- src += self->numHostChannels * swidth;
- }
-
- /* Don't touch the channel we just wrote to */
- p += swidth;
- --unusedChans;
- }
-
- if( unusedChans > 0 )
- {
- /* Silence unused output channels */
- for( i = 0; i < numFrames; ++i )
- {
- memset( p, 0, swidth * unusedChans );
- p += self->numHostChannels * swidth;
- }
- }
- }
- else
- {
- /* We extract the last user channel */
- if( convertMono )
- {
- ENSURE_( snd_pcm_area_copy( self->channelAreas + self->numUserChannels, self->offset, self->channelAreas +
- (self->numUserChannels - 1), self->offset, numFrames, self->nativeFormat ), paUnanticipatedHostError );
- --unusedChans;
- }
- if( unusedChans > 0 )
- {
- snd_pcm_areas_silence( self->channelAreas + (self->numHostChannels - unusedChans), self->offset, unusedChans, numFrames,
- self->nativeFormat );
- }
- }
-
-error:
- return result;
-}
-
-static PaError PaAlsaStream_EndProcessing( PaAlsaStream *self, unsigned long numFrames, int *xrunOccurred )
-{
- PaError result = paNoError;
- int xrun = 0;
-
- if( self->capture.pcm )
- {
- PA_ENSURE( PaAlsaStreamComponent_EndProcessing( &self->capture, numFrames, &xrun ) );
- }
- if( self->playback.pcm )
- {
- if( self->playback.numHostChannels > self->playback.numUserChannels )
- PA_ENSURE( PaAlsaStreamComponent_DoChannelAdaption( &self->playback, &self->bufferProcessor, numFrames ) );
- PA_ENSURE( PaAlsaStreamComponent_EndProcessing( &self->playback, numFrames, &xrun ) );
- }
-
-error:
- *xrunOccurred = xrun;
- return result;
-}
-
-/** Update the number of available frames.
- *
- */
-static PaError PaAlsaStreamComponent_GetAvailableFrames( PaAlsaStreamComponent *self, unsigned long *numFrames, int *xrunOccurred )
-{
- PaError result = paNoError;
- snd_pcm_sframes_t framesAvail = snd_pcm_avail_update( self->pcm );
- *xrunOccurred = 0;
-
- if( -EPIPE == framesAvail )
- {
- *xrunOccurred = 1;
- framesAvail = 0;
- }
- else
- ENSURE_( framesAvail, paUnanticipatedHostError );
-
- *numFrames = framesAvail;
-
-error:
- return result;
-}
-
-/** Fill in pollfd objects.
- */
-static PaError PaAlsaStreamComponent_BeginPolling( PaAlsaStreamComponent *self, struct pollfd *pfds )
-{
- PaError result = paNoError;
- int ret = snd_pcm_poll_descriptors( self->pcm, pfds, self->nfds );
- assert( ret == self->nfds );
-
- self->ready = 0;
-
- return result;
-}
-
-/** Examine results from poll().
- *
- * @param pfds pollfds to inspect
- * @param shouldPoll Should we continue to poll
- * @param xrun Has an xrun occurred
- */
-static PaError PaAlsaStreamComponent_EndPolling( PaAlsaStreamComponent *self, struct pollfd *pfds, int *shouldPoll, int *xrun )
-{
- PaError result = paNoError;
- unsigned short revents;
-
- ENSURE_( snd_pcm_poll_descriptors_revents( self->pcm, pfds, self->nfds, &revents ), paUnanticipatedHostError );
- if( revents != 0 )
- {
- if( revents & POLLERR )
- {
- *xrun = 1;
- }
- else
- self->ready = 1;
-
- *shouldPoll = 0;
- }
-
-error:
- return result;
-}
-
-/** Return the number of available frames for this stream.
- *
- * @concern FullDuplex The minimum available for the two directions is calculated, it might be desirable to ignore
- * one direction however (not marked ready from poll), so this is controlled by queryCapture and queryPlayback.
- *
- * @param queryCapture Check available for capture
- * @param queryPlayback Check available for playback
- * @param available The returned number of frames
- * @param xrunOccurred Return whether an xrun has occurred
- */
-static PaError PaAlsaStream_GetAvailableFrames( PaAlsaStream *self, int queryCapture, int queryPlayback, unsigned long
- *available, int *xrunOccurred )
-{
- PaError result = paNoError;
- unsigned long captureFrames, playbackFrames;
- *xrunOccurred = 0;
-
- assert( queryCapture || queryPlayback );
-
- if( queryCapture )
- {
- assert( self->capture.pcm );
- PA_ENSURE( PaAlsaStreamComponent_GetAvailableFrames( &self->capture, &captureFrames, xrunOccurred ) );
- if( *xrunOccurred )
- goto end;
- }
- if( queryPlayback )
- {
- assert( self->playback.pcm );
- PA_ENSURE( PaAlsaStreamComponent_GetAvailableFrames( &self->playback, &playbackFrames, xrunOccurred ) );
- if( *xrunOccurred )
- goto end;
- }
-
- if( queryCapture && queryPlayback )
- {
- *available = PA_MIN( captureFrames, playbackFrames );
- }
- else if( queryCapture )
- {
- *available = captureFrames;
- }
- else
- {
- *available = playbackFrames;
- }
-
-end:
-error:
- return result;
-}
-
-/** Wait for and report available buffer space from ALSA.
- *
- * Unless ALSA reports a minimum of frames available for I/O, we poll the ALSA filedescriptors for more.
- * Both of these operations can uncover xrun conditions.
- *
- * @concern Xruns Both polling and querying available frames can report an xrun condition.
- *
- * @param framesAvail Return the number of available frames
- * @param xrunOccurred Return whether an xrun has occurred
- */
-static PaError PaAlsaStream_WaitForFrames( PaAlsaStream *self, unsigned long *framesAvail, int *xrunOccurred )
-{
- PaError result = paNoError;
- int pollPlayback = self->playback.pcm != NULL, pollCapture = self->capture.pcm != NULL;
- int pollTimeout = self->pollTimeout;
- int xrun = 0;
-
- assert( self );
- assert( framesAvail );
-
- if( !self->callbackMode )
- {
- /* In blocking mode we will only wait if necessary */
- PA_ENSURE( PaAlsaStream_GetAvailableFrames( self, self->capture.pcm != NULL, self->playback.pcm != NULL,
- framesAvail, &xrun ) );
- if( xrun )
- {
- goto end;
- }
-
- if( *framesAvail > 0 )
- {
- /* Mark pcms ready from poll */
- if( self->capture.pcm )
- self->capture.ready = 1;
- if( self->playback.pcm )
- self->playback.ready = 1;
-
- goto end;
- }
- }
-
- while( pollPlayback || pollCapture )
- {
- int totalFds = 0;
- struct pollfd *capturePfds = NULL, *playbackPfds = NULL;
-
- pthread_testcancel();
-
- if( pollCapture )
- {
- capturePfds = self->pfds;
- PA_ENSURE( PaAlsaStreamComponent_BeginPolling( &self->capture, capturePfds ) );
- totalFds += self->capture.nfds;
- }
- if( pollPlayback )
- {
- playbackPfds = self->pfds + (self->capture.pcm ? self->capture.nfds : 0);
- PA_ENSURE( PaAlsaStreamComponent_BeginPolling( &self->playback, playbackPfds ) );
- totalFds += self->playback.nfds;
- }
-
- if( poll( self->pfds, totalFds, pollTimeout ) < 0 )
- {
- /* XXX: Depend on preprocessor condition? */
- if( errno == EINTR ) { /* gdb */
- continue;
- }
-
- /* TODO: Add macro for checking system calls */
- PA_ENSURE( paInternalError );
- }
-
- /* check the return status of our pfds */
- if( pollCapture )
- {
- PA_ENSURE( PaAlsaStreamComponent_EndPolling( &self->capture, capturePfds, &pollCapture, &xrun ) );
- }
- if( pollPlayback )
- {
- PA_ENSURE( PaAlsaStreamComponent_EndPolling( &self->playback, playbackPfds, &pollPlayback, &xrun ) );
- }
- if( xrun )
- {
- break;
- }
-
- /* @concern FullDuplex If only one of two pcms is ready we may want to compromise between the two.
- * If there is less than half a period's worth of samples left of frames in the other pcm's buffer we will
- * stop polling.
- */
- if( self->capture.pcm && self->playback.pcm )
- {
- if( pollCapture && !pollPlayback )
- {
- PA_ENSURE( ContinuePoll( self, StreamDirection_In, &pollTimeout, &pollCapture ) );
- }
- else if( pollPlayback && !pollCapture )
- {
- PA_ENSURE( ContinuePoll( self, StreamDirection_Out, &pollTimeout, &pollPlayback ) );
- }
- }
- }
-
- if( !xrun )
- {
- /* Get the number of available frames for the pcms that are marked ready.
- * @concern FullDuplex If only one direction is marked ready (from poll), the number of frames available for
- * the other direction is returned. This under the assumption that input is dropped earlier if paNeverDropInput
- * is not specified.
- */
- int captureReady = self->capture.pcm ? self->capture.ready : 0,
- playbackReady = self->playback.pcm ? self->playback.ready : 0;
- PA_ENSURE( PaAlsaStream_GetAvailableFrames( self, captureReady, playbackReady, framesAvail, &xrun ) );
-
- if( self->capture.pcm && self->playback.pcm )
- {
- if( !self->playback.ready && !self->neverDropInput )
- {
- /* TODO: Drop input */
- }
- }
- }
-
-end:
-error:
- if( xrun )
- {
- /* Recover from the xrun state */
- PA_ENSURE( PaAlsaStream_HandleXrun( self ) );
- *framesAvail = 0;
- }
- *xrunOccurred = xrun;
-
- return result;
-}
-
-/** Register per-channel ALSA buffer information with buffer processor.
- *
- * Mmapped buffer space is acquired from ALSA, and registered with the buffer processor. Differences between the
- * number of host and user channels is taken into account.
- *
- * @param numFrames On entrance the number of requested frames, on exit the number of contiguously accessible frames.
- */
-static PaError PaAlsaStreamComponent_RegisterChannels( PaAlsaStreamComponent *self, PaUtilBufferProcessor *bp,
- unsigned long *numFrames, int *xrun )
-{
- PaError result = paNoError;
- const snd_pcm_channel_area_t *areas, *area;
- void (*setChannel)(PaUtilBufferProcessor *, unsigned int, void *, unsigned int) =
- StreamDirection_In == self->streamDir ? PaUtil_SetInputChannel : PaUtil_SetOutputChannel;
- unsigned char *buffer, *p;
- int i;
- unsigned long framesAvail;
-
- /* This _must_ be called before mmap_begin */
- PA_ENSURE( PaAlsaStreamComponent_GetAvailableFrames( self, &framesAvail, xrun ) );
- if( *xrun )
- {
- *numFrames = 0;
- goto end;
- }
-
- ENSURE_( snd_pcm_mmap_begin( self->pcm, &areas, &self->offset, numFrames ), paUnanticipatedHostError );
-
- if( self->hostInterleaved )
- {
- int swidth = snd_pcm_format_size( self->nativeFormat, 1 );
-
- p = buffer = ExtractAddress( areas, self->offset );
- for( i = 0; i < self->numUserChannels; ++i )
- {
- /* We're setting the channels up to userChannels, but the stride will be hostChannels samples */
- setChannel( bp, i, p, self->numHostChannels );
- p += swidth;
- }
- }
- else
- {
- for( i = 0; i < self->numUserChannels; ++i )
- {
- area = areas + i;
- buffer = ExtractAddress( area, self->offset );
- setChannel( bp, i, buffer, 1 );
- }
- }
-
- /* @concern ChannelAdaption Buffer address is recorded so we can do some channel adaption later */
- self->channelAreas = (snd_pcm_channel_area_t *)areas;
-
-end:
-error:
- return result;
-}
-
-/** Initiate buffer processing.
- *
- * ALSA buffers are registered with the PA buffer processor and the buffer size (in frames) set.
- *
- * @concern FullDuplex If both directions are being processed, the minimum amount of frames for the two directions is
- * calculated.
- *
- * @param numFrames On entrance the number of available frames, on exit the number of received frames
- * @param xrunOccurred Return whether an xrun has occurred
- */
-static PaError PaAlsaStream_SetUpBuffers( PaAlsaStream *self, unsigned long *numFrames, int *xrunOccurred )
-{
- PaError result = paNoError;
- unsigned long captureFrames = ULONG_MAX, playbackFrames = ULONG_MAX, commonFrames = 0;
- int xrun = 0;
-
- /* Extract per-channel ALSA buffer pointers and register them with the buffer processor.
- * It is possible that a direction is not marked ready however, because it is out of sync with the other.
- */
- if( self->capture.pcm && self->capture.ready )
- {
- captureFrames = *numFrames;
- PA_ENSURE( PaAlsaStreamComponent_RegisterChannels( &self->capture, &self->bufferProcessor, &captureFrames,
- &xrun ) );
- }
- if( self->playback.pcm && self->playback.ready )
- {
- playbackFrames = *numFrames;
- PA_ENSURE( PaAlsaStreamComponent_RegisterChannels( &self->playback, &self->bufferProcessor, &playbackFrames,
- &xrun ) );
- }
- if( xrun )
- {
- /* Nothing more to do */
- assert( 0 == commonFrames );
- goto end;
- }
-
- commonFrames = PA_MIN( captureFrames, playbackFrames );
- assert( commonFrames <= *numFrames );
-
- /* Inform PortAudio of the number of frames we got.
- * @concern FullDuplex We might be experiencing underflow in either end; if its an input underflow, we go on
- * with output. If its output underflow however, depending on the paNeverDropInput flag, we may want to simply
- * discard the excess input or call the callback with paOutputOverflow flagged.
- */
- if( self->capture.pcm )
- {
- if( self->capture.ready )
- {
- PaUtil_SetInputFrameCount( &self->bufferProcessor, commonFrames );
- }
- else
- {
- /* We have input underflow */
- PaUtil_SetNoInput( &self->bufferProcessor );
- }
- }
- if( self->playback.pcm )
- {
- if( self->playback.ready )
- {
- PaUtil_SetOutputFrameCount( &self->bufferProcessor, commonFrames );
- }
- else
- {
- /* We have output underflow, but keeping input data (paNeverDropInput) */
- /* assert( self->neverDropInput ); */
- PaUtil_SetNoOutput( &self->bufferProcessor );
- }
- }
-
-end:
- *numFrames = commonFrames;
-error:
- if( xrun )
- {
- PA_ENSURE( PaAlsaStream_HandleXrun( self ) );
- *numFrames = 0;
- }
- *xrunOccurred = xrun;
-
- return result;
-}
-
-/** Callback thread's function.
- *
- * Roughly, the workflow can be described in the following way: The number of available frames that can be processed
- * directly is obtained from ALSA, we then request as much directly accessible memory as possible within this amount
- * from ALSA. The buffer memory is registered with the PA buffer processor and processing is carried out with
- * PaUtil_EndBufferProcessing. Finally, the number of processed frames is reported to ALSA. The processing can
- * happen in several iterations untill we have consumed the known number of available frames (or an xrun is detected).
- */
-static void *CallbackThreadFunc( void *userData )
-{
- PaError result = paNoError, *pres = NULL;
- PaAlsaStream *stream = (PaAlsaStream*) userData;
- PaStreamCallbackTimeInfo timeInfo = {0, 0, 0};
- snd_pcm_sframes_t startThreshold = 0;
- int callbackResult = paContinue;
- PaStreamCallbackFlags cbFlags = 0; /* We might want to keep state across iterations */
- int streamStarted = 0;
-
- assert( stream );
-
- callbackThread_ = pthread_self();
- /* Execute OnExit when exiting */
- pthread_cleanup_push( &OnExit, stream );
-
- /* Not implemented */
- assert( !stream->primeBuffers );
-
- /* @concern StreamStart If the output is being primed the output pcm needs to be prepared, otherwise the
- * stream is started immediately. The latter involves signaling the waiting main thread.
- */
- if( stream->primeBuffers )
- {
- snd_pcm_sframes_t avail;
-
- if( stream->playback.pcm )
- ENSURE_( snd_pcm_prepare( stream->playback.pcm ), paUnanticipatedHostError );
- if( stream->capture.pcm && !stream->pcmsSynced )
- ENSURE_( snd_pcm_prepare( stream->capture.pcm ), paUnanticipatedHostError );
-
- /* We can't be certain that the whole ring buffer is available for priming, but there should be
- * at least one period */
- avail = snd_pcm_avail_update( stream->playback.pcm );
- startThreshold = avail - (avail % stream->playback.framesPerBuffer);
- assert( startThreshold >= stream->playback.framesPerBuffer );
- }
- else
- {
- ASSERT_CALL_( pthread_mutex_lock( &stream->startMtx ), 0 );
- PA_ENSURE( AlsaStart( stream, 0 ) ); /* Buffer will be zeroed */
- ASSERT_CALL_( pthread_cond_signal( &stream->startCond ), 0 );
- ASSERT_CALL_( pthread_mutex_unlock( &stream->startMtx ), 0 );
-
- streamStarted = 1;
- }
-
- while( 1 )
- {
- unsigned long framesAvail, framesGot;
- int xrun = 0;
-
- pthread_testcancel();
-
- /* @concern StreamStop if the main thread has requested a stop and the stream has not been effectively
- * stopped we signal this condition by modifying callbackResult (we'll want to flush buffered output).
- */
- if( stream->callbackStop && paContinue == callbackResult )
- {
- PA_DEBUG(( "Setting callbackResult to paComplete\n" ));
- callbackResult = paComplete;
- }
-
- if( paContinue != callbackResult )
- {
- stream->callbackAbort = (paAbort == callbackResult);
- if( stream->callbackAbort ||
- /** @concern BlockAdaption Go on if adaption buffers are empty */
- PaUtil_IsBufferProcessorOutputEmpty( &stream->bufferProcessor ) )
- goto end;
-
- PA_DEBUG(( "%s: Flushing buffer processor\n", __FUNCTION__ ));
- /* There is still buffered output that needs to be processed */
- }
-
- /* Wait for data to become available, this comes down to polling the ALSA file descriptors untill we have
- * a number of available frames.
- */
- PA_ENSURE( PaAlsaStream_WaitForFrames( stream, &framesAvail, &xrun ) );
- if( xrun )
- {
- assert( 0 == framesAvail );
- continue;
-
- /* XXX: Report xruns to the user? A situation is conceivable where the callback is never invoked due
- * to constant xruns, it might be desirable to notify the user of this.
- */
- }
-
- /* Consume buffer space. Once we have a number of frames available for consumption we must retrieve the
- * mmapped buffers from ALSA, this is contiguously accessible memory however, so we may receive smaller
- * portions at a time than is available as a whole. Therefore we should be prepared to process several
- * chunks successively. The buffers are passed to the PA buffer processor.
- */
- while( framesAvail > 0 )
- {
- xrun = 0;
-
- pthread_testcancel();
-
- /** @concern Xruns Under/overflows are to be reported to the callback */
- if( stream->underrun > 0.0 )
- {
- cbFlags |= paOutputUnderflow;
- stream->underrun = 0.0;
- }
- if( stream->overrun > 0.0 )
- {
- cbFlags |= paInputOverflow;
- stream->overrun = 0.0;
- }
- if( stream->capture.pcm && stream->playback.pcm )
- {
- /** @concern FullDuplex It's possible that only one direction is being processed to avoid an
- * under- or overflow, this should be reported correspondingly */
- if( !stream->capture.ready )
- {
- cbFlags |= paInputUnderflow;
- PA_DEBUG(( "%s: Input underflow\n", __FUNCTION__ ));
- }
- else if( !stream->playback.ready )
- {
- cbFlags |= paOutputOverflow;
- PA_DEBUG(( "%s: Output overflow\n", __FUNCTION__ ));
- }
- }
-
- CallbackUpdate( &stream->threading );
- CalculateTimeInfo( stream, &timeInfo );
- PaUtil_BeginBufferProcessing( &stream->bufferProcessor, &timeInfo, cbFlags );
- cbFlags = 0;
-
- /* CPU load measurement should include processing activivity external to the stream callback */
- PaUtil_BeginCpuLoadMeasurement( &stream->cpuLoadMeasurer );
-
- framesGot = framesAvail;
- PA_ENSURE( PaAlsaStream_SetUpBuffers( stream, &framesGot, &xrun ) );
- framesAvail -= framesGot;
-
- if( framesGot > 0 )
- {
- assert( !xrun );
-
- PaUtil_EndBufferProcessing( &stream->bufferProcessor, &callbackResult );
- PA_ENSURE( PaAlsaStream_EndProcessing( stream, framesGot, &xrun ) );
- }
- PaUtil_EndCpuLoadMeasurement( &stream->cpuLoadMeasurer, framesGot );
-
- if( framesGot == 0 )
- {
- if( !xrun )
- PA_DEBUG(( "%s: Received less frames than reported from ALSA!\n", __FUNCTION__ ));
-
- /* Go back to polling for more frames */
- break;
-
- }
-
- if( paContinue != callbackResult )
- break;
- }
- }
-
- /* Match pthread_cleanup_push */
- pthread_cleanup_pop( 1 );
-
-end:
- pthread_exit( pres );
-
-error:
- /* Pass on error code */
- pres = malloc( sizeof (PaError) );
- *pres = result;
-
- goto end;
-}
-
-/* Blocking interface */
-
-static PaError ReadStream( PaStream* s, void *buffer, unsigned long frames )
-{
- PaError result = paNoError;
- PaAlsaStream *stream = (PaAlsaStream*)s;
- unsigned long framesGot, framesAvail;
- void *userBuffer;
- snd_pcm_t *save = stream->playback.pcm;
-
- assert( stream );
-
- PA_UNLESS( stream->capture.pcm, paCanNotReadFromAnOutputOnlyStream );
-
- /* Disregard playback */
- stream->playback.pcm = NULL;
-
- if( stream->overrun > 0. )
- {
- result = paInputOverflowed;
- stream->overrun = 0.0;
- }
-
- if( stream->capture.userInterleaved )
- userBuffer = buffer;
- else
- {
- /* Copy channels into local array */
- userBuffer = stream->capture.userBuffers;
- memcpy( userBuffer, buffer, sizeof (void *) * stream->capture.numUserChannels );
- }
-
- /* Start stream if in prepared state */
- if( snd_pcm_state( stream->capture.pcm ) == SND_PCM_STATE_PREPARED )
- {
- ENSURE_( snd_pcm_start( stream->capture.pcm ), paUnanticipatedHostError );
- }
-
- while( frames > 0 )
- {
- int xrun = 0;
- PA_ENSURE( PaAlsaStream_WaitForFrames( stream, &framesAvail, &xrun ) );
- framesGot = PA_MIN( framesAvail, frames );
-
- PA_ENSURE( PaAlsaStream_SetUpBuffers( stream, &framesGot, &xrun ) );
- if( framesGot > 0 )
- {
- framesGot = PaUtil_CopyInput( &stream->bufferProcessor, &userBuffer, framesGot );
- PA_ENSURE( PaAlsaStream_EndProcessing( stream, framesGot, &xrun ) );
- frames -= framesGot;
- }
- }
-
-end:
- stream->playback.pcm = save;
- return result;
-error:
- goto end;
-}
-
-static PaError WriteStream( PaStream* s, const void *buffer, unsigned long frames )
-{
- PaError result = paNoError;
- signed long err;
- PaAlsaStream *stream = (PaAlsaStream*)s;
- snd_pcm_uframes_t framesGot, framesAvail;
- const void *userBuffer;
- snd_pcm_t *save = stream->capture.pcm;
-
- assert( stream );
-
- PA_UNLESS( stream->playback.pcm, paCanNotWriteToAnInputOnlyStream );
-
- /* Disregard capture */
- stream->capture.pcm = NULL;
-
- if( stream->underrun > 0. )
- {
- result = paOutputUnderflowed;
- stream->underrun = 0.0;
- }
-
- if( stream->playback.userInterleaved )
- userBuffer = buffer;
- else /* Copy channels into local array */
- {
- userBuffer = stream->playback.userBuffers;
- memcpy( (void *)userBuffer, buffer, sizeof (void *) * stream->playback.numUserChannels );
- }
-
- while( frames > 0 )
- {
- int xrun = 0;
- snd_pcm_uframes_t hwAvail;
-
- PA_ENSURE( PaAlsaStream_WaitForFrames( stream, &framesAvail, &xrun ) );
- framesGot = PA_MIN( framesAvail, frames );
-
- PA_ENSURE( PaAlsaStream_SetUpBuffers( stream, &framesGot, &xrun ) );
- if( framesGot > 0 )
- {
- framesGot = PaUtil_CopyOutput( &stream->bufferProcessor, &userBuffer, framesGot );
- PA_ENSURE( PaAlsaStream_EndProcessing( stream, framesGot, &xrun ) );
- frames -= framesGot;
- }
-
- /* Frames residing in buffer */
- PA_ENSURE( err = GetStreamWriteAvailable( stream ) );
- framesAvail = err;
- hwAvail = stream->playback.bufferSize - framesAvail;
-
- /* Start stream after one period of samples worth */
- if( snd_pcm_state( stream->playback.pcm ) == SND_PCM_STATE_PREPARED &&
- hwAvail >= stream->playback.framesPerBuffer )
- {
- ENSURE_( snd_pcm_start( stream->playback.pcm ), paUnanticipatedHostError );
- }
- }
-
-end:
- stream->capture.pcm = save;
- return result;
-error:
- goto end;
-}
-
-/* Return frames available for reading. In the event of an overflow, the capture pcm will be restarted */
-static signed long GetStreamReadAvailable( PaStream* s )
-{
- PaError result = paNoError;
- PaAlsaStream *stream = (PaAlsaStream*)s;
- unsigned long avail;
- int xrun;
-
- PA_ENSURE( PaAlsaStreamComponent_GetAvailableFrames( &stream->capture, &avail, &xrun ) );
- if( xrun )
- {
- PA_ENSURE( PaAlsaStream_HandleXrun( stream ) );
- PA_ENSURE( PaAlsaStreamComponent_GetAvailableFrames( &stream->capture, &avail, &xrun ) );
- if( xrun )
- PA_ENSURE( paInputOverflowed );
- }
-
- return (signed long)avail;
-
-error:
- return result;
-}
-
-static signed long GetStreamWriteAvailable( PaStream* s )
-{
- PaError result = paNoError;
- PaAlsaStream *stream = (PaAlsaStream*)s;
- unsigned long avail;
- int xrun;
-
- PA_ENSURE( PaAlsaStreamComponent_GetAvailableFrames( &stream->playback, &avail, &xrun ) );
- if( xrun )
- {
- snd_pcm_sframes_t savail;
-
- PA_ENSURE( PaAlsaStream_HandleXrun( stream ) );
- savail = snd_pcm_avail_update( stream->playback.pcm );
-
- /* savail should not contain -EPIPE now, since PaAlsaStream_HandleXrun will only prepare the pcm */
- ENSURE_( savail, paUnanticipatedHostError );
-
- avail = (unsigned long) savail;
- }
-
- return (signed long)avail;
-
-error:
- return result;
-}
-
-/* Extensions */
-
-/* Initialize host api specific structure */
-void PaAlsa_InitializeStreamInfo( PaAlsaStreamInfo *info )
-{
- info->size = sizeof (PaAlsaStreamInfo);
- info->hostApiType = paALSA;
- info->version = 1;
- info->deviceString = NULL;
-}
-
-void PaAlsa_EnableRealtimeScheduling( PaStream *s, int enable )
-{
- PaAlsaStream *stream = (PaAlsaStream *) s;
- stream->threading.rtSched = enable;
-}
-
-void PaAlsa_EnableWatchdog( PaStream *s, int enable )
-{
- PaAlsaStream *stream = (PaAlsaStream *) s;
- stream->threading.useWatchdog = enable;
-}
+/*
+ * $Id: pa_linux_alsa.c,v 1.1.2.71 2005/04/15 18:20:18 aknudsen Exp $
+ * PortAudio Portable Real-Time Audio Library
+ * Latest Version at: http://www.portaudio.com
+ * ALSA implementation by Joshua Haberman and Arve Knudsen
+ *
+ * Copyright (c) 2002 Joshua Haberman <joshua@haberman.com>
+ * Copyright (c) 2005 Arve Knudsen <aknuds-1@broadpark.no>
+ *
+ * Based on the Open Source API proposed by Ross Bencina
+ * Copyright (c) 1999-2002 Ross Bencina, Phil Burk
+ *
+ * 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.
+ *
+ * Any person wishing to distribute modifications to the Software is
+ * requested to send the modifications to the original developer so that
+ * they can be incorporated into the canonical version.
+ *
+ * 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.
+ */
+
+#define ALSA_PCM_NEW_HW_PARAMS_API
+#define ALSA_PCM_NEW_SW_PARAMS_API
+#include <alsa/asoundlib.h>
+#undef ALSA_PCM_NEW_HW_PARAMS_API
+#undef ALSA_PCM_NEW_SW_PARAMS_API
+
+#include <sys/poll.h>
+#include <string.h> /* strlen() */
+#include <limits.h>
+#include <math.h>
+#include <pthread.h>
+#include <signal.h>
+#include <time.h>
+#include <sys/mman.h>
+#include <signal.h> /* For sig_atomic_t */
+
+#include "portaudio.h"
+#include "pa_util.h"
+#include "pa_unix_util.h"
+#include "pa_allocation.h"
+#include "pa_hostapi.h"
+#include "pa_stream.h"
+#include "pa_cpuload.h"
+#include "pa_process.h"
+
+#include "pa_linux_alsa.h"
+
+/* Check return value of ALSA function, and map it to PaError */
+#define ENSURE_(expr, code) \
+ do { \
+ if( UNLIKELY( (aErr_ = (expr)) < 0 ) ) \
+ { \
+ /* PaUtil_SetLastHostErrorInfo should only be used in the main thread */ \
+ if( (code) == paUnanticipatedHostError && pthread_self() != callbackThread_ ) \
+ { \
+ PaUtil_SetLastHostErrorInfo( paALSA, aErr_, snd_strerror( aErr_ ) ); \
+ } \
+ PaUtil_DebugPrint(( "Expression '" #expr "' failed in '" __FILE__ "', line: " STRINGIZE( __LINE__ ) "\n" )); \
+ result = (code); \
+ goto error; \
+ } \
+ } while( 0 );
+
+#define ASSERT_CALL_(expr, success) \
+ aErr_ = (expr); \
+ assert( aErr_ == success );
+
+static int aErr_; /* Used with ENSURE_ */
+static pthread_t callbackThread_;
+
+typedef enum
+{
+ StreamDirection_In,
+ StreamDirection_Out
+} StreamDirection;
+
+/* Threading utility struct */
+typedef struct PaAlsaThreading
+{
+ pthread_t watchdogThread;
+ pthread_t callbackThread;
+ int watchdogRunning;
+ int rtSched;
+ int rtPrio;
+ int useWatchdog;
+ unsigned long throttledSleepTime;
+ volatile PaTime callbackTime;
+ volatile PaTime callbackCpuTime;
+ PaUtilCpuLoadMeasurer *cpuLoadMeasurer;
+} PaAlsaThreading;
+
+typedef struct
+{
+ PaSampleFormat hostSampleFormat;
+ unsigned long framesPerBuffer;
+ int numUserChannels, numHostChannels;
+ int userInterleaved, hostInterleaved;
+
+ snd_pcm_t *pcm;
+ snd_pcm_uframes_t bufferSize;
+ snd_pcm_format_t nativeFormat;
+ unsigned int nfds;
+ int ready; /* Marked ready from poll */
+ void **userBuffers;
+ snd_pcm_uframes_t offset;
+ StreamDirection streamDir;
+
+ snd_pcm_channel_area_t *channelAreas; /* Needed for channel adaption */
+} PaAlsaStreamComponent;
+
+/* Implementation specific stream structure */
+typedef struct PaAlsaStream
+{
+ PaUtilStreamRepresentation streamRepresentation;
+ PaUtilCpuLoadMeasurer cpuLoadMeasurer;
+ PaUtilBufferProcessor bufferProcessor;
+ PaAlsaThreading threading;
+
+ unsigned long framesPerUserBuffer;
+
+ int primeBuffers;
+ int callbackMode; /* bool: are we running in callback mode? */
+ int pcmsSynced; /* Have we successfully synced pcms */
+
+ /* the callback thread uses these to poll the sound device(s), waiting
+ * for data to be ready/available */
+ struct pollfd *pfds;
+ int pollTimeout;
+
+ /* Used in communication between threads */
+ volatile sig_atomic_t callback_finished; /* bool: are we in the "callback finished" state? */
+ volatile sig_atomic_t callbackAbort; /* Drop frames? */
+ volatile sig_atomic_t callbackStop; /* Signal a stop */
+ volatile sig_atomic_t isActive; /* Is stream in active state? (Between StartStream and StopStream || !paContinue) */
+ pthread_mutex_t stateMtx; /* Used to synchronize access to stream state */
+ pthread_mutex_t startMtx; /* Used to synchronize stream start in callback mode */
+ pthread_cond_t startCond; /* Wait untill audio is started in callback thread */
+
+ int neverDropInput;
+
+ PaTime underrun;
+ PaTime overrun;
+
+ PaAlsaStreamComponent capture, playback;
+}
+PaAlsaStream;
+
+/* PaAlsaHostApiRepresentation - host api datastructure specific to this implementation */
+
+typedef struct PaAlsaHostApiRepresentation
+{
+ PaUtilHostApiRepresentation commonHostApiRep;
+ PaUtilStreamInterface callbackStreamInterface;
+ PaUtilStreamInterface blockingStreamInterface;
+
+ PaUtilAllocationGroup *allocations;
+
+ PaHostApiIndex hostApiIndex;
+}
+PaAlsaHostApiRepresentation;
+
+typedef struct PaAlsaDeviceInfo
+{
+ PaDeviceInfo commonDeviceInfo;
+ char *alsaName;
+ int isPlug;
+ int minInputChannels;
+ int minOutputChannels;
+}
+PaAlsaDeviceInfo;
+
+/* Threading utilities */
+
+static void InitializeThreading( PaAlsaThreading *th, PaUtilCpuLoadMeasurer *clm )
+{
+ th->watchdogRunning = 0;
+ th->rtSched = 0;
+ th->callbackTime = 0;
+ th->callbackCpuTime = 0;
+ th->useWatchdog = 1;
+ th->throttledSleepTime = 0;
+ th->cpuLoadMeasurer = clm;
+
+ th->rtPrio = (sched_get_priority_max( SCHED_FIFO ) - sched_get_priority_min( SCHED_FIFO )) / 2
+ + sched_get_priority_min( SCHED_FIFO );
+}
+
+static PaError KillCallbackThread( PaAlsaThreading *th, int wait, PaError *exitResult, PaError *watchdogExitResult )
+{
+ PaError result = paNoError;
+ void *pret;
+
+ if( exitResult )
+ *exitResult = paNoError;
+ if( watchdogExitResult )
+ *watchdogExitResult = paNoError;
+
+ if( th->watchdogRunning )
+ {
+ pthread_cancel( th->watchdogThread );
+ ASSERT_CALL_( pthread_join( th->watchdogThread, &pret ), 0 );
+
+ if( pret && pret != PTHREAD_CANCELED )
+ {
+ if( watchdogExitResult )
+ *watchdogExitResult = *(PaError *) pret;
+ free( pret );
+ }
+ }
+
+ /* Only kill the thread if it isn't in the process of stopping (flushing adaptation buffers) */
+ /* TODO: Make join time out */
+ if( !wait )
+ pthread_cancel( th->callbackThread ); /* XXX: Safe to call this if the thread has exited on its own? */
+ ASSERT_CALL_( pthread_join( th->callbackThread, &pret ), 0 );
+
+ if( pret && pret != PTHREAD_CANCELED )
+ {
+ if( exitResult )
+ *exitResult = *(PaError *) pret;
+ free( pret );
+ }
+
+ return result;
+}
+
+static void OnWatchdogExit( void *userData )
+{
+ PaAlsaThreading *th = (PaAlsaThreading *) userData;
+ struct sched_param spm = { 0 };
+ assert( th );
+
+ ASSERT_CALL_( pthread_setschedparam( th->callbackThread, SCHED_OTHER, &spm ), 0 ); /* Lower before exiting */
+ PA_DEBUG(( "Watchdog exiting\n" ));
+}
+
+static PaError BoostPriority( PaAlsaThreading *th )
+{
+ PaError result = paNoError;
+ struct sched_param spm = { 0 };
+ spm.sched_priority = th->rtPrio;
+
+ assert( th );
+
+ if( pthread_setschedparam( th->callbackThread, SCHED_FIFO, &spm ) != 0 )
+ {
+ PA_UNLESS( errno == EPERM, paInternalError ); /* Lack permission to raise priority */
+ PA_DEBUG(( "Failed bumping priority\n" ));
+ result = 0;
+ }
+ else
+ result = 1; /* Success */
+error:
+ return result;
+}
+
+static void *WatchdogFunc( void *userData )
+{
+ PaError result = paNoError, *pres = NULL;
+ int err;
+ PaAlsaThreading *th = (PaAlsaThreading *) userData;
+ unsigned intervalMsec = 500;
+ const PaTime maxSeconds = 3.; /* Max seconds between callbacks */
+ PaTime timeThen = PaUtil_GetTime(), timeNow, timeElapsed, cpuTimeThen, cpuTimeNow, cpuTimeElapsed;
+ double cpuLoad, avgCpuLoad = 0.;
+ int throttled = 0;
+
+ assert( th );
+
+ pthread_cleanup_push( &OnWatchdogExit, th ); /* Execute OnWatchdogExit when exiting */
+
+ /* Boost priority of callback thread */
+ PA_ENSURE( result = BoostPriority( th ) );
+ if( !result )
+ {
+ pthread_exit( NULL ); /* Boost failed, might as well exit */
+ }
+
+ cpuTimeThen = th->callbackCpuTime;
+ {
+ int policy;
+ struct sched_param spm = { 0 };
+ pthread_getschedparam( pthread_self(), &policy, &spm );
+ PA_DEBUG(( "%s: Watchdog priority is %d\n", __FUNCTION__, spm.sched_priority ));
+ }
+
+ while( 1 )
+ {
+ double lowpassCoeff = 0.9, lowpassCoeff1 = 0.99999 - lowpassCoeff;
+
+ /* Test before and after in case whatever underlying sleep call isn't interrupted by pthread_cancel */
+ pthread_testcancel();
+ Pa_Sleep( intervalMsec );
+ pthread_testcancel();
+
+ if( PaUtil_GetTime() - th->callbackTime > maxSeconds )
+ {
+ PA_DEBUG(( "Watchdog: Terminating callback thread\n" ));
+ /* Tell thread to terminate */
+ err = pthread_kill( th->callbackThread, SIGKILL );
+ pthread_exit( NULL );
+ }
+
+ PA_DEBUG(( "%s: PortAudio reports CPU load: %g\n", __FUNCTION__, PaUtil_GetCpuLoad( th->cpuLoadMeasurer ) ));
+
+ /* Check if we should throttle, or unthrottle :P */
+ cpuTimeNow = th->callbackCpuTime;
+ cpuTimeElapsed = cpuTimeNow - cpuTimeThen;
+ cpuTimeThen = cpuTimeNow;
+
+ timeNow = PaUtil_GetTime();
+ timeElapsed = timeNow - timeThen;
+ timeThen = timeNow;
+ cpuLoad = cpuTimeElapsed / timeElapsed;
+ avgCpuLoad = avgCpuLoad * lowpassCoeff + cpuLoad * lowpassCoeff1;
+ /*
+ if( throttled )
+ PA_DEBUG(( "Watchdog: CPU load: %g, %g\n", avgCpuLoad, cpuTimeElapsed ));
+ */
+ if( PaUtil_GetCpuLoad( th->cpuLoadMeasurer ) > .925 )
+ {
+ static int policy;
+ static struct sched_param spm = { 0 };
+ static const struct sched_param defaultSpm = { 0 };
+ PA_DEBUG(( "%s: Throttling audio thread, priority %d\n", __FUNCTION__, spm.sched_priority ));
+
+ pthread_getschedparam( th->callbackThread, &policy, &spm );
+ if( !pthread_setschedparam( th->callbackThread, SCHED_OTHER, &defaultSpm ) )
+ {
+ throttled = 1;
+ }
+ else
+ PA_DEBUG(( "Watchdog: Couldn't lower priority of audio thread: %s\n", strerror( errno ) ));
+
+ /* Give other processes a go, before raising priority again */
+ PA_DEBUG(( "%s: Watchdog sleeping for %lu msecs before unthrottling\n", __FUNCTION__, th->throttledSleepTime ));
+ Pa_Sleep( th->throttledSleepTime );
+
+ /* Reset callback priority */
+ if( pthread_setschedparam( th->callbackThread, SCHED_FIFO, &spm ) != 0 )
+ {
+ PA_DEBUG(( "%s: Couldn't raise priority of audio thread: %s\n", __FUNCTION__, strerror( errno ) ));
+ }
+
+ if( PaUtil_GetCpuLoad( th->cpuLoadMeasurer ) >= .99 )
+ intervalMsec = 50;
+ else
+ intervalMsec = 100;
+
+ /*
+ lowpassCoeff = .97;
+ lowpassCoeff1 = .99999 - lowpassCoeff;
+ */
+ }
+ else if( throttled && avgCpuLoad < .8 )
+ {
+ intervalMsec = 500;
+ throttled = 0;
+
+ /*
+ lowpassCoeff = .9;
+ lowpassCoeff1 = .99999 - lowpassCoeff;
+ */
+ }
+ }
+
+ pthread_cleanup_pop( 1 ); /* Execute cleanup on exit */
+
+error:
+ /* Shouldn't get here in the normal case */
+
+ /* Pass on error code */
+ pres = malloc( sizeof (PaError) );
+ *pres = result;
+
+ pthread_exit( pres );
+}
+
+static PaError CreateCallbackThread( PaAlsaThreading *th, void *(*callbackThreadFunc)( void * ), PaStream *s )
+{
+ PaError result = paNoError;
+ pthread_attr_t attr;
+ int started = 0;
+
+#if defined _POSIX_MEMLOCK && (_POSIX_MEMLOCK != -1)
+ if( th->rtSched )
+ {
+ if( mlockall( MCL_CURRENT | MCL_FUTURE ) < 0 )
+ {
+ int savedErrno = errno; /* In case errno gets overwritten */
+ assert( savedErrno != EINVAL ); /* Most likely a programmer error */
+ PA_UNLESS( (savedErrno == EPERM), paInternalError );
+ PA_DEBUG(( "%s: Failed locking memory\n", __FUNCTION__ ));
+ }
+ else
+ PA_DEBUG(( "%s: Successfully locked memory\n", __FUNCTION__ ));
+ }
+#endif
+
+ PA_UNLESS( !pthread_attr_init( &attr ), paInternalError );
+ /* Priority relative to other processes */
+ PA_UNLESS( !pthread_attr_setscope( &attr, PTHREAD_SCOPE_SYSTEM ), paInternalError );
+
+ PA_UNLESS( !pthread_create( &th->callbackThread, &attr, callbackThreadFunc, s ), paInternalError );
+ started = 1;
+
+ if( th->rtSched )
+ {
+ if( th->useWatchdog )
+ {
+ int err;
+ struct sched_param wdSpm = { 0 };
+ /* Launch watchdog, watchdog sets callback thread priority */
+ int prio = PA_MIN( th->rtPrio + 4, sched_get_priority_max( SCHED_FIFO ) );
+ wdSpm.sched_priority = prio;
+
+ PA_UNLESS( !pthread_attr_init( &attr ), paInternalError );
+ PA_UNLESS( !pthread_attr_setinheritsched( &attr, PTHREAD_EXPLICIT_SCHED ), paInternalError );
+ PA_UNLESS( !pthread_attr_setscope( &attr, PTHREAD_SCOPE_SYSTEM ), paInternalError );
+ PA_UNLESS( !pthread_attr_setschedpolicy( &attr, SCHED_FIFO ), paInternalError );
+ PA_UNLESS( !pthread_attr_setschedparam( &attr, &wdSpm ), paInternalError );
+ if( (err = pthread_create( &th->watchdogThread, &attr, &WatchdogFunc, th )) )
+ {
+ PA_UNLESS( err == EPERM, paInternalError );
+ /* Permission error, go on without realtime privileges */
+ PA_DEBUG(( "Failed bumping priority\n" ));
+ }
+ else
+ {
+ int policy;
+ th->watchdogRunning = 1;
+ ASSERT_CALL_( pthread_getschedparam( th->watchdogThread, &policy, &wdSpm ), 0 );
+ /* Check if priority is right, policy could potentially differ from SCHED_FIFO (but that's alright) */
+ if( wdSpm.sched_priority != prio )
+ {
+ PA_DEBUG(( "Watchdog priority not set correctly (%d)\n", wdSpm.sched_priority ));
+ PA_ENSURE( paInternalError );
+ }
+ }
+ }
+ else
+ PA_ENSURE( BoostPriority( th ) );
+ }
+
+end:
+ return result;
+error:
+ if( started )
+ KillCallbackThread( th, 0, NULL, NULL );
+
+ goto end;
+}
+
+static void CallbackUpdate( PaAlsaThreading *th )
+{
+ th->callbackTime = PaUtil_GetTime();
+ th->callbackCpuTime = PaUtil_GetCpuLoad( th->cpuLoadMeasurer );
+}
+
+/* prototypes for functions declared in this file */
+
+static void Terminate( struct PaUtilHostApiRepresentation *hostApi );
+static PaError IsFormatSupported( struct PaUtilHostApiRepresentation *hostApi,
+ const PaStreamParameters *inputParameters,
+ const PaStreamParameters *outputParameters,
+ double sampleRate );
+static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi,
+ PaStream** s,
+ const PaStreamParameters *inputParameters,
+ const PaStreamParameters *outputParameters,
+ double sampleRate,
+ unsigned long framesPerBuffer,
+ PaStreamFlags streamFlags,
+ PaStreamCallback *callback,
+ void *userData );
+static PaError CloseStream( PaStream* stream );
+static PaError StartStream( PaStream *stream );
+static PaError StopStream( PaStream *stream );
+static PaError AbortStream( PaStream *stream );
+static PaError IsStreamStopped( PaStream *s );
+static PaError IsStreamActive( PaStream *stream );
+static PaTime GetStreamTime( PaStream *stream );
+static double GetStreamCpuLoad( PaStream* stream );
+static PaError BuildDeviceList( PaAlsaHostApiRepresentation *hostApi );
+static int SetApproximateSampleRate( snd_pcm_t *pcm, snd_pcm_hw_params_t *hwParams, double sampleRate );
+static int GetExactSampleRate( snd_pcm_hw_params_t *hwParams, double *sampleRate );
+
+/* Callback prototypes */
+static void *CallbackThreadFunc( void *userData );
+
+/* Blocking prototypes */
+static signed long GetStreamReadAvailable( PaStream* s );
+static signed long GetStreamWriteAvailable( PaStream* s );
+static PaError ReadStream( PaStream* stream, void *buffer, unsigned long frames );
+static PaError WriteStream( PaStream* stream, const void *buffer, unsigned long frames );
+
+
+static const PaAlsaDeviceInfo *GetDeviceInfo( const PaUtilHostApiRepresentation *hostApi, int device )
+{
+ return (const PaAlsaDeviceInfo *)hostApi->deviceInfos[device];
+}
+
+PaError PaAlsa_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiIndex hostApiIndex )
+{
+ PaError result = paNoError;
+ PaAlsaHostApiRepresentation *alsaHostApi = NULL;
+
+ PA_UNLESS( alsaHostApi = (PaAlsaHostApiRepresentation*) PaUtil_AllocateMemory(
+ sizeof(PaAlsaHostApiRepresentation) ), paInsufficientMemory );
+ PA_UNLESS( alsaHostApi->allocations = PaUtil_CreateAllocationGroup(), paInsufficientMemory );
+ alsaHostApi->hostApiIndex = hostApiIndex;
+
+ *hostApi = (PaUtilHostApiRepresentation*)alsaHostApi;
+ (*hostApi)->info.structVersion = 1;
+ (*hostApi)->info.type = paALSA;
+ (*hostApi)->info.name = "ALSA";
+
+ (*hostApi)->Terminate = Terminate;
+ (*hostApi)->OpenStream = OpenStream;
+ (*hostApi)->IsFormatSupported = IsFormatSupported;
+
+ PA_ENSURE( BuildDeviceList( alsaHostApi ) );
+
+ PaUtil_InitializeStreamInterface( &alsaHostApi->callbackStreamInterface,
+ CloseStream, StartStream,
+ StopStream, AbortStream,
+ IsStreamStopped, IsStreamActive,
+ GetStreamTime, GetStreamCpuLoad,
+ PaUtil_DummyRead, PaUtil_DummyWrite,
+ PaUtil_DummyGetReadAvailable,
+ PaUtil_DummyGetWriteAvailable );
+
+ PaUtil_InitializeStreamInterface( &alsaHostApi->blockingStreamInterface,
+ CloseStream, StartStream,
+ StopStream, AbortStream,
+ IsStreamStopped, IsStreamActive,
+ GetStreamTime, PaUtil_DummyGetCpuLoad,
+ ReadStream, WriteStream,
+ GetStreamReadAvailable,
+ GetStreamWriteAvailable );
+
+ return result;
+
+error:
+ if( alsaHostApi )
+ {
+ if( alsaHostApi->allocations )
+ {
+ PaUtil_FreeAllAllocations( alsaHostApi->allocations );
+ PaUtil_DestroyAllocationGroup( alsaHostApi->allocations );
+ }
+
+ PaUtil_FreeMemory( alsaHostApi );
+ }
+
+ return result;
+}
+
+static void Terminate( struct PaUtilHostApiRepresentation *hostApi )
+{
+ PaAlsaHostApiRepresentation *alsaHostApi = (PaAlsaHostApiRepresentation*)hostApi;
+
+ assert( hostApi );
+
+ if( alsaHostApi->allocations )
+ {
+ PaUtil_FreeAllAllocations( alsaHostApi->allocations );
+ PaUtil_DestroyAllocationGroup( alsaHostApi->allocations );
+ }
+
+ PaUtil_FreeMemory( alsaHostApi );
+ snd_config_update_free_global();
+}
+
+/*! Determine max channels and default latencies.
+ *
+ * This function provides functionality to grope an opened (might be opened for capture or playback) pcm device for
+ * traits like max channels, suitable default latencies and default sample rate. Upon error, max channels is set to zero,
+ * and a suitable result returned. The device is closed before returning.
+ */
+static PaError GropeDevice( snd_pcm_t *pcm, int *minChannels, int *maxChannels, double *defaultLowLatency,
+ double *defaultHighLatency, double *defaultSampleRate, int isPlug )
+{
+ PaError result = paNoError;
+ snd_pcm_hw_params_t *hwParams;
+ snd_pcm_uframes_t lowLatency = 512, highLatency = 2048;
+ unsigned int minChans, maxChans;
+ double defaultSr = *defaultSampleRate;
+
+ assert( pcm );
+
+ ENSURE_( snd_pcm_nonblock( pcm, 0 ), paUnanticipatedHostError );
+
+ snd_pcm_hw_params_alloca( &hwParams );
+ snd_pcm_hw_params_any( pcm, hwParams );
+
+ if( defaultSr >= 0 )
+ {
+ /* Could be that the device opened in one mode supports samplerates that the other mode wont have,
+ * so try again .. */
+ if( SetApproximateSampleRate( pcm, hwParams, defaultSr ) < 0 )
+ {
+ defaultSr = -1.;
+ PA_DEBUG(( "%s: Original default samplerate failed, trying again ..\n", __FUNCTION__ ));
+ }
+ }
+
+ if( defaultSr < 0. ) /* Default sample rate not set */
+ {
+ unsigned int sampleRate = 44100; /* Will contain approximate rate returned by alsa-lib */
+ ENSURE_( snd_pcm_hw_params_set_rate_near( pcm, hwParams, &sampleRate, NULL ), paUnanticipatedHostError );
+ ENSURE_( GetExactSampleRate( hwParams, &defaultSr ), paUnanticipatedHostError );
+ }
+
+ ENSURE_( snd_pcm_hw_params_get_channels_min( hwParams, &minChans ), paUnanticipatedHostError );
+ ENSURE_( snd_pcm_hw_params_get_channels_max( hwParams, &maxChans ), paUnanticipatedHostError );
+ assert( maxChans <= INT_MAX );
+ assert( maxChans > 0 ); /* Weird linking issue could cause wrong version of ALSA symbols to be called,
+ resulting in zeroed values */
+
+ /* XXX: Limit to sensible number (ALSA plugins accept a crazy amount of channels)? */
+ if( isPlug && maxChans > 128 )
+ {
+ maxChans = 128;
+ PA_DEBUG(( "%s: Limiting number of plugin channels to %u\n", __FUNCTION__, maxChans ));
+ }
+
+ /* TWEAKME:
+ *
+ * Giving values for default min and max latency is not
+ * straightforward. Here are our objectives:
+ *
+ * * for low latency, we want to give the lowest value
+ * that will work reliably. This varies based on the
+ * sound card, kernel, CPU, etc. I think it is better
+ * to give sub-optimal latency than to give a number
+ * too low and cause dropouts. My conservative
+ * estimate at this point is to base it on 4096-sample
+ * latency at 44.1 kHz, which gives a latency of 23ms.
+ * * for high latency we want to give a large enough
+ * value that dropouts are basically impossible. This
+ * doesn't really require as much tweaking, since
+ * providing too large a number will just cause us to
+ * select the nearest setting that will work at stream
+ * config time.
+ */
+ ENSURE_( snd_pcm_hw_params_set_buffer_size_near( pcm, hwParams, &lowLatency ), paUnanticipatedHostError );
+
+ /* Have to reset hwParams, to set new buffer size */
+ ENSURE_( snd_pcm_hw_params_any( pcm, hwParams ), paUnanticipatedHostError );
+ ENSURE_( snd_pcm_hw_params_set_buffer_size_near( pcm, hwParams, &highLatency ), paUnanticipatedHostError );
+
+ *minChannels = (int)minChans;
+ *maxChannels = (int)maxChans;
+ *defaultSampleRate = defaultSr;
+ *defaultLowLatency = (double) lowLatency / *defaultSampleRate;
+ *defaultHighLatency = (double) highLatency / *defaultSampleRate;
+
+end:
+ snd_pcm_close( pcm );
+ return result;
+
+error:
+ goto end;
+}
+
+/* Initialize device info with invalid values (maxInputChannels and maxOutputChannels are set to zero since these indicate
+ * wether input/output is available) */
+static void InitializeDeviceInfo( PaDeviceInfo *deviceInfo )
+{
+ deviceInfo->structVersion = -1;
+ deviceInfo->name = NULL;
+ deviceInfo->hostApi = -1;
+ deviceInfo->maxInputChannels = 0;
+ deviceInfo->maxOutputChannels = 0;
+ deviceInfo->defaultLowInputLatency = -1.;
+ deviceInfo->defaultLowOutputLatency = -1.;
+ deviceInfo->defaultHighInputLatency = -1.;
+ deviceInfo->defaultHighOutputLatency = -1.;
+ deviceInfo->defaultSampleRate = -1.;
+}
+
+/* Helper struct */
+typedef struct
+{
+ char *alsaName;
+ char *name;
+ int isPlug;
+ int hasPlayback;
+ int hasCapture;
+} DeviceNames;
+
+static PaError PaAlsa_StrDup( PaAlsaHostApiRepresentation *alsaApi,
+ char **dst,
+ const char *src)
+{
+ PaError result = paNoError;
+ int len = strlen( src ) + 1;
+
+ /* PA_DEBUG(("PaStrDup %s %d\n", src, len)); */
+
+ PA_UNLESS( *dst = (char *)PaUtil_GroupAllocateMemory( alsaApi->allocations, len ),
+ paInsufficientMemory );
+ strncpy( *dst, src, len );
+
+error:
+ return result;
+}
+
+/* Disregard standard plugins
+ * XXX: Might want to make the "default" plugin available, if we can make it work
+ */
+static int IgnorePlugin( const char *pluginId )
+{
+#define numIgnored 10
+ static const char *ignoredPlugins[numIgnored] = {"hw", "plughw", "plug", "default", "dsnoop", "dmix", "tee",
+ "file", "null", "shm"};
+ int i;
+
+ for( i = 0; i < numIgnored; ++i )
+ {
+ if( !strcmp( pluginId, ignoredPlugins[i] ) )
+ {
+ return 1;
+ }
+ }
+
+ return 0;
+}
+
+/* Build PaDeviceInfo list, ignore devices for which we cannot determine capabilities (possibly busy, sigh) */
+static PaError BuildDeviceList( PaAlsaHostApiRepresentation *alsaApi )
+{
+ PaUtilHostApiRepresentation *commonApi = &alsaApi->commonHostApiRep;
+ PaAlsaDeviceInfo *deviceInfoArray;
+ int cardIdx = -1, devIdx = 0;
+ snd_ctl_card_info_t *cardInfo;
+ PaError result = paNoError;
+ size_t numDeviceNames = 0, maxDeviceNames = 1, i;
+ DeviceNames *deviceNames = NULL;
+ snd_config_t *topNode = NULL;
+ snd_pcm_info_t *pcmInfo;
+ int res;
+ int blocking = SND_PCM_NONBLOCK;
+ char alsaCardName[50];
+ if( getenv( "PA_ALSA_INITIALIZE_BLOCK" ) && atoi( getenv( "PA_ALSA_INITIALIZE_BLOCK" ) ) )
+ blocking = 0;
+
+ /* These two will be set to the first working input and output device, respectively */
+ commonApi->info.defaultInputDevice = paNoDevice;
+ commonApi->info.defaultOutputDevice = paNoDevice;
+
+ /* count the devices by enumerating all the card numbers */
+
+ /* snd_card_next() modifies the integer passed to it to be:
+ * the index of the first card if the parameter is -1
+ * the index of the next card if the parameter is the index of a card
+ * -1 if there are no more cards
+ *
+ * The function itself returns 0 if it succeeded. */
+ cardIdx = -1;
+ snd_ctl_card_info_alloca( &cardInfo );
+ snd_pcm_info_alloca( &pcmInfo );
+ while( snd_card_next( &cardIdx ) == 0 && cardIdx >= 0 )
+ {
+ char *cardName;
+ int devIdx = -1;
+ snd_ctl_t *ctl;
+ char buf[50];
+
+ snprintf( alsaCardName, sizeof (alsaCardName), "hw:%d", cardIdx );
+
+ /* Acquire name of card */
+ if( snd_ctl_open( &ctl, alsaCardName, 0 ) < 0 )
+ continue; /* Unable to open card :( */
+ snd_ctl_card_info( ctl, cardInfo );
+
+ PA_ENSURE( PaAlsa_StrDup( alsaApi, &cardName, snd_ctl_card_info_get_name( cardInfo )) );
+
+ while( snd_ctl_pcm_next_device( ctl, &devIdx ) == 0 && devIdx >= 0 )
+ {
+ char *alsaDeviceName, *deviceName;
+ size_t len;
+ int hasPlayback = 0, hasCapture = 0;
+ snprintf( buf, sizeof (buf), "%s:%d,%d", "hw", cardIdx, devIdx );
+
+ /* Obtain info about this particular device */
+ snd_pcm_info_set_device( pcmInfo, devIdx );
+ snd_pcm_info_set_subdevice( pcmInfo, 0 );
+ snd_pcm_info_set_stream( pcmInfo, SND_PCM_STREAM_CAPTURE );
+ if( snd_ctl_pcm_info( ctl, pcmInfo ) >= 0 )
+ hasCapture = 1;
+
+ snd_pcm_info_set_stream( pcmInfo, SND_PCM_STREAM_PLAYBACK );
+ if( snd_ctl_pcm_info( ctl, pcmInfo ) >= 0 )
+ hasPlayback = 1;
+
+ if( !hasPlayback && !hasCapture )
+ {
+ continue; /* Error */
+ }
+
+ /* The length of the string written by snprintf plus terminating 0 */
+ len = snprintf( NULL, 0, "%s: %s (%s)", cardName, snd_pcm_info_get_name( pcmInfo ), buf ) + 1;
+ PA_UNLESS( deviceName = (char *)PaUtil_GroupAllocateMemory( alsaApi->allocations, len ),
+ paInsufficientMemory );
+ snprintf( deviceName, len, "%s: %s (%s)", cardName,
+ snd_pcm_info_get_name( pcmInfo ), buf );
+
+ ++numDeviceNames;
+ if( !deviceNames || numDeviceNames > maxDeviceNames )
+ {
+ maxDeviceNames *= 2;
+ PA_UNLESS( deviceNames = (DeviceNames *) realloc( deviceNames, maxDeviceNames * sizeof (DeviceNames) ),
+ paInsufficientMemory );
+ }
+
+ PA_ENSURE( PaAlsa_StrDup( alsaApi, &alsaDeviceName, buf ) );
+
+ deviceNames[ numDeviceNames - 1 ].alsaName = alsaDeviceName;
+ deviceNames[ numDeviceNames - 1 ].name = deviceName;
+ deviceNames[ numDeviceNames - 1 ].isPlug = 0;
+ deviceNames[ numDeviceNames - 1 ].hasPlayback = hasPlayback;
+ deviceNames[ numDeviceNames - 1 ].hasCapture = hasCapture;
+ }
+ snd_ctl_close( ctl );
+ }
+
+ /* Iterate over plugin devices */
+ snd_config_update();
+ if( (res = snd_config_search( snd_config, "pcm", &topNode )) >= 0 )
+ {
+ snd_config_iterator_t i, next;
+
+ snd_config_for_each( i, next, topNode )
+ {
+ const char *tpStr = NULL, *idStr = NULL;
+ char *alsaDeviceName, *deviceName;
+ snd_config_t *n = snd_config_iterator_entry( i ), *tp = NULL;
+ if( snd_config_get_type( n ) != SND_CONFIG_TYPE_COMPOUND )
+ continue;
+
+ ENSURE_( snd_config_search( n, "type", &tp ), paUnanticipatedHostError );
+ ENSURE_( snd_config_get_string( tp, &tpStr ), paUnanticipatedHostError );
+
+ ENSURE_( snd_config_get_id( n, &idStr ), paUnanticipatedHostError );
+ if( IgnorePlugin( idStr ) )
+ {
+ PA_DEBUG(( "%s: Ignoring ALSA plugin device %s of type %s\n", __FUNCTION__, idStr, tpStr ));
+ continue;
+ }
+
+ PA_DEBUG(( "%s: Found plugin %s of type %s\n", __FUNCTION__, idStr, tpStr ));
+
+ PA_UNLESS( alsaDeviceName = (char*)PaUtil_GroupAllocateMemory( alsaApi->allocations,
+ strlen(idStr) + 6 ), paInsufficientMemory );
+ strcpy( alsaDeviceName, idStr );
+ PA_UNLESS( deviceName = (char*)PaUtil_GroupAllocateMemory( alsaApi->allocations,
+ strlen(idStr) + 1 ), paInsufficientMemory );
+ strcpy( deviceName, idStr );
+
+ ++numDeviceNames;
+ if( !deviceNames || numDeviceNames > maxDeviceNames )
+ {
+ maxDeviceNames *= 2;
+ PA_UNLESS( deviceNames = (DeviceNames *) realloc( deviceNames, maxDeviceNames * sizeof (DeviceNames) ),
+ paInsufficientMemory );
+ }
+
+ deviceNames[numDeviceNames - 1].alsaName = alsaDeviceName;
+ deviceNames[numDeviceNames - 1].name = deviceName;
+ deviceNames[numDeviceNames - 1].isPlug = 1;
+ deviceNames[numDeviceNames - 1].hasPlayback = 1;
+ deviceNames[numDeviceNames - 1].hasCapture = 1;
+ }
+ }
+ else
+ PA_DEBUG(( "%s: Iterating over ALSA plugins failed: %s\n", __FUNCTION__, snd_strerror( res ) ));
+
+ /* allocate deviceInfo memory based on the number of devices */
+ PA_UNLESS( commonApi->deviceInfos = (PaDeviceInfo**)PaUtil_GroupAllocateMemory(
+ alsaApi->allocations, sizeof(PaDeviceInfo*) * (numDeviceNames) ), paInsufficientMemory );
+
+ /* allocate all device info structs in a contiguous block */
+ PA_UNLESS( deviceInfoArray = (PaAlsaDeviceInfo*)PaUtil_GroupAllocateMemory(
+ alsaApi->allocations, sizeof(PaAlsaDeviceInfo) * numDeviceNames ), paInsufficientMemory );
+
+ /* Loop over list of cards, filling in info, if a device is deemed unavailable (can't get name),
+ * it's ignored.
+ */
+ /* while( snd_card_next( &cardIdx ) == 0 && cardIdx >= 0 ) */
+ for( i = 0, devIdx = 0; i < numDeviceNames; ++i )
+ {
+ snd_pcm_t *pcm;
+ PaAlsaDeviceInfo *deviceInfo = &deviceInfoArray[devIdx];
+ PaDeviceInfo *commonDeviceInfo = &deviceInfo->commonDeviceInfo;
+
+ /* Zero fields */
+ InitializeDeviceInfo( commonDeviceInfo );
+
+ /* to determine device capabilities, we must open the device and query the
+ * hardware parameter configuration space */
+
+ /* Query capture */
+ if( deviceNames[i].hasCapture &&
+ snd_pcm_open( &pcm, deviceNames[i].alsaName, SND_PCM_STREAM_CAPTURE, blocking ) >= 0 )
+ {
+ if( GropeDevice( pcm, &deviceInfo->minInputChannels, &commonDeviceInfo->maxInputChannels,
+ &commonDeviceInfo->defaultLowInputLatency, &commonDeviceInfo->defaultHighInputLatency,
+ &commonDeviceInfo->defaultSampleRate, deviceNames[i].isPlug ) != paNoError )
+ continue; /* Error */
+ }
+
+ /* Query playback */
+ if( deviceNames[i].hasPlayback &&
+ snd_pcm_open( &pcm, deviceNames[i].alsaName, SND_PCM_STREAM_PLAYBACK, blocking ) >= 0 )
+ {
+ if( GropeDevice( pcm, &deviceInfo->minOutputChannels, &commonDeviceInfo->maxOutputChannels,
+ &commonDeviceInfo->defaultLowOutputLatency, &commonDeviceInfo->defaultHighOutputLatency,
+ &commonDeviceInfo->defaultSampleRate, deviceNames[i].isPlug ) != paNoError )
+ continue; /* Error */
+ }
+
+ commonDeviceInfo->structVersion = 2;
+ commonDeviceInfo->hostApi = alsaApi->hostApiIndex;
+ commonDeviceInfo->name = deviceNames[i].name;
+ deviceInfo->alsaName = deviceNames[i].alsaName;
+ deviceInfo->isPlug = deviceNames[i].isPlug;
+
+ /* A: Storing pointer to PaAlsaDeviceInfo object as pointer to PaDeviceInfo object.
+ * Should now be safe to add device info, unless the device supports neither capture nor playback
+ */
+ if( commonDeviceInfo->maxInputChannels > 0 || commonDeviceInfo->maxOutputChannels > 0 )
+ {
+ if( commonApi->info.defaultInputDevice == paNoDevice && commonDeviceInfo->maxInputChannels > 0 )
+ commonApi->info.defaultInputDevice = devIdx;
+ if( commonApi->info.defaultOutputDevice == paNoDevice && commonDeviceInfo->maxOutputChannels > 0 )
+ commonApi->info.defaultOutputDevice = devIdx;
+
+ commonApi->deviceInfos[devIdx++] = (PaDeviceInfo *) deviceInfo;
+ }
+ }
+ free( deviceNames );
+
+ commonApi->info.deviceCount = devIdx; /* Number of successfully queried devices */
+
+end:
+ return result;
+
+error:
+ goto end; /* No particular action */
+}
+
+/* Check against known device capabilities */
+static PaError ValidateParameters( const PaStreamParameters *parameters, PaUtilHostApiRepresentation *hostApi, StreamDirection mode )
+{
+ PaError result = paNoError;
+ int maxChans;
+ const PaAlsaDeviceInfo *deviceInfo = NULL;
+ assert( parameters );
+
+ if( parameters->device != paUseHostApiSpecificDeviceSpecification )
+ {
+ assert( parameters->device < hostApi->info.deviceCount );
+ PA_UNLESS( parameters->hostApiSpecificStreamInfo == NULL, paBadIODeviceCombination );
+ deviceInfo = GetDeviceInfo( hostApi, parameters->device );
+ }
+ else
+ {
+ const PaAlsaStreamInfo *streamInfo = parameters->hostApiSpecificStreamInfo;
+
+ PA_UNLESS( parameters->device == paUseHostApiSpecificDeviceSpecification, paInvalidDevice );
+ PA_UNLESS( streamInfo->size == sizeof (PaAlsaStreamInfo) && streamInfo->version == 1,
+ paIncompatibleHostApiSpecificStreamInfo );
+
+ return paNoError; /* Skip further checking */
+ }
+
+ assert( deviceInfo );
+ assert( parameters->hostApiSpecificStreamInfo == NULL );
+ maxChans = (StreamDirection_In == mode ? deviceInfo->commonDeviceInfo.maxInputChannels :
+ deviceInfo->commonDeviceInfo.maxOutputChannels);
+ PA_UNLESS( parameters->channelCount <= maxChans, paInvalidChannelCount );
+
+error:
+ return result;
+}
+
+/* Given an open stream, what sample formats are available? */
+
+static PaSampleFormat GetAvailableFormats( snd_pcm_t *pcm )
+{
+ PaSampleFormat available = 0;
+ snd_pcm_hw_params_t *hwParams;
+ snd_pcm_hw_params_alloca( &hwParams );
+
+ snd_pcm_hw_params_any( pcm, hwParams );
+
+ if( snd_pcm_hw_params_test_format( pcm, hwParams, SND_PCM_FORMAT_FLOAT ) >= 0)
+ available |= paFloat32;
+
+ if( snd_pcm_hw_params_test_format( pcm, hwParams, SND_PCM_FORMAT_S32 ) >= 0)
+ available |= paInt32;
+
+ if( snd_pcm_hw_params_test_format( pcm, hwParams, SND_PCM_FORMAT_S24 ) >= 0)
+ available |= paInt24;
+
+ if( snd_pcm_hw_params_test_format( pcm, hwParams, SND_PCM_FORMAT_S16 ) >= 0)
+ available |= paInt16;
+
+ if( snd_pcm_hw_params_test_format( pcm, hwParams, SND_PCM_FORMAT_U8 ) >= 0)
+ available |= paUInt8;
+
+ if( snd_pcm_hw_params_test_format( pcm, hwParams, SND_PCM_FORMAT_S8 ) >= 0)
+ available |= paInt8;
+
+ return available;
+}
+
+static snd_pcm_format_t Pa2AlsaFormat( PaSampleFormat paFormat )
+{
+ switch( paFormat )
+ {
+ case paFloat32:
+ return SND_PCM_FORMAT_FLOAT;
+
+ case paInt16:
+ return SND_PCM_FORMAT_S16;
+
+ case paInt24:
+ return SND_PCM_FORMAT_S24;
+
+ case paInt32:
+ return SND_PCM_FORMAT_S32;
+
+ case paInt8:
+ return SND_PCM_FORMAT_S8;
+
+ case paUInt8:
+ return SND_PCM_FORMAT_U8;
+
+ default:
+ return SND_PCM_FORMAT_UNKNOWN;
+ }
+}
+
+/** Open an ALSA pcm handle.
+ *
+ * The device to be open can be specified in a custom PaAlsaStreamInfo struct, or it will be a device number. In case of a
+ * device number, it maybe specified through an env variable (PA_ALSA_PLUGHW) that we should open the corresponding plugin
+ * device.
+ */
+static PaError AlsaOpen( const PaUtilHostApiRepresentation *hostApi, const PaStreamParameters *params, StreamDirection
+ streamDir, snd_pcm_t **pcm )
+{
+ PaError result = paNoError;
+ int ret;
+ const char *deviceName = alloca( 50 );
+ const PaAlsaDeviceInfo *deviceInfo = NULL;
+ PaAlsaStreamInfo *streamInfo = (PaAlsaStreamInfo *)params->hostApiSpecificStreamInfo;
+
+ if( !streamInfo )
+ {
+ int usePlug = 0;
+ deviceInfo = GetDeviceInfo( hostApi, params->device );
+
+ /* If device name starts with hw: and PA_ALSA_PLUGHW is 1, we open the plughw device instead */
+ if( !strncmp( "hw:", deviceInfo->alsaName, 3 ) && getenv( "PA_ALSA_PLUGHW" ) )
+ usePlug = atoi( getenv( "PA_ALSA_PLUGHW" ) );
+ if( usePlug )
+ snprintf( (char *) deviceName, 50, "plug%s", deviceInfo->alsaName );
+ else
+ deviceName = deviceInfo->alsaName;
+ }
+ else
+ deviceName = streamInfo->deviceString;
+
+ if( (ret = snd_pcm_open( pcm, deviceName, streamDir == StreamDirection_In ? SND_PCM_STREAM_CAPTURE : SND_PCM_STREAM_PLAYBACK,
+ SND_PCM_NONBLOCK )) < 0 )
+ {
+ *pcm = NULL; /* Not to be closed */
+ ENSURE_( ret, ret == -EBUSY ? paDeviceUnavailable : paBadIODeviceCombination );
+ }
+ ENSURE_( snd_pcm_nonblock( *pcm, 0 ), paUnanticipatedHostError );
+
+end:
+ return result;
+
+error:
+ goto end;
+}
+
+static PaError TestParameters( const PaUtilHostApiRepresentation *hostApi, const PaStreamParameters *parameters,
+ double sampleRate, StreamDirection streamDir )
+{
+ PaError result = paNoError;
+ snd_pcm_t *pcm = NULL;
+ PaSampleFormat availableFormats;
+ /* We are able to adapt to a number of channels less than what the device supports */
+ unsigned int numHostChannels;
+ PaSampleFormat hostFormat;
+ snd_pcm_hw_params_t *hwParams;
+ snd_pcm_hw_params_alloca( &hwParams );
+
+ if( !parameters->hostApiSpecificStreamInfo )
+ {
+ const PaAlsaDeviceInfo *devInfo = GetDeviceInfo( hostApi, parameters->device );
+ numHostChannels = PA_MAX( parameters->channelCount, StreamDirection_In == streamDir ?
+ devInfo->minInputChannels : devInfo->minOutputChannels );
+ }
+ else
+ numHostChannels = parameters->channelCount;
+
+ PA_ENSURE( AlsaOpen( hostApi, parameters, streamDir, &pcm ) );
+
+ snd_pcm_hw_params_any( pcm, hwParams );
+
+ if( SetApproximateSampleRate( pcm, hwParams, sampleRate ) < 0 )
+ {
+ result = paInvalidSampleRate;
+ goto error;
+ }
+
+ if( snd_pcm_hw_params_set_channels( pcm, hwParams, numHostChannels ) < 0 )
+ {
+ result = paInvalidChannelCount;
+ goto error;
+ }
+
+ /* See if we can find a best possible match */
+ availableFormats = GetAvailableFormats( pcm );
+ PA_ENSURE( hostFormat = PaUtil_SelectClosestAvailableFormat( availableFormats, parameters->sampleFormat ) );
+ ENSURE_( snd_pcm_hw_params_set_format( pcm, hwParams, Pa2AlsaFormat( hostFormat ) ), paUnanticipatedHostError );
+
+ ENSURE_( snd_pcm_hw_params( pcm, hwParams ), paUnanticipatedHostError );
+
+end:
+ if( pcm )
+ snd_pcm_close( pcm );
+ return result;
+
+error:
+ goto end;
+}
+
+static PaError IsFormatSupported( struct PaUtilHostApiRepresentation *hostApi,
+ const PaStreamParameters *inputParameters,
+ const PaStreamParameters *outputParameters,
+ double sampleRate )
+{
+ int inputChannelCount = 0, outputChannelCount = 0;
+ PaSampleFormat inputSampleFormat, outputSampleFormat;
+ PaError result = paFormatIsSupported;
+
+ if( inputParameters )
+ {
+ PA_ENSURE( ValidateParameters( inputParameters, hostApi, StreamDirection_In ) );
+
+ inputChannelCount = inputParameters->channelCount;
+ inputSampleFormat = inputParameters->sampleFormat;
+ }
+
+ if( outputParameters )
+ {
+ PA_ENSURE( ValidateParameters( outputParameters, hostApi, StreamDirection_Out ) );
+
+ outputChannelCount = outputParameters->channelCount;
+ outputSampleFormat = outputParameters->sampleFormat;
+ }
+
+ if( inputChannelCount )
+ {
+ if( (result = TestParameters( hostApi, inputParameters, sampleRate, StreamDirection_In ))
+ != paNoError )
+ goto error;
+ }
+ if ( outputChannelCount )
+ {
+ if( (result = TestParameters( hostApi, outputParameters, sampleRate, StreamDirection_Out ))
+ != paNoError )
+ goto error;
+ }
+
+ return paFormatIsSupported;
+
+error:
+ return result;
+}
+
+static PaError PaAlsaStreamComponent_Initialize( PaAlsaStreamComponent *self, PaAlsaHostApiRepresentation *alsaApi,
+ const PaStreamParameters *params, StreamDirection streamDir, int callbackMode )
+{
+ PaError result = paNoError;
+ PaSampleFormat userSampleFormat = params->sampleFormat, hostSampleFormat;
+ assert( params->channelCount > 0 );
+
+ /* Make sure things have an initial value */
+ memset( self, 0, sizeof (PaAlsaStreamComponent) );
+
+ if( NULL == params->hostApiSpecificStreamInfo )
+ {
+ const PaAlsaDeviceInfo *devInfo = GetDeviceInfo( &alsaApi->commonHostApiRep, params->device );
+ self->numHostChannels = PA_MAX( params->channelCount, StreamDirection_In == streamDir ? devInfo->minInputChannels
+ : devInfo->minOutputChannels );
+ }
+ else
+ {
+ /* We're blissfully unaware of the minimum channelCount */
+ self->numHostChannels = params->channelCount;
+ }
+
+ PA_ENSURE( AlsaOpen( &alsaApi->commonHostApiRep, params, streamDir, &self->pcm ) );
+ self->nfds = snd_pcm_poll_descriptors_count( self->pcm );
+ hostSampleFormat = PaUtil_SelectClosestAvailableFormat( GetAvailableFormats( self->pcm ), userSampleFormat );
+
+ self->hostSampleFormat = hostSampleFormat;
+ self->nativeFormat = Pa2AlsaFormat( hostSampleFormat );
+ self->hostInterleaved = self->userInterleaved = !(userSampleFormat & paNonInterleaved);
+ self->numUserChannels = params->channelCount;
+ self->streamDir = streamDir;
+
+ if( !callbackMode && !self->userInterleaved )
+ {
+ /* Pre-allocate non-interleaved user provided buffers */
+ PA_UNLESS( self->userBuffers = PaUtil_AllocateMemory( sizeof (void *) * self->numUserChannels ),
+ paInsufficientMemory );
+ }
+
+error:
+ return result;
+}
+
+static void PaAlsaStreamComponent_Terminate( PaAlsaStreamComponent *self )
+{
+ snd_pcm_close( self->pcm );
+ if( self->userBuffers )
+ PaUtil_FreeMemory( self->userBuffers );
+}
+
+/** Configure the associated ALSA pcm.
+ *
+ */
+static PaError PaAlsaStreamComponent_Configure( PaAlsaStreamComponent *self, const PaStreamParameters *params, unsigned long
+ framesPerHostBuffer, int primeBuffers, int callbackMode, double *sampleRate, PaTime *returnedLatency )
+{
+ /*
+ int numPeriods;
+
+ if( getenv("PA_NUMPERIODS") != NULL )
+ numPeriods = atoi( getenv("PA_NUMPERIODS") );
+ else
+ numPeriods = ( (latency * sampleRate) / *framesPerBuffer ) + 1;
+
+ PA_DEBUG(( "latency: %f, rate: %f, framesPerBuffer: %d\n", latency, sampleRate, *framesPerBuffer ));
+ if( numPeriods <= 1 )
+ numPeriods = 2;
+ */
+
+ /* Configuration consists of setting all of ALSA's parameters.
+ * These parameters come in two flavors: hardware parameters
+ * and software paramters. Hardware parameters will affect
+ * the way the device is initialized, software parameters
+ * affect the way ALSA interacts with me, the user-level client.
+ */
+
+ snd_pcm_hw_params_t *hwParams;
+ snd_pcm_sw_params_t *swParams;
+ PaError result = paNoError;
+ snd_pcm_access_t accessMode, alternateAccessMode;
+ unsigned int numPeriods, minPeriods = 2;
+ int dir = 0;
+ snd_pcm_t *pcm = self->pcm;
+ PaTime latency = params->suggestedLatency;
+ double sr = *sampleRate;
+ *returnedLatency = -1.;
+
+ snd_pcm_hw_params_alloca( &hwParams );
+ snd_pcm_sw_params_alloca( &swParams );
+
+ self->framesPerBuffer = framesPerHostBuffer;
+
+ /* ... fill up the configuration space with all possibile
+ * combinations of parameters this device will accept */
+ ENSURE_( snd_pcm_hw_params_any( pcm, hwParams ), paUnanticipatedHostError );
+
+ ENSURE_( snd_pcm_hw_params_set_periods_integer( pcm, hwParams ), paUnanticipatedHostError );
+ ENSURE_( snd_pcm_hw_params_set_period_size_integer( pcm, hwParams ), paUnanticipatedHostError );
+
+ if( self->userInterleaved )
+ {
+ accessMode = SND_PCM_ACCESS_MMAP_INTERLEAVED;
+ alternateAccessMode = SND_PCM_ACCESS_MMAP_NONINTERLEAVED;
+ }
+ else
+ {
+ accessMode = SND_PCM_ACCESS_MMAP_NONINTERLEAVED;
+ alternateAccessMode = SND_PCM_ACCESS_MMAP_INTERLEAVED;
+ }
+
+ /* If requested access mode fails, try alternate mode */
+ if( snd_pcm_hw_params_set_access( pcm, hwParams, accessMode ) < 0 )
+ {
+ ENSURE_( snd_pcm_hw_params_set_access( pcm, hwParams, alternateAccessMode ), paUnanticipatedHostError );
+ /* Flip mode */
+ self->hostInterleaved = !self->userInterleaved;
+ }
+
+ ENSURE_( snd_pcm_hw_params_set_format( pcm, hwParams, self->nativeFormat ), paUnanticipatedHostError );
+
+ ENSURE_( SetApproximateSampleRate( pcm, hwParams, sr ), paInvalidSampleRate );
+ ENSURE_( GetExactSampleRate( hwParams, &sr ), paUnanticipatedHostError );
+ /* reject if there's no sample rate within 1% of the one requested */
+ if( (fabs( *sampleRate - sr ) / *sampleRate) > 0.01 )
+ {
+ PA_DEBUG(("%s: Wanted %f, closest sample rate was %d\n", __FUNCTION__, sampleRate, sr ));
+ PA_ENSURE( paInvalidSampleRate );
+ }
+
+ ENSURE_( snd_pcm_hw_params_set_channels( pcm, hwParams, self->numHostChannels ), paInvalidChannelCount );
+
+ /* I think there should be at least 2 periods (even though ALSA doesn't appear to enforce this) */
+ dir = 0;
+ ENSURE_( snd_pcm_hw_params_set_periods_min( pcm, hwParams, &minPeriods, &dir ), paUnanticipatedHostError );
+ dir = 0;
+ ENSURE_( snd_pcm_hw_params_set_period_size_near( pcm, hwParams, &self->framesPerBuffer, &dir ), paUnanticipatedHostError );
+
+ /* Find an acceptable number of periods */
+ numPeriods = (latency * sr) / self->framesPerBuffer + 1;
+ dir = 0;
+ ENSURE_( snd_pcm_hw_params_set_periods_near( pcm, hwParams, &numPeriods, &dir ), paUnanticipatedHostError );
+ /* Minimum of periods should already be 2 */
+ PA_UNLESS( numPeriods >= 2, paInternalError );
+
+ /* Set the parameters! */
+ ENSURE_( snd_pcm_hw_params( pcm, hwParams ), paUnanticipatedHostError );
+ ENSURE_( snd_pcm_hw_params_get_buffer_size( hwParams, &self->bufferSize ), paUnanticipatedHostError );
+
+ /* Latency in seconds, one period is not counted as latency */
+ latency = (numPeriods - 1) * self->framesPerBuffer / sr;
+
+ /* Now software parameters... */
+ ENSURE_( snd_pcm_sw_params_current( pcm, swParams ), paUnanticipatedHostError );
+
+ ENSURE_( snd_pcm_sw_params_set_start_threshold( pcm, swParams, self->framesPerBuffer ), paUnanticipatedHostError );
+ ENSURE_( snd_pcm_sw_params_set_stop_threshold( pcm, swParams, self->bufferSize ), paUnanticipatedHostError );
+
+ /* Silence buffer in the case of underrun */
+ if( !primeBuffers ) /* XXX: Make sense? */
+ {
+ snd_pcm_uframes_t boundary;
+ ENSURE_( snd_pcm_sw_params_get_boundary( swParams, &boundary ), paUnanticipatedHostError );
+ ENSURE_( snd_pcm_sw_params_set_silence_threshold( pcm, swParams, 0 ), paUnanticipatedHostError );
+ ENSURE_( snd_pcm_sw_params_set_silence_size( pcm, swParams, boundary ), paUnanticipatedHostError );
+ }
+
+ ENSURE_( snd_pcm_sw_params_set_avail_min( pcm, swParams, self->framesPerBuffer ), paUnanticipatedHostError );
+ ENSURE_( snd_pcm_sw_params_set_xfer_align( pcm, swParams, 1 ), paUnanticipatedHostError );
+ ENSURE_( snd_pcm_sw_params_set_tstamp_mode( pcm, swParams, SND_PCM_TSTAMP_MMAP ), paUnanticipatedHostError );
+
+ /* Set the parameters! */
+ ENSURE_( snd_pcm_sw_params( pcm, swParams ), paUnanticipatedHostError );
+
+ *sampleRate = sr;
+ *returnedLatency = latency;
+
+end:
+ return result;
+
+error:
+ goto end; /* No particular action */
+}
+
+static PaError PaAlsaStream_Initialize( PaAlsaStream *self, PaAlsaHostApiRepresentation *alsaApi, const PaStreamParameters *inParams,
+ const PaStreamParameters *outParams, double sampleRate, unsigned long framesPerUserBuffer, PaStreamCallback callback,
+ PaStreamFlags streamFlags, void *userData )
+{
+ PaError result = paNoError;
+ assert( self );
+
+ memset( self, 0, sizeof (PaAlsaStream) );
+
+ if( NULL != callback )
+ {
+ PaUtil_InitializeStreamRepresentation( &self->streamRepresentation,
+ &alsaApi->callbackStreamInterface,
+ callback, userData );
+ self->callbackMode = 1;
+ }
+ else
+ {
+ PaUtil_InitializeStreamRepresentation( &self->streamRepresentation,
+ &alsaApi->blockingStreamInterface,
+ NULL, userData );
+ }
+
+ self->framesPerUserBuffer = framesPerUserBuffer;
+ self->neverDropInput = streamFlags & paNeverDropInput;
+ /* XXX: Ignore paPrimeOutputBuffersUsingStreamCallback untill buffer priming is fully supported in pa_process.c */
+ /*
+ if( outParams & streamFlags & paPrimeOutputBuffersUsingStreamCallback )
+ self->primeBuffers = 1;
+ */
+ memset( &self->capture, 0, sizeof (PaAlsaStreamComponent) );
+ memset( &self->playback, 0, sizeof (PaAlsaStreamComponent) );
+ if( inParams )
+ PA_ENSURE( PaAlsaStreamComponent_Initialize( &self->capture, alsaApi, inParams, StreamDirection_In, NULL != callback ) );
+ if( outParams )
+ PA_ENSURE( PaAlsaStreamComponent_Initialize( &self->playback, alsaApi, outParams, StreamDirection_Out, NULL != callback ) );
+
+ assert( self->capture.nfds || self->playback.nfds );
+
+ PA_UNLESS( self->pfds = (struct pollfd*)PaUtil_AllocateMemory( (self->capture.nfds +
+ self->playback.nfds) * sizeof (struct pollfd) ), paInsufficientMemory );
+
+ PaUtil_InitializeCpuLoadMeasurer( &self->cpuLoadMeasurer, sampleRate );
+ InitializeThreading( &self->threading, &self->cpuLoadMeasurer );
+ ASSERT_CALL_( pthread_mutex_init( &self->stateMtx, NULL ), 0 );
+ ASSERT_CALL_( pthread_mutex_init( &self->startMtx, NULL ), 0 );
+ ASSERT_CALL_( pthread_cond_init( &self->startCond, NULL ), 0 );
+
+error:
+ return result;
+}
+
+/** Free resources associated with stream, and eventually stream itself.
+ *
+ * Frees allocated memory, and terminates individual StreamComponents.
+ */
+static void PaAlsaStream_Terminate( PaAlsaStream *self )
+{
+ assert( self );
+
+ if( self->capture.pcm )
+ {
+ PaAlsaStreamComponent_Terminate( &self->capture );
+ }
+ if( self->playback.pcm )
+ {
+ PaAlsaStreamComponent_Terminate( &self->playback );
+ }
+
+ PaUtil_FreeMemory( self->pfds );
+ ASSERT_CALL_( pthread_mutex_destroy( &self->stateMtx ), 0 );
+ ASSERT_CALL_( pthread_mutex_destroy( &self->startMtx ), 0 );
+ ASSERT_CALL_( pthread_cond_destroy( &self->startCond ), 0 );
+
+ PaUtil_FreeMemory( self );
+}
+
+/** Calculate polling timeout
+ *
+ * @param frames Time to wait
+ * @return Polling timeout in milliseconds
+ */
+static int CalculatePollTimeout( const PaAlsaStream *stream, unsigned long frames )
+{
+ assert( stream->streamRepresentation.streamInfo.sampleRate > 0.0 );
+ /* Period in msecs, rounded up */
+ return (int)ceil( 1000 * frames / stream->streamRepresentation.streamInfo.sampleRate );
+}
+
+/** Set up ALSA stream parameters.
+ *
+ */
+static PaError PaAlsaStream_Configure( PaAlsaStream *self, const PaStreamParameters *inParams, const PaStreamParameters
+ *outParams, double sampleRate, unsigned long framesPerHostBuffer, double *inputLatency, double *outputLatency,
+ unsigned long *maxHostBufferSize )
+{
+ PaError result = paNoError;
+ double realSr = sampleRate;
+
+ if( self->capture.pcm )
+ PA_ENSURE( PaAlsaStreamComponent_Configure( &self->capture, inParams, framesPerHostBuffer, self->primeBuffers,
+ self->callbackMode, &realSr, inputLatency ) );
+ if( self->playback.pcm )
+ PA_ENSURE( PaAlsaStreamComponent_Configure( &self->playback, outParams, framesPerHostBuffer, self->primeBuffers,
+ self->callbackMode, &realSr, outputLatency ) );
+
+ /* Should be exact now */
+ self->streamRepresentation.streamInfo.sampleRate = realSr;
+
+ /* this will cause the two streams to automatically start/stop/prepare in sync.
+ * We only need to execute these operations on one of the pair.
+ * A: We don't want to do this on a blocking stream.
+ */
+ if( self->callbackMode && self->capture.pcm && self->playback.pcm )
+ {
+ int err = snd_pcm_link( self->capture.pcm, self->playback.pcm );
+ if( err >= 0 )
+ self->pcmsSynced = 1;
+ else
+ PA_DEBUG(( "%s: Unable to sync pcms: %s\n", __FUNCTION__, snd_strerror( err ) ));
+ }
+
+ /* Frames per host buffer for the stream is set as a compromise between the two directions */
+ framesPerHostBuffer = PA_MIN( self->capture.pcm ? self->capture.framesPerBuffer : ULONG_MAX,
+ self->playback.pcm ? self->playback.framesPerBuffer : ULONG_MAX );
+ self->pollTimeout = CalculatePollTimeout( self, framesPerHostBuffer ); /* Period in msecs, rounded up */
+
+ *maxHostBufferSize = PA_MAX( self->capture.pcm ? self->capture.bufferSize : 0,
+ self->playback.pcm ? self->playback.bufferSize : 0 );
+
+ /* Time before watchdog unthrottles realtime thread == 1/4 of period time in msecs */
+ self->threading.throttledSleepTime = (unsigned long) (framesPerHostBuffer / sampleRate / 4 * 1000);
+
+ if( self->callbackMode )
+ {
+ /* If the user expects a certain number of frames per callback we will either have to rely on block adaption
+ * (framesPerHostBuffer is not an integer multiple of framesPerBuffer) or we can simply align the number
+ * of host buffer frames with what the user specified */
+ if( self->framesPerUserBuffer != paFramesPerBufferUnspecified )
+ {
+ /* self->alignFrames = 1; */
+
+ /* Unless the ratio between number of host and user buffer frames is an integer we will have to rely
+ * on block adaption */
+ /*
+ if( framesPerHostBuffer % framesPerBuffer != 0 || (self->capture.pcm && self->playback.pcm &&
+ self->capture.framesPerBuffer != self->playback.framesPerBuffer) )
+ self->useBlockAdaption = 1;
+ else
+ self->alignFrames = 1;
+ */
+ }
+ }
+
+error:
+ return result;
+}
+
+/* We need to determine how many frames per host buffer to use. Our
+ * goals are to provide the best possible performance, but also to
+ * most closely honor the requested latency settings. Therefore this
+ * decision is based on:
+ *
+ * - the period sizes that playback and/or capture support. The
+ * host buffer size has to be one of these.
+ * - the number of periods that playback and/or capture support.
+ *
+ * We want to make period_size*(num_periods-1) to be as close as possible
+ * to latency*rate for both playback and capture.
+ *
+ * This is one of those blocks of code that will just take a lot of
+ * refinement to be any good.
+ *
+ * In the full-duplex case it is possible that the routine was unable
+ * to find a number of frames per buffer acceptable to both devices
+ * TODO: Implement an algorithm to find the value closest to acceptance
+ * by both devices, to minimize difference between period sizes?
+ */
+static PaError DetermineFramesPerBuffer( const PaAlsaStream *stream, double sampleRate, const PaStreamParameters *inputParameters,
+ const PaStreamParameters *outputParameters, unsigned long *determinedFrames, const PaUtilHostApiRepresentation *hostApi )
+{
+ PaError result = paNoError;
+ unsigned long framesPerBuffer = 0;
+ int numHostInputChannels = 0, numHostOutputChannels = 0;
+
+ /* XXX: Clean this up */
+ if( stream->capture.pcm )
+ {
+ const PaAlsaDeviceInfo *devInfo = GetDeviceInfo( hostApi, inputParameters->device );
+ numHostInputChannels = PA_MAX( inputParameters->channelCount, devInfo->minInputChannels );
+ }
+ if( stream->playback.pcm )
+ {
+ const PaAlsaDeviceInfo *devInfo = GetDeviceInfo( hostApi, outputParameters->device );
+ numHostOutputChannels = PA_MAX( outputParameters->channelCount, devInfo->minOutputChannels );
+ }
+
+ if( stream->capture.pcm && stream->playback.pcm )
+ {
+ snd_pcm_uframes_t desiredLatency, e;
+ snd_pcm_uframes_t minPeriodSize, minPlayback, minCapture, maxPeriodSize, maxPlayback, maxCapture,
+ optimalPeriodSize, periodSize;
+ int dir = 0;
+ unsigned int minPeriods = 2;
+
+ snd_pcm_t *pcm;
+ snd_pcm_hw_params_t *hwParamsPlayback, *hwParamsCapture;
+
+ snd_pcm_hw_params_alloca( &hwParamsPlayback );
+ snd_pcm_hw_params_alloca( &hwParamsCapture );
+
+ /* Come up with a common desired latency */
+ pcm = stream->playback.pcm;
+ snd_pcm_hw_params_any( pcm, hwParamsPlayback );
+ ENSURE_( SetApproximateSampleRate( pcm, hwParamsPlayback, sampleRate ), paInvalidSampleRate );
+ ENSURE_( snd_pcm_hw_params_set_channels( pcm, hwParamsPlayback, numHostOutputChannels ),
+ paBadIODeviceCombination );
+
+ ENSURE_( snd_pcm_hw_params_set_period_size_integer( pcm, hwParamsPlayback ), paUnanticipatedHostError );
+ ENSURE_( snd_pcm_hw_params_set_periods_integer( pcm, hwParamsPlayback ), paUnanticipatedHostError );
+ ENSURE_( snd_pcm_hw_params_set_periods_min( pcm, hwParamsPlayback, &minPeriods, &dir ), paUnanticipatedHostError );
+ ENSURE_( snd_pcm_hw_params_get_period_size_min( hwParamsPlayback, &minPlayback, &dir ), paUnanticipatedHostError );
+ ENSURE_( snd_pcm_hw_params_get_period_size_max( hwParamsPlayback, &maxPlayback, &dir ), paUnanticipatedHostError );
+
+ pcm = stream->capture.pcm;
+ ENSURE_( snd_pcm_hw_params_any( pcm, hwParamsCapture ), paUnanticipatedHostError );
+ ENSURE_( SetApproximateSampleRate( pcm, hwParamsCapture, sampleRate ), paBadIODeviceCombination );
+ ENSURE_( snd_pcm_hw_params_set_channels( pcm, hwParamsCapture, numHostInputChannels ),
+ paBadIODeviceCombination );
+
+ ENSURE_( snd_pcm_hw_params_set_period_size_integer( pcm, hwParamsCapture ), paUnanticipatedHostError );
+ ENSURE_( snd_pcm_hw_params_set_periods_integer( pcm, hwParamsCapture ), paUnanticipatedHostError );
+ ENSURE_( snd_pcm_hw_params_set_periods_min( pcm, hwParamsCapture, &minPeriods, &dir ), paUnanticipatedHostError );
+ ENSURE_( snd_pcm_hw_params_get_period_size_min( hwParamsCapture, &minCapture, &dir ), paUnanticipatedHostError );
+ ENSURE_( snd_pcm_hw_params_get_period_size_max( hwParamsCapture, &maxCapture, &dir ), paUnanticipatedHostError );
+
+ minPeriodSize = PA_MAX( minPlayback, minCapture );
+ maxPeriodSize = PA_MIN( maxPlayback, maxCapture );
+
+ desiredLatency = (snd_pcm_uframes_t) (PA_MIN( outputParameters->suggestedLatency, inputParameters->suggestedLatency )
+ * sampleRate);
+ /* Clamp desiredLatency */
+ {
+ snd_pcm_uframes_t tmp, maxBufferSize = ULONG_MAX;
+ ENSURE_( snd_pcm_hw_params_get_buffer_size_max( hwParamsPlayback, &maxBufferSize ), paUnanticipatedHostError );
+ ENSURE_( snd_pcm_hw_params_get_buffer_size_max( hwParamsCapture, &tmp ), paUnanticipatedHostError );
+ maxBufferSize = PA_MIN( maxBufferSize, tmp );
+
+ desiredLatency = PA_MIN( desiredLatency, maxBufferSize );
+ }
+
+ /* Find the closest power of 2 */
+ e = ilogb( minPeriodSize );
+ if( minPeriodSize & (minPeriodSize - 1) )
+ e += 1;
+ periodSize = (snd_pcm_uframes_t) pow( 2, e );
+
+ while( periodSize <= maxPeriodSize )
+ {
+ if( snd_pcm_hw_params_test_period_size( stream->playback.pcm, hwParamsPlayback, periodSize, 0 ) >= 0 &&
+ snd_pcm_hw_params_test_period_size( stream->capture.pcm, hwParamsCapture, periodSize, 0 ) >= 0 )
+ break; /* Ok! */
+
+ periodSize *= 2;
+ }
+
+ /* 4 periods considered optimal */
+ optimalPeriodSize = PA_MAX( desiredLatency / 4, minPeriodSize );
+ optimalPeriodSize = PA_MIN( optimalPeriodSize, maxPeriodSize );
+
+ /* Find the closest power of 2 */
+ e = ilogb( optimalPeriodSize );
+ if( optimalPeriodSize & (optimalPeriodSize - 1) )
+ e += 1;
+ optimalPeriodSize = (snd_pcm_uframes_t) pow( 2, e );
+
+ while( optimalPeriodSize >= periodSize )
+ {
+ pcm = stream->playback.pcm;
+ if( snd_pcm_hw_params_test_period_size( pcm, hwParamsPlayback, optimalPeriodSize, 0 ) < 0 )
+ continue;
+
+ pcm = stream->capture.pcm;
+ if( snd_pcm_hw_params_test_period_size( pcm, hwParamsCapture, optimalPeriodSize, 0 ) >= 0 )
+ break;
+
+ optimalPeriodSize /= 2;
+ }
+
+ if( optimalPeriodSize > periodSize )
+ periodSize = optimalPeriodSize;
+
+ if( periodSize <= maxPeriodSize )
+ {
+ /* Looks good */
+ framesPerBuffer = periodSize;
+ }
+ else
+ {
+ /* Unable to find a common period size, oh well */
+ optimalPeriodSize = PA_MAX( desiredLatency / 4, minPeriodSize );
+ optimalPeriodSize = PA_MIN( optimalPeriodSize, maxPeriodSize );
+
+ /* ConfigureStream should find individual period sizes acceptable for each device */
+ framesPerBuffer = optimalPeriodSize;
+ /* PA_ENSURE( paBadIODeviceCombination ); */
+ }
+ }
+ else /* half-duplex is a slightly simpler case */
+ {
+ unsigned long bufferSize, channels;
+ snd_pcm_t *pcm;
+ snd_pcm_hw_params_t *hwParams;
+
+ snd_pcm_hw_params_alloca( &hwParams );
+
+ if( stream->capture.pcm )
+ {
+ pcm = stream->capture.pcm;
+ bufferSize = inputParameters->suggestedLatency * sampleRate;
+ channels = numHostInputChannels;
+ }
+ else
+ {
+ pcm = stream->playback.pcm;
+ bufferSize = outputParameters->suggestedLatency * sampleRate;
+ channels = numHostOutputChannels;
+ }
+
+ ENSURE_( snd_pcm_hw_params_any( pcm, hwParams ), paUnanticipatedHostError );
+ ENSURE_( SetApproximateSampleRate( pcm, hwParams, sampleRate ), paInvalidSampleRate );
+ ENSURE_( snd_pcm_hw_params_set_channels( pcm, hwParams, channels ), paBadIODeviceCombination );
+
+ ENSURE_( snd_pcm_hw_params_set_period_size_integer( pcm, hwParams ), paUnanticipatedHostError );
+ ENSURE_( snd_pcm_hw_params_set_periods_integer( pcm, hwParams ), paUnanticipatedHostError );
+
+ /* Using 5 as a base number of periods, we try to approximate the suggested latency (+1 period),
+ finding a combination of period/buffer size which best fits these constraints */
+ framesPerBuffer = bufferSize / 4;
+ bufferSize += framesPerBuffer; /* One period doesn't count as latency */
+ ENSURE_( snd_pcm_hw_params_set_buffer_size_near( pcm, hwParams, &bufferSize ), paUnanticipatedHostError );
+ ENSURE_( snd_pcm_hw_params_set_period_size_near( pcm, hwParams, &framesPerBuffer, NULL ), paUnanticipatedHostError );
+ }
+
+ PA_UNLESS( framesPerBuffer != 0, paInternalError );
+ *determinedFrames = framesPerBuffer;
+
+error:
+ return result;
+}
+
+static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi,
+ PaStream** s,
+ const PaStreamParameters *inputParameters,
+ const PaStreamParameters *outputParameters,
+ double sampleRate,
+ unsigned long framesPerBuffer,
+ PaStreamFlags streamFlags,
+ PaStreamCallback *callback,
+ void *userData )
+{
+ PaError result = paNoError;
+ PaAlsaHostApiRepresentation *alsaHostApi = (PaAlsaHostApiRepresentation*)hostApi;
+ PaAlsaStream *stream = NULL;
+ PaSampleFormat hostInputSampleFormat = 0, hostOutputSampleFormat = 0;
+ PaSampleFormat inputSampleFormat = 0, outputSampleFormat = 0;
+ int numInputChannels = 0, numOutputChannels = 0;
+ PaTime inputLatency, outputLatency;
+ unsigned long framesPerHostBuffer;
+ PaUtilHostBufferSizeMode hostBufferSizeMode = paUtilBoundedHostBufferSize;
+ unsigned long maxHostBufferSize; /* Upper bound of host buffer size */
+
+ if( (streamFlags & paPlatformSpecificFlags) != 0 )
+ return paInvalidFlag;
+
+ if( inputParameters )
+ {
+ PA_ENSURE( ValidateParameters( inputParameters, hostApi, StreamDirection_In ) );
+
+ numInputChannels = inputParameters->channelCount;
+ inputSampleFormat = inputParameters->sampleFormat;
+ }
+ if( outputParameters )
+ {
+ PA_ENSURE( ValidateParameters( outputParameters, hostApi, StreamDirection_Out ) );
+
+ numOutputChannels = outputParameters->channelCount;
+ outputSampleFormat = outputParameters->sampleFormat;
+ }
+
+ /* XXX: Why do we support this anyway? */
+ if( framesPerBuffer == paFramesPerBufferUnspecified && getenv( "PA_ALSA_PERIODSIZE" ) != NULL )
+ {
+ PA_DEBUG(( "%s: Getting framesPerBuffer from environment\n", __FUNCTION__ ));
+ framesPerBuffer = atoi( getenv("PA_ALSA_PERIODSIZE") );
+ }
+ framesPerHostBuffer = framesPerBuffer;
+
+ PA_UNLESS( stream = (PaAlsaStream*)PaUtil_AllocateMemory( sizeof(PaAlsaStream) ), paInsufficientMemory );
+ PA_ENSURE( PaAlsaStream_Initialize( stream, alsaHostApi, inputParameters, outputParameters, sampleRate,
+ framesPerBuffer, callback, streamFlags, userData ) );
+
+ /* If the number of frames per buffer is unspecified, we have to come up with
+ * one. This is both a blessing and a curse: a blessing because we can optimize
+ * the number to best meet the requirements, but a curse because that's really
+ * hard to do well. For this reason we also support an interface where the user
+ * specifies these by setting environment variables. */
+ if( framesPerBuffer == paFramesPerBufferUnspecified )
+ {
+ PA_ENSURE( DetermineFramesPerBuffer( stream, sampleRate, inputParameters, outputParameters, &framesPerHostBuffer,
+ hostApi ) );
+ }
+
+ PA_ENSURE( PaAlsaStream_Configure( stream, inputParameters, outputParameters, sampleRate, framesPerHostBuffer,
+ &inputLatency, &outputLatency, &maxHostBufferSize ) );
+ hostInputSampleFormat = stream->capture.hostSampleFormat;
+ hostOutputSampleFormat = stream->playback.hostSampleFormat;
+
+ if( framesPerHostBuffer != framesPerBuffer )
+ {
+ PA_DEBUG(( "%s: Number of frames per user and host buffer differs\n", __FUNCTION__ ));
+ }
+
+ PA_ENSURE( PaUtil_InitializeBufferProcessor( &stream->bufferProcessor,
+ numInputChannels, inputSampleFormat, hostInputSampleFormat,
+ numOutputChannels, outputSampleFormat, hostOutputSampleFormat,
+ sampleRate, streamFlags, framesPerBuffer, maxHostBufferSize,
+ hostBufferSizeMode, callback, userData ) );
+
+ /* Ok, buffer processor is initialized, now we can deduce it's latency */
+ if( numInputChannels > 0 )
+ stream->streamRepresentation.streamInfo.inputLatency = inputLatency + PaUtil_GetBufferProcessorInputLatency(
+ &stream->bufferProcessor );
+ if( numOutputChannels > 0 )
+ stream->streamRepresentation.streamInfo.outputLatency = outputLatency + PaUtil_GetBufferProcessorOutputLatency(
+ &stream->bufferProcessor );
+
+ *s = (PaStream*)stream;
+
+ return result;
+
+error:
+ if( stream )
+ PaAlsaStream_Terminate( stream );
+
+ return result;
+}
+
+static PaError CloseStream( PaStream* s )
+{
+ PaError result = paNoError;
+ PaAlsaStream *stream = (PaAlsaStream*)s;
+
+ PaUtil_TerminateBufferProcessor( &stream->bufferProcessor );
+ PaUtil_TerminateStreamRepresentation( &stream->streamRepresentation );
+
+ PaAlsaStream_Terminate( stream );
+
+ return result;
+}
+
+static void SilenceBuffer( PaAlsaStream *stream )
+{
+ const snd_pcm_channel_area_t *areas;
+ snd_pcm_uframes_t frames = (snd_pcm_uframes_t)snd_pcm_avail_update( stream->playback.pcm ), offset;
+
+ snd_pcm_mmap_begin( stream->playback.pcm, &areas, &offset, &frames );
+ snd_pcm_areas_silence( areas, offset, stream->playback.numHostChannels, frames, stream->playback.nativeFormat );
+ snd_pcm_mmap_commit( stream->playback.pcm, offset, frames );
+}
+
+/** Start/prepare pcm(s) for streaming.
+ *
+ * Depending on wether the stream is in callback or blocking mode, we will respectively start or simply
+ * prepare the playback pcm. If the buffer has _not_ been primed, we will in callback mode prepare and
+ * silence the buffer before starting playback. In blocking mode we simply prepare, as the playback will
+ * be started automatically as the user writes to output.
+ *
+ * The capture pcm, however, will simply be prepared and started.
+ *
+ * PaAlsaStream::startMtx makes sure access is synchronized (useful in callback mode)
+ */
+static PaError AlsaStart( PaAlsaStream *stream, int priming )
+{
+ PaError result = paNoError;
+
+ if( stream->playback.pcm )
+ {
+ if( stream->callbackMode )
+ {
+ if( !priming )
+ {
+ /* Buffer isn't primed, so prepare and silence */
+ ENSURE_( snd_pcm_prepare( stream->playback.pcm ), paUnanticipatedHostError );
+ SilenceBuffer( stream );
+ }
+ ENSURE_( snd_pcm_start( stream->playback.pcm ), paUnanticipatedHostError );
+ }
+ else
+ ENSURE_( snd_pcm_prepare( stream->playback.pcm ), paUnanticipatedHostError );
+ }
+ if( stream->capture.pcm && !stream->pcmsSynced )
+ {
+ ENSURE_( snd_pcm_prepare( stream->capture.pcm ), paUnanticipatedHostError );
+ /* For a blocking stream we want to start capture as well, since nothing will happen otherwise */
+ ENSURE_( snd_pcm_start( stream->capture.pcm ), paUnanticipatedHostError );
+ }
+
+end:
+ return result;
+error:
+ goto end;
+}
+
+/** Utility function for determining if pcms are in running state.
+ *
+ */
+static int IsRunning( PaAlsaStream *stream )
+{
+ int result = 0;
+
+ ASSERT_CALL_( pthread_mutex_lock( &stream->stateMtx ), 0 ); /* Synchronize access to pcm state */
+ if( stream->capture.pcm )
+ {
+ snd_pcm_state_t capture_state = snd_pcm_state( stream->capture.pcm );
+
+ if( capture_state == SND_PCM_STATE_RUNNING || capture_state == SND_PCM_STATE_XRUN
+ || capture_state == SND_PCM_STATE_DRAINING )
+ {
+ result = 1;
+ goto end;
+ }
+ }
+
+ if( stream->playback.pcm )
+ {
+ snd_pcm_state_t playback_state = snd_pcm_state( stream->playback.pcm );
+
+ if( playback_state == SND_PCM_STATE_RUNNING || playback_state == SND_PCM_STATE_XRUN
+ || playback_state == SND_PCM_STATE_DRAINING )
+ {
+ result = 1;
+ goto end;
+ }
+ }
+
+end:
+ ASSERT_CALL_( pthread_mutex_unlock( &stream->stateMtx ), 0 );
+
+ return result;
+}
+
+static PaError StartStream( PaStream *s )
+{
+ PaError result = paNoError;
+ PaAlsaStream *stream = (PaAlsaStream*)s;
+ int streamStarted = 0; /* So we can know wether we need to take the stream down */
+
+ /* Ready the processor */
+ PaUtil_ResetBufferProcessor( &stream->bufferProcessor );
+
+ /* Set now, so we can test for activity further down */
+ stream->isActive = 1;
+
+ if( stream->callbackMode )
+ {
+ int res = 0;
+ PaTime pt = PaUtil_GetTime();
+ struct timespec ts;
+
+ PA_ENSURE( CreateCallbackThread( &stream->threading, &CallbackThreadFunc, stream ) );
+ streamStarted = 1;
+
+ /* Wait for stream to be started */
+ ts.tv_sec = (time_t) floor( pt + 1 );
+ ts.tv_nsec = (long) ((pt - floor( pt )) * 1000000000);
+
+ /* Since we'll be holding a lock on the startMtx (when not waiting on the condition), IsRunning won't be checking
+ * stream state at the same time as the callback thread affects it. We also check IsStreamActive, in the unlikely
+ * case the callback thread exits in the meantime (the stream will be considered inactive after the thread exits) */
+ ASSERT_CALL_( pthread_mutex_lock( &stream->startMtx ), 0 );
+
+ /* Due to possible spurious wakeups, we enclose in a loop */
+ while( !IsRunning( stream ) && IsStreamActive( s ) && !res )
+ {
+ res = pthread_cond_timedwait( &stream->startCond, &stream->startMtx, &ts );
+ }
+ ASSERT_CALL_( pthread_mutex_unlock( &stream->startMtx ), 0 );
+
+ PA_UNLESS( !res || res == ETIMEDOUT, paInternalError );
+ PA_DEBUG(( "%s: Waited for %g seconds for stream to start\n", __FUNCTION__, PaUtil_GetTime() - pt ));
+
+ if( res == ETIMEDOUT )
+ {
+ PA_ENSURE( paTimedOut );
+ }
+ }
+ else
+ {
+ PA_ENSURE( AlsaStart( stream, 0 ) );
+ streamStarted = 1;
+ }
+
+end:
+ return result;
+error:
+ if( streamStarted )
+ AbortStream( stream );
+ stream->isActive = 0;
+
+ goto end;
+}
+
+static PaError AlsaStop( PaAlsaStream *stream, int abort )
+{
+ PaError result = paNoError;
+
+ if( abort )
+ {
+ if( stream->playback.pcm )
+ ENSURE_( snd_pcm_drop( stream->playback.pcm ), paUnanticipatedHostError );
+ if( stream->capture.pcm && !stream->pcmsSynced )
+ ENSURE_( snd_pcm_drop( stream->capture.pcm ), paUnanticipatedHostError );
+
+ PA_DEBUG(( "Dropped frames\n" ));
+ }
+ else
+ {
+ if( stream->playback.pcm )
+ ENSURE_( snd_pcm_drain( stream->playback.pcm ), paUnanticipatedHostError );
+ if( stream->capture.pcm && !stream->pcmsSynced )
+ ENSURE_( snd_pcm_drain( stream->capture.pcm ), paUnanticipatedHostError );
+ }
+
+end:
+ return result;
+error:
+ goto end;
+}
+
+/** Stop or abort stream.
+ *
+ * If a stream is in callback mode we will have to inspect wether the background thread has
+ * finished, or we will have to take it out. In either case we join the thread before
+ * returning. In blocking mode, we simply tell ALSA to stop abruptly (abort) or finish
+ * buffers (drain)
+ *
+ * Stream will be considered inactive (!PaAlsaStream::isActive) after a call to this function
+ */
+static PaError RealStop( PaAlsaStream *stream, int abort )
+{
+ PaError result = paNoError;
+
+ /* First deal with the callback thread, cancelling and/or joining
+ * it if necessary
+ */
+ if( stream->callbackMode )
+ {
+ PaError threadRes, watchdogRes;
+ stream->callbackAbort = abort;
+
+ if( !abort )
+ {
+ PA_DEBUG(( "Stopping callback\n" ));
+ stream->callbackStop = 1;
+ }
+ PA_ENSURE( KillCallbackThread( &stream->threading, !abort, &threadRes, &watchdogRes ) );
+ if( threadRes != paNoError )
+ PA_DEBUG(( "Callback thread returned: %d\n", threadRes ));
+ if( watchdogRes != paNoError )
+ PA_DEBUG(( "Watchdog thread returned: %d\n", watchdogRes ));
+
+ stream->callbackStop = 0; /* The deed is done */
+ stream->callback_finished = 0;
+ }
+ else
+ {
+ PA_ENSURE( AlsaStop( stream, abort ) );
+ }
+
+ stream->isActive = 0;
+
+end:
+ return result;
+
+error:
+ goto end;
+}
+
+static PaError StopStream( PaStream *s )
+{
+ return RealStop( (PaAlsaStream *) s, 0 );
+}
+
+static PaError AbortStream( PaStream *s )
+{
+ return RealStop( (PaAlsaStream * ) s, 1 );
+}
+
+/** The stream is considered stopped before StartStream, or AFTER a call to Abort/StopStream (callback
+ * returning !paContinue is not considered)
+ *
+ */
+static PaError IsStreamStopped( PaStream *s )
+{
+ PaAlsaStream *stream = (PaAlsaStream *)s;
+
+ /* callback_finished indicates we need to join callback thread (ie. in Abort/StopStream) */
+ return !IsStreamActive( s ) && !stream->callback_finished;
+}
+
+static PaError IsStreamActive( PaStream *s )
+{
+ PaAlsaStream *stream = (PaAlsaStream*)s;
+ return stream->isActive;
+}
+
+static PaTime GetStreamTime( PaStream *s )
+{
+ PaAlsaStream *stream = (PaAlsaStream*)s;
+
+ snd_timestamp_t timestamp;
+ snd_pcm_status_t *status;
+ snd_pcm_status_alloca( &status );
+
+ /* TODO: what if we have both? does it really matter? */
+
+ /* TODO: if running in callback mode, this will mean
+ * libasound routines are being called from multiple threads.
+ * need to verify that libasound is thread-safe. */
+
+ if( stream->capture.pcm )
+ {
+ snd_pcm_status( stream->capture.pcm, status );
+ }
+ else if( stream->playback.pcm )
+ {
+ snd_pcm_status( stream->playback.pcm, status );
+ }
+
+ snd_pcm_status_get_tstamp( status, &timestamp );
+ return timestamp.tv_sec + (PaTime)timestamp.tv_usec / 1000000.0;
+}
+
+static double GetStreamCpuLoad( PaStream* s )
+{
+ PaAlsaStream *stream = (PaAlsaStream*)s;
+
+ return PaUtil_GetCpuLoad( &stream->cpuLoadMeasurer );
+}
+
+static int SetApproximateSampleRate( snd_pcm_t *pcm, snd_pcm_hw_params_t *hwParams, double sampleRate )
+{
+ unsigned long approx = (unsigned long) sampleRate;
+ int dir = 0;
+ double fraction = sampleRate - approx;
+
+ assert( pcm && hwParams );
+
+ if( fraction > 0.0 )
+ {
+ if( fraction > 0.5 )
+ {
+ ++approx;
+ dir = -1;
+ }
+ else
+ dir = 1;
+ }
+
+ return snd_pcm_hw_params_set_rate( pcm, hwParams, approx, dir );
+}
+
+/* Return exact sample rate in param sampleRate */
+static int GetExactSampleRate( snd_pcm_hw_params_t *hwParams, double *sampleRate )
+{
+ unsigned int num, den;
+ int err;
+
+ assert( hwParams );
+
+ err = snd_pcm_hw_params_get_rate_numden( hwParams, &num, &den );
+ *sampleRate = (double) num / den;
+
+ return err;
+}
+
+/* Utility functions for blocking/callback interfaces */
+
+/* Atomic restart of stream (we don't want the intermediate state visible) */
+static PaError AlsaRestart( PaAlsaStream *stream )
+{
+ PaError result = paNoError;
+
+ ASSERT_CALL_( pthread_mutex_lock( &stream->stateMtx ), 0 );
+ PA_ENSURE( AlsaStop( stream, 0 ) );
+ PA_ENSURE( AlsaStart( stream, 0 ) );
+
+ PA_DEBUG(( "%s: Restarted audio\n", __FUNCTION__ ));
+
+error:
+ ASSERT_CALL_( pthread_mutex_unlock( &stream->stateMtx ), 0 );
+ return result;
+}
+
+/** Recover from xrun state.
+ *
+ */
+static PaError PaAlsaStream_HandleXrun( PaAlsaStream *self )
+{
+ PaError result = paNoError;
+ snd_pcm_status_t *st;
+ PaTime now = PaUtil_GetTime();
+ snd_timestamp_t t;
+
+ snd_pcm_status_alloca( &st );
+
+ if( self->playback.pcm )
+ {
+ snd_pcm_status( self->playback.pcm, st );
+ if( snd_pcm_status_get_state( st ) == SND_PCM_STATE_XRUN )
+ {
+ snd_pcm_status_get_trigger_tstamp( st, &t );
+ self->underrun = now * 1000 - ((PaTime) t.tv_sec * 1000 + (PaTime) t.tv_usec / 1000);
+ }
+ }
+ if( self->capture.pcm )
+ {
+ snd_pcm_status( self->capture.pcm, st );
+ if( snd_pcm_status_get_state( st ) == SND_PCM_STATE_XRUN )
+ {
+ snd_pcm_status_get_trigger_tstamp( st, &t );
+ self->overrun = now * 1000 - ((PaTime) t.tv_sec * 1000 + (PaTime) t.tv_usec / 1000);
+ }
+ }
+
+ PA_ENSURE( AlsaRestart( self ) );
+
+end:
+ return result;
+error:
+ goto end;
+}
+
+/** Decide if we should continue polling for specified direction, eventually adjust the poll timeout.
+ *
+ */
+static PaError ContinuePoll( const PaAlsaStream *stream, StreamDirection streamDir, int *pollTimeout, int *continuePoll )
+{
+ PaError result = paNoError;
+ snd_pcm_sframes_t delay, margin;
+ int err;
+ const PaAlsaStreamComponent *component = NULL, *otherComponent = NULL;
+
+ *continuePoll = 1;
+
+ if( StreamDirection_In == streamDir )
+ {
+ component = &stream->capture;
+ otherComponent = &stream->playback;
+ }
+ else
+ {
+ component = &stream->playback;
+ otherComponent = &stream->capture;
+ }
+
+ /* ALSA docs say that negative delay should indicate xrun, but in my experience snd_pcm_delay returns -EPIPE */
+ if( (err = snd_pcm_delay( otherComponent->pcm, &delay )) < 0 )
+ {
+ if( err == -EPIPE )
+ {
+ /* Xrun */
+ *continuePoll = 0;
+ goto error;
+ }
+
+ ENSURE_( err, paUnanticipatedHostError );
+ }
+
+ if( StreamDirection_Out == streamDir )
+ {
+ /* Number of eligible frames before capture overrun */
+ delay = otherComponent->bufferSize - delay;
+ }
+ margin = delay - otherComponent->framesPerBuffer / 2;
+
+ if( margin < 0 )
+ {
+ PA_DEBUG(( "%s: Stopping poll for %s\n", __FUNCTION__, StreamDirection_In == streamDir ? "capture" : "playback" ));
+ *continuePoll = 0;
+ }
+ else if( margin < otherComponent->framesPerBuffer )
+ {
+ *pollTimeout = CalculatePollTimeout( stream, margin );
+ PA_DEBUG(( "%s: Trying to poll again for %s frames, pollTimeout: %d\n",
+ __FUNCTION__, StreamDirection_In == streamDir ? "capture" : "playback", *pollTimeout ));
+ }
+
+error:
+ return result;
+}
+
+/* Callback interface */
+
+static void OnExit( void *data )
+{
+ PaAlsaStream *stream = (PaAlsaStream *) data;
+
+ assert( data );
+
+ PaUtil_ResetCpuLoadMeasurer( &stream->cpuLoadMeasurer );
+
+ stream->callback_finished = 1; /* Let the outside world know stream was stopped in callback */
+ AlsaStop( stream, stream->callbackAbort );
+ stream->callbackAbort = 0; /* Clear state */
+
+ PA_DEBUG(( "OnExit: Stoppage\n" ));
+
+ /* Eventually notify user all buffers have played */
+ if( stream->streamRepresentation.streamFinishedCallback )
+ stream->streamRepresentation.streamFinishedCallback( stream->streamRepresentation.userData );
+ stream->isActive = 0;
+}
+
+static void CalculateTimeInfo( PaAlsaStream *stream, PaStreamCallbackTimeInfo *timeInfo )
+{
+ snd_pcm_status_t *capture_status, *playback_status;
+ snd_timestamp_t capture_timestamp, playback_timestamp;
+ PaTime capture_time = 0., playback_time = 0.;
+
+ snd_pcm_status_alloca( &capture_status );
+ snd_pcm_status_alloca( &playback_status );
+
+ if( stream->capture.pcm )
+ {
+ snd_pcm_sframes_t capture_delay;
+
+ snd_pcm_status( stream->capture.pcm, capture_status );
+ snd_pcm_status_get_tstamp( capture_status, &capture_timestamp );
+
+ capture_time = capture_timestamp.tv_sec +
+ ((PaTime)capture_timestamp.tv_usec / 1000000.0);
+ timeInfo->currentTime = capture_time;
+
+ capture_delay = snd_pcm_status_get_delay( capture_status );
+ timeInfo->inputBufferAdcTime = timeInfo->currentTime -
+ (PaTime)capture_delay / stream->streamRepresentation.streamInfo.sampleRate;
+ }
+ if( stream->playback.pcm )
+ {
+ snd_pcm_sframes_t playback_delay;
+
+ snd_pcm_status( stream->playback.pcm, playback_status );
+ snd_pcm_status_get_tstamp( playback_status, &playback_timestamp );
+
+ playback_time = playback_timestamp.tv_sec +
+ ((PaTime)playback_timestamp.tv_usec / 1000000.0);
+
+ if( stream->capture.pcm ) /* Full duplex */
+ {
+ /* Hmm, we have both a playback and a capture timestamp.
+ * Hopefully they are the same... */
+ if( fabs( capture_time - playback_time ) > 0.01 )
+ PA_DEBUG(("Capture time and playback time differ by %f\n", fabs(capture_time-playback_time)));
+ }
+ else
+ timeInfo->currentTime = playback_time;
+
+ playback_delay = snd_pcm_status_get_delay( playback_status );
+ timeInfo->outputBufferDacTime = timeInfo->currentTime +
+ (PaTime)playback_delay / stream->streamRepresentation.streamInfo.sampleRate;
+ }
+}
+
+/** Called after buffer processing is finished.
+ *
+ * A number of mmapped frames is committed, it is possible that an xrun has occurred in the meantime.
+ *
+ * @param numFrames The number of frames that has been processed
+ * @param xrun Return whether an xrun has occurred
+ */
+static PaError PaAlsaStreamComponent_EndProcessing( PaAlsaStreamComponent *self, unsigned long numFrames, int *xrun )
+{
+ PaError result = paNoError;
+ int res;
+
+ /* @concern FullDuplex It is possible that only one direction is marked ready after polling, and processed
+ * afterwards
+ */
+ if( !self->ready )
+ goto end;
+
+ res = snd_pcm_mmap_commit( self->pcm, self->offset, numFrames );
+ if( res == -EPIPE || res == -ESTRPIPE )
+ {
+ *xrun = 1;
+ }
+ else
+ {
+ ENSURE_( res, paUnanticipatedHostError );
+ }
+
+end:
+error:
+ return result;
+}
+
+/* Extract buffer from channel area */
+static unsigned char *ExtractAddress( const snd_pcm_channel_area_t *area, snd_pcm_uframes_t offset )
+{
+ return (unsigned char *) area->addr + (area->first + offset * area->step) / 8;
+}
+
+/** Do necessary adaption between user and host channels.
+ *
+ @concern ChannelAdaption Adapting between user and host channels can involve silencing unused channels and
+ duplicating mono information if host outputs come in pairs.
+ */
+static PaError PaAlsaStreamComponent_DoChannelAdaption( PaAlsaStreamComponent *self, PaUtilBufferProcessor *bp, int numFrames )
+{
+ PaError result = paNoError;
+ unsigned char *p;
+ int i;
+ int unusedChans = self->numHostChannels - self->numUserChannels;
+ unsigned char *src, *dst;
+ int convertMono = (self->numHostChannels % 2) == 0 && (self->numUserChannels % 2) != 0;
+
+ assert( StreamDirection_Out == self->streamDir );
+
+ if( self->hostInterleaved )
+ {
+ int swidth = snd_pcm_format_size( self->nativeFormat, 1 );
+ unsigned char *buffer = ExtractAddress( self->channelAreas, self->offset );
+
+ /* Start after the last user channel */
+ p = buffer + self->numUserChannels * swidth;
+
+ if( convertMono )
+ {
+ /* Convert the last user channel into stereo pair */
+ src = buffer + (self->numUserChannels - 1) * swidth;
+ for( i = 0; i < numFrames; ++i )
+ {
+ dst = src + swidth;
+ memcpy( dst, src, swidth );
+ src += self->numHostChannels * swidth;
+ }
+
+ /* Don't touch the channel we just wrote to */
+ p += swidth;
+ --unusedChans;
+ }
+
+ if( unusedChans > 0 )
+ {
+ /* Silence unused output channels */
+ for( i = 0; i < numFrames; ++i )
+ {
+ memset( p, 0, swidth * unusedChans );
+ p += self->numHostChannels * swidth;
+ }
+ }
+ }
+ else
+ {
+ /* We extract the last user channel */
+ if( convertMono )
+ {
+ ENSURE_( snd_pcm_area_copy( self->channelAreas + self->numUserChannels, self->offset, self->channelAreas +
+ (self->numUserChannels - 1), self->offset, numFrames, self->nativeFormat ), paUnanticipatedHostError );
+ --unusedChans;
+ }
+ if( unusedChans > 0 )
+ {
+ snd_pcm_areas_silence( self->channelAreas + (self->numHostChannels - unusedChans), self->offset, unusedChans, numFrames,
+ self->nativeFormat );
+ }
+ }
+
+error:
+ return result;
+}
+
+static PaError PaAlsaStream_EndProcessing( PaAlsaStream *self, unsigned long numFrames, int *xrunOccurred )
+{
+ PaError result = paNoError;
+ int xrun = 0;
+
+ if( self->capture.pcm )
+ {
+ PA_ENSURE( PaAlsaStreamComponent_EndProcessing( &self->capture, numFrames, &xrun ) );
+ }
+ if( self->playback.pcm )
+ {
+ if( self->playback.numHostChannels > self->playback.numUserChannels )
+ PA_ENSURE( PaAlsaStreamComponent_DoChannelAdaption( &self->playback, &self->bufferProcessor, numFrames ) );
+ PA_ENSURE( PaAlsaStreamComponent_EndProcessing( &self->playback, numFrames, &xrun ) );
+ }
+
+error:
+ *xrunOccurred = xrun;
+ return result;
+}
+
+/** Update the number of available frames.
+ *
+ */
+static PaError PaAlsaStreamComponent_GetAvailableFrames( PaAlsaStreamComponent *self, unsigned long *numFrames, int *xrunOccurred )
+{
+ PaError result = paNoError;
+ snd_pcm_sframes_t framesAvail = snd_pcm_avail_update( self->pcm );
+ *xrunOccurred = 0;
+
+ if( -EPIPE == framesAvail )
+ {
+ *xrunOccurred = 1;
+ framesAvail = 0;
+ }
+ else
+ ENSURE_( framesAvail, paUnanticipatedHostError );
+
+ *numFrames = framesAvail;
+
+error:
+ return result;
+}
+
+/** Fill in pollfd objects.
+ */
+static PaError PaAlsaStreamComponent_BeginPolling( PaAlsaStreamComponent *self, struct pollfd *pfds )
+{
+ PaError result = paNoError;
+ int ret = snd_pcm_poll_descriptors( self->pcm, pfds, self->nfds );
+ assert( ret == self->nfds );
+
+ self->ready = 0;
+
+ return result;
+}
+
+/** Examine results from poll().
+ *
+ * @param pfds pollfds to inspect
+ * @param shouldPoll Should we continue to poll
+ * @param xrun Has an xrun occurred
+ */
+static PaError PaAlsaStreamComponent_EndPolling( PaAlsaStreamComponent *self, struct pollfd *pfds, int *shouldPoll, int *xrun )
+{
+ PaError result = paNoError;
+ unsigned short revents;
+
+ ENSURE_( snd_pcm_poll_descriptors_revents( self->pcm, pfds, self->nfds, &revents ), paUnanticipatedHostError );
+ if( revents != 0 )
+ {
+ if( revents & POLLERR )
+ {
+ *xrun = 1;
+ }
+ else
+ self->ready = 1;
+
+ *shouldPoll = 0;
+ }
+
+error:
+ return result;
+}
+
+/** Return the number of available frames for this stream.
+ *
+ * @concern FullDuplex The minimum available for the two directions is calculated, it might be desirable to ignore
+ * one direction however (not marked ready from poll), so this is controlled by queryCapture and queryPlayback.
+ *
+ * @param queryCapture Check available for capture
+ * @param queryPlayback Check available for playback
+ * @param available The returned number of frames
+ * @param xrunOccurred Return whether an xrun has occurred
+ */
+static PaError PaAlsaStream_GetAvailableFrames( PaAlsaStream *self, int queryCapture, int queryPlayback, unsigned long
+ *available, int *xrunOccurred )
+{
+ PaError result = paNoError;
+ unsigned long captureFrames, playbackFrames;
+ *xrunOccurred = 0;
+
+ assert( queryCapture || queryPlayback );
+
+ if( queryCapture )
+ {
+ assert( self->capture.pcm );
+ PA_ENSURE( PaAlsaStreamComponent_GetAvailableFrames( &self->capture, &captureFrames, xrunOccurred ) );
+ if( *xrunOccurred )
+ goto end;
+ }
+ if( queryPlayback )
+ {
+ assert( self->playback.pcm );
+ PA_ENSURE( PaAlsaStreamComponent_GetAvailableFrames( &self->playback, &playbackFrames, xrunOccurred ) );
+ if( *xrunOccurred )
+ goto end;
+ }
+
+ if( queryCapture && queryPlayback )
+ {
+ *available = PA_MIN( captureFrames, playbackFrames );
+ }
+ else if( queryCapture )
+ {
+ *available = captureFrames;
+ }
+ else
+ {
+ *available = playbackFrames;
+ }
+
+end:
+error:
+ return result;
+}
+
+/** Wait for and report available buffer space from ALSA.
+ *
+ * Unless ALSA reports a minimum of frames available for I/O, we poll the ALSA filedescriptors for more.
+ * Both of these operations can uncover xrun conditions.
+ *
+ * @concern Xruns Both polling and querying available frames can report an xrun condition.
+ *
+ * @param framesAvail Return the number of available frames
+ * @param xrunOccurred Return whether an xrun has occurred
+ */
+static PaError PaAlsaStream_WaitForFrames( PaAlsaStream *self, unsigned long *framesAvail, int *xrunOccurred )
+{
+ PaError result = paNoError;
+ int pollPlayback = self->playback.pcm != NULL, pollCapture = self->capture.pcm != NULL;
+ int pollTimeout = self->pollTimeout;
+ int xrun = 0;
+
+ assert( self );
+ assert( framesAvail );
+
+ if( !self->callbackMode )
+ {
+ /* In blocking mode we will only wait if necessary */
+ PA_ENSURE( PaAlsaStream_GetAvailableFrames( self, self->capture.pcm != NULL, self->playback.pcm != NULL,
+ framesAvail, &xrun ) );
+ if( xrun )
+ {
+ goto end;
+ }
+
+ if( *framesAvail > 0 )
+ {
+ /* Mark pcms ready from poll */
+ if( self->capture.pcm )
+ self->capture.ready = 1;
+ if( self->playback.pcm )
+ self->playback.ready = 1;
+
+ goto end;
+ }
+ }
+
+ while( pollPlayback || pollCapture )
+ {
+ int totalFds = 0;
+ struct pollfd *capturePfds = NULL, *playbackPfds = NULL;
+
+ pthread_testcancel();
+
+ if( pollCapture )
+ {
+ capturePfds = self->pfds;
+ PA_ENSURE( PaAlsaStreamComponent_BeginPolling( &self->capture, capturePfds ) );
+ totalFds += self->capture.nfds;
+ }
+ if( pollPlayback )
+ {
+ playbackPfds = self->pfds + (self->capture.pcm ? self->capture.nfds : 0);
+ PA_ENSURE( PaAlsaStreamComponent_BeginPolling( &self->playback, playbackPfds ) );
+ totalFds += self->playback.nfds;
+ }
+
+ if( poll( self->pfds, totalFds, pollTimeout ) < 0 )
+ {
+ /* XXX: Depend on preprocessor condition? */
+ if( errno == EINTR ) { /* gdb */
+ continue;
+ }
+
+ /* TODO: Add macro for checking system calls */
+ PA_ENSURE( paInternalError );
+ }
+
+ /* check the return status of our pfds */
+ if( pollCapture )
+ {
+ PA_ENSURE( PaAlsaStreamComponent_EndPolling( &self->capture, capturePfds, &pollCapture, &xrun ) );
+ }
+ if( pollPlayback )
+ {
+ PA_ENSURE( PaAlsaStreamComponent_EndPolling( &self->playback, playbackPfds, &pollPlayback, &xrun ) );
+ }
+ if( xrun )
+ {
+ break;
+ }
+
+ /* @concern FullDuplex If only one of two pcms is ready we may want to compromise between the two.
+ * If there is less than half a period's worth of samples left of frames in the other pcm's buffer we will
+ * stop polling.
+ */
+ if( self->capture.pcm && self->playback.pcm )
+ {
+ if( pollCapture && !pollPlayback )
+ {
+ PA_ENSURE( ContinuePoll( self, StreamDirection_In, &pollTimeout, &pollCapture ) );
+ }
+ else if( pollPlayback && !pollCapture )
+ {
+ PA_ENSURE( ContinuePoll( self, StreamDirection_Out, &pollTimeout, &pollPlayback ) );
+ }
+ }
+ }
+
+ if( !xrun )
+ {
+ /* Get the number of available frames for the pcms that are marked ready.
+ * @concern FullDuplex If only one direction is marked ready (from poll), the number of frames available for
+ * the other direction is returned. This under the assumption that input is dropped earlier if paNeverDropInput
+ * is not specified.
+ */
+ int captureReady = self->capture.pcm ? self->capture.ready : 0,
+ playbackReady = self->playback.pcm ? self->playback.ready : 0;
+ PA_ENSURE( PaAlsaStream_GetAvailableFrames( self, captureReady, playbackReady, framesAvail, &xrun ) );
+
+ if( self->capture.pcm && self->playback.pcm )
+ {
+ if( !self->playback.ready && !self->neverDropInput )
+ {
+ /* TODO: Drop input */
+ }
+ }
+ }
+
+end:
+error:
+ if( xrun )
+ {
+ /* Recover from the xrun state */
+ PA_ENSURE( PaAlsaStream_HandleXrun( self ) );
+ *framesAvail = 0;
+ }
+ *xrunOccurred = xrun;
+
+ return result;
+}
+
+/** Register per-channel ALSA buffer information with buffer processor.
+ *
+ * Mmapped buffer space is acquired from ALSA, and registered with the buffer processor. Differences between the
+ * number of host and user channels is taken into account.
+ *
+ * @param numFrames On entrance the number of requested frames, on exit the number of contiguously accessible frames.
+ */
+static PaError PaAlsaStreamComponent_RegisterChannels( PaAlsaStreamComponent *self, PaUtilBufferProcessor *bp,
+ unsigned long *numFrames, int *xrun )
+{
+ PaError result = paNoError;
+ const snd_pcm_channel_area_t *areas, *area;
+ void (*setChannel)(PaUtilBufferProcessor *, unsigned int, void *, unsigned int) =
+ StreamDirection_In == self->streamDir ? PaUtil_SetInputChannel : PaUtil_SetOutputChannel;
+ unsigned char *buffer, *p;
+ int i;
+ unsigned long framesAvail;
+
+ /* This _must_ be called before mmap_begin */
+ PA_ENSURE( PaAlsaStreamComponent_GetAvailableFrames( self, &framesAvail, xrun ) );
+ if( *xrun )
+ {
+ *numFrames = 0;
+ goto end;
+ }
+
+ ENSURE_( snd_pcm_mmap_begin( self->pcm, &areas, &self->offset, numFrames ), paUnanticipatedHostError );
+
+ if( self->hostInterleaved )
+ {
+ int swidth = snd_pcm_format_size( self->nativeFormat, 1 );
+
+ p = buffer = ExtractAddress( areas, self->offset );
+ for( i = 0; i < self->numUserChannels; ++i )
+ {
+ /* We're setting the channels up to userChannels, but the stride will be hostChannels samples */
+ setChannel( bp, i, p, self->numHostChannels );
+ p += swidth;
+ }
+ }
+ else
+ {
+ for( i = 0; i < self->numUserChannels; ++i )
+ {
+ area = areas + i;
+ buffer = ExtractAddress( area, self->offset );
+ setChannel( bp, i, buffer, 1 );
+ }
+ }
+
+ /* @concern ChannelAdaption Buffer address is recorded so we can do some channel adaption later */
+ self->channelAreas = (snd_pcm_channel_area_t *)areas;
+
+end:
+error:
+ return result;
+}
+
+/** Initiate buffer processing.
+ *
+ * ALSA buffers are registered with the PA buffer processor and the buffer size (in frames) set.
+ *
+ * @concern FullDuplex If both directions are being processed, the minimum amount of frames for the two directions is
+ * calculated.
+ *
+ * @param numFrames On entrance the number of available frames, on exit the number of received frames
+ * @param xrunOccurred Return whether an xrun has occurred
+ */
+static PaError PaAlsaStream_SetUpBuffers( PaAlsaStream *self, unsigned long *numFrames, int *xrunOccurred )
+{
+ PaError result = paNoError;
+ unsigned long captureFrames = ULONG_MAX, playbackFrames = ULONG_MAX, commonFrames = 0;
+ int xrun = 0;
+
+ /* Extract per-channel ALSA buffer pointers and register them with the buffer processor.
+ * It is possible that a direction is not marked ready however, because it is out of sync with the other.
+ */
+ if( self->capture.pcm && self->capture.ready )
+ {
+ captureFrames = *numFrames;
+ PA_ENSURE( PaAlsaStreamComponent_RegisterChannels( &self->capture, &self->bufferProcessor, &captureFrames,
+ &xrun ) );
+ }
+ if( self->playback.pcm && self->playback.ready )
+ {
+ playbackFrames = *numFrames;
+ PA_ENSURE( PaAlsaStreamComponent_RegisterChannels( &self->playback, &self->bufferProcessor, &playbackFrames,
+ &xrun ) );
+ }
+ if( xrun )
+ {
+ /* Nothing more to do */
+ assert( 0 == commonFrames );
+ goto end;
+ }
+
+ commonFrames = PA_MIN( captureFrames, playbackFrames );
+ assert( commonFrames <= *numFrames );
+
+ /* Inform PortAudio of the number of frames we got.
+ * @concern FullDuplex We might be experiencing underflow in either end; if its an input underflow, we go on
+ * with output. If its output underflow however, depending on the paNeverDropInput flag, we may want to simply
+ * discard the excess input or call the callback with paOutputOverflow flagged.
+ */
+ if( self->capture.pcm )
+ {
+ if( self->capture.ready )
+ {
+ PaUtil_SetInputFrameCount( &self->bufferProcessor, commonFrames );
+ }
+ else
+ {
+ /* We have input underflow */
+ PaUtil_SetNoInput( &self->bufferProcessor );
+ }
+ }
+ if( self->playback.pcm )
+ {
+ if( self->playback.ready )
+ {
+ PaUtil_SetOutputFrameCount( &self->bufferProcessor, commonFrames );
+ }
+ else
+ {
+ /* We have output underflow, but keeping input data (paNeverDropInput) */
+ /* assert( self->neverDropInput ); */
+ PaUtil_SetNoOutput( &self->bufferProcessor );
+ }
+ }
+
+end:
+ *numFrames = commonFrames;
+error:
+ if( xrun )
+ {
+ PA_ENSURE( PaAlsaStream_HandleXrun( self ) );
+ *numFrames = 0;
+ }
+ *xrunOccurred = xrun;
+
+ return result;
+}
+
+/** Callback thread's function.
+ *
+ * Roughly, the workflow can be described in the following way: The number of available frames that can be processed
+ * directly is obtained from ALSA, we then request as much directly accessible memory as possible within this amount
+ * from ALSA. The buffer memory is registered with the PA buffer processor and processing is carried out with
+ * PaUtil_EndBufferProcessing. Finally, the number of processed frames is reported to ALSA. The processing can
+ * happen in several iterations untill we have consumed the known number of available frames (or an xrun is detected).
+ */
+static void *CallbackThreadFunc( void *userData )
+{
+ PaError result = paNoError, *pres = NULL;
+ PaAlsaStream *stream = (PaAlsaStream*) userData;
+ PaStreamCallbackTimeInfo timeInfo = {0, 0, 0};
+ snd_pcm_sframes_t startThreshold = 0;
+ int callbackResult = paContinue;
+ PaStreamCallbackFlags cbFlags = 0; /* We might want to keep state across iterations */
+ int streamStarted = 0;
+
+ assert( stream );
+
+ callbackThread_ = pthread_self();
+ /* Execute OnExit when exiting */
+ pthread_cleanup_push( &OnExit, stream );
+
+ /* Not implemented */
+ assert( !stream->primeBuffers );
+
+ /* @concern StreamStart If the output is being primed the output pcm needs to be prepared, otherwise the
+ * stream is started immediately. The latter involves signaling the waiting main thread.
+ */
+ if( stream->primeBuffers )
+ {
+ snd_pcm_sframes_t avail;
+
+ if( stream->playback.pcm )
+ ENSURE_( snd_pcm_prepare( stream->playback.pcm ), paUnanticipatedHostError );
+ if( stream->capture.pcm && !stream->pcmsSynced )
+ ENSURE_( snd_pcm_prepare( stream->capture.pcm ), paUnanticipatedHostError );
+
+ /* We can't be certain that the whole ring buffer is available for priming, but there should be
+ * at least one period */
+ avail = snd_pcm_avail_update( stream->playback.pcm );
+ startThreshold = avail - (avail % stream->playback.framesPerBuffer);
+ assert( startThreshold >= stream->playback.framesPerBuffer );
+ }
+ else
+ {
+ ASSERT_CALL_( pthread_mutex_lock( &stream->startMtx ), 0 );
+ PA_ENSURE( AlsaStart( stream, 0 ) ); /* Buffer will be zeroed */
+ ASSERT_CALL_( pthread_cond_signal( &stream->startCond ), 0 );
+ ASSERT_CALL_( pthread_mutex_unlock( &stream->startMtx ), 0 );
+
+ streamStarted = 1;
+ }
+
+ while( 1 )
+ {
+ unsigned long framesAvail, framesGot;
+ int xrun = 0;
+
+ pthread_testcancel();
+
+ /* @concern StreamStop if the main thread has requested a stop and the stream has not been effectively
+ * stopped we signal this condition by modifying callbackResult (we'll want to flush buffered output).
+ */
+ if( stream->callbackStop && paContinue == callbackResult )
+ {
+ PA_DEBUG(( "Setting callbackResult to paComplete\n" ));
+ callbackResult = paComplete;
+ }
+
+ if( paContinue != callbackResult )
+ {
+ stream->callbackAbort = (paAbort == callbackResult);
+ if( stream->callbackAbort ||
+ /** @concern BlockAdaption Go on if adaption buffers are empty */
+ PaUtil_IsBufferProcessorOutputEmpty( &stream->bufferProcessor ) )
+ goto end;
+
+ PA_DEBUG(( "%s: Flushing buffer processor\n", __FUNCTION__ ));
+ /* There is still buffered output that needs to be processed */
+ }
+
+ /* Wait for data to become available, this comes down to polling the ALSA file descriptors untill we have
+ * a number of available frames.
+ */
+ PA_ENSURE( PaAlsaStream_WaitForFrames( stream, &framesAvail, &xrun ) );
+ if( xrun )
+ {
+ assert( 0 == framesAvail );
+ continue;
+
+ /* XXX: Report xruns to the user? A situation is conceivable where the callback is never invoked due
+ * to constant xruns, it might be desirable to notify the user of this.
+ */
+ }
+
+ /* Consume buffer space. Once we have a number of frames available for consumption we must retrieve the
+ * mmapped buffers from ALSA, this is contiguously accessible memory however, so we may receive smaller
+ * portions at a time than is available as a whole. Therefore we should be prepared to process several
+ * chunks successively. The buffers are passed to the PA buffer processor.
+ */
+ while( framesAvail > 0 )
+ {
+ xrun = 0;
+
+ pthread_testcancel();
+
+ /** @concern Xruns Under/overflows are to be reported to the callback */
+ if( stream->underrun > 0.0 )
+ {
+ cbFlags |= paOutputUnderflow;
+ stream->underrun = 0.0;
+ }
+ if( stream->overrun > 0.0 )
+ {
+ cbFlags |= paInputOverflow;
+ stream->overrun = 0.0;
+ }
+ if( stream->capture.pcm && stream->playback.pcm )
+ {
+ /** @concern FullDuplex It's possible that only one direction is being processed to avoid an
+ * under- or overflow, this should be reported correspondingly */
+ if( !stream->capture.ready )
+ {
+ cbFlags |= paInputUnderflow;
+ PA_DEBUG(( "%s: Input underflow\n", __FUNCTION__ ));
+ }
+ else if( !stream->playback.ready )
+ {
+ cbFlags |= paOutputOverflow;
+ PA_DEBUG(( "%s: Output overflow\n", __FUNCTION__ ));
+ }
+ }
+
+ CallbackUpdate( &stream->threading );
+ CalculateTimeInfo( stream, &timeInfo );
+ PaUtil_BeginBufferProcessing( &stream->bufferProcessor, &timeInfo, cbFlags );
+ cbFlags = 0;
+
+ /* CPU load measurement should include processing activivity external to the stream callback */
+ PaUtil_BeginCpuLoadMeasurement( &stream->cpuLoadMeasurer );
+
+ framesGot = framesAvail;
+ PA_ENSURE( PaAlsaStream_SetUpBuffers( stream, &framesGot, &xrun ) );
+ framesAvail -= framesGot;
+
+ if( framesGot > 0 )
+ {
+ assert( !xrun );
+
+ PaUtil_EndBufferProcessing( &stream->bufferProcessor, &callbackResult );
+ PA_ENSURE( PaAlsaStream_EndProcessing( stream, framesGot, &xrun ) );
+ }
+ PaUtil_EndCpuLoadMeasurement( &stream->cpuLoadMeasurer, framesGot );
+
+ if( framesGot == 0 )
+ {
+ if( !xrun )
+ PA_DEBUG(( "%s: Received less frames than reported from ALSA!\n", __FUNCTION__ ));
+
+ /* Go back to polling for more frames */
+ break;
+
+ }
+
+ if( paContinue != callbackResult )
+ break;
+ }
+ }
+
+ /* Match pthread_cleanup_push */
+ pthread_cleanup_pop( 1 );
+
+end:
+ pthread_exit( pres );
+
+error:
+ /* Pass on error code */
+ pres = malloc( sizeof (PaError) );
+ *pres = result;
+
+ goto end;
+}
+
+/* Blocking interface */
+
+static PaError ReadStream( PaStream* s, void *buffer, unsigned long frames )
+{
+ PaError result = paNoError;
+ PaAlsaStream *stream = (PaAlsaStream*)s;
+ unsigned long framesGot, framesAvail;
+ void *userBuffer;
+ snd_pcm_t *save = stream->playback.pcm;
+
+ assert( stream );
+
+ PA_UNLESS( stream->capture.pcm, paCanNotReadFromAnOutputOnlyStream );
+
+ /* Disregard playback */
+ stream->playback.pcm = NULL;
+
+ if( stream->overrun > 0. )
+ {
+ result = paInputOverflowed;
+ stream->overrun = 0.0;
+ }
+
+ if( stream->capture.userInterleaved )
+ userBuffer = buffer;
+ else
+ {
+ /* Copy channels into local array */
+ userBuffer = stream->capture.userBuffers;
+ memcpy( userBuffer, buffer, sizeof (void *) * stream->capture.numUserChannels );
+ }
+
+ /* Start stream if in prepared state */
+ if( snd_pcm_state( stream->capture.pcm ) == SND_PCM_STATE_PREPARED )
+ {
+ ENSURE_( snd_pcm_start( stream->capture.pcm ), paUnanticipatedHostError );
+ }
+
+ while( frames > 0 )
+ {
+ int xrun = 0;
+ PA_ENSURE( PaAlsaStream_WaitForFrames( stream, &framesAvail, &xrun ) );
+ framesGot = PA_MIN( framesAvail, frames );
+
+ PA_ENSURE( PaAlsaStream_SetUpBuffers( stream, &framesGot, &xrun ) );
+ if( framesGot > 0 )
+ {
+ framesGot = PaUtil_CopyInput( &stream->bufferProcessor, &userBuffer, framesGot );
+ PA_ENSURE( PaAlsaStream_EndProcessing( stream, framesGot, &xrun ) );
+ frames -= framesGot;
+ }
+ }
+
+end:
+ stream->playback.pcm = save;
+ return result;
+error:
+ goto end;
+}
+
+static PaError WriteStream( PaStream* s, const void *buffer, unsigned long frames )
+{
+ PaError result = paNoError;
+ signed long err;
+ PaAlsaStream *stream = (PaAlsaStream*)s;
+ snd_pcm_uframes_t framesGot, framesAvail;
+ const void *userBuffer;
+ snd_pcm_t *save = stream->capture.pcm;
+
+ assert( stream );
+
+ PA_UNLESS( stream->playback.pcm, paCanNotWriteToAnInputOnlyStream );
+
+ /* Disregard capture */
+ stream->capture.pcm = NULL;
+
+ if( stream->underrun > 0. )
+ {
+ result = paOutputUnderflowed;
+ stream->underrun = 0.0;
+ }
+
+ if( stream->playback.userInterleaved )
+ userBuffer = buffer;
+ else /* Copy channels into local array */
+ {
+ userBuffer = stream->playback.userBuffers;
+ memcpy( (void *)userBuffer, buffer, sizeof (void *) * stream->playback.numUserChannels );
+ }
+
+ while( frames > 0 )
+ {
+ int xrun = 0;
+ snd_pcm_uframes_t hwAvail;
+
+ PA_ENSURE( PaAlsaStream_WaitForFrames( stream, &framesAvail, &xrun ) );
+ framesGot = PA_MIN( framesAvail, frames );
+
+ PA_ENSURE( PaAlsaStream_SetUpBuffers( stream, &framesGot, &xrun ) );
+ if( framesGot > 0 )
+ {
+ framesGot = PaUtil_CopyOutput( &stream->bufferProcessor, &userBuffer, framesGot );
+ PA_ENSURE( PaAlsaStream_EndProcessing( stream, framesGot, &xrun ) );
+ frames -= framesGot;
+ }
+
+ /* Frames residing in buffer */
+ PA_ENSURE( err = GetStreamWriteAvailable( stream ) );
+ framesAvail = err;
+ hwAvail = stream->playback.bufferSize - framesAvail;
+
+ /* Start stream after one period of samples worth */
+ if( snd_pcm_state( stream->playback.pcm ) == SND_PCM_STATE_PREPARED &&
+ hwAvail >= stream->playback.framesPerBuffer )
+ {
+ ENSURE_( snd_pcm_start( stream->playback.pcm ), paUnanticipatedHostError );
+ }
+ }
+
+end:
+ stream->capture.pcm = save;
+ return result;
+error:
+ goto end;
+}
+
+/* Return frames available for reading. In the event of an overflow, the capture pcm will be restarted */
+static signed long GetStreamReadAvailable( PaStream* s )
+{
+ PaError result = paNoError;
+ PaAlsaStream *stream = (PaAlsaStream*)s;
+ unsigned long avail;
+ int xrun;
+
+ PA_ENSURE( PaAlsaStreamComponent_GetAvailableFrames( &stream->capture, &avail, &xrun ) );
+ if( xrun )
+ {
+ PA_ENSURE( PaAlsaStream_HandleXrun( stream ) );
+ PA_ENSURE( PaAlsaStreamComponent_GetAvailableFrames( &stream->capture, &avail, &xrun ) );
+ if( xrun )
+ PA_ENSURE( paInputOverflowed );
+ }
+
+ return (signed long)avail;
+
+error:
+ return result;
+}
+
+static signed long GetStreamWriteAvailable( PaStream* s )
+{
+ PaError result = paNoError;
+ PaAlsaStream *stream = (PaAlsaStream*)s;
+ unsigned long avail;
+ int xrun;
+
+ PA_ENSURE( PaAlsaStreamComponent_GetAvailableFrames( &stream->playback, &avail, &xrun ) );
+ if( xrun )
+ {
+ snd_pcm_sframes_t savail;
+
+ PA_ENSURE( PaAlsaStream_HandleXrun( stream ) );
+ savail = snd_pcm_avail_update( stream->playback.pcm );
+
+ /* savail should not contain -EPIPE now, since PaAlsaStream_HandleXrun will only prepare the pcm */
+ ENSURE_( savail, paUnanticipatedHostError );
+
+ avail = (unsigned long) savail;
+ }
+
+ return (signed long)avail;
+
+error:
+ return result;
+}
+
+/* Extensions */
+
+/* Initialize host api specific structure */
+void PaAlsa_InitializeStreamInfo( PaAlsaStreamInfo *info )
+{
+ info->size = sizeof (PaAlsaStreamInfo);
+ info->hostApiType = paALSA;
+ info->version = 1;
+ info->deviceString = NULL;
+}
+
+void PaAlsa_EnableRealtimeScheduling( PaStream *s, int enable )
+{
+ PaAlsaStream *stream = (PaAlsaStream *) s;
+ stream->threading.rtSched = enable;
+}
+
+void PaAlsa_EnableWatchdog( PaStream *s, int enable )
+{
+ PaAlsaStream *stream = (PaAlsaStream *) s;
+ stream->threading.useWatchdog = enable;
+}
diff --git a/pjmedia/src/pjmedia/portaudio/pa_linux_alsa.h b/pjmedia/src/pjmedia/portaudio/pa_linux_alsa.h
index fd417452..e6f44b16 100644
--- a/pjmedia/src/pjmedia/portaudio/pa_linux_alsa.h
+++ b/pjmedia/src/pjmedia/portaudio/pa_linux_alsa.h
@@ -1,64 +1,64 @@
-#ifndef PA_LINUX_ALSA_H
-#define PA_LINUX_ALSA_H
-
-/*
- * $Id: pa_linux_alsa.h,v 1.1.2.12 2004/09/25 14:15:25 aknudsen Exp $
- * PortAudio Portable Real-Time Audio Library
- * ALSA-specific extensions
- *
- * Copyright (c) 1999-2000 Ross Bencina and Phil Burk
- *
- * 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.
- *
- * Any person wishing to distribute modifications to the Software is
- * requested to send the modifications to the original developer so that
- * they can be incorporated into the canonical version.
- *
- * 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.
- *
- */
-
-/** @file
- * ALSA-specific PortAudio API extension header file.
- */
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-typedef struct PaAlsaStreamInfo
-{
- unsigned long size;
- PaHostApiTypeId hostApiType;
- unsigned long version;
-
- const char *deviceString;
-}
-PaAlsaStreamInfo;
-
-void PaAlsa_InitializeStreamInfo( PaAlsaStreamInfo *info );
-
-void PaAlsa_EnableRealtimeScheduling( PaStream *s, int enable );
-
-void PaAlsa_EnableWatchdog( PaStream *s, int enable );
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif
+#ifndef PA_LINUX_ALSA_H
+#define PA_LINUX_ALSA_H
+
+/*
+ * $Id: pa_linux_alsa.h,v 1.1.2.12 2004/09/25 14:15:25 aknudsen Exp $
+ * PortAudio Portable Real-Time Audio Library
+ * ALSA-specific extensions
+ *
+ * Copyright (c) 1999-2000 Ross Bencina and Phil Burk
+ *
+ * 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.
+ *
+ * Any person wishing to distribute modifications to the Software is
+ * requested to send the modifications to the original developer so that
+ * they can be incorporated into the canonical version.
+ *
+ * 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.
+ *
+ */
+
+/** @file
+ * ALSA-specific PortAudio API extension header file.
+ */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef struct PaAlsaStreamInfo
+{
+ unsigned long size;
+ PaHostApiTypeId hostApiType;
+ unsigned long version;
+
+ const char *deviceString;
+}
+PaAlsaStreamInfo;
+
+void PaAlsa_InitializeStreamInfo( PaAlsaStreamInfo *info );
+
+void PaAlsa_EnableRealtimeScheduling( PaStream *s, int enable );
+
+void PaAlsa_EnableWatchdog( PaStream *s, int enable );
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/pjmedia/src/pjmedia/portaudio/pa_process.c b/pjmedia/src/pjmedia/portaudio/pa_process.c
index 7b25b8bf..cf711d41 100644
--- a/pjmedia/src/pjmedia/portaudio/pa_process.c
+++ b/pjmedia/src/pjmedia/portaudio/pa_process.c
@@ -1,1756 +1,1756 @@
-/*
- * $Id: pa_process.c,v 1.1.2.48 2004/12/13 09:48:43 rossbencina Exp $
- * Portable Audio I/O Library
- * streamCallback <-> host buffer processing adapter
- *
- * Based on the Open Source API proposed by Ross Bencina
- * Copyright (c) 1999-2002 Ross Bencina, Phil Burk
- *
- * 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.
- *
- * Any person wishing to distribute modifications to the Software is
- * requested to send the modifications to the original developer so that
- * they can be incorporated into the canonical version.
- *
- * 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.
- */
-
-/** @file
- @brief Buffer Processor implementation.
-
- The code in this file is not optimised yet - although it's not clear that
- it needs to be. there may appear to be redundancies
- that could be factored into common functions, but the redundanceis are left
- intentionally as each appearance may have different optimisation possibilities.
-
- The optimisations which are planned involve only converting data in-place
- where possible, rather than copying to the temp buffer(s).
-
- Note that in the extreme case of being able to convert in-place, and there
- being no conversion necessary there should be some code which short-circuits
- the operation.
-
- @todo Consider cache tilings for intereave<->deinterleave.
-
- @todo implement timeInfo->currentTime int PaUtil_BeginBufferProcessing()
-
- @todo specify and implement some kind of logical policy for handling the
- underflow and overflow stream flags when the underflow/overflow overlaps
- multiple user buffers/callbacks.
-
- @todo provide support for priming the buffers with data from the callback.
- The client interface is now implemented through PaUtil_SetNoInput()
- which sets bp->hostInputChannels[0][0].data to zero. However this is
- currently only implemented in NonAdaptingProcess(). It shouldn't be
- needed for AdaptingInputOnlyProcess() (no priming should ever be
- requested for AdaptingInputOnlyProcess()).
- Not sure if additional work should be required to make it work with
- AdaptingOutputOnlyProcess, but it definitely is required for
- AdaptingProcess.
-
- @todo implement PaUtil_SetNoOutput for AdaptingProcess
-
- @todo don't allocate temp buffers for blocking streams unless they are
- needed. At the moment they are needed, but perhaps for host APIs
- where the implementation passes a buffer to the host they could be
- used.
-*/
-
-
-#include <assert.h>
-#include <string.h> /* memset() */
-
-#include "pa_process.h"
-#include "pa_util.h"
-
-
-#define PA_FRAMES_PER_TEMP_BUFFER_WHEN_HOST_BUFFER_SIZE_IS_UNKNOWN_ 1024
-
-#define PA_MIN_( a, b ) ( ((a)<(b)) ? (a) : (b) )
-
-
-/* greatest common divisor - PGCD in French */
-static unsigned long GCD( unsigned long a, unsigned long b )
-{
- return (b==0) ? a : GCD( b, a%b);
-}
-
-/* least common multiple - PPCM in French */
-static unsigned long LCM( unsigned long a, unsigned long b )
-{
- return (a*b) / GCD(a,b);
-}
-
-#define PA_MAX_( a, b ) (((a) > (b)) ? (a) : (b))
-
-static unsigned long CalculateFrameShift( unsigned long M, unsigned long N )
-{
- unsigned long result = 0;
- unsigned long i;
- unsigned long lcm;
-
- assert( M > 0 );
- assert( N > 0 );
-
- lcm = LCM( M, N );
- for( i = M; i < lcm; i += M )
- result = PA_MAX_( result, i % N );
-
- return result;
-}
-
-
-PaError PaUtil_InitializeBufferProcessor( PaUtilBufferProcessor* bp,
- int inputChannelCount, PaSampleFormat userInputSampleFormat,
- PaSampleFormat hostInputSampleFormat,
- int outputChannelCount, PaSampleFormat userOutputSampleFormat,
- PaSampleFormat hostOutputSampleFormat,
- double sampleRate,
- PaStreamFlags streamFlags,
- unsigned long framesPerUserBuffer,
- unsigned long framesPerHostBuffer,
- PaUtilHostBufferSizeMode hostBufferSizeMode,
- PaStreamCallback *streamCallback, void *userData )
-{
- PaError result = paNoError;
- PaError bytesPerSample;
- unsigned long tempInputBufferSize, tempOutputBufferSize;
-
- /* initialize buffer ptrs to zero so they can be freed if necessary in error */
- bp->tempInputBuffer = 0;
- bp->tempInputBufferPtrs = 0;
- bp->tempOutputBuffer = 0;
- bp->tempOutputBufferPtrs = 0;
-
- bp->framesPerUserBuffer = framesPerUserBuffer;
- bp->framesPerHostBuffer = framesPerHostBuffer;
-
- bp->inputChannelCount = inputChannelCount;
- bp->outputChannelCount = outputChannelCount;
-
- bp->hostBufferSizeMode = hostBufferSizeMode;
-
- bp->hostInputChannels[0] = bp->hostInputChannels[1] = 0;
- bp->hostOutputChannels[0] = bp->hostOutputChannels[1] = 0;
-
- if( framesPerUserBuffer == 0 ) /* streamCallback will accept any buffer size */
- {
- bp->useNonAdaptingProcess = 1;
- bp->initialFramesInTempInputBuffer = 0;
- bp->initialFramesInTempOutputBuffer = 0;
-
- if( hostBufferSizeMode == paUtilFixedHostBufferSize
- || hostBufferSizeMode == paUtilBoundedHostBufferSize )
- {
- bp->framesPerTempBuffer = framesPerHostBuffer;
- }
- else /* unknown host buffer size */
- {
- bp->framesPerTempBuffer = PA_FRAMES_PER_TEMP_BUFFER_WHEN_HOST_BUFFER_SIZE_IS_UNKNOWN_;
- }
- }
- else
- {
- bp->framesPerTempBuffer = framesPerUserBuffer;
-
- if( hostBufferSizeMode == paUtilFixedHostBufferSize
- && framesPerHostBuffer % framesPerUserBuffer == 0 )
- {
- bp->useNonAdaptingProcess = 1;
- bp->initialFramesInTempInputBuffer = 0;
- bp->initialFramesInTempOutputBuffer = 0;
- }
- else
- {
- bp->useNonAdaptingProcess = 0;
-
- if( inputChannelCount > 0 && outputChannelCount > 0 )
- {
- /* full duplex */
- if( hostBufferSizeMode == paUtilFixedHostBufferSize )
- {
- unsigned long frameShift =
- CalculateFrameShift( framesPerHostBuffer, framesPerUserBuffer );
-
- if( framesPerUserBuffer > framesPerHostBuffer )
- {
- bp->initialFramesInTempInputBuffer = frameShift;
- bp->initialFramesInTempOutputBuffer = 0;
- }
- else
- {
- bp->initialFramesInTempInputBuffer = 0;
- bp->initialFramesInTempOutputBuffer = frameShift;
- }
- }
- else /* variable host buffer size, add framesPerUserBuffer latency */
- {
- bp->initialFramesInTempInputBuffer = 0;
- bp->initialFramesInTempOutputBuffer = framesPerUserBuffer;
- }
- }
- else
- {
- /* half duplex */
- bp->initialFramesInTempInputBuffer = 0;
- bp->initialFramesInTempOutputBuffer = 0;
- }
- }
- }
-
-
- bp->framesInTempInputBuffer = bp->initialFramesInTempInputBuffer;
- bp->framesInTempOutputBuffer = bp->initialFramesInTempOutputBuffer;
-
-
- if( inputChannelCount > 0 )
- {
- bytesPerSample = Pa_GetSampleSize( hostInputSampleFormat );
- if( bytesPerSample > 0 )
- {
- bp->bytesPerHostInputSample = bytesPerSample;
- }
- else
- {
- result = bytesPerSample;
- goto error;
- }
-
- bytesPerSample = Pa_GetSampleSize( userInputSampleFormat );
- if( bytesPerSample > 0 )
- {
- bp->bytesPerUserInputSample = bytesPerSample;
- }
- else
- {
- result = bytesPerSample;
- goto error;
- }
-
- bp->inputConverter =
- PaUtil_SelectConverter( hostInputSampleFormat, userInputSampleFormat, streamFlags );
-
- bp->inputZeroer = PaUtil_SelectZeroer( hostInputSampleFormat );
-
- bp->userInputIsInterleaved = (userInputSampleFormat & paNonInterleaved)?0:1;
-
-
- tempInputBufferSize =
- bp->framesPerTempBuffer * bp->bytesPerUserInputSample * inputChannelCount;
-
- bp->tempInputBuffer = PaUtil_AllocateMemory( tempInputBufferSize );
- if( bp->tempInputBuffer == 0 )
- {
- result = paInsufficientMemory;
- goto error;
- }
-
- if( bp->framesInTempInputBuffer > 0 )
- memset( bp->tempInputBuffer, 0, tempInputBufferSize );
-
- if( userInputSampleFormat & paNonInterleaved )
- {
- bp->tempInputBufferPtrs =
- (void **)PaUtil_AllocateMemory( sizeof(void*)*inputChannelCount );
- if( bp->tempInputBufferPtrs == 0 )
- {
- result = paInsufficientMemory;
- goto error;
- }
- }
-
- bp->hostInputChannels[0] = (PaUtilChannelDescriptor*)
- PaUtil_AllocateMemory( sizeof(PaUtilChannelDescriptor) * inputChannelCount * 2);
- if( bp->hostInputChannels[0] == 0 )
- {
- result = paInsufficientMemory;
- goto error;
- }
-
- bp->hostInputChannels[1] = &bp->hostInputChannels[0][inputChannelCount];
- }
-
- if( outputChannelCount > 0 )
- {
- bytesPerSample = Pa_GetSampleSize( hostOutputSampleFormat );
- if( bytesPerSample > 0 )
- {
- bp->bytesPerHostOutputSample = bytesPerSample;
- }
- else
- {
- result = bytesPerSample;
- goto error;
- }
-
- bytesPerSample = Pa_GetSampleSize( userOutputSampleFormat );
- if( bytesPerSample > 0 )
- {
- bp->bytesPerUserOutputSample = bytesPerSample;
- }
- else
- {
- result = bytesPerSample;
- goto error;
- }
-
- bp->outputConverter =
- PaUtil_SelectConverter( userOutputSampleFormat, hostOutputSampleFormat, streamFlags );
-
- bp->outputZeroer = PaUtil_SelectZeroer( hostOutputSampleFormat );
-
- bp->userOutputIsInterleaved = (userOutputSampleFormat & paNonInterleaved)?0:1;
-
- tempOutputBufferSize =
- bp->framesPerTempBuffer * bp->bytesPerUserOutputSample * outputChannelCount;
-
- bp->tempOutputBuffer = PaUtil_AllocateMemory( tempOutputBufferSize );
- if( bp->tempOutputBuffer == 0 )
- {
- result = paInsufficientMemory;
- goto error;
- }
-
- if( bp->framesInTempOutputBuffer > 0 )
- memset( bp->tempOutputBuffer, 0, tempOutputBufferSize );
-
- if( userOutputSampleFormat & paNonInterleaved )
- {
- bp->tempOutputBufferPtrs =
- (void **)PaUtil_AllocateMemory( sizeof(void*)*outputChannelCount );
- if( bp->tempOutputBufferPtrs == 0 )
- {
- result = paInsufficientMemory;
- goto error;
- }
- }
-
- bp->hostOutputChannels[0] = (PaUtilChannelDescriptor*)
- PaUtil_AllocateMemory( sizeof(PaUtilChannelDescriptor)*outputChannelCount * 2 );
- if( bp->hostOutputChannels[0] == 0 )
- {
- result = paInsufficientMemory;
- goto error;
- }
-
- bp->hostOutputChannels[1] = &bp->hostOutputChannels[0][outputChannelCount];
- }
-
- PaUtil_InitializeTriangularDitherState( &bp->ditherGenerator );
-
- bp->samplePeriod = 1. / sampleRate;
-
- bp->streamCallback = streamCallback;
- bp->userData = userData;
-
- return result;
-
-error:
- if( bp->tempInputBuffer )
- PaUtil_FreeMemory( bp->tempInputBuffer );
-
- if( bp->tempInputBufferPtrs )
- PaUtil_FreeMemory( bp->tempInputBufferPtrs );
-
- if( bp->hostInputChannels[0] )
- PaUtil_FreeMemory( bp->hostInputChannels[0] );
-
- if( bp->tempOutputBuffer )
- PaUtil_FreeMemory( bp->tempOutputBuffer );
-
- if( bp->tempOutputBufferPtrs )
- PaUtil_FreeMemory( bp->tempOutputBufferPtrs );
-
- if( bp->hostOutputChannels[0] )
- PaUtil_FreeMemory( bp->hostOutputChannels[0] );
-
- return result;
-}
-
-
-void PaUtil_TerminateBufferProcessor( PaUtilBufferProcessor* bp )
-{
- if( bp->tempInputBuffer )
- PaUtil_FreeMemory( bp->tempInputBuffer );
-
- if( bp->tempInputBufferPtrs )
- PaUtil_FreeMemory( bp->tempInputBufferPtrs );
-
- if( bp->hostInputChannels[0] )
- PaUtil_FreeMemory( bp->hostInputChannels[0] );
-
- if( bp->tempOutputBuffer )
- PaUtil_FreeMemory( bp->tempOutputBuffer );
-
- if( bp->tempOutputBufferPtrs )
- PaUtil_FreeMemory( bp->tempOutputBufferPtrs );
-
- if( bp->hostOutputChannels[0] )
- PaUtil_FreeMemory( bp->hostOutputChannels[0] );
-}
-
-
-void PaUtil_ResetBufferProcessor( PaUtilBufferProcessor* bp )
-{
- unsigned long tempInputBufferSize, tempOutputBufferSize;
-
- bp->framesInTempInputBuffer = bp->initialFramesInTempInputBuffer;
- bp->framesInTempOutputBuffer = bp->initialFramesInTempOutputBuffer;
-
- if( bp->framesInTempInputBuffer > 0 )
- {
- tempInputBufferSize =
- bp->framesPerTempBuffer * bp->bytesPerUserInputSample * bp->inputChannelCount;
- memset( bp->tempInputBuffer, 0, tempInputBufferSize );
- }
-
- if( bp->framesInTempOutputBuffer > 0 )
- {
- tempOutputBufferSize =
- bp->framesPerTempBuffer * bp->bytesPerUserOutputSample * bp->outputChannelCount;
- memset( bp->tempOutputBuffer, 0, tempOutputBufferSize );
- }
-}
-
-
-unsigned long PaUtil_GetBufferProcessorInputLatency( PaUtilBufferProcessor* bp )
-{
- return bp->initialFramesInTempInputBuffer;
-}
-
-
-unsigned long PaUtil_GetBufferProcessorOutputLatency( PaUtilBufferProcessor* bp )
-{
- return bp->initialFramesInTempOutputBuffer;
-}
-
-
-void PaUtil_SetInputFrameCount( PaUtilBufferProcessor* bp,
- unsigned long frameCount )
-{
- if( frameCount == 0 )
- bp->hostInputFrameCount[0] = bp->framesPerHostBuffer;
- else
- bp->hostInputFrameCount[0] = frameCount;
-}
-
-
-void PaUtil_SetNoInput( PaUtilBufferProcessor* bp )
-{
- assert( bp->inputChannelCount > 0 );
-
- bp->hostInputChannels[0][0].data = 0;
-}
-
-
-void PaUtil_SetInputChannel( PaUtilBufferProcessor* bp,
- unsigned int channel, void *data, unsigned int stride )
-{
- assert( channel < bp->inputChannelCount );
-
- bp->hostInputChannels[0][channel].data = data;
- bp->hostInputChannels[0][channel].stride = stride;
-}
-
-
-void PaUtil_SetInterleavedInputChannels( PaUtilBufferProcessor* bp,
- unsigned int firstChannel, void *data, unsigned int channelCount )
-{
- unsigned int i;
- unsigned int channel = firstChannel;
- unsigned char *p = (unsigned char*)data;
-
- if( channelCount == 0 )
- channelCount = bp->inputChannelCount;
-
- assert( firstChannel < bp->inputChannelCount );
- assert( firstChannel + channelCount <= bp->inputChannelCount );
-
- for( i=0; i< channelCount; ++i )
- {
- bp->hostInputChannels[0][channel+i].data = p;
- p += bp->bytesPerHostInputSample;
- bp->hostInputChannels[0][channel+i].stride = channelCount;
- }
-}
-
-
-void PaUtil_SetNonInterleavedInputChannel( PaUtilBufferProcessor* bp,
- unsigned int channel, void *data )
-{
- assert( channel < bp->inputChannelCount );
-
- bp->hostInputChannels[0][channel].data = data;
- bp->hostInputChannels[0][channel].stride = 1;
-}
-
-
-void PaUtil_Set2ndInputFrameCount( PaUtilBufferProcessor* bp,
- unsigned long frameCount )
-{
- bp->hostInputFrameCount[1] = frameCount;
-}
-
-
-void PaUtil_Set2ndInputChannel( PaUtilBufferProcessor* bp,
- unsigned int channel, void *data, unsigned int stride )
-{
- assert( channel < bp->inputChannelCount );
-
- bp->hostInputChannels[1][channel].data = data;
- bp->hostInputChannels[1][channel].stride = stride;
-}
-
-
-void PaUtil_Set2ndInterleavedInputChannels( PaUtilBufferProcessor* bp,
- unsigned int firstChannel, void *data, unsigned int channelCount )
-{
- unsigned int i;
- unsigned int channel = firstChannel;
- unsigned char *p = (unsigned char*)data;
-
- if( channelCount == 0 )
- channelCount = bp->inputChannelCount;
-
- assert( firstChannel < bp->inputChannelCount );
- assert( firstChannel + channelCount <= bp->inputChannelCount );
-
- for( i=0; i< channelCount; ++i )
- {
- bp->hostInputChannels[1][channel+i].data = p;
- p += bp->bytesPerHostInputSample;
- bp->hostInputChannels[1][channel+i].stride = channelCount;
- }
-}
-
-
-void PaUtil_Set2ndNonInterleavedInputChannel( PaUtilBufferProcessor* bp,
- unsigned int channel, void *data )
-{
- assert( channel < bp->inputChannelCount );
-
- bp->hostInputChannels[1][channel].data = data;
- bp->hostInputChannels[1][channel].stride = 1;
-}
-
-
-void PaUtil_SetOutputFrameCount( PaUtilBufferProcessor* bp,
- unsigned long frameCount )
-{
- if( frameCount == 0 )
- bp->hostOutputFrameCount[0] = bp->framesPerHostBuffer;
- else
- bp->hostOutputFrameCount[0] = frameCount;
-}
-
-
-void PaUtil_SetNoOutput( PaUtilBufferProcessor* bp )
-{
- assert( bp->outputChannelCount > 0 );
-
- bp->hostOutputChannels[0][0].data = 0;
-}
-
-
-void PaUtil_SetOutputChannel( PaUtilBufferProcessor* bp,
- unsigned int channel, void *data, unsigned int stride )
-{
- assert( channel < bp->outputChannelCount );
-
- bp->hostOutputChannels[0][channel].data = data;
- bp->hostOutputChannels[0][channel].stride = stride;
-}
-
-
-void PaUtil_SetInterleavedOutputChannels( PaUtilBufferProcessor* bp,
- unsigned int firstChannel, void *data, unsigned int channelCount )
-{
- unsigned int i;
- unsigned int channel = firstChannel;
- unsigned char *p = (unsigned char*)data;
-
- if( channelCount == 0 )
- channelCount = bp->outputChannelCount;
-
- assert( firstChannel < bp->outputChannelCount );
- assert( firstChannel + channelCount <= bp->outputChannelCount );
-
- for( i=0; i< channelCount; ++i )
- {
- bp->hostOutputChannels[0][channel+i].data = p;
- p += bp->bytesPerHostOutputSample;
- bp->hostOutputChannels[0][channel+i].stride = channelCount;
- }
-}
-
-
-void PaUtil_SetNonInterleavedOutputChannel( PaUtilBufferProcessor* bp,
- unsigned int channel, void *data )
-{
- assert( channel < bp->outputChannelCount );
-
- bp->hostOutputChannels[0][channel].data = data;
- bp->hostOutputChannels[0][channel].stride = 1;
-}
-
-
-void PaUtil_Set2ndOutputFrameCount( PaUtilBufferProcessor* bp,
- unsigned long frameCount )
-{
- bp->hostOutputFrameCount[1] = frameCount;
-}
-
-
-void PaUtil_Set2ndOutputChannel( PaUtilBufferProcessor* bp,
- unsigned int channel, void *data, unsigned int stride )
-{
- assert( channel < bp->outputChannelCount );
-
- bp->hostOutputChannels[1][channel].data = data;
- bp->hostOutputChannels[1][channel].stride = stride;
-}
-
-
-void PaUtil_Set2ndInterleavedOutputChannels( PaUtilBufferProcessor* bp,
- unsigned int firstChannel, void *data, unsigned int channelCount )
-{
- unsigned int i;
- unsigned int channel = firstChannel;
- unsigned char *p = (unsigned char*)data;
-
- if( channelCount == 0 )
- channelCount = bp->outputChannelCount;
-
- assert( firstChannel < bp->outputChannelCount );
- assert( firstChannel + channelCount <= bp->outputChannelCount );
-
- for( i=0; i< channelCount; ++i )
- {
- bp->hostOutputChannels[1][channel+i].data = p;
- p += bp->bytesPerHostOutputSample;
- bp->hostOutputChannels[1][channel+i].stride = channelCount;
- }
-}
-
-
-void PaUtil_Set2ndNonInterleavedOutputChannel( PaUtilBufferProcessor* bp,
- unsigned int channel, void *data )
-{
- assert( channel < bp->outputChannelCount );
-
- bp->hostOutputChannels[1][channel].data = data;
- bp->hostOutputChannels[1][channel].stride = 1;
-}
-
-
-void PaUtil_BeginBufferProcessing( PaUtilBufferProcessor* bp,
- PaStreamCallbackTimeInfo* timeInfo, PaStreamCallbackFlags callbackStatusFlags )
-{
- bp->timeInfo = timeInfo;
-
- /* the first streamCallback will be called to process samples which are
- currently in the input buffer before the ones starting at the timeInfo time */
-
- bp->timeInfo->inputBufferAdcTime -= bp->framesInTempInputBuffer * bp->samplePeriod;
-
- bp->timeInfo->currentTime = 0; /** FIXME: @todo time info currentTime not implemented */
-
- /* the first streamCallback will be called to generate samples which will be
- outputted after the frames currently in the output buffer have been
- outputted. */
- bp->timeInfo->outputBufferDacTime += bp->framesInTempOutputBuffer * bp->samplePeriod;
-
- bp->callbackStatusFlags = callbackStatusFlags;
-
- bp->hostInputFrameCount[1] = 0;
- bp->hostOutputFrameCount[1] = 0;
-}
-
-
-/*
- NonAdaptingProcess() is a simple buffer copying adaptor that can handle
- both full and half duplex copies. It processes framesToProcess frames,
- broken into blocks bp->framesPerTempBuffer long.
- This routine can be used when the streamCallback doesn't care what length
- the buffers are, or when framesToProcess is an integer multiple of
- bp->framesPerTempBuffer, in which case streamCallback will always be called
- with bp->framesPerTempBuffer samples.
-*/
-static unsigned long NonAdaptingProcess( PaUtilBufferProcessor *bp,
- int *streamCallbackResult,
- PaUtilChannelDescriptor *hostInputChannels,
- PaUtilChannelDescriptor *hostOutputChannels,
- unsigned long framesToProcess )
-{
- void *userInput, *userOutput;
- unsigned char *srcBytePtr, *destBytePtr;
- unsigned int srcSampleStrideSamples; /* stride from one sample to the next within a channel, in samples */
- unsigned int srcChannelStrideBytes; /* stride from one channel to the next, in bytes */
- unsigned int destSampleStrideSamples; /* stride from one sample to the next within a channel, in samples */
- unsigned int destChannelStrideBytes; /* stride from one channel to the next, in bytes */
- unsigned int i;
- unsigned long frameCount;
- unsigned long framesToGo = framesToProcess;
- unsigned long framesProcessed = 0;
-
-
- if( *streamCallbackResult == paContinue )
- {
- do
- {
- frameCount = PA_MIN_( bp->framesPerTempBuffer, framesToGo );
-
- /* configure user input buffer and convert input data (host -> user) */
- if( bp->inputChannelCount == 0 )
- {
- /* no input */
- userInput = 0;
- }
- else /* there are input channels */
- {
- /*
- could use more elaborate logic here and sometimes process
- buffers in-place.
- */
-
- destBytePtr = (unsigned char *)bp->tempInputBuffer;
-
- if( bp->userInputIsInterleaved )
- {
- destSampleStrideSamples = bp->inputChannelCount;
- destChannelStrideBytes = bp->bytesPerUserInputSample;
- userInput = bp->tempInputBuffer;
- }
- else /* user input is not interleaved */
- {
- destSampleStrideSamples = 1;
- destChannelStrideBytes = frameCount * bp->bytesPerUserInputSample;
-
- /* setup non-interleaved ptrs */
- for( i=0; i<bp->inputChannelCount; ++i )
- {
- bp->tempInputBufferPtrs[i] = ((unsigned char*)bp->tempInputBuffer) +
- i * bp->bytesPerUserInputSample * frameCount;
- }
-
- userInput = bp->tempInputBufferPtrs;
- }
-
- if( !bp->hostInputChannels[0][0].data )
- {
- /* no input was supplied (see PaUtil_SetNoInput), so
- zero the input buffer */
-
- for( i=0; i<bp->inputChannelCount; ++i )
- {
- bp->inputZeroer( destBytePtr, destSampleStrideSamples, frameCount );
- destBytePtr += destChannelStrideBytes; /* skip to next destination channel */
- }
- }
- else
- {
- for( i=0; i<bp->inputChannelCount; ++i )
- {
- bp->inputConverter( destBytePtr, destSampleStrideSamples,
- hostInputChannels[i].data,
- hostInputChannels[i].stride,
- frameCount, &bp->ditherGenerator );
-
- destBytePtr += destChannelStrideBytes; /* skip to next destination channel */
-
- /* advance src ptr for next iteration */
- hostInputChannels[i].data = ((unsigned char*)hostInputChannels[i].data) +
- frameCount * hostInputChannels[i].stride * bp->bytesPerHostInputSample;
- }
- }
- }
-
- /* configure user output buffer */
- if( bp->outputChannelCount == 0 )
- {
- /* no output */
- userOutput = 0;
- }
- else /* there are output channels */
- {
- if( bp->userOutputIsInterleaved )
- {
- userOutput = bp->tempOutputBuffer;
- }
- else /* user output is not interleaved */
- {
- for( i = 0; i < bp->outputChannelCount; ++i )
- {
- bp->tempOutputBufferPtrs[i] = ((unsigned char*)bp->tempOutputBuffer) +
- i * bp->bytesPerUserOutputSample * frameCount;
- }
-
- userOutput = bp->tempOutputBufferPtrs;
- }
- }
-
- *streamCallbackResult = bp->streamCallback( userInput, userOutput,
- frameCount, bp->timeInfo, bp->callbackStatusFlags, bp->userData );
-
- if( *streamCallbackResult == paAbort )
- {
- /* callback returned paAbort, don't advance framesProcessed
- and framesToGo, they will be handled below */
- }
- else
- {
- bp->timeInfo->inputBufferAdcTime += frameCount * bp->samplePeriod;
- bp->timeInfo->outputBufferDacTime += frameCount * bp->samplePeriod;
-
- /* convert output data (user -> host) */
-
- if( bp->outputChannelCount != 0 && bp->hostOutputChannels[0][0].data )
- {
- /*
- could use more elaborate logic here and sometimes process
- buffers in-place.
- */
-
- srcBytePtr = (unsigned char *)bp->tempOutputBuffer;
-
- if( bp->userOutputIsInterleaved )
- {
- srcSampleStrideSamples = bp->outputChannelCount;
- srcChannelStrideBytes = bp->bytesPerUserOutputSample;
- }
- else /* user output is not interleaved */
- {
- srcSampleStrideSamples = 1;
- srcChannelStrideBytes = frameCount * bp->bytesPerUserOutputSample;
- }
-
- for( i=0; i<bp->outputChannelCount; ++i )
- {
- bp->outputConverter( hostOutputChannels[i].data,
- hostOutputChannels[i].stride,
- srcBytePtr, srcSampleStrideSamples,
- frameCount, &bp->ditherGenerator );
-
- srcBytePtr += srcChannelStrideBytes; /* skip to next source channel */
-
- /* advance dest ptr for next iteration */
- hostOutputChannels[i].data = ((unsigned char*)hostOutputChannels[i].data) +
- frameCount * hostOutputChannels[i].stride * bp->bytesPerHostOutputSample;
- }
- }
-
- framesProcessed += frameCount;
-
- framesToGo -= frameCount;
- }
- }
- while( framesToGo > 0 && *streamCallbackResult == paContinue );
- }
-
- if( framesToGo > 0 )
- {
- /* zero any remaining frames output. There will only be remaining frames
- if the callback has returned paComplete or paAbort */
-
- frameCount = framesToGo;
-
- if( bp->outputChannelCount != 0 && bp->hostOutputChannels[0][0].data )
- {
- for( i=0; i<bp->outputChannelCount; ++i )
- {
- bp->outputZeroer( hostOutputChannels[i].data,
- hostOutputChannels[i].stride,
- frameCount );
-
- /* advance dest ptr for next iteration */
- hostOutputChannels[i].data = ((unsigned char*)hostOutputChannels[i].data) +
- frameCount * hostOutputChannels[i].stride * bp->bytesPerHostOutputSample;
- }
- }
-
- framesProcessed += frameCount;
- }
-
- return framesProcessed;
-}
-
-
-/*
- AdaptingInputOnlyProcess() is a half duplex input buffer processor. It
- converts data from the input buffers into the temporary input buffer,
- when the temporary input buffer is full, it calls the streamCallback.
-*/
-static unsigned long AdaptingInputOnlyProcess( PaUtilBufferProcessor *bp,
- int *streamCallbackResult,
- PaUtilChannelDescriptor *hostInputChannels,
- unsigned long framesToProcess )
-{
- void *userInput, *userOutput;
- unsigned char *destBytePtr;
- unsigned int destSampleStrideSamples; /* stride from one sample to the next within a channel, in samples */
- unsigned int destChannelStrideBytes; /* stride from one channel to the next, in bytes */
- unsigned int i;
- unsigned long frameCount;
- unsigned long framesToGo = framesToProcess;
- unsigned long framesProcessed = 0;
-
- userOutput = 0;
-
- do
- {
- frameCount = ( bp->framesInTempInputBuffer + framesToGo > bp->framesPerUserBuffer )
- ? ( bp->framesPerUserBuffer - bp->framesInTempInputBuffer )
- : framesToGo;
-
- /* convert frameCount samples into temp buffer */
-
- if( bp->userInputIsInterleaved )
- {
- destBytePtr = ((unsigned char*)bp->tempInputBuffer) +
- bp->bytesPerUserInputSample * bp->inputChannelCount *
- bp->framesInTempInputBuffer;
-
- destSampleStrideSamples = bp->inputChannelCount;
- destChannelStrideBytes = bp->bytesPerUserInputSample;
-
- userInput = bp->tempInputBuffer;
- }
- else /* user input is not interleaved */
- {
- destBytePtr = ((unsigned char*)bp->tempInputBuffer) +
- bp->bytesPerUserInputSample * bp->framesInTempInputBuffer;
-
- destSampleStrideSamples = 1;
- destChannelStrideBytes = bp->framesPerUserBuffer * bp->bytesPerUserInputSample;
-
- /* setup non-interleaved ptrs */
- for( i=0; i<bp->inputChannelCount; ++i )
- {
- bp->tempInputBufferPtrs[i] = ((unsigned char*)bp->tempInputBuffer) +
- i * bp->bytesPerUserInputSample * bp->framesPerUserBuffer;
- }
-
- userInput = bp->tempInputBufferPtrs;
- }
-
- for( i=0; i<bp->inputChannelCount; ++i )
- {
- bp->inputConverter( destBytePtr, destSampleStrideSamples,
- hostInputChannels[i].data,
- hostInputChannels[i].stride,
- frameCount, &bp->ditherGenerator );
-
- destBytePtr += destChannelStrideBytes; /* skip to next destination channel */
-
- /* advance src ptr for next iteration */
- hostInputChannels[i].data = ((unsigned char*)hostInputChannels[i].data) +
- frameCount * hostInputChannels[i].stride * bp->bytesPerHostInputSample;
- }
-
- bp->framesInTempInputBuffer += frameCount;
-
- if( bp->framesInTempInputBuffer == bp->framesPerUserBuffer )
- {
- /**
- @todo (non-critical optimisation)
- The conditional below implements the continue/complete/abort mechanism
- simply by continuing on iterating through the input buffer, but not
- passing the data to the callback. With care, the outer loop could be
- terminated earlier, thus some unneeded conversion cycles would be
- saved.
- */
- if( *streamCallbackResult == paContinue )
- {
- bp->timeInfo->outputBufferDacTime = 0;
-
- *streamCallbackResult = bp->streamCallback( userInput, userOutput,
- bp->framesPerUserBuffer, bp->timeInfo,
- bp->callbackStatusFlags, bp->userData );
-
- bp->timeInfo->inputBufferAdcTime += frameCount * bp->samplePeriod;
- }
-
- bp->framesInTempInputBuffer = 0;
- }
-
- framesProcessed += frameCount;
-
- framesToGo -= frameCount;
- }while( framesToGo > 0 );
-
- return framesProcessed;
-}
-
-
-/*
- AdaptingOutputOnlyProcess() is a half duplex output buffer processor.
- It converts data from the temporary output buffer, to the output buffers,
- when the temporary output buffer is empty, it calls the streamCallback.
-*/
-static unsigned long AdaptingOutputOnlyProcess( PaUtilBufferProcessor *bp,
- int *streamCallbackResult,
- PaUtilChannelDescriptor *hostOutputChannels,
- unsigned long framesToProcess )
-{
- void *userInput, *userOutput;
- unsigned char *srcBytePtr;
- unsigned int srcSampleStrideSamples; /* stride from one sample to the next within a channel, in samples */
- unsigned int srcChannelStrideBytes; /* stride from one channel to the next, in bytes */
- unsigned int i;
- unsigned long frameCount;
- unsigned long framesToGo = framesToProcess;
- unsigned long framesProcessed = 0;
-
- do
- {
- if( bp->framesInTempOutputBuffer == 0 && *streamCallbackResult == paContinue )
- {
- userInput = 0;
-
- /* setup userOutput */
- if( bp->userOutputIsInterleaved )
- {
- userOutput = bp->tempOutputBuffer;
- }
- else /* user output is not interleaved */
- {
- for( i = 0; i < bp->outputChannelCount; ++i )
- {
- bp->tempOutputBufferPtrs[i] = ((unsigned char*)bp->tempOutputBuffer) +
- i * bp->framesPerUserBuffer * bp->bytesPerUserOutputSample;
- }
-
- userOutput = bp->tempOutputBufferPtrs;
- }
-
- bp->timeInfo->inputBufferAdcTime = 0;
-
- *streamCallbackResult = bp->streamCallback( userInput, userOutput,
- bp->framesPerUserBuffer, bp->timeInfo,
- bp->callbackStatusFlags, bp->userData );
-
- if( *streamCallbackResult == paAbort )
- {
- /* if the callback returned paAbort, we disregard its output */
- }
- else
- {
- bp->timeInfo->outputBufferDacTime += bp->framesPerUserBuffer * bp->samplePeriod;
-
- bp->framesInTempOutputBuffer = bp->framesPerUserBuffer;
- }
- }
-
- if( bp->framesInTempOutputBuffer > 0 )
- {
- /* convert frameCount frames from user buffer to host buffer */
-
- frameCount = PA_MIN_( bp->framesInTempOutputBuffer, framesToGo );
-
- if( bp->userOutputIsInterleaved )
- {
- srcBytePtr = ((unsigned char*)bp->tempOutputBuffer) +
- bp->bytesPerUserOutputSample * bp->outputChannelCount *
- (bp->framesPerUserBuffer - bp->framesInTempOutputBuffer);
-
- srcSampleStrideSamples = bp->outputChannelCount;
- srcChannelStrideBytes = bp->bytesPerUserOutputSample;
- }
- else /* user output is not interleaved */
- {
- srcBytePtr = ((unsigned char*)bp->tempOutputBuffer) +
- bp->bytesPerUserOutputSample *
- (bp->framesPerUserBuffer - bp->framesInTempOutputBuffer);
-
- srcSampleStrideSamples = 1;
- srcChannelStrideBytes = bp->framesPerUserBuffer * bp->bytesPerUserOutputSample;
- }
-
- for( i=0; i<bp->outputChannelCount; ++i )
- {
- bp->outputConverter( hostOutputChannels[i].data,
- hostOutputChannels[i].stride,
- srcBytePtr, srcSampleStrideSamples,
- frameCount, &bp->ditherGenerator );
-
- srcBytePtr += srcChannelStrideBytes; /* skip to next source channel */
-
- /* advance dest ptr for next iteration */
- hostOutputChannels[i].data = ((unsigned char*)hostOutputChannels[i].data) +
- frameCount * hostOutputChannels[i].stride * bp->bytesPerHostOutputSample;
- }
-
- bp->framesInTempOutputBuffer -= frameCount;
- }
- else
- {
- /* no more user data is available because the callback has returned
- paComplete or paAbort. Fill the remainder of the host buffer
- with zeros.
- */
-
- frameCount = framesToGo;
-
- for( i=0; i<bp->outputChannelCount; ++i )
- {
- bp->outputZeroer( hostOutputChannels[i].data,
- hostOutputChannels[i].stride,
- frameCount );
-
- /* advance dest ptr for next iteration */
- hostOutputChannels[i].data = ((unsigned char*)hostOutputChannels[i].data) +
- frameCount * hostOutputChannels[i].stride * bp->bytesPerHostOutputSample;
- }
- }
-
- framesProcessed += frameCount;
-
- framesToGo -= frameCount;
-
- }while( framesToGo > 0 );
-
- return framesProcessed;
-}
-
-/* CopyTempOutputBuffersToHostOutputBuffers is called from AdaptingProcess to copy frames from
- tempOutputBuffer to hostOutputChannels. This includes data conversion
- and interleaving.
-*/
-static void CopyTempOutputBuffersToHostOutputBuffers( PaUtilBufferProcessor *bp)
-{
- unsigned long maxFramesToCopy;
- PaUtilChannelDescriptor *hostOutputChannels;
- unsigned int frameCount;
- unsigned char *srcBytePtr;
- unsigned int srcSampleStrideSamples; /* stride from one sample to the next within a channel, in samples */
- unsigned int srcChannelStrideBytes; /* stride from one channel to the next, in bytes */
- unsigned int i;
-
- /* copy frames from user to host output buffers */
- while( bp->framesInTempOutputBuffer > 0 &&
- ((bp->hostOutputFrameCount[0] + bp->hostOutputFrameCount[1]) > 0) )
- {
- maxFramesToCopy = bp->framesInTempOutputBuffer;
-
- /* select the output buffer set (1st or 2nd) */
- if( bp->hostOutputFrameCount[0] > 0 )
- {
- hostOutputChannels = bp->hostOutputChannels[0];
- frameCount = PA_MIN_( bp->hostOutputFrameCount[0], maxFramesToCopy );
- }
- else
- {
- hostOutputChannels = bp->hostOutputChannels[1];
- frameCount = PA_MIN_( bp->hostOutputFrameCount[1], maxFramesToCopy );
- }
-
- if( bp->userOutputIsInterleaved )
- {
- srcBytePtr = ((unsigned char*)bp->tempOutputBuffer) +
- bp->bytesPerUserOutputSample * bp->outputChannelCount *
- (bp->framesPerUserBuffer - bp->framesInTempOutputBuffer);
-
- srcSampleStrideSamples = bp->outputChannelCount;
- srcChannelStrideBytes = bp->bytesPerUserOutputSample;
- }
- else /* user output is not interleaved */
- {
- srcBytePtr = ((unsigned char*)bp->tempOutputBuffer) +
- bp->bytesPerUserOutputSample *
- (bp->framesPerUserBuffer - bp->framesInTempOutputBuffer);
-
- srcSampleStrideSamples = 1;
- srcChannelStrideBytes = bp->framesPerUserBuffer * bp->bytesPerUserOutputSample;
- }
-
- for( i=0; i<bp->outputChannelCount; ++i )
- {
- bp->outputConverter( hostOutputChannels[i].data,
- hostOutputChannels[i].stride,
- srcBytePtr, srcSampleStrideSamples,
- frameCount, &bp->ditherGenerator );
-
- srcBytePtr += srcChannelStrideBytes; /* skip to next source channel */
-
- /* advance dest ptr for next iteration */
- hostOutputChannels[i].data = ((unsigned char*)hostOutputChannels[i].data) +
- frameCount * hostOutputChannels[i].stride * bp->bytesPerHostOutputSample;
- }
-
- if( bp->hostOutputFrameCount[0] > 0 )
- bp->hostOutputFrameCount[0] -= frameCount;
- else
- bp->hostOutputFrameCount[1] -= frameCount;
-
- bp->framesInTempOutputBuffer -= frameCount;
- }
-}
-
-/*
- AdaptingProcess is a full duplex adapting buffer processor. It converts
- data from the temporary output buffer into the host output buffers, then
- from the host input buffers into the temporary input buffers. Calling the
- streamCallback when necessary.
- When processPartialUserBuffers is 0, all available input data will be
- consumed and all available output space will be filled. When
- processPartialUserBuffers is non-zero, as many full user buffers
- as possible will be processed, but partial buffers will not be consumed.
-*/
-static unsigned long AdaptingProcess( PaUtilBufferProcessor *bp,
- int *streamCallbackResult, int processPartialUserBuffers )
-{
- void *userInput, *userOutput;
- unsigned long framesProcessed = 0;
- unsigned long framesAvailable;
- unsigned long endProcessingMinFrameCount;
- unsigned long maxFramesToCopy;
- PaUtilChannelDescriptor *hostInputChannels, *hostOutputChannels;
- unsigned int frameCount;
- unsigned char *destBytePtr;
- unsigned int destSampleStrideSamples; /* stride from one sample to the next within a channel, in samples */
- unsigned int destChannelStrideBytes; /* stride from one channel to the next, in bytes */
- unsigned int i, j;
-
-
- framesAvailable = bp->hostInputFrameCount[0] + bp->hostInputFrameCount[1];/* this is assumed to be the same as the output buffer's frame count */
-
- if( processPartialUserBuffers )
- endProcessingMinFrameCount = 0;
- else
- endProcessingMinFrameCount = (bp->framesPerUserBuffer - 1);
-
- /* Fill host output with remaining frames in user output (tempOutputBuffer) */
- CopyTempOutputBuffersToHostOutputBuffers( bp );
-
- while( framesAvailable > endProcessingMinFrameCount )
- {
-
- if( bp->framesInTempOutputBuffer == 0 && *streamCallbackResult != paContinue )
- {
- /* the callback will not be called any more, so zero what remains
- of the host output buffers */
-
- for( i=0; i<2; ++i )
- {
- frameCount = bp->hostOutputFrameCount[i];
- if( frameCount > 0 )
- {
- hostOutputChannels = bp->hostOutputChannels[i];
-
- for( j=0; j<bp->outputChannelCount; ++j )
- {
- bp->outputZeroer( hostOutputChannels[j].data,
- hostOutputChannels[j].stride,
- frameCount );
-
- /* advance dest ptr for next iteration */
- hostOutputChannels[j].data = ((unsigned char*)hostOutputChannels[j].data) +
- frameCount * hostOutputChannels[j].stride * bp->bytesPerHostOutputSample;
- }
- bp->hostOutputFrameCount[i] = 0;
- }
- }
- }
-
-
- /* copy frames from host to user input buffers */
- while( bp->framesInTempInputBuffer < bp->framesPerUserBuffer &&
- ((bp->hostInputFrameCount[0] + bp->hostInputFrameCount[1]) > 0) )
- {
- maxFramesToCopy = bp->framesPerUserBuffer - bp->framesInTempInputBuffer;
-
- /* select the input buffer set (1st or 2nd) */
- if( bp->hostInputFrameCount[0] > 0 )
- {
- hostInputChannels = bp->hostInputChannels[0];
- frameCount = PA_MIN_( bp->hostInputFrameCount[0], maxFramesToCopy );
- }
- else
- {
- hostInputChannels = bp->hostInputChannels[1];
- frameCount = PA_MIN_( bp->hostInputFrameCount[1], maxFramesToCopy );
- }
-
- /* configure conversion destination pointers */
- if( bp->userInputIsInterleaved )
- {
- destBytePtr = ((unsigned char*)bp->tempInputBuffer) +
- bp->bytesPerUserInputSample * bp->inputChannelCount *
- bp->framesInTempInputBuffer;
-
- destSampleStrideSamples = bp->inputChannelCount;
- destChannelStrideBytes = bp->bytesPerUserInputSample;
- }
- else /* user input is not interleaved */
- {
- destBytePtr = ((unsigned char*)bp->tempInputBuffer) +
- bp->bytesPerUserInputSample * bp->framesInTempInputBuffer;
-
- destSampleStrideSamples = 1;
- destChannelStrideBytes = bp->framesPerUserBuffer * bp->bytesPerUserInputSample;
- }
-
- for( i=0; i<bp->inputChannelCount; ++i )
- {
- bp->inputConverter( destBytePtr, destSampleStrideSamples,
- hostInputChannels[i].data,
- hostInputChannels[i].stride,
- frameCount, &bp->ditherGenerator );
-
- destBytePtr += destChannelStrideBytes; /* skip to next destination channel */
-
- /* advance src ptr for next iteration */
- hostInputChannels[i].data = ((unsigned char*)hostInputChannels[i].data) +
- frameCount * hostInputChannels[i].stride * bp->bytesPerHostInputSample;
- }
-
- if( bp->hostInputFrameCount[0] > 0 )
- bp->hostInputFrameCount[0] -= frameCount;
- else
- bp->hostInputFrameCount[1] -= frameCount;
-
- bp->framesInTempInputBuffer += frameCount;
-
- /* update framesAvailable and framesProcessed based on input consumed
- unless something is very wrong this will also correspond to the
- amount of output generated */
- framesAvailable -= frameCount;
- framesProcessed += frameCount;
- }
-
- /* call streamCallback */
- if( bp->framesInTempInputBuffer == bp->framesPerUserBuffer &&
- bp->framesInTempOutputBuffer == 0 )
- {
- if( *streamCallbackResult == paContinue )
- {
- /* setup userInput */
- if( bp->userInputIsInterleaved )
- {
- userInput = bp->tempInputBuffer;
- }
- else /* user input is not interleaved */
- {
- for( i = 0; i < bp->inputChannelCount; ++i )
- {
- bp->tempInputBufferPtrs[i] = ((unsigned char*)bp->tempInputBuffer) +
- i * bp->framesPerUserBuffer * bp->bytesPerUserInputSample;
- }
-
- userInput = bp->tempInputBufferPtrs;
- }
-
- /* setup userOutput */
- if( bp->userOutputIsInterleaved )
- {
- userOutput = bp->tempOutputBuffer;
- }
- else /* user output is not interleaved */
- {
- for( i = 0; i < bp->outputChannelCount; ++i )
- {
- bp->tempOutputBufferPtrs[i] = ((unsigned char*)bp->tempOutputBuffer) +
- i * bp->framesPerUserBuffer * bp->bytesPerUserOutputSample;
- }
-
- userOutput = bp->tempOutputBufferPtrs;
- }
-
- /* call streamCallback */
-
- *streamCallbackResult = bp->streamCallback( userInput, userOutput,
- bp->framesPerUserBuffer, bp->timeInfo,
- bp->callbackStatusFlags, bp->userData );
-
- bp->timeInfo->inputBufferAdcTime += bp->framesPerUserBuffer * bp->samplePeriod;
- bp->timeInfo->outputBufferDacTime += bp->framesPerUserBuffer * bp->samplePeriod;
-
- bp->framesInTempInputBuffer = 0;
-
- if( *streamCallbackResult == paAbort )
- bp->framesInTempOutputBuffer = 0;
- else
- bp->framesInTempOutputBuffer = bp->framesPerUserBuffer;
- }
- else
- {
- /* paComplete or paAbort has already been called. */
-
- bp->framesInTempInputBuffer = 0;
- }
- }
-
- /* copy frames from user (tempOutputBuffer) to host output buffers (hostOutputChannels)
- Means to process the user output provided by the callback. Has to be called after
- each callback. */
- CopyTempOutputBuffersToHostOutputBuffers( bp );
-
- }
-
- return framesProcessed;
-}
-
-
-unsigned long PaUtil_EndBufferProcessing( PaUtilBufferProcessor* bp, int *streamCallbackResult )
-{
- unsigned long framesToProcess, framesToGo;
- unsigned long framesProcessed = 0;
-
- if( bp->inputChannelCount != 0 && bp->outputChannelCount != 0
- && bp->hostInputChannels[0][0].data /* input was supplied (see PaUtil_SetNoInput) */
- && bp->hostOutputChannels[0][0].data /* output was supplied (see PaUtil_SetNoOutput) */ )
- {
- assert( (bp->hostInputFrameCount[0] + bp->hostInputFrameCount[1]) ==
- (bp->hostOutputFrameCount[0] + bp->hostOutputFrameCount[1]) );
- }
-
- assert( *streamCallbackResult == paContinue
- || *streamCallbackResult == paComplete
- || *streamCallbackResult == paAbort ); /* don't forget to pass in a valid callback result value */
-
- if( bp->useNonAdaptingProcess )
- {
- if( bp->inputChannelCount != 0 && bp->outputChannelCount != 0 )
- {
- /* full duplex non-adapting process, splice buffers if they are
- different lengths */
-
- framesToGo = bp->hostOutputFrameCount[0] + bp->hostOutputFrameCount[1]; /* relies on assert above for input/output equivalence */
-
- do{
- unsigned long noInputInputFrameCount;
- unsigned long *hostInputFrameCount;
- PaUtilChannelDescriptor *hostInputChannels;
- unsigned long noOutputOutputFrameCount;
- unsigned long *hostOutputFrameCount;
- PaUtilChannelDescriptor *hostOutputChannels;
- unsigned long framesProcessedThisIteration;
-
- if( !bp->hostInputChannels[0][0].data )
- {
- /* no input was supplied (see PaUtil_SetNoInput)
- NonAdaptingProcess knows how to deal with this
- */
- noInputInputFrameCount = framesToGo;
- hostInputFrameCount = &noInputInputFrameCount;
- hostInputChannels = 0;
- }
- else if( bp->hostInputFrameCount[0] != 0 )
- {
- hostInputFrameCount = &bp->hostInputFrameCount[0];
- hostInputChannels = bp->hostInputChannels[0];
- }
- else
- {
- hostInputFrameCount = &bp->hostInputFrameCount[1];
- hostInputChannels = bp->hostInputChannels[1];
- }
-
- if( !bp->hostOutputChannels[0][0].data )
- {
- /* no output was supplied (see PaUtil_SetNoOutput)
- NonAdaptingProcess knows how to deal with this
- */
- noOutputOutputFrameCount = framesToGo;
- hostOutputFrameCount = &noOutputOutputFrameCount;
- hostOutputChannels = 0;
- }
- if( bp->hostOutputFrameCount[0] != 0 )
- {
- hostOutputFrameCount = &bp->hostOutputFrameCount[0];
- hostOutputChannels = bp->hostOutputChannels[0];
- }
- else
- {
- hostOutputFrameCount = &bp->hostOutputFrameCount[1];
- hostOutputChannels = bp->hostOutputChannels[1];
- }
-
- framesToProcess = PA_MIN_( *hostInputFrameCount,
- *hostOutputFrameCount );
-
- assert( framesToProcess != 0 );
-
- framesProcessedThisIteration = NonAdaptingProcess( bp, streamCallbackResult,
- hostInputChannels, hostOutputChannels,
- framesToProcess );
-
- *hostInputFrameCount -= framesProcessedThisIteration;
- *hostOutputFrameCount -= framesProcessedThisIteration;
-
- framesProcessed += framesProcessedThisIteration;
- framesToGo -= framesProcessedThisIteration;
-
- }while( framesToGo > 0 );
- }
- else
- {
- /* half duplex non-adapting process, just process 1st and 2nd buffer */
- /* process first buffer */
-
- framesToProcess = (bp->inputChannelCount != 0)
- ? bp->hostInputFrameCount[0]
- : bp->hostOutputFrameCount[0];
-
- framesProcessed = NonAdaptingProcess( bp, streamCallbackResult,
- bp->hostInputChannels[0], bp->hostOutputChannels[0],
- framesToProcess );
-
- /* process second buffer if provided */
-
- framesToProcess = (bp->inputChannelCount != 0)
- ? bp->hostInputFrameCount[1]
- : bp->hostOutputFrameCount[1];
- if( framesToProcess > 0 )
- {
- framesProcessed += NonAdaptingProcess( bp, streamCallbackResult,
- bp->hostInputChannels[1], bp->hostOutputChannels[1],
- framesToProcess );
- }
- }
- }
- else /* block adaption necessary*/
- {
-
- if( bp->inputChannelCount != 0 && bp->outputChannelCount != 0 )
- {
- /* full duplex */
-
- if( bp->hostBufferSizeMode == paUtilVariableHostBufferSizePartialUsageAllowed )
- {
- framesProcessed = AdaptingProcess( bp, streamCallbackResult,
- 0 /* dont process partial user buffers */ );
- }
- else
- {
- framesProcessed = AdaptingProcess( bp, streamCallbackResult,
- 1 /* process partial user buffers */ );
- }
- }
- else if( bp->inputChannelCount != 0 )
- {
- /* input only */
- framesToProcess = bp->hostInputFrameCount[0];
-
- framesProcessed = AdaptingInputOnlyProcess( bp, streamCallbackResult,
- bp->hostInputChannels[0], framesToProcess );
-
- framesToProcess = bp->hostInputFrameCount[1];
- if( framesToProcess > 0 )
- {
- framesProcessed += AdaptingInputOnlyProcess( bp, streamCallbackResult,
- bp->hostInputChannels[1], framesToProcess );
- }
- }
- else
- {
- /* output only */
- framesToProcess = bp->hostOutputFrameCount[0];
-
- framesProcessed = AdaptingOutputOnlyProcess( bp, streamCallbackResult,
- bp->hostOutputChannels[0], framesToProcess );
-
- framesToProcess = bp->hostOutputFrameCount[1];
- if( framesToProcess > 0 )
- {
- framesProcessed += AdaptingOutputOnlyProcess( bp, streamCallbackResult,
- bp->hostOutputChannels[1], framesToProcess );
- }
- }
- }
-
- return framesProcessed;
-}
-
-
-int PaUtil_IsBufferProcessorOutputEmpty( PaUtilBufferProcessor* bp )
-{
- return (bp->framesInTempOutputBuffer) ? 0 : 1;
-}
-
-
-unsigned long PaUtil_CopyInput( PaUtilBufferProcessor* bp,
- void **buffer, unsigned long frameCount )
-{
- PaUtilChannelDescriptor *hostInputChannels;
- unsigned int framesToCopy;
- unsigned char *destBytePtr;
- void **nonInterleavedDestPtrs;
- unsigned int destSampleStrideSamples; /* stride from one sample to the next within a channel, in samples */
- unsigned int destChannelStrideBytes; /* stride from one channel to the next, in bytes */
- unsigned int i;
-
- hostInputChannels = bp->hostInputChannels[0];
- framesToCopy = PA_MIN_( bp->hostInputFrameCount[0], frameCount );
-
- if( bp->userInputIsInterleaved )
- {
- destBytePtr = (unsigned char*)*buffer;
-
- destSampleStrideSamples = bp->inputChannelCount;
- destChannelStrideBytes = bp->bytesPerUserInputSample;
-
- for( i=0; i<bp->inputChannelCount; ++i )
- {
- bp->inputConverter( destBytePtr, destSampleStrideSamples,
- hostInputChannels[i].data,
- hostInputChannels[i].stride,
- framesToCopy, &bp->ditherGenerator );
-
- destBytePtr += destChannelStrideBytes; /* skip to next source channel */
-
- /* advance dest ptr for next iteration */
- hostInputChannels[i].data = ((unsigned char*)hostInputChannels[i].data) +
- framesToCopy * hostInputChannels[i].stride * bp->bytesPerHostInputSample;
- }
-
- /* advance callers dest pointer (buffer) */
- *buffer = ((unsigned char *)*buffer) +
- framesToCopy * bp->inputChannelCount * bp->bytesPerUserInputSample;
- }
- else
- {
- /* user input is not interleaved */
-
- nonInterleavedDestPtrs = (void**)*buffer;
-
- destSampleStrideSamples = 1;
-
- for( i=0; i<bp->inputChannelCount; ++i )
- {
- destBytePtr = (unsigned char*)nonInterleavedDestPtrs[i];
-
- bp->inputConverter( destBytePtr, destSampleStrideSamples,
- hostInputChannels[i].data,
- hostInputChannels[i].stride,
- framesToCopy, &bp->ditherGenerator );
-
- /* advance callers dest pointer (nonInterleavedDestPtrs[i]) */
- destBytePtr += bp->bytesPerUserInputSample * framesToCopy;
- nonInterleavedDestPtrs[i] = destBytePtr;
-
- /* advance dest ptr for next iteration */
- hostInputChannels[i].data = ((unsigned char*)hostInputChannels[i].data) +
- framesToCopy * hostInputChannels[i].stride * bp->bytesPerHostInputSample;
- }
- }
-
- bp->hostInputFrameCount[0] -= framesToCopy;
-
- return framesToCopy;
-}
-
-unsigned long PaUtil_CopyOutput( PaUtilBufferProcessor* bp,
- const void ** buffer, unsigned long frameCount )
-{
- PaUtilChannelDescriptor *hostOutputChannels;
- unsigned int framesToCopy;
- unsigned char *srcBytePtr;
- void **nonInterleavedSrcPtrs;
- unsigned int srcSampleStrideSamples; /* stride from one sample to the next within a channel, in samples */
- unsigned int srcChannelStrideBytes; /* stride from one channel to the next, in bytes */
- unsigned int i;
-
- hostOutputChannels = bp->hostOutputChannels[0];
- framesToCopy = PA_MIN_( bp->hostOutputFrameCount[0], frameCount );
-
- if( bp->userOutputIsInterleaved )
- {
- srcBytePtr = (unsigned char*)*buffer;
-
- srcSampleStrideSamples = bp->outputChannelCount;
- srcChannelStrideBytes = bp->bytesPerUserOutputSample;
-
- for( i=0; i<bp->outputChannelCount; ++i )
- {
- bp->outputConverter( hostOutputChannels[i].data,
- hostOutputChannels[i].stride,
- srcBytePtr, srcSampleStrideSamples,
- framesToCopy, &bp->ditherGenerator );
-
- srcBytePtr += srcChannelStrideBytes; /* skip to next source channel */
-
- /* advance dest ptr for next iteration */
- hostOutputChannels[i].data = ((unsigned char*)hostOutputChannels[i].data) +
- framesToCopy * hostOutputChannels[i].stride * bp->bytesPerHostOutputSample;
- }
-
- /* advance callers source pointer (buffer) */
- *buffer = ((unsigned char *)*buffer) +
- framesToCopy * bp->outputChannelCount * bp->bytesPerUserOutputSample;
-
- }
- else
- {
- /* user output is not interleaved */
-
- nonInterleavedSrcPtrs = (void**)*buffer;
-
- srcSampleStrideSamples = 1;
-
- for( i=0; i<bp->outputChannelCount; ++i )
- {
- srcBytePtr = (unsigned char*)nonInterleavedSrcPtrs[i];
-
- bp->outputConverter( hostOutputChannels[i].data,
- hostOutputChannels[i].stride,
- srcBytePtr, srcSampleStrideSamples,
- framesToCopy, &bp->ditherGenerator );
-
-
- /* advance callers source pointer (nonInterleavedSrcPtrs[i]) */
- srcBytePtr += bp->bytesPerUserOutputSample * framesToCopy;
- nonInterleavedSrcPtrs[i] = srcBytePtr;
-
- /* advance dest ptr for next iteration */
- hostOutputChannels[i].data = ((unsigned char*)hostOutputChannels[i].data) +
- framesToCopy * hostOutputChannels[i].stride * bp->bytesPerHostOutputSample;
- }
- }
-
- bp->hostOutputFrameCount[0] += framesToCopy;
-
- return framesToCopy;
-}
-
-
-unsigned long PaUtil_ZeroOutput( PaUtilBufferProcessor* bp, unsigned long frameCount )
-{
- PaUtilChannelDescriptor *hostOutputChannels;
- unsigned int framesToZero;
- unsigned int i;
-
- hostOutputChannels = bp->hostOutputChannels[0];
- framesToZero = PA_MIN_( bp->hostOutputFrameCount[0], frameCount );
-
- for( i=0; i<bp->outputChannelCount; ++i )
- {
- bp->outputZeroer( hostOutputChannels[i].data,
- hostOutputChannels[i].stride,
- framesToZero );
-
-
- /* advance dest ptr for next iteration */
- hostOutputChannels[i].data = ((unsigned char*)hostOutputChannels[i].data) +
- framesToZero * hostOutputChannels[i].stride * bp->bytesPerHostOutputSample;
- }
-
- bp->hostOutputFrameCount[0] += framesToZero;
-
- return framesToZero;
-}
+/*
+ * $Id: pa_process.c,v 1.1.2.48 2004/12/13 09:48:43 rossbencina Exp $
+ * Portable Audio I/O Library
+ * streamCallback <-> host buffer processing adapter
+ *
+ * Based on the Open Source API proposed by Ross Bencina
+ * Copyright (c) 1999-2002 Ross Bencina, Phil Burk
+ *
+ * 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.
+ *
+ * Any person wishing to distribute modifications to the Software is
+ * requested to send the modifications to the original developer so that
+ * they can be incorporated into the canonical version.
+ *
+ * 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.
+ */
+
+/** @file
+ @brief Buffer Processor implementation.
+
+ The code in this file is not optimised yet - although it's not clear that
+ it needs to be. there may appear to be redundancies
+ that could be factored into common functions, but the redundanceis are left
+ intentionally as each appearance may have different optimisation possibilities.
+
+ The optimisations which are planned involve only converting data in-place
+ where possible, rather than copying to the temp buffer(s).
+
+ Note that in the extreme case of being able to convert in-place, and there
+ being no conversion necessary there should be some code which short-circuits
+ the operation.
+
+ @todo Consider cache tilings for intereave<->deinterleave.
+
+ @todo implement timeInfo->currentTime int PaUtil_BeginBufferProcessing()
+
+ @todo specify and implement some kind of logical policy for handling the
+ underflow and overflow stream flags when the underflow/overflow overlaps
+ multiple user buffers/callbacks.
+
+ @todo provide support for priming the buffers with data from the callback.
+ The client interface is now implemented through PaUtil_SetNoInput()
+ which sets bp->hostInputChannels[0][0].data to zero. However this is
+ currently only implemented in NonAdaptingProcess(). It shouldn't be
+ needed for AdaptingInputOnlyProcess() (no priming should ever be
+ requested for AdaptingInputOnlyProcess()).
+ Not sure if additional work should be required to make it work with
+ AdaptingOutputOnlyProcess, but it definitely is required for
+ AdaptingProcess.
+
+ @todo implement PaUtil_SetNoOutput for AdaptingProcess
+
+ @todo don't allocate temp buffers for blocking streams unless they are
+ needed. At the moment they are needed, but perhaps for host APIs
+ where the implementation passes a buffer to the host they could be
+ used.
+*/
+
+
+#include <assert.h>
+#include <string.h> /* memset() */
+
+#include "pa_process.h"
+#include "pa_util.h"
+
+
+#define PA_FRAMES_PER_TEMP_BUFFER_WHEN_HOST_BUFFER_SIZE_IS_UNKNOWN_ 1024
+
+#define PA_MIN_( a, b ) ( ((a)<(b)) ? (a) : (b) )
+
+
+/* greatest common divisor - PGCD in French */
+static unsigned long GCD( unsigned long a, unsigned long b )
+{
+ return (b==0) ? a : GCD( b, a%b);
+}
+
+/* least common multiple - PPCM in French */
+static unsigned long LCM( unsigned long a, unsigned long b )
+{
+ return (a*b) / GCD(a,b);
+}
+
+#define PA_MAX_( a, b ) (((a) > (b)) ? (a) : (b))
+
+static unsigned long CalculateFrameShift( unsigned long M, unsigned long N )
+{
+ unsigned long result = 0;
+ unsigned long i;
+ unsigned long lcm;
+
+ assert( M > 0 );
+ assert( N > 0 );
+
+ lcm = LCM( M, N );
+ for( i = M; i < lcm; i += M )
+ result = PA_MAX_( result, i % N );
+
+ return result;
+}
+
+
+PaError PaUtil_InitializeBufferProcessor( PaUtilBufferProcessor* bp,
+ int inputChannelCount, PaSampleFormat userInputSampleFormat,
+ PaSampleFormat hostInputSampleFormat,
+ int outputChannelCount, PaSampleFormat userOutputSampleFormat,
+ PaSampleFormat hostOutputSampleFormat,
+ double sampleRate,
+ PaStreamFlags streamFlags,
+ unsigned long framesPerUserBuffer,
+ unsigned long framesPerHostBuffer,
+ PaUtilHostBufferSizeMode hostBufferSizeMode,
+ PaStreamCallback *streamCallback, void *userData )
+{
+ PaError result = paNoError;
+ PaError bytesPerSample;
+ unsigned long tempInputBufferSize, tempOutputBufferSize;
+
+ /* initialize buffer ptrs to zero so they can be freed if necessary in error */
+ bp->tempInputBuffer = 0;
+ bp->tempInputBufferPtrs = 0;
+ bp->tempOutputBuffer = 0;
+ bp->tempOutputBufferPtrs = 0;
+
+ bp->framesPerUserBuffer = framesPerUserBuffer;
+ bp->framesPerHostBuffer = framesPerHostBuffer;
+
+ bp->inputChannelCount = inputChannelCount;
+ bp->outputChannelCount = outputChannelCount;
+
+ bp->hostBufferSizeMode = hostBufferSizeMode;
+
+ bp->hostInputChannels[0] = bp->hostInputChannels[1] = 0;
+ bp->hostOutputChannels[0] = bp->hostOutputChannels[1] = 0;
+
+ if( framesPerUserBuffer == 0 ) /* streamCallback will accept any buffer size */
+ {
+ bp->useNonAdaptingProcess = 1;
+ bp->initialFramesInTempInputBuffer = 0;
+ bp->initialFramesInTempOutputBuffer = 0;
+
+ if( hostBufferSizeMode == paUtilFixedHostBufferSize
+ || hostBufferSizeMode == paUtilBoundedHostBufferSize )
+ {
+ bp->framesPerTempBuffer = framesPerHostBuffer;
+ }
+ else /* unknown host buffer size */
+ {
+ bp->framesPerTempBuffer = PA_FRAMES_PER_TEMP_BUFFER_WHEN_HOST_BUFFER_SIZE_IS_UNKNOWN_;
+ }
+ }
+ else
+ {
+ bp->framesPerTempBuffer = framesPerUserBuffer;
+
+ if( hostBufferSizeMode == paUtilFixedHostBufferSize
+ && framesPerHostBuffer % framesPerUserBuffer == 0 )
+ {
+ bp->useNonAdaptingProcess = 1;
+ bp->initialFramesInTempInputBuffer = 0;
+ bp->initialFramesInTempOutputBuffer = 0;
+ }
+ else
+ {
+ bp->useNonAdaptingProcess = 0;
+
+ if( inputChannelCount > 0 && outputChannelCount > 0 )
+ {
+ /* full duplex */
+ if( hostBufferSizeMode == paUtilFixedHostBufferSize )
+ {
+ unsigned long frameShift =
+ CalculateFrameShift( framesPerHostBuffer, framesPerUserBuffer );
+
+ if( framesPerUserBuffer > framesPerHostBuffer )
+ {
+ bp->initialFramesInTempInputBuffer = frameShift;
+ bp->initialFramesInTempOutputBuffer = 0;
+ }
+ else
+ {
+ bp->initialFramesInTempInputBuffer = 0;
+ bp->initialFramesInTempOutputBuffer = frameShift;
+ }
+ }
+ else /* variable host buffer size, add framesPerUserBuffer latency */
+ {
+ bp->initialFramesInTempInputBuffer = 0;
+ bp->initialFramesInTempOutputBuffer = framesPerUserBuffer;
+ }
+ }
+ else
+ {
+ /* half duplex */
+ bp->initialFramesInTempInputBuffer = 0;
+ bp->initialFramesInTempOutputBuffer = 0;
+ }
+ }
+ }
+
+
+ bp->framesInTempInputBuffer = bp->initialFramesInTempInputBuffer;
+ bp->framesInTempOutputBuffer = bp->initialFramesInTempOutputBuffer;
+
+
+ if( inputChannelCount > 0 )
+ {
+ bytesPerSample = Pa_GetSampleSize( hostInputSampleFormat );
+ if( bytesPerSample > 0 )
+ {
+ bp->bytesPerHostInputSample = bytesPerSample;
+ }
+ else
+ {
+ result = bytesPerSample;
+ goto error;
+ }
+
+ bytesPerSample = Pa_GetSampleSize( userInputSampleFormat );
+ if( bytesPerSample > 0 )
+ {
+ bp->bytesPerUserInputSample = bytesPerSample;
+ }
+ else
+ {
+ result = bytesPerSample;
+ goto error;
+ }
+
+ bp->inputConverter =
+ PaUtil_SelectConverter( hostInputSampleFormat, userInputSampleFormat, streamFlags );
+
+ bp->inputZeroer = PaUtil_SelectZeroer( hostInputSampleFormat );
+
+ bp->userInputIsInterleaved = (userInputSampleFormat & paNonInterleaved)?0:1;
+
+
+ tempInputBufferSize =
+ bp->framesPerTempBuffer * bp->bytesPerUserInputSample * inputChannelCount;
+
+ bp->tempInputBuffer = PaUtil_AllocateMemory( tempInputBufferSize );
+ if( bp->tempInputBuffer == 0 )
+ {
+ result = paInsufficientMemory;
+ goto error;
+ }
+
+ if( bp->framesInTempInputBuffer > 0 )
+ memset( bp->tempInputBuffer, 0, tempInputBufferSize );
+
+ if( userInputSampleFormat & paNonInterleaved )
+ {
+ bp->tempInputBufferPtrs =
+ (void **)PaUtil_AllocateMemory( sizeof(void*)*inputChannelCount );
+ if( bp->tempInputBufferPtrs == 0 )
+ {
+ result = paInsufficientMemory;
+ goto error;
+ }
+ }
+
+ bp->hostInputChannels[0] = (PaUtilChannelDescriptor*)
+ PaUtil_AllocateMemory( sizeof(PaUtilChannelDescriptor) * inputChannelCount * 2);
+ if( bp->hostInputChannels[0] == 0 )
+ {
+ result = paInsufficientMemory;
+ goto error;
+ }
+
+ bp->hostInputChannels[1] = &bp->hostInputChannels[0][inputChannelCount];
+ }
+
+ if( outputChannelCount > 0 )
+ {
+ bytesPerSample = Pa_GetSampleSize( hostOutputSampleFormat );
+ if( bytesPerSample > 0 )
+ {
+ bp->bytesPerHostOutputSample = bytesPerSample;
+ }
+ else
+ {
+ result = bytesPerSample;
+ goto error;
+ }
+
+ bytesPerSample = Pa_GetSampleSize( userOutputSampleFormat );
+ if( bytesPerSample > 0 )
+ {
+ bp->bytesPerUserOutputSample = bytesPerSample;
+ }
+ else
+ {
+ result = bytesPerSample;
+ goto error;
+ }
+
+ bp->outputConverter =
+ PaUtil_SelectConverter( userOutputSampleFormat, hostOutputSampleFormat, streamFlags );
+
+ bp->outputZeroer = PaUtil_SelectZeroer( hostOutputSampleFormat );
+
+ bp->userOutputIsInterleaved = (userOutputSampleFormat & paNonInterleaved)?0:1;
+
+ tempOutputBufferSize =
+ bp->framesPerTempBuffer * bp->bytesPerUserOutputSample * outputChannelCount;
+
+ bp->tempOutputBuffer = PaUtil_AllocateMemory( tempOutputBufferSize );
+ if( bp->tempOutputBuffer == 0 )
+ {
+ result = paInsufficientMemory;
+ goto error;
+ }
+
+ if( bp->framesInTempOutputBuffer > 0 )
+ memset( bp->tempOutputBuffer, 0, tempOutputBufferSize );
+
+ if( userOutputSampleFormat & paNonInterleaved )
+ {
+ bp->tempOutputBufferPtrs =
+ (void **)PaUtil_AllocateMemory( sizeof(void*)*outputChannelCount );
+ if( bp->tempOutputBufferPtrs == 0 )
+ {
+ result = paInsufficientMemory;
+ goto error;
+ }
+ }
+
+ bp->hostOutputChannels[0] = (PaUtilChannelDescriptor*)
+ PaUtil_AllocateMemory( sizeof(PaUtilChannelDescriptor)*outputChannelCount * 2 );
+ if( bp->hostOutputChannels[0] == 0 )
+ {
+ result = paInsufficientMemory;
+ goto error;
+ }
+
+ bp->hostOutputChannels[1] = &bp->hostOutputChannels[0][outputChannelCount];
+ }
+
+ PaUtil_InitializeTriangularDitherState( &bp->ditherGenerator );
+
+ bp->samplePeriod = 1. / sampleRate;
+
+ bp->streamCallback = streamCallback;
+ bp->userData = userData;
+
+ return result;
+
+error:
+ if( bp->tempInputBuffer )
+ PaUtil_FreeMemory( bp->tempInputBuffer );
+
+ if( bp->tempInputBufferPtrs )
+ PaUtil_FreeMemory( bp->tempInputBufferPtrs );
+
+ if( bp->hostInputChannels[0] )
+ PaUtil_FreeMemory( bp->hostInputChannels[0] );
+
+ if( bp->tempOutputBuffer )
+ PaUtil_FreeMemory( bp->tempOutputBuffer );
+
+ if( bp->tempOutputBufferPtrs )
+ PaUtil_FreeMemory( bp->tempOutputBufferPtrs );
+
+ if( bp->hostOutputChannels[0] )
+ PaUtil_FreeMemory( bp->hostOutputChannels[0] );
+
+ return result;
+}
+
+
+void PaUtil_TerminateBufferProcessor( PaUtilBufferProcessor* bp )
+{
+ if( bp->tempInputBuffer )
+ PaUtil_FreeMemory( bp->tempInputBuffer );
+
+ if( bp->tempInputBufferPtrs )
+ PaUtil_FreeMemory( bp->tempInputBufferPtrs );
+
+ if( bp->hostInputChannels[0] )
+ PaUtil_FreeMemory( bp->hostInputChannels[0] );
+
+ if( bp->tempOutputBuffer )
+ PaUtil_FreeMemory( bp->tempOutputBuffer );
+
+ if( bp->tempOutputBufferPtrs )
+ PaUtil_FreeMemory( bp->tempOutputBufferPtrs );
+
+ if( bp->hostOutputChannels[0] )
+ PaUtil_FreeMemory( bp->hostOutputChannels[0] );
+}
+
+
+void PaUtil_ResetBufferProcessor( PaUtilBufferProcessor* bp )
+{
+ unsigned long tempInputBufferSize, tempOutputBufferSize;
+
+ bp->framesInTempInputBuffer = bp->initialFramesInTempInputBuffer;
+ bp->framesInTempOutputBuffer = bp->initialFramesInTempOutputBuffer;
+
+ if( bp->framesInTempInputBuffer > 0 )
+ {
+ tempInputBufferSize =
+ bp->framesPerTempBuffer * bp->bytesPerUserInputSample * bp->inputChannelCount;
+ memset( bp->tempInputBuffer, 0, tempInputBufferSize );
+ }
+
+ if( bp->framesInTempOutputBuffer > 0 )
+ {
+ tempOutputBufferSize =
+ bp->framesPerTempBuffer * bp->bytesPerUserOutputSample * bp->outputChannelCount;
+ memset( bp->tempOutputBuffer, 0, tempOutputBufferSize );
+ }
+}
+
+
+unsigned long PaUtil_GetBufferProcessorInputLatency( PaUtilBufferProcessor* bp )
+{
+ return bp->initialFramesInTempInputBuffer;
+}
+
+
+unsigned long PaUtil_GetBufferProcessorOutputLatency( PaUtilBufferProcessor* bp )
+{
+ return bp->initialFramesInTempOutputBuffer;
+}
+
+
+void PaUtil_SetInputFrameCount( PaUtilBufferProcessor* bp,
+ unsigned long frameCount )
+{
+ if( frameCount == 0 )
+ bp->hostInputFrameCount[0] = bp->framesPerHostBuffer;
+ else
+ bp->hostInputFrameCount[0] = frameCount;
+}
+
+
+void PaUtil_SetNoInput( PaUtilBufferProcessor* bp )
+{
+ assert( bp->inputChannelCount > 0 );
+
+ bp->hostInputChannels[0][0].data = 0;
+}
+
+
+void PaUtil_SetInputChannel( PaUtilBufferProcessor* bp,
+ unsigned int channel, void *data, unsigned int stride )
+{
+ assert( channel < bp->inputChannelCount );
+
+ bp->hostInputChannels[0][channel].data = data;
+ bp->hostInputChannels[0][channel].stride = stride;
+}
+
+
+void PaUtil_SetInterleavedInputChannels( PaUtilBufferProcessor* bp,
+ unsigned int firstChannel, void *data, unsigned int channelCount )
+{
+ unsigned int i;
+ unsigned int channel = firstChannel;
+ unsigned char *p = (unsigned char*)data;
+
+ if( channelCount == 0 )
+ channelCount = bp->inputChannelCount;
+
+ assert( firstChannel < bp->inputChannelCount );
+ assert( firstChannel + channelCount <= bp->inputChannelCount );
+
+ for( i=0; i< channelCount; ++i )
+ {
+ bp->hostInputChannels[0][channel+i].data = p;
+ p += bp->bytesPerHostInputSample;
+ bp->hostInputChannels[0][channel+i].stride = channelCount;
+ }
+}
+
+
+void PaUtil_SetNonInterleavedInputChannel( PaUtilBufferProcessor* bp,
+ unsigned int channel, void *data )
+{
+ assert( channel < bp->inputChannelCount );
+
+ bp->hostInputChannels[0][channel].data = data;
+ bp->hostInputChannels[0][channel].stride = 1;
+}
+
+
+void PaUtil_Set2ndInputFrameCount( PaUtilBufferProcessor* bp,
+ unsigned long frameCount )
+{
+ bp->hostInputFrameCount[1] = frameCount;
+}
+
+
+void PaUtil_Set2ndInputChannel( PaUtilBufferProcessor* bp,
+ unsigned int channel, void *data, unsigned int stride )
+{
+ assert( channel < bp->inputChannelCount );
+
+ bp->hostInputChannels[1][channel].data = data;
+ bp->hostInputChannels[1][channel].stride = stride;
+}
+
+
+void PaUtil_Set2ndInterleavedInputChannels( PaUtilBufferProcessor* bp,
+ unsigned int firstChannel, void *data, unsigned int channelCount )
+{
+ unsigned int i;
+ unsigned int channel = firstChannel;
+ unsigned char *p = (unsigned char*)data;
+
+ if( channelCount == 0 )
+ channelCount = bp->inputChannelCount;
+
+ assert( firstChannel < bp->inputChannelCount );
+ assert( firstChannel + channelCount <= bp->inputChannelCount );
+
+ for( i=0; i< channelCount; ++i )
+ {
+ bp->hostInputChannels[1][channel+i].data = p;
+ p += bp->bytesPerHostInputSample;
+ bp->hostInputChannels[1][channel+i].stride = channelCount;
+ }
+}
+
+
+void PaUtil_Set2ndNonInterleavedInputChannel( PaUtilBufferProcessor* bp,
+ unsigned int channel, void *data )
+{
+ assert( channel < bp->inputChannelCount );
+
+ bp->hostInputChannels[1][channel].data = data;
+ bp->hostInputChannels[1][channel].stride = 1;
+}
+
+
+void PaUtil_SetOutputFrameCount( PaUtilBufferProcessor* bp,
+ unsigned long frameCount )
+{
+ if( frameCount == 0 )
+ bp->hostOutputFrameCount[0] = bp->framesPerHostBuffer;
+ else
+ bp->hostOutputFrameCount[0] = frameCount;
+}
+
+
+void PaUtil_SetNoOutput( PaUtilBufferProcessor* bp )
+{
+ assert( bp->outputChannelCount > 0 );
+
+ bp->hostOutputChannels[0][0].data = 0;
+}
+
+
+void PaUtil_SetOutputChannel( PaUtilBufferProcessor* bp,
+ unsigned int channel, void *data, unsigned int stride )
+{
+ assert( channel < bp->outputChannelCount );
+
+ bp->hostOutputChannels[0][channel].data = data;
+ bp->hostOutputChannels[0][channel].stride = stride;
+}
+
+
+void PaUtil_SetInterleavedOutputChannels( PaUtilBufferProcessor* bp,
+ unsigned int firstChannel, void *data, unsigned int channelCount )
+{
+ unsigned int i;
+ unsigned int channel = firstChannel;
+ unsigned char *p = (unsigned char*)data;
+
+ if( channelCount == 0 )
+ channelCount = bp->outputChannelCount;
+
+ assert( firstChannel < bp->outputChannelCount );
+ assert( firstChannel + channelCount <= bp->outputChannelCount );
+
+ for( i=0; i< channelCount; ++i )
+ {
+ bp->hostOutputChannels[0][channel+i].data = p;
+ p += bp->bytesPerHostOutputSample;
+ bp->hostOutputChannels[0][channel+i].stride = channelCount;
+ }
+}
+
+
+void PaUtil_SetNonInterleavedOutputChannel( PaUtilBufferProcessor* bp,
+ unsigned int channel, void *data )
+{
+ assert( channel < bp->outputChannelCount );
+
+ bp->hostOutputChannels[0][channel].data = data;
+ bp->hostOutputChannels[0][channel].stride = 1;
+}
+
+
+void PaUtil_Set2ndOutputFrameCount( PaUtilBufferProcessor* bp,
+ unsigned long frameCount )
+{
+ bp->hostOutputFrameCount[1] = frameCount;
+}
+
+
+void PaUtil_Set2ndOutputChannel( PaUtilBufferProcessor* bp,
+ unsigned int channel, void *data, unsigned int stride )
+{
+ assert( channel < bp->outputChannelCount );
+
+ bp->hostOutputChannels[1][channel].data = data;
+ bp->hostOutputChannels[1][channel].stride = stride;
+}
+
+
+void PaUtil_Set2ndInterleavedOutputChannels( PaUtilBufferProcessor* bp,
+ unsigned int firstChannel, void *data, unsigned int channelCount )
+{
+ unsigned int i;
+ unsigned int channel = firstChannel;
+ unsigned char *p = (unsigned char*)data;
+
+ if( channelCount == 0 )
+ channelCount = bp->outputChannelCount;
+
+ assert( firstChannel < bp->outputChannelCount );
+ assert( firstChannel + channelCount <= bp->outputChannelCount );
+
+ for( i=0; i< channelCount; ++i )
+ {
+ bp->hostOutputChannels[1][channel+i].data = p;
+ p += bp->bytesPerHostOutputSample;
+ bp->hostOutputChannels[1][channel+i].stride = channelCount;
+ }
+}
+
+
+void PaUtil_Set2ndNonInterleavedOutputChannel( PaUtilBufferProcessor* bp,
+ unsigned int channel, void *data )
+{
+ assert( channel < bp->outputChannelCount );
+
+ bp->hostOutputChannels[1][channel].data = data;
+ bp->hostOutputChannels[1][channel].stride = 1;
+}
+
+
+void PaUtil_BeginBufferProcessing( PaUtilBufferProcessor* bp,
+ PaStreamCallbackTimeInfo* timeInfo, PaStreamCallbackFlags callbackStatusFlags )
+{
+ bp->timeInfo = timeInfo;
+
+ /* the first streamCallback will be called to process samples which are
+ currently in the input buffer before the ones starting at the timeInfo time */
+
+ bp->timeInfo->inputBufferAdcTime -= bp->framesInTempInputBuffer * bp->samplePeriod;
+
+ bp->timeInfo->currentTime = 0; /** FIXME: @todo time info currentTime not implemented */
+
+ /* the first streamCallback will be called to generate samples which will be
+ outputted after the frames currently in the output buffer have been
+ outputted. */
+ bp->timeInfo->outputBufferDacTime += bp->framesInTempOutputBuffer * bp->samplePeriod;
+
+ bp->callbackStatusFlags = callbackStatusFlags;
+
+ bp->hostInputFrameCount[1] = 0;
+ bp->hostOutputFrameCount[1] = 0;
+}
+
+
+/*
+ NonAdaptingProcess() is a simple buffer copying adaptor that can handle
+ both full and half duplex copies. It processes framesToProcess frames,
+ broken into blocks bp->framesPerTempBuffer long.
+ This routine can be used when the streamCallback doesn't care what length
+ the buffers are, or when framesToProcess is an integer multiple of
+ bp->framesPerTempBuffer, in which case streamCallback will always be called
+ with bp->framesPerTempBuffer samples.
+*/
+static unsigned long NonAdaptingProcess( PaUtilBufferProcessor *bp,
+ int *streamCallbackResult,
+ PaUtilChannelDescriptor *hostInputChannels,
+ PaUtilChannelDescriptor *hostOutputChannels,
+ unsigned long framesToProcess )
+{
+ void *userInput, *userOutput;
+ unsigned char *srcBytePtr, *destBytePtr;
+ unsigned int srcSampleStrideSamples; /* stride from one sample to the next within a channel, in samples */
+ unsigned int srcChannelStrideBytes; /* stride from one channel to the next, in bytes */
+ unsigned int destSampleStrideSamples; /* stride from one sample to the next within a channel, in samples */
+ unsigned int destChannelStrideBytes; /* stride from one channel to the next, in bytes */
+ unsigned int i;
+ unsigned long frameCount;
+ unsigned long framesToGo = framesToProcess;
+ unsigned long framesProcessed = 0;
+
+
+ if( *streamCallbackResult == paContinue )
+ {
+ do
+ {
+ frameCount = PA_MIN_( bp->framesPerTempBuffer, framesToGo );
+
+ /* configure user input buffer and convert input data (host -> user) */
+ if( bp->inputChannelCount == 0 )
+ {
+ /* no input */
+ userInput = 0;
+ }
+ else /* there are input channels */
+ {
+ /*
+ could use more elaborate logic here and sometimes process
+ buffers in-place.
+ */
+
+ destBytePtr = (unsigned char *)bp->tempInputBuffer;
+
+ if( bp->userInputIsInterleaved )
+ {
+ destSampleStrideSamples = bp->inputChannelCount;
+ destChannelStrideBytes = bp->bytesPerUserInputSample;
+ userInput = bp->tempInputBuffer;
+ }
+ else /* user input is not interleaved */
+ {
+ destSampleStrideSamples = 1;
+ destChannelStrideBytes = frameCount * bp->bytesPerUserInputSample;
+
+ /* setup non-interleaved ptrs */
+ for( i=0; i<bp->inputChannelCount; ++i )
+ {
+ bp->tempInputBufferPtrs[i] = ((unsigned char*)bp->tempInputBuffer) +
+ i * bp->bytesPerUserInputSample * frameCount;
+ }
+
+ userInput = bp->tempInputBufferPtrs;
+ }
+
+ if( !bp->hostInputChannels[0][0].data )
+ {
+ /* no input was supplied (see PaUtil_SetNoInput), so
+ zero the input buffer */
+
+ for( i=0; i<bp->inputChannelCount; ++i )
+ {
+ bp->inputZeroer( destBytePtr, destSampleStrideSamples, frameCount );
+ destBytePtr += destChannelStrideBytes; /* skip to next destination channel */
+ }
+ }
+ else
+ {
+ for( i=0; i<bp->inputChannelCount; ++i )
+ {
+ bp->inputConverter( destBytePtr, destSampleStrideSamples,
+ hostInputChannels[i].data,
+ hostInputChannels[i].stride,
+ frameCount, &bp->ditherGenerator );
+
+ destBytePtr += destChannelStrideBytes; /* skip to next destination channel */
+
+ /* advance src ptr for next iteration */
+ hostInputChannels[i].data = ((unsigned char*)hostInputChannels[i].data) +
+ frameCount * hostInputChannels[i].stride * bp->bytesPerHostInputSample;
+ }
+ }
+ }
+
+ /* configure user output buffer */
+ if( bp->outputChannelCount == 0 )
+ {
+ /* no output */
+ userOutput = 0;
+ }
+ else /* there are output channels */
+ {
+ if( bp->userOutputIsInterleaved )
+ {
+ userOutput = bp->tempOutputBuffer;
+ }
+ else /* user output is not interleaved */
+ {
+ for( i = 0; i < bp->outputChannelCount; ++i )
+ {
+ bp->tempOutputBufferPtrs[i] = ((unsigned char*)bp->tempOutputBuffer) +
+ i * bp->bytesPerUserOutputSample * frameCount;
+ }
+
+ userOutput = bp->tempOutputBufferPtrs;
+ }
+ }
+
+ *streamCallbackResult = bp->streamCallback( userInput, userOutput,
+ frameCount, bp->timeInfo, bp->callbackStatusFlags, bp->userData );
+
+ if( *streamCallbackResult == paAbort )
+ {
+ /* callback returned paAbort, don't advance framesProcessed
+ and framesToGo, they will be handled below */
+ }
+ else
+ {
+ bp->timeInfo->inputBufferAdcTime += frameCount * bp->samplePeriod;
+ bp->timeInfo->outputBufferDacTime += frameCount * bp->samplePeriod;
+
+ /* convert output data (user -> host) */
+
+ if( bp->outputChannelCount != 0 && bp->hostOutputChannels[0][0].data )
+ {
+ /*
+ could use more elaborate logic here and sometimes process
+ buffers in-place.
+ */
+
+ srcBytePtr = (unsigned char *)bp->tempOutputBuffer;
+
+ if( bp->userOutputIsInterleaved )
+ {
+ srcSampleStrideSamples = bp->outputChannelCount;
+ srcChannelStrideBytes = bp->bytesPerUserOutputSample;
+ }
+ else /* user output is not interleaved */
+ {
+ srcSampleStrideSamples = 1;
+ srcChannelStrideBytes = frameCount * bp->bytesPerUserOutputSample;
+ }
+
+ for( i=0; i<bp->outputChannelCount; ++i )
+ {
+ bp->outputConverter( hostOutputChannels[i].data,
+ hostOutputChannels[i].stride,
+ srcBytePtr, srcSampleStrideSamples,
+ frameCount, &bp->ditherGenerator );
+
+ srcBytePtr += srcChannelStrideBytes; /* skip to next source channel */
+
+ /* advance dest ptr for next iteration */
+ hostOutputChannels[i].data = ((unsigned char*)hostOutputChannels[i].data) +
+ frameCount * hostOutputChannels[i].stride * bp->bytesPerHostOutputSample;
+ }
+ }
+
+ framesProcessed += frameCount;
+
+ framesToGo -= frameCount;
+ }
+ }
+ while( framesToGo > 0 && *streamCallbackResult == paContinue );
+ }
+
+ if( framesToGo > 0 )
+ {
+ /* zero any remaining frames output. There will only be remaining frames
+ if the callback has returned paComplete or paAbort */
+
+ frameCount = framesToGo;
+
+ if( bp->outputChannelCount != 0 && bp->hostOutputChannels[0][0].data )
+ {
+ for( i=0; i<bp->outputChannelCount; ++i )
+ {
+ bp->outputZeroer( hostOutputChannels[i].data,
+ hostOutputChannels[i].stride,
+ frameCount );
+
+ /* advance dest ptr for next iteration */
+ hostOutputChannels[i].data = ((unsigned char*)hostOutputChannels[i].data) +
+ frameCount * hostOutputChannels[i].stride * bp->bytesPerHostOutputSample;
+ }
+ }
+
+ framesProcessed += frameCount;
+ }
+
+ return framesProcessed;
+}
+
+
+/*
+ AdaptingInputOnlyProcess() is a half duplex input buffer processor. It
+ converts data from the input buffers into the temporary input buffer,
+ when the temporary input buffer is full, it calls the streamCallback.
+*/
+static unsigned long AdaptingInputOnlyProcess( PaUtilBufferProcessor *bp,
+ int *streamCallbackResult,
+ PaUtilChannelDescriptor *hostInputChannels,
+ unsigned long framesToProcess )
+{
+ void *userInput, *userOutput;
+ unsigned char *destBytePtr;
+ unsigned int destSampleStrideSamples; /* stride from one sample to the next within a channel, in samples */
+ unsigned int destChannelStrideBytes; /* stride from one channel to the next, in bytes */
+ unsigned int i;
+ unsigned long frameCount;
+ unsigned long framesToGo = framesToProcess;
+ unsigned long framesProcessed = 0;
+
+ userOutput = 0;
+
+ do
+ {
+ frameCount = ( bp->framesInTempInputBuffer + framesToGo > bp->framesPerUserBuffer )
+ ? ( bp->framesPerUserBuffer - bp->framesInTempInputBuffer )
+ : framesToGo;
+
+ /* convert frameCount samples into temp buffer */
+
+ if( bp->userInputIsInterleaved )
+ {
+ destBytePtr = ((unsigned char*)bp->tempInputBuffer) +
+ bp->bytesPerUserInputSample * bp->inputChannelCount *
+ bp->framesInTempInputBuffer;
+
+ destSampleStrideSamples = bp->inputChannelCount;
+ destChannelStrideBytes = bp->bytesPerUserInputSample;
+
+ userInput = bp->tempInputBuffer;
+ }
+ else /* user input is not interleaved */
+ {
+ destBytePtr = ((unsigned char*)bp->tempInputBuffer) +
+ bp->bytesPerUserInputSample * bp->framesInTempInputBuffer;
+
+ destSampleStrideSamples = 1;
+ destChannelStrideBytes = bp->framesPerUserBuffer * bp->bytesPerUserInputSample;
+
+ /* setup non-interleaved ptrs */
+ for( i=0; i<bp->inputChannelCount; ++i )
+ {
+ bp->tempInputBufferPtrs[i] = ((unsigned char*)bp->tempInputBuffer) +
+ i * bp->bytesPerUserInputSample * bp->framesPerUserBuffer;
+ }
+
+ userInput = bp->tempInputBufferPtrs;
+ }
+
+ for( i=0; i<bp->inputChannelCount; ++i )
+ {
+ bp->inputConverter( destBytePtr, destSampleStrideSamples,
+ hostInputChannels[i].data,
+ hostInputChannels[i].stride,
+ frameCount, &bp->ditherGenerator );
+
+ destBytePtr += destChannelStrideBytes; /* skip to next destination channel */
+
+ /* advance src ptr for next iteration */
+ hostInputChannels[i].data = ((unsigned char*)hostInputChannels[i].data) +
+ frameCount * hostInputChannels[i].stride * bp->bytesPerHostInputSample;
+ }
+
+ bp->framesInTempInputBuffer += frameCount;
+
+ if( bp->framesInTempInputBuffer == bp->framesPerUserBuffer )
+ {
+ /**
+ @todo (non-critical optimisation)
+ The conditional below implements the continue/complete/abort mechanism
+ simply by continuing on iterating through the input buffer, but not
+ passing the data to the callback. With care, the outer loop could be
+ terminated earlier, thus some unneeded conversion cycles would be
+ saved.
+ */
+ if( *streamCallbackResult == paContinue )
+ {
+ bp->timeInfo->outputBufferDacTime = 0;
+
+ *streamCallbackResult = bp->streamCallback( userInput, userOutput,
+ bp->framesPerUserBuffer, bp->timeInfo,
+ bp->callbackStatusFlags, bp->userData );
+
+ bp->timeInfo->inputBufferAdcTime += frameCount * bp->samplePeriod;
+ }
+
+ bp->framesInTempInputBuffer = 0;
+ }
+
+ framesProcessed += frameCount;
+
+ framesToGo -= frameCount;
+ }while( framesToGo > 0 );
+
+ return framesProcessed;
+}
+
+
+/*
+ AdaptingOutputOnlyProcess() is a half duplex output buffer processor.
+ It converts data from the temporary output buffer, to the output buffers,
+ when the temporary output buffer is empty, it calls the streamCallback.
+*/
+static unsigned long AdaptingOutputOnlyProcess( PaUtilBufferProcessor *bp,
+ int *streamCallbackResult,
+ PaUtilChannelDescriptor *hostOutputChannels,
+ unsigned long framesToProcess )
+{
+ void *userInput, *userOutput;
+ unsigned char *srcBytePtr;
+ unsigned int srcSampleStrideSamples; /* stride from one sample to the next within a channel, in samples */
+ unsigned int srcChannelStrideBytes; /* stride from one channel to the next, in bytes */
+ unsigned int i;
+ unsigned long frameCount;
+ unsigned long framesToGo = framesToProcess;
+ unsigned long framesProcessed = 0;
+
+ do
+ {
+ if( bp->framesInTempOutputBuffer == 0 && *streamCallbackResult == paContinue )
+ {
+ userInput = 0;
+
+ /* setup userOutput */
+ if( bp->userOutputIsInterleaved )
+ {
+ userOutput = bp->tempOutputBuffer;
+ }
+ else /* user output is not interleaved */
+ {
+ for( i = 0; i < bp->outputChannelCount; ++i )
+ {
+ bp->tempOutputBufferPtrs[i] = ((unsigned char*)bp->tempOutputBuffer) +
+ i * bp->framesPerUserBuffer * bp->bytesPerUserOutputSample;
+ }
+
+ userOutput = bp->tempOutputBufferPtrs;
+ }
+
+ bp->timeInfo->inputBufferAdcTime = 0;
+
+ *streamCallbackResult = bp->streamCallback( userInput, userOutput,
+ bp->framesPerUserBuffer, bp->timeInfo,
+ bp->callbackStatusFlags, bp->userData );
+
+ if( *streamCallbackResult == paAbort )
+ {
+ /* if the callback returned paAbort, we disregard its output */
+ }
+ else
+ {
+ bp->timeInfo->outputBufferDacTime += bp->framesPerUserBuffer * bp->samplePeriod;
+
+ bp->framesInTempOutputBuffer = bp->framesPerUserBuffer;
+ }
+ }
+
+ if( bp->framesInTempOutputBuffer > 0 )
+ {
+ /* convert frameCount frames from user buffer to host buffer */
+
+ frameCount = PA_MIN_( bp->framesInTempOutputBuffer, framesToGo );
+
+ if( bp->userOutputIsInterleaved )
+ {
+ srcBytePtr = ((unsigned char*)bp->tempOutputBuffer) +
+ bp->bytesPerUserOutputSample * bp->outputChannelCount *
+ (bp->framesPerUserBuffer - bp->framesInTempOutputBuffer);
+
+ srcSampleStrideSamples = bp->outputChannelCount;
+ srcChannelStrideBytes = bp->bytesPerUserOutputSample;
+ }
+ else /* user output is not interleaved */
+ {
+ srcBytePtr = ((unsigned char*)bp->tempOutputBuffer) +
+ bp->bytesPerUserOutputSample *
+ (bp->framesPerUserBuffer - bp->framesInTempOutputBuffer);
+
+ srcSampleStrideSamples = 1;
+ srcChannelStrideBytes = bp->framesPerUserBuffer * bp->bytesPerUserOutputSample;
+ }
+
+ for( i=0; i<bp->outputChannelCount; ++i )
+ {
+ bp->outputConverter( hostOutputChannels[i].data,
+ hostOutputChannels[i].stride,
+ srcBytePtr, srcSampleStrideSamples,
+ frameCount, &bp->ditherGenerator );
+
+ srcBytePtr += srcChannelStrideBytes; /* skip to next source channel */
+
+ /* advance dest ptr for next iteration */
+ hostOutputChannels[i].data = ((unsigned char*)hostOutputChannels[i].data) +
+ frameCount * hostOutputChannels[i].stride * bp->bytesPerHostOutputSample;
+ }
+
+ bp->framesInTempOutputBuffer -= frameCount;
+ }
+ else
+ {
+ /* no more user data is available because the callback has returned
+ paComplete or paAbort. Fill the remainder of the host buffer
+ with zeros.
+ */
+
+ frameCount = framesToGo;
+
+ for( i=0; i<bp->outputChannelCount; ++i )
+ {
+ bp->outputZeroer( hostOutputChannels[i].data,
+ hostOutputChannels[i].stride,
+ frameCount );
+
+ /* advance dest ptr for next iteration */
+ hostOutputChannels[i].data = ((unsigned char*)hostOutputChannels[i].data) +
+ frameCount * hostOutputChannels[i].stride * bp->bytesPerHostOutputSample;
+ }
+ }
+
+ framesProcessed += frameCount;
+
+ framesToGo -= frameCount;
+
+ }while( framesToGo > 0 );
+
+ return framesProcessed;
+}
+
+/* CopyTempOutputBuffersToHostOutputBuffers is called from AdaptingProcess to copy frames from
+ tempOutputBuffer to hostOutputChannels. This includes data conversion
+ and interleaving.
+*/
+static void CopyTempOutputBuffersToHostOutputBuffers( PaUtilBufferProcessor *bp)
+{
+ unsigned long maxFramesToCopy;
+ PaUtilChannelDescriptor *hostOutputChannels;
+ unsigned int frameCount;
+ unsigned char *srcBytePtr;
+ unsigned int srcSampleStrideSamples; /* stride from one sample to the next within a channel, in samples */
+ unsigned int srcChannelStrideBytes; /* stride from one channel to the next, in bytes */
+ unsigned int i;
+
+ /* copy frames from user to host output buffers */
+ while( bp->framesInTempOutputBuffer > 0 &&
+ ((bp->hostOutputFrameCount[0] + bp->hostOutputFrameCount[1]) > 0) )
+ {
+ maxFramesToCopy = bp->framesInTempOutputBuffer;
+
+ /* select the output buffer set (1st or 2nd) */
+ if( bp->hostOutputFrameCount[0] > 0 )
+ {
+ hostOutputChannels = bp->hostOutputChannels[0];
+ frameCount = PA_MIN_( bp->hostOutputFrameCount[0], maxFramesToCopy );
+ }
+ else
+ {
+ hostOutputChannels = bp->hostOutputChannels[1];
+ frameCount = PA_MIN_( bp->hostOutputFrameCount[1], maxFramesToCopy );
+ }
+
+ if( bp->userOutputIsInterleaved )
+ {
+ srcBytePtr = ((unsigned char*)bp->tempOutputBuffer) +
+ bp->bytesPerUserOutputSample * bp->outputChannelCount *
+ (bp->framesPerUserBuffer - bp->framesInTempOutputBuffer);
+
+ srcSampleStrideSamples = bp->outputChannelCount;
+ srcChannelStrideBytes = bp->bytesPerUserOutputSample;
+ }
+ else /* user output is not interleaved */
+ {
+ srcBytePtr = ((unsigned char*)bp->tempOutputBuffer) +
+ bp->bytesPerUserOutputSample *
+ (bp->framesPerUserBuffer - bp->framesInTempOutputBuffer);
+
+ srcSampleStrideSamples = 1;
+ srcChannelStrideBytes = bp->framesPerUserBuffer * bp->bytesPerUserOutputSample;
+ }
+
+ for( i=0; i<bp->outputChannelCount; ++i )
+ {
+ bp->outputConverter( hostOutputChannels[i].data,
+ hostOutputChannels[i].stride,
+ srcBytePtr, srcSampleStrideSamples,
+ frameCount, &bp->ditherGenerator );
+
+ srcBytePtr += srcChannelStrideBytes; /* skip to next source channel */
+
+ /* advance dest ptr for next iteration */
+ hostOutputChannels[i].data = ((unsigned char*)hostOutputChannels[i].data) +
+ frameCount * hostOutputChannels[i].stride * bp->bytesPerHostOutputSample;
+ }
+
+ if( bp->hostOutputFrameCount[0] > 0 )
+ bp->hostOutputFrameCount[0] -= frameCount;
+ else
+ bp->hostOutputFrameCount[1] -= frameCount;
+
+ bp->framesInTempOutputBuffer -= frameCount;
+ }
+}
+
+/*
+ AdaptingProcess is a full duplex adapting buffer processor. It converts
+ data from the temporary output buffer into the host output buffers, then
+ from the host input buffers into the temporary input buffers. Calling the
+ streamCallback when necessary.
+ When processPartialUserBuffers is 0, all available input data will be
+ consumed and all available output space will be filled. When
+ processPartialUserBuffers is non-zero, as many full user buffers
+ as possible will be processed, but partial buffers will not be consumed.
+*/
+static unsigned long AdaptingProcess( PaUtilBufferProcessor *bp,
+ int *streamCallbackResult, int processPartialUserBuffers )
+{
+ void *userInput, *userOutput;
+ unsigned long framesProcessed = 0;
+ unsigned long framesAvailable;
+ unsigned long endProcessingMinFrameCount;
+ unsigned long maxFramesToCopy;
+ PaUtilChannelDescriptor *hostInputChannels, *hostOutputChannels;
+ unsigned int frameCount;
+ unsigned char *destBytePtr;
+ unsigned int destSampleStrideSamples; /* stride from one sample to the next within a channel, in samples */
+ unsigned int destChannelStrideBytes; /* stride from one channel to the next, in bytes */
+ unsigned int i, j;
+
+
+ framesAvailable = bp->hostInputFrameCount[0] + bp->hostInputFrameCount[1];/* this is assumed to be the same as the output buffer's frame count */
+
+ if( processPartialUserBuffers )
+ endProcessingMinFrameCount = 0;
+ else
+ endProcessingMinFrameCount = (bp->framesPerUserBuffer - 1);
+
+ /* Fill host output with remaining frames in user output (tempOutputBuffer) */
+ CopyTempOutputBuffersToHostOutputBuffers( bp );
+
+ while( framesAvailable > endProcessingMinFrameCount )
+ {
+
+ if( bp->framesInTempOutputBuffer == 0 && *streamCallbackResult != paContinue )
+ {
+ /* the callback will not be called any more, so zero what remains
+ of the host output buffers */
+
+ for( i=0; i<2; ++i )
+ {
+ frameCount = bp->hostOutputFrameCount[i];
+ if( frameCount > 0 )
+ {
+ hostOutputChannels = bp->hostOutputChannels[i];
+
+ for( j=0; j<bp->outputChannelCount; ++j )
+ {
+ bp->outputZeroer( hostOutputChannels[j].data,
+ hostOutputChannels[j].stride,
+ frameCount );
+
+ /* advance dest ptr for next iteration */
+ hostOutputChannels[j].data = ((unsigned char*)hostOutputChannels[j].data) +
+ frameCount * hostOutputChannels[j].stride * bp->bytesPerHostOutputSample;
+ }
+ bp->hostOutputFrameCount[i] = 0;
+ }
+ }
+ }
+
+
+ /* copy frames from host to user input buffers */
+ while( bp->framesInTempInputBuffer < bp->framesPerUserBuffer &&
+ ((bp->hostInputFrameCount[0] + bp->hostInputFrameCount[1]) > 0) )
+ {
+ maxFramesToCopy = bp->framesPerUserBuffer - bp->framesInTempInputBuffer;
+
+ /* select the input buffer set (1st or 2nd) */
+ if( bp->hostInputFrameCount[0] > 0 )
+ {
+ hostInputChannels = bp->hostInputChannels[0];
+ frameCount = PA_MIN_( bp->hostInputFrameCount[0], maxFramesToCopy );
+ }
+ else
+ {
+ hostInputChannels = bp->hostInputChannels[1];
+ frameCount = PA_MIN_( bp->hostInputFrameCount[1], maxFramesToCopy );
+ }
+
+ /* configure conversion destination pointers */
+ if( bp->userInputIsInterleaved )
+ {
+ destBytePtr = ((unsigned char*)bp->tempInputBuffer) +
+ bp->bytesPerUserInputSample * bp->inputChannelCount *
+ bp->framesInTempInputBuffer;
+
+ destSampleStrideSamples = bp->inputChannelCount;
+ destChannelStrideBytes = bp->bytesPerUserInputSample;
+ }
+ else /* user input is not interleaved */
+ {
+ destBytePtr = ((unsigned char*)bp->tempInputBuffer) +
+ bp->bytesPerUserInputSample * bp->framesInTempInputBuffer;
+
+ destSampleStrideSamples = 1;
+ destChannelStrideBytes = bp->framesPerUserBuffer * bp->bytesPerUserInputSample;
+ }
+
+ for( i=0; i<bp->inputChannelCount; ++i )
+ {
+ bp->inputConverter( destBytePtr, destSampleStrideSamples,
+ hostInputChannels[i].data,
+ hostInputChannels[i].stride,
+ frameCount, &bp->ditherGenerator );
+
+ destBytePtr += destChannelStrideBytes; /* skip to next destination channel */
+
+ /* advance src ptr for next iteration */
+ hostInputChannels[i].data = ((unsigned char*)hostInputChannels[i].data) +
+ frameCount * hostInputChannels[i].stride * bp->bytesPerHostInputSample;
+ }
+
+ if( bp->hostInputFrameCount[0] > 0 )
+ bp->hostInputFrameCount[0] -= frameCount;
+ else
+ bp->hostInputFrameCount[1] -= frameCount;
+
+ bp->framesInTempInputBuffer += frameCount;
+
+ /* update framesAvailable and framesProcessed based on input consumed
+ unless something is very wrong this will also correspond to the
+ amount of output generated */
+ framesAvailable -= frameCount;
+ framesProcessed += frameCount;
+ }
+
+ /* call streamCallback */
+ if( bp->framesInTempInputBuffer == bp->framesPerUserBuffer &&
+ bp->framesInTempOutputBuffer == 0 )
+ {
+ if( *streamCallbackResult == paContinue )
+ {
+ /* setup userInput */
+ if( bp->userInputIsInterleaved )
+ {
+ userInput = bp->tempInputBuffer;
+ }
+ else /* user input is not interleaved */
+ {
+ for( i = 0; i < bp->inputChannelCount; ++i )
+ {
+ bp->tempInputBufferPtrs[i] = ((unsigned char*)bp->tempInputBuffer) +
+ i * bp->framesPerUserBuffer * bp->bytesPerUserInputSample;
+ }
+
+ userInput = bp->tempInputBufferPtrs;
+ }
+
+ /* setup userOutput */
+ if( bp->userOutputIsInterleaved )
+ {
+ userOutput = bp->tempOutputBuffer;
+ }
+ else /* user output is not interleaved */
+ {
+ for( i = 0; i < bp->outputChannelCount; ++i )
+ {
+ bp->tempOutputBufferPtrs[i] = ((unsigned char*)bp->tempOutputBuffer) +
+ i * bp->framesPerUserBuffer * bp->bytesPerUserOutputSample;
+ }
+
+ userOutput = bp->tempOutputBufferPtrs;
+ }
+
+ /* call streamCallback */
+
+ *streamCallbackResult = bp->streamCallback( userInput, userOutput,
+ bp->framesPerUserBuffer, bp->timeInfo,
+ bp->callbackStatusFlags, bp->userData );
+
+ bp->timeInfo->inputBufferAdcTime += bp->framesPerUserBuffer * bp->samplePeriod;
+ bp->timeInfo->outputBufferDacTime += bp->framesPerUserBuffer * bp->samplePeriod;
+
+ bp->framesInTempInputBuffer = 0;
+
+ if( *streamCallbackResult == paAbort )
+ bp->framesInTempOutputBuffer = 0;
+ else
+ bp->framesInTempOutputBuffer = bp->framesPerUserBuffer;
+ }
+ else
+ {
+ /* paComplete or paAbort has already been called. */
+
+ bp->framesInTempInputBuffer = 0;
+ }
+ }
+
+ /* copy frames from user (tempOutputBuffer) to host output buffers (hostOutputChannels)
+ Means to process the user output provided by the callback. Has to be called after
+ each callback. */
+ CopyTempOutputBuffersToHostOutputBuffers( bp );
+
+ }
+
+ return framesProcessed;
+}
+
+
+unsigned long PaUtil_EndBufferProcessing( PaUtilBufferProcessor* bp, int *streamCallbackResult )
+{
+ unsigned long framesToProcess, framesToGo;
+ unsigned long framesProcessed = 0;
+
+ if( bp->inputChannelCount != 0 && bp->outputChannelCount != 0
+ && bp->hostInputChannels[0][0].data /* input was supplied (see PaUtil_SetNoInput) */
+ && bp->hostOutputChannels[0][0].data /* output was supplied (see PaUtil_SetNoOutput) */ )
+ {
+ assert( (bp->hostInputFrameCount[0] + bp->hostInputFrameCount[1]) ==
+ (bp->hostOutputFrameCount[0] + bp->hostOutputFrameCount[1]) );
+ }
+
+ assert( *streamCallbackResult == paContinue
+ || *streamCallbackResult == paComplete
+ || *streamCallbackResult == paAbort ); /* don't forget to pass in a valid callback result value */
+
+ if( bp->useNonAdaptingProcess )
+ {
+ if( bp->inputChannelCount != 0 && bp->outputChannelCount != 0 )
+ {
+ /* full duplex non-adapting process, splice buffers if they are
+ different lengths */
+
+ framesToGo = bp->hostOutputFrameCount[0] + bp->hostOutputFrameCount[1]; /* relies on assert above for input/output equivalence */
+
+ do{
+ unsigned long noInputInputFrameCount;
+ unsigned long *hostInputFrameCount;
+ PaUtilChannelDescriptor *hostInputChannels;
+ unsigned long noOutputOutputFrameCount;
+ unsigned long *hostOutputFrameCount;
+ PaUtilChannelDescriptor *hostOutputChannels;
+ unsigned long framesProcessedThisIteration;
+
+ if( !bp->hostInputChannels[0][0].data )
+ {
+ /* no input was supplied (see PaUtil_SetNoInput)
+ NonAdaptingProcess knows how to deal with this
+ */
+ noInputInputFrameCount = framesToGo;
+ hostInputFrameCount = &noInputInputFrameCount;
+ hostInputChannels = 0;
+ }
+ else if( bp->hostInputFrameCount[0] != 0 )
+ {
+ hostInputFrameCount = &bp->hostInputFrameCount[0];
+ hostInputChannels = bp->hostInputChannels[0];
+ }
+ else
+ {
+ hostInputFrameCount = &bp->hostInputFrameCount[1];
+ hostInputChannels = bp->hostInputChannels[1];
+ }
+
+ if( !bp->hostOutputChannels[0][0].data )
+ {
+ /* no output was supplied (see PaUtil_SetNoOutput)
+ NonAdaptingProcess knows how to deal with this
+ */
+ noOutputOutputFrameCount = framesToGo;
+ hostOutputFrameCount = &noOutputOutputFrameCount;
+ hostOutputChannels = 0;
+ }
+ if( bp->hostOutputFrameCount[0] != 0 )
+ {
+ hostOutputFrameCount = &bp->hostOutputFrameCount[0];
+ hostOutputChannels = bp->hostOutputChannels[0];
+ }
+ else
+ {
+ hostOutputFrameCount = &bp->hostOutputFrameCount[1];
+ hostOutputChannels = bp->hostOutputChannels[1];
+ }
+
+ framesToProcess = PA_MIN_( *hostInputFrameCount,
+ *hostOutputFrameCount );
+
+ assert( framesToProcess != 0 );
+
+ framesProcessedThisIteration = NonAdaptingProcess( bp, streamCallbackResult,
+ hostInputChannels, hostOutputChannels,
+ framesToProcess );
+
+ *hostInputFrameCount -= framesProcessedThisIteration;
+ *hostOutputFrameCount -= framesProcessedThisIteration;
+
+ framesProcessed += framesProcessedThisIteration;
+ framesToGo -= framesProcessedThisIteration;
+
+ }while( framesToGo > 0 );
+ }
+ else
+ {
+ /* half duplex non-adapting process, just process 1st and 2nd buffer */
+ /* process first buffer */
+
+ framesToProcess = (bp->inputChannelCount != 0)
+ ? bp->hostInputFrameCount[0]
+ : bp->hostOutputFrameCount[0];
+
+ framesProcessed = NonAdaptingProcess( bp, streamCallbackResult,
+ bp->hostInputChannels[0], bp->hostOutputChannels[0],
+ framesToProcess );
+
+ /* process second buffer if provided */
+
+ framesToProcess = (bp->inputChannelCount != 0)
+ ? bp->hostInputFrameCount[1]
+ : bp->hostOutputFrameCount[1];
+ if( framesToProcess > 0 )
+ {
+ framesProcessed += NonAdaptingProcess( bp, streamCallbackResult,
+ bp->hostInputChannels[1], bp->hostOutputChannels[1],
+ framesToProcess );
+ }
+ }
+ }
+ else /* block adaption necessary*/
+ {
+
+ if( bp->inputChannelCount != 0 && bp->outputChannelCount != 0 )
+ {
+ /* full duplex */
+
+ if( bp->hostBufferSizeMode == paUtilVariableHostBufferSizePartialUsageAllowed )
+ {
+ framesProcessed = AdaptingProcess( bp, streamCallbackResult,
+ 0 /* dont process partial user buffers */ );
+ }
+ else
+ {
+ framesProcessed = AdaptingProcess( bp, streamCallbackResult,
+ 1 /* process partial user buffers */ );
+ }
+ }
+ else if( bp->inputChannelCount != 0 )
+ {
+ /* input only */
+ framesToProcess = bp->hostInputFrameCount[0];
+
+ framesProcessed = AdaptingInputOnlyProcess( bp, streamCallbackResult,
+ bp->hostInputChannels[0], framesToProcess );
+
+ framesToProcess = bp->hostInputFrameCount[1];
+ if( framesToProcess > 0 )
+ {
+ framesProcessed += AdaptingInputOnlyProcess( bp, streamCallbackResult,
+ bp->hostInputChannels[1], framesToProcess );
+ }
+ }
+ else
+ {
+ /* output only */
+ framesToProcess = bp->hostOutputFrameCount[0];
+
+ framesProcessed = AdaptingOutputOnlyProcess( bp, streamCallbackResult,
+ bp->hostOutputChannels[0], framesToProcess );
+
+ framesToProcess = bp->hostOutputFrameCount[1];
+ if( framesToProcess > 0 )
+ {
+ framesProcessed += AdaptingOutputOnlyProcess( bp, streamCallbackResult,
+ bp->hostOutputChannels[1], framesToProcess );
+ }
+ }
+ }
+
+ return framesProcessed;
+}
+
+
+int PaUtil_IsBufferProcessorOutputEmpty( PaUtilBufferProcessor* bp )
+{
+ return (bp->framesInTempOutputBuffer) ? 0 : 1;
+}
+
+
+unsigned long PaUtil_CopyInput( PaUtilBufferProcessor* bp,
+ void **buffer, unsigned long frameCount )
+{
+ PaUtilChannelDescriptor *hostInputChannels;
+ unsigned int framesToCopy;
+ unsigned char *destBytePtr;
+ void **nonInterleavedDestPtrs;
+ unsigned int destSampleStrideSamples; /* stride from one sample to the next within a channel, in samples */
+ unsigned int destChannelStrideBytes; /* stride from one channel to the next, in bytes */
+ unsigned int i;
+
+ hostInputChannels = bp->hostInputChannels[0];
+ framesToCopy = PA_MIN_( bp->hostInputFrameCount[0], frameCount );
+
+ if( bp->userInputIsInterleaved )
+ {
+ destBytePtr = (unsigned char*)*buffer;
+
+ destSampleStrideSamples = bp->inputChannelCount;
+ destChannelStrideBytes = bp->bytesPerUserInputSample;
+
+ for( i=0; i<bp->inputChannelCount; ++i )
+ {
+ bp->inputConverter( destBytePtr, destSampleStrideSamples,
+ hostInputChannels[i].data,
+ hostInputChannels[i].stride,
+ framesToCopy, &bp->ditherGenerator );
+
+ destBytePtr += destChannelStrideBytes; /* skip to next source channel */
+
+ /* advance dest ptr for next iteration */
+ hostInputChannels[i].data = ((unsigned char*)hostInputChannels[i].data) +
+ framesToCopy * hostInputChannels[i].stride * bp->bytesPerHostInputSample;
+ }
+
+ /* advance callers dest pointer (buffer) */
+ *buffer = ((unsigned char *)*buffer) +
+ framesToCopy * bp->inputChannelCount * bp->bytesPerUserInputSample;
+ }
+ else
+ {
+ /* user input is not interleaved */
+
+ nonInterleavedDestPtrs = (void**)*buffer;
+
+ destSampleStrideSamples = 1;
+
+ for( i=0; i<bp->inputChannelCount; ++i )
+ {
+ destBytePtr = (unsigned char*)nonInterleavedDestPtrs[i];
+
+ bp->inputConverter( destBytePtr, destSampleStrideSamples,
+ hostInputChannels[i].data,
+ hostInputChannels[i].stride,
+ framesToCopy, &bp->ditherGenerator );
+
+ /* advance callers dest pointer (nonInterleavedDestPtrs[i]) */
+ destBytePtr += bp->bytesPerUserInputSample * framesToCopy;
+ nonInterleavedDestPtrs[i] = destBytePtr;
+
+ /* advance dest ptr for next iteration */
+ hostInputChannels[i].data = ((unsigned char*)hostInputChannels[i].data) +
+ framesToCopy * hostInputChannels[i].stride * bp->bytesPerHostInputSample;
+ }
+ }
+
+ bp->hostInputFrameCount[0] -= framesToCopy;
+
+ return framesToCopy;
+}
+
+unsigned long PaUtil_CopyOutput( PaUtilBufferProcessor* bp,
+ const void ** buffer, unsigned long frameCount )
+{
+ PaUtilChannelDescriptor *hostOutputChannels;
+ unsigned int framesToCopy;
+ unsigned char *srcBytePtr;
+ void **nonInterleavedSrcPtrs;
+ unsigned int srcSampleStrideSamples; /* stride from one sample to the next within a channel, in samples */
+ unsigned int srcChannelStrideBytes; /* stride from one channel to the next, in bytes */
+ unsigned int i;
+
+ hostOutputChannels = bp->hostOutputChannels[0];
+ framesToCopy = PA_MIN_( bp->hostOutputFrameCount[0], frameCount );
+
+ if( bp->userOutputIsInterleaved )
+ {
+ srcBytePtr = (unsigned char*)*buffer;
+
+ srcSampleStrideSamples = bp->outputChannelCount;
+ srcChannelStrideBytes = bp->bytesPerUserOutputSample;
+
+ for( i=0; i<bp->outputChannelCount; ++i )
+ {
+ bp->outputConverter( hostOutputChannels[i].data,
+ hostOutputChannels[i].stride,
+ srcBytePtr, srcSampleStrideSamples,
+ framesToCopy, &bp->ditherGenerator );
+
+ srcBytePtr += srcChannelStrideBytes; /* skip to next source channel */
+
+ /* advance dest ptr for next iteration */
+ hostOutputChannels[i].data = ((unsigned char*)hostOutputChannels[i].data) +
+ framesToCopy * hostOutputChannels[i].stride * bp->bytesPerHostOutputSample;
+ }
+
+ /* advance callers source pointer (buffer) */
+ *buffer = ((unsigned char *)*buffer) +
+ framesToCopy * bp->outputChannelCount * bp->bytesPerUserOutputSample;
+
+ }
+ else
+ {
+ /* user output is not interleaved */
+
+ nonInterleavedSrcPtrs = (void**)*buffer;
+
+ srcSampleStrideSamples = 1;
+
+ for( i=0; i<bp->outputChannelCount; ++i )
+ {
+ srcBytePtr = (unsigned char*)nonInterleavedSrcPtrs[i];
+
+ bp->outputConverter( hostOutputChannels[i].data,
+ hostOutputChannels[i].stride,
+ srcBytePtr, srcSampleStrideSamples,
+ framesToCopy, &bp->ditherGenerator );
+
+
+ /* advance callers source pointer (nonInterleavedSrcPtrs[i]) */
+ srcBytePtr += bp->bytesPerUserOutputSample * framesToCopy;
+ nonInterleavedSrcPtrs[i] = srcBytePtr;
+
+ /* advance dest ptr for next iteration */
+ hostOutputChannels[i].data = ((unsigned char*)hostOutputChannels[i].data) +
+ framesToCopy * hostOutputChannels[i].stride * bp->bytesPerHostOutputSample;
+ }
+ }
+
+ bp->hostOutputFrameCount[0] += framesToCopy;
+
+ return framesToCopy;
+}
+
+
+unsigned long PaUtil_ZeroOutput( PaUtilBufferProcessor* bp, unsigned long frameCount )
+{
+ PaUtilChannelDescriptor *hostOutputChannels;
+ unsigned int framesToZero;
+ unsigned int i;
+
+ hostOutputChannels = bp->hostOutputChannels[0];
+ framesToZero = PA_MIN_( bp->hostOutputFrameCount[0], frameCount );
+
+ for( i=0; i<bp->outputChannelCount; ++i )
+ {
+ bp->outputZeroer( hostOutputChannels[i].data,
+ hostOutputChannels[i].stride,
+ framesToZero );
+
+
+ /* advance dest ptr for next iteration */
+ hostOutputChannels[i].data = ((unsigned char*)hostOutputChannels[i].data) +
+ framesToZero * hostOutputChannels[i].stride * bp->bytesPerHostOutputSample;
+ }
+
+ bp->hostOutputFrameCount[0] += framesToZero;
+
+ return framesToZero;
+}
diff --git a/pjmedia/src/pjmedia/portaudio/pa_process.h b/pjmedia/src/pjmedia/portaudio/pa_process.h
index 8ebfbada..c52e9ea0 100644
--- a/pjmedia/src/pjmedia/portaudio/pa_process.h
+++ b/pjmedia/src/pjmedia/portaudio/pa_process.h
@@ -1,741 +1,741 @@
-#ifndef PA_PROCESS_H
-#define PA_PROCESS_H
-/*
- * $Id: pa_process.h,v 1.1.2.30 2004/12/13 09:48:44 rossbencina Exp $
- * Portable Audio I/O Library callback buffer processing adapters
- *
- * Based on the Open Source API proposed by Ross Bencina
- * Copyright (c) 1999-2002 Phil Burk, Ross Bencina
- *
- * 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.
- *
- * Any person wishing to distribute modifications to the Software is
- * requested to send the modifications to the original developer so that
- * they can be incorporated into the canonical version.
- *
- * 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.
- */
-
-/** @file
- @brief Buffer Processor prototypes. A Buffer Processor performs buffer length
- adaption, coordinates sample format conversion, and interleaves/deinterleaves
- channels.
-
- <h3>Overview</h3>
-
- The "Buffer Processor" (PaUtilBufferProcessor) manages conversion of audio
- data from host buffers to user buffers and back again. Where required, the
- buffer processor takes care of converting between host and user sample formats,
- interleaving and deinterleaving multichannel buffers, and adapting between host
- and user buffers with different lengths. The buffer processor may be used with
- full and half duplex streams, for both callback streams and blocking read/write
- streams.
-
- One of the important capabilities provided by the buffer processor is
- the ability to adapt between user and host buffer sizes of different lengths
- with minimum latency. Although this task is relatively easy to perform when
- the host buffer size is an integer multiple of the user buffer size, the
- problem is more complicated when this is not the case - especially for
- full-duplex callback streams. Where necessary the adaption is implemented by
- internally buffering some input and/or output data. The buffer adation
- algorithm used by the buffer processor was originally implemented by
- Stephan Letz for the ASIO version of PortAudio, and is described in his
- Callback_adaption_.pdf which is included in the distribution.
-
- The buffer processor performs sample conversion using the functions provided
- by pa_converters.c.
-
- The following sections provide an overview of how to use the buffer processor.
- Interested readers are advised to consult the host API implementations for
- examples of buffer processor usage.
-
-
- <h4>Initialization, resetting and termination</h4>
-
- When a stream is opened, the buffer processor should be initialized using
- PaUtil_InitializeBufferProcessor. This function initializes internal state
- and allocates temporary buffers as neccesary according to the supplied
- configuration parameters. Some of the parameters correspond to those requested
- by the user in their call to Pa_OpenStream(), others reflect the requirements
- of the host API implementation - they indicate host buffer sizes, formats,
- and the type of buffering which the Host API uses. The buffer processor should
- be initialized for callback streams and blocking read/write streams.
-
- Call PaUtil_ResetBufferProcessor to clear any sample data which is present
- in the buffer processor before starting to use it (for example when
- Pa_StartStream is called).
-
- When the buffer processor is no longer used call
- PaUtil_TerminateBufferProcessor.
-
-
- <h4>Using the buffer processor for a callback stream</h4>
-
- The buffer processor's role in a callback stream is to take host input buffers
- process them with the stream callback, and fill host output buffers. For a
- full duplex stream, the buffer processor handles input and output simultaneously
- due to the requirements of the minimum-latency buffer adation algorithm.
-
- When a host buffer becomes available, the implementation should call
- the buffer processor to process the buffer. The buffer processor calls the
- stream callback to consume and/or produce audio data as necessary. The buffer
- processor will convert sample formats, interleave/deinterleave channels,
- and slice or chunk the data to the appropriate buffer lengths according to
- the requirements of the stream callback and the host API.
-
- To process a host buffer (or a pair of host buffers for a full-duplex stream)
- use the following calling sequence:
-
- -# Call PaUtil_BeginBufferProcessing
- -# For a stream which takes input:
- - Call PaUtil_SetInputFrameCount with the number of frames in the host input
- buffer.
- - Call one of the following functions one or more times to tell the
- buffer processor about the host input buffer(s): PaUtil_SetInputChannel,
- PaUtil_SetInterleavedInputChannels, PaUtil_SetNonInterleavedInputChannel.
- Which function you call will depend on whether the host buffer(s) are
- interleaved or not.
- - If the available host data is split accross two buffers (for example a
- data range at the end of a circular buffer and another range at the
- beginning of the circular buffer), also call
- PaUtil_Set2ndInputFrameCount, PaUtil_Set2ndInputChannel,
- PaUtil_Set2ndInterleavedInputChannels,
- PaUtil_Set2ndNonInterleavedInputChannel as necessary to tell the buffer
- processor about the second buffer.
- -# For a stream which generates output:
- - Call PaUtil_SetOutputFrameCount with the number of frames in the host
- output buffer.
- - Call one of the following functions one or more times to tell the
- buffer processor about the host output buffer(s): PaUtil_SetOutputChannel,
- PaUtil_SetInterleavedOutputChannels, PaUtil_SetNonInterleavedOutputChannel.
- Which function you call will depend on whether the host buffer(s) are
- interleaved or not.
- - If the available host output buffer space is split accross two buffers
- (for example a data range at the end of a circular buffer and another
- range at the beginning of the circular buffer), call
- PaUtil_Set2ndOutputFrameCount, PaUtil_Set2ndOutputChannel,
- PaUtil_Set2ndInterleavedOutputChannels,
- PaUtil_Set2ndNonInterleavedOutputChannel as necessary to tell the buffer
- processor about the second buffer.
- -# Call PaUtil_EndBufferProcessing, this function performs the actual data
- conversion and processing.
-
-
- <h4>Using the buffer processor for a blocking read/write stream</h4>
-
- Blocking read/write streams use the buffer processor to convert and copy user
- output data to a host buffer, and to convert and copy host input data to
- the user's buffer. The buffer processor does not perform any buffer adaption.
- When using the buffer processor in a blocking read/write stream the input and
- output conversion are performed separately by the PaUtil_CopyInput and
- PaUtil_CopyOutput functions.
-
- To copy data from a host input buffer to the buffer(s) which the user supplies
- to Pa_ReadStream, use the following calling sequence.
-
- - Repeat the following three steps until the user buffer(s) have been filled
- with samples from the host input buffers:
- -# Call PaUtil_SetInputFrameCount with the number of frames in the host
- input buffer.
- -# Call one of the following functions one or more times to tell the
- buffer processor about the host input buffer(s): PaUtil_SetInputChannel,
- PaUtil_SetInterleavedInputChannels, PaUtil_SetNonInterleavedInputChannel.
- Which function you call will depend on whether the host buffer(s) are
- interleaved or not.
- -# Call PaUtil_CopyInput with the user buffer pointer (or a copy of the
- array of buffer pointers for a non-interleaved stream) passed to
- Pa_ReadStream, along with the number of frames in the user buffer(s).
- Be careful to pass a <i>copy</i> of the user buffer pointers to
- PaUtil_CopyInput because PaUtil_CopyInput advances the pointers to
- the start of the next region to copy.
- - PaUtil_CopyInput will not copy more data than is available in the
- host buffer(s), so the above steps need to be repeated until the user
- buffer(s) are full.
-
-
- To copy data to the host output buffer from the user buffers(s) supplied
- to Pa_WriteStream use the following calling sequence.
-
- - Repeat the following three steps until all frames from the user buffer(s)
- have been copied to the host API:
- -# Call PaUtil_SetOutputFrameCount with the number of frames in the host
- output buffer.
- -# Call one of the following functions one or more times to tell the
- buffer processor about the host output buffer(s): PaUtil_SetOutputChannel,
- PaUtil_SetInterleavedOutputChannels, PaUtil_SetNonInterleavedOutputChannel.
- Which function you call will depend on whether the host buffer(s) are
- interleaved or not.
- -# Call PaUtil_CopyOutput with the user buffer pointer (or a copy of the
- array of buffer pointers for a non-interleaved stream) passed to
- Pa_WriteStream, along with the number of frames in the user buffer(s).
- Be careful to pass a <i>copy</i> of the user buffer pointers to
- PaUtil_CopyOutput because PaUtil_CopyOutput advances the pointers to
- the start of the next region to copy.
- - PaUtil_CopyOutput will not copy more data than fits in the host buffer(s),
- so the above steps need to be repeated until all user data is copied.
-*/
-
-
-#include "portaudio.h"
-#include "pa_converters.h"
-#include "pa_dither.h"
-
-#ifdef __cplusplus
-extern "C"
-{
-#endif /* __cplusplus */
-
-
-/** @brief Mode flag passed to PaUtil_InitializeBufferProcessor indicating the type
- of buffering that the host API uses.
-
- The mode used depends on whether the host API or the implementation manages
- the buffers, and how these buffers are used (scatter gather, circular buffer).
-*/
-typedef enum {
-/** The host buffer size is a fixed known size. */
- paUtilFixedHostBufferSize,
-
-/** The host buffer size may vary, but has a known maximum size. */
- paUtilBoundedHostBufferSize,
-
-/** Nothing is known about the host buffer size. */
- paUtilUnknownHostBufferSize,
-
-/** The host buffer size varies, and the client does not require the buffer
- processor to consume all of the input and fill all of the output buffer. This
- is useful when the implementation has access to the host API's circular buffer
- and only needs to consume/fill some of it, not necessarily all of it, with each
- call to the buffer processor. This is the only mode where
- PaUtil_EndBufferProcessing() may not consume the whole buffer.
-*/
- paUtilVariableHostBufferSizePartialUsageAllowed
-}PaUtilHostBufferSizeMode;
-
-
-/** @brief An auxilliary data structure used internally by the buffer processor
- to represent host input and output buffers. */
-typedef struct PaUtilChannelDescriptor{
- void *data;
- unsigned int stride; /**< stride in samples, not bytes */
-}PaUtilChannelDescriptor;
-
-
-/** @brief The main buffer processor data structure.
-
- Allocate one of these, initialize it with PaUtil_InitializeBufferProcessor
- and terminate it with PaUtil_TerminateBufferProcessor.
-*/
-typedef struct {
- unsigned long framesPerUserBuffer;
- unsigned long framesPerHostBuffer;
-
- PaUtilHostBufferSizeMode hostBufferSizeMode;
- int useNonAdaptingProcess;
- unsigned long framesPerTempBuffer;
-
- unsigned int inputChannelCount;
- unsigned int bytesPerHostInputSample;
- unsigned int bytesPerUserInputSample;
- int userInputIsInterleaved;
- PaUtilConverter *inputConverter;
- PaUtilZeroer *inputZeroer;
-
- unsigned int outputChannelCount;
- unsigned int bytesPerHostOutputSample;
- unsigned int bytesPerUserOutputSample;
- int userOutputIsInterleaved;
- PaUtilConverter *outputConverter;
- PaUtilZeroer *outputZeroer;
-
- unsigned long initialFramesInTempInputBuffer;
- unsigned long initialFramesInTempOutputBuffer;
-
- void *tempInputBuffer; /**< used for slips, block adaption, and conversion. */
- void **tempInputBufferPtrs; /**< storage for non-interleaved buffer pointers, NULL for interleaved user input */
- unsigned long framesInTempInputBuffer; /**< frames remaining in input buffer from previous adaption iteration */
-
- void *tempOutputBuffer; /**< used for slips, block adaption, and conversion. */
- void **tempOutputBufferPtrs; /**< storage for non-interleaved buffer pointers, NULL for interleaved user output */
- unsigned long framesInTempOutputBuffer; /**< frames remaining in input buffer from previous adaption iteration */
-
- PaStreamCallbackTimeInfo *timeInfo;
-
- PaStreamCallbackFlags callbackStatusFlags;
-
- unsigned long hostInputFrameCount[2];
- PaUtilChannelDescriptor *hostInputChannels[2]; /**< pointers to arrays of channel descriptors.
- pointers are NULL for half-duplex output processing.
- hostInputChannels[i].data is NULL when the caller
- calls PaUtil_SetNoInput()
- */
- unsigned long hostOutputFrameCount[2];
- PaUtilChannelDescriptor *hostOutputChannels[2]; /**< pointers to arrays of channel descriptors.
- pointers are NULL for half-duplex input processing.
- hostOutputChannels[i].data is NULL when the caller
- calls PaUtil_SetNoOutput()
- */
-
- PaUtilTriangularDitherGenerator ditherGenerator;
-
- double samplePeriod;
-
- PaStreamCallback *streamCallback;
- void *userData;
-} PaUtilBufferProcessor;
-
-
-/** @name Initialization, termination, resetting and info */
-/*@{*/
-
-/** Initialize a buffer processor's representation stored in a
- PaUtilBufferProcessor structure. Be sure to call
- PaUtil_TerminateBufferProcessor after finishing with a buffer processor.
-
- @param bufferProcessor The buffer processor structure to initialize.
-
- @param inputChannelCount The number of input channels as passed to
- Pa_OpenStream or 0 for an output-only stream.
-
- @param userInputSampleFormat Format of user input samples, as passed to
- Pa_OpenStream. This parameter is ignored for ouput-only streams.
-
- @param hostInputSampleFormat Format of host input samples. This parameter is
- ignored for output-only streams. See note about host buffer interleave below.
-
- @param outputChannelCount The number of output channels as passed to
- Pa_OpenStream or 0 for an input-only stream.
-
- @param userOutputSampleFormat Format of user output samples, as passed to
- Pa_OpenStream. This parameter is ignored for input-only streams.
-
- @param hostOutputSampleFormat Format of host output samples. This parameter is
- ignored for input-only streams. See note about host buffer interleave below.
-
- @param sampleRate Sample rate of the stream. The more accurate this is the
- better - it is used for updating time stamps when adapting buffers.
-
- @param streamFlags Stream flags as passed to Pa_OpenStream, this parameter is
- used for selecting special sample conversion options such as clipping and
- dithering.
-
- @param framesPerUserBuffer Number of frames per user buffer, as requested
- by the framesPerBuffer parameter to Pa_OpenStream. This parameter may be
- zero to indicate that the user will accept any (and varying) buffer sizes.
-
- @param framesPerHostBuffer Specifies the number of frames per host buffer
- for the fixed buffer size mode, and the maximum number of frames
- per host buffer for the bounded host buffer size mode. It is ignored for
- the other modes.
-
- @param hostBufferSizeMode A mode flag indicating the size variability of
- host buffers that will be passed to the buffer processor. See
- PaUtilHostBufferSizeMode for further details.
-
- @param streamCallback The user stream callback passed to Pa_OpenStream.
-
- @param userData The user data field passed to Pa_OpenStream.
-
- @note The interleave flag is ignored for host buffer formats. Host
- interleave is determined by the use of different SetInput and SetOutput
- functions.
-
- @return An error code indicating whether the initialization was successful.
- If the error code is not PaNoError, the buffer processor was not initialized
- and should not be used.
-
- @see Pa_OpenStream, PaUtilHostBufferSizeMode, PaUtil_TerminateBufferProcessor
-*/
-PaError PaUtil_InitializeBufferProcessor( PaUtilBufferProcessor* bufferProcessor,
- int inputChannelCount, PaSampleFormat userInputSampleFormat,
- PaSampleFormat hostInputSampleFormat,
- int outputChannelCount, PaSampleFormat userOutputSampleFormat,
- PaSampleFormat hostOutputSampleFormat,
- double sampleRate,
- PaStreamFlags streamFlags,
- unsigned long framesPerUserBuffer, /* 0 indicates don't care */
- unsigned long framesPerHostBuffer,
- PaUtilHostBufferSizeMode hostBufferSizeMode,
- PaStreamCallback *streamCallback, void *userData );
-
-
-/** Terminate a buffer processor's representation. Deallocates any temporary
- buffers allocated by PaUtil_InitializeBufferProcessor.
-
- @param bufferProcessor The buffer processor structure to terminate.
-
- @see PaUtil_InitializeBufferProcessor.
-*/
-void PaUtil_TerminateBufferProcessor( PaUtilBufferProcessor* bufferProcessor );
-
-
-/** Clear any internally buffered data. If you call
- PaUtil_InitializeBufferProcessor in your OpenStream routine, make sure you
- call PaUtil_ResetBufferProcessor in your StartStream call.
-
- @param bufferProcessor The buffer processor to reset.
-*/
-void PaUtil_ResetBufferProcessor( PaUtilBufferProcessor* bufferProcessor );
-
-
-/** Retrieve the input latency of a buffer processor.
-
- @param bufferProcessor The buffer processor examine.
-
- @return The input latency introduced by the buffer processor, in frames.
-
- @see PaUtil_GetBufferProcessorOutputLatency
-*/
-unsigned long PaUtil_GetBufferProcessorInputLatency( PaUtilBufferProcessor* bufferProcessor );
-
-/** Retrieve the output latency of a buffer processor.
-
- @param bufferProcessor The buffer processor examine.
-
- @return The output latency introduced by the buffer processor, in frames.
-
- @see PaUtil_GetBufferProcessorInputLatency
-*/
-unsigned long PaUtil_GetBufferProcessorOutputLatency( PaUtilBufferProcessor* bufferProcessor );
-
-/*@}*/
-
-
-/** @name Host buffer pointer configuration
-
- Functions to set host input and output buffers, used by both callback streams
- and blocking read/write streams.
-*/
-/*@{*/
-
-
-/** Set the number of frames in the input host buffer(s) specified by the
- PaUtil_Set*InputChannel functions.
-
- @param bufferProcessor The buffer processor.
-
- @param frameCount The number of host input frames. A 0 frameCount indicates to
- use the framesPerHostBuffer value passed to PaUtil_InitializeBufferProcessor.
-
- @see PaUtil_SetNoInput, PaUtil_SetInputChannel,
- PaUtil_SetInterleavedInputChannels, PaUtil_SetNonInterleavedInputChannel
-*/
-void PaUtil_SetInputFrameCount( PaUtilBufferProcessor* bufferProcessor,
- unsigned long frameCount );
-
-
-/** Indicate that no input is avalable. This function should be used when
- priming the output of a full-duplex stream opened with the
- paPrimeOutputBuffersUsingStreamCallback flag. Note that it is not necessary
- to call this or any othe PaUtil_Set*Input* functions for ouput-only streams.
-
- @param bufferProcessor The buffer processor.
-*/
-void PaUtil_SetNoInput( PaUtilBufferProcessor* bufferProcessor );
-
-
-/** Provide the buffer processor with a pointer to a host input channel.
-
- @param bufferProcessor The buffer processor.
- @param channel The channel number.
- @param data The buffer.
- @param stride The stride from one sample to the next, in samples. For
- interleaved host buffers, the stride will usually be the same as the number of
- channels in the buffer.
-*/
-void PaUtil_SetInputChannel( PaUtilBufferProcessor* bufferProcessor,
- unsigned int channel, void *data, unsigned int stride );
-
-
-/** Provide the buffer processor with a pointer to an number of interleaved
- host input channels.
-
- @param bufferProcessor The buffer processor.
- @param firstChannel The first channel number.
- @param data The buffer.
- @param channelCount The number of interleaved channels in the buffer. If
- channelCount is zero, the number of channels specified to
- PaUtil_InitializeBufferProcessor will be used.
-*/
-void PaUtil_SetInterleavedInputChannels( PaUtilBufferProcessor* bufferProcessor,
- unsigned int firstChannel, void *data, unsigned int channelCount );
-
-
-/** Provide the buffer processor with a pointer to one non-interleaved host
- output channel.
-
- @param bufferProcessor The buffer processor.
- @param channel The channel number.
- @param data The buffer.
-*/
-void PaUtil_SetNonInterleavedInputChannel( PaUtilBufferProcessor* bufferProcessor,
- unsigned int channel, void *data );
-
-
-/** Use for the second buffer half when the input buffer is split in two halves.
- @see PaUtil_SetInputFrameCount
-*/
-void PaUtil_Set2ndInputFrameCount( PaUtilBufferProcessor* bufferProcessor,
- unsigned long frameCount );
-
-/** Use for the second buffer half when the input buffer is split in two halves.
- @see PaUtil_SetInputChannel
-*/
-void PaUtil_Set2ndInputChannel( PaUtilBufferProcessor* bufferProcessor,
- unsigned int channel, void *data, unsigned int stride );
-
-/** Use for the second buffer half when the input buffer is split in two halves.
- @see PaUtil_SetInterleavedInputChannels
-*/
-void PaUtil_Set2ndInterleavedInputChannels( PaUtilBufferProcessor* bufferProcessor,
- unsigned int firstChannel, void *data, unsigned int channelCount );
-
-/** Use for the second buffer half when the input buffer is split in two halves.
- @see PaUtil_SetNonInterleavedInputChannel
-*/
-void PaUtil_Set2ndNonInterleavedInputChannel( PaUtilBufferProcessor* bufferProcessor,
- unsigned int channel, void *data );
-
-
-/** Set the number of frames in the output host buffer(s) specified by the
- PaUtil_Set*OutputChannel functions.
-
- @param bufferProcessor The buffer processor.
-
- @param frameCount The number of host output frames. A 0 frameCount indicates to
- use the framesPerHostBuffer value passed to PaUtil_InitializeBufferProcessor.
-
- @see PaUtil_SetOutputChannel, PaUtil_SetInterleavedOutputChannels,
- PaUtil_SetNonInterleavedOutputChannel
-*/
-void PaUtil_SetOutputFrameCount( PaUtilBufferProcessor* bufferProcessor,
- unsigned long frameCount );
-
-
-/** Indicate that the output will be discarded. This function should be used
- when implementing the paNeverDropInput mode for full duplex streams.
-
- @param bufferProcessor The buffer processor.
-*/
-void PaUtil_SetNoOutput( PaUtilBufferProcessor* bufferProcessor );
-
-
-/** Provide the buffer processor with a pointer to a host output channel.
-
- @param bufferProcessor The buffer processor.
- @param channel The channel number.
- @param data The buffer.
- @param stride The stride from one sample to the next, in samples. For
- interleaved host buffers, the stride will usually be the same as the number of
- channels in the buffer.
-*/
-void PaUtil_SetOutputChannel( PaUtilBufferProcessor* bufferProcessor,
- unsigned int channel, void *data, unsigned int stride );
-
-
-/** Provide the buffer processor with a pointer to a number of interleaved
- host output channels.
-
- @param bufferProcessor The buffer processor.
- @param firstChannel The first channel number.
- @param data The buffer.
- @param channelCount The number of interleaved channels in the buffer. If
- channelCount is zero, the number of channels specified to
- PaUtil_InitializeBufferProcessor will be used.
-*/
-void PaUtil_SetInterleavedOutputChannels( PaUtilBufferProcessor* bufferProcessor,
- unsigned int firstChannel, void *data, unsigned int channelCount );
-
-
-/** Provide the buffer processor with a pointer to one non-interleaved host
- output channel.
-
- @param bufferProcessor The buffer processor.
- @param channel The channel number.
- @param data The buffer.
-*/
-void PaUtil_SetNonInterleavedOutputChannel( PaUtilBufferProcessor* bufferProcessor,
- unsigned int channel, void *data );
-
-
-/** Use for the second buffer half when the output buffer is split in two halves.
- @see PaUtil_SetOutputFrameCount
-*/
-void PaUtil_Set2ndOutputFrameCount( PaUtilBufferProcessor* bufferProcessor,
- unsigned long frameCount );
-
-/** Use for the second buffer half when the output buffer is split in two halves.
- @see PaUtil_SetOutputChannel
-*/
-void PaUtil_Set2ndOutputChannel( PaUtilBufferProcessor* bufferProcessor,
- unsigned int channel, void *data, unsigned int stride );
-
-/** Use for the second buffer half when the output buffer is split in two halves.
- @see PaUtil_SetInterleavedOutputChannels
-*/
-void PaUtil_Set2ndInterleavedOutputChannels( PaUtilBufferProcessor* bufferProcessor,
- unsigned int firstChannel, void *data, unsigned int channelCount );
-
-/** Use for the second buffer half when the output buffer is split in two halves.
- @see PaUtil_SetNonInterleavedOutputChannel
-*/
-void PaUtil_Set2ndNonInterleavedOutputChannel( PaUtilBufferProcessor* bufferProcessor,
- unsigned int channel, void *data );
-
-/*@}*/
-
-
-/** @name Buffer processing functions for callback streams
-*/
-/*@{*/
-
-/** Commence processing a host buffer (or a pair of host buffers in the
- full-duplex case) for a callback stream.
-
- @param bufferProcessor The buffer processor.
-
- @param timeInfo Timing information for the first sample of the host
- buffer(s). This information may be adjusted when buffer adaption is being
- performed.
-
- @param callbackStatusFlags Flags indicating whether underruns and overruns
- have occurred since the last time the buffer processor was called.
-*/
-void PaUtil_BeginBufferProcessing( PaUtilBufferProcessor* bufferProcessor,
- PaStreamCallbackTimeInfo* timeInfo, PaStreamCallbackFlags callbackStatusFlags );
-
-
-/** Finish processing a host buffer (or a pair of host buffers in the
- full-duplex case) for a callback stream.
-
- @param bufferProcessor The buffer processor.
-
- @param callbackResult On input, indicates a previous callback result, and on
- exit, the result of the user stream callback, if it is called.
- On entry callbackResult should contain one of { paContinue, paComplete, or
- paAbort}. If paComplete is passed, the stream callback will not be called
- but any audio that was generated by previous stream callbacks will be copied
- to the output buffer(s). You can check whether the buffer processor's internal
- buffer is empty by calling PaUtil_IsBufferProcessorOutputEmpty.
-
- If the stream callback is called its result is stored in *callbackResult. If
- the stream callback returns paComplete or paAbort, all output buffers will be
- full of valid data - some of which may be zeros to acount for data that
- wasn't generated by the terminating callback.
-
- @return The number of frames processed. This usually corresponds to the
- number of frames specified by the PaUtil_Set*FrameCount functions, exept in
- the paUtilVariableHostBufferSizePartialUsageAllowed buffer size mode when a
- smaller value may be returned.
-*/
-unsigned long PaUtil_EndBufferProcessing( PaUtilBufferProcessor* bufferProcessor,
- int *callbackResult );
-
-
-/** Determine whether any callback generated output remains in the bufffer
- processor's internal buffers. This method may be used to determine when to
- continue calling PaUtil_EndBufferProcessing() after the callback has returned
- a callbackResult of paComplete.
-
- @param bufferProcessor The buffer processor.
-
- @return Returns non-zero when callback generated output remains in the internal
- buffer and zero (0) when there internal buffer contains no callback generated
- data.
-*/
-int PaUtil_IsBufferProcessorOutputEmpty( PaUtilBufferProcessor* bufferProcessor );
-
-/*@}*/
-
-
-/** @name Buffer processing functions for blocking read/write streams
-*/
-/*@{*/
-
-/** Copy samples from host input channels set up by the PaUtil_Set*InputChannels
- functions to a user supplied buffer. This function is intended for use with
- blocking read/write streams. Copies the minimum of the number of
- user frames (specified by the frameCount parameter) and the number of available
- host frames (specified in a previous call to SetInputFrameCount()).
-
- @param bufferProcessor The buffer processor.
-
- @param buffer A pointer to the user buffer pointer, or a pointer to a pointer
- to an array of user buffer pointers for a non-interleaved stream. It is
- important that this parameter points to a copy of the user buffer pointers,
- not to the actual user buffer pointers, because this function updates the
- pointers before returning.
-
- @param frameCount The number of frames of data in the buffer(s) pointed to by
- the buffer parameter.
-
- @return The number of frames copied. The buffer pointer(s) pointed to by the
- buffer parameter are advanced to point to the frame(s) following the last one
- filled.
-*/
-unsigned long PaUtil_CopyInput( PaUtilBufferProcessor* bufferProcessor,
- void **buffer, unsigned long frameCount );
-
-
-/* Copy samples from a user supplied buffer to host output channels set up by
- the PaUtil_Set*OutputChannels functions. This function is intended for use with
- blocking read/write streams. Copies the minimum of the number of
- user frames (specified by the frameCount parameter) and the number of
- host frames (specified in a previous call to SetOutputFrameCount()).
-
- @param bufferProcessor The buffer processor.
-
- @param buffer A pointer to the user buffer pointer, or a pointer to a pointer
- to an array of user buffer pointers for a non-interleaved stream. It is
- important that this parameter points to a copy of the user buffer pointers,
- not to the actual user buffer pointers, because this function updates the
- pointers before returning.
-
- @param frameCount The number of frames of data in the buffer(s) pointed to by
- the buffer parameter.
-
- @return The number of frames copied. The buffer pointer(s) pointed to by the
- buffer parameter are advanced to point to the frame(s) following the last one
- copied.
-*/
-unsigned long PaUtil_CopyOutput( PaUtilBufferProcessor* bufferProcessor,
- const void ** buffer, unsigned long frameCount );
-
-
-/* Zero samples in host output channels set up by the PaUtil_Set*OutputChannels
- functions. This function is useful for flushing streams.
- Zeros the minimum of frameCount and the number of host frames specified in a
- previous call to SetOutputFrameCount().
-
- @param bufferProcessor The buffer processor.
-
- @param frameCount The maximum number of frames to zero.
-
- @return The number of frames zeroed.
-*/
-unsigned long PaUtil_ZeroOutput( PaUtilBufferProcessor* bufferProcessor,
- unsigned long frameCount );
-
-
-/*@}*/
-
-
-#ifdef __cplusplus
-}
-#endif /* __cplusplus */
-#endif /* PA_PROCESS_H */
+#ifndef PA_PROCESS_H
+#define PA_PROCESS_H
+/*
+ * $Id: pa_process.h,v 1.1.2.30 2004/12/13 09:48:44 rossbencina Exp $
+ * Portable Audio I/O Library callback buffer processing adapters
+ *
+ * Based on the Open Source API proposed by Ross Bencina
+ * Copyright (c) 1999-2002 Phil Burk, Ross Bencina
+ *
+ * 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.
+ *
+ * Any person wishing to distribute modifications to the Software is
+ * requested to send the modifications to the original developer so that
+ * they can be incorporated into the canonical version.
+ *
+ * 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.
+ */
+
+/** @file
+ @brief Buffer Processor prototypes. A Buffer Processor performs buffer length
+ adaption, coordinates sample format conversion, and interleaves/deinterleaves
+ channels.
+
+ <h3>Overview</h3>
+
+ The "Buffer Processor" (PaUtilBufferProcessor) manages conversion of audio
+ data from host buffers to user buffers and back again. Where required, the
+ buffer processor takes care of converting between host and user sample formats,
+ interleaving and deinterleaving multichannel buffers, and adapting between host
+ and user buffers with different lengths. The buffer processor may be used with
+ full and half duplex streams, for both callback streams and blocking read/write
+ streams.
+
+ One of the important capabilities provided by the buffer processor is
+ the ability to adapt between user and host buffer sizes of different lengths
+ with minimum latency. Although this task is relatively easy to perform when
+ the host buffer size is an integer multiple of the user buffer size, the
+ problem is more complicated when this is not the case - especially for
+ full-duplex callback streams. Where necessary the adaption is implemented by
+ internally buffering some input and/or output data. The buffer adation
+ algorithm used by the buffer processor was originally implemented by
+ Stephan Letz for the ASIO version of PortAudio, and is described in his
+ Callback_adaption_.pdf which is included in the distribution.
+
+ The buffer processor performs sample conversion using the functions provided
+ by pa_converters.c.
+
+ The following sections provide an overview of how to use the buffer processor.
+ Interested readers are advised to consult the host API implementations for
+ examples of buffer processor usage.
+
+
+ <h4>Initialization, resetting and termination</h4>
+
+ When a stream is opened, the buffer processor should be initialized using
+ PaUtil_InitializeBufferProcessor. This function initializes internal state
+ and allocates temporary buffers as neccesary according to the supplied
+ configuration parameters. Some of the parameters correspond to those requested
+ by the user in their call to Pa_OpenStream(), others reflect the requirements
+ of the host API implementation - they indicate host buffer sizes, formats,
+ and the type of buffering which the Host API uses. The buffer processor should
+ be initialized for callback streams and blocking read/write streams.
+
+ Call PaUtil_ResetBufferProcessor to clear any sample data which is present
+ in the buffer processor before starting to use it (for example when
+ Pa_StartStream is called).
+
+ When the buffer processor is no longer used call
+ PaUtil_TerminateBufferProcessor.
+
+
+ <h4>Using the buffer processor for a callback stream</h4>
+
+ The buffer processor's role in a callback stream is to take host input buffers
+ process them with the stream callback, and fill host output buffers. For a
+ full duplex stream, the buffer processor handles input and output simultaneously
+ due to the requirements of the minimum-latency buffer adation algorithm.
+
+ When a host buffer becomes available, the implementation should call
+ the buffer processor to process the buffer. The buffer processor calls the
+ stream callback to consume and/or produce audio data as necessary. The buffer
+ processor will convert sample formats, interleave/deinterleave channels,
+ and slice or chunk the data to the appropriate buffer lengths according to
+ the requirements of the stream callback and the host API.
+
+ To process a host buffer (or a pair of host buffers for a full-duplex stream)
+ use the following calling sequence:
+
+ -# Call PaUtil_BeginBufferProcessing
+ -# For a stream which takes input:
+ - Call PaUtil_SetInputFrameCount with the number of frames in the host input
+ buffer.
+ - Call one of the following functions one or more times to tell the
+ buffer processor about the host input buffer(s): PaUtil_SetInputChannel,
+ PaUtil_SetInterleavedInputChannels, PaUtil_SetNonInterleavedInputChannel.
+ Which function you call will depend on whether the host buffer(s) are
+ interleaved or not.
+ - If the available host data is split accross two buffers (for example a
+ data range at the end of a circular buffer and another range at the
+ beginning of the circular buffer), also call
+ PaUtil_Set2ndInputFrameCount, PaUtil_Set2ndInputChannel,
+ PaUtil_Set2ndInterleavedInputChannels,
+ PaUtil_Set2ndNonInterleavedInputChannel as necessary to tell the buffer
+ processor about the second buffer.
+ -# For a stream which generates output:
+ - Call PaUtil_SetOutputFrameCount with the number of frames in the host
+ output buffer.
+ - Call one of the following functions one or more times to tell the
+ buffer processor about the host output buffer(s): PaUtil_SetOutputChannel,
+ PaUtil_SetInterleavedOutputChannels, PaUtil_SetNonInterleavedOutputChannel.
+ Which function you call will depend on whether the host buffer(s) are
+ interleaved or not.
+ - If the available host output buffer space is split accross two buffers
+ (for example a data range at the end of a circular buffer and another
+ range at the beginning of the circular buffer), call
+ PaUtil_Set2ndOutputFrameCount, PaUtil_Set2ndOutputChannel,
+ PaUtil_Set2ndInterleavedOutputChannels,
+ PaUtil_Set2ndNonInterleavedOutputChannel as necessary to tell the buffer
+ processor about the second buffer.
+ -# Call PaUtil_EndBufferProcessing, this function performs the actual data
+ conversion and processing.
+
+
+ <h4>Using the buffer processor for a blocking read/write stream</h4>
+
+ Blocking read/write streams use the buffer processor to convert and copy user
+ output data to a host buffer, and to convert and copy host input data to
+ the user's buffer. The buffer processor does not perform any buffer adaption.
+ When using the buffer processor in a blocking read/write stream the input and
+ output conversion are performed separately by the PaUtil_CopyInput and
+ PaUtil_CopyOutput functions.
+
+ To copy data from a host input buffer to the buffer(s) which the user supplies
+ to Pa_ReadStream, use the following calling sequence.
+
+ - Repeat the following three steps until the user buffer(s) have been filled
+ with samples from the host input buffers:
+ -# Call PaUtil_SetInputFrameCount with the number of frames in the host
+ input buffer.
+ -# Call one of the following functions one or more times to tell the
+ buffer processor about the host input buffer(s): PaUtil_SetInputChannel,
+ PaUtil_SetInterleavedInputChannels, PaUtil_SetNonInterleavedInputChannel.
+ Which function you call will depend on whether the host buffer(s) are
+ interleaved or not.
+ -# Call PaUtil_CopyInput with the user buffer pointer (or a copy of the
+ array of buffer pointers for a non-interleaved stream) passed to
+ Pa_ReadStream, along with the number of frames in the user buffer(s).
+ Be careful to pass a <i>copy</i> of the user buffer pointers to
+ PaUtil_CopyInput because PaUtil_CopyInput advances the pointers to
+ the start of the next region to copy.
+ - PaUtil_CopyInput will not copy more data than is available in the
+ host buffer(s), so the above steps need to be repeated until the user
+ buffer(s) are full.
+
+
+ To copy data to the host output buffer from the user buffers(s) supplied
+ to Pa_WriteStream use the following calling sequence.
+
+ - Repeat the following three steps until all frames from the user buffer(s)
+ have been copied to the host API:
+ -# Call PaUtil_SetOutputFrameCount with the number of frames in the host
+ output buffer.
+ -# Call one of the following functions one or more times to tell the
+ buffer processor about the host output buffer(s): PaUtil_SetOutputChannel,
+ PaUtil_SetInterleavedOutputChannels, PaUtil_SetNonInterleavedOutputChannel.
+ Which function you call will depend on whether the host buffer(s) are
+ interleaved or not.
+ -# Call PaUtil_CopyOutput with the user buffer pointer (or a copy of the
+ array of buffer pointers for a non-interleaved stream) passed to
+ Pa_WriteStream, along with the number of frames in the user buffer(s).
+ Be careful to pass a <i>copy</i> of the user buffer pointers to
+ PaUtil_CopyOutput because PaUtil_CopyOutput advances the pointers to
+ the start of the next region to copy.
+ - PaUtil_CopyOutput will not copy more data than fits in the host buffer(s),
+ so the above steps need to be repeated until all user data is copied.
+*/
+
+
+#include "portaudio.h"
+#include "pa_converters.h"
+#include "pa_dither.h"
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif /* __cplusplus */
+
+
+/** @brief Mode flag passed to PaUtil_InitializeBufferProcessor indicating the type
+ of buffering that the host API uses.
+
+ The mode used depends on whether the host API or the implementation manages
+ the buffers, and how these buffers are used (scatter gather, circular buffer).
+*/
+typedef enum {
+/** The host buffer size is a fixed known size. */
+ paUtilFixedHostBufferSize,
+
+/** The host buffer size may vary, but has a known maximum size. */
+ paUtilBoundedHostBufferSize,
+
+/** Nothing is known about the host buffer size. */
+ paUtilUnknownHostBufferSize,
+
+/** The host buffer size varies, and the client does not require the buffer
+ processor to consume all of the input and fill all of the output buffer. This
+ is useful when the implementation has access to the host API's circular buffer
+ and only needs to consume/fill some of it, not necessarily all of it, with each
+ call to the buffer processor. This is the only mode where
+ PaUtil_EndBufferProcessing() may not consume the whole buffer.
+*/
+ paUtilVariableHostBufferSizePartialUsageAllowed
+}PaUtilHostBufferSizeMode;
+
+
+/** @brief An auxilliary data structure used internally by the buffer processor
+ to represent host input and output buffers. */
+typedef struct PaUtilChannelDescriptor{
+ void *data;
+ unsigned int stride; /**< stride in samples, not bytes */
+}PaUtilChannelDescriptor;
+
+
+/** @brief The main buffer processor data structure.
+
+ Allocate one of these, initialize it with PaUtil_InitializeBufferProcessor
+ and terminate it with PaUtil_TerminateBufferProcessor.
+*/
+typedef struct {
+ unsigned long framesPerUserBuffer;
+ unsigned long framesPerHostBuffer;
+
+ PaUtilHostBufferSizeMode hostBufferSizeMode;
+ int useNonAdaptingProcess;
+ unsigned long framesPerTempBuffer;
+
+ unsigned int inputChannelCount;
+ unsigned int bytesPerHostInputSample;
+ unsigned int bytesPerUserInputSample;
+ int userInputIsInterleaved;
+ PaUtilConverter *inputConverter;
+ PaUtilZeroer *inputZeroer;
+
+ unsigned int outputChannelCount;
+ unsigned int bytesPerHostOutputSample;
+ unsigned int bytesPerUserOutputSample;
+ int userOutputIsInterleaved;
+ PaUtilConverter *outputConverter;
+ PaUtilZeroer *outputZeroer;
+
+ unsigned long initialFramesInTempInputBuffer;
+ unsigned long initialFramesInTempOutputBuffer;
+
+ void *tempInputBuffer; /**< used for slips, block adaption, and conversion. */
+ void **tempInputBufferPtrs; /**< storage for non-interleaved buffer pointers, NULL for interleaved user input */
+ unsigned long framesInTempInputBuffer; /**< frames remaining in input buffer from previous adaption iteration */
+
+ void *tempOutputBuffer; /**< used for slips, block adaption, and conversion. */
+ void **tempOutputBufferPtrs; /**< storage for non-interleaved buffer pointers, NULL for interleaved user output */
+ unsigned long framesInTempOutputBuffer; /**< frames remaining in input buffer from previous adaption iteration */
+
+ PaStreamCallbackTimeInfo *timeInfo;
+
+ PaStreamCallbackFlags callbackStatusFlags;
+
+ unsigned long hostInputFrameCount[2];
+ PaUtilChannelDescriptor *hostInputChannels[2]; /**< pointers to arrays of channel descriptors.
+ pointers are NULL for half-duplex output processing.
+ hostInputChannels[i].data is NULL when the caller
+ calls PaUtil_SetNoInput()
+ */
+ unsigned long hostOutputFrameCount[2];
+ PaUtilChannelDescriptor *hostOutputChannels[2]; /**< pointers to arrays of channel descriptors.
+ pointers are NULL for half-duplex input processing.
+ hostOutputChannels[i].data is NULL when the caller
+ calls PaUtil_SetNoOutput()
+ */
+
+ PaUtilTriangularDitherGenerator ditherGenerator;
+
+ double samplePeriod;
+
+ PaStreamCallback *streamCallback;
+ void *userData;
+} PaUtilBufferProcessor;
+
+
+/** @name Initialization, termination, resetting and info */
+/*@{*/
+
+/** Initialize a buffer processor's representation stored in a
+ PaUtilBufferProcessor structure. Be sure to call
+ PaUtil_TerminateBufferProcessor after finishing with a buffer processor.
+
+ @param bufferProcessor The buffer processor structure to initialize.
+
+ @param inputChannelCount The number of input channels as passed to
+ Pa_OpenStream or 0 for an output-only stream.
+
+ @param userInputSampleFormat Format of user input samples, as passed to
+ Pa_OpenStream. This parameter is ignored for ouput-only streams.
+
+ @param hostInputSampleFormat Format of host input samples. This parameter is
+ ignored for output-only streams. See note about host buffer interleave below.
+
+ @param outputChannelCount The number of output channels as passed to
+ Pa_OpenStream or 0 for an input-only stream.
+
+ @param userOutputSampleFormat Format of user output samples, as passed to
+ Pa_OpenStream. This parameter is ignored for input-only streams.
+
+ @param hostOutputSampleFormat Format of host output samples. This parameter is
+ ignored for input-only streams. See note about host buffer interleave below.
+
+ @param sampleRate Sample rate of the stream. The more accurate this is the
+ better - it is used for updating time stamps when adapting buffers.
+
+ @param streamFlags Stream flags as passed to Pa_OpenStream, this parameter is
+ used for selecting special sample conversion options such as clipping and
+ dithering.
+
+ @param framesPerUserBuffer Number of frames per user buffer, as requested
+ by the framesPerBuffer parameter to Pa_OpenStream. This parameter may be
+ zero to indicate that the user will accept any (and varying) buffer sizes.
+
+ @param framesPerHostBuffer Specifies the number of frames per host buffer
+ for the fixed buffer size mode, and the maximum number of frames
+ per host buffer for the bounded host buffer size mode. It is ignored for
+ the other modes.
+
+ @param hostBufferSizeMode A mode flag indicating the size variability of
+ host buffers that will be passed to the buffer processor. See
+ PaUtilHostBufferSizeMode for further details.
+
+ @param streamCallback The user stream callback passed to Pa_OpenStream.
+
+ @param userData The user data field passed to Pa_OpenStream.
+
+ @note The interleave flag is ignored for host buffer formats. Host
+ interleave is determined by the use of different SetInput and SetOutput
+ functions.
+
+ @return An error code indicating whether the initialization was successful.
+ If the error code is not PaNoError, the buffer processor was not initialized
+ and should not be used.
+
+ @see Pa_OpenStream, PaUtilHostBufferSizeMode, PaUtil_TerminateBufferProcessor
+*/
+PaError PaUtil_InitializeBufferProcessor( PaUtilBufferProcessor* bufferProcessor,
+ int inputChannelCount, PaSampleFormat userInputSampleFormat,
+ PaSampleFormat hostInputSampleFormat,
+ int outputChannelCount, PaSampleFormat userOutputSampleFormat,
+ PaSampleFormat hostOutputSampleFormat,
+ double sampleRate,
+ PaStreamFlags streamFlags,
+ unsigned long framesPerUserBuffer, /* 0 indicates don't care */
+ unsigned long framesPerHostBuffer,
+ PaUtilHostBufferSizeMode hostBufferSizeMode,
+ PaStreamCallback *streamCallback, void *userData );
+
+
+/** Terminate a buffer processor's representation. Deallocates any temporary
+ buffers allocated by PaUtil_InitializeBufferProcessor.
+
+ @param bufferProcessor The buffer processor structure to terminate.
+
+ @see PaUtil_InitializeBufferProcessor.
+*/
+void PaUtil_TerminateBufferProcessor( PaUtilBufferProcessor* bufferProcessor );
+
+
+/** Clear any internally buffered data. If you call
+ PaUtil_InitializeBufferProcessor in your OpenStream routine, make sure you
+ call PaUtil_ResetBufferProcessor in your StartStream call.
+
+ @param bufferProcessor The buffer processor to reset.
+*/
+void PaUtil_ResetBufferProcessor( PaUtilBufferProcessor* bufferProcessor );
+
+
+/** Retrieve the input latency of a buffer processor.
+
+ @param bufferProcessor The buffer processor examine.
+
+ @return The input latency introduced by the buffer processor, in frames.
+
+ @see PaUtil_GetBufferProcessorOutputLatency
+*/
+unsigned long PaUtil_GetBufferProcessorInputLatency( PaUtilBufferProcessor* bufferProcessor );
+
+/** Retrieve the output latency of a buffer processor.
+
+ @param bufferProcessor The buffer processor examine.
+
+ @return The output latency introduced by the buffer processor, in frames.
+
+ @see PaUtil_GetBufferProcessorInputLatency
+*/
+unsigned long PaUtil_GetBufferProcessorOutputLatency( PaUtilBufferProcessor* bufferProcessor );
+
+/*@}*/
+
+
+/** @name Host buffer pointer configuration
+
+ Functions to set host input and output buffers, used by both callback streams
+ and blocking read/write streams.
+*/
+/*@{*/
+
+
+/** Set the number of frames in the input host buffer(s) specified by the
+ PaUtil_Set*InputChannel functions.
+
+ @param bufferProcessor The buffer processor.
+
+ @param frameCount The number of host input frames. A 0 frameCount indicates to
+ use the framesPerHostBuffer value passed to PaUtil_InitializeBufferProcessor.
+
+ @see PaUtil_SetNoInput, PaUtil_SetInputChannel,
+ PaUtil_SetInterleavedInputChannels, PaUtil_SetNonInterleavedInputChannel
+*/
+void PaUtil_SetInputFrameCount( PaUtilBufferProcessor* bufferProcessor,
+ unsigned long frameCount );
+
+
+/** Indicate that no input is avalable. This function should be used when
+ priming the output of a full-duplex stream opened with the
+ paPrimeOutputBuffersUsingStreamCallback flag. Note that it is not necessary
+ to call this or any othe PaUtil_Set*Input* functions for ouput-only streams.
+
+ @param bufferProcessor The buffer processor.
+*/
+void PaUtil_SetNoInput( PaUtilBufferProcessor* bufferProcessor );
+
+
+/** Provide the buffer processor with a pointer to a host input channel.
+
+ @param bufferProcessor The buffer processor.
+ @param channel The channel number.
+ @param data The buffer.
+ @param stride The stride from one sample to the next, in samples. For
+ interleaved host buffers, the stride will usually be the same as the number of
+ channels in the buffer.
+*/
+void PaUtil_SetInputChannel( PaUtilBufferProcessor* bufferProcessor,
+ unsigned int channel, void *data, unsigned int stride );
+
+
+/** Provide the buffer processor with a pointer to an number of interleaved
+ host input channels.
+
+ @param bufferProcessor The buffer processor.
+ @param firstChannel The first channel number.
+ @param data The buffer.
+ @param channelCount The number of interleaved channels in the buffer. If
+ channelCount is zero, the number of channels specified to
+ PaUtil_InitializeBufferProcessor will be used.
+*/
+void PaUtil_SetInterleavedInputChannels( PaUtilBufferProcessor* bufferProcessor,
+ unsigned int firstChannel, void *data, unsigned int channelCount );
+
+
+/** Provide the buffer processor with a pointer to one non-interleaved host
+ output channel.
+
+ @param bufferProcessor The buffer processor.
+ @param channel The channel number.
+ @param data The buffer.
+*/
+void PaUtil_SetNonInterleavedInputChannel( PaUtilBufferProcessor* bufferProcessor,
+ unsigned int channel, void *data );
+
+
+/** Use for the second buffer half when the input buffer is split in two halves.
+ @see PaUtil_SetInputFrameCount
+*/
+void PaUtil_Set2ndInputFrameCount( PaUtilBufferProcessor* bufferProcessor,
+ unsigned long frameCount );
+
+/** Use for the second buffer half when the input buffer is split in two halves.
+ @see PaUtil_SetInputChannel
+*/
+void PaUtil_Set2ndInputChannel( PaUtilBufferProcessor* bufferProcessor,
+ unsigned int channel, void *data, unsigned int stride );
+
+/** Use for the second buffer half when the input buffer is split in two halves.
+ @see PaUtil_SetInterleavedInputChannels
+*/
+void PaUtil_Set2ndInterleavedInputChannels( PaUtilBufferProcessor* bufferProcessor,
+ unsigned int firstChannel, void *data, unsigned int channelCount );
+
+/** Use for the second buffer half when the input buffer is split in two halves.
+ @see PaUtil_SetNonInterleavedInputChannel
+*/
+void PaUtil_Set2ndNonInterleavedInputChannel( PaUtilBufferProcessor* bufferProcessor,
+ unsigned int channel, void *data );
+
+
+/** Set the number of frames in the output host buffer(s) specified by the
+ PaUtil_Set*OutputChannel functions.
+
+ @param bufferProcessor The buffer processor.
+
+ @param frameCount The number of host output frames. A 0 frameCount indicates to
+ use the framesPerHostBuffer value passed to PaUtil_InitializeBufferProcessor.
+
+ @see PaUtil_SetOutputChannel, PaUtil_SetInterleavedOutputChannels,
+ PaUtil_SetNonInterleavedOutputChannel
+*/
+void PaUtil_SetOutputFrameCount( PaUtilBufferProcessor* bufferProcessor,
+ unsigned long frameCount );
+
+
+/** Indicate that the output will be discarded. This function should be used
+ when implementing the paNeverDropInput mode for full duplex streams.
+
+ @param bufferProcessor The buffer processor.
+*/
+void PaUtil_SetNoOutput( PaUtilBufferProcessor* bufferProcessor );
+
+
+/** Provide the buffer processor with a pointer to a host output channel.
+
+ @param bufferProcessor The buffer processor.
+ @param channel The channel number.
+ @param data The buffer.
+ @param stride The stride from one sample to the next, in samples. For
+ interleaved host buffers, the stride will usually be the same as the number of
+ channels in the buffer.
+*/
+void PaUtil_SetOutputChannel( PaUtilBufferProcessor* bufferProcessor,
+ unsigned int channel, void *data, unsigned int stride );
+
+
+/** Provide the buffer processor with a pointer to a number of interleaved
+ host output channels.
+
+ @param bufferProcessor The buffer processor.
+ @param firstChannel The first channel number.
+ @param data The buffer.
+ @param channelCount The number of interleaved channels in the buffer. If
+ channelCount is zero, the number of channels specified to
+ PaUtil_InitializeBufferProcessor will be used.
+*/
+void PaUtil_SetInterleavedOutputChannels( PaUtilBufferProcessor* bufferProcessor,
+ unsigned int firstChannel, void *data, unsigned int channelCount );
+
+
+/** Provide the buffer processor with a pointer to one non-interleaved host
+ output channel.
+
+ @param bufferProcessor The buffer processor.
+ @param channel The channel number.
+ @param data The buffer.
+*/
+void PaUtil_SetNonInterleavedOutputChannel( PaUtilBufferProcessor* bufferProcessor,
+ unsigned int channel, void *data );
+
+
+/** Use for the second buffer half when the output buffer is split in two halves.
+ @see PaUtil_SetOutputFrameCount
+*/
+void PaUtil_Set2ndOutputFrameCount( PaUtilBufferProcessor* bufferProcessor,
+ unsigned long frameCount );
+
+/** Use for the second buffer half when the output buffer is split in two halves.
+ @see PaUtil_SetOutputChannel
+*/
+void PaUtil_Set2ndOutputChannel( PaUtilBufferProcessor* bufferProcessor,
+ unsigned int channel, void *data, unsigned int stride );
+
+/** Use for the second buffer half when the output buffer is split in two halves.
+ @see PaUtil_SetInterleavedOutputChannels
+*/
+void PaUtil_Set2ndInterleavedOutputChannels( PaUtilBufferProcessor* bufferProcessor,
+ unsigned int firstChannel, void *data, unsigned int channelCount );
+
+/** Use for the second buffer half when the output buffer is split in two halves.
+ @see PaUtil_SetNonInterleavedOutputChannel
+*/
+void PaUtil_Set2ndNonInterleavedOutputChannel( PaUtilBufferProcessor* bufferProcessor,
+ unsigned int channel, void *data );
+
+/*@}*/
+
+
+/** @name Buffer processing functions for callback streams
+*/
+/*@{*/
+
+/** Commence processing a host buffer (or a pair of host buffers in the
+ full-duplex case) for a callback stream.
+
+ @param bufferProcessor The buffer processor.
+
+ @param timeInfo Timing information for the first sample of the host
+ buffer(s). This information may be adjusted when buffer adaption is being
+ performed.
+
+ @param callbackStatusFlags Flags indicating whether underruns and overruns
+ have occurred since the last time the buffer processor was called.
+*/
+void PaUtil_BeginBufferProcessing( PaUtilBufferProcessor* bufferProcessor,
+ PaStreamCallbackTimeInfo* timeInfo, PaStreamCallbackFlags callbackStatusFlags );
+
+
+/** Finish processing a host buffer (or a pair of host buffers in the
+ full-duplex case) for a callback stream.
+
+ @param bufferProcessor The buffer processor.
+
+ @param callbackResult On input, indicates a previous callback result, and on
+ exit, the result of the user stream callback, if it is called.
+ On entry callbackResult should contain one of { paContinue, paComplete, or
+ paAbort}. If paComplete is passed, the stream callback will not be called
+ but any audio that was generated by previous stream callbacks will be copied
+ to the output buffer(s). You can check whether the buffer processor's internal
+ buffer is empty by calling PaUtil_IsBufferProcessorOutputEmpty.
+
+ If the stream callback is called its result is stored in *callbackResult. If
+ the stream callback returns paComplete or paAbort, all output buffers will be
+ full of valid data - some of which may be zeros to acount for data that
+ wasn't generated by the terminating callback.
+
+ @return The number of frames processed. This usually corresponds to the
+ number of frames specified by the PaUtil_Set*FrameCount functions, exept in
+ the paUtilVariableHostBufferSizePartialUsageAllowed buffer size mode when a
+ smaller value may be returned.
+*/
+unsigned long PaUtil_EndBufferProcessing( PaUtilBufferProcessor* bufferProcessor,
+ int *callbackResult );
+
+
+/** Determine whether any callback generated output remains in the bufffer
+ processor's internal buffers. This method may be used to determine when to
+ continue calling PaUtil_EndBufferProcessing() after the callback has returned
+ a callbackResult of paComplete.
+
+ @param bufferProcessor The buffer processor.
+
+ @return Returns non-zero when callback generated output remains in the internal
+ buffer and zero (0) when there internal buffer contains no callback generated
+ data.
+*/
+int PaUtil_IsBufferProcessorOutputEmpty( PaUtilBufferProcessor* bufferProcessor );
+
+/*@}*/
+
+
+/** @name Buffer processing functions for blocking read/write streams
+*/
+/*@{*/
+
+/** Copy samples from host input channels set up by the PaUtil_Set*InputChannels
+ functions to a user supplied buffer. This function is intended for use with
+ blocking read/write streams. Copies the minimum of the number of
+ user frames (specified by the frameCount parameter) and the number of available
+ host frames (specified in a previous call to SetInputFrameCount()).
+
+ @param bufferProcessor The buffer processor.
+
+ @param buffer A pointer to the user buffer pointer, or a pointer to a pointer
+ to an array of user buffer pointers for a non-interleaved stream. It is
+ important that this parameter points to a copy of the user buffer pointers,
+ not to the actual user buffer pointers, because this function updates the
+ pointers before returning.
+
+ @param frameCount The number of frames of data in the buffer(s) pointed to by
+ the buffer parameter.
+
+ @return The number of frames copied. The buffer pointer(s) pointed to by the
+ buffer parameter are advanced to point to the frame(s) following the last one
+ filled.
+*/
+unsigned long PaUtil_CopyInput( PaUtilBufferProcessor* bufferProcessor,
+ void **buffer, unsigned long frameCount );
+
+
+/* Copy samples from a user supplied buffer to host output channels set up by
+ the PaUtil_Set*OutputChannels functions. This function is intended for use with
+ blocking read/write streams. Copies the minimum of the number of
+ user frames (specified by the frameCount parameter) and the number of
+ host frames (specified in a previous call to SetOutputFrameCount()).
+
+ @param bufferProcessor The buffer processor.
+
+ @param buffer A pointer to the user buffer pointer, or a pointer to a pointer
+ to an array of user buffer pointers for a non-interleaved stream. It is
+ important that this parameter points to a copy of the user buffer pointers,
+ not to the actual user buffer pointers, because this function updates the
+ pointers before returning.
+
+ @param frameCount The number of frames of data in the buffer(s) pointed to by
+ the buffer parameter.
+
+ @return The number of frames copied. The buffer pointer(s) pointed to by the
+ buffer parameter are advanced to point to the frame(s) following the last one
+ copied.
+*/
+unsigned long PaUtil_CopyOutput( PaUtilBufferProcessor* bufferProcessor,
+ const void ** buffer, unsigned long frameCount );
+
+
+/* Zero samples in host output channels set up by the PaUtil_Set*OutputChannels
+ functions. This function is useful for flushing streams.
+ Zeros the minimum of frameCount and the number of host frames specified in a
+ previous call to SetOutputFrameCount().
+
+ @param bufferProcessor The buffer processor.
+
+ @param frameCount The maximum number of frames to zero.
+
+ @return The number of frames zeroed.
+*/
+unsigned long PaUtil_ZeroOutput( PaUtilBufferProcessor* bufferProcessor,
+ unsigned long frameCount );
+
+
+/*@}*/
+
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+#endif /* PA_PROCESS_H */
diff --git a/pjmedia/src/pjmedia/portaudio/pa_skeleton.c b/pjmedia/src/pjmedia/portaudio/pa_skeleton.c
index bea14d9f..ebc1f501 100644
--- a/pjmedia/src/pjmedia/portaudio/pa_skeleton.c
+++ b/pjmedia/src/pjmedia/portaudio/pa_skeleton.c
@@ -1,807 +1,807 @@
-/*
- * $Id: pa_skeleton.c,v 1.1.2.39 2003/11/26 14:56:09 rossbencina Exp $
- * Portable Audio I/O Library skeleton implementation
- * demonstrates how to use the common functions to implement support
- * for a host API
- *
- * Based on the Open Source API proposed by Ross Bencina
- * Copyright (c) 1999-2002 Ross Bencina, Phil Burk
- *
- * 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.
- *
- * Any person wishing to distribute modifications to the Software is
- * requested to send the modifications to the original developer so that
- * they can be incorporated into the canonical version.
- *
- * 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.
- */
-
-/** @file
- @brief Skeleton implementation of support for a host API.
-
- @note This file is provided as a starting point for implementing support for
- a new host API. IMPLEMENT ME comments are used to indicate functionality
- which much be customised for each implementation.
-*/
-
-
-#include <string.h> /* strlen() */
-
-#include "pa_util.h"
-#include "pa_allocation.h"
-#include "pa_hostapi.h"
-#include "pa_stream.h"
-#include "pa_cpuload.h"
-#include "pa_process.h"
-
-
-/* prototypes for functions declared in this file */
-
-#ifdef __cplusplus
-extern "C"
-{
-#endif /* __cplusplus */
-
-PaError PaSkeleton_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiIndex index );
-
-#ifdef __cplusplus
-}
-#endif /* __cplusplus */
-
-
-static void Terminate( struct PaUtilHostApiRepresentation *hostApi );
-static PaError IsFormatSupported( struct PaUtilHostApiRepresentation *hostApi,
- const PaStreamParameters *inputParameters,
- const PaStreamParameters *outputParameters,
- double sampleRate );
-static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi,
- PaStream** s,
- const PaStreamParameters *inputParameters,
- const PaStreamParameters *outputParameters,
- double sampleRate,
- unsigned long framesPerBuffer,
- PaStreamFlags streamFlags,
- PaStreamCallback *streamCallback,
- void *userData );
-static PaError CloseStream( PaStream* stream );
-static PaError StartStream( PaStream *stream );
-static PaError StopStream( PaStream *stream );
-static PaError AbortStream( PaStream *stream );
-static PaError IsStreamStopped( PaStream *s );
-static PaError IsStreamActive( PaStream *stream );
-static PaTime GetStreamTime( PaStream *stream );
-static double GetStreamCpuLoad( PaStream* stream );
-static PaError ReadStream( PaStream* stream, void *buffer, unsigned long frames );
-static PaError WriteStream( PaStream* stream, const void *buffer, unsigned long frames );
-static signed long GetStreamReadAvailable( PaStream* stream );
-static signed long GetStreamWriteAvailable( PaStream* stream );
-
-
-/* IMPLEMENT ME: a macro like the following one should be used for reporting
- host errors */
-#define PA_SKELETON_SET_LAST_HOST_ERROR( errorCode, errorText ) \
- PaUtil_SetLastHostErrorInfo( paInDevelopment, errorCode, errorText )
-
-/* PaSkeletonHostApiRepresentation - host api datastructure specific to this implementation */
-
-typedef struct
-{
- PaUtilHostApiRepresentation inheritedHostApiRep;
- PaUtilStreamInterface callbackStreamInterface;
- PaUtilStreamInterface blockingStreamInterface;
-
- PaUtilAllocationGroup *allocations;
-
- /* implementation specific data goes here */
-}
-PaSkeletonHostApiRepresentation; /* IMPLEMENT ME: rename this */
-
-
-PaError PaSkeleton_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiIndex hostApiIndex )
-{
- PaError result = paNoError;
- int i, deviceCount;
- PaSkeletonHostApiRepresentation *skeletonHostApi;
- PaDeviceInfo *deviceInfoArray;
-
- skeletonHostApi = (PaSkeletonHostApiRepresentation*)PaUtil_AllocateMemory( sizeof(PaSkeletonHostApiRepresentation) );
- if( !skeletonHostApi )
- {
- result = paInsufficientMemory;
- goto error;
- }
-
- skeletonHostApi->allocations = PaUtil_CreateAllocationGroup();
- if( !skeletonHostApi->allocations )
- {
- result = paInsufficientMemory;
- goto error;
- }
-
- *hostApi = &skeletonHostApi->inheritedHostApiRep;
- (*hostApi)->info.structVersion = 1;
- (*hostApi)->info.type = paInDevelopment; /* IMPLEMENT ME: change to correct type id */
- (*hostApi)->info.name = "skeleton implementation"; /* IMPLEMENT ME: change to correct name */
-
- (*hostApi)->info.defaultInputDevice = paNoDevice; /* IMPLEMENT ME */
- (*hostApi)->info.defaultOutputDevice = paNoDevice; /* IMPLEMENT ME */
-
- (*hostApi)->info.deviceCount = 0;
-
- deviceCount = 0; /* IMPLEMENT ME */
-
- if( deviceCount > 0 )
- {
- (*hostApi)->deviceInfos = (PaDeviceInfo**)PaUtil_GroupAllocateMemory(
- skeletonHostApi->allocations, sizeof(PaDeviceInfo*) * deviceCount );
- if( !(*hostApi)->deviceInfos )
- {
- result = paInsufficientMemory;
- goto error;
- }
-
- /* allocate all device info structs in a contiguous block */
- deviceInfoArray = (PaDeviceInfo*)PaUtil_GroupAllocateMemory(
- skeletonHostApi->allocations, sizeof(PaDeviceInfo) * deviceCount );
- if( !deviceInfoArray )
- {
- result = paInsufficientMemory;
- goto error;
- }
-
- for( i=0; i < deviceCount; ++i )
- {
- PaDeviceInfo *deviceInfo = &deviceInfoArray[i];
- deviceInfo->structVersion = 2;
- deviceInfo->hostApi = hostApiIndex;
- deviceInfo->name = 0; /* IMPLEMENT ME: allocate block and copy name eg:
- deviceName = (char*)PaUtil_GroupAllocateMemory( skeletonHostApi->allocations, strlen(srcName) + 1 );
- if( !deviceName )
- {
- result = paInsufficientMemory;
- goto error;
- }
- strcpy( deviceName, srcName );
- deviceInfo->name = deviceName;
- */
-
- deviceInfo->maxInputChannels = 0; /* IMPLEMENT ME */
- deviceInfo->maxOutputChannels = 0; /* IMPLEMENT ME */
-
- deviceInfo->defaultLowInputLatency = 0.; /* IMPLEMENT ME */
- deviceInfo->defaultLowOutputLatency = 0.; /* IMPLEMENT ME */
- deviceInfo->defaultHighInputLatency = 0.; /* IMPLEMENT ME */
- deviceInfo->defaultHighOutputLatency = 0.; /* IMPLEMENT ME */
-
- deviceInfo->defaultSampleRate = 0.; /* IMPLEMENT ME */
-
- (*hostApi)->deviceInfos[i] = deviceInfo;
- ++(*hostApi)->info.deviceCount;
- }
- }
-
- (*hostApi)->Terminate = Terminate;
- (*hostApi)->OpenStream = OpenStream;
- (*hostApi)->IsFormatSupported = IsFormatSupported;
-
- PaUtil_InitializeStreamInterface( &skeletonHostApi->callbackStreamInterface, CloseStream, StartStream,
- StopStream, AbortStream, IsStreamStopped, IsStreamActive,
- GetStreamTime, GetStreamCpuLoad,
- PaUtil_DummyRead, PaUtil_DummyWrite,
- PaUtil_DummyGetReadAvailable, PaUtil_DummyGetWriteAvailable );
-
- PaUtil_InitializeStreamInterface( &skeletonHostApi->blockingStreamInterface, CloseStream, StartStream,
- StopStream, AbortStream, IsStreamStopped, IsStreamActive,
- GetStreamTime, PaUtil_DummyGetCpuLoad,
- ReadStream, WriteStream, GetStreamReadAvailable, GetStreamWriteAvailable );
-
- return result;
-
-error:
- if( skeletonHostApi )
- {
- if( skeletonHostApi->allocations )
- {
- PaUtil_FreeAllAllocations( skeletonHostApi->allocations );
- PaUtil_DestroyAllocationGroup( skeletonHostApi->allocations );
- }
-
- PaUtil_FreeMemory( skeletonHostApi );
- }
- return result;
-}
-
-
-static void Terminate( struct PaUtilHostApiRepresentation *hostApi )
-{
- PaSkeletonHostApiRepresentation *skeletonHostApi = (PaSkeletonHostApiRepresentation*)hostApi;
-
- /*
- IMPLEMENT ME:
- - clean up any resources not handled by the allocation group
- */
-
- if( skeletonHostApi->allocations )
- {
- PaUtil_FreeAllAllocations( skeletonHostApi->allocations );
- PaUtil_DestroyAllocationGroup( skeletonHostApi->allocations );
- }
-
- PaUtil_FreeMemory( skeletonHostApi );
-}
-
-
-static PaError IsFormatSupported( struct PaUtilHostApiRepresentation *hostApi,
- const PaStreamParameters *inputParameters,
- const PaStreamParameters *outputParameters,
- double sampleRate )
-{
- int inputChannelCount, outputChannelCount;
- PaSampleFormat inputSampleFormat, outputSampleFormat;
-
- if( inputParameters )
- {
- inputChannelCount = inputParameters->channelCount;
- inputSampleFormat = inputParameters->sampleFormat;
-
- /* all standard sample formats are supported by the buffer adapter,
- this implementation doesn't support any custom sample formats */
- if( inputSampleFormat & paCustomFormat )
- return paSampleFormatNotSupported;
-
- /* unless alternate device specification is supported, reject the use of
- paUseHostApiSpecificDeviceSpecification */
-
- if( inputParameters->device == paUseHostApiSpecificDeviceSpecification )
- return paInvalidDevice;
-
- /* check that input device can support inputChannelCount */
- if( inputChannelCount > hostApi->deviceInfos[ inputParameters->device ]->maxInputChannels )
- return paInvalidChannelCount;
-
- /* validate inputStreamInfo */
- if( inputParameters->hostApiSpecificStreamInfo )
- return paIncompatibleHostApiSpecificStreamInfo; /* this implementation doesn't use custom stream info */
- }
- else
- {
- inputChannelCount = 0;
- }
-
- if( outputParameters )
- {
- outputChannelCount = outputParameters->channelCount;
- outputSampleFormat = outputParameters->sampleFormat;
-
- /* all standard sample formats are supported by the buffer adapter,
- this implementation doesn't support any custom sample formats */
- if( outputSampleFormat & paCustomFormat )
- return paSampleFormatNotSupported;
-
- /* unless alternate device specification is supported, reject the use of
- paUseHostApiSpecificDeviceSpecification */
-
- if( outputParameters->device == paUseHostApiSpecificDeviceSpecification )
- return paInvalidDevice;
-
- /* check that output device can support outputChannelCount */
- if( outputChannelCount > hostApi->deviceInfos[ outputParameters->device ]->maxOutputChannels )
- return paInvalidChannelCount;
-
- /* validate outputStreamInfo */
- if( outputParameters->hostApiSpecificStreamInfo )
- return paIncompatibleHostApiSpecificStreamInfo; /* this implementation doesn't use custom stream info */
- }
- else
- {
- outputChannelCount = 0;
- }
-
- /*
- IMPLEMENT ME:
-
- - if a full duplex stream is requested, check that the combination
- of input and output parameters is supported if necessary
-
- - check that the device supports sampleRate
-
- Because the buffer adapter handles conversion between all standard
- sample formats, the following checks are only required if paCustomFormat
- is implemented, or under some other unusual conditions.
-
- - check that input device can support inputSampleFormat, or that
- we have the capability to convert from inputSampleFormat to
- a native format
-
- - check that output device can support outputSampleFormat, or that
- we have the capability to convert from outputSampleFormat to
- a native format
- */
-
-
- /* suppress unused variable warnings */
- (void) sampleRate;
-
- return paFormatIsSupported;
-}
-
-/* PaSkeletonStream - a stream data structure specifically for this implementation */
-
-typedef struct PaSkeletonStream
-{ /* IMPLEMENT ME: rename this */
- PaUtilStreamRepresentation streamRepresentation;
- PaUtilCpuLoadMeasurer cpuLoadMeasurer;
- PaUtilBufferProcessor bufferProcessor;
-
- /* IMPLEMENT ME:
- - implementation specific data goes here
- */
- unsigned long framesPerHostCallback; /* just an example */
-}
-PaSkeletonStream;
-
-/* see pa_hostapi.h for a list of validity guarantees made about OpenStream parameters */
-
-static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi,
- PaStream** s,
- const PaStreamParameters *inputParameters,
- const PaStreamParameters *outputParameters,
- double sampleRate,
- unsigned long framesPerBuffer,
- PaStreamFlags streamFlags,
- PaStreamCallback *streamCallback,
- void *userData )
-{
- PaError result = paNoError;
- PaSkeletonHostApiRepresentation *skeletonHostApi = (PaSkeletonHostApiRepresentation*)hostApi;
- PaSkeletonStream *stream = 0;
- unsigned long framesPerHostBuffer = framesPerBuffer; /* these may not be equivalent for all implementations */
- int inputChannelCount, outputChannelCount;
- PaSampleFormat inputSampleFormat, outputSampleFormat;
- PaSampleFormat hostInputSampleFormat, hostOutputSampleFormat;
-
-
- if( inputParameters )
- {
- inputChannelCount = inputParameters->channelCount;
- inputSampleFormat = inputParameters->sampleFormat;
-
- /* unless alternate device specification is supported, reject the use of
- paUseHostApiSpecificDeviceSpecification */
-
- if( inputParameters->device == paUseHostApiSpecificDeviceSpecification )
- return paInvalidDevice;
-
- /* check that input device can support inputChannelCount */
- if( inputChannelCount > hostApi->deviceInfos[ inputParameters->device ]->maxInputChannels )
- return paInvalidChannelCount;
-
- /* validate inputStreamInfo */
- if( inputParameters->hostApiSpecificStreamInfo )
- return paIncompatibleHostApiSpecificStreamInfo; /* this implementation doesn't use custom stream info */
-
- /* IMPLEMENT ME - establish which host formats are available */
- hostInputSampleFormat =
- PaUtil_SelectClosestAvailableFormat( paInt16 /* native formats */, inputSampleFormat );
- }
- else
- {
- inputChannelCount = 0;
- inputSampleFormat = hostInputSampleFormat = paInt16; /* Surpress 'uninitialised var' warnings. */
- }
-
- if( outputParameters )
- {
- outputChannelCount = outputParameters->channelCount;
- outputSampleFormat = outputParameters->sampleFormat;
-
- /* unless alternate device specification is supported, reject the use of
- paUseHostApiSpecificDeviceSpecification */
-
- if( outputParameters->device == paUseHostApiSpecificDeviceSpecification )
- return paInvalidDevice;
-
- /* check that output device can support inputChannelCount */
- if( outputChannelCount > hostApi->deviceInfos[ outputParameters->device ]->maxOutputChannels )
- return paInvalidChannelCount;
-
- /* validate outputStreamInfo */
- if( outputParameters->hostApiSpecificStreamInfo )
- return paIncompatibleHostApiSpecificStreamInfo; /* this implementation doesn't use custom stream info */
-
- /* IMPLEMENT ME - establish which host formats are available */
- hostOutputSampleFormat =
- PaUtil_SelectClosestAvailableFormat( paInt16 /* native formats */, outputSampleFormat );
- }
- else
- {
- outputChannelCount = 0;
- outputSampleFormat = hostOutputSampleFormat = paInt16; /* Surpress 'uninitialized var' warnings. */
- }
-
- /*
- IMPLEMENT ME:
-
- ( the following two checks are taken care of by PaUtil_InitializeBufferProcessor() FIXME - checks needed? )
-
- - check that input device can support inputSampleFormat, or that
- we have the capability to convert from outputSampleFormat to
- a native format
-
- - check that output device can support outputSampleFormat, or that
- we have the capability to convert from outputSampleFormat to
- a native format
-
- - if a full duplex stream is requested, check that the combination
- of input and output parameters is supported
-
- - check that the device supports sampleRate
-
- - alter sampleRate to a close allowable rate if possible / necessary
-
- - validate suggestedInputLatency and suggestedOutputLatency parameters,
- use default values where necessary
- */
-
-
-
-
- /* validate platform specific flags */
- if( (streamFlags & paPlatformSpecificFlags) != 0 )
- return paInvalidFlag; /* unexpected platform specific flag */
-
-
- stream = (PaSkeletonStream*)PaUtil_AllocateMemory( sizeof(PaSkeletonStream) );
- if( !stream )
- {
- result = paInsufficientMemory;
- goto error;
- }
-
- if( streamCallback )
- {
- PaUtil_InitializeStreamRepresentation( &stream->streamRepresentation,
- &skeletonHostApi->callbackStreamInterface, streamCallback, userData );
- }
- else
- {
- PaUtil_InitializeStreamRepresentation( &stream->streamRepresentation,
- &skeletonHostApi->blockingStreamInterface, streamCallback, userData );
- }
-
- PaUtil_InitializeCpuLoadMeasurer( &stream->cpuLoadMeasurer, sampleRate );
-
-
- /* we assume a fixed host buffer size in this example, but the buffer processor
- can also support bounded and unknown host buffer sizes by passing
- paUtilBoundedHostBufferSize or paUtilUnknownHostBufferSize instead of
- paUtilFixedHostBufferSize below. */
-
- result = PaUtil_InitializeBufferProcessor( &stream->bufferProcessor,
- inputChannelCount, inputSampleFormat, hostInputSampleFormat,
- outputChannelCount, outputSampleFormat, hostOutputSampleFormat,
- sampleRate, streamFlags, framesPerBuffer,
- framesPerHostBuffer, paUtilFixedHostBufferSize,
- streamCallback, userData );
- if( result != paNoError )
- goto error;
-
-
- /*
- IMPLEMENT ME: initialise the following fields with estimated or actual
- values.
- */
- stream->streamRepresentation.streamInfo.inputLatency =
- PaUtil_GetBufferProcessorInputLatency(&stream->bufferProcessor);
- stream->streamRepresentation.streamInfo.outputLatency =
- PaUtil_GetBufferProcessorOutputLatency(&stream->bufferProcessor);
- stream->streamRepresentation.streamInfo.sampleRate = sampleRate;
-
-
- /*
- IMPLEMENT ME:
- - additional stream setup + opening
- */
-
- stream->framesPerHostCallback = framesPerHostBuffer;
-
- *s = (PaStream*)stream;
-
- return result;
-
-error:
- if( stream )
- PaUtil_FreeMemory( stream );
-
- return result;
-}
-
-/*
- ExampleHostProcessingLoop() illustrates the kind of processing which may
- occur in a host implementation.
-
-*/
-static void ExampleHostProcessingLoop( void *inputBuffer, void *outputBuffer, void *userData )
-{
- PaSkeletonStream *stream = (PaSkeletonStream*)userData;
- PaStreamCallbackTimeInfo timeInfo = {0,0,0}; /* IMPLEMENT ME */
- int callbackResult;
- unsigned long framesProcessed;
-
- PaUtil_BeginCpuLoadMeasurement( &stream->cpuLoadMeasurer );
-
- /*
- IMPLEMENT ME:
- - generate timing information
- - handle buffer slips
- */
-
- /*
- If you need to byte swap or shift inputBuffer to convert it into a
- portaudio format, do it here.
- */
-
-
-
- PaUtil_BeginBufferProcessing( &stream->bufferProcessor, &timeInfo, 0 /* IMPLEMENT ME: pass underflow/overflow flags when necessary */ );
-
- /*
- depending on whether the host buffers are interleaved, non-interleaved
- or a mixture, you will want to call PaUtil_SetInterleaved*Channels(),
- PaUtil_SetNonInterleaved*Channel() or PaUtil_Set*Channel() here.
- */
-
- PaUtil_SetInputFrameCount( &stream->bufferProcessor, 0 /* default to host buffer size */ );
- PaUtil_SetInterleavedInputChannels( &stream->bufferProcessor,
- 0, /* first channel of inputBuffer is channel 0 */
- inputBuffer,
- 0 ); /* 0 - use inputChannelCount passed to init buffer processor */
-
- PaUtil_SetOutputFrameCount( &stream->bufferProcessor, 0 /* default to host buffer size */ );
- PaUtil_SetInterleavedOutputChannels( &stream->bufferProcessor,
- 0, /* first channel of outputBuffer is channel 0 */
- outputBuffer,
- 0 ); /* 0 - use outputChannelCount passed to init buffer processor */
-
- /* you must pass a valid value of callback result to PaUtil_EndBufferProcessing()
- in general you would pass paContinue for normal operation, and
- paComplete to drain the buffer processor's internal output buffer.
- You can check whether the buffer processor's output buffer is empty
- using PaUtil_IsBufferProcessorOuputEmpty( bufferProcessor )
- */
- callbackResult = paContinue;
- framesProcessed = PaUtil_EndBufferProcessing( &stream->bufferProcessor, &callbackResult );
-
-
- /*
- If you need to byte swap or shift outputBuffer to convert it to
- host format, do it here.
- */
-
- PaUtil_EndCpuLoadMeasurement( &stream->cpuLoadMeasurer, framesProcessed );
-
-
- if( callbackResult == paContinue )
- {
- /* nothing special to do */
- }
- else if( callbackResult == paAbort )
- {
- /* IMPLEMENT ME - finish playback immediately */
-
- /* once finished, call the finished callback */
- if( stream->streamRepresentation.streamFinishedCallback != 0 )
- stream->streamRepresentation.streamFinishedCallback( stream->streamRepresentation.userData );
- }
- else
- {
- /* User callback has asked us to stop with paComplete or other non-zero value */
-
- /* IMPLEMENT ME - finish playback once currently queued audio has completed */
-
- /* once finished, call the finished callback */
- if( stream->streamRepresentation.streamFinishedCallback != 0 )
- stream->streamRepresentation.streamFinishedCallback( stream->streamRepresentation.userData );
- }
-}
-
-
-/*
- When CloseStream() is called, the multi-api layer ensures that
- the stream has already been stopped or aborted.
-*/
-static PaError CloseStream( PaStream* s )
-{
- PaError result = paNoError;
- PaSkeletonStream *stream = (PaSkeletonStream*)s;
-
- /*
- IMPLEMENT ME:
- - additional stream closing + cleanup
- */
-
- PaUtil_TerminateBufferProcessor( &stream->bufferProcessor );
- PaUtil_TerminateStreamRepresentation( &stream->streamRepresentation );
- PaUtil_FreeMemory( stream );
-
- return result;
-}
-
-
-static PaError StartStream( PaStream *s )
-{
- PaError result = paNoError;
- PaSkeletonStream *stream = (PaSkeletonStream*)s;
-
- PaUtil_ResetBufferProcessor( &stream->bufferProcessor );
-
- /* IMPLEMENT ME, see portaudio.h for required behavior */
-
- /* suppress unused function warning. the code in ExampleHostProcessingLoop or
- something similar should be implemented to feed samples to and from the
- host after StartStream() is called.
- */
- (void) ExampleHostProcessingLoop;
-
- return result;
-}
-
-
-static PaError StopStream( PaStream *s )
-{
- PaError result = paNoError;
- PaSkeletonStream *stream = (PaSkeletonStream*)s;
-
- /* suppress unused variable warnings */
- (void) stream;
-
- /* IMPLEMENT ME, see portaudio.h for required behavior */
-
- return result;
-}
-
-
-static PaError AbortStream( PaStream *s )
-{
- PaError result = paNoError;
- PaSkeletonStream *stream = (PaSkeletonStream*)s;
-
- /* suppress unused variable warnings */
- (void) stream;
-
- /* IMPLEMENT ME, see portaudio.h for required behavior */
-
- return result;
-}
-
-
-static PaError IsStreamStopped( PaStream *s )
-{
- PaSkeletonStream *stream = (PaSkeletonStream*)s;
-
- /* suppress unused variable warnings */
- (void) stream;
-
- /* IMPLEMENT ME, see portaudio.h for required behavior */
-
- return 0;
-}
-
-
-static PaError IsStreamActive( PaStream *s )
-{
- PaSkeletonStream *stream = (PaSkeletonStream*)s;
-
- /* suppress unused variable warnings */
- (void) stream;
-
- /* IMPLEMENT ME, see portaudio.h for required behavior */
-
- return 0;
-}
-
-
-static PaTime GetStreamTime( PaStream *s )
-{
- PaSkeletonStream *stream = (PaSkeletonStream*)s;
-
- /* suppress unused variable warnings */
- (void) stream;
-
- /* IMPLEMENT ME, see portaudio.h for required behavior*/
-
- return 0;
-}
-
-
-static double GetStreamCpuLoad( PaStream* s )
-{
- PaSkeletonStream *stream = (PaSkeletonStream*)s;
-
- return PaUtil_GetCpuLoad( &stream->cpuLoadMeasurer );
-}
-
-
-/*
- As separate stream interfaces are used for blocking and callback
- streams, the following functions can be guaranteed to only be called
- for blocking streams.
-*/
-
-static PaError ReadStream( PaStream* s,
- void *buffer,
- unsigned long frames )
-{
- PaSkeletonStream *stream = (PaSkeletonStream*)s;
-
- /* suppress unused variable warnings */
- (void) buffer;
- (void) frames;
- (void) stream;
-
- /* IMPLEMENT ME, see portaudio.h for required behavior*/
-
- return paNoError;
-}
-
-
-static PaError WriteStream( PaStream* s,
- const void *buffer,
- unsigned long frames )
-{
- PaSkeletonStream *stream = (PaSkeletonStream*)s;
-
- /* suppress unused variable warnings */
- (void) buffer;
- (void) frames;
- (void) stream;
-
- /* IMPLEMENT ME, see portaudio.h for required behavior*/
-
- return paNoError;
-}
-
-
-static signed long GetStreamReadAvailable( PaStream* s )
-{
- PaSkeletonStream *stream = (PaSkeletonStream*)s;
-
- /* suppress unused variable warnings */
- (void) stream;
-
- /* IMPLEMENT ME, see portaudio.h for required behavior*/
-
- return 0;
-}
-
-
-static signed long GetStreamWriteAvailable( PaStream* s )
-{
- PaSkeletonStream *stream = (PaSkeletonStream*)s;
-
- /* suppress unused variable warnings */
- (void) stream;
-
- /* IMPLEMENT ME, see portaudio.h for required behavior*/
-
- return 0;
-}
-
-
-
-
+/*
+ * $Id: pa_skeleton.c,v 1.1.2.39 2003/11/26 14:56:09 rossbencina Exp $
+ * Portable Audio I/O Library skeleton implementation
+ * demonstrates how to use the common functions to implement support
+ * for a host API
+ *
+ * Based on the Open Source API proposed by Ross Bencina
+ * Copyright (c) 1999-2002 Ross Bencina, Phil Burk
+ *
+ * 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.
+ *
+ * Any person wishing to distribute modifications to the Software is
+ * requested to send the modifications to the original developer so that
+ * they can be incorporated into the canonical version.
+ *
+ * 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.
+ */
+
+/** @file
+ @brief Skeleton implementation of support for a host API.
+
+ @note This file is provided as a starting point for implementing support for
+ a new host API. IMPLEMENT ME comments are used to indicate functionality
+ which much be customised for each implementation.
+*/
+
+
+#include <string.h> /* strlen() */
+
+#include "pa_util.h"
+#include "pa_allocation.h"
+#include "pa_hostapi.h"
+#include "pa_stream.h"
+#include "pa_cpuload.h"
+#include "pa_process.h"
+
+
+/* prototypes for functions declared in this file */
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif /* __cplusplus */
+
+PaError PaSkeleton_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiIndex index );
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+
+static void Terminate( struct PaUtilHostApiRepresentation *hostApi );
+static PaError IsFormatSupported( struct PaUtilHostApiRepresentation *hostApi,
+ const PaStreamParameters *inputParameters,
+ const PaStreamParameters *outputParameters,
+ double sampleRate );
+static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi,
+ PaStream** s,
+ const PaStreamParameters *inputParameters,
+ const PaStreamParameters *outputParameters,
+ double sampleRate,
+ unsigned long framesPerBuffer,
+ PaStreamFlags streamFlags,
+ PaStreamCallback *streamCallback,
+ void *userData );
+static PaError CloseStream( PaStream* stream );
+static PaError StartStream( PaStream *stream );
+static PaError StopStream( PaStream *stream );
+static PaError AbortStream( PaStream *stream );
+static PaError IsStreamStopped( PaStream *s );
+static PaError IsStreamActive( PaStream *stream );
+static PaTime GetStreamTime( PaStream *stream );
+static double GetStreamCpuLoad( PaStream* stream );
+static PaError ReadStream( PaStream* stream, void *buffer, unsigned long frames );
+static PaError WriteStream( PaStream* stream, const void *buffer, unsigned long frames );
+static signed long GetStreamReadAvailable( PaStream* stream );
+static signed long GetStreamWriteAvailable( PaStream* stream );
+
+
+/* IMPLEMENT ME: a macro like the following one should be used for reporting
+ host errors */
+#define PA_SKELETON_SET_LAST_HOST_ERROR( errorCode, errorText ) \
+ PaUtil_SetLastHostErrorInfo( paInDevelopment, errorCode, errorText )
+
+/* PaSkeletonHostApiRepresentation - host api datastructure specific to this implementation */
+
+typedef struct
+{
+ PaUtilHostApiRepresentation inheritedHostApiRep;
+ PaUtilStreamInterface callbackStreamInterface;
+ PaUtilStreamInterface blockingStreamInterface;
+
+ PaUtilAllocationGroup *allocations;
+
+ /* implementation specific data goes here */
+}
+PaSkeletonHostApiRepresentation; /* IMPLEMENT ME: rename this */
+
+
+PaError PaSkeleton_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiIndex hostApiIndex )
+{
+ PaError result = paNoError;
+ int i, deviceCount;
+ PaSkeletonHostApiRepresentation *skeletonHostApi;
+ PaDeviceInfo *deviceInfoArray;
+
+ skeletonHostApi = (PaSkeletonHostApiRepresentation*)PaUtil_AllocateMemory( sizeof(PaSkeletonHostApiRepresentation) );
+ if( !skeletonHostApi )
+ {
+ result = paInsufficientMemory;
+ goto error;
+ }
+
+ skeletonHostApi->allocations = PaUtil_CreateAllocationGroup();
+ if( !skeletonHostApi->allocations )
+ {
+ result = paInsufficientMemory;
+ goto error;
+ }
+
+ *hostApi = &skeletonHostApi->inheritedHostApiRep;
+ (*hostApi)->info.structVersion = 1;
+ (*hostApi)->info.type = paInDevelopment; /* IMPLEMENT ME: change to correct type id */
+ (*hostApi)->info.name = "skeleton implementation"; /* IMPLEMENT ME: change to correct name */
+
+ (*hostApi)->info.defaultInputDevice = paNoDevice; /* IMPLEMENT ME */
+ (*hostApi)->info.defaultOutputDevice = paNoDevice; /* IMPLEMENT ME */
+
+ (*hostApi)->info.deviceCount = 0;
+
+ deviceCount = 0; /* IMPLEMENT ME */
+
+ if( deviceCount > 0 )
+ {
+ (*hostApi)->deviceInfos = (PaDeviceInfo**)PaUtil_GroupAllocateMemory(
+ skeletonHostApi->allocations, sizeof(PaDeviceInfo*) * deviceCount );
+ if( !(*hostApi)->deviceInfos )
+ {
+ result = paInsufficientMemory;
+ goto error;
+ }
+
+ /* allocate all device info structs in a contiguous block */
+ deviceInfoArray = (PaDeviceInfo*)PaUtil_GroupAllocateMemory(
+ skeletonHostApi->allocations, sizeof(PaDeviceInfo) * deviceCount );
+ if( !deviceInfoArray )
+ {
+ result = paInsufficientMemory;
+ goto error;
+ }
+
+ for( i=0; i < deviceCount; ++i )
+ {
+ PaDeviceInfo *deviceInfo = &deviceInfoArray[i];
+ deviceInfo->structVersion = 2;
+ deviceInfo->hostApi = hostApiIndex;
+ deviceInfo->name = 0; /* IMPLEMENT ME: allocate block and copy name eg:
+ deviceName = (char*)PaUtil_GroupAllocateMemory( skeletonHostApi->allocations, strlen(srcName) + 1 );
+ if( !deviceName )
+ {
+ result = paInsufficientMemory;
+ goto error;
+ }
+ strcpy( deviceName, srcName );
+ deviceInfo->name = deviceName;
+ */
+
+ deviceInfo->maxInputChannels = 0; /* IMPLEMENT ME */
+ deviceInfo->maxOutputChannels = 0; /* IMPLEMENT ME */
+
+ deviceInfo->defaultLowInputLatency = 0.; /* IMPLEMENT ME */
+ deviceInfo->defaultLowOutputLatency = 0.; /* IMPLEMENT ME */
+ deviceInfo->defaultHighInputLatency = 0.; /* IMPLEMENT ME */
+ deviceInfo->defaultHighOutputLatency = 0.; /* IMPLEMENT ME */
+
+ deviceInfo->defaultSampleRate = 0.; /* IMPLEMENT ME */
+
+ (*hostApi)->deviceInfos[i] = deviceInfo;
+ ++(*hostApi)->info.deviceCount;
+ }
+ }
+
+ (*hostApi)->Terminate = Terminate;
+ (*hostApi)->OpenStream = OpenStream;
+ (*hostApi)->IsFormatSupported = IsFormatSupported;
+
+ PaUtil_InitializeStreamInterface( &skeletonHostApi->callbackStreamInterface, CloseStream, StartStream,
+ StopStream, AbortStream, IsStreamStopped, IsStreamActive,
+ GetStreamTime, GetStreamCpuLoad,
+ PaUtil_DummyRead, PaUtil_DummyWrite,
+ PaUtil_DummyGetReadAvailable, PaUtil_DummyGetWriteAvailable );
+
+ PaUtil_InitializeStreamInterface( &skeletonHostApi->blockingStreamInterface, CloseStream, StartStream,
+ StopStream, AbortStream, IsStreamStopped, IsStreamActive,
+ GetStreamTime, PaUtil_DummyGetCpuLoad,
+ ReadStream, WriteStream, GetStreamReadAvailable, GetStreamWriteAvailable );
+
+ return result;
+
+error:
+ if( skeletonHostApi )
+ {
+ if( skeletonHostApi->allocations )
+ {
+ PaUtil_FreeAllAllocations( skeletonHostApi->allocations );
+ PaUtil_DestroyAllocationGroup( skeletonHostApi->allocations );
+ }
+
+ PaUtil_FreeMemory( skeletonHostApi );
+ }
+ return result;
+}
+
+
+static void Terminate( struct PaUtilHostApiRepresentation *hostApi )
+{
+ PaSkeletonHostApiRepresentation *skeletonHostApi = (PaSkeletonHostApiRepresentation*)hostApi;
+
+ /*
+ IMPLEMENT ME:
+ - clean up any resources not handled by the allocation group
+ */
+
+ if( skeletonHostApi->allocations )
+ {
+ PaUtil_FreeAllAllocations( skeletonHostApi->allocations );
+ PaUtil_DestroyAllocationGroup( skeletonHostApi->allocations );
+ }
+
+ PaUtil_FreeMemory( skeletonHostApi );
+}
+
+
+static PaError IsFormatSupported( struct PaUtilHostApiRepresentation *hostApi,
+ const PaStreamParameters *inputParameters,
+ const PaStreamParameters *outputParameters,
+ double sampleRate )
+{
+ int inputChannelCount, outputChannelCount;
+ PaSampleFormat inputSampleFormat, outputSampleFormat;
+
+ if( inputParameters )
+ {
+ inputChannelCount = inputParameters->channelCount;
+ inputSampleFormat = inputParameters->sampleFormat;
+
+ /* all standard sample formats are supported by the buffer adapter,
+ this implementation doesn't support any custom sample formats */
+ if( inputSampleFormat & paCustomFormat )
+ return paSampleFormatNotSupported;
+
+ /* unless alternate device specification is supported, reject the use of
+ paUseHostApiSpecificDeviceSpecification */
+
+ if( inputParameters->device == paUseHostApiSpecificDeviceSpecification )
+ return paInvalidDevice;
+
+ /* check that input device can support inputChannelCount */
+ if( inputChannelCount > hostApi->deviceInfos[ inputParameters->device ]->maxInputChannels )
+ return paInvalidChannelCount;
+
+ /* validate inputStreamInfo */
+ if( inputParameters->hostApiSpecificStreamInfo )
+ return paIncompatibleHostApiSpecificStreamInfo; /* this implementation doesn't use custom stream info */
+ }
+ else
+ {
+ inputChannelCount = 0;
+ }
+
+ if( outputParameters )
+ {
+ outputChannelCount = outputParameters->channelCount;
+ outputSampleFormat = outputParameters->sampleFormat;
+
+ /* all standard sample formats are supported by the buffer adapter,
+ this implementation doesn't support any custom sample formats */
+ if( outputSampleFormat & paCustomFormat )
+ return paSampleFormatNotSupported;
+
+ /* unless alternate device specification is supported, reject the use of
+ paUseHostApiSpecificDeviceSpecification */
+
+ if( outputParameters->device == paUseHostApiSpecificDeviceSpecification )
+ return paInvalidDevice;
+
+ /* check that output device can support outputChannelCount */
+ if( outputChannelCount > hostApi->deviceInfos[ outputParameters->device ]->maxOutputChannels )
+ return paInvalidChannelCount;
+
+ /* validate outputStreamInfo */
+ if( outputParameters->hostApiSpecificStreamInfo )
+ return paIncompatibleHostApiSpecificStreamInfo; /* this implementation doesn't use custom stream info */
+ }
+ else
+ {
+ outputChannelCount = 0;
+ }
+
+ /*
+ IMPLEMENT ME:
+
+ - if a full duplex stream is requested, check that the combination
+ of input and output parameters is supported if necessary
+
+ - check that the device supports sampleRate
+
+ Because the buffer adapter handles conversion between all standard
+ sample formats, the following checks are only required if paCustomFormat
+ is implemented, or under some other unusual conditions.
+
+ - check that input device can support inputSampleFormat, or that
+ we have the capability to convert from inputSampleFormat to
+ a native format
+
+ - check that output device can support outputSampleFormat, or that
+ we have the capability to convert from outputSampleFormat to
+ a native format
+ */
+
+
+ /* suppress unused variable warnings */
+ (void) sampleRate;
+
+ return paFormatIsSupported;
+}
+
+/* PaSkeletonStream - a stream data structure specifically for this implementation */
+
+typedef struct PaSkeletonStream
+{ /* IMPLEMENT ME: rename this */
+ PaUtilStreamRepresentation streamRepresentation;
+ PaUtilCpuLoadMeasurer cpuLoadMeasurer;
+ PaUtilBufferProcessor bufferProcessor;
+
+ /* IMPLEMENT ME:
+ - implementation specific data goes here
+ */
+ unsigned long framesPerHostCallback; /* just an example */
+}
+PaSkeletonStream;
+
+/* see pa_hostapi.h for a list of validity guarantees made about OpenStream parameters */
+
+static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi,
+ PaStream** s,
+ const PaStreamParameters *inputParameters,
+ const PaStreamParameters *outputParameters,
+ double sampleRate,
+ unsigned long framesPerBuffer,
+ PaStreamFlags streamFlags,
+ PaStreamCallback *streamCallback,
+ void *userData )
+{
+ PaError result = paNoError;
+ PaSkeletonHostApiRepresentation *skeletonHostApi = (PaSkeletonHostApiRepresentation*)hostApi;
+ PaSkeletonStream *stream = 0;
+ unsigned long framesPerHostBuffer = framesPerBuffer; /* these may not be equivalent for all implementations */
+ int inputChannelCount, outputChannelCount;
+ PaSampleFormat inputSampleFormat, outputSampleFormat;
+ PaSampleFormat hostInputSampleFormat, hostOutputSampleFormat;
+
+
+ if( inputParameters )
+ {
+ inputChannelCount = inputParameters->channelCount;
+ inputSampleFormat = inputParameters->sampleFormat;
+
+ /* unless alternate device specification is supported, reject the use of
+ paUseHostApiSpecificDeviceSpecification */
+
+ if( inputParameters->device == paUseHostApiSpecificDeviceSpecification )
+ return paInvalidDevice;
+
+ /* check that input device can support inputChannelCount */
+ if( inputChannelCount > hostApi->deviceInfos[ inputParameters->device ]->maxInputChannels )
+ return paInvalidChannelCount;
+
+ /* validate inputStreamInfo */
+ if( inputParameters->hostApiSpecificStreamInfo )
+ return paIncompatibleHostApiSpecificStreamInfo; /* this implementation doesn't use custom stream info */
+
+ /* IMPLEMENT ME - establish which host formats are available */
+ hostInputSampleFormat =
+ PaUtil_SelectClosestAvailableFormat( paInt16 /* native formats */, inputSampleFormat );
+ }
+ else
+ {
+ inputChannelCount = 0;
+ inputSampleFormat = hostInputSampleFormat = paInt16; /* Surpress 'uninitialised var' warnings. */
+ }
+
+ if( outputParameters )
+ {
+ outputChannelCount = outputParameters->channelCount;
+ outputSampleFormat = outputParameters->sampleFormat;
+
+ /* unless alternate device specification is supported, reject the use of
+ paUseHostApiSpecificDeviceSpecification */
+
+ if( outputParameters->device == paUseHostApiSpecificDeviceSpecification )
+ return paInvalidDevice;
+
+ /* check that output device can support inputChannelCount */
+ if( outputChannelCount > hostApi->deviceInfos[ outputParameters->device ]->maxOutputChannels )
+ return paInvalidChannelCount;
+
+ /* validate outputStreamInfo */
+ if( outputParameters->hostApiSpecificStreamInfo )
+ return paIncompatibleHostApiSpecificStreamInfo; /* this implementation doesn't use custom stream info */
+
+ /* IMPLEMENT ME - establish which host formats are available */
+ hostOutputSampleFormat =
+ PaUtil_SelectClosestAvailableFormat( paInt16 /* native formats */, outputSampleFormat );
+ }
+ else
+ {
+ outputChannelCount = 0;
+ outputSampleFormat = hostOutputSampleFormat = paInt16; /* Surpress 'uninitialized var' warnings. */
+ }
+
+ /*
+ IMPLEMENT ME:
+
+ ( the following two checks are taken care of by PaUtil_InitializeBufferProcessor() FIXME - checks needed? )
+
+ - check that input device can support inputSampleFormat, or that
+ we have the capability to convert from outputSampleFormat to
+ a native format
+
+ - check that output device can support outputSampleFormat, or that
+ we have the capability to convert from outputSampleFormat to
+ a native format
+
+ - if a full duplex stream is requested, check that the combination
+ of input and output parameters is supported
+
+ - check that the device supports sampleRate
+
+ - alter sampleRate to a close allowable rate if possible / necessary
+
+ - validate suggestedInputLatency and suggestedOutputLatency parameters,
+ use default values where necessary
+ */
+
+
+
+
+ /* validate platform specific flags */
+ if( (streamFlags & paPlatformSpecificFlags) != 0 )
+ return paInvalidFlag; /* unexpected platform specific flag */
+
+
+ stream = (PaSkeletonStream*)PaUtil_AllocateMemory( sizeof(PaSkeletonStream) );
+ if( !stream )
+ {
+ result = paInsufficientMemory;
+ goto error;
+ }
+
+ if( streamCallback )
+ {
+ PaUtil_InitializeStreamRepresentation( &stream->streamRepresentation,
+ &skeletonHostApi->callbackStreamInterface, streamCallback, userData );
+ }
+ else
+ {
+ PaUtil_InitializeStreamRepresentation( &stream->streamRepresentation,
+ &skeletonHostApi->blockingStreamInterface, streamCallback, userData );
+ }
+
+ PaUtil_InitializeCpuLoadMeasurer( &stream->cpuLoadMeasurer, sampleRate );
+
+
+ /* we assume a fixed host buffer size in this example, but the buffer processor
+ can also support bounded and unknown host buffer sizes by passing
+ paUtilBoundedHostBufferSize or paUtilUnknownHostBufferSize instead of
+ paUtilFixedHostBufferSize below. */
+
+ result = PaUtil_InitializeBufferProcessor( &stream->bufferProcessor,
+ inputChannelCount, inputSampleFormat, hostInputSampleFormat,
+ outputChannelCount, outputSampleFormat, hostOutputSampleFormat,
+ sampleRate, streamFlags, framesPerBuffer,
+ framesPerHostBuffer, paUtilFixedHostBufferSize,
+ streamCallback, userData );
+ if( result != paNoError )
+ goto error;
+
+
+ /*
+ IMPLEMENT ME: initialise the following fields with estimated or actual
+ values.
+ */
+ stream->streamRepresentation.streamInfo.inputLatency =
+ PaUtil_GetBufferProcessorInputLatency(&stream->bufferProcessor);
+ stream->streamRepresentation.streamInfo.outputLatency =
+ PaUtil_GetBufferProcessorOutputLatency(&stream->bufferProcessor);
+ stream->streamRepresentation.streamInfo.sampleRate = sampleRate;
+
+
+ /*
+ IMPLEMENT ME:
+ - additional stream setup + opening
+ */
+
+ stream->framesPerHostCallback = framesPerHostBuffer;
+
+ *s = (PaStream*)stream;
+
+ return result;
+
+error:
+ if( stream )
+ PaUtil_FreeMemory( stream );
+
+ return result;
+}
+
+/*
+ ExampleHostProcessingLoop() illustrates the kind of processing which may
+ occur in a host implementation.
+
+*/
+static void ExampleHostProcessingLoop( void *inputBuffer, void *outputBuffer, void *userData )
+{
+ PaSkeletonStream *stream = (PaSkeletonStream*)userData;
+ PaStreamCallbackTimeInfo timeInfo = {0,0,0}; /* IMPLEMENT ME */
+ int callbackResult;
+ unsigned long framesProcessed;
+
+ PaUtil_BeginCpuLoadMeasurement( &stream->cpuLoadMeasurer );
+
+ /*
+ IMPLEMENT ME:
+ - generate timing information
+ - handle buffer slips
+ */
+
+ /*
+ If you need to byte swap or shift inputBuffer to convert it into a
+ portaudio format, do it here.
+ */
+
+
+
+ PaUtil_BeginBufferProcessing( &stream->bufferProcessor, &timeInfo, 0 /* IMPLEMENT ME: pass underflow/overflow flags when necessary */ );
+
+ /*
+ depending on whether the host buffers are interleaved, non-interleaved
+ or a mixture, you will want to call PaUtil_SetInterleaved*Channels(),
+ PaUtil_SetNonInterleaved*Channel() or PaUtil_Set*Channel() here.
+ */
+
+ PaUtil_SetInputFrameCount( &stream->bufferProcessor, 0 /* default to host buffer size */ );
+ PaUtil_SetInterleavedInputChannels( &stream->bufferProcessor,
+ 0, /* first channel of inputBuffer is channel 0 */
+ inputBuffer,
+ 0 ); /* 0 - use inputChannelCount passed to init buffer processor */
+
+ PaUtil_SetOutputFrameCount( &stream->bufferProcessor, 0 /* default to host buffer size */ );
+ PaUtil_SetInterleavedOutputChannels( &stream->bufferProcessor,
+ 0, /* first channel of outputBuffer is channel 0 */
+ outputBuffer,
+ 0 ); /* 0 - use outputChannelCount passed to init buffer processor */
+
+ /* you must pass a valid value of callback result to PaUtil_EndBufferProcessing()
+ in general you would pass paContinue for normal operation, and
+ paComplete to drain the buffer processor's internal output buffer.
+ You can check whether the buffer processor's output buffer is empty
+ using PaUtil_IsBufferProcessorOuputEmpty( bufferProcessor )
+ */
+ callbackResult = paContinue;
+ framesProcessed = PaUtil_EndBufferProcessing( &stream->bufferProcessor, &callbackResult );
+
+
+ /*
+ If you need to byte swap or shift outputBuffer to convert it to
+ host format, do it here.
+ */
+
+ PaUtil_EndCpuLoadMeasurement( &stream->cpuLoadMeasurer, framesProcessed );
+
+
+ if( callbackResult == paContinue )
+ {
+ /* nothing special to do */
+ }
+ else if( callbackResult == paAbort )
+ {
+ /* IMPLEMENT ME - finish playback immediately */
+
+ /* once finished, call the finished callback */
+ if( stream->streamRepresentation.streamFinishedCallback != 0 )
+ stream->streamRepresentation.streamFinishedCallback( stream->streamRepresentation.userData );
+ }
+ else
+ {
+ /* User callback has asked us to stop with paComplete or other non-zero value */
+
+ /* IMPLEMENT ME - finish playback once currently queued audio has completed */
+
+ /* once finished, call the finished callback */
+ if( stream->streamRepresentation.streamFinishedCallback != 0 )
+ stream->streamRepresentation.streamFinishedCallback( stream->streamRepresentation.userData );
+ }
+}
+
+
+/*
+ When CloseStream() is called, the multi-api layer ensures that
+ the stream has already been stopped or aborted.
+*/
+static PaError CloseStream( PaStream* s )
+{
+ PaError result = paNoError;
+ PaSkeletonStream *stream = (PaSkeletonStream*)s;
+
+ /*
+ IMPLEMENT ME:
+ - additional stream closing + cleanup
+ */
+
+ PaUtil_TerminateBufferProcessor( &stream->bufferProcessor );
+ PaUtil_TerminateStreamRepresentation( &stream->streamRepresentation );
+ PaUtil_FreeMemory( stream );
+
+ return result;
+}
+
+
+static PaError StartStream( PaStream *s )
+{
+ PaError result = paNoError;
+ PaSkeletonStream *stream = (PaSkeletonStream*)s;
+
+ PaUtil_ResetBufferProcessor( &stream->bufferProcessor );
+
+ /* IMPLEMENT ME, see portaudio.h for required behavior */
+
+ /* suppress unused function warning. the code in ExampleHostProcessingLoop or
+ something similar should be implemented to feed samples to and from the
+ host after StartStream() is called.
+ */
+ (void) ExampleHostProcessingLoop;
+
+ return result;
+}
+
+
+static PaError StopStream( PaStream *s )
+{
+ PaError result = paNoError;
+ PaSkeletonStream *stream = (PaSkeletonStream*)s;
+
+ /* suppress unused variable warnings */
+ (void) stream;
+
+ /* IMPLEMENT ME, see portaudio.h for required behavior */
+
+ return result;
+}
+
+
+static PaError AbortStream( PaStream *s )
+{
+ PaError result = paNoError;
+ PaSkeletonStream *stream = (PaSkeletonStream*)s;
+
+ /* suppress unused variable warnings */
+ (void) stream;
+
+ /* IMPLEMENT ME, see portaudio.h for required behavior */
+
+ return result;
+}
+
+
+static PaError IsStreamStopped( PaStream *s )
+{
+ PaSkeletonStream *stream = (PaSkeletonStream*)s;
+
+ /* suppress unused variable warnings */
+ (void) stream;
+
+ /* IMPLEMENT ME, see portaudio.h for required behavior */
+
+ return 0;
+}
+
+
+static PaError IsStreamActive( PaStream *s )
+{
+ PaSkeletonStream *stream = (PaSkeletonStream*)s;
+
+ /* suppress unused variable warnings */
+ (void) stream;
+
+ /* IMPLEMENT ME, see portaudio.h for required behavior */
+
+ return 0;
+}
+
+
+static PaTime GetStreamTime( PaStream *s )
+{
+ PaSkeletonStream *stream = (PaSkeletonStream*)s;
+
+ /* suppress unused variable warnings */
+ (void) stream;
+
+ /* IMPLEMENT ME, see portaudio.h for required behavior*/
+
+ return 0;
+}
+
+
+static double GetStreamCpuLoad( PaStream* s )
+{
+ PaSkeletonStream *stream = (PaSkeletonStream*)s;
+
+ return PaUtil_GetCpuLoad( &stream->cpuLoadMeasurer );
+}
+
+
+/*
+ As separate stream interfaces are used for blocking and callback
+ streams, the following functions can be guaranteed to only be called
+ for blocking streams.
+*/
+
+static PaError ReadStream( PaStream* s,
+ void *buffer,
+ unsigned long frames )
+{
+ PaSkeletonStream *stream = (PaSkeletonStream*)s;
+
+ /* suppress unused variable warnings */
+ (void) buffer;
+ (void) frames;
+ (void) stream;
+
+ /* IMPLEMENT ME, see portaudio.h for required behavior*/
+
+ return paNoError;
+}
+
+
+static PaError WriteStream( PaStream* s,
+ const void *buffer,
+ unsigned long frames )
+{
+ PaSkeletonStream *stream = (PaSkeletonStream*)s;
+
+ /* suppress unused variable warnings */
+ (void) buffer;
+ (void) frames;
+ (void) stream;
+
+ /* IMPLEMENT ME, see portaudio.h for required behavior*/
+
+ return paNoError;
+}
+
+
+static signed long GetStreamReadAvailable( PaStream* s )
+{
+ PaSkeletonStream *stream = (PaSkeletonStream*)s;
+
+ /* suppress unused variable warnings */
+ (void) stream;
+
+ /* IMPLEMENT ME, see portaudio.h for required behavior*/
+
+ return 0;
+}
+
+
+static signed long GetStreamWriteAvailable( PaStream* s )
+{
+ PaSkeletonStream *stream = (PaSkeletonStream*)s;
+
+ /* suppress unused variable warnings */
+ (void) stream;
+
+ /* IMPLEMENT ME, see portaudio.h for required behavior*/
+
+ return 0;
+}
+
+
+
+
diff --git a/pjmedia/src/pjmedia/portaudio/pa_stream.c b/pjmedia/src/pjmedia/portaudio/pa_stream.c
index 590b7943..044dde29 100644
--- a/pjmedia/src/pjmedia/portaudio/pa_stream.c
+++ b/pjmedia/src/pjmedia/portaudio/pa_stream.c
@@ -1,141 +1,141 @@
-/*
- * $Id: pa_stream.c,v 1.1.2.12 2003/09/20 21:31:00 rossbencina Exp $
- * Portable Audio I/O Library
- *
- *
- * Based on the Open Source API proposed by Ross Bencina
- * Copyright (c) 2002 Ross Bencina
- *
- * 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.
- *
- * Any person wishing to distribute modifications to the Software is
- * requested to send the modifications to the original developer so that
- * they can be incorporated into the canonical version.
- *
- * 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.
- */
-
-/** @file
- @brief Interface used by pa_front to virtualize functions which operate on
- streams.
-*/
-
-
-#include "pa_stream.h"
-
-
-void PaUtil_InitializeStreamInterface( PaUtilStreamInterface *streamInterface,
- PaError (*Close)( PaStream* ),
- PaError (*Start)( PaStream* ),
- PaError (*Stop)( PaStream* ),
- PaError (*Abort)( PaStream* ),
- PaError (*IsStopped)( PaStream* ),
- PaError (*IsActive)( PaStream* ),
- PaTime (*GetTime)( PaStream* ),
- double (*GetCpuLoad)( PaStream* ),
- PaError (*Read)( PaStream*, void *, unsigned long ),
- PaError (*Write)( PaStream*, const void *, unsigned long ),
- signed long (*GetReadAvailable)( PaStream* ),
- signed long (*GetWriteAvailable)( PaStream* ) )
-{
- streamInterface->Close = Close;
- streamInterface->Start = Start;
- streamInterface->Stop = Stop;
- streamInterface->Abort = Abort;
- streamInterface->IsStopped = IsStopped;
- streamInterface->IsActive = IsActive;
- streamInterface->GetTime = GetTime;
- streamInterface->GetCpuLoad = GetCpuLoad;
- streamInterface->Read = Read;
- streamInterface->Write = Write;
- streamInterface->GetReadAvailable = GetReadAvailable;
- streamInterface->GetWriteAvailable = GetWriteAvailable;
-}
-
-
-void PaUtil_InitializeStreamRepresentation( PaUtilStreamRepresentation *streamRepresentation,
- PaUtilStreamInterface *streamInterface,
- PaStreamCallback *streamCallback,
- void *userData )
-{
- streamRepresentation->magic = PA_STREAM_MAGIC;
- streamRepresentation->nextOpenStream = 0;
- streamRepresentation->streamInterface = streamInterface;
- streamRepresentation->streamCallback = streamCallback;
- streamRepresentation->streamFinishedCallback = 0;
-
- streamRepresentation->userData = userData;
-
- streamRepresentation->streamInfo.inputLatency = 0.;
- streamRepresentation->streamInfo.outputLatency = 0.;
- streamRepresentation->streamInfo.sampleRate = 0.;
-}
-
-
-void PaUtil_TerminateStreamRepresentation( PaUtilStreamRepresentation *streamRepresentation )
-{
- streamRepresentation->magic = 0;
-}
-
-
-PaError PaUtil_DummyRead( PaStream* stream,
- void *buffer,
- unsigned long frames )
-{
- (void)stream; /* unused parameter */
- (void)buffer; /* unused parameter */
- (void)frames; /* unused parameter */
-
- return paCanNotReadFromACallbackStream;
-}
-
-
-PaError PaUtil_DummyWrite( PaStream* stream,
- const void *buffer,
- unsigned long frames )
-{
- (void)stream; /* unused parameter */
- (void)buffer; /* unused parameter */
- (void)frames; /* unused parameter */
-
- return paCanNotWriteToACallbackStream;
-}
-
-
-signed long PaUtil_DummyGetReadAvailable( PaStream* stream )
-{
- (void)stream; /* unused parameter */
-
- return paCanNotReadFromACallbackStream;
-}
-
-
-signed long PaUtil_DummyGetWriteAvailable( PaStream* stream )
-{
- (void)stream; /* unused parameter */
-
- return paCanNotWriteToACallbackStream;
-}
-
-
-double PaUtil_DummyGetCpuLoad( PaStream* stream )
-{
- (void)stream; /* unused parameter */
-
- return 0.0;
-}
+/*
+ * $Id: pa_stream.c,v 1.1.2.12 2003/09/20 21:31:00 rossbencina Exp $
+ * Portable Audio I/O Library
+ *
+ *
+ * Based on the Open Source API proposed by Ross Bencina
+ * Copyright (c) 2002 Ross Bencina
+ *
+ * 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.
+ *
+ * Any person wishing to distribute modifications to the Software is
+ * requested to send the modifications to the original developer so that
+ * they can be incorporated into the canonical version.
+ *
+ * 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.
+ */
+
+/** @file
+ @brief Interface used by pa_front to virtualize functions which operate on
+ streams.
+*/
+
+
+#include "pa_stream.h"
+
+
+void PaUtil_InitializeStreamInterface( PaUtilStreamInterface *streamInterface,
+ PaError (*Close)( PaStream* ),
+ PaError (*Start)( PaStream* ),
+ PaError (*Stop)( PaStream* ),
+ PaError (*Abort)( PaStream* ),
+ PaError (*IsStopped)( PaStream* ),
+ PaError (*IsActive)( PaStream* ),
+ PaTime (*GetTime)( PaStream* ),
+ double (*GetCpuLoad)( PaStream* ),
+ PaError (*Read)( PaStream*, void *, unsigned long ),
+ PaError (*Write)( PaStream*, const void *, unsigned long ),
+ signed long (*GetReadAvailable)( PaStream* ),
+ signed long (*GetWriteAvailable)( PaStream* ) )
+{
+ streamInterface->Close = Close;
+ streamInterface->Start = Start;
+ streamInterface->Stop = Stop;
+ streamInterface->Abort = Abort;
+ streamInterface->IsStopped = IsStopped;
+ streamInterface->IsActive = IsActive;
+ streamInterface->GetTime = GetTime;
+ streamInterface->GetCpuLoad = GetCpuLoad;
+ streamInterface->Read = Read;
+ streamInterface->Write = Write;
+ streamInterface->GetReadAvailable = GetReadAvailable;
+ streamInterface->GetWriteAvailable = GetWriteAvailable;
+}
+
+
+void PaUtil_InitializeStreamRepresentation( PaUtilStreamRepresentation *streamRepresentation,
+ PaUtilStreamInterface *streamInterface,
+ PaStreamCallback *streamCallback,
+ void *userData )
+{
+ streamRepresentation->magic = PA_STREAM_MAGIC;
+ streamRepresentation->nextOpenStream = 0;
+ streamRepresentation->streamInterface = streamInterface;
+ streamRepresentation->streamCallback = streamCallback;
+ streamRepresentation->streamFinishedCallback = 0;
+
+ streamRepresentation->userData = userData;
+
+ streamRepresentation->streamInfo.inputLatency = 0.;
+ streamRepresentation->streamInfo.outputLatency = 0.;
+ streamRepresentation->streamInfo.sampleRate = 0.;
+}
+
+
+void PaUtil_TerminateStreamRepresentation( PaUtilStreamRepresentation *streamRepresentation )
+{
+ streamRepresentation->magic = 0;
+}
+
+
+PaError PaUtil_DummyRead( PaStream* stream,
+ void *buffer,
+ unsigned long frames )
+{
+ (void)stream; /* unused parameter */
+ (void)buffer; /* unused parameter */
+ (void)frames; /* unused parameter */
+
+ return paCanNotReadFromACallbackStream;
+}
+
+
+PaError PaUtil_DummyWrite( PaStream* stream,
+ const void *buffer,
+ unsigned long frames )
+{
+ (void)stream; /* unused parameter */
+ (void)buffer; /* unused parameter */
+ (void)frames; /* unused parameter */
+
+ return paCanNotWriteToACallbackStream;
+}
+
+
+signed long PaUtil_DummyGetReadAvailable( PaStream* stream )
+{
+ (void)stream; /* unused parameter */
+
+ return paCanNotReadFromACallbackStream;
+}
+
+
+signed long PaUtil_DummyGetWriteAvailable( PaStream* stream )
+{
+ (void)stream; /* unused parameter */
+
+ return paCanNotWriteToACallbackStream;
+}
+
+
+double PaUtil_DummyGetCpuLoad( PaStream* stream )
+{
+ (void)stream; /* unused parameter */
+
+ return 0.0;
+}
diff --git a/pjmedia/src/pjmedia/portaudio/pa_stream.h b/pjmedia/src/pjmedia/portaudio/pa_stream.h
index c5d8f9f4..8b86943b 100644
--- a/pjmedia/src/pjmedia/portaudio/pa_stream.h
+++ b/pjmedia/src/pjmedia/portaudio/pa_stream.h
@@ -1,196 +1,196 @@
-#ifndef PA_STREAM_H
-#define PA_STREAM_H
-/*
- * $Id: pa_stream.h,v 1.1.2.13 2003/11/01 06:37:28 rossbencina Exp $
- * Portable Audio I/O Library
- * stream interface
- *
- * Based on the Open Source API proposed by Ross Bencina
- * Copyright (c) 1999-2002 Ross Bencina, Phil Burk
- *
- * 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.
- *
- * Any person wishing to distribute modifications to the Software is
- * requested to send the modifications to the original developer so that
- * they can be incorporated into the canonical version.
- *
- * 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.
- */
-
-/** @file
- @brief Interface used by pa_front to virtualize functions which operate on
- streams.
-*/
-
-
-#include "portaudio.h"
-
-#ifdef __cplusplus
-extern "C"
-{
-#endif /* __cplusplus */
-
-
-#define PA_STREAM_MAGIC (0x18273645)
-
-
-/** A structure representing an (abstract) interface to a host API. Contains
- pointers to functions which implement the interface.
-
- All PaStreamInterface functions are guaranteed to be called with a non-null,
- valid stream parameter.
-*/
-typedef struct {
- PaError (*Close)( PaStream* stream );
- PaError (*Start)( PaStream *stream );
- PaError (*Stop)( PaStream *stream );
- PaError (*Abort)( PaStream *stream );
- PaError (*IsStopped)( PaStream *stream );
- PaError (*IsActive)( PaStream *stream );
- PaTime (*GetTime)( PaStream *stream );
- double (*GetCpuLoad)( PaStream* stream );
- PaError (*Read)( PaStream* stream, void *buffer, unsigned long frames );
- PaError (*Write)( PaStream* stream, const void *buffer, unsigned long frames );
- signed long (*GetReadAvailable)( PaStream* stream );
- signed long (*GetWriteAvailable)( PaStream* stream );
-} PaUtilStreamInterface;
-
-
-/** Initialize the fields of a PaUtilStreamInterface structure.
-*/
-void PaUtil_InitializeStreamInterface( PaUtilStreamInterface *streamInterface,
- PaError (*Close)( PaStream* ),
- PaError (*Start)( PaStream* ),
- PaError (*Stop)( PaStream* ),
- PaError (*Abort)( PaStream* ),
- PaError (*IsStopped)( PaStream* ),
- PaError (*IsActive)( PaStream* ),
- PaTime (*GetTime)( PaStream* ),
- double (*GetCpuLoad)( PaStream* ),
- PaError (*Read)( PaStream* stream, void *buffer, unsigned long frames ),
- PaError (*Write)( PaStream* stream, const void *buffer, unsigned long frames ),
- signed long (*GetReadAvailable)( PaStream* stream ),
- signed long (*GetWriteAvailable)( PaStream* stream ) );
-
-
-/** Dummy Read function for use in interfaces to a callback based streams.
- Pass to the Read parameter of PaUtil_InitializeStreamInterface.
- @return An error code indicating that the function has no effect
- because the stream is a callback stream.
-*/
-PaError PaUtil_DummyRead( PaStream* stream,
- void *buffer,
- unsigned long frames );
-
-
-/** Dummy Write function for use in an interfaces to callback based streams.
- Pass to the Write parameter of PaUtil_InitializeStreamInterface.
- @return An error code indicating that the function has no effect
- because the stream is a callback stream.
-*/
-PaError PaUtil_DummyWrite( PaStream* stream,
- const void *buffer,
- unsigned long frames );
-
-
-/** Dummy GetReadAvailable function for use in interfaces to callback based
- streams. Pass to the GetReadAvailable parameter of PaUtil_InitializeStreamInterface.
- @return An error code indicating that the function has no effect
- because the stream is a callback stream.
-*/
-signed long PaUtil_DummyGetReadAvailable( PaStream* stream );
-
-
-/** Dummy GetWriteAvailable function for use in interfaces to callback based
- streams. Pass to the GetWriteAvailable parameter of PaUtil_InitializeStreamInterface.
- @return An error code indicating that the function has no effect
- because the stream is a callback stream.
-*/
-signed long PaUtil_DummyGetWriteAvailable( PaStream* stream );
-
-
-
-/** Dummy GetCpuLoad function for use in an interface to a read/write stream.
- Pass to the GetCpuLoad parameter of PaUtil_InitializeStreamInterface.
- @return Returns 0.
-*/
-double PaUtil_DummyGetCpuLoad( PaStream* stream );
-
-
-/** Non host specific data for a stream. This data is used by pa_front to
- forward to the appropriate functions in the streamInterface structure.
-*/
-typedef struct PaUtilStreamRepresentation {
- unsigned long magic; /**< set to PA_STREAM_MAGIC */
- struct PaUtilStreamRepresentation *nextOpenStream; /**< field used by multi-api code */
- PaUtilStreamInterface *streamInterface;
- PaStreamCallback *streamCallback;
- PaStreamFinishedCallback *streamFinishedCallback;
- void *userData;
- PaStreamInfo streamInfo;
-} PaUtilStreamRepresentation;
-
-
-/** Initialize a PaUtilStreamRepresentation structure.
-
- @see PaUtil_InitializeStreamRepresentation
-*/
-void PaUtil_InitializeStreamRepresentation(
- PaUtilStreamRepresentation *streamRepresentation,
- PaUtilStreamInterface *streamInterface,
- PaStreamCallback *streamCallback,
- void *userData );
-
-
-/** Clean up a PaUtilStreamRepresentation structure previously initialized
- by a call to PaUtil_InitializeStreamRepresentation.
-
- @see PaUtil_InitializeStreamRepresentation
-*/
-void PaUtil_TerminateStreamRepresentation( PaUtilStreamRepresentation *streamRepresentation );
-
-
-/** Check that the stream pointer is valid.
-
- @return Returns paNoError if the stream pointer appears to be OK, otherwise
- returns an error indicating the cause of failure.
-*/
-PaError PaUtil_ValidateStreamPointer( PaStream *stream );
-
-
-/** Cast an opaque stream pointer into a pointer to a PaUtilStreamRepresentation.
-
- @see PaUtilStreamRepresentation
-*/
-#define PA_STREAM_REP( stream )\
- ((PaUtilStreamRepresentation*) (stream) )
-
-
-/** Cast an opaque stream pointer into a pointer to a PaUtilStreamInterface.
-
- @see PaUtilStreamRepresentation, PaUtilStreamInterface
-*/
-#define PA_STREAM_INTERFACE( stream )\
- PA_STREAM_REP( (stream) )->streamInterface
-
-
-
-#ifdef __cplusplus
-}
-#endif /* __cplusplus */
-#endif /* PA_STREAM_H */
+#ifndef PA_STREAM_H
+#define PA_STREAM_H
+/*
+ * $Id: pa_stream.h,v 1.1.2.13 2003/11/01 06:37:28 rossbencina Exp $
+ * Portable Audio I/O Library
+ * stream interface
+ *
+ * Based on the Open Source API proposed by Ross Bencina
+ * Copyright (c) 1999-2002 Ross Bencina, Phil Burk
+ *
+ * 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.
+ *
+ * Any person wishing to distribute modifications to the Software is
+ * requested to send the modifications to the original developer so that
+ * they can be incorporated into the canonical version.
+ *
+ * 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.
+ */
+
+/** @file
+ @brief Interface used by pa_front to virtualize functions which operate on
+ streams.
+*/
+
+
+#include "portaudio.h"
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif /* __cplusplus */
+
+
+#define PA_STREAM_MAGIC (0x18273645)
+
+
+/** A structure representing an (abstract) interface to a host API. Contains
+ pointers to functions which implement the interface.
+
+ All PaStreamInterface functions are guaranteed to be called with a non-null,
+ valid stream parameter.
+*/
+typedef struct {
+ PaError (*Close)( PaStream* stream );
+ PaError (*Start)( PaStream *stream );
+ PaError (*Stop)( PaStream *stream );
+ PaError (*Abort)( PaStream *stream );
+ PaError (*IsStopped)( PaStream *stream );
+ PaError (*IsActive)( PaStream *stream );
+ PaTime (*GetTime)( PaStream *stream );
+ double (*GetCpuLoad)( PaStream* stream );
+ PaError (*Read)( PaStream* stream, void *buffer, unsigned long frames );
+ PaError (*Write)( PaStream* stream, const void *buffer, unsigned long frames );
+ signed long (*GetReadAvailable)( PaStream* stream );
+ signed long (*GetWriteAvailable)( PaStream* stream );
+} PaUtilStreamInterface;
+
+
+/** Initialize the fields of a PaUtilStreamInterface structure.
+*/
+void PaUtil_InitializeStreamInterface( PaUtilStreamInterface *streamInterface,
+ PaError (*Close)( PaStream* ),
+ PaError (*Start)( PaStream* ),
+ PaError (*Stop)( PaStream* ),
+ PaError (*Abort)( PaStream* ),
+ PaError (*IsStopped)( PaStream* ),
+ PaError (*IsActive)( PaStream* ),
+ PaTime (*GetTime)( PaStream* ),
+ double (*GetCpuLoad)( PaStream* ),
+ PaError (*Read)( PaStream* stream, void *buffer, unsigned long frames ),
+ PaError (*Write)( PaStream* stream, const void *buffer, unsigned long frames ),
+ signed long (*GetReadAvailable)( PaStream* stream ),
+ signed long (*GetWriteAvailable)( PaStream* stream ) );
+
+
+/** Dummy Read function for use in interfaces to a callback based streams.
+ Pass to the Read parameter of PaUtil_InitializeStreamInterface.
+ @return An error code indicating that the function has no effect
+ because the stream is a callback stream.
+*/
+PaError PaUtil_DummyRead( PaStream* stream,
+ void *buffer,
+ unsigned long frames );
+
+
+/** Dummy Write function for use in an interfaces to callback based streams.
+ Pass to the Write parameter of PaUtil_InitializeStreamInterface.
+ @return An error code indicating that the function has no effect
+ because the stream is a callback stream.
+*/
+PaError PaUtil_DummyWrite( PaStream* stream,
+ const void *buffer,
+ unsigned long frames );
+
+
+/** Dummy GetReadAvailable function for use in interfaces to callback based
+ streams. Pass to the GetReadAvailable parameter of PaUtil_InitializeStreamInterface.
+ @return An error code indicating that the function has no effect
+ because the stream is a callback stream.
+*/
+signed long PaUtil_DummyGetReadAvailable( PaStream* stream );
+
+
+/** Dummy GetWriteAvailable function for use in interfaces to callback based
+ streams. Pass to the GetWriteAvailable parameter of PaUtil_InitializeStreamInterface.
+ @return An error code indicating that the function has no effect
+ because the stream is a callback stream.
+*/
+signed long PaUtil_DummyGetWriteAvailable( PaStream* stream );
+
+
+
+/** Dummy GetCpuLoad function for use in an interface to a read/write stream.
+ Pass to the GetCpuLoad parameter of PaUtil_InitializeStreamInterface.
+ @return Returns 0.
+*/
+double PaUtil_DummyGetCpuLoad( PaStream* stream );
+
+
+/** Non host specific data for a stream. This data is used by pa_front to
+ forward to the appropriate functions in the streamInterface structure.
+*/
+typedef struct PaUtilStreamRepresentation {
+ unsigned long magic; /**< set to PA_STREAM_MAGIC */
+ struct PaUtilStreamRepresentation *nextOpenStream; /**< field used by multi-api code */
+ PaUtilStreamInterface *streamInterface;
+ PaStreamCallback *streamCallback;
+ PaStreamFinishedCallback *streamFinishedCallback;
+ void *userData;
+ PaStreamInfo streamInfo;
+} PaUtilStreamRepresentation;
+
+
+/** Initialize a PaUtilStreamRepresentation structure.
+
+ @see PaUtil_InitializeStreamRepresentation
+*/
+void PaUtil_InitializeStreamRepresentation(
+ PaUtilStreamRepresentation *streamRepresentation,
+ PaUtilStreamInterface *streamInterface,
+ PaStreamCallback *streamCallback,
+ void *userData );
+
+
+/** Clean up a PaUtilStreamRepresentation structure previously initialized
+ by a call to PaUtil_InitializeStreamRepresentation.
+
+ @see PaUtil_InitializeStreamRepresentation
+*/
+void PaUtil_TerminateStreamRepresentation( PaUtilStreamRepresentation *streamRepresentation );
+
+
+/** Check that the stream pointer is valid.
+
+ @return Returns paNoError if the stream pointer appears to be OK, otherwise
+ returns an error indicating the cause of failure.
+*/
+PaError PaUtil_ValidateStreamPointer( PaStream *stream );
+
+
+/** Cast an opaque stream pointer into a pointer to a PaUtilStreamRepresentation.
+
+ @see PaUtilStreamRepresentation
+*/
+#define PA_STREAM_REP( stream )\
+ ((PaUtilStreamRepresentation*) (stream) )
+
+
+/** Cast an opaque stream pointer into a pointer to a PaUtilStreamInterface.
+
+ @see PaUtilStreamRepresentation, PaUtilStreamInterface
+*/
+#define PA_STREAM_INTERFACE( stream )\
+ PA_STREAM_REP( (stream) )->streamInterface
+
+
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+#endif /* PA_STREAM_H */
diff --git a/pjmedia/src/pjmedia/portaudio/pa_trace.c b/pjmedia/src/pjmedia/portaudio/pa_trace.c
index a80541b2..83514a05 100644
--- a/pjmedia/src/pjmedia/portaudio/pa_trace.c
+++ b/pjmedia/src/pjmedia/portaudio/pa_trace.c
@@ -1,88 +1,88 @@
-/*
- * $Id: pa_trace.c,v 1.1.1.1.2.3 2003/09/20 21:09:15 rossbencina Exp $
- * Portable Audio I/O Library Trace Facility
- * Store trace information in real-time for later printing.
- *
- * Based on the Open Source API proposed by Ross Bencina
- * Copyright (c) 1999-2000 Phil Burk
- *
- * 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.
- *
- * Any person wishing to distribute modifications to the Software is
- * requested to send the modifications to the original developer so that
- * they can be incorporated into the canonical version.
- *
- * 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.
- */
-
-/** @file
- @brief Event trace mechanism for debugging.
-*/
-
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include "pa_trace.h"
-
-#if PA_TRACE_REALTIME_EVENTS
-
-static char *traceTextArray[MAX_TRACE_RECORDS];
-static int traceIntArray[MAX_TRACE_RECORDS];
-static int traceIndex = 0;
-static int traceBlock = 0;
-
-/*********************************************************************/
-void PaUtil_ResetTraceMessages()
-{
- traceIndex = 0;
-}
-
-/*********************************************************************/
-void PaUtil_DumpTraceMessages()
-{
- int i;
- int messageCount = (traceIndex < PA_MAX_TRACE_RECORDS) ? traceIndex : PA_MAX_TRACE_RECORDS;
-
- printf("DumpTraceMessages: traceIndex = %d\n", traceIndex );
- for( i=0; i<messageCount; i++ )
- {
- printf("%3d: %s = 0x%08X\n",
- i, traceTextArray[i], traceIntArray[i] );
- }
- ResetTraceMessages();
- fflush(stdout);
-}
-
-/*********************************************************************/
-void PaUtil_AddTraceMessage( const char *msg, int data )
-{
- if( (traceIndex == PA_MAX_TRACE_RECORDS) && (traceBlock == 0) )
- {
- traceBlock = 1;
- /* PaUtil_DumpTraceMessages(); */
- }
- else if( traceIndex < PA_MAX_TRACE_RECORDS )
- {
- traceTextArray[traceIndex] = msg;
- traceIntArray[traceIndex] = data;
- traceIndex++;
- }
-}
-
-#endif /* TRACE_REALTIME_EVENTS */
+/*
+ * $Id: pa_trace.c,v 1.1.1.1.2.3 2003/09/20 21:09:15 rossbencina Exp $
+ * Portable Audio I/O Library Trace Facility
+ * Store trace information in real-time for later printing.
+ *
+ * Based on the Open Source API proposed by Ross Bencina
+ * Copyright (c) 1999-2000 Phil Burk
+ *
+ * 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.
+ *
+ * Any person wishing to distribute modifications to the Software is
+ * requested to send the modifications to the original developer so that
+ * they can be incorporated into the canonical version.
+ *
+ * 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.
+ */
+
+/** @file
+ @brief Event trace mechanism for debugging.
+*/
+
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include "pa_trace.h"
+
+#if PA_TRACE_REALTIME_EVENTS
+
+static char *traceTextArray[MAX_TRACE_RECORDS];
+static int traceIntArray[MAX_TRACE_RECORDS];
+static int traceIndex = 0;
+static int traceBlock = 0;
+
+/*********************************************************************/
+void PaUtil_ResetTraceMessages()
+{
+ traceIndex = 0;
+}
+
+/*********************************************************************/
+void PaUtil_DumpTraceMessages()
+{
+ int i;
+ int messageCount = (traceIndex < PA_MAX_TRACE_RECORDS) ? traceIndex : PA_MAX_TRACE_RECORDS;
+
+ printf("DumpTraceMessages: traceIndex = %d\n", traceIndex );
+ for( i=0; i<messageCount; i++ )
+ {
+ printf("%3d: %s = 0x%08X\n",
+ i, traceTextArray[i], traceIntArray[i] );
+ }
+ ResetTraceMessages();
+ fflush(stdout);
+}
+
+/*********************************************************************/
+void PaUtil_AddTraceMessage( const char *msg, int data )
+{
+ if( (traceIndex == PA_MAX_TRACE_RECORDS) && (traceBlock == 0) )
+ {
+ traceBlock = 1;
+ /* PaUtil_DumpTraceMessages(); */
+ }
+ else if( traceIndex < PA_MAX_TRACE_RECORDS )
+ {
+ traceTextArray[traceIndex] = msg;
+ traceIntArray[traceIndex] = data;
+ traceIndex++;
+ }
+}
+
+#endif /* TRACE_REALTIME_EVENTS */
diff --git a/pjmedia/src/pjmedia/portaudio/pa_trace.h b/pjmedia/src/pjmedia/portaudio/pa_trace.h
index 1faaa385..f72a78b3 100644
--- a/pjmedia/src/pjmedia/portaudio/pa_trace.h
+++ b/pjmedia/src/pjmedia/portaudio/pa_trace.h
@@ -1,70 +1,70 @@
-#ifndef PA_TRACE_H
-#define PA_TRACE_H
-/*
- * $Id: pa_trace.h,v 1.1.1.1.2.3 2003/09/20 21:09:15 rossbencina Exp $
- * Portable Audio I/O Library Trace Facility
- * Store trace information in real-time for later printing.
- *
- * Based on the Open Source API proposed by Ross Bencina
- * Copyright (c) 1999-2000 Phil Burk
- *
- * 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.
- *
- * Any person wishing to distribute modifications to the Software is
- * requested to send the modifications to the original developer so that
- * they can be incorporated into the canonical version.
- *
- * 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.
- */
-
-/** @file
- @brief Event trace mechanism for debugging.
-
- Allows data to be written to the buffer at interrupt time and dumped later.
-*/
-
-
-#define PA_TRACE_REALTIME_EVENTS (0) /* Keep log of various real-time events. */
-#define PA_MAX_TRACE_RECORDS (2048)
-
-#ifdef __cplusplus
-extern "C"
-{
-#endif /* __cplusplus */
-
-
-#if PA_TRACE_REALTIME_EVENTS
-
-void PaUtil_ResetTraceMessages();
-void PaUtil_AddTraceMessage( const char *msg, int data );
-void PaUtil_DumpTraceMessages();
-
-#else
-
-#define PaUtil_ResetTraceMessages() /* noop */
-#define PaUtil_AddTraceMessage(msg,data) /* noop */
-#define PaUtil_DumpTraceMessages() /* noop */
-
-#endif
-
-
-#ifdef __cplusplus
-}
-#endif /* __cplusplus */
-
-#endif /* PA_TRACE_H */
+#ifndef PA_TRACE_H
+#define PA_TRACE_H
+/*
+ * $Id: pa_trace.h,v 1.1.1.1.2.3 2003/09/20 21:09:15 rossbencina Exp $
+ * Portable Audio I/O Library Trace Facility
+ * Store trace information in real-time for later printing.
+ *
+ * Based on the Open Source API proposed by Ross Bencina
+ * Copyright (c) 1999-2000 Phil Burk
+ *
+ * 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.
+ *
+ * Any person wishing to distribute modifications to the Software is
+ * requested to send the modifications to the original developer so that
+ * they can be incorporated into the canonical version.
+ *
+ * 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.
+ */
+
+/** @file
+ @brief Event trace mechanism for debugging.
+
+ Allows data to be written to the buffer at interrupt time and dumped later.
+*/
+
+
+#define PA_TRACE_REALTIME_EVENTS (0) /* Keep log of various real-time events. */
+#define PA_MAX_TRACE_RECORDS (2048)
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif /* __cplusplus */
+
+
+#if PA_TRACE_REALTIME_EVENTS
+
+void PaUtil_ResetTraceMessages();
+void PaUtil_AddTraceMessage( const char *msg, int data );
+void PaUtil_DumpTraceMessages();
+
+#else
+
+#define PaUtil_ResetTraceMessages() /* noop */
+#define PaUtil_AddTraceMessage(msg,data) /* noop */
+#define PaUtil_DumpTraceMessages() /* noop */
+
+#endif
+
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+#endif /* PA_TRACE_H */
diff --git a/pjmedia/src/pjmedia/portaudio/pa_types.h b/pjmedia/src/pjmedia/portaudio/pa_types.h
index 44c0a5f8..343dc8cf 100644
--- a/pjmedia/src/pjmedia/portaudio/pa_types.h
+++ b/pjmedia/src/pjmedia/portaudio/pa_types.h
@@ -1,65 +1,65 @@
-#ifndef PA_TYPES_H
-#define PA_TYPES_H
-
-/*
- SIZEOF_SHORT, SIZEOF_INT and SIZEOF_LONG are set by the configure script
- when it is used. Otherwise we default to the common 32 bit values, if your
- platform doesn't use configure, and doesn't use the default values below
- you will need to explicitly define these symbols in your make file.
-
- A PA_VALIDATE_SIZES macro is provided to assert that the values set in this
- file are correct.
-*/
-
-#ifndef SIZEOF_SHORT
-#define SIZEOF_SHORT 2
-#endif
-
-#ifndef SIZEOF_INT
-#define SIZEOF_INT 4
-#endif
-
-#ifndef SIZEOF_LONG
-#define SIZEOF_LONG 4
-#endif
-
-
-#if SIZEOF_SHORT == 2
-typedef signed short PaInt16;
-typedef unsigned short PaUint16;
-#elif SIZEOF_INT == 2
-typedef signed int PaInt16;
-typedef unsigned int PaUint16;
-#else
-#error pa_types.h was unable to determine which type to use for 16bit integers on the target platform
-#endif
-
-#if SIZEOF_SHORT == 4
-typedef signed short PaInt32;
-typedef unsigned short PaUint32;
-#elif SIZEOF_INT == 4
-typedef signed int PaInt32;
-typedef unsigned int PaUint32;
-#elif SIZEOF_LONG == 4
-typedef signed long PaInt32;
-typedef unsigned long PaUint32;
-#else
-#error pa_types.h was unable to determine which type to use for 32bit integers on the target platform
-#endif
-
-
-/* PA_VALIDATE_TYPE_SIZES compares the size of the integer types at runtime to
- ensure that PortAudio was configured correctly, and raises an assertion if
- they don't match the expected values. <assert.h> must be included in the
- context in which this macro is used.
-*/
-#define PA_VALIDATE_TYPE_SIZES \
- { \
- assert( "PortAudio: type sizes are not correct in pa_types.h" && sizeof( PaUint16 ) == 2 ); \
- assert( "PortAudio: type sizes are not correct in pa_types.h" && sizeof( PaInt16 ) == 2 ); \
- assert( "PortAudio: type sizes are not correct in pa_types.h" && sizeof( PaUint32 ) == 4 ); \
- assert( "PortAudio: type sizes are not correct in pa_types.h" && sizeof( PaInt32 ) == 4 ); \
- }
-
-
-#endif /* PA_TYPES_H */
+#ifndef PA_TYPES_H
+#define PA_TYPES_H
+
+/*
+ SIZEOF_SHORT, SIZEOF_INT and SIZEOF_LONG are set by the configure script
+ when it is used. Otherwise we default to the common 32 bit values, if your
+ platform doesn't use configure, and doesn't use the default values below
+ you will need to explicitly define these symbols in your make file.
+
+ A PA_VALIDATE_SIZES macro is provided to assert that the values set in this
+ file are correct.
+*/
+
+#ifndef SIZEOF_SHORT
+#define SIZEOF_SHORT 2
+#endif
+
+#ifndef SIZEOF_INT
+#define SIZEOF_INT 4
+#endif
+
+#ifndef SIZEOF_LONG
+#define SIZEOF_LONG 4
+#endif
+
+
+#if SIZEOF_SHORT == 2
+typedef signed short PaInt16;
+typedef unsigned short PaUint16;
+#elif SIZEOF_INT == 2
+typedef signed int PaInt16;
+typedef unsigned int PaUint16;
+#else
+#error pa_types.h was unable to determine which type to use for 16bit integers on the target platform
+#endif
+
+#if SIZEOF_SHORT == 4
+typedef signed short PaInt32;
+typedef unsigned short PaUint32;
+#elif SIZEOF_INT == 4
+typedef signed int PaInt32;
+typedef unsigned int PaUint32;
+#elif SIZEOF_LONG == 4
+typedef signed long PaInt32;
+typedef unsigned long PaUint32;
+#else
+#error pa_types.h was unable to determine which type to use for 32bit integers on the target platform
+#endif
+
+
+/* PA_VALIDATE_TYPE_SIZES compares the size of the integer types at runtime to
+ ensure that PortAudio was configured correctly, and raises an assertion if
+ they don't match the expected values. <assert.h> must be included in the
+ context in which this macro is used.
+*/
+#define PA_VALIDATE_TYPE_SIZES \
+ { \
+ assert( "PortAudio: type sizes are not correct in pa_types.h" && sizeof( PaUint16 ) == 2 ); \
+ assert( "PortAudio: type sizes are not correct in pa_types.h" && sizeof( PaInt16 ) == 2 ); \
+ assert( "PortAudio: type sizes are not correct in pa_types.h" && sizeof( PaUint32 ) == 4 ); \
+ assert( "PortAudio: type sizes are not correct in pa_types.h" && sizeof( PaInt32 ) == 4 ); \
+ }
+
+
+#endif /* PA_TYPES_H */
diff --git a/pjmedia/src/pjmedia/portaudio/pa_unix_hostapis.c b/pjmedia/src/pjmedia/portaudio/pa_unix_hostapis.c
index ac94b095..9bddc2e0 100644
--- a/pjmedia/src/pjmedia/portaudio/pa_unix_hostapis.c
+++ b/pjmedia/src/pjmedia/portaudio/pa_unix_hostapis.c
@@ -1,64 +1,64 @@
-/*
- * $Id: pa_unix_hostapis.c,v 1.1.2.5 2003/10/02 12:35:46 pieter Exp $
- * Portable Audio I/O Library UNIX initialization table
- *
- * Based on the Open Source API proposed by Ross Bencina
- * Copyright (c) 1999-2002 Ross Bencina, Phil Burk
- *
- * 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.
- *
- * Any person wishing to distribute modifications to the Software is
- * requested to send the modifications to the original developer so that
- * they can be incorporated into the canonical version.
- *
- * 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 "pa_hostapi.h"
-
-PaError PaJack_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiIndex index );
-PaError PaAlsa_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiIndex index );
-PaError PaOSS_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiIndex index );
-/* Added for IRIX, Pieter, oct 2, 2003: */
-PaError PaSGI_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiIndex index );
-
-
-PaUtilHostApiInitializer *paHostApiInitializers[] =
- {
-#ifdef PA_USE_OSS
- PaOSS_Initialize,
-#endif
-
-#ifdef PA_USE_ALSA
- PaAlsa_Initialize,
-#endif
-
-#ifdef PA_USE_JACK
- PaJack_Initialize,
-#endif
- /* Added for IRIX, Pieter, oct 2, 2003: */
-#ifdef PA_USE_SGI
- PaSGI_Initialize,
-#endif
- 0 /* NULL terminated array */
- };
-
-int paDefaultHostApiIndex = 0;
-
-
+/*
+ * $Id: pa_unix_hostapis.c,v 1.1.2.5 2003/10/02 12:35:46 pieter Exp $
+ * Portable Audio I/O Library UNIX initialization table
+ *
+ * Based on the Open Source API proposed by Ross Bencina
+ * Copyright (c) 1999-2002 Ross Bencina, Phil Burk
+ *
+ * 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.
+ *
+ * Any person wishing to distribute modifications to the Software is
+ * requested to send the modifications to the original developer so that
+ * they can be incorporated into the canonical version.
+ *
+ * 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 "pa_hostapi.h"
+
+PaError PaJack_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiIndex index );
+PaError PaAlsa_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiIndex index );
+PaError PaOSS_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiIndex index );
+/* Added for IRIX, Pieter, oct 2, 2003: */
+PaError PaSGI_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiIndex index );
+
+
+PaUtilHostApiInitializer *paHostApiInitializers[] =
+ {
+#ifdef PA_USE_OSS
+ PaOSS_Initialize,
+#endif
+
+#ifdef PA_USE_ALSA
+ PaAlsa_Initialize,
+#endif
+
+#ifdef PA_USE_JACK
+ PaJack_Initialize,
+#endif
+ /* Added for IRIX, Pieter, oct 2, 2003: */
+#ifdef PA_USE_SGI
+ PaSGI_Initialize,
+#endif
+ 0 /* NULL terminated array */
+ };
+
+int paDefaultHostApiIndex = 0;
+
+
diff --git a/pjmedia/src/pjmedia/portaudio/pa_unix_oss.c b/pjmedia/src/pjmedia/portaudio/pa_unix_oss.c
index 82d8c931..45c49da0 100644
--- a/pjmedia/src/pjmedia/portaudio/pa_unix_oss.c
+++ b/pjmedia/src/pjmedia/portaudio/pa_unix_oss.c
@@ -1,1917 +1,1917 @@
-/*
- * $Id: pa_unix_oss.c,v 1.6.2.22 2005/03/08 21:26:53 aknudsen Exp $
- * PortAudio Portable Real-Time Audio Library
- * Latest Version at: http://www.portaudio.com
- * OSS implementation by:
- * Douglas Repetto
- * Phil Burk
- * Dominic Mazzoni
- * Arve Knudsen
- *
- * Based on the Open Source API proposed by Ross Bencina
- * Copyright (c) 1999-2002 Ross Bencina, Phil Burk
- *
- * 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.
- *
- * Any person wishing to distribute modifications to the Software is
- * requested to send the modifications to the original developer so that
- * they can be incorporated into the canonical version.
- *
- * 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 <stdio.h>
-#include <string.h>
-#include <math.h>
-#include <fcntl.h>
-#include <sys/ioctl.h>
-#include <unistd.h>
-#include <pthread.h>
-#include <alloca.h>
-#include <malloc.h>
-#include <assert.h>
-#include <errno.h>
-#include <sys/types.h>
-#include <sys/stat.h>
-#include <sys/poll.h>
-#include <limits.h>
-#include <semaphore.h>
-
-#ifdef __linux__
-# include <linux/soundcard.h>
-# define DEVICE_NAME_BASE "/dev/dsp"
-#else
-# include <machine/soundcard.h> /* JH20010905 */
-# define DEVICE_NAME_BASE "/dev/audio"
-#endif
-
-#include "portaudio.h"
-#include "pa_util.h"
-#include "pa_allocation.h"
-#include "pa_hostapi.h"
-#include "pa_stream.h"
-#include "pa_cpuload.h"
-#include "pa_process.h"
-#include "pa_unix_util.h"
-
-static int sysErr_;
-static pthread_t mainThread_;
-
-/* Check return value of system call, and map it to PaError */
-#define ENSURE_(expr, code) \
- do { \
- if( UNLIKELY( (sysErr_ = (expr)) < 0 ) ) \
- { \
- /* PaUtil_SetLastHostErrorInfo should only be used in the main thread */ \
- if( (code) == paUnanticipatedHostError && pthread_self() == mainThread_ ) \
- { \
- PaUtil_SetLastHostErrorInfo( paALSA, sysErr_, strerror( errno ) ); \
- } \
- \
- PaUtil_DebugPrint(( "Expression '" #expr "' failed in '" __FILE__ "', line: " STRINGIZE( __LINE__ ) "\n" )); \
- result = (code); \
- goto error; \
- } \
- } while( 0 );
-
-#ifndef AFMT_S16_NE
-#define AFMT_S16_NE Get_AFMT_S16_NE()
-/*********************************************************************
- * Some versions of OSS do not define AFMT_S16_NE. So check CPU.
- * PowerPC is Big Endian. X86 is Little Endian.
- */
-static int Get_AFMT_S16_NE( void )
-{
- long testData = 1;
- char *ptr = (char *) &testData;
- int isLittle = ( *ptr == 1 ); /* Does address point to least significant byte? */
- return isLittle ? AFMT_S16_LE : AFMT_S16_BE;
-}
-#endif
-
-/* PaOSSHostApiRepresentation - host api datastructure specific to this implementation */
-
-typedef struct
-{
- PaUtilHostApiRepresentation inheritedHostApiRep;
- PaUtilStreamInterface callbackStreamInterface;
- PaUtilStreamInterface blockingStreamInterface;
-
- PaUtilAllocationGroup *allocations;
-
- PaHostApiIndex hostApiIndex;
-}
-PaOSSHostApiRepresentation;
-
-/** Per-direction structure for PaOssStream.
- *
- * Aspect StreamChannels: In case the user requests to open the same device for both capture and playback,
- * but with different number of channels we will have to adapt between the number of user and host
- * channels for at least one direction, since the configuration space is the same for both directions
- * of an OSS device.
- */
-typedef struct
-{
- int fd;
- const char *devName;
- int userChannelCount, hostChannelCount;
- int userInterleaved;
- void *buffer;
- PaSampleFormat userFormat, hostFormat;
- double latency;
- unsigned long hostFrames, numBufs;
- void **userBuffers; /* For non-interleaved blocking */
-} PaOssStreamComponent;
-
-/** Implementation specific representation of a PaStream.
- *
- */
-typedef struct PaOssStream
-{
- PaUtilStreamRepresentation streamRepresentation;
- PaUtilCpuLoadMeasurer cpuLoadMeasurer;
- PaUtilBufferProcessor bufferProcessor;
-
- PaUtilThreading threading;
-
- int sharedDevice;
- unsigned long framesPerHostBuffer;
- int triggered; /* Have the devices been triggered yet (first start) */
-
- int isActive;
- int isStopped;
-
- int lastPosPtr;
- double lastStreamBytes;
-
- int framesProcessed;
-
- double sampleRate;
-
- int callbackMode;
- int callbackStop, callbackAbort;
-
- PaOssStreamComponent *capture, *playback;
- unsigned long pollTimeout;
- sem_t semaphore;
-}
-PaOssStream;
-
-typedef enum {
- StreamMode_In,
- StreamMode_Out
-} StreamMode;
-
-/* prototypes for functions declared in this file */
-
-static void Terminate( struct PaUtilHostApiRepresentation *hostApi );
-static PaError IsFormatSupported( struct PaUtilHostApiRepresentation *hostApi,
- const PaStreamParameters *inputParameters,
- const PaStreamParameters *outputParameters,
- double sampleRate );
-static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi,
- PaStream** s,
- const PaStreamParameters *inputParameters,
- const PaStreamParameters *outputParameters,
- double sampleRate,
- unsigned long framesPerBuffer,
- PaStreamFlags streamFlags,
- PaStreamCallback *streamCallback,
- void *userData );
-static PaError CloseStream( PaStream* stream );
-static PaError StartStream( PaStream *stream );
-static PaError StopStream( PaStream *stream );
-static PaError AbortStream( PaStream *stream );
-static PaError IsStreamStopped( PaStream *s );
-static PaError IsStreamActive( PaStream *stream );
-static PaTime GetStreamTime( PaStream *stream );
-static double GetStreamCpuLoad( PaStream* stream );
-static PaError ReadStream( PaStream* stream, void *buffer, unsigned long frames );
-static PaError WriteStream( PaStream* stream, const void *buffer, unsigned long frames );
-static signed long GetStreamReadAvailable( PaStream* stream );
-static signed long GetStreamWriteAvailable( PaStream* stream );
-static PaError BuildDeviceList( PaOSSHostApiRepresentation *hostApi );
-
-
-/** Initialize the OSS API implementation.
- *
- * This function will initialize host API datastructures and query host devices for information.
- *
- * Aspect DeviceCapabilities: Enumeration of host API devices is initiated from here
- *
- * Aspect FreeResources: If an error is encountered under way we have to free each resource allocated in this function,
- * this happens with the usual "error" label.
- */
-PaError PaOSS_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiIndex hostApiIndex )
-{
- PaError result = paNoError;
- PaOSSHostApiRepresentation *ossHostApi = NULL;
-
- PA_UNLESS( ossHostApi = (PaOSSHostApiRepresentation*)PaUtil_AllocateMemory( sizeof(PaOSSHostApiRepresentation) ),
- paInsufficientMemory );
- PA_UNLESS( ossHostApi->allocations = PaUtil_CreateAllocationGroup(), paInsufficientMemory );
- ossHostApi->hostApiIndex = hostApiIndex;
-
- /* Initialize host API structure */
- *hostApi = &ossHostApi->inheritedHostApiRep;
- (*hostApi)->info.structVersion = 1;
- (*hostApi)->info.type = paOSS;
- (*hostApi)->info.name = "OSS";
- (*hostApi)->Terminate = Terminate;
- (*hostApi)->OpenStream = OpenStream;
- (*hostApi)->IsFormatSupported = IsFormatSupported;
-
- PA_ENSURE( BuildDeviceList( ossHostApi ) );
-
- PaUtil_InitializeStreamInterface( &ossHostApi->callbackStreamInterface, CloseStream, StartStream,
- StopStream, AbortStream, IsStreamStopped, IsStreamActive,
- GetStreamTime, GetStreamCpuLoad,
- PaUtil_DummyRead, PaUtil_DummyWrite,
- PaUtil_DummyGetReadAvailable,
- PaUtil_DummyGetWriteAvailable );
-
- PaUtil_InitializeStreamInterface( &ossHostApi->blockingStreamInterface, CloseStream, StartStream,
- StopStream, AbortStream, IsStreamStopped, IsStreamActive,
- GetStreamTime, PaUtil_DummyGetCpuLoad,
- ReadStream, WriteStream, GetStreamReadAvailable, GetStreamWriteAvailable );
-
- mainThread_ = pthread_self();
-
- return result;
-
-error:
- if( ossHostApi )
- {
- if( ossHostApi->allocations )
- {
- PaUtil_FreeAllAllocations( ossHostApi->allocations );
- PaUtil_DestroyAllocationGroup( ossHostApi->allocations );
- }
-
- PaUtil_FreeMemory( ossHostApi );
- }
- return result;
-}
-
-PaError PaUtil_InitializeDeviceInfo( PaDeviceInfo *deviceInfo, const char *name, PaHostApiIndex hostApiIndex, int maxInputChannels,
- int maxOutputChannels, PaTime defaultLowInputLatency, PaTime defaultLowOutputLatency, PaTime defaultHighInputLatency,
- PaTime defaultHighOutputLatency, double defaultSampleRate, PaUtilAllocationGroup *allocations )
-{
- PaError result = paNoError;
-
- deviceInfo->structVersion = 2;
- if( allocations )
- {
- size_t len = strlen( name ) + 1;
- PA_UNLESS( deviceInfo->name = PaUtil_GroupAllocateMemory( allocations, len ), paInsufficientMemory );
- strncpy( (char *)deviceInfo->name, name, len );
- }
- else
- deviceInfo->name = name;
-
- deviceInfo->hostApi = hostApiIndex;
- deviceInfo->maxInputChannels = maxInputChannels;
- deviceInfo->maxOutputChannels = maxOutputChannels;
- deviceInfo->defaultLowInputLatency = defaultLowInputLatency;
- deviceInfo->defaultLowOutputLatency = defaultLowOutputLatency;
- deviceInfo->defaultHighInputLatency = defaultHighInputLatency;
- deviceInfo->defaultHighOutputLatency = defaultHighOutputLatency;
- deviceInfo->defaultSampleRate = defaultSampleRate;
-
-error:
- return result;
-}
-
-static PaError QueryDirection( const char *deviceName, StreamMode mode, double *defaultSampleRate, int *maxChannelCount,
- double *defaultLowLatency, double *defaultHighLatency )
-{
- PaError result = paNoError;
- int numChannels, maxNumChannels;
- int busy = 0;
- int devHandle = -1;
- int sr;
- *maxChannelCount = 0; /* Default value in case this fails */
-
- if ( (devHandle = open( deviceName, (mode == StreamMode_In ? O_RDONLY : O_WRONLY) | O_NONBLOCK )) < 0 )
- {
- if( errno == EBUSY || errno == EAGAIN )
- {
- PA_DEBUG(( "%s: Device %s busy\n", __FUNCTION__, deviceName ));
- }
- else
- {
- PA_DEBUG(( "%s: Can't access device: %s\n", __FUNCTION__, strerror( errno ) ));
- }
-
- return paDeviceUnavailable;
- }
-
- /* Negotiate for the maximum number of channels for this device. PLB20010927
- * Consider up to 16 as the upper number of channels.
- * Variable maxNumChannels should contain the actual upper limit after the call.
- * Thanks to John Lazzaro and Heiko Purnhagen for suggestions.
- */
- maxNumChannels = 0;
- for( numChannels = 1; numChannels <= 16; numChannels++ )
- {
- int temp = numChannels;
- if( ioctl( devHandle, SNDCTL_DSP_CHANNELS, &temp ) < 0 )
- {
- busy = EAGAIN == errno || EBUSY == errno;
- /* ioctl() failed so bail out if we already have stereo */
- if( maxNumChannels >= 2 )
- break;
- }
- else
- {
- /* ioctl() worked but bail out if it does not support numChannels.
- * We don't want to leave gaps in the numChannels supported.
- */
- if( (numChannels > 2) && (temp != numChannels) )
- break;
- if( temp > maxNumChannels )
- maxNumChannels = temp; /* Save maximum. */
- }
- }
- /* A: We're able to open a device for capture if it's busy playing back and vice versa,
- * but we can't configure anything */
- if( 0 == maxNumChannels && busy )
- {
- result = paDeviceUnavailable;
- goto error;
- }
-
- /* The above negotiation may fail for an old driver so try this older technique. */
- if( maxNumChannels < 1 )
- {
- int stereo = 1;
- if( ioctl( devHandle, SNDCTL_DSP_STEREO, &stereo ) < 0 )
- {
- maxNumChannels = 1;
- }
- else
- {
- maxNumChannels = (stereo) ? 2 : 1;
- }
- PA_DEBUG(( "%s: use SNDCTL_DSP_STEREO, maxNumChannels = %d\n", __FUNCTION__, maxNumChannels ))
- }
-
- /* During channel negotiation, the last ioctl() may have failed. This can
- * also cause sample rate negotiation to fail. Hence the following, to return
- * to a supported number of channels. SG20011005 */
- {
- /* use most reasonable default value */
- int temp = PA_MIN( maxNumChannels, 2 );
- ENSURE_( ioctl( devHandle, SNDCTL_DSP_CHANNELS, &temp ), paUnanticipatedHostError );
- }
-
- /* Get supported sample rate closest to 44100 Hz */
- if( *defaultSampleRate < 0 )
- {
- sr = 44100;
- if( ioctl( devHandle, SNDCTL_DSP_SPEED, &sr ) < 0 )
- {
- result = paUnanticipatedHostError;
- goto error;
- }
-
- *defaultSampleRate = sr;
- }
-
- *maxChannelCount = maxNumChannels;
- /* TODO */
- *defaultLowLatency = 512. / *defaultSampleRate;
- *defaultHighLatency = 2048. / *defaultSampleRate;
-
-error:
- if( devHandle >= 0 )
- close( devHandle );
-
- return result;
-}
-
-/** Query OSS device.
- *
- * This is where PaDeviceInfo objects are constructed and filled in with relevant information.
- *
- * Aspect DeviceCapabilities: The inferred device capabilities are recorded in a PaDeviceInfo object that is constructed
- * in place.
- */
-static PaError QueryDevice( char *deviceName, PaOSSHostApiRepresentation *ossApi, PaDeviceInfo **deviceInfo )
-{
- PaError result = paNoError;
- double sampleRate = -1.;
- int maxInputChannels, maxOutputChannels;
- PaTime defaultLowInputLatency, defaultLowOutputLatency, defaultHighInputLatency, defaultHighOutputLatency;
- PaError tmpRes = paNoError;
- int busy = 0;
- *deviceInfo = NULL;
-
- /* douglas:
- we have to do this querying in a slightly different order. apparently
- some sound cards will give you different info based on their settins.
- e.g. a card might give you stereo at 22kHz but only mono at 44kHz.
- the correct order for OSS is: format, channels, sample rate
- */
-
- /* Aspect StreamChannels: The number of channels supported for a device may depend on the mode it is
- * opened in, it may have more channels available for capture than playback and vice versa. Therefore
- * we will open the device in both read- and write-only mode to determine the supported number.
- */
- if( (tmpRes = QueryDirection( deviceName, StreamMode_In, &sampleRate, &maxInputChannels, &defaultLowInputLatency,
- &defaultHighInputLatency )) != paNoError )
- {
- if( tmpRes != paDeviceUnavailable )
- {
- PA_DEBUG(( "%s: Querying device %s for capture failed!\n", __FUNCTION__, deviceName ));
- /* PA_ENSURE( tmpRes ); */
- }
- ++busy;
- }
- if( (tmpRes = QueryDirection( deviceName, StreamMode_Out, &sampleRate, &maxOutputChannels, &defaultLowOutputLatency,
- &defaultHighOutputLatency )) != paNoError )
- {
- if( tmpRes != paDeviceUnavailable )
- {
- PA_DEBUG(( "%s: Querying device %s for playback failed!\n", __FUNCTION__, deviceName ));
- /* PA_ENSURE( tmpRes ); */
- }
- ++busy;
- }
- assert( 0 <= busy && busy <= 2 );
- if( 2 == busy ) /* Both directions are unavailable to us */
- {
- result = paDeviceUnavailable;
- goto error;
- }
-
- PA_UNLESS( *deviceInfo = PaUtil_GroupAllocateMemory( ossApi->allocations, sizeof (PaDeviceInfo) ), paInsufficientMemory );
- PA_ENSURE( PaUtil_InitializeDeviceInfo( *deviceInfo, deviceName, ossApi->hostApiIndex, maxInputChannels, maxOutputChannels,
- defaultLowInputLatency, defaultLowOutputLatency, defaultHighInputLatency, defaultHighOutputLatency, sampleRate,
- ossApi->allocations ) );
-
-error:
- return result;
-}
-
-/** Query host devices.
- *
- * Loop over host devices and query their capabilitiesu
- *
- * Aspect DeviceCapabilities: This function calls QueryDevice on each device entry and receives a filled in PaDeviceInfo object
- * per device, these are placed in the host api representation's deviceInfos array.
- */
-static PaError BuildDeviceList( PaOSSHostApiRepresentation *ossApi )
-{
- PaError result = paNoError;
- PaUtilHostApiRepresentation *commonApi = &ossApi->inheritedHostApiRep;
- int i;
- int numDevices = 0, maxDeviceInfos = 1;
- PaDeviceInfo **deviceInfos = NULL;
-
- /* These two will be set to the first working input and output device, respectively */
- commonApi->info.defaultInputDevice = paNoDevice;
- commonApi->info.defaultOutputDevice = paNoDevice;
-
- /* Find devices by calling QueryDevice on each
- * potential device names. When we find a valid one,
- * add it to a linked list.
- * A: Can there only be 10 devices? */
-
- for( i = 0; i < 10; i++ )
- {
- char deviceName[32];
- PaDeviceInfo *deviceInfo;
- int testResult;
- struct stat stbuf;
-
- if( i == 0 )
- snprintf(deviceName, sizeof (deviceName), "%s", DEVICE_NAME_BASE);
- else
- snprintf(deviceName, sizeof (deviceName), "%s%d", DEVICE_NAME_BASE, i);
-
- /* PA_DEBUG(("PaOSS BuildDeviceList: trying device %s\n", deviceName )); */
- if( stat( deviceName, &stbuf ) < 0 )
- {
- if( ENOENT != errno )
- PA_DEBUG(( "%s: Error stat'ing %s: %s\n", __FUNCTION__, deviceName, strerror( errno ) ));
- continue;
- }
- if( (testResult = QueryDevice( deviceName, ossApi, &deviceInfo )) != paNoError )
- {
- if( testResult != paDeviceUnavailable )
- PA_ENSURE( testResult );
-
- continue;
- }
-
- ++numDevices;
- if( !deviceInfos || numDevices > maxDeviceInfos )
- {
- maxDeviceInfos *= 2;
- PA_UNLESS( deviceInfos = (PaDeviceInfo **) realloc( deviceInfos, maxDeviceInfos * sizeof (PaDeviceInfo *) ),
- paInsufficientMemory );
- }
- deviceInfos[numDevices - 1] = deviceInfo;
-
- if( commonApi->info.defaultInputDevice == paNoDevice && deviceInfo->maxInputChannels > 0 )
- commonApi->info.defaultInputDevice = i;
- if( commonApi->info.defaultOutputDevice == paNoDevice && deviceInfo->maxOutputChannels > 0 )
- commonApi->info.defaultOutputDevice = i;
- }
-
- /* Make an array of PaDeviceInfo pointers out of the linked list */
-
- PA_DEBUG(("PaOSS %s: Total number of devices found: %d\n", __FUNCTION__, numDevices));
-
- commonApi->deviceInfos = (PaDeviceInfo**)PaUtil_GroupAllocateMemory(
- ossApi->allocations, sizeof(PaDeviceInfo*) * numDevices );
- memcpy( commonApi->deviceInfos, deviceInfos, numDevices * sizeof (PaDeviceInfo *) );
-
- commonApi->info.deviceCount = numDevices;
-
-error:
- free( deviceInfos );
-
- return result;
-}
-
-static void Terminate( struct PaUtilHostApiRepresentation *hostApi )
-{
- PaOSSHostApiRepresentation *ossHostApi = (PaOSSHostApiRepresentation*)hostApi;
-
- if( ossHostApi->allocations )
- {
- PaUtil_FreeAllAllocations( ossHostApi->allocations );
- PaUtil_DestroyAllocationGroup( ossHostApi->allocations );
- }
-
- PaUtil_FreeMemory( ossHostApi );
-}
-
-static PaError IsFormatSupported( struct PaUtilHostApiRepresentation *hostApi,
- const PaStreamParameters *inputParameters,
- const PaStreamParameters *outputParameters,
- double sampleRate )
-{
- PaError result = paNoError;
- PaDeviceIndex device;
- PaDeviceInfo *deviceInfo;
- char *deviceName;
- int inputChannelCount, outputChannelCount;
- int tempDevHandle = -1;
- int flags;
- PaSampleFormat inputSampleFormat, outputSampleFormat;
-
- if( inputParameters )
- {
- inputChannelCount = inputParameters->channelCount;
- inputSampleFormat = inputParameters->sampleFormat;
-
- /* unless alternate device specification is supported, reject the use of
- paUseHostApiSpecificDeviceSpecification */
-
- if( inputParameters->device == paUseHostApiSpecificDeviceSpecification )
- return paInvalidDevice;
-
- /* check that input device can support inputChannelCount */
- if( inputChannelCount > hostApi->deviceInfos[ inputParameters->device ]->maxInputChannels )
- return paInvalidChannelCount;
-
- /* validate inputStreamInfo */
- if( inputParameters->hostApiSpecificStreamInfo )
- return paIncompatibleHostApiSpecificStreamInfo; /* this implementation doesn't use custom stream info */
- }
- else
- {
- inputChannelCount = 0;
- }
-
- if( outputParameters )
- {
- outputChannelCount = outputParameters->channelCount;
- outputSampleFormat = outputParameters->sampleFormat;
-
- /* unless alternate device specification is supported, reject the use of
- paUseHostApiSpecificDeviceSpecification */
-
- if( outputParameters->device == paUseHostApiSpecificDeviceSpecification )
- return paInvalidDevice;
-
- /* check that output device can support inputChannelCount */
- if( outputChannelCount > hostApi->deviceInfos[ outputParameters->device ]->maxOutputChannels )
- return paInvalidChannelCount;
-
- /* validate outputStreamInfo */
- if( outputParameters->hostApiSpecificStreamInfo )
- return paIncompatibleHostApiSpecificStreamInfo; /* this implementation doesn't use custom stream info */
- }
- else
- {
- outputChannelCount = 0;
- }
-
- if (inputChannelCount == 0 && outputChannelCount == 0)
- return paInvalidChannelCount;
-
- /* if full duplex, make sure that they're the same device */
-
- if (inputChannelCount > 0 && outputChannelCount > 0 &&
- inputParameters->device != outputParameters->device)
- return paInvalidDevice;
-
- /* if full duplex, also make sure that they're the same number of channels */
-
- if (inputChannelCount > 0 && outputChannelCount > 0 &&
- inputChannelCount != outputChannelCount)
- return paInvalidChannelCount;
-
- /* open the device so we can do more tests */
-
- if( inputChannelCount > 0 )
- {
- result = PaUtil_DeviceIndexToHostApiDeviceIndex(&device, inputParameters->device, hostApi);
- if (result != paNoError)
- return result;
- }
- else
- {
- result = PaUtil_DeviceIndexToHostApiDeviceIndex(&device, outputParameters->device, hostApi);
- if (result != paNoError)
- return result;
- }
-
- deviceInfo = hostApi->deviceInfos[device];
- deviceName = (char *)deviceInfo->name;
-
- flags = O_NONBLOCK;
- if (inputChannelCount > 0 && outputChannelCount > 0)
- flags |= O_RDWR;
- else if (inputChannelCount > 0)
- flags |= O_RDONLY;
- else
- flags |= O_WRONLY;
-
- ENSURE_( tempDevHandle = open( deviceInfo->name, flags ), paDeviceUnavailable );
-
- /* PaOssStream_Configure will do the rest of the checking for us */
- /* PA_ENSURE( PaOssStream_Configure( tempDevHandle, deviceName, outputChannelCount, &sampleRate ) ); */
-
- /* everything succeeded! */
-
- error:
- if( tempDevHandle >= 0 )
- close( tempDevHandle );
-
- return result;
-}
-
-/** Validate stream parameters.
- *
- * Aspect StreamChannels: We verify that the number of channels is within the allowed range for the device
- */
-static PaError ValidateParameters( const PaStreamParameters *parameters, const PaDeviceInfo *deviceInfo, StreamMode mode )
-{
- int maxChans;
-
- assert( parameters );
-
- if( parameters->device == paUseHostApiSpecificDeviceSpecification )
- {
- return paInvalidDevice;
- }
-
- maxChans = (mode == StreamMode_In ? deviceInfo->maxInputChannels :
- deviceInfo->maxOutputChannels);
- if( parameters->channelCount > maxChans )
- {
- return paInvalidChannelCount;
- }
-
- return paNoError;
-}
-
-static PaError PaOssStreamComponent_Initialize( PaOssStreamComponent *component, const PaStreamParameters *parameters,
- int callbackMode, int fd, const char *deviceName )
-{
- PaError result = paNoError;
- assert( component );
-
- memset( component, 0, sizeof (PaOssStreamComponent) );
-
- component->fd = fd;
- component->devName = deviceName;
- component->userChannelCount = parameters->channelCount;
- component->userFormat = parameters->sampleFormat;
- component->latency = parameters->suggestedLatency;
- component->userInterleaved = !(parameters->sampleFormat & paNonInterleaved);
-
- if( !callbackMode && !component->userInterleaved )
- {
- /* Pre-allocate non-interleaved user provided buffers */
- PA_UNLESS( component->userBuffers = PaUtil_AllocateMemory( sizeof (void *) * component->userChannelCount ),
- paInsufficientMemory );
- }
-
-error:
- return result;
-}
-
-static void PaOssStreamComponent_Terminate( PaOssStreamComponent *component )
-{
- assert( component );
-
- if( component->fd >= 0 )
- close( component->fd );
- if( component->buffer )
- PaUtil_FreeMemory( component->buffer );
-
- if( component->userBuffers )
- PaUtil_FreeMemory( component->userBuffers );
-
- PaUtil_FreeMemory( component );
-}
-
-static PaError ModifyBlocking( int fd, int blocking )
-{
- PaError result = paNoError;
- int fflags;
-
- ENSURE_( fflags = fcntl( fd, F_GETFL ), paUnanticipatedHostError );
-
- if( blocking )
- fflags &= ~O_NONBLOCK;
- else
- fflags |= O_NONBLOCK;
-
- ENSURE_( fcntl( fd, F_SETFL, fflags ), paUnanticipatedHostError );
-
-error:
- return result;
-}
-
-static PaError OpenDevices( const char *idevName, const char *odevName, int *idev, int *odev )
-{
- PaError result = paNoError;
- int flags = O_NONBLOCK, duplex = 0;
- int enableBits = 0;
- *idev = *odev = -1;
-
- if( idevName && odevName )
- {
- duplex = 1;
- flags |= O_RDWR;
- }
- else if( idevName )
- flags |= O_RDONLY;
- else
- flags |= O_WRONLY;
-
- /* open first in nonblocking mode, in case it's busy...
- * A: then unset the non-blocking attribute */
- assert( flags & O_NONBLOCK );
- if( idevName )
- {
- ENSURE_( *idev = open( idevName, flags ), paDeviceUnavailable );
- PA_ENSURE( ModifyBlocking( *idev, 1 ) ); /* Blocking */
-
- /* Initially disable */
- enableBits = ~PCM_ENABLE_INPUT;
- ENSURE_( ioctl( *idev, SNDCTL_DSP_SETTRIGGER, &enableBits ), paUnanticipatedHostError );
- }
- if( odevName )
- {
- if( !idevName )
- {
- ENSURE_( *odev = open( odevName, flags ), paDeviceUnavailable );
- PA_ENSURE( ModifyBlocking( *odev, 1 ) ); /* Blocking */
-
- /* Initially disable */
- enableBits = ~PCM_ENABLE_OUTPUT;
- ENSURE_( ioctl( *odev, SNDCTL_DSP_SETTRIGGER, &enableBits ), paUnanticipatedHostError );
- }
- else
- {
- ENSURE_( *odev = dup( *idev ), paUnanticipatedHostError );
- }
- }
-
-error:
- return result;
-}
-
-static PaError PaOssStream_Initialize( PaOssStream *stream, const PaStreamParameters *inputParameters, const PaStreamParameters *outputParameters,
- PaStreamCallback callback, void *userData, PaStreamFlags streamFlags,
- PaOSSHostApiRepresentation *ossApi )
-{
- PaError result = paNoError;
- int idev, odev;
- PaUtilHostApiRepresentation *hostApi = &ossApi->inheritedHostApiRep;
- const char *idevName = NULL, *odevName = NULL;
-
- assert( stream );
-
- memset( stream, 0, sizeof (PaOssStream) );
- stream->isStopped = 1;
-
- PA_ENSURE( PaUtil_InitializeThreading( &stream->threading ) );
-
- if( inputParameters && outputParameters )
- {
- if( inputParameters->device == outputParameters->device )
- stream->sharedDevice = 1;
- }
-
- if( inputParameters )
- idevName = hostApi->deviceInfos[inputParameters->device]->name;
- if( outputParameters )
- odevName = hostApi->deviceInfos[outputParameters->device]->name;
- PA_ENSURE( OpenDevices( idevName, odevName, &idev, &odev ) );
- if( inputParameters )
- {
- PA_UNLESS( stream->capture = PaUtil_AllocateMemory( sizeof (PaOssStreamComponent) ), paInsufficientMemory );
- PA_ENSURE( PaOssStreamComponent_Initialize( stream->capture, inputParameters, callback != NULL, idev, idevName ) );
- }
- if( outputParameters )
- {
- PA_UNLESS( stream->playback = PaUtil_AllocateMemory( sizeof (PaOssStreamComponent) ), paInsufficientMemory );
- PA_ENSURE( PaOssStreamComponent_Initialize( stream->playback, outputParameters, callback != NULL, odev, odevName ) );
- }
-
- if( callback != NULL )
- {
- PaUtil_InitializeStreamRepresentation( &stream->streamRepresentation,
- &ossApi->callbackStreamInterface, callback, userData );
- stream->callbackMode = 1;
- }
- else
- {
- PaUtil_InitializeStreamRepresentation( &stream->streamRepresentation,
- &ossApi->blockingStreamInterface, callback, userData );
- }
-
- ENSURE_( sem_init( &stream->semaphore, 0, 0 ), paInternalError );
-
-error:
- return result;
-}
-
-static void PaOssStream_Terminate( PaOssStream *stream )
-{
- assert( stream );
-
- PaUtil_TerminateStreamRepresentation( &stream->streamRepresentation );
- PaUtil_TerminateThreading( &stream->threading );
-
- if( stream->capture )
- PaOssStreamComponent_Terminate( stream->capture );
- if( stream->playback )
- PaOssStreamComponent_Terminate( stream->playback );
-
- sem_destroy( &stream->semaphore );
-
- PaUtil_FreeMemory( stream );
-}
-
-/** Translate from PA format to OSS native.
- *
- */
-static PaError Pa2OssFormat( PaSampleFormat paFormat, int *ossFormat )
-{
- switch( paFormat )
- {
- case paUInt8:
- *ossFormat = AFMT_U8;
- break;
- case paInt8:
- *ossFormat = AFMT_S8;
- break;
- case paInt16:
- *ossFormat = AFMT_S16_NE;
- break;
- default:
- return paInternalError; /* This shouldn't happen */
- }
-
- return paNoError;
-}
-
-/** Return the PA-compatible formats that this device can support.
- *
- */
-static PaError GetAvailableFormats( PaOssStreamComponent *component, PaSampleFormat *availableFormats )
-{
- PaError result = paNoError;
- int mask = 0;
- PaSampleFormat frmts = 0;
-
- ENSURE_( ioctl( component->fd, SNDCTL_DSP_GETFMTS, &mask ), paUnanticipatedHostError );
- if( mask & AFMT_U8 )
- frmts |= paUInt8;
- if( mask & AFMT_S8 )
- frmts |= paInt8;
- if( mask & AFMT_S16_NE )
- frmts |= paInt16;
- else
- result = paSampleFormatNotSupported;
-
- *availableFormats = frmts;
-
-error:
- return result;
-}
-
-static unsigned int PaOssStreamComponent_FrameSize( PaOssStreamComponent *component )
-{
- return Pa_GetSampleSize( component->hostFormat ) * component->hostChannelCount;
-}
-
-/** Buffer size in bytes.
- *
- */
-static unsigned long PaOssStreamComponent_BufferSize( PaOssStreamComponent *component )
-{
- return PaOssStreamComponent_FrameSize( component ) * component->hostFrames * component->numBufs;
-}
-
-static int CalcHigherLogTwo( int n )
-{
- int log2 = 0;
- while( (1<<log2) < n ) log2++;
- return log2;
-}
-
-static PaError PaOssStreamComponent_Configure( PaOssStreamComponent *component, double sampleRate, unsigned long framesPerBuffer,
- StreamMode streamMode, PaOssStreamComponent *master )
-{
- PaError result = paNoError;
- int temp, nativeFormat;
- int sr = (int)sampleRate;
- PaSampleFormat availableFormats, hostFormat;
- int chans = component->userChannelCount;
- int frgmt;
- int numBufs;
- int bytesPerBuf;
- double bufSz;
- unsigned long fragSz;
- audio_buf_info bufInfo;
-
- /* We may have a situation where only one component (the master) is configured, if both point to the same device.
- * In that case, the second component will copy settings from the other */
- if( !master )
- {
- /* Aspect BufferSettings: If framesPerBuffer is unspecified we have to infer a suitable fragment size.
- * The hardware need not respect the requested fragment size, so we may have to adapt.
- */
- if( framesPerBuffer == paFramesPerBufferUnspecified )
- {
- bufSz = component->latency * sampleRate;
- fragSz = bufSz / 4;
- }
- else
- {
- fragSz = framesPerBuffer;
- bufSz = component->latency * sampleRate + fragSz; /* Latency + 1 buffer */
- }
-
- PA_ENSURE( GetAvailableFormats( component, &availableFormats ) );
- hostFormat = PaUtil_SelectClosestAvailableFormat( availableFormats, component->userFormat );
-
- /* OSS demands at least 2 buffers, and 16 bytes per buffer */
- numBufs = PA_MAX( bufSz / fragSz, 2 );
- bytesPerBuf = PA_MAX( fragSz * Pa_GetSampleSize( hostFormat ) * chans, 16 );
-
- /* The fragment parameters are encoded like this:
- * Most significant byte: number of fragments
- * Least significant byte: exponent of fragment size (i.e., for 256, 8)
- */
- frgmt = (numBufs << 16) + (CalcHigherLogTwo( bytesPerBuf ) & 0xffff);
- ENSURE_( ioctl( component->fd, SNDCTL_DSP_SETFRAGMENT, &frgmt ), paUnanticipatedHostError );
-
- /* A: according to the OSS programmer's guide parameters should be set in this order:
- * format, channels, rate */
-
- /* This format should be deemed good before we get this far */
- PA_ENSURE( Pa2OssFormat( hostFormat, &temp ) );
- nativeFormat = temp;
- ENSURE_( ioctl( component->fd, SNDCTL_DSP_SETFMT, &temp ), paUnanticipatedHostError );
- PA_UNLESS( temp == nativeFormat, paInternalError );
-
- /* try to set the number of channels */
- ENSURE_( ioctl( component->fd, SNDCTL_DSP_CHANNELS, &chans ), paSampleFormatNotSupported ); /* XXX: Should be paInvalidChannelCount? */
- /* It's possible that the minimum number of host channels is greater than what the user requested */
- PA_UNLESS( chans >= component->userChannelCount, paInvalidChannelCount );
-
- /* try to set the sample rate */
- ENSURE_( ioctl( component->fd, SNDCTL_DSP_SPEED, &sr ), paInvalidSampleRate );
-
- /* reject if there's no sample rate within 1% of the one requested */
- if( (fabs( sampleRate - sr ) / sampleRate) > 0.01 )
- {
- PA_DEBUG(("%s: Wanted %f, closest sample rate was %d\n", __FUNCTION__, sampleRate, sr ));
- PA_ENSURE( paInvalidSampleRate );
- }
-
- ENSURE_( ioctl( component->fd, streamMode == StreamMode_In ? SNDCTL_DSP_GETISPACE : SNDCTL_DSP_GETOSPACE, &bufInfo ),
- paUnanticipatedHostError );
- component->numBufs = bufInfo.fragstotal;
-
- /* This needs to be the last ioctl call before the first read/write, according to the OSS programmer's guide */
- ENSURE_( ioctl( component->fd, SNDCTL_DSP_GETBLKSIZE, &bytesPerBuf ), paUnanticipatedHostError );
-
- component->hostFrames = bytesPerBuf / Pa_GetSampleSize( hostFormat ) / chans;
- component->hostChannelCount = chans;
- component->hostFormat = hostFormat;
- }
- else
- {
- component->hostFormat = master->hostFormat;
- component->hostFrames = master->hostFrames;
- component->hostChannelCount = master->hostChannelCount;
- component->numBufs = master->numBufs;
- }
-
- PA_UNLESS( component->buffer = PaUtil_AllocateMemory( PaOssStreamComponent_BufferSize( component ) ),
- paInsufficientMemory );
-
-error:
- return result;
-}
-
-static PaError PaOssStreamComponent_Read( PaOssStreamComponent *component, unsigned long *frames )
-{
- PaError result = paNoError;
- size_t len = *frames * PaOssStreamComponent_FrameSize( component );
- ssize_t bytesRead;
-
- ENSURE_( bytesRead = read( component->fd, component->buffer, len ), paUnanticipatedHostError );
- *frames = bytesRead / PaOssStreamComponent_FrameSize( component );
-
-error:
- return result;
-}
-
-static PaError PaOssStreamComponent_Write( PaOssStreamComponent *component, unsigned long *frames )
-{
- PaError result = paNoError;
- size_t len = *frames * PaOssStreamComponent_FrameSize( component );
- ssize_t bytesWritten;
-
- ENSURE_( bytesWritten = write( component->fd, component->buffer, len ), paUnanticipatedHostError );
- *frames = bytesWritten / PaOssStreamComponent_FrameSize( component );
-
-error:
- return result;
-}
-
-/** Configure the stream according to input/output parameters.
- *
- * Aspect StreamChannels: The minimum number of channels supported by the device may exceed that requested by
- * the user, if so we'll record the actual number of host channels and adapt later.
- */
-static PaError PaOssStream_Configure( PaOssStream *stream, double sampleRate, unsigned long framesPerBuffer,
- double *inputLatency, double *outputLatency )
-{
- PaError result = paNoError;
- int duplex = stream->capture && stream->playback;
- unsigned long framesPerHostBuffer = 0;
-
- /* We should request full duplex first thing after opening the device */
- if( duplex && stream->sharedDevice )
- ENSURE_( ioctl( stream->capture->fd, SNDCTL_DSP_SETDUPLEX, 0 ), paUnanticipatedHostError );
-
- if( stream->capture )
- {
- PaOssStreamComponent *component = stream->capture;
- PaOssStreamComponent_Configure( component, sampleRate, framesPerBuffer, StreamMode_In, NULL );
-
- assert( component->hostChannelCount > 0 );
- assert( component->hostFrames > 0 );
-
- *inputLatency = component->hostFrames * (component->numBufs - 1) / sampleRate;
- }
- if( stream->playback )
- {
- PaOssStreamComponent *component = stream->playback, *master = stream->sharedDevice ? stream->capture : NULL;
- PA_ENSURE( PaOssStreamComponent_Configure( component, sampleRate, framesPerBuffer, StreamMode_Out,
- master ) );
-
- assert( component->hostChannelCount > 0 );
- assert( component->hostFrames > 0 );
-
- *outputLatency = component->hostFrames * (component->numBufs - 1) / sampleRate;
- }
-
- if( duplex )
- framesPerHostBuffer = PA_MIN( stream->capture->hostFrames, stream->playback->hostFrames );
- else if( stream->capture )
- framesPerHostBuffer = stream->capture->hostFrames;
- else if( stream->playback )
- framesPerHostBuffer = stream->playback->hostFrames;
-
- stream->framesPerHostBuffer = framesPerHostBuffer;
- stream->pollTimeout = (int) ceil( 1e6 * framesPerHostBuffer / sampleRate ); /* Period in usecs, rounded up */
-
- stream->streamRepresentation.streamInfo.sampleRate = sampleRate;
-
-error:
- return result;
-}
-
-/* see pa_hostapi.h for a list of validity guarantees made about OpenStream parameters */
-
-/** Open a PA OSS stream.
- *
- * Aspect StreamChannels: The number of channels is specified per direction (in/out), and can differ between the
- * two. However, OSS doesn't support separate configuration spaces for capture and playback so if both
- * directions are the same device we will demand the same number of channels. The number of channels can range
- * from 1 to the maximum supported by the device.
- *
- * Aspect BufferSettings: If framesPerBuffer != paFramesPerBufferUnspecified the number of frames per callback
- * must reflect this, in addition the host latency per device should approximate the corresponding
- * suggestedLatency. Based on these constraints we need to determine a number of frames per host buffer that
- * both capture and playback can agree on (they can be different devices), the buffer processor can adapt
- * between host and user buffer size, but the ratio should preferably be integral.
- */
-static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi,
- PaStream** s,
- const PaStreamParameters *inputParameters,
- const PaStreamParameters *outputParameters,
- double sampleRate,
- unsigned long framesPerBuffer,
- PaStreamFlags streamFlags,
- PaStreamCallback *streamCallback,
- void *userData )
-{
- PaError result = paNoError;
- PaOSSHostApiRepresentation *ossHostApi = (PaOSSHostApiRepresentation*)hostApi;
- PaOssStream *stream = NULL;
- int inputChannelCount = 0, outputChannelCount = 0;
- PaSampleFormat inputSampleFormat = 0, outputSampleFormat = 0, inputHostFormat = 0, outputHostFormat = 0;
- const PaDeviceInfo *inputDeviceInfo = 0, *outputDeviceInfo = 0;
- int bpInitialized = 0;
- double inLatency, outLatency;
-
- /* validate platform specific flags */
- if( (streamFlags & paPlatformSpecificFlags) != 0 )
- return paInvalidFlag; /* unexpected platform specific flag */
-
- if( inputParameters )
- {
- /* unless alternate device specification is supported, reject the use of
- paUseHostApiSpecificDeviceSpecification */
- inputDeviceInfo = hostApi->deviceInfos[inputParameters->device];
- PA_ENSURE( ValidateParameters( inputParameters, inputDeviceInfo, StreamMode_In ) );
-
- inputChannelCount = inputParameters->channelCount;
- inputSampleFormat = inputParameters->sampleFormat;
- }
- if( outputParameters )
- {
- outputDeviceInfo = hostApi->deviceInfos[outputParameters->device];
- PA_ENSURE( ValidateParameters( outputParameters, outputDeviceInfo, StreamMode_Out ) );
-
- outputChannelCount = outputParameters->channelCount;
- outputSampleFormat = outputParameters->sampleFormat;
- }
-
- /* Aspect StreamChannels: We currently demand that number of input and output channels are the same, if the same
- * device is opened for both directions
- */
- if( inputChannelCount > 0 && outputChannelCount > 0 )
- {
- if( inputParameters->device == outputParameters->device )
- {
- if( inputParameters->channelCount != outputParameters->channelCount )
- return paInvalidChannelCount;
- }
- }
-
- /* allocate and do basic initialization of the stream structure */
- PA_UNLESS( stream = (PaOssStream*)PaUtil_AllocateMemory( sizeof(PaOssStream) ), paInsufficientMemory );
- PaOssStream_Initialize( stream, inputParameters, outputParameters, streamCallback, userData, streamFlags, ossHostApi );
-
- PA_ENSURE( PaOssStream_Configure( stream, sampleRate, framesPerBuffer, &inLatency, &outLatency ) );
-
- PaUtil_InitializeCpuLoadMeasurer( &stream->cpuLoadMeasurer, sampleRate );
-
- if( inputParameters )
- {
- inputHostFormat = stream->capture->hostFormat;
- stream->streamRepresentation.streamInfo.inputLatency = inLatency +
- PaUtil_GetBufferProcessorInputLatency( &stream->bufferProcessor ) / sampleRate;
- }
- if( outputParameters )
- {
- outputHostFormat = stream->playback->hostFormat;
- stream->streamRepresentation.streamInfo.outputLatency = outLatency +
- PaUtil_GetBufferProcessorOutputLatency( &stream->bufferProcessor ) / sampleRate;
- }
-
- /* Initialize buffer processor with fixed host buffer size.
- * Aspect StreamSampleFormat: Here we commit the user and host sample formats, PA infrastructure will
- * convert between the two.
- */
- PA_ENSURE( PaUtil_InitializeBufferProcessor( &stream->bufferProcessor,
- inputChannelCount, inputSampleFormat, inputHostFormat, outputChannelCount, outputSampleFormat,
- outputHostFormat, sampleRate, streamFlags, framesPerBuffer, stream->framesPerHostBuffer,
- paUtilFixedHostBufferSize, streamCallback, userData ) );
- bpInitialized = 1;
-
- *s = (PaStream*)stream;
-
- return result;
-
-error:
- if( bpInitialized )
- PaUtil_TerminateBufferProcessor( &stream->bufferProcessor );
- if( stream )
- PaOssStream_Terminate( stream );
-
- return result;
-}
-
-/*! Poll on I/O filedescriptors.
-
- Poll till we've determined there's data for read or write. In the full-duplex case,
- we don't want to hang around forever waiting for either input or output frames, so
- whenever we have a timed out filedescriptor we check if we're nearing under/overrun
- for the other direction (critical limit set at one buffer). If so, we exit the waiting
- state, and go on with what we got. We align the number of frames on a host buffer
- boundary because it is possible that the buffer size differs for the two directions and
- the host buffer size is a compromise between the two.
- */
-static PaError PaOssStream_WaitForFrames( PaOssStream *stream, unsigned long *frames )
-{
- PaError result = paNoError;
- int pollPlayback = 0, pollCapture = 0;
- int captureAvail = INT_MAX, playbackAvail = INT_MAX, commonAvail;
- audio_buf_info bufInfo;
- /* int ofs = 0, nfds = stream->nfds; */
- fd_set readFds, writeFds;
- int nfds = 0;
- struct timeval selectTimeval = {0, 0};
- unsigned long timeout = stream->pollTimeout; /* In usecs */
- int captureFd = -1, playbackFd = -1;
-
- assert( stream );
- assert( frames );
-
- if( stream->capture )
- {
- pollCapture = 1;
- captureFd = stream->capture->fd;
- /* stream->capture->pfd->events = POLLIN; */
- }
- if( stream->playback )
- {
- pollPlayback = 1;
- playbackFd = stream->playback->fd;
- /* stream->playback->pfd->events = POLLOUT; */
- }
-
- FD_ZERO( &readFds );
- FD_ZERO( &writeFds );
-
- while( pollPlayback || pollCapture )
- {
- pthread_testcancel();
-
- /* select may modify the timeout parameter */
- selectTimeval.tv_usec = timeout;
- nfds = 0;
-
- if( pollCapture )
- {
- FD_SET( captureFd, &readFds );
- nfds = captureFd + 1;
- }
- if( pollPlayback )
- {
- FD_SET( playbackFd, &writeFds );
- nfds = PA_MAX( nfds, playbackFd + 1 );
- }
- ENSURE_( select( nfds, &readFds, &writeFds, NULL, &selectTimeval ), paUnanticipatedHostError );
- /*
- if( poll( stream->pfds + ofs, nfds, stream->pollTimeout ) < 0 )
- {
-
- ENSURE_( -1, paUnanticipatedHostError );
- }
- */
- pthread_testcancel();
-
- if( pollCapture )
- {
- if( FD_ISSET( captureFd, &readFds ) )
- {
- FD_CLR( captureFd, &readFds );
- pollCapture = 0;
- }
- /*
- if( stream->capture->pfd->revents & POLLIN )
- {
- --nfds;
- ++ofs;
- pollCapture = 0;
- }
- */
- else if( stream->playback ) /* Timed out, go on with playback? */
- {
- /*PA_DEBUG(( "%s: Trying to poll again for capture frames, pollTimeout: %d\n",
- __FUNCTION__, stream->pollTimeout ));*/
- }
- }
- if( pollPlayback )
- {
- if( FD_ISSET( playbackFd, &writeFds ) )
- {
- FD_CLR( playbackFd, &writeFds );
- pollPlayback = 0;
- }
- /*
- if( stream->playback->pfd->revents & POLLOUT )
- {
- --nfds;
- pollPlayback = 0;
- }
- */
- else if( stream->capture ) /* Timed out, go on with capture? */
- {
- /*PA_DEBUG(( "%s: Trying to poll again for playback frames, pollTimeout: %d\n\n",
- __FUNCTION__, stream->pollTimeout ));*/
- }
- }
- }
-
- if( stream->capture )
- {
- ENSURE_( ioctl( captureFd, SNDCTL_DSP_GETISPACE, &bufInfo ), paUnanticipatedHostError );
- captureAvail = bufInfo.fragments * stream->capture->hostFrames;
- if( !captureAvail )
- PA_DEBUG(( "%s: captureAvail: 0\n", __FUNCTION__ ));
-
- captureAvail = captureAvail == 0 ? INT_MAX : captureAvail; /* Disregard if zero */
- }
- if( stream->playback )
- {
- ENSURE_( ioctl( playbackFd, SNDCTL_DSP_GETOSPACE, &bufInfo ), paUnanticipatedHostError );
- playbackAvail = bufInfo.fragments * stream->playback->hostFrames;
- if( !playbackAvail )
- {
- PA_DEBUG(( "%s: playbackAvail: 0\n", __FUNCTION__ ));
- }
-
- playbackAvail = playbackAvail == 0 ? INT_MAX : playbackAvail; /* Disregard if zero */
- }
-
- commonAvail = PA_MIN( captureAvail, playbackAvail );
- if( commonAvail == INT_MAX )
- commonAvail = 0;
- commonAvail -= commonAvail % stream->framesPerHostBuffer;
-
- assert( commonAvail != INT_MAX );
- assert( commonAvail >= 0 );
- *frames = commonAvail;
-
-error:
- return result;
-}
-
-/** Prepare stream for capture/playback.
- *
- * In order to synchronize capture and playback properly we use the SETTRIGGER command.
- */
-static PaError PaOssStream_Prepare( PaOssStream *stream )
-{
- PaError result = paNoError;
- int enableBits = 0;
-
- if( stream->triggered )
- return result;
-
- if( stream->playback )
- {
- size_t bufSz = PaOssStreamComponent_BufferSize( stream->playback );
- memset( stream->playback->buffer, 0, bufSz );
-
- /* Looks like we have to turn off blocking before we try this, but if we don't fill the buffer
- * OSS will complain. */
- PA_ENSURE( ModifyBlocking( stream->playback->fd, 0 ) );
- while (1)
- {
- if( write( stream->playback->fd, stream->playback->buffer, bufSz ) < 0 )
- break;
- }
- PA_ENSURE( ModifyBlocking( stream->playback->fd, 1 ) );
- }
-
- if( stream->sharedDevice )
- {
- enableBits = PCM_ENABLE_INPUT | PCM_ENABLE_OUTPUT;
- ENSURE_( ioctl( stream->capture->fd, SNDCTL_DSP_SETTRIGGER, &enableBits ), paUnanticipatedHostError );
- }
- else
- {
- if( stream->capture )
- {
- enableBits = PCM_ENABLE_INPUT;
- ENSURE_( ioctl( stream->capture->fd, SNDCTL_DSP_SETTRIGGER, &enableBits ), paUnanticipatedHostError );
- }
- if( stream->playback )
- {
- enableBits = PCM_ENABLE_OUTPUT;
- ENSURE_( ioctl( stream->playback->fd, SNDCTL_DSP_SETTRIGGER, &enableBits ), paUnanticipatedHostError );
- }
- }
-
- /* Ok, we have triggered the stream */
- stream->triggered = 1;
-
-error:
- return result;
-}
-
-/** Stop audio processing
- *
- */
-static PaError PaOssStream_Stop( PaOssStream *stream, int abort )
-{
- PaError result = paNoError;
-
- /* Looks like the only safe way to stop audio without reopening the device is SNDCTL_DSP_POST.
- * Also disable capture/playback till the stream is started again */
- if( stream->capture )
- {
- ENSURE_( ioctl( stream->capture->fd, SNDCTL_DSP_POST, 0 ), paUnanticipatedHostError );
- }
- if( stream->playback && !stream->sharedDevice )
- {
- ENSURE_( ioctl( stream->playback->fd, SNDCTL_DSP_POST, 0 ), paUnanticipatedHostError );
- }
-
-error:
- return result;
-}
-
-/** Clean up after thread exit.
- *
- * Aspect StreamState: If the user has registered a streamFinishedCallback it will be called here
- */
-static void OnExit( void *data )
-{
- PaOssStream *stream = (PaOssStream *) data;
- assert( data );
-
- PaUtil_ResetCpuLoadMeasurer( &stream->cpuLoadMeasurer );
-
- PaOssStream_Stop( stream, stream->callbackAbort );
-
- PA_DEBUG(( "OnExit: Stoppage\n" ));
-
- /* Eventually notify user all buffers have played */
- if( stream->streamRepresentation.streamFinishedCallback )
- stream->streamRepresentation.streamFinishedCallback( stream->streamRepresentation.userData );
-
- stream->callbackAbort = 0; /* Clear state */
- stream->isActive = 0;
-}
-
-static PaError SetUpBuffers( PaOssStream *stream, unsigned long framesAvail )
-{
- PaError result = paNoError;
-
- if( stream->capture )
- {
- PaUtil_SetInterleavedInputChannels( &stream->bufferProcessor, 0, stream->capture->buffer,
- stream->capture->hostChannelCount );
- PaUtil_SetInputFrameCount( &stream->bufferProcessor, framesAvail );
- }
- if( stream->playback )
- {
- PaUtil_SetInterleavedOutputChannels( &stream->bufferProcessor, 0, stream->playback->buffer,
- stream->playback->hostChannelCount );
- PaUtil_SetOutputFrameCount( &stream->bufferProcessor, framesAvail );
- }
-
- return result;
-}
-
-/** Thread procedure for callback processing.
- *
- * Aspect StreamState: StartStream will wait on this to initiate audio processing, useful in case the
- * callback should be used for buffer priming. When the stream is cancelled a separate function will
- * take care of the transition to the Callback Finished state (the stream isn't considered Stopped
- * before StopStream() or AbortStream() are called).
- */
-static void *PaOSS_AudioThreadProc( void *userData )
-{
- PaError result = paNoError;
- PaOssStream *stream = (PaOssStream*)userData;
- unsigned long framesAvail, framesProcessed;
- int callbackResult = paContinue;
- int triggered = stream->triggered; /* See if SNDCTL_DSP_TRIGGER has been issued already */
- int initiateProcessing = triggered; /* Already triggered? */
- PaStreamCallbackFlags cbFlags = 0; /* We might want to keep state across iterations */
-
- /*
-#if ( SOUND_VERSION > 0x030904 )
- audio_errinfo errinfo;
-#endif
-*/
-
- assert( stream );
-
- pthread_cleanup_push( &OnExit, stream ); /* Execute OnExit when exiting */
-
- /* The first time the stream is started we use SNDCTL_DSP_TRIGGER to accurately start capture and
- * playback in sync, when the stream is restarted after being stopped we simply start by reading/
- * writing.
- */
- PA_ENSURE( PaOssStream_Prepare( stream ) );
-
- /* If we are to initiate processing implicitly by reading/writing data, we start off in blocking mode */
- if( initiateProcessing )
- {
- /* Make sure devices are in blocking mode */
- if( stream->capture )
- ModifyBlocking( stream->capture->fd, 1 );
- if( stream->playback )
- ModifyBlocking( stream->playback->fd, 1 );
- }
-
- while( 1 )
- {
- PaStreamCallbackTimeInfo timeInfo = {0,0,0}; /* TODO: IMPLEMENT ME */
-
- pthread_testcancel();
-
- if( stream->callbackStop && callbackResult == paContinue )
- {
- PA_DEBUG(( "Setting callbackResult to paComplete\n" ));
- callbackResult = paComplete;
- }
-
- /* Aspect StreamState: Because of the messy OSS scheme we can't explicitly trigger device start unless
- * the stream has been recently started, we will have to go right ahead and read/write in blocking
- * fashion to trigger operation. Therefore we begin with processing one host buffer before we switch
- * to non-blocking mode.
- */
- if( !initiateProcessing )
- {
- PA_ENSURE( PaOssStream_WaitForFrames( stream, &framesAvail ) ); /* Wait on available frames */
- assert( framesAvail % stream->framesPerHostBuffer == 0 );
- }
- else
- {
- framesAvail = stream->framesPerHostBuffer;
- }
-
- while( framesAvail > 0 )
- {
- unsigned long frames = framesAvail;
-
- pthread_testcancel();
-
- PaUtil_BeginCpuLoadMeasurement( &stream->cpuLoadMeasurer );
-
- /* Read data */
- if ( stream->capture )
- {
- PA_ENSURE( PaOssStreamComponent_Read( stream->capture, &frames ) );
- assert( frames == framesAvail );
- }
-
-#if ( SOUND_VERSION >= 0x030904 )
- /*
- Check with OSS to see if there have been any under/overruns
- since last time we checked.
- */
- /*
- if( ioctl( stream->deviceHandle, SNDCTL_DSP_GETERROR, &errinfo ) >= 0 )
- {
- if( errinfo.play_underruns )
- cbFlags |= paOutputUnderflow ;
- if( errinfo.record_underruns )
- cbFlags |= paInputUnderflow ;
- }
- else
- PA_DEBUG(( "SNDCTL_DSP_GETERROR command failed: %s\n", strerror( errno ) ));
- */
-#endif
-
- PaUtil_BeginBufferProcessing( &stream->bufferProcessor, &timeInfo,
- cbFlags );
- cbFlags = 0;
- PA_ENSURE( SetUpBuffers( stream, framesAvail ) );
-
- framesProcessed = PaUtil_EndBufferProcessing( &stream->bufferProcessor,
- &callbackResult );
- assert( framesProcessed == framesAvail );
- PaUtil_EndCpuLoadMeasurement( &stream->cpuLoadMeasurer, framesProcessed );
-
- if ( stream->playback )
- {
- frames = framesAvail;
-
- PA_ENSURE( PaOssStreamComponent_Write( stream->playback, &frames ) );
- assert( frames == framesAvail );
-
- /* TODO: handle bytesWritten != bytesRequested (slippage?) */
- }
-
- framesAvail -= framesProcessed;
- stream->framesProcessed += framesProcessed;
-
- if( callbackResult != paContinue )
- break;
- }
-
- if( initiateProcessing || !triggered )
- {
- /* Non-blocking */
- if( stream->capture )
- PA_ENSURE( ModifyBlocking( stream->capture->fd, 0 ) );
- if( stream->playback && !stream->sharedDevice )
- PA_ENSURE( ModifyBlocking( stream->playback->fd, 0 ) );
-
- initiateProcessing = 0;
- sem_post( &stream->semaphore );
- }
-
- if( callbackResult != paContinue )
- {
- stream->callbackAbort = callbackResult == paAbort;
- if( stream->callbackAbort || PaUtil_IsBufferProcessorOutputEmpty( &stream->bufferProcessor ) )
- break;
- }
- }
-
- pthread_cleanup_pop( 1 );
-
-error:
- pthread_exit( NULL );
-}
-
-/** Close the stream.
- *
- */
-static PaError CloseStream( PaStream* s )
-{
- PaError result = paNoError;
- PaOssStream *stream = (PaOssStream*)s;
-
- assert( stream );
-
- PaUtil_TerminateBufferProcessor( &stream->bufferProcessor );
- PaOssStream_Terminate( stream );
-
- return result;
-}
-
-/** Start the stream.
- *
- * Aspect StreamState: After returning, the stream shall be in the Active state, implying that an eventual
- * callback will be repeatedly called in a separate thread. If a separate thread is started this function
- * will block untill it has started processing audio, otherwise audio processing is started directly.
- */
-static PaError StartStream( PaStream *s )
-{
- PaError result = paNoError;
- PaOssStream *stream = (PaOssStream*)s;
-
- stream->isActive = 1;
- stream->isStopped = 0;
- stream->lastPosPtr = 0;
- stream->lastStreamBytes = 0;
- stream->framesProcessed = 0;
-
- /* only use the thread for callback streams */
- if( stream->bufferProcessor.streamCallback )
- {
- PA_ENSURE( PaUtil_StartThreading( &stream->threading, &PaOSS_AudioThreadProc, stream ) );
- sem_wait( &stream->semaphore );
- }
- else
- PA_ENSURE( PaOssStream_Prepare( stream ) );
-
-error:
- return result;
-}
-
-static PaError RealStop( PaOssStream *stream, int abort )
-{
- PaError result = paNoError;
-
- if( stream->callbackMode )
- {
- if( abort )
- stream->callbackAbort = 1;
- else
- stream->callbackStop = 1;
-
- PA_ENSURE( PaUtil_CancelThreading( &stream->threading, !abort, NULL ) );
-
- stream->callbackStop = stream->callbackAbort = 0;
- }
- else
- PA_ENSURE( PaOssStream_Stop( stream, abort ) );
-
- stream->isStopped = 1;
-
-error:
- return result;
-}
-
-/** Stop the stream.
- *
- * Aspect StreamState: This will cause the stream to transition to the Stopped state, playing all enqueued
- * buffers.
- */
-static PaError StopStream( PaStream *s )
-{
- return RealStop( (PaOssStream *)s, 0 );
-}
-
-/** Abort the stream.
- *
- * Aspect StreamState: This will cause the stream to transition to the Stopped state, discarding all enqueued
- * buffers. Note that the buffers are not currently correctly discarded, this is difficult without closing
- * the OSS device.
- */
-static PaError AbortStream( PaStream *s )
-{
- return RealStop( (PaOssStream *)s, 1 );
-}
-
-/** Is the stream in the Stopped state.
- *
- */
-static PaError IsStreamStopped( PaStream *s )
-{
- PaOssStream *stream = (PaOssStream*)s;
-
- return (stream->isStopped);
-}
-
-/** Is the stream in the Active state.
- *
- */
-static PaError IsStreamActive( PaStream *s )
-{
- PaOssStream *stream = (PaOssStream*)s;
-
- return (stream->isActive);
-}
-
-static PaTime GetStreamTime( PaStream *s )
-{
- PaOssStream *stream = (PaOssStream*)s;
- count_info info;
- int delta;
-
- if( stream->playback ) {
- if( ioctl( stream->playback->fd, SNDCTL_DSP_GETOPTR, &info) == 0 ) {
- delta = ( info.bytes - stream->lastPosPtr ) & 0x000FFFFF;
- return ( stream->lastStreamBytes + delta) / PaOssStreamComponent_FrameSize( stream->playback ) / stream->sampleRate;
- }
- }
- else {
- if (ioctl( stream->capture->fd, SNDCTL_DSP_GETIPTR, &info) == 0) {
- delta = (info.bytes - stream->lastPosPtr) & 0x000FFFFF;
- return ( stream->lastStreamBytes + delta) / PaOssStreamComponent_FrameSize( stream->capture ) / stream->sampleRate;
- }
- }
-
- /* the ioctl failed, but we can still give a coarse estimate */
-
- return stream->framesProcessed / stream->sampleRate;
-}
-
-
-static double GetStreamCpuLoad( PaStream* s )
-{
- PaOssStream *stream = (PaOssStream*)s;
-
- return PaUtil_GetCpuLoad( &stream->cpuLoadMeasurer );
-}
-
-
-/*
- As separate stream interfaces are used for blocking and callback
- streams, the following functions can be guaranteed to only be called
- for blocking streams.
-*/
-
-
-static PaError ReadStream( PaStream* s,
- void *buffer,
- unsigned long frames )
-{
- PaOssStream *stream = (PaOssStream*)s;
- int bytesRequested, bytesRead;
- unsigned long framesRequested;
- void *userBuffer;
-
- /* If user input is non-interleaved, PaUtil_CopyInput will manipulate the channel pointers,
- * so we copy the user provided pointers */
- if( stream->bufferProcessor.userInputIsInterleaved )
- userBuffer = buffer;
- else /* Copy channels into local array */
- {
- userBuffer = stream->capture->userBuffers;
- memcpy( (void *)userBuffer, buffer, sizeof (void *) * stream->capture->userChannelCount );
- }
-
- while( frames )
- {
- framesRequested = PA_MIN( frames, stream->capture->hostFrames );
-
- bytesRequested = framesRequested * PaOssStreamComponent_FrameSize( stream->capture );
- bytesRead = read( stream->capture->fd, stream->capture->buffer, bytesRequested );
- if ( bytesRequested != bytesRead )
- return paUnanticipatedHostError;
-
- PaUtil_SetInputFrameCount( &stream->bufferProcessor, stream->capture->hostFrames );
- PaUtil_SetInterleavedInputChannels( &stream->bufferProcessor, 0, stream->capture->buffer, stream->capture->hostChannelCount );
- PaUtil_CopyInput( &stream->bufferProcessor, &userBuffer, framesRequested );
- frames -= framesRequested;
- }
- return paNoError;
-}
-
-
-static PaError WriteStream( PaStream* s,
- const void *buffer,
- unsigned long frames )
-{
- PaOssStream *stream = (PaOssStream*)s;
- int bytesRequested, bytesWritten;
- unsigned long framesConverted;
- const void *userBuffer;
-
- /* If user output is non-interleaved, PaUtil_CopyOutput will manipulate the channel pointers,
- * so we copy the user provided pointers */
- if( stream->bufferProcessor.userOutputIsInterleaved )
- userBuffer = buffer;
- else /* Copy channels into local array */
- {
- userBuffer = stream->playback->userBuffers;
- memcpy( (void *)userBuffer, buffer, sizeof (void *) * stream->playback->userChannelCount );
- }
-
- while( frames )
- {
- PaUtil_SetOutputFrameCount( &stream->bufferProcessor, stream->playback->hostFrames );
- PaUtil_SetInterleavedOutputChannels( &stream->bufferProcessor, 0, stream->playback->buffer, stream->playback->hostChannelCount );
-
- framesConverted = PaUtil_CopyOutput( &stream->bufferProcessor, &userBuffer, frames );
- frames -= framesConverted;
-
- bytesRequested = framesConverted * PaOssStreamComponent_FrameSize( stream->playback );
- bytesWritten = write( stream->playback->fd, stream->playback->buffer, bytesRequested );
-
- if ( bytesRequested != bytesWritten )
- return paUnanticipatedHostError;
- }
- return paNoError;
-}
-
-
-static signed long GetStreamReadAvailable( PaStream* s )
-{
- PaOssStream *stream = (PaOssStream*)s;
- audio_buf_info info;
-
- if( ioctl( stream->capture->fd, SNDCTL_DSP_GETISPACE, &info ) < 0 )
- return paUnanticipatedHostError;
- return info.fragments * stream->capture->hostFrames;
-}
-
-
-/* TODO: Compute number of allocated bytes somewhere else, can we use ODELAY with capture */
-static signed long GetStreamWriteAvailable( PaStream* s )
-{
- PaOssStream *stream = (PaOssStream*)s;
- int delay = 0;
-
- if( ioctl( stream->playback->fd, SNDCTL_DSP_GETODELAY, &delay ) < 0 )
- return paUnanticipatedHostError;
-
- return (PaOssStreamComponent_BufferSize( stream->playback ) - delay) / PaOssStreamComponent_FrameSize( stream->playback );
-}
-
+/*
+ * $Id: pa_unix_oss.c,v 1.6.2.22 2005/03/08 21:26:53 aknudsen Exp $
+ * PortAudio Portable Real-Time Audio Library
+ * Latest Version at: http://www.portaudio.com
+ * OSS implementation by:
+ * Douglas Repetto
+ * Phil Burk
+ * Dominic Mazzoni
+ * Arve Knudsen
+ *
+ * Based on the Open Source API proposed by Ross Bencina
+ * Copyright (c) 1999-2002 Ross Bencina, Phil Burk
+ *
+ * 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.
+ *
+ * Any person wishing to distribute modifications to the Software is
+ * requested to send the modifications to the original developer so that
+ * they can be incorporated into the canonical version.
+ *
+ * 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 <stdio.h>
+#include <string.h>
+#include <math.h>
+#include <fcntl.h>
+#include <sys/ioctl.h>
+#include <unistd.h>
+#include <pthread.h>
+#include <alloca.h>
+#include <malloc.h>
+#include <assert.h>
+#include <errno.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/poll.h>
+#include <limits.h>
+#include <semaphore.h>
+
+#ifdef __linux__
+# include <linux/soundcard.h>
+# define DEVICE_NAME_BASE "/dev/dsp"
+#else
+# include <machine/soundcard.h> /* JH20010905 */
+# define DEVICE_NAME_BASE "/dev/audio"
+#endif
+
+#include "portaudio.h"
+#include "pa_util.h"
+#include "pa_allocation.h"
+#include "pa_hostapi.h"
+#include "pa_stream.h"
+#include "pa_cpuload.h"
+#include "pa_process.h"
+#include "pa_unix_util.h"
+
+static int sysErr_;
+static pthread_t mainThread_;
+
+/* Check return value of system call, and map it to PaError */
+#define ENSURE_(expr, code) \
+ do { \
+ if( UNLIKELY( (sysErr_ = (expr)) < 0 ) ) \
+ { \
+ /* PaUtil_SetLastHostErrorInfo should only be used in the main thread */ \
+ if( (code) == paUnanticipatedHostError && pthread_self() == mainThread_ ) \
+ { \
+ PaUtil_SetLastHostErrorInfo( paALSA, sysErr_, strerror( errno ) ); \
+ } \
+ \
+ PaUtil_DebugPrint(( "Expression '" #expr "' failed in '" __FILE__ "', line: " STRINGIZE( __LINE__ ) "\n" )); \
+ result = (code); \
+ goto error; \
+ } \
+ } while( 0 );
+
+#ifndef AFMT_S16_NE
+#define AFMT_S16_NE Get_AFMT_S16_NE()
+/*********************************************************************
+ * Some versions of OSS do not define AFMT_S16_NE. So check CPU.
+ * PowerPC is Big Endian. X86 is Little Endian.
+ */
+static int Get_AFMT_S16_NE( void )
+{
+ long testData = 1;
+ char *ptr = (char *) &testData;
+ int isLittle = ( *ptr == 1 ); /* Does address point to least significant byte? */
+ return isLittle ? AFMT_S16_LE : AFMT_S16_BE;
+}
+#endif
+
+/* PaOSSHostApiRepresentation - host api datastructure specific to this implementation */
+
+typedef struct
+{
+ PaUtilHostApiRepresentation inheritedHostApiRep;
+ PaUtilStreamInterface callbackStreamInterface;
+ PaUtilStreamInterface blockingStreamInterface;
+
+ PaUtilAllocationGroup *allocations;
+
+ PaHostApiIndex hostApiIndex;
+}
+PaOSSHostApiRepresentation;
+
+/** Per-direction structure for PaOssStream.
+ *
+ * Aspect StreamChannels: In case the user requests to open the same device for both capture and playback,
+ * but with different number of channels we will have to adapt between the number of user and host
+ * channels for at least one direction, since the configuration space is the same for both directions
+ * of an OSS device.
+ */
+typedef struct
+{
+ int fd;
+ const char *devName;
+ int userChannelCount, hostChannelCount;
+ int userInterleaved;
+ void *buffer;
+ PaSampleFormat userFormat, hostFormat;
+ double latency;
+ unsigned long hostFrames, numBufs;
+ void **userBuffers; /* For non-interleaved blocking */
+} PaOssStreamComponent;
+
+/** Implementation specific representation of a PaStream.
+ *
+ */
+typedef struct PaOssStream
+{
+ PaUtilStreamRepresentation streamRepresentation;
+ PaUtilCpuLoadMeasurer cpuLoadMeasurer;
+ PaUtilBufferProcessor bufferProcessor;
+
+ PaUtilThreading threading;
+
+ int sharedDevice;
+ unsigned long framesPerHostBuffer;
+ int triggered; /* Have the devices been triggered yet (first start) */
+
+ int isActive;
+ int isStopped;
+
+ int lastPosPtr;
+ double lastStreamBytes;
+
+ int framesProcessed;
+
+ double sampleRate;
+
+ int callbackMode;
+ int callbackStop, callbackAbort;
+
+ PaOssStreamComponent *capture, *playback;
+ unsigned long pollTimeout;
+ sem_t semaphore;
+}
+PaOssStream;
+
+typedef enum {
+ StreamMode_In,
+ StreamMode_Out
+} StreamMode;
+
+/* prototypes for functions declared in this file */
+
+static void Terminate( struct PaUtilHostApiRepresentation *hostApi );
+static PaError IsFormatSupported( struct PaUtilHostApiRepresentation *hostApi,
+ const PaStreamParameters *inputParameters,
+ const PaStreamParameters *outputParameters,
+ double sampleRate );
+static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi,
+ PaStream** s,
+ const PaStreamParameters *inputParameters,
+ const PaStreamParameters *outputParameters,
+ double sampleRate,
+ unsigned long framesPerBuffer,
+ PaStreamFlags streamFlags,
+ PaStreamCallback *streamCallback,
+ void *userData );
+static PaError CloseStream( PaStream* stream );
+static PaError StartStream( PaStream *stream );
+static PaError StopStream( PaStream *stream );
+static PaError AbortStream( PaStream *stream );
+static PaError IsStreamStopped( PaStream *s );
+static PaError IsStreamActive( PaStream *stream );
+static PaTime GetStreamTime( PaStream *stream );
+static double GetStreamCpuLoad( PaStream* stream );
+static PaError ReadStream( PaStream* stream, void *buffer, unsigned long frames );
+static PaError WriteStream( PaStream* stream, const void *buffer, unsigned long frames );
+static signed long GetStreamReadAvailable( PaStream* stream );
+static signed long GetStreamWriteAvailable( PaStream* stream );
+static PaError BuildDeviceList( PaOSSHostApiRepresentation *hostApi );
+
+
+/** Initialize the OSS API implementation.
+ *
+ * This function will initialize host API datastructures and query host devices for information.
+ *
+ * Aspect DeviceCapabilities: Enumeration of host API devices is initiated from here
+ *
+ * Aspect FreeResources: If an error is encountered under way we have to free each resource allocated in this function,
+ * this happens with the usual "error" label.
+ */
+PaError PaOSS_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiIndex hostApiIndex )
+{
+ PaError result = paNoError;
+ PaOSSHostApiRepresentation *ossHostApi = NULL;
+
+ PA_UNLESS( ossHostApi = (PaOSSHostApiRepresentation*)PaUtil_AllocateMemory( sizeof(PaOSSHostApiRepresentation) ),
+ paInsufficientMemory );
+ PA_UNLESS( ossHostApi->allocations = PaUtil_CreateAllocationGroup(), paInsufficientMemory );
+ ossHostApi->hostApiIndex = hostApiIndex;
+
+ /* Initialize host API structure */
+ *hostApi = &ossHostApi->inheritedHostApiRep;
+ (*hostApi)->info.structVersion = 1;
+ (*hostApi)->info.type = paOSS;
+ (*hostApi)->info.name = "OSS";
+ (*hostApi)->Terminate = Terminate;
+ (*hostApi)->OpenStream = OpenStream;
+ (*hostApi)->IsFormatSupported = IsFormatSupported;
+
+ PA_ENSURE( BuildDeviceList( ossHostApi ) );
+
+ PaUtil_InitializeStreamInterface( &ossHostApi->callbackStreamInterface, CloseStream, StartStream,
+ StopStream, AbortStream, IsStreamStopped, IsStreamActive,
+ GetStreamTime, GetStreamCpuLoad,
+ PaUtil_DummyRead, PaUtil_DummyWrite,
+ PaUtil_DummyGetReadAvailable,
+ PaUtil_DummyGetWriteAvailable );
+
+ PaUtil_InitializeStreamInterface( &ossHostApi->blockingStreamInterface, CloseStream, StartStream,
+ StopStream, AbortStream, IsStreamStopped, IsStreamActive,
+ GetStreamTime, PaUtil_DummyGetCpuLoad,
+ ReadStream, WriteStream, GetStreamReadAvailable, GetStreamWriteAvailable );
+
+ mainThread_ = pthread_self();
+
+ return result;
+
+error:
+ if( ossHostApi )
+ {
+ if( ossHostApi->allocations )
+ {
+ PaUtil_FreeAllAllocations( ossHostApi->allocations );
+ PaUtil_DestroyAllocationGroup( ossHostApi->allocations );
+ }
+
+ PaUtil_FreeMemory( ossHostApi );
+ }
+ return result;
+}
+
+PaError PaUtil_InitializeDeviceInfo( PaDeviceInfo *deviceInfo, const char *name, PaHostApiIndex hostApiIndex, int maxInputChannels,
+ int maxOutputChannels, PaTime defaultLowInputLatency, PaTime defaultLowOutputLatency, PaTime defaultHighInputLatency,
+ PaTime defaultHighOutputLatency, double defaultSampleRate, PaUtilAllocationGroup *allocations )
+{
+ PaError result = paNoError;
+
+ deviceInfo->structVersion = 2;
+ if( allocations )
+ {
+ size_t len = strlen( name ) + 1;
+ PA_UNLESS( deviceInfo->name = PaUtil_GroupAllocateMemory( allocations, len ), paInsufficientMemory );
+ strncpy( (char *)deviceInfo->name, name, len );
+ }
+ else
+ deviceInfo->name = name;
+
+ deviceInfo->hostApi = hostApiIndex;
+ deviceInfo->maxInputChannels = maxInputChannels;
+ deviceInfo->maxOutputChannels = maxOutputChannels;
+ deviceInfo->defaultLowInputLatency = defaultLowInputLatency;
+ deviceInfo->defaultLowOutputLatency = defaultLowOutputLatency;
+ deviceInfo->defaultHighInputLatency = defaultHighInputLatency;
+ deviceInfo->defaultHighOutputLatency = defaultHighOutputLatency;
+ deviceInfo->defaultSampleRate = defaultSampleRate;
+
+error:
+ return result;
+}
+
+static PaError QueryDirection( const char *deviceName, StreamMode mode, double *defaultSampleRate, int *maxChannelCount,
+ double *defaultLowLatency, double *defaultHighLatency )
+{
+ PaError result = paNoError;
+ int numChannels, maxNumChannels;
+ int busy = 0;
+ int devHandle = -1;
+ int sr;
+ *maxChannelCount = 0; /* Default value in case this fails */
+
+ if ( (devHandle = open( deviceName, (mode == StreamMode_In ? O_RDONLY : O_WRONLY) | O_NONBLOCK )) < 0 )
+ {
+ if( errno == EBUSY || errno == EAGAIN )
+ {
+ PA_DEBUG(( "%s: Device %s busy\n", __FUNCTION__, deviceName ));
+ }
+ else
+ {
+ PA_DEBUG(( "%s: Can't access device: %s\n", __FUNCTION__, strerror( errno ) ));
+ }
+
+ return paDeviceUnavailable;
+ }
+
+ /* Negotiate for the maximum number of channels for this device. PLB20010927
+ * Consider up to 16 as the upper number of channels.
+ * Variable maxNumChannels should contain the actual upper limit after the call.
+ * Thanks to John Lazzaro and Heiko Purnhagen for suggestions.
+ */
+ maxNumChannels = 0;
+ for( numChannels = 1; numChannels <= 16; numChannels++ )
+ {
+ int temp = numChannels;
+ if( ioctl( devHandle, SNDCTL_DSP_CHANNELS, &temp ) < 0 )
+ {
+ busy = EAGAIN == errno || EBUSY == errno;
+ /* ioctl() failed so bail out if we already have stereo */
+ if( maxNumChannels >= 2 )
+ break;
+ }
+ else
+ {
+ /* ioctl() worked but bail out if it does not support numChannels.
+ * We don't want to leave gaps in the numChannels supported.
+ */
+ if( (numChannels > 2) && (temp != numChannels) )
+ break;
+ if( temp > maxNumChannels )
+ maxNumChannels = temp; /* Save maximum. */
+ }
+ }
+ /* A: We're able to open a device for capture if it's busy playing back and vice versa,
+ * but we can't configure anything */
+ if( 0 == maxNumChannels && busy )
+ {
+ result = paDeviceUnavailable;
+ goto error;
+ }
+
+ /* The above negotiation may fail for an old driver so try this older technique. */
+ if( maxNumChannels < 1 )
+ {
+ int stereo = 1;
+ if( ioctl( devHandle, SNDCTL_DSP_STEREO, &stereo ) < 0 )
+ {
+ maxNumChannels = 1;
+ }
+ else
+ {
+ maxNumChannels = (stereo) ? 2 : 1;
+ }
+ PA_DEBUG(( "%s: use SNDCTL_DSP_STEREO, maxNumChannels = %d\n", __FUNCTION__, maxNumChannels ))
+ }
+
+ /* During channel negotiation, the last ioctl() may have failed. This can
+ * also cause sample rate negotiation to fail. Hence the following, to return
+ * to a supported number of channels. SG20011005 */
+ {
+ /* use most reasonable default value */
+ int temp = PA_MIN( maxNumChannels, 2 );
+ ENSURE_( ioctl( devHandle, SNDCTL_DSP_CHANNELS, &temp ), paUnanticipatedHostError );
+ }
+
+ /* Get supported sample rate closest to 44100 Hz */
+ if( *defaultSampleRate < 0 )
+ {
+ sr = 44100;
+ if( ioctl( devHandle, SNDCTL_DSP_SPEED, &sr ) < 0 )
+ {
+ result = paUnanticipatedHostError;
+ goto error;
+ }
+
+ *defaultSampleRate = sr;
+ }
+
+ *maxChannelCount = maxNumChannels;
+ /* TODO */
+ *defaultLowLatency = 512. / *defaultSampleRate;
+ *defaultHighLatency = 2048. / *defaultSampleRate;
+
+error:
+ if( devHandle >= 0 )
+ close( devHandle );
+
+ return result;
+}
+
+/** Query OSS device.
+ *
+ * This is where PaDeviceInfo objects are constructed and filled in with relevant information.
+ *
+ * Aspect DeviceCapabilities: The inferred device capabilities are recorded in a PaDeviceInfo object that is constructed
+ * in place.
+ */
+static PaError QueryDevice( char *deviceName, PaOSSHostApiRepresentation *ossApi, PaDeviceInfo **deviceInfo )
+{
+ PaError result = paNoError;
+ double sampleRate = -1.;
+ int maxInputChannels, maxOutputChannels;
+ PaTime defaultLowInputLatency, defaultLowOutputLatency, defaultHighInputLatency, defaultHighOutputLatency;
+ PaError tmpRes = paNoError;
+ int busy = 0;
+ *deviceInfo = NULL;
+
+ /* douglas:
+ we have to do this querying in a slightly different order. apparently
+ some sound cards will give you different info based on their settins.
+ e.g. a card might give you stereo at 22kHz but only mono at 44kHz.
+ the correct order for OSS is: format, channels, sample rate
+ */
+
+ /* Aspect StreamChannels: The number of channels supported for a device may depend on the mode it is
+ * opened in, it may have more channels available for capture than playback and vice versa. Therefore
+ * we will open the device in both read- and write-only mode to determine the supported number.
+ */
+ if( (tmpRes = QueryDirection( deviceName, StreamMode_In, &sampleRate, &maxInputChannels, &defaultLowInputLatency,
+ &defaultHighInputLatency )) != paNoError )
+ {
+ if( tmpRes != paDeviceUnavailable )
+ {
+ PA_DEBUG(( "%s: Querying device %s for capture failed!\n", __FUNCTION__, deviceName ));
+ /* PA_ENSURE( tmpRes ); */
+ }
+ ++busy;
+ }
+ if( (tmpRes = QueryDirection( deviceName, StreamMode_Out, &sampleRate, &maxOutputChannels, &defaultLowOutputLatency,
+ &defaultHighOutputLatency )) != paNoError )
+ {
+ if( tmpRes != paDeviceUnavailable )
+ {
+ PA_DEBUG(( "%s: Querying device %s for playback failed!\n", __FUNCTION__, deviceName ));
+ /* PA_ENSURE( tmpRes ); */
+ }
+ ++busy;
+ }
+ assert( 0 <= busy && busy <= 2 );
+ if( 2 == busy ) /* Both directions are unavailable to us */
+ {
+ result = paDeviceUnavailable;
+ goto error;
+ }
+
+ PA_UNLESS( *deviceInfo = PaUtil_GroupAllocateMemory( ossApi->allocations, sizeof (PaDeviceInfo) ), paInsufficientMemory );
+ PA_ENSURE( PaUtil_InitializeDeviceInfo( *deviceInfo, deviceName, ossApi->hostApiIndex, maxInputChannels, maxOutputChannels,
+ defaultLowInputLatency, defaultLowOutputLatency, defaultHighInputLatency, defaultHighOutputLatency, sampleRate,
+ ossApi->allocations ) );
+
+error:
+ return result;
+}
+
+/** Query host devices.
+ *
+ * Loop over host devices and query their capabilitiesu
+ *
+ * Aspect DeviceCapabilities: This function calls QueryDevice on each device entry and receives a filled in PaDeviceInfo object
+ * per device, these are placed in the host api representation's deviceInfos array.
+ */
+static PaError BuildDeviceList( PaOSSHostApiRepresentation *ossApi )
+{
+ PaError result = paNoError;
+ PaUtilHostApiRepresentation *commonApi = &ossApi->inheritedHostApiRep;
+ int i;
+ int numDevices = 0, maxDeviceInfos = 1;
+ PaDeviceInfo **deviceInfos = NULL;
+
+ /* These two will be set to the first working input and output device, respectively */
+ commonApi->info.defaultInputDevice = paNoDevice;
+ commonApi->info.defaultOutputDevice = paNoDevice;
+
+ /* Find devices by calling QueryDevice on each
+ * potential device names. When we find a valid one,
+ * add it to a linked list.
+ * A: Can there only be 10 devices? */
+
+ for( i = 0; i < 10; i++ )
+ {
+ char deviceName[32];
+ PaDeviceInfo *deviceInfo;
+ int testResult;
+ struct stat stbuf;
+
+ if( i == 0 )
+ snprintf(deviceName, sizeof (deviceName), "%s", DEVICE_NAME_BASE);
+ else
+ snprintf(deviceName, sizeof (deviceName), "%s%d", DEVICE_NAME_BASE, i);
+
+ /* PA_DEBUG(("PaOSS BuildDeviceList: trying device %s\n", deviceName )); */
+ if( stat( deviceName, &stbuf ) < 0 )
+ {
+ if( ENOENT != errno )
+ PA_DEBUG(( "%s: Error stat'ing %s: %s\n", __FUNCTION__, deviceName, strerror( errno ) ));
+ continue;
+ }
+ if( (testResult = QueryDevice( deviceName, ossApi, &deviceInfo )) != paNoError )
+ {
+ if( testResult != paDeviceUnavailable )
+ PA_ENSURE( testResult );
+
+ continue;
+ }
+
+ ++numDevices;
+ if( !deviceInfos || numDevices > maxDeviceInfos )
+ {
+ maxDeviceInfos *= 2;
+ PA_UNLESS( deviceInfos = (PaDeviceInfo **) realloc( deviceInfos, maxDeviceInfos * sizeof (PaDeviceInfo *) ),
+ paInsufficientMemory );
+ }
+ deviceInfos[numDevices - 1] = deviceInfo;
+
+ if( commonApi->info.defaultInputDevice == paNoDevice && deviceInfo->maxInputChannels > 0 )
+ commonApi->info.defaultInputDevice = i;
+ if( commonApi->info.defaultOutputDevice == paNoDevice && deviceInfo->maxOutputChannels > 0 )
+ commonApi->info.defaultOutputDevice = i;
+ }
+
+ /* Make an array of PaDeviceInfo pointers out of the linked list */
+
+ PA_DEBUG(("PaOSS %s: Total number of devices found: %d\n", __FUNCTION__, numDevices));
+
+ commonApi->deviceInfos = (PaDeviceInfo**)PaUtil_GroupAllocateMemory(
+ ossApi->allocations, sizeof(PaDeviceInfo*) * numDevices );
+ memcpy( commonApi->deviceInfos, deviceInfos, numDevices * sizeof (PaDeviceInfo *) );
+
+ commonApi->info.deviceCount = numDevices;
+
+error:
+ free( deviceInfos );
+
+ return result;
+}
+
+static void Terminate( struct PaUtilHostApiRepresentation *hostApi )
+{
+ PaOSSHostApiRepresentation *ossHostApi = (PaOSSHostApiRepresentation*)hostApi;
+
+ if( ossHostApi->allocations )
+ {
+ PaUtil_FreeAllAllocations( ossHostApi->allocations );
+ PaUtil_DestroyAllocationGroup( ossHostApi->allocations );
+ }
+
+ PaUtil_FreeMemory( ossHostApi );
+}
+
+static PaError IsFormatSupported( struct PaUtilHostApiRepresentation *hostApi,
+ const PaStreamParameters *inputParameters,
+ const PaStreamParameters *outputParameters,
+ double sampleRate )
+{
+ PaError result = paNoError;
+ PaDeviceIndex device;
+ PaDeviceInfo *deviceInfo;
+ char *deviceName;
+ int inputChannelCount, outputChannelCount;
+ int tempDevHandle = -1;
+ int flags;
+ PaSampleFormat inputSampleFormat, outputSampleFormat;
+
+ if( inputParameters )
+ {
+ inputChannelCount = inputParameters->channelCount;
+ inputSampleFormat = inputParameters->sampleFormat;
+
+ /* unless alternate device specification is supported, reject the use of
+ paUseHostApiSpecificDeviceSpecification */
+
+ if( inputParameters->device == paUseHostApiSpecificDeviceSpecification )
+ return paInvalidDevice;
+
+ /* check that input device can support inputChannelCount */
+ if( inputChannelCount > hostApi->deviceInfos[ inputParameters->device ]->maxInputChannels )
+ return paInvalidChannelCount;
+
+ /* validate inputStreamInfo */
+ if( inputParameters->hostApiSpecificStreamInfo )
+ return paIncompatibleHostApiSpecificStreamInfo; /* this implementation doesn't use custom stream info */
+ }
+ else
+ {
+ inputChannelCount = 0;
+ }
+
+ if( outputParameters )
+ {
+ outputChannelCount = outputParameters->channelCount;
+ outputSampleFormat = outputParameters->sampleFormat;
+
+ /* unless alternate device specification is supported, reject the use of
+ paUseHostApiSpecificDeviceSpecification */
+
+ if( outputParameters->device == paUseHostApiSpecificDeviceSpecification )
+ return paInvalidDevice;
+
+ /* check that output device can support inputChannelCount */
+ if( outputChannelCount > hostApi->deviceInfos[ outputParameters->device ]->maxOutputChannels )
+ return paInvalidChannelCount;
+
+ /* validate outputStreamInfo */
+ if( outputParameters->hostApiSpecificStreamInfo )
+ return paIncompatibleHostApiSpecificStreamInfo; /* this implementation doesn't use custom stream info */
+ }
+ else
+ {
+ outputChannelCount = 0;
+ }
+
+ if (inputChannelCount == 0 && outputChannelCount == 0)
+ return paInvalidChannelCount;
+
+ /* if full duplex, make sure that they're the same device */
+
+ if (inputChannelCount > 0 && outputChannelCount > 0 &&
+ inputParameters->device != outputParameters->device)
+ return paInvalidDevice;
+
+ /* if full duplex, also make sure that they're the same number of channels */
+
+ if (inputChannelCount > 0 && outputChannelCount > 0 &&
+ inputChannelCount != outputChannelCount)
+ return paInvalidChannelCount;
+
+ /* open the device so we can do more tests */
+
+ if( inputChannelCount > 0 )
+ {
+ result = PaUtil_DeviceIndexToHostApiDeviceIndex(&device, inputParameters->device, hostApi);
+ if (result != paNoError)
+ return result;
+ }
+ else
+ {
+ result = PaUtil_DeviceIndexToHostApiDeviceIndex(&device, outputParameters->device, hostApi);
+ if (result != paNoError)
+ return result;
+ }
+
+ deviceInfo = hostApi->deviceInfos[device];
+ deviceName = (char *)deviceInfo->name;
+
+ flags = O_NONBLOCK;
+ if (inputChannelCount > 0 && outputChannelCount > 0)
+ flags |= O_RDWR;
+ else if (inputChannelCount > 0)
+ flags |= O_RDONLY;
+ else
+ flags |= O_WRONLY;
+
+ ENSURE_( tempDevHandle = open( deviceInfo->name, flags ), paDeviceUnavailable );
+
+ /* PaOssStream_Configure will do the rest of the checking for us */
+ /* PA_ENSURE( PaOssStream_Configure( tempDevHandle, deviceName, outputChannelCount, &sampleRate ) ); */
+
+ /* everything succeeded! */
+
+ error:
+ if( tempDevHandle >= 0 )
+ close( tempDevHandle );
+
+ return result;
+}
+
+/** Validate stream parameters.
+ *
+ * Aspect StreamChannels: We verify that the number of channels is within the allowed range for the device
+ */
+static PaError ValidateParameters( const PaStreamParameters *parameters, const PaDeviceInfo *deviceInfo, StreamMode mode )
+{
+ int maxChans;
+
+ assert( parameters );
+
+ if( parameters->device == paUseHostApiSpecificDeviceSpecification )
+ {
+ return paInvalidDevice;
+ }
+
+ maxChans = (mode == StreamMode_In ? deviceInfo->maxInputChannels :
+ deviceInfo->maxOutputChannels);
+ if( parameters->channelCount > maxChans )
+ {
+ return paInvalidChannelCount;
+ }
+
+ return paNoError;
+}
+
+static PaError PaOssStreamComponent_Initialize( PaOssStreamComponent *component, const PaStreamParameters *parameters,
+ int callbackMode, int fd, const char *deviceName )
+{
+ PaError result = paNoError;
+ assert( component );
+
+ memset( component, 0, sizeof (PaOssStreamComponent) );
+
+ component->fd = fd;
+ component->devName = deviceName;
+ component->userChannelCount = parameters->channelCount;
+ component->userFormat = parameters->sampleFormat;
+ component->latency = parameters->suggestedLatency;
+ component->userInterleaved = !(parameters->sampleFormat & paNonInterleaved);
+
+ if( !callbackMode && !component->userInterleaved )
+ {
+ /* Pre-allocate non-interleaved user provided buffers */
+ PA_UNLESS( component->userBuffers = PaUtil_AllocateMemory( sizeof (void *) * component->userChannelCount ),
+ paInsufficientMemory );
+ }
+
+error:
+ return result;
+}
+
+static void PaOssStreamComponent_Terminate( PaOssStreamComponent *component )
+{
+ assert( component );
+
+ if( component->fd >= 0 )
+ close( component->fd );
+ if( component->buffer )
+ PaUtil_FreeMemory( component->buffer );
+
+ if( component->userBuffers )
+ PaUtil_FreeMemory( component->userBuffers );
+
+ PaUtil_FreeMemory( component );
+}
+
+static PaError ModifyBlocking( int fd, int blocking )
+{
+ PaError result = paNoError;
+ int fflags;
+
+ ENSURE_( fflags = fcntl( fd, F_GETFL ), paUnanticipatedHostError );
+
+ if( blocking )
+ fflags &= ~O_NONBLOCK;
+ else
+ fflags |= O_NONBLOCK;
+
+ ENSURE_( fcntl( fd, F_SETFL, fflags ), paUnanticipatedHostError );
+
+error:
+ return result;
+}
+
+static PaError OpenDevices( const char *idevName, const char *odevName, int *idev, int *odev )
+{
+ PaError result = paNoError;
+ int flags = O_NONBLOCK, duplex = 0;
+ int enableBits = 0;
+ *idev = *odev = -1;
+
+ if( idevName && odevName )
+ {
+ duplex = 1;
+ flags |= O_RDWR;
+ }
+ else if( idevName )
+ flags |= O_RDONLY;
+ else
+ flags |= O_WRONLY;
+
+ /* open first in nonblocking mode, in case it's busy...
+ * A: then unset the non-blocking attribute */
+ assert( flags & O_NONBLOCK );
+ if( idevName )
+ {
+ ENSURE_( *idev = open( idevName, flags ), paDeviceUnavailable );
+ PA_ENSURE( ModifyBlocking( *idev, 1 ) ); /* Blocking */
+
+ /* Initially disable */
+ enableBits = ~PCM_ENABLE_INPUT;
+ ENSURE_( ioctl( *idev, SNDCTL_DSP_SETTRIGGER, &enableBits ), paUnanticipatedHostError );
+ }
+ if( odevName )
+ {
+ if( !idevName )
+ {
+ ENSURE_( *odev = open( odevName, flags ), paDeviceUnavailable );
+ PA_ENSURE( ModifyBlocking( *odev, 1 ) ); /* Blocking */
+
+ /* Initially disable */
+ enableBits = ~PCM_ENABLE_OUTPUT;
+ ENSURE_( ioctl( *odev, SNDCTL_DSP_SETTRIGGER, &enableBits ), paUnanticipatedHostError );
+ }
+ else
+ {
+ ENSURE_( *odev = dup( *idev ), paUnanticipatedHostError );
+ }
+ }
+
+error:
+ return result;
+}
+
+static PaError PaOssStream_Initialize( PaOssStream *stream, const PaStreamParameters *inputParameters, const PaStreamParameters *outputParameters,
+ PaStreamCallback callback, void *userData, PaStreamFlags streamFlags,
+ PaOSSHostApiRepresentation *ossApi )
+{
+ PaError result = paNoError;
+ int idev, odev;
+ PaUtilHostApiRepresentation *hostApi = &ossApi->inheritedHostApiRep;
+ const char *idevName = NULL, *odevName = NULL;
+
+ assert( stream );
+
+ memset( stream, 0, sizeof (PaOssStream) );
+ stream->isStopped = 1;
+
+ PA_ENSURE( PaUtil_InitializeThreading( &stream->threading ) );
+
+ if( inputParameters && outputParameters )
+ {
+ if( inputParameters->device == outputParameters->device )
+ stream->sharedDevice = 1;
+ }
+
+ if( inputParameters )
+ idevName = hostApi->deviceInfos[inputParameters->device]->name;
+ if( outputParameters )
+ odevName = hostApi->deviceInfos[outputParameters->device]->name;
+ PA_ENSURE( OpenDevices( idevName, odevName, &idev, &odev ) );
+ if( inputParameters )
+ {
+ PA_UNLESS( stream->capture = PaUtil_AllocateMemory( sizeof (PaOssStreamComponent) ), paInsufficientMemory );
+ PA_ENSURE( PaOssStreamComponent_Initialize( stream->capture, inputParameters, callback != NULL, idev, idevName ) );
+ }
+ if( outputParameters )
+ {
+ PA_UNLESS( stream->playback = PaUtil_AllocateMemory( sizeof (PaOssStreamComponent) ), paInsufficientMemory );
+ PA_ENSURE( PaOssStreamComponent_Initialize( stream->playback, outputParameters, callback != NULL, odev, odevName ) );
+ }
+
+ if( callback != NULL )
+ {
+ PaUtil_InitializeStreamRepresentation( &stream->streamRepresentation,
+ &ossApi->callbackStreamInterface, callback, userData );
+ stream->callbackMode = 1;
+ }
+ else
+ {
+ PaUtil_InitializeStreamRepresentation( &stream->streamRepresentation,
+ &ossApi->blockingStreamInterface, callback, userData );
+ }
+
+ ENSURE_( sem_init( &stream->semaphore, 0, 0 ), paInternalError );
+
+error:
+ return result;
+}
+
+static void PaOssStream_Terminate( PaOssStream *stream )
+{
+ assert( stream );
+
+ PaUtil_TerminateStreamRepresentation( &stream->streamRepresentation );
+ PaUtil_TerminateThreading( &stream->threading );
+
+ if( stream->capture )
+ PaOssStreamComponent_Terminate( stream->capture );
+ if( stream->playback )
+ PaOssStreamComponent_Terminate( stream->playback );
+
+ sem_destroy( &stream->semaphore );
+
+ PaUtil_FreeMemory( stream );
+}
+
+/** Translate from PA format to OSS native.
+ *
+ */
+static PaError Pa2OssFormat( PaSampleFormat paFormat, int *ossFormat )
+{
+ switch( paFormat )
+ {
+ case paUInt8:
+ *ossFormat = AFMT_U8;
+ break;
+ case paInt8:
+ *ossFormat = AFMT_S8;
+ break;
+ case paInt16:
+ *ossFormat = AFMT_S16_NE;
+ break;
+ default:
+ return paInternalError; /* This shouldn't happen */
+ }
+
+ return paNoError;
+}
+
+/** Return the PA-compatible formats that this device can support.
+ *
+ */
+static PaError GetAvailableFormats( PaOssStreamComponent *component, PaSampleFormat *availableFormats )
+{
+ PaError result = paNoError;
+ int mask = 0;
+ PaSampleFormat frmts = 0;
+
+ ENSURE_( ioctl( component->fd, SNDCTL_DSP_GETFMTS, &mask ), paUnanticipatedHostError );
+ if( mask & AFMT_U8 )
+ frmts |= paUInt8;
+ if( mask & AFMT_S8 )
+ frmts |= paInt8;
+ if( mask & AFMT_S16_NE )
+ frmts |= paInt16;
+ else
+ result = paSampleFormatNotSupported;
+
+ *availableFormats = frmts;
+
+error:
+ return result;
+}
+
+static unsigned int PaOssStreamComponent_FrameSize( PaOssStreamComponent *component )
+{
+ return Pa_GetSampleSize( component->hostFormat ) * component->hostChannelCount;
+}
+
+/** Buffer size in bytes.
+ *
+ */
+static unsigned long PaOssStreamComponent_BufferSize( PaOssStreamComponent *component )
+{
+ return PaOssStreamComponent_FrameSize( component ) * component->hostFrames * component->numBufs;
+}
+
+static int CalcHigherLogTwo( int n )
+{
+ int log2 = 0;
+ while( (1<<log2) < n ) log2++;
+ return log2;
+}
+
+static PaError PaOssStreamComponent_Configure( PaOssStreamComponent *component, double sampleRate, unsigned long framesPerBuffer,
+ StreamMode streamMode, PaOssStreamComponent *master )
+{
+ PaError result = paNoError;
+ int temp, nativeFormat;
+ int sr = (int)sampleRate;
+ PaSampleFormat availableFormats, hostFormat;
+ int chans = component->userChannelCount;
+ int frgmt;
+ int numBufs;
+ int bytesPerBuf;
+ double bufSz;
+ unsigned long fragSz;
+ audio_buf_info bufInfo;
+
+ /* We may have a situation where only one component (the master) is configured, if both point to the same device.
+ * In that case, the second component will copy settings from the other */
+ if( !master )
+ {
+ /* Aspect BufferSettings: If framesPerBuffer is unspecified we have to infer a suitable fragment size.
+ * The hardware need not respect the requested fragment size, so we may have to adapt.
+ */
+ if( framesPerBuffer == paFramesPerBufferUnspecified )
+ {
+ bufSz = component->latency * sampleRate;
+ fragSz = bufSz / 4;
+ }
+ else
+ {
+ fragSz = framesPerBuffer;
+ bufSz = component->latency * sampleRate + fragSz; /* Latency + 1 buffer */
+ }
+
+ PA_ENSURE( GetAvailableFormats( component, &availableFormats ) );
+ hostFormat = PaUtil_SelectClosestAvailableFormat( availableFormats, component->userFormat );
+
+ /* OSS demands at least 2 buffers, and 16 bytes per buffer */
+ numBufs = PA_MAX( bufSz / fragSz, 2 );
+ bytesPerBuf = PA_MAX( fragSz * Pa_GetSampleSize( hostFormat ) * chans, 16 );
+
+ /* The fragment parameters are encoded like this:
+ * Most significant byte: number of fragments
+ * Least significant byte: exponent of fragment size (i.e., for 256, 8)
+ */
+ frgmt = (numBufs << 16) + (CalcHigherLogTwo( bytesPerBuf ) & 0xffff);
+ ENSURE_( ioctl( component->fd, SNDCTL_DSP_SETFRAGMENT, &frgmt ), paUnanticipatedHostError );
+
+ /* A: according to the OSS programmer's guide parameters should be set in this order:
+ * format, channels, rate */
+
+ /* This format should be deemed good before we get this far */
+ PA_ENSURE( Pa2OssFormat( hostFormat, &temp ) );
+ nativeFormat = temp;
+ ENSURE_( ioctl( component->fd, SNDCTL_DSP_SETFMT, &temp ), paUnanticipatedHostError );
+ PA_UNLESS( temp == nativeFormat, paInternalError );
+
+ /* try to set the number of channels */
+ ENSURE_( ioctl( component->fd, SNDCTL_DSP_CHANNELS, &chans ), paSampleFormatNotSupported ); /* XXX: Should be paInvalidChannelCount? */
+ /* It's possible that the minimum number of host channels is greater than what the user requested */
+ PA_UNLESS( chans >= component->userChannelCount, paInvalidChannelCount );
+
+ /* try to set the sample rate */
+ ENSURE_( ioctl( component->fd, SNDCTL_DSP_SPEED, &sr ), paInvalidSampleRate );
+
+ /* reject if there's no sample rate within 1% of the one requested */
+ if( (fabs( sampleRate - sr ) / sampleRate) > 0.01 )
+ {
+ PA_DEBUG(("%s: Wanted %f, closest sample rate was %d\n", __FUNCTION__, sampleRate, sr ));
+ PA_ENSURE( paInvalidSampleRate );
+ }
+
+ ENSURE_( ioctl( component->fd, streamMode == StreamMode_In ? SNDCTL_DSP_GETISPACE : SNDCTL_DSP_GETOSPACE, &bufInfo ),
+ paUnanticipatedHostError );
+ component->numBufs = bufInfo.fragstotal;
+
+ /* This needs to be the last ioctl call before the first read/write, according to the OSS programmer's guide */
+ ENSURE_( ioctl( component->fd, SNDCTL_DSP_GETBLKSIZE, &bytesPerBuf ), paUnanticipatedHostError );
+
+ component->hostFrames = bytesPerBuf / Pa_GetSampleSize( hostFormat ) / chans;
+ component->hostChannelCount = chans;
+ component->hostFormat = hostFormat;
+ }
+ else
+ {
+ component->hostFormat = master->hostFormat;
+ component->hostFrames = master->hostFrames;
+ component->hostChannelCount = master->hostChannelCount;
+ component->numBufs = master->numBufs;
+ }
+
+ PA_UNLESS( component->buffer = PaUtil_AllocateMemory( PaOssStreamComponent_BufferSize( component ) ),
+ paInsufficientMemory );
+
+error:
+ return result;
+}
+
+static PaError PaOssStreamComponent_Read( PaOssStreamComponent *component, unsigned long *frames )
+{
+ PaError result = paNoError;
+ size_t len = *frames * PaOssStreamComponent_FrameSize( component );
+ ssize_t bytesRead;
+
+ ENSURE_( bytesRead = read( component->fd, component->buffer, len ), paUnanticipatedHostError );
+ *frames = bytesRead / PaOssStreamComponent_FrameSize( component );
+
+error:
+ return result;
+}
+
+static PaError PaOssStreamComponent_Write( PaOssStreamComponent *component, unsigned long *frames )
+{
+ PaError result = paNoError;
+ size_t len = *frames * PaOssStreamComponent_FrameSize( component );
+ ssize_t bytesWritten;
+
+ ENSURE_( bytesWritten = write( component->fd, component->buffer, len ), paUnanticipatedHostError );
+ *frames = bytesWritten / PaOssStreamComponent_FrameSize( component );
+
+error:
+ return result;
+}
+
+/** Configure the stream according to input/output parameters.
+ *
+ * Aspect StreamChannels: The minimum number of channels supported by the device may exceed that requested by
+ * the user, if so we'll record the actual number of host channels and adapt later.
+ */
+static PaError PaOssStream_Configure( PaOssStream *stream, double sampleRate, unsigned long framesPerBuffer,
+ double *inputLatency, double *outputLatency )
+{
+ PaError result = paNoError;
+ int duplex = stream->capture && stream->playback;
+ unsigned long framesPerHostBuffer = 0;
+
+ /* We should request full duplex first thing after opening the device */
+ if( duplex && stream->sharedDevice )
+ ENSURE_( ioctl( stream->capture->fd, SNDCTL_DSP_SETDUPLEX, 0 ), paUnanticipatedHostError );
+
+ if( stream->capture )
+ {
+ PaOssStreamComponent *component = stream->capture;
+ PaOssStreamComponent_Configure( component, sampleRate, framesPerBuffer, StreamMode_In, NULL );
+
+ assert( component->hostChannelCount > 0 );
+ assert( component->hostFrames > 0 );
+
+ *inputLatency = component->hostFrames * (component->numBufs - 1) / sampleRate;
+ }
+ if( stream->playback )
+ {
+ PaOssStreamComponent *component = stream->playback, *master = stream->sharedDevice ? stream->capture : NULL;
+ PA_ENSURE( PaOssStreamComponent_Configure( component, sampleRate, framesPerBuffer, StreamMode_Out,
+ master ) );
+
+ assert( component->hostChannelCount > 0 );
+ assert( component->hostFrames > 0 );
+
+ *outputLatency = component->hostFrames * (component->numBufs - 1) / sampleRate;
+ }
+
+ if( duplex )
+ framesPerHostBuffer = PA_MIN( stream->capture->hostFrames, stream->playback->hostFrames );
+ else if( stream->capture )
+ framesPerHostBuffer = stream->capture->hostFrames;
+ else if( stream->playback )
+ framesPerHostBuffer = stream->playback->hostFrames;
+
+ stream->framesPerHostBuffer = framesPerHostBuffer;
+ stream->pollTimeout = (int) ceil( 1e6 * framesPerHostBuffer / sampleRate ); /* Period in usecs, rounded up */
+
+ stream->streamRepresentation.streamInfo.sampleRate = sampleRate;
+
+error:
+ return result;
+}
+
+/* see pa_hostapi.h for a list of validity guarantees made about OpenStream parameters */
+
+/** Open a PA OSS stream.
+ *
+ * Aspect StreamChannels: The number of channels is specified per direction (in/out), and can differ between the
+ * two. However, OSS doesn't support separate configuration spaces for capture and playback so if both
+ * directions are the same device we will demand the same number of channels. The number of channels can range
+ * from 1 to the maximum supported by the device.
+ *
+ * Aspect BufferSettings: If framesPerBuffer != paFramesPerBufferUnspecified the number of frames per callback
+ * must reflect this, in addition the host latency per device should approximate the corresponding
+ * suggestedLatency. Based on these constraints we need to determine a number of frames per host buffer that
+ * both capture and playback can agree on (they can be different devices), the buffer processor can adapt
+ * between host and user buffer size, but the ratio should preferably be integral.
+ */
+static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi,
+ PaStream** s,
+ const PaStreamParameters *inputParameters,
+ const PaStreamParameters *outputParameters,
+ double sampleRate,
+ unsigned long framesPerBuffer,
+ PaStreamFlags streamFlags,
+ PaStreamCallback *streamCallback,
+ void *userData )
+{
+ PaError result = paNoError;
+ PaOSSHostApiRepresentation *ossHostApi = (PaOSSHostApiRepresentation*)hostApi;
+ PaOssStream *stream = NULL;
+ int inputChannelCount = 0, outputChannelCount = 0;
+ PaSampleFormat inputSampleFormat = 0, outputSampleFormat = 0, inputHostFormat = 0, outputHostFormat = 0;
+ const PaDeviceInfo *inputDeviceInfo = 0, *outputDeviceInfo = 0;
+ int bpInitialized = 0;
+ double inLatency, outLatency;
+
+ /* validate platform specific flags */
+ if( (streamFlags & paPlatformSpecificFlags) != 0 )
+ return paInvalidFlag; /* unexpected platform specific flag */
+
+ if( inputParameters )
+ {
+ /* unless alternate device specification is supported, reject the use of
+ paUseHostApiSpecificDeviceSpecification */
+ inputDeviceInfo = hostApi->deviceInfos[inputParameters->device];
+ PA_ENSURE( ValidateParameters( inputParameters, inputDeviceInfo, StreamMode_In ) );
+
+ inputChannelCount = inputParameters->channelCount;
+ inputSampleFormat = inputParameters->sampleFormat;
+ }
+ if( outputParameters )
+ {
+ outputDeviceInfo = hostApi->deviceInfos[outputParameters->device];
+ PA_ENSURE( ValidateParameters( outputParameters, outputDeviceInfo, StreamMode_Out ) );
+
+ outputChannelCount = outputParameters->channelCount;
+ outputSampleFormat = outputParameters->sampleFormat;
+ }
+
+ /* Aspect StreamChannels: We currently demand that number of input and output channels are the same, if the same
+ * device is opened for both directions
+ */
+ if( inputChannelCount > 0 && outputChannelCount > 0 )
+ {
+ if( inputParameters->device == outputParameters->device )
+ {
+ if( inputParameters->channelCount != outputParameters->channelCount )
+ return paInvalidChannelCount;
+ }
+ }
+
+ /* allocate and do basic initialization of the stream structure */
+ PA_UNLESS( stream = (PaOssStream*)PaUtil_AllocateMemory( sizeof(PaOssStream) ), paInsufficientMemory );
+ PaOssStream_Initialize( stream, inputParameters, outputParameters, streamCallback, userData, streamFlags, ossHostApi );
+
+ PA_ENSURE( PaOssStream_Configure( stream, sampleRate, framesPerBuffer, &inLatency, &outLatency ) );
+
+ PaUtil_InitializeCpuLoadMeasurer( &stream->cpuLoadMeasurer, sampleRate );
+
+ if( inputParameters )
+ {
+ inputHostFormat = stream->capture->hostFormat;
+ stream->streamRepresentation.streamInfo.inputLatency = inLatency +
+ PaUtil_GetBufferProcessorInputLatency( &stream->bufferProcessor ) / sampleRate;
+ }
+ if( outputParameters )
+ {
+ outputHostFormat = stream->playback->hostFormat;
+ stream->streamRepresentation.streamInfo.outputLatency = outLatency +
+ PaUtil_GetBufferProcessorOutputLatency( &stream->bufferProcessor ) / sampleRate;
+ }
+
+ /* Initialize buffer processor with fixed host buffer size.
+ * Aspect StreamSampleFormat: Here we commit the user and host sample formats, PA infrastructure will
+ * convert between the two.
+ */
+ PA_ENSURE( PaUtil_InitializeBufferProcessor( &stream->bufferProcessor,
+ inputChannelCount, inputSampleFormat, inputHostFormat, outputChannelCount, outputSampleFormat,
+ outputHostFormat, sampleRate, streamFlags, framesPerBuffer, stream->framesPerHostBuffer,
+ paUtilFixedHostBufferSize, streamCallback, userData ) );
+ bpInitialized = 1;
+
+ *s = (PaStream*)stream;
+
+ return result;
+
+error:
+ if( bpInitialized )
+ PaUtil_TerminateBufferProcessor( &stream->bufferProcessor );
+ if( stream )
+ PaOssStream_Terminate( stream );
+
+ return result;
+}
+
+/*! Poll on I/O filedescriptors.
+
+ Poll till we've determined there's data for read or write. In the full-duplex case,
+ we don't want to hang around forever waiting for either input or output frames, so
+ whenever we have a timed out filedescriptor we check if we're nearing under/overrun
+ for the other direction (critical limit set at one buffer). If so, we exit the waiting
+ state, and go on with what we got. We align the number of frames on a host buffer
+ boundary because it is possible that the buffer size differs for the two directions and
+ the host buffer size is a compromise between the two.
+ */
+static PaError PaOssStream_WaitForFrames( PaOssStream *stream, unsigned long *frames )
+{
+ PaError result = paNoError;
+ int pollPlayback = 0, pollCapture = 0;
+ int captureAvail = INT_MAX, playbackAvail = INT_MAX, commonAvail;
+ audio_buf_info bufInfo;
+ /* int ofs = 0, nfds = stream->nfds; */
+ fd_set readFds, writeFds;
+ int nfds = 0;
+ struct timeval selectTimeval = {0, 0};
+ unsigned long timeout = stream->pollTimeout; /* In usecs */
+ int captureFd = -1, playbackFd = -1;
+
+ assert( stream );
+ assert( frames );
+
+ if( stream->capture )
+ {
+ pollCapture = 1;
+ captureFd = stream->capture->fd;
+ /* stream->capture->pfd->events = POLLIN; */
+ }
+ if( stream->playback )
+ {
+ pollPlayback = 1;
+ playbackFd = stream->playback->fd;
+ /* stream->playback->pfd->events = POLLOUT; */
+ }
+
+ FD_ZERO( &readFds );
+ FD_ZERO( &writeFds );
+
+ while( pollPlayback || pollCapture )
+ {
+ pthread_testcancel();
+
+ /* select may modify the timeout parameter */
+ selectTimeval.tv_usec = timeout;
+ nfds = 0;
+
+ if( pollCapture )
+ {
+ FD_SET( captureFd, &readFds );
+ nfds = captureFd + 1;
+ }
+ if( pollPlayback )
+ {
+ FD_SET( playbackFd, &writeFds );
+ nfds = PA_MAX( nfds, playbackFd + 1 );
+ }
+ ENSURE_( select( nfds, &readFds, &writeFds, NULL, &selectTimeval ), paUnanticipatedHostError );
+ /*
+ if( poll( stream->pfds + ofs, nfds, stream->pollTimeout ) < 0 )
+ {
+
+ ENSURE_( -1, paUnanticipatedHostError );
+ }
+ */
+ pthread_testcancel();
+
+ if( pollCapture )
+ {
+ if( FD_ISSET( captureFd, &readFds ) )
+ {
+ FD_CLR( captureFd, &readFds );
+ pollCapture = 0;
+ }
+ /*
+ if( stream->capture->pfd->revents & POLLIN )
+ {
+ --nfds;
+ ++ofs;
+ pollCapture = 0;
+ }
+ */
+ else if( stream->playback ) /* Timed out, go on with playback? */
+ {
+ /*PA_DEBUG(( "%s: Trying to poll again for capture frames, pollTimeout: %d\n",
+ __FUNCTION__, stream->pollTimeout ));*/
+ }
+ }
+ if( pollPlayback )
+ {
+ if( FD_ISSET( playbackFd, &writeFds ) )
+ {
+ FD_CLR( playbackFd, &writeFds );
+ pollPlayback = 0;
+ }
+ /*
+ if( stream->playback->pfd->revents & POLLOUT )
+ {
+ --nfds;
+ pollPlayback = 0;
+ }
+ */
+ else if( stream->capture ) /* Timed out, go on with capture? */
+ {
+ /*PA_DEBUG(( "%s: Trying to poll again for playback frames, pollTimeout: %d\n\n",
+ __FUNCTION__, stream->pollTimeout ));*/
+ }
+ }
+ }
+
+ if( stream->capture )
+ {
+ ENSURE_( ioctl( captureFd, SNDCTL_DSP_GETISPACE, &bufInfo ), paUnanticipatedHostError );
+ captureAvail = bufInfo.fragments * stream->capture->hostFrames;
+ if( !captureAvail )
+ PA_DEBUG(( "%s: captureAvail: 0\n", __FUNCTION__ ));
+
+ captureAvail = captureAvail == 0 ? INT_MAX : captureAvail; /* Disregard if zero */
+ }
+ if( stream->playback )
+ {
+ ENSURE_( ioctl( playbackFd, SNDCTL_DSP_GETOSPACE, &bufInfo ), paUnanticipatedHostError );
+ playbackAvail = bufInfo.fragments * stream->playback->hostFrames;
+ if( !playbackAvail )
+ {
+ PA_DEBUG(( "%s: playbackAvail: 0\n", __FUNCTION__ ));
+ }
+
+ playbackAvail = playbackAvail == 0 ? INT_MAX : playbackAvail; /* Disregard if zero */
+ }
+
+ commonAvail = PA_MIN( captureAvail, playbackAvail );
+ if( commonAvail == INT_MAX )
+ commonAvail = 0;
+ commonAvail -= commonAvail % stream->framesPerHostBuffer;
+
+ assert( commonAvail != INT_MAX );
+ assert( commonAvail >= 0 );
+ *frames = commonAvail;
+
+error:
+ return result;
+}
+
+/** Prepare stream for capture/playback.
+ *
+ * In order to synchronize capture and playback properly we use the SETTRIGGER command.
+ */
+static PaError PaOssStream_Prepare( PaOssStream *stream )
+{
+ PaError result = paNoError;
+ int enableBits = 0;
+
+ if( stream->triggered )
+ return result;
+
+ if( stream->playback )
+ {
+ size_t bufSz = PaOssStreamComponent_BufferSize( stream->playback );
+ memset( stream->playback->buffer, 0, bufSz );
+
+ /* Looks like we have to turn off blocking before we try this, but if we don't fill the buffer
+ * OSS will complain. */
+ PA_ENSURE( ModifyBlocking( stream->playback->fd, 0 ) );
+ while (1)
+ {
+ if( write( stream->playback->fd, stream->playback->buffer, bufSz ) < 0 )
+ break;
+ }
+ PA_ENSURE( ModifyBlocking( stream->playback->fd, 1 ) );
+ }
+
+ if( stream->sharedDevice )
+ {
+ enableBits = PCM_ENABLE_INPUT | PCM_ENABLE_OUTPUT;
+ ENSURE_( ioctl( stream->capture->fd, SNDCTL_DSP_SETTRIGGER, &enableBits ), paUnanticipatedHostError );
+ }
+ else
+ {
+ if( stream->capture )
+ {
+ enableBits = PCM_ENABLE_INPUT;
+ ENSURE_( ioctl( stream->capture->fd, SNDCTL_DSP_SETTRIGGER, &enableBits ), paUnanticipatedHostError );
+ }
+ if( stream->playback )
+ {
+ enableBits = PCM_ENABLE_OUTPUT;
+ ENSURE_( ioctl( stream->playback->fd, SNDCTL_DSP_SETTRIGGER, &enableBits ), paUnanticipatedHostError );
+ }
+ }
+
+ /* Ok, we have triggered the stream */
+ stream->triggered = 1;
+
+error:
+ return result;
+}
+
+/** Stop audio processing
+ *
+ */
+static PaError PaOssStream_Stop( PaOssStream *stream, int abort )
+{
+ PaError result = paNoError;
+
+ /* Looks like the only safe way to stop audio without reopening the device is SNDCTL_DSP_POST.
+ * Also disable capture/playback till the stream is started again */
+ if( stream->capture )
+ {
+ ENSURE_( ioctl( stream->capture->fd, SNDCTL_DSP_POST, 0 ), paUnanticipatedHostError );
+ }
+ if( stream->playback && !stream->sharedDevice )
+ {
+ ENSURE_( ioctl( stream->playback->fd, SNDCTL_DSP_POST, 0 ), paUnanticipatedHostError );
+ }
+
+error:
+ return result;
+}
+
+/** Clean up after thread exit.
+ *
+ * Aspect StreamState: If the user has registered a streamFinishedCallback it will be called here
+ */
+static void OnExit( void *data )
+{
+ PaOssStream *stream = (PaOssStream *) data;
+ assert( data );
+
+ PaUtil_ResetCpuLoadMeasurer( &stream->cpuLoadMeasurer );
+
+ PaOssStream_Stop( stream, stream->callbackAbort );
+
+ PA_DEBUG(( "OnExit: Stoppage\n" ));
+
+ /* Eventually notify user all buffers have played */
+ if( stream->streamRepresentation.streamFinishedCallback )
+ stream->streamRepresentation.streamFinishedCallback( stream->streamRepresentation.userData );
+
+ stream->callbackAbort = 0; /* Clear state */
+ stream->isActive = 0;
+}
+
+static PaError SetUpBuffers( PaOssStream *stream, unsigned long framesAvail )
+{
+ PaError result = paNoError;
+
+ if( stream->capture )
+ {
+ PaUtil_SetInterleavedInputChannels( &stream->bufferProcessor, 0, stream->capture->buffer,
+ stream->capture->hostChannelCount );
+ PaUtil_SetInputFrameCount( &stream->bufferProcessor, framesAvail );
+ }
+ if( stream->playback )
+ {
+ PaUtil_SetInterleavedOutputChannels( &stream->bufferProcessor, 0, stream->playback->buffer,
+ stream->playback->hostChannelCount );
+ PaUtil_SetOutputFrameCount( &stream->bufferProcessor, framesAvail );
+ }
+
+ return result;
+}
+
+/** Thread procedure for callback processing.
+ *
+ * Aspect StreamState: StartStream will wait on this to initiate audio processing, useful in case the
+ * callback should be used for buffer priming. When the stream is cancelled a separate function will
+ * take care of the transition to the Callback Finished state (the stream isn't considered Stopped
+ * before StopStream() or AbortStream() are called).
+ */
+static void *PaOSS_AudioThreadProc( void *userData )
+{
+ PaError result = paNoError;
+ PaOssStream *stream = (PaOssStream*)userData;
+ unsigned long framesAvail, framesProcessed;
+ int callbackResult = paContinue;
+ int triggered = stream->triggered; /* See if SNDCTL_DSP_TRIGGER has been issued already */
+ int initiateProcessing = triggered; /* Already triggered? */
+ PaStreamCallbackFlags cbFlags = 0; /* We might want to keep state across iterations */
+
+ /*
+#if ( SOUND_VERSION > 0x030904 )
+ audio_errinfo errinfo;
+#endif
+*/
+
+ assert( stream );
+
+ pthread_cleanup_push( &OnExit, stream ); /* Execute OnExit when exiting */
+
+ /* The first time the stream is started we use SNDCTL_DSP_TRIGGER to accurately start capture and
+ * playback in sync, when the stream is restarted after being stopped we simply start by reading/
+ * writing.
+ */
+ PA_ENSURE( PaOssStream_Prepare( stream ) );
+
+ /* If we are to initiate processing implicitly by reading/writing data, we start off in blocking mode */
+ if( initiateProcessing )
+ {
+ /* Make sure devices are in blocking mode */
+ if( stream->capture )
+ ModifyBlocking( stream->capture->fd, 1 );
+ if( stream->playback )
+ ModifyBlocking( stream->playback->fd, 1 );
+ }
+
+ while( 1 )
+ {
+ PaStreamCallbackTimeInfo timeInfo = {0,0,0}; /* TODO: IMPLEMENT ME */
+
+ pthread_testcancel();
+
+ if( stream->callbackStop && callbackResult == paContinue )
+ {
+ PA_DEBUG(( "Setting callbackResult to paComplete\n" ));
+ callbackResult = paComplete;
+ }
+
+ /* Aspect StreamState: Because of the messy OSS scheme we can't explicitly trigger device start unless
+ * the stream has been recently started, we will have to go right ahead and read/write in blocking
+ * fashion to trigger operation. Therefore we begin with processing one host buffer before we switch
+ * to non-blocking mode.
+ */
+ if( !initiateProcessing )
+ {
+ PA_ENSURE( PaOssStream_WaitForFrames( stream, &framesAvail ) ); /* Wait on available frames */
+ assert( framesAvail % stream->framesPerHostBuffer == 0 );
+ }
+ else
+ {
+ framesAvail = stream->framesPerHostBuffer;
+ }
+
+ while( framesAvail > 0 )
+ {
+ unsigned long frames = framesAvail;
+
+ pthread_testcancel();
+
+ PaUtil_BeginCpuLoadMeasurement( &stream->cpuLoadMeasurer );
+
+ /* Read data */
+ if ( stream->capture )
+ {
+ PA_ENSURE( PaOssStreamComponent_Read( stream->capture, &frames ) );
+ assert( frames == framesAvail );
+ }
+
+#if ( SOUND_VERSION >= 0x030904 )
+ /*
+ Check with OSS to see if there have been any under/overruns
+ since last time we checked.
+ */
+ /*
+ if( ioctl( stream->deviceHandle, SNDCTL_DSP_GETERROR, &errinfo ) >= 0 )
+ {
+ if( errinfo.play_underruns )
+ cbFlags |= paOutputUnderflow ;
+ if( errinfo.record_underruns )
+ cbFlags |= paInputUnderflow ;
+ }
+ else
+ PA_DEBUG(( "SNDCTL_DSP_GETERROR command failed: %s\n", strerror( errno ) ));
+ */
+#endif
+
+ PaUtil_BeginBufferProcessing( &stream->bufferProcessor, &timeInfo,
+ cbFlags );
+ cbFlags = 0;
+ PA_ENSURE( SetUpBuffers( stream, framesAvail ) );
+
+ framesProcessed = PaUtil_EndBufferProcessing( &stream->bufferProcessor,
+ &callbackResult );
+ assert( framesProcessed == framesAvail );
+ PaUtil_EndCpuLoadMeasurement( &stream->cpuLoadMeasurer, framesProcessed );
+
+ if ( stream->playback )
+ {
+ frames = framesAvail;
+
+ PA_ENSURE( PaOssStreamComponent_Write( stream->playback, &frames ) );
+ assert( frames == framesAvail );
+
+ /* TODO: handle bytesWritten != bytesRequested (slippage?) */
+ }
+
+ framesAvail -= framesProcessed;
+ stream->framesProcessed += framesProcessed;
+
+ if( callbackResult != paContinue )
+ break;
+ }
+
+ if( initiateProcessing || !triggered )
+ {
+ /* Non-blocking */
+ if( stream->capture )
+ PA_ENSURE( ModifyBlocking( stream->capture->fd, 0 ) );
+ if( stream->playback && !stream->sharedDevice )
+ PA_ENSURE( ModifyBlocking( stream->playback->fd, 0 ) );
+
+ initiateProcessing = 0;
+ sem_post( &stream->semaphore );
+ }
+
+ if( callbackResult != paContinue )
+ {
+ stream->callbackAbort = callbackResult == paAbort;
+ if( stream->callbackAbort || PaUtil_IsBufferProcessorOutputEmpty( &stream->bufferProcessor ) )
+ break;
+ }
+ }
+
+ pthread_cleanup_pop( 1 );
+
+error:
+ pthread_exit( NULL );
+}
+
+/** Close the stream.
+ *
+ */
+static PaError CloseStream( PaStream* s )
+{
+ PaError result = paNoError;
+ PaOssStream *stream = (PaOssStream*)s;
+
+ assert( stream );
+
+ PaUtil_TerminateBufferProcessor( &stream->bufferProcessor );
+ PaOssStream_Terminate( stream );
+
+ return result;
+}
+
+/** Start the stream.
+ *
+ * Aspect StreamState: After returning, the stream shall be in the Active state, implying that an eventual
+ * callback will be repeatedly called in a separate thread. If a separate thread is started this function
+ * will block untill it has started processing audio, otherwise audio processing is started directly.
+ */
+static PaError StartStream( PaStream *s )
+{
+ PaError result = paNoError;
+ PaOssStream *stream = (PaOssStream*)s;
+
+ stream->isActive = 1;
+ stream->isStopped = 0;
+ stream->lastPosPtr = 0;
+ stream->lastStreamBytes = 0;
+ stream->framesProcessed = 0;
+
+ /* only use the thread for callback streams */
+ if( stream->bufferProcessor.streamCallback )
+ {
+ PA_ENSURE( PaUtil_StartThreading( &stream->threading, &PaOSS_AudioThreadProc, stream ) );
+ sem_wait( &stream->semaphore );
+ }
+ else
+ PA_ENSURE( PaOssStream_Prepare( stream ) );
+
+error:
+ return result;
+}
+
+static PaError RealStop( PaOssStream *stream, int abort )
+{
+ PaError result = paNoError;
+
+ if( stream->callbackMode )
+ {
+ if( abort )
+ stream->callbackAbort = 1;
+ else
+ stream->callbackStop = 1;
+
+ PA_ENSURE( PaUtil_CancelThreading( &stream->threading, !abort, NULL ) );
+
+ stream->callbackStop = stream->callbackAbort = 0;
+ }
+ else
+ PA_ENSURE( PaOssStream_Stop( stream, abort ) );
+
+ stream->isStopped = 1;
+
+error:
+ return result;
+}
+
+/** Stop the stream.
+ *
+ * Aspect StreamState: This will cause the stream to transition to the Stopped state, playing all enqueued
+ * buffers.
+ */
+static PaError StopStream( PaStream *s )
+{
+ return RealStop( (PaOssStream *)s, 0 );
+}
+
+/** Abort the stream.
+ *
+ * Aspect StreamState: This will cause the stream to transition to the Stopped state, discarding all enqueued
+ * buffers. Note that the buffers are not currently correctly discarded, this is difficult without closing
+ * the OSS device.
+ */
+static PaError AbortStream( PaStream *s )
+{
+ return RealStop( (PaOssStream *)s, 1 );
+}
+
+/** Is the stream in the Stopped state.
+ *
+ */
+static PaError IsStreamStopped( PaStream *s )
+{
+ PaOssStream *stream = (PaOssStream*)s;
+
+ return (stream->isStopped);
+}
+
+/** Is the stream in the Active state.
+ *
+ */
+static PaError IsStreamActive( PaStream *s )
+{
+ PaOssStream *stream = (PaOssStream*)s;
+
+ return (stream->isActive);
+}
+
+static PaTime GetStreamTime( PaStream *s )
+{
+ PaOssStream *stream = (PaOssStream*)s;
+ count_info info;
+ int delta;
+
+ if( stream->playback ) {
+ if( ioctl( stream->playback->fd, SNDCTL_DSP_GETOPTR, &info) == 0 ) {
+ delta = ( info.bytes - stream->lastPosPtr ) & 0x000FFFFF;
+ return ( stream->lastStreamBytes + delta) / PaOssStreamComponent_FrameSize( stream->playback ) / stream->sampleRate;
+ }
+ }
+ else {
+ if (ioctl( stream->capture->fd, SNDCTL_DSP_GETIPTR, &info) == 0) {
+ delta = (info.bytes - stream->lastPosPtr) & 0x000FFFFF;
+ return ( stream->lastStreamBytes + delta) / PaOssStreamComponent_FrameSize( stream->capture ) / stream->sampleRate;
+ }
+ }
+
+ /* the ioctl failed, but we can still give a coarse estimate */
+
+ return stream->framesProcessed / stream->sampleRate;
+}
+
+
+static double GetStreamCpuLoad( PaStream* s )
+{
+ PaOssStream *stream = (PaOssStream*)s;
+
+ return PaUtil_GetCpuLoad( &stream->cpuLoadMeasurer );
+}
+
+
+/*
+ As separate stream interfaces are used for blocking and callback
+ streams, the following functions can be guaranteed to only be called
+ for blocking streams.
+*/
+
+
+static PaError ReadStream( PaStream* s,
+ void *buffer,
+ unsigned long frames )
+{
+ PaOssStream *stream = (PaOssStream*)s;
+ int bytesRequested, bytesRead;
+ unsigned long framesRequested;
+ void *userBuffer;
+
+ /* If user input is non-interleaved, PaUtil_CopyInput will manipulate the channel pointers,
+ * so we copy the user provided pointers */
+ if( stream->bufferProcessor.userInputIsInterleaved )
+ userBuffer = buffer;
+ else /* Copy channels into local array */
+ {
+ userBuffer = stream->capture->userBuffers;
+ memcpy( (void *)userBuffer, buffer, sizeof (void *) * stream->capture->userChannelCount );
+ }
+
+ while( frames )
+ {
+ framesRequested = PA_MIN( frames, stream->capture->hostFrames );
+
+ bytesRequested = framesRequested * PaOssStreamComponent_FrameSize( stream->capture );
+ bytesRead = read( stream->capture->fd, stream->capture->buffer, bytesRequested );
+ if ( bytesRequested != bytesRead )
+ return paUnanticipatedHostError;
+
+ PaUtil_SetInputFrameCount( &stream->bufferProcessor, stream->capture->hostFrames );
+ PaUtil_SetInterleavedInputChannels( &stream->bufferProcessor, 0, stream->capture->buffer, stream->capture->hostChannelCount );
+ PaUtil_CopyInput( &stream->bufferProcessor, &userBuffer, framesRequested );
+ frames -= framesRequested;
+ }
+ return paNoError;
+}
+
+
+static PaError WriteStream( PaStream* s,
+ const void *buffer,
+ unsigned long frames )
+{
+ PaOssStream *stream = (PaOssStream*)s;
+ int bytesRequested, bytesWritten;
+ unsigned long framesConverted;
+ const void *userBuffer;
+
+ /* If user output is non-interleaved, PaUtil_CopyOutput will manipulate the channel pointers,
+ * so we copy the user provided pointers */
+ if( stream->bufferProcessor.userOutputIsInterleaved )
+ userBuffer = buffer;
+ else /* Copy channels into local array */
+ {
+ userBuffer = stream->playback->userBuffers;
+ memcpy( (void *)userBuffer, buffer, sizeof (void *) * stream->playback->userChannelCount );
+ }
+
+ while( frames )
+ {
+ PaUtil_SetOutputFrameCount( &stream->bufferProcessor, stream->playback->hostFrames );
+ PaUtil_SetInterleavedOutputChannels( &stream->bufferProcessor, 0, stream->playback->buffer, stream->playback->hostChannelCount );
+
+ framesConverted = PaUtil_CopyOutput( &stream->bufferProcessor, &userBuffer, frames );
+ frames -= framesConverted;
+
+ bytesRequested = framesConverted * PaOssStreamComponent_FrameSize( stream->playback );
+ bytesWritten = write( stream->playback->fd, stream->playback->buffer, bytesRequested );
+
+ if ( bytesRequested != bytesWritten )
+ return paUnanticipatedHostError;
+ }
+ return paNoError;
+}
+
+
+static signed long GetStreamReadAvailable( PaStream* s )
+{
+ PaOssStream *stream = (PaOssStream*)s;
+ audio_buf_info info;
+
+ if( ioctl( stream->capture->fd, SNDCTL_DSP_GETISPACE, &info ) < 0 )
+ return paUnanticipatedHostError;
+ return info.fragments * stream->capture->hostFrames;
+}
+
+
+/* TODO: Compute number of allocated bytes somewhere else, can we use ODELAY with capture */
+static signed long GetStreamWriteAvailable( PaStream* s )
+{
+ PaOssStream *stream = (PaOssStream*)s;
+ int delay = 0;
+
+ if( ioctl( stream->playback->fd, SNDCTL_DSP_GETODELAY, &delay ) < 0 )
+ return paUnanticipatedHostError;
+
+ return (PaOssStreamComponent_BufferSize( stream->playback ) - delay) / PaOssStreamComponent_FrameSize( stream->playback );
+}
+
diff --git a/pjmedia/src/pjmedia/portaudio/pa_unix_util.c b/pjmedia/src/pjmedia/portaudio/pa_unix_util.c
index 16866596..cec60913 100644
--- a/pjmedia/src/pjmedia/portaudio/pa_unix_util.c
+++ b/pjmedia/src/pjmedia/portaudio/pa_unix_util.c
@@ -1,173 +1,173 @@
-/*
- * $Id: pa_unix_util.c,v 1.1.2.7 2005/03/31 15:02:48 aknudsen Exp $
- * Portable Audio I/O Library
- * UNIX platform-specific support functions
- *
- * Based on the Open Source API proposed by Ross Bencina
- * Copyright (c) 1999-2000 Ross Bencina
- *
- * 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.
- *
- * Any person wishing to distribute modifications to the Software is
- * requested to send the modifications to the original developer so that
- * they can be incorporated into the canonical version.
- *
- * 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 <pthread.h>
-#include <unistd.h>
-#include <stdlib.h>
-#include <sys/time.h>
-#include <assert.h>
-#include <string.h> /* For memset */
-
-#include "pa_util.h"
-#include "pa_unix_util.h"
-
-/*
- Track memory allocations to avoid leaks.
- */
-
-#if PA_TRACK_MEMORY
-static int numAllocations_ = 0;
-#endif
-
-
-void *PaUtil_AllocateMemory( long size )
-{
- void *result = malloc( size );
-
-#if PA_TRACK_MEMORY
- if( result != NULL ) numAllocations_ += 1;
-#endif
- return result;
-}
-
-
-void PaUtil_FreeMemory( void *block )
-{
- if( block != NULL )
- {
- free( block );
-#if PA_TRACK_MEMORY
- numAllocations_ -= 1;
-#endif
-
- }
-}
-
-
-int PaUtil_CountCurrentlyAllocatedBlocks( void )
-{
-#if PA_TRACK_MEMORY
- return numAllocations_;
-#else
- return 0;
-#endif
-}
-
-
-void Pa_Sleep( long msec )
-{
- while( msec > 999 ) /* For OpenBSD and IRIX, argument */
- { /* to usleep must be < 1000000. */
- usleep( 999000 );
- msec -= 999;
- }
- usleep( msec * 1000 );
-}
-
-/* *** NOT USED YET: ***
-static int usePerformanceCounter_;
-static double microsecondsPerTick_;
-*/
-
-void PaUtil_InitializeClock( void )
-{
- /* TODO */
-}
-
-
-PaTime PaUtil_GetTime( void )
-{
- struct timeval tv;
- gettimeofday( &tv, NULL );
- return (PaTime) tv.tv_usec / 1000000. + tv.tv_sec;
-}
-
-PaError PaUtil_InitializeThreading( PaUtilThreading *threading )
-{
- (void) paUtilErr_;
- return paNoError;
-}
-
-void PaUtil_TerminateThreading( PaUtilThreading *threading )
-{
-}
-
-PaError PaUtil_StartThreading( PaUtilThreading *threading, void *(*threadRoutine)(void *), void *data )
-{
- pthread_create( &threading->callbackThread, NULL, threadRoutine, data );
- return paNoError;
-}
-
-PaError PaUtil_CancelThreading( PaUtilThreading *threading, int wait, PaError *exitResult )
-{
- PaError result = paNoError;
- void *pret;
-
- if( exitResult )
- *exitResult = paNoError;
-
- /* Only kill the thread if it isn't in the process of stopping (flushing adaptation buffers) */
- if( !wait )
- pthread_cancel( threading->callbackThread ); /* XXX: Safe to call this if the thread has exited on its own? */
- pthread_join( threading->callbackThread, &pret );
-
-#ifdef PTHREAD_CANCELED
- if( pret && PTHREAD_CANCELED != pret )
-#else
- /* !wait means the thread may have been canceled */
- if( pret && wait )
-#endif
- {
- if( exitResult )
- *exitResult = *(PaError *) pret;
- free( pret );
- }
-
- return result;
-}
-
-/*
-static void *CanaryFunc( void *userData )
-{
- const unsigned intervalMsec = 1000;
- PaUtilThreading *th = (PaUtilThreading *) userData;
-
- while( 1 )
- {
- th->canaryTime = PaUtil_GetTime();
-
- pthread_testcancel();
- Pa_Sleep( intervalMsec );
- }
-
- pthread_exit( NULL );
-}
-*/
+/*
+ * $Id: pa_unix_util.c,v 1.1.2.7 2005/03/31 15:02:48 aknudsen Exp $
+ * Portable Audio I/O Library
+ * UNIX platform-specific support functions
+ *
+ * Based on the Open Source API proposed by Ross Bencina
+ * Copyright (c) 1999-2000 Ross Bencina
+ *
+ * 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.
+ *
+ * Any person wishing to distribute modifications to the Software is
+ * requested to send the modifications to the original developer so that
+ * they can be incorporated into the canonical version.
+ *
+ * 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 <pthread.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <sys/time.h>
+#include <assert.h>
+#include <string.h> /* For memset */
+
+#include "pa_util.h"
+#include "pa_unix_util.h"
+
+/*
+ Track memory allocations to avoid leaks.
+ */
+
+#if PA_TRACK_MEMORY
+static int numAllocations_ = 0;
+#endif
+
+
+void *PaUtil_AllocateMemory( long size )
+{
+ void *result = malloc( size );
+
+#if PA_TRACK_MEMORY
+ if( result != NULL ) numAllocations_ += 1;
+#endif
+ return result;
+}
+
+
+void PaUtil_FreeMemory( void *block )
+{
+ if( block != NULL )
+ {
+ free( block );
+#if PA_TRACK_MEMORY
+ numAllocations_ -= 1;
+#endif
+
+ }
+}
+
+
+int PaUtil_CountCurrentlyAllocatedBlocks( void )
+{
+#if PA_TRACK_MEMORY
+ return numAllocations_;
+#else
+ return 0;
+#endif
+}
+
+
+void Pa_Sleep( long msec )
+{
+ while( msec > 999 ) /* For OpenBSD and IRIX, argument */
+ { /* to usleep must be < 1000000. */
+ usleep( 999000 );
+ msec -= 999;
+ }
+ usleep( msec * 1000 );
+}
+
+/* *** NOT USED YET: ***
+static int usePerformanceCounter_;
+static double microsecondsPerTick_;
+*/
+
+void PaUtil_InitializeClock( void )
+{
+ /* TODO */
+}
+
+
+PaTime PaUtil_GetTime( void )
+{
+ struct timeval tv;
+ gettimeofday( &tv, NULL );
+ return (PaTime) tv.tv_usec / 1000000. + tv.tv_sec;
+}
+
+PaError PaUtil_InitializeThreading( PaUtilThreading *threading )
+{
+ (void) paUtilErr_;
+ return paNoError;
+}
+
+void PaUtil_TerminateThreading( PaUtilThreading *threading )
+{
+}
+
+PaError PaUtil_StartThreading( PaUtilThreading *threading, void *(*threadRoutine)(void *), void *data )
+{
+ pthread_create( &threading->callbackThread, NULL, threadRoutine, data );
+ return paNoError;
+}
+
+PaError PaUtil_CancelThreading( PaUtilThreading *threading, int wait, PaError *exitResult )
+{
+ PaError result = paNoError;
+ void *pret;
+
+ if( exitResult )
+ *exitResult = paNoError;
+
+ /* Only kill the thread if it isn't in the process of stopping (flushing adaptation buffers) */
+ if( !wait )
+ pthread_cancel( threading->callbackThread ); /* XXX: Safe to call this if the thread has exited on its own? */
+ pthread_join( threading->callbackThread, &pret );
+
+#ifdef PTHREAD_CANCELED
+ if( pret && PTHREAD_CANCELED != pret )
+#else
+ /* !wait means the thread may have been canceled */
+ if( pret && wait )
+#endif
+ {
+ if( exitResult )
+ *exitResult = *(PaError *) pret;
+ free( pret );
+ }
+
+ return result;
+}
+
+/*
+static void *CanaryFunc( void *userData )
+{
+ const unsigned intervalMsec = 1000;
+ PaUtilThreading *th = (PaUtilThreading *) userData;
+
+ while( 1 )
+ {
+ th->canaryTime = PaUtil_GetTime();
+
+ pthread_testcancel();
+ Pa_Sleep( intervalMsec );
+ }
+
+ pthread_exit( NULL );
+}
+*/
diff --git a/pjmedia/src/pjmedia/portaudio/pa_unix_util.h b/pjmedia/src/pjmedia/portaudio/pa_unix_util.h
index f27624a0..01dda01e 100644
--- a/pjmedia/src/pjmedia/portaudio/pa_unix_util.h
+++ b/pjmedia/src/pjmedia/portaudio/pa_unix_util.h
@@ -1,73 +1,73 @@
-#ifndef PA_UNIX_UTIL_H
-#define PA_UNIX_UTIL_H
-
-#include "pa_cpuload.h"
-
-#ifdef __cplusplus
-extern "C"
-{
-#endif /* __cplusplus */
-
-#define PA_MIN(x,y) ( (x) < (y) ? (x) : (y) )
-#define PA_MAX(x,y) ( (x) > (y) ? (x) : (y) )
-
-/* Utilize GCC branch prediction for error tests */
-#if defined __GNUC__ && __GNUC__ >= 3
-#define UNLIKELY(expr) __builtin_expect( (expr), 0 )
-#else
-#define UNLIKELY(expr) (expr)
-#endif
-
-#define STRINGIZE_HELPER(expr) #expr
-#define STRINGIZE(expr) STRINGIZE_HELPER(expr)
-
-#define PA_UNLESS(expr, code) \
- do { \
- if( UNLIKELY( (expr) == 0 ) ) \
- { \
- PaUtil_DebugPrint(( "Expression '" #expr "' failed in '" __FILE__ "', line: " STRINGIZE( __LINE__ ) "\n" )); \
- result = (code); \
- goto error; \
- } \
- } while (0);
-
-static PaError paUtilErr_; /* Used with PA_ENSURE */
-
-/* Check PaError */
-#define PA_ENSURE(expr) \
- do { \
- if( UNLIKELY( (paUtilErr_ = (expr)) < paNoError ) ) \
- { \
- PaUtil_DebugPrint(( "Expression '" #expr "' failed in '" __FILE__ "', line: " STRINGIZE( __LINE__ ) "\n" )); \
- result = paUtilErr_; \
- goto error; \
- } \
- } while (0);
-
-typedef struct {
- pthread_t callbackThread;
-} PaUtilThreading;
-
-PaError PaUtil_InitializeThreading( PaUtilThreading *threading );
-void PaUtil_TerminateThreading( PaUtilThreading *threading );
-PaError PaUtil_StartThreading( PaUtilThreading *threading, void *(*threadRoutine)(void *), void *data );
-PaError PaUtil_CancelThreading( PaUtilThreading *threading, int wait, PaError *exitResult );
-
-/* State accessed by utility functions */
-
-/*
-void PaUnix_SetRealtimeScheduling( int rt );
-
-void PaUtil_InitializeThreading( PaUtilThreading *th, PaUtilCpuLoadMeasurer *clm );
-
-PaError PaUtil_CreateCallbackThread( PaUtilThreading *th, void *(*CallbackThreadFunc)( void * ), PaStream *s );
-
-PaError PaUtil_KillCallbackThread( PaUtilThreading *th, PaError *exitResult );
-
-void PaUtil_CallbackUpdate( PaUtilThreading *th );
-*/
-
-#ifdef __cplusplus
-}
-#endif /* __cplusplus */
-#endif
+#ifndef PA_UNIX_UTIL_H
+#define PA_UNIX_UTIL_H
+
+#include "pa_cpuload.h"
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif /* __cplusplus */
+
+#define PA_MIN(x,y) ( (x) < (y) ? (x) : (y) )
+#define PA_MAX(x,y) ( (x) > (y) ? (x) : (y) )
+
+/* Utilize GCC branch prediction for error tests */
+#if defined __GNUC__ && __GNUC__ >= 3
+#define UNLIKELY(expr) __builtin_expect( (expr), 0 )
+#else
+#define UNLIKELY(expr) (expr)
+#endif
+
+#define STRINGIZE_HELPER(expr) #expr
+#define STRINGIZE(expr) STRINGIZE_HELPER(expr)
+
+#define PA_UNLESS(expr, code) \
+ do { \
+ if( UNLIKELY( (expr) == 0 ) ) \
+ { \
+ PaUtil_DebugPrint(( "Expression '" #expr "' failed in '" __FILE__ "', line: " STRINGIZE( __LINE__ ) "\n" )); \
+ result = (code); \
+ goto error; \
+ } \
+ } while (0);
+
+static PaError paUtilErr_; /* Used with PA_ENSURE */
+
+/* Check PaError */
+#define PA_ENSURE(expr) \
+ do { \
+ if( UNLIKELY( (paUtilErr_ = (expr)) < paNoError ) ) \
+ { \
+ PaUtil_DebugPrint(( "Expression '" #expr "' failed in '" __FILE__ "', line: " STRINGIZE( __LINE__ ) "\n" )); \
+ result = paUtilErr_; \
+ goto error; \
+ } \
+ } while (0);
+
+typedef struct {
+ pthread_t callbackThread;
+} PaUtilThreading;
+
+PaError PaUtil_InitializeThreading( PaUtilThreading *threading );
+void PaUtil_TerminateThreading( PaUtilThreading *threading );
+PaError PaUtil_StartThreading( PaUtilThreading *threading, void *(*threadRoutine)(void *), void *data );
+PaError PaUtil_CancelThreading( PaUtilThreading *threading, int wait, PaError *exitResult );
+
+/* State accessed by utility functions */
+
+/*
+void PaUnix_SetRealtimeScheduling( int rt );
+
+void PaUtil_InitializeThreading( PaUtilThreading *th, PaUtilCpuLoadMeasurer *clm );
+
+PaError PaUtil_CreateCallbackThread( PaUtilThreading *th, void *(*CallbackThreadFunc)( void * ), PaStream *s );
+
+PaError PaUtil_KillCallbackThread( PaUtilThreading *th, PaError *exitResult );
+
+void PaUtil_CallbackUpdate( PaUtilThreading *th );
+*/
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+#endif
diff --git a/pjmedia/src/pjmedia/portaudio/pa_util.h b/pjmedia/src/pjmedia/portaudio/pa_util.h
index 87bd5c8d..149fbca3 100644
--- a/pjmedia/src/pjmedia/portaudio/pa_util.h
+++ b/pjmedia/src/pjmedia/portaudio/pa_util.h
@@ -1,167 +1,167 @@
-#ifndef PA_UTIL_H
-#define PA_UTIL_H
-/*
- * $Id: pa_util.h,v 1.1.2.12 2003/09/20 21:09:55 rossbencina Exp $
- * Portable Audio I/O Library implementation utilities header
- * common implementation utilities and interfaces
- *
- * Based on the Open Source API proposed by Ross Bencina
- * Copyright (c) 1999-2002 Ross Bencina, Phil Burk
- *
- * 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.
- *
- * Any person wishing to distribute modifications to the Software is
- * requested to send the modifications to the original developer so that
- * they can be incorporated into the canonical version.
- *
- * 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.
- */
-
-/** @file
- @brief Prototypes for utility functions used by PortAudio implementations.
-
- @todo Document and adhere to the alignment guarantees provided by
- PaUtil_AllocateMemory().
-*/
-
-
-#include "portaudio.h"
-
-#ifdef __cplusplus
-extern "C"
-{
-#endif /* __cplusplus */
-
-
-struct PaUtilHostApiRepresentation;
-
-
-/** Retrieve a specific host API representation. This function can be used
- by implementations to retrieve a pointer to their representation in
- host api specific extension functions which aren't passed a rep pointer
- by pa_front.c.
-
- @param hostApi A pointer to a host API represenation pointer. Apon success
- this will receive the requested representation pointer.
-
- @param type A valid host API type identifier.
-
- @returns An error code. If the result is PaNoError then a pointer to the
- requested host API representation will be stored in *hostApi. If the host API
- specified by type is not found, this function returns paHostApiNotFound.
-*/
-PaError PaUtil_GetHostApiRepresentation( struct PaUtilHostApiRepresentation **hostApi,
- PaHostApiTypeId type );
-
-
-/** Convert a PortAudio device index into a host API specific device index.
- @param hostApiDevice Pointer to a device index, on success this will recieve the
- converted device index value.
- @param device The PortAudio device index to convert.
- @param hostApi The host api which the index should be converted for.
-
- @returns On success returns PaNoError and places the converted index in the
- hostApiDevice parameter.
-*/
-PaError PaUtil_DeviceIndexToHostApiDeviceIndex(
- PaDeviceIndex *hostApiDevice, PaDeviceIndex device,
- struct PaUtilHostApiRepresentation *hostApi );
-
-
-/** Set the host error information returned by Pa_GetLastHostErrorInfo. This
- function and the paUnanticipatedHostError error code should be used as a
- last resort. Implementors should use existing PA error codes where possible,
- or nominate new ones. Note that at it is always better to use
- PaUtil_SetLastHostErrorInfo() and paUnanticipatedHostError than to return an
- ambiguous or inaccurate PaError code.
-
- @param hostApiType The host API which encountered the error (ie of the caller)
-
- @param errorCode The error code returned by the native API function.
-
- @param errorText A string describing the error. PaUtil_SetLastHostErrorInfo
- makes a copy of the string, so it is not necessary for the pointer to remain
- valid after the call to PaUtil_SetLastHostErrorInfo() returns.
-
-*/
-void PaUtil_SetLastHostErrorInfo( PaHostApiTypeId hostApiType, long errorCode,
- const char *errorText );
-
-
-
-/** PA_DEBUG() provides a simple debug message printing facility. The macro
- passes it's argument to a printf-like function called PaUtil_DebugPrint()
- which prints to stderr and always flushes the stream after printing.
- Because preprocessor macros cannot directly accept variable length argument
- lists, calls to the macro must include an additional set of parenthesis, eg:
- PA_DEBUG(("errorno: %d", 1001 ));
-*/
-
-void PaUtil_DebugPrint( const char *format, ... );
-
-#if (0) /* set to 1 to print debug messages */
-#define PA_DEBUG(x) PaUtil_DebugPrint x ;
-#else
-#define PA_DEBUG(x)
-#endif
-
-
-/* the following functions are implemented in a platform platform specific
- .c file
-*/
-
-/** Allocate size bytes, guaranteed to be aligned to a FIXME byte boundary */
-void *PaUtil_AllocateMemory( long size );
-
-
-/** Realease block if non-NULL. block may be NULL */
-void PaUtil_FreeMemory( void *block );
-
-
-/** Return the number of currently allocated blocks. This function can be
- used for detecting memory leaks.
-
- @note Allocations will only be tracked if PA_TRACK_MEMORY is #defined. If
- it isn't, this function will always return 0.
-*/
-int PaUtil_CountCurrentlyAllocatedBlocks( void );
-
-
-/** Initialize the clock used by PaUtil_GetTime(). Call this before calling
- PaUtil_GetTime.
-
- @see PaUtil_GetTime
-*/
-void PaUtil_InitializeClock( void );
-
-
-/** Return the system time in seconds. Used to implement CPU load functions
-
- @see PaUtil_InitializeClock
-*/
-double PaUtil_GetTime( void );
-
-
-/* void Pa_Sleep( long msec ); must also be implemented in per-platform .c file */
-
-
-
-#ifdef __cplusplus
-}
-#endif /* __cplusplus */
-#endif /* PA_UTIL_H */
+#ifndef PA_UTIL_H
+#define PA_UTIL_H
+/*
+ * $Id: pa_util.h,v 1.1.2.12 2003/09/20 21:09:55 rossbencina Exp $
+ * Portable Audio I/O Library implementation utilities header
+ * common implementation utilities and interfaces
+ *
+ * Based on the Open Source API proposed by Ross Bencina
+ * Copyright (c) 1999-2002 Ross Bencina, Phil Burk
+ *
+ * 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.
+ *
+ * Any person wishing to distribute modifications to the Software is
+ * requested to send the modifications to the original developer so that
+ * they can be incorporated into the canonical version.
+ *
+ * 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.
+ */
+
+/** @file
+ @brief Prototypes for utility functions used by PortAudio implementations.
+
+ @todo Document and adhere to the alignment guarantees provided by
+ PaUtil_AllocateMemory().
+*/
+
+
+#include "portaudio.h"
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif /* __cplusplus */
+
+
+struct PaUtilHostApiRepresentation;
+
+
+/** Retrieve a specific host API representation. This function can be used
+ by implementations to retrieve a pointer to their representation in
+ host api specific extension functions which aren't passed a rep pointer
+ by pa_front.c.
+
+ @param hostApi A pointer to a host API represenation pointer. Apon success
+ this will receive the requested representation pointer.
+
+ @param type A valid host API type identifier.
+
+ @returns An error code. If the result is PaNoError then a pointer to the
+ requested host API representation will be stored in *hostApi. If the host API
+ specified by type is not found, this function returns paHostApiNotFound.
+*/
+PaError PaUtil_GetHostApiRepresentation( struct PaUtilHostApiRepresentation **hostApi,
+ PaHostApiTypeId type );
+
+
+/** Convert a PortAudio device index into a host API specific device index.
+ @param hostApiDevice Pointer to a device index, on success this will recieve the
+ converted device index value.
+ @param device The PortAudio device index to convert.
+ @param hostApi The host api which the index should be converted for.
+
+ @returns On success returns PaNoError and places the converted index in the
+ hostApiDevice parameter.
+*/
+PaError PaUtil_DeviceIndexToHostApiDeviceIndex(
+ PaDeviceIndex *hostApiDevice, PaDeviceIndex device,
+ struct PaUtilHostApiRepresentation *hostApi );
+
+
+/** Set the host error information returned by Pa_GetLastHostErrorInfo. This
+ function and the paUnanticipatedHostError error code should be used as a
+ last resort. Implementors should use existing PA error codes where possible,
+ or nominate new ones. Note that at it is always better to use
+ PaUtil_SetLastHostErrorInfo() and paUnanticipatedHostError than to return an
+ ambiguous or inaccurate PaError code.
+
+ @param hostApiType The host API which encountered the error (ie of the caller)
+
+ @param errorCode The error code returned by the native API function.
+
+ @param errorText A string describing the error. PaUtil_SetLastHostErrorInfo
+ makes a copy of the string, so it is not necessary for the pointer to remain
+ valid after the call to PaUtil_SetLastHostErrorInfo() returns.
+
+*/
+void PaUtil_SetLastHostErrorInfo( PaHostApiTypeId hostApiType, long errorCode,
+ const char *errorText );
+
+
+
+/** PA_DEBUG() provides a simple debug message printing facility. The macro
+ passes it's argument to a printf-like function called PaUtil_DebugPrint()
+ which prints to stderr and always flushes the stream after printing.
+ Because preprocessor macros cannot directly accept variable length argument
+ lists, calls to the macro must include an additional set of parenthesis, eg:
+ PA_DEBUG(("errorno: %d", 1001 ));
+*/
+
+void PaUtil_DebugPrint( const char *format, ... );
+
+#if (0) /* set to 1 to print debug messages */
+#define PA_DEBUG(x) PaUtil_DebugPrint x ;
+#else
+#define PA_DEBUG(x)
+#endif
+
+
+/* the following functions are implemented in a platform platform specific
+ .c file
+*/
+
+/** Allocate size bytes, guaranteed to be aligned to a FIXME byte boundary */
+void *PaUtil_AllocateMemory( long size );
+
+
+/** Realease block if non-NULL. block may be NULL */
+void PaUtil_FreeMemory( void *block );
+
+
+/** Return the number of currently allocated blocks. This function can be
+ used for detecting memory leaks.
+
+ @note Allocations will only be tracked if PA_TRACK_MEMORY is #defined. If
+ it isn't, this function will always return 0.
+*/
+int PaUtil_CountCurrentlyAllocatedBlocks( void );
+
+
+/** Initialize the clock used by PaUtil_GetTime(). Call this before calling
+ PaUtil_GetTime.
+
+ @see PaUtil_GetTime
+*/
+void PaUtil_InitializeClock( void );
+
+
+/** Return the system time in seconds. Used to implement CPU load functions
+
+ @see PaUtil_InitializeClock
+*/
+double PaUtil_GetTime( void );
+
+
+/* void Pa_Sleep( long msec ); must also be implemented in per-platform .c file */
+
+
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+#endif /* PA_UTIL_H */
diff --git a/pjmedia/src/pjmedia/portaudio/pa_win_ds.c b/pjmedia/src/pjmedia/portaudio/pa_win_ds.c
index 128a8a17..940867da 100644
--- a/pjmedia/src/pjmedia/portaudio/pa_win_ds.c
+++ b/pjmedia/src/pjmedia/portaudio/pa_win_ds.c
@@ -1,1828 +1,1828 @@
-/*
- * $Id: pa_win_ds.c,v 1.1.2.49 2004/05/16 04:08:55 rossbencina Exp $
- * Portable Audio I/O Library DirectSound implementation
- *
- * Based on the Open Source API proposed by Ross Bencina
- * Copyright (c) 1999-2002 Ross Bencina, Phil Burk
- *
- * 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.
- *
- * Any person wishing to distribute modifications to the Software is
- * requested to send the modifications to the original developer so that
- * they can be incorporated into the canonical version.
- *
- * 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.
- */
-
-/** @file
-
- @todo implement paInputOverflow callback status flag
-
- @todo implement paNeverDropInput.
-
- @todo implement host api specific extension to set i/o buffer sizes in frames
-
- @todo implement initialisation of PaDeviceInfo default*Latency fields (currently set to 0.)
-
- @todo implement ReadStream, WriteStream, GetStreamReadAvailable, GetStreamWriteAvailable
-
- @todo audit handling of DirectSound result codes - in many cases we could convert a HRESULT into
- a native portaudio error code. Standard DirectSound result codes are documented at msdn.
-
- @todo implement IsFormatSupported
-
- @todo check that CoInitialize() CoUninitialize() are always correctly
- paired, even in error cases.
-
- @todo call PaUtil_SetLastHostErrorInfo with a specific error string (currently just "DSound error").
-
- @todo make sure all buffers have been played before stopping the stream
- when the stream callback returns paComplete
-
- old TODOs from phil, need to work out if these have been done:
- O- fix "patest_stop.c"
-*/
-
-#include <stdio.h>
-#include <string.h> /* strlen() */
-
-#include "pa_util.h"
-#include "pa_allocation.h"
-#include "pa_hostapi.h"
-#include "pa_stream.h"
-#include "pa_cpuload.h"
-#include "pa_process.h"
-
-#include "dsound_wrapper.h"
-
-#if (defined(WIN32) && (defined(_MSC_VER) && (_MSC_VER >= 1200))) /* MSC version 6 and above */
-#pragma comment( lib, "dsound.lib" )
-#pragma comment( lib, "winmm.lib" )
-#endif
-
-
-#define PRINT(x) /* { printf x; fflush(stdout); } */
-#define ERR_RPT(x) PRINT(x)
-#define DBUG(x) /* PRINT(x) */
-#define DBUGX(x) /* PRINT(x) */
-
-#define PA_USE_HIGH_LATENCY (0)
-#if PA_USE_HIGH_LATENCY
-#define PA_WIN_9X_LATENCY (500)
-#define PA_WIN_NT_LATENCY (600)
-#else
-#define PA_WIN_9X_LATENCY (140)
-#define PA_WIN_NT_LATENCY (280)
-#endif
-
-#define PA_WIN_WDM_LATENCY (120)
-
-#define SECONDS_PER_MSEC (0.001)
-#define MSEC_PER_SECOND (1000)
-
-/* prototypes for functions declared in this file */
-
-#ifdef __cplusplus
-extern "C"
-{
-#endif /* __cplusplus */
-
-PaError PaWinDs_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiIndex index );
-
-#ifdef __cplusplus
-}
-#endif /* __cplusplus */
-
-static void Terminate( struct PaUtilHostApiRepresentation *hostApi );
-static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi,
- PaStream** s,
- const PaStreamParameters *inputParameters,
- const PaStreamParameters *outputParameters,
- double sampleRate,
- unsigned long framesPerBuffer,
- PaStreamFlags streamFlags,
- PaStreamCallback *streamCallback,
- void *userData );
-static PaError IsFormatSupported( struct PaUtilHostApiRepresentation *hostApi,
- const PaStreamParameters *inputParameters,
- const PaStreamParameters *outputParameters,
- double sampleRate );
-static PaError CloseStream( PaStream* stream );
-static PaError StartStream( PaStream *stream );
-static PaError StopStream( PaStream *stream );
-static PaError AbortStream( PaStream *stream );
-static PaError IsStreamStopped( PaStream *s );
-static PaError IsStreamActive( PaStream *stream );
-static PaTime GetStreamTime( PaStream *stream );
-static double GetStreamCpuLoad( PaStream* stream );
-static PaError ReadStream( PaStream* stream, void *buffer, unsigned long frames );
-static PaError WriteStream( PaStream* stream, const void *buffer, unsigned long frames );
-static signed long GetStreamReadAvailable( PaStream* stream );
-static signed long GetStreamWriteAvailable( PaStream* stream );
-
-
-/* FIXME: should convert hr to a string */
-#define PA_DS_SET_LAST_DIRECTSOUND_ERROR( hr ) \
- PaUtil_SetLastHostErrorInfo( paDirectSound, hr, "DirectSound error" )
-
-/************************************************* DX Prototypes **********/
-static BOOL CALLBACK CollectGUIDsProc(LPGUID lpGUID,
- LPCTSTR lpszDesc,
- LPCTSTR lpszDrvName,
- LPVOID lpContext );
-
-/************************************************************************************/
-/********************** Structures **************************************************/
-/************************************************************************************/
-/* PaWinDsHostApiRepresentation - host api datastructure specific to this implementation */
-
-typedef struct PaWinDsDeviceInfo
-{
- GUID guid;
- GUID *lpGUID;
- double sampleRates[3];
-} PaWinDsDeviceInfo;
-
-typedef struct
-{
- PaUtilHostApiRepresentation inheritedHostApiRep;
- PaUtilStreamInterface callbackStreamInterface;
- PaUtilStreamInterface blockingStreamInterface;
-
- PaUtilAllocationGroup *allocations;
-
- /* implementation specific data goes here */
- PaWinDsDeviceInfo *winDsDeviceInfos;
-
-} PaWinDsHostApiRepresentation;
-
-/* PaWinDsStream - a stream data structure specifically for this implementation */
-
-typedef struct PaWinDsStream
-{
- PaUtilStreamRepresentation streamRepresentation;
- PaUtilCpuLoadMeasurer cpuLoadMeasurer;
- PaUtilBufferProcessor bufferProcessor;
-
-/* DirectSound specific data. */
- DSoundWrapper directSoundWrapper;
- MMRESULT timerID;
- BOOL ifInsideCallback; /* Test for reentrancy. */
- int framesPerDSBuffer;
- double framesWritten;
- double secondsPerHostByte; /* Used to optimize latency calculation for outTime */
-
- PaStreamCallbackFlags callbackFlags;
-
-/* FIXME - move all below to PaUtilStreamRepresentation */
- volatile int isStarted;
- volatile int isActive;
- volatile int stopProcessing; /* stop thread once existing buffers have been returned */
- volatile int abortProcessing; /* stop thread immediately */
-} PaWinDsStream;
-
-
-/************************************************************************************
-** Duplicate the input string using the allocations allocator.
-** A NULL string is converted to a zero length string.
-** If memory cannot be allocated, NULL is returned.
-**/
-static char *DuplicateDeviceNameString( PaUtilAllocationGroup *allocations, const char* src )
-{
- char *result = 0;
-
- if( src != NULL )
- {
- size_t len = strlen(src);
- result = (char*)PaUtil_GroupAllocateMemory( allocations, (long)(len + 1) );
- if( result )
- memcpy( (void *) result, src, len+1 );
- }
- else
- {
- result = (char*)PaUtil_GroupAllocateMemory( allocations, 1 );
- if( result )
- result[0] = '\0';
- }
-
- return result;
-}
-
-/************************************************************************************
-** DSDeviceNameAndGUID, DSDeviceNameAndGUIDVector used for collecting preliminary
-** information during device enumeration.
-*/
-typedef struct DSDeviceNameAndGUID{
- char *name; // allocated from parent's allocations, never deleted by this structure
- GUID guid;
- LPGUID lpGUID;
-} DSDeviceNameAndGUID;
-
-typedef struct DSDeviceNameAndGUIDVector{
- PaUtilAllocationGroup *allocations;
- PaError enumerationError;
-
- int count;
- int free;
- DSDeviceNameAndGUID *items; // Allocated using LocalAlloc()
-} DSDeviceNameAndGUIDVector;
-
-static PaError InitializeDSDeviceNameAndGUIDVector(
- DSDeviceNameAndGUIDVector *guidVector, PaUtilAllocationGroup *allocations )
-{
- PaError result = paNoError;
-
- guidVector->allocations = allocations;
- guidVector->enumerationError = paNoError;
-
- guidVector->count = 0;
- guidVector->free = 8;
- guidVector->items = (DSDeviceNameAndGUID*)LocalAlloc( LMEM_FIXED, sizeof(DSDeviceNameAndGUID) * guidVector->free );
- if( guidVector->items == NULL )
- result = paInsufficientMemory;
-
- return result;
-}
-
-static PaError ExpandDSDeviceNameAndGUIDVector( DSDeviceNameAndGUIDVector *guidVector )
-{
- PaError result = paNoError;
- DSDeviceNameAndGUID *newItems;
- int i;
-
- /* double size of vector */
- int size = guidVector->count + guidVector->free;
- guidVector->free += size;
-
- newItems = (DSDeviceNameAndGUID*)LocalAlloc( LMEM_FIXED, sizeof(DSDeviceNameAndGUID) * size * 2 );
- if( newItems == NULL )
- {
- result = paInsufficientMemory;
- }
- else
- {
- for( i=0; i < guidVector->count; ++i )
- {
- newItems[i].name = guidVector->items[i].name;
- if( guidVector->items[i].lpGUID == NULL )
- {
- newItems[i].lpGUID = NULL;
- }
- else
- {
- newItems[i].lpGUID = &newItems[i].guid;
- memcpy( &newItems[i].guid, guidVector->items[i].lpGUID, sizeof(GUID) );;
- }
- }
-
- LocalFree( guidVector->items );
- guidVector->items = newItems;
- }
-
- return result;
-}
-
-/*
- it's safe to call DSDeviceNameAndGUIDVector multiple times
-*/
-static PaError TerminateDSDeviceNameAndGUIDVector( DSDeviceNameAndGUIDVector *guidVector )
-{
- PaError result = paNoError;
-
- if( guidVector->items != NULL )
- {
- if( LocalFree( guidVector->items ) != NULL )
- result = paInsufficientMemory; /** @todo this isn't the correct error to return from a deallocation failure */
-
- guidVector->items = NULL;
- }
-
- return result;
-}
-
-/************************************************************************************
-** Collect preliminary device information during DirectSound enumeration
-*/
-static BOOL CALLBACK CollectGUIDsProc(LPGUID lpGUID,
- LPCTSTR lpszDesc,
- LPCTSTR lpszDrvName,
- LPVOID lpContext )
-{
- DSDeviceNameAndGUIDVector *namesAndGUIDs = (DSDeviceNameAndGUIDVector*)lpContext;
- PaError error;
-
- (void) lpszDrvName; /* unused variable */
-
- if( namesAndGUIDs->free == 0 )
- {
- error = ExpandDSDeviceNameAndGUIDVector( namesAndGUIDs );
- if( error != paNoError )
- {
- namesAndGUIDs->enumerationError = error;
- return FALSE;
- }
- }
-
- /* Set GUID pointer, copy GUID to storage in DSDeviceNameAndGUIDVector. */
- if( lpGUID == NULL )
- {
- namesAndGUIDs->items[namesAndGUIDs->count].lpGUID = NULL;
- }
- else
- {
- namesAndGUIDs->items[namesAndGUIDs->count].lpGUID =
- &namesAndGUIDs->items[namesAndGUIDs->count].guid;
-
- memcpy( &namesAndGUIDs->items[namesAndGUIDs->count].guid, lpGUID, sizeof(GUID) );
- }
-
- namesAndGUIDs->items[namesAndGUIDs->count].name =
- DuplicateDeviceNameString( namesAndGUIDs->allocations, lpszDesc );
- if( namesAndGUIDs->items[namesAndGUIDs->count].name == NULL )
- {
- namesAndGUIDs->enumerationError = paInsufficientMemory;
- return FALSE;
- }
-
- ++namesAndGUIDs->count;
- --namesAndGUIDs->free;
-
- return TRUE;
-}
-
-
-#define PA_DEFAULTSAMPLERATESEARCHORDER_COUNT_ (13) /* must match array length below */
-static double defaultSampleRateSearchOrder_[] =
- { 44100.0, 48000.0, 32000.0, 24000.0, 22050.0, 88200.0, 96000.0, 192000.0,
- 16000.0, 12000.0, 11025.0, 9600.0, 8000.0 };
-
-
-/************************************************************************************
-** Extract capabilities from an output device, and add it to the device info list
-** if successful. This function assumes that there is enough room in the
-** device info list to accomodate all entries.
-**
-** The device will not be added to the device list if any errors are encountered.
-*/
-static PaError AddOutputDeviceInfoFromDirectSound(
- PaWinDsHostApiRepresentation *winDsHostApi, char *name, LPGUID lpGUID )
-{
- PaUtilHostApiRepresentation *hostApi = &winDsHostApi->inheritedHostApiRep;
- PaDeviceInfo *deviceInfo = hostApi->deviceInfos[hostApi->info.deviceCount];
- PaWinDsDeviceInfo *winDsDeviceInfo = &winDsHostApi->winDsDeviceInfos[hostApi->info.deviceCount];
- HRESULT hr;
- LPDIRECTSOUND lpDirectSound;
- DSCAPS caps;
- int deviceOK = TRUE;
- PaError result = paNoError;
- int i;
-
- /* Copy GUID to the device info structure. Set pointer. */
- if( lpGUID == NULL )
- {
- winDsDeviceInfo->lpGUID = NULL;
- }
- else
- {
- memcpy( &winDsDeviceInfo->guid, lpGUID, sizeof(GUID) );
- winDsDeviceInfo->lpGUID = &winDsDeviceInfo->guid;
- }
-
-
- /* Create a DirectSound object for the specified GUID
- Note that using CoCreateInstance doesn't work on windows CE.
- */
- hr = dswDSoundEntryPoints.DirectSoundCreate( lpGUID, &lpDirectSound, NULL );
-
- /** try using CoCreateInstance because DirectSoundCreate was hanging under
- some circumstances - note this was probably related to the
- #define BOOL short bug which has now been fixed
- @todo delete this comment and the following code once we've ensured
- there is no bug.
- */
- /*
- hr = CoCreateInstance( &CLSID_DirectSound, NULL, CLSCTX_INPROC_SERVER,
- &IID_IDirectSound, (void**)&lpDirectSound );
-
- if( hr == S_OK )
- {
- hr = IDirectSound_Initialize( lpDirectSound, lpGUID );
- }
- */
-
- if( hr != DS_OK )
- {
- DBUG(("Cannot create DirectSound for %s. Result = 0x%x\n", name, hr ));
- deviceOK = FALSE;
- }
- else
- {
- /* Query device characteristics. */
- memset( &caps, 0, sizeof(caps) );
- caps.dwSize = sizeof(caps);
- hr = IDirectSound_GetCaps( lpDirectSound, &caps );
- if( hr != DS_OK )
- {
- DBUG(("Cannot GetCaps() for DirectSound device %s. Result = 0x%x\n", name, hr ));
- deviceOK = FALSE;
- }
- else
- {
-
-#ifndef PA_NO_WMME
- if( caps.dwFlags & DSCAPS_EMULDRIVER )
- {
- /* If WMME supported, then reject Emulated drivers because they are lousy. */
- deviceOK = FALSE;
- }
-#endif
-
- if( deviceOK )
- {
- deviceInfo->maxInputChannels = 0;
- /* Mono or stereo device? */
- deviceInfo->maxOutputChannels = ( caps.dwFlags & DSCAPS_PRIMARYSTEREO ) ? 2 : 1;
-
- deviceInfo->defaultLowInputLatency = 0.; /** @todo IMPLEMENT ME */
- deviceInfo->defaultLowOutputLatency = 0.; /** @todo IMPLEMENT ME */
- deviceInfo->defaultHighInputLatency = 0.; /** @todo IMPLEMENT ME */
- deviceInfo->defaultHighOutputLatency = 0.; /** @todo IMPLEMENT ME */
-
- /* initialize defaultSampleRate */
-
- if( caps.dwFlags & DSCAPS_CONTINUOUSRATE )
- {
- /* initialize to caps.dwMaxSecondarySampleRate incase none of the standard rates match */
- deviceInfo->defaultSampleRate = caps.dwMaxSecondarySampleRate;
-
- for( i = 0; i < PA_DEFAULTSAMPLERATESEARCHORDER_COUNT_; ++i )
- {
- if( defaultSampleRateSearchOrder_[i] >= caps.dwMinSecondarySampleRate
- && defaultSampleRateSearchOrder_[i] <= caps.dwMaxSecondarySampleRate ){
-
- deviceInfo->defaultSampleRate = defaultSampleRateSearchOrder_[i];
- break;
- }
- }
- }
- else if( caps.dwMinSecondarySampleRate == caps.dwMaxSecondarySampleRate )
- {
- if( caps.dwMinSecondarySampleRate == 0 )
- {
- /*
- ** On my Thinkpad 380Z, DirectSoundV6 returns min-max=0 !!
- ** But it supports continuous sampling.
- ** So fake range of rates, and hope it really supports it.
- */
- deviceInfo->defaultSampleRate = 44100.0f;
-
- DBUG(("PA - Reported rates both zero. Setting to fake values for device #%d\n", sDeviceIndex ));
- }
- else
- {
- deviceInfo->defaultSampleRate = caps.dwMaxSecondarySampleRate;
- }
- }
- else if( (caps.dwMinSecondarySampleRate < 1000.0) && (caps.dwMaxSecondarySampleRate > 50000.0) )
- {
- /* The EWS88MT drivers lie, lie, lie. The say they only support two rates, 100 & 100000.
- ** But we know that they really support a range of rates!
- ** So when we see a ridiculous set of rates, assume it is a range.
- */
- deviceInfo->defaultSampleRate = 44100.0f;
- DBUG(("PA - Sample rate range used instead of two odd values for device #%d\n", sDeviceIndex ));
- }
- else deviceInfo->defaultSampleRate = caps.dwMaxSecondarySampleRate;
-
-
- //printf( "min %d max %d\n", caps.dwMinSecondarySampleRate, caps.dwMaxSecondarySampleRate );
- // dwFlags | DSCAPS_CONTINUOUSRATE
- }
- }
-
- IDirectSound_Release( lpDirectSound );
- }
-
- if( deviceOK )
- {
- deviceInfo->name = name;
-
- if( lpGUID == NULL )
- hostApi->info.defaultOutputDevice = hostApi->info.deviceCount;
-
- hostApi->info.deviceCount++;
- }
-
- return result;
-}
-
-
-/************************************************************************************
-** Extract capabilities from an input device, and add it to the device info list
-** if successful. This function assumes that there is enough room in the
-** device info list to accomodate all entries.
-**
-** The device will not be added to the device list if any errors are encountered.
-*/
-static PaError AddInputDeviceInfoFromDirectSoundCapture(
- PaWinDsHostApiRepresentation *winDsHostApi, char *name, LPGUID lpGUID )
-{
- PaUtilHostApiRepresentation *hostApi = &winDsHostApi->inheritedHostApiRep;
- PaDeviceInfo *deviceInfo = hostApi->deviceInfos[hostApi->info.deviceCount];
- PaWinDsDeviceInfo *winDsDeviceInfo = &winDsHostApi->winDsDeviceInfos[hostApi->info.deviceCount];
- HRESULT hr;
- LPDIRECTSOUNDCAPTURE lpDirectSoundCapture;
- DSCCAPS caps;
- int deviceOK = TRUE;
- PaError result = paNoError;
-
- /* Copy GUID to the device info structure. Set pointer. */
- if( lpGUID == NULL )
- {
- winDsDeviceInfo->lpGUID = NULL;
- }
- else
- {
- winDsDeviceInfo->lpGUID = &winDsDeviceInfo->guid;
- memcpy( &winDsDeviceInfo->guid, lpGUID, sizeof(GUID) );
- }
-
-
- hr = dswDSoundEntryPoints.DirectSoundCaptureCreate( lpGUID, &lpDirectSoundCapture, NULL );
-
- /** try using CoCreateInstance because DirectSoundCreate was hanging under
- some circumstances - note this was probably related to the
- #define BOOL short bug which has now been fixed
- @todo delete this comment and the following code once we've ensured
- there is no bug.
- */
- /*
- hr = CoCreateInstance( &CLSID_DirectSoundCapture, NULL, CLSCTX_INPROC_SERVER,
- &IID_IDirectSoundCapture, (void**)&lpDirectSoundCapture );
- */
- if( hr != DS_OK )
- {
- DBUG(("Cannot create Capture for %s. Result = 0x%x\n", name, hr ));
- deviceOK = FALSE;
- }
- else
- {
- /* Query device characteristics. */
- memset( &caps, 0, sizeof(caps) );
- caps.dwSize = sizeof(caps);
- hr = IDirectSoundCapture_GetCaps( lpDirectSoundCapture, &caps );
- if( hr != DS_OK )
- {
- DBUG(("Cannot GetCaps() for Capture device %s. Result = 0x%x\n", name, hr ));
- deviceOK = FALSE;
- }
- else
- {
-#ifndef PA_NO_WMME
- if( caps.dwFlags & DSCAPS_EMULDRIVER )
- {
- /* If WMME supported, then reject Emulated drivers because they are lousy. */
- deviceOK = FALSE;
- }
-#endif
-
- if( deviceOK )
- {
- deviceInfo->maxInputChannels = caps.dwChannels;
- deviceInfo->maxOutputChannels = 0;
-
- deviceInfo->defaultLowInputLatency = 0.; /** @todo IMPLEMENT ME */
- deviceInfo->defaultLowOutputLatency = 0.; /** @todo IMPLEMENT ME */
- deviceInfo->defaultHighInputLatency = 0.; /** @todo IMPLEMENT ME */
- deviceInfo->defaultHighOutputLatency = 0.; /** @todo IMPLEMENT ME */
-
-/* constants from a WINE patch by Francois Gouget, see:
- http://www.winehq.com/hypermail/wine-patches/2003/01/0290.html
-
- ---
- Date: Fri, 14 May 2004 10:38:12 +0200 (CEST)
- From: Francois Gouget <fgouget@ ... .fr>
- To: Ross Bencina <rbencina@ ... .au>
- Subject: Re: Permission to use wine 48/96 wave patch in BSD licensed library
-
- [snip]
-
- I give you permission to use the patch below under the BSD license.
- http://www.winehq.com/hypermail/wine-patches/2003/01/0290.html
-
- [snip]
-*/
-#ifndef WAVE_FORMAT_48M08
-#define WAVE_FORMAT_48M08 0x00001000 /* 48 kHz, Mono, 8-bit */
-#define WAVE_FORMAT_48S08 0x00002000 /* 48 kHz, Stereo, 8-bit */
-#define WAVE_FORMAT_48M16 0x00004000 /* 48 kHz, Mono, 16-bit */
-#define WAVE_FORMAT_48S16 0x00008000 /* 48 kHz, Stereo, 16-bit */
-#define WAVE_FORMAT_96M08 0x00010000 /* 96 kHz, Mono, 8-bit */
-#define WAVE_FORMAT_96S08 0x00020000 /* 96 kHz, Stereo, 8-bit */
-#define WAVE_FORMAT_96M16 0x00040000 /* 96 kHz, Mono, 16-bit */
-#define WAVE_FORMAT_96S16 0x00080000 /* 96 kHz, Stereo, 16-bit */
-#endif
-
- /* defaultSampleRate */
- if( caps.dwChannels == 2 )
- {
- if( caps.dwFormats & WAVE_FORMAT_4S16 )
- deviceInfo->defaultSampleRate = 44100.0;
- else if( caps.dwFormats & WAVE_FORMAT_48S16 )
- deviceInfo->defaultSampleRate = 48000.0;
- else if( caps.dwFormats & WAVE_FORMAT_2S16 )
- deviceInfo->defaultSampleRate = 22050.0;
- else if( caps.dwFormats & WAVE_FORMAT_1S16 )
- deviceInfo->defaultSampleRate = 11025.0;
- else if( caps.dwFormats & WAVE_FORMAT_96S16 )
- deviceInfo->defaultSampleRate = 96000.0;
- else
- deviceInfo->defaultSampleRate = 0.;
- }
- else if( caps.dwChannels == 1 )
- {
- if( caps.dwFormats & WAVE_FORMAT_4M16 )
- deviceInfo->defaultSampleRate = 44100.0;
- else if( caps.dwFormats & WAVE_FORMAT_48M16 )
- deviceInfo->defaultSampleRate = 48000.0;
- else if( caps.dwFormats & WAVE_FORMAT_2M16 )
- deviceInfo->defaultSampleRate = 22050.0;
- else if( caps.dwFormats & WAVE_FORMAT_1M16 )
- deviceInfo->defaultSampleRate = 11025.0;
- else if( caps.dwFormats & WAVE_FORMAT_96M16 )
- deviceInfo->defaultSampleRate = 96000.0;
- else
- deviceInfo->defaultSampleRate = 0.;
- }
- else deviceInfo->defaultSampleRate = 0.;
- }
- }
-
- IDirectSoundCapture_Release( lpDirectSoundCapture );
- }
-
- if( deviceOK )
- {
- deviceInfo->name = name;
-
- if( lpGUID == NULL )
- hostApi->info.defaultInputDevice = hostApi->info.deviceCount;
-
- hostApi->info.deviceCount++;
- }
-
- return result;
-}
-
-
-/***********************************************************************************/
-PaError PaWinDs_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiIndex hostApiIndex )
-{
- PaError result = paNoError;
- int i, deviceCount;
- PaWinDsHostApiRepresentation *winDsHostApi;
- DSDeviceNameAndGUIDVector inputNamesAndGUIDs, outputNamesAndGUIDs;
- PaDeviceInfo *deviceInfoArray;
-
- HRESULT hr = CoInitialize(NULL); /** @todo: should uninitialize too */
- if( FAILED(hr) ){
- return paUnanticipatedHostError;
- }
-
- /* initialise guid vectors so they can be safely deleted on error */
- inputNamesAndGUIDs.items = NULL;
- outputNamesAndGUIDs.items = NULL;
-
- DSW_InitializeDSoundEntryPoints();
-
- winDsHostApi = (PaWinDsHostApiRepresentation*)PaUtil_AllocateMemory( sizeof(PaWinDsHostApiRepresentation) );
- if( !winDsHostApi )
- {
- result = paInsufficientMemory;
- goto error;
- }
-
- winDsHostApi->allocations = PaUtil_CreateAllocationGroup();
- if( !winDsHostApi->allocations )
- {
- result = paInsufficientMemory;
- goto error;
- }
-
- *hostApi = &winDsHostApi->inheritedHostApiRep;
- (*hostApi)->info.structVersion = 1;
- (*hostApi)->info.type = paDirectSound;
- (*hostApi)->info.name = "Windows DirectSound";
-
- (*hostApi)->info.deviceCount = 0;
- (*hostApi)->info.defaultInputDevice = paNoDevice;
- (*hostApi)->info.defaultOutputDevice = paNoDevice;
-
-
-/* DSound - enumerate devices to count them and to gather their GUIDs */
-
-
- result = InitializeDSDeviceNameAndGUIDVector( &inputNamesAndGUIDs, winDsHostApi->allocations );
- if( result != paNoError )
- goto error;
-
- result = InitializeDSDeviceNameAndGUIDVector( &outputNamesAndGUIDs, winDsHostApi->allocations );
- if( result != paNoError )
- goto error;
-
- dswDSoundEntryPoints.DirectSoundCaptureEnumerate( (LPDSENUMCALLBACK)CollectGUIDsProc, (void *)&inputNamesAndGUIDs );
-
- dswDSoundEntryPoints.DirectSoundEnumerate( (LPDSENUMCALLBACK)CollectGUIDsProc, (void *)&outputNamesAndGUIDs );
-
- if( inputNamesAndGUIDs.enumerationError != paNoError )
- {
- result = inputNamesAndGUIDs.enumerationError;
- goto error;
- }
-
- if( outputNamesAndGUIDs.enumerationError != paNoError )
- {
- result = outputNamesAndGUIDs.enumerationError;
- goto error;
- }
-
- deviceCount = inputNamesAndGUIDs.count + outputNamesAndGUIDs.count;
-
- if( deviceCount > 0 )
- {
- /* allocate array for pointers to PaDeviceInfo structs */
- (*hostApi)->deviceInfos = (PaDeviceInfo**)PaUtil_GroupAllocateMemory(
- winDsHostApi->allocations, sizeof(PaDeviceInfo*) * deviceCount );
- if( !(*hostApi)->deviceInfos )
- {
- result = paInsufficientMemory;
- goto error;
- }
-
- /* allocate all PaDeviceInfo structs in a contiguous block */
- deviceInfoArray = (PaDeviceInfo*)PaUtil_GroupAllocateMemory(
- winDsHostApi->allocations, sizeof(PaDeviceInfo) * deviceCount );
- if( !deviceInfoArray )
- {
- result = paInsufficientMemory;
- goto error;
- }
-
- /* allocate all DSound specific info structs in a contiguous block */
- winDsHostApi->winDsDeviceInfos = (PaWinDsDeviceInfo*)PaUtil_GroupAllocateMemory(
- winDsHostApi->allocations, sizeof(PaWinDsDeviceInfo) * deviceCount );
- if( !winDsHostApi->winDsDeviceInfos )
- {
- result = paInsufficientMemory;
- goto error;
- }
-
- for( i=0; i < deviceCount; ++i )
- {
- PaDeviceInfo *deviceInfo = &deviceInfoArray[i];
- deviceInfo->structVersion = 2;
- deviceInfo->hostApi = hostApiIndex;
- deviceInfo->name = 0;
- (*hostApi)->deviceInfos[i] = deviceInfo;
- }
-
- for( i=0; i< inputNamesAndGUIDs.count; ++i )
- {
- result = AddInputDeviceInfoFromDirectSoundCapture( winDsHostApi,
- inputNamesAndGUIDs.items[i].name,
- inputNamesAndGUIDs.items[i].lpGUID );
- if( result != paNoError )
- goto error;
- }
-
- for( i=0; i< outputNamesAndGUIDs.count; ++i )
- {
- result = AddOutputDeviceInfoFromDirectSound( winDsHostApi,
- outputNamesAndGUIDs.items[i].name,
- outputNamesAndGUIDs.items[i].lpGUID );
- if( result != paNoError )
- goto error;
- }
- }
-
- result = TerminateDSDeviceNameAndGUIDVector( &inputNamesAndGUIDs );
- if( result != paNoError )
- goto error;
-
- result = TerminateDSDeviceNameAndGUIDVector( &outputNamesAndGUIDs );
- if( result != paNoError )
- goto error;
-
-
- (*hostApi)->Terminate = Terminate;
- (*hostApi)->OpenStream = OpenStream;
- (*hostApi)->IsFormatSupported = IsFormatSupported;
-
- PaUtil_InitializeStreamInterface( &winDsHostApi->callbackStreamInterface, CloseStream, StartStream,
- StopStream, AbortStream, IsStreamStopped, IsStreamActive,
- GetStreamTime, GetStreamCpuLoad,
- PaUtil_DummyRead, PaUtil_DummyWrite,
- PaUtil_DummyGetReadAvailable, PaUtil_DummyGetWriteAvailable );
-
- PaUtil_InitializeStreamInterface( &winDsHostApi->blockingStreamInterface, CloseStream, StartStream,
- StopStream, AbortStream, IsStreamStopped, IsStreamActive,
- GetStreamTime, PaUtil_DummyGetCpuLoad,
- ReadStream, WriteStream, GetStreamReadAvailable, GetStreamWriteAvailable );
-
- return result;
-
-error:
- if( winDsHostApi )
- {
- if( winDsHostApi->allocations )
- {
- PaUtil_FreeAllAllocations( winDsHostApi->allocations );
- PaUtil_DestroyAllocationGroup( winDsHostApi->allocations );
- }
-
- PaUtil_FreeMemory( winDsHostApi );
- }
-
- TerminateDSDeviceNameAndGUIDVector( &inputNamesAndGUIDs );
- TerminateDSDeviceNameAndGUIDVector( &outputNamesAndGUIDs );
-
- return result;
-}
-
-
-/***********************************************************************************/
-static void Terminate( struct PaUtilHostApiRepresentation *hostApi )
-{
- PaWinDsHostApiRepresentation *winDsHostApi = (PaWinDsHostApiRepresentation*)hostApi;
-
- /*
- IMPLEMENT ME:
- - clean up any resources not handled by the allocation group
- */
-
- if( winDsHostApi->allocations )
- {
- PaUtil_FreeAllAllocations( winDsHostApi->allocations );
- PaUtil_DestroyAllocationGroup( winDsHostApi->allocations );
- }
-
- PaUtil_FreeMemory( winDsHostApi );
-
- DSW_TerminateDSoundEntryPoints();
-
- CoUninitialize();
-}
-
-
-/* Set minimal latency based on whether NT or Win95.
- * NT has higher latency.
- */
-static int PaWinDS_GetMinSystemLatency( void )
-{
- int minLatencyMsec;
- /* Set minimal latency based on whether NT or other OS.
- * NT has higher latency.
- */
- OSVERSIONINFO osvi;
- osvi.dwOSVersionInfoSize = sizeof( osvi );
- GetVersionEx( &osvi );
- DBUG(("PA - PlatformId = 0x%x\n", osvi.dwPlatformId ));
- DBUG(("PA - MajorVersion = 0x%x\n", osvi.dwMajorVersion ));
- DBUG(("PA - MinorVersion = 0x%x\n", osvi.dwMinorVersion ));
- /* Check for NT */
- if( (osvi.dwMajorVersion == 4) && (osvi.dwPlatformId == 2) )
- {
- minLatencyMsec = PA_WIN_NT_LATENCY;
- }
- else if(osvi.dwMajorVersion >= 5)
- {
- minLatencyMsec = PA_WIN_WDM_LATENCY;
- }
- else
- {
- minLatencyMsec = PA_WIN_9X_LATENCY;
- }
- return minLatencyMsec;
-}
-
-/***********************************************************************************/
-static PaError IsFormatSupported( struct PaUtilHostApiRepresentation *hostApi,
- const PaStreamParameters *inputParameters,
- const PaStreamParameters *outputParameters,
- double sampleRate )
-{
- int inputChannelCount, outputChannelCount;
- PaSampleFormat inputSampleFormat, outputSampleFormat;
-
- if( inputParameters )
- {
- inputChannelCount = inputParameters->channelCount;
- inputSampleFormat = inputParameters->sampleFormat;
-
- /* unless alternate device specification is supported, reject the use of
- paUseHostApiSpecificDeviceSpecification */
-
- if( inputParameters->device == paUseHostApiSpecificDeviceSpecification )
- return paInvalidDevice;
-
- /* check that input device can support inputChannelCount */
- if( inputChannelCount > hostApi->deviceInfos[ inputParameters->device ]->maxInputChannels )
- return paInvalidChannelCount;
-
- /* validate inputStreamInfo */
- if( inputParameters->hostApiSpecificStreamInfo )
- return paIncompatibleHostApiSpecificStreamInfo; /* this implementation doesn't use custom stream info */
- }
- else
- {
- inputChannelCount = 0;
- }
-
- if( outputParameters )
- {
- outputChannelCount = outputParameters->channelCount;
- outputSampleFormat = outputParameters->sampleFormat;
-
- /* unless alternate device specification is supported, reject the use of
- paUseHostApiSpecificDeviceSpecification */
-
- if( outputParameters->device == paUseHostApiSpecificDeviceSpecification )
- return paInvalidDevice;
-
- /* check that output device can support inputChannelCount */
- if( outputChannelCount > hostApi->deviceInfos[ outputParameters->device ]->maxOutputChannels )
- return paInvalidChannelCount;
-
- /* validate outputStreamInfo */
- if( outputParameters->hostApiSpecificStreamInfo )
- return paIncompatibleHostApiSpecificStreamInfo; /* this implementation doesn't use custom stream info */
- }
- else
- {
- outputChannelCount = 0;
- }
-
- /*
- IMPLEMENT ME:
-
- - if a full duplex stream is requested, check that the combination
- of input and output parameters is supported if necessary
-
- - check that the device supports sampleRate
-
- Because the buffer adapter handles conversion between all standard
- sample formats, the following checks are only required if paCustomFormat
- is implemented, or under some other unusual conditions.
-
- - check that input device can support inputSampleFormat, or that
- we have the capability to convert from outputSampleFormat to
- a native format
-
- - check that output device can support outputSampleFormat, or that
- we have the capability to convert from outputSampleFormat to
- a native format
- */
-
- return paFormatIsSupported;
-}
-
-
-/*************************************************************************
-** Determine minimum number of buffers required for this host based
-** on minimum latency. Latency can be optionally set by user by setting
-** an environment variable. For example, to set latency to 200 msec, put:
-**
-** set PA_MIN_LATENCY_MSEC=200
-**
-** in the AUTOEXEC.BAT file and reboot.
-** If the environment variable is not set, then the latency will be determined
-** based on the OS. Windows NT has higher latency than Win95.
-*/
-#define PA_LATENCY_ENV_NAME ("PA_MIN_LATENCY_MSEC")
-#define PA_ENV_BUF_SIZE (32)
-
-static int PaWinDs_GetMinLatencyFrames( double sampleRate )
-{
- char envbuf[PA_ENV_BUF_SIZE];
- DWORD hresult;
- int minLatencyMsec = 0;
-
- /* Let user determine minimal latency by setting environment variable. */
- hresult = GetEnvironmentVariable( PA_LATENCY_ENV_NAME, envbuf, PA_ENV_BUF_SIZE );
- if( (hresult > 0) && (hresult < PA_ENV_BUF_SIZE) )
- {
- minLatencyMsec = atoi( envbuf );
- }
- else
- {
- minLatencyMsec = PaWinDS_GetMinSystemLatency();
-#if PA_USE_HIGH_LATENCY
- PRINT(("PA - Minimum Latency set to %d msec!\n", minLatencyMsec ));
-#endif
-
- }
-
- return (int) (minLatencyMsec * sampleRate * SECONDS_PER_MSEC);
-}
-
-#ifndef NDEBUG
-#define EZ = 0
-#else
-#define EZ
-#endif
-
-/***********************************************************************************/
-/* see pa_hostapi.h for a list of validity guarantees made about OpenStream parameters */
-
-static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi,
- PaStream** s,
- const PaStreamParameters *inputParameters,
- const PaStreamParameters *outputParameters,
- double sampleRate,
- unsigned long framesPerBuffer,
- PaStreamFlags streamFlags,
- PaStreamCallback *streamCallback,
- void *userData )
-{
- PaError result = paNoError;
- PaWinDsHostApiRepresentation *winDsHostApi = (PaWinDsHostApiRepresentation*)hostApi;
- PaWinDsStream *stream = 0;
- int inputChannelCount, outputChannelCount;
- PaSampleFormat inputSampleFormat EZ, outputSampleFormat EZ;
- PaSampleFormat hostInputSampleFormat EZ, hostOutputSampleFormat EZ;
- unsigned long suggestedInputLatencyFrames, suggestedOutputLatencyFrames;
-
- if( inputParameters )
- {
- inputChannelCount = inputParameters->channelCount;
- inputSampleFormat = inputParameters->sampleFormat;
- suggestedInputLatencyFrames = (unsigned long)(inputParameters->suggestedLatency * sampleRate);
-
- /* IDEA: the following 3 checks could be performed by default by pa_front
- unless some flag indicated otherwise */
-
- /* unless alternate device specification is supported, reject the use of
- paUseHostApiSpecificDeviceSpecification */
- if( inputParameters->device == paUseHostApiSpecificDeviceSpecification )
- return paInvalidDevice;
-
- /* check that input device can support inputChannelCount */
- if( inputChannelCount > hostApi->deviceInfos[ inputParameters->device ]->maxInputChannels )
- return paInvalidChannelCount;
-
- /* validate hostApiSpecificStreamInfo */
- if( inputParameters->hostApiSpecificStreamInfo )
- return paIncompatibleHostApiSpecificStreamInfo; /* this implementation doesn't use custom stream info */
- }
- else
- {
- inputChannelCount = 0;
- suggestedInputLatencyFrames = 0;
- }
-
-
- if( outputParameters )
- {
- outputChannelCount = outputParameters->channelCount;
- outputSampleFormat = outputParameters->sampleFormat;
- suggestedOutputLatencyFrames = (unsigned long)(outputParameters->suggestedLatency * sampleRate);
-
- /* unless alternate device specification is supported, reject the use of
- paUseHostApiSpecificDeviceSpecification */
- if( outputParameters->device == paUseHostApiSpecificDeviceSpecification )
- return paInvalidDevice;
-
- /* check that output device can support inputChannelCount */
- if( outputChannelCount > hostApi->deviceInfos[ outputParameters->device ]->maxOutputChannels )
- return paInvalidChannelCount;
-
- /* validate hostApiSpecificStreamInfo */
- if( outputParameters->hostApiSpecificStreamInfo )
- return paIncompatibleHostApiSpecificStreamInfo; /* this implementation doesn't use custom stream info */
- }
- else
- {
- outputChannelCount = 0;
- suggestedOutputLatencyFrames = 0;
- }
-
-
- /*
- IMPLEMENT ME:
-
- ( the following two checks are taken care of by PaUtil_InitializeBufferProcessor() )
-
- - check that input device can support inputSampleFormat, or that
- we have the capability to convert from outputSampleFormat to
- a native format
-
- - check that output device can support outputSampleFormat, or that
- we have the capability to convert from outputSampleFormat to
- a native format
-
- - if a full duplex stream is requested, check that the combination
- of input and output parameters is supported
-
- - check that the device supports sampleRate
-
- - alter sampleRate to a close allowable rate if possible / necessary
-
- - validate suggestedInputLatency and suggestedOutputLatency parameters,
- use default values where necessary
- */
-
-
- /* validate platform specific flags */
- if( (streamFlags & paPlatformSpecificFlags) != 0 )
- return paInvalidFlag; /* unexpected platform specific flag */
-
-
- stream = (PaWinDsStream*)PaUtil_AllocateMemory( sizeof(PaWinDsStream) );
- if( !stream )
- {
- result = paInsufficientMemory;
- goto error;
- }
-
- if( streamCallback )
- {
- PaUtil_InitializeStreamRepresentation( &stream->streamRepresentation,
- &winDsHostApi->callbackStreamInterface, streamCallback, userData );
- }
- else
- {
- PaUtil_InitializeStreamRepresentation( &stream->streamRepresentation,
- &winDsHostApi->blockingStreamInterface, streamCallback, userData );
- }
-
- PaUtil_InitializeCpuLoadMeasurer( &stream->cpuLoadMeasurer, sampleRate );
-
-
- if( inputParameters )
- {
- /* IMPLEMENT ME - establish which host formats are available */
- hostInputSampleFormat =
- PaUtil_SelectClosestAvailableFormat( paInt16 /* native formats */, inputParameters->sampleFormat );
- }
-
- if( outputParameters )
- {
- /* IMPLEMENT ME - establish which host formats are available */
- hostOutputSampleFormat =
- PaUtil_SelectClosestAvailableFormat( paInt16 /* native formats */, outputParameters->sampleFormat );
- }
-
- result = PaUtil_InitializeBufferProcessor( &stream->bufferProcessor,
- inputChannelCount, inputSampleFormat, hostInputSampleFormat,
- outputChannelCount, outputSampleFormat, hostOutputSampleFormat,
- sampleRate, streamFlags, framesPerBuffer,
- framesPerBuffer, /* ignored in paUtilVariableHostBufferSizePartialUsageAllowed mode. */
- /* This next mode is required because DS can split the host buffer when it wraps around. */
- paUtilVariableHostBufferSizePartialUsageAllowed,
- streamCallback, userData );
- if( result != paNoError )
- goto error;
-
-
- stream->streamRepresentation.streamInfo.inputLatency =
- PaUtil_GetBufferProcessorInputLatency(&stream->bufferProcessor); /* FIXME: not initialised anywhere else */
- stream->streamRepresentation.streamInfo.outputLatency =
- PaUtil_GetBufferProcessorOutputLatency(&stream->bufferProcessor); /* FIXME: not initialised anywhere else */
- stream->streamRepresentation.streamInfo.sampleRate = sampleRate;
-
-
-/* DirectSound specific initialization */
- {
- HRESULT hr;
- int bytesPerDirectSoundBuffer;
- DSoundWrapper *dsw;
- int userLatencyFrames;
- int minLatencyFrames;
-
- stream->timerID = 0;
- dsw = &stream->directSoundWrapper;
- DSW_Init( dsw );
-
- /* Get system minimum latency. */
- minLatencyFrames = PaWinDs_GetMinLatencyFrames( sampleRate );
-
- /* Let user override latency by passing latency parameter. */
- userLatencyFrames = (suggestedInputLatencyFrames > suggestedOutputLatencyFrames)
- ? suggestedInputLatencyFrames
- : suggestedOutputLatencyFrames;
- if( userLatencyFrames > 0 ) minLatencyFrames = userLatencyFrames;
-
- /* Calculate stream->framesPerDSBuffer depending on framesPerBuffer */
- if( framesPerBuffer == paFramesPerBufferUnspecified )
- {
- /* App support variable framesPerBuffer */
- stream->framesPerDSBuffer = minLatencyFrames;
-
- stream->streamRepresentation.streamInfo.outputLatency = (double)(minLatencyFrames - 1) / sampleRate;
- }
- else
- {
- /* Round up to number of buffers needed to guarantee that latency. */
- int numUserBuffers = (minLatencyFrames + framesPerBuffer - 1) / framesPerBuffer;
- if( numUserBuffers < 1 ) numUserBuffers = 1;
- numUserBuffers += 1; /* So we have latency worth of buffers ahead of current buffer. */
- stream->framesPerDSBuffer = framesPerBuffer * numUserBuffers;
-
- stream->streamRepresentation.streamInfo.outputLatency = (double)(framesPerBuffer * (numUserBuffers-1)) / sampleRate;
- }
-
- {
- /** @todo REVIEW: this calculation seems incorrect to me - rossb. */
- int msecLatency = (int) ((stream->framesPerDSBuffer * MSEC_PER_SECOND) / sampleRate);
- PRINT(("PortAudio on DirectSound - Latency = %d frames, %d msec\n", stream->framesPerDSBuffer, msecLatency ));
- }
-
-
- /* ------------------ OUTPUT */
- if( outputParameters )
- {
- /*
- PaDeviceInfo *deviceInfo = hostApi->deviceInfos[ outputParameters->device ];
- DBUG(("PaHost_OpenStream: deviceID = 0x%x\n", outputParameters->device));
- */
-
- bytesPerDirectSoundBuffer = stream->framesPerDSBuffer * outputParameters->channelCount * sizeof(short);
- if( bytesPerDirectSoundBuffer < DSBSIZE_MIN )
- {
- result = paBufferTooSmall;
- goto error;
- }
- else if( bytesPerDirectSoundBuffer > DSBSIZE_MAX )
- {
- result = paBufferTooBig;
- goto error;
- }
-
-
- hr = dswDSoundEntryPoints.DirectSoundCreate( winDsHostApi->winDsDeviceInfos[outputParameters->device].lpGUID,
- &dsw->dsw_pDirectSound, NULL );
- if( hr != DS_OK )
- {
- ERR_RPT(("PortAudio: DirectSoundCreate() failed!\n"));
- result = paUnanticipatedHostError;
- PA_DS_SET_LAST_DIRECTSOUND_ERROR( hr );
- goto error;
- }
- hr = DSW_InitOutputBuffer( dsw,
- (unsigned long) (sampleRate + 0.5),
- (WORD)outputParameters->channelCount, bytesPerDirectSoundBuffer );
- DBUG(("DSW_InitOutputBuffer() returns %x\n", hr));
- if( hr != DS_OK )
- {
- result = paUnanticipatedHostError;
- PA_DS_SET_LAST_DIRECTSOUND_ERROR( hr );
- goto error;
- }
- /* Calculate value used in latency calculation to avoid real-time divides. */
- stream->secondsPerHostByte = 1.0 /
- (stream->bufferProcessor.bytesPerHostOutputSample *
- outputChannelCount * sampleRate);
- }
-
- /* ------------------ INPUT */
- if( inputParameters )
- {
- /*
- PaDeviceInfo *deviceInfo = hostApi->deviceInfos[ inputParameters->device ];
- DBUG(("PaHost_OpenStream: deviceID = 0x%x\n", inputParameters->device));
- */
-
- bytesPerDirectSoundBuffer = stream->framesPerDSBuffer * inputParameters->channelCount * sizeof(short);
- if( bytesPerDirectSoundBuffer < DSBSIZE_MIN )
- {
- result = paBufferTooSmall;
- goto error;
- }
- else if( bytesPerDirectSoundBuffer > DSBSIZE_MAX )
- {
- result = paBufferTooBig;
- goto error;
- }
-
- hr = dswDSoundEntryPoints.DirectSoundCaptureCreate( winDsHostApi->winDsDeviceInfos[inputParameters->device].lpGUID,
- &dsw->dsw_pDirectSoundCapture, NULL );
- if( hr != DS_OK )
- {
- ERR_RPT(("PortAudio: DirectSoundCaptureCreate() failed!\n"));
- result = paUnanticipatedHostError;
- PA_DS_SET_LAST_DIRECTSOUND_ERROR( hr );
- goto error;
- }
- hr = DSW_InitInputBuffer( dsw,
- (unsigned long) (sampleRate + 0.5),
- (WORD)inputParameters->channelCount, bytesPerDirectSoundBuffer );
- DBUG(("DSW_InitInputBuffer() returns %x\n", hr));
- if( hr != DS_OK )
- {
- ERR_RPT(("PortAudio: DSW_InitInputBuffer() returns %x\n", hr));
- result = paUnanticipatedHostError;
- PA_DS_SET_LAST_DIRECTSOUND_ERROR( hr );
- goto error;
- }
- }
-
- }
-
- *s = (PaStream*)stream;
-
- return result;
-
-error:
- if( stream )
- PaUtil_FreeMemory( stream );
-
- return result;
-}
-
-
-/***********************************************************************************/
-static PaError Pa_TimeSlice( PaWinDsStream *stream )
-{
- PaError result = 0; /* FIXME: this should be declared int and this function should also return that type (same as stream callback return type)*/
- DSoundWrapper *dsw;
- long numFrames = 0;
- long bytesEmpty = 0;
- long bytesFilled = 0;
- long bytesToXfer = 0;
- long framesToXfer = 0;
- long numInFramesReady = 0;
- long numOutFramesReady = 0;
- long bytesProcessed;
- HRESULT hresult;
- double outputLatency = 0;
- PaStreamCallbackTimeInfo timeInfo = {0,0,0}; /** @todo implement inputBufferAdcTime */
-
-/* Input */
- LPBYTE lpInBuf1 = NULL;
- LPBYTE lpInBuf2 = NULL;
- DWORD dwInSize1 = 0;
- DWORD dwInSize2 = 0;
-/* Output */
- LPBYTE lpOutBuf1 = NULL;
- LPBYTE lpOutBuf2 = NULL;
- DWORD dwOutSize1 = 0;
- DWORD dwOutSize2 = 0;
-
- dsw = &stream->directSoundWrapper;
-
- /* How much input data is available? */
- if( stream->bufferProcessor.inputChannelCount > 0 )
- {
- DSW_QueryInputFilled( dsw, &bytesFilled );
- framesToXfer = numInFramesReady = bytesFilled / dsw->dsw_BytesPerInputFrame;
- outputLatency = ((double)bytesFilled) * stream->secondsPerHostByte;
-
- /** @todo Check for overflow */
- }
-
- /* How much output room is available? */
- if( stream->bufferProcessor.outputChannelCount > 0 )
- {
- UINT previousUnderflowCount = dsw->dsw_OutputUnderflows;
- DSW_QueryOutputSpace( dsw, &bytesEmpty );
- framesToXfer = numOutFramesReady = bytesEmpty / dsw->dsw_BytesPerOutputFrame;
-
- /* Check for underflow */
- if( dsw->dsw_OutputUnderflows != previousUnderflowCount )
- stream->callbackFlags |= paOutputUnderflow;
- }
-
- if( (numInFramesReady > 0) && (numOutFramesReady > 0) )
- {
- framesToXfer = (numOutFramesReady < numInFramesReady) ? numOutFramesReady : numInFramesReady;
- }
-
- if( framesToXfer > 0 )
- {
-
- PaUtil_BeginCpuLoadMeasurement( &stream->cpuLoadMeasurer );
-
- /* The outputBufferDacTime parameter should indicates the time at which
- the first sample of the output buffer is heard at the DACs. */
- timeInfo.currentTime = PaUtil_GetTime();
- timeInfo.outputBufferDacTime = timeInfo.currentTime + outputLatency;
-
-
- PaUtil_BeginBufferProcessing( &stream->bufferProcessor, &timeInfo, stream->callbackFlags );
- stream->callbackFlags = 0;
-
- /* Input */
- if( stream->bufferProcessor.inputChannelCount > 0 )
- {
- bytesToXfer = framesToXfer * dsw->dsw_BytesPerInputFrame;
- hresult = IDirectSoundCaptureBuffer_Lock ( dsw->dsw_InputBuffer,
- dsw->dsw_ReadOffset, bytesToXfer,
- (void **) &lpInBuf1, &dwInSize1,
- (void **) &lpInBuf2, &dwInSize2, 0);
- if (hresult != DS_OK)
- {
- ERR_RPT(("DirectSound IDirectSoundCaptureBuffer_Lock failed, hresult = 0x%x\n",hresult));
- result = paUnanticipatedHostError;
- PA_DS_SET_LAST_DIRECTSOUND_ERROR( hresult );
- goto error2;
- }
-
- numFrames = dwInSize1 / dsw->dsw_BytesPerInputFrame;
- PaUtil_SetInputFrameCount( &stream->bufferProcessor, numFrames );
- PaUtil_SetInterleavedInputChannels( &stream->bufferProcessor, 0, lpInBuf1, 0 );
- /* Is input split into two regions. */
- if( dwInSize2 > 0 )
- {
- numFrames = dwInSize2 / dsw->dsw_BytesPerInputFrame;
- PaUtil_Set2ndInputFrameCount( &stream->bufferProcessor, numFrames );
- PaUtil_Set2ndInterleavedInputChannels( &stream->bufferProcessor, 0, lpInBuf2, 0 );
- }
- }
-
- /* Output */
- if( stream->bufferProcessor.outputChannelCount > 0 )
- {
- bytesToXfer = framesToXfer * dsw->dsw_BytesPerOutputFrame;
- hresult = IDirectSoundBuffer_Lock ( dsw->dsw_OutputBuffer,
- dsw->dsw_WriteOffset, bytesToXfer,
- (void **) &lpOutBuf1, &dwOutSize1,
- (void **) &lpOutBuf2, &dwOutSize2, 0);
- if (hresult != DS_OK)
- {
- ERR_RPT(("DirectSound IDirectSoundBuffer_Lock failed, hresult = 0x%x\n",hresult));
- result = paUnanticipatedHostError;
- PA_DS_SET_LAST_DIRECTSOUND_ERROR( hresult );
- goto error1;
- }
-
- numFrames = dwOutSize1 / dsw->dsw_BytesPerOutputFrame;
- PaUtil_SetOutputFrameCount( &stream->bufferProcessor, numFrames );
- PaUtil_SetInterleavedOutputChannels( &stream->bufferProcessor, 0, lpOutBuf1, 0 );
-
- /* Is output split into two regions. */
- if( dwOutSize2 > 0 )
- {
- numFrames = dwOutSize2 / dsw->dsw_BytesPerOutputFrame;
- PaUtil_Set2ndOutputFrameCount( &stream->bufferProcessor, numFrames );
- PaUtil_Set2ndInterleavedOutputChannels( &stream->bufferProcessor, 0, lpOutBuf2, 0 );
- }
- }
-
- result = paContinue;
- numFrames = PaUtil_EndBufferProcessing( &stream->bufferProcessor, &result );
- stream->framesWritten += numFrames;
-
- if( stream->bufferProcessor.outputChannelCount > 0 )
- {
- /* FIXME: an underflow could happen here */
-
- /* Update our buffer offset and unlock sound buffer */
- bytesProcessed = numFrames * dsw->dsw_BytesPerOutputFrame;
- dsw->dsw_WriteOffset = (dsw->dsw_WriteOffset + bytesProcessed) % dsw->dsw_OutputSize;
- IDirectSoundBuffer_Unlock( dsw->dsw_OutputBuffer, lpOutBuf1, dwOutSize1, lpOutBuf2, dwOutSize2);
- dsw->dsw_FramesWritten += numFrames;
- }
-
-error1:
- if( stream->bufferProcessor.inputChannelCount > 0 )
- {
- /* FIXME: an overflow could happen here */
-
- /* Update our buffer offset and unlock sound buffer */
- bytesProcessed = numFrames * dsw->dsw_BytesPerInputFrame;
- dsw->dsw_ReadOffset = (dsw->dsw_ReadOffset + bytesProcessed) % dsw->dsw_InputSize;
- IDirectSoundCaptureBuffer_Unlock( dsw->dsw_InputBuffer, lpInBuf1, dwInSize1, lpInBuf2, dwInSize2);
- }
-error2:
-
- PaUtil_EndCpuLoadMeasurement( &stream->cpuLoadMeasurer, numFrames );
-
- }
-
- return result;
-}
-/*******************************************************************/
-static void CALLBACK Pa_TimerCallback(UINT uID, UINT uMsg, DWORD dwUser, DWORD dw1, DWORD dw2)
-{
- PaWinDsStream *stream;
-
- /* suppress unused variable warnings */
- (void) uID;
- (void) uMsg;
- (void) dw1;
- (void) dw2;
-
- stream = (PaWinDsStream *) dwUser;
- if( stream == NULL ) return;
-
- if( stream->isActive )
- {
- if( stream->abortProcessing )
- {
- stream->isActive = 0;
- }
- else if( stream->stopProcessing )
- {
- DSoundWrapper *dsw = &stream->directSoundWrapper;
- if( stream->bufferProcessor.outputChannelCount > 0 )
- {
- DSW_ZeroEmptySpace( dsw );
- /* clear isActive when all sound played */
- if( dsw->dsw_FramesPlayed >= stream->framesWritten )
- {
- stream->isActive = 0;
- }
- }
- else
- {
- stream->isActive = 0;
- }
- }
- else
- {
- if( Pa_TimeSlice( stream ) != 0) /* Call time slice independant of timing method. */
- {
- /* FIXME implement handling of paComplete and paAbort if possible */
- stream->stopProcessing = 1;
- }
- }
-
- if( !stream->isActive ){
- if( stream->streamRepresentation.streamFinishedCallback != 0 )
- stream->streamRepresentation.streamFinishedCallback( stream->streamRepresentation.userData );
- }
- }
-}
-
-/***********************************************************************************
- When CloseStream() is called, the multi-api layer ensures that
- the stream has already been stopped or aborted.
-*/
-static PaError CloseStream( PaStream* s )
-{
- PaError result = paNoError;
- PaWinDsStream *stream = (PaWinDsStream*)s;
-
- DSW_Term( &stream->directSoundWrapper );
-
- PaUtil_TerminateBufferProcessor( &stream->bufferProcessor );
- PaUtil_TerminateStreamRepresentation( &stream->streamRepresentation );
- PaUtil_FreeMemory( stream );
-
- return result;
-}
-
-/***********************************************************************************/
-static PaError StartStream( PaStream *s )
-{
- PaError result = paNoError;
- PaWinDsStream *stream = (PaWinDsStream*)s;
- HRESULT hr;
-
- PaUtil_ResetBufferProcessor( &stream->bufferProcessor );
-
- if( stream->bufferProcessor.inputChannelCount > 0 )
- {
- hr = DSW_StartInput( &stream->directSoundWrapper );
- DBUG(("StartStream: DSW_StartInput returned = 0x%X.\n", hr));
- if( hr != DS_OK )
- {
- result = paUnanticipatedHostError;
- PA_DS_SET_LAST_DIRECTSOUND_ERROR( hr );
- goto error;
- }
- }
-
- stream->framesWritten = 0;
- stream->callbackFlags = 0;
-
- stream->abortProcessing = 0;
- stream->stopProcessing = 0;
- stream->isActive = 1;
-
- if( stream->bufferProcessor.outputChannelCount > 0 )
- {
- /* Give user callback a chance to pre-fill buffer. REVIEW - i thought we weren't pre-filling, rb. */
- result = Pa_TimeSlice( stream );
- if( result != paNoError ) return result; // FIXME - what if finished?
-
- hr = DSW_StartOutput( &stream->directSoundWrapper );
- DBUG(("PaHost_StartOutput: DSW_StartOutput returned = 0x%X.\n", hr));
- if( hr != DS_OK )
- {
- result = paUnanticipatedHostError;
- PA_DS_SET_LAST_DIRECTSOUND_ERROR( hr );
- goto error;
- }
- }
-
-
- /* Create timer that will wake us up so we can fill the DSound buffer. */
- {
- int resolution;
- int framesPerWakeup = stream->framesPerDSBuffer / 4;
- int msecPerWakeup = MSEC_PER_SECOND * framesPerWakeup / (int) stream->streamRepresentation.streamInfo.sampleRate;
- if( msecPerWakeup < 10 ) msecPerWakeup = 10;
- else if( msecPerWakeup > 100 ) msecPerWakeup = 100;
- resolution = msecPerWakeup/4;
- stream->timerID = timeSetEvent( msecPerWakeup, resolution, (LPTIMECALLBACK) Pa_TimerCallback,
- (DWORD) stream, TIME_PERIODIC );
- }
- if( stream->timerID == 0 )
- {
- stream->isActive = 0;
- result = paUnanticipatedHostError;
- PA_DS_SET_LAST_DIRECTSOUND_ERROR( hr );
- goto error;
- }
-
- stream->isStarted = TRUE;
-
-error:
- return result;
-}
-
-
-/***********************************************************************************/
-static PaError StopStream( PaStream *s )
-{
- PaError result = paNoError;
- PaWinDsStream *stream = (PaWinDsStream*)s;
- HRESULT hr;
- int timeoutMsec;
-
- stream->stopProcessing = 1;
- /* Set timeout at 20% beyond maximum time we might wait. */
- timeoutMsec = (int) (1200.0 * stream->framesPerDSBuffer / stream->streamRepresentation.streamInfo.sampleRate);
- while( stream->isActive && (timeoutMsec > 0) )
- {
- Sleep(10);
- timeoutMsec -= 10;
- }
- if( stream->timerID != 0 )
- {
- timeKillEvent(stream->timerID); /* Stop callback timer. */
- stream->timerID = 0;
- }
-
-
- if( stream->bufferProcessor.outputChannelCount > 0 )
- {
- hr = DSW_StopOutput( &stream->directSoundWrapper );
- }
-
- if( stream->bufferProcessor.inputChannelCount > 0 )
- {
- hr = DSW_StopInput( &stream->directSoundWrapper );
- }
-
- stream->isStarted = FALSE;
-
- return result;
-}
-
-
-/***********************************************************************************/
-static PaError AbortStream( PaStream *s )
-{
- PaWinDsStream *stream = (PaWinDsStream*)s;
-
- stream->abortProcessing = 1;
- return StopStream( s );
-}
-
-
-/***********************************************************************************/
-static PaError IsStreamStopped( PaStream *s )
-{
- PaWinDsStream *stream = (PaWinDsStream*)s;
-
- return !stream->isStarted;
-}
-
-
-/***********************************************************************************/
-static PaError IsStreamActive( PaStream *s )
-{
- PaWinDsStream *stream = (PaWinDsStream*)s;
-
- return stream->isActive;
-}
-
-/***********************************************************************************/
-static PaTime GetStreamTime( PaStream *s )
-{
- /* suppress unused variable warnings */
- (void) s;
-
-
-/*
- new behavior for GetStreamTime is to return a stream based seconds clock
- used for the outTime parameter to the callback.
- FIXME: delete this comment when the other unnecessary related code has
- been cleaned from this file.
-
- PaWinDsStream *stream = (PaWinDsStream*)s;
- DSoundWrapper *dsw;
- dsw = &stream->directSoundWrapper;
- return dsw->dsw_FramesPlayed;
-*/
- return PaUtil_GetTime();
-}
-
-
-/***********************************************************************************/
-static double GetStreamCpuLoad( PaStream* s )
-{
- PaWinDsStream *stream = (PaWinDsStream*)s;
-
- return PaUtil_GetCpuLoad( &stream->cpuLoadMeasurer );
-}
-
-
-
-/***********************************************************************************
- As separate stream interfaces are used for blocking and callback
- streams, the following functions can be guaranteed to only be called
- for blocking streams.
-*/
-
-static PaError ReadStream( PaStream* s,
- void *buffer,
- unsigned long frames )
-{
- PaWinDsStream *stream = (PaWinDsStream*)s;
-
- /* suppress unused variable warnings */
- (void) buffer;
- (void) frames;
- (void) stream;
-
- /* IMPLEMENT ME, see portaudio.h for required behavior*/
-
- return paNoError;
-}
-
-
-/***********************************************************************************/
-static PaError WriteStream( PaStream* s,
- const void *buffer,
- unsigned long frames )
-{
- PaWinDsStream *stream = (PaWinDsStream*)s;
-
- /* suppress unused variable warnings */
- (void) buffer;
- (void) frames;
- (void) stream;
-
- /* IMPLEMENT ME, see portaudio.h for required behavior*/
-
- return paNoError;
-}
-
-
-/***********************************************************************************/
-static signed long GetStreamReadAvailable( PaStream* s )
-{
- PaWinDsStream *stream = (PaWinDsStream*)s;
-
- /* suppress unused variable warnings */
- (void) stream;
-
- /* IMPLEMENT ME, see portaudio.h for required behavior*/
-
- return 0;
-}
-
-
-/***********************************************************************************/
-static signed long GetStreamWriteAvailable( PaStream* s )
-{
- PaWinDsStream *stream = (PaWinDsStream*)s;
-
- /* suppress unused variable warnings */
- (void) stream;
-
- /* IMPLEMENT ME, see portaudio.h for required behavior*/
-
- return 0;
-}
-
-
-
+/*
+ * $Id: pa_win_ds.c,v 1.1.2.49 2004/05/16 04:08:55 rossbencina Exp $
+ * Portable Audio I/O Library DirectSound implementation
+ *
+ * Based on the Open Source API proposed by Ross Bencina
+ * Copyright (c) 1999-2002 Ross Bencina, Phil Burk
+ *
+ * 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.
+ *
+ * Any person wishing to distribute modifications to the Software is
+ * requested to send the modifications to the original developer so that
+ * they can be incorporated into the canonical version.
+ *
+ * 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.
+ */
+
+/** @file
+
+ @todo implement paInputOverflow callback status flag
+
+ @todo implement paNeverDropInput.
+
+ @todo implement host api specific extension to set i/o buffer sizes in frames
+
+ @todo implement initialisation of PaDeviceInfo default*Latency fields (currently set to 0.)
+
+ @todo implement ReadStream, WriteStream, GetStreamReadAvailable, GetStreamWriteAvailable
+
+ @todo audit handling of DirectSound result codes - in many cases we could convert a HRESULT into
+ a native portaudio error code. Standard DirectSound result codes are documented at msdn.
+
+ @todo implement IsFormatSupported
+
+ @todo check that CoInitialize() CoUninitialize() are always correctly
+ paired, even in error cases.
+
+ @todo call PaUtil_SetLastHostErrorInfo with a specific error string (currently just "DSound error").
+
+ @todo make sure all buffers have been played before stopping the stream
+ when the stream callback returns paComplete
+
+ old TODOs from phil, need to work out if these have been done:
+ O- fix "patest_stop.c"
+*/
+
+#include <stdio.h>
+#include <string.h> /* strlen() */
+
+#include "pa_util.h"
+#include "pa_allocation.h"
+#include "pa_hostapi.h"
+#include "pa_stream.h"
+#include "pa_cpuload.h"
+#include "pa_process.h"
+
+#include "dsound_wrapper.h"
+
+#if (defined(WIN32) && (defined(_MSC_VER) && (_MSC_VER >= 1200))) /* MSC version 6 and above */
+#pragma comment( lib, "dsound.lib" )
+#pragma comment( lib, "winmm.lib" )
+#endif
+
+
+#define PRINT(x) /* { printf x; fflush(stdout); } */
+#define ERR_RPT(x) PRINT(x)
+#define DBUG(x) /* PRINT(x) */
+#define DBUGX(x) /* PRINT(x) */
+
+#define PA_USE_HIGH_LATENCY (0)
+#if PA_USE_HIGH_LATENCY
+#define PA_WIN_9X_LATENCY (500)
+#define PA_WIN_NT_LATENCY (600)
+#else
+#define PA_WIN_9X_LATENCY (140)
+#define PA_WIN_NT_LATENCY (280)
+#endif
+
+#define PA_WIN_WDM_LATENCY (120)
+
+#define SECONDS_PER_MSEC (0.001)
+#define MSEC_PER_SECOND (1000)
+
+/* prototypes for functions declared in this file */
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif /* __cplusplus */
+
+PaError PaWinDs_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiIndex index );
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+static void Terminate( struct PaUtilHostApiRepresentation *hostApi );
+static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi,
+ PaStream** s,
+ const PaStreamParameters *inputParameters,
+ const PaStreamParameters *outputParameters,
+ double sampleRate,
+ unsigned long framesPerBuffer,
+ PaStreamFlags streamFlags,
+ PaStreamCallback *streamCallback,
+ void *userData );
+static PaError IsFormatSupported( struct PaUtilHostApiRepresentation *hostApi,
+ const PaStreamParameters *inputParameters,
+ const PaStreamParameters *outputParameters,
+ double sampleRate );
+static PaError CloseStream( PaStream* stream );
+static PaError StartStream( PaStream *stream );
+static PaError StopStream( PaStream *stream );
+static PaError AbortStream( PaStream *stream );
+static PaError IsStreamStopped( PaStream *s );
+static PaError IsStreamActive( PaStream *stream );
+static PaTime GetStreamTime( PaStream *stream );
+static double GetStreamCpuLoad( PaStream* stream );
+static PaError ReadStream( PaStream* stream, void *buffer, unsigned long frames );
+static PaError WriteStream( PaStream* stream, const void *buffer, unsigned long frames );
+static signed long GetStreamReadAvailable( PaStream* stream );
+static signed long GetStreamWriteAvailable( PaStream* stream );
+
+
+/* FIXME: should convert hr to a string */
+#define PA_DS_SET_LAST_DIRECTSOUND_ERROR( hr ) \
+ PaUtil_SetLastHostErrorInfo( paDirectSound, hr, "DirectSound error" )
+
+/************************************************* DX Prototypes **********/
+static BOOL CALLBACK CollectGUIDsProc(LPGUID lpGUID,
+ LPCTSTR lpszDesc,
+ LPCTSTR lpszDrvName,
+ LPVOID lpContext );
+
+/************************************************************************************/
+/********************** Structures **************************************************/
+/************************************************************************************/
+/* PaWinDsHostApiRepresentation - host api datastructure specific to this implementation */
+
+typedef struct PaWinDsDeviceInfo
+{
+ GUID guid;
+ GUID *lpGUID;
+ double sampleRates[3];
+} PaWinDsDeviceInfo;
+
+typedef struct
+{
+ PaUtilHostApiRepresentation inheritedHostApiRep;
+ PaUtilStreamInterface callbackStreamInterface;
+ PaUtilStreamInterface blockingStreamInterface;
+
+ PaUtilAllocationGroup *allocations;
+
+ /* implementation specific data goes here */
+ PaWinDsDeviceInfo *winDsDeviceInfos;
+
+} PaWinDsHostApiRepresentation;
+
+/* PaWinDsStream - a stream data structure specifically for this implementation */
+
+typedef struct PaWinDsStream
+{
+ PaUtilStreamRepresentation streamRepresentation;
+ PaUtilCpuLoadMeasurer cpuLoadMeasurer;
+ PaUtilBufferProcessor bufferProcessor;
+
+/* DirectSound specific data. */
+ DSoundWrapper directSoundWrapper;
+ MMRESULT timerID;
+ BOOL ifInsideCallback; /* Test for reentrancy. */
+ int framesPerDSBuffer;
+ double framesWritten;
+ double secondsPerHostByte; /* Used to optimize latency calculation for outTime */
+
+ PaStreamCallbackFlags callbackFlags;
+
+/* FIXME - move all below to PaUtilStreamRepresentation */
+ volatile int isStarted;
+ volatile int isActive;
+ volatile int stopProcessing; /* stop thread once existing buffers have been returned */
+ volatile int abortProcessing; /* stop thread immediately */
+} PaWinDsStream;
+
+
+/************************************************************************************
+** Duplicate the input string using the allocations allocator.
+** A NULL string is converted to a zero length string.
+** If memory cannot be allocated, NULL is returned.
+**/
+static char *DuplicateDeviceNameString( PaUtilAllocationGroup *allocations, const char* src )
+{
+ char *result = 0;
+
+ if( src != NULL )
+ {
+ size_t len = strlen(src);
+ result = (char*)PaUtil_GroupAllocateMemory( allocations, (long)(len + 1) );
+ if( result )
+ memcpy( (void *) result, src, len+1 );
+ }
+ else
+ {
+ result = (char*)PaUtil_GroupAllocateMemory( allocations, 1 );
+ if( result )
+ result[0] = '\0';
+ }
+
+ return result;
+}
+
+/************************************************************************************
+** DSDeviceNameAndGUID, DSDeviceNameAndGUIDVector used for collecting preliminary
+** information during device enumeration.
+*/
+typedef struct DSDeviceNameAndGUID{
+ char *name; // allocated from parent's allocations, never deleted by this structure
+ GUID guid;
+ LPGUID lpGUID;
+} DSDeviceNameAndGUID;
+
+typedef struct DSDeviceNameAndGUIDVector{
+ PaUtilAllocationGroup *allocations;
+ PaError enumerationError;
+
+ int count;
+ int free;
+ DSDeviceNameAndGUID *items; // Allocated using LocalAlloc()
+} DSDeviceNameAndGUIDVector;
+
+static PaError InitializeDSDeviceNameAndGUIDVector(
+ DSDeviceNameAndGUIDVector *guidVector, PaUtilAllocationGroup *allocations )
+{
+ PaError result = paNoError;
+
+ guidVector->allocations = allocations;
+ guidVector->enumerationError = paNoError;
+
+ guidVector->count = 0;
+ guidVector->free = 8;
+ guidVector->items = (DSDeviceNameAndGUID*)LocalAlloc( LMEM_FIXED, sizeof(DSDeviceNameAndGUID) * guidVector->free );
+ if( guidVector->items == NULL )
+ result = paInsufficientMemory;
+
+ return result;
+}
+
+static PaError ExpandDSDeviceNameAndGUIDVector( DSDeviceNameAndGUIDVector *guidVector )
+{
+ PaError result = paNoError;
+ DSDeviceNameAndGUID *newItems;
+ int i;
+
+ /* double size of vector */
+ int size = guidVector->count + guidVector->free;
+ guidVector->free += size;
+
+ newItems = (DSDeviceNameAndGUID*)LocalAlloc( LMEM_FIXED, sizeof(DSDeviceNameAndGUID) * size * 2 );
+ if( newItems == NULL )
+ {
+ result = paInsufficientMemory;
+ }
+ else
+ {
+ for( i=0; i < guidVector->count; ++i )
+ {
+ newItems[i].name = guidVector->items[i].name;
+ if( guidVector->items[i].lpGUID == NULL )
+ {
+ newItems[i].lpGUID = NULL;
+ }
+ else
+ {
+ newItems[i].lpGUID = &newItems[i].guid;
+ memcpy( &newItems[i].guid, guidVector->items[i].lpGUID, sizeof(GUID) );;
+ }
+ }
+
+ LocalFree( guidVector->items );
+ guidVector->items = newItems;
+ }
+
+ return result;
+}
+
+/*
+ it's safe to call DSDeviceNameAndGUIDVector multiple times
+*/
+static PaError TerminateDSDeviceNameAndGUIDVector( DSDeviceNameAndGUIDVector *guidVector )
+{
+ PaError result = paNoError;
+
+ if( guidVector->items != NULL )
+ {
+ if( LocalFree( guidVector->items ) != NULL )
+ result = paInsufficientMemory; /** @todo this isn't the correct error to return from a deallocation failure */
+
+ guidVector->items = NULL;
+ }
+
+ return result;
+}
+
+/************************************************************************************
+** Collect preliminary device information during DirectSound enumeration
+*/
+static BOOL CALLBACK CollectGUIDsProc(LPGUID lpGUID,
+ LPCTSTR lpszDesc,
+ LPCTSTR lpszDrvName,
+ LPVOID lpContext )
+{
+ DSDeviceNameAndGUIDVector *namesAndGUIDs = (DSDeviceNameAndGUIDVector*)lpContext;
+ PaError error;
+
+ (void) lpszDrvName; /* unused variable */
+
+ if( namesAndGUIDs->free == 0 )
+ {
+ error = ExpandDSDeviceNameAndGUIDVector( namesAndGUIDs );
+ if( error != paNoError )
+ {
+ namesAndGUIDs->enumerationError = error;
+ return FALSE;
+ }
+ }
+
+ /* Set GUID pointer, copy GUID to storage in DSDeviceNameAndGUIDVector. */
+ if( lpGUID == NULL )
+ {
+ namesAndGUIDs->items[namesAndGUIDs->count].lpGUID = NULL;
+ }
+ else
+ {
+ namesAndGUIDs->items[namesAndGUIDs->count].lpGUID =
+ &namesAndGUIDs->items[namesAndGUIDs->count].guid;
+
+ memcpy( &namesAndGUIDs->items[namesAndGUIDs->count].guid, lpGUID, sizeof(GUID) );
+ }
+
+ namesAndGUIDs->items[namesAndGUIDs->count].name =
+ DuplicateDeviceNameString( namesAndGUIDs->allocations, lpszDesc );
+ if( namesAndGUIDs->items[namesAndGUIDs->count].name == NULL )
+ {
+ namesAndGUIDs->enumerationError = paInsufficientMemory;
+ return FALSE;
+ }
+
+ ++namesAndGUIDs->count;
+ --namesAndGUIDs->free;
+
+ return TRUE;
+}
+
+
+#define PA_DEFAULTSAMPLERATESEARCHORDER_COUNT_ (13) /* must match array length below */
+static double defaultSampleRateSearchOrder_[] =
+ { 44100.0, 48000.0, 32000.0, 24000.0, 22050.0, 88200.0, 96000.0, 192000.0,
+ 16000.0, 12000.0, 11025.0, 9600.0, 8000.0 };
+
+
+/************************************************************************************
+** Extract capabilities from an output device, and add it to the device info list
+** if successful. This function assumes that there is enough room in the
+** device info list to accomodate all entries.
+**
+** The device will not be added to the device list if any errors are encountered.
+*/
+static PaError AddOutputDeviceInfoFromDirectSound(
+ PaWinDsHostApiRepresentation *winDsHostApi, char *name, LPGUID lpGUID )
+{
+ PaUtilHostApiRepresentation *hostApi = &winDsHostApi->inheritedHostApiRep;
+ PaDeviceInfo *deviceInfo = hostApi->deviceInfos[hostApi->info.deviceCount];
+ PaWinDsDeviceInfo *winDsDeviceInfo = &winDsHostApi->winDsDeviceInfos[hostApi->info.deviceCount];
+ HRESULT hr;
+ LPDIRECTSOUND lpDirectSound;
+ DSCAPS caps;
+ int deviceOK = TRUE;
+ PaError result = paNoError;
+ int i;
+
+ /* Copy GUID to the device info structure. Set pointer. */
+ if( lpGUID == NULL )
+ {
+ winDsDeviceInfo->lpGUID = NULL;
+ }
+ else
+ {
+ memcpy( &winDsDeviceInfo->guid, lpGUID, sizeof(GUID) );
+ winDsDeviceInfo->lpGUID = &winDsDeviceInfo->guid;
+ }
+
+
+ /* Create a DirectSound object for the specified GUID
+ Note that using CoCreateInstance doesn't work on windows CE.
+ */
+ hr = dswDSoundEntryPoints.DirectSoundCreate( lpGUID, &lpDirectSound, NULL );
+
+ /** try using CoCreateInstance because DirectSoundCreate was hanging under
+ some circumstances - note this was probably related to the
+ #define BOOL short bug which has now been fixed
+ @todo delete this comment and the following code once we've ensured
+ there is no bug.
+ */
+ /*
+ hr = CoCreateInstance( &CLSID_DirectSound, NULL, CLSCTX_INPROC_SERVER,
+ &IID_IDirectSound, (void**)&lpDirectSound );
+
+ if( hr == S_OK )
+ {
+ hr = IDirectSound_Initialize( lpDirectSound, lpGUID );
+ }
+ */
+
+ if( hr != DS_OK )
+ {
+ DBUG(("Cannot create DirectSound for %s. Result = 0x%x\n", name, hr ));
+ deviceOK = FALSE;
+ }
+ else
+ {
+ /* Query device characteristics. */
+ memset( &caps, 0, sizeof(caps) );
+ caps.dwSize = sizeof(caps);
+ hr = IDirectSound_GetCaps( lpDirectSound, &caps );
+ if( hr != DS_OK )
+ {
+ DBUG(("Cannot GetCaps() for DirectSound device %s. Result = 0x%x\n", name, hr ));
+ deviceOK = FALSE;
+ }
+ else
+ {
+
+#ifndef PA_NO_WMME
+ if( caps.dwFlags & DSCAPS_EMULDRIVER )
+ {
+ /* If WMME supported, then reject Emulated drivers because they are lousy. */
+ deviceOK = FALSE;
+ }
+#endif
+
+ if( deviceOK )
+ {
+ deviceInfo->maxInputChannels = 0;
+ /* Mono or stereo device? */
+ deviceInfo->maxOutputChannels = ( caps.dwFlags & DSCAPS_PRIMARYSTEREO ) ? 2 : 1;
+
+ deviceInfo->defaultLowInputLatency = 0.; /** @todo IMPLEMENT ME */
+ deviceInfo->defaultLowOutputLatency = 0.; /** @todo IMPLEMENT ME */
+ deviceInfo->defaultHighInputLatency = 0.; /** @todo IMPLEMENT ME */
+ deviceInfo->defaultHighOutputLatency = 0.; /** @todo IMPLEMENT ME */
+
+ /* initialize defaultSampleRate */
+
+ if( caps.dwFlags & DSCAPS_CONTINUOUSRATE )
+ {
+ /* initialize to caps.dwMaxSecondarySampleRate incase none of the standard rates match */
+ deviceInfo->defaultSampleRate = caps.dwMaxSecondarySampleRate;
+
+ for( i = 0; i < PA_DEFAULTSAMPLERATESEARCHORDER_COUNT_; ++i )
+ {
+ if( defaultSampleRateSearchOrder_[i] >= caps.dwMinSecondarySampleRate
+ && defaultSampleRateSearchOrder_[i] <= caps.dwMaxSecondarySampleRate ){
+
+ deviceInfo->defaultSampleRate = defaultSampleRateSearchOrder_[i];
+ break;
+ }
+ }
+ }
+ else if( caps.dwMinSecondarySampleRate == caps.dwMaxSecondarySampleRate )
+ {
+ if( caps.dwMinSecondarySampleRate == 0 )
+ {
+ /*
+ ** On my Thinkpad 380Z, DirectSoundV6 returns min-max=0 !!
+ ** But it supports continuous sampling.
+ ** So fake range of rates, and hope it really supports it.
+ */
+ deviceInfo->defaultSampleRate = 44100.0f;
+
+ DBUG(("PA - Reported rates both zero. Setting to fake values for device #%d\n", sDeviceIndex ));
+ }
+ else
+ {
+ deviceInfo->defaultSampleRate = caps.dwMaxSecondarySampleRate;
+ }
+ }
+ else if( (caps.dwMinSecondarySampleRate < 1000.0) && (caps.dwMaxSecondarySampleRate > 50000.0) )
+ {
+ /* The EWS88MT drivers lie, lie, lie. The say they only support two rates, 100 & 100000.
+ ** But we know that they really support a range of rates!
+ ** So when we see a ridiculous set of rates, assume it is a range.
+ */
+ deviceInfo->defaultSampleRate = 44100.0f;
+ DBUG(("PA - Sample rate range used instead of two odd values for device #%d\n", sDeviceIndex ));
+ }
+ else deviceInfo->defaultSampleRate = caps.dwMaxSecondarySampleRate;
+
+
+ //printf( "min %d max %d\n", caps.dwMinSecondarySampleRate, caps.dwMaxSecondarySampleRate );
+ // dwFlags | DSCAPS_CONTINUOUSRATE
+ }
+ }
+
+ IDirectSound_Release( lpDirectSound );
+ }
+
+ if( deviceOK )
+ {
+ deviceInfo->name = name;
+
+ if( lpGUID == NULL )
+ hostApi->info.defaultOutputDevice = hostApi->info.deviceCount;
+
+ hostApi->info.deviceCount++;
+ }
+
+ return result;
+}
+
+
+/************************************************************************************
+** Extract capabilities from an input device, and add it to the device info list
+** if successful. This function assumes that there is enough room in the
+** device info list to accomodate all entries.
+**
+** The device will not be added to the device list if any errors are encountered.
+*/
+static PaError AddInputDeviceInfoFromDirectSoundCapture(
+ PaWinDsHostApiRepresentation *winDsHostApi, char *name, LPGUID lpGUID )
+{
+ PaUtilHostApiRepresentation *hostApi = &winDsHostApi->inheritedHostApiRep;
+ PaDeviceInfo *deviceInfo = hostApi->deviceInfos[hostApi->info.deviceCount];
+ PaWinDsDeviceInfo *winDsDeviceInfo = &winDsHostApi->winDsDeviceInfos[hostApi->info.deviceCount];
+ HRESULT hr;
+ LPDIRECTSOUNDCAPTURE lpDirectSoundCapture;
+ DSCCAPS caps;
+ int deviceOK = TRUE;
+ PaError result = paNoError;
+
+ /* Copy GUID to the device info structure. Set pointer. */
+ if( lpGUID == NULL )
+ {
+ winDsDeviceInfo->lpGUID = NULL;
+ }
+ else
+ {
+ winDsDeviceInfo->lpGUID = &winDsDeviceInfo->guid;
+ memcpy( &winDsDeviceInfo->guid, lpGUID, sizeof(GUID) );
+ }
+
+
+ hr = dswDSoundEntryPoints.DirectSoundCaptureCreate( lpGUID, &lpDirectSoundCapture, NULL );
+
+ /** try using CoCreateInstance because DirectSoundCreate was hanging under
+ some circumstances - note this was probably related to the
+ #define BOOL short bug which has now been fixed
+ @todo delete this comment and the following code once we've ensured
+ there is no bug.
+ */
+ /*
+ hr = CoCreateInstance( &CLSID_DirectSoundCapture, NULL, CLSCTX_INPROC_SERVER,
+ &IID_IDirectSoundCapture, (void**)&lpDirectSoundCapture );
+ */
+ if( hr != DS_OK )
+ {
+ DBUG(("Cannot create Capture for %s. Result = 0x%x\n", name, hr ));
+ deviceOK = FALSE;
+ }
+ else
+ {
+ /* Query device characteristics. */
+ memset( &caps, 0, sizeof(caps) );
+ caps.dwSize = sizeof(caps);
+ hr = IDirectSoundCapture_GetCaps( lpDirectSoundCapture, &caps );
+ if( hr != DS_OK )
+ {
+ DBUG(("Cannot GetCaps() for Capture device %s. Result = 0x%x\n", name, hr ));
+ deviceOK = FALSE;
+ }
+ else
+ {
+#ifndef PA_NO_WMME
+ if( caps.dwFlags & DSCAPS_EMULDRIVER )
+ {
+ /* If WMME supported, then reject Emulated drivers because they are lousy. */
+ deviceOK = FALSE;
+ }
+#endif
+
+ if( deviceOK )
+ {
+ deviceInfo->maxInputChannels = caps.dwChannels;
+ deviceInfo->maxOutputChannels = 0;
+
+ deviceInfo->defaultLowInputLatency = 0.; /** @todo IMPLEMENT ME */
+ deviceInfo->defaultLowOutputLatency = 0.; /** @todo IMPLEMENT ME */
+ deviceInfo->defaultHighInputLatency = 0.; /** @todo IMPLEMENT ME */
+ deviceInfo->defaultHighOutputLatency = 0.; /** @todo IMPLEMENT ME */
+
+/* constants from a WINE patch by Francois Gouget, see:
+ http://www.winehq.com/hypermail/wine-patches/2003/01/0290.html
+
+ ---
+ Date: Fri, 14 May 2004 10:38:12 +0200 (CEST)
+ From: Francois Gouget <fgouget@ ... .fr>
+ To: Ross Bencina <rbencina@ ... .au>
+ Subject: Re: Permission to use wine 48/96 wave patch in BSD licensed library
+
+ [snip]
+
+ I give you permission to use the patch below under the BSD license.
+ http://www.winehq.com/hypermail/wine-patches/2003/01/0290.html
+
+ [snip]
+*/
+#ifndef WAVE_FORMAT_48M08
+#define WAVE_FORMAT_48M08 0x00001000 /* 48 kHz, Mono, 8-bit */
+#define WAVE_FORMAT_48S08 0x00002000 /* 48 kHz, Stereo, 8-bit */
+#define WAVE_FORMAT_48M16 0x00004000 /* 48 kHz, Mono, 16-bit */
+#define WAVE_FORMAT_48S16 0x00008000 /* 48 kHz, Stereo, 16-bit */
+#define WAVE_FORMAT_96M08 0x00010000 /* 96 kHz, Mono, 8-bit */
+#define WAVE_FORMAT_96S08 0x00020000 /* 96 kHz, Stereo, 8-bit */
+#define WAVE_FORMAT_96M16 0x00040000 /* 96 kHz, Mono, 16-bit */
+#define WAVE_FORMAT_96S16 0x00080000 /* 96 kHz, Stereo, 16-bit */
+#endif
+
+ /* defaultSampleRate */
+ if( caps.dwChannels == 2 )
+ {
+ if( caps.dwFormats & WAVE_FORMAT_4S16 )
+ deviceInfo->defaultSampleRate = 44100.0;
+ else if( caps.dwFormats & WAVE_FORMAT_48S16 )
+ deviceInfo->defaultSampleRate = 48000.0;
+ else if( caps.dwFormats & WAVE_FORMAT_2S16 )
+ deviceInfo->defaultSampleRate = 22050.0;
+ else if( caps.dwFormats & WAVE_FORMAT_1S16 )
+ deviceInfo->defaultSampleRate = 11025.0;
+ else if( caps.dwFormats & WAVE_FORMAT_96S16 )
+ deviceInfo->defaultSampleRate = 96000.0;
+ else
+ deviceInfo->defaultSampleRate = 0.;
+ }
+ else if( caps.dwChannels == 1 )
+ {
+ if( caps.dwFormats & WAVE_FORMAT_4M16 )
+ deviceInfo->defaultSampleRate = 44100.0;
+ else if( caps.dwFormats & WAVE_FORMAT_48M16 )
+ deviceInfo->defaultSampleRate = 48000.0;
+ else if( caps.dwFormats & WAVE_FORMAT_2M16 )
+ deviceInfo->defaultSampleRate = 22050.0;
+ else if( caps.dwFormats & WAVE_FORMAT_1M16 )
+ deviceInfo->defaultSampleRate = 11025.0;
+ else if( caps.dwFormats & WAVE_FORMAT_96M16 )
+ deviceInfo->defaultSampleRate = 96000.0;
+ else
+ deviceInfo->defaultSampleRate = 0.;
+ }
+ else deviceInfo->defaultSampleRate = 0.;
+ }
+ }
+
+ IDirectSoundCapture_Release( lpDirectSoundCapture );
+ }
+
+ if( deviceOK )
+ {
+ deviceInfo->name = name;
+
+ if( lpGUID == NULL )
+ hostApi->info.defaultInputDevice = hostApi->info.deviceCount;
+
+ hostApi->info.deviceCount++;
+ }
+
+ return result;
+}
+
+
+/***********************************************************************************/
+PaError PaWinDs_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiIndex hostApiIndex )
+{
+ PaError result = paNoError;
+ int i, deviceCount;
+ PaWinDsHostApiRepresentation *winDsHostApi;
+ DSDeviceNameAndGUIDVector inputNamesAndGUIDs, outputNamesAndGUIDs;
+ PaDeviceInfo *deviceInfoArray;
+
+ HRESULT hr = CoInitialize(NULL); /** @todo: should uninitialize too */
+ if( FAILED(hr) ){
+ return paUnanticipatedHostError;
+ }
+
+ /* initialise guid vectors so they can be safely deleted on error */
+ inputNamesAndGUIDs.items = NULL;
+ outputNamesAndGUIDs.items = NULL;
+
+ DSW_InitializeDSoundEntryPoints();
+
+ winDsHostApi = (PaWinDsHostApiRepresentation*)PaUtil_AllocateMemory( sizeof(PaWinDsHostApiRepresentation) );
+ if( !winDsHostApi )
+ {
+ result = paInsufficientMemory;
+ goto error;
+ }
+
+ winDsHostApi->allocations = PaUtil_CreateAllocationGroup();
+ if( !winDsHostApi->allocations )
+ {
+ result = paInsufficientMemory;
+ goto error;
+ }
+
+ *hostApi = &winDsHostApi->inheritedHostApiRep;
+ (*hostApi)->info.structVersion = 1;
+ (*hostApi)->info.type = paDirectSound;
+ (*hostApi)->info.name = "Windows DirectSound";
+
+ (*hostApi)->info.deviceCount = 0;
+ (*hostApi)->info.defaultInputDevice = paNoDevice;
+ (*hostApi)->info.defaultOutputDevice = paNoDevice;
+
+
+/* DSound - enumerate devices to count them and to gather their GUIDs */
+
+
+ result = InitializeDSDeviceNameAndGUIDVector( &inputNamesAndGUIDs, winDsHostApi->allocations );
+ if( result != paNoError )
+ goto error;
+
+ result = InitializeDSDeviceNameAndGUIDVector( &outputNamesAndGUIDs, winDsHostApi->allocations );
+ if( result != paNoError )
+ goto error;
+
+ dswDSoundEntryPoints.DirectSoundCaptureEnumerate( (LPDSENUMCALLBACK)CollectGUIDsProc, (void *)&inputNamesAndGUIDs );
+
+ dswDSoundEntryPoints.DirectSoundEnumerate( (LPDSENUMCALLBACK)CollectGUIDsProc, (void *)&outputNamesAndGUIDs );
+
+ if( inputNamesAndGUIDs.enumerationError != paNoError )
+ {
+ result = inputNamesAndGUIDs.enumerationError;
+ goto error;
+ }
+
+ if( outputNamesAndGUIDs.enumerationError != paNoError )
+ {
+ result = outputNamesAndGUIDs.enumerationError;
+ goto error;
+ }
+
+ deviceCount = inputNamesAndGUIDs.count + outputNamesAndGUIDs.count;
+
+ if( deviceCount > 0 )
+ {
+ /* allocate array for pointers to PaDeviceInfo structs */
+ (*hostApi)->deviceInfos = (PaDeviceInfo**)PaUtil_GroupAllocateMemory(
+ winDsHostApi->allocations, sizeof(PaDeviceInfo*) * deviceCount );
+ if( !(*hostApi)->deviceInfos )
+ {
+ result = paInsufficientMemory;
+ goto error;
+ }
+
+ /* allocate all PaDeviceInfo structs in a contiguous block */
+ deviceInfoArray = (PaDeviceInfo*)PaUtil_GroupAllocateMemory(
+ winDsHostApi->allocations, sizeof(PaDeviceInfo) * deviceCount );
+ if( !deviceInfoArray )
+ {
+ result = paInsufficientMemory;
+ goto error;
+ }
+
+ /* allocate all DSound specific info structs in a contiguous block */
+ winDsHostApi->winDsDeviceInfos = (PaWinDsDeviceInfo*)PaUtil_GroupAllocateMemory(
+ winDsHostApi->allocations, sizeof(PaWinDsDeviceInfo) * deviceCount );
+ if( !winDsHostApi->winDsDeviceInfos )
+ {
+ result = paInsufficientMemory;
+ goto error;
+ }
+
+ for( i=0; i < deviceCount; ++i )
+ {
+ PaDeviceInfo *deviceInfo = &deviceInfoArray[i];
+ deviceInfo->structVersion = 2;
+ deviceInfo->hostApi = hostApiIndex;
+ deviceInfo->name = 0;
+ (*hostApi)->deviceInfos[i] = deviceInfo;
+ }
+
+ for( i=0; i< inputNamesAndGUIDs.count; ++i )
+ {
+ result = AddInputDeviceInfoFromDirectSoundCapture( winDsHostApi,
+ inputNamesAndGUIDs.items[i].name,
+ inputNamesAndGUIDs.items[i].lpGUID );
+ if( result != paNoError )
+ goto error;
+ }
+
+ for( i=0; i< outputNamesAndGUIDs.count; ++i )
+ {
+ result = AddOutputDeviceInfoFromDirectSound( winDsHostApi,
+ outputNamesAndGUIDs.items[i].name,
+ outputNamesAndGUIDs.items[i].lpGUID );
+ if( result != paNoError )
+ goto error;
+ }
+ }
+
+ result = TerminateDSDeviceNameAndGUIDVector( &inputNamesAndGUIDs );
+ if( result != paNoError )
+ goto error;
+
+ result = TerminateDSDeviceNameAndGUIDVector( &outputNamesAndGUIDs );
+ if( result != paNoError )
+ goto error;
+
+
+ (*hostApi)->Terminate = Terminate;
+ (*hostApi)->OpenStream = OpenStream;
+ (*hostApi)->IsFormatSupported = IsFormatSupported;
+
+ PaUtil_InitializeStreamInterface( &winDsHostApi->callbackStreamInterface, CloseStream, StartStream,
+ StopStream, AbortStream, IsStreamStopped, IsStreamActive,
+ GetStreamTime, GetStreamCpuLoad,
+ PaUtil_DummyRead, PaUtil_DummyWrite,
+ PaUtil_DummyGetReadAvailable, PaUtil_DummyGetWriteAvailable );
+
+ PaUtil_InitializeStreamInterface( &winDsHostApi->blockingStreamInterface, CloseStream, StartStream,
+ StopStream, AbortStream, IsStreamStopped, IsStreamActive,
+ GetStreamTime, PaUtil_DummyGetCpuLoad,
+ ReadStream, WriteStream, GetStreamReadAvailable, GetStreamWriteAvailable );
+
+ return result;
+
+error:
+ if( winDsHostApi )
+ {
+ if( winDsHostApi->allocations )
+ {
+ PaUtil_FreeAllAllocations( winDsHostApi->allocations );
+ PaUtil_DestroyAllocationGroup( winDsHostApi->allocations );
+ }
+
+ PaUtil_FreeMemory( winDsHostApi );
+ }
+
+ TerminateDSDeviceNameAndGUIDVector( &inputNamesAndGUIDs );
+ TerminateDSDeviceNameAndGUIDVector( &outputNamesAndGUIDs );
+
+ return result;
+}
+
+
+/***********************************************************************************/
+static void Terminate( struct PaUtilHostApiRepresentation *hostApi )
+{
+ PaWinDsHostApiRepresentation *winDsHostApi = (PaWinDsHostApiRepresentation*)hostApi;
+
+ /*
+ IMPLEMENT ME:
+ - clean up any resources not handled by the allocation group
+ */
+
+ if( winDsHostApi->allocations )
+ {
+ PaUtil_FreeAllAllocations( winDsHostApi->allocations );
+ PaUtil_DestroyAllocationGroup( winDsHostApi->allocations );
+ }
+
+ PaUtil_FreeMemory( winDsHostApi );
+
+ DSW_TerminateDSoundEntryPoints();
+
+ CoUninitialize();
+}
+
+
+/* Set minimal latency based on whether NT or Win95.
+ * NT has higher latency.
+ */
+static int PaWinDS_GetMinSystemLatency( void )
+{
+ int minLatencyMsec;
+ /* Set minimal latency based on whether NT or other OS.
+ * NT has higher latency.
+ */
+ OSVERSIONINFO osvi;
+ osvi.dwOSVersionInfoSize = sizeof( osvi );
+ GetVersionEx( &osvi );
+ DBUG(("PA - PlatformId = 0x%x\n", osvi.dwPlatformId ));
+ DBUG(("PA - MajorVersion = 0x%x\n", osvi.dwMajorVersion ));
+ DBUG(("PA - MinorVersion = 0x%x\n", osvi.dwMinorVersion ));
+ /* Check for NT */
+ if( (osvi.dwMajorVersion == 4) && (osvi.dwPlatformId == 2) )
+ {
+ minLatencyMsec = PA_WIN_NT_LATENCY;
+ }
+ else if(osvi.dwMajorVersion >= 5)
+ {
+ minLatencyMsec = PA_WIN_WDM_LATENCY;
+ }
+ else
+ {
+ minLatencyMsec = PA_WIN_9X_LATENCY;
+ }
+ return minLatencyMsec;
+}
+
+/***********************************************************************************/
+static PaError IsFormatSupported( struct PaUtilHostApiRepresentation *hostApi,
+ const PaStreamParameters *inputParameters,
+ const PaStreamParameters *outputParameters,
+ double sampleRate )
+{
+ int inputChannelCount, outputChannelCount;
+ PaSampleFormat inputSampleFormat, outputSampleFormat;
+
+ if( inputParameters )
+ {
+ inputChannelCount = inputParameters->channelCount;
+ inputSampleFormat = inputParameters->sampleFormat;
+
+ /* unless alternate device specification is supported, reject the use of
+ paUseHostApiSpecificDeviceSpecification */
+
+ if( inputParameters->device == paUseHostApiSpecificDeviceSpecification )
+ return paInvalidDevice;
+
+ /* check that input device can support inputChannelCount */
+ if( inputChannelCount > hostApi->deviceInfos[ inputParameters->device ]->maxInputChannels )
+ return paInvalidChannelCount;
+
+ /* validate inputStreamInfo */
+ if( inputParameters->hostApiSpecificStreamInfo )
+ return paIncompatibleHostApiSpecificStreamInfo; /* this implementation doesn't use custom stream info */
+ }
+ else
+ {
+ inputChannelCount = 0;
+ }
+
+ if( outputParameters )
+ {
+ outputChannelCount = outputParameters->channelCount;
+ outputSampleFormat = outputParameters->sampleFormat;
+
+ /* unless alternate device specification is supported, reject the use of
+ paUseHostApiSpecificDeviceSpecification */
+
+ if( outputParameters->device == paUseHostApiSpecificDeviceSpecification )
+ return paInvalidDevice;
+
+ /* check that output device can support inputChannelCount */
+ if( outputChannelCount > hostApi->deviceInfos[ outputParameters->device ]->maxOutputChannels )
+ return paInvalidChannelCount;
+
+ /* validate outputStreamInfo */
+ if( outputParameters->hostApiSpecificStreamInfo )
+ return paIncompatibleHostApiSpecificStreamInfo; /* this implementation doesn't use custom stream info */
+ }
+ else
+ {
+ outputChannelCount = 0;
+ }
+
+ /*
+ IMPLEMENT ME:
+
+ - if a full duplex stream is requested, check that the combination
+ of input and output parameters is supported if necessary
+
+ - check that the device supports sampleRate
+
+ Because the buffer adapter handles conversion between all standard
+ sample formats, the following checks are only required if paCustomFormat
+ is implemented, or under some other unusual conditions.
+
+ - check that input device can support inputSampleFormat, or that
+ we have the capability to convert from outputSampleFormat to
+ a native format
+
+ - check that output device can support outputSampleFormat, or that
+ we have the capability to convert from outputSampleFormat to
+ a native format
+ */
+
+ return paFormatIsSupported;
+}
+
+
+/*************************************************************************
+** Determine minimum number of buffers required for this host based
+** on minimum latency. Latency can be optionally set by user by setting
+** an environment variable. For example, to set latency to 200 msec, put:
+**
+** set PA_MIN_LATENCY_MSEC=200
+**
+** in the AUTOEXEC.BAT file and reboot.
+** If the environment variable is not set, then the latency will be determined
+** based on the OS. Windows NT has higher latency than Win95.
+*/
+#define PA_LATENCY_ENV_NAME ("PA_MIN_LATENCY_MSEC")
+#define PA_ENV_BUF_SIZE (32)
+
+static int PaWinDs_GetMinLatencyFrames( double sampleRate )
+{
+ char envbuf[PA_ENV_BUF_SIZE];
+ DWORD hresult;
+ int minLatencyMsec = 0;
+
+ /* Let user determine minimal latency by setting environment variable. */
+ hresult = GetEnvironmentVariable( PA_LATENCY_ENV_NAME, envbuf, PA_ENV_BUF_SIZE );
+ if( (hresult > 0) && (hresult < PA_ENV_BUF_SIZE) )
+ {
+ minLatencyMsec = atoi( envbuf );
+ }
+ else
+ {
+ minLatencyMsec = PaWinDS_GetMinSystemLatency();
+#if PA_USE_HIGH_LATENCY
+ PRINT(("PA - Minimum Latency set to %d msec!\n", minLatencyMsec ));
+#endif
+
+ }
+
+ return (int) (minLatencyMsec * sampleRate * SECONDS_PER_MSEC);
+}
+
+#ifndef NDEBUG
+#define EZ = 0
+#else
+#define EZ
+#endif
+
+/***********************************************************************************/
+/* see pa_hostapi.h for a list of validity guarantees made about OpenStream parameters */
+
+static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi,
+ PaStream** s,
+ const PaStreamParameters *inputParameters,
+ const PaStreamParameters *outputParameters,
+ double sampleRate,
+ unsigned long framesPerBuffer,
+ PaStreamFlags streamFlags,
+ PaStreamCallback *streamCallback,
+ void *userData )
+{
+ PaError result = paNoError;
+ PaWinDsHostApiRepresentation *winDsHostApi = (PaWinDsHostApiRepresentation*)hostApi;
+ PaWinDsStream *stream = 0;
+ int inputChannelCount, outputChannelCount;
+ PaSampleFormat inputSampleFormat EZ, outputSampleFormat EZ;
+ PaSampleFormat hostInputSampleFormat EZ, hostOutputSampleFormat EZ;
+ unsigned long suggestedInputLatencyFrames, suggestedOutputLatencyFrames;
+
+ if( inputParameters )
+ {
+ inputChannelCount = inputParameters->channelCount;
+ inputSampleFormat = inputParameters->sampleFormat;
+ suggestedInputLatencyFrames = (unsigned long)(inputParameters->suggestedLatency * sampleRate);
+
+ /* IDEA: the following 3 checks could be performed by default by pa_front
+ unless some flag indicated otherwise */
+
+ /* unless alternate device specification is supported, reject the use of
+ paUseHostApiSpecificDeviceSpecification */
+ if( inputParameters->device == paUseHostApiSpecificDeviceSpecification )
+ return paInvalidDevice;
+
+ /* check that input device can support inputChannelCount */
+ if( inputChannelCount > hostApi->deviceInfos[ inputParameters->device ]->maxInputChannels )
+ return paInvalidChannelCount;
+
+ /* validate hostApiSpecificStreamInfo */
+ if( inputParameters->hostApiSpecificStreamInfo )
+ return paIncompatibleHostApiSpecificStreamInfo; /* this implementation doesn't use custom stream info */
+ }
+ else
+ {
+ inputChannelCount = 0;
+ suggestedInputLatencyFrames = 0;
+ }
+
+
+ if( outputParameters )
+ {
+ outputChannelCount = outputParameters->channelCount;
+ outputSampleFormat = outputParameters->sampleFormat;
+ suggestedOutputLatencyFrames = (unsigned long)(outputParameters->suggestedLatency * sampleRate);
+
+ /* unless alternate device specification is supported, reject the use of
+ paUseHostApiSpecificDeviceSpecification */
+ if( outputParameters->device == paUseHostApiSpecificDeviceSpecification )
+ return paInvalidDevice;
+
+ /* check that output device can support inputChannelCount */
+ if( outputChannelCount > hostApi->deviceInfos[ outputParameters->device ]->maxOutputChannels )
+ return paInvalidChannelCount;
+
+ /* validate hostApiSpecificStreamInfo */
+ if( outputParameters->hostApiSpecificStreamInfo )
+ return paIncompatibleHostApiSpecificStreamInfo; /* this implementation doesn't use custom stream info */
+ }
+ else
+ {
+ outputChannelCount = 0;
+ suggestedOutputLatencyFrames = 0;
+ }
+
+
+ /*
+ IMPLEMENT ME:
+
+ ( the following two checks are taken care of by PaUtil_InitializeBufferProcessor() )
+
+ - check that input device can support inputSampleFormat, or that
+ we have the capability to convert from outputSampleFormat to
+ a native format
+
+ - check that output device can support outputSampleFormat, or that
+ we have the capability to convert from outputSampleFormat to
+ a native format
+
+ - if a full duplex stream is requested, check that the combination
+ of input and output parameters is supported
+
+ - check that the device supports sampleRate
+
+ - alter sampleRate to a close allowable rate if possible / necessary
+
+ - validate suggestedInputLatency and suggestedOutputLatency parameters,
+ use default values where necessary
+ */
+
+
+ /* validate platform specific flags */
+ if( (streamFlags & paPlatformSpecificFlags) != 0 )
+ return paInvalidFlag; /* unexpected platform specific flag */
+
+
+ stream = (PaWinDsStream*)PaUtil_AllocateMemory( sizeof(PaWinDsStream) );
+ if( !stream )
+ {
+ result = paInsufficientMemory;
+ goto error;
+ }
+
+ if( streamCallback )
+ {
+ PaUtil_InitializeStreamRepresentation( &stream->streamRepresentation,
+ &winDsHostApi->callbackStreamInterface, streamCallback, userData );
+ }
+ else
+ {
+ PaUtil_InitializeStreamRepresentation( &stream->streamRepresentation,
+ &winDsHostApi->blockingStreamInterface, streamCallback, userData );
+ }
+
+ PaUtil_InitializeCpuLoadMeasurer( &stream->cpuLoadMeasurer, sampleRate );
+
+
+ if( inputParameters )
+ {
+ /* IMPLEMENT ME - establish which host formats are available */
+ hostInputSampleFormat =
+ PaUtil_SelectClosestAvailableFormat( paInt16 /* native formats */, inputParameters->sampleFormat );
+ }
+
+ if( outputParameters )
+ {
+ /* IMPLEMENT ME - establish which host formats are available */
+ hostOutputSampleFormat =
+ PaUtil_SelectClosestAvailableFormat( paInt16 /* native formats */, outputParameters->sampleFormat );
+ }
+
+ result = PaUtil_InitializeBufferProcessor( &stream->bufferProcessor,
+ inputChannelCount, inputSampleFormat, hostInputSampleFormat,
+ outputChannelCount, outputSampleFormat, hostOutputSampleFormat,
+ sampleRate, streamFlags, framesPerBuffer,
+ framesPerBuffer, /* ignored in paUtilVariableHostBufferSizePartialUsageAllowed mode. */
+ /* This next mode is required because DS can split the host buffer when it wraps around. */
+ paUtilVariableHostBufferSizePartialUsageAllowed,
+ streamCallback, userData );
+ if( result != paNoError )
+ goto error;
+
+
+ stream->streamRepresentation.streamInfo.inputLatency =
+ PaUtil_GetBufferProcessorInputLatency(&stream->bufferProcessor); /* FIXME: not initialised anywhere else */
+ stream->streamRepresentation.streamInfo.outputLatency =
+ PaUtil_GetBufferProcessorOutputLatency(&stream->bufferProcessor); /* FIXME: not initialised anywhere else */
+ stream->streamRepresentation.streamInfo.sampleRate = sampleRate;
+
+
+/* DirectSound specific initialization */
+ {
+ HRESULT hr;
+ int bytesPerDirectSoundBuffer;
+ DSoundWrapper *dsw;
+ int userLatencyFrames;
+ int minLatencyFrames;
+
+ stream->timerID = 0;
+ dsw = &stream->directSoundWrapper;
+ DSW_Init( dsw );
+
+ /* Get system minimum latency. */
+ minLatencyFrames = PaWinDs_GetMinLatencyFrames( sampleRate );
+
+ /* Let user override latency by passing latency parameter. */
+ userLatencyFrames = (suggestedInputLatencyFrames > suggestedOutputLatencyFrames)
+ ? suggestedInputLatencyFrames
+ : suggestedOutputLatencyFrames;
+ if( userLatencyFrames > 0 ) minLatencyFrames = userLatencyFrames;
+
+ /* Calculate stream->framesPerDSBuffer depending on framesPerBuffer */
+ if( framesPerBuffer == paFramesPerBufferUnspecified )
+ {
+ /* App support variable framesPerBuffer */
+ stream->framesPerDSBuffer = minLatencyFrames;
+
+ stream->streamRepresentation.streamInfo.outputLatency = (double)(minLatencyFrames - 1) / sampleRate;
+ }
+ else
+ {
+ /* Round up to number of buffers needed to guarantee that latency. */
+ int numUserBuffers = (minLatencyFrames + framesPerBuffer - 1) / framesPerBuffer;
+ if( numUserBuffers < 1 ) numUserBuffers = 1;
+ numUserBuffers += 1; /* So we have latency worth of buffers ahead of current buffer. */
+ stream->framesPerDSBuffer = framesPerBuffer * numUserBuffers;
+
+ stream->streamRepresentation.streamInfo.outputLatency = (double)(framesPerBuffer * (numUserBuffers-1)) / sampleRate;
+ }
+
+ {
+ /** @todo REVIEW: this calculation seems incorrect to me - rossb. */
+ int msecLatency = (int) ((stream->framesPerDSBuffer * MSEC_PER_SECOND) / sampleRate);
+ PRINT(("PortAudio on DirectSound - Latency = %d frames, %d msec\n", stream->framesPerDSBuffer, msecLatency ));
+ }
+
+
+ /* ------------------ OUTPUT */
+ if( outputParameters )
+ {
+ /*
+ PaDeviceInfo *deviceInfo = hostApi->deviceInfos[ outputParameters->device ];
+ DBUG(("PaHost_OpenStream: deviceID = 0x%x\n", outputParameters->device));
+ */
+
+ bytesPerDirectSoundBuffer = stream->framesPerDSBuffer * outputParameters->channelCount * sizeof(short);
+ if( bytesPerDirectSoundBuffer < DSBSIZE_MIN )
+ {
+ result = paBufferTooSmall;
+ goto error;
+ }
+ else if( bytesPerDirectSoundBuffer > DSBSIZE_MAX )
+ {
+ result = paBufferTooBig;
+ goto error;
+ }
+
+
+ hr = dswDSoundEntryPoints.DirectSoundCreate( winDsHostApi->winDsDeviceInfos[outputParameters->device].lpGUID,
+ &dsw->dsw_pDirectSound, NULL );
+ if( hr != DS_OK )
+ {
+ ERR_RPT(("PortAudio: DirectSoundCreate() failed!\n"));
+ result = paUnanticipatedHostError;
+ PA_DS_SET_LAST_DIRECTSOUND_ERROR( hr );
+ goto error;
+ }
+ hr = DSW_InitOutputBuffer( dsw,
+ (unsigned long) (sampleRate + 0.5),
+ (WORD)outputParameters->channelCount, bytesPerDirectSoundBuffer );
+ DBUG(("DSW_InitOutputBuffer() returns %x\n", hr));
+ if( hr != DS_OK )
+ {
+ result = paUnanticipatedHostError;
+ PA_DS_SET_LAST_DIRECTSOUND_ERROR( hr );
+ goto error;
+ }
+ /* Calculate value used in latency calculation to avoid real-time divides. */
+ stream->secondsPerHostByte = 1.0 /
+ (stream->bufferProcessor.bytesPerHostOutputSample *
+ outputChannelCount * sampleRate);
+ }
+
+ /* ------------------ INPUT */
+ if( inputParameters )
+ {
+ /*
+ PaDeviceInfo *deviceInfo = hostApi->deviceInfos[ inputParameters->device ];
+ DBUG(("PaHost_OpenStream: deviceID = 0x%x\n", inputParameters->device));
+ */
+
+ bytesPerDirectSoundBuffer = stream->framesPerDSBuffer * inputParameters->channelCount * sizeof(short);
+ if( bytesPerDirectSoundBuffer < DSBSIZE_MIN )
+ {
+ result = paBufferTooSmall;
+ goto error;
+ }
+ else if( bytesPerDirectSoundBuffer > DSBSIZE_MAX )
+ {
+ result = paBufferTooBig;
+ goto error;
+ }
+
+ hr = dswDSoundEntryPoints.DirectSoundCaptureCreate( winDsHostApi->winDsDeviceInfos[inputParameters->device].lpGUID,
+ &dsw->dsw_pDirectSoundCapture, NULL );
+ if( hr != DS_OK )
+ {
+ ERR_RPT(("PortAudio: DirectSoundCaptureCreate() failed!\n"));
+ result = paUnanticipatedHostError;
+ PA_DS_SET_LAST_DIRECTSOUND_ERROR( hr );
+ goto error;
+ }
+ hr = DSW_InitInputBuffer( dsw,
+ (unsigned long) (sampleRate + 0.5),
+ (WORD)inputParameters->channelCount, bytesPerDirectSoundBuffer );
+ DBUG(("DSW_InitInputBuffer() returns %x\n", hr));
+ if( hr != DS_OK )
+ {
+ ERR_RPT(("PortAudio: DSW_InitInputBuffer() returns %x\n", hr));
+ result = paUnanticipatedHostError;
+ PA_DS_SET_LAST_DIRECTSOUND_ERROR( hr );
+ goto error;
+ }
+ }
+
+ }
+
+ *s = (PaStream*)stream;
+
+ return result;
+
+error:
+ if( stream )
+ PaUtil_FreeMemory( stream );
+
+ return result;
+}
+
+
+/***********************************************************************************/
+static PaError Pa_TimeSlice( PaWinDsStream *stream )
+{
+ PaError result = 0; /* FIXME: this should be declared int and this function should also return that type (same as stream callback return type)*/
+ DSoundWrapper *dsw;
+ long numFrames = 0;
+ long bytesEmpty = 0;
+ long bytesFilled = 0;
+ long bytesToXfer = 0;
+ long framesToXfer = 0;
+ long numInFramesReady = 0;
+ long numOutFramesReady = 0;
+ long bytesProcessed;
+ HRESULT hresult;
+ double outputLatency = 0;
+ PaStreamCallbackTimeInfo timeInfo = {0,0,0}; /** @todo implement inputBufferAdcTime */
+
+/* Input */
+ LPBYTE lpInBuf1 = NULL;
+ LPBYTE lpInBuf2 = NULL;
+ DWORD dwInSize1 = 0;
+ DWORD dwInSize2 = 0;
+/* Output */
+ LPBYTE lpOutBuf1 = NULL;
+ LPBYTE lpOutBuf2 = NULL;
+ DWORD dwOutSize1 = 0;
+ DWORD dwOutSize2 = 0;
+
+ dsw = &stream->directSoundWrapper;
+
+ /* How much input data is available? */
+ if( stream->bufferProcessor.inputChannelCount > 0 )
+ {
+ DSW_QueryInputFilled( dsw, &bytesFilled );
+ framesToXfer = numInFramesReady = bytesFilled / dsw->dsw_BytesPerInputFrame;
+ outputLatency = ((double)bytesFilled) * stream->secondsPerHostByte;
+
+ /** @todo Check for overflow */
+ }
+
+ /* How much output room is available? */
+ if( stream->bufferProcessor.outputChannelCount > 0 )
+ {
+ UINT previousUnderflowCount = dsw->dsw_OutputUnderflows;
+ DSW_QueryOutputSpace( dsw, &bytesEmpty );
+ framesToXfer = numOutFramesReady = bytesEmpty / dsw->dsw_BytesPerOutputFrame;
+
+ /* Check for underflow */
+ if( dsw->dsw_OutputUnderflows != previousUnderflowCount )
+ stream->callbackFlags |= paOutputUnderflow;
+ }
+
+ if( (numInFramesReady > 0) && (numOutFramesReady > 0) )
+ {
+ framesToXfer = (numOutFramesReady < numInFramesReady) ? numOutFramesReady : numInFramesReady;
+ }
+
+ if( framesToXfer > 0 )
+ {
+
+ PaUtil_BeginCpuLoadMeasurement( &stream->cpuLoadMeasurer );
+
+ /* The outputBufferDacTime parameter should indicates the time at which
+ the first sample of the output buffer is heard at the DACs. */
+ timeInfo.currentTime = PaUtil_GetTime();
+ timeInfo.outputBufferDacTime = timeInfo.currentTime + outputLatency;
+
+
+ PaUtil_BeginBufferProcessing( &stream->bufferProcessor, &timeInfo, stream->callbackFlags );
+ stream->callbackFlags = 0;
+
+ /* Input */
+ if( stream->bufferProcessor.inputChannelCount > 0 )
+ {
+ bytesToXfer = framesToXfer * dsw->dsw_BytesPerInputFrame;
+ hresult = IDirectSoundCaptureBuffer_Lock ( dsw->dsw_InputBuffer,
+ dsw->dsw_ReadOffset, bytesToXfer,
+ (void **) &lpInBuf1, &dwInSize1,
+ (void **) &lpInBuf2, &dwInSize2, 0);
+ if (hresult != DS_OK)
+ {
+ ERR_RPT(("DirectSound IDirectSoundCaptureBuffer_Lock failed, hresult = 0x%x\n",hresult));
+ result = paUnanticipatedHostError;
+ PA_DS_SET_LAST_DIRECTSOUND_ERROR( hresult );
+ goto error2;
+ }
+
+ numFrames = dwInSize1 / dsw->dsw_BytesPerInputFrame;
+ PaUtil_SetInputFrameCount( &stream->bufferProcessor, numFrames );
+ PaUtil_SetInterleavedInputChannels( &stream->bufferProcessor, 0, lpInBuf1, 0 );
+ /* Is input split into two regions. */
+ if( dwInSize2 > 0 )
+ {
+ numFrames = dwInSize2 / dsw->dsw_BytesPerInputFrame;
+ PaUtil_Set2ndInputFrameCount( &stream->bufferProcessor, numFrames );
+ PaUtil_Set2ndInterleavedInputChannels( &stream->bufferProcessor, 0, lpInBuf2, 0 );
+ }
+ }
+
+ /* Output */
+ if( stream->bufferProcessor.outputChannelCount > 0 )
+ {
+ bytesToXfer = framesToXfer * dsw->dsw_BytesPerOutputFrame;
+ hresult = IDirectSoundBuffer_Lock ( dsw->dsw_OutputBuffer,
+ dsw->dsw_WriteOffset, bytesToXfer,
+ (void **) &lpOutBuf1, &dwOutSize1,
+ (void **) &lpOutBuf2, &dwOutSize2, 0);
+ if (hresult != DS_OK)
+ {
+ ERR_RPT(("DirectSound IDirectSoundBuffer_Lock failed, hresult = 0x%x\n",hresult));
+ result = paUnanticipatedHostError;
+ PA_DS_SET_LAST_DIRECTSOUND_ERROR( hresult );
+ goto error1;
+ }
+
+ numFrames = dwOutSize1 / dsw->dsw_BytesPerOutputFrame;
+ PaUtil_SetOutputFrameCount( &stream->bufferProcessor, numFrames );
+ PaUtil_SetInterleavedOutputChannels( &stream->bufferProcessor, 0, lpOutBuf1, 0 );
+
+ /* Is output split into two regions. */
+ if( dwOutSize2 > 0 )
+ {
+ numFrames = dwOutSize2 / dsw->dsw_BytesPerOutputFrame;
+ PaUtil_Set2ndOutputFrameCount( &stream->bufferProcessor, numFrames );
+ PaUtil_Set2ndInterleavedOutputChannels( &stream->bufferProcessor, 0, lpOutBuf2, 0 );
+ }
+ }
+
+ result = paContinue;
+ numFrames = PaUtil_EndBufferProcessing( &stream->bufferProcessor, &result );
+ stream->framesWritten += numFrames;
+
+ if( stream->bufferProcessor.outputChannelCount > 0 )
+ {
+ /* FIXME: an underflow could happen here */
+
+ /* Update our buffer offset and unlock sound buffer */
+ bytesProcessed = numFrames * dsw->dsw_BytesPerOutputFrame;
+ dsw->dsw_WriteOffset = (dsw->dsw_WriteOffset + bytesProcessed) % dsw->dsw_OutputSize;
+ IDirectSoundBuffer_Unlock( dsw->dsw_OutputBuffer, lpOutBuf1, dwOutSize1, lpOutBuf2, dwOutSize2);
+ dsw->dsw_FramesWritten += numFrames;
+ }
+
+error1:
+ if( stream->bufferProcessor.inputChannelCount > 0 )
+ {
+ /* FIXME: an overflow could happen here */
+
+ /* Update our buffer offset and unlock sound buffer */
+ bytesProcessed = numFrames * dsw->dsw_BytesPerInputFrame;
+ dsw->dsw_ReadOffset = (dsw->dsw_ReadOffset + bytesProcessed) % dsw->dsw_InputSize;
+ IDirectSoundCaptureBuffer_Unlock( dsw->dsw_InputBuffer, lpInBuf1, dwInSize1, lpInBuf2, dwInSize2);
+ }
+error2:
+
+ PaUtil_EndCpuLoadMeasurement( &stream->cpuLoadMeasurer, numFrames );
+
+ }
+
+ return result;
+}
+/*******************************************************************/
+static void CALLBACK Pa_TimerCallback(UINT uID, UINT uMsg, DWORD dwUser, DWORD dw1, DWORD dw2)
+{
+ PaWinDsStream *stream;
+
+ /* suppress unused variable warnings */
+ (void) uID;
+ (void) uMsg;
+ (void) dw1;
+ (void) dw2;
+
+ stream = (PaWinDsStream *) dwUser;
+ if( stream == NULL ) return;
+
+ if( stream->isActive )
+ {
+ if( stream->abortProcessing )
+ {
+ stream->isActive = 0;
+ }
+ else if( stream->stopProcessing )
+ {
+ DSoundWrapper *dsw = &stream->directSoundWrapper;
+ if( stream->bufferProcessor.outputChannelCount > 0 )
+ {
+ DSW_ZeroEmptySpace( dsw );
+ /* clear isActive when all sound played */
+ if( dsw->dsw_FramesPlayed >= stream->framesWritten )
+ {
+ stream->isActive = 0;
+ }
+ }
+ else
+ {
+ stream->isActive = 0;
+ }
+ }
+ else
+ {
+ if( Pa_TimeSlice( stream ) != 0) /* Call time slice independant of timing method. */
+ {
+ /* FIXME implement handling of paComplete and paAbort if possible */
+ stream->stopProcessing = 1;
+ }
+ }
+
+ if( !stream->isActive ){
+ if( stream->streamRepresentation.streamFinishedCallback != 0 )
+ stream->streamRepresentation.streamFinishedCallback( stream->streamRepresentation.userData );
+ }
+ }
+}
+
+/***********************************************************************************
+ When CloseStream() is called, the multi-api layer ensures that
+ the stream has already been stopped or aborted.
+*/
+static PaError CloseStream( PaStream* s )
+{
+ PaError result = paNoError;
+ PaWinDsStream *stream = (PaWinDsStream*)s;
+
+ DSW_Term( &stream->directSoundWrapper );
+
+ PaUtil_TerminateBufferProcessor( &stream->bufferProcessor );
+ PaUtil_TerminateStreamRepresentation( &stream->streamRepresentation );
+ PaUtil_FreeMemory( stream );
+
+ return result;
+}
+
+/***********************************************************************************/
+static PaError StartStream( PaStream *s )
+{
+ PaError result = paNoError;
+ PaWinDsStream *stream = (PaWinDsStream*)s;
+ HRESULT hr;
+
+ PaUtil_ResetBufferProcessor( &stream->bufferProcessor );
+
+ if( stream->bufferProcessor.inputChannelCount > 0 )
+ {
+ hr = DSW_StartInput( &stream->directSoundWrapper );
+ DBUG(("StartStream: DSW_StartInput returned = 0x%X.\n", hr));
+ if( hr != DS_OK )
+ {
+ result = paUnanticipatedHostError;
+ PA_DS_SET_LAST_DIRECTSOUND_ERROR( hr );
+ goto error;
+ }
+ }
+
+ stream->framesWritten = 0;
+ stream->callbackFlags = 0;
+
+ stream->abortProcessing = 0;
+ stream->stopProcessing = 0;
+ stream->isActive = 1;
+
+ if( stream->bufferProcessor.outputChannelCount > 0 )
+ {
+ /* Give user callback a chance to pre-fill buffer. REVIEW - i thought we weren't pre-filling, rb. */
+ result = Pa_TimeSlice( stream );
+ if( result != paNoError ) return result; // FIXME - what if finished?
+
+ hr = DSW_StartOutput( &stream->directSoundWrapper );
+ DBUG(("PaHost_StartOutput: DSW_StartOutput returned = 0x%X.\n", hr));
+ if( hr != DS_OK )
+ {
+ result = paUnanticipatedHostError;
+ PA_DS_SET_LAST_DIRECTSOUND_ERROR( hr );
+ goto error;
+ }
+ }
+
+
+ /* Create timer that will wake us up so we can fill the DSound buffer. */
+ {
+ int resolution;
+ int framesPerWakeup = stream->framesPerDSBuffer / 4;
+ int msecPerWakeup = MSEC_PER_SECOND * framesPerWakeup / (int) stream->streamRepresentation.streamInfo.sampleRate;
+ if( msecPerWakeup < 10 ) msecPerWakeup = 10;
+ else if( msecPerWakeup > 100 ) msecPerWakeup = 100;
+ resolution = msecPerWakeup/4;
+ stream->timerID = timeSetEvent( msecPerWakeup, resolution, (LPTIMECALLBACK) Pa_TimerCallback,
+ (DWORD) stream, TIME_PERIODIC );
+ }
+ if( stream->timerID == 0 )
+ {
+ stream->isActive = 0;
+ result = paUnanticipatedHostError;
+ PA_DS_SET_LAST_DIRECTSOUND_ERROR( hr );
+ goto error;
+ }
+
+ stream->isStarted = TRUE;
+
+error:
+ return result;
+}
+
+
+/***********************************************************************************/
+static PaError StopStream( PaStream *s )
+{
+ PaError result = paNoError;
+ PaWinDsStream *stream = (PaWinDsStream*)s;
+ HRESULT hr;
+ int timeoutMsec;
+
+ stream->stopProcessing = 1;
+ /* Set timeout at 20% beyond maximum time we might wait. */
+ timeoutMsec = (int) (1200.0 * stream->framesPerDSBuffer / stream->streamRepresentation.streamInfo.sampleRate);
+ while( stream->isActive && (timeoutMsec > 0) )
+ {
+ Sleep(10);
+ timeoutMsec -= 10;
+ }
+ if( stream->timerID != 0 )
+ {
+ timeKillEvent(stream->timerID); /* Stop callback timer. */
+ stream->timerID = 0;
+ }
+
+
+ if( stream->bufferProcessor.outputChannelCount > 0 )
+ {
+ hr = DSW_StopOutput( &stream->directSoundWrapper );
+ }
+
+ if( stream->bufferProcessor.inputChannelCount > 0 )
+ {
+ hr = DSW_StopInput( &stream->directSoundWrapper );
+ }
+
+ stream->isStarted = FALSE;
+
+ return result;
+}
+
+
+/***********************************************************************************/
+static PaError AbortStream( PaStream *s )
+{
+ PaWinDsStream *stream = (PaWinDsStream*)s;
+
+ stream->abortProcessing = 1;
+ return StopStream( s );
+}
+
+
+/***********************************************************************************/
+static PaError IsStreamStopped( PaStream *s )
+{
+ PaWinDsStream *stream = (PaWinDsStream*)s;
+
+ return !stream->isStarted;
+}
+
+
+/***********************************************************************************/
+static PaError IsStreamActive( PaStream *s )
+{
+ PaWinDsStream *stream = (PaWinDsStream*)s;
+
+ return stream->isActive;
+}
+
+/***********************************************************************************/
+static PaTime GetStreamTime( PaStream *s )
+{
+ /* suppress unused variable warnings */
+ (void) s;
+
+
+/*
+ new behavior for GetStreamTime is to return a stream based seconds clock
+ used for the outTime parameter to the callback.
+ FIXME: delete this comment when the other unnecessary related code has
+ been cleaned from this file.
+
+ PaWinDsStream *stream = (PaWinDsStream*)s;
+ DSoundWrapper *dsw;
+ dsw = &stream->directSoundWrapper;
+ return dsw->dsw_FramesPlayed;
+*/
+ return PaUtil_GetTime();
+}
+
+
+/***********************************************************************************/
+static double GetStreamCpuLoad( PaStream* s )
+{
+ PaWinDsStream *stream = (PaWinDsStream*)s;
+
+ return PaUtil_GetCpuLoad( &stream->cpuLoadMeasurer );
+}
+
+
+
+/***********************************************************************************
+ As separate stream interfaces are used for blocking and callback
+ streams, the following functions can be guaranteed to only be called
+ for blocking streams.
+*/
+
+static PaError ReadStream( PaStream* s,
+ void *buffer,
+ unsigned long frames )
+{
+ PaWinDsStream *stream = (PaWinDsStream*)s;
+
+ /* suppress unused variable warnings */
+ (void) buffer;
+ (void) frames;
+ (void) stream;
+
+ /* IMPLEMENT ME, see portaudio.h for required behavior*/
+
+ return paNoError;
+}
+
+
+/***********************************************************************************/
+static PaError WriteStream( PaStream* s,
+ const void *buffer,
+ unsigned long frames )
+{
+ PaWinDsStream *stream = (PaWinDsStream*)s;
+
+ /* suppress unused variable warnings */
+ (void) buffer;
+ (void) frames;
+ (void) stream;
+
+ /* IMPLEMENT ME, see portaudio.h for required behavior*/
+
+ return paNoError;
+}
+
+
+/***********************************************************************************/
+static signed long GetStreamReadAvailable( PaStream* s )
+{
+ PaWinDsStream *stream = (PaWinDsStream*)s;
+
+ /* suppress unused variable warnings */
+ (void) stream;
+
+ /* IMPLEMENT ME, see portaudio.h for required behavior*/
+
+ return 0;
+}
+
+
+/***********************************************************************************/
+static signed long GetStreamWriteAvailable( PaStream* s )
+{
+ PaWinDsStream *stream = (PaWinDsStream*)s;
+
+ /* suppress unused variable warnings */
+ (void) stream;
+
+ /* IMPLEMENT ME, see portaudio.h for required behavior*/
+
+ return 0;
+}
+
+
+
diff --git a/pjmedia/src/pjmedia/portaudio/pa_win_hostapis.c b/pjmedia/src/pjmedia/portaudio/pa_win_hostapis.c
index 4b6d1dc2..4e07fee2 100644
--- a/pjmedia/src/pjmedia/portaudio/pa_win_hostapis.c
+++ b/pjmedia/src/pjmedia/portaudio/pa_win_hostapis.c
@@ -1,86 +1,86 @@
-/*
- * $Id: pa_win_hostapis.c,v 1.1.2.10 2004/09/08 17:31:37 rossbencina Exp $
- * Portable Audio I/O Library Windows initialization table
- *
- * Based on the Open Source API proposed by Ross Bencina
- * Copyright (c) 1999-2002 Ross Bencina, Phil Burk
- *
- * 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.
- *
- * Any person wishing to distribute modifications to the Software is
- * requested to send the modifications to the original developer so that
- * they can be incorporated into the canonical version.
- *
- * 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.
- */
-
-/** @file
- Win32 host API initialization function table.
-
- @todo Consider using PA_USE_WMME etc instead of PA_NO_WMME. This is what
- the Unix version does, we should consider being consistent.
-*/
-
-
-#include <pa_hostapi.h>
-
-#ifdef __cplusplus
-extern "C"
-{
-#endif /* __cplusplus */
-
-PaError PaSkeleton_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiIndex index );
-PaError PaWinMme_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiIndex index );
-PaError PaWinDs_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiIndex index );
-PaError PaAsio_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiIndex index );
-PaError PaWinWdm_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiIndex index );
-
-#ifdef __cplusplus
-}
-#endif /* __cplusplus */
-
-
-PaUtilHostApiInitializer *paHostApiInitializers[] =
- {
-
-#ifndef PA_NO_WMME
- PaWinMme_Initialize,
-#endif
-
-#ifndef PA_NO_DS
- PaWinDs_Initialize,
-#endif
-
-#ifndef PA_NO_ASIO
- PaAsio_Initialize,
-#endif
-
-/*
-#ifndef PA_NO_WDMKS
- PaWinWdm_Initialize,
-#endif
-*/
-
- PaSkeleton_Initialize, /* just for testing */
-
- 0 /* NULL terminated array */
- };
-
-
-int paDefaultHostApiIndex = 0;
-
+/*
+ * $Id: pa_win_hostapis.c,v 1.1.2.10 2004/09/08 17:31:37 rossbencina Exp $
+ * Portable Audio I/O Library Windows initialization table
+ *
+ * Based on the Open Source API proposed by Ross Bencina
+ * Copyright (c) 1999-2002 Ross Bencina, Phil Burk
+ *
+ * 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.
+ *
+ * Any person wishing to distribute modifications to the Software is
+ * requested to send the modifications to the original developer so that
+ * they can be incorporated into the canonical version.
+ *
+ * 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.
+ */
+
+/** @file
+ Win32 host API initialization function table.
+
+ @todo Consider using PA_USE_WMME etc instead of PA_NO_WMME. This is what
+ the Unix version does, we should consider being consistent.
+*/
+
+
+#include <pa_hostapi.h>
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif /* __cplusplus */
+
+PaError PaSkeleton_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiIndex index );
+PaError PaWinMme_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiIndex index );
+PaError PaWinDs_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiIndex index );
+PaError PaAsio_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiIndex index );
+PaError PaWinWdm_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiIndex index );
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+
+PaUtilHostApiInitializer *paHostApiInitializers[] =
+ {
+
+#ifndef PA_NO_WMME
+ PaWinMme_Initialize,
+#endif
+
+#ifndef PA_NO_DS
+ PaWinDs_Initialize,
+#endif
+
+#ifndef PA_NO_ASIO
+ PaAsio_Initialize,
+#endif
+
+/*
+#ifndef PA_NO_WDMKS
+ PaWinWdm_Initialize,
+#endif
+*/
+
+ PaSkeleton_Initialize, /* just for testing */
+
+ 0 /* NULL terminated array */
+ };
+
+
+int paDefaultHostApiIndex = 0;
+
diff --git a/pjmedia/src/pjmedia/portaudio/pa_win_util.c b/pjmedia/src/pjmedia/portaudio/pa_win_util.c
index 3e10cf05..0c7e38ee 100644
--- a/pjmedia/src/pjmedia/portaudio/pa_win_util.c
+++ b/pjmedia/src/pjmedia/portaudio/pa_win_util.c
@@ -1,133 +1,133 @@
-/*
- * $Id: pa_win_util.c,v 1.1.2.7 2003/09/15 18:30:26 rossbencina Exp $
- * Portable Audio I/O Library
- * Win32 platform-specific support functions
- *
- * Based on the Open Source API proposed by Ross Bencina
- * Copyright (c) 1999-2000 Ross Bencina
- *
- * 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.
- *
- * Any person wishing to distribute modifications to the Software is
- * requested to send the modifications to the original developer so that
- * they can be incorporated into the canonical version.
- *
- * 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.
- */
-/** @file
- Win32 platform-specific support functions.
-
- @todo Implement workaround for QueryPerformanceCounter() skipping forward
- bug. (see msdn kb Q274323).
-*/
-
-#include <windows.h>
-#include <mmsystem.h> /* for timeGetTime() */
-
-#include "pa_util.h"
-
-
-/*
- Track memory allocations to avoid leaks.
- */
-
-#if PA_TRACK_MEMORY
-static int numAllocations_ = 0;
-#endif
-
-
-void *PaUtil_AllocateMemory( long size )
-{
- void *result = GlobalAlloc( GPTR, size );
-
-#if PA_TRACK_MEMORY
- if( result != NULL ) numAllocations_ += 1;
-#endif
- return result;
-}
-
-
-void PaUtil_FreeMemory( void *block )
-{
- if( block != NULL )
- {
- GlobalFree( block );
-#if PA_TRACK_MEMORY
- numAllocations_ -= 1;
-#endif
-
- }
-}
-
-
-int PaUtil_CountCurrentlyAllocatedBlocks( void )
-{
-#if PA_TRACK_MEMORY
- return numAllocations_;
-#else
- return 0;
-#endif
-}
-
-
-void Pa_Sleep( long msec )
-{
- Sleep( msec );
-}
-
-static int usePerformanceCounter_;
-static double secondsPerTick_;
-
-void PaUtil_InitializeClock( void )
-{
- LARGE_INTEGER ticksPerSecond;
-
- if( QueryPerformanceFrequency( &ticksPerSecond ) != 0 )
- {
- usePerformanceCounter_ = 1;
- secondsPerTick_ = 1.0 / (double)ticksPerSecond.QuadPart;
- }
- else
- {
- usePerformanceCounter_ = 0;
- }
-}
-
-
-double PaUtil_GetTime( void )
-{
- LARGE_INTEGER time;
-
- if( usePerformanceCounter_ )
- {
- /* FIXME:
- according to this knowledge-base article, QueryPerformanceCounter
- can skip forward by seconds!
- http://support.microsoft.com/default.aspx?scid=KB;EN-US;Q274323&
-
- it may be better to use the rtdsc instruction using inline asm,
- however then a method is needed to calculate a ticks/seconds ratio.
- */
- QueryPerformanceCounter( &time );
- return time.QuadPart * secondsPerTick_;
- }
- else
- {
- return timeGetTime() * .001;
- }
-}
+/*
+ * $Id: pa_win_util.c,v 1.1.2.7 2003/09/15 18:30:26 rossbencina Exp $
+ * Portable Audio I/O Library
+ * Win32 platform-specific support functions
+ *
+ * Based on the Open Source API proposed by Ross Bencina
+ * Copyright (c) 1999-2000 Ross Bencina
+ *
+ * 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.
+ *
+ * Any person wishing to distribute modifications to the Software is
+ * requested to send the modifications to the original developer so that
+ * they can be incorporated into the canonical version.
+ *
+ * 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.
+ */
+/** @file
+ Win32 platform-specific support functions.
+
+ @todo Implement workaround for QueryPerformanceCounter() skipping forward
+ bug. (see msdn kb Q274323).
+*/
+
+#include <windows.h>
+#include <mmsystem.h> /* for timeGetTime() */
+
+#include "pa_util.h"
+
+
+/*
+ Track memory allocations to avoid leaks.
+ */
+
+#if PA_TRACK_MEMORY
+static int numAllocations_ = 0;
+#endif
+
+
+void *PaUtil_AllocateMemory( long size )
+{
+ void *result = GlobalAlloc( GPTR, size );
+
+#if PA_TRACK_MEMORY
+ if( result != NULL ) numAllocations_ += 1;
+#endif
+ return result;
+}
+
+
+void PaUtil_FreeMemory( void *block )
+{
+ if( block != NULL )
+ {
+ GlobalFree( block );
+#if PA_TRACK_MEMORY
+ numAllocations_ -= 1;
+#endif
+
+ }
+}
+
+
+int PaUtil_CountCurrentlyAllocatedBlocks( void )
+{
+#if PA_TRACK_MEMORY
+ return numAllocations_;
+#else
+ return 0;
+#endif
+}
+
+
+void Pa_Sleep( long msec )
+{
+ Sleep( msec );
+}
+
+static int usePerformanceCounter_;
+static double secondsPerTick_;
+
+void PaUtil_InitializeClock( void )
+{
+ LARGE_INTEGER ticksPerSecond;
+
+ if( QueryPerformanceFrequency( &ticksPerSecond ) != 0 )
+ {
+ usePerformanceCounter_ = 1;
+ secondsPerTick_ = 1.0 / (double)ticksPerSecond.QuadPart;
+ }
+ else
+ {
+ usePerformanceCounter_ = 0;
+ }
+}
+
+
+double PaUtil_GetTime( void )
+{
+ LARGE_INTEGER time;
+
+ if( usePerformanceCounter_ )
+ {
+ /* FIXME:
+ according to this knowledge-base article, QueryPerformanceCounter
+ can skip forward by seconds!
+ http://support.microsoft.com/default.aspx?scid=KB;EN-US;Q274323&
+
+ it may be better to use the rtdsc instruction using inline asm,
+ however then a method is needed to calculate a ticks/seconds ratio.
+ */
+ QueryPerformanceCounter( &time );
+ return time.QuadPart * secondsPerTick_;
+ }
+ else
+ {
+ return timeGetTime() * .001;
+ }
+}
diff --git a/pjmedia/src/pjmedia/portaudio/pa_win_wmme.c b/pjmedia/src/pjmedia/portaudio/pa_win_wmme.c
index ab9d9007..c3d7fe6d 100644
--- a/pjmedia/src/pjmedia/portaudio/pa_win_wmme.c
+++ b/pjmedia/src/pjmedia/portaudio/pa_win_wmme.c
@@ -1,3623 +1,3623 @@
-/*
- * $Id: pa_win_wmme.c,v 1.6.2.86 2004/02/21 11:38:28 rossbencina Exp $
- * pa_win_wmme.c
- * Implementation of PortAudio for Windows MultiMedia Extensions (WMME)
- *
- * PortAudio Portable Real-Time Audio Library
- * Latest Version at: http://www.portaudio.com
- *
- * Authors: Ross Bencina and Phil Burk
- * Copyright (c) 1999-2000 Ross Bencina and Phil Burk
- *
- * 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.
- *
- * Any person wishing to distribute modifications to the Software is
- * requested to send the modifications to the original developer so that
- * they can be incorporated into the canonical version.
- *
- * 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.
- *
- */
-
-/* Modification History:
- PLB = Phil Burk
- JM = Julien Maillard
- RDB = Ross Bencina
- PLB20010402 - sDevicePtrs now allocates based on sizeof(pointer)
- PLB20010413 - check for excessive numbers of channels
- PLB20010422 - apply Mike Berry's changes for CodeWarrior on PC
- including conditional inclusion of memory.h,
- and explicit typecasting on memory allocation
- PLB20010802 - use GlobalAlloc for sDevicesPtr instead of PaHost_AllocFastMemory
- PLB20010816 - pass process instead of thread to SetPriorityClass()
- PLB20010927 - use number of frames instead of real-time for CPULoad calculation.
- JM20020118 - prevent hung thread when buffers underflow.
- PLB20020321 - detect Win XP versus NT, 9x; fix DBUG typo; removed init of CurrentCount
- RDB20020411 - various renaming cleanups, factored streamData alloc and cpu usage init
- RDB20020417 - stopped counting WAVE_MAPPER when there were no real devices
- refactoring, renaming and fixed a few edge case bugs
- RDB20020531 - converted to V19 framework
- ** NOTE maintanance history is now stored in CVS **
-*/
-
-/** @file
-
- @todo Fix buffer catch up code, can sometimes get stuck (perhaps fixed now,
- needs to be reviewed and tested.)
-
- @todo implement paInputUnderflow, paOutputOverflow streamCallback statusFlags, paNeverDropInput.
-
- @todo BUG: PA_MME_SET_LAST_WAVEIN/OUT_ERROR is used in functions which may
- be called asynchronously from the callback thread. this is bad.
-
- @todo implement inputBufferAdcTime in callback thread
-
- @todo review/fix error recovery and cleanup in marked functions
-
- @todo implement timeInfo for stream priming
-
- @todo handle the case where the callback returns paAbort or paComplete during stream priming.
-
- @todo review input overflow and output underflow handling in ReadStream and WriteStream
-
-Non-critical stuff for the future:
-
- @todo Investigate supporting host buffer formats > 16 bits
-
- @todo define UNICODE and _UNICODE in the project settings and see what breaks
-
-*/
-
-/*
- How it works:
-
- For both callback and blocking read/write streams we open the MME devices
- in CALLBACK_EVENT mode. In this mode, MME signals an Event object whenever
- it has finished with a buffer (either filled it for input, or played it
- for output). Where necessary we block waiting for Event objects using
- WaitMultipleObjects().
-
- When implementing a PA callback stream, we set up a high priority thread
- which waits on the MME buffer Events and drains/fills the buffers when
- they are ready.
-
- When implementing a PA blocking read/write stream, we simply wait on these
- Events (when necessary) inside the ReadStream() and WriteStream() functions.
-*/
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <math.h>
-#include <windows.h>
-#include <mmsystem.h>
-#include <process.h>
-#include <assert.h>
-/* PLB20010422 - "memory.h" doesn't work on CodeWarrior for PC. Thanks Mike Berry for the mod. */
-#ifndef __MWERKS__
-#include <malloc.h>
-#include <memory.h>
-#endif /* __MWERKS__ */
-
-#include "portaudio.h"
-#include "pa_trace.h"
-#include "pa_util.h"
-#include "pa_allocation.h"
-#include "pa_hostapi.h"
-#include "pa_stream.h"
-#include "pa_cpuload.h"
-#include "pa_process.h"
-
-#include "pa_win_wmme.h"
-
-#if (defined(WIN32) && (defined(_MSC_VER) && (_MSC_VER >= 1200))) /* MSC version 6 and above */
-#pragma comment(lib, "winmm.lib")
-#endif
-
-/************************************************* Constants ********/
-
-#define PA_MME_USE_HIGH_DEFAULT_LATENCY_ (0) /* For debugging glitches. */
-
-#if PA_MME_USE_HIGH_DEFAULT_LATENCY_
- #define PA_MME_WIN_9X_DEFAULT_LATENCY_ (0.4)
- #define PA_MME_MIN_HOST_OUTPUT_BUFFER_COUNT_ (4)
- #define PA_MME_MIN_HOST_INPUT_BUFFER_COUNT_FULL_DUPLEX_ (4)
- #define PA_MME_MIN_HOST_INPUT_BUFFER_COUNT_HALF_DUPLEX_ (4)
- #define PA_MME_MIN_HOST_BUFFER_FRAMES_WHEN_UNSPECIFIED_ (16)
- #define PA_MME_MAX_HOST_BUFFER_SECS_ (0.3) /* Do not exceed unless user buffer exceeds */
- #define PA_MME_MAX_HOST_BUFFER_BYTES_ (32 * 1024) /* Has precedence over PA_MME_MAX_HOST_BUFFER_SECS_, some drivers are known to crash with buffer sizes > 32k */
-#else
- #define PA_MME_WIN_9X_DEFAULT_LATENCY_ (0.2)
- #define PA_MME_MIN_HOST_OUTPUT_BUFFER_COUNT_ (2)
- #define PA_MME_MIN_HOST_INPUT_BUFFER_COUNT_FULL_DUPLEX_ (3)
- #define PA_MME_MIN_HOST_INPUT_BUFFER_COUNT_HALF_DUPLEX_ (2)
- #define PA_MME_MIN_HOST_BUFFER_FRAMES_WHEN_UNSPECIFIED_ (16)
- #define PA_MME_MAX_HOST_BUFFER_SECS_ (0.1) /* Do not exceed unless user buffer exceeds */
- #define PA_MME_MAX_HOST_BUFFER_BYTES_ (32 * 1024) /* Has precedence over PA_MME_MAX_HOST_BUFFER_SECS_, some drivers are known to crash with buffer sizes > 32k */
-#endif
-
-/* Use higher latency for NT because it is even worse at real-time
- operation than Win9x.
-*/
-#define PA_MME_WIN_NT_DEFAULT_LATENCY_ (PA_MME_WIN_9X_DEFAULT_LATENCY_ * 2)
-#define PA_MME_WIN_WDM_DEFAULT_LATENCY_ (PA_MME_WIN_9X_DEFAULT_LATENCY_)
-
-
-#define PA_MME_MIN_TIMEOUT_MSEC_ (1000)
-
-static const char constInputMapperSuffix_[] = " - Input";
-static const char constOutputMapperSuffix_[] = " - Output";
-
-/********************************************************************/
-
-typedef struct PaWinMmeStream PaWinMmeStream; /* forward declaration */
-
-/* prototypes for functions declared in this file */
-
-#ifdef __cplusplus
-extern "C"
-{
-#endif /* __cplusplus */
-
-PaError PaWinMme_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiIndex index );
-
-#ifdef __cplusplus
-}
-#endif /* __cplusplus */
-
-static void Terminate( struct PaUtilHostApiRepresentation *hostApi );
-static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi,
- PaStream** stream,
- const PaStreamParameters *inputParameters,
- const PaStreamParameters *outputParameters,
- double sampleRate,
- unsigned long framesPerBuffer,
- PaStreamFlags streamFlags,
- PaStreamCallback *streamCallback,
- void *userData );
-static PaError IsFormatSupported( struct PaUtilHostApiRepresentation *hostApi,
- const PaStreamParameters *inputParameters,
- const PaStreamParameters *outputParameters,
- double sampleRate );
-static PaError CloseStream( PaStream* stream );
-static PaError StartStream( PaStream *stream );
-static PaError StopStream( PaStream *stream );
-static PaError AbortStream( PaStream *stream );
-static PaError IsStreamStopped( PaStream *s );
-static PaError IsStreamActive( PaStream *stream );
-static PaTime GetStreamTime( PaStream *stream );
-static double GetStreamCpuLoad( PaStream* stream );
-static PaError ReadStream( PaStream* stream, void *buffer, unsigned long frames );
-static PaError WriteStream( PaStream* stream, const void *buffer, unsigned long frames );
-static signed long GetStreamReadAvailable( PaStream* stream );
-static signed long GetStreamWriteAvailable( PaStream* stream );
-
-
-/* macros for setting last host error information */
-
-#ifdef UNICODE
-
-#define PA_MME_SET_LAST_WAVEIN_ERROR( mmresult ) \
- { \
- wchar_t mmeErrorTextWide[ MAXERRORLENGTH ]; \
- char mmeErrorText[ MAXERRORLENGTH ]; \
- waveInGetErrorText( mmresult, mmeErrorTextWide, MAXERRORLENGTH ); \
- WideCharToMultiByte( CP_ACP, WC_COMPOSITECHECK | WC_DEFAULTCHAR,\
- mmeErrorTextWide, -1, mmeErrorText, MAXERRORLENGTH, NULL, NULL ); \
- PaUtil_SetLastHostErrorInfo( paMME, mmresult, mmeErrorText ); \
- }
-
-#define PA_MME_SET_LAST_WAVEOUT_ERROR( mmresult ) \
- { \
- wchar_t mmeErrorTextWide[ MAXERRORLENGTH ]; \
- char mmeErrorText[ MAXERRORLENGTH ]; \
- waveOutGetErrorText( mmresult, mmeErrorTextWide, MAXERRORLENGTH ); \
- WideCharToMultiByte( CP_ACP, WC_COMPOSITECHECK | WC_DEFAULTCHAR,\
- mmeErrorTextWide, -1, mmeErrorText, MAXERRORLENGTH, NULL, NULL ); \
- PaUtil_SetLastHostErrorInfo( paMME, mmresult, mmeErrorText ); \
- }
-
-#else /* !UNICODE */
-
-#define PA_MME_SET_LAST_WAVEIN_ERROR( mmresult ) \
- { \
- char mmeErrorText[ MAXERRORLENGTH ]; \
- waveInGetErrorText( mmresult, mmeErrorText, MAXERRORLENGTH ); \
- PaUtil_SetLastHostErrorInfo( paMME, mmresult, mmeErrorText ); \
- }
-
-#define PA_MME_SET_LAST_WAVEOUT_ERROR( mmresult ) \
- { \
- char mmeErrorText[ MAXERRORLENGTH ]; \
- waveOutGetErrorText( mmresult, mmeErrorText, MAXERRORLENGTH ); \
- PaUtil_SetLastHostErrorInfo( paMME, mmresult, mmeErrorText ); \
- }
-
-#endif /* UNICODE */
-
-
-static void PaMme_SetLastSystemError( DWORD errorCode )
-{
- char *lpMsgBuf;
- FormatMessage(
- FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM,
- NULL,
- errorCode,
- MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
- (LPTSTR) &lpMsgBuf,
- 0,
- NULL
- );
- PaUtil_SetLastHostErrorInfo( paMME, errorCode, lpMsgBuf );
- LocalFree( lpMsgBuf );
-}
-
-#define PA_MME_SET_LAST_SYSTEM_ERROR( errorCode ) \
- PaMme_SetLastSystemError( errorCode )
-
-
-/* PaError returning wrappers for some commonly used win32 functions
- note that we allow passing a null ptr to have no effect.
-*/
-
-static PaError CreateEventWithPaError( HANDLE *handle,
- LPSECURITY_ATTRIBUTES lpEventAttributes,
- BOOL bManualReset,
- BOOL bInitialState,
- LPCTSTR lpName )
-{
- PaError result = paNoError;
-
- *handle = NULL;
-
- *handle = CreateEvent( lpEventAttributes, bManualReset, bInitialState, lpName );
- if( *handle == NULL )
- {
- result = paUnanticipatedHostError;
- PA_MME_SET_LAST_SYSTEM_ERROR( GetLastError() );
- }
-
- return result;
-}
-
-
-static PaError ResetEventWithPaError( HANDLE handle )
-{
- PaError result = paNoError;
-
- if( handle )
- {
- if( ResetEvent( handle ) == 0 )
- {
- result = paUnanticipatedHostError;
- PA_MME_SET_LAST_SYSTEM_ERROR( GetLastError() );
- }
- }
-
- return result;
-}
-
-
-static PaError CloseHandleWithPaError( HANDLE handle )
-{
- PaError result = paNoError;
-
- if( handle )
- {
- if( CloseHandle( handle ) == 0 )
- {
- result = paUnanticipatedHostError;
- PA_MME_SET_LAST_SYSTEM_ERROR( GetLastError() );
- }
- }
-
- return result;
-}
-
-
-/* PaWinMmeHostApiRepresentation - host api datastructure specific to this implementation */
-
-typedef struct
-{
- PaUtilHostApiRepresentation inheritedHostApiRep;
- PaUtilStreamInterface callbackStreamInterface;
- PaUtilStreamInterface blockingStreamInterface;
-
- PaUtilAllocationGroup *allocations;
-
- int inputDeviceCount, outputDeviceCount;
-
- /** winMmeDeviceIds is an array of WinMme device ids.
- fields in the range [0, inputDeviceCount) are input device ids,
- and [inputDeviceCount, inputDeviceCount + outputDeviceCount) are output
- device ids.
- */
- UINT *winMmeDeviceIds;
-}
-PaWinMmeHostApiRepresentation;
-
-
-typedef struct
-{
- PaDeviceInfo inheritedDeviceInfo;
- DWORD dwFormats; /**<< standard formats bitmask from the WAVEINCAPS and WAVEOUTCAPS structures */
-}
-PaWinMmeDeviceInfo;
-
-
-/*************************************************************************
- * Returns recommended device ID.
- * On the PC, the recommended device can be specified by the user by
- * setting an environment variable. For example, to use device #1.
- *
- * set PA_RECOMMENDED_OUTPUT_DEVICE=1
- *
- * The user should first determine the available device ID by using
- * the supplied application "pa_devs".
- */
-#define PA_ENV_BUF_SIZE_ (32)
-#define PA_REC_IN_DEV_ENV_NAME_ ("PA_RECOMMENDED_INPUT_DEVICE")
-#define PA_REC_OUT_DEV_ENV_NAME_ ("PA_RECOMMENDED_OUTPUT_DEVICE")
-static PaDeviceIndex GetEnvDefaultDeviceID( char *envName )
-{
- PaDeviceIndex recommendedIndex = paNoDevice;
- DWORD hresult;
- char envbuf[PA_ENV_BUF_SIZE_];
-
-#ifndef WIN32_PLATFORM_PSPC /* no GetEnvironmentVariable on PocketPC */
-
- /* Let user determine default device by setting environment variable. */
- hresult = GetEnvironmentVariable( envName, envbuf, PA_ENV_BUF_SIZE_ );
- if( (hresult > 0) && (hresult < PA_ENV_BUF_SIZE_) )
- {
- recommendedIndex = atoi( envbuf );
- }
-#endif
-
- return recommendedIndex;
-}
-
-
-static void InitializeDefaultDeviceIdsFromEnv( PaWinMmeHostApiRepresentation *hostApi )
-{
- PaDeviceIndex device;
-
- /* input */
- device = GetEnvDefaultDeviceID( PA_REC_IN_DEV_ENV_NAME_ );
- if( device != paNoDevice &&
- ( device >= 0 && device < hostApi->inheritedHostApiRep.info.deviceCount ) &&
- hostApi->inheritedHostApiRep.deviceInfos[ device ]->maxInputChannels > 0 )
- {
- hostApi->inheritedHostApiRep.info.defaultInputDevice = device;
- }
-
- /* output */
- device = GetEnvDefaultDeviceID( PA_REC_OUT_DEV_ENV_NAME_ );
- if( device != paNoDevice &&
- ( device >= 0 && device < hostApi->inheritedHostApiRep.info.deviceCount ) &&
- hostApi->inheritedHostApiRep.deviceInfos[ device ]->maxOutputChannels > 0 )
- {
- hostApi->inheritedHostApiRep.info.defaultOutputDevice = device;
- }
-}
-
-
-/** Convert external PA ID to a windows multimedia device ID
-*/
-static UINT LocalDeviceIndexToWinMmeDeviceId( PaWinMmeHostApiRepresentation *hostApi, PaDeviceIndex device )
-{
- assert( device >= 0 && device < hostApi->inputDeviceCount + hostApi->outputDeviceCount );
-
- return hostApi->winMmeDeviceIds[ device ];
-}
-
-
-static PaError QueryInputWaveFormatEx( int deviceId, WAVEFORMATEX *waveFormatEx )
-{
- MMRESULT mmresult;
-
- switch( mmresult = waveInOpen( NULL, deviceId, waveFormatEx, 0, 0, WAVE_FORMAT_QUERY ) )
- {
- case MMSYSERR_NOERROR:
- return paNoError;
- case MMSYSERR_ALLOCATED: /* Specified resource is already allocated. */
- return paDeviceUnavailable;
- case MMSYSERR_NODRIVER: /* No device driver is present. */
- return paDeviceUnavailable;
- case MMSYSERR_NOMEM: /* Unable to allocate or lock memory. */
- return paInsufficientMemory;
- case WAVERR_BADFORMAT: /* Attempted to open with an unsupported waveform-audio format. */
- return paSampleFormatNotSupported;
-
- case MMSYSERR_BADDEVICEID: /* Specified device identifier is out of range. */
- /* falls through */
- default:
- PA_MME_SET_LAST_WAVEIN_ERROR( mmresult );
- return paUnanticipatedHostError;
- }
-}
-
-
-static PaError QueryOutputWaveFormatEx( int deviceId, WAVEFORMATEX *waveFormatEx )
-{
- MMRESULT mmresult;
-
- switch( mmresult = waveOutOpen( NULL, deviceId, waveFormatEx, 0, 0, WAVE_FORMAT_QUERY ) )
- {
- case MMSYSERR_NOERROR:
- return paNoError;
- case MMSYSERR_ALLOCATED: /* Specified resource is already allocated. */
- return paDeviceUnavailable;
- case MMSYSERR_NODRIVER: /* No device driver is present. */
- return paDeviceUnavailable;
- case MMSYSERR_NOMEM: /* Unable to allocate or lock memory. */
- return paInsufficientMemory;
- case WAVERR_BADFORMAT: /* Attempted to open with an unsupported waveform-audio format. */
- return paSampleFormatNotSupported;
-
- case MMSYSERR_BADDEVICEID: /* Specified device identifier is out of range. */
- /* falls through */
- default:
- PA_MME_SET_LAST_WAVEOUT_ERROR( mmresult );
- return paUnanticipatedHostError;
- }
-}
-
-
-static PaError QueryFormatSupported( PaDeviceInfo *deviceInfo,
- PaError (*waveFormatExQueryFunction)(int, WAVEFORMATEX*),
- int winMmeDeviceId, int channels, double sampleRate )
-{
- PaWinMmeDeviceInfo *winMmeDeviceInfo = (PaWinMmeDeviceInfo*)deviceInfo;
- WAVEFORMATEX waveFormatEx;
-
- if( sampleRate == 11025.0
- && ( (channels == 1 && (winMmeDeviceInfo->dwFormats & WAVE_FORMAT_1M16))
- || (channels == 2 && (winMmeDeviceInfo->dwFormats & WAVE_FORMAT_1S16)) ) ){
-
- return paNoError;
- }
-
- if( sampleRate == 22050.0
- && ( (channels == 1 && (winMmeDeviceInfo->dwFormats & WAVE_FORMAT_2M16))
- || (channels == 2 && (winMmeDeviceInfo->dwFormats & WAVE_FORMAT_2S16)) ) ){
-
- return paNoError;
- }
-
- if( sampleRate == 44100.0
- && ( (channels == 1 && (winMmeDeviceInfo->dwFormats & WAVE_FORMAT_4M16))
- || (channels == 2 && (winMmeDeviceInfo->dwFormats & WAVE_FORMAT_4S16)) ) ){
-
- return paNoError;
- }
-
- waveFormatEx.wFormatTag = WAVE_FORMAT_PCM;
- waveFormatEx.nChannels = (WORD)channels;
- waveFormatEx.nSamplesPerSec = (DWORD)sampleRate;
- waveFormatEx.nAvgBytesPerSec = waveFormatEx.nSamplesPerSec * channels * sizeof(short);
- waveFormatEx.nBlockAlign = (WORD)(channels * sizeof(short));
- waveFormatEx.wBitsPerSample = 16;
- waveFormatEx.cbSize = 0;
-
- return waveFormatExQueryFunction( winMmeDeviceId, &waveFormatEx );
-}
-
-
-#define PA_DEFAULTSAMPLERATESEARCHORDER_COUNT_ (13) /* must match array length below */
-static double defaultSampleRateSearchOrder_[] =
- { 44100.0, 48000.0, 32000.0, 24000.0, 22050.0, 88200.0, 96000.0, 192000.0,
- 16000.0, 12000.0, 11025.0, 9600.0, 8000.0 };
-
-static void DetectDefaultSampleRate( PaWinMmeDeviceInfo *winMmeDeviceInfo, int winMmeDeviceId,
- PaError (*waveFormatExQueryFunction)(int, WAVEFORMATEX*), int maxChannels )
-{
- PaDeviceInfo *deviceInfo = &winMmeDeviceInfo->inheritedDeviceInfo;
- int i;
-
- deviceInfo->defaultSampleRate = 0.;
-
- for( i=0; i < PA_DEFAULTSAMPLERATESEARCHORDER_COUNT_; ++i )
- {
- double sampleRate = defaultSampleRateSearchOrder_[ i ];
- PaError paerror = QueryFormatSupported( deviceInfo, waveFormatExQueryFunction, winMmeDeviceId, maxChannels, sampleRate );
- if( paerror == paNoError )
- {
- deviceInfo->defaultSampleRate = sampleRate;
- break;
- }
- }
-}
-
-
-static PaError InitializeInputDeviceInfo( PaWinMmeHostApiRepresentation *winMmeHostApi,
- PaWinMmeDeviceInfo *winMmeDeviceInfo, UINT winMmeInputDeviceId, int *success )
-{
- PaError result = paNoError;
- char *deviceName; /* non-const ptr */
- MMRESULT mmresult;
- WAVEINCAPS wic;
- PaDeviceInfo *deviceInfo = &winMmeDeviceInfo->inheritedDeviceInfo;
-
- *success = 0;
-
- mmresult = waveInGetDevCaps( winMmeInputDeviceId, &wic, sizeof( WAVEINCAPS ) );
- if( mmresult == MMSYSERR_NOMEM )
- {
- result = paInsufficientMemory;
- goto error;
- }
- else if( mmresult != MMSYSERR_NOERROR )
- {
- /* instead of returning paUnanticipatedHostError we return
- paNoError, but leave success set as 0. This allows
- Pa_Initialize to just ignore this device, without failing
- the entire initialisation process.
- */
- return paNoError;
- }
-
- if( winMmeInputDeviceId == WAVE_MAPPER )
- {
- /* Append I/O suffix to WAVE_MAPPER device. */
- deviceName = (char *)PaUtil_GroupAllocateMemory(
- winMmeHostApi->allocations, strlen( wic.szPname ) + 1 + sizeof(constInputMapperSuffix_) );
- if( !deviceName )
- {
- result = paInsufficientMemory;
- goto error;
- }
- strcpy( deviceName, wic.szPname );
- strcat( deviceName, constInputMapperSuffix_ );
- }
- else
- {
- deviceName = (char*)PaUtil_GroupAllocateMemory(
- winMmeHostApi->allocations, strlen( wic.szPname ) + 1 );
- if( !deviceName )
- {
- result = paInsufficientMemory;
- goto error;
- }
- strcpy( deviceName, wic.szPname );
- }
- deviceInfo->name = deviceName;
-
- deviceInfo->maxInputChannels = wic.wChannels;
- /* Sometimes a device can return a rediculously large number of channels.
- * This happened with an SBLive card on a Windows ME box.
- * If that happens, then force it to 2 channels. PLB20010413
- */
- if( (deviceInfo->maxInputChannels < 1) || (deviceInfo->maxInputChannels > 256) )
- {
- PA_DEBUG(("Pa_GetDeviceInfo: Num input channels reported as %d! Changed to 2.\n", deviceInfo->maxInputChannels ));
- deviceInfo->maxInputChannels = 2;
- }
-
- winMmeDeviceInfo->dwFormats = wic.dwFormats;
-
- DetectDefaultSampleRate( winMmeDeviceInfo, winMmeInputDeviceId,
- QueryInputWaveFormatEx, deviceInfo->maxInputChannels );
-
- *success = 1;
-
-error:
- return result;
-}
-
-
-static PaError InitializeOutputDeviceInfo( PaWinMmeHostApiRepresentation *winMmeHostApi,
- PaWinMmeDeviceInfo *winMmeDeviceInfo, UINT winMmeOutputDeviceId, int *success )
-{
- PaError result = paNoError;
- char *deviceName; /* non-const ptr */
- MMRESULT mmresult;
- WAVEOUTCAPS woc;
- PaDeviceInfo *deviceInfo = &winMmeDeviceInfo->inheritedDeviceInfo;
-
- *success = 0;
-
- mmresult = waveOutGetDevCaps( winMmeOutputDeviceId, &woc, sizeof( WAVEOUTCAPS ) );
- if( mmresult == MMSYSERR_NOMEM )
- {
- result = paInsufficientMemory;
- goto error;
- }
- else if( mmresult != MMSYSERR_NOERROR )
- {
- /* instead of returning paUnanticipatedHostError we return
- paNoError, but leave success set as 0. This allows
- Pa_Initialize to just ignore this device, without failing
- the entire initialisation process.
- */
- return paNoError;
- }
-
- if( winMmeOutputDeviceId == WAVE_MAPPER )
- {
- /* Append I/O suffix to WAVE_MAPPER device. */
- deviceName = (char *)PaUtil_GroupAllocateMemory(
- winMmeHostApi->allocations, strlen( woc.szPname ) + 1 + sizeof(constOutputMapperSuffix_) );
- if( !deviceName )
- {
- result = paInsufficientMemory;
- goto error;
- }
- strcpy( deviceName, woc.szPname );
- strcat( deviceName, constOutputMapperSuffix_ );
- }
- else
- {
- deviceName = (char*)PaUtil_GroupAllocateMemory(
- winMmeHostApi->allocations, strlen( woc.szPname ) + 1 );
- if( !deviceName )
- {
- result = paInsufficientMemory;
- goto error;
- }
- strcpy( deviceName, woc.szPname );
- }
- deviceInfo->name = deviceName;
-
- deviceInfo->maxOutputChannels = woc.wChannels;
- /* Sometimes a device can return a rediculously large number of channels.
- * This happened with an SBLive card on a Windows ME box.
- * It also happens on Win XP!
- */
- if( (deviceInfo->maxOutputChannels < 1) || (deviceInfo->maxOutputChannels > 256) )
- {
- PA_DEBUG(("Pa_GetDeviceInfo: Num output channels reported as %d! Changed to 2.\n", deviceInfo->maxOutputChannels ));
- deviceInfo->maxOutputChannels = 2;
- }
-
- winMmeDeviceInfo->dwFormats = woc.dwFormats;
-
- DetectDefaultSampleRate( winMmeDeviceInfo, winMmeOutputDeviceId,
- QueryOutputWaveFormatEx, deviceInfo->maxOutputChannels );
-
- *success = 1;
-
-error:
- return result;
-}
-
-
-static void GetDefaultLatencies( PaTime *defaultLowLatency, PaTime *defaultHighLatency )
-{
- OSVERSIONINFO osvi;
- osvi.dwOSVersionInfoSize = sizeof( osvi );
- GetVersionEx( &osvi );
-
- /* Check for NT */
- if( (osvi.dwMajorVersion == 4) && (osvi.dwPlatformId == 2) )
- {
- *defaultLowLatency = PA_MME_WIN_NT_DEFAULT_LATENCY_;
- }
- else if(osvi.dwMajorVersion >= 5)
- {
- *defaultLowLatency = PA_MME_WIN_WDM_DEFAULT_LATENCY_;
- }
- else
- {
- *defaultLowLatency = PA_MME_WIN_9X_DEFAULT_LATENCY_;
- }
-
- *defaultHighLatency = *defaultLowLatency * 2;
-}
-
-
-PaError PaWinMme_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiIndex hostApiIndex )
-{
- PaError result = paNoError;
- int i;
- PaWinMmeHostApiRepresentation *winMmeHostApi;
- int inputDeviceCount, outputDeviceCount, maximumPossibleDeviceCount;
- PaWinMmeDeviceInfo *deviceInfoArray;
- int deviceInfoInitializationSucceeded;
- PaTime defaultLowLatency, defaultHighLatency;
-
- winMmeHostApi = (PaWinMmeHostApiRepresentation*)PaUtil_AllocateMemory( sizeof(PaWinMmeHostApiRepresentation) );
- if( !winMmeHostApi )
- {
- result = paInsufficientMemory;
- goto error;
- }
-
- winMmeHostApi->allocations = PaUtil_CreateAllocationGroup();
- if( !winMmeHostApi->allocations )
- {
- result = paInsufficientMemory;
- goto error;
- }
-
- *hostApi = &winMmeHostApi->inheritedHostApiRep;
- (*hostApi)->info.structVersion = 1;
- (*hostApi)->info.type = paMME;
- (*hostApi)->info.name = "MME";
-
-
- /* initialise device counts and default devices under the assumption that
- there are no devices. These values are incremented below if and when
- devices are successfully initialized.
- */
- (*hostApi)->info.deviceCount = 0;
- (*hostApi)->info.defaultInputDevice = paNoDevice;
- (*hostApi)->info.defaultOutputDevice = paNoDevice;
- winMmeHostApi->inputDeviceCount = 0;
- winMmeHostApi->outputDeviceCount = 0;
-
-
- maximumPossibleDeviceCount = 0;
-
- inputDeviceCount = waveInGetNumDevs();
- if( inputDeviceCount > 0 )
- maximumPossibleDeviceCount += inputDeviceCount + 1; /* assume there is a WAVE_MAPPER */
-
- outputDeviceCount = waveOutGetNumDevs();
- if( outputDeviceCount > 0 )
- maximumPossibleDeviceCount += outputDeviceCount + 1; /* assume there is a WAVE_MAPPER */
-
-
- if( maximumPossibleDeviceCount > 0 ){
-
- (*hostApi)->deviceInfos = (PaDeviceInfo**)PaUtil_GroupAllocateMemory(
- winMmeHostApi->allocations, sizeof(PaDeviceInfo*) * maximumPossibleDeviceCount );
- if( !(*hostApi)->deviceInfos )
- {
- result = paInsufficientMemory;
- goto error;
- }
-
- /* allocate all device info structs in a contiguous block */
- deviceInfoArray = (PaWinMmeDeviceInfo*)PaUtil_GroupAllocateMemory(
- winMmeHostApi->allocations, sizeof(PaWinMmeDeviceInfo) * maximumPossibleDeviceCount );
- if( !deviceInfoArray )
- {
- result = paInsufficientMemory;
- goto error;
- }
-
- winMmeHostApi->winMmeDeviceIds = (UINT*)PaUtil_GroupAllocateMemory(
- winMmeHostApi->allocations, sizeof(int) * maximumPossibleDeviceCount );
- if( !winMmeHostApi->winMmeDeviceIds )
- {
- result = paInsufficientMemory;
- goto error;
- }
-
- GetDefaultLatencies( &defaultLowLatency, &defaultHighLatency );
-
- if( inputDeviceCount > 0 ){
- /* -1 is the WAVE_MAPPER */
- for( i = -1; i < inputDeviceCount; ++i ){
- UINT winMmeDeviceId = (UINT)((i==-1) ? WAVE_MAPPER : i);
- PaWinMmeDeviceInfo *wmmeDeviceInfo = &deviceInfoArray[ (*hostApi)->info.deviceCount ];
- PaDeviceInfo *deviceInfo = &wmmeDeviceInfo->inheritedDeviceInfo;
- deviceInfo->structVersion = 2;
- deviceInfo->hostApi = hostApiIndex;
-
- deviceInfo->maxInputChannels = 0;
- deviceInfo->maxOutputChannels = 0;
-
- deviceInfo->defaultLowInputLatency = defaultLowLatency;
- deviceInfo->defaultLowOutputLatency = defaultLowLatency;
- deviceInfo->defaultHighInputLatency = defaultHighLatency;
- deviceInfo->defaultHighOutputLatency = defaultHighLatency;
-
- result = InitializeInputDeviceInfo( winMmeHostApi, wmmeDeviceInfo,
- winMmeDeviceId, &deviceInfoInitializationSucceeded );
- if( result != paNoError )
- goto error;
-
- if( deviceInfoInitializationSucceeded ){
- if( (*hostApi)->info.defaultInputDevice == paNoDevice )
- (*hostApi)->info.defaultInputDevice = (*hostApi)->info.deviceCount;
-
- winMmeHostApi->winMmeDeviceIds[ (*hostApi)->info.deviceCount ] = winMmeDeviceId;
- (*hostApi)->deviceInfos[ (*hostApi)->info.deviceCount ] = deviceInfo;
-
- winMmeHostApi->inputDeviceCount++;
- (*hostApi)->info.deviceCount++;
- }
- }
- }
-
- if( outputDeviceCount > 0 ){
- /* -1 is the WAVE_MAPPER */
- for( i = -1; i < outputDeviceCount; ++i ){
- UINT winMmeDeviceId = (UINT)((i==-1) ? WAVE_MAPPER : i);
- PaWinMmeDeviceInfo *wmmeDeviceInfo = &deviceInfoArray[ (*hostApi)->info.deviceCount ];
- PaDeviceInfo *deviceInfo = &wmmeDeviceInfo->inheritedDeviceInfo;
- deviceInfo->structVersion = 2;
- deviceInfo->hostApi = hostApiIndex;
-
- deviceInfo->maxInputChannels = 0;
- deviceInfo->maxOutputChannels = 0;
-
- deviceInfo->defaultLowInputLatency = defaultLowLatency;
- deviceInfo->defaultLowOutputLatency = defaultLowLatency;
- deviceInfo->defaultHighInputLatency = defaultHighLatency;
- deviceInfo->defaultHighOutputLatency = defaultHighLatency;
-
- result = InitializeOutputDeviceInfo( winMmeHostApi, wmmeDeviceInfo,
- winMmeDeviceId, &deviceInfoInitializationSucceeded );
- if( result != paNoError )
- goto error;
-
- if( deviceInfoInitializationSucceeded ){
- if( (*hostApi)->info.defaultOutputDevice == paNoDevice )
- (*hostApi)->info.defaultOutputDevice = (*hostApi)->info.deviceCount;
-
- winMmeHostApi->winMmeDeviceIds[ (*hostApi)->info.deviceCount ] = winMmeDeviceId;
- (*hostApi)->deviceInfos[ (*hostApi)->info.deviceCount ] = deviceInfo;
-
- winMmeHostApi->outputDeviceCount++;
- (*hostApi)->info.deviceCount++;
- }
- }
- }
- }
-
-
- InitializeDefaultDeviceIdsFromEnv( winMmeHostApi );
-
- (*hostApi)->Terminate = Terminate;
- (*hostApi)->OpenStream = OpenStream;
- (*hostApi)->IsFormatSupported = IsFormatSupported;
-
- PaUtil_InitializeStreamInterface( &winMmeHostApi->callbackStreamInterface, CloseStream, StartStream,
- StopStream, AbortStream, IsStreamStopped, IsStreamActive,
- GetStreamTime, GetStreamCpuLoad,
- PaUtil_DummyRead, PaUtil_DummyWrite,
- PaUtil_DummyGetReadAvailable, PaUtil_DummyGetWriteAvailable );
-
- PaUtil_InitializeStreamInterface( &winMmeHostApi->blockingStreamInterface, CloseStream, StartStream,
- StopStream, AbortStream, IsStreamStopped, IsStreamActive,
- GetStreamTime, PaUtil_DummyGetCpuLoad,
- ReadStream, WriteStream, GetStreamReadAvailable, GetStreamWriteAvailable );
-
- return result;
-
-error:
- if( winMmeHostApi )
- {
- if( winMmeHostApi->allocations )
- {
- PaUtil_FreeAllAllocations( winMmeHostApi->allocations );
- PaUtil_DestroyAllocationGroup( winMmeHostApi->allocations );
- }
-
- PaUtil_FreeMemory( winMmeHostApi );
- }
-
- return result;
-}
-
-
-static void Terminate( struct PaUtilHostApiRepresentation *hostApi )
-{
- PaWinMmeHostApiRepresentation *winMmeHostApi = (PaWinMmeHostApiRepresentation*)hostApi;
-
- if( winMmeHostApi->allocations )
- {
- PaUtil_FreeAllAllocations( winMmeHostApi->allocations );
- PaUtil_DestroyAllocationGroup( winMmeHostApi->allocations );
- }
-
- PaUtil_FreeMemory( winMmeHostApi );
-}
-
-
-static PaError IsFormatSupported( struct PaUtilHostApiRepresentation *hostApi,
- const PaStreamParameters *inputParameters,
- const PaStreamParameters *outputParameters,
- double sampleRate )
-{
- PaWinMmeHostApiRepresentation *winMmeHostApi = (PaWinMmeHostApiRepresentation*)hostApi;
- PaDeviceInfo *inputDeviceInfo, *outputDeviceInfo;
- int inputChannelCount, outputChannelCount;
- int inputMultipleDeviceChannelCount, outputMultipleDeviceChannelCount;
- PaSampleFormat inputSampleFormat, outputSampleFormat;
- PaWinMmeStreamInfo *inputStreamInfo, *outputStreamInfo;
- UINT winMmeInputDeviceId, winMmeOutputDeviceId;
- unsigned int i;
- PaError paerror;
-
- /* The calls to QueryFormatSupported below are intended to detect invalid
- sample rates. If we assume that the channel count and format are OK,
- then the only thing that could fail is the sample rate. This isn't
- strictly true, but I can't think of a better way to test that the
- sample rate is valid.
- */
-
- if( inputParameters )
- {
- inputChannelCount = inputParameters->channelCount;
- inputSampleFormat = inputParameters->sampleFormat;
- inputStreamInfo = inputParameters->hostApiSpecificStreamInfo;
-
- /* all standard sample formats are supported by the buffer adapter,
- this implementation doesn't support any custom sample formats */
- if( inputSampleFormat & paCustomFormat )
- return paSampleFormatNotSupported;
-
- if( inputParameters->device == paUseHostApiSpecificDeviceSpecification
- && inputStreamInfo && (inputStreamInfo->flags & paWinMmeUseMultipleDevices) )
- {
- inputMultipleDeviceChannelCount = 0;
- for( i=0; i< inputStreamInfo->deviceCount; ++i )
- {
- inputMultipleDeviceChannelCount += inputStreamInfo->devices[i].channelCount;
-
- inputDeviceInfo = hostApi->deviceInfos[ inputStreamInfo->devices[i].device ];
-
- /* check that input device can support inputChannelCount */
- if( inputStreamInfo->devices[i].channelCount <= 0
- || inputStreamInfo->devices[i].channelCount > inputDeviceInfo->maxInputChannels )
- return paInvalidChannelCount;
-
- /* test for valid sample rate, see comment above */
- winMmeInputDeviceId = LocalDeviceIndexToWinMmeDeviceId( winMmeHostApi, inputStreamInfo->devices[i].device );
- paerror = QueryFormatSupported( inputDeviceInfo, QueryInputWaveFormatEx, winMmeInputDeviceId, inputStreamInfo->devices[i].channelCount, sampleRate );
- if( paerror != paNoError )
- return paInvalidSampleRate;
- }
-
- if( inputMultipleDeviceChannelCount != inputChannelCount )
- return paIncompatibleHostApiSpecificStreamInfo;
- }
- else
- {
- if( inputStreamInfo && (inputStreamInfo->flags & paWinMmeUseMultipleDevices) )
- return paIncompatibleHostApiSpecificStreamInfo; /* paUseHostApiSpecificDeviceSpecification was not supplied as the input device */
-
- inputDeviceInfo = hostApi->deviceInfos[ inputParameters->device ];
-
- /* check that input device can support inputChannelCount */
- if( inputChannelCount > inputDeviceInfo->maxInputChannels )
- return paInvalidChannelCount;
-
- /* test for valid sample rate, see comment above */
- winMmeInputDeviceId = LocalDeviceIndexToWinMmeDeviceId( winMmeHostApi, inputParameters->device );
- paerror = QueryFormatSupported( inputDeviceInfo, QueryInputWaveFormatEx, winMmeInputDeviceId, inputChannelCount, sampleRate );
- if( paerror != paNoError )
- return paInvalidSampleRate;
- }
- }
-
- if( outputParameters )
- {
- outputChannelCount = outputParameters->channelCount;
- outputSampleFormat = outputParameters->sampleFormat;
- outputStreamInfo = outputParameters->hostApiSpecificStreamInfo;
-
- /* all standard sample formats are supported by the buffer adapter,
- this implementation doesn't support any custom sample formats */
- if( outputSampleFormat & paCustomFormat )
- return paSampleFormatNotSupported;
-
- if( outputParameters->device == paUseHostApiSpecificDeviceSpecification
- && outputStreamInfo && (outputStreamInfo->flags & paWinMmeUseMultipleDevices) )
- {
- outputMultipleDeviceChannelCount = 0;
- for( i=0; i< outputStreamInfo->deviceCount; ++i )
- {
- outputMultipleDeviceChannelCount += outputStreamInfo->devices[i].channelCount;
-
- outputDeviceInfo = hostApi->deviceInfos[ outputStreamInfo->devices[i].device ];
-
- /* check that output device can support outputChannelCount */
- if( outputStreamInfo->devices[i].channelCount <= 0
- || outputStreamInfo->devices[i].channelCount > outputDeviceInfo->maxOutputChannels )
- return paInvalidChannelCount;
-
- /* test for valid sample rate, see comment above */
- winMmeOutputDeviceId = LocalDeviceIndexToWinMmeDeviceId( winMmeHostApi, outputStreamInfo->devices[i].device );
- paerror = QueryFormatSupported( outputDeviceInfo, QueryOutputWaveFormatEx, winMmeOutputDeviceId, outputStreamInfo->devices[i].channelCount, sampleRate );
- if( paerror != paNoError )
- return paInvalidSampleRate;
- }
-
- if( outputMultipleDeviceChannelCount != outputChannelCount )
- return paIncompatibleHostApiSpecificStreamInfo;
- }
- else
- {
- if( outputStreamInfo && (outputStreamInfo->flags & paWinMmeUseMultipleDevices) )
- return paIncompatibleHostApiSpecificStreamInfo; /* paUseHostApiSpecificDeviceSpecification was not supplied as the output device */
-
- outputDeviceInfo = hostApi->deviceInfos[ outputParameters->device ];
-
- /* check that output device can support outputChannelCount */
- if( outputChannelCount > outputDeviceInfo->maxOutputChannels )
- return paInvalidChannelCount;
-
- /* test for valid sample rate, see comment above */
- winMmeOutputDeviceId = LocalDeviceIndexToWinMmeDeviceId( winMmeHostApi, outputParameters->device );
- paerror = QueryFormatSupported( outputDeviceInfo, QueryOutputWaveFormatEx, winMmeOutputDeviceId, outputChannelCount, sampleRate );
- if( paerror != paNoError )
- return paInvalidSampleRate;
- }
- }
-
- /*
- - if a full duplex stream is requested, check that the combination
- of input and output parameters is supported
-
- - check that the device supports sampleRate
-
- for mme all we can do is test that the input and output devices
- support the requested sample rate and number of channels. we
- cannot test for full duplex compatibility.
- */
-
- return paFormatIsSupported;
-}
-
-
-
-static void SelectBufferSizeAndCount( unsigned long baseBufferSize,
- unsigned long requestedLatency,
- unsigned long baseBufferCount, unsigned long minimumBufferCount,
- unsigned long maximumBufferSize, unsigned long *hostBufferSize,
- unsigned long *hostBufferCount )
-{
- unsigned long sizeMultiplier, bufferCount, latency;
- unsigned long nextLatency, nextBufferSize;
- int baseBufferSizeIsPowerOfTwo;
-
- sizeMultiplier = 1;
- bufferCount = baseBufferCount;
-
- /* count-1 below because latency is always determined by one less
- than the total number of buffers.
- */
- latency = (baseBufferSize * sizeMultiplier) * (bufferCount-1);
-
- if( latency > requestedLatency )
- {
-
- /* reduce number of buffers without falling below suggested latency */
-
- nextLatency = (baseBufferSize * sizeMultiplier) * (bufferCount-2);
- while( bufferCount > minimumBufferCount && nextLatency >= requestedLatency )
- {
- --bufferCount;
- nextLatency = (baseBufferSize * sizeMultiplier) * (bufferCount-2);
- }
-
- }else if( latency < requestedLatency ){
-
- baseBufferSizeIsPowerOfTwo = (! (baseBufferSize & (baseBufferSize - 1)));
- if( baseBufferSizeIsPowerOfTwo ){
-
- /* double size of buffers without exceeding requestedLatency */
-
- nextBufferSize = (baseBufferSize * (sizeMultiplier*2));
- nextLatency = nextBufferSize * (bufferCount-1);
- while( nextBufferSize <= maximumBufferSize
- && nextLatency < requestedLatency )
- {
- sizeMultiplier *= 2;
- nextBufferSize = (baseBufferSize * (sizeMultiplier*2));
- nextLatency = nextBufferSize * (bufferCount-1);
- }
-
- }else{
-
- /* increase size of buffers upto first excess of requestedLatency */
-
- nextBufferSize = (baseBufferSize * (sizeMultiplier+1));
- nextLatency = nextBufferSize * (bufferCount-1);
- while( nextBufferSize <= maximumBufferSize
- && nextLatency < requestedLatency )
- {
- ++sizeMultiplier;
- nextBufferSize = (baseBufferSize * (sizeMultiplier+1));
- nextLatency = nextBufferSize * (bufferCount-1);
- }
-
- if( nextLatency < requestedLatency )
- ++sizeMultiplier;
- }
-
- /* increase number of buffers until requestedLatency is reached */
-
- latency = (baseBufferSize * sizeMultiplier) * (bufferCount-1);
- while( latency < requestedLatency )
- {
- ++bufferCount;
- latency = (baseBufferSize * sizeMultiplier) * (bufferCount-1);
- }
- }
-
- *hostBufferSize = baseBufferSize * sizeMultiplier;
- *hostBufferCount = bufferCount;
-}
-
-
-static void ReselectBufferCount( unsigned long bufferSize,
- unsigned long requestedLatency,
- unsigned long baseBufferCount, unsigned long minimumBufferCount,
- unsigned long *hostBufferCount )
-{
- unsigned long bufferCount, latency;
- unsigned long nextLatency;
-
- bufferCount = baseBufferCount;
-
- /* count-1 below because latency is always determined by one less
- than the total number of buffers.
- */
- latency = bufferSize * (bufferCount-1);
-
- if( latency > requestedLatency )
- {
- /* reduce number of buffers without falling below suggested latency */
-
- nextLatency = bufferSize * (bufferCount-2);
- while( bufferCount > minimumBufferCount && nextLatency >= requestedLatency )
- {
- --bufferCount;
- nextLatency = bufferSize * (bufferCount-2);
- }
-
- }else if( latency < requestedLatency ){
-
- /* increase number of buffers until requestedLatency is reached */
-
- latency = bufferSize * (bufferCount-1);
- while( latency < requestedLatency )
- {
- ++bufferCount;
- latency = bufferSize * (bufferCount-1);
- }
- }
-
- *hostBufferCount = bufferCount;
-}
-
-
-/* CalculateBufferSettings() fills the framesPerHostInputBuffer, hostInputBufferCount,
- framesPerHostOutputBuffer and hostOutputBufferCount parameters based on the values
- of the other parameters.
-*/
-
-static PaError CalculateBufferSettings(
- unsigned long *framesPerHostInputBuffer, unsigned long *hostInputBufferCount,
- unsigned long *framesPerHostOutputBuffer, unsigned long *hostOutputBufferCount,
- int inputChannelCount, PaSampleFormat hostInputSampleFormat,
- PaTime suggestedInputLatency, PaWinMmeStreamInfo *inputStreamInfo,
- int outputChannelCount, PaSampleFormat hostOutputSampleFormat,
- PaTime suggestedOutputLatency, PaWinMmeStreamInfo *outputStreamInfo,
- double sampleRate, unsigned long framesPerBuffer )
-{
- PaError result = paNoError;
- int effectiveInputChannelCount, effectiveOutputChannelCount;
- int hostInputFrameSize = 0;
- unsigned int i;
-
- if( inputChannelCount > 0 )
- {
- int hostInputSampleSize = Pa_GetSampleSize( hostInputSampleFormat );
- if( hostInputSampleSize < 0 )
- {
- result = hostInputSampleSize;
- goto error;
- }
-
- if( inputStreamInfo
- && ( inputStreamInfo->flags & paWinMmeUseMultipleDevices ) )
- {
- /* set effectiveInputChannelCount to the largest number of
- channels on any one device.
- */
- effectiveInputChannelCount = 0;
- for( i=0; i< inputStreamInfo->deviceCount; ++i )
- {
- if( inputStreamInfo->devices[i].channelCount > effectiveInputChannelCount )
- effectiveInputChannelCount = inputStreamInfo->devices[i].channelCount;
- }
- }
- else
- {
- effectiveInputChannelCount = inputChannelCount;
- }
-
- hostInputFrameSize = hostInputSampleSize * effectiveInputChannelCount;
-
- if( inputStreamInfo
- && ( inputStreamInfo->flags & paWinMmeUseLowLevelLatencyParameters ) )
- {
- if( inputStreamInfo->bufferCount <= 0
- || inputStreamInfo->framesPerBuffer <= 0 )
- {
- result = paIncompatibleHostApiSpecificStreamInfo;
- goto error;
- }
-
- *framesPerHostInputBuffer = inputStreamInfo->framesPerBuffer;
- *hostInputBufferCount = inputStreamInfo->bufferCount;
- }
- else
- {
- unsigned long hostBufferSizeBytes, hostBufferCount;
- unsigned long minimumBufferCount = (outputChannelCount > 0)
- ? PA_MME_MIN_HOST_INPUT_BUFFER_COUNT_FULL_DUPLEX_
- : PA_MME_MIN_HOST_INPUT_BUFFER_COUNT_HALF_DUPLEX_;
-
- unsigned long maximumBufferSize = (long) ((PA_MME_MAX_HOST_BUFFER_SECS_ * sampleRate) * hostInputFrameSize);
- if( maximumBufferSize > PA_MME_MAX_HOST_BUFFER_BYTES_ )
- maximumBufferSize = PA_MME_MAX_HOST_BUFFER_BYTES_;
-
- /* compute the following in bytes, then convert back to frames */
-
- SelectBufferSizeAndCount(
- ((framesPerBuffer == paFramesPerBufferUnspecified)
- ? PA_MME_MIN_HOST_BUFFER_FRAMES_WHEN_UNSPECIFIED_
- : framesPerBuffer ) * hostInputFrameSize, /* baseBufferSize */
- ((unsigned long)(suggestedInputLatency * sampleRate)) * hostInputFrameSize, /* suggestedLatency */
- 4, /* baseBufferCount */
- minimumBufferCount, maximumBufferSize,
- &hostBufferSizeBytes, &hostBufferCount );
-
- *framesPerHostInputBuffer = hostBufferSizeBytes / hostInputFrameSize;
- *hostInputBufferCount = hostBufferCount;
- }
- }
- else
- {
- *framesPerHostInputBuffer = 0;
- *hostInputBufferCount = 0;
- }
-
- if( outputChannelCount > 0 )
- {
- if( outputStreamInfo
- && ( outputStreamInfo->flags & paWinMmeUseLowLevelLatencyParameters ) )
- {
- if( outputStreamInfo->bufferCount <= 0
- || outputStreamInfo->framesPerBuffer <= 0 )
- {
- result = paIncompatibleHostApiSpecificStreamInfo;
- goto error;
- }
-
- *framesPerHostOutputBuffer = outputStreamInfo->framesPerBuffer;
- *hostOutputBufferCount = outputStreamInfo->bufferCount;
-
-
- if( inputChannelCount > 0 ) /* full duplex */
- {
- if( *framesPerHostInputBuffer != *framesPerHostOutputBuffer )
- {
- if( inputStreamInfo
- && ( inputStreamInfo->flags & paWinMmeUseLowLevelLatencyParameters ) )
- {
- /* a custom StreamInfo was used for specifying both input
- and output buffer sizes, the larger buffer size
- must be a multiple of the smaller buffer size */
-
- if( *framesPerHostInputBuffer < *framesPerHostOutputBuffer )
- {
- if( *framesPerHostOutputBuffer % *framesPerHostInputBuffer != 0 )
- {
- result = paIncompatibleHostApiSpecificStreamInfo;
- goto error;
- }
- }
- else
- {
- assert( *framesPerHostInputBuffer > *framesPerHostOutputBuffer );
- if( *framesPerHostInputBuffer % *framesPerHostOutputBuffer != 0 )
- {
- result = paIncompatibleHostApiSpecificStreamInfo;
- goto error;
- }
- }
- }
- else
- {
- /* a custom StreamInfo was not used for specifying the input buffer size,
- so use the output buffer size, and approximately the same latency. */
-
- *framesPerHostInputBuffer = *framesPerHostOutputBuffer;
- *hostInputBufferCount = (((unsigned long)(suggestedInputLatency * sampleRate)) / *framesPerHostInputBuffer) + 1;
-
- if( *hostInputBufferCount < PA_MME_MIN_HOST_INPUT_BUFFER_COUNT_FULL_DUPLEX_ )
- *hostInputBufferCount = PA_MME_MIN_HOST_INPUT_BUFFER_COUNT_FULL_DUPLEX_;
- }
- }
- }
- }
- else
- {
- unsigned long hostBufferSizeBytes, hostBufferCount;
- unsigned long minimumBufferCount = PA_MME_MIN_HOST_OUTPUT_BUFFER_COUNT_;
- unsigned long maximumBufferSize;
- int hostOutputFrameSize;
- int hostOutputSampleSize;
-
- hostOutputSampleSize = Pa_GetSampleSize( hostOutputSampleFormat );
- if( hostOutputSampleSize < 0 )
- {
- result = hostOutputSampleSize;
- goto error;
- }
-
- if( outputStreamInfo
- && ( outputStreamInfo->flags & paWinMmeUseMultipleDevices ) )
- {
- /* set effectiveOutputChannelCount to the largest number of
- channels on any one device.
- */
- effectiveOutputChannelCount = 0;
- for( i=0; i< outputStreamInfo->deviceCount; ++i )
- {
- if( outputStreamInfo->devices[i].channelCount > effectiveOutputChannelCount )
- effectiveOutputChannelCount = outputStreamInfo->devices[i].channelCount;
- }
- }
- else
- {
- effectiveOutputChannelCount = outputChannelCount;
- }
-
- hostOutputFrameSize = hostOutputSampleSize * effectiveOutputChannelCount;
-
- maximumBufferSize = (long) ((PA_MME_MAX_HOST_BUFFER_SECS_ * sampleRate) * hostOutputFrameSize);
- if( maximumBufferSize > PA_MME_MAX_HOST_BUFFER_BYTES_ )
- maximumBufferSize = PA_MME_MAX_HOST_BUFFER_BYTES_;
-
-
- /* compute the following in bytes, then convert back to frames */
-
- SelectBufferSizeAndCount(
- ((framesPerBuffer == paFramesPerBufferUnspecified)
- ? PA_MME_MIN_HOST_BUFFER_FRAMES_WHEN_UNSPECIFIED_
- : framesPerBuffer ) * hostOutputFrameSize, /* baseBufferSize */
- ((unsigned long)(suggestedOutputLatency * sampleRate)) * hostOutputFrameSize, /* suggestedLatency */
- 4, /* baseBufferCount */
- minimumBufferCount,
- maximumBufferSize,
- &hostBufferSizeBytes, &hostBufferCount );
-
- *framesPerHostOutputBuffer = hostBufferSizeBytes / hostOutputFrameSize;
- *hostOutputBufferCount = hostBufferCount;
-
-
- if( inputChannelCount > 0 )
- {
- /* ensure that both input and output buffer sizes are the same.
- if they don't match at this stage, choose the smallest one
- and use that for input and output
- */
-
- if( *framesPerHostOutputBuffer != *framesPerHostInputBuffer )
- {
- if( framesPerHostInputBuffer < framesPerHostOutputBuffer )
- {
- unsigned long framesPerHostBuffer = *framesPerHostInputBuffer;
-
- minimumBufferCount = PA_MME_MIN_HOST_OUTPUT_BUFFER_COUNT_;
- ReselectBufferCount(
- framesPerHostBuffer * hostOutputFrameSize, /* bufferSize */
- ((unsigned long)(suggestedOutputLatency * sampleRate)) * hostOutputFrameSize, /* suggestedLatency */
- 4, /* baseBufferCount */
- minimumBufferCount,
- &hostBufferCount );
-
- *framesPerHostOutputBuffer = framesPerHostBuffer;
- *hostOutputBufferCount = hostBufferCount;
- }
- else
- {
- unsigned long framesPerHostBuffer = *framesPerHostOutputBuffer;
-
- minimumBufferCount = PA_MME_MIN_HOST_INPUT_BUFFER_COUNT_FULL_DUPLEX_;
- ReselectBufferCount(
- framesPerHostBuffer * hostInputFrameSize, /* bufferSize */
- ((unsigned long)(suggestedInputLatency * sampleRate)) * hostInputFrameSize, /* suggestedLatency */
- 4, /* baseBufferCount */
- minimumBufferCount,
- &hostBufferCount );
-
- *framesPerHostInputBuffer = framesPerHostBuffer;
- *hostInputBufferCount = hostBufferCount;
- }
- }
- }
- }
- }
- else
- {
- *framesPerHostOutputBuffer = 0;
- *hostOutputBufferCount = 0;
- }
-
-error:
- return result;
-}
-
-
-typedef struct
-{
- HANDLE bufferEvent;
- void *waveHandles;
- unsigned int deviceCount;
- /* unsigned int channelCount; */
- WAVEHDR **waveHeaders; /* waveHeaders[device][buffer] */
- unsigned int bufferCount;
- unsigned int currentBufferIndex;
- unsigned int framesPerBuffer;
- unsigned int framesUsedInCurrentBuffer;
-}PaWinMmeSingleDirectionHandlesAndBuffers;
-
-/* prototypes for functions operating on PaWinMmeSingleDirectionHandlesAndBuffers */
-
-static void InitializeSingleDirectionHandlesAndBuffers( PaWinMmeSingleDirectionHandlesAndBuffers *handlesAndBuffers );
-static PaError InitializeWaveHandles( PaWinMmeHostApiRepresentation *winMmeHostApi,
- PaWinMmeSingleDirectionHandlesAndBuffers *handlesAndBuffers,
- unsigned long bytesPerHostSample,
- double sampleRate, PaWinMmeDeviceAndChannelCount *devices,
- unsigned int deviceCount, int isInput );
-static PaError TerminateWaveHandles( PaWinMmeSingleDirectionHandlesAndBuffers *handlesAndBuffers, int isInput, int currentlyProcessingAnError );
-static PaError InitializeWaveHeaders( PaWinMmeSingleDirectionHandlesAndBuffers *handlesAndBuffers,
- unsigned long hostBufferCount,
- PaSampleFormat hostSampleFormat,
- unsigned long framesPerHostBuffer,
- PaWinMmeDeviceAndChannelCount *devices,
- int isInput );
-static void TerminateWaveHeaders( PaWinMmeSingleDirectionHandlesAndBuffers *handlesAndBuffers, int isInput );
-
-
-static void InitializeSingleDirectionHandlesAndBuffers( PaWinMmeSingleDirectionHandlesAndBuffers *handlesAndBuffers )
-{
- handlesAndBuffers->bufferEvent = 0;
- handlesAndBuffers->waveHandles = 0;
- handlesAndBuffers->deviceCount = 0;
- handlesAndBuffers->waveHeaders = 0;
- handlesAndBuffers->bufferCount = 0;
-}
-
-static PaError InitializeWaveHandles( PaWinMmeHostApiRepresentation *winMmeHostApi,
- PaWinMmeSingleDirectionHandlesAndBuffers *handlesAndBuffers,
- unsigned long bytesPerHostSample,
- double sampleRate, PaWinMmeDeviceAndChannelCount *devices,
- unsigned int deviceCount, int isInput )
-{
- PaError result;
- MMRESULT mmresult;
- unsigned long bytesPerFrame;
- WAVEFORMATEX wfx;
- signed int i;
-
- /* for error cleanup we expect that InitializeSingleDirectionHandlesAndBuffers()
- has already been called to zero some fields */
-
- result = CreateEventWithPaError( &handlesAndBuffers->bufferEvent, NULL, FALSE, FALSE, NULL );
- if( result != paNoError ) goto error;
-
- if( isInput )
- handlesAndBuffers->waveHandles = (void*)PaUtil_AllocateMemory( sizeof(HWAVEIN) * deviceCount );
- else
- handlesAndBuffers->waveHandles = (void*)PaUtil_AllocateMemory( sizeof(HWAVEOUT) * deviceCount );
- if( !handlesAndBuffers->waveHandles )
- {
- result = paInsufficientMemory;
- goto error;
- }
-
- handlesAndBuffers->deviceCount = deviceCount;
-
- for( i = 0; i < (signed int)deviceCount; ++i )
- {
- if( isInput )
- ((HWAVEIN*)handlesAndBuffers->waveHandles)[i] = 0;
- else
- ((HWAVEOUT*)handlesAndBuffers->waveHandles)[i] = 0;
- }
-
- wfx.wFormatTag = WAVE_FORMAT_PCM;
- wfx.nSamplesPerSec = (DWORD) sampleRate;
- wfx.cbSize = 0;
-
- for( i = 0; i < (signed int)deviceCount; ++i )
- {
- UINT winMmeDeviceId;
-
- winMmeDeviceId = LocalDeviceIndexToWinMmeDeviceId( winMmeHostApi, devices[i].device );
- wfx.nChannels = (WORD)devices[i].channelCount;
-
- bytesPerFrame = wfx.nChannels * bytesPerHostSample;
-
- wfx.nAvgBytesPerSec = (DWORD)(bytesPerFrame * sampleRate);
- wfx.nBlockAlign = (WORD)bytesPerFrame;
- wfx.wBitsPerSample = (WORD)((bytesPerFrame/wfx.nChannels) * 8);
-
- /* REVIEW: consider not firing an event for input when a full duplex
- stream is being used. this would probably depend on the
- neverDropInput flag. */
-
- if( isInput )
- mmresult = waveInOpen( &((HWAVEIN*)handlesAndBuffers->waveHandles)[i], winMmeDeviceId, &wfx,
- (DWORD)handlesAndBuffers->bufferEvent, (DWORD)0, CALLBACK_EVENT );
- else
- mmresult = waveOutOpen( &((HWAVEOUT*)handlesAndBuffers->waveHandles)[i], winMmeDeviceId, &wfx,
- (DWORD)handlesAndBuffers->bufferEvent, (DWORD)0, CALLBACK_EVENT );
-
- if( mmresult != MMSYSERR_NOERROR )
- {
- switch( mmresult )
- {
- case MMSYSERR_ALLOCATED: /* Specified resource is already allocated. */
- result = paDeviceUnavailable;
- break;
- case MMSYSERR_NODRIVER: /* No device driver is present. */
- result = paDeviceUnavailable;
- break;
- case MMSYSERR_NOMEM: /* Unable to allocate or lock memory. */
- result = paInsufficientMemory;
- break;
-
- case MMSYSERR_BADDEVICEID: /* Specified device identifier is out of range. */
- /* falls through */
- case WAVERR_BADFORMAT: /* Attempted to open with an unsupported waveform-audio format. */
- /* falls through */
- default:
- result = paUnanticipatedHostError;
- if( isInput )
- {
- PA_MME_SET_LAST_WAVEIN_ERROR( mmresult );
- }
- else
- {
- PA_MME_SET_LAST_WAVEOUT_ERROR( mmresult );
- }
- }
- goto error;
- }
- }
-
- return result;
-
-error:
- TerminateWaveHandles( handlesAndBuffers, isInput, 1 /* currentlyProcessingAnError */ );
-
- return result;
-}
-
-
-static PaError TerminateWaveHandles( PaWinMmeSingleDirectionHandlesAndBuffers *handlesAndBuffers, int isInput, int currentlyProcessingAnError )
-{
- PaError result = paNoError;
- MMRESULT mmresult;
- signed int i;
-
- if( handlesAndBuffers->waveHandles )
- {
- for( i = handlesAndBuffers->deviceCount-1; i >= 0; --i )
- {
- if( isInput )
- {
- if( ((HWAVEIN*)handlesAndBuffers->waveHandles)[i] )
- mmresult = waveInClose( ((HWAVEIN*)handlesAndBuffers->waveHandles)[i] );
- }
- else
- {
- if( ((HWAVEOUT*)handlesAndBuffers->waveHandles)[i] )
- mmresult = waveOutClose( ((HWAVEOUT*)handlesAndBuffers->waveHandles)[i] );
- }
-
- if( mmresult != MMSYSERR_NOERROR &&
- !currentlyProcessingAnError ) /* don't update the error state if we're already processing an error */
- {
- result = paUnanticipatedHostError;
- if( isInput )
- {
- PA_MME_SET_LAST_WAVEIN_ERROR( mmresult );
- }
- else
- {
- PA_MME_SET_LAST_WAVEOUT_ERROR( mmresult );
- }
- /* note that we don't break here, we try to continue closing devices */
- }
- }
-
- PaUtil_FreeMemory( handlesAndBuffers->waveHandles );
- handlesAndBuffers->waveHandles = 0;
- }
-
- if( handlesAndBuffers->bufferEvent )
- {
- result = CloseHandleWithPaError( handlesAndBuffers->bufferEvent );
- handlesAndBuffers->bufferEvent = 0;
- }
-
- return result;
-}
-
-
-static PaError InitializeWaveHeaders( PaWinMmeSingleDirectionHandlesAndBuffers *handlesAndBuffers,
- unsigned long hostBufferCount,
- PaSampleFormat hostSampleFormat,
- unsigned long framesPerHostBuffer,
- PaWinMmeDeviceAndChannelCount *devices,
- int isInput )
-{
- PaError result = paNoError;
- MMRESULT mmresult;
- WAVEHDR *deviceWaveHeaders;
- signed int i, j;
-
- /* for error cleanup we expect that InitializeSingleDirectionHandlesAndBuffers()
- has already been called to zero some fields */
-
-
- /* allocate an array of pointers to arrays of wave headers, one array of
- wave headers per device */
- handlesAndBuffers->waveHeaders = (WAVEHDR**)PaUtil_AllocateMemory( sizeof(WAVEHDR*) * handlesAndBuffers->deviceCount );
- if( !handlesAndBuffers->waveHeaders )
- {
- result = paInsufficientMemory;
- goto error;
- }
-
- for( i = 0; i < (signed int)handlesAndBuffers->deviceCount; ++i )
- handlesAndBuffers->waveHeaders[i] = 0;
-
- handlesAndBuffers->bufferCount = hostBufferCount;
-
- for( i = 0; i < (signed int)handlesAndBuffers->deviceCount; ++i )
- {
- int bufferBytes = Pa_GetSampleSize( hostSampleFormat ) *
- framesPerHostBuffer * devices[i].channelCount;
- if( bufferBytes < 0 )
- {
- result = paInternalError;
- goto error;
- }
-
- /* Allocate an array of wave headers for device i */
- deviceWaveHeaders = (WAVEHDR *) PaUtil_AllocateMemory( sizeof(WAVEHDR)*hostBufferCount );
- if( !deviceWaveHeaders )
- {
- result = paInsufficientMemory;
- goto error;
- }
-
- for( j=0; j < (signed int)hostBufferCount; ++j )
- deviceWaveHeaders[j].lpData = 0;
-
- handlesAndBuffers->waveHeaders[i] = deviceWaveHeaders;
-
- /* Allocate a buffer for each wave header */
- for( j=0; j < (signed int)hostBufferCount; ++j )
- {
- deviceWaveHeaders[j].lpData = (char *)PaUtil_AllocateMemory( bufferBytes );
- if( !deviceWaveHeaders[j].lpData )
- {
- result = paInsufficientMemory;
- goto error;
- }
- deviceWaveHeaders[j].dwBufferLength = bufferBytes;
- deviceWaveHeaders[j].dwUser = 0xFFFFFFFF; /* indicates that *PrepareHeader() has not yet been called, for error clean up code */
-
- if( isInput )
- {
- mmresult = waveInPrepareHeader( ((HWAVEIN*)handlesAndBuffers->waveHandles)[i], &deviceWaveHeaders[j], sizeof(WAVEHDR) );
- if( mmresult != MMSYSERR_NOERROR )
- {
- result = paUnanticipatedHostError;
- PA_MME_SET_LAST_WAVEIN_ERROR( mmresult );
- goto error;
- }
- }
- else /* output */
- {
- mmresult = waveOutPrepareHeader( ((HWAVEOUT*)handlesAndBuffers->waveHandles)[i], &deviceWaveHeaders[j], sizeof(WAVEHDR) );
- if( mmresult != MMSYSERR_NOERROR )
- {
- result = paUnanticipatedHostError;
- PA_MME_SET_LAST_WAVEIN_ERROR( mmresult );
- goto error;
- }
- }
- deviceWaveHeaders[j].dwUser = devices[i].channelCount;
- }
- }
-
- return result;
-
-error:
- TerminateWaveHeaders( handlesAndBuffers, isInput );
-
- return result;
-}
-
-
-static void TerminateWaveHeaders( PaWinMmeSingleDirectionHandlesAndBuffers *handlesAndBuffers, int isInput )
-{
- signed int i, j;
- WAVEHDR *deviceWaveHeaders;
-
- if( handlesAndBuffers->waveHeaders )
- {
- for( i = handlesAndBuffers->deviceCount-1; i >= 0 ; --i )
- {
- deviceWaveHeaders = handlesAndBuffers->waveHeaders[i]; /* wave headers for device i */
- if( deviceWaveHeaders )
- {
- for( j = handlesAndBuffers->bufferCount-1; j >= 0; --j )
- {
- if( deviceWaveHeaders[j].lpData )
- {
- if( deviceWaveHeaders[j].dwUser != 0xFFFFFFFF )
- {
- if( isInput )
- waveInUnprepareHeader( ((HWAVEIN*)handlesAndBuffers->waveHandles)[i], &deviceWaveHeaders[j], sizeof(WAVEHDR) );
- else
- waveOutUnprepareHeader( ((HWAVEOUT*)handlesAndBuffers->waveHandles)[i], &deviceWaveHeaders[j], sizeof(WAVEHDR) );
- }
-
- PaUtil_FreeMemory( deviceWaveHeaders[j].lpData );
- }
- }
-
- PaUtil_FreeMemory( deviceWaveHeaders );
- }
- }
-
- PaUtil_FreeMemory( handlesAndBuffers->waveHeaders );
- handlesAndBuffers->waveHeaders = 0;
- }
-}
-
-
-
-/* PaWinMmeStream - a stream data structure specifically for this implementation */
-/* note that struct PaWinMmeStream is typedeffed to PaWinMmeStream above. */
-struct PaWinMmeStream
-{
- PaUtilStreamRepresentation streamRepresentation;
- PaUtilCpuLoadMeasurer cpuLoadMeasurer;
- PaUtilBufferProcessor bufferProcessor;
-
- int primeStreamUsingCallback;
-
- PaWinMmeSingleDirectionHandlesAndBuffers input;
- PaWinMmeSingleDirectionHandlesAndBuffers output;
-
- /* Processing thread management -------------- */
- HANDLE abortEvent;
- HANDLE processingThread;
- DWORD processingThreadId;
-
- char throttleProcessingThreadOnOverload; /* 0 -> don't throtte, non-0 -> throttle */
- int processingThreadPriority;
- int highThreadPriority;
- int throttledThreadPriority;
- unsigned long throttledSleepMsecs;
-
- int isStopped;
- volatile int isActive;
- volatile int stopProcessing; /* stop thread once existing buffers have been returned */
- volatile int abortProcessing; /* stop thread immediately */
-
- DWORD allBuffersDurationMs; /* used to calculate timeouts */
-};
-
-/* updates deviceCount if PaWinMmeUseMultipleDevices is used */
-
-static PaError ValidateWinMmeSpecificStreamInfo(
- const PaStreamParameters *streamParameters,
- const PaWinMmeStreamInfo *streamInfo,
- char *throttleProcessingThreadOnOverload,
- unsigned long *deviceCount )
-{
- if( streamInfo )
- {
- if( streamInfo->size != sizeof( PaWinMmeStreamInfo )
- || streamInfo->version != 1 )
- {
- return paIncompatibleHostApiSpecificStreamInfo;
- }
-
- if( streamInfo->flags & paWinMmeDontThrottleOverloadedProcessingThread )
- *throttleProcessingThreadOnOverload = 0;
-
- if( streamInfo->flags & paWinMmeUseMultipleDevices )
- {
- if( streamParameters->device != paUseHostApiSpecificDeviceSpecification )
- return paInvalidDevice;
-
- *deviceCount = streamInfo->deviceCount;
- }
- }
-
- return paNoError;
-}
-
-static PaError RetrieveDevicesFromStreamParameters(
- struct PaUtilHostApiRepresentation *hostApi,
- const PaStreamParameters *streamParameters,
- const PaWinMmeStreamInfo *streamInfo,
- PaWinMmeDeviceAndChannelCount *devices,
- unsigned long deviceCount )
-{
- PaError result = paNoError;
- unsigned int i;
- int totalChannelCount;
- PaDeviceIndex hostApiDevice;
-
- if( streamInfo && streamInfo->flags & paWinMmeUseMultipleDevices )
- {
- totalChannelCount = 0;
- for( i=0; i < deviceCount; ++i )
- {
- /* validate that the device number is within range */
- result = PaUtil_DeviceIndexToHostApiDeviceIndex( &hostApiDevice,
- streamInfo->devices[i].device, hostApi );
- if( result != paNoError )
- return result;
-
- devices[i].device = hostApiDevice;
- devices[i].channelCount = streamInfo->devices[i].channelCount;
-
- totalChannelCount += devices[i].channelCount;
- }
-
- if( totalChannelCount != streamParameters->channelCount )
- {
- /* channelCount must match total channels specified by multiple devices */
- return paInvalidChannelCount; /* REVIEW use of this error code */
- }
- }
- else
- {
- devices[0].device = streamParameters->device;
- devices[0].channelCount = streamParameters->channelCount;
- }
-
- return result;
-}
-
-static PaError ValidateInputChannelCounts(
- struct PaUtilHostApiRepresentation *hostApi,
- PaWinMmeDeviceAndChannelCount *devices,
- unsigned long deviceCount )
-{
- unsigned int i;
-
- for( i=0; i < deviceCount; ++i )
- {
- if( devices[i].channelCount < 1 || devices[i].channelCount
- > hostApi->deviceInfos[ devices[i].device ]->maxInputChannels )
- return paInvalidChannelCount;
- }
-
- return paNoError;
-}
-
-static PaError ValidateOutputChannelCounts(
- struct PaUtilHostApiRepresentation *hostApi,
- PaWinMmeDeviceAndChannelCount *devices,
- unsigned long deviceCount )
-{
- unsigned int i;
-
- for( i=0; i < deviceCount; ++i )
- {
- if( devices[i].channelCount < 1 || devices[i].channelCount
- > hostApi->deviceInfos[ devices[i].device ]->maxOutputChannels )
- return paInvalidChannelCount;
- }
-
- return paNoError;
-}
-
-
-/* the following macros are intended to improve the readability of the following code */
-#define PA_IS_INPUT_STREAM_( stream ) ( stream ->input.waveHandles )
-#define PA_IS_OUTPUT_STREAM_( stream ) ( stream ->output.waveHandles )
-#define PA_IS_FULL_DUPLEX_STREAM_( stream ) ( stream ->input.waveHandles && stream ->output.waveHandles )
-#define PA_IS_HALF_DUPLEX_STREAM_( stream ) ( !(stream ->input.waveHandles && stream ->output.waveHandles) )
-
-static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi,
- PaStream** s,
- const PaStreamParameters *inputParameters,
- const PaStreamParameters *outputParameters,
- double sampleRate,
- unsigned long framesPerBuffer,
- PaStreamFlags streamFlags,
- PaStreamCallback *streamCallback,
- void *userData )
-{
- PaError result;
- PaWinMmeHostApiRepresentation *winMmeHostApi = (PaWinMmeHostApiRepresentation*)hostApi;
- PaWinMmeStream *stream = 0;
- int bufferProcessorIsInitialized = 0;
- int streamRepresentationIsInitialized = 0;
- PaSampleFormat hostInputSampleFormat, hostOutputSampleFormat;
- int inputChannelCount, outputChannelCount;
- PaSampleFormat inputSampleFormat, outputSampleFormat;
- double suggestedInputLatency, suggestedOutputLatency;
- PaWinMmeStreamInfo *inputStreamInfo, *outputStreamInfo;
- unsigned long framesPerHostInputBuffer;
- unsigned long hostInputBufferCount;
- unsigned long framesPerHostOutputBuffer;
- unsigned long hostOutputBufferCount;
- unsigned long framesPerBufferProcessorCall;
- PaWinMmeDeviceAndChannelCount *inputDevices = 0; /* contains all devices and channel counts as local host api ids, even when PaWinMmeUseMultipleDevices is not used */
- unsigned long inputDeviceCount = 0;
- PaWinMmeDeviceAndChannelCount *outputDevices = 0;
- unsigned long outputDeviceCount = 0; /* contains all devices and channel counts as local host api ids, even when PaWinMmeUseMultipleDevices is not used */
- char throttleProcessingThreadOnOverload = 1;
-
-
- if( inputParameters )
- {
- inputChannelCount = inputParameters->channelCount;
- inputSampleFormat = inputParameters->sampleFormat;
- suggestedInputLatency = inputParameters->suggestedLatency;
-
- inputDeviceCount = 1;
-
- /* validate input hostApiSpecificStreamInfo */
- inputStreamInfo = (PaWinMmeStreamInfo*)inputParameters->hostApiSpecificStreamInfo;
- result = ValidateWinMmeSpecificStreamInfo( inputParameters, inputStreamInfo,
- &throttleProcessingThreadOnOverload,
- &inputDeviceCount );
- if( result != paNoError ) return result;
-
- inputDevices = (PaWinMmeDeviceAndChannelCount*)alloca( sizeof(PaWinMmeDeviceAndChannelCount) * inputDeviceCount );
- if( !inputDevices ) return paInsufficientMemory;
-
- result = RetrieveDevicesFromStreamParameters( hostApi, inputParameters, inputStreamInfo, inputDevices, inputDeviceCount );
- if( result != paNoError ) return result;
-
- result = ValidateInputChannelCounts( hostApi, inputDevices, inputDeviceCount );
- if( result != paNoError ) return result;
-
- hostInputSampleFormat =
- PaUtil_SelectClosestAvailableFormat( paInt16 /* native formats */, inputSampleFormat );
- }
- else
- {
- inputChannelCount = 0;
- inputSampleFormat = 0;
- suggestedInputLatency = 0.;
- inputStreamInfo = 0;
- hostInputSampleFormat = 0;
- }
-
-
- if( outputParameters )
- {
- outputChannelCount = outputParameters->channelCount;
- outputSampleFormat = outputParameters->sampleFormat;
- suggestedOutputLatency = outputParameters->suggestedLatency;
-
- outputDeviceCount = 1;
-
- /* validate output hostApiSpecificStreamInfo */
- outputStreamInfo = (PaWinMmeStreamInfo*)outputParameters->hostApiSpecificStreamInfo;
- result = ValidateWinMmeSpecificStreamInfo( outputParameters, outputStreamInfo,
- &throttleProcessingThreadOnOverload,
- &outputDeviceCount );
- if( result != paNoError ) return result;
-
- outputDevices = (PaWinMmeDeviceAndChannelCount*)alloca( sizeof(PaWinMmeDeviceAndChannelCount) * outputDeviceCount );
- if( !outputDevices ) return paInsufficientMemory;
-
- result = RetrieveDevicesFromStreamParameters( hostApi, outputParameters, outputStreamInfo, outputDevices, outputDeviceCount );
- if( result != paNoError ) return result;
-
- result = ValidateOutputChannelCounts( hostApi, outputDevices, outputDeviceCount );
- if( result != paNoError ) return result;
-
- hostOutputSampleFormat =
- PaUtil_SelectClosestAvailableFormat( paInt16 /* native formats */, outputSampleFormat );
- }
- else
- {
- outputChannelCount = 0;
- outputSampleFormat = 0;
- outputStreamInfo = 0;
- hostOutputSampleFormat = 0;
- suggestedOutputLatency = 0.;
- }
-
-
- /*
- IMPLEMENT ME:
- - alter sampleRate to a close allowable rate if possible / necessary
- */
-
-
- /* validate platform specific flags */
- if( (streamFlags & paPlatformSpecificFlags) != 0 )
- return paInvalidFlag; /* unexpected platform specific flag */
-
-
- result = CalculateBufferSettings( &framesPerHostInputBuffer, &hostInputBufferCount,
- &framesPerHostOutputBuffer, &hostOutputBufferCount,
- inputChannelCount, hostInputSampleFormat, suggestedInputLatency, inputStreamInfo,
- outputChannelCount, hostOutputSampleFormat, suggestedOutputLatency, outputStreamInfo,
- sampleRate, framesPerBuffer );
- if( result != paNoError ) goto error;
-
-
- stream = (PaWinMmeStream*)PaUtil_AllocateMemory( sizeof(PaWinMmeStream) );
- if( !stream )
- {
- result = paInsufficientMemory;
- goto error;
- }
-
- InitializeSingleDirectionHandlesAndBuffers( &stream->input );
- InitializeSingleDirectionHandlesAndBuffers( &stream->output );
-
- stream->abortEvent = 0;
- stream->processingThread = 0;
-
- stream->throttleProcessingThreadOnOverload = throttleProcessingThreadOnOverload;
-
- PaUtil_InitializeStreamRepresentation( &stream->streamRepresentation,
- ( (streamCallback)
- ? &winMmeHostApi->callbackStreamInterface
- : &winMmeHostApi->blockingStreamInterface ),
- streamCallback, userData );
- streamRepresentationIsInitialized = 1;
-
- PaUtil_InitializeCpuLoadMeasurer( &stream->cpuLoadMeasurer, sampleRate );
-
-
- if( inputParameters && outputParameters ) /* full duplex */
- {
- if( framesPerHostInputBuffer < framesPerHostOutputBuffer )
- {
- assert( (framesPerHostOutputBuffer % framesPerHostInputBuffer) == 0 ); /* CalculateBufferSettings() should guarantee this condition */
-
- framesPerBufferProcessorCall = framesPerHostInputBuffer;
- }
- else
- {
- assert( (framesPerHostInputBuffer % framesPerHostOutputBuffer) == 0 ); /* CalculateBufferSettings() should guarantee this condition */
-
- framesPerBufferProcessorCall = framesPerHostOutputBuffer;
- }
- }
- else if( inputParameters )
- {
- framesPerBufferProcessorCall = framesPerHostInputBuffer;
- }
- else if( outputParameters )
- {
- framesPerBufferProcessorCall = framesPerHostOutputBuffer;
- }
-
- stream->input.framesPerBuffer = framesPerHostInputBuffer;
- stream->output.framesPerBuffer = framesPerHostOutputBuffer;
-
- result = PaUtil_InitializeBufferProcessor( &stream->bufferProcessor,
- inputChannelCount, inputSampleFormat, hostInputSampleFormat,
- outputChannelCount, outputSampleFormat, hostOutputSampleFormat,
- sampleRate, streamFlags, framesPerBuffer,
- framesPerBufferProcessorCall, paUtilFixedHostBufferSize,
- streamCallback, userData );
- if( result != paNoError ) goto error;
-
- bufferProcessorIsInitialized = 1;
-
- stream->streamRepresentation.streamInfo.inputLatency =
- (double)(PaUtil_GetBufferProcessorInputLatency(&stream->bufferProcessor)
- +(framesPerHostInputBuffer * (hostInputBufferCount-1))) / sampleRate;
- stream->streamRepresentation.streamInfo.outputLatency =
- (double)(PaUtil_GetBufferProcessorOutputLatency(&stream->bufferProcessor)
- +(framesPerHostOutputBuffer * (hostOutputBufferCount-1))) / sampleRate;
- stream->streamRepresentation.streamInfo.sampleRate = sampleRate;
-
- stream->primeStreamUsingCallback = ( (streamFlags&paPrimeOutputBuffersUsingStreamCallback) && streamCallback ) ? 1 : 0;
-
- /* time to sleep when throttling due to >100% cpu usage.
- -a quater of a buffer's duration */
- stream->throttledSleepMsecs =
- (unsigned long)(stream->bufferProcessor.framesPerHostBuffer *
- stream->bufferProcessor.samplePeriod * .25 * 1000);
-
- stream->isStopped = 1;
- stream->isActive = 0;
-
-
- /* for maximum compatibility with multi-device multichannel drivers,
- we first open all devices, then we prepare all buffers, finally
- we start all devices ( in StartStream() ). teardown in reverse order.
- */
-
- if( inputParameters )
- {
- result = InitializeWaveHandles( winMmeHostApi, &stream->input,
- stream->bufferProcessor.bytesPerHostInputSample, sampleRate,
- inputDevices, inputDeviceCount, 1 /* isInput */ );
- if( result != paNoError ) goto error;
- }
-
- if( outputParameters )
- {
- result = InitializeWaveHandles( winMmeHostApi, &stream->output,
- stream->bufferProcessor.bytesPerHostOutputSample, sampleRate,
- outputDevices, outputDeviceCount, 0 /* isInput */ );
- if( result != paNoError ) goto error;
- }
-
- if( inputParameters )
- {
- result = InitializeWaveHeaders( &stream->input, hostInputBufferCount,
- hostInputSampleFormat, framesPerHostInputBuffer, inputDevices, 1 /* isInput */ );
- if( result != paNoError ) goto error;
- }
-
- if( outputParameters )
- {
- result = InitializeWaveHeaders( &stream->output, hostOutputBufferCount,
- hostOutputSampleFormat, framesPerHostOutputBuffer, outputDevices, 0 /* not isInput */ );
- if( result != paNoError ) goto error;
-
- stream->allBuffersDurationMs = (DWORD) (1000.0 * (framesPerHostOutputBuffer * stream->output.bufferCount) / sampleRate);
- }
- else
- {
- stream->allBuffersDurationMs = (DWORD) (1000.0 * (framesPerHostInputBuffer * stream->input.bufferCount) / sampleRate);
- }
-
-
- if( streamCallback )
- {
- /* abort event is only needed for callback streams */
- result = CreateEventWithPaError( &stream->abortEvent, NULL, TRUE, FALSE, NULL );
- if( result != paNoError ) goto error;
- }
-
- *s = (PaStream*)stream;
-
- return result;
-
-error:
-
- if( stream )
- {
- if( stream->abortEvent )
- CloseHandle( stream->abortEvent );
-
- TerminateWaveHeaders( &stream->output, 0 /* not isInput */ );
- TerminateWaveHeaders( &stream->input, 1 /* isInput */ );
-
- TerminateWaveHandles( &stream->output, 0 /* not isInput */, 1 /* currentlyProcessingAnError */ );
- TerminateWaveHandles( &stream->input, 1 /* isInput */, 1 /* currentlyProcessingAnError */ );
-
- if( bufferProcessorIsInitialized )
- PaUtil_TerminateBufferProcessor( &stream->bufferProcessor );
-
- if( streamRepresentationIsInitialized )
- PaUtil_TerminateStreamRepresentation( &stream->streamRepresentation );
-
- PaUtil_FreeMemory( stream );
- }
-
- return result;
-}
-
-
-/* return non-zero if all current buffers are done */
-static int BuffersAreDone( WAVEHDR **waveHeaders, unsigned int deviceCount, int bufferIndex )
-{
- unsigned int i;
-
- for( i=0; i < deviceCount; ++i )
- {
- if( !(waveHeaders[i][ bufferIndex ].dwFlags & WHDR_DONE) )
- {
- return 0;
- }
- }
-
- return 1;
-}
-
-static int CurrentInputBuffersAreDone( PaWinMmeStream *stream )
-{
- return BuffersAreDone( stream->input.waveHeaders, stream->input.deviceCount, stream->input.currentBufferIndex );
-}
-
-static int CurrentOutputBuffersAreDone( PaWinMmeStream *stream )
-{
- return BuffersAreDone( stream->output.waveHeaders, stream->output.deviceCount, stream->output.currentBufferIndex );
-}
-
-
-/* return non-zero if any buffers are queued */
-static int NoBuffersAreQueued( PaWinMmeSingleDirectionHandlesAndBuffers *handlesAndBuffers )
-{
- unsigned int i, j;
-
- if( handlesAndBuffers->waveHandles )
- {
- for( i=0; i < handlesAndBuffers->bufferCount; ++i )
- {
- for( j=0; j < handlesAndBuffers->deviceCount; ++j )
- {
- if( !( handlesAndBuffers->waveHeaders[ j ][ i ].dwFlags & WHDR_DONE) )
- {
- return 0;
- }
- }
- }
- }
-
- return 1;
-}
-
-
-#define PA_CIRCULAR_INCREMENT_( current, max )\
- ( (((current) + 1) >= (max)) ? (0) : (current+1) )
-
-#define PA_CIRCULAR_DECREMENT_( current, max )\
- ( ((current) == 0) ? ((max)-1) : (current-1) )
-
-
-static signed long GetAvailableFrames( PaWinMmeSingleDirectionHandlesAndBuffers *handlesAndBuffers )
-{
- signed long result = 0;
- unsigned int i;
-
- if( BuffersAreDone( handlesAndBuffers->waveHeaders, handlesAndBuffers->deviceCount, handlesAndBuffers->currentBufferIndex ) )
- {
- /* we could calculate the following in O(1) if we kept track of the
- last done buffer */
- result = handlesAndBuffers->framesPerBuffer - handlesAndBuffers->framesUsedInCurrentBuffer;
-
- i = PA_CIRCULAR_INCREMENT_( handlesAndBuffers->currentBufferIndex, handlesAndBuffers->bufferCount );
- while( i != handlesAndBuffers->currentBufferIndex )
- {
- if( BuffersAreDone( handlesAndBuffers->waveHeaders, handlesAndBuffers->deviceCount, i ) )
- {
- result += handlesAndBuffers->framesPerBuffer;
- i = PA_CIRCULAR_INCREMENT_( i, handlesAndBuffers->bufferCount );
- }
- else
- break;
- }
- }
-
- return result;
-}
-
-
-static PaError AdvanceToNextInputBuffer( PaWinMmeStream *stream )
-{
- PaError result = paNoError;
- MMRESULT mmresult;
- unsigned int i;
-
- for( i=0; i < stream->input.deviceCount; ++i )
- {
- mmresult = waveInAddBuffer( ((HWAVEIN*)stream->input.waveHandles)[i],
- &stream->input.waveHeaders[i][ stream->input.currentBufferIndex ],
- sizeof(WAVEHDR) );
- if( mmresult != MMSYSERR_NOERROR )
- {
- result = paUnanticipatedHostError;
- PA_MME_SET_LAST_WAVEIN_ERROR( mmresult );
- }
- }
-
- stream->input.currentBufferIndex =
- PA_CIRCULAR_INCREMENT_( stream->input.currentBufferIndex, stream->input.bufferCount );
-
- stream->input.framesUsedInCurrentBuffer = 0;
-
- return result;
-}
-
-
-static PaError AdvanceToNextOutputBuffer( PaWinMmeStream *stream )
-{
- PaError result = paNoError;
- MMRESULT mmresult;
- unsigned int i;
-
- for( i=0; i < stream->output.deviceCount; ++i )
- {
- mmresult = waveOutWrite( ((HWAVEOUT*)stream->output.waveHandles)[i],
- &stream->output.waveHeaders[i][ stream->output.currentBufferIndex ],
- sizeof(WAVEHDR) );
- if( mmresult != MMSYSERR_NOERROR )
- {
- result = paUnanticipatedHostError;
- PA_MME_SET_LAST_WAVEOUT_ERROR( mmresult );
- }
- }
-
- stream->output.currentBufferIndex =
- PA_CIRCULAR_INCREMENT_( stream->output.currentBufferIndex, stream->output.bufferCount );
-
- stream->output.framesUsedInCurrentBuffer = 0;
-
- return result;
-}
-
-
-/* requeue all but the most recent input with the driver. Used for catching
- up after a total input buffer underrun */
-static PaError CatchUpInputBuffers( PaWinMmeStream *stream )
-{
- PaError result = paNoError;
- unsigned int i;
-
- for( i=0; i < stream->input.bufferCount - 1; ++i )
- {
- result = AdvanceToNextInputBuffer( stream );
- if( result != paNoError )
- break;
- }
-
- return result;
-}
-
-
-/* take the most recent output and duplicate it to all other output buffers
- and requeue them. Used for catching up after a total output buffer underrun.
-*/
-static PaError CatchUpOutputBuffers( PaWinMmeStream *stream )
-{
- PaError result = paNoError;
- unsigned int i, j;
- unsigned int previousBufferIndex =
- PA_CIRCULAR_DECREMENT_( stream->output.currentBufferIndex, stream->output.bufferCount );
-
- for( i=0; i < stream->output.bufferCount - 1; ++i )
- {
- for( j=0; j < stream->output.deviceCount; ++j )
- {
- if( stream->output.waveHeaders[j][ stream->output.currentBufferIndex ].lpData
- != stream->output.waveHeaders[j][ previousBufferIndex ].lpData )
- {
- CopyMemory( stream->output.waveHeaders[j][ stream->output.currentBufferIndex ].lpData,
- stream->output.waveHeaders[j][ previousBufferIndex ].lpData,
- stream->output.waveHeaders[j][ stream->output.currentBufferIndex ].dwBufferLength );
- }
- }
-
- result = AdvanceToNextOutputBuffer( stream );
- if( result != paNoError )
- break;
- }
-
- return result;
-}
-
-
-static DWORD WINAPI ProcessingThreadProc( void *pArg )
-{
- PaWinMmeStream *stream = (PaWinMmeStream *)pArg;
- HANDLE events[3];
- int eventCount = 0;
- DWORD result = paNoError;
- DWORD waitResult;
- DWORD timeout = (unsigned long)(stream->allBuffersDurationMs * 0.5);
- int hostBuffersAvailable;
- signed int hostInputBufferIndex, hostOutputBufferIndex;
- PaStreamCallbackFlags statusFlags;
- int callbackResult;
- int done = 0;
- unsigned int channel, i;
- unsigned long framesProcessed;
-
- /* prepare event array for call to WaitForMultipleObjects() */
- if( stream->input.bufferEvent )
- events[eventCount++] = stream->input.bufferEvent;
- if( stream->output.bufferEvent )
- events[eventCount++] = stream->output.bufferEvent;
- events[eventCount++] = stream->abortEvent;
-
- statusFlags = 0; /** @todo support paInputUnderflow, paOutputOverflow and paNeverDropInput */
-
- /* loop until something causes us to stop */
- do{
- /* wait for MME to signal that a buffer is available, or for
- the PA abort event to be signaled.
-
- When this indicates that one or more buffers are available
- NoBuffersAreQueued() and Current*BuffersAreDone are used below to
- poll for additional done buffers. NoBuffersAreQueued() will fail
- to identify an underrun/overflow if the driver doesn't mark all done
- buffers prior to signalling the event. Some drivers do this
- (eg RME Digi96, and others don't eg VIA PC 97 input). This isn't a
- huge problem, it just means that we won't always be able to detect
- underflow/overflow.
- */
- waitResult = WaitForMultipleObjects( eventCount, events, FALSE /* wait all = FALSE */, timeout );
- if( waitResult == WAIT_FAILED )
- {
- result = paUnanticipatedHostError;
- /** @todo FIXME/REVIEW: can't return host error info from an asyncronous thread */
- done = 1;
- }
- else if( waitResult == WAIT_TIMEOUT )
- {
- /* if a timeout is encountered, continue */
- }
-
- if( stream->abortProcessing )
- {
- /* Pa_AbortStream() has been called, stop processing immediately */
- done = 1;
- }
- else if( stream->stopProcessing )
- {
- /* Pa_StopStream() has been called or the user callback returned
- non-zero, processing will continue until all output buffers
- are marked as done. The stream will stop immediately if it
- is input-only.
- */
-
- if( PA_IS_OUTPUT_STREAM_(stream) )
- {
- if( NoBuffersAreQueued( &stream->output ) )
- done = 1; /* Will cause thread to return. */
- }
- else
- {
- /* input only stream */
- done = 1; /* Will cause thread to return. */
- }
- }
- else
- {
- hostBuffersAvailable = 1;
-
- /* process all available host buffers */
- do
- {
- hostInputBufferIndex = -1;
- hostOutputBufferIndex = -1;
-
- if( PA_IS_INPUT_STREAM_(stream) )
- {
- if( CurrentInputBuffersAreDone( stream ) )
- {
- if( NoBuffersAreQueued( &stream->input ) )
- {
- /** @todo
- if all of the other buffers are also ready then
- we discard all but the most recent. This is an
- input buffer overflow. FIXME: these buffers should
- be passed to the callback in a paNeverDropInput
- stream.
-
- note that it is also possible for an input overflow
- to happen while the callback is processing a buffer.
- that is handled further down.
- */
- result = CatchUpInputBuffers( stream );
- if( result != paNoError )
- done = 1;
-
- statusFlags |= paInputOverflow;
- }
-
- hostInputBufferIndex = stream->input.currentBufferIndex;
- }
- }
-
- if( PA_IS_OUTPUT_STREAM_(stream) )
- {
- if( CurrentOutputBuffersAreDone( stream ) )
- {
- /* ok, we have an output buffer */
-
- if( NoBuffersAreQueued( &stream->output ) )
- {
- /*
- if all of the other buffers are also ready, catch up by copying
- the most recently generated buffer into all but one of the output
- buffers.
-
- note that this catch up code only handles the case where all
- buffers have been played out due to this thread not having
- woken up at all. a more common case occurs when this thread
- is woken up, processes one buffer, but takes too long, and as
- a result all the other buffers have become un-queued. that
- case is handled further down.
- */
-
- result = CatchUpOutputBuffers( stream );
- if( result != paNoError )
- done = 1;
-
- statusFlags |= paOutputUnderflow;
- }
-
- hostOutputBufferIndex = stream->output.currentBufferIndex;
- }
- }
-
-
- if( (PA_IS_FULL_DUPLEX_STREAM_(stream) && hostInputBufferIndex != -1 && hostOutputBufferIndex != -1) ||
- (PA_IS_HALF_DUPLEX_STREAM_(stream) && ( hostInputBufferIndex != -1 || hostOutputBufferIndex != -1 ) ) )
- {
- PaStreamCallbackTimeInfo timeInfo = {0,0,0}; /** @todo implement inputBufferAdcTime */
-
-
- if( PA_IS_OUTPUT_STREAM_(stream) )
- {
- /* set timeInfo.currentTime and calculate timeInfo.outputBufferDacTime
- from the current wave out position */
- MMTIME mmtime;
- double timeBeforeGetPosition, timeAfterGetPosition;
- double time;
- long framesInBufferRing;
- long writePosition;
- long playbackPosition;
- HWAVEOUT firstWaveOutDevice = ((HWAVEOUT*)stream->output.waveHandles)[0];
-
- mmtime.wType = TIME_SAMPLES;
- timeBeforeGetPosition = PaUtil_GetTime();
- waveOutGetPosition( firstWaveOutDevice, &mmtime, sizeof(MMTIME) );
- timeAfterGetPosition = PaUtil_GetTime();
-
- timeInfo.currentTime = timeAfterGetPosition;
-
- /* approximate time at which wave out position was measured
- as half way between timeBeforeGetPosition and timeAfterGetPosition */
- time = timeBeforeGetPosition + (timeAfterGetPosition - timeBeforeGetPosition) * .5;
-
- framesInBufferRing = stream->output.bufferCount * stream->bufferProcessor.framesPerHostBuffer;
- playbackPosition = mmtime.u.sample % framesInBufferRing;
-
- writePosition = stream->output.currentBufferIndex * stream->bufferProcessor.framesPerHostBuffer
- + stream->output.framesUsedInCurrentBuffer;
-
- if( playbackPosition >= writePosition ){
- timeInfo.outputBufferDacTime =
- time + ((double)( writePosition + (framesInBufferRing - playbackPosition) ) * stream->bufferProcessor.samplePeriod );
- }else{
- timeInfo.outputBufferDacTime =
- time + ((double)( writePosition - playbackPosition ) * stream->bufferProcessor.samplePeriod );
- }
- }
-
-
- PaUtil_BeginCpuLoadMeasurement( &stream->cpuLoadMeasurer );
-
- PaUtil_BeginBufferProcessing( &stream->bufferProcessor, &timeInfo, statusFlags );
-
- /* reset status flags once they have been passed to the buffer processor */
- statusFlags = 0;
-
- if( PA_IS_INPUT_STREAM_(stream) )
- {
- PaUtil_SetInputFrameCount( &stream->bufferProcessor, 0 /* default to host buffer size */ );
-
- channel = 0;
- for( i=0; i<stream->input.deviceCount; ++i )
- {
- /* we have stored the number of channels in the buffer in dwUser */
- int channelCount = stream->input.waveHeaders[i][ hostInputBufferIndex ].dwUser;
-
- PaUtil_SetInterleavedInputChannels( &stream->bufferProcessor, channel,
- stream->input.waveHeaders[i][ hostInputBufferIndex ].lpData +
- stream->input.framesUsedInCurrentBuffer * channelCount *
- stream->bufferProcessor.bytesPerHostInputSample,
- channelCount );
-
-
- channel += channelCount;
- }
- }
-
- if( PA_IS_OUTPUT_STREAM_(stream) )
- {
- PaUtil_SetOutputFrameCount( &stream->bufferProcessor, 0 /* default to host buffer size */ );
-
- channel = 0;
- for( i=0; i<stream->output.deviceCount; ++i )
- {
- /* we have stored the number of channels in the buffer in dwUser */
- int channelCount = stream->output.waveHeaders[i][ hostOutputBufferIndex ].dwUser;
-
- PaUtil_SetInterleavedOutputChannels( &stream->bufferProcessor, channel,
- stream->output.waveHeaders[i][ hostOutputBufferIndex ].lpData +
- stream->output.framesUsedInCurrentBuffer * channelCount *
- stream->bufferProcessor.bytesPerHostOutputSample,
- channelCount );
-
- channel += channelCount;
- }
- }
-
- callbackResult = paContinue;
- framesProcessed = PaUtil_EndBufferProcessing( &stream->bufferProcessor, &callbackResult );
-
- stream->input.framesUsedInCurrentBuffer += framesProcessed;
- stream->output.framesUsedInCurrentBuffer += framesProcessed;
-
- PaUtil_EndCpuLoadMeasurement( &stream->cpuLoadMeasurer, framesProcessed );
-
- if( callbackResult == paContinue )
- {
- /* nothing special to do */
- }
- else if( callbackResult == paAbort )
- {
- stream->abortProcessing = 1;
- done = 1;
- /** @todo FIXME: should probably reset the output device immediately once the callback returns paAbort */
- result = paNoError;
- }
- else
- {
- /* User callback has asked us to stop with paComplete or other non-zero value */
- stream->stopProcessing = 1; /* stop once currently queued audio has finished */
- result = paNoError;
- }
-
-
- if( PA_IS_INPUT_STREAM_(stream)
- && stream->stopProcessing == 0 && stream->abortProcessing == 0
- && stream->input.framesUsedInCurrentBuffer == stream->input.framesPerBuffer )
- {
- if( NoBuffersAreQueued( &stream->input ) )
- {
- /** @todo need to handle PaNeverDropInput here where necessary */
- result = CatchUpInputBuffers( stream );
- if( result != paNoError )
- done = 1;
-
- statusFlags |= paInputOverflow;
- }
-
- result = AdvanceToNextInputBuffer( stream );
- if( result != paNoError )
- done = 1;
- }
-
-
- if( PA_IS_OUTPUT_STREAM_(stream) && !stream->abortProcessing )
- {
- if( stream->stopProcessing &&
- stream->output.framesUsedInCurrentBuffer < stream->output.framesPerBuffer )
- {
- /* zero remaining samples in output output buffer and flush */
-
- stream->output.framesUsedInCurrentBuffer += PaUtil_ZeroOutput( &stream->bufferProcessor,
- stream->output.framesPerBuffer - stream->output.framesUsedInCurrentBuffer );
-
- /* we send the entire buffer to the output devices, but we could
- just send a partial buffer, rather than zeroing the unused
- samples.
- */
- }
-
- if( stream->output.framesUsedInCurrentBuffer == stream->output.framesPerBuffer )
- {
- /* check for underflow before enquing the just-generated buffer,
- but recover from underflow after enquing it. This ensures
- that the most recent audio segment is repeated */
- int outputUnderflow = NoBuffersAreQueued( &stream->output );
-
- result = AdvanceToNextOutputBuffer( stream );
- if( result != paNoError )
- done = 1;
-
- if( outputUnderflow && !done && !stream->stopProcessing )
- {
- /* Recover from underflow in the case where the
- underflow occured while processing the buffer
- we just finished */
-
- result = CatchUpOutputBuffers( stream );
- if( result != paNoError )
- done = 1;
-
- statusFlags |= paOutputUnderflow;
- }
- }
- }
-
- if( stream->throttleProcessingThreadOnOverload != 0 )
- {
- if( stream->stopProcessing || stream->abortProcessing )
- {
- if( stream->processingThreadPriority != stream->highThreadPriority )
- {
- SetThreadPriority( stream->processingThread, stream->highThreadPriority );
- stream->processingThreadPriority = stream->highThreadPriority;
- }
- }
- else if( PaUtil_GetCpuLoad( &stream->cpuLoadMeasurer ) > 1. )
- {
- if( stream->processingThreadPriority != stream->throttledThreadPriority )
- {
- SetThreadPriority( stream->processingThread, stream->throttledThreadPriority );
- stream->processingThreadPriority = stream->throttledThreadPriority;
- }
-
- /* sleep to give other processes a go */
- Sleep( stream->throttledSleepMsecs );
- }
- else
- {
- if( stream->processingThreadPriority != stream->highThreadPriority )
- {
- SetThreadPriority( stream->processingThread, stream->highThreadPriority );
- stream->processingThreadPriority = stream->highThreadPriority;
- }
- }
- }
- }
- else
- {
- hostBuffersAvailable = 0;
- }
- }
- while( hostBuffersAvailable &&
- stream->stopProcessing == 0 &&
- stream->abortProcessing == 0 &&
- !done );
- }
- }
- while( !done );
-
- stream->isActive = 0;
-
- if( stream->streamRepresentation.streamFinishedCallback != 0 )
- stream->streamRepresentation.streamFinishedCallback( stream->streamRepresentation.userData );
-
- PaUtil_ResetCpuLoadMeasurer( &stream->cpuLoadMeasurer );
-
- return result;
-}
-
-
-/*
- When CloseStream() is called, the multi-api layer ensures that
- the stream has already been stopped or aborted.
-*/
-static PaError CloseStream( PaStream* s )
-{
- PaError result;
- PaWinMmeStream *stream = (PaWinMmeStream*)s;
-
- result = CloseHandleWithPaError( stream->abortEvent );
- if( result != paNoError ) goto error;
-
- TerminateWaveHeaders( &stream->output, 0 /* not isInput */ );
- TerminateWaveHeaders( &stream->input, 1 /* isInput */ );
-
- TerminateWaveHandles( &stream->output, 0 /* not isInput */, 0 /* not currentlyProcessingAnError */ );
- TerminateWaveHandles( &stream->input, 1 /* isInput */, 0 /* not currentlyProcessingAnError */ );
-
- PaUtil_TerminateBufferProcessor( &stream->bufferProcessor );
- PaUtil_TerminateStreamRepresentation( &stream->streamRepresentation );
- PaUtil_FreeMemory( stream );
-
-error:
- /** @todo REVIEW: what is the best way to clean up a stream if an error is detected? */
- return result;
-}
-
-
-static PaError StartStream( PaStream *s )
-{
- PaError result;
- PaWinMmeStream *stream = (PaWinMmeStream*)s;
- MMRESULT mmresult;
- unsigned int i, j;
- int callbackResult;
- unsigned int channel;
- unsigned long framesProcessed;
- PaStreamCallbackTimeInfo timeInfo = {0,0,0}; /** @todo implement this for stream priming */
-
- PaUtil_ResetBufferProcessor( &stream->bufferProcessor );
-
- if( PA_IS_INPUT_STREAM_(stream) )
- {
- for( i=0; i<stream->input.bufferCount; ++i )
- {
- for( j=0; j<stream->input.deviceCount; ++j )
- {
- mmresult = waveInAddBuffer( ((HWAVEIN*)stream->input.waveHandles)[j], &stream->input.waveHeaders[j][i], sizeof(WAVEHDR) );
- if( mmresult != MMSYSERR_NOERROR )
- {
- result = paUnanticipatedHostError;
- PA_MME_SET_LAST_WAVEIN_ERROR( mmresult );
- goto error;
- }
- }
- }
- stream->input.currentBufferIndex = 0;
- stream->input.framesUsedInCurrentBuffer = 0;
- }
-
- if( PA_IS_OUTPUT_STREAM_(stream) )
- {
- for( i=0; i<stream->output.deviceCount; ++i )
- {
- if( (mmresult = waveOutPause( ((HWAVEOUT*)stream->output.waveHandles)[i] )) != MMSYSERR_NOERROR )
- {
- result = paUnanticipatedHostError;
- PA_MME_SET_LAST_WAVEOUT_ERROR( mmresult );
- goto error;
- }
- }
-
- for( i=0; i<stream->output.bufferCount; ++i )
- {
- if( stream->primeStreamUsingCallback )
- {
-
- stream->output.framesUsedInCurrentBuffer = 0;
- do{
-
- PaUtil_BeginBufferProcessing( &stream->bufferProcessor,
- &timeInfo,
- paPrimingOutput | ((stream->input.bufferCount > 0 ) ? paInputUnderflow : 0));
-
- if( stream->input.bufferCount > 0 )
- PaUtil_SetNoInput( &stream->bufferProcessor );
-
- PaUtil_SetOutputFrameCount( &stream->bufferProcessor, 0 /* default to host buffer size */ );
-
- channel = 0;
- for( j=0; j<stream->output.deviceCount; ++j )
- {
- /* we have stored the number of channels in the buffer in dwUser */
- int channelCount = stream->output.waveHeaders[j][i].dwUser;
-
- PaUtil_SetInterleavedOutputChannels( &stream->bufferProcessor, channel,
- stream->output.waveHeaders[j][i].lpData +
- stream->output.framesUsedInCurrentBuffer * channelCount *
- stream->bufferProcessor.bytesPerHostOutputSample,
- channelCount );
-
- /* we have stored the number of channels in the buffer in dwUser */
- channel += channelCount;
- }
-
- callbackResult = paContinue;
- framesProcessed = PaUtil_EndBufferProcessing( &stream->bufferProcessor, &callbackResult );
- stream->output.framesUsedInCurrentBuffer += framesProcessed;
-
- if( callbackResult != paContinue )
- {
- /** @todo fix this, what do we do if callback result is non-zero during stream
- priming?
-
- for complete: play out primed waveHeaders as usual
- for abort: clean up immediately.
- */
- }
-
- }while( stream->output.framesUsedInCurrentBuffer != stream->output.framesPerBuffer );
-
- }
- else
- {
- for( j=0; j<stream->output.deviceCount; ++j )
- {
- ZeroMemory( stream->output.waveHeaders[j][i].lpData, stream->output.waveHeaders[j][i].dwBufferLength );
- }
- }
-
- /* we queue all channels of a single buffer frame (accross all
- devices, because some multidevice multichannel drivers work
- better this way */
- for( j=0; j<stream->output.deviceCount; ++j )
- {
- mmresult = waveOutWrite( ((HWAVEOUT*)stream->output.waveHandles)[j], &stream->output.waveHeaders[j][i], sizeof(WAVEHDR) );
- if( mmresult != MMSYSERR_NOERROR )
- {
- result = paUnanticipatedHostError;
- PA_MME_SET_LAST_WAVEOUT_ERROR( mmresult );
- goto error;
- }
- }
- }
- stream->output.currentBufferIndex = 0;
- stream->output.framesUsedInCurrentBuffer = 0;
- }
-
-
- stream->isStopped = 0;
- stream->isActive = 1;
- stream->stopProcessing = 0;
- stream->abortProcessing = 0;
-
- result = ResetEventWithPaError( stream->input.bufferEvent );
- if( result != paNoError ) goto error;
-
- result = ResetEventWithPaError( stream->output.bufferEvent );
- if( result != paNoError ) goto error;
-
-
- if( stream->streamRepresentation.streamCallback )
- {
- /* callback stream */
-
- result = ResetEventWithPaError( stream->abortEvent );
- if( result != paNoError ) goto error;
-
- /* Create thread that waits for audio buffers to be ready for processing. */
- stream->processingThread = CreateThread( 0, 0, ProcessingThreadProc, stream, 0, &stream->processingThreadId );
- if( !stream->processingThread )
- {
- result = paUnanticipatedHostError;
- PA_MME_SET_LAST_SYSTEM_ERROR( GetLastError() );
- goto error;
- }
-
- /** @todo could have mme specific stream parameters to allow the user
- to set the callback thread priorities */
- stream->highThreadPriority = THREAD_PRIORITY_TIME_CRITICAL;
- stream->throttledThreadPriority = THREAD_PRIORITY_NORMAL;
-
- if( !SetThreadPriority( stream->processingThread, stream->highThreadPriority ) )
- {
- result = paUnanticipatedHostError;
- PA_MME_SET_LAST_SYSTEM_ERROR( GetLastError() );
- goto error;
- }
- stream->processingThreadPriority = stream->highThreadPriority;
- }
- else
- {
- /* blocking read/write stream */
-
- }
-
- if( PA_IS_INPUT_STREAM_(stream) )
- {
- for( i=0; i < stream->input.deviceCount; ++i )
- {
- mmresult = waveInStart( ((HWAVEIN*)stream->input.waveHandles)[i] );
- PA_DEBUG(("Pa_StartStream: waveInStart returned = 0x%X.\n", mmresult));
- if( mmresult != MMSYSERR_NOERROR )
- {
- result = paUnanticipatedHostError;
- PA_MME_SET_LAST_WAVEIN_ERROR( mmresult );
- goto error;
- }
- }
- }
-
- if( PA_IS_OUTPUT_STREAM_(stream) )
- {
- for( i=0; i < stream->output.deviceCount; ++i )
- {
- if( (mmresult = waveOutRestart( ((HWAVEOUT*)stream->output.waveHandles)[i] )) != MMSYSERR_NOERROR )
- {
- result = paUnanticipatedHostError;
- PA_MME_SET_LAST_WAVEOUT_ERROR( mmresult );
- goto error;
- }
- }
- }
-
- return result;
-
-error:
- /** @todo FIXME: implement recovery as best we can
- This should involve rolling back to a state as-if this function had never been called
- */
- return result;
-}
-
-
-static PaError StopStream( PaStream *s )
-{
- PaError result = paNoError;
- PaWinMmeStream *stream = (PaWinMmeStream*)s;
- int timeout;
- DWORD waitResult;
- MMRESULT mmresult;
- signed int hostOutputBufferIndex;
- unsigned int channel, waitCount, i;
-
- /** @todo
- REVIEW: the error checking in this function needs review. the basic
- idea is to return from this function in a known state - for example
- there is no point avoiding calling waveInReset just because
- the thread times out.
- */
-
- if( stream->processingThread )
- {
- /* callback stream */
-
- /* Tell processing thread to stop generating more data and to let current data play out. */
- stream->stopProcessing = 1;
-
- /* Calculate timeOut longer than longest time it could take to return all buffers. */
- timeout = (int)(stream->allBuffersDurationMs * 1.5);
- if( timeout < PA_MME_MIN_TIMEOUT_MSEC_ )
- timeout = PA_MME_MIN_TIMEOUT_MSEC_;
-
- PA_DEBUG(("WinMME StopStream: waiting for background thread.\n"));
-
- waitResult = WaitForSingleObject( stream->processingThread, timeout );
- if( waitResult == WAIT_TIMEOUT )
- {
- /* try to abort */
- stream->abortProcessing = 1;
- SetEvent( stream->abortEvent );
- waitResult = WaitForSingleObject( stream->processingThread, timeout );
- if( waitResult == WAIT_TIMEOUT )
- {
- PA_DEBUG(("WinMME StopStream: timed out while waiting for background thread to finish.\n"));
- result = paTimedOut;
- }
- }
-
- CloseHandle( stream->processingThread );
- stream->processingThread = NULL;
- }
- else
- {
- /* blocking read / write stream */
-
- if( PA_IS_OUTPUT_STREAM_(stream) )
- {
- if( stream->output.framesUsedInCurrentBuffer > 0 )
- {
- /* there are still unqueued frames in the current buffer, so flush them */
-
- hostOutputBufferIndex = stream->output.currentBufferIndex;
-
- PaUtil_SetOutputFrameCount( &stream->bufferProcessor,
- stream->output.framesPerBuffer - stream->output.framesUsedInCurrentBuffer );
-
- channel = 0;
- for( i=0; i<stream->output.deviceCount; ++i )
- {
- /* we have stored the number of channels in the buffer in dwUser */
- int channelCount = stream->output.waveHeaders[i][ hostOutputBufferIndex ].dwUser;
-
- PaUtil_SetInterleavedOutputChannels( &stream->bufferProcessor, channel,
- stream->output.waveHeaders[i][ hostOutputBufferIndex ].lpData +
- stream->output.framesUsedInCurrentBuffer * channelCount *
- stream->bufferProcessor.bytesPerHostOutputSample,
- channelCount );
-
- channel += channelCount;
- }
-
- PaUtil_ZeroOutput( &stream->bufferProcessor,
- stream->output.framesPerBuffer - stream->output.framesUsedInCurrentBuffer );
-
- /* we send the entire buffer to the output devices, but we could
- just send a partial buffer, rather than zeroing the unused
- samples.
- */
- AdvanceToNextOutputBuffer( stream );
- }
-
-
- timeout = (stream->allBuffersDurationMs / stream->output.bufferCount) + 1;
- if( timeout < PA_MME_MIN_TIMEOUT_MSEC_ )
- timeout = PA_MME_MIN_TIMEOUT_MSEC_;
-
- waitCount = 0;
- while( !NoBuffersAreQueued( &stream->output ) && waitCount <= stream->output.bufferCount )
- {
- /* wait for MME to signal that a buffer is available */
- waitResult = WaitForSingleObject( stream->output.bufferEvent, timeout );
- if( waitResult == WAIT_FAILED )
- {
- break;
- }
- else if( waitResult == WAIT_TIMEOUT )
- {
- /* keep waiting */
- }
-
- ++waitCount;
- }
- }
- }
-
- if( PA_IS_OUTPUT_STREAM_(stream) )
- {
- for( i =0; i < stream->output.deviceCount; ++i )
- {
- mmresult = waveOutReset( ((HWAVEOUT*)stream->output.waveHandles)[i] );
- if( mmresult != MMSYSERR_NOERROR )
- {
- result = paUnanticipatedHostError;
- PA_MME_SET_LAST_WAVEOUT_ERROR( mmresult );
- }
- }
- }
-
- if( PA_IS_INPUT_STREAM_(stream) )
- {
- for( i=0; i < stream->input.deviceCount; ++i )
- {
- mmresult = waveInReset( ((HWAVEIN*)stream->input.waveHandles)[i] );
- if( mmresult != MMSYSERR_NOERROR )
- {
- result = paUnanticipatedHostError;
- PA_MME_SET_LAST_WAVEIN_ERROR( mmresult );
- }
- }
- }
-
- stream->isStopped = 1;
- stream->isActive = 0;
-
- return result;
-}
-
-
-static PaError AbortStream( PaStream *s )
-{
- PaError result = paNoError;
- PaWinMmeStream *stream = (PaWinMmeStream*)s;
- int timeout;
- DWORD waitResult;
- MMRESULT mmresult;
- unsigned int i;
-
- /** @todo
- REVIEW: the error checking in this function needs review. the basic
- idea is to return from this function in a known state - for example
- there is no point avoiding calling waveInReset just because
- the thread times out.
- */
-
- if( stream->processingThread )
- {
- /* callback stream */
-
- /* Tell processing thread to abort immediately */
- stream->abortProcessing = 1;
- SetEvent( stream->abortEvent );
- }
-
-
- if( PA_IS_OUTPUT_STREAM_(stream) )
- {
- for( i =0; i < stream->output.deviceCount; ++i )
- {
- mmresult = waveOutReset( ((HWAVEOUT*)stream->output.waveHandles)[i] );
- if( mmresult != MMSYSERR_NOERROR )
- {
- PA_MME_SET_LAST_WAVEOUT_ERROR( mmresult );
- return paUnanticipatedHostError;
- }
- }
- }
-
- if( PA_IS_INPUT_STREAM_(stream) )
- {
- for( i=0; i < stream->input.deviceCount; ++i )
- {
- mmresult = waveInReset( ((HWAVEIN*)stream->input.waveHandles)[i] );
- if( mmresult != MMSYSERR_NOERROR )
- {
- PA_MME_SET_LAST_WAVEIN_ERROR( mmresult );
- return paUnanticipatedHostError;
- }
- }
- }
-
-
- if( stream->processingThread )
- {
- /* callback stream */
-
- PA_DEBUG(("WinMME AbortStream: waiting for background thread.\n"));
-
- /* Calculate timeOut longer than longest time it could take to return all buffers. */
- timeout = (int)(stream->allBuffersDurationMs * 1.5);
- if( timeout < PA_MME_MIN_TIMEOUT_MSEC_ )
- timeout = PA_MME_MIN_TIMEOUT_MSEC_;
-
- waitResult = WaitForSingleObject( stream->processingThread, timeout );
- if( waitResult == WAIT_TIMEOUT )
- {
- PA_DEBUG(("WinMME AbortStream: timed out while waiting for background thread to finish.\n"));
- return paTimedOut;
- }
-
- CloseHandle( stream->processingThread );
- stream->processingThread = NULL;
- }
-
- stream->isStopped = 1;
- stream->isActive = 0;
-
- return result;
-}
-
-
-static PaError IsStreamStopped( PaStream *s )
-{
- PaWinMmeStream *stream = (PaWinMmeStream*)s;
-
- return stream->isStopped;
-}
-
-
-static PaError IsStreamActive( PaStream *s )
-{
- PaWinMmeStream *stream = (PaWinMmeStream*)s;
-
- return stream->isActive;
-}
-
-
-static PaTime GetStreamTime( PaStream *s )
-{
- (void) s; /* unused parameter */
-
- return PaUtil_GetTime();
-}
-
-
-static double GetStreamCpuLoad( PaStream* s )
-{
- PaWinMmeStream *stream = (PaWinMmeStream*)s;
-
- return PaUtil_GetCpuLoad( &stream->cpuLoadMeasurer );
-}
-
-
-/*
- As separate stream interfaces are used for blocking and callback
- streams, the following functions can be guaranteed to only be called
- for blocking streams.
-*/
-
-static PaError ReadStream( PaStream* s,
- void *buffer,
- unsigned long frames )
-{
- PaError result = paNoError;
- PaWinMmeStream *stream = (PaWinMmeStream*)s;
- void *userBuffer;
- unsigned long framesRead = 0;
- unsigned long framesProcessed;
- signed int hostInputBufferIndex;
- DWORD waitResult;
- DWORD timeout = (unsigned long)(stream->allBuffersDurationMs * 0.5);
- unsigned int channel, i;
-
- if( PA_IS_INPUT_STREAM_(stream) )
- {
- /* make a local copy of the user buffer pointer(s). this is necessary
- because PaUtil_CopyInput() advances these pointers every time
- it is called.
- */
- if( stream->bufferProcessor.userInputIsInterleaved )
- {
- userBuffer = buffer;
- }
- else
- {
- userBuffer = alloca( sizeof(void*) * stream->bufferProcessor.inputChannelCount );
- if( !userBuffer )
- return paInsufficientMemory;
- for( i = 0; i<stream->bufferProcessor.inputChannelCount; ++i )
- ((void**)userBuffer)[i] = ((void**)buffer)[i];
- }
-
- do{
- if( CurrentInputBuffersAreDone( stream ) )
- {
- if( NoBuffersAreQueued( &stream->input ) )
- {
- /** @todo REVIEW: consider what to do if the input overflows.
- do we requeue all of the buffers? should we be running
- a thread to make sure they are always queued? */
-
- result = paInputOverflowed;
- }
-
- hostInputBufferIndex = stream->input.currentBufferIndex;
-
- PaUtil_SetInputFrameCount( &stream->bufferProcessor,
- stream->input.framesPerBuffer - stream->input.framesUsedInCurrentBuffer );
-
- channel = 0;
- for( i=0; i<stream->input.deviceCount; ++i )
- {
- /* we have stored the number of channels in the buffer in dwUser */
- int channelCount = stream->input.waveHeaders[i][ hostInputBufferIndex ].dwUser;
-
- PaUtil_SetInterleavedInputChannels( &stream->bufferProcessor, channel,
- stream->input.waveHeaders[i][ hostInputBufferIndex ].lpData +
- stream->input.framesUsedInCurrentBuffer * channelCount *
- stream->bufferProcessor.bytesPerHostInputSample,
- channelCount );
-
- channel += channelCount;
- }
-
- framesProcessed = PaUtil_CopyInput( &stream->bufferProcessor, &userBuffer, frames - framesRead );
-
- stream->input.framesUsedInCurrentBuffer += framesProcessed;
- if( stream->input.framesUsedInCurrentBuffer == stream->input.framesPerBuffer )
- {
- result = AdvanceToNextInputBuffer( stream );
- if( result != paNoError )
- break;
- }
-
- framesRead += framesProcessed;
-
- }else{
- /* wait for MME to signal that a buffer is available */
- waitResult = WaitForSingleObject( stream->input.bufferEvent, timeout );
- if( waitResult == WAIT_FAILED )
- {
- result = paUnanticipatedHostError;
- break;
- }
- else if( waitResult == WAIT_TIMEOUT )
- {
- /* if a timeout is encountered, continue,
- perhaps we should give up eventually
- */
- }
- }
- }while( framesRead < frames );
- }
- else
- {
- result = paCanNotReadFromAnOutputOnlyStream;
- }
-
- return result;
-}
-
-
-static PaError WriteStream( PaStream* s,
- const void *buffer,
- unsigned long frames )
-{
- PaError result = paNoError;
- PaWinMmeStream *stream = (PaWinMmeStream*)s;
- const void *userBuffer;
- unsigned long framesWritten = 0;
- unsigned long framesProcessed;
- signed int hostOutputBufferIndex;
- DWORD waitResult;
- DWORD timeout = (unsigned long)(stream->allBuffersDurationMs * 0.5);
- unsigned int channel, i;
-
-
- if( PA_IS_OUTPUT_STREAM_(stream) )
- {
- /* make a local copy of the user buffer pointer(s). this is necessary
- because PaUtil_CopyOutput() advances these pointers every time
- it is called.
- */
- if( stream->bufferProcessor.userOutputIsInterleaved )
- {
- userBuffer = buffer;
- }
- else
- {
- userBuffer = alloca( sizeof(void*) * stream->bufferProcessor.outputChannelCount );
- if( !userBuffer )
- return paInsufficientMemory;
- for( i = 0; i<stream->bufferProcessor.outputChannelCount; ++i )
- ((const void**)userBuffer)[i] = ((const void**)buffer)[i];
- }
-
- do{
- if( CurrentOutputBuffersAreDone( stream ) )
- {
- if( NoBuffersAreQueued( &stream->output ) )
- {
- /** @todo REVIEW: consider what to do if the output
- underflows. do we requeue all the existing buffers with
- zeros? should we run a separate thread to keep the buffers
- enqueued at all times? */
-
- result = paOutputUnderflowed;
- }
-
- hostOutputBufferIndex = stream->output.currentBufferIndex;
-
- PaUtil_SetOutputFrameCount( &stream->bufferProcessor,
- stream->output.framesPerBuffer - stream->output.framesUsedInCurrentBuffer );
-
- channel = 0;
- for( i=0; i<stream->output.deviceCount; ++i )
- {
- /* we have stored the number of channels in the buffer in dwUser */
- int channelCount = stream->output.waveHeaders[i][ hostOutputBufferIndex ].dwUser;
-
- PaUtil_SetInterleavedOutputChannels( &stream->bufferProcessor, channel,
- stream->output.waveHeaders[i][ hostOutputBufferIndex ].lpData +
- stream->output.framesUsedInCurrentBuffer * channelCount *
- stream->bufferProcessor.bytesPerHostOutputSample,
- channelCount );
-
- channel += channelCount;
- }
-
- framesProcessed = PaUtil_CopyOutput( &stream->bufferProcessor, &userBuffer, frames - framesWritten );
-
- stream->output.framesUsedInCurrentBuffer += framesProcessed;
- if( stream->output.framesUsedInCurrentBuffer == stream->output.framesPerBuffer )
- {
- result = AdvanceToNextOutputBuffer( stream );
- if( result != paNoError )
- break;
- }
-
- framesWritten += framesProcessed;
- }
- else
- {
- /* wait for MME to signal that a buffer is available */
- waitResult = WaitForSingleObject( stream->output.bufferEvent, timeout );
- if( waitResult == WAIT_FAILED )
- {
- result = paUnanticipatedHostError;
- break;
- }
- else if( waitResult == WAIT_TIMEOUT )
- {
- /* if a timeout is encountered, continue,
- perhaps we should give up eventually
- */
- }
- }
- }while( framesWritten < frames );
- }
- else
- {
- result = paCanNotWriteToAnInputOnlyStream;
- }
-
- return result;
-}
-
-
-static signed long GetStreamReadAvailable( PaStream* s )
-{
- PaWinMmeStream *stream = (PaWinMmeStream*)s;
-
- if( PA_IS_INPUT_STREAM_(stream) )
- return GetAvailableFrames( &stream->input );
- else
- return paCanNotReadFromAnOutputOnlyStream;
-}
-
-
-static signed long GetStreamWriteAvailable( PaStream* s )
-{
- PaWinMmeStream *stream = (PaWinMmeStream*)s;
-
- if( PA_IS_OUTPUT_STREAM_(stream) )
- return GetAvailableFrames( &stream->output );
- else
- return paCanNotWriteToAnInputOnlyStream;
-}
-
-
-/* NOTE: the following functions are MME-stream specific, and are called directly
- by client code. We need to check for many more error conditions here because
- we don't have the benefit of pa_front.c's parameter checking.
-*/
-
-static PaError GetWinMMEStreamPointer( PaWinMmeStream **stream, PaStream *s )
-{
- PaError result;
- PaUtilHostApiRepresentation *hostApi;
- PaWinMmeHostApiRepresentation *winMmeHostApi;
-
- result = PaUtil_ValidateStreamPointer( s );
- if( result != paNoError )
- return result;
-
- result = PaUtil_GetHostApiRepresentation( &hostApi, paMME );
- if( result != paNoError )
- return result;
-
- winMmeHostApi = (PaWinMmeHostApiRepresentation*)hostApi;
-
- /* note, the following would be easier if there was a generic way of testing
- that a stream belongs to a specific host API */
-
- if( PA_STREAM_REP( s )->streamInterface == &winMmeHostApi->callbackStreamInterface
- || PA_STREAM_REP( s )->streamInterface == &winMmeHostApi->blockingStreamInterface )
- {
- /* s is a WinMME stream */
- *stream = (PaWinMmeStream *)s;
- return paNoError;
- }
- else
- {
- return paIncompatibleStreamHostApi;
- }
-}
-
-
-int PaWinMME_GetStreamInputHandleCount( PaStream* s )
-{
- PaWinMmeStream *stream;
- PaError result = GetWinMMEStreamPointer( &stream, s );
-
- if( result == paNoError )
- return (PA_IS_INPUT_STREAM_(stream)) ? stream->input.deviceCount : 0;
- else
- return result;
-}
-
-
-HWAVEIN PaWinMME_GetStreamInputHandle( PaStream* s, int handleIndex )
-{
- PaWinMmeStream *stream;
- PaError result = GetWinMMEStreamPointer( &stream, s );
-
- if( result == paNoError
- && PA_IS_INPUT_STREAM_(stream)
- && handleIndex >= 0
- && (unsigned int)handleIndex < stream->input.deviceCount )
- return ((HWAVEIN*)stream->input.waveHandles)[handleIndex];
- else
- return 0;
-}
-
-
-int PaWinMME_GetStreamOutputHandleCount( PaStream* s)
-{
- PaWinMmeStream *stream;
- PaError result = GetWinMMEStreamPointer( &stream, s );
-
- if( result == paNoError )
- return (PA_IS_OUTPUT_STREAM_(stream)) ? stream->output.deviceCount : 0;
- else
- return result;
-}
-
-
-HWAVEOUT PaWinMME_GetStreamOutputHandle( PaStream* s, int handleIndex )
-{
- PaWinMmeStream *stream;
- PaError result = GetWinMMEStreamPointer( &stream, s );
-
- if( result == paNoError
- && PA_IS_OUTPUT_STREAM_(stream)
- && handleIndex >= 0
- && (unsigned int)handleIndex < stream->output.deviceCount )
- return ((HWAVEOUT*)stream->output.waveHandles)[handleIndex];
- else
- return 0;
-}
-
-
-
-
-
+/*
+ * $Id: pa_win_wmme.c,v 1.6.2.86 2004/02/21 11:38:28 rossbencina Exp $
+ * pa_win_wmme.c
+ * Implementation of PortAudio for Windows MultiMedia Extensions (WMME)
+ *
+ * PortAudio Portable Real-Time Audio Library
+ * Latest Version at: http://www.portaudio.com
+ *
+ * Authors: Ross Bencina and Phil Burk
+ * Copyright (c) 1999-2000 Ross Bencina and Phil Burk
+ *
+ * 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.
+ *
+ * Any person wishing to distribute modifications to the Software is
+ * requested to send the modifications to the original developer so that
+ * they can be incorporated into the canonical version.
+ *
+ * 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.
+ *
+ */
+
+/* Modification History:
+ PLB = Phil Burk
+ JM = Julien Maillard
+ RDB = Ross Bencina
+ PLB20010402 - sDevicePtrs now allocates based on sizeof(pointer)
+ PLB20010413 - check for excessive numbers of channels
+ PLB20010422 - apply Mike Berry's changes for CodeWarrior on PC
+ including conditional inclusion of memory.h,
+ and explicit typecasting on memory allocation
+ PLB20010802 - use GlobalAlloc for sDevicesPtr instead of PaHost_AllocFastMemory
+ PLB20010816 - pass process instead of thread to SetPriorityClass()
+ PLB20010927 - use number of frames instead of real-time for CPULoad calculation.
+ JM20020118 - prevent hung thread when buffers underflow.
+ PLB20020321 - detect Win XP versus NT, 9x; fix DBUG typo; removed init of CurrentCount
+ RDB20020411 - various renaming cleanups, factored streamData alloc and cpu usage init
+ RDB20020417 - stopped counting WAVE_MAPPER when there were no real devices
+ refactoring, renaming and fixed a few edge case bugs
+ RDB20020531 - converted to V19 framework
+ ** NOTE maintanance history is now stored in CVS **
+*/
+
+/** @file
+
+ @todo Fix buffer catch up code, can sometimes get stuck (perhaps fixed now,
+ needs to be reviewed and tested.)
+
+ @todo implement paInputUnderflow, paOutputOverflow streamCallback statusFlags, paNeverDropInput.
+
+ @todo BUG: PA_MME_SET_LAST_WAVEIN/OUT_ERROR is used in functions which may
+ be called asynchronously from the callback thread. this is bad.
+
+ @todo implement inputBufferAdcTime in callback thread
+
+ @todo review/fix error recovery and cleanup in marked functions
+
+ @todo implement timeInfo for stream priming
+
+ @todo handle the case where the callback returns paAbort or paComplete during stream priming.
+
+ @todo review input overflow and output underflow handling in ReadStream and WriteStream
+
+Non-critical stuff for the future:
+
+ @todo Investigate supporting host buffer formats > 16 bits
+
+ @todo define UNICODE and _UNICODE in the project settings and see what breaks
+
+*/
+
+/*
+ How it works:
+
+ For both callback and blocking read/write streams we open the MME devices
+ in CALLBACK_EVENT mode. In this mode, MME signals an Event object whenever
+ it has finished with a buffer (either filled it for input, or played it
+ for output). Where necessary we block waiting for Event objects using
+ WaitMultipleObjects().
+
+ When implementing a PA callback stream, we set up a high priority thread
+ which waits on the MME buffer Events and drains/fills the buffers when
+ they are ready.
+
+ When implementing a PA blocking read/write stream, we simply wait on these
+ Events (when necessary) inside the ReadStream() and WriteStream() functions.
+*/
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <math.h>
+#include <windows.h>
+#include <mmsystem.h>
+#include <process.h>
+#include <assert.h>
+/* PLB20010422 - "memory.h" doesn't work on CodeWarrior for PC. Thanks Mike Berry for the mod. */
+#ifndef __MWERKS__
+#include <malloc.h>
+#include <memory.h>
+#endif /* __MWERKS__ */
+
+#include "portaudio.h"
+#include "pa_trace.h"
+#include "pa_util.h"
+#include "pa_allocation.h"
+#include "pa_hostapi.h"
+#include "pa_stream.h"
+#include "pa_cpuload.h"
+#include "pa_process.h"
+
+#include "pa_win_wmme.h"
+
+#if (defined(WIN32) && (defined(_MSC_VER) && (_MSC_VER >= 1200))) /* MSC version 6 and above */
+#pragma comment(lib, "winmm.lib")
+#endif
+
+/************************************************* Constants ********/
+
+#define PA_MME_USE_HIGH_DEFAULT_LATENCY_ (0) /* For debugging glitches. */
+
+#if PA_MME_USE_HIGH_DEFAULT_LATENCY_
+ #define PA_MME_WIN_9X_DEFAULT_LATENCY_ (0.4)
+ #define PA_MME_MIN_HOST_OUTPUT_BUFFER_COUNT_ (4)
+ #define PA_MME_MIN_HOST_INPUT_BUFFER_COUNT_FULL_DUPLEX_ (4)
+ #define PA_MME_MIN_HOST_INPUT_BUFFER_COUNT_HALF_DUPLEX_ (4)
+ #define PA_MME_MIN_HOST_BUFFER_FRAMES_WHEN_UNSPECIFIED_ (16)
+ #define PA_MME_MAX_HOST_BUFFER_SECS_ (0.3) /* Do not exceed unless user buffer exceeds */
+ #define PA_MME_MAX_HOST_BUFFER_BYTES_ (32 * 1024) /* Has precedence over PA_MME_MAX_HOST_BUFFER_SECS_, some drivers are known to crash with buffer sizes > 32k */
+#else
+ #define PA_MME_WIN_9X_DEFAULT_LATENCY_ (0.2)
+ #define PA_MME_MIN_HOST_OUTPUT_BUFFER_COUNT_ (2)
+ #define PA_MME_MIN_HOST_INPUT_BUFFER_COUNT_FULL_DUPLEX_ (3)
+ #define PA_MME_MIN_HOST_INPUT_BUFFER_COUNT_HALF_DUPLEX_ (2)
+ #define PA_MME_MIN_HOST_BUFFER_FRAMES_WHEN_UNSPECIFIED_ (16)
+ #define PA_MME_MAX_HOST_BUFFER_SECS_ (0.1) /* Do not exceed unless user buffer exceeds */
+ #define PA_MME_MAX_HOST_BUFFER_BYTES_ (32 * 1024) /* Has precedence over PA_MME_MAX_HOST_BUFFER_SECS_, some drivers are known to crash with buffer sizes > 32k */
+#endif
+
+/* Use higher latency for NT because it is even worse at real-time
+ operation than Win9x.
+*/
+#define PA_MME_WIN_NT_DEFAULT_LATENCY_ (PA_MME_WIN_9X_DEFAULT_LATENCY_ * 2)
+#define PA_MME_WIN_WDM_DEFAULT_LATENCY_ (PA_MME_WIN_9X_DEFAULT_LATENCY_)
+
+
+#define PA_MME_MIN_TIMEOUT_MSEC_ (1000)
+
+static const char constInputMapperSuffix_[] = " - Input";
+static const char constOutputMapperSuffix_[] = " - Output";
+
+/********************************************************************/
+
+typedef struct PaWinMmeStream PaWinMmeStream; /* forward declaration */
+
+/* prototypes for functions declared in this file */
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif /* __cplusplus */
+
+PaError PaWinMme_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiIndex index );
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+static void Terminate( struct PaUtilHostApiRepresentation *hostApi );
+static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi,
+ PaStream** stream,
+ const PaStreamParameters *inputParameters,
+ const PaStreamParameters *outputParameters,
+ double sampleRate,
+ unsigned long framesPerBuffer,
+ PaStreamFlags streamFlags,
+ PaStreamCallback *streamCallback,
+ void *userData );
+static PaError IsFormatSupported( struct PaUtilHostApiRepresentation *hostApi,
+ const PaStreamParameters *inputParameters,
+ const PaStreamParameters *outputParameters,
+ double sampleRate );
+static PaError CloseStream( PaStream* stream );
+static PaError StartStream( PaStream *stream );
+static PaError StopStream( PaStream *stream );
+static PaError AbortStream( PaStream *stream );
+static PaError IsStreamStopped( PaStream *s );
+static PaError IsStreamActive( PaStream *stream );
+static PaTime GetStreamTime( PaStream *stream );
+static double GetStreamCpuLoad( PaStream* stream );
+static PaError ReadStream( PaStream* stream, void *buffer, unsigned long frames );
+static PaError WriteStream( PaStream* stream, const void *buffer, unsigned long frames );
+static signed long GetStreamReadAvailable( PaStream* stream );
+static signed long GetStreamWriteAvailable( PaStream* stream );
+
+
+/* macros for setting last host error information */
+
+#ifdef UNICODE
+
+#define PA_MME_SET_LAST_WAVEIN_ERROR( mmresult ) \
+ { \
+ wchar_t mmeErrorTextWide[ MAXERRORLENGTH ]; \
+ char mmeErrorText[ MAXERRORLENGTH ]; \
+ waveInGetErrorText( mmresult, mmeErrorTextWide, MAXERRORLENGTH ); \
+ WideCharToMultiByte( CP_ACP, WC_COMPOSITECHECK | WC_DEFAULTCHAR,\
+ mmeErrorTextWide, -1, mmeErrorText, MAXERRORLENGTH, NULL, NULL ); \
+ PaUtil_SetLastHostErrorInfo( paMME, mmresult, mmeErrorText ); \
+ }
+
+#define PA_MME_SET_LAST_WAVEOUT_ERROR( mmresult ) \
+ { \
+ wchar_t mmeErrorTextWide[ MAXERRORLENGTH ]; \
+ char mmeErrorText[ MAXERRORLENGTH ]; \
+ waveOutGetErrorText( mmresult, mmeErrorTextWide, MAXERRORLENGTH ); \
+ WideCharToMultiByte( CP_ACP, WC_COMPOSITECHECK | WC_DEFAULTCHAR,\
+ mmeErrorTextWide, -1, mmeErrorText, MAXERRORLENGTH, NULL, NULL ); \
+ PaUtil_SetLastHostErrorInfo( paMME, mmresult, mmeErrorText ); \
+ }
+
+#else /* !UNICODE */
+
+#define PA_MME_SET_LAST_WAVEIN_ERROR( mmresult ) \
+ { \
+ char mmeErrorText[ MAXERRORLENGTH ]; \
+ waveInGetErrorText( mmresult, mmeErrorText, MAXERRORLENGTH ); \
+ PaUtil_SetLastHostErrorInfo( paMME, mmresult, mmeErrorText ); \
+ }
+
+#define PA_MME_SET_LAST_WAVEOUT_ERROR( mmresult ) \
+ { \
+ char mmeErrorText[ MAXERRORLENGTH ]; \
+ waveOutGetErrorText( mmresult, mmeErrorText, MAXERRORLENGTH ); \
+ PaUtil_SetLastHostErrorInfo( paMME, mmresult, mmeErrorText ); \
+ }
+
+#endif /* UNICODE */
+
+
+static void PaMme_SetLastSystemError( DWORD errorCode )
+{
+ char *lpMsgBuf;
+ FormatMessage(
+ FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM,
+ NULL,
+ errorCode,
+ MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
+ (LPTSTR) &lpMsgBuf,
+ 0,
+ NULL
+ );
+ PaUtil_SetLastHostErrorInfo( paMME, errorCode, lpMsgBuf );
+ LocalFree( lpMsgBuf );
+}
+
+#define PA_MME_SET_LAST_SYSTEM_ERROR( errorCode ) \
+ PaMme_SetLastSystemError( errorCode )
+
+
+/* PaError returning wrappers for some commonly used win32 functions
+ note that we allow passing a null ptr to have no effect.
+*/
+
+static PaError CreateEventWithPaError( HANDLE *handle,
+ LPSECURITY_ATTRIBUTES lpEventAttributes,
+ BOOL bManualReset,
+ BOOL bInitialState,
+ LPCTSTR lpName )
+{
+ PaError result = paNoError;
+
+ *handle = NULL;
+
+ *handle = CreateEvent( lpEventAttributes, bManualReset, bInitialState, lpName );
+ if( *handle == NULL )
+ {
+ result = paUnanticipatedHostError;
+ PA_MME_SET_LAST_SYSTEM_ERROR( GetLastError() );
+ }
+
+ return result;
+}
+
+
+static PaError ResetEventWithPaError( HANDLE handle )
+{
+ PaError result = paNoError;
+
+ if( handle )
+ {
+ if( ResetEvent( handle ) == 0 )
+ {
+ result = paUnanticipatedHostError;
+ PA_MME_SET_LAST_SYSTEM_ERROR( GetLastError() );
+ }
+ }
+
+ return result;
+}
+
+
+static PaError CloseHandleWithPaError( HANDLE handle )
+{
+ PaError result = paNoError;
+
+ if( handle )
+ {
+ if( CloseHandle( handle ) == 0 )
+ {
+ result = paUnanticipatedHostError;
+ PA_MME_SET_LAST_SYSTEM_ERROR( GetLastError() );
+ }
+ }
+
+ return result;
+}
+
+
+/* PaWinMmeHostApiRepresentation - host api datastructure specific to this implementation */
+
+typedef struct
+{
+ PaUtilHostApiRepresentation inheritedHostApiRep;
+ PaUtilStreamInterface callbackStreamInterface;
+ PaUtilStreamInterface blockingStreamInterface;
+
+ PaUtilAllocationGroup *allocations;
+
+ int inputDeviceCount, outputDeviceCount;
+
+ /** winMmeDeviceIds is an array of WinMme device ids.
+ fields in the range [0, inputDeviceCount) are input device ids,
+ and [inputDeviceCount, inputDeviceCount + outputDeviceCount) are output
+ device ids.
+ */
+ UINT *winMmeDeviceIds;
+}
+PaWinMmeHostApiRepresentation;
+
+
+typedef struct
+{
+ PaDeviceInfo inheritedDeviceInfo;
+ DWORD dwFormats; /**<< standard formats bitmask from the WAVEINCAPS and WAVEOUTCAPS structures */
+}
+PaWinMmeDeviceInfo;
+
+
+/*************************************************************************
+ * Returns recommended device ID.
+ * On the PC, the recommended device can be specified by the user by
+ * setting an environment variable. For example, to use device #1.
+ *
+ * set PA_RECOMMENDED_OUTPUT_DEVICE=1
+ *
+ * The user should first determine the available device ID by using
+ * the supplied application "pa_devs".
+ */
+#define PA_ENV_BUF_SIZE_ (32)
+#define PA_REC_IN_DEV_ENV_NAME_ ("PA_RECOMMENDED_INPUT_DEVICE")
+#define PA_REC_OUT_DEV_ENV_NAME_ ("PA_RECOMMENDED_OUTPUT_DEVICE")
+static PaDeviceIndex GetEnvDefaultDeviceID( char *envName )
+{
+ PaDeviceIndex recommendedIndex = paNoDevice;
+ DWORD hresult;
+ char envbuf[PA_ENV_BUF_SIZE_];
+
+#ifndef WIN32_PLATFORM_PSPC /* no GetEnvironmentVariable on PocketPC */
+
+ /* Let user determine default device by setting environment variable. */
+ hresult = GetEnvironmentVariable( envName, envbuf, PA_ENV_BUF_SIZE_ );
+ if( (hresult > 0) && (hresult < PA_ENV_BUF_SIZE_) )
+ {
+ recommendedIndex = atoi( envbuf );
+ }
+#endif
+
+ return recommendedIndex;
+}
+
+
+static void InitializeDefaultDeviceIdsFromEnv( PaWinMmeHostApiRepresentation *hostApi )
+{
+ PaDeviceIndex device;
+
+ /* input */
+ device = GetEnvDefaultDeviceID( PA_REC_IN_DEV_ENV_NAME_ );
+ if( device != paNoDevice &&
+ ( device >= 0 && device < hostApi->inheritedHostApiRep.info.deviceCount ) &&
+ hostApi->inheritedHostApiRep.deviceInfos[ device ]->maxInputChannels > 0 )
+ {
+ hostApi->inheritedHostApiRep.info.defaultInputDevice = device;
+ }
+
+ /* output */
+ device = GetEnvDefaultDeviceID( PA_REC_OUT_DEV_ENV_NAME_ );
+ if( device != paNoDevice &&
+ ( device >= 0 && device < hostApi->inheritedHostApiRep.info.deviceCount ) &&
+ hostApi->inheritedHostApiRep.deviceInfos[ device ]->maxOutputChannels > 0 )
+ {
+ hostApi->inheritedHostApiRep.info.defaultOutputDevice = device;
+ }
+}
+
+
+/** Convert external PA ID to a windows multimedia device ID
+*/
+static UINT LocalDeviceIndexToWinMmeDeviceId( PaWinMmeHostApiRepresentation *hostApi, PaDeviceIndex device )
+{
+ assert( device >= 0 && device < hostApi->inputDeviceCount + hostApi->outputDeviceCount );
+
+ return hostApi->winMmeDeviceIds[ device ];
+}
+
+
+static PaError QueryInputWaveFormatEx( int deviceId, WAVEFORMATEX *waveFormatEx )
+{
+ MMRESULT mmresult;
+
+ switch( mmresult = waveInOpen( NULL, deviceId, waveFormatEx, 0, 0, WAVE_FORMAT_QUERY ) )
+ {
+ case MMSYSERR_NOERROR:
+ return paNoError;
+ case MMSYSERR_ALLOCATED: /* Specified resource is already allocated. */
+ return paDeviceUnavailable;
+ case MMSYSERR_NODRIVER: /* No device driver is present. */
+ return paDeviceUnavailable;
+ case MMSYSERR_NOMEM: /* Unable to allocate or lock memory. */
+ return paInsufficientMemory;
+ case WAVERR_BADFORMAT: /* Attempted to open with an unsupported waveform-audio format. */
+ return paSampleFormatNotSupported;
+
+ case MMSYSERR_BADDEVICEID: /* Specified device identifier is out of range. */
+ /* falls through */
+ default:
+ PA_MME_SET_LAST_WAVEIN_ERROR( mmresult );
+ return paUnanticipatedHostError;
+ }
+}
+
+
+static PaError QueryOutputWaveFormatEx( int deviceId, WAVEFORMATEX *waveFormatEx )
+{
+ MMRESULT mmresult;
+
+ switch( mmresult = waveOutOpen( NULL, deviceId, waveFormatEx, 0, 0, WAVE_FORMAT_QUERY ) )
+ {
+ case MMSYSERR_NOERROR:
+ return paNoError;
+ case MMSYSERR_ALLOCATED: /* Specified resource is already allocated. */
+ return paDeviceUnavailable;
+ case MMSYSERR_NODRIVER: /* No device driver is present. */
+ return paDeviceUnavailable;
+ case MMSYSERR_NOMEM: /* Unable to allocate or lock memory. */
+ return paInsufficientMemory;
+ case WAVERR_BADFORMAT: /* Attempted to open with an unsupported waveform-audio format. */
+ return paSampleFormatNotSupported;
+
+ case MMSYSERR_BADDEVICEID: /* Specified device identifier is out of range. */
+ /* falls through */
+ default:
+ PA_MME_SET_LAST_WAVEOUT_ERROR( mmresult );
+ return paUnanticipatedHostError;
+ }
+}
+
+
+static PaError QueryFormatSupported( PaDeviceInfo *deviceInfo,
+ PaError (*waveFormatExQueryFunction)(int, WAVEFORMATEX*),
+ int winMmeDeviceId, int channels, double sampleRate )
+{
+ PaWinMmeDeviceInfo *winMmeDeviceInfo = (PaWinMmeDeviceInfo*)deviceInfo;
+ WAVEFORMATEX waveFormatEx;
+
+ if( sampleRate == 11025.0
+ && ( (channels == 1 && (winMmeDeviceInfo->dwFormats & WAVE_FORMAT_1M16))
+ || (channels == 2 && (winMmeDeviceInfo->dwFormats & WAVE_FORMAT_1S16)) ) ){
+
+ return paNoError;
+ }
+
+ if( sampleRate == 22050.0
+ && ( (channels == 1 && (winMmeDeviceInfo->dwFormats & WAVE_FORMAT_2M16))
+ || (channels == 2 && (winMmeDeviceInfo->dwFormats & WAVE_FORMAT_2S16)) ) ){
+
+ return paNoError;
+ }
+
+ if( sampleRate == 44100.0
+ && ( (channels == 1 && (winMmeDeviceInfo->dwFormats & WAVE_FORMAT_4M16))
+ || (channels == 2 && (winMmeDeviceInfo->dwFormats & WAVE_FORMAT_4S16)) ) ){
+
+ return paNoError;
+ }
+
+ waveFormatEx.wFormatTag = WAVE_FORMAT_PCM;
+ waveFormatEx.nChannels = (WORD)channels;
+ waveFormatEx.nSamplesPerSec = (DWORD)sampleRate;
+ waveFormatEx.nAvgBytesPerSec = waveFormatEx.nSamplesPerSec * channels * sizeof(short);
+ waveFormatEx.nBlockAlign = (WORD)(channels * sizeof(short));
+ waveFormatEx.wBitsPerSample = 16;
+ waveFormatEx.cbSize = 0;
+
+ return waveFormatExQueryFunction( winMmeDeviceId, &waveFormatEx );
+}
+
+
+#define PA_DEFAULTSAMPLERATESEARCHORDER_COUNT_ (13) /* must match array length below */
+static double defaultSampleRateSearchOrder_[] =
+ { 44100.0, 48000.0, 32000.0, 24000.0, 22050.0, 88200.0, 96000.0, 192000.0,
+ 16000.0, 12000.0, 11025.0, 9600.0, 8000.0 };
+
+static void DetectDefaultSampleRate( PaWinMmeDeviceInfo *winMmeDeviceInfo, int winMmeDeviceId,
+ PaError (*waveFormatExQueryFunction)(int, WAVEFORMATEX*), int maxChannels )
+{
+ PaDeviceInfo *deviceInfo = &winMmeDeviceInfo->inheritedDeviceInfo;
+ int i;
+
+ deviceInfo->defaultSampleRate = 0.;
+
+ for( i=0; i < PA_DEFAULTSAMPLERATESEARCHORDER_COUNT_; ++i )
+ {
+ double sampleRate = defaultSampleRateSearchOrder_[ i ];
+ PaError paerror = QueryFormatSupported( deviceInfo, waveFormatExQueryFunction, winMmeDeviceId, maxChannels, sampleRate );
+ if( paerror == paNoError )
+ {
+ deviceInfo->defaultSampleRate = sampleRate;
+ break;
+ }
+ }
+}
+
+
+static PaError InitializeInputDeviceInfo( PaWinMmeHostApiRepresentation *winMmeHostApi,
+ PaWinMmeDeviceInfo *winMmeDeviceInfo, UINT winMmeInputDeviceId, int *success )
+{
+ PaError result = paNoError;
+ char *deviceName; /* non-const ptr */
+ MMRESULT mmresult;
+ WAVEINCAPS wic;
+ PaDeviceInfo *deviceInfo = &winMmeDeviceInfo->inheritedDeviceInfo;
+
+ *success = 0;
+
+ mmresult = waveInGetDevCaps( winMmeInputDeviceId, &wic, sizeof( WAVEINCAPS ) );
+ if( mmresult == MMSYSERR_NOMEM )
+ {
+ result = paInsufficientMemory;
+ goto error;
+ }
+ else if( mmresult != MMSYSERR_NOERROR )
+ {
+ /* instead of returning paUnanticipatedHostError we return
+ paNoError, but leave success set as 0. This allows
+ Pa_Initialize to just ignore this device, without failing
+ the entire initialisation process.
+ */
+ return paNoError;
+ }
+
+ if( winMmeInputDeviceId == WAVE_MAPPER )
+ {
+ /* Append I/O suffix to WAVE_MAPPER device. */
+ deviceName = (char *)PaUtil_GroupAllocateMemory(
+ winMmeHostApi->allocations, strlen( wic.szPname ) + 1 + sizeof(constInputMapperSuffix_) );
+ if( !deviceName )
+ {
+ result = paInsufficientMemory;
+ goto error;
+ }
+ strcpy( deviceName, wic.szPname );
+ strcat( deviceName, constInputMapperSuffix_ );
+ }
+ else
+ {
+ deviceName = (char*)PaUtil_GroupAllocateMemory(
+ winMmeHostApi->allocations, strlen( wic.szPname ) + 1 );
+ if( !deviceName )
+ {
+ result = paInsufficientMemory;
+ goto error;
+ }
+ strcpy( deviceName, wic.szPname );
+ }
+ deviceInfo->name = deviceName;
+
+ deviceInfo->maxInputChannels = wic.wChannels;
+ /* Sometimes a device can return a rediculously large number of channels.
+ * This happened with an SBLive card on a Windows ME box.
+ * If that happens, then force it to 2 channels. PLB20010413
+ */
+ if( (deviceInfo->maxInputChannels < 1) || (deviceInfo->maxInputChannels > 256) )
+ {
+ PA_DEBUG(("Pa_GetDeviceInfo: Num input channels reported as %d! Changed to 2.\n", deviceInfo->maxInputChannels ));
+ deviceInfo->maxInputChannels = 2;
+ }
+
+ winMmeDeviceInfo->dwFormats = wic.dwFormats;
+
+ DetectDefaultSampleRate( winMmeDeviceInfo, winMmeInputDeviceId,
+ QueryInputWaveFormatEx, deviceInfo->maxInputChannels );
+
+ *success = 1;
+
+error:
+ return result;
+}
+
+
+static PaError InitializeOutputDeviceInfo( PaWinMmeHostApiRepresentation *winMmeHostApi,
+ PaWinMmeDeviceInfo *winMmeDeviceInfo, UINT winMmeOutputDeviceId, int *success )
+{
+ PaError result = paNoError;
+ char *deviceName; /* non-const ptr */
+ MMRESULT mmresult;
+ WAVEOUTCAPS woc;
+ PaDeviceInfo *deviceInfo = &winMmeDeviceInfo->inheritedDeviceInfo;
+
+ *success = 0;
+
+ mmresult = waveOutGetDevCaps( winMmeOutputDeviceId, &woc, sizeof( WAVEOUTCAPS ) );
+ if( mmresult == MMSYSERR_NOMEM )
+ {
+ result = paInsufficientMemory;
+ goto error;
+ }
+ else if( mmresult != MMSYSERR_NOERROR )
+ {
+ /* instead of returning paUnanticipatedHostError we return
+ paNoError, but leave success set as 0. This allows
+ Pa_Initialize to just ignore this device, without failing
+ the entire initialisation process.
+ */
+ return paNoError;
+ }
+
+ if( winMmeOutputDeviceId == WAVE_MAPPER )
+ {
+ /* Append I/O suffix to WAVE_MAPPER device. */
+ deviceName = (char *)PaUtil_GroupAllocateMemory(
+ winMmeHostApi->allocations, strlen( woc.szPname ) + 1 + sizeof(constOutputMapperSuffix_) );
+ if( !deviceName )
+ {
+ result = paInsufficientMemory;
+ goto error;
+ }
+ strcpy( deviceName, woc.szPname );
+ strcat( deviceName, constOutputMapperSuffix_ );
+ }
+ else
+ {
+ deviceName = (char*)PaUtil_GroupAllocateMemory(
+ winMmeHostApi->allocations, strlen( woc.szPname ) + 1 );
+ if( !deviceName )
+ {
+ result = paInsufficientMemory;
+ goto error;
+ }
+ strcpy( deviceName, woc.szPname );
+ }
+ deviceInfo->name = deviceName;
+
+ deviceInfo->maxOutputChannels = woc.wChannels;
+ /* Sometimes a device can return a rediculously large number of channels.
+ * This happened with an SBLive card on a Windows ME box.
+ * It also happens on Win XP!
+ */
+ if( (deviceInfo->maxOutputChannels < 1) || (deviceInfo->maxOutputChannels > 256) )
+ {
+ PA_DEBUG(("Pa_GetDeviceInfo: Num output channels reported as %d! Changed to 2.\n", deviceInfo->maxOutputChannels ));
+ deviceInfo->maxOutputChannels = 2;
+ }
+
+ winMmeDeviceInfo->dwFormats = woc.dwFormats;
+
+ DetectDefaultSampleRate( winMmeDeviceInfo, winMmeOutputDeviceId,
+ QueryOutputWaveFormatEx, deviceInfo->maxOutputChannels );
+
+ *success = 1;
+
+error:
+ return result;
+}
+
+
+static void GetDefaultLatencies( PaTime *defaultLowLatency, PaTime *defaultHighLatency )
+{
+ OSVERSIONINFO osvi;
+ osvi.dwOSVersionInfoSize = sizeof( osvi );
+ GetVersionEx( &osvi );
+
+ /* Check for NT */
+ if( (osvi.dwMajorVersion == 4) && (osvi.dwPlatformId == 2) )
+ {
+ *defaultLowLatency = PA_MME_WIN_NT_DEFAULT_LATENCY_;
+ }
+ else if(osvi.dwMajorVersion >= 5)
+ {
+ *defaultLowLatency = PA_MME_WIN_WDM_DEFAULT_LATENCY_;
+ }
+ else
+ {
+ *defaultLowLatency = PA_MME_WIN_9X_DEFAULT_LATENCY_;
+ }
+
+ *defaultHighLatency = *defaultLowLatency * 2;
+}
+
+
+PaError PaWinMme_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiIndex hostApiIndex )
+{
+ PaError result = paNoError;
+ int i;
+ PaWinMmeHostApiRepresentation *winMmeHostApi;
+ int inputDeviceCount, outputDeviceCount, maximumPossibleDeviceCount;
+ PaWinMmeDeviceInfo *deviceInfoArray;
+ int deviceInfoInitializationSucceeded;
+ PaTime defaultLowLatency, defaultHighLatency;
+
+ winMmeHostApi = (PaWinMmeHostApiRepresentation*)PaUtil_AllocateMemory( sizeof(PaWinMmeHostApiRepresentation) );
+ if( !winMmeHostApi )
+ {
+ result = paInsufficientMemory;
+ goto error;
+ }
+
+ winMmeHostApi->allocations = PaUtil_CreateAllocationGroup();
+ if( !winMmeHostApi->allocations )
+ {
+ result = paInsufficientMemory;
+ goto error;
+ }
+
+ *hostApi = &winMmeHostApi->inheritedHostApiRep;
+ (*hostApi)->info.structVersion = 1;
+ (*hostApi)->info.type = paMME;
+ (*hostApi)->info.name = "MME";
+
+
+ /* initialise device counts and default devices under the assumption that
+ there are no devices. These values are incremented below if and when
+ devices are successfully initialized.
+ */
+ (*hostApi)->info.deviceCount = 0;
+ (*hostApi)->info.defaultInputDevice = paNoDevice;
+ (*hostApi)->info.defaultOutputDevice = paNoDevice;
+ winMmeHostApi->inputDeviceCount = 0;
+ winMmeHostApi->outputDeviceCount = 0;
+
+
+ maximumPossibleDeviceCount = 0;
+
+ inputDeviceCount = waveInGetNumDevs();
+ if( inputDeviceCount > 0 )
+ maximumPossibleDeviceCount += inputDeviceCount + 1; /* assume there is a WAVE_MAPPER */
+
+ outputDeviceCount = waveOutGetNumDevs();
+ if( outputDeviceCount > 0 )
+ maximumPossibleDeviceCount += outputDeviceCount + 1; /* assume there is a WAVE_MAPPER */
+
+
+ if( maximumPossibleDeviceCount > 0 ){
+
+ (*hostApi)->deviceInfos = (PaDeviceInfo**)PaUtil_GroupAllocateMemory(
+ winMmeHostApi->allocations, sizeof(PaDeviceInfo*) * maximumPossibleDeviceCount );
+ if( !(*hostApi)->deviceInfos )
+ {
+ result = paInsufficientMemory;
+ goto error;
+ }
+
+ /* allocate all device info structs in a contiguous block */
+ deviceInfoArray = (PaWinMmeDeviceInfo*)PaUtil_GroupAllocateMemory(
+ winMmeHostApi->allocations, sizeof(PaWinMmeDeviceInfo) * maximumPossibleDeviceCount );
+ if( !deviceInfoArray )
+ {
+ result = paInsufficientMemory;
+ goto error;
+ }
+
+ winMmeHostApi->winMmeDeviceIds = (UINT*)PaUtil_GroupAllocateMemory(
+ winMmeHostApi->allocations, sizeof(int) * maximumPossibleDeviceCount );
+ if( !winMmeHostApi->winMmeDeviceIds )
+ {
+ result = paInsufficientMemory;
+ goto error;
+ }
+
+ GetDefaultLatencies( &defaultLowLatency, &defaultHighLatency );
+
+ if( inputDeviceCount > 0 ){
+ /* -1 is the WAVE_MAPPER */
+ for( i = -1; i < inputDeviceCount; ++i ){
+ UINT winMmeDeviceId = (UINT)((i==-1) ? WAVE_MAPPER : i);
+ PaWinMmeDeviceInfo *wmmeDeviceInfo = &deviceInfoArray[ (*hostApi)->info.deviceCount ];
+ PaDeviceInfo *deviceInfo = &wmmeDeviceInfo->inheritedDeviceInfo;
+ deviceInfo->structVersion = 2;
+ deviceInfo->hostApi = hostApiIndex;
+
+ deviceInfo->maxInputChannels = 0;
+ deviceInfo->maxOutputChannels = 0;
+
+ deviceInfo->defaultLowInputLatency = defaultLowLatency;
+ deviceInfo->defaultLowOutputLatency = defaultLowLatency;
+ deviceInfo->defaultHighInputLatency = defaultHighLatency;
+ deviceInfo->defaultHighOutputLatency = defaultHighLatency;
+
+ result = InitializeInputDeviceInfo( winMmeHostApi, wmmeDeviceInfo,
+ winMmeDeviceId, &deviceInfoInitializationSucceeded );
+ if( result != paNoError )
+ goto error;
+
+ if( deviceInfoInitializationSucceeded ){
+ if( (*hostApi)->info.defaultInputDevice == paNoDevice )
+ (*hostApi)->info.defaultInputDevice = (*hostApi)->info.deviceCount;
+
+ winMmeHostApi->winMmeDeviceIds[ (*hostApi)->info.deviceCount ] = winMmeDeviceId;
+ (*hostApi)->deviceInfos[ (*hostApi)->info.deviceCount ] = deviceInfo;
+
+ winMmeHostApi->inputDeviceCount++;
+ (*hostApi)->info.deviceCount++;
+ }
+ }
+ }
+
+ if( outputDeviceCount > 0 ){
+ /* -1 is the WAVE_MAPPER */
+ for( i = -1; i < outputDeviceCount; ++i ){
+ UINT winMmeDeviceId = (UINT)((i==-1) ? WAVE_MAPPER : i);
+ PaWinMmeDeviceInfo *wmmeDeviceInfo = &deviceInfoArray[ (*hostApi)->info.deviceCount ];
+ PaDeviceInfo *deviceInfo = &wmmeDeviceInfo->inheritedDeviceInfo;
+ deviceInfo->structVersion = 2;
+ deviceInfo->hostApi = hostApiIndex;
+
+ deviceInfo->maxInputChannels = 0;
+ deviceInfo->maxOutputChannels = 0;
+
+ deviceInfo->defaultLowInputLatency = defaultLowLatency;
+ deviceInfo->defaultLowOutputLatency = defaultLowLatency;
+ deviceInfo->defaultHighInputLatency = defaultHighLatency;
+ deviceInfo->defaultHighOutputLatency = defaultHighLatency;
+
+ result = InitializeOutputDeviceInfo( winMmeHostApi, wmmeDeviceInfo,
+ winMmeDeviceId, &deviceInfoInitializationSucceeded );
+ if( result != paNoError )
+ goto error;
+
+ if( deviceInfoInitializationSucceeded ){
+ if( (*hostApi)->info.defaultOutputDevice == paNoDevice )
+ (*hostApi)->info.defaultOutputDevice = (*hostApi)->info.deviceCount;
+
+ winMmeHostApi->winMmeDeviceIds[ (*hostApi)->info.deviceCount ] = winMmeDeviceId;
+ (*hostApi)->deviceInfos[ (*hostApi)->info.deviceCount ] = deviceInfo;
+
+ winMmeHostApi->outputDeviceCount++;
+ (*hostApi)->info.deviceCount++;
+ }
+ }
+ }
+ }
+
+
+ InitializeDefaultDeviceIdsFromEnv( winMmeHostApi );
+
+ (*hostApi)->Terminate = Terminate;
+ (*hostApi)->OpenStream = OpenStream;
+ (*hostApi)->IsFormatSupported = IsFormatSupported;
+
+ PaUtil_InitializeStreamInterface( &winMmeHostApi->callbackStreamInterface, CloseStream, StartStream,
+ StopStream, AbortStream, IsStreamStopped, IsStreamActive,
+ GetStreamTime, GetStreamCpuLoad,
+ PaUtil_DummyRead, PaUtil_DummyWrite,
+ PaUtil_DummyGetReadAvailable, PaUtil_DummyGetWriteAvailable );
+
+ PaUtil_InitializeStreamInterface( &winMmeHostApi->blockingStreamInterface, CloseStream, StartStream,
+ StopStream, AbortStream, IsStreamStopped, IsStreamActive,
+ GetStreamTime, PaUtil_DummyGetCpuLoad,
+ ReadStream, WriteStream, GetStreamReadAvailable, GetStreamWriteAvailable );
+
+ return result;
+
+error:
+ if( winMmeHostApi )
+ {
+ if( winMmeHostApi->allocations )
+ {
+ PaUtil_FreeAllAllocations( winMmeHostApi->allocations );
+ PaUtil_DestroyAllocationGroup( winMmeHostApi->allocations );
+ }
+
+ PaUtil_FreeMemory( winMmeHostApi );
+ }
+
+ return result;
+}
+
+
+static void Terminate( struct PaUtilHostApiRepresentation *hostApi )
+{
+ PaWinMmeHostApiRepresentation *winMmeHostApi = (PaWinMmeHostApiRepresentation*)hostApi;
+
+ if( winMmeHostApi->allocations )
+ {
+ PaUtil_FreeAllAllocations( winMmeHostApi->allocations );
+ PaUtil_DestroyAllocationGroup( winMmeHostApi->allocations );
+ }
+
+ PaUtil_FreeMemory( winMmeHostApi );
+}
+
+
+static PaError IsFormatSupported( struct PaUtilHostApiRepresentation *hostApi,
+ const PaStreamParameters *inputParameters,
+ const PaStreamParameters *outputParameters,
+ double sampleRate )
+{
+ PaWinMmeHostApiRepresentation *winMmeHostApi = (PaWinMmeHostApiRepresentation*)hostApi;
+ PaDeviceInfo *inputDeviceInfo, *outputDeviceInfo;
+ int inputChannelCount, outputChannelCount;
+ int inputMultipleDeviceChannelCount, outputMultipleDeviceChannelCount;
+ PaSampleFormat inputSampleFormat, outputSampleFormat;
+ PaWinMmeStreamInfo *inputStreamInfo, *outputStreamInfo;
+ UINT winMmeInputDeviceId, winMmeOutputDeviceId;
+ unsigned int i;
+ PaError paerror;
+
+ /* The calls to QueryFormatSupported below are intended to detect invalid
+ sample rates. If we assume that the channel count and format are OK,
+ then the only thing that could fail is the sample rate. This isn't
+ strictly true, but I can't think of a better way to test that the
+ sample rate is valid.
+ */
+
+ if( inputParameters )
+ {
+ inputChannelCount = inputParameters->channelCount;
+ inputSampleFormat = inputParameters->sampleFormat;
+ inputStreamInfo = inputParameters->hostApiSpecificStreamInfo;
+
+ /* all standard sample formats are supported by the buffer adapter,
+ this implementation doesn't support any custom sample formats */
+ if( inputSampleFormat & paCustomFormat )
+ return paSampleFormatNotSupported;
+
+ if( inputParameters->device == paUseHostApiSpecificDeviceSpecification
+ && inputStreamInfo && (inputStreamInfo->flags & paWinMmeUseMultipleDevices) )
+ {
+ inputMultipleDeviceChannelCount = 0;
+ for( i=0; i< inputStreamInfo->deviceCount; ++i )
+ {
+ inputMultipleDeviceChannelCount += inputStreamInfo->devices[i].channelCount;
+
+ inputDeviceInfo = hostApi->deviceInfos[ inputStreamInfo->devices[i].device ];
+
+ /* check that input device can support inputChannelCount */
+ if( inputStreamInfo->devices[i].channelCount <= 0
+ || inputStreamInfo->devices[i].channelCount > inputDeviceInfo->maxInputChannels )
+ return paInvalidChannelCount;
+
+ /* test for valid sample rate, see comment above */
+ winMmeInputDeviceId = LocalDeviceIndexToWinMmeDeviceId( winMmeHostApi, inputStreamInfo->devices[i].device );
+ paerror = QueryFormatSupported( inputDeviceInfo, QueryInputWaveFormatEx, winMmeInputDeviceId, inputStreamInfo->devices[i].channelCount, sampleRate );
+ if( paerror != paNoError )
+ return paInvalidSampleRate;
+ }
+
+ if( inputMultipleDeviceChannelCount != inputChannelCount )
+ return paIncompatibleHostApiSpecificStreamInfo;
+ }
+ else
+ {
+ if( inputStreamInfo && (inputStreamInfo->flags & paWinMmeUseMultipleDevices) )
+ return paIncompatibleHostApiSpecificStreamInfo; /* paUseHostApiSpecificDeviceSpecification was not supplied as the input device */
+
+ inputDeviceInfo = hostApi->deviceInfos[ inputParameters->device ];
+
+ /* check that input device can support inputChannelCount */
+ if( inputChannelCount > inputDeviceInfo->maxInputChannels )
+ return paInvalidChannelCount;
+
+ /* test for valid sample rate, see comment above */
+ winMmeInputDeviceId = LocalDeviceIndexToWinMmeDeviceId( winMmeHostApi, inputParameters->device );
+ paerror = QueryFormatSupported( inputDeviceInfo, QueryInputWaveFormatEx, winMmeInputDeviceId, inputChannelCount, sampleRate );
+ if( paerror != paNoError )
+ return paInvalidSampleRate;
+ }
+ }
+
+ if( outputParameters )
+ {
+ outputChannelCount = outputParameters->channelCount;
+ outputSampleFormat = outputParameters->sampleFormat;
+ outputStreamInfo = outputParameters->hostApiSpecificStreamInfo;
+
+ /* all standard sample formats are supported by the buffer adapter,
+ this implementation doesn't support any custom sample formats */
+ if( outputSampleFormat & paCustomFormat )
+ return paSampleFormatNotSupported;
+
+ if( outputParameters->device == paUseHostApiSpecificDeviceSpecification
+ && outputStreamInfo && (outputStreamInfo->flags & paWinMmeUseMultipleDevices) )
+ {
+ outputMultipleDeviceChannelCount = 0;
+ for( i=0; i< outputStreamInfo->deviceCount; ++i )
+ {
+ outputMultipleDeviceChannelCount += outputStreamInfo->devices[i].channelCount;
+
+ outputDeviceInfo = hostApi->deviceInfos[ outputStreamInfo->devices[i].device ];
+
+ /* check that output device can support outputChannelCount */
+ if( outputStreamInfo->devices[i].channelCount <= 0
+ || outputStreamInfo->devices[i].channelCount > outputDeviceInfo->maxOutputChannels )
+ return paInvalidChannelCount;
+
+ /* test for valid sample rate, see comment above */
+ winMmeOutputDeviceId = LocalDeviceIndexToWinMmeDeviceId( winMmeHostApi, outputStreamInfo->devices[i].device );
+ paerror = QueryFormatSupported( outputDeviceInfo, QueryOutputWaveFormatEx, winMmeOutputDeviceId, outputStreamInfo->devices[i].channelCount, sampleRate );
+ if( paerror != paNoError )
+ return paInvalidSampleRate;
+ }
+
+ if( outputMultipleDeviceChannelCount != outputChannelCount )
+ return paIncompatibleHostApiSpecificStreamInfo;
+ }
+ else
+ {
+ if( outputStreamInfo && (outputStreamInfo->flags & paWinMmeUseMultipleDevices) )
+ return paIncompatibleHostApiSpecificStreamInfo; /* paUseHostApiSpecificDeviceSpecification was not supplied as the output device */
+
+ outputDeviceInfo = hostApi->deviceInfos[ outputParameters->device ];
+
+ /* check that output device can support outputChannelCount */
+ if( outputChannelCount > outputDeviceInfo->maxOutputChannels )
+ return paInvalidChannelCount;
+
+ /* test for valid sample rate, see comment above */
+ winMmeOutputDeviceId = LocalDeviceIndexToWinMmeDeviceId( winMmeHostApi, outputParameters->device );
+ paerror = QueryFormatSupported( outputDeviceInfo, QueryOutputWaveFormatEx, winMmeOutputDeviceId, outputChannelCount, sampleRate );
+ if( paerror != paNoError )
+ return paInvalidSampleRate;
+ }
+ }
+
+ /*
+ - if a full duplex stream is requested, check that the combination
+ of input and output parameters is supported
+
+ - check that the device supports sampleRate
+
+ for mme all we can do is test that the input and output devices
+ support the requested sample rate and number of channels. we
+ cannot test for full duplex compatibility.
+ */
+
+ return paFormatIsSupported;
+}
+
+
+
+static void SelectBufferSizeAndCount( unsigned long baseBufferSize,
+ unsigned long requestedLatency,
+ unsigned long baseBufferCount, unsigned long minimumBufferCount,
+ unsigned long maximumBufferSize, unsigned long *hostBufferSize,
+ unsigned long *hostBufferCount )
+{
+ unsigned long sizeMultiplier, bufferCount, latency;
+ unsigned long nextLatency, nextBufferSize;
+ int baseBufferSizeIsPowerOfTwo;
+
+ sizeMultiplier = 1;
+ bufferCount = baseBufferCount;
+
+ /* count-1 below because latency is always determined by one less
+ than the total number of buffers.
+ */
+ latency = (baseBufferSize * sizeMultiplier) * (bufferCount-1);
+
+ if( latency > requestedLatency )
+ {
+
+ /* reduce number of buffers without falling below suggested latency */
+
+ nextLatency = (baseBufferSize * sizeMultiplier) * (bufferCount-2);
+ while( bufferCount > minimumBufferCount && nextLatency >= requestedLatency )
+ {
+ --bufferCount;
+ nextLatency = (baseBufferSize * sizeMultiplier) * (bufferCount-2);
+ }
+
+ }else if( latency < requestedLatency ){
+
+ baseBufferSizeIsPowerOfTwo = (! (baseBufferSize & (baseBufferSize - 1)));
+ if( baseBufferSizeIsPowerOfTwo ){
+
+ /* double size of buffers without exceeding requestedLatency */
+
+ nextBufferSize = (baseBufferSize * (sizeMultiplier*2));
+ nextLatency = nextBufferSize * (bufferCount-1);
+ while( nextBufferSize <= maximumBufferSize
+ && nextLatency < requestedLatency )
+ {
+ sizeMultiplier *= 2;
+ nextBufferSize = (baseBufferSize * (sizeMultiplier*2));
+ nextLatency = nextBufferSize * (bufferCount-1);
+ }
+
+ }else{
+
+ /* increase size of buffers upto first excess of requestedLatency */
+
+ nextBufferSize = (baseBufferSize * (sizeMultiplier+1));
+ nextLatency = nextBufferSize * (bufferCount-1);
+ while( nextBufferSize <= maximumBufferSize
+ && nextLatency < requestedLatency )
+ {
+ ++sizeMultiplier;
+ nextBufferSize = (baseBufferSize * (sizeMultiplier+1));
+ nextLatency = nextBufferSize * (bufferCount-1);
+ }
+
+ if( nextLatency < requestedLatency )
+ ++sizeMultiplier;
+ }
+
+ /* increase number of buffers until requestedLatency is reached */
+
+ latency = (baseBufferSize * sizeMultiplier) * (bufferCount-1);
+ while( latency < requestedLatency )
+ {
+ ++bufferCount;
+ latency = (baseBufferSize * sizeMultiplier) * (bufferCount-1);
+ }
+ }
+
+ *hostBufferSize = baseBufferSize * sizeMultiplier;
+ *hostBufferCount = bufferCount;
+}
+
+
+static void ReselectBufferCount( unsigned long bufferSize,
+ unsigned long requestedLatency,
+ unsigned long baseBufferCount, unsigned long minimumBufferCount,
+ unsigned long *hostBufferCount )
+{
+ unsigned long bufferCount, latency;
+ unsigned long nextLatency;
+
+ bufferCount = baseBufferCount;
+
+ /* count-1 below because latency is always determined by one less
+ than the total number of buffers.
+ */
+ latency = bufferSize * (bufferCount-1);
+
+ if( latency > requestedLatency )
+ {
+ /* reduce number of buffers without falling below suggested latency */
+
+ nextLatency = bufferSize * (bufferCount-2);
+ while( bufferCount > minimumBufferCount && nextLatency >= requestedLatency )
+ {
+ --bufferCount;
+ nextLatency = bufferSize * (bufferCount-2);
+ }
+
+ }else if( latency < requestedLatency ){
+
+ /* increase number of buffers until requestedLatency is reached */
+
+ latency = bufferSize * (bufferCount-1);
+ while( latency < requestedLatency )
+ {
+ ++bufferCount;
+ latency = bufferSize * (bufferCount-1);
+ }
+ }
+
+ *hostBufferCount = bufferCount;
+}
+
+
+/* CalculateBufferSettings() fills the framesPerHostInputBuffer, hostInputBufferCount,
+ framesPerHostOutputBuffer and hostOutputBufferCount parameters based on the values
+ of the other parameters.
+*/
+
+static PaError CalculateBufferSettings(
+ unsigned long *framesPerHostInputBuffer, unsigned long *hostInputBufferCount,
+ unsigned long *framesPerHostOutputBuffer, unsigned long *hostOutputBufferCount,
+ int inputChannelCount, PaSampleFormat hostInputSampleFormat,
+ PaTime suggestedInputLatency, PaWinMmeStreamInfo *inputStreamInfo,
+ int outputChannelCount, PaSampleFormat hostOutputSampleFormat,
+ PaTime suggestedOutputLatency, PaWinMmeStreamInfo *outputStreamInfo,
+ double sampleRate, unsigned long framesPerBuffer )
+{
+ PaError result = paNoError;
+ int effectiveInputChannelCount, effectiveOutputChannelCount;
+ int hostInputFrameSize = 0;
+ unsigned int i;
+
+ if( inputChannelCount > 0 )
+ {
+ int hostInputSampleSize = Pa_GetSampleSize( hostInputSampleFormat );
+ if( hostInputSampleSize < 0 )
+ {
+ result = hostInputSampleSize;
+ goto error;
+ }
+
+ if( inputStreamInfo
+ && ( inputStreamInfo->flags & paWinMmeUseMultipleDevices ) )
+ {
+ /* set effectiveInputChannelCount to the largest number of
+ channels on any one device.
+ */
+ effectiveInputChannelCount = 0;
+ for( i=0; i< inputStreamInfo->deviceCount; ++i )
+ {
+ if( inputStreamInfo->devices[i].channelCount > effectiveInputChannelCount )
+ effectiveInputChannelCount = inputStreamInfo->devices[i].channelCount;
+ }
+ }
+ else
+ {
+ effectiveInputChannelCount = inputChannelCount;
+ }
+
+ hostInputFrameSize = hostInputSampleSize * effectiveInputChannelCount;
+
+ if( inputStreamInfo
+ && ( inputStreamInfo->flags & paWinMmeUseLowLevelLatencyParameters ) )
+ {
+ if( inputStreamInfo->bufferCount <= 0
+ || inputStreamInfo->framesPerBuffer <= 0 )
+ {
+ result = paIncompatibleHostApiSpecificStreamInfo;
+ goto error;
+ }
+
+ *framesPerHostInputBuffer = inputStreamInfo->framesPerBuffer;
+ *hostInputBufferCount = inputStreamInfo->bufferCount;
+ }
+ else
+ {
+ unsigned long hostBufferSizeBytes, hostBufferCount;
+ unsigned long minimumBufferCount = (outputChannelCount > 0)
+ ? PA_MME_MIN_HOST_INPUT_BUFFER_COUNT_FULL_DUPLEX_
+ : PA_MME_MIN_HOST_INPUT_BUFFER_COUNT_HALF_DUPLEX_;
+
+ unsigned long maximumBufferSize = (long) ((PA_MME_MAX_HOST_BUFFER_SECS_ * sampleRate) * hostInputFrameSize);
+ if( maximumBufferSize > PA_MME_MAX_HOST_BUFFER_BYTES_ )
+ maximumBufferSize = PA_MME_MAX_HOST_BUFFER_BYTES_;
+
+ /* compute the following in bytes, then convert back to frames */
+
+ SelectBufferSizeAndCount(
+ ((framesPerBuffer == paFramesPerBufferUnspecified)
+ ? PA_MME_MIN_HOST_BUFFER_FRAMES_WHEN_UNSPECIFIED_
+ : framesPerBuffer ) * hostInputFrameSize, /* baseBufferSize */
+ ((unsigned long)(suggestedInputLatency * sampleRate)) * hostInputFrameSize, /* suggestedLatency */
+ 4, /* baseBufferCount */
+ minimumBufferCount, maximumBufferSize,
+ &hostBufferSizeBytes, &hostBufferCount );
+
+ *framesPerHostInputBuffer = hostBufferSizeBytes / hostInputFrameSize;
+ *hostInputBufferCount = hostBufferCount;
+ }
+ }
+ else
+ {
+ *framesPerHostInputBuffer = 0;
+ *hostInputBufferCount = 0;
+ }
+
+ if( outputChannelCount > 0 )
+ {
+ if( outputStreamInfo
+ && ( outputStreamInfo->flags & paWinMmeUseLowLevelLatencyParameters ) )
+ {
+ if( outputStreamInfo->bufferCount <= 0
+ || outputStreamInfo->framesPerBuffer <= 0 )
+ {
+ result = paIncompatibleHostApiSpecificStreamInfo;
+ goto error;
+ }
+
+ *framesPerHostOutputBuffer = outputStreamInfo->framesPerBuffer;
+ *hostOutputBufferCount = outputStreamInfo->bufferCount;
+
+
+ if( inputChannelCount > 0 ) /* full duplex */
+ {
+ if( *framesPerHostInputBuffer != *framesPerHostOutputBuffer )
+ {
+ if( inputStreamInfo
+ && ( inputStreamInfo->flags & paWinMmeUseLowLevelLatencyParameters ) )
+ {
+ /* a custom StreamInfo was used for specifying both input
+ and output buffer sizes, the larger buffer size
+ must be a multiple of the smaller buffer size */
+
+ if( *framesPerHostInputBuffer < *framesPerHostOutputBuffer )
+ {
+ if( *framesPerHostOutputBuffer % *framesPerHostInputBuffer != 0 )
+ {
+ result = paIncompatibleHostApiSpecificStreamInfo;
+ goto error;
+ }
+ }
+ else
+ {
+ assert( *framesPerHostInputBuffer > *framesPerHostOutputBuffer );
+ if( *framesPerHostInputBuffer % *framesPerHostOutputBuffer != 0 )
+ {
+ result = paIncompatibleHostApiSpecificStreamInfo;
+ goto error;
+ }
+ }
+ }
+ else
+ {
+ /* a custom StreamInfo was not used for specifying the input buffer size,
+ so use the output buffer size, and approximately the same latency. */
+
+ *framesPerHostInputBuffer = *framesPerHostOutputBuffer;
+ *hostInputBufferCount = (((unsigned long)(suggestedInputLatency * sampleRate)) / *framesPerHostInputBuffer) + 1;
+
+ if( *hostInputBufferCount < PA_MME_MIN_HOST_INPUT_BUFFER_COUNT_FULL_DUPLEX_ )
+ *hostInputBufferCount = PA_MME_MIN_HOST_INPUT_BUFFER_COUNT_FULL_DUPLEX_;
+ }
+ }
+ }
+ }
+ else
+ {
+ unsigned long hostBufferSizeBytes, hostBufferCount;
+ unsigned long minimumBufferCount = PA_MME_MIN_HOST_OUTPUT_BUFFER_COUNT_;
+ unsigned long maximumBufferSize;
+ int hostOutputFrameSize;
+ int hostOutputSampleSize;
+
+ hostOutputSampleSize = Pa_GetSampleSize( hostOutputSampleFormat );
+ if( hostOutputSampleSize < 0 )
+ {
+ result = hostOutputSampleSize;
+ goto error;
+ }
+
+ if( outputStreamInfo
+ && ( outputStreamInfo->flags & paWinMmeUseMultipleDevices ) )
+ {
+ /* set effectiveOutputChannelCount to the largest number of
+ channels on any one device.
+ */
+ effectiveOutputChannelCount = 0;
+ for( i=0; i< outputStreamInfo->deviceCount; ++i )
+ {
+ if( outputStreamInfo->devices[i].channelCount > effectiveOutputChannelCount )
+ effectiveOutputChannelCount = outputStreamInfo->devices[i].channelCount;
+ }
+ }
+ else
+ {
+ effectiveOutputChannelCount = outputChannelCount;
+ }
+
+ hostOutputFrameSize = hostOutputSampleSize * effectiveOutputChannelCount;
+
+ maximumBufferSize = (long) ((PA_MME_MAX_HOST_BUFFER_SECS_ * sampleRate) * hostOutputFrameSize);
+ if( maximumBufferSize > PA_MME_MAX_HOST_BUFFER_BYTES_ )
+ maximumBufferSize = PA_MME_MAX_HOST_BUFFER_BYTES_;
+
+
+ /* compute the following in bytes, then convert back to frames */
+
+ SelectBufferSizeAndCount(
+ ((framesPerBuffer == paFramesPerBufferUnspecified)
+ ? PA_MME_MIN_HOST_BUFFER_FRAMES_WHEN_UNSPECIFIED_
+ : framesPerBuffer ) * hostOutputFrameSize, /* baseBufferSize */
+ ((unsigned long)(suggestedOutputLatency * sampleRate)) * hostOutputFrameSize, /* suggestedLatency */
+ 4, /* baseBufferCount */
+ minimumBufferCount,
+ maximumBufferSize,
+ &hostBufferSizeBytes, &hostBufferCount );
+
+ *framesPerHostOutputBuffer = hostBufferSizeBytes / hostOutputFrameSize;
+ *hostOutputBufferCount = hostBufferCount;
+
+
+ if( inputChannelCount > 0 )
+ {
+ /* ensure that both input and output buffer sizes are the same.
+ if they don't match at this stage, choose the smallest one
+ and use that for input and output
+ */
+
+ if( *framesPerHostOutputBuffer != *framesPerHostInputBuffer )
+ {
+ if( framesPerHostInputBuffer < framesPerHostOutputBuffer )
+ {
+ unsigned long framesPerHostBuffer = *framesPerHostInputBuffer;
+
+ minimumBufferCount = PA_MME_MIN_HOST_OUTPUT_BUFFER_COUNT_;
+ ReselectBufferCount(
+ framesPerHostBuffer * hostOutputFrameSize, /* bufferSize */
+ ((unsigned long)(suggestedOutputLatency * sampleRate)) * hostOutputFrameSize, /* suggestedLatency */
+ 4, /* baseBufferCount */
+ minimumBufferCount,
+ &hostBufferCount );
+
+ *framesPerHostOutputBuffer = framesPerHostBuffer;
+ *hostOutputBufferCount = hostBufferCount;
+ }
+ else
+ {
+ unsigned long framesPerHostBuffer = *framesPerHostOutputBuffer;
+
+ minimumBufferCount = PA_MME_MIN_HOST_INPUT_BUFFER_COUNT_FULL_DUPLEX_;
+ ReselectBufferCount(
+ framesPerHostBuffer * hostInputFrameSize, /* bufferSize */
+ ((unsigned long)(suggestedInputLatency * sampleRate)) * hostInputFrameSize, /* suggestedLatency */
+ 4, /* baseBufferCount */
+ minimumBufferCount,
+ &hostBufferCount );
+
+ *framesPerHostInputBuffer = framesPerHostBuffer;
+ *hostInputBufferCount = hostBufferCount;
+ }
+ }
+ }
+ }
+ }
+ else
+ {
+ *framesPerHostOutputBuffer = 0;
+ *hostOutputBufferCount = 0;
+ }
+
+error:
+ return result;
+}
+
+
+typedef struct
+{
+ HANDLE bufferEvent;
+ void *waveHandles;
+ unsigned int deviceCount;
+ /* unsigned int channelCount; */
+ WAVEHDR **waveHeaders; /* waveHeaders[device][buffer] */
+ unsigned int bufferCount;
+ unsigned int currentBufferIndex;
+ unsigned int framesPerBuffer;
+ unsigned int framesUsedInCurrentBuffer;
+}PaWinMmeSingleDirectionHandlesAndBuffers;
+
+/* prototypes for functions operating on PaWinMmeSingleDirectionHandlesAndBuffers */
+
+static void InitializeSingleDirectionHandlesAndBuffers( PaWinMmeSingleDirectionHandlesAndBuffers *handlesAndBuffers );
+static PaError InitializeWaveHandles( PaWinMmeHostApiRepresentation *winMmeHostApi,
+ PaWinMmeSingleDirectionHandlesAndBuffers *handlesAndBuffers,
+ unsigned long bytesPerHostSample,
+ double sampleRate, PaWinMmeDeviceAndChannelCount *devices,
+ unsigned int deviceCount, int isInput );
+static PaError TerminateWaveHandles( PaWinMmeSingleDirectionHandlesAndBuffers *handlesAndBuffers, int isInput, int currentlyProcessingAnError );
+static PaError InitializeWaveHeaders( PaWinMmeSingleDirectionHandlesAndBuffers *handlesAndBuffers,
+ unsigned long hostBufferCount,
+ PaSampleFormat hostSampleFormat,
+ unsigned long framesPerHostBuffer,
+ PaWinMmeDeviceAndChannelCount *devices,
+ int isInput );
+static void TerminateWaveHeaders( PaWinMmeSingleDirectionHandlesAndBuffers *handlesAndBuffers, int isInput );
+
+
+static void InitializeSingleDirectionHandlesAndBuffers( PaWinMmeSingleDirectionHandlesAndBuffers *handlesAndBuffers )
+{
+ handlesAndBuffers->bufferEvent = 0;
+ handlesAndBuffers->waveHandles = 0;
+ handlesAndBuffers->deviceCount = 0;
+ handlesAndBuffers->waveHeaders = 0;
+ handlesAndBuffers->bufferCount = 0;
+}
+
+static PaError InitializeWaveHandles( PaWinMmeHostApiRepresentation *winMmeHostApi,
+ PaWinMmeSingleDirectionHandlesAndBuffers *handlesAndBuffers,
+ unsigned long bytesPerHostSample,
+ double sampleRate, PaWinMmeDeviceAndChannelCount *devices,
+ unsigned int deviceCount, int isInput )
+{
+ PaError result;
+ MMRESULT mmresult;
+ unsigned long bytesPerFrame;
+ WAVEFORMATEX wfx;
+ signed int i;
+
+ /* for error cleanup we expect that InitializeSingleDirectionHandlesAndBuffers()
+ has already been called to zero some fields */
+
+ result = CreateEventWithPaError( &handlesAndBuffers->bufferEvent, NULL, FALSE, FALSE, NULL );
+ if( result != paNoError ) goto error;
+
+ if( isInput )
+ handlesAndBuffers->waveHandles = (void*)PaUtil_AllocateMemory( sizeof(HWAVEIN) * deviceCount );
+ else
+ handlesAndBuffers->waveHandles = (void*)PaUtil_AllocateMemory( sizeof(HWAVEOUT) * deviceCount );
+ if( !handlesAndBuffers->waveHandles )
+ {
+ result = paInsufficientMemory;
+ goto error;
+ }
+
+ handlesAndBuffers->deviceCount = deviceCount;
+
+ for( i = 0; i < (signed int)deviceCount; ++i )
+ {
+ if( isInput )
+ ((HWAVEIN*)handlesAndBuffers->waveHandles)[i] = 0;
+ else
+ ((HWAVEOUT*)handlesAndBuffers->waveHandles)[i] = 0;
+ }
+
+ wfx.wFormatTag = WAVE_FORMAT_PCM;
+ wfx.nSamplesPerSec = (DWORD) sampleRate;
+ wfx.cbSize = 0;
+
+ for( i = 0; i < (signed int)deviceCount; ++i )
+ {
+ UINT winMmeDeviceId;
+
+ winMmeDeviceId = LocalDeviceIndexToWinMmeDeviceId( winMmeHostApi, devices[i].device );
+ wfx.nChannels = (WORD)devices[i].channelCount;
+
+ bytesPerFrame = wfx.nChannels * bytesPerHostSample;
+
+ wfx.nAvgBytesPerSec = (DWORD)(bytesPerFrame * sampleRate);
+ wfx.nBlockAlign = (WORD)bytesPerFrame;
+ wfx.wBitsPerSample = (WORD)((bytesPerFrame/wfx.nChannels) * 8);
+
+ /* REVIEW: consider not firing an event for input when a full duplex
+ stream is being used. this would probably depend on the
+ neverDropInput flag. */
+
+ if( isInput )
+ mmresult = waveInOpen( &((HWAVEIN*)handlesAndBuffers->waveHandles)[i], winMmeDeviceId, &wfx,
+ (DWORD)handlesAndBuffers->bufferEvent, (DWORD)0, CALLBACK_EVENT );
+ else
+ mmresult = waveOutOpen( &((HWAVEOUT*)handlesAndBuffers->waveHandles)[i], winMmeDeviceId, &wfx,
+ (DWORD)handlesAndBuffers->bufferEvent, (DWORD)0, CALLBACK_EVENT );
+
+ if( mmresult != MMSYSERR_NOERROR )
+ {
+ switch( mmresult )
+ {
+ case MMSYSERR_ALLOCATED: /* Specified resource is already allocated. */
+ result = paDeviceUnavailable;
+ break;
+ case MMSYSERR_NODRIVER: /* No device driver is present. */
+ result = paDeviceUnavailable;
+ break;
+ case MMSYSERR_NOMEM: /* Unable to allocate or lock memory. */
+ result = paInsufficientMemory;
+ break;
+
+ case MMSYSERR_BADDEVICEID: /* Specified device identifier is out of range. */
+ /* falls through */
+ case WAVERR_BADFORMAT: /* Attempted to open with an unsupported waveform-audio format. */
+ /* falls through */
+ default:
+ result = paUnanticipatedHostError;
+ if( isInput )
+ {
+ PA_MME_SET_LAST_WAVEIN_ERROR( mmresult );
+ }
+ else
+ {
+ PA_MME_SET_LAST_WAVEOUT_ERROR( mmresult );
+ }
+ }
+ goto error;
+ }
+ }
+
+ return result;
+
+error:
+ TerminateWaveHandles( handlesAndBuffers, isInput, 1 /* currentlyProcessingAnError */ );
+
+ return result;
+}
+
+
+static PaError TerminateWaveHandles( PaWinMmeSingleDirectionHandlesAndBuffers *handlesAndBuffers, int isInput, int currentlyProcessingAnError )
+{
+ PaError result = paNoError;
+ MMRESULT mmresult;
+ signed int i;
+
+ if( handlesAndBuffers->waveHandles )
+ {
+ for( i = handlesAndBuffers->deviceCount-1; i >= 0; --i )
+ {
+ if( isInput )
+ {
+ if( ((HWAVEIN*)handlesAndBuffers->waveHandles)[i] )
+ mmresult = waveInClose( ((HWAVEIN*)handlesAndBuffers->waveHandles)[i] );
+ }
+ else
+ {
+ if( ((HWAVEOUT*)handlesAndBuffers->waveHandles)[i] )
+ mmresult = waveOutClose( ((HWAVEOUT*)handlesAndBuffers->waveHandles)[i] );
+ }
+
+ if( mmresult != MMSYSERR_NOERROR &&
+ !currentlyProcessingAnError ) /* don't update the error state if we're already processing an error */
+ {
+ result = paUnanticipatedHostError;
+ if( isInput )
+ {
+ PA_MME_SET_LAST_WAVEIN_ERROR( mmresult );
+ }
+ else
+ {
+ PA_MME_SET_LAST_WAVEOUT_ERROR( mmresult );
+ }
+ /* note that we don't break here, we try to continue closing devices */
+ }
+ }
+
+ PaUtil_FreeMemory( handlesAndBuffers->waveHandles );
+ handlesAndBuffers->waveHandles = 0;
+ }
+
+ if( handlesAndBuffers->bufferEvent )
+ {
+ result = CloseHandleWithPaError( handlesAndBuffers->bufferEvent );
+ handlesAndBuffers->bufferEvent = 0;
+ }
+
+ return result;
+}
+
+
+static PaError InitializeWaveHeaders( PaWinMmeSingleDirectionHandlesAndBuffers *handlesAndBuffers,
+ unsigned long hostBufferCount,
+ PaSampleFormat hostSampleFormat,
+ unsigned long framesPerHostBuffer,
+ PaWinMmeDeviceAndChannelCount *devices,
+ int isInput )
+{
+ PaError result = paNoError;
+ MMRESULT mmresult;
+ WAVEHDR *deviceWaveHeaders;
+ signed int i, j;
+
+ /* for error cleanup we expect that InitializeSingleDirectionHandlesAndBuffers()
+ has already been called to zero some fields */
+
+
+ /* allocate an array of pointers to arrays of wave headers, one array of
+ wave headers per device */
+ handlesAndBuffers->waveHeaders = (WAVEHDR**)PaUtil_AllocateMemory( sizeof(WAVEHDR*) * handlesAndBuffers->deviceCount );
+ if( !handlesAndBuffers->waveHeaders )
+ {
+ result = paInsufficientMemory;
+ goto error;
+ }
+
+ for( i = 0; i < (signed int)handlesAndBuffers->deviceCount; ++i )
+ handlesAndBuffers->waveHeaders[i] = 0;
+
+ handlesAndBuffers->bufferCount = hostBufferCount;
+
+ for( i = 0; i < (signed int)handlesAndBuffers->deviceCount; ++i )
+ {
+ int bufferBytes = Pa_GetSampleSize( hostSampleFormat ) *
+ framesPerHostBuffer * devices[i].channelCount;
+ if( bufferBytes < 0 )
+ {
+ result = paInternalError;
+ goto error;
+ }
+
+ /* Allocate an array of wave headers for device i */
+ deviceWaveHeaders = (WAVEHDR *) PaUtil_AllocateMemory( sizeof(WAVEHDR)*hostBufferCount );
+ if( !deviceWaveHeaders )
+ {
+ result = paInsufficientMemory;
+ goto error;
+ }
+
+ for( j=0; j < (signed int)hostBufferCount; ++j )
+ deviceWaveHeaders[j].lpData = 0;
+
+ handlesAndBuffers->waveHeaders[i] = deviceWaveHeaders;
+
+ /* Allocate a buffer for each wave header */
+ for( j=0; j < (signed int)hostBufferCount; ++j )
+ {
+ deviceWaveHeaders[j].lpData = (char *)PaUtil_AllocateMemory( bufferBytes );
+ if( !deviceWaveHeaders[j].lpData )
+ {
+ result = paInsufficientMemory;
+ goto error;
+ }
+ deviceWaveHeaders[j].dwBufferLength = bufferBytes;
+ deviceWaveHeaders[j].dwUser = 0xFFFFFFFF; /* indicates that *PrepareHeader() has not yet been called, for error clean up code */
+
+ if( isInput )
+ {
+ mmresult = waveInPrepareHeader( ((HWAVEIN*)handlesAndBuffers->waveHandles)[i], &deviceWaveHeaders[j], sizeof(WAVEHDR) );
+ if( mmresult != MMSYSERR_NOERROR )
+ {
+ result = paUnanticipatedHostError;
+ PA_MME_SET_LAST_WAVEIN_ERROR( mmresult );
+ goto error;
+ }
+ }
+ else /* output */
+ {
+ mmresult = waveOutPrepareHeader( ((HWAVEOUT*)handlesAndBuffers->waveHandles)[i], &deviceWaveHeaders[j], sizeof(WAVEHDR) );
+ if( mmresult != MMSYSERR_NOERROR )
+ {
+ result = paUnanticipatedHostError;
+ PA_MME_SET_LAST_WAVEIN_ERROR( mmresult );
+ goto error;
+ }
+ }
+ deviceWaveHeaders[j].dwUser = devices[i].channelCount;
+ }
+ }
+
+ return result;
+
+error:
+ TerminateWaveHeaders( handlesAndBuffers, isInput );
+
+ return result;
+}
+
+
+static void TerminateWaveHeaders( PaWinMmeSingleDirectionHandlesAndBuffers *handlesAndBuffers, int isInput )
+{
+ signed int i, j;
+ WAVEHDR *deviceWaveHeaders;
+
+ if( handlesAndBuffers->waveHeaders )
+ {
+ for( i = handlesAndBuffers->deviceCount-1; i >= 0 ; --i )
+ {
+ deviceWaveHeaders = handlesAndBuffers->waveHeaders[i]; /* wave headers for device i */
+ if( deviceWaveHeaders )
+ {
+ for( j = handlesAndBuffers->bufferCount-1; j >= 0; --j )
+ {
+ if( deviceWaveHeaders[j].lpData )
+ {
+ if( deviceWaveHeaders[j].dwUser != 0xFFFFFFFF )
+ {
+ if( isInput )
+ waveInUnprepareHeader( ((HWAVEIN*)handlesAndBuffers->waveHandles)[i], &deviceWaveHeaders[j], sizeof(WAVEHDR) );
+ else
+ waveOutUnprepareHeader( ((HWAVEOUT*)handlesAndBuffers->waveHandles)[i], &deviceWaveHeaders[j], sizeof(WAVEHDR) );
+ }
+
+ PaUtil_FreeMemory( deviceWaveHeaders[j].lpData );
+ }
+ }
+
+ PaUtil_FreeMemory( deviceWaveHeaders );
+ }
+ }
+
+ PaUtil_FreeMemory( handlesAndBuffers->waveHeaders );
+ handlesAndBuffers->waveHeaders = 0;
+ }
+}
+
+
+
+/* PaWinMmeStream - a stream data structure specifically for this implementation */
+/* note that struct PaWinMmeStream is typedeffed to PaWinMmeStream above. */
+struct PaWinMmeStream
+{
+ PaUtilStreamRepresentation streamRepresentation;
+ PaUtilCpuLoadMeasurer cpuLoadMeasurer;
+ PaUtilBufferProcessor bufferProcessor;
+
+ int primeStreamUsingCallback;
+
+ PaWinMmeSingleDirectionHandlesAndBuffers input;
+ PaWinMmeSingleDirectionHandlesAndBuffers output;
+
+ /* Processing thread management -------------- */
+ HANDLE abortEvent;
+ HANDLE processingThread;
+ DWORD processingThreadId;
+
+ char throttleProcessingThreadOnOverload; /* 0 -> don't throtte, non-0 -> throttle */
+ int processingThreadPriority;
+ int highThreadPriority;
+ int throttledThreadPriority;
+ unsigned long throttledSleepMsecs;
+
+ int isStopped;
+ volatile int isActive;
+ volatile int stopProcessing; /* stop thread once existing buffers have been returned */
+ volatile int abortProcessing; /* stop thread immediately */
+
+ DWORD allBuffersDurationMs; /* used to calculate timeouts */
+};
+
+/* updates deviceCount if PaWinMmeUseMultipleDevices is used */
+
+static PaError ValidateWinMmeSpecificStreamInfo(
+ const PaStreamParameters *streamParameters,
+ const PaWinMmeStreamInfo *streamInfo,
+ char *throttleProcessingThreadOnOverload,
+ unsigned long *deviceCount )
+{
+ if( streamInfo )
+ {
+ if( streamInfo->size != sizeof( PaWinMmeStreamInfo )
+ || streamInfo->version != 1 )
+ {
+ return paIncompatibleHostApiSpecificStreamInfo;
+ }
+
+ if( streamInfo->flags & paWinMmeDontThrottleOverloadedProcessingThread )
+ *throttleProcessingThreadOnOverload = 0;
+
+ if( streamInfo->flags & paWinMmeUseMultipleDevices )
+ {
+ if( streamParameters->device != paUseHostApiSpecificDeviceSpecification )
+ return paInvalidDevice;
+
+ *deviceCount = streamInfo->deviceCount;
+ }
+ }
+
+ return paNoError;
+}
+
+static PaError RetrieveDevicesFromStreamParameters(
+ struct PaUtilHostApiRepresentation *hostApi,
+ const PaStreamParameters *streamParameters,
+ const PaWinMmeStreamInfo *streamInfo,
+ PaWinMmeDeviceAndChannelCount *devices,
+ unsigned long deviceCount )
+{
+ PaError result = paNoError;
+ unsigned int i;
+ int totalChannelCount;
+ PaDeviceIndex hostApiDevice;
+
+ if( streamInfo && streamInfo->flags & paWinMmeUseMultipleDevices )
+ {
+ totalChannelCount = 0;
+ for( i=0; i < deviceCount; ++i )
+ {
+ /* validate that the device number is within range */
+ result = PaUtil_DeviceIndexToHostApiDeviceIndex( &hostApiDevice,
+ streamInfo->devices[i].device, hostApi );
+ if( result != paNoError )
+ return result;
+
+ devices[i].device = hostApiDevice;
+ devices[i].channelCount = streamInfo->devices[i].channelCount;
+
+ totalChannelCount += devices[i].channelCount;
+ }
+
+ if( totalChannelCount != streamParameters->channelCount )
+ {
+ /* channelCount must match total channels specified by multiple devices */
+ return paInvalidChannelCount; /* REVIEW use of this error code */
+ }
+ }
+ else
+ {
+ devices[0].device = streamParameters->device;
+ devices[0].channelCount = streamParameters->channelCount;
+ }
+
+ return result;
+}
+
+static PaError ValidateInputChannelCounts(
+ struct PaUtilHostApiRepresentation *hostApi,
+ PaWinMmeDeviceAndChannelCount *devices,
+ unsigned long deviceCount )
+{
+ unsigned int i;
+
+ for( i=0; i < deviceCount; ++i )
+ {
+ if( devices[i].channelCount < 1 || devices[i].channelCount
+ > hostApi->deviceInfos[ devices[i].device ]->maxInputChannels )
+ return paInvalidChannelCount;
+ }
+
+ return paNoError;
+}
+
+static PaError ValidateOutputChannelCounts(
+ struct PaUtilHostApiRepresentation *hostApi,
+ PaWinMmeDeviceAndChannelCount *devices,
+ unsigned long deviceCount )
+{
+ unsigned int i;
+
+ for( i=0; i < deviceCount; ++i )
+ {
+ if( devices[i].channelCount < 1 || devices[i].channelCount
+ > hostApi->deviceInfos[ devices[i].device ]->maxOutputChannels )
+ return paInvalidChannelCount;
+ }
+
+ return paNoError;
+}
+
+
+/* the following macros are intended to improve the readability of the following code */
+#define PA_IS_INPUT_STREAM_( stream ) ( stream ->input.waveHandles )
+#define PA_IS_OUTPUT_STREAM_( stream ) ( stream ->output.waveHandles )
+#define PA_IS_FULL_DUPLEX_STREAM_( stream ) ( stream ->input.waveHandles && stream ->output.waveHandles )
+#define PA_IS_HALF_DUPLEX_STREAM_( stream ) ( !(stream ->input.waveHandles && stream ->output.waveHandles) )
+
+static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi,
+ PaStream** s,
+ const PaStreamParameters *inputParameters,
+ const PaStreamParameters *outputParameters,
+ double sampleRate,
+ unsigned long framesPerBuffer,
+ PaStreamFlags streamFlags,
+ PaStreamCallback *streamCallback,
+ void *userData )
+{
+ PaError result;
+ PaWinMmeHostApiRepresentation *winMmeHostApi = (PaWinMmeHostApiRepresentation*)hostApi;
+ PaWinMmeStream *stream = 0;
+ int bufferProcessorIsInitialized = 0;
+ int streamRepresentationIsInitialized = 0;
+ PaSampleFormat hostInputSampleFormat, hostOutputSampleFormat;
+ int inputChannelCount, outputChannelCount;
+ PaSampleFormat inputSampleFormat, outputSampleFormat;
+ double suggestedInputLatency, suggestedOutputLatency;
+ PaWinMmeStreamInfo *inputStreamInfo, *outputStreamInfo;
+ unsigned long framesPerHostInputBuffer;
+ unsigned long hostInputBufferCount;
+ unsigned long framesPerHostOutputBuffer;
+ unsigned long hostOutputBufferCount;
+ unsigned long framesPerBufferProcessorCall;
+ PaWinMmeDeviceAndChannelCount *inputDevices = 0; /* contains all devices and channel counts as local host api ids, even when PaWinMmeUseMultipleDevices is not used */
+ unsigned long inputDeviceCount = 0;
+ PaWinMmeDeviceAndChannelCount *outputDevices = 0;
+ unsigned long outputDeviceCount = 0; /* contains all devices and channel counts as local host api ids, even when PaWinMmeUseMultipleDevices is not used */
+ char throttleProcessingThreadOnOverload = 1;
+
+
+ if( inputParameters )
+ {
+ inputChannelCount = inputParameters->channelCount;
+ inputSampleFormat = inputParameters->sampleFormat;
+ suggestedInputLatency = inputParameters->suggestedLatency;
+
+ inputDeviceCount = 1;
+
+ /* validate input hostApiSpecificStreamInfo */
+ inputStreamInfo = (PaWinMmeStreamInfo*)inputParameters->hostApiSpecificStreamInfo;
+ result = ValidateWinMmeSpecificStreamInfo( inputParameters, inputStreamInfo,
+ &throttleProcessingThreadOnOverload,
+ &inputDeviceCount );
+ if( result != paNoError ) return result;
+
+ inputDevices = (PaWinMmeDeviceAndChannelCount*)alloca( sizeof(PaWinMmeDeviceAndChannelCount) * inputDeviceCount );
+ if( !inputDevices ) return paInsufficientMemory;
+
+ result = RetrieveDevicesFromStreamParameters( hostApi, inputParameters, inputStreamInfo, inputDevices, inputDeviceCount );
+ if( result != paNoError ) return result;
+
+ result = ValidateInputChannelCounts( hostApi, inputDevices, inputDeviceCount );
+ if( result != paNoError ) return result;
+
+ hostInputSampleFormat =
+ PaUtil_SelectClosestAvailableFormat( paInt16 /* native formats */, inputSampleFormat );
+ }
+ else
+ {
+ inputChannelCount = 0;
+ inputSampleFormat = 0;
+ suggestedInputLatency = 0.;
+ inputStreamInfo = 0;
+ hostInputSampleFormat = 0;
+ }
+
+
+ if( outputParameters )
+ {
+ outputChannelCount = outputParameters->channelCount;
+ outputSampleFormat = outputParameters->sampleFormat;
+ suggestedOutputLatency = outputParameters->suggestedLatency;
+
+ outputDeviceCount = 1;
+
+ /* validate output hostApiSpecificStreamInfo */
+ outputStreamInfo = (PaWinMmeStreamInfo*)outputParameters->hostApiSpecificStreamInfo;
+ result = ValidateWinMmeSpecificStreamInfo( outputParameters, outputStreamInfo,
+ &throttleProcessingThreadOnOverload,
+ &outputDeviceCount );
+ if( result != paNoError ) return result;
+
+ outputDevices = (PaWinMmeDeviceAndChannelCount*)alloca( sizeof(PaWinMmeDeviceAndChannelCount) * outputDeviceCount );
+ if( !outputDevices ) return paInsufficientMemory;
+
+ result = RetrieveDevicesFromStreamParameters( hostApi, outputParameters, outputStreamInfo, outputDevices, outputDeviceCount );
+ if( result != paNoError ) return result;
+
+ result = ValidateOutputChannelCounts( hostApi, outputDevices, outputDeviceCount );
+ if( result != paNoError ) return result;
+
+ hostOutputSampleFormat =
+ PaUtil_SelectClosestAvailableFormat( paInt16 /* native formats */, outputSampleFormat );
+ }
+ else
+ {
+ outputChannelCount = 0;
+ outputSampleFormat = 0;
+ outputStreamInfo = 0;
+ hostOutputSampleFormat = 0;
+ suggestedOutputLatency = 0.;
+ }
+
+
+ /*
+ IMPLEMENT ME:
+ - alter sampleRate to a close allowable rate if possible / necessary
+ */
+
+
+ /* validate platform specific flags */
+ if( (streamFlags & paPlatformSpecificFlags) != 0 )
+ return paInvalidFlag; /* unexpected platform specific flag */
+
+
+ result = CalculateBufferSettings( &framesPerHostInputBuffer, &hostInputBufferCount,
+ &framesPerHostOutputBuffer, &hostOutputBufferCount,
+ inputChannelCount, hostInputSampleFormat, suggestedInputLatency, inputStreamInfo,
+ outputChannelCount, hostOutputSampleFormat, suggestedOutputLatency, outputStreamInfo,
+ sampleRate, framesPerBuffer );
+ if( result != paNoError ) goto error;
+
+
+ stream = (PaWinMmeStream*)PaUtil_AllocateMemory( sizeof(PaWinMmeStream) );
+ if( !stream )
+ {
+ result = paInsufficientMemory;
+ goto error;
+ }
+
+ InitializeSingleDirectionHandlesAndBuffers( &stream->input );
+ InitializeSingleDirectionHandlesAndBuffers( &stream->output );
+
+ stream->abortEvent = 0;
+ stream->processingThread = 0;
+
+ stream->throttleProcessingThreadOnOverload = throttleProcessingThreadOnOverload;
+
+ PaUtil_InitializeStreamRepresentation( &stream->streamRepresentation,
+ ( (streamCallback)
+ ? &winMmeHostApi->callbackStreamInterface
+ : &winMmeHostApi->blockingStreamInterface ),
+ streamCallback, userData );
+ streamRepresentationIsInitialized = 1;
+
+ PaUtil_InitializeCpuLoadMeasurer( &stream->cpuLoadMeasurer, sampleRate );
+
+
+ if( inputParameters && outputParameters ) /* full duplex */
+ {
+ if( framesPerHostInputBuffer < framesPerHostOutputBuffer )
+ {
+ assert( (framesPerHostOutputBuffer % framesPerHostInputBuffer) == 0 ); /* CalculateBufferSettings() should guarantee this condition */
+
+ framesPerBufferProcessorCall = framesPerHostInputBuffer;
+ }
+ else
+ {
+ assert( (framesPerHostInputBuffer % framesPerHostOutputBuffer) == 0 ); /* CalculateBufferSettings() should guarantee this condition */
+
+ framesPerBufferProcessorCall = framesPerHostOutputBuffer;
+ }
+ }
+ else if( inputParameters )
+ {
+ framesPerBufferProcessorCall = framesPerHostInputBuffer;
+ }
+ else if( outputParameters )
+ {
+ framesPerBufferProcessorCall = framesPerHostOutputBuffer;
+ }
+
+ stream->input.framesPerBuffer = framesPerHostInputBuffer;
+ stream->output.framesPerBuffer = framesPerHostOutputBuffer;
+
+ result = PaUtil_InitializeBufferProcessor( &stream->bufferProcessor,
+ inputChannelCount, inputSampleFormat, hostInputSampleFormat,
+ outputChannelCount, outputSampleFormat, hostOutputSampleFormat,
+ sampleRate, streamFlags, framesPerBuffer,
+ framesPerBufferProcessorCall, paUtilFixedHostBufferSize,
+ streamCallback, userData );
+ if( result != paNoError ) goto error;
+
+ bufferProcessorIsInitialized = 1;
+
+ stream->streamRepresentation.streamInfo.inputLatency =
+ (double)(PaUtil_GetBufferProcessorInputLatency(&stream->bufferProcessor)
+ +(framesPerHostInputBuffer * (hostInputBufferCount-1))) / sampleRate;
+ stream->streamRepresentation.streamInfo.outputLatency =
+ (double)(PaUtil_GetBufferProcessorOutputLatency(&stream->bufferProcessor)
+ +(framesPerHostOutputBuffer * (hostOutputBufferCount-1))) / sampleRate;
+ stream->streamRepresentation.streamInfo.sampleRate = sampleRate;
+
+ stream->primeStreamUsingCallback = ( (streamFlags&paPrimeOutputBuffersUsingStreamCallback) && streamCallback ) ? 1 : 0;
+
+ /* time to sleep when throttling due to >100% cpu usage.
+ -a quater of a buffer's duration */
+ stream->throttledSleepMsecs =
+ (unsigned long)(stream->bufferProcessor.framesPerHostBuffer *
+ stream->bufferProcessor.samplePeriod * .25 * 1000);
+
+ stream->isStopped = 1;
+ stream->isActive = 0;
+
+
+ /* for maximum compatibility with multi-device multichannel drivers,
+ we first open all devices, then we prepare all buffers, finally
+ we start all devices ( in StartStream() ). teardown in reverse order.
+ */
+
+ if( inputParameters )
+ {
+ result = InitializeWaveHandles( winMmeHostApi, &stream->input,
+ stream->bufferProcessor.bytesPerHostInputSample, sampleRate,
+ inputDevices, inputDeviceCount, 1 /* isInput */ );
+ if( result != paNoError ) goto error;
+ }
+
+ if( outputParameters )
+ {
+ result = InitializeWaveHandles( winMmeHostApi, &stream->output,
+ stream->bufferProcessor.bytesPerHostOutputSample, sampleRate,
+ outputDevices, outputDeviceCount, 0 /* isInput */ );
+ if( result != paNoError ) goto error;
+ }
+
+ if( inputParameters )
+ {
+ result = InitializeWaveHeaders( &stream->input, hostInputBufferCount,
+ hostInputSampleFormat, framesPerHostInputBuffer, inputDevices, 1 /* isInput */ );
+ if( result != paNoError ) goto error;
+ }
+
+ if( outputParameters )
+ {
+ result = InitializeWaveHeaders( &stream->output, hostOutputBufferCount,
+ hostOutputSampleFormat, framesPerHostOutputBuffer, outputDevices, 0 /* not isInput */ );
+ if( result != paNoError ) goto error;
+
+ stream->allBuffersDurationMs = (DWORD) (1000.0 * (framesPerHostOutputBuffer * stream->output.bufferCount) / sampleRate);
+ }
+ else
+ {
+ stream->allBuffersDurationMs = (DWORD) (1000.0 * (framesPerHostInputBuffer * stream->input.bufferCount) / sampleRate);
+ }
+
+
+ if( streamCallback )
+ {
+ /* abort event is only needed for callback streams */
+ result = CreateEventWithPaError( &stream->abortEvent, NULL, TRUE, FALSE, NULL );
+ if( result != paNoError ) goto error;
+ }
+
+ *s = (PaStream*)stream;
+
+ return result;
+
+error:
+
+ if( stream )
+ {
+ if( stream->abortEvent )
+ CloseHandle( stream->abortEvent );
+
+ TerminateWaveHeaders( &stream->output, 0 /* not isInput */ );
+ TerminateWaveHeaders( &stream->input, 1 /* isInput */ );
+
+ TerminateWaveHandles( &stream->output, 0 /* not isInput */, 1 /* currentlyProcessingAnError */ );
+ TerminateWaveHandles( &stream->input, 1 /* isInput */, 1 /* currentlyProcessingAnError */ );
+
+ if( bufferProcessorIsInitialized )
+ PaUtil_TerminateBufferProcessor( &stream->bufferProcessor );
+
+ if( streamRepresentationIsInitialized )
+ PaUtil_TerminateStreamRepresentation( &stream->streamRepresentation );
+
+ PaUtil_FreeMemory( stream );
+ }
+
+ return result;
+}
+
+
+/* return non-zero if all current buffers are done */
+static int BuffersAreDone( WAVEHDR **waveHeaders, unsigned int deviceCount, int bufferIndex )
+{
+ unsigned int i;
+
+ for( i=0; i < deviceCount; ++i )
+ {
+ if( !(waveHeaders[i][ bufferIndex ].dwFlags & WHDR_DONE) )
+ {
+ return 0;
+ }
+ }
+
+ return 1;
+}
+
+static int CurrentInputBuffersAreDone( PaWinMmeStream *stream )
+{
+ return BuffersAreDone( stream->input.waveHeaders, stream->input.deviceCount, stream->input.currentBufferIndex );
+}
+
+static int CurrentOutputBuffersAreDone( PaWinMmeStream *stream )
+{
+ return BuffersAreDone( stream->output.waveHeaders, stream->output.deviceCount, stream->output.currentBufferIndex );
+}
+
+
+/* return non-zero if any buffers are queued */
+static int NoBuffersAreQueued( PaWinMmeSingleDirectionHandlesAndBuffers *handlesAndBuffers )
+{
+ unsigned int i, j;
+
+ if( handlesAndBuffers->waveHandles )
+ {
+ for( i=0; i < handlesAndBuffers->bufferCount; ++i )
+ {
+ for( j=0; j < handlesAndBuffers->deviceCount; ++j )
+ {
+ if( !( handlesAndBuffers->waveHeaders[ j ][ i ].dwFlags & WHDR_DONE) )
+ {
+ return 0;
+ }
+ }
+ }
+ }
+
+ return 1;
+}
+
+
+#define PA_CIRCULAR_INCREMENT_( current, max )\
+ ( (((current) + 1) >= (max)) ? (0) : (current+1) )
+
+#define PA_CIRCULAR_DECREMENT_( current, max )\
+ ( ((current) == 0) ? ((max)-1) : (current-1) )
+
+
+static signed long GetAvailableFrames( PaWinMmeSingleDirectionHandlesAndBuffers *handlesAndBuffers )
+{
+ signed long result = 0;
+ unsigned int i;
+
+ if( BuffersAreDone( handlesAndBuffers->waveHeaders, handlesAndBuffers->deviceCount, handlesAndBuffers->currentBufferIndex ) )
+ {
+ /* we could calculate the following in O(1) if we kept track of the
+ last done buffer */
+ result = handlesAndBuffers->framesPerBuffer - handlesAndBuffers->framesUsedInCurrentBuffer;
+
+ i = PA_CIRCULAR_INCREMENT_( handlesAndBuffers->currentBufferIndex, handlesAndBuffers->bufferCount );
+ while( i != handlesAndBuffers->currentBufferIndex )
+ {
+ if( BuffersAreDone( handlesAndBuffers->waveHeaders, handlesAndBuffers->deviceCount, i ) )
+ {
+ result += handlesAndBuffers->framesPerBuffer;
+ i = PA_CIRCULAR_INCREMENT_( i, handlesAndBuffers->bufferCount );
+ }
+ else
+ break;
+ }
+ }
+
+ return result;
+}
+
+
+static PaError AdvanceToNextInputBuffer( PaWinMmeStream *stream )
+{
+ PaError result = paNoError;
+ MMRESULT mmresult;
+ unsigned int i;
+
+ for( i=0; i < stream->input.deviceCount; ++i )
+ {
+ mmresult = waveInAddBuffer( ((HWAVEIN*)stream->input.waveHandles)[i],
+ &stream->input.waveHeaders[i][ stream->input.currentBufferIndex ],
+ sizeof(WAVEHDR) );
+ if( mmresult != MMSYSERR_NOERROR )
+ {
+ result = paUnanticipatedHostError;
+ PA_MME_SET_LAST_WAVEIN_ERROR( mmresult );
+ }
+ }
+
+ stream->input.currentBufferIndex =
+ PA_CIRCULAR_INCREMENT_( stream->input.currentBufferIndex, stream->input.bufferCount );
+
+ stream->input.framesUsedInCurrentBuffer = 0;
+
+ return result;
+}
+
+
+static PaError AdvanceToNextOutputBuffer( PaWinMmeStream *stream )
+{
+ PaError result = paNoError;
+ MMRESULT mmresult;
+ unsigned int i;
+
+ for( i=0; i < stream->output.deviceCount; ++i )
+ {
+ mmresult = waveOutWrite( ((HWAVEOUT*)stream->output.waveHandles)[i],
+ &stream->output.waveHeaders[i][ stream->output.currentBufferIndex ],
+ sizeof(WAVEHDR) );
+ if( mmresult != MMSYSERR_NOERROR )
+ {
+ result = paUnanticipatedHostError;
+ PA_MME_SET_LAST_WAVEOUT_ERROR( mmresult );
+ }
+ }
+
+ stream->output.currentBufferIndex =
+ PA_CIRCULAR_INCREMENT_( stream->output.currentBufferIndex, stream->output.bufferCount );
+
+ stream->output.framesUsedInCurrentBuffer = 0;
+
+ return result;
+}
+
+
+/* requeue all but the most recent input with the driver. Used for catching
+ up after a total input buffer underrun */
+static PaError CatchUpInputBuffers( PaWinMmeStream *stream )
+{
+ PaError result = paNoError;
+ unsigned int i;
+
+ for( i=0; i < stream->input.bufferCount - 1; ++i )
+ {
+ result = AdvanceToNextInputBuffer( stream );
+ if( result != paNoError )
+ break;
+ }
+
+ return result;
+}
+
+
+/* take the most recent output and duplicate it to all other output buffers
+ and requeue them. Used for catching up after a total output buffer underrun.
+*/
+static PaError CatchUpOutputBuffers( PaWinMmeStream *stream )
+{
+ PaError result = paNoError;
+ unsigned int i, j;
+ unsigned int previousBufferIndex =
+ PA_CIRCULAR_DECREMENT_( stream->output.currentBufferIndex, stream->output.bufferCount );
+
+ for( i=0; i < stream->output.bufferCount - 1; ++i )
+ {
+ for( j=0; j < stream->output.deviceCount; ++j )
+ {
+ if( stream->output.waveHeaders[j][ stream->output.currentBufferIndex ].lpData
+ != stream->output.waveHeaders[j][ previousBufferIndex ].lpData )
+ {
+ CopyMemory( stream->output.waveHeaders[j][ stream->output.currentBufferIndex ].lpData,
+ stream->output.waveHeaders[j][ previousBufferIndex ].lpData,
+ stream->output.waveHeaders[j][ stream->output.currentBufferIndex ].dwBufferLength );
+ }
+ }
+
+ result = AdvanceToNextOutputBuffer( stream );
+ if( result != paNoError )
+ break;
+ }
+
+ return result;
+}
+
+
+static DWORD WINAPI ProcessingThreadProc( void *pArg )
+{
+ PaWinMmeStream *stream = (PaWinMmeStream *)pArg;
+ HANDLE events[3];
+ int eventCount = 0;
+ DWORD result = paNoError;
+ DWORD waitResult;
+ DWORD timeout = (unsigned long)(stream->allBuffersDurationMs * 0.5);
+ int hostBuffersAvailable;
+ signed int hostInputBufferIndex, hostOutputBufferIndex;
+ PaStreamCallbackFlags statusFlags;
+ int callbackResult;
+ int done = 0;
+ unsigned int channel, i;
+ unsigned long framesProcessed;
+
+ /* prepare event array for call to WaitForMultipleObjects() */
+ if( stream->input.bufferEvent )
+ events[eventCount++] = stream->input.bufferEvent;
+ if( stream->output.bufferEvent )
+ events[eventCount++] = stream->output.bufferEvent;
+ events[eventCount++] = stream->abortEvent;
+
+ statusFlags = 0; /** @todo support paInputUnderflow, paOutputOverflow and paNeverDropInput */
+
+ /* loop until something causes us to stop */
+ do{
+ /* wait for MME to signal that a buffer is available, or for
+ the PA abort event to be signaled.
+
+ When this indicates that one or more buffers are available
+ NoBuffersAreQueued() and Current*BuffersAreDone are used below to
+ poll for additional done buffers. NoBuffersAreQueued() will fail
+ to identify an underrun/overflow if the driver doesn't mark all done
+ buffers prior to signalling the event. Some drivers do this
+ (eg RME Digi96, and others don't eg VIA PC 97 input). This isn't a
+ huge problem, it just means that we won't always be able to detect
+ underflow/overflow.
+ */
+ waitResult = WaitForMultipleObjects( eventCount, events, FALSE /* wait all = FALSE */, timeout );
+ if( waitResult == WAIT_FAILED )
+ {
+ result = paUnanticipatedHostError;
+ /** @todo FIXME/REVIEW: can't return host error info from an asyncronous thread */
+ done = 1;
+ }
+ else if( waitResult == WAIT_TIMEOUT )
+ {
+ /* if a timeout is encountered, continue */
+ }
+
+ if( stream->abortProcessing )
+ {
+ /* Pa_AbortStream() has been called, stop processing immediately */
+ done = 1;
+ }
+ else if( stream->stopProcessing )
+ {
+ /* Pa_StopStream() has been called or the user callback returned
+ non-zero, processing will continue until all output buffers
+ are marked as done. The stream will stop immediately if it
+ is input-only.
+ */
+
+ if( PA_IS_OUTPUT_STREAM_(stream) )
+ {
+ if( NoBuffersAreQueued( &stream->output ) )
+ done = 1; /* Will cause thread to return. */
+ }
+ else
+ {
+ /* input only stream */
+ done = 1; /* Will cause thread to return. */
+ }
+ }
+ else
+ {
+ hostBuffersAvailable = 1;
+
+ /* process all available host buffers */
+ do
+ {
+ hostInputBufferIndex = -1;
+ hostOutputBufferIndex = -1;
+
+ if( PA_IS_INPUT_STREAM_(stream) )
+ {
+ if( CurrentInputBuffersAreDone( stream ) )
+ {
+ if( NoBuffersAreQueued( &stream->input ) )
+ {
+ /** @todo
+ if all of the other buffers are also ready then
+ we discard all but the most recent. This is an
+ input buffer overflow. FIXME: these buffers should
+ be passed to the callback in a paNeverDropInput
+ stream.
+
+ note that it is also possible for an input overflow
+ to happen while the callback is processing a buffer.
+ that is handled further down.
+ */
+ result = CatchUpInputBuffers( stream );
+ if( result != paNoError )
+ done = 1;
+
+ statusFlags |= paInputOverflow;
+ }
+
+ hostInputBufferIndex = stream->input.currentBufferIndex;
+ }
+ }
+
+ if( PA_IS_OUTPUT_STREAM_(stream) )
+ {
+ if( CurrentOutputBuffersAreDone( stream ) )
+ {
+ /* ok, we have an output buffer */
+
+ if( NoBuffersAreQueued( &stream->output ) )
+ {
+ /*
+ if all of the other buffers are also ready, catch up by copying
+ the most recently generated buffer into all but one of the output
+ buffers.
+
+ note that this catch up code only handles the case where all
+ buffers have been played out due to this thread not having
+ woken up at all. a more common case occurs when this thread
+ is woken up, processes one buffer, but takes too long, and as
+ a result all the other buffers have become un-queued. that
+ case is handled further down.
+ */
+
+ result = CatchUpOutputBuffers( stream );
+ if( result != paNoError )
+ done = 1;
+
+ statusFlags |= paOutputUnderflow;
+ }
+
+ hostOutputBufferIndex = stream->output.currentBufferIndex;
+ }
+ }
+
+
+ if( (PA_IS_FULL_DUPLEX_STREAM_(stream) && hostInputBufferIndex != -1 && hostOutputBufferIndex != -1) ||
+ (PA_IS_HALF_DUPLEX_STREAM_(stream) && ( hostInputBufferIndex != -1 || hostOutputBufferIndex != -1 ) ) )
+ {
+ PaStreamCallbackTimeInfo timeInfo = {0,0,0}; /** @todo implement inputBufferAdcTime */
+
+
+ if( PA_IS_OUTPUT_STREAM_(stream) )
+ {
+ /* set timeInfo.currentTime and calculate timeInfo.outputBufferDacTime
+ from the current wave out position */
+ MMTIME mmtime;
+ double timeBeforeGetPosition, timeAfterGetPosition;
+ double time;
+ long framesInBufferRing;
+ long writePosition;
+ long playbackPosition;
+ HWAVEOUT firstWaveOutDevice = ((HWAVEOUT*)stream->output.waveHandles)[0];
+
+ mmtime.wType = TIME_SAMPLES;
+ timeBeforeGetPosition = PaUtil_GetTime();
+ waveOutGetPosition( firstWaveOutDevice, &mmtime, sizeof(MMTIME) );
+ timeAfterGetPosition = PaUtil_GetTime();
+
+ timeInfo.currentTime = timeAfterGetPosition;
+
+ /* approximate time at which wave out position was measured
+ as half way between timeBeforeGetPosition and timeAfterGetPosition */
+ time = timeBeforeGetPosition + (timeAfterGetPosition - timeBeforeGetPosition) * .5;
+
+ framesInBufferRing = stream->output.bufferCount * stream->bufferProcessor.framesPerHostBuffer;
+ playbackPosition = mmtime.u.sample % framesInBufferRing;
+
+ writePosition = stream->output.currentBufferIndex * stream->bufferProcessor.framesPerHostBuffer
+ + stream->output.framesUsedInCurrentBuffer;
+
+ if( playbackPosition >= writePosition ){
+ timeInfo.outputBufferDacTime =
+ time + ((double)( writePosition + (framesInBufferRing - playbackPosition) ) * stream->bufferProcessor.samplePeriod );
+ }else{
+ timeInfo.outputBufferDacTime =
+ time + ((double)( writePosition - playbackPosition ) * stream->bufferProcessor.samplePeriod );
+ }
+ }
+
+
+ PaUtil_BeginCpuLoadMeasurement( &stream->cpuLoadMeasurer );
+
+ PaUtil_BeginBufferProcessing( &stream->bufferProcessor, &timeInfo, statusFlags );
+
+ /* reset status flags once they have been passed to the buffer processor */
+ statusFlags = 0;
+
+ if( PA_IS_INPUT_STREAM_(stream) )
+ {
+ PaUtil_SetInputFrameCount( &stream->bufferProcessor, 0 /* default to host buffer size */ );
+
+ channel = 0;
+ for( i=0; i<stream->input.deviceCount; ++i )
+ {
+ /* we have stored the number of channels in the buffer in dwUser */
+ int channelCount = stream->input.waveHeaders[i][ hostInputBufferIndex ].dwUser;
+
+ PaUtil_SetInterleavedInputChannels( &stream->bufferProcessor, channel,
+ stream->input.waveHeaders[i][ hostInputBufferIndex ].lpData +
+ stream->input.framesUsedInCurrentBuffer * channelCount *
+ stream->bufferProcessor.bytesPerHostInputSample,
+ channelCount );
+
+
+ channel += channelCount;
+ }
+ }
+
+ if( PA_IS_OUTPUT_STREAM_(stream) )
+ {
+ PaUtil_SetOutputFrameCount( &stream->bufferProcessor, 0 /* default to host buffer size */ );
+
+ channel = 0;
+ for( i=0; i<stream->output.deviceCount; ++i )
+ {
+ /* we have stored the number of channels in the buffer in dwUser */
+ int channelCount = stream->output.waveHeaders[i][ hostOutputBufferIndex ].dwUser;
+
+ PaUtil_SetInterleavedOutputChannels( &stream->bufferProcessor, channel,
+ stream->output.waveHeaders[i][ hostOutputBufferIndex ].lpData +
+ stream->output.framesUsedInCurrentBuffer * channelCount *
+ stream->bufferProcessor.bytesPerHostOutputSample,
+ channelCount );
+
+ channel += channelCount;
+ }
+ }
+
+ callbackResult = paContinue;
+ framesProcessed = PaUtil_EndBufferProcessing( &stream->bufferProcessor, &callbackResult );
+
+ stream->input.framesUsedInCurrentBuffer += framesProcessed;
+ stream->output.framesUsedInCurrentBuffer += framesProcessed;
+
+ PaUtil_EndCpuLoadMeasurement( &stream->cpuLoadMeasurer, framesProcessed );
+
+ if( callbackResult == paContinue )
+ {
+ /* nothing special to do */
+ }
+ else if( callbackResult == paAbort )
+ {
+ stream->abortProcessing = 1;
+ done = 1;
+ /** @todo FIXME: should probably reset the output device immediately once the callback returns paAbort */
+ result = paNoError;
+ }
+ else
+ {
+ /* User callback has asked us to stop with paComplete or other non-zero value */
+ stream->stopProcessing = 1; /* stop once currently queued audio has finished */
+ result = paNoError;
+ }
+
+
+ if( PA_IS_INPUT_STREAM_(stream)
+ && stream->stopProcessing == 0 && stream->abortProcessing == 0
+ && stream->input.framesUsedInCurrentBuffer == stream->input.framesPerBuffer )
+ {
+ if( NoBuffersAreQueued( &stream->input ) )
+ {
+ /** @todo need to handle PaNeverDropInput here where necessary */
+ result = CatchUpInputBuffers( stream );
+ if( result != paNoError )
+ done = 1;
+
+ statusFlags |= paInputOverflow;
+ }
+
+ result = AdvanceToNextInputBuffer( stream );
+ if( result != paNoError )
+ done = 1;
+ }
+
+
+ if( PA_IS_OUTPUT_STREAM_(stream) && !stream->abortProcessing )
+ {
+ if( stream->stopProcessing &&
+ stream->output.framesUsedInCurrentBuffer < stream->output.framesPerBuffer )
+ {
+ /* zero remaining samples in output output buffer and flush */
+
+ stream->output.framesUsedInCurrentBuffer += PaUtil_ZeroOutput( &stream->bufferProcessor,
+ stream->output.framesPerBuffer - stream->output.framesUsedInCurrentBuffer );
+
+ /* we send the entire buffer to the output devices, but we could
+ just send a partial buffer, rather than zeroing the unused
+ samples.
+ */
+ }
+
+ if( stream->output.framesUsedInCurrentBuffer == stream->output.framesPerBuffer )
+ {
+ /* check for underflow before enquing the just-generated buffer,
+ but recover from underflow after enquing it. This ensures
+ that the most recent audio segment is repeated */
+ int outputUnderflow = NoBuffersAreQueued( &stream->output );
+
+ result = AdvanceToNextOutputBuffer( stream );
+ if( result != paNoError )
+ done = 1;
+
+ if( outputUnderflow && !done && !stream->stopProcessing )
+ {
+ /* Recover from underflow in the case where the
+ underflow occured while processing the buffer
+ we just finished */
+
+ result = CatchUpOutputBuffers( stream );
+ if( result != paNoError )
+ done = 1;
+
+ statusFlags |= paOutputUnderflow;
+ }
+ }
+ }
+
+ if( stream->throttleProcessingThreadOnOverload != 0 )
+ {
+ if( stream->stopProcessing || stream->abortProcessing )
+ {
+ if( stream->processingThreadPriority != stream->highThreadPriority )
+ {
+ SetThreadPriority( stream->processingThread, stream->highThreadPriority );
+ stream->processingThreadPriority = stream->highThreadPriority;
+ }
+ }
+ else if( PaUtil_GetCpuLoad( &stream->cpuLoadMeasurer ) > 1. )
+ {
+ if( stream->processingThreadPriority != stream->throttledThreadPriority )
+ {
+ SetThreadPriority( stream->processingThread, stream->throttledThreadPriority );
+ stream->processingThreadPriority = stream->throttledThreadPriority;
+ }
+
+ /* sleep to give other processes a go */
+ Sleep( stream->throttledSleepMsecs );
+ }
+ else
+ {
+ if( stream->processingThreadPriority != stream->highThreadPriority )
+ {
+ SetThreadPriority( stream->processingThread, stream->highThreadPriority );
+ stream->processingThreadPriority = stream->highThreadPriority;
+ }
+ }
+ }
+ }
+ else
+ {
+ hostBuffersAvailable = 0;
+ }
+ }
+ while( hostBuffersAvailable &&
+ stream->stopProcessing == 0 &&
+ stream->abortProcessing == 0 &&
+ !done );
+ }
+ }
+ while( !done );
+
+ stream->isActive = 0;
+
+ if( stream->streamRepresentation.streamFinishedCallback != 0 )
+ stream->streamRepresentation.streamFinishedCallback( stream->streamRepresentation.userData );
+
+ PaUtil_ResetCpuLoadMeasurer( &stream->cpuLoadMeasurer );
+
+ return result;
+}
+
+
+/*
+ When CloseStream() is called, the multi-api layer ensures that
+ the stream has already been stopped or aborted.
+*/
+static PaError CloseStream( PaStream* s )
+{
+ PaError result;
+ PaWinMmeStream *stream = (PaWinMmeStream*)s;
+
+ result = CloseHandleWithPaError( stream->abortEvent );
+ if( result != paNoError ) goto error;
+
+ TerminateWaveHeaders( &stream->output, 0 /* not isInput */ );
+ TerminateWaveHeaders( &stream->input, 1 /* isInput */ );
+
+ TerminateWaveHandles( &stream->output, 0 /* not isInput */, 0 /* not currentlyProcessingAnError */ );
+ TerminateWaveHandles( &stream->input, 1 /* isInput */, 0 /* not currentlyProcessingAnError */ );
+
+ PaUtil_TerminateBufferProcessor( &stream->bufferProcessor );
+ PaUtil_TerminateStreamRepresentation( &stream->streamRepresentation );
+ PaUtil_FreeMemory( stream );
+
+error:
+ /** @todo REVIEW: what is the best way to clean up a stream if an error is detected? */
+ return result;
+}
+
+
+static PaError StartStream( PaStream *s )
+{
+ PaError result;
+ PaWinMmeStream *stream = (PaWinMmeStream*)s;
+ MMRESULT mmresult;
+ unsigned int i, j;
+ int callbackResult;
+ unsigned int channel;
+ unsigned long framesProcessed;
+ PaStreamCallbackTimeInfo timeInfo = {0,0,0}; /** @todo implement this for stream priming */
+
+ PaUtil_ResetBufferProcessor( &stream->bufferProcessor );
+
+ if( PA_IS_INPUT_STREAM_(stream) )
+ {
+ for( i=0; i<stream->input.bufferCount; ++i )
+ {
+ for( j=0; j<stream->input.deviceCount; ++j )
+ {
+ mmresult = waveInAddBuffer( ((HWAVEIN*)stream->input.waveHandles)[j], &stream->input.waveHeaders[j][i], sizeof(WAVEHDR) );
+ if( mmresult != MMSYSERR_NOERROR )
+ {
+ result = paUnanticipatedHostError;
+ PA_MME_SET_LAST_WAVEIN_ERROR( mmresult );
+ goto error;
+ }
+ }
+ }
+ stream->input.currentBufferIndex = 0;
+ stream->input.framesUsedInCurrentBuffer = 0;
+ }
+
+ if( PA_IS_OUTPUT_STREAM_(stream) )
+ {
+ for( i=0; i<stream->output.deviceCount; ++i )
+ {
+ if( (mmresult = waveOutPause( ((HWAVEOUT*)stream->output.waveHandles)[i] )) != MMSYSERR_NOERROR )
+ {
+ result = paUnanticipatedHostError;
+ PA_MME_SET_LAST_WAVEOUT_ERROR( mmresult );
+ goto error;
+ }
+ }
+
+ for( i=0; i<stream->output.bufferCount; ++i )
+ {
+ if( stream->primeStreamUsingCallback )
+ {
+
+ stream->output.framesUsedInCurrentBuffer = 0;
+ do{
+
+ PaUtil_BeginBufferProcessing( &stream->bufferProcessor,
+ &timeInfo,
+ paPrimingOutput | ((stream->input.bufferCount > 0 ) ? paInputUnderflow : 0));
+
+ if( stream->input.bufferCount > 0 )
+ PaUtil_SetNoInput( &stream->bufferProcessor );
+
+ PaUtil_SetOutputFrameCount( &stream->bufferProcessor, 0 /* default to host buffer size */ );
+
+ channel = 0;
+ for( j=0; j<stream->output.deviceCount; ++j )
+ {
+ /* we have stored the number of channels in the buffer in dwUser */
+ int channelCount = stream->output.waveHeaders[j][i].dwUser;
+
+ PaUtil_SetInterleavedOutputChannels( &stream->bufferProcessor, channel,
+ stream->output.waveHeaders[j][i].lpData +
+ stream->output.framesUsedInCurrentBuffer * channelCount *
+ stream->bufferProcessor.bytesPerHostOutputSample,
+ channelCount );
+
+ /* we have stored the number of channels in the buffer in dwUser */
+ channel += channelCount;
+ }
+
+ callbackResult = paContinue;
+ framesProcessed = PaUtil_EndBufferProcessing( &stream->bufferProcessor, &callbackResult );
+ stream->output.framesUsedInCurrentBuffer += framesProcessed;
+
+ if( callbackResult != paContinue )
+ {
+ /** @todo fix this, what do we do if callback result is non-zero during stream
+ priming?
+
+ for complete: play out primed waveHeaders as usual
+ for abort: clean up immediately.
+ */
+ }
+
+ }while( stream->output.framesUsedInCurrentBuffer != stream->output.framesPerBuffer );
+
+ }
+ else
+ {
+ for( j=0; j<stream->output.deviceCount; ++j )
+ {
+ ZeroMemory( stream->output.waveHeaders[j][i].lpData, stream->output.waveHeaders[j][i].dwBufferLength );
+ }
+ }
+
+ /* we queue all channels of a single buffer frame (accross all
+ devices, because some multidevice multichannel drivers work
+ better this way */
+ for( j=0; j<stream->output.deviceCount; ++j )
+ {
+ mmresult = waveOutWrite( ((HWAVEOUT*)stream->output.waveHandles)[j], &stream->output.waveHeaders[j][i], sizeof(WAVEHDR) );
+ if( mmresult != MMSYSERR_NOERROR )
+ {
+ result = paUnanticipatedHostError;
+ PA_MME_SET_LAST_WAVEOUT_ERROR( mmresult );
+ goto error;
+ }
+ }
+ }
+ stream->output.currentBufferIndex = 0;
+ stream->output.framesUsedInCurrentBuffer = 0;
+ }
+
+
+ stream->isStopped = 0;
+ stream->isActive = 1;
+ stream->stopProcessing = 0;
+ stream->abortProcessing = 0;
+
+ result = ResetEventWithPaError( stream->input.bufferEvent );
+ if( result != paNoError ) goto error;
+
+ result = ResetEventWithPaError( stream->output.bufferEvent );
+ if( result != paNoError ) goto error;
+
+
+ if( stream->streamRepresentation.streamCallback )
+ {
+ /* callback stream */
+
+ result = ResetEventWithPaError( stream->abortEvent );
+ if( result != paNoError ) goto error;
+
+ /* Create thread that waits for audio buffers to be ready for processing. */
+ stream->processingThread = CreateThread( 0, 0, ProcessingThreadProc, stream, 0, &stream->processingThreadId );
+ if( !stream->processingThread )
+ {
+ result = paUnanticipatedHostError;
+ PA_MME_SET_LAST_SYSTEM_ERROR( GetLastError() );
+ goto error;
+ }
+
+ /** @todo could have mme specific stream parameters to allow the user
+ to set the callback thread priorities */
+ stream->highThreadPriority = THREAD_PRIORITY_TIME_CRITICAL;
+ stream->throttledThreadPriority = THREAD_PRIORITY_NORMAL;
+
+ if( !SetThreadPriority( stream->processingThread, stream->highThreadPriority ) )
+ {
+ result = paUnanticipatedHostError;
+ PA_MME_SET_LAST_SYSTEM_ERROR( GetLastError() );
+ goto error;
+ }
+ stream->processingThreadPriority = stream->highThreadPriority;
+ }
+ else
+ {
+ /* blocking read/write stream */
+
+ }
+
+ if( PA_IS_INPUT_STREAM_(stream) )
+ {
+ for( i=0; i < stream->input.deviceCount; ++i )
+ {
+ mmresult = waveInStart( ((HWAVEIN*)stream->input.waveHandles)[i] );
+ PA_DEBUG(("Pa_StartStream: waveInStart returned = 0x%X.\n", mmresult));
+ if( mmresult != MMSYSERR_NOERROR )
+ {
+ result = paUnanticipatedHostError;
+ PA_MME_SET_LAST_WAVEIN_ERROR( mmresult );
+ goto error;
+ }
+ }
+ }
+
+ if( PA_IS_OUTPUT_STREAM_(stream) )
+ {
+ for( i=0; i < stream->output.deviceCount; ++i )
+ {
+ if( (mmresult = waveOutRestart( ((HWAVEOUT*)stream->output.waveHandles)[i] )) != MMSYSERR_NOERROR )
+ {
+ result = paUnanticipatedHostError;
+ PA_MME_SET_LAST_WAVEOUT_ERROR( mmresult );
+ goto error;
+ }
+ }
+ }
+
+ return result;
+
+error:
+ /** @todo FIXME: implement recovery as best we can
+ This should involve rolling back to a state as-if this function had never been called
+ */
+ return result;
+}
+
+
+static PaError StopStream( PaStream *s )
+{
+ PaError result = paNoError;
+ PaWinMmeStream *stream = (PaWinMmeStream*)s;
+ int timeout;
+ DWORD waitResult;
+ MMRESULT mmresult;
+ signed int hostOutputBufferIndex;
+ unsigned int channel, waitCount, i;
+
+ /** @todo
+ REVIEW: the error checking in this function needs review. the basic
+ idea is to return from this function in a known state - for example
+ there is no point avoiding calling waveInReset just because
+ the thread times out.
+ */
+
+ if( stream->processingThread )
+ {
+ /* callback stream */
+
+ /* Tell processing thread to stop generating more data and to let current data play out. */
+ stream->stopProcessing = 1;
+
+ /* Calculate timeOut longer than longest time it could take to return all buffers. */
+ timeout = (int)(stream->allBuffersDurationMs * 1.5);
+ if( timeout < PA_MME_MIN_TIMEOUT_MSEC_ )
+ timeout = PA_MME_MIN_TIMEOUT_MSEC_;
+
+ PA_DEBUG(("WinMME StopStream: waiting for background thread.\n"));
+
+ waitResult = WaitForSingleObject( stream->processingThread, timeout );
+ if( waitResult == WAIT_TIMEOUT )
+ {
+ /* try to abort */
+ stream->abortProcessing = 1;
+ SetEvent( stream->abortEvent );
+ waitResult = WaitForSingleObject( stream->processingThread, timeout );
+ if( waitResult == WAIT_TIMEOUT )
+ {
+ PA_DEBUG(("WinMME StopStream: timed out while waiting for background thread to finish.\n"));
+ result = paTimedOut;
+ }
+ }
+
+ CloseHandle( stream->processingThread );
+ stream->processingThread = NULL;
+ }
+ else
+ {
+ /* blocking read / write stream */
+
+ if( PA_IS_OUTPUT_STREAM_(stream) )
+ {
+ if( stream->output.framesUsedInCurrentBuffer > 0 )
+ {
+ /* there are still unqueued frames in the current buffer, so flush them */
+
+ hostOutputBufferIndex = stream->output.currentBufferIndex;
+
+ PaUtil_SetOutputFrameCount( &stream->bufferProcessor,
+ stream->output.framesPerBuffer - stream->output.framesUsedInCurrentBuffer );
+
+ channel = 0;
+ for( i=0; i<stream->output.deviceCount; ++i )
+ {
+ /* we have stored the number of channels in the buffer in dwUser */
+ int channelCount = stream->output.waveHeaders[i][ hostOutputBufferIndex ].dwUser;
+
+ PaUtil_SetInterleavedOutputChannels( &stream->bufferProcessor, channel,
+ stream->output.waveHeaders[i][ hostOutputBufferIndex ].lpData +
+ stream->output.framesUsedInCurrentBuffer * channelCount *
+ stream->bufferProcessor.bytesPerHostOutputSample,
+ channelCount );
+
+ channel += channelCount;
+ }
+
+ PaUtil_ZeroOutput( &stream->bufferProcessor,
+ stream->output.framesPerBuffer - stream->output.framesUsedInCurrentBuffer );
+
+ /* we send the entire buffer to the output devices, but we could
+ just send a partial buffer, rather than zeroing the unused
+ samples.
+ */
+ AdvanceToNextOutputBuffer( stream );
+ }
+
+
+ timeout = (stream->allBuffersDurationMs / stream->output.bufferCount) + 1;
+ if( timeout < PA_MME_MIN_TIMEOUT_MSEC_ )
+ timeout = PA_MME_MIN_TIMEOUT_MSEC_;
+
+ waitCount = 0;
+ while( !NoBuffersAreQueued( &stream->output ) && waitCount <= stream->output.bufferCount )
+ {
+ /* wait for MME to signal that a buffer is available */
+ waitResult = WaitForSingleObject( stream->output.bufferEvent, timeout );
+ if( waitResult == WAIT_FAILED )
+ {
+ break;
+ }
+ else if( waitResult == WAIT_TIMEOUT )
+ {
+ /* keep waiting */
+ }
+
+ ++waitCount;
+ }
+ }
+ }
+
+ if( PA_IS_OUTPUT_STREAM_(stream) )
+ {
+ for( i =0; i < stream->output.deviceCount; ++i )
+ {
+ mmresult = waveOutReset( ((HWAVEOUT*)stream->output.waveHandles)[i] );
+ if( mmresult != MMSYSERR_NOERROR )
+ {
+ result = paUnanticipatedHostError;
+ PA_MME_SET_LAST_WAVEOUT_ERROR( mmresult );
+ }
+ }
+ }
+
+ if( PA_IS_INPUT_STREAM_(stream) )
+ {
+ for( i=0; i < stream->input.deviceCount; ++i )
+ {
+ mmresult = waveInReset( ((HWAVEIN*)stream->input.waveHandles)[i] );
+ if( mmresult != MMSYSERR_NOERROR )
+ {
+ result = paUnanticipatedHostError;
+ PA_MME_SET_LAST_WAVEIN_ERROR( mmresult );
+ }
+ }
+ }
+
+ stream->isStopped = 1;
+ stream->isActive = 0;
+
+ return result;
+}
+
+
+static PaError AbortStream( PaStream *s )
+{
+ PaError result = paNoError;
+ PaWinMmeStream *stream = (PaWinMmeStream*)s;
+ int timeout;
+ DWORD waitResult;
+ MMRESULT mmresult;
+ unsigned int i;
+
+ /** @todo
+ REVIEW: the error checking in this function needs review. the basic
+ idea is to return from this function in a known state - for example
+ there is no point avoiding calling waveInReset just because
+ the thread times out.
+ */
+
+ if( stream->processingThread )
+ {
+ /* callback stream */
+
+ /* Tell processing thread to abort immediately */
+ stream->abortProcessing = 1;
+ SetEvent( stream->abortEvent );
+ }
+
+
+ if( PA_IS_OUTPUT_STREAM_(stream) )
+ {
+ for( i =0; i < stream->output.deviceCount; ++i )
+ {
+ mmresult = waveOutReset( ((HWAVEOUT*)stream->output.waveHandles)[i] );
+ if( mmresult != MMSYSERR_NOERROR )
+ {
+ PA_MME_SET_LAST_WAVEOUT_ERROR( mmresult );
+ return paUnanticipatedHostError;
+ }
+ }
+ }
+
+ if( PA_IS_INPUT_STREAM_(stream) )
+ {
+ for( i=0; i < stream->input.deviceCount; ++i )
+ {
+ mmresult = waveInReset( ((HWAVEIN*)stream->input.waveHandles)[i] );
+ if( mmresult != MMSYSERR_NOERROR )
+ {
+ PA_MME_SET_LAST_WAVEIN_ERROR( mmresult );
+ return paUnanticipatedHostError;
+ }
+ }
+ }
+
+
+ if( stream->processingThread )
+ {
+ /* callback stream */
+
+ PA_DEBUG(("WinMME AbortStream: waiting for background thread.\n"));
+
+ /* Calculate timeOut longer than longest time it could take to return all buffers. */
+ timeout = (int)(stream->allBuffersDurationMs * 1.5);
+ if( timeout < PA_MME_MIN_TIMEOUT_MSEC_ )
+ timeout = PA_MME_MIN_TIMEOUT_MSEC_;
+
+ waitResult = WaitForSingleObject( stream->processingThread, timeout );
+ if( waitResult == WAIT_TIMEOUT )
+ {
+ PA_DEBUG(("WinMME AbortStream: timed out while waiting for background thread to finish.\n"));
+ return paTimedOut;
+ }
+
+ CloseHandle( stream->processingThread );
+ stream->processingThread = NULL;
+ }
+
+ stream->isStopped = 1;
+ stream->isActive = 0;
+
+ return result;
+}
+
+
+static PaError IsStreamStopped( PaStream *s )
+{
+ PaWinMmeStream *stream = (PaWinMmeStream*)s;
+
+ return stream->isStopped;
+}
+
+
+static PaError IsStreamActive( PaStream *s )
+{
+ PaWinMmeStream *stream = (PaWinMmeStream*)s;
+
+ return stream->isActive;
+}
+
+
+static PaTime GetStreamTime( PaStream *s )
+{
+ (void) s; /* unused parameter */
+
+ return PaUtil_GetTime();
+}
+
+
+static double GetStreamCpuLoad( PaStream* s )
+{
+ PaWinMmeStream *stream = (PaWinMmeStream*)s;
+
+ return PaUtil_GetCpuLoad( &stream->cpuLoadMeasurer );
+}
+
+
+/*
+ As separate stream interfaces are used for blocking and callback
+ streams, the following functions can be guaranteed to only be called
+ for blocking streams.
+*/
+
+static PaError ReadStream( PaStream* s,
+ void *buffer,
+ unsigned long frames )
+{
+ PaError result = paNoError;
+ PaWinMmeStream *stream = (PaWinMmeStream*)s;
+ void *userBuffer;
+ unsigned long framesRead = 0;
+ unsigned long framesProcessed;
+ signed int hostInputBufferIndex;
+ DWORD waitResult;
+ DWORD timeout = (unsigned long)(stream->allBuffersDurationMs * 0.5);
+ unsigned int channel, i;
+
+ if( PA_IS_INPUT_STREAM_(stream) )
+ {
+ /* make a local copy of the user buffer pointer(s). this is necessary
+ because PaUtil_CopyInput() advances these pointers every time
+ it is called.
+ */
+ if( stream->bufferProcessor.userInputIsInterleaved )
+ {
+ userBuffer = buffer;
+ }
+ else
+ {
+ userBuffer = alloca( sizeof(void*) * stream->bufferProcessor.inputChannelCount );
+ if( !userBuffer )
+ return paInsufficientMemory;
+ for( i = 0; i<stream->bufferProcessor.inputChannelCount; ++i )
+ ((void**)userBuffer)[i] = ((void**)buffer)[i];
+ }
+
+ do{
+ if( CurrentInputBuffersAreDone( stream ) )
+ {
+ if( NoBuffersAreQueued( &stream->input ) )
+ {
+ /** @todo REVIEW: consider what to do if the input overflows.
+ do we requeue all of the buffers? should we be running
+ a thread to make sure they are always queued? */
+
+ result = paInputOverflowed;
+ }
+
+ hostInputBufferIndex = stream->input.currentBufferIndex;
+
+ PaUtil_SetInputFrameCount( &stream->bufferProcessor,
+ stream->input.framesPerBuffer - stream->input.framesUsedInCurrentBuffer );
+
+ channel = 0;
+ for( i=0; i<stream->input.deviceCount; ++i )
+ {
+ /* we have stored the number of channels in the buffer in dwUser */
+ int channelCount = stream->input.waveHeaders[i][ hostInputBufferIndex ].dwUser;
+
+ PaUtil_SetInterleavedInputChannels( &stream->bufferProcessor, channel,
+ stream->input.waveHeaders[i][ hostInputBufferIndex ].lpData +
+ stream->input.framesUsedInCurrentBuffer * channelCount *
+ stream->bufferProcessor.bytesPerHostInputSample,
+ channelCount );
+
+ channel += channelCount;
+ }
+
+ framesProcessed = PaUtil_CopyInput( &stream->bufferProcessor, &userBuffer, frames - framesRead );
+
+ stream->input.framesUsedInCurrentBuffer += framesProcessed;
+ if( stream->input.framesUsedInCurrentBuffer == stream->input.framesPerBuffer )
+ {
+ result = AdvanceToNextInputBuffer( stream );
+ if( result != paNoError )
+ break;
+ }
+
+ framesRead += framesProcessed;
+
+ }else{
+ /* wait for MME to signal that a buffer is available */
+ waitResult = WaitForSingleObject( stream->input.bufferEvent, timeout );
+ if( waitResult == WAIT_FAILED )
+ {
+ result = paUnanticipatedHostError;
+ break;
+ }
+ else if( waitResult == WAIT_TIMEOUT )
+ {
+ /* if a timeout is encountered, continue,
+ perhaps we should give up eventually
+ */
+ }
+ }
+ }while( framesRead < frames );
+ }
+ else
+ {
+ result = paCanNotReadFromAnOutputOnlyStream;
+ }
+
+ return result;
+}
+
+
+static PaError WriteStream( PaStream* s,
+ const void *buffer,
+ unsigned long frames )
+{
+ PaError result = paNoError;
+ PaWinMmeStream *stream = (PaWinMmeStream*)s;
+ const void *userBuffer;
+ unsigned long framesWritten = 0;
+ unsigned long framesProcessed;
+ signed int hostOutputBufferIndex;
+ DWORD waitResult;
+ DWORD timeout = (unsigned long)(stream->allBuffersDurationMs * 0.5);
+ unsigned int channel, i;
+
+
+ if( PA_IS_OUTPUT_STREAM_(stream) )
+ {
+ /* make a local copy of the user buffer pointer(s). this is necessary
+ because PaUtil_CopyOutput() advances these pointers every time
+ it is called.
+ */
+ if( stream->bufferProcessor.userOutputIsInterleaved )
+ {
+ userBuffer = buffer;
+ }
+ else
+ {
+ userBuffer = alloca( sizeof(void*) * stream->bufferProcessor.outputChannelCount );
+ if( !userBuffer )
+ return paInsufficientMemory;
+ for( i = 0; i<stream->bufferProcessor.outputChannelCount; ++i )
+ ((const void**)userBuffer)[i] = ((const void**)buffer)[i];
+ }
+
+ do{
+ if( CurrentOutputBuffersAreDone( stream ) )
+ {
+ if( NoBuffersAreQueued( &stream->output ) )
+ {
+ /** @todo REVIEW: consider what to do if the output
+ underflows. do we requeue all the existing buffers with
+ zeros? should we run a separate thread to keep the buffers
+ enqueued at all times? */
+
+ result = paOutputUnderflowed;
+ }
+
+ hostOutputBufferIndex = stream->output.currentBufferIndex;
+
+ PaUtil_SetOutputFrameCount( &stream->bufferProcessor,
+ stream->output.framesPerBuffer - stream->output.framesUsedInCurrentBuffer );
+
+ channel = 0;
+ for( i=0; i<stream->output.deviceCount; ++i )
+ {
+ /* we have stored the number of channels in the buffer in dwUser */
+ int channelCount = stream->output.waveHeaders[i][ hostOutputBufferIndex ].dwUser;
+
+ PaUtil_SetInterleavedOutputChannels( &stream->bufferProcessor, channel,
+ stream->output.waveHeaders[i][ hostOutputBufferIndex ].lpData +
+ stream->output.framesUsedInCurrentBuffer * channelCount *
+ stream->bufferProcessor.bytesPerHostOutputSample,
+ channelCount );
+
+ channel += channelCount;
+ }
+
+ framesProcessed = PaUtil_CopyOutput( &stream->bufferProcessor, &userBuffer, frames - framesWritten );
+
+ stream->output.framesUsedInCurrentBuffer += framesProcessed;
+ if( stream->output.framesUsedInCurrentBuffer == stream->output.framesPerBuffer )
+ {
+ result = AdvanceToNextOutputBuffer( stream );
+ if( result != paNoError )
+ break;
+ }
+
+ framesWritten += framesProcessed;
+ }
+ else
+ {
+ /* wait for MME to signal that a buffer is available */
+ waitResult = WaitForSingleObject( stream->output.bufferEvent, timeout );
+ if( waitResult == WAIT_FAILED )
+ {
+ result = paUnanticipatedHostError;
+ break;
+ }
+ else if( waitResult == WAIT_TIMEOUT )
+ {
+ /* if a timeout is encountered, continue,
+ perhaps we should give up eventually
+ */
+ }
+ }
+ }while( framesWritten < frames );
+ }
+ else
+ {
+ result = paCanNotWriteToAnInputOnlyStream;
+ }
+
+ return result;
+}
+
+
+static signed long GetStreamReadAvailable( PaStream* s )
+{
+ PaWinMmeStream *stream = (PaWinMmeStream*)s;
+
+ if( PA_IS_INPUT_STREAM_(stream) )
+ return GetAvailableFrames( &stream->input );
+ else
+ return paCanNotReadFromAnOutputOnlyStream;
+}
+
+
+static signed long GetStreamWriteAvailable( PaStream* s )
+{
+ PaWinMmeStream *stream = (PaWinMmeStream*)s;
+
+ if( PA_IS_OUTPUT_STREAM_(stream) )
+ return GetAvailableFrames( &stream->output );
+ else
+ return paCanNotWriteToAnInputOnlyStream;
+}
+
+
+/* NOTE: the following functions are MME-stream specific, and are called directly
+ by client code. We need to check for many more error conditions here because
+ we don't have the benefit of pa_front.c's parameter checking.
+*/
+
+static PaError GetWinMMEStreamPointer( PaWinMmeStream **stream, PaStream *s )
+{
+ PaError result;
+ PaUtilHostApiRepresentation *hostApi;
+ PaWinMmeHostApiRepresentation *winMmeHostApi;
+
+ result = PaUtil_ValidateStreamPointer( s );
+ if( result != paNoError )
+ return result;
+
+ result = PaUtil_GetHostApiRepresentation( &hostApi, paMME );
+ if( result != paNoError )
+ return result;
+
+ winMmeHostApi = (PaWinMmeHostApiRepresentation*)hostApi;
+
+ /* note, the following would be easier if there was a generic way of testing
+ that a stream belongs to a specific host API */
+
+ if( PA_STREAM_REP( s )->streamInterface == &winMmeHostApi->callbackStreamInterface
+ || PA_STREAM_REP( s )->streamInterface == &winMmeHostApi->blockingStreamInterface )
+ {
+ /* s is a WinMME stream */
+ *stream = (PaWinMmeStream *)s;
+ return paNoError;
+ }
+ else
+ {
+ return paIncompatibleStreamHostApi;
+ }
+}
+
+
+int PaWinMME_GetStreamInputHandleCount( PaStream* s )
+{
+ PaWinMmeStream *stream;
+ PaError result = GetWinMMEStreamPointer( &stream, s );
+
+ if( result == paNoError )
+ return (PA_IS_INPUT_STREAM_(stream)) ? stream->input.deviceCount : 0;
+ else
+ return result;
+}
+
+
+HWAVEIN PaWinMME_GetStreamInputHandle( PaStream* s, int handleIndex )
+{
+ PaWinMmeStream *stream;
+ PaError result = GetWinMMEStreamPointer( &stream, s );
+
+ if( result == paNoError
+ && PA_IS_INPUT_STREAM_(stream)
+ && handleIndex >= 0
+ && (unsigned int)handleIndex < stream->input.deviceCount )
+ return ((HWAVEIN*)stream->input.waveHandles)[handleIndex];
+ else
+ return 0;
+}
+
+
+int PaWinMME_GetStreamOutputHandleCount( PaStream* s)
+{
+ PaWinMmeStream *stream;
+ PaError result = GetWinMMEStreamPointer( &stream, s );
+
+ if( result == paNoError )
+ return (PA_IS_OUTPUT_STREAM_(stream)) ? stream->output.deviceCount : 0;
+ else
+ return result;
+}
+
+
+HWAVEOUT PaWinMME_GetStreamOutputHandle( PaStream* s, int handleIndex )
+{
+ PaWinMmeStream *stream;
+ PaError result = GetWinMMEStreamPointer( &stream, s );
+
+ if( result == paNoError
+ && PA_IS_OUTPUT_STREAM_(stream)
+ && handleIndex >= 0
+ && (unsigned int)handleIndex < stream->output.deviceCount )
+ return ((HWAVEOUT*)stream->output.waveHandles)[handleIndex];
+ else
+ return 0;
+}
+
+
+
+
+
diff --git a/pjmedia/src/pjmedia/portaudio/pa_win_wmme.h b/pjmedia/src/pjmedia/portaudio/pa_win_wmme.h
index 43469132..1a71633a 100644
--- a/pjmedia/src/pjmedia/portaudio/pa_win_wmme.h
+++ b/pjmedia/src/pjmedia/portaudio/pa_win_wmme.h
@@ -1,160 +1,160 @@
-#ifndef PA_WIN_WMME_H
-#define PA_WIN_WMME_H
-/*
- * $Id: pa_win_wmme.h,v 1.1.2.14 2004/02/20 14:16:53 rossbencina Exp $
- * PortAudio Portable Real-Time Audio Library
- * MME specific extensions
- *
- * Copyright (c) 1999-2000 Ross Bencina and Phil Burk
- *
- * 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.
- *
- * Any person wishing to distribute modifications to the Software is
- * requested to send the modifications to the original developer so that
- * they can be incorporated into the canonical version.
- *
- * 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.
- *
- */
-
-/** @file
- @brief WMME-specific PortAudio API extension header file.
-*/
-
-
-#include "portaudio.h"
-
-#ifdef __cplusplus
-extern "C"
-{
-#endif /* __cplusplus */
-
-
-#define paWinMmeUseLowLevelLatencyParameters (0x01)
-#define paWinMmeUseMultipleDevices (0x02) /* use mme specific multiple device feature */
-
-
-/* By default, the mme implementation drops the processing thread's priority
- to THREAD_PRIORITY_NORMAL and sleeps the thread if the CPU load exceeds 100%
- This flag disables any priority throttling. The processing thread will always
- run at THREAD_PRIORITY_TIME_CRITICAL.
-*/
-#define paWinMmeDontThrottleOverloadedProcessingThread (0x08)
-
-
-typedef struct PaWinMmeDeviceAndChannelCount{
- PaDeviceIndex device;
- int channelCount;
-}PaWinMmeDeviceAndChannelCount;
-
-
-typedef struct PaWinMmeStreamInfo{
- unsigned long size; /**< sizeof(PaWinMmeStreamInfo) */
- PaHostApiTypeId hostApiType; /**< paMME */
- unsigned long version; /**< 1 */
-
- unsigned long flags;
-
- /* low-level latency setting support
- These settings control the number and size of host buffers in order
- to set latency. They will be used instead of the generic parameters
- to Pa_OpenStream() if flags contains the PaWinMmeUseLowLevelLatencyParameters
- flag.
-
- If PaWinMmeStreamInfo structures with PaWinMmeUseLowLevelLatencyParameters
- are supplied for both input and output in a full duplex stream, then the
- input and output framesPerBuffer must be the same, or the larger of the
- two must be a multiple of the smaller, otherwise a
- paIncompatibleHostApiSpecificStreamInfo error will be returned from
- Pa_OpenStream().
- */
- unsigned long framesPerBuffer;
- unsigned long bufferCount; /* formerly numBuffers */
-
- /* multiple devices per direction support
- If flags contains the PaWinMmeUseMultipleDevices flag,
- this functionality will be used, otherwise the device parameter to
- Pa_OpenStream() will be used instead.
- If devices are specified here, the corresponding device parameter
- to Pa_OpenStream() should be set to paUseHostApiSpecificDeviceSpecification,
- otherwise an paInvalidDevice error will result.
- The total number of channels accross all specified devices
- must agree with the corresponding channelCount parameter to
- Pa_OpenStream() otherwise a paInvalidChannelCount error will result.
- */
- PaWinMmeDeviceAndChannelCount *devices;
- unsigned long deviceCount;
-
-}PaWinMmeStreamInfo;
-
-
-/** Retrieve the number of wave in handles used by a PortAudio WinMME stream.
- Returns zero if the stream is output only.
-
- @return A non-negative value indicating the number of wave in handles
- or, a PaErrorCode (which are always negative) if PortAudio is not initialized
- or an error is encountered.
-
- @see PaWinMME_GetStreamInputHandle
-*/
-int PaWinMME_GetStreamInputHandleCount( PaStream* stream );
-
-
-/** Retrieve a wave in handle used by a PortAudio WinMME stream.
-
- @param stream The stream to query.
- @param handleIndex The zero based index of the wave in handle to retrieve. This
- should be in the range [0, PaWinMME_GetStreamInputHandle(stream)-1].
-
- @return A valid wave in handle, or NULL if an error occurred.
-
- @see PaWinMME_GetStreamInputHandle
-*/
-HWAVEIN PaWinMME_GetStreamInputHandle( PaStream* stream, int handleIndex );
-
-
-/** Retrieve the number of wave out handles used by a PortAudio WinMME stream.
- Returns zero if the stream is input only.
-
- @return A non-negative value indicating the number of wave out handles
- or, a PaErrorCode (which are always negative) if PortAudio is not initialized
- or an error is encountered.
-
- @see PaWinMME_GetStreamOutputHandle
-*/
-int PaWinMME_GetStreamOutputHandleCount( PaStream* stream );
-
-
-/** Retrieve a wave out handle used by a PortAudio WinMME stream.
-
- @param stream The stream to query.
- @param handleIndex The zero based index of the wave out handle to retrieve.
- This should be in the range [0, PaWinMME_GetStreamOutputHandleCount(stream)-1].
-
- @return A valid wave out handle, or NULL if an error occurred.
-
- @see PaWinMME_GetStreamOutputHandleCount
-*/
-HWAVEOUT PaWinMME_GetStreamOutputHandle( PaStream* stream, int handleIndex );
-
-
-#ifdef __cplusplus
-}
-#endif /* __cplusplus */
-
-#endif /* PA_WIN_WMME_H */
+#ifndef PA_WIN_WMME_H
+#define PA_WIN_WMME_H
+/*
+ * $Id: pa_win_wmme.h,v 1.1.2.14 2004/02/20 14:16:53 rossbencina Exp $
+ * PortAudio Portable Real-Time Audio Library
+ * MME specific extensions
+ *
+ * Copyright (c) 1999-2000 Ross Bencina and Phil Burk
+ *
+ * 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.
+ *
+ * Any person wishing to distribute modifications to the Software is
+ * requested to send the modifications to the original developer so that
+ * they can be incorporated into the canonical version.
+ *
+ * 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.
+ *
+ */
+
+/** @file
+ @brief WMME-specific PortAudio API extension header file.
+*/
+
+
+#include "portaudio.h"
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif /* __cplusplus */
+
+
+#define paWinMmeUseLowLevelLatencyParameters (0x01)
+#define paWinMmeUseMultipleDevices (0x02) /* use mme specific multiple device feature */
+
+
+/* By default, the mme implementation drops the processing thread's priority
+ to THREAD_PRIORITY_NORMAL and sleeps the thread if the CPU load exceeds 100%
+ This flag disables any priority throttling. The processing thread will always
+ run at THREAD_PRIORITY_TIME_CRITICAL.
+*/
+#define paWinMmeDontThrottleOverloadedProcessingThread (0x08)
+
+
+typedef struct PaWinMmeDeviceAndChannelCount{
+ PaDeviceIndex device;
+ int channelCount;
+}PaWinMmeDeviceAndChannelCount;
+
+
+typedef struct PaWinMmeStreamInfo{
+ unsigned long size; /**< sizeof(PaWinMmeStreamInfo) */
+ PaHostApiTypeId hostApiType; /**< paMME */
+ unsigned long version; /**< 1 */
+
+ unsigned long flags;
+
+ /* low-level latency setting support
+ These settings control the number and size of host buffers in order
+ to set latency. They will be used instead of the generic parameters
+ to Pa_OpenStream() if flags contains the PaWinMmeUseLowLevelLatencyParameters
+ flag.
+
+ If PaWinMmeStreamInfo structures with PaWinMmeUseLowLevelLatencyParameters
+ are supplied for both input and output in a full duplex stream, then the
+ input and output framesPerBuffer must be the same, or the larger of the
+ two must be a multiple of the smaller, otherwise a
+ paIncompatibleHostApiSpecificStreamInfo error will be returned from
+ Pa_OpenStream().
+ */
+ unsigned long framesPerBuffer;
+ unsigned long bufferCount; /* formerly numBuffers */
+
+ /* multiple devices per direction support
+ If flags contains the PaWinMmeUseMultipleDevices flag,
+ this functionality will be used, otherwise the device parameter to
+ Pa_OpenStream() will be used instead.
+ If devices are specified here, the corresponding device parameter
+ to Pa_OpenStream() should be set to paUseHostApiSpecificDeviceSpecification,
+ otherwise an paInvalidDevice error will result.
+ The total number of channels accross all specified devices
+ must agree with the corresponding channelCount parameter to
+ Pa_OpenStream() otherwise a paInvalidChannelCount error will result.
+ */
+ PaWinMmeDeviceAndChannelCount *devices;
+ unsigned long deviceCount;
+
+}PaWinMmeStreamInfo;
+
+
+/** Retrieve the number of wave in handles used by a PortAudio WinMME stream.
+ Returns zero if the stream is output only.
+
+ @return A non-negative value indicating the number of wave in handles
+ or, a PaErrorCode (which are always negative) if PortAudio is not initialized
+ or an error is encountered.
+
+ @see PaWinMME_GetStreamInputHandle
+*/
+int PaWinMME_GetStreamInputHandleCount( PaStream* stream );
+
+
+/** Retrieve a wave in handle used by a PortAudio WinMME stream.
+
+ @param stream The stream to query.
+ @param handleIndex The zero based index of the wave in handle to retrieve. This
+ should be in the range [0, PaWinMME_GetStreamInputHandle(stream)-1].
+
+ @return A valid wave in handle, or NULL if an error occurred.
+
+ @see PaWinMME_GetStreamInputHandle
+*/
+HWAVEIN PaWinMME_GetStreamInputHandle( PaStream* stream, int handleIndex );
+
+
+/** Retrieve the number of wave out handles used by a PortAudio WinMME stream.
+ Returns zero if the stream is input only.
+
+ @return A non-negative value indicating the number of wave out handles
+ or, a PaErrorCode (which are always negative) if PortAudio is not initialized
+ or an error is encountered.
+
+ @see PaWinMME_GetStreamOutputHandle
+*/
+int PaWinMME_GetStreamOutputHandleCount( PaStream* stream );
+
+
+/** Retrieve a wave out handle used by a PortAudio WinMME stream.
+
+ @param stream The stream to query.
+ @param handleIndex The zero based index of the wave out handle to retrieve.
+ This should be in the range [0, PaWinMME_GetStreamOutputHandleCount(stream)-1].
+
+ @return A valid wave out handle, or NULL if an error occurred.
+
+ @see PaWinMME_GetStreamOutputHandleCount
+*/
+HWAVEOUT PaWinMME_GetStreamOutputHandle( PaStream* stream, int handleIndex );
+
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+#endif /* PA_WIN_WMME_H */
diff --git a/pjmedia/src/pjmedia/portaudio/pa_x86_plain_converters.c b/pjmedia/src/pjmedia/portaudio/pa_x86_plain_converters.c
index 07b3c2ec..3a33540b 100644
--- a/pjmedia/src/pjmedia/portaudio/pa_x86_plain_converters.c
+++ b/pjmedia/src/pjmedia/portaudio/pa_x86_plain_converters.c
@@ -1,1167 +1,1167 @@
-#include "pa_x86_plain_converters.h"
-
-#include "pa_converters.h"
-#include "pa_dither.h"
-
-/*
- plain intel assemby versions of standard pa converter functions.
-
- the main reason these versions are faster than the equivalent C versions
- is that float -> int casting is expensive in C on x86 because the rounding
- mode needs to be changed for every cast. these versions only set
- the rounding mode once outside the loop.
-
- small additional speed gains are made by the way that clamping is
- implemented.
-
-TODO:
- o- inline dither code
- o- implement Dither only (no-clip) versions
- o- implement int8 and uint8 versions
- o- test thouroughly
-
- o- the packed 24 bit functions could benefit from unrolling and avoiding
- byte and word sized register access.
-*/
-
-/* -------------------------------------------------------------------------- */
-
-/*
-#define PA_CLIP_( val, min, max )\
- { val = ((val) < (min)) ? (min) : (((val) > (max)) ? (max) : (val)); }
-*/
-
-/*
- the following notes were used to determine whether a floating point
- value should be saturated (ie >1 or <-1) by loading it into an integer
- register. these should be rewritten so that they make sense.
-
- an ieee floating point value
-
- 1.xxxxxxxxxxxxxxxxxxxx?
-
-
- is less than or equal to 1 and greater than or equal to -1 either:
-
- if the mantissa is 0 and the unbiased exponent is 0
-
- OR
-
- if the unbiased exponent < 0
-
- this translates to:
-
- if the mantissa is 0 and the biased exponent is 7F
-
- or
-
- if the biased exponent is less than 7F
-
-
- therefore the value is greater than 1 or less than -1 if
-
- the mantissa is not 0 and the biased exponent is 7F
-
- or
-
- if the biased exponent is greater than 7F
-
-
- in other words, if we mask out the sign bit, the value is
- greater than 1 or less than -1 if its integer representation is greater than:
-
- 0 01111111 0000 0000 0000 0000 0000 000
-
- 0011 1111 1000 0000 0000 0000 0000 0000 => 0x3F800000
-*/
-
-/* -------------------------------------------------------------------------- */
-
-static const short fpuControlWord_ = 0x033F; /*round to nearest, 64 bit precision, all exceptions masked*/
-static const double int32Scaler_ = 0x7FFFFFFF;
-static const double ditheredInt32Scaler_ = 0x7FFFFFFE;
-static const double int24Scaler_ = 0x7FFFFF;
-static const double ditheredInt24Scaler_ = 0x7FFFFE;
-static const double int16Scaler_ = 0x7FFF;
-static const double ditheredInt16Scaler_ = 0x7FFE;
-
-#define PA_DITHER_BITS_ (15)
-/* Multiply by PA_FLOAT_DITHER_SCALE_ to get a float between -2.0 and +1.99999 */
-#define PA_FLOAT_DITHER_SCALE_ (1.0 / ((1<<PA_DITHER_BITS_)-1))
-static const float const_float_dither_scale_ = (float) PA_FLOAT_DITHER_SCALE_;
-#define PA_DITHER_SHIFT_ ((32 - PA_DITHER_BITS_) + 1)
-
-/* -------------------------------------------------------------------------- */
-
-static void Float32_To_Int32(
- void *destinationBuffer, signed int destinationStride,
- void *sourceBuffer, signed int sourceStride,
- unsigned int count, PaUtilTriangularDitherGenerator *ditherGenerator )
-{
-/*
- float *src = (float*)sourceBuffer;
- signed long *dest = (signed long*)destinationBuffer;
- (void)ditherGenerator; // unused parameter
-
- while( count-- )
- {
- // REVIEW
- double scaled = *src * 0x7FFFFFFF;
- *dest = (signed long) scaled;
-
- src += sourceStride;
- dest += destinationStride;
- }
-*/
-
- short savedFpuControlWord;
-
- (void) ditherGenerator; /* unused parameter */
-
-
- __asm{
- // esi -> source ptr
- // eax -> source byte stride
- // edi -> destination ptr
- // ebx -> destination byte stride
- // ecx -> source end ptr
- // edx -> temp
-
- mov esi, sourceBuffer
-
- mov edx, 4 // sizeof float32 and int32
- mov eax, sourceStride
- imul eax, edx
-
- mov ecx, count
- imul ecx, eax
- add ecx, esi
-
- mov edi, destinationBuffer
-
- mov ebx, destinationStride
- imul ebx, edx
-
- fwait
- fstcw savedFpuControlWord
- fldcw fpuControlWord_
-
- fld int32Scaler_ // stack: (int)0x7FFFFFFF
-
- Float32_To_Int32_loop:
-
- // load unscaled value into st(0)
- fld dword ptr [esi] // stack: value, (int)0x7FFFFFFF
- add esi, eax // increment source ptr
- //lea esi, [esi+eax]
- fmul st(0), st(1) // st(0) *= st(1), stack: value*0x7FFFFFFF, (int)0x7FFFFFFF
- /*
- note: we could store to a temporary qword here which would cause
- wraparound distortion instead of int indefinite 0x10. that would
- be more work, and given that not enabling clipping is only advisable
- when you know that your signal isn't going to clip it isn't worth it.
- */
- fistp dword ptr [edi] // pop st(0) into dest, stack: (int)0x7FFFFFFF
-
- add edi, ebx // increment destination ptr
- //lea edi, [edi+ebx]
-
- cmp esi, ecx // has src ptr reached end?
- jne Float32_To_Int32_loop
-
- ffree st(0)
- fincstp
-
- fwait
- fnclex
- fldcw savedFpuControlWord
- }
-}
-
-/* -------------------------------------------------------------------------- */
-
-static void Float32_To_Int32_Clip(
- void *destinationBuffer, signed int destinationStride,
- void *sourceBuffer, signed int sourceStride,
- unsigned int count, PaUtilTriangularDitherGenerator *ditherGenerator )
-{
-/*
- float *src = (float*)sourceBuffer;
- signed long *dest = (signed long*)destinationBuffer;
- (void) ditherGenerator; // unused parameter
-
- while( count-- )
- {
- // REVIEW
- double scaled = *src * 0x7FFFFFFF;
- PA_CLIP_( scaled, -2147483648., 2147483647. );
- *dest = (signed long) scaled;
-
- src += sourceStride;
- dest += destinationStride;
- }
-*/
-
- short savedFpuControlWord;
-
- (void) ditherGenerator; /* unused parameter */
-
- __asm{
- // esi -> source ptr
- // eax -> source byte stride
- // edi -> destination ptr
- // ebx -> destination byte stride
- // ecx -> source end ptr
- // edx -> temp
-
- mov esi, sourceBuffer
-
- mov edx, 4 // sizeof float32 and int32
- mov eax, sourceStride
- imul eax, edx
-
- mov ecx, count
- imul ecx, eax
- add ecx, esi
-
- mov edi, destinationBuffer
-
- mov ebx, destinationStride
- imul ebx, edx
-
- fwait
- fstcw savedFpuControlWord
- fldcw fpuControlWord_
-
- fld int32Scaler_ // stack: (int)0x7FFFFFFF
-
- Float32_To_Int32_Clip_loop:
-
- mov edx, dword ptr [esi] // load floating point value into integer register
-
- and edx, 0x7FFFFFFF // mask off sign
- cmp edx, 0x3F800000 // greater than 1.0 or less than -1.0
-
- jg Float32_To_Int32_Clip_clamp
-
- // load unscaled value into st(0)
- fld dword ptr [esi] // stack: value, (int)0x7FFFFFFF
- add esi, eax // increment source ptr
- //lea esi, [esi+eax]
- fmul st(0), st(1) // st(0) *= st(1), stack: value*0x7FFFFFFF, (int)0x7FFFFFFF
- fistp dword ptr [edi] // pop st(0) into dest, stack: (int)0x7FFFFFFF
- jmp Float32_To_Int32_Clip_stored
-
- Float32_To_Int32_Clip_clamp:
- mov edx, dword ptr [esi] // load floating point value into integer register
- shr edx, 31 // move sign bit into bit 0
- add esi, eax // increment source ptr
- //lea esi, [esi+eax]
- add edx, 0x7FFFFFFF // convert to maximum range integers
- mov dword ptr [edi], edx
-
- Float32_To_Int32_Clip_stored:
-
- //add edi, ebx // increment destination ptr
- lea edi, [edi+ebx]
-
- cmp esi, ecx // has src ptr reached end?
- jne Float32_To_Int32_Clip_loop
-
- ffree st(0)
- fincstp
-
- fwait
- fnclex
- fldcw savedFpuControlWord
- }
-}
-
-/* -------------------------------------------------------------------------- */
-
-static void Float32_To_Int32_DitherClip(
- void *destinationBuffer, signed int destinationStride,
- void *sourceBuffer, signed int sourceStride,
- unsigned int count, PaUtilTriangularDitherGenerator *ditherGenerator )
-{
- /*
- float *src = (float*)sourceBuffer;
- signed long *dest = (signed long*)destinationBuffer;
-
- while( count-- )
- {
- // REVIEW
- double dither = PaUtil_GenerateFloatTriangularDither( ditherGenerator );
- // use smaller scaler to prevent overflow when we add the dither
- double dithered = ((double)*src * (2147483646.0)) + dither;
- PA_CLIP_( dithered, -2147483648., 2147483647. );
- *dest = (signed long) dithered;
-
-
- src += sourceStride;
- dest += destinationStride;
- }
- */
-
- short savedFpuControlWord;
-
- // spill storage:
- signed long sourceByteStride;
- signed long highpassedDither;
-
- // dither state:
- unsigned long ditherPrevious = ditherGenerator->previous;
- unsigned long ditherRandSeed1 = ditherGenerator->randSeed1;
- unsigned long ditherRandSeed2 = ditherGenerator->randSeed2;
-
- __asm{
- // esi -> source ptr
- // eax -> source byte stride
- // edi -> destination ptr
- // ebx -> destination byte stride
- // ecx -> source end ptr
- // edx -> temp
-
- mov esi, sourceBuffer
-
- mov edx, 4 // sizeof float32 and int32
- mov eax, sourceStride
- imul eax, edx
-
- mov ecx, count
- imul ecx, eax
- add ecx, esi
-
- mov edi, destinationBuffer
-
- mov ebx, destinationStride
- imul ebx, edx
-
- fwait
- fstcw savedFpuControlWord
- fldcw fpuControlWord_
-
- fld ditheredInt32Scaler_ // stack: int scaler
-
- Float32_To_Int32_DitherClip_loop:
-
- mov edx, dword ptr [esi] // load floating point value into integer register
-
- and edx, 0x7FFFFFFF // mask off sign
- cmp edx, 0x3F800000 // greater than 1.0 or less than -1.0
-
- jg Float32_To_Int32_DitherClip_clamp
-
- // load unscaled value into st(0)
- fld dword ptr [esi] // stack: value, int scaler
- add esi, eax // increment source ptr
- //lea esi, [esi+eax]
- fmul st(0), st(1) // st(0) *= st(1), stack: value*(int scaler), int scaler
-
- /*
- // call PaUtil_GenerateFloatTriangularDither with C calling convention
- mov sourceByteStride, eax // save eax
- mov sourceEnd, ecx // save ecx
- push ditherGenerator // pass ditherGenerator parameter on stack
- call PaUtil_GenerateFloatTriangularDither // stack: dither, value*(int scaler), int scaler
- pop edx // clear parameter off stack
- mov ecx, sourceEnd // restore ecx
- mov eax, sourceByteStride // restore eax
- */
-
- // generate dither
- mov sourceByteStride, eax // save eax
- mov edx, 196314165
- mov eax, ditherRandSeed1
- mul edx // eax:edx = eax * 196314165
- //add eax, 907633515
- lea eax, [eax+907633515]
- mov ditherRandSeed1, eax
- mov edx, 196314165
- mov eax, ditherRandSeed2
- mul edx // eax:edx = eax * 196314165
- //add eax, 907633515
- lea eax, [eax+907633515]
- mov edx, ditherRandSeed1
- shr edx, PA_DITHER_SHIFT_
- mov ditherRandSeed2, eax
- shr eax, PA_DITHER_SHIFT_
- //add eax, edx // eax -> current
- lea eax, [eax+edx]
- mov edx, ditherPrevious
- neg edx
- lea edx, [eax+edx] // highpass = current - previous
- mov highpassedDither, edx
- mov ditherPrevious, eax // previous = current
- mov eax, sourceByteStride // restore eax
- fild highpassedDither
- fmul const_float_dither_scale_
- // end generate dither, dither signal in st(0)
-
- faddp st(1), st(0) // stack: dither + value*(int scaler), int scaler
- fistp dword ptr [edi] // pop st(0) into dest, stack: int scaler
- jmp Float32_To_Int32_DitherClip_stored
-
- Float32_To_Int32_DitherClip_clamp:
- mov edx, dword ptr [esi] // load floating point value into integer register
- shr edx, 31 // move sign bit into bit 0
- add esi, eax // increment source ptr
- //lea esi, [esi+eax]
- add edx, 0x7FFFFFFF // convert to maximum range integers
- mov dword ptr [edi], edx
-
- Float32_To_Int32_DitherClip_stored:
-
- //add edi, ebx // increment destination ptr
- lea edi, [edi+ebx]
-
- cmp esi, ecx // has src ptr reached end?
- jne Float32_To_Int32_DitherClip_loop
-
- ffree st(0)
- fincstp
-
- fwait
- fnclex
- fldcw savedFpuControlWord
- }
-
- ditherGenerator->previous = ditherPrevious;
- ditherGenerator->randSeed1 = ditherRandSeed1;
- ditherGenerator->randSeed2 = ditherRandSeed2;
-}
-
-/* -------------------------------------------------------------------------- */
-
-static void Float32_To_Int24(
- void *destinationBuffer, signed int destinationStride,
- void *sourceBuffer, signed int sourceStride,
- unsigned int count, PaUtilTriangularDitherGenerator *ditherGenerator )
-{
-/*
- float *src = (float*)sourceBuffer;
- unsigned char *dest = (unsigned char*)destinationBuffer;
- signed long temp;
-
- (void) ditherGenerator; // unused parameter
-
- while( count-- )
- {
- // convert to 32 bit and drop the low 8 bits
- double scaled = *src * 0x7FFFFFFF;
- temp = (signed long) scaled;
-
- dest[0] = (unsigned char)(temp >> 8);
- dest[1] = (unsigned char)(temp >> 16);
- dest[2] = (unsigned char)(temp >> 24);
-
- src += sourceStride;
- dest += destinationStride * 3;
- }
-*/
-
- short savedFpuControlWord;
-
- signed long tempInt32;
-
- (void) ditherGenerator; /* unused parameter */
-
- __asm{
- // esi -> source ptr
- // eax -> source byte stride
- // edi -> destination ptr
- // ebx -> destination byte stride
- // ecx -> source end ptr
- // edx -> temp
-
- mov esi, sourceBuffer
-
- mov edx, 4 // sizeof float32
- mov eax, sourceStride
- imul eax, edx
-
- mov ecx, count
- imul ecx, eax
- add ecx, esi
-
- mov edi, destinationBuffer
-
- mov edx, 3 // sizeof int24
- mov ebx, destinationStride
- imul ebx, edx
-
- fwait
- fstcw savedFpuControlWord
- fldcw fpuControlWord_
-
- fld int24Scaler_ // stack: (int)0x7FFFFF
-
- Float32_To_Int24_loop:
-
- // load unscaled value into st(0)
- fld dword ptr [esi] // stack: value, (int)0x7FFFFF
- add esi, eax // increment source ptr
- //lea esi, [esi+eax]
- fmul st(0), st(1) // st(0) *= st(1), stack: value*0x7FFFFF, (int)0x7FFFFF
- fistp tempInt32 // pop st(0) into tempInt32, stack: (int)0x7FFFFF
- mov edx, tempInt32
-
- mov byte ptr [edi], DL
- shr edx, 8
- //mov byte ptr [edi+1], DL
- //mov byte ptr [edi+2], DH
- mov word ptr [edi+1], DX
-
- //add edi, ebx // increment destination ptr
- lea edi, [edi+ebx]
-
- cmp esi, ecx // has src ptr reached end?
- jne Float32_To_Int24_loop
-
- ffree st(0)
- fincstp
-
- fwait
- fnclex
- fldcw savedFpuControlWord
- }
-}
-
-/* -------------------------------------------------------------------------- */
-
-static void Float32_To_Int24_Clip(
- void *destinationBuffer, signed int destinationStride,
- void *sourceBuffer, signed int sourceStride,
- unsigned int count, PaUtilTriangularDitherGenerator *ditherGenerator )
-{
-/*
- float *src = (float*)sourceBuffer;
- unsigned char *dest = (unsigned char*)destinationBuffer;
- signed long temp;
-
- (void) ditherGenerator; // unused parameter
-
- while( count-- )
- {
- // convert to 32 bit and drop the low 8 bits
- double scaled = *src * 0x7FFFFFFF;
- PA_CLIP_( scaled, -2147483648., 2147483647. );
- temp = (signed long) scaled;
-
- dest[0] = (unsigned char)(temp >> 8);
- dest[1] = (unsigned char)(temp >> 16);
- dest[2] = (unsigned char)(temp >> 24);
-
- src += sourceStride;
- dest += destinationStride * 3;
- }
-*/
-
- short savedFpuControlWord;
-
- signed long tempInt32;
-
- (void) ditherGenerator; /* unused parameter */
-
- __asm{
- // esi -> source ptr
- // eax -> source byte stride
- // edi -> destination ptr
- // ebx -> destination byte stride
- // ecx -> source end ptr
- // edx -> temp
-
- mov esi, sourceBuffer
-
- mov edx, 4 // sizeof float32
- mov eax, sourceStride
- imul eax, edx
-
- mov ecx, count
- imul ecx, eax
- add ecx, esi
-
- mov edi, destinationBuffer
-
- mov edx, 3 // sizeof int24
- mov ebx, destinationStride
- imul ebx, edx
-
- fwait
- fstcw savedFpuControlWord
- fldcw fpuControlWord_
-
- fld int24Scaler_ // stack: (int)0x7FFFFF
-
- Float32_To_Int24_Clip_loop:
-
- mov edx, dword ptr [esi] // load floating point value into integer register
-
- and edx, 0x7FFFFFFF // mask off sign
- cmp edx, 0x3F800000 // greater than 1.0 or less than -1.0
-
- jg Float32_To_Int24_Clip_clamp
-
- // load unscaled value into st(0)
- fld dword ptr [esi] // stack: value, (int)0x7FFFFF
- add esi, eax // increment source ptr
- //lea esi, [esi+eax]
- fmul st(0), st(1) // st(0) *= st(1), stack: value*0x7FFFFF, (int)0x7FFFFF
- fistp tempInt32 // pop st(0) into tempInt32, stack: (int)0x7FFFFF
- mov edx, tempInt32
- jmp Float32_To_Int24_Clip_store
-
- Float32_To_Int24_Clip_clamp:
- mov edx, dword ptr [esi] // load floating point value into integer register
- shr edx, 31 // move sign bit into bit 0
- add esi, eax // increment source ptr
- //lea esi, [esi+eax]
- add edx, 0x7FFFFF // convert to maximum range integers
-
- Float32_To_Int24_Clip_store:
-
- mov byte ptr [edi], DL
- shr edx, 8
- //mov byte ptr [edi+1], DL
- //mov byte ptr [edi+2], DH
- mov word ptr [edi+1], DX
-
- //add edi, ebx // increment destination ptr
- lea edi, [edi+ebx]
-
- cmp esi, ecx // has src ptr reached end?
- jne Float32_To_Int24_Clip_loop
-
- ffree st(0)
- fincstp
-
- fwait
- fnclex
- fldcw savedFpuControlWord
- }
-}
-
-/* -------------------------------------------------------------------------- */
-
-static void Float32_To_Int24_DitherClip(
- void *destinationBuffer, signed int destinationStride,
- void *sourceBuffer, signed int sourceStride,
- unsigned int count, PaUtilTriangularDitherGenerator *ditherGenerator )
-{
-/*
- float *src = (float*)sourceBuffer;
- unsigned char *dest = (unsigned char*)destinationBuffer;
- signed long temp;
-
- while( count-- )
- {
- // convert to 32 bit and drop the low 8 bits
-
- // FIXME: the dither amplitude here appears to be too small by 8 bits
- double dither = PaUtil_GenerateFloatTriangularDither( ditherGenerator );
- // use smaller scaler to prevent overflow when we add the dither
- double dithered = ((double)*src * (2147483646.0)) + dither;
- PA_CLIP_( dithered, -2147483648., 2147483647. );
-
- temp = (signed long) dithered;
-
- dest[0] = (unsigned char)(temp >> 8);
- dest[1] = (unsigned char)(temp >> 16);
- dest[2] = (unsigned char)(temp >> 24);
-
- src += sourceStride;
- dest += destinationStride * 3;
- }
-*/
-
- short savedFpuControlWord;
-
- // spill storage:
- signed long sourceByteStride;
- signed long highpassedDither;
-
- // dither state:
- unsigned long ditherPrevious = ditherGenerator->previous;
- unsigned long ditherRandSeed1 = ditherGenerator->randSeed1;
- unsigned long ditherRandSeed2 = ditherGenerator->randSeed2;
-
- signed long tempInt32;
-
- __asm{
- // esi -> source ptr
- // eax -> source byte stride
- // edi -> destination ptr
- // ebx -> destination byte stride
- // ecx -> source end ptr
- // edx -> temp
-
- mov esi, sourceBuffer
-
- mov edx, 4 // sizeof float32
- mov eax, sourceStride
- imul eax, edx
-
- mov ecx, count
- imul ecx, eax
- add ecx, esi
-
- mov edi, destinationBuffer
-
- mov edx, 3 // sizeof int24
- mov ebx, destinationStride
- imul ebx, edx
-
- fwait
- fstcw savedFpuControlWord
- fldcw fpuControlWord_
-
- fld ditheredInt24Scaler_ // stack: int scaler
-
- Float32_To_Int24_DitherClip_loop:
-
- mov edx, dword ptr [esi] // load floating point value into integer register
-
- and edx, 0x7FFFFFFF // mask off sign
- cmp edx, 0x3F800000 // greater than 1.0 or less than -1.0
-
- jg Float32_To_Int24_DitherClip_clamp
-
- // load unscaled value into st(0)
- fld dword ptr [esi] // stack: value, int scaler
- add esi, eax // increment source ptr
- //lea esi, [esi+eax]
- fmul st(0), st(1) // st(0) *= st(1), stack: value*(int scaler), int scaler
-
- /*
- // call PaUtil_GenerateFloatTriangularDither with C calling convention
- mov sourceByteStride, eax // save eax
- mov sourceEnd, ecx // save ecx
- push ditherGenerator // pass ditherGenerator parameter on stack
- call PaUtil_GenerateFloatTriangularDither // stack: dither, value*(int scaler), int scaler
- pop edx // clear parameter off stack
- mov ecx, sourceEnd // restore ecx
- mov eax, sourceByteStride // restore eax
- */
-
- // generate dither
- mov sourceByteStride, eax // save eax
- mov edx, 196314165
- mov eax, ditherRandSeed1
- mul edx // eax:edx = eax * 196314165
- //add eax, 907633515
- lea eax, [eax+907633515]
- mov ditherRandSeed1, eax
- mov edx, 196314165
- mov eax, ditherRandSeed2
- mul edx // eax:edx = eax * 196314165
- //add eax, 907633515
- lea eax, [eax+907633515]
- mov edx, ditherRandSeed1
- shr edx, PA_DITHER_SHIFT_
- mov ditherRandSeed2, eax
- shr eax, PA_DITHER_SHIFT_
- //add eax, edx // eax -> current
- lea eax, [eax+edx]
- mov edx, ditherPrevious
- neg edx
- lea edx, [eax+edx] // highpass = current - previous
- mov highpassedDither, edx
- mov ditherPrevious, eax // previous = current
- mov eax, sourceByteStride // restore eax
- fild highpassedDither
- fmul const_float_dither_scale_
- // end generate dither, dither signal in st(0)
-
- faddp st(1), st(0) // stack: dither * value*(int scaler), int scaler
- fistp tempInt32 // pop st(0) into tempInt32, stack: int scaler
- mov edx, tempInt32
- jmp Float32_To_Int24_DitherClip_store
-
- Float32_To_Int24_DitherClip_clamp:
- mov edx, dword ptr [esi] // load floating point value into integer register
- shr edx, 31 // move sign bit into bit 0
- add esi, eax // increment source ptr
- //lea esi, [esi+eax]
- add edx, 0x7FFFFF // convert to maximum range integers
-
- Float32_To_Int24_DitherClip_store:
-
- mov byte ptr [edi], DL
- shr edx, 8
- //mov byte ptr [edi+1], DL
- //mov byte ptr [edi+2], DH
- mov word ptr [edi+1], DX
-
- //add edi, ebx // increment destination ptr
- lea edi, [edi+ebx]
-
- cmp esi, ecx // has src ptr reached end?
- jne Float32_To_Int24_DitherClip_loop
-
- ffree st(0)
- fincstp
-
- fwait
- fnclex
- fldcw savedFpuControlWord
- }
-
- ditherGenerator->previous = ditherPrevious;
- ditherGenerator->randSeed1 = ditherRandSeed1;
- ditherGenerator->randSeed2 = ditherRandSeed2;
-}
-
-/* -------------------------------------------------------------------------- */
-
-static void Float32_To_Int16(
- void *destinationBuffer, signed int destinationStride,
- void *sourceBuffer, signed int sourceStride,
- unsigned int count, PaUtilTriangularDitherGenerator *ditherGenerator )
-{
-/*
- float *src = (float*)sourceBuffer;
- signed short *dest = (signed short*)destinationBuffer;
- (void)ditherGenerator; // unused parameter
-
- while( count-- )
- {
-
- short samp = (short) (*src * (32767.0f));
- *dest = samp;
-
- src += sourceStride;
- dest += destinationStride;
- }
-*/
-
- short savedFpuControlWord;
-
- (void) ditherGenerator; /* unused parameter */
-
- __asm{
- // esi -> source ptr
- // eax -> source byte stride
- // edi -> destination ptr
- // ebx -> destination byte stride
- // ecx -> source end ptr
- // edx -> temp
-
- mov esi, sourceBuffer
-
- mov edx, 4 // sizeof float32
- mov eax, sourceStride
- imul eax, edx // source byte stride
-
- mov ecx, count
- imul ecx, eax
- add ecx, esi // source end ptr = count * source byte stride + source ptr
-
- mov edi, destinationBuffer
-
- mov edx, 2 // sizeof int16
- mov ebx, destinationStride
- imul ebx, edx // destination byte stride
-
- fwait
- fstcw savedFpuControlWord
- fldcw fpuControlWord_
-
- fld int16Scaler_ // stack: (int)0x7FFF
-
- Float32_To_Int16_loop:
-
- // load unscaled value into st(0)
- fld dword ptr [esi] // stack: value, (int)0x7FFF
- add esi, eax // increment source ptr
- //lea esi, [esi+eax]
- fmul st(0), st(1) // st(0) *= st(1), stack: value*0x7FFF, (int)0x7FFF
- fistp word ptr [edi] // store scaled int into dest, stack: (int)0x7FFF
-
- add edi, ebx // increment destination ptr
- //lea edi, [edi+ebx]
-
- cmp esi, ecx // has src ptr reached end?
- jne Float32_To_Int16_loop
-
- ffree st(0)
- fincstp
-
- fwait
- fnclex
- fldcw savedFpuControlWord
- }
-}
-
-/* -------------------------------------------------------------------------- */
-
-static void Float32_To_Int16_Clip(
- void *destinationBuffer, signed int destinationStride,
- void *sourceBuffer, signed int sourceStride,
- unsigned int count, PaUtilTriangularDitherGenerator *ditherGenerator )
-{
-/*
- float *src = (float*)sourceBuffer;
- signed short *dest = (signed short*)destinationBuffer;
- (void)ditherGenerator; // unused parameter
-
- while( count-- )
- {
- long samp = (signed long) (*src * (32767.0f));
- PA_CLIP_( samp, -0x8000, 0x7FFF );
- *dest = (signed short) samp;
-
- src += sourceStride;
- dest += destinationStride;
- }
-*/
-
- short savedFpuControlWord;
-
- (void) ditherGenerator; /* unused parameter */
-
- __asm{
- // esi -> source ptr
- // eax -> source byte stride
- // edi -> destination ptr
- // ebx -> destination byte stride
- // ecx -> source end ptr
- // edx -> temp
-
- mov esi, sourceBuffer
-
- mov edx, 4 // sizeof float32
- mov eax, sourceStride
- imul eax, edx // source byte stride
-
- mov ecx, count
- imul ecx, eax
- add ecx, esi // source end ptr = count * source byte stride + source ptr
-
- mov edi, destinationBuffer
-
- mov edx, 2 // sizeof int16
- mov ebx, destinationStride
- imul ebx, edx // destination byte stride
-
- fwait
- fstcw savedFpuControlWord
- fldcw fpuControlWord_
-
- fld int16Scaler_ // stack: (int)0x7FFF
-
- Float32_To_Int16_Clip_loop:
-
- mov edx, dword ptr [esi] // load floating point value into integer register
-
- and edx, 0x7FFFFFFF // mask off sign
- cmp edx, 0x3F800000 // greater than 1.0 or less than -1.0
-
- jg Float32_To_Int16_Clip_clamp
-
- // load unscaled value into st(0)
- fld dword ptr [esi] // stack: value, (int)0x7FFF
- add esi, eax // increment source ptr
- //lea esi, [esi+eax]
- fmul st(0), st(1) // st(0) *= st(1), stack: value*0x7FFF, (int)0x7FFF
- fistp word ptr [edi] // store scaled int into dest, stack: (int)0x7FFF
- jmp Float32_To_Int16_Clip_stored
-
- Float32_To_Int16_Clip_clamp:
- mov edx, dword ptr [esi] // load floating point value into integer register
- shr edx, 31 // move sign bit into bit 0
- add esi, eax // increment source ptr
- //lea esi, [esi+eax]
- add dx, 0x7FFF // convert to maximum range integers
- mov word ptr [edi], dx // store clamped into into dest
-
- Float32_To_Int16_Clip_stored:
-
- add edi, ebx // increment destination ptr
- //lea edi, [edi+ebx]
-
- cmp esi, ecx // has src ptr reached end?
- jne Float32_To_Int16_Clip_loop
-
- ffree st(0)
- fincstp
-
- fwait
- fnclex
- fldcw savedFpuControlWord
- }
-}
-
-/* -------------------------------------------------------------------------- */
-
-static void Float32_To_Int16_DitherClip(
- void *destinationBuffer, signed int destinationStride,
- void *sourceBuffer, signed int sourceStride,
- unsigned int count, PaUtilTriangularDitherGenerator *ditherGenerator )
-{
-/*
- float *src = (float*)sourceBuffer;
- signed short *dest = (signed short*)destinationBuffer;
- (void)ditherGenerator; // unused parameter
-
- while( count-- )
- {
-
- float dither = PaUtil_GenerateFloatTriangularDither( ditherGenerator );
- // use smaller scaler to prevent overflow when we add the dither
- float dithered = (*src * (32766.0f)) + dither;
- signed long samp = (signed long) dithered;
- PA_CLIP_( samp, -0x8000, 0x7FFF );
- *dest = (signed short) samp;
-
- src += sourceStride;
- dest += destinationStride;
- }
-*/
-
- short savedFpuControlWord;
-
- // spill storage:
- signed long sourceByteStride;
- signed long highpassedDither;
-
- // dither state:
- unsigned long ditherPrevious = ditherGenerator->previous;
- unsigned long ditherRandSeed1 = ditherGenerator->randSeed1;
- unsigned long ditherRandSeed2 = ditherGenerator->randSeed2;
-
- __asm{
- // esi -> source ptr
- // eax -> source byte stride
- // edi -> destination ptr
- // ebx -> destination byte stride
- // ecx -> source end ptr
- // edx -> temp
-
- mov esi, sourceBuffer
-
- mov edx, 4 // sizeof float32
- mov eax, sourceStride
- imul eax, edx // source byte stride
-
- mov ecx, count
- imul ecx, eax
- add ecx, esi // source end ptr = count * source byte stride + source ptr
-
- mov edi, destinationBuffer
-
- mov edx, 2 // sizeof int16
- mov ebx, destinationStride
- imul ebx, edx // destination byte stride
-
- fwait
- fstcw savedFpuControlWord
- fldcw fpuControlWord_
-
- fld ditheredInt16Scaler_ // stack: int scaler
-
- Float32_To_Int16_DitherClip_loop:
-
- mov edx, dword ptr [esi] // load floating point value into integer register
-
- and edx, 0x7FFFFFFF // mask off sign
- cmp edx, 0x3F800000 // greater than 1.0 or less than -1.0
-
- jg Float32_To_Int16_DitherClip_clamp
-
- // load unscaled value into st(0)
- fld dword ptr [esi] // stack: value, int scaler
- add esi, eax // increment source ptr
- //lea esi, [esi+eax]
- fmul st(0), st(1) // st(0) *= st(1), stack: value*(int scaler), int scaler
-
- /*
- // call PaUtil_GenerateFloatTriangularDither with C calling convention
- mov sourceByteStride, eax // save eax
- mov sourceEnd, ecx // save ecx
- push ditherGenerator // pass ditherGenerator parameter on stack
- call PaUtil_GenerateFloatTriangularDither // stack: dither, value*(int scaler), int scaler
- pop edx // clear parameter off stack
- mov ecx, sourceEnd // restore ecx
- mov eax, sourceByteStride // restore eax
- */
-
- // generate dither
- mov sourceByteStride, eax // save eax
- mov edx, 196314165
- mov eax, ditherRandSeed1
- mul edx // eax:edx = eax * 196314165
- //add eax, 907633515
- lea eax, [eax+907633515]
- mov ditherRandSeed1, eax
- mov edx, 196314165
- mov eax, ditherRandSeed2
- mul edx // eax:edx = eax * 196314165
- //add eax, 907633515
- lea eax, [eax+907633515]
- mov edx, ditherRandSeed1
- shr edx, PA_DITHER_SHIFT_
- mov ditherRandSeed2, eax
- shr eax, PA_DITHER_SHIFT_
- //add eax, edx // eax -> current
- lea eax, [eax+edx] // current = randSeed1>>x + randSeed2>>x
- mov edx, ditherPrevious
- neg edx
- lea edx, [eax+edx] // highpass = current - previous
- mov highpassedDither, edx
- mov ditherPrevious, eax // previous = current
- mov eax, sourceByteStride // restore eax
- fild highpassedDither
- fmul const_float_dither_scale_
- // end generate dither, dither signal in st(0)
-
- faddp st(1), st(0) // stack: dither * value*(int scaler), int scaler
- fistp word ptr [edi] // store scaled int into dest, stack: int scaler
- jmp Float32_To_Int16_DitherClip_stored
-
- Float32_To_Int16_DitherClip_clamp:
- mov edx, dword ptr [esi] // load floating point value into integer register
- shr edx, 31 // move sign bit into bit 0
- add esi, eax // increment source ptr
- //lea esi, [esi+eax]
- add dx, 0x7FFF // convert to maximum range integers
- mov word ptr [edi], dx // store clamped into into dest
-
- Float32_To_Int16_DitherClip_stored:
-
- add edi, ebx // increment destination ptr
- //lea edi, [edi+ebx]
-
- cmp esi, ecx // has src ptr reached end?
- jne Float32_To_Int16_DitherClip_loop
-
- ffree st(0)
- fincstp
-
- fwait
- fnclex
- fldcw savedFpuControlWord
- }
-
- ditherGenerator->previous = ditherPrevious;
- ditherGenerator->randSeed1 = ditherRandSeed1;
- ditherGenerator->randSeed2 = ditherRandSeed2;
-}
-
-/* -------------------------------------------------------------------------- */
-
-void PaUtil_InitializeX86PlainConverters( void )
-{
- paConverters.Float32_To_Int32 = Float32_To_Int32;
- paConverters.Float32_To_Int32_Clip = Float32_To_Int32_Clip;
- paConverters.Float32_To_Int32_DitherClip = Float32_To_Int32_DitherClip;
-
- paConverters.Float32_To_Int24 = Float32_To_Int24;
- paConverters.Float32_To_Int24_Clip = Float32_To_Int24_Clip;
- paConverters.Float32_To_Int24_DitherClip = Float32_To_Int24_DitherClip;
-
- paConverters.Float32_To_Int16 = Float32_To_Int16;
- paConverters.Float32_To_Int16_Clip = Float32_To_Int16_Clip;
- paConverters.Float32_To_Int16_DitherClip = Float32_To_Int16_DitherClip;
-}
-
-/* -------------------------------------------------------------------------- */
+#include "pa_x86_plain_converters.h"
+
+#include "pa_converters.h"
+#include "pa_dither.h"
+
+/*
+ plain intel assemby versions of standard pa converter functions.
+
+ the main reason these versions are faster than the equivalent C versions
+ is that float -> int casting is expensive in C on x86 because the rounding
+ mode needs to be changed for every cast. these versions only set
+ the rounding mode once outside the loop.
+
+ small additional speed gains are made by the way that clamping is
+ implemented.
+
+TODO:
+ o- inline dither code
+ o- implement Dither only (no-clip) versions
+ o- implement int8 and uint8 versions
+ o- test thouroughly
+
+ o- the packed 24 bit functions could benefit from unrolling and avoiding
+ byte and word sized register access.
+*/
+
+/* -------------------------------------------------------------------------- */
+
+/*
+#define PA_CLIP_( val, min, max )\
+ { val = ((val) < (min)) ? (min) : (((val) > (max)) ? (max) : (val)); }
+*/
+
+/*
+ the following notes were used to determine whether a floating point
+ value should be saturated (ie >1 or <-1) by loading it into an integer
+ register. these should be rewritten so that they make sense.
+
+ an ieee floating point value
+
+ 1.xxxxxxxxxxxxxxxxxxxx?
+
+
+ is less than or equal to 1 and greater than or equal to -1 either:
+
+ if the mantissa is 0 and the unbiased exponent is 0
+
+ OR
+
+ if the unbiased exponent < 0
+
+ this translates to:
+
+ if the mantissa is 0 and the biased exponent is 7F
+
+ or
+
+ if the biased exponent is less than 7F
+
+
+ therefore the value is greater than 1 or less than -1 if
+
+ the mantissa is not 0 and the biased exponent is 7F
+
+ or
+
+ if the biased exponent is greater than 7F
+
+
+ in other words, if we mask out the sign bit, the value is
+ greater than 1 or less than -1 if its integer representation is greater than:
+
+ 0 01111111 0000 0000 0000 0000 0000 000
+
+ 0011 1111 1000 0000 0000 0000 0000 0000 => 0x3F800000
+*/
+
+/* -------------------------------------------------------------------------- */
+
+static const short fpuControlWord_ = 0x033F; /*round to nearest, 64 bit precision, all exceptions masked*/
+static const double int32Scaler_ = 0x7FFFFFFF;
+static const double ditheredInt32Scaler_ = 0x7FFFFFFE;
+static const double int24Scaler_ = 0x7FFFFF;
+static const double ditheredInt24Scaler_ = 0x7FFFFE;
+static const double int16Scaler_ = 0x7FFF;
+static const double ditheredInt16Scaler_ = 0x7FFE;
+
+#define PA_DITHER_BITS_ (15)
+/* Multiply by PA_FLOAT_DITHER_SCALE_ to get a float between -2.0 and +1.99999 */
+#define PA_FLOAT_DITHER_SCALE_ (1.0 / ((1<<PA_DITHER_BITS_)-1))
+static const float const_float_dither_scale_ = (float) PA_FLOAT_DITHER_SCALE_;
+#define PA_DITHER_SHIFT_ ((32 - PA_DITHER_BITS_) + 1)
+
+/* -------------------------------------------------------------------------- */
+
+static void Float32_To_Int32(
+ void *destinationBuffer, signed int destinationStride,
+ void *sourceBuffer, signed int sourceStride,
+ unsigned int count, PaUtilTriangularDitherGenerator *ditherGenerator )
+{
+/*
+ float *src = (float*)sourceBuffer;
+ signed long *dest = (signed long*)destinationBuffer;
+ (void)ditherGenerator; // unused parameter
+
+ while( count-- )
+ {
+ // REVIEW
+ double scaled = *src * 0x7FFFFFFF;
+ *dest = (signed long) scaled;
+
+ src += sourceStride;
+ dest += destinationStride;
+ }
+*/
+
+ short savedFpuControlWord;
+
+ (void) ditherGenerator; /* unused parameter */
+
+
+ __asm{
+ // esi -> source ptr
+ // eax -> source byte stride
+ // edi -> destination ptr
+ // ebx -> destination byte stride
+ // ecx -> source end ptr
+ // edx -> temp
+
+ mov esi, sourceBuffer
+
+ mov edx, 4 // sizeof float32 and int32
+ mov eax, sourceStride
+ imul eax, edx
+
+ mov ecx, count
+ imul ecx, eax
+ add ecx, esi
+
+ mov edi, destinationBuffer
+
+ mov ebx, destinationStride
+ imul ebx, edx
+
+ fwait
+ fstcw savedFpuControlWord
+ fldcw fpuControlWord_
+
+ fld int32Scaler_ // stack: (int)0x7FFFFFFF
+
+ Float32_To_Int32_loop:
+
+ // load unscaled value into st(0)
+ fld dword ptr [esi] // stack: value, (int)0x7FFFFFFF
+ add esi, eax // increment source ptr
+ //lea esi, [esi+eax]
+ fmul st(0), st(1) // st(0) *= st(1), stack: value*0x7FFFFFFF, (int)0x7FFFFFFF
+ /*
+ note: we could store to a temporary qword here which would cause
+ wraparound distortion instead of int indefinite 0x10. that would
+ be more work, and given that not enabling clipping is only advisable
+ when you know that your signal isn't going to clip it isn't worth it.
+ */
+ fistp dword ptr [edi] // pop st(0) into dest, stack: (int)0x7FFFFFFF
+
+ add edi, ebx // increment destination ptr
+ //lea edi, [edi+ebx]
+
+ cmp esi, ecx // has src ptr reached end?
+ jne Float32_To_Int32_loop
+
+ ffree st(0)
+ fincstp
+
+ fwait
+ fnclex
+ fldcw savedFpuControlWord
+ }
+}
+
+/* -------------------------------------------------------------------------- */
+
+static void Float32_To_Int32_Clip(
+ void *destinationBuffer, signed int destinationStride,
+ void *sourceBuffer, signed int sourceStride,
+ unsigned int count, PaUtilTriangularDitherGenerator *ditherGenerator )
+{
+/*
+ float *src = (float*)sourceBuffer;
+ signed long *dest = (signed long*)destinationBuffer;
+ (void) ditherGenerator; // unused parameter
+
+ while( count-- )
+ {
+ // REVIEW
+ double scaled = *src * 0x7FFFFFFF;
+ PA_CLIP_( scaled, -2147483648., 2147483647. );
+ *dest = (signed long) scaled;
+
+ src += sourceStride;
+ dest += destinationStride;
+ }
+*/
+
+ short savedFpuControlWord;
+
+ (void) ditherGenerator; /* unused parameter */
+
+ __asm{
+ // esi -> source ptr
+ // eax -> source byte stride
+ // edi -> destination ptr
+ // ebx -> destination byte stride
+ // ecx -> source end ptr
+ // edx -> temp
+
+ mov esi, sourceBuffer
+
+ mov edx, 4 // sizeof float32 and int32
+ mov eax, sourceStride
+ imul eax, edx
+
+ mov ecx, count
+ imul ecx, eax
+ add ecx, esi
+
+ mov edi, destinationBuffer
+
+ mov ebx, destinationStride
+ imul ebx, edx
+
+ fwait
+ fstcw savedFpuControlWord
+ fldcw fpuControlWord_
+
+ fld int32Scaler_ // stack: (int)0x7FFFFFFF
+
+ Float32_To_Int32_Clip_loop:
+
+ mov edx, dword ptr [esi] // load floating point value into integer register
+
+ and edx, 0x7FFFFFFF // mask off sign
+ cmp edx, 0x3F800000 // greater than 1.0 or less than -1.0
+
+ jg Float32_To_Int32_Clip_clamp
+
+ // load unscaled value into st(0)
+ fld dword ptr [esi] // stack: value, (int)0x7FFFFFFF
+ add esi, eax // increment source ptr
+ //lea esi, [esi+eax]
+ fmul st(0), st(1) // st(0) *= st(1), stack: value*0x7FFFFFFF, (int)0x7FFFFFFF
+ fistp dword ptr [edi] // pop st(0) into dest, stack: (int)0x7FFFFFFF
+ jmp Float32_To_Int32_Clip_stored
+
+ Float32_To_Int32_Clip_clamp:
+ mov edx, dword ptr [esi] // load floating point value into integer register
+ shr edx, 31 // move sign bit into bit 0
+ add esi, eax // increment source ptr
+ //lea esi, [esi+eax]
+ add edx, 0x7FFFFFFF // convert to maximum range integers
+ mov dword ptr [edi], edx
+
+ Float32_To_Int32_Clip_stored:
+
+ //add edi, ebx // increment destination ptr
+ lea edi, [edi+ebx]
+
+ cmp esi, ecx // has src ptr reached end?
+ jne Float32_To_Int32_Clip_loop
+
+ ffree st(0)
+ fincstp
+
+ fwait
+ fnclex
+ fldcw savedFpuControlWord
+ }
+}
+
+/* -------------------------------------------------------------------------- */
+
+static void Float32_To_Int32_DitherClip(
+ void *destinationBuffer, signed int destinationStride,
+ void *sourceBuffer, signed int sourceStride,
+ unsigned int count, PaUtilTriangularDitherGenerator *ditherGenerator )
+{
+ /*
+ float *src = (float*)sourceBuffer;
+ signed long *dest = (signed long*)destinationBuffer;
+
+ while( count-- )
+ {
+ // REVIEW
+ double dither = PaUtil_GenerateFloatTriangularDither( ditherGenerator );
+ // use smaller scaler to prevent overflow when we add the dither
+ double dithered = ((double)*src * (2147483646.0)) + dither;
+ PA_CLIP_( dithered, -2147483648., 2147483647. );
+ *dest = (signed long) dithered;
+
+
+ src += sourceStride;
+ dest += destinationStride;
+ }
+ */
+
+ short savedFpuControlWord;
+
+ // spill storage:
+ signed long sourceByteStride;
+ signed long highpassedDither;
+
+ // dither state:
+ unsigned long ditherPrevious = ditherGenerator->previous;
+ unsigned long ditherRandSeed1 = ditherGenerator->randSeed1;
+ unsigned long ditherRandSeed2 = ditherGenerator->randSeed2;
+
+ __asm{
+ // esi -> source ptr
+ // eax -> source byte stride
+ // edi -> destination ptr
+ // ebx -> destination byte stride
+ // ecx -> source end ptr
+ // edx -> temp
+
+ mov esi, sourceBuffer
+
+ mov edx, 4 // sizeof float32 and int32
+ mov eax, sourceStride
+ imul eax, edx
+
+ mov ecx, count
+ imul ecx, eax
+ add ecx, esi
+
+ mov edi, destinationBuffer
+
+ mov ebx, destinationStride
+ imul ebx, edx
+
+ fwait
+ fstcw savedFpuControlWord
+ fldcw fpuControlWord_
+
+ fld ditheredInt32Scaler_ // stack: int scaler
+
+ Float32_To_Int32_DitherClip_loop:
+
+ mov edx, dword ptr [esi] // load floating point value into integer register
+
+ and edx, 0x7FFFFFFF // mask off sign
+ cmp edx, 0x3F800000 // greater than 1.0 or less than -1.0
+
+ jg Float32_To_Int32_DitherClip_clamp
+
+ // load unscaled value into st(0)
+ fld dword ptr [esi] // stack: value, int scaler
+ add esi, eax // increment source ptr
+ //lea esi, [esi+eax]
+ fmul st(0), st(1) // st(0) *= st(1), stack: value*(int scaler), int scaler
+
+ /*
+ // call PaUtil_GenerateFloatTriangularDither with C calling convention
+ mov sourceByteStride, eax // save eax
+ mov sourceEnd, ecx // save ecx
+ push ditherGenerator // pass ditherGenerator parameter on stack
+ call PaUtil_GenerateFloatTriangularDither // stack: dither, value*(int scaler), int scaler
+ pop edx // clear parameter off stack
+ mov ecx, sourceEnd // restore ecx
+ mov eax, sourceByteStride // restore eax
+ */
+
+ // generate dither
+ mov sourceByteStride, eax // save eax
+ mov edx, 196314165
+ mov eax, ditherRandSeed1
+ mul edx // eax:edx = eax * 196314165
+ //add eax, 907633515
+ lea eax, [eax+907633515]
+ mov ditherRandSeed1, eax
+ mov edx, 196314165
+ mov eax, ditherRandSeed2
+ mul edx // eax:edx = eax * 196314165
+ //add eax, 907633515
+ lea eax, [eax+907633515]
+ mov edx, ditherRandSeed1
+ shr edx, PA_DITHER_SHIFT_
+ mov ditherRandSeed2, eax
+ shr eax, PA_DITHER_SHIFT_
+ //add eax, edx // eax -> current
+ lea eax, [eax+edx]
+ mov edx, ditherPrevious
+ neg edx
+ lea edx, [eax+edx] // highpass = current - previous
+ mov highpassedDither, edx
+ mov ditherPrevious, eax // previous = current
+ mov eax, sourceByteStride // restore eax
+ fild highpassedDither
+ fmul const_float_dither_scale_
+ // end generate dither, dither signal in st(0)
+
+ faddp st(1), st(0) // stack: dither + value*(int scaler), int scaler
+ fistp dword ptr [edi] // pop st(0) into dest, stack: int scaler
+ jmp Float32_To_Int32_DitherClip_stored
+
+ Float32_To_Int32_DitherClip_clamp:
+ mov edx, dword ptr [esi] // load floating point value into integer register
+ shr edx, 31 // move sign bit into bit 0
+ add esi, eax // increment source ptr
+ //lea esi, [esi+eax]
+ add edx, 0x7FFFFFFF // convert to maximum range integers
+ mov dword ptr [edi], edx
+
+ Float32_To_Int32_DitherClip_stored:
+
+ //add edi, ebx // increment destination ptr
+ lea edi, [edi+ebx]
+
+ cmp esi, ecx // has src ptr reached end?
+ jne Float32_To_Int32_DitherClip_loop
+
+ ffree st(0)
+ fincstp
+
+ fwait
+ fnclex
+ fldcw savedFpuControlWord
+ }
+
+ ditherGenerator->previous = ditherPrevious;
+ ditherGenerator->randSeed1 = ditherRandSeed1;
+ ditherGenerator->randSeed2 = ditherRandSeed2;
+}
+
+/* -------------------------------------------------------------------------- */
+
+static void Float32_To_Int24(
+ void *destinationBuffer, signed int destinationStride,
+ void *sourceBuffer, signed int sourceStride,
+ unsigned int count, PaUtilTriangularDitherGenerator *ditherGenerator )
+{
+/*
+ float *src = (float*)sourceBuffer;
+ unsigned char *dest = (unsigned char*)destinationBuffer;
+ signed long temp;
+
+ (void) ditherGenerator; // unused parameter
+
+ while( count-- )
+ {
+ // convert to 32 bit and drop the low 8 bits
+ double scaled = *src * 0x7FFFFFFF;
+ temp = (signed long) scaled;
+
+ dest[0] = (unsigned char)(temp >> 8);
+ dest[1] = (unsigned char)(temp >> 16);
+ dest[2] = (unsigned char)(temp >> 24);
+
+ src += sourceStride;
+ dest += destinationStride * 3;
+ }
+*/
+
+ short savedFpuControlWord;
+
+ signed long tempInt32;
+
+ (void) ditherGenerator; /* unused parameter */
+
+ __asm{
+ // esi -> source ptr
+ // eax -> source byte stride
+ // edi -> destination ptr
+ // ebx -> destination byte stride
+ // ecx -> source end ptr
+ // edx -> temp
+
+ mov esi, sourceBuffer
+
+ mov edx, 4 // sizeof float32
+ mov eax, sourceStride
+ imul eax, edx
+
+ mov ecx, count
+ imul ecx, eax
+ add ecx, esi
+
+ mov edi, destinationBuffer
+
+ mov edx, 3 // sizeof int24
+ mov ebx, destinationStride
+ imul ebx, edx
+
+ fwait
+ fstcw savedFpuControlWord
+ fldcw fpuControlWord_
+
+ fld int24Scaler_ // stack: (int)0x7FFFFF
+
+ Float32_To_Int24_loop:
+
+ // load unscaled value into st(0)
+ fld dword ptr [esi] // stack: value, (int)0x7FFFFF
+ add esi, eax // increment source ptr
+ //lea esi, [esi+eax]
+ fmul st(0), st(1) // st(0) *= st(1), stack: value*0x7FFFFF, (int)0x7FFFFF
+ fistp tempInt32 // pop st(0) into tempInt32, stack: (int)0x7FFFFF
+ mov edx, tempInt32
+
+ mov byte ptr [edi], DL
+ shr edx, 8
+ //mov byte ptr [edi+1], DL
+ //mov byte ptr [edi+2], DH
+ mov word ptr [edi+1], DX
+
+ //add edi, ebx // increment destination ptr
+ lea edi, [edi+ebx]
+
+ cmp esi, ecx // has src ptr reached end?
+ jne Float32_To_Int24_loop
+
+ ffree st(0)
+ fincstp
+
+ fwait
+ fnclex
+ fldcw savedFpuControlWord
+ }
+}
+
+/* -------------------------------------------------------------------------- */
+
+static void Float32_To_Int24_Clip(
+ void *destinationBuffer, signed int destinationStride,
+ void *sourceBuffer, signed int sourceStride,
+ unsigned int count, PaUtilTriangularDitherGenerator *ditherGenerator )
+{
+/*
+ float *src = (float*)sourceBuffer;
+ unsigned char *dest = (unsigned char*)destinationBuffer;
+ signed long temp;
+
+ (void) ditherGenerator; // unused parameter
+
+ while( count-- )
+ {
+ // convert to 32 bit and drop the low 8 bits
+ double scaled = *src * 0x7FFFFFFF;
+ PA_CLIP_( scaled, -2147483648., 2147483647. );
+ temp = (signed long) scaled;
+
+ dest[0] = (unsigned char)(temp >> 8);
+ dest[1] = (unsigned char)(temp >> 16);
+ dest[2] = (unsigned char)(temp >> 24);
+
+ src += sourceStride;
+ dest += destinationStride * 3;
+ }
+*/
+
+ short savedFpuControlWord;
+
+ signed long tempInt32;
+
+ (void) ditherGenerator; /* unused parameter */
+
+ __asm{
+ // esi -> source ptr
+ // eax -> source byte stride
+ // edi -> destination ptr
+ // ebx -> destination byte stride
+ // ecx -> source end ptr
+ // edx -> temp
+
+ mov esi, sourceBuffer
+
+ mov edx, 4 // sizeof float32
+ mov eax, sourceStride
+ imul eax, edx
+
+ mov ecx, count
+ imul ecx, eax
+ add ecx, esi
+
+ mov edi, destinationBuffer
+
+ mov edx, 3 // sizeof int24
+ mov ebx, destinationStride
+ imul ebx, edx
+
+ fwait
+ fstcw savedFpuControlWord
+ fldcw fpuControlWord_
+
+ fld int24Scaler_ // stack: (int)0x7FFFFF
+
+ Float32_To_Int24_Clip_loop:
+
+ mov edx, dword ptr [esi] // load floating point value into integer register
+
+ and edx, 0x7FFFFFFF // mask off sign
+ cmp edx, 0x3F800000 // greater than 1.0 or less than -1.0
+
+ jg Float32_To_Int24_Clip_clamp
+
+ // load unscaled value into st(0)
+ fld dword ptr [esi] // stack: value, (int)0x7FFFFF
+ add esi, eax // increment source ptr
+ //lea esi, [esi+eax]
+ fmul st(0), st(1) // st(0) *= st(1), stack: value*0x7FFFFF, (int)0x7FFFFF
+ fistp tempInt32 // pop st(0) into tempInt32, stack: (int)0x7FFFFF
+ mov edx, tempInt32
+ jmp Float32_To_Int24_Clip_store
+
+ Float32_To_Int24_Clip_clamp:
+ mov edx, dword ptr [esi] // load floating point value into integer register
+ shr edx, 31 // move sign bit into bit 0
+ add esi, eax // increment source ptr
+ //lea esi, [esi+eax]
+ add edx, 0x7FFFFF // convert to maximum range integers
+
+ Float32_To_Int24_Clip_store:
+
+ mov byte ptr [edi], DL
+ shr edx, 8
+ //mov byte ptr [edi+1], DL
+ //mov byte ptr [edi+2], DH
+ mov word ptr [edi+1], DX
+
+ //add edi, ebx // increment destination ptr
+ lea edi, [edi+ebx]
+
+ cmp esi, ecx // has src ptr reached end?
+ jne Float32_To_Int24_Clip_loop
+
+ ffree st(0)
+ fincstp
+
+ fwait
+ fnclex
+ fldcw savedFpuControlWord
+ }
+}
+
+/* -------------------------------------------------------------------------- */
+
+static void Float32_To_Int24_DitherClip(
+ void *destinationBuffer, signed int destinationStride,
+ void *sourceBuffer, signed int sourceStride,
+ unsigned int count, PaUtilTriangularDitherGenerator *ditherGenerator )
+{
+/*
+ float *src = (float*)sourceBuffer;
+ unsigned char *dest = (unsigned char*)destinationBuffer;
+ signed long temp;
+
+ while( count-- )
+ {
+ // convert to 32 bit and drop the low 8 bits
+
+ // FIXME: the dither amplitude here appears to be too small by 8 bits
+ double dither = PaUtil_GenerateFloatTriangularDither( ditherGenerator );
+ // use smaller scaler to prevent overflow when we add the dither
+ double dithered = ((double)*src * (2147483646.0)) + dither;
+ PA_CLIP_( dithered, -2147483648., 2147483647. );
+
+ temp = (signed long) dithered;
+
+ dest[0] = (unsigned char)(temp >> 8);
+ dest[1] = (unsigned char)(temp >> 16);
+ dest[2] = (unsigned char)(temp >> 24);
+
+ src += sourceStride;
+ dest += destinationStride * 3;
+ }
+*/
+
+ short savedFpuControlWord;
+
+ // spill storage:
+ signed long sourceByteStride;
+ signed long highpassedDither;
+
+ // dither state:
+ unsigned long ditherPrevious = ditherGenerator->previous;
+ unsigned long ditherRandSeed1 = ditherGenerator->randSeed1;
+ unsigned long ditherRandSeed2 = ditherGenerator->randSeed2;
+
+ signed long tempInt32;
+
+ __asm{
+ // esi -> source ptr
+ // eax -> source byte stride
+ // edi -> destination ptr
+ // ebx -> destination byte stride
+ // ecx -> source end ptr
+ // edx -> temp
+
+ mov esi, sourceBuffer
+
+ mov edx, 4 // sizeof float32
+ mov eax, sourceStride
+ imul eax, edx
+
+ mov ecx, count
+ imul ecx, eax
+ add ecx, esi
+
+ mov edi, destinationBuffer
+
+ mov edx, 3 // sizeof int24
+ mov ebx, destinationStride
+ imul ebx, edx
+
+ fwait
+ fstcw savedFpuControlWord
+ fldcw fpuControlWord_
+
+ fld ditheredInt24Scaler_ // stack: int scaler
+
+ Float32_To_Int24_DitherClip_loop:
+
+ mov edx, dword ptr [esi] // load floating point value into integer register
+
+ and edx, 0x7FFFFFFF // mask off sign
+ cmp edx, 0x3F800000 // greater than 1.0 or less than -1.0
+
+ jg Float32_To_Int24_DitherClip_clamp
+
+ // load unscaled value into st(0)
+ fld dword ptr [esi] // stack: value, int scaler
+ add esi, eax // increment source ptr
+ //lea esi, [esi+eax]
+ fmul st(0), st(1) // st(0) *= st(1), stack: value*(int scaler), int scaler
+
+ /*
+ // call PaUtil_GenerateFloatTriangularDither with C calling convention
+ mov sourceByteStride, eax // save eax
+ mov sourceEnd, ecx // save ecx
+ push ditherGenerator // pass ditherGenerator parameter on stack
+ call PaUtil_GenerateFloatTriangularDither // stack: dither, value*(int scaler), int scaler
+ pop edx // clear parameter off stack
+ mov ecx, sourceEnd // restore ecx
+ mov eax, sourceByteStride // restore eax
+ */
+
+ // generate dither
+ mov sourceByteStride, eax // save eax
+ mov edx, 196314165
+ mov eax, ditherRandSeed1
+ mul edx // eax:edx = eax * 196314165
+ //add eax, 907633515
+ lea eax, [eax+907633515]
+ mov ditherRandSeed1, eax
+ mov edx, 196314165
+ mov eax, ditherRandSeed2
+ mul edx // eax:edx = eax * 196314165
+ //add eax, 907633515
+ lea eax, [eax+907633515]
+ mov edx, ditherRandSeed1
+ shr edx, PA_DITHER_SHIFT_
+ mov ditherRandSeed2, eax
+ shr eax, PA_DITHER_SHIFT_
+ //add eax, edx // eax -> current
+ lea eax, [eax+edx]
+ mov edx, ditherPrevious
+ neg edx
+ lea edx, [eax+edx] // highpass = current - previous
+ mov highpassedDither, edx
+ mov ditherPrevious, eax // previous = current
+ mov eax, sourceByteStride // restore eax
+ fild highpassedDither
+ fmul const_float_dither_scale_
+ // end generate dither, dither signal in st(0)
+
+ faddp st(1), st(0) // stack: dither * value*(int scaler), int scaler
+ fistp tempInt32 // pop st(0) into tempInt32, stack: int scaler
+ mov edx, tempInt32
+ jmp Float32_To_Int24_DitherClip_store
+
+ Float32_To_Int24_DitherClip_clamp:
+ mov edx, dword ptr [esi] // load floating point value into integer register
+ shr edx, 31 // move sign bit into bit 0
+ add esi, eax // increment source ptr
+ //lea esi, [esi+eax]
+ add edx, 0x7FFFFF // convert to maximum range integers
+
+ Float32_To_Int24_DitherClip_store:
+
+ mov byte ptr [edi], DL
+ shr edx, 8
+ //mov byte ptr [edi+1], DL
+ //mov byte ptr [edi+2], DH
+ mov word ptr [edi+1], DX
+
+ //add edi, ebx // increment destination ptr
+ lea edi, [edi+ebx]
+
+ cmp esi, ecx // has src ptr reached end?
+ jne Float32_To_Int24_DitherClip_loop
+
+ ffree st(0)
+ fincstp
+
+ fwait
+ fnclex
+ fldcw savedFpuControlWord
+ }
+
+ ditherGenerator->previous = ditherPrevious;
+ ditherGenerator->randSeed1 = ditherRandSeed1;
+ ditherGenerator->randSeed2 = ditherRandSeed2;
+}
+
+/* -------------------------------------------------------------------------- */
+
+static void Float32_To_Int16(
+ void *destinationBuffer, signed int destinationStride,
+ void *sourceBuffer, signed int sourceStride,
+ unsigned int count, PaUtilTriangularDitherGenerator *ditherGenerator )
+{
+/*
+ float *src = (float*)sourceBuffer;
+ signed short *dest = (signed short*)destinationBuffer;
+ (void)ditherGenerator; // unused parameter
+
+ while( count-- )
+ {
+
+ short samp = (short) (*src * (32767.0f));
+ *dest = samp;
+
+ src += sourceStride;
+ dest += destinationStride;
+ }
+*/
+
+ short savedFpuControlWord;
+
+ (void) ditherGenerator; /* unused parameter */
+
+ __asm{
+ // esi -> source ptr
+ // eax -> source byte stride
+ // edi -> destination ptr
+ // ebx -> destination byte stride
+ // ecx -> source end ptr
+ // edx -> temp
+
+ mov esi, sourceBuffer
+
+ mov edx, 4 // sizeof float32
+ mov eax, sourceStride
+ imul eax, edx // source byte stride
+
+ mov ecx, count
+ imul ecx, eax
+ add ecx, esi // source end ptr = count * source byte stride + source ptr
+
+ mov edi, destinationBuffer
+
+ mov edx, 2 // sizeof int16
+ mov ebx, destinationStride
+ imul ebx, edx // destination byte stride
+
+ fwait
+ fstcw savedFpuControlWord
+ fldcw fpuControlWord_
+
+ fld int16Scaler_ // stack: (int)0x7FFF
+
+ Float32_To_Int16_loop:
+
+ // load unscaled value into st(0)
+ fld dword ptr [esi] // stack: value, (int)0x7FFF
+ add esi, eax // increment source ptr
+ //lea esi, [esi+eax]
+ fmul st(0), st(1) // st(0) *= st(1), stack: value*0x7FFF, (int)0x7FFF
+ fistp word ptr [edi] // store scaled int into dest, stack: (int)0x7FFF
+
+ add edi, ebx // increment destination ptr
+ //lea edi, [edi+ebx]
+
+ cmp esi, ecx // has src ptr reached end?
+ jne Float32_To_Int16_loop
+
+ ffree st(0)
+ fincstp
+
+ fwait
+ fnclex
+ fldcw savedFpuControlWord
+ }
+}
+
+/* -------------------------------------------------------------------------- */
+
+static void Float32_To_Int16_Clip(
+ void *destinationBuffer, signed int destinationStride,
+ void *sourceBuffer, signed int sourceStride,
+ unsigned int count, PaUtilTriangularDitherGenerator *ditherGenerator )
+{
+/*
+ float *src = (float*)sourceBuffer;
+ signed short *dest = (signed short*)destinationBuffer;
+ (void)ditherGenerator; // unused parameter
+
+ while( count-- )
+ {
+ long samp = (signed long) (*src * (32767.0f));
+ PA_CLIP_( samp, -0x8000, 0x7FFF );
+ *dest = (signed short) samp;
+
+ src += sourceStride;
+ dest += destinationStride;
+ }
+*/
+
+ short savedFpuControlWord;
+
+ (void) ditherGenerator; /* unused parameter */
+
+ __asm{
+ // esi -> source ptr
+ // eax -> source byte stride
+ // edi -> destination ptr
+ // ebx -> destination byte stride
+ // ecx -> source end ptr
+ // edx -> temp
+
+ mov esi, sourceBuffer
+
+ mov edx, 4 // sizeof float32
+ mov eax, sourceStride
+ imul eax, edx // source byte stride
+
+ mov ecx, count
+ imul ecx, eax
+ add ecx, esi // source end ptr = count * source byte stride + source ptr
+
+ mov edi, destinationBuffer
+
+ mov edx, 2 // sizeof int16
+ mov ebx, destinationStride
+ imul ebx, edx // destination byte stride
+
+ fwait
+ fstcw savedFpuControlWord
+ fldcw fpuControlWord_
+
+ fld int16Scaler_ // stack: (int)0x7FFF
+
+ Float32_To_Int16_Clip_loop:
+
+ mov edx, dword ptr [esi] // load floating point value into integer register
+
+ and edx, 0x7FFFFFFF // mask off sign
+ cmp edx, 0x3F800000 // greater than 1.0 or less than -1.0
+
+ jg Float32_To_Int16_Clip_clamp
+
+ // load unscaled value into st(0)
+ fld dword ptr [esi] // stack: value, (int)0x7FFF
+ add esi, eax // increment source ptr
+ //lea esi, [esi+eax]
+ fmul st(0), st(1) // st(0) *= st(1), stack: value*0x7FFF, (int)0x7FFF
+ fistp word ptr [edi] // store scaled int into dest, stack: (int)0x7FFF
+ jmp Float32_To_Int16_Clip_stored
+
+ Float32_To_Int16_Clip_clamp:
+ mov edx, dword ptr [esi] // load floating point value into integer register
+ shr edx, 31 // move sign bit into bit 0
+ add esi, eax // increment source ptr
+ //lea esi, [esi+eax]
+ add dx, 0x7FFF // convert to maximum range integers
+ mov word ptr [edi], dx // store clamped into into dest
+
+ Float32_To_Int16_Clip_stored:
+
+ add edi, ebx // increment destination ptr
+ //lea edi, [edi+ebx]
+
+ cmp esi, ecx // has src ptr reached end?
+ jne Float32_To_Int16_Clip_loop
+
+ ffree st(0)
+ fincstp
+
+ fwait
+ fnclex
+ fldcw savedFpuControlWord
+ }
+}
+
+/* -------------------------------------------------------------------------- */
+
+static void Float32_To_Int16_DitherClip(
+ void *destinationBuffer, signed int destinationStride,
+ void *sourceBuffer, signed int sourceStride,
+ unsigned int count, PaUtilTriangularDitherGenerator *ditherGenerator )
+{
+/*
+ float *src = (float*)sourceBuffer;
+ signed short *dest = (signed short*)destinationBuffer;
+ (void)ditherGenerator; // unused parameter
+
+ while( count-- )
+ {
+
+ float dither = PaUtil_GenerateFloatTriangularDither( ditherGenerator );
+ // use smaller scaler to prevent overflow when we add the dither
+ float dithered = (*src * (32766.0f)) + dither;
+ signed long samp = (signed long) dithered;
+ PA_CLIP_( samp, -0x8000, 0x7FFF );
+ *dest = (signed short) samp;
+
+ src += sourceStride;
+ dest += destinationStride;
+ }
+*/
+
+ short savedFpuControlWord;
+
+ // spill storage:
+ signed long sourceByteStride;
+ signed long highpassedDither;
+
+ // dither state:
+ unsigned long ditherPrevious = ditherGenerator->previous;
+ unsigned long ditherRandSeed1 = ditherGenerator->randSeed1;
+ unsigned long ditherRandSeed2 = ditherGenerator->randSeed2;
+
+ __asm{
+ // esi -> source ptr
+ // eax -> source byte stride
+ // edi -> destination ptr
+ // ebx -> destination byte stride
+ // ecx -> source end ptr
+ // edx -> temp
+
+ mov esi, sourceBuffer
+
+ mov edx, 4 // sizeof float32
+ mov eax, sourceStride
+ imul eax, edx // source byte stride
+
+ mov ecx, count
+ imul ecx, eax
+ add ecx, esi // source end ptr = count * source byte stride + source ptr
+
+ mov edi, destinationBuffer
+
+ mov edx, 2 // sizeof int16
+ mov ebx, destinationStride
+ imul ebx, edx // destination byte stride
+
+ fwait
+ fstcw savedFpuControlWord
+ fldcw fpuControlWord_
+
+ fld ditheredInt16Scaler_ // stack: int scaler
+
+ Float32_To_Int16_DitherClip_loop:
+
+ mov edx, dword ptr [esi] // load floating point value into integer register
+
+ and edx, 0x7FFFFFFF // mask off sign
+ cmp edx, 0x3F800000 // greater than 1.0 or less than -1.0
+
+ jg Float32_To_Int16_DitherClip_clamp
+
+ // load unscaled value into st(0)
+ fld dword ptr [esi] // stack: value, int scaler
+ add esi, eax // increment source ptr
+ //lea esi, [esi+eax]
+ fmul st(0), st(1) // st(0) *= st(1), stack: value*(int scaler), int scaler
+
+ /*
+ // call PaUtil_GenerateFloatTriangularDither with C calling convention
+ mov sourceByteStride, eax // save eax
+ mov sourceEnd, ecx // save ecx
+ push ditherGenerator // pass ditherGenerator parameter on stack
+ call PaUtil_GenerateFloatTriangularDither // stack: dither, value*(int scaler), int scaler
+ pop edx // clear parameter off stack
+ mov ecx, sourceEnd // restore ecx
+ mov eax, sourceByteStride // restore eax
+ */
+
+ // generate dither
+ mov sourceByteStride, eax // save eax
+ mov edx, 196314165
+ mov eax, ditherRandSeed1
+ mul edx // eax:edx = eax * 196314165
+ //add eax, 907633515
+ lea eax, [eax+907633515]
+ mov ditherRandSeed1, eax
+ mov edx, 196314165
+ mov eax, ditherRandSeed2
+ mul edx // eax:edx = eax * 196314165
+ //add eax, 907633515
+ lea eax, [eax+907633515]
+ mov edx, ditherRandSeed1
+ shr edx, PA_DITHER_SHIFT_
+ mov ditherRandSeed2, eax
+ shr eax, PA_DITHER_SHIFT_
+ //add eax, edx // eax -> current
+ lea eax, [eax+edx] // current = randSeed1>>x + randSeed2>>x
+ mov edx, ditherPrevious
+ neg edx
+ lea edx, [eax+edx] // highpass = current - previous
+ mov highpassedDither, edx
+ mov ditherPrevious, eax // previous = current
+ mov eax, sourceByteStride // restore eax
+ fild highpassedDither
+ fmul const_float_dither_scale_
+ // end generate dither, dither signal in st(0)
+
+ faddp st(1), st(0) // stack: dither * value*(int scaler), int scaler
+ fistp word ptr [edi] // store scaled int into dest, stack: int scaler
+ jmp Float32_To_Int16_DitherClip_stored
+
+ Float32_To_Int16_DitherClip_clamp:
+ mov edx, dword ptr [esi] // load floating point value into integer register
+ shr edx, 31 // move sign bit into bit 0
+ add esi, eax // increment source ptr
+ //lea esi, [esi+eax]
+ add dx, 0x7FFF // convert to maximum range integers
+ mov word ptr [edi], dx // store clamped into into dest
+
+ Float32_To_Int16_DitherClip_stored:
+
+ add edi, ebx // increment destination ptr
+ //lea edi, [edi+ebx]
+
+ cmp esi, ecx // has src ptr reached end?
+ jne Float32_To_Int16_DitherClip_loop
+
+ ffree st(0)
+ fincstp
+
+ fwait
+ fnclex
+ fldcw savedFpuControlWord
+ }
+
+ ditherGenerator->previous = ditherPrevious;
+ ditherGenerator->randSeed1 = ditherRandSeed1;
+ ditherGenerator->randSeed2 = ditherRandSeed2;
+}
+
+/* -------------------------------------------------------------------------- */
+
+void PaUtil_InitializeX86PlainConverters( void )
+{
+ paConverters.Float32_To_Int32 = Float32_To_Int32;
+ paConverters.Float32_To_Int32_Clip = Float32_To_Int32_Clip;
+ paConverters.Float32_To_Int32_DitherClip = Float32_To_Int32_DitherClip;
+
+ paConverters.Float32_To_Int24 = Float32_To_Int24;
+ paConverters.Float32_To_Int24_Clip = Float32_To_Int24_Clip;
+ paConverters.Float32_To_Int24_DitherClip = Float32_To_Int24_DitherClip;
+
+ paConverters.Float32_To_Int16 = Float32_To_Int16;
+ paConverters.Float32_To_Int16_Clip = Float32_To_Int16_Clip;
+ paConverters.Float32_To_Int16_DitherClip = Float32_To_Int16_DitherClip;
+}
+
+/* -------------------------------------------------------------------------- */
diff --git a/pjmedia/src/pjmedia/portaudio/pa_x86_plain_converters.h b/pjmedia/src/pjmedia/portaudio/pa_x86_plain_converters.h
index 36959a23..f56c710f 100644
--- a/pjmedia/src/pjmedia/portaudio/pa_x86_plain_converters.h
+++ b/pjmedia/src/pjmedia/portaudio/pa_x86_plain_converters.h
@@ -1,19 +1,19 @@
-#ifndef PA_X86_PLAIN_CONVERTERS_H
-#define PA_X86_PLAIN_CONVERTERS_H
-
-#ifdef __cplusplus
-extern "C"
-{
-#endif /* __cplusplus */
-
-
-/**
- @brief Install optimised converter functions suitable for all IA32 processors
-*/
-void PaUtil_InitializeX86PlainConverters( void );
-
-
-#ifdef __cplusplus
-}
-#endif /* __cplusplus */
-#endif /* PA_X86_PLAIN_CONVERTERS_H */
+#ifndef PA_X86_PLAIN_CONVERTERS_H
+#define PA_X86_PLAIN_CONVERTERS_H
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif /* __cplusplus */
+
+
+/**
+ @brief Install optimised converter functions suitable for all IA32 processors
+*/
+void PaUtil_InitializeX86PlainConverters( void );
+
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+#endif /* PA_X86_PLAIN_CONVERTERS_H */
diff --git a/pjmedia/src/pjmedia/portaudio/portaudio.h b/pjmedia/src/pjmedia/portaudio/portaudio.h
index 8f934fd4..5d4a97ef 100644
--- a/pjmedia/src/pjmedia/portaudio/portaudio.h
+++ b/pjmedia/src/pjmedia/portaudio/portaudio.h
@@ -1,1122 +1,1122 @@
-#ifndef PORTAUDIO_H
-#define PORTAUDIO_H
-/*
- * $Id: portaudio.h,v 1.5.2.50 2004/12/13 11:50:40 rossbencina Exp $
- * PortAudio Portable Real-Time Audio Library
- * PortAudio API Header File
- * Latest version available at: http://www.portaudio.com/
- *
- * Copyright (c) 1999-2002 Ross Bencina and Phil Burk
- *
- * 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.
- *
- * Any person wishing to distribute modifications to the Software is
- * requested to send the modifications to the original developer so that
- * they can be incorporated into the canonical version.
- *
- * 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.
- */
-
-/** @file
- @brief The PortAudio API.
-*/
-
-
-#ifdef __cplusplus
-extern "C"
-{
-#endif /* __cplusplus */
-
-
-/** Retrieve the release number of the currently running PortAudio build,
- eg 1900.
-*/
-int Pa_GetVersion( void );
-
-
-/** Retrieve a textual description of the current PortAudio build,
- eg "PortAudio V19-devel 13 October 2002".
-*/
-const char* Pa_GetVersionText( void );
-
-
-/** Error codes returned by PortAudio functions.
- Note that with the exception of paNoError, all PaErrorCodes are negative.
-*/
-
-typedef int PaError;
-typedef enum PaErrorCode
-{
- paNoError = 0,
-
- paNotInitialized = -10000,
- paUnanticipatedHostError,
- paInvalidChannelCount,
- paInvalidSampleRate,
- paInvalidDevice,
- paInvalidFlag,
- paSampleFormatNotSupported,
- paBadIODeviceCombination,
- paInsufficientMemory,
- paBufferTooBig,
- paBufferTooSmall,
- paNullCallback,
- paBadStreamPtr,
- paTimedOut,
- paInternalError,
- paDeviceUnavailable,
- paIncompatibleHostApiSpecificStreamInfo,
- paStreamIsStopped,
- paStreamIsNotStopped,
- paInputOverflowed,
- paOutputUnderflowed,
- paHostApiNotFound,
- paInvalidHostApi,
- paCanNotReadFromACallbackStream, /**< @todo review error code name */
- paCanNotWriteToACallbackStream, /**< @todo review error code name */
- paCanNotReadFromAnOutputOnlyStream, /**< @todo review error code name */
- paCanNotWriteToAnInputOnlyStream, /**< @todo review error code name */
- paIncompatibleStreamHostApi
-} PaErrorCode;
-
-
-/** Translate the supplied PortAudio error code into a human readable
- message.
-*/
-const char *Pa_GetErrorText( PaError errorCode );
-
-
-/** Library initialization function - call this before using PortAudio.
- This function initialises internal data structures and prepares underlying
- host APIs for use. This function MUST be called before using any other
- PortAudio API functions.
-
- If Pa_Initialize() is called multiple times, each successful
- call must be matched with a corresponding call to Pa_Terminate().
- Pairs of calls to Pa_Initialize()/Pa_Terminate() may overlap, and are not
- required to be fully nested.
-
- Note that if Pa_Initialize() returns an error code, Pa_Terminate() should
- NOT be called.
-
- @return paNoError if successful, otherwise an error code indicating the cause
- of failure.
-
- @see Pa_Terminate
-*/
-PaError Pa_Initialize( void );
-
-
-/** Library termination function - call this when finished using PortAudio.
- This function deallocates all resources allocated by PortAudio since it was
- initializied by a call to Pa_Initialize(). In cases where Pa_Initialise() has
- been called multiple times, each call must be matched with a corresponding call
- to Pa_Terminate(). The final matching call to Pa_Terminate() will automatically
- close any PortAudio streams that are still open.
-
- Pa_Terminate() MUST be called before exiting a program which uses PortAudio.
- Failure to do so may result in serious resource leaks, such as audio devices
- not being available until the next reboot.
-
- @return paNoError if successful, otherwise an error code indicating the cause
- of failure.
-
- @see Pa_Initialize
-*/
-PaError Pa_Terminate( void );
-
-
-
-/** The type used to refer to audio devices. Values of this type usually
- range from 0 to (Pa_DeviceCount-1), and may also take on the PaNoDevice
- and paUseHostApiSpecificDeviceSpecification values.
-
- @see Pa_DeviceCount, paNoDevice, paUseHostApiSpecificDeviceSpecification
-*/
-typedef int PaDeviceIndex;
-
-
-/** A special PaDeviceIndex value indicating that no device is available,
- or should be used.
-
- @see PaDeviceIndex
-*/
-#define paNoDevice ((PaDeviceIndex)-1)
-
-
-/** A special PaDeviceIndex value indicating that the device(s) to be used
- are specified in the host api specific stream info structure.
-
- @see PaDeviceIndex
-*/
-#define paUseHostApiSpecificDeviceSpecification ((PaDeviceIndex)-2)
-
-
-/* Host API enumeration mechanism */
-
-/** The type used to enumerate to host APIs at runtime. Values of this type
- range from 0 to (Pa_GetHostApiCount()-1).
-
- @see Pa_GetHostApiCount
-*/
-typedef int PaHostApiIndex;
-
-
-/** Retrieve the number of available host APIs. Even if a host API is
- available it may have no devices available.
-
- @return A non-negative value indicating the number of available host APIs
- or, a PaErrorCode (which are always negative) if PortAudio is not initialized
- or an error is encountered.
-
- @see PaHostApiIndex
-*/
-PaHostApiIndex Pa_GetHostApiCount( void );
-
-
-/** Retrieve the index of the default host API. The default host API will be
- the lowest common denominator host API on the current platform and is
- unlikely to provide the best performance.
-
- @return A non-negative value ranging from 0 to (Pa_GetHostApiCount()-1)
- indicating the default host API index or, a PaErrorCode (which are always
- negative) if PortAudio is not initialized or an error is encountered.
-*/
-PaHostApiIndex Pa_GetDefaultHostApi( void );
-
-
-/** Unchanging unique identifiers for each supported host API. This type
- is used in the PaHostApiInfo structure. The values are guaranteed to be
- unique and to never change, thus allowing code to be written that
- conditionally uses host API specific extensions.
-
- New type ids will be allocated when support for a host API reaches
- "public alpha" status, prior to that developers should use the
- paInDevelopment type id.
-
- @see PaHostApiInfo
-*/
-typedef enum PaHostApiTypeId
-{
- paInDevelopment=0, /* use while developing support for a new host API */
- paDirectSound=1,
- paMME=2,
- paASIO=3,
- paSoundManager=4,
- paCoreAudio=5,
- paOSS=7,
- paALSA=8,
- paAL=9,
- paBeOS=10,
- paWDMKS=11,
- paJACK=12
-} PaHostApiTypeId;
-
-
-/** A structure containing information about a particular host API. */
-
-typedef struct PaHostApiInfo
-{
- /** this is struct version 1 */
- int structVersion;
- /** The well known unique identifier of this host API @see PaHostApiTypeId */
- PaHostApiTypeId type;
- /** A textual description of the host API for display on user interfaces. */
- const char *name;
-
- /** The number of devices belonging to this host API. This field may be
- used in conjunction with Pa_HostApiDeviceIndexToDeviceIndex() to enumerate
- all devices for this host API.
- @see Pa_HostApiDeviceIndexToDeviceIndex
- */
- int deviceCount;
-
- /** The the default input device for this host API. The value will be a
- device index ranging from 0 to (Pa_GetDeviceCount()-1), or paNoDevice
- if no default input device is available.
- */
- PaDeviceIndex defaultInputDevice;
-
- /** The the default output device for this host API. The value will be a
- device index ranging from 0 to (Pa_GetDeviceCount()-1), or paNoDevice
- if no default output device is available.
- */
- PaDeviceIndex defaultOutputDevice;
-
-} PaHostApiInfo;
-
-
-/** Retrieve a pointer to a structure containing information about a specific
- host Api.
-
- @param hostApi A valid host API index ranging from 0 to (Pa_GetHostApiCount()-1)
-
- @return A pointer to an immutable PaHostApiInfo structure describing
- a specific host API. If the hostApi parameter is out of range or an error
- is encountered, the function returns NULL.
-
- The returned structure is owned by the PortAudio implementation and must not
- be manipulated or freed. The pointer is only guaranteed to be valid between
- calls to Pa_Initialize() and Pa_Terminate().
-*/
-const PaHostApiInfo * Pa_GetHostApiInfo( PaHostApiIndex hostApi );
-
-
-/** Convert a static host API unique identifier, into a runtime
- host API index.
-
- @param type A unique host API identifier belonging to the PaHostApiTypeId
- enumeration.
-
- @return A valid PaHostApiIndex ranging from 0 to (Pa_GetHostApiCount()-1) or,
- a PaErrorCode (which are always negative) if PortAudio is not initialized
- or an error is encountered.
-
- The paHostApiNotFound error code indicates that the host API specified by the
- type parameter is not available.
-
- @see PaHostApiTypeId
-*/
-PaHostApiIndex Pa_HostApiTypeIdToHostApiIndex( PaHostApiTypeId type );
-
-
-/** Convert a host-API-specific device index to standard PortAudio device index.
- This function may be used in conjunction with the deviceCount field of
- PaHostApiInfo to enumerate all devices for the specified host API.
-
- @param hostApi A valid host API index ranging from 0 to (Pa_GetHostApiCount()-1)
-
- @param hostApiDeviceIndex A valid per-host device index in the range
- 0 to (Pa_GetHostApiInfo(hostApi)->deviceCount-1)
-
- @return A non-negative PaDeviceIndex ranging from 0 to (Pa_GetDeviceCount()-1)
- or, a PaErrorCode (which are always negative) if PortAudio is not initialized
- or an error is encountered.
-
- A paInvalidHostApi error code indicates that the host API index specified by
- the hostApi parameter is out of range.
-
- A paInvalidDevice error code indicates that the hostApiDeviceIndex parameter
- is out of range.
-
- @see PaHostApiInfo
-*/
-PaDeviceIndex Pa_HostApiDeviceIndexToDeviceIndex( PaHostApiIndex hostApi,
- int hostApiDeviceIndex );
-
-
-
-/** Structure used to return information about a host error condition.
-*/
-typedef struct PaHostErrorInfo{
- PaHostApiTypeId hostApiType; /**< the host API which returned the error code */
- long errorCode; /**< the error code returned */
- const char *errorText; /**< a textual description of the error if available, otherwise a zero-length string */
-}PaHostErrorInfo;
-
-
-/** Return information about the last host error encountered. The error
- information returned by Pa_GetLastHostErrorInfo() will never be modified
- asyncronously by errors occurring in other PortAudio owned threads
- (such as the thread that manages the stream callback.)
-
- This function is provided as a last resort, primarily to enhance debugging
- by providing clients with access to all available error information.
-
- @return A pointer to an immutable structure constaining information about
- the host error. The values in this structure will only be valid if a
- PortAudio function has previously returned the paUnanticipatedHostError
- error code.
-*/
-const PaHostErrorInfo* Pa_GetLastHostErrorInfo( void );
-
-
-
-/* Device enumeration and capabilities */
-
-/** Retrieve the number of available devices. The number of available devices
- may be zero.
-
- @return A non-negative value indicating the number of available devices or,
- a PaErrorCode (which are always negative) if PortAudio is not initialized
- or an error is encountered.
-*/
-PaDeviceIndex Pa_GetDeviceCount( void );
-
-
-/** Retrieve the index of the default input device. The result can be
- used in the inputDevice parameter to Pa_OpenStream().
-
- @return The default input device index for the default host API, or paNoDevice
- if no default input device is available or an error was encountered.
-*/
-PaDeviceIndex Pa_GetDefaultInputDevice( void );
-
-
-/** Retrieve the index of the default output device. The result can be
- used in the outputDevice parameter to Pa_OpenStream().
-
- @return The default output device index for the defualt host API, or paNoDevice
- if no default output device is available or an error was encountered.
-
- @note
- On the PC, the user can specify a default device by
- setting an environment variable. For example, to use device #1.
-<pre>
- set PA_RECOMMENDED_OUTPUT_DEVICE=1
-</pre>
- The user should first determine the available device ids by using
- the supplied application "pa_devs".
-*/
-PaDeviceIndex Pa_GetDefaultOutputDevice( void );
-
-
-/** The type used to represent monotonic time in seconds that can be used
- for syncronisation. The type is used for the outTime argument to the
- PaStreamCallback and as the result of Pa_GetStreamTime().
-
- @see PaStreamCallback, Pa_GetStreamTime
-*/
-typedef double PaTime;
-
-
-/** A type used to specify one or more sample formats. Each value indicates
- a possible format for sound data passed to and from the stream callback,
- Pa_ReadStream and Pa_WriteStream.
-
- The standard formats paFloat32, paInt16, paInt32, paInt24, paInt8
- and aUInt8 are usually implemented by all implementations.
-
- The floating point representation (paFloat32) uses +1.0 and -1.0 as the
- maximum and minimum respectively.
-
- paUInt8 is an unsigned 8 bit format where 128 is considered "ground"
-
- The paNonInterleaved flag indicates that a multichannel buffer is passed
- as a set of non-interleaved pointers.
-
- @see Pa_OpenStream, Pa_OpenDefaultStream, PaDeviceInfo
- @see paFloat32, paInt16, paInt32, paInt24, paInt8
- @see paUInt8, paCustomFormat, paNonInterleaved
-*/
-typedef unsigned long PaSampleFormat;
-
-
-#define paFloat32 ((PaSampleFormat) 0x00000001) /**< @see PaSampleFormat */
-#define paInt32 ((PaSampleFormat) 0x00000002) /**< @see PaSampleFormat */
-#define paInt24 ((PaSampleFormat) 0x00000004) /**< Packed 24 bit format. @see PaSampleFormat */
-#define paInt16 ((PaSampleFormat) 0x00000008) /**< @see PaSampleFormat */
-#define paInt8 ((PaSampleFormat) 0x00000010) /**< @see PaSampleFormat */
-#define paUInt8 ((PaSampleFormat) 0x00000020) /**< @see PaSampleFormat */
-#define paCustomFormat ((PaSampleFormat) 0x00010000)/**< @see PaSampleFormat */
-
-#define paNonInterleaved ((PaSampleFormat) 0x80000000)
-
-/** A structure providing information and capabilities of PortAudio devices.
- Devices may support input, output or both input and output.
-*/
-typedef struct PaDeviceInfo
-{
- int structVersion; /* this is struct version 2 */
- const char *name;
- PaHostApiIndex hostApi; /* note this is a host API index, not a type id*/
-
- int maxInputChannels;
- int maxOutputChannels;
-
- /* Default latency values for interactive performance. */
- PaTime defaultLowInputLatency;
- PaTime defaultLowOutputLatency;
- /* Default latency values for robust non-interactive applications (eg. playing sound files). */
- PaTime defaultHighInputLatency;
- PaTime defaultHighOutputLatency;
-
- double defaultSampleRate;
-} PaDeviceInfo;
-
-
-/** Retrieve a pointer to a PaDeviceInfo structure containing information
- about the specified device.
- @return A pointer to an immutable PaDeviceInfo structure. If the device
- parameter is out of range the function returns NULL.
-
- @param device A valid device index in the range 0 to (Pa_GetDeviceCount()-1)
-
- @note PortAudio manages the memory referenced by the returned pointer,
- the client must not manipulate or free the memory. The pointer is only
- guaranteed to be valid between calls to Pa_Initialize() and Pa_Terminate().
-
- @see PaDeviceInfo, PaDeviceIndex
-*/
-const PaDeviceInfo* Pa_GetDeviceInfo( PaDeviceIndex device );
-
-
-/** Parameters for one direction (input or output) of a stream.
-*/
-typedef struct PaStreamParameters
-{
- /** A valid device index in the range 0 to (Pa_GetDeviceCount()-1)
- specifying the device to be used or the special constant
- paUseHostApiSpecificDeviceSpecification which indicates that the actual
- device(s) to use are specified in hostApiSpecificStreamInfo.
- This field must not be set to paNoDevice.
- */
- PaDeviceIndex device;
-
- /** The number of channels of sound to be delivered to the
- stream callback or accessed by Pa_ReadStream() or Pa_WriteStream().
- It can range from 1 to the value of maxInputChannels in the
- PaDeviceInfo record for the device specified by the device parameter.
- */
- int channelCount;
-
- /** The sample format of the buffer provided to the stream callback,
- a_ReadStream() or Pa_WriteStream(). It may be any of the formats described
- by the PaSampleFormat enumeration.
- */
- PaSampleFormat sampleFormat;
-
- /** The desired latency in seconds. Where practical, implementations should
- configure their latency based on these parameters, otherwise they may
- choose the closest viable latency instead. Unless the suggested latency
- is greater than the absolute upper limit for the device implementations
- shouldround the suggestedLatency up to the next practial value - ie to
- provide an equal or higher latency than suggestedLatency whereever possibe.
- Actual latency values for an open stream may be retrieved using the
- inputLatency and outputLatency fields of the PaStreamInfo structure
- returned by Pa_GetStreamInfo().
- @see default*Latency in PaDeviceInfo, *Latency in PaStreamInfo
- */
- PaTime suggestedLatency;
-
- /** An optional pointer to a host api specific data structure
- containing additional information for device setup and/or stream processing.
- hostApiSpecificStreamInfo is never required for correct operation,
- if not used it should be set to NULL.
- */
- void *hostApiSpecificStreamInfo;
-
-} PaStreamParameters;
-
-
-/** Return code for Pa_IsFormatSupported indicating success. */
-#define paFormatIsSupported (0)
-
-/** Determine whether it would be possible to open a stream with the specified
- parameters.
-
- @param inputParameters A structure that describes the input parameters used to
- open a stream. The suggestedLatency field is ignored. See PaStreamParameters
- for a description of these parameters. inputParameters must be NULL for
- output-only streams.
-
- @param outputParameters A structure that describes the output parameters used
- to open a stream. The suggestedLatency field is ignored. See PaStreamParameters
- for a description of these parameters. outputParameters must be NULL for
- input-only streams.
-
- @param sampleRate The required sampleRate. For full-duplex streams it is the
- sample rate for both input and output
-
- @return Returns 0 if the format is supported, and an error code indicating why
- the format is not supported otherwise. The constant paFormatIsSupported is
- provided to compare with the return value for success.
-
- @see paFormatIsSupported, PaStreamParameters
-*/
-PaError Pa_IsFormatSupported( const PaStreamParameters *inputParameters,
- const PaStreamParameters *outputParameters,
- double sampleRate );
-
-
-
-/* Streaming types and functions */
-
-
-/**
- A single PaStream can provide multiple channels of real-time
- streaming audio input and output to a client application. A stream
- provides access to audio hardware represented by one or more
- PaDevices. Depending on the underlying Host API, it may be possible
- to open multiple streams using the same device, however this behavior
- is implementation defined. Portable applications should assume that
- a PaDevice may be simultaneously used by at most one PaStream.
-
- Pointers to PaStream objects are passed between PortAudio functions that
- operate on streams.
-
- @see Pa_OpenStream, Pa_OpenDefaultStream, Pa_OpenDefaultStream, Pa_CloseStream,
- Pa_StartStream, Pa_StopStream, Pa_AbortStream, Pa_IsStreamActive,
- Pa_GetStreamTime, Pa_GetStreamCpuLoad
-
-*/
-typedef void PaStream;
-
-
-/** Can be passed as the framesPerBuffer parameter to Pa_OpenStream()
- or Pa_OpenDefaultStream() to indicate that the stream callback will
- accept buffers of any size.
-*/
-#define paFramesPerBufferUnspecified (0)
-
-
-/** Flags used to control the behavior of a stream. They are passed as
- parameters to Pa_OpenStream or Pa_OpenDefaultStream. Multiple flags may be
- ORed together.
-
- @see Pa_OpenStream, Pa_OpenDefaultStream
- @see paNoFlag, paClipOff, paDitherOff, paNeverDropInput,
- paPrimeOutputBuffersUsingStreamCallback, paPlatformSpecificFlags
-*/
-typedef unsigned long PaStreamFlags;
-
-/** @see PaStreamFlags */
-#define paNoFlag ((PaStreamFlags) 0)
-
-/** Disable default clipping of out of range samples.
- @see PaStreamFlags
-*/
-#define paClipOff ((PaStreamFlags) 0x00000001)
-
-/** Disable default dithering.
- @see PaStreamFlags
-*/
-#define paDitherOff ((PaStreamFlags) 0x00000002)
-
-/** Flag requests that where possible a full duplex stream will not discard
- overflowed input samples without calling the stream callback. This flag is
- only valid for full duplex callback streams and only when used in combination
- with the paFramesPerBufferUnspecified (0) framesPerBuffer parameter. Using
- this flag incorrectly results in a paInvalidFlag error being returned from
- Pa_OpenStream and Pa_OpenDefaultStream.
-
- @see PaStreamFlags, paFramesPerBufferUnspecified
-*/
-#define paNeverDropInput ((PaStreamFlags) 0x00000004)
-
-/** Call the stream callback to fill initial output buffers, rather than the
- default behavior of priming the buffers with zeros (silence). This flag has
- no effect for input-only and blocking read/write streams.
-
- @see PaStreamFlags
-*/
-#define paPrimeOutputBuffersUsingStreamCallback ((PaStreamFlags) 0x00000008)
-
-/** A mask specifying the platform specific bits.
- @see PaStreamFlags
-*/
-#define paPlatformSpecificFlags ((PaStreamFlags)0xFFFF0000)
-
-/**
- Timing information for the buffers passed to the stream callback.
-*/
-typedef struct PaStreamCallbackTimeInfo{
- PaTime inputBufferAdcTime;
- PaTime currentTime;
- PaTime outputBufferDacTime;
-} PaStreamCallbackTimeInfo;
-
-
-/**
- Flag bit constants for the statusFlags to PaStreamCallback.
-
- @see paInputUnderflow, paInputOverflow, paOutputUnderflow, paOutputOverflow,
- paPrimingOutput
-*/
-typedef unsigned long PaStreamCallbackFlags;
-
-/** In a stream opened with paFramesPerBufferUnspecified, indicates that
- input data is all silence (zeros) because no real data is available. In a
- stream opened without paFramesPerBufferUnspecified, it indicates that one or
- more zero samples have been inserted into the input buffer to compensate
- for an input underflow.
- @see PaStreamCallbackFlags
-*/
-#define paInputUnderflow ((PaStreamCallbackFlags) 0x00000001)
-
-/** In a stream opened with paFramesPerBufferUnspecified, indicates that data
- prior to the first sample of the input buffer was discarded due to an
- overflow, possibly because the stream callback is using too much CPU time.
- Otherwise indicates that data prior to one or more samples in the
- input buffer was discarded.
- @see PaStreamCallbackFlags
-*/
-#define paInputOverflow ((PaStreamCallbackFlags) 0x00000002)
-
-/** Indicates that output data (or a gap) was inserted, possibly because the
- stream callback is using too much CPU time.
- @see PaStreamCallbackFlags
-*/
-#define paOutputUnderflow ((PaStreamCallbackFlags) 0x00000004)
-
-/** Indicates that output data will be discarded because no room is available.
- @see PaStreamCallbackFlags
-*/
-#define paOutputOverflow ((PaStreamCallbackFlags) 0x00000008)
-
-/** Some of all of the output data will be used to prime the stream, input
- data may be zero.
- @see PaStreamCallbackFlags
-*/
-#define paPrimingOutput ((PaStreamCallbackFlags) 0x00000010)
-
-/**
- Allowable return values for the PaStreamCallback.
- @see PaStreamCallback
-*/
-typedef enum PaStreamCallbackResult
-{
- paContinue=0,
- paComplete=1,
- paAbort=2
-} PaStreamCallbackResult;
-
-
-/**
- Functions of type PaStreamCallback are implemented by PortAudio clients.
- They consume, process or generate audio in response to requests from an
- active PortAudio stream.
-
- @param input and @param output are arrays of interleaved samples,
- the format, packing and number of channels used by the buffers are
- determined by parameters to Pa_OpenStream().
-
- @param frameCount The number of sample frames to be processed by
- the stream callback.
-
- @param timeInfo The time in seconds when the first sample of the input
- buffer was received at the audio input, the time in seconds when the first
- sample of the output buffer will begin being played at the audio output, and
- the time in seconds when the stream callback was called.
- See also Pa_GetStreamTime()
-
- @param statusFlags Flags indicating whether input and/or output buffers
- have been inserted or will be dropped to overcome underflow or overflow
- conditions.
-
- @param userData The value of a user supplied pointer passed to
- Pa_OpenStream() intended for storing synthesis data etc.
-
- @return
- The stream callback should return one of the values in the
- PaStreamCallbackResult enumeration. To ensure that the callback continues
- to be called, it should return paContinue (0). Either paComplete or paAbort
- can be returned to finish stream processing, after either of these values is
- returned the callback will not be called again. If paAbort is returned the
- stream will finish as soon as possible. If paComplete is returned, the stream
- will continue until all buffers generated by the callback have been played.
- This may be useful in applications such as soundfile players where a specific
- duration of output is required. However, it is not necessary to utilise this
- mechanism as Pa_StopStream(), Pa_AbortStream() or Pa_CloseStream() can also
- be used to stop the stream. The callback must always fill the entire output
- buffer irrespective of its return value.
-
- @see Pa_OpenStream, Pa_OpenDefaultStream
-
- @note With the exception of Pa_GetStreamCpuLoad() it is not permissable to call
- PortAudio API functions from within the stream callback.
-*/
-typedef int PaStreamCallback(
- const void *input, void *output,
- unsigned long frameCount,
- const PaStreamCallbackTimeInfo* timeInfo,
- PaStreamCallbackFlags statusFlags,
- void *userData );
-
-
-/** Opens a stream for either input, output or both.
-
- @param stream The address of a PaStream pointer which will receive
- a pointer to the newly opened stream.
-
- @param inputParameters A structure that describes the input parameters used by
- the opened stream. See PaStreamParameters for a description of these parameters.
- inputParameters must be NULL for output-only streams.
-
- @param outputParameters A structure that describes the output parameters used by
- the opened stream. See PaStreamParameters for a description of these parameters.
- outputParameters must be NULL for input-only streams.
-
- @param sampleRate The desired sampleRate. For full-duplex streams it is the
- sample rate for both input and output
-
- @param framesPerBuffer The number of frames passed to the stream callback
- function, or the preferred block granularity for a blocking read/write stream.
- The special value paFramesPerBufferUnspecified (0) may be used to request that
- the stream callback will recieve an optimal (and possibly varying) number of
- frames based on host requirements and the requested latency settings.
- Note: With some host APIs, the use of non-zero framesPerBuffer for a callback
- stream may introduce an additional layer of buffering which could introduce
- additional latency. PortAudio guarantees that the additional latency
- will be kept to the theoretical minimum however, it is strongly recommended
- that a non-zero framesPerBuffer value only be used when your algorithm
- requires a fixed number of frames per stream callback.
-
- @param streamFlags Flags which modify the behaviour of the streaming process.
- This parameter may contain a combination of flags ORed together. Some flags may
- only be relevant to certain buffer formats.
-
- @param streamCallback A pointer to a client supplied function that is responsible
- for processing and filling input and output buffers. If this parameter is NULL
- the stream will be opened in 'blocking read/write' mode. In blocking mode,
- the client can receive sample data using Pa_ReadStream and write sample data
- using Pa_WriteStream, the number of samples that may be read or written
- without blocking is returned by Pa_GetStreamReadAvailable and
- Pa_GetStreamWriteAvailable respectively.
-
- @param userData A client supplied pointer which is passed to the stream callback
- function. It could for example, contain a pointer to instance data necessary
- for processing the audio buffers. This parameter is ignored if streamCallback
- is NULL.
-
- @return
- Upon success Pa_OpenStream() returns paNoError and places a pointer to a
- valid PaStream in the stream argument. The stream is inactive (stopped).
- If a call to Pa_OpenStream() fails, a non-zero error code is returned (see
- PaError for possible error codes) and the value of stream is invalid.
-
- @see PaStreamParameters, PaStreamCallback, Pa_ReadStream, Pa_WriteStream,
- Pa_GetStreamReadAvailable, Pa_GetStreamWriteAvailable
-*/
-PaError Pa_OpenStream( PaStream** stream,
- const PaStreamParameters *inputParameters,
- const PaStreamParameters *outputParameters,
- double sampleRate,
- unsigned long framesPerBuffer,
- PaStreamFlags streamFlags,
- PaStreamCallback *streamCallback,
- void *userData );
-
-
-/** A simplified version of Pa_OpenStream() that opens the default input
- and/or output devices.
-
- @param stream The address of a PaStream pointer which will receive
- a pointer to the newly opened stream.
-
- @param numInputChannels The number of channels of sound that will be supplied
- to the stream callback or returned by Pa_ReadStream. It can range from 1 to
- the value of maxInputChannels in the PaDeviceInfo record for the default input
- device. If 0 the stream is opened as an output-only stream.
-
- @param numOutputChannels The number of channels of sound to be delivered to the
- stream callback or passed to Pa_WriteStream. It can range from 1 to the value
- of maxOutputChannels in the PaDeviceInfo record for the default output dvice.
- If 0 the stream is opened as an output-only stream.
-
- @param sampleFormat The sample format of both the input and output buffers
- provided to the callback or passed to and from Pa_ReadStream and Pa_WriteStream.
- sampleFormat may be any of the formats described by the PaSampleFormat
- enumeration.
-
- @param sampleRate Same as Pa_OpenStream parameter of the same name.
- @param framesPerBuffer Same as Pa_OpenStream parameter of the same name.
- @param streamCallback Same as Pa_OpenStream parameter of the same name.
- @param userData Same as Pa_OpenStream parameter of the same name.
-
- @return As for Pa_OpenStream
-
- @see Pa_OpenStream, PaStreamCallback
-*/
-PaError Pa_OpenDefaultStream( PaStream** stream,
- int numInputChannels,
- int numOutputChannels,
- PaSampleFormat sampleFormat,
- double sampleRate,
- unsigned long framesPerBuffer,
- PaStreamCallback *streamCallback,
- void *userData );
-
-
-/** Closes an audio stream. If the audio stream is active it
- discards any pending buffers as if Pa_AbortStream() had been called.
-*/
-PaError Pa_CloseStream( PaStream *stream );
-
-
-/** Functions of type PaStreamFinishedCallback are implemented by PortAudio
- clients. They can be registered with a stream using the Pa_SetStreamFinishedCallback
- function. Once registered they are called when the stream becomes inactive
- (ie once a call to Pa_StopStream() will not block).
- A stream will become inactive after the stream callback returns non-zero,
- or when Pa_StopStream or Pa_AbortStream is called. For a stream providing audio
- output, if the stream callback returns paComplete, or Pa_StopStream is called,
- the stream finished callback will not be called until all generated sample data
- has been played.
-
- @param userData The userData parameter supplied to Pa_OpenStream()
-
- @see Pa_SetStreamFinishedCallback
-*/
-typedef void PaStreamFinishedCallback( void *userData );
-
-
-/** Register a stream finished callback function which will be called when the
- stream becomes inactive. See the description of PaStreamFinishedCallback for
- further details about when the callback will be called.
-
- @param stream a pointer to a PaStream that is in the stopped state - if the
- stream is not stopped, the stream's finished callback will remain unchanged
- and an error code will be returned.
-
- @param streamFinishedCallback a pointer to a function with the same signature
- as PaStreamFinishedCallback, that will be called when the stream becomes
- inactive. Passing NULL for this parameter will un-register a previously
- registered stream finished callback function.
-
- @return on success returns paNoError, otherwise an error code indicating the cause
- of the error.
-
- @see PaStreamFinishedCallback
-*/
-PaError Pa_SetStreamFinishedCallback( PaStream *stream, PaStreamFinishedCallback* streamFinishedCallback );
-
-
-/** Commences audio processing.
-*/
-PaError Pa_StartStream( PaStream *stream );
-
-
-/** Terminates audio processing. It waits until all pending
- audio buffers have been played before it returns.
-*/
-PaError Pa_StopStream( PaStream *stream );
-
-
-/** Terminates audio processing immediately without waiting for pending
- buffers to complete.
-*/
-PaError Pa_AbortStream( PaStream *stream );
-
-
-/** Determine whether the stream is stopped.
- A stream is considered to be stopped prior to a successful call to
- Pa_StartStream and after a successful call to Pa_StopStream or Pa_AbortStream.
- If a stream callback returns a value other than paContinue the stream is NOT
- considered to be stopped.
-
- @return Returns one (1) when the stream is stopped, zero (0) when
- the stream is running or, a PaErrorCode (which are always negative) if
- PortAudio is not initialized or an error is encountered.
-
- @see Pa_StopStream, Pa_AbortStream, Pa_IsStreamActive
-*/
-PaError Pa_IsStreamStopped( PaStream *stream );
-
-
-/** Determine whether the stream is active.
- A stream is active after a successful call to Pa_StartStream(), until it
- becomes inactive either as a result of a call to Pa_StopStream() or
- Pa_AbortStream(), or as a result of a return value other than paContinue from
- the stream callback. In the latter case, the stream is considered inactive
- after the last buffer has finished playing.
-
- @return Returns one (1) when the stream is active (ie playing or recording
- audio), zero (0) when not playing or, a PaErrorCode (which are always negative)
- if PortAudio is not initialized or an error is encountered.
-
- @see Pa_StopStream, Pa_AbortStream, Pa_IsStreamStopped
-*/
-PaError Pa_IsStreamActive( PaStream *stream );
-
-
-
-/** A structure containing unchanging information about an open stream.
- @see Pa_GetStreamInfo
-*/
-
-typedef struct PaStreamInfo
-{
- /** this is struct version 1 */
- int structVersion;
-
- /** The input latency of the stream in seconds. This value provides the most
- accurate estimate of input latency available to the implementation. It may
- differ significantly from the suggestedLatency value passed to Pa_OpenStream().
- The value of this field will be zero (0.) for output-only streams.
- @see PaTime
- */
- PaTime inputLatency;
-
- /** The output latency of the stream in seconds. This value provides the most
- accurate estimate of output latency available to the implementation. It may
- differ significantly from the suggestedLatency value passed to Pa_OpenStream().
- The value of this field will be zero (0.) for input-only streams.
- @see PaTime
- */
- PaTime outputLatency;
-
- /** The sample rate of the stream in Hertz (samples per second). In cases
- where the hardware sample rate is inaccurate and PortAudio is aware of it,
- the value of this field may be different from the sampleRate parameter
- passed to Pa_OpenStream(). If information about the actual hardware sample
- rate is not available, this field will have the same value as the sampleRate
- parameter passed to Pa_OpenStream().
- */
- double sampleRate;
-
-} PaStreamInfo;
-
-
-/** Retrieve a pointer to a PaStreamInfo structure containing information
- about the specified stream.
- @return A pointer to an immutable PaStreamInfo structure. If the stream
- parameter invalid, or an error is encountered, the function returns NULL.
-
- @param stream A pointer to an open stream previously created with Pa_OpenStream.
-
- @note PortAudio manages the memory referenced by the returned pointer,
- the client must not manipulate or free the memory. The pointer is only
- guaranteed to be valid until the specified stream is closed.
-
- @see PaStreamInfo
-*/
-const PaStreamInfo* Pa_GetStreamInfo( PaStream *stream );
-
-
-/** Determine the current time for the stream according to the same clock used
- to generate buffer timestamps. This time may be used for syncronising other
- events to the audio stream, for example synchronizing audio to MIDI.
-
- @return The stream's current time in seconds, or 0 if an error occurred.
-
- @see PaTime, PaStreamCallback
-*/
-PaTime Pa_GetStreamTime( PaStream *stream );
-
-
-/** Retrieve CPU usage information for the specified stream.
- The "CPU Load" is a fraction of total CPU time consumed by a callback stream's
- audio processing routines including, but not limited to the client supplied
- stream callback. This function does not work with blocking read/write streams.
-
- This function may be called from the stream callback function or the
- application.
-
- @return
- A floating point value, typically between 0.0 and 1.0, where 1.0 indicates
- that the stream callback is consuming the maximum number of CPU cycles possible
- to maintain real-time operation. A value of 0.5 would imply that PortAudio and
- the stream callback was consuming roughly 50% of the available CPU time. The
- return value may exceed 1.0. A value of 0.0 will always be returned for a
- blocking read/write stream, or if an error occurrs.
-*/
-double Pa_GetStreamCpuLoad( PaStream* stream );
-
-
-/** Read samples from an input stream. The function doesn't return until
- the entire buffer has been filled - this may involve waiting for the operating
- system to supply the data.
-
- @param stream A pointer to an open stream previously created with Pa_OpenStream.
-
- @param buffer A pointer to a buffer of sample frames. The buffer contains
- samples in the format specified by the inputParameters->sampleFormat field
- used to open the stream, and the number of channels specified by
- inputParameters->numChannels. If non-interleaved samples were requested,
- buffer is a pointer to the first element of an array of non-interleaved
- buffer pointers, one for each channel.
-
- @param frames The number of frames to be read into buffer. This parameter
- is not constrained to a specific range, however high performance applications
- will want to match this parameter to the framesPerBuffer parameter used
- when opening the stream.
-
- @return On success PaNoError will be returned, or PaInputOverflowed if input
- data was discarded by PortAudio after the previous call and before this call.
-*/
-PaError Pa_ReadStream( PaStream* stream,
- void *buffer,
- unsigned long frames );
-
-
-/** Write samples to an output stream. This function doesn't return until the
- entire buffer has been consumed - this may involve waiting for the operating
- system to consume the data.
-
- @param stream A pointer to an open stream previously created with Pa_OpenStream.
-
- @param buffer A pointer to a buffer of sample frames. The buffer contains
- samples in the format specified by the outputParameters->sampleFormat field
- used to open the stream, and the number of channels specified by
- outputParameters->numChannels. If non-interleaved samples were requested,
- buffer is a pointer to the first element of an array of non-interleaved
- buffer pointers, one for each channel.
-
- @param frames The number of frames to be written from buffer. This parameter
- is not constrained to a specific range, however high performance applications
- will want to match this parameter to the framesPerBuffer parameter used
- when opening the stream.
-
- @return On success PaNoError will be returned, or paOutputUnderflowed if
- additional output data was inserted after the previous call and before this
- call.
-*/
-PaError Pa_WriteStream( PaStream* stream,
- const void *buffer,
- unsigned long frames );
-
-
-/** Retrieve the number of frames that can be read from the stream without
- waiting.
-
- @return Returns a non-negative value representing the maximum number of frames
- that can be read from the stream without blocking or busy waiting or, a
- PaErrorCode (which are always negative) if PortAudio is not initialized or an
- error is encountered.
-*/
-signed long Pa_GetStreamReadAvailable( PaStream* stream );
-
-
-/** Retrieve the number of frames that can be written to the stream without
- waiting.
-
- @return Returns a non-negative value representing the maximum number of frames
- that can be written to the stream without blocking or busy waiting or, a
- PaErrorCode (which are always negative) if PortAudio is not initialized or an
- error is encountered.
-*/
-signed long Pa_GetStreamWriteAvailable( PaStream* stream );
-
-
-/* Miscellaneous utilities */
-
-
-/** Retrieve the size of a given sample format in bytes.
-
- @return The size in bytes of a single sample in the specified format,
- or paSampleFormatNotSupported if the format is not supported.
-*/
-PaError Pa_GetSampleSize( PaSampleFormat format );
-
-
-/** Put the caller to sleep for at least 'msec' milliseconds. This function is
- provided only as a convenience for authors of portable code (such as the tests
- and examples in the PortAudio distribution.)
-
- The function may sleep longer than requested so don't rely on this for accurate
- musical timing.
-*/
-void Pa_Sleep( long msec );
-
-
-
-#ifdef __cplusplus
-}
-#endif /* __cplusplus */
-#endif /* PORTAUDIO_H */
+#ifndef PORTAUDIO_H
+#define PORTAUDIO_H
+/*
+ * $Id: portaudio.h,v 1.5.2.50 2004/12/13 11:50:40 rossbencina Exp $
+ * PortAudio Portable Real-Time Audio Library
+ * PortAudio API Header File
+ * Latest version available at: http://www.portaudio.com/
+ *
+ * Copyright (c) 1999-2002 Ross Bencina and Phil Burk
+ *
+ * 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.
+ *
+ * Any person wishing to distribute modifications to the Software is
+ * requested to send the modifications to the original developer so that
+ * they can be incorporated into the canonical version.
+ *
+ * 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.
+ */
+
+/** @file
+ @brief The PortAudio API.
+*/
+
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif /* __cplusplus */
+
+
+/** Retrieve the release number of the currently running PortAudio build,
+ eg 1900.
+*/
+int Pa_GetVersion( void );
+
+
+/** Retrieve a textual description of the current PortAudio build,
+ eg "PortAudio V19-devel 13 October 2002".
+*/
+const char* Pa_GetVersionText( void );
+
+
+/** Error codes returned by PortAudio functions.
+ Note that with the exception of paNoError, all PaErrorCodes are negative.
+*/
+
+typedef int PaError;
+typedef enum PaErrorCode
+{
+ paNoError = 0,
+
+ paNotInitialized = -10000,
+ paUnanticipatedHostError,
+ paInvalidChannelCount,
+ paInvalidSampleRate,
+ paInvalidDevice,
+ paInvalidFlag,
+ paSampleFormatNotSupported,
+ paBadIODeviceCombination,
+ paInsufficientMemory,
+ paBufferTooBig,
+ paBufferTooSmall,
+ paNullCallback,
+ paBadStreamPtr,
+ paTimedOut,
+ paInternalError,
+ paDeviceUnavailable,
+ paIncompatibleHostApiSpecificStreamInfo,
+ paStreamIsStopped,
+ paStreamIsNotStopped,
+ paInputOverflowed,
+ paOutputUnderflowed,
+ paHostApiNotFound,
+ paInvalidHostApi,
+ paCanNotReadFromACallbackStream, /**< @todo review error code name */
+ paCanNotWriteToACallbackStream, /**< @todo review error code name */
+ paCanNotReadFromAnOutputOnlyStream, /**< @todo review error code name */
+ paCanNotWriteToAnInputOnlyStream, /**< @todo review error code name */
+ paIncompatibleStreamHostApi
+} PaErrorCode;
+
+
+/** Translate the supplied PortAudio error code into a human readable
+ message.
+*/
+const char *Pa_GetErrorText( PaError errorCode );
+
+
+/** Library initialization function - call this before using PortAudio.
+ This function initialises internal data structures and prepares underlying
+ host APIs for use. This function MUST be called before using any other
+ PortAudio API functions.
+
+ If Pa_Initialize() is called multiple times, each successful
+ call must be matched with a corresponding call to Pa_Terminate().
+ Pairs of calls to Pa_Initialize()/Pa_Terminate() may overlap, and are not
+ required to be fully nested.
+
+ Note that if Pa_Initialize() returns an error code, Pa_Terminate() should
+ NOT be called.
+
+ @return paNoError if successful, otherwise an error code indicating the cause
+ of failure.
+
+ @see Pa_Terminate
+*/
+PaError Pa_Initialize( void );
+
+
+/** Library termination function - call this when finished using PortAudio.
+ This function deallocates all resources allocated by PortAudio since it was
+ initializied by a call to Pa_Initialize(). In cases where Pa_Initialise() has
+ been called multiple times, each call must be matched with a corresponding call
+ to Pa_Terminate(). The final matching call to Pa_Terminate() will automatically
+ close any PortAudio streams that are still open.
+
+ Pa_Terminate() MUST be called before exiting a program which uses PortAudio.
+ Failure to do so may result in serious resource leaks, such as audio devices
+ not being available until the next reboot.
+
+ @return paNoError if successful, otherwise an error code indicating the cause
+ of failure.
+
+ @see Pa_Initialize
+*/
+PaError Pa_Terminate( void );
+
+
+
+/** The type used to refer to audio devices. Values of this type usually
+ range from 0 to (Pa_DeviceCount-1), and may also take on the PaNoDevice
+ and paUseHostApiSpecificDeviceSpecification values.
+
+ @see Pa_DeviceCount, paNoDevice, paUseHostApiSpecificDeviceSpecification
+*/
+typedef int PaDeviceIndex;
+
+
+/** A special PaDeviceIndex value indicating that no device is available,
+ or should be used.
+
+ @see PaDeviceIndex
+*/
+#define paNoDevice ((PaDeviceIndex)-1)
+
+
+/** A special PaDeviceIndex value indicating that the device(s) to be used
+ are specified in the host api specific stream info structure.
+
+ @see PaDeviceIndex
+*/
+#define paUseHostApiSpecificDeviceSpecification ((PaDeviceIndex)-2)
+
+
+/* Host API enumeration mechanism */
+
+/** The type used to enumerate to host APIs at runtime. Values of this type
+ range from 0 to (Pa_GetHostApiCount()-1).
+
+ @see Pa_GetHostApiCount
+*/
+typedef int PaHostApiIndex;
+
+
+/** Retrieve the number of available host APIs. Even if a host API is
+ available it may have no devices available.
+
+ @return A non-negative value indicating the number of available host APIs
+ or, a PaErrorCode (which are always negative) if PortAudio is not initialized
+ or an error is encountered.
+
+ @see PaHostApiIndex
+*/
+PaHostApiIndex Pa_GetHostApiCount( void );
+
+
+/** Retrieve the index of the default host API. The default host API will be
+ the lowest common denominator host API on the current platform and is
+ unlikely to provide the best performance.
+
+ @return A non-negative value ranging from 0 to (Pa_GetHostApiCount()-1)
+ indicating the default host API index or, a PaErrorCode (which are always
+ negative) if PortAudio is not initialized or an error is encountered.
+*/
+PaHostApiIndex Pa_GetDefaultHostApi( void );
+
+
+/** Unchanging unique identifiers for each supported host API. This type
+ is used in the PaHostApiInfo structure. The values are guaranteed to be
+ unique and to never change, thus allowing code to be written that
+ conditionally uses host API specific extensions.
+
+ New type ids will be allocated when support for a host API reaches
+ "public alpha" status, prior to that developers should use the
+ paInDevelopment type id.
+
+ @see PaHostApiInfo
+*/
+typedef enum PaHostApiTypeId
+{
+ paInDevelopment=0, /* use while developing support for a new host API */
+ paDirectSound=1,
+ paMME=2,
+ paASIO=3,
+ paSoundManager=4,
+ paCoreAudio=5,
+ paOSS=7,
+ paALSA=8,
+ paAL=9,
+ paBeOS=10,
+ paWDMKS=11,
+ paJACK=12
+} PaHostApiTypeId;
+
+
+/** A structure containing information about a particular host API. */
+
+typedef struct PaHostApiInfo
+{
+ /** this is struct version 1 */
+ int structVersion;
+ /** The well known unique identifier of this host API @see PaHostApiTypeId */
+ PaHostApiTypeId type;
+ /** A textual description of the host API for display on user interfaces. */
+ const char *name;
+
+ /** The number of devices belonging to this host API. This field may be
+ used in conjunction with Pa_HostApiDeviceIndexToDeviceIndex() to enumerate
+ all devices for this host API.
+ @see Pa_HostApiDeviceIndexToDeviceIndex
+ */
+ int deviceCount;
+
+ /** The the default input device for this host API. The value will be a
+ device index ranging from 0 to (Pa_GetDeviceCount()-1), or paNoDevice
+ if no default input device is available.
+ */
+ PaDeviceIndex defaultInputDevice;
+
+ /** The the default output device for this host API. The value will be a
+ device index ranging from 0 to (Pa_GetDeviceCount()-1), or paNoDevice
+ if no default output device is available.
+ */
+ PaDeviceIndex defaultOutputDevice;
+
+} PaHostApiInfo;
+
+
+/** Retrieve a pointer to a structure containing information about a specific
+ host Api.
+
+ @param hostApi A valid host API index ranging from 0 to (Pa_GetHostApiCount()-1)
+
+ @return A pointer to an immutable PaHostApiInfo structure describing
+ a specific host API. If the hostApi parameter is out of range or an error
+ is encountered, the function returns NULL.
+
+ The returned structure is owned by the PortAudio implementation and must not
+ be manipulated or freed. The pointer is only guaranteed to be valid between
+ calls to Pa_Initialize() and Pa_Terminate().
+*/
+const PaHostApiInfo * Pa_GetHostApiInfo( PaHostApiIndex hostApi );
+
+
+/** Convert a static host API unique identifier, into a runtime
+ host API index.
+
+ @param type A unique host API identifier belonging to the PaHostApiTypeId
+ enumeration.
+
+ @return A valid PaHostApiIndex ranging from 0 to (Pa_GetHostApiCount()-1) or,
+ a PaErrorCode (which are always negative) if PortAudio is not initialized
+ or an error is encountered.
+
+ The paHostApiNotFound error code indicates that the host API specified by the
+ type parameter is not available.
+
+ @see PaHostApiTypeId
+*/
+PaHostApiIndex Pa_HostApiTypeIdToHostApiIndex( PaHostApiTypeId type );
+
+
+/** Convert a host-API-specific device index to standard PortAudio device index.
+ This function may be used in conjunction with the deviceCount field of
+ PaHostApiInfo to enumerate all devices for the specified host API.
+
+ @param hostApi A valid host API index ranging from 0 to (Pa_GetHostApiCount()-1)
+
+ @param hostApiDeviceIndex A valid per-host device index in the range
+ 0 to (Pa_GetHostApiInfo(hostApi)->deviceCount-1)
+
+ @return A non-negative PaDeviceIndex ranging from 0 to (Pa_GetDeviceCount()-1)
+ or, a PaErrorCode (which are always negative) if PortAudio is not initialized
+ or an error is encountered.
+
+ A paInvalidHostApi error code indicates that the host API index specified by
+ the hostApi parameter is out of range.
+
+ A paInvalidDevice error code indicates that the hostApiDeviceIndex parameter
+ is out of range.
+
+ @see PaHostApiInfo
+*/
+PaDeviceIndex Pa_HostApiDeviceIndexToDeviceIndex( PaHostApiIndex hostApi,
+ int hostApiDeviceIndex );
+
+
+
+/** Structure used to return information about a host error condition.
+*/
+typedef struct PaHostErrorInfo{
+ PaHostApiTypeId hostApiType; /**< the host API which returned the error code */
+ long errorCode; /**< the error code returned */
+ const char *errorText; /**< a textual description of the error if available, otherwise a zero-length string */
+}PaHostErrorInfo;
+
+
+/** Return information about the last host error encountered. The error
+ information returned by Pa_GetLastHostErrorInfo() will never be modified
+ asyncronously by errors occurring in other PortAudio owned threads
+ (such as the thread that manages the stream callback.)
+
+ This function is provided as a last resort, primarily to enhance debugging
+ by providing clients with access to all available error information.
+
+ @return A pointer to an immutable structure constaining information about
+ the host error. The values in this structure will only be valid if a
+ PortAudio function has previously returned the paUnanticipatedHostError
+ error code.
+*/
+const PaHostErrorInfo* Pa_GetLastHostErrorInfo( void );
+
+
+
+/* Device enumeration and capabilities */
+
+/** Retrieve the number of available devices. The number of available devices
+ may be zero.
+
+ @return A non-negative value indicating the number of available devices or,
+ a PaErrorCode (which are always negative) if PortAudio is not initialized
+ or an error is encountered.
+*/
+PaDeviceIndex Pa_GetDeviceCount( void );
+
+
+/** Retrieve the index of the default input device. The result can be
+ used in the inputDevice parameter to Pa_OpenStream().
+
+ @return The default input device index for the default host API, or paNoDevice
+ if no default input device is available or an error was encountered.
+*/
+PaDeviceIndex Pa_GetDefaultInputDevice( void );
+
+
+/** Retrieve the index of the default output device. The result can be
+ used in the outputDevice parameter to Pa_OpenStream().
+
+ @return The default output device index for the defualt host API, or paNoDevice
+ if no default output device is available or an error was encountered.
+
+ @note
+ On the PC, the user can specify a default device by
+ setting an environment variable. For example, to use device #1.
+<pre>
+ set PA_RECOMMENDED_OUTPUT_DEVICE=1
+</pre>
+ The user should first determine the available device ids by using
+ the supplied application "pa_devs".
+*/
+PaDeviceIndex Pa_GetDefaultOutputDevice( void );
+
+
+/** The type used to represent monotonic time in seconds that can be used
+ for syncronisation. The type is used for the outTime argument to the
+ PaStreamCallback and as the result of Pa_GetStreamTime().
+
+ @see PaStreamCallback, Pa_GetStreamTime
+*/
+typedef double PaTime;
+
+
+/** A type used to specify one or more sample formats. Each value indicates
+ a possible format for sound data passed to and from the stream callback,
+ Pa_ReadStream and Pa_WriteStream.
+
+ The standard formats paFloat32, paInt16, paInt32, paInt24, paInt8
+ and aUInt8 are usually implemented by all implementations.
+
+ The floating point representation (paFloat32) uses +1.0 and -1.0 as the
+ maximum and minimum respectively.
+
+ paUInt8 is an unsigned 8 bit format where 128 is considered "ground"
+
+ The paNonInterleaved flag indicates that a multichannel buffer is passed
+ as a set of non-interleaved pointers.
+
+ @see Pa_OpenStream, Pa_OpenDefaultStream, PaDeviceInfo
+ @see paFloat32, paInt16, paInt32, paInt24, paInt8
+ @see paUInt8, paCustomFormat, paNonInterleaved
+*/
+typedef unsigned long PaSampleFormat;
+
+
+#define paFloat32 ((PaSampleFormat) 0x00000001) /**< @see PaSampleFormat */
+#define paInt32 ((PaSampleFormat) 0x00000002) /**< @see PaSampleFormat */
+#define paInt24 ((PaSampleFormat) 0x00000004) /**< Packed 24 bit format. @see PaSampleFormat */
+#define paInt16 ((PaSampleFormat) 0x00000008) /**< @see PaSampleFormat */
+#define paInt8 ((PaSampleFormat) 0x00000010) /**< @see PaSampleFormat */
+#define paUInt8 ((PaSampleFormat) 0x00000020) /**< @see PaSampleFormat */
+#define paCustomFormat ((PaSampleFormat) 0x00010000)/**< @see PaSampleFormat */
+
+#define paNonInterleaved ((PaSampleFormat) 0x80000000)
+
+/** A structure providing information and capabilities of PortAudio devices.
+ Devices may support input, output or both input and output.
+*/
+typedef struct PaDeviceInfo
+{
+ int structVersion; /* this is struct version 2 */
+ const char *name;
+ PaHostApiIndex hostApi; /* note this is a host API index, not a type id*/
+
+ int maxInputChannels;
+ int maxOutputChannels;
+
+ /* Default latency values for interactive performance. */
+ PaTime defaultLowInputLatency;
+ PaTime defaultLowOutputLatency;
+ /* Default latency values for robust non-interactive applications (eg. playing sound files). */
+ PaTime defaultHighInputLatency;
+ PaTime defaultHighOutputLatency;
+
+ double defaultSampleRate;
+} PaDeviceInfo;
+
+
+/** Retrieve a pointer to a PaDeviceInfo structure containing information
+ about the specified device.
+ @return A pointer to an immutable PaDeviceInfo structure. If the device
+ parameter is out of range the function returns NULL.
+
+ @param device A valid device index in the range 0 to (Pa_GetDeviceCount()-1)
+
+ @note PortAudio manages the memory referenced by the returned pointer,
+ the client must not manipulate or free the memory. The pointer is only
+ guaranteed to be valid between calls to Pa_Initialize() and Pa_Terminate().
+
+ @see PaDeviceInfo, PaDeviceIndex
+*/
+const PaDeviceInfo* Pa_GetDeviceInfo( PaDeviceIndex device );
+
+
+/** Parameters for one direction (input or output) of a stream.
+*/
+typedef struct PaStreamParameters
+{
+ /** A valid device index in the range 0 to (Pa_GetDeviceCount()-1)
+ specifying the device to be used or the special constant
+ paUseHostApiSpecificDeviceSpecification which indicates that the actual
+ device(s) to use are specified in hostApiSpecificStreamInfo.
+ This field must not be set to paNoDevice.
+ */
+ PaDeviceIndex device;
+
+ /** The number of channels of sound to be delivered to the
+ stream callback or accessed by Pa_ReadStream() or Pa_WriteStream().
+ It can range from 1 to the value of maxInputChannels in the
+ PaDeviceInfo record for the device specified by the device parameter.
+ */
+ int channelCount;
+
+ /** The sample format of the buffer provided to the stream callback,
+ a_ReadStream() or Pa_WriteStream(). It may be any of the formats described
+ by the PaSampleFormat enumeration.
+ */
+ PaSampleFormat sampleFormat;
+
+ /** The desired latency in seconds. Where practical, implementations should
+ configure their latency based on these parameters, otherwise they may
+ choose the closest viable latency instead. Unless the suggested latency
+ is greater than the absolute upper limit for the device implementations
+ shouldround the suggestedLatency up to the next practial value - ie to
+ provide an equal or higher latency than suggestedLatency whereever possibe.
+ Actual latency values for an open stream may be retrieved using the
+ inputLatency and outputLatency fields of the PaStreamInfo structure
+ returned by Pa_GetStreamInfo().
+ @see default*Latency in PaDeviceInfo, *Latency in PaStreamInfo
+ */
+ PaTime suggestedLatency;
+
+ /** An optional pointer to a host api specific data structure
+ containing additional information for device setup and/or stream processing.
+ hostApiSpecificStreamInfo is never required for correct operation,
+ if not used it should be set to NULL.
+ */
+ void *hostApiSpecificStreamInfo;
+
+} PaStreamParameters;
+
+
+/** Return code for Pa_IsFormatSupported indicating success. */
+#define paFormatIsSupported (0)
+
+/** Determine whether it would be possible to open a stream with the specified
+ parameters.
+
+ @param inputParameters A structure that describes the input parameters used to
+ open a stream. The suggestedLatency field is ignored. See PaStreamParameters
+ for a description of these parameters. inputParameters must be NULL for
+ output-only streams.
+
+ @param outputParameters A structure that describes the output parameters used
+ to open a stream. The suggestedLatency field is ignored. See PaStreamParameters
+ for a description of these parameters. outputParameters must be NULL for
+ input-only streams.
+
+ @param sampleRate The required sampleRate. For full-duplex streams it is the
+ sample rate for both input and output
+
+ @return Returns 0 if the format is supported, and an error code indicating why
+ the format is not supported otherwise. The constant paFormatIsSupported is
+ provided to compare with the return value for success.
+
+ @see paFormatIsSupported, PaStreamParameters
+*/
+PaError Pa_IsFormatSupported( const PaStreamParameters *inputParameters,
+ const PaStreamParameters *outputParameters,
+ double sampleRate );
+
+
+
+/* Streaming types and functions */
+
+
+/**
+ A single PaStream can provide multiple channels of real-time
+ streaming audio input and output to a client application. A stream
+ provides access to audio hardware represented by one or more
+ PaDevices. Depending on the underlying Host API, it may be possible
+ to open multiple streams using the same device, however this behavior
+ is implementation defined. Portable applications should assume that
+ a PaDevice may be simultaneously used by at most one PaStream.
+
+ Pointers to PaStream objects are passed between PortAudio functions that
+ operate on streams.
+
+ @see Pa_OpenStream, Pa_OpenDefaultStream, Pa_OpenDefaultStream, Pa_CloseStream,
+ Pa_StartStream, Pa_StopStream, Pa_AbortStream, Pa_IsStreamActive,
+ Pa_GetStreamTime, Pa_GetStreamCpuLoad
+
+*/
+typedef void PaStream;
+
+
+/** Can be passed as the framesPerBuffer parameter to Pa_OpenStream()
+ or Pa_OpenDefaultStream() to indicate that the stream callback will
+ accept buffers of any size.
+*/
+#define paFramesPerBufferUnspecified (0)
+
+
+/** Flags used to control the behavior of a stream. They are passed as
+ parameters to Pa_OpenStream or Pa_OpenDefaultStream. Multiple flags may be
+ ORed together.
+
+ @see Pa_OpenStream, Pa_OpenDefaultStream
+ @see paNoFlag, paClipOff, paDitherOff, paNeverDropInput,
+ paPrimeOutputBuffersUsingStreamCallback, paPlatformSpecificFlags
+*/
+typedef unsigned long PaStreamFlags;
+
+/** @see PaStreamFlags */
+#define paNoFlag ((PaStreamFlags) 0)
+
+/** Disable default clipping of out of range samples.
+ @see PaStreamFlags
+*/
+#define paClipOff ((PaStreamFlags) 0x00000001)
+
+/** Disable default dithering.
+ @see PaStreamFlags
+*/
+#define paDitherOff ((PaStreamFlags) 0x00000002)
+
+/** Flag requests that where possible a full duplex stream will not discard
+ overflowed input samples without calling the stream callback. This flag is
+ only valid for full duplex callback streams and only when used in combination
+ with the paFramesPerBufferUnspecified (0) framesPerBuffer parameter. Using
+ this flag incorrectly results in a paInvalidFlag error being returned from
+ Pa_OpenStream and Pa_OpenDefaultStream.
+
+ @see PaStreamFlags, paFramesPerBufferUnspecified
+*/
+#define paNeverDropInput ((PaStreamFlags) 0x00000004)
+
+/** Call the stream callback to fill initial output buffers, rather than the
+ default behavior of priming the buffers with zeros (silence). This flag has
+ no effect for input-only and blocking read/write streams.
+
+ @see PaStreamFlags
+*/
+#define paPrimeOutputBuffersUsingStreamCallback ((PaStreamFlags) 0x00000008)
+
+/** A mask specifying the platform specific bits.
+ @see PaStreamFlags
+*/
+#define paPlatformSpecificFlags ((PaStreamFlags)0xFFFF0000)
+
+/**
+ Timing information for the buffers passed to the stream callback.
+*/
+typedef struct PaStreamCallbackTimeInfo{
+ PaTime inputBufferAdcTime;
+ PaTime currentTime;
+ PaTime outputBufferDacTime;
+} PaStreamCallbackTimeInfo;
+
+
+/**
+ Flag bit constants for the statusFlags to PaStreamCallback.
+
+ @see paInputUnderflow, paInputOverflow, paOutputUnderflow, paOutputOverflow,
+ paPrimingOutput
+*/
+typedef unsigned long PaStreamCallbackFlags;
+
+/** In a stream opened with paFramesPerBufferUnspecified, indicates that
+ input data is all silence (zeros) because no real data is available. In a
+ stream opened without paFramesPerBufferUnspecified, it indicates that one or
+ more zero samples have been inserted into the input buffer to compensate
+ for an input underflow.
+ @see PaStreamCallbackFlags
+*/
+#define paInputUnderflow ((PaStreamCallbackFlags) 0x00000001)
+
+/** In a stream opened with paFramesPerBufferUnspecified, indicates that data
+ prior to the first sample of the input buffer was discarded due to an
+ overflow, possibly because the stream callback is using too much CPU time.
+ Otherwise indicates that data prior to one or more samples in the
+ input buffer was discarded.
+ @see PaStreamCallbackFlags
+*/
+#define paInputOverflow ((PaStreamCallbackFlags) 0x00000002)
+
+/** Indicates that output data (or a gap) was inserted, possibly because the
+ stream callback is using too much CPU time.
+ @see PaStreamCallbackFlags
+*/
+#define paOutputUnderflow ((PaStreamCallbackFlags) 0x00000004)
+
+/** Indicates that output data will be discarded because no room is available.
+ @see PaStreamCallbackFlags
+*/
+#define paOutputOverflow ((PaStreamCallbackFlags) 0x00000008)
+
+/** Some of all of the output data will be used to prime the stream, input
+ data may be zero.
+ @see PaStreamCallbackFlags
+*/
+#define paPrimingOutput ((PaStreamCallbackFlags) 0x00000010)
+
+/**
+ Allowable return values for the PaStreamCallback.
+ @see PaStreamCallback
+*/
+typedef enum PaStreamCallbackResult
+{
+ paContinue=0,
+ paComplete=1,
+ paAbort=2
+} PaStreamCallbackResult;
+
+
+/**
+ Functions of type PaStreamCallback are implemented by PortAudio clients.
+ They consume, process or generate audio in response to requests from an
+ active PortAudio stream.
+
+ @param input and @param output are arrays of interleaved samples,
+ the format, packing and number of channels used by the buffers are
+ determined by parameters to Pa_OpenStream().
+
+ @param frameCount The number of sample frames to be processed by
+ the stream callback.
+
+ @param timeInfo The time in seconds when the first sample of the input
+ buffer was received at the audio input, the time in seconds when the first
+ sample of the output buffer will begin being played at the audio output, and
+ the time in seconds when the stream callback was called.
+ See also Pa_GetStreamTime()
+
+ @param statusFlags Flags indicating whether input and/or output buffers
+ have been inserted or will be dropped to overcome underflow or overflow
+ conditions.
+
+ @param userData The value of a user supplied pointer passed to
+ Pa_OpenStream() intended for storing synthesis data etc.
+
+ @return
+ The stream callback should return one of the values in the
+ PaStreamCallbackResult enumeration. To ensure that the callback continues
+ to be called, it should return paContinue (0). Either paComplete or paAbort
+ can be returned to finish stream processing, after either of these values is
+ returned the callback will not be called again. If paAbort is returned the
+ stream will finish as soon as possible. If paComplete is returned, the stream
+ will continue until all buffers generated by the callback have been played.
+ This may be useful in applications such as soundfile players where a specific
+ duration of output is required. However, it is not necessary to utilise this
+ mechanism as Pa_StopStream(), Pa_AbortStream() or Pa_CloseStream() can also
+ be used to stop the stream. The callback must always fill the entire output
+ buffer irrespective of its return value.
+
+ @see Pa_OpenStream, Pa_OpenDefaultStream
+
+ @note With the exception of Pa_GetStreamCpuLoad() it is not permissable to call
+ PortAudio API functions from within the stream callback.
+*/
+typedef int PaStreamCallback(
+ const void *input, void *output,
+ unsigned long frameCount,
+ const PaStreamCallbackTimeInfo* timeInfo,
+ PaStreamCallbackFlags statusFlags,
+ void *userData );
+
+
+/** Opens a stream for either input, output or both.
+
+ @param stream The address of a PaStream pointer which will receive
+ a pointer to the newly opened stream.
+
+ @param inputParameters A structure that describes the input parameters used by
+ the opened stream. See PaStreamParameters for a description of these parameters.
+ inputParameters must be NULL for output-only streams.
+
+ @param outputParameters A structure that describes the output parameters used by
+ the opened stream. See PaStreamParameters for a description of these parameters.
+ outputParameters must be NULL for input-only streams.
+
+ @param sampleRate The desired sampleRate. For full-duplex streams it is the
+ sample rate for both input and output
+
+ @param framesPerBuffer The number of frames passed to the stream callback
+ function, or the preferred block granularity for a blocking read/write stream.
+ The special value paFramesPerBufferUnspecified (0) may be used to request that
+ the stream callback will recieve an optimal (and possibly varying) number of
+ frames based on host requirements and the requested latency settings.
+ Note: With some host APIs, the use of non-zero framesPerBuffer for a callback
+ stream may introduce an additional layer of buffering which could introduce
+ additional latency. PortAudio guarantees that the additional latency
+ will be kept to the theoretical minimum however, it is strongly recommended
+ that a non-zero framesPerBuffer value only be used when your algorithm
+ requires a fixed number of frames per stream callback.
+
+ @param streamFlags Flags which modify the behaviour of the streaming process.
+ This parameter may contain a combination of flags ORed together. Some flags may
+ only be relevant to certain buffer formats.
+
+ @param streamCallback A pointer to a client supplied function that is responsible
+ for processing and filling input and output buffers. If this parameter is NULL
+ the stream will be opened in 'blocking read/write' mode. In blocking mode,
+ the client can receive sample data using Pa_ReadStream and write sample data
+ using Pa_WriteStream, the number of samples that may be read or written
+ without blocking is returned by Pa_GetStreamReadAvailable and
+ Pa_GetStreamWriteAvailable respectively.
+
+ @param userData A client supplied pointer which is passed to the stream callback
+ function. It could for example, contain a pointer to instance data necessary
+ for processing the audio buffers. This parameter is ignored if streamCallback
+ is NULL.
+
+ @return
+ Upon success Pa_OpenStream() returns paNoError and places a pointer to a
+ valid PaStream in the stream argument. The stream is inactive (stopped).
+ If a call to Pa_OpenStream() fails, a non-zero error code is returned (see
+ PaError for possible error codes) and the value of stream is invalid.
+
+ @see PaStreamParameters, PaStreamCallback, Pa_ReadStream, Pa_WriteStream,
+ Pa_GetStreamReadAvailable, Pa_GetStreamWriteAvailable
+*/
+PaError Pa_OpenStream( PaStream** stream,
+ const PaStreamParameters *inputParameters,
+ const PaStreamParameters *outputParameters,
+ double sampleRate,
+ unsigned long framesPerBuffer,
+ PaStreamFlags streamFlags,
+ PaStreamCallback *streamCallback,
+ void *userData );
+
+
+/** A simplified version of Pa_OpenStream() that opens the default input
+ and/or output devices.
+
+ @param stream The address of a PaStream pointer which will receive
+ a pointer to the newly opened stream.
+
+ @param numInputChannels The number of channels of sound that will be supplied
+ to the stream callback or returned by Pa_ReadStream. It can range from 1 to
+ the value of maxInputChannels in the PaDeviceInfo record for the default input
+ device. If 0 the stream is opened as an output-only stream.
+
+ @param numOutputChannels The number of channels of sound to be delivered to the
+ stream callback or passed to Pa_WriteStream. It can range from 1 to the value
+ of maxOutputChannels in the PaDeviceInfo record for the default output dvice.
+ If 0 the stream is opened as an output-only stream.
+
+ @param sampleFormat The sample format of both the input and output buffers
+ provided to the callback or passed to and from Pa_ReadStream and Pa_WriteStream.
+ sampleFormat may be any of the formats described by the PaSampleFormat
+ enumeration.
+
+ @param sampleRate Same as Pa_OpenStream parameter of the same name.
+ @param framesPerBuffer Same as Pa_OpenStream parameter of the same name.
+ @param streamCallback Same as Pa_OpenStream parameter of the same name.
+ @param userData Same as Pa_OpenStream parameter of the same name.
+
+ @return As for Pa_OpenStream
+
+ @see Pa_OpenStream, PaStreamCallback
+*/
+PaError Pa_OpenDefaultStream( PaStream** stream,
+ int numInputChannels,
+ int numOutputChannels,
+ PaSampleFormat sampleFormat,
+ double sampleRate,
+ unsigned long framesPerBuffer,
+ PaStreamCallback *streamCallback,
+ void *userData );
+
+
+/** Closes an audio stream. If the audio stream is active it
+ discards any pending buffers as if Pa_AbortStream() had been called.
+*/
+PaError Pa_CloseStream( PaStream *stream );
+
+
+/** Functions of type PaStreamFinishedCallback are implemented by PortAudio
+ clients. They can be registered with a stream using the Pa_SetStreamFinishedCallback
+ function. Once registered they are called when the stream becomes inactive
+ (ie once a call to Pa_StopStream() will not block).
+ A stream will become inactive after the stream callback returns non-zero,
+ or when Pa_StopStream or Pa_AbortStream is called. For a stream providing audio
+ output, if the stream callback returns paComplete, or Pa_StopStream is called,
+ the stream finished callback will not be called until all generated sample data
+ has been played.
+
+ @param userData The userData parameter supplied to Pa_OpenStream()
+
+ @see Pa_SetStreamFinishedCallback
+*/
+typedef void PaStreamFinishedCallback( void *userData );
+
+
+/** Register a stream finished callback function which will be called when the
+ stream becomes inactive. See the description of PaStreamFinishedCallback for
+ further details about when the callback will be called.
+
+ @param stream a pointer to a PaStream that is in the stopped state - if the
+ stream is not stopped, the stream's finished callback will remain unchanged
+ and an error code will be returned.
+
+ @param streamFinishedCallback a pointer to a function with the same signature
+ as PaStreamFinishedCallback, that will be called when the stream becomes
+ inactive. Passing NULL for this parameter will un-register a previously
+ registered stream finished callback function.
+
+ @return on success returns paNoError, otherwise an error code indicating the cause
+ of the error.
+
+ @see PaStreamFinishedCallback
+*/
+PaError Pa_SetStreamFinishedCallback( PaStream *stream, PaStreamFinishedCallback* streamFinishedCallback );
+
+
+/** Commences audio processing.
+*/
+PaError Pa_StartStream( PaStream *stream );
+
+
+/** Terminates audio processing. It waits until all pending
+ audio buffers have been played before it returns.
+*/
+PaError Pa_StopStream( PaStream *stream );
+
+
+/** Terminates audio processing immediately without waiting for pending
+ buffers to complete.
+*/
+PaError Pa_AbortStream( PaStream *stream );
+
+
+/** Determine whether the stream is stopped.
+ A stream is considered to be stopped prior to a successful call to
+ Pa_StartStream and after a successful call to Pa_StopStream or Pa_AbortStream.
+ If a stream callback returns a value other than paContinue the stream is NOT
+ considered to be stopped.
+
+ @return Returns one (1) when the stream is stopped, zero (0) when
+ the stream is running or, a PaErrorCode (which are always negative) if
+ PortAudio is not initialized or an error is encountered.
+
+ @see Pa_StopStream, Pa_AbortStream, Pa_IsStreamActive
+*/
+PaError Pa_IsStreamStopped( PaStream *stream );
+
+
+/** Determine whether the stream is active.
+ A stream is active after a successful call to Pa_StartStream(), until it
+ becomes inactive either as a result of a call to Pa_StopStream() or
+ Pa_AbortStream(), or as a result of a return value other than paContinue from
+ the stream callback. In the latter case, the stream is considered inactive
+ after the last buffer has finished playing.
+
+ @return Returns one (1) when the stream is active (ie playing or recording
+ audio), zero (0) when not playing or, a PaErrorCode (which are always negative)
+ if PortAudio is not initialized or an error is encountered.
+
+ @see Pa_StopStream, Pa_AbortStream, Pa_IsStreamStopped
+*/
+PaError Pa_IsStreamActive( PaStream *stream );
+
+
+
+/** A structure containing unchanging information about an open stream.
+ @see Pa_GetStreamInfo
+*/
+
+typedef struct PaStreamInfo
+{
+ /** this is struct version 1 */
+ int structVersion;
+
+ /** The input latency of the stream in seconds. This value provides the most
+ accurate estimate of input latency available to the implementation. It may
+ differ significantly from the suggestedLatency value passed to Pa_OpenStream().
+ The value of this field will be zero (0.) for output-only streams.
+ @see PaTime
+ */
+ PaTime inputLatency;
+
+ /** The output latency of the stream in seconds. This value provides the most
+ accurate estimate of output latency available to the implementation. It may
+ differ significantly from the suggestedLatency value passed to Pa_OpenStream().
+ The value of this field will be zero (0.) for input-only streams.
+ @see PaTime
+ */
+ PaTime outputLatency;
+
+ /** The sample rate of the stream in Hertz (samples per second). In cases
+ where the hardware sample rate is inaccurate and PortAudio is aware of it,
+ the value of this field may be different from the sampleRate parameter
+ passed to Pa_OpenStream(). If information about the actual hardware sample
+ rate is not available, this field will have the same value as the sampleRate
+ parameter passed to Pa_OpenStream().
+ */
+ double sampleRate;
+
+} PaStreamInfo;
+
+
+/** Retrieve a pointer to a PaStreamInfo structure containing information
+ about the specified stream.
+ @return A pointer to an immutable PaStreamInfo structure. If the stream
+ parameter invalid, or an error is encountered, the function returns NULL.
+
+ @param stream A pointer to an open stream previously created with Pa_OpenStream.
+
+ @note PortAudio manages the memory referenced by the returned pointer,
+ the client must not manipulate or free the memory. The pointer is only
+ guaranteed to be valid until the specified stream is closed.
+
+ @see PaStreamInfo
+*/
+const PaStreamInfo* Pa_GetStreamInfo( PaStream *stream );
+
+
+/** Determine the current time for the stream according to the same clock used
+ to generate buffer timestamps. This time may be used for syncronising other
+ events to the audio stream, for example synchronizing audio to MIDI.
+
+ @return The stream's current time in seconds, or 0 if an error occurred.
+
+ @see PaTime, PaStreamCallback
+*/
+PaTime Pa_GetStreamTime( PaStream *stream );
+
+
+/** Retrieve CPU usage information for the specified stream.
+ The "CPU Load" is a fraction of total CPU time consumed by a callback stream's
+ audio processing routines including, but not limited to the client supplied
+ stream callback. This function does not work with blocking read/write streams.
+
+ This function may be called from the stream callback function or the
+ application.
+
+ @return
+ A floating point value, typically between 0.0 and 1.0, where 1.0 indicates
+ that the stream callback is consuming the maximum number of CPU cycles possible
+ to maintain real-time operation. A value of 0.5 would imply that PortAudio and
+ the stream callback was consuming roughly 50% of the available CPU time. The
+ return value may exceed 1.0. A value of 0.0 will always be returned for a
+ blocking read/write stream, or if an error occurrs.
+*/
+double Pa_GetStreamCpuLoad( PaStream* stream );
+
+
+/** Read samples from an input stream. The function doesn't return until
+ the entire buffer has been filled - this may involve waiting for the operating
+ system to supply the data.
+
+ @param stream A pointer to an open stream previously created with Pa_OpenStream.
+
+ @param buffer A pointer to a buffer of sample frames. The buffer contains
+ samples in the format specified by the inputParameters->sampleFormat field
+ used to open the stream, and the number of channels specified by
+ inputParameters->numChannels. If non-interleaved samples were requested,
+ buffer is a pointer to the first element of an array of non-interleaved
+ buffer pointers, one for each channel.
+
+ @param frames The number of frames to be read into buffer. This parameter
+ is not constrained to a specific range, however high performance applications
+ will want to match this parameter to the framesPerBuffer parameter used
+ when opening the stream.
+
+ @return On success PaNoError will be returned, or PaInputOverflowed if input
+ data was discarded by PortAudio after the previous call and before this call.
+*/
+PaError Pa_ReadStream( PaStream* stream,
+ void *buffer,
+ unsigned long frames );
+
+
+/** Write samples to an output stream. This function doesn't return until the
+ entire buffer has been consumed - this may involve waiting for the operating
+ system to consume the data.
+
+ @param stream A pointer to an open stream previously created with Pa_OpenStream.
+
+ @param buffer A pointer to a buffer of sample frames. The buffer contains
+ samples in the format specified by the outputParameters->sampleFormat field
+ used to open the stream, and the number of channels specified by
+ outputParameters->numChannels. If non-interleaved samples were requested,
+ buffer is a pointer to the first element of an array of non-interleaved
+ buffer pointers, one for each channel.
+
+ @param frames The number of frames to be written from buffer. This parameter
+ is not constrained to a specific range, however high performance applications
+ will want to match this parameter to the framesPerBuffer parameter used
+ when opening the stream.
+
+ @return On success PaNoError will be returned, or paOutputUnderflowed if
+ additional output data was inserted after the previous call and before this
+ call.
+*/
+PaError Pa_WriteStream( PaStream* stream,
+ const void *buffer,
+ unsigned long frames );
+
+
+/** Retrieve the number of frames that can be read from the stream without
+ waiting.
+
+ @return Returns a non-negative value representing the maximum number of frames
+ that can be read from the stream without blocking or busy waiting or, a
+ PaErrorCode (which are always negative) if PortAudio is not initialized or an
+ error is encountered.
+*/
+signed long Pa_GetStreamReadAvailable( PaStream* stream );
+
+
+/** Retrieve the number of frames that can be written to the stream without
+ waiting.
+
+ @return Returns a non-negative value representing the maximum number of frames
+ that can be written to the stream without blocking or busy waiting or, a
+ PaErrorCode (which are always negative) if PortAudio is not initialized or an
+ error is encountered.
+*/
+signed long Pa_GetStreamWriteAvailable( PaStream* stream );
+
+
+/* Miscellaneous utilities */
+
+
+/** Retrieve the size of a given sample format in bytes.
+
+ @return The size in bytes of a single sample in the specified format,
+ or paSampleFormatNotSupported if the format is not supported.
+*/
+PaError Pa_GetSampleSize( PaSampleFormat format );
+
+
+/** Put the caller to sleep for at least 'msec' milliseconds. This function is
+ provided only as a convenience for authors of portable code (such as the tests
+ and examples in the PortAudio distribution.)
+
+ The function may sleep longer than requested so don't rely on this for accurate
+ musical timing.
+*/
+void Pa_Sleep( long msec );
+
+
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+#endif /* PORTAUDIO_H */
diff --git a/pjmedia/src/test/audio_tool.c b/pjmedia/src/test/audio_tool.c
index 7a8e649f..074802be 100644
--- a/pjmedia/src/test/audio_tool.c
+++ b/pjmedia/src/test/audio_tool.c
@@ -1,409 +1,409 @@
-/* $Id$ */
-/*
- * Copyright (C) 2003-2006 Benny Prijono <benny@prijono.org>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
-#include <pjmedia.h>
-#include <pjlib.h>
-#include <stdio.h>
-
-#define THIS_FILE "audio_tool.c"
-
-static pj_caching_pool caching_pool;
-static pj_pool_factory *pf;
-static FILE *fhnd;
-static pj_med_mgr_t *mm;
-static pj_codec *codec;
-static pj_codec_attr cattr;
-
-
-#define WRITE_ORIGINAL_PCM 0
-#if WRITE_ORIGINAL_PCM
-static FILE *fhnd_pcm;
-#endif
-
-static char talker_sdp[] =
- "v=0\r\n"
- "o=- 0 0 IN IP4 127.0.0.1\r\n"
- "s=-\r\n"
- "c=IN IP4 127.0.0.1\r\n"
- "t=0 0\r\n"
- "m=audio 4002 RTP/AVP 0\r\n"
- "a=rtpmap:0 PCMU/8000\r\n"
- "a=sendonly\r\n";
-static char listener_sdp[] =
- "v=0\r\n"
- "o=- 0 0 IN IP4 127.0.0.1\r\n"
- "s=-\r\n"
- "c=IN IP4 127.0.0.1\r\n"
- "t=0 0\r\n"
- "m=audio 4000 RTP/AVP 0\r\n"
- "a=rtpmap:0 PCMU/8000\r\n"
- "a=recvonly\r\n";
-
-static pj_status_t play_callback(/* in */ void *user_data,
- /* in */ pj_uint32_t timestamp,
- /* out */ void *frame,
- /* out */ unsigned size)
-{
- char pkt[160];
- struct pj_audio_frame in, out;
- int frmsz = cattr.avg_bps / 8 * cattr.ptime / 1000;
-
- if (fread(pkt, frmsz, 1, fhnd) != 1) {
- puts("EOF");
- return -1;
- } else {
- in.type = PJ_AUDIO_FRAME_AUDIO;
- in.buf = pkt;
- in.size = frmsz;
- out.buf = frame;
- if (codec->op->decode (codec, &in, size, &out) != 0)
- return -1;
-
- size = out.size;
- return 0;
- }
-}
-
-static pj_status_t rec_callback( /* in */ void *user_data,
- /* in */ pj_uint32_t timestamp,
- /* in */ const void *frame,
- /* in*/ unsigned size)
-{
- char pkt[160];
- struct pj_audio_frame in, out;
- //int frmsz = cattr.avg_bps / 8 * cattr.ptime / 1000;
-
-#if WRITE_ORIGINAL_PCM
- fwrite(frame, size, 1, fhnd_pcm);
-#endif
-
- in.type = PJ_AUDIO_FRAME_AUDIO;
- in.buf = (void*)frame;
- in.size = size;
- out.buf = pkt;
-
- if (codec->op->encode(codec, &in, sizeof(pkt), &out) != 0)
- return -1;
-
- if (fwrite(pkt, out.size, 1, fhnd) != 1)
- return -1;
- return 0;
-}
-
-static pj_status_t init()
-{
- pj_codec_mgr *cm;
- pj_codec_id id;
- int i;
-
- pj_caching_pool_init(&caching_pool, &pj_pool_factory_default_policy, 0);
- pf = &caching_pool.factory;
-
- if (pj_snd_init(&caching_pool.factory))
- return -1;
-
- PJ_LOG(3,(THIS_FILE, "Dumping audio devices:"));
- for (i=0; i<pj_snd_get_dev_count(); ++i) {
- const pj_snd_dev_info *info;
- info = pj_snd_get_dev_info(i);
- PJ_LOG(3,(THIS_FILE, " %d: %s\t(%d in, %d out",
- i, info->name,
- info->input_count, info->output_count));
- }
-
- mm = pj_med_mgr_create (&caching_pool.factory);
- cm = pj_med_mgr_get_codec_mgr (mm);
-
- id.type = PJ_MEDIA_TYPE_AUDIO;
- id.pt = 0;
- id.encoding_name = pj_str("PCMU");
- id.sample_rate = 8000;
-
- codec = pj_codec_mgr_alloc_codec (cm, &id);
- codec->op->default_attr(codec, &cattr);
- codec->op->open(codec, &cattr);
- return 0;
-}
-
-static pj_status_t deinit()
-{
- pj_codec_mgr *cm;
- cm = pj_med_mgr_get_codec_mgr (mm);
- codec->op->close(codec);
- pj_codec_mgr_dealloc_codec (cm, codec);
- pj_med_mgr_destroy (mm);
- pj_caching_pool_destroy(&caching_pool);
- return 0;
-}
-
-static pj_status_t record_file (const char *filename)
-{
- pj_snd_stream *stream;
- pj_snd_stream_info info;
- int status;
- char s[10];
-
- printf("Recording to file %s...\n", filename);
-
- fhnd = fopen(filename, "wb");
- if (!fhnd)
- return -1;
-
-#if WRITE_ORIGINAL_PCM
- fhnd_pcm = fopen("ORIGINAL.PCM", "wb");
- if (!fhnd_pcm)
- return -1;
-#endif
-
- pj_memset(&info, 0, sizeof(info));
- info.bits_per_sample = 16;
- info.bytes_per_frame = 2;
- info.frames_per_packet = 160;
- info.samples_per_frame = 1;
- info.samples_per_sec = 8000;
-
- stream = pj_snd_open_recorder(-1, &info, &rec_callback, NULL);
- if (!stream)
- return -1;
-
- status = pj_snd_stream_start(stream);
- if (status != 0)
- goto on_error;
-
- puts("Press <ENTER> to exit recording");
- fgets(s, sizeof(s), stdin);
-
- pj_snd_stream_stop(stream);
- pj_snd_stream_close(stream);
-
-#if WRITE_ORIGINAL_PCM
- fclose(fhnd_pcm);
-#endif
- fclose(fhnd);
- return 0;
-
-on_error:
- pj_snd_stream_stop(stream);
- pj_snd_stream_close(stream);
- return -1;
-}
-
-
-static pj_status_t play_file (const char *filename)
-{
- pj_snd_stream *stream;
- pj_snd_stream_info info;
- int status;
- char s[10];
-
- printf("Playing file %s...\n", filename);
-
- fhnd = fopen(filename, "rb");
- if (!fhnd)
- return -1;
-
- pj_memset(&info, 0, sizeof(info));
- info.bits_per_sample = 16;
- info.bytes_per_frame = 2;
- info.frames_per_packet = 160;
- info.samples_per_frame = 1;
- info.samples_per_sec = 8000;
-
- stream = pj_snd_open_player(-1, &info, &play_callback, NULL);
- if (!stream)
- return -1;
-
- status = pj_snd_stream_start(stream);
- if (status != 0)
- goto on_error;
-
- puts("Press <ENTER> to exit playing");
- fgets(s, sizeof(s), stdin);
-
- pj_snd_stream_stop(stream);
- pj_snd_stream_close(stream);
-
- fclose(fhnd);
- return 0;
-
-on_error:
- pj_snd_stream_stop(stream);
- pj_snd_stream_close(stream);
- return -1;
-}
-
-static int create_ses_by_remote_sdp(int local_port, char *sdp)
-{
- pj_media_session_t *ses = NULL;
- pjsdp_session_desc *sdp_ses;
- pj_media_sock_info skinfo;
- pj_pool_t *pool;
- char s[4];
- const pj_media_stream_info *info[2];
- int i, count;
-
- pool = pj_pool_create(pf, "sdp", 1024, 0, NULL);
- if (!pool) {
- PJ_LOG(1,(THIS_FILE, "Unable to create pool"));
- return -1;
- }
-
- pj_memset(&skinfo, 0, sizeof(skinfo));
- skinfo.rtp_sock = skinfo.rtcp_sock = pj_sock_socket(PJ_AF_INET, PJ_SOCK_DGRAM, 0, 0);
- if (skinfo.rtp_sock == PJ_INVALID_SOCKET) {
- PJ_LOG(1,(THIS_FILE, "Unable to create socket"));
- goto on_error;
- }
-
- pj_sockaddr_init2(&skinfo.rtp_addr_name, "0.0.0.0", local_port);
- if (pj_sock_bind(skinfo.rtp_sock, (struct pj_sockaddr*)&skinfo.rtp_addr_name, sizeof(pj_sockaddr_in)) != 0) {
- PJ_LOG(1,(THIS_FILE, "Unable to bind socket"));
- goto on_error;
- }
-
- sdp_ses = pjsdp_parse(sdp, strlen(sdp), pool);
- if (!sdp_ses) {
- PJ_LOG(1,(THIS_FILE, "Error parsing SDP"));
- goto on_error;
- }
-
- ses = pj_media_session_create_from_sdp(mm, sdp_ses, &skinfo);
- if (!ses) {
- PJ_LOG(1,(THIS_FILE, "Unable to create session from SDP"));
- goto on_error;
- }
-
- if (pj_media_session_activate(ses) != 0) {
- PJ_LOG(1,(THIS_FILE, "Error activating session"));
- goto on_error;
- }
-
- count = pj_media_session_enum_streams(ses, 2, info);
- printf("\nDumping streams: \n");
- for (i=0; i<count; ++i) {
- const char *dir;
- char *local_ip;
-
- switch (info[i]->dir) {
- case PJ_MEDIA_DIR_NONE:
- dir = "- NONE -"; break;
- case PJ_MEDIA_DIR_ENCODING:
- dir = "SENDONLY"; break;
- case PJ_MEDIA_DIR_DECODING:
- dir = "RECVONLY"; break;
- case PJ_MEDIA_DIR_ENCODING_DECODING:
- dir = "SENDRECV"; break;
- default:
- dir = "?UNKNOWN"; break;
- }
-
- local_ip = pj_sockaddr_get_str_addr(&info[i]->sock_info.rtp_addr_name);
-
- printf(" Stream %d: %.*s %s local=%s:%d remote=%.*s:%d\n",
- i, info[i]->type.slen, info[i]->type.ptr,
- dir,
- local_ip, pj_sockaddr_get_port(&info[i]->sock_info.rtp_addr_name),
- info[i]->rem_addr.slen, info[i]->rem_addr.ptr, info[i]->rem_port);
- }
-
- puts("Press <ENTER> to quit");
- fgets(s, sizeof(s), stdin);
-
- pj_media_session_destroy(ses);
- pj_sock_close(skinfo.rtp_sock);
- pj_pool_release(pool);
-
- return 0;
-
-on_error:
- if (ses)
- pj_media_session_destroy(ses);
- if (skinfo.rtp_sock != PJ_INVALID_SOCKET)
- pj_sock_close(skinfo.rtp_sock);
- if (pool)
- pj_pool_release(pool);
- return -1;
-}
-
-#if WRITE_ORIGINAL_PCM
-static pj_status_t convert(const char *src, const char *dst)
-{
- char pcm[320];
- char frame[160];
- struct pj_audio_frame in, out;
-
- fhnd_pcm = fopen(src, "rb");
- if (!fhnd_pcm)
- return -1;
- fhnd = fopen(dst, "wb");
- if (!fhnd)
- return -1;
-
- while (fread(pcm, 320, 1, fhnd_pcm) == 1) {
-
- in.type = PJ_AUDIO_FRAME_AUDIO;
- in.buf = pcm;
- in.size = 320;
- out.buf = frame;
-
- if (codec->op->encode(codec, &in, 160, &out) != 0)
- break;
-
- if (fwrite(frame, out.size, 1, fhnd) != 1)
- break;
-
- }
-
- fclose(fhnd);
- fclose(fhnd_pcm);
- return 0;
-}
-#endif
-
-static void usage(const char *exe)
-{
- printf("Usage: %s <command> <file>\n", exe);
- puts("where:");
- puts(" <command> play|record|send|recv");
-}
-
-int main(int argc, char *argv[])
-{
- if (argc < 2) {
- usage(argv[0]);
- return 1;
- }
-
- pj_init();
-
- init();
-
- if (stricmp(argv[1], "record")==0) {
- record_file("FILE.PCM");
- } else if (stricmp(argv[1], "play")==0) {
- play_file("FILE.PCM");
- } else if (stricmp(argv[1], "send")==0) {
- create_ses_by_remote_sdp(4002, listener_sdp);
- } else if (stricmp(argv[1], "recv")==0) {
- create_ses_by_remote_sdp(4000, talker_sdp);
- } else {
- usage(argv[0]);
- }
- deinit();
- return 0;
-}
+/* $Id$ */
+/*
+ * Copyright (C) 2003-2006 Benny Prijono <benny@prijono.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#include <pjmedia.h>
+#include <pjlib.h>
+#include <stdio.h>
+
+#define THIS_FILE "audio_tool.c"
+
+static pj_caching_pool caching_pool;
+static pj_pool_factory *pf;
+static FILE *fhnd;
+static pj_med_mgr_t *mm;
+static pj_codec *codec;
+static pj_codec_attr cattr;
+
+
+#define WRITE_ORIGINAL_PCM 0
+#if WRITE_ORIGINAL_PCM
+static FILE *fhnd_pcm;
+#endif
+
+static char talker_sdp[] =
+ "v=0\r\n"
+ "o=- 0 0 IN IP4 127.0.0.1\r\n"
+ "s=-\r\n"
+ "c=IN IP4 127.0.0.1\r\n"
+ "t=0 0\r\n"
+ "m=audio 4002 RTP/AVP 0\r\n"
+ "a=rtpmap:0 PCMU/8000\r\n"
+ "a=sendonly\r\n";
+static char listener_sdp[] =
+ "v=0\r\n"
+ "o=- 0 0 IN IP4 127.0.0.1\r\n"
+ "s=-\r\n"
+ "c=IN IP4 127.0.0.1\r\n"
+ "t=0 0\r\n"
+ "m=audio 4000 RTP/AVP 0\r\n"
+ "a=rtpmap:0 PCMU/8000\r\n"
+ "a=recvonly\r\n";
+
+static pj_status_t play_callback(/* in */ void *user_data,
+ /* in */ pj_uint32_t timestamp,
+ /* out */ void *frame,
+ /* out */ unsigned size)
+{
+ char pkt[160];
+ struct pj_audio_frame in, out;
+ int frmsz = cattr.avg_bps / 8 * cattr.ptime / 1000;
+
+ if (fread(pkt, frmsz, 1, fhnd) != 1) {
+ puts("EOF");
+ return -1;
+ } else {
+ in.type = PJ_AUDIO_FRAME_AUDIO;
+ in.buf = pkt;
+ in.size = frmsz;
+ out.buf = frame;
+ if (codec->op->decode (codec, &in, size, &out) != 0)
+ return -1;
+
+ size = out.size;
+ return 0;
+ }
+}
+
+static pj_status_t rec_callback( /* in */ void *user_data,
+ /* in */ pj_uint32_t timestamp,
+ /* in */ const void *frame,
+ /* in*/ unsigned size)
+{
+ char pkt[160];
+ struct pj_audio_frame in, out;
+ //int frmsz = cattr.avg_bps / 8 * cattr.ptime / 1000;
+
+#if WRITE_ORIGINAL_PCM
+ fwrite(frame, size, 1, fhnd_pcm);
+#endif
+
+ in.type = PJ_AUDIO_FRAME_AUDIO;
+ in.buf = (void*)frame;
+ in.size = size;
+ out.buf = pkt;
+
+ if (codec->op->encode(codec, &in, sizeof(pkt), &out) != 0)
+ return -1;
+
+ if (fwrite(pkt, out.size, 1, fhnd) != 1)
+ return -1;
+ return 0;
+}
+
+static pj_status_t init()
+{
+ pj_codec_mgr *cm;
+ pj_codec_id id;
+ int i;
+
+ pj_caching_pool_init(&caching_pool, &pj_pool_factory_default_policy, 0);
+ pf = &caching_pool.factory;
+
+ if (pj_snd_init(&caching_pool.factory))
+ return -1;
+
+ PJ_LOG(3,(THIS_FILE, "Dumping audio devices:"));
+ for (i=0; i<pj_snd_get_dev_count(); ++i) {
+ const pj_snd_dev_info *info;
+ info = pj_snd_get_dev_info(i);
+ PJ_LOG(3,(THIS_FILE, " %d: %s\t(%d in, %d out",
+ i, info->name,
+ info->input_count, info->output_count));
+ }
+
+ mm = pj_med_mgr_create (&caching_pool.factory);
+ cm = pj_med_mgr_get_codec_mgr (mm);
+
+ id.type = PJ_MEDIA_TYPE_AUDIO;
+ id.pt = 0;
+ id.encoding_name = pj_str("PCMU");
+ id.sample_rate = 8000;
+
+ codec = pj_codec_mgr_alloc_codec (cm, &id);
+ codec->op->default_attr(codec, &cattr);
+ codec->op->open(codec, &cattr);
+ return 0;
+}
+
+static pj_status_t deinit()
+{
+ pj_codec_mgr *cm;
+ cm = pj_med_mgr_get_codec_mgr (mm);
+ codec->op->close(codec);
+ pj_codec_mgr_dealloc_codec (cm, codec);
+ pj_med_mgr_destroy (mm);
+ pj_caching_pool_destroy(&caching_pool);
+ return 0;
+}
+
+static pj_status_t record_file (const char *filename)
+{
+ pj_snd_stream *stream;
+ pj_snd_stream_info info;
+ int status;
+ char s[10];
+
+ printf("Recording to file %s...\n", filename);
+
+ fhnd = fopen(filename, "wb");
+ if (!fhnd)
+ return -1;
+
+#if WRITE_ORIGINAL_PCM
+ fhnd_pcm = fopen("ORIGINAL.PCM", "wb");
+ if (!fhnd_pcm)
+ return -1;
+#endif
+
+ pj_memset(&info, 0, sizeof(info));
+ info.bits_per_sample = 16;
+ info.bytes_per_frame = 2;
+ info.frames_per_packet = 160;
+ info.samples_per_frame = 1;
+ info.samples_per_sec = 8000;
+
+ stream = pj_snd_open_recorder(-1, &info, &rec_callback, NULL);
+ if (!stream)
+ return -1;
+
+ status = pj_snd_stream_start(stream);
+ if (status != 0)
+ goto on_error;
+
+ puts("Press <ENTER> to exit recording");
+ fgets(s, sizeof(s), stdin);
+
+ pj_snd_stream_stop(stream);
+ pj_snd_stream_close(stream);
+
+#if WRITE_ORIGINAL_PCM
+ fclose(fhnd_pcm);
+#endif
+ fclose(fhnd);
+ return 0;
+
+on_error:
+ pj_snd_stream_stop(stream);
+ pj_snd_stream_close(stream);
+ return -1;
+}
+
+
+static pj_status_t play_file (const char *filename)
+{
+ pj_snd_stream *stream;
+ pj_snd_stream_info info;
+ int status;
+ char s[10];
+
+ printf("Playing file %s...\n", filename);
+
+ fhnd = fopen(filename, "rb");
+ if (!fhnd)
+ return -1;
+
+ pj_memset(&info, 0, sizeof(info));
+ info.bits_per_sample = 16;
+ info.bytes_per_frame = 2;
+ info.frames_per_packet = 160;
+ info.samples_per_frame = 1;
+ info.samples_per_sec = 8000;
+
+ stream = pj_snd_open_player(-1, &info, &play_callback, NULL);
+ if (!stream)
+ return -1;
+
+ status = pj_snd_stream_start(stream);
+ if (status != 0)
+ goto on_error;
+
+ puts("Press <ENTER> to exit playing");
+ fgets(s, sizeof(s), stdin);
+
+ pj_snd_stream_stop(stream);
+ pj_snd_stream_close(stream);
+
+ fclose(fhnd);
+ return 0;
+
+on_error:
+ pj_snd_stream_stop(stream);
+ pj_snd_stream_close(stream);
+ return -1;
+}
+
+static int create_ses_by_remote_sdp(int local_port, char *sdp)
+{
+ pj_media_session_t *ses = NULL;
+ pjsdp_session_desc *sdp_ses;
+ pj_media_sock_info skinfo;
+ pj_pool_t *pool;
+ char s[4];
+ const pj_media_stream_info *info[2];
+ int i, count;
+
+ pool = pj_pool_create(pf, "sdp", 1024, 0, NULL);
+ if (!pool) {
+ PJ_LOG(1,(THIS_FILE, "Unable to create pool"));
+ return -1;
+ }
+
+ pj_memset(&skinfo, 0, sizeof(skinfo));
+ skinfo.rtp_sock = skinfo.rtcp_sock = pj_sock_socket(PJ_AF_INET, PJ_SOCK_DGRAM, 0, 0);
+ if (skinfo.rtp_sock == PJ_INVALID_SOCKET) {
+ PJ_LOG(1,(THIS_FILE, "Unable to create socket"));
+ goto on_error;
+ }
+
+ pj_sockaddr_init2(&skinfo.rtp_addr_name, "0.0.0.0", local_port);
+ if (pj_sock_bind(skinfo.rtp_sock, (struct pj_sockaddr*)&skinfo.rtp_addr_name, sizeof(pj_sockaddr_in)) != 0) {
+ PJ_LOG(1,(THIS_FILE, "Unable to bind socket"));
+ goto on_error;
+ }
+
+ sdp_ses = pjsdp_parse(sdp, strlen(sdp), pool);
+ if (!sdp_ses) {
+ PJ_LOG(1,(THIS_FILE, "Error parsing SDP"));
+ goto on_error;
+ }
+
+ ses = pj_media_session_create_from_sdp(mm, sdp_ses, &skinfo);
+ if (!ses) {
+ PJ_LOG(1,(THIS_FILE, "Unable to create session from SDP"));
+ goto on_error;
+ }
+
+ if (pj_media_session_activate(ses) != 0) {
+ PJ_LOG(1,(THIS_FILE, "Error activating session"));
+ goto on_error;
+ }
+
+ count = pj_media_session_enum_streams(ses, 2, info);
+ printf("\nDumping streams: \n");
+ for (i=0; i<count; ++i) {
+ const char *dir;
+ char *local_ip;
+
+ switch (info[i]->dir) {
+ case PJ_MEDIA_DIR_NONE:
+ dir = "- NONE -"; break;
+ case PJ_MEDIA_DIR_ENCODING:
+ dir = "SENDONLY"; break;
+ case PJ_MEDIA_DIR_DECODING:
+ dir = "RECVONLY"; break;
+ case PJ_MEDIA_DIR_ENCODING_DECODING:
+ dir = "SENDRECV"; break;
+ default:
+ dir = "?UNKNOWN"; break;
+ }
+
+ local_ip = pj_sockaddr_get_str_addr(&info[i]->sock_info.rtp_addr_name);
+
+ printf(" Stream %d: %.*s %s local=%s:%d remote=%.*s:%d\n",
+ i, info[i]->type.slen, info[i]->type.ptr,
+ dir,
+ local_ip, pj_sockaddr_get_port(&info[i]->sock_info.rtp_addr_name),
+ info[i]->rem_addr.slen, info[i]->rem_addr.ptr, info[i]->rem_port);
+ }
+
+ puts("Press <ENTER> to quit");
+ fgets(s, sizeof(s), stdin);
+
+ pj_media_session_destroy(ses);
+ pj_sock_close(skinfo.rtp_sock);
+ pj_pool_release(pool);
+
+ return 0;
+
+on_error:
+ if (ses)
+ pj_media_session_destroy(ses);
+ if (skinfo.rtp_sock != PJ_INVALID_SOCKET)
+ pj_sock_close(skinfo.rtp_sock);
+ if (pool)
+ pj_pool_release(pool);
+ return -1;
+}
+
+#if WRITE_ORIGINAL_PCM
+static pj_status_t convert(const char *src, const char *dst)
+{
+ char pcm[320];
+ char frame[160];
+ struct pj_audio_frame in, out;
+
+ fhnd_pcm = fopen(src, "rb");
+ if (!fhnd_pcm)
+ return -1;
+ fhnd = fopen(dst, "wb");
+ if (!fhnd)
+ return -1;
+
+ while (fread(pcm, 320, 1, fhnd_pcm) == 1) {
+
+ in.type = PJ_AUDIO_FRAME_AUDIO;
+ in.buf = pcm;
+ in.size = 320;
+ out.buf = frame;
+
+ if (codec->op->encode(codec, &in, 160, &out) != 0)
+ break;
+
+ if (fwrite(frame, out.size, 1, fhnd) != 1)
+ break;
+
+ }
+
+ fclose(fhnd);
+ fclose(fhnd_pcm);
+ return 0;
+}
+#endif
+
+static void usage(const char *exe)
+{
+ printf("Usage: %s <command> <file>\n", exe);
+ puts("where:");
+ puts(" <command> play|record|send|recv");
+}
+
+int main(int argc, char *argv[])
+{
+ if (argc < 2) {
+ usage(argv[0]);
+ return 1;
+ }
+
+ pj_init();
+
+ init();
+
+ if (stricmp(argv[1], "record")==0) {
+ record_file("FILE.PCM");
+ } else if (stricmp(argv[1], "play")==0) {
+ play_file("FILE.PCM");
+ } else if (stricmp(argv[1], "send")==0) {
+ create_ses_by_remote_sdp(4002, listener_sdp);
+ } else if (stricmp(argv[1], "recv")==0) {
+ create_ses_by_remote_sdp(4000, talker_sdp);
+ } else {
+ usage(argv[0]);
+ }
+ deinit();
+ return 0;
+}
diff --git a/pjmedia/src/test/jbuf_test.c b/pjmedia/src/test/jbuf_test.c
index 6981fc7e..1e054fb8 100644
--- a/pjmedia/src/test/jbuf_test.c
+++ b/pjmedia/src/test/jbuf_test.c
@@ -1,154 +1,154 @@
-/* $Id$ */
-/*
- * Copyright (C) 2003-2006 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 <stdio.h>
-#include <ctype.h>
-#include <pjmedia/jbuf.h>
-#include <pj/pool.h>
-
-#define JB_MIN 1
-#define JB_MAX 8
-#define JB_BUF_SIZE 10
-
-#define REPORT
-//#define PRINT_COMMENT
-
-int jbuf_main(pj_pool_factory *pf)
-{
- pj_jitter_buffer jb;
- FILE *input = fopen("JBTEST.DAT", "rt");
- unsigned lastseq;
- void *data = "Hello world";
- char line[1024], *p;
- int lastget = 0, lastput = 0;
- pj_pool_t *pool;
-
- pj_init();
- pool = pj_pool_create(pf, "JBPOOL", 256*16, 256*16, NULL);
-
- pj_jb_init(&jb, pool, JB_MIN, JB_MAX, JB_BUF_SIZE);
-
- lastseq = 1;
-
- while ((p=fgets(line, sizeof(line), input)) != NULL) {
-
- while (*p && isspace(*p))
- ++p;
-
- if (!*p)
- continue;
-
- if (*p == '#') {
-#ifdef PRINT_COMMENT
- printf("\n%s", p);
-#endif
- continue;
- }
-
- pj_jb_reset(&jb);
-#ifdef REPORT
- printf( "Initial\t%c size=%d prefetch=%d level=%d\n",
- ' ', jb.lst.count, jb.prefetch, jb.level);
-#endif
-
- while (*p) {
- int c;
- unsigned seq = 0;
- void *thedata;
- int status = 1234;
-
- c = *p++;
- if (isspace(c))
- continue;
-
- if (c == '/') {
- char *end;
-
- printf("/*");
-
- do {
- putchar(*++p);
- } while (*p != '/');
-
- putchar('\n');
-
- c = *++p;
- end = p;
-
- }
-
- if (isspace(c))
- continue;
-
- if (isdigit(c)) {
- seq = c - '0';
- while (*p) {
- c = *p++;
-
- if (isspace(c))
- continue;
-
- if (!isdigit(c))
- break;
-
- seq = seq * 10 + c - '0';
- }
- }
-
- if (!*p)
- break;
-
- switch (toupper(c)) {
- case 'G':
- seq = -1;
- status = pj_jb_get(&jb, &seq, &thedata);
- lastget = seq;
- break;
- case 'P':
- if (seq == 0)
- seq = lastseq++;
- else
- lastseq = seq;
- status = pj_jb_put(&jb, seq, data);
- if (status == 0)
- lastput = seq;
- break;
- default:
- printf("Unknown character '%c'\n", c);
- break;
- }
-
-#ifdef REPORT
- printf("seq=%d\t%c rc=%d\tsize=%d\tpfch=%d\tlvl=%d\tmxl=%d\tdelay=%d\n",
- seq, toupper(c), status, jb.lst.count, jb.prefetch, jb.level, jb.max_level,
- (lastget>0 && lastput>0) ? lastput-lastget : -1);
-#endif
- }
- }
-
-#ifdef REPORT
- printf("0\t%c size=%d prefetch=%d level=%d\n",
- ' ', jb.lst.count, jb.prefetch, jb.level);
-#endif
-
- if (input != stdin)
- fclose(input);
-
- pj_pool_release(pool);
- return 0;
-}
+/* $Id$ */
+/*
+ * Copyright (C) 2003-2006 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 <stdio.h>
+#include <ctype.h>
+#include <pjmedia/jbuf.h>
+#include <pj/pool.h>
+
+#define JB_MIN 1
+#define JB_MAX 8
+#define JB_BUF_SIZE 10
+
+#define REPORT
+//#define PRINT_COMMENT
+
+int jbuf_main(pj_pool_factory *pf)
+{
+ pj_jitter_buffer jb;
+ FILE *input = fopen("JBTEST.DAT", "rt");
+ unsigned lastseq;
+ void *data = "Hello world";
+ char line[1024], *p;
+ int lastget = 0, lastput = 0;
+ pj_pool_t *pool;
+
+ pj_init();
+ pool = pj_pool_create(pf, "JBPOOL", 256*16, 256*16, NULL);
+
+ pj_jb_init(&jb, pool, JB_MIN, JB_MAX, JB_BUF_SIZE);
+
+ lastseq = 1;
+
+ while ((p=fgets(line, sizeof(line), input)) != NULL) {
+
+ while (*p && isspace(*p))
+ ++p;
+
+ if (!*p)
+ continue;
+
+ if (*p == '#') {
+#ifdef PRINT_COMMENT
+ printf("\n%s", p);
+#endif
+ continue;
+ }
+
+ pj_jb_reset(&jb);
+#ifdef REPORT
+ printf( "Initial\t%c size=%d prefetch=%d level=%d\n",
+ ' ', jb.lst.count, jb.prefetch, jb.level);
+#endif
+
+ while (*p) {
+ int c;
+ unsigned seq = 0;
+ void *thedata;
+ int status = 1234;
+
+ c = *p++;
+ if (isspace(c))
+ continue;
+
+ if (c == '/') {
+ char *end;
+
+ printf("/*");
+
+ do {
+ putchar(*++p);
+ } while (*p != '/');
+
+ putchar('\n');
+
+ c = *++p;
+ end = p;
+
+ }
+
+ if (isspace(c))
+ continue;
+
+ if (isdigit(c)) {
+ seq = c - '0';
+ while (*p) {
+ c = *p++;
+
+ if (isspace(c))
+ continue;
+
+ if (!isdigit(c))
+ break;
+
+ seq = seq * 10 + c - '0';
+ }
+ }
+
+ if (!*p)
+ break;
+
+ switch (toupper(c)) {
+ case 'G':
+ seq = -1;
+ status = pj_jb_get(&jb, &seq, &thedata);
+ lastget = seq;
+ break;
+ case 'P':
+ if (seq == 0)
+ seq = lastseq++;
+ else
+ lastseq = seq;
+ status = pj_jb_put(&jb, seq, data);
+ if (status == 0)
+ lastput = seq;
+ break;
+ default:
+ printf("Unknown character '%c'\n", c);
+ break;
+ }
+
+#ifdef REPORT
+ printf("seq=%d\t%c rc=%d\tsize=%d\tpfch=%d\tlvl=%d\tmxl=%d\tdelay=%d\n",
+ seq, toupper(c), status, jb.lst.count, jb.prefetch, jb.level, jb.max_level,
+ (lastget>0 && lastput>0) ? lastput-lastget : -1);
+#endif
+ }
+ }
+
+#ifdef REPORT
+ printf("0\t%c size=%d prefetch=%d level=%d\n",
+ ' ', jb.lst.count, jb.prefetch, jb.level);
+#endif
+
+ if (input != stdin)
+ fclose(input);
+
+ pj_pool_release(pool);
+ return 0;
+}
diff --git a/pjmedia/src/test/main.c b/pjmedia/src/test/main.c
index 7140c0c3..31ca3c32 100644
--- a/pjmedia/src/test/main.c
+++ b/pjmedia/src/test/main.c
@@ -1,42 +1,42 @@
-/* $Id$ */
-/*
- * Copyright (C) 2003-2006 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 <pj/os.h>
-#include <pj/pool.h>
-#include <pjmedia/sound.h>
-
-pj_status_t session_test (pj_pool_factory *pf);
-pj_status_t rtp_test (pj_pool_factory *pf);
-pj_status_t sdp_test(pj_pool_factory *pf);
-int jbuf_main(pj_pool_factory *pf);
-
-int main()
-{
- pj_caching_pool caching_pool;
-
- pj_init();
- pj_caching_pool_init(&caching_pool, &pj_pool_factory_default_policy, 0);
-
- sdp_test (&caching_pool.factory);
- rtp_test(&caching_pool.factory);
- session_test (&caching_pool.factory);
- //jbuf_main(&caching_pool.factory);
-
- pj_caching_pool_destroy(&caching_pool);
- return 0;
-}
+/* $Id$ */
+/*
+ * Copyright (C) 2003-2006 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 <pj/os.h>
+#include <pj/pool.h>
+#include <pjmedia/sound.h>
+
+pj_status_t session_test (pj_pool_factory *pf);
+pj_status_t rtp_test (pj_pool_factory *pf);
+pj_status_t sdp_test(pj_pool_factory *pf);
+int jbuf_main(pj_pool_factory *pf);
+
+int main()
+{
+ pj_caching_pool caching_pool;
+
+ pj_init();
+ pj_caching_pool_init(&caching_pool, &pj_pool_factory_default_policy, 0);
+
+ sdp_test (&caching_pool.factory);
+ rtp_test(&caching_pool.factory);
+ session_test (&caching_pool.factory);
+ //jbuf_main(&caching_pool.factory);
+
+ pj_caching_pool_destroy(&caching_pool);
+ return 0;
+}
diff --git a/pjmedia/src/test/rtp_test.c b/pjmedia/src/test/rtp_test.c
index 3d47e428..db916236 100644
--- a/pjmedia/src/test/rtp_test.c
+++ b/pjmedia/src/test/rtp_test.c
@@ -1,37 +1,37 @@
-/* $Id$ */
-/*
- * Copyright (C) 2003-2006 Benny Prijono <benny@prijono.org>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
-#include <pjmedia/rtp.h>
-#include <stdio.h>
-
-int rtp_test()
-{
- pj_rtp_session rtp;
- FILE *fhnd = fopen("RTP.DAT", "wb");
- const void *rtphdr;
- int hdrlen;
-
- if (!fhnd)
- return -1;
-
- pj_rtp_session_init (&rtp, 4, 0x12345678);
- pj_rtp_encode_rtp (&rtp, 4, 0, 0, 160, &rtphdr, &hdrlen);
- fwrite (rtphdr, hdrlen, 1, fhnd);
- fclose(fhnd);
- return 0;
-}
+/* $Id$ */
+/*
+ * Copyright (C) 2003-2006 Benny Prijono <benny@prijono.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#include <pjmedia/rtp.h>
+#include <stdio.h>
+
+int rtp_test()
+{
+ pj_rtp_session rtp;
+ FILE *fhnd = fopen("RTP.DAT", "wb");
+ const void *rtphdr;
+ int hdrlen;
+
+ if (!fhnd)
+ return -1;
+
+ pj_rtp_session_init (&rtp, 4, 0x12345678);
+ pj_rtp_encode_rtp (&rtp, 4, 0, 0, 160, &rtphdr, &hdrlen);
+ fwrite (rtphdr, hdrlen, 1, fhnd);
+ fclose(fhnd);
+ return 0;
+}
diff --git a/pjmedia/src/test/sdptest.c b/pjmedia/src/test/sdptest.c
index f185b9a9..681e8887 100644
--- a/pjmedia/src/test/sdptest.c
+++ b/pjmedia/src/test/sdptest.c
@@ -1,121 +1,121 @@
-/* $Id$ */
-/*
- * Copyright (C) 2003-2006 Benny Prijono <benny@prijono.org>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
-#include <pjmedia/sdp.h>
-#include <pj/os.h>
-#include <pj/pool.h>
-#include <stdio.h>
-#include <string.h>
-
-static char *sdp[] = {
- /*
- "v=0\r\n"
- "o=mhandley 2890844526 2890842807 IN IP4 126.16.64.4\r\n"
- "s=SDP Seminar\r\n"
- "i=A Seminar on the session description protocol\r\n"
- "u=http://www.cs.ucl.ac.uk/staff/M.Handley/sdp.03.ps\r\n"
- "e=mjh@isi.edu (Mark Handley)\r\n"
- "c=IN IP4 224.2.17.12/127\r\n"
- "t=2873397496 2873404696\r\n"
- "a=recvonly\r\n"
- "m=audio 49170 RTP/AVP 0\r\n"
- "m=video 51372 RTP/AVP 31\r\n"
- "m=application 32416 udp wb\r\n"
- "a=orient:portrait\r\n"
- "m=audio 49230 RTP/AVP 96 97 98\r\n"
- "a=rtpmap:96 L8/8000\r\n"
- "a=rtpmap:97 L16/8000\r\n"
- "a=rtpmap:98 L16/11025/2\r\n",
- */
- "v=0\r\n"
- "o=usera 2890844526 2890844527 IN IP4 alice.example.com\r\n"
- "s=\r\n"
- "c=IN IP4 alice.example.com\r\n"
- "t=0 0\r\n"
- "m=message 7394 msrp/tcp *\r\n"
- "a=accept-types: message/cpim text/plain text/html\r\n"
- "a=path:msrp://alice.example.com:7394/2s93i9;tcp\r\n"
-};
-
-static int sdp_perform_test(pj_pool_factory *pf)
-{
- pj_pool_t *pool;
- int inputlen, len;
- pjsdp_session_desc *ses;
- char buf[1500];
- enum { LOOP=1000000 };
- int i;
- pj_time_val start, end;
-
- printf("Parsing and printing %d SDP messages..\n", LOOP);
-
- pool = pj_pool_create(pf, "", 4096, 0, NULL);
- inputlen = strlen(sdp[0]);
- pj_gettimeofday(&start);
- for (i=0; i<LOOP; ++i) {
- ses = pjsdp_parse(sdp[0], inputlen, pool);
- len = pjsdp_print(ses, buf, sizeof(buf));
- buf[len] = '\0';
- pj_pool_reset(pool);
- }
- pj_gettimeofday(&end);
-
- printf("Original:\n%s\n", sdp[0]);
- printf("Parsed:\n%s\n", buf);
-
- PJ_TIME_VAL_SUB(end, start);
- printf("Time: %ld:%03lds\n", end.sec, end.msec);
-
- if (end.msec==0 && end.sec==0) end.msec=1;
- printf("Performance: %ld msg/sec\n", LOOP*1000/PJ_TIME_VAL_MSEC(end));
- puts("");
-
- pj_pool_release(pool);
- return 0;
-}
-
-static int sdp_conform_test(pj_pool_factory *pf)
-{
- pj_pool_t *pool;
- pjsdp_session_desc *ses;
- int i, len;
- char buf[1500];
-
- for (i=0; i<sizeof(sdp)/sizeof(sdp[0]); ++i) {
- pool = pj_pool_create(pf, "sdp", 4096, 0, NULL);
- ses = pjsdp_parse(sdp[i], strlen(sdp[i]), pool);
- len = pjsdp_print(ses, buf, sizeof(buf));
- buf[len] = '\0';
- printf("%s\n", buf);
- pj_pool_release(pool);
- }
-
- return 0;
-}
-
-pj_status_t sdp_test(pj_pool_factory *pf)
-{
- if (sdp_conform_test(pf) != 0)
- return -2;
-
- if (sdp_perform_test(pf) != 0)
- return -3;
-
- return 0;
-}
-
+/* $Id$ */
+/*
+ * Copyright (C) 2003-2006 Benny Prijono <benny@prijono.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#include <pjmedia/sdp.h>
+#include <pj/os.h>
+#include <pj/pool.h>
+#include <stdio.h>
+#include <string.h>
+
+static char *sdp[] = {
+ /*
+ "v=0\r\n"
+ "o=mhandley 2890844526 2890842807 IN IP4 126.16.64.4\r\n"
+ "s=SDP Seminar\r\n"
+ "i=A Seminar on the session description protocol\r\n"
+ "u=http://www.cs.ucl.ac.uk/staff/M.Handley/sdp.03.ps\r\n"
+ "e=mjh@isi.edu (Mark Handley)\r\n"
+ "c=IN IP4 224.2.17.12/127\r\n"
+ "t=2873397496 2873404696\r\n"
+ "a=recvonly\r\n"
+ "m=audio 49170 RTP/AVP 0\r\n"
+ "m=video 51372 RTP/AVP 31\r\n"
+ "m=application 32416 udp wb\r\n"
+ "a=orient:portrait\r\n"
+ "m=audio 49230 RTP/AVP 96 97 98\r\n"
+ "a=rtpmap:96 L8/8000\r\n"
+ "a=rtpmap:97 L16/8000\r\n"
+ "a=rtpmap:98 L16/11025/2\r\n",
+ */
+ "v=0\r\n"
+ "o=usera 2890844526 2890844527 IN IP4 alice.example.com\r\n"
+ "s=\r\n"
+ "c=IN IP4 alice.example.com\r\n"
+ "t=0 0\r\n"
+ "m=message 7394 msrp/tcp *\r\n"
+ "a=accept-types: message/cpim text/plain text/html\r\n"
+ "a=path:msrp://alice.example.com:7394/2s93i9;tcp\r\n"
+};
+
+static int sdp_perform_test(pj_pool_factory *pf)
+{
+ pj_pool_t *pool;
+ int inputlen, len;
+ pjsdp_session_desc *ses;
+ char buf[1500];
+ enum { LOOP=1000000 };
+ int i;
+ pj_time_val start, end;
+
+ printf("Parsing and printing %d SDP messages..\n", LOOP);
+
+ pool = pj_pool_create(pf, "", 4096, 0, NULL);
+ inputlen = strlen(sdp[0]);
+ pj_gettimeofday(&start);
+ for (i=0; i<LOOP; ++i) {
+ ses = pjsdp_parse(sdp[0], inputlen, pool);
+ len = pjsdp_print(ses, buf, sizeof(buf));
+ buf[len] = '\0';
+ pj_pool_reset(pool);
+ }
+ pj_gettimeofday(&end);
+
+ printf("Original:\n%s\n", sdp[0]);
+ printf("Parsed:\n%s\n", buf);
+
+ PJ_TIME_VAL_SUB(end, start);
+ printf("Time: %ld:%03lds\n", end.sec, end.msec);
+
+ if (end.msec==0 && end.sec==0) end.msec=1;
+ printf("Performance: %ld msg/sec\n", LOOP*1000/PJ_TIME_VAL_MSEC(end));
+ puts("");
+
+ pj_pool_release(pool);
+ return 0;
+}
+
+static int sdp_conform_test(pj_pool_factory *pf)
+{
+ pj_pool_t *pool;
+ pjsdp_session_desc *ses;
+ int i, len;
+ char buf[1500];
+
+ for (i=0; i<sizeof(sdp)/sizeof(sdp[0]); ++i) {
+ pool = pj_pool_create(pf, "sdp", 4096, 0, NULL);
+ ses = pjsdp_parse(sdp[i], strlen(sdp[i]), pool);
+ len = pjsdp_print(ses, buf, sizeof(buf));
+ buf[len] = '\0';
+ printf("%s\n", buf);
+ pj_pool_release(pool);
+ }
+
+ return 0;
+}
+
+pj_status_t sdp_test(pj_pool_factory *pf)
+{
+ if (sdp_conform_test(pf) != 0)
+ return -2;
+
+ if (sdp_perform_test(pf) != 0)
+ return -3;
+
+ return 0;
+}
+
diff --git a/pjmedia/src/test/session_test.c b/pjmedia/src/test/session_test.c
index 8bc69299..c106f04d 100644
--- a/pjmedia/src/test/session_test.c
+++ b/pjmedia/src/test/session_test.c
@@ -1,130 +1,130 @@
-/* $Id$ */
-/*
- * Copyright (C) 2003-2006 Benny Prijono <benny@prijono.org>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
-#include <pjmedia/mediamgr.h>
-#include <pjmedia/session.h>
-#include <pj/sock.h>
-#include <pj/pool.h>
-#include <stdio.h>
-#include <pj/string.h>
-
-pj_status_t session_test (pj_pool_factory *pf)
-{
- pj_med_mgr_t *mm;
- pj_media_session_t *s1, *s2;
- pj_pool_t *pool;
- pjsdp_session_desc *sdp;
- pj_media_stream_info sd_info;
- char buf[1024];
- int len;
- pj_media_stream_stat tx_stat, rx_stat;
-
- pool = pj_pool_create(pf, "test", 4096, 1024, NULL);
-
- // Init media manager.
- mm = pj_med_mgr_create ( pf );
-
- // Create caller session.
- // THIS WILL DEFINITELY CRASH (NULL as argument)!
- s1 = pj_media_session_create (mm, NULL);
-
- // Set caller's media to send-only.
- sd_info.dir = PJ_MEDIA_DIR_ENCODING;
- pj_media_session_modify_stream (s1, 0, PJ_MEDIA_STREAM_MODIFY_DIR, &sd_info);
-
- // Create caller SDP.
- sdp = pj_media_session_create_sdp (s1, pool, 0);
- len = pjsdp_print (sdp, buf, sizeof(buf));
- buf[len] = '\0';
- printf("Caller's initial SDP:\n<BEGIN>\n%s\n<END>\n", buf);
-
- // Parse SDP from caller.
- sdp = pjsdp_parse (buf, len, pool);
-
- // Create callee session based on caller's SDP.
- // THIS WILL DEFINITELY CRASH (NULL as argument)!
- s2 = pj_media_session_create_from_sdp (mm, sdp, NULL);
-
- // Create callee SDP
- sdp = pj_media_session_create_sdp (s2, pool, 0);
- len = pjsdp_print (sdp, buf, sizeof(buf));
- buf[len] = '\0';
- printf("Callee's SDP:\n<BEGIN>\n%s\n<END>\n", buf);
-
- // Parse SDP from callee.
- sdp = pjsdp_parse (buf, len, pool);
-
- // Update caller
- pj_media_session_update (s1, sdp);
- sdp = pj_media_session_create_sdp (s1, pool, 0);
- pjsdp_print (sdp, buf, sizeof(buf));
- printf("Caller's SDP after update:\n<BEGIN>\n%s\n<END>\n", buf);
-
- // Now start media.
- pj_media_session_activate (s2);
- pj_media_session_activate (s1);
-
- // Wait
- for (;;) {
- int has_stat;
-
- printf("Enter q to exit, 1 or 2 to print statistics.\n");
- fgets (buf, 10, stdin);
- has_stat = 0;
-
- switch (buf[0]) {
- case 'q':
- case 'Q':
- goto done;
- break;
- case '1':
- pj_media_session_get_stat (s1, 0, &tx_stat, &rx_stat);
- has_stat = 1;
- break;
- case '2':
- pj_media_session_get_stat (s2, 0, &tx_stat, &rx_stat);
- has_stat = 1;
- break;
- }
-
- if (has_stat) {
- pj_media_stream_stat *stat[2] = { &tx_stat, &rx_stat };
- const char *statname[2] = { "TX", "RX" };
- int i;
-
- for (i=0; i<2; ++i) {
- printf("%s statistics:\n", statname[i]);
- printf(" Pkt TX=%d RX=%d\n", stat[i]->pkt_tx, stat[i]->pkt_rx);
- printf(" Octets TX=%d RX=%d\n", stat[i]->oct_tx, stat[i]->oct_rx);
- printf(" Jitter %d ms\n", stat[i]->jitter);
- printf(" Pkt lost %d\n", stat[i]->pkt_lost);
- }
- printf("\n");
- }
- }
-
-done:
-
- // Done.
- pj_pool_release (pool);
- pj_media_session_destroy (s2);
- pj_media_session_destroy (s1);
- pj_med_mgr_destroy (mm);
-
- return 0;
-}
+/* $Id$ */
+/*
+ * Copyright (C) 2003-2006 Benny Prijono <benny@prijono.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#include <pjmedia/mediamgr.h>
+#include <pjmedia/session.h>
+#include <pj/sock.h>
+#include <pj/pool.h>
+#include <stdio.h>
+#include <pj/string.h>
+
+pj_status_t session_test (pj_pool_factory *pf)
+{
+ pj_med_mgr_t *mm;
+ pj_media_session_t *s1, *s2;
+ pj_pool_t *pool;
+ pjsdp_session_desc *sdp;
+ pj_media_stream_info sd_info;
+ char buf[1024];
+ int len;
+ pj_media_stream_stat tx_stat, rx_stat;
+
+ pool = pj_pool_create(pf, "test", 4096, 1024, NULL);
+
+ // Init media manager.
+ mm = pj_med_mgr_create ( pf );
+
+ // Create caller session.
+ // THIS WILL DEFINITELY CRASH (NULL as argument)!
+ s1 = pj_media_session_create (mm, NULL);
+
+ // Set caller's media to send-only.
+ sd_info.dir = PJ_MEDIA_DIR_ENCODING;
+ pj_media_session_modify_stream (s1, 0, PJ_MEDIA_STREAM_MODIFY_DIR, &sd_info);
+
+ // Create caller SDP.
+ sdp = pj_media_session_create_sdp (s1, pool, 0);
+ len = pjsdp_print (sdp, buf, sizeof(buf));
+ buf[len] = '\0';
+ printf("Caller's initial SDP:\n<BEGIN>\n%s\n<END>\n", buf);
+
+ // Parse SDP from caller.
+ sdp = pjsdp_parse (buf, len, pool);
+
+ // Create callee session based on caller's SDP.
+ // THIS WILL DEFINITELY CRASH (NULL as argument)!
+ s2 = pj_media_session_create_from_sdp (mm, sdp, NULL);
+
+ // Create callee SDP
+ sdp = pj_media_session_create_sdp (s2, pool, 0);
+ len = pjsdp_print (sdp, buf, sizeof(buf));
+ buf[len] = '\0';
+ printf("Callee's SDP:\n<BEGIN>\n%s\n<END>\n", buf);
+
+ // Parse SDP from callee.
+ sdp = pjsdp_parse (buf, len, pool);
+
+ // Update caller
+ pj_media_session_update (s1, sdp);
+ sdp = pj_media_session_create_sdp (s1, pool, 0);
+ pjsdp_print (sdp, buf, sizeof(buf));
+ printf("Caller's SDP after update:\n<BEGIN>\n%s\n<END>\n", buf);
+
+ // Now start media.
+ pj_media_session_activate (s2);
+ pj_media_session_activate (s1);
+
+ // Wait
+ for (;;) {
+ int has_stat;
+
+ printf("Enter q to exit, 1 or 2 to print statistics.\n");
+ fgets (buf, 10, stdin);
+ has_stat = 0;
+
+ switch (buf[0]) {
+ case 'q':
+ case 'Q':
+ goto done;
+ break;
+ case '1':
+ pj_media_session_get_stat (s1, 0, &tx_stat, &rx_stat);
+ has_stat = 1;
+ break;
+ case '2':
+ pj_media_session_get_stat (s2, 0, &tx_stat, &rx_stat);
+ has_stat = 1;
+ break;
+ }
+
+ if (has_stat) {
+ pj_media_stream_stat *stat[2] = { &tx_stat, &rx_stat };
+ const char *statname[2] = { "TX", "RX" };
+ int i;
+
+ for (i=0; i<2; ++i) {
+ printf("%s statistics:\n", statname[i]);
+ printf(" Pkt TX=%d RX=%d\n", stat[i]->pkt_tx, stat[i]->pkt_rx);
+ printf(" Octets TX=%d RX=%d\n", stat[i]->oct_tx, stat[i]->oct_rx);
+ printf(" Jitter %d ms\n", stat[i]->jitter);
+ printf(" Pkt lost %d\n", stat[i]->pkt_lost);
+ }
+ printf("\n");
+ }
+ }
+
+done:
+
+ // Done.
+ pj_pool_release (pool);
+ pj_media_session_destroy (s2);
+ pj_media_session_destroy (s1);
+ pj_med_mgr_destroy (mm);
+
+ return 0;
+}
diff --git a/pjsip/include/pjsip-simple/event_notify.h b/pjsip/include/pjsip-simple/event_notify.h
index 15561505..5f002582 100644
--- a/pjsip/include/pjsip-simple/event_notify.h
+++ b/pjsip/include/pjsip-simple/event_notify.h
@@ -1,330 +1,330 @@
-/* $Id$ */
-/*
- * Copyright (C) 2003-2006 Benny Prijono <benny@prijono.org>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
-#ifndef __PJSIP_SIMPLE_EVENT_NOTIFY_H__
-#define __PJSIP_SIMPLE_EVENT_NOTIFY_H__
-
-/**
- * @file event_notify.h
- * @brief SIP Specific Event Notification Extension (RFC 3265)
- */
-
-#include <pjsip/sip_types.h>
-#include <pjsip/sip_auth.h>
-#include <pjsip_simple/event_notify_msg.h>
-#include <pj/timer.h>
-
-/**
- * @defgroup PJSIP_EVENT_NOT SIP Event Notification (RFC 3265) Module
- * @ingroup PJSIP_SIMPLE
- * @{
- *
- * This module provides the implementation of SIP Extension for SIP Specific
- * Event Notification (RFC 3265). It extends PJSIP by supporting SUBSCRIBE and
- * NOTIFY methods.
- *
- * This module itself is extensible; new event packages can be registered to
- * this module to handle specific extensions (such as presence).
- */
-
-PJ_BEGIN_DECL
-
-typedef struct pjsip_event_sub_cb pjsip_event_sub_cb;
-typedef struct pjsip_event_sub pjsip_event_sub;
-
-/**
- * This enumeration describes subscription state as described in the RFC 3265.
- * The standard specifies that extensions may define additional states. In the
- * case where the state is not known, the subscription state will be set to
- * PJSIP_EVENT_SUB_STATE_UNKNOWN, and the token will be kept in state_str
- * member of the susbcription structure.
- */
-typedef enum pjsip_event_sub_state
-{
- /** State is NULL. */
- PJSIP_EVENT_SUB_STATE_NULL,
-
- /** Subscription is active. */
- PJSIP_EVENT_SUB_STATE_ACTIVE,
-
- /** Subscription is pending. */
- PJSIP_EVENT_SUB_STATE_PENDING,
-
- /** Subscription is terminated. */
- PJSIP_EVENT_SUB_STATE_TERMINATED,
-
- /** Subscription state can not be determined. Application can query
- * the state information in state_str member.
- */
- PJSIP_EVENT_SUB_STATE_UNKNOWN,
-
-} pjsip_event_sub_state;
-
-/**
- * This structure describes notification to be called when incoming SUBSCRIBE
- * request is received. The module will call the callback registered by package
- * that matches the event description in the incoming SUBSCRIBE.
- */
-typedef struct pjsip_event_sub_pkg_cb
-{
- /**
- * This callback is called to first enquery the package whether it wants
- * to accept incoming SUBSCRIBE request. If it does, then on_subscribe
- * will be called.
- *
- * @param rdata The incoming request.
- * @param status The status code to be returned back to subscriber.
- */
- void (*on_query_subscribe)(pjsip_rx_data *rdata, int *status);
-
- /**
- * This callback is called when the module receives incoming SUBSCRIBE
- * request.
- *
- * @param sub The subscription instance.
- * @param rdata The received buffer.
- * @param cb Callback to be registered to the subscription instance.
- * @param expires The expiration to be set.
- */
- void (*on_subscribe)(pjsip_event_sub *sub, pjsip_rx_data *rdata,
- pjsip_event_sub_cb **cb, int *expires);
-
-} pjsip_event_sub_pkg_cb;
-
-/**
- * This structure describes callback that is registered by application or
- * package to receive notifications about a subscription.
- */
-struct pjsip_event_sub_cb
-{
- /**
- * This callback is used by both subscriber and notifier. It is called
- * when the subscription has been terminated.
- *
- * @param sub The subscription instance.
- * @param reason The termination reason.
- */
- void (*on_sub_terminated)(pjsip_event_sub *sub, const pj_str_t *reason);
-
- /**
- * This callback is called when we received SUBSCRIBE request to refresh
- * the subscription.
- *
- * @param sub The subscription instance.
- * @param rdata The received SUBSCRIBE request.
- */
- void (*on_received_refresh)(pjsip_event_sub *sub, pjsip_rx_data *rdata);
-
- /**
- * This callback is called when the module receives final response on
- * previously sent SUBSCRIBE request.
- *
- * @param sub The subscription instance.
- * @param event The event.
- */
- void (*on_received_sub_response)(pjsip_event_sub *sub, pjsip_event *event);
-
- /**
- * This callback is called when the module receives incoming NOTIFY
- * request.
- *
- * @param sub The subscription instance.
- * @param rdata The received data.
- */
- void (*on_received_notify)(pjsip_event_sub *sub, pjsip_rx_data *rdata);
-
- /**
- * This callback is called when the module receives final response to
- * previously sent NOTIFY request.
- *
- * @param sub The subscription instance.
- * @param event The event.
- */
- void (*on_received_notify_response)(pjsip_event_sub *sub, pjsip_event *event);
-
-};
-
-/**
- * This structure describes an event subscription record. The structure is used
- * to represent both subscriber and notifier.
- */
-struct pjsip_event_sub
-{
- pj_pool_t *pool; /**< Pool. */
- pjsip_endpoint *endpt; /**< Endpoint. */
- pjsip_event_sub_cb cb; /**< Callback. */
- pj_mutex_t *mutex; /**< Mutex. */
- pjsip_role_e role; /**< Role (UAC=subscriber, UAS=notifier) */
- pjsip_event_sub_state state; /**< Subscription state. */
- pj_str_t state_str; /**< String describing the state. */
- pjsip_from_hdr *from; /**< Cached local info (From) */
- pjsip_to_hdr *to; /**< Cached remote info (To) */
- pjsip_contact_hdr *contact; /**< Cached local contact. */
- pjsip_cid_hdr *call_id; /**< Cached Call-ID */
- int cseq; /**< Outgoing CSeq */
- pjsip_event_hdr *event; /**< Event description. */
- pjsip_expires_hdr *uac_expires; /**< Cached Expires header (UAC only). */
- pjsip_accept_hdr *local_accept; /**< Local Accept header. */
- pjsip_route_hdr route_set; /**< Route-set. */
-
- pj_str_t key; /**< Key in the hash table. */
- void *user_data; /**< Application data. */
- int default_interval; /**< Refresh interval. */
- pj_timer_entry timer; /**< Internal timer. */
- pj_time_val expiry_time; /**< Time when subscription expires. */
- int pending_tsx; /**< Number of pending transactions. */
- pj_bool_t delete_flag; /**< Pending deletion flag. */
-
- pjsip_auth_session auth_sess; /**< Authorization sessions. */
- unsigned cred_cnt; /**< Number of credentials. */
- pjsip_cred_info *cred_info; /**< Array of credentials. */
-};
-
-
-
-
-/**
- * Initialize the module and get the instance of the module to be registered to
- * endpoint.
- *
- * @return The module instance.
- */
-PJ_DECL(pjsip_module*) pjsip_event_sub_get_module(void);
-
-
-/**
- * Register event package.
- *
- * @param event The event identification for the package.
- * @param accept_cnt Number of strings in Accept array.
- * @param accept Array of Accept value.
- * @param cb Callback to receive incoming SUBSCRIBE for the package.
- *
- * @return Zero on success.
- */
-PJ_DECL(pj_status_t) pjsip_event_sub_register_pkg( const pj_str_t *event_name,
- int accept_cnt,
- const pj_str_t accept[],
- const pjsip_event_sub_pkg_cb *cb );
-
-
-/**
- * Create initial subscription instance (client).
- *
- * @param endpt The endpoint.
- * @param from URL to put in From header.
- * @param to The target resource.
- * @param event Event package.
- * @param expires Expiration time.
- * @param accept Accept specification.
- * @param user_data Application data to attach to this subscription.
- *
- * @return New client subscription instance.
- */
-PJ_DECL(pjsip_event_sub*) pjsip_event_sub_create( pjsip_endpoint *endpt,
- const pj_str_t *from,
- const pj_str_t *to,
- const pj_str_t *event,
- int expires,
- int accept_cnt,
- const pj_str_t accept[],
- void *user_data,
- const pjsip_event_sub_cb *cb);
-
-/**
- * Set credentials to be used for outgoing request messages.
- *
- * @param sub Subscription instance.
- * @param count Number of credentials.
- * @param cred Array of credential info.
- *
- * @return Zero on success.
- */
-PJ_DECL(pj_status_t) pjsip_event_sub_set_credentials( pjsip_event_sub *sub,
- int count,
- const pjsip_cred_info cred[]);
-
-/**
- * Set route set for outgoing requests.
- *
- * @param sub Subscription instance.
- * @param route_set List of route headers.
- *
- * @return Zero on success.
- */
-PJ_DECL(pj_status_t) pjsip_event_sub_set_route_set( pjsip_event_sub *sub,
- const pjsip_route_hdr *route_set );
-
-
-/**
- * Send SUBSCRIBE request.
- *
- * @param sub Subscription instance.
- *
- * @return Zero on success.
- */
-PJ_DECL(pj_status_t) pjsip_event_sub_subscribe( pjsip_event_sub *sub );
-
-/**
- * Terminate subscription (client). This will send unsubscription request to
- * notifier.
- *
- * @param sub Client subscription instance.
- *
- * @return Zero on success.
- */
-PJ_DECL(pj_status_t) pjsip_event_sub_unsubscribe( pjsip_event_sub *sub );
-
-
-/**
- * For notifier, send NOTIFY request to subscriber, and set the state of
- * the subscription.
- *
- * @param sub The server subscription (notifier) instance.
- * @param state New state to set.
- * @param reason Specify reason if new state is terminated, otherwise
- * put NULL.
- * @param type Description of content type.
- * @param body Text body to send with the NOTIFY, or NULL if the
- * NOTIFY request should not contain any message body.
- *
- * @return Zero on success.
- */
-PJ_DECL(pj_status_t) pjsip_event_sub_notify( pjsip_event_sub *sub,
- pjsip_event_sub_state state,
- const pj_str_t *reason,
- pjsip_msg_body *body);
-
-/**
- * Destroy subscription instance.
- *
- * @param sub The client or server subscription instance.
- *
- * @return Zero on success, one if the subscription will be
- * deleted automatically later, or -1 on error.
- */
-PJ_DECL(pj_status_t) pjsip_event_sub_destroy(pjsip_event_sub *sub);
-
-
-PJ_END_DECL
-
-/**
- * @}
- */
-
-#endif /* __PJSIP_SIMPLE_EVENT_NOTIFY_H__ */
+/* $Id$ */
+/*
+ * Copyright (C) 2003-2006 Benny Prijono <benny@prijono.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#ifndef __PJSIP_SIMPLE_EVENT_NOTIFY_H__
+#define __PJSIP_SIMPLE_EVENT_NOTIFY_H__
+
+/**
+ * @file event_notify.h
+ * @brief SIP Specific Event Notification Extension (RFC 3265)
+ */
+
+#include <pjsip/sip_types.h>
+#include <pjsip/sip_auth.h>
+#include <pjsip_simple/event_notify_msg.h>
+#include <pj/timer.h>
+
+/**
+ * @defgroup PJSIP_EVENT_NOT SIP Event Notification (RFC 3265) Module
+ * @ingroup PJSIP_SIMPLE
+ * @{
+ *
+ * This module provides the implementation of SIP Extension for SIP Specific
+ * Event Notification (RFC 3265). It extends PJSIP by supporting SUBSCRIBE and
+ * NOTIFY methods.
+ *
+ * This module itself is extensible; new event packages can be registered to
+ * this module to handle specific extensions (such as presence).
+ */
+
+PJ_BEGIN_DECL
+
+typedef struct pjsip_event_sub_cb pjsip_event_sub_cb;
+typedef struct pjsip_event_sub pjsip_event_sub;
+
+/**
+ * This enumeration describes subscription state as described in the RFC 3265.
+ * The standard specifies that extensions may define additional states. In the
+ * case where the state is not known, the subscription state will be set to
+ * PJSIP_EVENT_SUB_STATE_UNKNOWN, and the token will be kept in state_str
+ * member of the susbcription structure.
+ */
+typedef enum pjsip_event_sub_state
+{
+ /** State is NULL. */
+ PJSIP_EVENT_SUB_STATE_NULL,
+
+ /** Subscription is active. */
+ PJSIP_EVENT_SUB_STATE_ACTIVE,
+
+ /** Subscription is pending. */
+ PJSIP_EVENT_SUB_STATE_PENDING,
+
+ /** Subscription is terminated. */
+ PJSIP_EVENT_SUB_STATE_TERMINATED,
+
+ /** Subscription state can not be determined. Application can query
+ * the state information in state_str member.
+ */
+ PJSIP_EVENT_SUB_STATE_UNKNOWN,
+
+} pjsip_event_sub_state;
+
+/**
+ * This structure describes notification to be called when incoming SUBSCRIBE
+ * request is received. The module will call the callback registered by package
+ * that matches the event description in the incoming SUBSCRIBE.
+ */
+typedef struct pjsip_event_sub_pkg_cb
+{
+ /**
+ * This callback is called to first enquery the package whether it wants
+ * to accept incoming SUBSCRIBE request. If it does, then on_subscribe
+ * will be called.
+ *
+ * @param rdata The incoming request.
+ * @param status The status code to be returned back to subscriber.
+ */
+ void (*on_query_subscribe)(pjsip_rx_data *rdata, int *status);
+
+ /**
+ * This callback is called when the module receives incoming SUBSCRIBE
+ * request.
+ *
+ * @param sub The subscription instance.
+ * @param rdata The received buffer.
+ * @param cb Callback to be registered to the subscription instance.
+ * @param expires The expiration to be set.
+ */
+ void (*on_subscribe)(pjsip_event_sub *sub, pjsip_rx_data *rdata,
+ pjsip_event_sub_cb **cb, int *expires);
+
+} pjsip_event_sub_pkg_cb;
+
+/**
+ * This structure describes callback that is registered by application or
+ * package to receive notifications about a subscription.
+ */
+struct pjsip_event_sub_cb
+{
+ /**
+ * This callback is used by both subscriber and notifier. It is called
+ * when the subscription has been terminated.
+ *
+ * @param sub The subscription instance.
+ * @param reason The termination reason.
+ */
+ void (*on_sub_terminated)(pjsip_event_sub *sub, const pj_str_t *reason);
+
+ /**
+ * This callback is called when we received SUBSCRIBE request to refresh
+ * the subscription.
+ *
+ * @param sub The subscription instance.
+ * @param rdata The received SUBSCRIBE request.
+ */
+ void (*on_received_refresh)(pjsip_event_sub *sub, pjsip_rx_data *rdata);
+
+ /**
+ * This callback is called when the module receives final response on
+ * previously sent SUBSCRIBE request.
+ *
+ * @param sub The subscription instance.
+ * @param event The event.
+ */
+ void (*on_received_sub_response)(pjsip_event_sub *sub, pjsip_event *event);
+
+ /**
+ * This callback is called when the module receives incoming NOTIFY
+ * request.
+ *
+ * @param sub The subscription instance.
+ * @param rdata The received data.
+ */
+ void (*on_received_notify)(pjsip_event_sub *sub, pjsip_rx_data *rdata);
+
+ /**
+ * This callback is called when the module receives final response to
+ * previously sent NOTIFY request.
+ *
+ * @param sub The subscription instance.
+ * @param event The event.
+ */
+ void (*on_received_notify_response)(pjsip_event_sub *sub, pjsip_event *event);
+
+};
+
+/**
+ * This structure describes an event subscription record. The structure is used
+ * to represent both subscriber and notifier.
+ */
+struct pjsip_event_sub
+{
+ pj_pool_t *pool; /**< Pool. */
+ pjsip_endpoint *endpt; /**< Endpoint. */
+ pjsip_event_sub_cb cb; /**< Callback. */
+ pj_mutex_t *mutex; /**< Mutex. */
+ pjsip_role_e role; /**< Role (UAC=subscriber, UAS=notifier) */
+ pjsip_event_sub_state state; /**< Subscription state. */
+ pj_str_t state_str; /**< String describing the state. */
+ pjsip_from_hdr *from; /**< Cached local info (From) */
+ pjsip_to_hdr *to; /**< Cached remote info (To) */
+ pjsip_contact_hdr *contact; /**< Cached local contact. */
+ pjsip_cid_hdr *call_id; /**< Cached Call-ID */
+ int cseq; /**< Outgoing CSeq */
+ pjsip_event_hdr *event; /**< Event description. */
+ pjsip_expires_hdr *uac_expires; /**< Cached Expires header (UAC only). */
+ pjsip_accept_hdr *local_accept; /**< Local Accept header. */
+ pjsip_route_hdr route_set; /**< Route-set. */
+
+ pj_str_t key; /**< Key in the hash table. */
+ void *user_data; /**< Application data. */
+ int default_interval; /**< Refresh interval. */
+ pj_timer_entry timer; /**< Internal timer. */
+ pj_time_val expiry_time; /**< Time when subscription expires. */
+ int pending_tsx; /**< Number of pending transactions. */
+ pj_bool_t delete_flag; /**< Pending deletion flag. */
+
+ pjsip_auth_session auth_sess; /**< Authorization sessions. */
+ unsigned cred_cnt; /**< Number of credentials. */
+ pjsip_cred_info *cred_info; /**< Array of credentials. */
+};
+
+
+
+
+/**
+ * Initialize the module and get the instance of the module to be registered to
+ * endpoint.
+ *
+ * @return The module instance.
+ */
+PJ_DECL(pjsip_module*) pjsip_event_sub_get_module(void);
+
+
+/**
+ * Register event package.
+ *
+ * @param event The event identification for the package.
+ * @param accept_cnt Number of strings in Accept array.
+ * @param accept Array of Accept value.
+ * @param cb Callback to receive incoming SUBSCRIBE for the package.
+ *
+ * @return Zero on success.
+ */
+PJ_DECL(pj_status_t) pjsip_event_sub_register_pkg( const pj_str_t *event_name,
+ int accept_cnt,
+ const pj_str_t accept[],
+ const pjsip_event_sub_pkg_cb *cb );
+
+
+/**
+ * Create initial subscription instance (client).
+ *
+ * @param endpt The endpoint.
+ * @param from URL to put in From header.
+ * @param to The target resource.
+ * @param event Event package.
+ * @param expires Expiration time.
+ * @param accept Accept specification.
+ * @param user_data Application data to attach to this subscription.
+ *
+ * @return New client subscription instance.
+ */
+PJ_DECL(pjsip_event_sub*) pjsip_event_sub_create( pjsip_endpoint *endpt,
+ const pj_str_t *from,
+ const pj_str_t *to,
+ const pj_str_t *event,
+ int expires,
+ int accept_cnt,
+ const pj_str_t accept[],
+ void *user_data,
+ const pjsip_event_sub_cb *cb);
+
+/**
+ * Set credentials to be used for outgoing request messages.
+ *
+ * @param sub Subscription instance.
+ * @param count Number of credentials.
+ * @param cred Array of credential info.
+ *
+ * @return Zero on success.
+ */
+PJ_DECL(pj_status_t) pjsip_event_sub_set_credentials( pjsip_event_sub *sub,
+ int count,
+ const pjsip_cred_info cred[]);
+
+/**
+ * Set route set for outgoing requests.
+ *
+ * @param sub Subscription instance.
+ * @param route_set List of route headers.
+ *
+ * @return Zero on success.
+ */
+PJ_DECL(pj_status_t) pjsip_event_sub_set_route_set( pjsip_event_sub *sub,
+ const pjsip_route_hdr *route_set );
+
+
+/**
+ * Send SUBSCRIBE request.
+ *
+ * @param sub Subscription instance.
+ *
+ * @return Zero on success.
+ */
+PJ_DECL(pj_status_t) pjsip_event_sub_subscribe( pjsip_event_sub *sub );
+
+/**
+ * Terminate subscription (client). This will send unsubscription request to
+ * notifier.
+ *
+ * @param sub Client subscription instance.
+ *
+ * @return Zero on success.
+ */
+PJ_DECL(pj_status_t) pjsip_event_sub_unsubscribe( pjsip_event_sub *sub );
+
+
+/**
+ * For notifier, send NOTIFY request to subscriber, and set the state of
+ * the subscription.
+ *
+ * @param sub The server subscription (notifier) instance.
+ * @param state New state to set.
+ * @param reason Specify reason if new state is terminated, otherwise
+ * put NULL.
+ * @param type Description of content type.
+ * @param body Text body to send with the NOTIFY, or NULL if the
+ * NOTIFY request should not contain any message body.
+ *
+ * @return Zero on success.
+ */
+PJ_DECL(pj_status_t) pjsip_event_sub_notify( pjsip_event_sub *sub,
+ pjsip_event_sub_state state,
+ const pj_str_t *reason,
+ pjsip_msg_body *body);
+
+/**
+ * Destroy subscription instance.
+ *
+ * @param sub The client or server subscription instance.
+ *
+ * @return Zero on success, one if the subscription will be
+ * deleted automatically later, or -1 on error.
+ */
+PJ_DECL(pj_status_t) pjsip_event_sub_destroy(pjsip_event_sub *sub);
+
+
+PJ_END_DECL
+
+/**
+ * @}
+ */
+
+#endif /* __PJSIP_SIMPLE_EVENT_NOTIFY_H__ */
diff --git a/pjsip/include/pjsip-simple/event_notify_msg.h b/pjsip/include/pjsip-simple/event_notify_msg.h
index 95f5a48a..76b7d35c 100644
--- a/pjsip/include/pjsip-simple/event_notify_msg.h
+++ b/pjsip/include/pjsip-simple/event_notify_msg.h
@@ -1,117 +1,117 @@
-/* $Id$ */
-/*
- * Copyright (C) 2003-2006 Benny Prijono <benny@prijono.org>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
-#ifndef __PJSIP_SIMPLE_EVENT_NOTIFY_MSG_H__
-#define __PJSIP_SIMPLE_EVENT_NOTIFY_MSG_H__
-
-/**
- * @file event_notify_msg.h
- * @brief SIP Event Notification Headers (RFC 3265)
- */
-#include <pjsip/sip_msg.h>
-
-/**
- * @ingroup PJSIP_EVENT_NOT
- * @{
- */
-
-PJ_BEGIN_DECL
-
-
-/** Max events in Allow-Events header. */
-#define PJSIP_MAX_ALLOW_EVENTS 16
-
-/**
- * This structure describes Event header.
- */
-typedef struct pjsip_event_hdr
-{
- PJSIP_DECL_HDR_MEMBER(struct pjsip_event_hdr)
- pj_str_t event_type; /**< Event name. */
- pj_str_t id_param; /**< Optional event ID parameter. */
- pj_str_t other_param; /**< Other parameter, concatenated together. */
-} pjsip_event_hdr;
-
-/**
- * Create an Event header.
- *
- * @param pool The pool.
- *
- * @return New Event header instance.
- */
-PJ_DECL(pjsip_event_hdr*) pjsip_event_hdr_create(pj_pool_t *pool);
-
-
-/**
- * This structure describes Allow-Events header.
- */
-typedef struct pjsip_allow_events_hdr
-{
- PJSIP_DECL_HDR_MEMBER(struct pjsip_allow_events_hdr)
- int event_cnt; /**< Number of event names. */
- pj_str_t events[PJSIP_MAX_ALLOW_EVENTS]; /**< Event names. */
-} pjsip_allow_events_hdr;
-
-
-/**
- * Create a new Allow-Events header.
- *
- * @param pool. The pool.
- *
- * @return Allow-Events header.
- */
-PJ_DECL(pjsip_allow_events_hdr*) pjsip_allow_events_hdr_create(pj_pool_t *pool);
-
-
-/**
- * This structure describes Subscription-State header.
- */
-typedef struct pjsip_sub_state_hdr
-{
- PJSIP_DECL_HDR_MEMBER(struct pjsip_sub_state_hdr)
- pj_str_t sub_state; /**< Subscription state. */
- pj_str_t reason_param; /**< Optional termination reason. */
- int expires_param; /**< Expires param, or -1. */
- int retry_after; /**< Retry after param, or -1. */
- pj_str_t other_param; /**< Other parameter, concatenated together. */
-} pjsip_sub_state_hdr;
-
-/**
- * Create new Subscription-State header.
- *
- * @param pool The pool.
- *
- * @return Subscription-State header.
- */
-PJ_DECL(pjsip_sub_state_hdr*) pjsip_sub_state_hdr_create(pj_pool_t *pool);
-
-/**
- * Initialize parser for event notify module.
- */
-PJ_DEF(void) pjsip_event_notify_init_parser(void);
-
-
-PJ_END_DECL
-
-
-/**
- * @}
- */
-
-#endif /* __PJSIP_SIMPLE_EVENT_NOTIFY_MSG_H__ */
-
+/* $Id$ */
+/*
+ * Copyright (C) 2003-2006 Benny Prijono <benny@prijono.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#ifndef __PJSIP_SIMPLE_EVENT_NOTIFY_MSG_H__
+#define __PJSIP_SIMPLE_EVENT_NOTIFY_MSG_H__
+
+/**
+ * @file event_notify_msg.h
+ * @brief SIP Event Notification Headers (RFC 3265)
+ */
+#include <pjsip/sip_msg.h>
+
+/**
+ * @ingroup PJSIP_EVENT_NOT
+ * @{
+ */
+
+PJ_BEGIN_DECL
+
+
+/** Max events in Allow-Events header. */
+#define PJSIP_MAX_ALLOW_EVENTS 16
+
+/**
+ * This structure describes Event header.
+ */
+typedef struct pjsip_event_hdr
+{
+ PJSIP_DECL_HDR_MEMBER(struct pjsip_event_hdr)
+ pj_str_t event_type; /**< Event name. */
+ pj_str_t id_param; /**< Optional event ID parameter. */
+ pj_str_t other_param; /**< Other parameter, concatenated together. */
+} pjsip_event_hdr;
+
+/**
+ * Create an Event header.
+ *
+ * @param pool The pool.
+ *
+ * @return New Event header instance.
+ */
+PJ_DECL(pjsip_event_hdr*) pjsip_event_hdr_create(pj_pool_t *pool);
+
+
+/**
+ * This structure describes Allow-Events header.
+ */
+typedef struct pjsip_allow_events_hdr
+{
+ PJSIP_DECL_HDR_MEMBER(struct pjsip_allow_events_hdr)
+ int event_cnt; /**< Number of event names. */
+ pj_str_t events[PJSIP_MAX_ALLOW_EVENTS]; /**< Event names. */
+} pjsip_allow_events_hdr;
+
+
+/**
+ * Create a new Allow-Events header.
+ *
+ * @param pool. The pool.
+ *
+ * @return Allow-Events header.
+ */
+PJ_DECL(pjsip_allow_events_hdr*) pjsip_allow_events_hdr_create(pj_pool_t *pool);
+
+
+/**
+ * This structure describes Subscription-State header.
+ */
+typedef struct pjsip_sub_state_hdr
+{
+ PJSIP_DECL_HDR_MEMBER(struct pjsip_sub_state_hdr)
+ pj_str_t sub_state; /**< Subscription state. */
+ pj_str_t reason_param; /**< Optional termination reason. */
+ int expires_param; /**< Expires param, or -1. */
+ int retry_after; /**< Retry after param, or -1. */
+ pj_str_t other_param; /**< Other parameter, concatenated together. */
+} pjsip_sub_state_hdr;
+
+/**
+ * Create new Subscription-State header.
+ *
+ * @param pool The pool.
+ *
+ * @return Subscription-State header.
+ */
+PJ_DECL(pjsip_sub_state_hdr*) pjsip_sub_state_hdr_create(pj_pool_t *pool);
+
+/**
+ * Initialize parser for event notify module.
+ */
+PJ_DEF(void) pjsip_event_notify_init_parser(void);
+
+
+PJ_END_DECL
+
+
+/**
+ * @}
+ */
+
+#endif /* __PJSIP_SIMPLE_EVENT_NOTIFY_MSG_H__ */
+
diff --git a/pjsip/include/pjsip-simple/messaging.h b/pjsip/include/pjsip-simple/messaging.h
index 25bf5dd5..742d739c 100644
--- a/pjsip/include/pjsip-simple/messaging.h
+++ b/pjsip/include/pjsip-simple/messaging.h
@@ -1,268 +1,268 @@
-/* $Id$ */
-/*
- * Copyright (C) 2003-2006 Benny Prijono <benny@prijono.org>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
-#ifndef __PJSIP_SIMPLE_MESSAGING_H__
-#define __PJSIP_SIMPLE_MESSAGING_H__
-
-/**
- * @file messaging.h
- * @brief Instant Messaging Extension (RFC 3428)
- */
-
-#include <pjsip/sip_msg.h>
-
-PJ_BEGIN_DECL
-
-/**
- * @defgroup PJSIP_MESSAGING SIP Instant Messaging (RFC 3428) Module
- * @ingroup PJSIP_SIMPLE
- * @{
- *
- * This module provides the implementation of SIP Extension for Instant
- * Messaging (RFC 3428). It extends PJSIP by supporting MESSAGE method.
- *
- * The RFC 3428 doesn't provide any means of dialog for the purpose of sending/
- * receiving instant messaging. IM with SIP is basicly sessionless, which means
- * that there is absolutely no correlation between IM messages sent or received
- * by a host. Any correlation between IM messages is only perceivable by
- * user, phsychologically.
- *
- * However, the RFC doesn't prohibit sending IM within a dialog (presumably
- * using the same Call-ID and CSeq namespace), although it prohibits creating
- * a dialog specificly for creating IM session.
- *
- * The implementation here is modeled to support both ways of sending IM msgs,
- * i.e. sending IM message individually and sending IM message within a dialog.
- * Although IM message can be associated with a dialog, this implementation of
- * IM module is completely independent of the User Agent library in PJSIP. Yes,
- * that's what is called modularity, and it demonstrates the clearness
- * of PJSIP design (the last sentence is of course marketing crap :)).
- *
- * To send an IM message as part of dialog, application would first create the
- * message using #pjsip_messaging_create_msg, using dialog's Call-ID, CSeq,
- * From, and To header, then send the message using #pjsip_dlg_send_msg instead
- * of #pjsip_messaging_send_msg.
- *
- * To send IM messages individually, application has two options. The first is
- * to create the request with #pjsip_messaging_create_msg then send it with
- * #pjsip_messaging_send_msg. But this way, application has to pre-construct
- * From and To header first, which is not too convenient.
- *
- * The second option (to send IM messages not associated with a dialog) is to
- * first create an 'IM session'. An 'IM session' here is not a SIP dialog, as
- * it doesn't have Contact header etc. An 'IM session' here is just a local
- * state to cache most of IM headers, for convenience and optimization. Appl
- * creates an IM session with #pjsip_messaging_create_session, and destroy
- * the session with #pjsip_messaging_destroy_session. To create an outgoing
- * IM message, application would call #pjsip_messaging_session_create_msg,
- * and to send the message it would use #pjsip_messaging_send_msg.
- *
- * Message authorization is handled by application, as usual, by inserting a
- * proper WWW-Authenticate or Proxy-Authenticate header before sending the
- * message.
- *
- * And the last bit, to handle incoing IM messages.
- *
- * To handle incoming IM messages, application would register a global callback
- * to be called when incoming messages arrive, by registering with function
- * #pjsip_messaging_set_incoming_callback. This will be the global callback
- * for all incoming IM messages. Although the message was sent as part of
- * a dialog, it would still come here. And as long as the request has proper
- * identification (Call-ID, From/To tag), the dialog will be aware of the
- * request and update it's state (remote CSeq) accordingly.
- */
-
-
-
-/**
- * Typedef for callback to be called when outgoing message has been sent
- * and a final response has been received.
- */
-typedef void (*pjsip_messaging_cb)(void *token, int status_code);
-
-/**
- * Typedef for callback to receive incoming message.
- *
- * @param rdata Incoming message data.
- *
- * @return The status code to be returned back to original sender.
- * Application must return a final status code upon returning
- * from this function, or otherwise the stack will respond
- * with error.
- */
-typedef int (*pjsip_on_new_msg_cb)(pjsip_rx_data *rdata);
-
-/**
- * Opaque data type for instant messaging session.
- */
-typedef struct pjsip_messaging_session pjsip_messaging_session;
-
-/**
- * Get the messaging module.
- *
- * @return SIP module.
- */
-PJ_DECL(pjsip_module*) pjsip_messaging_get_module();
-
-/**
- * Set the global callback to be called when incoming message is received.
- *
- * @param cb The callback to be called when incoming message is received.
- *
- * @return The previous callback.
- */
-PJ_DECL(pjsip_on_new_msg_cb)
-pjsip_messaging_set_incoming_callback(pjsip_on_new_msg_cb cb);
-
-
-/**
- * Create an instant message transmit data buffer using the specified arguments.
- * The returned transmit data buffers will have it's reference counter set
- * to 1, and when application send the buffer, the send function will decrement
- * the reference counter. When the reference counter reach zero, the buffer
- * will be deleted. As long as the function does not increment the buffer's
- * reference counter between creating and sending the request, the buffer
- * will always be deleted and no memory leak will occur.
- *
- * @param endpt Endpoint instance.
- * @param target Target URL.
- * @param from The "From" header, which content will be copied to request.
- * If the "From" header doesn't have a tag parameter, the
- * function will generate one.
- * @param to The "To" header, which content will be copied to request.
- * @param call_id Optionally specify Call-ID, or put NULL to make this
- * function generate a unique Call-ID automatically.
- * @param cseq Optionally specify CSeq, or put -1 to make this function
- * generate a random CSeq.
- * @param text Optionally specify "text/plain" message body, or put NULL
- * if application wants to put body other than "text/plain"
- * manually.
- *
- * @return SIP transmit data buffer, which reference count has been
- * set to 1.
- */
-PJ_DECL(pjsip_tx_data*)
-pjsip_messaging_create_msg_from_hdr(pjsip_endpoint *endpt,
- const pjsip_uri *target,
- const pjsip_from_hdr *from,
- const pjsip_to_hdr *to,
- const pjsip_cid_hdr *call_id,
- int cseq,
- const pj_str_t *text);
-
-/**
- * Create instant message, by specifying URL string for both From and To header.
- *
- * @param endpt Endpoint instance.
- * @param target Target URL.
- * @param from URL of the sender.
- * @param to URL of the recipient.
- * @param call_id Optionally specify Call-ID, or put NULL to make this
- * function generate a unique Call-ID automatically.
- * @param cseq Optionally specify CSeq, or put -1 to make this function
- * generate a random CSeq.
- * @param text Optionally specify "text/plain" message body, or put NULL
- * if application wants to put body other than "text/plain"
- * manually.
- *
- * @return SIP transmit data buffer, which reference count has been
- * set to 1.
- */
-PJ_DECL(pjsip_tx_data*) pjsip_messaging_create_msg( pjsip_endpoint *endpt,
- const pj_str_t *target,
- const pj_str_t *from,
- const pj_str_t *to,
- const pj_str_t *call_id,
- int cseq,
- const pj_str_t *text);
-
-/**
- * Send the instant message transmit buffer and attach a callback to be called
- * when the request has received a final response. This function will decrement
- * the transmit buffer's reference counter, and when the reference counter
- * reach zero, the buffer will be deleted. As long as the function does not
- * increment the buffer's reference counter between creating the request and
- * calling this function, the buffer will always be deleted regardless whether
- * the sending was failed or succeeded.
- *
- * @param endpt Endpoint instance.
- * @param tdata Transmit data buffer.
- * @param token Token to be associated with the SIP transaction which sends
- * this request.
- * @param cb The callback to be called when the SIP request has received
- * a final response from destination.
- *
- * @return Zero if the transaction was started successfully. Note that
- * this doesn't mean the request has been received successfully
- * by remote recipient.
- */
-PJ_DECL(pj_status_t) pjsip_messaging_send_msg( pjsip_endpoint *endpt,
- pjsip_tx_data *tdata,
- void *token,
- pjsip_messaging_cb cb );
-
-/**
- * Create an instant messaging session, which can conveniently be used to send
- * multiple instant messages to the same recipient.
- *
- * @param endpt Endpoint instance.
- * @param from URI of sender. The function will add a unique tag parameter
- * to this URI in the From header.
- * @param to URI of recipient.
- *
- * @return Messaging session.
- */
-PJ_DECL(pjsip_messaging_session*)
-pjsip_messaging_create_session( pjsip_endpoint *endpt,
- const pj_str_t *from,
- const pj_str_t *to );
-
-/**
- * Create an instant message using instant messaging session, and optionally
- * attach a text message.
- *
- * @param ses The instant messaging session.
- * @param text Optional "text/plain" message to be attached as the
- * message body. If this parameter is NULL, then no message
- * body will be created, and application can attach any
- * type of message body before the request is sent.
- *
- * @return SIP transmit data buffer, which reference counter has been
- * set to 1.
- */
-PJ_DECL(pjsip_tx_data*)
-pjsip_messaging_session_create_msg( pjsip_messaging_session *ses,
- const pj_str_t *text );
-
-/**
- * Destroy an instant messaging session.
- *
- * @param ses The instant messaging session.
- *
- * @return Zero on successfull.
- */
-PJ_DECL(pj_status_t)
-pjsip_messaging_destroy_session( pjsip_messaging_session *ses );
-
-/**
- * @}
- */
-
-PJ_END_DECL
-
-#endif
+/* $Id$ */
+/*
+ * Copyright (C) 2003-2006 Benny Prijono <benny@prijono.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#ifndef __PJSIP_SIMPLE_MESSAGING_H__
+#define __PJSIP_SIMPLE_MESSAGING_H__
+
+/**
+ * @file messaging.h
+ * @brief Instant Messaging Extension (RFC 3428)
+ */
+
+#include <pjsip/sip_msg.h>
+
+PJ_BEGIN_DECL
+
+/**
+ * @defgroup PJSIP_MESSAGING SIP Instant Messaging (RFC 3428) Module
+ * @ingroup PJSIP_SIMPLE
+ * @{
+ *
+ * This module provides the implementation of SIP Extension for Instant
+ * Messaging (RFC 3428). It extends PJSIP by supporting MESSAGE method.
+ *
+ * The RFC 3428 doesn't provide any means of dialog for the purpose of sending/
+ * receiving instant messaging. IM with SIP is basicly sessionless, which means
+ * that there is absolutely no correlation between IM messages sent or received
+ * by a host. Any correlation between IM messages is only perceivable by
+ * user, phsychologically.
+ *
+ * However, the RFC doesn't prohibit sending IM within a dialog (presumably
+ * using the same Call-ID and CSeq namespace), although it prohibits creating
+ * a dialog specificly for creating IM session.
+ *
+ * The implementation here is modeled to support both ways of sending IM msgs,
+ * i.e. sending IM message individually and sending IM message within a dialog.
+ * Although IM message can be associated with a dialog, this implementation of
+ * IM module is completely independent of the User Agent library in PJSIP. Yes,
+ * that's what is called modularity, and it demonstrates the clearness
+ * of PJSIP design (the last sentence is of course marketing crap :)).
+ *
+ * To send an IM message as part of dialog, application would first create the
+ * message using #pjsip_messaging_create_msg, using dialog's Call-ID, CSeq,
+ * From, and To header, then send the message using #pjsip_dlg_send_msg instead
+ * of #pjsip_messaging_send_msg.
+ *
+ * To send IM messages individually, application has two options. The first is
+ * to create the request with #pjsip_messaging_create_msg then send it with
+ * #pjsip_messaging_send_msg. But this way, application has to pre-construct
+ * From and To header first, which is not too convenient.
+ *
+ * The second option (to send IM messages not associated with a dialog) is to
+ * first create an 'IM session'. An 'IM session' here is not a SIP dialog, as
+ * it doesn't have Contact header etc. An 'IM session' here is just a local
+ * state to cache most of IM headers, for convenience and optimization. Appl
+ * creates an IM session with #pjsip_messaging_create_session, and destroy
+ * the session with #pjsip_messaging_destroy_session. To create an outgoing
+ * IM message, application would call #pjsip_messaging_session_create_msg,
+ * and to send the message it would use #pjsip_messaging_send_msg.
+ *
+ * Message authorization is handled by application, as usual, by inserting a
+ * proper WWW-Authenticate or Proxy-Authenticate header before sending the
+ * message.
+ *
+ * And the last bit, to handle incoing IM messages.
+ *
+ * To handle incoming IM messages, application would register a global callback
+ * to be called when incoming messages arrive, by registering with function
+ * #pjsip_messaging_set_incoming_callback. This will be the global callback
+ * for all incoming IM messages. Although the message was sent as part of
+ * a dialog, it would still come here. And as long as the request has proper
+ * identification (Call-ID, From/To tag), the dialog will be aware of the
+ * request and update it's state (remote CSeq) accordingly.
+ */
+
+
+
+/**
+ * Typedef for callback to be called when outgoing message has been sent
+ * and a final response has been received.
+ */
+typedef void (*pjsip_messaging_cb)(void *token, int status_code);
+
+/**
+ * Typedef for callback to receive incoming message.
+ *
+ * @param rdata Incoming message data.
+ *
+ * @return The status code to be returned back to original sender.
+ * Application must return a final status code upon returning
+ * from this function, or otherwise the stack will respond
+ * with error.
+ */
+typedef int (*pjsip_on_new_msg_cb)(pjsip_rx_data *rdata);
+
+/**
+ * Opaque data type for instant messaging session.
+ */
+typedef struct pjsip_messaging_session pjsip_messaging_session;
+
+/**
+ * Get the messaging module.
+ *
+ * @return SIP module.
+ */
+PJ_DECL(pjsip_module*) pjsip_messaging_get_module();
+
+/**
+ * Set the global callback to be called when incoming message is received.
+ *
+ * @param cb The callback to be called when incoming message is received.
+ *
+ * @return The previous callback.
+ */
+PJ_DECL(pjsip_on_new_msg_cb)
+pjsip_messaging_set_incoming_callback(pjsip_on_new_msg_cb cb);
+
+
+/**
+ * Create an instant message transmit data buffer using the specified arguments.
+ * The returned transmit data buffers will have it's reference counter set
+ * to 1, and when application send the buffer, the send function will decrement
+ * the reference counter. When the reference counter reach zero, the buffer
+ * will be deleted. As long as the function does not increment the buffer's
+ * reference counter between creating and sending the request, the buffer
+ * will always be deleted and no memory leak will occur.
+ *
+ * @param endpt Endpoint instance.
+ * @param target Target URL.
+ * @param from The "From" header, which content will be copied to request.
+ * If the "From" header doesn't have a tag parameter, the
+ * function will generate one.
+ * @param to The "To" header, which content will be copied to request.
+ * @param call_id Optionally specify Call-ID, or put NULL to make this
+ * function generate a unique Call-ID automatically.
+ * @param cseq Optionally specify CSeq, or put -1 to make this function
+ * generate a random CSeq.
+ * @param text Optionally specify "text/plain" message body, or put NULL
+ * if application wants to put body other than "text/plain"
+ * manually.
+ *
+ * @return SIP transmit data buffer, which reference count has been
+ * set to 1.
+ */
+PJ_DECL(pjsip_tx_data*)
+pjsip_messaging_create_msg_from_hdr(pjsip_endpoint *endpt,
+ const pjsip_uri *target,
+ const pjsip_from_hdr *from,
+ const pjsip_to_hdr *to,
+ const pjsip_cid_hdr *call_id,
+ int cseq,
+ const pj_str_t *text);
+
+/**
+ * Create instant message, by specifying URL string for both From and To header.
+ *
+ * @param endpt Endpoint instance.
+ * @param target Target URL.
+ * @param from URL of the sender.
+ * @param to URL of the recipient.
+ * @param call_id Optionally specify Call-ID, or put NULL to make this
+ * function generate a unique Call-ID automatically.
+ * @param cseq Optionally specify CSeq, or put -1 to make this function
+ * generate a random CSeq.
+ * @param text Optionally specify "text/plain" message body, or put NULL
+ * if application wants to put body other than "text/plain"
+ * manually.
+ *
+ * @return SIP transmit data buffer, which reference count has been
+ * set to 1.
+ */
+PJ_DECL(pjsip_tx_data*) pjsip_messaging_create_msg( pjsip_endpoint *endpt,
+ const pj_str_t *target,
+ const pj_str_t *from,
+ const pj_str_t *to,
+ const pj_str_t *call_id,
+ int cseq,
+ const pj_str_t *text);
+
+/**
+ * Send the instant message transmit buffer and attach a callback to be called
+ * when the request has received a final response. This function will decrement
+ * the transmit buffer's reference counter, and when the reference counter
+ * reach zero, the buffer will be deleted. As long as the function does not
+ * increment the buffer's reference counter between creating the request and
+ * calling this function, the buffer will always be deleted regardless whether
+ * the sending was failed or succeeded.
+ *
+ * @param endpt Endpoint instance.
+ * @param tdata Transmit data buffer.
+ * @param token Token to be associated with the SIP transaction which sends
+ * this request.
+ * @param cb The callback to be called when the SIP request has received
+ * a final response from destination.
+ *
+ * @return Zero if the transaction was started successfully. Note that
+ * this doesn't mean the request has been received successfully
+ * by remote recipient.
+ */
+PJ_DECL(pj_status_t) pjsip_messaging_send_msg( pjsip_endpoint *endpt,
+ pjsip_tx_data *tdata,
+ void *token,
+ pjsip_messaging_cb cb );
+
+/**
+ * Create an instant messaging session, which can conveniently be used to send
+ * multiple instant messages to the same recipient.
+ *
+ * @param endpt Endpoint instance.
+ * @param from URI of sender. The function will add a unique tag parameter
+ * to this URI in the From header.
+ * @param to URI of recipient.
+ *
+ * @return Messaging session.
+ */
+PJ_DECL(pjsip_messaging_session*)
+pjsip_messaging_create_session( pjsip_endpoint *endpt,
+ const pj_str_t *from,
+ const pj_str_t *to );
+
+/**
+ * Create an instant message using instant messaging session, and optionally
+ * attach a text message.
+ *
+ * @param ses The instant messaging session.
+ * @param text Optional "text/plain" message to be attached as the
+ * message body. If this parameter is NULL, then no message
+ * body will be created, and application can attach any
+ * type of message body before the request is sent.
+ *
+ * @return SIP transmit data buffer, which reference counter has been
+ * set to 1.
+ */
+PJ_DECL(pjsip_tx_data*)
+pjsip_messaging_session_create_msg( pjsip_messaging_session *ses,
+ const pj_str_t *text );
+
+/**
+ * Destroy an instant messaging session.
+ *
+ * @param ses The instant messaging session.
+ *
+ * @return Zero on successfull.
+ */
+PJ_DECL(pj_status_t)
+pjsip_messaging_destroy_session( pjsip_messaging_session *ses );
+
+/**
+ * @}
+ */
+
+PJ_END_DECL
+
+#endif
diff --git a/pjsip/include/pjsip-simple/pidf.h b/pjsip/include/pjsip-simple/pidf.h
index 70e12907..b9dd4509 100644
--- a/pjsip/include/pjsip-simple/pidf.h
+++ b/pjsip/include/pjsip-simple/pidf.h
@@ -1,176 +1,176 @@
-/* $Id$ */
-/*
- * Copyright (C) 2003-2006 Benny Prijono <benny@prijono.org>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
-#ifndef __PJSIP_SIMPLE_PIDF_H__
-#define __PJSIP_SIMPLE_PIDF_H__
-
-/**
- * @file pidf.h
- * @brief PIDF/Presence Information Data Format (RFC 3863)
- */
-#include <pj/types.h>
-#include <pj/xml.h>
-
-PJ_BEGIN_DECL
-
-
-/**
- * @defgroup PJSIP_SIMPLE_PIDF PIDF/Presence Information Data Format (RFC 3863)
- * @ingroup PJSIP_SIMPLE
- * @{
- *
- * This file provides tools for manipulating Presence Information Data
- * Format (PIDF) as described in RFC 3863.
- */
-typedef struct pj_xml_node pjpidf_pres;
-typedef struct pj_xml_node pjpidf_tuple;
-typedef struct pj_xml_node pjpidf_status;
-typedef struct pj_xml_node pjpidf_note;
-
-typedef struct pjpidf_status_op
-{
- void (*construct)(pj_pool_t*, pjpidf_status*);
- pj_bool_t (*is_basic_open)(const pjpidf_status*);
- void (*set_basic_open)(pjpidf_status*, pj_bool_t);
-} pjpidf_status_op;
-
-typedef struct pjpidf_tuple_op
-{
- void (*construct)(pj_pool_t*, pjpidf_tuple*, const pj_str_t*);
-
- const pj_str_t* (*get_id)(const pjpidf_tuple* );
- void (*set_id)(pj_pool_t*, pjpidf_tuple *, const pj_str_t*);
-
- pjpidf_status* (*get_status)(pjpidf_tuple* );
-
- const pj_str_t* (*get_contact)(const pjpidf_tuple*);
- void (*set_contact)(pj_pool_t*, pjpidf_tuple*, const pj_str_t*);
- void (*set_contact_prio)(pj_pool_t*, pjpidf_tuple*, const pj_str_t*);
- const pj_str_t* (*get_contact_prio)(const pjpidf_tuple*);
-
- pjpidf_note* (*add_note)(pj_pool_t*, pjpidf_tuple*, const pj_str_t*);
- pjpidf_note* (*get_first_note)(pjpidf_tuple*);
- pjpidf_note* (*get_next_note)(pjpidf_tuple*, pjpidf_note*);
-
- const pj_str_t* (*get_timestamp)(const pjpidf_tuple*);
- void (*set_timestamp)(pj_pool_t*, pjpidf_tuple*, const pj_str_t*);
- void (*set_timestamp_np)(pj_pool_t*,pjpidf_tuple*, pj_str_t*);
-
-} pjpidf_tuple_op;
-
-typedef struct pjpidf_pres_op
-{
- void (*construct)(pj_pool_t*, pjpidf_pres*, const pj_str_t*);
-
- pjpidf_tuple* (*add_tuple)(pj_pool_t*, pjpidf_pres*, const pj_str_t*);
- pjpidf_tuple* (*get_first_tuple)(pjpidf_pres*);
- pjpidf_tuple* (*get_next_tuple)(pjpidf_pres*, pjpidf_tuple*);
- pjpidf_tuple* (*find_tuple)(pjpidf_pres*, const pj_str_t*);
- void (*remove_tuple)(pjpidf_pres*, pjpidf_tuple*);
-
- pjpidf_note* (*add_note)(pj_pool_t*, pjpidf_pres*, const pj_str_t*);
- pjpidf_note* (*get_first_note)(pjpidf_pres*);
- pjpidf_note* (*get_next_note)(pjpidf_pres*, pjpidf_note*);
-
-} pjpidf_pres_op;
-
-
-extern struct pjpidf_op_desc
-{
- pjpidf_pres_op pres;
- pjpidf_tuple_op tuple;
- pjpidf_status_op status;
-} pjpidf_op;
-
-
-/******************************************************************************
- * Top level API for managing presence document.
- *****************************************************************************/
-PJ_DECL(pjpidf_pres*) pjpidf_create(pj_pool_t *pool, const pj_str_t *entity);
-PJ_DECL(pjpidf_pres*) pjpidf_parse(pj_pool_t *pool, char *text, int len);
-PJ_DECL(int) pjpidf_print(const pjpidf_pres* pres, char *buf, int len);
-
-
-/******************************************************************************
- * API for managing Presence node.
- *****************************************************************************/
-PJ_DECL(void) pjpidf_pres_construct(pj_pool_t *pool, pjpidf_pres *pres,
- const pj_str_t *entity);
-PJ_DECL(pjpidf_tuple*) pjpidf_pres_add_tuple(pj_pool_t *pool, pjpidf_pres *pres,
- const pj_str_t *id);
-PJ_DECL(pjpidf_tuple*) pjpidf_pres_get_first_tuple(pjpidf_pres *pres);
-PJ_DECL(pjpidf_tuple*) pjpidf_pres_get_next_tuple(pjpidf_pres *pres,
- pjpidf_tuple *t);
-PJ_DECL(pjpidf_tuple*) pjpidf_pres_find_tuple(pjpidf_pres *pres,
- const pj_str_t *id);
-PJ_DECL(void) pjpidf_pres_remove_tuple(pjpidf_pres *pres,
- pjpidf_tuple*);
-
-PJ_DECL(pjpidf_note*) pjpidf_pres_add_note(pj_pool_t *pool, pjpidf_pres *pres,
- const pj_str_t *text);
-PJ_DECL(pjpidf_note*) pjpidf_pres_get_first_note(pjpidf_pres *pres);
-PJ_DECL(pjpidf_note*) pjpidf_pres_get_next_note(pjpidf_pres*, pjpidf_note*);
-
-
-/******************************************************************************
- * API for managing Tuple node.
- *****************************************************************************/
-PJ_DECL(void) pjpidf_tuple_construct(pj_pool_t *pool, pjpidf_tuple *t,
- const pj_str_t *id);
-PJ_DECL(const pj_str_t*) pjpidf_tuple_get_id(const pjpidf_tuple *t );
-PJ_DECL(void) pjpidf_tuple_set_id(pj_pool_t *pool, pjpidf_tuple *t,
- const pj_str_t *id);
-
-PJ_DECL(pjpidf_status*) pjpidf_tuple_get_status(pjpidf_tuple *t);
-
-PJ_DECL(const pj_str_t*) pjpidf_tuple_get_contact(const pjpidf_tuple *t);
-PJ_DECL(void) pjpidf_tuple_set_contact(pj_pool_t *pool, pjpidf_tuple *t,
- const pj_str_t *contact);
-PJ_DECL(void) pjpidf_tuple_set_contact_prio(pj_pool_t *pool, pjpidf_tuple *t,
- const pj_str_t *prio);
-PJ_DECL(const pj_str_t*) pjpidf_tuple_get_contact_prio(const pjpidf_tuple *t);
-
-PJ_DECL(pjpidf_note*) pjpidf_tuple_add_note(pj_pool_t *pool, pjpidf_tuple *t,
- const pj_str_t *text);
-PJ_DECL(pjpidf_note*) pjpidf_tuple_get_first_note(pjpidf_tuple *t);
-PJ_DECL(pjpidf_note*) pjpidf_tuple_get_next_note(pjpidf_tuple *t, pjpidf_note *n);
-
-PJ_DECL(const pj_str_t*) pjpidf_tuple_get_timestamp(const pjpidf_tuple *t);
-PJ_DECL(void) pjpidf_tuple_set_timestamp(pj_pool_t *pool, pjpidf_tuple *t,
- const pj_str_t *ts);
-PJ_DECL(void) pjpidf_tuple_set_timestamp_np( pj_pool_t*, pjpidf_tuple *t,
- pj_str_t *ts);
-
-
-/******************************************************************************
- * API for managing Status node.
- *****************************************************************************/
-PJ_DECL(void) pjpidf_status_construct(pj_pool_t*, pjpidf_status*);
-PJ_DECL(pj_bool_t) pjpidf_status_is_basic_open(const pjpidf_status*);
-PJ_DECL(void) pjpidf_status_set_basic_open(pjpidf_status*, pj_bool_t);
-
-
-/**
- * @}
- */
-
-
-PJ_END_DECL
-
-
-#endif /* __PJSIP_SIMPLE_PIDF_H__ */
+/* $Id$ */
+/*
+ * Copyright (C) 2003-2006 Benny Prijono <benny@prijono.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#ifndef __PJSIP_SIMPLE_PIDF_H__
+#define __PJSIP_SIMPLE_PIDF_H__
+
+/**
+ * @file pidf.h
+ * @brief PIDF/Presence Information Data Format (RFC 3863)
+ */
+#include <pj/types.h>
+#include <pj/xml.h>
+
+PJ_BEGIN_DECL
+
+
+/**
+ * @defgroup PJSIP_SIMPLE_PIDF PIDF/Presence Information Data Format (RFC 3863)
+ * @ingroup PJSIP_SIMPLE
+ * @{
+ *
+ * This file provides tools for manipulating Presence Information Data
+ * Format (PIDF) as described in RFC 3863.
+ */
+typedef struct pj_xml_node pjpidf_pres;
+typedef struct pj_xml_node pjpidf_tuple;
+typedef struct pj_xml_node pjpidf_status;
+typedef struct pj_xml_node pjpidf_note;
+
+typedef struct pjpidf_status_op
+{
+ void (*construct)(pj_pool_t*, pjpidf_status*);
+ pj_bool_t (*is_basic_open)(const pjpidf_status*);
+ void (*set_basic_open)(pjpidf_status*, pj_bool_t);
+} pjpidf_status_op;
+
+typedef struct pjpidf_tuple_op
+{
+ void (*construct)(pj_pool_t*, pjpidf_tuple*, const pj_str_t*);
+
+ const pj_str_t* (*get_id)(const pjpidf_tuple* );
+ void (*set_id)(pj_pool_t*, pjpidf_tuple *, const pj_str_t*);
+
+ pjpidf_status* (*get_status)(pjpidf_tuple* );
+
+ const pj_str_t* (*get_contact)(const pjpidf_tuple*);
+ void (*set_contact)(pj_pool_t*, pjpidf_tuple*, const pj_str_t*);
+ void (*set_contact_prio)(pj_pool_t*, pjpidf_tuple*, const pj_str_t*);
+ const pj_str_t* (*get_contact_prio)(const pjpidf_tuple*);
+
+ pjpidf_note* (*add_note)(pj_pool_t*, pjpidf_tuple*, const pj_str_t*);
+ pjpidf_note* (*get_first_note)(pjpidf_tuple*);
+ pjpidf_note* (*get_next_note)(pjpidf_tuple*, pjpidf_note*);
+
+ const pj_str_t* (*get_timestamp)(const pjpidf_tuple*);
+ void (*set_timestamp)(pj_pool_t*, pjpidf_tuple*, const pj_str_t*);
+ void (*set_timestamp_np)(pj_pool_t*,pjpidf_tuple*, pj_str_t*);
+
+} pjpidf_tuple_op;
+
+typedef struct pjpidf_pres_op
+{
+ void (*construct)(pj_pool_t*, pjpidf_pres*, const pj_str_t*);
+
+ pjpidf_tuple* (*add_tuple)(pj_pool_t*, pjpidf_pres*, const pj_str_t*);
+ pjpidf_tuple* (*get_first_tuple)(pjpidf_pres*);
+ pjpidf_tuple* (*get_next_tuple)(pjpidf_pres*, pjpidf_tuple*);
+ pjpidf_tuple* (*find_tuple)(pjpidf_pres*, const pj_str_t*);
+ void (*remove_tuple)(pjpidf_pres*, pjpidf_tuple*);
+
+ pjpidf_note* (*add_note)(pj_pool_t*, pjpidf_pres*, const pj_str_t*);
+ pjpidf_note* (*get_first_note)(pjpidf_pres*);
+ pjpidf_note* (*get_next_note)(pjpidf_pres*, pjpidf_note*);
+
+} pjpidf_pres_op;
+
+
+extern struct pjpidf_op_desc
+{
+ pjpidf_pres_op pres;
+ pjpidf_tuple_op tuple;
+ pjpidf_status_op status;
+} pjpidf_op;
+
+
+/******************************************************************************
+ * Top level API for managing presence document.
+ *****************************************************************************/
+PJ_DECL(pjpidf_pres*) pjpidf_create(pj_pool_t *pool, const pj_str_t *entity);
+PJ_DECL(pjpidf_pres*) pjpidf_parse(pj_pool_t *pool, char *text, int len);
+PJ_DECL(int) pjpidf_print(const pjpidf_pres* pres, char *buf, int len);
+
+
+/******************************************************************************
+ * API for managing Presence node.
+ *****************************************************************************/
+PJ_DECL(void) pjpidf_pres_construct(pj_pool_t *pool, pjpidf_pres *pres,
+ const pj_str_t *entity);
+PJ_DECL(pjpidf_tuple*) pjpidf_pres_add_tuple(pj_pool_t *pool, pjpidf_pres *pres,
+ const pj_str_t *id);
+PJ_DECL(pjpidf_tuple*) pjpidf_pres_get_first_tuple(pjpidf_pres *pres);
+PJ_DECL(pjpidf_tuple*) pjpidf_pres_get_next_tuple(pjpidf_pres *pres,
+ pjpidf_tuple *t);
+PJ_DECL(pjpidf_tuple*) pjpidf_pres_find_tuple(pjpidf_pres *pres,
+ const pj_str_t *id);
+PJ_DECL(void) pjpidf_pres_remove_tuple(pjpidf_pres *pres,
+ pjpidf_tuple*);
+
+PJ_DECL(pjpidf_note*) pjpidf_pres_add_note(pj_pool_t *pool, pjpidf_pres *pres,
+ const pj_str_t *text);
+PJ_DECL(pjpidf_note*) pjpidf_pres_get_first_note(pjpidf_pres *pres);
+PJ_DECL(pjpidf_note*) pjpidf_pres_get_next_note(pjpidf_pres*, pjpidf_note*);
+
+
+/******************************************************************************
+ * API for managing Tuple node.
+ *****************************************************************************/
+PJ_DECL(void) pjpidf_tuple_construct(pj_pool_t *pool, pjpidf_tuple *t,
+ const pj_str_t *id);
+PJ_DECL(const pj_str_t*) pjpidf_tuple_get_id(const pjpidf_tuple *t );
+PJ_DECL(void) pjpidf_tuple_set_id(pj_pool_t *pool, pjpidf_tuple *t,
+ const pj_str_t *id);
+
+PJ_DECL(pjpidf_status*) pjpidf_tuple_get_status(pjpidf_tuple *t);
+
+PJ_DECL(const pj_str_t*) pjpidf_tuple_get_contact(const pjpidf_tuple *t);
+PJ_DECL(void) pjpidf_tuple_set_contact(pj_pool_t *pool, pjpidf_tuple *t,
+ const pj_str_t *contact);
+PJ_DECL(void) pjpidf_tuple_set_contact_prio(pj_pool_t *pool, pjpidf_tuple *t,
+ const pj_str_t *prio);
+PJ_DECL(const pj_str_t*) pjpidf_tuple_get_contact_prio(const pjpidf_tuple *t);
+
+PJ_DECL(pjpidf_note*) pjpidf_tuple_add_note(pj_pool_t *pool, pjpidf_tuple *t,
+ const pj_str_t *text);
+PJ_DECL(pjpidf_note*) pjpidf_tuple_get_first_note(pjpidf_tuple *t);
+PJ_DECL(pjpidf_note*) pjpidf_tuple_get_next_note(pjpidf_tuple *t, pjpidf_note *n);
+
+PJ_DECL(const pj_str_t*) pjpidf_tuple_get_timestamp(const pjpidf_tuple *t);
+PJ_DECL(void) pjpidf_tuple_set_timestamp(pj_pool_t *pool, pjpidf_tuple *t,
+ const pj_str_t *ts);
+PJ_DECL(void) pjpidf_tuple_set_timestamp_np( pj_pool_t*, pjpidf_tuple *t,
+ pj_str_t *ts);
+
+
+/******************************************************************************
+ * API for managing Status node.
+ *****************************************************************************/
+PJ_DECL(void) pjpidf_status_construct(pj_pool_t*, pjpidf_status*);
+PJ_DECL(pj_bool_t) pjpidf_status_is_basic_open(const pjpidf_status*);
+PJ_DECL(void) pjpidf_status_set_basic_open(pjpidf_status*, pj_bool_t);
+
+
+/**
+ * @}
+ */
+
+
+PJ_END_DECL
+
+
+#endif /* __PJSIP_SIMPLE_PIDF_H__ */
diff --git a/pjsip/include/pjsip-simple/presence.h b/pjsip/include/pjsip-simple/presence.h
index e4f34274..180ac4d1 100644
--- a/pjsip/include/pjsip-simple/presence.h
+++ b/pjsip/include/pjsip-simple/presence.h
@@ -1,229 +1,229 @@
-/* $Id$ */
-/*
- * Copyright (C) 2003-2006 Benny Prijono <benny@prijono.org>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
-#ifndef __PJSIP_SIMPLE_PRESENCE_H__
-#define __PJSIP_SIMPLE_PRESENCE_H__
-
-/**
- * @file presence.h
- * @brief SIP Extension for Presence (RFC 3856)
- */
-#include <pjsip_simple/event_notify.h>
-#include <pjsip_simple/pidf.h>
-#include <pjsip_simple/xpidf.h>
-
-
-PJ_BEGIN_DECL
-
-
-/**
- * @defgroup PJSIP_SIMPLE_PRES SIP Extension for Presence (RFC 3856)
- * @ingroup PJSIP_SIMPLE
- * @{
- *
- * This module contains the implementation of SIP Presence Extension as
- * described in RFC 3856. It uses the SIP Event Notification framework
- * (event_notify.h) and extends the framework by implementing "presence"
- * event package.
- */
-
-/**
- * Presence message body type.
- */
-typedef enum pjsip_pres_type
-{
- PJSIP_PRES_TYPE_PIDF,
- PJSIP_PRES_TYPE_XPIDF,
-} pjsip_pres_type;
-
-/**
- * This structure describe a presentity, for both subscriber and notifier.
- */
-typedef struct pjsip_presentity
-{
- pjsip_event_sub *sub; /**< Event subscribtion record. */
- pjsip_pres_type pres_type; /**< Presentity type. */
- pjsip_msg_body *uas_body; /**< Message body (UAS only). */
- union {
- pjpidf_pres *pidf;
- pjxpidf_pres *xpidf;
- } uas_data; /**< UAS data. */
- pj_str_t timestamp; /**< Time of last update. */
- void *user_data; /**< Application data. */
-} pjsip_presentity;
-
-
-/**
- * This structure describe callback that is registered to receive notification
- * from the presence module.
- */
-typedef struct pjsip_presence_cb
-{
- /**
- * This callback is first called when the module receives incoming
- * SUBSCRIBE request to determine whether application wants to accept
- * the request. If it does, then on_presence_request will be called.
- *
- * @param rdata The received message.
- * @return Application should return 2xx to accept the request,
- * or failure status (>=300) to reject the request.
- */
- void (*accept_presence)(pjsip_rx_data *rdata, int *status);
-
- /**
- * This callback is called when the module receive the first presence
- * subscription request.
- *
- * @param pres The presence descriptor.
- * @param rdata The incoming request.
- * @param timeout Timeout to be set for incoming request. Otherwise
- * app can just leave this and accept the default.
- */
- void (*on_received_request)(pjsip_presentity *pres, pjsip_rx_data *rdata,
- int *timeout);
-
- /**
- * This callback is called when the module received subscription refresh
- * request.
- *
- * @param pres The presence descriptor.
- * @param rdata The incoming request.
- */
- void (*on_received_refresh)(pjsip_presentity *pres, pjsip_rx_data *rdata);
-
- /**
- * This callback is called when the module receives incoming NOTIFY
- * request.
- *
- * @param pres The presence descriptor.
- * @param open The latest status of the presentity.
- */
- void (*on_received_update)(pjsip_presentity *pres, pj_bool_t open);
-
- /**
- * This callback is called when the subscription has terminated.
- *
- * @param sub The subscription instance.
- * @param reason The termination reason.
- */
- void (*on_terminated)(pjsip_presentity *pres, const pj_str_t *reason);
-
-} pjsip_presence_cb;
-
-
-/**
- * Initialize the presence module and register callback.
- *
- * @param cb Callback structure.
- */
-PJ_DECL(void) pjsip_presence_init(const pjsip_presence_cb *cb);
-
-
-/**
- * Create to presence subscription of a presentity URL.
- *
- * @param endpt Endpoint instance.
- * @param local_url Local URL.
- * @param remote_url Remote URL which the presence is being subscribed.
- * @param expires The expiration.
- * @param user_data User data to attach to presence subscription.
- *
- * @return The presence structure if successfull, or NULL if
- * failed.
- */
-PJ_DECL(pjsip_presentity*) pjsip_presence_create( pjsip_endpoint *endpt,
- const pj_str_t *local_url,
- const pj_str_t *remote_url,
- int expires,
- void *user_data );
-
-/**
- * Set credentials to be used by this presentity for outgoing requests.
- *
- * @param pres Presentity instance.
- * @param count Number of credentials in the array.
- * @param cred Array of credentials.
- *
- * @return Zero on success.
- */
-PJ_DECL(pj_status_t) pjsip_presence_set_credentials( pjsip_presentity *pres,
- int count,
- const pjsip_cred_info cred[]);
-
-/**
- * Set route set for outgoing requests.
- *
- * @param pres Presentity instance.
- * @param route_set List of route headers.
- *
- * @return Zero on success.
- */
-PJ_DECL(pj_status_t) pjsip_presence_set_route_set( pjsip_presentity *pres,
- const pjsip_route_hdr *hdr );
-
-/**
- * Send SUBSCRIBE request for the specified presentity.
- *
- * @param pres The presentity instance.
- *
- * @return Zero on success.
- */
-PJ_DECL(pj_status_t) pjsip_presence_subscribe( pjsip_presentity *pres );
-
-/**
- * Ceased the presence subscription.
- *
- * @param pres The presence structure.
- *
- * @return Zero on success.
- */
-PJ_DECL(pj_status_t) pjsip_presence_unsubscribe( pjsip_presentity *pres );
-
-/**
- * Notify subscriber about change in local status.
- *
- * @param pres The presence structure.
- * @param state Set the state of the subscription.
- * @param open Set the presence status (open or closed).
- *
- * @return Zero if a NOTIFY request can be sent.
- */
-PJ_DECL(pj_status_t) pjsip_presence_notify( pjsip_presentity *pres,
- pjsip_event_sub_state state,
- pj_bool_t open );
-
-/**
- * Destroy presence structure and the underlying subscription.
- *
- * @param pres The presence structure.
- *
- * @return Zero if the subscription was destroyed, or one if
- * the subscription can not be destroyed immediately
- * and will be destroyed later, or -1 if failed.
- */
-PJ_DECL(pj_status_t) pjsip_presence_destroy( pjsip_presentity *pres );
-
-
-/**
- * @}
- */
-
-PJ_END_DECL
-
-
-#endif /* __PJSIP_SIMPLE_PRESENCE_H__ */
+/* $Id$ */
+/*
+ * Copyright (C) 2003-2006 Benny Prijono <benny@prijono.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#ifndef __PJSIP_SIMPLE_PRESENCE_H__
+#define __PJSIP_SIMPLE_PRESENCE_H__
+
+/**
+ * @file presence.h
+ * @brief SIP Extension for Presence (RFC 3856)
+ */
+#include <pjsip_simple/event_notify.h>
+#include <pjsip_simple/pidf.h>
+#include <pjsip_simple/xpidf.h>
+
+
+PJ_BEGIN_DECL
+
+
+/**
+ * @defgroup PJSIP_SIMPLE_PRES SIP Extension for Presence (RFC 3856)
+ * @ingroup PJSIP_SIMPLE
+ * @{
+ *
+ * This module contains the implementation of SIP Presence Extension as
+ * described in RFC 3856. It uses the SIP Event Notification framework
+ * (event_notify.h) and extends the framework by implementing "presence"
+ * event package.
+ */
+
+/**
+ * Presence message body type.
+ */
+typedef enum pjsip_pres_type
+{
+ PJSIP_PRES_TYPE_PIDF,
+ PJSIP_PRES_TYPE_XPIDF,
+} pjsip_pres_type;
+
+/**
+ * This structure describe a presentity, for both subscriber and notifier.
+ */
+typedef struct pjsip_presentity
+{
+ pjsip_event_sub *sub; /**< Event subscribtion record. */
+ pjsip_pres_type pres_type; /**< Presentity type. */
+ pjsip_msg_body *uas_body; /**< Message body (UAS only). */
+ union {
+ pjpidf_pres *pidf;
+ pjxpidf_pres *xpidf;
+ } uas_data; /**< UAS data. */
+ pj_str_t timestamp; /**< Time of last update. */
+ void *user_data; /**< Application data. */
+} pjsip_presentity;
+
+
+/**
+ * This structure describe callback that is registered to receive notification
+ * from the presence module.
+ */
+typedef struct pjsip_presence_cb
+{
+ /**
+ * This callback is first called when the module receives incoming
+ * SUBSCRIBE request to determine whether application wants to accept
+ * the request. If it does, then on_presence_request will be called.
+ *
+ * @param rdata The received message.
+ * @return Application should return 2xx to accept the request,
+ * or failure status (>=300) to reject the request.
+ */
+ void (*accept_presence)(pjsip_rx_data *rdata, int *status);
+
+ /**
+ * This callback is called when the module receive the first presence
+ * subscription request.
+ *
+ * @param pres The presence descriptor.
+ * @param rdata The incoming request.
+ * @param timeout Timeout to be set for incoming request. Otherwise
+ * app can just leave this and accept the default.
+ */
+ void (*on_received_request)(pjsip_presentity *pres, pjsip_rx_data *rdata,
+ int *timeout);
+
+ /**
+ * This callback is called when the module received subscription refresh
+ * request.
+ *
+ * @param pres The presence descriptor.
+ * @param rdata The incoming request.
+ */
+ void (*on_received_refresh)(pjsip_presentity *pres, pjsip_rx_data *rdata);
+
+ /**
+ * This callback is called when the module receives incoming NOTIFY
+ * request.
+ *
+ * @param pres The presence descriptor.
+ * @param open The latest status of the presentity.
+ */
+ void (*on_received_update)(pjsip_presentity *pres, pj_bool_t open);
+
+ /**
+ * This callback is called when the subscription has terminated.
+ *
+ * @param sub The subscription instance.
+ * @param reason The termination reason.
+ */
+ void (*on_terminated)(pjsip_presentity *pres, const pj_str_t *reason);
+
+} pjsip_presence_cb;
+
+
+/**
+ * Initialize the presence module and register callback.
+ *
+ * @param cb Callback structure.
+ */
+PJ_DECL(void) pjsip_presence_init(const pjsip_presence_cb *cb);
+
+
+/**
+ * Create to presence subscription of a presentity URL.
+ *
+ * @param endpt Endpoint instance.
+ * @param local_url Local URL.
+ * @param remote_url Remote URL which the presence is being subscribed.
+ * @param expires The expiration.
+ * @param user_data User data to attach to presence subscription.
+ *
+ * @return The presence structure if successfull, or NULL if
+ * failed.
+ */
+PJ_DECL(pjsip_presentity*) pjsip_presence_create( pjsip_endpoint *endpt,
+ const pj_str_t *local_url,
+ const pj_str_t *remote_url,
+ int expires,
+ void *user_data );
+
+/**
+ * Set credentials to be used by this presentity for outgoing requests.
+ *
+ * @param pres Presentity instance.
+ * @param count Number of credentials in the array.
+ * @param cred Array of credentials.
+ *
+ * @return Zero on success.
+ */
+PJ_DECL(pj_status_t) pjsip_presence_set_credentials( pjsip_presentity *pres,
+ int count,
+ const pjsip_cred_info cred[]);
+
+/**
+ * Set route set for outgoing requests.
+ *
+ * @param pres Presentity instance.
+ * @param route_set List of route headers.
+ *
+ * @return Zero on success.
+ */
+PJ_DECL(pj_status_t) pjsip_presence_set_route_set( pjsip_presentity *pres,
+ const pjsip_route_hdr *hdr );
+
+/**
+ * Send SUBSCRIBE request for the specified presentity.
+ *
+ * @param pres The presentity instance.
+ *
+ * @return Zero on success.
+ */
+PJ_DECL(pj_status_t) pjsip_presence_subscribe( pjsip_presentity *pres );
+
+/**
+ * Ceased the presence subscription.
+ *
+ * @param pres The presence structure.
+ *
+ * @return Zero on success.
+ */
+PJ_DECL(pj_status_t) pjsip_presence_unsubscribe( pjsip_presentity *pres );
+
+/**
+ * Notify subscriber about change in local status.
+ *
+ * @param pres The presence structure.
+ * @param state Set the state of the subscription.
+ * @param open Set the presence status (open or closed).
+ *
+ * @return Zero if a NOTIFY request can be sent.
+ */
+PJ_DECL(pj_status_t) pjsip_presence_notify( pjsip_presentity *pres,
+ pjsip_event_sub_state state,
+ pj_bool_t open );
+
+/**
+ * Destroy presence structure and the underlying subscription.
+ *
+ * @param pres The presence structure.
+ *
+ * @return Zero if the subscription was destroyed, or one if
+ * the subscription can not be destroyed immediately
+ * and will be destroyed later, or -1 if failed.
+ */
+PJ_DECL(pj_status_t) pjsip_presence_destroy( pjsip_presentity *pres );
+
+
+/**
+ * @}
+ */
+
+PJ_END_DECL
+
+
+#endif /* __PJSIP_SIMPLE_PRESENCE_H__ */
diff --git a/pjsip/include/pjsip-simple/xpidf.h b/pjsip/include/pjsip-simple/xpidf.h
index a94a4f92..59d3c398 100644
--- a/pjsip/include/pjsip-simple/xpidf.h
+++ b/pjsip/include/pjsip-simple/xpidf.h
@@ -1,133 +1,133 @@
-/* $Id$ */
-/*
- * Copyright (C) 2003-2006 Benny Prijono <benny@prijono.org>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
-#ifndef __PJSIP_SIMPLE_XPIDF_H__
-#define __PJSIP_SIMPLE_XPIDF_H__
-
-/**
- * @file xpidf.h
- * @brief XPIDF/Presence Information Data Format
- */
-#include <pj/types.h>
-#include <pj/xml.h>
-
-PJ_BEGIN_DECL
-
-/**
- * @defgroup PJSIP_SIMPLE_XPIDF XPIDF/Presence Information Data Format
- * @ingroup PJSIP_SIMPLE
- * @{
- *
- * This is an old presence data format as described in:
- * draft-rosenberg-impp-pidf-00.txt.
- *
- * We won't support this format extensively here, as it seems there's not
- * too many implementations support this anymore, as it shouldn't.
- */
-
-/** Type definitions for XPIDF root document. */
-typedef pj_xml_node pjxpidf_pres;
-
-
-/**
- * Create a new XPIDF document.
- *
- * @param pool Pool.
- * @param uri URI to set in the XPIDF document.
- *
- * @return XPIDF document.
- */
-PJ_DECL(pjxpidf_pres*) pjxpidf_create(pj_pool_t *pool, const pj_str_t *uri);
-
-
-/**
- * Parse XPIDF document.
- *
- * @param pool Pool.
- * @param text Input text.
- * @param len Length of input text.
- *
- * @return XPIDF document.
- */
-PJ_DECL(pjxpidf_pres*) pjxpidf_parse(pj_pool_t *pool, char *text, pj_size_t len);
-
-
-/**
- * Print XPIDF document.
- *
- * @param pres The XPIDF document to print.
- * @param text Buffer to place the output.
- * @param len Length of the buffer.
- *
- * @return The length printed.
- */
-PJ_DECL(int) pjxpidf_print( pjxpidf_pres *pres, char *text, pj_size_t len);
-
-
-/**
- * Get URI in the XPIDF document
- *
- * @param pres XPIDF document
- *
- * @return The URI, or an empty string.
- */
-PJ_DECL(pj_str_t*) pjxpidf_get_uri(pjxpidf_pres *pres);
-
-
-/**
- * Set the URI of the XPIDF document.
- *
- * @param pool Pool.
- * @param pres The XPIDF document.
- * @param uri URI to set in the XPIDF document.
- *
- * @return Zero on success.
- */
-PJ_DECL(pj_status_t) pjxpidf_set_uri(pj_pool_t *pool, pjxpidf_pres *pres,
- const pj_str_t *uri);
-
-
-/**
- * Get presence status in the XPIDF document.
- *
- * @param pres XPIDF document.
- *
- * @return True to indicate the contact is online.
- */
-PJ_DECL(pj_bool_t) pjxpidf_get_status(pjxpidf_pres *pres);
-
-
-/**
- * Set presence status in the XPIDF document.
- *
- * @param pres XPIDF document.
- * @param status Status to set, True for online, False for offline.
- *
- * @return Zero on success.
- */
-PJ_DECL(pj_status_t) pjxpidf_set_status(pjxpidf_pres *pres, pj_bool_t status);
-
-
-/**
- * @}
- */
-
-PJ_END_DECL
-
-
-#endif /* __PJSIP_SIMPLE_XPIDF_H__ */
+/* $Id$ */
+/*
+ * Copyright (C) 2003-2006 Benny Prijono <benny@prijono.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#ifndef __PJSIP_SIMPLE_XPIDF_H__
+#define __PJSIP_SIMPLE_XPIDF_H__
+
+/**
+ * @file xpidf.h
+ * @brief XPIDF/Presence Information Data Format
+ */
+#include <pj/types.h>
+#include <pj/xml.h>
+
+PJ_BEGIN_DECL
+
+/**
+ * @defgroup PJSIP_SIMPLE_XPIDF XPIDF/Presence Information Data Format
+ * @ingroup PJSIP_SIMPLE
+ * @{
+ *
+ * This is an old presence data format as described in:
+ * draft-rosenberg-impp-pidf-00.txt.
+ *
+ * We won't support this format extensively here, as it seems there's not
+ * too many implementations support this anymore, as it shouldn't.
+ */
+
+/** Type definitions for XPIDF root document. */
+typedef pj_xml_node pjxpidf_pres;
+
+
+/**
+ * Create a new XPIDF document.
+ *
+ * @param pool Pool.
+ * @param uri URI to set in the XPIDF document.
+ *
+ * @return XPIDF document.
+ */
+PJ_DECL(pjxpidf_pres*) pjxpidf_create(pj_pool_t *pool, const pj_str_t *uri);
+
+
+/**
+ * Parse XPIDF document.
+ *
+ * @param pool Pool.
+ * @param text Input text.
+ * @param len Length of input text.
+ *
+ * @return XPIDF document.
+ */
+PJ_DECL(pjxpidf_pres*) pjxpidf_parse(pj_pool_t *pool, char *text, pj_size_t len);
+
+
+/**
+ * Print XPIDF document.
+ *
+ * @param pres The XPIDF document to print.
+ * @param text Buffer to place the output.
+ * @param len Length of the buffer.
+ *
+ * @return The length printed.
+ */
+PJ_DECL(int) pjxpidf_print( pjxpidf_pres *pres, char *text, pj_size_t len);
+
+
+/**
+ * Get URI in the XPIDF document
+ *
+ * @param pres XPIDF document
+ *
+ * @return The URI, or an empty string.
+ */
+PJ_DECL(pj_str_t*) pjxpidf_get_uri(pjxpidf_pres *pres);
+
+
+/**
+ * Set the URI of the XPIDF document.
+ *
+ * @param pool Pool.
+ * @param pres The XPIDF document.
+ * @param uri URI to set in the XPIDF document.
+ *
+ * @return Zero on success.
+ */
+PJ_DECL(pj_status_t) pjxpidf_set_uri(pj_pool_t *pool, pjxpidf_pres *pres,
+ const pj_str_t *uri);
+
+
+/**
+ * Get presence status in the XPIDF document.
+ *
+ * @param pres XPIDF document.
+ *
+ * @return True to indicate the contact is online.
+ */
+PJ_DECL(pj_bool_t) pjxpidf_get_status(pjxpidf_pres *pres);
+
+
+/**
+ * Set presence status in the XPIDF document.
+ *
+ * @param pres XPIDF document.
+ * @param status Status to set, True for online, False for offline.
+ *
+ * @return Zero on success.
+ */
+PJ_DECL(pj_status_t) pjxpidf_set_status(pjxpidf_pres *pres, pj_bool_t status);
+
+
+/**
+ * @}
+ */
+
+PJ_END_DECL
+
+
+#endif /* __PJSIP_SIMPLE_XPIDF_H__ */
diff --git a/pjsip/include/pjsip-ua/sip_dialog.h b/pjsip/include/pjsip-ua/sip_dialog.h
index b02ddb35..d46e3873 100644
--- a/pjsip/include/pjsip-ua/sip_dialog.h
+++ b/pjsip/include/pjsip-ua/sip_dialog.h
@@ -1,635 +1,635 @@
-/* $Id$ */
-/*
- * Copyright (C) 2003-2006 Benny Prijono <benny@prijono.org>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
-#ifndef __PJSIP_DIALOG_H__
-#define __PJSIP_DIALOG_H__
-
-/**
- * @file dialog.h
- * @brief SIP Dialog abstraction
- */
-
-#include <pjsip/sip_msg.h>
-#include <pjsip/sip_auth.h>
-#include <pj/sock.h>
-
-PJ_BEGIN_DECL
-
-/**
- * @defgroup PJSUA_DIALOG SIP Dialog
- * @ingroup PJSUA
- * @{
- * \brief
- * This file contains SIP dialog, a higher level abstraction of SIP session.
- *
- * \par Overview
- * A SIP dialog is an abstraction of communication session between two user
- * agents that persist for some time. The dialog facilitates sequencing of
- * messages between the user agents and proper routing of requests between both
- * of them. The dialog represents a context in which to interpret SIP messages.
- * However method independent User Agent processing for requests and responses
- * outside of a dialog exists, hence a dialog is not necessary for message
- * processing.
- *
- * A dialog is identified at each User Agent with a dialog Id, which consists
- * of a Call-Id value, a local tag and a remote tag.
- *
- * A dialog contains certain pieces of data needed for further message
- * transmissions within the dialog. This data consists of:
- * - Dialog Id - used to identify the dialog.
- * - Local sequence number - used to order requests from the UA to its peer.
- * - Remote sequence number - used to order requests from its peer to the UA.
- * - Local URI - the address of the local party.
- * - Remote URI - the address of the remote party.
- * - Remote target - the address from the Contact header field of the request
- * or response or refresh request or response.
- * - "secure" boolean - determines if the dialog is secure.
- * - Route set - an ordered list of URIs. The route set is the list of servers
- * that need to be traversed to send a request to the peer.
- * - Authentication info - array of authentication credentials to be used
- * by the dialog to authenticate to proxies and servers.
- *
- * \par Manipulating Dialog
- * Application should use functions declared in this file to do something with
- * the dialog. Among other things, application can:
- * - create outgoing dialog (#pjsip_dlg_init()).
- * - sends outgoing invitation (#pjsip_dlg_invite()).
- * - sends response (provisional and final) to incoming invitation
- * (#pjsip_dlg_answer())
- * - disconnect dialog (#pjsip_dlg_disconnect()).
- * - send other request (#pjsip_dlg_create_request() and #pjsip_dlg_send_msg())
- *
- * \par Getting Dialog's Notification
- * Dialog emits notification about various things that's happening to it (e.g.
- * a message is received, dialog state has changed, etc.). Normally it is in
- * the interest of the application to capture these notifications, by
- * supplying the function to be called when the event occurs in #pjsip_dlg_callback
- * structure, and register this structure to user agent by calling
- * #pjsip_ua_set_dialog_callback().
- *
- * \par Incoming Invitation
- * Upon receiving a new incoming invitation, user agent will automatically create
- * a new dialog, and inform application via \b pjsip_dlg_callback.
- */
-
-/** Forward declaration for user agent structure. */
-typedef struct pjsip_user_agent pjsip_user_agent;
-
-/** Forward declaration for dialog structure. */
-typedef struct pjsip_dlg pjsip_dlg;
-
-/**
- * \brief Type of events that are reported by the dialog to the application callback
- * function.
- */
-typedef enum pjsip_dlg_event_e
-{
- /** Dialog state has changed. */
- PJSIP_DIALOG_EVENT_STATE_CHANGED,
-
- /** Any mid-call messages (reinvitation, message, etc.). */
- PJSIP_DIALOG_EVENT_MID_CALL_REQUEST,
-
- /** Other events (low level events). */
- PJSIP_DIALOG_EVENT_OTHER,
-
-} pjsip_dlg_event_e;
-
-
-/**
- * \brief Structure registered by applications to receive dialog notifications
- * from the User Agent.
- *
- * Applications registers this structure to get notifications from the User Agent
- * about dialog state changes and other events. Application can set any of
- * the callback function to NULL if it doesn't want to handle the notification,
- * however, setting some callbacks to NULL probably will cause some undesired
- * result (such as setting \b on_incoming to NULL will cause the creation of
- * a lot of dialogs with no owner).
- */
-struct pjsip_dlg_callback
-{
- /**
- * This is a low level, uninterpreted callback that is called by framework
- * for all kinds of events, such as transaction events, dialog events, etc.
- * @param dlg The dialog.
- * @param dlg_event The type of dialog event.
- * @param event The event descriptor.
- */
- void (*on_all_events)(pjsip_dlg *dlg, pjsip_dlg_event_e dlg_event,
- pjsip_event *event );
-
- /**
- * This is a low level callback that is called by the framework when the
- * underlying transaction is about to send outgoing message. This callback
- * is provided to allow application to modify the message before it is
- * transmitted.
- * @param dlg The dialog.
- * @param tsx The transaction that transmits the message.
- * @param tdata The transmission data, which contains the message.
- * @param retransmission The number of times this message has been sent.
- * Zero indicates the message is about to be sent the first time,
- * one indicates this is the first retransmission, etc.
- */
- void (*on_before_tx)(pjsip_dlg *dlg, pjsip_transaction *tsx,
- pjsip_tx_data *tdata, pj_bool_t retransmission);
-
- /**
- * This is a low level callback that is called by the framework when the dialog
- * has sent a message. Note that a receive of retransmission will not trigger
- * this callback since retransmission is handled internally by transaction.
- * @param dlg The dialog.
- * @param tsx The transaction that transmits the message.
- * @param tdata The transmission data, which contains the message.
- */
- void (*on_tx_msg)(pjsip_dlg *dlg, pjsip_transaction *tsx,
- pjsip_tx_data *tdata);
-
- /**
- * This is a low level callback that is called by the framework when the
- * dialog has received a message. Note that a receipt of retransmission
- * will not trigger this callback since retransmission is handled internally
- * by transaction.
- * @param dlg The dialog.
- * @param tsx The transaction that receives the message.
- * @param rdata The receive data, which contains the message.
- */
- void (*on_rx_msg)(pjsip_dlg *dlg, pjsip_transaction *tsx,
- pjsip_rx_data *rdata);
-
- /**
- * This callback is called by the framework when the user agent
- * instance receives an incoming INVITE message.
- * @param dlg The new dialog that's just created to handle the incoming call.
- * @param tsx The INVITE transaction that's just created.
- * @param rdata The receive data, which contains the INVITE message.
- */
- void (*on_incoming)(pjsip_dlg *dlg, pjsip_transaction *tsx,
- pjsip_rx_data *rdata);
-
- /**
- * This callback is called by the framework when the dialog is sending
- * the first outgoing INVITE message.
- * @param dlg The dialog.
- * @param tsx The INVITE transaction.
- * @param tdata The transmit data, which contains the INVITE message.
- */
- void (*on_calling)(pjsip_dlg *dlg, pjsip_transaction *tsx,
- pjsip_tx_data *tdata);
-
- /**
- * This callback is called by the framework when the initial INVITE
- * transaction has sent/received provisional response.
- * @param dlg The dialog.
- * @param tsx The transaction.
- * @param event The event, which src_type will always be either
- * PJSIP_EVENT_RX_MSG or PJSIP_EVENT_TX_MSG. The provisional
- * response message itself will be in either \b rdata or \b tdata.
- * @see pjsip_event.
- */
- void (*on_provisional)(pjsip_dlg *dlg, pjsip_transaction *tsx,
- pjsip_event *event);
-
- /**
- * This callback is called for both UAS and UAC dialog when 200 response
- * to INVITE is sent or received.
- * @param dlg The dialog.
- * @param event The event, which src_type can only be either
- * PJSIP_EVENT_TX_MSG or PJSIP_EVENT_RX_MSG.
- * @see pjsip_event
- */
- void (*on_connecting)(pjsip_dlg *dlg, pjsip_event *event);
-
- /**
- * This callback is called for both UAS and UAC when an ACK request is
- * sent or received by the dialog.
- * @param dlg The dialog.
- * @param event The event, which src_type can only be either
- * PJSIP_EVENT_TX_MSG or PJSIP_EVENT_RX_MSG.
- * @see pjsip_event
- */
- void (*on_established)(pjsip_dlg *dlg, pjsip_event *event);
-
- /**
- * This callback is called when the dialog is disconnected, i.e. upon
- * sending/receiving non-200 response to INVITE, sending/receiving
- * CANCEL to initial INVITE, and sending/receiving BYE.
- *
- * @param dlg The dialog.
- * @param event The event.
- * @see pjsip_event
- */
- void (*on_disconnected)(pjsip_dlg *dlg, pjsip_event *event);
-
- /**
- * This callback is called when the dialog is about to be destroyed.
- * @param dlg The dialog.
- */
- void (*on_terminated)(pjsip_dlg *dlg);
-
- /**
- * This callback will be called when the dialog receives mid call events
- * such as re-invitation or incoming pager.
- *
- * @param dlg The dialog.
- * @param event The event.
- */
- void (*on_mid_call_events)(pjsip_dlg *dlg, pjsip_event *event);
-
-}; /* struct pjsip_dlg_callback */
-
-
-
-/**
- * Dialog state.
- */
-typedef enum pjsip_dlg_state_e
-{
- /**
- * State NULL is after the dialog is instantiated but before any
- * initialization is done.
- */
- PJSIP_DIALOG_STATE_NULL,
-
- /**
- * State INCOMING is after the (callee) dialog has been initialized with
- * the incoming request, but before any responses is sent by the dialog.
- */
- PJSIP_DIALOG_STATE_INCOMING,
-
- /**
- * State CALLING is after the (caller) dialog has sent outgoing invitation
- * but before any responses are received.
- */
- PJSIP_DIALOG_STATE_CALLING,
-
- /**
- * State PROCEEDING is after the dialog sent/received provisional
- * responses, but before final response is sent/received.
- */
- PJSIP_DIALOG_STATE_PROCEEDING,
-
- /**
- * State CONNECTING is after the dialog has sent/received final response
- * to the invitation, but before acknowledgement is sent.
- */
- PJSIP_DIALOG_STATE_CONNECTING,
-
- /**
- * State ESTABLISHED occurs after the invitation has been accepted and
- * acknowledged.
- */
- PJSIP_DIALOG_STATE_ESTABLISHED,
-
- /**
- * State DISCONNECTED occurs after either party successfully disconnect
- * the session.
- */
- PJSIP_DIALOG_STATE_DISCONNECTED,
-
- /**
- * State TERMINATE occurs when the dialog is ready to be destroyed.
- */
- PJSIP_DIALOG_STATE_TERMINATED,
-
-} pjsip_dlg_state_e;
-
-
-/**
- * Get the dialog string state.
- *
- * @param state Dialog state.
- * @return The string describing the state.
- */
-const char *pjsip_dlg_state_str(pjsip_dlg_state_e state);
-
-/**
- * This structure is used to describe dialog's participants, which in this
- * case is local party (i.e. us) and remote party.
- */
-typedef struct pjsip_dlg_party
-{
- pjsip_uri *target; /**< Target URL. */
- pjsip_fromto_hdr *info; /**< URL in From/To header. */
- pj_str_t tag; /**< Tag. */
- pjsip_contact_hdr *contact; /**< URL in Contact. */
- pj_sockaddr_in addr; /**< The current transport address. */
- int cseq; /**< Sequence number counter. */
-} pjsip_dlg_party;
-
-
-/**
- * This structure describes the dialog structure.
- */
-struct pjsip_dlg
-{
- PJ_DECL_LIST_MEMBER(struct pjsip_dlg)
-
- char obj_name[PJ_MAX_OBJ_NAME]; /**< Log identification. */
-
- pjsip_user_agent *ua; /**< User agent instance. */
- pj_pool_t *pool; /**< Dialog's pool. */
- pjsip_dlg_state_e state; /**< Dialog's call state. */
- pjsip_role_e role; /**< Dialog's role. */
- pj_mutex_t *mutex; /**< Dialog's mutex. */
-
- pjsip_dlg_party local; /**< Local party info. */
- pjsip_dlg_party remote; /**< Remote party info. */
-
- pjsip_cid_hdr *call_id; /**< Call-ID */
- pj_bool_t secure; /**< Use secure transport? */
-
- pjsip_route_hdr route_set; /**< Dialog's route set. */
- pjsip_transaction *invite_tsx; /**< Current INVITE transaction. */
- int pending_tsx_count; /**< Total pending tsx count. */
-
- int cred_count; /**< Number of credentials. */
- pjsip_cred_info *cred_info; /**< Array of credentials. */
-
- pjsip_auth_session auth_sess; /**< List of auth session. */
-
- pjsip_msg_body *body;
-
- void *user_data; /**< Application's data. */
-
- int (*handle_tsx_event)(struct pjsip_dlg *, /**< Internal state handler.*/
- pjsip_transaction *,
- pjsip_event *);
-};
-
-
-/**
- * Initialize dialog with local and remote info. This function is normally
- * called after application creates the dialog with #pjsip_ua_create_dialog
- * for UAC dialogs.
- *
- * This function will initialize local and remote info from the URL, generate
- * a globally unique Call-ID, initialize CSeq, and initialize other dialog's
- * internal attributes.
- *
- * @param dlg The dialog to initialize.
- * @param local_info URI/name address to be used as local info
- * (From and Contact headers).
- * @param remote_info URI/name address to be used as remote info (To header).
- * @param target URI for initial remote's target, or NULL to set the
- * initial target the same as remote_info.
- *
- * @return zero on success.
- */
-PJ_DECL(pj_status_t) pjsip_dlg_init( pjsip_dlg *dlg,
- const pj_str_t *local_info,
- const pj_str_t *remote_info,
- const pj_str_t *target);
-
-
-/**
- * Set authentication credentials to be used by this dialog.
- *
- * If authentication credentials are set for the dialog, the dialog will try to
- * perform authentication automatically using the credentials supplied, and
- * also cache the last Authorization or Proxy-Authorization headers for next
- * requests.
- *
- * If none of the credentials are suitable or accepted by remote, then
- * the dialog will just pass the authorization failure response back to
- * application.
- *
- * @param dlg The dialog.
- * @param count Number of credentials in the array.
- * @param cred Array of credentials.
- *
- * @return Zero on success.
- */
-PJ_DECL(pj_status_t) pjsip_dlg_set_credentials( pjsip_dlg *dlg,
- int count,
- const pjsip_cred_info cred[]);
-
-/**
- * Override local contact details.
- *
- * Call this function to change the contact details to be advertised in Contact
- * header. Application normally need to call this function for incoming calls
- * before answering the call with 200/OK, because for an incoming dialogs, the
- * initial local contact info are generated from the To header, which is
- * normally not the appropriate one.
- *
- * @param dlg The dialog.
- * @param contact The contact to use.
- *
- * @return Zero on success.
- */
-PJ_DECL(pj_status_t) pjsip_dlg_set_contact( pjsip_dlg *dlg,
- const pj_str_t *contact );
-
-
-/**
- * Set initial route set to be used by the dialog. This initial route set
- * governs where and how the initial INVITE request will be routed. This
- * initial route set will be overwritten with the route set found in the
- * 2xx response of INVITE.
- *
- * Application only needs to call this function if it wants to have custom
- * route for individual dialogs. If only a single route for all dialogs is
- * needed, then application can set the global route by calling function
- * #pjsip_endpt_set_proxies().
- *
- * @param dlg The dialog.
- * @param route_set The route set list.
- *
- * @return Zero on success.
- */
-PJ_DECL(pj_status_t) pjsip_dlg_set_route_set( pjsip_dlg *dlg,
- const pjsip_route_hdr *route_set );
-
-
-/**
- * Variation of #pjsip_dlg_set_route_set where the headers will be used
- * as it is (i.e. without cloned).
- *
- * @param dlg The dialog.
- * @param route_set The route set list.
- *
- * @return Zero on success.
- */
-PJ_DECL(pj_status_t) pjsip_dlg_set_route_set_np( pjsip_dlg *dlg,
- pjsip_route_hdr *route_set);
-
-/**
- * Create initial outgoing INVITE message.
- *
- * This function is just a simple wrapper to #pjsip_dlg_create_request(),
- * so it follows the same rule there. In addition, this function also adds
- * \b Allow header to the outgoing request.
- *
- * After the message is successfully created, application must call
- * #pjsip_dlg_send_msg() to actually send the message and update the dialog's
- * state. Note that upon return the reference counter of the transmit data
- * will be set to one.
- *
- * @param dlg The dialog.
- *
- * @return The dialog transmit data, or NULL.
- */
-PJ_DECL(pjsip_tx_data*) pjsip_dlg_invite( pjsip_dlg *dlg );
-
-
-/**
- * Answer incoming dialog invitation, with either provisional responses
- * or a final response. Application can only call this function when there's
- * a pending invitation to be answered.
- *
- * After the message is successfully created, application must call
- * #pjsip_dlg_send_msg() to actually send the message and update the dialog's
- * state. Note that upon return the reference counter of the transmit data
- * will be set to one.
- *
- * @param dlg The dialog.
- * @param code The response code, which can be:
- * - 100-199 Provisional response (application can issue multiple
- * provisional responses).
- * - 200-299 To answer the invitation (normally status code 200
- * is sent).
- * - 300-699 To reject the invitation.
- * @return Transmit data if successfull.
- */
-PJ_DECL(pjsip_tx_data*) pjsip_dlg_answer( pjsip_dlg *dlg, int code );
-
-
-/**
- * High level function to create message to disconnect dialog. Depending
- * on dialog's state, this function will either create CANCEL, final response,
- * or BYE message. A status code must be supplied, which will be set if dialog
- * will be transmitting a final response to INVITE.
- *
- * After the message is successfully created, application must call
- * #pjsip_dlg_send_msg to actually send the message and update the dialog's
- * state. Note that upon return the reference counter of the transmit data
- * will be set to one.
- *
- * @param dlg The dialog.
- * @param status_code The status code for disconnection.
- * @return Transmit data if successfull.
- */
-PJ_DECL(pjsip_tx_data*) pjsip_dlg_disconnect( pjsip_dlg *dlg, int status_code);
-
-/**
- * Create CANCEL message to cancel pending outgoing dialog invitation.
- * Normally application should call #pjsip_dlg_disconnect() instead, because
- * that function will create the correct message regardless of the state of
- * the dialog.
- *
- * Application can call this function at anytime after it issues outgoing
- * invitation and before receiving final response. However, there's no
- * guarantee that the invitation will be successfully cancelled, since the
- * CANCEL request and the final response can pass over in the wire. So the
- * application must prepare to have the dialog connected even after the
- * dialog is cancelled.
- *
- * The final state of the dialog will be reported in the dialog callback.
- * If the CANCEL request succeeded, then the dialog will be disconnected with
- * status code \a PJSIP_SC_REQUEST_TERMINATED.
- *
- * After the message is successfully created, application must call
- * #pjsip_dlg_send_msg() to actually send the message and update the dialog's
- * state.
- *
- * Upon return of this function, the reference counter of the transmit data
- * will be set to one.
- *
- * @param dlg The dialog.
- * @return The dialog transmit data containing the CANCEL message,
- * or NULL.
- */
-PJ_DECL(pjsip_tx_data*) pjsip_dlg_cancel( pjsip_dlg *dlg );
-
-
-/**
- * Create BYE message. Application shouldn't normally need to use this function,
- * but rather it's preferable to use #pjsip_dlg_disconnect() instead because
- * that function will work to disconnect the session no matter what the state
- * is.
- *
- * After the message is successfully created, application must call
- * #pjsip_dlg_send_msg() to actually send the message and update the dialog's
- * state. Note that upon return the reference counter of the transmit data
- * will be set to one.
- *
- * @param dlg The dialog.
- * @return The BYE message or NULL.
- */
-PJ_DECL(pjsip_tx_data*) pjsip_dlg_bye( pjsip_dlg *dlg );
-
-/**
- * This function is called by application to create new outgoing request
- * message for this dialog. After the request is created, application can
- * modify the message (such adding headers), and eventually send the request
- * by calling #pjsip_dlg_send_msg().
- *
- * This function will initialize the request message with dialog's properties
- * as follows:
- * - the request line is initialized with the method and the target is
- * initialized from current remote target.
- * - \b From, \b To, \b Contact, and \b Call-Id headers will be added.
- * - An initial \b CSeq header will be provided (although the value will be
- * verified again when the message is actually sent with #pjsip_dlg_send_msg().
- * - \b Route headers will be added from dialog's route set.
- * - Authentication headers (\b Authorization or \b Proxy-Authorization) will
- * be added from dialog's authorization cache.
- *
- * Note that upon return the reference counter of the transmit data
- * will be set to one. When the message is sent, #pjsip_dlg_send_msg() will
- * decrement the reference counter, and when the reference counter reach zero,
- * the message will be deleted.
- *
- * @param dlg The dialog.
- * @param method The request method.
- * @param cseq Specify CSeq, or -1 to let the dialog specify CSeq.
- *
- * @return Transmit data for the new request.
- *
- * @see pjsip_dlg_send_msg()
- */
-PJ_DECL(pjsip_tx_data*) pjsip_dlg_create_request( pjsip_dlg *dlg,
- const pjsip_method *method,
- int cseq);
-
-
-/**
- * This function can be called by application to send outgoing message (request
- * or response) to remote party. Note that after calling this function, the
- * transmit data will be deleted regardless of the return status. To prevent
- * deletion, application must increase the reference count, but then it will
- * be responsible to delete this transmit data itself (by decreasing the
- * reference count).
- *
- * @param dlg The dialog.
- * @param tdata The transmit data, which contains the request message.
- * @return zero on success.
- */
-PJ_DECL(pj_status_t) pjsip_dlg_send_msg( pjsip_dlg *dlg,
- pjsip_tx_data *tdata );
-
-
-/**
- * @}
- */
-
-PJ_END_DECL
-
-#endif /* __PJSIP_DIALOG_H__ */
-
+/* $Id$ */
+/*
+ * Copyright (C) 2003-2006 Benny Prijono <benny@prijono.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#ifndef __PJSIP_DIALOG_H__
+#define __PJSIP_DIALOG_H__
+
+/**
+ * @file dialog.h
+ * @brief SIP Dialog abstraction
+ */
+
+#include <pjsip/sip_msg.h>
+#include <pjsip/sip_auth.h>
+#include <pj/sock.h>
+
+PJ_BEGIN_DECL
+
+/**
+ * @defgroup PJSUA_DIALOG SIP Dialog
+ * @ingroup PJSUA
+ * @{
+ * \brief
+ * This file contains SIP dialog, a higher level abstraction of SIP session.
+ *
+ * \par Overview
+ * A SIP dialog is an abstraction of communication session between two user
+ * agents that persist for some time. The dialog facilitates sequencing of
+ * messages between the user agents and proper routing of requests between both
+ * of them. The dialog represents a context in which to interpret SIP messages.
+ * However method independent User Agent processing for requests and responses
+ * outside of a dialog exists, hence a dialog is not necessary for message
+ * processing.
+ *
+ * A dialog is identified at each User Agent with a dialog Id, which consists
+ * of a Call-Id value, a local tag and a remote tag.
+ *
+ * A dialog contains certain pieces of data needed for further message
+ * transmissions within the dialog. This data consists of:
+ * - Dialog Id - used to identify the dialog.
+ * - Local sequence number - used to order requests from the UA to its peer.
+ * - Remote sequence number - used to order requests from its peer to the UA.
+ * - Local URI - the address of the local party.
+ * - Remote URI - the address of the remote party.
+ * - Remote target - the address from the Contact header field of the request
+ * or response or refresh request or response.
+ * - "secure" boolean - determines if the dialog is secure.
+ * - Route set - an ordered list of URIs. The route set is the list of servers
+ * that need to be traversed to send a request to the peer.
+ * - Authentication info - array of authentication credentials to be used
+ * by the dialog to authenticate to proxies and servers.
+ *
+ * \par Manipulating Dialog
+ * Application should use functions declared in this file to do something with
+ * the dialog. Among other things, application can:
+ * - create outgoing dialog (#pjsip_dlg_init()).
+ * - sends outgoing invitation (#pjsip_dlg_invite()).
+ * - sends response (provisional and final) to incoming invitation
+ * (#pjsip_dlg_answer())
+ * - disconnect dialog (#pjsip_dlg_disconnect()).
+ * - send other request (#pjsip_dlg_create_request() and #pjsip_dlg_send_msg())
+ *
+ * \par Getting Dialog's Notification
+ * Dialog emits notification about various things that's happening to it (e.g.
+ * a message is received, dialog state has changed, etc.). Normally it is in
+ * the interest of the application to capture these notifications, by
+ * supplying the function to be called when the event occurs in #pjsip_dlg_callback
+ * structure, and register this structure to user agent by calling
+ * #pjsip_ua_set_dialog_callback().
+ *
+ * \par Incoming Invitation
+ * Upon receiving a new incoming invitation, user agent will automatically create
+ * a new dialog, and inform application via \b pjsip_dlg_callback.
+ */
+
+/** Forward declaration for user agent structure. */
+typedef struct pjsip_user_agent pjsip_user_agent;
+
+/** Forward declaration for dialog structure. */
+typedef struct pjsip_dlg pjsip_dlg;
+
+/**
+ * \brief Type of events that are reported by the dialog to the application callback
+ * function.
+ */
+typedef enum pjsip_dlg_event_e
+{
+ /** Dialog state has changed. */
+ PJSIP_DIALOG_EVENT_STATE_CHANGED,
+
+ /** Any mid-call messages (reinvitation, message, etc.). */
+ PJSIP_DIALOG_EVENT_MID_CALL_REQUEST,
+
+ /** Other events (low level events). */
+ PJSIP_DIALOG_EVENT_OTHER,
+
+} pjsip_dlg_event_e;
+
+
+/**
+ * \brief Structure registered by applications to receive dialog notifications
+ * from the User Agent.
+ *
+ * Applications registers this structure to get notifications from the User Agent
+ * about dialog state changes and other events. Application can set any of
+ * the callback function to NULL if it doesn't want to handle the notification,
+ * however, setting some callbacks to NULL probably will cause some undesired
+ * result (such as setting \b on_incoming to NULL will cause the creation of
+ * a lot of dialogs with no owner).
+ */
+struct pjsip_dlg_callback
+{
+ /**
+ * This is a low level, uninterpreted callback that is called by framework
+ * for all kinds of events, such as transaction events, dialog events, etc.
+ * @param dlg The dialog.
+ * @param dlg_event The type of dialog event.
+ * @param event The event descriptor.
+ */
+ void (*on_all_events)(pjsip_dlg *dlg, pjsip_dlg_event_e dlg_event,
+ pjsip_event *event );
+
+ /**
+ * This is a low level callback that is called by the framework when the
+ * underlying transaction is about to send outgoing message. This callback
+ * is provided to allow application to modify the message before it is
+ * transmitted.
+ * @param dlg The dialog.
+ * @param tsx The transaction that transmits the message.
+ * @param tdata The transmission data, which contains the message.
+ * @param retransmission The number of times this message has been sent.
+ * Zero indicates the message is about to be sent the first time,
+ * one indicates this is the first retransmission, etc.
+ */
+ void (*on_before_tx)(pjsip_dlg *dlg, pjsip_transaction *tsx,
+ pjsip_tx_data *tdata, pj_bool_t retransmission);
+
+ /**
+ * This is a low level callback that is called by the framework when the dialog
+ * has sent a message. Note that a receive of retransmission will not trigger
+ * this callback since retransmission is handled internally by transaction.
+ * @param dlg The dialog.
+ * @param tsx The transaction that transmits the message.
+ * @param tdata The transmission data, which contains the message.
+ */
+ void (*on_tx_msg)(pjsip_dlg *dlg, pjsip_transaction *tsx,
+ pjsip_tx_data *tdata);
+
+ /**
+ * This is a low level callback that is called by the framework when the
+ * dialog has received a message. Note that a receipt of retransmission
+ * will not trigger this callback since retransmission is handled internally
+ * by transaction.
+ * @param dlg The dialog.
+ * @param tsx The transaction that receives the message.
+ * @param rdata The receive data, which contains the message.
+ */
+ void (*on_rx_msg)(pjsip_dlg *dlg, pjsip_transaction *tsx,
+ pjsip_rx_data *rdata);
+
+ /**
+ * This callback is called by the framework when the user agent
+ * instance receives an incoming INVITE message.
+ * @param dlg The new dialog that's just created to handle the incoming call.
+ * @param tsx The INVITE transaction that's just created.
+ * @param rdata The receive data, which contains the INVITE message.
+ */
+ void (*on_incoming)(pjsip_dlg *dlg, pjsip_transaction *tsx,
+ pjsip_rx_data *rdata);
+
+ /**
+ * This callback is called by the framework when the dialog is sending
+ * the first outgoing INVITE message.
+ * @param dlg The dialog.
+ * @param tsx The INVITE transaction.
+ * @param tdata The transmit data, which contains the INVITE message.
+ */
+ void (*on_calling)(pjsip_dlg *dlg, pjsip_transaction *tsx,
+ pjsip_tx_data *tdata);
+
+ /**
+ * This callback is called by the framework when the initial INVITE
+ * transaction has sent/received provisional response.
+ * @param dlg The dialog.
+ * @param tsx The transaction.
+ * @param event The event, which src_type will always be either
+ * PJSIP_EVENT_RX_MSG or PJSIP_EVENT_TX_MSG. The provisional
+ * response message itself will be in either \b rdata or \b tdata.
+ * @see pjsip_event.
+ */
+ void (*on_provisional)(pjsip_dlg *dlg, pjsip_transaction *tsx,
+ pjsip_event *event);
+
+ /**
+ * This callback is called for both UAS and UAC dialog when 200 response
+ * to INVITE is sent or received.
+ * @param dlg The dialog.
+ * @param event The event, which src_type can only be either
+ * PJSIP_EVENT_TX_MSG or PJSIP_EVENT_RX_MSG.
+ * @see pjsip_event
+ */
+ void (*on_connecting)(pjsip_dlg *dlg, pjsip_event *event);
+
+ /**
+ * This callback is called for both UAS and UAC when an ACK request is
+ * sent or received by the dialog.
+ * @param dlg The dialog.
+ * @param event The event, which src_type can only be either
+ * PJSIP_EVENT_TX_MSG or PJSIP_EVENT_RX_MSG.
+ * @see pjsip_event
+ */
+ void (*on_established)(pjsip_dlg *dlg, pjsip_event *event);
+
+ /**
+ * This callback is called when the dialog is disconnected, i.e. upon
+ * sending/receiving non-200 response to INVITE, sending/receiving
+ * CANCEL to initial INVITE, and sending/receiving BYE.
+ *
+ * @param dlg The dialog.
+ * @param event The event.
+ * @see pjsip_event
+ */
+ void (*on_disconnected)(pjsip_dlg *dlg, pjsip_event *event);
+
+ /**
+ * This callback is called when the dialog is about to be destroyed.
+ * @param dlg The dialog.
+ */
+ void (*on_terminated)(pjsip_dlg *dlg);
+
+ /**
+ * This callback will be called when the dialog receives mid call events
+ * such as re-invitation or incoming pager.
+ *
+ * @param dlg The dialog.
+ * @param event The event.
+ */
+ void (*on_mid_call_events)(pjsip_dlg *dlg, pjsip_event *event);
+
+}; /* struct pjsip_dlg_callback */
+
+
+
+/**
+ * Dialog state.
+ */
+typedef enum pjsip_dlg_state_e
+{
+ /**
+ * State NULL is after the dialog is instantiated but before any
+ * initialization is done.
+ */
+ PJSIP_DIALOG_STATE_NULL,
+
+ /**
+ * State INCOMING is after the (callee) dialog has been initialized with
+ * the incoming request, but before any responses is sent by the dialog.
+ */
+ PJSIP_DIALOG_STATE_INCOMING,
+
+ /**
+ * State CALLING is after the (caller) dialog has sent outgoing invitation
+ * but before any responses are received.
+ */
+ PJSIP_DIALOG_STATE_CALLING,
+
+ /**
+ * State PROCEEDING is after the dialog sent/received provisional
+ * responses, but before final response is sent/received.
+ */
+ PJSIP_DIALOG_STATE_PROCEEDING,
+
+ /**
+ * State CONNECTING is after the dialog has sent/received final response
+ * to the invitation, but before acknowledgement is sent.
+ */
+ PJSIP_DIALOG_STATE_CONNECTING,
+
+ /**
+ * State ESTABLISHED occurs after the invitation has been accepted and
+ * acknowledged.
+ */
+ PJSIP_DIALOG_STATE_ESTABLISHED,
+
+ /**
+ * State DISCONNECTED occurs after either party successfully disconnect
+ * the session.
+ */
+ PJSIP_DIALOG_STATE_DISCONNECTED,
+
+ /**
+ * State TERMINATE occurs when the dialog is ready to be destroyed.
+ */
+ PJSIP_DIALOG_STATE_TERMINATED,
+
+} pjsip_dlg_state_e;
+
+
+/**
+ * Get the dialog string state.
+ *
+ * @param state Dialog state.
+ * @return The string describing the state.
+ */
+const char *pjsip_dlg_state_str(pjsip_dlg_state_e state);
+
+/**
+ * This structure is used to describe dialog's participants, which in this
+ * case is local party (i.e. us) and remote party.
+ */
+typedef struct pjsip_dlg_party
+{
+ pjsip_uri *target; /**< Target URL. */
+ pjsip_fromto_hdr *info; /**< URL in From/To header. */
+ pj_str_t tag; /**< Tag. */
+ pjsip_contact_hdr *contact; /**< URL in Contact. */
+ pj_sockaddr_in addr; /**< The current transport address. */
+ int cseq; /**< Sequence number counter. */
+} pjsip_dlg_party;
+
+
+/**
+ * This structure describes the dialog structure.
+ */
+struct pjsip_dlg
+{
+ PJ_DECL_LIST_MEMBER(struct pjsip_dlg)
+
+ char obj_name[PJ_MAX_OBJ_NAME]; /**< Log identification. */
+
+ pjsip_user_agent *ua; /**< User agent instance. */
+ pj_pool_t *pool; /**< Dialog's pool. */
+ pjsip_dlg_state_e state; /**< Dialog's call state. */
+ pjsip_role_e role; /**< Dialog's role. */
+ pj_mutex_t *mutex; /**< Dialog's mutex. */
+
+ pjsip_dlg_party local; /**< Local party info. */
+ pjsip_dlg_party remote; /**< Remote party info. */
+
+ pjsip_cid_hdr *call_id; /**< Call-ID */
+ pj_bool_t secure; /**< Use secure transport? */
+
+ pjsip_route_hdr route_set; /**< Dialog's route set. */
+ pjsip_transaction *invite_tsx; /**< Current INVITE transaction. */
+ int pending_tsx_count; /**< Total pending tsx count. */
+
+ int cred_count; /**< Number of credentials. */
+ pjsip_cred_info *cred_info; /**< Array of credentials. */
+
+ pjsip_auth_session auth_sess; /**< List of auth session. */
+
+ pjsip_msg_body *body;
+
+ void *user_data; /**< Application's data. */
+
+ int (*handle_tsx_event)(struct pjsip_dlg *, /**< Internal state handler.*/
+ pjsip_transaction *,
+ pjsip_event *);
+};
+
+
+/**
+ * Initialize dialog with local and remote info. This function is normally
+ * called after application creates the dialog with #pjsip_ua_create_dialog
+ * for UAC dialogs.
+ *
+ * This function will initialize local and remote info from the URL, generate
+ * a globally unique Call-ID, initialize CSeq, and initialize other dialog's
+ * internal attributes.
+ *
+ * @param dlg The dialog to initialize.
+ * @param local_info URI/name address to be used as local info
+ * (From and Contact headers).
+ * @param remote_info URI/name address to be used as remote info (To header).
+ * @param target URI for initial remote's target, or NULL to set the
+ * initial target the same as remote_info.
+ *
+ * @return zero on success.
+ */
+PJ_DECL(pj_status_t) pjsip_dlg_init( pjsip_dlg *dlg,
+ const pj_str_t *local_info,
+ const pj_str_t *remote_info,
+ const pj_str_t *target);
+
+
+/**
+ * Set authentication credentials to be used by this dialog.
+ *
+ * If authentication credentials are set for the dialog, the dialog will try to
+ * perform authentication automatically using the credentials supplied, and
+ * also cache the last Authorization or Proxy-Authorization headers for next
+ * requests.
+ *
+ * If none of the credentials are suitable or accepted by remote, then
+ * the dialog will just pass the authorization failure response back to
+ * application.
+ *
+ * @param dlg The dialog.
+ * @param count Number of credentials in the array.
+ * @param cred Array of credentials.
+ *
+ * @return Zero on success.
+ */
+PJ_DECL(pj_status_t) pjsip_dlg_set_credentials( pjsip_dlg *dlg,
+ int count,
+ const pjsip_cred_info cred[]);
+
+/**
+ * Override local contact details.
+ *
+ * Call this function to change the contact details to be advertised in Contact
+ * header. Application normally need to call this function for incoming calls
+ * before answering the call with 200/OK, because for an incoming dialogs, the
+ * initial local contact info are generated from the To header, which is
+ * normally not the appropriate one.
+ *
+ * @param dlg The dialog.
+ * @param contact The contact to use.
+ *
+ * @return Zero on success.
+ */
+PJ_DECL(pj_status_t) pjsip_dlg_set_contact( pjsip_dlg *dlg,
+ const pj_str_t *contact );
+
+
+/**
+ * Set initial route set to be used by the dialog. This initial route set
+ * governs where and how the initial INVITE request will be routed. This
+ * initial route set will be overwritten with the route set found in the
+ * 2xx response of INVITE.
+ *
+ * Application only needs to call this function if it wants to have custom
+ * route for individual dialogs. If only a single route for all dialogs is
+ * needed, then application can set the global route by calling function
+ * #pjsip_endpt_set_proxies().
+ *
+ * @param dlg The dialog.
+ * @param route_set The route set list.
+ *
+ * @return Zero on success.
+ */
+PJ_DECL(pj_status_t) pjsip_dlg_set_route_set( pjsip_dlg *dlg,
+ const pjsip_route_hdr *route_set );
+
+
+/**
+ * Variation of #pjsip_dlg_set_route_set where the headers will be used
+ * as it is (i.e. without cloned).
+ *
+ * @param dlg The dialog.
+ * @param route_set The route set list.
+ *
+ * @return Zero on success.
+ */
+PJ_DECL(pj_status_t) pjsip_dlg_set_route_set_np( pjsip_dlg *dlg,
+ pjsip_route_hdr *route_set);
+
+/**
+ * Create initial outgoing INVITE message.
+ *
+ * This function is just a simple wrapper to #pjsip_dlg_create_request(),
+ * so it follows the same rule there. In addition, this function also adds
+ * \b Allow header to the outgoing request.
+ *
+ * After the message is successfully created, application must call
+ * #pjsip_dlg_send_msg() to actually send the message and update the dialog's
+ * state. Note that upon return the reference counter of the transmit data
+ * will be set to one.
+ *
+ * @param dlg The dialog.
+ *
+ * @return The dialog transmit data, or NULL.
+ */
+PJ_DECL(pjsip_tx_data*) pjsip_dlg_invite( pjsip_dlg *dlg );
+
+
+/**
+ * Answer incoming dialog invitation, with either provisional responses
+ * or a final response. Application can only call this function when there's
+ * a pending invitation to be answered.
+ *
+ * After the message is successfully created, application must call
+ * #pjsip_dlg_send_msg() to actually send the message and update the dialog's
+ * state. Note that upon return the reference counter of the transmit data
+ * will be set to one.
+ *
+ * @param dlg The dialog.
+ * @param code The response code, which can be:
+ * - 100-199 Provisional response (application can issue multiple
+ * provisional responses).
+ * - 200-299 To answer the invitation (normally status code 200
+ * is sent).
+ * - 300-699 To reject the invitation.
+ * @return Transmit data if successfull.
+ */
+PJ_DECL(pjsip_tx_data*) pjsip_dlg_answer( pjsip_dlg *dlg, int code );
+
+
+/**
+ * High level function to create message to disconnect dialog. Depending
+ * on dialog's state, this function will either create CANCEL, final response,
+ * or BYE message. A status code must be supplied, which will be set if dialog
+ * will be transmitting a final response to INVITE.
+ *
+ * After the message is successfully created, application must call
+ * #pjsip_dlg_send_msg to actually send the message and update the dialog's
+ * state. Note that upon return the reference counter of the transmit data
+ * will be set to one.
+ *
+ * @param dlg The dialog.
+ * @param status_code The status code for disconnection.
+ * @return Transmit data if successfull.
+ */
+PJ_DECL(pjsip_tx_data*) pjsip_dlg_disconnect( pjsip_dlg *dlg, int status_code);
+
+/**
+ * Create CANCEL message to cancel pending outgoing dialog invitation.
+ * Normally application should call #pjsip_dlg_disconnect() instead, because
+ * that function will create the correct message regardless of the state of
+ * the dialog.
+ *
+ * Application can call this function at anytime after it issues outgoing
+ * invitation and before receiving final response. However, there's no
+ * guarantee that the invitation will be successfully cancelled, since the
+ * CANCEL request and the final response can pass over in the wire. So the
+ * application must prepare to have the dialog connected even after the
+ * dialog is cancelled.
+ *
+ * The final state of the dialog will be reported in the dialog callback.
+ * If the CANCEL request succeeded, then the dialog will be disconnected with
+ * status code \a PJSIP_SC_REQUEST_TERMINATED.
+ *
+ * After the message is successfully created, application must call
+ * #pjsip_dlg_send_msg() to actually send the message and update the dialog's
+ * state.
+ *
+ * Upon return of this function, the reference counter of the transmit data
+ * will be set to one.
+ *
+ * @param dlg The dialog.
+ * @return The dialog transmit data containing the CANCEL message,
+ * or NULL.
+ */
+PJ_DECL(pjsip_tx_data*) pjsip_dlg_cancel( pjsip_dlg *dlg );
+
+
+/**
+ * Create BYE message. Application shouldn't normally need to use this function,
+ * but rather it's preferable to use #pjsip_dlg_disconnect() instead because
+ * that function will work to disconnect the session no matter what the state
+ * is.
+ *
+ * After the message is successfully created, application must call
+ * #pjsip_dlg_send_msg() to actually send the message and update the dialog's
+ * state. Note that upon return the reference counter of the transmit data
+ * will be set to one.
+ *
+ * @param dlg The dialog.
+ * @return The BYE message or NULL.
+ */
+PJ_DECL(pjsip_tx_data*) pjsip_dlg_bye( pjsip_dlg *dlg );
+
+/**
+ * This function is called by application to create new outgoing request
+ * message for this dialog. After the request is created, application can
+ * modify the message (such adding headers), and eventually send the request
+ * by calling #pjsip_dlg_send_msg().
+ *
+ * This function will initialize the request message with dialog's properties
+ * as follows:
+ * - the request line is initialized with the method and the target is
+ * initialized from current remote target.
+ * - \b From, \b To, \b Contact, and \b Call-Id headers will be added.
+ * - An initial \b CSeq header will be provided (although the value will be
+ * verified again when the message is actually sent with #pjsip_dlg_send_msg().
+ * - \b Route headers will be added from dialog's route set.
+ * - Authentication headers (\b Authorization or \b Proxy-Authorization) will
+ * be added from dialog's authorization cache.
+ *
+ * Note that upon return the reference counter of the transmit data
+ * will be set to one. When the message is sent, #pjsip_dlg_send_msg() will
+ * decrement the reference counter, and when the reference counter reach zero,
+ * the message will be deleted.
+ *
+ * @param dlg The dialog.
+ * @param method The request method.
+ * @param cseq Specify CSeq, or -1 to let the dialog specify CSeq.
+ *
+ * @return Transmit data for the new request.
+ *
+ * @see pjsip_dlg_send_msg()
+ */
+PJ_DECL(pjsip_tx_data*) pjsip_dlg_create_request( pjsip_dlg *dlg,
+ const pjsip_method *method,
+ int cseq);
+
+
+/**
+ * This function can be called by application to send outgoing message (request
+ * or response) to remote party. Note that after calling this function, the
+ * transmit data will be deleted regardless of the return status. To prevent
+ * deletion, application must increase the reference count, but then it will
+ * be responsible to delete this transmit data itself (by decreasing the
+ * reference count).
+ *
+ * @param dlg The dialog.
+ * @param tdata The transmit data, which contains the request message.
+ * @return zero on success.
+ */
+PJ_DECL(pj_status_t) pjsip_dlg_send_msg( pjsip_dlg *dlg,
+ pjsip_tx_data *tdata );
+
+
+/**
+ * @}
+ */
+
+PJ_END_DECL
+
+#endif /* __PJSIP_DIALOG_H__ */
+
diff --git a/pjsip/include/pjsip-ua/sip_regc.h b/pjsip/include/pjsip-ua/sip_regc.h
index b9752dc1..5a4e2ebe 100644
--- a/pjsip/include/pjsip-ua/sip_regc.h
+++ b/pjsip/include/pjsip-ua/sip_regc.h
@@ -1,210 +1,210 @@
-/* $Id$ */
-/*
- * Copyright (C) 2003-2006 Benny Prijono <benny@prijono.org>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
-#ifndef __PJSIP_SIP_REG_H__
-#define __PJSIP_SIP_REG_H__
-
-/**
- * @file sip_reg.h
- * @brief SIP Registration Client
- */
-
-#include <pjsip/sip_types.h>
-#include <pjsip/sip_auth.h>
-#include <pjsip_mod_ua/sip_ua.h>
-
-PJ_BEGIN_DECL
-
-/**
- * @defgroup PJSUA_REGC SIP Registration Client
- * @ingroup PJSUA
- * @{
- * \brief
- * API for performing registration for user agent.
- */
-
-/** Typedef for client registration data. */
-typedef struct pjsip_regc pjsip_regc;
-
-/** Maximum contacts in registration. */
-#define PJSIP_REGC_MAX_CONTACT 10
-
-/** Expiration not specified. */
-#define PJSIP_REGC_EXPIRATION_NOT_SPECIFIED ((pj_uint32_t)0xFFFFFFFFUL)
-
-/** Buffer to hold all contacts. */
-#define PJSIP_REGC_CONTACT_BUF_SIZE 512
-
-/** Structure to hold parameters when calling application's callback.
- * The application's callback is called when the client registration process
- * has finished.
- */
-struct pjsip_regc_cbparam
-{
- pjsip_regc *regc;
- void *token;
- int code;
- pj_str_t reason;
- pjsip_rx_data *rdata;
- int contact_cnt;
- int expiration;
- pjsip_contact_hdr *contact[PJSIP_REGC_MAX_CONTACT];
-};
-
-
-/** Type declaration for callback to receive registration result. */
-typedef void pjsip_regc_cb(struct pjsip_regc_cbparam *param);
-
-
-/**
- * Get the module instance for client registration module.
- *
- * @return client registration module.
- */
-PJ_DECL(pjsip_module*) pjsip_regc_get_module(void);
-
-
-/**
- * Create client registration structure.
- *
- * @param endpt Endpoint, used to allocate pool from.
- * @param token A data to be associated with the client registration struct.
- * @param cb Pointer to callback function to receive registration status.
- *
- * @return client registration structure.
- */
-PJ_DECL(pjsip_regc*) pjsip_regc_create( pjsip_endpoint *endpt, void *token,
- pjsip_regc_cb *cb);
-
-
-/**
- * Destroy client registration structure. If a registration transaction is
- * in progress, then the structure will be deleted only after a final response
- * has been received, and in this case, the callback won't be called.
- *
- * @param regc The client registration structure.
- */
-PJ_DECL(void) pjsip_regc_destroy(pjsip_regc *regc);
-
-/**
- * Get the memory pool associated with a registration client handle.
- *
- * @param regc The client registration structure.
- * @return pool handle.
- */
-PJ_DECL(pj_pool_t*) pjsip_regc_get_pool(pjsip_regc *regc);
-
-/**
- * Initialize client registration structure with various information needed to
- * perform the registration.
- *
- * @param regc The client registration structure.
- * @param from_url The person performing the registration, must be a SIP URL type.
- * @param to_url The address of record for which the registration is targetd, must
- * be a SIP/SIPS URL.
- * @param ccnt Number of contacts in the array.
- * @param contact Array of contacts.
- * @param expires Default expiration interval (in seconds) to be applied for
- * contact URL that doesn't have expiration settings. If the
- * value PJSIP_REGC_EXPIRATION_NOT_SPECIFIED is given, then
- * no default expiration will be applied.
- * @return zero on success.
- */
-PJ_DECL(pj_status_t) pjsip_regc_init(pjsip_regc *regc,
- const pj_str_t *srv_url,
- const pj_str_t *from_url,
- const pj_str_t *to_url,
- int ccnt,
- const pj_str_t contact[],
- pj_uint32_t expires);
-
-
-/**
- * Set authentication credentials to use by this registration.
- *
- * @param dlg The registration structure.
- * @param count Number of credentials in the array.
- * @param cred Array of credentials.
- *
- * @return Zero on success.
- */
-PJ_DECL(pj_status_t) pjsip_regc_set_credentials( pjsip_regc *regc,
- int count,
- const pjsip_cred_info cred[] );
-
-/**
- * Create REGISTER request for the specified client registration structure.
- *
- * After successfull registration, application can inspect the contacts in
- * the client registration structure to list what contacts are associaciated
- * with the address of record being targeted in the registration.
- *
- * @param regc The client registration structure.
- * @param autoreg If non zero, the library will automatically refresh the
- * next registration until application unregister.
- *
- * @return SIP REGISTER request.
- */
-PJ_DECL(pjsip_tx_data*) pjsip_regc_register(pjsip_regc *regc, pj_bool_t autoreg);
-
-
-/**
- * Create REGISTER request to unregister all contacts from server records.
- *
- * @param regc The client registration structure.
- *
- * @return SIP REGISTER request.
- */
-PJ_DECL(pjsip_tx_data*) pjsip_regc_unregister(pjsip_regc *regc);
-
-/**
- * Update Contact details in the client registration structure.
- *
- * @param regc The client registration structure.
- * @param ccnt Number of contacts.
- * @param contact Array of contacts.
- * @return zero if sucessfull.
- */
-PJ_DECL(pj_status_t) pjsip_regc_update_contact( pjsip_regc *regc,
- int ccnt,
- const pj_str_t contact[] );
-
-/**
- * Update the expires value.
- *
- * @param regc The client registration structure.
- * @param expires The new expires value.
- * @return zero on successfull.
- */
-PJ_DECL(pj_status_t) pjsip_regc_update_expires( pjsip_regc *regc,
- pj_uint32_t expires );
-
-/**
- * Sends outgoing REGISTER request.
- * The process will complete asynchronously, and application
- * will be notified via the callback when the process completes.
- *
- * @param regc The client registration structure.
- * @param tdata Transmit data.
- */
-PJ_DECL(void) pjsip_regc_send(pjsip_regc *regc, pjsip_tx_data *tdata);
-
-
-PJ_END_DECL
-
-#endif /* __PJSIP_REG_H__ */
+/* $Id$ */
+/*
+ * Copyright (C) 2003-2006 Benny Prijono <benny@prijono.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#ifndef __PJSIP_SIP_REG_H__
+#define __PJSIP_SIP_REG_H__
+
+/**
+ * @file sip_reg.h
+ * @brief SIP Registration Client
+ */
+
+#include <pjsip/sip_types.h>
+#include <pjsip/sip_auth.h>
+#include <pjsip_mod_ua/sip_ua.h>
+
+PJ_BEGIN_DECL
+
+/**
+ * @defgroup PJSUA_REGC SIP Registration Client
+ * @ingroup PJSUA
+ * @{
+ * \brief
+ * API for performing registration for user agent.
+ */
+
+/** Typedef for client registration data. */
+typedef struct pjsip_regc pjsip_regc;
+
+/** Maximum contacts in registration. */
+#define PJSIP_REGC_MAX_CONTACT 10
+
+/** Expiration not specified. */
+#define PJSIP_REGC_EXPIRATION_NOT_SPECIFIED ((pj_uint32_t)0xFFFFFFFFUL)
+
+/** Buffer to hold all contacts. */
+#define PJSIP_REGC_CONTACT_BUF_SIZE 512
+
+/** Structure to hold parameters when calling application's callback.
+ * The application's callback is called when the client registration process
+ * has finished.
+ */
+struct pjsip_regc_cbparam
+{
+ pjsip_regc *regc;
+ void *token;
+ int code;
+ pj_str_t reason;
+ pjsip_rx_data *rdata;
+ int contact_cnt;
+ int expiration;
+ pjsip_contact_hdr *contact[PJSIP_REGC_MAX_CONTACT];
+};
+
+
+/** Type declaration for callback to receive registration result. */
+typedef void pjsip_regc_cb(struct pjsip_regc_cbparam *param);
+
+
+/**
+ * Get the module instance for client registration module.
+ *
+ * @return client registration module.
+ */
+PJ_DECL(pjsip_module*) pjsip_regc_get_module(void);
+
+
+/**
+ * Create client registration structure.
+ *
+ * @param endpt Endpoint, used to allocate pool from.
+ * @param token A data to be associated with the client registration struct.
+ * @param cb Pointer to callback function to receive registration status.
+ *
+ * @return client registration structure.
+ */
+PJ_DECL(pjsip_regc*) pjsip_regc_create( pjsip_endpoint *endpt, void *token,
+ pjsip_regc_cb *cb);
+
+
+/**
+ * Destroy client registration structure. If a registration transaction is
+ * in progress, then the structure will be deleted only after a final response
+ * has been received, and in this case, the callback won't be called.
+ *
+ * @param regc The client registration structure.
+ */
+PJ_DECL(void) pjsip_regc_destroy(pjsip_regc *regc);
+
+/**
+ * Get the memory pool associated with a registration client handle.
+ *
+ * @param regc The client registration structure.
+ * @return pool handle.
+ */
+PJ_DECL(pj_pool_t*) pjsip_regc_get_pool(pjsip_regc *regc);
+
+/**
+ * Initialize client registration structure with various information needed to
+ * perform the registration.
+ *
+ * @param regc The client registration structure.
+ * @param from_url The person performing the registration, must be a SIP URL type.
+ * @param to_url The address of record for which the registration is targetd, must
+ * be a SIP/SIPS URL.
+ * @param ccnt Number of contacts in the array.
+ * @param contact Array of contacts.
+ * @param expires Default expiration interval (in seconds) to be applied for
+ * contact URL that doesn't have expiration settings. If the
+ * value PJSIP_REGC_EXPIRATION_NOT_SPECIFIED is given, then
+ * no default expiration will be applied.
+ * @return zero on success.
+ */
+PJ_DECL(pj_status_t) pjsip_regc_init(pjsip_regc *regc,
+ const pj_str_t *srv_url,
+ const pj_str_t *from_url,
+ const pj_str_t *to_url,
+ int ccnt,
+ const pj_str_t contact[],
+ pj_uint32_t expires);
+
+
+/**
+ * Set authentication credentials to use by this registration.
+ *
+ * @param dlg The registration structure.
+ * @param count Number of credentials in the array.
+ * @param cred Array of credentials.
+ *
+ * @return Zero on success.
+ */
+PJ_DECL(pj_status_t) pjsip_regc_set_credentials( pjsip_regc *regc,
+ int count,
+ const pjsip_cred_info cred[] );
+
+/**
+ * Create REGISTER request for the specified client registration structure.
+ *
+ * After successfull registration, application can inspect the contacts in
+ * the client registration structure to list what contacts are associaciated
+ * with the address of record being targeted in the registration.
+ *
+ * @param regc The client registration structure.
+ * @param autoreg If non zero, the library will automatically refresh the
+ * next registration until application unregister.
+ *
+ * @return SIP REGISTER request.
+ */
+PJ_DECL(pjsip_tx_data*) pjsip_regc_register(pjsip_regc *regc, pj_bool_t autoreg);
+
+
+/**
+ * Create REGISTER request to unregister all contacts from server records.
+ *
+ * @param regc The client registration structure.
+ *
+ * @return SIP REGISTER request.
+ */
+PJ_DECL(pjsip_tx_data*) pjsip_regc_unregister(pjsip_regc *regc);
+
+/**
+ * Update Contact details in the client registration structure.
+ *
+ * @param regc The client registration structure.
+ * @param ccnt Number of contacts.
+ * @param contact Array of contacts.
+ * @return zero if sucessfull.
+ */
+PJ_DECL(pj_status_t) pjsip_regc_update_contact( pjsip_regc *regc,
+ int ccnt,
+ const pj_str_t contact[] );
+
+/**
+ * Update the expires value.
+ *
+ * @param regc The client registration structure.
+ * @param expires The new expires value.
+ * @return zero on successfull.
+ */
+PJ_DECL(pj_status_t) pjsip_regc_update_expires( pjsip_regc *regc,
+ pj_uint32_t expires );
+
+/**
+ * Sends outgoing REGISTER request.
+ * The process will complete asynchronously, and application
+ * will be notified via the callback when the process completes.
+ *
+ * @param regc The client registration structure.
+ * @param tdata Transmit data.
+ */
+PJ_DECL(void) pjsip_regc_send(pjsip_regc *regc, pjsip_tx_data *tdata);
+
+
+PJ_END_DECL
+
+#endif /* __PJSIP_REG_H__ */
diff --git a/pjsip/include/pjsip-ua/sip_ua.h b/pjsip/include/pjsip-ua/sip_ua.h
index d2cc605a..92bf0db1 100644
--- a/pjsip/include/pjsip-ua/sip_ua.h
+++ b/pjsip/include/pjsip-ua/sip_ua.h
@@ -1,97 +1,97 @@
-/* $Id$ */
-/*
- * Copyright (C) 2003-2006 Benny Prijono <benny@prijono.org>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
-#ifndef __PJSIP_SIP_UA_H__
-#define __PJSIP_SIP_UA_H__
-
-/**
- * @file ua.h
- * @brief SIP User Agent Library
- */
-
-#include <pjsip_mod_ua/sip_dialog.h>
-
-PJ_BEGIN_DECL
-
-/**
- * @defgroup PJSUA SIP User Agent Stack
- */
-
-/**
- * @defgroup PJSUA_UA SIP User Agent
- * @ingroup PJSUA
- * @{
- * \brief
- * User Agent manages the interactions between application and SIP dialogs.
- */
-
-typedef struct pjsip_dlg_callback pjsip_dlg_callback;
-
-/**
- * \brief This structure describes a User Agent instance.
- */
-struct pjsip_user_agent
-{
- pjsip_endpoint *endpt;
- pj_pool_t *pool;
- pj_mutex_t *mutex;
- pj_uint32_t mod_id;
- pj_hash_table_t *dlg_table;
- pjsip_dlg_callback *dlg_cb;
- pj_list dlg_list;
-};
-
-/**
- * Create a new dialog.
- */
-PJ_DECL(pjsip_dlg*) pjsip_ua_create_dialog( pjsip_user_agent *ua,
- pjsip_role_e role );
-
-
-/**
- * Destroy dialog.
- */
-PJ_DECL(void) pjsip_ua_destroy_dialog( pjsip_dlg *dlg );
-
-
-/**
- * Register callback to receive dialog notifications.
- */
-PJ_DECL(void) pjsip_ua_set_dialog_callback( pjsip_user_agent *ua,
- pjsip_dlg_callback *cb );
-
-
-/**
- * Get the module interface for the UA module.
- */
-PJ_DECL(pjsip_module*) pjsip_ua_get_module(void);
-
-
-/**
- * Dump user agent state to log file.
- */
-PJ_DECL(void) pjsip_ua_dump( pjsip_user_agent *ua );
-
-/**
- * @}
- */
-
-PJ_END_DECL
-
-#endif /* __PJSIP_UA_H__ */
-
+/* $Id$ */
+/*
+ * Copyright (C) 2003-2006 Benny Prijono <benny@prijono.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#ifndef __PJSIP_SIP_UA_H__
+#define __PJSIP_SIP_UA_H__
+
+/**
+ * @file ua.h
+ * @brief SIP User Agent Library
+ */
+
+#include <pjsip_mod_ua/sip_dialog.h>
+
+PJ_BEGIN_DECL
+
+/**
+ * @defgroup PJSUA SIP User Agent Stack
+ */
+
+/**
+ * @defgroup PJSUA_UA SIP User Agent
+ * @ingroup PJSUA
+ * @{
+ * \brief
+ * User Agent manages the interactions between application and SIP dialogs.
+ */
+
+typedef struct pjsip_dlg_callback pjsip_dlg_callback;
+
+/**
+ * \brief This structure describes a User Agent instance.
+ */
+struct pjsip_user_agent
+{
+ pjsip_endpoint *endpt;
+ pj_pool_t *pool;
+ pj_mutex_t *mutex;
+ pj_uint32_t mod_id;
+ pj_hash_table_t *dlg_table;
+ pjsip_dlg_callback *dlg_cb;
+ pj_list dlg_list;
+};
+
+/**
+ * Create a new dialog.
+ */
+PJ_DECL(pjsip_dlg*) pjsip_ua_create_dialog( pjsip_user_agent *ua,
+ pjsip_role_e role );
+
+
+/**
+ * Destroy dialog.
+ */
+PJ_DECL(void) pjsip_ua_destroy_dialog( pjsip_dlg *dlg );
+
+
+/**
+ * Register callback to receive dialog notifications.
+ */
+PJ_DECL(void) pjsip_ua_set_dialog_callback( pjsip_user_agent *ua,
+ pjsip_dlg_callback *cb );
+
+
+/**
+ * Get the module interface for the UA module.
+ */
+PJ_DECL(pjsip_module*) pjsip_ua_get_module(void);
+
+
+/**
+ * Dump user agent state to log file.
+ */
+PJ_DECL(void) pjsip_ua_dump( pjsip_user_agent *ua );
+
+/**
+ * @}
+ */
+
+PJ_END_DECL
+
+#endif /* __PJSIP_UA_H__ */
+
diff --git a/pjsip/include/pjsip/print_util.h b/pjsip/include/pjsip/print_util.h
index 56f05851..b1362a72 100644
--- a/pjsip/include/pjsip/print_util.h
+++ b/pjsip/include/pjsip/print_util.h
@@ -1,144 +1,144 @@
-/* $Id$ */
-/*
- * Copyright (C) 2003-2006 Benny Prijono <benny@prijono.org>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
-#ifndef __PJSIP_PRINT_H__
-#define __PJSIP_PRINT_H__
-
-/* Minimum space left in the buffer */
-#define MIN_SPACE 10
-
-#define copy_advance_check(buf,str) \
- do { \
- if ((str).slen+MIN_SPACE >= (endbuf-buf)) return -1; \
- pj_memcpy(buf, (str).ptr, (str).slen); \
- buf += (str).slen; \
- } while (0)
-
-/*
-static char *imp_copy_advance_pair(char *buf, char *endbuf, const char *str1, int len1, const pj_str_t *str2)
-{
- if (str2->slen) {
- int printed = len1+str2->slen;
- if (printed+MIN_SPACE >= (endbuf-buf)) return NULL;
- pj_memcpy(buf,str1,len1);
- pj_memcpy(buf+len1, str2->ptr, str2->slen);
- return buf + printed;
- } else
- return buf;
-}
-*/
-
-#define copy_advance_pair_check(buf,str1,len1,str2) \
- do { \
- if (str2.slen) { \
- printed = len1+str2.slen; \
- if (printed+MIN_SPACE >= (endbuf-buf)) return -1; \
- pj_memcpy(buf,str1,len1); \
- pj_memcpy(buf+len1, str2.ptr, str2.slen); \
- buf += printed; \
- } \
- } while (0)
-/*
-#define copy_advance_pair(buf,str1,len1,str2) \
- do { \
- buf = imp_copy_advance_pair(buf, endbuf, str1, len1, &str2); \
- if (buf == NULL) return -1; \
- } while (0)
-*/
-
-#define copy_advance_pair_quote_check(buf,str1,len1,str2,quotebegin,quoteend) \
- do { \
- if (str2.slen) { \
- printed = len1+str2.slen+2; \
- if (printed+MIN_SPACE >= (endbuf-buf)) return -1; \
- pj_memcpy(buf,str1,len1); \
- *(buf+len1)=quotebegin; \
- pj_memcpy(buf+len1+1, str2.ptr, str2.slen); \
- *(buf+printed-1) = quoteend; \
- buf += printed; \
- } \
- } while (0)
-
-#define copy_advance_pair_escape(buf,str1,len1,str2,unres) \
- do { \
- if (str2.slen) { \
- pj_ssize_t esc_len; \
- if (len1+str2.slen+MIN_SPACE >= (endbuf-buf)) return -1; \
- pj_memcpy(buf,str1,len1); \
- buf += len1; \
- esc_len=pj_strncpy2_escape(buf, &str2, (endbuf-buf), &unres); \
- if (esc_len < 0) return -1; \
- buf += esc_len; \
- if (endbuf-buf < MIN_SPACE) return -1; \
- } \
- } while (0)
-
-
-#define copy_advance_no_check(buf,str) \
- do { \
- pj_memcpy(buf, (str).ptr, (str).slen); \
- buf += (str).slen; \
- } while (0)
-
-#define copy_advance_escape(buf,str,unres) \
- do { \
- pj_ssize_t len = \
- pj_strncpy2_escape(buf, &(str), (endbuf-buf), &(unres)); \
- if (len < 0) return -1; \
- buf += len; \
- if (endbuf-buf < MIN_SPACE) return -1; \
- } while (0)
-
-#define copy_advance_pair_no_check(buf,str1,len1,str2) \
- if (str2.slen) { \
- pj_memcpy(buf,str1,len1); \
- pj_memcpy(buf+len1, str2.ptr, str2.slen); \
- buf += len1+str2.slen; \
- }
-
-#define copy_advance copy_advance_check
-#define copy_advance_pair copy_advance_pair_check
-#define copy_advance_pair_quote copy_advance_pair_quote_check
-
-#define copy_advance_pair_quote_cond(buf,str1,len1,str2,quotebegin,quoteend) \
- do { \
- if (str2.slen && *str2.ptr!=quotebegin) \
- copy_advance_pair_quote(buf,str1,len1,str2,quotebegin,quoteend); \
- else \
- copy_advance_pair(buf,str1,len1,str2); \
- } while (0)
-
-/*
- * Internal type declarations.
- */
-typedef void* (*pjsip_hdr_clone_fptr)(pj_pool_t *, const void*);
-typedef int (*pjsip_hdr_print_fptr)(void *hdr, char *buf, pj_size_t len);
-
-extern const pj_str_t pjsip_hdr_names[];
-
-PJ_INLINE(void) init_hdr(void *hptr, pjsip_hdr_e htype, void *vptr)
-{
- pjsip_hdr *hdr = hptr;
- hdr->type = htype;
- hdr->name = hdr->sname = pjsip_hdr_names[htype];
- hdr->vptr = vptr;
- pj_list_init(hdr);
-}
-
-#endif /* __PJSIP_PRINT_H__ */
-
+/* $Id$ */
+/*
+ * Copyright (C) 2003-2006 Benny Prijono <benny@prijono.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#ifndef __PJSIP_PRINT_H__
+#define __PJSIP_PRINT_H__
+
+/* Minimum space left in the buffer */
+#define MIN_SPACE 10
+
+#define copy_advance_check(buf,str) \
+ do { \
+ if ((str).slen+MIN_SPACE >= (endbuf-buf)) return -1; \
+ pj_memcpy(buf, (str).ptr, (str).slen); \
+ buf += (str).slen; \
+ } while (0)
+
+/*
+static char *imp_copy_advance_pair(char *buf, char *endbuf, const char *str1, int len1, const pj_str_t *str2)
+{
+ if (str2->slen) {
+ int printed = len1+str2->slen;
+ if (printed+MIN_SPACE >= (endbuf-buf)) return NULL;
+ pj_memcpy(buf,str1,len1);
+ pj_memcpy(buf+len1, str2->ptr, str2->slen);
+ return buf + printed;
+ } else
+ return buf;
+}
+*/
+
+#define copy_advance_pair_check(buf,str1,len1,str2) \
+ do { \
+ if (str2.slen) { \
+ printed = len1+str2.slen; \
+ if (printed+MIN_SPACE >= (endbuf-buf)) return -1; \
+ pj_memcpy(buf,str1,len1); \
+ pj_memcpy(buf+len1, str2.ptr, str2.slen); \
+ buf += printed; \
+ } \
+ } while (0)
+/*
+#define copy_advance_pair(buf,str1,len1,str2) \
+ do { \
+ buf = imp_copy_advance_pair(buf, endbuf, str1, len1, &str2); \
+ if (buf == NULL) return -1; \
+ } while (0)
+*/
+
+#define copy_advance_pair_quote_check(buf,str1,len1,str2,quotebegin,quoteend) \
+ do { \
+ if (str2.slen) { \
+ printed = len1+str2.slen+2; \
+ if (printed+MIN_SPACE >= (endbuf-buf)) return -1; \
+ pj_memcpy(buf,str1,len1); \
+ *(buf+len1)=quotebegin; \
+ pj_memcpy(buf+len1+1, str2.ptr, str2.slen); \
+ *(buf+printed-1) = quoteend; \
+ buf += printed; \
+ } \
+ } while (0)
+
+#define copy_advance_pair_escape(buf,str1,len1,str2,unres) \
+ do { \
+ if (str2.slen) { \
+ pj_ssize_t esc_len; \
+ if (len1+str2.slen+MIN_SPACE >= (endbuf-buf)) return -1; \
+ pj_memcpy(buf,str1,len1); \
+ buf += len1; \
+ esc_len=pj_strncpy2_escape(buf, &str2, (endbuf-buf), &unres); \
+ if (esc_len < 0) return -1; \
+ buf += esc_len; \
+ if (endbuf-buf < MIN_SPACE) return -1; \
+ } \
+ } while (0)
+
+
+#define copy_advance_no_check(buf,str) \
+ do { \
+ pj_memcpy(buf, (str).ptr, (str).slen); \
+ buf += (str).slen; \
+ } while (0)
+
+#define copy_advance_escape(buf,str,unres) \
+ do { \
+ pj_ssize_t len = \
+ pj_strncpy2_escape(buf, &(str), (endbuf-buf), &(unres)); \
+ if (len < 0) return -1; \
+ buf += len; \
+ if (endbuf-buf < MIN_SPACE) return -1; \
+ } while (0)
+
+#define copy_advance_pair_no_check(buf,str1,len1,str2) \
+ if (str2.slen) { \
+ pj_memcpy(buf,str1,len1); \
+ pj_memcpy(buf+len1, str2.ptr, str2.slen); \
+ buf += len1+str2.slen; \
+ }
+
+#define copy_advance copy_advance_check
+#define copy_advance_pair copy_advance_pair_check
+#define copy_advance_pair_quote copy_advance_pair_quote_check
+
+#define copy_advance_pair_quote_cond(buf,str1,len1,str2,quotebegin,quoteend) \
+ do { \
+ if (str2.slen && *str2.ptr!=quotebegin) \
+ copy_advance_pair_quote(buf,str1,len1,str2,quotebegin,quoteend); \
+ else \
+ copy_advance_pair(buf,str1,len1,str2); \
+ } while (0)
+
+/*
+ * Internal type declarations.
+ */
+typedef void* (*pjsip_hdr_clone_fptr)(pj_pool_t *, const void*);
+typedef int (*pjsip_hdr_print_fptr)(void *hdr, char *buf, pj_size_t len);
+
+extern const pj_str_t pjsip_hdr_names[];
+
+PJ_INLINE(void) init_hdr(void *hptr, pjsip_hdr_e htype, void *vptr)
+{
+ pjsip_hdr *hdr = hptr;
+ hdr->type = htype;
+ hdr->name = hdr->sname = pjsip_hdr_names[htype];
+ hdr->vptr = vptr;
+ pj_list_init(hdr);
+}
+
+#endif /* __PJSIP_PRINT_H__ */
+
diff --git a/pjsip/include/pjsip/sip_auth.h b/pjsip/include/pjsip/sip_auth.h
index 56a5fe26..7d8b9c62 100644
--- a/pjsip/include/pjsip/sip_auth.h
+++ b/pjsip/include/pjsip/sip_auth.h
@@ -1,247 +1,247 @@
-/* $Id$ */
-/*
- * Copyright (C) 2003-2006 Benny Prijono <benny@prijono.org>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
-#ifndef __PJSIP_AUTH_SIP_AUTH_H__
-#define __PJSIP_AUTH_SIP_AUTH_H__
-
-/**
- * @file pjsip_auth.h
- * @brief SIP Authorization Module.
- */
-
-#include <pjsip/sip_config.h>
-#include <pjsip/sip_auth_msg.h>
-
-PJ_BEGIN_DECL
-
-/**
- * @defgroup PJSIP_AUTH_API Authorization API's
- * @ingroup PJSIP_AUTH
- * @{
- */
-
- /** Type of data in the credential information. */
-typedef enum pjsip_cred_data_type
-{
- PJSIP_CRED_DATA_PLAIN_PASSWD, /**< Plain text password. */
- PJSIP_CRED_DATA_DIGEST, /**< Hashed digest. */
-} pjsip_cred_data_type;
-
-/** Authentication's quality of protection (qop) type. */
-typedef enum pjsip_auth_qop_type
-{
- PJSIP_AUTH_QOP_NONE, /**< No quality of protection. */
- PJSIP_AUTH_QOP_AUTH, /**< Authentication. */
- PJSIP_AUTH_QOP_AUTH_INT, /**< Authentication with integrity protection. */
- PJSIP_AUTH_QOP_UNKNOWN, /**< Unknown protection. */
-} pjsip_auth_qop_type;
-
-
-/**
- * This structure describes credential information.
- * A credential information is a static, persistent information that identifies
- * username and password required to authorize to a specific realm.
- */
-struct pjsip_cred_info
-{
- pj_str_t realm; /**< Realm. */
- pj_str_t scheme; /**< Scheme. */
- pj_str_t username; /**< User name. */
- int data_type; /**< Type of data. */
- pj_str_t data; /**< The data, which can be a plaintext
- password or a hashed digest. */
-};
-
-/**
- * This structure describes cached value of previously sent Authorization
- * or Proxy-Authorization header. The authentication framework keeps a list
- * of this structure and will resend the same header to the same server
- * as long as the method, uri, and nonce stays the same.
- */
-typedef struct pjsip_cached_auth_hdr
-{
- PJ_DECL_LIST_MEMBER(struct pjsip_cached_auth_hdr);
-
- pjsip_method method;
- pjsip_authorization_hdr *hdr;
-
-} pjsip_cached_auth_hdr;
-
-
-/**
- * This structure describes authentication information for the specified
- * realm. Each instance of this structure describes authentication "session"
- * between this endpoint and remote server. This "session" information is
- * usefull to keep information that persists for more than one challenge,
- * such as nonce-count and cnonce value.
- *
- * Other than that, this structure also keeps the last authorization headers
- * that have been sent in the cache list.
- */
-typedef struct pjsip_auth_session
-{
- PJ_DECL_LIST_MEMBER(struct pjsip_auth_session);
-
- pj_str_t realm;
- pj_bool_t is_proxy;
- pjsip_auth_qop_type qop_value;
-#if PJSIP_AUTH_QOP_SUPPORT
- pj_uint32_t nc;
- pj_str_t cnonce;
-#endif
-#if PJSIP_AUTH_AUTO_SEND_NEXT
- pjsip_www_authenticate_hdr *last_chal;
-#endif
-#if PJSIP_AUTH_HEADER_CACHING
- pjsip_cached_auth_hdr cached_hdr;
-#endif
-
-} pjsip_auth_session;
-
-
-/**
- * Create authorization header for the specified credential.
- * Application calls this function to create Authorization or Proxy-Authorization
- * header after receiving WWW-Authenticate or Proxy-Authenticate challenge
- * (normally in 401/407 response).
- * If authorization session argument is specified, this function will update
- * the session with the updated information if required (e.g. to update
- * nonce-count when qop is "auth" or "auth-int"). This function will also
- * save the authorization header in the session's cached header list.
- *
- * @param req_pool Pool to allocate new header for the request.
- * @param hdr The WWW-Authenticate or Proxy-Authenticate found in
- * the response.
- * @param uri The URI for which authorization is targeted to.
- * @param cred_info The credential to be used for authentication.
- * @param method The method.
- * @param sess_pool Session pool to update session or to allocate message
- * in the cache. May be NULL if auth_sess is NULL.
- * @param auth_sess If not NULL, this specifies the specific authentication
- * session to be used or updated.
- *
- * @return The Authorization header, which can be typecasted to
- * Proxy-Authorization.
- */
-PJ_DECL(pjsip_authorization_hdr*) pjsip_auth_respond(
- pj_pool_t *req_pool,
- const pjsip_www_authenticate_hdr *hdr,
- const pjsip_uri *uri,
- const pjsip_cred_info *cred_info,
- const pjsip_method *method,
- pj_pool_t *sess_pool,
- pjsip_auth_session *auth_sess);
-
-/**
- * Verify digest in the authorization request.
- *
- * @param hdr The incoming Authorization/Proxy-Authorization header.
- * @param method The method.
- * @param password The plaintext password to verify.
- *
- * @return Non-zero if authorization succeed.
- */
-PJ_DECL(pj_bool_t) pjsip_auth_verify( const pjsip_authorization_hdr *hdr,
- const pj_str_t *method,
- const pjsip_cred_info *cred_info );
-
-
-/**
- * This function can be used to find credential information which matches
- * the specified realm.
- *
- * @param count Number of credentials in the parameter.
- * @param cred The array of credentials.
- * @param realm Realm to search.
- * @param scheme Authentication scheme.
- *
- * @return The credential which matches the specified realm.
- */
-PJ_DECL(const pjsip_cred_info*) pjsip_auth_find_cred( unsigned count,
- const pjsip_cred_info cred[],
- const pj_str_t *realm,
- const pj_str_t *scheme );
-
-
-/**
- * Initialize new request message with authorization headers.
- * This function will put Authorization/Proxy-Authorization headers to the
- * outgoing request message. If caching is enabled (PJSIP_AUTH_HEADER_CACHING)
- * and the session has previously sent Authorization/Proxy-Authorization header
- * with the same method, then the same Authorization/Proxy-Authorization header
- * will be resent from the cache only if qop is not present. If the stack is
- * configured to automatically generate next Authorization/Proxy-Authorization
- * headers (PJSIP_AUTH_AUTO_SEND_NEXT flag), then new Authorization/Proxy-
- * Authorization headers are calculated and generated when they are not present
- * in the case or if authorization session has qop.
- *
- * If both PJSIP_AUTH_HEADER_CACHING flag and PJSIP_AUTH_AUTO_SEND_NEXT flag
- * are not set, this function will do nothing. The stack then will only send
- * Authorization/Proxy-Authorization to respond 401/407 response.
- *
- * @param sess_pool Session level pool, where memory will be allocated from
- * for data that persists across requests (e.g. caching).
- * @param tdata The request message to be initialized.
- * @param sess_list List of authorization sessions that have been recorded.
- * @param cred_count Number of credentials.
- * @param cred_info Array of credentials.
- *
- * @return Zero if successfull.
- */
-PJ_DECL(pj_status_t) pjsip_auth_init_req( pj_pool_t *sess_pool,
- pjsip_tx_data *tdata,
- pjsip_auth_session *sess_list,
- int cred_count,
- const pjsip_cred_info cred_info[]);
-
-/**
- * Call this function when a transaction failed with 401 or 407 response.
- * This function will reinitialize the original request message with the
- * authentication challenge found in the response message, and add the
- * new authorization header in the authorization cache.
- *
- * Note that upon return the reference counter of the transmit data
- * will be incremented.
- *
- * @param endpt Endpoint.
- * @param pool The pool to allocate memory for new cred_info.
- * @param cached_list Cached authorization headers.
- * @param cred_count Number of credentials.
- * @param cred_info Array of credentials to use.
- * @param tdata The original request message, which normally can be
- * retrieved from tsx->last_tx.
- * @param rdata The response message containing 401/407 status.
- *
- * @return New transmit data buffer, or NULL if the dialog
- * can not respond to the authorization challenge.
- */
-PJ_DECL(pjsip_tx_data*)
-pjsip_auth_reinit_req( pjsip_endpoint *endpt,
- pj_pool_t *ses_pool,
- pjsip_auth_session *sess_list,
- int cred_count, const pjsip_cred_info cred_info[],
- pjsip_tx_data *tdata, const pjsip_rx_data *rdata);
-
-/**
- * @}
- */
-
-PJ_END_DECL
-
-#endif /* __PJSIP_AUTH_SIP_AUTH_H__ */
-
+/* $Id$ */
+/*
+ * Copyright (C) 2003-2006 Benny Prijono <benny@prijono.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#ifndef __PJSIP_AUTH_SIP_AUTH_H__
+#define __PJSIP_AUTH_SIP_AUTH_H__
+
+/**
+ * @file pjsip_auth.h
+ * @brief SIP Authorization Module.
+ */
+
+#include <pjsip/sip_config.h>
+#include <pjsip/sip_auth_msg.h>
+
+PJ_BEGIN_DECL
+
+/**
+ * @defgroup PJSIP_AUTH_API Authorization API's
+ * @ingroup PJSIP_AUTH
+ * @{
+ */
+
+ /** Type of data in the credential information. */
+typedef enum pjsip_cred_data_type
+{
+ PJSIP_CRED_DATA_PLAIN_PASSWD, /**< Plain text password. */
+ PJSIP_CRED_DATA_DIGEST, /**< Hashed digest. */
+} pjsip_cred_data_type;
+
+/** Authentication's quality of protection (qop) type. */
+typedef enum pjsip_auth_qop_type
+{
+ PJSIP_AUTH_QOP_NONE, /**< No quality of protection. */
+ PJSIP_AUTH_QOP_AUTH, /**< Authentication. */
+ PJSIP_AUTH_QOP_AUTH_INT, /**< Authentication with integrity protection. */
+ PJSIP_AUTH_QOP_UNKNOWN, /**< Unknown protection. */
+} pjsip_auth_qop_type;
+
+
+/**
+ * This structure describes credential information.
+ * A credential information is a static, persistent information that identifies
+ * username and password required to authorize to a specific realm.
+ */
+struct pjsip_cred_info
+{
+ pj_str_t realm; /**< Realm. */
+ pj_str_t scheme; /**< Scheme. */
+ pj_str_t username; /**< User name. */
+ int data_type; /**< Type of data. */
+ pj_str_t data; /**< The data, which can be a plaintext
+ password or a hashed digest. */
+};
+
+/**
+ * This structure describes cached value of previously sent Authorization
+ * or Proxy-Authorization header. The authentication framework keeps a list
+ * of this structure and will resend the same header to the same server
+ * as long as the method, uri, and nonce stays the same.
+ */
+typedef struct pjsip_cached_auth_hdr
+{
+ PJ_DECL_LIST_MEMBER(struct pjsip_cached_auth_hdr);
+
+ pjsip_method method;
+ pjsip_authorization_hdr *hdr;
+
+} pjsip_cached_auth_hdr;
+
+
+/**
+ * This structure describes authentication information for the specified
+ * realm. Each instance of this structure describes authentication "session"
+ * between this endpoint and remote server. This "session" information is
+ * usefull to keep information that persists for more than one challenge,
+ * such as nonce-count and cnonce value.
+ *
+ * Other than that, this structure also keeps the last authorization headers
+ * that have been sent in the cache list.
+ */
+typedef struct pjsip_auth_session
+{
+ PJ_DECL_LIST_MEMBER(struct pjsip_auth_session);
+
+ pj_str_t realm;
+ pj_bool_t is_proxy;
+ pjsip_auth_qop_type qop_value;
+#if PJSIP_AUTH_QOP_SUPPORT
+ pj_uint32_t nc;
+ pj_str_t cnonce;
+#endif
+#if PJSIP_AUTH_AUTO_SEND_NEXT
+ pjsip_www_authenticate_hdr *last_chal;
+#endif
+#if PJSIP_AUTH_HEADER_CACHING
+ pjsip_cached_auth_hdr cached_hdr;
+#endif
+
+} pjsip_auth_session;
+
+
+/**
+ * Create authorization header for the specified credential.
+ * Application calls this function to create Authorization or Proxy-Authorization
+ * header after receiving WWW-Authenticate or Proxy-Authenticate challenge
+ * (normally in 401/407 response).
+ * If authorization session argument is specified, this function will update
+ * the session with the updated information if required (e.g. to update
+ * nonce-count when qop is "auth" or "auth-int"). This function will also
+ * save the authorization header in the session's cached header list.
+ *
+ * @param req_pool Pool to allocate new header for the request.
+ * @param hdr The WWW-Authenticate or Proxy-Authenticate found in
+ * the response.
+ * @param uri The URI for which authorization is targeted to.
+ * @param cred_info The credential to be used for authentication.
+ * @param method The method.
+ * @param sess_pool Session pool to update session or to allocate message
+ * in the cache. May be NULL if auth_sess is NULL.
+ * @param auth_sess If not NULL, this specifies the specific authentication
+ * session to be used or updated.
+ *
+ * @return The Authorization header, which can be typecasted to
+ * Proxy-Authorization.
+ */
+PJ_DECL(pjsip_authorization_hdr*) pjsip_auth_respond(
+ pj_pool_t *req_pool,
+ const pjsip_www_authenticate_hdr *hdr,
+ const pjsip_uri *uri,
+ const pjsip_cred_info *cred_info,
+ const pjsip_method *method,
+ pj_pool_t *sess_pool,
+ pjsip_auth_session *auth_sess);
+
+/**
+ * Verify digest in the authorization request.
+ *
+ * @param hdr The incoming Authorization/Proxy-Authorization header.
+ * @param method The method.
+ * @param password The plaintext password to verify.
+ *
+ * @return Non-zero if authorization succeed.
+ */
+PJ_DECL(pj_bool_t) pjsip_auth_verify( const pjsip_authorization_hdr *hdr,
+ const pj_str_t *method,
+ const pjsip_cred_info *cred_info );
+
+
+/**
+ * This function can be used to find credential information which matches
+ * the specified realm.
+ *
+ * @param count Number of credentials in the parameter.
+ * @param cred The array of credentials.
+ * @param realm Realm to search.
+ * @param scheme Authentication scheme.
+ *
+ * @return The credential which matches the specified realm.
+ */
+PJ_DECL(const pjsip_cred_info*) pjsip_auth_find_cred( unsigned count,
+ const pjsip_cred_info cred[],
+ const pj_str_t *realm,
+ const pj_str_t *scheme );
+
+
+/**
+ * Initialize new request message with authorization headers.
+ * This function will put Authorization/Proxy-Authorization headers to the
+ * outgoing request message. If caching is enabled (PJSIP_AUTH_HEADER_CACHING)
+ * and the session has previously sent Authorization/Proxy-Authorization header
+ * with the same method, then the same Authorization/Proxy-Authorization header
+ * will be resent from the cache only if qop is not present. If the stack is
+ * configured to automatically generate next Authorization/Proxy-Authorization
+ * headers (PJSIP_AUTH_AUTO_SEND_NEXT flag), then new Authorization/Proxy-
+ * Authorization headers are calculated and generated when they are not present
+ * in the case or if authorization session has qop.
+ *
+ * If both PJSIP_AUTH_HEADER_CACHING flag and PJSIP_AUTH_AUTO_SEND_NEXT flag
+ * are not set, this function will do nothing. The stack then will only send
+ * Authorization/Proxy-Authorization to respond 401/407 response.
+ *
+ * @param sess_pool Session level pool, where memory will be allocated from
+ * for data that persists across requests (e.g. caching).
+ * @param tdata The request message to be initialized.
+ * @param sess_list List of authorization sessions that have been recorded.
+ * @param cred_count Number of credentials.
+ * @param cred_info Array of credentials.
+ *
+ * @return Zero if successfull.
+ */
+PJ_DECL(pj_status_t) pjsip_auth_init_req( pj_pool_t *sess_pool,
+ pjsip_tx_data *tdata,
+ pjsip_auth_session *sess_list,
+ int cred_count,
+ const pjsip_cred_info cred_info[]);
+
+/**
+ * Call this function when a transaction failed with 401 or 407 response.
+ * This function will reinitialize the original request message with the
+ * authentication challenge found in the response message, and add the
+ * new authorization header in the authorization cache.
+ *
+ * Note that upon return the reference counter of the transmit data
+ * will be incremented.
+ *
+ * @param endpt Endpoint.
+ * @param pool The pool to allocate memory for new cred_info.
+ * @param cached_list Cached authorization headers.
+ * @param cred_count Number of credentials.
+ * @param cred_info Array of credentials to use.
+ * @param tdata The original request message, which normally can be
+ * retrieved from tsx->last_tx.
+ * @param rdata The response message containing 401/407 status.
+ *
+ * @return New transmit data buffer, or NULL if the dialog
+ * can not respond to the authorization challenge.
+ */
+PJ_DECL(pjsip_tx_data*)
+pjsip_auth_reinit_req( pjsip_endpoint *endpt,
+ pj_pool_t *ses_pool,
+ pjsip_auth_session *sess_list,
+ int cred_count, const pjsip_cred_info cred_info[],
+ pjsip_tx_data *tdata, const pjsip_rx_data *rdata);
+
+/**
+ * @}
+ */
+
+PJ_END_DECL
+
+#endif /* __PJSIP_AUTH_SIP_AUTH_H__ */
+
diff --git a/pjsip/include/pjsip/sip_auth_msg.h b/pjsip/include/pjsip/sip_auth_msg.h
index 5ef74d23..81d6cd09 100644
--- a/pjsip/include/pjsip/sip_auth_msg.h
+++ b/pjsip/include/pjsip/sip_auth_msg.h
@@ -1,209 +1,213 @@
-/* $Id$ */
-/*
- * Copyright (C) 2003-2006 Benny Prijono <benny@prijono.org>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
-#ifndef __PJSIP_AUTH_SIP_AUTH_MSG_H__
-#define __PJSIP_AUTH_SIP_AUTH_MSG_H__
-
-#include <pjsip/sip_msg.h>
-
-PJ_BEGIN_DECL
-
-/**
- * @defgroup PJSIP_MSG_AUTHORIZATION Header Field: Authorization and Proxy-Authorization
- * @brief Authorization and Proxy-Authorization header field.
- * @ingroup PJSIP_MSG
- * @{
- */
-
-/**
- * Common credential.
- */
-struct pjsip_common_credential
-{
- pj_str_t realm;
-};
-
-typedef struct pjsip_common_credential pjsip_common_credential;
-
-
-/**
- * This structure describe credential used in Authorization and
- * Proxy-Authorization header for digest authentication scheme.
- */
-struct pjsip_digest_credential
-{
- pj_str_t realm;
- pj_str_t username;
- pj_str_t nonce;
- pj_str_t uri;
- pj_str_t response;
- pj_str_t algorithm;
- pj_str_t cnonce;
- pj_str_t opaque;
- pj_str_t qop;
- pj_str_t nc;
- pj_str_t other_param;
-};
-
-typedef struct pjsip_digest_credential pjsip_digest_credential;
-
-/**
- * This structure describe credential used in Authorization and
- * Proxy-Authorization header for PGP authentication scheme.
- */
-struct pjsip_pgp_credential
-{
- pj_str_t realm;
- pj_str_t version;
- pj_str_t signature;
- pj_str_t signed_by;
- pj_str_t nonce;
-};
-
-typedef struct pjsip_pgp_credential pjsip_pgp_credential;
-
-/**
- * This structure describes SIP Authorization header (and also SIP
- * Proxy-Authorization header).
- */
-struct pjsip_authorization_hdr
-{
- PJSIP_DECL_HDR_MEMBER(struct pjsip_authorization_hdr);
- pj_str_t scheme;
- union
- {
- pjsip_common_credential common;
- pjsip_digest_credential digest;
- pjsip_pgp_credential pgp;
- } credential;
-};
-
-typedef struct pjsip_authorization_hdr pjsip_authorization_hdr;
-
-/** SIP Proxy-Authorization header shares the same structure as SIP
- Authorization header.
- */
-typedef struct pjsip_authorization_hdr pjsip_proxy_authorization_hdr;
-
-/**
- * Create SIP Authorization header.
- * @param pool Pool where memory will be allocated from.
- * @return SIP Authorization header.
- */
-PJ_DECL(pjsip_authorization_hdr*) pjsip_authorization_hdr_create(pj_pool_t *pool);
-
-/**
- * Create SIP Proxy-Authorization header.
- * @param pool Pool where memory will be allocated from.
- * @return SIP Proxy-Authorization header.
- */
-PJ_DECL(pjsip_proxy_authorization_hdr*) pjsip_proxy_authorization_hdr_create(pj_pool_t *pool);
-
-
-/**
- * @}
- */
-
-/**
- * @defgroup PJSIP_WWW_AUTH Header Field: Proxy-Authenticate and WWW-Authenticate
- * @brief Proxy-Authenticate and WWW-Authenticate.
- * @ingroup PJSIP_MSG
- * @{
- */
-
-struct pjsip_common_challenge
-{
- pj_str_t realm;
-};
-
-typedef struct pjsip_common_challenge pjsip_common_challenge;
-
-/**
- * This structure describes authentication challenge used in Proxy-Authenticate
- * or WWW-Authenticate for digest authentication scheme.
- */
-struct pjsip_digest_challenge
-{
- pj_str_t realm;
- pj_str_t domain;
- pj_str_t nonce;
- pj_str_t opaque;
- int stale;
- pj_str_t algorithm;
- pj_str_t qop;
- pj_str_t other_param;
-};
-
-typedef struct pjsip_digest_challenge pjsip_digest_challenge;
-
-/**
- * This structure describes authentication challenge used in Proxy-Authenticate
- * or WWW-Authenticate for PGP authentication scheme.
- */
-struct pjsip_pgp_challenge
-{
- pj_str_t realm;
- pj_str_t version;
- pj_str_t micalgorithm;
- pj_str_t pubalgorithm;
- pj_str_t nonce;
-};
-
-typedef struct pjsip_pgp_challenge pjsip_pgp_challenge;
-
-/**
- * This structure describe SIP WWW-Authenticate header (Proxy-Authenticate
- * header also uses the same structure).
- */
-struct pjsip_www_authenticate_hdr
-{
- PJSIP_DECL_HDR_MEMBER(struct pjsip_www_authenticate_hdr);
- pj_str_t scheme;
- union
- {
- pjsip_common_challenge common;
- pjsip_digest_challenge digest;
- pjsip_pgp_challenge pgp;
- } challenge;
-};
-
-typedef struct pjsip_www_authenticate_hdr pjsip_www_authenticate_hdr;
-typedef struct pjsip_www_authenticate_hdr pjsip_proxy_authenticate_hdr;
-
-
-/**
- * Create SIP WWW-Authenticate header.
- * @param pool Pool where memory will be allocated from.
- * @return SIP WWW-Authenticate header.
- */
-PJ_DECL(pjsip_www_authenticate_hdr*) pjsip_www_authenticate_hdr_create(pj_pool_t *pool);
-
-/**
- * Create SIP Proxy-Authenticate header.
- * @param pool Pool where memory will be allocated from.
- * @return SIP Proxy-Authenticate header.
- */
-PJ_DECL(pjsip_proxy_authenticate_hdr*) pjsip_proxy_authenticate_hdr_create(pj_pool_t *pool);
-
-/**
- * @}
- */
-
-PJ_END_DECL
-
-#endif /* __PJSIP_AUTH_SIP_AUTH_MSG_H__ */
+/* $Id$ */
+/*
+ * Copyright (C) 2003-2006 Benny Prijono <benny@prijono.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#ifndef __PJSIP_AUTH_SIP_AUTH_MSG_H__
+#define __PJSIP_AUTH_SIP_AUTH_MSG_H__
+
+#include <pjsip/sip_msg.h>
+
+PJ_BEGIN_DECL
+
+/**
+ * @defgroup PJSIP_MSG_AUTHORIZATION Header Field: Authorization and Proxy-Authorization
+ * @brief Authorization and Proxy-Authorization header field.
+ * @ingroup PJSIP_MSG
+ * @{
+ */
+
+/**
+ * Common credential.
+ */
+struct pjsip_common_credential
+{
+ pj_str_t realm;
+ pjsip_param other_param;
+};
+
+typedef struct pjsip_common_credential pjsip_common_credential;
+
+
+/**
+ * This structure describe credential used in Authorization and
+ * Proxy-Authorization header for digest authentication scheme.
+ */
+struct pjsip_digest_credential
+{
+ pj_str_t realm;
+ pjsip_param other_param;
+ pj_str_t username;
+ pj_str_t nonce;
+ pj_str_t uri;
+ pj_str_t response;
+ pj_str_t algorithm;
+ pj_str_t cnonce;
+ pj_str_t opaque;
+ pj_str_t qop;
+ pj_str_t nc;
+};
+
+typedef struct pjsip_digest_credential pjsip_digest_credential;
+
+/**
+ * This structure describe credential used in Authorization and
+ * Proxy-Authorization header for PGP authentication scheme.
+ */
+struct pjsip_pgp_credential
+{
+ pj_str_t realm;
+ pjsip_param other_param;
+ pj_str_t version;
+ pj_str_t signature;
+ pj_str_t signed_by;
+ pj_str_t nonce;
+};
+
+typedef struct pjsip_pgp_credential pjsip_pgp_credential;
+
+/**
+ * This structure describes SIP Authorization header (and also SIP
+ * Proxy-Authorization header).
+ */
+struct pjsip_authorization_hdr
+{
+ PJSIP_DECL_HDR_MEMBER(struct pjsip_authorization_hdr);
+ pj_str_t scheme;
+ union
+ {
+ pjsip_common_credential common;
+ pjsip_digest_credential digest;
+ pjsip_pgp_credential pgp;
+ } credential;
+};
+
+typedef struct pjsip_authorization_hdr pjsip_authorization_hdr;
+
+/** SIP Proxy-Authorization header shares the same structure as SIP
+ Authorization header.
+ */
+typedef struct pjsip_authorization_hdr pjsip_proxy_authorization_hdr;
+
+/**
+ * Create SIP Authorization header.
+ * @param pool Pool where memory will be allocated from.
+ * @return SIP Authorization header.
+ */
+PJ_DECL(pjsip_authorization_hdr*) pjsip_authorization_hdr_create(pj_pool_t *pool);
+
+/**
+ * Create SIP Proxy-Authorization header.
+ * @param pool Pool where memory will be allocated from.
+ * @return SIP Proxy-Authorization header.
+ */
+PJ_DECL(pjsip_proxy_authorization_hdr*) pjsip_proxy_authorization_hdr_create(pj_pool_t *pool);
+
+
+/**
+ * @}
+ */
+
+/**
+ * @defgroup PJSIP_WWW_AUTH Header Field: Proxy-Authenticate and WWW-Authenticate
+ * @brief Proxy-Authenticate and WWW-Authenticate.
+ * @ingroup PJSIP_MSG
+ * @{
+ */
+
+struct pjsip_common_challenge
+{
+ pj_str_t realm;
+ pjsip_param other_param;
+};
+
+typedef struct pjsip_common_challenge pjsip_common_challenge;
+
+/**
+ * This structure describes authentication challenge used in Proxy-Authenticate
+ * or WWW-Authenticate for digest authentication scheme.
+ */
+struct pjsip_digest_challenge
+{
+ pj_str_t realm;
+ pjsip_param other_param;
+ pj_str_t domain;
+ pj_str_t nonce;
+ pj_str_t opaque;
+ int stale;
+ pj_str_t algorithm;
+ pj_str_t qop;
+};
+
+typedef struct pjsip_digest_challenge pjsip_digest_challenge;
+
+/**
+ * This structure describes authentication challenge used in Proxy-Authenticate
+ * or WWW-Authenticate for PGP authentication scheme.
+ */
+struct pjsip_pgp_challenge
+{
+ pj_str_t realm;
+ pjsip_param other_param;
+ pj_str_t version;
+ pj_str_t micalgorithm;
+ pj_str_t pubalgorithm;
+ pj_str_t nonce;
+};
+
+typedef struct pjsip_pgp_challenge pjsip_pgp_challenge;
+
+/**
+ * This structure describe SIP WWW-Authenticate header (Proxy-Authenticate
+ * header also uses the same structure).
+ */
+struct pjsip_www_authenticate_hdr
+{
+ PJSIP_DECL_HDR_MEMBER(struct pjsip_www_authenticate_hdr);
+ pj_str_t scheme;
+ union
+ {
+ pjsip_common_challenge common;
+ pjsip_digest_challenge digest;
+ pjsip_pgp_challenge pgp;
+ } challenge;
+};
+
+typedef struct pjsip_www_authenticate_hdr pjsip_www_authenticate_hdr;
+typedef struct pjsip_www_authenticate_hdr pjsip_proxy_authenticate_hdr;
+
+
+/**
+ * Create SIP WWW-Authenticate header.
+ * @param pool Pool where memory will be allocated from.
+ * @return SIP WWW-Authenticate header.
+ */
+PJ_DECL(pjsip_www_authenticate_hdr*) pjsip_www_authenticate_hdr_create(pj_pool_t *pool);
+
+/**
+ * Create SIP Proxy-Authenticate header.
+ * @param pool Pool where memory will be allocated from.
+ * @return SIP Proxy-Authenticate header.
+ */
+PJ_DECL(pjsip_proxy_authenticate_hdr*) pjsip_proxy_authenticate_hdr_create(pj_pool_t *pool);
+
+/**
+ * @}
+ */
+
+PJ_END_DECL
+
+#endif /* __PJSIP_AUTH_SIP_AUTH_MSG_H__ */
diff --git a/pjsip/include/pjsip/sip_auth_parser.h b/pjsip/include/pjsip/sip_auth_parser.h
index ee96c73e..3a214bef 100644
--- a/pjsip/include/pjsip/sip_auth_parser.h
+++ b/pjsip/include/pjsip/sip_auth_parser.h
@@ -1,87 +1,87 @@
-/* $Id$ */
-/*
- * Copyright (C) 2003-2006 Benny Prijono <benny@prijono.org>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
-#ifndef __PJSIP_AUTH_SIP_AUTH_PARSER_H__
-#define __PJSIP_AUTH_SIP_AUTH_PARSER_H__
-
-/**
- * @file pjsip_auth_parser.h
- * @brief SIP Authorization Parser Module.
- */
-
-#include <pj/types.h>
-
-PJ_BEGIN_DECL
-
-/**
- * @defgroup PJSIP_AUTH_PARSER_MODULE Authorization Parser Module
- * @ingroup PJSIP_AUTH
- * @{
- */
-
-/**
- * Initialize and register authorization parser module.
- * This will register parser handler for various Authorization related headers
- * such as Authorization, WWW-Authenticate, Proxy-Authorizization, and
- * Proxy-Authenticate headers.
- *
- * @return PJ_SUCCESS or the appropriate status code.
- */
-PJ_DECL(pj_status_t) pjsip_auth_init_parser(void);
-
-/**
- * DeInitialize authorization parser module.
- */
-PJ_DECL(void) pjsip_auth_deinit_parser();
-
-
-extern const pj_str_t pjsip_USERNAME_STR,
- pjsip_REALM_STR,
- pjsip_NONCE_STR,
- pjsip_URI_STR,
- pjsip_RESPONSE_STR,
- pjsip_ALGORITHM_STR,
- pjsip_DOMAIN_STR,
- pjsip_STALE_STR,
- pjsip_QOP_STR,
- pjsip_CNONCE_STR,
- pjsip_OPAQUE_STR,
- pjsip_NC_STR,
- pjsip_TRUE_STR,
- pjsip_FALSE_STR,
- pjsip_DIGEST_STR,
- pjsip_PGP_STR,
- pjsip_MD5_STR,
- pjsip_AUTH_STR;
-/*
-extern const pj_str_t pjsip_QUOTED_TRUE_STR,
- pjsip_QUOTED_FALSE_STR,
- pjsip_QUOTED_DIGEST_STR,
- pjsip_QUOTED_PGP_STR,
- pjsip_QUOTED_MD5_STR,
- pjsip_QUOTED_AUTH_STR;
-*/
-
-/**
- * @}
- */
-
-PJ_END_DECL
-
-#endif /* __PJSIP_AUTH_SIP_AUTH_PARSER_H__ */
-
+/* $Id$ */
+/*
+ * Copyright (C) 2003-2006 Benny Prijono <benny@prijono.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#ifndef __PJSIP_AUTH_SIP_AUTH_PARSER_H__
+#define __PJSIP_AUTH_SIP_AUTH_PARSER_H__
+
+/**
+ * @file pjsip_auth_parser.h
+ * @brief SIP Authorization Parser Module.
+ */
+
+#include <pj/types.h>
+
+PJ_BEGIN_DECL
+
+/**
+ * @defgroup PJSIP_AUTH_PARSER_MODULE Authorization Parser Module
+ * @ingroup PJSIP_AUTH
+ * @{
+ */
+
+/**
+ * Initialize and register authorization parser module.
+ * This will register parser handler for various Authorization related headers
+ * such as Authorization, WWW-Authenticate, Proxy-Authorizization, and
+ * Proxy-Authenticate headers.
+ *
+ * @return PJ_SUCCESS or the appropriate status code.
+ */
+PJ_DECL(pj_status_t) pjsip_auth_init_parser(void);
+
+/**
+ * DeInitialize authorization parser module.
+ */
+PJ_DECL(void) pjsip_auth_deinit_parser();
+
+
+extern const pj_str_t pjsip_USERNAME_STR,
+ pjsip_REALM_STR,
+ pjsip_NONCE_STR,
+ pjsip_URI_STR,
+ pjsip_RESPONSE_STR,
+ pjsip_ALGORITHM_STR,
+ pjsip_DOMAIN_STR,
+ pjsip_STALE_STR,
+ pjsip_QOP_STR,
+ pjsip_CNONCE_STR,
+ pjsip_OPAQUE_STR,
+ pjsip_NC_STR,
+ pjsip_TRUE_STR,
+ pjsip_FALSE_STR,
+ pjsip_DIGEST_STR,
+ pjsip_PGP_STR,
+ pjsip_MD5_STR,
+ pjsip_AUTH_STR;
+/*
+extern const pj_str_t pjsip_QUOTED_TRUE_STR,
+ pjsip_QUOTED_FALSE_STR,
+ pjsip_QUOTED_DIGEST_STR,
+ pjsip_QUOTED_PGP_STR,
+ pjsip_QUOTED_MD5_STR,
+ pjsip_QUOTED_AUTH_STR;
+*/
+
+/**
+ * @}
+ */
+
+PJ_END_DECL
+
+#endif /* __PJSIP_AUTH_SIP_AUTH_PARSER_H__ */
+
diff --git a/pjsip/include/pjsip/sip_config.h b/pjsip/include/pjsip/sip_config.h
index 5620c33b..98441918 100644
--- a/pjsip/include/pjsip/sip_config.h
+++ b/pjsip/include/pjsip/sip_config.h
@@ -1,163 +1,163 @@
-/* $Id$ */
-/*
- * Copyright (C) 2003-2006 Benny Prijono <benny@prijono.org>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
-#ifndef __PJSIP_SIP_CONFIG_H__
-#define __PJSIP_SIP_CONFIG_H__
-
-#include <pj/config.h>
-
-/* Endpoint. */
-#define PJSIP_MAX_TIMER_COUNT (2*PJSIP_MAX_TSX_COUNT + 2*PJSIP_MAX_DIALOG_COUNT)
-#define PJSIP_POOL_LEN_ENDPT (2048+64*PJSIP_MAX_TSX_COUNT)
-#define PJSIP_POOL_INC_ENDPT (1024)
-
-/* Transport related constants. */
-#define PJSIP_MAX_TRANSPORTS (PJ_IOQUEUE_MAX_HANDLES)
-#define PJSIP_MAX_PKT_LEN 1500
-#define PJSIP_POOL_LEN_RDATA 2500
-#define PJSIP_POOL_INC_RDATA 512
-#define PJSIP_POOL_LEN_TRANSPORT 512
-#define PJSIP_POOL_INC_TRANSPORT 512
-#define PJSIP_POOL_LEN_TDATA 2500
-#define PJSIP_POOL_INC_TDATA 512
-#define PJSIP_POOL_LEN_UA (64 + 32*PJSIP_MAX_DIALOG_COUNT)
-#define PJSIP_POOL_INC_UA 0
-#define PJSIP_TRANSPORT_CLOSE_TIMEOUT 30
-#define PJSIP_MAX_TRANSPORT_USAGE 16
-
-#define PJSIP_MAX_FORWARDS_VALUE 70
-
-#define PJSIP_RFC3261_BRANCH_ID "z9hG4bK"
-#define PJSIP_RFC3261_BRANCH_LEN 7
-
-/* Message/URL related constants. */
-#define PJSIP_MAX_CALL_ID_LEN PJ_GUID_STRING_LENGTH
-#define PJSIP_MAX_TAG_LEN PJ_GUID_STRING_LENGTH
-#define PJSIP_MAX_BRANCH_LEN (PJSIP_RFC3261_BRANCH_LEN + PJ_GUID_STRING_LENGTH)
-#define PJSIP_MAX_URL_SIZE 256
-#define PJSIP_MAX_HNAME_LEN 64
-
-/* Transction related constants. */
-#define PJSIP_MAX_TSX_COUNT (16*1024)
-#define PJSIP_POOL_LEN_TSX 1536 //768
-#define PJSIP_POOL_INC_TSX 256
-#define PJSIP_MAX_TSX_KEY_LEN (PJSIP_MAX_URL_SIZE*2)
-
-/* Dialog related constants. */
-#define PJSIP_MAX_DIALOG_COUNT (16*1024)
-#define PJSIP_POOL_LEN_DIALOG 1200
-#define PJSIP_POOL_INC_DIALOG 512
-
-/* Transport manager hash table size (must be 2^n-1). */
-#define PJSIP_TPMGR_HTABLE_SIZE 31
-
-/* Transport idle timeout before it's destroyed. */
-#define PJSIP_TRANSPORT_IDLE_TIME 30
-
-/* Max entries to process in timer heap per poll. */
-#define PJSIP_MAX_TIMED_OUT_ENTRIES 10
-
-/* Module related constants. */
-#define PJSIP_MAX_MODULE 8
-
-/*****************************************************************************
- * Default timeout settings, in miliseconds.
- */
-
-//#define PJSIP_T1_TIMEOUT 15000
-//#define PJSIP_T2_TIMEOUT 60000
-
-/* T1 timeout value. */
-#if !defined(PJSIP_T1_TIMEOUT)
-# define PJSIP_T1_TIMEOUT 500
-#endif
-
-/* T2 timeout value. */
-#if !defined(PJSIP_T2_TIMEOUT)
-# define PJSIP_T2_TIMEOUT 4000
-#endif
-
-/* Completed timer for non-INVITE */
-#if !defined(PJSIP_T4_TIMEOUT)
-# define PJSIP_T4_TIMEOUT 5000
-#endif
-
-/* Completed timer for INVITE */
-#if !defined(PJSIP_TD_TIMEOUT)
-# define PJSIP_TD_TIMEOUT 32000
-#endif
-
-
-/*****************************************************************************
- * Authorization
- */
-
-/*
- * If this flag is set, the stack will keep the Authorization/Proxy-Authorization
- * headers that are sent in a cache. Future requests with the same realm and
- * the same method will use the headers in the cache (as long as no qop is
- * required by server).
- *
- * Turning on this flag will make authorization process goes faster, but
- * will grow the memory usage undefinitely until the dialog/registration
- * session is terminated.
- *
- * Default: 1
- */
-#if !defined(PJSIP_AUTH_HEADER_CACHING)
-# define PJSIP_AUTH_HEADER_CACHING 1
-#endif
-
-/*
- * If this flag is set, the stack will proactively send Authorization/Proxy-
- * Authorization header for next requests. If next request has the same method
- * with any of previous requests, then the last header which is saved in
- * the cache will be used (if PJSIP_AUTH_CACHING is set). Otherwise a fresh
- * header will be recalculated. If a particular server has requested qop, then
- * a fresh header will always be calculated.
- *
- * If this flag is NOT set, then the stack will only send Authorization/Proxy-
- * Authorization headers when it receives 401/407 response from server.
- *
- * Turning ON this flag will grow memory usage of a dialog/registration pool
- * indefinitely until it is terminated, because the stack needs to keep the
- * last WWW-Authenticate/Proxy-Authenticate challenge.
- *
- * Default: 1
- */
-#if !defined(PJSIP_AUTH_AUTO_SEND_NEXT)
-# define PJSIP_AUTH_AUTO_SEND_NEXT 1
-#endif
-
-/*
- * Support qop="auth" directive.
- * This option also requires client to cache the last challenge offered by
- * server.
- *
- * Default: 1
- */
-#if !defined(PJSIP_AUTH_QOP_SUPPORT)
-# define PJSIP_AUTH_QOP_SUPPORT 1
-#endif
-
-
-#include <pj/config.h>
-
-
-#endif /* __PJSIP_SIP_CONFIG_H__ */
-
+/* $Id$ */
+/*
+ * Copyright (C) 2003-2006 Benny Prijono <benny@prijono.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#ifndef __PJSIP_SIP_CONFIG_H__
+#define __PJSIP_SIP_CONFIG_H__
+
+#include <pj/config.h>
+
+/* Endpoint. */
+#define PJSIP_MAX_TIMER_COUNT (2*PJSIP_MAX_TSX_COUNT + 2*PJSIP_MAX_DIALOG_COUNT)
+#define PJSIP_POOL_LEN_ENDPT (2048+64*PJSIP_MAX_TSX_COUNT)
+#define PJSIP_POOL_INC_ENDPT (1024)
+
+/* Transport related constants. */
+#define PJSIP_MAX_TRANSPORTS (PJ_IOQUEUE_MAX_HANDLES)
+#define PJSIP_MAX_PKT_LEN 1500
+#define PJSIP_POOL_LEN_RDATA 2500
+#define PJSIP_POOL_INC_RDATA 512
+#define PJSIP_POOL_LEN_TRANSPORT 512
+#define PJSIP_POOL_INC_TRANSPORT 512
+#define PJSIP_POOL_LEN_TDATA 2500
+#define PJSIP_POOL_INC_TDATA 512
+#define PJSIP_POOL_LEN_UA (64 + 32*PJSIP_MAX_DIALOG_COUNT)
+#define PJSIP_POOL_INC_UA 0
+#define PJSIP_TRANSPORT_CLOSE_TIMEOUT 30
+#define PJSIP_MAX_TRANSPORT_USAGE 16
+
+#define PJSIP_MAX_FORWARDS_VALUE 70
+
+#define PJSIP_RFC3261_BRANCH_ID "z9hG4bK"
+#define PJSIP_RFC3261_BRANCH_LEN 7
+
+/* Message/URL related constants. */
+#define PJSIP_MAX_CALL_ID_LEN PJ_GUID_STRING_LENGTH
+#define PJSIP_MAX_TAG_LEN PJ_GUID_STRING_LENGTH
+#define PJSIP_MAX_BRANCH_LEN (PJSIP_RFC3261_BRANCH_LEN + PJ_GUID_STRING_LENGTH)
+#define PJSIP_MAX_URL_SIZE 256
+#define PJSIP_MAX_HNAME_LEN 64
+
+/* Transction related constants. */
+#define PJSIP_MAX_TSX_COUNT (16*1024)
+#define PJSIP_POOL_LEN_TSX 1536 //768
+#define PJSIP_POOL_INC_TSX 256
+#define PJSIP_MAX_TSX_KEY_LEN (PJSIP_MAX_URL_SIZE*2)
+
+/* Dialog related constants. */
+#define PJSIP_MAX_DIALOG_COUNT (16*1024)
+#define PJSIP_POOL_LEN_DIALOG 1200
+#define PJSIP_POOL_INC_DIALOG 512
+
+/* Transport manager hash table size (must be 2^n-1). */
+#define PJSIP_TPMGR_HTABLE_SIZE 31
+
+/* Transport idle timeout before it's destroyed. */
+#define PJSIP_TRANSPORT_IDLE_TIME 30
+
+/* Max entries to process in timer heap per poll. */
+#define PJSIP_MAX_TIMED_OUT_ENTRIES 10
+
+/* Module related constants. */
+#define PJSIP_MAX_MODULE 8
+
+/*****************************************************************************
+ * Default timeout settings, in miliseconds.
+ */
+
+//#define PJSIP_T1_TIMEOUT 15000
+//#define PJSIP_T2_TIMEOUT 60000
+
+/* T1 timeout value. */
+#if !defined(PJSIP_T1_TIMEOUT)
+# define PJSIP_T1_TIMEOUT 500
+#endif
+
+/* T2 timeout value. */
+#if !defined(PJSIP_T2_TIMEOUT)
+# define PJSIP_T2_TIMEOUT 4000
+#endif
+
+/* Completed timer for non-INVITE */
+#if !defined(PJSIP_T4_TIMEOUT)
+# define PJSIP_T4_TIMEOUT 5000
+#endif
+
+/* Completed timer for INVITE */
+#if !defined(PJSIP_TD_TIMEOUT)
+# define PJSIP_TD_TIMEOUT 32000
+#endif
+
+
+/*****************************************************************************
+ * Authorization
+ */
+
+/*
+ * If this flag is set, the stack will keep the Authorization/Proxy-Authorization
+ * headers that are sent in a cache. Future requests with the same realm and
+ * the same method will use the headers in the cache (as long as no qop is
+ * required by server).
+ *
+ * Turning on this flag will make authorization process goes faster, but
+ * will grow the memory usage undefinitely until the dialog/registration
+ * session is terminated.
+ *
+ * Default: 1
+ */
+#if !defined(PJSIP_AUTH_HEADER_CACHING)
+# define PJSIP_AUTH_HEADER_CACHING 1
+#endif
+
+/*
+ * If this flag is set, the stack will proactively send Authorization/Proxy-
+ * Authorization header for next requests. If next request has the same method
+ * with any of previous requests, then the last header which is saved in
+ * the cache will be used (if PJSIP_AUTH_CACHING is set). Otherwise a fresh
+ * header will be recalculated. If a particular server has requested qop, then
+ * a fresh header will always be calculated.
+ *
+ * If this flag is NOT set, then the stack will only send Authorization/Proxy-
+ * Authorization headers when it receives 401/407 response from server.
+ *
+ * Turning ON this flag will grow memory usage of a dialog/registration pool
+ * indefinitely until it is terminated, because the stack needs to keep the
+ * last WWW-Authenticate/Proxy-Authenticate challenge.
+ *
+ * Default: 1
+ */
+#if !defined(PJSIP_AUTH_AUTO_SEND_NEXT)
+# define PJSIP_AUTH_AUTO_SEND_NEXT 1
+#endif
+
+/*
+ * Support qop="auth" directive.
+ * This option also requires client to cache the last challenge offered by
+ * server.
+ *
+ * Default: 1
+ */
+#if !defined(PJSIP_AUTH_QOP_SUPPORT)
+# define PJSIP_AUTH_QOP_SUPPORT 1
+#endif
+
+
+#include <pj/config.h>
+
+
+#endif /* __PJSIP_SIP_CONFIG_H__ */
+
diff --git a/pjsip/include/pjsip/sip_endpoint.h b/pjsip/include/pjsip/sip_endpoint.h
index 37f4acee..3e0702f1 100644
--- a/pjsip/include/pjsip/sip_endpoint.h
+++ b/pjsip/include/pjsip/sip_endpoint.h
@@ -1,386 +1,386 @@
-/* $Id$ */
-/*
- * Copyright (C) 2003-2006 Benny Prijono <benny@prijono.org>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
-#ifndef __PJSIP_SIP_ENDPOINT_H__
-#define __PJSIP_SIP_ENDPOINT_H__
-
-/**
- * @file sip_endpoint.h
- * @brief SIP Endpoint.
- */
-
-#include <pjsip/sip_transport.h>
-#include <pjsip/sip_resolve.h>
-
-PJ_BEGIN_DECL
-
-/**
- * @defgroup PJSIP SIP Stack Core
- * Implementation of core SIP protocol stack processing.
- */
-
-/**
- * @defgroup PJSIP_ENDPT SIP Endpoint
- * @ingroup PJSIP
- * @brief
- * Representation of SIP node instance.
- *
- * SIP Endpoint instance (pjsip_endpoint) can be viewed as the master/owner of
- * all SIP objects in an application. It performs the following roles:
- * - it manages the allocation/deallocation of memory pools for all objects.
- * - it manages listeners and transports, and how they are used by
- * transactions.
- * - it owns transaction hash table.
- * - it receives incoming messages from transport layer and automatically
- * dispatches them to the correct transaction (or create a new one).
- * - it has a single instance of timer management (timer heap).
- * - it manages modules, which is the primary means of extending the library.
- * - it provides single polling function for all objects and distributes
- * events.
- * - it provides SIP policy such as which outbound proxy to use for all
- * outgoing SIP request messages.
- * - it automatically handles incoming requests which can not be handled by
- * existing modules (such as when incoming request has unsupported method).
- * - and so on..
- *
- * Theoritically application can have multiple instances of SIP endpoint,
- * although it's not clear why application may want to do it.
- *
- * @{
- */
-
-/**
- * Create an instance of SIP endpoint from the specified pool factory.
- * The pool factory reference then will be kept by the endpoint, so that
- * future memory allocations by SIP components will be taken from the same
- * pool factory.
- *
- * @param pf Pool factory that will be used for the lifetime of
- * endpoint.
- * @param name Optional name to be specified for the endpoint.
- * If this parameter is NULL, then the name will use
- * local host name.
- * @param endpt Pointer to receive endpoint instance.
- *
- * @return PJ_SUCCESS on success.
- */
-PJ_DECL(pj_status_t) pjsip_endpt_create(pj_pool_factory *pf,
- const char *name,
- pjsip_endpoint **endpt);
-
-/**
- * Destroy endpoint instance. Application must make sure that all pending
- * transactions have been terminated properly, because this function does not
- * check for the presence of pending transactions.
- *
- * @param endpt The SIP endpoint to be destroyed.
- */
-PJ_DECL(void) pjsip_endpt_destroy(pjsip_endpoint *endpt);
-
-/**
- * Get endpoint name.
- *
- * @param endpt The SIP endpoint instance.
- *
- * @return Endpoint name, as was registered during endpoint
- * creation. The string is NULL terminated.
- */
-PJ_DECL(const pj_str_t*) pjsip_endpt_name(const pjsip_endpoint *endpt);
-
-/**
- * Poll for events. Application must call this function periodically to ensure
- * that all events from both transports and timer heap are handled in timely
- * manner. This function, like all other endpoint functions, is thread safe,
- * and application may have more than one thread concurrently calling this function.
- *
- * @param endpt The endpoint.
- * @param max_timeout Maximum time to wait for events, or NULL to wait forever
- * until event is received.
- */
-PJ_DECL(void) pjsip_endpt_handle_events( pjsip_endpoint *endpt,
- const pj_time_val *max_timeout);
-
-/**
- * Dump endpoint status to the log. This will print the status to the log
- * with log level 3.
- *
- * @param endpt The endpoint.
- * @param detail If non zero, then it will dump a detailed output.
- * BEWARE that this option may crash the system because
- * it tries to access all memory pools.
- */
-PJ_DECL(void) pjsip_endpt_dump( pjsip_endpoint *endpt, pj_bool_t detail );
-
-/**
- * Create pool from the endpoint. All SIP components should allocate their
- * memory pool by calling this function, to make sure that the pools are
- * allocated from the same pool factory. This function, like all other endpoint
- * functions, is thread safe.
- *
- * @param endpt The SIP endpoint.
- * @param pool_name Name to be assigned to the pool.
- * @param initial The initial size of the pool.
- * @param increment The resize size.
- * @return Memory pool, or NULL on failure.
- *
- * @see pj_pool_create
- */
-PJ_DECL(pj_pool_t*) pjsip_endpt_create_pool( pjsip_endpoint *endpt,
- const char *pool_name,
- pj_size_t initial,
- pj_size_t increment );
-
-/**
- * Return back pool to endpoint to be released back to the pool factory.
- * This function, like all other endpoint functions, is thread safe.
- *
- * @param endpt The endpoint.
- * @param pool The pool to be destroyed.
- */
-PJ_DECL(void) pjsip_endpt_destroy_pool( pjsip_endpoint *endpt,
- pj_pool_t *pool );
-
-/**
- * Schedule timer to endpoint's timer heap. Application must poll the endpoint
- * periodically (by calling #pjsip_endpt_handle_events) to ensure that the
- * timer events are handled in timely manner. When the timeout for the timer
- * has elapsed, the callback specified in the entry argument will be called.
- * This function, like all other endpoint functions, is thread safe.
- *
- * @param endpt The endpoint.
- * @param entry The timer entry.
- * @param delay The relative delay of the timer.
- * @return PJ_OK (zero) if successfull.
- */
-PJ_DECL(pj_status_t) pjsip_endpt_schedule_timer( pjsip_endpoint *endpt,
- pj_timer_entry *entry,
- const pj_time_val *delay );
-
-/**
- * Cancel the previously registered timer.
- * This function, like all other endpoint functions, is thread safe.
- *
- * @param endpt The endpoint.
- * @param entry The timer entry previously registered.
- */
-PJ_DECL(void) pjsip_endpt_cancel_timer( pjsip_endpoint *endpt,
- pj_timer_entry *entry );
-
-/**
- * Create a new transaction. After creating the transaction, application MUST
- * initialize the transaction as either UAC or UAS (by calling
- * #pjsip_tsx_init_uac or #pjsip_tsx_init_uas), then must register the
- * transaction to endpoint with #pjsip_endpt_register_tsx.
- * This function, like all other endpoint functions, is thread safe.
- *
- * @param endpt The SIP endpoint.
- * @param p_tsx Pointer to receive the transaction.
- *
- * @return PJ_SUCCESS or the appropriate error code.
- */
-PJ_DECL(pj_status_t) pjsip_endpt_create_tsx(pjsip_endpoint *endpt,
- pjsip_transaction **p_tsx);
-
-/**
- * Register the transaction to the endpoint's transaction table.
- * Before the transaction is registered, it must have been initialized as
- * either UAS or UAC by calling #pjsip_tsx_init_uac or #pjsip_tsx_init_uas.
- * This function, like all other endpoint functions, is thread safe.
- *
- * @param endpt The SIP endpoint.
- * @param tsx The transaction.
- */
-PJ_DECL(void) pjsip_endpt_register_tsx( pjsip_endpoint *endpt,
- pjsip_transaction *tsx);
-
-/**
- * Forcefull destroy the transaction.
- * The only time where application needs to call this function is when the
- * transaction fails to initialize in #pjsip_tsx_init_uac or
- * #pjsip_tsx_init_uas. For other cases. the transaction will be destroyed
- * automaticly by endpoint.
- *
- * @param endpt The endpoint.
- * @param tsx The transaction to destroy.
- */
-PJ_DECL(void) pjsip_endpt_destroy_tsx( pjsip_endpoint *endpt,
- pjsip_transaction *tsx);
-
-/**
- * Create a new transmit data buffer.
- * This function, like all other endpoint functions, is thread safe.
- *
- * @param endpt The endpoint.
- * @param p_tdata Pointer to receive transmit data buffer.
- *
- * @return PJ_SUCCESS or the appropriate error code.
- */
-PJ_DECL(pj_status_t) pjsip_endpt_create_tdata( pjsip_endpoint *endpt,
- pjsip_tx_data **p_tdata);
-
-/**
- * Asynchronously resolve a SIP target host or domain according to rule
- * specified in RFC 3263 (Locating SIP Servers). When the resolving operation
- * has completed, the callback will be called.
- *
- * Note: at the moment we don't have implementation of RFC 3263 yet!
- *
- * @param resolver The resolver engine.
- * @param pool The pool to allocate resolver job.
- * @param target The target specification to be resolved.
- * @param token A user defined token to be passed back to callback function.
- * @param cb The callback function.
- */
-PJ_DECL(void) pjsip_endpt_resolve( pjsip_endpoint *endpt,
- pj_pool_t *pool,
- pjsip_host_port *target,
- void *token,
- pjsip_resolver_callback *cb);
-
-/**
- * Get transport manager instance.
- *
- * @param endpt The endpoint.
- *
- * @return Transport manager instance.
- */
-PJ_DECL(pjsip_tpmgr*) pjsip_endpt_get_tpmgr(pjsip_endpoint *endpt);
-
-/**
- * Get ioqueue instance.
- *
- * @param endpt The endpoint.
- *
- * @return The ioqueue.
- */
-PJ_DECL(pj_ioqueue_t*) pjsip_endpt_get_ioqueue(pjsip_endpoint *endpt);
-
-/**
- * Find a SIP transport suitable for sending SIP message to the specified
- * address. This function will complete asynchronously when the transport is
- * ready (for example, when TCP socket is connected), and when it completes,
- * the callback will be called with the status of the operation.
- *
- * @see pjsip_transport_get
- */
-PJ_DECL(pj_status_t) pjsip_endpt_alloc_transport( pjsip_endpoint *endpt,
- pjsip_transport_type_e type,
- const pj_sockaddr_in *remote,
- pjsip_transport **p_transport);
-
-/**
- * Get additional headers to be put in outgoing request message.
- * This function is normally called by transaction layer when sending outgoing
- * requests.
- *
- * @param endpt The endpoint.
- *
- * @return List of additional headers to be put in outgoing requests.
- */
-PJ_DECL(const pjsip_hdr*) pjsip_endpt_get_request_headers(pjsip_endpoint *endpt);
-
-/**
- * Get "Allow" header from endpoint. The endpoint builds the "Allow" header
- * from the list of methods supported by modules.
- *
- * @param endpt The endpoint.
- *
- * @return "Allow" header, or NULL if endpoint doesn't have "Allow" header.
- */
-PJ_DECL(const pjsip_allow_hdr*) pjsip_endpt_get_allow_hdr( pjsip_endpoint *endpt );
-
-
-/**
- * Find transaction in endpoint's transaction table by the transaction's key.
- * This function normally is only used by modules. The key for a transaction
- * can be created by calling #pjsip_tsx_create_key.
- *
- * @param endpt The endpoint instance.
- * @param key Transaction key, as created with #pjsip_tsx_create_key.
- *
- * @return The transaction, or NULL if it's not found.
- */
-PJ_DECL(pjsip_transaction*) pjsip_endpt_find_tsx( pjsip_endpoint *endpt,
- const pj_str_t *key );
-
-/**
- * Set list of SIP proxies to be visited for all outbound request messages.
- * Application can call this function to specify how outgoing request messages
- * should be routed. For example, if outgoing requests should go through an
- * outbound proxy, then application can specify the URL of the proxy when
- * calling this function. More than one proxy can be specified, and the
- * order of which proxy is specified when calling this function specifies
- * the order of which proxy will be visited first by the request messages.
- *
- * @param endpt The endpoint instance.
- * @param url_cnt Number of proxies/URLs in the array.
- * @param url Array of proxy URL, which specifies the order of which
- * proxy will be visited first (e.g. url[0] will be visited
- * before url[1]).
- *
- * @return Zero on success.
- */
-PJ_DECL(pj_status_t) pjsip_endpt_set_proxies( pjsip_endpoint *endpt,
- int url_cnt, const pj_str_t url[]);
-
-/**
- * Get the list of "Route" header that are configured for this endpoint.
- * The "Route" header specifies how outbound request messages will be sent,
- * and is built when application sets the outbound proxy.
- *
- * @param endpt The endpoint instance.
- *
- * @return List of "Route" header.
- */
-PJ_DECL(const pjsip_route_hdr*) pjsip_endpt_get_routing( pjsip_endpoint *endpt );
-
-/**
- * Log an error.
- */
-PJ_DECL(void) pjsip_endpt_log_error( pjsip_endpoint *endpt,
- const char *sender,
- pj_status_t error_code,
- const char *format,
- ... );
-
-#define PJSIP_ENDPT_LOG_ERROR(expr) \
- pjsip_endpt_log_error expr
-
-#define PJSIP_ENDPT_TRACE(tracing,expr) \
- do { \
- if ((tracing)) \
- PJ_LOG(4,expr); \
- } while (0)
-
-/**
- * @}
- */
-
-/*
- * Internal functions.
- */
-/*
- * Receive transaction events from transactions and put in the event queue
- * to be processed later.
- */
-void pjsip_endpt_send_tsx_event( pjsip_endpoint *endpt, pjsip_event *evt );
-
-PJ_END_DECL
-
-#endif /* __PJSIP_SIP_ENDPOINT_H__ */
-
+/* $Id$ */
+/*
+ * Copyright (C) 2003-2006 Benny Prijono <benny@prijono.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#ifndef __PJSIP_SIP_ENDPOINT_H__
+#define __PJSIP_SIP_ENDPOINT_H__
+
+/**
+ * @file sip_endpoint.h
+ * @brief SIP Endpoint.
+ */
+
+#include <pjsip/sip_transport.h>
+#include <pjsip/sip_resolve.h>
+
+PJ_BEGIN_DECL
+
+/**
+ * @defgroup PJSIP SIP Stack Core
+ * Implementation of core SIP protocol stack processing.
+ */
+
+/**
+ * @defgroup PJSIP_ENDPT SIP Endpoint
+ * @ingroup PJSIP
+ * @brief
+ * Representation of SIP node instance.
+ *
+ * SIP Endpoint instance (pjsip_endpoint) can be viewed as the master/owner of
+ * all SIP objects in an application. It performs the following roles:
+ * - it manages the allocation/deallocation of memory pools for all objects.
+ * - it manages listeners and transports, and how they are used by
+ * transactions.
+ * - it owns transaction hash table.
+ * - it receives incoming messages from transport layer and automatically
+ * dispatches them to the correct transaction (or create a new one).
+ * - it has a single instance of timer management (timer heap).
+ * - it manages modules, which is the primary means of extending the library.
+ * - it provides single polling function for all objects and distributes
+ * events.
+ * - it provides SIP policy such as which outbound proxy to use for all
+ * outgoing SIP request messages.
+ * - it automatically handles incoming requests which can not be handled by
+ * existing modules (such as when incoming request has unsupported method).
+ * - and so on..
+ *
+ * Theoritically application can have multiple instances of SIP endpoint,
+ * although it's not clear why application may want to do it.
+ *
+ * @{
+ */
+
+/**
+ * Create an instance of SIP endpoint from the specified pool factory.
+ * The pool factory reference then will be kept by the endpoint, so that
+ * future memory allocations by SIP components will be taken from the same
+ * pool factory.
+ *
+ * @param pf Pool factory that will be used for the lifetime of
+ * endpoint.
+ * @param name Optional name to be specified for the endpoint.
+ * If this parameter is NULL, then the name will use
+ * local host name.
+ * @param endpt Pointer to receive endpoint instance.
+ *
+ * @return PJ_SUCCESS on success.
+ */
+PJ_DECL(pj_status_t) pjsip_endpt_create(pj_pool_factory *pf,
+ const char *name,
+ pjsip_endpoint **endpt);
+
+/**
+ * Destroy endpoint instance. Application must make sure that all pending
+ * transactions have been terminated properly, because this function does not
+ * check for the presence of pending transactions.
+ *
+ * @param endpt The SIP endpoint to be destroyed.
+ */
+PJ_DECL(void) pjsip_endpt_destroy(pjsip_endpoint *endpt);
+
+/**
+ * Get endpoint name.
+ *
+ * @param endpt The SIP endpoint instance.
+ *
+ * @return Endpoint name, as was registered during endpoint
+ * creation. The string is NULL terminated.
+ */
+PJ_DECL(const pj_str_t*) pjsip_endpt_name(const pjsip_endpoint *endpt);
+
+/**
+ * Poll for events. Application must call this function periodically to ensure
+ * that all events from both transports and timer heap are handled in timely
+ * manner. This function, like all other endpoint functions, is thread safe,
+ * and application may have more than one thread concurrently calling this function.
+ *
+ * @param endpt The endpoint.
+ * @param max_timeout Maximum time to wait for events, or NULL to wait forever
+ * until event is received.
+ */
+PJ_DECL(void) pjsip_endpt_handle_events( pjsip_endpoint *endpt,
+ const pj_time_val *max_timeout);
+
+/**
+ * Dump endpoint status to the log. This will print the status to the log
+ * with log level 3.
+ *
+ * @param endpt The endpoint.
+ * @param detail If non zero, then it will dump a detailed output.
+ * BEWARE that this option may crash the system because
+ * it tries to access all memory pools.
+ */
+PJ_DECL(void) pjsip_endpt_dump( pjsip_endpoint *endpt, pj_bool_t detail );
+
+/**
+ * Create pool from the endpoint. All SIP components should allocate their
+ * memory pool by calling this function, to make sure that the pools are
+ * allocated from the same pool factory. This function, like all other endpoint
+ * functions, is thread safe.
+ *
+ * @param endpt The SIP endpoint.
+ * @param pool_name Name to be assigned to the pool.
+ * @param initial The initial size of the pool.
+ * @param increment The resize size.
+ * @return Memory pool, or NULL on failure.
+ *
+ * @see pj_pool_create
+ */
+PJ_DECL(pj_pool_t*) pjsip_endpt_create_pool( pjsip_endpoint *endpt,
+ const char *pool_name,
+ pj_size_t initial,
+ pj_size_t increment );
+
+/**
+ * Return back pool to endpoint to be released back to the pool factory.
+ * This function, like all other endpoint functions, is thread safe.
+ *
+ * @param endpt The endpoint.
+ * @param pool The pool to be destroyed.
+ */
+PJ_DECL(void) pjsip_endpt_destroy_pool( pjsip_endpoint *endpt,
+ pj_pool_t *pool );
+
+/**
+ * Schedule timer to endpoint's timer heap. Application must poll the endpoint
+ * periodically (by calling #pjsip_endpt_handle_events) to ensure that the
+ * timer events are handled in timely manner. When the timeout for the timer
+ * has elapsed, the callback specified in the entry argument will be called.
+ * This function, like all other endpoint functions, is thread safe.
+ *
+ * @param endpt The endpoint.
+ * @param entry The timer entry.
+ * @param delay The relative delay of the timer.
+ * @return PJ_OK (zero) if successfull.
+ */
+PJ_DECL(pj_status_t) pjsip_endpt_schedule_timer( pjsip_endpoint *endpt,
+ pj_timer_entry *entry,
+ const pj_time_val *delay );
+
+/**
+ * Cancel the previously registered timer.
+ * This function, like all other endpoint functions, is thread safe.
+ *
+ * @param endpt The endpoint.
+ * @param entry The timer entry previously registered.
+ */
+PJ_DECL(void) pjsip_endpt_cancel_timer( pjsip_endpoint *endpt,
+ pj_timer_entry *entry );
+
+/**
+ * Create a new transaction. After creating the transaction, application MUST
+ * initialize the transaction as either UAC or UAS (by calling
+ * #pjsip_tsx_init_uac or #pjsip_tsx_init_uas), then must register the
+ * transaction to endpoint with #pjsip_endpt_register_tsx.
+ * This function, like all other endpoint functions, is thread safe.
+ *
+ * @param endpt The SIP endpoint.
+ * @param p_tsx Pointer to receive the transaction.
+ *
+ * @return PJ_SUCCESS or the appropriate error code.
+ */
+PJ_DECL(pj_status_t) pjsip_endpt_create_tsx(pjsip_endpoint *endpt,
+ pjsip_transaction **p_tsx);
+
+/**
+ * Register the transaction to the endpoint's transaction table.
+ * Before the transaction is registered, it must have been initialized as
+ * either UAS or UAC by calling #pjsip_tsx_init_uac or #pjsip_tsx_init_uas.
+ * This function, like all other endpoint functions, is thread safe.
+ *
+ * @param endpt The SIP endpoint.
+ * @param tsx The transaction.
+ */
+PJ_DECL(void) pjsip_endpt_register_tsx( pjsip_endpoint *endpt,
+ pjsip_transaction *tsx);
+
+/**
+ * Forcefull destroy the transaction.
+ * The only time where application needs to call this function is when the
+ * transaction fails to initialize in #pjsip_tsx_init_uac or
+ * #pjsip_tsx_init_uas. For other cases. the transaction will be destroyed
+ * automaticly by endpoint.
+ *
+ * @param endpt The endpoint.
+ * @param tsx The transaction to destroy.
+ */
+PJ_DECL(void) pjsip_endpt_destroy_tsx( pjsip_endpoint *endpt,
+ pjsip_transaction *tsx);
+
+/**
+ * Create a new transmit data buffer.
+ * This function, like all other endpoint functions, is thread safe.
+ *
+ * @param endpt The endpoint.
+ * @param p_tdata Pointer to receive transmit data buffer.
+ *
+ * @return PJ_SUCCESS or the appropriate error code.
+ */
+PJ_DECL(pj_status_t) pjsip_endpt_create_tdata( pjsip_endpoint *endpt,
+ pjsip_tx_data **p_tdata);
+
+/**
+ * Asynchronously resolve a SIP target host or domain according to rule
+ * specified in RFC 3263 (Locating SIP Servers). When the resolving operation
+ * has completed, the callback will be called.
+ *
+ * Note: at the moment we don't have implementation of RFC 3263 yet!
+ *
+ * @param resolver The resolver engine.
+ * @param pool The pool to allocate resolver job.
+ * @param target The target specification to be resolved.
+ * @param token A user defined token to be passed back to callback function.
+ * @param cb The callback function.
+ */
+PJ_DECL(void) pjsip_endpt_resolve( pjsip_endpoint *endpt,
+ pj_pool_t *pool,
+ pjsip_host_port *target,
+ void *token,
+ pjsip_resolver_callback *cb);
+
+/**
+ * Get transport manager instance.
+ *
+ * @param endpt The endpoint.
+ *
+ * @return Transport manager instance.
+ */
+PJ_DECL(pjsip_tpmgr*) pjsip_endpt_get_tpmgr(pjsip_endpoint *endpt);
+
+/**
+ * Get ioqueue instance.
+ *
+ * @param endpt The endpoint.
+ *
+ * @return The ioqueue.
+ */
+PJ_DECL(pj_ioqueue_t*) pjsip_endpt_get_ioqueue(pjsip_endpoint *endpt);
+
+/**
+ * Find a SIP transport suitable for sending SIP message to the specified
+ * address. This function will complete asynchronously when the transport is
+ * ready (for example, when TCP socket is connected), and when it completes,
+ * the callback will be called with the status of the operation.
+ *
+ * @see pjsip_transport_get
+ */
+PJ_DECL(pj_status_t) pjsip_endpt_alloc_transport( pjsip_endpoint *endpt,
+ pjsip_transport_type_e type,
+ const pj_sockaddr_in *remote,
+ pjsip_transport **p_transport);
+
+/**
+ * Get additional headers to be put in outgoing request message.
+ * This function is normally called by transaction layer when sending outgoing
+ * requests.
+ *
+ * @param endpt The endpoint.
+ *
+ * @return List of additional headers to be put in outgoing requests.
+ */
+PJ_DECL(const pjsip_hdr*) pjsip_endpt_get_request_headers(pjsip_endpoint *endpt);
+
+/**
+ * Get "Allow" header from endpoint. The endpoint builds the "Allow" header
+ * from the list of methods supported by modules.
+ *
+ * @param endpt The endpoint.
+ *
+ * @return "Allow" header, or NULL if endpoint doesn't have "Allow" header.
+ */
+PJ_DECL(const pjsip_allow_hdr*) pjsip_endpt_get_allow_hdr( pjsip_endpoint *endpt );
+
+
+/**
+ * Find transaction in endpoint's transaction table by the transaction's key.
+ * This function normally is only used by modules. The key for a transaction
+ * can be created by calling #pjsip_tsx_create_key.
+ *
+ * @param endpt The endpoint instance.
+ * @param key Transaction key, as created with #pjsip_tsx_create_key.
+ *
+ * @return The transaction, or NULL if it's not found.
+ */
+PJ_DECL(pjsip_transaction*) pjsip_endpt_find_tsx( pjsip_endpoint *endpt,
+ const pj_str_t *key );
+
+/**
+ * Set list of SIP proxies to be visited for all outbound request messages.
+ * Application can call this function to specify how outgoing request messages
+ * should be routed. For example, if outgoing requests should go through an
+ * outbound proxy, then application can specify the URL of the proxy when
+ * calling this function. More than one proxy can be specified, and the
+ * order of which proxy is specified when calling this function specifies
+ * the order of which proxy will be visited first by the request messages.
+ *
+ * @param endpt The endpoint instance.
+ * @param url_cnt Number of proxies/URLs in the array.
+ * @param url Array of proxy URL, which specifies the order of which
+ * proxy will be visited first (e.g. url[0] will be visited
+ * before url[1]).
+ *
+ * @return Zero on success.
+ */
+PJ_DECL(pj_status_t) pjsip_endpt_set_proxies( pjsip_endpoint *endpt,
+ int url_cnt, const pj_str_t url[]);
+
+/**
+ * Get the list of "Route" header that are configured for this endpoint.
+ * The "Route" header specifies how outbound request messages will be sent,
+ * and is built when application sets the outbound proxy.
+ *
+ * @param endpt The endpoint instance.
+ *
+ * @return List of "Route" header.
+ */
+PJ_DECL(const pjsip_route_hdr*) pjsip_endpt_get_routing( pjsip_endpoint *endpt );
+
+/**
+ * Log an error.
+ */
+PJ_DECL(void) pjsip_endpt_log_error( pjsip_endpoint *endpt,
+ const char *sender,
+ pj_status_t error_code,
+ const char *format,
+ ... );
+
+#define PJSIP_ENDPT_LOG_ERROR(expr) \
+ pjsip_endpt_log_error expr
+
+#define PJSIP_ENDPT_TRACE(tracing,expr) \
+ do { \
+ if ((tracing)) \
+ PJ_LOG(4,expr); \
+ } while (0)
+
+/**
+ * @}
+ */
+
+/*
+ * Internal functions.
+ */
+/*
+ * Receive transaction events from transactions and put in the event queue
+ * to be processed later.
+ */
+void pjsip_endpt_send_tsx_event( pjsip_endpoint *endpt, pjsip_event *evt );
+
+PJ_END_DECL
+
+#endif /* __PJSIP_SIP_ENDPOINT_H__ */
+
diff --git a/pjsip/include/pjsip/sip_errno.h b/pjsip/include/pjsip/sip_errno.h
index 2c8e6764..eefda673 100644
--- a/pjsip/include/pjsip/sip_errno.h
+++ b/pjsip/include/pjsip/sip_errno.h
@@ -1,228 +1,228 @@
-/* $Id$ */
-/*
- * Copyright (C) 2003-2006 Benny Prijono <benny@prijono.org>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
-#ifndef __PJSIP_SIP_ERRNO_H__
-#define __PJSIP_SIP_ERRNO_H__
-
-#include <pj/errno.h>
-
-PJ_BEGIN_DECL
-
-/*
- * PJSIP error codes occupies 170000 - 219000, and mapped as follows:
- * - 170100 - 170799: mapped to SIP status code in response msg.
- * - 171000 - 171999: mapped to errors generated from PJSIP core.
- */
-
-/**
- * Get error message for the specified error code.
- *
- * @param status The error code.
- * @param buffer The buffer where to put the error message.
- * @param bufsize Size of the buffer.
- *
- * @return The error message as NULL terminated string,
- * wrapped with pj_str_t.
- */
-PJ_DECL(pj_str_t) pjsip_strerror( pj_status_t status, char *buffer,
- pj_size_t bufsize);
-
-/**
- * Start of error code relative to PJ_ERRNO_START_USER.
- */
-#define PJSIP_ERRNO_START (PJ_ERRNO_START_USER)
-
-/**
- * Create error value from SIP status code.
- * @param code SIP status code.
- * @return Error code in pj_status_t namespace.
- */
-#define PJSIP_ERRNO_FROM_SIP_STATUS(code) (PJSIP_ERRNO_START+code)
-
-/**
- * Get SIP status code from error value.
- * If conversion to SIP status code is not available, a SIP status code
- * 599 will be returned.
- *
- * @param status Error code in pj_status_t namespace.
- * @return SIP status code.
- */
-#define PJSIP_ERRNO_TO_SIP_STATUS(status) \
- ((status>=PJSIP_ERRNO_FROM_SIP_STATUS(100) && \
- status<PJSIP_ERRNO_FROM_SIP_STATUS(800)) ? \
- status-PJSIP_ERRNO_FROM_SIP_STATUS(0) : 599)
-
-
-/**
- * Start of PJSIP generated error code values.
- */
-#define PJSIP_ERRNO_START_PJSIP (PJSIP_ERRNO_START + 1000)
-
-/************************************************************
- * GENERIC SIP ERRORS
- ***********************************************************/
-/**
- * @hideinitializer
- * SIP object is busy.
- */
-#define PJSIP_EBUSY (PJSIP_ERRNO_START_PJSIP + 1) /* 171001 */
-/**
- * @hideinitializer
- * SIP object with the same type already exists.
- */
-#define PJSIP_ETYPEEXISTS (PJSIP_ERRNO_START_PJSIP + 2) /* 171002 */
-
-
-/************************************************************
- * MESSAGING ERRORS
- ***********************************************************/
-/**
- * @hideinitializer
- * Invalid message (syntax error)
- */
-#define PJSIP_EINVALIDMSG (PJSIP_ERRNO_START_PJSIP + 20) /* 171020 */
-/**
- * @hideinitializer
- * Unsupported URL scheme.
- */
-#define PJSIP_EINVALIDSCHEME (PJSIP_ERRNO_START_PJSIP + 21) /* 171021 */
-/**
- * @hideinitializer
- * Message too long. See also PJSIP_ERXOVERFLOW.
- */
-#define PJSIP_EMSGTOOLONG (PJSIP_ERRNO_START_PJSIP + 22) /* 171022 */
-/**
- * @hideinitializer
- * Message not completely received.
- */
-#define PJSIP_EPARTIALMSG (PJSIP_ERRNO_START_PJSIP + 23) /* 171023 */
-/**
- * @hideinitializer
- * Missing required header(s).
- */
-#define PJSIP_EMISSINGHDR (PJSIP_ERRNO_START_PJSIP + 24) /* 171024 */
-/**
- * @hideinitializer
- * Invalid Via header in response (sent-by, etc).
- */
-#define PJSIP_EINVALIDVIA (PJSIP_ERRNO_START_PJSIP + 25) /* 171025 */
-/**
- * @hideinitializer
- * Multiple Via headers in response.
- */
-#define PJSIP_EMULTIPLEVIA (PJSIP_ERRNO_START_PJSIP + 26) /* 171026 */
-
-/************************************************************
- * TRANSPORT ERRORS
- ***********************************************************/
-/**
- * @hideinitializer
- * Unsupported transport type.
- */
-#define PJSIP_EUNSUPTRANSPORT (PJSIP_ERRNO_START_PJSIP + 40) /* 171040 */
-/**
- * @hideinitializer
- * Buffer is being sent, operation still pending.
- */
-#define PJSIP_EPENDINGTX (PJSIP_ERRNO_START_PJSIP + 41) /* 171041 */
-/**
- * @hideinitializer
- * Rx buffer overflow. See also PJSIP_EMSGTOOLONG.
- */
-#define PJSIP_ERXOVERFLOW (PJSIP_ERRNO_START_PJSIP + 42)/* 171042 */
-
-
-/************************************************************
- * TRANSACTION ERRORS
- ***********************************************************/
-/**
- * @hideinitializer
- * Transaction has just been destroyed.
- */
-#define PJSIP_ETSXDESTROYED (PJSIP_ERRNO_START_PJSIP + 60) /* 171060 */
-
-
-/************************************************************
- * URI COMPARISON RESULTS
- ***********************************************************/
-/**
- * @hideinitializer
- * Scheme mismatch.
- */
-#define PJSIP_ECMPSCHEME (PJSIP_ERRNO_START_PJSIP + 80) /* 171080 */
-/**
- * @hideinitializer
- * User part mismatch.
- */
-#define PJSIP_ECMPUSER (PJSIP_ERRNO_START_PJSIP + 81) /* 171081 */
-/**
- * @hideinitializer
- * Password part mismatch.
- */
-#define PJSIP_ECMPPASSWD (PJSIP_ERRNO_START_PJSIP + 82) /* 171082 */
-/**
- * @hideinitializer
- * Host part mismatch.
- */
-#define PJSIP_ECMPHOST (PJSIP_ERRNO_START_PJSIP + 83) /* 171083 */
-/**
- * @hideinitializer
- * Port part mismatch.
- */
-#define PJSIP_ECMPPORT (PJSIP_ERRNO_START_PJSIP + 84) /* 171084 */
-/**
- * @hideinitializer
- * Transport parameter part mismatch.
- */
-#define PJSIP_ECMPTRANSPORTPRM (PJSIP_ERRNO_START_PJSIP + 85) /* 171085 */
-/**
- * @hideinitializer
- * TTL parameter part mismatch.
- */
-#define PJSIP_ECMPTTLPARAM (PJSIP_ERRNO_START_PJSIP + 86) /* 171086 */
-/**
- * @hideinitializer
- * User parameter part mismatch.
- */
-#define PJSIP_ECMPUSERPARAM (PJSIP_ERRNO_START_PJSIP + 87) /* 171087 */
-/**
- * @hideinitializer
- * Method parameter part mismatch.
- */
-#define PJSIP_ECMPMETHODPARAM (PJSIP_ERRNO_START_PJSIP + 88) /* 171088 */
-/**
- * @hideinitializer
- * Maddr parameter part mismatch.
- */
-#define PJSIP_ECMPMADDRPARAM (PJSIP_ERRNO_START_PJSIP + 89) /* 171089 */
-/**
- * @hideinitializer
- * Parameter part in other_param mismatch.
- */
-#define PJSIP_ECMPOTHERPARAM (PJSIP_ERRNO_START_PJSIP + 90) /* 171090 */
-/**
- * @hideinitializer
- * Parameter part in header_param mismatch.
- */
-#define PJSIP_ECMPHEADERPARAM (PJSIP_ERRNO_START_PJSIP + 91) /* 171091 */
-
-
-
-PJ_END_DECL
-
-#endif /* __PJSIP_SIP_ERRNO_H__ */
+/* $Id$ */
+/*
+ * Copyright (C) 2003-2006 Benny Prijono <benny@prijono.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#ifndef __PJSIP_SIP_ERRNO_H__
+#define __PJSIP_SIP_ERRNO_H__
+
+#include <pj/errno.h>
+
+PJ_BEGIN_DECL
+
+/*
+ * PJSIP error codes occupies 170000 - 219000, and mapped as follows:
+ * - 170100 - 170799: mapped to SIP status code in response msg.
+ * - 171000 - 171999: mapped to errors generated from PJSIP core.
+ */
+
+/**
+ * Get error message for the specified error code.
+ *
+ * @param status The error code.
+ * @param buffer The buffer where to put the error message.
+ * @param bufsize Size of the buffer.
+ *
+ * @return The error message as NULL terminated string,
+ * wrapped with pj_str_t.
+ */
+PJ_DECL(pj_str_t) pjsip_strerror( pj_status_t status, char *buffer,
+ pj_size_t bufsize);
+
+/**
+ * Start of error code relative to PJ_ERRNO_START_USER.
+ */
+#define PJSIP_ERRNO_START (PJ_ERRNO_START_USER)
+
+/**
+ * Create error value from SIP status code.
+ * @param code SIP status code.
+ * @return Error code in pj_status_t namespace.
+ */
+#define PJSIP_ERRNO_FROM_SIP_STATUS(code) (PJSIP_ERRNO_START+code)
+
+/**
+ * Get SIP status code from error value.
+ * If conversion to SIP status code is not available, a SIP status code
+ * 599 will be returned.
+ *
+ * @param status Error code in pj_status_t namespace.
+ * @return SIP status code.
+ */
+#define PJSIP_ERRNO_TO_SIP_STATUS(status) \
+ ((status>=PJSIP_ERRNO_FROM_SIP_STATUS(100) && \
+ status<PJSIP_ERRNO_FROM_SIP_STATUS(800)) ? \
+ status-PJSIP_ERRNO_FROM_SIP_STATUS(0) : 599)
+
+
+/**
+ * Start of PJSIP generated error code values.
+ */
+#define PJSIP_ERRNO_START_PJSIP (PJSIP_ERRNO_START + 1000)
+
+/************************************************************
+ * GENERIC SIP ERRORS
+ ***********************************************************/
+/**
+ * @hideinitializer
+ * SIP object is busy.
+ */
+#define PJSIP_EBUSY (PJSIP_ERRNO_START_PJSIP + 1) /* 171001 */
+/**
+ * @hideinitializer
+ * SIP object with the same type already exists.
+ */
+#define PJSIP_ETYPEEXISTS (PJSIP_ERRNO_START_PJSIP + 2) /* 171002 */
+
+
+/************************************************************
+ * MESSAGING ERRORS
+ ***********************************************************/
+/**
+ * @hideinitializer
+ * Invalid message (syntax error)
+ */
+#define PJSIP_EINVALIDMSG (PJSIP_ERRNO_START_PJSIP + 20) /* 171020 */
+/**
+ * @hideinitializer
+ * Unsupported URL scheme.
+ */
+#define PJSIP_EINVALIDSCHEME (PJSIP_ERRNO_START_PJSIP + 21) /* 171021 */
+/**
+ * @hideinitializer
+ * Message too long. See also PJSIP_ERXOVERFLOW.
+ */
+#define PJSIP_EMSGTOOLONG (PJSIP_ERRNO_START_PJSIP + 22) /* 171022 */
+/**
+ * @hideinitializer
+ * Message not completely received.
+ */
+#define PJSIP_EPARTIALMSG (PJSIP_ERRNO_START_PJSIP + 23) /* 171023 */
+/**
+ * @hideinitializer
+ * Missing required header(s).
+ */
+#define PJSIP_EMISSINGHDR (PJSIP_ERRNO_START_PJSIP + 24) /* 171024 */
+/**
+ * @hideinitializer
+ * Invalid Via header in response (sent-by, etc).
+ */
+#define PJSIP_EINVALIDVIA (PJSIP_ERRNO_START_PJSIP + 25) /* 171025 */
+/**
+ * @hideinitializer
+ * Multiple Via headers in response.
+ */
+#define PJSIP_EMULTIPLEVIA (PJSIP_ERRNO_START_PJSIP + 26) /* 171026 */
+
+/************************************************************
+ * TRANSPORT ERRORS
+ ***********************************************************/
+/**
+ * @hideinitializer
+ * Unsupported transport type.
+ */
+#define PJSIP_EUNSUPTRANSPORT (PJSIP_ERRNO_START_PJSIP + 40) /* 171040 */
+/**
+ * @hideinitializer
+ * Buffer is being sent, operation still pending.
+ */
+#define PJSIP_EPENDINGTX (PJSIP_ERRNO_START_PJSIP + 41) /* 171041 */
+/**
+ * @hideinitializer
+ * Rx buffer overflow. See also PJSIP_EMSGTOOLONG.
+ */
+#define PJSIP_ERXOVERFLOW (PJSIP_ERRNO_START_PJSIP + 42)/* 171042 */
+
+
+/************************************************************
+ * TRANSACTION ERRORS
+ ***********************************************************/
+/**
+ * @hideinitializer
+ * Transaction has just been destroyed.
+ */
+#define PJSIP_ETSXDESTROYED (PJSIP_ERRNO_START_PJSIP + 60) /* 171060 */
+
+
+/************************************************************
+ * URI COMPARISON RESULTS
+ ***********************************************************/
+/**
+ * @hideinitializer
+ * Scheme mismatch.
+ */
+#define PJSIP_ECMPSCHEME (PJSIP_ERRNO_START_PJSIP + 80) /* 171080 */
+/**
+ * @hideinitializer
+ * User part mismatch.
+ */
+#define PJSIP_ECMPUSER (PJSIP_ERRNO_START_PJSIP + 81) /* 171081 */
+/**
+ * @hideinitializer
+ * Password part mismatch.
+ */
+#define PJSIP_ECMPPASSWD (PJSIP_ERRNO_START_PJSIP + 82) /* 171082 */
+/**
+ * @hideinitializer
+ * Host part mismatch.
+ */
+#define PJSIP_ECMPHOST (PJSIP_ERRNO_START_PJSIP + 83) /* 171083 */
+/**
+ * @hideinitializer
+ * Port part mismatch.
+ */
+#define PJSIP_ECMPPORT (PJSIP_ERRNO_START_PJSIP + 84) /* 171084 */
+/**
+ * @hideinitializer
+ * Transport parameter part mismatch.
+ */
+#define PJSIP_ECMPTRANSPORTPRM (PJSIP_ERRNO_START_PJSIP + 85) /* 171085 */
+/**
+ * @hideinitializer
+ * TTL parameter part mismatch.
+ */
+#define PJSIP_ECMPTTLPARAM (PJSIP_ERRNO_START_PJSIP + 86) /* 171086 */
+/**
+ * @hideinitializer
+ * User parameter part mismatch.
+ */
+#define PJSIP_ECMPUSERPARAM (PJSIP_ERRNO_START_PJSIP + 87) /* 171087 */
+/**
+ * @hideinitializer
+ * Method parameter part mismatch.
+ */
+#define PJSIP_ECMPMETHODPARAM (PJSIP_ERRNO_START_PJSIP + 88) /* 171088 */
+/**
+ * @hideinitializer
+ * Maddr parameter part mismatch.
+ */
+#define PJSIP_ECMPMADDRPARAM (PJSIP_ERRNO_START_PJSIP + 89) /* 171089 */
+/**
+ * @hideinitializer
+ * Parameter part in other_param mismatch.
+ */
+#define PJSIP_ECMPOTHERPARAM (PJSIP_ERRNO_START_PJSIP + 90) /* 171090 */
+/**
+ * @hideinitializer
+ * Parameter part in header_param mismatch.
+ */
+#define PJSIP_ECMPHEADERPARAM (PJSIP_ERRNO_START_PJSIP + 91) /* 171091 */
+
+
+
+PJ_END_DECL
+
+#endif /* __PJSIP_SIP_ERRNO_H__ */
diff --git a/pjsip/include/pjsip/sip_event.h b/pjsip/include/pjsip/sip_event.h
index e828ae96..22b00727 100644
--- a/pjsip/include/pjsip/sip_event.h
+++ b/pjsip/include/pjsip/sip_event.h
@@ -1,310 +1,310 @@
-/* $Id$ */
-/*
- * Copyright (C) 2003-2006 Benny Prijono <benny@prijono.org>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
-#ifndef __PJSIP_SIP_EVENT_H__
-#define __PJSIP_SIP_EVENT_H__
-
-/**
- * @file sip_event.h
- * @brief SIP Event
- */
-
-PJ_BEGIN_DECL
-
-/**
- * @defgroup PJSIP_EVENT SIP Event
- * @ingroup PJSIP
- * @{
- */
-#include <pj/types.h>
-
-
-/**
- * Event IDs.
- */
-typedef enum pjsip_event_id_e
-{
- /** Unidentified event. */
- PJSIP_EVENT_UNKNOWN,
-
- /** Timer event, normally only used internally in transaction. */
- PJSIP_EVENT_TIMER,
-
- /** Message transmission event. */
- PJSIP_EVENT_TX_MSG,
-
- /** Message received event. */
- PJSIP_EVENT_RX_MSG,
-
- /** Transport error event. */
- PJSIP_EVENT_TRANSPORT_ERROR,
-
- /** Transaction state changed event. */
- PJSIP_EVENT_TSX_STATE,
-
- /** 2xx response received event. */
- PJSIP_EVENT_RX_200_MSG,
-
- /** ACK request received event. */
- PJSIP_EVENT_RX_ACK_MSG,
-
- /** Message discarded event. */
- PJSIP_EVENT_DISCARD_MSG,
-
- /** Indicates that the event was triggered by user action. */
- PJSIP_EVENT_USER,
-
- /** On before transmitting message. */
- PJSIP_EVENT_PRE_TX_MSG,
-
-} pjsip_event_id_e;
-
-
-/**
- * \struct
- * \brief Event descriptor to fully identify a SIP event.
- *
- * Events are the only way for a lower layer object to inform something
- * to higher layer objects. Normally this is achieved by means of callback,
- * i.e. the higher layer objects register a callback to handle the event on
- * the lower layer objects.
- *
- * This event descriptor is used for example by transactions, to inform
- * endpoint about events, and by transports, to inform endpoint about
- * unexpected transport error.
- */
-struct pjsip_event
-{
- /** This is necessary so that we can put events as a list. */
- PJ_DECL_LIST_MEMBER(struct pjsip_event);
-
- /** The event type, can be any value of \b pjsip_event_id_e.
- */
- pjsip_event_id_e type;
-
- /*
- * The event body.
- * By convention, the first member of each struct in the union must be
- * the pointer which is relevant to the event.
- */
- union
- {
- /** Timer event. */
- struct
- {
- pj_timer_entry *entry; /**< The timer entry. */
- } timer;
-
- /** Transaction state has changed event. */
- struct
- {
- union
- {
- pjsip_rx_data *rdata; /**< The incoming message. */
- pjsip_tx_data *tdata; /**< The outgoing message. */
- pj_timer_entry *timer; /**< The timer. */
- pj_status_t status;/**< Transport error status. */
- void *data; /**< Generic data. */
- } src;
- pjsip_transaction *tsx; /**< The transaction. */
- pjsip_event_id_e type; /**< Type of event source:
- * - PJSIP_EVENT_TX_MSG
- * - PJSIP_EVENT_RX_MSG,
- * - PJSIP_EVENT_TRANSPORT_ERROR
- * - PJSIP_EVENT_TIMER
- * - PJSIP_EVENT_USER
- */
- } tsx_state;
-
- /** Message transmission event. */
- struct
- {
- pjsip_tx_data *tdata; /**< The transmit data buffer. */
- pjsip_transaction *tsx; /**< The transaction. */
-
- } tx_msg;
-
- /** Pre-transmission event. */
- struct
- {
- pjsip_tx_data *tdata; /**< Msg to be transmitted. */
- pjsip_transaction *tsx; /**< The transaction. */
- int retcnt;/**< Retransmission count. */
- } pre_tx_msg;
-
- /** Transmission error event. */
- struct
- {
- pjsip_tx_data *tdata; /**< The transmit data. */
- pjsip_transaction *tsx; /**< The transaction. */
- } tx_error;
-
- /** Message arrival event. */
- struct
- {
- pjsip_rx_data *rdata; /**< The receive data buffer. */
- pjsip_transaction *tsx; /**< The transaction. */
- } rx_msg;
-
- /** Receipt of 200/INVITE response. */
- struct
- {
- pjsip_rx_data *rdata; /**< The 200 response msg. */
- } rx_200_msg;
-
- /** Receipt of ACK message. */
- struct
- {
- pjsip_rx_data *rdata; /**< The ack message. */
- } rx_ack_msg;
-
- /** Notification that endpoint has discarded a message. */
- struct
- {
- pjsip_rx_data *rdata; /**< The discarded message. */
- pj_status_t reason;/**< The reason. */
- } discard_msg;
-
- /** User event. */
- struct
- {
- void *user1; /**< User data 1. */
- void *user2; /**< User data 2. */
- void *user3; /**< User data 3. */
- void *user4; /**< User data 4. */
- } user;
-
- } body;
-};
-
-/**
- * Init timer event.
- */
-#define PJSIP_EVENT_INIT_TIMER(event,pentry) \
- do { \
- (event).type = PJSIP_EVENT_TIMER; \
- (event).body.timer.entry = pentry; \
- } while (0)
-
-/**
- * Init tsx state event.
- */
-#define PJSIP_EVENT_INIT_TSX_STATE(event,ptsx,ptype,pdata) \
- do { \
- (event).type = PJSIP_EVENT_TSX_STATE; \
- (event).body.tsx_state.tsx = ptsx; \
- (event).body.tsx_state.type = ptype; \
- (event).body.tsx_state.src.data = pdata; \
- } while (0)
-
-/**
- * Init tx msg event.
- */
-#define PJSIP_EVENT_INIT_TX_MSG(event,ptsx,ptdata) \
- do { \
- (event).type = PJSIP_EVENT_TX_MSG; \
- (event).body.tx_msg.tsx = ptsx; \
- (event).body.tx_msg.tdata = ptdata; \
- } while (0)
-
-/**
- * Init rx msg event.
- */
-#define PJSIP_EVENT_INIT_RX_MSG(event,ptsx,prdata) \
- do { \
- (event).type = PJSIP_EVENT_RX_MSG; \
- (event).body.rx_msg.tsx = ptsx; \
- (event).body.rx_msg.rdata = prdata; \
- } while (0)
-
-/**
- * Init transport error event.
- */
-#define PJSIP_EVENT_INIT_TRANSPORT_ERROR(event,ptsx,ptdata) \
- do { \
- (event).type = PJSIP_EVENT_TRANSPORT_ERROR; \
- (event).body.tx_error.tsx = ptsx; \
- (event).body.tx_error.tdata = ptdata; \
- } while (0)
-
-/**
- * Init rx 200/INVITE event.
- */
-#define PJSIP_EVENT_INIT_RX_200_MSG(event,prdata) \
- do { \
- (event).type = PJSIP_EVENT_RX_200_MSG; \
- (event).body.rx_200_msg.rdata = prdata; \
- } while (0)
-
-/**
- * Init rx ack msg event.
- */
-#define PJSIP_EVENT_INIT_RX_ACK_MSG(event,prdata) \
- do { \
- (event).type = PJSIP_EVENT_RX_ACK_MSG; \
- (event).body.rx_ack_msg.rdata = prdata; \
- } while (0)
-
-/**
- * Init discard msg event.
- */
-#define PJSIP_EVENT_INIT_DISCARD_MSG(event,prdata,preason) \
- do { \
- (event).type = PJSIP_EVENT_DISCARD_MSG; \
- (event).body.discard_msg.rdata = prdata; \
- (event).body.discard_msg.reason = preason; \
- } while (0)
-
-/**
- * Init user event.
- */
-#define PJSIP_EVENT_INIT_USER(event,u1,u2,u3,u4) \
- do { \
- (event).type = PJSIP_EVENT_USER; \
- (event).body.user.user1 = (void*)u1; \
- (event).body.user.user2 = (void*)u2; \
- (event).body.user.user3 = (void*)u3; \
- (event).body.user.user4 = (void*)u4; \
- } while (0)
-
-/**
- * Init pre tx msg event.
- */
-#define PJSIP_EVENT_INIT_PRE_TX_MSG(event,ptsx,ptdata,pretcnt) \
- do { \
- (event).type = PJSIP_EVENT_PRE_TX_MSG; \
- (event).body.pre_tx_msg.tsx = ptsx; \
- (event).body.pre_tx_msg.tdata = ptdata; \
- (event).body.pre_tx_msg.retcnt = pretcnt; \
- } while (0)
-
-
-/**
- * Get the event string from the event ID.
- * @param e the event ID.
- * @notes defined in sip_util.c
- */
-PJ_DEF(const char *) pjsip_event_str(pjsip_event_id_e e);
-
-/**
- * @}
- */
-
-PJ_END_DECL
-
-#endif /* __PJSIP_SIP_EVENT_H__ */
+/* $Id$ */
+/*
+ * Copyright (C) 2003-2006 Benny Prijono <benny@prijono.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#ifndef __PJSIP_SIP_EVENT_H__
+#define __PJSIP_SIP_EVENT_H__
+
+/**
+ * @file sip_event.h
+ * @brief SIP Event
+ */
+
+PJ_BEGIN_DECL
+
+/**
+ * @defgroup PJSIP_EVENT SIP Event
+ * @ingroup PJSIP
+ * @{
+ */
+#include <pj/types.h>
+
+
+/**
+ * Event IDs.
+ */
+typedef enum pjsip_event_id_e
+{
+ /** Unidentified event. */
+ PJSIP_EVENT_UNKNOWN,
+
+ /** Timer event, normally only used internally in transaction. */
+ PJSIP_EVENT_TIMER,
+
+ /** Message transmission event. */
+ PJSIP_EVENT_TX_MSG,
+
+ /** Message received event. */
+ PJSIP_EVENT_RX_MSG,
+
+ /** Transport error event. */
+ PJSIP_EVENT_TRANSPORT_ERROR,
+
+ /** Transaction state changed event. */
+ PJSIP_EVENT_TSX_STATE,
+
+ /** 2xx response received event. */
+ PJSIP_EVENT_RX_200_MSG,
+
+ /** ACK request received event. */
+ PJSIP_EVENT_RX_ACK_MSG,
+
+ /** Message discarded event. */
+ PJSIP_EVENT_DISCARD_MSG,
+
+ /** Indicates that the event was triggered by user action. */
+ PJSIP_EVENT_USER,
+
+ /** On before transmitting message. */
+ PJSIP_EVENT_PRE_TX_MSG,
+
+} pjsip_event_id_e;
+
+
+/**
+ * \struct
+ * \brief Event descriptor to fully identify a SIP event.
+ *
+ * Events are the only way for a lower layer object to inform something
+ * to higher layer objects. Normally this is achieved by means of callback,
+ * i.e. the higher layer objects register a callback to handle the event on
+ * the lower layer objects.
+ *
+ * This event descriptor is used for example by transactions, to inform
+ * endpoint about events, and by transports, to inform endpoint about
+ * unexpected transport error.
+ */
+struct pjsip_event
+{
+ /** This is necessary so that we can put events as a list. */
+ PJ_DECL_LIST_MEMBER(struct pjsip_event);
+
+ /** The event type, can be any value of \b pjsip_event_id_e.
+ */
+ pjsip_event_id_e type;
+
+ /*
+ * The event body.
+ * By convention, the first member of each struct in the union must be
+ * the pointer which is relevant to the event.
+ */
+ union
+ {
+ /** Timer event. */
+ struct
+ {
+ pj_timer_entry *entry; /**< The timer entry. */
+ } timer;
+
+ /** Transaction state has changed event. */
+ struct
+ {
+ union
+ {
+ pjsip_rx_data *rdata; /**< The incoming message. */
+ pjsip_tx_data *tdata; /**< The outgoing message. */
+ pj_timer_entry *timer; /**< The timer. */
+ pj_status_t status;/**< Transport error status. */
+ void *data; /**< Generic data. */
+ } src;
+ pjsip_transaction *tsx; /**< The transaction. */
+ pjsip_event_id_e type; /**< Type of event source:
+ * - PJSIP_EVENT_TX_MSG
+ * - PJSIP_EVENT_RX_MSG,
+ * - PJSIP_EVENT_TRANSPORT_ERROR
+ * - PJSIP_EVENT_TIMER
+ * - PJSIP_EVENT_USER
+ */
+ } tsx_state;
+
+ /** Message transmission event. */
+ struct
+ {
+ pjsip_tx_data *tdata; /**< The transmit data buffer. */
+ pjsip_transaction *tsx; /**< The transaction. */
+
+ } tx_msg;
+
+ /** Pre-transmission event. */
+ struct
+ {
+ pjsip_tx_data *tdata; /**< Msg to be transmitted. */
+ pjsip_transaction *tsx; /**< The transaction. */
+ int retcnt;/**< Retransmission count. */
+ } pre_tx_msg;
+
+ /** Transmission error event. */
+ struct
+ {
+ pjsip_tx_data *tdata; /**< The transmit data. */
+ pjsip_transaction *tsx; /**< The transaction. */
+ } tx_error;
+
+ /** Message arrival event. */
+ struct
+ {
+ pjsip_rx_data *rdata; /**< The receive data buffer. */
+ pjsip_transaction *tsx; /**< The transaction. */
+ } rx_msg;
+
+ /** Receipt of 200/INVITE response. */
+ struct
+ {
+ pjsip_rx_data *rdata; /**< The 200 response msg. */
+ } rx_200_msg;
+
+ /** Receipt of ACK message. */
+ struct
+ {
+ pjsip_rx_data *rdata; /**< The ack message. */
+ } rx_ack_msg;
+
+ /** Notification that endpoint has discarded a message. */
+ struct
+ {
+ pjsip_rx_data *rdata; /**< The discarded message. */
+ pj_status_t reason;/**< The reason. */
+ } discard_msg;
+
+ /** User event. */
+ struct
+ {
+ void *user1; /**< User data 1. */
+ void *user2; /**< User data 2. */
+ void *user3; /**< User data 3. */
+ void *user4; /**< User data 4. */
+ } user;
+
+ } body;
+};
+
+/**
+ * Init timer event.
+ */
+#define PJSIP_EVENT_INIT_TIMER(event,pentry) \
+ do { \
+ (event).type = PJSIP_EVENT_TIMER; \
+ (event).body.timer.entry = pentry; \
+ } while (0)
+
+/**
+ * Init tsx state event.
+ */
+#define PJSIP_EVENT_INIT_TSX_STATE(event,ptsx,ptype,pdata) \
+ do { \
+ (event).type = PJSIP_EVENT_TSX_STATE; \
+ (event).body.tsx_state.tsx = ptsx; \
+ (event).body.tsx_state.type = ptype; \
+ (event).body.tsx_state.src.data = pdata; \
+ } while (0)
+
+/**
+ * Init tx msg event.
+ */
+#define PJSIP_EVENT_INIT_TX_MSG(event,ptsx,ptdata) \
+ do { \
+ (event).type = PJSIP_EVENT_TX_MSG; \
+ (event).body.tx_msg.tsx = ptsx; \
+ (event).body.tx_msg.tdata = ptdata; \
+ } while (0)
+
+/**
+ * Init rx msg event.
+ */
+#define PJSIP_EVENT_INIT_RX_MSG(event,ptsx,prdata) \
+ do { \
+ (event).type = PJSIP_EVENT_RX_MSG; \
+ (event).body.rx_msg.tsx = ptsx; \
+ (event).body.rx_msg.rdata = prdata; \
+ } while (0)
+
+/**
+ * Init transport error event.
+ */
+#define PJSIP_EVENT_INIT_TRANSPORT_ERROR(event,ptsx,ptdata) \
+ do { \
+ (event).type = PJSIP_EVENT_TRANSPORT_ERROR; \
+ (event).body.tx_error.tsx = ptsx; \
+ (event).body.tx_error.tdata = ptdata; \
+ } while (0)
+
+/**
+ * Init rx 200/INVITE event.
+ */
+#define PJSIP_EVENT_INIT_RX_200_MSG(event,prdata) \
+ do { \
+ (event).type = PJSIP_EVENT_RX_200_MSG; \
+ (event).body.rx_200_msg.rdata = prdata; \
+ } while (0)
+
+/**
+ * Init rx ack msg event.
+ */
+#define PJSIP_EVENT_INIT_RX_ACK_MSG(event,prdata) \
+ do { \
+ (event).type = PJSIP_EVENT_RX_ACK_MSG; \
+ (event).body.rx_ack_msg.rdata = prdata; \
+ } while (0)
+
+/**
+ * Init discard msg event.
+ */
+#define PJSIP_EVENT_INIT_DISCARD_MSG(event,prdata,preason) \
+ do { \
+ (event).type = PJSIP_EVENT_DISCARD_MSG; \
+ (event).body.discard_msg.rdata = prdata; \
+ (event).body.discard_msg.reason = preason; \
+ } while (0)
+
+/**
+ * Init user event.
+ */
+#define PJSIP_EVENT_INIT_USER(event,u1,u2,u3,u4) \
+ do { \
+ (event).type = PJSIP_EVENT_USER; \
+ (event).body.user.user1 = (void*)u1; \
+ (event).body.user.user2 = (void*)u2; \
+ (event).body.user.user3 = (void*)u3; \
+ (event).body.user.user4 = (void*)u4; \
+ } while (0)
+
+/**
+ * Init pre tx msg event.
+ */
+#define PJSIP_EVENT_INIT_PRE_TX_MSG(event,ptsx,ptdata,pretcnt) \
+ do { \
+ (event).type = PJSIP_EVENT_PRE_TX_MSG; \
+ (event).body.pre_tx_msg.tsx = ptsx; \
+ (event).body.pre_tx_msg.tdata = ptdata; \
+ (event).body.pre_tx_msg.retcnt = pretcnt; \
+ } while (0)
+
+
+/**
+ * Get the event string from the event ID.
+ * @param e the event ID.
+ * @notes defined in sip_util.c
+ */
+PJ_DEF(const char *) pjsip_event_str(pjsip_event_id_e e);
+
+/**
+ * @}
+ */
+
+PJ_END_DECL
+
+#endif /* __PJSIP_SIP_EVENT_H__ */
diff --git a/pjsip/include/pjsip/sip_module.h b/pjsip/include/pjsip/sip_module.h
index bd3bcd87..345be88a 100644
--- a/pjsip/include/pjsip/sip_module.h
+++ b/pjsip/include/pjsip/sip_module.h
@@ -1,140 +1,140 @@
-/* $Id$ */
-/*
- * Copyright (C) 2003-2006 Benny Prijono <benny@prijono.org>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
-#ifndef __PJSIP_SIP_MODULE_H__
-#define __PJSIP_SIP_MODULE_H__
-
-/**
- * @file sip_module.h
- * @brief Module helpers
- */
-#include <pjsip/sip_types.h>
-
-PJ_BEGIN_DECL
-
-/**
- * @defgroup PJSIP_MOD SIP Modules
- * @ingroup PJSIP
- * @{
- */
-
-/**
- * Module registration structure, which is passed by the module to the
- * endpoint during the module registration process. This structure enables
- * the endpoint to query the module capability and to further communicate
- * with the module.
- */
-struct pjsip_module
-{
- /**
- * Module name.
- */
- pj_str_t name;
-
- /**
- * Flag to indicate the type of interfaces supported by the module.
- */
- pj_uint32_t flag;
-
- /**
- * Integer number to identify module initialization and start order with
- * regard to other modules. Higher number will make the module gets
- * initialized later.
- */
- pj_uint32_t priority;
-
- /**
- * Opaque data which can be used by a module to identify a resource within
- * the module itself.
- */
- void *mod_data;
-
- /**
- * Number of methods supported by this module.
- */
- int method_cnt;
-
- /**
- * Array of methods supported by this module.
- */
- const pjsip_method *methods[8];
-
- /**
- * Pointer to function to be called to initialize the module.
- *
- * @param endpt The endpoint instance.
- * @param mod The module.
- * @param id The unique module ID assigned to this module.
- *
- * @return Module should return zero when initialization succeed.
- */
- pj_status_t (*init_module)(pjsip_endpoint *endpt,
- struct pjsip_module *mod, pj_uint32_t id);
-
- /**
- * Pointer to function to be called to start the module.
- *
- * @param mod The module.
- *
- * @return Module should return zero to indicate success.
- */
- pj_status_t (*start_module)(struct pjsip_module *mod);
-
- /**
- * Pointer to function to be called to deinitialize the module before
- * it is unloaded.
- *
- * @param mod The module.
- *
- * @return Module should return zero to indicate success.
- */
- pj_status_t (*deinit_module)(struct pjsip_module *mod);
-
- /**
- * Pointer to function to receive transaction related events.
- * If the module doesn't wish to receive such notification, this member
- * must be set to NULL.
- *
- * @param mod The module.
- * @param event The transaction event.
- */
- void (*tsx_handler)(struct pjsip_module *mod, pjsip_event *event);
-};
-
-
-/**
- * Prototype of function to register static modules (eg modules that are
- * linked staticly with the application). This function must be implemented
- * by any applications that use PJSIP library.
- *
- * @param count [input/output] On input, it contains the maximum number of
- * elements in the array. On output, the function fills with
- * the number of modules to be registered.
- * @param modules [output] array of pointer to modules to be registered.
- */
-pj_status_t register_static_modules( pj_size_t *count,
- pjsip_module **modules );
-
-/**
- * @}
- */
-
-PJ_END_DECL
-
-#endif /* __PJSIP_SIP_MODULE_H__ */
-
+/* $Id$ */
+/*
+ * Copyright (C) 2003-2006 Benny Prijono <benny@prijono.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#ifndef __PJSIP_SIP_MODULE_H__
+#define __PJSIP_SIP_MODULE_H__
+
+/**
+ * @file sip_module.h
+ * @brief Module helpers
+ */
+#include <pjsip/sip_types.h>
+
+PJ_BEGIN_DECL
+
+/**
+ * @defgroup PJSIP_MOD SIP Modules
+ * @ingroup PJSIP
+ * @{
+ */
+
+/**
+ * Module registration structure, which is passed by the module to the
+ * endpoint during the module registration process. This structure enables
+ * the endpoint to query the module capability and to further communicate
+ * with the module.
+ */
+struct pjsip_module
+{
+ /**
+ * Module name.
+ */
+ pj_str_t name;
+
+ /**
+ * Flag to indicate the type of interfaces supported by the module.
+ */
+ pj_uint32_t flag;
+
+ /**
+ * Integer number to identify module initialization and start order with
+ * regard to other modules. Higher number will make the module gets
+ * initialized later.
+ */
+ pj_uint32_t priority;
+
+ /**
+ * Opaque data which can be used by a module to identify a resource within
+ * the module itself.
+ */
+ void *mod_data;
+
+ /**
+ * Number of methods supported by this module.
+ */
+ int method_cnt;
+
+ /**
+ * Array of methods supported by this module.
+ */
+ const pjsip_method *methods[8];
+
+ /**
+ * Pointer to function to be called to initialize the module.
+ *
+ * @param endpt The endpoint instance.
+ * @param mod The module.
+ * @param id The unique module ID assigned to this module.
+ *
+ * @return Module should return zero when initialization succeed.
+ */
+ pj_status_t (*init_module)(pjsip_endpoint *endpt,
+ struct pjsip_module *mod, pj_uint32_t id);
+
+ /**
+ * Pointer to function to be called to start the module.
+ *
+ * @param mod The module.
+ *
+ * @return Module should return zero to indicate success.
+ */
+ pj_status_t (*start_module)(struct pjsip_module *mod);
+
+ /**
+ * Pointer to function to be called to deinitialize the module before
+ * it is unloaded.
+ *
+ * @param mod The module.
+ *
+ * @return Module should return zero to indicate success.
+ */
+ pj_status_t (*deinit_module)(struct pjsip_module *mod);
+
+ /**
+ * Pointer to function to receive transaction related events.
+ * If the module doesn't wish to receive such notification, this member
+ * must be set to NULL.
+ *
+ * @param mod The module.
+ * @param event The transaction event.
+ */
+ void (*tsx_handler)(struct pjsip_module *mod, pjsip_event *event);
+};
+
+
+/**
+ * Prototype of function to register static modules (eg modules that are
+ * linked staticly with the application). This function must be implemented
+ * by any applications that use PJSIP library.
+ *
+ * @param count [input/output] On input, it contains the maximum number of
+ * elements in the array. On output, the function fills with
+ * the number of modules to be registered.
+ * @param modules [output] array of pointer to modules to be registered.
+ */
+pj_status_t register_static_modules( pj_size_t *count,
+ pjsip_module **modules );
+
+/**
+ * @}
+ */
+
+PJ_END_DECL
+
+#endif /* __PJSIP_SIP_MODULE_H__ */
+
diff --git a/pjsip/include/pjsip/sip_msg.h b/pjsip/include/pjsip/sip_msg.h
index a547bfa2..836ce37b 100644
--- a/pjsip/include/pjsip/sip_msg.h
+++ b/pjsip/include/pjsip/sip_msg.h
@@ -1,1527 +1,1527 @@
-/* $Id$ */
-/*
- * Copyright (C) 2003-2006 Benny Prijono <benny@prijono.org>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
-#ifndef __PJSIP_SIP_MSG_H__
-#define __PJSIP_SIP_MSG_H__
-
-/**
- * @file sip_msg.h
- * @brief SIP Message Structure.
- */
-
-#include <pjsip/sip_types.h>
-#include <pjsip/sip_uri.h>
-#include <pj/list.h>
-
-PJ_BEGIN_DECL
-
-/**
- * @defgroup PJSIP_MSG SIP Message Structure
- * @ingroup PJSIP
- * @{
- */
-
-///////////////////////////////////////////////////////////////////////////////
-/**
- * @defgroup PJSIP_MSG_METHOD Methods
- * @brief Method names and manipulation.
- * @ingroup PJSIP_MSG
- * @{
- */
-
-/**
- * This enumeration declares SIP methods as described by RFC3261. Additional
- * methods do exist, and they are described by corresponding RFCs for the SIP
- * extentensions. Since they won't alter the characteristic of the processing
- * of the message, they don't need to be explicitly mentioned here.
- */
-typedef enum pjsip_method_e
-{
- /** INVITE method, for establishing dialogs. */
- PJSIP_INVITE_METHOD,
-
- /** CANCEL method, for cancelling request. */
- PJSIP_CANCEL_METHOD,
-
- /** ACK method, for acknowledging final response to INVITE. */
- PJSIP_ACK_METHOD,
-
- /** BYE method, for terminating dialog. */
- PJSIP_BYE_METHOD,
-
- /** REGISTER method. */
- PJSIP_REGISTER_METHOD,
-
- /** OPTIONS method, for querying remote capabilities. */
- PJSIP_OPTIONS_METHOD,
-
- /** Other method, which means that the method name itself will be stored
- elsewhere. */
- PJSIP_OTHER_METHOD,
-
-} pjsip_method_e;
-
-
-
-/**
- * This structure represents a SIP method.
- * Application must always use either #pjsip_method_init or #pjsip_method_set
- * to make sure that method name is initialized correctly. This way, the name
- * member will always contain a valid method string regardless whether the ID
- * is recognized or not.
- */
-typedef struct pjsip_method
-{
- pjsip_method_e id; /**< Method ID, from \a pjsip_method_e. */
- pj_str_t name; /**< Method name, which will always contain the
- method string. */
-} pjsip_method;
-
-
-/**
- * Initialize the method structure from a string.
- * This function will check whether the method is a known method then set
- * both the id and name accordingly.
- *
- * @param m The method to initialize.
- * @param pool Pool where memory allocation will be allocated from, if required.
- * @param str The method string.
- */
-PJ_DECL(void) pjsip_method_init( pjsip_method *m,
- pj_pool_t *pool,
- const pj_str_t *str);
-
-/**
- * Initialize the method structure from a string, without cloning the string.
- * See #pjsip_method_init.
- *
- * @param m The method structure to be initialized.
- * @param str The method string.
- */
-PJ_DECL(void) pjsip_method_init_np( pjsip_method *m,
- pj_str_t *str);
-
-/**
- * Set the method with the predefined method ID.
- * This function will also set the name member of the structure to the correct
- * string according to the method.
- *
- * @param m The method structure.
- * @param id The method ID.
- */
-PJ_DECL(void) pjsip_method_set( pjsip_method *m, pjsip_method_e id );
-
-
-/**
- * Copy one method structure to another. If the method is of the known methods,
- * then memory allocation is not required.
- *
- * @param pool Pool to allocate memory from, if required.
- * @param method The destination method to copy to.
- * @param rhs The source method to copy from.
- */
-PJ_DECL(void) pjsip_method_copy( pj_pool_t *pool,
- pjsip_method *method,
- const pjsip_method *rhs );
-
-/**
- * Compare one method with another, and conveniently determine whether the
- * first method is equal, less than, or greater than the second method.
- *
- * @param m1 The first method.
- * @param m2 The second method.
- *
- * @return Zero if equal, otherwise will return -1 if less or +1 if greater.
- */
-PJ_DECL(int) pjsip_method_cmp( const pjsip_method *m1, const pjsip_method *m2);
-
-/**
- * @}
- */
-
-///////////////////////////////////////////////////////////////////////////////
-/**
- * @defgroup PJSIP_MSG_HDR Header Fields General Structure.
- * @brief General Header Fields Structure.
- * @ingroup PJSIP_MSG
- * @{
- */
-
-/**
- * Header types, as defined by RFC3261.
- */
-typedef enum pjsip_hdr_e
-{
- /*
- * These are the headers documented in RFC3261. Headers not documented
- * there must have type PJSIP_H_OTHER, and the header type itself is
- * recorded in the header name string.
- *
- * DO NOT CHANGE THE VALUE/ORDER OF THE HEADER IDs!!!.
- */
- PJSIP_H_ACCEPT,
- PJSIP_H_ACCEPT_ENCODING_UNIMP,
- PJSIP_H_ACCEPT_LANGUAGE_UNIMP,
- PJSIP_H_ALERT_INFO_UNIMP,
- PJSIP_H_ALLOW,
- PJSIP_H_AUTHENTICATION_INFO_UNIMP,
- PJSIP_H_AUTHORIZATION,
- PJSIP_H_CALL_ID,
- PJSIP_H_CALL_INFO_UNIMP,
- PJSIP_H_CONTACT,
- PJSIP_H_CONTENT_DISPOSITION_UNIMP,
- PJSIP_H_CONTENT_ENCODING_UNIMP,
- PJSIP_H_CONTENT_LANGUAGE_UNIMP,
- PJSIP_H_CONTENT_LENGTH,
- PJSIP_H_CONTENT_TYPE,
- PJSIP_H_CSEQ,
- PJSIP_H_DATE_UNIMP,
- PJSIP_H_ERROR_INFO_UNIMP,
- PJSIP_H_EXPIRES,
- PJSIP_H_FROM,
- PJSIP_H_IN_REPLY_TO_UNIMP,
- PJSIP_H_MAX_FORWARDS,
- PJSIP_H_MIME_VERSION_UNIMP,
- PJSIP_H_MIN_EXPIRES,
- PJSIP_H_ORGANIZATION_UNIMP,
- PJSIP_H_PRIORITY_UNIMP,
- PJSIP_H_PROXY_AUTHENTICATE,
- PJSIP_H_PROXY_AUTHORIZATION,
- PJSIP_H_PROXY_REQUIRE_UNIMP,
- PJSIP_H_RECORD_ROUTE,
- PJSIP_H_REPLY_TO_UNIMP,
- PJSIP_H_REQUIRE,
- PJSIP_H_RETRY_AFTER,
- PJSIP_H_ROUTE,
- PJSIP_H_SERVER_UNIMP,
- PJSIP_H_SUBJECT_UNIMP,
- PJSIP_H_SUPPORTED,
- PJSIP_H_TIMESTAMP_UNIMP,
- PJSIP_H_TO,
- PJSIP_H_UNSUPPORTED,
- PJSIP_H_USER_AGENT_UNIMP,
- PJSIP_H_VIA,
- PJSIP_H_WARNING_UNIMP,
- PJSIP_H_WWW_AUTHENTICATE,
-
- PJSIP_H_OTHER,
-
-} pjsip_hdr_e;
-
-/**
- * This structure provides the pointer to basic functions that are needed
- * for generic header operations. All header fields will have pointer to
- * this structure, so that they can be manipulated uniformly.
- */
-typedef struct pjsip_hdr_vptr
-{
- /**
- * Function to clone the header.
- *
- * @param pool Memory pool to allocate the new header.
- * @param hdr Header to clone.
- *
- * @return A new instance of the header.
- */
- void *(*clone)(pj_pool_t *pool, const void *hdr);
-
- /**
- * Pointer to function to shallow clone the header.
- * Shallow cloning will just make a memory copy of the original header,
- * thus all pointers in original header will be kept intact. Because the
- * function does not need to perform deep copy, the operation should be
- * faster, but the application must make sure that the original header
- * is still valid throughout the lifetime of new header.
- *
- * @param pool Memory pool to allocate the new header.
- * @param hdr The header to clone.
- */
- void *(*shallow_clone)(pj_pool_t *pool, const void *hdr);
-
- /** Pointer to function to print the header to the specified buffer.
- * Returns the length of string written, or -1 if the remaining buffer
- * is not enough to hold the header.
- *
- * @param hdr The header to print.
- * @param buf The buffer.
- * @param len The size of the buffer.
- *
- * @return The size copied to buffer, or -1 if there's not enough space.
- */
- int (*print_on)(void *hdr, char *buf, pj_size_t len);
-
-} pjsip_hdr_vptr;
-
-
-/**
- * Generic fields for all SIP headers are declared using this macro, to make
- * sure that all headers will have exactly the same layout in their start of
- * the storage. This behaves like C++ inheritance actually.
- */
-#define PJSIP_DECL_HDR_MEMBER(hdr) \
- /** List members. */ \
- PJ_DECL_LIST_MEMBER(hdr); \
- /** Header type */ \
- pjsip_hdr_e type; \
- /** Header name. */ \
- pj_str_t name; \
- /** Header short name version. */ \
- pj_str_t sname; \
- /** Virtual function table. */ \
- pjsip_hdr_vptr *vptr
-
-
-/**
- * Generic SIP header structure, for generic manipulation for headers in the
- * message. All header fields can be typecasted to this type.
- */
-struct pjsip_hdr
-{
- PJSIP_DECL_HDR_MEMBER(struct pjsip_hdr);
-};
-
-
-/**
- * This generic function will clone any header, by calling "clone" function
- * in header's virtual function table.
- *
- * @param pool The pool to allocate memory from.
- * @param hdr The header to clone.
- *
- * @return A new instance copied from the original header.
- */
-PJ_DECL(void*) pjsip_hdr_clone( pj_pool_t *pool, const void *hdr );
-
-
-/**
- * This generic function will clone any header, by calling "shallow_clone"
- * function in header's virtual function table.
- *
- * @param pool The pool to allocate memory from.
- * @param hdr The header to clone.
- *
- * @return A new instance copied from the original header.
- */
-PJ_DECL(void*) pjsip_hdr_shallow_clone( pj_pool_t *pool, const void *hdr );
-
-/**
- * This generic function will print any header, by calling "print"
- * function in header's virtual function table.
- *
- * @param hdr The header to print.
- * @param buf The buffer.
- * @param len The size of the buffer.
- *
- * @return The size copied to buffer, or -1 if there's not enough space.
- */
-PJ_DECL(int) pjsip_hdr_print_on( void *hdr, char *buf, pj_size_t len);
-
-/**
- * @}
- */
-
-///////////////////////////////////////////////////////////////////////////////
-/**
- * @defgroup PJSIP_MSG_LINE Request and Status Line.
- * @brief Request and status line structures and manipulation.
- * @ingroup PJSIP_MSG
- * @{
- */
-
-/**
- * This structure describes SIP request line.
- */
-typedef struct pjsip_request_line
-{
- pjsip_method method; /**< Method for this request line. */
- pjsip_uri *uri; /**< URI for this request line. */
-} pjsip_request_line;
-
-
-/**
- * This structure describes SIP status line.
- */
-typedef struct pjsip_status_line
-{
- int code; /**< Status code. */
- pj_str_t reason; /**< Reason string. */
-} pjsip_status_line;
-
-
-/**
- * This enumeration lists standard SIP status codes according to RFC 3261.
- * In addition, it also declares new status class 7xx for errors generated
- * by the stack. This status class however should not get transmitted on the
- * wire.
- */
-typedef enum pjsip_status_code
-{
- PJSIP_SC_TRYING = 100,
- PJSIP_SC_RINGING = 180,
- PJSIP_SC_CALL_BEING_FORWARDED = 181,
- PJSIP_SC_QUEUED = 182,
- PJSIP_SC_PROGRESS = 183,
-
- PJSIP_SC_OK = 200,
-
- PJSIP_SC_MULTIPLE_CHOICES = 300,
- PJSIP_SC_MOVED_PERMANENTLY = 301,
- PJSIP_SC_MOVED_TEMPORARILY = 302,
- PJSIP_SC_USE_PROXY = 305,
- PJSIP_SC_ALTERNATIVE_SERVICE = 380,
-
- PJSIP_SC_BAD_REQUEST = 400,
- PJSIP_SC_UNAUTHORIZED = 401,
- PJSIP_SC_PAYMENT_REQUIRED = 402,
- PJSIP_SC_FORBIDDEN = 403,
- PJSIP_SC_NOT_FOUND = 404,
- PJSIP_SC_METHOD_NOT_ALLOWED = 405,
- PJSIP_SC_NOT_ACCEPTABLE = 406,
- PJSIP_SC_PROXY_AUTHENTICATION_REQUIRED = 407,
- PJSIP_SC_REQUEST_TIMEOUT = 408,
- PJSIP_SC_GONE = 410,
- PJSIP_SC_REQUEST_ENTITY_TOO_LARGE = 413,
- PJSIP_SC_REQUEST_URI_TOO_LONG = 414,
- PJSIP_SC_UNSUPPORTED_MEDIA_TYPE = 415,
- PJSIP_SC_UNSUPPORTED_URI_SCHEME = 416,
- PJSIP_SC_BAD_EXTENSION = 420,
- PJSIP_SC_EXTENSION_REQUIRED = 421,
- PJSIP_SC_INTERVAL_TOO_BRIEF = 423,
- PJSIP_SC_TEMPORARILY_UNAVAILABLE = 480,
- PJSIP_SC_CALL_TSX_DOES_NOT_EXIST = 481,
- PJSIP_SC_LOOP_DETECTED = 482,
- PJSIP_SC_TOO_MANY_HOPS = 483,
- PJSIP_SC_ADDRESS_INCOMPLETE = 484,
- PJSIP_AC_AMBIGUOUS = 485,
- PJSIP_SC_BUSY_HERE = 486,
- PJSIP_SC_REQUEST_TERMINATED = 487,
- PJSIP_SC_NOT_ACCEPTABLE_HERE = 488,
- PJSIP_SC_REQUEST_PENDING = 491,
- PJSIP_SC_UNDECIPHERABLE = 493,
-
- PJSIP_SC_INTERNAL_SERVER_ERROR = 500,
- PJSIP_SC_NOT_IMPLEMENTED = 501,
- PJSIP_SC_BAD_GATEWAY = 502,
- PJSIP_SC_SERVICE_UNAVAILABLE = 503,
- PJSIP_SC_SERVER_TIMEOUT = 504,
- PJSIP_SC_VERSION_NOT_SUPPORTED = 505,
- PJSIP_SC_MESSAGE_TOO_LARGE = 513,
-
- PJSIP_SC_BUSY_EVERYWHERE = 600,
- PJSIP_SC_DECLINE = 603,
- PJSIP_SC_DOES_NOT_EXIST_ANYWHERE = 604,
- PJSIP_SC_NOT_ACCEPTABLE_ANYWHERE = 606,
-
- PJSIP_SC_TSX_TIMEOUT = 701,
- PJSIP_SC_TSX_RESOLVE_ERROR = 702,
- PJSIP_SC_TSX_TRANSPORT_ERROR = 703,
-
-} pjsip_status_code;
-
-/**
- * Get the default status text for the status code.
- *
- * @param status_code SIP Status Code
- *
- * @return textual message for the status code.
- */
-PJ_DECL(const pj_str_t*) pjsip_get_status_text(int status_code);
-
-/**
- * This macro returns non-zero (TRUE) if the specified status_code is
- * in the same class as the code_class.
- *
- * @param status_code The status code.
- * @param code_class The status code in the class (for example 100, 200).
- */
-#define PJSIP_IS_STATUS_IN_CLASS(status_code, code_class) \
- (status_code/100 == code_class/100)
-
-/**
- * @}
- */
-
-///////////////////////////////////////////////////////////////////////////////
-/**
- * @addtogroup PJSIP_MSG_MEDIA Media Type
- * @brief Media type definitions and manipulations.
- * @ingroup PJSIP_MSG
- * @{
- */
-
-/**
- * This structure describes SIP media type, as used for example in
- * Accept and Content-Type header..
- */
-typedef struct pjsip_media_type
-{
- pj_str_t type; /**< Media type. */
- pj_str_t subtype; /**< Media subtype. */
- pj_str_t param; /**< Media type parameters (concatenated). */
-} pjsip_media_type;
-
-/**
- * @}
- */
-
-///////////////////////////////////////////////////////////////////////////////
-/**
- * @addtogroup PJSIP_MSG_BODY Message Body
- * @brief SIP message body structures and manipulation.
- * @ingroup PJSIP_MSG
- * @{
- */
-
-/**
- * Generic abstraction to message body.
- * When an incoming message is parsed (pjsip_parse_msg()), the parser fills in
- * all members with the appropriate value. The 'data' and 'len' member will
- * describe portion of incoming packet which denotes the message body.
- * When application needs to attach message body to outgoing SIP message, it
- * must fill in all members of this structure.
- */
-typedef struct pjsip_msg_body
-{
- /** MIME content type.
- * For incoming messages, the parser will fill in this member with the
- * content type found in Content-Type header.
- *
- * For outgoing messages, application must fill in this member with
- * appropriate value, because the stack will generate Content-Type header
- * based on the value specified here.
- */
- pjsip_media_type content_type;
-
- /** Pointer to buffer which holds the message body data.
- * For incoming messages, the parser will fill in this member with the
- * pointer to the body string.
- *
- * When sending outgoing message, this member doesn't need to point to the
- * actual message body string. It can be assigned with arbitrary pointer,
- * because the value will only need to be understood by the print_body()
- * function. The stack itself will not try to interpret this value, but
- * instead will always call the print_body() whenever it needs to get the
- * actual body string.
- */
- void *data;
-
- /** The length of the data.
- * For incoming messages, the parser will fill in this member with the
- * actual length of message body.
- *
- * When sending outgoing message, again just like the "data" member, the
- * "len" member doesn't need to point to the actual length of the body
- * string.
- */
- unsigned len;
-
- /** Pointer to function to print this message body.
- * Application must set a proper function here when sending outgoing
- * message.
- *
- * @param msg_body This structure itself.
- * @param buf The buffer.
- * @param size The buffer size.
- *
- * @return The length of the string printed, or -1 if there is
- * not enough space in the buffer to print the whole
- * message body.
- */
- int (*print_body)(struct pjsip_msg_body *msg_body,
- char *buf, pj_size_t size);
-
-} pjsip_msg_body;
-
-/**
- * General purpose function to textual data in a SIP body. Attach this function
- * in a SIP message body only if the data in pjsip_msg_body is a textual
- * message ready to be embedded in a SIP message. If the data in the message
- * body is not a textual body, then application must supply a custom function
- * to print that body.
- *
- * @param msg_body The message body.
- * @param buf Buffer to copy the message body to.
- * @param size The size of the buffer.
- *
- * @return The length copied to the buffer, or -1.
- */
-PJ_DECL(int) pjsip_print_text_body( pjsip_msg_body *msg_body,
- char *buf, pj_size_t size);
-
-/**
- * @}
- */
-
-///////////////////////////////////////////////////////////////////////////////
-/**
- * @defgroup PJSIP_MSG_MSG Message Structure
- * @brief Message structure and operations.
- * @ingroup PJSIP_MSG
- * @{
- */
-
-/**
- * Message type (request or response).
- */
-typedef enum pjsip_msg_type_e
-{
- PJSIP_REQUEST_MSG, /**< Indicates request message. */
- PJSIP_RESPONSE_MSG, /**< Indicates response message. */
-} pjsip_msg_type_e;
-
-
-/**
- * This structure describes a SIP message.
- */
-struct pjsip_msg
-{
- /** Message type (ie request or response). */
- pjsip_msg_type_e type;
-
- /** The first line of the message can be either request line for request
- * messages, or status line for response messages. It is represented here
- * as a union.
- */
- union
- {
- /** Request Line. */
- struct pjsip_request_line req;
-
- /** Status Line. */
- struct pjsip_status_line status;
- } line;
-
- /** List of message headers. */
- pjsip_hdr hdr;
-
- /** Pointer to message body, or NULL if no message body is attached to
- * this mesage.
- */
- pjsip_msg_body *body;
-};
-
-
-/**
- * Create new request or response message.
- *
- * @param pool The pool.
- * @param type Message type.
- * @return New message, or THROW exception if failed.
- */
-PJ_DECL(pjsip_msg*) pjsip_msg_create( pj_pool_t *pool, pjsip_msg_type_e type);
-
-/**
- * Find a header in the message by the header type.
- *
- * @param msg The message.
- * @param type The header type to find.
- * @param start The first header field where the search should begin.
- * If NULL is specified, then the search will begin from the
- * first header, otherwise the search will begin at the
- * specified header.
- *
- * @return The header field, or NULL if no header with the specified
- * type is found.
- */
-PJ_DECL(void*) pjsip_msg_find_hdr( pjsip_msg *msg,
- pjsip_hdr_e type, void *start);
-
-/**
- * Find a header in the message by its name.
- *
- * @param msg The message.
- * @param name The header name to find.
- * @param start The first header field where the search should begin.
- * If NULL is specified, then the search will begin from the
- * first header, otherwise the search will begin at the
- * specified header.
- *
- * @return The header field, or NULL if no header with the specified
- * type is found.
- */
-PJ_DECL(void*) pjsip_msg_find_hdr_by_name( pjsip_msg *msg,
- const pj_str_t *name, void *start);
-
-/**
- * Find and remove a header in the message.
- *
- * @param msg The message.
- * @param hdr The header type to find.
- * @param start The first header field where the search should begin,
- * or NULL to search from the first header in the message.
- *
- * @return The header field, or NULL if not found.
- */
-PJ_DECL(void*) pjsip_msg_find_remove_hdr( pjsip_msg *msg,
- pjsip_hdr_e hdr, void *start);
-
-/**
- * Add a header to the message, putting it last in the header list.
- *
- * @param msg The message.
- * @param hdr The header to add.
- *
- * @bug Once the header is put in a list (or message), it can not be put in
- * other list (or message). Otherwise Real Bad Thing will happen.
- */
-PJ_IDECL(void) pjsip_msg_add_hdr( pjsip_msg *msg, pjsip_hdr *hdr );
-
-/**
- * Add header field to the message, putting it in the front of the header list.
- *
- * @param msg The message.
- * @param hdr The header to add.
- *
- * @bug Once the header is put in a list (or message), it can not be put in
- * other list (or message). Otherwise Real Bad Thing will happen.
- */
-PJ_IDECL(void) pjsip_msg_insert_first_hdr( pjsip_msg *msg, pjsip_hdr *hdr );
-
-/**
- * Print the message to the specified buffer.
- *
- * @param msg The message to print.
- * @param buf The buffer
- * @param size The size of the buffer.
- *
- * @return The length of the printed characters (in bytes), or NEGATIVE
- * value if the message is too large for the specified buffer.
- */
-PJ_DECL(pj_ssize_t) pjsip_msg_print(pjsip_msg *msg, char *buf, pj_size_t size);
-
-/**
- * @}
- */
-
-///////////////////////////////////////////////////////////////////////////////
-/**
- * @addtogroup PJSIP_MSG_HDR_GEN Header Field: Generic
- * @brief Generic header field which contains header name and value.
- * @ingroup PJSIP_MSG
- * @{
- */
-
-/**
- * Generic SIP header, which contains hname and a string hvalue.
- * Note that this header is not supposed to be used as 'base' class for headers.
- */
-typedef struct pjsip_generic_string_hdr
-{
- PJSIP_DECL_HDR_MEMBER(struct pjsip_generic_string_hdr); /**< Standard header field. */
- pj_str_t hvalue; /**< hvalue */
-} pjsip_generic_string_hdr;
-
-
-/**
- * Create a new instance of generic header. A generic header can have an
- * arbitrary header name.
- *
- * @param pool The pool.
- * @param hname The header name to be assigned to the header, or NULL to
- * assign the header name with some string.
- *
- * @return The header, or THROW exception.
- */
-PJ_DECL(pjsip_generic_string_hdr*) pjsip_generic_string_hdr_create( pj_pool_t *pool,
- const pj_str_t *hname );
-
-/**
- * Create a generic header along with the text content.
- *
- * @param pool The pool.
- * @param hname The header name.
- * @param hvalue The header text content.
- *
- * @return The header instance.
- */
-PJ_DECL(pjsip_generic_string_hdr*)
-pjsip_generic_string_hdr_create_with_text( pj_pool_t *pool,
- const pj_str_t *hname,
- const pj_str_t *hvalue);
-
-/**
- * @}
- */
-
-///////////////////////////////////////////////////////////////////////////////
-/**
- * @addtogroup PJSIP_MSG_HDR_GEN_INT Header Field: Generic Integer
- * @brief Generic header field which contains header name and value.
- * @ingroup PJSIP_MSG
- * @{
- */
-
-/**
- * Generic SIP header, which contains hname and a string hvalue.
- */
-typedef struct pjsip_generic_int_hdr
-{
- PJSIP_DECL_HDR_MEMBER(struct pjsip_generic_int_hdr); /**< Standard header field. */
- pj_int32_t ivalue; /**< ivalue */
-} pjsip_generic_int_hdr;
-
-
-/**
- * Create a new instance of generic header. A generic header can have an
- * arbitrary header name.
- *
- * @param pool The pool.
- * @param hname The header name to be assigned to the header, or NULL to
- * assign the header name with some string.
- *
- * @return The header, or THROW exception.
- */
-PJ_DECL(pjsip_generic_int_hdr*) pjsip_generic_int_hdr_create( pj_pool_t *pool,
- const pj_str_t *hname );
-
-/**
- * Create a generic header along with the value.
- *
- * @param pool The pool.
- * @param hname The header name.
- * @param value The header value content.
- *
- * @return The header instance.
- */
-PJ_DECL(pjsip_generic_int_hdr*)
-pjsip_generic_int_hdr_create_with_value( pj_pool_t *pool,
- const pj_str_t *hname,
- int value);
-
-/**
- * @}
- */
-
-///////////////////////////////////////////////////////////////////////////////
-/**
- * @defgroup PJSIP_MSG_HDR_GENERIC_LIST Header Field: Generic string list.
- * @brief Header with list of strings separated with comma
- * @ingroup PJSIP_MSG
- * @{
- */
-
-/** Maximum elements in the header array. */
-#define PJSIP_GENERIC_ARRAY_MAX_COUNT 32
-
-typedef struct pjsip_generic_array_hdr
-{
- PJSIP_DECL_HDR_MEMBER(struct pjsip_generic_array_hdr);
- unsigned count; /**< Number of elements. */
- pj_str_t values[PJSIP_GENERIC_ARRAY_MAX_COUNT]; /**< Elements. */
-} pjsip_generic_array_hdr;
-
-/**
- * Create generic array header.
- *
- * @param pool Pool to allocate memory from.
- *
- * @return New generic array header.
- */
-PJ_DECL(pjsip_generic_array_hdr*) pjsip_generic_array_create(pj_pool_t *pool,
- const pj_str_t *hnames);
-
-/**
- * @}
- */
-
-///////////////////////////////////////////////////////////////////////////////
-/**
- * @defgroup PJSIP_MSG_HDR_ACCEPT Header Field: Accept
- * @brief Accept header field.
- * @ingroup PJSIP_MSG
- * @{
- */
-/** Accept header. */
-typedef pjsip_generic_array_hdr pjsip_accept_hdr;
-
-/** Maximum fields in Accept header. */
-#define PJSIP_MAX_ACCEPT_COUNT PJSIP_GENERIC_ARRAY_MAX_COUNT
-
-/**
- * Create new Accept header instance.
- *
- * @param pool The pool.
- *
- * @return New Accept header instance.
- */
-PJ_DECL(pjsip_accept_hdr*) pjsip_accept_hdr_create(pj_pool_t *pool);
-
-
-/**
- * @}
- */
-
-///////////////////////////////////////////////////////////////////////////////
-/**
- * @defgroup PJSIP_MSG_HDR_ALLOW Header Field: Allow
- * @brief Allow header field.
- * @ingroup PJSIP_MSG
- * @{
- */
-typedef pjsip_generic_array_hdr pjsip_allow_hdr;
-
-/**
- * Create new Allow header instance.
- *
- * @param pool The pool.
- *
- * @return New Allow header instance.
- */
-PJ_DECL(pjsip_allow_hdr*) pjsip_allow_hdr_create(pj_pool_t *pool);
-
-
-/**
- * @}
- */
-
-///////////////////////////////////////////////////////////////////////////////
-/**
- * @defgroup PJSIP_MSG_HDR_CID Header Field: Call-ID
- * @brief Call-ID header field.
- * @ingroup PJSIP_MSG
- * @{
- */
-/**
- * Call-ID header.
- */
-typedef struct pjsip_cid_hdr
-{
- PJSIP_DECL_HDR_MEMBER(struct pjsip_cid_hdr);
- pj_str_t id; /**< Call-ID string. */
-} pjsip_cid_hdr;
-
-
-/**
- * Create new Call-ID header.
- *
- * @param pool The pool.
- *
- * @return new Call-ID header.
- */
-PJ_DECL(pjsip_cid_hdr*) pjsip_cid_hdr_create( pj_pool_t *pool );
-
-
-/**
- * @}
- */
-
-///////////////////////////////////////////////////////////////////////////////
-/**
- * @defgroup PJSIP_MSG_HDR_CLEN Header Field: Content-Length
- * @brief Content-Length header field.
- * @ingroup PJSIP_MSG
- * @{
- */
-/**
- * Content-Length header.
- */
-typedef struct pjsip_clen_hdr
-{
- PJSIP_DECL_HDR_MEMBER(struct pjsip_clen_hdr);
- int len; /**< Content length. */
-} pjsip_clen_hdr;
-
-/**
- * Create new Content-Length header.
- *
- * @param pool the pool.
- * @return A new Content-Length header instance.
- */
-PJ_DECL(pjsip_clen_hdr*) pjsip_clen_hdr_create( pj_pool_t *pool );
-
-/**
- * @}
- */
-
-///////////////////////////////////////////////////////////////////////////////
-/**
- * @defgroup PJSIP_MSG_HDR_CSEQ Header Field: CSeq
- * @brief CSeq header field.
- * @ingroup PJSIP_MSG
- * @{
- */
-/**
- * CSeq header.
- */
-typedef struct pjsip_cseq_hdr
-{
- PJSIP_DECL_HDR_MEMBER(struct pjsip_cseq_hdr);
- int cseq; /**< CSeq number. */
- pjsip_method method; /**< CSeq method. */
-} pjsip_cseq_hdr;
-
-
-/** Create new CSeq header.
- *
- * @param pool The pool.
- * @return A new CSeq header instance.
- */
-PJ_DECL(pjsip_cseq_hdr*) pjsip_cseq_hdr_create( pj_pool_t *pool );
-
-/**
- * @}
- */
-
-///////////////////////////////////////////////////////////////////////////////
-/**
- * @defgroup PJSIP_MSG_HDR_CONTACT Header Field: Contact
- * @brief Contact header field.
- * @ingroup PJSIP_MSG
- * @{
- */
-/**
- * Contact header.
- * In this library, contact header only contains single URI. If a message has
- * multiple URI in the Contact header, the URI will be put in separate Contact
- * headers.
- */
-typedef struct pjsip_contact_hdr
-{
- PJSIP_DECL_HDR_MEMBER(struct pjsip_contact_hdr);
- int star; /**< The contact contains only a '*' character */
- pjsip_uri *uri; /**< URI in the contact. */
- int q1000; /**< The "q" value times 1000 (to avoid float) */
- pj_int32_t expires; /**< Expires parameter, otherwise -1 if not present. */
- pj_str_t other_param; /**< Other parameters, concatenated in a single string. */
-} pjsip_contact_hdr;
-
-
-/**
- * Create a new Contact header.
- *
- * @param pool The pool.
- * @return A new instance of Contact header.
- */
-PJ_DECL(pjsip_contact_hdr*) pjsip_contact_hdr_create( pj_pool_t *pool );
-
-/**
- * @}
- */
-
-///////////////////////////////////////////////////////////////////////////////
-/**
- * @defgroup PJSIP_MSG_HDR_CTYPE Header Field: Content-Type
- * @brief Content-Type header field.
- * @ingroup PJSIP_MSG
- * @{
- */
-/**
- * Content-Type.
- */
-typedef struct pjsip_ctype_hdr
-{
- PJSIP_DECL_HDR_MEMBER(struct pjsip_ctype_hdr);
- pjsip_media_type media; /**< Media type. */
-} pjsip_ctype_hdr;
-
-
-/**
- * Create a nwe Content Type header.
- *
- * @param pool The pool.
- * @return A new Content-Type header.
- */
-PJ_DECL(pjsip_ctype_hdr*) pjsip_ctype_hdr_create( pj_pool_t *pool );
-
-/**
- * @}
- */
-
-///////////////////////////////////////////////////////////////////////////////
-/**
- * @defgroup PJSIP_MSG_HDR_EXPIRES Header Field: Expires
- * @brief Expires header field.
- * @ingroup PJSIP_MSG
- * @{
- */
-/** Expires header. */
-typedef pjsip_generic_int_hdr pjsip_expires_hdr;
-
-/**
- * Create a new Expires header.
- *
- * @param pool The pool.
- * @return A new Expires header.
- */
-PJ_DECL(pjsip_expires_hdr*) pjsip_expires_hdr_create( pj_pool_t *pool );
-
-/**
- * @}
- */
-
-///////////////////////////////////////////////////////////////////////////////
-/**
- * @defgroup PJSIP_MSG_HDR_FROMTO Header Field: From/To
- * @brief From and To header field.
- * @ingroup PJSIP_MSG
- * @{
- */
-/**
- * To or From header.
- */
-typedef struct pjsip_fromto_hdr
-{
- PJSIP_DECL_HDR_MEMBER(struct pjsip_fromto_hdr);
- pjsip_uri *uri; /**< URI in From/To header. */
- pj_str_t tag; /**< Header "tag" parameter. */
- pj_str_t other_param; /**< Other params, concatenated as a single string. */
-} pjsip_fromto_hdr;
-
-/** Alias for From header. */
-typedef pjsip_fromto_hdr pjsip_from_hdr;
-
-/** Alias for To header. */
-typedef pjsip_fromto_hdr pjsip_to_hdr;
-
-/**
- * Create a From header.
- *
- * @param pool The pool.
- * @return New instance of From header.
- */
-PJ_DECL(pjsip_from_hdr*) pjsip_from_hdr_create( pj_pool_t *pool );
-
-/**
- * Create a To header.
- *
- * @param pool The pool.
- * @return New instance of To header.
- */
-PJ_DECL(pjsip_to_hdr*) pjsip_to_hdr_create( pj_pool_t *pool );
-
-/**
- * Convert the header to a From header.
- *
- * @param pool The pool.
- * @return "From" header.
- */
-PJ_DECL(pjsip_from_hdr*) pjsip_fromto_set_from( pjsip_fromto_hdr *hdr );
-
-/**
- * Convert the header to a To header.
- *
- * @param pool The pool.
- * @return "To" header.
- */
-PJ_DECL(pjsip_to_hdr*) pjsip_fromto_set_to( pjsip_fromto_hdr *hdr );
-
-/**
- * @}
- */
-
-
-///////////////////////////////////////////////////////////////////////////////
-/**
- * @defgroup PJSIP_MSG_HDR_MAX_FORWARDS Header Field: Max-Forwards
- * @brief Max-Forwards header field.
- * @ingroup PJSIP_MSG
- * @{
- */
-typedef pjsip_generic_int_hdr pjsip_max_forwards_hdr;
-
-/**
- * Create new Max-Forwards header instance.
- *
- * @param pool The pool.
- *
- * @return New Max-Forwards header instance.
- */
-PJ_DECL(pjsip_max_forwards_hdr*) pjsip_max_forwards_hdr_create(pj_pool_t *pool);
-
-
-/**
- * @}
- */
-
-
-///////////////////////////////////////////////////////////////////////////////
-/**
- * @defgroup PJSIP_MSG_HDR_MIN_EXPIRES Header Field: Min-Expires
- * @brief Min-Expires header field.
- * @ingroup PJSIP_MSG
- * @{
- */
-typedef pjsip_generic_int_hdr pjsip_min_expires_hdr;
-
-/**
- * Create new Max-Forwards header instance.
- *
- * @param pool The pool.
- *
- * @return New Max-Forwards header instance.
- */
-PJ_DECL(pjsip_min_expires_hdr*) pjsip_min_expires_hdr_create(pj_pool_t *pool);
-
-
-/**
- * @}
- */
-
-
-///////////////////////////////////////////////////////////////////////////////
-/**
- * @defgroup PJSIP_MSG_HDR_ROUTING Header Field: Record-Route/Route
- * @brief Record-Route and Route header fields.
- * @ingroup PJSIP_MSG
- * @{
- */
-/**
- * Record-Route and Route headers.
- */
-typedef struct pjsip_routing_hdr
-{
- PJSIP_DECL_HDR_MEMBER(struct pjsip_routing_hdr); /**< Generic header fields. */
- pjsip_name_addr name_addr; /**< The URL in the Route/Record-Route header. */
- pj_str_t other_param; /** Other parameter. */
-} pjsip_routing_hdr;
-
-/** Alias for Record-Route header. */
-typedef pjsip_routing_hdr pjsip_rr_hdr;
-
-/** Alias for Route header. */
-typedef pjsip_routing_hdr pjsip_route_hdr;
-
-
-/**
- * Create new Record-Route header from the pool.
- *
- * @param pool The pool.
- * @return A new instance of Record-Route header.
- */
-PJ_DECL(pjsip_rr_hdr*) pjsip_rr_hdr_create( pj_pool_t *pool );
-
-/**
- * Create new Route header from the pool.
- *
- * @param pool The pool.
- * @return A new instance of "Route" header.
- */
-PJ_DECL(pjsip_route_hdr*) pjsip_route_hdr_create( pj_pool_t *pool );
-
-/**
- * Convert generic routing header to Record-Route header.
- *
- * @param r The generic routing header, or a "Routing" header.
- * @return Record-Route header.
- */
-PJ_DECL(pjsip_rr_hdr*) pjsip_routing_hdr_set_rr( pjsip_routing_hdr *r );
-
-/**
- * Convert generic routing header to "Route" header.
- *
- * @param r The generic routing header, or a "Record-Route" header.
- * @return "Route" header.
- */
-PJ_DECL(pjsip_route_hdr*) pjsip_routing_hdr_set_route( pjsip_routing_hdr *r );
-
-/**
- * @}
- */
-
-///////////////////////////////////////////////////////////////////////////////
-/**
- * @defgroup PJSIP_MSG_HDR_REQUIRE Header Field: Require
- * @brief Require header field.
- * @ingroup PJSIP_MSG
- * @{
- */
-typedef pjsip_generic_array_hdr pjsip_require_hdr;
-
-/**
- * Create new Require header instance.
- *
- * @param pool The pool.
- *
- * @return New Require header instance.
- */
-PJ_DECL(pjsip_require_hdr*) pjsip_require_hdr_create(pj_pool_t *pool);
-
-
-/**
- * @}
- */
-
-
-///////////////////////////////////////////////////////////////////////////////
-/**
- * @defgroup PJSIP_MSG_HDR_RETRY_AFTER Header Field: Retry-After
- * @brief Retry-After header field.
- * @ingroup PJSIP_MSG
- * @{
- */
-typedef pjsip_generic_int_hdr pjsip_retry_after_hdr;
-
-/**
- * Create new Retry-After header instance.
- *
- * @param pool The pool.
- *
- * @return New Retry-After header instance.
- */
-PJ_DECL(pjsip_retry_after_hdr*) pjsip_retry_after_hdr_create(pj_pool_t *pool);
-
-
-/**
- * @}
- */
-
-///////////////////////////////////////////////////////////////////////////////
-/**
- * @defgroup PJSIP_MSG_HDR_SUPPORTED Header Field: Supported
- * @brief Supported header field.
- * @ingroup PJSIP_MSG
- * @{
- */
-typedef pjsip_generic_array_hdr pjsip_supported_hdr;
-
-/**
- * Create new Supported header instance.
- *
- * @param pool The pool.
- *
- * @return New Supported header instance.
- */
-PJ_DECL(pjsip_supported_hdr*) pjsip_supported_hdr_create(pj_pool_t *pool);
-
-
-/**
- * @}
- */
-
-///////////////////////////////////////////////////////////////////////////////
-/**
- * @defgroup PJSIP_MSG_HDR_UNSUPPORTED Header Field: Unsupported
- * @brief Unsupported header field.
- * @ingroup PJSIP_MSG
- * @{
- */
-typedef pjsip_generic_array_hdr pjsip_unsupported_hdr;
-
-/**
- * Create new Unsupported header instance.
- *
- * @param pool The pool.
- *
- * @return New Unsupported header instance.
- */
-PJ_DECL(pjsip_unsupported_hdr*) pjsip_unsupported_hdr_create(pj_pool_t *pool);
-
-
-/**
- * @}
- */
-
-
-///////////////////////////////////////////////////////////////////////////////
-/**
- * @defgroup PJSIP_MSG_HDR_VIA Header Field: Via
- * @brief Via header field.
- * @ingroup PJSIP_MSG
- * @{
- */
-/**
- * SIP Via header.
- * In this implementation, Via header can only have one element in each header.
- * If a message arrives with multiple elements in a single Via, then they will
- * be split up into multiple Via headers.
- */
-typedef struct pjsip_via_hdr
-{
- PJSIP_DECL_HDR_MEMBER(struct pjsip_via_hdr);
- pj_str_t transport; /**< Transport type. */
- pjsip_host_port sent_by; /**< Host and optional port */
- int ttl_param; /**< TTL parameter, or -1 if it's not specified. */
- int rport_param; /**< "rport" parameter, 0 to specify without
- port number, -1 means doesn't exist. */
- pj_str_t maddr_param; /**< "maddr" parameter. */
- pj_str_t recvd_param; /**< "received" parameter. */
- pj_str_t branch_param; /**< "branch" parameter. */
- pj_str_t other_param; /**< Other parameters, concatenated as single string. */
- pj_str_t comment; /**< Comment. */
-} pjsip_via_hdr;
-
-/**
- * Create a new Via header.
- *
- * @param pool The pool.
- * @return A new "Via" header instance.
- */
-PJ_DECL(pjsip_via_hdr*) pjsip_via_hdr_create( pj_pool_t *pool );
-
-/**
- * @}
- */
-
-/**
- * @bug Once a header is put in the message, the header CAN NOT be put in
- * other list. Solution:
- * - always clone header in the message.
- * - create a list node for each header in the message.
- */
-
-
-///////////////////////////////////////////////////////////////////////////////
-/**
- * @defgroup PJSIP_MSG_HDR_UNIMP Unimplemented Header Fields
- * @brief Unimplemented header fields.
- * @ingroup PJSIP_MSG
- * @{
- */
-/** Accept-Encoding header. */
-typedef pjsip_generic_string_hdr pjsip_accept_encoding_hdr;
-
-/** Create Accept-Encoding header. */
-#define pjsip_accept_encoding_hdr_create pjsip_generic_string_hdr_create
-
-/** Accept-Language header. */
-typedef pjsip_generic_string_hdr pjsip_accept_lang_hdr;
-
-/** Create Accept-Language header. */
-#define pjsip_accept_lang_hdr_create pjsip_generic_string_hdr_create
-
-/** Alert-Info header. */
-typedef pjsip_generic_string_hdr pjsip_alert_info_hdr;
-
-/** Create Alert-Info header. */
-#define pjsip_alert_info_hdr_create pjsip_generic_string_hdr_create
-
-/** Authentication-Info header. */
-typedef pjsip_generic_string_hdr pjsip_auth_info_hdr;
-
-/** Create Authentication-Info header. */
-#define pjsip_auth_info_hdr_create pjsip_generic_string_hdr_create
-
-/** Call-Info header. */
-typedef pjsip_generic_string_hdr pjsip_call_info_hdr;
-
-/** Create Call-Info header. */
-#define pjsip_call_info_hdr_create pjsip_generic_string_hdr_create
-
-/** Content-Disposition header. */
-typedef pjsip_generic_string_hdr pjsip_content_disposition_hdr;
-
-/** Create Content-Disposition header. */
-#define pjsip_content_disposition_hdr_create pjsip_generic_string_hdr_create
-
-/** Content-Encoding header. */
-typedef pjsip_generic_string_hdr pjsip_content_encoding_hdr;
-
-/** Create Content-Encoding header. */
-#define pjsip_content_encoding_hdr_create pjsip_generic_string_hdr_create
-
-/** Content-Language header. */
-typedef pjsip_generic_string_hdr pjsip_content_lang_hdr;
-
-/** Create Content-Language header. */
-#define pjsip_content_lang_hdr_create pjsip_generic_string_hdr_create
-
-/** Date header. */
-typedef pjsip_generic_string_hdr pjsip_date_hdr;
-
-/** Create Date header. */
-#define pjsip_date_hdr_create pjsip_generic_string_hdr_create
-
-/** Error-Info header. */
-typedef pjsip_generic_string_hdr pjsip_err_info_hdr;
-
-/** Create Error-Info header. */
-#define pjsip_err_info_hdr_create pjsip_generic_string_hdr_create
-
-/** In-Reply-To header. */
-typedef pjsip_generic_string_hdr pjsip_in_reply_to_hdr;
-
-/** Create In-Reply-To header. */
-#define pjsip_in_reply_to_hdr_create pjsip_generic_string_hdr_create
-
-/** MIME-Version header. */
-typedef pjsip_generic_string_hdr pjsip_mime_version_hdr;
-
-/** Create MIME-Version header. */
-#define pjsip_mime_version_hdr_create pjsip_generic_string_hdr_create
-
-/** Organization header. */
-typedef pjsip_generic_string_hdr pjsip_organization_hdr;
-
-/** Create Organization header. */
-#define pjsip_organization_hdr_create pjsip_genric_string_hdr_create
-
-/** Priority header. */
-typedef pjsip_generic_string_hdr pjsip_priority_hdr;
-
-/** Create Priority header. */
-#define pjsip_priority_hdr_create pjsip_generic_string_hdr_create
-
-/** Proxy-Require header. */
-typedef pjsip_generic_string_hdr pjsip_proxy_require_hdr;
-
-/** Reply-To header. */
-typedef pjsip_generic_string_hdr pjsip_reply_to_hdr;
-
-/** Create Reply-To header. */
-#define pjsip_reply_to_hdr_create pjsip_generic_string_hdr_create
-
-/** Server header. */
-typedef pjsip_generic_string_hdr pjsip_server_hdr;
-
-/** Create Server header. */
-#define pjsip_server_hdr_create pjsip_generic_string_hdr_create
-
-/** Subject header. */
-typedef pjsip_generic_string_hdr pjsip_subject_hdr;
-
-/** Create Subject header. */
-#define pjsip_subject_hdr_create pjsip_generic_string_hdr_create
-
-/** Timestamp header. */
-typedef pjsip_generic_string_hdr pjsip_timestamp_hdr;
-
-/** Create Timestamp header. */
-#define pjsip_timestamp_hdr_create pjsip_generic_string_hdr_create
-
-/** User-Agent header. */
-typedef pjsip_generic_string_hdr pjsip_user_agent_hdr;
-
-/** Create User-Agent header. */
-#define pjsip_user_agent_hdr_create pjsip_generic_string_hdr_create
-
-/** Warning header. */
-typedef pjsip_generic_string_hdr pjsip_warning_hdr;
-
-/** Create Warning header. */
-#define pjsip_warning_hdr_create pjsip_generic_string_hdr_create
-
-/**
- * @}
- */
-
-/**
- * @} // PJSIP_MSG
- */
-
-/*
- * Include inline definitions.
- */
-#if PJ_FUNCTIONS_ARE_INLINED
-# include <pjsip/sip_msg_i.h>
-#endif
-
-
-PJ_END_DECL
-
-#endif /* __PJSIP_SIP_MSG_H__ */
-
+/* $Id$ */
+/*
+ * Copyright (C) 2003-2006 Benny Prijono <benny@prijono.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#ifndef __PJSIP_SIP_MSG_H__
+#define __PJSIP_SIP_MSG_H__
+
+/**
+ * @file sip_msg.h
+ * @brief SIP Message Structure.
+ */
+
+#include <pjsip/sip_types.h>
+#include <pjsip/sip_uri.h>
+#include <pj/list.h>
+
+PJ_BEGIN_DECL
+
+/**
+ * @defgroup PJSIP_MSG SIP Message Structure
+ * @ingroup PJSIP
+ * @{
+ */
+
+///////////////////////////////////////////////////////////////////////////////
+/**
+ * @defgroup PJSIP_MSG_METHOD Methods
+ * @brief Method names and manipulation.
+ * @ingroup PJSIP_MSG
+ * @{
+ */
+
+/**
+ * This enumeration declares SIP methods as described by RFC3261. Additional
+ * methods do exist, and they are described by corresponding RFCs for the SIP
+ * extentensions. Since they won't alter the characteristic of the processing
+ * of the message, they don't need to be explicitly mentioned here.
+ */
+typedef enum pjsip_method_e
+{
+ /** INVITE method, for establishing dialogs. */
+ PJSIP_INVITE_METHOD,
+
+ /** CANCEL method, for cancelling request. */
+ PJSIP_CANCEL_METHOD,
+
+ /** ACK method, for acknowledging final response to INVITE. */
+ PJSIP_ACK_METHOD,
+
+ /** BYE method, for terminating dialog. */
+ PJSIP_BYE_METHOD,
+
+ /** REGISTER method. */
+ PJSIP_REGISTER_METHOD,
+
+ /** OPTIONS method, for querying remote capabilities. */
+ PJSIP_OPTIONS_METHOD,
+
+ /** Other method, which means that the method name itself will be stored
+ elsewhere. */
+ PJSIP_OTHER_METHOD,
+
+} pjsip_method_e;
+
+
+
+/**
+ * This structure represents a SIP method.
+ * Application must always use either #pjsip_method_init or #pjsip_method_set
+ * to make sure that method name is initialized correctly. This way, the name
+ * member will always contain a valid method string regardless whether the ID
+ * is recognized or not.
+ */
+typedef struct pjsip_method
+{
+ pjsip_method_e id; /**< Method ID, from \a pjsip_method_e. */
+ pj_str_t name; /**< Method name, which will always contain the
+ method string. */
+} pjsip_method;
+
+
+/**
+ * Initialize the method structure from a string.
+ * This function will check whether the method is a known method then set
+ * both the id and name accordingly.
+ *
+ * @param m The method to initialize.
+ * @param pool Pool where memory allocation will be allocated from, if required.
+ * @param str The method string.
+ */
+PJ_DECL(void) pjsip_method_init( pjsip_method *m,
+ pj_pool_t *pool,
+ const pj_str_t *str);
+
+/**
+ * Initialize the method structure from a string, without cloning the string.
+ * See #pjsip_method_init.
+ *
+ * @param m The method structure to be initialized.
+ * @param str The method string.
+ */
+PJ_DECL(void) pjsip_method_init_np( pjsip_method *m,
+ pj_str_t *str);
+
+/**
+ * Set the method with the predefined method ID.
+ * This function will also set the name member of the structure to the correct
+ * string according to the method.
+ *
+ * @param m The method structure.
+ * @param id The method ID.
+ */
+PJ_DECL(void) pjsip_method_set( pjsip_method *m, pjsip_method_e id );
+
+
+/**
+ * Copy one method structure to another. If the method is of the known methods,
+ * then memory allocation is not required.
+ *
+ * @param pool Pool to allocate memory from, if required.
+ * @param method The destination method to copy to.
+ * @param rhs The source method to copy from.
+ */
+PJ_DECL(void) pjsip_method_copy( pj_pool_t *pool,
+ pjsip_method *method,
+ const pjsip_method *rhs );
+
+/**
+ * Compare one method with another, and conveniently determine whether the
+ * first method is equal, less than, or greater than the second method.
+ *
+ * @param m1 The first method.
+ * @param m2 The second method.
+ *
+ * @return Zero if equal, otherwise will return -1 if less or +1 if greater.
+ */
+PJ_DECL(int) pjsip_method_cmp( const pjsip_method *m1, const pjsip_method *m2);
+
+/**
+ * @}
+ */
+
+///////////////////////////////////////////////////////////////////////////////
+/**
+ * @defgroup PJSIP_MSG_HDR Header Fields General Structure.
+ * @brief General Header Fields Structure.
+ * @ingroup PJSIP_MSG
+ * @{
+ */
+
+/**
+ * Header types, as defined by RFC3261.
+ */
+typedef enum pjsip_hdr_e
+{
+ /*
+ * These are the headers documented in RFC3261. Headers not documented
+ * there must have type PJSIP_H_OTHER, and the header type itself is
+ * recorded in the header name string.
+ *
+ * DO NOT CHANGE THE VALUE/ORDER OF THE HEADER IDs!!!.
+ */
+ PJSIP_H_ACCEPT,
+ PJSIP_H_ACCEPT_ENCODING_UNIMP,
+ PJSIP_H_ACCEPT_LANGUAGE_UNIMP,
+ PJSIP_H_ALERT_INFO_UNIMP,
+ PJSIP_H_ALLOW,
+ PJSIP_H_AUTHENTICATION_INFO_UNIMP,
+ PJSIP_H_AUTHORIZATION,
+ PJSIP_H_CALL_ID,
+ PJSIP_H_CALL_INFO_UNIMP,
+ PJSIP_H_CONTACT,
+ PJSIP_H_CONTENT_DISPOSITION_UNIMP,
+ PJSIP_H_CONTENT_ENCODING_UNIMP,
+ PJSIP_H_CONTENT_LANGUAGE_UNIMP,
+ PJSIP_H_CONTENT_LENGTH,
+ PJSIP_H_CONTENT_TYPE,
+ PJSIP_H_CSEQ,
+ PJSIP_H_DATE_UNIMP,
+ PJSIP_H_ERROR_INFO_UNIMP,
+ PJSIP_H_EXPIRES,
+ PJSIP_H_FROM,
+ PJSIP_H_IN_REPLY_TO_UNIMP,
+ PJSIP_H_MAX_FORWARDS,
+ PJSIP_H_MIME_VERSION_UNIMP,
+ PJSIP_H_MIN_EXPIRES,
+ PJSIP_H_ORGANIZATION_UNIMP,
+ PJSIP_H_PRIORITY_UNIMP,
+ PJSIP_H_PROXY_AUTHENTICATE,
+ PJSIP_H_PROXY_AUTHORIZATION,
+ PJSIP_H_PROXY_REQUIRE_UNIMP,
+ PJSIP_H_RECORD_ROUTE,
+ PJSIP_H_REPLY_TO_UNIMP,
+ PJSIP_H_REQUIRE,
+ PJSIP_H_RETRY_AFTER,
+ PJSIP_H_ROUTE,
+ PJSIP_H_SERVER_UNIMP,
+ PJSIP_H_SUBJECT_UNIMP,
+ PJSIP_H_SUPPORTED,
+ PJSIP_H_TIMESTAMP_UNIMP,
+ PJSIP_H_TO,
+ PJSIP_H_UNSUPPORTED,
+ PJSIP_H_USER_AGENT_UNIMP,
+ PJSIP_H_VIA,
+ PJSIP_H_WARNING_UNIMP,
+ PJSIP_H_WWW_AUTHENTICATE,
+
+ PJSIP_H_OTHER,
+
+} pjsip_hdr_e;
+
+/**
+ * This structure provides the pointer to basic functions that are needed
+ * for generic header operations. All header fields will have pointer to
+ * this structure, so that they can be manipulated uniformly.
+ */
+typedef struct pjsip_hdr_vptr
+{
+ /**
+ * Function to clone the header.
+ *
+ * @param pool Memory pool to allocate the new header.
+ * @param hdr Header to clone.
+ *
+ * @return A new instance of the header.
+ */
+ void *(*clone)(pj_pool_t *pool, const void *hdr);
+
+ /**
+ * Pointer to function to shallow clone the header.
+ * Shallow cloning will just make a memory copy of the original header,
+ * thus all pointers in original header will be kept intact. Because the
+ * function does not need to perform deep copy, the operation should be
+ * faster, but the application must make sure that the original header
+ * is still valid throughout the lifetime of new header.
+ *
+ * @param pool Memory pool to allocate the new header.
+ * @param hdr The header to clone.
+ */
+ void *(*shallow_clone)(pj_pool_t *pool, const void *hdr);
+
+ /** Pointer to function to print the header to the specified buffer.
+ * Returns the length of string written, or -1 if the remaining buffer
+ * is not enough to hold the header.
+ *
+ * @param hdr The header to print.
+ * @param buf The buffer.
+ * @param len The size of the buffer.
+ *
+ * @return The size copied to buffer, or -1 if there's not enough space.
+ */
+ int (*print_on)(void *hdr, char *buf, pj_size_t len);
+
+} pjsip_hdr_vptr;
+
+
+/**
+ * Generic fields for all SIP headers are declared using this macro, to make
+ * sure that all headers will have exactly the same layout in their start of
+ * the storage. This behaves like C++ inheritance actually.
+ */
+#define PJSIP_DECL_HDR_MEMBER(hdr) \
+ /** List members. */ \
+ PJ_DECL_LIST_MEMBER(hdr); \
+ /** Header type */ \
+ pjsip_hdr_e type; \
+ /** Header name. */ \
+ pj_str_t name; \
+ /** Header short name version. */ \
+ pj_str_t sname; \
+ /** Virtual function table. */ \
+ pjsip_hdr_vptr *vptr
+
+
+/**
+ * Generic SIP header structure, for generic manipulation for headers in the
+ * message. All header fields can be typecasted to this type.
+ */
+struct pjsip_hdr
+{
+ PJSIP_DECL_HDR_MEMBER(struct pjsip_hdr);
+};
+
+
+/**
+ * This generic function will clone any header, by calling "clone" function
+ * in header's virtual function table.
+ *
+ * @param pool The pool to allocate memory from.
+ * @param hdr The header to clone.
+ *
+ * @return A new instance copied from the original header.
+ */
+PJ_DECL(void*) pjsip_hdr_clone( pj_pool_t *pool, const void *hdr );
+
+
+/**
+ * This generic function will clone any header, by calling "shallow_clone"
+ * function in header's virtual function table.
+ *
+ * @param pool The pool to allocate memory from.
+ * @param hdr The header to clone.
+ *
+ * @return A new instance copied from the original header.
+ */
+PJ_DECL(void*) pjsip_hdr_shallow_clone( pj_pool_t *pool, const void *hdr );
+
+/**
+ * This generic function will print any header, by calling "print"
+ * function in header's virtual function table.
+ *
+ * @param hdr The header to print.
+ * @param buf The buffer.
+ * @param len The size of the buffer.
+ *
+ * @return The size copied to buffer, or -1 if there's not enough space.
+ */
+PJ_DECL(int) pjsip_hdr_print_on( void *hdr, char *buf, pj_size_t len);
+
+/**
+ * @}
+ */
+
+///////////////////////////////////////////////////////////////////////////////
+/**
+ * @defgroup PJSIP_MSG_LINE Request and Status Line.
+ * @brief Request and status line structures and manipulation.
+ * @ingroup PJSIP_MSG
+ * @{
+ */
+
+/**
+ * This structure describes SIP request line.
+ */
+typedef struct pjsip_request_line
+{
+ pjsip_method method; /**< Method for this request line. */
+ pjsip_uri *uri; /**< URI for this request line. */
+} pjsip_request_line;
+
+
+/**
+ * This structure describes SIP status line.
+ */
+typedef struct pjsip_status_line
+{
+ int code; /**< Status code. */
+ pj_str_t reason; /**< Reason string. */
+} pjsip_status_line;
+
+
+/**
+ * This enumeration lists standard SIP status codes according to RFC 3261.
+ * In addition, it also declares new status class 7xx for errors generated
+ * by the stack. This status class however should not get transmitted on the
+ * wire.
+ */
+typedef enum pjsip_status_code
+{
+ PJSIP_SC_TRYING = 100,
+ PJSIP_SC_RINGING = 180,
+ PJSIP_SC_CALL_BEING_FORWARDED = 181,
+ PJSIP_SC_QUEUED = 182,
+ PJSIP_SC_PROGRESS = 183,
+
+ PJSIP_SC_OK = 200,
+
+ PJSIP_SC_MULTIPLE_CHOICES = 300,
+ PJSIP_SC_MOVED_PERMANENTLY = 301,
+ PJSIP_SC_MOVED_TEMPORARILY = 302,
+ PJSIP_SC_USE_PROXY = 305,
+ PJSIP_SC_ALTERNATIVE_SERVICE = 380,
+
+ PJSIP_SC_BAD_REQUEST = 400,
+ PJSIP_SC_UNAUTHORIZED = 401,
+ PJSIP_SC_PAYMENT_REQUIRED = 402,
+ PJSIP_SC_FORBIDDEN = 403,
+ PJSIP_SC_NOT_FOUND = 404,
+ PJSIP_SC_METHOD_NOT_ALLOWED = 405,
+ PJSIP_SC_NOT_ACCEPTABLE = 406,
+ PJSIP_SC_PROXY_AUTHENTICATION_REQUIRED = 407,
+ PJSIP_SC_REQUEST_TIMEOUT = 408,
+ PJSIP_SC_GONE = 410,
+ PJSIP_SC_REQUEST_ENTITY_TOO_LARGE = 413,
+ PJSIP_SC_REQUEST_URI_TOO_LONG = 414,
+ PJSIP_SC_UNSUPPORTED_MEDIA_TYPE = 415,
+ PJSIP_SC_UNSUPPORTED_URI_SCHEME = 416,
+ PJSIP_SC_BAD_EXTENSION = 420,
+ PJSIP_SC_EXTENSION_REQUIRED = 421,
+ PJSIP_SC_INTERVAL_TOO_BRIEF = 423,
+ PJSIP_SC_TEMPORARILY_UNAVAILABLE = 480,
+ PJSIP_SC_CALL_TSX_DOES_NOT_EXIST = 481,
+ PJSIP_SC_LOOP_DETECTED = 482,
+ PJSIP_SC_TOO_MANY_HOPS = 483,
+ PJSIP_SC_ADDRESS_INCOMPLETE = 484,
+ PJSIP_AC_AMBIGUOUS = 485,
+ PJSIP_SC_BUSY_HERE = 486,
+ PJSIP_SC_REQUEST_TERMINATED = 487,
+ PJSIP_SC_NOT_ACCEPTABLE_HERE = 488,
+ PJSIP_SC_REQUEST_PENDING = 491,
+ PJSIP_SC_UNDECIPHERABLE = 493,
+
+ PJSIP_SC_INTERNAL_SERVER_ERROR = 500,
+ PJSIP_SC_NOT_IMPLEMENTED = 501,
+ PJSIP_SC_BAD_GATEWAY = 502,
+ PJSIP_SC_SERVICE_UNAVAILABLE = 503,
+ PJSIP_SC_SERVER_TIMEOUT = 504,
+ PJSIP_SC_VERSION_NOT_SUPPORTED = 505,
+ PJSIP_SC_MESSAGE_TOO_LARGE = 513,
+
+ PJSIP_SC_BUSY_EVERYWHERE = 600,
+ PJSIP_SC_DECLINE = 603,
+ PJSIP_SC_DOES_NOT_EXIST_ANYWHERE = 604,
+ PJSIP_SC_NOT_ACCEPTABLE_ANYWHERE = 606,
+
+ PJSIP_SC_TSX_TIMEOUT = 701,
+ PJSIP_SC_TSX_RESOLVE_ERROR = 702,
+ PJSIP_SC_TSX_TRANSPORT_ERROR = 703,
+
+} pjsip_status_code;
+
+/**
+ * Get the default status text for the status code.
+ *
+ * @param status_code SIP Status Code
+ *
+ * @return textual message for the status code.
+ */
+PJ_DECL(const pj_str_t*) pjsip_get_status_text(int status_code);
+
+/**
+ * This macro returns non-zero (TRUE) if the specified status_code is
+ * in the same class as the code_class.
+ *
+ * @param status_code The status code.
+ * @param code_class The status code in the class (for example 100, 200).
+ */
+#define PJSIP_IS_STATUS_IN_CLASS(status_code, code_class) \
+ (status_code/100 == code_class/100)
+
+/**
+ * @}
+ */
+
+///////////////////////////////////////////////////////////////////////////////
+/**
+ * @addtogroup PJSIP_MSG_MEDIA Media Type
+ * @brief Media type definitions and manipulations.
+ * @ingroup PJSIP_MSG
+ * @{
+ */
+
+/**
+ * This structure describes SIP media type, as used for example in
+ * Accept and Content-Type header..
+ */
+typedef struct pjsip_media_type
+{
+ pj_str_t type; /**< Media type. */
+ pj_str_t subtype; /**< Media subtype. */
+ pj_str_t param; /**< Media type parameters (concatenated). */
+} pjsip_media_type;
+
+/**
+ * @}
+ */
+
+///////////////////////////////////////////////////////////////////////////////
+/**
+ * @addtogroup PJSIP_MSG_BODY Message Body
+ * @brief SIP message body structures and manipulation.
+ * @ingroup PJSIP_MSG
+ * @{
+ */
+
+/**
+ * Generic abstraction to message body.
+ * When an incoming message is parsed (pjsip_parse_msg()), the parser fills in
+ * all members with the appropriate value. The 'data' and 'len' member will
+ * describe portion of incoming packet which denotes the message body.
+ * When application needs to attach message body to outgoing SIP message, it
+ * must fill in all members of this structure.
+ */
+typedef struct pjsip_msg_body
+{
+ /** MIME content type.
+ * For incoming messages, the parser will fill in this member with the
+ * content type found in Content-Type header.
+ *
+ * For outgoing messages, application must fill in this member with
+ * appropriate value, because the stack will generate Content-Type header
+ * based on the value specified here.
+ */
+ pjsip_media_type content_type;
+
+ /** Pointer to buffer which holds the message body data.
+ * For incoming messages, the parser will fill in this member with the
+ * pointer to the body string.
+ *
+ * When sending outgoing message, this member doesn't need to point to the
+ * actual message body string. It can be assigned with arbitrary pointer,
+ * because the value will only need to be understood by the print_body()
+ * function. The stack itself will not try to interpret this value, but
+ * instead will always call the print_body() whenever it needs to get the
+ * actual body string.
+ */
+ void *data;
+
+ /** The length of the data.
+ * For incoming messages, the parser will fill in this member with the
+ * actual length of message body.
+ *
+ * When sending outgoing message, again just like the "data" member, the
+ * "len" member doesn't need to point to the actual length of the body
+ * string.
+ */
+ unsigned len;
+
+ /** Pointer to function to print this message body.
+ * Application must set a proper function here when sending outgoing
+ * message.
+ *
+ * @param msg_body This structure itself.
+ * @param buf The buffer.
+ * @param size The buffer size.
+ *
+ * @return The length of the string printed, or -1 if there is
+ * not enough space in the buffer to print the whole
+ * message body.
+ */
+ int (*print_body)(struct pjsip_msg_body *msg_body,
+ char *buf, pj_size_t size);
+
+} pjsip_msg_body;
+
+/**
+ * General purpose function to textual data in a SIP body. Attach this function
+ * in a SIP message body only if the data in pjsip_msg_body is a textual
+ * message ready to be embedded in a SIP message. If the data in the message
+ * body is not a textual body, then application must supply a custom function
+ * to print that body.
+ *
+ * @param msg_body The message body.
+ * @param buf Buffer to copy the message body to.
+ * @param size The size of the buffer.
+ *
+ * @return The length copied to the buffer, or -1.
+ */
+PJ_DECL(int) pjsip_print_text_body( pjsip_msg_body *msg_body,
+ char *buf, pj_size_t size);
+
+/**
+ * @}
+ */
+
+///////////////////////////////////////////////////////////////////////////////
+/**
+ * @defgroup PJSIP_MSG_MSG Message Structure
+ * @brief Message structure and operations.
+ * @ingroup PJSIP_MSG
+ * @{
+ */
+
+/**
+ * Message type (request or response).
+ */
+typedef enum pjsip_msg_type_e
+{
+ PJSIP_REQUEST_MSG, /**< Indicates request message. */
+ PJSIP_RESPONSE_MSG, /**< Indicates response message. */
+} pjsip_msg_type_e;
+
+
+/**
+ * This structure describes a SIP message.
+ */
+struct pjsip_msg
+{
+ /** Message type (ie request or response). */
+ pjsip_msg_type_e type;
+
+ /** The first line of the message can be either request line for request
+ * messages, or status line for response messages. It is represented here
+ * as a union.
+ */
+ union
+ {
+ /** Request Line. */
+ struct pjsip_request_line req;
+
+ /** Status Line. */
+ struct pjsip_status_line status;
+ } line;
+
+ /** List of message headers. */
+ pjsip_hdr hdr;
+
+ /** Pointer to message body, or NULL if no message body is attached to
+ * this mesage.
+ */
+ pjsip_msg_body *body;
+};
+
+
+/**
+ * Create new request or response message.
+ *
+ * @param pool The pool.
+ * @param type Message type.
+ * @return New message, or THROW exception if failed.
+ */
+PJ_DECL(pjsip_msg*) pjsip_msg_create( pj_pool_t *pool, pjsip_msg_type_e type);
+
+/**
+ * Find a header in the message by the header type.
+ *
+ * @param msg The message.
+ * @param type The header type to find.
+ * @param start The first header field where the search should begin.
+ * If NULL is specified, then the search will begin from the
+ * first header, otherwise the search will begin at the
+ * specified header.
+ *
+ * @return The header field, or NULL if no header with the specified
+ * type is found.
+ */
+PJ_DECL(void*) pjsip_msg_find_hdr( pjsip_msg *msg,
+ pjsip_hdr_e type, void *start);
+
+/**
+ * Find a header in the message by its name.
+ *
+ * @param msg The message.
+ * @param name The header name to find.
+ * @param start The first header field where the search should begin.
+ * If NULL is specified, then the search will begin from the
+ * first header, otherwise the search will begin at the
+ * specified header.
+ *
+ * @return The header field, or NULL if no header with the specified
+ * type is found.
+ */
+PJ_DECL(void*) pjsip_msg_find_hdr_by_name( pjsip_msg *msg,
+ const pj_str_t *name, void *start);
+
+/**
+ * Find and remove a header in the message.
+ *
+ * @param msg The message.
+ * @param hdr The header type to find.
+ * @param start The first header field where the search should begin,
+ * or NULL to search from the first header in the message.
+ *
+ * @return The header field, or NULL if not found.
+ */
+PJ_DECL(void*) pjsip_msg_find_remove_hdr( pjsip_msg *msg,
+ pjsip_hdr_e hdr, void *start);
+
+/**
+ * Add a header to the message, putting it last in the header list.
+ *
+ * @param msg The message.
+ * @param hdr The header to add.
+ *
+ * @bug Once the header is put in a list (or message), it can not be put in
+ * other list (or message). Otherwise Real Bad Thing will happen.
+ */
+PJ_IDECL(void) pjsip_msg_add_hdr( pjsip_msg *msg, pjsip_hdr *hdr );
+
+/**
+ * Add header field to the message, putting it in the front of the header list.
+ *
+ * @param msg The message.
+ * @param hdr The header to add.
+ *
+ * @bug Once the header is put in a list (or message), it can not be put in
+ * other list (or message). Otherwise Real Bad Thing will happen.
+ */
+PJ_IDECL(void) pjsip_msg_insert_first_hdr( pjsip_msg *msg, pjsip_hdr *hdr );
+
+/**
+ * Print the message to the specified buffer.
+ *
+ * @param msg The message to print.
+ * @param buf The buffer
+ * @param size The size of the buffer.
+ *
+ * @return The length of the printed characters (in bytes), or NEGATIVE
+ * value if the message is too large for the specified buffer.
+ */
+PJ_DECL(pj_ssize_t) pjsip_msg_print(pjsip_msg *msg, char *buf, pj_size_t size);
+
+/**
+ * @}
+ */
+
+///////////////////////////////////////////////////////////////////////////////
+/**
+ * @addtogroup PJSIP_MSG_HDR_GEN Header Field: Generic
+ * @brief Generic header field which contains header name and value.
+ * @ingroup PJSIP_MSG
+ * @{
+ */
+
+/**
+ * Generic SIP header, which contains hname and a string hvalue.
+ * Note that this header is not supposed to be used as 'base' class for headers.
+ */
+typedef struct pjsip_generic_string_hdr
+{
+ PJSIP_DECL_HDR_MEMBER(struct pjsip_generic_string_hdr); /**< Standard header field. */
+ pj_str_t hvalue; /**< hvalue */
+} pjsip_generic_string_hdr;
+
+
+/**
+ * Create a new instance of generic header. A generic header can have an
+ * arbitrary header name.
+ *
+ * @param pool The pool.
+ * @param hname The header name to be assigned to the header, or NULL to
+ * assign the header name with some string.
+ *
+ * @return The header, or THROW exception.
+ */
+PJ_DECL(pjsip_generic_string_hdr*) pjsip_generic_string_hdr_create( pj_pool_t *pool,
+ const pj_str_t *hname );
+
+/**
+ * Create a generic header along with the text content.
+ *
+ * @param pool The pool.
+ * @param hname The header name.
+ * @param hvalue The header text content.
+ *
+ * @return The header instance.
+ */
+PJ_DECL(pjsip_generic_string_hdr*)
+pjsip_generic_string_hdr_create_with_text( pj_pool_t *pool,
+ const pj_str_t *hname,
+ const pj_str_t *hvalue);
+
+/**
+ * @}
+ */
+
+///////////////////////////////////////////////////////////////////////////////
+/**
+ * @addtogroup PJSIP_MSG_HDR_GEN_INT Header Field: Generic Integer
+ * @brief Generic header field which contains header name and value.
+ * @ingroup PJSIP_MSG
+ * @{
+ */
+
+/**
+ * Generic SIP header, which contains hname and a string hvalue.
+ */
+typedef struct pjsip_generic_int_hdr
+{
+ PJSIP_DECL_HDR_MEMBER(struct pjsip_generic_int_hdr); /**< Standard header field. */
+ pj_int32_t ivalue; /**< ivalue */
+} pjsip_generic_int_hdr;
+
+
+/**
+ * Create a new instance of generic header. A generic header can have an
+ * arbitrary header name.
+ *
+ * @param pool The pool.
+ * @param hname The header name to be assigned to the header, or NULL to
+ * assign the header name with some string.
+ *
+ * @return The header, or THROW exception.
+ */
+PJ_DECL(pjsip_generic_int_hdr*) pjsip_generic_int_hdr_create( pj_pool_t *pool,
+ const pj_str_t *hname );
+
+/**
+ * Create a generic header along with the value.
+ *
+ * @param pool The pool.
+ * @param hname The header name.
+ * @param value The header value content.
+ *
+ * @return The header instance.
+ */
+PJ_DECL(pjsip_generic_int_hdr*)
+pjsip_generic_int_hdr_create_with_value( pj_pool_t *pool,
+ const pj_str_t *hname,
+ int value);
+
+/**
+ * @}
+ */
+
+///////////////////////////////////////////////////////////////////////////////
+/**
+ * @defgroup PJSIP_MSG_HDR_GENERIC_LIST Header Field: Generic string list.
+ * @brief Header with list of strings separated with comma
+ * @ingroup PJSIP_MSG
+ * @{
+ */
+
+/** Maximum elements in the header array. */
+#define PJSIP_GENERIC_ARRAY_MAX_COUNT 32
+
+typedef struct pjsip_generic_array_hdr
+{
+ PJSIP_DECL_HDR_MEMBER(struct pjsip_generic_array_hdr);
+ unsigned count; /**< Number of elements. */
+ pj_str_t values[PJSIP_GENERIC_ARRAY_MAX_COUNT]; /**< Elements. */
+} pjsip_generic_array_hdr;
+
+/**
+ * Create generic array header.
+ *
+ * @param pool Pool to allocate memory from.
+ *
+ * @return New generic array header.
+ */
+PJ_DECL(pjsip_generic_array_hdr*) pjsip_generic_array_create(pj_pool_t *pool,
+ const pj_str_t *hnames);
+
+/**
+ * @}
+ */
+
+///////////////////////////////////////////////////////////////////////////////
+/**
+ * @defgroup PJSIP_MSG_HDR_ACCEPT Header Field: Accept
+ * @brief Accept header field.
+ * @ingroup PJSIP_MSG
+ * @{
+ */
+/** Accept header. */
+typedef pjsip_generic_array_hdr pjsip_accept_hdr;
+
+/** Maximum fields in Accept header. */
+#define PJSIP_MAX_ACCEPT_COUNT PJSIP_GENERIC_ARRAY_MAX_COUNT
+
+/**
+ * Create new Accept header instance.
+ *
+ * @param pool The pool.
+ *
+ * @return New Accept header instance.
+ */
+PJ_DECL(pjsip_accept_hdr*) pjsip_accept_hdr_create(pj_pool_t *pool);
+
+
+/**
+ * @}
+ */
+
+///////////////////////////////////////////////////////////////////////////////
+/**
+ * @defgroup PJSIP_MSG_HDR_ALLOW Header Field: Allow
+ * @brief Allow header field.
+ * @ingroup PJSIP_MSG
+ * @{
+ */
+typedef pjsip_generic_array_hdr pjsip_allow_hdr;
+
+/**
+ * Create new Allow header instance.
+ *
+ * @param pool The pool.
+ *
+ * @return New Allow header instance.
+ */
+PJ_DECL(pjsip_allow_hdr*) pjsip_allow_hdr_create(pj_pool_t *pool);
+
+
+/**
+ * @}
+ */
+
+///////////////////////////////////////////////////////////////////////////////
+/**
+ * @defgroup PJSIP_MSG_HDR_CID Header Field: Call-ID
+ * @brief Call-ID header field.
+ * @ingroup PJSIP_MSG
+ * @{
+ */
+/**
+ * Call-ID header.
+ */
+typedef struct pjsip_cid_hdr
+{
+ PJSIP_DECL_HDR_MEMBER(struct pjsip_cid_hdr);
+ pj_str_t id; /**< Call-ID string. */
+} pjsip_cid_hdr;
+
+
+/**
+ * Create new Call-ID header.
+ *
+ * @param pool The pool.
+ *
+ * @return new Call-ID header.
+ */
+PJ_DECL(pjsip_cid_hdr*) pjsip_cid_hdr_create( pj_pool_t *pool );
+
+
+/**
+ * @}
+ */
+
+///////////////////////////////////////////////////////////////////////////////
+/**
+ * @defgroup PJSIP_MSG_HDR_CLEN Header Field: Content-Length
+ * @brief Content-Length header field.
+ * @ingroup PJSIP_MSG
+ * @{
+ */
+/**
+ * Content-Length header.
+ */
+typedef struct pjsip_clen_hdr
+{
+ PJSIP_DECL_HDR_MEMBER(struct pjsip_clen_hdr);
+ int len; /**< Content length. */
+} pjsip_clen_hdr;
+
+/**
+ * Create new Content-Length header.
+ *
+ * @param pool the pool.
+ * @return A new Content-Length header instance.
+ */
+PJ_DECL(pjsip_clen_hdr*) pjsip_clen_hdr_create( pj_pool_t *pool );
+
+/**
+ * @}
+ */
+
+///////////////////////////////////////////////////////////////////////////////
+/**
+ * @defgroup PJSIP_MSG_HDR_CSEQ Header Field: CSeq
+ * @brief CSeq header field.
+ * @ingroup PJSIP_MSG
+ * @{
+ */
+/**
+ * CSeq header.
+ */
+typedef struct pjsip_cseq_hdr
+{
+ PJSIP_DECL_HDR_MEMBER(struct pjsip_cseq_hdr);
+ int cseq; /**< CSeq number. */
+ pjsip_method method; /**< CSeq method. */
+} pjsip_cseq_hdr;
+
+
+/** Create new CSeq header.
+ *
+ * @param pool The pool.
+ * @return A new CSeq header instance.
+ */
+PJ_DECL(pjsip_cseq_hdr*) pjsip_cseq_hdr_create( pj_pool_t *pool );
+
+/**
+ * @}
+ */
+
+///////////////////////////////////////////////////////////////////////////////
+/**
+ * @defgroup PJSIP_MSG_HDR_CONTACT Header Field: Contact
+ * @brief Contact header field.
+ * @ingroup PJSIP_MSG
+ * @{
+ */
+/**
+ * Contact header.
+ * In this library, contact header only contains single URI. If a message has
+ * multiple URI in the Contact header, the URI will be put in separate Contact
+ * headers.
+ */
+typedef struct pjsip_contact_hdr
+{
+ PJSIP_DECL_HDR_MEMBER(struct pjsip_contact_hdr);
+ int star; /**< The contact contains only a '*' character */
+ pjsip_uri *uri; /**< URI in the contact. */
+ int q1000; /**< The "q" value times 1000 (to avoid float) */
+ pj_int32_t expires; /**< Expires parameter, otherwise -1 if not present. */
+ pjsip_param other_param; /**< Other parameters, concatenated in a single string. */
+} pjsip_contact_hdr;
+
+
+/**
+ * Create a new Contact header.
+ *
+ * @param pool The pool.
+ * @return A new instance of Contact header.
+ */
+PJ_DECL(pjsip_contact_hdr*) pjsip_contact_hdr_create( pj_pool_t *pool );
+
+/**
+ * @}
+ */
+
+///////////////////////////////////////////////////////////////////////////////
+/**
+ * @defgroup PJSIP_MSG_HDR_CTYPE Header Field: Content-Type
+ * @brief Content-Type header field.
+ * @ingroup PJSIP_MSG
+ * @{
+ */
+/**
+ * Content-Type.
+ */
+typedef struct pjsip_ctype_hdr
+{
+ PJSIP_DECL_HDR_MEMBER(struct pjsip_ctype_hdr);
+ pjsip_media_type media; /**< Media type. */
+} pjsip_ctype_hdr;
+
+
+/**
+ * Create a nwe Content Type header.
+ *
+ * @param pool The pool.
+ * @return A new Content-Type header.
+ */
+PJ_DECL(pjsip_ctype_hdr*) pjsip_ctype_hdr_create( pj_pool_t *pool );
+
+/**
+ * @}
+ */
+
+///////////////////////////////////////////////////////////////////////////////
+/**
+ * @defgroup PJSIP_MSG_HDR_EXPIRES Header Field: Expires
+ * @brief Expires header field.
+ * @ingroup PJSIP_MSG
+ * @{
+ */
+/** Expires header. */
+typedef pjsip_generic_int_hdr pjsip_expires_hdr;
+
+/**
+ * Create a new Expires header.
+ *
+ * @param pool The pool.
+ * @return A new Expires header.
+ */
+PJ_DECL(pjsip_expires_hdr*) pjsip_expires_hdr_create( pj_pool_t *pool );
+
+/**
+ * @}
+ */
+
+///////////////////////////////////////////////////////////////////////////////
+/**
+ * @defgroup PJSIP_MSG_HDR_FROMTO Header Field: From/To
+ * @brief From and To header field.
+ * @ingroup PJSIP_MSG
+ * @{
+ */
+/**
+ * To or From header.
+ */
+typedef struct pjsip_fromto_hdr
+{
+ PJSIP_DECL_HDR_MEMBER(struct pjsip_fromto_hdr);
+ pjsip_uri *uri; /**< URI in From/To header. */
+ pj_str_t tag; /**< Header "tag" parameter. */
+ pjsip_param other_param; /**< Other params, concatenated as a single string. */
+} pjsip_fromto_hdr;
+
+/** Alias for From header. */
+typedef pjsip_fromto_hdr pjsip_from_hdr;
+
+/** Alias for To header. */
+typedef pjsip_fromto_hdr pjsip_to_hdr;
+
+/**
+ * Create a From header.
+ *
+ * @param pool The pool.
+ * @return New instance of From header.
+ */
+PJ_DECL(pjsip_from_hdr*) pjsip_from_hdr_create( pj_pool_t *pool );
+
+/**
+ * Create a To header.
+ *
+ * @param pool The pool.
+ * @return New instance of To header.
+ */
+PJ_DECL(pjsip_to_hdr*) pjsip_to_hdr_create( pj_pool_t *pool );
+
+/**
+ * Convert the header to a From header.
+ *
+ * @param pool The pool.
+ * @return "From" header.
+ */
+PJ_DECL(pjsip_from_hdr*) pjsip_fromto_set_from( pjsip_fromto_hdr *hdr );
+
+/**
+ * Convert the header to a To header.
+ *
+ * @param pool The pool.
+ * @return "To" header.
+ */
+PJ_DECL(pjsip_to_hdr*) pjsip_fromto_set_to( pjsip_fromto_hdr *hdr );
+
+/**
+ * @}
+ */
+
+
+///////////////////////////////////////////////////////////////////////////////
+/**
+ * @defgroup PJSIP_MSG_HDR_MAX_FORWARDS Header Field: Max-Forwards
+ * @brief Max-Forwards header field.
+ * @ingroup PJSIP_MSG
+ * @{
+ */
+typedef pjsip_generic_int_hdr pjsip_max_forwards_hdr;
+
+/**
+ * Create new Max-Forwards header instance.
+ *
+ * @param pool The pool.
+ *
+ * @return New Max-Forwards header instance.
+ */
+PJ_DECL(pjsip_max_forwards_hdr*) pjsip_max_forwards_hdr_create(pj_pool_t *pool);
+
+
+/**
+ * @}
+ */
+
+
+///////////////////////////////////////////////////////////////////////////////
+/**
+ * @defgroup PJSIP_MSG_HDR_MIN_EXPIRES Header Field: Min-Expires
+ * @brief Min-Expires header field.
+ * @ingroup PJSIP_MSG
+ * @{
+ */
+typedef pjsip_generic_int_hdr pjsip_min_expires_hdr;
+
+/**
+ * Create new Max-Forwards header instance.
+ *
+ * @param pool The pool.
+ *
+ * @return New Max-Forwards header instance.
+ */
+PJ_DECL(pjsip_min_expires_hdr*) pjsip_min_expires_hdr_create(pj_pool_t *pool);
+
+
+/**
+ * @}
+ */
+
+
+///////////////////////////////////////////////////////////////////////////////
+/**
+ * @defgroup PJSIP_MSG_HDR_ROUTING Header Field: Record-Route/Route
+ * @brief Record-Route and Route header fields.
+ * @ingroup PJSIP_MSG
+ * @{
+ */
+/**
+ * Record-Route and Route headers.
+ */
+typedef struct pjsip_routing_hdr
+{
+ PJSIP_DECL_HDR_MEMBER(struct pjsip_routing_hdr); /**< Generic header fields. */
+ pjsip_name_addr name_addr; /**< The URL in the Route/Record-Route header. */
+ pjsip_param other_param; /** Other parameter. */
+} pjsip_routing_hdr;
+
+/** Alias for Record-Route header. */
+typedef pjsip_routing_hdr pjsip_rr_hdr;
+
+/** Alias for Route header. */
+typedef pjsip_routing_hdr pjsip_route_hdr;
+
+
+/**
+ * Create new Record-Route header from the pool.
+ *
+ * @param pool The pool.
+ * @return A new instance of Record-Route header.
+ */
+PJ_DECL(pjsip_rr_hdr*) pjsip_rr_hdr_create( pj_pool_t *pool );
+
+/**
+ * Create new Route header from the pool.
+ *
+ * @param pool The pool.
+ * @return A new instance of "Route" header.
+ */
+PJ_DECL(pjsip_route_hdr*) pjsip_route_hdr_create( pj_pool_t *pool );
+
+/**
+ * Convert generic routing header to Record-Route header.
+ *
+ * @param r The generic routing header, or a "Routing" header.
+ * @return Record-Route header.
+ */
+PJ_DECL(pjsip_rr_hdr*) pjsip_routing_hdr_set_rr( pjsip_routing_hdr *r );
+
+/**
+ * Convert generic routing header to "Route" header.
+ *
+ * @param r The generic routing header, or a "Record-Route" header.
+ * @return "Route" header.
+ */
+PJ_DECL(pjsip_route_hdr*) pjsip_routing_hdr_set_route( pjsip_routing_hdr *r );
+
+/**
+ * @}
+ */
+
+///////////////////////////////////////////////////////////////////////////////
+/**
+ * @defgroup PJSIP_MSG_HDR_REQUIRE Header Field: Require
+ * @brief Require header field.
+ * @ingroup PJSIP_MSG
+ * @{
+ */
+typedef pjsip_generic_array_hdr pjsip_require_hdr;
+
+/**
+ * Create new Require header instance.
+ *
+ * @param pool The pool.
+ *
+ * @return New Require header instance.
+ */
+PJ_DECL(pjsip_require_hdr*) pjsip_require_hdr_create(pj_pool_t *pool);
+
+
+/**
+ * @}
+ */
+
+
+///////////////////////////////////////////////////////////////////////////////
+/**
+ * @defgroup PJSIP_MSG_HDR_RETRY_AFTER Header Field: Retry-After
+ * @brief Retry-After header field.
+ * @ingroup PJSIP_MSG
+ * @{
+ */
+typedef pjsip_generic_int_hdr pjsip_retry_after_hdr;
+
+/**
+ * Create new Retry-After header instance.
+ *
+ * @param pool The pool.
+ *
+ * @return New Retry-After header instance.
+ */
+PJ_DECL(pjsip_retry_after_hdr*) pjsip_retry_after_hdr_create(pj_pool_t *pool);
+
+
+/**
+ * @}
+ */
+
+///////////////////////////////////////////////////////////////////////////////
+/**
+ * @defgroup PJSIP_MSG_HDR_SUPPORTED Header Field: Supported
+ * @brief Supported header field.
+ * @ingroup PJSIP_MSG
+ * @{
+ */
+typedef pjsip_generic_array_hdr pjsip_supported_hdr;
+
+/**
+ * Create new Supported header instance.
+ *
+ * @param pool The pool.
+ *
+ * @return New Supported header instance.
+ */
+PJ_DECL(pjsip_supported_hdr*) pjsip_supported_hdr_create(pj_pool_t *pool);
+
+
+/**
+ * @}
+ */
+
+///////////////////////////////////////////////////////////////////////////////
+/**
+ * @defgroup PJSIP_MSG_HDR_UNSUPPORTED Header Field: Unsupported
+ * @brief Unsupported header field.
+ * @ingroup PJSIP_MSG
+ * @{
+ */
+typedef pjsip_generic_array_hdr pjsip_unsupported_hdr;
+
+/**
+ * Create new Unsupported header instance.
+ *
+ * @param pool The pool.
+ *
+ * @return New Unsupported header instance.
+ */
+PJ_DECL(pjsip_unsupported_hdr*) pjsip_unsupported_hdr_create(pj_pool_t *pool);
+
+
+/**
+ * @}
+ */
+
+
+///////////////////////////////////////////////////////////////////////////////
+/**
+ * @defgroup PJSIP_MSG_HDR_VIA Header Field: Via
+ * @brief Via header field.
+ * @ingroup PJSIP_MSG
+ * @{
+ */
+/**
+ * SIP Via header.
+ * In this implementation, Via header can only have one element in each header.
+ * If a message arrives with multiple elements in a single Via, then they will
+ * be split up into multiple Via headers.
+ */
+typedef struct pjsip_via_hdr
+{
+ PJSIP_DECL_HDR_MEMBER(struct pjsip_via_hdr);
+ pj_str_t transport; /**< Transport type. */
+ pjsip_host_port sent_by; /**< Host and optional port */
+ int ttl_param; /**< TTL parameter, or -1 if it's not specified. */
+ int rport_param; /**< "rport" parameter, 0 to specify without
+ port number, -1 means doesn't exist. */
+ pj_str_t maddr_param; /**< "maddr" parameter. */
+ pj_str_t recvd_param; /**< "received" parameter. */
+ pj_str_t branch_param; /**< "branch" parameter. */
+ pjsip_param other_param; /**< Other parameters, concatenated as single string. */
+ pj_str_t comment; /**< Comment. */
+} pjsip_via_hdr;
+
+/**
+ * Create a new Via header.
+ *
+ * @param pool The pool.
+ * @return A new "Via" header instance.
+ */
+PJ_DECL(pjsip_via_hdr*) pjsip_via_hdr_create( pj_pool_t *pool );
+
+/**
+ * @}
+ */
+
+/**
+ * @bug Once a header is put in the message, the header CAN NOT be put in
+ * other list. Solution:
+ * - always clone header in the message.
+ * - create a list node for each header in the message.
+ */
+
+
+///////////////////////////////////////////////////////////////////////////////
+/**
+ * @defgroup PJSIP_MSG_HDR_UNIMP Unimplemented Header Fields
+ * @brief Unimplemented header fields.
+ * @ingroup PJSIP_MSG
+ * @{
+ */
+/** Accept-Encoding header. */
+typedef pjsip_generic_string_hdr pjsip_accept_encoding_hdr;
+
+/** Create Accept-Encoding header. */
+#define pjsip_accept_encoding_hdr_create pjsip_generic_string_hdr_create
+
+/** Accept-Language header. */
+typedef pjsip_generic_string_hdr pjsip_accept_lang_hdr;
+
+/** Create Accept-Language header. */
+#define pjsip_accept_lang_hdr_create pjsip_generic_string_hdr_create
+
+/** Alert-Info header. */
+typedef pjsip_generic_string_hdr pjsip_alert_info_hdr;
+
+/** Create Alert-Info header. */
+#define pjsip_alert_info_hdr_create pjsip_generic_string_hdr_create
+
+/** Authentication-Info header. */
+typedef pjsip_generic_string_hdr pjsip_auth_info_hdr;
+
+/** Create Authentication-Info header. */
+#define pjsip_auth_info_hdr_create pjsip_generic_string_hdr_create
+
+/** Call-Info header. */
+typedef pjsip_generic_string_hdr pjsip_call_info_hdr;
+
+/** Create Call-Info header. */
+#define pjsip_call_info_hdr_create pjsip_generic_string_hdr_create
+
+/** Content-Disposition header. */
+typedef pjsip_generic_string_hdr pjsip_content_disposition_hdr;
+
+/** Create Content-Disposition header. */
+#define pjsip_content_disposition_hdr_create pjsip_generic_string_hdr_create
+
+/** Content-Encoding header. */
+typedef pjsip_generic_string_hdr pjsip_content_encoding_hdr;
+
+/** Create Content-Encoding header. */
+#define pjsip_content_encoding_hdr_create pjsip_generic_string_hdr_create
+
+/** Content-Language header. */
+typedef pjsip_generic_string_hdr pjsip_content_lang_hdr;
+
+/** Create Content-Language header. */
+#define pjsip_content_lang_hdr_create pjsip_generic_string_hdr_create
+
+/** Date header. */
+typedef pjsip_generic_string_hdr pjsip_date_hdr;
+
+/** Create Date header. */
+#define pjsip_date_hdr_create pjsip_generic_string_hdr_create
+
+/** Error-Info header. */
+typedef pjsip_generic_string_hdr pjsip_err_info_hdr;
+
+/** Create Error-Info header. */
+#define pjsip_err_info_hdr_create pjsip_generic_string_hdr_create
+
+/** In-Reply-To header. */
+typedef pjsip_generic_string_hdr pjsip_in_reply_to_hdr;
+
+/** Create In-Reply-To header. */
+#define pjsip_in_reply_to_hdr_create pjsip_generic_string_hdr_create
+
+/** MIME-Version header. */
+typedef pjsip_generic_string_hdr pjsip_mime_version_hdr;
+
+/** Create MIME-Version header. */
+#define pjsip_mime_version_hdr_create pjsip_generic_string_hdr_create
+
+/** Organization header. */
+typedef pjsip_generic_string_hdr pjsip_organization_hdr;
+
+/** Create Organization header. */
+#define pjsip_organization_hdr_create pjsip_genric_string_hdr_create
+
+/** Priority header. */
+typedef pjsip_generic_string_hdr pjsip_priority_hdr;
+
+/** Create Priority header. */
+#define pjsip_priority_hdr_create pjsip_generic_string_hdr_create
+
+/** Proxy-Require header. */
+typedef pjsip_generic_string_hdr pjsip_proxy_require_hdr;
+
+/** Reply-To header. */
+typedef pjsip_generic_string_hdr pjsip_reply_to_hdr;
+
+/** Create Reply-To header. */
+#define pjsip_reply_to_hdr_create pjsip_generic_string_hdr_create
+
+/** Server header. */
+typedef pjsip_generic_string_hdr pjsip_server_hdr;
+
+/** Create Server header. */
+#define pjsip_server_hdr_create pjsip_generic_string_hdr_create
+
+/** Subject header. */
+typedef pjsip_generic_string_hdr pjsip_subject_hdr;
+
+/** Create Subject header. */
+#define pjsip_subject_hdr_create pjsip_generic_string_hdr_create
+
+/** Timestamp header. */
+typedef pjsip_generic_string_hdr pjsip_timestamp_hdr;
+
+/** Create Timestamp header. */
+#define pjsip_timestamp_hdr_create pjsip_generic_string_hdr_create
+
+/** User-Agent header. */
+typedef pjsip_generic_string_hdr pjsip_user_agent_hdr;
+
+/** Create User-Agent header. */
+#define pjsip_user_agent_hdr_create pjsip_generic_string_hdr_create
+
+/** Warning header. */
+typedef pjsip_generic_string_hdr pjsip_warning_hdr;
+
+/** Create Warning header. */
+#define pjsip_warning_hdr_create pjsip_generic_string_hdr_create
+
+/**
+ * @}
+ */
+
+/**
+ * @} // PJSIP_MSG
+ */
+
+/*
+ * Include inline definitions.
+ */
+#if PJ_FUNCTIONS_ARE_INLINED
+# include <pjsip/sip_msg_i.h>
+#endif
+
+
+PJ_END_DECL
+
+#endif /* __PJSIP_SIP_MSG_H__ */
+
diff --git a/pjsip/include/pjsip/sip_msg_i.h b/pjsip/include/pjsip/sip_msg_i.h
index 4a545ca8..c6c87b9a 100644
--- a/pjsip/include/pjsip/sip_msg_i.h
+++ b/pjsip/include/pjsip/sip_msg_i.h
@@ -1,29 +1,29 @@
-/* $Id$ */
-/*
- * Copyright (C) 2003-2006 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
- */
-
-PJ_IDEF(void) pjsip_msg_add_hdr( pjsip_msg *msg, pjsip_hdr *hdr )
-{
- pj_list_insert_before(&msg->hdr, hdr);
-}
-
-PJ_IDEF(void) pjsip_msg_insert_first_hdr( pjsip_msg *msg, pjsip_hdr *hdr )
-{
- pj_list_insert_after(&msg->hdr, hdr);
-}
-
+/* $Id$ */
+/*
+ * Copyright (C) 2003-2006 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
+ */
+
+PJ_IDEF(void) pjsip_msg_add_hdr( pjsip_msg *msg, pjsip_hdr *hdr )
+{
+ pj_list_insert_before(&msg->hdr, hdr);
+}
+
+PJ_IDEF(void) pjsip_msg_insert_first_hdr( pjsip_msg *msg, pjsip_hdr *hdr )
+{
+ pj_list_insert_after(&msg->hdr, hdr);
+}
+
diff --git a/pjsip/include/pjsip/sip_parser.h b/pjsip/include/pjsip/sip_parser.h
index 6c30e837..2b4e6c14 100644
--- a/pjsip/include/pjsip/sip_parser.h
+++ b/pjsip/include/pjsip/sip_parser.h
@@ -1,353 +1,353 @@
-/* $Id$ */
-/*
- * Copyright (C) 2003-2006 Benny Prijono <benny@prijono.org>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
-#ifndef __PJSIP_SIP_PARSER_H__
-#define __PJSIP_SIP_PARSER_H__
-
-/**
- * @file sip_parser.h
- * @brief SIP Message Parser
- */
-
-#include <pjsip/sip_types.h>
-#include <pjlib-util/scanner.h>
-#include <pj/list.h>
-
-PJ_BEGIN_DECL
-
-/**
- * @defgroup PJSIP_PARSER SIP Message Parser
- * @ingroup PJSIP
- * @{
- */
-
-/**
- * URI Parsing options.
- */
-enum
-{
- /** If this option is specified, function #pjsip_parse_uri will return
- * the URI object as pjsip_name_addr instead of the corresponding
- * URI object.
- */
- PJSIP_PARSE_URI_AS_NAMEADDR = 1,
-
- /** If this option is specified, function #pjsip_parse_uri and other
- * internal functions that this function calls will parse URI according
- * to convention for parsing From/To header. For example, when the URI
- * is not enclosed in brackets ("<" and ">"), all parameters will not
- * be stored to the URI (it will be stored to the header).
- */
- PJSIP_PARSE_URI_IN_FROM_TO_HDR = 2,
-};
-
-/**
- * Parser syntax error exception value.
- */
-#define PJSIP_SYN_ERR_EXCEPTION 1
-
-/**
- * This structure is used to get error reporting from parser.
- */
-typedef struct pjsip_parser_err_report
-{
- PJ_DECL_LIST_MEMBER(struct pjsip_parser_err_report);
- int exception_code; /**< Error exception (e.g. PJSIP_SYN_ERR_EXCEPTION) */
- int line; /**< Line number. */
- int col; /**< Column number. */
- pj_str_t hname; /**< Header name, if any. */
-} pjsip_parser_err_report;
-
-
-/**
- * Parsing context, the default argument for parsing functions.
- */
-typedef struct pjsip_parse_ctx
-{
- pj_scanner *scanner; /**< The scanner. */
- pj_pool_t *pool; /**< The pool. */
- pjsip_rx_data *rdata; /**< Optional rdata. */
-} pjsip_parse_ctx;
-
-
-/**
- * Type of function to parse header. The parsing function must follow these
- * specification:
- * - It must not modify the input text.
- * - The hname and HCOLON has been parsed prior to invoking the handler.
- * - It returns the header instance on success.
- * - For error reporting, it must throw PJSIP_SYN_ERR_EXCEPTION exception
- * instead of just returning NULL.
- * When exception is thrown, the return value is ignored.
- * - It must read the header separator after finished reading the header
- * body. The separator types are described below, and if they don't exist,
- * exception must be thrown. Header separator can be a:
- * - newline, such as when the header is part of a SIP message.
- * - ampersand, such as when the header is part of an URI.
- * - for the last header, these separator is optional since parsing
- * can be terminated when seeing EOF.
- */
-typedef pjsip_hdr* (pjsip_parse_hdr_func)(pjsip_parse_ctx *context);
-
-/**
- * Type of function to parse URI scheme.
- * Most of the specification of header parser handler (pjsip_parse_hdr_func)
- * also applies here (except the separator part).
- */
-typedef void* (pjsip_parse_uri_func)(pj_scanner *scanner, pj_pool_t *pool);
-
-/**
- * Register header parser handler. The parser handler MUST follow the
- * specification of header parser handler function. New registration
- * overwrites previous registration with the same name.
- *
- * @param hname The header name.
- * @param hshortname The short header name or NULL.
- * @param fptr The pointer to function to parser the header.
- *
- * @return PJ_SUCCESS if success, or the appropriate error code.
- */
-PJ_DECL(pj_status_t) pjsip_register_hdr_parser( const char *hname,
- const char *hshortname,
- pjsip_parse_hdr_func *fptr);
-
-/**
- * Unregister previously registered header parser handler.
- * All the arguments MUST exactly equal to the value specified upon
- * registration of the handler.
- *
- * @param hname The header name registered.
- * @param hshortname The short header name registered, or NULL.
- *
- * @return zero if unregistration was successfull.
- */
-PJ_DECL(pj_status_t) pjsip_unregister_hdr_parser( const char *hname,
- const char *hshortname,
- pjsip_parse_hdr_func *fptr);
-
-/**
- * Register URI scheme parser handler.
- *
- * @param scheme The URI scheme registered.
- * @param func The URI parser function.
- *
- * @return zero on success.
- */
-PJ_DECL(pj_status_t) pjsip_register_uri_parser( const char *scheme,
- pjsip_parse_uri_func *func);
-
-/**
- * Unregister URI scheme parser handler.
- * All the arguments MUST exactly equal to the value specified upon
- * registration of the handler.
- *
- * @param scheme The URI scheme as registered previously.
- * @param func The function handler as registered previously.
- *
- * @return zero if the registration was successfull.
- */
-PJ_DECL(pj_status_t) pjsip_unregister_uri_parser( const char *scheme,
- pjsip_parse_uri_func *func);
-
-/**
- * Parse an URI in the input and return the correct instance of URI.
- *
- * @param pool The pool to get memory allocations.
- * @param buf The input buffer, which size must be at least (size+1)
- * because the function will temporarily put NULL
- * termination at the end of the buffer during parsing.
- * @param size The length of the string (not counting NULL terminator).
- * @param options If no options are given (value is zero), the object
- * returned is dependent on the syntax of the URI,
- * eg. basic SIP URL, TEL URL, or name address.
- * If option PJSIP_PARSE_URI_AS_NAMEADDR is given,
- * then the returned object is always name address object,
- * with the relevant URI object contained in the name
- * address object.
- * @return The URI or NULL when failed. No exception is thrown by
- * this function (or any public parser functions).
- */
-PJ_DECL(pjsip_uri*) pjsip_parse_uri( pj_pool_t *pool,
- char *buf, pj_size_t size,
- unsigned option);
-
-/**
- * Parse a packet buffer and build a full SIP message from the packet. This
- * function parses all parts of the message, including request/status line,
- * all headers, and the message body. The message body however is only
- * treated as a text block, ie. the function will not try to parse the content
- * of the body.
- *
- * @param pool The pool to allocate memory.
- * @param buf The input buffer, which size must be at least (size+1)
- * because the function will temporarily put NULL
- * termination at the end of the buffer during parsing.
- * @param size The length of the string (not counting NULL terminator).
- * @param err_list If this parameter is not NULL, then the parser will
- * put error messages during parsing in this list.
- *
- * @return The message or NULL when failed. No exception is thrown
- * by this function (or any public parser functions).
- */
-PJ_DECL(pjsip_msg *) pjsip_parse_msg( pj_pool_t *pool,
- char *buf, pj_size_t size,
- pjsip_parser_err_report *err_list);
-
-
-/**
- * Parse a packet buffer and build a rdata. The resulting message will be
- * stored in \c msg field in the \c rdata. This behaves pretty much like
- * #pjsip_parse_msg(), except that it will also initialize the header fields
- * in the \c rdata.
- *
- * This function is normally called by the transport layer.
- *
- * @param buf The input buffer
- * @param buf The input buffer, which size must be at least (size+1)
- * because the function will temporarily put NULL
- * termination at the end of the buffer during parsing.
- * @param size The length of the string (not counting NULL terminator).
- * @param rdata The receive data buffer to store the message and
- * its elements.
- *
- * @return The message inside the rdata if successfull, or NULL.
- */
-PJ_DECL(pjsip_msg *) pjsip_parse_rdata( char *buf, pj_size_t size,
- pjsip_rx_data *rdata );
-
-/**
- * Check incoming packet to see if a (probably) valid SIP message has been
- * received.
- *
- * @param buf The input buffer, which must be NULL terminated.
- * @param size The buffer size.
- * @param msg_size [out] If message is valid, this parameter will contain
- * the size of the SIP message (including body, if any).
- *
- * @return PJ_SUCCESS if a message is found, or an error code.
- */
-PJ_DECL(pj_status_t) pjsip_find_msg(const char *buf,
- pj_size_t size,
- pj_bool_t is_datagram,
- pj_size_t *msg_size);
-
-/**
- * Parse the content of a header and return the header instance.
- * This function parses the content of a header (ie. part after colon) according
- * to the expected name, and will return the correct instance of header.
- *
- * @param pool Pool to allocate memory for the header.
- * @param hname Header name which is used to find the correct function
- * to parse the header.
- * @param line Header content, which size must be at least size+1.
- * @param size The length of the string (not counting NULL terminator,
- * if any).
- * @param parsed_len If the value is not NULL, then upon return the function
- * will fill the pointer with the length of the string
- * that has been parsed. This is usefull for two purposes,
- * one is when the string may contain more than one header
- * lines, and two when an error happen the value can
- * pinpoint the location of the error in the buffer.
- *
- * @return The instance of the header if parsing was successfull,
- * or otherwise a NULL pointer will be returned.
- */
-PJ_DECL(void*) pjsip_parse_hdr( pj_pool_t *pool, const pj_str_t *hname,
- char *line, pj_size_t size,
- int *parsed_len);
-
-/**
- * Parse header line(s). Multiple headers can be parsed by this function.
- * When there are multiple headers, the headers MUST be separated by either
- * a newline (as in SIP message) or ampersand mark (as in URI). This separator
- * however is optional for the last header.
- *
- * @param pool the pool.
- * @param buf the input text to parse.
- * @param size the text length.
- * @param hlist the header list to store the parsed headers. This list must
- * have been initialized before calling this function.
- * @return zero if successfull, or -1 if error is encountered. Upon error,
- * the \a hlist argument MAY contain successfully parsed headers.
- */
-PJ_DECL(pj_status_t) pjsip_parse_headers( pj_pool_t *pool,
- char *input, pj_size_t size,
- pj_list *hlist );
-
-
-/*
- * Various specification used in parsing, exported here as extern for other
- * parsers.
- */
-extern pj_cis_t
- pjsip_HOST_SPEC, /**< For scanning host part. */
- pjsip_DIGIT_SPEC, /**< Decimal digits */
- pjsip_ALPHA_SPEC, /**< Alpha (A-Z, a-z) */
- pjsip_ALNUM_SPEC, /**< Decimal + Alpha. */
- pjsip_TOKEN_SPEC, /**< Token. */
- pjsip_HEX_SPEC, /**< Hexadecimal digits. */
- pjsip_PARAM_CHAR_SPEC, /**< For scanning pname (or pvalue when it's
- not quoted.) */
- pjsip_HDR_CHAR_SPEC, /**< Chars in hname/havalue in URL. */
- pjsip_PROBE_USER_HOST_SPEC, /**< Hostname characters. */
- pjsip_PASSWD_SPEC, /**< Password. */
- pjsip_USER_SPEC, /**< User */
- pjsip_NEWLINE_OR_EOF_SPEC, /**< For eating up header.*/
- pjsip_DISPLAY_SCAN_SPEC; /**< Used when searching for display name. */
-
-/*
- * Various string constants.
- */
-extern const pj_str_t pjsip_USER_STR,
- pjsip_METHOD_STR,
- pjsip_TRANSPORT_STR,
- pjsip_MADDR_STR,
- pjsip_LR_STR,
- pjsip_SIP_STR,
- pjsip_SIPS_STR,
- pjsip_TEL_STR,
- pjsip_BRANCH_STR,
- pjsip_TTL_STR,
- pjsip_PNAME_STR,
- pjsip_Q_STR,
- pjsip_EXPIRES_STR,
- pjsip_TAG_STR;
-
-/*
- * Parser utilities.
- */
-enum
-{
- PJSIP_PARSE_REMOVE_QUOTE = 1,
-};
-
-void pjsip_parse_param_imp( pj_scanner *scanner,
- pj_str_t *pname, pj_str_t *pvalue,
- unsigned opt);
-void pjsip_concat_param_imp( pj_str_t *param, pj_pool_t *pool,
- const pj_str_t *pname, const pj_str_t *pvalue, int sepchar);
-void pjsip_parse_end_hdr_imp ( pj_scanner *scanner );
-
-/**
- * @}
- */
-
-PJ_END_DECL
-
-#endif /* __PJSIP_SIP_PARSER_H__ */
-
+/* $Id$ */
+/*
+ * Copyright (C) 2003-2006 Benny Prijono <benny@prijono.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#ifndef __PJSIP_SIP_PARSER_H__
+#define __PJSIP_SIP_PARSER_H__
+
+/**
+ * @file sip_parser.h
+ * @brief SIP Message Parser
+ */
+
+#include <pjsip/sip_types.h>
+#include <pjlib-util/scanner.h>
+#include <pj/list.h>
+
+PJ_BEGIN_DECL
+
+/**
+ * @defgroup PJSIP_PARSER SIP Message Parser
+ * @ingroup PJSIP
+ * @{
+ */
+
+/**
+ * URI Parsing options.
+ */
+enum
+{
+ /** If this option is specified, function #pjsip_parse_uri will return
+ * the URI object as pjsip_name_addr instead of the corresponding
+ * URI object.
+ */
+ PJSIP_PARSE_URI_AS_NAMEADDR = 1,
+
+ /** If this option is specified, function #pjsip_parse_uri and other
+ * internal functions that this function calls will parse URI according
+ * to convention for parsing From/To header. For example, when the URI
+ * is not enclosed in brackets ("<" and ">"), all parameters will not
+ * be stored to the URI (it will be stored to the header).
+ */
+ PJSIP_PARSE_URI_IN_FROM_TO_HDR = 2,
+};
+
+/**
+ * Parser syntax error exception value.
+ */
+#define PJSIP_SYN_ERR_EXCEPTION 1
+
+/**
+ * This structure is used to get error reporting from parser.
+ */
+typedef struct pjsip_parser_err_report
+{
+ PJ_DECL_LIST_MEMBER(struct pjsip_parser_err_report);
+ int exception_code; /**< Error exception (e.g. PJSIP_SYN_ERR_EXCEPTION) */
+ int line; /**< Line number. */
+ int col; /**< Column number. */
+ pj_str_t hname; /**< Header name, if any. */
+} pjsip_parser_err_report;
+
+
+/**
+ * Parsing context, the default argument for parsing functions.
+ */
+typedef struct pjsip_parse_ctx
+{
+ pj_scanner *scanner; /**< The scanner. */
+ pj_pool_t *pool; /**< The pool. */
+ pjsip_rx_data *rdata; /**< Optional rdata. */
+} pjsip_parse_ctx;
+
+
+/**
+ * Type of function to parse header. The parsing function must follow these
+ * specification:
+ * - It must not modify the input text.
+ * - The hname and HCOLON has been parsed prior to invoking the handler.
+ * - It returns the header instance on success.
+ * - For error reporting, it must throw PJSIP_SYN_ERR_EXCEPTION exception
+ * instead of just returning NULL.
+ * When exception is thrown, the return value is ignored.
+ * - It must read the header separator after finished reading the header
+ * body. The separator types are described below, and if they don't exist,
+ * exception must be thrown. Header separator can be a:
+ * - newline, such as when the header is part of a SIP message.
+ * - ampersand, such as when the header is part of an URI.
+ * - for the last header, these separator is optional since parsing
+ * can be terminated when seeing EOF.
+ */
+typedef pjsip_hdr* (pjsip_parse_hdr_func)(pjsip_parse_ctx *context);
+
+/**
+ * Type of function to parse URI scheme.
+ * Most of the specification of header parser handler (pjsip_parse_hdr_func)
+ * also applies here (except the separator part).
+ */
+typedef void* (pjsip_parse_uri_func)(pj_scanner *scanner, pj_pool_t *pool);
+
+/**
+ * Register header parser handler. The parser handler MUST follow the
+ * specification of header parser handler function. New registration
+ * overwrites previous registration with the same name.
+ *
+ * @param hname The header name.
+ * @param hshortname The short header name or NULL.
+ * @param fptr The pointer to function to parser the header.
+ *
+ * @return PJ_SUCCESS if success, or the appropriate error code.
+ */
+PJ_DECL(pj_status_t) pjsip_register_hdr_parser( const char *hname,
+ const char *hshortname,
+ pjsip_parse_hdr_func *fptr);
+
+/**
+ * Unregister previously registered header parser handler.
+ * All the arguments MUST exactly equal to the value specified upon
+ * registration of the handler.
+ *
+ * @param hname The header name registered.
+ * @param hshortname The short header name registered, or NULL.
+ *
+ * @return zero if unregistration was successfull.
+ */
+PJ_DECL(pj_status_t) pjsip_unregister_hdr_parser( const char *hname,
+ const char *hshortname,
+ pjsip_parse_hdr_func *fptr);
+
+/**
+ * Register URI scheme parser handler.
+ *
+ * @param scheme The URI scheme registered.
+ * @param func The URI parser function.
+ *
+ * @return zero on success.
+ */
+PJ_DECL(pj_status_t) pjsip_register_uri_parser( const char *scheme,
+ pjsip_parse_uri_func *func);
+
+/**
+ * Unregister URI scheme parser handler.
+ * All the arguments MUST exactly equal to the value specified upon
+ * registration of the handler.
+ *
+ * @param scheme The URI scheme as registered previously.
+ * @param func The function handler as registered previously.
+ *
+ * @return zero if the registration was successfull.
+ */
+PJ_DECL(pj_status_t) pjsip_unregister_uri_parser( const char *scheme,
+ pjsip_parse_uri_func *func);
+
+/**
+ * Parse an URI in the input and return the correct instance of URI.
+ *
+ * @param pool The pool to get memory allocations.
+ * @param buf The input buffer, which size must be at least (size+1)
+ * because the function will temporarily put NULL
+ * termination at the end of the buffer during parsing.
+ * @param size The length of the string (not counting NULL terminator).
+ * @param options If no options are given (value is zero), the object
+ * returned is dependent on the syntax of the URI,
+ * eg. basic SIP URL, TEL URL, or name address.
+ * If option PJSIP_PARSE_URI_AS_NAMEADDR is given,
+ * then the returned object is always name address object,
+ * with the relevant URI object contained in the name
+ * address object.
+ * @return The URI or NULL when failed. No exception is thrown by
+ * this function (or any public parser functions).
+ */
+PJ_DECL(pjsip_uri*) pjsip_parse_uri( pj_pool_t *pool,
+ char *buf, pj_size_t size,
+ unsigned option);
+
+/**
+ * Parse a packet buffer and build a full SIP message from the packet. This
+ * function parses all parts of the message, including request/status line,
+ * all headers, and the message body. The message body however is only
+ * treated as a text block, ie. the function will not try to parse the content
+ * of the body.
+ *
+ * @param pool The pool to allocate memory.
+ * @param buf The input buffer, which size must be at least (size+1)
+ * because the function will temporarily put NULL
+ * termination at the end of the buffer during parsing.
+ * @param size The length of the string (not counting NULL terminator).
+ * @param err_list If this parameter is not NULL, then the parser will
+ * put error messages during parsing in this list.
+ *
+ * @return The message or NULL when failed. No exception is thrown
+ * by this function (or any public parser functions).
+ */
+PJ_DECL(pjsip_msg *) pjsip_parse_msg( pj_pool_t *pool,
+ char *buf, pj_size_t size,
+ pjsip_parser_err_report *err_list);
+
+
+/**
+ * Parse a packet buffer and build a rdata. The resulting message will be
+ * stored in \c msg field in the \c rdata. This behaves pretty much like
+ * #pjsip_parse_msg(), except that it will also initialize the header fields
+ * in the \c rdata.
+ *
+ * This function is normally called by the transport layer.
+ *
+ * @param buf The input buffer
+ * @param buf The input buffer, which size must be at least (size+1)
+ * because the function will temporarily put NULL
+ * termination at the end of the buffer during parsing.
+ * @param size The length of the string (not counting NULL terminator).
+ * @param rdata The receive data buffer to store the message and
+ * its elements.
+ *
+ * @return The message inside the rdata if successfull, or NULL.
+ */
+PJ_DECL(pjsip_msg *) pjsip_parse_rdata( char *buf, pj_size_t size,
+ pjsip_rx_data *rdata );
+
+/**
+ * Check incoming packet to see if a (probably) valid SIP message has been
+ * received.
+ *
+ * @param buf The input buffer, which must be NULL terminated.
+ * @param size The buffer size.
+ * @param msg_size [out] If message is valid, this parameter will contain
+ * the size of the SIP message (including body, if any).
+ *
+ * @return PJ_SUCCESS if a message is found, or an error code.
+ */
+PJ_DECL(pj_status_t) pjsip_find_msg(const char *buf,
+ pj_size_t size,
+ pj_bool_t is_datagram,
+ pj_size_t *msg_size);
+
+/**
+ * Parse the content of a header and return the header instance.
+ * This function parses the content of a header (ie. part after colon) according
+ * to the expected name, and will return the correct instance of header.
+ *
+ * @param pool Pool to allocate memory for the header.
+ * @param hname Header name which is used to find the correct function
+ * to parse the header.
+ * @param line Header content, which size must be at least size+1.
+ * @param size The length of the string (not counting NULL terminator,
+ * if any).
+ * @param parsed_len If the value is not NULL, then upon return the function
+ * will fill the pointer with the length of the string
+ * that has been parsed. This is usefull for two purposes,
+ * one is when the string may contain more than one header
+ * lines, and two when an error happen the value can
+ * pinpoint the location of the error in the buffer.
+ *
+ * @return The instance of the header if parsing was successfull,
+ * or otherwise a NULL pointer will be returned.
+ */
+PJ_DECL(void*) pjsip_parse_hdr( pj_pool_t *pool, const pj_str_t *hname,
+ char *line, pj_size_t size,
+ int *parsed_len);
+
+/**
+ * Parse header line(s). Multiple headers can be parsed by this function.
+ * When there are multiple headers, the headers MUST be separated by either
+ * a newline (as in SIP message) or ampersand mark (as in URI). This separator
+ * however is optional for the last header.
+ *
+ * @param pool the pool.
+ * @param buf the input text to parse.
+ * @param size the text length.
+ * @param hlist the header list to store the parsed headers. This list must
+ * have been initialized before calling this function.
+ * @return zero if successfull, or -1 if error is encountered. Upon error,
+ * the \a hlist argument MAY contain successfully parsed headers.
+ */
+PJ_DECL(pj_status_t) pjsip_parse_headers( pj_pool_t *pool,
+ char *input, pj_size_t size,
+ pj_list *hlist );
+
+
+/*
+ * Various specification used in parsing, exported here as extern for other
+ * parsers.
+ */
+extern pj_cis_t
+ pjsip_HOST_SPEC, /**< For scanning host part. */
+ pjsip_DIGIT_SPEC, /**< Decimal digits */
+ pjsip_ALPHA_SPEC, /**< Alpha (A-Z, a-z) */
+ pjsip_ALNUM_SPEC, /**< Decimal + Alpha. */
+ pjsip_TOKEN_SPEC, /**< Token. */
+ pjsip_HEX_SPEC, /**< Hexadecimal digits. */
+ pjsip_PARAM_CHAR_SPEC, /**< For scanning pname (or pvalue when it's
+ not quoted.) */
+ pjsip_HDR_CHAR_SPEC, /**< Chars in hname/havalue in URL. */
+ pjsip_PROBE_USER_HOST_SPEC, /**< Hostname characters. */
+ pjsip_PASSWD_SPEC, /**< Password. */
+ pjsip_USER_SPEC, /**< User */
+ pjsip_NEWLINE_OR_EOF_SPEC, /**< For eating up header.*/
+ pjsip_DISPLAY_SCAN_SPEC; /**< Used when searching for display name. */
+
+/*
+ * Various string constants.
+ */
+extern const pj_str_t pjsip_USER_STR,
+ pjsip_METHOD_STR,
+ pjsip_TRANSPORT_STR,
+ pjsip_MADDR_STR,
+ pjsip_LR_STR,
+ pjsip_SIP_STR,
+ pjsip_SIPS_STR,
+ pjsip_TEL_STR,
+ pjsip_BRANCH_STR,
+ pjsip_TTL_STR,
+ pjsip_PNAME_STR,
+ pjsip_Q_STR,
+ pjsip_EXPIRES_STR,
+ pjsip_TAG_STR;
+
+/*
+ * Parser utilities.
+ */
+enum
+{
+ PJSIP_PARSE_REMOVE_QUOTE = 1,
+};
+
+void pjsip_parse_param_imp( pj_scanner *scanner, pj_pool_t *pool,
+ pj_str_t *pname, pj_str_t *pvalue,
+ unsigned opt);
+void pjsip_concat_param_imp( pj_str_t *param, pj_pool_t *pool,
+ const pj_str_t *pname, const pj_str_t *pvalue, int sepchar);
+void pjsip_parse_end_hdr_imp ( pj_scanner *scanner );
+
+/**
+ * @}
+ */
+
+PJ_END_DECL
+
+#endif /* __PJSIP_SIP_PARSER_H__ */
+
diff --git a/pjsip/include/pjsip/sip_private.h b/pjsip/include/pjsip/sip_private.h
index 1c14e607..80417906 100644
--- a/pjsip/include/pjsip/sip_private.h
+++ b/pjsip/include/pjsip/sip_private.h
@@ -1,46 +1,46 @@
-/* $Id$ */
-/*
- * Copyright (C) 2003-2006 Benny Prijono <benny@prijono.org>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
-#ifndef __PJSIP_SIP_PRIVATE_H__
-#define __PJSIP_SIP_PRIVATE_H__
-
-/**
- * @file sip_private.h
- * @brief Private structures and functions for PJSIP Library.
- */
-
-#include <pjsip/sip_types.h>
-
-PJ_BEGIN_DECL
-
-/**
- * @defgroup PJSIP_PRIVATE Private structures and functions (PJSIP internals)
- * @ingroup PJSIP
- * @{
- */
-
-
-
-/**
- * @}
- */
-
-PJ_END_DECL
-
-#endif /* __PJSIP_PRIVATE_I_H__ */
-
+/* $Id$ */
+/*
+ * Copyright (C) 2003-2006 Benny Prijono <benny@prijono.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#ifndef __PJSIP_SIP_PRIVATE_H__
+#define __PJSIP_SIP_PRIVATE_H__
+
+/**
+ * @file sip_private.h
+ * @brief Private structures and functions for PJSIP Library.
+ */
+
+#include <pjsip/sip_types.h>
+
+PJ_BEGIN_DECL
+
+/**
+ * @defgroup PJSIP_PRIVATE Private structures and functions (PJSIP internals)
+ * @ingroup PJSIP
+ * @{
+ */
+
+
+
+/**
+ * @}
+ */
+
+PJ_END_DECL
+
+#endif /* __PJSIP_PRIVATE_I_H__ */
+
diff --git a/pjsip/include/pjsip/sip_resolve.h b/pjsip/include/pjsip/sip_resolve.h
index efc54ace..673de2a7 100644
--- a/pjsip/include/pjsip/sip_resolve.h
+++ b/pjsip/include/pjsip/sip_resolve.h
@@ -1,120 +1,120 @@
-/* $Id$ */
-/*
- * Copyright (C) 2003-2006 Benny Prijono <benny@prijono.org>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
-#ifndef __PJSIP_SIP_RESOLVE_H__
-#define __PJSIP_SIP_RESOLVE_H__
-
-/**
- * @file sip_resolve.h
- * @brief
- * This module contains the mechanism to resolve server address as specified by
- * RFC 3263 - Locating SIP Servers
- */
-
-#include <pjsip/sip_types.h>
-#include <pj/sock.h>
-
-PJ_BEGIN_DECL
-
-/**
- * @defgroup PJSIP_RESOLVE SIP Server Resolver
- * @ingroup PJSIP
- * @{
- */
-
-/**
- * Maximum number of addresses returned by the resolver.
- */
-#define PJSIP_MAX_RESOLVED_ADDRESSES 8
-
-typedef struct pjsip_server_addresses pjsip_server_addresses;
-
-/**
- * The server addresses returned by the resolver.
- */
-struct pjsip_server_addresses
-{
- /** Number of address records. */
- unsigned count;
-
- /** Address records. */
- struct
- {
- /** Preferable transport to be used to contact this address. */
- pjsip_transport_type_e type;
-
- /** The server's address. */
- pj_sockaddr_in addr;
-
- } entry[PJSIP_MAX_RESOLVED_ADDRESSES];
-
-};
-
-/**
- * The type of callback function to be called when resolver finishes the job.
- *
- * @param status The status of the operation, which is zero on success.
- * @param token The token that was associated with the job when application
- * call the resolve function.
- * @param addr The addresses resolved by the operation.
- */
-typedef void pjsip_resolver_callback(pj_status_t status,
- void *token,
- const struct pjsip_server_addresses *addr);
-
-/**
- * Create resolver engine.
- *
- * @param pool The Pool.
- * @return The resolver engine.
- */
-PJ_DECL(pjsip_resolver_t*) pjsip_resolver_create(pj_pool_t *pool);
-
-/**
- * Destroy resolver engine.
- *
- * @param resolver The resolver.
- */
-PJ_DECL(void) pjsip_resolver_destroy(pjsip_resolver_t *resolver);
-
-/**
- * Asynchronously resolve a SIP target host or domain according to rule
- * specified in RFC 3263 (Locating SIP Servers). When the resolving operation
- * has completed, the callback will be called.
- *
- * Note: at the moment we don't have implementation of RFC 3263 yet!
- *
- * @param resolver The resolver engine.
- * @param pool The pool to allocate resolver job.
- * @param target The target specification to be resolved.
- * @param token A user defined token to be passed back to callback function.
- * @param cb The callback function.
- */
-PJ_DECL(void) pjsip_resolve( pjsip_resolver_t *resolver,
- pj_pool_t *pool,
- pjsip_host_port *target,
- void *token,
- pjsip_resolver_callback *cb);
-
-/**
- * @}
- */
-
-PJ_END_DECL
-
-#endif /* __PJSIP_SIP_RESOLVE_H__ */
+/* $Id$ */
+/*
+ * Copyright (C) 2003-2006 Benny Prijono <benny@prijono.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#ifndef __PJSIP_SIP_RESOLVE_H__
+#define __PJSIP_SIP_RESOLVE_H__
+
+/**
+ * @file sip_resolve.h
+ * @brief
+ * This module contains the mechanism to resolve server address as specified by
+ * RFC 3263 - Locating SIP Servers
+ */
+
+#include <pjsip/sip_types.h>
+#include <pj/sock.h>
+
+PJ_BEGIN_DECL
+
+/**
+ * @defgroup PJSIP_RESOLVE SIP Server Resolver
+ * @ingroup PJSIP
+ * @{
+ */
+
+/**
+ * Maximum number of addresses returned by the resolver.
+ */
+#define PJSIP_MAX_RESOLVED_ADDRESSES 8
+
+typedef struct pjsip_server_addresses pjsip_server_addresses;
+
+/**
+ * The server addresses returned by the resolver.
+ */
+struct pjsip_server_addresses
+{
+ /** Number of address records. */
+ unsigned count;
+
+ /** Address records. */
+ struct
+ {
+ /** Preferable transport to be used to contact this address. */
+ pjsip_transport_type_e type;
+
+ /** The server's address. */
+ pj_sockaddr_in addr;
+
+ } entry[PJSIP_MAX_RESOLVED_ADDRESSES];
+
+};
+
+/**
+ * The type of callback function to be called when resolver finishes the job.
+ *
+ * @param status The status of the operation, which is zero on success.
+ * @param token The token that was associated with the job when application
+ * call the resolve function.
+ * @param addr The addresses resolved by the operation.
+ */
+typedef void pjsip_resolver_callback(pj_status_t status,
+ void *token,
+ const struct pjsip_server_addresses *addr);
+
+/**
+ * Create resolver engine.
+ *
+ * @param pool The Pool.
+ * @return The resolver engine.
+ */
+PJ_DECL(pjsip_resolver_t*) pjsip_resolver_create(pj_pool_t *pool);
+
+/**
+ * Destroy resolver engine.
+ *
+ * @param resolver The resolver.
+ */
+PJ_DECL(void) pjsip_resolver_destroy(pjsip_resolver_t *resolver);
+
+/**
+ * Asynchronously resolve a SIP target host or domain according to rule
+ * specified in RFC 3263 (Locating SIP Servers). When the resolving operation
+ * has completed, the callback will be called.
+ *
+ * Note: at the moment we don't have implementation of RFC 3263 yet!
+ *
+ * @param resolver The resolver engine.
+ * @param pool The pool to allocate resolver job.
+ * @param target The target specification to be resolved.
+ * @param token A user defined token to be passed back to callback function.
+ * @param cb The callback function.
+ */
+PJ_DECL(void) pjsip_resolve( pjsip_resolver_t *resolver,
+ pj_pool_t *pool,
+ pjsip_host_port *target,
+ void *token,
+ pjsip_resolver_callback *cb);
+
+/**
+ * @}
+ */
+
+PJ_END_DECL
+
+#endif /* __PJSIP_SIP_RESOLVE_H__ */
diff --git a/pjsip/include/pjsip/sip_transaction.h b/pjsip/include/pjsip/sip_transaction.h
index e7edceef..e95c66b1 100644
--- a/pjsip/include/pjsip/sip_transaction.h
+++ b/pjsip/include/pjsip/sip_transaction.h
@@ -1,271 +1,271 @@
-/* $Id$ */
-/*
- * Copyright (C) 2003-2006 Benny Prijono <benny@prijono.org>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
-#ifndef __PJSIP_SIP_TRANSACTION_H__
-#define __PJSIP_SIP_TRANSACTION_H__
-
-/**
- * @file sip_transaction.h
- * @brief SIP Transaction
- */
-
-#include <pjsip/sip_msg.h>
-#include <pjsip/sip_resolve.h>
-#include <pj/timer.h>
-
-PJ_BEGIN_DECL
-
-/**
- * @defgroup PJSIP_TRANSACT SIP Transaction
- * @ingroup PJSIP
- * @{
- */
-
-/* Forward decl. */
-struct pjsip_transaction;
-
-
-/**
- * Transaction state.
- */
-typedef enum pjsip_tsx_state_e
-{
- PJSIP_TSX_STATE_NULL,
- PJSIP_TSX_STATE_CALLING,
- PJSIP_TSX_STATE_TRYING,
- PJSIP_TSX_STATE_PROCEEDING,
- PJSIP_TSX_STATE_COMPLETED,
- PJSIP_TSX_STATE_CONFIRMED,
- PJSIP_TSX_STATE_TERMINATED,
- PJSIP_TSX_STATE_DESTROYED,
- PJSIP_TSX_STATE_MAX,
-} pjsip_tsx_state_e;
-
-
-/**
- * State of the transport in the transaction.
- * The transport is progressing independently of the transaction.
- */
-typedef enum pjsip_tsx_transport_state_e
-{
- PJSIP_TSX_TRANSPORT_STATE_NULL,
- PJSIP_TSX_TRANSPORT_STATE_RESOLVING,
- PJSIP_TSX_TRANSPORT_STATE_CONNECTING,
- PJSIP_TSX_TRANSPORT_STATE_FINAL,
-} pjsip_tsx_transport_state_e;
-
-
-/**
- * Transaction state.
- */
-struct pjsip_transaction
-{
- /*
- * Administrivia
- */
- pj_pool_t *pool; /**< Pool owned by the tsx. */
- pjsip_endpoint *endpt; /**< Endpoint instance. */
- pj_mutex_t *mutex; /**< Mutex for this tsx. */
- char obj_name[PJ_MAX_OBJ_NAME]; /**< Tsx name. */
- int tracing; /**< Tracing enabled? */
-
- /*
- * Transaction identification.
- */
- pjsip_role_e role; /**< Role (UAS or UAC) */
- pjsip_method method; /**< The method. */
- int cseq; /**< The CSeq */
- pj_str_t transaction_key;/**< hash table key. */
- pj_str_t branch; /**< The branch Id. */
-
- /*
- * State and status.
- */
- int status_code; /**< Last status code seen. */
- pjsip_tsx_state_e state; /**< State. */
- int handle_ack; /**< Should we handle ACK? */
-
- /** Handler according to current state. */
- pj_status_t (*state_handler)(struct pjsip_transaction *, pjsip_event *);
-
- /*
- * Transport.
- */
- pjsip_tsx_transport_state_e transport_state;/**< Transport's state. */
- pjsip_host_port dest_name; /**< Destination address. */
- pjsip_server_addresses remote_addr; /**< Addresses resolved. */
- int current_addr; /**< Address currently used. */
-
- pjsip_transport *transport; /**< Transport to use. */
-
- /*
- * Messages and timer.
- */
- pjsip_tx_data *last_tx; /**< Msg kept for retrans. */
- int has_unsent_msg; /**< Non-zero if tsx need to
- transmit msg once resolver
- completes. */
- int retransmit_count;/**< Retransmission count. */
- pj_timer_entry retransmit_timer;/**< Retransmit timer. */
- pj_timer_entry timeout_timer; /**< Timeout timer. */
-
- /** Module specific data. */
- void *module_data[PJSIP_MAX_MODULE];
-};
-
-
-/**
- * Create new transaction. Application would normally use
- * #pjsip_endpt_create_tsx rather than this function.
- *
- * @param pool Pool to use by the transaction.
- * @param endpt Endpoint.
- * @param p_tsx Pointer to return the transaction.
- *
- * @return PJ_SUCCESS or the appropriate error code.
- *
- * @see pjsip_endpt_create_tsx
- *
- */
-PJ_DEF(pj_status_t) pjsip_tsx_create( pj_pool_t *pool,
- pjsip_endpoint *endpt,
- pjsip_transaction **p_tsx);
-
-/**
- * Init transaction as UAC from the specified transmit data (\c tdata).
- * The transmit data must have a valid \c Request-Line and \c CSeq header.
- * If \c Route headers are present, it will be used to calculate remote
- * destination.
- *
- * If \c Via header does not exist, it will be created along with a unique
- * \c branch parameter. If it exists and contains branch parameter, then
- * the \c branch parameter will be used as is as the transaction key.
- *
- * The \c Route headers in the transmit data, if present, are used to
- * calculate remote destination.
- *
- * At the end of the function, the transaction will start resolving the
- * addresses of remote server to contact. Transport will be acquired as soon
- * as the resolving job completes.
- *
- * @param tsx The transaction.
- * @param tdata The transmit data.
- *
- * @return PJ_SUCCESS if successfull.
- */
-PJ_DECL(pj_status_t) pjsip_tsx_init_uac( pjsip_transaction *tsx,
- pjsip_tx_data *tdata);
-
-/**
- * Init transaction as UAS.
- *
- * @param tsx The transaction to be initialized.
- * @param rdata The received incoming request.
- *
- * @return PJ_SUCCESS if successfull.
- */
-PJ_DECL(pj_status_t) pjsip_tsx_init_uas( pjsip_transaction *tsx,
- pjsip_rx_data *rdata);
-
-/**
- * Process incoming message for this transaction.
- *
- * @param tsx The transaction.
- * @param rdata The incoming message.
- */
-PJ_DECL(void) pjsip_tsx_on_rx_msg( pjsip_transaction *tsx,
- pjsip_rx_data *rdata);
-
-/**
- * Transmit message with this transaction.
- *
- * @param tsx The transaction.
- * @param tdata The outgoing message.
- */
-PJ_DECL(void) pjsip_tsx_on_tx_msg( pjsip_transaction *tsx,
- pjsip_tx_data *tdata);
-
-
-/**
- * Transmit ACK message for 2xx/INVITE with this transaction. The ACK for
- * non-2xx/INVITE is automatically sent by the transaction.
- * This operation is only valid if the transaction is configured to handle ACK
- * (tsx->handle_ack is non-zero). If this attribute is not set, then the
- * transaction will comply with RFC-3261, i.e. it will set itself to
- * TERMINATED state when it receives 2xx/INVITE.
- *
- * @param tsx The transaction.
- * @param tdata The ACK request.
- */
-PJ_DECL(void) pjsip_tsx_on_tx_ack( pjsip_transaction *tsx,
- pjsip_tx_data *tdata);
-
-/**
- * Force terminate transaction.
- *
- * @param tsx The transaction.
- * @param code The status code to report.
- */
-PJ_DECL(void) pjsip_tsx_terminate( pjsip_transaction *tsx,
- int code );
-
-/**
- * Create transaction key, which is used to match incoming requests
- * or response (retransmissions) against transactions.
- *
- * @param pool The pool
- * @param key Output key.
- * @param role The role of the transaction.
- * @param method The method to be put as a key.
- * @param rdata The received data to calculate.
- *
- * @return PJ_SUCCESS or the appropriate error code.
- */
-PJ_DECL(pj_status_t) pjsip_tsx_create_key( pj_pool_t *pool,
- pj_str_t *key,
- pjsip_role_e role,
- const pjsip_method *method,
- const pjsip_rx_data *rdata );
-
-
-/**
- * @}
- */
-
-/*
- * Internal.
- */
-
-/*
- * Get the string name for the state.
- */
-PJ_DECL(const char *) pjsip_tsx_state_str(pjsip_tsx_state_e state);
-
-/*
- * Get the role name.
- */
-PJ_DECL(const char *) pjsip_role_name(pjsip_role_e role);
-
-
-/* Thread Local Storage ID for transaction lock (initialized by endpoint) */
-extern long pjsip_tsx_lock_tls_id;
-
-PJ_END_DECL
-
-#endif /* __PJSIP_TRANSACT_H__ */
-
+/* $Id$ */
+/*
+ * Copyright (C) 2003-2006 Benny Prijono <benny@prijono.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#ifndef __PJSIP_SIP_TRANSACTION_H__
+#define __PJSIP_SIP_TRANSACTION_H__
+
+/**
+ * @file sip_transaction.h
+ * @brief SIP Transaction
+ */
+
+#include <pjsip/sip_msg.h>
+#include <pjsip/sip_resolve.h>
+#include <pj/timer.h>
+
+PJ_BEGIN_DECL
+
+/**
+ * @defgroup PJSIP_TRANSACT SIP Transaction
+ * @ingroup PJSIP
+ * @{
+ */
+
+/* Forward decl. */
+struct pjsip_transaction;
+
+
+/**
+ * Transaction state.
+ */
+typedef enum pjsip_tsx_state_e
+{
+ PJSIP_TSX_STATE_NULL,
+ PJSIP_TSX_STATE_CALLING,
+ PJSIP_TSX_STATE_TRYING,
+ PJSIP_TSX_STATE_PROCEEDING,
+ PJSIP_TSX_STATE_COMPLETED,
+ PJSIP_TSX_STATE_CONFIRMED,
+ PJSIP_TSX_STATE_TERMINATED,
+ PJSIP_TSX_STATE_DESTROYED,
+ PJSIP_TSX_STATE_MAX,
+} pjsip_tsx_state_e;
+
+
+/**
+ * State of the transport in the transaction.
+ * The transport is progressing independently of the transaction.
+ */
+typedef enum pjsip_tsx_transport_state_e
+{
+ PJSIP_TSX_TRANSPORT_STATE_NULL,
+ PJSIP_TSX_TRANSPORT_STATE_RESOLVING,
+ PJSIP_TSX_TRANSPORT_STATE_CONNECTING,
+ PJSIP_TSX_TRANSPORT_STATE_FINAL,
+} pjsip_tsx_transport_state_e;
+
+
+/**
+ * Transaction state.
+ */
+struct pjsip_transaction
+{
+ /*
+ * Administrivia
+ */
+ pj_pool_t *pool; /**< Pool owned by the tsx. */
+ pjsip_endpoint *endpt; /**< Endpoint instance. */
+ pj_mutex_t *mutex; /**< Mutex for this tsx. */
+ char obj_name[PJ_MAX_OBJ_NAME]; /**< Tsx name. */
+ int tracing; /**< Tracing enabled? */
+
+ /*
+ * Transaction identification.
+ */
+ pjsip_role_e role; /**< Role (UAS or UAC) */
+ pjsip_method method; /**< The method. */
+ int cseq; /**< The CSeq */
+ pj_str_t transaction_key;/**< hash table key. */
+ pj_str_t branch; /**< The branch Id. */
+
+ /*
+ * State and status.
+ */
+ int status_code; /**< Last status code seen. */
+ pjsip_tsx_state_e state; /**< State. */
+ int handle_ack; /**< Should we handle ACK? */
+
+ /** Handler according to current state. */
+ pj_status_t (*state_handler)(struct pjsip_transaction *, pjsip_event *);
+
+ /*
+ * Transport.
+ */
+ pjsip_tsx_transport_state_e transport_state;/**< Transport's state. */
+ pjsip_host_port dest_name; /**< Destination address. */
+ pjsip_server_addresses remote_addr; /**< Addresses resolved. */
+ int current_addr; /**< Address currently used. */
+
+ pjsip_transport *transport; /**< Transport to use. */
+
+ /*
+ * Messages and timer.
+ */
+ pjsip_tx_data *last_tx; /**< Msg kept for retrans. */
+ int has_unsent_msg; /**< Non-zero if tsx need to
+ transmit msg once resolver
+ completes. */
+ int retransmit_count;/**< Retransmission count. */
+ pj_timer_entry retransmit_timer;/**< Retransmit timer. */
+ pj_timer_entry timeout_timer; /**< Timeout timer. */
+
+ /** Module specific data. */
+ void *module_data[PJSIP_MAX_MODULE];
+};
+
+
+/**
+ * Create new transaction. Application would normally use
+ * #pjsip_endpt_create_tsx rather than this function.
+ *
+ * @param pool Pool to use by the transaction.
+ * @param endpt Endpoint.
+ * @param p_tsx Pointer to return the transaction.
+ *
+ * @return PJ_SUCCESS or the appropriate error code.
+ *
+ * @see pjsip_endpt_create_tsx
+ *
+ */
+PJ_DEF(pj_status_t) pjsip_tsx_create( pj_pool_t *pool,
+ pjsip_endpoint *endpt,
+ pjsip_transaction **p_tsx);
+
+/**
+ * Init transaction as UAC from the specified transmit data (\c tdata).
+ * The transmit data must have a valid \c Request-Line and \c CSeq header.
+ * If \c Route headers are present, it will be used to calculate remote
+ * destination.
+ *
+ * If \c Via header does not exist, it will be created along with a unique
+ * \c branch parameter. If it exists and contains branch parameter, then
+ * the \c branch parameter will be used as is as the transaction key.
+ *
+ * The \c Route headers in the transmit data, if present, are used to
+ * calculate remote destination.
+ *
+ * At the end of the function, the transaction will start resolving the
+ * addresses of remote server to contact. Transport will be acquired as soon
+ * as the resolving job completes.
+ *
+ * @param tsx The transaction.
+ * @param tdata The transmit data.
+ *
+ * @return PJ_SUCCESS if successfull.
+ */
+PJ_DECL(pj_status_t) pjsip_tsx_init_uac( pjsip_transaction *tsx,
+ pjsip_tx_data *tdata);
+
+/**
+ * Init transaction as UAS.
+ *
+ * @param tsx The transaction to be initialized.
+ * @param rdata The received incoming request.
+ *
+ * @return PJ_SUCCESS if successfull.
+ */
+PJ_DECL(pj_status_t) pjsip_tsx_init_uas( pjsip_transaction *tsx,
+ pjsip_rx_data *rdata);
+
+/**
+ * Process incoming message for this transaction.
+ *
+ * @param tsx The transaction.
+ * @param rdata The incoming message.
+ */
+PJ_DECL(void) pjsip_tsx_on_rx_msg( pjsip_transaction *tsx,
+ pjsip_rx_data *rdata);
+
+/**
+ * Transmit message with this transaction.
+ *
+ * @param tsx The transaction.
+ * @param tdata The outgoing message.
+ */
+PJ_DECL(void) pjsip_tsx_on_tx_msg( pjsip_transaction *tsx,
+ pjsip_tx_data *tdata);
+
+
+/**
+ * Transmit ACK message for 2xx/INVITE with this transaction. The ACK for
+ * non-2xx/INVITE is automatically sent by the transaction.
+ * This operation is only valid if the transaction is configured to handle ACK
+ * (tsx->handle_ack is non-zero). If this attribute is not set, then the
+ * transaction will comply with RFC-3261, i.e. it will set itself to
+ * TERMINATED state when it receives 2xx/INVITE.
+ *
+ * @param tsx The transaction.
+ * @param tdata The ACK request.
+ */
+PJ_DECL(void) pjsip_tsx_on_tx_ack( pjsip_transaction *tsx,
+ pjsip_tx_data *tdata);
+
+/**
+ * Force terminate transaction.
+ *
+ * @param tsx The transaction.
+ * @param code The status code to report.
+ */
+PJ_DECL(void) pjsip_tsx_terminate( pjsip_transaction *tsx,
+ int code );
+
+/**
+ * Create transaction key, which is used to match incoming requests
+ * or response (retransmissions) against transactions.
+ *
+ * @param pool The pool
+ * @param key Output key.
+ * @param role The role of the transaction.
+ * @param method The method to be put as a key.
+ * @param rdata The received data to calculate.
+ *
+ * @return PJ_SUCCESS or the appropriate error code.
+ */
+PJ_DECL(pj_status_t) pjsip_tsx_create_key( pj_pool_t *pool,
+ pj_str_t *key,
+ pjsip_role_e role,
+ const pjsip_method *method,
+ const pjsip_rx_data *rdata );
+
+
+/**
+ * @}
+ */
+
+/*
+ * Internal.
+ */
+
+/*
+ * Get the string name for the state.
+ */
+PJ_DECL(const char *) pjsip_tsx_state_str(pjsip_tsx_state_e state);
+
+/*
+ * Get the role name.
+ */
+PJ_DECL(const char *) pjsip_role_name(pjsip_role_e role);
+
+
+/* Thread Local Storage ID for transaction lock (initialized by endpoint) */
+extern long pjsip_tsx_lock_tls_id;
+
+PJ_END_DECL
+
+#endif /* __PJSIP_TRANSACT_H__ */
+
diff --git a/pjsip/include/pjsip/sip_transport.h b/pjsip/include/pjsip/sip_transport.h
index 869641bd..dac608ff 100644
--- a/pjsip/include/pjsip/sip_transport.h
+++ b/pjsip/include/pjsip/sip_transport.h
@@ -1,635 +1,635 @@
-/* $Id$ */
-/*
- * Copyright (C) 2003-2006 Benny Prijono <benny@prijono.org>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
-#ifndef __PJSIP_SIP_TRANSPORT_H__
-#define __PJSIP_SIP_TRANSPORT_H__
-
-/**
- * @file sip_transport.h
- * @brief SIP Transport
- */
-
-#include <pjsip/sip_msg.h>
-#include <pjsip/sip_parser.h>
-#include <pj/sock.h>
-#include <pj/list.h>
-#include <pj/ioqueue.h>
-#include <pj/timer.h>
-
-PJ_BEGIN_DECL
-
-/**
- * @defgroup PJSIP_TRANSPORT SIP Transport
- * @ingroup PJSIP
- *
- * This is the low-level transport layer. Application normally won't need to
- * use this function, but instead can use transaction or higher layer API to
- * send and receive messages.
- *
- * @{
- */
-
-/*****************************************************************************
- *
- * GENERAL TRANSPORT (NAMES, TYPES, ETC.)
- *
- *****************************************************************************/
-
-/**
- * Flags for SIP transports.
- */
-enum pjsip_transport_flags_e
-{
- PJSIP_TRANSPORT_RELIABLE = 1, /**< Transport is reliable. */
- PJSIP_TRANSPORT_SECURE = 2, /**< Transport is secure. */
- PJSIP_TRANSPORT_DATAGRAM = 4, /**< Datagram based transport. */
-};
-
-/**
- * Check if transport tp is reliable.
- */
-#define PJSIP_TRANSPORT_IS_RELIABLE(tp) \
- ((tp)->flag & PJSIP_TRANSPORT_RELIABLE)
-
-/**
- * Get the transport type from the transport name.
- *
- * @param name Transport name, such as "TCP", or "UDP".
- *
- * @return The transport type, or PJSIP_TRANSPORT_UNSPECIFIED if
- * the name is not recognized as the name of supported
- * transport.
- */
-PJ_DECL(pjsip_transport_type_e)
-pjsip_transport_get_type_from_name(const pj_str_t *name);
-
-/**
- * Get the transport type for the specified flags.
- *
- * @param flag The transport flag.
- *
- * @return Transport type.
- */
-PJ_DECL(pjsip_transport_type_e)
-pjsip_transport_get_type_from_flag(unsigned flag);
-
-/**
- * Get transport flag from type.
- *
- * @param type Transport type.
- *
- * @return Transport flags.
- */
-PJ_DECL(unsigned)
-pjsip_transport_get_flag_from_type( pjsip_transport_type_e type );
-
-/**
- * Get the default SIP port number for the specified type.
- *
- * @param type Transport type.
- *
- * @return The port number, which is the default SIP port number for
- * the specified type.
- */
-PJ_DECL(int)
-pjsip_transport_get_default_port_for_type(pjsip_transport_type_e type);
-
-
-/*****************************************************************************
- *
- * RECEIVE DATA BUFFER.
- *
- *****************************************************************************/
-
-/**
- * A customized ioqueue async operation key which is used by transport
- * to locate rdata when a pending read operation completes.
- */
-typedef struct pjsip_rx_data_op_key
-{
- pj_ioqueue_op_key_t op_key;
- pjsip_rx_data *rdata;
-} pjsip_rx_data_op_key;
-
-
-/**
- * Incoming message buffer.
- * This structure keep all the information regarding the received message. This
- * buffer lifetime is only very short, normally after the transaction has been
- * called, this buffer will be deleted/recycled. So care must be taken when
- * allocating storage from the pool of this buffer.
- */
-struct pjsip_rx_data
-{
-
- /**
- * tp_info is part of rdata that remains static for the duration of the
- * buffer. It is initialized when the buffer was created by transport.
- */
- struct
- {
- /** Memory pool for this buffer. */
- pj_pool_t *pool;
-
- /** The transport object which received this packet. */
- pjsip_transport *transport;
-
- /** Ioqueue key. */
- pjsip_rx_data_op_key op_key;
-
- } tp_info;
-
-
- /**
- * pkt_info is initialized by transport when it receives an incoming
- * packet.
- */
- struct
- {
- /** Time when the message was received. */
- pj_time_val timestamp;
-
- /** Pointer to the original packet. */
- char packet[PJSIP_MAX_PKT_LEN];
-
- /** Zero termination for the packet. */
- pj_uint32_t zero;
-
- /** The length of the packet received. */
- int len;
-
- /** The source address from which the packet was received. */
- pj_sockaddr_in addr;
-
- /** The length of the source address. */
- int addr_len;
-
- } pkt_info;
-
-
- /**
- * msg_info is initialized by transport mgr (tpmgr) before this buffer
- * is passed to endpoint.
- */
- struct
- {
- /** Start of msg buffer. */
- char *msg_buf;
-
- /** Length fo message. */
- int len;
-
- /** The parsed message, if any. */
- pjsip_msg *msg;
-
- /** The Call-ID header as found in the message. */
- pj_str_t call_id;
-
- /** The From header as found in the message. */
- pjsip_from_hdr *from;
-
- /** The To header as found in the message. */
- pjsip_to_hdr *to;
-
- /** The topmost Via header as found in the message. */
- pjsip_via_hdr *via;
-
- /** The CSeq header as found in the message. */
- pjsip_cseq_hdr *cseq;
-
- /** Max forwards header. */
- pjsip_max_forwards_hdr *max_fwd;
-
- /** The first route header. */
- pjsip_route_hdr *route;
-
- /** The first record-route header. */
- pjsip_rr_hdr *record_route;
-
- /** Content-type header. */
- pjsip_ctype_hdr *ctype;
-
- /** Content-length header. */
- pjsip_clen_hdr *clen;
-
- /** The first Require header. */
- pjsip_require_hdr *require;
-
- /** The list of error generated by the parser when parsing
- this message.
- */
- pjsip_parser_err_report parse_err;
-
- } msg_info;
-
-
- /**
- * endpt_info is initialized by endpoint after this buffer reaches
- * endpoint.
- */
- struct
- {
- /**
- * This the transaction key generated for the message.
- */
- pj_str_t key;
-
- } endpt_info;
-
-};
-
-
-/*****************************************************************************
- *
- * TRANSMIT DATA BUFFER MANIPULATION.
- *
- *****************************************************************************/
-
-/** Customized ioqueue async operation key, used by transport to keep
- * callback parameters.
- */
-typedef struct pjsip_tx_data_op_key
-{
- pj_ioqueue_op_key_t key;
- pjsip_tx_data *tdata;
- void *token;
- void (*callback)(pjsip_transport*,void*,pj_ssize_t);
-} pjsip_tx_data_op_key;
-
-
-/**
- * Data structure for sending outgoing message. Application normally creates
- * this buffer by calling #pjsip_endpt_create_tdata.
- *
- * The lifetime of this buffer is controlled by the reference counter in this
- * structure, which is manipulated by calling #pjsip_tx_data_add_ref and
- * #pjsip_tx_data_dec_ref. When the reference counter has reached zero, then
- * this buffer will be destroyed.
- *
- * A transaction object normally will add reference counter to this buffer
- * when application calls #pjsip_tsx_on_tx_msg, because it needs to keep the
- * message for retransmission. The transaction will release the reference
- * counter once its state has reached final state.
- */
-struct pjsip_tx_data
-{
- /** This is for transmission queue; it's managed by transports. */
- PJ_DECL_LIST_MEMBER(struct pjsip_tx_data);
-
- /** Memory pool for this buffer. */
- pj_pool_t *pool;
-
- /** A name to identify this buffer. */
- char obj_name[PJ_MAX_OBJ_NAME];
-
- /** For response message, this contains the reference to timestamp when
- * the original request message was received. The value of this field
- * is set when application creates response message to a request by
- * calling #pjsip_endpt_create_response.
- */
- pj_time_val rx_timestamp;
-
- /** The transport manager for this buffer. */
- pjsip_tpmgr *mgr;
-
- /** Ioqueue asynchronous operation key. */
- pjsip_tx_data_op_key op_key;
-
- /** Lock object. */
- pj_lock_t *lock;
-
- /** The message in this buffer. */
- pjsip_msg *msg;
-
- /** Buffer to the printed text representation of the message. When the
- * content of this buffer is set, then the transport will send the content
- * of this buffer instead of re-printing the message structure. If the
- * message structure has changed, then application must invalidate this
- * buffer by calling #pjsip_tx_data_invalidate_msg.
- */
- pjsip_buffer buf;
-
- /** Reference counter. */
- pj_atomic_t *ref_cnt;
-
- /** Being processed by transport? */
- int is_pending;
-
- /** Transport manager internal. */
- void *token;
- void (*cb)(void*, pjsip_tx_data*, pj_ssize_t);
-};
-
-
-/**
- * Create a new, blank transmit buffer. The reference count is initialized
- * to zero.
- *
- * @param mgr The transport manager.
- * @param tdata Pointer to receive transmit data.
- *
- * @return PJ_SUCCESS, or the appropriate error code.
- *
- * @see pjsip_endpt_create_tdata
- */
-pj_status_t pjsip_tx_data_create( pjsip_tpmgr *mgr,
- pjsip_tx_data **tdata );
-
-/**
- * Add reference counter to the transmit buffer. The reference counter controls
- * the life time of the buffer, ie. when the counter reaches zero, then it
- * will be destroyed.
- *
- * @param tdata The transmit buffer.
- */
-PJ_DECL(void) pjsip_tx_data_add_ref( pjsip_tx_data *tdata );
-
-/**
- * Decrement reference counter of the transmit buffer.
- * When the transmit buffer is no longer used, it will be destroyed.
- *
- * @param tdata The transmit buffer data.
- */
-PJ_DECL(void) pjsip_tx_data_dec_ref( pjsip_tx_data *tdata );
-
-/**
- * Check if transmit data buffer contains a valid message.
- *
- * @param tdata The transmit buffer.
- */
-PJ_DECL(pj_bool_t) pjsip_tx_data_is_valid( pjsip_tx_data *tdata );
-
-/**
- * Invalidate the print buffer to force message to be re-printed. Call
- * when the message has changed after it has been printed to buffer. The
- * message is printed to buffer normally by transport when it is about to be
- * sent to the wire. Subsequent sending of the message will not cause
- * the message to be re-printed, unless application invalidates the buffer
- * by calling this function.
- *
- * @param tdata The transmit buffer.
- */
-PJ_DECL(void) pjsip_tx_data_invalidate_msg( pjsip_tx_data *tdata );
-
-
-/*****************************************************************************
- *
- * TRANSPORT
- *
- *****************************************************************************/
-
-/**
- * This structure represent the "public" interface of a SIP transport.
- * Applications normally extend this structure to include transport
- * specific members.
- */
-typedef struct pjsip_transport
-{
- char obj_name[PJ_MAX_OBJ_NAME]; /**< Name. */
-
- pj_pool_t *pool; /**< Pool used by transport. */
- pj_atomic_t *ref_cnt; /**< Reference counter. */
- pj_lock_t *lock; /**< Lock object. */
- int tracing; /**< Tracing enabled? */
-
- pjsip_transport_type_e type; /**< Transport type. */
- char type_name[8]; /**< Type name. */
- unsigned flag; /**< #pjsip_transport_flags_e */
-
- pj_sockaddr_in local_addr; /**< Bound address. */
- pj_sockaddr_in public_addr; /**< STUN addres. */
- pj_sockaddr_in rem_addr; /**< Remote addr (zero for UDP) */
-
- pjsip_endpoint *endpt; /**< Endpoint instance. */
- pjsip_tpmgr *tpmgr; /**< Transport manager. */
- pj_timer_entry idle_timer; /**< Timer when ref cnt is zero.*/
-
- /**
- * Function to be called by transport manager to send SIP message.
- *
- * @param transport The transport to send the message.
- * @param packet The buffer to send.
- * @param length The length of the buffer to send.
- * @param op_key Completion token, which will be supplied to
- * caller when pending send operation completes.
- * @param rem_addr The remote destination address.
- * @param callback If supplied, the callback will be called
- * once a pending transmission has completed. If
- * the function completes immediately (i.e. return
- * code is not PJ_EPENDING), the callback will not
- * be called.
- *
- * @return Should return PJ_SUCCESS only if data has been
- * succesfully queued to operating system for
- * transmission. Otherwise it may return PJ_EPENDING
- * if the underlying transport can not send the
- * data immediately and will send it later, which in
- * this case caller doesn't have to do anything
- * except wait the calback to be called, if it
- * supplies one.
- * Other return values indicate the error code.
- */
- pj_status_t (*send_msg)(pjsip_transport *transport,
- pjsip_tx_data *tdata,
- const pj_sockaddr_in *rem_addr,
- void *token,
- void (*callback)(pjsip_transport *transport,
- void *token,
- pj_ssize_t sent_bytes));
-
- /**
- * Destroy this transport.
- */
- pj_status_t (*destroy)(pjsip_transport *transport);
-
- /*
- * Application may extend this structure..
- */
-} pjsip_transport;
-
-
-/**
- * Register a transport.
- */
-PJ_DECL(pj_status_t) pjsip_transport_register( pjsip_tpmgr *mgr,
- pjsip_transport *tp );
-
-
-/**
- * Unregister transport. This will eventually call the transport to
- * destroy itself.
- */
-PJ_DECL(pj_status_t) pjsip_transport_unregister( pjsip_tpmgr *mgr,
- pjsip_transport *tp);
-
-/**
- * Add ref.
- */
-PJ_DECL(pj_status_t) pjsip_transport_add_ref( pjsip_transport *tp );
-
-/**
- * Dec ref.
- */
-PJ_DECL(pj_status_t) pjsip_transport_dec_ref( pjsip_transport *tp );
-
-
-/**
- * Call for incoming message.
- */
-PJ_DECL(pj_ssize_t) pjsip_tpmgr_receive_packet(pjsip_tpmgr *mgr,
- pjsip_rx_data *rdata);
-
-
-/*****************************************************************************
- *
- * TRANSPORT FACTORY
- *
- *****************************************************************************/
-
-
-/**
- * Transport factory.
- */
-typedef struct pjsip_tpfactory pjsip_tpfactory;
-
-/**
- * Transport factory.
- */
-struct pjsip_tpfactory
-{
- /* This list is managed by transport manager. */
- PJ_DECL_LIST_MEMBER(struct pjsip_tpfactory);
-
- pj_pool_t *pool;
- pj_lock_t *lock;
-
- pjsip_transport_type_e type;
- char type_name[8];
- unsigned flag;
-
- pj_sockaddr_in local_addr;
- pj_sockaddr_in public_addr;
-
- /**
- * Create new outbound connection.
- */
- pj_status_t (*create_transport)(pjsip_tpfactory *factory,
- pjsip_tpmgr *mgr,
- pjsip_endpoint *endpt,
- const pj_sockaddr_in *rem_addr,
- pjsip_transport **transport);
-
- /*
- * Application may extend this structure..
- */
-};
-
-
-
-/**
- * Register a transport factory.
- *
- * @param mgr The transport manager.
- * @param factory Transport factory.
- *
- * @return PJ_SUCCESS if listener was successfully created.
- */
-PJ_DECL(pj_status_t) pjsip_tpmgr_register_tpfactory(pjsip_tpmgr *mgr,
- pjsip_tpfactory *tpf);
-
-/**
- * Unregister factory.
- */
-PJ_DECL(pj_status_t) pjsip_tpmgr_unregister_tpfactory(pjsip_tpmgr *mgr,
- pjsip_tpfactory *tpf);
-
-
-/*****************************************************************************
- *
- * TRANSPORT MANAGER
- *
- *****************************************************************************/
-
-/**
- * Create a new transport manager.
- *
- * @param pool Pool.
- * @param endpt Endpoint instance.
- * @param cb Callback to receive incoming message.
- * @param p_mgr Pointer to receive the new transport manager.
- *
- * @return PJ_SUCCESS or the appropriate error code on error.
- */
-PJ_DECL(pj_status_t) pjsip_tpmgr_create( pj_pool_t *pool,
- pjsip_endpoint * endpt,
- void (*cb)(pjsip_endpoint*,
- pj_status_t,
- pjsip_rx_data *),
- pjsip_tpmgr **p_mgr);
-
-
-/**
- * Destroy transport manager.
- */
-PJ_DECL(pj_status_t) pjsip_tpmgr_destroy(pjsip_tpmgr *mgr);
-
-
-/**
- * Dump transport info.
- */
-PJ_DECL(void) pjsip_tpmgr_dump_transports(pjsip_tpmgr *mgr);
-
-
-/*****************************************************************************
- *
- * PUBLIC API
- *
- *****************************************************************************/
-
-
-/**
- * Find transport to be used to send message to remote destination. If no
- * suitable transport is found, a new one will be created.
- */
-PJ_DECL(pj_status_t) pjsip_tpmgr_alloc_transport( pjsip_tpmgr *mgr,
- pjsip_transport_type_e type,
- const pj_sockaddr_in *remote,
- pjsip_transport **p_transport );
-
-
-/**
- * Send a SIP message using the specified transport.
- */
-PJ_DECL(pj_status_t) pjsip_transport_send( pjsip_transport *tr,
- pjsip_tx_data *tdata,
- const pj_sockaddr_in *addr,
- void *token,
- void (*cb)(void *token,
- pjsip_tx_data *tdata,
- pj_ssize_t bytes_sent));
-
-
-/**
- * @}
- */
-
-
-PJ_END_DECL
-
-#endif /* __PJSIP_SIP_TRANSPORT_H__ */
-
+/* $Id$ */
+/*
+ * Copyright (C) 2003-2006 Benny Prijono <benny@prijono.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#ifndef __PJSIP_SIP_TRANSPORT_H__
+#define __PJSIP_SIP_TRANSPORT_H__
+
+/**
+ * @file sip_transport.h
+ * @brief SIP Transport
+ */
+
+#include <pjsip/sip_msg.h>
+#include <pjsip/sip_parser.h>
+#include <pj/sock.h>
+#include <pj/list.h>
+#include <pj/ioqueue.h>
+#include <pj/timer.h>
+
+PJ_BEGIN_DECL
+
+/**
+ * @defgroup PJSIP_TRANSPORT SIP Transport
+ * @ingroup PJSIP
+ *
+ * This is the low-level transport layer. Application normally won't need to
+ * use this function, but instead can use transaction or higher layer API to
+ * send and receive messages.
+ *
+ * @{
+ */
+
+/*****************************************************************************
+ *
+ * GENERAL TRANSPORT (NAMES, TYPES, ETC.)
+ *
+ *****************************************************************************/
+
+/**
+ * Flags for SIP transports.
+ */
+enum pjsip_transport_flags_e
+{
+ PJSIP_TRANSPORT_RELIABLE = 1, /**< Transport is reliable. */
+ PJSIP_TRANSPORT_SECURE = 2, /**< Transport is secure. */
+ PJSIP_TRANSPORT_DATAGRAM = 4, /**< Datagram based transport. */
+};
+
+/**
+ * Check if transport tp is reliable.
+ */
+#define PJSIP_TRANSPORT_IS_RELIABLE(tp) \
+ ((tp)->flag & PJSIP_TRANSPORT_RELIABLE)
+
+/**
+ * Get the transport type from the transport name.
+ *
+ * @param name Transport name, such as "TCP", or "UDP".
+ *
+ * @return The transport type, or PJSIP_TRANSPORT_UNSPECIFIED if
+ * the name is not recognized as the name of supported
+ * transport.
+ */
+PJ_DECL(pjsip_transport_type_e)
+pjsip_transport_get_type_from_name(const pj_str_t *name);
+
+/**
+ * Get the transport type for the specified flags.
+ *
+ * @param flag The transport flag.
+ *
+ * @return Transport type.
+ */
+PJ_DECL(pjsip_transport_type_e)
+pjsip_transport_get_type_from_flag(unsigned flag);
+
+/**
+ * Get transport flag from type.
+ *
+ * @param type Transport type.
+ *
+ * @return Transport flags.
+ */
+PJ_DECL(unsigned)
+pjsip_transport_get_flag_from_type( pjsip_transport_type_e type );
+
+/**
+ * Get the default SIP port number for the specified type.
+ *
+ * @param type Transport type.
+ *
+ * @return The port number, which is the default SIP port number for
+ * the specified type.
+ */
+PJ_DECL(int)
+pjsip_transport_get_default_port_for_type(pjsip_transport_type_e type);
+
+
+/*****************************************************************************
+ *
+ * RECEIVE DATA BUFFER.
+ *
+ *****************************************************************************/
+
+/**
+ * A customized ioqueue async operation key which is used by transport
+ * to locate rdata when a pending read operation completes.
+ */
+typedef struct pjsip_rx_data_op_key
+{
+ pj_ioqueue_op_key_t op_key;
+ pjsip_rx_data *rdata;
+} pjsip_rx_data_op_key;
+
+
+/**
+ * Incoming message buffer.
+ * This structure keep all the information regarding the received message. This
+ * buffer lifetime is only very short, normally after the transaction has been
+ * called, this buffer will be deleted/recycled. So care must be taken when
+ * allocating storage from the pool of this buffer.
+ */
+struct pjsip_rx_data
+{
+
+ /**
+ * tp_info is part of rdata that remains static for the duration of the
+ * buffer. It is initialized when the buffer was created by transport.
+ */
+ struct
+ {
+ /** Memory pool for this buffer. */
+ pj_pool_t *pool;
+
+ /** The transport object which received this packet. */
+ pjsip_transport *transport;
+
+ /** Ioqueue key. */
+ pjsip_rx_data_op_key op_key;
+
+ } tp_info;
+
+
+ /**
+ * pkt_info is initialized by transport when it receives an incoming
+ * packet.
+ */
+ struct
+ {
+ /** Time when the message was received. */
+ pj_time_val timestamp;
+
+ /** Pointer to the original packet. */
+ char packet[PJSIP_MAX_PKT_LEN];
+
+ /** Zero termination for the packet. */
+ pj_uint32_t zero;
+
+ /** The length of the packet received. */
+ int len;
+
+ /** The source address from which the packet was received. */
+ pj_sockaddr_in addr;
+
+ /** The length of the source address. */
+ int addr_len;
+
+ } pkt_info;
+
+
+ /**
+ * msg_info is initialized by transport mgr (tpmgr) before this buffer
+ * is passed to endpoint.
+ */
+ struct
+ {
+ /** Start of msg buffer. */
+ char *msg_buf;
+
+ /** Length fo message. */
+ int len;
+
+ /** The parsed message, if any. */
+ pjsip_msg *msg;
+
+ /** The Call-ID header as found in the message. */
+ pj_str_t call_id;
+
+ /** The From header as found in the message. */
+ pjsip_from_hdr *from;
+
+ /** The To header as found in the message. */
+ pjsip_to_hdr *to;
+
+ /** The topmost Via header as found in the message. */
+ pjsip_via_hdr *via;
+
+ /** The CSeq header as found in the message. */
+ pjsip_cseq_hdr *cseq;
+
+ /** Max forwards header. */
+ pjsip_max_forwards_hdr *max_fwd;
+
+ /** The first route header. */
+ pjsip_route_hdr *route;
+
+ /** The first record-route header. */
+ pjsip_rr_hdr *record_route;
+
+ /** Content-type header. */
+ pjsip_ctype_hdr *ctype;
+
+ /** Content-length header. */
+ pjsip_clen_hdr *clen;
+
+ /** The first Require header. */
+ pjsip_require_hdr *require;
+
+ /** The list of error generated by the parser when parsing
+ this message.
+ */
+ pjsip_parser_err_report parse_err;
+
+ } msg_info;
+
+
+ /**
+ * endpt_info is initialized by endpoint after this buffer reaches
+ * endpoint.
+ */
+ struct
+ {
+ /**
+ * This the transaction key generated for the message.
+ */
+ pj_str_t key;
+
+ } endpt_info;
+
+};
+
+
+/*****************************************************************************
+ *
+ * TRANSMIT DATA BUFFER MANIPULATION.
+ *
+ *****************************************************************************/
+
+/** Customized ioqueue async operation key, used by transport to keep
+ * callback parameters.
+ */
+typedef struct pjsip_tx_data_op_key
+{
+ pj_ioqueue_op_key_t key;
+ pjsip_tx_data *tdata;
+ void *token;
+ void (*callback)(pjsip_transport*,void*,pj_ssize_t);
+} pjsip_tx_data_op_key;
+
+
+/**
+ * Data structure for sending outgoing message. Application normally creates
+ * this buffer by calling #pjsip_endpt_create_tdata.
+ *
+ * The lifetime of this buffer is controlled by the reference counter in this
+ * structure, which is manipulated by calling #pjsip_tx_data_add_ref and
+ * #pjsip_tx_data_dec_ref. When the reference counter has reached zero, then
+ * this buffer will be destroyed.
+ *
+ * A transaction object normally will add reference counter to this buffer
+ * when application calls #pjsip_tsx_on_tx_msg, because it needs to keep the
+ * message for retransmission. The transaction will release the reference
+ * counter once its state has reached final state.
+ */
+struct pjsip_tx_data
+{
+ /** This is for transmission queue; it's managed by transports. */
+ PJ_DECL_LIST_MEMBER(struct pjsip_tx_data);
+
+ /** Memory pool for this buffer. */
+ pj_pool_t *pool;
+
+ /** A name to identify this buffer. */
+ char obj_name[PJ_MAX_OBJ_NAME];
+
+ /** For response message, this contains the reference to timestamp when
+ * the original request message was received. The value of this field
+ * is set when application creates response message to a request by
+ * calling #pjsip_endpt_create_response.
+ */
+ pj_time_val rx_timestamp;
+
+ /** The transport manager for this buffer. */
+ pjsip_tpmgr *mgr;
+
+ /** Ioqueue asynchronous operation key. */
+ pjsip_tx_data_op_key op_key;
+
+ /** Lock object. */
+ pj_lock_t *lock;
+
+ /** The message in this buffer. */
+ pjsip_msg *msg;
+
+ /** Buffer to the printed text representation of the message. When the
+ * content of this buffer is set, then the transport will send the content
+ * of this buffer instead of re-printing the message structure. If the
+ * message structure has changed, then application must invalidate this
+ * buffer by calling #pjsip_tx_data_invalidate_msg.
+ */
+ pjsip_buffer buf;
+
+ /** Reference counter. */
+ pj_atomic_t *ref_cnt;
+
+ /** Being processed by transport? */
+ int is_pending;
+
+ /** Transport manager internal. */
+ void *token;
+ void (*cb)(void*, pjsip_tx_data*, pj_ssize_t);
+};
+
+
+/**
+ * Create a new, blank transmit buffer. The reference count is initialized
+ * to zero.
+ *
+ * @param mgr The transport manager.
+ * @param tdata Pointer to receive transmit data.
+ *
+ * @return PJ_SUCCESS, or the appropriate error code.
+ *
+ * @see pjsip_endpt_create_tdata
+ */
+pj_status_t pjsip_tx_data_create( pjsip_tpmgr *mgr,
+ pjsip_tx_data **tdata );
+
+/**
+ * Add reference counter to the transmit buffer. The reference counter controls
+ * the life time of the buffer, ie. when the counter reaches zero, then it
+ * will be destroyed.
+ *
+ * @param tdata The transmit buffer.
+ */
+PJ_DECL(void) pjsip_tx_data_add_ref( pjsip_tx_data *tdata );
+
+/**
+ * Decrement reference counter of the transmit buffer.
+ * When the transmit buffer is no longer used, it will be destroyed.
+ *
+ * @param tdata The transmit buffer data.
+ */
+PJ_DECL(void) pjsip_tx_data_dec_ref( pjsip_tx_data *tdata );
+
+/**
+ * Check if transmit data buffer contains a valid message.
+ *
+ * @param tdata The transmit buffer.
+ */
+PJ_DECL(pj_bool_t) pjsip_tx_data_is_valid( pjsip_tx_data *tdata );
+
+/**
+ * Invalidate the print buffer to force message to be re-printed. Call
+ * when the message has changed after it has been printed to buffer. The
+ * message is printed to buffer normally by transport when it is about to be
+ * sent to the wire. Subsequent sending of the message will not cause
+ * the message to be re-printed, unless application invalidates the buffer
+ * by calling this function.
+ *
+ * @param tdata The transmit buffer.
+ */
+PJ_DECL(void) pjsip_tx_data_invalidate_msg( pjsip_tx_data *tdata );
+
+
+/*****************************************************************************
+ *
+ * TRANSPORT
+ *
+ *****************************************************************************/
+
+/**
+ * This structure represent the "public" interface of a SIP transport.
+ * Applications normally extend this structure to include transport
+ * specific members.
+ */
+typedef struct pjsip_transport
+{
+ char obj_name[PJ_MAX_OBJ_NAME]; /**< Name. */
+
+ pj_pool_t *pool; /**< Pool used by transport. */
+ pj_atomic_t *ref_cnt; /**< Reference counter. */
+ pj_lock_t *lock; /**< Lock object. */
+ int tracing; /**< Tracing enabled? */
+
+ pjsip_transport_type_e type; /**< Transport type. */
+ char type_name[8]; /**< Type name. */
+ unsigned flag; /**< #pjsip_transport_flags_e */
+
+ pj_sockaddr_in local_addr; /**< Bound address. */
+ pj_sockaddr_in public_addr; /**< STUN addres. */
+ pj_sockaddr_in rem_addr; /**< Remote addr (zero for UDP) */
+
+ pjsip_endpoint *endpt; /**< Endpoint instance. */
+ pjsip_tpmgr *tpmgr; /**< Transport manager. */
+ pj_timer_entry idle_timer; /**< Timer when ref cnt is zero.*/
+
+ /**
+ * Function to be called by transport manager to send SIP message.
+ *
+ * @param transport The transport to send the message.
+ * @param packet The buffer to send.
+ * @param length The length of the buffer to send.
+ * @param op_key Completion token, which will be supplied to
+ * caller when pending send operation completes.
+ * @param rem_addr The remote destination address.
+ * @param callback If supplied, the callback will be called
+ * once a pending transmission has completed. If
+ * the function completes immediately (i.e. return
+ * code is not PJ_EPENDING), the callback will not
+ * be called.
+ *
+ * @return Should return PJ_SUCCESS only if data has been
+ * succesfully queued to operating system for
+ * transmission. Otherwise it may return PJ_EPENDING
+ * if the underlying transport can not send the
+ * data immediately and will send it later, which in
+ * this case caller doesn't have to do anything
+ * except wait the calback to be called, if it
+ * supplies one.
+ * Other return values indicate the error code.
+ */
+ pj_status_t (*send_msg)(pjsip_transport *transport,
+ pjsip_tx_data *tdata,
+ const pj_sockaddr_in *rem_addr,
+ void *token,
+ void (*callback)(pjsip_transport *transport,
+ void *token,
+ pj_ssize_t sent_bytes));
+
+ /**
+ * Destroy this transport.
+ */
+ pj_status_t (*destroy)(pjsip_transport *transport);
+
+ /*
+ * Application may extend this structure..
+ */
+} pjsip_transport;
+
+
+/**
+ * Register a transport.
+ */
+PJ_DECL(pj_status_t) pjsip_transport_register( pjsip_tpmgr *mgr,
+ pjsip_transport *tp );
+
+
+/**
+ * Unregister transport. This will eventually call the transport to
+ * destroy itself.
+ */
+PJ_DECL(pj_status_t) pjsip_transport_unregister( pjsip_tpmgr *mgr,
+ pjsip_transport *tp);
+
+/**
+ * Add ref.
+ */
+PJ_DECL(pj_status_t) pjsip_transport_add_ref( pjsip_transport *tp );
+
+/**
+ * Dec ref.
+ */
+PJ_DECL(pj_status_t) pjsip_transport_dec_ref( pjsip_transport *tp );
+
+
+/**
+ * Call for incoming message.
+ */
+PJ_DECL(pj_ssize_t) pjsip_tpmgr_receive_packet(pjsip_tpmgr *mgr,
+ pjsip_rx_data *rdata);
+
+
+/*****************************************************************************
+ *
+ * TRANSPORT FACTORY
+ *
+ *****************************************************************************/
+
+
+/**
+ * Transport factory.
+ */
+typedef struct pjsip_tpfactory pjsip_tpfactory;
+
+/**
+ * Transport factory.
+ */
+struct pjsip_tpfactory
+{
+ /* This list is managed by transport manager. */
+ PJ_DECL_LIST_MEMBER(struct pjsip_tpfactory);
+
+ pj_pool_t *pool;
+ pj_lock_t *lock;
+
+ pjsip_transport_type_e type;
+ char type_name[8];
+ unsigned flag;
+
+ pj_sockaddr_in local_addr;
+ pj_sockaddr_in public_addr;
+
+ /**
+ * Create new outbound connection.
+ */
+ pj_status_t (*create_transport)(pjsip_tpfactory *factory,
+ pjsip_tpmgr *mgr,
+ pjsip_endpoint *endpt,
+ const pj_sockaddr_in *rem_addr,
+ pjsip_transport **transport);
+
+ /*
+ * Application may extend this structure..
+ */
+};
+
+
+
+/**
+ * Register a transport factory.
+ *
+ * @param mgr The transport manager.
+ * @param factory Transport factory.
+ *
+ * @return PJ_SUCCESS if listener was successfully created.
+ */
+PJ_DECL(pj_status_t) pjsip_tpmgr_register_tpfactory(pjsip_tpmgr *mgr,
+ pjsip_tpfactory *tpf);
+
+/**
+ * Unregister factory.
+ */
+PJ_DECL(pj_status_t) pjsip_tpmgr_unregister_tpfactory(pjsip_tpmgr *mgr,
+ pjsip_tpfactory *tpf);
+
+
+/*****************************************************************************
+ *
+ * TRANSPORT MANAGER
+ *
+ *****************************************************************************/
+
+/**
+ * Create a new transport manager.
+ *
+ * @param pool Pool.
+ * @param endpt Endpoint instance.
+ * @param cb Callback to receive incoming message.
+ * @param p_mgr Pointer to receive the new transport manager.
+ *
+ * @return PJ_SUCCESS or the appropriate error code on error.
+ */
+PJ_DECL(pj_status_t) pjsip_tpmgr_create( pj_pool_t *pool,
+ pjsip_endpoint * endpt,
+ void (*cb)(pjsip_endpoint*,
+ pj_status_t,
+ pjsip_rx_data *),
+ pjsip_tpmgr **p_mgr);
+
+
+/**
+ * Destroy transport manager.
+ */
+PJ_DECL(pj_status_t) pjsip_tpmgr_destroy(pjsip_tpmgr *mgr);
+
+
+/**
+ * Dump transport info.
+ */
+PJ_DECL(void) pjsip_tpmgr_dump_transports(pjsip_tpmgr *mgr);
+
+
+/*****************************************************************************
+ *
+ * PUBLIC API
+ *
+ *****************************************************************************/
+
+
+/**
+ * Find transport to be used to send message to remote destination. If no
+ * suitable transport is found, a new one will be created.
+ */
+PJ_DECL(pj_status_t) pjsip_tpmgr_alloc_transport( pjsip_tpmgr *mgr,
+ pjsip_transport_type_e type,
+ const pj_sockaddr_in *remote,
+ pjsip_transport **p_transport );
+
+
+/**
+ * Send a SIP message using the specified transport.
+ */
+PJ_DECL(pj_status_t) pjsip_transport_send( pjsip_transport *tr,
+ pjsip_tx_data *tdata,
+ const pj_sockaddr_in *addr,
+ void *token,
+ void (*cb)(void *token,
+ pjsip_tx_data *tdata,
+ pj_ssize_t bytes_sent));
+
+
+/**
+ * @}
+ */
+
+
+PJ_END_DECL
+
+#endif /* __PJSIP_SIP_TRANSPORT_H__ */
+
diff --git a/pjsip/include/pjsip/sip_transport_udp.h b/pjsip/include/pjsip/sip_transport_udp.h
index 10ace7fc..99f5ab67 100644
--- a/pjsip/include/pjsip/sip_transport_udp.h
+++ b/pjsip/include/pjsip/sip_transport_udp.h
@@ -1,68 +1,68 @@
-/* $Id: $ */
-/*
- * Copyright (C) 2003-2006 Benny Prijono <benny@prijono.org>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
-#ifndef __PJSIP_TRANSPORT_UDP_H__
-#define __PJSIP_TRANSPORT_UDP_H__
-
-#include <pjsip/sip_transport.h>
-
-PJ_DECL
-
-/**
- * Start UDP transport.
- *
- * @param endpt The SIP endpoint.
- * @param local Local address to bind.
- * @param pub_addr Public address to advertise.
- * @param async_cnt Number of simultaneous async operations.
- * @param p_transport Pointer to receive the transport.
- *
- * @return PJ_SUCCESS when the transport has been successfully
- * started and registered to transport manager, or
- * the appropriate error code.
- */
-PJ_DECL(pj_status_t) pjsip_udp_transport_start(pjsip_endpoint *endpt,
- const pj_sockaddr_in *local,
- const pj_sockaddr_in *pub_addr,
- unsigned async_cnt,
- pjsip_transport **p_transport);
-
-/**
- * Attach UDP socket as a new transport and start the transport.
- *
- * @param endpt The SIP endpoint.
- * @param sock UDP socket to use.
- * @param pub_addr Public address to advertise.
- * @param async_cnt Number of simultaneous async operations.
- * @param p_transport Pointer to receive the transport.
- *
- * @return PJ_SUCCESS when the transport has been successfully
- * started and registered to transport manager, or
- * the appropriate error code.
- */
-PJ_DECL(pj_status_t) pjsip_udp_transport_attach(pjsip_endpoint *endpt,
- pj_sock_t sock,
- const pj_sockaddr_in *pub_addr,
- unsigned async_cnt,
- pjsip_transport **p_transport);
-
-
-PJ_END_DECL
-
-
-#endif /* __PJSIP_TRANSPORT_UDP_H__ */
+/* $Id: $ */
+/*
+ * Copyright (C) 2003-2006 Benny Prijono <benny@prijono.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#ifndef __PJSIP_TRANSPORT_UDP_H__
+#define __PJSIP_TRANSPORT_UDP_H__
+
+#include <pjsip/sip_transport.h>
+
+PJ_DECL
+
+/**
+ * Start UDP transport.
+ *
+ * @param endpt The SIP endpoint.
+ * @param local Local address to bind.
+ * @param pub_addr Public address to advertise.
+ * @param async_cnt Number of simultaneous async operations.
+ * @param p_transport Pointer to receive the transport.
+ *
+ * @return PJ_SUCCESS when the transport has been successfully
+ * started and registered to transport manager, or
+ * the appropriate error code.
+ */
+PJ_DECL(pj_status_t) pjsip_udp_transport_start(pjsip_endpoint *endpt,
+ const pj_sockaddr_in *local,
+ const pj_sockaddr_in *pub_addr,
+ unsigned async_cnt,
+ pjsip_transport **p_transport);
+
+/**
+ * Attach UDP socket as a new transport and start the transport.
+ *
+ * @param endpt The SIP endpoint.
+ * @param sock UDP socket to use.
+ * @param pub_addr Public address to advertise.
+ * @param async_cnt Number of simultaneous async operations.
+ * @param p_transport Pointer to receive the transport.
+ *
+ * @return PJ_SUCCESS when the transport has been successfully
+ * started and registered to transport manager, or
+ * the appropriate error code.
+ */
+PJ_DECL(pj_status_t) pjsip_udp_transport_attach(pjsip_endpoint *endpt,
+ pj_sock_t sock,
+ const pj_sockaddr_in *pub_addr,
+ unsigned async_cnt,
+ pjsip_transport **p_transport);
+
+
+PJ_END_DECL
+
+
+#endif /* __PJSIP_TRANSPORT_UDP_H__ */
diff --git a/pjsip/include/pjsip/sip_types.h b/pjsip/include/pjsip/sip_types.h
index 6554b4ce..2f8f2d13 100644
--- a/pjsip/include/pjsip/sip_types.h
+++ b/pjsip/include/pjsip/sip_types.h
@@ -1,175 +1,175 @@
-/* $Id$ */
-/*
- * Copyright (C) 2003-2006 Benny Prijono <benny@prijono.org>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
-#ifndef __PJSIP_SIP_TYPES_H__
-#define __PJSIP_SIP_TYPES_H__
-
-#include <pjsip/sip_config.h>
-#include <pj/types.h>
-
-/**
- * Opaque data structure for transports (sip_transport.h).
- */
-typedef struct pjsip_transport pjsip_transport;
-
-/**
- * Opaque data type for transport manager (sip_transport.h).
- */
-typedef struct pjsip_tpmgr pjsip_tpmgr;
-
-/**
- * Transport types.
- */
-typedef enum pjsip_transport_type_e
-{
- /** Unspecified. */
- PJSIP_TRANSPORT_UNSPECIFIED,
-
- /** UDP. */
- PJSIP_TRANSPORT_UDP,
-
- /** TCP. */
- PJSIP_TRANSPORT_TCP,
-
- /** TLS. */
- PJSIP_TRANSPORT_TLS,
-
- /** SCTP. */
- PJSIP_TRANSPORT_SCTP,
-
-} pjsip_transport_type_e;
-
-
-/**
- * Forward declaration for endpoint (sip_endpoint.h).
- */
-typedef struct pjsip_endpoint pjsip_endpoint;
-
-/**
- * Forward declaration for transactions (sip_transaction.h).
- */
-typedef struct pjsip_transaction pjsip_transaction;
-
-/**
- * Forward declaration for events (sip_event.h).
- */
-typedef struct pjsip_event pjsip_event;
-
-/**
- * Forward declaration for transmit data/buffer (sip_transport.h).
- */
-typedef struct pjsip_tx_data pjsip_tx_data;
-
-/**
- * Forward declaration for receive data/buffer (sip_transport.h).
- */
-typedef struct pjsip_rx_data pjsip_rx_data;
-
-/**
- * Forward declaration for message (sip_msg.h).
- */
-typedef struct pjsip_msg pjsip_msg;
-
-/**
- * Forward declaration for header field (sip_msg.h).
- */
-typedef struct pjsip_hdr pjsip_hdr;
-
-/**
- * Forward declaration for URI (sip_uri.h).
- */
-typedef struct pjsip_uri pjsip_uri;
-
-/**
- * Opaque data type for the resolver engine (sip_resolve.h).
- */
-typedef struct pjsip_resolver_t pjsip_resolver_t;
-
-/**
- * Forward declaration for credential.
- */
-typedef struct pjsip_cred_info pjsip_cred_info;
-
-
-/**
- * Forward declaration for module (sip_module.h).
- */
-typedef struct pjsip_module pjsip_module;
-
-/**
- * Transaction role.
- */
-typedef enum pjsip_role_e
-{
- PJSIP_ROLE_UAC, /**< Transaction role is UAC. */
- PJSIP_ROLE_UAS, /**< Transaction role is UAS. */
-} pjsip_role_e;
-
-
-/**
- * General purpose buffer.
- */
-typedef struct pjsip_buffer
-{
- /** The start of the buffer. */
- char *start;
-
- /** Pointer to current end of the buffer, which also indicates the position
- of subsequent buffer write.
- */
- char *cur;
-
- /** The absolute end of the buffer. */
- char *end;
-
-} pjsip_buffer;
-
-
-/**
- * General host:port pair, used for example as Via sent-by.
- */
-typedef struct pjsip_host_port
-{
- unsigned flag; /**< Flags of pjsip_transport_flags_e (not used in Via). */
- unsigned type; /**< Transport type (pjsip_transport_type_e), or zero. */
- pj_str_t host; /**< Host part. */
- int port; /**< Port number. */
-} pjsip_host_port;
-
-
-/**
- * Convert exception ID into pj_status_t status.
- *
- * @param exception_id Exception Id.
- *
- * @return Error code for the specified exception Id.
- */
-PJ_DECL(pj_status_t) pjsip_exception_to_status(int exception_id);
-
-/**
- * Return standard pj_status_t status from current exception.
- */
-#define PJSIP_RETURN_EXCEPTION() pjsip_exception_to_status(PJ_GET_EXCEPTION())
-
-/**
- * Attributes to inform that the function may throw exceptions.
- */
-#define PJSIP_THROW_SPEC(list)
-
-#endif /* __PJSIP_SIP_TYPES_H__ */
-
+/* $Id$ */
+/*
+ * Copyright (C) 2003-2006 Benny Prijono <benny@prijono.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#ifndef __PJSIP_SIP_TYPES_H__
+#define __PJSIP_SIP_TYPES_H__
+
+#include <pjsip/sip_config.h>
+#include <pj/types.h>
+
+/**
+ * Opaque data structure for transports (sip_transport.h).
+ */
+typedef struct pjsip_transport pjsip_transport;
+
+/**
+ * Opaque data type for transport manager (sip_transport.h).
+ */
+typedef struct pjsip_tpmgr pjsip_tpmgr;
+
+/**
+ * Transport types.
+ */
+typedef enum pjsip_transport_type_e
+{
+ /** Unspecified. */
+ PJSIP_TRANSPORT_UNSPECIFIED,
+
+ /** UDP. */
+ PJSIP_TRANSPORT_UDP,
+
+ /** TCP. */
+ PJSIP_TRANSPORT_TCP,
+
+ /** TLS. */
+ PJSIP_TRANSPORT_TLS,
+
+ /** SCTP. */
+ PJSIP_TRANSPORT_SCTP,
+
+} pjsip_transport_type_e;
+
+
+/**
+ * Forward declaration for endpoint (sip_endpoint.h).
+ */
+typedef struct pjsip_endpoint pjsip_endpoint;
+
+/**
+ * Forward declaration for transactions (sip_transaction.h).
+ */
+typedef struct pjsip_transaction pjsip_transaction;
+
+/**
+ * Forward declaration for events (sip_event.h).
+ */
+typedef struct pjsip_event pjsip_event;
+
+/**
+ * Forward declaration for transmit data/buffer (sip_transport.h).
+ */
+typedef struct pjsip_tx_data pjsip_tx_data;
+
+/**
+ * Forward declaration for receive data/buffer (sip_transport.h).
+ */
+typedef struct pjsip_rx_data pjsip_rx_data;
+
+/**
+ * Forward declaration for message (sip_msg.h).
+ */
+typedef struct pjsip_msg pjsip_msg;
+
+/**
+ * Forward declaration for header field (sip_msg.h).
+ */
+typedef struct pjsip_hdr pjsip_hdr;
+
+/**
+ * Forward declaration for URI (sip_uri.h).
+ */
+typedef struct pjsip_uri pjsip_uri;
+
+/**
+ * Opaque data type for the resolver engine (sip_resolve.h).
+ */
+typedef struct pjsip_resolver_t pjsip_resolver_t;
+
+/**
+ * Forward declaration for credential.
+ */
+typedef struct pjsip_cred_info pjsip_cred_info;
+
+
+/**
+ * Forward declaration for module (sip_module.h).
+ */
+typedef struct pjsip_module pjsip_module;
+
+/**
+ * Transaction role.
+ */
+typedef enum pjsip_role_e
+{
+ PJSIP_ROLE_UAC, /**< Transaction role is UAC. */
+ PJSIP_ROLE_UAS, /**< Transaction role is UAS. */
+} pjsip_role_e;
+
+
+/**
+ * General purpose buffer.
+ */
+typedef struct pjsip_buffer
+{
+ /** The start of the buffer. */
+ char *start;
+
+ /** Pointer to current end of the buffer, which also indicates the position
+ of subsequent buffer write.
+ */
+ char *cur;
+
+ /** The absolute end of the buffer. */
+ char *end;
+
+} pjsip_buffer;
+
+
+/**
+ * General host:port pair, used for example as Via sent-by.
+ */
+typedef struct pjsip_host_port
+{
+ unsigned flag; /**< Flags of pjsip_transport_flags_e (not used in Via). */
+ unsigned type; /**< Transport type (pjsip_transport_type_e), or zero. */
+ pj_str_t host; /**< Host part. */
+ int port; /**< Port number. */
+} pjsip_host_port;
+
+
+/**
+ * Convert exception ID into pj_status_t status.
+ *
+ * @param exception_id Exception Id.
+ *
+ * @return Error code for the specified exception Id.
+ */
+PJ_DECL(pj_status_t) pjsip_exception_to_status(int exception_id);
+
+/**
+ * Return standard pj_status_t status from current exception.
+ */
+#define PJSIP_RETURN_EXCEPTION() pjsip_exception_to_status(PJ_GET_EXCEPTION())
+
+/**
+ * Attributes to inform that the function may throw exceptions.
+ */
+#define PJSIP_THROW_SPEC(list)
+
+#endif /* __PJSIP_SIP_TYPES_H__ */
+
diff --git a/pjsip/include/pjsip/sip_uri.h b/pjsip/include/pjsip/sip_uri.h
index b72721f2..0e13b300 100644
--- a/pjsip/include/pjsip/sip_uri.h
+++ b/pjsip/include/pjsip/sip_uri.h
@@ -1,359 +1,384 @@
-/* $Id$ */
-/*
- * Copyright (C) 2003-2006 Benny Prijono <benny@prijono.org>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
-#ifndef __PJSIP_SIP_URI_H__
-#define __PJSIP_SIP_URI_H__
-
-/**
- * @file sip_uri.h
- * @brief SIP URL Structures and Manipulations
- */
-
-#include <pjsip/sip_types.h>
-#include <pjsip/sip_config.h>
-#include <pj/list.h>
-
-PJ_BEGIN_DECL
-
-
-/**
- * @defgroup PJSIP_URL URL Structures
- * @brief SIP Url, tel: Url, and generic URI.
- * @ingroup PJSIP_MSG
- * @{
- */
-
-/**
- * Generic parameter, normally used in other_param or header_param.
- */
-typedef struct pjsip_param
-{
- PJ_DECL_LIST_MEMBER(struct pjsip_param); /**< Generic list member. */
- pj_str_t name; /**< Param/header name. */
- pj_str_t value; /**< Param/header value. */
-} pjsip_param;
-
-
-/**
- * Find the specified parameter name in the list. The name will be compared
- * in case-insensitive comparison.
- *
- * @param param_list List of parameters to find.
- * @param name Parameter/header name to find.
- *
- * @return The parameter if found, or NULL.
- */
-PJ_DECL(pjsip_param*) pjsip_param_find( pjsip_param *param_list,
- const pj_str_t *name );
-
-
-/**
- * Find the specified parameter name in the list. The name will be compared
- * in case-insensitive comparison.
- *
- * @param param_list List of parameters to find.
- * @param name Parameter/header name to find.
- *
- * @return The parameter if found, or NULL.
- */
-PJ_DECL(const pjsip_param*) pjsip_param_cfind(const pjsip_param *param_list,
- const pj_str_t *name );
-
-
-/**
- * Duplicate the parameters.
- *
- * @param pool Pool to allocate memory from.
- * @param dst_list Destination list.
- * @param src_list Source list.
- */
-PJ_DECL(void) pjsip_param_clone(pj_pool_t *pool, pjsip_param *dst_list,
- const pjsip_param *src_list);
-
-/**
- * URI context.
- */
-typedef enum pjsip_uri_context_e
-{
- PJSIP_URI_IN_REQ_URI, /**< The URI is in Request URI. */
- PJSIP_URI_IN_FROMTO_HDR, /**< The URI is in From/To header. */
- PJSIP_URI_IN_CONTACT_HDR, /**< The URI is in Contact header. */
- PJSIP_URI_IN_ROUTING_HDR, /**< The URI is in Route/Record-Route header. */
- PJSIP_URI_IN_OTHER, /**< Other context (web page, business card, etc.) */
-} pjsip_uri_context_e;
-
-/**
- * URI 'virtual' function table.
- * All types of URI in this library (such as sip:, sips:, tel:, and name-addr)
- * will have pointer to this table as their first struct member. This table
- * provides polimorphic behaviour to the URI.
- */
-typedef struct pjsip_uri_vptr
-{
- /**
- * Get URI scheme.
- * @param uri the URI (self).
- * @return the URI scheme.
- */
- const pj_str_t* (*p_get_scheme)(const void *uri);
-
- /**
- * Get the URI object contained by this URI, or the URI itself if
- * it doesn't contain another URI.
- * @param uri the URI (self).
- */
- void* (*p_get_uri)(void *uri);
-
- /**
- * Print URI components to the buffer, following the rule of which
- * components are allowed for the context.
- * @param context the context where the URI will be placed.
- * @param uri the URI (self).
- * @param buf the buffer.
- * @param size the size of the buffer.
- * @return the length printed.
- */
- int (*p_print)(pjsip_uri_context_e context,
- const void *uri,
- char *buf, pj_size_t size);
-
- /**
- * Compare two URIs according to the context.
- * @param context the context.
- * @param uri1 the first URI (self).
- * @param uri2 the second URI.
- * @return PJ_SUCCESS if equal, or otherwise the error status which
- * should point to the mismatch part.
- */
- pj_status_t (*p_compare)(pjsip_uri_context_e context,
- const void *uri1, const void *uri2);
-
- /**
- * Clone URI.
- * @param pool the pool.
- * @param the URI to clone (self).
- * @return new URI.
- */
- void *(*p_clone)(pj_pool_t *pool, const void *uri);
-
-} pjsip_uri_vptr;
-
-
-/**
- * The declaration of 'base class' for all URI scheme.
- */
-struct pjsip_uri
-{
- /** All URIs must have URI virtual function table as their first member. */
- pjsip_uri_vptr *vptr;
-};
-
-/**
- * This macro checks that the URL is a "sip:" or "sips:" URL.
- * @param url The URL (pointer to)
- * @return non-zero if TRUE.
- */
-#define PJSIP_URI_SCHEME_IS_SIP(url) \
- (pj_strnicmp2(pjsip_uri_get_scheme(url), "sip", 3)==0)
-
-/**
- * This macro checks that the URL is a "sips:" URL (not SIP).
- * @param url The URL (pointer to)
- * @return non-zero if TRUE.
- */
-#define PJSIP_URI_SCHEME_IS_SIPS(url) \
- (pj_strnicmp2(pjsip_uri_get_scheme(url), "sips", 4)==0)
-
-/**
- * This macro checks that the URL is a "tel:" URL.
- * @param url The URL (pointer to)
- * @return non-zero if TRUE.
- */
-#define PJSIP_URI_SCHEME_IS_TEL(url) \
- (pj_strnicmp2(pjsip_uri_get_scheme(url), "tel", 3)==0)
-
-
-/**
- * SIP and SIPS URL scheme.
- */
-typedef struct pjsip_url
-{
- pjsip_uri_vptr *vptr; /**< Pointer to virtual function table.*/
- pj_str_t user; /**< Optional user part. */
- pj_str_t passwd; /**< Optional password part. */
- pj_str_t host; /**< Host part, always exists. */
- int port; /**< Optional port number, or zero. */
- pj_str_t user_param; /**< Optional user parameter */
- pj_str_t method_param; /**< Optional method parameter. */
- pj_str_t transport_param; /**< Optional transport parameter. */
- int ttl_param; /**< Optional TTL param, or -1. */
- int lr_param; /**< Optional loose routing param, or zero */
- pj_str_t maddr_param; /**< Optional maddr param */
- pjsip_param other_param; /**< Other parameters grouped together. */
- pjsip_param header_param; /**< Optional header parameter. */
-} pjsip_url;
-
-
-/**
- * SIP name-addr, which typically appear in From, To, and Contact header.
- * The SIP name-addr contains a generic URI and a display name.
- */
-typedef struct pjsip_name_addr
-{
- /** Pointer to virtual function table. */
- pjsip_uri_vptr *vptr;
-
- /** Optional display name. */
- pj_str_t display;
-
- /** URI part. */
- pjsip_uri *uri;
-
-} pjsip_name_addr;
-
-
-/**
- * Generic function to get the URI scheme.
- * @param uri the URI object.
- * @return the URI scheme.
- */
-PJ_INLINE(const pj_str_t*) pjsip_uri_get_scheme(const void *uri)
-{
- return (*((pjsip_uri*)uri)->vptr->p_get_scheme)(uri);
-}
-
-/**
- * Generic function to get the URI object contained by this URI, or the URI
- * itself if it doesn't contain another URI.
- *
- * @param uri the URI.
- * @return the URI.
- */
-PJ_INLINE(void*) pjsip_uri_get_uri(void *uri)
-{
- return (*((pjsip_uri*)uri)->vptr->p_get_uri)(uri);
-}
-
-/**
- * Generic function to compare two URIs.
- *
- * @param context Comparison context.
- * @param uri1 The first URI.
- * @param uri2 The second URI.
- * @return PJ_SUCCESS if equal, or otherwise the error status which
- * should point to the mismatch part.
- */
-PJ_INLINE(pj_status_t) pjsip_uri_cmp(pjsip_uri_context_e context,
- const void *uri1, const void *uri2)
-{
- return (*((const pjsip_uri*)uri1)->vptr->p_compare)(context, uri1, uri2);
-}
-
-/**
- * Generic function to print an URI object.
- *
- * @param context Print context.
- * @param uri The URI to print.
- * @param buf The buffer.
- * @param size Size of the buffer.
- * @return Length printed.
- */
-PJ_INLINE(int) pjsip_uri_print(pjsip_uri_context_e context,
- const void *uri,
- char *buf, pj_size_t size)
-{
- return (*((const pjsip_uri*)uri)->vptr->p_print)(context, uri, buf, size);
-}
-
-/**
- * Generic function to clone an URI object.
- *
- * @param pool Pool.
- * @param uri URI to clone.
- * @return New URI.
- */
-PJ_INLINE(void*) pjsip_uri_clone( pj_pool_t *pool, const void *uri )
-{
- return (*((const pjsip_uri*)uri)->vptr->p_clone)(pool, uri);
-}
-
-
-/**
- * Create new SIP URL and initialize all fields with zero or NULL.
- * @param pool The pool.
- * @param secure Tlag to indicate whether secure transport should be used.
- * @return SIP URL.
- */
-PJ_DECL(pjsip_url*) pjsip_url_create( pj_pool_t *pool, int secure );
-
-/**
- * Create new SIPS URL and initialize all fields with zero or NULL.
- * @param pool The pool.
- * @return SIPS URL.
- */
-PJ_DECL(pjsip_url*) pjsips_url_create( pj_pool_t *pool );
-
-/**
- * Initialize SIP URL (all fields are set to NULL or zero).
- * @param url The URL.
- */
-PJ_DECL(void) pjsip_url_init(pjsip_url *url, int secure);
-
-/**
- * Perform full assignment to the SIP URL.
- * @param pool The pool.
- * @param url Destination URL.
- * @param rhs The source URL.
- */
-PJ_DECL(void) pjsip_url_assign(pj_pool_t *pool, pjsip_url *url, const pjsip_url *rhs);
-
-/**
- * Create new instance of name address and initialize all fields with zero or
- * NULL.
- * @param pool The pool.
- * @return New SIP name address.
- */
-PJ_DECL(pjsip_name_addr*) pjsip_name_addr_create(pj_pool_t *pool);
-
-/**
- * Initialize with default value.
- * @param name_addr The name address.
- */
-PJ_DECL(void) pjsip_name_addr_init(pjsip_name_addr *name_addr);
-
-/**
- * Perform full assignment to the name address.
- * @param pool The pool.
- * @param addr The destination name address.
- * @param rhs The source name address.
- */
-PJ_DECL(void) pjsip_name_addr_assign(pj_pool_t *pool,
- pjsip_name_addr *addr,
- const pjsip_name_addr *rhs);
-
-
-
-
-/**
- * @}
- */
-
-PJ_END_DECL
-
-#endif /* __PJSIP_URL_H__ */
-
+/* $Id$ */
+/*
+ * Copyright (C) 2003-2006 Benny Prijono <benny@prijono.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#ifndef __PJSIP_SIP_URI_H__
+#define __PJSIP_SIP_URI_H__
+
+/**
+ * @file sip_uri.h
+ * @brief SIP URL Structures and Manipulations
+ */
+
+#include <pjsip/sip_types.h>
+#include <pjsip/sip_config.h>
+#include <pj/list.h>
+
+PJ_BEGIN_DECL
+
+
+/**
+ * @defgroup PJSIP_URL URL Structures
+ * @brief SIP Url, tel: Url, and generic URI.
+ * @ingroup PJSIP_MSG
+ * @{
+ */
+
+/**
+ * Generic parameter, normally used in other_param or header_param.
+ */
+typedef struct pjsip_param
+{
+ PJ_DECL_LIST_MEMBER(struct pjsip_param); /**< Generic list member. */
+ pj_str_t name; /**< Param/header name. */
+ pj_str_t value; /**< Param/header value. */
+} pjsip_param;
+
+
+/**
+ * Find the specified parameter name in the list. The name will be compared
+ * in case-insensitive comparison.
+ *
+ * @param param_list List of parameters to find.
+ * @param name Parameter/header name to find.
+ *
+ * @return The parameter if found, or NULL.
+ */
+PJ_DECL(pjsip_param*) pjsip_param_find( pjsip_param *param_list,
+ const pj_str_t *name );
+
+
+/**
+ * Find the specified parameter name in the list. The name will be compared
+ * in case-insensitive comparison.
+ *
+ * @param param_list List of parameters to find.
+ * @param name Parameter/header name to find.
+ *
+ * @return The parameter if found, or NULL.
+ */
+PJ_DECL(const pjsip_param*) pjsip_param_cfind(const pjsip_param *param_list,
+ const pj_str_t *name );
+
+
+/**
+ * Duplicate the parameters.
+ *
+ * @param pool Pool to allocate memory from.
+ * @param dst_list Destination list.
+ * @param src_list Source list.
+ */
+PJ_DECL(void) pjsip_param_clone(pj_pool_t *pool, pjsip_param *dst_list,
+ const pjsip_param *src_list);
+
+/**
+ * Duplicate the parameters.
+ *
+ * @param pool Pool to allocate memory from.
+ * @param dst_list Destination list.
+ * @param src_list Source list.
+ */
+PJ_DECL(void) pjsip_param_shallow_clone(pj_pool_t *pool,
+ pjsip_param *dst_list,
+ const pjsip_param *src_list);
+
+/**
+ * Print parameters.
+ *
+ * @param param_list The parameter list.
+ * @param buf Buffer.
+ * @param size Size of buffer.
+ * @param sep Separator character (either ';' or ',').
+ *
+ * @return The number of bytes printed, or -1 on errr.
+ */
+PJ_DECL(pj_ssize_t) pjsip_param_print_on(const pjsip_param *param_list,
+ char *buf, pj_size_t size,
+ int sep);
+
+/**
+ * URI context.
+ */
+typedef enum pjsip_uri_context_e
+{
+ PJSIP_URI_IN_REQ_URI, /**< The URI is in Request URI. */
+ PJSIP_URI_IN_FROMTO_HDR, /**< The URI is in From/To header. */
+ PJSIP_URI_IN_CONTACT_HDR, /**< The URI is in Contact header. */
+ PJSIP_URI_IN_ROUTING_HDR, /**< The URI is in Route/Record-Route header. */
+ PJSIP_URI_IN_OTHER, /**< Other context (web page, business card, etc.) */
+} pjsip_uri_context_e;
+
+/**
+ * URI 'virtual' function table.
+ * All types of URI in this library (such as sip:, sips:, tel:, and name-addr)
+ * will have pointer to this table as their first struct member. This table
+ * provides polimorphic behaviour to the URI.
+ */
+typedef struct pjsip_uri_vptr
+{
+ /**
+ * Get URI scheme.
+ * @param uri the URI (self).
+ * @return the URI scheme.
+ */
+ const pj_str_t* (*p_get_scheme)(const void *uri);
+
+ /**
+ * Get the URI object contained by this URI, or the URI itself if
+ * it doesn't contain another URI.
+ * @param uri the URI (self).
+ */
+ void* (*p_get_uri)(void *uri);
+
+ /**
+ * Print URI components to the buffer, following the rule of which
+ * components are allowed for the context.
+ * @param context the context where the URI will be placed.
+ * @param uri the URI (self).
+ * @param buf the buffer.
+ * @param size the size of the buffer.
+ * @return the length printed.
+ */
+ int (*p_print)(pjsip_uri_context_e context,
+ const void *uri,
+ char *buf, pj_size_t size);
+
+ /**
+ * Compare two URIs according to the context.
+ * @param context the context.
+ * @param uri1 the first URI (self).
+ * @param uri2 the second URI.
+ * @return PJ_SUCCESS if equal, or otherwise the error status which
+ * should point to the mismatch part.
+ */
+ pj_status_t (*p_compare)(pjsip_uri_context_e context,
+ const void *uri1, const void *uri2);
+
+ /**
+ * Clone URI.
+ * @param pool the pool.
+ * @param the URI to clone (self).
+ * @return new URI.
+ */
+ void *(*p_clone)(pj_pool_t *pool, const void *uri);
+
+} pjsip_uri_vptr;
+
+
+/**
+ * The declaration of 'base class' for all URI scheme.
+ */
+struct pjsip_uri
+{
+ /** All URIs must have URI virtual function table as their first member. */
+ pjsip_uri_vptr *vptr;
+};
+
+/**
+ * This macro checks that the URL is a "sip:" or "sips:" URL.
+ * @param url The URL (pointer to)
+ * @return non-zero if TRUE.
+ */
+#define PJSIP_URI_SCHEME_IS_SIP(url) \
+ (pj_strnicmp2(pjsip_uri_get_scheme(url), "sip", 3)==0)
+
+/**
+ * This macro checks that the URL is a "sips:" URL (not SIP).
+ * @param url The URL (pointer to)
+ * @return non-zero if TRUE.
+ */
+#define PJSIP_URI_SCHEME_IS_SIPS(url) \
+ (pj_strnicmp2(pjsip_uri_get_scheme(url), "sips", 4)==0)
+
+/**
+ * This macro checks that the URL is a "tel:" URL.
+ * @param url The URL (pointer to)
+ * @return non-zero if TRUE.
+ */
+#define PJSIP_URI_SCHEME_IS_TEL(url) \
+ (pj_strnicmp2(pjsip_uri_get_scheme(url), "tel", 3)==0)
+
+
+/**
+ * SIP and SIPS URL scheme.
+ */
+typedef struct pjsip_url
+{
+ pjsip_uri_vptr *vptr; /**< Pointer to virtual function table.*/
+ pj_str_t user; /**< Optional user part. */
+ pj_str_t passwd; /**< Optional password part. */
+ pj_str_t host; /**< Host part, always exists. */
+ int port; /**< Optional port number, or zero. */
+ pj_str_t user_param; /**< Optional user parameter */
+ pj_str_t method_param; /**< Optional method parameter. */
+ pj_str_t transport_param; /**< Optional transport parameter. */
+ int ttl_param; /**< Optional TTL param, or -1. */
+ int lr_param; /**< Optional loose routing param, or zero */
+ pj_str_t maddr_param; /**< Optional maddr param */
+ pjsip_param other_param; /**< Other parameters grouped together. */
+ pjsip_param header_param; /**< Optional header parameter. */
+} pjsip_url;
+
+
+/**
+ * SIP name-addr, which typically appear in From, To, and Contact header.
+ * The SIP name-addr contains a generic URI and a display name.
+ */
+typedef struct pjsip_name_addr
+{
+ /** Pointer to virtual function table. */
+ pjsip_uri_vptr *vptr;
+
+ /** Optional display name. */
+ pj_str_t display;
+
+ /** URI part. */
+ pjsip_uri *uri;
+
+} pjsip_name_addr;
+
+
+/**
+ * Generic function to get the URI scheme.
+ * @param uri the URI object.
+ * @return the URI scheme.
+ */
+PJ_INLINE(const pj_str_t*) pjsip_uri_get_scheme(const void *uri)
+{
+ return (*((pjsip_uri*)uri)->vptr->p_get_scheme)(uri);
+}
+
+/**
+ * Generic function to get the URI object contained by this URI, or the URI
+ * itself if it doesn't contain another URI.
+ *
+ * @param uri the URI.
+ * @return the URI.
+ */
+PJ_INLINE(void*) pjsip_uri_get_uri(void *uri)
+{
+ return (*((pjsip_uri*)uri)->vptr->p_get_uri)(uri);
+}
+
+/**
+ * Generic function to compare two URIs.
+ *
+ * @param context Comparison context.
+ * @param uri1 The first URI.
+ * @param uri2 The second URI.
+ * @return PJ_SUCCESS if equal, or otherwise the error status which
+ * should point to the mismatch part.
+ */
+PJ_INLINE(pj_status_t) pjsip_uri_cmp(pjsip_uri_context_e context,
+ const void *uri1, const void *uri2)
+{
+ return (*((const pjsip_uri*)uri1)->vptr->p_compare)(context, uri1, uri2);
+}
+
+/**
+ * Generic function to print an URI object.
+ *
+ * @param context Print context.
+ * @param uri The URI to print.
+ * @param buf The buffer.
+ * @param size Size of the buffer.
+ * @return Length printed.
+ */
+PJ_INLINE(int) pjsip_uri_print(pjsip_uri_context_e context,
+ const void *uri,
+ char *buf, pj_size_t size)
+{
+ return (*((const pjsip_uri*)uri)->vptr->p_print)(context, uri, buf, size);
+}
+
+/**
+ * Generic function to clone an URI object.
+ *
+ * @param pool Pool.
+ * @param uri URI to clone.
+ * @return New URI.
+ */
+PJ_INLINE(void*) pjsip_uri_clone( pj_pool_t *pool, const void *uri )
+{
+ return (*((const pjsip_uri*)uri)->vptr->p_clone)(pool, uri);
+}
+
+
+/**
+ * Create new SIP URL and initialize all fields with zero or NULL.
+ * @param pool The pool.
+ * @param secure Tlag to indicate whether secure transport should be used.
+ * @return SIP URL.
+ */
+PJ_DECL(pjsip_url*) pjsip_url_create( pj_pool_t *pool, int secure );
+
+/**
+ * Create new SIPS URL and initialize all fields with zero or NULL.
+ * @param pool The pool.
+ * @return SIPS URL.
+ */
+PJ_DECL(pjsip_url*) pjsips_url_create( pj_pool_t *pool );
+
+/**
+ * Initialize SIP URL (all fields are set to NULL or zero).
+ * @param url The URL.
+ */
+PJ_DECL(void) pjsip_url_init(pjsip_url *url, int secure);
+
+/**
+ * Perform full assignment to the SIP URL.
+ * @param pool The pool.
+ * @param url Destination URL.
+ * @param rhs The source URL.
+ */
+PJ_DECL(void) pjsip_url_assign(pj_pool_t *pool, pjsip_url *url, const pjsip_url *rhs);
+
+/**
+ * Create new instance of name address and initialize all fields with zero or
+ * NULL.
+ * @param pool The pool.
+ * @return New SIP name address.
+ */
+PJ_DECL(pjsip_name_addr*) pjsip_name_addr_create(pj_pool_t *pool);
+
+/**
+ * Initialize with default value.
+ * @param name_addr The name address.
+ */
+PJ_DECL(void) pjsip_name_addr_init(pjsip_name_addr *name_addr);
+
+/**
+ * Perform full assignment to the name address.
+ * @param pool The pool.
+ * @param addr The destination name address.
+ * @param rhs The source name address.
+ */
+PJ_DECL(void) pjsip_name_addr_assign(pj_pool_t *pool,
+ pjsip_name_addr *addr,
+ const pjsip_name_addr *rhs);
+
+
+
+
+/**
+ * @}
+ */
+
+PJ_END_DECL
+
+#endif /* __PJSIP_URL_H__ */
+
diff --git a/pjsip/include/pjsip/sip_util.h b/pjsip/include/pjsip/sip_util.h
index f8685e54..e158212a 100644
--- a/pjsip/include/pjsip/sip_util.h
+++ b/pjsip/include/pjsip/sip_util.h
@@ -1,192 +1,192 @@
-/* $Id$ */
-/*
- * Copyright (C) 2003-2006 Benny Prijono <benny@prijono.org>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
-#ifndef __PJSIP_SIP_MISC_H__
-#define __PJSIP_SIP_MISC_H__
-
-#include <pjsip/sip_msg.h>
-
-PJ_BEGIN_DECL
-
-/**
- * @defgroup PJSIP_ENDPT SIP Endpoint
- * @ingroup PJSIP
- * @{
- */
-
-/**
- * Create an independent request message. This can be used to build any
- * request outside a dialog, such as OPTIONS, MESSAGE, etc. To create a request
- * inside a dialog, application should use #pjsip_dlg_create_request.
- *
- * Once a transmit data is created, the reference counter is initialized to 1.
- *
- * @param endpt Endpoint instance.
- * @param method SIP Method.
- * @param target Target URI.
- * @param from URL to put in From header.
- * @param to URL to put in To header.
- * @param contact URL to put in Contact header.
- * @param call_id Optional Call-ID (put NULL to generate unique Call-ID).
- * @param cseq Optional CSeq (put -1 to generate random CSeq).
- * @param text Optional text body (put NULL to omit body).
- * @param p_tdata Pointer to receive the transmit data.
- *
- * @return PJ_SUCCESS, or the appropriate error code.
- */
-PJ_DECL(pj_status_t) pjsip_endpt_create_request( pjsip_endpoint *endpt,
- const pjsip_method *method,
- const pj_str_t *target,
- const pj_str_t *from,
- const pj_str_t *to,
- const pj_str_t *contact,
- const pj_str_t *call_id,
- int cseq,
- const pj_str_t *text,
- pjsip_tx_data **p_tdata);
-
-/**
- * Create an independent request message from the specified headers. This
- * function will shallow clone the headers and put them in the request.
- *
- * Once a transmit data is created, the reference counter is initialized to 1.
- *
- * @param endpt Endpoint instance.
- * @param method SIP Method.
- * @param target Target URI.
- * @param from URL to put in From header.
- * @param to URL to put in To header.
- * @param contact URL to put in Contact header.
- * @param call_id Optional Call-ID (put NULL to generate unique Call-ID).
- * @param cseq Optional CSeq (put -1 to generate random CSeq).
- * @param text Optional text body (put NULL to omit body).
- * @param p_tdata Pointer to receive the transmit data.
- *
- * @return PJ_SUCCESS, or the appropriate error code.
- */
-PJ_DECL(pj_status_t)
-pjsip_endpt_create_request_from_hdr( pjsip_endpoint *endpt,
- const pjsip_method *method,
- const pjsip_uri *target,
- const pjsip_from_hdr *from,
- const pjsip_to_hdr *to,
- const pjsip_contact_hdr *contact,
- const pjsip_cid_hdr *call_id,
- int cseq,
- const pj_str_t *text,
- pjsip_tx_data **p_tdata);
-
-/**
- * Send outgoing request and initiate UAC transaction for the request.
- * This is an auxiliary function to be used by application to send arbitrary
- * requests outside a dialog. To send a request within a dialog, application
- * should use #pjsip_dlg_send_msg instead.
- *
- * @param endpt The endpoint instance.
- * @param tdata The transmit data to be sent.
- * @param timeout Optional timeout for final response to be received, or -1
- * if the transaction should not have a timeout restriction.
- * @param token Optional token to be associated with the transaction, and
- * to be passed to the callback.
- * @param cb Optional callback to be called when the transaction has
- * received a final response. The callback will be called with
- * the previously registered token and the event that triggers
- * the completion of the transaction.
- *
- * @return PJ_SUCCESS, or the appropriate error code.
- */
-PJ_DECL(pj_status_t) pjsip_endpt_send_request( pjsip_endpoint *endpt,
- pjsip_tx_data *tdata,
- int timeout,
- void *token,
- void (*cb)(void*,pjsip_event*));
-
-/**
- * Construct a minimal response message for the received request. This function
- * will construct all the Via, Record-Route, Call-ID, From, To, CSeq, and
- * Call-ID headers from the request.
- *
- * Note: the txdata reference counter is set to ZERO!.
- *
- * @param endpt The endpoint.
- * @param rdata The request receive data.
- * @param code Status code to be put in the response.
- * @param p_tdata Pointer to receive the transmit data.
- *
- * @return PJ_SUCCESS, or the appropriate error code.
- */
-PJ_DECL(pj_status_t) pjsip_endpt_create_response( pjsip_endpoint *endpt,
- const pjsip_rx_data *rdata,
- int code,
- pjsip_tx_data **p_tdata);
-
-/**
- * Construct a full ACK request for the received non-2xx final response.
- * This utility function is normally called by the transaction to construct
- * an ACK request to 3xx-6xx final response.
- * The generation of ACK message for 2xx final response is different than
- * this one.
- *
- * @param endpt The endpoint.
- * @param tdata On input, this contains the original INVITE request, and on
- * output, it contains the ACK message.
- * @param rdata The final response message.
- */
-PJ_DECL(void) pjsip_endpt_create_ack( pjsip_endpoint *endpt,
- pjsip_tx_data *tdata,
- const pjsip_rx_data *rdata );
-
-
-/**
- * Construct CANCEL request for the previously sent request.
- *
- * @param endpt The endpoint.
- * @param tdata The transmit buffer for the request being cancelled.
- * @param p_tdata Pointer to receive the transmit data.
- *
- * @return PJ_SUCCESS, or the appropriate error code.
- */
-PJ_DECL(pj_status_t) pjsip_endpt_create_cancel( pjsip_endpoint *endpt,
- pjsip_tx_data *tdata,
- pjsip_tx_data **p_tdata);
-
-
-/**
- * Get the address parameters (host, port, flag, TTL, etc) to send the
- * response.
- *
- * @param pool The pool.
- * @param tr The transport where the request was received.
- * @param via The top-most Via header of the request.
- * @param addr The send address concluded from the calculation.
- *
- * @return zero (PJ_OK) if successfull.
- */
-PJ_DECL(pj_status_t) pjsip_get_response_addr(pj_pool_t *pool,
- const pjsip_transport *tr,
- const pjsip_via_hdr *via,
- pjsip_host_port *addr);
-
-/**
- * @}
- */
-
-PJ_END_DECL
-
-#endif /* __PJSIP_SIP_MISC_H__ */
-
+/* $Id$ */
+/*
+ * Copyright (C) 2003-2006 Benny Prijono <benny@prijono.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#ifndef __PJSIP_SIP_MISC_H__
+#define __PJSIP_SIP_MISC_H__
+
+#include <pjsip/sip_msg.h>
+
+PJ_BEGIN_DECL
+
+/**
+ * @defgroup PJSIP_ENDPT SIP Endpoint
+ * @ingroup PJSIP
+ * @{
+ */
+
+/**
+ * Create an independent request message. This can be used to build any
+ * request outside a dialog, such as OPTIONS, MESSAGE, etc. To create a request
+ * inside a dialog, application should use #pjsip_dlg_create_request.
+ *
+ * Once a transmit data is created, the reference counter is initialized to 1.
+ *
+ * @param endpt Endpoint instance.
+ * @param method SIP Method.
+ * @param target Target URI.
+ * @param from URL to put in From header.
+ * @param to URL to put in To header.
+ * @param contact URL to put in Contact header.
+ * @param call_id Optional Call-ID (put NULL to generate unique Call-ID).
+ * @param cseq Optional CSeq (put -1 to generate random CSeq).
+ * @param text Optional text body (put NULL to omit body).
+ * @param p_tdata Pointer to receive the transmit data.
+ *
+ * @return PJ_SUCCESS, or the appropriate error code.
+ */
+PJ_DECL(pj_status_t) pjsip_endpt_create_request( pjsip_endpoint *endpt,
+ const pjsip_method *method,
+ const pj_str_t *target,
+ const pj_str_t *from,
+ const pj_str_t *to,
+ const pj_str_t *contact,
+ const pj_str_t *call_id,
+ int cseq,
+ const pj_str_t *text,
+ pjsip_tx_data **p_tdata);
+
+/**
+ * Create an independent request message from the specified headers. This
+ * function will shallow clone the headers and put them in the request.
+ *
+ * Once a transmit data is created, the reference counter is initialized to 1.
+ *
+ * @param endpt Endpoint instance.
+ * @param method SIP Method.
+ * @param target Target URI.
+ * @param from URL to put in From header.
+ * @param to URL to put in To header.
+ * @param contact URL to put in Contact header.
+ * @param call_id Optional Call-ID (put NULL to generate unique Call-ID).
+ * @param cseq Optional CSeq (put -1 to generate random CSeq).
+ * @param text Optional text body (put NULL to omit body).
+ * @param p_tdata Pointer to receive the transmit data.
+ *
+ * @return PJ_SUCCESS, or the appropriate error code.
+ */
+PJ_DECL(pj_status_t)
+pjsip_endpt_create_request_from_hdr( pjsip_endpoint *endpt,
+ const pjsip_method *method,
+ const pjsip_uri *target,
+ const pjsip_from_hdr *from,
+ const pjsip_to_hdr *to,
+ const pjsip_contact_hdr *contact,
+ const pjsip_cid_hdr *call_id,
+ int cseq,
+ const pj_str_t *text,
+ pjsip_tx_data **p_tdata);
+
+/**
+ * Send outgoing request and initiate UAC transaction for the request.
+ * This is an auxiliary function to be used by application to send arbitrary
+ * requests outside a dialog. To send a request within a dialog, application
+ * should use #pjsip_dlg_send_msg instead.
+ *
+ * @param endpt The endpoint instance.
+ * @param tdata The transmit data to be sent.
+ * @param timeout Optional timeout for final response to be received, or -1
+ * if the transaction should not have a timeout restriction.
+ * @param token Optional token to be associated with the transaction, and
+ * to be passed to the callback.
+ * @param cb Optional callback to be called when the transaction has
+ * received a final response. The callback will be called with
+ * the previously registered token and the event that triggers
+ * the completion of the transaction.
+ *
+ * @return PJ_SUCCESS, or the appropriate error code.
+ */
+PJ_DECL(pj_status_t) pjsip_endpt_send_request( pjsip_endpoint *endpt,
+ pjsip_tx_data *tdata,
+ int timeout,
+ void *token,
+ void (*cb)(void*,pjsip_event*));
+
+/**
+ * Construct a minimal response message for the received request. This function
+ * will construct all the Via, Record-Route, Call-ID, From, To, CSeq, and
+ * Call-ID headers from the request.
+ *
+ * Note: the txdata reference counter is set to ZERO!.
+ *
+ * @param endpt The endpoint.
+ * @param rdata The request receive data.
+ * @param code Status code to be put in the response.
+ * @param p_tdata Pointer to receive the transmit data.
+ *
+ * @return PJ_SUCCESS, or the appropriate error code.
+ */
+PJ_DECL(pj_status_t) pjsip_endpt_create_response( pjsip_endpoint *endpt,
+ const pjsip_rx_data *rdata,
+ int code,
+ pjsip_tx_data **p_tdata);
+
+/**
+ * Construct a full ACK request for the received non-2xx final response.
+ * This utility function is normally called by the transaction to construct
+ * an ACK request to 3xx-6xx final response.
+ * The generation of ACK message for 2xx final response is different than
+ * this one.
+ *
+ * @param endpt The endpoint.
+ * @param tdata On input, this contains the original INVITE request, and on
+ * output, it contains the ACK message.
+ * @param rdata The final response message.
+ */
+PJ_DECL(void) pjsip_endpt_create_ack( pjsip_endpoint *endpt,
+ pjsip_tx_data *tdata,
+ const pjsip_rx_data *rdata );
+
+
+/**
+ * Construct CANCEL request for the previously sent request.
+ *
+ * @param endpt The endpoint.
+ * @param tdata The transmit buffer for the request being cancelled.
+ * @param p_tdata Pointer to receive the transmit data.
+ *
+ * @return PJ_SUCCESS, or the appropriate error code.
+ */
+PJ_DECL(pj_status_t) pjsip_endpt_create_cancel( pjsip_endpoint *endpt,
+ pjsip_tx_data *tdata,
+ pjsip_tx_data **p_tdata);
+
+
+/**
+ * Get the address parameters (host, port, flag, TTL, etc) to send the
+ * response.
+ *
+ * @param pool The pool.
+ * @param tr The transport where the request was received.
+ * @param via The top-most Via header of the request.
+ * @param addr The send address concluded from the calculation.
+ *
+ * @return zero (PJ_OK) if successfull.
+ */
+PJ_DECL(pj_status_t) pjsip_get_response_addr(pj_pool_t *pool,
+ const pjsip_transport *tr,
+ const pjsip_via_hdr *via,
+ pjsip_host_port *addr);
+
+/**
+ * @}
+ */
+
+PJ_END_DECL
+
+#endif /* __PJSIP_SIP_MISC_H__ */
+
diff --git a/pjsip/src/pjsip-simple/event_notify.c b/pjsip/src/pjsip-simple/event_notify.c
index 4879f884..25869c40 100644
--- a/pjsip/src/pjsip-simple/event_notify.c
+++ b/pjsip/src/pjsip-simple/event_notify.c
@@ -1,1644 +1,1644 @@
-/* $Id$ */
-/*
- * Copyright (C) 2003-2006 Benny Prijono <benny@prijono.org>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
-#include <pjsip_simple/event_notify.h>
-#include <pjsip/sip_msg.h>
-#include <pjsip/sip_util.h>
-#include <pjsip/sip_endpoint.h>
-#include <pjsip/sip_module.h>
-#include <pjsip/sip_transaction.h>
-#include <pjsip/sip_event.h>
-#include <pj/pool.h>
-#include <pj/timer.h>
-#include <pj/string.h>
-#include <pj/hash.h>
-#include <pj/os.h>
-#include <pj/except.h>
-#include <pj/log.h>
-#include <pj/guid.h>
-
-#define THIS_FILE "event_sub"
-
-/* String names for state.
- * The names here should be compliant with sub_state names in RFC3265.
- */
-static const pj_str_t state[] = {
- { "null", 4 },
- { "active", 6 },
- { "pending", 7 },
- { "terminated", 10 },
- { "unknown", 7 }
-};
-
-/* Timer IDs */
-#define TIMER_ID_REFRESH 1
-#define TIMER_ID_UAS_EXPIRY 2
-
-/* Static configuration. */
-#define SECONDS_BEFORE_EXPIRY 10
-#define MGR_POOL_SIZE 512
-#define MGR_POOL_INC 0
-#define SUB_POOL_SIZE 2048
-#define SUB_POOL_INC 0
-#define HASH_TABLE_SIZE 32
-
-/* Static vars. */
-static int mod_id;
-static const pjsip_method SUBSCRIBE = { PJSIP_OTHER_METHOD, {"SUBSCRIBE", 9}};
-static const pjsip_method NOTIFY = { PJSIP_OTHER_METHOD, { "NOTIFY", 6}};
-
-typedef struct package
-{
- PJ_DECL_LIST_MEMBER(struct package)
- pj_str_t event;
- int accept_cnt;
- pj_str_t *accept;
- pjsip_event_sub_pkg_cb cb;
-} package;
-
-/* Event subscription manager singleton instance. */
-static struct pjsip_event_sub_mgr
-{
- pj_pool_t *pool;
- pj_hash_table_t *ht;
- pjsip_endpoint *endpt;
- pj_mutex_t *mutex;
- pjsip_allow_events_hdr *allow_events;
- package pkg_list;
-} mgr;
-
-/* Fordward declarations for static functions. */
-static pj_status_t mod_init(pjsip_endpoint *, pjsip_module *, pj_uint32_t);
-static pj_status_t mod_deinit(pjsip_module*);
-static void tsx_handler(pjsip_module*, pjsip_event*);
-static pjsip_event_sub *find_sub(pjsip_rx_data *);
-static void on_subscribe_request(pjsip_transaction*, pjsip_rx_data*);
-static void on_subscribe_response(void *, pjsip_event*);
-static void on_notify_request(pjsip_transaction *, pjsip_rx_data*);
-static void on_notify_response(void *, pjsip_event *);
-static void refresh_timer_cb(pj_timer_heap_t*, pj_timer_entry*);
-static void uas_expire_timer_cb(pj_timer_heap_t*, pj_timer_entry*);
-static pj_status_t send_sub_refresh( pjsip_event_sub *sub );
-
-/* Module descriptor. */
-static pjsip_module event_sub_module =
-{
- {"EventSub", 8}, /* Name. */
- 0, /* Flag */
- 128, /* Priority */
- &mgr, /* User data. */
- 2, /* Number of methods supported . */
- { &SUBSCRIBE, &NOTIFY }, /* Array of methods */
- &mod_init, /* init_module() */
- NULL, /* start_module() */
- &mod_deinit, /* deinit_module() */
- &tsx_handler, /* tsx_handler() */
-};
-
-/*
- * Module initialization.
- * This will be called by endpoint when it initializes all modules.
- */
-static pj_status_t mod_init( pjsip_endpoint *endpt,
- struct pjsip_module *mod, pj_uint32_t id )
-{
- pj_pool_t *pool;
-
- pool = pjsip_endpt_create_pool(endpt, "esubmgr", MGR_POOL_SIZE, MGR_POOL_INC);
- if (!pool)
- return -1;
-
- /* Manager initialization: create hash table and mutex. */
- mgr.pool = pool;
- mgr.endpt = endpt;
- mgr.ht = pj_hash_create(pool, HASH_TABLE_SIZE);
- if (!mgr.ht)
- return -1;
-
- mgr.mutex = pj_mutex_create(pool, "esubmgr", PJ_MUTEX_SIMPLE);
- if (!mgr.mutex)
- return -1;
-
- /* Attach manager to module. */
- mod->mod_data = &mgr;
-
- /* Init package list. */
- pj_list_init(&mgr.pkg_list);
-
- /* Init Allow-Events header. */
- mgr.allow_events = pjsip_allow_events_hdr_create(mgr.pool);
-
- /* Save the module ID. */
- mod_id = id;
-
- pjsip_event_notify_init_parser();
- return 0;
-}
-
-/*
- * Module deinitialization.
- * Called by endpoint.
- */
-static pj_status_t mod_deinit( struct pjsip_module *mod )
-{
- pj_mutex_lock(mgr.mutex);
- pj_mutex_destroy(mgr.mutex);
- pjsip_endpt_destroy_pool(mgr.endpt, mgr.pool);
- return 0;
-}
-
-/*
- * This public function is called by application to register callback.
- * In exchange, the instance of the module is returned.
- */
-PJ_DEF(pjsip_module*) pjsip_event_sub_get_module(void)
-{
- return &event_sub_module;
-}
-
-/*
- * Register event package.
- */
-PJ_DEF(pj_status_t) pjsip_event_sub_register_pkg( const pj_str_t *event,
- int accept_cnt,
- const pj_str_t accept[],
- const pjsip_event_sub_pkg_cb *cb )
-{
- package *pkg;
- int i;
-
- pj_mutex_lock(mgr.mutex);
-
- /* Create and register new package. */
- pkg = pj_pool_alloc(mgr.pool, sizeof(*pkg));
- pj_strdup(mgr.pool, &pkg->event, event);
- pj_list_insert_before(&mgr.pkg_list, pkg);
-
- /* Save Accept specification. */
- pkg->accept_cnt = accept_cnt;
- pkg->accept = pj_pool_alloc(mgr.pool, accept_cnt*sizeof(pj_str_t));
- for (i=0; i<accept_cnt; ++i) {
- pj_strdup(mgr.pool, &pkg->accept[i], &accept[i]);
- }
-
- /* Copy callback. */
- pj_memcpy(&pkg->cb, cb, sizeof(*cb));
-
- /* Update Allow-Events header. */
- pj_assert(mgr.allow_events->event_cnt < PJSIP_MAX_ALLOW_EVENTS);
- mgr.allow_events->events[mgr.allow_events->event_cnt++] = pkg->event;
-
- pj_mutex_unlock(mgr.mutex);
- return 0;
-}
-
-/*
- * Create subscription key (for hash table).
- */
-static void create_subscriber_key( pj_str_t *key, pj_pool_t *pool,
- pjsip_role_e role,
- const pj_str_t *call_id, const pj_str_t *from_tag)
-{
- char *p;
-
- p = key->ptr = pj_pool_alloc(pool, call_id->slen + from_tag->slen + 3);
- *p++ = (role == PJSIP_ROLE_UAS ? 'S' : 'C');
- *p++ = '$';
- pj_memcpy(p, call_id->ptr, call_id->slen);
- p += call_id->slen;
- *p++ = '$';
- pj_memcpy(p, from_tag->ptr, from_tag->slen);
- p += from_tag->slen;
-
- key->slen = p - key->ptr;
-}
-
-
-/*
- * Create UAC subscription.
- */
-PJ_DEF(pjsip_event_sub*) pjsip_event_sub_create( pjsip_endpoint *endpt,
- const pj_str_t *from,
- const pj_str_t *to,
- const pj_str_t *event,
- int expires,
- int accept_cnt,
- const pj_str_t accept[],
- void *user_data,
- const pjsip_event_sub_cb *cb)
-{
- pjsip_tx_data *tdata;
- pj_pool_t *pool;
- const pjsip_hdr *hdr;
- pjsip_event_sub *sub;
- PJ_USE_EXCEPTION;
-
- PJ_LOG(5,(THIS_FILE, "Creating event subscription %.*s to %.*s",
- event->slen, event->ptr, to->slen, to->ptr));
-
- /* Create pool for the event subscription. */
- pool = pjsip_endpt_create_pool(endpt, "esub", SUB_POOL_SIZE, SUB_POOL_INC);
- if (!pool) {
- return NULL;
- }
-
- /* Init subscription. */
- sub = pj_pool_calloc(pool, 1, sizeof(*sub));
- sub->pool = pool;
- sub->endpt = endpt;
- sub->role = PJSIP_ROLE_UAC;
- sub->state = PJSIP_EVENT_SUB_STATE_PENDING;
- sub->state_str = state[sub->state];
- sub->user_data = user_data;
- sub->timer.id = 0;
- sub->default_interval = expires;
- pj_memcpy(&sub->cb, cb, sizeof(*cb));
- pj_list_init(&sub->auth_sess);
- pj_list_init(&sub->route_set);
- sub->mutex = pj_mutex_create(pool, "esub", PJ_MUTEX_RECURSE);
- if (!sub->mutex) {
- pjsip_endpt_destroy_pool(endpt, pool);
- return NULL;
- }
-
- /* The easiest way to parse the parameters is to create a dummy request! */
- tdata = pjsip_endpt_create_request( endpt, &SUBSCRIBE, to, from, to, from,
- NULL, -1, NULL);
- if (!tdata) {
- pj_mutex_destroy(sub->mutex);
- pjsip_endpt_destroy_pool(endpt, pool);
- return NULL;
- }
-
- /*
- * Duplicate headers in the request to our structure.
- */
- PJ_TRY {
- int i;
-
- /* From */
- hdr = pjsip_msg_find_hdr(tdata->msg, PJSIP_H_FROM, NULL);
- pj_assert(hdr != NULL);
- sub->from = pjsip_hdr_clone(pool, hdr);
-
- /* To */
- hdr = pjsip_msg_find_hdr(tdata->msg, PJSIP_H_TO, NULL);
- pj_assert(hdr != NULL);
- sub->to = pjsip_hdr_clone(pool, hdr);
-
- /* Contact. */
- sub->contact = pjsip_contact_hdr_create(pool);
- sub->contact->uri = sub->from->uri;
-
- /* Call-ID */
- hdr = pjsip_msg_find_hdr(tdata->msg, PJSIP_H_CALL_ID, NULL);
- pj_assert(hdr != NULL);
- sub->call_id = pjsip_hdr_clone(pool, hdr);
-
- /* CSeq */
- sub->cseq = pj_rand() % 0xFFFF;
-
- /* Event. */
- sub->event = pjsip_event_hdr_create(sub->pool);
- pj_strdup(pool, &sub->event->event_type, event);
-
- /* Expires. */
- sub->uac_expires = pjsip_expires_hdr_create(pool);
- sub->uac_expires->ivalue = expires;
-
- /* Accept. */
- sub->local_accept = pjsip_accept_hdr_create(pool);
- for (i=0; i<accept_cnt && i < PJSIP_MAX_ACCEPT_COUNT; ++i) {
- sub->local_accept->count++;
- pj_strdup(sub->pool, &sub->local_accept->values[i], &accept[i]);
- }
-
- /* Register to hash table. */
- create_subscriber_key( &sub->key, pool, PJSIP_ROLE_UAC,
- &sub->call_id->id, &sub->from->tag);
- pj_mutex_lock( mgr.mutex );
- pj_hash_set( pool, mgr.ht, sub->key.ptr, sub->key.slen, sub);
- pj_mutex_unlock( mgr.mutex );
-
- }
- PJ_DEFAULT {
- PJ_LOG(4,(THIS_FILE, "event_sub%p (%s): caught exception %d during init",
- sub, state[sub->state].ptr, PJ_GET_EXCEPTION()));
-
- pjsip_tx_data_dec_ref(tdata);
- pj_mutex_destroy(sub->mutex);
- pjsip_endpt_destroy_pool(endpt, sub->pool);
- return NULL;
- }
- PJ_END;
-
- /* All set, delete temporary transmit data as we don't need it. */
- pjsip_tx_data_dec_ref(tdata);
-
- PJ_LOG(4,(THIS_FILE, "event_sub%p (%s): client created, target=%.*s, event=%.*s",
- sub, state[sub->state].ptr,
- to->slen, to->ptr, event->slen, event->ptr));
-
- return sub;
-}
-
-/*
- * Set credentials.
- */
-PJ_DEF(pj_status_t) pjsip_event_sub_set_credentials( pjsip_event_sub *sub,
- int count,
- const pjsip_cred_info cred[])
-{
- pj_mutex_lock(sub->mutex);
- if (count > 0) {
- sub->cred_info = pj_pool_alloc(sub->pool, count*sizeof(pjsip_cred_info));
- pj_memcpy( sub->cred_info, cred, count*sizeof(pjsip_cred_info));
- }
- sub->cred_cnt = count;
- pj_mutex_unlock(sub->mutex);
- return 0;
-}
-
-/*
- * Set route-set.
- */
-PJ_DEF(pj_status_t) pjsip_event_sub_set_route_set( pjsip_event_sub *sub,
- const pjsip_route_hdr *route_set )
-{
- const pjsip_route_hdr *hdr;
-
- pj_mutex_lock(sub->mutex);
-
- /* Clear existing route set. */
- pj_list_init(&sub->route_set);
-
- /* Duplicate route headers. */
- hdr = route_set->next;
- while (hdr != route_set) {
- pjsip_route_hdr *new_hdr = pjsip_hdr_clone(sub->pool, hdr);
- pj_list_insert_before(&sub->route_set, new_hdr);
- hdr = hdr->next;
- }
-
- pj_mutex_unlock(sub->mutex);
-
- return 0;
-}
-
-/*
- * Send subscribe request.
- */
-PJ_DEF(pj_status_t) pjsip_event_sub_subscribe( pjsip_event_sub *sub )
-{
- pj_status_t status;
-
- pj_mutex_lock(sub->mutex);
- status = send_sub_refresh(sub);
- pj_mutex_unlock(sub->mutex);
-
- return status;
-}
-
-/*
- * Destroy subscription.
- * If there are pending transactions, then this will just set the flag.
- */
-PJ_DEF(pj_status_t) pjsip_event_sub_destroy(pjsip_event_sub *sub)
-{
- pj_assert(sub != NULL);
- if (sub == NULL)
- return -1;
-
- /* Application must terminate the subscription first. */
- pj_assert(sub->state == PJSIP_EVENT_SUB_STATE_NULL ||
- sub->state == PJSIP_EVENT_SUB_STATE_TERMINATED);
-
- PJ_LOG(4,(THIS_FILE, "event_sub%p (%s): about to be destroyed",
- sub, state[sub->state].ptr));
-
- pj_mutex_lock(mgr.mutex);
- pj_mutex_lock(sub->mutex);
-
- /* Set delete flag. */
- sub->delete_flag = 1;
-
- /* Unregister timer, if any. */
- if (sub->timer.id != 0) {
- pjsip_endpt_cancel_timer(sub->endpt, &sub->timer);
- sub->timer.id = 0;
- }
-
- if (sub->pending_tsx > 0) {
- pj_mutex_unlock(sub->mutex);
- pj_mutex_unlock(mgr.mutex);
- PJ_LOG(4,(THIS_FILE, "event_sub%p (%s): has %d pending, will destroy later",
- sub, state[sub->state].ptr,
- sub->pending_tsx));
- return 1;
- }
-
- /* Unregister from hash table. */
- pj_hash_set(sub->pool, mgr.ht, sub->key.ptr, sub->key.slen, NULL);
-
- /* Destroy. */
- pj_mutex_destroy(sub->mutex);
- pjsip_endpt_destroy_pool(sub->endpt, sub->pool);
-
- pj_mutex_unlock(mgr.mutex);
-
- PJ_LOG(4,(THIS_FILE, "event_sub%p: destroyed", sub));
- return 0;
-}
-
-/* Change state. */
-static void sub_set_state( pjsip_event_sub *sub, int new_state)
-{
- PJ_LOG(4,(THIS_FILE, "event_sub%p (%s): changed state to %s",
- sub, state[sub->state].ptr, state[new_state].ptr));
- sub->state = new_state;
- sub->state_str = state[new_state];
-}
-
-/*
- * Refresh subscription.
- */
-static pj_status_t send_sub_refresh( pjsip_event_sub *sub )
-{
- pjsip_tx_data *tdata;
- pj_status_t status;
- const pjsip_route_hdr *route;
-
- pj_assert(sub->role == PJSIP_ROLE_UAC);
- pj_assert(sub->state != PJSIP_EVENT_SUB_STATE_TERMINATED);
- if (sub->role != PJSIP_ROLE_UAC ||
- sub->state == PJSIP_EVENT_SUB_STATE_TERMINATED)
- {
- return -1;
- }
-
- PJ_LOG(4,(THIS_FILE, "event_sub%p (%s): refreshing subscription",
- sub, state[sub->state].ptr));
-
- /* Create request. */
- tdata = pjsip_endpt_create_request_from_hdr( sub->endpt,
- &SUBSCRIBE,
- sub->to->uri,
- sub->from, sub->to,
- sub->contact, sub->call_id,
- sub->cseq++,
- NULL);
-
- if (!tdata) {
- PJ_LOG(4,(THIS_FILE, "event_sub%p (%s): refresh: unable to create tx data!",
- sub, state[sub->state].ptr));
- return -1;
- }
-
- pjsip_msg_add_hdr( tdata->msg,
- pjsip_hdr_shallow_clone(tdata->pool, sub->event));
- pjsip_msg_add_hdr( tdata->msg,
- pjsip_hdr_shallow_clone(tdata->pool, sub->uac_expires));
- pjsip_msg_add_hdr( tdata->msg,
- pjsip_hdr_shallow_clone(tdata->pool, sub->local_accept));
- pjsip_msg_add_hdr( tdata->msg,
- pjsip_hdr_shallow_clone(tdata->pool, mgr.allow_events));
-
- /* Authentication */
- pjsip_auth_init_req( sub->pool, tdata, &sub->auth_sess,
- sub->cred_cnt, sub->cred_info);
-
- /* Route set. */
- route = sub->route_set.next;
- while (route != &sub->route_set) {
- pj_list_insert_before( &tdata->msg->hdr,
- pjsip_hdr_shallow_clone(tdata->pool, route));
- route = route->next;
- }
-
- /* Send */
- status = pjsip_endpt_send_request( sub->endpt, tdata, -1, sub,
- &on_subscribe_response);
- if (status == 0) {
- sub->pending_tsx++;
- } else {
- PJ_LOG(4,(THIS_FILE, "event_sub%p (%s): FAILED to refresh subscription!",
- sub, state[sub->state].ptr));
- }
-
- return status;
-}
-
-/*
- * Stop subscription.
- */
-PJ_DEF(pj_status_t) pjsip_event_sub_unsubscribe( pjsip_event_sub *sub )
-{
- pjsip_tx_data *tdata;
- const pjsip_route_hdr *route;
- pj_status_t status;
-
- PJ_LOG(4,(THIS_FILE, "event_sub%p (%s): unsubscribing...",
- sub, state[sub->state].ptr));
-
- /* Lock subscription. */
- pj_mutex_lock(sub->mutex);
-
- pj_assert(sub->role == PJSIP_ROLE_UAC);
-
- /* Kill refresh timer, if any. */
- if (sub->timer.id != 0) {
- sub->timer.id = 0;
- pjsip_endpt_cancel_timer(sub->endpt, &sub->timer);
- }
-
- /* Create request. */
- tdata = pjsip_endpt_create_request_from_hdr( sub->endpt,
- &SUBSCRIBE,
- sub->to->uri,
- sub->from, sub->to,
- sub->contact, sub->call_id,
- sub->cseq++,
- NULL);
-
- if (!tdata) {
- pj_mutex_unlock(sub->mutex);
- return -1;
- }
-
- /* Add headers to request. */
- pjsip_msg_add_hdr( tdata->msg, pjsip_hdr_shallow_clone(tdata->pool, sub->event));
- sub->uac_expires->ivalue = 0;
- pjsip_msg_add_hdr( tdata->msg, pjsip_hdr_shallow_clone(tdata->pool, sub->uac_expires));
-
- /* Add authentication. */
- pjsip_auth_init_req( sub->pool, tdata, &sub->auth_sess,
- sub->cred_cnt, sub->cred_info);
-
-
- /* Route set. */
- route = sub->route_set.next;
- while (route != &sub->route_set) {
- pj_list_insert_before( &tdata->msg->hdr,
- pjsip_hdr_shallow_clone(tdata->pool, route));
- route = route->next;
- }
-
- /* Prevent timer from refreshing itself. */
- sub->default_interval = 0;
-
- /* Set state. */
- sub_set_state( sub, PJSIP_EVENT_SUB_STATE_TERMINATED );
-
- /* Send the request. */
- status = pjsip_endpt_send_request( sub->endpt, tdata, -1, sub,
- &on_subscribe_response);
- if (status == 0) {
- sub->pending_tsx++;
- }
-
- pj_mutex_unlock(sub->mutex);
-
- if (status != 0) {
- PJ_LOG(4,(THIS_FILE, "event_sub%p (%s): FAILED to unsubscribe!",
- sub, state[sub->state].ptr));
- }
-
- return status;
-}
-
-/*
- * Send notify.
- */
-PJ_DEF(pj_status_t) pjsip_event_sub_notify(pjsip_event_sub *sub,
- pjsip_event_sub_state new_state,
- const pj_str_t *reason,
- pjsip_msg_body *body)
-{
- pjsip_tx_data *tdata;
- pjsip_sub_state_hdr *ss_hdr;
- const pjsip_route_hdr *route;
- pj_time_val now;
- pj_status_t status;
- pjsip_event_sub_state old_state = sub->state;
-
- pj_gettimeofday(&now);
-
- pj_assert(sub->role == PJSIP_ROLE_UAS);
- if (sub->role != PJSIP_ROLE_UAS)
- return -1;
-
- PJ_LOG(4,(THIS_FILE, "event_sub%p (%s): sending NOTIFY",
- sub, state[new_state].ptr));
-
- /* Lock subscription. */
- pj_mutex_lock(sub->mutex);
-
- /* Can not send NOTIFY if current state is NULL. We can accept TERMINATED. */
- if (sub->state==PJSIP_EVENT_SUB_STATE_NULL) {
- pj_assert(0);
- pj_mutex_unlock(sub->mutex);
- return -1;
- }
-
- /* Update state no matter what. */
- sub_set_state(sub, new_state);
-
- /* Create transmit data. */
- tdata = pjsip_endpt_create_request_from_hdr( sub->endpt,
- &NOTIFY,
- sub->to->uri,
- sub->from, sub->to,
- sub->contact, sub->call_id,
- sub->cseq++,
- NULL);
- if (!tdata) {
- pj_mutex_unlock(sub->mutex);
- return -1;
- }
-
- /* Add Event header. */
- pjsip_msg_add_hdr(tdata->msg, pjsip_hdr_shallow_clone(tdata->pool, sub->event));
-
- /* Add Subscription-State header. */
- ss_hdr = pjsip_sub_state_hdr_create(tdata->pool);
- ss_hdr->sub_state = state[new_state];
- ss_hdr->expires_param = sub->expiry_time.sec - now.sec;
- if (ss_hdr->expires_param < 0)
- ss_hdr->expires_param = 0;
- if (reason)
- pj_strdup(tdata->pool, &ss_hdr->reason_param, reason);
- pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr*)ss_hdr);
-
- /* Add Allow-Events header. */
- pjsip_msg_add_hdr( tdata->msg,
- pjsip_hdr_shallow_clone(tdata->pool, mgr.allow_events));
-
- /* Add authentication */
- pjsip_auth_init_req( sub->pool, tdata, &sub->auth_sess,
- sub->cred_cnt, sub->cred_info);
-
- /* Route set. */
- route = sub->route_set.next;
- while (route != &sub->route_set) {
- pj_list_insert_before( &tdata->msg->hdr,
- pjsip_hdr_shallow_clone(tdata->pool, route));
- route = route->next;
- }
-
- /* Attach body. */
- tdata->msg->body = body;
-
- /* That's it, send! */
- status = pjsip_endpt_send_request( sub->endpt, tdata, -1, sub, &on_notify_response);
- if (status == 0)
- sub->pending_tsx++;
-
- /* If terminated notify application. */
- if (new_state!=old_state && new_state==PJSIP_EVENT_SUB_STATE_TERMINATED) {
- if (sub->cb.on_sub_terminated) {
- sub->pending_tsx++;
- (*sub->cb.on_sub_terminated)(sub, reason);
- sub->pending_tsx--;
- }
- }
-
- /* Unlock subscription. */
- pj_mutex_unlock(sub->mutex);
-
- if (status != 0) {
- PJ_LOG(4,(THIS_FILE, "event_sub%p (%s): failed to send NOTIFY",
- sub, state[sub->state].ptr));
- }
-
- if (sub->delete_flag && sub->pending_tsx <= 0) {
- pjsip_event_sub_destroy(sub);
- }
- return status;
-}
-
-
-/* If this timer callback is called, it means subscriber hasn't refreshed its
- * subscription on-time. Set the state to terminated. This will also send
- * NOTIFY with Subscription-State set to terminated.
- */
-static void uas_expire_timer_cb( pj_timer_heap_t *timer_heap, pj_timer_entry *entry)
-{
- pjsip_event_sub *sub = entry->user_data;
- pj_str_t reason = { "timeout", 7 };
-
- PJ_LOG(4,(THIS_FILE, "event_sub%p (%s): UAS subscription expired!",
- sub, state[sub->state].ptr));
-
- pj_mutex_lock(sub->mutex);
- sub->timer.id = 0;
-
- if (sub->cb.on_sub_terminated && sub->state!=PJSIP_EVENT_SUB_STATE_TERMINATED) {
- /* Notify application, but prevent app from destroying the sub. */
- ++sub->pending_tsx;
- (*sub->cb.on_sub_terminated)(sub, &reason);
- --sub->pending_tsx;
- }
- //pjsip_event_sub_notify( sub, PJSIP_EVENT_SUB_STATE_TERMINATED,
- // &reason, NULL);
- pj_mutex_unlock(sub->mutex);
-
-}
-
-/* Schedule notifier expiration. */
-static void sub_schedule_uas_expire( pjsip_event_sub *sub, int sec_delay)
-{
- pj_time_val delay = { 0, 0 };
- pj_parsed_time pt;
-
- if (sub->timer.id != 0)
- pjsip_endpt_cancel_timer(sub->endpt, &sub->timer);
-
- pj_gettimeofday(&sub->expiry_time);
- sub->expiry_time.sec += sec_delay;
-
- sub->timer.id = TIMER_ID_UAS_EXPIRY;
- sub->timer.user_data = sub;
- sub->timer.cb = &uas_expire_timer_cb;
- delay.sec = sec_delay;
- pjsip_endpt_schedule_timer( sub->endpt, &sub->timer, &delay);
-
- pj_time_decode(&sub->expiry_time, &pt);
- PJ_LOG(4,(THIS_FILE,
- "event_sub%p (%s)(UAS): will expire at %02d:%02d:%02d (in %d secs)",
- sub, state[sub->state].ptr, pt.hour, pt.min, pt.sec, sec_delay));
-}
-
-/* This timer is called for UAC to refresh the subscription. */
-static void refresh_timer_cb( pj_timer_heap_t *timer_heap, pj_timer_entry *entry)
-{
- pjsip_event_sub *sub = entry->user_data;
-
- PJ_LOG(4,(THIS_FILE, "event_sub%p (%s): refresh subscription timer",
- sub, state[sub->state].ptr));
-
- pj_mutex_lock(sub->mutex);
- sub->timer.id = 0;
- send_sub_refresh(sub);
- pj_mutex_unlock(sub->mutex);
-}
-
-
-/* This will update the UAC's refresh schedule. */
-static void update_next_refresh(pjsip_event_sub *sub, int interval)
-{
- pj_time_val delay = {0, 0};
- pj_parsed_time pt;
-
- if (interval < SECONDS_BEFORE_EXPIRY) {
- PJ_LOG(4,(THIS_FILE,
- "event_sub%p (%s): expiration delay too short (%d sec)! updated.",
- sub, state[sub->state].ptr, interval));
- interval = SECONDS_BEFORE_EXPIRY;
- }
-
- if (sub->timer.id != 0)
- pjsip_endpt_cancel_timer(sub->endpt, &sub->timer);
-
- sub->timer.id = TIMER_ID_REFRESH;
- sub->timer.user_data = sub;
- sub->timer.cb = &refresh_timer_cb;
- pj_gettimeofday(&sub->expiry_time);
- delay.sec = interval - SECONDS_BEFORE_EXPIRY;
- sub->expiry_time.sec += delay.sec;
-
- pj_time_decode(&sub->expiry_time, &pt);
- PJ_LOG(4,(THIS_FILE,
- "event_sub%p (%s): will send SUBSCRIBE at %02d:%02d:%02d (in %d secs)",
- sub, state[sub->state].ptr,
- pt.hour, pt.min, pt.sec,
- delay.sec));
-
- pjsip_endpt_schedule_timer( sub->endpt, &sub->timer, &delay );
-}
-
-
-/* Find subscription in the hash table.
- * If found, lock the subscription before returning to caller.
- */
-static pjsip_event_sub *find_sub(pjsip_rx_data *rdata)
-{
- pj_str_t key;
- pjsip_role_e role;
- pjsip_event_sub *sub;
- pjsip_method *method = &rdata->msg->line.req.method;
- pj_str_t *tag;
-
- if (rdata->msg->type == PJSIP_REQUEST_MSG) {
- if (pjsip_method_cmp(method, &SUBSCRIBE)==0) {
- role = PJSIP_ROLE_UAS;
- tag = &rdata->to_tag;
- } else {
- pj_assert(pjsip_method_cmp(method, &NOTIFY) == 0);
- role = PJSIP_ROLE_UAC;
- tag = &rdata->to_tag;
- }
- } else {
- if (pjsip_method_cmp(&rdata->cseq->method, &SUBSCRIBE)==0) {
- role = PJSIP_ROLE_UAC;
- tag = &rdata->from_tag;
- } else {
- pj_assert(pjsip_method_cmp(method, &NOTIFY) == 0);
- role = PJSIP_ROLE_UAS;
- tag = &rdata->from_tag;
- }
- }
- create_subscriber_key( &key, rdata->pool, role, &rdata->call_id, tag);
-
- pj_mutex_lock(mgr.mutex);
- sub = pj_hash_get(mgr.ht, key.ptr, key.slen);
- if (sub)
- pj_mutex_lock(sub->mutex);
- pj_mutex_unlock(mgr.mutex);
-
- return sub;
-}
-
-
-/* This function is called when we receive SUBSCRIBE request message
- * to refresh existing subscription.
- */
-static void on_received_sub_refresh( pjsip_event_sub *sub,
- pjsip_transaction *tsx, pjsip_rx_data *rdata)
-{
- pjsip_event_hdr *e;
- pjsip_expires_hdr *expires;
- pj_str_t hname;
- int status = 200;
- pj_str_t reason_phrase = { NULL, 0 };
- int new_state = sub->state;
- int old_state = sub->state;
- int new_interval = 0;
- pjsip_tx_data *tdata;
-
- PJ_LOG(4,(THIS_FILE, "event_sub%p (%s): received target refresh",
- sub, state[sub->state].ptr));
-
- /* Check that the event matches. */
- hname = pj_str("Event");
- e = pjsip_msg_find_hdr_by_name( rdata->msg, &hname, NULL);
- if (!e) {
- status = 400;
- reason_phrase = pj_str("Missing Event header");
- goto send_response;
- }
- if (pj_stricmp(&e->event_type, &sub->event->event_type) != 0 ||
- pj_stricmp(&e->id_param, &sub->event->id_param) != 0)
- {
- status = 481;
- reason_phrase = pj_str("Subscription does not exist");
- goto send_response;
- }
-
- /* Check server state. */
- if (sub->state == PJSIP_EVENT_SUB_STATE_TERMINATED) {
- status = 481;
- reason_phrase = pj_str("Subscription does not exist");
- goto send_response;
- }
-
- /* Check expires header. */
- expires = pjsip_msg_find_hdr(rdata->msg, PJSIP_H_EXPIRES, NULL);
- if (!expires) {
- /*
- status = 400;
- reason_phrase = pj_str("Missing Expires header");
- goto send_response;
- */
- new_interval = sub->default_interval;
- } else {
- /* Check that interval is not too short.
- * Note that expires time may be zero (for unsubscription).
- */
- new_interval = expires->ivalue;
- if (new_interval != 0 && new_interval < SECONDS_BEFORE_EXPIRY) {
- status = PJSIP_SC_INTERVAL_TOO_BRIEF;
- goto send_response;
- }
- }
-
- /* Update interval. */
- sub->default_interval = new_interval;
- pj_gettimeofday(&sub->expiry_time);
- sub->expiry_time.sec += new_interval;
-
- /* Update timer only if this is not unsubscription. */
- if (new_interval > 0) {
- sub->default_interval = new_interval;
- sub_schedule_uas_expire( sub, new_interval );
-
- /* Call callback. */
- if (sub->cb.on_received_refresh) {
- sub->pending_tsx++;
- (*sub->cb.on_received_refresh)(sub, rdata);
- sub->pending_tsx--;
- }
- }
-
-send_response:
- tdata = pjsip_endpt_create_response( sub->endpt, rdata, status);
- if (tdata) {
- if (reason_phrase.slen)
- tdata->msg->line.status.reason = reason_phrase;
-
- /* Add Expires header. */
- expires = pjsip_expires_hdr_create(tdata->pool);
- expires->ivalue = sub->default_interval;
- pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr*)expires);
-
- if (PJSIP_IS_STATUS_IN_CLASS(status,200)) {
- pjsip_msg_add_hdr(tdata->msg,
- pjsip_hdr_shallow_clone(tdata->pool, mgr.allow_events));
- }
- /* Send down to transaction. */
- pjsip_tsx_on_tx_msg(tsx, tdata);
- }
-
- if (sub->default_interval==0 || !PJSIP_IS_STATUS_IN_CLASS(status,200)) {
- /* Notify application if sub is terminated. */
- new_state = PJSIP_EVENT_SUB_STATE_TERMINATED;
- sub_set_state(sub, new_state);
- if (new_state!=old_state && sub->cb.on_sub_terminated) {
- pj_str_t reason = {"", 0};
- if (reason_phrase.slen) reason = reason_phrase;
- else reason = *pjsip_get_status_text(status);
-
- sub->pending_tsx++;
- (*sub->cb.on_sub_terminated)(sub, &reason);
- sub->pending_tsx--;
- }
- }
-
- pj_mutex_unlock(sub->mutex);
-
- /* Prefer to call log when we're not holding the mutex. */
- PJ_LOG(4,(THIS_FILE, "event_sub%p (%s): sent refresh response %s, status=%d",
- sub, state[sub->state].ptr,
- (tdata ? tdata->obj_name : "null"), status));
-
- /* Check if application has requested deletion. */
- if (sub->delete_flag && sub->pending_tsx <= 0) {
- pjsip_event_sub_destroy(sub);
- }
-
-}
-
-
-/* This function is called when we receive SUBSCRIBE request message for
- * a new subscription.
- */
-static void on_new_subscription( pjsip_transaction *tsx, pjsip_rx_data *rdata )
-{
- package *pkg;
- pj_pool_t *pool;
- pjsip_event_sub *sub = NULL;
- pj_str_t hname;
- int status = 200;
- pj_str_t reason = { NULL, 0 };
- pjsip_tx_data *tdata;
- pjsip_expires_hdr *expires;
- pjsip_accept_hdr *accept;
- pjsip_event_hdr *evhdr;
-
- /* Get the Event header. */
- hname = pj_str("Event");
- evhdr = pjsip_msg_find_hdr_by_name(rdata->msg, &hname, NULL);
- if (!evhdr) {
- status = 400;
- reason = pj_str("No Event header in request");
- goto send_response;
- }
-
- /* Find corresponding package.
- * We don't lock the manager's mutex since we assume the package list
- * won't change once the application is running!
- */
- pkg = mgr.pkg_list.next;
- while (pkg != &mgr.pkg_list) {
- if (pj_stricmp(&pkg->event, &evhdr->event_type) == 0)
- break;
- pkg = pkg->next;
- }
-
- if (pkg == &mgr.pkg_list) {
- /* Event type is not supported by any packages! */
- status = 489;
- reason = pj_str("Bad Event");
- goto send_response;
- }
-
- /* First check that the Accept specification matches the
- * package's Accept types.
- */
- accept = pjsip_msg_find_hdr(rdata->msg, PJSIP_H_ACCEPT, NULL);
- if (accept) {
- unsigned i;
- pj_str_t *content_type = NULL;
-
- for (i=0; i<accept->count && !content_type; ++i) {
- int j;
- for (j=0; j<pkg->accept_cnt; ++j) {
- if (pj_stricmp(&accept->values[i], &pkg->accept[j])==0) {
- content_type = &pkg->accept[j];
- break;
- }
- }
- }
-
- if (!content_type) {
- status = PJSIP_SC_NOT_ACCEPTABLE_HERE;
- goto send_response;
- }
- }
-
- /* Check whether the package wants to accept the subscription. */
- pj_assert(pkg->cb.on_query_subscribe != NULL);
- (*pkg->cb.on_query_subscribe)(rdata, &status);
- if (!PJSIP_IS_STATUS_IN_CLASS(status,200))
- goto send_response;
-
- /* Create new subscription record. */
- pool = pjsip_endpt_create_pool(tsx->endpt, "esub",
- SUB_POOL_SIZE, SUB_POOL_INC);
- if (!pool) {
- status = 500;
- goto send_response;
- }
- sub = pj_pool_calloc(pool, 1, sizeof(*sub));
- sub->pool = pool;
- sub->mutex = pj_mutex_create(pool, "esub", PJ_MUTEX_RECURSE);
- if (!sub->mutex) {
- status = 500;
- goto send_response;
- }
-
- PJ_LOG(4,(THIS_FILE, "event_sub%p: notifier is created.", sub));
-
- /* Start locking mutex. */
- pj_mutex_lock(sub->mutex);
-
- /* Init UAS subscription */
- sub->endpt = tsx->endpt;
- sub->role = PJSIP_ROLE_UAS;
- sub->state = PJSIP_EVENT_SUB_STATE_PENDING;
- sub->state_str = state[sub->state];
- pj_list_init(&sub->auth_sess);
- pj_list_init(&sub->route_set);
- sub->from = pjsip_hdr_clone(pool, rdata->to);
- pjsip_fromto_set_from(sub->from);
- if (sub->from->tag.slen == 0) {
- pj_create_unique_string(pool, &sub->from->tag);
- rdata->to->tag = sub->from->tag;
- }
- sub->to = pjsip_hdr_clone(pool, rdata->from);
- pjsip_fromto_set_to(sub->to);
- sub->contact = pjsip_contact_hdr_create(pool);
- sub->contact->uri = sub->from->uri;
- sub->call_id = pjsip_cid_hdr_create(pool);
- pj_strdup(pool, &sub->call_id->id, &rdata->call_id);
- sub->cseq = pj_rand() % 0xFFFF;
-
- expires = pjsip_msg_find_hdr( rdata->msg, PJSIP_H_EXPIRES, NULL);
- if (expires) {
- sub->default_interval = expires->ivalue;
- if (sub->default_interval > 0 &&
- sub->default_interval < SECONDS_BEFORE_EXPIRY)
- {
- status = 423; /* Interval too short. */
- goto send_response;
- }
- } else {
- sub->default_interval = 600;
- }
-
- /* Clone Event header. */
- sub->event = pjsip_hdr_clone(pool, evhdr);
-
- /* Register to hash table. */
- create_subscriber_key(&sub->key, pool, PJSIP_ROLE_UAS, &sub->call_id->id,
- &sub->from->tag);
- pj_mutex_lock(mgr.mutex);
- pj_hash_set(pool, mgr.ht, sub->key.ptr, sub->key.slen, sub);
- pj_mutex_unlock(mgr.mutex);
-
- /* Set timer where subscription will expire only when expires<>0.
- * Subscriber may send new subscription with expires==0.
- */
- if (sub->default_interval != 0) {
- sub_schedule_uas_expire( sub, sub->default_interval-SECONDS_BEFORE_EXPIRY);
- }
-
- /* Notify application. */
- if (pkg->cb.on_subscribe) {
- pjsip_event_sub_cb *cb = NULL;
- sub->pending_tsx++;
- (*pkg->cb.on_subscribe)(sub, rdata, &cb, &sub->default_interval);
- sub->pending_tsx--;
- if (cb == NULL)
- pj_memset(&sub->cb, 0, sizeof(*cb));
- else
- pj_memcpy(&sub->cb, cb, sizeof(*cb));
- }
-
-
-send_response:
- PJ_LOG(4,(THIS_FILE, "event_sub%p (%s)(UAS): status=%d",
- sub, state[sub->state].ptr, status));
-
- tdata = pjsip_endpt_create_response( tsx->endpt, rdata, status);
- if (tdata) {
- if (reason.slen) {
- /* Customize reason text. */
- tdata->msg->line.status.reason = reason;
- }
- if (PJSIP_IS_STATUS_IN_CLASS(status,200)) {
- /* Add Expires header. */
- pjsip_expires_hdr *hdr;
-
- hdr = pjsip_expires_hdr_create(tdata->pool);
- hdr->ivalue = sub->default_interval;
- pjsip_msg_add_hdr( tdata->msg, (pjsip_hdr*)hdr );
- }
- if (status == 423) {
- /* Add Min-Expires header. */
- pjsip_min_expires_hdr *hdr;
-
- hdr = pjsip_min_expires_hdr_create(tdata->pool);
- hdr->ivalue = SECONDS_BEFORE_EXPIRY;
- pjsip_msg_add_hdr( tdata->msg, (pjsip_hdr*)hdr);
- }
- if (status == 489 ||
- status==PJSIP_SC_NOT_ACCEPTABLE_HERE ||
- PJSIP_IS_STATUS_IN_CLASS(status,200))
- {
- /* Add Allow-Events header. */
- pjsip_hdr *hdr;
- hdr = pjsip_hdr_shallow_clone(tdata->pool, mgr.allow_events);
- pjsip_msg_add_hdr(tdata->msg, hdr);
-
- /* Should add Accept header?. */
- }
-
- pjsip_tsx_on_tx_msg(tsx, tdata);
- }
-
- /* If received new subscription with expires=0, terminate. */
- if (sub && sub->default_interval == 0) {
- pj_assert(sub->state == PJSIP_EVENT_SUB_STATE_TERMINATED);
- if (sub->cb.on_sub_terminated) {
- pj_str_t reason = { "timeout", 7 };
- (*sub->cb.on_sub_terminated)(sub, &reason);
- }
- }
-
- if (!PJSIP_IS_STATUS_IN_CLASS(status,200) || (sub && sub->delete_flag)) {
- if (sub && sub->mutex) {
- pjsip_event_sub_destroy(sub);
- } else if (sub) {
- pjsip_endpt_destroy_pool(tsx->endpt, sub->pool);
- }
- } else {
- pj_assert(status >= 200);
- pj_mutex_unlock(sub->mutex);
- }
-}
-
-/* This is the main callback when SUBSCRIBE request is received. */
-static void on_subscribe_request(pjsip_transaction *tsx, pjsip_rx_data *rdata)
-{
- pjsip_event_sub *sub = find_sub(rdata);
-
- if (sub)
- on_received_sub_refresh(sub, tsx, rdata);
- else
- on_new_subscription(tsx, rdata);
-}
-
-
-/* This callback is called when response to SUBSCRIBE is received. */
-static void on_subscribe_response(void *token, pjsip_event *event)
-{
- pjsip_event_sub *sub = token;
- pjsip_transaction *tsx = event->obj.tsx;
- int new_state, old_state = sub->state;
-
- pj_assert(tsx->status_code >= 200);
- if (tsx->status_code < 200)
- return;
-
- pj_assert(sub->role == PJSIP_ROLE_UAC);
-
- /* Lock mutex. */
- pj_mutex_lock(sub->mutex);
-
- /* If request failed with 401/407 error, silently retry the request. */
- if (tsx->status_code==401 || tsx->status_code==407) {
- pjsip_tx_data *tdata;
- tdata = pjsip_auth_reinit_req(sub->endpt,
- sub->pool, &sub->auth_sess,
- sub->cred_cnt, sub->cred_info,
- tsx->last_tx, event->src.rdata );
- if (tdata) {
- int status;
- pjsip_cseq_hdr *cseq;
- cseq = pjsip_msg_find_hdr(tdata->msg, PJSIP_H_CSEQ, NULL);
- cseq->cseq = sub->cseq++;
- status = pjsip_endpt_send_request( sub->endpt, tdata,
- -1, sub,
- &on_subscribe_response);
- if (status == 0) {
- pj_mutex_unlock(sub->mutex);
- return;
- }
- }
- }
-
- if (PJSIP_IS_STATUS_IN_CLASS(tsx->status_code,200)) {
- /* Update To tag. */
- if (sub->to->tag.slen == 0)
- pj_strdup(sub->pool, &sub->to->tag, &event->src.rdata->to_tag);
-
- new_state = sub->state;
-
- } else if (tsx->status_code == 481) {
- new_state = PJSIP_EVENT_SUB_STATE_TERMINATED;
-
- } else if (tsx->status_code >= 300) {
- /* RFC 3265 Section 3.1.4.2:
- * If a SUBSCRIBE request to refresh a subscription fails
- * with a non-481 response, the original subscription is still
- * considered valid for the duration of original exires.
- *
- * Note:
- * Since we normally send SUBSCRIBE for refreshing the subscription,
- * it means the subscription already expired anyway. So we terminate
- * the subscription now.
- */
- if (sub->state != PJSIP_EVENT_SUB_STATE_ACTIVE) {
- new_state = PJSIP_EVENT_SUB_STATE_TERMINATED;
- } else {
- /* Use this to be compliant with Section 3.1.4.2
- new_state = sub->state;
- */
- new_state = PJSIP_EVENT_SUB_STATE_TERMINATED;
- }
- } else {
- pj_assert(0);
- new_state = sub->state;
- }
-
- if (new_state != sub->state && sub->state != PJSIP_EVENT_SUB_STATE_TERMINATED) {
- sub_set_state(sub, new_state);
- }
-
- if (sub->state == PJSIP_EVENT_SUB_STATE_ACTIVE ||
- sub->state == PJSIP_EVENT_SUB_STATE_PENDING)
- {
- /*
- * Register timer for next subscription refresh, but only when
- * we're not unsubscribing. Also update default_interval and Expires
- * header.
- */
- if (sub->default_interval > 0 && !sub->delete_flag) {
- pjsip_expires_hdr *exp = NULL;
-
- /* Could be transaction timeout. */
- if (event->src_type == PJSIP_EVENT_RX_MSG) {
- exp = pjsip_msg_find_hdr(event->src.rdata->msg,
- PJSIP_H_EXPIRES, NULL);
- }
-
- if (exp) {
- int delay = exp->ivalue;
- if (delay > 0) {
- pj_time_val new_expiry;
- pj_gettimeofday(&new_expiry);
- new_expiry.sec += delay;
- if (sub->timer.id==0 ||
- new_expiry.sec < sub->expiry_time.sec-SECONDS_BEFORE_EXPIRY/2)
- {
- //if (delay > 0 && delay < sub->default_interval) {
- sub->default_interval = delay;
- sub->uac_expires->ivalue = delay;
- update_next_refresh(sub, delay);
- }
- }
- }
- }
- }
-
- /* Call callback. */
- if (!sub->delete_flag) {
- if (sub->cb.on_received_sub_response) {
- (*sub->cb.on_received_sub_response)(sub, event);
- }
- }
-
- /* Notify application if we're terminated. */
- if (new_state!=old_state && new_state==PJSIP_EVENT_SUB_STATE_TERMINATED) {
- if (sub->cb.on_sub_terminated) {
- pj_str_t reason;
- if (event->src_type == PJSIP_EVENT_RX_MSG)
- reason = event->src.rdata->msg->line.status.reason;
- else
- reason = *pjsip_get_status_text(tsx->status_code);
-
- (*sub->cb.on_sub_terminated)(sub, &reason);
- }
- }
-
- /* Decrement pending tsx count. */
- --sub->pending_tsx;
- pj_assert(sub->pending_tsx >= 0);
-
- if (sub->delete_flag && sub->pending_tsx <= 0) {
- pjsip_event_sub_destroy(sub);
- } else {
- pj_mutex_unlock(sub->mutex);
- }
-
- /* DO NOT ACCESS sub FROM NOW ON! IT MIGHT HAVE BEEN DELETED */
-}
-
-/*
- * This callback called when we receive incoming NOTIFY request.
- */
-static void on_notify_request(pjsip_transaction *tsx, pjsip_rx_data *rdata)
-{
- pjsip_event_sub *sub;
- pjsip_tx_data *tdata;
- int status = 200;
- int old_state;
- pj_str_t reason = { NULL, 0 };
- pj_str_t reason_phrase = { NULL, 0 };
- int new_state = PJSIP_EVENT_SUB_STATE_NULL;
-
- /* Find subscription based on Call-ID and From tag.
- * This will also automatically lock the subscription, if it's found.
- */
- sub = find_sub(rdata);
- if (!sub) {
- /* RFC 3265: Section 3.2 Description of NOTIFY Behavior:
- * Answer with 481 Subscription does not exist.
- */
- PJ_LOG(4,(THIS_FILE, "Unable to find subscription for incoming NOTIFY!"));
- status = 481;
- reason_phrase = pj_str("Subscription does not exist");
-
- } else {
- pj_assert(sub->role == PJSIP_ROLE_UAC);
- PJ_LOG(4,(THIS_FILE, "event_sub%p (%s): received NOTIFY",
- sub, state[sub->state].ptr));
-
- }
-
- new_state = old_state = sub->state;
-
- /* RFC 3265: Section 3.2.1
- * Check that the Event header match the subscription.
- */
- if (status == 200) {
- pjsip_event_hdr *hdr;
- pj_str_t hname = { "Event", 5 };
-
- hdr = pjsip_msg_find_hdr_by_name(rdata->msg, &hname, NULL);
- if (!hdr) {
- status = PJSIP_SC_BAD_REQUEST;
- reason_phrase = pj_str("No Event header found");
- } else if (pj_stricmp(&hdr->event_type, &sub->event->event_type) != 0 ||
- pj_stricmp(&hdr->id_param, &sub->event->id_param) != 0)
- {
- status = 481;
- reason_phrase = pj_str("Subscription does not exist");
- }
- }
-
- /* Update subscription state and timer. */
- if (status == 200) {
- pjsip_sub_state_hdr *hdr;
- const pj_str_t hname = { "Subscription-State", 18 };
- const pj_str_t state_active = { "active", 6 },
- state_pending = { "pending", 7},
- state_terminated = { "terminated", 10 };
-
- hdr = pjsip_msg_find_hdr_by_name( rdata->msg, &hname, NULL);
- if (!hdr) {
- status = PJSIP_SC_BAD_REQUEST;
- reason_phrase = pj_str("No Subscription-State header found");
- goto process;
- }
-
- /*
- * Update subscription state.
- */
- if (pj_stricmp(&hdr->sub_state, &state_active) == 0) {
- if (sub->state != PJSIP_EVENT_SUB_STATE_TERMINATED)
- new_state = PJSIP_EVENT_SUB_STATE_ACTIVE;
- } else if (pj_stricmp(&hdr->sub_state, &state_pending) == 0) {
- if (sub->state != PJSIP_EVENT_SUB_STATE_TERMINATED)
- new_state = PJSIP_EVENT_SUB_STATE_PENDING;
- } else if (pj_stricmp(&hdr->sub_state, &state_terminated) == 0) {
- new_state = PJSIP_EVENT_SUB_STATE_TERMINATED;
- } else {
- new_state = PJSIP_EVENT_SUB_STATE_UNKNOWN;
- }
-
- reason = hdr->reason_param;
-
- if (new_state != sub->state && new_state != PJSIP_EVENT_SUB_STATE_NULL &&
- sub->state != PJSIP_EVENT_SUB_STATE_TERMINATED)
- {
- sub_set_state(sub, new_state);
- if (new_state == PJSIP_EVENT_SUB_STATE_UNKNOWN) {
- pj_strdup_with_null(sub->pool, &sub->state_str, &hdr->sub_state);
- } else {
- sub->state_str = state[new_state];
- }
- }
-
- /*
- * Update timeout timer in required, just in case notifier changed the
- * expiration to shorter time.
- * Section 3.2.2: the expires param can only shorten the interval.
- */
- if ((sub->state==PJSIP_EVENT_SUB_STATE_ACTIVE ||
- sub->state==PJSIP_EVENT_SUB_STATE_PENDING) && hdr->expires_param > 0)
- {
- pj_time_val now, new_expiry;
-
- pj_gettimeofday(&now);
- new_expiry.sec = now.sec + hdr->expires_param;
- if (sub->timer.id==0 ||
- new_expiry.sec < sub->expiry_time.sec-SECONDS_BEFORE_EXPIRY/2)
- {
- update_next_refresh(sub, hdr->expires_param);
- }
- }
- }
-
-process:
- /* Note: here we sub MAY BE NULL! */
-
- /* Send response to NOTIFY */
- tdata = pjsip_endpt_create_response( tsx->endpt, rdata, status );
- if (tdata) {
- if (reason_phrase.slen)
- tdata->msg->line.status.reason = reason_phrase;
-
- if (PJSIP_IS_STATUS_IN_CLASS(status,200)) {
- pjsip_hdr *hdr;
- hdr = pjsip_hdr_shallow_clone(tdata->pool, mgr.allow_events);
- pjsip_msg_add_hdr( tdata->msg, hdr);
- }
-
- pjsip_tsx_on_tx_msg(tsx, tdata);
- }
-
- /* Call NOTIFY callback, if any. */
- if (sub && PJSIP_IS_STATUS_IN_CLASS(status,200) && sub->cb.on_received_notify) {
- sub->pending_tsx++;
- (*sub->cb.on_received_notify)(sub, rdata);
- sub->pending_tsx--;
- }
-
- /* Check if subscription is terminated and call callback. */
- if (sub && new_state!=old_state && new_state==PJSIP_EVENT_SUB_STATE_TERMINATED) {
- if (sub->cb.on_sub_terminated) {
- sub->pending_tsx++;
- (*sub->cb.on_sub_terminated)(sub, &reason);
- sub->pending_tsx--;
- }
- }
-
- /* Check if application has requested deletion. */
- if (sub && sub->delete_flag && sub->pending_tsx <= 0) {
- pjsip_event_sub_destroy(sub);
- } else if (sub) {
- pj_mutex_unlock(sub->mutex);
- }
-}
-
-/* This callback is called when we received NOTIFY response. */
-static void on_notify_response(void *token, pjsip_event *event)
-{
- pjsip_event_sub *sub = token;
- pjsip_event_sub_state old_state = sub->state;
- pjsip_transaction *tsx = event->obj.tsx;
-
- /* Lock the subscription. */
- pj_mutex_lock(sub->mutex);
-
- pj_assert(sub->role == PJSIP_ROLE_UAS);
-
- /* If request failed with authorization failure, silently retry. */
- if (tsx->status_code==401 || tsx->status_code==407) {
- pjsip_tx_data *tdata;
- tdata = pjsip_auth_reinit_req(sub->endpt,
- sub->pool, &sub->auth_sess,
- sub->cred_cnt, sub->cred_info,
- tsx->last_tx, event->src.rdata );
- if (tdata) {
- int status;
- pjsip_cseq_hdr *cseq;
- cseq = pjsip_msg_find_hdr(tdata->msg, PJSIP_H_CSEQ, NULL);
- cseq->cseq = sub->cseq++;
- status = pjsip_endpt_send_request( sub->endpt, tdata,
- -1, sub,
- &on_notify_response);
- if (status == 0) {
- pj_mutex_unlock(sub->mutex);
- return;
- }
- }
- }
-
- /* Notify application. */
- if (sub->cb.on_received_notify_response)
- (*sub->cb.on_received_notify_response)(sub, event);
-
- /* Check for response 481. */
- if (event->obj.tsx->status_code == 481) {
- /* Remote says that the subscription does not exist!
- * Terminate subscription!
- */
- sub_set_state(sub, PJSIP_EVENT_SUB_STATE_TERMINATED);
- if (sub->timer.id) {
- pjsip_endpt_cancel_timer(sub->endpt, &sub->timer);
- sub->timer.id = 0;
- }
-
- PJ_LOG(4, (THIS_FILE,
- "event_sub%p (%s): got 481 response to NOTIFY. Terminating...",
- sub, state[sub->state].ptr));
-
- /* Notify app. */
- if (sub->state!=old_state && sub->cb.on_sub_terminated)
- (*sub->cb.on_sub_terminated)(sub, &event->src.rdata->msg->line.status.reason);
- }
-
- /* Decrement pending transaction count. */
- --sub->pending_tsx;
- pj_assert(sub->pending_tsx >= 0);
-
- /* Check that the subscription is marked for deletion. */
- if (sub->delete_flag && sub->pending_tsx <= 0) {
- pjsip_event_sub_destroy(sub);
- } else {
- pj_mutex_unlock(sub->mutex);
- }
-
- /* DO NOT ACCESS sub, IT MIGHT HAVE BEEN DESTROYED! */
-}
-
-
-/* This is the transaction handler for incoming SUBSCRIBE and NOTIFY
- * requests.
- */
-static void tsx_handler( struct pjsip_module *mod, pjsip_event *event )
-{
- pjsip_msg *msg;
- pjsip_rx_data *rdata;
-
- /* Only want incoming message events. */
- if (event->src_type != PJSIP_EVENT_RX_MSG)
- return;
-
- rdata = event->src.rdata;
- msg = rdata->msg;
-
- /* Only want to process request messages. */
- if (msg->type != PJSIP_REQUEST_MSG)
- return;
-
- /* Only want the first notification. */
- if (event->obj.tsx && event->obj.tsx->status_code >= 100)
- return;
-
- if (pjsip_method_cmp(&msg->line.req.method, &SUBSCRIBE)==0) {
- /* Process incoming SUBSCRIBE request. */
- on_subscribe_request( event->obj.tsx, rdata );
- } else if (pjsip_method_cmp(&msg->line.req.method, &NOTIFY)==0) {
- /* Process incoming NOTIFY request. */
- on_notify_request( event->obj.tsx, rdata );
- }
-}
-
+/* $Id$ */
+/*
+ * Copyright (C) 2003-2006 Benny Prijono <benny@prijono.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#include <pjsip_simple/event_notify.h>
+#include <pjsip/sip_msg.h>
+#include <pjsip/sip_util.h>
+#include <pjsip/sip_endpoint.h>
+#include <pjsip/sip_module.h>
+#include <pjsip/sip_transaction.h>
+#include <pjsip/sip_event.h>
+#include <pj/pool.h>
+#include <pj/timer.h>
+#include <pj/string.h>
+#include <pj/hash.h>
+#include <pj/os.h>
+#include <pj/except.h>
+#include <pj/log.h>
+#include <pj/guid.h>
+
+#define THIS_FILE "event_sub"
+
+/* String names for state.
+ * The names here should be compliant with sub_state names in RFC3265.
+ */
+static const pj_str_t state[] = {
+ { "null", 4 },
+ { "active", 6 },
+ { "pending", 7 },
+ { "terminated", 10 },
+ { "unknown", 7 }
+};
+
+/* Timer IDs */
+#define TIMER_ID_REFRESH 1
+#define TIMER_ID_UAS_EXPIRY 2
+
+/* Static configuration. */
+#define SECONDS_BEFORE_EXPIRY 10
+#define MGR_POOL_SIZE 512
+#define MGR_POOL_INC 0
+#define SUB_POOL_SIZE 2048
+#define SUB_POOL_INC 0
+#define HASH_TABLE_SIZE 32
+
+/* Static vars. */
+static int mod_id;
+static const pjsip_method SUBSCRIBE = { PJSIP_OTHER_METHOD, {"SUBSCRIBE", 9}};
+static const pjsip_method NOTIFY = { PJSIP_OTHER_METHOD, { "NOTIFY", 6}};
+
+typedef struct package
+{
+ PJ_DECL_LIST_MEMBER(struct package)
+ pj_str_t event;
+ int accept_cnt;
+ pj_str_t *accept;
+ pjsip_event_sub_pkg_cb cb;
+} package;
+
+/* Event subscription manager singleton instance. */
+static struct pjsip_event_sub_mgr
+{
+ pj_pool_t *pool;
+ pj_hash_table_t *ht;
+ pjsip_endpoint *endpt;
+ pj_mutex_t *mutex;
+ pjsip_allow_events_hdr *allow_events;
+ package pkg_list;
+} mgr;
+
+/* Fordward declarations for static functions. */
+static pj_status_t mod_init(pjsip_endpoint *, pjsip_module *, pj_uint32_t);
+static pj_status_t mod_deinit(pjsip_module*);
+static void tsx_handler(pjsip_module*, pjsip_event*);
+static pjsip_event_sub *find_sub(pjsip_rx_data *);
+static void on_subscribe_request(pjsip_transaction*, pjsip_rx_data*);
+static void on_subscribe_response(void *, pjsip_event*);
+static void on_notify_request(pjsip_transaction *, pjsip_rx_data*);
+static void on_notify_response(void *, pjsip_event *);
+static void refresh_timer_cb(pj_timer_heap_t*, pj_timer_entry*);
+static void uas_expire_timer_cb(pj_timer_heap_t*, pj_timer_entry*);
+static pj_status_t send_sub_refresh( pjsip_event_sub *sub );
+
+/* Module descriptor. */
+static pjsip_module event_sub_module =
+{
+ {"EventSub", 8}, /* Name. */
+ 0, /* Flag */
+ 128, /* Priority */
+ &mgr, /* User data. */
+ 2, /* Number of methods supported . */
+ { &SUBSCRIBE, &NOTIFY }, /* Array of methods */
+ &mod_init, /* init_module() */
+ NULL, /* start_module() */
+ &mod_deinit, /* deinit_module() */
+ &tsx_handler, /* tsx_handler() */
+};
+
+/*
+ * Module initialization.
+ * This will be called by endpoint when it initializes all modules.
+ */
+static pj_status_t mod_init( pjsip_endpoint *endpt,
+ struct pjsip_module *mod, pj_uint32_t id )
+{
+ pj_pool_t *pool;
+
+ pool = pjsip_endpt_create_pool(endpt, "esubmgr", MGR_POOL_SIZE, MGR_POOL_INC);
+ if (!pool)
+ return -1;
+
+ /* Manager initialization: create hash table and mutex. */
+ mgr.pool = pool;
+ mgr.endpt = endpt;
+ mgr.ht = pj_hash_create(pool, HASH_TABLE_SIZE);
+ if (!mgr.ht)
+ return -1;
+
+ mgr.mutex = pj_mutex_create(pool, "esubmgr", PJ_MUTEX_SIMPLE);
+ if (!mgr.mutex)
+ return -1;
+
+ /* Attach manager to module. */
+ mod->mod_data = &mgr;
+
+ /* Init package list. */
+ pj_list_init(&mgr.pkg_list);
+
+ /* Init Allow-Events header. */
+ mgr.allow_events = pjsip_allow_events_hdr_create(mgr.pool);
+
+ /* Save the module ID. */
+ mod_id = id;
+
+ pjsip_event_notify_init_parser();
+ return 0;
+}
+
+/*
+ * Module deinitialization.
+ * Called by endpoint.
+ */
+static pj_status_t mod_deinit( struct pjsip_module *mod )
+{
+ pj_mutex_lock(mgr.mutex);
+ pj_mutex_destroy(mgr.mutex);
+ pjsip_endpt_destroy_pool(mgr.endpt, mgr.pool);
+ return 0;
+}
+
+/*
+ * This public function is called by application to register callback.
+ * In exchange, the instance of the module is returned.
+ */
+PJ_DEF(pjsip_module*) pjsip_event_sub_get_module(void)
+{
+ return &event_sub_module;
+}
+
+/*
+ * Register event package.
+ */
+PJ_DEF(pj_status_t) pjsip_event_sub_register_pkg( const pj_str_t *event,
+ int accept_cnt,
+ const pj_str_t accept[],
+ const pjsip_event_sub_pkg_cb *cb )
+{
+ package *pkg;
+ int i;
+
+ pj_mutex_lock(mgr.mutex);
+
+ /* Create and register new package. */
+ pkg = pj_pool_alloc(mgr.pool, sizeof(*pkg));
+ pj_strdup(mgr.pool, &pkg->event, event);
+ pj_list_insert_before(&mgr.pkg_list, pkg);
+
+ /* Save Accept specification. */
+ pkg->accept_cnt = accept_cnt;
+ pkg->accept = pj_pool_alloc(mgr.pool, accept_cnt*sizeof(pj_str_t));
+ for (i=0; i<accept_cnt; ++i) {
+ pj_strdup(mgr.pool, &pkg->accept[i], &accept[i]);
+ }
+
+ /* Copy callback. */
+ pj_memcpy(&pkg->cb, cb, sizeof(*cb));
+
+ /* Update Allow-Events header. */
+ pj_assert(mgr.allow_events->event_cnt < PJSIP_MAX_ALLOW_EVENTS);
+ mgr.allow_events->events[mgr.allow_events->event_cnt++] = pkg->event;
+
+ pj_mutex_unlock(mgr.mutex);
+ return 0;
+}
+
+/*
+ * Create subscription key (for hash table).
+ */
+static void create_subscriber_key( pj_str_t *key, pj_pool_t *pool,
+ pjsip_role_e role,
+ const pj_str_t *call_id, const pj_str_t *from_tag)
+{
+ char *p;
+
+ p = key->ptr = pj_pool_alloc(pool, call_id->slen + from_tag->slen + 3);
+ *p++ = (role == PJSIP_ROLE_UAS ? 'S' : 'C');
+ *p++ = '$';
+ pj_memcpy(p, call_id->ptr, call_id->slen);
+ p += call_id->slen;
+ *p++ = '$';
+ pj_memcpy(p, from_tag->ptr, from_tag->slen);
+ p += from_tag->slen;
+
+ key->slen = p - key->ptr;
+}
+
+
+/*
+ * Create UAC subscription.
+ */
+PJ_DEF(pjsip_event_sub*) pjsip_event_sub_create( pjsip_endpoint *endpt,
+ const pj_str_t *from,
+ const pj_str_t *to,
+ const pj_str_t *event,
+ int expires,
+ int accept_cnt,
+ const pj_str_t accept[],
+ void *user_data,
+ const pjsip_event_sub_cb *cb)
+{
+ pjsip_tx_data *tdata;
+ pj_pool_t *pool;
+ const pjsip_hdr *hdr;
+ pjsip_event_sub *sub;
+ PJ_USE_EXCEPTION;
+
+ PJ_LOG(5,(THIS_FILE, "Creating event subscription %.*s to %.*s",
+ event->slen, event->ptr, to->slen, to->ptr));
+
+ /* Create pool for the event subscription. */
+ pool = pjsip_endpt_create_pool(endpt, "esub", SUB_POOL_SIZE, SUB_POOL_INC);
+ if (!pool) {
+ return NULL;
+ }
+
+ /* Init subscription. */
+ sub = pj_pool_calloc(pool, 1, sizeof(*sub));
+ sub->pool = pool;
+ sub->endpt = endpt;
+ sub->role = PJSIP_ROLE_UAC;
+ sub->state = PJSIP_EVENT_SUB_STATE_PENDING;
+ sub->state_str = state[sub->state];
+ sub->user_data = user_data;
+ sub->timer.id = 0;
+ sub->default_interval = expires;
+ pj_memcpy(&sub->cb, cb, sizeof(*cb));
+ pj_list_init(&sub->auth_sess);
+ pj_list_init(&sub->route_set);
+ sub->mutex = pj_mutex_create(pool, "esub", PJ_MUTEX_RECURSE);
+ if (!sub->mutex) {
+ pjsip_endpt_destroy_pool(endpt, pool);
+ return NULL;
+ }
+
+ /* The easiest way to parse the parameters is to create a dummy request! */
+ tdata = pjsip_endpt_create_request( endpt, &SUBSCRIBE, to, from, to, from,
+ NULL, -1, NULL);
+ if (!tdata) {
+ pj_mutex_destroy(sub->mutex);
+ pjsip_endpt_destroy_pool(endpt, pool);
+ return NULL;
+ }
+
+ /*
+ * Duplicate headers in the request to our structure.
+ */
+ PJ_TRY {
+ int i;
+
+ /* From */
+ hdr = pjsip_msg_find_hdr(tdata->msg, PJSIP_H_FROM, NULL);
+ pj_assert(hdr != NULL);
+ sub->from = pjsip_hdr_clone(pool, hdr);
+
+ /* To */
+ hdr = pjsip_msg_find_hdr(tdata->msg, PJSIP_H_TO, NULL);
+ pj_assert(hdr != NULL);
+ sub->to = pjsip_hdr_clone(pool, hdr);
+
+ /* Contact. */
+ sub->contact = pjsip_contact_hdr_create(pool);
+ sub->contact->uri = sub->from->uri;
+
+ /* Call-ID */
+ hdr = pjsip_msg_find_hdr(tdata->msg, PJSIP_H_CALL_ID, NULL);
+ pj_assert(hdr != NULL);
+ sub->call_id = pjsip_hdr_clone(pool, hdr);
+
+ /* CSeq */
+ sub->cseq = pj_rand() % 0xFFFF;
+
+ /* Event. */
+ sub->event = pjsip_event_hdr_create(sub->pool);
+ pj_strdup(pool, &sub->event->event_type, event);
+
+ /* Expires. */
+ sub->uac_expires = pjsip_expires_hdr_create(pool);
+ sub->uac_expires->ivalue = expires;
+
+ /* Accept. */
+ sub->local_accept = pjsip_accept_hdr_create(pool);
+ for (i=0; i<accept_cnt && i < PJSIP_MAX_ACCEPT_COUNT; ++i) {
+ sub->local_accept->count++;
+ pj_strdup(sub->pool, &sub->local_accept->values[i], &accept[i]);
+ }
+
+ /* Register to hash table. */
+ create_subscriber_key( &sub->key, pool, PJSIP_ROLE_UAC,
+ &sub->call_id->id, &sub->from->tag);
+ pj_mutex_lock( mgr.mutex );
+ pj_hash_set( pool, mgr.ht, sub->key.ptr, sub->key.slen, sub);
+ pj_mutex_unlock( mgr.mutex );
+
+ }
+ PJ_DEFAULT {
+ PJ_LOG(4,(THIS_FILE, "event_sub%p (%s): caught exception %d during init",
+ sub, state[sub->state].ptr, PJ_GET_EXCEPTION()));
+
+ pjsip_tx_data_dec_ref(tdata);
+ pj_mutex_destroy(sub->mutex);
+ pjsip_endpt_destroy_pool(endpt, sub->pool);
+ return NULL;
+ }
+ PJ_END;
+
+ /* All set, delete temporary transmit data as we don't need it. */
+ pjsip_tx_data_dec_ref(tdata);
+
+ PJ_LOG(4,(THIS_FILE, "event_sub%p (%s): client created, target=%.*s, event=%.*s",
+ sub, state[sub->state].ptr,
+ to->slen, to->ptr, event->slen, event->ptr));
+
+ return sub;
+}
+
+/*
+ * Set credentials.
+ */
+PJ_DEF(pj_status_t) pjsip_event_sub_set_credentials( pjsip_event_sub *sub,
+ int count,
+ const pjsip_cred_info cred[])
+{
+ pj_mutex_lock(sub->mutex);
+ if (count > 0) {
+ sub->cred_info = pj_pool_alloc(sub->pool, count*sizeof(pjsip_cred_info));
+ pj_memcpy( sub->cred_info, cred, count*sizeof(pjsip_cred_info));
+ }
+ sub->cred_cnt = count;
+ pj_mutex_unlock(sub->mutex);
+ return 0;
+}
+
+/*
+ * Set route-set.
+ */
+PJ_DEF(pj_status_t) pjsip_event_sub_set_route_set( pjsip_event_sub *sub,
+ const pjsip_route_hdr *route_set )
+{
+ const pjsip_route_hdr *hdr;
+
+ pj_mutex_lock(sub->mutex);
+
+ /* Clear existing route set. */
+ pj_list_init(&sub->route_set);
+
+ /* Duplicate route headers. */
+ hdr = route_set->next;
+ while (hdr != route_set) {
+ pjsip_route_hdr *new_hdr = pjsip_hdr_clone(sub->pool, hdr);
+ pj_list_insert_before(&sub->route_set, new_hdr);
+ hdr = hdr->next;
+ }
+
+ pj_mutex_unlock(sub->mutex);
+
+ return 0;
+}
+
+/*
+ * Send subscribe request.
+ */
+PJ_DEF(pj_status_t) pjsip_event_sub_subscribe( pjsip_event_sub *sub )
+{
+ pj_status_t status;
+
+ pj_mutex_lock(sub->mutex);
+ status = send_sub_refresh(sub);
+ pj_mutex_unlock(sub->mutex);
+
+ return status;
+}
+
+/*
+ * Destroy subscription.
+ * If there are pending transactions, then this will just set the flag.
+ */
+PJ_DEF(pj_status_t) pjsip_event_sub_destroy(pjsip_event_sub *sub)
+{
+ pj_assert(sub != NULL);
+ if (sub == NULL)
+ return -1;
+
+ /* Application must terminate the subscription first. */
+ pj_assert(sub->state == PJSIP_EVENT_SUB_STATE_NULL ||
+ sub->state == PJSIP_EVENT_SUB_STATE_TERMINATED);
+
+ PJ_LOG(4,(THIS_FILE, "event_sub%p (%s): about to be destroyed",
+ sub, state[sub->state].ptr));
+
+ pj_mutex_lock(mgr.mutex);
+ pj_mutex_lock(sub->mutex);
+
+ /* Set delete flag. */
+ sub->delete_flag = 1;
+
+ /* Unregister timer, if any. */
+ if (sub->timer.id != 0) {
+ pjsip_endpt_cancel_timer(sub->endpt, &sub->timer);
+ sub->timer.id = 0;
+ }
+
+ if (sub->pending_tsx > 0) {
+ pj_mutex_unlock(sub->mutex);
+ pj_mutex_unlock(mgr.mutex);
+ PJ_LOG(4,(THIS_FILE, "event_sub%p (%s): has %d pending, will destroy later",
+ sub, state[sub->state].ptr,
+ sub->pending_tsx));
+ return 1;
+ }
+
+ /* Unregister from hash table. */
+ pj_hash_set(sub->pool, mgr.ht, sub->key.ptr, sub->key.slen, NULL);
+
+ /* Destroy. */
+ pj_mutex_destroy(sub->mutex);
+ pjsip_endpt_destroy_pool(sub->endpt, sub->pool);
+
+ pj_mutex_unlock(mgr.mutex);
+
+ PJ_LOG(4,(THIS_FILE, "event_sub%p: destroyed", sub));
+ return 0;
+}
+
+/* Change state. */
+static void sub_set_state( pjsip_event_sub *sub, int new_state)
+{
+ PJ_LOG(4,(THIS_FILE, "event_sub%p (%s): changed state to %s",
+ sub, state[sub->state].ptr, state[new_state].ptr));
+ sub->state = new_state;
+ sub->state_str = state[new_state];
+}
+
+/*
+ * Refresh subscription.
+ */
+static pj_status_t send_sub_refresh( pjsip_event_sub *sub )
+{
+ pjsip_tx_data *tdata;
+ pj_status_t status;
+ const pjsip_route_hdr *route;
+
+ pj_assert(sub->role == PJSIP_ROLE_UAC);
+ pj_assert(sub->state != PJSIP_EVENT_SUB_STATE_TERMINATED);
+ if (sub->role != PJSIP_ROLE_UAC ||
+ sub->state == PJSIP_EVENT_SUB_STATE_TERMINATED)
+ {
+ return -1;
+ }
+
+ PJ_LOG(4,(THIS_FILE, "event_sub%p (%s): refreshing subscription",
+ sub, state[sub->state].ptr));
+
+ /* Create request. */
+ tdata = pjsip_endpt_create_request_from_hdr( sub->endpt,
+ &SUBSCRIBE,
+ sub->to->uri,
+ sub->from, sub->to,
+ sub->contact, sub->call_id,
+ sub->cseq++,
+ NULL);
+
+ if (!tdata) {
+ PJ_LOG(4,(THIS_FILE, "event_sub%p (%s): refresh: unable to create tx data!",
+ sub, state[sub->state].ptr));
+ return -1;
+ }
+
+ pjsip_msg_add_hdr( tdata->msg,
+ pjsip_hdr_shallow_clone(tdata->pool, sub->event));
+ pjsip_msg_add_hdr( tdata->msg,
+ pjsip_hdr_shallow_clone(tdata->pool, sub->uac_expires));
+ pjsip_msg_add_hdr( tdata->msg,
+ pjsip_hdr_shallow_clone(tdata->pool, sub->local_accept));
+ pjsip_msg_add_hdr( tdata->msg,
+ pjsip_hdr_shallow_clone(tdata->pool, mgr.allow_events));
+
+ /* Authentication */
+ pjsip_auth_init_req( sub->pool, tdata, &sub->auth_sess,
+ sub->cred_cnt, sub->cred_info);
+
+ /* Route set. */
+ route = sub->route_set.next;
+ while (route != &sub->route_set) {
+ pj_list_insert_before( &tdata->msg->hdr,
+ pjsip_hdr_shallow_clone(tdata->pool, route));
+ route = route->next;
+ }
+
+ /* Send */
+ status = pjsip_endpt_send_request( sub->endpt, tdata, -1, sub,
+ &on_subscribe_response);
+ if (status == 0) {
+ sub->pending_tsx++;
+ } else {
+ PJ_LOG(4,(THIS_FILE, "event_sub%p (%s): FAILED to refresh subscription!",
+ sub, state[sub->state].ptr));
+ }
+
+ return status;
+}
+
+/*
+ * Stop subscription.
+ */
+PJ_DEF(pj_status_t) pjsip_event_sub_unsubscribe( pjsip_event_sub *sub )
+{
+ pjsip_tx_data *tdata;
+ const pjsip_route_hdr *route;
+ pj_status_t status;
+
+ PJ_LOG(4,(THIS_FILE, "event_sub%p (%s): unsubscribing...",
+ sub, state[sub->state].ptr));
+
+ /* Lock subscription. */
+ pj_mutex_lock(sub->mutex);
+
+ pj_assert(sub->role == PJSIP_ROLE_UAC);
+
+ /* Kill refresh timer, if any. */
+ if (sub->timer.id != 0) {
+ sub->timer.id = 0;
+ pjsip_endpt_cancel_timer(sub->endpt, &sub->timer);
+ }
+
+ /* Create request. */
+ tdata = pjsip_endpt_create_request_from_hdr( sub->endpt,
+ &SUBSCRIBE,
+ sub->to->uri,
+ sub->from, sub->to,
+ sub->contact, sub->call_id,
+ sub->cseq++,
+ NULL);
+
+ if (!tdata) {
+ pj_mutex_unlock(sub->mutex);
+ return -1;
+ }
+
+ /* Add headers to request. */
+ pjsip_msg_add_hdr( tdata->msg, pjsip_hdr_shallow_clone(tdata->pool, sub->event));
+ sub->uac_expires->ivalue = 0;
+ pjsip_msg_add_hdr( tdata->msg, pjsip_hdr_shallow_clone(tdata->pool, sub->uac_expires));
+
+ /* Add authentication. */
+ pjsip_auth_init_req( sub->pool, tdata, &sub->auth_sess,
+ sub->cred_cnt, sub->cred_info);
+
+
+ /* Route set. */
+ route = sub->route_set.next;
+ while (route != &sub->route_set) {
+ pj_list_insert_before( &tdata->msg->hdr,
+ pjsip_hdr_shallow_clone(tdata->pool, route));
+ route = route->next;
+ }
+
+ /* Prevent timer from refreshing itself. */
+ sub->default_interval = 0;
+
+ /* Set state. */
+ sub_set_state( sub, PJSIP_EVENT_SUB_STATE_TERMINATED );
+
+ /* Send the request. */
+ status = pjsip_endpt_send_request( sub->endpt, tdata, -1, sub,
+ &on_subscribe_response);
+ if (status == 0) {
+ sub->pending_tsx++;
+ }
+
+ pj_mutex_unlock(sub->mutex);
+
+ if (status != 0) {
+ PJ_LOG(4,(THIS_FILE, "event_sub%p (%s): FAILED to unsubscribe!",
+ sub, state[sub->state].ptr));
+ }
+
+ return status;
+}
+
+/*
+ * Send notify.
+ */
+PJ_DEF(pj_status_t) pjsip_event_sub_notify(pjsip_event_sub *sub,
+ pjsip_event_sub_state new_state,
+ const pj_str_t *reason,
+ pjsip_msg_body *body)
+{
+ pjsip_tx_data *tdata;
+ pjsip_sub_state_hdr *ss_hdr;
+ const pjsip_route_hdr *route;
+ pj_time_val now;
+ pj_status_t status;
+ pjsip_event_sub_state old_state = sub->state;
+
+ pj_gettimeofday(&now);
+
+ pj_assert(sub->role == PJSIP_ROLE_UAS);
+ if (sub->role != PJSIP_ROLE_UAS)
+ return -1;
+
+ PJ_LOG(4,(THIS_FILE, "event_sub%p (%s): sending NOTIFY",
+ sub, state[new_state].ptr));
+
+ /* Lock subscription. */
+ pj_mutex_lock(sub->mutex);
+
+ /* Can not send NOTIFY if current state is NULL. We can accept TERMINATED. */
+ if (sub->state==PJSIP_EVENT_SUB_STATE_NULL) {
+ pj_assert(0);
+ pj_mutex_unlock(sub->mutex);
+ return -1;
+ }
+
+ /* Update state no matter what. */
+ sub_set_state(sub, new_state);
+
+ /* Create transmit data. */
+ tdata = pjsip_endpt_create_request_from_hdr( sub->endpt,
+ &NOTIFY,
+ sub->to->uri,
+ sub->from, sub->to,
+ sub->contact, sub->call_id,
+ sub->cseq++,
+ NULL);
+ if (!tdata) {
+ pj_mutex_unlock(sub->mutex);
+ return -1;
+ }
+
+ /* Add Event header. */
+ pjsip_msg_add_hdr(tdata->msg, pjsip_hdr_shallow_clone(tdata->pool, sub->event));
+
+ /* Add Subscription-State header. */
+ ss_hdr = pjsip_sub_state_hdr_create(tdata->pool);
+ ss_hdr->sub_state = state[new_state];
+ ss_hdr->expires_param = sub->expiry_time.sec - now.sec;
+ if (ss_hdr->expires_param < 0)
+ ss_hdr->expires_param = 0;
+ if (reason)
+ pj_strdup(tdata->pool, &ss_hdr->reason_param, reason);
+ pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr*)ss_hdr);
+
+ /* Add Allow-Events header. */
+ pjsip_msg_add_hdr( tdata->msg,
+ pjsip_hdr_shallow_clone(tdata->pool, mgr.allow_events));
+
+ /* Add authentication */
+ pjsip_auth_init_req( sub->pool, tdata, &sub->auth_sess,
+ sub->cred_cnt, sub->cred_info);
+
+ /* Route set. */
+ route = sub->route_set.next;
+ while (route != &sub->route_set) {
+ pj_list_insert_before( &tdata->msg->hdr,
+ pjsip_hdr_shallow_clone(tdata->pool, route));
+ route = route->next;
+ }
+
+ /* Attach body. */
+ tdata->msg->body = body;
+
+ /* That's it, send! */
+ status = pjsip_endpt_send_request( sub->endpt, tdata, -1, sub, &on_notify_response);
+ if (status == 0)
+ sub->pending_tsx++;
+
+ /* If terminated notify application. */
+ if (new_state!=old_state && new_state==PJSIP_EVENT_SUB_STATE_TERMINATED) {
+ if (sub->cb.on_sub_terminated) {
+ sub->pending_tsx++;
+ (*sub->cb.on_sub_terminated)(sub, reason);
+ sub->pending_tsx--;
+ }
+ }
+
+ /* Unlock subscription. */
+ pj_mutex_unlock(sub->mutex);
+
+ if (status != 0) {
+ PJ_LOG(4,(THIS_FILE, "event_sub%p (%s): failed to send NOTIFY",
+ sub, state[sub->state].ptr));
+ }
+
+ if (sub->delete_flag && sub->pending_tsx <= 0) {
+ pjsip_event_sub_destroy(sub);
+ }
+ return status;
+}
+
+
+/* If this timer callback is called, it means subscriber hasn't refreshed its
+ * subscription on-time. Set the state to terminated. This will also send
+ * NOTIFY with Subscription-State set to terminated.
+ */
+static void uas_expire_timer_cb( pj_timer_heap_t *timer_heap, pj_timer_entry *entry)
+{
+ pjsip_event_sub *sub = entry->user_data;
+ pj_str_t reason = { "timeout", 7 };
+
+ PJ_LOG(4,(THIS_FILE, "event_sub%p (%s): UAS subscription expired!",
+ sub, state[sub->state].ptr));
+
+ pj_mutex_lock(sub->mutex);
+ sub->timer.id = 0;
+
+ if (sub->cb.on_sub_terminated && sub->state!=PJSIP_EVENT_SUB_STATE_TERMINATED) {
+ /* Notify application, but prevent app from destroying the sub. */
+ ++sub->pending_tsx;
+ (*sub->cb.on_sub_terminated)(sub, &reason);
+ --sub->pending_tsx;
+ }
+ //pjsip_event_sub_notify( sub, PJSIP_EVENT_SUB_STATE_TERMINATED,
+ // &reason, NULL);
+ pj_mutex_unlock(sub->mutex);
+
+}
+
+/* Schedule notifier expiration. */
+static void sub_schedule_uas_expire( pjsip_event_sub *sub, int sec_delay)
+{
+ pj_time_val delay = { 0, 0 };
+ pj_parsed_time pt;
+
+ if (sub->timer.id != 0)
+ pjsip_endpt_cancel_timer(sub->endpt, &sub->timer);
+
+ pj_gettimeofday(&sub->expiry_time);
+ sub->expiry_time.sec += sec_delay;
+
+ sub->timer.id = TIMER_ID_UAS_EXPIRY;
+ sub->timer.user_data = sub;
+ sub->timer.cb = &uas_expire_timer_cb;
+ delay.sec = sec_delay;
+ pjsip_endpt_schedule_timer( sub->endpt, &sub->timer, &delay);
+
+ pj_time_decode(&sub->expiry_time, &pt);
+ PJ_LOG(4,(THIS_FILE,
+ "event_sub%p (%s)(UAS): will expire at %02d:%02d:%02d (in %d secs)",
+ sub, state[sub->state].ptr, pt.hour, pt.min, pt.sec, sec_delay));
+}
+
+/* This timer is called for UAC to refresh the subscription. */
+static void refresh_timer_cb( pj_timer_heap_t *timer_heap, pj_timer_entry *entry)
+{
+ pjsip_event_sub *sub = entry->user_data;
+
+ PJ_LOG(4,(THIS_FILE, "event_sub%p (%s): refresh subscription timer",
+ sub, state[sub->state].ptr));
+
+ pj_mutex_lock(sub->mutex);
+ sub->timer.id = 0;
+ send_sub_refresh(sub);
+ pj_mutex_unlock(sub->mutex);
+}
+
+
+/* This will update the UAC's refresh schedule. */
+static void update_next_refresh(pjsip_event_sub *sub, int interval)
+{
+ pj_time_val delay = {0, 0};
+ pj_parsed_time pt;
+
+ if (interval < SECONDS_BEFORE_EXPIRY) {
+ PJ_LOG(4,(THIS_FILE,
+ "event_sub%p (%s): expiration delay too short (%d sec)! updated.",
+ sub, state[sub->state].ptr, interval));
+ interval = SECONDS_BEFORE_EXPIRY;
+ }
+
+ if (sub->timer.id != 0)
+ pjsip_endpt_cancel_timer(sub->endpt, &sub->timer);
+
+ sub->timer.id = TIMER_ID_REFRESH;
+ sub->timer.user_data = sub;
+ sub->timer.cb = &refresh_timer_cb;
+ pj_gettimeofday(&sub->expiry_time);
+ delay.sec = interval - SECONDS_BEFORE_EXPIRY;
+ sub->expiry_time.sec += delay.sec;
+
+ pj_time_decode(&sub->expiry_time, &pt);
+ PJ_LOG(4,(THIS_FILE,
+ "event_sub%p (%s): will send SUBSCRIBE at %02d:%02d:%02d (in %d secs)",
+ sub, state[sub->state].ptr,
+ pt.hour, pt.min, pt.sec,
+ delay.sec));
+
+ pjsip_endpt_schedule_timer( sub->endpt, &sub->timer, &delay );
+}
+
+
+/* Find subscription in the hash table.
+ * If found, lock the subscription before returning to caller.
+ */
+static pjsip_event_sub *find_sub(pjsip_rx_data *rdata)
+{
+ pj_str_t key;
+ pjsip_role_e role;
+ pjsip_event_sub *sub;
+ pjsip_method *method = &rdata->msg->line.req.method;
+ pj_str_t *tag;
+
+ if (rdata->msg->type == PJSIP_REQUEST_MSG) {
+ if (pjsip_method_cmp(method, &SUBSCRIBE)==0) {
+ role = PJSIP_ROLE_UAS;
+ tag = &rdata->to_tag;
+ } else {
+ pj_assert(pjsip_method_cmp(method, &NOTIFY) == 0);
+ role = PJSIP_ROLE_UAC;
+ tag = &rdata->to_tag;
+ }
+ } else {
+ if (pjsip_method_cmp(&rdata->cseq->method, &SUBSCRIBE)==0) {
+ role = PJSIP_ROLE_UAC;
+ tag = &rdata->from_tag;
+ } else {
+ pj_assert(pjsip_method_cmp(method, &NOTIFY) == 0);
+ role = PJSIP_ROLE_UAS;
+ tag = &rdata->from_tag;
+ }
+ }
+ create_subscriber_key( &key, rdata->pool, role, &rdata->call_id, tag);
+
+ pj_mutex_lock(mgr.mutex);
+ sub = pj_hash_get(mgr.ht, key.ptr, key.slen);
+ if (sub)
+ pj_mutex_lock(sub->mutex);
+ pj_mutex_unlock(mgr.mutex);
+
+ return sub;
+}
+
+
+/* This function is called when we receive SUBSCRIBE request message
+ * to refresh existing subscription.
+ */
+static void on_received_sub_refresh( pjsip_event_sub *sub,
+ pjsip_transaction *tsx, pjsip_rx_data *rdata)
+{
+ pjsip_event_hdr *e;
+ pjsip_expires_hdr *expires;
+ pj_str_t hname;
+ int status = 200;
+ pj_str_t reason_phrase = { NULL, 0 };
+ int new_state = sub->state;
+ int old_state = sub->state;
+ int new_interval = 0;
+ pjsip_tx_data *tdata;
+
+ PJ_LOG(4,(THIS_FILE, "event_sub%p (%s): received target refresh",
+ sub, state[sub->state].ptr));
+
+ /* Check that the event matches. */
+ hname = pj_str("Event");
+ e = pjsip_msg_find_hdr_by_name( rdata->msg, &hname, NULL);
+ if (!e) {
+ status = 400;
+ reason_phrase = pj_str("Missing Event header");
+ goto send_response;
+ }
+ if (pj_stricmp(&e->event_type, &sub->event->event_type) != 0 ||
+ pj_stricmp(&e->id_param, &sub->event->id_param) != 0)
+ {
+ status = 481;
+ reason_phrase = pj_str("Subscription does not exist");
+ goto send_response;
+ }
+
+ /* Check server state. */
+ if (sub->state == PJSIP_EVENT_SUB_STATE_TERMINATED) {
+ status = 481;
+ reason_phrase = pj_str("Subscription does not exist");
+ goto send_response;
+ }
+
+ /* Check expires header. */
+ expires = pjsip_msg_find_hdr(rdata->msg, PJSIP_H_EXPIRES, NULL);
+ if (!expires) {
+ /*
+ status = 400;
+ reason_phrase = pj_str("Missing Expires header");
+ goto send_response;
+ */
+ new_interval = sub->default_interval;
+ } else {
+ /* Check that interval is not too short.
+ * Note that expires time may be zero (for unsubscription).
+ */
+ new_interval = expires->ivalue;
+ if (new_interval != 0 && new_interval < SECONDS_BEFORE_EXPIRY) {
+ status = PJSIP_SC_INTERVAL_TOO_BRIEF;
+ goto send_response;
+ }
+ }
+
+ /* Update interval. */
+ sub->default_interval = new_interval;
+ pj_gettimeofday(&sub->expiry_time);
+ sub->expiry_time.sec += new_interval;
+
+ /* Update timer only if this is not unsubscription. */
+ if (new_interval > 0) {
+ sub->default_interval = new_interval;
+ sub_schedule_uas_expire( sub, new_interval );
+
+ /* Call callback. */
+ if (sub->cb.on_received_refresh) {
+ sub->pending_tsx++;
+ (*sub->cb.on_received_refresh)(sub, rdata);
+ sub->pending_tsx--;
+ }
+ }
+
+send_response:
+ tdata = pjsip_endpt_create_response( sub->endpt, rdata, status);
+ if (tdata) {
+ if (reason_phrase.slen)
+ tdata->msg->line.status.reason = reason_phrase;
+
+ /* Add Expires header. */
+ expires = pjsip_expires_hdr_create(tdata->pool);
+ expires->ivalue = sub->default_interval;
+ pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr*)expires);
+
+ if (PJSIP_IS_STATUS_IN_CLASS(status,200)) {
+ pjsip_msg_add_hdr(tdata->msg,
+ pjsip_hdr_shallow_clone(tdata->pool, mgr.allow_events));
+ }
+ /* Send down to transaction. */
+ pjsip_tsx_on_tx_msg(tsx, tdata);
+ }
+
+ if (sub->default_interval==0 || !PJSIP_IS_STATUS_IN_CLASS(status,200)) {
+ /* Notify application if sub is terminated. */
+ new_state = PJSIP_EVENT_SUB_STATE_TERMINATED;
+ sub_set_state(sub, new_state);
+ if (new_state!=old_state && sub->cb.on_sub_terminated) {
+ pj_str_t reason = {"", 0};
+ if (reason_phrase.slen) reason = reason_phrase;
+ else reason = *pjsip_get_status_text(status);
+
+ sub->pending_tsx++;
+ (*sub->cb.on_sub_terminated)(sub, &reason);
+ sub->pending_tsx--;
+ }
+ }
+
+ pj_mutex_unlock(sub->mutex);
+
+ /* Prefer to call log when we're not holding the mutex. */
+ PJ_LOG(4,(THIS_FILE, "event_sub%p (%s): sent refresh response %s, status=%d",
+ sub, state[sub->state].ptr,
+ (tdata ? tdata->obj_name : "null"), status));
+
+ /* Check if application has requested deletion. */
+ if (sub->delete_flag && sub->pending_tsx <= 0) {
+ pjsip_event_sub_destroy(sub);
+ }
+
+}
+
+
+/* This function is called when we receive SUBSCRIBE request message for
+ * a new subscription.
+ */
+static void on_new_subscription( pjsip_transaction *tsx, pjsip_rx_data *rdata )
+{
+ package *pkg;
+ pj_pool_t *pool;
+ pjsip_event_sub *sub = NULL;
+ pj_str_t hname;
+ int status = 200;
+ pj_str_t reason = { NULL, 0 };
+ pjsip_tx_data *tdata;
+ pjsip_expires_hdr *expires;
+ pjsip_accept_hdr *accept;
+ pjsip_event_hdr *evhdr;
+
+ /* Get the Event header. */
+ hname = pj_str("Event");
+ evhdr = pjsip_msg_find_hdr_by_name(rdata->msg, &hname, NULL);
+ if (!evhdr) {
+ status = 400;
+ reason = pj_str("No Event header in request");
+ goto send_response;
+ }
+
+ /* Find corresponding package.
+ * We don't lock the manager's mutex since we assume the package list
+ * won't change once the application is running!
+ */
+ pkg = mgr.pkg_list.next;
+ while (pkg != &mgr.pkg_list) {
+ if (pj_stricmp(&pkg->event, &evhdr->event_type) == 0)
+ break;
+ pkg = pkg->next;
+ }
+
+ if (pkg == &mgr.pkg_list) {
+ /* Event type is not supported by any packages! */
+ status = 489;
+ reason = pj_str("Bad Event");
+ goto send_response;
+ }
+
+ /* First check that the Accept specification matches the
+ * package's Accept types.
+ */
+ accept = pjsip_msg_find_hdr(rdata->msg, PJSIP_H_ACCEPT, NULL);
+ if (accept) {
+ unsigned i;
+ pj_str_t *content_type = NULL;
+
+ for (i=0; i<accept->count && !content_type; ++i) {
+ int j;
+ for (j=0; j<pkg->accept_cnt; ++j) {
+ if (pj_stricmp(&accept->values[i], &pkg->accept[j])==0) {
+ content_type = &pkg->accept[j];
+ break;
+ }
+ }
+ }
+
+ if (!content_type) {
+ status = PJSIP_SC_NOT_ACCEPTABLE_HERE;
+ goto send_response;
+ }
+ }
+
+ /* Check whether the package wants to accept the subscription. */
+ pj_assert(pkg->cb.on_query_subscribe != NULL);
+ (*pkg->cb.on_query_subscribe)(rdata, &status);
+ if (!PJSIP_IS_STATUS_IN_CLASS(status,200))
+ goto send_response;
+
+ /* Create new subscription record. */
+ pool = pjsip_endpt_create_pool(tsx->endpt, "esub",
+ SUB_POOL_SIZE, SUB_POOL_INC);
+ if (!pool) {
+ status = 500;
+ goto send_response;
+ }
+ sub = pj_pool_calloc(pool, 1, sizeof(*sub));
+ sub->pool = pool;
+ sub->mutex = pj_mutex_create(pool, "esub", PJ_MUTEX_RECURSE);
+ if (!sub->mutex) {
+ status = 500;
+ goto send_response;
+ }
+
+ PJ_LOG(4,(THIS_FILE, "event_sub%p: notifier is created.", sub));
+
+ /* Start locking mutex. */
+ pj_mutex_lock(sub->mutex);
+
+ /* Init UAS subscription */
+ sub->endpt = tsx->endpt;
+ sub->role = PJSIP_ROLE_UAS;
+ sub->state = PJSIP_EVENT_SUB_STATE_PENDING;
+ sub->state_str = state[sub->state];
+ pj_list_init(&sub->auth_sess);
+ pj_list_init(&sub->route_set);
+ sub->from = pjsip_hdr_clone(pool, rdata->to);
+ pjsip_fromto_set_from(sub->from);
+ if (sub->from->tag.slen == 0) {
+ pj_create_unique_string(pool, &sub->from->tag);
+ rdata->to->tag = sub->from->tag;
+ }
+ sub->to = pjsip_hdr_clone(pool, rdata->from);
+ pjsip_fromto_set_to(sub->to);
+ sub->contact = pjsip_contact_hdr_create(pool);
+ sub->contact->uri = sub->from->uri;
+ sub->call_id = pjsip_cid_hdr_create(pool);
+ pj_strdup(pool, &sub->call_id->id, &rdata->call_id);
+ sub->cseq = pj_rand() % 0xFFFF;
+
+ expires = pjsip_msg_find_hdr( rdata->msg, PJSIP_H_EXPIRES, NULL);
+ if (expires) {
+ sub->default_interval = expires->ivalue;
+ if (sub->default_interval > 0 &&
+ sub->default_interval < SECONDS_BEFORE_EXPIRY)
+ {
+ status = 423; /* Interval too short. */
+ goto send_response;
+ }
+ } else {
+ sub->default_interval = 600;
+ }
+
+ /* Clone Event header. */
+ sub->event = pjsip_hdr_clone(pool, evhdr);
+
+ /* Register to hash table. */
+ create_subscriber_key(&sub->key, pool, PJSIP_ROLE_UAS, &sub->call_id->id,
+ &sub->from->tag);
+ pj_mutex_lock(mgr.mutex);
+ pj_hash_set(pool, mgr.ht, sub->key.ptr, sub->key.slen, sub);
+ pj_mutex_unlock(mgr.mutex);
+
+ /* Set timer where subscription will expire only when expires<>0.
+ * Subscriber may send new subscription with expires==0.
+ */
+ if (sub->default_interval != 0) {
+ sub_schedule_uas_expire( sub, sub->default_interval-SECONDS_BEFORE_EXPIRY);
+ }
+
+ /* Notify application. */
+ if (pkg->cb.on_subscribe) {
+ pjsip_event_sub_cb *cb = NULL;
+ sub->pending_tsx++;
+ (*pkg->cb.on_subscribe)(sub, rdata, &cb, &sub->default_interval);
+ sub->pending_tsx--;
+ if (cb == NULL)
+ pj_memset(&sub->cb, 0, sizeof(*cb));
+ else
+ pj_memcpy(&sub->cb, cb, sizeof(*cb));
+ }
+
+
+send_response:
+ PJ_LOG(4,(THIS_FILE, "event_sub%p (%s)(UAS): status=%d",
+ sub, state[sub->state].ptr, status));
+
+ tdata = pjsip_endpt_create_response( tsx->endpt, rdata, status);
+ if (tdata) {
+ if (reason.slen) {
+ /* Customize reason text. */
+ tdata->msg->line.status.reason = reason;
+ }
+ if (PJSIP_IS_STATUS_IN_CLASS(status,200)) {
+ /* Add Expires header. */
+ pjsip_expires_hdr *hdr;
+
+ hdr = pjsip_expires_hdr_create(tdata->pool);
+ hdr->ivalue = sub->default_interval;
+ pjsip_msg_add_hdr( tdata->msg, (pjsip_hdr*)hdr );
+ }
+ if (status == 423) {
+ /* Add Min-Expires header. */
+ pjsip_min_expires_hdr *hdr;
+
+ hdr = pjsip_min_expires_hdr_create(tdata->pool);
+ hdr->ivalue = SECONDS_BEFORE_EXPIRY;
+ pjsip_msg_add_hdr( tdata->msg, (pjsip_hdr*)hdr);
+ }
+ if (status == 489 ||
+ status==PJSIP_SC_NOT_ACCEPTABLE_HERE ||
+ PJSIP_IS_STATUS_IN_CLASS(status,200))
+ {
+ /* Add Allow-Events header. */
+ pjsip_hdr *hdr;
+ hdr = pjsip_hdr_shallow_clone(tdata->pool, mgr.allow_events);
+ pjsip_msg_add_hdr(tdata->msg, hdr);
+
+ /* Should add Accept header?. */
+ }
+
+ pjsip_tsx_on_tx_msg(tsx, tdata);
+ }
+
+ /* If received new subscription with expires=0, terminate. */
+ if (sub && sub->default_interval == 0) {
+ pj_assert(sub->state == PJSIP_EVENT_SUB_STATE_TERMINATED);
+ if (sub->cb.on_sub_terminated) {
+ pj_str_t reason = { "timeout", 7 };
+ (*sub->cb.on_sub_terminated)(sub, &reason);
+ }
+ }
+
+ if (!PJSIP_IS_STATUS_IN_CLASS(status,200) || (sub && sub->delete_flag)) {
+ if (sub && sub->mutex) {
+ pjsip_event_sub_destroy(sub);
+ } else if (sub) {
+ pjsip_endpt_destroy_pool(tsx->endpt, sub->pool);
+ }
+ } else {
+ pj_assert(status >= 200);
+ pj_mutex_unlock(sub->mutex);
+ }
+}
+
+/* This is the main callback when SUBSCRIBE request is received. */
+static void on_subscribe_request(pjsip_transaction *tsx, pjsip_rx_data *rdata)
+{
+ pjsip_event_sub *sub = find_sub(rdata);
+
+ if (sub)
+ on_received_sub_refresh(sub, tsx, rdata);
+ else
+ on_new_subscription(tsx, rdata);
+}
+
+
+/* This callback is called when response to SUBSCRIBE is received. */
+static void on_subscribe_response(void *token, pjsip_event *event)
+{
+ pjsip_event_sub *sub = token;
+ pjsip_transaction *tsx = event->obj.tsx;
+ int new_state, old_state = sub->state;
+
+ pj_assert(tsx->status_code >= 200);
+ if (tsx->status_code < 200)
+ return;
+
+ pj_assert(sub->role == PJSIP_ROLE_UAC);
+
+ /* Lock mutex. */
+ pj_mutex_lock(sub->mutex);
+
+ /* If request failed with 401/407 error, silently retry the request. */
+ if (tsx->status_code==401 || tsx->status_code==407) {
+ pjsip_tx_data *tdata;
+ tdata = pjsip_auth_reinit_req(sub->endpt,
+ sub->pool, &sub->auth_sess,
+ sub->cred_cnt, sub->cred_info,
+ tsx->last_tx, event->src.rdata );
+ if (tdata) {
+ int status;
+ pjsip_cseq_hdr *cseq;
+ cseq = pjsip_msg_find_hdr(tdata->msg, PJSIP_H_CSEQ, NULL);
+ cseq->cseq = sub->cseq++;
+ status = pjsip_endpt_send_request( sub->endpt, tdata,
+ -1, sub,
+ &on_subscribe_response);
+ if (status == 0) {
+ pj_mutex_unlock(sub->mutex);
+ return;
+ }
+ }
+ }
+
+ if (PJSIP_IS_STATUS_IN_CLASS(tsx->status_code,200)) {
+ /* Update To tag. */
+ if (sub->to->tag.slen == 0)
+ pj_strdup(sub->pool, &sub->to->tag, &event->src.rdata->to_tag);
+
+ new_state = sub->state;
+
+ } else if (tsx->status_code == 481) {
+ new_state = PJSIP_EVENT_SUB_STATE_TERMINATED;
+
+ } else if (tsx->status_code >= 300) {
+ /* RFC 3265 Section 3.1.4.2:
+ * If a SUBSCRIBE request to refresh a subscription fails
+ * with a non-481 response, the original subscription is still
+ * considered valid for the duration of original exires.
+ *
+ * Note:
+ * Since we normally send SUBSCRIBE for refreshing the subscription,
+ * it means the subscription already expired anyway. So we terminate
+ * the subscription now.
+ */
+ if (sub->state != PJSIP_EVENT_SUB_STATE_ACTIVE) {
+ new_state = PJSIP_EVENT_SUB_STATE_TERMINATED;
+ } else {
+ /* Use this to be compliant with Section 3.1.4.2
+ new_state = sub->state;
+ */
+ new_state = PJSIP_EVENT_SUB_STATE_TERMINATED;
+ }
+ } else {
+ pj_assert(0);
+ new_state = sub->state;
+ }
+
+ if (new_state != sub->state && sub->state != PJSIP_EVENT_SUB_STATE_TERMINATED) {
+ sub_set_state(sub, new_state);
+ }
+
+ if (sub->state == PJSIP_EVENT_SUB_STATE_ACTIVE ||
+ sub->state == PJSIP_EVENT_SUB_STATE_PENDING)
+ {
+ /*
+ * Register timer for next subscription refresh, but only when
+ * we're not unsubscribing. Also update default_interval and Expires
+ * header.
+ */
+ if (sub->default_interval > 0 && !sub->delete_flag) {
+ pjsip_expires_hdr *exp = NULL;
+
+ /* Could be transaction timeout. */
+ if (event->src_type == PJSIP_EVENT_RX_MSG) {
+ exp = pjsip_msg_find_hdr(event->src.rdata->msg,
+ PJSIP_H_EXPIRES, NULL);
+ }
+
+ if (exp) {
+ int delay = exp->ivalue;
+ if (delay > 0) {
+ pj_time_val new_expiry;
+ pj_gettimeofday(&new_expiry);
+ new_expiry.sec += delay;
+ if (sub->timer.id==0 ||
+ new_expiry.sec < sub->expiry_time.sec-SECONDS_BEFORE_EXPIRY/2)
+ {
+ //if (delay > 0 && delay < sub->default_interval) {
+ sub->default_interval = delay;
+ sub->uac_expires->ivalue = delay;
+ update_next_refresh(sub, delay);
+ }
+ }
+ }
+ }
+ }
+
+ /* Call callback. */
+ if (!sub->delete_flag) {
+ if (sub->cb.on_received_sub_response) {
+ (*sub->cb.on_received_sub_response)(sub, event);
+ }
+ }
+
+ /* Notify application if we're terminated. */
+ if (new_state!=old_state && new_state==PJSIP_EVENT_SUB_STATE_TERMINATED) {
+ if (sub->cb.on_sub_terminated) {
+ pj_str_t reason;
+ if (event->src_type == PJSIP_EVENT_RX_MSG)
+ reason = event->src.rdata->msg->line.status.reason;
+ else
+ reason = *pjsip_get_status_text(tsx->status_code);
+
+ (*sub->cb.on_sub_terminated)(sub, &reason);
+ }
+ }
+
+ /* Decrement pending tsx count. */
+ --sub->pending_tsx;
+ pj_assert(sub->pending_tsx >= 0);
+
+ if (sub->delete_flag && sub->pending_tsx <= 0) {
+ pjsip_event_sub_destroy(sub);
+ } else {
+ pj_mutex_unlock(sub->mutex);
+ }
+
+ /* DO NOT ACCESS sub FROM NOW ON! IT MIGHT HAVE BEEN DELETED */
+}
+
+/*
+ * This callback called when we receive incoming NOTIFY request.
+ */
+static void on_notify_request(pjsip_transaction *tsx, pjsip_rx_data *rdata)
+{
+ pjsip_event_sub *sub;
+ pjsip_tx_data *tdata;
+ int status = 200;
+ int old_state;
+ pj_str_t reason = { NULL, 0 };
+ pj_str_t reason_phrase = { NULL, 0 };
+ int new_state = PJSIP_EVENT_SUB_STATE_NULL;
+
+ /* Find subscription based on Call-ID and From tag.
+ * This will also automatically lock the subscription, if it's found.
+ */
+ sub = find_sub(rdata);
+ if (!sub) {
+ /* RFC 3265: Section 3.2 Description of NOTIFY Behavior:
+ * Answer with 481 Subscription does not exist.
+ */
+ PJ_LOG(4,(THIS_FILE, "Unable to find subscription for incoming NOTIFY!"));
+ status = 481;
+ reason_phrase = pj_str("Subscription does not exist");
+
+ } else {
+ pj_assert(sub->role == PJSIP_ROLE_UAC);
+ PJ_LOG(4,(THIS_FILE, "event_sub%p (%s): received NOTIFY",
+ sub, state[sub->state].ptr));
+
+ }
+
+ new_state = old_state = sub->state;
+
+ /* RFC 3265: Section 3.2.1
+ * Check that the Event header match the subscription.
+ */
+ if (status == 200) {
+ pjsip_event_hdr *hdr;
+ pj_str_t hname = { "Event", 5 };
+
+ hdr = pjsip_msg_find_hdr_by_name(rdata->msg, &hname, NULL);
+ if (!hdr) {
+ status = PJSIP_SC_BAD_REQUEST;
+ reason_phrase = pj_str("No Event header found");
+ } else if (pj_stricmp(&hdr->event_type, &sub->event->event_type) != 0 ||
+ pj_stricmp(&hdr->id_param, &sub->event->id_param) != 0)
+ {
+ status = 481;
+ reason_phrase = pj_str("Subscription does not exist");
+ }
+ }
+
+ /* Update subscription state and timer. */
+ if (status == 200) {
+ pjsip_sub_state_hdr *hdr;
+ const pj_str_t hname = { "Subscription-State", 18 };
+ const pj_str_t state_active = { "active", 6 },
+ state_pending = { "pending", 7},
+ state_terminated = { "terminated", 10 };
+
+ hdr = pjsip_msg_find_hdr_by_name( rdata->msg, &hname, NULL);
+ if (!hdr) {
+ status = PJSIP_SC_BAD_REQUEST;
+ reason_phrase = pj_str("No Subscription-State header found");
+ goto process;
+ }
+
+ /*
+ * Update subscription state.
+ */
+ if (pj_stricmp(&hdr->sub_state, &state_active) == 0) {
+ if (sub->state != PJSIP_EVENT_SUB_STATE_TERMINATED)
+ new_state = PJSIP_EVENT_SUB_STATE_ACTIVE;
+ } else if (pj_stricmp(&hdr->sub_state, &state_pending) == 0) {
+ if (sub->state != PJSIP_EVENT_SUB_STATE_TERMINATED)
+ new_state = PJSIP_EVENT_SUB_STATE_PENDING;
+ } else if (pj_stricmp(&hdr->sub_state, &state_terminated) == 0) {
+ new_state = PJSIP_EVENT_SUB_STATE_TERMINATED;
+ } else {
+ new_state = PJSIP_EVENT_SUB_STATE_UNKNOWN;
+ }
+
+ reason = hdr->reason_param;
+
+ if (new_state != sub->state && new_state != PJSIP_EVENT_SUB_STATE_NULL &&
+ sub->state != PJSIP_EVENT_SUB_STATE_TERMINATED)
+ {
+ sub_set_state(sub, new_state);
+ if (new_state == PJSIP_EVENT_SUB_STATE_UNKNOWN) {
+ pj_strdup_with_null(sub->pool, &sub->state_str, &hdr->sub_state);
+ } else {
+ sub->state_str = state[new_state];
+ }
+ }
+
+ /*
+ * Update timeout timer in required, just in case notifier changed the
+ * expiration to shorter time.
+ * Section 3.2.2: the expires param can only shorten the interval.
+ */
+ if ((sub->state==PJSIP_EVENT_SUB_STATE_ACTIVE ||
+ sub->state==PJSIP_EVENT_SUB_STATE_PENDING) && hdr->expires_param > 0)
+ {
+ pj_time_val now, new_expiry;
+
+ pj_gettimeofday(&now);
+ new_expiry.sec = now.sec + hdr->expires_param;
+ if (sub->timer.id==0 ||
+ new_expiry.sec < sub->expiry_time.sec-SECONDS_BEFORE_EXPIRY/2)
+ {
+ update_next_refresh(sub, hdr->expires_param);
+ }
+ }
+ }
+
+process:
+ /* Note: here we sub MAY BE NULL! */
+
+ /* Send response to NOTIFY */
+ tdata = pjsip_endpt_create_response( tsx->endpt, rdata, status );
+ if (tdata) {
+ if (reason_phrase.slen)
+ tdata->msg->line.status.reason = reason_phrase;
+
+ if (PJSIP_IS_STATUS_IN_CLASS(status,200)) {
+ pjsip_hdr *hdr;
+ hdr = pjsip_hdr_shallow_clone(tdata->pool, mgr.allow_events);
+ pjsip_msg_add_hdr( tdata->msg, hdr);
+ }
+
+ pjsip_tsx_on_tx_msg(tsx, tdata);
+ }
+
+ /* Call NOTIFY callback, if any. */
+ if (sub && PJSIP_IS_STATUS_IN_CLASS(status,200) && sub->cb.on_received_notify) {
+ sub->pending_tsx++;
+ (*sub->cb.on_received_notify)(sub, rdata);
+ sub->pending_tsx--;
+ }
+
+ /* Check if subscription is terminated and call callback. */
+ if (sub && new_state!=old_state && new_state==PJSIP_EVENT_SUB_STATE_TERMINATED) {
+ if (sub->cb.on_sub_terminated) {
+ sub->pending_tsx++;
+ (*sub->cb.on_sub_terminated)(sub, &reason);
+ sub->pending_tsx--;
+ }
+ }
+
+ /* Check if application has requested deletion. */
+ if (sub && sub->delete_flag && sub->pending_tsx <= 0) {
+ pjsip_event_sub_destroy(sub);
+ } else if (sub) {
+ pj_mutex_unlock(sub->mutex);
+ }
+}
+
+/* This callback is called when we received NOTIFY response. */
+static void on_notify_response(void *token, pjsip_event *event)
+{
+ pjsip_event_sub *sub = token;
+ pjsip_event_sub_state old_state = sub->state;
+ pjsip_transaction *tsx = event->obj.tsx;
+
+ /* Lock the subscription. */
+ pj_mutex_lock(sub->mutex);
+
+ pj_assert(sub->role == PJSIP_ROLE_UAS);
+
+ /* If request failed with authorization failure, silently retry. */
+ if (tsx->status_code==401 || tsx->status_code==407) {
+ pjsip_tx_data *tdata;
+ tdata = pjsip_auth_reinit_req(sub->endpt,
+ sub->pool, &sub->auth_sess,
+ sub->cred_cnt, sub->cred_info,
+ tsx->last_tx, event->src.rdata );
+ if (tdata) {
+ int status;
+ pjsip_cseq_hdr *cseq;
+ cseq = pjsip_msg_find_hdr(tdata->msg, PJSIP_H_CSEQ, NULL);
+ cseq->cseq = sub->cseq++;
+ status = pjsip_endpt_send_request( sub->endpt, tdata,
+ -1, sub,
+ &on_notify_response);
+ if (status == 0) {
+ pj_mutex_unlock(sub->mutex);
+ return;
+ }
+ }
+ }
+
+ /* Notify application. */
+ if (sub->cb.on_received_notify_response)
+ (*sub->cb.on_received_notify_response)(sub, event);
+
+ /* Check for response 481. */
+ if (event->obj.tsx->status_code == 481) {
+ /* Remote says that the subscription does not exist!
+ * Terminate subscription!
+ */
+ sub_set_state(sub, PJSIP_EVENT_SUB_STATE_TERMINATED);
+ if (sub->timer.id) {
+ pjsip_endpt_cancel_timer(sub->endpt, &sub->timer);
+ sub->timer.id = 0;
+ }
+
+ PJ_LOG(4, (THIS_FILE,
+ "event_sub%p (%s): got 481 response to NOTIFY. Terminating...",
+ sub, state[sub->state].ptr));
+
+ /* Notify app. */
+ if (sub->state!=old_state && sub->cb.on_sub_terminated)
+ (*sub->cb.on_sub_terminated)(sub, &event->src.rdata->msg->line.status.reason);
+ }
+
+ /* Decrement pending transaction count. */
+ --sub->pending_tsx;
+ pj_assert(sub->pending_tsx >= 0);
+
+ /* Check that the subscription is marked for deletion. */
+ if (sub->delete_flag && sub->pending_tsx <= 0) {
+ pjsip_event_sub_destroy(sub);
+ } else {
+ pj_mutex_unlock(sub->mutex);
+ }
+
+ /* DO NOT ACCESS sub, IT MIGHT HAVE BEEN DESTROYED! */
+}
+
+
+/* This is the transaction handler for incoming SUBSCRIBE and NOTIFY
+ * requests.
+ */
+static void tsx_handler( struct pjsip_module *mod, pjsip_event *event )
+{
+ pjsip_msg *msg;
+ pjsip_rx_data *rdata;
+
+ /* Only want incoming message events. */
+ if (event->src_type != PJSIP_EVENT_RX_MSG)
+ return;
+
+ rdata = event->src.rdata;
+ msg = rdata->msg;
+
+ /* Only want to process request messages. */
+ if (msg->type != PJSIP_REQUEST_MSG)
+ return;
+
+ /* Only want the first notification. */
+ if (event->obj.tsx && event->obj.tsx->status_code >= 100)
+ return;
+
+ if (pjsip_method_cmp(&msg->line.req.method, &SUBSCRIBE)==0) {
+ /* Process incoming SUBSCRIBE request. */
+ on_subscribe_request( event->obj.tsx, rdata );
+ } else if (pjsip_method_cmp(&msg->line.req.method, &NOTIFY)==0) {
+ /* Process incoming NOTIFY request. */
+ on_notify_request( event->obj.tsx, rdata );
+ }
+}
+
diff --git a/pjsip/src/pjsip-simple/event_notify_msg.c b/pjsip/src/pjsip-simple/event_notify_msg.c
index 55758052..a5f946ef 100644
--- a/pjsip/src/pjsip-simple/event_notify_msg.c
+++ b/pjsip/src/pjsip-simple/event_notify_msg.c
@@ -1,322 +1,322 @@
-/* $Id$ */
-/*
- * Copyright (C) 2003-2006 Benny Prijono <benny@prijono.org>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
-#include <pjsip_simple/event_notify_msg.h>
-#include <pjsip/print.h>
-#include <pjsip/sip_parser.h>
-#include <pj/pool.h>
-#include <pj/string.h>
-#include <pj/except.h>
-
-static int pjsip_event_hdr_print( pjsip_event_hdr *hdr,
- char *buf, pj_size_t size);
-static pjsip_event_hdr* pjsip_event_hdr_clone( pj_pool_t *pool,
- const pjsip_event_hdr *hdr);
-static pjsip_event_hdr* pjsip_event_hdr_shallow_clone( pj_pool_t *pool,
- const pjsip_event_hdr*);
-
-static pjsip_hdr_vptr event_hdr_vptr =
-{
- (pjsip_hdr_clone_fptr) &pjsip_event_hdr_clone,
- (pjsip_hdr_clone_fptr) &pjsip_event_hdr_shallow_clone,
- (pjsip_hdr_print_fptr) &pjsip_event_hdr_print,
-};
-
-
-PJ_DEF(pjsip_event_hdr*) pjsip_event_hdr_create(pj_pool_t *pool)
-{
- pj_str_t event = { "Event", 5 };
- pjsip_event_hdr *hdr = pj_pool_calloc(pool, 1, sizeof(*hdr));
- hdr->type = PJSIP_H_OTHER;
- hdr->name = hdr->sname = event;
- hdr->vptr = &event_hdr_vptr;
- pj_list_init(hdr);
- return hdr;
-}
-
-static int pjsip_event_hdr_print( pjsip_event_hdr *hdr,
- char *buf, pj_size_t size)
-{
- char *p = buf;
- char *endbuf = buf+size;
- int printed;
-
- copy_advance(p, hdr->name);
- *p++ = ':';
- *p++ = ' ';
-
- copy_advance(p, hdr->event_type);
- copy_advance_pair(p, ";id=", 4, hdr->id_param);
- if (hdr->other_param.slen)
- copy_advance(p, hdr->other_param);
- return p - buf;
-}
-
-static pjsip_event_hdr* pjsip_event_hdr_clone( pj_pool_t *pool,
- const pjsip_event_hdr *rhs)
-{
- pjsip_event_hdr *hdr = pjsip_event_hdr_create(pool);
- pj_strdup(pool, &hdr->event_type, &rhs->event_type);
- pj_strdup(pool, &hdr->id_param, &rhs->id_param);
- pj_strdup(pool, &hdr->other_param, &rhs->other_param);
- return hdr;
-}
-
-static pjsip_event_hdr* pjsip_event_hdr_shallow_clone( pj_pool_t *pool,
- const pjsip_event_hdr *rhs )
-{
- pjsip_event_hdr *hdr = pj_pool_alloc(pool, sizeof(*hdr));
- pj_memcpy(hdr, rhs, sizeof(*hdr));
- return hdr;
-}
-
-
-static int pjsip_allow_events_hdr_print(pjsip_allow_events_hdr *hdr,
- char *buf, pj_size_t size);
-static pjsip_allow_events_hdr*
-pjsip_allow_events_hdr_clone(pj_pool_t *pool,
- const pjsip_allow_events_hdr *hdr);
-static pjsip_allow_events_hdr*
-pjsip_allow_events_hdr_shallow_clone(pj_pool_t *pool,
- const pjsip_allow_events_hdr*);
-
-static pjsip_hdr_vptr allow_event_hdr_vptr =
-{
- (pjsip_hdr_clone_fptr) &pjsip_allow_events_hdr_clone,
- (pjsip_hdr_clone_fptr) &pjsip_allow_events_hdr_shallow_clone,
- (pjsip_hdr_print_fptr) &pjsip_allow_events_hdr_print,
-};
-
-
-PJ_DEF(pjsip_allow_events_hdr*) pjsip_allow_events_hdr_create(pj_pool_t *pool)
-{
- pj_str_t allow_events = { "Allow-Events", 12 };
- pjsip_allow_events_hdr *hdr = pj_pool_calloc(pool, 1, sizeof(*hdr));
- hdr->type = PJSIP_H_OTHER;
- hdr->name = hdr->sname = allow_events;
- hdr->vptr = &allow_event_hdr_vptr;
- pj_list_init(hdr);
- return hdr;
-}
-
-static int pjsip_allow_events_hdr_print(pjsip_allow_events_hdr *hdr,
- char *buf, pj_size_t size)
-{
- char *p = buf;
- char *endbuf = buf+size;
- int printed;
-
- copy_advance(p, hdr->name);
- *p++ = ':';
- *p++ = ' ';
-
- if (hdr->event_cnt > 0) {
- int i;
- copy_advance(p, hdr->events[0]);
- for (i=1; i<hdr->event_cnt; ++i) {
- copy_advance_pair(p, ",", 1, hdr->events[i]);
- }
- }
-
- return p - buf;
-}
-
-static pjsip_allow_events_hdr*
-pjsip_allow_events_hdr_clone(pj_pool_t *pool,
- const pjsip_allow_events_hdr *rhs)
-{
- int i;
-
- pjsip_allow_events_hdr *hdr = pjsip_allow_events_hdr_create(pool);
- hdr->event_cnt = rhs->event_cnt;
- for (i=0; i<rhs->event_cnt; ++i) {
- pj_strdup(pool, &hdr->events[i], &rhs->events[i]);
- }
- return hdr;
-}
-
-static pjsip_allow_events_hdr*
-pjsip_allow_events_hdr_shallow_clone(pj_pool_t *pool,
- const pjsip_allow_events_hdr *rhs)
-{
- pjsip_allow_events_hdr *hdr = pj_pool_alloc(pool, sizeof(*hdr));
- pj_memcpy(hdr, rhs, sizeof(*hdr));
- return hdr;
-}
-
-
-static int pjsip_sub_state_hdr_print(pjsip_sub_state_hdr *hdr,
- char *buf, pj_size_t size);
-static pjsip_sub_state_hdr*
-pjsip_sub_state_hdr_clone(pj_pool_t *pool,
- const pjsip_sub_state_hdr *hdr);
-static pjsip_sub_state_hdr*
-pjsip_sub_state_hdr_shallow_clone(pj_pool_t *pool,
- const pjsip_sub_state_hdr*);
-
-static pjsip_hdr_vptr sub_state_hdr_vptr =
-{
- (pjsip_hdr_clone_fptr) &pjsip_sub_state_hdr_clone,
- (pjsip_hdr_clone_fptr) &pjsip_sub_state_hdr_shallow_clone,
- (pjsip_hdr_print_fptr) &pjsip_sub_state_hdr_print,
-};
-
-
-PJ_DEF(pjsip_sub_state_hdr*) pjsip_sub_state_hdr_create(pj_pool_t *pool)
-{
- pj_str_t sub_state = { "Subscription-State", 18 };
- pjsip_sub_state_hdr *hdr = pj_pool_calloc(pool, 1, sizeof(*hdr));
- hdr->type = PJSIP_H_OTHER;
- hdr->name = hdr->sname = sub_state;
- hdr->vptr = &sub_state_hdr_vptr;
- hdr->expires_param = -1;
- hdr->retry_after = -1;
- pj_list_init(hdr);
- return hdr;
-}
-
-static int pjsip_sub_state_hdr_print(pjsip_sub_state_hdr *hdr,
- char *buf, pj_size_t size)
-{
- char *p = buf;
- char *endbuf = buf+size;
- int printed;
-
- copy_advance(p, hdr->name);
- *p++ = ':';
- *p++ = ' ';
-
- copy_advance(p, hdr->sub_state);
- copy_advance_pair(p, ";reason=", 8, hdr->reason_param);
- if (hdr->expires_param >= 0) {
- pj_memcpy(p, ";expires=", 9);
- p += 9;
- printed = pj_utoa(hdr->expires_param, p);
- p += printed;
- }
- if (hdr->retry_after >= 0) {
- pj_memcpy(p, ";retry-after=", 13);
- p += 9;
- printed = pj_utoa(hdr->retry_after, p);
- p += printed;
- }
- if (hdr->other_param.slen)
- copy_advance(p, hdr->other_param);
-
- return p - buf;
-}
-
-static pjsip_sub_state_hdr*
-pjsip_sub_state_hdr_clone(pj_pool_t *pool,
- const pjsip_sub_state_hdr *rhs)
-{
- pjsip_sub_state_hdr *hdr = pjsip_sub_state_hdr_create(pool);
- pj_strdup(pool, &hdr->sub_state, &rhs->sub_state);
- pj_strdup(pool, &hdr->reason_param, &rhs->reason_param);
- hdr->retry_after = rhs->retry_after;
- hdr->expires_param = rhs->expires_param;
- pj_strdup(pool, &hdr->other_param, &rhs->other_param);
- return hdr;
-}
-
-static pjsip_sub_state_hdr*
-pjsip_sub_state_hdr_shallow_clone(pj_pool_t *pool,
- const pjsip_sub_state_hdr *rhs)
-{
- pjsip_sub_state_hdr *hdr = pj_pool_alloc(pool, sizeof(*hdr));
- pj_memcpy(hdr, rhs, sizeof(*hdr));
- return hdr;
-}
-
-static pjsip_event_hdr *parse_hdr_event(pj_scanner *scanner,
- pj_pool_t *pool)
-{
- pjsip_event_hdr *hdr = pjsip_event_hdr_create(pool);
- const pj_str_t id_param = { "id", 2 };
-
- pj_scan_get(scanner, pjsip_TOKEN_SPEC, &hdr->event_type);
-
- while (*scanner->current == ';') {
- pj_str_t pname, pvalue;
- pj_scan_get_char(scanner);
- pjsip_parse_param_imp(scanner, &pname, &pvalue, 0);
- if (pj_stricmp(&pname, &id_param)==0) {
- hdr->id_param = pvalue;
- } else {
- pjsip_concat_param_imp(&hdr->other_param, pool, &pname, &pvalue, ';');
- }
- }
- pjsip_parse_end_hdr_imp( scanner );
- return hdr;
-}
-
-static pjsip_allow_events_hdr *parse_hdr_allow_events(pj_scanner *scanner,
- pj_pool_t *pool)
-{
- pjsip_allow_events_hdr *hdr = pjsip_allow_events_hdr_create(pool);
-
- pj_scan_get(scanner, pjsip_TOKEN_SPEC, &hdr->events[0]);
- hdr->event_cnt = 1;
-
- while (*scanner->current == ',') {
- pj_scan_get_char(scanner);
- pj_scan_get(scanner, pjsip_TOKEN_SPEC, &hdr->events[hdr->event_cnt++]);
- if (hdr->event_cnt == PJSIP_MAX_ALLOW_EVENTS) {
- PJ_THROW(PJSIP_SYN_ERR_EXCEPTION);
- }
- }
-
- pjsip_parse_end_hdr_imp( scanner );
- return hdr;
-}
-
-static pjsip_sub_state_hdr *parse_hdr_sub_state(pj_scanner *scanner,
- pj_pool_t *pool)
-{
- pjsip_sub_state_hdr *hdr = pjsip_sub_state_hdr_create(pool);
- const pj_str_t reason = { "reason", 6 },
- expires = { "expires", 7 },
- retry_after = { "retry-after", 11 };
- pj_scan_get(scanner, pjsip_TOKEN_SPEC, &hdr->sub_state);
-
- while (*scanner->current == ';') {
- pj_str_t pname, pvalue;
-
- pj_scan_get_char(scanner);
- pjsip_parse_param_imp(scanner, &pname, &pvalue, 0);
- if (pj_stricmp(&pname, &reason) == 0) {
- hdr->reason_param = pvalue;
- } else if (pj_stricmp(&pname, &expires) == 0) {
- hdr->expires_param = pj_strtoul(&pvalue);
- } else if (pj_stricmp(&pname, &retry_after) == 0) {
- hdr->retry_after = pj_strtoul(&pvalue);
- } else {
- pjsip_concat_param_imp(&hdr->other_param, pool, &pname, &pvalue, ';');
- }
- }
-
- pjsip_parse_end_hdr_imp( scanner );
- return hdr;
-}
-
-PJ_DEF(void) pjsip_event_notify_init_parser(void)
-{
- pjsip_register_hdr_parser( "Event", NULL, (pjsip_parse_hdr_func*) &parse_hdr_event);
- pjsip_register_hdr_parser( "Allow-Events", NULL, (pjsip_parse_hdr_func*) &parse_hdr_allow_events);
- pjsip_register_hdr_parser( "Subscription-State", NULL, (pjsip_parse_hdr_func*) &parse_hdr_sub_state);
-}
+/* $Id$ */
+/*
+ * Copyright (C) 2003-2006 Benny Prijono <benny@prijono.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#include <pjsip_simple/event_notify_msg.h>
+#include <pjsip/print.h>
+#include <pjsip/sip_parser.h>
+#include <pj/pool.h>
+#include <pj/string.h>
+#include <pj/except.h>
+
+static int pjsip_event_hdr_print( pjsip_event_hdr *hdr,
+ char *buf, pj_size_t size);
+static pjsip_event_hdr* pjsip_event_hdr_clone( pj_pool_t *pool,
+ const pjsip_event_hdr *hdr);
+static pjsip_event_hdr* pjsip_event_hdr_shallow_clone( pj_pool_t *pool,
+ const pjsip_event_hdr*);
+
+static pjsip_hdr_vptr event_hdr_vptr =
+{
+ (pjsip_hdr_clone_fptr) &pjsip_event_hdr_clone,
+ (pjsip_hdr_clone_fptr) &pjsip_event_hdr_shallow_clone,
+ (pjsip_hdr_print_fptr) &pjsip_event_hdr_print,
+};
+
+
+PJ_DEF(pjsip_event_hdr*) pjsip_event_hdr_create(pj_pool_t *pool)
+{
+ pj_str_t event = { "Event", 5 };
+ pjsip_event_hdr *hdr = pj_pool_calloc(pool, 1, sizeof(*hdr));
+ hdr->type = PJSIP_H_OTHER;
+ hdr->name = hdr->sname = event;
+ hdr->vptr = &event_hdr_vptr;
+ pj_list_init(hdr);
+ return hdr;
+}
+
+static int pjsip_event_hdr_print( pjsip_event_hdr *hdr,
+ char *buf, pj_size_t size)
+{
+ char *p = buf;
+ char *endbuf = buf+size;
+ int printed;
+
+ copy_advance(p, hdr->name);
+ *p++ = ':';
+ *p++ = ' ';
+
+ copy_advance(p, hdr->event_type);
+ copy_advance_pair(p, ";id=", 4, hdr->id_param);
+ if (hdr->other_param.slen)
+ copy_advance(p, hdr->other_param);
+ return p - buf;
+}
+
+static pjsip_event_hdr* pjsip_event_hdr_clone( pj_pool_t *pool,
+ const pjsip_event_hdr *rhs)
+{
+ pjsip_event_hdr *hdr = pjsip_event_hdr_create(pool);
+ pj_strdup(pool, &hdr->event_type, &rhs->event_type);
+ pj_strdup(pool, &hdr->id_param, &rhs->id_param);
+ pj_strdup(pool, &hdr->other_param, &rhs->other_param);
+ return hdr;
+}
+
+static pjsip_event_hdr* pjsip_event_hdr_shallow_clone( pj_pool_t *pool,
+ const pjsip_event_hdr *rhs )
+{
+ pjsip_event_hdr *hdr = pj_pool_alloc(pool, sizeof(*hdr));
+ pj_memcpy(hdr, rhs, sizeof(*hdr));
+ return hdr;
+}
+
+
+static int pjsip_allow_events_hdr_print(pjsip_allow_events_hdr *hdr,
+ char *buf, pj_size_t size);
+static pjsip_allow_events_hdr*
+pjsip_allow_events_hdr_clone(pj_pool_t *pool,
+ const pjsip_allow_events_hdr *hdr);
+static pjsip_allow_events_hdr*
+pjsip_allow_events_hdr_shallow_clone(pj_pool_t *pool,
+ const pjsip_allow_events_hdr*);
+
+static pjsip_hdr_vptr allow_event_hdr_vptr =
+{
+ (pjsip_hdr_clone_fptr) &pjsip_allow_events_hdr_clone,
+ (pjsip_hdr_clone_fptr) &pjsip_allow_events_hdr_shallow_clone,
+ (pjsip_hdr_print_fptr) &pjsip_allow_events_hdr_print,
+};
+
+
+PJ_DEF(pjsip_allow_events_hdr*) pjsip_allow_events_hdr_create(pj_pool_t *pool)
+{
+ pj_str_t allow_events = { "Allow-Events", 12 };
+ pjsip_allow_events_hdr *hdr = pj_pool_calloc(pool, 1, sizeof(*hdr));
+ hdr->type = PJSIP_H_OTHER;
+ hdr->name = hdr->sname = allow_events;
+ hdr->vptr = &allow_event_hdr_vptr;
+ pj_list_init(hdr);
+ return hdr;
+}
+
+static int pjsip_allow_events_hdr_print(pjsip_allow_events_hdr *hdr,
+ char *buf, pj_size_t size)
+{
+ char *p = buf;
+ char *endbuf = buf+size;
+ int printed;
+
+ copy_advance(p, hdr->name);
+ *p++ = ':';
+ *p++ = ' ';
+
+ if (hdr->event_cnt > 0) {
+ int i;
+ copy_advance(p, hdr->events[0]);
+ for (i=1; i<hdr->event_cnt; ++i) {
+ copy_advance_pair(p, ",", 1, hdr->events[i]);
+ }
+ }
+
+ return p - buf;
+}
+
+static pjsip_allow_events_hdr*
+pjsip_allow_events_hdr_clone(pj_pool_t *pool,
+ const pjsip_allow_events_hdr *rhs)
+{
+ int i;
+
+ pjsip_allow_events_hdr *hdr = pjsip_allow_events_hdr_create(pool);
+ hdr->event_cnt = rhs->event_cnt;
+ for (i=0; i<rhs->event_cnt; ++i) {
+ pj_strdup(pool, &hdr->events[i], &rhs->events[i]);
+ }
+ return hdr;
+}
+
+static pjsip_allow_events_hdr*
+pjsip_allow_events_hdr_shallow_clone(pj_pool_t *pool,
+ const pjsip_allow_events_hdr *rhs)
+{
+ pjsip_allow_events_hdr *hdr = pj_pool_alloc(pool, sizeof(*hdr));
+ pj_memcpy(hdr, rhs, sizeof(*hdr));
+ return hdr;
+}
+
+
+static int pjsip_sub_state_hdr_print(pjsip_sub_state_hdr *hdr,
+ char *buf, pj_size_t size);
+static pjsip_sub_state_hdr*
+pjsip_sub_state_hdr_clone(pj_pool_t *pool,
+ const pjsip_sub_state_hdr *hdr);
+static pjsip_sub_state_hdr*
+pjsip_sub_state_hdr_shallow_clone(pj_pool_t *pool,
+ const pjsip_sub_state_hdr*);
+
+static pjsip_hdr_vptr sub_state_hdr_vptr =
+{
+ (pjsip_hdr_clone_fptr) &pjsip_sub_state_hdr_clone,
+ (pjsip_hdr_clone_fptr) &pjsip_sub_state_hdr_shallow_clone,
+ (pjsip_hdr_print_fptr) &pjsip_sub_state_hdr_print,
+};
+
+
+PJ_DEF(pjsip_sub_state_hdr*) pjsip_sub_state_hdr_create(pj_pool_t *pool)
+{
+ pj_str_t sub_state = { "Subscription-State", 18 };
+ pjsip_sub_state_hdr *hdr = pj_pool_calloc(pool, 1, sizeof(*hdr));
+ hdr->type = PJSIP_H_OTHER;
+ hdr->name = hdr->sname = sub_state;
+ hdr->vptr = &sub_state_hdr_vptr;
+ hdr->expires_param = -1;
+ hdr->retry_after = -1;
+ pj_list_init(hdr);
+ return hdr;
+}
+
+static int pjsip_sub_state_hdr_print(pjsip_sub_state_hdr *hdr,
+ char *buf, pj_size_t size)
+{
+ char *p = buf;
+ char *endbuf = buf+size;
+ int printed;
+
+ copy_advance(p, hdr->name);
+ *p++ = ':';
+ *p++ = ' ';
+
+ copy_advance(p, hdr->sub_state);
+ copy_advance_pair(p, ";reason=", 8, hdr->reason_param);
+ if (hdr->expires_param >= 0) {
+ pj_memcpy(p, ";expires=", 9);
+ p += 9;
+ printed = pj_utoa(hdr->expires_param, p);
+ p += printed;
+ }
+ if (hdr->retry_after >= 0) {
+ pj_memcpy(p, ";retry-after=", 13);
+ p += 9;
+ printed = pj_utoa(hdr->retry_after, p);
+ p += printed;
+ }
+ if (hdr->other_param.slen)
+ copy_advance(p, hdr->other_param);
+
+ return p - buf;
+}
+
+static pjsip_sub_state_hdr*
+pjsip_sub_state_hdr_clone(pj_pool_t *pool,
+ const pjsip_sub_state_hdr *rhs)
+{
+ pjsip_sub_state_hdr *hdr = pjsip_sub_state_hdr_create(pool);
+ pj_strdup(pool, &hdr->sub_state, &rhs->sub_state);
+ pj_strdup(pool, &hdr->reason_param, &rhs->reason_param);
+ hdr->retry_after = rhs->retry_after;
+ hdr->expires_param = rhs->expires_param;
+ pj_strdup(pool, &hdr->other_param, &rhs->other_param);
+ return hdr;
+}
+
+static pjsip_sub_state_hdr*
+pjsip_sub_state_hdr_shallow_clone(pj_pool_t *pool,
+ const pjsip_sub_state_hdr *rhs)
+{
+ pjsip_sub_state_hdr *hdr = pj_pool_alloc(pool, sizeof(*hdr));
+ pj_memcpy(hdr, rhs, sizeof(*hdr));
+ return hdr;
+}
+
+static pjsip_event_hdr *parse_hdr_event(pj_scanner *scanner,
+ pj_pool_t *pool)
+{
+ pjsip_event_hdr *hdr = pjsip_event_hdr_create(pool);
+ const pj_str_t id_param = { "id", 2 };
+
+ pj_scan_get(scanner, pjsip_TOKEN_SPEC, &hdr->event_type);
+
+ while (*scanner->current == ';') {
+ pj_str_t pname, pvalue;
+ pj_scan_get_char(scanner);
+ pjsip_parse_param_imp(scanner, &pname, &pvalue, 0);
+ if (pj_stricmp(&pname, &id_param)==0) {
+ hdr->id_param = pvalue;
+ } else {
+ pjsip_concat_param_imp(&hdr->other_param, pool, &pname, &pvalue, ';');
+ }
+ }
+ pjsip_parse_end_hdr_imp( scanner );
+ return hdr;
+}
+
+static pjsip_allow_events_hdr *parse_hdr_allow_events(pj_scanner *scanner,
+ pj_pool_t *pool)
+{
+ pjsip_allow_events_hdr *hdr = pjsip_allow_events_hdr_create(pool);
+
+ pj_scan_get(scanner, pjsip_TOKEN_SPEC, &hdr->events[0]);
+ hdr->event_cnt = 1;
+
+ while (*scanner->current == ',') {
+ pj_scan_get_char(scanner);
+ pj_scan_get(scanner, pjsip_TOKEN_SPEC, &hdr->events[hdr->event_cnt++]);
+ if (hdr->event_cnt == PJSIP_MAX_ALLOW_EVENTS) {
+ PJ_THROW(PJSIP_SYN_ERR_EXCEPTION);
+ }
+ }
+
+ pjsip_parse_end_hdr_imp( scanner );
+ return hdr;
+}
+
+static pjsip_sub_state_hdr *parse_hdr_sub_state(pj_scanner *scanner,
+ pj_pool_t *pool)
+{
+ pjsip_sub_state_hdr *hdr = pjsip_sub_state_hdr_create(pool);
+ const pj_str_t reason = { "reason", 6 },
+ expires = { "expires", 7 },
+ retry_after = { "retry-after", 11 };
+ pj_scan_get(scanner, pjsip_TOKEN_SPEC, &hdr->sub_state);
+
+ while (*scanner->current == ';') {
+ pj_str_t pname, pvalue;
+
+ pj_scan_get_char(scanner);
+ pjsip_parse_param_imp(scanner, &pname, &pvalue, 0);
+ if (pj_stricmp(&pname, &reason) == 0) {
+ hdr->reason_param = pvalue;
+ } else if (pj_stricmp(&pname, &expires) == 0) {
+ hdr->expires_param = pj_strtoul(&pvalue);
+ } else if (pj_stricmp(&pname, &retry_after) == 0) {
+ hdr->retry_after = pj_strtoul(&pvalue);
+ } else {
+ pjsip_concat_param_imp(&hdr->other_param, pool, &pname, &pvalue, ';');
+ }
+ }
+
+ pjsip_parse_end_hdr_imp( scanner );
+ return hdr;
+}
+
+PJ_DEF(void) pjsip_event_notify_init_parser(void)
+{
+ pjsip_register_hdr_parser( "Event", NULL, (pjsip_parse_hdr_func*) &parse_hdr_event);
+ pjsip_register_hdr_parser( "Allow-Events", NULL, (pjsip_parse_hdr_func*) &parse_hdr_allow_events);
+ pjsip_register_hdr_parser( "Subscription-State", NULL, (pjsip_parse_hdr_func*) &parse_hdr_sub_state);
+}
diff --git a/pjsip/src/pjsip-simple/messaging.c b/pjsip/src/pjsip-simple/messaging.c
index d43acc0c..6e08af68 100644
--- a/pjsip/src/pjsip-simple/messaging.c
+++ b/pjsip/src/pjsip-simple/messaging.c
@@ -1,352 +1,352 @@
-/* $Id$ */
-/*
- * Copyright (C) 2003-2006 Benny Prijono <benny@prijono.org>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
-#include <pjsip_simple/messaging.h>
-#include <pjsip/sip_endpoint.h>
-#include <pjsip/sip_parser.h>
-#include <pjsip/sip_transaction.h>
-#include <pjsip/sip_event.h>
-#include <pjsip/sip_module.h>
-#include <pjsip/sip_util.h>
-#include <pj/string.h>
-#include <pj/pool.h>
-#include <pj/guid.h>
-#include <pj/string.h>
-#include <pj/log.h>
-#include <stdio.h>
-#include <stdlib.h>
-
-#define THIS_FILE "messaging"
-
-struct messaging_data
-{
- void *token;
- pjsip_messaging_cb cb;
-};
-
-struct pjsip_messaging_session
-{
- pj_pool_t *pool;
- pjsip_endpoint *endpt;
- pjsip_from_hdr *from;
- pjsip_to_hdr *to;
- pjsip_cid_hdr *call_id;
- pjsip_cseq_hdr *cseq;
-};
-
-static int module_id;
-static pjsip_on_new_msg_cb incoming_cb;
-static pjsip_method message_method;
-
-
-/*
- * Set global callback to receive incoming message.
- */
-PJ_DEF(pjsip_on_new_msg_cb)
-pjsip_messaging_set_incoming_callback(pjsip_on_new_msg_cb cb)
-{
- pjsip_on_new_msg_cb prev_cb = incoming_cb;
- incoming_cb = cb;
- return prev_cb;
-}
-
-
-/*
- * Create an independent message (ie. not associated with a session).
- */
-PJ_DEF(pjsip_tx_data*)
-pjsip_messaging_create_msg_from_hdr(pjsip_endpoint *endpt,
- const pjsip_uri *target,
- const pjsip_from_hdr *param_from,
- const pjsip_to_hdr *param_to,
- const pjsip_cid_hdr *param_call_id,
- int param_cseq,
- const pj_str_t *param_text)
-{
- return pjsip_endpt_create_request_from_hdr( endpt, &message_method,
- target,
- param_from, param_to,
- NULL, param_call_id,
- param_cseq, param_text );
-}
-
-/*
- * Create independent message from string (instead of from header).
- */
-PJ_DEF(pjsip_tx_data*)
-pjsip_messaging_create_msg( pjsip_endpoint *endpt,
- const pj_str_t *target,
- const pj_str_t *param_from,
- const pj_str_t *param_to,
- const pj_str_t *param_call_id,
- int param_cseq,
- const pj_str_t *param_text)
-{
- return pjsip_endpt_create_request( endpt, &message_method, target,
- param_from, param_to, NULL, param_call_id,
- param_cseq, param_text);
-}
-
-/*
- * Initiate transaction to send outgoing message.
- */
-PJ_DEF(pj_status_t)
-pjsip_messaging_send_msg( pjsip_endpoint *endpt, pjsip_tx_data *tdata,
- void *token, pjsip_messaging_cb cb )
-{
- pjsip_transaction *tsx;
- struct messaging_data *msg_data;
-
- /* Create transaction. */
- tsx = pjsip_endpt_create_tsx(endpt);
- if (!tsx) {
- pjsip_tx_data_dec_ref(tdata);
- return -1;
- }
-
- /* Save parameters to messaging data and attach to tsx. */
- msg_data = pj_pool_calloc(tsx->pool, 1, sizeof(struct messaging_data));
- msg_data->cb = cb;
- msg_data->token = token;
-
- /* Init transaction. */
- tsx->module_data[module_id] = msg_data;
- if (pjsip_tsx_init_uac(tsx, tdata) != 0) {
- pjsip_tx_data_dec_ref(tdata);
- pjsip_endpt_destroy_tsx(endpt, tsx);
- return -1;
- }
-
- pjsip_endpt_register_tsx(endpt, tsx);
-
- /*
- * Instruct transaction to send message.
- * Further events will be received via transaction's event.
- */
- pjsip_tsx_on_tx_msg(tsx, tdata);
-
- /* Decrement reference counter. */
- pjsip_tx_data_dec_ref(tdata);
- return 0;
-}
-
-
-/*
- * Create 'IM session'.
- */
-PJ_DEF(pjsip_messaging_session*)
-pjsip_messaging_create_session( pjsip_endpoint *endpt, const pj_str_t *param_from,
- const pj_str_t *param_to )
-{
- pj_pool_t *pool;
- pjsip_messaging_session *ses;
- pj_str_t tmp, to;
-
- pool = pjsip_endpt_create_pool(endpt, "imsess", 1024, 1024);
- if (!pool)
- return NULL;
-
- ses = pj_pool_calloc(pool, 1, sizeof(pjsip_messaging_session));
- ses->pool = pool;
- ses->endpt = endpt;
-
- ses->call_id = pjsip_cid_hdr_create(pool);
- pj_create_unique_string(pool, &ses->call_id->id);
-
- ses->cseq = pjsip_cseq_hdr_create(pool);
- ses->cseq->cseq = pj_rand();
- ses->cseq->method = message_method;
-
- ses->from = pjsip_from_hdr_create(pool);
- pj_strdup_with_null(pool, &tmp, param_from);
- ses->from->uri = pjsip_parse_uri(pool, tmp.ptr, tmp.slen, PJSIP_PARSE_URI_AS_NAMEADDR);
- if (ses->from->uri == NULL) {
- pjsip_endpt_destroy_pool(endpt, pool);
- return NULL;
- }
- pj_create_unique_string(pool, &ses->from->tag);
-
- ses->to = pjsip_to_hdr_create(pool);
- pj_strdup_with_null(pool, &to, param_from);
- ses->to->uri = pjsip_parse_uri(pool, to.ptr, to.slen, PJSIP_PARSE_URI_AS_NAMEADDR);
- if (ses->to->uri == NULL) {
- pjsip_endpt_destroy_pool(endpt, pool);
- return NULL;
- }
-
- PJ_LOG(4,(THIS_FILE, "IM session created: recipient=%s", to.ptr));
- return ses;
-}
-
-
-/*
- * Send IM message using identification from 'IM session'.
- */
-PJ_DEF(pjsip_tx_data*)
-pjsip_messaging_session_create_msg( pjsip_messaging_session *ses, const pj_str_t *text )
-{
- return pjsip_endpt_create_request_from_hdr( ses->endpt,
- &message_method,
- ses->to->uri,
- ses->from,
- ses->to,
- NULL,
- ses->call_id,
- ses->cseq->cseq++,
- text);
-}
-
-
-/*
- * Destroy 'IM session'.
- */
-PJ_DEF(pj_status_t)
-pjsip_messaging_destroy_session( pjsip_messaging_session *ses )
-{
- /*
- * NOTE ABOUT POSSIBLE BUG HERE...
- *
- * We don't check number of pending transaction before destroying IM
- * session. As the result, the headers in the txdata of pending transaction
- * wil be INVALID once the IM session is deleted (because we only
- * shallo_clone()-ed them).
- *
- * This normally should be okay, because once the message is
- * submitted to transaction, the transaction (or rather the transport)
- * will 'print' the message to a buffer, and once it is printed, it
- * won't try to access the original message again. So even when the
- * original message has a dangling pointer, we should be safe.
- *
- * However, it will cause a problem if:
- * - resolving completes asynchronously and with a substantial delay,
- * and before the resolver/transport finished its job the user
- * destroy the IM session.
- * - if the transmit data is invalidated after the IM session is
- * destroyed.
- */
-
- pjsip_endpt_destroy_pool(ses->endpt, ses->pool);
- return 0;
-}
-
-
-static pj_status_t messaging_init( pjsip_endpoint *endpt,
- struct pjsip_module *mod, pj_uint32_t id )
-{
- PJ_UNUSED_ARG(endpt)
- PJ_UNUSED_ARG(mod)
-
- module_id = id;
- return 0;
-}
-
-static pj_status_t messaging_start( struct pjsip_module *mod )
-{
- PJ_UNUSED_ARG(mod)
- return 0;
-}
-
-static pj_status_t messaging_deinit( struct pjsip_module *mod )
-{
- PJ_UNUSED_ARG(mod)
- return 0;
-}
-
-static void messaging_tsx_handler( struct pjsip_module *mod, pjsip_event *event )
-{
- pjsip_transaction *tsx = event->obj.tsx;
- struct messaging_data *mod_data;
-
- PJ_UNUSED_ARG(mod)
-
- /* Ignore non transaction event */
- if (event->type != PJSIP_EVENT_TSX_STATE_CHANGED || tsx == NULL)
- return;
-
- /* If this is an incoming message, inform application. */
- if (tsx->role == PJSIP_ROLE_UAS) {
- int status = 100;
- pjsip_tx_data *tdata;
-
- /* Check if we already answered this request. */
- if (tsx->status_code >= 200)
- return;
-
- /* Only handle MESSAGE requests!. */
- if (pjsip_method_cmp(&tsx->method, &message_method) != 0)
- return;
-
- /* Call application callback. */
- if (incoming_cb)
- status = (*incoming_cb)(event->src.rdata);
-
- if (status < 200 || status >= 700)
- status = PJSIP_SC_INTERNAL_SERVER_ERROR;
-
- /* Respond request. */
- tdata = pjsip_endpt_create_response(tsx->endpt, event->src.rdata, status );
- if (tdata)
- pjsip_tsx_on_tx_msg(tsx, tdata);
-
- return;
- }
-
- /* Ignore if it's not something that came from messaging module. */
- mod_data = tsx->module_data[ module_id ];
- if (mod_data == NULL)
- return;
-
- /* Ignore non final response. */
- if (tsx->status_code < 200)
- return;
-
- /* Don't want to call the callback more than once. */
- tsx->module_data[ module_id ] = NULL;
-
- /* Now call the callback. */
- if (mod_data->cb) {
- (*mod_data->cb)(mod_data->token, tsx->status_code);
- }
-}
-
-static pjsip_module messaging_module =
-{
- { "Messaging", 9}, /* Name. */
- 0, /* Flag */
- 128, /* Priority */
- NULL, /* User agent instance, initialized by APP. */
- 0, /* Number of methods supported (will be initialized later). */
- { 0 }, /* Array of methods (will be initialized later) */
- &messaging_init, /* init_module() */
- &messaging_start, /* start_module() */
- &messaging_deinit, /* deinit_module() */
- &messaging_tsx_handler, /* tsx_handler() */
-};
-
-PJ_DEF(pjsip_module*) pjsip_messaging_get_module()
-{
- static pj_str_t method_str = { "MESSAGE", 7 };
-
- pjsip_method_init_np( &message_method, &method_str);
-
- messaging_module.method_cnt = 1;
- messaging_module.methods[0] = &message_method;
-
- return &messaging_module;
-}
-
+/* $Id$ */
+/*
+ * Copyright (C) 2003-2006 Benny Prijono <benny@prijono.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#include <pjsip_simple/messaging.h>
+#include <pjsip/sip_endpoint.h>
+#include <pjsip/sip_parser.h>
+#include <pjsip/sip_transaction.h>
+#include <pjsip/sip_event.h>
+#include <pjsip/sip_module.h>
+#include <pjsip/sip_util.h>
+#include <pj/string.h>
+#include <pj/pool.h>
+#include <pj/guid.h>
+#include <pj/string.h>
+#include <pj/log.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#define THIS_FILE "messaging"
+
+struct messaging_data
+{
+ void *token;
+ pjsip_messaging_cb cb;
+};
+
+struct pjsip_messaging_session
+{
+ pj_pool_t *pool;
+ pjsip_endpoint *endpt;
+ pjsip_from_hdr *from;
+ pjsip_to_hdr *to;
+ pjsip_cid_hdr *call_id;
+ pjsip_cseq_hdr *cseq;
+};
+
+static int module_id;
+static pjsip_on_new_msg_cb incoming_cb;
+static pjsip_method message_method;
+
+
+/*
+ * Set global callback to receive incoming message.
+ */
+PJ_DEF(pjsip_on_new_msg_cb)
+pjsip_messaging_set_incoming_callback(pjsip_on_new_msg_cb cb)
+{
+ pjsip_on_new_msg_cb prev_cb = incoming_cb;
+ incoming_cb = cb;
+ return prev_cb;
+}
+
+
+/*
+ * Create an independent message (ie. not associated with a session).
+ */
+PJ_DEF(pjsip_tx_data*)
+pjsip_messaging_create_msg_from_hdr(pjsip_endpoint *endpt,
+ const pjsip_uri *target,
+ const pjsip_from_hdr *param_from,
+ const pjsip_to_hdr *param_to,
+ const pjsip_cid_hdr *param_call_id,
+ int param_cseq,
+ const pj_str_t *param_text)
+{
+ return pjsip_endpt_create_request_from_hdr( endpt, &message_method,
+ target,
+ param_from, param_to,
+ NULL, param_call_id,
+ param_cseq, param_text );
+}
+
+/*
+ * Create independent message from string (instead of from header).
+ */
+PJ_DEF(pjsip_tx_data*)
+pjsip_messaging_create_msg( pjsip_endpoint *endpt,
+ const pj_str_t *target,
+ const pj_str_t *param_from,
+ const pj_str_t *param_to,
+ const pj_str_t *param_call_id,
+ int param_cseq,
+ const pj_str_t *param_text)
+{
+ return pjsip_endpt_create_request( endpt, &message_method, target,
+ param_from, param_to, NULL, param_call_id,
+ param_cseq, param_text);
+}
+
+/*
+ * Initiate transaction to send outgoing message.
+ */
+PJ_DEF(pj_status_t)
+pjsip_messaging_send_msg( pjsip_endpoint *endpt, pjsip_tx_data *tdata,
+ void *token, pjsip_messaging_cb cb )
+{
+ pjsip_transaction *tsx;
+ struct messaging_data *msg_data;
+
+ /* Create transaction. */
+ tsx = pjsip_endpt_create_tsx(endpt);
+ if (!tsx) {
+ pjsip_tx_data_dec_ref(tdata);
+ return -1;
+ }
+
+ /* Save parameters to messaging data and attach to tsx. */
+ msg_data = pj_pool_calloc(tsx->pool, 1, sizeof(struct messaging_data));
+ msg_data->cb = cb;
+ msg_data->token = token;
+
+ /* Init transaction. */
+ tsx->module_data[module_id] = msg_data;
+ if (pjsip_tsx_init_uac(tsx, tdata) != 0) {
+ pjsip_tx_data_dec_ref(tdata);
+ pjsip_endpt_destroy_tsx(endpt, tsx);
+ return -1;
+ }
+
+ pjsip_endpt_register_tsx(endpt, tsx);
+
+ /*
+ * Instruct transaction to send message.
+ * Further events will be received via transaction's event.
+ */
+ pjsip_tsx_on_tx_msg(tsx, tdata);
+
+ /* Decrement reference counter. */
+ pjsip_tx_data_dec_ref(tdata);
+ return 0;
+}
+
+
+/*
+ * Create 'IM session'.
+ */
+PJ_DEF(pjsip_messaging_session*)
+pjsip_messaging_create_session( pjsip_endpoint *endpt, const pj_str_t *param_from,
+ const pj_str_t *param_to )
+{
+ pj_pool_t *pool;
+ pjsip_messaging_session *ses;
+ pj_str_t tmp, to;
+
+ pool = pjsip_endpt_create_pool(endpt, "imsess", 1024, 1024);
+ if (!pool)
+ return NULL;
+
+ ses = pj_pool_calloc(pool, 1, sizeof(pjsip_messaging_session));
+ ses->pool = pool;
+ ses->endpt = endpt;
+
+ ses->call_id = pjsip_cid_hdr_create(pool);
+ pj_create_unique_string(pool, &ses->call_id->id);
+
+ ses->cseq = pjsip_cseq_hdr_create(pool);
+ ses->cseq->cseq = pj_rand();
+ ses->cseq->method = message_method;
+
+ ses->from = pjsip_from_hdr_create(pool);
+ pj_strdup_with_null(pool, &tmp, param_from);
+ ses->from->uri = pjsip_parse_uri(pool, tmp.ptr, tmp.slen, PJSIP_PARSE_URI_AS_NAMEADDR);
+ if (ses->from->uri == NULL) {
+ pjsip_endpt_destroy_pool(endpt, pool);
+ return NULL;
+ }
+ pj_create_unique_string(pool, &ses->from->tag);
+
+ ses->to = pjsip_to_hdr_create(pool);
+ pj_strdup_with_null(pool, &to, param_from);
+ ses->to->uri = pjsip_parse_uri(pool, to.ptr, to.slen, PJSIP_PARSE_URI_AS_NAMEADDR);
+ if (ses->to->uri == NULL) {
+ pjsip_endpt_destroy_pool(endpt, pool);
+ return NULL;
+ }
+
+ PJ_LOG(4,(THIS_FILE, "IM session created: recipient=%s", to.ptr));
+ return ses;
+}
+
+
+/*
+ * Send IM message using identification from 'IM session'.
+ */
+PJ_DEF(pjsip_tx_data*)
+pjsip_messaging_session_create_msg( pjsip_messaging_session *ses, const pj_str_t *text )
+{
+ return pjsip_endpt_create_request_from_hdr( ses->endpt,
+ &message_method,
+ ses->to->uri,
+ ses->from,
+ ses->to,
+ NULL,
+ ses->call_id,
+ ses->cseq->cseq++,
+ text);
+}
+
+
+/*
+ * Destroy 'IM session'.
+ */
+PJ_DEF(pj_status_t)
+pjsip_messaging_destroy_session( pjsip_messaging_session *ses )
+{
+ /*
+ * NOTE ABOUT POSSIBLE BUG HERE...
+ *
+ * We don't check number of pending transaction before destroying IM
+ * session. As the result, the headers in the txdata of pending transaction
+ * wil be INVALID once the IM session is deleted (because we only
+ * shallo_clone()-ed them).
+ *
+ * This normally should be okay, because once the message is
+ * submitted to transaction, the transaction (or rather the transport)
+ * will 'print' the message to a buffer, and once it is printed, it
+ * won't try to access the original message again. So even when the
+ * original message has a dangling pointer, we should be safe.
+ *
+ * However, it will cause a problem if:
+ * - resolving completes asynchronously and with a substantial delay,
+ * and before the resolver/transport finished its job the user
+ * destroy the IM session.
+ * - if the transmit data is invalidated after the IM session is
+ * destroyed.
+ */
+
+ pjsip_endpt_destroy_pool(ses->endpt, ses->pool);
+ return 0;
+}
+
+
+static pj_status_t messaging_init( pjsip_endpoint *endpt,
+ struct pjsip_module *mod, pj_uint32_t id )
+{
+ PJ_UNUSED_ARG(endpt)
+ PJ_UNUSED_ARG(mod)
+
+ module_id = id;
+ return 0;
+}
+
+static pj_status_t messaging_start( struct pjsip_module *mod )
+{
+ PJ_UNUSED_ARG(mod)
+ return 0;
+}
+
+static pj_status_t messaging_deinit( struct pjsip_module *mod )
+{
+ PJ_UNUSED_ARG(mod)
+ return 0;
+}
+
+static void messaging_tsx_handler( struct pjsip_module *mod, pjsip_event *event )
+{
+ pjsip_transaction *tsx = event->obj.tsx;
+ struct messaging_data *mod_data;
+
+ PJ_UNUSED_ARG(mod)
+
+ /* Ignore non transaction event */
+ if (event->type != PJSIP_EVENT_TSX_STATE_CHANGED || tsx == NULL)
+ return;
+
+ /* If this is an incoming message, inform application. */
+ if (tsx->role == PJSIP_ROLE_UAS) {
+ int status = 100;
+ pjsip_tx_data *tdata;
+
+ /* Check if we already answered this request. */
+ if (tsx->status_code >= 200)
+ return;
+
+ /* Only handle MESSAGE requests!. */
+ if (pjsip_method_cmp(&tsx->method, &message_method) != 0)
+ return;
+
+ /* Call application callback. */
+ if (incoming_cb)
+ status = (*incoming_cb)(event->src.rdata);
+
+ if (status < 200 || status >= 700)
+ status = PJSIP_SC_INTERNAL_SERVER_ERROR;
+
+ /* Respond request. */
+ tdata = pjsip_endpt_create_response(tsx->endpt, event->src.rdata, status );
+ if (tdata)
+ pjsip_tsx_on_tx_msg(tsx, tdata);
+
+ return;
+ }
+
+ /* Ignore if it's not something that came from messaging module. */
+ mod_data = tsx->module_data[ module_id ];
+ if (mod_data == NULL)
+ return;
+
+ /* Ignore non final response. */
+ if (tsx->status_code < 200)
+ return;
+
+ /* Don't want to call the callback more than once. */
+ tsx->module_data[ module_id ] = NULL;
+
+ /* Now call the callback. */
+ if (mod_data->cb) {
+ (*mod_data->cb)(mod_data->token, tsx->status_code);
+ }
+}
+
+static pjsip_module messaging_module =
+{
+ { "Messaging", 9}, /* Name. */
+ 0, /* Flag */
+ 128, /* Priority */
+ NULL, /* User agent instance, initialized by APP. */
+ 0, /* Number of methods supported (will be initialized later). */
+ { 0 }, /* Array of methods (will be initialized later) */
+ &messaging_init, /* init_module() */
+ &messaging_start, /* start_module() */
+ &messaging_deinit, /* deinit_module() */
+ &messaging_tsx_handler, /* tsx_handler() */
+};
+
+PJ_DEF(pjsip_module*) pjsip_messaging_get_module()
+{
+ static pj_str_t method_str = { "MESSAGE", 7 };
+
+ pjsip_method_init_np( &message_method, &method_str);
+
+ messaging_module.method_cnt = 1;
+ messaging_module.methods[0] = &message_method;
+
+ return &messaging_module;
+}
+
diff --git a/pjsip/src/pjsip-simple/pidf.c b/pjsip/src/pjsip-simple/pidf.c
index 8e4a7c38..7ababcad 100644
--- a/pjsip/src/pjsip-simple/pidf.c
+++ b/pjsip/src/pjsip-simple/pidf.c
@@ -1,350 +1,350 @@
-/* $Id$ */
-/*
- * Copyright (C) 2003-2006 Benny Prijono <benny@prijono.org>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
-#include <pjsip_simple/pidf.h>
-#include <pj/string.h>
-#include <pj/pool.h>
-
-struct pjpidf_op_desc pjpidf_op =
-{
- {
- &pjpidf_pres_construct,
- &pjpidf_pres_add_tuple,
- &pjpidf_pres_get_first_tuple,
- &pjpidf_pres_get_next_tuple,
- &pjpidf_pres_find_tuple,
- &pjpidf_pres_remove_tuple,
- &pjpidf_pres_add_note,
- &pjpidf_pres_get_first_note,
- &pjpidf_pres_get_next_note
- },
- {
- &pjpidf_tuple_construct,
- &pjpidf_tuple_get_id,
- &pjpidf_tuple_set_id,
- &pjpidf_tuple_get_status,
- &pjpidf_tuple_get_contact,
- &pjpidf_tuple_set_contact,
- &pjpidf_tuple_set_contact_prio,
- &pjpidf_tuple_get_contact_prio,
- &pjpidf_tuple_add_note,
- &pjpidf_tuple_get_first_note,
- &pjpidf_tuple_get_next_note,
- &pjpidf_tuple_get_timestamp,
- &pjpidf_tuple_set_timestamp,
- &pjpidf_tuple_set_timestamp_np
- },
- {
- &pjpidf_status_construct,
- &pjpidf_status_is_basic_open,
- &pjpidf_status_set_basic_open
- }
-};
-
-static pj_str_t PRESENCE = { "presence", 8 };
-static pj_str_t ENTITY = { "entity", 6};
-static pj_str_t TUPLE = { "tuple", 5 };
-static pj_str_t ID = { "id", 2 };
-static pj_str_t NOTE = { "note", 4 };
-static pj_str_t STATUS = { "status", 6 };
-static pj_str_t CONTACT = { "contact", 7 };
-static pj_str_t PRIORITY = { "priority", 8 };
-static pj_str_t TIMESTAMP = { "timestamp", 9 };
-static pj_str_t BASIC = { "basic", 5 };
-static pj_str_t OPEN = { "open", 4 };
-static pj_str_t CLOSED = { "closed", 6 };
-static pj_str_t EMPTY_STRING = { NULL, 0 };
-
-static void xml_init_node(pj_pool_t *pool, pj_xml_node *node,
- pj_str_t *name, const pj_str_t *value)
-{
- pj_list_init(&node->attr_head);
- pj_list_init(&node->node_head);
- node->name = *name;
- if (value) pj_strdup(pool, &node->content, value);
- else node->content.ptr=NULL, node->content.slen=0;
-}
-
-static pj_xml_attr* xml_create_attr(pj_pool_t *pool, pj_str_t *name,
- const pj_str_t *value)
-{
- pj_xml_attr *attr = pj_pool_alloc(pool, sizeof(*attr));
- attr->name = *name;
- pj_strdup(pool, &attr->value, value);
- return attr;
-}
-
-/* Presence */
-PJ_DEF(void) pjpidf_pres_construct(pj_pool_t *pool, pjpidf_pres *pres,
- const pj_str_t *entity)
-{
- pj_xml_attr *attr;
-
- xml_init_node(pool, pres, &PRESENCE, NULL);
- attr = xml_create_attr(pool, &ENTITY, entity);
- pj_xml_add_attr(pres, attr);
-}
-
-PJ_DEF(pjpidf_tuple*) pjpidf_pres_add_tuple(pj_pool_t *pool, pjpidf_pres *pres,
- const pj_str_t *id)
-{
- pjpidf_tuple *t = pj_pool_alloc(pool, sizeof(*t));
- pjpidf_tuple_construct(pool, t, id);
- pj_xml_add_node(pres, t);
- return t;
-}
-
-PJ_DEF(pjpidf_tuple*) pjpidf_pres_get_first_tuple(pjpidf_pres *pres)
-{
- return pj_xml_find_node(pres, &TUPLE);
-}
-
-PJ_DEF(pjpidf_tuple*) pjpidf_pres_get_next_tuple(pjpidf_pres *pres,
- pjpidf_tuple *tuple)
-{
- return pj_xml_find_next_node(pres, tuple, &TUPLE);
-}
-
-static pj_bool_t find_tuple_by_id(pj_xml_node *node, const void *id)
-{
- return pj_xml_find_attr(node, &ID, id) != NULL;
-}
-
-PJ_DEF(pjpidf_tuple*) pjpidf_pres_find_tuple(pjpidf_pres *pres, const pj_str_t *id)
-{
- return pj_xml_find(pres, &TUPLE, id, &find_tuple_by_id);
-}
-
-PJ_DEF(void) pjpidf_pres_remove_tuple(pjpidf_pres *pres, pjpidf_tuple *t)
-{
- PJ_UNUSED_ARG(pres)
- pj_list_erase(t);
-}
-
-PJ_DEF(pjpidf_note*) pjpidf_pres_add_note(pj_pool_t *pool, pjpidf_pres *pres,
- const pj_str_t *text)
-{
- pjpidf_note *note = pj_pool_alloc(pool, sizeof(*note));
- xml_init_node(pool, note, &NOTE, text);
- pj_xml_add_node(pres, note);
- return note;
-}
-
-PJ_DEF(pjpidf_note*) pjpidf_pres_get_first_note(pjpidf_pres *pres)
-{
- return pj_xml_find_node( pres, &NOTE);
-}
-
-PJ_DEF(pjpidf_note*) pjpidf_pres_get_next_note(pjpidf_pres *t, pjpidf_note *note)
-{
- return pj_xml_find_next_node(t, note, &NOTE);
-}
-
-
-/* Tuple */
-PJ_DEF(void) pjpidf_tuple_construct(pj_pool_t *pool, pjpidf_tuple *t,
- const pj_str_t *id)
-{
- pj_xml_attr *attr;
- pjpidf_status *st;
-
- xml_init_node(pool, t, &TUPLE, NULL);
- attr = xml_create_attr(pool, &ID, id);
- pj_xml_add_attr(t, attr);
- st = pj_pool_alloc(pool, sizeof(*st));
- pjpidf_status_construct(pool, st);
- pj_xml_add_node(t, st);
-}
-
-PJ_DEF(const pj_str_t*) pjpidf_tuple_get_id(const pjpidf_tuple *t)
-{
- const pj_xml_attr *attr = pj_xml_find_attr((pj_xml_node*)t, &ID, NULL);
- pj_assert(attr);
- return &attr->value;
-}
-
-PJ_DEF(void) pjpidf_tuple_set_id(pj_pool_t *pool, pjpidf_tuple *t, const pj_str_t *id)
-{
- pj_xml_attr *attr = pj_xml_find_attr(t, &ID, NULL);
- pj_assert(attr);
- pj_strdup(pool, &attr->value, id);
-}
-
-
-PJ_DEF(pjpidf_status*) pjpidf_tuple_get_status(pjpidf_tuple *t)
-{
- pjpidf_status *st = (pjpidf_status*)pj_xml_find_node(t, &STATUS);
- pj_assert(st);
- return st;
-}
-
-
-PJ_DEF(const pj_str_t*) pjpidf_tuple_get_contact(const pjpidf_tuple *t)
-{
- pj_xml_node *node = pj_xml_find_node((pj_xml_node*)t, &CONTACT);
- if (!node)
- return &EMPTY_STRING;
- return &node->content;
-}
-
-PJ_DEF(void) pjpidf_tuple_set_contact(pj_pool_t *pool, pjpidf_tuple *t,
- const pj_str_t *contact)
-{
- pj_xml_node *node = pj_xml_find_node(t, &CONTACT);
- if (!node) {
- node = pj_pool_alloc(pool, sizeof(*node));
- xml_init_node(pool, node, &CONTACT, contact);
- pj_xml_add_node(t, node);
- } else {
- pj_strdup(pool, &node->content, contact);
- }
-}
-
-PJ_DEF(void) pjpidf_tuple_set_contact_prio(pj_pool_t *pool, pjpidf_tuple *t,
- const pj_str_t *prio)
-{
- pj_xml_node *node = pj_xml_find_node(t, &CONTACT);
- pj_xml_attr *attr;
-
- if (!node) {
- node = pj_pool_alloc(pool, sizeof(*node));
- xml_init_node(pool, node, &CONTACT, NULL);
- pj_xml_add_node(t, node);
- }
- attr = pj_xml_find_attr(node, &PRIORITY, NULL);
- if (!attr) {
- attr = xml_create_attr(pool, &PRIORITY, prio);
- pj_xml_add_attr(node, attr);
- } else {
- pj_strdup(pool, &attr->value, prio);
- }
-}
-
-PJ_DEF(const pj_str_t*) pjpidf_tuple_get_contact_prio(const pjpidf_tuple *t)
-{
- pj_xml_node *node = pj_xml_find_node((pj_xml_node*)t, &CONTACT);
- pj_xml_attr *attr;
-
- if (!node)
- return &EMPTY_STRING;
- attr = pj_xml_find_attr(node, &PRIORITY, NULL);
- if (!attr)
- return &EMPTY_STRING;
- return &attr->value;
-}
-
-
-PJ_DEF(pjpidf_note*) pjpidf_tuple_add_note(pj_pool_t *pool, pjpidf_tuple *t,
- const pj_str_t *text)
-{
- pjpidf_note *note = pj_pool_alloc(pool, sizeof(*note));
- xml_init_node(pool, note, &NOTE, text);
- pj_xml_add_node(t, note);
- return note;
-}
-
-PJ_DEF(pjpidf_note*) pjpidf_tuple_get_first_note(pjpidf_tuple *t)
-{
- return pj_xml_find_node(t, &NOTE);
-}
-
-PJ_DEF(pjpidf_note*) pjpidf_tuple_get_next_note(pjpidf_tuple *t, pjpidf_note *n)
-{
- return pj_xml_find_next_node(t, n, &NOTE);
-}
-
-
-PJ_DEF(const pj_str_t*) pjpidf_tuple_get_timestamp(const pjpidf_tuple *t)
-{
- pj_xml_node *node = pj_xml_find_node((pj_xml_node*)t, &TIMESTAMP);
- return node ? &node->content : &EMPTY_STRING;
-}
-
-PJ_DEF(void) pjpidf_tuple_set_timestamp(pj_pool_t *pool, pjpidf_tuple *t,
- const pj_str_t *ts)
-{
- pj_xml_node *node = pj_xml_find_node(t, &TIMESTAMP);
- if (!node) {
- node = pj_pool_alloc(pool, sizeof(*node));
- xml_init_node(pool, node, &TIMESTAMP, ts);
- } else {
- pj_strdup(pool, &node->content, ts);
- }
-}
-
-
-PJ_DEF(void) pjpidf_tuple_set_timestamp_np(pj_pool_t *pool, pjpidf_tuple *t,
- pj_str_t *ts)
-{
- pj_xml_node *node = pj_xml_find_node(t, &TIMESTAMP);
- if (!node) {
- node = pj_pool_alloc(pool, sizeof(*node));
- xml_init_node(pool, node, &TIMESTAMP, ts);
- } else {
- node->content = *ts;
- }
-}
-
-
-/* Status */
-PJ_DEF(void) pjpidf_status_construct(pj_pool_t *pool, pjpidf_status *st)
-{
- pj_xml_node *node;
-
- xml_init_node(pool, st, &STATUS, NULL);
- node = pj_pool_alloc(pool, sizeof(*node));
- xml_init_node(pool, node, &BASIC, &CLOSED);
- pj_xml_add_node(st, node);
-}
-
-PJ_DEF(pj_bool_t) pjpidf_status_is_basic_open(const pjpidf_status *st)
-{
- pj_xml_node *node = pj_xml_find_node((pj_xml_node*)st, &BASIC);
- pj_assert(node != NULL);
- return pj_stricmp(&node->content, &OPEN)==0;
-}
-
-PJ_DEF(void) pjpidf_status_set_basic_open(pjpidf_status *st, pj_bool_t open)
-{
- pj_xml_node *node = pj_xml_find_node(st, &BASIC);
- pj_assert(node != NULL);
- node->content = open ? OPEN : CLOSED;
-}
-
-PJ_DEF(pjpidf_pres*) pjpidf_create(pj_pool_t *pool, const pj_str_t *entity)
-{
- pjpidf_pres *pres = pj_pool_alloc(pool, sizeof(*pres));
- pjpidf_pres_construct(pool, pres, entity);
- return pres;
-}
-
-PJ_DEF(pjpidf_pres*) pjpidf_parse(pj_pool_t *pool, char *text, int len)
-{
- pjpidf_pres *pres = pj_xml_parse(pool, text, len);
- if (pres) {
- if (pj_stricmp(&pres->name, &PRESENCE) != 0)
- return NULL;
- }
- return pres;
-}
-
-PJ_DEF(int) pjpidf_print(const pjpidf_pres* pres, char *buf, int len)
-{
- return pj_xml_print(pres, buf, len, PJ_TRUE);
-}
-
+/* $Id$ */
+/*
+ * Copyright (C) 2003-2006 Benny Prijono <benny@prijono.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#include <pjsip_simple/pidf.h>
+#include <pj/string.h>
+#include <pj/pool.h>
+
+struct pjpidf_op_desc pjpidf_op =
+{
+ {
+ &pjpidf_pres_construct,
+ &pjpidf_pres_add_tuple,
+ &pjpidf_pres_get_first_tuple,
+ &pjpidf_pres_get_next_tuple,
+ &pjpidf_pres_find_tuple,
+ &pjpidf_pres_remove_tuple,
+ &pjpidf_pres_add_note,
+ &pjpidf_pres_get_first_note,
+ &pjpidf_pres_get_next_note
+ },
+ {
+ &pjpidf_tuple_construct,
+ &pjpidf_tuple_get_id,
+ &pjpidf_tuple_set_id,
+ &pjpidf_tuple_get_status,
+ &pjpidf_tuple_get_contact,
+ &pjpidf_tuple_set_contact,
+ &pjpidf_tuple_set_contact_prio,
+ &pjpidf_tuple_get_contact_prio,
+ &pjpidf_tuple_add_note,
+ &pjpidf_tuple_get_first_note,
+ &pjpidf_tuple_get_next_note,
+ &pjpidf_tuple_get_timestamp,
+ &pjpidf_tuple_set_timestamp,
+ &pjpidf_tuple_set_timestamp_np
+ },
+ {
+ &pjpidf_status_construct,
+ &pjpidf_status_is_basic_open,
+ &pjpidf_status_set_basic_open
+ }
+};
+
+static pj_str_t PRESENCE = { "presence", 8 };
+static pj_str_t ENTITY = { "entity", 6};
+static pj_str_t TUPLE = { "tuple", 5 };
+static pj_str_t ID = { "id", 2 };
+static pj_str_t NOTE = { "note", 4 };
+static pj_str_t STATUS = { "status", 6 };
+static pj_str_t CONTACT = { "contact", 7 };
+static pj_str_t PRIORITY = { "priority", 8 };
+static pj_str_t TIMESTAMP = { "timestamp", 9 };
+static pj_str_t BASIC = { "basic", 5 };
+static pj_str_t OPEN = { "open", 4 };
+static pj_str_t CLOSED = { "closed", 6 };
+static pj_str_t EMPTY_STRING = { NULL, 0 };
+
+static void xml_init_node(pj_pool_t *pool, pj_xml_node *node,
+ pj_str_t *name, const pj_str_t *value)
+{
+ pj_list_init(&node->attr_head);
+ pj_list_init(&node->node_head);
+ node->name = *name;
+ if (value) pj_strdup(pool, &node->content, value);
+ else node->content.ptr=NULL, node->content.slen=0;
+}
+
+static pj_xml_attr* xml_create_attr(pj_pool_t *pool, pj_str_t *name,
+ const pj_str_t *value)
+{
+ pj_xml_attr *attr = pj_pool_alloc(pool, sizeof(*attr));
+ attr->name = *name;
+ pj_strdup(pool, &attr->value, value);
+ return attr;
+}
+
+/* Presence */
+PJ_DEF(void) pjpidf_pres_construct(pj_pool_t *pool, pjpidf_pres *pres,
+ const pj_str_t *entity)
+{
+ pj_xml_attr *attr;
+
+ xml_init_node(pool, pres, &PRESENCE, NULL);
+ attr = xml_create_attr(pool, &ENTITY, entity);
+ pj_xml_add_attr(pres, attr);
+}
+
+PJ_DEF(pjpidf_tuple*) pjpidf_pres_add_tuple(pj_pool_t *pool, pjpidf_pres *pres,
+ const pj_str_t *id)
+{
+ pjpidf_tuple *t = pj_pool_alloc(pool, sizeof(*t));
+ pjpidf_tuple_construct(pool, t, id);
+ pj_xml_add_node(pres, t);
+ return t;
+}
+
+PJ_DEF(pjpidf_tuple*) pjpidf_pres_get_first_tuple(pjpidf_pres *pres)
+{
+ return pj_xml_find_node(pres, &TUPLE);
+}
+
+PJ_DEF(pjpidf_tuple*) pjpidf_pres_get_next_tuple(pjpidf_pres *pres,
+ pjpidf_tuple *tuple)
+{
+ return pj_xml_find_next_node(pres, tuple, &TUPLE);
+}
+
+static pj_bool_t find_tuple_by_id(pj_xml_node *node, const void *id)
+{
+ return pj_xml_find_attr(node, &ID, id) != NULL;
+}
+
+PJ_DEF(pjpidf_tuple*) pjpidf_pres_find_tuple(pjpidf_pres *pres, const pj_str_t *id)
+{
+ return pj_xml_find(pres, &TUPLE, id, &find_tuple_by_id);
+}
+
+PJ_DEF(void) pjpidf_pres_remove_tuple(pjpidf_pres *pres, pjpidf_tuple *t)
+{
+ PJ_UNUSED_ARG(pres)
+ pj_list_erase(t);
+}
+
+PJ_DEF(pjpidf_note*) pjpidf_pres_add_note(pj_pool_t *pool, pjpidf_pres *pres,
+ const pj_str_t *text)
+{
+ pjpidf_note *note = pj_pool_alloc(pool, sizeof(*note));
+ xml_init_node(pool, note, &NOTE, text);
+ pj_xml_add_node(pres, note);
+ return note;
+}
+
+PJ_DEF(pjpidf_note*) pjpidf_pres_get_first_note(pjpidf_pres *pres)
+{
+ return pj_xml_find_node( pres, &NOTE);
+}
+
+PJ_DEF(pjpidf_note*) pjpidf_pres_get_next_note(pjpidf_pres *t, pjpidf_note *note)
+{
+ return pj_xml_find_next_node(t, note, &NOTE);
+}
+
+
+/* Tuple */
+PJ_DEF(void) pjpidf_tuple_construct(pj_pool_t *pool, pjpidf_tuple *t,
+ const pj_str_t *id)
+{
+ pj_xml_attr *attr;
+ pjpidf_status *st;
+
+ xml_init_node(pool, t, &TUPLE, NULL);
+ attr = xml_create_attr(pool, &ID, id);
+ pj_xml_add_attr(t, attr);
+ st = pj_pool_alloc(pool, sizeof(*st));
+ pjpidf_status_construct(pool, st);
+ pj_xml_add_node(t, st);
+}
+
+PJ_DEF(const pj_str_t*) pjpidf_tuple_get_id(const pjpidf_tuple *t)
+{
+ const pj_xml_attr *attr = pj_xml_find_attr((pj_xml_node*)t, &ID, NULL);
+ pj_assert(attr);
+ return &attr->value;
+}
+
+PJ_DEF(void) pjpidf_tuple_set_id(pj_pool_t *pool, pjpidf_tuple *t, const pj_str_t *id)
+{
+ pj_xml_attr *attr = pj_xml_find_attr(t, &ID, NULL);
+ pj_assert(attr);
+ pj_strdup(pool, &attr->value, id);
+}
+
+
+PJ_DEF(pjpidf_status*) pjpidf_tuple_get_status(pjpidf_tuple *t)
+{
+ pjpidf_status *st = (pjpidf_status*)pj_xml_find_node(t, &STATUS);
+ pj_assert(st);
+ return st;
+}
+
+
+PJ_DEF(const pj_str_t*) pjpidf_tuple_get_contact(const pjpidf_tuple *t)
+{
+ pj_xml_node *node = pj_xml_find_node((pj_xml_node*)t, &CONTACT);
+ if (!node)
+ return &EMPTY_STRING;
+ return &node->content;
+}
+
+PJ_DEF(void) pjpidf_tuple_set_contact(pj_pool_t *pool, pjpidf_tuple *t,
+ const pj_str_t *contact)
+{
+ pj_xml_node *node = pj_xml_find_node(t, &CONTACT);
+ if (!node) {
+ node = pj_pool_alloc(pool, sizeof(*node));
+ xml_init_node(pool, node, &CONTACT, contact);
+ pj_xml_add_node(t, node);
+ } else {
+ pj_strdup(pool, &node->content, contact);
+ }
+}
+
+PJ_DEF(void) pjpidf_tuple_set_contact_prio(pj_pool_t *pool, pjpidf_tuple *t,
+ const pj_str_t *prio)
+{
+ pj_xml_node *node = pj_xml_find_node(t, &CONTACT);
+ pj_xml_attr *attr;
+
+ if (!node) {
+ node = pj_pool_alloc(pool, sizeof(*node));
+ xml_init_node(pool, node, &CONTACT, NULL);
+ pj_xml_add_node(t, node);
+ }
+ attr = pj_xml_find_attr(node, &PRIORITY, NULL);
+ if (!attr) {
+ attr = xml_create_attr(pool, &PRIORITY, prio);
+ pj_xml_add_attr(node, attr);
+ } else {
+ pj_strdup(pool, &attr->value, prio);
+ }
+}
+
+PJ_DEF(const pj_str_t*) pjpidf_tuple_get_contact_prio(const pjpidf_tuple *t)
+{
+ pj_xml_node *node = pj_xml_find_node((pj_xml_node*)t, &CONTACT);
+ pj_xml_attr *attr;
+
+ if (!node)
+ return &EMPTY_STRING;
+ attr = pj_xml_find_attr(node, &PRIORITY, NULL);
+ if (!attr)
+ return &EMPTY_STRING;
+ return &attr->value;
+}
+
+
+PJ_DEF(pjpidf_note*) pjpidf_tuple_add_note(pj_pool_t *pool, pjpidf_tuple *t,
+ const pj_str_t *text)
+{
+ pjpidf_note *note = pj_pool_alloc(pool, sizeof(*note));
+ xml_init_node(pool, note, &NOTE, text);
+ pj_xml_add_node(t, note);
+ return note;
+}
+
+PJ_DEF(pjpidf_note*) pjpidf_tuple_get_first_note(pjpidf_tuple *t)
+{
+ return pj_xml_find_node(t, &NOTE);
+}
+
+PJ_DEF(pjpidf_note*) pjpidf_tuple_get_next_note(pjpidf_tuple *t, pjpidf_note *n)
+{
+ return pj_xml_find_next_node(t, n, &NOTE);
+}
+
+
+PJ_DEF(const pj_str_t*) pjpidf_tuple_get_timestamp(const pjpidf_tuple *t)
+{
+ pj_xml_node *node = pj_xml_find_node((pj_xml_node*)t, &TIMESTAMP);
+ return node ? &node->content : &EMPTY_STRING;
+}
+
+PJ_DEF(void) pjpidf_tuple_set_timestamp(pj_pool_t *pool, pjpidf_tuple *t,
+ const pj_str_t *ts)
+{
+ pj_xml_node *node = pj_xml_find_node(t, &TIMESTAMP);
+ if (!node) {
+ node = pj_pool_alloc(pool, sizeof(*node));
+ xml_init_node(pool, node, &TIMESTAMP, ts);
+ } else {
+ pj_strdup(pool, &node->content, ts);
+ }
+}
+
+
+PJ_DEF(void) pjpidf_tuple_set_timestamp_np(pj_pool_t *pool, pjpidf_tuple *t,
+ pj_str_t *ts)
+{
+ pj_xml_node *node = pj_xml_find_node(t, &TIMESTAMP);
+ if (!node) {
+ node = pj_pool_alloc(pool, sizeof(*node));
+ xml_init_node(pool, node, &TIMESTAMP, ts);
+ } else {
+ node->content = *ts;
+ }
+}
+
+
+/* Status */
+PJ_DEF(void) pjpidf_status_construct(pj_pool_t *pool, pjpidf_status *st)
+{
+ pj_xml_node *node;
+
+ xml_init_node(pool, st, &STATUS, NULL);
+ node = pj_pool_alloc(pool, sizeof(*node));
+ xml_init_node(pool, node, &BASIC, &CLOSED);
+ pj_xml_add_node(st, node);
+}
+
+PJ_DEF(pj_bool_t) pjpidf_status_is_basic_open(const pjpidf_status *st)
+{
+ pj_xml_node *node = pj_xml_find_node((pj_xml_node*)st, &BASIC);
+ pj_assert(node != NULL);
+ return pj_stricmp(&node->content, &OPEN)==0;
+}
+
+PJ_DEF(void) pjpidf_status_set_basic_open(pjpidf_status *st, pj_bool_t open)
+{
+ pj_xml_node *node = pj_xml_find_node(st, &BASIC);
+ pj_assert(node != NULL);
+ node->content = open ? OPEN : CLOSED;
+}
+
+PJ_DEF(pjpidf_pres*) pjpidf_create(pj_pool_t *pool, const pj_str_t *entity)
+{
+ pjpidf_pres *pres = pj_pool_alloc(pool, sizeof(*pres));
+ pjpidf_pres_construct(pool, pres, entity);
+ return pres;
+}
+
+PJ_DEF(pjpidf_pres*) pjpidf_parse(pj_pool_t *pool, char *text, int len)
+{
+ pjpidf_pres *pres = pj_xml_parse(pool, text, len);
+ if (pres) {
+ if (pj_stricmp(&pres->name, &PRESENCE) != 0)
+ return NULL;
+ }
+ return pres;
+}
+
+PJ_DEF(int) pjpidf_print(const pjpidf_pres* pres, char *buf, int len)
+{
+ return pj_xml_print(pres, buf, len, PJ_TRUE);
+}
+
diff --git a/pjsip/src/pjsip-simple/presence.c b/pjsip/src/pjsip-simple/presence.c
index 6f822568..a9cc6108 100644
--- a/pjsip/src/pjsip-simple/presence.c
+++ b/pjsip/src/pjsip-simple/presence.c
@@ -1,399 +1,399 @@
-/* $Id$ */
-/*
- * Copyright (C) 2003-2006 Benny Prijono <benny@prijono.org>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
-#include <pjsip_simple/presence.h>
-#include <pjsip/sip_transport.h>
-#include <pj/pool.h>
-#include <pj/string.h>
-#include <pj/guid.h>
-#include <pj/os.h>
-#include <stdio.h>
-
-/* Forward declarations. */
-static void on_query_subscribe(pjsip_rx_data *rdata, int *status);
-static void on_subscribe(pjsip_event_sub *sub, pjsip_rx_data *rdata,
- pjsip_event_sub_cb **cb, int *expires);
-static void on_sub_terminated(pjsip_event_sub *sub, const pj_str_t *reason);
-static void on_sub_received_refresh(pjsip_event_sub *sub, pjsip_rx_data *rdata);
-static void on_received_notify(pjsip_event_sub *sub, pjsip_rx_data *rdata);
-
-/* Some string constants. */
-static pj_str_t PRESENCE_EVENT = { "presence", 8 };
-
-/* Accept types. */
-static pj_str_t accept_names[] = {
- { "application/pidf+xml", 20 },
- { "application/xpidf+xml", 21 }
-};
-static pjsip_media_type accept_types[] = {
- {
- { "application", 11 },
- { "pidf+xml", 8 }
- },
- {
- { "application", 11 },
- { "xpidf+xml", 9 }
- }
-};
-
-/* Callback that is registered by application. */
-static pjsip_presence_cb cb;
-
-/* Package callback to be register to event_notify */
-static pjsip_event_sub_pkg_cb pkg_cb = { &on_query_subscribe,
- &on_subscribe };
-
-/* Global/static callback to be registered to event_notify */
-static pjsip_event_sub_cb sub_cb = { &on_sub_terminated,
- &on_sub_received_refresh,
- NULL,
- &on_received_notify,
- NULL };
-
-/*
- * Initialize presence module.
- * This will register event package "presence" to event framework.
- */
-PJ_DEF(void) pjsip_presence_init(const pjsip_presence_cb *pcb)
-{
- pj_memcpy(&cb, pcb, sizeof(*pcb));
- pjsip_event_sub_register_pkg( &PRESENCE_EVENT,
- sizeof(accept_names)/sizeof(accept_names[0]),
- accept_names,
- &pkg_cb);
-}
-
-/*
- * Create presence subscription.
- */
-PJ_DEF(pjsip_presentity*) pjsip_presence_create( pjsip_endpoint *endpt,
- const pj_str_t *local_url,
- const pj_str_t *remote_url,
- int expires,
- void *user_data )
-{
- pjsip_event_sub *sub;
- pjsip_presentity *pres;
-
- if (expires < 0)
- expires = 300;
-
- /* Create event subscription */
- sub = pjsip_event_sub_create(endpt, local_url, remote_url, &PRESENCE_EVENT,
- expires,
- sizeof(accept_names)/sizeof(accept_names[0]),
- accept_names,
- NULL, &sub_cb);
- if (!sub)
- return NULL;
-
- /* Allocate presence descriptor. */
- pres = pj_pool_calloc(sub->pool, 1, sizeof(*pres));
- pres->sub = sub;
- pres->user_data = user_data;
- sub->user_data = pres;
-
- return pres;
-}
-
-/*
- * Send SUBSCRIBE.
- */
-PJ_DEF(pj_status_t) pjsip_presence_subscribe( pjsip_presentity *pres )
-{
- return pjsip_event_sub_subscribe( pres->sub );
-}
-
-/*
- * Set credentials to be used for outgoing requests.
- */
-PJ_DEF(pj_status_t) pjsip_presence_set_credentials( pjsip_presentity *pres,
- int count,
- const pjsip_cred_info cred[])
-{
- return pjsip_event_sub_set_credentials(pres->sub, count, cred);
-}
-
-/*
- * Set route-set.
- */
-PJ_DEF(pj_status_t) pjsip_presence_set_route_set( pjsip_presentity *pres,
- const pjsip_route_hdr *hdr )
-{
- return pjsip_event_sub_set_route_set( pres->sub, hdr );
-}
-
-/*
- * Unsubscribe.
- */
-PJ_DEF(pj_status_t) pjsip_presence_unsubscribe( pjsip_presentity *pres )
-{
- return pjsip_event_sub_unsubscribe(pres->sub);
-}
-
-/*
- * This is the pjsip_msg_body callback to print XML body.
- */
-static int print_xml(pjsip_msg_body *body, char *buf, pj_size_t size)
-{
- return pj_xml_print( body->data, buf, size, PJ_TRUE );
-}
-
-/*
- * Create and initialize PIDF document and msg body (notifier only).
- */
-static pj_status_t init_presence_info( pjsip_presentity *pres )
-{
- pj_str_t uri;
- pj_pool_t *pool = pres->sub->pool;
- char tmp[PJSIP_MAX_URL_SIZE];
- pjpidf_tuple *tuple;
- const pjsip_media_type *content_type = NULL;
-
- pj_assert(pres->uas_body == NULL);
-
- /* Make entity_id */
- uri.ptr = tmp;
- uri.slen = pjsip_uri_print(PJSIP_URI_IN_REQ_URI, pres->sub->from->uri,
- tmp, sizeof(tmp));
- if (uri.slen < 0)
- return -1;
-
- if (pres->pres_type == PJSIP_PRES_TYPE_PIDF) {
- pj_str_t s;
-
- /* Create <presence>. */
- pres->uas_data.pidf = pjpidf_create(pool, &s);
-
- /* Create <tuple> */
- pj_create_unique_string(pool, &s);
- tuple = pjpidf_pres_add_tuple(pool, pres->uas_data.pidf, &s);
-
- /* Set <contact> */
- s.ptr = tmp;
- s.slen = pjsip_uri_print(PJSIP_URI_IN_REQ_URI, pres->sub->contact->uri, tmp, sizeof(tmp));
- if (s.slen < 0)
- return -1;
- pjpidf_tuple_set_contact(pool, tuple, &s);
-
- /* Content-Type */
- content_type = &accept_types[PJSIP_PRES_TYPE_PIDF];
-
- } else if (pres->pres_type == PJSIP_PRES_TYPE_XPIDF) {
-
- /* Create XPIDF */
- pres->uas_data.xpidf = pjxpidf_create(pool, &uri);
-
- /* Content-Type. */
- content_type = &accept_types[PJSIP_PRES_TYPE_XPIDF];
- }
-
- /* Create message body */
- pres->uas_body = pj_pool_alloc(pool, sizeof(pjsip_msg_body));
- pres->uas_body->content_type = *content_type;
- pres->uas_body->data = pres->uas_data.pidf;
- pres->uas_body->len = 0;
- pres->uas_body->print_body = &print_xml;
-
- return 0;
-}
-
-/*
- * Send NOTIFY and set subscription state.
- */
-PJ_DEF(pj_status_t) pjsip_presence_notify( pjsip_presentity *pres,
- pjsip_event_sub_state state,
- pj_bool_t is_online )
-{
- pj_str_t reason = { "", 0 };
-
- if (pres->uas_data.pidf == NULL) {
- if (init_presence_info(pres) != 0)
- return -1;
- }
-
- /* Update basic status in PIDF/XPIDF document. */
- if (pres->pres_type == PJSIP_PRES_TYPE_PIDF) {
- pjpidf_tuple *first;
- pjpidf_status *status;
- pj_time_val now;
- pj_parsed_time pnow;
-
- first = pjpidf_op.pres.get_first_tuple(pres->uas_data.pidf);
- pj_assert(first);
- status = pjpidf_op.tuple.get_status(first);
- pj_assert(status);
- pjpidf_op.status.set_basic_open(status, is_online);
-
- /* Update timestamp. */
- if (pres->timestamp.ptr == 0) {
- pres->timestamp.ptr = pj_pool_alloc(pres->sub->pool, 24);
- }
- pj_gettimeofday(&now);
- pj_time_decode(&now, &pnow);
- pres->timestamp.slen = sprintf(pres->timestamp.ptr,
- "%04d-%02d-%02dT%02d:%02d:%02dZ",
- pnow.year, pnow.mon, pnow.day,
- pnow.hour, pnow.min, pnow.sec);
- pjpidf_op.tuple.set_timestamp_np(pres->sub->pool, first, &pres->timestamp);
-
- } else if (pres->pres_type == PJSIP_PRES_TYPE_XPIDF) {
- pjxpidf_set_status( pres->uas_data.xpidf, is_online );
-
- } else {
- pj_assert(0);
- }
-
- /* Send notify. */
- return pjsip_event_sub_notify( pres->sub, state, &reason, pres->uas_body);
-}
-
-/*
- * Destroy subscription (can be called for both subscriber and notifier).
- */
-PJ_DEF(pj_status_t) pjsip_presence_destroy( pjsip_presentity *pres )
-{
- return pjsip_event_sub_destroy(pres->sub);
-}
-
-/*
- * This callback is called by event framework to query whether we want to
- * accept an incoming subscription.
- */
-static void on_query_subscribe(pjsip_rx_data *rdata, int *status)
-{
- if (cb.accept_presence) {
- (*cb.accept_presence)(rdata, status);
- }
-}
-
-/*
- * This callback is called by event framework after we accept the incoming
- * subscription, to notify about the new subscription instance.
- */
-static void on_subscribe(pjsip_event_sub *sub, pjsip_rx_data *rdata,
- pjsip_event_sub_cb **set_sub_cb, int *expires)
-{
- pjsip_presentity *pres;
- pjsip_accept_hdr *accept;
-
- pres = pj_pool_calloc(sub->pool, 1, sizeof(*pres));
- pres->sub = sub;
- pres->pres_type = PJSIP_PRES_TYPE_PIDF;
- sub->user_data = pres;
- *set_sub_cb = &sub_cb;
-
- accept = pjsip_msg_find_hdr(rdata->msg, PJSIP_H_ACCEPT, NULL);
- if (accept) {
- unsigned i;
- int found = 0;
- for (i=0; i<accept->count && !found; ++i) {
- int j;
- for (j=0; j<sizeof(accept_names)/sizeof(accept_names[0]); ++j) {
- if (!pj_stricmp(&accept->values[i], &accept_names[j])) {
- pres->pres_type = j;
- found = 1;
- break;
- }
- }
- }
- pj_assert(found );
- }
-
- (*cb.on_received_request)(pres, rdata, expires);
-}
-
-/*
- * This callback is called by event framework when the subscription is
- * terminated.
- */
-static void on_sub_terminated(pjsip_event_sub *sub, const pj_str_t *reason)
-{
- pjsip_presentity *pres = sub->user_data;
- if (cb.on_terminated)
- (*cb.on_terminated)(pres, reason);
-}
-
-/*
- * This callback is called by event framework when it receives incoming
- * SUBSCRIBE request to refresh the subscription.
- */
-static void on_sub_received_refresh(pjsip_event_sub *sub, pjsip_rx_data *rdata)
-{
- pjsip_presentity *pres = sub->user_data;
- if (cb.on_received_refresh)
- (*cb.on_received_refresh)(pres, rdata);
-}
-
-/*
- * This callback is called by event framework when it receives incoming
- * NOTIFY request.
- */
-static void on_received_notify(pjsip_event_sub *sub, pjsip_rx_data *rdata)
-{
- pjsip_presentity *pres = sub->user_data;
-
- if (cb.on_received_update) {
- pj_status_t is_open;
- pjsip_msg_body *body;
- int i;
-
- body = rdata->msg->body;
- if (!body)
- return;
-
- for (i=0; i<sizeof(accept_types)/sizeof(accept_types[0]); ++i) {
- if (!pj_stricmp(&body->content_type.type, &accept_types[i].type) &&
- !pj_stricmp(&body->content_type.subtype, &accept_types[i].subtype))
- {
- break;
- }
- }
-
- if (i==PJSIP_PRES_TYPE_PIDF) {
- pjpidf_pres *pres;
- pjpidf_tuple *tuple;
- pjpidf_status *status;
-
- pres = pjpidf_parse(rdata->pool, body->data, body->len);
- if (!pres)
- return;
- tuple = pjpidf_pres_get_first_tuple(pres);
- if (!tuple)
- return;
- status = pjpidf_tuple_get_status(tuple);
- if (!status)
- return;
- is_open = pjpidf_status_is_basic_open(status);
-
- } else if (i==PJSIP_PRES_TYPE_XPIDF) {
- pjxpidf_pres *pres;
-
- pres = pjxpidf_parse(rdata->pool, body->data, body->len);
- if (!pres)
- return;
- is_open = pjxpidf_get_status(pres);
-
- } else {
- return;
- }
-
- (*cb.on_received_update)(pres, is_open);
- }
-}
-
+/* $Id$ */
+/*
+ * Copyright (C) 2003-2006 Benny Prijono <benny@prijono.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#include <pjsip_simple/presence.h>
+#include <pjsip/sip_transport.h>
+#include <pj/pool.h>
+#include <pj/string.h>
+#include <pj/guid.h>
+#include <pj/os.h>
+#include <stdio.h>
+
+/* Forward declarations. */
+static void on_query_subscribe(pjsip_rx_data *rdata, int *status);
+static void on_subscribe(pjsip_event_sub *sub, pjsip_rx_data *rdata,
+ pjsip_event_sub_cb **cb, int *expires);
+static void on_sub_terminated(pjsip_event_sub *sub, const pj_str_t *reason);
+static void on_sub_received_refresh(pjsip_event_sub *sub, pjsip_rx_data *rdata);
+static void on_received_notify(pjsip_event_sub *sub, pjsip_rx_data *rdata);
+
+/* Some string constants. */
+static pj_str_t PRESENCE_EVENT = { "presence", 8 };
+
+/* Accept types. */
+static pj_str_t accept_names[] = {
+ { "application/pidf+xml", 20 },
+ { "application/xpidf+xml", 21 }
+};
+static pjsip_media_type accept_types[] = {
+ {
+ { "application", 11 },
+ { "pidf+xml", 8 }
+ },
+ {
+ { "application", 11 },
+ { "xpidf+xml", 9 }
+ }
+};
+
+/* Callback that is registered by application. */
+static pjsip_presence_cb cb;
+
+/* Package callback to be register to event_notify */
+static pjsip_event_sub_pkg_cb pkg_cb = { &on_query_subscribe,
+ &on_subscribe };
+
+/* Global/static callback to be registered to event_notify */
+static pjsip_event_sub_cb sub_cb = { &on_sub_terminated,
+ &on_sub_received_refresh,
+ NULL,
+ &on_received_notify,
+ NULL };
+
+/*
+ * Initialize presence module.
+ * This will register event package "presence" to event framework.
+ */
+PJ_DEF(void) pjsip_presence_init(const pjsip_presence_cb *pcb)
+{
+ pj_memcpy(&cb, pcb, sizeof(*pcb));
+ pjsip_event_sub_register_pkg( &PRESENCE_EVENT,
+ sizeof(accept_names)/sizeof(accept_names[0]),
+ accept_names,
+ &pkg_cb);
+}
+
+/*
+ * Create presence subscription.
+ */
+PJ_DEF(pjsip_presentity*) pjsip_presence_create( pjsip_endpoint *endpt,
+ const pj_str_t *local_url,
+ const pj_str_t *remote_url,
+ int expires,
+ void *user_data )
+{
+ pjsip_event_sub *sub;
+ pjsip_presentity *pres;
+
+ if (expires < 0)
+ expires = 300;
+
+ /* Create event subscription */
+ sub = pjsip_event_sub_create(endpt, local_url, remote_url, &PRESENCE_EVENT,
+ expires,
+ sizeof(accept_names)/sizeof(accept_names[0]),
+ accept_names,
+ NULL, &sub_cb);
+ if (!sub)
+ return NULL;
+
+ /* Allocate presence descriptor. */
+ pres = pj_pool_calloc(sub->pool, 1, sizeof(*pres));
+ pres->sub = sub;
+ pres->user_data = user_data;
+ sub->user_data = pres;
+
+ return pres;
+}
+
+/*
+ * Send SUBSCRIBE.
+ */
+PJ_DEF(pj_status_t) pjsip_presence_subscribe( pjsip_presentity *pres )
+{
+ return pjsip_event_sub_subscribe( pres->sub );
+}
+
+/*
+ * Set credentials to be used for outgoing requests.
+ */
+PJ_DEF(pj_status_t) pjsip_presence_set_credentials( pjsip_presentity *pres,
+ int count,
+ const pjsip_cred_info cred[])
+{
+ return pjsip_event_sub_set_credentials(pres->sub, count, cred);
+}
+
+/*
+ * Set route-set.
+ */
+PJ_DEF(pj_status_t) pjsip_presence_set_route_set( pjsip_presentity *pres,
+ const pjsip_route_hdr *hdr )
+{
+ return pjsip_event_sub_set_route_set( pres->sub, hdr );
+}
+
+/*
+ * Unsubscribe.
+ */
+PJ_DEF(pj_status_t) pjsip_presence_unsubscribe( pjsip_presentity *pres )
+{
+ return pjsip_event_sub_unsubscribe(pres->sub);
+}
+
+/*
+ * This is the pjsip_msg_body callback to print XML body.
+ */
+static int print_xml(pjsip_msg_body *body, char *buf, pj_size_t size)
+{
+ return pj_xml_print( body->data, buf, size, PJ_TRUE );
+}
+
+/*
+ * Create and initialize PIDF document and msg body (notifier only).
+ */
+static pj_status_t init_presence_info( pjsip_presentity *pres )
+{
+ pj_str_t uri;
+ pj_pool_t *pool = pres->sub->pool;
+ char tmp[PJSIP_MAX_URL_SIZE];
+ pjpidf_tuple *tuple;
+ const pjsip_media_type *content_type = NULL;
+
+ pj_assert(pres->uas_body == NULL);
+
+ /* Make entity_id */
+ uri.ptr = tmp;
+ uri.slen = pjsip_uri_print(PJSIP_URI_IN_REQ_URI, pres->sub->from->uri,
+ tmp, sizeof(tmp));
+ if (uri.slen < 0)
+ return -1;
+
+ if (pres->pres_type == PJSIP_PRES_TYPE_PIDF) {
+ pj_str_t s;
+
+ /* Create <presence>. */
+ pres->uas_data.pidf = pjpidf_create(pool, &s);
+
+ /* Create <tuple> */
+ pj_create_unique_string(pool, &s);
+ tuple = pjpidf_pres_add_tuple(pool, pres->uas_data.pidf, &s);
+
+ /* Set <contact> */
+ s.ptr = tmp;
+ s.slen = pjsip_uri_print(PJSIP_URI_IN_REQ_URI, pres->sub->contact->uri, tmp, sizeof(tmp));
+ if (s.slen < 0)
+ return -1;
+ pjpidf_tuple_set_contact(pool, tuple, &s);
+
+ /* Content-Type */
+ content_type = &accept_types[PJSIP_PRES_TYPE_PIDF];
+
+ } else if (pres->pres_type == PJSIP_PRES_TYPE_XPIDF) {
+
+ /* Create XPIDF */
+ pres->uas_data.xpidf = pjxpidf_create(pool, &uri);
+
+ /* Content-Type. */
+ content_type = &accept_types[PJSIP_PRES_TYPE_XPIDF];
+ }
+
+ /* Create message body */
+ pres->uas_body = pj_pool_alloc(pool, sizeof(pjsip_msg_body));
+ pres->uas_body->content_type = *content_type;
+ pres->uas_body->data = pres->uas_data.pidf;
+ pres->uas_body->len = 0;
+ pres->uas_body->print_body = &print_xml;
+
+ return 0;
+}
+
+/*
+ * Send NOTIFY and set subscription state.
+ */
+PJ_DEF(pj_status_t) pjsip_presence_notify( pjsip_presentity *pres,
+ pjsip_event_sub_state state,
+ pj_bool_t is_online )
+{
+ pj_str_t reason = { "", 0 };
+
+ if (pres->uas_data.pidf == NULL) {
+ if (init_presence_info(pres) != 0)
+ return -1;
+ }
+
+ /* Update basic status in PIDF/XPIDF document. */
+ if (pres->pres_type == PJSIP_PRES_TYPE_PIDF) {
+ pjpidf_tuple *first;
+ pjpidf_status *status;
+ pj_time_val now;
+ pj_parsed_time pnow;
+
+ first = pjpidf_op.pres.get_first_tuple(pres->uas_data.pidf);
+ pj_assert(first);
+ status = pjpidf_op.tuple.get_status(first);
+ pj_assert(status);
+ pjpidf_op.status.set_basic_open(status, is_online);
+
+ /* Update timestamp. */
+ if (pres->timestamp.ptr == 0) {
+ pres->timestamp.ptr = pj_pool_alloc(pres->sub->pool, 24);
+ }
+ pj_gettimeofday(&now);
+ pj_time_decode(&now, &pnow);
+ pres->timestamp.slen = sprintf(pres->timestamp.ptr,
+ "%04d-%02d-%02dT%02d:%02d:%02dZ",
+ pnow.year, pnow.mon, pnow.day,
+ pnow.hour, pnow.min, pnow.sec);
+ pjpidf_op.tuple.set_timestamp_np(pres->sub->pool, first, &pres->timestamp);
+
+ } else if (pres->pres_type == PJSIP_PRES_TYPE_XPIDF) {
+ pjxpidf_set_status( pres->uas_data.xpidf, is_online );
+
+ } else {
+ pj_assert(0);
+ }
+
+ /* Send notify. */
+ return pjsip_event_sub_notify( pres->sub, state, &reason, pres->uas_body);
+}
+
+/*
+ * Destroy subscription (can be called for both subscriber and notifier).
+ */
+PJ_DEF(pj_status_t) pjsip_presence_destroy( pjsip_presentity *pres )
+{
+ return pjsip_event_sub_destroy(pres->sub);
+}
+
+/*
+ * This callback is called by event framework to query whether we want to
+ * accept an incoming subscription.
+ */
+static void on_query_subscribe(pjsip_rx_data *rdata, int *status)
+{
+ if (cb.accept_presence) {
+ (*cb.accept_presence)(rdata, status);
+ }
+}
+
+/*
+ * This callback is called by event framework after we accept the incoming
+ * subscription, to notify about the new subscription instance.
+ */
+static void on_subscribe(pjsip_event_sub *sub, pjsip_rx_data *rdata,
+ pjsip_event_sub_cb **set_sub_cb, int *expires)
+{
+ pjsip_presentity *pres;
+ pjsip_accept_hdr *accept;
+
+ pres = pj_pool_calloc(sub->pool, 1, sizeof(*pres));
+ pres->sub = sub;
+ pres->pres_type = PJSIP_PRES_TYPE_PIDF;
+ sub->user_data = pres;
+ *set_sub_cb = &sub_cb;
+
+ accept = pjsip_msg_find_hdr(rdata->msg, PJSIP_H_ACCEPT, NULL);
+ if (accept) {
+ unsigned i;
+ int found = 0;
+ for (i=0; i<accept->count && !found; ++i) {
+ int j;
+ for (j=0; j<sizeof(accept_names)/sizeof(accept_names[0]); ++j) {
+ if (!pj_stricmp(&accept->values[i], &accept_names[j])) {
+ pres->pres_type = j;
+ found = 1;
+ break;
+ }
+ }
+ }
+ pj_assert(found );
+ }
+
+ (*cb.on_received_request)(pres, rdata, expires);
+}
+
+/*
+ * This callback is called by event framework when the subscription is
+ * terminated.
+ */
+static void on_sub_terminated(pjsip_event_sub *sub, const pj_str_t *reason)
+{
+ pjsip_presentity *pres = sub->user_data;
+ if (cb.on_terminated)
+ (*cb.on_terminated)(pres, reason);
+}
+
+/*
+ * This callback is called by event framework when it receives incoming
+ * SUBSCRIBE request to refresh the subscription.
+ */
+static void on_sub_received_refresh(pjsip_event_sub *sub, pjsip_rx_data *rdata)
+{
+ pjsip_presentity *pres = sub->user_data;
+ if (cb.on_received_refresh)
+ (*cb.on_received_refresh)(pres, rdata);
+}
+
+/*
+ * This callback is called by event framework when it receives incoming
+ * NOTIFY request.
+ */
+static void on_received_notify(pjsip_event_sub *sub, pjsip_rx_data *rdata)
+{
+ pjsip_presentity *pres = sub->user_data;
+
+ if (cb.on_received_update) {
+ pj_status_t is_open;
+ pjsip_msg_body *body;
+ int i;
+
+ body = rdata->msg->body;
+ if (!body)
+ return;
+
+ for (i=0; i<sizeof(accept_types)/sizeof(accept_types[0]); ++i) {
+ if (!pj_stricmp(&body->content_type.type, &accept_types[i].type) &&
+ !pj_stricmp(&body->content_type.subtype, &accept_types[i].subtype))
+ {
+ break;
+ }
+ }
+
+ if (i==PJSIP_PRES_TYPE_PIDF) {
+ pjpidf_pres *pres;
+ pjpidf_tuple *tuple;
+ pjpidf_status *status;
+
+ pres = pjpidf_parse(rdata->pool, body->data, body->len);
+ if (!pres)
+ return;
+ tuple = pjpidf_pres_get_first_tuple(pres);
+ if (!tuple)
+ return;
+ status = pjpidf_tuple_get_status(tuple);
+ if (!status)
+ return;
+ is_open = pjpidf_status_is_basic_open(status);
+
+ } else if (i==PJSIP_PRES_TYPE_XPIDF) {
+ pjxpidf_pres *pres;
+
+ pres = pjxpidf_parse(rdata->pool, body->data, body->len);
+ if (!pres)
+ return;
+ is_open = pjxpidf_get_status(pres);
+
+ } else {
+ return;
+ }
+
+ (*cb.on_received_update)(pres, is_open);
+ }
+}
+
diff --git a/pjsip/src/pjsip-simple/xpidf.c b/pjsip/src/pjsip-simple/xpidf.c
index 8717466f..7cc377ba 100644
--- a/pjsip/src/pjsip-simple/xpidf.c
+++ b/pjsip/src/pjsip-simple/xpidf.c
@@ -1,294 +1,294 @@
-/* $Id$ */
-/*
- * Copyright (C) 2003-2006 Benny Prijono <benny@prijono.org>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
-#include <pjsip_simple/xpidf.h>
-#include <pj/pool.h>
-#include <pj/string.h>
-#include <pj/guid.h>
-
-static pj_str_t PRESENCE = { "presence", 8 };
-static pj_str_t STATUS = { "status", 6 };
-static pj_str_t OPEN = { "open", 4 };
-static pj_str_t CLOSED = { "closed", 6 };
-static pj_str_t URI = { "uri", 3 };
-static pj_str_t ATOM = { "atom", 4 };
-static pj_str_t ATOMID = { "atomid", 6 };
-static pj_str_t ADDRESS = { "address", 7 };
-static pj_str_t SUBSCRIBE_PARAM = { ";method=SUBSCRIBE", 17 };
-static pj_str_t PRESENTITY = { "presentity", 10 };
-static pj_str_t EMPTY_STRING = { NULL, 0 };
-
-static pj_xml_node* xml_create_node(pj_pool_t *pool,
- pj_str_t *name, const pj_str_t *value)
-{
- pj_xml_node *node;
-
- node = pj_pool_alloc(pool, sizeof(pj_xml_node));
- pj_list_init(&node->attr_head);
- pj_list_init(&node->node_head);
- node->name = *name;
- if (value) pj_strdup(pool, &node->content, value);
- else node->content.ptr=NULL, node->content.slen=0;
-
- return node;
-}
-
-static pj_xml_attr* xml_create_attr(pj_pool_t *pool, pj_str_t *name,
- const pj_str_t *value)
-{
- pj_xml_attr *attr = pj_pool_alloc(pool, sizeof(*attr));
- attr->name = *name;
- pj_strdup(pool, &attr->value, value);
- return attr;
-}
-
-
-PJ_DEF(pjxpidf_pres*) pjxpidf_create(pj_pool_t *pool, const pj_str_t *uri_cstr)
-{
- pjxpidf_pres *pres;
- pj_xml_node *presentity;
- pj_xml_node *atom;
- pj_xml_node *addr;
- pj_xml_node *status;
- pj_xml_attr *attr;
- pj_str_t uri;
- pj_str_t tmp;
-
- /* <presence> */
- pres = xml_create_node(pool, &PRESENCE, NULL);
-
- /* <presentity> */
- presentity = xml_create_node(pool, &PRESENTITY, NULL);
- pj_xml_add_node(pres, presentity);
-
- /* uri attribute */
- uri.ptr = pj_pool_alloc(pool, uri_cstr->slen + SUBSCRIBE_PARAM.slen);
- pj_strcpy( &uri, uri_cstr);
- pj_strcat( &uri, &SUBSCRIBE_PARAM);
- attr = xml_create_attr(pool, &URI, &uri);
- pj_xml_add_attr(presentity, attr);
-
- /* <atom> */
- atom = xml_create_node(pool, &ATOM, NULL);
- pj_xml_add_node(pres, atom);
-
- /* atom id */
- pj_create_unique_string(pool, &tmp);
- attr = xml_create_attr(pool, &ATOMID, &tmp);
- pj_xml_add_attr(atom, attr);
-
- /* address */
- addr = xml_create_node(pool, &ADDRESS, NULL);
- pj_xml_add_node(atom, addr);
-
- /* address'es uri */
- attr = xml_create_attr(pool, &URI, uri_cstr);
- pj_xml_add_attr(addr, attr);
-
- /* status */
- status = xml_create_node(pool, &STATUS, NULL);
- pj_xml_add_node(addr, status);
-
- /* status attr */
- attr = xml_create_attr(pool, &STATUS, &OPEN);
- pj_xml_add_attr(status, attr);
-
- return pres;
-}
-
-
-
-PJ_DEF(pjxpidf_pres*) pjxpidf_parse(pj_pool_t *pool, char *text, pj_size_t len)
-{
- pjxpidf_pres *pres;
- pj_xml_node *node;
-
- pres = pj_xml_parse(pool, text, len);
- if (!pres)
- return NULL;
-
- /* Validate <presence> */
- if (pj_stricmp(&pres->name, &PRESENCE) != 0)
- return NULL;
- if (pj_xml_find_attr(pres, &URI, NULL) == NULL)
- return NULL;
-
- /* Validate <presentity> */
- node = pj_xml_find_node(pres, &PRESENTITY);
- if (node == NULL)
- return NULL;
-
- /* Validate <atom> */
- node = pj_xml_find_node(pres, &ATOM);
- if (node == NULL)
- return NULL;
- if (pj_xml_find_attr(node, &ATOMID, NULL) == NULL)
- return NULL;
-
- /* Address */
- node = pj_xml_find_node(node, &ADDRESS);
- if (node == NULL)
- return NULL;
- if (pj_xml_find_attr(node, &URI, NULL) == NULL)
- return NULL;
-
-
- /* Status */
- node = pj_xml_find_node(node, &STATUS);
- if (node == NULL)
- return NULL;
- if (pj_xml_find_attr(node, &STATUS, NULL) == NULL)
- return NULL;
-
- return pres;
-}
-
-
-PJ_DEF(int) pjxpidf_print( pjxpidf_pres *pres, char *text, pj_size_t len)
-{
- return pj_xml_print(pres, text, len, PJ_TRUE);
-}
-
-
-PJ_DEF(pj_str_t*) pjxpidf_get_uri(pjxpidf_pres *pres)
-{
- pj_xml_node *presentity;
- pj_xml_attr *attr;
-
- presentity = pj_xml_find_node(pres, &PRESENTITY);
- if (!presentity)
- return &EMPTY_STRING;
-
- attr = pj_xml_find_attr(presentity, &URI, NULL);
- if (!attr)
- return &EMPTY_STRING;
-
- return &attr->value;
-}
-
-
-PJ_DEF(pj_status_t) pjxpidf_set_uri(pj_pool_t *pool, pjxpidf_pres *pres,
- const pj_str_t *uri)
-{
- pj_xml_node *presentity;
- pj_xml_node *atom;
- pj_xml_node *addr;
- pj_xml_attr *attr;
- pj_str_t dup_uri;
-
- presentity = pj_xml_find_node(pres, &PRESENTITY);
- if (!presentity) {
- pj_assert(0);
- return -1;
- }
- atom = pj_xml_find_node(pres, &ATOM);
- if (!atom) {
- pj_assert(0);
- return -1;
- }
- addr = pj_xml_find_node(atom, &ADDRESS);
- if (!addr) {
- pj_assert(0);
- return -1;
- }
-
- /* Set uri in presentity */
- attr = pj_xml_find_attr(presentity, &URI, NULL);
- if (!attr) {
- pj_assert(0);
- return -1;
- }
- pj_strdup(pool, &dup_uri, uri);
- attr->value = dup_uri;
-
- /* Set uri in address. */
- attr = pj_xml_find_attr(addr, &URI, NULL);
- if (!attr) {
- pj_assert(0);
- return -1;
- }
- attr->value = dup_uri;
-
- return 0;
-}
-
-
-PJ_DEF(pj_bool_t) pjxpidf_get_status(pjxpidf_pres *pres)
-{
- pj_xml_node *atom;
- pj_xml_node *addr;
- pj_xml_node *status;
- pj_xml_attr *attr;
-
- atom = pj_xml_find_node(pres, &ATOM);
- if (!atom) {
- pj_assert(0);
- return PJ_FALSE;
- }
- addr = pj_xml_find_node(atom, &ADDRESS);
- if (!addr) {
- pj_assert(0);
- return PJ_FALSE;
- }
- status = pj_xml_find_node(atom, &STATUS);
- if (!status) {
- pj_assert(0);
- return PJ_FALSE;
- }
- attr = pj_xml_find_attr(status, &STATUS, NULL);
- if (!attr) {
- pj_assert(0);
- return PJ_FALSE;
- }
-
- return pj_stricmp(&attr->value, &OPEN) ? PJ_TRUE : PJ_FALSE;
-}
-
-
-PJ_DEF(pj_status_t) pjxpidf_set_status(pjxpidf_pres *pres, pj_bool_t online_status)
-{
- pj_xml_node *atom;
- pj_xml_node *addr;
- pj_xml_node *status;
- pj_xml_attr *attr;
-
- atom = pj_xml_find_node(pres, &ATOM);
- if (!atom) {
- pj_assert(0);
- return -1;
- }
- addr = pj_xml_find_node(atom, &ADDRESS);
- if (!addr) {
- pj_assert(0);
- return -1;
- }
- status = pj_xml_find_node(addr, &STATUS);
- if (!status) {
- pj_assert(0);
- return -1;
- }
- attr = pj_xml_find_attr(status, &STATUS, NULL);
- if (!attr) {
- pj_assert(0);
- return -1;
- }
-
- attr->value = ( online_status ? OPEN : CLOSED );
- return 0;
-}
-
+/* $Id$ */
+/*
+ * Copyright (C) 2003-2006 Benny Prijono <benny@prijono.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#include <pjsip_simple/xpidf.h>
+#include <pj/pool.h>
+#include <pj/string.h>
+#include <pj/guid.h>
+
+static pj_str_t PRESENCE = { "presence", 8 };
+static pj_str_t STATUS = { "status", 6 };
+static pj_str_t OPEN = { "open", 4 };
+static pj_str_t CLOSED = { "closed", 6 };
+static pj_str_t URI = { "uri", 3 };
+static pj_str_t ATOM = { "atom", 4 };
+static pj_str_t ATOMID = { "atomid", 6 };
+static pj_str_t ADDRESS = { "address", 7 };
+static pj_str_t SUBSCRIBE_PARAM = { ";method=SUBSCRIBE", 17 };
+static pj_str_t PRESENTITY = { "presentity", 10 };
+static pj_str_t EMPTY_STRING = { NULL, 0 };
+
+static pj_xml_node* xml_create_node(pj_pool_t *pool,
+ pj_str_t *name, const pj_str_t *value)
+{
+ pj_xml_node *node;
+
+ node = pj_pool_alloc(pool, sizeof(pj_xml_node));
+ pj_list_init(&node->attr_head);
+ pj_list_init(&node->node_head);
+ node->name = *name;
+ if (value) pj_strdup(pool, &node->content, value);
+ else node->content.ptr=NULL, node->content.slen=0;
+
+ return node;
+}
+
+static pj_xml_attr* xml_create_attr(pj_pool_t *pool, pj_str_t *name,
+ const pj_str_t *value)
+{
+ pj_xml_attr *attr = pj_pool_alloc(pool, sizeof(*attr));
+ attr->name = *name;
+ pj_strdup(pool, &attr->value, value);
+ return attr;
+}
+
+
+PJ_DEF(pjxpidf_pres*) pjxpidf_create(pj_pool_t *pool, const pj_str_t *uri_cstr)
+{
+ pjxpidf_pres *pres;
+ pj_xml_node *presentity;
+ pj_xml_node *atom;
+ pj_xml_node *addr;
+ pj_xml_node *status;
+ pj_xml_attr *attr;
+ pj_str_t uri;
+ pj_str_t tmp;
+
+ /* <presence> */
+ pres = xml_create_node(pool, &PRESENCE, NULL);
+
+ /* <presentity> */
+ presentity = xml_create_node(pool, &PRESENTITY, NULL);
+ pj_xml_add_node(pres, presentity);
+
+ /* uri attribute */
+ uri.ptr = pj_pool_alloc(pool, uri_cstr->slen + SUBSCRIBE_PARAM.slen);
+ pj_strcpy( &uri, uri_cstr);
+ pj_strcat( &uri, &SUBSCRIBE_PARAM);
+ attr = xml_create_attr(pool, &URI, &uri);
+ pj_xml_add_attr(presentity, attr);
+
+ /* <atom> */
+ atom = xml_create_node(pool, &ATOM, NULL);
+ pj_xml_add_node(pres, atom);
+
+ /* atom id */
+ pj_create_unique_string(pool, &tmp);
+ attr = xml_create_attr(pool, &ATOMID, &tmp);
+ pj_xml_add_attr(atom, attr);
+
+ /* address */
+ addr = xml_create_node(pool, &ADDRESS, NULL);
+ pj_xml_add_node(atom, addr);
+
+ /* address'es uri */
+ attr = xml_create_attr(pool, &URI, uri_cstr);
+ pj_xml_add_attr(addr, attr);
+
+ /* status */
+ status = xml_create_node(pool, &STATUS, NULL);
+ pj_xml_add_node(addr, status);
+
+ /* status attr */
+ attr = xml_create_attr(pool, &STATUS, &OPEN);
+ pj_xml_add_attr(status, attr);
+
+ return pres;
+}
+
+
+
+PJ_DEF(pjxpidf_pres*) pjxpidf_parse(pj_pool_t *pool, char *text, pj_size_t len)
+{
+ pjxpidf_pres *pres;
+ pj_xml_node *node;
+
+ pres = pj_xml_parse(pool, text, len);
+ if (!pres)
+ return NULL;
+
+ /* Validate <presence> */
+ if (pj_stricmp(&pres->name, &PRESENCE) != 0)
+ return NULL;
+ if (pj_xml_find_attr(pres, &URI, NULL) == NULL)
+ return NULL;
+
+ /* Validate <presentity> */
+ node = pj_xml_find_node(pres, &PRESENTITY);
+ if (node == NULL)
+ return NULL;
+
+ /* Validate <atom> */
+ node = pj_xml_find_node(pres, &ATOM);
+ if (node == NULL)
+ return NULL;
+ if (pj_xml_find_attr(node, &ATOMID, NULL) == NULL)
+ return NULL;
+
+ /* Address */
+ node = pj_xml_find_node(node, &ADDRESS);
+ if (node == NULL)
+ return NULL;
+ if (pj_xml_find_attr(node, &URI, NULL) == NULL)
+ return NULL;
+
+
+ /* Status */
+ node = pj_xml_find_node(node, &STATUS);
+ if (node == NULL)
+ return NULL;
+ if (pj_xml_find_attr(node, &STATUS, NULL) == NULL)
+ return NULL;
+
+ return pres;
+}
+
+
+PJ_DEF(int) pjxpidf_print( pjxpidf_pres *pres, char *text, pj_size_t len)
+{
+ return pj_xml_print(pres, text, len, PJ_TRUE);
+}
+
+
+PJ_DEF(pj_str_t*) pjxpidf_get_uri(pjxpidf_pres *pres)
+{
+ pj_xml_node *presentity;
+ pj_xml_attr *attr;
+
+ presentity = pj_xml_find_node(pres, &PRESENTITY);
+ if (!presentity)
+ return &EMPTY_STRING;
+
+ attr = pj_xml_find_attr(presentity, &URI, NULL);
+ if (!attr)
+ return &EMPTY_STRING;
+
+ return &attr->value;
+}
+
+
+PJ_DEF(pj_status_t) pjxpidf_set_uri(pj_pool_t *pool, pjxpidf_pres *pres,
+ const pj_str_t *uri)
+{
+ pj_xml_node *presentity;
+ pj_xml_node *atom;
+ pj_xml_node *addr;
+ pj_xml_attr *attr;
+ pj_str_t dup_uri;
+
+ presentity = pj_xml_find_node(pres, &PRESENTITY);
+ if (!presentity) {
+ pj_assert(0);
+ return -1;
+ }
+ atom = pj_xml_find_node(pres, &ATOM);
+ if (!atom) {
+ pj_assert(0);
+ return -1;
+ }
+ addr = pj_xml_find_node(atom, &ADDRESS);
+ if (!addr) {
+ pj_assert(0);
+ return -1;
+ }
+
+ /* Set uri in presentity */
+ attr = pj_xml_find_attr(presentity, &URI, NULL);
+ if (!attr) {
+ pj_assert(0);
+ return -1;
+ }
+ pj_strdup(pool, &dup_uri, uri);
+ attr->value = dup_uri;
+
+ /* Set uri in address. */
+ attr = pj_xml_find_attr(addr, &URI, NULL);
+ if (!attr) {
+ pj_assert(0);
+ return -1;
+ }
+ attr->value = dup_uri;
+
+ return 0;
+}
+
+
+PJ_DEF(pj_bool_t) pjxpidf_get_status(pjxpidf_pres *pres)
+{
+ pj_xml_node *atom;
+ pj_xml_node *addr;
+ pj_xml_node *status;
+ pj_xml_attr *attr;
+
+ atom = pj_xml_find_node(pres, &ATOM);
+ if (!atom) {
+ pj_assert(0);
+ return PJ_FALSE;
+ }
+ addr = pj_xml_find_node(atom, &ADDRESS);
+ if (!addr) {
+ pj_assert(0);
+ return PJ_FALSE;
+ }
+ status = pj_xml_find_node(atom, &STATUS);
+ if (!status) {
+ pj_assert(0);
+ return PJ_FALSE;
+ }
+ attr = pj_xml_find_attr(status, &STATUS, NULL);
+ if (!attr) {
+ pj_assert(0);
+ return PJ_FALSE;
+ }
+
+ return pj_stricmp(&attr->value, &OPEN) ? PJ_TRUE : PJ_FALSE;
+}
+
+
+PJ_DEF(pj_status_t) pjxpidf_set_status(pjxpidf_pres *pres, pj_bool_t online_status)
+{
+ pj_xml_node *atom;
+ pj_xml_node *addr;
+ pj_xml_node *status;
+ pj_xml_attr *attr;
+
+ atom = pj_xml_find_node(pres, &ATOM);
+ if (!atom) {
+ pj_assert(0);
+ return -1;
+ }
+ addr = pj_xml_find_node(atom, &ADDRESS);
+ if (!addr) {
+ pj_assert(0);
+ return -1;
+ }
+ status = pj_xml_find_node(addr, &STATUS);
+ if (!status) {
+ pj_assert(0);
+ return -1;
+ }
+ attr = pj_xml_find_attr(status, &STATUS, NULL);
+ if (!attr) {
+ pj_assert(0);
+ return -1;
+ }
+
+ attr->value = ( online_status ? OPEN : CLOSED );
+ return 0;
+}
+
diff --git a/pjsip/src/pjsip-ua/sip_dialog.c b/pjsip/src/pjsip-ua/sip_dialog.c
index ac110412..bb4861b8 100644
--- a/pjsip/src/pjsip-ua/sip_dialog.c
+++ b/pjsip/src/pjsip-ua/sip_dialog.c
@@ -1,1802 +1,1802 @@
-/* $Id$ */
-/*
- * Copyright (C) 2003-2006 Benny Prijono <benny@prijono.org>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
-#include <pjsip_mod_ua/sip_dialog.h>
-#include <pjsip_mod_ua/sip_ua.h>
-#include <pjsip_mod_ua/sip_ua_private.h>
-#include <pjsip/sip_transport.h>
-#include <pjsip/sip_transaction.h>
-#include <pjsip/sip_types.h>
-#include <pjsip/sip_endpoint.h>
-#include <pjsip/sip_uri.h>
-#include <pjsip/sip_util.h>
-#include <pjsip/sip_module.h>
-#include <pjsip/sip_event.h>
-#include <pjsip/sip_parser.h>
-#include <pj/string.h>
-#include <pj/log.h>
-#include <pj/os.h>
-#include <pj/guid.h>
-#include <pj/except.h>
-#include <pj/pool.h>
-
-/* TLS to keep dialog lock record (initialized by UA) */
-int pjsip_dlg_lock_tls_id;
-
-struct dialog_lock_data
-{
- struct dialog_lock_data *prev;
- pjsip_dlg *dlg;
- int is_alive;
-};
-
-/*
- * Static function prototypes.
- */
-static void dlg_create_request_throw( pjsip_tx_data **p_tdata,
- pjsip_dlg *dlg,
- const pjsip_method *method,
- int cseq );
-static int dlg_on_all_state_pre( pjsip_dlg *dlg,
- pjsip_transaction *tsx,
- pjsip_event *event);
-static int dlg_on_all_state_post( pjsip_dlg *dlg,
- pjsip_transaction *tsx,
- pjsip_event *event);
-static int dlg_on_state_null( pjsip_dlg *dlg,
- pjsip_transaction *tsx,
- pjsip_event *event);
-static int dlg_on_state_incoming( pjsip_dlg *dlg,
- pjsip_transaction *tsx,
- pjsip_event *event);
-static int dlg_on_state_calling( pjsip_dlg *dlg,
- pjsip_transaction *tsx,
- pjsip_event *event);
-static int dlg_on_state_proceeding( pjsip_dlg *dlg,
- pjsip_transaction *tsx,
- pjsip_event *event);
-static int dlg_on_state_proceeding_caller( pjsip_dlg *dlg,
- pjsip_transaction *tsx,
- pjsip_event *event);
-static int dlg_on_state_proceeding_callee( pjsip_dlg *dlg,
- pjsip_transaction *tsx,
- pjsip_event *event);
-static int dlg_on_state_connecting( pjsip_dlg *dlg,
- pjsip_transaction *tsx,
- pjsip_event *event);
-static int dlg_on_state_established( pjsip_dlg *dlg,
- pjsip_transaction *tsx,
- pjsip_event *event);
-static int dlg_on_state_disconnected( pjsip_dlg *dlg,
- pjsip_transaction *tsx,
- pjsip_event *event);
-static int dlg_on_state_terminated( pjsip_dlg *dlg,
- pjsip_transaction *tsx,
- pjsip_event *event);
-
-/*
- * Dialog state handlers.
- */
-static int (*dlg_state_handlers[])(struct pjsip_dlg *, pjsip_transaction *,
- pjsip_event *) =
-{
- &dlg_on_state_null,
- &dlg_on_state_incoming,
- &dlg_on_state_calling,
- &dlg_on_state_proceeding,
- &dlg_on_state_connecting,
- &dlg_on_state_established,
- &dlg_on_state_disconnected,
- &dlg_on_state_terminated,
-};
-
-/*
- * Dialog state names.
- */
-static const char* dlg_state_names[] =
-{
- "STATE_NULL",
- "STATE_INCOMING",
- "STATE_CALLING",
- "STATE_PROCEEDING",
- "STATE_CONNECTING",
- "STATE_ESTABLISHED",
- "STATE_DISCONNECTED",
- "STATE_TERMINATED",
-};
-
-
-/*
- * Get the dialog string state, normally for logging purpose.
- */
-const char *pjsip_dlg_state_str(pjsip_dlg_state_e state)
-{
- return dlg_state_names[state];
-}
-
-/* Lock dialog mutex. */
-static void lock_dialog(pjsip_dlg *dlg, struct dialog_lock_data *lck)
-{
- struct dialog_lock_data *prev;
-
- pj_mutex_lock(dlg->mutex);
- prev = pj_thread_local_get(pjsip_dlg_lock_tls_id);
- lck->prev = prev;
- lck->dlg = dlg;
- lck->is_alive = 1;
- pj_thread_local_set(pjsip_dlg_lock_tls_id, lck);
-}
-
-/* Carefully unlock dialog mutex, protect against situation when the dialog
- * has already been destroyed.
- */
-static pj_status_t unlock_dialog(pjsip_dlg *dlg, struct dialog_lock_data *lck)
-{
- pj_assert(pj_thread_local_get(pjsip_dlg_lock_tls_id) == lck);
- pj_assert(dlg == lck->dlg);
-
- pj_thread_local_set(pjsip_dlg_lock_tls_id, lck->prev);
- if (lck->is_alive)
- pj_mutex_unlock(dlg->mutex);
-
- return lck->is_alive ? 0 : -1;
-}
-
-/*
- * This is called by dialog's FSM to change dialog's state.
- */
-static void dlg_set_state( pjsip_dlg *dlg, pjsip_dlg_state_e state,
- pjsip_event *event)
-{
- PJ_UNUSED_ARG(event);
-
- PJ_LOG(4, (dlg->obj_name, "State %s-->%s (ev=%s, src=%s, data=%p)",
- pjsip_dlg_state_str(dlg->state), pjsip_dlg_state_str(state),
- event ? pjsip_event_str(event->type) : "",
- event ? pjsip_event_str(event->src_type) : "",
- event ? event->src.data : NULL));
-
- dlg->state = state;
- dlg->handle_tsx_event = dlg_state_handlers[state];
-}
-
-/*
- * Invoke dialog's callback.
- * This function is called by dialog's FSM, and interpret the event and call
- * the corresponding callback registered by application.
- */
-static void dlg_call_dlg_callback( pjsip_dlg *dlg, pjsip_dlg_event_e dlg_event,
- pjsip_event *event )
-{
- pjsip_dlg_callback *cb = dlg->ua->dlg_cb;
- if (!cb) {
- PJ_LOG(4,(dlg->obj_name, "Can not call callback (none registered)."));
- return;
- }
-
- /* Low level event: call the all-events callback. */
- if (cb->on_all_events) {
- (*cb->on_all_events)(dlg, dlg_event, event);
- }
-
- /* Low level event: call the tx/rx callback if this is a tx/rx event. */
- if (event->type == PJSIP_EVENT_BEFORE_TX && cb->on_before_tx)
- {
- (*cb->on_before_tx)(dlg, event->obj.tsx, event->src.tdata, event->data.long_data);
- }
- else if (event->type == PJSIP_EVENT_TX_MSG &&
- event->src_type == PJSIP_EVENT_TX_MSG && cb->on_tx_msg)
- {
- (*cb->on_tx_msg)(dlg, event->obj.tsx, event->src.tdata);
- }
- else if (event->type == PJSIP_EVENT_RX_MSG &&
- event->src_type == PJSIP_EVENT_RX_MSG && cb->on_rx_msg) {
- (*cb->on_rx_msg)(dlg, event->obj.tsx, event->src.rdata);
- }
-
- /* Now trigger high level events.
- * High level event should only occurs when dialog's state has changed,
- * except for on_provisional, which may be called multiple times whenever
- * response message is sent
- */
- if (dlg->state == PJSIP_DIALOG_STATE_PROCEEDING &&
- (event->type== PJSIP_EVENT_TSX_STATE_CHANGED) &&
- event->obj.tsx == dlg->invite_tsx)
- {
- /* Sent/received provisional responses. */
- if (cb->on_provisional)
- (*cb->on_provisional)(dlg, event->obj.tsx, event);
- }
-
- if (dlg_event == PJSIP_DIALOG_EVENT_MID_CALL_REQUEST) {
- if (cb->on_mid_call_events)
- (*cb->on_mid_call_events)(dlg, event);
- return;
- }
-
- if (dlg_event != PJSIP_DIALOG_EVENT_STATE_CHANGED)
- return;
-
- if (dlg->state == PJSIP_DIALOG_STATE_INCOMING) {
-
- /* New incoming dialog. */
- pj_assert (event->src_type == PJSIP_EVENT_RX_MSG);
- (*cb->on_incoming)(dlg, event->obj.tsx, event->src.rdata);
-
- } else if (dlg->state == PJSIP_DIALOG_STATE_CALLING) {
-
- /* Dialog has just sent the first INVITE. */
- if (cb->on_calling) {
- (*cb->on_calling)(dlg, event->obj.tsx, event->src.tdata);
- }
-
- } else if (dlg->state == PJSIP_DIALOG_STATE_DISCONNECTED) {
-
- if (cb->on_disconnected)
- (*cb->on_disconnected)(dlg, event);
-
- } else if (dlg->state == PJSIP_DIALOG_STATE_TERMINATED) {
-
- if (cb->on_terminated)
- (*cb->on_terminated)(dlg);
-
- pjsip_ua_destroy_dialog(dlg);
-
- } else if (dlg->state == PJSIP_DIALOG_STATE_CONNECTING) {
-
- if (cb->on_connecting)
- (*cb->on_connecting)(dlg, event);
-
- } else if (dlg->state == PJSIP_DIALOG_STATE_ESTABLISHED) {
-
- if (cb->on_established)
- (*cb->on_established)(dlg, event);
- }
-}
-
-/*
- * This callback receives event from the transaction layer (via User Agent),
- * or from dialog timer (for 200/INVITE or ACK retransmission).
- */
-void pjsip_dlg_on_tsx_event( pjsip_dlg *dlg,
- pjsip_transaction *tsx,
- pjsip_event *event)
-{
- int status = 0;
- struct dialog_lock_data lck;
-
- PJ_LOG(4, (dlg->obj_name, "state=%s (evt=%s, src=%s)",
- pjsip_dlg_state_str(dlg->state),
- pjsip_event_str(event->type),
- pjsip_event_str(event->src_type)));
-
-
- lock_dialog(dlg, &lck);
-
- status = dlg_on_all_state_pre( dlg, tsx, event);
-
- if (status==0) {
- status = (*dlg->handle_tsx_event)(dlg, tsx, event);
- }
- if (status==0) {
- status = dlg_on_all_state_post( dlg, tsx, event);
- }
-
- unlock_dialog(dlg, &lck);
-}
-
-/*
- * This function contains common processing in all states, and it is called
- * before the FSM is invoked.
- */
-static int dlg_on_all_state_pre( pjsip_dlg *dlg,
- pjsip_transaction *tsx,
- pjsip_event *event)
-{
- PJ_UNUSED_ARG(event)
-
- if (event->type != PJSIP_EVENT_TSX_STATE_CHANGED)
- return 0;
-
- if (tsx && (tsx->state==PJSIP_TSX_STATE_CALLING ||
- tsx->state==PJSIP_TSX_STATE_TRYING))
- {
- ++dlg->pending_tsx_count;
-
- } else if (tsx && tsx->state==PJSIP_TSX_STATE_DESTROYED)
- {
- --dlg->pending_tsx_count;
- if (tsx == dlg->invite_tsx)
- dlg->invite_tsx = NULL;
- }
-
- if (tsx->method.id == PJSIP_INVITE_METHOD) {
- tsx->handle_ack = 1;
- }
- return 0;
-}
-
-
-/*
- * This function contains common processing in all states, and it is called
- * after the FSM is invoked.
- */
-static int dlg_on_all_state_post( pjsip_dlg *dlg,
- pjsip_transaction *tsx,
- pjsip_event *event)
-{
- PJ_UNUSED_ARG(event)
-
- if (tsx && tsx->state==PJSIP_TSX_STATE_DESTROYED) {
- if (dlg->pending_tsx_count == 0 &&
- dlg->state != PJSIP_DIALOG_STATE_CONNECTING &&
- dlg->state != PJSIP_DIALOG_STATE_ESTABLISHED &&
- dlg->state != PJSIP_DIALOG_STATE_TERMINATED)
- {
- dlg_set_state(dlg, PJSIP_DIALOG_STATE_TERMINATED, event);
- dlg_call_dlg_callback(dlg, PJSIP_DIALOG_EVENT_STATE_CHANGED, event);
- return -1;
- }
- }
-
- return 0;
-}
-
-
-/*
- * Internal function to initialize dialog, contains common initialization
- * for both UAS and UAC dialog.
- */
-static pj_status_t dlg_init( pjsip_dlg *dlg )
-{
- /* Init mutex. */
- dlg->mutex = pj_mutex_create(dlg->pool, "mdlg%p", 0);
- if (!dlg->mutex) {
- PJ_PERROR((dlg->obj_name, "pj_mutex_create()"));
- return -1;
- }
-
- /* Init route-set (Initially empty) */
- pj_list_init(&dlg->route_set);
-
- /* Init auth credential list. */
- pj_list_init(&dlg->auth_sess);
-
- return PJ_SUCCESS;
-}
-
-/*
- * This one is called just before dialog is destroyed.
- * It is called while mutex is held.
- */
-PJ_DEF(void) pjsip_on_dialog_destroyed( pjsip_dlg *dlg )
-{
- struct dialog_lock_data *lck;
-
- //PJ_TODO(CHECK_THIS);
- pj_assert(dlg->pending_tsx_count == 0);
-
- /* Mark dialog as dead. */
- lck = pj_thread_local_get(pjsip_dlg_lock_tls_id);
- while (lck) {
- if (lck->dlg == dlg)
- lck->is_alive = 0;
- lck = lck->prev;
- }
-}
-
-/*
- * Initialize dialog from the received request.
- * This is an internal function which is called by the User Agent (sip_ua.c),
- * and it will initialize most of dialog's properties with values from the
- * received message.
- */
-pj_status_t pjsip_dlg_init_from_rdata( pjsip_dlg *dlg, pjsip_rx_data *rdata )
-{
- pjsip_msg *msg = rdata->msg;
- pjsip_to_hdr *to;
- pjsip_contact_hdr *contact;
- pjsip_name_addr *name_addr;
- pjsip_url *url;
- unsigned flag;
- pjsip_event event;
-
- pj_assert(dlg && rdata);
-
- PJ_LOG(5, (dlg->obj_name, "init_from_rdata(%p)", rdata));
-
- /* Must be an INVITE request. */
- pj_assert(msg->type == PJSIP_REQUEST_MSG &&
- msg->line.req.method.id == PJSIP_INVITE_METHOD);
-
- /* Init general dialog data. */
- if (dlg_init(dlg) != PJ_SUCCESS) {
- return -1;
- }
-
- /* Get the To header. */
- to = rdata->to;
-
- /* Copy URI in the To header as our local URI. */
- dlg->local.info = pjsip_hdr_clone( dlg->pool, to);
-
- /* Set tag in the local info. */
- dlg->local.info->tag = dlg->local.tag;
-
- /* Create local Contact to be advertised in the response.
- * At the moment, just copy URI from the local URI as our contact.
- */
- dlg->local.contact = pjsip_contact_hdr_create( dlg->pool );
- dlg->local.contact->star = 0;
- name_addr = (pjsip_name_addr *)dlg->local.info->uri;
- dlg->local.contact->uri = (pjsip_uri*) name_addr;
- url = (pjsip_url*) name_addr->uri;
- //url->port = rdata->via->sent_by.port;
- //url->port = pj_sockaddr_get_port( pjsip_transport_get_local_addr(rdata->transport) );
-
- /* Save remote URI. */
- dlg->remote.info = pjsip_hdr_clone( dlg->pool, rdata->from );
- pjsip_fromto_set_to( dlg->remote.info );
- pj_strdup( dlg->pool, &dlg->remote.tag, &rdata->from->tag );
-
- /* Save remote Contact. */
- contact = pjsip_msg_find_hdr( msg, PJSIP_H_CONTACT, NULL);
- if (contact) {
- dlg->remote.contact = pjsip_hdr_clone( dlg->pool, contact );
- } else {
- PJ_LOG(3,(dlg->obj_name, "No Contact header in INVITE from %s",
- pj_sockaddr_get_str_addr(&rdata->addr)));
- dlg->remote.contact = pjsip_contact_hdr_create( dlg->pool );
- dlg->remote.contact->uri = dlg->remote.info->uri;
- }
-
- /* Save Call-ID. */
- dlg->call_id = pjsip_cid_hdr_create(dlg->pool);
- pj_strdup( dlg->pool, &dlg->call_id->id, &rdata->call_id );
-
- /* Initialize local CSeq and save remote CSeq.*/
- dlg->local.cseq = rdata->timestamp.sec & 0xFFFF;
- dlg->remote.cseq = rdata->cseq->cseq;
-
- /* Secure? */
- flag = pjsip_transport_get_flag(rdata->transport);
- dlg->secure = (flag & PJSIP_TRANSPORT_SECURE) != 0;
-
- /* Initial state is NULL. */
- event.type = event.src_type = PJSIP_EVENT_RX_MSG;
- event.src.rdata = rdata;
- dlg_set_state(dlg, PJSIP_DIALOG_STATE_NULL, &event);
-
- PJ_LOG(5, (dlg->obj_name, "init_from_rdata(%p) complete", rdata));
- return PJ_SUCCESS;
-}
-
-/*
- * Set the contact details.
- */
-PJ_DEF(pj_status_t) pjsip_dlg_set_contact( pjsip_dlg *dlg,
- const pj_str_t *contact )
-{
- pjsip_uri *local_uri;
- pj_str_t tmp;
-
- pj_strdup_with_null(dlg->pool, &tmp, contact);
- local_uri = pjsip_parse_uri( dlg->pool, tmp.ptr, tmp.slen,
- PJSIP_PARSE_URI_AS_NAMEADDR);
- if (local_uri == NULL) {
- PJ_LOG(2, (dlg->obj_name, "set_contact: invalid URI"));
- return -1;
- }
-
- dlg->local.contact->star = 0;
- dlg->local.contact->uri = local_uri;
- return 0;
-}
-
-/*
- * Set route set.
- */
-PJ_DEF(pj_status_t) pjsip_dlg_set_route_set( pjsip_dlg *dlg,
- const pjsip_route_hdr *route_set )
-{
- pjsip_route_hdr *hdr;
-
- pj_list_init(&dlg->route_set);
- hdr = route_set->next;
- while (hdr != route_set) {
- pjsip_route_hdr *cloned = pjsip_hdr_clone(dlg->pool, hdr);
- pj_list_insert_before( &dlg->route_set, cloned);
- hdr = hdr->next;
- }
- return 0;
-}
-
-/*
- * Set route set without cloning the header.
- */
-PJ_DEF(pj_status_t) pjsip_dlg_set_route_set_np( pjsip_dlg *dlg,
- pjsip_route_hdr *route_set)
-{
- pjsip_route_hdr *hdr;
-
- pj_list_init(&dlg->route_set);
- hdr = route_set->next;
- while (hdr != route_set) {
- pj_list_insert_before( &dlg->route_set, hdr);
- hdr = hdr->next;
- }
- return 0;
-}
-
-/*
- * Application calls this function when it wants to initiate an outgoing
- * dialog (incoming dialogs are created automatically by UA when it receives
- * INVITE, by calling pjsip_dlg_init_from_rdata()).
- * This function should initialize most of the dialog's properties.
- */
-PJ_DEF(pj_status_t) pjsip_dlg_init( pjsip_dlg *dlg,
- const pj_str_t *c_local_info,
- const pj_str_t *c_remote_info,
- const pj_str_t *c_target)
-{
- pj_time_val tv;
- pjsip_event event;
- pj_str_t buf;
-
- if (!dlg || !c_local_info || !c_remote_info) {
- pj_assert(dlg && c_local_info && c_remote_info);
- return -1;
- }
-
- PJ_LOG(5, (dlg->obj_name, "initializing"));
-
- /* Init general dialog */
- if (dlg_init(dlg) != PJ_SUCCESS) {
- return -1;
- }
-
- /* Duplicate local info. */
- pj_strdup_with_null( dlg->pool, &buf, c_local_info);
-
- /* Build local URI. */
- dlg->local.target = pjsip_parse_uri(dlg->pool, buf.ptr, buf.slen,
- PJSIP_PARSE_URI_AS_NAMEADDR);
- if (dlg->local.target == NULL) {
- PJ_LOG(2, (dlg->obj_name,
- "pjsip_dlg_init: invalid local URI %s", buf.ptr));
- return -1;
- }
-
- /* Set local URI. */
- dlg->local.info = pjsip_from_hdr_create(dlg->pool);
- dlg->local.info->uri = dlg->local.target;
- dlg->local.info->tag = dlg->local.tag;
-
- /* Create local Contact to be advertised in the response. */
- dlg->local.contact = pjsip_contact_hdr_create( dlg->pool );
- dlg->local.contact->star = 0;
- dlg->local.contact->uri = dlg->local.target;
-
- /* Set remote URI. */
- dlg->remote.info = pjsip_to_hdr_create(dlg->pool);
-
- /* Duplicate to buffer. */
- pj_strdup_with_null( dlg->pool, &buf, c_remote_info);
-
- /* Build remote info. */
- dlg->remote.info->uri = pjsip_parse_uri( dlg->pool, buf.ptr, buf.slen,
- PJSIP_PARSE_URI_AS_NAMEADDR);
- if (dlg->remote.info->uri == NULL) {
- PJ_LOG(2, (dlg->obj_name,
- "pjsip_dlg_init: invalid remote URI %s", buf.ptr));
- return -1;
- }
-
- /* Set remote Contact initially equal to the remote URI. */
- dlg->remote.contact = pjsip_contact_hdr_create(dlg->pool);
- dlg->remote.contact->star = 0;
- dlg->remote.contact->uri = dlg->remote.info->uri;
-
- /* Set initial remote target. */
- if (c_target != NULL) {
- pj_strdup_with_null( dlg->pool, &buf, c_target);
- dlg->remote.target = pjsip_parse_uri( dlg->pool, buf.ptr, buf.slen, 0);
- if (dlg->remote.target == NULL) {
- PJ_LOG(2, (dlg->obj_name,
- "pjsip_dlg_init: invalid remote target %s", buf.ptr));
- return -1;
- }
- } else {
- dlg->remote.target = dlg->remote.info->uri;
- }
-
- /* Create globally unique Call-ID */
- dlg->call_id = pjsip_cid_hdr_create(dlg->pool);
- pj_create_unique_string( dlg->pool, &dlg->call_id->id );
-
- /* Local and remote CSeq */
- pj_gettimeofday(&tv);
- dlg->local.cseq = tv.sec & 0xFFFF;
- dlg->remote.cseq = 0;
-
- /* Initial state is NULL. */
- event.type = event.src_type = PJSIP_EVENT_TX_MSG;
- event.src.data = NULL;
- dlg_set_state(dlg, PJSIP_DIALOG_STATE_NULL, &event);
-
- /* Done. */
- PJ_LOG(4, (dlg->obj_name, "%s dialog initialized, From: %.*s, To: %.*s",
- pjsip_role_name(dlg->role),
- c_local_info->slen, c_local_info->ptr,
- c_remote_info->slen, c_remote_info->ptr));
-
- return PJ_SUCCESS;
-}
-
-/*
- * Set credentials.
- */
-PJ_DEF(pj_status_t) pjsip_dlg_set_credentials( pjsip_dlg *dlg,
- int count,
- const pjsip_cred_info *cred)
-{
- if (count > 0) {
- dlg->cred_info = pj_pool_alloc(dlg->pool, count * sizeof(pjsip_cred_info));
- pj_memcpy(dlg->cred_info, cred, count * sizeof(pjsip_cred_info));
- }
- dlg->cred_count = count;
- return 0;
-}
-
-/*
- * Create a new request within dialog (i.e. after the dialog session has been
- * established). The construction of such requests follows the rule in
- * RFC3261 section 12.2.1.
- */
-static void dlg_create_request_throw( pjsip_tx_data **p_tdata,
- pjsip_dlg *dlg,
- const pjsip_method *method,
- int cseq )
-{
- pjsip_tx_data *tdata;
- pjsip_contact_hdr *contact;
- pjsip_route_hdr *route, *end_list;
-
- /* Contact Header field.
- * Contact can only be present in requests that establish dialog (in the
- * core SIP spec, only INVITE).
- */
- if (method->id == PJSIP_INVITE_METHOD)
- contact = dlg->local.contact;
- else
- contact = NULL;
-
- tdata = pjsip_endpt_create_request_from_hdr( dlg->ua->endpt,
- method,
- dlg->remote.target,
- dlg->local.info,
- dlg->remote.info,
- contact,
- dlg->call_id,
- cseq,
- NULL);
- if (!tdata) {
- PJ_THROW(1);
- return;
- }
-
- /* Just copy dialog route-set to Route header.
- * The transaction will do the processing as specified in Section 12.2.1
- * of RFC 3261 in function tsx_process_route() in sip_transaction.c.
- */
- route = dlg->route_set.next;
- end_list = &dlg->route_set;
- for (; route != end_list; route = route->next ) {
- pjsip_route_hdr *r;
- r = pjsip_hdr_shallow_clone( tdata->pool, route );
- pjsip_routing_hdr_set_route(r);
- pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr*)r);
- }
-
- /* Copy authorization headers. */
- pjsip_auth_init_req( dlg->pool, tdata, &dlg->auth_sess,
- dlg->cred_count, dlg->cred_info);
-
- *p_tdata = tdata;
-}
-
-
-/*
- * This function is called by application to create new outgoing request
- * message for this dialog. After the request is created, application can
- * modify the message (such adding headers), and eventually send the request.
- */
-PJ_DEF(pjsip_tx_data*) pjsip_dlg_create_request( pjsip_dlg *dlg,
- const pjsip_method *method,
- int cseq)
-{
- PJ_USE_EXCEPTION;
- struct dialog_lock_data lck;
- pjsip_tx_data *tdata = NULL;
-
- pj_assert(dlg != NULL && method != NULL);
- if (!dlg || !method) {
- return NULL;
- }
-
- PJ_LOG(5, (dlg->obj_name, "Creating request"));
-
- /* Lock dialog. */
- lock_dialog(dlg, &lck);
-
- /* Use outgoing CSeq and increment it by one. */
- if (cseq < 0)
- cseq = dlg->local.cseq + 1;
-
- PJ_LOG(5, (dlg->obj_name, "creating request %.*s cseq=%d",
- method->name.slen, method->name.ptr, cseq));
-
- /* Create the request. */
- PJ_TRY {
- dlg_create_request_throw(&tdata, dlg, method, cseq);
- PJ_LOG(5, (dlg->obj_name, "request data %s created", tdata->obj_name));
- }
- PJ_DEFAULT {
- /* Failed! Delete transmit data. */
- if (tdata) {
- pjsip_tx_data_dec_ref( tdata );
- tdata = NULL;
- }
- }
- PJ_END;
-
- /* Unlock dialog. */
- unlock_dialog(dlg, &lck);
-
- return tdata;
-}
-
-/*
- * Sends request.
- * Select the transport for the request message
- */
-static pj_status_t dlg_send_request( pjsip_dlg *dlg, pjsip_tx_data *tdata )
-{
- pjsip_transaction *tsx;
- pj_status_t status = PJ_SUCCESS;
- struct dialog_lock_data lck;
-
- pj_assert(dlg != NULL && tdata != NULL);
- if (!dlg || !tdata) {
- return -1;
- }
-
- PJ_LOG(5, (dlg->obj_name, "sending request %s", tdata->obj_name));
-
- /* Lock dialog. */
- lock_dialog(dlg, &lck);
-
- /* Create a new transaction. */
- tsx = pjsip_endpt_create_tsx( dlg->ua->endpt );
- if (!tsx) {
- unlock_dialog(dlg, &lck);
- return -1;
- }
-
- PJ_LOG(4, (dlg->obj_name, "Created new UAC transaction: %s", tsx->obj_name));
-
- /* Initialize transaction */
- tsx->module_data[dlg->ua->mod_id] = dlg;
- status = pjsip_tsx_init_uac( tsx, tdata );
- if (status != PJ_SUCCESS) {
- unlock_dialog(dlg, &lck);
- pjsip_endpt_destroy_tsx( dlg->ua->endpt, tsx );
- return -1;
- }
- pjsip_endpt_register_tsx( dlg->ua->endpt, tsx );
-
- /* Start the transaction. */
- pjsip_tsx_on_tx_msg(tsx, tdata);
-
- /* Unlock dialog. */
- unlock_dialog(dlg, &lck);
-
- return status;
-}
-
-/*
- * This function can be called by application to send ANY outgoing message
- * to remote party.
- */
-PJ_DEF(pj_status_t) pjsip_dlg_send_msg( pjsip_dlg *dlg, pjsip_tx_data *tdata )
-{
- pj_status_t status;
- int tsx_status;
- struct dialog_lock_data lck;
-
- pj_assert(dlg != NULL && tdata != NULL);
- if (!dlg || !tdata) {
- return -1;
- }
-
- lock_dialog(dlg, &lck);
-
- if (tdata->msg->type == PJSIP_REQUEST_MSG) {
- int request_cseq;
- pjsip_msg *msg = tdata->msg;
- pjsip_cseq_hdr *cseq_hdr;
-
- switch (msg->line.req.method.id) {
- case PJSIP_CANCEL_METHOD:
-
- /* Check the INVITE transaction state. */
- tsx_status = dlg->invite_tsx->status_code;
- if (tsx_status >= 200) {
- /* Already terminated. Can't cancel. */
- status = -1;
- goto on_return;
- }
-
- /* If we've got provisional response, then send CANCEL and wait for
- * the response to INVITE to arrive. Otherwise just send CANCEL and
- * terminate the INVITE.
- */
- if (tsx_status < 100) {
- pjsip_tsx_terminate( dlg->invite_tsx,
- PJSIP_SC_REQUEST_TERMINATED);
- status = 0;
- goto on_return;
- }
-
- status = 0;
- request_cseq = dlg->invite_tsx->cseq;
- break;
-
- case PJSIP_ACK_METHOD:
- /* Sending ACK outside of transaction is not supported at present! */
- pj_assert(0);
- status = 0;
- request_cseq = dlg->local.cseq;
- break;
-
- case PJSIP_INVITE_METHOD:
- /* For an initial INVITE, reset dialog state to NULL so we get
- * 'normal' UAC notifications such as on_provisional(), etc.
- * Initial INVITE is the request that is sent when the dialog has
- * not been established yet. It's not necessarily the first INVITE
- * sent, as when the Authorization fails, subsequent INVITE are also
- * considered as an initial INVITE.
- */
- if (dlg->state != PJSIP_DIALOG_STATE_ESTABLISHED) {
- /* Set state to NULL. */
- dlg_set_state(dlg, PJSIP_DIALOG_STATE_NULL, NULL);
-
- } else {
- /* This is a re-INVITE */
- }
- status = 0;
- request_cseq = dlg->local.cseq + 1;
- break;
-
- default:
- status = 0;
- request_cseq = dlg->local.cseq + 1;
- break;
- }
-
- if (status != 0)
- goto on_return;
-
- /* Update dialog's local CSeq, if necessary. */
- if (request_cseq != dlg->local.cseq)
- dlg->local.cseq = request_cseq;
-
- /* Update CSeq header in the request. */
- cseq_hdr = (pjsip_cseq_hdr*) pjsip_msg_find_hdr( tdata->msg,
- PJSIP_H_CSEQ, NULL);
- pj_assert(cseq_hdr != NULL);
-
- /* Update the CSeq */
- cseq_hdr->cseq = request_cseq;
-
- /* Force the whole message to be re-printed. */
- pjsip_tx_data_invalidate_msg( tdata );
-
- /* Now send the request. */
- status = dlg_send_request(dlg, tdata);
-
- } else {
- /*
- * This is only valid for sending response to INVITE!
- */
- pjsip_cseq_hdr *cseq_hdr;
-
- if (dlg->invite_tsx == NULL || dlg->invite_tsx->status_code >= 200) {
- status = -1;
- goto on_return;
- }
-
- cseq_hdr = (pjsip_cseq_hdr*) pjsip_msg_find_hdr( tdata->msg,
- PJSIP_H_CSEQ, NULL);
- pj_assert(cseq_hdr);
-
- if (cseq_hdr->method.id != PJSIP_INVITE_METHOD) {
- status = -1;
- goto on_return;
- }
-
- pj_assert(cseq_hdr->cseq == dlg->invite_tsx->cseq);
-
- pjsip_tsx_on_tx_msg(dlg->invite_tsx, tdata);
- status = 0;
- }
-
-on_return:
- /* Unlock dialog. */
- unlock_dialog(dlg, &lck);
-
- /* Whatever happen delete the message. */
- pjsip_tx_data_dec_ref( tdata );
-
- return status;
-}
-
-/*
- * Sends outgoing invitation.
- */
-PJ_DEF(pjsip_tx_data*) pjsip_dlg_invite( pjsip_dlg *dlg )
-{
- pjsip_method method;
- struct dialog_lock_data lck;
- const pjsip_allow_hdr *allow_hdr;
- pjsip_tx_data *tdata;
-
- pj_assert(dlg != NULL);
- if (!dlg) {
- return NULL;
- }
-
- PJ_LOG(4, (dlg->obj_name, "request to send invitation"));
-
- /* Lock dialog. */
- lock_dialog(dlg, &lck);
-
- /* Create request. */
- pjsip_method_set( &method, PJSIP_INVITE_METHOD);
- tdata = pjsip_dlg_create_request( dlg, &method, -1 );
- if (tdata == NULL) {
- unlock_dialog(dlg, &lck);
- return NULL;
- }
-
- /* Invite SHOULD contain "Allow" header. */
- allow_hdr = pjsip_endpt_get_allow_hdr( dlg->ua->endpt );
- if (allow_hdr) {
- pjsip_msg_add_hdr( tdata->msg,
- pjsip_hdr_shallow_clone( tdata->pool, allow_hdr));
- }
-
- /* Unlock dialog. */
- unlock_dialog(dlg, &lck);
-
- return tdata;
-}
-
-/*
- * Cancel pending outgoing dialog invitation.
- */
-PJ_DEF(pjsip_tx_data*) pjsip_dlg_cancel( pjsip_dlg *dlg )
-{
- pjsip_tx_data *tdata = NULL;
- struct dialog_lock_data lck;
-
- pj_assert(dlg != NULL);
- if (!dlg) {
- return NULL;
- }
-
- PJ_LOG(4, (dlg->obj_name, "request to cancel invitation"));
-
- lock_dialog(dlg, &lck);
-
- /* Check the INVITE transaction. */
- if (dlg->invite_tsx == NULL || dlg->role != PJSIP_ROLE_UAC) {
- PJ_LOG(2, (dlg->obj_name, "pjsip_dlg_cancel failed: "
- "no INVITE transaction found"));
- goto on_return;
- }
-
- /* Construct the CANCEL request. */
- tdata = pjsip_endpt_create_cancel( dlg->ua->endpt,
- dlg->invite_tsx->last_tx );
- if (tdata == NULL) {
- PJ_LOG(2, (dlg->obj_name, "pjsip_dlg_cancel failed: "
- "unable to construct request"));
- goto on_return;
- }
-
- /* Add reference counter to tdata. */
- pjsip_tx_data_add_ref(tdata);
-
-on_return:
- unlock_dialog(dlg, &lck);
- return tdata;
-}
-
-
-/*
- * Answer incoming dialog invitation, with either provisional responses
- * or a final response.
- */
-PJ_DEF(pjsip_tx_data*) pjsip_dlg_answer( pjsip_dlg *dlg, int code )
-{
- pjsip_tx_data *tdata = NULL;
- pjsip_msg *msg;
- struct dialog_lock_data lck;
-
- pj_assert(dlg != NULL);
- if (!dlg) {
- return NULL;
- }
-
- PJ_LOG(4, (dlg->obj_name, "pjsip_dlg_answer: code=%d", code));
-
- /* Lock dialog. */
- lock_dialog(dlg, &lck);
-
- /* Must have pending INVITE. */
- if (dlg->invite_tsx == NULL) {
- PJ_LOG(2, (dlg->obj_name, "pjsip_dlg_answer: no INVITE transaction found"));
- goto on_return;
- }
- /* Must be UAS. */
- if (dlg->role != PJSIP_ROLE_UAS) {
- PJ_LOG(2, (dlg->obj_name, "pjsip_dlg_answer: not UAS"));
- goto on_return;
- }
- /* Must have not answered with final response before. */
- if (dlg->invite_tsx->status_code >= 200) {
- PJ_LOG(2, (dlg->obj_name, "pjsip_dlg_answer: transaction already terminated "
- "with status %d", dlg->invite_tsx->status_code));
- goto on_return;
- }
-
- /* Get transmit data and the message.
- * We will rewrite the message with a new status code.
- */
- only if tdata is not pending!!!
- tdata = dlg->invite_tsx->last_tx;
- msg = tdata->msg;
-
- /* Set status code and reason phrase. */
- if (code < 100 || code >= 700) code = 500;
- msg->line.status.code = code;
- msg->line.status.reason = *pjsip_get_status_text(code);
-
- /* For 2xx response, Contact and Record-Route must be added. */
- if (PJSIP_IS_STATUS_IN_CLASS(code,200)) {
- const pjsip_allow_hdr *allow_hdr;
-
- if (pjsip_msg_find_hdr(msg, PJSIP_H_CONTACT, NULL) == NULL) {
- pjsip_contact_hdr *contact;
- contact = pjsip_hdr_shallow_clone( tdata->pool, dlg->local.contact);
- pjsip_msg_add_hdr( msg, (pjsip_hdr*)contact );
- }
-
- /* 2xx response MUST contain "Allow" header. */
- allow_hdr = pjsip_endpt_get_allow_hdr( dlg->ua->endpt );
- if (allow_hdr) {
- pjsip_msg_add_hdr( msg, pjsip_hdr_shallow_clone( tdata->pool, allow_hdr));
- }
- }
-
- /* for all but 100 responses, To-tag must be set. */
- if (code != 100) {
- pjsip_to_hdr *to;
- to = pjsip_msg_find_hdr( msg, PJSIP_H_TO, NULL);
- to->tag = dlg->local.tag;
- }
-
- /* Reset packet buffer. */
- pjsip_tx_data_invalidate_msg(tdata);
-
- /* Add reference counter */
- pjsip_tx_data_add_ref(tdata);
-
-on_return:
-
- /* Unlock dialog. */
- unlock_dialog(dlg, &lck);
-
- return tdata;
-}
-
-
-/*
- * Send BYE request to terminate the dialog's session.
- */
-PJ_DEF(pjsip_tx_data*) pjsip_dlg_bye( pjsip_dlg *dlg )
-{
- pjsip_method method;
- struct dialog_lock_data lck;
- pjsip_tx_data *tdata;
-
- if (!dlg) {
- pj_assert(dlg != NULL);
- return NULL;
- }
-
- PJ_LOG(4, (dlg->obj_name, "request to terminate session"));
-
- lock_dialog(dlg, &lck);
-
- pjsip_method_set( &method, PJSIP_BYE_METHOD);
- tdata = pjsip_dlg_create_request( dlg, &method, -1 );
-
- unlock_dialog(dlg, &lck);
-
- return tdata;
-}
-
-/*
- * High level function to disconnect dialog's session. Depending on dialog's
- * state, this function will either send CANCEL, final response, or BYE to
- * trigger the disconnection. A status code must be supplied, which will be
- * sent if dialog will be transmitting a final response to INVITE.
- */
-PJ_DEF(pjsip_tx_data*) pjsip_dlg_disconnect( pjsip_dlg *dlg,
- int status_code )
-{
- pjsip_tx_data *tdata = NULL;
-
- pj_assert(dlg != NULL);
- if (!dlg) {
- return NULL;
- }
-
- switch (dlg->state) {
- case PJSIP_DIALOG_STATE_INCOMING:
- tdata = pjsip_dlg_answer(dlg, status_code);
- break;
-
- case PJSIP_DIALOG_STATE_CALLING:
- tdata = pjsip_dlg_cancel(dlg);
- break;
-
- case PJSIP_DIALOG_STATE_PROCEEDING:
- if (dlg->role == PJSIP_ROLE_UAC) {
- tdata = pjsip_dlg_cancel(dlg);
- } else {
- tdata = pjsip_dlg_answer(dlg, status_code);
- }
- break;
-
- case PJSIP_DIALOG_STATE_ESTABLISHED:
- tdata = pjsip_dlg_bye(dlg);
- break;
-
- default:
- PJ_LOG(4,(dlg->obj_name, "Invalid state %s in pjsip_dlg_disconnect()",
- dlg_state_names[dlg->state]));
- break;
- }
-
- return tdata;
-}
-
-/*
- * Handling of the receipt of 2xx/INVITE response.
- */
-static void dlg_on_recv_2xx_invite( pjsip_dlg *dlg,
- pjsip_event *event )
-{
- pjsip_msg *msg;
- pjsip_contact_hdr *contact;
- pjsip_hdr *hdr, *end_hdr;
- pjsip_method method;
- pjsip_tx_data *ack_tdata;
-
- /* Get the message */
- msg = event->src.rdata->msg;
-
- /* Update remote's tag information. */
- pj_strdup(dlg->pool, &dlg->remote.info->tag, &event->src.rdata->to_tag);
-
- /* Copy Contact information in the 2xx/INVITE response to dialog's.
- * remote contact
- */
- contact = pjsip_msg_find_hdr( msg, PJSIP_H_CONTACT, NULL);
- if (contact) {
- dlg->remote.contact = pjsip_hdr_clone( dlg->pool, contact );
- } else {
- /* duplicate contact from "From" header (?) */
- PJ_LOG(4,(dlg->obj_name, "Received 200/OK to INVITE with no Contact!"));
- dlg->remote.contact = pjsip_contact_hdr_create(dlg->pool);
- dlg->remote.contact->uri = dlg->remote.info->uri;
- }
-
- /* Copy Record-Route header (in reverse order) as dialog's route-set,
- * overwriting previous route-set, if any, even if the received route-set
- * is empty.
- */
- pj_list_init(&dlg->route_set);
- end_hdr = &msg->hdr;
- for (hdr = msg->hdr.prev; hdr!=end_hdr; hdr = hdr->prev) {
- if (hdr->type == PJSIP_H_RECORD_ROUTE) {
- pjsip_route_hdr *r;
- r = pjsip_hdr_clone(dlg->pool, hdr);
- pjsip_routing_hdr_set_route(r);
- pj_list_insert_before(&dlg->route_set, r);
- }
- }
-
- /* On receipt of 200/INVITE response, send ACK.
- * This ack must be saved and retransmitted whenever we receive
- * 200/INVITE retransmission, until 64*T1 seconds elapsed.
- */
- pjsip_method_set( &method, PJSIP_ACK_METHOD);
- ack_tdata = pjsip_dlg_create_request( dlg, &method, dlg->invite_tsx->cseq);
- if (ack_tdata == NULL) {
- //PJ_TODO(HANDLE_CREATE_ACK_FAILURE)
- PJ_LOG(2, (dlg->obj_name, "Error sending ACK msg: can't create request"));
- return;
- }
-
- /* Send with the transaction. */
- pjsip_tsx_on_tx_ack( dlg->invite_tsx, ack_tdata);
-
- /* Decrement reference counter because pjsip_dlg_create_request
- * automatically increments the request.
- */
- pjsip_tx_data_dec_ref( ack_tdata );
-}
-
-/*
- * State NULL, before any events have been received.
- */
-static int dlg_on_state_null( pjsip_dlg *dlg,
- pjsip_transaction *tsx,
- pjsip_event *event)
-{
- if (event->type == PJSIP_EVENT_TSX_STATE_CHANGED &&
- event->src_type == PJSIP_EVENT_RX_MSG)
- {
- pjsip_hdr *hdr, *hdr_list;
-
- pj_assert(tsx->method.id == PJSIP_INVITE_METHOD);
-
- /* Save the INVITE transaction. */
- dlg->invite_tsx = tsx;
-
- /* Change state to INCOMING */
- dlg_set_state(dlg, PJSIP_DIALOG_STATE_INCOMING, event);
-
- /* Create response buffer. */
- tsx->last_tx = pjsip_endpt_create_response( dlg->ua->endpt, event->src.rdata, 100);
- pjsip_tx_data_add_ref(tsx->last_tx);
-
- /* Copy the Record-Route headers into dialog's route_set, maintaining
- * the order.
- */
- pj_list_init(&dlg->route_set);
- hdr_list = &event->src.rdata->msg->hdr;
- hdr = hdr_list->next;
- while (hdr != hdr_list) {
- if (hdr->type == PJSIP_H_RECORD_ROUTE) {
- pjsip_route_hdr *route;
- route = pjsip_hdr_clone(dlg->pool, hdr);
- pjsip_routing_hdr_set_route(route);
- pj_list_insert_before(&dlg->route_set, route);
- }
- hdr = hdr->next;
- }
-
- /* Notify application. */
- dlg_call_dlg_callback(dlg, PJSIP_DIALOG_EVENT_STATE_CHANGED, event);
-
- } else if (event->type == PJSIP_EVENT_TSX_STATE_CHANGED &&
- event->src_type == PJSIP_EVENT_TX_MSG)
- {
- pj_assert(tsx->method.id == PJSIP_INVITE_METHOD);
-
- /* Save the INVITE transaction. */
- dlg->invite_tsx = tsx;
-
- /* Change state to CALLING. */
- dlg_set_state(dlg, PJSIP_DIALOG_STATE_CALLING, event);
-
- /* Notify application. */
- dlg_call_dlg_callback(dlg, PJSIP_DIALOG_EVENT_STATE_CHANGED, event);
-
- } else {
- dlg_call_dlg_callback(dlg, PJSIP_DIALOG_EVENT_OTHER, event);
- }
-
- return 0;
-}
-
-/*
- * State INCOMING is after the (callee) dialog has been initialized with
- * the incoming request, but before any responses is sent by the dialog.
- */
-static int dlg_on_state_incoming( pjsip_dlg *dlg,
- pjsip_transaction *tsx,
- pjsip_event *event)
-{
- return dlg_on_state_proceeding_callee( dlg, tsx, event );
-}
-
-/*
- * State CALLING is after the (caller) dialog has sent outgoing invitation
- * but before any responses are received.
- */
-static int dlg_on_state_calling( pjsip_dlg *dlg,
- pjsip_transaction *tsx,
- pjsip_event *event)
-{
- if (tsx == dlg->invite_tsx) {
- return dlg_on_state_proceeding_caller( dlg, tsx, event );
- }
- return 0;
-}
-
-/*
- * State PROCEEDING is after provisional response is received.
- * Since the processing is similar to state CALLING, this function is also
- * called for CALLING state.
- */
-static int dlg_on_state_proceeding_caller( pjsip_dlg *dlg,
- pjsip_transaction *tsx,
- pjsip_event *event)
-{
- int dlg_is_terminated = 0;
-
- /* We only care about our INVITE transaction.
- * Ignore other transaction progression (such as CANCEL).
- */
- if (tsx != dlg->invite_tsx) {
- dlg_call_dlg_callback(dlg, PJSIP_DIALOG_EVENT_OTHER, event);
- return 0;
- }
-
- if (event->type == PJSIP_EVENT_TSX_STATE_CHANGED) {
- switch (tsx->state) {
- case PJSIP_TSX_STATE_PROCEEDING:
- if (dlg->state != PJSIP_DIALOG_STATE_PROCEEDING) {
- /* Change state to PROCEEDING */
- dlg_set_state(dlg, PJSIP_DIALOG_STATE_PROCEEDING, event);
-
- /* Notify application. */
- dlg_call_dlg_callback(dlg, PJSIP_DIALOG_EVENT_STATE_CHANGED, event);
- } else {
- /* Also notify application. */
- dlg_call_dlg_callback(dlg, PJSIP_DIALOG_EVENT_OTHER, event);
- }
- break;
-
- case PJSIP_TSX_STATE_COMPLETED:
- /* Change dialog state. */
- if (PJSIP_IS_STATUS_IN_CLASS(tsx->status_code, 200)) {
- /* Update remote target, take it from the contact hdr. */
- pjsip_contact_hdr *contact;
- contact = pjsip_msg_find_hdr(event->src.rdata->msg,
- PJSIP_H_CONTACT, NULL);
- if (contact) {
- dlg->remote.target = pjsip_uri_clone(dlg->pool, contact->uri);
- } else {
- PJ_LOG(4,(dlg->obj_name,
- "Warning: found no Contact hdr in 200/OK"));
- }
- dlg_set_state(dlg, PJSIP_DIALOG_STATE_CONNECTING, event);
- } else if (tsx->status_code==401 || tsx->status_code==407) {
- /* Handle Authentication challenge. */
- pjsip_tx_data *tdata;
- tdata = pjsip_auth_reinit_req( dlg->ua->endpt,
- dlg->pool, &dlg->auth_sess,
- dlg->cred_count, dlg->cred_info,
- tsx->last_tx, event->src.rdata);
- if (tdata) {
- /* Re-use original request, with a new transaction.
- * Need not to worry about CSeq, dialog will take care.
- */
- pjsip_dlg_send_msg(dlg, tdata);
- return 0;
- } else {
- dlg_set_state(dlg, PJSIP_DIALOG_STATE_DISCONNECTED, event);
- }
- } else {
- dlg_set_state(dlg, PJSIP_DIALOG_STATE_DISCONNECTED, event);
- }
-
- /* Notify application. */
- dlg_call_dlg_callback(dlg, PJSIP_DIALOG_EVENT_STATE_CHANGED, event);
-
- /* Send ACK when dialog is connected. */
- if (PJSIP_IS_STATUS_IN_CLASS(tsx->status_code, 200)) {
- pj_assert(event->src_type == PJSIP_EVENT_RX_MSG);
- dlg_on_recv_2xx_invite(dlg, event);
- }
- break;
-
- case PJSIP_TSX_STATE_TERMINATED:
- /*
- * Transaction is terminated because of timeout or transport error.
- * To let the application go to normal state progression, call the
- * callback twice. First is to emulate disconnection, and then call
- * again (with state TERMINATED) to destroy the dialog.
- */
- dlg_set_state(dlg, PJSIP_DIALOG_STATE_DISCONNECTED, event);
- dlg_call_dlg_callback(dlg, PJSIP_DIALOG_EVENT_STATE_CHANGED, event);
-
- /* The INVITE transaction will be destroyed, so release reference
- * to it.
- */
- dlg->invite_tsx = NULL;
-
- /* We should terminate the dialog now.
- * But it's possible that we have other pending transactions (for
- * example, outgoing CANCEL is in progress).
- * So destroy the dialog only if there's no other transaction.
- */
- if (dlg->pending_tsx_count == 0) {
- dlg_set_state(dlg, PJSIP_DIALOG_STATE_TERMINATED, event);
- dlg_call_dlg_callback(dlg, PJSIP_DIALOG_EVENT_STATE_CHANGED, event);
- dlg_is_terminated = 1;
- }
- break;
-
- default:
- pj_assert(0);
- break;
- }
- } else {
- dlg_call_dlg_callback(dlg, PJSIP_DIALOG_EVENT_OTHER, event);
- }
- return dlg_is_terminated ? -1 : 0;
-}
-
-/*
- * State PROCEEDING for UAS is after the callee send provisional response.
- * This function is also called for INCOMING state.
- */
-static int dlg_on_state_proceeding_callee( pjsip_dlg *dlg,
- pjsip_transaction *tsx,
- pjsip_event *event)
-{
- int dlg_is_terminated = 0;
-
- pj_assert( dlg->invite_tsx != NULL );
-
- if (event->type == PJSIP_EVENT_TSX_STATE_CHANGED &&
- event->src_type == PJSIP_EVENT_TX_MSG &&
- tsx == dlg->invite_tsx)
- {
- switch (tsx->state) {
- case PJSIP_TSX_STATE_PROCEEDING:
- /* Change state to PROCEEDING */
- dlg_set_state(dlg, PJSIP_DIALOG_STATE_PROCEEDING, event);
-
- /* Notify application. */
- dlg_call_dlg_callback(dlg, PJSIP_DIALOG_EVENT_STATE_CHANGED, event);
- break;
-
- case PJSIP_TSX_STATE_COMPLETED:
- case PJSIP_TSX_STATE_TERMINATED:
- /* Change dialog state. */
- if (PJSIP_IS_STATUS_IN_CLASS(tsx->status_code, 200)) {
- dlg_set_state(dlg, PJSIP_DIALOG_STATE_CONNECTING, event);
- } else {
- dlg_set_state(dlg, PJSIP_DIALOG_STATE_DISCONNECTED, event);
- }
-
- /* Notify application. */
- dlg_call_dlg_callback(dlg, PJSIP_DIALOG_EVENT_STATE_CHANGED, event);
-
- /* If transaction is terminated in non-2xx situation,
- * terminate dialog as well. This happens when something unexpected
- * occurs, such as transport error.
- */
- if (tsx->state == PJSIP_TSX_STATE_TERMINATED &&
- !PJSIP_IS_STATUS_IN_CLASS(tsx->status_code, 200))
- {
- dlg_set_state(dlg, PJSIP_DIALOG_STATE_TERMINATED, event);
- dlg_call_dlg_callback(dlg, PJSIP_DIALOG_EVENT_STATE_CHANGED, event);
- dlg_is_terminated = 1;
- }
- break;
-
- default:
- pj_assert(0);
- break;
- }
-
- } else if (event->type == PJSIP_EVENT_TSX_STATE_CHANGED &&
- event->src_type == PJSIP_EVENT_RX_MSG &&
- tsx->method.id == PJSIP_CANCEL_METHOD)
- {
- pjsip_tx_data *tdata;
-
- /* Check if sequence number matches the pending INVITE. */
- if (dlg->invite_tsx==NULL ||
- pj_strcmp(&tsx->branch, &dlg->invite_tsx->branch) != 0)
- {
- PJ_LOG(4, (dlg->obj_name, "Received CANCEL with no matching INVITE"));
-
- /* No matching INVITE transaction found. */
- tdata = pjsip_endpt_create_response(dlg->ua->endpt,
- event->src.rdata,
- PJSIP_SC_CALL_TSX_DOES_NOT_EXIST );
- pjsip_tsx_on_tx_msg(tsx, tdata);
- return 0;
- }
-
- /* Always respond the CANCEL with 200/CANCEL no matter what. */
- tdata = pjsip_endpt_create_response(dlg->ua->endpt,
- event->src.rdata,
- 200 );
- pjsip_tsx_on_tx_msg( tsx, tdata );
-
- /* Respond the INVITE transaction with 487, only if transaction has
- * not completed.
- */
- if (dlg->invite_tsx->last_tx) {
- if (dlg->invite_tsx->status_code < 200) {
- tdata = dlg->invite_tsx->last_tx;
- tdata->msg->line.status.code = 487;
- tdata->msg->line.status.reason = *pjsip_get_status_text(487);
- /* Reset packet buffer. */
- pjsip_tx_data_invalidate_msg(tdata);
- pjsip_tsx_on_tx_msg( dlg->invite_tsx, tdata );
- } else {
- PJ_LOG(4, (dlg->obj_name, "Received CANCEL with no effect, "
- "Transaction already terminated "
- "with status %d",
- dlg->invite_tsx->status_code));
- }
- } else {
- tdata = pjsip_endpt_create_response(dlg->ua->endpt,
- event->src.rdata,
- 487);
- pjsip_tsx_on_tx_msg( dlg->invite_tsx, tdata );
- }
- } else {
- dlg_call_dlg_callback(dlg, PJSIP_DIALOG_EVENT_OTHER, event);
- }
-
- return dlg_is_terminated ? -1 : 0;
-}
-
-static int dlg_on_state_proceeding( pjsip_dlg *dlg,
- pjsip_transaction *tsx,
- pjsip_event *event)
-{
- if (dlg->role == PJSIP_ROLE_UAC) {
- return dlg_on_state_proceeding_caller( dlg, tsx, event );
- } else {
- return dlg_on_state_proceeding_callee( dlg, tsx, event );
- }
-}
-
-static int dlg_on_state_connecting( pjsip_dlg *dlg,
- pjsip_transaction *tsx,
- pjsip_event *event)
-{
- if (tsx == dlg->invite_tsx) {
- if (event->type == PJSIP_EVENT_TSX_STATE_CHANGED &&
- (tsx->state == PJSIP_TSX_STATE_TERMINATED ||
- tsx->state == PJSIP_TSX_STATE_COMPLETED ||
- tsx->state == PJSIP_TSX_STATE_CONFIRMED))
- {
- if (PJSIP_IS_STATUS_IN_CLASS(tsx->status_code, 200)) {
- dlg_set_state(dlg, PJSIP_DIALOG_STATE_ESTABLISHED, event);
- } else {
- /* Probably because we never get the ACK, or transport error
- * when sending ACK.
- */
- dlg_set_state(dlg, PJSIP_DIALOG_STATE_DISCONNECTED, event);
- }
- dlg_call_dlg_callback(dlg, PJSIP_DIALOG_EVENT_STATE_CHANGED, event);
- } else {
- dlg_call_dlg_callback(dlg, PJSIP_DIALOG_EVENT_OTHER, event);
- }
- } else {
- /* Handle case when transaction is started when dialog is connecting
- * (e.g. BYE requests cross wire.
- */
- if (event->type == PJSIP_EVENT_TSX_STATE_CHANGED &&
- event->src_type == PJSIP_EVENT_RX_MSG &&
- tsx->role == PJSIP_ROLE_UAS)
- {
- pjsip_tx_data *response;
-
- if (tsx->status_code >= 200)
- return 0;
-
- if (tsx->method.id == PJSIP_BYE_METHOD) {
- /* Set state to DISCONNECTED. */
- dlg_set_state(dlg, PJSIP_DIALOG_STATE_DISCONNECTED, event);
-
- /* Notify application. */
- dlg_call_dlg_callback(dlg, PJSIP_DIALOG_EVENT_STATE_CHANGED, event);
-
- response = pjsip_endpt_create_response( dlg->ua->endpt,
- event->src.rdata, 200);
- } else {
- response = pjsip_endpt_create_response( dlg->ua->endpt, event->src.rdata,
- PJSIP_SC_INTERNAL_SERVER_ERROR);
- }
-
- if (response)
- pjsip_tsx_on_tx_msg(tsx, response);
-
- return 0;
- }
- }
- return 0;
-}
-
-static int dlg_on_state_established( pjsip_dlg *dlg,
- pjsip_transaction *tsx,
- pjsip_event *event)
-{
- PJ_UNUSED_ARG(tsx)
-
- if (tsx && tsx->method.id == PJSIP_BYE_METHOD) {
- /* Set state to DISCONNECTED. */
- dlg_set_state(dlg, PJSIP_DIALOG_STATE_DISCONNECTED, event);
-
- /* Notify application. */
- dlg_call_dlg_callback(dlg, PJSIP_DIALOG_EVENT_STATE_CHANGED, event);
-
- /* Answer with 200/BYE. */
- if (event->src_type == PJSIP_EVENT_RX_MSG) {
- pjsip_tx_data *tdata;
- tdata = pjsip_endpt_create_response(dlg->ua->endpt,
- event->src.rdata,
- 200 );
- if (tdata)
- pjsip_tsx_on_tx_msg( tsx, tdata );
- }
- } else if (tsx && event->src_type == PJSIP_EVENT_RX_MSG) {
- pjsip_method_e method = event->src.rdata->cseq->method.id;
-
- PJ_TODO(PROPERLY_HANDLE_REINVITATION)
-
- /* Reinvitation. The message may be INVITE or an ACK. */
- if (method == PJSIP_INVITE_METHOD) {
- if (dlg->invite_tsx && dlg->invite_tsx->status_code < 200) {
- /* Section 14.2: A UAS that receives a second INVITE before it
- * sends the final response to a first INVITE with a lower
- * CSeq sequence number on the same dialog MUST return a 500
- * (Server Internal Error) response to the second INVITE and
- * MUST include a Retry-After header field with a randomly
- * chosen value of between 0 and 10 seconds.
- */
- pjsip_retry_after_hdr *hdr;
- pjsip_tx_data *tdata =
- pjsip_endpt_create_response(dlg->ua->endpt,
- event->src.rdata, 500);
-
- if (!tdata)
- return 0;
-
- /* Add Retry-After. */
- hdr = pjsip_retry_after_hdr_create(tdata->pool);
- hdr->ivalue = 9;
- pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr*)hdr);
-
- /* Send. */
- pjsip_tsx_on_tx_msg(tsx, tdata);
-
- return 0;
- }
-
- /* Keep this as our current INVITE transaction. */
- dlg->invite_tsx = tsx;
-
- /* Create response buffer. */
- tsx->last_tx = pjsip_endpt_create_response( dlg->ua->endpt,
- event->src.rdata, 100);
- pjsip_tx_data_add_ref(tsx->last_tx);
-
- }
-
- /* Notify application. */
- dlg_call_dlg_callback(dlg, PJSIP_DIALOG_EVENT_MID_CALL_REQUEST, event);
-
- } else {
- dlg_call_dlg_callback(dlg, PJSIP_DIALOG_EVENT_OTHER, event);
- }
-
- return 0;
-}
-
-static int dlg_on_state_disconnected( pjsip_dlg *dlg,
- pjsip_transaction *tsx,
- pjsip_event *event)
-{
- PJ_UNUSED_ARG(tsx)
-
- /* Handle case when transaction is started when dialog is disconnected
- * (e.g. BYE requests cross wire.
- */
- if (event->type == PJSIP_EVENT_TSX_STATE_CHANGED &&
- event->src_type == PJSIP_EVENT_RX_MSG &&
- tsx->role == PJSIP_ROLE_UAS)
- {
- pjsip_tx_data *response = NULL;
-
- if (tsx->status_code >= 200)
- return 0;
-
- if (tsx->method.id == PJSIP_BYE_METHOD) {
- response = pjsip_endpt_create_response( dlg->ua->endpt,
- event->src.rdata, 200);
- } else {
- response = pjsip_endpt_create_response( dlg->ua->endpt, event->src.rdata,
- PJSIP_SC_INTERNAL_SERVER_ERROR);
- }
- if (response)
- pjsip_tsx_on_tx_msg(tsx, response);
-
- return 0;
- }
- /* Handle case when outgoing BYE was rejected with 401/407 */
- else if (event->type == PJSIP_EVENT_TSX_STATE_CHANGED &&
- event->src_type == PJSIP_EVENT_RX_MSG &&
- tsx->role == PJSIP_ROLE_UAC)
- {
- if (tsx->status_code==401 || tsx->status_code==407) {
- pjsip_tx_data *tdata;
- tdata = pjsip_auth_reinit_req( dlg->ua->endpt, dlg->pool,
- &dlg->auth_sess,
- dlg->cred_count, dlg->cred_info,
- tsx->last_tx, event->src.rdata);
- if (tdata) {
- pjsip_dlg_send_msg(dlg, tdata);
- }
- }
- }
-
-
- if (dlg->pending_tsx_count == 0) {
- /* Set state to TERMINATED. */
- dlg_set_state(dlg, PJSIP_DIALOG_STATE_TERMINATED, event);
-
- /* Notify application. */
- dlg_call_dlg_callback(dlg, PJSIP_DIALOG_EVENT_STATE_CHANGED, event);
-
- return -1;
- } else {
- dlg_call_dlg_callback(dlg, PJSIP_DIALOG_EVENT_OTHER, event);
- }
-
- return 0;
-}
-
-static int dlg_on_state_terminated( pjsip_dlg *dlg,
- pjsip_transaction *tsx,
- pjsip_event *event)
-{
- PJ_UNUSED_ARG(dlg)
- PJ_UNUSED_ARG(tsx)
- PJ_UNUSED_ARG(event)
-
- return -1;
-}
-
+/* $Id$ */
+/*
+ * Copyright (C) 2003-2006 Benny Prijono <benny@prijono.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#include <pjsip_mod_ua/sip_dialog.h>
+#include <pjsip_mod_ua/sip_ua.h>
+#include <pjsip_mod_ua/sip_ua_private.h>
+#include <pjsip/sip_transport.h>
+#include <pjsip/sip_transaction.h>
+#include <pjsip/sip_types.h>
+#include <pjsip/sip_endpoint.h>
+#include <pjsip/sip_uri.h>
+#include <pjsip/sip_util.h>
+#include <pjsip/sip_module.h>
+#include <pjsip/sip_event.h>
+#include <pjsip/sip_parser.h>
+#include <pj/string.h>
+#include <pj/log.h>
+#include <pj/os.h>
+#include <pj/guid.h>
+#include <pj/except.h>
+#include <pj/pool.h>
+
+/* TLS to keep dialog lock record (initialized by UA) */
+int pjsip_dlg_lock_tls_id;
+
+struct dialog_lock_data
+{
+ struct dialog_lock_data *prev;
+ pjsip_dlg *dlg;
+ int is_alive;
+};
+
+/*
+ * Static function prototypes.
+ */
+static void dlg_create_request_throw( pjsip_tx_data **p_tdata,
+ pjsip_dlg *dlg,
+ const pjsip_method *method,
+ int cseq );
+static int dlg_on_all_state_pre( pjsip_dlg *dlg,
+ pjsip_transaction *tsx,
+ pjsip_event *event);
+static int dlg_on_all_state_post( pjsip_dlg *dlg,
+ pjsip_transaction *tsx,
+ pjsip_event *event);
+static int dlg_on_state_null( pjsip_dlg *dlg,
+ pjsip_transaction *tsx,
+ pjsip_event *event);
+static int dlg_on_state_incoming( pjsip_dlg *dlg,
+ pjsip_transaction *tsx,
+ pjsip_event *event);
+static int dlg_on_state_calling( pjsip_dlg *dlg,
+ pjsip_transaction *tsx,
+ pjsip_event *event);
+static int dlg_on_state_proceeding( pjsip_dlg *dlg,
+ pjsip_transaction *tsx,
+ pjsip_event *event);
+static int dlg_on_state_proceeding_caller( pjsip_dlg *dlg,
+ pjsip_transaction *tsx,
+ pjsip_event *event);
+static int dlg_on_state_proceeding_callee( pjsip_dlg *dlg,
+ pjsip_transaction *tsx,
+ pjsip_event *event);
+static int dlg_on_state_connecting( pjsip_dlg *dlg,
+ pjsip_transaction *tsx,
+ pjsip_event *event);
+static int dlg_on_state_established( pjsip_dlg *dlg,
+ pjsip_transaction *tsx,
+ pjsip_event *event);
+static int dlg_on_state_disconnected( pjsip_dlg *dlg,
+ pjsip_transaction *tsx,
+ pjsip_event *event);
+static int dlg_on_state_terminated( pjsip_dlg *dlg,
+ pjsip_transaction *tsx,
+ pjsip_event *event);
+
+/*
+ * Dialog state handlers.
+ */
+static int (*dlg_state_handlers[])(struct pjsip_dlg *, pjsip_transaction *,
+ pjsip_event *) =
+{
+ &dlg_on_state_null,
+ &dlg_on_state_incoming,
+ &dlg_on_state_calling,
+ &dlg_on_state_proceeding,
+ &dlg_on_state_connecting,
+ &dlg_on_state_established,
+ &dlg_on_state_disconnected,
+ &dlg_on_state_terminated,
+};
+
+/*
+ * Dialog state names.
+ */
+static const char* dlg_state_names[] =
+{
+ "STATE_NULL",
+ "STATE_INCOMING",
+ "STATE_CALLING",
+ "STATE_PROCEEDING",
+ "STATE_CONNECTING",
+ "STATE_ESTABLISHED",
+ "STATE_DISCONNECTED",
+ "STATE_TERMINATED",
+};
+
+
+/*
+ * Get the dialog string state, normally for logging purpose.
+ */
+const char *pjsip_dlg_state_str(pjsip_dlg_state_e state)
+{
+ return dlg_state_names[state];
+}
+
+/* Lock dialog mutex. */
+static void lock_dialog(pjsip_dlg *dlg, struct dialog_lock_data *lck)
+{
+ struct dialog_lock_data *prev;
+
+ pj_mutex_lock(dlg->mutex);
+ prev = pj_thread_local_get(pjsip_dlg_lock_tls_id);
+ lck->prev = prev;
+ lck->dlg = dlg;
+ lck->is_alive = 1;
+ pj_thread_local_set(pjsip_dlg_lock_tls_id, lck);
+}
+
+/* Carefully unlock dialog mutex, protect against situation when the dialog
+ * has already been destroyed.
+ */
+static pj_status_t unlock_dialog(pjsip_dlg *dlg, struct dialog_lock_data *lck)
+{
+ pj_assert(pj_thread_local_get(pjsip_dlg_lock_tls_id) == lck);
+ pj_assert(dlg == lck->dlg);
+
+ pj_thread_local_set(pjsip_dlg_lock_tls_id, lck->prev);
+ if (lck->is_alive)
+ pj_mutex_unlock(dlg->mutex);
+
+ return lck->is_alive ? 0 : -1;
+}
+
+/*
+ * This is called by dialog's FSM to change dialog's state.
+ */
+static void dlg_set_state( pjsip_dlg *dlg, pjsip_dlg_state_e state,
+ pjsip_event *event)
+{
+ PJ_UNUSED_ARG(event);
+
+ PJ_LOG(4, (dlg->obj_name, "State %s-->%s (ev=%s, src=%s, data=%p)",
+ pjsip_dlg_state_str(dlg->state), pjsip_dlg_state_str(state),
+ event ? pjsip_event_str(event->type) : "",
+ event ? pjsip_event_str(event->src_type) : "",
+ event ? event->src.data : NULL));
+
+ dlg->state = state;
+ dlg->handle_tsx_event = dlg_state_handlers[state];
+}
+
+/*
+ * Invoke dialog's callback.
+ * This function is called by dialog's FSM, and interpret the event and call
+ * the corresponding callback registered by application.
+ */
+static void dlg_call_dlg_callback( pjsip_dlg *dlg, pjsip_dlg_event_e dlg_event,
+ pjsip_event *event )
+{
+ pjsip_dlg_callback *cb = dlg->ua->dlg_cb;
+ if (!cb) {
+ PJ_LOG(4,(dlg->obj_name, "Can not call callback (none registered)."));
+ return;
+ }
+
+ /* Low level event: call the all-events callback. */
+ if (cb->on_all_events) {
+ (*cb->on_all_events)(dlg, dlg_event, event);
+ }
+
+ /* Low level event: call the tx/rx callback if this is a tx/rx event. */
+ if (event->type == PJSIP_EVENT_BEFORE_TX && cb->on_before_tx)
+ {
+ (*cb->on_before_tx)(dlg, event->obj.tsx, event->src.tdata, event->data.long_data);
+ }
+ else if (event->type == PJSIP_EVENT_TX_MSG &&
+ event->src_type == PJSIP_EVENT_TX_MSG && cb->on_tx_msg)
+ {
+ (*cb->on_tx_msg)(dlg, event->obj.tsx, event->src.tdata);
+ }
+ else if (event->type == PJSIP_EVENT_RX_MSG &&
+ event->src_type == PJSIP_EVENT_RX_MSG && cb->on_rx_msg) {
+ (*cb->on_rx_msg)(dlg, event->obj.tsx, event->src.rdata);
+ }
+
+ /* Now trigger high level events.
+ * High level event should only occurs when dialog's state has changed,
+ * except for on_provisional, which may be called multiple times whenever
+ * response message is sent
+ */
+ if (dlg->state == PJSIP_DIALOG_STATE_PROCEEDING &&
+ (event->type== PJSIP_EVENT_TSX_STATE_CHANGED) &&
+ event->obj.tsx == dlg->invite_tsx)
+ {
+ /* Sent/received provisional responses. */
+ if (cb->on_provisional)
+ (*cb->on_provisional)(dlg, event->obj.tsx, event);
+ }
+
+ if (dlg_event == PJSIP_DIALOG_EVENT_MID_CALL_REQUEST) {
+ if (cb->on_mid_call_events)
+ (*cb->on_mid_call_events)(dlg, event);
+ return;
+ }
+
+ if (dlg_event != PJSIP_DIALOG_EVENT_STATE_CHANGED)
+ return;
+
+ if (dlg->state == PJSIP_DIALOG_STATE_INCOMING) {
+
+ /* New incoming dialog. */
+ pj_assert (event->src_type == PJSIP_EVENT_RX_MSG);
+ (*cb->on_incoming)(dlg, event->obj.tsx, event->src.rdata);
+
+ } else if (dlg->state == PJSIP_DIALOG_STATE_CALLING) {
+
+ /* Dialog has just sent the first INVITE. */
+ if (cb->on_calling) {
+ (*cb->on_calling)(dlg, event->obj.tsx, event->src.tdata);
+ }
+
+ } else if (dlg->state == PJSIP_DIALOG_STATE_DISCONNECTED) {
+
+ if (cb->on_disconnected)
+ (*cb->on_disconnected)(dlg, event);
+
+ } else if (dlg->state == PJSIP_DIALOG_STATE_TERMINATED) {
+
+ if (cb->on_terminated)
+ (*cb->on_terminated)(dlg);
+
+ pjsip_ua_destroy_dialog(dlg);
+
+ } else if (dlg->state == PJSIP_DIALOG_STATE_CONNECTING) {
+
+ if (cb->on_connecting)
+ (*cb->on_connecting)(dlg, event);
+
+ } else if (dlg->state == PJSIP_DIALOG_STATE_ESTABLISHED) {
+
+ if (cb->on_established)
+ (*cb->on_established)(dlg, event);
+ }
+}
+
+/*
+ * This callback receives event from the transaction layer (via User Agent),
+ * or from dialog timer (for 200/INVITE or ACK retransmission).
+ */
+void pjsip_dlg_on_tsx_event( pjsip_dlg *dlg,
+ pjsip_transaction *tsx,
+ pjsip_event *event)
+{
+ int status = 0;
+ struct dialog_lock_data lck;
+
+ PJ_LOG(4, (dlg->obj_name, "state=%s (evt=%s, src=%s)",
+ pjsip_dlg_state_str(dlg->state),
+ pjsip_event_str(event->type),
+ pjsip_event_str(event->src_type)));
+
+
+ lock_dialog(dlg, &lck);
+
+ status = dlg_on_all_state_pre( dlg, tsx, event);
+
+ if (status==0) {
+ status = (*dlg->handle_tsx_event)(dlg, tsx, event);
+ }
+ if (status==0) {
+ status = dlg_on_all_state_post( dlg, tsx, event);
+ }
+
+ unlock_dialog(dlg, &lck);
+}
+
+/*
+ * This function contains common processing in all states, and it is called
+ * before the FSM is invoked.
+ */
+static int dlg_on_all_state_pre( pjsip_dlg *dlg,
+ pjsip_transaction *tsx,
+ pjsip_event *event)
+{
+ PJ_UNUSED_ARG(event)
+
+ if (event->type != PJSIP_EVENT_TSX_STATE_CHANGED)
+ return 0;
+
+ if (tsx && (tsx->state==PJSIP_TSX_STATE_CALLING ||
+ tsx->state==PJSIP_TSX_STATE_TRYING))
+ {
+ ++dlg->pending_tsx_count;
+
+ } else if (tsx && tsx->state==PJSIP_TSX_STATE_DESTROYED)
+ {
+ --dlg->pending_tsx_count;
+ if (tsx == dlg->invite_tsx)
+ dlg->invite_tsx = NULL;
+ }
+
+ if (tsx->method.id == PJSIP_INVITE_METHOD) {
+ tsx->handle_ack = 1;
+ }
+ return 0;
+}
+
+
+/*
+ * This function contains common processing in all states, and it is called
+ * after the FSM is invoked.
+ */
+static int dlg_on_all_state_post( pjsip_dlg *dlg,
+ pjsip_transaction *tsx,
+ pjsip_event *event)
+{
+ PJ_UNUSED_ARG(event)
+
+ if (tsx && tsx->state==PJSIP_TSX_STATE_DESTROYED) {
+ if (dlg->pending_tsx_count == 0 &&
+ dlg->state != PJSIP_DIALOG_STATE_CONNECTING &&
+ dlg->state != PJSIP_DIALOG_STATE_ESTABLISHED &&
+ dlg->state != PJSIP_DIALOG_STATE_TERMINATED)
+ {
+ dlg_set_state(dlg, PJSIP_DIALOG_STATE_TERMINATED, event);
+ dlg_call_dlg_callback(dlg, PJSIP_DIALOG_EVENT_STATE_CHANGED, event);
+ return -1;
+ }
+ }
+
+ return 0;
+}
+
+
+/*
+ * Internal function to initialize dialog, contains common initialization
+ * for both UAS and UAC dialog.
+ */
+static pj_status_t dlg_init( pjsip_dlg *dlg )
+{
+ /* Init mutex. */
+ dlg->mutex = pj_mutex_create(dlg->pool, "mdlg%p", 0);
+ if (!dlg->mutex) {
+ PJ_PERROR((dlg->obj_name, "pj_mutex_create()"));
+ return -1;
+ }
+
+ /* Init route-set (Initially empty) */
+ pj_list_init(&dlg->route_set);
+
+ /* Init auth credential list. */
+ pj_list_init(&dlg->auth_sess);
+
+ return PJ_SUCCESS;
+}
+
+/*
+ * This one is called just before dialog is destroyed.
+ * It is called while mutex is held.
+ */
+PJ_DEF(void) pjsip_on_dialog_destroyed( pjsip_dlg *dlg )
+{
+ struct dialog_lock_data *lck;
+
+ //PJ_TODO(CHECK_THIS);
+ pj_assert(dlg->pending_tsx_count == 0);
+
+ /* Mark dialog as dead. */
+ lck = pj_thread_local_get(pjsip_dlg_lock_tls_id);
+ while (lck) {
+ if (lck->dlg == dlg)
+ lck->is_alive = 0;
+ lck = lck->prev;
+ }
+}
+
+/*
+ * Initialize dialog from the received request.
+ * This is an internal function which is called by the User Agent (sip_ua.c),
+ * and it will initialize most of dialog's properties with values from the
+ * received message.
+ */
+pj_status_t pjsip_dlg_init_from_rdata( pjsip_dlg *dlg, pjsip_rx_data *rdata )
+{
+ pjsip_msg *msg = rdata->msg;
+ pjsip_to_hdr *to;
+ pjsip_contact_hdr *contact;
+ pjsip_name_addr *name_addr;
+ pjsip_url *url;
+ unsigned flag;
+ pjsip_event event;
+
+ pj_assert(dlg && rdata);
+
+ PJ_LOG(5, (dlg->obj_name, "init_from_rdata(%p)", rdata));
+
+ /* Must be an INVITE request. */
+ pj_assert(msg->type == PJSIP_REQUEST_MSG &&
+ msg->line.req.method.id == PJSIP_INVITE_METHOD);
+
+ /* Init general dialog data. */
+ if (dlg_init(dlg) != PJ_SUCCESS) {
+ return -1;
+ }
+
+ /* Get the To header. */
+ to = rdata->to;
+
+ /* Copy URI in the To header as our local URI. */
+ dlg->local.info = pjsip_hdr_clone( dlg->pool, to);
+
+ /* Set tag in the local info. */
+ dlg->local.info->tag = dlg->local.tag;
+
+ /* Create local Contact to be advertised in the response.
+ * At the moment, just copy URI from the local URI as our contact.
+ */
+ dlg->local.contact = pjsip_contact_hdr_create( dlg->pool );
+ dlg->local.contact->star = 0;
+ name_addr = (pjsip_name_addr *)dlg->local.info->uri;
+ dlg->local.contact->uri = (pjsip_uri*) name_addr;
+ url = (pjsip_url*) name_addr->uri;
+ //url->port = rdata->via->sent_by.port;
+ //url->port = pj_sockaddr_get_port( pjsip_transport_get_local_addr(rdata->transport) );
+
+ /* Save remote URI. */
+ dlg->remote.info = pjsip_hdr_clone( dlg->pool, rdata->from );
+ pjsip_fromto_set_to( dlg->remote.info );
+ pj_strdup( dlg->pool, &dlg->remote.tag, &rdata->from->tag );
+
+ /* Save remote Contact. */
+ contact = pjsip_msg_find_hdr( msg, PJSIP_H_CONTACT, NULL);
+ if (contact) {
+ dlg->remote.contact = pjsip_hdr_clone( dlg->pool, contact );
+ } else {
+ PJ_LOG(3,(dlg->obj_name, "No Contact header in INVITE from %s",
+ pj_sockaddr_get_str_addr(&rdata->addr)));
+ dlg->remote.contact = pjsip_contact_hdr_create( dlg->pool );
+ dlg->remote.contact->uri = dlg->remote.info->uri;
+ }
+
+ /* Save Call-ID. */
+ dlg->call_id = pjsip_cid_hdr_create(dlg->pool);
+ pj_strdup( dlg->pool, &dlg->call_id->id, &rdata->call_id );
+
+ /* Initialize local CSeq and save remote CSeq.*/
+ dlg->local.cseq = rdata->timestamp.sec & 0xFFFF;
+ dlg->remote.cseq = rdata->cseq->cseq;
+
+ /* Secure? */
+ flag = pjsip_transport_get_flag(rdata->transport);
+ dlg->secure = (flag & PJSIP_TRANSPORT_SECURE) != 0;
+
+ /* Initial state is NULL. */
+ event.type = event.src_type = PJSIP_EVENT_RX_MSG;
+ event.src.rdata = rdata;
+ dlg_set_state(dlg, PJSIP_DIALOG_STATE_NULL, &event);
+
+ PJ_LOG(5, (dlg->obj_name, "init_from_rdata(%p) complete", rdata));
+ return PJ_SUCCESS;
+}
+
+/*
+ * Set the contact details.
+ */
+PJ_DEF(pj_status_t) pjsip_dlg_set_contact( pjsip_dlg *dlg,
+ const pj_str_t *contact )
+{
+ pjsip_uri *local_uri;
+ pj_str_t tmp;
+
+ pj_strdup_with_null(dlg->pool, &tmp, contact);
+ local_uri = pjsip_parse_uri( dlg->pool, tmp.ptr, tmp.slen,
+ PJSIP_PARSE_URI_AS_NAMEADDR);
+ if (local_uri == NULL) {
+ PJ_LOG(2, (dlg->obj_name, "set_contact: invalid URI"));
+ return -1;
+ }
+
+ dlg->local.contact->star = 0;
+ dlg->local.contact->uri = local_uri;
+ return 0;
+}
+
+/*
+ * Set route set.
+ */
+PJ_DEF(pj_status_t) pjsip_dlg_set_route_set( pjsip_dlg *dlg,
+ const pjsip_route_hdr *route_set )
+{
+ pjsip_route_hdr *hdr;
+
+ pj_list_init(&dlg->route_set);
+ hdr = route_set->next;
+ while (hdr != route_set) {
+ pjsip_route_hdr *cloned = pjsip_hdr_clone(dlg->pool, hdr);
+ pj_list_insert_before( &dlg->route_set, cloned);
+ hdr = hdr->next;
+ }
+ return 0;
+}
+
+/*
+ * Set route set without cloning the header.
+ */
+PJ_DEF(pj_status_t) pjsip_dlg_set_route_set_np( pjsip_dlg *dlg,
+ pjsip_route_hdr *route_set)
+{
+ pjsip_route_hdr *hdr;
+
+ pj_list_init(&dlg->route_set);
+ hdr = route_set->next;
+ while (hdr != route_set) {
+ pj_list_insert_before( &dlg->route_set, hdr);
+ hdr = hdr->next;
+ }
+ return 0;
+}
+
+/*
+ * Application calls this function when it wants to initiate an outgoing
+ * dialog (incoming dialogs are created automatically by UA when it receives
+ * INVITE, by calling pjsip_dlg_init_from_rdata()).
+ * This function should initialize most of the dialog's properties.
+ */
+PJ_DEF(pj_status_t) pjsip_dlg_init( pjsip_dlg *dlg,
+ const pj_str_t *c_local_info,
+ const pj_str_t *c_remote_info,
+ const pj_str_t *c_target)
+{
+ pj_time_val tv;
+ pjsip_event event;
+ pj_str_t buf;
+
+ if (!dlg || !c_local_info || !c_remote_info) {
+ pj_assert(dlg && c_local_info && c_remote_info);
+ return -1;
+ }
+
+ PJ_LOG(5, (dlg->obj_name, "initializing"));
+
+ /* Init general dialog */
+ if (dlg_init(dlg) != PJ_SUCCESS) {
+ return -1;
+ }
+
+ /* Duplicate local info. */
+ pj_strdup_with_null( dlg->pool, &buf, c_local_info);
+
+ /* Build local URI. */
+ dlg->local.target = pjsip_parse_uri(dlg->pool, buf.ptr, buf.slen,
+ PJSIP_PARSE_URI_AS_NAMEADDR);
+ if (dlg->local.target == NULL) {
+ PJ_LOG(2, (dlg->obj_name,
+ "pjsip_dlg_init: invalid local URI %s", buf.ptr));
+ return -1;
+ }
+
+ /* Set local URI. */
+ dlg->local.info = pjsip_from_hdr_create(dlg->pool);
+ dlg->local.info->uri = dlg->local.target;
+ dlg->local.info->tag = dlg->local.tag;
+
+ /* Create local Contact to be advertised in the response. */
+ dlg->local.contact = pjsip_contact_hdr_create( dlg->pool );
+ dlg->local.contact->star = 0;
+ dlg->local.contact->uri = dlg->local.target;
+
+ /* Set remote URI. */
+ dlg->remote.info = pjsip_to_hdr_create(dlg->pool);
+
+ /* Duplicate to buffer. */
+ pj_strdup_with_null( dlg->pool, &buf, c_remote_info);
+
+ /* Build remote info. */
+ dlg->remote.info->uri = pjsip_parse_uri( dlg->pool, buf.ptr, buf.slen,
+ PJSIP_PARSE_URI_AS_NAMEADDR);
+ if (dlg->remote.info->uri == NULL) {
+ PJ_LOG(2, (dlg->obj_name,
+ "pjsip_dlg_init: invalid remote URI %s", buf.ptr));
+ return -1;
+ }
+
+ /* Set remote Contact initially equal to the remote URI. */
+ dlg->remote.contact = pjsip_contact_hdr_create(dlg->pool);
+ dlg->remote.contact->star = 0;
+ dlg->remote.contact->uri = dlg->remote.info->uri;
+
+ /* Set initial remote target. */
+ if (c_target != NULL) {
+ pj_strdup_with_null( dlg->pool, &buf, c_target);
+ dlg->remote.target = pjsip_parse_uri( dlg->pool, buf.ptr, buf.slen, 0);
+ if (dlg->remote.target == NULL) {
+ PJ_LOG(2, (dlg->obj_name,
+ "pjsip_dlg_init: invalid remote target %s", buf.ptr));
+ return -1;
+ }
+ } else {
+ dlg->remote.target = dlg->remote.info->uri;
+ }
+
+ /* Create globally unique Call-ID */
+ dlg->call_id = pjsip_cid_hdr_create(dlg->pool);
+ pj_create_unique_string( dlg->pool, &dlg->call_id->id );
+
+ /* Local and remote CSeq */
+ pj_gettimeofday(&tv);
+ dlg->local.cseq = tv.sec & 0xFFFF;
+ dlg->remote.cseq = 0;
+
+ /* Initial state is NULL. */
+ event.type = event.src_type = PJSIP_EVENT_TX_MSG;
+ event.src.data = NULL;
+ dlg_set_state(dlg, PJSIP_DIALOG_STATE_NULL, &event);
+
+ /* Done. */
+ PJ_LOG(4, (dlg->obj_name, "%s dialog initialized, From: %.*s, To: %.*s",
+ pjsip_role_name(dlg->role),
+ c_local_info->slen, c_local_info->ptr,
+ c_remote_info->slen, c_remote_info->ptr));
+
+ return PJ_SUCCESS;
+}
+
+/*
+ * Set credentials.
+ */
+PJ_DEF(pj_status_t) pjsip_dlg_set_credentials( pjsip_dlg *dlg,
+ int count,
+ const pjsip_cred_info *cred)
+{
+ if (count > 0) {
+ dlg->cred_info = pj_pool_alloc(dlg->pool, count * sizeof(pjsip_cred_info));
+ pj_memcpy(dlg->cred_info, cred, count * sizeof(pjsip_cred_info));
+ }
+ dlg->cred_count = count;
+ return 0;
+}
+
+/*
+ * Create a new request within dialog (i.e. after the dialog session has been
+ * established). The construction of such requests follows the rule in
+ * RFC3261 section 12.2.1.
+ */
+static void dlg_create_request_throw( pjsip_tx_data **p_tdata,
+ pjsip_dlg *dlg,
+ const pjsip_method *method,
+ int cseq )
+{
+ pjsip_tx_data *tdata;
+ pjsip_contact_hdr *contact;
+ pjsip_route_hdr *route, *end_list;
+
+ /* Contact Header field.
+ * Contact can only be present in requests that establish dialog (in the
+ * core SIP spec, only INVITE).
+ */
+ if (method->id == PJSIP_INVITE_METHOD)
+ contact = dlg->local.contact;
+ else
+ contact = NULL;
+
+ tdata = pjsip_endpt_create_request_from_hdr( dlg->ua->endpt,
+ method,
+ dlg->remote.target,
+ dlg->local.info,
+ dlg->remote.info,
+ contact,
+ dlg->call_id,
+ cseq,
+ NULL);
+ if (!tdata) {
+ PJ_THROW(1);
+ return;
+ }
+
+ /* Just copy dialog route-set to Route header.
+ * The transaction will do the processing as specified in Section 12.2.1
+ * of RFC 3261 in function tsx_process_route() in sip_transaction.c.
+ */
+ route = dlg->route_set.next;
+ end_list = &dlg->route_set;
+ for (; route != end_list; route = route->next ) {
+ pjsip_route_hdr *r;
+ r = pjsip_hdr_shallow_clone( tdata->pool, route );
+ pjsip_routing_hdr_set_route(r);
+ pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr*)r);
+ }
+
+ /* Copy authorization headers. */
+ pjsip_auth_init_req( dlg->pool, tdata, &dlg->auth_sess,
+ dlg->cred_count, dlg->cred_info);
+
+ *p_tdata = tdata;
+}
+
+
+/*
+ * This function is called by application to create new outgoing request
+ * message for this dialog. After the request is created, application can
+ * modify the message (such adding headers), and eventually send the request.
+ */
+PJ_DEF(pjsip_tx_data*) pjsip_dlg_create_request( pjsip_dlg *dlg,
+ const pjsip_method *method,
+ int cseq)
+{
+ PJ_USE_EXCEPTION;
+ struct dialog_lock_data lck;
+ pjsip_tx_data *tdata = NULL;
+
+ pj_assert(dlg != NULL && method != NULL);
+ if (!dlg || !method) {
+ return NULL;
+ }
+
+ PJ_LOG(5, (dlg->obj_name, "Creating request"));
+
+ /* Lock dialog. */
+ lock_dialog(dlg, &lck);
+
+ /* Use outgoing CSeq and increment it by one. */
+ if (cseq < 0)
+ cseq = dlg->local.cseq + 1;
+
+ PJ_LOG(5, (dlg->obj_name, "creating request %.*s cseq=%d",
+ method->name.slen, method->name.ptr, cseq));
+
+ /* Create the request. */
+ PJ_TRY {
+ dlg_create_request_throw(&tdata, dlg, method, cseq);
+ PJ_LOG(5, (dlg->obj_name, "request data %s created", tdata->obj_name));
+ }
+ PJ_DEFAULT {
+ /* Failed! Delete transmit data. */
+ if (tdata) {
+ pjsip_tx_data_dec_ref( tdata );
+ tdata = NULL;
+ }
+ }
+ PJ_END;
+
+ /* Unlock dialog. */
+ unlock_dialog(dlg, &lck);
+
+ return tdata;
+}
+
+/*
+ * Sends request.
+ * Select the transport for the request message
+ */
+static pj_status_t dlg_send_request( pjsip_dlg *dlg, pjsip_tx_data *tdata )
+{
+ pjsip_transaction *tsx;
+ pj_status_t status = PJ_SUCCESS;
+ struct dialog_lock_data lck;
+
+ pj_assert(dlg != NULL && tdata != NULL);
+ if (!dlg || !tdata) {
+ return -1;
+ }
+
+ PJ_LOG(5, (dlg->obj_name, "sending request %s", tdata->obj_name));
+
+ /* Lock dialog. */
+ lock_dialog(dlg, &lck);
+
+ /* Create a new transaction. */
+ tsx = pjsip_endpt_create_tsx( dlg->ua->endpt );
+ if (!tsx) {
+ unlock_dialog(dlg, &lck);
+ return -1;
+ }
+
+ PJ_LOG(4, (dlg->obj_name, "Created new UAC transaction: %s", tsx->obj_name));
+
+ /* Initialize transaction */
+ tsx->module_data[dlg->ua->mod_id] = dlg;
+ status = pjsip_tsx_init_uac( tsx, tdata );
+ if (status != PJ_SUCCESS) {
+ unlock_dialog(dlg, &lck);
+ pjsip_endpt_destroy_tsx( dlg->ua->endpt, tsx );
+ return -1;
+ }
+ pjsip_endpt_register_tsx( dlg->ua->endpt, tsx );
+
+ /* Start the transaction. */
+ pjsip_tsx_on_tx_msg(tsx, tdata);
+
+ /* Unlock dialog. */
+ unlock_dialog(dlg, &lck);
+
+ return status;
+}
+
+/*
+ * This function can be called by application to send ANY outgoing message
+ * to remote party.
+ */
+PJ_DEF(pj_status_t) pjsip_dlg_send_msg( pjsip_dlg *dlg, pjsip_tx_data *tdata )
+{
+ pj_status_t status;
+ int tsx_status;
+ struct dialog_lock_data lck;
+
+ pj_assert(dlg != NULL && tdata != NULL);
+ if (!dlg || !tdata) {
+ return -1;
+ }
+
+ lock_dialog(dlg, &lck);
+
+ if (tdata->msg->type == PJSIP_REQUEST_MSG) {
+ int request_cseq;
+ pjsip_msg *msg = tdata->msg;
+ pjsip_cseq_hdr *cseq_hdr;
+
+ switch (msg->line.req.method.id) {
+ case PJSIP_CANCEL_METHOD:
+
+ /* Check the INVITE transaction state. */
+ tsx_status = dlg->invite_tsx->status_code;
+ if (tsx_status >= 200) {
+ /* Already terminated. Can't cancel. */
+ status = -1;
+ goto on_return;
+ }
+
+ /* If we've got provisional response, then send CANCEL and wait for
+ * the response to INVITE to arrive. Otherwise just send CANCEL and
+ * terminate the INVITE.
+ */
+ if (tsx_status < 100) {
+ pjsip_tsx_terminate( dlg->invite_tsx,
+ PJSIP_SC_REQUEST_TERMINATED);
+ status = 0;
+ goto on_return;
+ }
+
+ status = 0;
+ request_cseq = dlg->invite_tsx->cseq;
+ break;
+
+ case PJSIP_ACK_METHOD:
+ /* Sending ACK outside of transaction is not supported at present! */
+ pj_assert(0);
+ status = 0;
+ request_cseq = dlg->local.cseq;
+ break;
+
+ case PJSIP_INVITE_METHOD:
+ /* For an initial INVITE, reset dialog state to NULL so we get
+ * 'normal' UAC notifications such as on_provisional(), etc.
+ * Initial INVITE is the request that is sent when the dialog has
+ * not been established yet. It's not necessarily the first INVITE
+ * sent, as when the Authorization fails, subsequent INVITE are also
+ * considered as an initial INVITE.
+ */
+ if (dlg->state != PJSIP_DIALOG_STATE_ESTABLISHED) {
+ /* Set state to NULL. */
+ dlg_set_state(dlg, PJSIP_DIALOG_STATE_NULL, NULL);
+
+ } else {
+ /* This is a re-INVITE */
+ }
+ status = 0;
+ request_cseq = dlg->local.cseq + 1;
+ break;
+
+ default:
+ status = 0;
+ request_cseq = dlg->local.cseq + 1;
+ break;
+ }
+
+ if (status != 0)
+ goto on_return;
+
+ /* Update dialog's local CSeq, if necessary. */
+ if (request_cseq != dlg->local.cseq)
+ dlg->local.cseq = request_cseq;
+
+ /* Update CSeq header in the request. */
+ cseq_hdr = (pjsip_cseq_hdr*) pjsip_msg_find_hdr( tdata->msg,
+ PJSIP_H_CSEQ, NULL);
+ pj_assert(cseq_hdr != NULL);
+
+ /* Update the CSeq */
+ cseq_hdr->cseq = request_cseq;
+
+ /* Force the whole message to be re-printed. */
+ pjsip_tx_data_invalidate_msg( tdata );
+
+ /* Now send the request. */
+ status = dlg_send_request(dlg, tdata);
+
+ } else {
+ /*
+ * This is only valid for sending response to INVITE!
+ */
+ pjsip_cseq_hdr *cseq_hdr;
+
+ if (dlg->invite_tsx == NULL || dlg->invite_tsx->status_code >= 200) {
+ status = -1;
+ goto on_return;
+ }
+
+ cseq_hdr = (pjsip_cseq_hdr*) pjsip_msg_find_hdr( tdata->msg,
+ PJSIP_H_CSEQ, NULL);
+ pj_assert(cseq_hdr);
+
+ if (cseq_hdr->method.id != PJSIP_INVITE_METHOD) {
+ status = -1;
+ goto on_return;
+ }
+
+ pj_assert(cseq_hdr->cseq == dlg->invite_tsx->cseq);
+
+ pjsip_tsx_on_tx_msg(dlg->invite_tsx, tdata);
+ status = 0;
+ }
+
+on_return:
+ /* Unlock dialog. */
+ unlock_dialog(dlg, &lck);
+
+ /* Whatever happen delete the message. */
+ pjsip_tx_data_dec_ref( tdata );
+
+ return status;
+}
+
+/*
+ * Sends outgoing invitation.
+ */
+PJ_DEF(pjsip_tx_data*) pjsip_dlg_invite( pjsip_dlg *dlg )
+{
+ pjsip_method method;
+ struct dialog_lock_data lck;
+ const pjsip_allow_hdr *allow_hdr;
+ pjsip_tx_data *tdata;
+
+ pj_assert(dlg != NULL);
+ if (!dlg) {
+ return NULL;
+ }
+
+ PJ_LOG(4, (dlg->obj_name, "request to send invitation"));
+
+ /* Lock dialog. */
+ lock_dialog(dlg, &lck);
+
+ /* Create request. */
+ pjsip_method_set( &method, PJSIP_INVITE_METHOD);
+ tdata = pjsip_dlg_create_request( dlg, &method, -1 );
+ if (tdata == NULL) {
+ unlock_dialog(dlg, &lck);
+ return NULL;
+ }
+
+ /* Invite SHOULD contain "Allow" header. */
+ allow_hdr = pjsip_endpt_get_allow_hdr( dlg->ua->endpt );
+ if (allow_hdr) {
+ pjsip_msg_add_hdr( tdata->msg,
+ pjsip_hdr_shallow_clone( tdata->pool, allow_hdr));
+ }
+
+ /* Unlock dialog. */
+ unlock_dialog(dlg, &lck);
+
+ return tdata;
+}
+
+/*
+ * Cancel pending outgoing dialog invitation.
+ */
+PJ_DEF(pjsip_tx_data*) pjsip_dlg_cancel( pjsip_dlg *dlg )
+{
+ pjsip_tx_data *tdata = NULL;
+ struct dialog_lock_data lck;
+
+ pj_assert(dlg != NULL);
+ if (!dlg) {
+ return NULL;
+ }
+
+ PJ_LOG(4, (dlg->obj_name, "request to cancel invitation"));
+
+ lock_dialog(dlg, &lck);
+
+ /* Check the INVITE transaction. */
+ if (dlg->invite_tsx == NULL || dlg->role != PJSIP_ROLE_UAC) {
+ PJ_LOG(2, (dlg->obj_name, "pjsip_dlg_cancel failed: "
+ "no INVITE transaction found"));
+ goto on_return;
+ }
+
+ /* Construct the CANCEL request. */
+ tdata = pjsip_endpt_create_cancel( dlg->ua->endpt,
+ dlg->invite_tsx->last_tx );
+ if (tdata == NULL) {
+ PJ_LOG(2, (dlg->obj_name, "pjsip_dlg_cancel failed: "
+ "unable to construct request"));
+ goto on_return;
+ }
+
+ /* Add reference counter to tdata. */
+ pjsip_tx_data_add_ref(tdata);
+
+on_return:
+ unlock_dialog(dlg, &lck);
+ return tdata;
+}
+
+
+/*
+ * Answer incoming dialog invitation, with either provisional responses
+ * or a final response.
+ */
+PJ_DEF(pjsip_tx_data*) pjsip_dlg_answer( pjsip_dlg *dlg, int code )
+{
+ pjsip_tx_data *tdata = NULL;
+ pjsip_msg *msg;
+ struct dialog_lock_data lck;
+
+ pj_assert(dlg != NULL);
+ if (!dlg) {
+ return NULL;
+ }
+
+ PJ_LOG(4, (dlg->obj_name, "pjsip_dlg_answer: code=%d", code));
+
+ /* Lock dialog. */
+ lock_dialog(dlg, &lck);
+
+ /* Must have pending INVITE. */
+ if (dlg->invite_tsx == NULL) {
+ PJ_LOG(2, (dlg->obj_name, "pjsip_dlg_answer: no INVITE transaction found"));
+ goto on_return;
+ }
+ /* Must be UAS. */
+ if (dlg->role != PJSIP_ROLE_UAS) {
+ PJ_LOG(2, (dlg->obj_name, "pjsip_dlg_answer: not UAS"));
+ goto on_return;
+ }
+ /* Must have not answered with final response before. */
+ if (dlg->invite_tsx->status_code >= 200) {
+ PJ_LOG(2, (dlg->obj_name, "pjsip_dlg_answer: transaction already terminated "
+ "with status %d", dlg->invite_tsx->status_code));
+ goto on_return;
+ }
+
+ /* Get transmit data and the message.
+ * We will rewrite the message with a new status code.
+ */
+ only if tdata is not pending!!!
+ tdata = dlg->invite_tsx->last_tx;
+ msg = tdata->msg;
+
+ /* Set status code and reason phrase. */
+ if (code < 100 || code >= 700) code = 500;
+ msg->line.status.code = code;
+ msg->line.status.reason = *pjsip_get_status_text(code);
+
+ /* For 2xx response, Contact and Record-Route must be added. */
+ if (PJSIP_IS_STATUS_IN_CLASS(code,200)) {
+ const pjsip_allow_hdr *allow_hdr;
+
+ if (pjsip_msg_find_hdr(msg, PJSIP_H_CONTACT, NULL) == NULL) {
+ pjsip_contact_hdr *contact;
+ contact = pjsip_hdr_shallow_clone( tdata->pool, dlg->local.contact);
+ pjsip_msg_add_hdr( msg, (pjsip_hdr*)contact );
+ }
+
+ /* 2xx response MUST contain "Allow" header. */
+ allow_hdr = pjsip_endpt_get_allow_hdr( dlg->ua->endpt );
+ if (allow_hdr) {
+ pjsip_msg_add_hdr( msg, pjsip_hdr_shallow_clone( tdata->pool, allow_hdr));
+ }
+ }
+
+ /* for all but 100 responses, To-tag must be set. */
+ if (code != 100) {
+ pjsip_to_hdr *to;
+ to = pjsip_msg_find_hdr( msg, PJSIP_H_TO, NULL);
+ to->tag = dlg->local.tag;
+ }
+
+ /* Reset packet buffer. */
+ pjsip_tx_data_invalidate_msg(tdata);
+
+ /* Add reference counter */
+ pjsip_tx_data_add_ref(tdata);
+
+on_return:
+
+ /* Unlock dialog. */
+ unlock_dialog(dlg, &lck);
+
+ return tdata;
+}
+
+
+/*
+ * Send BYE request to terminate the dialog's session.
+ */
+PJ_DEF(pjsip_tx_data*) pjsip_dlg_bye( pjsip_dlg *dlg )
+{
+ pjsip_method method;
+ struct dialog_lock_data lck;
+ pjsip_tx_data *tdata;
+
+ if (!dlg) {
+ pj_assert(dlg != NULL);
+ return NULL;
+ }
+
+ PJ_LOG(4, (dlg->obj_name, "request to terminate session"));
+
+ lock_dialog(dlg, &lck);
+
+ pjsip_method_set( &method, PJSIP_BYE_METHOD);
+ tdata = pjsip_dlg_create_request( dlg, &method, -1 );
+
+ unlock_dialog(dlg, &lck);
+
+ return tdata;
+}
+
+/*
+ * High level function to disconnect dialog's session. Depending on dialog's
+ * state, this function will either send CANCEL, final response, or BYE to
+ * trigger the disconnection. A status code must be supplied, which will be
+ * sent if dialog will be transmitting a final response to INVITE.
+ */
+PJ_DEF(pjsip_tx_data*) pjsip_dlg_disconnect( pjsip_dlg *dlg,
+ int status_code )
+{
+ pjsip_tx_data *tdata = NULL;
+
+ pj_assert(dlg != NULL);
+ if (!dlg) {
+ return NULL;
+ }
+
+ switch (dlg->state) {
+ case PJSIP_DIALOG_STATE_INCOMING:
+ tdata = pjsip_dlg_answer(dlg, status_code);
+ break;
+
+ case PJSIP_DIALOG_STATE_CALLING:
+ tdata = pjsip_dlg_cancel(dlg);
+ break;
+
+ case PJSIP_DIALOG_STATE_PROCEEDING:
+ if (dlg->role == PJSIP_ROLE_UAC) {
+ tdata = pjsip_dlg_cancel(dlg);
+ } else {
+ tdata = pjsip_dlg_answer(dlg, status_code);
+ }
+ break;
+
+ case PJSIP_DIALOG_STATE_ESTABLISHED:
+ tdata = pjsip_dlg_bye(dlg);
+ break;
+
+ default:
+ PJ_LOG(4,(dlg->obj_name, "Invalid state %s in pjsip_dlg_disconnect()",
+ dlg_state_names[dlg->state]));
+ break;
+ }
+
+ return tdata;
+}
+
+/*
+ * Handling of the receipt of 2xx/INVITE response.
+ */
+static void dlg_on_recv_2xx_invite( pjsip_dlg *dlg,
+ pjsip_event *event )
+{
+ pjsip_msg *msg;
+ pjsip_contact_hdr *contact;
+ pjsip_hdr *hdr, *end_hdr;
+ pjsip_method method;
+ pjsip_tx_data *ack_tdata;
+
+ /* Get the message */
+ msg = event->src.rdata->msg;
+
+ /* Update remote's tag information. */
+ pj_strdup(dlg->pool, &dlg->remote.info->tag, &event->src.rdata->to_tag);
+
+ /* Copy Contact information in the 2xx/INVITE response to dialog's.
+ * remote contact
+ */
+ contact = pjsip_msg_find_hdr( msg, PJSIP_H_CONTACT, NULL);
+ if (contact) {
+ dlg->remote.contact = pjsip_hdr_clone( dlg->pool, contact );
+ } else {
+ /* duplicate contact from "From" header (?) */
+ PJ_LOG(4,(dlg->obj_name, "Received 200/OK to INVITE with no Contact!"));
+ dlg->remote.contact = pjsip_contact_hdr_create(dlg->pool);
+ dlg->remote.contact->uri = dlg->remote.info->uri;
+ }
+
+ /* Copy Record-Route header (in reverse order) as dialog's route-set,
+ * overwriting previous route-set, if any, even if the received route-set
+ * is empty.
+ */
+ pj_list_init(&dlg->route_set);
+ end_hdr = &msg->hdr;
+ for (hdr = msg->hdr.prev; hdr!=end_hdr; hdr = hdr->prev) {
+ if (hdr->type == PJSIP_H_RECORD_ROUTE) {
+ pjsip_route_hdr *r;
+ r = pjsip_hdr_clone(dlg->pool, hdr);
+ pjsip_routing_hdr_set_route(r);
+ pj_list_insert_before(&dlg->route_set, r);
+ }
+ }
+
+ /* On receipt of 200/INVITE response, send ACK.
+ * This ack must be saved and retransmitted whenever we receive
+ * 200/INVITE retransmission, until 64*T1 seconds elapsed.
+ */
+ pjsip_method_set( &method, PJSIP_ACK_METHOD);
+ ack_tdata = pjsip_dlg_create_request( dlg, &method, dlg->invite_tsx->cseq);
+ if (ack_tdata == NULL) {
+ //PJ_TODO(HANDLE_CREATE_ACK_FAILURE)
+ PJ_LOG(2, (dlg->obj_name, "Error sending ACK msg: can't create request"));
+ return;
+ }
+
+ /* Send with the transaction. */
+ pjsip_tsx_on_tx_ack( dlg->invite_tsx, ack_tdata);
+
+ /* Decrement reference counter because pjsip_dlg_create_request
+ * automatically increments the request.
+ */
+ pjsip_tx_data_dec_ref( ack_tdata );
+}
+
+/*
+ * State NULL, before any events have been received.
+ */
+static int dlg_on_state_null( pjsip_dlg *dlg,
+ pjsip_transaction *tsx,
+ pjsip_event *event)
+{
+ if (event->type == PJSIP_EVENT_TSX_STATE_CHANGED &&
+ event->src_type == PJSIP_EVENT_RX_MSG)
+ {
+ pjsip_hdr *hdr, *hdr_list;
+
+ pj_assert(tsx->method.id == PJSIP_INVITE_METHOD);
+
+ /* Save the INVITE transaction. */
+ dlg->invite_tsx = tsx;
+
+ /* Change state to INCOMING */
+ dlg_set_state(dlg, PJSIP_DIALOG_STATE_INCOMING, event);
+
+ /* Create response buffer. */
+ tsx->last_tx = pjsip_endpt_create_response( dlg->ua->endpt, event->src.rdata, 100);
+ pjsip_tx_data_add_ref(tsx->last_tx);
+
+ /* Copy the Record-Route headers into dialog's route_set, maintaining
+ * the order.
+ */
+ pj_list_init(&dlg->route_set);
+ hdr_list = &event->src.rdata->msg->hdr;
+ hdr = hdr_list->next;
+ while (hdr != hdr_list) {
+ if (hdr->type == PJSIP_H_RECORD_ROUTE) {
+ pjsip_route_hdr *route;
+ route = pjsip_hdr_clone(dlg->pool, hdr);
+ pjsip_routing_hdr_set_route(route);
+ pj_list_insert_before(&dlg->route_set, route);
+ }
+ hdr = hdr->next;
+ }
+
+ /* Notify application. */
+ dlg_call_dlg_callback(dlg, PJSIP_DIALOG_EVENT_STATE_CHANGED, event);
+
+ } else if (event->type == PJSIP_EVENT_TSX_STATE_CHANGED &&
+ event->src_type == PJSIP_EVENT_TX_MSG)
+ {
+ pj_assert(tsx->method.id == PJSIP_INVITE_METHOD);
+
+ /* Save the INVITE transaction. */
+ dlg->invite_tsx = tsx;
+
+ /* Change state to CALLING. */
+ dlg_set_state(dlg, PJSIP_DIALOG_STATE_CALLING, event);
+
+ /* Notify application. */
+ dlg_call_dlg_callback(dlg, PJSIP_DIALOG_EVENT_STATE_CHANGED, event);
+
+ } else {
+ dlg_call_dlg_callback(dlg, PJSIP_DIALOG_EVENT_OTHER, event);
+ }
+
+ return 0;
+}
+
+/*
+ * State INCOMING is after the (callee) dialog has been initialized with
+ * the incoming request, but before any responses is sent by the dialog.
+ */
+static int dlg_on_state_incoming( pjsip_dlg *dlg,
+ pjsip_transaction *tsx,
+ pjsip_event *event)
+{
+ return dlg_on_state_proceeding_callee( dlg, tsx, event );
+}
+
+/*
+ * State CALLING is after the (caller) dialog has sent outgoing invitation
+ * but before any responses are received.
+ */
+static int dlg_on_state_calling( pjsip_dlg *dlg,
+ pjsip_transaction *tsx,
+ pjsip_event *event)
+{
+ if (tsx == dlg->invite_tsx) {
+ return dlg_on_state_proceeding_caller( dlg, tsx, event );
+ }
+ return 0;
+}
+
+/*
+ * State PROCEEDING is after provisional response is received.
+ * Since the processing is similar to state CALLING, this function is also
+ * called for CALLING state.
+ */
+static int dlg_on_state_proceeding_caller( pjsip_dlg *dlg,
+ pjsip_transaction *tsx,
+ pjsip_event *event)
+{
+ int dlg_is_terminated = 0;
+
+ /* We only care about our INVITE transaction.
+ * Ignore other transaction progression (such as CANCEL).
+ */
+ if (tsx != dlg->invite_tsx) {
+ dlg_call_dlg_callback(dlg, PJSIP_DIALOG_EVENT_OTHER, event);
+ return 0;
+ }
+
+ if (event->type == PJSIP_EVENT_TSX_STATE_CHANGED) {
+ switch (tsx->state) {
+ case PJSIP_TSX_STATE_PROCEEDING:
+ if (dlg->state != PJSIP_DIALOG_STATE_PROCEEDING) {
+ /* Change state to PROCEEDING */
+ dlg_set_state(dlg, PJSIP_DIALOG_STATE_PROCEEDING, event);
+
+ /* Notify application. */
+ dlg_call_dlg_callback(dlg, PJSIP_DIALOG_EVENT_STATE_CHANGED, event);
+ } else {
+ /* Also notify application. */
+ dlg_call_dlg_callback(dlg, PJSIP_DIALOG_EVENT_OTHER, event);
+ }
+ break;
+
+ case PJSIP_TSX_STATE_COMPLETED:
+ /* Change dialog state. */
+ if (PJSIP_IS_STATUS_IN_CLASS(tsx->status_code, 200)) {
+ /* Update remote target, take it from the contact hdr. */
+ pjsip_contact_hdr *contact;
+ contact = pjsip_msg_find_hdr(event->src.rdata->msg,
+ PJSIP_H_CONTACT, NULL);
+ if (contact) {
+ dlg->remote.target = pjsip_uri_clone(dlg->pool, contact->uri);
+ } else {
+ PJ_LOG(4,(dlg->obj_name,
+ "Warning: found no Contact hdr in 200/OK"));
+ }
+ dlg_set_state(dlg, PJSIP_DIALOG_STATE_CONNECTING, event);
+ } else if (tsx->status_code==401 || tsx->status_code==407) {
+ /* Handle Authentication challenge. */
+ pjsip_tx_data *tdata;
+ tdata = pjsip_auth_reinit_req( dlg->ua->endpt,
+ dlg->pool, &dlg->auth_sess,
+ dlg->cred_count, dlg->cred_info,
+ tsx->last_tx, event->src.rdata);
+ if (tdata) {
+ /* Re-use original request, with a new transaction.
+ * Need not to worry about CSeq, dialog will take care.
+ */
+ pjsip_dlg_send_msg(dlg, tdata);
+ return 0;
+ } else {
+ dlg_set_state(dlg, PJSIP_DIALOG_STATE_DISCONNECTED, event);
+ }
+ } else {
+ dlg_set_state(dlg, PJSIP_DIALOG_STATE_DISCONNECTED, event);
+ }
+
+ /* Notify application. */
+ dlg_call_dlg_callback(dlg, PJSIP_DIALOG_EVENT_STATE_CHANGED, event);
+
+ /* Send ACK when dialog is connected. */
+ if (PJSIP_IS_STATUS_IN_CLASS(tsx->status_code, 200)) {
+ pj_assert(event->src_type == PJSIP_EVENT_RX_MSG);
+ dlg_on_recv_2xx_invite(dlg, event);
+ }
+ break;
+
+ case PJSIP_TSX_STATE_TERMINATED:
+ /*
+ * Transaction is terminated because of timeout or transport error.
+ * To let the application go to normal state progression, call the
+ * callback twice. First is to emulate disconnection, and then call
+ * again (with state TERMINATED) to destroy the dialog.
+ */
+ dlg_set_state(dlg, PJSIP_DIALOG_STATE_DISCONNECTED, event);
+ dlg_call_dlg_callback(dlg, PJSIP_DIALOG_EVENT_STATE_CHANGED, event);
+
+ /* The INVITE transaction will be destroyed, so release reference
+ * to it.
+ */
+ dlg->invite_tsx = NULL;
+
+ /* We should terminate the dialog now.
+ * But it's possible that we have other pending transactions (for
+ * example, outgoing CANCEL is in progress).
+ * So destroy the dialog only if there's no other transaction.
+ */
+ if (dlg->pending_tsx_count == 0) {
+ dlg_set_state(dlg, PJSIP_DIALOG_STATE_TERMINATED, event);
+ dlg_call_dlg_callback(dlg, PJSIP_DIALOG_EVENT_STATE_CHANGED, event);
+ dlg_is_terminated = 1;
+ }
+ break;
+
+ default:
+ pj_assert(0);
+ break;
+ }
+ } else {
+ dlg_call_dlg_callback(dlg, PJSIP_DIALOG_EVENT_OTHER, event);
+ }
+ return dlg_is_terminated ? -1 : 0;
+}
+
+/*
+ * State PROCEEDING for UAS is after the callee send provisional response.
+ * This function is also called for INCOMING state.
+ */
+static int dlg_on_state_proceeding_callee( pjsip_dlg *dlg,
+ pjsip_transaction *tsx,
+ pjsip_event *event)
+{
+ int dlg_is_terminated = 0;
+
+ pj_assert( dlg->invite_tsx != NULL );
+
+ if (event->type == PJSIP_EVENT_TSX_STATE_CHANGED &&
+ event->src_type == PJSIP_EVENT_TX_MSG &&
+ tsx == dlg->invite_tsx)
+ {
+ switch (tsx->state) {
+ case PJSIP_TSX_STATE_PROCEEDING:
+ /* Change state to PROCEEDING */
+ dlg_set_state(dlg, PJSIP_DIALOG_STATE_PROCEEDING, event);
+
+ /* Notify application. */
+ dlg_call_dlg_callback(dlg, PJSIP_DIALOG_EVENT_STATE_CHANGED, event);
+ break;
+
+ case PJSIP_TSX_STATE_COMPLETED:
+ case PJSIP_TSX_STATE_TERMINATED:
+ /* Change dialog state. */
+ if (PJSIP_IS_STATUS_IN_CLASS(tsx->status_code, 200)) {
+ dlg_set_state(dlg, PJSIP_DIALOG_STATE_CONNECTING, event);
+ } else {
+ dlg_set_state(dlg, PJSIP_DIALOG_STATE_DISCONNECTED, event);
+ }
+
+ /* Notify application. */
+ dlg_call_dlg_callback(dlg, PJSIP_DIALOG_EVENT_STATE_CHANGED, event);
+
+ /* If transaction is terminated in non-2xx situation,
+ * terminate dialog as well. This happens when something unexpected
+ * occurs, such as transport error.
+ */
+ if (tsx->state == PJSIP_TSX_STATE_TERMINATED &&
+ !PJSIP_IS_STATUS_IN_CLASS(tsx->status_code, 200))
+ {
+ dlg_set_state(dlg, PJSIP_DIALOG_STATE_TERMINATED, event);
+ dlg_call_dlg_callback(dlg, PJSIP_DIALOG_EVENT_STATE_CHANGED, event);
+ dlg_is_terminated = 1;
+ }
+ break;
+
+ default:
+ pj_assert(0);
+ break;
+ }
+
+ } else if (event->type == PJSIP_EVENT_TSX_STATE_CHANGED &&
+ event->src_type == PJSIP_EVENT_RX_MSG &&
+ tsx->method.id == PJSIP_CANCEL_METHOD)
+ {
+ pjsip_tx_data *tdata;
+
+ /* Check if sequence number matches the pending INVITE. */
+ if (dlg->invite_tsx==NULL ||
+ pj_strcmp(&tsx->branch, &dlg->invite_tsx->branch) != 0)
+ {
+ PJ_LOG(4, (dlg->obj_name, "Received CANCEL with no matching INVITE"));
+
+ /* No matching INVITE transaction found. */
+ tdata = pjsip_endpt_create_response(dlg->ua->endpt,
+ event->src.rdata,
+ PJSIP_SC_CALL_TSX_DOES_NOT_EXIST );
+ pjsip_tsx_on_tx_msg(tsx, tdata);
+ return 0;
+ }
+
+ /* Always respond the CANCEL with 200/CANCEL no matter what. */
+ tdata = pjsip_endpt_create_response(dlg->ua->endpt,
+ event->src.rdata,
+ 200 );
+ pjsip_tsx_on_tx_msg( tsx, tdata );
+
+ /* Respond the INVITE transaction with 487, only if transaction has
+ * not completed.
+ */
+ if (dlg->invite_tsx->last_tx) {
+ if (dlg->invite_tsx->status_code < 200) {
+ tdata = dlg->invite_tsx->last_tx;
+ tdata->msg->line.status.code = 487;
+ tdata->msg->line.status.reason = *pjsip_get_status_text(487);
+ /* Reset packet buffer. */
+ pjsip_tx_data_invalidate_msg(tdata);
+ pjsip_tsx_on_tx_msg( dlg->invite_tsx, tdata );
+ } else {
+ PJ_LOG(4, (dlg->obj_name, "Received CANCEL with no effect, "
+ "Transaction already terminated "
+ "with status %d",
+ dlg->invite_tsx->status_code));
+ }
+ } else {
+ tdata = pjsip_endpt_create_response(dlg->ua->endpt,
+ event->src.rdata,
+ 487);
+ pjsip_tsx_on_tx_msg( dlg->invite_tsx, tdata );
+ }
+ } else {
+ dlg_call_dlg_callback(dlg, PJSIP_DIALOG_EVENT_OTHER, event);
+ }
+
+ return dlg_is_terminated ? -1 : 0;
+}
+
+static int dlg_on_state_proceeding( pjsip_dlg *dlg,
+ pjsip_transaction *tsx,
+ pjsip_event *event)
+{
+ if (dlg->role == PJSIP_ROLE_UAC) {
+ return dlg_on_state_proceeding_caller( dlg, tsx, event );
+ } else {
+ return dlg_on_state_proceeding_callee( dlg, tsx, event );
+ }
+}
+
+static int dlg_on_state_connecting( pjsip_dlg *dlg,
+ pjsip_transaction *tsx,
+ pjsip_event *event)
+{
+ if (tsx == dlg->invite_tsx) {
+ if (event->type == PJSIP_EVENT_TSX_STATE_CHANGED &&
+ (tsx->state == PJSIP_TSX_STATE_TERMINATED ||
+ tsx->state == PJSIP_TSX_STATE_COMPLETED ||
+ tsx->state == PJSIP_TSX_STATE_CONFIRMED))
+ {
+ if (PJSIP_IS_STATUS_IN_CLASS(tsx->status_code, 200)) {
+ dlg_set_state(dlg, PJSIP_DIALOG_STATE_ESTABLISHED, event);
+ } else {
+ /* Probably because we never get the ACK, or transport error
+ * when sending ACK.
+ */
+ dlg_set_state(dlg, PJSIP_DIALOG_STATE_DISCONNECTED, event);
+ }
+ dlg_call_dlg_callback(dlg, PJSIP_DIALOG_EVENT_STATE_CHANGED, event);
+ } else {
+ dlg_call_dlg_callback(dlg, PJSIP_DIALOG_EVENT_OTHER, event);
+ }
+ } else {
+ /* Handle case when transaction is started when dialog is connecting
+ * (e.g. BYE requests cross wire.
+ */
+ if (event->type == PJSIP_EVENT_TSX_STATE_CHANGED &&
+ event->src_type == PJSIP_EVENT_RX_MSG &&
+ tsx->role == PJSIP_ROLE_UAS)
+ {
+ pjsip_tx_data *response;
+
+ if (tsx->status_code >= 200)
+ return 0;
+
+ if (tsx->method.id == PJSIP_BYE_METHOD) {
+ /* Set state to DISCONNECTED. */
+ dlg_set_state(dlg, PJSIP_DIALOG_STATE_DISCONNECTED, event);
+
+ /* Notify application. */
+ dlg_call_dlg_callback(dlg, PJSIP_DIALOG_EVENT_STATE_CHANGED, event);
+
+ response = pjsip_endpt_create_response( dlg->ua->endpt,
+ event->src.rdata, 200);
+ } else {
+ response = pjsip_endpt_create_response( dlg->ua->endpt, event->src.rdata,
+ PJSIP_SC_INTERNAL_SERVER_ERROR);
+ }
+
+ if (response)
+ pjsip_tsx_on_tx_msg(tsx, response);
+
+ return 0;
+ }
+ }
+ return 0;
+}
+
+static int dlg_on_state_established( pjsip_dlg *dlg,
+ pjsip_transaction *tsx,
+ pjsip_event *event)
+{
+ PJ_UNUSED_ARG(tsx)
+
+ if (tsx && tsx->method.id == PJSIP_BYE_METHOD) {
+ /* Set state to DISCONNECTED. */
+ dlg_set_state(dlg, PJSIP_DIALOG_STATE_DISCONNECTED, event);
+
+ /* Notify application. */
+ dlg_call_dlg_callback(dlg, PJSIP_DIALOG_EVENT_STATE_CHANGED, event);
+
+ /* Answer with 200/BYE. */
+ if (event->src_type == PJSIP_EVENT_RX_MSG) {
+ pjsip_tx_data *tdata;
+ tdata = pjsip_endpt_create_response(dlg->ua->endpt,
+ event->src.rdata,
+ 200 );
+ if (tdata)
+ pjsip_tsx_on_tx_msg( tsx, tdata );
+ }
+ } else if (tsx && event->src_type == PJSIP_EVENT_RX_MSG) {
+ pjsip_method_e method = event->src.rdata->cseq->method.id;
+
+ PJ_TODO(PROPERLY_HANDLE_REINVITATION)
+
+ /* Reinvitation. The message may be INVITE or an ACK. */
+ if (method == PJSIP_INVITE_METHOD) {
+ if (dlg->invite_tsx && dlg->invite_tsx->status_code < 200) {
+ /* Section 14.2: A UAS that receives a second INVITE before it
+ * sends the final response to a first INVITE with a lower
+ * CSeq sequence number on the same dialog MUST return a 500
+ * (Server Internal Error) response to the second INVITE and
+ * MUST include a Retry-After header field with a randomly
+ * chosen value of between 0 and 10 seconds.
+ */
+ pjsip_retry_after_hdr *hdr;
+ pjsip_tx_data *tdata =
+ pjsip_endpt_create_response(dlg->ua->endpt,
+ event->src.rdata, 500);
+
+ if (!tdata)
+ return 0;
+
+ /* Add Retry-After. */
+ hdr = pjsip_retry_after_hdr_create(tdata->pool);
+ hdr->ivalue = 9;
+ pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr*)hdr);
+
+ /* Send. */
+ pjsip_tsx_on_tx_msg(tsx, tdata);
+
+ return 0;
+ }
+
+ /* Keep this as our current INVITE transaction. */
+ dlg->invite_tsx = tsx;
+
+ /* Create response buffer. */
+ tsx->last_tx = pjsip_endpt_create_response( dlg->ua->endpt,
+ event->src.rdata, 100);
+ pjsip_tx_data_add_ref(tsx->last_tx);
+
+ }
+
+ /* Notify application. */
+ dlg_call_dlg_callback(dlg, PJSIP_DIALOG_EVENT_MID_CALL_REQUEST, event);
+
+ } else {
+ dlg_call_dlg_callback(dlg, PJSIP_DIALOG_EVENT_OTHER, event);
+ }
+
+ return 0;
+}
+
+static int dlg_on_state_disconnected( pjsip_dlg *dlg,
+ pjsip_transaction *tsx,
+ pjsip_event *event)
+{
+ PJ_UNUSED_ARG(tsx)
+
+ /* Handle case when transaction is started when dialog is disconnected
+ * (e.g. BYE requests cross wire.
+ */
+ if (event->type == PJSIP_EVENT_TSX_STATE_CHANGED &&
+ event->src_type == PJSIP_EVENT_RX_MSG &&
+ tsx->role == PJSIP_ROLE_UAS)
+ {
+ pjsip_tx_data *response = NULL;
+
+ if (tsx->status_code >= 200)
+ return 0;
+
+ if (tsx->method.id == PJSIP_BYE_METHOD) {
+ response = pjsip_endpt_create_response( dlg->ua->endpt,
+ event->src.rdata, 200);
+ } else {
+ response = pjsip_endpt_create_response( dlg->ua->endpt, event->src.rdata,
+ PJSIP_SC_INTERNAL_SERVER_ERROR);
+ }
+ if (response)
+ pjsip_tsx_on_tx_msg(tsx, response);
+
+ return 0;
+ }
+ /* Handle case when outgoing BYE was rejected with 401/407 */
+ else if (event->type == PJSIP_EVENT_TSX_STATE_CHANGED &&
+ event->src_type == PJSIP_EVENT_RX_MSG &&
+ tsx->role == PJSIP_ROLE_UAC)
+ {
+ if (tsx->status_code==401 || tsx->status_code==407) {
+ pjsip_tx_data *tdata;
+ tdata = pjsip_auth_reinit_req( dlg->ua->endpt, dlg->pool,
+ &dlg->auth_sess,
+ dlg->cred_count, dlg->cred_info,
+ tsx->last_tx, event->src.rdata);
+ if (tdata) {
+ pjsip_dlg_send_msg(dlg, tdata);
+ }
+ }
+ }
+
+
+ if (dlg->pending_tsx_count == 0) {
+ /* Set state to TERMINATED. */
+ dlg_set_state(dlg, PJSIP_DIALOG_STATE_TERMINATED, event);
+
+ /* Notify application. */
+ dlg_call_dlg_callback(dlg, PJSIP_DIALOG_EVENT_STATE_CHANGED, event);
+
+ return -1;
+ } else {
+ dlg_call_dlg_callback(dlg, PJSIP_DIALOG_EVENT_OTHER, event);
+ }
+
+ return 0;
+}
+
+static int dlg_on_state_terminated( pjsip_dlg *dlg,
+ pjsip_transaction *tsx,
+ pjsip_event *event)
+{
+ PJ_UNUSED_ARG(dlg)
+ PJ_UNUSED_ARG(tsx)
+ PJ_UNUSED_ARG(event)
+
+ return -1;
+}
+
diff --git a/pjsip/src/pjsip-ua/sip_reg.c b/pjsip/src/pjsip-ua/sip_reg.c
index e7bf9bd6..1b22170e 100644
--- a/pjsip/src/pjsip-ua/sip_reg.c
+++ b/pjsip/src/pjsip-ua/sip_reg.c
@@ -1,511 +1,511 @@
-/* $Id$ */
-/*
- * Copyright (C) 2003-2006 Benny Prijono <benny@prijono.org>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
-#include <pjsip_mod_ua/sip_reg.h>
-#include <pjsip/sip_endpoint.h>
-#include <pjsip/sip_parser.h>
-#include <pjsip/sip_module.h>
-#include <pjsip/sip_transaction.h>
-#include <pjsip/sip_event.h>
-#include <pjsip/sip_util.h>
-#include <pjsip/sip_auth_msg.h>
-#include <pj/pool.h>
-#include <pj/string.h>
-#include <pj/guid.h>
-#include <pj/log.h>
-
-#define REFRESH_TIMER 1
-#define DELAY_BEFORE_REFRESH 5
-#define THIS_FILE "sip_regc.c"
-
-/**
- * SIP client registration structure.
- */
-struct pjsip_regc
-{
- pj_pool_t *pool;
- pjsip_endpoint *endpt;
- pj_bool_t _delete_flag;
- int pending_tsx;
-
- void *token;
- pjsip_regc_cb *cb;
-
- pj_str_t str_srv_url;
- pjsip_uri *srv_url;
- pjsip_cid_hdr *cid_hdr;
- pjsip_cseq_hdr *cseq_hdr;
- pjsip_from_hdr *from_hdr;
- pjsip_to_hdr *to_hdr;
- char *contact_buf;
- pjsip_generic_string_hdr *contact_hdr;
- pjsip_expires_hdr *expires_hdr;
- pjsip_contact_hdr *unreg_contact_hdr;
- pjsip_expires_hdr *unreg_expires_hdr;
- pj_uint32_t expires;
-
- /* Credentials. */
- int cred_count;
- pjsip_cred_info *cred_info;
-
- /* Authorization sessions. */
- pjsip_auth_session auth_sess_list;
-
- /* Auto refresh registration. */
- pj_bool_t auto_reg;
- pj_timer_entry timer;
-};
-
-
-
-PJ_DEF(pjsip_regc*) pjsip_regc_create( pjsip_endpoint *endpt, void *token,
- pjsip_regc_cb *cb)
-{
- pj_pool_t *pool;
- pjsip_regc *regc;
-
- if (cb == NULL)
- return NULL;
-
- pool = pjsip_endpt_create_pool(endpt, "regc%p", 1024, 1024);
- regc = pj_pool_calloc(pool, 1, sizeof(struct pjsip_regc));
-
- regc->pool = pool;
- regc->endpt = endpt;
- regc->token = token;
- regc->cb = cb;
- regc->contact_buf = pj_pool_alloc(pool, PJSIP_REGC_CONTACT_BUF_SIZE);
- regc->expires = PJSIP_REGC_EXPIRATION_NOT_SPECIFIED;
-
- pj_list_init(&regc->auth_sess_list);
-
- return regc;
-}
-
-
-PJ_DEF(void) pjsip_regc_destroy(pjsip_regc *regc)
-{
- if (regc->pending_tsx) {
- regc->_delete_flag = 1;
- regc->cb = NULL;
- } else {
- pjsip_endpt_destroy_pool(regc->endpt, regc->pool);
- }
-}
-
-
-PJ_DEF(pj_pool_t*) pjsip_regc_get_pool(pjsip_regc *regc)
-{
- return regc->pool;
-}
-
-static void set_expires( pjsip_regc *regc, pj_uint32_t expires)
-{
- if (expires != regc->expires) {
- regc->expires_hdr = pjsip_expires_hdr_create(regc->pool);
- regc->expires_hdr->ivalue = expires;
- } else {
- regc->expires_hdr = NULL;
- }
-}
-
-
-static pj_status_t set_contact( pjsip_regc *regc,
- int contact_cnt,
- const pj_str_t contact[] )
-{
- int i;
- char *s;
- const pj_str_t contact_STR = { "Contact", 7};
-
- /* Concatenate contacts. */
- for (i=0, s=regc->contact_buf; i<contact_cnt; ++i) {
- if ((s-regc->contact_buf) + contact[i].slen + 2 > PJSIP_REGC_CONTACT_BUF_SIZE) {
- return -1;
- }
- pj_memcpy(s, contact[i].ptr, contact[i].slen);
- s += contact[i].slen;
-
- if (i != contact_cnt - 1) {
- *s++ = ',';
- *s++ = ' ';
- }
- }
-
- /* Set "Contact" header. */
- regc->contact_hdr = pjsip_generic_string_hdr_create( regc->pool, &contact_STR);
- regc->contact_hdr->hvalue.ptr = regc->contact_buf;
- regc->contact_hdr->hvalue.slen = (s - regc->contact_buf);
-
- return 0;
-}
-
-
-PJ_DEF(pj_status_t) pjsip_regc_init( pjsip_regc *regc,
- const pj_str_t *srv_url,
- const pj_str_t *from_url,
- const pj_str_t *to_url,
- int contact_cnt,
- const pj_str_t contact[],
- pj_uint32_t expires)
-{
- pj_str_t tmp;
-
- /* Copy server URL. */
- pj_strdup_with_null(regc->pool, &regc->str_srv_url, srv_url);
-
- /* Set server URL. */
- tmp = regc->str_srv_url;
- regc->srv_url = pjsip_parse_uri( regc->pool, tmp.ptr, tmp.slen, 0);
- if (regc->srv_url == NULL) {
- return -1;
- }
-
- /* Set "From" header. */
- pj_strdup_with_null(regc->pool, &tmp, from_url);
- regc->from_hdr = pjsip_from_hdr_create(regc->pool);
- regc->from_hdr->uri = pjsip_parse_uri(regc->pool, tmp.ptr, tmp.slen,
- PJSIP_PARSE_URI_AS_NAMEADDR);
- if (!regc->from_hdr->uri) {
- PJ_LOG(4,(THIS_FILE, "regc: invalid source URI %.*s", from_url->slen, from_url->ptr));
- return -1;
- }
-
- /* Set "To" header. */
- pj_strdup_with_null(regc->pool, &tmp, to_url);
- regc->to_hdr = pjsip_to_hdr_create(regc->pool);
- regc->to_hdr->uri = pjsip_parse_uri(regc->pool, tmp.ptr, tmp.slen,
- PJSIP_PARSE_URI_AS_NAMEADDR);
- if (!regc->to_hdr->uri) {
- PJ_LOG(4,(THIS_FILE, "regc: invalid target URI %.*s", to_url->slen, to_url->ptr));
- return -1;
- }
-
-
- /* Set "Contact" header. */
- if (set_contact( regc, contact_cnt, contact) != 0)
- return -1;
-
- /* Set "Expires" header, if required. */
- set_expires( regc, expires);
-
- /* Set "Call-ID" header. */
- regc->cid_hdr = pjsip_cid_hdr_create(regc->pool);
- pj_create_unique_string(regc->pool, &regc->cid_hdr->id);
-
- /* Set "CSeq" header. */
- regc->cseq_hdr = pjsip_cseq_hdr_create(regc->pool);
- regc->cseq_hdr->cseq = 0;
- pjsip_method_set( &regc->cseq_hdr->method, PJSIP_REGISTER_METHOD);
-
- /* Create "Contact" header used in unregistration. */
- regc->unreg_contact_hdr = pjsip_contact_hdr_create(regc->pool);
- regc->unreg_contact_hdr->star = 1;
-
- /* Create "Expires" header used in unregistration. */
- regc->unreg_expires_hdr = pjsip_expires_hdr_create( regc->pool);
- regc->unreg_expires_hdr->ivalue = 0;
-
- /* Done. */
- return 0;
-}
-
-PJ_DEF(pj_status_t) pjsip_regc_set_credentials( pjsip_regc *regc,
- int count,
- const pjsip_cred_info cred[] )
-{
- if (count > 0) {
- regc->cred_info = pj_pool_alloc(regc->pool, count * sizeof(pjsip_cred_info));
- pj_memcpy(regc->cred_info, cred, count * sizeof(pjsip_cred_info));
- }
- regc->cred_count = count;
- return 0;
-}
-
-static pjsip_tx_data *create_request(pjsip_regc *regc)
-{
- pjsip_tx_data *tdata;
- pjsip_msg *msg;
-
- /* Create transmit data. */
- tdata = pjsip_endpt_create_tdata(regc->endpt);
- if (!tdata) {
- return NULL;
- }
-
- /* Create request message. */
- msg = pjsip_msg_create(tdata->pool, PJSIP_REQUEST_MSG);
- tdata->msg = msg;
-
- /* Initialize request line. */
- pjsip_method_set(&msg->line.req.method, PJSIP_REGISTER_METHOD);
- msg->line.req.uri = regc->srv_url;
-
- /* Add headers. */
- pjsip_msg_add_hdr(msg, (pjsip_hdr*) regc->from_hdr);
- pjsip_msg_add_hdr(msg, (pjsip_hdr*) regc->to_hdr);
- pjsip_msg_add_hdr(msg, (pjsip_hdr*) regc->cid_hdr);
- pjsip_msg_add_hdr(msg, (pjsip_hdr*) regc->cseq_hdr);
-
- /* Add cached authorization headers. */
- pjsip_auth_init_req( regc->pool, tdata, &regc->auth_sess_list,
- regc->cred_count, regc->cred_info );
-
- /* Add reference counter to transmit data. */
- pjsip_tx_data_add_ref(tdata);
-
- return tdata;
-}
-
-
-PJ_DEF(pjsip_tx_data*) pjsip_regc_register(pjsip_regc *regc, pj_bool_t autoreg)
-{
- pjsip_msg *msg;
- pjsip_tx_data *tdata;
-
- tdata = create_request(regc);
- if (!tdata)
- return NULL;
-
- msg = tdata->msg;
- pjsip_msg_add_hdr(msg, (pjsip_hdr*) regc->contact_hdr);
- if (regc->expires_hdr)
- pjsip_msg_add_hdr(msg, (pjsip_hdr*) regc->expires_hdr);
-
- if (regc->timer.id != 0) {
- pjsip_endpt_cancel_timer(regc->endpt, &regc->timer);
- regc->timer.id = 0;
- }
-
- regc->auto_reg = autoreg;
-
- return tdata;
-}
-
-
-PJ_DEF(pjsip_tx_data*) pjsip_regc_unregister(pjsip_regc *regc)
-{
- pjsip_tx_data *tdata;
- pjsip_msg *msg;
-
- if (regc->timer.id != 0) {
- pjsip_endpt_cancel_timer(regc->endpt, &regc->timer);
- regc->timer.id = 0;
- }
-
- tdata = create_request(regc);
- if (!tdata)
- return NULL;
-
- msg = tdata->msg;
- pjsip_msg_add_hdr( msg, (pjsip_hdr*)regc->unreg_contact_hdr);
- pjsip_msg_add_hdr( msg, (pjsip_hdr*)regc->unreg_expires_hdr);
-
- return tdata;
-}
-
-
-PJ_DEF(pj_status_t) pjsip_regc_update_contact( pjsip_regc *regc,
- int contact_cnt,
- const pj_str_t contact[] )
-{
- return set_contact( regc, contact_cnt, contact );
-}
-
-
-PJ_DEF(pj_status_t) pjsip_regc_update_expires( pjsip_regc *regc,
- pj_uint32_t expires )
-{
- set_expires( regc, expires );
- return 0;
-}
-
-
-static void call_callback(pjsip_regc *regc, int status, const pj_str_t *reason,
- pjsip_rx_data *rdata, pj_int32_t expiration,
- int contact_cnt, pjsip_contact_hdr *contact[])
-{
- struct pjsip_regc_cbparam cbparam;
-
-
- cbparam.regc = regc;
- cbparam.token = regc->token;
- cbparam.code = status;
- cbparam.reason = *reason;
- cbparam.rdata = rdata;
- cbparam.contact_cnt = contact_cnt;
- cbparam.expiration = expiration;
- if (contact_cnt) {
- pj_memcpy( cbparam.contact, contact,
- contact_cnt*sizeof(pjsip_contact_hdr*));
- }
-
- (*regc->cb)(&cbparam);
-}
-
-static void regc_refresh_timer_cb( pj_timer_heap_t *timer_heap,
- struct pj_timer_entry *entry)
-{
- pjsip_regc *regc = entry->user_data;
- pjsip_tx_data *tdata;
-
- PJ_UNUSED_ARG(timer_heap)
-
- entry->id = 0;
- tdata = pjsip_regc_register(regc, 1);
- if (tdata) {
- pjsip_regc_send(regc, tdata);
- } else {
- pj_str_t reason = pj_str("Unable to create txdata");
- call_callback(regc, -1, &reason, NULL, -1, 0, NULL);
- }
-}
-
-static void tsx_callback(void *token, pjsip_event *event)
-{
- pjsip_regc *regc = token;
- pjsip_transaction *tsx = event->obj.tsx;
-
- /* If registration data has been deleted by user then remove registration
- * data from transaction's callback, and don't call callback.
- */
- if (regc->_delete_flag) {
- --regc->pending_tsx;
-
- } else if (tsx->status_code == PJSIP_SC_PROXY_AUTHENTICATION_REQUIRED ||
- tsx->status_code == PJSIP_SC_UNAUTHORIZED)
- {
- pjsip_rx_data *rdata = event->src.rdata;
- pjsip_tx_data *tdata;
-
- tdata = pjsip_auth_reinit_req( regc->endpt,
- regc->pool, &regc->auth_sess_list,
- regc->cred_count, regc->cred_info,
- tsx->last_tx, event->src.rdata );
-
- if (tdata) {
- --regc->pending_tsx;
- pjsip_regc_send(regc, tdata);
- return;
- } else {
- call_callback(regc, tsx->status_code, &rdata->msg->line.status.reason,
- rdata, -1, 0, NULL);
- --regc->pending_tsx;
- }
- } else {
- int contact_cnt = 0;
- pjsip_contact_hdr *contact[PJSIP_REGC_MAX_CONTACT];
- pjsip_rx_data *rdata;
- pj_int32_t expiration = 0xFFFF;
-
- if (tsx->status_code/100 == 2) {
- int i;
- pjsip_contact_hdr *hdr;
- pjsip_msg *msg;
- pjsip_expires_hdr *expires;
-
- rdata = event->src.rdata;
- msg = rdata->msg;
- hdr = pjsip_msg_find_hdr( msg, PJSIP_H_CONTACT, NULL);
- while (hdr) {
- contact[contact_cnt++] = hdr;
- hdr = hdr->next;
- if (hdr == (void*)&msg->hdr)
- break;
- hdr = pjsip_msg_find_hdr(msg, PJSIP_H_CONTACT, hdr);
- }
-
- expires = pjsip_msg_find_hdr(msg, PJSIP_H_EXPIRES, NULL);
-
- if (expires)
- expiration = expires->ivalue;
-
- for (i=0; i<contact_cnt; ++i) {
- hdr = contact[i];
- if (hdr->expires >= 0 && hdr->expires < expiration)
- expiration = contact[i]->expires;
- }
-
- if (regc->auto_reg && expiration != 0 && expiration != 0xFFFF) {
- pj_time_val delay = { 0, 0};
-
- delay.sec = expiration - DELAY_BEFORE_REFRESH;
- if (regc->expires != PJSIP_REGC_EXPIRATION_NOT_SPECIFIED &&
- delay.sec > (pj_int32_t)regc->expires)
- {
- delay.sec = regc->expires;
- }
- if (delay.sec < DELAY_BEFORE_REFRESH)
- delay.sec = DELAY_BEFORE_REFRESH;
- regc->timer.cb = &regc_refresh_timer_cb;
- regc->timer.id = REFRESH_TIMER;
- regc->timer.user_data = regc;
- pjsip_endpt_schedule_timer( regc->endpt, &regc->timer, &delay);
- }
-
- } else {
- rdata = (event->src_type==PJSIP_EVENT_RX_MSG) ? event->src.rdata : NULL;
- }
-
-
- /* Call callback. */
- if (expiration == 0xFFFF) expiration = -1;
- call_callback(regc, tsx->status_code,
- (rdata ? &rdata->msg->line.status.reason
- : pjsip_get_status_text(tsx->status_code)),
- rdata, expiration,
- contact_cnt, contact);
-
- --regc->pending_tsx;
- }
-
- /* Delete the record if user destroy regc during the callback. */
- if (regc->_delete_flag && regc->pending_tsx==0) {
- pjsip_regc_destroy(regc);
- }
-}
-
-PJ_DEF(void) pjsip_regc_send(pjsip_regc *regc, pjsip_tx_data *tdata)
-{
- int status;
-
- /* Make sure we don't have pending transaction. */
- if (regc->pending_tsx) {
- pj_str_t reason = pj_str("Transaction in progress");
- call_callback(regc, -1, &reason, NULL, -1, 0, NULL);
- pjsip_tx_data_dec_ref( tdata );
- return;
- }
-
- /* Invalidate message buffer. */
- pjsip_tx_data_invalidate_msg(tdata);
-
- /* Increment CSeq */
- regc->cseq_hdr->cseq++;
-
- /* Send. */
- status = pjsip_endpt_send_request(regc->endpt, tdata, -1, regc, &tsx_callback);
- if (status==0)
- ++regc->pending_tsx;
- else {
- pj_str_t reason = pj_str("Unable to send request.");
- call_callback(regc, status, &reason, NULL, -1, 0, NULL);
- }
-}
-
-
+/* $Id$ */
+/*
+ * Copyright (C) 2003-2006 Benny Prijono <benny@prijono.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#include <pjsip_mod_ua/sip_reg.h>
+#include <pjsip/sip_endpoint.h>
+#include <pjsip/sip_parser.h>
+#include <pjsip/sip_module.h>
+#include <pjsip/sip_transaction.h>
+#include <pjsip/sip_event.h>
+#include <pjsip/sip_util.h>
+#include <pjsip/sip_auth_msg.h>
+#include <pj/pool.h>
+#include <pj/string.h>
+#include <pj/guid.h>
+#include <pj/log.h>
+
+#define REFRESH_TIMER 1
+#define DELAY_BEFORE_REFRESH 5
+#define THIS_FILE "sip_regc.c"
+
+/**
+ * SIP client registration structure.
+ */
+struct pjsip_regc
+{
+ pj_pool_t *pool;
+ pjsip_endpoint *endpt;
+ pj_bool_t _delete_flag;
+ int pending_tsx;
+
+ void *token;
+ pjsip_regc_cb *cb;
+
+ pj_str_t str_srv_url;
+ pjsip_uri *srv_url;
+ pjsip_cid_hdr *cid_hdr;
+ pjsip_cseq_hdr *cseq_hdr;
+ pjsip_from_hdr *from_hdr;
+ pjsip_to_hdr *to_hdr;
+ char *contact_buf;
+ pjsip_generic_string_hdr *contact_hdr;
+ pjsip_expires_hdr *expires_hdr;
+ pjsip_contact_hdr *unreg_contact_hdr;
+ pjsip_expires_hdr *unreg_expires_hdr;
+ pj_uint32_t expires;
+
+ /* Credentials. */
+ int cred_count;
+ pjsip_cred_info *cred_info;
+
+ /* Authorization sessions. */
+ pjsip_auth_session auth_sess_list;
+
+ /* Auto refresh registration. */
+ pj_bool_t auto_reg;
+ pj_timer_entry timer;
+};
+
+
+
+PJ_DEF(pjsip_regc*) pjsip_regc_create( pjsip_endpoint *endpt, void *token,
+ pjsip_regc_cb *cb)
+{
+ pj_pool_t *pool;
+ pjsip_regc *regc;
+
+ if (cb == NULL)
+ return NULL;
+
+ pool = pjsip_endpt_create_pool(endpt, "regc%p", 1024, 1024);
+ regc = pj_pool_calloc(pool, 1, sizeof(struct pjsip_regc));
+
+ regc->pool = pool;
+ regc->endpt = endpt;
+ regc->token = token;
+ regc->cb = cb;
+ regc->contact_buf = pj_pool_alloc(pool, PJSIP_REGC_CONTACT_BUF_SIZE);
+ regc->expires = PJSIP_REGC_EXPIRATION_NOT_SPECIFIED;
+
+ pj_list_init(&regc->auth_sess_list);
+
+ return regc;
+}
+
+
+PJ_DEF(void) pjsip_regc_destroy(pjsip_regc *regc)
+{
+ if (regc->pending_tsx) {
+ regc->_delete_flag = 1;
+ regc->cb = NULL;
+ } else {
+ pjsip_endpt_destroy_pool(regc->endpt, regc->pool);
+ }
+}
+
+
+PJ_DEF(pj_pool_t*) pjsip_regc_get_pool(pjsip_regc *regc)
+{
+ return regc->pool;
+}
+
+static void set_expires( pjsip_regc *regc, pj_uint32_t expires)
+{
+ if (expires != regc->expires) {
+ regc->expires_hdr = pjsip_expires_hdr_create(regc->pool);
+ regc->expires_hdr->ivalue = expires;
+ } else {
+ regc->expires_hdr = NULL;
+ }
+}
+
+
+static pj_status_t set_contact( pjsip_regc *regc,
+ int contact_cnt,
+ const pj_str_t contact[] )
+{
+ int i;
+ char *s;
+ const pj_str_t contact_STR = { "Contact", 7};
+
+ /* Concatenate contacts. */
+ for (i=0, s=regc->contact_buf; i<contact_cnt; ++i) {
+ if ((s-regc->contact_buf) + contact[i].slen + 2 > PJSIP_REGC_CONTACT_BUF_SIZE) {
+ return -1;
+ }
+ pj_memcpy(s, contact[i].ptr, contact[i].slen);
+ s += contact[i].slen;
+
+ if (i != contact_cnt - 1) {
+ *s++ = ',';
+ *s++ = ' ';
+ }
+ }
+
+ /* Set "Contact" header. */
+ regc->contact_hdr = pjsip_generic_string_hdr_create( regc->pool, &contact_STR);
+ regc->contact_hdr->hvalue.ptr = regc->contact_buf;
+ regc->contact_hdr->hvalue.slen = (s - regc->contact_buf);
+
+ return 0;
+}
+
+
+PJ_DEF(pj_status_t) pjsip_regc_init( pjsip_regc *regc,
+ const pj_str_t *srv_url,
+ const pj_str_t *from_url,
+ const pj_str_t *to_url,
+ int contact_cnt,
+ const pj_str_t contact[],
+ pj_uint32_t expires)
+{
+ pj_str_t tmp;
+
+ /* Copy server URL. */
+ pj_strdup_with_null(regc->pool, &regc->str_srv_url, srv_url);
+
+ /* Set server URL. */
+ tmp = regc->str_srv_url;
+ regc->srv_url = pjsip_parse_uri( regc->pool, tmp.ptr, tmp.slen, 0);
+ if (regc->srv_url == NULL) {
+ return -1;
+ }
+
+ /* Set "From" header. */
+ pj_strdup_with_null(regc->pool, &tmp, from_url);
+ regc->from_hdr = pjsip_from_hdr_create(regc->pool);
+ regc->from_hdr->uri = pjsip_parse_uri(regc->pool, tmp.ptr, tmp.slen,
+ PJSIP_PARSE_URI_AS_NAMEADDR);
+ if (!regc->from_hdr->uri) {
+ PJ_LOG(4,(THIS_FILE, "regc: invalid source URI %.*s", from_url->slen, from_url->ptr));
+ return -1;
+ }
+
+ /* Set "To" header. */
+ pj_strdup_with_null(regc->pool, &tmp, to_url);
+ regc->to_hdr = pjsip_to_hdr_create(regc->pool);
+ regc->to_hdr->uri = pjsip_parse_uri(regc->pool, tmp.ptr, tmp.slen,
+ PJSIP_PARSE_URI_AS_NAMEADDR);
+ if (!regc->to_hdr->uri) {
+ PJ_LOG(4,(THIS_FILE, "regc: invalid target URI %.*s", to_url->slen, to_url->ptr));
+ return -1;
+ }
+
+
+ /* Set "Contact" header. */
+ if (set_contact( regc, contact_cnt, contact) != 0)
+ return -1;
+
+ /* Set "Expires" header, if required. */
+ set_expires( regc, expires);
+
+ /* Set "Call-ID" header. */
+ regc->cid_hdr = pjsip_cid_hdr_create(regc->pool);
+ pj_create_unique_string(regc->pool, &regc->cid_hdr->id);
+
+ /* Set "CSeq" header. */
+ regc->cseq_hdr = pjsip_cseq_hdr_create(regc->pool);
+ regc->cseq_hdr->cseq = 0;
+ pjsip_method_set( &regc->cseq_hdr->method, PJSIP_REGISTER_METHOD);
+
+ /* Create "Contact" header used in unregistration. */
+ regc->unreg_contact_hdr = pjsip_contact_hdr_create(regc->pool);
+ regc->unreg_contact_hdr->star = 1;
+
+ /* Create "Expires" header used in unregistration. */
+ regc->unreg_expires_hdr = pjsip_expires_hdr_create( regc->pool);
+ regc->unreg_expires_hdr->ivalue = 0;
+
+ /* Done. */
+ return 0;
+}
+
+PJ_DEF(pj_status_t) pjsip_regc_set_credentials( pjsip_regc *regc,
+ int count,
+ const pjsip_cred_info cred[] )
+{
+ if (count > 0) {
+ regc->cred_info = pj_pool_alloc(regc->pool, count * sizeof(pjsip_cred_info));
+ pj_memcpy(regc->cred_info, cred, count * sizeof(pjsip_cred_info));
+ }
+ regc->cred_count = count;
+ return 0;
+}
+
+static pjsip_tx_data *create_request(pjsip_regc *regc)
+{
+ pjsip_tx_data *tdata;
+ pjsip_msg *msg;
+
+ /* Create transmit data. */
+ tdata = pjsip_endpt_create_tdata(regc->endpt);
+ if (!tdata) {
+ return NULL;
+ }
+
+ /* Create request message. */
+ msg = pjsip_msg_create(tdata->pool, PJSIP_REQUEST_MSG);
+ tdata->msg = msg;
+
+ /* Initialize request line. */
+ pjsip_method_set(&msg->line.req.method, PJSIP_REGISTER_METHOD);
+ msg->line.req.uri = regc->srv_url;
+
+ /* Add headers. */
+ pjsip_msg_add_hdr(msg, (pjsip_hdr*) regc->from_hdr);
+ pjsip_msg_add_hdr(msg, (pjsip_hdr*) regc->to_hdr);
+ pjsip_msg_add_hdr(msg, (pjsip_hdr*) regc->cid_hdr);
+ pjsip_msg_add_hdr(msg, (pjsip_hdr*) regc->cseq_hdr);
+
+ /* Add cached authorization headers. */
+ pjsip_auth_init_req( regc->pool, tdata, &regc->auth_sess_list,
+ regc->cred_count, regc->cred_info );
+
+ /* Add reference counter to transmit data. */
+ pjsip_tx_data_add_ref(tdata);
+
+ return tdata;
+}
+
+
+PJ_DEF(pjsip_tx_data*) pjsip_regc_register(pjsip_regc *regc, pj_bool_t autoreg)
+{
+ pjsip_msg *msg;
+ pjsip_tx_data *tdata;
+
+ tdata = create_request(regc);
+ if (!tdata)
+ return NULL;
+
+ msg = tdata->msg;
+ pjsip_msg_add_hdr(msg, (pjsip_hdr*) regc->contact_hdr);
+ if (regc->expires_hdr)
+ pjsip_msg_add_hdr(msg, (pjsip_hdr*) regc->expires_hdr);
+
+ if (regc->timer.id != 0) {
+ pjsip_endpt_cancel_timer(regc->endpt, &regc->timer);
+ regc->timer.id = 0;
+ }
+
+ regc->auto_reg = autoreg;
+
+ return tdata;
+}
+
+
+PJ_DEF(pjsip_tx_data*) pjsip_regc_unregister(pjsip_regc *regc)
+{
+ pjsip_tx_data *tdata;
+ pjsip_msg *msg;
+
+ if (regc->timer.id != 0) {
+ pjsip_endpt_cancel_timer(regc->endpt, &regc->timer);
+ regc->timer.id = 0;
+ }
+
+ tdata = create_request(regc);
+ if (!tdata)
+ return NULL;
+
+ msg = tdata->msg;
+ pjsip_msg_add_hdr( msg, (pjsip_hdr*)regc->unreg_contact_hdr);
+ pjsip_msg_add_hdr( msg, (pjsip_hdr*)regc->unreg_expires_hdr);
+
+ return tdata;
+}
+
+
+PJ_DEF(pj_status_t) pjsip_regc_update_contact( pjsip_regc *regc,
+ int contact_cnt,
+ const pj_str_t contact[] )
+{
+ return set_contact( regc, contact_cnt, contact );
+}
+
+
+PJ_DEF(pj_status_t) pjsip_regc_update_expires( pjsip_regc *regc,
+ pj_uint32_t expires )
+{
+ set_expires( regc, expires );
+ return 0;
+}
+
+
+static void call_callback(pjsip_regc *regc, int status, const pj_str_t *reason,
+ pjsip_rx_data *rdata, pj_int32_t expiration,
+ int contact_cnt, pjsip_contact_hdr *contact[])
+{
+ struct pjsip_regc_cbparam cbparam;
+
+
+ cbparam.regc = regc;
+ cbparam.token = regc->token;
+ cbparam.code = status;
+ cbparam.reason = *reason;
+ cbparam.rdata = rdata;
+ cbparam.contact_cnt = contact_cnt;
+ cbparam.expiration = expiration;
+ if (contact_cnt) {
+ pj_memcpy( cbparam.contact, contact,
+ contact_cnt*sizeof(pjsip_contact_hdr*));
+ }
+
+ (*regc->cb)(&cbparam);
+}
+
+static void regc_refresh_timer_cb( pj_timer_heap_t *timer_heap,
+ struct pj_timer_entry *entry)
+{
+ pjsip_regc *regc = entry->user_data;
+ pjsip_tx_data *tdata;
+
+ PJ_UNUSED_ARG(timer_heap)
+
+ entry->id = 0;
+ tdata = pjsip_regc_register(regc, 1);
+ if (tdata) {
+ pjsip_regc_send(regc, tdata);
+ } else {
+ pj_str_t reason = pj_str("Unable to create txdata");
+ call_callback(regc, -1, &reason, NULL, -1, 0, NULL);
+ }
+}
+
+static void tsx_callback(void *token, pjsip_event *event)
+{
+ pjsip_regc *regc = token;
+ pjsip_transaction *tsx = event->obj.tsx;
+
+ /* If registration data has been deleted by user then remove registration
+ * data from transaction's callback, and don't call callback.
+ */
+ if (regc->_delete_flag) {
+ --regc->pending_tsx;
+
+ } else if (tsx->status_code == PJSIP_SC_PROXY_AUTHENTICATION_REQUIRED ||
+ tsx->status_code == PJSIP_SC_UNAUTHORIZED)
+ {
+ pjsip_rx_data *rdata = event->src.rdata;
+ pjsip_tx_data *tdata;
+
+ tdata = pjsip_auth_reinit_req( regc->endpt,
+ regc->pool, &regc->auth_sess_list,
+ regc->cred_count, regc->cred_info,
+ tsx->last_tx, event->src.rdata );
+
+ if (tdata) {
+ --regc->pending_tsx;
+ pjsip_regc_send(regc, tdata);
+ return;
+ } else {
+ call_callback(regc, tsx->status_code, &rdata->msg->line.status.reason,
+ rdata, -1, 0, NULL);
+ --regc->pending_tsx;
+ }
+ } else {
+ int contact_cnt = 0;
+ pjsip_contact_hdr *contact[PJSIP_REGC_MAX_CONTACT];
+ pjsip_rx_data *rdata;
+ pj_int32_t expiration = 0xFFFF;
+
+ if (tsx->status_code/100 == 2) {
+ int i;
+ pjsip_contact_hdr *hdr;
+ pjsip_msg *msg;
+ pjsip_expires_hdr *expires;
+
+ rdata = event->src.rdata;
+ msg = rdata->msg;
+ hdr = pjsip_msg_find_hdr( msg, PJSIP_H_CONTACT, NULL);
+ while (hdr) {
+ contact[contact_cnt++] = hdr;
+ hdr = hdr->next;
+ if (hdr == (void*)&msg->hdr)
+ break;
+ hdr = pjsip_msg_find_hdr(msg, PJSIP_H_CONTACT, hdr);
+ }
+
+ expires = pjsip_msg_find_hdr(msg, PJSIP_H_EXPIRES, NULL);
+
+ if (expires)
+ expiration = expires->ivalue;
+
+ for (i=0; i<contact_cnt; ++i) {
+ hdr = contact[i];
+ if (hdr->expires >= 0 && hdr->expires < expiration)
+ expiration = contact[i]->expires;
+ }
+
+ if (regc->auto_reg && expiration != 0 && expiration != 0xFFFF) {
+ pj_time_val delay = { 0, 0};
+
+ delay.sec = expiration - DELAY_BEFORE_REFRESH;
+ if (regc->expires != PJSIP_REGC_EXPIRATION_NOT_SPECIFIED &&
+ delay.sec > (pj_int32_t)regc->expires)
+ {
+ delay.sec = regc->expires;
+ }
+ if (delay.sec < DELAY_BEFORE_REFRESH)
+ delay.sec = DELAY_BEFORE_REFRESH;
+ regc->timer.cb = &regc_refresh_timer_cb;
+ regc->timer.id = REFRESH_TIMER;
+ regc->timer.user_data = regc;
+ pjsip_endpt_schedule_timer( regc->endpt, &regc->timer, &delay);
+ }
+
+ } else {
+ rdata = (event->src_type==PJSIP_EVENT_RX_MSG) ? event->src.rdata : NULL;
+ }
+
+
+ /* Call callback. */
+ if (expiration == 0xFFFF) expiration = -1;
+ call_callback(regc, tsx->status_code,
+ (rdata ? &rdata->msg->line.status.reason
+ : pjsip_get_status_text(tsx->status_code)),
+ rdata, expiration,
+ contact_cnt, contact);
+
+ --regc->pending_tsx;
+ }
+
+ /* Delete the record if user destroy regc during the callback. */
+ if (regc->_delete_flag && regc->pending_tsx==0) {
+ pjsip_regc_destroy(regc);
+ }
+}
+
+PJ_DEF(void) pjsip_regc_send(pjsip_regc *regc, pjsip_tx_data *tdata)
+{
+ int status;
+
+ /* Make sure we don't have pending transaction. */
+ if (regc->pending_tsx) {
+ pj_str_t reason = pj_str("Transaction in progress");
+ call_callback(regc, -1, &reason, NULL, -1, 0, NULL);
+ pjsip_tx_data_dec_ref( tdata );
+ return;
+ }
+
+ /* Invalidate message buffer. */
+ pjsip_tx_data_invalidate_msg(tdata);
+
+ /* Increment CSeq */
+ regc->cseq_hdr->cseq++;
+
+ /* Send. */
+ status = pjsip_endpt_send_request(regc->endpt, tdata, -1, regc, &tsx_callback);
+ if (status==0)
+ ++regc->pending_tsx;
+ else {
+ pj_str_t reason = pj_str("Unable to send request.");
+ call_callback(regc, status, &reason, NULL, -1, 0, NULL);
+ }
+}
+
+
diff --git a/pjsip/src/pjsip-ua/sip_ua.c b/pjsip/src/pjsip-ua/sip_ua.c
index 11f7670c..094be07f 100644
--- a/pjsip/src/pjsip-ua/sip_ua.c
+++ b/pjsip/src/pjsip-ua/sip_ua.c
@@ -1,506 +1,506 @@
-/* $Id$ */
-/*
- * Copyright (C) 2003-2006 Benny Prijono <benny@prijono.org>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
-#include <pjsip_mod_ua/sip_ua.h>
-#include <pjsip_mod_ua/sip_dialog.h>
-#include <pjsip_mod_ua/sip_ua_private.h>
-#include <pjsip/sip_module.h>
-#include <pjsip/sip_event.h>
-#include <pjsip/sip_util.h>
-#include <pjsip/sip_endpoint.h>
-#include <pjsip/sip_transaction.h>
-#include <pj/list.h>
-#include <pj/log.h>
-#include <pj/string.h>
-#include <pj/guid.h>
-#include <pj/os.h>
-#include <pj/hash.h>
-#include <pj/pool.h>
-
-#define PJSIP_POOL_LEN_USER_AGENT 1024
-#define PJSIP_POOL_INC_USER_AGENT 0
-
-
-#define LOG_THIS "useragent.."
-
-/*
- * Static prototypes.
- */
-static pj_status_t ua_init( pjsip_endpoint *endpt,
- struct pjsip_module *mod, pj_uint32_t id );
-static pj_status_t ua_start( struct pjsip_module *mod );
-static pj_status_t ua_deinit( struct pjsip_module *mod );
-static void ua_tsx_handler( struct pjsip_module *mod, pjsip_event *evt );
-static pjsip_dlg *find_dialog( pjsip_user_agent *ua,
- pjsip_rx_data *rdata );
-static pj_status_t ua_register_dialog( pjsip_user_agent *ua, pjsip_dlg *dlg,
- pj_str_t *key );
-PJ_DECL(void) pjsip_on_dialog_destroyed( pjsip_dlg *dlg );
-
-/*
- * Default UA instance.
- */
-static pjsip_user_agent ua_instance;
-
-/*
- * Module interface.
- */
-static struct pjsip_module mod_ua =
-{
- { "User-Agent", 10 }, /* Name. */
- 0, /* Flag */
- 128, /* Priority */
- NULL, /* User agent instance, initialized by APP. */
- 0, /* Number of methods supported (will be initialized later). */
- { 0 }, /* Array of methods (will be initialized later) */
- &ua_init, /* init_module() */
- &ua_start, /* start_module() */
- &ua_deinit, /* deinit_module() */
- &ua_tsx_handler, /* tsx_handler() */
-};
-
-/*
- * Initialize user agent instance.
- */
-static pj_status_t ua_init( pjsip_endpoint *endpt,
- struct pjsip_module *mod, pj_uint32_t id )
-{
- static pjsip_method m_invite, m_ack, m_cancel, m_bye;
- pjsip_user_agent *ua = mod->mod_data;
- extern int pjsip_dlg_lock_tls_id; /* defined in sip_dialog.c */
-
- pjsip_method_set( &m_invite, PJSIP_INVITE_METHOD );
- pjsip_method_set( &m_ack, PJSIP_ACK_METHOD );
- pjsip_method_set( &m_cancel, PJSIP_CANCEL_METHOD );
- pjsip_method_set( &m_bye, PJSIP_BYE_METHOD );
-
- mod->method_cnt = 4;
- mod->methods[0] = &m_invite;
- mod->methods[1] = &m_ack;
- mod->methods[2] = &m_cancel;
- mod->methods[3] = &m_bye;
-
- /* Initialize the user agent. */
- ua->endpt = endpt;
- ua->pool = pjsip_endpt_create_pool(endpt, "pua%p", PJSIP_POOL_LEN_UA,
- PJSIP_POOL_INC_UA);
- if (!ua->pool) {
- return -1;
- }
- ua->mod_id = id;
- ua->mutex = pj_mutex_create(ua->pool, " ua%p", 0);
- if (!ua->mutex) {
- return -1;
- }
- ua->dlg_table = pj_hash_create(ua->pool, PJSIP_MAX_DIALOG_COUNT);
- if (ua->dlg_table == NULL) {
- return -1;
- }
- pj_list_init(&ua->dlg_list);
-
- /* Initialize dialog lock. */
- pjsip_dlg_lock_tls_id = pj_thread_local_alloc();
- if (pjsip_dlg_lock_tls_id == -1) {
- return -1;
- }
- pj_thread_local_set(pjsip_dlg_lock_tls_id, NULL);
-
- return 0;
-}
-
-/*
- * Start user agent instance.
- */
-static pj_status_t ua_start( struct pjsip_module *mod )
-{
- PJ_UNUSED_ARG(mod)
- return 0;
-}
-
-/*
- * Destroy user agent.
- */
-static pj_status_t ua_deinit( struct pjsip_module *mod )
-{
- pjsip_user_agent *ua = mod->mod_data;
-
- pj_mutex_unlock(ua->mutex);
-
- /* Release pool */
- if (ua->pool) {
- pjsip_endpt_destroy_pool( ua->endpt, ua->pool );
- }
- return 0;
-}
-
-/*
- * Get the module interface for the UA module.
- */
-PJ_DEF(pjsip_module*) pjsip_ua_get_module(void)
-{
- mod_ua.mod_data = &ua_instance;
- return &mod_ua;
-}
-
-/*
- * Register callback to receive dialog notifications.
- */
-PJ_DEF(void) pjsip_ua_set_dialog_callback( pjsip_user_agent *ua,
- pjsip_dlg_callback *cb )
-{
- ua->dlg_cb = cb;
-}
-
-/*
- * Find dialog.
- * This function is called for a new transactions, which a dialog hasn't been
- * 'attached' to the transaction.
- */
-static pjsip_dlg *find_dialog( pjsip_user_agent *ua, pjsip_rx_data *rdata )
-{
- pjsip_dlg *dlg;
- pj_str_t *tag;
-
- /* Non-CANCEL requests/response can be found by looking at the tag in the
- * hash table. CANCEL requests don't have tags, so instead we'll try to
- * find the UAS INVITE transaction in endpoint's hash table
- */
- if (rdata->cseq->method.id == PJSIP_CANCEL_METHOD) {
-
- /* Create key for the rdata, but this time, use INVITE as the
- * method.
- */
- pj_str_t key;
- pjsip_role_e role;
- pjsip_method invite_method;
- pjsip_transaction *invite_tsx;
-
- if (rdata->msg->type == PJSIP_REQUEST_MSG) {
- role = PJSIP_ROLE_UAS;
- } else {
- role = PJSIP_ROLE_UAC;
- }
- pjsip_method_set(&invite_method, PJSIP_INVITE_METHOD);
- pjsip_tsx_create_key(rdata->pool, &key, role, &invite_method, rdata);
-
- /* Lookup the INVITE transaction */
- invite_tsx = pjsip_endpt_find_tsx(ua->endpt, &key);
-
- /* We should find the dialog attached to the INVITE transaction */
- return invite_tsx ?
- (pjsip_dlg*) invite_tsx->module_data[ua->mod_id] : NULL;
-
- } else {
- if (rdata->msg->type == PJSIP_REQUEST_MSG) {
- tag = &rdata->to_tag;
- } else {
- tag = &rdata->from_tag;
- }
- /* Find the dialog in UA hash table */
- pj_mutex_lock(ua->mutex);
- dlg = pj_hash_get( ua->dlg_table, tag->ptr, tag->slen );
- pj_mutex_unlock(ua->mutex);
- }
-
- return dlg;
-}
-
-/*
- * This function receives event notification from transactions. It is called by
- * endpoint.
- */
-static void ua_tsx_handler( struct pjsip_module *mod, pjsip_event *event )
-{
- pjsip_user_agent *ua = mod->mod_data;
- pjsip_dlg *dlg = NULL;
- pjsip_transaction *tsx = event->obj.tsx;
-
- PJ_LOG(5, (LOG_THIS, "ua_tsx_handler(tsx=%s, evt=%s, src=%s, data=%p)",
- (tsx ? tsx->obj_name : "NULL"), pjsip_event_str(event->type),
- pjsip_event_str(event->src_type), event->src.data));
-
- /* Special case to handle ACK which doesn't match any INVITE transactions. */
- if (event->type == PJSIP_EVENT_RX_ACK_MSG) {
- /* Find the dialog based on the "tag". */
- dlg = find_dialog( ua, event->src.rdata );
-
- /* We should be able to find it. */
- if (!dlg) {
- PJ_LOG(4,(LOG_THIS, "Unable to find dialog for incoming ACK"));
- return;
- }
-
- /* Match CSeq with pending INVITE in dialog. */
- if (dlg->invite_tsx && dlg->invite_tsx->cseq==event->src.rdata->cseq->cseq) {
- /* A match found. */
- tsx = dlg->invite_tsx;
-
- /* Pass the event to transaction if transaction handles ACK. */
- if (tsx->handle_ack) {
- PJ_LOG(4,(LOG_THIS, "Re-routing strandled ACK to transaction"));
- pjsip_tsx_on_rx_msg(tsx, event->src.rdata);
- return;
- }
- } else {
- tsx = NULL;
- PJ_LOG(4,(LOG_THIS, "Unable to find INVITE tsx for incoming ACK"));
- return;
- }
- }
-
- /* For discard event, transaction is NULL. */
- if (tsx == NULL) {
- return;
- }
-
- /* Try to pickup the dlg from the transaction. */
- dlg = (pjsip_dlg*) tsx->module_data[ua->mod_id];
-
- if (dlg != NULL) {
-
- /* Nothing to do now. */
-
- } else if (event->src_type == PJSIP_EVENT_RX_MSG) {
-
- /* This must be a new UAS transaction. */
-
- /* Finds dlg that can handle this transaction. */
- dlg = find_dialog( ua, event->src.rdata);
-
- /* Create a new dlg if there's no existing dlg that can handle
- the request, ONLY if the incoming message is an INVITE request.
- */
- if (dlg==NULL && event->src.rdata->msg->type == PJSIP_REQUEST_MSG) {
-
- if (event->src.rdata->msg->line.req.method.id == PJSIP_INVITE_METHOD) {
- /* Create new dialog. */
- dlg = pjsip_ua_create_dialog( ua, PJSIP_ROLE_UAS );
-
- if (dlg == NULL ||
- pjsip_dlg_init_from_rdata( dlg, event->src.rdata) != 0)
- {
- pjsip_tx_data *tdata;
-
- /* Dialog initialization has failed. Respond request with 500 */
- if (dlg) {
- pjsip_ua_destroy_dialog(dlg);
- }
- tdata = pjsip_endpt_create_response(ua->endpt, event->src.rdata,
- PJSIP_SC_INTERNAL_SERVER_ERROR);
- if (tdata) {
- pjsip_tsx_on_tx_msg( event->obj.tsx, tdata );
- }
- return;
- }
-
- } else {
- pjsip_tx_data *tdata;
-
- /* Check the method */
- switch (tsx->method.id) {
- case PJSIP_INVITE_METHOD:
- case PJSIP_ACK_METHOD:
- case PJSIP_BYE_METHOD:
- case PJSIP_CANCEL_METHOD:
- /* Stale non-INVITE request.
- * For now, respond all stale requests with 481 (?).
- */
- tdata = pjsip_endpt_create_response(ua->endpt, event->src.rdata,
- PJSIP_SC_CALL_TSX_DOES_NOT_EXIST);
- if (tdata) {
- pjsip_tsx_on_tx_msg( event->obj.tsx, tdata );
- }
- break;
- }
-
- return;
- }
- } else {
- /* Check the method */
- switch (tsx->method.id) {
- case PJSIP_INVITE_METHOD:
- case PJSIP_ACK_METHOD:
- case PJSIP_BYE_METHOD:
- case PJSIP_CANCEL_METHOD:
- /* These methods belongs to dialog.
- * If we receive these methods while no dialog is found,
- * then it must be a stale responses.
- */
- break;
- default:
- return;
- }
-
- }
-
- if (dlg == NULL) {
- PJ_LOG(3, (LOG_THIS, "Receives spurious rdata %p from %s:%d",
- event->src.rdata,
- pj_sockaddr_get_str_addr(&event->src.rdata->addr),
- pj_sockaddr_get_port(&event->src.rdata->addr)));
- }
-
- /* Set the dlg in the transaction (dlg can be NULL). */
- tsx->module_data[ua->mod_id] = dlg;
-
- } else {
- /* This CAN happen with event->src_type == PJSIP_EVENT_TX_MSG
- * if UAS is responding to a transaction which does not exist.
- * Just ignore.
- */
- return;
- }
-
- /* Pass the event to the dlg. */
- if (dlg) {
- pjsip_dlg_on_tsx_event(dlg, tsx, event);
- }
-}
-
-/*
- * Register dialog to UA.
- */
-static pj_status_t ua_register_dialog( pjsip_user_agent *ua, pjsip_dlg *dlg,
- pj_str_t *key )
-{
- /* Assure that no entry with similar key exists in the hash table. */
- pj_assert( pj_hash_get( ua->dlg_table, key->ptr, key->slen) == 0);
-
- /* Insert entry to hash table. */
- pj_hash_set( dlg->pool, ua->dlg_table,
- key->ptr, key->slen, dlg);
-
- /* Insert to the list. */
- pj_list_insert_before(&ua->dlg_list, dlg);
- return PJ_SUCCESS;
-}
-
-/*
- * Create a new dialog.
- */
-PJ_DEF(pjsip_dlg*) pjsip_ua_create_dialog( pjsip_user_agent *ua,
- pjsip_role_e role )
-{
- pj_pool_t *pool;
- pjsip_dlg *dlg;
-
- PJ_UNUSED_ARG(ua)
-
- /* Create pool for the dialog. */
- pool = pjsip_endpt_create_pool( ua->endpt, "pdlg%p",
- PJSIP_POOL_LEN_DIALOG,
- PJSIP_POOL_INC_DIALOG);
-
- /* Create the dialog. */
- dlg = pj_pool_calloc(pool, 1, sizeof(pjsip_dlg));
- dlg->pool = pool;
- dlg->ua = ua;
- dlg->role = role;
- sprintf(dlg->obj_name, "dlg%p", dlg);
-
- /* Create mutex for the dialog. */
- dlg->mutex = pj_mutex_create(dlg->pool, "mdlg%p", 0);
- if (!dlg->mutex) {
- pjsip_endpt_destroy_pool(ua->endpt, pool);
- return NULL;
- }
-
- /* Create unique tag for the dialog. */
- pj_create_unique_string( pool, &dlg->local.tag );
-
- /* Register dialog. */
- pj_mutex_lock(ua->mutex);
- if (ua_register_dialog(ua, dlg, &dlg->local.tag) != PJ_SUCCESS) {
- pj_mutex_unlock(ua->mutex);
- pj_mutex_destroy(dlg->mutex);
- pjsip_endpt_destroy_pool( ua->endpt, pool );
- return NULL;
- }
- pj_mutex_unlock(ua->mutex);
-
- PJ_LOG(4, (dlg->obj_name, "new %s dialog created", pjsip_role_name(role)));
- return dlg;
-}
-
-/*
- * Destroy dialog.
- */
-PJ_DEF(void) pjsip_ua_destroy_dialog( pjsip_dlg *dlg )
-{
- PJ_LOG(5, (dlg->obj_name, "destroying.."));
-
- /* Lock dialog's mutex.
- * Check the mutex validity first since this function can be called
- * on dialog initialization failure (which might be because mutex could not
- * be allocated in the first place).
- */
- if (dlg->mutex) {
- pj_mutex_lock(dlg->mutex);
- }
-
- /* This must be called while holding dialog's mutex, if any. */
- pjsip_on_dialog_destroyed(dlg);
-
- /* Lock UA. */
- pj_mutex_lock(dlg->ua->mutex);
-
- /* Erase from hash table. */
- pj_hash_set( dlg->pool, dlg->ua->dlg_table,
- dlg->local.tag.ptr, dlg->local.tag.slen, NULL);
-
- /* Erase from the list. */
- pj_list_erase(dlg);
-
- /* Unlock UA. */
- pj_mutex_unlock(dlg->ua->mutex);
-
- /* Unlock mutex. */
- if (dlg->mutex) {
- pj_mutex_unlock(dlg->mutex);
- }
-
- /* Destroy the pool. */
- pjsip_endpt_destroy_pool( dlg->ua->endpt, dlg->pool);
-}
-
-/*
- * Dump user agent state to log file.
- */
-PJ_DEF(void) pjsip_ua_dump(pjsip_user_agent *ua)
-{
-#if PJ_LOG_MAX_LEVEL >= 3
- PJ_LOG(3,(LOG_THIS, "Dumping user agent"));
- PJ_LOG(3,(LOG_THIS, " Pool capacity=%u, used=%u",
- pj_pool_get_capacity(ua->pool),
- pj_pool_get_used_size(ua->pool)));
- PJ_LOG(3,(LOG_THIS, " Number of dialogs=%u", pj_hash_count(ua->dlg_table)));
-
- if (pj_hash_count(ua->dlg_table)) {
- pjsip_dlg *dlg;
-
- PJ_LOG(3,(LOG_THIS, " Dumping dialog list:"));
- dlg = ua->dlg_list.next;
- while (dlg != (pjsip_dlg*) &ua->dlg_list) {
- PJ_LOG(3, (LOG_THIS, " %s %s", dlg->obj_name,
- pjsip_dlg_state_str(dlg->state)));
- dlg = dlg->next;
- }
- }
-#endif
-}
-
+/* $Id$ */
+/*
+ * Copyright (C) 2003-2006 Benny Prijono <benny@prijono.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#include <pjsip_mod_ua/sip_ua.h>
+#include <pjsip_mod_ua/sip_dialog.h>
+#include <pjsip_mod_ua/sip_ua_private.h>
+#include <pjsip/sip_module.h>
+#include <pjsip/sip_event.h>
+#include <pjsip/sip_util.h>
+#include <pjsip/sip_endpoint.h>
+#include <pjsip/sip_transaction.h>
+#include <pj/list.h>
+#include <pj/log.h>
+#include <pj/string.h>
+#include <pj/guid.h>
+#include <pj/os.h>
+#include <pj/hash.h>
+#include <pj/pool.h>
+
+#define PJSIP_POOL_LEN_USER_AGENT 1024
+#define PJSIP_POOL_INC_USER_AGENT 0
+
+
+#define LOG_THIS "useragent.."
+
+/*
+ * Static prototypes.
+ */
+static pj_status_t ua_init( pjsip_endpoint *endpt,
+ struct pjsip_module *mod, pj_uint32_t id );
+static pj_status_t ua_start( struct pjsip_module *mod );
+static pj_status_t ua_deinit( struct pjsip_module *mod );
+static void ua_tsx_handler( struct pjsip_module *mod, pjsip_event *evt );
+static pjsip_dlg *find_dialog( pjsip_user_agent *ua,
+ pjsip_rx_data *rdata );
+static pj_status_t ua_register_dialog( pjsip_user_agent *ua, pjsip_dlg *dlg,
+ pj_str_t *key );
+PJ_DECL(void) pjsip_on_dialog_destroyed( pjsip_dlg *dlg );
+
+/*
+ * Default UA instance.
+ */
+static pjsip_user_agent ua_instance;
+
+/*
+ * Module interface.
+ */
+static struct pjsip_module mod_ua =
+{
+ { "User-Agent", 10 }, /* Name. */
+ 0, /* Flag */
+ 128, /* Priority */
+ NULL, /* User agent instance, initialized by APP. */
+ 0, /* Number of methods supported (will be initialized later). */
+ { 0 }, /* Array of methods (will be initialized later) */
+ &ua_init, /* init_module() */
+ &ua_start, /* start_module() */
+ &ua_deinit, /* deinit_module() */
+ &ua_tsx_handler, /* tsx_handler() */
+};
+
+/*
+ * Initialize user agent instance.
+ */
+static pj_status_t ua_init( pjsip_endpoint *endpt,
+ struct pjsip_module *mod, pj_uint32_t id )
+{
+ static pjsip_method m_invite, m_ack, m_cancel, m_bye;
+ pjsip_user_agent *ua = mod->mod_data;
+ extern int pjsip_dlg_lock_tls_id; /* defined in sip_dialog.c */
+
+ pjsip_method_set( &m_invite, PJSIP_INVITE_METHOD );
+ pjsip_method_set( &m_ack, PJSIP_ACK_METHOD );
+ pjsip_method_set( &m_cancel, PJSIP_CANCEL_METHOD );
+ pjsip_method_set( &m_bye, PJSIP_BYE_METHOD );
+
+ mod->method_cnt = 4;
+ mod->methods[0] = &m_invite;
+ mod->methods[1] = &m_ack;
+ mod->methods[2] = &m_cancel;
+ mod->methods[3] = &m_bye;
+
+ /* Initialize the user agent. */
+ ua->endpt = endpt;
+ ua->pool = pjsip_endpt_create_pool(endpt, "pua%p", PJSIP_POOL_LEN_UA,
+ PJSIP_POOL_INC_UA);
+ if (!ua->pool) {
+ return -1;
+ }
+ ua->mod_id = id;
+ ua->mutex = pj_mutex_create(ua->pool, " ua%p", 0);
+ if (!ua->mutex) {
+ return -1;
+ }
+ ua->dlg_table = pj_hash_create(ua->pool, PJSIP_MAX_DIALOG_COUNT);
+ if (ua->dlg_table == NULL) {
+ return -1;
+ }
+ pj_list_init(&ua->dlg_list);
+
+ /* Initialize dialog lock. */
+ pjsip_dlg_lock_tls_id = pj_thread_local_alloc();
+ if (pjsip_dlg_lock_tls_id == -1) {
+ return -1;
+ }
+ pj_thread_local_set(pjsip_dlg_lock_tls_id, NULL);
+
+ return 0;
+}
+
+/*
+ * Start user agent instance.
+ */
+static pj_status_t ua_start( struct pjsip_module *mod )
+{
+ PJ_UNUSED_ARG(mod)
+ return 0;
+}
+
+/*
+ * Destroy user agent.
+ */
+static pj_status_t ua_deinit( struct pjsip_module *mod )
+{
+ pjsip_user_agent *ua = mod->mod_data;
+
+ pj_mutex_unlock(ua->mutex);
+
+ /* Release pool */
+ if (ua->pool) {
+ pjsip_endpt_destroy_pool( ua->endpt, ua->pool );
+ }
+ return 0;
+}
+
+/*
+ * Get the module interface for the UA module.
+ */
+PJ_DEF(pjsip_module*) pjsip_ua_get_module(void)
+{
+ mod_ua.mod_data = &ua_instance;
+ return &mod_ua;
+}
+
+/*
+ * Register callback to receive dialog notifications.
+ */
+PJ_DEF(void) pjsip_ua_set_dialog_callback( pjsip_user_agent *ua,
+ pjsip_dlg_callback *cb )
+{
+ ua->dlg_cb = cb;
+}
+
+/*
+ * Find dialog.
+ * This function is called for a new transactions, which a dialog hasn't been
+ * 'attached' to the transaction.
+ */
+static pjsip_dlg *find_dialog( pjsip_user_agent *ua, pjsip_rx_data *rdata )
+{
+ pjsip_dlg *dlg;
+ pj_str_t *tag;
+
+ /* Non-CANCEL requests/response can be found by looking at the tag in the
+ * hash table. CANCEL requests don't have tags, so instead we'll try to
+ * find the UAS INVITE transaction in endpoint's hash table
+ */
+ if (rdata->cseq->method.id == PJSIP_CANCEL_METHOD) {
+
+ /* Create key for the rdata, but this time, use INVITE as the
+ * method.
+ */
+ pj_str_t key;
+ pjsip_role_e role;
+ pjsip_method invite_method;
+ pjsip_transaction *invite_tsx;
+
+ if (rdata->msg->type == PJSIP_REQUEST_MSG) {
+ role = PJSIP_ROLE_UAS;
+ } else {
+ role = PJSIP_ROLE_UAC;
+ }
+ pjsip_method_set(&invite_method, PJSIP_INVITE_METHOD);
+ pjsip_tsx_create_key(rdata->pool, &key, role, &invite_method, rdata);
+
+ /* Lookup the INVITE transaction */
+ invite_tsx = pjsip_endpt_find_tsx(ua->endpt, &key);
+
+ /* We should find the dialog attached to the INVITE transaction */
+ return invite_tsx ?
+ (pjsip_dlg*) invite_tsx->module_data[ua->mod_id] : NULL;
+
+ } else {
+ if (rdata->msg->type == PJSIP_REQUEST_MSG) {
+ tag = &rdata->to_tag;
+ } else {
+ tag = &rdata->from_tag;
+ }
+ /* Find the dialog in UA hash table */
+ pj_mutex_lock(ua->mutex);
+ dlg = pj_hash_get( ua->dlg_table, tag->ptr, tag->slen );
+ pj_mutex_unlock(ua->mutex);
+ }
+
+ return dlg;
+}
+
+/*
+ * This function receives event notification from transactions. It is called by
+ * endpoint.
+ */
+static void ua_tsx_handler( struct pjsip_module *mod, pjsip_event *event )
+{
+ pjsip_user_agent *ua = mod->mod_data;
+ pjsip_dlg *dlg = NULL;
+ pjsip_transaction *tsx = event->obj.tsx;
+
+ PJ_LOG(5, (LOG_THIS, "ua_tsx_handler(tsx=%s, evt=%s, src=%s, data=%p)",
+ (tsx ? tsx->obj_name : "NULL"), pjsip_event_str(event->type),
+ pjsip_event_str(event->src_type), event->src.data));
+
+ /* Special case to handle ACK which doesn't match any INVITE transactions. */
+ if (event->type == PJSIP_EVENT_RX_ACK_MSG) {
+ /* Find the dialog based on the "tag". */
+ dlg = find_dialog( ua, event->src.rdata );
+
+ /* We should be able to find it. */
+ if (!dlg) {
+ PJ_LOG(4,(LOG_THIS, "Unable to find dialog for incoming ACK"));
+ return;
+ }
+
+ /* Match CSeq with pending INVITE in dialog. */
+ if (dlg->invite_tsx && dlg->invite_tsx->cseq==event->src.rdata->cseq->cseq) {
+ /* A match found. */
+ tsx = dlg->invite_tsx;
+
+ /* Pass the event to transaction if transaction handles ACK. */
+ if (tsx->handle_ack) {
+ PJ_LOG(4,(LOG_THIS, "Re-routing strandled ACK to transaction"));
+ pjsip_tsx_on_rx_msg(tsx, event->src.rdata);
+ return;
+ }
+ } else {
+ tsx = NULL;
+ PJ_LOG(4,(LOG_THIS, "Unable to find INVITE tsx for incoming ACK"));
+ return;
+ }
+ }
+
+ /* For discard event, transaction is NULL. */
+ if (tsx == NULL) {
+ return;
+ }
+
+ /* Try to pickup the dlg from the transaction. */
+ dlg = (pjsip_dlg*) tsx->module_data[ua->mod_id];
+
+ if (dlg != NULL) {
+
+ /* Nothing to do now. */
+
+ } else if (event->src_type == PJSIP_EVENT_RX_MSG) {
+
+ /* This must be a new UAS transaction. */
+
+ /* Finds dlg that can handle this transaction. */
+ dlg = find_dialog( ua, event->src.rdata);
+
+ /* Create a new dlg if there's no existing dlg that can handle
+ the request, ONLY if the incoming message is an INVITE request.
+ */
+ if (dlg==NULL && event->src.rdata->msg->type == PJSIP_REQUEST_MSG) {
+
+ if (event->src.rdata->msg->line.req.method.id == PJSIP_INVITE_METHOD) {
+ /* Create new dialog. */
+ dlg = pjsip_ua_create_dialog( ua, PJSIP_ROLE_UAS );
+
+ if (dlg == NULL ||
+ pjsip_dlg_init_from_rdata( dlg, event->src.rdata) != 0)
+ {
+ pjsip_tx_data *tdata;
+
+ /* Dialog initialization has failed. Respond request with 500 */
+ if (dlg) {
+ pjsip_ua_destroy_dialog(dlg);
+ }
+ tdata = pjsip_endpt_create_response(ua->endpt, event->src.rdata,
+ PJSIP_SC_INTERNAL_SERVER_ERROR);
+ if (tdata) {
+ pjsip_tsx_on_tx_msg( event->obj.tsx, tdata );
+ }
+ return;
+ }
+
+ } else {
+ pjsip_tx_data *tdata;
+
+ /* Check the method */
+ switch (tsx->method.id) {
+ case PJSIP_INVITE_METHOD:
+ case PJSIP_ACK_METHOD:
+ case PJSIP_BYE_METHOD:
+ case PJSIP_CANCEL_METHOD:
+ /* Stale non-INVITE request.
+ * For now, respond all stale requests with 481 (?).
+ */
+ tdata = pjsip_endpt_create_response(ua->endpt, event->src.rdata,
+ PJSIP_SC_CALL_TSX_DOES_NOT_EXIST);
+ if (tdata) {
+ pjsip_tsx_on_tx_msg( event->obj.tsx, tdata );
+ }
+ break;
+ }
+
+ return;
+ }
+ } else {
+ /* Check the method */
+ switch (tsx->method.id) {
+ case PJSIP_INVITE_METHOD:
+ case PJSIP_ACK_METHOD:
+ case PJSIP_BYE_METHOD:
+ case PJSIP_CANCEL_METHOD:
+ /* These methods belongs to dialog.
+ * If we receive these methods while no dialog is found,
+ * then it must be a stale responses.
+ */
+ break;
+ default:
+ return;
+ }
+
+ }
+
+ if (dlg == NULL) {
+ PJ_LOG(3, (LOG_THIS, "Receives spurious rdata %p from %s:%d",
+ event->src.rdata,
+ pj_sockaddr_get_str_addr(&event->src.rdata->addr),
+ pj_sockaddr_get_port(&event->src.rdata->addr)));
+ }
+
+ /* Set the dlg in the transaction (dlg can be NULL). */
+ tsx->module_data[ua->mod_id] = dlg;
+
+ } else {
+ /* This CAN happen with event->src_type == PJSIP_EVENT_TX_MSG
+ * if UAS is responding to a transaction which does not exist.
+ * Just ignore.
+ */
+ return;
+ }
+
+ /* Pass the event to the dlg. */
+ if (dlg) {
+ pjsip_dlg_on_tsx_event(dlg, tsx, event);
+ }
+}
+
+/*
+ * Register dialog to UA.
+ */
+static pj_status_t ua_register_dialog( pjsip_user_agent *ua, pjsip_dlg *dlg,
+ pj_str_t *key )
+{
+ /* Assure that no entry with similar key exists in the hash table. */
+ pj_assert( pj_hash_get( ua->dlg_table, key->ptr, key->slen) == 0);
+
+ /* Insert entry to hash table. */
+ pj_hash_set( dlg->pool, ua->dlg_table,
+ key->ptr, key->slen, dlg);
+
+ /* Insert to the list. */
+ pj_list_insert_before(&ua->dlg_list, dlg);
+ return PJ_SUCCESS;
+}
+
+/*
+ * Create a new dialog.
+ */
+PJ_DEF(pjsip_dlg*) pjsip_ua_create_dialog( pjsip_user_agent *ua,
+ pjsip_role_e role )
+{
+ pj_pool_t *pool;
+ pjsip_dlg *dlg;
+
+ PJ_UNUSED_ARG(ua)
+
+ /* Create pool for the dialog. */
+ pool = pjsip_endpt_create_pool( ua->endpt, "pdlg%p",
+ PJSIP_POOL_LEN_DIALOG,
+ PJSIP_POOL_INC_DIALOG);
+
+ /* Create the dialog. */
+ dlg = pj_pool_calloc(pool, 1, sizeof(pjsip_dlg));
+ dlg->pool = pool;
+ dlg->ua = ua;
+ dlg->role = role;
+ sprintf(dlg->obj_name, "dlg%p", dlg);
+
+ /* Create mutex for the dialog. */
+ dlg->mutex = pj_mutex_create(dlg->pool, "mdlg%p", 0);
+ if (!dlg->mutex) {
+ pjsip_endpt_destroy_pool(ua->endpt, pool);
+ return NULL;
+ }
+
+ /* Create unique tag for the dialog. */
+ pj_create_unique_string( pool, &dlg->local.tag );
+
+ /* Register dialog. */
+ pj_mutex_lock(ua->mutex);
+ if (ua_register_dialog(ua, dlg, &dlg->local.tag) != PJ_SUCCESS) {
+ pj_mutex_unlock(ua->mutex);
+ pj_mutex_destroy(dlg->mutex);
+ pjsip_endpt_destroy_pool( ua->endpt, pool );
+ return NULL;
+ }
+ pj_mutex_unlock(ua->mutex);
+
+ PJ_LOG(4, (dlg->obj_name, "new %s dialog created", pjsip_role_name(role)));
+ return dlg;
+}
+
+/*
+ * Destroy dialog.
+ */
+PJ_DEF(void) pjsip_ua_destroy_dialog( pjsip_dlg *dlg )
+{
+ PJ_LOG(5, (dlg->obj_name, "destroying.."));
+
+ /* Lock dialog's mutex.
+ * Check the mutex validity first since this function can be called
+ * on dialog initialization failure (which might be because mutex could not
+ * be allocated in the first place).
+ */
+ if (dlg->mutex) {
+ pj_mutex_lock(dlg->mutex);
+ }
+
+ /* This must be called while holding dialog's mutex, if any. */
+ pjsip_on_dialog_destroyed(dlg);
+
+ /* Lock UA. */
+ pj_mutex_lock(dlg->ua->mutex);
+
+ /* Erase from hash table. */
+ pj_hash_set( dlg->pool, dlg->ua->dlg_table,
+ dlg->local.tag.ptr, dlg->local.tag.slen, NULL);
+
+ /* Erase from the list. */
+ pj_list_erase(dlg);
+
+ /* Unlock UA. */
+ pj_mutex_unlock(dlg->ua->mutex);
+
+ /* Unlock mutex. */
+ if (dlg->mutex) {
+ pj_mutex_unlock(dlg->mutex);
+ }
+
+ /* Destroy the pool. */
+ pjsip_endpt_destroy_pool( dlg->ua->endpt, dlg->pool);
+}
+
+/*
+ * Dump user agent state to log file.
+ */
+PJ_DEF(void) pjsip_ua_dump(pjsip_user_agent *ua)
+{
+#if PJ_LOG_MAX_LEVEL >= 3
+ PJ_LOG(3,(LOG_THIS, "Dumping user agent"));
+ PJ_LOG(3,(LOG_THIS, " Pool capacity=%u, used=%u",
+ pj_pool_get_capacity(ua->pool),
+ pj_pool_get_used_size(ua->pool)));
+ PJ_LOG(3,(LOG_THIS, " Number of dialogs=%u", pj_hash_count(ua->dlg_table)));
+
+ if (pj_hash_count(ua->dlg_table)) {
+ pjsip_dlg *dlg;
+
+ PJ_LOG(3,(LOG_THIS, " Dumping dialog list:"));
+ dlg = ua->dlg_list.next;
+ while (dlg != (pjsip_dlg*) &ua->dlg_list) {
+ PJ_LOG(3, (LOG_THIS, " %s %s", dlg->obj_name,
+ pjsip_dlg_state_str(dlg->state)));
+ dlg = dlg->next;
+ }
+ }
+#endif
+}
+
diff --git a/pjsip/src/pjsip-ua/sip_ua_private.h b/pjsip/src/pjsip-ua/sip_ua_private.h
index 93657ddf..8a174afe 100644
--- a/pjsip/src/pjsip-ua/sip_ua_private.h
+++ b/pjsip/src/pjsip-ua/sip_ua_private.h
@@ -1,36 +1,36 @@
-/* $Id$ */
-/*
- * Copyright (C) 2003-2006 Benny Prijono <benny@prijono.org>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
-#ifndef __PJSIP_UA_PRIVATE_H__
-#define __PJSIP_UA_PRIVATE_H__
-
-
-/*
- * Internal dialog functions.
- */
-pj_status_t pjsip_dlg_init_from_rdata( pjsip_dlg *dlg,
- pjsip_rx_data *rdata );
-
-
-void pjsip_dlg_on_tsx_event( pjsip_dlg *dlg,
- pjsip_transaction *tsx,
- pjsip_event *event);
-
-
-#endif /* __PJSIP_UA_PRIVATE_H__ */
-
+/* $Id$ */
+/*
+ * Copyright (C) 2003-2006 Benny Prijono <benny@prijono.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#ifndef __PJSIP_UA_PRIVATE_H__
+#define __PJSIP_UA_PRIVATE_H__
+
+
+/*
+ * Internal dialog functions.
+ */
+pj_status_t pjsip_dlg_init_from_rdata( pjsip_dlg *dlg,
+ pjsip_rx_data *rdata );
+
+
+void pjsip_dlg_on_tsx_event( pjsip_dlg *dlg,
+ pjsip_transaction *tsx,
+ pjsip_event *event);
+
+
+#endif /* __PJSIP_UA_PRIVATE_H__ */
+
diff --git a/pjsip/src/pjsip/sip_auth.c b/pjsip/src/pjsip/sip_auth.c
index b3375867..9b7c52bb 100644
--- a/pjsip/src/pjsip/sip_auth.c
+++ b/pjsip/src/pjsip/sip_auth.c
@@ -1,804 +1,804 @@
-/* $Id$ */
-/*
- * Copyright (C) 2003-2006 Benny Prijono <benny@prijono.org>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
-#include <pjsip/sip_auth.h>
-#include <pjsip/sip_auth_parser.h> /* just to get pjsip_DIGEST_STR */
-#include <pjsip/sip_transport.h>
-#include <pjsip/sip_endpoint.h>
-#include <pjlib-util/md5.h>
-#include <pj/log.h>
-#include <pj/string.h>
-#include <pj/pool.h>
-#include <pj/guid.h>
-#include <pj/assert.h>
-#include <pj/ctype.h>
-
-/* Length of digest string. */
-#define MD5STRLEN 32
-
-/* Maximum stack size we use for storing username+realm+password etc. */
-#define MAX_TEMP 128
-
-/* A macro just to get rid of type mismatch between char and unsigned char */
-#define MD5_APPEND(pms,buf,len) pj_md5_update(pms, (const pj_uint8_t*)buf, len)
-
-/* Logging. */
-#define THIS_FILE "sip_auth.c"
-#if 0
-# define AUTH_TRACE_(expr) PJ_LOG(3, expr)
-#else
-# define AUTH_TRACE_(expr)
-#endif
-
-static const char hex[] = "0123456789abcdef";
-
-/* Transform digest to string.
- * output must be at least MD5STRLEN+1 bytes.
- *
- * NOTE: THE OUTPUT STRING IS NOT NULL TERMINATED!
- */
-static void digest2str(const unsigned char digest[], char *output)
-{
- char *p = output;
- int i;
-
- for (i = 0; i<16; ++i) {
- int val = digest[i];
- *p++ = hex[val >> 4];
- *p++ = hex[val & 0x0F];
- }
-}
-
-/*
- * Create response digest based on the parameters and store the
- * digest ASCII in 'result'.
- */
-static void create_digest( pj_str_t *result,
- const pj_str_t *nonce,
- const pj_str_t *nc,
- const pj_str_t *cnonce,
- const pj_str_t *qop,
- const pj_str_t *uri,
- const pjsip_cred_info *cred_info,
- const pj_str_t *method)
-{
- char ha1[MD5STRLEN];
- char ha2[MD5STRLEN];
- unsigned char digest[16];
- pj_md5_context pms;
-
- pj_assert(result->slen >= MD5STRLEN);
-
- AUTH_TRACE_((THIS_FILE, "Begin creating digest"));
-
- if (cred_info->data_type == PJSIP_CRED_DATA_PLAIN_PASSWD) {
- /***
- *** ha1 = MD5(username ":" realm ":" password)
- ***/
- pj_md5_init(&pms);
- MD5_APPEND( &pms, cred_info->username.ptr, cred_info->username.slen);
- MD5_APPEND( &pms, ":", 1);
- MD5_APPEND( &pms, cred_info->realm.ptr, cred_info->realm.slen);
- MD5_APPEND( &pms, ":", 1);
- MD5_APPEND( &pms, cred_info->data.ptr, cred_info->data.slen);
- pj_md5_final(&pms, digest);
-
- digest2str(digest, ha1);
-
- } else if (cred_info->data_type == PJSIP_CRED_DATA_DIGEST) {
- pj_assert(cred_info->data.slen == 32);
- pj_memcpy( ha1, cred_info->data.ptr, cred_info->data.slen );
- }
-
- AUTH_TRACE_((THIS_FILE, " ha1=%.32s", ha1));
-
- /***
- *** ha2 = MD5(method ":" req_uri)
- ***/
- pj_md5_init(&pms);
- MD5_APPEND( &pms, method->ptr, method->slen);
- MD5_APPEND( &pms, ":", 1);
- MD5_APPEND( &pms, uri->ptr, uri->slen);
- pj_md5_final(&pms, digest);
- digest2str(digest, ha2);
-
- AUTH_TRACE_((THIS_FILE, " ha2=%.32s", ha2));
-
- /***
- *** When qop is not used:
- *** response = MD5(ha1 ":" nonce ":" ha2)
- ***
- *** When qop=auth is used:
- *** response = MD5(ha1 ":" nonce ":" nc ":" cnonce ":" qop ":" ha2)
- ***/
- pj_md5_init(&pms);
- MD5_APPEND( &pms, ha1, MD5STRLEN);
- MD5_APPEND( &pms, ":", 1);
- MD5_APPEND( &pms, nonce->ptr, nonce->slen);
- if (qop && qop->slen != 0) {
- MD5_APPEND( &pms, ":", 1);
- MD5_APPEND( &pms, nc->ptr, nc->slen);
- MD5_APPEND( &pms, ":", 1);
- MD5_APPEND( &pms, cnonce->ptr, cnonce->slen);
- MD5_APPEND( &pms, ":", 1);
- MD5_APPEND( &pms, qop->ptr, qop->slen);
- }
- MD5_APPEND( &pms, ":", 1);
- MD5_APPEND( &pms, ha2, MD5STRLEN);
-
- /* This is the final response digest. */
- pj_md5_final(&pms, digest);
-
- /* Convert digest to string and store in chal->response. */
- result->slen = MD5STRLEN;
- digest2str(digest, result->ptr);
-
- AUTH_TRACE_((THIS_FILE, " digest=%.32s", result->ptr));
- AUTH_TRACE_((THIS_FILE, "Digest created"));
-}
-
-/*
- * Finds out if qop offer contains "auth" token.
- */
-static pj_bool_t has_auth_qop( pj_pool_t *pool, const pj_str_t *qop_offer)
-{
- pj_str_t qop;
- char *p;
-
- pj_strdup_with_null( pool, &qop, qop_offer);
- p = qop.ptr;
- while (*p) {
- *p = (char)pj_tolower(*p);
- ++p;
- }
-
- p = qop.ptr;
- while (*p) {
- if (*p=='a' && *(p+1)=='u' && *(p+2)=='t' && *(p+3)=='h') {
- int e = *(p+4);
- if (e=='"' || e==',' || e==0)
- return PJ_TRUE;
- else
- p += 4;
- } else {
- ++p;
- }
- }
-
- return PJ_FALSE;
-}
-
-/*
- * Generate response digest.
- * Most of the parameters to generate the digest (i.e. username, realm, uri,
- * and nonce) are expected to be in the credential. Additional parameters (i.e.
- * password and method param) should be supplied in the argument.
- *
- * The resulting digest will be stored in cred->response.
- * The pool is used to allocate 32 bytes to store the digest in cred->response.
- */
-static pj_status_t respond_digest( pj_pool_t *pool,
- pjsip_digest_credential *cred,
- const pjsip_digest_challenge *chal,
- const pj_str_t *uri,
- const pjsip_cred_info *cred_info,
- const pj_str_t *cnonce,
- pj_uint32_t nc,
- const pj_str_t *method)
-{
- /* Check algorithm is supported. We only support MD5. */
- if (chal->algorithm.slen && pj_stricmp(&chal->algorithm, &pjsip_MD5_STR))
- {
- PJ_LOG(4,(THIS_FILE, "Unsupported digest algorithm \"%.*s\"",
- chal->algorithm.slen, chal->algorithm.ptr));
- return -1;
- }
-
- /* Build digest credential from arguments. */
- pj_strdup(pool, &cred->username, &cred_info->username);
- pj_strdup(pool, &cred->realm, &chal->realm);
- pj_strdup(pool, &cred->nonce, &chal->nonce);
- pj_strdup(pool, &cred->uri, uri);
- cred->algorithm = pjsip_MD5_STR;
- pj_strdup(pool, &cred->opaque, &chal->opaque);
-
- /* Allocate memory. */
- cred->response.ptr = pj_pool_alloc(pool, MD5STRLEN);
- cred->response.slen = MD5STRLEN;
-
- if (chal->qop.slen == 0) {
- /* Server doesn't require quality of protection. */
-
- /* Convert digest to string and store in chal->response. */
- create_digest( &cred->response, &cred->nonce, NULL, NULL, NULL,
- uri, cred_info, method);
-
- } else if (has_auth_qop(pool, &chal->qop)) {
- /* Server requires quality of protection.
- * We respond with selecting "qop=auth" protection.
- */
- cred->qop = pjsip_AUTH_STR;
- cred->nc.ptr = pj_pool_alloc(pool, 16);
- pj_snprintf(cred->nc.ptr, 16, "%06u", nc);
-
- if (cnonce && cnonce->slen) {
- pj_strdup(pool, &cred->cnonce, cnonce);
- } else {
- pj_str_t dummy_cnonce = { "b39971", 6};
- pj_strdup(pool, &cred->cnonce, &dummy_cnonce);
- }
-
- create_digest( &cred->response, &cred->nonce, &cred->nc, cnonce,
- &pjsip_AUTH_STR, uri, cred_info, method );
-
- } else {
- /* Server requires quality protection that we don't support. */
- PJ_LOG(4,(THIS_FILE, "Unsupported qop offer %.*s",
- chal->qop.slen, chal->qop.ptr));
- return -1;
- }
-
- return 0;
-}
-
-#if PJSIP_AUTH_QOP_SUPPORT
-/*
- * Update authentication session with a challenge.
- */
-static void update_digest_session( pj_pool_t *ses_pool,
- pjsip_auth_session *auth_sess,
- const pjsip_www_authenticate_hdr *hdr )
-{
- if (hdr->challenge.digest.qop.slen == 0)
- return;
-
- /* Initialize cnonce and qop if not present. */
- if (auth_sess->cnonce.slen == 0) {
- /* Save the whole challenge */
- auth_sess->last_chal = pjsip_hdr_clone(ses_pool, hdr);
-
- /* Create cnonce */
- pj_create_unique_string( ses_pool, &auth_sess->cnonce );
-
- /* Initialize nonce-count */
- auth_sess->nc = 1;
-
- /* Save realm. */
- pj_assert(auth_sess->realm.slen != 0);
- if (auth_sess->realm.slen == 0) {
- pj_strdup(ses_pool, &auth_sess->realm,
- &hdr->challenge.digest.realm);
- }
-
- } else {
- /* Update last_nonce and nonce-count */
- if (!pj_strcmp(&hdr->challenge.digest.nonce,
- &auth_sess->last_chal->challenge.digest.nonce))
- {
- /* Same nonce, increment nonce-count */
- ++auth_sess->nc;
- } else {
- /* Server gives new nonce. */
- pj_strdup(ses_pool, &auth_sess->last_chal->challenge.digest.nonce,
- &hdr->challenge.digest.nonce);
- /* Has the opaque changed? */
- if (pj_strcmp(&auth_sess->last_chal->challenge.digest.opaque,
- &hdr->challenge.digest.opaque))
- {
- pj_strdup(ses_pool,
- &auth_sess->last_chal->challenge.digest.opaque,
- &hdr->challenge.digest.opaque);
- }
- auth_sess->nc = 1;
- }
- }
-}
-#endif /* PJSIP_AUTH_QOP_SUPPORT */
-
-
-/* Find authentication session in the list. */
-static pjsip_auth_session *find_session( pjsip_auth_session *sess_list,
- const pj_str_t *realm )
-{
- pjsip_auth_session *sess = sess_list->next;
- while (sess != sess_list) {
- if (pj_stricmp(&sess->realm, realm) == 0)
- return sess;
- sess = sess->next;
- }
-
- return NULL;
-}
-
-/*
- * Create Authorization/Proxy-Authorization response header based on the challege
- * in WWW-Authenticate/Proxy-Authenticate header.
- */
-PJ_DEF(pjsip_authorization_hdr*)
-pjsip_auth_respond( pj_pool_t *req_pool,
- const pjsip_www_authenticate_hdr *hdr,
- const pjsip_uri *uri,
- const pjsip_cred_info *cred_info,
- const pjsip_method *method,
- pj_pool_t *sess_pool,
- pjsip_auth_session *auth_sess)
-{
- pjsip_authorization_hdr *auth;
- char tmp[PJSIP_MAX_URL_SIZE];
- pj_str_t uri_str;
- pj_pool_t *pool;
-
- pj_assert(hdr != NULL);
- pj_assert(uri != NULL);
- pj_assert(cred_info != NULL);
- pj_assert(method != NULL);
-
- /* Print URL in the original request. */
- uri_str.ptr = tmp;
- uri_str.slen = pjsip_uri_print(PJSIP_URI_IN_REQ_URI, uri, tmp, sizeof(tmp));
- if (uri_str.slen < 1) {
- pj_assert(!"URL is too long!");
- PJ_LOG(4,(THIS_FILE, "Unable to authorize: URI is too long!"));
- return NULL;
- }
-
-# if (PJSIP_AUTH_HEADER_CACHING)
- {
- pool = sess_pool;
- PJ_UNUSED_ARG(req_pool);
- }
-# else
- {
- pool = req_pool;
- PJ_UNUSED_ARG(sess_pool);
- }
-# endif
-
- if (hdr->type == PJSIP_H_WWW_AUTHENTICATE)
- auth = pjsip_authorization_hdr_create(pool);
- else if (hdr->type == PJSIP_H_PROXY_AUTHENTICATE)
- auth = pjsip_proxy_authorization_hdr_create(pool);
- else {
- pj_assert(0);
- return NULL;
- }
-
- /* Only support digest scheme at the moment. */
- if (!pj_stricmp(&hdr->scheme, &pjsip_DIGEST_STR)) {
- pj_status_t rc;
- pj_str_t *cnonce = NULL;
- pj_uint32_t nc = 1;
-
- /* Update the session (nonce-count etc) if required. */
-# if PJSIP_AUTH_QOP_SUPPORT
- {
- if (auth_sess) {
- update_digest_session( sess_pool, auth_sess, hdr );
-
- cnonce = &auth_sess->cnonce;
- nc = auth_sess->nc;
- }
- }
-# endif /* PJSIP_AUTH_QOP_SUPPORT */
-
- auth->scheme = pjsip_DIGEST_STR;
- rc = respond_digest( pool, &auth->credential.digest,
- &hdr->challenge.digest, &uri_str, cred_info,
- cnonce, nc, &method->name);
- if (rc != 0)
- return NULL;
-
- /* Set qop type in auth session the first time only. */
- if (hdr->challenge.digest.qop.slen != 0 && auth_sess) {
- if (auth_sess->qop_value == PJSIP_AUTH_QOP_NONE) {
- pj_str_t *qop_val = &auth->credential.digest.qop;
- if (!pj_strcmp(qop_val, &pjsip_AUTH_STR)) {
- auth_sess->qop_value = PJSIP_AUTH_QOP_AUTH;
- } else {
- auth_sess->qop_value = PJSIP_AUTH_QOP_UNKNOWN;
- }
- }
- }
- } else {
- auth = NULL;
- }
-
- /* Keep the new authorization header in the cache, only
- * if no qop is not present.
- */
-# if PJSIP_AUTH_HEADER_CACHING
- {
- if (auth && auth_sess && auth_sess->qop_value == PJSIP_AUTH_QOP_NONE) {
- pjsip_cached_auth_hdr *cached_hdr;
-
- /* Delete old header with the same method. */
- cached_hdr = auth_sess->cached_hdr.next;
- while (cached_hdr != &auth_sess->cached_hdr) {
- if (pjsip_method_cmp(method, &cached_hdr->method)==0)
- break;
- cached_hdr = cached_hdr->next;
- }
-
- /* Save the header to the list. */
- if (cached_hdr != &auth_sess->cached_hdr) {
- cached_hdr->hdr = auth;
- } else {
- cached_hdr = pj_pool_alloc(pool, sizeof(*cached_hdr));
- pjsip_method_copy( pool, &cached_hdr->method, method);
- cached_hdr->hdr = auth;
- pj_list_insert_before( &auth_sess->cached_hdr, cached_hdr );
- }
- }
- }
-# endif
-
- return auth;
-
-}
-
-/* Verify incoming Authorization/Proxy-Authorization header against existing
- * credentials. Will return TRUE if the authorization request matches any of
- * the credential.
- */
-PJ_DEF(pj_bool_t) pjsip_auth_verify(const pjsip_authorization_hdr *hdr,
- const pj_str_t *method,
- const pjsip_cred_info *cred_info )
-{
- if (pj_stricmp(&hdr->scheme, &pjsip_DIGEST_STR) == 0) {
- char digest_buf[MD5STRLEN];
- pj_str_t digest;
- const pjsip_digest_credential *dig = &hdr->credential.digest;
-
- /* Check that username match. */
- if (pj_strcmp(&dig->username, &cred_info->username) != 0)
- return PJ_FALSE;
-
- /* Check that realm match. */
- if (pj_strcmp(&dig->realm, &cred_info->realm) != 0)
- return PJ_FALSE;
-
- /* Prepare for our digest calculation. */
- digest.ptr = digest_buf;
- digest.slen = MD5STRLEN;
-
- /* Create digest for comparison. */
- create_digest( &digest,
- &hdr->credential.digest.nonce,
- &hdr->credential.digest.nc,
- &hdr->credential.digest.cnonce,
- &hdr->credential.digest.qop,
- &hdr->credential.digest.uri,
- cred_info,
- method );
-
- return pj_stricmp(&digest, &hdr->credential.digest.response) == 0;
-
- } else {
- pj_assert(0);
- return PJ_FALSE;
- }
-}
-
-/* Find credential to use for the specified realm and scheme. */
-PJ_DEF(const pjsip_cred_info*) pjsip_auth_find_cred( unsigned count,
- const pjsip_cred_info cred[],
- const pj_str_t *realm,
- const pj_str_t *scheme)
-{
- unsigned i;
- PJ_UNUSED_ARG(scheme);
- for (i=0; i<count; ++i) {
- if (pj_stricmp(&cred[i].realm, realm) == 0)
- return &cred[i];
- }
- return NULL;
-}
-
-#if PJSIP_AUTH_AUTO_SEND_NEXT
-static void new_auth_for_req( pjsip_tx_data *tdata,
- pj_pool_t *sess_pool,
- pjsip_auth_session *sess,
- int cred_count,
- const pjsip_cred_info cred_info[])
-{
- const pjsip_cred_info *cred;
- pjsip_authorization_hdr *hauth;
-
- pj_assert(sess->last_chal != NULL);
-
- cred = pjsip_auth_find_cred( cred_count, cred_info, &sess->realm,
- &sess->last_chal->scheme );
- if (!cred)
- return;
-
-
- hauth = pjsip_auth_respond( tdata->pool, sess->last_chal,
- tdata->msg->line.req.uri,
- cred, &tdata->msg->line.req.method,
- sess_pool, sess);
- if (hauth) {
- pjsip_msg_add_hdr( tdata->msg, (pjsip_hdr*)hauth);
- }
-}
-#endif
-
-/*
- * Initialize new request message with authorization headers.
- * This function will put Authorization/Proxy-Authorization headers to the
- * outgoing request message. If caching is enabled (PJSIP_AUTH_HEADER_CACHING)
- * and the session has previously sent Authorization/Proxy-Authorization header
- * with the same method, then the same Authorization/Proxy-Authorization header
- * will be resent from the cache only if qop is not present. If the stack is
- * configured to automatically generate next Authorization/Proxy-Authorization
- * headers (PJSIP_AUTH_AUTO_SEND_NEXT flag), then new Authorization/Proxy-
- * Authorization headers are calculated and generated when they are not present
- * in the case or if authorization session has qop.
- *
- * If both PJSIP_AUTH_HEADER_CACHING flag and PJSIP_AUTH_AUTO_SEND_NEXT flag
- * are not set, this function will do nothing. The stack then will only send
- * Authorization/Proxy-Authorization to respond 401/407 response.
- */
-PJ_DEF(pj_status_t) pjsip_auth_init_req( pj_pool_t *sess_pool,
- pjsip_tx_data *tdata,
- pjsip_auth_session *sess_list,
- int cred_count,
- const pjsip_cred_info cred_info[])
-{
- pjsip_auth_session *sess;
- pjsip_method *method = &tdata->msg->line.req.method;
-
- pj_assert(tdata->msg->type == PJSIP_REQUEST_MSG);
-
- if (!sess_list)
- return 0;
-
- sess = sess_list->next;
- while (sess != sess_list) {
- if (sess->qop_value == PJSIP_AUTH_QOP_NONE) {
-# if (PJSIP_AUTH_HEADER_CACHING)
- {
- pjsip_cached_auth_hdr *entry = sess->cached_hdr.next;
- while (entry != &sess->cached_hdr) {
- if (pjsip_method_cmp(&entry->method, method)==0) {
- pjsip_authorization_hdr *hauth;
- hauth = pjsip_hdr_shallow_clone(tdata->pool, entry->hdr);
- pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr*)hauth);
- } else {
-# if (PJSIP_AUTH_AUTO_SEND_NEXT)
- {
- new_auth_for_req( tdata, sess_pool, sess,
- cred_count, cred_info);
- }
-# else
- {
- PJ_UNUSED_ARG(sess_pool);
- PJ_UNUSED_ARG(cred_count);
- PJ_UNUSED_ARG(cred_info);
- }
-# endif /* PJSIP_AUTH_AUTO_SEND_NEXT */
- }
- entry = entry->next;
- }
- }
-# elif (PJSIP_AUTH_AUTO_SEND_NEXT)
- {
- new_auth_for_req( tdata, sess_pool, sess,
- cred_count, cred_info);
- }
-# else
- {
- PJ_UNUSED_ARG(sess_pool);
- PJ_UNUSED_ARG(cred_count);
- PJ_UNUSED_ARG(cred_info);
- }
-# endif /* PJSIP_AUTH_HEADER_CACHING */
-
- }
-# if (PJSIP_AUTH_QOP_SUPPORT && PJSIP_AUTH_AUTO_SEND_NEXT)
- else if (sess->qop_value == PJSIP_AUTH_QOP_AUTH) {
- /* For qop="auth", we have to re-create the authorization header.
- */
- const pjsip_cred_info *cred;
- pjsip_authorization_hdr *hauth;
-
- cred = pjsip_auth_find_cred( cred_count, cred_info,
- &sess->realm,
- &sess->last_chal->scheme);
- if (!cred) {
- sess = sess->next;
- continue;
- }
-
- hauth = pjsip_auth_respond( tdata->pool, sess->last_chal,
- tdata->msg->line.req.uri,
- cred,
- &tdata->msg->line.req.method,
- sess_pool, sess );
- if (hauth) {
- pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr*)hauth);
- }
- }
-# endif /* PJSIP_AUTH_QOP_SUPPORT && PJSIP_AUTH_AUTO_SEND_NEXT */
-
- sess = sess->next;
- }
- return 0;
-}
-
-/* Process authorization challenge */
-static pjsip_authorization_hdr *process_auth( pj_pool_t *req_pool,
- const pjsip_www_authenticate_hdr *hchal,
- const pjsip_uri *uri,
- pjsip_tx_data *tdata,
- int cred_count,
- const pjsip_cred_info cred_info[],
- pj_pool_t *ses_pool,
- pjsip_auth_session *auth_sess)
-{
- const pjsip_cred_info *cred;
- pjsip_authorization_hdr *sent_auth = NULL, *hauth;
- pjsip_hdr *hdr;
-
- /* See if we have sent authorization header for this realm */
- hdr = tdata->msg->hdr.next;
- while (hdr != &tdata->msg->hdr) {
- if ((hchal->type == PJSIP_H_WWW_AUTHENTICATE &&
- hdr->type == PJSIP_H_AUTHORIZATION) ||
- (hchal->type == PJSIP_H_PROXY_AUTHENTICATE &&
- hdr->type == PJSIP_H_PROXY_AUTHORIZATION))
- {
- sent_auth = (pjsip_authorization_hdr*) hdr;
- if (pj_stricmp(&hchal->challenge.common.realm,
- &sent_auth->credential.common.realm )==0)
- {
- break;
- }
- }
- hdr = hdr->next;
- }
-
- /* If we have sent, see if server rejected because of stale nonce or
- * other causes.
- */
- if (hdr != &tdata->msg->hdr) {
- if (hchal->challenge.digest.stale == 0) {
- /* Our credential is rejected. No point in trying to re-supply
- * the same credential.
- */
- PJ_LOG(4, (THIS_FILE, "Authorization failed for %.*s@%.*s",
- sent_auth->credential.digest.username.slen,
- sent_auth->credential.digest.username.ptr,
- sent_auth->credential.digest.realm.slen,
- sent_auth->credential.digest.realm.ptr));
- return NULL;
- }
-
- /* Otherwise remove old, stale authorization header from the mesasge.
- * We will supply a new one.
- */
- pj_list_erase(sent_auth);
- }
-
- /* Find credential to be used for the challenge. */
- cred = pjsip_auth_find_cred( cred_count, cred_info,
- &hchal->challenge.common.realm, &hchal->scheme);
- if (!cred) {
- const pj_str_t *realm = &hchal->challenge.common.realm;
- PJ_LOG(4,(THIS_FILE,
- "Unable to set auth for %s: can not find credential for %.*s/%.*s",
- tdata->obj_name,
- realm->slen, realm->ptr,
- hchal->scheme.slen, hchal->scheme.ptr));
- return NULL;
- }
-
- /* Respond to authorization challenge. */
- hauth = pjsip_auth_respond( req_pool, hchal, uri, cred,
- &tdata->msg->line.req.method,
- ses_pool, auth_sess);
- return hauth;
-}
-
-
-/* Reinitialize outgoing request after 401/407 response is received.
- * The purpose of this function is:
- * - to add a Authorization/Proxy-Authorization header.
- * - to put the newly created Authorization/Proxy-Authorization header
- * in cached_list.
- */
-PJ_DEF(pjsip_tx_data*) pjsip_auth_reinit_req( pjsip_endpoint *endpt,
- pj_pool_t *ses_pool,
- pjsip_auth_session *sess_list,
- int cred_count,
- const pjsip_cred_info cred_info[],
- pjsip_tx_data *tdata,
- const pjsip_rx_data *rdata)
-{
- const pjsip_hdr *hdr;
- pjsip_via_hdr *via;
-
- PJ_UNUSED_ARG(endpt);
-
- pj_assert(rdata->msg_info.msg->type == PJSIP_RESPONSE_MSG);
- pj_assert(rdata->msg_info.msg->line.status.code == 401 ||
- rdata->msg_info.msg->line.status.code == 407 );
-
- /*
- * Respond to each authentication challenge.
- */
- hdr = rdata->msg_info.msg->hdr.next;
- while (hdr != &rdata->msg_info.msg->hdr) {
- pjsip_auth_session *sess;
- const pjsip_www_authenticate_hdr *hchal;
- pjsip_authorization_hdr *hauth;
-
- /* Find WWW-Authenticate or Proxy-Authenticate header. */
- while (hdr->type != PJSIP_H_WWW_AUTHENTICATE &&
- hdr->type != PJSIP_H_PROXY_AUTHENTICATE &&
- hdr != &rdata->msg_info.msg->hdr)
- {
- hdr = hdr->next;
- }
- if (hdr == &rdata->msg_info.msg->hdr)
- break;
-
- hchal = (const pjsip_www_authenticate_hdr*) hdr;
-
- /* Find authentication session for this realm, create a new one
- * if not present.
- */
- sess = find_session(sess_list, &hchal->challenge.common.realm );
- if (!sess) {
- sess = pj_pool_calloc( ses_pool, 1, sizeof(*sess));
- pj_strdup( ses_pool, &sess->realm, &hchal->challenge.common.realm);
- sess->is_proxy = (hchal->type == PJSIP_H_PROXY_AUTHENTICATE);
-# if (PJSIP_AUTH_HEADER_CACHING)
- {
- pj_list_init(&sess->cached_hdr);
- }
-# endif
- pj_list_insert_before( sess_list, sess );
- }
-
- /* Create authorization header for this challenge, and update
- * authorization session.
- */
- hauth = process_auth( tdata->pool, hchal, tdata->msg->line.req.uri,
- tdata, cred_count, cred_info, ses_pool, sess );
- if (!hauth)
- return NULL;
-
- /* Add to the message. */
- pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr*)hauth);
-
- /* Process next header. */
- hdr = hdr->next;
- }
-
-
- /* Remove branch param in Via header. */
- via = pjsip_msg_find_hdr(tdata->msg, PJSIP_H_VIA, NULL);
- via->branch_param.slen = 0;
-
- /* Increment reference counter. */
- pjsip_tx_data_add_ref(tdata);
-
- /* Done. */
- return tdata;
-}
-
+/* $Id$ */
+/*
+ * Copyright (C) 2003-2006 Benny Prijono <benny@prijono.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#include <pjsip/sip_auth.h>
+#include <pjsip/sip_auth_parser.h> /* just to get pjsip_DIGEST_STR */
+#include <pjsip/sip_transport.h>
+#include <pjsip/sip_endpoint.h>
+#include <pjlib-util/md5.h>
+#include <pj/log.h>
+#include <pj/string.h>
+#include <pj/pool.h>
+#include <pj/guid.h>
+#include <pj/assert.h>
+#include <pj/ctype.h>
+
+/* Length of digest string. */
+#define MD5STRLEN 32
+
+/* Maximum stack size we use for storing username+realm+password etc. */
+#define MAX_TEMP 128
+
+/* A macro just to get rid of type mismatch between char and unsigned char */
+#define MD5_APPEND(pms,buf,len) pj_md5_update(pms, (const pj_uint8_t*)buf, len)
+
+/* Logging. */
+#define THIS_FILE "sip_auth.c"
+#if 0
+# define AUTH_TRACE_(expr) PJ_LOG(3, expr)
+#else
+# define AUTH_TRACE_(expr)
+#endif
+
+static const char hex[] = "0123456789abcdef";
+
+/* Transform digest to string.
+ * output must be at least MD5STRLEN+1 bytes.
+ *
+ * NOTE: THE OUTPUT STRING IS NOT NULL TERMINATED!
+ */
+static void digest2str(const unsigned char digest[], char *output)
+{
+ char *p = output;
+ int i;
+
+ for (i = 0; i<16; ++i) {
+ int val = digest[i];
+ *p++ = hex[val >> 4];
+ *p++ = hex[val & 0x0F];
+ }
+}
+
+/*
+ * Create response digest based on the parameters and store the
+ * digest ASCII in 'result'.
+ */
+static void create_digest( pj_str_t *result,
+ const pj_str_t *nonce,
+ const pj_str_t *nc,
+ const pj_str_t *cnonce,
+ const pj_str_t *qop,
+ const pj_str_t *uri,
+ const pjsip_cred_info *cred_info,
+ const pj_str_t *method)
+{
+ char ha1[MD5STRLEN];
+ char ha2[MD5STRLEN];
+ unsigned char digest[16];
+ pj_md5_context pms;
+
+ pj_assert(result->slen >= MD5STRLEN);
+
+ AUTH_TRACE_((THIS_FILE, "Begin creating digest"));
+
+ if (cred_info->data_type == PJSIP_CRED_DATA_PLAIN_PASSWD) {
+ /***
+ *** ha1 = MD5(username ":" realm ":" password)
+ ***/
+ pj_md5_init(&pms);
+ MD5_APPEND( &pms, cred_info->username.ptr, cred_info->username.slen);
+ MD5_APPEND( &pms, ":", 1);
+ MD5_APPEND( &pms, cred_info->realm.ptr, cred_info->realm.slen);
+ MD5_APPEND( &pms, ":", 1);
+ MD5_APPEND( &pms, cred_info->data.ptr, cred_info->data.slen);
+ pj_md5_final(&pms, digest);
+
+ digest2str(digest, ha1);
+
+ } else if (cred_info->data_type == PJSIP_CRED_DATA_DIGEST) {
+ pj_assert(cred_info->data.slen == 32);
+ pj_memcpy( ha1, cred_info->data.ptr, cred_info->data.slen );
+ }
+
+ AUTH_TRACE_((THIS_FILE, " ha1=%.32s", ha1));
+
+ /***
+ *** ha2 = MD5(method ":" req_uri)
+ ***/
+ pj_md5_init(&pms);
+ MD5_APPEND( &pms, method->ptr, method->slen);
+ MD5_APPEND( &pms, ":", 1);
+ MD5_APPEND( &pms, uri->ptr, uri->slen);
+ pj_md5_final(&pms, digest);
+ digest2str(digest, ha2);
+
+ AUTH_TRACE_((THIS_FILE, " ha2=%.32s", ha2));
+
+ /***
+ *** When qop is not used:
+ *** response = MD5(ha1 ":" nonce ":" ha2)
+ ***
+ *** When qop=auth is used:
+ *** response = MD5(ha1 ":" nonce ":" nc ":" cnonce ":" qop ":" ha2)
+ ***/
+ pj_md5_init(&pms);
+ MD5_APPEND( &pms, ha1, MD5STRLEN);
+ MD5_APPEND( &pms, ":", 1);
+ MD5_APPEND( &pms, nonce->ptr, nonce->slen);
+ if (qop && qop->slen != 0) {
+ MD5_APPEND( &pms, ":", 1);
+ MD5_APPEND( &pms, nc->ptr, nc->slen);
+ MD5_APPEND( &pms, ":", 1);
+ MD5_APPEND( &pms, cnonce->ptr, cnonce->slen);
+ MD5_APPEND( &pms, ":", 1);
+ MD5_APPEND( &pms, qop->ptr, qop->slen);
+ }
+ MD5_APPEND( &pms, ":", 1);
+ MD5_APPEND( &pms, ha2, MD5STRLEN);
+
+ /* This is the final response digest. */
+ pj_md5_final(&pms, digest);
+
+ /* Convert digest to string and store in chal->response. */
+ result->slen = MD5STRLEN;
+ digest2str(digest, result->ptr);
+
+ AUTH_TRACE_((THIS_FILE, " digest=%.32s", result->ptr));
+ AUTH_TRACE_((THIS_FILE, "Digest created"));
+}
+
+/*
+ * Finds out if qop offer contains "auth" token.
+ */
+static pj_bool_t has_auth_qop( pj_pool_t *pool, const pj_str_t *qop_offer)
+{
+ pj_str_t qop;
+ char *p;
+
+ pj_strdup_with_null( pool, &qop, qop_offer);
+ p = qop.ptr;
+ while (*p) {
+ *p = (char)pj_tolower(*p);
+ ++p;
+ }
+
+ p = qop.ptr;
+ while (*p) {
+ if (*p=='a' && *(p+1)=='u' && *(p+2)=='t' && *(p+3)=='h') {
+ int e = *(p+4);
+ if (e=='"' || e==',' || e==0)
+ return PJ_TRUE;
+ else
+ p += 4;
+ } else {
+ ++p;
+ }
+ }
+
+ return PJ_FALSE;
+}
+
+/*
+ * Generate response digest.
+ * Most of the parameters to generate the digest (i.e. username, realm, uri,
+ * and nonce) are expected to be in the credential. Additional parameters (i.e.
+ * password and method param) should be supplied in the argument.
+ *
+ * The resulting digest will be stored in cred->response.
+ * The pool is used to allocate 32 bytes to store the digest in cred->response.
+ */
+static pj_status_t respond_digest( pj_pool_t *pool,
+ pjsip_digest_credential *cred,
+ const pjsip_digest_challenge *chal,
+ const pj_str_t *uri,
+ const pjsip_cred_info *cred_info,
+ const pj_str_t *cnonce,
+ pj_uint32_t nc,
+ const pj_str_t *method)
+{
+ /* Check algorithm is supported. We only support MD5. */
+ if (chal->algorithm.slen && pj_stricmp(&chal->algorithm, &pjsip_MD5_STR))
+ {
+ PJ_LOG(4,(THIS_FILE, "Unsupported digest algorithm \"%.*s\"",
+ chal->algorithm.slen, chal->algorithm.ptr));
+ return -1;
+ }
+
+ /* Build digest credential from arguments. */
+ pj_strdup(pool, &cred->username, &cred_info->username);
+ pj_strdup(pool, &cred->realm, &chal->realm);
+ pj_strdup(pool, &cred->nonce, &chal->nonce);
+ pj_strdup(pool, &cred->uri, uri);
+ cred->algorithm = pjsip_MD5_STR;
+ pj_strdup(pool, &cred->opaque, &chal->opaque);
+
+ /* Allocate memory. */
+ cred->response.ptr = pj_pool_alloc(pool, MD5STRLEN);
+ cred->response.slen = MD5STRLEN;
+
+ if (chal->qop.slen == 0) {
+ /* Server doesn't require quality of protection. */
+
+ /* Convert digest to string and store in chal->response. */
+ create_digest( &cred->response, &cred->nonce, NULL, NULL, NULL,
+ uri, cred_info, method);
+
+ } else if (has_auth_qop(pool, &chal->qop)) {
+ /* Server requires quality of protection.
+ * We respond with selecting "qop=auth" protection.
+ */
+ cred->qop = pjsip_AUTH_STR;
+ cred->nc.ptr = pj_pool_alloc(pool, 16);
+ pj_snprintf(cred->nc.ptr, 16, "%06u", nc);
+
+ if (cnonce && cnonce->slen) {
+ pj_strdup(pool, &cred->cnonce, cnonce);
+ } else {
+ pj_str_t dummy_cnonce = { "b39971", 6};
+ pj_strdup(pool, &cred->cnonce, &dummy_cnonce);
+ }
+
+ create_digest( &cred->response, &cred->nonce, &cred->nc, cnonce,
+ &pjsip_AUTH_STR, uri, cred_info, method );
+
+ } else {
+ /* Server requires quality protection that we don't support. */
+ PJ_LOG(4,(THIS_FILE, "Unsupported qop offer %.*s",
+ chal->qop.slen, chal->qop.ptr));
+ return -1;
+ }
+
+ return 0;
+}
+
+#if PJSIP_AUTH_QOP_SUPPORT
+/*
+ * Update authentication session with a challenge.
+ */
+static void update_digest_session( pj_pool_t *ses_pool,
+ pjsip_auth_session *auth_sess,
+ const pjsip_www_authenticate_hdr *hdr )
+{
+ if (hdr->challenge.digest.qop.slen == 0)
+ return;
+
+ /* Initialize cnonce and qop if not present. */
+ if (auth_sess->cnonce.slen == 0) {
+ /* Save the whole challenge */
+ auth_sess->last_chal = pjsip_hdr_clone(ses_pool, hdr);
+
+ /* Create cnonce */
+ pj_create_unique_string( ses_pool, &auth_sess->cnonce );
+
+ /* Initialize nonce-count */
+ auth_sess->nc = 1;
+
+ /* Save realm. */
+ pj_assert(auth_sess->realm.slen != 0);
+ if (auth_sess->realm.slen == 0) {
+ pj_strdup(ses_pool, &auth_sess->realm,
+ &hdr->challenge.digest.realm);
+ }
+
+ } else {
+ /* Update last_nonce and nonce-count */
+ if (!pj_strcmp(&hdr->challenge.digest.nonce,
+ &auth_sess->last_chal->challenge.digest.nonce))
+ {
+ /* Same nonce, increment nonce-count */
+ ++auth_sess->nc;
+ } else {
+ /* Server gives new nonce. */
+ pj_strdup(ses_pool, &auth_sess->last_chal->challenge.digest.nonce,
+ &hdr->challenge.digest.nonce);
+ /* Has the opaque changed? */
+ if (pj_strcmp(&auth_sess->last_chal->challenge.digest.opaque,
+ &hdr->challenge.digest.opaque))
+ {
+ pj_strdup(ses_pool,
+ &auth_sess->last_chal->challenge.digest.opaque,
+ &hdr->challenge.digest.opaque);
+ }
+ auth_sess->nc = 1;
+ }
+ }
+}
+#endif /* PJSIP_AUTH_QOP_SUPPORT */
+
+
+/* Find authentication session in the list. */
+static pjsip_auth_session *find_session( pjsip_auth_session *sess_list,
+ const pj_str_t *realm )
+{
+ pjsip_auth_session *sess = sess_list->next;
+ while (sess != sess_list) {
+ if (pj_stricmp(&sess->realm, realm) == 0)
+ return sess;
+ sess = sess->next;
+ }
+
+ return NULL;
+}
+
+/*
+ * Create Authorization/Proxy-Authorization response header based on the challege
+ * in WWW-Authenticate/Proxy-Authenticate header.
+ */
+PJ_DEF(pjsip_authorization_hdr*)
+pjsip_auth_respond( pj_pool_t *req_pool,
+ const pjsip_www_authenticate_hdr *hdr,
+ const pjsip_uri *uri,
+ const pjsip_cred_info *cred_info,
+ const pjsip_method *method,
+ pj_pool_t *sess_pool,
+ pjsip_auth_session *auth_sess)
+{
+ pjsip_authorization_hdr *auth;
+ char tmp[PJSIP_MAX_URL_SIZE];
+ pj_str_t uri_str;
+ pj_pool_t *pool;
+
+ pj_assert(hdr != NULL);
+ pj_assert(uri != NULL);
+ pj_assert(cred_info != NULL);
+ pj_assert(method != NULL);
+
+ /* Print URL in the original request. */
+ uri_str.ptr = tmp;
+ uri_str.slen = pjsip_uri_print(PJSIP_URI_IN_REQ_URI, uri, tmp, sizeof(tmp));
+ if (uri_str.slen < 1) {
+ pj_assert(!"URL is too long!");
+ PJ_LOG(4,(THIS_FILE, "Unable to authorize: URI is too long!"));
+ return NULL;
+ }
+
+# if (PJSIP_AUTH_HEADER_CACHING)
+ {
+ pool = sess_pool;
+ PJ_UNUSED_ARG(req_pool);
+ }
+# else
+ {
+ pool = req_pool;
+ PJ_UNUSED_ARG(sess_pool);
+ }
+# endif
+
+ if (hdr->type == PJSIP_H_WWW_AUTHENTICATE)
+ auth = pjsip_authorization_hdr_create(pool);
+ else if (hdr->type == PJSIP_H_PROXY_AUTHENTICATE)
+ auth = pjsip_proxy_authorization_hdr_create(pool);
+ else {
+ pj_assert(0);
+ return NULL;
+ }
+
+ /* Only support digest scheme at the moment. */
+ if (!pj_stricmp(&hdr->scheme, &pjsip_DIGEST_STR)) {
+ pj_status_t rc;
+ pj_str_t *cnonce = NULL;
+ pj_uint32_t nc = 1;
+
+ /* Update the session (nonce-count etc) if required. */
+# if PJSIP_AUTH_QOP_SUPPORT
+ {
+ if (auth_sess) {
+ update_digest_session( sess_pool, auth_sess, hdr );
+
+ cnonce = &auth_sess->cnonce;
+ nc = auth_sess->nc;
+ }
+ }
+# endif /* PJSIP_AUTH_QOP_SUPPORT */
+
+ auth->scheme = pjsip_DIGEST_STR;
+ rc = respond_digest( pool, &auth->credential.digest,
+ &hdr->challenge.digest, &uri_str, cred_info,
+ cnonce, nc, &method->name);
+ if (rc != 0)
+ return NULL;
+
+ /* Set qop type in auth session the first time only. */
+ if (hdr->challenge.digest.qop.slen != 0 && auth_sess) {
+ if (auth_sess->qop_value == PJSIP_AUTH_QOP_NONE) {
+ pj_str_t *qop_val = &auth->credential.digest.qop;
+ if (!pj_strcmp(qop_val, &pjsip_AUTH_STR)) {
+ auth_sess->qop_value = PJSIP_AUTH_QOP_AUTH;
+ } else {
+ auth_sess->qop_value = PJSIP_AUTH_QOP_UNKNOWN;
+ }
+ }
+ }
+ } else {
+ auth = NULL;
+ }
+
+ /* Keep the new authorization header in the cache, only
+ * if no qop is not present.
+ */
+# if PJSIP_AUTH_HEADER_CACHING
+ {
+ if (auth && auth_sess && auth_sess->qop_value == PJSIP_AUTH_QOP_NONE) {
+ pjsip_cached_auth_hdr *cached_hdr;
+
+ /* Delete old header with the same method. */
+ cached_hdr = auth_sess->cached_hdr.next;
+ while (cached_hdr != &auth_sess->cached_hdr) {
+ if (pjsip_method_cmp(method, &cached_hdr->method)==0)
+ break;
+ cached_hdr = cached_hdr->next;
+ }
+
+ /* Save the header to the list. */
+ if (cached_hdr != &auth_sess->cached_hdr) {
+ cached_hdr->hdr = auth;
+ } else {
+ cached_hdr = pj_pool_alloc(pool, sizeof(*cached_hdr));
+ pjsip_method_copy( pool, &cached_hdr->method, method);
+ cached_hdr->hdr = auth;
+ pj_list_insert_before( &auth_sess->cached_hdr, cached_hdr );
+ }
+ }
+ }
+# endif
+
+ return auth;
+
+}
+
+/* Verify incoming Authorization/Proxy-Authorization header against existing
+ * credentials. Will return TRUE if the authorization request matches any of
+ * the credential.
+ */
+PJ_DEF(pj_bool_t) pjsip_auth_verify(const pjsip_authorization_hdr *hdr,
+ const pj_str_t *method,
+ const pjsip_cred_info *cred_info )
+{
+ if (pj_stricmp(&hdr->scheme, &pjsip_DIGEST_STR) == 0) {
+ char digest_buf[MD5STRLEN];
+ pj_str_t digest;
+ const pjsip_digest_credential *dig = &hdr->credential.digest;
+
+ /* Check that username match. */
+ if (pj_strcmp(&dig->username, &cred_info->username) != 0)
+ return PJ_FALSE;
+
+ /* Check that realm match. */
+ if (pj_strcmp(&dig->realm, &cred_info->realm) != 0)
+ return PJ_FALSE;
+
+ /* Prepare for our digest calculation. */
+ digest.ptr = digest_buf;
+ digest.slen = MD5STRLEN;
+
+ /* Create digest for comparison. */
+ create_digest( &digest,
+ &hdr->credential.digest.nonce,
+ &hdr->credential.digest.nc,
+ &hdr->credential.digest.cnonce,
+ &hdr->credential.digest.qop,
+ &hdr->credential.digest.uri,
+ cred_info,
+ method );
+
+ return pj_stricmp(&digest, &hdr->credential.digest.response) == 0;
+
+ } else {
+ pj_assert(0);
+ return PJ_FALSE;
+ }
+}
+
+/* Find credential to use for the specified realm and scheme. */
+PJ_DEF(const pjsip_cred_info*) pjsip_auth_find_cred( unsigned count,
+ const pjsip_cred_info cred[],
+ const pj_str_t *realm,
+ const pj_str_t *scheme)
+{
+ unsigned i;
+ PJ_UNUSED_ARG(scheme);
+ for (i=0; i<count; ++i) {
+ if (pj_stricmp(&cred[i].realm, realm) == 0)
+ return &cred[i];
+ }
+ return NULL;
+}
+
+#if PJSIP_AUTH_AUTO_SEND_NEXT
+static void new_auth_for_req( pjsip_tx_data *tdata,
+ pj_pool_t *sess_pool,
+ pjsip_auth_session *sess,
+ int cred_count,
+ const pjsip_cred_info cred_info[])
+{
+ const pjsip_cred_info *cred;
+ pjsip_authorization_hdr *hauth;
+
+ pj_assert(sess->last_chal != NULL);
+
+ cred = pjsip_auth_find_cred( cred_count, cred_info, &sess->realm,
+ &sess->last_chal->scheme );
+ if (!cred)
+ return;
+
+
+ hauth = pjsip_auth_respond( tdata->pool, sess->last_chal,
+ tdata->msg->line.req.uri,
+ cred, &tdata->msg->line.req.method,
+ sess_pool, sess);
+ if (hauth) {
+ pjsip_msg_add_hdr( tdata->msg, (pjsip_hdr*)hauth);
+ }
+}
+#endif
+
+/*
+ * Initialize new request message with authorization headers.
+ * This function will put Authorization/Proxy-Authorization headers to the
+ * outgoing request message. If caching is enabled (PJSIP_AUTH_HEADER_CACHING)
+ * and the session has previously sent Authorization/Proxy-Authorization header
+ * with the same method, then the same Authorization/Proxy-Authorization header
+ * will be resent from the cache only if qop is not present. If the stack is
+ * configured to automatically generate next Authorization/Proxy-Authorization
+ * headers (PJSIP_AUTH_AUTO_SEND_NEXT flag), then new Authorization/Proxy-
+ * Authorization headers are calculated and generated when they are not present
+ * in the case or if authorization session has qop.
+ *
+ * If both PJSIP_AUTH_HEADER_CACHING flag and PJSIP_AUTH_AUTO_SEND_NEXT flag
+ * are not set, this function will do nothing. The stack then will only send
+ * Authorization/Proxy-Authorization to respond 401/407 response.
+ */
+PJ_DEF(pj_status_t) pjsip_auth_init_req( pj_pool_t *sess_pool,
+ pjsip_tx_data *tdata,
+ pjsip_auth_session *sess_list,
+ int cred_count,
+ const pjsip_cred_info cred_info[])
+{
+ pjsip_auth_session *sess;
+ pjsip_method *method = &tdata->msg->line.req.method;
+
+ pj_assert(tdata->msg->type == PJSIP_REQUEST_MSG);
+
+ if (!sess_list)
+ return 0;
+
+ sess = sess_list->next;
+ while (sess != sess_list) {
+ if (sess->qop_value == PJSIP_AUTH_QOP_NONE) {
+# if (PJSIP_AUTH_HEADER_CACHING)
+ {
+ pjsip_cached_auth_hdr *entry = sess->cached_hdr.next;
+ while (entry != &sess->cached_hdr) {
+ if (pjsip_method_cmp(&entry->method, method)==0) {
+ pjsip_authorization_hdr *hauth;
+ hauth = pjsip_hdr_shallow_clone(tdata->pool, entry->hdr);
+ pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr*)hauth);
+ } else {
+# if (PJSIP_AUTH_AUTO_SEND_NEXT)
+ {
+ new_auth_for_req( tdata, sess_pool, sess,
+ cred_count, cred_info);
+ }
+# else
+ {
+ PJ_UNUSED_ARG(sess_pool);
+ PJ_UNUSED_ARG(cred_count);
+ PJ_UNUSED_ARG(cred_info);
+ }
+# endif /* PJSIP_AUTH_AUTO_SEND_NEXT */
+ }
+ entry = entry->next;
+ }
+ }
+# elif (PJSIP_AUTH_AUTO_SEND_NEXT)
+ {
+ new_auth_for_req( tdata, sess_pool, sess,
+ cred_count, cred_info);
+ }
+# else
+ {
+ PJ_UNUSED_ARG(sess_pool);
+ PJ_UNUSED_ARG(cred_count);
+ PJ_UNUSED_ARG(cred_info);
+ }
+# endif /* PJSIP_AUTH_HEADER_CACHING */
+
+ }
+# if (PJSIP_AUTH_QOP_SUPPORT && PJSIP_AUTH_AUTO_SEND_NEXT)
+ else if (sess->qop_value == PJSIP_AUTH_QOP_AUTH) {
+ /* For qop="auth", we have to re-create the authorization header.
+ */
+ const pjsip_cred_info *cred;
+ pjsip_authorization_hdr *hauth;
+
+ cred = pjsip_auth_find_cred( cred_count, cred_info,
+ &sess->realm,
+ &sess->last_chal->scheme);
+ if (!cred) {
+ sess = sess->next;
+ continue;
+ }
+
+ hauth = pjsip_auth_respond( tdata->pool, sess->last_chal,
+ tdata->msg->line.req.uri,
+ cred,
+ &tdata->msg->line.req.method,
+ sess_pool, sess );
+ if (hauth) {
+ pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr*)hauth);
+ }
+ }
+# endif /* PJSIP_AUTH_QOP_SUPPORT && PJSIP_AUTH_AUTO_SEND_NEXT */
+
+ sess = sess->next;
+ }
+ return 0;
+}
+
+/* Process authorization challenge */
+static pjsip_authorization_hdr *process_auth( pj_pool_t *req_pool,
+ const pjsip_www_authenticate_hdr *hchal,
+ const pjsip_uri *uri,
+ pjsip_tx_data *tdata,
+ int cred_count,
+ const pjsip_cred_info cred_info[],
+ pj_pool_t *ses_pool,
+ pjsip_auth_session *auth_sess)
+{
+ const pjsip_cred_info *cred;
+ pjsip_authorization_hdr *sent_auth = NULL, *hauth;
+ pjsip_hdr *hdr;
+
+ /* See if we have sent authorization header for this realm */
+ hdr = tdata->msg->hdr.next;
+ while (hdr != &tdata->msg->hdr) {
+ if ((hchal->type == PJSIP_H_WWW_AUTHENTICATE &&
+ hdr->type == PJSIP_H_AUTHORIZATION) ||
+ (hchal->type == PJSIP_H_PROXY_AUTHENTICATE &&
+ hdr->type == PJSIP_H_PROXY_AUTHORIZATION))
+ {
+ sent_auth = (pjsip_authorization_hdr*) hdr;
+ if (pj_stricmp(&hchal->challenge.common.realm,
+ &sent_auth->credential.common.realm )==0)
+ {
+ break;
+ }
+ }
+ hdr = hdr->next;
+ }
+
+ /* If we have sent, see if server rejected because of stale nonce or
+ * other causes.
+ */
+ if (hdr != &tdata->msg->hdr) {
+ if (hchal->challenge.digest.stale == 0) {
+ /* Our credential is rejected. No point in trying to re-supply
+ * the same credential.
+ */
+ PJ_LOG(4, (THIS_FILE, "Authorization failed for %.*s@%.*s",
+ sent_auth->credential.digest.username.slen,
+ sent_auth->credential.digest.username.ptr,
+ sent_auth->credential.digest.realm.slen,
+ sent_auth->credential.digest.realm.ptr));
+ return NULL;
+ }
+
+ /* Otherwise remove old, stale authorization header from the mesasge.
+ * We will supply a new one.
+ */
+ pj_list_erase(sent_auth);
+ }
+
+ /* Find credential to be used for the challenge. */
+ cred = pjsip_auth_find_cred( cred_count, cred_info,
+ &hchal->challenge.common.realm, &hchal->scheme);
+ if (!cred) {
+ const pj_str_t *realm = &hchal->challenge.common.realm;
+ PJ_LOG(4,(THIS_FILE,
+ "Unable to set auth for %s: can not find credential for %.*s/%.*s",
+ tdata->obj_name,
+ realm->slen, realm->ptr,
+ hchal->scheme.slen, hchal->scheme.ptr));
+ return NULL;
+ }
+
+ /* Respond to authorization challenge. */
+ hauth = pjsip_auth_respond( req_pool, hchal, uri, cred,
+ &tdata->msg->line.req.method,
+ ses_pool, auth_sess);
+ return hauth;
+}
+
+
+/* Reinitialize outgoing request after 401/407 response is received.
+ * The purpose of this function is:
+ * - to add a Authorization/Proxy-Authorization header.
+ * - to put the newly created Authorization/Proxy-Authorization header
+ * in cached_list.
+ */
+PJ_DEF(pjsip_tx_data*) pjsip_auth_reinit_req( pjsip_endpoint *endpt,
+ pj_pool_t *ses_pool,
+ pjsip_auth_session *sess_list,
+ int cred_count,
+ const pjsip_cred_info cred_info[],
+ pjsip_tx_data *tdata,
+ const pjsip_rx_data *rdata)
+{
+ const pjsip_hdr *hdr;
+ pjsip_via_hdr *via;
+
+ PJ_UNUSED_ARG(endpt);
+
+ pj_assert(rdata->msg_info.msg->type == PJSIP_RESPONSE_MSG);
+ pj_assert(rdata->msg_info.msg->line.status.code == 401 ||
+ rdata->msg_info.msg->line.status.code == 407 );
+
+ /*
+ * Respond to each authentication challenge.
+ */
+ hdr = rdata->msg_info.msg->hdr.next;
+ while (hdr != &rdata->msg_info.msg->hdr) {
+ pjsip_auth_session *sess;
+ const pjsip_www_authenticate_hdr *hchal;
+ pjsip_authorization_hdr *hauth;
+
+ /* Find WWW-Authenticate or Proxy-Authenticate header. */
+ while (hdr->type != PJSIP_H_WWW_AUTHENTICATE &&
+ hdr->type != PJSIP_H_PROXY_AUTHENTICATE &&
+ hdr != &rdata->msg_info.msg->hdr)
+ {
+ hdr = hdr->next;
+ }
+ if (hdr == &rdata->msg_info.msg->hdr)
+ break;
+
+ hchal = (const pjsip_www_authenticate_hdr*) hdr;
+
+ /* Find authentication session for this realm, create a new one
+ * if not present.
+ */
+ sess = find_session(sess_list, &hchal->challenge.common.realm );
+ if (!sess) {
+ sess = pj_pool_calloc( ses_pool, 1, sizeof(*sess));
+ pj_strdup( ses_pool, &sess->realm, &hchal->challenge.common.realm);
+ sess->is_proxy = (hchal->type == PJSIP_H_PROXY_AUTHENTICATE);
+# if (PJSIP_AUTH_HEADER_CACHING)
+ {
+ pj_list_init(&sess->cached_hdr);
+ }
+# endif
+ pj_list_insert_before( sess_list, sess );
+ }
+
+ /* Create authorization header for this challenge, and update
+ * authorization session.
+ */
+ hauth = process_auth( tdata->pool, hchal, tdata->msg->line.req.uri,
+ tdata, cred_count, cred_info, ses_pool, sess );
+ if (!hauth)
+ return NULL;
+
+ /* Add to the message. */
+ pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr*)hauth);
+
+ /* Process next header. */
+ hdr = hdr->next;
+ }
+
+
+ /* Remove branch param in Via header. */
+ via = pjsip_msg_find_hdr(tdata->msg, PJSIP_H_VIA, NULL);
+ via->branch_param.slen = 0;
+
+ /* Increment reference counter. */
+ pjsip_tx_data_add_ref(tdata);
+
+ /* Done. */
+ return tdata;
+}
+
diff --git a/pjsip/src/pjsip/sip_auth_msg.c b/pjsip/src/pjsip/sip_auth_msg.c
index 3eca0cb2..366bff0b 100644
--- a/pjsip/src/pjsip/sip_auth_msg.c
+++ b/pjsip/src/pjsip/sip_auth_msg.c
@@ -1,310 +1,327 @@
-/* $Id$ */
-/*
- * Copyright (C) 2003-2006 Benny Prijono <benny@prijono.org>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
-#include <pjsip/sip_auth_msg.h>
-#include <pjsip/sip_auth_parser.h>
-#include <pj/pool.h>
-#include <pj/list.h>
-#include <pj/string.h>
-#include <pj/assert.h>
-#include <pjsip/print_util.h>
-
-///////////////////////////////////////////////////////////////////////////////
-/*
- * Authorization and Proxy-Authorization header.
- */
-static pjsip_authorization_hdr* pjsip_authorization_hdr_clone( pj_pool_t *pool,
- const pjsip_authorization_hdr *hdr);
-static pjsip_authorization_hdr* pjsip_authorization_hdr_shallow_clone( pj_pool_t *pool,
- const pjsip_authorization_hdr *hdr);
-static int pjsip_authorization_hdr_print( pjsip_authorization_hdr *hdr,
- char *buf, pj_size_t size);
-
-static pjsip_hdr_vptr authorization_hdr_vptr =
-{
- (pjsip_hdr_clone_fptr) &pjsip_authorization_hdr_clone,
- (pjsip_hdr_clone_fptr) &pjsip_authorization_hdr_shallow_clone,
- (pjsip_hdr_print_fptr) &pjsip_authorization_hdr_print,
-};
-
-
-PJ_DEF(pjsip_authorization_hdr*) pjsip_authorization_hdr_create(pj_pool_t *pool)
-{
- pjsip_authorization_hdr *hdr = pj_pool_calloc(pool, 1, sizeof(*hdr));
- init_hdr(hdr, PJSIP_H_AUTHORIZATION, &authorization_hdr_vptr);
- return hdr;
-}
-
-PJ_DEF(pjsip_proxy_authorization_hdr*) pjsip_proxy_authorization_hdr_create(pj_pool_t *pool)
-{
- pjsip_proxy_authorization_hdr *hdr = pj_pool_calloc(pool, 1, sizeof(*hdr));
- init_hdr(hdr, PJSIP_H_PROXY_AUTHORIZATION, &authorization_hdr_vptr);
- return hdr;
-}
-
-static int print_digest_credential(pjsip_digest_credential *cred, char *buf, pj_size_t size)
-{
- int printed;
- char *startbuf = buf;
- char *endbuf = buf + size;
-
- copy_advance_pair_quote_cond(buf, "username=", 9, cred->username, '"', '"');
- copy_advance_pair_quote_cond(buf, ", realm=", 8, cred->realm, '"', '"');
- copy_advance_pair_quote_cond(buf, ", nonce=", 8, cred->nonce, '"', '"');
- copy_advance_pair_quote_cond(buf, ", uri=", 6, cred->uri, '"', '"');
- copy_advance_pair_quote_cond(buf, ", response=", 11, cred->response, '"', '"');
- copy_advance_pair(buf, ", algorithm=", 12, cred->algorithm);
- copy_advance_pair_quote_cond(buf, ", cnonce=", 9, cred->cnonce, '"', '"');
- copy_advance_pair_quote_cond(buf, ", opaque=", 9, cred->opaque, '"', '"');
- //Note: there's no dbl-quote in qop in Authorization header
- // (unlike WWW-Authenticate)
- //copy_advance_pair_quote_cond(buf, ", qop=", 6, cred->qop, '"', '"');
- copy_advance_pair(buf, ", qop=", 6, cred->qop);
- copy_advance_pair(buf, ", nc=", 5, cred->nc);
- copy_advance(buf, cred->other_param);
-
- return (int) (buf-startbuf);
-}
-
-static int print_pgp_credential(pjsip_pgp_credential *cred, char *buf, pj_size_t size)
-{
- PJ_UNUSED_ARG(cred);
- PJ_UNUSED_ARG(buf);
- PJ_UNUSED_ARG(size);
- return -1;
-}
-
-static int pjsip_authorization_hdr_print( pjsip_authorization_hdr *hdr,
- char *buf, pj_size_t size)
-{
- int printed;
- char *startbuf = buf;
- char *endbuf = buf + size;
-
- copy_advance(buf, hdr->name);
- *buf++ = ':';
- *buf++ = ' ';
-
- copy_advance(buf, hdr->scheme);
- *buf++ = ' ';
-
- if (pj_stricmp(&hdr->scheme, &pjsip_DIGEST_STR) == 0)
- {
- printed = print_digest_credential(&hdr->credential.digest, buf, endbuf - buf);
- }
- else if (pj_stricmp(&hdr->scheme, &pjsip_PGP_STR) == 0)
- {
- printed = print_pgp_credential(&hdr->credential.pgp, buf, endbuf - buf);
- }
- else {
- pj_assert(0);
- return -1;
- }
-
- if (printed == -1)
- return -1;
-
- buf += printed;
- *buf = '\0';
- return (int)(buf-startbuf);
-}
-
-static pjsip_authorization_hdr* pjsip_authorization_hdr_clone( pj_pool_t *pool,
- const pjsip_authorization_hdr *rhs)
-{
- /* This function also serves Proxy-Authorization header. */
- pjsip_authorization_hdr *hdr;
- if (rhs->type == PJSIP_H_AUTHORIZATION)
- hdr = pjsip_authorization_hdr_create(pool);
- else
- hdr = pjsip_proxy_authorization_hdr_create(pool);
-
- pj_strdup(pool, &hdr->scheme, &rhs->scheme);
-
- if (pj_stricmp2(&hdr->scheme, "digest") == 0) {
- pj_strdup(pool, &hdr->credential.digest.username, &rhs->credential.digest.username);
- pj_strdup(pool, &hdr->credential.digest.realm, &rhs->credential.digest.realm);
- pj_strdup(pool, &hdr->credential.digest.nonce, &rhs->credential.digest.nonce);
- pj_strdup(pool, &hdr->credential.digest.uri, &rhs->credential.digest.uri);
- pj_strdup(pool, &hdr->credential.digest.response, &rhs->credential.digest.response);
- pj_strdup(pool, &hdr->credential.digest.algorithm, &rhs->credential.digest.algorithm);
- pj_strdup(pool, &hdr->credential.digest.cnonce, &rhs->credential.digest.cnonce);
- pj_strdup(pool, &hdr->credential.digest.opaque, &rhs->credential.digest.opaque);
- pj_strdup(pool, &hdr->credential.digest.qop, &rhs->credential.digest.qop);
- pj_strdup(pool, &hdr->credential.digest.nc, &rhs->credential.digest.nc);
- pj_strdup(pool, &hdr->credential.digest.other_param, &rhs->credential.digest.other_param);
- } else if (pj_stricmp2(&hdr->scheme, "pgp") == 0) {
- pj_assert(0);
- return NULL;
- } else {
- pj_assert(0);
- return NULL;
- }
-
- return hdr;
-}
-
-static pjsip_authorization_hdr* pjsip_authorization_hdr_shallow_clone( pj_pool_t *pool,
- const pjsip_authorization_hdr *rhs)
-{
- /* This function also serves Proxy-Authorization header. */
- pjsip_authorization_hdr *hdr = pj_pool_alloc(pool, sizeof(*hdr));
- pj_memcpy(hdr, rhs, sizeof(*hdr));
- return hdr;
-}
-
-
-///////////////////////////////////////////////////////////////////////////////
-/*
- * Proxy-Authenticate and WWW-Authenticate header.
- */
-static int pjsip_www_authenticate_hdr_print( pjsip_www_authenticate_hdr *hdr,
- char *buf, pj_size_t size);
-static pjsip_www_authenticate_hdr* pjsip_www_authenticate_hdr_clone( pj_pool_t *pool,
- const pjsip_www_authenticate_hdr *hdr);
-static pjsip_www_authenticate_hdr* pjsip_www_authenticate_hdr_shallow_clone( pj_pool_t *pool,
- const pjsip_www_authenticate_hdr *hdr);
-
-static pjsip_hdr_vptr www_authenticate_hdr_vptr =
-{
- (pjsip_hdr_clone_fptr) &pjsip_www_authenticate_hdr_clone,
- (pjsip_hdr_clone_fptr) &pjsip_www_authenticate_hdr_shallow_clone,
- (pjsip_hdr_print_fptr) &pjsip_www_authenticate_hdr_print,
-};
-
-
-PJ_DEF(pjsip_www_authenticate_hdr*) pjsip_www_authenticate_hdr_create(pj_pool_t *pool)
-{
- pjsip_www_authenticate_hdr *hdr = pj_pool_calloc(pool, 1, sizeof(*hdr));
- init_hdr(hdr, PJSIP_H_WWW_AUTHENTICATE, &www_authenticate_hdr_vptr);
- return hdr;
-}
-
-
-PJ_DEF(pjsip_proxy_authenticate_hdr*) pjsip_proxy_authenticate_hdr_create(pj_pool_t *pool)
-{
- pjsip_proxy_authenticate_hdr *hdr = pj_pool_calloc(pool, 1, sizeof(*hdr));
- init_hdr(hdr, PJSIP_H_PROXY_AUTHENTICATE, &www_authenticate_hdr_vptr);
- return hdr;
-}
-
-static int print_digest_challenge( pjsip_digest_challenge *chal,
- char *buf, pj_size_t size)
-{
- int printed;
- char *startbuf = buf;
- char *endbuf = buf + size;
-
- copy_advance_pair_quote_cond(buf, " realm=", 7, chal->realm, '"', '"');
- copy_advance_pair_quote_cond(buf, ",domain=", 8, chal->domain, '"', '"');
- copy_advance_pair_quote_cond(buf, ",nonce=", 7, chal->nonce, '"', '"');
- copy_advance_pair_quote_cond(buf, ",opaque=", 8, chal->opaque, '"', '"');
- if (chal->stale) {
- pj_str_t true_str = { "true", 4 };
- copy_advance_pair(buf, ",stale=", 7, true_str);
- }
- copy_advance_pair(buf, ",algorithm=", 11, chal->algorithm);
- copy_advance_pair_quote_cond(buf, ",qop=", 5, chal->qop, '"', '"');
- copy_advance(buf, chal->other_param);
-
- return (int)(buf-startbuf);
-}
-
-static int print_pgp_challenge( pjsip_pgp_challenge *chal,
- char *buf, pj_size_t size)
-{
- PJ_UNUSED_ARG(chal);
- PJ_UNUSED_ARG(buf);
- PJ_UNUSED_ARG(size);
- return -1;
-}
-
-static int pjsip_www_authenticate_hdr_print( pjsip_www_authenticate_hdr *hdr,
- char *buf, pj_size_t size)
-{
- int printed;
- char *startbuf = buf;
- char *endbuf = buf + size;
-
- copy_advance(buf, hdr->name);
- *buf++ = ':';
- *buf++ = ' ';
-
- copy_advance(buf, hdr->scheme);
- *buf++ = ' ';
-
- if (pj_stricmp2(&hdr->scheme, "digest") == 0)
- printed = print_digest_challenge(&hdr->challenge.digest, buf, endbuf - buf);
- else if (pj_stricmp2(&hdr->scheme, "pgp") == 0)
- printed = print_pgp_challenge(&hdr->challenge.pgp, buf, endbuf - buf);
- else {
- pj_assert(0);
- return -1;
- }
-
- if (printed == -1)
- return -1;
-
- buf += printed;
- *buf = '\0';
- return (int)(buf-startbuf);
-}
-
-static pjsip_www_authenticate_hdr* pjsip_www_authenticate_hdr_clone( pj_pool_t *pool,
- const pjsip_www_authenticate_hdr *rhs)
-{
- /* This function also serves Proxy-Authenticate header. */
- pjsip_www_authenticate_hdr *hdr;
- if (rhs->type == PJSIP_H_WWW_AUTHENTICATE)
- hdr = pjsip_www_authenticate_hdr_create(pool);
- else
- hdr = pjsip_proxy_authenticate_hdr_create(pool);
-
- pj_strdup(pool, &hdr->scheme, &rhs->scheme);
-
- if (pj_stricmp2(&hdr->scheme, "digest") == 0) {
- pj_strdup(pool, &hdr->challenge.digest.realm, &rhs->challenge.digest.realm);
- pj_strdup(pool, &hdr->challenge.digest.domain, &rhs->challenge.digest.domain);
- pj_strdup(pool, &hdr->challenge.digest.nonce, &rhs->challenge.digest.nonce);
- pj_strdup(pool, &hdr->challenge.digest.opaque, &rhs->challenge.digest.opaque);
- hdr->challenge.digest.stale = rhs->challenge.digest.stale;
- pj_strdup(pool, &hdr->challenge.digest.algorithm, &rhs->challenge.digest.algorithm);
- pj_strdup(pool, &hdr->challenge.digest.qop, &rhs->challenge.digest.qop);
- pj_strdup(pool, &hdr->challenge.digest.other_param, &rhs->challenge.digest.other_param);
- } else if (pj_stricmp2(&hdr->scheme, "pgp") == 0) {
- pj_assert(0);
- return NULL;
- } else {
- pj_assert(0);
- return NULL;
- }
-
- return hdr;
-
-}
-
-static pjsip_www_authenticate_hdr* pjsip_www_authenticate_hdr_shallow_clone( pj_pool_t *pool,
- const pjsip_www_authenticate_hdr *rhs)
-{
- /* This function also serves Proxy-Authenticate header. */
- pjsip_www_authenticate_hdr *hdr = pj_pool_alloc(pool, sizeof(*hdr));
- pj_memcpy(hdr, rhs, sizeof(*hdr));
- return hdr;
-}
-
-
+/* $Id$ */
+/*
+ * Copyright (C) 2003-2006 Benny Prijono <benny@prijono.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#include <pjsip/sip_auth_msg.h>
+#include <pjsip/sip_auth_parser.h>
+#include <pj/pool.h>
+#include <pj/list.h>
+#include <pj/string.h>
+#include <pj/assert.h>
+#include <pjsip/print_util.h>
+
+///////////////////////////////////////////////////////////////////////////////
+/*
+ * Authorization and Proxy-Authorization header.
+ */
+static pjsip_authorization_hdr* pjsip_authorization_hdr_clone( pj_pool_t *pool,
+ const pjsip_authorization_hdr *hdr);
+static pjsip_authorization_hdr* pjsip_authorization_hdr_shallow_clone( pj_pool_t *pool,
+ const pjsip_authorization_hdr *hdr);
+static int pjsip_authorization_hdr_print( pjsip_authorization_hdr *hdr,
+ char *buf, pj_size_t size);
+
+static pjsip_hdr_vptr authorization_hdr_vptr =
+{
+ (pjsip_hdr_clone_fptr) &pjsip_authorization_hdr_clone,
+ (pjsip_hdr_clone_fptr) &pjsip_authorization_hdr_shallow_clone,
+ (pjsip_hdr_print_fptr) &pjsip_authorization_hdr_print,
+};
+
+
+PJ_DEF(pjsip_authorization_hdr*) pjsip_authorization_hdr_create(pj_pool_t *pool)
+{
+ pjsip_authorization_hdr *hdr = pj_pool_calloc(pool, 1, sizeof(*hdr));
+ init_hdr(hdr, PJSIP_H_AUTHORIZATION, &authorization_hdr_vptr);
+ pj_list_init(&hdr->credential.common.other_param);
+ return hdr;
+}
+
+PJ_DEF(pjsip_proxy_authorization_hdr*) pjsip_proxy_authorization_hdr_create(pj_pool_t *pool)
+{
+ pjsip_proxy_authorization_hdr *hdr = pj_pool_calloc(pool, 1, sizeof(*hdr));
+ init_hdr(hdr, PJSIP_H_PROXY_AUTHORIZATION, &authorization_hdr_vptr);
+ pj_list_init(&hdr->credential.common.other_param);
+ return hdr;
+}
+
+static int print_digest_credential(pjsip_digest_credential *cred, char *buf, pj_size_t size)
+{
+ int printed;
+ char *startbuf = buf;
+ char *endbuf = buf + size;
+
+ copy_advance_pair_quote_cond(buf, "username=", 9, cred->username, '"', '"');
+ copy_advance_pair_quote_cond(buf, ", realm=", 8, cred->realm, '"', '"');
+ copy_advance_pair_quote_cond(buf, ", nonce=", 8, cred->nonce, '"', '"');
+ copy_advance_pair_quote_cond(buf, ", uri=", 6, cred->uri, '"', '"');
+ copy_advance_pair_quote_cond(buf, ", response=", 11, cred->response, '"', '"');
+ copy_advance_pair(buf, ", algorithm=", 12, cred->algorithm);
+ copy_advance_pair_quote_cond(buf, ", cnonce=", 9, cred->cnonce, '"', '"');
+ copy_advance_pair_quote_cond(buf, ", opaque=", 9, cred->opaque, '"', '"');
+ //Note: there's no dbl-quote in qop in Authorization header
+ // (unlike WWW-Authenticate)
+ //copy_advance_pair_quote_cond(buf, ", qop=", 6, cred->qop, '"', '"');
+ copy_advance_pair(buf, ", qop=", 6, cred->qop);
+ copy_advance_pair(buf, ", nc=", 5, cred->nc);
+
+ printed = pjsip_param_print_on(&cred->other_param, buf, endbuf-buf, ',');
+ if (printed < 0)
+ return -1;
+ buf += printed;
+
+ return (int) (buf-startbuf);
+}
+
+static int print_pgp_credential(pjsip_pgp_credential *cred, char *buf, pj_size_t size)
+{
+ PJ_UNUSED_ARG(cred);
+ PJ_UNUSED_ARG(buf);
+ PJ_UNUSED_ARG(size);
+ return -1;
+}
+
+static int pjsip_authorization_hdr_print( pjsip_authorization_hdr *hdr,
+ char *buf, pj_size_t size)
+{
+ int printed;
+ char *startbuf = buf;
+ char *endbuf = buf + size;
+
+ copy_advance(buf, hdr->name);
+ *buf++ = ':';
+ *buf++ = ' ';
+
+ copy_advance(buf, hdr->scheme);
+ *buf++ = ' ';
+
+ if (pj_stricmp(&hdr->scheme, &pjsip_DIGEST_STR) == 0)
+ {
+ printed = print_digest_credential(&hdr->credential.digest, buf, endbuf - buf);
+ }
+ else if (pj_stricmp(&hdr->scheme, &pjsip_PGP_STR) == 0)
+ {
+ printed = print_pgp_credential(&hdr->credential.pgp, buf, endbuf - buf);
+ }
+ else {
+ pj_assert(0);
+ return -1;
+ }
+
+ if (printed == -1)
+ return -1;
+
+ buf += printed;
+ *buf = '\0';
+ return (int)(buf-startbuf);
+}
+
+static pjsip_authorization_hdr* pjsip_authorization_hdr_clone( pj_pool_t *pool,
+ const pjsip_authorization_hdr *rhs)
+{
+ /* This function also serves Proxy-Authorization header. */
+ pjsip_authorization_hdr *hdr;
+ if (rhs->type == PJSIP_H_AUTHORIZATION)
+ hdr = pjsip_authorization_hdr_create(pool);
+ else
+ hdr = pjsip_proxy_authorization_hdr_create(pool);
+
+ pj_strdup(pool, &hdr->scheme, &rhs->scheme);
+
+ if (pj_stricmp2(&hdr->scheme, "digest") == 0) {
+ pj_strdup(pool, &hdr->credential.digest.username, &rhs->credential.digest.username);
+ pj_strdup(pool, &hdr->credential.digest.realm, &rhs->credential.digest.realm);
+ pj_strdup(pool, &hdr->credential.digest.nonce, &rhs->credential.digest.nonce);
+ pj_strdup(pool, &hdr->credential.digest.uri, &rhs->credential.digest.uri);
+ pj_strdup(pool, &hdr->credential.digest.response, &rhs->credential.digest.response);
+ pj_strdup(pool, &hdr->credential.digest.algorithm, &rhs->credential.digest.algorithm);
+ pj_strdup(pool, &hdr->credential.digest.cnonce, &rhs->credential.digest.cnonce);
+ pj_strdup(pool, &hdr->credential.digest.opaque, &rhs->credential.digest.opaque);
+ pj_strdup(pool, &hdr->credential.digest.qop, &rhs->credential.digest.qop);
+ pj_strdup(pool, &hdr->credential.digest.nc, &rhs->credential.digest.nc);
+ pjsip_param_clone(pool, &hdr->credential.digest.other_param, &rhs->credential.digest.other_param);
+ } else if (pj_stricmp2(&hdr->scheme, "pgp") == 0) {
+ pj_assert(0);
+ return NULL;
+ } else {
+ pj_assert(0);
+ return NULL;
+ }
+
+ return hdr;
+}
+
+static pjsip_authorization_hdr* pjsip_authorization_hdr_shallow_clone( pj_pool_t *pool,
+ const pjsip_authorization_hdr *rhs)
+{
+ /* This function also serves Proxy-Authorization header. */
+ pjsip_authorization_hdr *hdr = pj_pool_alloc(pool, sizeof(*hdr));
+ pj_memcpy(hdr, rhs, sizeof(*hdr));
+ pjsip_param_shallow_clone(pool, &hdr->credential.common.other_param,
+ &rhs->credential.common.other_param);
+ return hdr;
+}
+
+
+///////////////////////////////////////////////////////////////////////////////
+/*
+ * Proxy-Authenticate and WWW-Authenticate header.
+ */
+static int pjsip_www_authenticate_hdr_print( pjsip_www_authenticate_hdr *hdr,
+ char *buf, pj_size_t size);
+static pjsip_www_authenticate_hdr* pjsip_www_authenticate_hdr_clone( pj_pool_t *pool,
+ const pjsip_www_authenticate_hdr *hdr);
+static pjsip_www_authenticate_hdr* pjsip_www_authenticate_hdr_shallow_clone( pj_pool_t *pool,
+ const pjsip_www_authenticate_hdr *hdr);
+
+static pjsip_hdr_vptr www_authenticate_hdr_vptr =
+{
+ (pjsip_hdr_clone_fptr) &pjsip_www_authenticate_hdr_clone,
+ (pjsip_hdr_clone_fptr) &pjsip_www_authenticate_hdr_shallow_clone,
+ (pjsip_hdr_print_fptr) &pjsip_www_authenticate_hdr_print,
+};
+
+
+PJ_DEF(pjsip_www_authenticate_hdr*) pjsip_www_authenticate_hdr_create(pj_pool_t *pool)
+{
+ pjsip_www_authenticate_hdr *hdr = pj_pool_calloc(pool, 1, sizeof(*hdr));
+ init_hdr(hdr, PJSIP_H_WWW_AUTHENTICATE, &www_authenticate_hdr_vptr);
+ pj_list_init(&hdr->challenge.common.other_param);
+ return hdr;
+}
+
+
+PJ_DEF(pjsip_proxy_authenticate_hdr*) pjsip_proxy_authenticate_hdr_create(pj_pool_t *pool)
+{
+ pjsip_proxy_authenticate_hdr *hdr = pj_pool_calloc(pool, 1, sizeof(*hdr));
+ init_hdr(hdr, PJSIP_H_PROXY_AUTHENTICATE, &www_authenticate_hdr_vptr);
+ pj_list_init(&hdr->challenge.common.other_param);
+ return hdr;
+}
+
+static int print_digest_challenge( pjsip_digest_challenge *chal,
+ char *buf, pj_size_t size)
+{
+ int printed;
+ char *startbuf = buf;
+ char *endbuf = buf + size;
+
+ copy_advance_pair_quote_cond(buf, " realm=", 7, chal->realm, '"', '"');
+ copy_advance_pair_quote_cond(buf, ",domain=", 8, chal->domain, '"', '"');
+ copy_advance_pair_quote_cond(buf, ",nonce=", 7, chal->nonce, '"', '"');
+ copy_advance_pair_quote_cond(buf, ",opaque=", 8, chal->opaque, '"', '"');
+ if (chal->stale) {
+ pj_str_t true_str = { "true", 4 };
+ copy_advance_pair(buf, ",stale=", 7, true_str);
+ }
+ copy_advance_pair(buf, ",algorithm=", 11, chal->algorithm);
+ copy_advance_pair_quote_cond(buf, ",qop=", 5, chal->qop, '"', '"');
+
+ printed = pjsip_param_print_on(&chal->other_param, buf, endbuf-buf, ',');
+ if (printed < 0)
+ return -1;
+ buf += printed;
+
+ return (int)(buf-startbuf);
+}
+
+static int print_pgp_challenge( pjsip_pgp_challenge *chal,
+ char *buf, pj_size_t size)
+{
+ PJ_UNUSED_ARG(chal);
+ PJ_UNUSED_ARG(buf);
+ PJ_UNUSED_ARG(size);
+ return -1;
+}
+
+static int pjsip_www_authenticate_hdr_print( pjsip_www_authenticate_hdr *hdr,
+ char *buf, pj_size_t size)
+{
+ int printed;
+ char *startbuf = buf;
+ char *endbuf = buf + size;
+
+ copy_advance(buf, hdr->name);
+ *buf++ = ':';
+ *buf++ = ' ';
+
+ copy_advance(buf, hdr->scheme);
+ *buf++ = ' ';
+
+ if (pj_stricmp2(&hdr->scheme, "digest") == 0)
+ printed = print_digest_challenge(&hdr->challenge.digest, buf, endbuf - buf);
+ else if (pj_stricmp2(&hdr->scheme, "pgp") == 0)
+ printed = print_pgp_challenge(&hdr->challenge.pgp, buf, endbuf - buf);
+ else {
+ pj_assert(0);
+ return -1;
+ }
+
+ if (printed == -1)
+ return -1;
+
+ buf += printed;
+ *buf = '\0';
+ return (int)(buf-startbuf);
+}
+
+static pjsip_www_authenticate_hdr* pjsip_www_authenticate_hdr_clone( pj_pool_t *pool,
+ const pjsip_www_authenticate_hdr *rhs)
+{
+ /* This function also serves Proxy-Authenticate header. */
+ pjsip_www_authenticate_hdr *hdr;
+ if (rhs->type == PJSIP_H_WWW_AUTHENTICATE)
+ hdr = pjsip_www_authenticate_hdr_create(pool);
+ else
+ hdr = pjsip_proxy_authenticate_hdr_create(pool);
+
+ pj_strdup(pool, &hdr->scheme, &rhs->scheme);
+
+ if (pj_stricmp2(&hdr->scheme, "digest") == 0) {
+ pj_strdup(pool, &hdr->challenge.digest.realm, &rhs->challenge.digest.realm);
+ pj_strdup(pool, &hdr->challenge.digest.domain, &rhs->challenge.digest.domain);
+ pj_strdup(pool, &hdr->challenge.digest.nonce, &rhs->challenge.digest.nonce);
+ pj_strdup(pool, &hdr->challenge.digest.opaque, &rhs->challenge.digest.opaque);
+ hdr->challenge.digest.stale = rhs->challenge.digest.stale;
+ pj_strdup(pool, &hdr->challenge.digest.algorithm, &rhs->challenge.digest.algorithm);
+ pj_strdup(pool, &hdr->challenge.digest.qop, &rhs->challenge.digest.qop);
+ pjsip_param_clone(pool, &hdr->challenge.digest.other_param,
+ &rhs->challenge.digest.other_param);
+ } else if (pj_stricmp2(&hdr->scheme, "pgp") == 0) {
+ pj_assert(0);
+ return NULL;
+ } else {
+ pj_assert(0);
+ return NULL;
+ }
+
+ return hdr;
+
+}
+
+static pjsip_www_authenticate_hdr* pjsip_www_authenticate_hdr_shallow_clone( pj_pool_t *pool,
+ const pjsip_www_authenticate_hdr *rhs)
+{
+ /* This function also serves Proxy-Authenticate header. */
+ pjsip_www_authenticate_hdr *hdr = pj_pool_alloc(pool, sizeof(*hdr));
+ pj_memcpy(hdr, rhs, sizeof(*hdr));
+ pjsip_param_shallow_clone(pool, &hdr->challenge.common.other_param,
+ &rhs->challenge.common.other_param);
+ return hdr;
+}
+
+
diff --git a/pjsip/src/pjsip/sip_auth_parser.c b/pjsip/src/pjsip/sip_auth_parser.c
index 4a1ffbb7..d050ffb2 100644
--- a/pjsip/src/pjsip/sip_auth_parser.c
+++ b/pjsip/src/pjsip/sip_auth_parser.c
@@ -1,291 +1,303 @@
-/* $Id$ */
-/*
- * Copyright (C) 2003-2006 Benny Prijono <benny@prijono.org>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
-#include <pjsip/sip_auth_parser.h>
-#include <pjsip/sip_auth_msg.h>
-#include <pjsip/sip_parser.h>
-#include <pj/assert.h>
-#include <pj/string.h>
-#include <pj/except.h>
-
-static pjsip_hdr* parse_hdr_authorization ( pjsip_parse_ctx *ctx );
-static pjsip_hdr* parse_hdr_proxy_authorization ( pjsip_parse_ctx *ctx );
-static pjsip_hdr* parse_hdr_www_authenticate ( pjsip_parse_ctx *ctx );
-static pjsip_hdr* parse_hdr_proxy_authenticate ( pjsip_parse_ctx *ctx );
-
-static void parse_digest_credential ( pj_scanner *scanner, pj_pool_t *pool,
- pjsip_digest_credential *cred);
-static void parse_pgp_credential ( pj_scanner *scanner, pj_pool_t *pool,
- pjsip_pgp_credential *cred);
-static void parse_digest_challenge ( pj_scanner *scanner, pj_pool_t *pool,
- pjsip_digest_challenge *chal);
-static void parse_pgp_challenge ( pj_scanner *scanner, pj_pool_t *pool,
- pjsip_pgp_challenge *chal);
-
-const pj_str_t pjsip_USERNAME_STR = { "username", 8 },
- pjsip_REALM_STR = { "realm", 5},
- pjsip_NONCE_STR = { "nonce", 5},
- pjsip_URI_STR = { "uri", 3 },
- pjsip_RESPONSE_STR = { "response", 8 },
- pjsip_ALGORITHM_STR = { "algorithm", 9 },
- pjsip_DOMAIN_STR = { "domain", 6 },
- pjsip_STALE_STR = { "stale", 5},
- pjsip_QOP_STR = { "qop", 3},
- pjsip_CNONCE_STR = { "cnonce", 6},
- pjsip_OPAQUE_STR = { "opaque", 6},
- pjsip_NC_STR = { "nc", 2},
- pjsip_TRUE_STR = { "true", 4},
- pjsip_QUOTED_TRUE_STR = { "\"true\"", 6},
- pjsip_FALSE_STR = { "false", 5},
- pjsip_QUOTED_FALSE_STR = { "\"false\"", 7},
- pjsip_DIGEST_STR = { "Digest", 6},
- pjsip_QUOTED_DIGEST_STR = { "\"Digest\"", 8},
- pjsip_PGP_STR = { "PGP", 3 },
- pjsip_QUOTED_PGP_STR = { "\"PGP\"", 5 },
- pjsip_MD5_STR = { "md5", 3 },
- pjsip_QUOTED_MD5_STR = { "\"md5\"", 5},
- pjsip_AUTH_STR = { "auth", 4},
- pjsip_QUOTED_AUTH_STR = { "\"auth\"", 6 };
-
-
-static void parse_digest_credential( pj_scanner *scanner, pj_pool_t *pool,
- pjsip_digest_credential *cred)
-{
- for (;;) {
- pj_str_t name, value;
-
- pjsip_parse_param_imp(scanner, &name, &value,PJSIP_PARSE_REMOVE_QUOTE);
-
- if (!pj_stricmp(&name, &pjsip_USERNAME_STR)) {
- cred->username = value;
-
- } else if (!pj_stricmp(&name, &pjsip_REALM_STR)) {
- cred->realm = value;
-
- } else if (!pj_stricmp(&name, &pjsip_NONCE_STR)) {
- cred->nonce = value;
-
- } else if (!pj_stricmp(&name, &pjsip_URI_STR)) {
- cred->uri = value;
-
- } else if (!pj_stricmp(&name, &pjsip_RESPONSE_STR)) {
- cred->response = value;
-
- } else if (!pj_stricmp(&name, &pjsip_ALGORITHM_STR)) {
- cred->algorithm = value;
-
- } else if (!pj_stricmp(&name, &pjsip_CNONCE_STR)) {
- cred->cnonce = value;
-
- } else if (!pj_stricmp(&name, &pjsip_OPAQUE_STR)) {
- cred->opaque = value;
-
- } else if (!pj_stricmp(&name, &pjsip_QOP_STR)) {
- cred->qop = value;
-
- } else if (!pj_stricmp(&name, &pjsip_NC_STR)) {
- cred->nc = value;
-
- } else {
- pjsip_concat_param_imp(&cred->other_param,pool,&name,&value, ',');
- }
-
- /* Eat comma */
- if (!pj_scan_is_eof(scanner) && *scanner->curptr == ',')
- pj_scan_get_char(scanner);
- else
- break;
- }
-}
-
-static void parse_pgp_credential( pj_scanner *scanner, pj_pool_t *pool,
- pjsip_pgp_credential *cred)
-{
- PJ_UNUSED_ARG(scanner);
- PJ_UNUSED_ARG(pool);
- PJ_UNUSED_ARG(cred);
-
- PJ_THROW(PJSIP_SYN_ERR_EXCEPTION);
-}
-
-static void parse_digest_challenge( pj_scanner *scanner, pj_pool_t *pool,
- pjsip_digest_challenge *chal)
-{
- for (;;) {
- pj_str_t name, value;
-
- pjsip_parse_param_imp(scanner, &name, &value,PJSIP_PARSE_REMOVE_QUOTE);
-
- if (!pj_stricmp(&name, &pjsip_REALM_STR)) {
- chal->realm = value;
-
- } else if (!pj_stricmp(&name, &pjsip_DOMAIN_STR)) {
- chal->domain = value;
-
- } else if (!pj_stricmp(&name, &pjsip_NONCE_STR)) {
- chal->nonce = value;
-
- } else if (!pj_stricmp(&name, &pjsip_OPAQUE_STR)) {
- chal->opaque = value;
-
- } else if (!pj_stricmp(&name, &pjsip_STALE_STR)) {
- if (!pj_stricmp(&value, &pjsip_TRUE_STR) ||
- !pj_stricmp(&value, &pjsip_QUOTED_TRUE_STR))
- {
- chal->stale = 1;
- }
-
- } else if (!pj_stricmp(&name, &pjsip_ALGORITHM_STR)) {
- chal->algorithm = value;
-
-
- } else if (!pj_stricmp(&name, &pjsip_QOP_STR)) {
- chal->qop = value;
-
- } else {
- pjsip_concat_param_imp(&chal->other_param, pool,
- &name, &value, ',');
- }
-
- /* Eat comma */
- if (!pj_scan_is_eof(scanner) && *scanner->curptr == ',')
- pj_scan_get_char(scanner);
- else
- break;
- }
-}
-
-static void parse_pgp_challenge( pj_scanner *scanner, pj_pool_t *pool,
- pjsip_pgp_challenge *chal)
-{
- PJ_UNUSED_ARG(scanner);
- PJ_UNUSED_ARG(pool);
- PJ_UNUSED_ARG(chal);
-
- PJ_THROW(PJSIP_SYN_ERR_EXCEPTION);
-}
-
-static void int_parse_hdr_authorization( pj_scanner *scanner, pj_pool_t *pool,
- pjsip_authorization_hdr *hdr)
-{
- if (*scanner->curptr == '"') {
- pj_scan_get_quote(scanner, '"', '"', &hdr->scheme);
- hdr->scheme.ptr++;
- hdr->scheme.slen -= 2;
- } else {
- pj_scan_get(scanner, &pjsip_TOKEN_SPEC, &hdr->scheme);
- }
-
- if (!pj_stricmp(&hdr->scheme, &pjsip_DIGEST_STR)) {
-
- parse_digest_credential(scanner, pool, &hdr->credential.digest);
-
- } else if (!pj_stricmp(&hdr->scheme, &pjsip_PGP_STR)) {
-
- parse_pgp_credential( scanner, pool, &hdr->credential.pgp);
-
- } else {
- PJ_THROW(PJSIP_SYN_ERR_EXCEPTION);
- }
-
- pjsip_parse_end_hdr_imp( scanner );
-}
-
-static void int_parse_hdr_authenticate( pj_scanner *scanner, pj_pool_t *pool,
- pjsip_www_authenticate_hdr *hdr)
-{
- if (*scanner->curptr == '"') {
- pj_scan_get_quote(scanner, '"', '"', &hdr->scheme);
- hdr->scheme.ptr++;
- hdr->scheme.slen -= 2;
- } else {
- pj_scan_get(scanner, &pjsip_TOKEN_SPEC, &hdr->scheme);
- }
-
- if (!pj_stricmp(&hdr->scheme, &pjsip_DIGEST_STR)) {
-
- parse_digest_challenge(scanner, pool, &hdr->challenge.digest);
-
- } else if (!pj_stricmp(&hdr->scheme, &pjsip_PGP_STR)) {
-
- parse_pgp_challenge(scanner, pool, &hdr->challenge.pgp);
-
- } else {
- PJ_THROW(PJSIP_SYN_ERR_EXCEPTION);
- }
-
- pjsip_parse_end_hdr_imp( scanner );
-}
-
-
-static pjsip_hdr* parse_hdr_authorization( pjsip_parse_ctx *ctx )
-{
- pjsip_authorization_hdr *hdr = pjsip_authorization_hdr_create(ctx->pool);
- int_parse_hdr_authorization(ctx->scanner, ctx->pool, hdr);
- return (pjsip_hdr*)hdr;
-}
-
-static pjsip_hdr* parse_hdr_proxy_authorization( pjsip_parse_ctx *ctx )
-{
- pjsip_proxy_authorization_hdr *hdr =
- pjsip_proxy_authorization_hdr_create(ctx->pool);
- int_parse_hdr_authorization(ctx->scanner, ctx->pool, hdr);
- return (pjsip_hdr*)hdr;
-}
-
-static pjsip_hdr* parse_hdr_www_authenticate( pjsip_parse_ctx *ctx )
-{
- pjsip_www_authenticate_hdr *hdr =
- pjsip_www_authenticate_hdr_create(ctx->pool);
- int_parse_hdr_authenticate(ctx->scanner, ctx->pool, hdr);
- return (pjsip_hdr*)hdr;
-}
-
-static pjsip_hdr* parse_hdr_proxy_authenticate( pjsip_parse_ctx *ctx )
-{
- pjsip_proxy_authenticate_hdr *hdr =
- pjsip_proxy_authenticate_hdr_create(ctx->pool);
- int_parse_hdr_authenticate(ctx->scanner, ctx->pool, hdr);
- return (pjsip_hdr*)hdr;
-}
-
-
-PJ_DEF(pj_status_t) pjsip_auth_init_parser()
-{
- pj_status_t status;
-
- status = pjsip_register_hdr_parser( "Authorization", NULL,
- &parse_hdr_authorization);
- PJ_ASSERT_RETURN(status==PJ_SUCCESS, status);
- status = pjsip_register_hdr_parser( "Proxy-Authorization", NULL,
- &parse_hdr_proxy_authorization);
- PJ_ASSERT_RETURN(status==PJ_SUCCESS, status);
- status = pjsip_register_hdr_parser( "WWW-Authenticate", NULL,
- &parse_hdr_www_authenticate);
- PJ_ASSERT_RETURN(status==PJ_SUCCESS, status);
- status = pjsip_register_hdr_parser( "Proxy-Authenticate", NULL,
- &parse_hdr_proxy_authenticate);
- PJ_ASSERT_RETURN(status==PJ_SUCCESS, status);
-
- return PJ_SUCCESS;
-}
-
-PJ_DEF(void) pjsip_auth_deinit_parser()
-{
-}
-
+/* $Id$ */
+/*
+ * Copyright (C) 2003-2006 Benny Prijono <benny@prijono.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#include <pjsip/sip_auth_parser.h>
+#include <pjsip/sip_auth_msg.h>
+#include <pjsip/sip_parser.h>
+#include <pj/assert.h>
+#include <pj/string.h>
+#include <pj/except.h>
+#include <pj/pool.h>
+
+static pjsip_hdr* parse_hdr_authorization ( pjsip_parse_ctx *ctx );
+static pjsip_hdr* parse_hdr_proxy_authorization ( pjsip_parse_ctx *ctx );
+static pjsip_hdr* parse_hdr_www_authenticate ( pjsip_parse_ctx *ctx );
+static pjsip_hdr* parse_hdr_proxy_authenticate ( pjsip_parse_ctx *ctx );
+
+static void parse_digest_credential ( pj_scanner *scanner, pj_pool_t *pool,
+ pjsip_digest_credential *cred);
+static void parse_pgp_credential ( pj_scanner *scanner, pj_pool_t *pool,
+ pjsip_pgp_credential *cred);
+static void parse_digest_challenge ( pj_scanner *scanner, pj_pool_t *pool,
+ pjsip_digest_challenge *chal);
+static void parse_pgp_challenge ( pj_scanner *scanner, pj_pool_t *pool,
+ pjsip_pgp_challenge *chal);
+
+const pj_str_t pjsip_USERNAME_STR = { "username", 8 },
+ pjsip_REALM_STR = { "realm", 5},
+ pjsip_NONCE_STR = { "nonce", 5},
+ pjsip_URI_STR = { "uri", 3 },
+ pjsip_RESPONSE_STR = { "response", 8 },
+ pjsip_ALGORITHM_STR = { "algorithm", 9 },
+ pjsip_DOMAIN_STR = { "domain", 6 },
+ pjsip_STALE_STR = { "stale", 5},
+ pjsip_QOP_STR = { "qop", 3},
+ pjsip_CNONCE_STR = { "cnonce", 6},
+ pjsip_OPAQUE_STR = { "opaque", 6},
+ pjsip_NC_STR = { "nc", 2},
+ pjsip_TRUE_STR = { "true", 4},
+ pjsip_QUOTED_TRUE_STR = { "\"true\"", 6},
+ pjsip_FALSE_STR = { "false", 5},
+ pjsip_QUOTED_FALSE_STR = { "\"false\"", 7},
+ pjsip_DIGEST_STR = { "Digest", 6},
+ pjsip_QUOTED_DIGEST_STR = { "\"Digest\"", 8},
+ pjsip_PGP_STR = { "PGP", 3 },
+ pjsip_QUOTED_PGP_STR = { "\"PGP\"", 5 },
+ pjsip_MD5_STR = { "md5", 3 },
+ pjsip_QUOTED_MD5_STR = { "\"md5\"", 5},
+ pjsip_AUTH_STR = { "auth", 4},
+ pjsip_QUOTED_AUTH_STR = { "\"auth\"", 6 };
+
+
+static void parse_digest_credential( pj_scanner *scanner, pj_pool_t *pool,
+ pjsip_digest_credential *cred)
+{
+ pj_list_init(&cred->other_param);
+
+ for (;;) {
+ pj_str_t name, value;
+
+ pjsip_parse_param_imp(scanner, pool, &name, &value,
+ PJSIP_PARSE_REMOVE_QUOTE);
+
+ if (!pj_stricmp(&name, &pjsip_USERNAME_STR)) {
+ cred->username = value;
+
+ } else if (!pj_stricmp(&name, &pjsip_REALM_STR)) {
+ cred->realm = value;
+
+ } else if (!pj_stricmp(&name, &pjsip_NONCE_STR)) {
+ cred->nonce = value;
+
+ } else if (!pj_stricmp(&name, &pjsip_URI_STR)) {
+ cred->uri = value;
+
+ } else if (!pj_stricmp(&name, &pjsip_RESPONSE_STR)) {
+ cred->response = value;
+
+ } else if (!pj_stricmp(&name, &pjsip_ALGORITHM_STR)) {
+ cred->algorithm = value;
+
+ } else if (!pj_stricmp(&name, &pjsip_CNONCE_STR)) {
+ cred->cnonce = value;
+
+ } else if (!pj_stricmp(&name, &pjsip_OPAQUE_STR)) {
+ cred->opaque = value;
+
+ } else if (!pj_stricmp(&name, &pjsip_QOP_STR)) {
+ cred->qop = value;
+
+ } else if (!pj_stricmp(&name, &pjsip_NC_STR)) {
+ cred->nc = value;
+
+ } else {
+ pjsip_param *p = pj_pool_alloc(pool, sizeof(pjsip_param));
+ p->name = name;
+ p->value = value;
+ pj_list_insert_before(&cred->other_param, p);
+ }
+
+ /* Eat comma */
+ if (!pj_scan_is_eof(scanner) && *scanner->curptr == ',')
+ pj_scan_get_char(scanner);
+ else
+ break;
+ }
+}
+
+static void parse_pgp_credential( pj_scanner *scanner, pj_pool_t *pool,
+ pjsip_pgp_credential *cred)
+{
+ PJ_UNUSED_ARG(scanner);
+ PJ_UNUSED_ARG(pool);
+ PJ_UNUSED_ARG(cred);
+
+ PJ_THROW(PJSIP_SYN_ERR_EXCEPTION);
+}
+
+static void parse_digest_challenge( pj_scanner *scanner, pj_pool_t *pool,
+ pjsip_digest_challenge *chal)
+{
+ pj_list_init(&chal->other_param);
+
+ for (;;) {
+ pj_str_t name, value;
+
+ pjsip_parse_param_imp(scanner, pool, &name, &value,
+ PJSIP_PARSE_REMOVE_QUOTE);
+
+ if (!pj_stricmp(&name, &pjsip_REALM_STR)) {
+ chal->realm = value;
+
+ } else if (!pj_stricmp(&name, &pjsip_DOMAIN_STR)) {
+ chal->domain = value;
+
+ } else if (!pj_stricmp(&name, &pjsip_NONCE_STR)) {
+ chal->nonce = value;
+
+ } else if (!pj_stricmp(&name, &pjsip_OPAQUE_STR)) {
+ chal->opaque = value;
+
+ } else if (!pj_stricmp(&name, &pjsip_STALE_STR)) {
+ if (!pj_stricmp(&value, &pjsip_TRUE_STR) ||
+ !pj_stricmp(&value, &pjsip_QUOTED_TRUE_STR))
+ {
+ chal->stale = 1;
+ }
+
+ } else if (!pj_stricmp(&name, &pjsip_ALGORITHM_STR)) {
+ chal->algorithm = value;
+
+
+ } else if (!pj_stricmp(&name, &pjsip_QOP_STR)) {
+ chal->qop = value;
+
+ } else {
+ pjsip_param *p = pj_pool_alloc(pool, sizeof(pjsip_param));
+ p->name = name;
+ p->value = value;
+ pj_list_insert_before(&chal->other_param, p);
+ }
+
+ /* Eat comma */
+ if (!pj_scan_is_eof(scanner) && *scanner->curptr == ',')
+ pj_scan_get_char(scanner);
+ else
+ break;
+ }
+}
+
+static void parse_pgp_challenge( pj_scanner *scanner, pj_pool_t *pool,
+ pjsip_pgp_challenge *chal)
+{
+ PJ_UNUSED_ARG(scanner);
+ PJ_UNUSED_ARG(pool);
+ PJ_UNUSED_ARG(chal);
+
+ PJ_THROW(PJSIP_SYN_ERR_EXCEPTION);
+}
+
+static void int_parse_hdr_authorization( pj_scanner *scanner, pj_pool_t *pool,
+ pjsip_authorization_hdr *hdr)
+{
+ if (*scanner->curptr == '"') {
+ pj_scan_get_quote(scanner, '"', '"', &hdr->scheme);
+ hdr->scheme.ptr++;
+ hdr->scheme.slen -= 2;
+ } else {
+ pj_scan_get(scanner, &pjsip_TOKEN_SPEC, &hdr->scheme);
+ }
+
+ if (!pj_stricmp(&hdr->scheme, &pjsip_DIGEST_STR)) {
+
+ parse_digest_credential(scanner, pool, &hdr->credential.digest);
+
+ } else if (!pj_stricmp(&hdr->scheme, &pjsip_PGP_STR)) {
+
+ parse_pgp_credential( scanner, pool, &hdr->credential.pgp);
+
+ } else {
+ PJ_THROW(PJSIP_SYN_ERR_EXCEPTION);
+ }
+
+ pjsip_parse_end_hdr_imp( scanner );
+}
+
+static void int_parse_hdr_authenticate( pj_scanner *scanner, pj_pool_t *pool,
+ pjsip_www_authenticate_hdr *hdr)
+{
+ if (*scanner->curptr == '"') {
+ pj_scan_get_quote(scanner, '"', '"', &hdr->scheme);
+ hdr->scheme.ptr++;
+ hdr->scheme.slen -= 2;
+ } else {
+ pj_scan_get(scanner, &pjsip_TOKEN_SPEC, &hdr->scheme);
+ }
+
+ if (!pj_stricmp(&hdr->scheme, &pjsip_DIGEST_STR)) {
+
+ parse_digest_challenge(scanner, pool, &hdr->challenge.digest);
+
+ } else if (!pj_stricmp(&hdr->scheme, &pjsip_PGP_STR)) {
+
+ parse_pgp_challenge(scanner, pool, &hdr->challenge.pgp);
+
+ } else {
+ PJ_THROW(PJSIP_SYN_ERR_EXCEPTION);
+ }
+
+ pjsip_parse_end_hdr_imp( scanner );
+}
+
+
+static pjsip_hdr* parse_hdr_authorization( pjsip_parse_ctx *ctx )
+{
+ pjsip_authorization_hdr *hdr = pjsip_authorization_hdr_create(ctx->pool);
+ int_parse_hdr_authorization(ctx->scanner, ctx->pool, hdr);
+ return (pjsip_hdr*)hdr;
+}
+
+static pjsip_hdr* parse_hdr_proxy_authorization( pjsip_parse_ctx *ctx )
+{
+ pjsip_proxy_authorization_hdr *hdr =
+ pjsip_proxy_authorization_hdr_create(ctx->pool);
+ int_parse_hdr_authorization(ctx->scanner, ctx->pool, hdr);
+ return (pjsip_hdr*)hdr;
+}
+
+static pjsip_hdr* parse_hdr_www_authenticate( pjsip_parse_ctx *ctx )
+{
+ pjsip_www_authenticate_hdr *hdr =
+ pjsip_www_authenticate_hdr_create(ctx->pool);
+ int_parse_hdr_authenticate(ctx->scanner, ctx->pool, hdr);
+ return (pjsip_hdr*)hdr;
+}
+
+static pjsip_hdr* parse_hdr_proxy_authenticate( pjsip_parse_ctx *ctx )
+{
+ pjsip_proxy_authenticate_hdr *hdr =
+ pjsip_proxy_authenticate_hdr_create(ctx->pool);
+ int_parse_hdr_authenticate(ctx->scanner, ctx->pool, hdr);
+ return (pjsip_hdr*)hdr;
+}
+
+
+PJ_DEF(pj_status_t) pjsip_auth_init_parser()
+{
+ pj_status_t status;
+
+ status = pjsip_register_hdr_parser( "Authorization", NULL,
+ &parse_hdr_authorization);
+ PJ_ASSERT_RETURN(status==PJ_SUCCESS, status);
+ status = pjsip_register_hdr_parser( "Proxy-Authorization", NULL,
+ &parse_hdr_proxy_authorization);
+ PJ_ASSERT_RETURN(status==PJ_SUCCESS, status);
+ status = pjsip_register_hdr_parser( "WWW-Authenticate", NULL,
+ &parse_hdr_www_authenticate);
+ PJ_ASSERT_RETURN(status==PJ_SUCCESS, status);
+ status = pjsip_register_hdr_parser( "Proxy-Authenticate", NULL,
+ &parse_hdr_proxy_authenticate);
+ PJ_ASSERT_RETURN(status==PJ_SUCCESS, status);
+
+ return PJ_SUCCESS;
+}
+
+PJ_DEF(void) pjsip_auth_deinit_parser()
+{
+}
+
diff --git a/pjsip/src/pjsip/sip_endpoint.c b/pjsip/src/pjsip/sip_endpoint.c
index d68d35d0..53311f36 100644
--- a/pjsip/src/pjsip/sip_endpoint.c
+++ b/pjsip/src/pjsip/sip_endpoint.c
@@ -1,1112 +1,1112 @@
-/* $Id$ */
-/*
- * Copyright (C) 2003-2006 Benny Prijono <benny@prijono.org>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
-#include <pjsip/sip_endpoint.h>
-#include <pjsip/sip_transaction.h>
-#include <pjsip/sip_private.h>
-#include <pjsip/sip_event.h>
-#include <pjsip/sip_resolve.h>
-#include <pjsip/sip_module.h>
-#include <pjsip/sip_util.h>
-#include <pjsip/sip_errno.h>
-#include <pj/except.h>
-#include <pj/log.h>
-#include <pj/string.h>
-#include <pj/os.h>
-#include <pj/pool.h>
-#include <pj/hash.h>
-#include <pj/assert.h>
-#include <pj/errno.h>
-#include <pj/lock.h>
-
-#define PJSIP_EX_NO_MEMORY PJ_NO_MEMORY_EXCEPTION
-#define THIS_FILE "endpoint"
-
-#define MAX_METHODS 32
-
-/**
- * The SIP endpoint.
- */
-struct pjsip_endpoint
-{
- /** Pool to allocate memory for the endpoint. */
- pj_pool_t *pool;
-
- /** Mutex for the pool, hash table, and event list/queue. */
- pj_mutex_t *mutex;
-
- /** Pool factory. */
- pj_pool_factory *pf;
-
- /** Name. */
- pj_str_t name;
-
- /** Transaction table. */
- pj_hash_table_t *tsx_table;
-
- /** Mutex for transaction table. */
- pj_mutex_t *tsx_table_mutex;
-
- /** Timer heap. */
- pj_timer_heap_t *timer_heap;
-
- /** Transport manager. */
- pjsip_tpmgr *transport_mgr;
-
- /** Ioqueue. */
- pj_ioqueue_t *ioqueue;
-
- /** DNS Resolver. */
- pjsip_resolver_t *resolver;
-
- /** Number of modules registered. */
- pj_uint32_t mod_count;
-
- /** Modules. */
- pjsip_module *modules[PJSIP_MAX_MODULE];
-
- /** Number of supported methods. */
- unsigned method_cnt;
-
- /** Array of supported methods. */
- const pjsip_method *methods[MAX_METHODS];
-
- /** Allow header. */
- pjsip_allow_hdr *allow_hdr;
-
- /** Route header list. */
- pjsip_route_hdr route_hdr_list;
-
- /** Additional request headers. */
- pjsip_hdr req_hdr;
-};
-
-
-
-/*
- * Prototypes.
- */
-static void endpt_transport_callback(pjsip_endpoint*,
- pj_status_t, pjsip_rx_data*);
-
-
-/*
- * This is the global handler for memory allocation failure, for pools that
- * are created by the endpoint (by default, all pools ARE allocated by
- * endpoint). The error is handled by throwing exception, and hopefully,
- * the exception will be handled by the application (or this library).
- */
-static void pool_callback( pj_pool_t *pool, pj_size_t size )
-{
- PJ_UNUSED_ARG(pool);
- PJ_UNUSED_ARG(size);
-
- PJ_THROW(PJSIP_EX_NO_MEMORY);
-}
-
-
-/*
- * Initialize modules.
- */
-static pj_status_t init_modules( pjsip_endpoint *endpt )
-{
- pj_status_t status;
- unsigned i;
- //pj_str_t str_COMMA = { ", ", 2 };
- extern pjsip_module aux_tsx_module;
-
- PJ_LOG(5, (THIS_FILE, "init_modules()"));
-
- /* Load static modules. */
- endpt->mod_count = PJSIP_MAX_MODULE;
- status = register_static_modules( &endpt->mod_count, endpt->modules );
- if (status != 0) {
- return status;
- }
-
- /* Add mini aux module. */
- endpt->modules[endpt->mod_count++] = &aux_tsx_module;
-
- /* Load dynamic modules. */
- // Not supported yet!
-
- /* Sort modules on the priority. */
- for (i=endpt->mod_count-1; i>0; --i) {
- pj_uint32_t max = 0;
- unsigned j;
- for (j=1; j<=i; ++j) {
- if (endpt->modules[j]->priority > endpt->modules[max]->priority)
- max = j;
- }
- if (max != i) {
- pjsip_module *temp = endpt->modules[max];
- endpt->modules[max] = endpt->modules[i];
- endpt->modules[i] = temp;
- }
- }
-
- /* Initialize each module. */
- for (i=0; i < endpt->mod_count; ++i) {
- int j;
-
- pjsip_module *mod = endpt->modules[i];
- if (mod->init_module) {
- status = mod->init_module(endpt, mod, i);
- if (status != 0) {
- return status;
- }
- }
-
- /* Collect all supported methods from modules. */
- for (j=0; j<mod->method_cnt; ++j) {
- unsigned k;
- for (k=0; k<endpt->method_cnt; ++k) {
- if (pjsip_method_cmp(mod->methods[j], endpt->methods[k]) == 0)
- break;
- }
- if (k == endpt->method_cnt) {
- if (endpt->method_cnt < MAX_METHODS) {
- endpt->methods[endpt->method_cnt++] = mod->methods[j];
- } else {
- PJ_LOG(1,(THIS_FILE, "Too many methods"));
- return -1;
- }
- }
- }
- }
-
- /* Create Allow header. */
- endpt->allow_hdr = pjsip_allow_hdr_create( endpt->pool );
- endpt->allow_hdr->count = endpt->method_cnt;
- for (i=0; i<endpt->method_cnt; ++i) {
- endpt->allow_hdr->values[i] = endpt->methods[i]->name;
- }
-
- /* Start each module. */
- for (i=0; i < endpt->mod_count; ++i) {
- pjsip_module *mod = endpt->modules[i];
- if (mod->start_module) {
- status = mod->start_module(mod);
- if (status != 0) {
- return status;
- }
- }
- }
-
- /* Done. */
- return 0;
-}
-
-/*
- * Unregister the transaction from the hash table, and destroy the resources
- * from the transaction.
- */
-PJ_DEF(void) pjsip_endpt_destroy_tsx( pjsip_endpoint *endpt,
- pjsip_transaction *tsx)
-{
- PJ_LOG(5, (THIS_FILE, "pjsip_endpt_destroy_tsx(%s)", tsx->obj_name));
-
- pj_assert(tsx->state == PJSIP_TSX_STATE_DESTROYED);
-
- /* No need to lock transaction.
- * This function typically is called from the transaction callback, which
- * means that transaction mutex is being held.
- */
- pj_assert( pj_mutex_is_locked(tsx->mutex) );
-
- /* Lock endpoint. */
- pj_mutex_lock( endpt->tsx_table_mutex );
-
- /* Unregister from the hash table. */
- pj_hash_set( NULL, endpt->tsx_table, tsx->transaction_key.ptr,
- tsx->transaction_key.slen, NULL);
-
- /* Unlock endpoint mutex. */
- pj_mutex_unlock( endpt->tsx_table_mutex );
-
- /* Destroy transaction mutex. */
- pj_mutex_destroy( tsx->mutex );
-
- /* Release the pool for the transaction. */
- pj_pool_release(tsx->pool);
-
- PJ_LOG(4, (THIS_FILE, "tsx%p destroyed", tsx));
-}
-
-
-/*
- * Receive transaction events from transactions and dispatch them to the
- * modules.
- */
-static void endpt_do_event( pjsip_endpoint *endpt, pjsip_event *evt)
-{
- unsigned i;
-
- /* Dispatch event to modules. */
- for (i=0; i<endpt->mod_count; ++i) {
- pjsip_module *mod = endpt->modules[i];
- if (mod && mod->tsx_handler) {
- mod->tsx_handler( mod, evt );
- }
- }
-
- /* Destroy transaction if it is terminated. */
- if (evt->type == PJSIP_EVENT_TSX_STATE &&
- evt->body.tsx_state.tsx->state == PJSIP_TSX_STATE_DESTROYED)
- {
- /* No need to lock mutex. Mutex is locked inside the destroy function */
- pjsip_endpt_destroy_tsx( endpt, evt->body.tsx_state.tsx );
- }
-}
-
-/*
- * Receive transaction events from transactions and put in the event queue
- * to be processed later.
- */
-void pjsip_endpt_send_tsx_event( pjsip_endpoint *endpt, pjsip_event *evt )
-{
- // Need to protect this with try/catch?
- endpt_do_event(endpt, evt);
-}
-
-/*
- * Get "Allow" header.
- */
-PJ_DEF(const pjsip_allow_hdr*) pjsip_endpt_get_allow_hdr( pjsip_endpoint *endpt )
-{
- return endpt->allow_hdr;
-}
-
-/*
- * Get additional headers to be put in outgoing request message.
- */
-PJ_DEF(const pjsip_hdr*) pjsip_endpt_get_request_headers(pjsip_endpoint *endpt)
-{
- return &endpt->req_hdr;
-}
-
-PJ_DEF(pj_status_t) pjsip_endpt_set_proxies( pjsip_endpoint *endpt,
- int url_cnt, const pj_str_t url[])
-{
- int i;
- pjsip_route_hdr *hdr;
- pj_str_t str_ROUTE = { "Route", 5 };
-
- /* Lock endpoint mutex. */
- pj_mutex_lock(endpt->mutex);
-
- pj_list_init(&endpt->route_hdr_list);
-
- for (i=0; i<url_cnt; ++i) {
- int len = url[i].slen;
- char *dup = pj_pool_alloc(endpt->pool, len + 1);
- pj_memcpy(dup, url[i].ptr, len);
- dup[len] = '\0';
-
- hdr = pjsip_parse_hdr(endpt->pool, &str_ROUTE, dup, len, NULL);
- if (!hdr) {
- pj_mutex_unlock(endpt->mutex);
- PJ_LOG(4,(THIS_FILE, "Invalid URL %s in proxy URL", dup));
- return -1;
- }
-
- pj_assert(hdr->type == PJSIP_H_ROUTE);
- pj_list_insert_before(&endpt->route_hdr_list, hdr);
- }
-
- /* Unlock endpoint mutex. */
- pj_mutex_unlock(endpt->mutex);
-
- return 0;
-}
-
-/*
- * Get "Route" header list.
- */
-PJ_DEF(const pjsip_route_hdr*) pjsip_endpt_get_routing( pjsip_endpoint *endpt )
-{
- return &endpt->route_hdr_list;
-}
-
-
-/*
- * Initialize endpoint.
- */
-PJ_DEF(pj_status_t) pjsip_endpt_create(pj_pool_factory *pf,
- const char *name,
- pjsip_endpoint **p_endpt)
-{
- pj_status_t status;
- pj_pool_t *pool;
- pjsip_endpoint *endpt;
- pjsip_max_forwards_hdr *mf_hdr;
- pj_lock_t *lock = NULL;
-
- PJ_LOG(5, (THIS_FILE, "pjsip_endpt_create()"));
-
- *p_endpt = NULL;
-
- /* Create pool */
- pool = pj_pool_create(pf, "pept%p",
- PJSIP_POOL_LEN_ENDPT, PJSIP_POOL_INC_ENDPT,
- &pool_callback);
- if (!pool)
- return PJ_ENOMEM;
-
- /* Create endpoint. */
- endpt = pj_pool_calloc(pool, 1, sizeof(*endpt));
- endpt->pool = pool;
- endpt->pf = pf;
-
- /* Get name. */
- if (name != NULL) {
- pj_str_t temp;
- pj_strdup_with_null(endpt->pool, &endpt->name, pj_cstr(&temp, name));
- } else {
- pj_strdup_with_null(endpt->pool, &endpt->name, pj_gethostname());
- }
-
- /* Create mutex for the events, etc. */
- status = pj_mutex_create_recursive( endpt->pool, "ept%p", &endpt->mutex );
- if (status != PJ_SUCCESS) {
- goto on_error;
- }
-
- /* Create mutex for the transaction table. */
- status = pj_mutex_create_recursive( endpt->pool, "mtbl%p",
- &endpt->tsx_table_mutex);
- if (status != PJ_SUCCESS) {
- goto on_error;
- }
-
- /* Create hash table for transaction. */
- endpt->tsx_table = pj_hash_create( endpt->pool, PJSIP_MAX_TSX_COUNT );
- if (!endpt->tsx_table) {
- status = PJ_ENOMEM;
- goto on_error;
- }
-
- /* Create timer heap to manage all timers within this endpoint. */
- status = pj_timer_heap_create( endpt->pool, PJSIP_MAX_TIMER_COUNT,
- &endpt->timer_heap);
- if (status != PJ_SUCCESS) {
- goto on_error;
- }
-
- /* Set recursive lock for the timer heap. */
- status = pj_lock_create_recursive_mutex( endpt->pool, "edpt%p", &lock);
- if (status != PJ_SUCCESS) {
- goto on_error;
- }
- pj_timer_heap_set_lock(endpt->timer_heap, lock, PJ_TRUE);
-
- /* Set maximum timed out entries to process in a single poll. */
- pj_timer_heap_set_max_timed_out_per_poll(endpt->timer_heap,
- PJSIP_MAX_TIMED_OUT_ENTRIES);
-
- /* Create ioqueue. */
- status = pj_ioqueue_create( endpt->pool, PJSIP_MAX_TRANSPORTS, &endpt->ioqueue);
- if (status != PJ_SUCCESS) {
- goto on_error;
- }
-
- /* Create transport manager. */
- status = pjsip_tpmgr_create( endpt->pool, endpt,
- &endpt_transport_callback,
- &endpt->transport_mgr);
- if (status != PJ_SUCCESS) {
- goto on_error;
- }
-
- /* Create asynchronous DNS resolver. */
- endpt->resolver = pjsip_resolver_create(endpt->pool);
- if (!endpt->resolver) {
- PJ_LOG(4, (THIS_FILE, "pjsip_endpt_init(): error creating resolver"));
- goto on_error;
- }
-
- /* Initialize TLS ID for transaction lock. */
- status = pj_thread_local_alloc(&pjsip_tsx_lock_tls_id);
- if (status != PJ_SUCCESS) {
- goto on_error;
- }
- pj_thread_local_set(pjsip_tsx_lock_tls_id, NULL);
-
- /* Initialize request headers. */
- pj_list_init(&endpt->req_hdr);
-
- /* Initialist "Route" header list. */
- pj_list_init(&endpt->route_hdr_list);
-
- /* Add "Max-Forwards" for request header. */
- mf_hdr = pjsip_max_forwards_hdr_create(endpt->pool);
- mf_hdr->ivalue = PJSIP_MAX_FORWARDS_VALUE;
- pj_list_insert_before( &endpt->req_hdr, mf_hdr);
-
- /* Load and init modules. */
- status = init_modules(endpt);
- if (status != PJ_SUCCESS) {
- PJ_LOG(4, (THIS_FILE, "pjsip_endpt_init(): error in init_modules()"));
- return status;
- }
-
- /* Done. */
- *p_endpt = endpt;
- return status;
-
-on_error:
- if (endpt->transport_mgr) {
- pjsip_tpmgr_destroy(endpt->transport_mgr);
- endpt->transport_mgr = NULL;
- }
- if (endpt->mutex) {
- pj_mutex_destroy(endpt->mutex);
- endpt->mutex = NULL;
- }
- if (endpt->tsx_table_mutex) {
- pj_mutex_destroy(endpt->tsx_table_mutex);
- endpt->tsx_table_mutex = NULL;
- }
- pj_pool_release( endpt->pool );
-
- PJ_LOG(4, (THIS_FILE, "pjsip_endpt_init() failed"));
- return status;
-}
-
-/*
- * Destroy endpoint.
- */
-PJ_DEF(void) pjsip_endpt_destroy(pjsip_endpoint *endpt)
-{
- PJ_LOG(5, (THIS_FILE, "pjsip_endpt_destroy()"));
-
- /* Shutdown and destroy all transports. */
- pjsip_tpmgr_destroy(endpt->transport_mgr);
-
- /* Delete endpoint mutex. */
- pj_mutex_destroy(endpt->mutex);
-
- /* Delete transaction table mutex. */
- pj_mutex_destroy(endpt->tsx_table_mutex);
-
- /* Finally destroy pool. */
- pj_pool_release(endpt->pool);
-}
-
-/*
- * Get endpoint name.
- */
-PJ_DEF(const pj_str_t*) pjsip_endpt_name(const pjsip_endpoint *endpt)
-{
- return &endpt->name;
-}
-
-
-/*
- * Create new pool.
- */
-PJ_DEF(pj_pool_t*) pjsip_endpt_create_pool( pjsip_endpoint *endpt,
- const char *pool_name,
- pj_size_t initial,
- pj_size_t increment )
-{
- pj_pool_t *pool;
-
- PJ_LOG(5, (THIS_FILE, "pjsip_endpt_create_pool()"));
-
- /* Lock endpoint mutex. */
- pj_mutex_lock(endpt->mutex);
-
- /* Create pool */
- pool = pj_pool_create( endpt->pf, pool_name,
- initial, increment, &pool_callback);
-
- /* Unlock mutex. */
- pj_mutex_unlock(endpt->mutex);
-
- if (pool) {
- PJ_LOG(5, (THIS_FILE, " pool %s created", pj_pool_getobjname(pool)));
- } else {
- PJ_LOG(4, (THIS_FILE, "Unable to create pool %s!", pool_name));
- }
-
- return pool;
-}
-
-/*
- * Return back pool to endpoint's pool manager to be either destroyed or
- * recycled.
- */
-PJ_DEF(void) pjsip_endpt_destroy_pool( pjsip_endpoint *endpt, pj_pool_t *pool )
-{
- PJ_LOG(5, (THIS_FILE, "pjsip_endpt_destroy_pool(%s)", pj_pool_getobjname(pool)));
-
- pj_mutex_lock(endpt->mutex);
- pj_pool_release( pool );
- pj_mutex_unlock(endpt->mutex);
-}
-
-/*
- * Handle events.
- */
-PJ_DEF(void) pjsip_endpt_handle_events( pjsip_endpoint *endpt,
- const pj_time_val *max_timeout)
-{
- /* timeout is 'out' var. This just to make compiler happy. */
- pj_time_val timeout = { 0, 0};
-
- PJ_LOG(5, (THIS_FILE, "pjsip_endpt_handle_events()"));
-
- /* Poll the timer. The timer heap has its own mutex for better
- * granularity, so we don't need to lock end endpoint.
- */
- timeout.sec = timeout.msec = 0;
- pj_timer_heap_poll( endpt->timer_heap, &timeout );
-
- /* If caller specifies maximum time to wait, then compare the value with
- * the timeout to wait from timer, and use the minimum value.
- */
- if (max_timeout && PJ_TIME_VAL_GT(timeout, *max_timeout)) {
- timeout = *max_timeout;
- }
-
- /* Poll ioqueue. */
- pj_ioqueue_poll( endpt->ioqueue, &timeout);
-}
-
-/*
- * Schedule timer.
- */
-PJ_DEF(pj_status_t) pjsip_endpt_schedule_timer( pjsip_endpoint *endpt,
- pj_timer_entry *entry,
- const pj_time_val *delay )
-{
- PJ_LOG(5, (THIS_FILE, "pjsip_endpt_schedule_timer(entry=%p, delay=%u.%u)",
- entry, delay->sec, delay->msec));
- return pj_timer_heap_schedule( endpt->timer_heap, entry, delay );
-}
-
-/*
- * Cancel the previously registered timer.
- */
-PJ_DEF(void) pjsip_endpt_cancel_timer( pjsip_endpoint *endpt,
- pj_timer_entry *entry )
-{
- PJ_LOG(5, (THIS_FILE, "pjsip_endpt_cancel_timer(entry=%p)", entry));
- pj_timer_heap_cancel( endpt->timer_heap, entry );
-}
-
-/*
- * Create a new transaction.
- * Endpoint must then initialize the new transaction as either UAS or UAC, and
- * register it to the hash table.
- */
-PJ_DEF(pj_status_t) pjsip_endpt_create_tsx(pjsip_endpoint *endpt,
- pjsip_transaction **p_tsx)
-{
- pj_pool_t *pool;
-
- PJ_ASSERT_RETURN(endpt && p_tsx, PJ_EINVAL);
-
- PJ_LOG(5, (THIS_FILE, "pjsip_endpt_create_tsx()"));
-
- /* Request one pool for the transaction. Mutex is locked there. */
- pool = pjsip_endpt_create_pool(endpt, "ptsx%p",
- PJSIP_POOL_LEN_TSX, PJSIP_POOL_INC_TSX);
- if (pool == NULL) {
- return PJ_ENOMEM;
- }
-
- /* Create the transaction. */
- return pjsip_tsx_create(pool, endpt, p_tsx);
-}
-
-/*
- * Register the transaction to the endpoint.
- * This will put the transaction to the transaction hash table. Before calling
- * this function, the transaction must be INITIALIZED as either UAS or UAC, so
- * that the transaction key is built.
- */
-PJ_DEF(void) pjsip_endpt_register_tsx( pjsip_endpoint *endpt,
- pjsip_transaction *tsx)
-{
- PJ_LOG(5, (THIS_FILE, "pjsip_endpt_register_tsx(%s)", tsx->obj_name));
-
- pj_assert(tsx->transaction_key.slen != 0);
- //pj_assert(tsx->state != PJSIP_TSX_STATE_NULL);
-
- /* Lock hash table mutex. */
- pj_mutex_lock(endpt->tsx_table_mutex);
-
- /* Register the transaction to the hash table. */
- pj_hash_set( tsx->pool, endpt->tsx_table, tsx->transaction_key.ptr,
- tsx->transaction_key.slen, tsx);
-
- /* Unlock mutex. */
- pj_mutex_unlock(endpt->tsx_table_mutex);
-}
-
-/*
- * Find transaction by the key.
- */
-PJ_DEF(pjsip_transaction*) pjsip_endpt_find_tsx( pjsip_endpoint *endpt,
- const pj_str_t *key )
-{
- pjsip_transaction *tsx;
-
- PJ_LOG(5, (THIS_FILE, "pjsip_endpt_find_tsx()"));
-
- /* Start lock mutex in the endpoint. */
- pj_mutex_lock(endpt->tsx_table_mutex);
-
- /* Find the transaction in the hash table. */
- tsx = pj_hash_get( endpt->tsx_table, key->ptr, key->slen );
-
- /* Unlock mutex. */
- pj_mutex_unlock(endpt->tsx_table_mutex);
-
- return tsx;
-}
-
-/*
- * Create key.
- */
-static void rdata_create_key( pjsip_rx_data *rdata)
-{
- pjsip_role_e role;
- if (rdata->msg_info.msg->type == PJSIP_REQUEST_MSG) {
- role = PJSIP_ROLE_UAS;
- } else {
- role = PJSIP_ROLE_UAC;
- }
- pjsip_tsx_create_key(rdata->tp_info.pool, &rdata->endpt_info.key, role,
- &rdata->msg_info.cseq->method, rdata);
-}
-
-/*
- * This is the callback that is called by the transport manager when it
- * receives a message from the network.
- */
-static void endpt_transport_callback( pjsip_endpoint *endpt,
- pj_status_t status,
- pjsip_rx_data *rdata )
-{
- pjsip_msg *msg = rdata->msg_info.msg;
- pjsip_transaction *tsx;
- pj_bool_t a_new_transaction_just_been_created = PJ_FALSE;
-
- PJ_LOG(5, (THIS_FILE, "endpt_transport_callback(rdata=%p)", rdata));
-
- if (status != PJ_SUCCESS) {
- const char *src_addr = pj_inet_ntoa(rdata->pkt_info.addr.sin_addr);
- int port = pj_ntohs(rdata->pkt_info.addr.sin_port);
- PJSIP_ENDPT_LOG_ERROR((endpt, "transport", status,
- "Src.addr=%s:%d, packet:--\n"
- "%s\n"
- "-- end of packet. Error",
- src_addr, port, rdata->msg_info.msg_buf));
- return;
- }
-
- /* For response, check that the value in Via sent-by match the transport.
- * If not matched, silently drop the response.
- * Ref: RFC3261 Section 18.1.2 Receiving Response
- */
- if (msg->type == PJSIP_RESPONSE_MSG) {
- const pj_sockaddr_in *addr;
- const char *addr_addr;
- int port = rdata->msg_info.via->sent_by.port;
- pj_bool_t mismatch = PJ_FALSE;
- if (port == 0) {
- int type;
- type = rdata->tp_info.transport->type;
- port = pjsip_transport_get_default_port_for_type(type);
- }
- addr = &rdata->tp_info.transport->public_addr;
- addr_addr = pj_inet_ntoa(addr->sin_addr);
- if (pj_strcmp2(&rdata->msg_info.via->sent_by.host, addr_addr) != 0)
- mismatch = PJ_TRUE;
- else if (port != pj_ntohs(addr->sin_port)) {
- /* Port or address mismatch, we should discard response */
- /* But we saw one implementation (we don't want to name it to
- * protect the innocence) which put wrong sent-by port although
- * the "rport" parameter is correct.
- * So we discard the response only if the port doesn't match
- * both the port in sent-by and rport. We try to be lenient here!
- */
- if (rdata->msg_info.via->rport_param != pj_sockaddr_in_get_port(addr))
- mismatch = PJ_TRUE;
- else {
- PJ_LOG(4,(THIS_FILE, "Response %p has mismatch port in sent-by"
- " but the rport parameter is correct",
- rdata));
- }
- }
-
- if (mismatch) {
- pjsip_event e;
-
- PJSIP_EVENT_INIT_DISCARD_MSG(e, rdata, PJSIP_EINVALIDVIA);
- endpt_do_event( endpt, &e );
- return;
- }
- }
-
- /* Create key for transaction lookup. */
- rdata_create_key( rdata);
-
- /* Find the transaction for the received message. */
- PJ_LOG(5, (THIS_FILE, "finding tsx with key=%.*s",
- rdata->endpt_info.key.slen, rdata->endpt_info.key.ptr));
-
- /* Start lock mutex in the endpoint. */
- pj_mutex_lock(endpt->tsx_table_mutex);
-
- /* Find the transaction in the hash table. */
- tsx = pj_hash_get( endpt->tsx_table, rdata->endpt_info.key.ptr, rdata->endpt_info.key.slen );
-
- /* Unlock mutex. */
- pj_mutex_unlock(endpt->tsx_table_mutex);
-
- /* If the transaction is not found... */
- if (tsx == NULL || tsx->state == PJSIP_TSX_STATE_TERMINATED) {
-
- /*
- * For response message, discard the message, except if the response is
- * an 2xx class response to INVITE, which in this case it must be
- * passed to TU to be acked.
- */
- if (msg->type == PJSIP_RESPONSE_MSG) {
-
- /* Inform TU about the 200 message, only if it's INVITE. */
- if (PJSIP_IS_STATUS_IN_CLASS(msg->line.status.code, 200) &&
- rdata->msg_info.cseq->method.id == PJSIP_INVITE_METHOD)
- {
- pjsip_event e;
-
- /* Should not happen for UA. Tsx theoritically lives until
- * all responses are absorbed.
- */
- pj_assert(0);
-
- PJSIP_EVENT_INIT_RX_200_MSG(e, rdata);
- endpt_do_event( endpt, &e );
-
- } else {
- /* Just discard the response, inform TU. */
- pjsip_event e;
-
- PJSIP_EVENT_INIT_DISCARD_MSG(e, rdata,
- PJSIP_ERRNO_FROM_SIP_STATUS(PJSIP_SC_CALL_TSX_DOES_NOT_EXIST));
- endpt_do_event( endpt, &e );
- }
-
- /*
- * For non-ACK request message, create a new transaction.
- */
- } else if (rdata->msg_info.msg->line.req.method.id != PJSIP_ACK_METHOD) {
-
- pj_status_t status;
-
- /* Create transaction, mutex is locked there. */
- status = pjsip_endpt_create_tsx(endpt, &tsx);
- if (status != PJ_SUCCESS) {
- PJSIP_ENDPT_LOG_ERROR((endpt, THIS_FILE, status,
- "Unable to create transaction"));
- return;
- }
-
- /* Initialize transaction as UAS. */
- pjsip_tsx_init_uas( tsx, rdata );
-
- /* Register transaction, mutex is locked there. */
- pjsip_endpt_register_tsx( endpt, tsx );
-
- a_new_transaction_just_been_created = PJ_TRUE;
- }
- }
-
- /* If transaction is found (or newly created), pass the message.
- * Otherwise if it's an ACK request, pass directly to TU.
- */
- if (tsx && tsx->state != PJSIP_TSX_STATE_TERMINATED) {
- /* Dispatch message to transaction. */
- pjsip_tsx_on_rx_msg( tsx, rdata );
-
- } else if (rdata->msg_info.msg->line.req.method.id == PJSIP_ACK_METHOD) {
- /*
- * This is an ACK message, but the INVITE transaction could not
- * be found (possibly because the branch parameter in Via in ACK msg
- * is different than the branch in original INVITE). This happens with
- * SER!
- */
- pjsip_event event;
-
- PJSIP_EVENT_INIT_RX_ACK_MSG(event,rdata);
- endpt_do_event( endpt, &event );
- }
-
- /*
- * If a new request message has just been receieved, but no modules
- * seem to be able to handle the request message, then terminate the
- * transaction.
- *
- * Ideally for cases like "unsupported method", we should be able to
- * answer the request statelessly. But we can not do that since the
- * endpoint shoule be able to be used as both user agent and proxy stack,
- * and a proxy stack should be able to handle arbitrary methods.
- */
- if (a_new_transaction_just_been_created && tsx->status_code < 100) {
- /* Certainly no modules has sent any response message.
- * Check that any modules has attached a module data.
- */
- int i;
- for (i=0; i<PJSIP_MAX_MODULE; ++i) {
- if (tsx->module_data[i] != NULL) {
- break;
- }
- }
- if (i == PJSIP_MAX_MODULE) {
- /* No modules have attached itself to the transaction.
- * Terminate the transaction with 501/Not Implemented.
- */
- pjsip_tx_data *tdata;
- pj_status_t status;
-
- if (tsx->method.id == PJSIP_OPTIONS_METHOD) {
- status = pjsip_endpt_create_response(endpt, rdata, 200,
- &tdata);
- } else {
- status = pjsip_endpt_create_response(endpt, rdata,
- PJSIP_SC_METHOD_NOT_ALLOWED,
- &tdata);
- }
-
- if (status != PJ_SUCCESS) {
- PJSIP_ENDPT_LOG_ERROR((endpt, THIS_FILE, status,
- "Unable to create response"));
- return;
- }
-
- if (endpt->allow_hdr) {
- pjsip_msg_add_hdr( tdata->msg,
- pjsip_hdr_shallow_clone(tdata->pool, endpt->allow_hdr));
- }
- pjsip_tsx_on_tx_msg( tsx, tdata );
-
- } else {
- /*
- * If a module has registered itself in the transaction but it
- * hasn't responded the request, chances are the module wouldn't
- * respond to the request at all. We terminate the request here
- * with 500/Internal Server Error, to be safe.
- */
- pjsip_tx_data *tdata;
- pj_status_t status;
-
- status = pjsip_endpt_create_response(endpt, rdata, 500, &tdata);
- if (status != PJ_SUCCESS) {
- PJSIP_ENDPT_LOG_ERROR((endpt, THIS_FILE, status,
- "Unable to create response"));
- return;
- }
-
- pjsip_tsx_on_tx_msg(tsx, tdata);
- }
- }
-}
-
-/*
- * Create transmit data buffer.
- */
-PJ_DEF(pj_status_t) pjsip_endpt_create_tdata( pjsip_endpoint *endpt,
- pjsip_tx_data **p_tdata)
-{
- PJ_LOG(5, (THIS_FILE, "pjsip_endpt_create_tdata()"));
- return pjsip_tx_data_create(endpt->transport_mgr, p_tdata);
-}
-
-/*
- * Resolve
- */
-PJ_DEF(void) pjsip_endpt_resolve( pjsip_endpoint *endpt,
- pj_pool_t *pool,
- pjsip_host_port *target,
- void *token,
- pjsip_resolver_callback *cb)
-{
- PJ_LOG(5, (THIS_FILE, "pjsip_endpt_resolve()"));
- pjsip_resolve( endpt->resolver, pool, target, token, cb);
-}
-
-/*
- * Get transport manager.
- */
-PJ_DEF(pjsip_tpmgr*) pjsip_endpt_get_tpmgr(pjsip_endpoint *endpt)
-{
- return endpt->transport_mgr;
-}
-
-/*
- * Get ioqueue instance.
- */
-PJ_DEF(pj_ioqueue_t*) pjsip_endpt_get_ioqueue(pjsip_endpoint *endpt)
-{
- return endpt->ioqueue;
-}
-
-/*
- * Find/create transport.
- */
-PJ_DEF(pj_status_t) pjsip_endpt_alloc_transport( pjsip_endpoint *endpt,
- pjsip_transport_type_e type,
- const pj_sockaddr_in *remote,
- pjsip_transport **p_transport)
-{
- PJ_LOG(5, (THIS_FILE, "pjsip_endpt_alloc_transport()"));
- return pjsip_tpmgr_alloc_transport( endpt->transport_mgr, type, remote,
- p_transport);
-}
-
-
-/*
- * Report error.
- */
-PJ_DEF(void) pjsip_endpt_log_error( pjsip_endpoint *endpt,
- const char *sender,
- pj_status_t error_code,
- const char *format,
- ... )
-{
- char newformat[256];
- int len;
- va_list marker;
-
- va_start(marker, format);
-
- PJ_UNUSED_ARG(endpt);
-
- len = pj_native_strlen(format);
- if (len < sizeof(newformat)-30) {
- pj_str_t errstr;
-
- pj_native_strcpy(newformat, format);
- pj_snprintf(newformat+len, sizeof(newformat)-len-1,
- ": [err %d] ", error_code);
- len += pj_native_strlen(newformat+len);
-
- errstr = pjsip_strerror(error_code, newformat+len,
- sizeof(newformat)-len-1);
-
- len += errstr.slen;
- newformat[len] = '\0';
-
- pj_log(sender, 1, newformat, marker);
- } else {
- pj_log(sender, 1, format, marker);
- }
-
- va_end(marker);
-}
-
-
-/*
- * Dump endpoint.
- */
-PJ_DEF(void) pjsip_endpt_dump( pjsip_endpoint *endpt, pj_bool_t detail )
-{
-#if PJ_LOG_MAX_LEVEL >= 3
- unsigned count;
-
- PJ_LOG(5, (THIS_FILE, "pjsip_endpt_dump()"));
-
- /* Lock mutex. */
- pj_mutex_lock(endpt->mutex);
-
- PJ_LOG(3, (THIS_FILE, "Dumping endpoint %p:", endpt));
-
- /* Dumping pool factory. */
- (*endpt->pf->dump_status)(endpt->pf, detail);
-
- /* Pool health. */
- PJ_LOG(3, (THIS_FILE," Endpoint pool capacity=%u, used_size=%u",
- pj_pool_get_capacity(endpt->pool),
- pj_pool_get_used_size(endpt->pool)));
-
- /* Transaction tables. */
- count = pj_hash_count(endpt->tsx_table);
- PJ_LOG(3, (THIS_FILE, " Number of transactions: %u", count));
-
- if (count && detail) {
- pj_hash_iterator_t it_val;
- pj_hash_iterator_t *it;
- pj_time_val now;
-
- PJ_LOG(3, (THIS_FILE, " Dumping transaction tables:"));
-
- pj_gettimeofday(&now);
- it = pj_hash_first(endpt->tsx_table, &it_val);
-
- while (it != NULL) {
- int timeout_diff;
-
- /* Get the transaction. No need to lock transaction's mutex
- * since we already hold endpoint mutex, so that no transactions
- * will be deleted.
- */
- pjsip_transaction *tsx = pj_hash_this(endpt->tsx_table, it);
-
- const char *role = (tsx->role == PJSIP_ROLE_UAS ? "UAS" : "UAC");
-
- if (tsx->timeout_timer._timer_id != -1) {
- if (tsx->timeout_timer._timer_value.sec > now.sec) {
- timeout_diff = tsx->timeout_timer._timer_value.sec - now.sec;
- } else {
- timeout_diff = now.sec - tsx->timeout_timer._timer_value.sec;
- timeout_diff = 0 - timeout_diff;
- }
- } else {
- timeout_diff = -1;
- }
-
- PJ_LOG(3, (THIS_FILE, " %s %s %10.*s %.9u %s t=%ds",
- tsx->obj_name, role,
- tsx->method.name.slen, tsx->method.name.ptr,
- tsx->cseq,
- pjsip_tsx_state_str(tsx->state),
- timeout_diff));
-
- it = pj_hash_next(endpt->tsx_table, it);
- }
- }
-
- /* Transports.
- */
- pjsip_tpmgr_dump_transports( endpt->transport_mgr );
-
- /* Timer. */
- PJ_LOG(3,(THIS_FILE, " Timer heap has %u entries",
- pj_timer_heap_count(endpt->timer_heap)));
-
- /* Unlock mutex. */
- pj_mutex_unlock(endpt->mutex);
-#else
- PJ_LOG(3,(THIS_FILE, "pjsip_end_dump: can't dump because it's disabled."));
-#endif
-}
-
+/* $Id$ */
+/*
+ * Copyright (C) 2003-2006 Benny Prijono <benny@prijono.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#include <pjsip/sip_endpoint.h>
+#include <pjsip/sip_transaction.h>
+#include <pjsip/sip_private.h>
+#include <pjsip/sip_event.h>
+#include <pjsip/sip_resolve.h>
+#include <pjsip/sip_module.h>
+#include <pjsip/sip_util.h>
+#include <pjsip/sip_errno.h>
+#include <pj/except.h>
+#include <pj/log.h>
+#include <pj/string.h>
+#include <pj/os.h>
+#include <pj/pool.h>
+#include <pj/hash.h>
+#include <pj/assert.h>
+#include <pj/errno.h>
+#include <pj/lock.h>
+
+#define PJSIP_EX_NO_MEMORY PJ_NO_MEMORY_EXCEPTION
+#define THIS_FILE "endpoint"
+
+#define MAX_METHODS 32
+
+/**
+ * The SIP endpoint.
+ */
+struct pjsip_endpoint
+{
+ /** Pool to allocate memory for the endpoint. */
+ pj_pool_t *pool;
+
+ /** Mutex for the pool, hash table, and event list/queue. */
+ pj_mutex_t *mutex;
+
+ /** Pool factory. */
+ pj_pool_factory *pf;
+
+ /** Name. */
+ pj_str_t name;
+
+ /** Transaction table. */
+ pj_hash_table_t *tsx_table;
+
+ /** Mutex for transaction table. */
+ pj_mutex_t *tsx_table_mutex;
+
+ /** Timer heap. */
+ pj_timer_heap_t *timer_heap;
+
+ /** Transport manager. */
+ pjsip_tpmgr *transport_mgr;
+
+ /** Ioqueue. */
+ pj_ioqueue_t *ioqueue;
+
+ /** DNS Resolver. */
+ pjsip_resolver_t *resolver;
+
+ /** Number of modules registered. */
+ pj_uint32_t mod_count;
+
+ /** Modules. */
+ pjsip_module *modules[PJSIP_MAX_MODULE];
+
+ /** Number of supported methods. */
+ unsigned method_cnt;
+
+ /** Array of supported methods. */
+ const pjsip_method *methods[MAX_METHODS];
+
+ /** Allow header. */
+ pjsip_allow_hdr *allow_hdr;
+
+ /** Route header list. */
+ pjsip_route_hdr route_hdr_list;
+
+ /** Additional request headers. */
+ pjsip_hdr req_hdr;
+};
+
+
+
+/*
+ * Prototypes.
+ */
+static void endpt_transport_callback(pjsip_endpoint*,
+ pj_status_t, pjsip_rx_data*);
+
+
+/*
+ * This is the global handler for memory allocation failure, for pools that
+ * are created by the endpoint (by default, all pools ARE allocated by
+ * endpoint). The error is handled by throwing exception, and hopefully,
+ * the exception will be handled by the application (or this library).
+ */
+static void pool_callback( pj_pool_t *pool, pj_size_t size )
+{
+ PJ_UNUSED_ARG(pool);
+ PJ_UNUSED_ARG(size);
+
+ PJ_THROW(PJSIP_EX_NO_MEMORY);
+}
+
+
+/*
+ * Initialize modules.
+ */
+static pj_status_t init_modules( pjsip_endpoint *endpt )
+{
+ pj_status_t status;
+ unsigned i;
+ //pj_str_t str_COMMA = { ", ", 2 };
+ extern pjsip_module aux_tsx_module;
+
+ PJ_LOG(5, (THIS_FILE, "init_modules()"));
+
+ /* Load static modules. */
+ endpt->mod_count = PJSIP_MAX_MODULE;
+ status = register_static_modules( &endpt->mod_count, endpt->modules );
+ if (status != 0) {
+ return status;
+ }
+
+ /* Add mini aux module. */
+ endpt->modules[endpt->mod_count++] = &aux_tsx_module;
+
+ /* Load dynamic modules. */
+ // Not supported yet!
+
+ /* Sort modules on the priority. */
+ for (i=endpt->mod_count-1; i>0; --i) {
+ pj_uint32_t max = 0;
+ unsigned j;
+ for (j=1; j<=i; ++j) {
+ if (endpt->modules[j]->priority > endpt->modules[max]->priority)
+ max = j;
+ }
+ if (max != i) {
+ pjsip_module *temp = endpt->modules[max];
+ endpt->modules[max] = endpt->modules[i];
+ endpt->modules[i] = temp;
+ }
+ }
+
+ /* Initialize each module. */
+ for (i=0; i < endpt->mod_count; ++i) {
+ int j;
+
+ pjsip_module *mod = endpt->modules[i];
+ if (mod->init_module) {
+ status = mod->init_module(endpt, mod, i);
+ if (status != 0) {
+ return status;
+ }
+ }
+
+ /* Collect all supported methods from modules. */
+ for (j=0; j<mod->method_cnt; ++j) {
+ unsigned k;
+ for (k=0; k<endpt->method_cnt; ++k) {
+ if (pjsip_method_cmp(mod->methods[j], endpt->methods[k]) == 0)
+ break;
+ }
+ if (k == endpt->method_cnt) {
+ if (endpt->method_cnt < MAX_METHODS) {
+ endpt->methods[endpt->method_cnt++] = mod->methods[j];
+ } else {
+ PJ_LOG(1,(THIS_FILE, "Too many methods"));
+ return -1;
+ }
+ }
+ }
+ }
+
+ /* Create Allow header. */
+ endpt->allow_hdr = pjsip_allow_hdr_create( endpt->pool );
+ endpt->allow_hdr->count = endpt->method_cnt;
+ for (i=0; i<endpt->method_cnt; ++i) {
+ endpt->allow_hdr->values[i] = endpt->methods[i]->name;
+ }
+
+ /* Start each module. */
+ for (i=0; i < endpt->mod_count; ++i) {
+ pjsip_module *mod = endpt->modules[i];
+ if (mod->start_module) {
+ status = mod->start_module(mod);
+ if (status != 0) {
+ return status;
+ }
+ }
+ }
+
+ /* Done. */
+ return 0;
+}
+
+/*
+ * Unregister the transaction from the hash table, and destroy the resources
+ * from the transaction.
+ */
+PJ_DEF(void) pjsip_endpt_destroy_tsx( pjsip_endpoint *endpt,
+ pjsip_transaction *tsx)
+{
+ PJ_LOG(5, (THIS_FILE, "pjsip_endpt_destroy_tsx(%s)", tsx->obj_name));
+
+ pj_assert(tsx->state == PJSIP_TSX_STATE_DESTROYED);
+
+ /* No need to lock transaction.
+ * This function typically is called from the transaction callback, which
+ * means that transaction mutex is being held.
+ */
+ pj_assert( pj_mutex_is_locked(tsx->mutex) );
+
+ /* Lock endpoint. */
+ pj_mutex_lock( endpt->tsx_table_mutex );
+
+ /* Unregister from the hash table. */
+ pj_hash_set( NULL, endpt->tsx_table, tsx->transaction_key.ptr,
+ tsx->transaction_key.slen, NULL);
+
+ /* Unlock endpoint mutex. */
+ pj_mutex_unlock( endpt->tsx_table_mutex );
+
+ /* Destroy transaction mutex. */
+ pj_mutex_destroy( tsx->mutex );
+
+ /* Release the pool for the transaction. */
+ pj_pool_release(tsx->pool);
+
+ PJ_LOG(4, (THIS_FILE, "tsx%p destroyed", tsx));
+}
+
+
+/*
+ * Receive transaction events from transactions and dispatch them to the
+ * modules.
+ */
+static void endpt_do_event( pjsip_endpoint *endpt, pjsip_event *evt)
+{
+ unsigned i;
+
+ /* Dispatch event to modules. */
+ for (i=0; i<endpt->mod_count; ++i) {
+ pjsip_module *mod = endpt->modules[i];
+ if (mod && mod->tsx_handler) {
+ mod->tsx_handler( mod, evt );
+ }
+ }
+
+ /* Destroy transaction if it is terminated. */
+ if (evt->type == PJSIP_EVENT_TSX_STATE &&
+ evt->body.tsx_state.tsx->state == PJSIP_TSX_STATE_DESTROYED)
+ {
+ /* No need to lock mutex. Mutex is locked inside the destroy function */
+ pjsip_endpt_destroy_tsx( endpt, evt->body.tsx_state.tsx );
+ }
+}
+
+/*
+ * Receive transaction events from transactions and put in the event queue
+ * to be processed later.
+ */
+void pjsip_endpt_send_tsx_event( pjsip_endpoint *endpt, pjsip_event *evt )
+{
+ // Need to protect this with try/catch?
+ endpt_do_event(endpt, evt);
+}
+
+/*
+ * Get "Allow" header.
+ */
+PJ_DEF(const pjsip_allow_hdr*) pjsip_endpt_get_allow_hdr( pjsip_endpoint *endpt )
+{
+ return endpt->allow_hdr;
+}
+
+/*
+ * Get additional headers to be put in outgoing request message.
+ */
+PJ_DEF(const pjsip_hdr*) pjsip_endpt_get_request_headers(pjsip_endpoint *endpt)
+{
+ return &endpt->req_hdr;
+}
+
+PJ_DEF(pj_status_t) pjsip_endpt_set_proxies( pjsip_endpoint *endpt,
+ int url_cnt, const pj_str_t url[])
+{
+ int i;
+ pjsip_route_hdr *hdr;
+ pj_str_t str_ROUTE = { "Route", 5 };
+
+ /* Lock endpoint mutex. */
+ pj_mutex_lock(endpt->mutex);
+
+ pj_list_init(&endpt->route_hdr_list);
+
+ for (i=0; i<url_cnt; ++i) {
+ int len = url[i].slen;
+ char *dup = pj_pool_alloc(endpt->pool, len + 1);
+ pj_memcpy(dup, url[i].ptr, len);
+ dup[len] = '\0';
+
+ hdr = pjsip_parse_hdr(endpt->pool, &str_ROUTE, dup, len, NULL);
+ if (!hdr) {
+ pj_mutex_unlock(endpt->mutex);
+ PJ_LOG(4,(THIS_FILE, "Invalid URL %s in proxy URL", dup));
+ return -1;
+ }
+
+ pj_assert(hdr->type == PJSIP_H_ROUTE);
+ pj_list_insert_before(&endpt->route_hdr_list, hdr);
+ }
+
+ /* Unlock endpoint mutex. */
+ pj_mutex_unlock(endpt->mutex);
+
+ return 0;
+}
+
+/*
+ * Get "Route" header list.
+ */
+PJ_DEF(const pjsip_route_hdr*) pjsip_endpt_get_routing( pjsip_endpoint *endpt )
+{
+ return &endpt->route_hdr_list;
+}
+
+
+/*
+ * Initialize endpoint.
+ */
+PJ_DEF(pj_status_t) pjsip_endpt_create(pj_pool_factory *pf,
+ const char *name,
+ pjsip_endpoint **p_endpt)
+{
+ pj_status_t status;
+ pj_pool_t *pool;
+ pjsip_endpoint *endpt;
+ pjsip_max_forwards_hdr *mf_hdr;
+ pj_lock_t *lock = NULL;
+
+ PJ_LOG(5, (THIS_FILE, "pjsip_endpt_create()"));
+
+ *p_endpt = NULL;
+
+ /* Create pool */
+ pool = pj_pool_create(pf, "pept%p",
+ PJSIP_POOL_LEN_ENDPT, PJSIP_POOL_INC_ENDPT,
+ &pool_callback);
+ if (!pool)
+ return PJ_ENOMEM;
+
+ /* Create endpoint. */
+ endpt = pj_pool_calloc(pool, 1, sizeof(*endpt));
+ endpt->pool = pool;
+ endpt->pf = pf;
+
+ /* Get name. */
+ if (name != NULL) {
+ pj_str_t temp;
+ pj_strdup_with_null(endpt->pool, &endpt->name, pj_cstr(&temp, name));
+ } else {
+ pj_strdup_with_null(endpt->pool, &endpt->name, pj_gethostname());
+ }
+
+ /* Create mutex for the events, etc. */
+ status = pj_mutex_create_recursive( endpt->pool, "ept%p", &endpt->mutex );
+ if (status != PJ_SUCCESS) {
+ goto on_error;
+ }
+
+ /* Create mutex for the transaction table. */
+ status = pj_mutex_create_recursive( endpt->pool, "mtbl%p",
+ &endpt->tsx_table_mutex);
+ if (status != PJ_SUCCESS) {
+ goto on_error;
+ }
+
+ /* Create hash table for transaction. */
+ endpt->tsx_table = pj_hash_create( endpt->pool, PJSIP_MAX_TSX_COUNT );
+ if (!endpt->tsx_table) {
+ status = PJ_ENOMEM;
+ goto on_error;
+ }
+
+ /* Create timer heap to manage all timers within this endpoint. */
+ status = pj_timer_heap_create( endpt->pool, PJSIP_MAX_TIMER_COUNT,
+ &endpt->timer_heap);
+ if (status != PJ_SUCCESS) {
+ goto on_error;
+ }
+
+ /* Set recursive lock for the timer heap. */
+ status = pj_lock_create_recursive_mutex( endpt->pool, "edpt%p", &lock);
+ if (status != PJ_SUCCESS) {
+ goto on_error;
+ }
+ pj_timer_heap_set_lock(endpt->timer_heap, lock, PJ_TRUE);
+
+ /* Set maximum timed out entries to process in a single poll. */
+ pj_timer_heap_set_max_timed_out_per_poll(endpt->timer_heap,
+ PJSIP_MAX_TIMED_OUT_ENTRIES);
+
+ /* Create ioqueue. */
+ status = pj_ioqueue_create( endpt->pool, PJSIP_MAX_TRANSPORTS, &endpt->ioqueue);
+ if (status != PJ_SUCCESS) {
+ goto on_error;
+ }
+
+ /* Create transport manager. */
+ status = pjsip_tpmgr_create( endpt->pool, endpt,
+ &endpt_transport_callback,
+ &endpt->transport_mgr);
+ if (status != PJ_SUCCESS) {
+ goto on_error;
+ }
+
+ /* Create asynchronous DNS resolver. */
+ endpt->resolver = pjsip_resolver_create(endpt->pool);
+ if (!endpt->resolver) {
+ PJ_LOG(4, (THIS_FILE, "pjsip_endpt_init(): error creating resolver"));
+ goto on_error;
+ }
+
+ /* Initialize TLS ID for transaction lock. */
+ status = pj_thread_local_alloc(&pjsip_tsx_lock_tls_id);
+ if (status != PJ_SUCCESS) {
+ goto on_error;
+ }
+ pj_thread_local_set(pjsip_tsx_lock_tls_id, NULL);
+
+ /* Initialize request headers. */
+ pj_list_init(&endpt->req_hdr);
+
+ /* Initialist "Route" header list. */
+ pj_list_init(&endpt->route_hdr_list);
+
+ /* Add "Max-Forwards" for request header. */
+ mf_hdr = pjsip_max_forwards_hdr_create(endpt->pool);
+ mf_hdr->ivalue = PJSIP_MAX_FORWARDS_VALUE;
+ pj_list_insert_before( &endpt->req_hdr, mf_hdr);
+
+ /* Load and init modules. */
+ status = init_modules(endpt);
+ if (status != PJ_SUCCESS) {
+ PJ_LOG(4, (THIS_FILE, "pjsip_endpt_init(): error in init_modules()"));
+ return status;
+ }
+
+ /* Done. */
+ *p_endpt = endpt;
+ return status;
+
+on_error:
+ if (endpt->transport_mgr) {
+ pjsip_tpmgr_destroy(endpt->transport_mgr);
+ endpt->transport_mgr = NULL;
+ }
+ if (endpt->mutex) {
+ pj_mutex_destroy(endpt->mutex);
+ endpt->mutex = NULL;
+ }
+ if (endpt->tsx_table_mutex) {
+ pj_mutex_destroy(endpt->tsx_table_mutex);
+ endpt->tsx_table_mutex = NULL;
+ }
+ pj_pool_release( endpt->pool );
+
+ PJ_LOG(4, (THIS_FILE, "pjsip_endpt_init() failed"));
+ return status;
+}
+
+/*
+ * Destroy endpoint.
+ */
+PJ_DEF(void) pjsip_endpt_destroy(pjsip_endpoint *endpt)
+{
+ PJ_LOG(5, (THIS_FILE, "pjsip_endpt_destroy()"));
+
+ /* Shutdown and destroy all transports. */
+ pjsip_tpmgr_destroy(endpt->transport_mgr);
+
+ /* Delete endpoint mutex. */
+ pj_mutex_destroy(endpt->mutex);
+
+ /* Delete transaction table mutex. */
+ pj_mutex_destroy(endpt->tsx_table_mutex);
+
+ /* Finally destroy pool. */
+ pj_pool_release(endpt->pool);
+}
+
+/*
+ * Get endpoint name.
+ */
+PJ_DEF(const pj_str_t*) pjsip_endpt_name(const pjsip_endpoint *endpt)
+{
+ return &endpt->name;
+}
+
+
+/*
+ * Create new pool.
+ */
+PJ_DEF(pj_pool_t*) pjsip_endpt_create_pool( pjsip_endpoint *endpt,
+ const char *pool_name,
+ pj_size_t initial,
+ pj_size_t increment )
+{
+ pj_pool_t *pool;
+
+ PJ_LOG(5, (THIS_FILE, "pjsip_endpt_create_pool()"));
+
+ /* Lock endpoint mutex. */
+ pj_mutex_lock(endpt->mutex);
+
+ /* Create pool */
+ pool = pj_pool_create( endpt->pf, pool_name,
+ initial, increment, &pool_callback);
+
+ /* Unlock mutex. */
+ pj_mutex_unlock(endpt->mutex);
+
+ if (pool) {
+ PJ_LOG(5, (THIS_FILE, " pool %s created", pj_pool_getobjname(pool)));
+ } else {
+ PJ_LOG(4, (THIS_FILE, "Unable to create pool %s!", pool_name));
+ }
+
+ return pool;
+}
+
+/*
+ * Return back pool to endpoint's pool manager to be either destroyed or
+ * recycled.
+ */
+PJ_DEF(void) pjsip_endpt_destroy_pool( pjsip_endpoint *endpt, pj_pool_t *pool )
+{
+ PJ_LOG(5, (THIS_FILE, "pjsip_endpt_destroy_pool(%s)", pj_pool_getobjname(pool)));
+
+ pj_mutex_lock(endpt->mutex);
+ pj_pool_release( pool );
+ pj_mutex_unlock(endpt->mutex);
+}
+
+/*
+ * Handle events.
+ */
+PJ_DEF(void) pjsip_endpt_handle_events( pjsip_endpoint *endpt,
+ const pj_time_val *max_timeout)
+{
+ /* timeout is 'out' var. This just to make compiler happy. */
+ pj_time_val timeout = { 0, 0};
+
+ PJ_LOG(5, (THIS_FILE, "pjsip_endpt_handle_events()"));
+
+ /* Poll the timer. The timer heap has its own mutex for better
+ * granularity, so we don't need to lock end endpoint.
+ */
+ timeout.sec = timeout.msec = 0;
+ pj_timer_heap_poll( endpt->timer_heap, &timeout );
+
+ /* If caller specifies maximum time to wait, then compare the value with
+ * the timeout to wait from timer, and use the minimum value.
+ */
+ if (max_timeout && PJ_TIME_VAL_GT(timeout, *max_timeout)) {
+ timeout = *max_timeout;
+ }
+
+ /* Poll ioqueue. */
+ pj_ioqueue_poll( endpt->ioqueue, &timeout);
+}
+
+/*
+ * Schedule timer.
+ */
+PJ_DEF(pj_status_t) pjsip_endpt_schedule_timer( pjsip_endpoint *endpt,
+ pj_timer_entry *entry,
+ const pj_time_val *delay )
+{
+ PJ_LOG(5, (THIS_FILE, "pjsip_endpt_schedule_timer(entry=%p, delay=%u.%u)",
+ entry, delay->sec, delay->msec));
+ return pj_timer_heap_schedule( endpt->timer_heap, entry, delay );
+}
+
+/*
+ * Cancel the previously registered timer.
+ */
+PJ_DEF(void) pjsip_endpt_cancel_timer( pjsip_endpoint *endpt,
+ pj_timer_entry *entry )
+{
+ PJ_LOG(5, (THIS_FILE, "pjsip_endpt_cancel_timer(entry=%p)", entry));
+ pj_timer_heap_cancel( endpt->timer_heap, entry );
+}
+
+/*
+ * Create a new transaction.
+ * Endpoint must then initialize the new transaction as either UAS or UAC, and
+ * register it to the hash table.
+ */
+PJ_DEF(pj_status_t) pjsip_endpt_create_tsx(pjsip_endpoint *endpt,
+ pjsip_transaction **p_tsx)
+{
+ pj_pool_t *pool;
+
+ PJ_ASSERT_RETURN(endpt && p_tsx, PJ_EINVAL);
+
+ PJ_LOG(5, (THIS_FILE, "pjsip_endpt_create_tsx()"));
+
+ /* Request one pool for the transaction. Mutex is locked there. */
+ pool = pjsip_endpt_create_pool(endpt, "ptsx%p",
+ PJSIP_POOL_LEN_TSX, PJSIP_POOL_INC_TSX);
+ if (pool == NULL) {
+ return PJ_ENOMEM;
+ }
+
+ /* Create the transaction. */
+ return pjsip_tsx_create(pool, endpt, p_tsx);
+}
+
+/*
+ * Register the transaction to the endpoint.
+ * This will put the transaction to the transaction hash table. Before calling
+ * this function, the transaction must be INITIALIZED as either UAS or UAC, so
+ * that the transaction key is built.
+ */
+PJ_DEF(void) pjsip_endpt_register_tsx( pjsip_endpoint *endpt,
+ pjsip_transaction *tsx)
+{
+ PJ_LOG(5, (THIS_FILE, "pjsip_endpt_register_tsx(%s)", tsx->obj_name));
+
+ pj_assert(tsx->transaction_key.slen != 0);
+ //pj_assert(tsx->state != PJSIP_TSX_STATE_NULL);
+
+ /* Lock hash table mutex. */
+ pj_mutex_lock(endpt->tsx_table_mutex);
+
+ /* Register the transaction to the hash table. */
+ pj_hash_set( tsx->pool, endpt->tsx_table, tsx->transaction_key.ptr,
+ tsx->transaction_key.slen, tsx);
+
+ /* Unlock mutex. */
+ pj_mutex_unlock(endpt->tsx_table_mutex);
+}
+
+/*
+ * Find transaction by the key.
+ */
+PJ_DEF(pjsip_transaction*) pjsip_endpt_find_tsx( pjsip_endpoint *endpt,
+ const pj_str_t *key )
+{
+ pjsip_transaction *tsx;
+
+ PJ_LOG(5, (THIS_FILE, "pjsip_endpt_find_tsx()"));
+
+ /* Start lock mutex in the endpoint. */
+ pj_mutex_lock(endpt->tsx_table_mutex);
+
+ /* Find the transaction in the hash table. */
+ tsx = pj_hash_get( endpt->tsx_table, key->ptr, key->slen );
+
+ /* Unlock mutex. */
+ pj_mutex_unlock(endpt->tsx_table_mutex);
+
+ return tsx;
+}
+
+/*
+ * Create key.
+ */
+static void rdata_create_key( pjsip_rx_data *rdata)
+{
+ pjsip_role_e role;
+ if (rdata->msg_info.msg->type == PJSIP_REQUEST_MSG) {
+ role = PJSIP_ROLE_UAS;
+ } else {
+ role = PJSIP_ROLE_UAC;
+ }
+ pjsip_tsx_create_key(rdata->tp_info.pool, &rdata->endpt_info.key, role,
+ &rdata->msg_info.cseq->method, rdata);
+}
+
+/*
+ * This is the callback that is called by the transport manager when it
+ * receives a message from the network.
+ */
+static void endpt_transport_callback( pjsip_endpoint *endpt,
+ pj_status_t status,
+ pjsip_rx_data *rdata )
+{
+ pjsip_msg *msg = rdata->msg_info.msg;
+ pjsip_transaction *tsx;
+ pj_bool_t a_new_transaction_just_been_created = PJ_FALSE;
+
+ PJ_LOG(5, (THIS_FILE, "endpt_transport_callback(rdata=%p)", rdata));
+
+ if (status != PJ_SUCCESS) {
+ const char *src_addr = pj_inet_ntoa(rdata->pkt_info.addr.sin_addr);
+ int port = pj_ntohs(rdata->pkt_info.addr.sin_port);
+ PJSIP_ENDPT_LOG_ERROR((endpt, "transport", status,
+ "Src.addr=%s:%d, packet:--\n"
+ "%s\n"
+ "-- end of packet. Error",
+ src_addr, port, rdata->msg_info.msg_buf));
+ return;
+ }
+
+ /* For response, check that the value in Via sent-by match the transport.
+ * If not matched, silently drop the response.
+ * Ref: RFC3261 Section 18.1.2 Receiving Response
+ */
+ if (msg->type == PJSIP_RESPONSE_MSG) {
+ const pj_sockaddr_in *addr;
+ const char *addr_addr;
+ int port = rdata->msg_info.via->sent_by.port;
+ pj_bool_t mismatch = PJ_FALSE;
+ if (port == 0) {
+ int type;
+ type = rdata->tp_info.transport->type;
+ port = pjsip_transport_get_default_port_for_type(type);
+ }
+ addr = &rdata->tp_info.transport->public_addr;
+ addr_addr = pj_inet_ntoa(addr->sin_addr);
+ if (pj_strcmp2(&rdata->msg_info.via->sent_by.host, addr_addr) != 0)
+ mismatch = PJ_TRUE;
+ else if (port != pj_ntohs(addr->sin_port)) {
+ /* Port or address mismatch, we should discard response */
+ /* But we saw one implementation (we don't want to name it to
+ * protect the innocence) which put wrong sent-by port although
+ * the "rport" parameter is correct.
+ * So we discard the response only if the port doesn't match
+ * both the port in sent-by and rport. We try to be lenient here!
+ */
+ if (rdata->msg_info.via->rport_param != pj_sockaddr_in_get_port(addr))
+ mismatch = PJ_TRUE;
+ else {
+ PJ_LOG(4,(THIS_FILE, "Response %p has mismatch port in sent-by"
+ " but the rport parameter is correct",
+ rdata));
+ }
+ }
+
+ if (mismatch) {
+ pjsip_event e;
+
+ PJSIP_EVENT_INIT_DISCARD_MSG(e, rdata, PJSIP_EINVALIDVIA);
+ endpt_do_event( endpt, &e );
+ return;
+ }
+ }
+
+ /* Create key for transaction lookup. */
+ rdata_create_key( rdata);
+
+ /* Find the transaction for the received message. */
+ PJ_LOG(5, (THIS_FILE, "finding tsx with key=%.*s",
+ rdata->endpt_info.key.slen, rdata->endpt_info.key.ptr));
+
+ /* Start lock mutex in the endpoint. */
+ pj_mutex_lock(endpt->tsx_table_mutex);
+
+ /* Find the transaction in the hash table. */
+ tsx = pj_hash_get( endpt->tsx_table, rdata->endpt_info.key.ptr, rdata->endpt_info.key.slen );
+
+ /* Unlock mutex. */
+ pj_mutex_unlock(endpt->tsx_table_mutex);
+
+ /* If the transaction is not found... */
+ if (tsx == NULL || tsx->state == PJSIP_TSX_STATE_TERMINATED) {
+
+ /*
+ * For response message, discard the message, except if the response is
+ * an 2xx class response to INVITE, which in this case it must be
+ * passed to TU to be acked.
+ */
+ if (msg->type == PJSIP_RESPONSE_MSG) {
+
+ /* Inform TU about the 200 message, only if it's INVITE. */
+ if (PJSIP_IS_STATUS_IN_CLASS(msg->line.status.code, 200) &&
+ rdata->msg_info.cseq->method.id == PJSIP_INVITE_METHOD)
+ {
+ pjsip_event e;
+
+ /* Should not happen for UA. Tsx theoritically lives until
+ * all responses are absorbed.
+ */
+ pj_assert(0);
+
+ PJSIP_EVENT_INIT_RX_200_MSG(e, rdata);
+ endpt_do_event( endpt, &e );
+
+ } else {
+ /* Just discard the response, inform TU. */
+ pjsip_event e;
+
+ PJSIP_EVENT_INIT_DISCARD_MSG(e, rdata,
+ PJSIP_ERRNO_FROM_SIP_STATUS(PJSIP_SC_CALL_TSX_DOES_NOT_EXIST));
+ endpt_do_event( endpt, &e );
+ }
+
+ /*
+ * For non-ACK request message, create a new transaction.
+ */
+ } else if (rdata->msg_info.msg->line.req.method.id != PJSIP_ACK_METHOD) {
+
+ pj_status_t status;
+
+ /* Create transaction, mutex is locked there. */
+ status = pjsip_endpt_create_tsx(endpt, &tsx);
+ if (status != PJ_SUCCESS) {
+ PJSIP_ENDPT_LOG_ERROR((endpt, THIS_FILE, status,
+ "Unable to create transaction"));
+ return;
+ }
+
+ /* Initialize transaction as UAS. */
+ pjsip_tsx_init_uas( tsx, rdata );
+
+ /* Register transaction, mutex is locked there. */
+ pjsip_endpt_register_tsx( endpt, tsx );
+
+ a_new_transaction_just_been_created = PJ_TRUE;
+ }
+ }
+
+ /* If transaction is found (or newly created), pass the message.
+ * Otherwise if it's an ACK request, pass directly to TU.
+ */
+ if (tsx && tsx->state != PJSIP_TSX_STATE_TERMINATED) {
+ /* Dispatch message to transaction. */
+ pjsip_tsx_on_rx_msg( tsx, rdata );
+
+ } else if (rdata->msg_info.msg->line.req.method.id == PJSIP_ACK_METHOD) {
+ /*
+ * This is an ACK message, but the INVITE transaction could not
+ * be found (possibly because the branch parameter in Via in ACK msg
+ * is different than the branch in original INVITE). This happens with
+ * SER!
+ */
+ pjsip_event event;
+
+ PJSIP_EVENT_INIT_RX_ACK_MSG(event,rdata);
+ endpt_do_event( endpt, &event );
+ }
+
+ /*
+ * If a new request message has just been receieved, but no modules
+ * seem to be able to handle the request message, then terminate the
+ * transaction.
+ *
+ * Ideally for cases like "unsupported method", we should be able to
+ * answer the request statelessly. But we can not do that since the
+ * endpoint shoule be able to be used as both user agent and proxy stack,
+ * and a proxy stack should be able to handle arbitrary methods.
+ */
+ if (a_new_transaction_just_been_created && tsx->status_code < 100) {
+ /* Certainly no modules has sent any response message.
+ * Check that any modules has attached a module data.
+ */
+ int i;
+ for (i=0; i<PJSIP_MAX_MODULE; ++i) {
+ if (tsx->module_data[i] != NULL) {
+ break;
+ }
+ }
+ if (i == PJSIP_MAX_MODULE) {
+ /* No modules have attached itself to the transaction.
+ * Terminate the transaction with 501/Not Implemented.
+ */
+ pjsip_tx_data *tdata;
+ pj_status_t status;
+
+ if (tsx->method.id == PJSIP_OPTIONS_METHOD) {
+ status = pjsip_endpt_create_response(endpt, rdata, 200,
+ &tdata);
+ } else {
+ status = pjsip_endpt_create_response(endpt, rdata,
+ PJSIP_SC_METHOD_NOT_ALLOWED,
+ &tdata);
+ }
+
+ if (status != PJ_SUCCESS) {
+ PJSIP_ENDPT_LOG_ERROR((endpt, THIS_FILE, status,
+ "Unable to create response"));
+ return;
+ }
+
+ if (endpt->allow_hdr) {
+ pjsip_msg_add_hdr( tdata->msg,
+ pjsip_hdr_shallow_clone(tdata->pool, endpt->allow_hdr));
+ }
+ pjsip_tsx_on_tx_msg( tsx, tdata );
+
+ } else {
+ /*
+ * If a module has registered itself in the transaction but it
+ * hasn't responded the request, chances are the module wouldn't
+ * respond to the request at all. We terminate the request here
+ * with 500/Internal Server Error, to be safe.
+ */
+ pjsip_tx_data *tdata;
+ pj_status_t status;
+
+ status = pjsip_endpt_create_response(endpt, rdata, 500, &tdata);
+ if (status != PJ_SUCCESS) {
+ PJSIP_ENDPT_LOG_ERROR((endpt, THIS_FILE, status,
+ "Unable to create response"));
+ return;
+ }
+
+ pjsip_tsx_on_tx_msg(tsx, tdata);
+ }
+ }
+}
+
+/*
+ * Create transmit data buffer.
+ */
+PJ_DEF(pj_status_t) pjsip_endpt_create_tdata( pjsip_endpoint *endpt,
+ pjsip_tx_data **p_tdata)
+{
+ PJ_LOG(5, (THIS_FILE, "pjsip_endpt_create_tdata()"));
+ return pjsip_tx_data_create(endpt->transport_mgr, p_tdata);
+}
+
+/*
+ * Resolve
+ */
+PJ_DEF(void) pjsip_endpt_resolve( pjsip_endpoint *endpt,
+ pj_pool_t *pool,
+ pjsip_host_port *target,
+ void *token,
+ pjsip_resolver_callback *cb)
+{
+ PJ_LOG(5, (THIS_FILE, "pjsip_endpt_resolve()"));
+ pjsip_resolve( endpt->resolver, pool, target, token, cb);
+}
+
+/*
+ * Get transport manager.
+ */
+PJ_DEF(pjsip_tpmgr*) pjsip_endpt_get_tpmgr(pjsip_endpoint *endpt)
+{
+ return endpt->transport_mgr;
+}
+
+/*
+ * Get ioqueue instance.
+ */
+PJ_DEF(pj_ioqueue_t*) pjsip_endpt_get_ioqueue(pjsip_endpoint *endpt)
+{
+ return endpt->ioqueue;
+}
+
+/*
+ * Find/create transport.
+ */
+PJ_DEF(pj_status_t) pjsip_endpt_alloc_transport( pjsip_endpoint *endpt,
+ pjsip_transport_type_e type,
+ const pj_sockaddr_in *remote,
+ pjsip_transport **p_transport)
+{
+ PJ_LOG(5, (THIS_FILE, "pjsip_endpt_alloc_transport()"));
+ return pjsip_tpmgr_alloc_transport( endpt->transport_mgr, type, remote,
+ p_transport);
+}
+
+
+/*
+ * Report error.
+ */
+PJ_DEF(void) pjsip_endpt_log_error( pjsip_endpoint *endpt,
+ const char *sender,
+ pj_status_t error_code,
+ const char *format,
+ ... )
+{
+ char newformat[256];
+ int len;
+ va_list marker;
+
+ va_start(marker, format);
+
+ PJ_UNUSED_ARG(endpt);
+
+ len = pj_native_strlen(format);
+ if (len < sizeof(newformat)-30) {
+ pj_str_t errstr;
+
+ pj_native_strcpy(newformat, format);
+ pj_snprintf(newformat+len, sizeof(newformat)-len-1,
+ ": [err %d] ", error_code);
+ len += pj_native_strlen(newformat+len);
+
+ errstr = pjsip_strerror(error_code, newformat+len,
+ sizeof(newformat)-len-1);
+
+ len += errstr.slen;
+ newformat[len] = '\0';
+
+ pj_log(sender, 1, newformat, marker);
+ } else {
+ pj_log(sender, 1, format, marker);
+ }
+
+ va_end(marker);
+}
+
+
+/*
+ * Dump endpoint.
+ */
+PJ_DEF(void) pjsip_endpt_dump( pjsip_endpoint *endpt, pj_bool_t detail )
+{
+#if PJ_LOG_MAX_LEVEL >= 3
+ unsigned count;
+
+ PJ_LOG(5, (THIS_FILE, "pjsip_endpt_dump()"));
+
+ /* Lock mutex. */
+ pj_mutex_lock(endpt->mutex);
+
+ PJ_LOG(3, (THIS_FILE, "Dumping endpoint %p:", endpt));
+
+ /* Dumping pool factory. */
+ (*endpt->pf->dump_status)(endpt->pf, detail);
+
+ /* Pool health. */
+ PJ_LOG(3, (THIS_FILE," Endpoint pool capacity=%u, used_size=%u",
+ pj_pool_get_capacity(endpt->pool),
+ pj_pool_get_used_size(endpt->pool)));
+
+ /* Transaction tables. */
+ count = pj_hash_count(endpt->tsx_table);
+ PJ_LOG(3, (THIS_FILE, " Number of transactions: %u", count));
+
+ if (count && detail) {
+ pj_hash_iterator_t it_val;
+ pj_hash_iterator_t *it;
+ pj_time_val now;
+
+ PJ_LOG(3, (THIS_FILE, " Dumping transaction tables:"));
+
+ pj_gettimeofday(&now);
+ it = pj_hash_first(endpt->tsx_table, &it_val);
+
+ while (it != NULL) {
+ int timeout_diff;
+
+ /* Get the transaction. No need to lock transaction's mutex
+ * since we already hold endpoint mutex, so that no transactions
+ * will be deleted.
+ */
+ pjsip_transaction *tsx = pj_hash_this(endpt->tsx_table, it);
+
+ const char *role = (tsx->role == PJSIP_ROLE_UAS ? "UAS" : "UAC");
+
+ if (tsx->timeout_timer._timer_id != -1) {
+ if (tsx->timeout_timer._timer_value.sec > now.sec) {
+ timeout_diff = tsx->timeout_timer._timer_value.sec - now.sec;
+ } else {
+ timeout_diff = now.sec - tsx->timeout_timer._timer_value.sec;
+ timeout_diff = 0 - timeout_diff;
+ }
+ } else {
+ timeout_diff = -1;
+ }
+
+ PJ_LOG(3, (THIS_FILE, " %s %s %10.*s %.9u %s t=%ds",
+ tsx->obj_name, role,
+ tsx->method.name.slen, tsx->method.name.ptr,
+ tsx->cseq,
+ pjsip_tsx_state_str(tsx->state),
+ timeout_diff));
+
+ it = pj_hash_next(endpt->tsx_table, it);
+ }
+ }
+
+ /* Transports.
+ */
+ pjsip_tpmgr_dump_transports( endpt->transport_mgr );
+
+ /* Timer. */
+ PJ_LOG(3,(THIS_FILE, " Timer heap has %u entries",
+ pj_timer_heap_count(endpt->timer_heap)));
+
+ /* Unlock mutex. */
+ pj_mutex_unlock(endpt->mutex);
+#else
+ PJ_LOG(3,(THIS_FILE, "pjsip_end_dump: can't dump because it's disabled."));
+#endif
+}
+
diff --git a/pjsip/src/pjsip/sip_errno.c b/pjsip/src/pjsip/sip_errno.c
index 7e24f3ee..999156c6 100644
--- a/pjsip/src/pjsip/sip_errno.c
+++ b/pjsip/src/pjsip/sip_errno.c
@@ -1,125 +1,125 @@
-/* $Id: $ */
-/*
- * Copyright (C) 2003-2006 Benny Prijono <benny@prijono.org>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
-#include <pjsip/sip_errno.h>
-#include <pjsip/sip_msg.h>
-#include <pj/string.h>
-#include <pj/errno.h>
-
-/* PJSIP's own error codes/messages
- * MUST KEEP THIS ARRAY SORTED!!
- */
-static const struct
-{
- int code;
- const char *msg;
-} err_str[] =
-{
- /* Generic SIP errors */
- { PJSIP_EBUSY, "Object is busy" },
- { PJSIP_ETYPEEXISTS , "Object with the same type exists" },
-
- /* Messaging errors */
- { PJSIP_EINVALIDMSG, "Invalid message/syntax error" },
- { PJSIP_EINVALIDSCHEME, "Invalid URI scheme" },
- { PJSIP_EMSGTOOLONG, "Message too long" },
- { PJSIP_EPARTIALMSG, "Partial message" },
- { PJSIP_EMISSINGHDR, "Missing required header(s)" },
- { PJSIP_EINVALIDVIA, "Invalid Via header" },
- { PJSIP_EMULTIPLEVIA, "Multiple Via headers in response" },
-
- /* Transport errors */
- { PJSIP_EUNSUPTRANSPORT, "Unsupported transport"},
- { PJSIP_EPENDINGTX, "Transmit buffer already pending"},
- { PJSIP_ERXOVERFLOW, "Rx buffer overflow"},
-
- /* Transaction errors */
- { PJSIP_ETSXDESTROYED, "Transaction has been destroyed"},
-};
-
-
-/*
- * pjsip_strerror()
- */
-PJ_DEF(pj_str_t) pjsip_strerror( pj_status_t statcode,
- char *buf, pj_size_t bufsize )
-{
- pj_str_t errstr;
-
- if (statcode >= PJSIP_ERRNO_START && statcode < PJSIP_ERRNO_START+800)
- {
- /* Status code. */
- const pj_str_t *status_text =
- pjsip_get_status_text(PJSIP_ERRNO_TO_SIP_STATUS(statcode));
-
- errstr.ptr = buf;
- pj_strncpy_with_null(&errstr, status_text, bufsize);
- return errstr;
- }
- else if (statcode >= PJSIP_ERRNO_START_PJSIP &&
- statcode < PJSIP_ERRNO_START_PJSIP + 1000)
- {
- /* Find the error in the table.
- * Use binary search!
- */
- int first = 0;
- int n = PJ_ARRAY_SIZE(err_str);
-
- while (n > 0) {
- int half = n/2;
- int mid = first + half;
-
- if (err_str[mid].code < statcode) {
- first = mid+1;
- n -= (half+1);
- } else if (err_str[mid].code > statcode) {
- n = half;
- } else {
- first = mid;
- break;
- }
- }
-
-
- if (PJ_ARRAY_SIZE(err_str) && err_str[first].code == statcode) {
- pj_str_t msg;
-
- msg.ptr = (char*)err_str[first].msg;
- msg.slen = pj_native_strlen(err_str[first].msg);
-
- errstr.ptr = buf;
- pj_strncpy_with_null(&errstr, &msg, bufsize);
- return errstr;
-
- } else {
- /* Error not found. */
- errstr.ptr = buf;
- errstr.slen = pj_snprintf(buf, bufsize,
- "Unknown error %d",
- statcode);
-
- return errstr;
- }
- }
- else {
- /* Not our code. Give it to PJLIB. */
- return pj_strerror(statcode, buf, bufsize);
- }
-
-}
-
+/* $Id: $ */
+/*
+ * Copyright (C) 2003-2006 Benny Prijono <benny@prijono.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#include <pjsip/sip_errno.h>
+#include <pjsip/sip_msg.h>
+#include <pj/string.h>
+#include <pj/errno.h>
+
+/* PJSIP's own error codes/messages
+ * MUST KEEP THIS ARRAY SORTED!!
+ */
+static const struct
+{
+ int code;
+ const char *msg;
+} err_str[] =
+{
+ /* Generic SIP errors */
+ { PJSIP_EBUSY, "Object is busy" },
+ { PJSIP_ETYPEEXISTS , "Object with the same type exists" },
+
+ /* Messaging errors */
+ { PJSIP_EINVALIDMSG, "Invalid message/syntax error" },
+ { PJSIP_EINVALIDSCHEME, "Invalid URI scheme" },
+ { PJSIP_EMSGTOOLONG, "Message too long" },
+ { PJSIP_EPARTIALMSG, "Partial message" },
+ { PJSIP_EMISSINGHDR, "Missing required header(s)" },
+ { PJSIP_EINVALIDVIA, "Invalid Via header" },
+ { PJSIP_EMULTIPLEVIA, "Multiple Via headers in response" },
+
+ /* Transport errors */
+ { PJSIP_EUNSUPTRANSPORT, "Unsupported transport"},
+ { PJSIP_EPENDINGTX, "Transmit buffer already pending"},
+ { PJSIP_ERXOVERFLOW, "Rx buffer overflow"},
+
+ /* Transaction errors */
+ { PJSIP_ETSXDESTROYED, "Transaction has been destroyed"},
+};
+
+
+/*
+ * pjsip_strerror()
+ */
+PJ_DEF(pj_str_t) pjsip_strerror( pj_status_t statcode,
+ char *buf, pj_size_t bufsize )
+{
+ pj_str_t errstr;
+
+ if (statcode >= PJSIP_ERRNO_START && statcode < PJSIP_ERRNO_START+800)
+ {
+ /* Status code. */
+ const pj_str_t *status_text =
+ pjsip_get_status_text(PJSIP_ERRNO_TO_SIP_STATUS(statcode));
+
+ errstr.ptr = buf;
+ pj_strncpy_with_null(&errstr, status_text, bufsize);
+ return errstr;
+ }
+ else if (statcode >= PJSIP_ERRNO_START_PJSIP &&
+ statcode < PJSIP_ERRNO_START_PJSIP + 1000)
+ {
+ /* Find the error in the table.
+ * Use binary search!
+ */
+ int first = 0;
+ int n = PJ_ARRAY_SIZE(err_str);
+
+ while (n > 0) {
+ int half = n/2;
+ int mid = first + half;
+
+ if (err_str[mid].code < statcode) {
+ first = mid+1;
+ n -= (half+1);
+ } else if (err_str[mid].code > statcode) {
+ n = half;
+ } else {
+ first = mid;
+ break;
+ }
+ }
+
+
+ if (PJ_ARRAY_SIZE(err_str) && err_str[first].code == statcode) {
+ pj_str_t msg;
+
+ msg.ptr = (char*)err_str[first].msg;
+ msg.slen = pj_native_strlen(err_str[first].msg);
+
+ errstr.ptr = buf;
+ pj_strncpy_with_null(&errstr, &msg, bufsize);
+ return errstr;
+
+ } else {
+ /* Error not found. */
+ errstr.ptr = buf;
+ errstr.slen = pj_snprintf(buf, bufsize,
+ "Unknown error %d",
+ statcode);
+
+ return errstr;
+ }
+ }
+ else {
+ /* Not our code. Give it to PJLIB. */
+ return pj_strerror(statcode, buf, bufsize);
+ }
+
+}
+
diff --git a/pjsip/src/pjsip/sip_msg.c b/pjsip/src/pjsip/sip_msg.c
index 23e184fa..6f963abb 100644
--- a/pjsip/src/pjsip/sip_msg.c
+++ b/pjsip/src/pjsip/sip_msg.c
@@ -1,1432 +1,1452 @@
-/* $Id$ */
-/*
- * Copyright (C) 2003-2006 Benny Prijono <benny@prijono.org>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
-#include <pjsip/sip_msg.h>
-#include <pjsip/print_util.h>
-#include <pj/string.h>
-#include <pj/pool.h>
-
-/*
- * Include inline definitions here if functions are NOT inlined.
- */
-#if PJ_FUNCTIONS_ARE_INLINED==0
-# include <pjsip/sip_msg_i.h>
-#endif
-
-static const pj_str_t method_names[] =
-{
- { "INVITE", 6 },
- { "CANCEL", 6 },
- { "ACK", 3 },
- { "BYE", 3 },
- { "REGISTER", 8 },
- { "OPTIONS", 7 },
-};
-
-const pj_str_t pjsip_hdr_names[] =
-{
- { "Accept", 6 }, // PJSIP_H_ACCEPT,
- { "Accept-Encoding", 15 }, // PJSIP_H_ACCEPT_ENCODING,
- { "Accept-Language", 15 }, // PJSIP_H_ACCEPT_LANGUAGE,
- { "Alert-Info", 10 }, // PJSIP_H_ALERT_INFO,
- { "Allow", 5 }, // PJSIP_H_ALLOW,
- { "Authentication-Info",19 }, // PJSIP_H_AUTHENTICATION_INFO,
- { "Authorization", 13 }, // PJSIP_H_AUTHORIZATION,
- { "Call-ID", 7 }, // PJSIP_H_CALL_ID,
- { "Call-Info", 9 }, // PJSIP_H_CALL_INFO,
- { "Contact", 7 }, // PJSIP_H_CONTACT,
- { "Content-Disposition",19 }, // PJSIP_H_CONTENT_DISPOSITION,
- { "Content-Encoding", 16 }, // PJSIP_H_CONTENT_ENCODING,
- { "Content-Language", 16 }, // PJSIP_H_CONTENT_LANGUAGE,
- { "Content-Length", 14 }, // PJSIP_H_CONTENT_LENGTH,
- { "Content-Type", 12 }, // PJSIP_H_CONTENT_TYPE,
- { "CSeq", 4 }, // PJSIP_H_CSEQ,
- { "Date", 4 }, // PJSIP_H_DATE,
- { "Error-Info", 10 }, // PJSIP_H_ERROR_INFO,
- { "Expires", 7 }, // PJSIP_H_EXPIRES,
- { "From", 4 }, // PJSIP_H_FROM,
- { "In-Reply-To", 11 }, // PJSIP_H_IN_REPLY_TO,
- { "Max-Forwards", 12 }, // PJSIP_H_MAX_FORWARDS,
- { "MIME-Version", 12 }, // PJSIP_H_MIME_VERSION,
- { "Min-Expires", 11 }, // PJSIP_H_MIN_EXPIRES,
- { "Organization", 12 }, // PJSIP_H_ORGANIZATION,
- { "Priority", 8 }, // PJSIP_H_PRIORITY,
- { "Proxy-Authenticate", 18 }, // PJSIP_H_PROXY_AUTHENTICATE,
- { "Proxy-Authorization",19 }, // PJSIP_H_PROXY_AUTHORIZATION,
- { "Proxy-Require", 13 }, // PJSIP_H_PROXY_REQUIRE,
- { "Record-Route", 12 }, // PJSIP_H_RECORD_ROUTE,
- { "Reply-To", 8 }, // PJSIP_H_REPLY_TO,
- { "Require", 7 }, // PJSIP_H_REQUIRE,
- { "Retry-After", 11 }, // PJSIP_H_RETRY_AFTER,
- { "Route", 5 }, // PJSIP_H_ROUTE,
- { "Server", 6 }, // PJSIP_H_SERVER,
- { "Subject", 7 }, // PJSIP_H_SUBJECT,
- { "Supported", 9 }, // PJSIP_H_SUPPORTED,
- { "Timestamp", 9 }, // PJSIP_H_TIMESTAMP,
- { "To", 2 }, // PJSIP_H_TO,
- { "Unsupported", 11 }, // PJSIP_H_UNSUPPORTED,
- { "User-Agent", 10 }, // PJSIP_H_USER_AGENT,
- { "Via", 3 }, // PJSIP_H_VIA,
- { "Warning", 7 }, // PJSIP_H_WARNING,
- { "WWW-Authenticate", 16 }, // PJSIP_H_WWW_AUTHENTICATE,
-
- { "_Unknown-Header", 15 }, // PJSIP_H_OTHER,
-};
-
-static pj_str_t status_phrase[710];
-static int print_media_type(char *buf, const pjsip_media_type *media);
-
-static int init_status_phrase()
-{
- int i;
- pj_str_t default_reason_phrase = { "Default status message", 22};
-
- for (i=0; i<PJ_ARRAY_SIZE(status_phrase); ++i)
- status_phrase[i] = default_reason_phrase;
-
- pj_strset2( &status_phrase[100], "Trying");
- pj_strset2( &status_phrase[180], "Ringing");
- pj_strset2( &status_phrase[181], "Call Is Being Forwarded");
- pj_strset2( &status_phrase[182], "Queued");
- pj_strset2( &status_phrase[183], "Session Progress");
-
- pj_strset2( &status_phrase[200], "OK");
-
- pj_strset2( &status_phrase[300], "Multiple Choices");
- pj_strset2( &status_phrase[301], "Moved Permanently");
- pj_strset2( &status_phrase[302], "Moved Temporarily");
- pj_strset2( &status_phrase[305], "Use Proxy");
- pj_strset2( &status_phrase[380], "Alternative Service");
-
- pj_strset2( &status_phrase[400], "Bad Request");
- pj_strset2( &status_phrase[401], "Unauthorized");
- pj_strset2( &status_phrase[402], "Payment Required");
- pj_strset2( &status_phrase[403], "Forbidden");
- pj_strset2( &status_phrase[404], "Not Found");
- pj_strset2( &status_phrase[405], "Method Not Allowed");
- pj_strset2( &status_phrase[406], "Not Acceptable");
- pj_strset2( &status_phrase[407], "Proxy Authentication Required");
- pj_strset2( &status_phrase[408], "Request Timeout");
- pj_strset2( &status_phrase[410], "Gone");
- pj_strset2( &status_phrase[413], "Request Entity Too Large");
- pj_strset2( &status_phrase[414], "Request URI Too Long");
- pj_strset2( &status_phrase[415], "Unsupported Media Type");
- pj_strset2( &status_phrase[416], "Unsupported URI Scheme");
- pj_strset2( &status_phrase[420], "Bad Extension");
- pj_strset2( &status_phrase[421], "Extension Required");
- pj_strset2( &status_phrase[423], "Interval Too Brief");
- pj_strset2( &status_phrase[480], "Temporarily Unavailable");
- pj_strset2( &status_phrase[481], "Call/Transaction Does Not Exist");
- pj_strset2( &status_phrase[482], "Loop Detected");
- pj_strset2( &status_phrase[483], "Too Many Hops");
- pj_strset2( &status_phrase[484], "Address Incompleted");
- pj_strset2( &status_phrase[485], "Ambiguous");
- pj_strset2( &status_phrase[486], "Busy Here");
- pj_strset2( &status_phrase[487], "Request Terminated");
- pj_strset2( &status_phrase[488], "Not Acceptable Here");
- pj_strset2( &status_phrase[491], "Request Pending");
- pj_strset2( &status_phrase[493], "Undecipherable");
-
- pj_strset2( &status_phrase[500], "Internal Server Error");
- pj_strset2( &status_phrase[501], "Not Implemented");
- pj_strset2( &status_phrase[502], "Bad Gateway");
- pj_strset2( &status_phrase[503], "Service Unavailable");
- pj_strset2( &status_phrase[504], "Server Timeout");
- pj_strset2( &status_phrase[505], "Version Not Supported");
- pj_strset2( &status_phrase[513], "Message Too Large");
-
- pj_strset2( &status_phrase[600], "Busy Everywhere");
- pj_strset2( &status_phrase[603], "Decline");
- pj_strset2( &status_phrase[604], "Does Not Exist Anywhere");
- pj_strset2( &status_phrase[606], "Not Acceptable");
-
- pj_strset2( &status_phrase[701], "No response from destination server");
- pj_strset2( &status_phrase[702], "Unable to resolve destination server");
- pj_strset2( &status_phrase[703], "Error sending message to destination server");
-
- return 1;
-}
-
-///////////////////////////////////////////////////////////////////////////////
-/*
- * Method.
- */
-
-PJ_DEF(void) pjsip_method_init( pjsip_method *m,
- pj_pool_t *pool,
- const pj_str_t *str)
-{
- pj_str_t dup;
- pjsip_method_init_np(m, pj_strdup(pool, &dup, str));
-}
-
-PJ_DEF(void) pjsip_method_set( pjsip_method *m, pjsip_method_e me )
-{
- m->id = me;
- m->name = method_names[me];
-}
-
-PJ_DEF(void) pjsip_method_init_np(pjsip_method *m,
- pj_str_t *str)
-{
- int i;
- for (i=0; i<PJ_ARRAY_SIZE(method_names); ++i) {
- if (pj_stricmp(str, &method_names[i])==0) {
- m->id = (pjsip_method_e)i;
- m->name = method_names[i];
- return;
- }
- }
- m->id = PJSIP_OTHER_METHOD;
- m->name = *str;
-}
-
-PJ_DEF(void) pjsip_method_copy( pj_pool_t *pool,
- pjsip_method *method,
- const pjsip_method *rhs )
-{
- method->id = rhs->id;
- if (rhs->id != PJSIP_OTHER_METHOD) {
- method->name = rhs->name;
- } else {
- pj_strdup(pool, &method->name, &rhs->name);
- }
-}
-
-
-PJ_DEF(int) pjsip_method_cmp( const pjsip_method *m1, const pjsip_method *m2)
-{
- if (m1->id == m2->id) {
- if (m1->id != PJSIP_OTHER_METHOD)
- return 0;
- return pj_stricmp(&m1->name, &m2->name);
- }
-
- return ( m1->id < m2->id ) ? -1 : 1;
-}
-
-///////////////////////////////////////////////////////////////////////////////
-/*
- * Message.
- */
-
-PJ_DEF(pjsip_msg*) pjsip_msg_create( pj_pool_t *pool, pjsip_msg_type_e type)
-{
- pjsip_msg *msg = pj_pool_alloc(pool, sizeof(pjsip_msg));
- pj_list_init(&msg->hdr);
- msg->type = type;
- msg->body = NULL;
- return msg;
-}
-
-PJ_DEF(void*) pjsip_msg_find_hdr( pjsip_msg *msg,
- pjsip_hdr_e hdr_type, void *start)
-{
- pjsip_hdr *hdr=start, *end=&msg->hdr;
-
- if (hdr == NULL) {
- hdr = msg->hdr.next;
- }
- for (; hdr!=end; hdr = hdr->next) {
- if (hdr->type == hdr_type)
- return hdr;
- }
- return NULL;
-}
-
-PJ_DEF(void*) pjsip_msg_find_hdr_by_name( pjsip_msg *msg,
- const pj_str_t *name, void *start)
-{
- pjsip_hdr *hdr=start, *end=&msg->hdr;
-
- if (hdr == NULL) {
- hdr = msg->hdr.next;
- }
- for (; hdr!=end; hdr = hdr->next) {
- if (hdr->type < PJSIP_H_OTHER) {
- if (pj_stricmp(&pjsip_hdr_names[hdr->type], name) == 0)
- return hdr;
- } else {
- if (pj_stricmp(&hdr->name, name) == 0)
- return hdr;
- }
- }
- return NULL;
-}
-
-PJ_DEF(void*) pjsip_msg_find_remove_hdr( pjsip_msg *msg,
- pjsip_hdr_e hdr_type, void *start)
-{
- pjsip_hdr *hdr = pjsip_msg_find_hdr(msg, hdr_type, start);
- if (hdr) {
- pj_list_erase(hdr);
- }
- return hdr;
-}
-
-PJ_DEF(pj_ssize_t) pjsip_msg_print( pjsip_msg *msg, char *buf, pj_size_t size)
-{
- char *p=buf, *end=buf+size;
- int len;
- pjsip_hdr *hdr;
- pj_str_t clen_hdr = { "Content-Length: ", 16};
-
- /* Get a wild guess on how many bytes are typically needed.
- * We'll check this later in detail, but this serves as a quick check.
- */
- if (size < 256)
- return -1;
-
- /* Print request line or status line depending on message type */
- if (msg->type == PJSIP_REQUEST_MSG) {
- pjsip_uri *uri;
-
- /* Add method. */
- len = msg->line.req.method.name.slen;
- pj_memcpy(p, msg->line.req.method.name.ptr, len);
- p += len;
- *p++ = ' ';
-
- /* Add URI */
- uri = pjsip_uri_get_uri(msg->line.req.uri);
- len = pjsip_uri_print( PJSIP_URI_IN_REQ_URI, uri, p, end-p);
- if (len < 1)
- return -1;
- p += len;
-
- /* Add ' SIP/2.0' */
- if (end-p < 16)
- return -1;
- pj_memcpy(p, " SIP/2.0\r\n", 10);
- p += 10;
-
- } else {
-
- /* Add 'SIP/2.0 ' */
- pj_memcpy(p, "SIP/2.0 ", 8);
- p += 8;
-
- /* Add status code. */
- len = pj_utoa(msg->line.status.code, p);
- p += len;
- *p++ = ' ';
-
- /* Add reason text. */
- len = msg->line.status.reason.slen;
- pj_memcpy(p, msg->line.status.reason.ptr, len );
- p += len;
-
- /* Add newline. */
- *p++ = '\r';
- *p++ = '\n';
- }
-
- /* Print each of the headers. */
- for (hdr=msg->hdr.next; hdr!=&msg->hdr; hdr=hdr->next) {
- len = (*hdr->vptr->print_on)(hdr, p, end-p);
- if (len < 1)
- return -1;
- p += len;
-
- if (p+3 >= end)
- return -1;
-
- *p++ = '\r';
- *p++ = '\n';
- }
-
- /* Process message body. */
- if (msg->body) {
- pj_str_t ctype_hdr = { "Content-Type: ", 14};
- int len;
- const pjsip_media_type *media = &msg->body->content_type;
- char *clen_pos;
-
- /* Add Content-Type header. */
- if ( (end-p) < 24+media->type.slen+media->subtype.slen+media->param.slen) {
- return -1;
- }
- pj_memcpy(p, ctype_hdr.ptr, ctype_hdr.slen);
- p += ctype_hdr.slen;
- p += print_media_type(p, media);
- *p++ = '\r';
- *p++ = '\n';
-
- /* Add Content-Length header. */
- if ((end-p) < clen_hdr.slen+12+2) {
- return -1;
- }
- pj_memcpy(p, clen_hdr.ptr, clen_hdr.slen);
- p += clen_hdr.slen;
-
- /* Print blanks after "Content-Type:", this is where we'll put
- * the content length value after we know the length of the
- * body.
- */
- pj_memset(p, ' ', 12);
- clen_pos = p;
- p += 12;
- *p++ = '\r';
- *p++ = '\n';
-
- /* Add blank newline. */
- *p++ = '\r';
- *p++ = '\n';
-
- /* Print the message body itself. */
- len = (*msg->body->print_body)(msg->body, p, end-p);
- if (len < 0) {
- return -1;
- }
- p += len;
-
- /* Now that we have the length of the body, print this to the
- * Content-Length header.
- */
- len = pj_utoa(len, clen_pos);
- clen_pos[len] = ' ';
-
- } else {
- /* There's no message body.
- * Add Content-Length with zero value.
- */
- if ((end-p) < clen_hdr.slen+8) {
- return -1;
- }
- pj_memcpy(p, clen_hdr.ptr, clen_hdr.slen);
- p += clen_hdr.slen;
- *p++ = '0';
- *p++ = '\r';
- *p++ = '\n';
- *p++ = '\r';
- *p++ = '\n';
- }
-
- *p = '\0';
- return p-buf;
-}
-
-///////////////////////////////////////////////////////////////////////////////
-PJ_DEF(void*) pjsip_hdr_clone( pj_pool_t *pool, const void *hdr_ptr )
-{
- const pjsip_hdr *hdr = hdr_ptr;
- return (*hdr->vptr->clone)(pool, hdr_ptr);
-}
-
-
-PJ_DEF(void*) pjsip_hdr_shallow_clone( pj_pool_t *pool, const void *hdr_ptr )
-{
- const pjsip_hdr *hdr = hdr_ptr;
- return (*hdr->vptr->shallow_clone)(pool, hdr_ptr);
-}
-
-PJ_DEF(int) pjsip_hdr_print_on( void *hdr_ptr, char *buf, pj_size_t len)
-{
- pjsip_hdr *hdr = hdr_ptr;
- return (*hdr->vptr->print_on)(hdr_ptr, buf, len);
-}
-
-///////////////////////////////////////////////////////////////////////////////
-/*
- * Status/Reason Phrase
- */
-
-PJ_DEF(const pj_str_t*) pjsip_get_status_text(int code)
-{
- static int is_initialized;
- if (is_initialized == 0) {
- is_initialized = 1;
- init_status_phrase();
- }
-
- return (code>=100 && code<(sizeof(status_phrase)/sizeof(status_phrase[0]))) ?
- &status_phrase[code] : &status_phrase[0];
-}
-
-///////////////////////////////////////////////////////////////////////////////
-/*
- * Generic pjsip_hdr_names/hvalue header.
- */
-
-static int pjsip_generic_string_hdr_print( pjsip_generic_string_hdr *hdr,
- char *buf, pj_size_t size);
-static pjsip_generic_string_hdr* pjsip_generic_string_hdr_clone( pj_pool_t *pool,
- const pjsip_generic_string_hdr *hdr);
-static pjsip_generic_string_hdr* pjsip_generic_string_hdr_shallow_clone( pj_pool_t *pool,
- const pjsip_generic_string_hdr *hdr );
-
-static pjsip_hdr_vptr generic_hdr_vptr =
-{
- (pjsip_hdr_clone_fptr) &pjsip_generic_string_hdr_clone,
- (pjsip_hdr_clone_fptr) &pjsip_generic_string_hdr_shallow_clone,
- (pjsip_hdr_print_fptr) &pjsip_generic_string_hdr_print,
-};
-
-PJ_DEF(pjsip_generic_string_hdr*) pjsip_generic_string_hdr_create( pj_pool_t *pool,
- const pj_str_t *hnames )
-{
- pjsip_generic_string_hdr *hdr = pj_pool_alloc(pool, sizeof(pjsip_generic_string_hdr));
- init_hdr(hdr, PJSIP_H_OTHER, &generic_hdr_vptr);
- if (hnames) {
- pj_strdup(pool, &hdr->name, hnames);
- hdr->sname = hdr->name;
- }
- hdr->hvalue.ptr = NULL;
- hdr->hvalue.slen = 0;
- return hdr;
-}
-
-PJ_DEF(pjsip_generic_string_hdr*) pjsip_generic_string_hdr_create_with_text( pj_pool_t *pool,
- const pj_str_t *hname,
- const pj_str_t *hvalue)
-{
- pjsip_generic_string_hdr *hdr = pjsip_generic_string_hdr_create(pool, hname);
- pj_strdup(pool, &hdr->hvalue, hvalue);
- return hdr;
-}
-
-static int pjsip_generic_string_hdr_print( pjsip_generic_string_hdr *hdr,
- char *buf, pj_size_t size)
-{
- char *p = buf;
-
- if ((pj_ssize_t)size < hdr->name.slen + hdr->hvalue.slen + 5)
- return -1;
-
- pj_memcpy(p, hdr->name.ptr, hdr->name.slen);
- p += hdr->name.slen;
- *p++ = ':';
- *p++ = ' ';
- pj_memcpy(p, hdr->hvalue.ptr, hdr->hvalue.slen);
- p += hdr->hvalue.slen;
- *p = '\0';
-
- return p - buf;
-}
-
-static pjsip_generic_string_hdr* pjsip_generic_string_hdr_clone( pj_pool_t *pool,
- const pjsip_generic_string_hdr *rhs)
-{
- pjsip_generic_string_hdr *hdr = pjsip_generic_string_hdr_create(pool, &rhs->name);
-
- hdr->type = rhs->type;
- hdr->sname = hdr->name;
- pj_strdup( pool, &hdr->hvalue, &rhs->hvalue);
- return hdr;
-}
-
-static pjsip_generic_string_hdr* pjsip_generic_string_hdr_shallow_clone( pj_pool_t *pool,
- const pjsip_generic_string_hdr *rhs )
-{
- pjsip_generic_string_hdr *hdr = pj_pool_alloc(pool, sizeof(*hdr));
- pj_memcpy(hdr, rhs, sizeof(*hdr));
- return hdr;
-}
-
-///////////////////////////////////////////////////////////////////////////////
-/*
- * Generic pjsip_hdr_names/integer value header.
- */
-
-static int pjsip_generic_int_hdr_print( pjsip_generic_int_hdr *hdr,
- char *buf, pj_size_t size);
-static pjsip_generic_int_hdr* pjsip_generic_int_hdr_clone( pj_pool_t *pool,
- const pjsip_generic_int_hdr *hdr);
-static pjsip_generic_int_hdr* pjsip_generic_int_hdr_shallow_clone( pj_pool_t *pool,
- const pjsip_generic_int_hdr *hdr );
-
-static pjsip_hdr_vptr generic_int_hdr_vptr =
-{
- (pjsip_hdr_clone_fptr) &pjsip_generic_int_hdr_clone,
- (pjsip_hdr_clone_fptr) &pjsip_generic_int_hdr_shallow_clone,
- (pjsip_hdr_print_fptr) &pjsip_generic_int_hdr_print,
-};
-
-PJ_DEF(pjsip_generic_int_hdr*) pjsip_generic_int_hdr_create( pj_pool_t *pool,
- const pj_str_t *hnames )
-{
- pjsip_generic_int_hdr *hdr = pj_pool_alloc(pool, sizeof(pjsip_generic_int_hdr));
- init_hdr(hdr, PJSIP_H_OTHER, &generic_int_hdr_vptr);
- if (hnames) {
- pj_strdup(pool, &hdr->name, hnames);
- hdr->sname = hdr->name;
- }
- hdr->ivalue = 0;
- return hdr;
-}
-
-PJ_DEF(pjsip_generic_int_hdr*) pjsip_generic_int_hdr_create_with_value( pj_pool_t *pool,
- const pj_str_t *hname,
- int value)
-{
- pjsip_generic_int_hdr *hdr = pjsip_generic_int_hdr_create(pool, hname);
- hdr->ivalue = value;
- return hdr;
-}
-
-static int pjsip_generic_int_hdr_print( pjsip_generic_int_hdr *hdr,
- char *buf, pj_size_t size)
-{
- char *p = buf;
-
- if ((pj_ssize_t)size < hdr->name.slen + 15)
- return -1;
-
- pj_memcpy(p, hdr->name.ptr, hdr->name.slen);
- p += hdr->name.slen;
- *p++ = ':';
- *p++ = ' ';
-
- p += pj_utoa(hdr->ivalue, p);
-
- return p - buf;
-}
-
-static pjsip_generic_int_hdr* pjsip_generic_int_hdr_clone( pj_pool_t *pool,
- const pjsip_generic_int_hdr *rhs)
-{
- pjsip_generic_int_hdr *hdr = pj_pool_alloc(pool, sizeof(*hdr));
- pj_memcpy(hdr, rhs, sizeof(*hdr));
- return hdr;
-}
-
-static pjsip_generic_int_hdr* pjsip_generic_int_hdr_shallow_clone( pj_pool_t *pool,
- const pjsip_generic_int_hdr *rhs )
-{
- pjsip_generic_int_hdr *hdr = pj_pool_alloc(pool, sizeof(*hdr));
- pj_memcpy(hdr, rhs, sizeof(*hdr));
- return hdr;
-}
-
-///////////////////////////////////////////////////////////////////////////////
-/*
- * Generic array header.
- */
-static int pjsip_generic_array_hdr_print( pjsip_generic_array_hdr *hdr, char *buf, pj_size_t size);
-static pjsip_generic_array_hdr* pjsip_generic_array_hdr_clone( pj_pool_t *pool,
- const pjsip_generic_array_hdr *hdr);
-static pjsip_generic_array_hdr* pjsip_generic_array_hdr_shallow_clone( pj_pool_t *pool,
- const pjsip_generic_array_hdr *hdr);
-
-static pjsip_hdr_vptr generic_array_hdr_vptr =
-{
- (pjsip_hdr_clone_fptr) &pjsip_generic_array_hdr_clone,
- (pjsip_hdr_clone_fptr) &pjsip_generic_array_hdr_shallow_clone,
- (pjsip_hdr_print_fptr) &pjsip_generic_array_hdr_print,
-};
-
-PJ_DEF(pjsip_generic_array_hdr*) pjsip_generic_array_create( pj_pool_t *pool,
- const pj_str_t *hnames)
-{
- pjsip_generic_array_hdr *hdr = pj_pool_alloc(pool, sizeof(pjsip_generic_array_hdr));
- init_hdr(hdr, PJSIP_H_OTHER, &generic_array_hdr_vptr);
- if (hnames) {
- pj_strdup(pool, &hdr->name, hnames);
- hdr->sname = hdr->name;
- }
- hdr->count = 0;
- return hdr;
-
-}
-
-static int pjsip_generic_array_hdr_print( pjsip_generic_array_hdr *hdr,
- char *buf, pj_size_t size)
-{
- char *p = buf, *endbuf = buf+size;
-
- copy_advance(p, hdr->name);
- *p++ = ':';
- *p++ = ' ';
-
- if (hdr->count > 0) {
- unsigned i;
- int printed;
- copy_advance(p, hdr->values[0]);
- for (i=1; i<hdr->count; ++i) {
- copy_advance_pair(p, ", ", 2, hdr->values[i]);
- }
- }
-
- return p - buf;
-}
-
-static pjsip_generic_array_hdr* pjsip_generic_array_hdr_clone( pj_pool_t *pool,
- const pjsip_generic_array_hdr *rhs)
-{
- unsigned i;
- pjsip_generic_array_hdr *hdr = pj_pool_alloc(pool, sizeof(*hdr));
-
- pj_memcpy(hdr, rhs, sizeof(*hdr));
- for (i=0; i<rhs->count; ++i) {
- pj_strdup(pool, &hdr->values[i], &rhs->values[i]);
- }
-
- return hdr;
-}
-
-
-static pjsip_generic_array_hdr* pjsip_generic_array_hdr_shallow_clone( pj_pool_t *pool,
- const pjsip_generic_array_hdr *rhs)
-{
- pjsip_generic_array_hdr *hdr = pj_pool_alloc(pool, sizeof(*hdr));
- pj_memcpy(hdr, rhs, sizeof(*hdr));
- return hdr;
-}
-
-///////////////////////////////////////////////////////////////////////////////
-/*
- * Accept header.
- */
-PJ_DEF(pjsip_accept_hdr*) pjsip_accept_hdr_create(pj_pool_t *pool)
-{
- pjsip_accept_hdr *hdr = pj_pool_alloc(pool, sizeof(*hdr));
- init_hdr(hdr, PJSIP_H_ACCEPT, &generic_array_hdr_vptr);
- hdr->count = 0;
- return hdr;
-}
-
-
-///////////////////////////////////////////////////////////////////////////////
-/*
- * Allow header.
- */
-
-PJ_DEF(pjsip_allow_hdr*) pjsip_allow_hdr_create(pj_pool_t *pool)
-{
- pjsip_allow_hdr *hdr = pj_pool_alloc(pool, sizeof(*hdr));
- init_hdr(hdr, PJSIP_H_ALLOW, &generic_array_hdr_vptr);
- hdr->count = 0;
- return hdr;
-}
-
-///////////////////////////////////////////////////////////////////////////////
-/*
- * Call-ID header.
- */
-
-PJ_DEF(pjsip_cid_hdr*) pjsip_cid_hdr_create( pj_pool_t *pool )
-{
- pjsip_cid_hdr *hdr = pj_pool_alloc(pool, sizeof(*hdr));
- init_hdr(hdr, PJSIP_H_CALL_ID, &generic_hdr_vptr);
- return hdr;
-}
-
-
-///////////////////////////////////////////////////////////////////////////////
-/*
- * Content-Length header.
- */
-static int pjsip_clen_hdr_print( pjsip_clen_hdr *hdr, char *buf, pj_size_t size);
-static pjsip_clen_hdr* pjsip_clen_hdr_clone( pj_pool_t *pool, const pjsip_clen_hdr *hdr);
-#define pjsip_clen_hdr_shallow_clone pjsip_clen_hdr_clone
-
-static pjsip_hdr_vptr clen_hdr_vptr =
-{
- (pjsip_hdr_clone_fptr) &pjsip_clen_hdr_clone,
- (pjsip_hdr_clone_fptr) &pjsip_clen_hdr_shallow_clone,
- (pjsip_hdr_print_fptr) &pjsip_clen_hdr_print,
-};
-
-PJ_DEF(pjsip_clen_hdr*) pjsip_clen_hdr_create( pj_pool_t *pool )
-{
- pjsip_clen_hdr *hdr = pj_pool_alloc(pool, sizeof(pjsip_clen_hdr));
- init_hdr(hdr, PJSIP_H_CONTENT_LENGTH, &clen_hdr_vptr);
- hdr->len = 0;
- return hdr;
-}
-
-static int pjsip_clen_hdr_print( pjsip_clen_hdr *hdr,
- char *buf, pj_size_t size)
-{
- char *p = buf;
- int len;
-
- if ((pj_ssize_t)size < hdr->name.slen + 14)
- return -1;
-
- pj_memcpy(p, hdr->name.ptr, hdr->name.slen);
- p += hdr->name.slen;
- *p++ = ':';
- *p++ = ' ';
-
- len = pj_utoa(hdr->len, p);
- p += len;
- *p = '\0';
-
- return p-buf;
-}
-
-static pjsip_clen_hdr* pjsip_clen_hdr_clone( pj_pool_t *pool, const pjsip_clen_hdr *rhs)
-{
- pjsip_clen_hdr *hdr = pjsip_clen_hdr_create(pool);
- hdr->len = rhs->len;
- return hdr;
-}
-
-
-///////////////////////////////////////////////////////////////////////////////
-/*
- * CSeq header.
- */
-static int pjsip_cseq_hdr_print( pjsip_cseq_hdr *hdr, char *buf, pj_size_t size);
-static pjsip_cseq_hdr* pjsip_cseq_hdr_clone( pj_pool_t *pool, const pjsip_cseq_hdr *hdr);
-static pjsip_cseq_hdr* pjsip_cseq_hdr_shallow_clone( pj_pool_t *pool, const pjsip_cseq_hdr *hdr );
-
-static pjsip_hdr_vptr cseq_hdr_vptr =
-{
- (pjsip_hdr_clone_fptr) &pjsip_cseq_hdr_clone,
- (pjsip_hdr_clone_fptr) &pjsip_cseq_hdr_shallow_clone,
- (pjsip_hdr_print_fptr) &pjsip_cseq_hdr_print,
-};
-
-PJ_DEF(pjsip_cseq_hdr*) pjsip_cseq_hdr_create( pj_pool_t *pool )
-{
- pjsip_cseq_hdr *hdr = pj_pool_alloc(pool, sizeof(pjsip_cseq_hdr));
- init_hdr(hdr, PJSIP_H_CSEQ, &cseq_hdr_vptr);
- hdr->cseq = 0;
- hdr->method.id = PJSIP_OTHER_METHOD;
- hdr->method.name.ptr = NULL;
- hdr->method.name.slen = 0;
- return hdr;
-}
-
-static int pjsip_cseq_hdr_print( pjsip_cseq_hdr *hdr, char *buf, pj_size_t size)
-{
- char *p = buf;
- int len;
-
- if ((pj_ssize_t)size < hdr->name.slen + hdr->method.name.slen + 15)
- return -1;
-
- pj_memcpy(p, hdr->name.ptr, hdr->name.slen);
- p += hdr->name.slen;
- *p++ = ':';
- *p++ = ' ';
-
- len = pj_utoa(hdr->cseq, p);
- p += len;
- *p++ = ' ';
-
- pj_memcpy(p, hdr->method.name.ptr, hdr->method.name.slen);
- p += hdr->method.name.slen;
-
- *p = '\0';
-
- return p-buf;
-}
-
-static pjsip_cseq_hdr* pjsip_cseq_hdr_clone( pj_pool_t *pool,
- const pjsip_cseq_hdr *rhs)
-{
- pjsip_cseq_hdr *hdr = pjsip_cseq_hdr_create(pool);
- hdr->cseq = rhs->cseq;
- pjsip_method_copy(pool, &hdr->method, &rhs->method);
- return hdr;
-}
-
-static pjsip_cseq_hdr* pjsip_cseq_hdr_shallow_clone( pj_pool_t *pool,
- const pjsip_cseq_hdr *rhs )
-{
- pjsip_cseq_hdr *hdr = pj_pool_alloc(pool, sizeof(*hdr));
- pj_memcpy(hdr, rhs, sizeof(*hdr));
- return hdr;
-}
-
-
-///////////////////////////////////////////////////////////////////////////////
-/*
- * Contact header.
- */
-static int pjsip_contact_hdr_print( pjsip_contact_hdr *hdr, char *buf, pj_size_t size);
-static pjsip_contact_hdr* pjsip_contact_hdr_clone( pj_pool_t *pool, const pjsip_contact_hdr *hdr);
-static pjsip_contact_hdr* pjsip_contact_hdr_shallow_clone( pj_pool_t *pool, const pjsip_contact_hdr *);
-
-static pjsip_hdr_vptr contact_hdr_vptr =
-{
- (pjsip_hdr_clone_fptr) &pjsip_contact_hdr_clone,
- (pjsip_hdr_clone_fptr) &pjsip_contact_hdr_shallow_clone,
- (pjsip_hdr_print_fptr) &pjsip_contact_hdr_print,
-};
-
-PJ_DEF(pjsip_contact_hdr*) pjsip_contact_hdr_create( pj_pool_t *pool )
-{
- pjsip_contact_hdr *hdr = pj_pool_calloc(pool, 1, sizeof(*hdr));
- init_hdr(hdr, PJSIP_H_CONTACT, &contact_hdr_vptr);
- hdr->expires = -1;
- return hdr;
-}
-
-static int pjsip_contact_hdr_print( pjsip_contact_hdr *hdr, char *buf,
- pj_size_t size)
-{
- if (hdr->star) {
- char *p = buf;
- if ((pj_ssize_t)size < hdr->name.slen + 6)
- return -1;
- pj_memcpy(p, hdr->name.ptr, hdr->name.slen);
- p += hdr->name.slen;
- *p++ = ':';
- *p++ = ' ';
- *p++ = '*';
- *p = '\0';
- return p - buf;
-
- } else {
- int printed;
- char *startbuf = buf;
- char *endbuf = buf + size;
-
- copy_advance(buf, hdr->name);
- *buf++ = ':';
- *buf++ = ' ';
-
- printed = pjsip_uri_print(PJSIP_URI_IN_CONTACT_HDR, hdr->uri,
- buf, endbuf-buf);
- if (printed < 1)
- return -1;
-
- buf += printed;
-
- if (hdr->q1000) {
- if (buf+19 >= endbuf)
- return -1;
-
- /*
- printed = sprintf(buf, ";q=%u.%03u",
- hdr->q1000/1000, hdr->q1000 % 1000);
- */
- pj_memcpy(buf, ";q=", 3);
- printed = pj_utoa(hdr->q1000/1000, buf+3);
- buf += printed + 3;
- *buf++ = '.';
- printed = pj_utoa(hdr->q1000 % 1000, buf);
- buf += printed;
- }
-
- if (hdr->expires >= 0) {
- if (buf+23 >= endbuf)
- return -1;
-
- pj_memcpy(buf, ";expires=", 9);
- printed = pj_utoa(hdr->expires, buf+9);
- buf += printed + 9;
- }
-
- if (hdr->other_param.slen) {
- copy_advance(buf, hdr->other_param);
- }
-
- *buf = '\0';
- return buf-startbuf;
- }
-}
-
-static pjsip_contact_hdr* pjsip_contact_hdr_clone( pj_pool_t *pool,
- const pjsip_contact_hdr *rhs)
-{
- pjsip_contact_hdr *hdr = pjsip_contact_hdr_create(pool);
-
- hdr->star = rhs->star;
- if (hdr->star)
- return hdr;
-
- hdr->uri = pjsip_uri_clone(pool, rhs->uri);
- hdr->q1000 = rhs->q1000;
- hdr->expires = rhs->expires;
- pj_strdup(pool, &hdr->other_param, &rhs->other_param);
- return hdr;
-}
-
-static pjsip_contact_hdr* pjsip_contact_hdr_shallow_clone( pj_pool_t *pool,
- const pjsip_contact_hdr *rhs)
-{
- pjsip_contact_hdr *hdr = pj_pool_alloc(pool, sizeof(*hdr));
- pj_memcpy(hdr, rhs, sizeof(*hdr));
- return hdr;
-}
-
-///////////////////////////////////////////////////////////////////////////////
-/*
- * Content-Type header..
- */
-static int pjsip_ctype_hdr_print( pjsip_ctype_hdr *hdr, char *buf, pj_size_t size);
-static pjsip_ctype_hdr* pjsip_ctype_hdr_clone( pj_pool_t *pool, const pjsip_ctype_hdr *hdr);
-#define pjsip_ctype_hdr_shallow_clone pjsip_ctype_hdr_clone
-
-static pjsip_hdr_vptr ctype_hdr_vptr =
-{
- (pjsip_hdr_clone_fptr) &pjsip_ctype_hdr_clone,
- (pjsip_hdr_clone_fptr) &pjsip_ctype_hdr_shallow_clone,
- (pjsip_hdr_print_fptr) &pjsip_ctype_hdr_print,
-};
-
-PJ_DEF(pjsip_ctype_hdr*) pjsip_ctype_hdr_create( pj_pool_t *pool )
-{
- pjsip_ctype_hdr *hdr = pj_pool_calloc(pool, 1, sizeof(*hdr));
- init_hdr(hdr, PJSIP_H_CONTENT_TYPE, &ctype_hdr_vptr);
- return hdr;
-}
-
-static int print_media_type(char *buf, const pjsip_media_type *media)
-{
- char *p = buf;
-
- pj_memcpy(p, media->type.ptr, media->type.slen);
- p += media->type.slen;
- *p++ = '/';
- pj_memcpy(p, media->subtype.ptr, media->subtype.slen);
- p += media->subtype.slen;
-
- if (media->param.slen) {
- pj_memcpy(p, media->param.ptr, media->param.slen);
- p += media->param.slen;
- }
-
- return p-buf;
-}
-
-static int pjsip_ctype_hdr_print( pjsip_ctype_hdr *hdr,
- char *buf, pj_size_t size)
-{
- char *p = buf;
- int len;
-
- if ((pj_ssize_t)size < hdr->name.slen +
- hdr->media.type.slen + hdr->media.subtype.slen +
- hdr->media.param.slen + 8)
- {
- return -1;
- }
-
- pj_memcpy(p, hdr->name.ptr, hdr->name.slen);
- p += hdr->name.slen;
- *p++ = ':';
- *p++ = ' ';
-
- len = print_media_type(p, &hdr->media);
- p += len;
-
- *p = '\0';
- return p-buf;
-}
-
-static pjsip_ctype_hdr* pjsip_ctype_hdr_clone( pj_pool_t *pool,
- const pjsip_ctype_hdr *rhs)
-{
- pjsip_ctype_hdr *hdr = pjsip_ctype_hdr_create(pool);
- pj_strdup(pool, &hdr->media.type, &rhs->media.type);
- pj_strdup(pool, &hdr->media.param, &rhs->media.param);
- return hdr;
-}
-
-
-///////////////////////////////////////////////////////////////////////////////
-/*
- * Expires header.
- */
-PJ_DEF(pjsip_expires_hdr*) pjsip_expires_hdr_create( pj_pool_t *pool )
-{
- pjsip_expires_hdr *hdr = pj_pool_alloc(pool, sizeof(*hdr));
- init_hdr(hdr, PJSIP_H_EXPIRES, &generic_int_hdr_vptr);
- hdr->ivalue = 0;
- return hdr;
-}
-
-///////////////////////////////////////////////////////////////////////////////
-/*
- * To or From header.
- */
-static int pjsip_fromto_hdr_print( pjsip_fromto_hdr *hdr,
- char *buf, pj_size_t size);
-static pjsip_fromto_hdr* pjsip_fromto_hdr_clone( pj_pool_t *pool,
- const pjsip_fromto_hdr *hdr);
-static pjsip_fromto_hdr* pjsip_fromto_hdr_shallow_clone( pj_pool_t *pool,
- const pjsip_fromto_hdr *hdr);
-
-
-static pjsip_hdr_vptr fromto_hdr_vptr =
-{
- (pjsip_hdr_clone_fptr) &pjsip_fromto_hdr_clone,
- (pjsip_hdr_clone_fptr) &pjsip_fromto_hdr_shallow_clone,
- (pjsip_hdr_print_fptr) &pjsip_fromto_hdr_print,
-};
-
-PJ_DEF(pjsip_from_hdr*) pjsip_from_hdr_create( pj_pool_t *pool )
-{
- pjsip_from_hdr *hdr = pj_pool_calloc(pool, 1, sizeof(*hdr));
- init_hdr(hdr, PJSIP_H_FROM, &fromto_hdr_vptr);
- return hdr;
-}
-
-PJ_DEF(pjsip_to_hdr*) pjsip_to_hdr_create( pj_pool_t *pool )
-{
- pjsip_to_hdr *hdr = pj_pool_calloc(pool, 1, sizeof(*hdr));
- init_hdr(hdr, PJSIP_H_TO, &fromto_hdr_vptr);
- return hdr;
-}
-
-PJ_DEF(pjsip_from_hdr*) pjsip_fromto_set_from( pjsip_fromto_hdr *hdr )
-{
- hdr->type = PJSIP_H_FROM;
- hdr->name = hdr->sname = pjsip_hdr_names[PJSIP_H_FROM];
- return hdr;
-}
-
-PJ_DEF(pjsip_to_hdr*) pjsip_fromto_set_to( pjsip_fromto_hdr *hdr )
-{
- hdr->type = PJSIP_H_TO;
- hdr->name = hdr->sname = pjsip_hdr_names[PJSIP_H_TO];
- return hdr;
-}
-
-static int pjsip_fromto_hdr_print( pjsip_fromto_hdr *hdr,
- char *buf, pj_size_t size)
-{
- int printed;
- char *startbuf = buf;
- char *endbuf = buf + size;
-
- copy_advance(buf, hdr->name);
- *buf++ = ':';
- *buf++ = ' ';
-
- printed = pjsip_uri_print(PJSIP_URI_IN_FROMTO_HDR, hdr->uri, buf, endbuf-buf);
- if (printed < 1)
- return -1;
-
- buf += printed;
-
- copy_advance_pair(buf, ";tag=", 5, hdr->tag);
- if (hdr->other_param.slen)
- copy_advance(buf, hdr->other_param);
-
- return buf-startbuf;
-}
-
-static pjsip_fromto_hdr* pjsip_fromto_hdr_clone( pj_pool_t *pool,
- const pjsip_fromto_hdr *rhs)
-{
- pjsip_fromto_hdr *hdr = pjsip_from_hdr_create(pool);
-
- hdr->type = rhs->type;
- hdr->name = rhs->name;
- hdr->sname = rhs->sname;
- hdr->uri = pjsip_uri_clone(pool, rhs->uri);
- pj_strdup( pool, &hdr->tag, &rhs->tag);
- pj_strdup( pool, &hdr->other_param, &rhs->other_param);
-
- return hdr;
-}
-
-static pjsip_fromto_hdr* pjsip_fromto_hdr_shallow_clone( pj_pool_t *pool,
- const pjsip_fromto_hdr *rhs)
-{
- pjsip_fromto_hdr *hdr = pj_pool_alloc(pool, sizeof(*hdr));
- pj_memcpy(hdr, rhs, sizeof(*hdr));
- return hdr;
-}
-
-
-///////////////////////////////////////////////////////////////////////////////
-/*
- * Max-Forwards header.
- */
-PJ_DEF(pjsip_max_forwards_hdr*) pjsip_max_forwards_hdr_create(pj_pool_t *pool)
-{
- pjsip_max_forwards_hdr *hdr = pj_pool_alloc(pool, sizeof(*hdr));
- init_hdr(hdr, PJSIP_H_MAX_FORWARDS, &generic_int_hdr_vptr);
- hdr->ivalue = 0;
- return hdr;
-}
-
-
-///////////////////////////////////////////////////////////////////////////////
-/*
- * Min-Expires header.
- */
-PJ_DECL(pjsip_min_expires_hdr*) pjsip_min_expires_hdr_create(pj_pool_t *pool)
-{
- pjsip_min_expires_hdr *hdr = pj_pool_alloc(pool, sizeof(*hdr));
- init_hdr(hdr, PJSIP_H_MIN_EXPIRES, &generic_int_hdr_vptr);
- hdr->ivalue = 0;
- return hdr;
-}
-
-///////////////////////////////////////////////////////////////////////////////
-/*
- * Record-Route and Route header.
- */
-static int pjsip_routing_hdr_print( pjsip_routing_hdr *r, char *buf, pj_size_t size );
-static pjsip_routing_hdr* pjsip_routing_hdr_clone( pj_pool_t *pool, const pjsip_routing_hdr *r );
-static pjsip_routing_hdr* pjsip_routing_hdr_shallow_clone( pj_pool_t *pool, const pjsip_routing_hdr *r );
-
-static pjsip_hdr_vptr routing_hdr_vptr =
-{
- (pjsip_hdr_clone_fptr) &pjsip_routing_hdr_clone,
- (pjsip_hdr_clone_fptr) &pjsip_routing_hdr_shallow_clone,
- (pjsip_hdr_print_fptr) &pjsip_routing_hdr_print,
-};
-
-PJ_DEF(pjsip_rr_hdr*) pjsip_rr_hdr_create( pj_pool_t *pool )
-{
- pjsip_rr_hdr *hdr = pj_pool_alloc(pool, sizeof(*hdr));
- init_hdr(hdr, PJSIP_H_RECORD_ROUTE, &routing_hdr_vptr);
- pjsip_name_addr_init(&hdr->name_addr);
- pj_memset(&hdr->other_param, 0, sizeof(hdr->other_param));
- return hdr;
-}
-
-PJ_DEF(pjsip_route_hdr*) pjsip_route_hdr_create( pj_pool_t *pool )
-{
- pjsip_route_hdr *hdr = pj_pool_alloc(pool, sizeof(*hdr));
- init_hdr(hdr, PJSIP_H_ROUTE, &routing_hdr_vptr);
- pjsip_name_addr_init(&hdr->name_addr);
- pj_memset(&hdr->other_param, 0, sizeof(hdr->other_param));
- return hdr;
-}
-
-PJ_DEF(pjsip_rr_hdr*) pjsip_routing_hdr_set_rr( pjsip_routing_hdr *hdr )
-{
- hdr->type = PJSIP_H_RECORD_ROUTE;
- hdr->name = hdr->sname = pjsip_hdr_names[PJSIP_H_RECORD_ROUTE];
- return hdr;
-}
-
-PJ_DEF(pjsip_route_hdr*) pjsip_routing_hdr_set_route( pjsip_routing_hdr *hdr )
-{
- hdr->type = PJSIP_H_ROUTE;
- hdr->name = hdr->sname = pjsip_hdr_names[PJSIP_H_ROUTE];
- return hdr;
-}
-
-static int pjsip_routing_hdr_print( pjsip_routing_hdr *hdr,
- char *buf, pj_size_t size )
-{
- int printed;
- char *startbuf = buf;
- char *endbuf = buf + size;
-
- copy_advance(buf, hdr->name);
- *buf++ = ':';
- *buf++ = ' ';
-
- printed = pjsip_uri_print(PJSIP_URI_IN_ROUTING_HDR, &hdr->name_addr, buf, endbuf-buf);
- if (printed < 1)
- return -1;
- buf += printed;
-
- if (hdr->other_param.slen) {
- copy_advance(buf, hdr->other_param);
- }
-
- return buf-startbuf;
-}
-
-static pjsip_routing_hdr* pjsip_routing_hdr_clone( pj_pool_t *pool,
- const pjsip_routing_hdr *rhs )
-{
- pjsip_routing_hdr *hdr = pj_pool_alloc(pool, sizeof(*hdr));
-
- init_hdr(hdr, rhs->type, rhs->vptr);
- pjsip_name_addr_init(&hdr->name_addr);
- pjsip_name_addr_assign(pool, &hdr->name_addr, &rhs->name_addr);
- pj_strdup( pool, &hdr->other_param, &rhs->other_param);
- return hdr;
-}
-
-static pjsip_routing_hdr* pjsip_routing_hdr_shallow_clone( pj_pool_t *pool,
- const pjsip_routing_hdr *rhs )
-{
- pjsip_routing_hdr *hdr = pj_pool_alloc(pool, sizeof(*hdr));
- pj_memcpy(hdr, rhs, sizeof(*hdr));
- return hdr;
-}
-
-
-///////////////////////////////////////////////////////////////////////////////
-/*
- * Require header.
- */
-PJ_DEF(pjsip_require_hdr*) pjsip_require_hdr_create(pj_pool_t *pool)
-{
- pjsip_require_hdr *hdr = pj_pool_alloc(pool, sizeof(*hdr));
- init_hdr(hdr, PJSIP_H_REQUIRE, &generic_array_hdr_vptr);
- hdr->count = 0;
- return hdr;
-}
-
-///////////////////////////////////////////////////////////////////////////////
-/*
- * Retry-After header.
- */
-PJ_DEF(pjsip_retry_after_hdr*) pjsip_retry_after_hdr_create(pj_pool_t *pool)
-{
- pjsip_retry_after_hdr *hdr = pj_pool_alloc(pool, sizeof(*hdr));
- init_hdr(hdr, PJSIP_H_RETRY_AFTER, &generic_int_hdr_vptr);
- hdr->ivalue = 0;
- return hdr;
-}
-
-
-///////////////////////////////////////////////////////////////////////////////
-/*
- * Supported header.
- */
-PJ_DEF(pjsip_supported_hdr*) pjsip_supported_hdr_create(pj_pool_t *pool)
-{
- pjsip_supported_hdr *hdr = pj_pool_alloc(pool, sizeof(*hdr));
- init_hdr(hdr, PJSIP_H_SUPPORTED, &generic_array_hdr_vptr);
- hdr->count = 0;
- return hdr;
-}
-
-
-///////////////////////////////////////////////////////////////////////////////
-/*
- * Unsupported header.
- */
-PJ_DEF(pjsip_unsupported_hdr*) pjsip_unsupported_hdr_create(pj_pool_t *pool)
-{
- pjsip_unsupported_hdr *hdr = pj_pool_alloc(pool, sizeof(*hdr));
- init_hdr(hdr, PJSIP_H_UNSUPPORTED, &generic_array_hdr_vptr);
- hdr->count = 0;
- return hdr;
-}
-
-///////////////////////////////////////////////////////////////////////////////
-/*
- * Via header.
- */
-static int pjsip_via_hdr_print( pjsip_via_hdr *hdr, char *buf, pj_size_t size);
-static pjsip_via_hdr* pjsip_via_hdr_clone( pj_pool_t *pool, const pjsip_via_hdr *hdr);
-static pjsip_via_hdr* pjsip_via_hdr_shallow_clone( pj_pool_t *pool, const pjsip_via_hdr *hdr );
-
-static pjsip_hdr_vptr via_hdr_vptr =
-{
- (pjsip_hdr_clone_fptr) &pjsip_via_hdr_clone,
- (pjsip_hdr_clone_fptr) &pjsip_via_hdr_shallow_clone,
- (pjsip_hdr_print_fptr) &pjsip_via_hdr_print,
-};
-
-PJ_DEF(pjsip_via_hdr*) pjsip_via_hdr_create( pj_pool_t *pool )
-{
- pjsip_via_hdr *hdr = pj_pool_calloc(pool, 1, sizeof(*hdr));
- init_hdr(hdr, PJSIP_H_VIA, &via_hdr_vptr);
- hdr->sent_by.port = 5060;
- hdr->ttl_param = -1;
- hdr->rport_param = -1;
- return hdr;
-}
-
-static int pjsip_via_hdr_print( pjsip_via_hdr *hdr,
- char *buf, pj_size_t size)
-{
- int printed;
- char *startbuf = buf;
- char *endbuf = buf + size;
- pj_str_t sip_ver = { "SIP/2.0/", 8 };
-
- if ((pj_ssize_t)size < hdr->name.slen + sip_ver.slen +
- hdr->transport.slen + hdr->sent_by.host.slen + 12)
- {
- return -1;
- }
-
- /* pjsip_hdr_names */
- copy_advance(buf, hdr->name);
- *buf++ = ':';
- *buf++ = ' ';
-
- /* SIP/2.0/transport host:port */
- pj_memcpy(buf, sip_ver.ptr, sip_ver.slen);
- buf += sip_ver.slen;
- pj_memcpy(buf, hdr->transport.ptr, hdr->transport.slen);
- buf += hdr->transport.slen;
- *buf++ = ' ';
- pj_memcpy(buf, hdr->sent_by.host.ptr, hdr->sent_by.host.slen);
- buf += hdr->sent_by.host.slen;
- *buf++ = ':';
- printed = pj_utoa(hdr->sent_by.port, buf);
- buf += printed;
-
- if (hdr->ttl_param >= 0) {
- size = endbuf-buf;
- if (size < 14)
- return -1;
- pj_memcpy(buf, ";ttl=", 5);
- printed = pj_utoa(hdr->ttl_param, buf+5);
- buf += printed + 5;
- }
-
- if (hdr->rport_param >= 0) {
- size = endbuf-buf;
- if (size < 14)
- return -1;
- pj_memcpy(buf, ";rport", 6);
- buf += 6;
- if (hdr->rport_param > 0) {
- *buf++ = '=';
- buf += pj_utoa(hdr->rport_param, buf);
- }
- }
-
-
- copy_advance_pair(buf, ";maddr=", 7, hdr->maddr_param);
- copy_advance_pair(buf, ";received=", 10, hdr->recvd_param);
- copy_advance_pair(buf, ";branch=", 8, hdr->branch_param);
- copy_advance(buf, hdr->other_param);
-
- *buf = '\0';
- return buf-startbuf;
-}
-
-static pjsip_via_hdr* pjsip_via_hdr_clone( pj_pool_t *pool,
- const pjsip_via_hdr *rhs)
-{
- pjsip_via_hdr *hdr = pjsip_via_hdr_create(pool);
- pj_strdup(pool, &hdr->transport, &rhs->transport);
- pj_strdup(pool, &hdr->sent_by.host, &rhs->sent_by.host);
- hdr->sent_by.port = rhs->sent_by.port;
- hdr->ttl_param = rhs->ttl_param;
- hdr->rport_param = rhs->rport_param;
- pj_strdup(pool, &hdr->maddr_param, &rhs->maddr_param);
- pj_strdup(pool, &hdr->recvd_param, &rhs->recvd_param);
- pj_strdup(pool, &hdr->branch_param, &rhs->branch_param);
- pj_strdup(pool, &hdr->other_param, &rhs->other_param);
- return hdr;
-}
-
-static pjsip_via_hdr* pjsip_via_hdr_shallow_clone( pj_pool_t *pool,
- const pjsip_via_hdr *rhs )
-{
- pjsip_via_hdr *hdr = pj_pool_alloc(pool, sizeof(*hdr));
- pj_memcpy(hdr, rhs, sizeof(*hdr));
- return hdr;
-}
-
-///////////////////////////////////////////////////////////////////////////////
-/*
- * General purpose function to textual data in a SIP body.
- */
-PJ_DEF(int) pjsip_print_text_body(pjsip_msg_body *msg_body, char *buf, pj_size_t size)
-{
- if (size < msg_body->len)
- return -1;
- pj_memcpy(buf, msg_body->data, msg_body->len);
- return msg_body->len;
-}
+/* $Id$ */
+/*
+ * Copyright (C) 2003-2006 Benny Prijono <benny@prijono.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#include <pjsip/sip_msg.h>
+#include <pjsip/print_util.h>
+#include <pj/string.h>
+#include <pj/pool.h>
+
+/*
+ * Include inline definitions here if functions are NOT inlined.
+ */
+#if PJ_FUNCTIONS_ARE_INLINED==0
+# include <pjsip/sip_msg_i.h>
+#endif
+
+static const pj_str_t method_names[] =
+{
+ { "INVITE", 6 },
+ { "CANCEL", 6 },
+ { "ACK", 3 },
+ { "BYE", 3 },
+ { "REGISTER", 8 },
+ { "OPTIONS", 7 },
+};
+
+const pj_str_t pjsip_hdr_names[] =
+{
+ { "Accept", 6 }, // PJSIP_H_ACCEPT,
+ { "Accept-Encoding", 15 }, // PJSIP_H_ACCEPT_ENCODING,
+ { "Accept-Language", 15 }, // PJSIP_H_ACCEPT_LANGUAGE,
+ { "Alert-Info", 10 }, // PJSIP_H_ALERT_INFO,
+ { "Allow", 5 }, // PJSIP_H_ALLOW,
+ { "Authentication-Info",19 }, // PJSIP_H_AUTHENTICATION_INFO,
+ { "Authorization", 13 }, // PJSIP_H_AUTHORIZATION,
+ { "Call-ID", 7 }, // PJSIP_H_CALL_ID,
+ { "Call-Info", 9 }, // PJSIP_H_CALL_INFO,
+ { "Contact", 7 }, // PJSIP_H_CONTACT,
+ { "Content-Disposition",19 }, // PJSIP_H_CONTENT_DISPOSITION,
+ { "Content-Encoding", 16 }, // PJSIP_H_CONTENT_ENCODING,
+ { "Content-Language", 16 }, // PJSIP_H_CONTENT_LANGUAGE,
+ { "Content-Length", 14 }, // PJSIP_H_CONTENT_LENGTH,
+ { "Content-Type", 12 }, // PJSIP_H_CONTENT_TYPE,
+ { "CSeq", 4 }, // PJSIP_H_CSEQ,
+ { "Date", 4 }, // PJSIP_H_DATE,
+ { "Error-Info", 10 }, // PJSIP_H_ERROR_INFO,
+ { "Expires", 7 }, // PJSIP_H_EXPIRES,
+ { "From", 4 }, // PJSIP_H_FROM,
+ { "In-Reply-To", 11 }, // PJSIP_H_IN_REPLY_TO,
+ { "Max-Forwards", 12 }, // PJSIP_H_MAX_FORWARDS,
+ { "MIME-Version", 12 }, // PJSIP_H_MIME_VERSION,
+ { "Min-Expires", 11 }, // PJSIP_H_MIN_EXPIRES,
+ { "Organization", 12 }, // PJSIP_H_ORGANIZATION,
+ { "Priority", 8 }, // PJSIP_H_PRIORITY,
+ { "Proxy-Authenticate", 18 }, // PJSIP_H_PROXY_AUTHENTICATE,
+ { "Proxy-Authorization",19 }, // PJSIP_H_PROXY_AUTHORIZATION,
+ { "Proxy-Require", 13 }, // PJSIP_H_PROXY_REQUIRE,
+ { "Record-Route", 12 }, // PJSIP_H_RECORD_ROUTE,
+ { "Reply-To", 8 }, // PJSIP_H_REPLY_TO,
+ { "Require", 7 }, // PJSIP_H_REQUIRE,
+ { "Retry-After", 11 }, // PJSIP_H_RETRY_AFTER,
+ { "Route", 5 }, // PJSIP_H_ROUTE,
+ { "Server", 6 }, // PJSIP_H_SERVER,
+ { "Subject", 7 }, // PJSIP_H_SUBJECT,
+ { "Supported", 9 }, // PJSIP_H_SUPPORTED,
+ { "Timestamp", 9 }, // PJSIP_H_TIMESTAMP,
+ { "To", 2 }, // PJSIP_H_TO,
+ { "Unsupported", 11 }, // PJSIP_H_UNSUPPORTED,
+ { "User-Agent", 10 }, // PJSIP_H_USER_AGENT,
+ { "Via", 3 }, // PJSIP_H_VIA,
+ { "Warning", 7 }, // PJSIP_H_WARNING,
+ { "WWW-Authenticate", 16 }, // PJSIP_H_WWW_AUTHENTICATE,
+
+ { "_Unknown-Header", 15 }, // PJSIP_H_OTHER,
+};
+
+static pj_str_t status_phrase[710];
+static int print_media_type(char *buf, const pjsip_media_type *media);
+
+static int init_status_phrase()
+{
+ int i;
+ pj_str_t default_reason_phrase = { "Default status message", 22};
+
+ for (i=0; i<PJ_ARRAY_SIZE(status_phrase); ++i)
+ status_phrase[i] = default_reason_phrase;
+
+ pj_strset2( &status_phrase[100], "Trying");
+ pj_strset2( &status_phrase[180], "Ringing");
+ pj_strset2( &status_phrase[181], "Call Is Being Forwarded");
+ pj_strset2( &status_phrase[182], "Queued");
+ pj_strset2( &status_phrase[183], "Session Progress");
+
+ pj_strset2( &status_phrase[200], "OK");
+
+ pj_strset2( &status_phrase[300], "Multiple Choices");
+ pj_strset2( &status_phrase[301], "Moved Permanently");
+ pj_strset2( &status_phrase[302], "Moved Temporarily");
+ pj_strset2( &status_phrase[305], "Use Proxy");
+ pj_strset2( &status_phrase[380], "Alternative Service");
+
+ pj_strset2( &status_phrase[400], "Bad Request");
+ pj_strset2( &status_phrase[401], "Unauthorized");
+ pj_strset2( &status_phrase[402], "Payment Required");
+ pj_strset2( &status_phrase[403], "Forbidden");
+ pj_strset2( &status_phrase[404], "Not Found");
+ pj_strset2( &status_phrase[405], "Method Not Allowed");
+ pj_strset2( &status_phrase[406], "Not Acceptable");
+ pj_strset2( &status_phrase[407], "Proxy Authentication Required");
+ pj_strset2( &status_phrase[408], "Request Timeout");
+ pj_strset2( &status_phrase[410], "Gone");
+ pj_strset2( &status_phrase[413], "Request Entity Too Large");
+ pj_strset2( &status_phrase[414], "Request URI Too Long");
+ pj_strset2( &status_phrase[415], "Unsupported Media Type");
+ pj_strset2( &status_phrase[416], "Unsupported URI Scheme");
+ pj_strset2( &status_phrase[420], "Bad Extension");
+ pj_strset2( &status_phrase[421], "Extension Required");
+ pj_strset2( &status_phrase[423], "Interval Too Brief");
+ pj_strset2( &status_phrase[480], "Temporarily Unavailable");
+ pj_strset2( &status_phrase[481], "Call/Transaction Does Not Exist");
+ pj_strset2( &status_phrase[482], "Loop Detected");
+ pj_strset2( &status_phrase[483], "Too Many Hops");
+ pj_strset2( &status_phrase[484], "Address Incompleted");
+ pj_strset2( &status_phrase[485], "Ambiguous");
+ pj_strset2( &status_phrase[486], "Busy Here");
+ pj_strset2( &status_phrase[487], "Request Terminated");
+ pj_strset2( &status_phrase[488], "Not Acceptable Here");
+ pj_strset2( &status_phrase[491], "Request Pending");
+ pj_strset2( &status_phrase[493], "Undecipherable");
+
+ pj_strset2( &status_phrase[500], "Internal Server Error");
+ pj_strset2( &status_phrase[501], "Not Implemented");
+ pj_strset2( &status_phrase[502], "Bad Gateway");
+ pj_strset2( &status_phrase[503], "Service Unavailable");
+ pj_strset2( &status_phrase[504], "Server Timeout");
+ pj_strset2( &status_phrase[505], "Version Not Supported");
+ pj_strset2( &status_phrase[513], "Message Too Large");
+
+ pj_strset2( &status_phrase[600], "Busy Everywhere");
+ pj_strset2( &status_phrase[603], "Decline");
+ pj_strset2( &status_phrase[604], "Does Not Exist Anywhere");
+ pj_strset2( &status_phrase[606], "Not Acceptable");
+
+ pj_strset2( &status_phrase[701], "No response from destination server");
+ pj_strset2( &status_phrase[702], "Unable to resolve destination server");
+ pj_strset2( &status_phrase[703], "Error sending message to destination server");
+
+ return 1;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+/*
+ * Method.
+ */
+
+PJ_DEF(void) pjsip_method_init( pjsip_method *m,
+ pj_pool_t *pool,
+ const pj_str_t *str)
+{
+ pj_str_t dup;
+ pjsip_method_init_np(m, pj_strdup(pool, &dup, str));
+}
+
+PJ_DEF(void) pjsip_method_set( pjsip_method *m, pjsip_method_e me )
+{
+ m->id = me;
+ m->name = method_names[me];
+}
+
+PJ_DEF(void) pjsip_method_init_np(pjsip_method *m,
+ pj_str_t *str)
+{
+ int i;
+ for (i=0; i<PJ_ARRAY_SIZE(method_names); ++i) {
+ if (pj_stricmp(str, &method_names[i])==0) {
+ m->id = (pjsip_method_e)i;
+ m->name = method_names[i];
+ return;
+ }
+ }
+ m->id = PJSIP_OTHER_METHOD;
+ m->name = *str;
+}
+
+PJ_DEF(void) pjsip_method_copy( pj_pool_t *pool,
+ pjsip_method *method,
+ const pjsip_method *rhs )
+{
+ method->id = rhs->id;
+ if (rhs->id != PJSIP_OTHER_METHOD) {
+ method->name = rhs->name;
+ } else {
+ pj_strdup(pool, &method->name, &rhs->name);
+ }
+}
+
+
+PJ_DEF(int) pjsip_method_cmp( const pjsip_method *m1, const pjsip_method *m2)
+{
+ if (m1->id == m2->id) {
+ if (m1->id != PJSIP_OTHER_METHOD)
+ return 0;
+ return pj_stricmp(&m1->name, &m2->name);
+ }
+
+ return ( m1->id < m2->id ) ? -1 : 1;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+/*
+ * Message.
+ */
+
+PJ_DEF(pjsip_msg*) pjsip_msg_create( pj_pool_t *pool, pjsip_msg_type_e type)
+{
+ pjsip_msg *msg = pj_pool_alloc(pool, sizeof(pjsip_msg));
+ pj_list_init(&msg->hdr);
+ msg->type = type;
+ msg->body = NULL;
+ return msg;
+}
+
+PJ_DEF(void*) pjsip_msg_find_hdr( pjsip_msg *msg,
+ pjsip_hdr_e hdr_type, void *start)
+{
+ pjsip_hdr *hdr=start, *end=&msg->hdr;
+
+ if (hdr == NULL) {
+ hdr = msg->hdr.next;
+ }
+ for (; hdr!=end; hdr = hdr->next) {
+ if (hdr->type == hdr_type)
+ return hdr;
+ }
+ return NULL;
+}
+
+PJ_DEF(void*) pjsip_msg_find_hdr_by_name( pjsip_msg *msg,
+ const pj_str_t *name, void *start)
+{
+ pjsip_hdr *hdr=start, *end=&msg->hdr;
+
+ if (hdr == NULL) {
+ hdr = msg->hdr.next;
+ }
+ for (; hdr!=end; hdr = hdr->next) {
+ if (hdr->type < PJSIP_H_OTHER) {
+ if (pj_stricmp(&pjsip_hdr_names[hdr->type], name) == 0)
+ return hdr;
+ } else {
+ if (pj_stricmp(&hdr->name, name) == 0)
+ return hdr;
+ }
+ }
+ return NULL;
+}
+
+PJ_DEF(void*) pjsip_msg_find_remove_hdr( pjsip_msg *msg,
+ pjsip_hdr_e hdr_type, void *start)
+{
+ pjsip_hdr *hdr = pjsip_msg_find_hdr(msg, hdr_type, start);
+ if (hdr) {
+ pj_list_erase(hdr);
+ }
+ return hdr;
+}
+
+PJ_DEF(pj_ssize_t) pjsip_msg_print( pjsip_msg *msg, char *buf, pj_size_t size)
+{
+ char *p=buf, *end=buf+size;
+ int len;
+ pjsip_hdr *hdr;
+ pj_str_t clen_hdr = { "Content-Length: ", 16};
+
+ /* Get a wild guess on how many bytes are typically needed.
+ * We'll check this later in detail, but this serves as a quick check.
+ */
+ if (size < 256)
+ return -1;
+
+ /* Print request line or status line depending on message type */
+ if (msg->type == PJSIP_REQUEST_MSG) {
+ pjsip_uri *uri;
+
+ /* Add method. */
+ len = msg->line.req.method.name.slen;
+ pj_memcpy(p, msg->line.req.method.name.ptr, len);
+ p += len;
+ *p++ = ' ';
+
+ /* Add URI */
+ uri = pjsip_uri_get_uri(msg->line.req.uri);
+ len = pjsip_uri_print( PJSIP_URI_IN_REQ_URI, uri, p, end-p);
+ if (len < 1)
+ return -1;
+ p += len;
+
+ /* Add ' SIP/2.0' */
+ if (end-p < 16)
+ return -1;
+ pj_memcpy(p, " SIP/2.0\r\n", 10);
+ p += 10;
+
+ } else {
+
+ /* Add 'SIP/2.0 ' */
+ pj_memcpy(p, "SIP/2.0 ", 8);
+ p += 8;
+
+ /* Add status code. */
+ len = pj_utoa(msg->line.status.code, p);
+ p += len;
+ *p++ = ' ';
+
+ /* Add reason text. */
+ len = msg->line.status.reason.slen;
+ pj_memcpy(p, msg->line.status.reason.ptr, len );
+ p += len;
+
+ /* Add newline. */
+ *p++ = '\r';
+ *p++ = '\n';
+ }
+
+ /* Print each of the headers. */
+ for (hdr=msg->hdr.next; hdr!=&msg->hdr; hdr=hdr->next) {
+ len = (*hdr->vptr->print_on)(hdr, p, end-p);
+ if (len < 1)
+ return -1;
+ p += len;
+
+ if (p+3 >= end)
+ return -1;
+
+ *p++ = '\r';
+ *p++ = '\n';
+ }
+
+ /* Process message body. */
+ if (msg->body) {
+ pj_str_t ctype_hdr = { "Content-Type: ", 14};
+ int len;
+ const pjsip_media_type *media = &msg->body->content_type;
+ char *clen_pos;
+
+ /* Add Content-Type header. */
+ if ( (end-p) < 24+media->type.slen+media->subtype.slen+media->param.slen) {
+ return -1;
+ }
+ pj_memcpy(p, ctype_hdr.ptr, ctype_hdr.slen);
+ p += ctype_hdr.slen;
+ p += print_media_type(p, media);
+ *p++ = '\r';
+ *p++ = '\n';
+
+ /* Add Content-Length header. */
+ if ((end-p) < clen_hdr.slen+12+2) {
+ return -1;
+ }
+ pj_memcpy(p, clen_hdr.ptr, clen_hdr.slen);
+ p += clen_hdr.slen;
+
+ /* Print blanks after "Content-Type:", this is where we'll put
+ * the content length value after we know the length of the
+ * body.
+ */
+ pj_memset(p, ' ', 12);
+ clen_pos = p;
+ p += 12;
+ *p++ = '\r';
+ *p++ = '\n';
+
+ /* Add blank newline. */
+ *p++ = '\r';
+ *p++ = '\n';
+
+ /* Print the message body itself. */
+ len = (*msg->body->print_body)(msg->body, p, end-p);
+ if (len < 0) {
+ return -1;
+ }
+ p += len;
+
+ /* Now that we have the length of the body, print this to the
+ * Content-Length header.
+ */
+ len = pj_utoa(len, clen_pos);
+ clen_pos[len] = ' ';
+
+ } else {
+ /* There's no message body.
+ * Add Content-Length with zero value.
+ */
+ if ((end-p) < clen_hdr.slen+8) {
+ return -1;
+ }
+ pj_memcpy(p, clen_hdr.ptr, clen_hdr.slen);
+ p += clen_hdr.slen;
+ *p++ = '0';
+ *p++ = '\r';
+ *p++ = '\n';
+ *p++ = '\r';
+ *p++ = '\n';
+ }
+
+ *p = '\0';
+ return p-buf;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+PJ_DEF(void*) pjsip_hdr_clone( pj_pool_t *pool, const void *hdr_ptr )
+{
+ const pjsip_hdr *hdr = hdr_ptr;
+ return (*hdr->vptr->clone)(pool, hdr_ptr);
+}
+
+
+PJ_DEF(void*) pjsip_hdr_shallow_clone( pj_pool_t *pool, const void *hdr_ptr )
+{
+ const pjsip_hdr *hdr = hdr_ptr;
+ return (*hdr->vptr->shallow_clone)(pool, hdr_ptr);
+}
+
+PJ_DEF(int) pjsip_hdr_print_on( void *hdr_ptr, char *buf, pj_size_t len)
+{
+ pjsip_hdr *hdr = hdr_ptr;
+ return (*hdr->vptr->print_on)(hdr_ptr, buf, len);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+/*
+ * Status/Reason Phrase
+ */
+
+PJ_DEF(const pj_str_t*) pjsip_get_status_text(int code)
+{
+ static int is_initialized;
+ if (is_initialized == 0) {
+ is_initialized = 1;
+ init_status_phrase();
+ }
+
+ return (code>=100 && code<(sizeof(status_phrase)/sizeof(status_phrase[0]))) ?
+ &status_phrase[code] : &status_phrase[0];
+}
+
+///////////////////////////////////////////////////////////////////////////////
+/*
+ * Generic pjsip_hdr_names/hvalue header.
+ */
+
+static int pjsip_generic_string_hdr_print( pjsip_generic_string_hdr *hdr,
+ char *buf, pj_size_t size);
+static pjsip_generic_string_hdr* pjsip_generic_string_hdr_clone( pj_pool_t *pool,
+ const pjsip_generic_string_hdr *hdr);
+static pjsip_generic_string_hdr* pjsip_generic_string_hdr_shallow_clone( pj_pool_t *pool,
+ const pjsip_generic_string_hdr *hdr );
+
+static pjsip_hdr_vptr generic_hdr_vptr =
+{
+ (pjsip_hdr_clone_fptr) &pjsip_generic_string_hdr_clone,
+ (pjsip_hdr_clone_fptr) &pjsip_generic_string_hdr_shallow_clone,
+ (pjsip_hdr_print_fptr) &pjsip_generic_string_hdr_print,
+};
+
+PJ_DEF(pjsip_generic_string_hdr*) pjsip_generic_string_hdr_create( pj_pool_t *pool,
+ const pj_str_t *hnames )
+{
+ pjsip_generic_string_hdr *hdr = pj_pool_alloc(pool, sizeof(pjsip_generic_string_hdr));
+ init_hdr(hdr, PJSIP_H_OTHER, &generic_hdr_vptr);
+ if (hnames) {
+ pj_strdup(pool, &hdr->name, hnames);
+ hdr->sname = hdr->name;
+ }
+ hdr->hvalue.ptr = NULL;
+ hdr->hvalue.slen = 0;
+ return hdr;
+}
+
+PJ_DEF(pjsip_generic_string_hdr*) pjsip_generic_string_hdr_create_with_text( pj_pool_t *pool,
+ const pj_str_t *hname,
+ const pj_str_t *hvalue)
+{
+ pjsip_generic_string_hdr *hdr = pjsip_generic_string_hdr_create(pool, hname);
+ pj_strdup(pool, &hdr->hvalue, hvalue);
+ return hdr;
+}
+
+static int pjsip_generic_string_hdr_print( pjsip_generic_string_hdr *hdr,
+ char *buf, pj_size_t size)
+{
+ char *p = buf;
+
+ if ((pj_ssize_t)size < hdr->name.slen + hdr->hvalue.slen + 5)
+ return -1;
+
+ pj_memcpy(p, hdr->name.ptr, hdr->name.slen);
+ p += hdr->name.slen;
+ *p++ = ':';
+ *p++ = ' ';
+ pj_memcpy(p, hdr->hvalue.ptr, hdr->hvalue.slen);
+ p += hdr->hvalue.slen;
+ *p = '\0';
+
+ return p - buf;
+}
+
+static pjsip_generic_string_hdr* pjsip_generic_string_hdr_clone( pj_pool_t *pool,
+ const pjsip_generic_string_hdr *rhs)
+{
+ pjsip_generic_string_hdr *hdr = pjsip_generic_string_hdr_create(pool, &rhs->name);
+
+ hdr->type = rhs->type;
+ hdr->sname = hdr->name;
+ pj_strdup( pool, &hdr->hvalue, &rhs->hvalue);
+ return hdr;
+}
+
+static pjsip_generic_string_hdr* pjsip_generic_string_hdr_shallow_clone( pj_pool_t *pool,
+ const pjsip_generic_string_hdr *rhs )
+{
+ pjsip_generic_string_hdr *hdr = pj_pool_alloc(pool, sizeof(*hdr));
+ pj_memcpy(hdr, rhs, sizeof(*hdr));
+ return hdr;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+/*
+ * Generic pjsip_hdr_names/integer value header.
+ */
+
+static int pjsip_generic_int_hdr_print( pjsip_generic_int_hdr *hdr,
+ char *buf, pj_size_t size);
+static pjsip_generic_int_hdr* pjsip_generic_int_hdr_clone( pj_pool_t *pool,
+ const pjsip_generic_int_hdr *hdr);
+static pjsip_generic_int_hdr* pjsip_generic_int_hdr_shallow_clone( pj_pool_t *pool,
+ const pjsip_generic_int_hdr *hdr );
+
+static pjsip_hdr_vptr generic_int_hdr_vptr =
+{
+ (pjsip_hdr_clone_fptr) &pjsip_generic_int_hdr_clone,
+ (pjsip_hdr_clone_fptr) &pjsip_generic_int_hdr_shallow_clone,
+ (pjsip_hdr_print_fptr) &pjsip_generic_int_hdr_print,
+};
+
+PJ_DEF(pjsip_generic_int_hdr*) pjsip_generic_int_hdr_create( pj_pool_t *pool,
+ const pj_str_t *hnames )
+{
+ pjsip_generic_int_hdr *hdr = pj_pool_alloc(pool, sizeof(pjsip_generic_int_hdr));
+ init_hdr(hdr, PJSIP_H_OTHER, &generic_int_hdr_vptr);
+ if (hnames) {
+ pj_strdup(pool, &hdr->name, hnames);
+ hdr->sname = hdr->name;
+ }
+ hdr->ivalue = 0;
+ return hdr;
+}
+
+PJ_DEF(pjsip_generic_int_hdr*) pjsip_generic_int_hdr_create_with_value( pj_pool_t *pool,
+ const pj_str_t *hname,
+ int value)
+{
+ pjsip_generic_int_hdr *hdr = pjsip_generic_int_hdr_create(pool, hname);
+ hdr->ivalue = value;
+ return hdr;
+}
+
+static int pjsip_generic_int_hdr_print( pjsip_generic_int_hdr *hdr,
+ char *buf, pj_size_t size)
+{
+ char *p = buf;
+
+ if ((pj_ssize_t)size < hdr->name.slen + 15)
+ return -1;
+
+ pj_memcpy(p, hdr->name.ptr, hdr->name.slen);
+ p += hdr->name.slen;
+ *p++ = ':';
+ *p++ = ' ';
+
+ p += pj_utoa(hdr->ivalue, p);
+
+ return p - buf;
+}
+
+static pjsip_generic_int_hdr* pjsip_generic_int_hdr_clone( pj_pool_t *pool,
+ const pjsip_generic_int_hdr *rhs)
+{
+ pjsip_generic_int_hdr *hdr = pj_pool_alloc(pool, sizeof(*hdr));
+ pj_memcpy(hdr, rhs, sizeof(*hdr));
+ return hdr;
+}
+
+static pjsip_generic_int_hdr* pjsip_generic_int_hdr_shallow_clone( pj_pool_t *pool,
+ const pjsip_generic_int_hdr *rhs )
+{
+ pjsip_generic_int_hdr *hdr = pj_pool_alloc(pool, sizeof(*hdr));
+ pj_memcpy(hdr, rhs, sizeof(*hdr));
+ return hdr;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+/*
+ * Generic array header.
+ */
+static int pjsip_generic_array_hdr_print( pjsip_generic_array_hdr *hdr, char *buf, pj_size_t size);
+static pjsip_generic_array_hdr* pjsip_generic_array_hdr_clone( pj_pool_t *pool,
+ const pjsip_generic_array_hdr *hdr);
+static pjsip_generic_array_hdr* pjsip_generic_array_hdr_shallow_clone( pj_pool_t *pool,
+ const pjsip_generic_array_hdr *hdr);
+
+static pjsip_hdr_vptr generic_array_hdr_vptr =
+{
+ (pjsip_hdr_clone_fptr) &pjsip_generic_array_hdr_clone,
+ (pjsip_hdr_clone_fptr) &pjsip_generic_array_hdr_shallow_clone,
+ (pjsip_hdr_print_fptr) &pjsip_generic_array_hdr_print,
+};
+
+PJ_DEF(pjsip_generic_array_hdr*) pjsip_generic_array_create( pj_pool_t *pool,
+ const pj_str_t *hnames)
+{
+ pjsip_generic_array_hdr *hdr = pj_pool_alloc(pool, sizeof(pjsip_generic_array_hdr));
+ init_hdr(hdr, PJSIP_H_OTHER, &generic_array_hdr_vptr);
+ if (hnames) {
+ pj_strdup(pool, &hdr->name, hnames);
+ hdr->sname = hdr->name;
+ }
+ hdr->count = 0;
+ return hdr;
+
+}
+
+static int pjsip_generic_array_hdr_print( pjsip_generic_array_hdr *hdr,
+ char *buf, pj_size_t size)
+{
+ char *p = buf, *endbuf = buf+size;
+
+ copy_advance(p, hdr->name);
+ *p++ = ':';
+ *p++ = ' ';
+
+ if (hdr->count > 0) {
+ unsigned i;
+ int printed;
+ copy_advance(p, hdr->values[0]);
+ for (i=1; i<hdr->count; ++i) {
+ copy_advance_pair(p, ", ", 2, hdr->values[i]);
+ }
+ }
+
+ return p - buf;
+}
+
+static pjsip_generic_array_hdr* pjsip_generic_array_hdr_clone( pj_pool_t *pool,
+ const pjsip_generic_array_hdr *rhs)
+{
+ unsigned i;
+ pjsip_generic_array_hdr *hdr = pj_pool_alloc(pool, sizeof(*hdr));
+
+ pj_memcpy(hdr, rhs, sizeof(*hdr));
+ for (i=0; i<rhs->count; ++i) {
+ pj_strdup(pool, &hdr->values[i], &rhs->values[i]);
+ }
+
+ return hdr;
+}
+
+
+static pjsip_generic_array_hdr* pjsip_generic_array_hdr_shallow_clone( pj_pool_t *pool,
+ const pjsip_generic_array_hdr *rhs)
+{
+ pjsip_generic_array_hdr *hdr = pj_pool_alloc(pool, sizeof(*hdr));
+ pj_memcpy(hdr, rhs, sizeof(*hdr));
+ return hdr;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+/*
+ * Accept header.
+ */
+PJ_DEF(pjsip_accept_hdr*) pjsip_accept_hdr_create(pj_pool_t *pool)
+{
+ pjsip_accept_hdr *hdr = pj_pool_alloc(pool, sizeof(*hdr));
+ init_hdr(hdr, PJSIP_H_ACCEPT, &generic_array_hdr_vptr);
+ hdr->count = 0;
+ return hdr;
+}
+
+
+///////////////////////////////////////////////////////////////////////////////
+/*
+ * Allow header.
+ */
+
+PJ_DEF(pjsip_allow_hdr*) pjsip_allow_hdr_create(pj_pool_t *pool)
+{
+ pjsip_allow_hdr *hdr = pj_pool_alloc(pool, sizeof(*hdr));
+ init_hdr(hdr, PJSIP_H_ALLOW, &generic_array_hdr_vptr);
+ hdr->count = 0;
+ return hdr;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+/*
+ * Call-ID header.
+ */
+
+PJ_DEF(pjsip_cid_hdr*) pjsip_cid_hdr_create( pj_pool_t *pool )
+{
+ pjsip_cid_hdr *hdr = pj_pool_alloc(pool, sizeof(*hdr));
+ init_hdr(hdr, PJSIP_H_CALL_ID, &generic_hdr_vptr);
+ return hdr;
+}
+
+
+///////////////////////////////////////////////////////////////////////////////
+/*
+ * Content-Length header.
+ */
+static int pjsip_clen_hdr_print( pjsip_clen_hdr *hdr, char *buf, pj_size_t size);
+static pjsip_clen_hdr* pjsip_clen_hdr_clone( pj_pool_t *pool, const pjsip_clen_hdr *hdr);
+#define pjsip_clen_hdr_shallow_clone pjsip_clen_hdr_clone
+
+static pjsip_hdr_vptr clen_hdr_vptr =
+{
+ (pjsip_hdr_clone_fptr) &pjsip_clen_hdr_clone,
+ (pjsip_hdr_clone_fptr) &pjsip_clen_hdr_shallow_clone,
+ (pjsip_hdr_print_fptr) &pjsip_clen_hdr_print,
+};
+
+PJ_DEF(pjsip_clen_hdr*) pjsip_clen_hdr_create( pj_pool_t *pool )
+{
+ pjsip_clen_hdr *hdr = pj_pool_alloc(pool, sizeof(pjsip_clen_hdr));
+ init_hdr(hdr, PJSIP_H_CONTENT_LENGTH, &clen_hdr_vptr);
+ hdr->len = 0;
+ return hdr;
+}
+
+static int pjsip_clen_hdr_print( pjsip_clen_hdr *hdr,
+ char *buf, pj_size_t size)
+{
+ char *p = buf;
+ int len;
+
+ if ((pj_ssize_t)size < hdr->name.slen + 14)
+ return -1;
+
+ pj_memcpy(p, hdr->name.ptr, hdr->name.slen);
+ p += hdr->name.slen;
+ *p++ = ':';
+ *p++ = ' ';
+
+ len = pj_utoa(hdr->len, p);
+ p += len;
+ *p = '\0';
+
+ return p-buf;
+}
+
+static pjsip_clen_hdr* pjsip_clen_hdr_clone( pj_pool_t *pool, const pjsip_clen_hdr *rhs)
+{
+ pjsip_clen_hdr *hdr = pjsip_clen_hdr_create(pool);
+ hdr->len = rhs->len;
+ return hdr;
+}
+
+
+///////////////////////////////////////////////////////////////////////////////
+/*
+ * CSeq header.
+ */
+static int pjsip_cseq_hdr_print( pjsip_cseq_hdr *hdr, char *buf, pj_size_t size);
+static pjsip_cseq_hdr* pjsip_cseq_hdr_clone( pj_pool_t *pool, const pjsip_cseq_hdr *hdr);
+static pjsip_cseq_hdr* pjsip_cseq_hdr_shallow_clone( pj_pool_t *pool, const pjsip_cseq_hdr *hdr );
+
+static pjsip_hdr_vptr cseq_hdr_vptr =
+{
+ (pjsip_hdr_clone_fptr) &pjsip_cseq_hdr_clone,
+ (pjsip_hdr_clone_fptr) &pjsip_cseq_hdr_shallow_clone,
+ (pjsip_hdr_print_fptr) &pjsip_cseq_hdr_print,
+};
+
+PJ_DEF(pjsip_cseq_hdr*) pjsip_cseq_hdr_create( pj_pool_t *pool )
+{
+ pjsip_cseq_hdr *hdr = pj_pool_alloc(pool, sizeof(pjsip_cseq_hdr));
+ init_hdr(hdr, PJSIP_H_CSEQ, &cseq_hdr_vptr);
+ hdr->cseq = 0;
+ hdr->method.id = PJSIP_OTHER_METHOD;
+ hdr->method.name.ptr = NULL;
+ hdr->method.name.slen = 0;
+ return hdr;
+}
+
+static int pjsip_cseq_hdr_print( pjsip_cseq_hdr *hdr, char *buf, pj_size_t size)
+{
+ char *p = buf;
+ int len;
+
+ if ((pj_ssize_t)size < hdr->name.slen + hdr->method.name.slen + 15)
+ return -1;
+
+ pj_memcpy(p, hdr->name.ptr, hdr->name.slen);
+ p += hdr->name.slen;
+ *p++ = ':';
+ *p++ = ' ';
+
+ len = pj_utoa(hdr->cseq, p);
+ p += len;
+ *p++ = ' ';
+
+ pj_memcpy(p, hdr->method.name.ptr, hdr->method.name.slen);
+ p += hdr->method.name.slen;
+
+ *p = '\0';
+
+ return p-buf;
+}
+
+static pjsip_cseq_hdr* pjsip_cseq_hdr_clone( pj_pool_t *pool,
+ const pjsip_cseq_hdr *rhs)
+{
+ pjsip_cseq_hdr *hdr = pjsip_cseq_hdr_create(pool);
+ hdr->cseq = rhs->cseq;
+ pjsip_method_copy(pool, &hdr->method, &rhs->method);
+ return hdr;
+}
+
+static pjsip_cseq_hdr* pjsip_cseq_hdr_shallow_clone( pj_pool_t *pool,
+ const pjsip_cseq_hdr *rhs )
+{
+ pjsip_cseq_hdr *hdr = pj_pool_alloc(pool, sizeof(*hdr));
+ pj_memcpy(hdr, rhs, sizeof(*hdr));
+ return hdr;
+}
+
+
+///////////////////////////////////////////////////////////////////////////////
+/*
+ * Contact header.
+ */
+static int pjsip_contact_hdr_print( pjsip_contact_hdr *hdr, char *buf, pj_size_t size);
+static pjsip_contact_hdr* pjsip_contact_hdr_clone( pj_pool_t *pool, const pjsip_contact_hdr *hdr);
+static pjsip_contact_hdr* pjsip_contact_hdr_shallow_clone( pj_pool_t *pool, const pjsip_contact_hdr *);
+
+static pjsip_hdr_vptr contact_hdr_vptr =
+{
+ (pjsip_hdr_clone_fptr) &pjsip_contact_hdr_clone,
+ (pjsip_hdr_clone_fptr) &pjsip_contact_hdr_shallow_clone,
+ (pjsip_hdr_print_fptr) &pjsip_contact_hdr_print,
+};
+
+PJ_DEF(pjsip_contact_hdr*) pjsip_contact_hdr_create( pj_pool_t *pool )
+{
+ pjsip_contact_hdr *hdr = pj_pool_calloc(pool, 1, sizeof(*hdr));
+ init_hdr(hdr, PJSIP_H_CONTACT, &contact_hdr_vptr);
+ hdr->expires = -1;
+ pj_list_init(&hdr->other_param);
+ return hdr;
+}
+
+static int pjsip_contact_hdr_print( pjsip_contact_hdr *hdr, char *buf,
+ pj_size_t size)
+{
+ if (hdr->star) {
+ char *p = buf;
+ if ((pj_ssize_t)size < hdr->name.slen + 6)
+ return -1;
+ pj_memcpy(p, hdr->name.ptr, hdr->name.slen);
+ p += hdr->name.slen;
+ *p++ = ':';
+ *p++ = ' ';
+ *p++ = '*';
+ return p - buf;
+
+ } else {
+ int printed;
+ char *startbuf = buf;
+ char *endbuf = buf + size;
+
+ copy_advance(buf, hdr->name);
+ *buf++ = ':';
+ *buf++ = ' ';
+
+ printed = pjsip_uri_print(PJSIP_URI_IN_CONTACT_HDR, hdr->uri,
+ buf, endbuf-buf);
+ if (printed < 1)
+ return -1;
+
+ buf += printed;
+
+ if (hdr->q1000) {
+ if (buf+19 >= endbuf)
+ return -1;
+
+ /*
+ printed = sprintf(buf, ";q=%u.%03u",
+ hdr->q1000/1000, hdr->q1000 % 1000);
+ */
+ pj_memcpy(buf, ";q=", 3);
+ printed = pj_utoa(hdr->q1000/1000, buf+3);
+ buf += printed + 3;
+ *buf++ = '.';
+ printed = pj_utoa(hdr->q1000 % 1000, buf);
+ buf += printed;
+ }
+
+ if (hdr->expires >= 0) {
+ if (buf+23 >= endbuf)
+ return -1;
+
+ pj_memcpy(buf, ";expires=", 9);
+ printed = pj_utoa(hdr->expires, buf+9);
+ buf += printed + 9;
+ }
+
+ printed = pjsip_param_print_on(&hdr->other_param, buf, endbuf-buf,';');
+ if (printed < 0)
+ return printed;
+ buf += printed;
+
+ return buf-startbuf;
+ }
+}
+
+static pjsip_contact_hdr* pjsip_contact_hdr_clone(pj_pool_t *pool,
+ const pjsip_contact_hdr *rhs)
+{
+ pjsip_contact_hdr *hdr = pjsip_contact_hdr_create(pool);
+
+ hdr->star = rhs->star;
+ if (hdr->star)
+ return hdr;
+
+ hdr->uri = pjsip_uri_clone(pool, rhs->uri);
+ hdr->q1000 = rhs->q1000;
+ hdr->expires = rhs->expires;
+ pjsip_param_clone(pool, &hdr->other_param, &rhs->other_param);
+ return hdr;
+}
+
+static pjsip_contact_hdr*
+pjsip_contact_hdr_shallow_clone( pj_pool_t *pool,
+ const pjsip_contact_hdr *rhs)
+{
+ pjsip_contact_hdr *hdr = pj_pool_alloc(pool, sizeof(*hdr));
+ pj_memcpy(hdr, rhs, sizeof(*hdr));
+ pjsip_param_shallow_clone(pool, &hdr->other_param, &rhs->other_param);
+ return hdr;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+/*
+ * Content-Type header..
+ */
+static int pjsip_ctype_hdr_print( pjsip_ctype_hdr *hdr, char *buf,
+ pj_size_t size);
+static pjsip_ctype_hdr* pjsip_ctype_hdr_clone(pj_pool_t *pool,
+ const pjsip_ctype_hdr *hdr);
+#define pjsip_ctype_hdr_shallow_clone pjsip_ctype_hdr_clone
+
+static pjsip_hdr_vptr ctype_hdr_vptr =
+{
+ (pjsip_hdr_clone_fptr) &pjsip_ctype_hdr_clone,
+ (pjsip_hdr_clone_fptr) &pjsip_ctype_hdr_shallow_clone,
+ (pjsip_hdr_print_fptr) &pjsip_ctype_hdr_print,
+};
+
+PJ_DEF(pjsip_ctype_hdr*) pjsip_ctype_hdr_create( pj_pool_t *pool )
+{
+ pjsip_ctype_hdr *hdr = pj_pool_calloc(pool, 1, sizeof(*hdr));
+ init_hdr(hdr, PJSIP_H_CONTENT_TYPE, &ctype_hdr_vptr);
+ return hdr;
+}
+
+static int print_media_type(char *buf, const pjsip_media_type *media)
+{
+ char *p = buf;
+
+ pj_memcpy(p, media->type.ptr, media->type.slen);
+ p += media->type.slen;
+ *p++ = '/';
+ pj_memcpy(p, media->subtype.ptr, media->subtype.slen);
+ p += media->subtype.slen;
+
+ if (media->param.slen) {
+ pj_memcpy(p, media->param.ptr, media->param.slen);
+ p += media->param.slen;
+ }
+
+ return p-buf;
+}
+
+static int pjsip_ctype_hdr_print( pjsip_ctype_hdr *hdr,
+ char *buf, pj_size_t size)
+{
+ char *p = buf;
+ int len;
+
+ if ((pj_ssize_t)size < hdr->name.slen +
+ hdr->media.type.slen + hdr->media.subtype.slen +
+ hdr->media.param.slen + 8)
+ {
+ return -1;
+ }
+
+ pj_memcpy(p, hdr->name.ptr, hdr->name.slen);
+ p += hdr->name.slen;
+ *p++ = ':';
+ *p++ = ' ';
+
+ len = print_media_type(p, &hdr->media);
+ p += len;
+
+ *p = '\0';
+ return p-buf;
+}
+
+static pjsip_ctype_hdr* pjsip_ctype_hdr_clone( pj_pool_t *pool,
+ const pjsip_ctype_hdr *rhs)
+{
+ pjsip_ctype_hdr *hdr = pjsip_ctype_hdr_create(pool);
+ pj_strdup(pool, &hdr->media.type, &rhs->media.type);
+ pj_strdup(pool, &hdr->media.param, &rhs->media.param);
+ return hdr;
+}
+
+
+///////////////////////////////////////////////////////////////////////////////
+/*
+ * Expires header.
+ */
+PJ_DEF(pjsip_expires_hdr*) pjsip_expires_hdr_create( pj_pool_t *pool )
+{
+ pjsip_expires_hdr *hdr = pj_pool_alloc(pool, sizeof(*hdr));
+ init_hdr(hdr, PJSIP_H_EXPIRES, &generic_int_hdr_vptr);
+ hdr->ivalue = 0;
+ return hdr;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+/*
+ * To or From header.
+ */
+static int pjsip_fromto_hdr_print( pjsip_fromto_hdr *hdr,
+ char *buf, pj_size_t size);
+static pjsip_fromto_hdr* pjsip_fromto_hdr_clone( pj_pool_t *pool,
+ const pjsip_fromto_hdr *hdr);
+static pjsip_fromto_hdr* pjsip_fromto_hdr_shallow_clone( pj_pool_t *pool,
+ const pjsip_fromto_hdr *hdr);
+
+
+static pjsip_hdr_vptr fromto_hdr_vptr =
+{
+ (pjsip_hdr_clone_fptr) &pjsip_fromto_hdr_clone,
+ (pjsip_hdr_clone_fptr) &pjsip_fromto_hdr_shallow_clone,
+ (pjsip_hdr_print_fptr) &pjsip_fromto_hdr_print,
+};
+
+PJ_DEF(pjsip_from_hdr*) pjsip_from_hdr_create( pj_pool_t *pool )
+{
+ pjsip_from_hdr *hdr = pj_pool_calloc(pool, 1, sizeof(*hdr));
+ init_hdr(hdr, PJSIP_H_FROM, &fromto_hdr_vptr);
+ pj_list_init(&hdr->other_param);
+ return hdr;
+}
+
+PJ_DEF(pjsip_to_hdr*) pjsip_to_hdr_create( pj_pool_t *pool )
+{
+ pjsip_to_hdr *hdr = pj_pool_calloc(pool, 1, sizeof(*hdr));
+ init_hdr(hdr, PJSIP_H_TO, &fromto_hdr_vptr);
+ pj_list_init(&hdr->other_param);
+ return hdr;
+}
+
+PJ_DEF(pjsip_from_hdr*) pjsip_fromto_set_from( pjsip_fromto_hdr *hdr )
+{
+ hdr->type = PJSIP_H_FROM;
+ hdr->name = hdr->sname = pjsip_hdr_names[PJSIP_H_FROM];
+ return hdr;
+}
+
+PJ_DEF(pjsip_to_hdr*) pjsip_fromto_set_to( pjsip_fromto_hdr *hdr )
+{
+ hdr->type = PJSIP_H_TO;
+ hdr->name = hdr->sname = pjsip_hdr_names[PJSIP_H_TO];
+ return hdr;
+}
+
+static int pjsip_fromto_hdr_print( pjsip_fromto_hdr *hdr,
+ char *buf, pj_size_t size)
+{
+ int printed;
+ char *startbuf = buf;
+ char *endbuf = buf + size;
+
+ copy_advance(buf, hdr->name);
+ *buf++ = ':';
+ *buf++ = ' ';
+
+ printed = pjsip_uri_print(PJSIP_URI_IN_FROMTO_HDR, hdr->uri,
+ buf, endbuf-buf);
+ if (printed < 1)
+ return -1;
+
+ buf += printed;
+
+ copy_advance_pair(buf, ";tag=", 5, hdr->tag);
+
+ printed = pjsip_param_print_on(&hdr->other_param, buf, endbuf-buf, ';');
+ if (printed < 0)
+ return -1;
+ buf += printed;
+
+ return buf-startbuf;
+}
+
+static pjsip_fromto_hdr* pjsip_fromto_hdr_clone( pj_pool_t *pool,
+ const pjsip_fromto_hdr *rhs)
+{
+ pjsip_fromto_hdr *hdr = pjsip_from_hdr_create(pool);
+
+ hdr->type = rhs->type;
+ hdr->name = rhs->name;
+ hdr->sname = rhs->sname;
+ hdr->uri = pjsip_uri_clone(pool, rhs->uri);
+ pj_strdup( pool, &hdr->tag, &rhs->tag);
+ pjsip_param_clone( pool, &hdr->other_param, &rhs->other_param);
+
+ return hdr;
+}
+
+static pjsip_fromto_hdr*
+pjsip_fromto_hdr_shallow_clone( pj_pool_t *pool,
+ const pjsip_fromto_hdr *rhs)
+{
+ pjsip_fromto_hdr *hdr = pj_pool_alloc(pool, sizeof(*hdr));
+ pj_memcpy(hdr, rhs, sizeof(*hdr));
+ pjsip_param_shallow_clone( pool, &hdr->other_param, &rhs->other_param);
+ return hdr;
+}
+
+
+///////////////////////////////////////////////////////////////////////////////
+/*
+ * Max-Forwards header.
+ */
+PJ_DEF(pjsip_max_forwards_hdr*) pjsip_max_forwards_hdr_create(pj_pool_t *pool)
+{
+ pjsip_max_forwards_hdr *hdr = pj_pool_alloc(pool, sizeof(*hdr));
+ init_hdr(hdr, PJSIP_H_MAX_FORWARDS, &generic_int_hdr_vptr);
+ hdr->ivalue = 0;
+ return hdr;
+}
+
+
+///////////////////////////////////////////////////////////////////////////////
+/*
+ * Min-Expires header.
+ */
+PJ_DECL(pjsip_min_expires_hdr*) pjsip_min_expires_hdr_create(pj_pool_t *pool)
+{
+ pjsip_min_expires_hdr *hdr = pj_pool_alloc(pool, sizeof(*hdr));
+ init_hdr(hdr, PJSIP_H_MIN_EXPIRES, &generic_int_hdr_vptr);
+ hdr->ivalue = 0;
+ return hdr;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+/*
+ * Record-Route and Route header.
+ */
+static int pjsip_routing_hdr_print( pjsip_routing_hdr *r, char *buf, pj_size_t size );
+static pjsip_routing_hdr* pjsip_routing_hdr_clone( pj_pool_t *pool, const pjsip_routing_hdr *r );
+static pjsip_routing_hdr* pjsip_routing_hdr_shallow_clone( pj_pool_t *pool, const pjsip_routing_hdr *r );
+
+static pjsip_hdr_vptr routing_hdr_vptr =
+{
+ (pjsip_hdr_clone_fptr) &pjsip_routing_hdr_clone,
+ (pjsip_hdr_clone_fptr) &pjsip_routing_hdr_shallow_clone,
+ (pjsip_hdr_print_fptr) &pjsip_routing_hdr_print,
+};
+
+PJ_DEF(pjsip_rr_hdr*) pjsip_rr_hdr_create( pj_pool_t *pool )
+{
+ pjsip_rr_hdr *hdr = pj_pool_alloc(pool, sizeof(*hdr));
+ init_hdr(hdr, PJSIP_H_RECORD_ROUTE, &routing_hdr_vptr);
+ pjsip_name_addr_init(&hdr->name_addr);
+ pj_list_init(&hdr->other_param);
+ return hdr;
+}
+
+PJ_DEF(pjsip_route_hdr*) pjsip_route_hdr_create( pj_pool_t *pool )
+{
+ pjsip_route_hdr *hdr = pj_pool_alloc(pool, sizeof(*hdr));
+ init_hdr(hdr, PJSIP_H_ROUTE, &routing_hdr_vptr);
+ pjsip_name_addr_init(&hdr->name_addr);
+ pj_list_init(&hdr->other_param);
+ return hdr;
+}
+
+PJ_DEF(pjsip_rr_hdr*) pjsip_routing_hdr_set_rr( pjsip_routing_hdr *hdr )
+{
+ hdr->type = PJSIP_H_RECORD_ROUTE;
+ hdr->name = hdr->sname = pjsip_hdr_names[PJSIP_H_RECORD_ROUTE];
+ return hdr;
+}
+
+PJ_DEF(pjsip_route_hdr*) pjsip_routing_hdr_set_route( pjsip_routing_hdr *hdr )
+{
+ hdr->type = PJSIP_H_ROUTE;
+ hdr->name = hdr->sname = pjsip_hdr_names[PJSIP_H_ROUTE];
+ return hdr;
+}
+
+static int pjsip_routing_hdr_print( pjsip_routing_hdr *hdr,
+ char *buf, pj_size_t size )
+{
+ int printed;
+ char *startbuf = buf;
+ char *endbuf = buf + size;
+
+ copy_advance(buf, hdr->name);
+ *buf++ = ':';
+ *buf++ = ' ';
+
+ printed = pjsip_uri_print(PJSIP_URI_IN_ROUTING_HDR, &hdr->name_addr, buf,
+ endbuf-buf);
+ if (printed < 1)
+ return -1;
+ buf += printed;
+
+ printed = pjsip_param_print_on(&hdr->other_param, buf, endbuf-buf, ';');
+ if (printed < 0)
+ return -1;
+ buf += printed;
+
+ return buf-startbuf;
+}
+
+static pjsip_routing_hdr* pjsip_routing_hdr_clone( pj_pool_t *pool,
+ const pjsip_routing_hdr *rhs )
+{
+ pjsip_routing_hdr *hdr = pj_pool_alloc(pool, sizeof(*hdr));
+
+ init_hdr(hdr, rhs->type, rhs->vptr);
+ pjsip_name_addr_init(&hdr->name_addr);
+ pjsip_name_addr_assign(pool, &hdr->name_addr, &rhs->name_addr);
+ pjsip_param_clone( pool, &hdr->other_param, &rhs->other_param);
+ return hdr;
+}
+
+static pjsip_routing_hdr* pjsip_routing_hdr_shallow_clone( pj_pool_t *pool,
+ const pjsip_routing_hdr *rhs )
+{
+ pjsip_routing_hdr *hdr = pj_pool_alloc(pool, sizeof(*hdr));
+ pj_memcpy(hdr, rhs, sizeof(*hdr));
+ pjsip_param_shallow_clone( pool, &hdr->other_param, &rhs->other_param);
+ return hdr;
+}
+
+
+///////////////////////////////////////////////////////////////////////////////
+/*
+ * Require header.
+ */
+PJ_DEF(pjsip_require_hdr*) pjsip_require_hdr_create(pj_pool_t *pool)
+{
+ pjsip_require_hdr *hdr = pj_pool_alloc(pool, sizeof(*hdr));
+ init_hdr(hdr, PJSIP_H_REQUIRE, &generic_array_hdr_vptr);
+ hdr->count = 0;
+ return hdr;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+/*
+ * Retry-After header.
+ */
+PJ_DEF(pjsip_retry_after_hdr*) pjsip_retry_after_hdr_create(pj_pool_t *pool)
+{
+ pjsip_retry_after_hdr *hdr = pj_pool_alloc(pool, sizeof(*hdr));
+ init_hdr(hdr, PJSIP_H_RETRY_AFTER, &generic_int_hdr_vptr);
+ hdr->ivalue = 0;
+ return hdr;
+}
+
+
+///////////////////////////////////////////////////////////////////////////////
+/*
+ * Supported header.
+ */
+PJ_DEF(pjsip_supported_hdr*) pjsip_supported_hdr_create(pj_pool_t *pool)
+{
+ pjsip_supported_hdr *hdr = pj_pool_alloc(pool, sizeof(*hdr));
+ init_hdr(hdr, PJSIP_H_SUPPORTED, &generic_array_hdr_vptr);
+ hdr->count = 0;
+ return hdr;
+}
+
+
+///////////////////////////////////////////////////////////////////////////////
+/*
+ * Unsupported header.
+ */
+PJ_DEF(pjsip_unsupported_hdr*) pjsip_unsupported_hdr_create(pj_pool_t *pool)
+{
+ pjsip_unsupported_hdr *hdr = pj_pool_alloc(pool, sizeof(*hdr));
+ init_hdr(hdr, PJSIP_H_UNSUPPORTED, &generic_array_hdr_vptr);
+ hdr->count = 0;
+ return hdr;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+/*
+ * Via header.
+ */
+static int pjsip_via_hdr_print( pjsip_via_hdr *hdr, char *buf, pj_size_t size);
+static pjsip_via_hdr* pjsip_via_hdr_clone( pj_pool_t *pool, const pjsip_via_hdr *hdr);
+static pjsip_via_hdr* pjsip_via_hdr_shallow_clone( pj_pool_t *pool, const pjsip_via_hdr *hdr );
+
+static pjsip_hdr_vptr via_hdr_vptr =
+{
+ (pjsip_hdr_clone_fptr) &pjsip_via_hdr_clone,
+ (pjsip_hdr_clone_fptr) &pjsip_via_hdr_shallow_clone,
+ (pjsip_hdr_print_fptr) &pjsip_via_hdr_print,
+};
+
+PJ_DEF(pjsip_via_hdr*) pjsip_via_hdr_create( pj_pool_t *pool )
+{
+ pjsip_via_hdr *hdr = pj_pool_calloc(pool, 1, sizeof(*hdr));
+ init_hdr(hdr, PJSIP_H_VIA, &via_hdr_vptr);
+ hdr->sent_by.port = 5060;
+ hdr->ttl_param = -1;
+ hdr->rport_param = -1;
+ pj_list_init(&hdr->other_param);
+ return hdr;
+}
+
+static int pjsip_via_hdr_print( pjsip_via_hdr *hdr,
+ char *buf, pj_size_t size)
+{
+ int printed;
+ char *startbuf = buf;
+ char *endbuf = buf + size;
+ pj_str_t sip_ver = { "SIP/2.0/", 8 };
+
+ if ((pj_ssize_t)size < hdr->name.slen + sip_ver.slen +
+ hdr->transport.slen + hdr->sent_by.host.slen + 12)
+ {
+ return -1;
+ }
+
+ /* pjsip_hdr_names */
+ copy_advance(buf, hdr->name);
+ *buf++ = ':';
+ *buf++ = ' ';
+
+ /* SIP/2.0/transport host:port */
+ pj_memcpy(buf, sip_ver.ptr, sip_ver.slen);
+ buf += sip_ver.slen;
+ pj_memcpy(buf, hdr->transport.ptr, hdr->transport.slen);
+ buf += hdr->transport.slen;
+ *buf++ = ' ';
+ pj_memcpy(buf, hdr->sent_by.host.ptr, hdr->sent_by.host.slen);
+ buf += hdr->sent_by.host.slen;
+ *buf++ = ':';
+ printed = pj_utoa(hdr->sent_by.port, buf);
+ buf += printed;
+
+ if (hdr->ttl_param >= 0) {
+ size = endbuf-buf;
+ if (size < 14)
+ return -1;
+ pj_memcpy(buf, ";ttl=", 5);
+ printed = pj_utoa(hdr->ttl_param, buf+5);
+ buf += printed + 5;
+ }
+
+ if (hdr->rport_param >= 0) {
+ size = endbuf-buf;
+ if (size < 14)
+ return -1;
+ pj_memcpy(buf, ";rport", 6);
+ buf += 6;
+ if (hdr->rport_param > 0) {
+ *buf++ = '=';
+ buf += pj_utoa(hdr->rport_param, buf);
+ }
+ }
+
+
+ copy_advance_pair(buf, ";maddr=", 7, hdr->maddr_param);
+ copy_advance_pair(buf, ";received=", 10, hdr->recvd_param);
+ copy_advance_pair(buf, ";branch=", 8, hdr->branch_param);
+
+ printed = pjsip_param_print_on(&hdr->other_param, buf, endbuf-buf, ';');
+ if (printed < 0)
+ return -1;
+ buf += printed;
+
+ return buf-startbuf;
+}
+
+static pjsip_via_hdr* pjsip_via_hdr_clone( pj_pool_t *pool,
+ const pjsip_via_hdr *rhs)
+{
+ pjsip_via_hdr *hdr = pjsip_via_hdr_create(pool);
+ pj_strdup(pool, &hdr->transport, &rhs->transport);
+ pj_strdup(pool, &hdr->sent_by.host, &rhs->sent_by.host);
+ hdr->sent_by.port = rhs->sent_by.port;
+ hdr->ttl_param = rhs->ttl_param;
+ hdr->rport_param = rhs->rport_param;
+ pj_strdup(pool, &hdr->maddr_param, &rhs->maddr_param);
+ pj_strdup(pool, &hdr->recvd_param, &rhs->recvd_param);
+ pj_strdup(pool, &hdr->branch_param, &rhs->branch_param);
+ pjsip_param_clone(pool, &hdr->other_param, &rhs->other_param);
+ return hdr;
+}
+
+static pjsip_via_hdr* pjsip_via_hdr_shallow_clone( pj_pool_t *pool,
+ const pjsip_via_hdr *rhs )
+{
+ pjsip_via_hdr *hdr = pj_pool_alloc(pool, sizeof(*hdr));
+ pj_memcpy(hdr, rhs, sizeof(*hdr));
+ pjsip_param_shallow_clone(pool, &hdr->other_param, &rhs->other_param);
+ return hdr;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+/*
+ * General purpose function to textual data in a SIP body.
+ */
+PJ_DEF(int) pjsip_print_text_body(pjsip_msg_body *msg_body, char *buf, pj_size_t size)
+{
+ if (size < msg_body->len)
+ return -1;
+ pj_memcpy(buf, msg_body->data, msg_body->len);
+ return msg_body->len;
+}
diff --git a/pjsip/src/pjsip/sip_parser.c b/pjsip/src/pjsip/sip_parser.c
index 7b869492..4af7a630 100644
--- a/pjsip/src/pjsip/sip_parser.c
+++ b/pjsip/src/pjsip/sip_parser.c
@@ -1,1774 +1,1790 @@
-/* $Id$ */
-/*
- * Copyright (C) 2003-2006 Benny Prijono <benny@prijono.org>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
-#include <pjsip/sip_parser.h>
-#include <pjsip/sip_uri.h>
-#include <pjsip/sip_msg.h>
-#include <pjsip/sip_auth_parser.h>
-#include <pjsip/sip_errno.h>
-#include <pjsip/sip_transport.h> /* rdata structure */
-#include <pjlib-util/scanner.h>
-#include <pjlib-util/string.h>
-#include <pj/except.h>
-#include <pj/log.h>
-#include <pj/hash.h>
-#include <pj/os.h>
-#include <pj/pool.h>
-#include <pj/string.h>
-#include <pj/ctype.h>
-#include <pj/assert.h>
-
-#define ALNUM
-#define RESERVED ";/?:@&=+$,"
-#define MARK "-_.!~*'()"
-#define UNRESERVED ALNUM MARK
-#define ESCAPED "%"
-#define USER_UNRESERVED "&=+$,;?/"
-#define PASS "&=+$,"
-#define TOKEN "-.!%*_=`'~+" /* '+' is because of app/pidf+xml
- * in Content-Type! */
-#define HOST "_-."
-#define HEX_DIGIT "abcdefABCDEF"
-#define PARAM_CHAR "[]/:&+$" UNRESERVED ESCAPED
-#define HNV_UNRESERVED "[]/?:+$"
-#define HDR_CHAR HNV_UNRESERVED UNRESERVED ESCAPED
-
-#define PJSIP_VERSION "SIP/2.0"
-#define PJSIP_SYN_ERR_EXCEPTION 1
-
-#define UNREACHED(expr)
-
-typedef struct handler_rec
-{
- char hname[PJSIP_MAX_HNAME_LEN+1];
- pj_size_t hname_len;
- pj_uint32_t hname_hash;
- pjsip_parse_hdr_func *handler;
-} handler_rec;
-
-static handler_rec handler[127];
-static unsigned handler_count;
-static int parser_is_initialized;
-
-
-/*
- * Global vars (also extern).
- */
-const pj_str_t pjsip_USER_STR = { "user", 4};
-const pj_str_t pjsip_METHOD_STR = { "method", 6};
-const pj_str_t pjsip_TRANSPORT_STR = { "transport", 9};
-const pj_str_t pjsip_MADDR_STR = { "maddr", 5 };
-const pj_str_t pjsip_LR_STR = { "lr", 2 };
-const pj_str_t pjsip_SIP_STR = { "sip", 3 };
-const pj_str_t pjsip_SIPS_STR = { "sips", 4 };
-const pj_str_t pjsip_TEL_STR = { "tel", 3 };
-const pj_str_t pjsip_BRANCH_STR = { "branch", 6 };
-const pj_str_t pjsip_TTL_STR = { "ttl", 3 };
-const pj_str_t pjsip_PNAME_STR = { "received", 8 };
-const pj_str_t pjsip_Q_STR = { "q", 1 };
-const pj_str_t pjsip_EXPIRES_STR = { "expires", 7 };
-const pj_str_t pjsip_TAG_STR = { "tag", 3 };
-const pj_str_t pjsip_RPORT_STR = { "rport", 5};
-
-/* Character Input Specification buffer. */
-static pj_cis_buf_t cis_buf;
-
-/* Character Input Specifications. */
-pj_cis_t pjsip_HOST_SPEC, /* For scanning host part. */
- pjsip_DIGIT_SPEC, /* Decimal digits */
- pjsip_ALPHA_SPEC, /* Alpha (A-Z, a-z) */
- pjsip_ALNUM_SPEC, /* Decimal + Alpha. */
- pjsip_TOKEN_SPEC, /* Token. */
- pjsip_HEX_SPEC, /* Hexadecimal digits. */
- pjsip_PARAM_CHAR_SPEC, /* For scanning pname (or pvalue when
- * it's not quoted.) */
- pjsip_HDR_CHAR_SPEC, /* Chars in hname or hvalue */
- pjsip_PROBE_USER_HOST_SPEC, /* Hostname characters. */
- pjsip_PASSWD_SPEC, /* Password. */
- pjsip_USER_SPEC, /* User */
- pjsip_ARRAY_ELEMENTS, /* Array separator. */
- pjsip_NEWLINE_OR_EOF_SPEC, /* For eating up header.*/
- pjsip_DISPLAY_SCAN_SPEC; /* Used when searching for display name
- * in URL. */
-
-
-/*
- * Forward decl.
- */
-static pjsip_msg * int_parse_msg( pjsip_parse_ctx *ctx,
- pjsip_parser_err_report *err_list);
-static void int_parse_param( pj_scanner *scanner,
- pj_str_t *pname,
- pj_str_t *pvalue);
-static void int_parse_hparam( pj_scanner *scanner,
- pj_str_t *hname,
- pj_str_t *hvalue );
-static void int_parse_req_line( pj_scanner *scanner,
- pj_pool_t *pool,
- pjsip_request_line *req_line);
-static int int_is_next_user( pj_scanner *scanner);
-static void int_parse_status_line( pj_scanner *scanner,
- pjsip_status_line *line);
-static void int_parse_user_pass( pj_scanner *scanner,
- pj_str_t *user,
- pj_str_t *pass);
-static void int_parse_uri_host_port( pj_scanner *scanner,
- pj_str_t *p_host,
- int *p_port);
-static pjsip_uri * int_parse_uri_or_name_addr( pj_scanner *scanner,
- pj_pool_t *pool,
- unsigned option);
-static pjsip_url * int_parse_sip_url( pj_scanner *scanner,
- pj_pool_t *pool,
- pj_bool_t parse_params);
-static pjsip_name_addr *
- int_parse_name_addr( pj_scanner *scanner,
- pj_pool_t *pool );
-static void parse_hdr_end( pj_scanner *scanner );
-
-static pjsip_hdr* parse_hdr_accept( pjsip_parse_ctx *ctx );
-static pjsip_hdr* parse_hdr_allow( pjsip_parse_ctx *ctx );
-static pjsip_hdr* parse_hdr_call_id( pjsip_parse_ctx *ctx);
-static pjsip_hdr* parse_hdr_contact( pjsip_parse_ctx *ctx);
-static pjsip_hdr* parse_hdr_content_len( pjsip_parse_ctx *ctx );
-static pjsip_hdr* parse_hdr_content_type( pjsip_parse_ctx *ctx );
-static pjsip_hdr* parse_hdr_cseq( pjsip_parse_ctx *ctx );
-static pjsip_hdr* parse_hdr_expires( pjsip_parse_ctx *ctx );
-static pjsip_hdr* parse_hdr_from( pjsip_parse_ctx *ctx );
-static pjsip_hdr* parse_hdr_max_forwards( pjsip_parse_ctx *ctx);
-static pjsip_hdr* parse_hdr_min_expires( pjsip_parse_ctx *ctx );
-static pjsip_hdr* parse_hdr_rr( pjsip_parse_ctx *ctx );
-static pjsip_hdr* parse_hdr_route( pjsip_parse_ctx *ctx );
-static pjsip_hdr* parse_hdr_require( pjsip_parse_ctx *ctx );
-static pjsip_hdr* parse_hdr_retry_after( pjsip_parse_ctx *ctx );
-static pjsip_hdr* parse_hdr_supported( pjsip_parse_ctx *ctx );
-static pjsip_hdr* parse_hdr_to( pjsip_parse_ctx *ctx );
-static pjsip_hdr* parse_hdr_unsupported( pjsip_parse_ctx *ctx );
-static pjsip_hdr* parse_hdr_via( pjsip_parse_ctx *ctx );
-static pjsip_hdr* parse_hdr_generic_string( pjsip_parse_ctx *ctx);
-
-/* Convert non NULL terminated string to integer. */
-static unsigned long pj_strtoul_mindigit(const pj_str_t *str,
- unsigned mindig)
-{
- unsigned long value;
- unsigned i;
-
- value = 0;
- for (i=0; i<(unsigned)str->slen; ++i) {
- value = value * 10 + (str->ptr[i] - '0');
- }
- for (; i<mindig; ++i) {
- value = value * 10;
- }
- return value;
-}
-
-/* Case insensitive comparison */
-#define parser_stricmp(str1, str2) pj_stricmp(&str1, &str2)
-
-
-/* Syntax error handler for parser. */
-static void on_syntax_error(pj_scanner *scanner)
-{
- PJ_UNUSED_ARG(scanner);
- PJ_THROW(PJSIP_SYN_ERR_EXCEPTION);
-}
-
-/* Concatenate unrecognized params into single string. */
-void pjsip_concat_param_imp( pj_str_t *param, pj_pool_t *pool,
- const pj_str_t *pname, const pj_str_t *pvalue,
- int sepchar)
-{
- char *new_param, *p;
- int len;
-
- len = param->slen + pname->slen + pvalue->slen + 3;
- p = new_param = pj_pool_alloc(pool, len);
-
- if (param->slen) {
- int old_len = param->slen;
- pj_memcpy(p, param->ptr, old_len);
- p += old_len;
- }
- *p++ = (char)sepchar;
- pj_memcpy(p, pname->ptr, pname->slen);
- p += pname->slen;
-
- if (pvalue->slen) {
- *p++ = '=';
- pj_memcpy(p, pvalue->ptr, pvalue->slen);
- p += pvalue->slen;
- }
-
- *p = '\0';
-
- param->ptr = new_param;
- param->slen = p - new_param;
-}
-
-/* Concatenate unrecognized params into single string. */
-static void concat_param( pj_str_t *param, pj_pool_t *pool,
- const pj_str_t *pname, const pj_str_t *pvalue )
-{
- pjsip_concat_param_imp(param, pool, pname, pvalue, ';');
-}
-
-/* Initialize static properties of the parser. */
-static pj_status_t init_parser()
-{
- static int initialized;
- pj_status_t status;
-
- if (initialized)
- return PJ_SUCCESS;
-
- initialized = 1;
-
- pj_cis_buf_init(&cis_buf);
-
- status = pj_cis_init(&cis_buf, &pjsip_DIGIT_SPEC);
- PJ_ASSERT_RETURN(status == PJ_SUCCESS, status);
- pj_cis_add_num(&pjsip_DIGIT_SPEC);
-
- status = pj_cis_init(&cis_buf, &pjsip_ALPHA_SPEC);
- PJ_ASSERT_RETURN(status == PJ_SUCCESS, status);
- pj_cis_add_alpha( &pjsip_ALPHA_SPEC );
-
- status = pj_cis_init(&cis_buf, &pjsip_ALNUM_SPEC);
- PJ_ASSERT_RETURN(status == PJ_SUCCESS, status);
- pj_cis_add_alpha( &pjsip_ALNUM_SPEC );
- pj_cis_add_num( &pjsip_ALNUM_SPEC );
-
- status = pj_cis_init(&cis_buf, &pjsip_NEWLINE_OR_EOF_SPEC);
- PJ_ASSERT_RETURN(status == PJ_SUCCESS, status);
- pj_cis_add_str(&pjsip_NEWLINE_OR_EOF_SPEC, "\r\n");
- //pj_cs_set(pjsip_NEWLINE_OR_EOF_SPEC, 0);
-
- status = pj_cis_init(&cis_buf, &pjsip_ARRAY_ELEMENTS);
- PJ_ASSERT_RETURN(status == PJ_SUCCESS, status);
- pj_cis_add_str( &pjsip_ARRAY_ELEMENTS, ",\r\n");
-
- status = pj_cis_dup(&pjsip_TOKEN_SPEC, &pjsip_ALNUM_SPEC);
- PJ_ASSERT_RETURN(status == PJ_SUCCESS, status);
- pj_cis_add_str( &pjsip_TOKEN_SPEC, TOKEN);
-
- status = pj_cis_dup(&pjsip_HOST_SPEC, &pjsip_ALNUM_SPEC);
- PJ_ASSERT_RETURN(status == PJ_SUCCESS, status);
- pj_cis_add_str( &pjsip_HOST_SPEC, HOST);
-
- status = pj_cis_dup(&pjsip_HEX_SPEC, &pjsip_DIGIT_SPEC);
- PJ_ASSERT_RETURN(status == PJ_SUCCESS, status);
- pj_cis_add_str( &pjsip_HEX_SPEC, HEX_DIGIT);
-
- status = pj_cis_dup(&pjsip_PARAM_CHAR_SPEC, &pjsip_ALNUM_SPEC);
- PJ_ASSERT_RETURN(status == PJ_SUCCESS, status);
- pj_cis_add_str(&pjsip_PARAM_CHAR_SPEC, PARAM_CHAR);
-
- status = pj_cis_dup(&pjsip_HDR_CHAR_SPEC, &pjsip_ALNUM_SPEC);
- PJ_ASSERT_RETURN(status == PJ_SUCCESS, status);
- pj_cis_add_str(&pjsip_HDR_CHAR_SPEC, HDR_CHAR);
-
- status = pj_cis_dup(&pjsip_USER_SPEC, &pjsip_ALNUM_SPEC);
- PJ_ASSERT_RETURN(status == PJ_SUCCESS, status);
- pj_cis_add_str( &pjsip_USER_SPEC, ESCAPED USER_UNRESERVED );
-
- status = pj_cis_dup(&pjsip_PASSWD_SPEC, &pjsip_ALNUM_SPEC);
- PJ_ASSERT_RETURN(status == PJ_SUCCESS, status);
- pj_cis_add_str( &pjsip_PASSWD_SPEC, MARK ESCAPED PASS);
-
- status = pj_cis_init(&cis_buf, &pjsip_PROBE_USER_HOST_SPEC);
- PJ_ASSERT_RETURN(status == PJ_SUCCESS, status);
- pj_cis_add_str( &pjsip_PROBE_USER_HOST_SPEC, "@ \n>");
- pj_cis_invert( &pjsip_PROBE_USER_HOST_SPEC );
-
- status = pj_cis_init(&cis_buf, &pjsip_DISPLAY_SCAN_SPEC);
- PJ_ASSERT_RETURN(status == PJ_SUCCESS, status);
- pj_cis_add_str( &pjsip_DISPLAY_SCAN_SPEC, ":\r\n<");
-
- status = pjsip_register_hdr_parser( "Accept", NULL, &parse_hdr_accept);
- PJ_ASSERT_RETURN(status == PJ_SUCCESS, status);
-
- status = pjsip_register_hdr_parser( "Allow", NULL, &parse_hdr_allow);
- PJ_ASSERT_RETURN(status == PJ_SUCCESS, status);
-
- status = pjsip_register_hdr_parser( "Call-ID", NULL, &parse_hdr_call_id);
- PJ_ASSERT_RETURN(status == PJ_SUCCESS, status);
-
- status = pjsip_register_hdr_parser( "Contact", "m", &parse_hdr_contact);
- PJ_ASSERT_RETURN(status == PJ_SUCCESS, status);
-
- status = pjsip_register_hdr_parser( "Content-Length", NULL,
- &parse_hdr_content_len);
- PJ_ASSERT_RETURN(status == PJ_SUCCESS, status);
-
- status = pjsip_register_hdr_parser( "Content-Type", NULL,
- &parse_hdr_content_type);
- PJ_ASSERT_RETURN(status == PJ_SUCCESS, status);
-
- status = pjsip_register_hdr_parser( "CSeq", NULL, &parse_hdr_cseq);
- PJ_ASSERT_RETURN(status == PJ_SUCCESS, status);
-
- status = pjsip_register_hdr_parser( "Expires", NULL, &parse_hdr_expires);
- PJ_ASSERT_RETURN(status == PJ_SUCCESS, status);
-
- status = pjsip_register_hdr_parser( "From", "f", &parse_hdr_from);
- PJ_ASSERT_RETURN(status == PJ_SUCCESS, status);
-
- status = pjsip_register_hdr_parser( "Max-Forwards", NULL,
- &parse_hdr_max_forwards);
- PJ_ASSERT_RETURN(status == PJ_SUCCESS, status);
-
- status = pjsip_register_hdr_parser( "Min-Expires", NULL,
- &parse_hdr_min_expires);
- PJ_ASSERT_RETURN(status == PJ_SUCCESS, status);
-
- status = pjsip_register_hdr_parser( "Record-Route", NULL, &parse_hdr_rr);
- PJ_ASSERT_RETURN(status == PJ_SUCCESS, status);
-
- status = pjsip_register_hdr_parser( "Route", NULL, &parse_hdr_route);
- PJ_ASSERT_RETURN(status == PJ_SUCCESS, status);
-
- status = pjsip_register_hdr_parser( "Require", NULL, &parse_hdr_require);
- PJ_ASSERT_RETURN(status == PJ_SUCCESS, status);
-
- status = pjsip_register_hdr_parser( "Retry-After", NULL,
- &parse_hdr_retry_after);
- PJ_ASSERT_RETURN(status == PJ_SUCCESS, status);
-
- status = pjsip_register_hdr_parser( "Supported", "k",
- &parse_hdr_supported);
- PJ_ASSERT_RETURN(status == PJ_SUCCESS, status);
-
- status = pjsip_register_hdr_parser( "To", "t", &parse_hdr_to);
- PJ_ASSERT_RETURN(status == PJ_SUCCESS, status);
-
- status = pjsip_register_hdr_parser( "Unsupported", NULL,
- &parse_hdr_unsupported);
- PJ_ASSERT_RETURN(status == PJ_SUCCESS, status);
-
- status = pjsip_register_hdr_parser( "Via", NULL, &parse_hdr_via);
- PJ_ASSERT_RETURN(status == PJ_SUCCESS, status);
-
- /* Register auth parser. */
- status = pjsip_auth_init_parser();
-
- return status;
-}
-
-static void init_sip_parser(void)
-{
- if (!parser_is_initialized) {
- /* Prevent race cond. */
- pj_enter_critical_section();
- if (!parser_is_initialized) {
- init_parser();
- parser_is_initialized = 1;
- }
- pj_leave_critical_section();
- }
-}
-
-/* Compare the handler record with header name, and return:
- * - 0 if handler match.
- * - <0 if handler is 'less' than the header name.
- * - >0 if handler is 'greater' than header name.
- */
-static int compare_handler( const handler_rec *r1,
- const char *name,
- pj_size_t name_len,
- pj_uint32_t hash )
-{
- /* Compare length. */
- if (r1->hname_len < name_len)
- return -1;
- if (r1->hname_len > name_len)
- return 1;
-
- /* Length is equal, compare hashed value. */
- if (r1->hname_hash < hash)
- return -1;
- if (r1->hname_hash > hash)
- return 1;
-
- /* Equal length and equal hash. compare the strings. */
- return pj_native_strcmp(r1->hname, name);
-}
-
-/* Register one handler for one header name. */
-static pj_status_t int_register_parser( const char *name,
- pjsip_parse_hdr_func *fptr )
-{
- unsigned pos;
- handler_rec rec;
- unsigned i;
-
- if (handler_count >= PJ_ARRAY_SIZE(handler)) {
- return PJ_ETOOMANY;
- }
-
- /* Initialize temporary handler. */
- rec.handler = fptr;
- rec.hname_len = strlen(name);
- if (rec.hname_len >= sizeof(rec.hname)) {
- return PJ_ENAMETOOLONG;
- }
- /* Name is copied in lowercase. */
- for (i=0; i<rec.hname_len; ++i) {
- rec.hname[i] = (char)pj_tolower(name[i]);
- }
- rec.hname[i] = '\0';
- /* Hash value is calculated from the lowercase name. */
- rec.hname_hash = pj_hash_calc(0, rec.hname, PJ_HASH_KEY_STRING);
-
- /* Get the pos to insert the new handler. */
- for (pos=0; pos < handler_count; ++pos) {
- int d;
- d = compare_handler(&handler[pos], rec.hname, rec.hname_len,
- rec.hname_hash);
- if (d == 0) {
- pj_assert(0);
- return PJ_EEXISTS;
- }
- if (d > 0) {
- break;
- }
- }
-
- /* Shift handlers. */
- if (pos != handler_count) {
- pj_memmove( &handler[pos+1], &handler[pos],
- (handler_count-pos)*sizeof(handler_rec));
- }
- /* Add new handler. */
- pj_memcpy( &handler[pos], &rec, sizeof(handler_rec));
- ++handler_count;
-
- return PJ_SUCCESS;
-}
-
-/* Register parser handler. If both header name and short name are valid,
- * then two instances of handler will be registered.
- */
-PJ_DEF(pj_status_t) pjsip_register_hdr_parser( const char *hname,
- const char *hshortname,
- pjsip_parse_hdr_func *fptr)
-{
- pj_status_t status;
-
- status = int_register_parser(hname, fptr);
- if (status != PJ_SUCCESS) {
- return status;
- }
- if (hshortname) {
- status = int_register_parser(hshortname, fptr);
- if (status != PJ_SUCCESS)
- return status;
- }
- return PJ_SUCCESS;
-}
-
-/* Find handler to parse the header name. */
-static pjsip_parse_hdr_func * find_handler(const pj_str_t *hname)
-{
- handler_rec *first;
- char hname_copy[PJSIP_MAX_HNAME_LEN];
- pj_uint32_t hash;
- int comp;
- unsigned n;
-
- if (hname->slen >= PJSIP_MAX_HNAME_LEN) {
- pj_assert(!"Header name is too long!");
- return NULL;
- }
-
- /* Calculate hash value while converting the header to lowercase.
- * Don't assume that 'hname' is NULL terminated.
- */
- hash = pj_hash_calc_tolower(0, hname_copy, hname);
- hname_copy[hname->slen] = '\0';
-
- /* Binary search for the handler. */
- comp = -1;
- first = &handler[0];
- n = handler_count;
- for (; n > 0; ) {
- unsigned half = n / 2;
- handler_rec *mid = first + half;
-
- comp = compare_handler(mid, hname_copy, hname->slen, hash);
- if (comp < 0) {
- first = ++mid;
- n -= half + 1;
- } else if (comp==0) {
- first = mid;
- break;
- } else {
- n = half;
- }
- }
-
- return comp==0 ? first->handler : NULL;
-}
-
-/* Public function to parse SIP message. */
-PJ_DEF(pjsip_msg*) pjsip_parse_msg( pj_pool_t *pool,
- char *buf, pj_size_t size,
- pjsip_parser_err_report *err_list)
-{
- pjsip_msg *msg = NULL;
- pj_scanner scanner;
- pjsip_parse_ctx context;
- PJ_USE_EXCEPTION;
-
- init_sip_parser();
-
- pj_scan_init(&scanner, buf, size, PJ_SCAN_AUTOSKIP_WS_HEADER,
- &on_syntax_error);
-
- context.scanner = &scanner;
- context.pool = pool;
- context.rdata = NULL;
-
- PJ_TRY {
- msg = int_parse_msg(&context, err_list);
- }
- PJ_DEFAULT {
- msg = NULL;
- }
- PJ_END
-
- pj_scan_fini(&scanner);
- return msg;
-}
-
-/* Public function to parse as rdata.*/
-PJ_DEF(pjsip_msg *) pjsip_parse_rdata( char *buf, pj_size_t size,
- pjsip_rx_data *rdata )
-{
- pj_scanner scanner;
- pjsip_parse_ctx context;
- PJ_USE_EXCEPTION;
-
- init_sip_parser();
-
- pj_scan_init(&scanner, buf, size, PJ_SCAN_AUTOSKIP_WS_HEADER,
- &on_syntax_error);
-
- context.scanner = &scanner;
- context.pool = rdata->tp_info.pool;
- context.rdata = rdata;
-
- PJ_TRY {
- rdata->msg_info.msg = int_parse_msg(&context, &rdata->msg_info.parse_err);
- }
- PJ_DEFAULT {
- rdata->msg_info.msg = NULL;
- }
- PJ_END
-
- pj_scan_fini(&scanner);
- return rdata->msg_info.msg;
-}
-
-/* Determine if a message has been received. */
-PJ_DEF(pj_bool_t) pjsip_find_msg( const char *buf, pj_size_t size,
- pj_bool_t is_datagram, pj_size_t *msg_size)
-{
-#if PJ_HAS_TCP
- const char *hdr_end;
- const char *body_start;
- const char *pos;
- const char *line;
- int content_length = -1;
-
- *msg_size = size;
-
- /* For datagram, the whole datagram IS the message. */
- if (is_datagram) {
- return PJ_SUCCESS;
- }
-
-
- /* Find the end of header area by finding an empty line. */
- if ((pos = pj_native_strstr(buf, "\n\r\n")) == NULL) {
- return PJSIP_EPARTIALMSG;
- }
-
- hdr_end = pos+1;
- body_start = pos+3;
-
- /* Find "Content-Length" header the hard way. */
- line = pj_native_strchr(buf, '\n');
- while (line && line < hdr_end-14) {
- ++line;
- if ( ((*line=='C' || *line=='c') &&
- pj_native_strncasecmp(line, "Content-Length", 14) == 0) ||
- ((*line=='l' || *line=='L') &&
- (*(line+1)==' ' || *(line+1)=='\t' || *(line+1)==':')))
- {
- /* Try to parse the header. */
- pj_scanner scanner;
- PJ_USE_EXCEPTION;
-
- init_sip_parser();
-
- pj_scan_init(&scanner, (char*)line, hdr_end-line,
- PJ_SCAN_AUTOSKIP_WS_HEADER, &on_syntax_error);
-
- PJ_TRY {
- pj_str_t str_clen;
-
- /* Get "Content-Length" or "L" name */
- if (*line=='C' || *line=='c')
- pj_scan_advance_n(&scanner, 14, PJ_TRUE);
- else if (*line=='l' || *line=='L')
- pj_scan_advance_n(&scanner, 1, PJ_TRUE);
-
- /* Get colon */
- if (pj_scan_get_char(&scanner) != ':') {
- PJ_THROW(PJSIP_SYN_ERR_EXCEPTION);
- }
-
- /* Get number */
- pj_scan_get(&scanner, &pjsip_DIGIT_SPEC, &str_clen);
-
- /* Get newline. */
- pj_scan_get_newline(&scanner);
-
- /* Found a valid Content-Length header. */
- content_length = pj_strtoul(&str_clen);
- }
- PJ_END
-
- pj_scan_fini(&scanner);
- }
-
- /* Found valid Content-Length? */
- if (content_length != -1)
- break;
-
- /* Go to next line. */
- line = pj_native_strchr(line, '\n');
- }
-
- /* Found Content-Length? */
- if (content_length == -1) {
- return PJSIP_EMISSINGHDR;
- }
-
- /* Enough packet received? */
- *msg_size = (body_start - buf) + content_length;
- return (*msg_size) <= size ? PJ_SUCCESS : PJSIP_EPARTIALMSG;
-#else
- PJ_UNUSED_ARG(buf);
- PJ_UNUSED_ARG(is_datagram);
- *msg_size = size;
- return PJ_SUCCESS;
-#endif
-}
-
-/* Public function to parse URI */
-PJ_DEF(pjsip_uri*) pjsip_parse_uri( pj_pool_t *pool,
- char *buf, pj_size_t size,
- unsigned option)
-{
- PJ_USE_EXCEPTION;
- pj_scanner scanner;
- pjsip_uri *uri = NULL;
-
- init_sip_parser();
-
- pj_scan_init(&scanner, buf, size, 0, &on_syntax_error);
-
-
- PJ_TRY {
- uri = int_parse_uri_or_name_addr(&scanner, pool, option);
- }
- PJ_END;
-
- /* Must have exhausted all inputs. */
- if (pj_scan_is_eof(&scanner) || *scanner.curptr=='\r' ||
- *scanner.curptr=='\n')
- {
- /* Success. */
- pj_scan_fini(&scanner);
- return uri;
- }
-
- /* Still have some characters unparsed. */
- pj_scan_fini(&scanner);
- return NULL;
-}
-
-/* Generic function to print message body.
- * This assumes that the 'data' member points to a contigous memory where the
- * actual body is laid.
- */
-static int generic_print_body (pjsip_msg_body *msg_body,
- char *buf, pj_size_t size)
-{
- pjsip_msg_body *body = msg_body;
- if (size < body->len)
- return 0;
-
- pj_memcpy (buf, body->data, body->len);
- return body->len;
-}
-
-/* Internal function to parse SIP message */
-static pjsip_msg *int_parse_msg( pjsip_parse_ctx *ctx,
- pjsip_parser_err_report *err_list)
-{
- PJ_USE_EXCEPTION;
- int ch;
- pjsip_msg *msg;
- pjsip_ctype_hdr *ctype_hdr = NULL;
- pj_scanner *scanner = ctx->scanner;
- pj_pool_t *pool = ctx->pool;
-
- /* Skip leading newlines. */
- ch = *scanner->curptr;
- while (ch=='\r' || ch=='\n') {
- pj_scan_get_char(scanner);
- ch = *scanner->curptr;
- }
-
- msg = pjsip_msg_create(pool, PJSIP_REQUEST_MSG);
-
- /* Parse request or status line */
- if (pj_scan_stricmp( scanner, PJSIP_VERSION, 7) == 0) {
- msg->type = PJSIP_RESPONSE_MSG;
- int_parse_status_line( scanner, &msg->line.status );
- } else {
- msg->type = PJSIP_REQUEST_MSG;
- int_parse_req_line(scanner, pool, &msg->line.req );
- }
-
- /* Parse headers. */
- do {
- pj_str_t hname;
- pjsip_parse_hdr_func * handler;
- pjsip_hdr *hdr = NULL;
-
- /* Get hname. */
- pj_scan_get( scanner, &pjsip_TOKEN_SPEC, &hname);
- ch = pj_scan_get_char( scanner );
- if (ch != ':') {
- PJ_THROW(PJSIP_SYN_ERR_EXCEPTION);
- }
-
- /* Find handler. */
- handler = find_handler(&hname);
-
- PJ_TRY {
- /* Call the handler if found.
- * If no handler is found, then treat the header as generic
- * hname/hvalue pair.
- */
- if (handler) {
- hdr = (*handler)(ctx);
- } else {
- hdr = parse_hdr_generic_string(ctx);
- hdr->type = PJSIP_H_OTHER;
- hdr->name = hdr->sname = hname;
- }
-
- /* Check if we've just parsed a Content-Type header.
- * We will check for a message body if we've got Content-Type header.
- */
- if (hdr->type == PJSIP_H_CONTENT_TYPE) {
- ctype_hdr = (pjsip_ctype_hdr*)hdr;
- }
-
- }
- PJ_DEFAULT {
- /* Exception was thrown during parsing.
- * Skip until newline, and parse next header.
- */
- pj_str_t token;
- hdr = NULL;
-
- //PJ_LOG(4,("sipparser",
- // "Syntax error in line %d col %d (hname=%.*s)",
- // scanner->line, scanner->col, hname.slen, hname.ptr));
-
- if (err_list) {
- pjsip_parser_err_report *err_info;
-
- err_info = pj_pool_alloc(pool, sizeof(*err_info));
- err_info->exception_code = PJ_GET_EXCEPTION();
- err_info->line = scanner->line;
- err_info->col = scanner->col;
- err_info->hname = hname;
-
- pj_list_insert_before(err_list, err_info);
- }
-
- if (!pj_scan_is_eof(scanner)) {
- pj_scan_get_until(scanner, &pjsip_NEWLINE_OR_EOF_SPEC, &token);
- parse_hdr_end(scanner);
- }
- }
- PJ_END;
-
- if (hdr) {
- /* Single parse of header line can produce multiple headers.
- * For example, if one Contact: header contains Contact list
- * separated by comma, then these Contacts will be split into
- * different Contact headers.
- * So here we must insert list instead of just insert one header.
- */
- pj_list_insert_nodes_before(&msg->hdr, hdr);
- }
-
- /* Parse until EOF or an empty line is found. */
- } while (!pj_scan_is_eof(scanner) &&
- *scanner->curptr != '\r' && *scanner->curptr != '\n');
-
- /* If empty line is found, eat it. */
- if (!pj_scan_is_eof(scanner)) {
- if (*scanner->curptr=='\r' || *scanner->curptr=='\n') {
- pj_scan_get_newline(scanner);
- }
- }
-
- /* If we have Content-Type header, treat the rest of the message as body.*/
- if (ctype_hdr) {
- pjsip_msg_body *body = pj_pool_alloc(pool, sizeof(pjsip_msg_body));
- pj_strdup(pool, &body->content_type.type, &ctype_hdr->media.type);
- pj_strdup(pool, &body->content_type.subtype, &ctype_hdr->media.subtype);
- pj_strdup(pool, &body->content_type.param, &ctype_hdr->media.param);
- body->data = scanner->curptr;
- body->len = scanner->end - scanner->curptr;
- body->print_body = &generic_print_body;
-
- msg->body = body;
- }
-
- return msg;
-}
-
-/* Parse parameter (pname ["=" pvalue]). */
-void pjsip_parse_param_imp( pj_scanner *scanner,
- pj_str_t *pname, pj_str_t *pvalue,
- unsigned option)
-{
- /* pname */
- pj_scan_get(scanner, &pjsip_PARAM_CHAR_SPEC, pname);
- pj_str_unescape(pname);
-
- /* pvalue, if any */
- if (*scanner->curptr == '=') {
- pj_scan_get_char(scanner);
- /* pvalue can be a quoted string. */
- if (*scanner->curptr == '"') {
- pj_scan_get_quote( scanner, '"', '"', pvalue);
- if (option & PJSIP_PARSE_REMOVE_QUOTE) {
- pvalue->ptr++;
- pvalue->slen -= 2;
- }
- } else {
- pj_scan_get(scanner, &pjsip_PARAM_CHAR_SPEC, pvalue);
- pj_str_unescape(pvalue);
- }
- } else {
- pvalue->ptr = NULL;
- pvalue->slen = 0;
- }
-}
-
-/* Parse parameter (";" pname ["=" pvalue]). */
-static void int_parse_param( pj_scanner *scanner,
- pj_str_t *pname, pj_str_t *pvalue)
-{
- /* Get ';' character */
- pj_scan_get_char(scanner);
-
- /* Get pname and optionally pvalue */
- pjsip_parse_param_imp(scanner, pname, pvalue, 0);
-}
-
-/* Parse header parameter. */
-static void int_parse_hparam( pj_scanner *scanner,
- pj_str_t *hname, pj_str_t *hvalue )
-{
- /* Get '?' or '&' character. */
- pj_scan_get_char(scanner);
-
- /* hname */
- pj_scan_get(scanner, &pjsip_HDR_CHAR_SPEC, hname);
- pj_str_unescape(hname);
-
- /* pvalue, if any */
- if (*scanner->curptr == '=') {
- pj_scan_get_char(scanner);
- pj_scan_get(scanner, &pjsip_HDR_CHAR_SPEC, hvalue);
- pj_str_unescape(hvalue);
- } else {
- hvalue->ptr = NULL;
- hvalue->slen = 0;
- }
-}
-
-/* Parse host:port in URI. */
-static void int_parse_uri_host_port( pj_scanner *scanner,
- pj_str_t *host, int *p_port)
-{
- pj_scan_get( scanner, &pjsip_HOST_SPEC, host);
- /* RFC3261 section 19.1.2: host don't need to be unescaped */
- if (*scanner->curptr == ':') {
- pj_str_t port;
- pj_scan_get_char(scanner);
- pj_scan_get(scanner, &pjsip_DIGIT_SPEC, &port);
- pj_str_unescape(&port);
- *p_port = pj_strtoul(&port);
- } else {
- *p_port = 0;
- }
-}
-
-/* Determine if the next token in an URI is a user specification. */
-static int int_is_next_user(pj_scanner *scanner)
-{
- pj_str_t dummy;
- int is_user;
-
- /* Find character '@'. If this character exist, then the token
- * must be a username.
- */
- if (pj_scan_peek( scanner, &pjsip_PROBE_USER_HOST_SPEC, &dummy) == '@')
- is_user = 1;
- else
- is_user = 0;
-
- return is_user;
-}
-
-/* Parse user:pass tokens in an URI. */
-static void int_parse_user_pass( pj_scanner *scanner,
- pj_str_t *user, pj_str_t *pass)
-{
- pj_scan_get( scanner, &pjsip_USER_SPEC, user);
- pj_str_unescape(user);
-
- if ( *scanner->curptr == ':') {
- pj_scan_get_char( scanner );
- pj_scan_get( scanner, &pjsip_PASSWD_SPEC, pass);
- pj_str_unescape(pass);
- } else {
- pass->ptr = NULL;
- pass->slen = 0;
- }
-
- /* Get the '@' */
- pj_scan_get_char( scanner );
-}
-
-/* Parse all types of URI. */
-static pjsip_uri *int_parse_uri_or_name_addr( pj_scanner *scanner, pj_pool_t *pool,
- unsigned opt)
-{
- pjsip_uri *uri;
- int is_name_addr = 0;
-
- /* Exhaust any whitespaces. */
- pj_scan_skip_whitespace(scanner);
-
- if (*scanner->curptr=='"' || *scanner->curptr=='<') {
- uri = (pjsip_uri*)int_parse_name_addr( scanner, pool );
- is_name_addr = 1;
- } else {
- pj_scan_state backtrack;
- pj_str_t scheme;
- int colon;
-
- pj_scan_save_state( scanner, &backtrack);
- pj_scan_get( scanner, &pjsip_TOKEN_SPEC, &scheme);
- colon = pj_scan_get_char( scanner );
- pj_scan_restore_state( scanner, &backtrack);
-
- if (colon==':' &&
- (parser_stricmp(scheme, pjsip_SIP_STR)==0 ||
- parser_stricmp(scheme, pjsip_SIPS_STR)==0))
- {
- uri = (pjsip_uri*)
- int_parse_sip_url( scanner, pool,
- (opt & PJSIP_PARSE_URI_IN_FROM_TO_HDR)== 0);
-
- } else if (colon==':' && parser_stricmp( scheme, pjsip_TEL_STR)==0) {
-
- /* Not supported. */
- PJ_THROW(PJSIP_SYN_ERR_EXCEPTION);
- UNREACHED({return NULL; /* Not reached. */});
-
- } else {
- uri = (pjsip_uri*)int_parse_name_addr( scanner, pool );
- is_name_addr = 1;
- }
- }
-
- /* Should we return the URI object as name address? */
- if (opt & PJSIP_PARSE_URI_AS_NAMEADDR) {
- if (is_name_addr == 0) {
- pjsip_name_addr *name_addr;
-
- name_addr = pjsip_name_addr_create(pool);
- name_addr->uri = uri;
-
- uri = (pjsip_uri*)name_addr;
- }
- }
-
- return uri;
-}
-
-/* Parse URI. */
-static pjsip_uri *int_parse_uri(pj_scanner *scanner, pj_pool_t *pool,
- pj_bool_t parse_params)
-{
- if (*scanner->curptr=='"' || *scanner->curptr=='<') {
- return (pjsip_uri*)int_parse_name_addr( scanner, pool );
- } else {
- pj_str_t scheme;
- int colon;
-
- /* Get scheme. */
- colon = pj_scan_peek(scanner, &pjsip_TOKEN_SPEC, &scheme);
- if (colon != ':') {
- PJ_THROW(PJSIP_SYN_ERR_EXCEPTION);
- }
-
- if ((parser_stricmp(scheme, pjsip_SIP_STR)==0 ||
- parser_stricmp(scheme, pjsip_SIPS_STR)==0))
- {
- return (pjsip_uri*)int_parse_sip_url( scanner, pool, parse_params);
-
- } else if (parser_stricmp(scheme, pjsip_TEL_STR)==0) {
- PJ_THROW(PJSIP_SYN_ERR_EXCEPTION);
- UNREACHED({ return NULL; /* Not reached. */ })
-
- } else {
- PJ_THROW(PJSIP_SYN_ERR_EXCEPTION);
- UNREACHED({ return NULL; /* Not reached. */ })
- }
- }
-}
-
-/* Parse "sip:" and "sips:" URI. */
-static pjsip_url *int_parse_sip_url( pj_scanner *scanner,
- pj_pool_t *pool,
- pj_bool_t parse_params)
-{
- pj_str_t scheme;
- pjsip_url *url;
- int colon;
- int skip_ws = scanner->skip_ws;
- int hsep = '?';
- scanner->skip_ws = 0;
-
- pj_scan_get(scanner, &pjsip_TOKEN_SPEC, &scheme);
- colon = pj_scan_get_char(scanner);
- if (colon != ':') {
- PJ_THROW(PJSIP_SYN_ERR_EXCEPTION);
- }
-
- if (parser_stricmp(scheme, pjsip_SIP_STR)==0) {
- url = pjsip_url_create(pool, 0);
-
- } else if (parser_stricmp(scheme, pjsip_SIPS_STR)==0) {
- url = pjsip_url_create(pool, 1);
-
- } else {
- PJ_THROW(PJSIP_SYN_ERR_EXCEPTION);
- /* should not reach here */
- UNREACHED({
- pj_assert(0);
- return 0;
- })
- }
-
- if (int_is_next_user(scanner)) {
- int_parse_user_pass(scanner, &url->user, &url->passwd);
- }
-
- /* Get host:port */
- int_parse_uri_host_port(scanner, &url->host, &url->port);
-
- /* Get URL parameters. */
- while ( parse_params && *scanner->curptr == ';' ) {
- pj_str_t pname, pvalue;
-
- int_parse_param( scanner, &pname, &pvalue);
-
- if (!parser_stricmp(pname, pjsip_USER_STR) && pvalue.slen) {
- url->user_param = pvalue;
-
- } else if (!parser_stricmp(pname, pjsip_METHOD_STR) && pvalue.slen) {
- url->method_param = pvalue;
-
- } else if (!parser_stricmp(pname,pjsip_TRANSPORT_STR) && pvalue.slen) {
- url->transport_param = pvalue;
-
- } else if (!parser_stricmp(pname, pjsip_TTL_STR) && pvalue.slen) {
- url->ttl_param = pj_strtoul(&pvalue);
-
- } else if (!parser_stricmp(pname, pjsip_MADDR_STR) && pvalue.slen) {
- url->maddr_param = pvalue;
-
- } else if (!parser_stricmp(pname, pjsip_LR_STR)) {
- url->lr_param = 1;
-
- } else {
- pjsip_param *p = pj_pool_alloc(pool, sizeof(pjsip_param));
- p->name = pname;
- p->value = pvalue;
- pj_list_insert_before(&url->other_param, p);
- }
- }
-
- /* Get header params. */
- while (parse_params && *scanner->curptr == hsep) {
- pjsip_param *param;
- param = pj_pool_alloc(pool, sizeof(pjsip_param));
- int_parse_hparam(scanner, &param->name, &param->value);
- pj_list_insert_before(&url->header_param, param);
- hsep = '&';
- }
-
- scanner->skip_ws = skip_ws;
- pj_scan_skip_whitespace(scanner);
- return url;
-}
-
-/* Parse nameaddr. */
-static pjsip_name_addr *int_parse_name_addr( pj_scanner *scanner,
- pj_pool_t *pool )
-{
- int has_bracket;
- pjsip_name_addr *name_addr;
-
- name_addr = pjsip_name_addr_create(pool);
-
- if (*scanner->curptr == '"') {
- pj_scan_get_quote( scanner, '"', '"', &name_addr->display);
-
- } else if (*scanner->curptr != '<') {
- int next;
- pj_str_t dummy;
-
- /* This can be either the start of display name,
- * the start of URL ("sip:", "sips:", "tel:", etc.), or '<' char.
- * We're only interested in display name, because SIP URL
- * will be parser later.
- */
- next = pj_scan_peek_until(scanner, &pjsip_DISPLAY_SCAN_SPEC, &dummy);
- if (next == '<') {
- /* Ok, this is what we're looking for, a display name. */
- pj_scan_get_until_ch( scanner, '<', &name_addr->display);
- pj_strtrim(&name_addr->display);
- }
- }
-
- /* Manually skip whitespace. */
- pj_scan_skip_whitespace(scanner);
-
- /* Get the SIP-URL */
- has_bracket = (*scanner->curptr == '<');
- if (has_bracket)
- pj_scan_get_char(scanner);
- name_addr->uri = int_parse_uri( scanner, pool, PJ_TRUE );
- if (has_bracket)
- pj_scan_get_char(scanner);
-
- return name_addr;
-}
-
-
-/* Parse SIP request line. */
-static void int_parse_req_line( pj_scanner *scanner, pj_pool_t *pool,
- pjsip_request_line *req_line)
-{
- pj_str_t token;
-
- pj_scan_get( scanner, &pjsip_TOKEN_SPEC, &token);
- pjsip_method_init_np( &req_line->method, &token);
-
- req_line->uri = int_parse_uri(scanner, pool, PJ_TRUE);
- if (pj_scan_stricmp( scanner, PJSIP_VERSION, 7) != 0)
- PJ_THROW( PJSIP_SYN_ERR_EXCEPTION);
- pj_scan_advance_n (scanner, 7, 1);
- pj_scan_get_newline( scanner );
-}
-
-/* Parse status line. */
-static void int_parse_status_line( pj_scanner *scanner,
- pjsip_status_line *status_line)
-{
- pj_str_t token;
-
- if (pj_scan_stricmp(scanner, PJSIP_VERSION, 7) != 0)
- PJ_THROW(PJSIP_SYN_ERR_EXCEPTION);
- pj_scan_advance_n( scanner, 7, 1);
-
- pj_scan_get( scanner, &pjsip_DIGIT_SPEC, &token);
- status_line->code = pj_strtoul(&token);
- pj_scan_get_until( scanner, &pjsip_NEWLINE_OR_EOF_SPEC,
- &status_line->reason);
- pj_scan_get_newline( scanner );
-}
-
-/* Parse ending of header. */
-static void parse_hdr_end( pj_scanner *scanner )
-{
- if (pj_scan_is_eof(scanner)) {
- ; /* Do nothing. */
- } else if (*scanner->curptr == '&') {
- pj_scan_get_char(scanner);
- } else {
- pj_scan_get_newline(scanner);
- }
-}
-
-/* Parse ending of header. */
-void pjsip_parse_end_hdr_imp( pj_scanner *scanner )
-{
- parse_hdr_end(scanner);
-}
-
-/* Parse generic array header. */
-static void parse_generic_array_hdr( pjsip_generic_array_hdr *hdr,
- pj_scanner *scanner)
-{
- pj_scan_get_until( scanner, &pjsip_ARRAY_ELEMENTS, &hdr->values[0]);
- hdr->count++;
-
- while (*scanner->curptr == ',') {
- pj_scan_get_char(scanner);
- pj_scan_get_until( scanner, &pjsip_ARRAY_ELEMENTS,
- &hdr->values[hdr->count]);
- hdr->count++;
- }
- parse_hdr_end(scanner);
-}
-
-/* Parse generic string header. */
-static void parse_generic_string_hdr( pjsip_generic_string_hdr *hdr,
- pj_scanner *scanner )
-{
- pj_scan_get_until( scanner, &pjsip_NEWLINE_OR_EOF_SPEC, &hdr->hvalue);
- parse_hdr_end(scanner);
-}
-
-/* Parse generic integer header. */
-static void parse_generic_int_hdr( pjsip_generic_int_hdr *hdr,
- pj_scanner *scanner )
-{
- pj_str_t tmp;
- pj_scan_get_until( scanner, &pjsip_NEWLINE_OR_EOF_SPEC, &tmp);
- hdr->ivalue = pj_strtoul(&tmp);
- parse_hdr_end(scanner);
-}
-
-
-/* Parse Accept header. */
-static pjsip_hdr* parse_hdr_accept(pjsip_parse_ctx *ctx)
-{
- pjsip_accept_hdr *accept = pjsip_accept_hdr_create(ctx->pool);
- parse_generic_array_hdr(accept, ctx->scanner);
- return (pjsip_hdr*)accept;
-}
-
-/* Parse Allow header. */
-static pjsip_hdr* parse_hdr_allow(pjsip_parse_ctx *ctx)
-{
- pjsip_allow_hdr *allow = pjsip_allow_hdr_create(ctx->pool);
- parse_generic_array_hdr(allow, ctx->scanner);
- return (pjsip_hdr*)allow;
-}
-
-/* Parse Call-ID header. */
-static pjsip_hdr* parse_hdr_call_id(pjsip_parse_ctx *ctx)
-{
- pjsip_cid_hdr *hdr = pjsip_cid_hdr_create(ctx->pool);
- pj_scan_get_until( ctx->scanner, &pjsip_NEWLINE_OR_EOF_SPEC, &hdr->id);
- parse_hdr_end(ctx->scanner);
-
- if (ctx->rdata)
- ctx->rdata->msg_info.call_id = hdr->id;
-
- return (pjsip_hdr*)hdr;
-}
-
-/* Parse and interpret Contact param. */
-static void int_parse_contact_param( pjsip_contact_hdr *hdr,
- pj_scanner *scanner,
- pj_pool_t *pool)
-{
- while ( *scanner->curptr == ';' ) {
- pj_str_t pname, pvalue;
-
- int_parse_param( scanner, &pname, &pvalue);
- if (!parser_stricmp(pname, pjsip_Q_STR) && pvalue.slen) {
- char *dot_pos = memchr(pvalue.ptr, '.', pvalue.slen);
- if (!dot_pos) {
- hdr->q1000 = pj_strtoul(&pvalue);
- } else {
- pvalue.slen = (pvalue.ptr+pvalue.slen) - (dot_pos+1);
- pvalue.ptr = dot_pos + 1;
- hdr->q1000 = pj_strtoul_mindigit(&pvalue, 3);
- }
- } else if (!parser_stricmp(pname, pjsip_EXPIRES_STR) && pvalue.slen) {
- hdr->expires = pj_strtoul(&pvalue);
-
- } else {
- concat_param(&hdr->other_param, pool, &pname, &pvalue);
- }
- }
-}
-
-/* Parse Contact header. */
-static pjsip_hdr* parse_hdr_contact( pjsip_parse_ctx *ctx )
-{
- pjsip_contact_hdr *first = NULL;
- pj_scanner *scanner = ctx->scanner;
-
- do {
- pjsip_contact_hdr *hdr = pjsip_contact_hdr_create(ctx->pool);
- if (first == NULL)
- first = hdr;
- else
- pj_list_insert_before(first, hdr);
-
- if (*scanner->curptr == '*') {
- pj_scan_get_char(scanner);
- hdr->star = 1;
-
- } else {
- hdr->star = 0;
- hdr->uri = int_parse_uri_or_name_addr(scanner, ctx->pool,
- PJSIP_PARSE_URI_AS_NAMEADDR);
-
- int_parse_contact_param(hdr, scanner, ctx->pool);
- }
-
- if (*scanner->curptr != ',')
- break;
-
- pj_scan_get_char(scanner);
-
- } while (1);
-
- parse_hdr_end(scanner);
-
- return (pjsip_hdr*)first;
-}
-
-/* Parse Content-Length header. */
-static pjsip_hdr* parse_hdr_content_len( pjsip_parse_ctx *ctx )
-{
- pj_str_t digit;
- pjsip_clen_hdr *hdr;
-
- hdr = pjsip_clen_hdr_create(ctx->pool);
- pj_scan_get(ctx->scanner, &pjsip_DIGIT_SPEC, &digit);
- hdr->len = pj_strtoul(&digit);
- parse_hdr_end(ctx->scanner);
-
- if (ctx->rdata)
- ctx->rdata->msg_info.clen = hdr;
-
- return (pjsip_hdr*)hdr;
-}
-
-/* Parse Content-Type header. */
-static pjsip_hdr* parse_hdr_content_type( pjsip_parse_ctx *ctx )
-{
- pjsip_ctype_hdr *hdr;
- pj_scanner *scanner = ctx->scanner;
-
- hdr = pjsip_ctype_hdr_create(ctx->pool);
-
- /* Parse media type and subtype. */
- pj_scan_get(scanner, &pjsip_TOKEN_SPEC, &hdr->media.type);
- pj_scan_get_char(scanner);
- pj_scan_get(scanner, &pjsip_TOKEN_SPEC, &hdr->media.subtype);
-
- /* Parse media parameters */
- while (*scanner->curptr == ';') {
- pj_str_t pname, pvalue;
- int_parse_param(scanner, &pname, &pvalue);
- concat_param(&hdr->media.param, ctx->pool, &pname, &pvalue);
- }
-
- parse_hdr_end(ctx->scanner);
-
- if (ctx->rdata)
- ctx->rdata->msg_info.ctype = hdr;
-
- return (pjsip_hdr*)hdr;
-}
-
-/* Parse CSeq header. */
-static pjsip_hdr* parse_hdr_cseq( pjsip_parse_ctx *ctx )
-{
- pj_str_t cseq, method;
- pjsip_cseq_hdr *hdr;
-
- hdr = pjsip_cseq_hdr_create(ctx->pool);
- pj_scan_get( ctx->scanner, &pjsip_DIGIT_SPEC, &cseq);
- hdr->cseq = pj_strtoul(&cseq);
-
- pj_scan_get( ctx->scanner, &pjsip_TOKEN_SPEC, &method);
- pjsip_method_init_np(&hdr->method, &method);
-
- parse_hdr_end( ctx->scanner );
-
- if (ctx->rdata)
- ctx->rdata->msg_info.cseq = hdr;
-
- return (pjsip_hdr*)hdr;
-}
-
-/* Parse Expires header. */
-static pjsip_hdr* parse_hdr_expires(pjsip_parse_ctx *ctx)
-{
- pjsip_expires_hdr *hdr = pjsip_expires_hdr_create(ctx->pool);
- parse_generic_int_hdr(hdr, ctx->scanner);
- return (pjsip_hdr*)hdr;
-}
-
-/* Parse From: or To: header. */
-static void parse_hdr_fromto( pj_scanner *scanner,
- pj_pool_t *pool,
- pjsip_from_hdr *hdr)
-{
- hdr->uri = int_parse_uri_or_name_addr(scanner, pool,
- PJSIP_PARSE_URI_AS_NAMEADDR |
- PJSIP_PARSE_URI_IN_FROM_TO_HDR);
-
- while ( *scanner->curptr == ';' ) {
- pj_str_t pname, pvalue;
-
- int_parse_param( scanner, &pname, &pvalue);
-
- if (!parser_stricmp(pname, pjsip_TAG_STR)) {
- hdr->tag = pvalue;
-
- } else {
- concat_param(&hdr->other_param, pool, &pname, &pvalue);
- }
- }
-
- parse_hdr_end(scanner);
-}
-
-/* Parse From: header. */
-static pjsip_hdr* parse_hdr_from( pjsip_parse_ctx *ctx )
-{
- pjsip_from_hdr *hdr = pjsip_from_hdr_create(ctx->pool);
- parse_hdr_fromto(ctx->scanner, ctx->pool, hdr);
- if (ctx->rdata)
- ctx->rdata->msg_info.from = hdr;
-
- return (pjsip_hdr*)hdr;
-}
-
-/* Parse Require: header. */
-static pjsip_hdr* parse_hdr_require( pjsip_parse_ctx *ctx )
-{
- pjsip_require_hdr *hdr = pjsip_require_hdr_create(ctx->pool);
- parse_generic_array_hdr(hdr, ctx->scanner);
-
- if (ctx->rdata && ctx->rdata->msg_info.require == NULL)
- ctx->rdata->msg_info.require = hdr;
-
- return (pjsip_hdr*)hdr;
-}
-
-/* Parse Retry-After: header. */
-static pjsip_hdr* parse_hdr_retry_after(pjsip_parse_ctx *ctx)
-{
- pjsip_retry_after_hdr *hdr;
- hdr = pjsip_retry_after_hdr_create(ctx->pool);
- parse_generic_int_hdr(hdr, ctx->scanner);
- return (pjsip_hdr*)hdr;
-}
-
-/* Parse Supported: header. */
-static pjsip_hdr* parse_hdr_supported(pjsip_parse_ctx *ctx)
-{
- pjsip_supported_hdr *hdr = pjsip_supported_hdr_create(ctx->pool);
- parse_generic_array_hdr(hdr, ctx->scanner);
- return (pjsip_hdr*)hdr;
-}
-
-
-/* Parse To: header. */
-static pjsip_hdr* parse_hdr_to( pjsip_parse_ctx *ctx )
-{
- pjsip_to_hdr *hdr = pjsip_to_hdr_create(ctx->pool);
- parse_hdr_fromto(ctx->scanner, ctx->pool, hdr);
-
- if (ctx->rdata)
- ctx->rdata->msg_info.to = hdr;
-
- return (pjsip_hdr*)hdr;
-}
-
-/* Parse Unsupported: header. */
-static pjsip_hdr* parse_hdr_unsupported(pjsip_parse_ctx *ctx)
-{
- pjsip_unsupported_hdr *hdr = pjsip_unsupported_hdr_create(ctx->pool);
- parse_generic_array_hdr(hdr, ctx->scanner);
- return (pjsip_hdr*)hdr;
-}
-
-/* Parse and interpret Via parameters. */
-static void int_parse_via_param( pjsip_via_hdr *hdr, pj_scanner *scanner,
- pj_pool_t *pool)
-{
- while ( *scanner->curptr == ';' ) {
- pj_str_t pname, pvalue;
-
- int_parse_param( scanner, &pname, &pvalue);
-
- if (!parser_stricmp(pname, pjsip_BRANCH_STR) && pvalue.slen) {
- hdr->branch_param = pvalue;
-
- } else if (!parser_stricmp(pname, pjsip_TTL_STR) && pvalue.slen) {
- hdr->ttl_param = pj_strtoul(&pvalue);
-
- } else if (!parser_stricmp(pname, pjsip_MADDR_STR) && pvalue.slen) {
- hdr->maddr_param = pvalue;
-
- } else if (!parser_stricmp(pname, pjsip_PNAME_STR) && pvalue.slen) {
- hdr->recvd_param = pvalue;
-
- } else if (!parser_stricmp(pname, pjsip_RPORT_STR)) {
- if (pvalue.slen)
- hdr->rport_param = pj_strtoul(&pvalue);
- else
- hdr->rport_param = 0;
- } else {
- concat_param( &hdr->other_param, pool, &pname, &pvalue);
- }
- }
-
-}
-
-/* Parse Max-Forwards header. */
-static pjsip_hdr* parse_hdr_max_forwards( pjsip_parse_ctx *ctx )
-{
- pjsip_max_forwards_hdr *hdr;
- hdr = pjsip_max_forwards_hdr_create(ctx->pool);
- parse_generic_int_hdr(hdr, ctx->scanner);
-
- if (ctx->rdata)
- ctx->rdata->msg_info.max_fwd = hdr;
-
- return (pjsip_hdr*)hdr;
-}
-
-/* Parse Min-Expires header. */
-static pjsip_hdr* parse_hdr_min_expires(pjsip_parse_ctx *ctx)
-{
- pjsip_min_expires_hdr *hdr;
- hdr = pjsip_min_expires_hdr_create(ctx->pool);
- parse_generic_int_hdr(hdr, ctx->scanner);
- return (pjsip_hdr*)hdr;
-}
-
-
-/* Parse Route: or Record-Route: header. */
-static void parse_hdr_rr_route( pj_scanner *scanner, pj_pool_t *pool,
- pjsip_routing_hdr *hdr )
-{
- pjsip_name_addr *temp=int_parse_name_addr(scanner, pool);
-
- pj_memcpy(&hdr->name_addr, temp, sizeof(*temp));
- if (*scanner->curptr == ';') {
- pj_scan_get_until(scanner, &pjsip_NEWLINE_OR_EOF_SPEC,
- &hdr->other_param);
- }
-}
-
-/* Parse Record-Route header. */
-static pjsip_hdr* parse_hdr_rr( pjsip_parse_ctx *ctx)
-{
- pjsip_rr_hdr *first = NULL;
- pj_scanner *scanner = ctx->scanner;
-
- do {
- pjsip_rr_hdr *hdr = pjsip_rr_hdr_create(ctx->pool);
- if (!first) {
- first = hdr;
- } else {
- pj_list_insert_before(first, hdr);
- }
- parse_hdr_rr_route(scanner, ctx->pool, hdr);
- if (*scanner->curptr == ',') {
- pj_scan_get_char(scanner);
- } else {
- break;
- }
- } while (1);
- parse_hdr_end(scanner);
-
- if (ctx->rdata && ctx->rdata->msg_info.record_route==NULL)
- ctx->rdata->msg_info.record_route = first;
-
- return (pjsip_hdr*)first;
-}
-
-/* Parse Route: header. */
-static pjsip_hdr* parse_hdr_route( pjsip_parse_ctx *ctx )
-{
- pjsip_route_hdr *first = NULL;
- pj_scanner *scanner = ctx->scanner;
-
- do {
- pjsip_route_hdr *hdr = pjsip_route_hdr_create(ctx->pool);
- if (!first) {
- first = hdr;
- } else {
- pj_list_insert_before(first, hdr);
- }
- parse_hdr_rr_route(scanner, ctx->pool, hdr);
- if (*scanner->curptr == ',') {
- pj_scan_get_char(scanner);
- } else {
- break;
- }
- } while (1);
- parse_hdr_end(scanner);
-
- if (ctx->rdata && ctx->rdata->msg_info.route==NULL)
- ctx->rdata->msg_info.route = first;
-
- return (pjsip_hdr*)first;
-}
-
-/* Parse Via: header. */
-static pjsip_hdr* parse_hdr_via( pjsip_parse_ctx *ctx )
-{
- pjsip_via_hdr *first = NULL;
- pj_scanner *scanner = ctx->scanner;
-
- do {
- pjsip_via_hdr *hdr = pjsip_via_hdr_create(ctx->pool);
- if (!first)
- first = hdr;
- else
- pj_list_insert_before(first, hdr);
-
- if (pj_scan_stricmp( scanner, PJSIP_VERSION "/", 8) != 0)
- PJ_THROW(PJSIP_SYN_ERR_EXCEPTION);
-
- pj_scan_advance_n( scanner, 8, 1);
-
- pj_scan_get( scanner, &pjsip_TOKEN_SPEC, &hdr->transport);
- pj_scan_get( scanner, &pjsip_HOST_SPEC, &hdr->sent_by.host);
-
- if (*scanner->curptr==':') {
- pj_str_t digit;
- pj_scan_get_char(scanner);
- pj_scan_get(scanner, &pjsip_DIGIT_SPEC, &digit);
- hdr->sent_by.port = pj_strtoul(&digit);
- } else {
- hdr->sent_by.port = 5060;
- }
-
- int_parse_via_param(hdr, scanner, ctx->pool);
-
- if (*scanner->curptr == '(') {
- pj_scan_get_char(scanner);
- pj_scan_get_until_ch( scanner, ')', &hdr->comment);
- pj_scan_get_char( scanner );
- }
-
- if (*scanner->curptr != ',')
- break;
-
- pj_scan_get_char(scanner);
-
- } while (1);
-
- parse_hdr_end(scanner);
-
- if (ctx->rdata && ctx->rdata->msg_info.via == NULL)
- ctx->rdata->msg_info.via = first;
-
- return (pjsip_hdr*)first;
-}
-
-/* Parse generic header. */
-static pjsip_hdr* parse_hdr_generic_string( pjsip_parse_ctx *ctx )
-{
- pjsip_generic_string_hdr *hdr;
-
- hdr = pjsip_generic_string_hdr_create(ctx->pool, NULL);
- parse_generic_string_hdr(hdr, ctx->scanner);
- return (pjsip_hdr*)hdr;
-
-}
-
-/* Public function to parse a header value. */
-PJ_DEF(void*) pjsip_parse_hdr( pj_pool_t *pool, const pj_str_t *hname,
- char *buf, pj_size_t size, int *parsed_len )
-{
- pj_scanner scanner;
- pjsip_hdr *hdr = NULL;
- pjsip_parse_ctx context;
- PJ_USE_EXCEPTION;
-
- init_sip_parser();
-
- pj_scan_init(&scanner, buf, size, PJ_SCAN_AUTOSKIP_WS_HEADER,
- &on_syntax_error);
-
- context.scanner = &scanner;
- context.pool = pool;
- context.rdata = NULL;
-
- PJ_TRY {
- pjsip_parse_hdr_func *handler = find_handler(hname);
- if (handler) {
- hdr = (*handler)(&context);
- } else {
- hdr = parse_hdr_generic_string(&context);
- hdr->type = PJSIP_H_OTHER;
- pj_strdup(pool, &hdr->name, hname);
- hdr->sname = hdr->name;
- }
-
- }
- PJ_DEFAULT {
- hdr = NULL;
- }
- PJ_END
-
- if (parsed_len) {
- *parsed_len = (scanner.curptr - scanner.begin);
- }
-
- pj_scan_fini(&scanner);
-
- return hdr;
-}
-
+/* $Id$ */
+/*
+ * Copyright (C) 2003-2006 Benny Prijono <benny@prijono.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#include <pjsip/sip_parser.h>
+#include <pjsip/sip_uri.h>
+#include <pjsip/sip_msg.h>
+#include <pjsip/sip_auth_parser.h>
+#include <pjsip/sip_errno.h>
+#include <pjsip/sip_transport.h> /* rdata structure */
+#include <pjlib-util/scanner.h>
+#include <pjlib-util/string.h>
+#include <pj/except.h>
+#include <pj/log.h>
+#include <pj/hash.h>
+#include <pj/os.h>
+#include <pj/pool.h>
+#include <pj/string.h>
+#include <pj/ctype.h>
+#include <pj/assert.h>
+
+#define ALNUM
+#define RESERVED ";/?:@&=+$,"
+#define MARK "-_.!~*'()"
+#define UNRESERVED ALNUM MARK
+#define ESCAPED "%"
+#define USER_UNRESERVED "&=+$,;?/"
+#define PASS "&=+$,"
+#define TOKEN "-.!%*_=`'~+" /* '+' is because of app/pidf+xml
+ * in Content-Type! */
+#define HOST "_-."
+#define HEX_DIGIT "abcdefABCDEF"
+#define PARAM_CHAR "[]/:&+$" UNRESERVED ESCAPED
+#define HNV_UNRESERVED "[]/?:+$"
+#define HDR_CHAR HNV_UNRESERVED UNRESERVED ESCAPED
+
+#define PJSIP_VERSION "SIP/2.0"
+#define PJSIP_SYN_ERR_EXCEPTION 1
+
+#define UNREACHED(expr)
+
+typedef struct handler_rec
+{
+ char hname[PJSIP_MAX_HNAME_LEN+1];
+ pj_size_t hname_len;
+ pj_uint32_t hname_hash;
+ pjsip_parse_hdr_func *handler;
+} handler_rec;
+
+static handler_rec handler[127];
+static unsigned handler_count;
+static int parser_is_initialized;
+
+
+/*
+ * Global vars (also extern).
+ */
+const pj_str_t pjsip_USER_STR = { "user", 4};
+const pj_str_t pjsip_METHOD_STR = { "method", 6};
+const pj_str_t pjsip_TRANSPORT_STR = { "transport", 9};
+const pj_str_t pjsip_MADDR_STR = { "maddr", 5 };
+const pj_str_t pjsip_LR_STR = { "lr", 2 };
+const pj_str_t pjsip_SIP_STR = { "sip", 3 };
+const pj_str_t pjsip_SIPS_STR = { "sips", 4 };
+const pj_str_t pjsip_TEL_STR = { "tel", 3 };
+const pj_str_t pjsip_BRANCH_STR = { "branch", 6 };
+const pj_str_t pjsip_TTL_STR = { "ttl", 3 };
+const pj_str_t pjsip_PNAME_STR = { "received", 8 };
+const pj_str_t pjsip_Q_STR = { "q", 1 };
+const pj_str_t pjsip_EXPIRES_STR = { "expires", 7 };
+const pj_str_t pjsip_TAG_STR = { "tag", 3 };
+const pj_str_t pjsip_RPORT_STR = { "rport", 5};
+
+/* Character Input Specification buffer. */
+static pj_cis_buf_t cis_buf;
+
+/* Character Input Specifications. */
+pj_cis_t pjsip_HOST_SPEC, /* For scanning host part. */
+ pjsip_DIGIT_SPEC, /* Decimal digits */
+ pjsip_ALPHA_SPEC, /* Alpha (A-Z, a-z) */
+ pjsip_ALNUM_SPEC, /* Decimal + Alpha. */
+ pjsip_TOKEN_SPEC, /* Token. */
+ pjsip_HEX_SPEC, /* Hexadecimal digits. */
+ pjsip_PARAM_CHAR_SPEC, /* For scanning pname (or pvalue when
+ * it's not quoted.) */
+ pjsip_HDR_CHAR_SPEC, /* Chars in hname or hvalue */
+ pjsip_PROBE_USER_HOST_SPEC, /* Hostname characters. */
+ pjsip_PASSWD_SPEC, /* Password. */
+ pjsip_USER_SPEC, /* User */
+ pjsip_ARRAY_ELEMENTS, /* Array separator. */
+ pjsip_NEWLINE_OR_EOF_SPEC, /* For eating up header.*/
+ pjsip_DISPLAY_SCAN_SPEC; /* Used when searching for display name
+ * in URL. */
+
+
+/*
+ * Forward decl.
+ */
+static pjsip_msg * int_parse_msg( pjsip_parse_ctx *ctx,
+ pjsip_parser_err_report *err_list);
+static void int_parse_param( pj_scanner *scanner,
+ pj_pool_t *pool,
+ pj_str_t *pname,
+ pj_str_t *pvalue);
+static void int_parse_hparam( pj_scanner *scanner,
+ pj_pool_t *pool,
+ pj_str_t *hname,
+ pj_str_t *hvalue );
+static void int_parse_req_line( pj_scanner *scanner,
+ pj_pool_t *pool,
+ pjsip_request_line *req_line);
+static int int_is_next_user( pj_scanner *scanner);
+static void int_parse_status_line( pj_scanner *scanner,
+ pjsip_status_line *line);
+static void int_parse_user_pass( pj_scanner *scanner,
+ pj_pool_t *pool,
+ pj_str_t *user,
+ pj_str_t *pass);
+static void int_parse_uri_host_port( pj_scanner *scanner,
+ pj_str_t *p_host,
+ int *p_port);
+static pjsip_uri * int_parse_uri_or_name_addr( pj_scanner *scanner,
+ pj_pool_t *pool,
+ unsigned option);
+static pjsip_url * int_parse_sip_url( pj_scanner *scanner,
+ pj_pool_t *pool,
+ pj_bool_t parse_params);
+static pjsip_name_addr *
+ int_parse_name_addr( pj_scanner *scanner,
+ pj_pool_t *pool );
+static void parse_hdr_end( pj_scanner *scanner );
+
+static pjsip_hdr* parse_hdr_accept( pjsip_parse_ctx *ctx );
+static pjsip_hdr* parse_hdr_allow( pjsip_parse_ctx *ctx );
+static pjsip_hdr* parse_hdr_call_id( pjsip_parse_ctx *ctx);
+static pjsip_hdr* parse_hdr_contact( pjsip_parse_ctx *ctx);
+static pjsip_hdr* parse_hdr_content_len( pjsip_parse_ctx *ctx );
+static pjsip_hdr* parse_hdr_content_type( pjsip_parse_ctx *ctx );
+static pjsip_hdr* parse_hdr_cseq( pjsip_parse_ctx *ctx );
+static pjsip_hdr* parse_hdr_expires( pjsip_parse_ctx *ctx );
+static pjsip_hdr* parse_hdr_from( pjsip_parse_ctx *ctx );
+static pjsip_hdr* parse_hdr_max_forwards( pjsip_parse_ctx *ctx);
+static pjsip_hdr* parse_hdr_min_expires( pjsip_parse_ctx *ctx );
+static pjsip_hdr* parse_hdr_rr( pjsip_parse_ctx *ctx );
+static pjsip_hdr* parse_hdr_route( pjsip_parse_ctx *ctx );
+static pjsip_hdr* parse_hdr_require( pjsip_parse_ctx *ctx );
+static pjsip_hdr* parse_hdr_retry_after( pjsip_parse_ctx *ctx );
+static pjsip_hdr* parse_hdr_supported( pjsip_parse_ctx *ctx );
+static pjsip_hdr* parse_hdr_to( pjsip_parse_ctx *ctx );
+static pjsip_hdr* parse_hdr_unsupported( pjsip_parse_ctx *ctx );
+static pjsip_hdr* parse_hdr_via( pjsip_parse_ctx *ctx );
+static pjsip_hdr* parse_hdr_generic_string( pjsip_parse_ctx *ctx);
+
+/* Convert non NULL terminated string to integer. */
+static unsigned long pj_strtoul_mindigit(const pj_str_t *str,
+ unsigned mindig)
+{
+ unsigned long value;
+ unsigned i;
+
+ value = 0;
+ for (i=0; i<(unsigned)str->slen; ++i) {
+ value = value * 10 + (str->ptr[i] - '0');
+ }
+ for (; i<mindig; ++i) {
+ value = value * 10;
+ }
+ return value;
+}
+
+/* Case insensitive comparison */
+#define parser_stricmp(str1, str2) pj_stricmp(&str1, &str2)
+
+
+/* Syntax error handler for parser. */
+static void on_syntax_error(pj_scanner *scanner)
+{
+ PJ_UNUSED_ARG(scanner);
+ PJ_THROW(PJSIP_SYN_ERR_EXCEPTION);
+}
+
+/* Concatenate unrecognized params into single string. */
+void pjsip_concat_param_imp( pj_str_t *param, pj_pool_t *pool,
+ const pj_str_t *pname, const pj_str_t *pvalue,
+ int sepchar)
+{
+ char *new_param, *p;
+ int len;
+
+ len = param->slen + pname->slen + pvalue->slen + 3;
+ p = new_param = pj_pool_alloc(pool, len);
+
+ if (param->slen) {
+ int old_len = param->slen;
+ pj_memcpy(p, param->ptr, old_len);
+ p += old_len;
+ }
+ *p++ = (char)sepchar;
+ pj_memcpy(p, pname->ptr, pname->slen);
+ p += pname->slen;
+
+ if (pvalue->slen) {
+ *p++ = '=';
+ pj_memcpy(p, pvalue->ptr, pvalue->slen);
+ p += pvalue->slen;
+ }
+
+ *p = '\0';
+
+ param->ptr = new_param;
+ param->slen = p - new_param;
+}
+
+/* Concatenate unrecognized params into single string. */
+static void concat_param( pj_str_t *param, pj_pool_t *pool,
+ const pj_str_t *pname, const pj_str_t *pvalue )
+{
+ pjsip_concat_param_imp(param, pool, pname, pvalue, ';');
+}
+
+/* Initialize static properties of the parser. */
+static pj_status_t init_parser()
+{
+ static int initialized;
+ pj_status_t status;
+
+ if (initialized)
+ return PJ_SUCCESS;
+
+ initialized = 1;
+
+ pj_cis_buf_init(&cis_buf);
+
+ status = pj_cis_init(&cis_buf, &pjsip_DIGIT_SPEC);
+ PJ_ASSERT_RETURN(status == PJ_SUCCESS, status);
+ pj_cis_add_num(&pjsip_DIGIT_SPEC);
+
+ status = pj_cis_init(&cis_buf, &pjsip_ALPHA_SPEC);
+ PJ_ASSERT_RETURN(status == PJ_SUCCESS, status);
+ pj_cis_add_alpha( &pjsip_ALPHA_SPEC );
+
+ status = pj_cis_init(&cis_buf, &pjsip_ALNUM_SPEC);
+ PJ_ASSERT_RETURN(status == PJ_SUCCESS, status);
+ pj_cis_add_alpha( &pjsip_ALNUM_SPEC );
+ pj_cis_add_num( &pjsip_ALNUM_SPEC );
+
+ status = pj_cis_init(&cis_buf, &pjsip_NEWLINE_OR_EOF_SPEC);
+ PJ_ASSERT_RETURN(status == PJ_SUCCESS, status);
+ pj_cis_add_str(&pjsip_NEWLINE_OR_EOF_SPEC, "\r\n");
+ //pj_cs_set(pjsip_NEWLINE_OR_EOF_SPEC, 0);
+
+ status = pj_cis_init(&cis_buf, &pjsip_ARRAY_ELEMENTS);
+ PJ_ASSERT_RETURN(status == PJ_SUCCESS, status);
+ pj_cis_add_str( &pjsip_ARRAY_ELEMENTS, ",\r\n");
+
+ status = pj_cis_dup(&pjsip_TOKEN_SPEC, &pjsip_ALNUM_SPEC);
+ PJ_ASSERT_RETURN(status == PJ_SUCCESS, status);
+ pj_cis_add_str( &pjsip_TOKEN_SPEC, TOKEN);
+
+ status = pj_cis_dup(&pjsip_HOST_SPEC, &pjsip_ALNUM_SPEC);
+ PJ_ASSERT_RETURN(status == PJ_SUCCESS, status);
+ pj_cis_add_str( &pjsip_HOST_SPEC, HOST);
+
+ status = pj_cis_dup(&pjsip_HEX_SPEC, &pjsip_DIGIT_SPEC);
+ PJ_ASSERT_RETURN(status == PJ_SUCCESS, status);
+ pj_cis_add_str( &pjsip_HEX_SPEC, HEX_DIGIT);
+
+ status = pj_cis_dup(&pjsip_PARAM_CHAR_SPEC, &pjsip_ALNUM_SPEC);
+ PJ_ASSERT_RETURN(status == PJ_SUCCESS, status);
+ pj_cis_add_str(&pjsip_PARAM_CHAR_SPEC, PARAM_CHAR);
+
+ status = pj_cis_dup(&pjsip_HDR_CHAR_SPEC, &pjsip_ALNUM_SPEC);
+ PJ_ASSERT_RETURN(status == PJ_SUCCESS, status);
+ pj_cis_add_str(&pjsip_HDR_CHAR_SPEC, HDR_CHAR);
+
+ status = pj_cis_dup(&pjsip_USER_SPEC, &pjsip_ALNUM_SPEC);
+ PJ_ASSERT_RETURN(status == PJ_SUCCESS, status);
+ pj_cis_add_str( &pjsip_USER_SPEC, UNRESERVED ESCAPED USER_UNRESERVED );
+
+ status = pj_cis_dup(&pjsip_PASSWD_SPEC, &pjsip_ALNUM_SPEC);
+ PJ_ASSERT_RETURN(status == PJ_SUCCESS, status);
+ pj_cis_add_str( &pjsip_PASSWD_SPEC, UNRESERVED ESCAPED PASS);
+
+ status = pj_cis_init(&cis_buf, &pjsip_PROBE_USER_HOST_SPEC);
+ PJ_ASSERT_RETURN(status == PJ_SUCCESS, status);
+ pj_cis_add_str( &pjsip_PROBE_USER_HOST_SPEC, "@ \n>");
+ pj_cis_invert( &pjsip_PROBE_USER_HOST_SPEC );
+
+ status = pj_cis_init(&cis_buf, &pjsip_DISPLAY_SCAN_SPEC);
+ PJ_ASSERT_RETURN(status == PJ_SUCCESS, status);
+ pj_cis_add_str( &pjsip_DISPLAY_SCAN_SPEC, ":\r\n<");
+
+ status = pjsip_register_hdr_parser( "Accept", NULL, &parse_hdr_accept);
+ PJ_ASSERT_RETURN(status == PJ_SUCCESS, status);
+
+ status = pjsip_register_hdr_parser( "Allow", NULL, &parse_hdr_allow);
+ PJ_ASSERT_RETURN(status == PJ_SUCCESS, status);
+
+ status = pjsip_register_hdr_parser( "Call-ID", NULL, &parse_hdr_call_id);
+ PJ_ASSERT_RETURN(status == PJ_SUCCESS, status);
+
+ status = pjsip_register_hdr_parser( "Contact", "m", &parse_hdr_contact);
+ PJ_ASSERT_RETURN(status == PJ_SUCCESS, status);
+
+ status = pjsip_register_hdr_parser( "Content-Length", NULL,
+ &parse_hdr_content_len);
+ PJ_ASSERT_RETURN(status == PJ_SUCCESS, status);
+
+ status = pjsip_register_hdr_parser( "Content-Type", NULL,
+ &parse_hdr_content_type);
+ PJ_ASSERT_RETURN(status == PJ_SUCCESS, status);
+
+ status = pjsip_register_hdr_parser( "CSeq", NULL, &parse_hdr_cseq);
+ PJ_ASSERT_RETURN(status == PJ_SUCCESS, status);
+
+ status = pjsip_register_hdr_parser( "Expires", NULL, &parse_hdr_expires);
+ PJ_ASSERT_RETURN(status == PJ_SUCCESS, status);
+
+ status = pjsip_register_hdr_parser( "From", "f", &parse_hdr_from);
+ PJ_ASSERT_RETURN(status == PJ_SUCCESS, status);
+
+ status = pjsip_register_hdr_parser( "Max-Forwards", NULL,
+ &parse_hdr_max_forwards);
+ PJ_ASSERT_RETURN(status == PJ_SUCCESS, status);
+
+ status = pjsip_register_hdr_parser( "Min-Expires", NULL,
+ &parse_hdr_min_expires);
+ PJ_ASSERT_RETURN(status == PJ_SUCCESS, status);
+
+ status = pjsip_register_hdr_parser( "Record-Route", NULL, &parse_hdr_rr);
+ PJ_ASSERT_RETURN(status == PJ_SUCCESS, status);
+
+ status = pjsip_register_hdr_parser( "Route", NULL, &parse_hdr_route);
+ PJ_ASSERT_RETURN(status == PJ_SUCCESS, status);
+
+ status = pjsip_register_hdr_parser( "Require", NULL, &parse_hdr_require);
+ PJ_ASSERT_RETURN(status == PJ_SUCCESS, status);
+
+ status = pjsip_register_hdr_parser( "Retry-After", NULL,
+ &parse_hdr_retry_after);
+ PJ_ASSERT_RETURN(status == PJ_SUCCESS, status);
+
+ status = pjsip_register_hdr_parser( "Supported", "k",
+ &parse_hdr_supported);
+ PJ_ASSERT_RETURN(status == PJ_SUCCESS, status);
+
+ status = pjsip_register_hdr_parser( "To", "t", &parse_hdr_to);
+ PJ_ASSERT_RETURN(status == PJ_SUCCESS, status);
+
+ status = pjsip_register_hdr_parser( "Unsupported", NULL,
+ &parse_hdr_unsupported);
+ PJ_ASSERT_RETURN(status == PJ_SUCCESS, status);
+
+ status = pjsip_register_hdr_parser( "Via", NULL, &parse_hdr_via);
+ PJ_ASSERT_RETURN(status == PJ_SUCCESS, status);
+
+ /* Register auth parser. */
+ status = pjsip_auth_init_parser();
+
+ return status;
+}
+
+static void init_sip_parser(void)
+{
+ if (!parser_is_initialized) {
+ /* Prevent race cond. */
+ pj_enter_critical_section();
+ if (!parser_is_initialized) {
+ init_parser();
+ parser_is_initialized = 1;
+ }
+ pj_leave_critical_section();
+ }
+}
+
+/* Compare the handler record with header name, and return:
+ * - 0 if handler match.
+ * - <0 if handler is 'less' than the header name.
+ * - >0 if handler is 'greater' than header name.
+ */
+static int compare_handler( const handler_rec *r1,
+ const char *name,
+ pj_size_t name_len,
+ pj_uint32_t hash )
+{
+ /* Compare length. */
+ if (r1->hname_len < name_len)
+ return -1;
+ if (r1->hname_len > name_len)
+ return 1;
+
+ /* Length is equal, compare hashed value. */
+ if (r1->hname_hash < hash)
+ return -1;
+ if (r1->hname_hash > hash)
+ return 1;
+
+ /* Equal length and equal hash. compare the strings. */
+ return pj_native_strcmp(r1->hname, name);
+}
+
+/* Register one handler for one header name. */
+static pj_status_t int_register_parser( const char *name,
+ pjsip_parse_hdr_func *fptr )
+{
+ unsigned pos;
+ handler_rec rec;
+ unsigned i;
+
+ if (handler_count >= PJ_ARRAY_SIZE(handler)) {
+ return PJ_ETOOMANY;
+ }
+
+ /* Initialize temporary handler. */
+ rec.handler = fptr;
+ rec.hname_len = strlen(name);
+ if (rec.hname_len >= sizeof(rec.hname)) {
+ return PJ_ENAMETOOLONG;
+ }
+ /* Name is copied in lowercase. */
+ for (i=0; i<rec.hname_len; ++i) {
+ rec.hname[i] = (char)pj_tolower(name[i]);
+ }
+ rec.hname[i] = '\0';
+ /* Hash value is calculated from the lowercase name. */
+ rec.hname_hash = pj_hash_calc(0, rec.hname, PJ_HASH_KEY_STRING);
+
+ /* Get the pos to insert the new handler. */
+ for (pos=0; pos < handler_count; ++pos) {
+ int d;
+ d = compare_handler(&handler[pos], rec.hname, rec.hname_len,
+ rec.hname_hash);
+ if (d == 0) {
+ pj_assert(0);
+ return PJ_EEXISTS;
+ }
+ if (d > 0) {
+ break;
+ }
+ }
+
+ /* Shift handlers. */
+ if (pos != handler_count) {
+ pj_memmove( &handler[pos+1], &handler[pos],
+ (handler_count-pos)*sizeof(handler_rec));
+ }
+ /* Add new handler. */
+ pj_memcpy( &handler[pos], &rec, sizeof(handler_rec));
+ ++handler_count;
+
+ return PJ_SUCCESS;
+}
+
+/* Register parser handler. If both header name and short name are valid,
+ * then two instances of handler will be registered.
+ */
+PJ_DEF(pj_status_t) pjsip_register_hdr_parser( const char *hname,
+ const char *hshortname,
+ pjsip_parse_hdr_func *fptr)
+{
+ pj_status_t status;
+
+ status = int_register_parser(hname, fptr);
+ if (status != PJ_SUCCESS) {
+ return status;
+ }
+ if (hshortname) {
+ status = int_register_parser(hshortname, fptr);
+ if (status != PJ_SUCCESS)
+ return status;
+ }
+ return PJ_SUCCESS;
+}
+
+/* Find handler to parse the header name. */
+static pjsip_parse_hdr_func * find_handler(const pj_str_t *hname)
+{
+ handler_rec *first;
+ char hname_copy[PJSIP_MAX_HNAME_LEN];
+ pj_uint32_t hash;
+ int comp;
+ unsigned n;
+
+ if (hname->slen >= PJSIP_MAX_HNAME_LEN) {
+ pj_assert(!"Header name is too long!");
+ return NULL;
+ }
+
+ /* Calculate hash value while converting the header to lowercase.
+ * Don't assume that 'hname' is NULL terminated.
+ */
+ hash = pj_hash_calc_tolower(0, hname_copy, hname);
+ hname_copy[hname->slen] = '\0';
+
+ /* Binary search for the handler. */
+ comp = -1;
+ first = &handler[0];
+ n = handler_count;
+ for (; n > 0; ) {
+ unsigned half = n / 2;
+ handler_rec *mid = first + half;
+
+ comp = compare_handler(mid, hname_copy, hname->slen, hash);
+ if (comp < 0) {
+ first = ++mid;
+ n -= half + 1;
+ } else if (comp==0) {
+ first = mid;
+ break;
+ } else {
+ n = half;
+ }
+ }
+
+ return comp==0 ? first->handler : NULL;
+}
+
+/* Public function to parse SIP message. */
+PJ_DEF(pjsip_msg*) pjsip_parse_msg( pj_pool_t *pool,
+ char *buf, pj_size_t size,
+ pjsip_parser_err_report *err_list)
+{
+ pjsip_msg *msg = NULL;
+ pj_scanner scanner;
+ pjsip_parse_ctx context;
+ PJ_USE_EXCEPTION;
+
+ init_sip_parser();
+
+ pj_scan_init(&scanner, buf, size, PJ_SCAN_AUTOSKIP_WS_HEADER,
+ &on_syntax_error);
+
+ context.scanner = &scanner;
+ context.pool = pool;
+ context.rdata = NULL;
+
+ PJ_TRY {
+ msg = int_parse_msg(&context, err_list);
+ }
+ PJ_DEFAULT {
+ msg = NULL;
+ }
+ PJ_END
+
+ pj_scan_fini(&scanner);
+ return msg;
+}
+
+/* Public function to parse as rdata.*/
+PJ_DEF(pjsip_msg *) pjsip_parse_rdata( char *buf, pj_size_t size,
+ pjsip_rx_data *rdata )
+{
+ pj_scanner scanner;
+ pjsip_parse_ctx context;
+ PJ_USE_EXCEPTION;
+
+ init_sip_parser();
+
+ pj_scan_init(&scanner, buf, size, PJ_SCAN_AUTOSKIP_WS_HEADER,
+ &on_syntax_error);
+
+ context.scanner = &scanner;
+ context.pool = rdata->tp_info.pool;
+ context.rdata = rdata;
+
+ PJ_TRY {
+ rdata->msg_info.msg = int_parse_msg(&context, &rdata->msg_info.parse_err);
+ }
+ PJ_DEFAULT {
+ rdata->msg_info.msg = NULL;
+ }
+ PJ_END
+
+ pj_scan_fini(&scanner);
+ return rdata->msg_info.msg;
+}
+
+/* Determine if a message has been received. */
+PJ_DEF(pj_bool_t) pjsip_find_msg( const char *buf, pj_size_t size,
+ pj_bool_t is_datagram, pj_size_t *msg_size)
+{
+#if PJ_HAS_TCP
+ const char *hdr_end;
+ const char *body_start;
+ const char *pos;
+ const char *line;
+ int content_length = -1;
+
+ *msg_size = size;
+
+ /* For datagram, the whole datagram IS the message. */
+ if (is_datagram) {
+ return PJ_SUCCESS;
+ }
+
+
+ /* Find the end of header area by finding an empty line. */
+ if ((pos = pj_native_strstr(buf, "\n\r\n")) == NULL) {
+ return PJSIP_EPARTIALMSG;
+ }
+
+ hdr_end = pos+1;
+ body_start = pos+3;
+
+ /* Find "Content-Length" header the hard way. */
+ line = pj_native_strchr(buf, '\n');
+ while (line && line < hdr_end-14) {
+ ++line;
+ if ( ((*line=='C' || *line=='c') &&
+ pj_native_strncasecmp(line, "Content-Length", 14) == 0) ||
+ ((*line=='l' || *line=='L') &&
+ (*(line+1)==' ' || *(line+1)=='\t' || *(line+1)==':')))
+ {
+ /* Try to parse the header. */
+ pj_scanner scanner;
+ PJ_USE_EXCEPTION;
+
+ init_sip_parser();
+
+ pj_scan_init(&scanner, (char*)line, hdr_end-line,
+ PJ_SCAN_AUTOSKIP_WS_HEADER, &on_syntax_error);
+
+ PJ_TRY {
+ pj_str_t str_clen;
+
+ /* Get "Content-Length" or "L" name */
+ if (*line=='C' || *line=='c')
+ pj_scan_advance_n(&scanner, 14, PJ_TRUE);
+ else if (*line=='l' || *line=='L')
+ pj_scan_advance_n(&scanner, 1, PJ_TRUE);
+
+ /* Get colon */
+ if (pj_scan_get_char(&scanner) != ':') {
+ PJ_THROW(PJSIP_SYN_ERR_EXCEPTION);
+ }
+
+ /* Get number */
+ pj_scan_get(&scanner, &pjsip_DIGIT_SPEC, &str_clen);
+
+ /* Get newline. */
+ pj_scan_get_newline(&scanner);
+
+ /* Found a valid Content-Length header. */
+ content_length = pj_strtoul(&str_clen);
+ }
+ PJ_END
+
+ pj_scan_fini(&scanner);
+ }
+
+ /* Found valid Content-Length? */
+ if (content_length != -1)
+ break;
+
+ /* Go to next line. */
+ line = pj_native_strchr(line, '\n');
+ }
+
+ /* Found Content-Length? */
+ if (content_length == -1) {
+ return PJSIP_EMISSINGHDR;
+ }
+
+ /* Enough packet received? */
+ *msg_size = (body_start - buf) + content_length;
+ return (*msg_size) <= size ? PJ_SUCCESS : PJSIP_EPARTIALMSG;
+#else
+ PJ_UNUSED_ARG(buf);
+ PJ_UNUSED_ARG(is_datagram);
+ *msg_size = size;
+ return PJ_SUCCESS;
+#endif
+}
+
+/* Public function to parse URI */
+PJ_DEF(pjsip_uri*) pjsip_parse_uri( pj_pool_t *pool,
+ char *buf, pj_size_t size,
+ unsigned option)
+{
+ PJ_USE_EXCEPTION;
+ pj_scanner scanner;
+ pjsip_uri *uri = NULL;
+
+ init_sip_parser();
+
+ pj_scan_init(&scanner, buf, size, 0, &on_syntax_error);
+
+
+ PJ_TRY {
+ uri = int_parse_uri_or_name_addr(&scanner, pool, option);
+ }
+ PJ_END;
+
+ /* Must have exhausted all inputs. */
+ if (pj_scan_is_eof(&scanner) || *scanner.curptr=='\r' ||
+ *scanner.curptr=='\n')
+ {
+ /* Success. */
+ pj_scan_fini(&scanner);
+ return uri;
+ }
+
+ /* Still have some characters unparsed. */
+ pj_scan_fini(&scanner);
+ return NULL;
+}
+
+/* Generic function to print message body.
+ * This assumes that the 'data' member points to a contigous memory where the
+ * actual body is laid.
+ */
+static int generic_print_body (pjsip_msg_body *msg_body,
+ char *buf, pj_size_t size)
+{
+ pjsip_msg_body *body = msg_body;
+ if (size < body->len)
+ return 0;
+
+ pj_memcpy (buf, body->data, body->len);
+ return body->len;
+}
+
+/* Internal function to parse SIP message */
+static pjsip_msg *int_parse_msg( pjsip_parse_ctx *ctx,
+ pjsip_parser_err_report *err_list)
+{
+ PJ_USE_EXCEPTION;
+ int ch;
+ pjsip_msg *msg;
+ pjsip_ctype_hdr *ctype_hdr = NULL;
+ pj_scanner *scanner = ctx->scanner;
+ pj_pool_t *pool = ctx->pool;
+
+ /* Skip leading newlines. */
+ ch = *scanner->curptr;
+ while (ch=='\r' || ch=='\n') {
+ pj_scan_get_char(scanner);
+ ch = *scanner->curptr;
+ }
+
+ msg = pjsip_msg_create(pool, PJSIP_REQUEST_MSG);
+
+ /* Parse request or status line */
+ if (pj_scan_stricmp( scanner, PJSIP_VERSION, 7) == 0) {
+ msg->type = PJSIP_RESPONSE_MSG;
+ int_parse_status_line( scanner, &msg->line.status );
+ } else {
+ msg->type = PJSIP_REQUEST_MSG;
+ int_parse_req_line(scanner, pool, &msg->line.req );
+ }
+
+ /* Parse headers. */
+ do {
+ pj_str_t hname;
+ pjsip_parse_hdr_func * handler;
+ pjsip_hdr *hdr = NULL;
+
+ /* Get hname. */
+ pj_scan_get( scanner, &pjsip_TOKEN_SPEC, &hname);
+ ch = pj_scan_get_char( scanner );
+ if (ch != ':') {
+ PJ_THROW(PJSIP_SYN_ERR_EXCEPTION);
+ }
+
+ /* Find handler. */
+ handler = find_handler(&hname);
+
+ PJ_TRY {
+ /* Call the handler if found.
+ * If no handler is found, then treat the header as generic
+ * hname/hvalue pair.
+ */
+ if (handler) {
+ hdr = (*handler)(ctx);
+ } else {
+ hdr = parse_hdr_generic_string(ctx);
+ hdr->type = PJSIP_H_OTHER;
+ hdr->name = hdr->sname = hname;
+ }
+
+ /* Check if we've just parsed a Content-Type header.
+ * We will check for a message body if we've got Content-Type header.
+ */
+ if (hdr->type == PJSIP_H_CONTENT_TYPE) {
+ ctype_hdr = (pjsip_ctype_hdr*)hdr;
+ }
+
+ }
+ PJ_DEFAULT {
+ /* Exception was thrown during parsing.
+ * Skip until newline, and parse next header.
+ */
+ pj_str_t token;
+ hdr = NULL;
+
+ //PJ_LOG(4,("sipparser",
+ // "Syntax error in line %d col %d (hname=%.*s)",
+ // scanner->line, scanner->col, hname.slen, hname.ptr));
+
+ if (err_list) {
+ pjsip_parser_err_report *err_info;
+
+ err_info = pj_pool_alloc(pool, sizeof(*err_info));
+ err_info->exception_code = PJ_GET_EXCEPTION();
+ err_info->line = scanner->line;
+ err_info->col = scanner->col;
+ err_info->hname = hname;
+
+ pj_list_insert_before(err_list, err_info);
+ }
+
+ if (!pj_scan_is_eof(scanner)) {
+ pj_scan_get_until(scanner, &pjsip_NEWLINE_OR_EOF_SPEC, &token);
+ parse_hdr_end(scanner);
+ }
+ }
+ PJ_END;
+
+ if (hdr) {
+ /* Single parse of header line can produce multiple headers.
+ * For example, if one Contact: header contains Contact list
+ * separated by comma, then these Contacts will be split into
+ * different Contact headers.
+ * So here we must insert list instead of just insert one header.
+ */
+ pj_list_insert_nodes_before(&msg->hdr, hdr);
+ }
+
+ /* Parse until EOF or an empty line is found. */
+ } while (!pj_scan_is_eof(scanner) &&
+ *scanner->curptr != '\r' && *scanner->curptr != '\n');
+
+ /* If empty line is found, eat it. */
+ if (!pj_scan_is_eof(scanner)) {
+ if (*scanner->curptr=='\r' || *scanner->curptr=='\n') {
+ pj_scan_get_newline(scanner);
+ }
+ }
+
+ /* If we have Content-Type header, treat the rest of the message as body.*/
+ if (ctype_hdr) {
+ pjsip_msg_body *body = pj_pool_alloc(pool, sizeof(pjsip_msg_body));
+ pj_strdup(pool, &body->content_type.type, &ctype_hdr->media.type);
+ pj_strdup(pool, &body->content_type.subtype, &ctype_hdr->media.subtype);
+ pj_strdup(pool, &body->content_type.param, &ctype_hdr->media.param);
+ body->data = scanner->curptr;
+ body->len = scanner->end - scanner->curptr;
+ body->print_body = &generic_print_body;
+
+ msg->body = body;
+ }
+
+ return msg;
+}
+
+/* Parse parameter (pname ["=" pvalue]). */
+void pjsip_parse_param_imp( pj_scanner *scanner, pj_pool_t *pool,
+ pj_str_t *pname, pj_str_t *pvalue,
+ unsigned option)
+{
+ /* pname */
+ pj_scan_get(scanner, &pjsip_PARAM_CHAR_SPEC, pname);
+ *pname = pj_str_unescape(pool, pname);
+
+ /* pvalue, if any */
+ if (*scanner->curptr == '=') {
+ pj_scan_get_char(scanner);
+ /* pvalue can be a quoted string. */
+ if (*scanner->curptr == '"') {
+ pj_scan_get_quote( scanner, '"', '"', pvalue);
+ if (option & PJSIP_PARSE_REMOVE_QUOTE) {
+ pvalue->ptr++;
+ pvalue->slen -= 2;
+ }
+ } else {
+ pj_scan_get(scanner, &pjsip_PARAM_CHAR_SPEC, pvalue);
+ *pvalue = pj_str_unescape(pool, pvalue);
+ }
+ } else {
+ pvalue->ptr = NULL;
+ pvalue->slen = 0;
+ }
+}
+
+/* Parse parameter (";" pname ["=" pvalue]). */
+static void int_parse_param( pj_scanner *scanner, pj_pool_t *pool,
+ pj_str_t *pname, pj_str_t *pvalue)
+{
+ /* Get ';' character */
+ pj_scan_get_char(scanner);
+
+ /* Get pname and optionally pvalue */
+ pjsip_parse_param_imp(scanner, pool, pname, pvalue,
+ PJSIP_PARSE_REMOVE_QUOTE);
+}
+
+/* Parse header parameter. */
+static void int_parse_hparam( pj_scanner *scanner, pj_pool_t *pool,
+ pj_str_t *hname, pj_str_t *hvalue )
+{
+ /* Get '?' or '&' character. */
+ pj_scan_get_char(scanner);
+
+ /* hname */
+ pj_scan_get(scanner, &pjsip_HDR_CHAR_SPEC, hname);
+ *hname = pj_str_unescape(pool, hname);
+
+ /* pvalue, if any */
+ if (*scanner->curptr == '=') {
+ pj_scan_get_char(scanner);
+ pj_scan_get(scanner, &pjsip_HDR_CHAR_SPEC, hvalue);
+ *hvalue = pj_str_unescape(pool, hvalue);
+ } else {
+ hvalue->ptr = NULL;
+ hvalue->slen = 0;
+ }
+}
+
+/* Parse host:port in URI. */
+static void int_parse_uri_host_port( pj_scanner *scanner,
+ pj_str_t *host, int *p_port)
+{
+ pj_scan_get( scanner, &pjsip_HOST_SPEC, host);
+ /* RFC3261 section 19.1.2: host don't need to be unescaped */
+ if (*scanner->curptr == ':') {
+ pj_str_t port;
+ pj_scan_get_char(scanner);
+ pj_scan_get(scanner, &pjsip_DIGIT_SPEC, &port);
+ *p_port = pj_strtoul(&port);
+ } else {
+ *p_port = 0;
+ }
+}
+
+/* Determine if the next token in an URI is a user specification. */
+static int int_is_next_user(pj_scanner *scanner)
+{
+ pj_str_t dummy;
+ int is_user;
+
+ /* Find character '@'. If this character exist, then the token
+ * must be a username.
+ */
+ if (pj_scan_peek( scanner, &pjsip_PROBE_USER_HOST_SPEC, &dummy) == '@')
+ is_user = 1;
+ else
+ is_user = 0;
+
+ return is_user;
+}
+
+/* Parse user:pass tokens in an URI. */
+static void int_parse_user_pass( pj_scanner *scanner, pj_pool_t *pool,
+ pj_str_t *user, pj_str_t *pass)
+{
+ pj_scan_get( scanner, &pjsip_USER_SPEC, user);
+ *user = pj_str_unescape(pool, user);
+
+ if ( *scanner->curptr == ':') {
+ pj_scan_get_char( scanner );
+ pj_scan_get( scanner, &pjsip_PASSWD_SPEC, pass);
+ *pass = pj_str_unescape(pool, pass);
+ } else {
+ pass->ptr = NULL;
+ pass->slen = 0;
+ }
+
+ /* Get the '@' */
+ pj_scan_get_char( scanner );
+}
+
+/* Parse all types of URI. */
+static pjsip_uri *int_parse_uri_or_name_addr( pj_scanner *scanner, pj_pool_t *pool,
+ unsigned opt)
+{
+ pjsip_uri *uri;
+ int is_name_addr = 0;
+
+ /* Exhaust any whitespaces. */
+ pj_scan_skip_whitespace(scanner);
+
+ if (*scanner->curptr=='"' || *scanner->curptr=='<') {
+ uri = (pjsip_uri*)int_parse_name_addr( scanner, pool );
+ is_name_addr = 1;
+ } else {
+ pj_scan_state backtrack;
+ pj_str_t scheme;
+ int colon;
+
+ pj_scan_save_state( scanner, &backtrack);
+ pj_scan_get( scanner, &pjsip_TOKEN_SPEC, &scheme);
+ colon = pj_scan_get_char( scanner );
+ pj_scan_restore_state( scanner, &backtrack);
+
+ if (colon==':' &&
+ (parser_stricmp(scheme, pjsip_SIP_STR)==0 ||
+ parser_stricmp(scheme, pjsip_SIPS_STR)==0))
+ {
+ uri = (pjsip_uri*)
+ int_parse_sip_url( scanner, pool,
+ (opt & PJSIP_PARSE_URI_IN_FROM_TO_HDR)== 0);
+
+ } else if (colon==':' && parser_stricmp( scheme, pjsip_TEL_STR)==0) {
+
+ /* Not supported. */
+ PJ_THROW(PJSIP_SYN_ERR_EXCEPTION);
+ UNREACHED({return NULL; /* Not reached. */});
+
+ } else {
+ uri = (pjsip_uri*)int_parse_name_addr( scanner, pool );
+ is_name_addr = 1;
+ }
+ }
+
+ /* Should we return the URI object as name address? */
+ if (opt & PJSIP_PARSE_URI_AS_NAMEADDR) {
+ if (is_name_addr == 0) {
+ pjsip_name_addr *name_addr;
+
+ name_addr = pjsip_name_addr_create(pool);
+ name_addr->uri = uri;
+
+ uri = (pjsip_uri*)name_addr;
+ }
+ }
+
+ return uri;
+}
+
+/* Parse URI. */
+static pjsip_uri *int_parse_uri(pj_scanner *scanner, pj_pool_t *pool,
+ pj_bool_t parse_params)
+{
+ if (*scanner->curptr=='"' || *scanner->curptr=='<') {
+ return (pjsip_uri*)int_parse_name_addr( scanner, pool );
+ } else {
+ pj_str_t scheme;
+ int colon;
+
+ /* Get scheme. */
+ colon = pj_scan_peek(scanner, &pjsip_TOKEN_SPEC, &scheme);
+ if (colon != ':') {
+ PJ_THROW(PJSIP_SYN_ERR_EXCEPTION);
+ }
+
+ if ((parser_stricmp(scheme, pjsip_SIP_STR)==0 ||
+ parser_stricmp(scheme, pjsip_SIPS_STR)==0))
+ {
+ return (pjsip_uri*)int_parse_sip_url( scanner, pool, parse_params);
+
+ } else if (parser_stricmp(scheme, pjsip_TEL_STR)==0) {
+ PJ_THROW(PJSIP_SYN_ERR_EXCEPTION);
+ UNREACHED({ return NULL; /* Not reached. */ })
+
+ } else {
+ PJ_THROW(PJSIP_SYN_ERR_EXCEPTION);
+ UNREACHED({ return NULL; /* Not reached. */ })
+ }
+ }
+}
+
+/* Parse "sip:" and "sips:" URI. */
+static pjsip_url *int_parse_sip_url( pj_scanner *scanner,
+ pj_pool_t *pool,
+ pj_bool_t parse_params)
+{
+ pj_str_t scheme;
+ pjsip_url *url;
+ int colon;
+ int skip_ws = scanner->skip_ws;
+ int hsep = '?';
+ scanner->skip_ws = 0;
+
+ pj_scan_get(scanner, &pjsip_TOKEN_SPEC, &scheme);
+ colon = pj_scan_get_char(scanner);
+ if (colon != ':') {
+ PJ_THROW(PJSIP_SYN_ERR_EXCEPTION);
+ }
+
+ if (parser_stricmp(scheme, pjsip_SIP_STR)==0) {
+ url = pjsip_url_create(pool, 0);
+
+ } else if (parser_stricmp(scheme, pjsip_SIPS_STR)==0) {
+ url = pjsip_url_create(pool, 1);
+
+ } else {
+ PJ_THROW(PJSIP_SYN_ERR_EXCEPTION);
+ /* should not reach here */
+ UNREACHED({
+ pj_assert(0);
+ return 0;
+ })
+ }
+
+ if (int_is_next_user(scanner)) {
+ int_parse_user_pass(scanner, pool, &url->user, &url->passwd);
+ }
+
+ /* Get host:port */
+ int_parse_uri_host_port(scanner, &url->host, &url->port);
+
+ /* Get URL parameters. */
+ while ( parse_params && *scanner->curptr == ';' ) {
+ pj_str_t pname, pvalue;
+
+ int_parse_param( scanner, pool, &pname, &pvalue);
+
+ if (!parser_stricmp(pname, pjsip_USER_STR) && pvalue.slen) {
+ url->user_param = pvalue;
+
+ } else if (!parser_stricmp(pname, pjsip_METHOD_STR) && pvalue.slen) {
+ url->method_param = pvalue;
+
+ } else if (!parser_stricmp(pname,pjsip_TRANSPORT_STR) && pvalue.slen) {
+ url->transport_param = pvalue;
+
+ } else if (!parser_stricmp(pname, pjsip_TTL_STR) && pvalue.slen) {
+ url->ttl_param = pj_strtoul(&pvalue);
+
+ } else if (!parser_stricmp(pname, pjsip_MADDR_STR) && pvalue.slen) {
+ url->maddr_param = pvalue;
+
+ } else if (!parser_stricmp(pname, pjsip_LR_STR)) {
+ url->lr_param = 1;
+
+ } else {
+ pjsip_param *p = pj_pool_alloc(pool, sizeof(pjsip_param));
+ p->name = pname;
+ p->value = pvalue;
+ pj_list_insert_before(&url->other_param, p);
+ }
+ }
+
+ /* Get header params. */
+ while (parse_params && *scanner->curptr == hsep) {
+ pjsip_param *param;
+ param = pj_pool_alloc(pool, sizeof(pjsip_param));
+ int_parse_hparam(scanner, pool, &param->name, &param->value);
+ pj_list_insert_before(&url->header_param, param);
+ hsep = '&';
+ }
+
+ scanner->skip_ws = skip_ws;
+ pj_scan_skip_whitespace(scanner);
+ return url;
+}
+
+/* Parse nameaddr. */
+static pjsip_name_addr *int_parse_name_addr( pj_scanner *scanner,
+ pj_pool_t *pool )
+{
+ int has_bracket;
+ pjsip_name_addr *name_addr;
+
+ name_addr = pjsip_name_addr_create(pool);
+
+ if (*scanner->curptr == '"') {
+ pj_scan_get_quote( scanner, '"', '"', &name_addr->display);
+ /* Trim the leading and ending quote */
+ name_addr->display.ptr++;
+ name_addr->display.slen -= 2;
+
+ } else if (*scanner->curptr != '<') {
+ int next;
+ pj_str_t dummy;
+
+ /* This can be either the start of display name,
+ * the start of URL ("sip:", "sips:", "tel:", etc.), or '<' char.
+ * We're only interested in display name, because SIP URL
+ * will be parser later.
+ */
+ next = pj_scan_peek_until(scanner, &pjsip_DISPLAY_SCAN_SPEC, &dummy);
+ if (next == '<') {
+ /* Ok, this is what we're looking for, a display name. */
+ pj_scan_get_until_ch( scanner, '<', &name_addr->display);
+ pj_strtrim(&name_addr->display);
+ }
+ }
+
+ /* Manually skip whitespace. */
+ pj_scan_skip_whitespace(scanner);
+
+ /* Get the SIP-URL */
+ has_bracket = (*scanner->curptr == '<');
+ if (has_bracket)
+ pj_scan_get_char(scanner);
+ name_addr->uri = int_parse_uri( scanner, pool, PJ_TRUE );
+ if (has_bracket)
+ pj_scan_get_char(scanner);
+
+ return name_addr;
+}
+
+
+/* Parse SIP request line. */
+static void int_parse_req_line( pj_scanner *scanner, pj_pool_t *pool,
+ pjsip_request_line *req_line)
+{
+ pj_str_t token;
+
+ pj_scan_get( scanner, &pjsip_TOKEN_SPEC, &token);
+ pjsip_method_init_np( &req_line->method, &token);
+
+ req_line->uri = int_parse_uri(scanner, pool, PJ_TRUE);
+ if (pj_scan_stricmp( scanner, PJSIP_VERSION, 7) != 0)
+ PJ_THROW( PJSIP_SYN_ERR_EXCEPTION);
+ pj_scan_advance_n (scanner, 7, 1);
+ pj_scan_get_newline( scanner );
+}
+
+/* Parse status line. */
+static void int_parse_status_line( pj_scanner *scanner,
+ pjsip_status_line *status_line)
+{
+ pj_str_t token;
+
+ if (pj_scan_stricmp(scanner, PJSIP_VERSION, 7) != 0)
+ PJ_THROW(PJSIP_SYN_ERR_EXCEPTION);
+ pj_scan_advance_n( scanner, 7, 1);
+
+ pj_scan_get( scanner, &pjsip_DIGIT_SPEC, &token);
+ status_line->code = pj_strtoul(&token);
+ pj_scan_get_until( scanner, &pjsip_NEWLINE_OR_EOF_SPEC,
+ &status_line->reason);
+ pj_scan_get_newline( scanner );
+}
+
+/* Parse ending of header. */
+static void parse_hdr_end( pj_scanner *scanner )
+{
+ if (pj_scan_is_eof(scanner)) {
+ ; /* Do nothing. */
+ } else if (*scanner->curptr == '&') {
+ pj_scan_get_char(scanner);
+ } else {
+ pj_scan_get_newline(scanner);
+ }
+}
+
+/* Parse ending of header. */
+void pjsip_parse_end_hdr_imp( pj_scanner *scanner )
+{
+ parse_hdr_end(scanner);
+}
+
+/* Parse generic array header. */
+static void parse_generic_array_hdr( pjsip_generic_array_hdr *hdr,
+ pj_scanner *scanner)
+{
+ pj_scan_get_until( scanner, &pjsip_ARRAY_ELEMENTS, &hdr->values[0]);
+ hdr->count++;
+
+ while (*scanner->curptr == ',') {
+ pj_scan_get_char(scanner);
+ pj_scan_get_until( scanner, &pjsip_ARRAY_ELEMENTS,
+ &hdr->values[hdr->count]);
+ hdr->count++;
+ }
+ parse_hdr_end(scanner);
+}
+
+/* Parse generic string header. */
+static void parse_generic_string_hdr( pjsip_generic_string_hdr *hdr,
+ pj_scanner *scanner )
+{
+ pj_scan_get_until( scanner, &pjsip_NEWLINE_OR_EOF_SPEC, &hdr->hvalue);
+ parse_hdr_end(scanner);
+}
+
+/* Parse generic integer header. */
+static void parse_generic_int_hdr( pjsip_generic_int_hdr *hdr,
+ pj_scanner *scanner )
+{
+ pj_str_t tmp;
+ pj_scan_get_until( scanner, &pjsip_NEWLINE_OR_EOF_SPEC, &tmp);
+ hdr->ivalue = pj_strtoul(&tmp);
+ parse_hdr_end(scanner);
+}
+
+
+/* Parse Accept header. */
+static pjsip_hdr* parse_hdr_accept(pjsip_parse_ctx *ctx)
+{
+ pjsip_accept_hdr *accept = pjsip_accept_hdr_create(ctx->pool);
+ parse_generic_array_hdr(accept, ctx->scanner);
+ return (pjsip_hdr*)accept;
+}
+
+/* Parse Allow header. */
+static pjsip_hdr* parse_hdr_allow(pjsip_parse_ctx *ctx)
+{
+ pjsip_allow_hdr *allow = pjsip_allow_hdr_create(ctx->pool);
+ parse_generic_array_hdr(allow, ctx->scanner);
+ return (pjsip_hdr*)allow;
+}
+
+/* Parse Call-ID header. */
+static pjsip_hdr* parse_hdr_call_id(pjsip_parse_ctx *ctx)
+{
+ pjsip_cid_hdr *hdr = pjsip_cid_hdr_create(ctx->pool);
+ pj_scan_get_until( ctx->scanner, &pjsip_NEWLINE_OR_EOF_SPEC, &hdr->id);
+ parse_hdr_end(ctx->scanner);
+
+ if (ctx->rdata)
+ ctx->rdata->msg_info.call_id = hdr->id;
+
+ return (pjsip_hdr*)hdr;
+}
+
+/* Parse and interpret Contact param. */
+static void int_parse_contact_param( pjsip_contact_hdr *hdr,
+ pj_scanner *scanner,
+ pj_pool_t *pool)
+{
+ while ( *scanner->curptr == ';' ) {
+ pj_str_t pname, pvalue;
+
+ int_parse_param( scanner, pool, &pname, &pvalue);
+ if (!parser_stricmp(pname, pjsip_Q_STR) && pvalue.slen) {
+ char *dot_pos = memchr(pvalue.ptr, '.', pvalue.slen);
+ if (!dot_pos) {
+ hdr->q1000 = pj_strtoul(&pvalue);
+ } else {
+ pvalue.slen = (pvalue.ptr+pvalue.slen) - (dot_pos+1);
+ pvalue.ptr = dot_pos + 1;
+ hdr->q1000 = pj_strtoul_mindigit(&pvalue, 3);
+ }
+ } else if (!parser_stricmp(pname, pjsip_EXPIRES_STR) && pvalue.slen) {
+ hdr->expires = pj_strtoul(&pvalue);
+
+ } else {
+ pjsip_param *p = pj_pool_alloc(pool, sizeof(pjsip_param));
+ p->name = pname;
+ p->value = pvalue;
+ pj_list_insert_before(&hdr->other_param, p);
+ }
+ }
+}
+
+/* Parse Contact header. */
+static pjsip_hdr* parse_hdr_contact( pjsip_parse_ctx *ctx )
+{
+ pjsip_contact_hdr *first = NULL;
+ pj_scanner *scanner = ctx->scanner;
+
+ do {
+ pjsip_contact_hdr *hdr = pjsip_contact_hdr_create(ctx->pool);
+ if (first == NULL)
+ first = hdr;
+ else
+ pj_list_insert_before(first, hdr);
+
+ if (*scanner->curptr == '*') {
+ pj_scan_get_char(scanner);
+ hdr->star = 1;
+
+ } else {
+ hdr->star = 0;
+ hdr->uri = int_parse_uri_or_name_addr(scanner, ctx->pool,
+ PJSIP_PARSE_URI_AS_NAMEADDR);
+
+ int_parse_contact_param(hdr, scanner, ctx->pool);
+ }
+
+ if (*scanner->curptr != ',')
+ break;
+
+ pj_scan_get_char(scanner);
+
+ } while (1);
+
+ parse_hdr_end(scanner);
+
+ return (pjsip_hdr*)first;
+}
+
+/* Parse Content-Length header. */
+static pjsip_hdr* parse_hdr_content_len( pjsip_parse_ctx *ctx )
+{
+ pj_str_t digit;
+ pjsip_clen_hdr *hdr;
+
+ hdr = pjsip_clen_hdr_create(ctx->pool);
+ pj_scan_get(ctx->scanner, &pjsip_DIGIT_SPEC, &digit);
+ hdr->len = pj_strtoul(&digit);
+ parse_hdr_end(ctx->scanner);
+
+ if (ctx->rdata)
+ ctx->rdata->msg_info.clen = hdr;
+
+ return (pjsip_hdr*)hdr;
+}
+
+/* Parse Content-Type header. */
+static pjsip_hdr* parse_hdr_content_type( pjsip_parse_ctx *ctx )
+{
+ pjsip_ctype_hdr *hdr;
+ pj_scanner *scanner = ctx->scanner;
+
+ hdr = pjsip_ctype_hdr_create(ctx->pool);
+
+ /* Parse media type and subtype. */
+ pj_scan_get(scanner, &pjsip_TOKEN_SPEC, &hdr->media.type);
+ pj_scan_get_char(scanner);
+ pj_scan_get(scanner, &pjsip_TOKEN_SPEC, &hdr->media.subtype);
+
+ /* Parse media parameters */
+ while (*scanner->curptr == ';') {
+ pj_str_t pname, pvalue;
+ int_parse_param(scanner, ctx->pool, &pname, &pvalue);
+ concat_param(&hdr->media.param, ctx->pool, &pname, &pvalue);
+ }
+
+ parse_hdr_end(ctx->scanner);
+
+ if (ctx->rdata)
+ ctx->rdata->msg_info.ctype = hdr;
+
+ return (pjsip_hdr*)hdr;
+}
+
+/* Parse CSeq header. */
+static pjsip_hdr* parse_hdr_cseq( pjsip_parse_ctx *ctx )
+{
+ pj_str_t cseq, method;
+ pjsip_cseq_hdr *hdr;
+
+ hdr = pjsip_cseq_hdr_create(ctx->pool);
+ pj_scan_get( ctx->scanner, &pjsip_DIGIT_SPEC, &cseq);
+ hdr->cseq = pj_strtoul(&cseq);
+
+ pj_scan_get( ctx->scanner, &pjsip_TOKEN_SPEC, &method);
+ pjsip_method_init_np(&hdr->method, &method);
+
+ parse_hdr_end( ctx->scanner );
+
+ if (ctx->rdata)
+ ctx->rdata->msg_info.cseq = hdr;
+
+ return (pjsip_hdr*)hdr;
+}
+
+/* Parse Expires header. */
+static pjsip_hdr* parse_hdr_expires(pjsip_parse_ctx *ctx)
+{
+ pjsip_expires_hdr *hdr = pjsip_expires_hdr_create(ctx->pool);
+ parse_generic_int_hdr(hdr, ctx->scanner);
+ return (pjsip_hdr*)hdr;
+}
+
+/* Parse From: or To: header. */
+static void parse_hdr_fromto( pj_scanner *scanner,
+ pj_pool_t *pool,
+ pjsip_from_hdr *hdr)
+{
+ hdr->uri = int_parse_uri_or_name_addr(scanner, pool,
+ PJSIP_PARSE_URI_AS_NAMEADDR |
+ PJSIP_PARSE_URI_IN_FROM_TO_HDR);
+
+ while ( *scanner->curptr == ';' ) {
+ pj_str_t pname, pvalue;
+
+ int_parse_param( scanner, pool, &pname, &pvalue);
+
+ if (!parser_stricmp(pname, pjsip_TAG_STR)) {
+ hdr->tag = pvalue;
+
+ } else {
+ pjsip_param *p = pj_pool_alloc(pool, sizeof(pjsip_param));
+ p->name = pname;
+ p->value = pvalue;
+ pj_list_insert_before(&hdr->other_param, p);
+ }
+ }
+
+ parse_hdr_end(scanner);
+}
+
+/* Parse From: header. */
+static pjsip_hdr* parse_hdr_from( pjsip_parse_ctx *ctx )
+{
+ pjsip_from_hdr *hdr = pjsip_from_hdr_create(ctx->pool);
+ parse_hdr_fromto(ctx->scanner, ctx->pool, hdr);
+ if (ctx->rdata)
+ ctx->rdata->msg_info.from = hdr;
+
+ return (pjsip_hdr*)hdr;
+}
+
+/* Parse Require: header. */
+static pjsip_hdr* parse_hdr_require( pjsip_parse_ctx *ctx )
+{
+ pjsip_require_hdr *hdr = pjsip_require_hdr_create(ctx->pool);
+ parse_generic_array_hdr(hdr, ctx->scanner);
+
+ if (ctx->rdata && ctx->rdata->msg_info.require == NULL)
+ ctx->rdata->msg_info.require = hdr;
+
+ return (pjsip_hdr*)hdr;
+}
+
+/* Parse Retry-After: header. */
+static pjsip_hdr* parse_hdr_retry_after(pjsip_parse_ctx *ctx)
+{
+ pjsip_retry_after_hdr *hdr;
+ hdr = pjsip_retry_after_hdr_create(ctx->pool);
+ parse_generic_int_hdr(hdr, ctx->scanner);
+ return (pjsip_hdr*)hdr;
+}
+
+/* Parse Supported: header. */
+static pjsip_hdr* parse_hdr_supported(pjsip_parse_ctx *ctx)
+{
+ pjsip_supported_hdr *hdr = pjsip_supported_hdr_create(ctx->pool);
+ parse_generic_array_hdr(hdr, ctx->scanner);
+ return (pjsip_hdr*)hdr;
+}
+
+
+/* Parse To: header. */
+static pjsip_hdr* parse_hdr_to( pjsip_parse_ctx *ctx )
+{
+ pjsip_to_hdr *hdr = pjsip_to_hdr_create(ctx->pool);
+ parse_hdr_fromto(ctx->scanner, ctx->pool, hdr);
+
+ if (ctx->rdata)
+ ctx->rdata->msg_info.to = hdr;
+
+ return (pjsip_hdr*)hdr;
+}
+
+/* Parse Unsupported: header. */
+static pjsip_hdr* parse_hdr_unsupported(pjsip_parse_ctx *ctx)
+{
+ pjsip_unsupported_hdr *hdr = pjsip_unsupported_hdr_create(ctx->pool);
+ parse_generic_array_hdr(hdr, ctx->scanner);
+ return (pjsip_hdr*)hdr;
+}
+
+/* Parse and interpret Via parameters. */
+static void int_parse_via_param( pjsip_via_hdr *hdr, pj_scanner *scanner,
+ pj_pool_t *pool)
+{
+ while ( *scanner->curptr == ';' ) {
+ pj_str_t pname, pvalue;
+
+ int_parse_param( scanner, pool, &pname, &pvalue);
+
+ if (!parser_stricmp(pname, pjsip_BRANCH_STR) && pvalue.slen) {
+ hdr->branch_param = pvalue;
+
+ } else if (!parser_stricmp(pname, pjsip_TTL_STR) && pvalue.slen) {
+ hdr->ttl_param = pj_strtoul(&pvalue);
+
+ } else if (!parser_stricmp(pname, pjsip_MADDR_STR) && pvalue.slen) {
+ hdr->maddr_param = pvalue;
+
+ } else if (!parser_stricmp(pname, pjsip_PNAME_STR) && pvalue.slen) {
+ hdr->recvd_param = pvalue;
+
+ } else if (!parser_stricmp(pname, pjsip_RPORT_STR)) {
+ if (pvalue.slen)
+ hdr->rport_param = pj_strtoul(&pvalue);
+ else
+ hdr->rport_param = 0;
+ } else {
+ pjsip_param *p = pj_pool_alloc(pool, sizeof(pjsip_param));
+ p->name = pname;
+ p->value = pvalue;
+ pj_list_insert_before(&hdr->other_param, p);
+ }
+ }
+}
+
+/* Parse Max-Forwards header. */
+static pjsip_hdr* parse_hdr_max_forwards( pjsip_parse_ctx *ctx )
+{
+ pjsip_max_forwards_hdr *hdr;
+ hdr = pjsip_max_forwards_hdr_create(ctx->pool);
+ parse_generic_int_hdr(hdr, ctx->scanner);
+
+ if (ctx->rdata)
+ ctx->rdata->msg_info.max_fwd = hdr;
+
+ return (pjsip_hdr*)hdr;
+}
+
+/* Parse Min-Expires header. */
+static pjsip_hdr* parse_hdr_min_expires(pjsip_parse_ctx *ctx)
+{
+ pjsip_min_expires_hdr *hdr;
+ hdr = pjsip_min_expires_hdr_create(ctx->pool);
+ parse_generic_int_hdr(hdr, ctx->scanner);
+ return (pjsip_hdr*)hdr;
+}
+
+
+/* Parse Route: or Record-Route: header. */
+static void parse_hdr_rr_route( pj_scanner *scanner, pj_pool_t *pool,
+ pjsip_routing_hdr *hdr )
+{
+ pjsip_name_addr *temp=int_parse_name_addr(scanner, pool);
+
+ pj_memcpy(&hdr->name_addr, temp, sizeof(*temp));
+
+ while (*scanner->curptr == ';') {
+ pjsip_param *p = pj_pool_alloc(pool, sizeof(pjsip_param));
+ int_parse_param(scanner, pool, &p->name, &p->value);
+ pj_list_insert_before(&hdr->other_param, p);
+ }
+}
+
+/* Parse Record-Route header. */
+static pjsip_hdr* parse_hdr_rr( pjsip_parse_ctx *ctx)
+{
+ pjsip_rr_hdr *first = NULL;
+ pj_scanner *scanner = ctx->scanner;
+
+ do {
+ pjsip_rr_hdr *hdr = pjsip_rr_hdr_create(ctx->pool);
+ if (!first) {
+ first = hdr;
+ } else {
+ pj_list_insert_before(first, hdr);
+ }
+ parse_hdr_rr_route(scanner, ctx->pool, hdr);
+ if (*scanner->curptr == ',') {
+ pj_scan_get_char(scanner);
+ } else {
+ break;
+ }
+ } while (1);
+ parse_hdr_end(scanner);
+
+ if (ctx->rdata && ctx->rdata->msg_info.record_route==NULL)
+ ctx->rdata->msg_info.record_route = first;
+
+ return (pjsip_hdr*)first;
+}
+
+/* Parse Route: header. */
+static pjsip_hdr* parse_hdr_route( pjsip_parse_ctx *ctx )
+{
+ pjsip_route_hdr *first = NULL;
+ pj_scanner *scanner = ctx->scanner;
+
+ do {
+ pjsip_route_hdr *hdr = pjsip_route_hdr_create(ctx->pool);
+ if (!first) {
+ first = hdr;
+ } else {
+ pj_list_insert_before(first, hdr);
+ }
+ parse_hdr_rr_route(scanner, ctx->pool, hdr);
+ if (*scanner->curptr == ',') {
+ pj_scan_get_char(scanner);
+ } else {
+ break;
+ }
+ } while (1);
+ parse_hdr_end(scanner);
+
+ if (ctx->rdata && ctx->rdata->msg_info.route==NULL)
+ ctx->rdata->msg_info.route = first;
+
+ return (pjsip_hdr*)first;
+}
+
+/* Parse Via: header. */
+static pjsip_hdr* parse_hdr_via( pjsip_parse_ctx *ctx )
+{
+ pjsip_via_hdr *first = NULL;
+ pj_scanner *scanner = ctx->scanner;
+
+ do {
+ pjsip_via_hdr *hdr = pjsip_via_hdr_create(ctx->pool);
+ if (!first)
+ first = hdr;
+ else
+ pj_list_insert_before(first, hdr);
+
+ if (pj_scan_stricmp( scanner, PJSIP_VERSION "/", 8) != 0)
+ PJ_THROW(PJSIP_SYN_ERR_EXCEPTION);
+
+ pj_scan_advance_n( scanner, 8, 1);
+
+ pj_scan_get( scanner, &pjsip_TOKEN_SPEC, &hdr->transport);
+ pj_scan_get( scanner, &pjsip_HOST_SPEC, &hdr->sent_by.host);
+
+ if (*scanner->curptr==':') {
+ pj_str_t digit;
+ pj_scan_get_char(scanner);
+ pj_scan_get(scanner, &pjsip_DIGIT_SPEC, &digit);
+ hdr->sent_by.port = pj_strtoul(&digit);
+ } else {
+ hdr->sent_by.port = 5060;
+ }
+
+ int_parse_via_param(hdr, scanner, ctx->pool);
+
+ if (*scanner->curptr == '(') {
+ pj_scan_get_char(scanner);
+ pj_scan_get_until_ch( scanner, ')', &hdr->comment);
+ pj_scan_get_char( scanner );
+ }
+
+ if (*scanner->curptr != ',')
+ break;
+
+ pj_scan_get_char(scanner);
+
+ } while (1);
+
+ parse_hdr_end(scanner);
+
+ if (ctx->rdata && ctx->rdata->msg_info.via == NULL)
+ ctx->rdata->msg_info.via = first;
+
+ return (pjsip_hdr*)first;
+}
+
+/* Parse generic header. */
+static pjsip_hdr* parse_hdr_generic_string( pjsip_parse_ctx *ctx )
+{
+ pjsip_generic_string_hdr *hdr;
+
+ hdr = pjsip_generic_string_hdr_create(ctx->pool, NULL);
+ parse_generic_string_hdr(hdr, ctx->scanner);
+ return (pjsip_hdr*)hdr;
+
+}
+
+/* Public function to parse a header value. */
+PJ_DEF(void*) pjsip_parse_hdr( pj_pool_t *pool, const pj_str_t *hname,
+ char *buf, pj_size_t size, int *parsed_len )
+{
+ pj_scanner scanner;
+ pjsip_hdr *hdr = NULL;
+ pjsip_parse_ctx context;
+ PJ_USE_EXCEPTION;
+
+ init_sip_parser();
+
+ pj_scan_init(&scanner, buf, size, PJ_SCAN_AUTOSKIP_WS_HEADER,
+ &on_syntax_error);
+
+ context.scanner = &scanner;
+ context.pool = pool;
+ context.rdata = NULL;
+
+ PJ_TRY {
+ pjsip_parse_hdr_func *handler = find_handler(hname);
+ if (handler) {
+ hdr = (*handler)(&context);
+ } else {
+ hdr = parse_hdr_generic_string(&context);
+ hdr->type = PJSIP_H_OTHER;
+ pj_strdup(pool, &hdr->name, hname);
+ hdr->sname = hdr->name;
+ }
+
+ }
+ PJ_DEFAULT {
+ hdr = NULL;
+ }
+ PJ_END
+
+ if (parsed_len) {
+ *parsed_len = (scanner.curptr - scanner.begin);
+ }
+
+ pj_scan_fini(&scanner);
+
+ return hdr;
+}
+
diff --git a/pjsip/src/pjsip/sip_resolve.c b/pjsip/src/pjsip/sip_resolve.c
index 2008fa97..b89b7c01 100644
--- a/pjsip/src/pjsip/sip_resolve.c
+++ b/pjsip/src/pjsip/sip_resolve.c
@@ -1,125 +1,125 @@
-/* $Id$ */
-/*
- * Copyright (C) 2003-2006 Benny Prijono <benny@prijono.org>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
-#include <pjsip/sip_resolve.h>
-#include <pjsip/sip_transport.h>
-#include <pj/pool.h>
-#include <pj/ctype.h>
-#include <pj/assert.h>
-
-struct pjsip_resolver_t
-{
- void *dummy;
-};
-
-PJ_DEF(pjsip_resolver_t*) pjsip_resolver_create(pj_pool_t *pool)
-{
- pjsip_resolver_t *resolver;
- resolver = (pjsip_resolver_t*) pj_pool_calloc(pool, 1, sizeof(*resolver));
- return resolver;
-}
-
-PJ_DEF(void) pjsip_resolver_destroy(pjsip_resolver_t *resolver)
-{
- PJ_UNUSED_ARG(resolver);
-}
-
-static int is_str_ip(const pj_str_t *host)
-{
- const char *p = host->ptr;
- const char *end = ((const char*)host->ptr) + host->slen;
-
- while (p != end) {
- if (pj_isdigit(*p) || *p=='.') {
- ++p;
- } else {
- return 0;
- }
- }
- return 1;
-}
-
-PJ_DEF(void) pjsip_resolve( pjsip_resolver_t *resolver,
- pj_pool_t *pool,
- pjsip_host_port *target,
- void *token,
- pjsip_resolver_callback *cb)
-{
- struct pjsip_server_addresses svr_addr;
- pj_status_t status;
- int is_ip_addr;
- pjsip_transport_type_e type = target->type;
-
- PJ_UNUSED_ARG(resolver);
- PJ_UNUSED_ARG(pool);
-
- /* We only do synchronous resolving at this moment. */
- PJ_TODO(SUPPORT_RFC3263_SERVER_RESOLUTION)
-
- /* Is it IP address or hostname?. */
- is_ip_addr = is_str_ip(&target->host);
-
- /* Set the transport type if not explicitly specified.
- * RFC 3263 section 4.1 specify rules to set up this.
- */
- if (type == PJSIP_TRANSPORT_UNSPECIFIED) {
- if (is_ip_addr || (target->port != 0)) {
-#if PJ_HAS_TCP
- if (target->flag & PJSIP_TRANSPORT_SECURE)
- {
- type = PJSIP_TRANSPORT_TLS;
- } else if (target->flag & PJSIP_TRANSPORT_RELIABLE)
- {
- type = PJSIP_TRANSPORT_TCP;
- } else
-#endif
- {
- type = PJSIP_TRANSPORT_UDP;
- }
- } else {
- /* No type or explicit port is specified, and the address is
- * not IP address.
- * In this case, full resolution must be performed.
- * But we don't support it (yet).
- */
- type = PJSIP_TRANSPORT_UDP;
- }
-
- }
-
- /* Set the port number if not specified. */
- if (target->port == 0) {
- target->port = pjsip_transport_get_default_port_for_type(type);
- }
-
- /* Resolve hostname. */
- if (!is_ip_addr) {
- status = pj_sockaddr_in_init(&svr_addr.entry[0].addr, &target->host,
- (pj_uint16_t)target->port);
- } else {
- status = pj_sockaddr_in_init(&svr_addr.entry[0].addr, &target->host,
- (pj_uint16_t)target->port);
- pj_assert(status == PJ_SUCCESS);
- }
-
- /* Call the callback. */
- svr_addr.count = (status == PJ_SUCCESS) ? 1 : 0;
- svr_addr.entry[0].type = type;
- (*cb)(status, token, &svr_addr);
-}
-
+/* $Id$ */
+/*
+ * Copyright (C) 2003-2006 Benny Prijono <benny@prijono.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#include <pjsip/sip_resolve.h>
+#include <pjsip/sip_transport.h>
+#include <pj/pool.h>
+#include <pj/ctype.h>
+#include <pj/assert.h>
+
+struct pjsip_resolver_t
+{
+ void *dummy;
+};
+
+PJ_DEF(pjsip_resolver_t*) pjsip_resolver_create(pj_pool_t *pool)
+{
+ pjsip_resolver_t *resolver;
+ resolver = (pjsip_resolver_t*) pj_pool_calloc(pool, 1, sizeof(*resolver));
+ return resolver;
+}
+
+PJ_DEF(void) pjsip_resolver_destroy(pjsip_resolver_t *resolver)
+{
+ PJ_UNUSED_ARG(resolver);
+}
+
+static int is_str_ip(const pj_str_t *host)
+{
+ const char *p = host->ptr;
+ const char *end = ((const char*)host->ptr) + host->slen;
+
+ while (p != end) {
+ if (pj_isdigit(*p) || *p=='.') {
+ ++p;
+ } else {
+ return 0;
+ }
+ }
+ return 1;
+}
+
+PJ_DEF(void) pjsip_resolve( pjsip_resolver_t *resolver,
+ pj_pool_t *pool,
+ pjsip_host_port *target,
+ void *token,
+ pjsip_resolver_callback *cb)
+{
+ struct pjsip_server_addresses svr_addr;
+ pj_status_t status;
+ int is_ip_addr;
+ pjsip_transport_type_e type = target->type;
+
+ PJ_UNUSED_ARG(resolver);
+ PJ_UNUSED_ARG(pool);
+
+ /* We only do synchronous resolving at this moment. */
+ PJ_TODO(SUPPORT_RFC3263_SERVER_RESOLUTION)
+
+ /* Is it IP address or hostname?. */
+ is_ip_addr = is_str_ip(&target->host);
+
+ /* Set the transport type if not explicitly specified.
+ * RFC 3263 section 4.1 specify rules to set up this.
+ */
+ if (type == PJSIP_TRANSPORT_UNSPECIFIED) {
+ if (is_ip_addr || (target->port != 0)) {
+#if PJ_HAS_TCP
+ if (target->flag & PJSIP_TRANSPORT_SECURE)
+ {
+ type = PJSIP_TRANSPORT_TLS;
+ } else if (target->flag & PJSIP_TRANSPORT_RELIABLE)
+ {
+ type = PJSIP_TRANSPORT_TCP;
+ } else
+#endif
+ {
+ type = PJSIP_TRANSPORT_UDP;
+ }
+ } else {
+ /* No type or explicit port is specified, and the address is
+ * not IP address.
+ * In this case, full resolution must be performed.
+ * But we don't support it (yet).
+ */
+ type = PJSIP_TRANSPORT_UDP;
+ }
+
+ }
+
+ /* Set the port number if not specified. */
+ if (target->port == 0) {
+ target->port = pjsip_transport_get_default_port_for_type(type);
+ }
+
+ /* Resolve hostname. */
+ if (!is_ip_addr) {
+ status = pj_sockaddr_in_init(&svr_addr.entry[0].addr, &target->host,
+ (pj_uint16_t)target->port);
+ } else {
+ status = pj_sockaddr_in_init(&svr_addr.entry[0].addr, &target->host,
+ (pj_uint16_t)target->port);
+ pj_assert(status == PJ_SUCCESS);
+ }
+
+ /* Call the callback. */
+ svr_addr.count = (status == PJ_SUCCESS) ? 1 : 0;
+ svr_addr.entry[0].type = type;
+ (*cb)(status, token, &svr_addr);
+}
+
diff --git a/pjsip/src/pjsip/sip_transaction.c b/pjsip/src/pjsip/sip_transaction.c
index bc4f21ff..eade0a4b 100644
--- a/pjsip/src/pjsip/sip_transaction.c
+++ b/pjsip/src/pjsip/sip_transaction.c
@@ -1,1995 +1,1995 @@
-/* $Id$ */
-/*
- * Copyright (C) 2003-2006 Benny Prijono <benny@prijono.org>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
-#include <pjsip/sip_transaction.h>
-#include <pjsip/sip_transport.h>
-#include <pjsip/sip_config.h>
-#include <pjsip/sip_util.h>
-#include <pjsip/sip_event.h>
-#include <pjsip/sip_endpoint.h>
-#include <pjsip/sip_errno.h>
-#include <pj/log.h>
-#include <pj/string.h>
-#include <pj/os.h>
-#include <pj/guid.h>
-#include <pj/pool.h>
-#include <pj/assert.h>
-
-/* Thread Local Storage ID for transaction lock (initialized by endpoint) */
-long pjsip_tsx_lock_tls_id;
-
-/* State names */
-static const char *state_str[] =
-{
- "Null",
- "Calling",
- "Trying",
- "Proceeding",
- "Completed",
- "Confirmed",
- "Terminated",
- "Destroyed",
-};
-
-/* Role names */
-static const char *role_name[] =
-{
- "Client",
- "Server"
-};
-
-/* Transaction lock. */
-typedef struct tsx_lock_data {
- struct tsx_lock_data *prev;
- pjsip_transaction *tsx;
- int is_alive;
-} tsx_lock_data;
-
-
-/* Timer timeout value constants */
-static const pj_time_val t1_timer_val = { PJSIP_T1_TIMEOUT/1000,
- PJSIP_T1_TIMEOUT%1000 };
-static const pj_time_val t4_timer_val = { PJSIP_T4_TIMEOUT/1000,
- PJSIP_T4_TIMEOUT%1000 };
-static const pj_time_val td_timer_val = { PJSIP_TD_TIMEOUT/1000,
- PJSIP_TD_TIMEOUT%1000 };
-static const pj_time_val timeout_timer_val = { (64*PJSIP_T1_TIMEOUT)/1000,
- (64*PJSIP_T1_TIMEOUT)%1000 };
-
-/* Internal timer IDs */
-enum Transaction_Timer_Id
-{
- TSX_TIMER_RETRANSMISSION,
- TSX_TIMER_TIMEOUT,
-};
-
-/* Function Prototypes */
-static pj_status_t pjsip_tsx_on_state_null( pjsip_transaction *tsx,
- pjsip_event *event);
-static pj_status_t pjsip_tsx_on_state_calling( pjsip_transaction *tsx,
- pjsip_event *event);
-static pj_status_t pjsip_tsx_on_state_trying( pjsip_transaction *tsx,
- pjsip_event *event);
-static pj_status_t pjsip_tsx_on_state_proceeding_uas( pjsip_transaction *tsx,
- pjsip_event *event);
-static pj_status_t pjsip_tsx_on_state_proceeding_uac( pjsip_transaction *tsx,
- pjsip_event *event);
-static pj_status_t pjsip_tsx_on_state_completed_uas( pjsip_transaction *tsx,
- pjsip_event *event);
-static pj_status_t pjsip_tsx_on_state_completed_uac( pjsip_transaction *tsx,
- pjsip_event *event);
-static pj_status_t pjsip_tsx_on_state_confirmed(pjsip_transaction *tsx,
- pjsip_event *event);
-static pj_status_t pjsip_tsx_on_state_terminated(pjsip_transaction *tsx,
- pjsip_event *event);
-static pj_status_t pjsip_tsx_on_state_destroyed(pjsip_transaction *tsx,
- pjsip_event *event);
-
-static void tsx_timer_callback( pj_timer_heap_t *theap,
- pj_timer_entry *entry);
-static int tsx_send_msg( pjsip_transaction *tsx,
- pjsip_tx_data *tdata);
-static void lock_tsx( pjsip_transaction *tsx, struct
- tsx_lock_data *lck );
-static pj_status_t unlock_tsx( pjsip_transaction *tsx,
- struct tsx_lock_data *lck );
-
-/* State handlers for UAC, indexed by state */
-static int (*tsx_state_handler_uac[PJSIP_TSX_STATE_MAX])(pjsip_transaction *,
- pjsip_event *) =
-{
- &pjsip_tsx_on_state_null,
- &pjsip_tsx_on_state_calling,
- &pjsip_tsx_on_state_trying,
- &pjsip_tsx_on_state_proceeding_uac,
- &pjsip_tsx_on_state_completed_uac,
- &pjsip_tsx_on_state_confirmed,
- &pjsip_tsx_on_state_terminated,
- &pjsip_tsx_on_state_destroyed,
-};
-
-/* State handlers for UAS */
-static int (*tsx_state_handler_uas[PJSIP_TSX_STATE_MAX])(pjsip_transaction *,
- pjsip_event *) =
-{
- &pjsip_tsx_on_state_null,
- &pjsip_tsx_on_state_calling,
- &pjsip_tsx_on_state_trying,
- &pjsip_tsx_on_state_proceeding_uas,
- &pjsip_tsx_on_state_completed_uas,
- &pjsip_tsx_on_state_confirmed,
- &pjsip_tsx_on_state_terminated,
- &pjsip_tsx_on_state_destroyed,
-};
-
-/*
- * Get transaction state name.
- */
-PJ_DEF(const char *) pjsip_tsx_state_str(pjsip_tsx_state_e state)
-{
- return state_str[state];
-}
-
-/*
- * Get the role name.
- */
-PJ_DEF(const char *) pjsip_role_name(pjsip_role_e role)
-{
- return role_name[role];
-}
-
-
-/*
- * Create transaction key for RFC2543 compliant messages, which don't have
- * unique branch parameter in the top most Via header.
- *
- * INVITE requests matches a transaction if the following attributes
- * match the original request:
- * - Request-URI
- * - To tag
- * - From tag
- * - Call-ID
- * - CSeq
- * - top Via header
- *
- * CANCEL matching is done similarly as INVITE, except:
- * - CSeq method will differ
- * - To tag is not matched.
- *
- * ACK matching is done similarly, except that:
- * - method of the CSeq will differ,
- * - To tag is matched to the response sent by the server transaction.
- *
- * The transaction key is constructed from the common components of above
- * components. Additional comparison is needed to fully match a transaction.
- */
-static pj_status_t create_tsx_key_2543( pj_pool_t *pool,
- pj_str_t *str,
- pjsip_role_e role,
- const pjsip_method *method,
- const pjsip_rx_data *rdata )
-{
-#define SEPARATOR '$'
- char *key, *p, *end;
- int len;
- pj_size_t len_required;
- pjsip_uri *req_uri;
- pj_str_t *host;
-
- PJ_ASSERT_RETURN(pool && str && method && rdata, PJ_EINVAL);
- PJ_ASSERT_RETURN(rdata->msg_info.msg, PJ_EINVAL);
- PJ_ASSERT_RETURN(rdata->msg_info.via, PJSIP_EMISSINGHDR);
- PJ_ASSERT_RETURN(rdata->msg_info.cseq, PJSIP_EMISSINGHDR);
- PJ_ASSERT_RETURN(rdata->msg_info.from, PJSIP_EMISSINGHDR);
-
- host = &rdata->msg_info.via->sent_by.host;
- req_uri = (pjsip_uri*)rdata->msg_info.msg->line.req.uri;
-
- /* Calculate length required. */
- len_required = 9 + /* CSeq number */
- rdata->msg_info.from->tag.slen + /* From tag. */
- rdata->msg_info.call_id.slen + /* Call-ID */
- host->slen + /* Via host. */
- 9 + /* Via port. */
- 16; /* Separator+Allowance. */
- key = p = pj_pool_alloc(pool, len_required);
- end = p + len_required;
-
- /* Add role. */
- *p++ = (char)(role==PJSIP_ROLE_UAC ? 'c' : 's');
- *p++ = SEPARATOR;
-
- /* Add Request-URI */
- /* This is BUG!
- * Response doesn't have Request-URI!
- *
- len = req_uri->vptr->print( PJSIP_URI_IN_REQ_URI, req_uri, p, end-p );
- p += len;
- *p++ = SEPARATOR;
- */
-
- /* Add method, except when method is INVITE or ACK. */
- if (method->id != PJSIP_INVITE_METHOD && method->id != PJSIP_ACK_METHOD) {
- pj_memcpy(p, method->name.ptr, method->name.slen);
- p += method->name.slen;
- *p++ = '$';
- }
-
- /* Add CSeq (only the number). */
- len = pj_utoa(rdata->msg_info.cseq->cseq, p);
- p += len;
- *p++ = SEPARATOR;
-
- /* Add From tag. */
- len = rdata->msg_info.from->tag.slen;
- pj_memcpy( p, rdata->msg_info.from->tag.ptr, len);
- p += len;
- *p++ = SEPARATOR;
-
- /* Add Call-ID. */
- len = rdata->msg_info.call_id.slen;
- pj_memcpy( p, rdata->msg_info.call_id.ptr, len );
- p += len;
- *p++ = SEPARATOR;
-
- /* Add top Via header.
- * We don't really care whether the port contains the real port (because
- * it can be omited if default port is used). Anyway this function is
- * only used to match request retransmission, and we expect that the
- * request retransmissions will contain the same port.
- */
- pj_memcpy(p, host->ptr, host->slen);
- p += host->slen;
- *p++ = ':';
-
- len = pj_utoa(rdata->msg_info.via->sent_by.port, p);
- p += len;
- *p++ = SEPARATOR;
-
- *p++ = '\0';
-
- /* Done. */
- str->ptr = key;
- str->slen = p-key;
-
- return PJ_SUCCESS;
-}
-
-/*
- * Create transaction key for RFC3161 compliant system.
- */
-static pj_status_t create_tsx_key_3261( pj_pool_t *pool,
- pj_str_t *key,
- pjsip_role_e role,
- const pjsip_method *method,
- const pj_str_t *branch)
-{
- char *p;
-
- PJ_ASSERT_RETURN(pool && key && method && branch, PJ_EINVAL);
-
- p = key->ptr = pj_pool_alloc(pool, branch->slen + method->name.slen + 4 );
-
- /* Add role. */
- *p++ = (char)(role==PJSIP_ROLE_UAC ? 'c' : 's');
- *p++ = SEPARATOR;
-
- /* Add method, except when method is INVITE or ACK. */
- if (method->id != PJSIP_INVITE_METHOD && method->id != PJSIP_ACK_METHOD) {
- pj_memcpy(p, method->name.ptr, method->name.slen);
- p += method->name.slen;
- *p++ = '$';
- }
-
- /* Add branch ID. */
- pj_memcpy(p, branch->ptr, branch->slen);
- p += branch->slen;
-
- /* Set length */
- key->slen = p - key->ptr;
-
- return PJ_SUCCESS;
-}
-
-/*
- * Create key from the incoming data, to be used to search the transaction
- * in the transaction hash table.
- */
-PJ_DEF(pj_status_t) pjsip_tsx_create_key( pj_pool_t *pool, pj_str_t *key,
- pjsip_role_e role,
- const pjsip_method *method,
- const pjsip_rx_data *rdata)
-{
- pj_str_t rfc3261_branch = {PJSIP_RFC3261_BRANCH_ID,
- PJSIP_RFC3261_BRANCH_LEN};
-
-
- /* Get the branch parameter in the top-most Via.
- * If branch parameter is started with "z9hG4bK", then the message was
- * generated by agent compliant with RFC3261. Otherwise, it will be
- * handled as RFC2543.
- */
- const pj_str_t *branch = &rdata->msg_info.via->branch_param;
-
- if (pj_strncmp(branch,&rfc3261_branch,PJSIP_RFC3261_BRANCH_LEN)==0) {
-
- /* Create transaction key. */
- return create_tsx_key_3261(pool, key, role, method, branch);
-
- } else {
- /* Create the key for the message. This key will be matched up
- * with the transaction key. For RFC2563 transactions, the
- * transaction key was created by the same function, so it will
- * match the message.
- */
- return create_tsx_key_2543( pool, key, role, method, rdata );
- }
-}
-
-
-/*
- * Create new transaction.
- */
-PJ_DEF(pj_status_t) pjsip_tsx_create( pj_pool_t *pool,
- pjsip_endpoint *endpt,
- pjsip_transaction **p_tsx)
-{
- pjsip_transaction *tsx;
- pj_status_t status;
-
- tsx = pj_pool_calloc(pool, 1, sizeof(pjsip_transaction));
-
- tsx->pool = pool;
- tsx->endpt = endpt;
- tsx->retransmit_timer.id = TSX_TIMER_RETRANSMISSION;
- tsx->retransmit_timer._timer_id = -1;
- tsx->retransmit_timer.user_data = tsx;
- tsx->retransmit_timer.cb = &tsx_timer_callback;
- tsx->timeout_timer.id = TSX_TIMER_TIMEOUT;
- tsx->timeout_timer._timer_id = -1;
- tsx->timeout_timer.user_data = tsx;
- tsx->timeout_timer.cb = &tsx_timer_callback;
- pj_sprintf(tsx->obj_name, "tsx%p", tsx);
- status = pj_mutex_create_recursive(pool, "mtsx%p", &tsx->mutex);
- if (status != PJ_SUCCESS) {
- return status;
- }
-
- *p_tsx = tsx;
- return PJ_SUCCESS;
-}
-
-/*
- * Lock transaction and set the value of Thread Local Storage.
- */
-static void lock_tsx(pjsip_transaction *tsx, struct tsx_lock_data *lck)
-{
- struct tsx_lock_data *prev_data;
-
- pj_mutex_lock(tsx->mutex);
- prev_data = (struct tsx_lock_data *)
- pj_thread_local_get(pjsip_tsx_lock_tls_id);
- lck->prev = prev_data;
- lck->tsx = tsx;
- lck->is_alive = 1;
- pj_thread_local_set(pjsip_tsx_lock_tls_id, lck);
-}
-
-
-/*
- * Unlock transaction.
- * This will selectively unlock the mutex ONLY IF the transaction has not been
- * destroyed. The function knows whether the transaction has been destroyed
- * because when transaction is destroyed the is_alive flag for the transaction
- * will be set to zero.
- */
-static pj_status_t unlock_tsx( pjsip_transaction *tsx,
- struct tsx_lock_data *lck)
-{
- pj_assert( (void*)pj_thread_local_get(pjsip_tsx_lock_tls_id) == lck);
- pj_assert( lck->tsx == tsx );
- pj_thread_local_set(pjsip_tsx_lock_tls_id, lck->prev);
- if (lck->is_alive)
- pj_mutex_unlock(tsx->mutex);
-
- return lck->is_alive ? PJ_SUCCESS : PJSIP_ETSXDESTROYED;
-}
-
-/*
- * Set transaction state, and inform TU about the transaction state change.
- */
-static void tsx_set_state( pjsip_transaction *tsx,
- pjsip_tsx_state_e state,
- pjsip_event_id_e event_src_type,
- void *event_src )
-{
- pjsip_event e;
-
- PJ_LOG(4, (tsx->obj_name, "STATE %s-->%s, cause = %s",
- state_str[tsx->state], state_str[state],
- pjsip_event_str(event_src_type)));
-
- /* Change state. */
- tsx->state = state;
-
- /* Update the state handlers. */
- if (tsx->role == PJSIP_ROLE_UAC) {
- tsx->state_handler = tsx_state_handler_uac[state];
- } else {
- tsx->state_handler = tsx_state_handler_uas[state];
- }
-
- /* Inform TU */
- PJSIP_EVENT_INIT_TSX_STATE(e, tsx, event_src_type, event_src);
- pjsip_endpt_send_tsx_event( tsx->endpt, &e );
-
- /* When the transaction is terminated, release transport, and free the
- * saved last transmitted message.
- */
- if (state == PJSIP_TSX_STATE_TERMINATED) {
-
- /* Decrement transport reference counter. */
- if (tsx->transport &&
- tsx->transport_state == PJSIP_TSX_TRANSPORT_STATE_FINAL)
- {
- pjsip_transport_dec_ref( tsx->transport );
- tsx->transport = NULL;
- }
- /* Free last transmitted message. */
- if (tsx->last_tx) {
- pjsip_tx_data_dec_ref( tsx->last_tx );
- tsx->last_tx = NULL;
- }
- /* Cancel timeout timer. */
- if (tsx->timeout_timer._timer_id != -1) {
- pjsip_endpt_cancel_timer(tsx->endpt, &tsx->timeout_timer);
- tsx->timeout_timer._timer_id = -1;
- }
- /* Cancel retransmission timer. */
- if (tsx->retransmit_timer._timer_id != -1) {
- pjsip_endpt_cancel_timer(tsx->endpt, &tsx->retransmit_timer);
- tsx->retransmit_timer._timer_id = -1;
- }
-
- /* If transport is not pending, reschedule timeout timer to
- * destroy this transaction.
- */
- if (tsx->transport_state == PJSIP_TSX_TRANSPORT_STATE_FINAL) {
- pj_time_val timeout = {0, 0};
- pjsip_endpt_schedule_timer( tsx->endpt, &tsx->timeout_timer,
- &timeout);
- }
-
- } else if (state == PJSIP_TSX_STATE_DESTROYED) {
-
- /* Clear TLS, so that mutex will not be unlocked */
- struct tsx_lock_data *lck = pj_thread_local_get(pjsip_tsx_lock_tls_id);
- while (lck) {
- if (lck->tsx == tsx) {
- lck->is_alive = 0;
- }
- lck = lck->prev;
- }
- }
-}
-
-/*
- * Look-up destination address and select which transport to be used to send
- * the request message. The procedure used here follows the guidelines on
- * sending the request in RFC3261 chapter 8.1.2.
- *
- * This function also modifies the message (request line and Route headers)
- * accordingly.
- */
-static pj_status_t tsx_process_route( pjsip_transaction *tsx,
- pjsip_tx_data *tdata,
- pjsip_host_port *send_addr )
-{
- const pjsip_uri *new_request_uri, *target_uri;
- const pjsip_name_addr *topmost_route_uri;
- pjsip_route_hdr *first_route_hdr, *last_route_hdr;
-
- pj_assert(tdata->msg->type == PJSIP_REQUEST_MSG);
-
- /* Get the first "Route" header from the message. If the message doesn't
- * have any "Route" headers but the endpoint has, then copy the "Route"
- * headers from the endpoint first.
- */
- last_route_hdr = first_route_hdr =
- pjsip_msg_find_hdr(tdata->msg, PJSIP_H_ROUTE, NULL);
- if (first_route_hdr) {
- topmost_route_uri = &first_route_hdr->name_addr;
- while (last_route_hdr->next != (void*)&tdata->msg->hdr) {
- pjsip_route_hdr *hdr;
- hdr = pjsip_msg_find_hdr(tdata->msg, PJSIP_H_ROUTE,
- last_route_hdr->next);
- if (!hdr)
- break;
- last_route_hdr = hdr;
- }
- } else {
- const pjsip_route_hdr *hdr_list;
- hdr_list = (pjsip_route_hdr*)pjsip_endpt_get_routing(tsx->endpt);
- if (hdr_list->next != hdr_list) {
- const pjsip_route_hdr *hdr = (pjsip_route_hdr*)hdr_list->next;
- first_route_hdr = NULL;
- topmost_route_uri = &hdr->name_addr;
- do {
- last_route_hdr = pjsip_hdr_shallow_clone(tdata->pool, hdr);
- if (first_route_hdr == NULL)
- first_route_hdr = last_route_hdr;
- pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr*)last_route_hdr);
- hdr = hdr->next;
- } while (hdr != hdr_list);
- } else {
- topmost_route_uri = NULL;
- }
- }
-
- /* If Route headers exist, and the first element indicates loose-route,
- * the URI is taken from the Request-URI, and we keep all existing Route
- * headers intact.
- * If Route headers exist, and the first element DOESN'T indicate loose
- * route, the URI is taken from the first Route header, and remove the
- * first Route header from the message.
- * Otherwise if there's no Route headers, the URI is taken from the
- * Request-URI.
- */
- if (topmost_route_uri) {
- pj_bool_t has_lr_param;
-
- if (PJSIP_URI_SCHEME_IS_SIP(topmost_route_uri) ||
- PJSIP_URI_SCHEME_IS_SIPS(topmost_route_uri))
- {
- const pjsip_url *url = pjsip_uri_get_uri((void*)topmost_route_uri);
- has_lr_param = url->lr_param;
- } else {
- has_lr_param = 0;
- }
-
- if (has_lr_param) {
- new_request_uri = tdata->msg->line.req.uri;
- /* We shouldn't need to delete topmost Route if it has lr param.
- * But seems like it breaks some proxy implementation, so we
- * delete it anyway.
- */
- /*
- pj_list_erase(first_route_hdr);
- if (first_route_hdr == last_route_hdr)
- last_route_hdr = NULL;
- */
- } else {
- new_request_uri = pjsip_uri_get_uri((void*)topmost_route_uri);
- pj_list_erase(first_route_hdr);
- if (first_route_hdr == last_route_hdr)
- last_route_hdr = NULL;
- }
-
- target_uri = (pjsip_uri*)topmost_route_uri;
-
- } else {
- target_uri = new_request_uri = tdata->msg->line.req.uri;
- }
-
- /* The target URI must be a SIP/SIPS URL so we can resolve it's address.
- * Otherwise we're in trouble (i.e. there's no host part in tel: URL).
- */
- pj_memset(send_addr, 0, sizeof(*send_addr));
-
- if (PJSIP_URI_SCHEME_IS_SIPS(target_uri)) {
- pjsip_uri *uri = (pjsip_uri*) target_uri;
- const pjsip_url *url = (const pjsip_url*)pjsip_uri_get_uri(uri);
- send_addr->flag |= (PJSIP_TRANSPORT_SECURE | PJSIP_TRANSPORT_RELIABLE);
- pj_strdup(tdata->pool, &send_addr->host, &url->host);
- send_addr->port = url->port;
- send_addr->type =
- pjsip_transport_get_type_from_name(&url->transport_param);
-
- } else if (PJSIP_URI_SCHEME_IS_SIP(target_uri)) {
- pjsip_uri *uri = (pjsip_uri*) target_uri;
- const pjsip_url *url = (const pjsip_url*)pjsip_uri_get_uri(uri);
- pj_strdup(tdata->pool, &send_addr->host, &url->host);
- send_addr->port = url->port;
- send_addr->type =
- pjsip_transport_get_type_from_name(&url->transport_param);
-#if PJ_HAS_TCP
- if (send_addr->type == PJSIP_TRANSPORT_TCP ||
- send_addr->type == PJSIP_TRANSPORT_SCTP)
- {
- send_addr->flag |= PJSIP_TRANSPORT_RELIABLE;
- }
-#endif
- } else {
- pj_assert(!"Unsupported URI scheme!");
- return PJSIP_EINVALIDSCHEME;
- }
-
- /* If target URI is different than request URI, replace
- * request URI add put the original URI in the last Route header.
- */
- if (new_request_uri && new_request_uri!=tdata->msg->line.req.uri) {
- pjsip_route_hdr *route = pjsip_route_hdr_create(tdata->pool);
- route->name_addr.uri = tdata->msg->line.req.uri;
- if (last_route_hdr)
- pj_list_insert_after(last_route_hdr, route);
- else
- pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr*)route);
- tdata->msg->line.req.uri = (pjsip_uri*)new_request_uri;
- }
-
- /* Success. */
- return PJ_SUCCESS;
-}
-
-
-/*
- * Callback from the transport job.
- * This callback is called when asychronous transport connect() operation
- * has completed, with or without error.
- */
-static void tsx_transport_callback(pjsip_transport *tr,
- void *token,
- pj_status_t status)
-{
- char addr[PJ_MAX_HOSTNAME];
- pjsip_transaction *tsx = token;
- struct tsx_lock_data lck;
-
- pj_memcpy(addr, tsx->dest_name.host.ptr, tsx->dest_name.host.slen);
- addr[tsx->dest_name.host.slen] = '\0';
-
-
- if (status == PJ_SUCCESS) {
- PJ_LOG(4, (tsx->obj_name, "%s connected to %s:%d",
- tr->type_name,
- addr, tsx->dest_name.port));
- } else {
- PJ_LOG(4, (tsx->obj_name, "%s unable to connect to %s:%d, status=%d",
- tr->type_name,
- addr, tsx->dest_name.port, status));
- }
-
- /* Lock transaction. */
- lock_tsx(tsx, &lck);
-
- if (status != PJ_SUCCESS) {
- tsx->transport_state = PJSIP_TSX_TRANSPORT_STATE_FINAL;
- tsx->status_code = PJSIP_SC_TSX_TRANSPORT_ERROR;
-
- tsx_set_state(tsx, PJSIP_TSX_STATE_TERMINATED,
- PJSIP_EVENT_TRANSPORT_ERROR, (void*)status);
-
- /* Unlock transaction. */
- unlock_tsx(tsx, &lck);
- return;
- }
-
- /* See if transaction has already been terminated.
- * If so, schedule to destroy the transaction.
- */
- if (tsx->state == PJSIP_TSX_STATE_TERMINATED) {
- pj_time_val timeout = {0, 0};
- pjsip_endpt_schedule_timer( tsx->endpt, &tsx->timeout_timer,
- &timeout);
-
- /* Unlock transaction. */
- unlock_tsx(tsx, &lck);
- return;
- }
-
- /* Add reference counter to the transport. */
- pjsip_transport_add_ref(tr);
-
- /* Mark transport as ready. */
- tsx->transport_state = PJSIP_TSX_TRANSPORT_STATE_FINAL;
- tsx->transport = tr;
-
- /* If there's a pending message to send, send it now. */
- if (tsx->has_unsent_msg) {
- tsx_send_msg( tsx, tsx->last_tx );
- }
-
- /* Unlock transaction. */
- unlock_tsx(tsx, &lck);
-}
-
-/*
- * Callback from the resolver job.
- */
-static void tsx_resolver_callback(pj_status_t status,
- void *token,
- const struct pjsip_server_addresses *addr)
-{
- pjsip_transaction *tsx = token;
- struct tsx_lock_data lck;
- pjsip_transport *tp;
-
- PJ_LOG(4, (tsx->obj_name, "resolver job complete, status=%d", status));
-
- if (status != PJ_SUCCESS || addr->count == 0) {
- lock_tsx(tsx, &lck);
- tsx->status_code = PJSIP_SC_TSX_RESOLVE_ERROR;
- tsx_set_state(tsx, PJSIP_TSX_STATE_TERMINATED,
- PJSIP_EVENT_TRANSPORT_ERROR, (void*)status);
- unlock_tsx(tsx, &lck);
- return;
- }
-
- /* Lock transaction. */
- lock_tsx(tsx, &lck);
-
- /* Copy server addresses. */
- pj_memcpy(&tsx->remote_addr, addr, sizeof(*addr));
-
- /* Create/find the transport for the remote address. */
- PJ_LOG(5,(tsx->obj_name, "tsx getting transport for %s:%d",
- pj_inet_ntoa(addr->entry[0].addr.sin_addr),
- pj_ntohs(addr->entry[0].addr.sin_port)));
-
- tsx->transport_state = PJSIP_TSX_TRANSPORT_STATE_CONNECTING;
- status = pjsip_endpt_alloc_transport( tsx->endpt, addr->entry[0].type,
- &addr->entry[0].addr,
- &tp);
- tsx_transport_callback(tp, tsx, status);
-
- /* Unlock transaction */
- unlock_tsx(tsx, &lck);
-
- /* There should be nothing to do after this point.
- * Execution for the transaction will resume when the callback for the
- * transport is called.
- */
-}
-
-/*
- * Initialize the transaction as UAC transaction.
- */
-PJ_DEF(pj_status_t) pjsip_tsx_init_uac( pjsip_transaction *tsx,
- pjsip_tx_data *tdata)
-{
- pjsip_msg *msg;
- pjsip_cseq_hdr *cseq;
- pjsip_via_hdr *via;
- pj_status_t status;
- struct tsx_lock_data lck;
-
- PJ_LOG(4,(tsx->obj_name, "initializing tsx as UAC (tdata=%p)", tdata));
-
- /* Lock transaction. */
- lock_tsx(tsx, &lck);
-
- /* Keep shortcut */
- msg = tdata->msg;
-
- /* Role is UAC. */
- tsx->role = PJSIP_ROLE_UAC;
-
- /* Save method. */
- pjsip_method_copy( tsx->pool, &tsx->method, &msg->line.req.method);
-
- /* Generate Via header if it doesn't exist. */
- via = pjsip_msg_find_hdr(msg, PJSIP_H_VIA, NULL);
- if (via == NULL) {
- via = pjsip_via_hdr_create(tdata->pool);
- pjsip_msg_insert_first_hdr(msg, (pjsip_hdr*) via);
- }
-
- if (via->branch_param.slen == 0) {
- pj_str_t tmp;
- via->branch_param.ptr = pj_pool_alloc(tsx->pool, PJSIP_MAX_BRANCH_LEN);
- via->branch_param.slen = PJSIP_MAX_BRANCH_LEN;
- pj_memcpy(via->branch_param.ptr, PJSIP_RFC3261_BRANCH_ID,
- PJSIP_RFC3261_BRANCH_LEN);
-
- tmp.ptr = via->branch_param.ptr + PJSIP_RFC3261_BRANCH_LEN;
- pj_generate_unique_string( &tmp );
-
- /* Save branch parameter. */
- tsx->branch = via->branch_param;
- } else {
- /* Copy branch parameter. */
- pj_strdup(tsx->pool, &tsx->branch, &via->branch_param);
- }
-
-
- /* Generate transaction key. */
- status = create_tsx_key_3261( tsx->pool, &tsx->transaction_key,
- PJSIP_ROLE_UAC, &tsx->method,
- &via->branch_param);
- if (status != PJ_SUCCESS) {
- unlock_tsx(tsx, &lck);
- return status;
- }
-
- PJ_LOG(6, (tsx->obj_name, "tsx_key=%.*s", tsx->transaction_key.slen,
- tsx->transaction_key.ptr));
-
- /* Save CSeq. */
- cseq = pjsip_msg_find_hdr(msg, PJSIP_H_CSEQ, NULL);
- if (!cseq) {
- pj_assert(!"CSeq header not present in outgoing message!");
- unlock_tsx(tsx, &lck);
- return PJSIP_EMISSINGHDR;
- }
- tsx->cseq = cseq->cseq;
-
-
- /* Begin with State_Null.
- * Manually set-up the state becase we don't want to call the callback.
- */
- tsx->state = PJSIP_TSX_STATE_NULL;
- tsx->state_handler = &pjsip_tsx_on_state_null;
-
- /* Get destination name from the message. */
- status = tsx_process_route(tsx, tdata, &tsx->dest_name);
- if (status != PJ_SUCCESS) {
- tsx->transport_state = PJSIP_TSX_TRANSPORT_STATE_FINAL;
- tsx->status_code = PJSIP_SC_TSX_TRANSPORT_ERROR;
- tsx_set_state(tsx, PJSIP_TSX_STATE_TERMINATED,
- PJSIP_EVENT_TRANSPORT_ERROR, (void*)status);
- unlock_tsx(tsx, &lck);
- return status;
- }
-
- /* Resolve destination.
- * This will start asynchronous resolver job, and when it finishes,
- * the callback will be called.
- */
- PJ_LOG(5,(tsx->obj_name, "tsx resolving destination %.*s:%d",
- tsx->dest_name.host.slen,
- tsx->dest_name.host.ptr,
- tsx->dest_name.port));
-
- tsx->transport_state = PJSIP_TSX_TRANSPORT_STATE_RESOLVING;
- pjsip_endpt_resolve( tsx->endpt, tsx->pool, &tsx->dest_name,
- tsx, &tsx_resolver_callback);
-
- /* There should be nothing to do after this point.
- * Execution for the transaction will resume when the resolver callback is
- * called.
- */
-
- /* Unlock transaction and return.
- * If transaction has been destroyed WITHIN the current thread, the
- * unlock_tsx() function will return -1.
- */
- return unlock_tsx(tsx, &lck);
-}
-
-
-/*
- * Initialize the transaction as UAS transaction.
- */
-PJ_DEF(pj_status_t) pjsip_tsx_init_uas( pjsip_transaction *tsx,
- pjsip_rx_data *rdata)
-{
- pjsip_msg *msg = rdata->msg_info.msg;
- pj_str_t *branch;
- pjsip_cseq_hdr *cseq;
- pj_status_t status;
- struct tsx_lock_data lck;
-
- PJ_LOG(4,(tsx->obj_name, "initializing tsx as UAS (rdata=%p)", rdata));
-
- /* Lock transaction. */
- lock_tsx(tsx, &lck);
-
- /* Keep shortcut to message */
- msg = rdata->msg_info.msg;
-
- /* Role is UAS */
- tsx->role = PJSIP_ROLE_UAS;
-
- /* Save method. */
- pjsip_method_copy( tsx->pool, &tsx->method, &msg->line.req.method);
-
- /* Get transaction key either from branch for RFC3261 message, or
- * create transaction key.
- */
- status = pjsip_tsx_create_key(tsx->pool, &tsx->transaction_key,
- PJSIP_ROLE_UAS, &tsx->method, rdata);
- if (status != PJ_SUCCESS) {
- unlock_tsx(tsx, &lck);
- return status;
- }
-
- /* Duplicate branch parameter for transaction. */
- branch = &rdata->msg_info.via->branch_param;
- pj_strdup(tsx->pool, &tsx->branch, branch);
-
- PJ_LOG(6, (tsx->obj_name, "tsx_key=%.*s", tsx->transaction_key.slen,
- tsx->transaction_key.ptr));
-
- /* Save CSeq */
- cseq = rdata->msg_info.cseq;
- tsx->cseq = cseq->cseq;
-
- /* Begin with state NULL
- * Manually set-up the state becase we don't want to call the callback.
- */
- tsx->state = PJSIP_TSX_STATE_NULL;
- tsx->state_handler = &pjsip_tsx_on_state_null;
-
- /* Get the transport to send the response.
- * According to section 18.2.2 of RFC3261, if the transport is reliable
- * then the response must be sent using that transport.
- */
- /* In addition, RFC 3581 says, if Via has "rport" parameter specified,
- * then return the response using the same transport.
- */
- if (PJSIP_TRANSPORT_IS_RELIABLE(rdata->tp_info.transport) ||
- rdata->msg_info.via->rport_param >= 0)
- {
- tsx->transport = rdata->tp_info.transport;
- pjsip_transport_add_ref(tsx->transport);
- tsx->transport_state = PJSIP_TSX_TRANSPORT_STATE_FINAL;
-
- tsx->current_addr = 0;
- tsx->remote_addr.count = 1;
- tsx->remote_addr.entry[0].type = tsx->transport->type;
- pj_memcpy(&tsx->remote_addr.entry[0].addr,
- &rdata->pkt_info.addr, rdata->pkt_info.addr_len);
-
- } else {
- pj_status_t status;
-
- status = pjsip_get_response_addr(tsx->pool, rdata->tp_info.transport,
- rdata->msg_info.via, &tsx->dest_name);
- if (status != PJ_SUCCESS) {
- tsx->transport_state = PJSIP_TSX_TRANSPORT_STATE_FINAL;
- tsx->status_code = PJSIP_SC_TSX_TRANSPORT_ERROR;
- tsx_set_state(tsx, PJSIP_TSX_STATE_TERMINATED,
- PJSIP_EVENT_TRANSPORT_ERROR, (void*)status);
- unlock_tsx(tsx, &lck);
- return status;
- }
-
- /* Resolve destination.
- * This will start asynchronous resolver job, and when it finishes,
- * the callback will be called.
- */
- PJ_LOG(5,(tsx->obj_name, "tsx resolving destination %.*s:%d",
- tsx->dest_name.host.slen,
- tsx->dest_name.host.ptr,
- tsx->dest_name.port));
-
- tsx->transport_state = PJSIP_TSX_TRANSPORT_STATE_RESOLVING;
- pjsip_endpt_resolve( tsx->endpt, tsx->pool, &tsx->dest_name,
- tsx, &tsx_resolver_callback);
- }
-
- /* There should be nothing to do after this point.
- * Execution for the transaction will resume when the resolver callback is
- * called.
- */
-
- /* Unlock transaction and return.
- * If transaction has been destroyed WITHIN the current thread, the
- * unlock_tsx() function will return -1.
- */
- return unlock_tsx(tsx, &lck);
-}
-
-/*
- * Callback when timer expires.
- */
-static void tsx_timer_callback( pj_timer_heap_t *theap, pj_timer_entry *entry)
-{
- pjsip_event event;
- pjsip_transaction *tsx = entry->user_data;
- struct tsx_lock_data lck;
-
- PJ_UNUSED_ARG(theap);
-
- PJ_LOG(5,(tsx->obj_name, "got timer event (%s timer)",
- (entry->id==TSX_TIMER_RETRANSMISSION ? "Retransmit" : "Timeout")));
-
-
- if (entry->id == TSX_TIMER_RETRANSMISSION) {
- PJSIP_EVENT_INIT_TIMER(event, &tsx->retransmit_timer);
- } else {
- PJSIP_EVENT_INIT_TIMER(event, &tsx->timeout_timer);
- }
-
- /* Dispatch event to transaction. */
- lock_tsx(tsx, &lck);
- (*tsx->state_handler)(tsx, &event);
- unlock_tsx(tsx, &lck);
-}
-
-/*
- * Transmit ACK message for 2xx/INVITE with this transaction. The ACK for
- * non-2xx/INVITE is automatically sent by the transaction.
- * This operation is only valid if the transaction is configured to handle ACK
- * (tsx->handle_ack is non-zero). If this attribute is not set, then the
- * transaction will comply with RFC-3261, i.e. it will set itself to
- * TERMINATED state when it receives 2xx/INVITE.
- */
-PJ_DEF(void) pjsip_tsx_on_tx_ack( pjsip_transaction *tsx, pjsip_tx_data *tdata)
-{
- pjsip_msg *msg;
- pjsip_host_port dest_addr;
- pjsip_via_hdr *via;
- struct tsx_lock_data lck;
- pj_status_t status = PJ_SUCCESS;
-
- /* Lock tsx. */
- lock_tsx(tsx, &lck);
-
- pj_assert(tsx->handle_ack != 0);
-
- msg = tdata->msg;
-
- /* Generate branch parameter if it doesn't exist. */
- via = pjsip_msg_find_hdr(msg, PJSIP_H_VIA, NULL);
- if (via == NULL) {
- via = pjsip_via_hdr_create(tdata->pool);
- pjsip_msg_add_hdr(msg, (pjsip_hdr*) via);
- }
-
- if (via->branch_param.slen == 0) {
- via->branch_param = tsx->branch;
- } else {
- pj_assert( pj_strcmp(&via->branch_param, &tsx->branch) == 0 );
- }
-
- /* Get destination name from the message. */
- status = tsx_process_route(tsx, tdata, &dest_addr);
- if (status != 0){
- goto on_error;
- }
-
- /* Compare message's destination name with transaction's destination name.
- * If NOT equal, then we'll have to resolve the destination.
- */
- if (dest_addr.type == tsx->dest_name.type &&
- dest_addr.flag == tsx->dest_name.flag &&
- dest_addr.port == tsx->dest_name.port &&
- pj_stricmp(&dest_addr.host, &tsx->dest_name.host) == 0)
- {
- /* Equal destination. We can use current transport. */
- pjsip_tsx_on_tx_msg(tsx, tdata);
- unlock_tsx(tsx, &lck);
- return;
-
- }
-
- /* New destination; we'll have to resolve host and create new transport. */
- pj_memcpy(&tsx->dest_name, &dest_addr, sizeof(dest_addr));
- pj_strdup(tsx->pool, &tsx->dest_name.host, &dest_addr.host);
-
- PJ_LOG(5,(tsx->obj_name, "tsx resolving destination %.*s:%d",
- tsx->dest_name.host.slen,
- tsx->dest_name.host.ptr,
- tsx->dest_name.port));
-
- tsx->transport_state = PJSIP_TSX_TRANSPORT_STATE_RESOLVING;
- pjsip_transport_dec_ref(tsx->transport);
- tsx->transport = NULL;
-
- /* Put the message in queue. */
- pjsip_tsx_on_tx_msg(tsx, tdata);
-
- /* This is a bug!
- * We shouldn't change transaction's state before actually sending the
- * message. Otherwise transaction will terminate before message is sent,
- * and timeout timer will be scheduled.
- */
- PJ_TODO(TSX_DONT_CHANGE_STATE_BEFORE_SENDING_ACK)
-
- /*
- * This will start asynchronous resolver job, and when it finishes,
- * the callback will be called.
- */
-
- tsx->transport_state = PJSIP_TSX_TRANSPORT_STATE_RESOLVING;
- pjsip_endpt_resolve( tsx->endpt, tsx->pool, &tsx->dest_name,
- tsx, &tsx_resolver_callback);
-
- unlock_tsx(tsx, &lck);
-
- /* There should be nothing to do after this point.
- * Execution for the transaction will resume when the resolver callback is
- * called.
- */
- return;
-
-on_error:
- /* Failure condition.
- * Send TERMINATED event.
- */
- tsx->status_code = PJSIP_SC_TSX_TRANSPORT_ERROR;
-
- tsx_set_state( tsx, PJSIP_TSX_STATE_TERMINATED,
- PJSIP_EVENT_TRANSPORT_ERROR, (void*)status);
-
- unlock_tsx(tsx, &lck);
-}
-
-
-/*
- * This function is called by TU to send a message.
- */
-PJ_DEF(void) pjsip_tsx_on_tx_msg( pjsip_transaction *tsx,
- pjsip_tx_data *tdata )
-{
- pjsip_event event;
- struct tsx_lock_data lck;
- pj_status_t status;
-
- PJ_LOG(5,(tsx->obj_name, "Request to transmit msg on state %s (tdata=%p)",
- state_str[tsx->state], tdata));
-
- PJSIP_EVENT_INIT_TX_MSG(event, tsx, tdata);
-
- /* Dispatch to transaction. */
- lock_tsx(tsx, &lck);
- status = (*tsx->state_handler)(tsx, &event);
- unlock_tsx(tsx, &lck);
-}
-
-/*
- * This function is called by endpoint when incoming message for the
- * transaction is received.
- */
-PJ_DEF(void) pjsip_tsx_on_rx_msg( pjsip_transaction *tsx,
- pjsip_rx_data *rdata)
-{
- pjsip_event event;
- struct tsx_lock_data lck;
- pj_status_t status;
-
- PJ_LOG(5,(tsx->obj_name, "Incoming msg on state %s (rdata=%p)",
- state_str[tsx->state], rdata));
-
- PJSIP_EVENT_INIT_RX_MSG(event, tsx, rdata);
-
- /* Dispatch to transaction. */
- lock_tsx(tsx, &lck);
- status = (*tsx->state_handler)(tsx, &event);
- unlock_tsx(tsx, &lck);
-}
-
-/*
- * Forcely terminate transaction.
- */
-PJ_DEF(void) pjsip_tsx_terminate( pjsip_transaction *tsx, int code )
-{
- struct tsx_lock_data lck;
-
- lock_tsx(tsx, &lck);
- tsx->status_code = code;
- tsx_set_state( tsx, PJSIP_TSX_STATE_TERMINATED,
- PJSIP_EVENT_USER, NULL);
-
- unlock_tsx(tsx, &lck);
-}
-
-/*
- * Transport send completion callback.
- */
-static void tsx_on_send_complete(void *token, pjsip_tx_data *tdata,
- pj_ssize_t bytes_sent)
-{
- PJ_UNUSED_ARG(token);
- PJ_UNUSED_ARG(tdata);
-
- if (bytes_sent <= 0) {
- PJ_TODO(HANDLE_TRANSPORT_ERROR);
- }
-}
-
-/*
- * Send message to the transport.
- * If transport is not yet available, then do nothing. The message will be
- * transmitted when transport connection completion callback is called.
- */
-static pj_status_t tsx_send_msg( pjsip_transaction *tsx,
- pjsip_tx_data *tdata)
-{
- pj_status_t status = PJ_SUCCESS;
-
- PJ_LOG(5,(tsx->obj_name, "sending msg (tdata=%p)", tdata));
-
- if (tsx->transport_state == PJSIP_TSX_TRANSPORT_STATE_FINAL) {
- pjsip_event before_tx_event;
-
- pj_assert(tsx->transport != NULL);
-
- /* Make sure Via transport info is filled up properly for
- * requests.
- */
- if (tdata->msg->type == PJSIP_REQUEST_MSG) {
- const pj_sockaddr_in *addr_name;
- pjsip_via_hdr *via = (pjsip_via_hdr*)
- pjsip_msg_find_hdr(tdata->msg, PJSIP_H_VIA, NULL);
-
- /* For request message, set "rport" parameter by default. */
- if (tdata->msg->type == PJSIP_REQUEST_MSG)
- via->rport_param = 0;
-
- /* Don't update Via sent-by on retransmission. */
- if (via->sent_by.host.slen == 0) {
- addr_name = &tsx->transport->public_addr;
- pj_strdup2(tdata->pool, &via->transport,
- tsx->transport->type_name);
- pj_strdup2(tdata->pool, &via->sent_by.host,
- pj_inet_ntoa(addr_name->sin_addr));
- via->sent_by.port = pj_ntohs(addr_name->sin_port);
- }
- }
-
- /* Notify everybody we're about to send message. */
- PJSIP_EVENT_INIT_PRE_TX_MSG(before_tx_event, tsx, tdata,
- tsx->retransmit_count);
- pjsip_endpt_send_tsx_event( tsx->endpt, &before_tx_event );
-
- tsx->has_unsent_msg = 0;
- status = pjsip_transport_send(tsx->transport, tdata,
- &tsx->remote_addr.entry[tsx->current_addr].addr,
- tsx, &tsx_on_send_complete);
- if (status != PJ_SUCCESS && status != PJ_EPENDING) {
- PJ_TODO(HANDLE_TRANSPORT_ERROR);
- goto on_error;
- }
- } else {
- tsx->has_unsent_msg = 1;
- }
-
- return 0;
-
-on_error:
- tsx->status_code = PJSIP_SC_TSX_TRANSPORT_ERROR;
- tsx_set_state( tsx, PJSIP_TSX_STATE_TERMINATED,
- PJSIP_EVENT_TRANSPORT_ERROR, (void*)status);
- return status;
-}
-
-/*
- * Retransmit last message sent.
- */
-static pj_status_t pjsip_tsx_retransmit( pjsip_transaction *tsx,
- int should_restart_timer)
-{
- pj_status_t status;
-
- PJ_LOG(4,(tsx->obj_name, "retransmiting (tdata=%p, count=%d, restart?=%d)",
- tsx->last_tx, tsx->retransmit_count, should_restart_timer));
-
- pj_assert(tsx->last_tx != NULL);
-
- ++tsx->retransmit_count;
-
- status = tsx_send_msg( tsx, tsx->last_tx);
- if (status != PJ_SUCCESS) {
- return status;
- }
-
- /* Restart timer T1. */
- if (should_restart_timer) {
- pj_time_val timeout;
- int msec_time = (1 << (tsx->retransmit_count)) * PJSIP_T1_TIMEOUT;
-
- if (tsx->method.id!=PJSIP_INVITE_METHOD && msec_time>PJSIP_T2_TIMEOUT)
- msec_time = PJSIP_T2_TIMEOUT;
-
- timeout.sec = msec_time / 1000;
- timeout.msec = msec_time % 1000;
- pjsip_endpt_schedule_timer( tsx->endpt, &tsx->retransmit_timer,
- &timeout);
- }
-
- return PJ_SUCCESS;
-}
-
-/*
- * Handler for events in state Null.
- */
-static pj_status_t pjsip_tsx_on_state_null( pjsip_transaction *tsx,
- pjsip_event *event )
-{
- pj_status_t status;
-
- pj_assert( tsx->state == PJSIP_TSX_STATE_NULL);
- pj_assert( tsx->last_tx == NULL );
- pj_assert( tsx->has_unsent_msg == 0);
-
- if (tsx->role == PJSIP_ROLE_UAS) {
-
- /* Set state to Trying. */
- pj_assert(event->type == PJSIP_EVENT_RX_MSG);
- tsx_set_state( tsx, PJSIP_TSX_STATE_TRYING,
- PJSIP_EVENT_RX_MSG, event->body.rx_msg.rdata );
-
- } else {
- pjsip_tx_data *tdata = event->body.tx_msg.tdata;
-
- /* Save the message for retransmission. */
- tsx->last_tx = tdata;
- pjsip_tx_data_add_ref(tdata);
-
- /* Send the message. */
- status = tsx_send_msg( tsx, tdata);
- if (status != PJ_SUCCESS) {
- return status;
- }
-
- /* Start Timer B (or called timer F for non-INVITE) for transaction
- * timeout.
- */
- pjsip_endpt_schedule_timer( tsx->endpt, &tsx->timeout_timer,
- &timeout_timer_val);
-
- /* Start Timer A (or timer E) for retransmission only if unreliable
- * transport is being used.
- */
- if (tsx->transport_state == PJSIP_TSX_TRANSPORT_STATE_FINAL &&
- PJSIP_TRANSPORT_IS_RELIABLE(tsx->transport)==0)
- {
- pjsip_endpt_schedule_timer(tsx->endpt, &tsx->retransmit_timer,
- &t1_timer_val);
- tsx->retransmit_count = 0;
- }
-
- /* Move state. */
- tsx_set_state( tsx, PJSIP_TSX_STATE_CALLING,
- PJSIP_EVENT_TX_MSG, tdata);
- }
-
- return PJ_SUCCESS;
-}
-
-/*
- * State Calling is for UAC after it sends request but before any responses
- * is received.
- */
-static pj_status_t pjsip_tsx_on_state_calling( pjsip_transaction *tsx,
- pjsip_event *event )
-{
- pj_assert(tsx->state == PJSIP_TSX_STATE_CALLING);
- pj_assert(tsx->role == PJSIP_ROLE_UAC);
-
- if (event->type == PJSIP_EVENT_TIMER &&
- event->body.timer.entry == &tsx->retransmit_timer)
- {
- pj_status_t status;
-
- /* Retransmit the request. */
- status = pjsip_tsx_retransmit( tsx, 1 );
- if (status != PJ_SUCCESS) {
- return status;
- }
-
- } else if (event->type == PJSIP_EVENT_TIMER &&
- event->body.timer.entry == &tsx->timeout_timer)
- {
-
- /* Cancel retransmission timer. */
- if (PJSIP_TRANSPORT_IS_RELIABLE(tsx->transport)==0) {
- pjsip_endpt_cancel_timer(tsx->endpt, &tsx->retransmit_timer);
- }
-
- /* Set status code */
- tsx->status_code = PJSIP_SC_TSX_TIMEOUT;
-
- /* Inform TU. */
- tsx_set_state( tsx, PJSIP_TSX_STATE_TERMINATED,
- PJSIP_EVENT_TIMER, &tsx->timeout_timer);
-
- /* Transaction is destroyed */
- return PJSIP_ETSXDESTROYED;
-
- } else if (event->type == PJSIP_EVENT_RX_MSG) {
- int code;
-
- /* Cancel retransmission timer A. */
- if (PJSIP_TRANSPORT_IS_RELIABLE(tsx->transport)==0)
- pjsip_endpt_cancel_timer(tsx->endpt, &tsx->retransmit_timer);
-
- /* Cancel timer B (transaction timeout) */
- pjsip_endpt_cancel_timer(tsx->endpt, &tsx->timeout_timer);
-
- /* Discard retransmission message if it is not INVITE.
- * The INVITE tdata is needed in case we have to generate ACK for
- * the final response.
- */
- /* Keep last_tx for authorization. */
- code = event->body.rx_msg.rdata->msg_info.msg->line.status.code;
- if (tsx->method.id != PJSIP_INVITE_METHOD && code!=401 && code!=407) {
- pjsip_tx_data_dec_ref(tsx->last_tx);
- tsx->last_tx = NULL;
- }
-
- /* Processing is similar to state Proceeding. */
- pjsip_tsx_on_state_proceeding_uac( tsx, event);
-
- } else {
- pj_assert(0);
- return PJ_EBUG;
- }
-
- return PJ_SUCCESS;
-}
-
-/*
- * State Trying is for UAS after it received request but before any responses
- * is sent.
- * Note: this is different than RFC3261, which can use Trying state for
- * non-INVITE client transaction (bug in RFC?).
- */
-static pj_status_t pjsip_tsx_on_state_trying( pjsip_transaction *tsx,
- pjsip_event *event)
-{
- pj_status_t status;
-
- pj_assert(tsx->state == PJSIP_TSX_STATE_TRYING);
-
- /* This state is only for UAS */
- pj_assert(tsx->role == PJSIP_ROLE_UAS);
-
- /* Better be transmission of response message.
- * If we've got request retransmission, this means that the TU hasn't
- * transmitted any responses within 500 ms, which is not allowed. If
- * this happens, just ignore the event (we couldn't retransmit last
- * response because we haven't sent any!).
- */
- //pj_assert(event->type == PJSIP_EVENT_TX_MSG);
- if (event->type != PJSIP_EVENT_TX_MSG) {
- return PJ_SUCCESS;
- }
-
- /* The rest of the processing of the event is exactly the same as in
- * "Proceeding" state.
- */
- status = pjsip_tsx_on_state_proceeding_uas( tsx, event);
-
- /* Inform the TU of the state transision if state is still State_Trying */
- if (status==PJ_SUCCESS && tsx->state == PJSIP_TSX_STATE_TRYING) {
- tsx_set_state( tsx, PJSIP_TSX_STATE_PROCEEDING,
- PJSIP_EVENT_TX_MSG, event->body.tx_msg.tdata);
- }
-
- return status;
-}
-
-/*
- * Handler for events in Proceeding for UAS
- * This state happens after the TU sends provisional response.
- */
-static pj_status_t pjsip_tsx_on_state_proceeding_uas( pjsip_transaction *tsx,
- pjsip_event *event)
-{
- pj_assert(tsx->state == PJSIP_TSX_STATE_PROCEEDING ||
- tsx->state == PJSIP_TSX_STATE_TRYING);
-
- /* This state is only for UAS. */
- pj_assert(tsx->role == PJSIP_ROLE_UAS);
-
- /* Receive request retransmission. */
- if (event->type == PJSIP_EVENT_RX_MSG) {
-
- pj_status_t status;
-
- /* Send last response. */
- status = pjsip_tsx_retransmit( tsx, 0 );
- if (status != PJ_SUCCESS) {
- return status;
- }
-
- } else if (event->type == PJSIP_EVENT_TX_MSG ) {
- pjsip_tx_data *tdata = event->body.tx_msg.tdata;
- pj_status_t status;
-
- /* The TU sends response message to the request. Save this message so
- * that we can retransmit the last response in case we receive request
- * retransmission.
- */
- pjsip_msg *msg = tdata->msg;
-
- /* This can only be a response message. */
- pj_assert(msg->type == PJSIP_RESPONSE_MSG);
-
- /* Status code must be higher than last sent. */
- pj_assert(msg->line.status.code >= tsx->status_code);
-
- /* Update last status */
- tsx->status_code = msg->line.status.code;
-
- /* Discard the saved last response (it will be updated later as
- * necessary).
- */
- if (tsx->last_tx && tsx->last_tx != tdata) {
- pjsip_tx_data_dec_ref( tsx->last_tx );
- tsx->last_tx = NULL;
- }
-
- /* Send the message. */
- status = tsx_send_msg(tsx, tdata);
- if (status != PJ_SUCCESS) {
- return status;
- }
-
- // Update To tag header for RFC2543 transaction.
- // TODO:
-
- /* Update transaction state */
- if (PJSIP_IS_STATUS_IN_CLASS(tsx->status_code, 100)) {
-
- if (tsx->last_tx != tdata) {
- tsx->last_tx = tdata;
- pjsip_tx_data_add_ref( tdata );
- }
- tsx_set_state( tsx, PJSIP_TSX_STATE_PROCEEDING,
- PJSIP_EVENT_TX_MSG, tdata );
-
- } else if (PJSIP_IS_STATUS_IN_CLASS(tsx->status_code, 200)) {
-
- if (tsx->method.id == PJSIP_INVITE_METHOD && tsx->handle_ack==0) {
-
- /* 2xx class message is not saved, because retransmission
- * is handled by TU.
- */
- tsx_set_state( tsx, PJSIP_TSX_STATE_TERMINATED,
- PJSIP_EVENT_TX_MSG, tdata );
-
- /* Transaction is destroyed. */
- return PJSIP_ETSXDESTROYED;
-
- } else {
- pj_time_val timeout;
-
- if (tsx->method.id == PJSIP_INVITE_METHOD) {
- tsx->retransmit_count = 0;
- pjsip_endpt_schedule_timer( tsx->endpt,
- &tsx->retransmit_timer,
- &t1_timer_val);
- }
-
- /* Save last response sent for retransmission when request
- * retransmission is received.
- */
- if (tsx->last_tx != tdata) {
- tsx->last_tx = tdata;
- pjsip_tx_data_add_ref(tdata);
- }
-
- /* Start timer J at 64*T1 for unreliable transport or zero for
- * reliable transport.
- */
- if (PJSIP_TRANSPORT_IS_RELIABLE(tsx->transport)==0) {
- timeout = timeout_timer_val;
- } else {
- timeout.sec = timeout.msec = 0;
- }
-
- pjsip_endpt_schedule_timer( tsx->endpt, &tsx->timeout_timer,
- &timeout);
-
- /* Set state to "Completed" */
- tsx_set_state( tsx, PJSIP_TSX_STATE_COMPLETED,
- PJSIP_EVENT_TX_MSG, tdata );
- }
-
- } else if (tsx->status_code >= 300) {
-
- /* 3xx-6xx class message causes transaction to move to
- * "Completed" state.
- */
- if (tsx->last_tx != tdata) {
- tsx->last_tx = tdata;
- pjsip_tx_data_add_ref( tdata );
- }
-
- /* Start timer H for transaction termination */
- pjsip_endpt_schedule_timer(tsx->endpt,&tsx->timeout_timer,
- &timeout_timer_val);
-
- /* For INVITE, if unreliable transport is used, retransmission
- * timer G will be scheduled (retransmission).
- */
- if (PJSIP_TRANSPORT_IS_RELIABLE(tsx->transport)==0) {
- pjsip_cseq_hdr *cseq = pjsip_msg_find_hdr( msg, PJSIP_H_CSEQ,
- NULL);
- if (cseq->method.id == PJSIP_INVITE_METHOD) {
- tsx->retransmit_count = 0;
- pjsip_endpt_schedule_timer(tsx->endpt,
- &tsx->retransmit_timer,
- &t1_timer_val);
- }
- }
-
- /* Inform TU */
- tsx_set_state( tsx, PJSIP_TSX_STATE_COMPLETED,
- PJSIP_EVENT_TX_MSG, tdata );
-
- } else {
- pj_assert(0);
- }
-
-
- } else if (event->type == PJSIP_EVENT_TIMER &&
- event->body.timer.entry == &tsx->retransmit_timer) {
- /* Retransmission timer elapsed. */
- pj_status_t status;
-
- /* Must have last response to retransmit. */
- pj_assert(tsx->last_tx != NULL);
-
- /* Retransmit the last response. */
- status = pjsip_tsx_retransmit( tsx, 1 );
- if (status != PJ_SUCCESS) {
- return status;
- }
-
- } else if (event->type == PJSIP_EVENT_TIMER &&
- event->body.timer.entry == &tsx->timeout_timer) {
-
- /* Timeout timer. should not happen? */
- pj_assert(0);
-
- tsx->status_code = PJSIP_SC_TSX_TIMEOUT;
-
- tsx_set_state( tsx, PJSIP_TSX_STATE_TERMINATED,
- PJSIP_EVENT_TIMER, &tsx->timeout_timer);
-
- return PJ_EBUG;
-
- } else {
- pj_assert(0);
- return PJ_EBUG;
- }
-
- return PJ_SUCCESS;
-}
-
-/*
- * Handler for events in Proceeding for UAC
- * This state happens after provisional response(s) has been received from
- * UAS.
- */
-static pj_status_t pjsip_tsx_on_state_proceeding_uac(pjsip_transaction *tsx,
- pjsip_event *event)
-{
-
- pj_assert(tsx->state == PJSIP_TSX_STATE_PROCEEDING ||
- tsx->state == PJSIP_TSX_STATE_CALLING);
-
- if (event->type != PJSIP_EVENT_TIMER) {
- /* Must be incoming response, because we should not retransmit
- * request once response has been received.
- */
- pj_assert(event->type == PJSIP_EVENT_RX_MSG);
- if (event->type != PJSIP_EVENT_RX_MSG) {
- return PJ_EINVALIDOP;
- }
-
- tsx->status_code = event->body.rx_msg.rdata->msg_info.msg->line.status.code;
- } else {
- tsx->status_code = PJSIP_SC_TSX_TIMEOUT;
- }
-
- if (PJSIP_IS_STATUS_IN_CLASS(tsx->status_code, 100)) {
-
- /* Inform the message to TU. */
- tsx_set_state( tsx, PJSIP_TSX_STATE_PROCEEDING,
- PJSIP_EVENT_RX_MSG, event->body.rx_msg.rdata );
-
- } else if (PJSIP_IS_STATUS_IN_CLASS(tsx->status_code,200)) {
-
- /* Stop timeout timer B/F. */
- pjsip_endpt_cancel_timer( tsx->endpt, &tsx->timeout_timer );
-
- /* For INVITE, the state moves to Terminated state (because ACK is
- * handled in TU). For non-INVITE, state moves to Completed.
- */
- if (tsx->method.id == PJSIP_INVITE_METHOD && tsx->handle_ack == 0) {
- tsx_set_state( tsx, PJSIP_TSX_STATE_TERMINATED,
- PJSIP_EVENT_RX_MSG, event->body.rx_msg.rdata );
- return PJSIP_ETSXDESTROYED;
-
- } else {
- pj_time_val timeout;
-
- /* For unreliable transport, start timer D (for INVITE) or
- * timer K for non-INVITE. */
- if (PJSIP_TRANSPORT_IS_RELIABLE(tsx->transport) == 0) {
- if (tsx->method.id == PJSIP_INVITE_METHOD) {
- timeout = td_timer_val;
- } else {
- timeout = t4_timer_val;
- }
- } else {
- timeout.sec = timeout.msec = 0;
- }
- pjsip_endpt_schedule_timer( tsx->endpt, &tsx->timeout_timer,
- &timeout);
-
- /* Move state to Completed, inform TU. */
- tsx_set_state( tsx, PJSIP_TSX_STATE_COMPLETED,
- PJSIP_EVENT_RX_MSG, event->body.rx_msg.rdata );
- }
-
- } else if (tsx->status_code >= 300 && tsx->status_code <= 699) {
- pj_time_val timeout;
- pj_status_t status;
-
- /* Stop timer B. */
- pjsip_endpt_cancel_timer( tsx->endpt, &tsx->timeout_timer );
-
- /* Generate and send ACK for INVITE. */
- if (tsx->method.id == PJSIP_INVITE_METHOD) {
- pjsip_endpt_create_ack( tsx->endpt, tsx->last_tx,
- event->body.rx_msg.rdata );
- status = tsx_send_msg( tsx, tsx->last_tx);
- if (status != PJ_SUCCESS) {
- return status;
- }
- }
-
- /* Start Timer D with TD/T4 timer if unreliable transport is used. */
- if (PJSIP_TRANSPORT_IS_RELIABLE(tsx->transport) == 0) {
- if (tsx->method.id == PJSIP_INVITE_METHOD) {
- timeout = td_timer_val;
- } else {
- timeout = t4_timer_val;
- }
- } else {
- timeout.sec = timeout.msec = 0;
- }
- pjsip_endpt_schedule_timer( tsx->endpt, &tsx->timeout_timer, &timeout);
-
- /* Inform TU. */
- tsx_set_state( tsx, PJSIP_TSX_STATE_COMPLETED,
- PJSIP_EVENT_RX_MSG, event->body.rx_msg.rdata );
-
- } else {
- // Shouldn't happen because there's no timer for this state.
- pj_assert(0);
- return PJ_EBUG;
- }
-
- return PJ_SUCCESS;
-}
-
-/*
- * Handler for events in Completed state for UAS
- */
-static pj_status_t pjsip_tsx_on_state_completed_uas( pjsip_transaction *tsx,
- pjsip_event *event)
-{
- pj_assert(tsx->state == PJSIP_TSX_STATE_COMPLETED);
-
- if (event->type == PJSIP_EVENT_RX_MSG) {
- pjsip_msg *msg = event->body.rx_msg.rdata->msg_info.msg;
- pjsip_cseq_hdr *cseq = pjsip_msg_find_hdr( msg, PJSIP_H_CSEQ, NULL );
-
- /* On receive request retransmission, retransmit last response. */
- if (cseq->method.id != PJSIP_ACK_METHOD) {
- pj_status_t status;
-
- status = pjsip_tsx_retransmit( tsx, 0 );
- if (status != PJ_SUCCESS) {
- return status;
- }
-
- } else {
- /* Process incoming ACK request. */
-
- /* Cease retransmission. */
- pjsip_endpt_cancel_timer( tsx->endpt, &tsx->retransmit_timer );
-
- /* Start timer I in T4 interval (transaction termination). */
- pjsip_endpt_cancel_timer( tsx->endpt, &tsx->timeout_timer );
- pjsip_endpt_schedule_timer( tsx->endpt, &tsx->timeout_timer,
- &t4_timer_val);
-
- /* Move state to "Confirmed" */
- tsx_set_state( tsx, PJSIP_TSX_STATE_CONFIRMED,
- PJSIP_EVENT_RX_MSG, event->body.rx_msg.rdata );
- }
-
- } else if (event->type == PJSIP_EVENT_TIMER) {
-
- if (event->body.timer.entry == &tsx->retransmit_timer) {
- /* Retransmit message. */
- pj_status_t status;
-
- status = pjsip_tsx_retransmit( tsx, 1 );
- if (status != PJ_SUCCESS) {
- return status;
- }
-
- } else {
- if (tsx->method.id == PJSIP_INVITE_METHOD) {
-
- /* For INVITE, this means that ACK was never received.
- * Set state to Terminated, and inform TU.
- */
-
- tsx->status_code = PJSIP_SC_TSX_TIMEOUT;
-
- tsx_set_state( tsx, PJSIP_TSX_STATE_TERMINATED,
- PJSIP_EVENT_TIMER, &tsx->timeout_timer );
-
- return PJSIP_ETSXDESTROYED;
-
- } else {
- /* Transaction terminated, it can now be deleted. */
- tsx_set_state( tsx, PJSIP_TSX_STATE_TERMINATED,
- PJSIP_EVENT_TIMER, &tsx->timeout_timer );
- return PJSIP_ETSXDESTROYED;
- }
- }
-
- } else {
- /* Ignore request to transmit. */
- pj_assert(event->body.tx_msg.tdata == tsx->last_tx);
- }
-
- return PJ_SUCCESS;
-}
-
-/*
- * Handler for events in Completed state for UAC transaction.
- */
-static pj_status_t pjsip_tsx_on_state_completed_uac( pjsip_transaction *tsx,
- pjsip_event *event)
-{
- pj_assert(tsx->state == PJSIP_TSX_STATE_COMPLETED);
-
- if (event->type == PJSIP_EVENT_TIMER) {
- /* Must be the timeout timer. */
- pj_assert(event->body.timer.entry == &tsx->timeout_timer);
-
- /* Move to Terminated state. */
- tsx_set_state( tsx, PJSIP_TSX_STATE_TERMINATED,
- PJSIP_EVENT_TIMER, event->body.timer.entry );
-
- /* Transaction has been destroyed. */
- return PJSIP_ETSXDESTROYED;
-
- } else if (event->type == PJSIP_EVENT_RX_MSG) {
- if (tsx->method.id == PJSIP_INVITE_METHOD) {
- /* On received of final response retransmission, retransmit the ACK.
- * TU doesn't need to be informed.
- */
- pjsip_msg *msg = event->body.rx_msg.rdata->msg_info.msg;
- pj_assert(msg->type == PJSIP_RESPONSE_MSG);
- if (msg->type==PJSIP_RESPONSE_MSG &&
- msg->line.status.code >= 200)
- {
- pj_status_t status;
-
- status = pjsip_tsx_retransmit( tsx, 0 );
- if (status != PJ_SUCCESS) {
- return status;
- }
- } else {
- /* Very late retransmission of privisional response. */
- pj_assert( msg->type == PJSIP_RESPONSE_MSG );
- }
- } else {
- /* Just drop the response. */
- }
- } else if (tsx->method.id == PJSIP_INVITE_METHOD &&
- event->type == PJSIP_EVENT_TX_MSG &&
- event->body.tx_msg.tdata->msg->line.req.method.id==PJSIP_ACK_METHOD) {
-
- pj_status_t status;
-
- /* Set last transmitted message. */
- if (tsx->last_tx != event->body.tx_msg.tdata) {
- pjsip_tx_data_dec_ref( tsx->last_tx );
- tsx->last_tx = event->body.tx_msg.tdata;
- pjsip_tx_data_add_ref( tsx->last_tx );
- }
-
- /* No state changed, but notify app.
- * Must notify now, so app has chance to put SDP in outgoing ACK msg.
- */
- tsx_set_state( tsx, PJSIP_TSX_STATE_COMPLETED,
- PJSIP_EVENT_TX_MSG, event->body.tx_msg.tdata );
-
- /* Send msg */
- status = tsx_send_msg(tsx, event->body.tx_msg.tdata);
- if (status != PJ_SUCCESS)
- return status;
-
- } else {
- pj_assert(0);
- return PJ_EBUG;
- }
-
- return PJ_SUCCESS;
-}
-
-/*
- * Handler for events in state Confirmed.
- */
-static pj_status_t pjsip_tsx_on_state_confirmed( pjsip_transaction *tsx,
- pjsip_event *event)
-{
- pj_assert(tsx->state == PJSIP_TSX_STATE_CONFIRMED);
-
- /* This state is only for UAS for INVITE. */
- pj_assert(tsx->role == PJSIP_ROLE_UAS);
- pj_assert(tsx->method.id == PJSIP_INVITE_METHOD);
-
- /* Absorb any ACK received. */
- if (event->type == PJSIP_EVENT_RX_MSG) {
-
- pjsip_method_e method_id =
- event->body.rx_msg.rdata->msg_info.msg->line.req.method.id;
-
- /* Must be a request message. */
- pj_assert(event->body.rx_msg.rdata->msg_info.msg->type == PJSIP_REQUEST_MSG);
-
- /* Must be an ACK request or a late INVITE retransmission. */
- pj_assert(method_id == PJSIP_ACK_METHOD ||
- method_id == PJSIP_INVITE_METHOD);
-
- /* Just so that compiler won't complain about unused vars when
- * building release code.
- */
- PJ_UNUSED_ARG(method_id);
-
- } else if (event->type == PJSIP_EVENT_TIMER) {
- /* Must be from timeout_timer_. */
- pj_assert(event->body.timer.entry == &tsx->timeout_timer);
-
- /* Move to Terminated state. */
- tsx_set_state( tsx, PJSIP_TSX_STATE_TERMINATED,
- PJSIP_EVENT_TIMER, &tsx->timeout_timer );
-
- /* Transaction has been destroyed. */
- return PJSIP_ETSXDESTROYED;
-
- } else {
- pj_assert(0);
- return PJ_EBUG;
- }
-
- return PJ_SUCCESS;
-}
-
-/*
- * Handler for events in state Terminated.
- */
-static pj_status_t pjsip_tsx_on_state_terminated( pjsip_transaction *tsx,
- pjsip_event *event)
-{
- pj_assert(tsx->state == PJSIP_TSX_STATE_TERMINATED);
-
- PJ_UNUSED_ARG(event);
-
- /* Destroy this transaction */
- tsx_set_state(tsx, PJSIP_TSX_STATE_DESTROYED,
- event->type, event->body.user.user1 );
-
- return PJ_SUCCESS;
-}
-
-
-static pj_status_t pjsip_tsx_on_state_destroyed(pjsip_transaction *tsx,
- pjsip_event *event)
-{
- PJ_UNUSED_ARG(tsx);
- PJ_UNUSED_ARG(event);
- return PJ_SUCCESS;
-}
-
+/* $Id$ */
+/*
+ * Copyright (C) 2003-2006 Benny Prijono <benny@prijono.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#include <pjsip/sip_transaction.h>
+#include <pjsip/sip_transport.h>
+#include <pjsip/sip_config.h>
+#include <pjsip/sip_util.h>
+#include <pjsip/sip_event.h>
+#include <pjsip/sip_endpoint.h>
+#include <pjsip/sip_errno.h>
+#include <pj/log.h>
+#include <pj/string.h>
+#include <pj/os.h>
+#include <pj/guid.h>
+#include <pj/pool.h>
+#include <pj/assert.h>
+
+/* Thread Local Storage ID for transaction lock (initialized by endpoint) */
+long pjsip_tsx_lock_tls_id;
+
+/* State names */
+static const char *state_str[] =
+{
+ "Null",
+ "Calling",
+ "Trying",
+ "Proceeding",
+ "Completed",
+ "Confirmed",
+ "Terminated",
+ "Destroyed",
+};
+
+/* Role names */
+static const char *role_name[] =
+{
+ "Client",
+ "Server"
+};
+
+/* Transaction lock. */
+typedef struct tsx_lock_data {
+ struct tsx_lock_data *prev;
+ pjsip_transaction *tsx;
+ int is_alive;
+} tsx_lock_data;
+
+
+/* Timer timeout value constants */
+static const pj_time_val t1_timer_val = { PJSIP_T1_TIMEOUT/1000,
+ PJSIP_T1_TIMEOUT%1000 };
+static const pj_time_val t4_timer_val = { PJSIP_T4_TIMEOUT/1000,
+ PJSIP_T4_TIMEOUT%1000 };
+static const pj_time_val td_timer_val = { PJSIP_TD_TIMEOUT/1000,
+ PJSIP_TD_TIMEOUT%1000 };
+static const pj_time_val timeout_timer_val = { (64*PJSIP_T1_TIMEOUT)/1000,
+ (64*PJSIP_T1_TIMEOUT)%1000 };
+
+/* Internal timer IDs */
+enum Transaction_Timer_Id
+{
+ TSX_TIMER_RETRANSMISSION,
+ TSX_TIMER_TIMEOUT,
+};
+
+/* Function Prototypes */
+static pj_status_t pjsip_tsx_on_state_null( pjsip_transaction *tsx,
+ pjsip_event *event);
+static pj_status_t pjsip_tsx_on_state_calling( pjsip_transaction *tsx,
+ pjsip_event *event);
+static pj_status_t pjsip_tsx_on_state_trying( pjsip_transaction *tsx,
+ pjsip_event *event);
+static pj_status_t pjsip_tsx_on_state_proceeding_uas( pjsip_transaction *tsx,
+ pjsip_event *event);
+static pj_status_t pjsip_tsx_on_state_proceeding_uac( pjsip_transaction *tsx,
+ pjsip_event *event);
+static pj_status_t pjsip_tsx_on_state_completed_uas( pjsip_transaction *tsx,
+ pjsip_event *event);
+static pj_status_t pjsip_tsx_on_state_completed_uac( pjsip_transaction *tsx,
+ pjsip_event *event);
+static pj_status_t pjsip_tsx_on_state_confirmed(pjsip_transaction *tsx,
+ pjsip_event *event);
+static pj_status_t pjsip_tsx_on_state_terminated(pjsip_transaction *tsx,
+ pjsip_event *event);
+static pj_status_t pjsip_tsx_on_state_destroyed(pjsip_transaction *tsx,
+ pjsip_event *event);
+
+static void tsx_timer_callback( pj_timer_heap_t *theap,
+ pj_timer_entry *entry);
+static int tsx_send_msg( pjsip_transaction *tsx,
+ pjsip_tx_data *tdata);
+static void lock_tsx( pjsip_transaction *tsx, struct
+ tsx_lock_data *lck );
+static pj_status_t unlock_tsx( pjsip_transaction *tsx,
+ struct tsx_lock_data *lck );
+
+/* State handlers for UAC, indexed by state */
+static int (*tsx_state_handler_uac[PJSIP_TSX_STATE_MAX])(pjsip_transaction *,
+ pjsip_event *) =
+{
+ &pjsip_tsx_on_state_null,
+ &pjsip_tsx_on_state_calling,
+ &pjsip_tsx_on_state_trying,
+ &pjsip_tsx_on_state_proceeding_uac,
+ &pjsip_tsx_on_state_completed_uac,
+ &pjsip_tsx_on_state_confirmed,
+ &pjsip_tsx_on_state_terminated,
+ &pjsip_tsx_on_state_destroyed,
+};
+
+/* State handlers for UAS */
+static int (*tsx_state_handler_uas[PJSIP_TSX_STATE_MAX])(pjsip_transaction *,
+ pjsip_event *) =
+{
+ &pjsip_tsx_on_state_null,
+ &pjsip_tsx_on_state_calling,
+ &pjsip_tsx_on_state_trying,
+ &pjsip_tsx_on_state_proceeding_uas,
+ &pjsip_tsx_on_state_completed_uas,
+ &pjsip_tsx_on_state_confirmed,
+ &pjsip_tsx_on_state_terminated,
+ &pjsip_tsx_on_state_destroyed,
+};
+
+/*
+ * Get transaction state name.
+ */
+PJ_DEF(const char *) pjsip_tsx_state_str(pjsip_tsx_state_e state)
+{
+ return state_str[state];
+}
+
+/*
+ * Get the role name.
+ */
+PJ_DEF(const char *) pjsip_role_name(pjsip_role_e role)
+{
+ return role_name[role];
+}
+
+
+/*
+ * Create transaction key for RFC2543 compliant messages, which don't have
+ * unique branch parameter in the top most Via header.
+ *
+ * INVITE requests matches a transaction if the following attributes
+ * match the original request:
+ * - Request-URI
+ * - To tag
+ * - From tag
+ * - Call-ID
+ * - CSeq
+ * - top Via header
+ *
+ * CANCEL matching is done similarly as INVITE, except:
+ * - CSeq method will differ
+ * - To tag is not matched.
+ *
+ * ACK matching is done similarly, except that:
+ * - method of the CSeq will differ,
+ * - To tag is matched to the response sent by the server transaction.
+ *
+ * The transaction key is constructed from the common components of above
+ * components. Additional comparison is needed to fully match a transaction.
+ */
+static pj_status_t create_tsx_key_2543( pj_pool_t *pool,
+ pj_str_t *str,
+ pjsip_role_e role,
+ const pjsip_method *method,
+ const pjsip_rx_data *rdata )
+{
+#define SEPARATOR '$'
+ char *key, *p, *end;
+ int len;
+ pj_size_t len_required;
+ pjsip_uri *req_uri;
+ pj_str_t *host;
+
+ PJ_ASSERT_RETURN(pool && str && method && rdata, PJ_EINVAL);
+ PJ_ASSERT_RETURN(rdata->msg_info.msg, PJ_EINVAL);
+ PJ_ASSERT_RETURN(rdata->msg_info.via, PJSIP_EMISSINGHDR);
+ PJ_ASSERT_RETURN(rdata->msg_info.cseq, PJSIP_EMISSINGHDR);
+ PJ_ASSERT_RETURN(rdata->msg_info.from, PJSIP_EMISSINGHDR);
+
+ host = &rdata->msg_info.via->sent_by.host;
+ req_uri = (pjsip_uri*)rdata->msg_info.msg->line.req.uri;
+
+ /* Calculate length required. */
+ len_required = 9 + /* CSeq number */
+ rdata->msg_info.from->tag.slen + /* From tag. */
+ rdata->msg_info.call_id.slen + /* Call-ID */
+ host->slen + /* Via host. */
+ 9 + /* Via port. */
+ 16; /* Separator+Allowance. */
+ key = p = pj_pool_alloc(pool, len_required);
+ end = p + len_required;
+
+ /* Add role. */
+ *p++ = (char)(role==PJSIP_ROLE_UAC ? 'c' : 's');
+ *p++ = SEPARATOR;
+
+ /* Add Request-URI */
+ /* This is BUG!
+ * Response doesn't have Request-URI!
+ *
+ len = req_uri->vptr->print( PJSIP_URI_IN_REQ_URI, req_uri, p, end-p );
+ p += len;
+ *p++ = SEPARATOR;
+ */
+
+ /* Add method, except when method is INVITE or ACK. */
+ if (method->id != PJSIP_INVITE_METHOD && method->id != PJSIP_ACK_METHOD) {
+ pj_memcpy(p, method->name.ptr, method->name.slen);
+ p += method->name.slen;
+ *p++ = '$';
+ }
+
+ /* Add CSeq (only the number). */
+ len = pj_utoa(rdata->msg_info.cseq->cseq, p);
+ p += len;
+ *p++ = SEPARATOR;
+
+ /* Add From tag. */
+ len = rdata->msg_info.from->tag.slen;
+ pj_memcpy( p, rdata->msg_info.from->tag.ptr, len);
+ p += len;
+ *p++ = SEPARATOR;
+
+ /* Add Call-ID. */
+ len = rdata->msg_info.call_id.slen;
+ pj_memcpy( p, rdata->msg_info.call_id.ptr, len );
+ p += len;
+ *p++ = SEPARATOR;
+
+ /* Add top Via header.
+ * We don't really care whether the port contains the real port (because
+ * it can be omited if default port is used). Anyway this function is
+ * only used to match request retransmission, and we expect that the
+ * request retransmissions will contain the same port.
+ */
+ pj_memcpy(p, host->ptr, host->slen);
+ p += host->slen;
+ *p++ = ':';
+
+ len = pj_utoa(rdata->msg_info.via->sent_by.port, p);
+ p += len;
+ *p++ = SEPARATOR;
+
+ *p++ = '\0';
+
+ /* Done. */
+ str->ptr = key;
+ str->slen = p-key;
+
+ return PJ_SUCCESS;
+}
+
+/*
+ * Create transaction key for RFC3161 compliant system.
+ */
+static pj_status_t create_tsx_key_3261( pj_pool_t *pool,
+ pj_str_t *key,
+ pjsip_role_e role,
+ const pjsip_method *method,
+ const pj_str_t *branch)
+{
+ char *p;
+
+ PJ_ASSERT_RETURN(pool && key && method && branch, PJ_EINVAL);
+
+ p = key->ptr = pj_pool_alloc(pool, branch->slen + method->name.slen + 4 );
+
+ /* Add role. */
+ *p++ = (char)(role==PJSIP_ROLE_UAC ? 'c' : 's');
+ *p++ = SEPARATOR;
+
+ /* Add method, except when method is INVITE or ACK. */
+ if (method->id != PJSIP_INVITE_METHOD && method->id != PJSIP_ACK_METHOD) {
+ pj_memcpy(p, method->name.ptr, method->name.slen);
+ p += method->name.slen;
+ *p++ = '$';
+ }
+
+ /* Add branch ID. */
+ pj_memcpy(p, branch->ptr, branch->slen);
+ p += branch->slen;
+
+ /* Set length */
+ key->slen = p - key->ptr;
+
+ return PJ_SUCCESS;
+}
+
+/*
+ * Create key from the incoming data, to be used to search the transaction
+ * in the transaction hash table.
+ */
+PJ_DEF(pj_status_t) pjsip_tsx_create_key( pj_pool_t *pool, pj_str_t *key,
+ pjsip_role_e role,
+ const pjsip_method *method,
+ const pjsip_rx_data *rdata)
+{
+ pj_str_t rfc3261_branch = {PJSIP_RFC3261_BRANCH_ID,
+ PJSIP_RFC3261_BRANCH_LEN};
+
+
+ /* Get the branch parameter in the top-most Via.
+ * If branch parameter is started with "z9hG4bK", then the message was
+ * generated by agent compliant with RFC3261. Otherwise, it will be
+ * handled as RFC2543.
+ */
+ const pj_str_t *branch = &rdata->msg_info.via->branch_param;
+
+ if (pj_strncmp(branch,&rfc3261_branch,PJSIP_RFC3261_BRANCH_LEN)==0) {
+
+ /* Create transaction key. */
+ return create_tsx_key_3261(pool, key, role, method, branch);
+
+ } else {
+ /* Create the key for the message. This key will be matched up
+ * with the transaction key. For RFC2563 transactions, the
+ * transaction key was created by the same function, so it will
+ * match the message.
+ */
+ return create_tsx_key_2543( pool, key, role, method, rdata );
+ }
+}
+
+
+/*
+ * Create new transaction.
+ */
+PJ_DEF(pj_status_t) pjsip_tsx_create( pj_pool_t *pool,
+ pjsip_endpoint *endpt,
+ pjsip_transaction **p_tsx)
+{
+ pjsip_transaction *tsx;
+ pj_status_t status;
+
+ tsx = pj_pool_calloc(pool, 1, sizeof(pjsip_transaction));
+
+ tsx->pool = pool;
+ tsx->endpt = endpt;
+ tsx->retransmit_timer.id = TSX_TIMER_RETRANSMISSION;
+ tsx->retransmit_timer._timer_id = -1;
+ tsx->retransmit_timer.user_data = tsx;
+ tsx->retransmit_timer.cb = &tsx_timer_callback;
+ tsx->timeout_timer.id = TSX_TIMER_TIMEOUT;
+ tsx->timeout_timer._timer_id = -1;
+ tsx->timeout_timer.user_data = tsx;
+ tsx->timeout_timer.cb = &tsx_timer_callback;
+ pj_sprintf(tsx->obj_name, "tsx%p", tsx);
+ status = pj_mutex_create_recursive(pool, "mtsx%p", &tsx->mutex);
+ if (status != PJ_SUCCESS) {
+ return status;
+ }
+
+ *p_tsx = tsx;
+ return PJ_SUCCESS;
+}
+
+/*
+ * Lock transaction and set the value of Thread Local Storage.
+ */
+static void lock_tsx(pjsip_transaction *tsx, struct tsx_lock_data *lck)
+{
+ struct tsx_lock_data *prev_data;
+
+ pj_mutex_lock(tsx->mutex);
+ prev_data = (struct tsx_lock_data *)
+ pj_thread_local_get(pjsip_tsx_lock_tls_id);
+ lck->prev = prev_data;
+ lck->tsx = tsx;
+ lck->is_alive = 1;
+ pj_thread_local_set(pjsip_tsx_lock_tls_id, lck);
+}
+
+
+/*
+ * Unlock transaction.
+ * This will selectively unlock the mutex ONLY IF the transaction has not been
+ * destroyed. The function knows whether the transaction has been destroyed
+ * because when transaction is destroyed the is_alive flag for the transaction
+ * will be set to zero.
+ */
+static pj_status_t unlock_tsx( pjsip_transaction *tsx,
+ struct tsx_lock_data *lck)
+{
+ pj_assert( (void*)pj_thread_local_get(pjsip_tsx_lock_tls_id) == lck);
+ pj_assert( lck->tsx == tsx );
+ pj_thread_local_set(pjsip_tsx_lock_tls_id, lck->prev);
+ if (lck->is_alive)
+ pj_mutex_unlock(tsx->mutex);
+
+ return lck->is_alive ? PJ_SUCCESS : PJSIP_ETSXDESTROYED;
+}
+
+/*
+ * Set transaction state, and inform TU about the transaction state change.
+ */
+static void tsx_set_state( pjsip_transaction *tsx,
+ pjsip_tsx_state_e state,
+ pjsip_event_id_e event_src_type,
+ void *event_src )
+{
+ pjsip_event e;
+
+ PJ_LOG(4, (tsx->obj_name, "STATE %s-->%s, cause = %s",
+ state_str[tsx->state], state_str[state],
+ pjsip_event_str(event_src_type)));
+
+ /* Change state. */
+ tsx->state = state;
+
+ /* Update the state handlers. */
+ if (tsx->role == PJSIP_ROLE_UAC) {
+ tsx->state_handler = tsx_state_handler_uac[state];
+ } else {
+ tsx->state_handler = tsx_state_handler_uas[state];
+ }
+
+ /* Inform TU */
+ PJSIP_EVENT_INIT_TSX_STATE(e, tsx, event_src_type, event_src);
+ pjsip_endpt_send_tsx_event( tsx->endpt, &e );
+
+ /* When the transaction is terminated, release transport, and free the
+ * saved last transmitted message.
+ */
+ if (state == PJSIP_TSX_STATE_TERMINATED) {
+
+ /* Decrement transport reference counter. */
+ if (tsx->transport &&
+ tsx->transport_state == PJSIP_TSX_TRANSPORT_STATE_FINAL)
+ {
+ pjsip_transport_dec_ref( tsx->transport );
+ tsx->transport = NULL;
+ }
+ /* Free last transmitted message. */
+ if (tsx->last_tx) {
+ pjsip_tx_data_dec_ref( tsx->last_tx );
+ tsx->last_tx = NULL;
+ }
+ /* Cancel timeout timer. */
+ if (tsx->timeout_timer._timer_id != -1) {
+ pjsip_endpt_cancel_timer(tsx->endpt, &tsx->timeout_timer);
+ tsx->timeout_timer._timer_id = -1;
+ }
+ /* Cancel retransmission timer. */
+ if (tsx->retransmit_timer._timer_id != -1) {
+ pjsip_endpt_cancel_timer(tsx->endpt, &tsx->retransmit_timer);
+ tsx->retransmit_timer._timer_id = -1;
+ }
+
+ /* If transport is not pending, reschedule timeout timer to
+ * destroy this transaction.
+ */
+ if (tsx->transport_state == PJSIP_TSX_TRANSPORT_STATE_FINAL) {
+ pj_time_val timeout = {0, 0};
+ pjsip_endpt_schedule_timer( tsx->endpt, &tsx->timeout_timer,
+ &timeout);
+ }
+
+ } else if (state == PJSIP_TSX_STATE_DESTROYED) {
+
+ /* Clear TLS, so that mutex will not be unlocked */
+ struct tsx_lock_data *lck = pj_thread_local_get(pjsip_tsx_lock_tls_id);
+ while (lck) {
+ if (lck->tsx == tsx) {
+ lck->is_alive = 0;
+ }
+ lck = lck->prev;
+ }
+ }
+}
+
+/*
+ * Look-up destination address and select which transport to be used to send
+ * the request message. The procedure used here follows the guidelines on
+ * sending the request in RFC3261 chapter 8.1.2.
+ *
+ * This function also modifies the message (request line and Route headers)
+ * accordingly.
+ */
+static pj_status_t tsx_process_route( pjsip_transaction *tsx,
+ pjsip_tx_data *tdata,
+ pjsip_host_port *send_addr )
+{
+ const pjsip_uri *new_request_uri, *target_uri;
+ const pjsip_name_addr *topmost_route_uri;
+ pjsip_route_hdr *first_route_hdr, *last_route_hdr;
+
+ pj_assert(tdata->msg->type == PJSIP_REQUEST_MSG);
+
+ /* Get the first "Route" header from the message. If the message doesn't
+ * have any "Route" headers but the endpoint has, then copy the "Route"
+ * headers from the endpoint first.
+ */
+ last_route_hdr = first_route_hdr =
+ pjsip_msg_find_hdr(tdata->msg, PJSIP_H_ROUTE, NULL);
+ if (first_route_hdr) {
+ topmost_route_uri = &first_route_hdr->name_addr;
+ while (last_route_hdr->next != (void*)&tdata->msg->hdr) {
+ pjsip_route_hdr *hdr;
+ hdr = pjsip_msg_find_hdr(tdata->msg, PJSIP_H_ROUTE,
+ last_route_hdr->next);
+ if (!hdr)
+ break;
+ last_route_hdr = hdr;
+ }
+ } else {
+ const pjsip_route_hdr *hdr_list;
+ hdr_list = (pjsip_route_hdr*)pjsip_endpt_get_routing(tsx->endpt);
+ if (hdr_list->next != hdr_list) {
+ const pjsip_route_hdr *hdr = (pjsip_route_hdr*)hdr_list->next;
+ first_route_hdr = NULL;
+ topmost_route_uri = &hdr->name_addr;
+ do {
+ last_route_hdr = pjsip_hdr_shallow_clone(tdata->pool, hdr);
+ if (first_route_hdr == NULL)
+ first_route_hdr = last_route_hdr;
+ pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr*)last_route_hdr);
+ hdr = hdr->next;
+ } while (hdr != hdr_list);
+ } else {
+ topmost_route_uri = NULL;
+ }
+ }
+
+ /* If Route headers exist, and the first element indicates loose-route,
+ * the URI is taken from the Request-URI, and we keep all existing Route
+ * headers intact.
+ * If Route headers exist, and the first element DOESN'T indicate loose
+ * route, the URI is taken from the first Route header, and remove the
+ * first Route header from the message.
+ * Otherwise if there's no Route headers, the URI is taken from the
+ * Request-URI.
+ */
+ if (topmost_route_uri) {
+ pj_bool_t has_lr_param;
+
+ if (PJSIP_URI_SCHEME_IS_SIP(topmost_route_uri) ||
+ PJSIP_URI_SCHEME_IS_SIPS(topmost_route_uri))
+ {
+ const pjsip_url *url = pjsip_uri_get_uri((void*)topmost_route_uri);
+ has_lr_param = url->lr_param;
+ } else {
+ has_lr_param = 0;
+ }
+
+ if (has_lr_param) {
+ new_request_uri = tdata->msg->line.req.uri;
+ /* We shouldn't need to delete topmost Route if it has lr param.
+ * But seems like it breaks some proxy implementation, so we
+ * delete it anyway.
+ */
+ /*
+ pj_list_erase(first_route_hdr);
+ if (first_route_hdr == last_route_hdr)
+ last_route_hdr = NULL;
+ */
+ } else {
+ new_request_uri = pjsip_uri_get_uri((void*)topmost_route_uri);
+ pj_list_erase(first_route_hdr);
+ if (first_route_hdr == last_route_hdr)
+ last_route_hdr = NULL;
+ }
+
+ target_uri = (pjsip_uri*)topmost_route_uri;
+
+ } else {
+ target_uri = new_request_uri = tdata->msg->line.req.uri;
+ }
+
+ /* The target URI must be a SIP/SIPS URL so we can resolve it's address.
+ * Otherwise we're in trouble (i.e. there's no host part in tel: URL).
+ */
+ pj_memset(send_addr, 0, sizeof(*send_addr));
+
+ if (PJSIP_URI_SCHEME_IS_SIPS(target_uri)) {
+ pjsip_uri *uri = (pjsip_uri*) target_uri;
+ const pjsip_url *url = (const pjsip_url*)pjsip_uri_get_uri(uri);
+ send_addr->flag |= (PJSIP_TRANSPORT_SECURE | PJSIP_TRANSPORT_RELIABLE);
+ pj_strdup(tdata->pool, &send_addr->host, &url->host);
+ send_addr->port = url->port;
+ send_addr->type =
+ pjsip_transport_get_type_from_name(&url->transport_param);
+
+ } else if (PJSIP_URI_SCHEME_IS_SIP(target_uri)) {
+ pjsip_uri *uri = (pjsip_uri*) target_uri;
+ const pjsip_url *url = (const pjsip_url*)pjsip_uri_get_uri(uri);
+ pj_strdup(tdata->pool, &send_addr->host, &url->host);
+ send_addr->port = url->port;
+ send_addr->type =
+ pjsip_transport_get_type_from_name(&url->transport_param);
+#if PJ_HAS_TCP
+ if (send_addr->type == PJSIP_TRANSPORT_TCP ||
+ send_addr->type == PJSIP_TRANSPORT_SCTP)
+ {
+ send_addr->flag |= PJSIP_TRANSPORT_RELIABLE;
+ }
+#endif
+ } else {
+ pj_assert(!"Unsupported URI scheme!");
+ return PJSIP_EINVALIDSCHEME;
+ }
+
+ /* If target URI is different than request URI, replace
+ * request URI add put the original URI in the last Route header.
+ */
+ if (new_request_uri && new_request_uri!=tdata->msg->line.req.uri) {
+ pjsip_route_hdr *route = pjsip_route_hdr_create(tdata->pool);
+ route->name_addr.uri = tdata->msg->line.req.uri;
+ if (last_route_hdr)
+ pj_list_insert_after(last_route_hdr, route);
+ else
+ pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr*)route);
+ tdata->msg->line.req.uri = (pjsip_uri*)new_request_uri;
+ }
+
+ /* Success. */
+ return PJ_SUCCESS;
+}
+
+
+/*
+ * Callback from the transport job.
+ * This callback is called when asychronous transport connect() operation
+ * has completed, with or without error.
+ */
+static void tsx_transport_callback(pjsip_transport *tr,
+ void *token,
+ pj_status_t status)
+{
+ char addr[PJ_MAX_HOSTNAME];
+ pjsip_transaction *tsx = token;
+ struct tsx_lock_data lck;
+
+ pj_memcpy(addr, tsx->dest_name.host.ptr, tsx->dest_name.host.slen);
+ addr[tsx->dest_name.host.slen] = '\0';
+
+
+ if (status == PJ_SUCCESS) {
+ PJ_LOG(4, (tsx->obj_name, "%s connected to %s:%d",
+ tr->type_name,
+ addr, tsx->dest_name.port));
+ } else {
+ PJ_LOG(4, (tsx->obj_name, "%s unable to connect to %s:%d, status=%d",
+ tr->type_name,
+ addr, tsx->dest_name.port, status));
+ }
+
+ /* Lock transaction. */
+ lock_tsx(tsx, &lck);
+
+ if (status != PJ_SUCCESS) {
+ tsx->transport_state = PJSIP_TSX_TRANSPORT_STATE_FINAL;
+ tsx->status_code = PJSIP_SC_TSX_TRANSPORT_ERROR;
+
+ tsx_set_state(tsx, PJSIP_TSX_STATE_TERMINATED,
+ PJSIP_EVENT_TRANSPORT_ERROR, (void*)status);
+
+ /* Unlock transaction. */
+ unlock_tsx(tsx, &lck);
+ return;
+ }
+
+ /* See if transaction has already been terminated.
+ * If so, schedule to destroy the transaction.
+ */
+ if (tsx->state == PJSIP_TSX_STATE_TERMINATED) {
+ pj_time_val timeout = {0, 0};
+ pjsip_endpt_schedule_timer( tsx->endpt, &tsx->timeout_timer,
+ &timeout);
+
+ /* Unlock transaction. */
+ unlock_tsx(tsx, &lck);
+ return;
+ }
+
+ /* Add reference counter to the transport. */
+ pjsip_transport_add_ref(tr);
+
+ /* Mark transport as ready. */
+ tsx->transport_state = PJSIP_TSX_TRANSPORT_STATE_FINAL;
+ tsx->transport = tr;
+
+ /* If there's a pending message to send, send it now. */
+ if (tsx->has_unsent_msg) {
+ tsx_send_msg( tsx, tsx->last_tx );
+ }
+
+ /* Unlock transaction. */
+ unlock_tsx(tsx, &lck);
+}
+
+/*
+ * Callback from the resolver job.
+ */
+static void tsx_resolver_callback(pj_status_t status,
+ void *token,
+ const struct pjsip_server_addresses *addr)
+{
+ pjsip_transaction *tsx = token;
+ struct tsx_lock_data lck;
+ pjsip_transport *tp;
+
+ PJ_LOG(4, (tsx->obj_name, "resolver job complete, status=%d", status));
+
+ if (status != PJ_SUCCESS || addr->count == 0) {
+ lock_tsx(tsx, &lck);
+ tsx->status_code = PJSIP_SC_TSX_RESOLVE_ERROR;
+ tsx_set_state(tsx, PJSIP_TSX_STATE_TERMINATED,
+ PJSIP_EVENT_TRANSPORT_ERROR, (void*)status);
+ unlock_tsx(tsx, &lck);
+ return;
+ }
+
+ /* Lock transaction. */
+ lock_tsx(tsx, &lck);
+
+ /* Copy server addresses. */
+ pj_memcpy(&tsx->remote_addr, addr, sizeof(*addr));
+
+ /* Create/find the transport for the remote address. */
+ PJ_LOG(5,(tsx->obj_name, "tsx getting transport for %s:%d",
+ pj_inet_ntoa(addr->entry[0].addr.sin_addr),
+ pj_ntohs(addr->entry[0].addr.sin_port)));
+
+ tsx->transport_state = PJSIP_TSX_TRANSPORT_STATE_CONNECTING;
+ status = pjsip_endpt_alloc_transport( tsx->endpt, addr->entry[0].type,
+ &addr->entry[0].addr,
+ &tp);
+ tsx_transport_callback(tp, tsx, status);
+
+ /* Unlock transaction */
+ unlock_tsx(tsx, &lck);
+
+ /* There should be nothing to do after this point.
+ * Execution for the transaction will resume when the callback for the
+ * transport is called.
+ */
+}
+
+/*
+ * Initialize the transaction as UAC transaction.
+ */
+PJ_DEF(pj_status_t) pjsip_tsx_init_uac( pjsip_transaction *tsx,
+ pjsip_tx_data *tdata)
+{
+ pjsip_msg *msg;
+ pjsip_cseq_hdr *cseq;
+ pjsip_via_hdr *via;
+ pj_status_t status;
+ struct tsx_lock_data lck;
+
+ PJ_LOG(4,(tsx->obj_name, "initializing tsx as UAC (tdata=%p)", tdata));
+
+ /* Lock transaction. */
+ lock_tsx(tsx, &lck);
+
+ /* Keep shortcut */
+ msg = tdata->msg;
+
+ /* Role is UAC. */
+ tsx->role = PJSIP_ROLE_UAC;
+
+ /* Save method. */
+ pjsip_method_copy( tsx->pool, &tsx->method, &msg->line.req.method);
+
+ /* Generate Via header if it doesn't exist. */
+ via = pjsip_msg_find_hdr(msg, PJSIP_H_VIA, NULL);
+ if (via == NULL) {
+ via = pjsip_via_hdr_create(tdata->pool);
+ pjsip_msg_insert_first_hdr(msg, (pjsip_hdr*) via);
+ }
+
+ if (via->branch_param.slen == 0) {
+ pj_str_t tmp;
+ via->branch_param.ptr = pj_pool_alloc(tsx->pool, PJSIP_MAX_BRANCH_LEN);
+ via->branch_param.slen = PJSIP_MAX_BRANCH_LEN;
+ pj_memcpy(via->branch_param.ptr, PJSIP_RFC3261_BRANCH_ID,
+ PJSIP_RFC3261_BRANCH_LEN);
+
+ tmp.ptr = via->branch_param.ptr + PJSIP_RFC3261_BRANCH_LEN;
+ pj_generate_unique_string( &tmp );
+
+ /* Save branch parameter. */
+ tsx->branch = via->branch_param;
+ } else {
+ /* Copy branch parameter. */
+ pj_strdup(tsx->pool, &tsx->branch, &via->branch_param);
+ }
+
+
+ /* Generate transaction key. */
+ status = create_tsx_key_3261( tsx->pool, &tsx->transaction_key,
+ PJSIP_ROLE_UAC, &tsx->method,
+ &via->branch_param);
+ if (status != PJ_SUCCESS) {
+ unlock_tsx(tsx, &lck);
+ return status;
+ }
+
+ PJ_LOG(6, (tsx->obj_name, "tsx_key=%.*s", tsx->transaction_key.slen,
+ tsx->transaction_key.ptr));
+
+ /* Save CSeq. */
+ cseq = pjsip_msg_find_hdr(msg, PJSIP_H_CSEQ, NULL);
+ if (!cseq) {
+ pj_assert(!"CSeq header not present in outgoing message!");
+ unlock_tsx(tsx, &lck);
+ return PJSIP_EMISSINGHDR;
+ }
+ tsx->cseq = cseq->cseq;
+
+
+ /* Begin with State_Null.
+ * Manually set-up the state becase we don't want to call the callback.
+ */
+ tsx->state = PJSIP_TSX_STATE_NULL;
+ tsx->state_handler = &pjsip_tsx_on_state_null;
+
+ /* Get destination name from the message. */
+ status = tsx_process_route(tsx, tdata, &tsx->dest_name);
+ if (status != PJ_SUCCESS) {
+ tsx->transport_state = PJSIP_TSX_TRANSPORT_STATE_FINAL;
+ tsx->status_code = PJSIP_SC_TSX_TRANSPORT_ERROR;
+ tsx_set_state(tsx, PJSIP_TSX_STATE_TERMINATED,
+ PJSIP_EVENT_TRANSPORT_ERROR, (void*)status);
+ unlock_tsx(tsx, &lck);
+ return status;
+ }
+
+ /* Resolve destination.
+ * This will start asynchronous resolver job, and when it finishes,
+ * the callback will be called.
+ */
+ PJ_LOG(5,(tsx->obj_name, "tsx resolving destination %.*s:%d",
+ tsx->dest_name.host.slen,
+ tsx->dest_name.host.ptr,
+ tsx->dest_name.port));
+
+ tsx->transport_state = PJSIP_TSX_TRANSPORT_STATE_RESOLVING;
+ pjsip_endpt_resolve( tsx->endpt, tsx->pool, &tsx->dest_name,
+ tsx, &tsx_resolver_callback);
+
+ /* There should be nothing to do after this point.
+ * Execution for the transaction will resume when the resolver callback is
+ * called.
+ */
+
+ /* Unlock transaction and return.
+ * If transaction has been destroyed WITHIN the current thread, the
+ * unlock_tsx() function will return -1.
+ */
+ return unlock_tsx(tsx, &lck);
+}
+
+
+/*
+ * Initialize the transaction as UAS transaction.
+ */
+PJ_DEF(pj_status_t) pjsip_tsx_init_uas( pjsip_transaction *tsx,
+ pjsip_rx_data *rdata)
+{
+ pjsip_msg *msg = rdata->msg_info.msg;
+ pj_str_t *branch;
+ pjsip_cseq_hdr *cseq;
+ pj_status_t status;
+ struct tsx_lock_data lck;
+
+ PJ_LOG(4,(tsx->obj_name, "initializing tsx as UAS (rdata=%p)", rdata));
+
+ /* Lock transaction. */
+ lock_tsx(tsx, &lck);
+
+ /* Keep shortcut to message */
+ msg = rdata->msg_info.msg;
+
+ /* Role is UAS */
+ tsx->role = PJSIP_ROLE_UAS;
+
+ /* Save method. */
+ pjsip_method_copy( tsx->pool, &tsx->method, &msg->line.req.method);
+
+ /* Get transaction key either from branch for RFC3261 message, or
+ * create transaction key.
+ */
+ status = pjsip_tsx_create_key(tsx->pool, &tsx->transaction_key,
+ PJSIP_ROLE_UAS, &tsx->method, rdata);
+ if (status != PJ_SUCCESS) {
+ unlock_tsx(tsx, &lck);
+ return status;
+ }
+
+ /* Duplicate branch parameter for transaction. */
+ branch = &rdata->msg_info.via->branch_param;
+ pj_strdup(tsx->pool, &tsx->branch, branch);
+
+ PJ_LOG(6, (tsx->obj_name, "tsx_key=%.*s", tsx->transaction_key.slen,
+ tsx->transaction_key.ptr));
+
+ /* Save CSeq */
+ cseq = rdata->msg_info.cseq;
+ tsx->cseq = cseq->cseq;
+
+ /* Begin with state NULL
+ * Manually set-up the state becase we don't want to call the callback.
+ */
+ tsx->state = PJSIP_TSX_STATE_NULL;
+ tsx->state_handler = &pjsip_tsx_on_state_null;
+
+ /* Get the transport to send the response.
+ * According to section 18.2.2 of RFC3261, if the transport is reliable
+ * then the response must be sent using that transport.
+ */
+ /* In addition, RFC 3581 says, if Via has "rport" parameter specified,
+ * then return the response using the same transport.
+ */
+ if (PJSIP_TRANSPORT_IS_RELIABLE(rdata->tp_info.transport) ||
+ rdata->msg_info.via->rport_param >= 0)
+ {
+ tsx->transport = rdata->tp_info.transport;
+ pjsip_transport_add_ref(tsx->transport);
+ tsx->transport_state = PJSIP_TSX_TRANSPORT_STATE_FINAL;
+
+ tsx->current_addr = 0;
+ tsx->remote_addr.count = 1;
+ tsx->remote_addr.entry[0].type = tsx->transport->type;
+ pj_memcpy(&tsx->remote_addr.entry[0].addr,
+ &rdata->pkt_info.addr, rdata->pkt_info.addr_len);
+
+ } else {
+ pj_status_t status;
+
+ status = pjsip_get_response_addr(tsx->pool, rdata->tp_info.transport,
+ rdata->msg_info.via, &tsx->dest_name);
+ if (status != PJ_SUCCESS) {
+ tsx->transport_state = PJSIP_TSX_TRANSPORT_STATE_FINAL;
+ tsx->status_code = PJSIP_SC_TSX_TRANSPORT_ERROR;
+ tsx_set_state(tsx, PJSIP_TSX_STATE_TERMINATED,
+ PJSIP_EVENT_TRANSPORT_ERROR, (void*)status);
+ unlock_tsx(tsx, &lck);
+ return status;
+ }
+
+ /* Resolve destination.
+ * This will start asynchronous resolver job, and when it finishes,
+ * the callback will be called.
+ */
+ PJ_LOG(5,(tsx->obj_name, "tsx resolving destination %.*s:%d",
+ tsx->dest_name.host.slen,
+ tsx->dest_name.host.ptr,
+ tsx->dest_name.port));
+
+ tsx->transport_state = PJSIP_TSX_TRANSPORT_STATE_RESOLVING;
+ pjsip_endpt_resolve( tsx->endpt, tsx->pool, &tsx->dest_name,
+ tsx, &tsx_resolver_callback);
+ }
+
+ /* There should be nothing to do after this point.
+ * Execution for the transaction will resume when the resolver callback is
+ * called.
+ */
+
+ /* Unlock transaction and return.
+ * If transaction has been destroyed WITHIN the current thread, the
+ * unlock_tsx() function will return -1.
+ */
+ return unlock_tsx(tsx, &lck);
+}
+
+/*
+ * Callback when timer expires.
+ */
+static void tsx_timer_callback( pj_timer_heap_t *theap, pj_timer_entry *entry)
+{
+ pjsip_event event;
+ pjsip_transaction *tsx = entry->user_data;
+ struct tsx_lock_data lck;
+
+ PJ_UNUSED_ARG(theap);
+
+ PJ_LOG(5,(tsx->obj_name, "got timer event (%s timer)",
+ (entry->id==TSX_TIMER_RETRANSMISSION ? "Retransmit" : "Timeout")));
+
+
+ if (entry->id == TSX_TIMER_RETRANSMISSION) {
+ PJSIP_EVENT_INIT_TIMER(event, &tsx->retransmit_timer);
+ } else {
+ PJSIP_EVENT_INIT_TIMER(event, &tsx->timeout_timer);
+ }
+
+ /* Dispatch event to transaction. */
+ lock_tsx(tsx, &lck);
+ (*tsx->state_handler)(tsx, &event);
+ unlock_tsx(tsx, &lck);
+}
+
+/*
+ * Transmit ACK message for 2xx/INVITE with this transaction. The ACK for
+ * non-2xx/INVITE is automatically sent by the transaction.
+ * This operation is only valid if the transaction is configured to handle ACK
+ * (tsx->handle_ack is non-zero). If this attribute is not set, then the
+ * transaction will comply with RFC-3261, i.e. it will set itself to
+ * TERMINATED state when it receives 2xx/INVITE.
+ */
+PJ_DEF(void) pjsip_tsx_on_tx_ack( pjsip_transaction *tsx, pjsip_tx_data *tdata)
+{
+ pjsip_msg *msg;
+ pjsip_host_port dest_addr;
+ pjsip_via_hdr *via;
+ struct tsx_lock_data lck;
+ pj_status_t status = PJ_SUCCESS;
+
+ /* Lock tsx. */
+ lock_tsx(tsx, &lck);
+
+ pj_assert(tsx->handle_ack != 0);
+
+ msg = tdata->msg;
+
+ /* Generate branch parameter if it doesn't exist. */
+ via = pjsip_msg_find_hdr(msg, PJSIP_H_VIA, NULL);
+ if (via == NULL) {
+ via = pjsip_via_hdr_create(tdata->pool);
+ pjsip_msg_add_hdr(msg, (pjsip_hdr*) via);
+ }
+
+ if (via->branch_param.slen == 0) {
+ via->branch_param = tsx->branch;
+ } else {
+ pj_assert( pj_strcmp(&via->branch_param, &tsx->branch) == 0 );
+ }
+
+ /* Get destination name from the message. */
+ status = tsx_process_route(tsx, tdata, &dest_addr);
+ if (status != 0){
+ goto on_error;
+ }
+
+ /* Compare message's destination name with transaction's destination name.
+ * If NOT equal, then we'll have to resolve the destination.
+ */
+ if (dest_addr.type == tsx->dest_name.type &&
+ dest_addr.flag == tsx->dest_name.flag &&
+ dest_addr.port == tsx->dest_name.port &&
+ pj_stricmp(&dest_addr.host, &tsx->dest_name.host) == 0)
+ {
+ /* Equal destination. We can use current transport. */
+ pjsip_tsx_on_tx_msg(tsx, tdata);
+ unlock_tsx(tsx, &lck);
+ return;
+
+ }
+
+ /* New destination; we'll have to resolve host and create new transport. */
+ pj_memcpy(&tsx->dest_name, &dest_addr, sizeof(dest_addr));
+ pj_strdup(tsx->pool, &tsx->dest_name.host, &dest_addr.host);
+
+ PJ_LOG(5,(tsx->obj_name, "tsx resolving destination %.*s:%d",
+ tsx->dest_name.host.slen,
+ tsx->dest_name.host.ptr,
+ tsx->dest_name.port));
+
+ tsx->transport_state = PJSIP_TSX_TRANSPORT_STATE_RESOLVING;
+ pjsip_transport_dec_ref(tsx->transport);
+ tsx->transport = NULL;
+
+ /* Put the message in queue. */
+ pjsip_tsx_on_tx_msg(tsx, tdata);
+
+ /* This is a bug!
+ * We shouldn't change transaction's state before actually sending the
+ * message. Otherwise transaction will terminate before message is sent,
+ * and timeout timer will be scheduled.
+ */
+ PJ_TODO(TSX_DONT_CHANGE_STATE_BEFORE_SENDING_ACK)
+
+ /*
+ * This will start asynchronous resolver job, and when it finishes,
+ * the callback will be called.
+ */
+
+ tsx->transport_state = PJSIP_TSX_TRANSPORT_STATE_RESOLVING;
+ pjsip_endpt_resolve( tsx->endpt, tsx->pool, &tsx->dest_name,
+ tsx, &tsx_resolver_callback);
+
+ unlock_tsx(tsx, &lck);
+
+ /* There should be nothing to do after this point.
+ * Execution for the transaction will resume when the resolver callback is
+ * called.
+ */
+ return;
+
+on_error:
+ /* Failure condition.
+ * Send TERMINATED event.
+ */
+ tsx->status_code = PJSIP_SC_TSX_TRANSPORT_ERROR;
+
+ tsx_set_state( tsx, PJSIP_TSX_STATE_TERMINATED,
+ PJSIP_EVENT_TRANSPORT_ERROR, (void*)status);
+
+ unlock_tsx(tsx, &lck);
+}
+
+
+/*
+ * This function is called by TU to send a message.
+ */
+PJ_DEF(void) pjsip_tsx_on_tx_msg( pjsip_transaction *tsx,
+ pjsip_tx_data *tdata )
+{
+ pjsip_event event;
+ struct tsx_lock_data lck;
+ pj_status_t status;
+
+ PJ_LOG(5,(tsx->obj_name, "Request to transmit msg on state %s (tdata=%p)",
+ state_str[tsx->state], tdata));
+
+ PJSIP_EVENT_INIT_TX_MSG(event, tsx, tdata);
+
+ /* Dispatch to transaction. */
+ lock_tsx(tsx, &lck);
+ status = (*tsx->state_handler)(tsx, &event);
+ unlock_tsx(tsx, &lck);
+}
+
+/*
+ * This function is called by endpoint when incoming message for the
+ * transaction is received.
+ */
+PJ_DEF(void) pjsip_tsx_on_rx_msg( pjsip_transaction *tsx,
+ pjsip_rx_data *rdata)
+{
+ pjsip_event event;
+ struct tsx_lock_data lck;
+ pj_status_t status;
+
+ PJ_LOG(5,(tsx->obj_name, "Incoming msg on state %s (rdata=%p)",
+ state_str[tsx->state], rdata));
+
+ PJSIP_EVENT_INIT_RX_MSG(event, tsx, rdata);
+
+ /* Dispatch to transaction. */
+ lock_tsx(tsx, &lck);
+ status = (*tsx->state_handler)(tsx, &event);
+ unlock_tsx(tsx, &lck);
+}
+
+/*
+ * Forcely terminate transaction.
+ */
+PJ_DEF(void) pjsip_tsx_terminate( pjsip_transaction *tsx, int code )
+{
+ struct tsx_lock_data lck;
+
+ lock_tsx(tsx, &lck);
+ tsx->status_code = code;
+ tsx_set_state( tsx, PJSIP_TSX_STATE_TERMINATED,
+ PJSIP_EVENT_USER, NULL);
+
+ unlock_tsx(tsx, &lck);
+}
+
+/*
+ * Transport send completion callback.
+ */
+static void tsx_on_send_complete(void *token, pjsip_tx_data *tdata,
+ pj_ssize_t bytes_sent)
+{
+ PJ_UNUSED_ARG(token);
+ PJ_UNUSED_ARG(tdata);
+
+ if (bytes_sent <= 0) {
+ PJ_TODO(HANDLE_TRANSPORT_ERROR);
+ }
+}
+
+/*
+ * Send message to the transport.
+ * If transport is not yet available, then do nothing. The message will be
+ * transmitted when transport connection completion callback is called.
+ */
+static pj_status_t tsx_send_msg( pjsip_transaction *tsx,
+ pjsip_tx_data *tdata)
+{
+ pj_status_t status = PJ_SUCCESS;
+
+ PJ_LOG(5,(tsx->obj_name, "sending msg (tdata=%p)", tdata));
+
+ if (tsx->transport_state == PJSIP_TSX_TRANSPORT_STATE_FINAL) {
+ pjsip_event before_tx_event;
+
+ pj_assert(tsx->transport != NULL);
+
+ /* Make sure Via transport info is filled up properly for
+ * requests.
+ */
+ if (tdata->msg->type == PJSIP_REQUEST_MSG) {
+ const pj_sockaddr_in *addr_name;
+ pjsip_via_hdr *via = (pjsip_via_hdr*)
+ pjsip_msg_find_hdr(tdata->msg, PJSIP_H_VIA, NULL);
+
+ /* For request message, set "rport" parameter by default. */
+ if (tdata->msg->type == PJSIP_REQUEST_MSG)
+ via->rport_param = 0;
+
+ /* Don't update Via sent-by on retransmission. */
+ if (via->sent_by.host.slen == 0) {
+ addr_name = &tsx->transport->public_addr;
+ pj_strdup2(tdata->pool, &via->transport,
+ tsx->transport->type_name);
+ pj_strdup2(tdata->pool, &via->sent_by.host,
+ pj_inet_ntoa(addr_name->sin_addr));
+ via->sent_by.port = pj_ntohs(addr_name->sin_port);
+ }
+ }
+
+ /* Notify everybody we're about to send message. */
+ PJSIP_EVENT_INIT_PRE_TX_MSG(before_tx_event, tsx, tdata,
+ tsx->retransmit_count);
+ pjsip_endpt_send_tsx_event( tsx->endpt, &before_tx_event );
+
+ tsx->has_unsent_msg = 0;
+ status = pjsip_transport_send(tsx->transport, tdata,
+ &tsx->remote_addr.entry[tsx->current_addr].addr,
+ tsx, &tsx_on_send_complete);
+ if (status != PJ_SUCCESS && status != PJ_EPENDING) {
+ PJ_TODO(HANDLE_TRANSPORT_ERROR);
+ goto on_error;
+ }
+ } else {
+ tsx->has_unsent_msg = 1;
+ }
+
+ return 0;
+
+on_error:
+ tsx->status_code = PJSIP_SC_TSX_TRANSPORT_ERROR;
+ tsx_set_state( tsx, PJSIP_TSX_STATE_TERMINATED,
+ PJSIP_EVENT_TRANSPORT_ERROR, (void*)status);
+ return status;
+}
+
+/*
+ * Retransmit last message sent.
+ */
+static pj_status_t pjsip_tsx_retransmit( pjsip_transaction *tsx,
+ int should_restart_timer)
+{
+ pj_status_t status;
+
+ PJ_LOG(4,(tsx->obj_name, "retransmiting (tdata=%p, count=%d, restart?=%d)",
+ tsx->last_tx, tsx->retransmit_count, should_restart_timer));
+
+ pj_assert(tsx->last_tx != NULL);
+
+ ++tsx->retransmit_count;
+
+ status = tsx_send_msg( tsx, tsx->last_tx);
+ if (status != PJ_SUCCESS) {
+ return status;
+ }
+
+ /* Restart timer T1. */
+ if (should_restart_timer) {
+ pj_time_val timeout;
+ int msec_time = (1 << (tsx->retransmit_count)) * PJSIP_T1_TIMEOUT;
+
+ if (tsx->method.id!=PJSIP_INVITE_METHOD && msec_time>PJSIP_T2_TIMEOUT)
+ msec_time = PJSIP_T2_TIMEOUT;
+
+ timeout.sec = msec_time / 1000;
+ timeout.msec = msec_time % 1000;
+ pjsip_endpt_schedule_timer( tsx->endpt, &tsx->retransmit_timer,
+ &timeout);
+ }
+
+ return PJ_SUCCESS;
+}
+
+/*
+ * Handler for events in state Null.
+ */
+static pj_status_t pjsip_tsx_on_state_null( pjsip_transaction *tsx,
+ pjsip_event *event )
+{
+ pj_status_t status;
+
+ pj_assert( tsx->state == PJSIP_TSX_STATE_NULL);
+ pj_assert( tsx->last_tx == NULL );
+ pj_assert( tsx->has_unsent_msg == 0);
+
+ if (tsx->role == PJSIP_ROLE_UAS) {
+
+ /* Set state to Trying. */
+ pj_assert(event->type == PJSIP_EVENT_RX_MSG);
+ tsx_set_state( tsx, PJSIP_TSX_STATE_TRYING,
+ PJSIP_EVENT_RX_MSG, event->body.rx_msg.rdata );
+
+ } else {
+ pjsip_tx_data *tdata = event->body.tx_msg.tdata;
+
+ /* Save the message for retransmission. */
+ tsx->last_tx = tdata;
+ pjsip_tx_data_add_ref(tdata);
+
+ /* Send the message. */
+ status = tsx_send_msg( tsx, tdata);
+ if (status != PJ_SUCCESS) {
+ return status;
+ }
+
+ /* Start Timer B (or called timer F for non-INVITE) for transaction
+ * timeout.
+ */
+ pjsip_endpt_schedule_timer( tsx->endpt, &tsx->timeout_timer,
+ &timeout_timer_val);
+
+ /* Start Timer A (or timer E) for retransmission only if unreliable
+ * transport is being used.
+ */
+ if (tsx->transport_state == PJSIP_TSX_TRANSPORT_STATE_FINAL &&
+ PJSIP_TRANSPORT_IS_RELIABLE(tsx->transport)==0)
+ {
+ pjsip_endpt_schedule_timer(tsx->endpt, &tsx->retransmit_timer,
+ &t1_timer_val);
+ tsx->retransmit_count = 0;
+ }
+
+ /* Move state. */
+ tsx_set_state( tsx, PJSIP_TSX_STATE_CALLING,
+ PJSIP_EVENT_TX_MSG, tdata);
+ }
+
+ return PJ_SUCCESS;
+}
+
+/*
+ * State Calling is for UAC after it sends request but before any responses
+ * is received.
+ */
+static pj_status_t pjsip_tsx_on_state_calling( pjsip_transaction *tsx,
+ pjsip_event *event )
+{
+ pj_assert(tsx->state == PJSIP_TSX_STATE_CALLING);
+ pj_assert(tsx->role == PJSIP_ROLE_UAC);
+
+ if (event->type == PJSIP_EVENT_TIMER &&
+ event->body.timer.entry == &tsx->retransmit_timer)
+ {
+ pj_status_t status;
+
+ /* Retransmit the request. */
+ status = pjsip_tsx_retransmit( tsx, 1 );
+ if (status != PJ_SUCCESS) {
+ return status;
+ }
+
+ } else if (event->type == PJSIP_EVENT_TIMER &&
+ event->body.timer.entry == &tsx->timeout_timer)
+ {
+
+ /* Cancel retransmission timer. */
+ if (PJSIP_TRANSPORT_IS_RELIABLE(tsx->transport)==0) {
+ pjsip_endpt_cancel_timer(tsx->endpt, &tsx->retransmit_timer);
+ }
+
+ /* Set status code */
+ tsx->status_code = PJSIP_SC_TSX_TIMEOUT;
+
+ /* Inform TU. */
+ tsx_set_state( tsx, PJSIP_TSX_STATE_TERMINATED,
+ PJSIP_EVENT_TIMER, &tsx->timeout_timer);
+
+ /* Transaction is destroyed */
+ return PJSIP_ETSXDESTROYED;
+
+ } else if (event->type == PJSIP_EVENT_RX_MSG) {
+ int code;
+
+ /* Cancel retransmission timer A. */
+ if (PJSIP_TRANSPORT_IS_RELIABLE(tsx->transport)==0)
+ pjsip_endpt_cancel_timer(tsx->endpt, &tsx->retransmit_timer);
+
+ /* Cancel timer B (transaction timeout) */
+ pjsip_endpt_cancel_timer(tsx->endpt, &tsx->timeout_timer);
+
+ /* Discard retransmission message if it is not INVITE.
+ * The INVITE tdata is needed in case we have to generate ACK for
+ * the final response.
+ */
+ /* Keep last_tx for authorization. */
+ code = event->body.rx_msg.rdata->msg_info.msg->line.status.code;
+ if (tsx->method.id != PJSIP_INVITE_METHOD && code!=401 && code!=407) {
+ pjsip_tx_data_dec_ref(tsx->last_tx);
+ tsx->last_tx = NULL;
+ }
+
+ /* Processing is similar to state Proceeding. */
+ pjsip_tsx_on_state_proceeding_uac( tsx, event);
+
+ } else {
+ pj_assert(0);
+ return PJ_EBUG;
+ }
+
+ return PJ_SUCCESS;
+}
+
+/*
+ * State Trying is for UAS after it received request but before any responses
+ * is sent.
+ * Note: this is different than RFC3261, which can use Trying state for
+ * non-INVITE client transaction (bug in RFC?).
+ */
+static pj_status_t pjsip_tsx_on_state_trying( pjsip_transaction *tsx,
+ pjsip_event *event)
+{
+ pj_status_t status;
+
+ pj_assert(tsx->state == PJSIP_TSX_STATE_TRYING);
+
+ /* This state is only for UAS */
+ pj_assert(tsx->role == PJSIP_ROLE_UAS);
+
+ /* Better be transmission of response message.
+ * If we've got request retransmission, this means that the TU hasn't
+ * transmitted any responses within 500 ms, which is not allowed. If
+ * this happens, just ignore the event (we couldn't retransmit last
+ * response because we haven't sent any!).
+ */
+ //pj_assert(event->type == PJSIP_EVENT_TX_MSG);
+ if (event->type != PJSIP_EVENT_TX_MSG) {
+ return PJ_SUCCESS;
+ }
+
+ /* The rest of the processing of the event is exactly the same as in
+ * "Proceeding" state.
+ */
+ status = pjsip_tsx_on_state_proceeding_uas( tsx, event);
+
+ /* Inform the TU of the state transision if state is still State_Trying */
+ if (status==PJ_SUCCESS && tsx->state == PJSIP_TSX_STATE_TRYING) {
+ tsx_set_state( tsx, PJSIP_TSX_STATE_PROCEEDING,
+ PJSIP_EVENT_TX_MSG, event->body.tx_msg.tdata);
+ }
+
+ return status;
+}
+
+/*
+ * Handler for events in Proceeding for UAS
+ * This state happens after the TU sends provisional response.
+ */
+static pj_status_t pjsip_tsx_on_state_proceeding_uas( pjsip_transaction *tsx,
+ pjsip_event *event)
+{
+ pj_assert(tsx->state == PJSIP_TSX_STATE_PROCEEDING ||
+ tsx->state == PJSIP_TSX_STATE_TRYING);
+
+ /* This state is only for UAS. */
+ pj_assert(tsx->role == PJSIP_ROLE_UAS);
+
+ /* Receive request retransmission. */
+ if (event->type == PJSIP_EVENT_RX_MSG) {
+
+ pj_status_t status;
+
+ /* Send last response. */
+ status = pjsip_tsx_retransmit( tsx, 0 );
+ if (status != PJ_SUCCESS) {
+ return status;
+ }
+
+ } else if (event->type == PJSIP_EVENT_TX_MSG ) {
+ pjsip_tx_data *tdata = event->body.tx_msg.tdata;
+ pj_status_t status;
+
+ /* The TU sends response message to the request. Save this message so
+ * that we can retransmit the last response in case we receive request
+ * retransmission.
+ */
+ pjsip_msg *msg = tdata->msg;
+
+ /* This can only be a response message. */
+ pj_assert(msg->type == PJSIP_RESPONSE_MSG);
+
+ /* Status code must be higher than last sent. */
+ pj_assert(msg->line.status.code >= tsx->status_code);
+
+ /* Update last status */
+ tsx->status_code = msg->line.status.code;
+
+ /* Discard the saved last response (it will be updated later as
+ * necessary).
+ */
+ if (tsx->last_tx && tsx->last_tx != tdata) {
+ pjsip_tx_data_dec_ref( tsx->last_tx );
+ tsx->last_tx = NULL;
+ }
+
+ /* Send the message. */
+ status = tsx_send_msg(tsx, tdata);
+ if (status != PJ_SUCCESS) {
+ return status;
+ }
+
+ // Update To tag header for RFC2543 transaction.
+ // TODO:
+
+ /* Update transaction state */
+ if (PJSIP_IS_STATUS_IN_CLASS(tsx->status_code, 100)) {
+
+ if (tsx->last_tx != tdata) {
+ tsx->last_tx = tdata;
+ pjsip_tx_data_add_ref( tdata );
+ }
+ tsx_set_state( tsx, PJSIP_TSX_STATE_PROCEEDING,
+ PJSIP_EVENT_TX_MSG, tdata );
+
+ } else if (PJSIP_IS_STATUS_IN_CLASS(tsx->status_code, 200)) {
+
+ if (tsx->method.id == PJSIP_INVITE_METHOD && tsx->handle_ack==0) {
+
+ /* 2xx class message is not saved, because retransmission
+ * is handled by TU.
+ */
+ tsx_set_state( tsx, PJSIP_TSX_STATE_TERMINATED,
+ PJSIP_EVENT_TX_MSG, tdata );
+
+ /* Transaction is destroyed. */
+ return PJSIP_ETSXDESTROYED;
+
+ } else {
+ pj_time_val timeout;
+
+ if (tsx->method.id == PJSIP_INVITE_METHOD) {
+ tsx->retransmit_count = 0;
+ pjsip_endpt_schedule_timer( tsx->endpt,
+ &tsx->retransmit_timer,
+ &t1_timer_val);
+ }
+
+ /* Save last response sent for retransmission when request
+ * retransmission is received.
+ */
+ if (tsx->last_tx != tdata) {
+ tsx->last_tx = tdata;
+ pjsip_tx_data_add_ref(tdata);
+ }
+
+ /* Start timer J at 64*T1 for unreliable transport or zero for
+ * reliable transport.
+ */
+ if (PJSIP_TRANSPORT_IS_RELIABLE(tsx->transport)==0) {
+ timeout = timeout_timer_val;
+ } else {
+ timeout.sec = timeout.msec = 0;
+ }
+
+ pjsip_endpt_schedule_timer( tsx->endpt, &tsx->timeout_timer,
+ &timeout);
+
+ /* Set state to "Completed" */
+ tsx_set_state( tsx, PJSIP_TSX_STATE_COMPLETED,
+ PJSIP_EVENT_TX_MSG, tdata );
+ }
+
+ } else if (tsx->status_code >= 300) {
+
+ /* 3xx-6xx class message causes transaction to move to
+ * "Completed" state.
+ */
+ if (tsx->last_tx != tdata) {
+ tsx->last_tx = tdata;
+ pjsip_tx_data_add_ref( tdata );
+ }
+
+ /* Start timer H for transaction termination */
+ pjsip_endpt_schedule_timer(tsx->endpt,&tsx->timeout_timer,
+ &timeout_timer_val);
+
+ /* For INVITE, if unreliable transport is used, retransmission
+ * timer G will be scheduled (retransmission).
+ */
+ if (PJSIP_TRANSPORT_IS_RELIABLE(tsx->transport)==0) {
+ pjsip_cseq_hdr *cseq = pjsip_msg_find_hdr( msg, PJSIP_H_CSEQ,
+ NULL);
+ if (cseq->method.id == PJSIP_INVITE_METHOD) {
+ tsx->retransmit_count = 0;
+ pjsip_endpt_schedule_timer(tsx->endpt,
+ &tsx->retransmit_timer,
+ &t1_timer_val);
+ }
+ }
+
+ /* Inform TU */
+ tsx_set_state( tsx, PJSIP_TSX_STATE_COMPLETED,
+ PJSIP_EVENT_TX_MSG, tdata );
+
+ } else {
+ pj_assert(0);
+ }
+
+
+ } else if (event->type == PJSIP_EVENT_TIMER &&
+ event->body.timer.entry == &tsx->retransmit_timer) {
+ /* Retransmission timer elapsed. */
+ pj_status_t status;
+
+ /* Must have last response to retransmit. */
+ pj_assert(tsx->last_tx != NULL);
+
+ /* Retransmit the last response. */
+ status = pjsip_tsx_retransmit( tsx, 1 );
+ if (status != PJ_SUCCESS) {
+ return status;
+ }
+
+ } else if (event->type == PJSIP_EVENT_TIMER &&
+ event->body.timer.entry == &tsx->timeout_timer) {
+
+ /* Timeout timer. should not happen? */
+ pj_assert(0);
+
+ tsx->status_code = PJSIP_SC_TSX_TIMEOUT;
+
+ tsx_set_state( tsx, PJSIP_TSX_STATE_TERMINATED,
+ PJSIP_EVENT_TIMER, &tsx->timeout_timer);
+
+ return PJ_EBUG;
+
+ } else {
+ pj_assert(0);
+ return PJ_EBUG;
+ }
+
+ return PJ_SUCCESS;
+}
+
+/*
+ * Handler for events in Proceeding for UAC
+ * This state happens after provisional response(s) has been received from
+ * UAS.
+ */
+static pj_status_t pjsip_tsx_on_state_proceeding_uac(pjsip_transaction *tsx,
+ pjsip_event *event)
+{
+
+ pj_assert(tsx->state == PJSIP_TSX_STATE_PROCEEDING ||
+ tsx->state == PJSIP_TSX_STATE_CALLING);
+
+ if (event->type != PJSIP_EVENT_TIMER) {
+ /* Must be incoming response, because we should not retransmit
+ * request once response has been received.
+ */
+ pj_assert(event->type == PJSIP_EVENT_RX_MSG);
+ if (event->type != PJSIP_EVENT_RX_MSG) {
+ return PJ_EINVALIDOP;
+ }
+
+ tsx->status_code = event->body.rx_msg.rdata->msg_info.msg->line.status.code;
+ } else {
+ tsx->status_code = PJSIP_SC_TSX_TIMEOUT;
+ }
+
+ if (PJSIP_IS_STATUS_IN_CLASS(tsx->status_code, 100)) {
+
+ /* Inform the message to TU. */
+ tsx_set_state( tsx, PJSIP_TSX_STATE_PROCEEDING,
+ PJSIP_EVENT_RX_MSG, event->body.rx_msg.rdata );
+
+ } else if (PJSIP_IS_STATUS_IN_CLASS(tsx->status_code,200)) {
+
+ /* Stop timeout timer B/F. */
+ pjsip_endpt_cancel_timer( tsx->endpt, &tsx->timeout_timer );
+
+ /* For INVITE, the state moves to Terminated state (because ACK is
+ * handled in TU). For non-INVITE, state moves to Completed.
+ */
+ if (tsx->method.id == PJSIP_INVITE_METHOD && tsx->handle_ack == 0) {
+ tsx_set_state( tsx, PJSIP_TSX_STATE_TERMINATED,
+ PJSIP_EVENT_RX_MSG, event->body.rx_msg.rdata );
+ return PJSIP_ETSXDESTROYED;
+
+ } else {
+ pj_time_val timeout;
+
+ /* For unreliable transport, start timer D (for INVITE) or
+ * timer K for non-INVITE. */
+ if (PJSIP_TRANSPORT_IS_RELIABLE(tsx->transport) == 0) {
+ if (tsx->method.id == PJSIP_INVITE_METHOD) {
+ timeout = td_timer_val;
+ } else {
+ timeout = t4_timer_val;
+ }
+ } else {
+ timeout.sec = timeout.msec = 0;
+ }
+ pjsip_endpt_schedule_timer( tsx->endpt, &tsx->timeout_timer,
+ &timeout);
+
+ /* Move state to Completed, inform TU. */
+ tsx_set_state( tsx, PJSIP_TSX_STATE_COMPLETED,
+ PJSIP_EVENT_RX_MSG, event->body.rx_msg.rdata );
+ }
+
+ } else if (tsx->status_code >= 300 && tsx->status_code <= 699) {
+ pj_time_val timeout;
+ pj_status_t status;
+
+ /* Stop timer B. */
+ pjsip_endpt_cancel_timer( tsx->endpt, &tsx->timeout_timer );
+
+ /* Generate and send ACK for INVITE. */
+ if (tsx->method.id == PJSIP_INVITE_METHOD) {
+ pjsip_endpt_create_ack( tsx->endpt, tsx->last_tx,
+ event->body.rx_msg.rdata );
+ status = tsx_send_msg( tsx, tsx->last_tx);
+ if (status != PJ_SUCCESS) {
+ return status;
+ }
+ }
+
+ /* Start Timer D with TD/T4 timer if unreliable transport is used. */
+ if (PJSIP_TRANSPORT_IS_RELIABLE(tsx->transport) == 0) {
+ if (tsx->method.id == PJSIP_INVITE_METHOD) {
+ timeout = td_timer_val;
+ } else {
+ timeout = t4_timer_val;
+ }
+ } else {
+ timeout.sec = timeout.msec = 0;
+ }
+ pjsip_endpt_schedule_timer( tsx->endpt, &tsx->timeout_timer, &timeout);
+
+ /* Inform TU. */
+ tsx_set_state( tsx, PJSIP_TSX_STATE_COMPLETED,
+ PJSIP_EVENT_RX_MSG, event->body.rx_msg.rdata );
+
+ } else {
+ // Shouldn't happen because there's no timer for this state.
+ pj_assert(0);
+ return PJ_EBUG;
+ }
+
+ return PJ_SUCCESS;
+}
+
+/*
+ * Handler for events in Completed state for UAS
+ */
+static pj_status_t pjsip_tsx_on_state_completed_uas( pjsip_transaction *tsx,
+ pjsip_event *event)
+{
+ pj_assert(tsx->state == PJSIP_TSX_STATE_COMPLETED);
+
+ if (event->type == PJSIP_EVENT_RX_MSG) {
+ pjsip_msg *msg = event->body.rx_msg.rdata->msg_info.msg;
+ pjsip_cseq_hdr *cseq = pjsip_msg_find_hdr( msg, PJSIP_H_CSEQ, NULL );
+
+ /* On receive request retransmission, retransmit last response. */
+ if (cseq->method.id != PJSIP_ACK_METHOD) {
+ pj_status_t status;
+
+ status = pjsip_tsx_retransmit( tsx, 0 );
+ if (status != PJ_SUCCESS) {
+ return status;
+ }
+
+ } else {
+ /* Process incoming ACK request. */
+
+ /* Cease retransmission. */
+ pjsip_endpt_cancel_timer( tsx->endpt, &tsx->retransmit_timer );
+
+ /* Start timer I in T4 interval (transaction termination). */
+ pjsip_endpt_cancel_timer( tsx->endpt, &tsx->timeout_timer );
+ pjsip_endpt_schedule_timer( tsx->endpt, &tsx->timeout_timer,
+ &t4_timer_val);
+
+ /* Move state to "Confirmed" */
+ tsx_set_state( tsx, PJSIP_TSX_STATE_CONFIRMED,
+ PJSIP_EVENT_RX_MSG, event->body.rx_msg.rdata );
+ }
+
+ } else if (event->type == PJSIP_EVENT_TIMER) {
+
+ if (event->body.timer.entry == &tsx->retransmit_timer) {
+ /* Retransmit message. */
+ pj_status_t status;
+
+ status = pjsip_tsx_retransmit( tsx, 1 );
+ if (status != PJ_SUCCESS) {
+ return status;
+ }
+
+ } else {
+ if (tsx->method.id == PJSIP_INVITE_METHOD) {
+
+ /* For INVITE, this means that ACK was never received.
+ * Set state to Terminated, and inform TU.
+ */
+
+ tsx->status_code = PJSIP_SC_TSX_TIMEOUT;
+
+ tsx_set_state( tsx, PJSIP_TSX_STATE_TERMINATED,
+ PJSIP_EVENT_TIMER, &tsx->timeout_timer );
+
+ return PJSIP_ETSXDESTROYED;
+
+ } else {
+ /* Transaction terminated, it can now be deleted. */
+ tsx_set_state( tsx, PJSIP_TSX_STATE_TERMINATED,
+ PJSIP_EVENT_TIMER, &tsx->timeout_timer );
+ return PJSIP_ETSXDESTROYED;
+ }
+ }
+
+ } else {
+ /* Ignore request to transmit. */
+ pj_assert(event->body.tx_msg.tdata == tsx->last_tx);
+ }
+
+ return PJ_SUCCESS;
+}
+
+/*
+ * Handler for events in Completed state for UAC transaction.
+ */
+static pj_status_t pjsip_tsx_on_state_completed_uac( pjsip_transaction *tsx,
+ pjsip_event *event)
+{
+ pj_assert(tsx->state == PJSIP_TSX_STATE_COMPLETED);
+
+ if (event->type == PJSIP_EVENT_TIMER) {
+ /* Must be the timeout timer. */
+ pj_assert(event->body.timer.entry == &tsx->timeout_timer);
+
+ /* Move to Terminated state. */
+ tsx_set_state( tsx, PJSIP_TSX_STATE_TERMINATED,
+ PJSIP_EVENT_TIMER, event->body.timer.entry );
+
+ /* Transaction has been destroyed. */
+ return PJSIP_ETSXDESTROYED;
+
+ } else if (event->type == PJSIP_EVENT_RX_MSG) {
+ if (tsx->method.id == PJSIP_INVITE_METHOD) {
+ /* On received of final response retransmission, retransmit the ACK.
+ * TU doesn't need to be informed.
+ */
+ pjsip_msg *msg = event->body.rx_msg.rdata->msg_info.msg;
+ pj_assert(msg->type == PJSIP_RESPONSE_MSG);
+ if (msg->type==PJSIP_RESPONSE_MSG &&
+ msg->line.status.code >= 200)
+ {
+ pj_status_t status;
+
+ status = pjsip_tsx_retransmit( tsx, 0 );
+ if (status != PJ_SUCCESS) {
+ return status;
+ }
+ } else {
+ /* Very late retransmission of privisional response. */
+ pj_assert( msg->type == PJSIP_RESPONSE_MSG );
+ }
+ } else {
+ /* Just drop the response. */
+ }
+ } else if (tsx->method.id == PJSIP_INVITE_METHOD &&
+ event->type == PJSIP_EVENT_TX_MSG &&
+ event->body.tx_msg.tdata->msg->line.req.method.id==PJSIP_ACK_METHOD) {
+
+ pj_status_t status;
+
+ /* Set last transmitted message. */
+ if (tsx->last_tx != event->body.tx_msg.tdata) {
+ pjsip_tx_data_dec_ref( tsx->last_tx );
+ tsx->last_tx = event->body.tx_msg.tdata;
+ pjsip_tx_data_add_ref( tsx->last_tx );
+ }
+
+ /* No state changed, but notify app.
+ * Must notify now, so app has chance to put SDP in outgoing ACK msg.
+ */
+ tsx_set_state( tsx, PJSIP_TSX_STATE_COMPLETED,
+ PJSIP_EVENT_TX_MSG, event->body.tx_msg.tdata );
+
+ /* Send msg */
+ status = tsx_send_msg(tsx, event->body.tx_msg.tdata);
+ if (status != PJ_SUCCESS)
+ return status;
+
+ } else {
+ pj_assert(0);
+ return PJ_EBUG;
+ }
+
+ return PJ_SUCCESS;
+}
+
+/*
+ * Handler for events in state Confirmed.
+ */
+static pj_status_t pjsip_tsx_on_state_confirmed( pjsip_transaction *tsx,
+ pjsip_event *event)
+{
+ pj_assert(tsx->state == PJSIP_TSX_STATE_CONFIRMED);
+
+ /* This state is only for UAS for INVITE. */
+ pj_assert(tsx->role == PJSIP_ROLE_UAS);
+ pj_assert(tsx->method.id == PJSIP_INVITE_METHOD);
+
+ /* Absorb any ACK received. */
+ if (event->type == PJSIP_EVENT_RX_MSG) {
+
+ pjsip_method_e method_id =
+ event->body.rx_msg.rdata->msg_info.msg->line.req.method.id;
+
+ /* Must be a request message. */
+ pj_assert(event->body.rx_msg.rdata->msg_info.msg->type == PJSIP_REQUEST_MSG);
+
+ /* Must be an ACK request or a late INVITE retransmission. */
+ pj_assert(method_id == PJSIP_ACK_METHOD ||
+ method_id == PJSIP_INVITE_METHOD);
+
+ /* Just so that compiler won't complain about unused vars when
+ * building release code.
+ */
+ PJ_UNUSED_ARG(method_id);
+
+ } else if (event->type == PJSIP_EVENT_TIMER) {
+ /* Must be from timeout_timer_. */
+ pj_assert(event->body.timer.entry == &tsx->timeout_timer);
+
+ /* Move to Terminated state. */
+ tsx_set_state( tsx, PJSIP_TSX_STATE_TERMINATED,
+ PJSIP_EVENT_TIMER, &tsx->timeout_timer );
+
+ /* Transaction has been destroyed. */
+ return PJSIP_ETSXDESTROYED;
+
+ } else {
+ pj_assert(0);
+ return PJ_EBUG;
+ }
+
+ return PJ_SUCCESS;
+}
+
+/*
+ * Handler for events in state Terminated.
+ */
+static pj_status_t pjsip_tsx_on_state_terminated( pjsip_transaction *tsx,
+ pjsip_event *event)
+{
+ pj_assert(tsx->state == PJSIP_TSX_STATE_TERMINATED);
+
+ PJ_UNUSED_ARG(event);
+
+ /* Destroy this transaction */
+ tsx_set_state(tsx, PJSIP_TSX_STATE_DESTROYED,
+ event->type, event->body.user.user1 );
+
+ return PJ_SUCCESS;
+}
+
+
+static pj_status_t pjsip_tsx_on_state_destroyed(pjsip_transaction *tsx,
+ pjsip_event *event)
+{
+ PJ_UNUSED_ARG(tsx);
+ PJ_UNUSED_ARG(event);
+ return PJ_SUCCESS;
+}
+
diff --git a/pjsip/src/pjsip/sip_transport.c b/pjsip/src/pjsip/sip_transport.c
index a32fb683..de1d13d9 100644
--- a/pjsip/src/pjsip/sip_transport.c
+++ b/pjsip/src/pjsip/sip_transport.c
@@ -1,843 +1,843 @@
-/* $Id$ */
-/*
- * Copyright (C) 2003-2006 Benny Prijono <benny@prijono.org>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
-#include <pjsip/sip_transport.h>
-#include <pjsip/sip_endpoint.h>
-#include <pjsip/sip_parser.h>
-#include <pjsip/sip_msg.h>
-#include <pjsip/sip_private.h>
-#include <pjsip/sip_errno.h>
-#include <pj/os.h>
-#include <pj/log.h>
-#include <pj/ioqueue.h>
-#include <pj/hash.h>
-#include <pj/string.h>
-#include <pj/pool.h>
-#include <pj/assert.h>
-#include <pj/lock.h>
-
-
-#define THIS_FILE "transport"
-
-/*
- * Transport manager.
- */
-struct pjsip_tpmgr
-{
- pj_hash_table_t *table;
- pj_lock_t *lock;
- pjsip_endpoint *endpt;
- pjsip_tpfactory factory_list;
- void (*msg_cb)(pjsip_endpoint*, pj_status_t, pjsip_rx_data*);
-};
-
-
-
-/*****************************************************************************
- *
- * GENERAL TRANSPORT (NAMES, TYPES, ETC.)
- *
- *****************************************************************************/
-
-/*
- * Transport names.
- */
-const struct
-{
- pjsip_transport_type_e type;
- pj_uint16_t port;
- pj_str_t name;
- unsigned flag;
-} transport_names[] =
-{
- { PJSIP_TRANSPORT_UNSPECIFIED, 0, {NULL, 0}, 0},
- { PJSIP_TRANSPORT_UDP, 5060, {"UDP", 3}, PJSIP_TRANSPORT_DATAGRAM},
- { PJSIP_TRANSPORT_TCP, 5060, {"TCP", 3}, PJSIP_TRANSPORT_RELIABLE},
- { PJSIP_TRANSPORT_TLS, 5061, {"TLS", 3}, PJSIP_TRANSPORT_RELIABLE | PJSIP_TRANSPORT_SECURE},
- { PJSIP_TRANSPORT_SCTP, 5060, {"SCTP", 4}, PJSIP_TRANSPORT_RELIABLE}
-};
-
-
-/*
- * Get transport type from name.
- */
-PJ_DEF(pjsip_transport_type_e)
-pjsip_transport_get_type_from_name(const pj_str_t *name)
-{
- unsigned i;
-
- for (i=0; i<PJ_ARRAY_SIZE(transport_names); ++i) {
- if (pj_stricmp(name, &transport_names[i].name) == 0) {
- return transport_names[i].type;
- }
- }
-
- pj_assert(!"Invalid transport name");
- return PJSIP_TRANSPORT_UNSPECIFIED;
-}
-
-
-/*
- * Get the transport type for the specified flags.
- */
-PJ_DEF(pjsip_transport_type_e)
-pjsip_transport_get_type_from_flag(unsigned flag)
-{
- unsigned i;
-
- for (i=0; i<PJ_ARRAY_SIZE(transport_names); ++i) {
- if (transport_names[i].flag == flag) {
- return transport_names[i].type;
- }
- }
-
- pj_assert(!"Invalid transport type");
- return PJSIP_TRANSPORT_UNSPECIFIED;
-}
-
-PJ_DEF(unsigned)
-pjsip_transport_get_flag_from_type( pjsip_transport_type_e type )
-{
- PJ_ASSERT_RETURN(type < PJ_ARRAY_SIZE(transport_names), 0);
- return transport_names[type].flag;
-}
-
-/*
- * Get the default SIP port number for the specified type.
- */
-PJ_DEF(int)
-pjsip_transport_get_default_port_for_type(pjsip_transport_type_e type)
-{
- PJ_ASSERT_RETURN(type < PJ_ARRAY_SIZE(transport_names), 5060);
- return transport_names[type].port;
-}
-
-
-/*****************************************************************************
- *
- * TRANSMIT DATA BUFFER MANIPULATION.
- *
- *****************************************************************************/
-
-/*
- * Create new transmit buffer.
- */
-PJ_DEF(pj_status_t) pjsip_tx_data_create( pjsip_tpmgr *mgr,
- pjsip_tx_data **p_tdata )
-{
- pj_pool_t *pool;
- pjsip_tx_data *tdata;
- pj_status_t status;
-
- PJ_ASSERT_RETURN(mgr && p_tdata, PJ_EINVAL);
-
- PJ_LOG(5, ("", "pjsip_tx_data_create"));
-
- pool = pjsip_endpt_create_pool( mgr->endpt, "tdta%p",
- PJSIP_POOL_LEN_TDATA,
- PJSIP_POOL_INC_TDATA );
- if (!pool)
- return PJ_ENOMEM;
-
- tdata = pj_pool_zalloc(pool, sizeof(pjsip_tx_data));
- tdata->pool = pool;
- tdata->mgr = mgr;
- pj_snprintf(tdata->obj_name, PJ_MAX_OBJ_NAME, "tdta%p", tdata);
-
- status = pj_atomic_create(tdata->pool, 0, &tdata->ref_cnt);
- if (status != PJ_SUCCESS) {
- pjsip_endpt_destroy_pool( mgr->endpt, tdata->pool );
- return status;
- }
-
- //status = pj_lock_create_simple_mutex(pool, "tdta%p", &tdata->lock);
- status = pj_lock_create_null_mutex(pool, "tdta%p", &tdata->lock);
- if (status != PJ_SUCCESS) {
- pjsip_endpt_destroy_pool( mgr->endpt, tdata->pool );
- return status;
- }
-
- pj_ioqueue_op_key_init(&tdata->op_key.key, sizeof(tdata->op_key));
-
- *p_tdata = tdata;
- return PJ_SUCCESS;
-}
-
-
-/*
- * Add reference to tx buffer.
- */
-PJ_DEF(void) pjsip_tx_data_add_ref( pjsip_tx_data *tdata )
-{
- pj_atomic_inc(tdata->ref_cnt);
-}
-
-/*
- * Decrease transport data reference, destroy it when the reference count
- * reaches zero.
- */
-PJ_DEF(void) pjsip_tx_data_dec_ref( pjsip_tx_data *tdata )
-{
- pj_assert( pj_atomic_get(tdata->ref_cnt) > 0);
- if (pj_atomic_dec_and_get(tdata->ref_cnt) <= 0) {
- PJ_LOG(5,(tdata->obj_name, "destroying txdata"));
- pj_atomic_destroy( tdata->ref_cnt );
- pj_lock_destroy( tdata->lock );
- pjsip_endpt_destroy_pool( tdata->mgr->endpt, tdata->pool );
- }
-}
-
-/*
- * Invalidate the content of the print buffer to force the message to be
- * re-printed when sent.
- */
-PJ_DEF(void) pjsip_tx_data_invalidate_msg( pjsip_tx_data *tdata )
-{
- tdata->buf.cur = tdata->buf.start;
-}
-
-PJ_DEF(pj_bool_t) pjsip_tx_data_is_valid( pjsip_tx_data *tdata )
-{
- return tdata->buf.cur != tdata->buf.start;
-}
-
-
-
-/*****************************************************************************
- *
- * TRANSPORT KEY
- *
- *****************************************************************************/
-
-/*
- * Transport key for indexing in the hash table.
- */
-typedef struct transport_key
-{
- pj_uint8_t type;
- pj_uint8_t zero;
- pj_uint16_t port;
- pj_uint32_t addr;
-} transport_key;
-
-
-/*****************************************************************************
- *
- * TRANSPORT
- *
- *****************************************************************************/
-
-static void transport_send_callback(pjsip_transport *transport,
- void *token,
- pj_ssize_t size)
-{
- pjsip_tx_data *tdata = token;
-
- PJ_UNUSED_ARG(transport);
-
- /* Mark pending off so that app can resend/reuse txdata from inside
- * the callback.
- */
- tdata->is_pending = 0;
-
- /* Call callback, if any. */
- if (tdata->cb) {
- (*tdata->cb)(tdata->token, tdata, size);
- }
-
- /* Decrement reference count. */
- pjsip_tx_data_dec_ref(tdata);
-}
-
-/*
- * Send a SIP message using the specified transport.
- */
-PJ_DEF(pj_status_t) pjsip_transport_send( pjsip_transport *tr,
- pjsip_tx_data *tdata,
- const pj_sockaddr_in *addr,
- void *token,
- void (*cb)(void *token,
- pjsip_tx_data *tdata,
- pj_ssize_t))
-{
- pj_status_t status;
-
- PJ_ASSERT_RETURN(tr && tdata && addr, PJ_EINVAL);
-
- /* Is it currently being sent? */
- if (tdata->is_pending) {
- pj_assert(!"Invalid operation step!");
- return PJSIP_EPENDINGTX;
- }
-
- /* Allocate buffer if necessary. */
- if (tdata->buf.start == NULL) {
- tdata->buf.start = pj_pool_alloc( tdata->pool, PJSIP_MAX_PKT_LEN);
- tdata->buf.cur = tdata->buf.start;
- tdata->buf.end = tdata->buf.start + PJSIP_MAX_PKT_LEN;
- }
-
- /* Do we need to reprint? */
- if (!pjsip_tx_data_is_valid(tdata)) {
- pj_ssize_t size;
-
- size = pjsip_msg_print( tdata->msg, tdata->buf.start,
- tdata->buf.end - tdata->buf.start);
- if (size < 0) {
- return PJSIP_EMSGTOOLONG;
- }
- pj_assert(size != 0);
- tdata->buf.cur += size;
- tdata->buf.cur[size] = '\0';
- }
-
- /* Save callback data. */
- tdata->token = token;
- tdata->cb = cb;
-
- /* Add reference counter. */
- pjsip_tx_data_add_ref(tdata);
-
- /* Mark as pending. */
- tdata->is_pending = 1;
-
- /* Send to transport. */
- status = (*tr->send_msg)(tr, tdata, addr, (void*)tdata,
- &transport_send_callback);
-
- if (status != PJ_EPENDING) {
- tdata->is_pending = 0;
- pjsip_tx_data_dec_ref(tdata);
- }
-
- return status;
-}
-
-static void transport_idle_callback(pj_timer_heap_t *timer_heap,
- struct pj_timer_entry *entry)
-{
- pjsip_transport *tp = entry->user_data;
- pj_assert(tp != NULL);
-
- PJ_UNUSED_ARG(timer_heap);
-
- entry->id = PJ_FALSE;
- pjsip_transport_unregister(tp->tpmgr, tp);
-}
-
-/*
- * Add ref.
- */
-PJ_DEF(pj_status_t) pjsip_transport_add_ref( pjsip_transport *tp )
-{
- PJ_ASSERT_RETURN(tp != NULL, PJ_EINVAL);
-
- if (pj_atomic_inc_and_get(tp->ref_cnt) == 1) {
- pj_lock_acquire(tp->tpmgr->lock);
- /* Verify again. */
- if (pj_atomic_get(tp->ref_cnt) == 1) {
- if (tp->idle_timer.id != PJ_FALSE) {
- pjsip_endpt_cancel_timer(tp->tpmgr->endpt, &tp->idle_timer);
- tp->idle_timer.id = PJ_FALSE;
- }
- }
- pj_lock_release(tp->tpmgr->lock);
- }
-
- return PJ_SUCCESS;
-}
-
-/*
- * Dec ref.
- */
-PJ_DEF(pj_status_t) pjsip_transport_dec_ref( pjsip_transport *tp )
-{
- PJ_ASSERT_RETURN(tp != NULL, PJ_EINVAL);
-
- pj_assert(pj_atomic_get(tp->ref_cnt) > 0);
-
- if (pj_atomic_dec_and_get(tp->ref_cnt) == 0) {
- pj_lock_acquire(tp->tpmgr->lock);
- /* Verify again. */
- if (pj_atomic_get(tp->ref_cnt) == 0) {
- pj_time_val delay = { PJSIP_TRANSPORT_IDLE_TIME, 0 };
-
- pj_assert(tp->idle_timer.id == 0);
- tp->idle_timer.id = PJ_TRUE;
- pjsip_endpt_schedule_timer(tp->tpmgr->endpt, &tp->idle_timer,
- &delay);
- }
- pj_lock_release(tp->tpmgr->lock);
- }
-
- return PJ_SUCCESS;
-}
-
-
-/**
- * Register a transport.
- */
-PJ_DEF(pj_status_t) pjsip_transport_register( pjsip_tpmgr *mgr,
- pjsip_transport *tp )
-{
- transport_key key;
-
- /* Init. */
- tp->tpmgr = mgr;
- pj_memset(&tp->idle_timer, 0, sizeof(tp->idle_timer));
- tp->idle_timer.user_data = tp;
- tp->idle_timer.cb = &transport_idle_callback;
-
- /*
- * Register to hash table.
- */
- key.type = (pj_uint8_t)tp->type;
- key.zero = 0;
- key.addr = pj_ntohl(tp->rem_addr.sin_addr.s_addr);
- key.port = pj_ntohs(tp->rem_addr.sin_port);
-
- pj_lock_acquire(mgr->lock);
- pj_hash_set(tp->pool, mgr->table, &key, sizeof(key), tp);
- pj_lock_release(mgr->lock);
-
- return PJ_SUCCESS;
-}
-
-
-/**
- * Unregister transport.
- */
-PJ_DEF(pj_status_t) pjsip_transport_unregister( pjsip_tpmgr *mgr,
- pjsip_transport *tp)
-{
- transport_key key;
-
- PJ_ASSERT_RETURN(pj_atomic_get(tp->ref_cnt) == 0, PJSIP_EBUSY);
-
- pj_lock_acquire(tp->lock);
- pj_lock_acquire(mgr->lock);
-
- /*
- * Unregister timer, if any.
- */
- pj_assert(tp->idle_timer.id == PJ_FALSE);
- if (tp->idle_timer.id != PJ_FALSE) {
- pjsip_endpt_cancel_timer(mgr->endpt, &tp->idle_timer);
- tp->idle_timer.id = PJ_FALSE;
- }
-
- /*
- * Unregister from hash table.
- */
- key.type = (pj_uint8_t)tp->type;
- key.zero = 0;
- key.addr = pj_ntohl(tp->rem_addr.sin_addr.s_addr);
- key.port = pj_ntohs(tp->rem_addr.sin_port);
-
- pj_hash_set(tp->pool, mgr->table, &key, sizeof(key), NULL);
-
- pj_lock_release(mgr->lock);
-
- /* Destroy. */
- return tp->destroy(tp);
-}
-
-
-
-/*****************************************************************************
- *
- * TRANSPORT FACTORY
- *
- *****************************************************************************/
-
-
-PJ_DEF(pj_status_t) pjsip_tpmgr_register_tpfactory( pjsip_tpmgr *mgr,
- pjsip_tpfactory *tpf)
-{
- pjsip_tpfactory *p;
- pj_status_t status;
-
- pj_lock_acquire(mgr->lock);
-
- /* Check that no factory with the same type has been registered. */
- status = PJ_SUCCESS;
- for (p=mgr->factory_list.next; p!=&mgr->factory_list; p=p->next) {
- if (p->type == tpf->type) {
- status = PJSIP_ETYPEEXISTS;
- break;
- }
- if (p == tpf) {
- status = PJ_EEXISTS;
- break;
- }
- }
-
- if (status != PJ_SUCCESS) {
- pj_lock_release(mgr->lock);
- return status;
- }
-
- pj_list_insert_before(&mgr->factory_list, tpf);
-
- pj_lock_release(mgr->lock);
-
- return PJ_SUCCESS;
-}
-
-
-/**
- * Unregister factory.
- */
-PJ_DEF(pj_status_t) pjsip_tpmgr_unregister_tpfactory( pjsip_tpmgr *mgr,
- pjsip_tpfactory *tpf)
-{
- pj_lock_acquire(mgr->lock);
-
- pj_assert(pj_list_find_node(&mgr->factory_list, tpf) == tpf);
- pj_list_erase(tpf);
-
- pj_lock_release(mgr->lock);
-
- return PJ_SUCCESS;
-}
-
-
-/*****************************************************************************
- *
- * TRANSPORT MANAGER
- *
- *****************************************************************************/
-
-/*
- * Create a new transport manager.
- */
-PJ_DEF(pj_status_t) pjsip_tpmgr_create( pj_pool_t *pool,
- pjsip_endpoint *endpt,
- void (*cb)(pjsip_endpoint*,
- pj_status_t,
- pjsip_rx_data *),
- pjsip_tpmgr **p_mgr)
-{
- pjsip_tpmgr *mgr;
- pj_status_t status;
-
- PJ_ASSERT_RETURN(pool && endpt && cb && p_mgr, PJ_EINVAL);
-
- PJ_LOG(5, (THIS_FILE, "pjsip_tpmgr_create()"));
-
- mgr = pj_pool_zalloc(pool, sizeof(*mgr));
- mgr->endpt = endpt;
- mgr->msg_cb = cb;
- pj_list_init(&mgr->factory_list);
-
- mgr->table = pj_hash_create(pool, PJSIP_TPMGR_HTABLE_SIZE);
- if (!mgr->table)
- return PJ_ENOMEM;
-
- status = pj_lock_create_recursive_mutex(pool, "tmgr%p", &mgr->lock);
- if (status != PJ_SUCCESS)
- return status;
-
- *p_mgr = mgr;
- return PJ_SUCCESS;
-}
-
-/*
- * pjsip_tpmgr_destroy()
- *
- * Destroy transport manager.
- */
-PJ_DEF(pj_status_t) pjsip_tpmgr_destroy( pjsip_tpmgr *mgr )
-{
- pj_hash_iterator_t itr_val;
- pj_hash_iterator_t *itr;
-
- PJ_LOG(5, (THIS_FILE, "pjsip_tpmgr_destroy()"));
-
- pj_lock_acquire(mgr->lock);
-
- itr = pj_hash_first(mgr->table, &itr_val);
- while (itr != NULL) {
- pj_hash_iterator_t *next;
- pjsip_transport *transport;
-
- transport = pj_hash_this(mgr->table, itr);
-
- next = pj_hash_next(mgr->table, itr);
-
- pj_atomic_set(transport->ref_cnt, 0);
- pjsip_transport_unregister(mgr, transport);
-
- itr = next;
- }
-
- pj_lock_release(mgr->lock);
-
- return PJ_SUCCESS;
-}
-
-
-/*
- * pjsip_tpmgr_receive_packet()
- *
- * Called by tranports when they receive a new packet.
- */
-PJ_DEF(pj_ssize_t) pjsip_tpmgr_receive_packet( pjsip_tpmgr *mgr,
- pjsip_rx_data *rdata)
-{
- pjsip_transport *tr = rdata->tp_info.transport;
- pj_str_t s;
-
- char *current_pkt;
- pj_size_t remaining_len;
- pj_size_t total_processed = 0;
-
- /* Check size. */
- pj_assert(rdata->pkt_info.len > 0);
- if (rdata->pkt_info.len <= 0)
- return -1;
-
- current_pkt = rdata->pkt_info.packet;
- remaining_len = rdata->pkt_info.len;
-
- /* Must NULL terminate buffer. This is the requirement of the
- * parser etc.
- */
- current_pkt[remaining_len] = '\0';
-
- /* Process all message fragments. */
- while (total_processed < remaining_len) {
-
- pjsip_msg *msg;
- pj_size_t msg_fragment_size = 0;
-
- /* Initialize default fragment size. */
- msg_fragment_size = remaining_len;
-
- /* Null terminate packet. */
-
- /* Clear and init msg_info in rdata.
- * Endpoint might inspect the values there when we call the callback
- * to report some errors.
- */
- pj_memset(&rdata->msg_info, 0, sizeof(rdata->msg_info));
- pj_list_init(&rdata->msg_info.parse_err);
- rdata->msg_info.msg_buf = current_pkt;
- rdata->msg_info.len = remaining_len;
-
- /* For TCP transport, check if the whole message has been received. */
- if ((tr->flag & PJSIP_TRANSPORT_DATAGRAM) == 0) {
- pj_status_t msg_status;
- msg_status = pjsip_find_msg(current_pkt, remaining_len, PJ_FALSE,
- &msg_fragment_size);
- if (msg_status != PJ_SUCCESS) {
- if (remaining_len == PJSIP_MAX_PKT_LEN) {
- mgr->msg_cb(mgr->endpt, PJSIP_ERXOVERFLOW, rdata);
- /* Exhaust all data. */
- return rdata->pkt_info.len;
- } else {
- /* Not enough data in packet. */
- return total_processed;
- }
- }
- }
-
- /* Update msg_info. */
- rdata->msg_info.len = msg_fragment_size;
-
- /* Parse the message. */
- rdata->msg_info.msg = msg =
- pjsip_parse_rdata( current_pkt, msg_fragment_size, rdata);
- if (msg == NULL) {
- mgr->msg_cb(mgr->endpt, PJSIP_EINVALIDMSG, rdata);
- goto finish_process_fragment;
- }
-
- /* Perform basic header checking. */
- if (rdata->msg_info.call_id.ptr == NULL ||
- rdata->msg_info.from == NULL ||
- rdata->msg_info.to == NULL ||
- rdata->msg_info.via == NULL ||
- rdata->msg_info.cseq == NULL)
- {
- mgr->msg_cb(mgr->endpt, PJSIP_EMISSINGHDR, rdata);
- goto finish_process_fragment;
- }
-
- /* If message is received from address that's different from sent-by,
- * MUST add received parameter to the via.
- */
- s = pj_str(pj_inet_ntoa(rdata->pkt_info.addr.sin_addr));
- if (pj_strcmp(&s, &rdata->msg_info.via->sent_by.host) != 0) {
- pj_strdup(rdata->tp_info.pool,
- &rdata->msg_info.via->recvd_param, &s);
- }
-
- /* RFC 3581:
- * If message contains "rport" param, put the received port there.
- */
- if (rdata->msg_info.via->rport_param == 0) {
- rdata->msg_info.via->rport_param =
- pj_ntohs(rdata->pkt_info.addr.sin_port);
- }
-
- /* Drop response message if it has more than one Via.
- */
- if (msg->type == PJSIP_RESPONSE_MSG) {
- pjsip_hdr *hdr;
- hdr = (pjsip_hdr*)rdata->msg_info.via->next;
- if (hdr != &msg->hdr) {
- hdr = pjsip_msg_find_hdr(msg, PJSIP_H_VIA, hdr);
- if (hdr) {
- mgr->msg_cb(mgr->endpt, PJSIP_EMULTIPLEVIA, rdata);
- goto finish_process_fragment;
- }
- }
- }
-
- /* Call the transport manager's upstream message callback.
- */
- mgr->msg_cb(mgr->endpt, PJ_SUCCESS, rdata);
-
-
-finish_process_fragment:
- total_processed += msg_fragment_size;
- current_pkt += msg_fragment_size;
- remaining_len -= msg_fragment_size;
-
- } /* while (rdata->pkt_info.len > 0) */
-
-
- return total_processed;
-}
-
-
-/*
- * pjsip_tpmgr_alloc_transport()
- *
- * Get transport suitable to communicate to remote. Create a new one
- * if necessary.
- */
-PJ_DEF(pj_status_t) pjsip_tpmgr_alloc_transport( pjsip_tpmgr *mgr,
- pjsip_transport_type_e type,
- const pj_sockaddr_in *remote,
- pjsip_transport **p_transport)
-{
- transport_key key;
- pjsip_transport *transport;
- pjsip_tpfactory *factory;
- pj_status_t status;
-
- pj_lock_acquire(mgr->lock);
-
- /* First try to get exact destination. */
- key.type = (pj_uint8_t)type;
- key.zero = 0;
- key.addr = pj_ntohl(remote->sin_addr.s_addr);
- key.port = pj_ntohs(remote->sin_port);
-
- transport = pj_hash_get(mgr->table, &key, sizeof(key));
- if (transport != NULL) {
- unsigned flag = pjsip_transport_get_flag_from_type(type);
-
- /* For datagram transports, try lookup with zero address. */
- if (flag & PJSIP_TRANSPORT_DATAGRAM) {
- key.addr = 0;
- key.port = 0;
-
- transport = pj_hash_get(mgr->table, &key, sizeof(key));
- }
- }
-
- if (transport != NULL) {
- /*
- * Transport found!
- */
- pjsip_transport_add_ref(transport);
- pj_lock_release(mgr->lock);
- *p_transport = transport;
- return PJ_SUCCESS;
- }
-
- /*
- * Transport not found!
- * Find factory that can create such transport.
- */
- factory = mgr->factory_list.next;
- while (factory != &mgr->factory_list) {
- if (factory->type == type)
- break;
- factory = factory->next;
- }
-
- if (factory == &mgr->factory_list) {
- /* No factory can create the transport! */
- pj_lock_release(mgr->lock);
- return PJSIP_EUNSUPTRANSPORT;
- }
-
- /* Request factory to create transport. */
- status = factory->create_transport(factory, mgr, mgr->endpt,
- remote, p_transport);
-
- pj_lock_release(mgr->lock);
- return status;
-}
-
-/**
- * Dump transport info.
- */
-PJ_DEF(void) pjsip_tpmgr_dump_transports(pjsip_tpmgr *mgr)
-{
-#if PJ_LOG_MAX_LEVEL >= 3
- pj_hash_iterator_t itr_val;
- pj_hash_iterator_t *itr;
-
- pj_lock_acquire(mgr->lock);
-
- itr = pj_hash_first(mgr->table, &itr_val);
- if (itr) {
- PJ_LOG(3, (THIS_FILE, " Dumping transports:"));
-
- do {
- char src_addr[128], dst_addr[128];
- int src_port, dst_port;
- pjsip_transport *t;
-
- t = pj_hash_this(mgr->table, itr);
- pj_native_strcpy(src_addr, pj_inet_ntoa(t->local_addr.sin_addr));
- src_port = pj_ntohs(t->local_addr.sin_port);
-
- pj_native_strcpy(dst_addr, pj_inet_ntoa(t->rem_addr.sin_addr));
- dst_port = pj_ntohs(t->rem_addr.sin_port);
-
- PJ_LOG(3, (THIS_FILE, " %s %s %s:%d --> %s:%d (refcnt=%d)",
- t->type_name,
- t->obj_name,
- src_addr, src_port,
- dst_addr, dst_port,
- pj_atomic_get(t->ref_cnt)));
-
- itr = pj_hash_next(mgr->table, itr);
- } while (itr);
- }
-
- pj_lock_release(mgr->lock);
-#endif
-}
-
+/* $Id$ */
+/*
+ * Copyright (C) 2003-2006 Benny Prijono <benny@prijono.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#include <pjsip/sip_transport.h>
+#include <pjsip/sip_endpoint.h>
+#include <pjsip/sip_parser.h>
+#include <pjsip/sip_msg.h>
+#include <pjsip/sip_private.h>
+#include <pjsip/sip_errno.h>
+#include <pj/os.h>
+#include <pj/log.h>
+#include <pj/ioqueue.h>
+#include <pj/hash.h>
+#include <pj/string.h>
+#include <pj/pool.h>
+#include <pj/assert.h>
+#include <pj/lock.h>
+
+
+#define THIS_FILE "transport"
+
+/*
+ * Transport manager.
+ */
+struct pjsip_tpmgr
+{
+ pj_hash_table_t *table;
+ pj_lock_t *lock;
+ pjsip_endpoint *endpt;
+ pjsip_tpfactory factory_list;
+ void (*msg_cb)(pjsip_endpoint*, pj_status_t, pjsip_rx_data*);
+};
+
+
+
+/*****************************************************************************
+ *
+ * GENERAL TRANSPORT (NAMES, TYPES, ETC.)
+ *
+ *****************************************************************************/
+
+/*
+ * Transport names.
+ */
+const struct
+{
+ pjsip_transport_type_e type;
+ pj_uint16_t port;
+ pj_str_t name;
+ unsigned flag;
+} transport_names[] =
+{
+ { PJSIP_TRANSPORT_UNSPECIFIED, 0, {NULL, 0}, 0},
+ { PJSIP_TRANSPORT_UDP, 5060, {"UDP", 3}, PJSIP_TRANSPORT_DATAGRAM},
+ { PJSIP_TRANSPORT_TCP, 5060, {"TCP", 3}, PJSIP_TRANSPORT_RELIABLE},
+ { PJSIP_TRANSPORT_TLS, 5061, {"TLS", 3}, PJSIP_TRANSPORT_RELIABLE | PJSIP_TRANSPORT_SECURE},
+ { PJSIP_TRANSPORT_SCTP, 5060, {"SCTP", 4}, PJSIP_TRANSPORT_RELIABLE}
+};
+
+
+/*
+ * Get transport type from name.
+ */
+PJ_DEF(pjsip_transport_type_e)
+pjsip_transport_get_type_from_name(const pj_str_t *name)
+{
+ unsigned i;
+
+ for (i=0; i<PJ_ARRAY_SIZE(transport_names); ++i) {
+ if (pj_stricmp(name, &transport_names[i].name) == 0) {
+ return transport_names[i].type;
+ }
+ }
+
+ pj_assert(!"Invalid transport name");
+ return PJSIP_TRANSPORT_UNSPECIFIED;
+}
+
+
+/*
+ * Get the transport type for the specified flags.
+ */
+PJ_DEF(pjsip_transport_type_e)
+pjsip_transport_get_type_from_flag(unsigned flag)
+{
+ unsigned i;
+
+ for (i=0; i<PJ_ARRAY_SIZE(transport_names); ++i) {
+ if (transport_names[i].flag == flag) {
+ return transport_names[i].type;
+ }
+ }
+
+ pj_assert(!"Invalid transport type");
+ return PJSIP_TRANSPORT_UNSPECIFIED;
+}
+
+PJ_DEF(unsigned)
+pjsip_transport_get_flag_from_type( pjsip_transport_type_e type )
+{
+ PJ_ASSERT_RETURN(type < PJ_ARRAY_SIZE(transport_names), 0);
+ return transport_names[type].flag;
+}
+
+/*
+ * Get the default SIP port number for the specified type.
+ */
+PJ_DEF(int)
+pjsip_transport_get_default_port_for_type(pjsip_transport_type_e type)
+{
+ PJ_ASSERT_RETURN(type < PJ_ARRAY_SIZE(transport_names), 5060);
+ return transport_names[type].port;
+}
+
+
+/*****************************************************************************
+ *
+ * TRANSMIT DATA BUFFER MANIPULATION.
+ *
+ *****************************************************************************/
+
+/*
+ * Create new transmit buffer.
+ */
+PJ_DEF(pj_status_t) pjsip_tx_data_create( pjsip_tpmgr *mgr,
+ pjsip_tx_data **p_tdata )
+{
+ pj_pool_t *pool;
+ pjsip_tx_data *tdata;
+ pj_status_t status;
+
+ PJ_ASSERT_RETURN(mgr && p_tdata, PJ_EINVAL);
+
+ PJ_LOG(5, ("", "pjsip_tx_data_create"));
+
+ pool = pjsip_endpt_create_pool( mgr->endpt, "tdta%p",
+ PJSIP_POOL_LEN_TDATA,
+ PJSIP_POOL_INC_TDATA );
+ if (!pool)
+ return PJ_ENOMEM;
+
+ tdata = pj_pool_zalloc(pool, sizeof(pjsip_tx_data));
+ tdata->pool = pool;
+ tdata->mgr = mgr;
+ pj_snprintf(tdata->obj_name, PJ_MAX_OBJ_NAME, "tdta%p", tdata);
+
+ status = pj_atomic_create(tdata->pool, 0, &tdata->ref_cnt);
+ if (status != PJ_SUCCESS) {
+ pjsip_endpt_destroy_pool( mgr->endpt, tdata->pool );
+ return status;
+ }
+
+ //status = pj_lock_create_simple_mutex(pool, "tdta%p", &tdata->lock);
+ status = pj_lock_create_null_mutex(pool, "tdta%p", &tdata->lock);
+ if (status != PJ_SUCCESS) {
+ pjsip_endpt_destroy_pool( mgr->endpt, tdata->pool );
+ return status;
+ }
+
+ pj_ioqueue_op_key_init(&tdata->op_key.key, sizeof(tdata->op_key));
+
+ *p_tdata = tdata;
+ return PJ_SUCCESS;
+}
+
+
+/*
+ * Add reference to tx buffer.
+ */
+PJ_DEF(void) pjsip_tx_data_add_ref( pjsip_tx_data *tdata )
+{
+ pj_atomic_inc(tdata->ref_cnt);
+}
+
+/*
+ * Decrease transport data reference, destroy it when the reference count
+ * reaches zero.
+ */
+PJ_DEF(void) pjsip_tx_data_dec_ref( pjsip_tx_data *tdata )
+{
+ pj_assert( pj_atomic_get(tdata->ref_cnt) > 0);
+ if (pj_atomic_dec_and_get(tdata->ref_cnt) <= 0) {
+ PJ_LOG(5,(tdata->obj_name, "destroying txdata"));
+ pj_atomic_destroy( tdata->ref_cnt );
+ pj_lock_destroy( tdata->lock );
+ pjsip_endpt_destroy_pool( tdata->mgr->endpt, tdata->pool );
+ }
+}
+
+/*
+ * Invalidate the content of the print buffer to force the message to be
+ * re-printed when sent.
+ */
+PJ_DEF(void) pjsip_tx_data_invalidate_msg( pjsip_tx_data *tdata )
+{
+ tdata->buf.cur = tdata->buf.start;
+}
+
+PJ_DEF(pj_bool_t) pjsip_tx_data_is_valid( pjsip_tx_data *tdata )
+{
+ return tdata->buf.cur != tdata->buf.start;
+}
+
+
+
+/*****************************************************************************
+ *
+ * TRANSPORT KEY
+ *
+ *****************************************************************************/
+
+/*
+ * Transport key for indexing in the hash table.
+ */
+typedef struct transport_key
+{
+ pj_uint8_t type;
+ pj_uint8_t zero;
+ pj_uint16_t port;
+ pj_uint32_t addr;
+} transport_key;
+
+
+/*****************************************************************************
+ *
+ * TRANSPORT
+ *
+ *****************************************************************************/
+
+static void transport_send_callback(pjsip_transport *transport,
+ void *token,
+ pj_ssize_t size)
+{
+ pjsip_tx_data *tdata = token;
+
+ PJ_UNUSED_ARG(transport);
+
+ /* Mark pending off so that app can resend/reuse txdata from inside
+ * the callback.
+ */
+ tdata->is_pending = 0;
+
+ /* Call callback, if any. */
+ if (tdata->cb) {
+ (*tdata->cb)(tdata->token, tdata, size);
+ }
+
+ /* Decrement reference count. */
+ pjsip_tx_data_dec_ref(tdata);
+}
+
+/*
+ * Send a SIP message using the specified transport.
+ */
+PJ_DEF(pj_status_t) pjsip_transport_send( pjsip_transport *tr,
+ pjsip_tx_data *tdata,
+ const pj_sockaddr_in *addr,
+ void *token,
+ void (*cb)(void *token,
+ pjsip_tx_data *tdata,
+ pj_ssize_t))
+{
+ pj_status_t status;
+
+ PJ_ASSERT_RETURN(tr && tdata && addr, PJ_EINVAL);
+
+ /* Is it currently being sent? */
+ if (tdata->is_pending) {
+ pj_assert(!"Invalid operation step!");
+ return PJSIP_EPENDINGTX;
+ }
+
+ /* Allocate buffer if necessary. */
+ if (tdata->buf.start == NULL) {
+ tdata->buf.start = pj_pool_alloc( tdata->pool, PJSIP_MAX_PKT_LEN);
+ tdata->buf.cur = tdata->buf.start;
+ tdata->buf.end = tdata->buf.start + PJSIP_MAX_PKT_LEN;
+ }
+
+ /* Do we need to reprint? */
+ if (!pjsip_tx_data_is_valid(tdata)) {
+ pj_ssize_t size;
+
+ size = pjsip_msg_print( tdata->msg, tdata->buf.start,
+ tdata->buf.end - tdata->buf.start);
+ if (size < 0) {
+ return PJSIP_EMSGTOOLONG;
+ }
+ pj_assert(size != 0);
+ tdata->buf.cur += size;
+ tdata->buf.cur[size] = '\0';
+ }
+
+ /* Save callback data. */
+ tdata->token = token;
+ tdata->cb = cb;
+
+ /* Add reference counter. */
+ pjsip_tx_data_add_ref(tdata);
+
+ /* Mark as pending. */
+ tdata->is_pending = 1;
+
+ /* Send to transport. */
+ status = (*tr->send_msg)(tr, tdata, addr, (void*)tdata,
+ &transport_send_callback);
+
+ if (status != PJ_EPENDING) {
+ tdata->is_pending = 0;
+ pjsip_tx_data_dec_ref(tdata);
+ }
+
+ return status;
+}
+
+static void transport_idle_callback(pj_timer_heap_t *timer_heap,
+ struct pj_timer_entry *entry)
+{
+ pjsip_transport *tp = entry->user_data;
+ pj_assert(tp != NULL);
+
+ PJ_UNUSED_ARG(timer_heap);
+
+ entry->id = PJ_FALSE;
+ pjsip_transport_unregister(tp->tpmgr, tp);
+}
+
+/*
+ * Add ref.
+ */
+PJ_DEF(pj_status_t) pjsip_transport_add_ref( pjsip_transport *tp )
+{
+ PJ_ASSERT_RETURN(tp != NULL, PJ_EINVAL);
+
+ if (pj_atomic_inc_and_get(tp->ref_cnt) == 1) {
+ pj_lock_acquire(tp->tpmgr->lock);
+ /* Verify again. */
+ if (pj_atomic_get(tp->ref_cnt) == 1) {
+ if (tp->idle_timer.id != PJ_FALSE) {
+ pjsip_endpt_cancel_timer(tp->tpmgr->endpt, &tp->idle_timer);
+ tp->idle_timer.id = PJ_FALSE;
+ }
+ }
+ pj_lock_release(tp->tpmgr->lock);
+ }
+
+ return PJ_SUCCESS;
+}
+
+/*
+ * Dec ref.
+ */
+PJ_DEF(pj_status_t) pjsip_transport_dec_ref( pjsip_transport *tp )
+{
+ PJ_ASSERT_RETURN(tp != NULL, PJ_EINVAL);
+
+ pj_assert(pj_atomic_get(tp->ref_cnt) > 0);
+
+ if (pj_atomic_dec_and_get(tp->ref_cnt) == 0) {
+ pj_lock_acquire(tp->tpmgr->lock);
+ /* Verify again. */
+ if (pj_atomic_get(tp->ref_cnt) == 0) {
+ pj_time_val delay = { PJSIP_TRANSPORT_IDLE_TIME, 0 };
+
+ pj_assert(tp->idle_timer.id == 0);
+ tp->idle_timer.id = PJ_TRUE;
+ pjsip_endpt_schedule_timer(tp->tpmgr->endpt, &tp->idle_timer,
+ &delay);
+ }
+ pj_lock_release(tp->tpmgr->lock);
+ }
+
+ return PJ_SUCCESS;
+}
+
+
+/**
+ * Register a transport.
+ */
+PJ_DEF(pj_status_t) pjsip_transport_register( pjsip_tpmgr *mgr,
+ pjsip_transport *tp )
+{
+ transport_key key;
+
+ /* Init. */
+ tp->tpmgr = mgr;
+ pj_memset(&tp->idle_timer, 0, sizeof(tp->idle_timer));
+ tp->idle_timer.user_data = tp;
+ tp->idle_timer.cb = &transport_idle_callback;
+
+ /*
+ * Register to hash table.
+ */
+ key.type = (pj_uint8_t)tp->type;
+ key.zero = 0;
+ key.addr = pj_ntohl(tp->rem_addr.sin_addr.s_addr);
+ key.port = pj_ntohs(tp->rem_addr.sin_port);
+
+ pj_lock_acquire(mgr->lock);
+ pj_hash_set(tp->pool, mgr->table, &key, sizeof(key), tp);
+ pj_lock_release(mgr->lock);
+
+ return PJ_SUCCESS;
+}
+
+
+/**
+ * Unregister transport.
+ */
+PJ_DEF(pj_status_t) pjsip_transport_unregister( pjsip_tpmgr *mgr,
+ pjsip_transport *tp)
+{
+ transport_key key;
+
+ PJ_ASSERT_RETURN(pj_atomic_get(tp->ref_cnt) == 0, PJSIP_EBUSY);
+
+ pj_lock_acquire(tp->lock);
+ pj_lock_acquire(mgr->lock);
+
+ /*
+ * Unregister timer, if any.
+ */
+ pj_assert(tp->idle_timer.id == PJ_FALSE);
+ if (tp->idle_timer.id != PJ_FALSE) {
+ pjsip_endpt_cancel_timer(mgr->endpt, &tp->idle_timer);
+ tp->idle_timer.id = PJ_FALSE;
+ }
+
+ /*
+ * Unregister from hash table.
+ */
+ key.type = (pj_uint8_t)tp->type;
+ key.zero = 0;
+ key.addr = pj_ntohl(tp->rem_addr.sin_addr.s_addr);
+ key.port = pj_ntohs(tp->rem_addr.sin_port);
+
+ pj_hash_set(tp->pool, mgr->table, &key, sizeof(key), NULL);
+
+ pj_lock_release(mgr->lock);
+
+ /* Destroy. */
+ return tp->destroy(tp);
+}
+
+
+
+/*****************************************************************************
+ *
+ * TRANSPORT FACTORY
+ *
+ *****************************************************************************/
+
+
+PJ_DEF(pj_status_t) pjsip_tpmgr_register_tpfactory( pjsip_tpmgr *mgr,
+ pjsip_tpfactory *tpf)
+{
+ pjsip_tpfactory *p;
+ pj_status_t status;
+
+ pj_lock_acquire(mgr->lock);
+
+ /* Check that no factory with the same type has been registered. */
+ status = PJ_SUCCESS;
+ for (p=mgr->factory_list.next; p!=&mgr->factory_list; p=p->next) {
+ if (p->type == tpf->type) {
+ status = PJSIP_ETYPEEXISTS;
+ break;
+ }
+ if (p == tpf) {
+ status = PJ_EEXISTS;
+ break;
+ }
+ }
+
+ if (status != PJ_SUCCESS) {
+ pj_lock_release(mgr->lock);
+ return status;
+ }
+
+ pj_list_insert_before(&mgr->factory_list, tpf);
+
+ pj_lock_release(mgr->lock);
+
+ return PJ_SUCCESS;
+}
+
+
+/**
+ * Unregister factory.
+ */
+PJ_DEF(pj_status_t) pjsip_tpmgr_unregister_tpfactory( pjsip_tpmgr *mgr,
+ pjsip_tpfactory *tpf)
+{
+ pj_lock_acquire(mgr->lock);
+
+ pj_assert(pj_list_find_node(&mgr->factory_list, tpf) == tpf);
+ pj_list_erase(tpf);
+
+ pj_lock_release(mgr->lock);
+
+ return PJ_SUCCESS;
+}
+
+
+/*****************************************************************************
+ *
+ * TRANSPORT MANAGER
+ *
+ *****************************************************************************/
+
+/*
+ * Create a new transport manager.
+ */
+PJ_DEF(pj_status_t) pjsip_tpmgr_create( pj_pool_t *pool,
+ pjsip_endpoint *endpt,
+ void (*cb)(pjsip_endpoint*,
+ pj_status_t,
+ pjsip_rx_data *),
+ pjsip_tpmgr **p_mgr)
+{
+ pjsip_tpmgr *mgr;
+ pj_status_t status;
+
+ PJ_ASSERT_RETURN(pool && endpt && cb && p_mgr, PJ_EINVAL);
+
+ PJ_LOG(5, (THIS_FILE, "pjsip_tpmgr_create()"));
+
+ mgr = pj_pool_zalloc(pool, sizeof(*mgr));
+ mgr->endpt = endpt;
+ mgr->msg_cb = cb;
+ pj_list_init(&mgr->factory_list);
+
+ mgr->table = pj_hash_create(pool, PJSIP_TPMGR_HTABLE_SIZE);
+ if (!mgr->table)
+ return PJ_ENOMEM;
+
+ status = pj_lock_create_recursive_mutex(pool, "tmgr%p", &mgr->lock);
+ if (status != PJ_SUCCESS)
+ return status;
+
+ *p_mgr = mgr;
+ return PJ_SUCCESS;
+}
+
+/*
+ * pjsip_tpmgr_destroy()
+ *
+ * Destroy transport manager.
+ */
+PJ_DEF(pj_status_t) pjsip_tpmgr_destroy( pjsip_tpmgr *mgr )
+{
+ pj_hash_iterator_t itr_val;
+ pj_hash_iterator_t *itr;
+
+ PJ_LOG(5, (THIS_FILE, "pjsip_tpmgr_destroy()"));
+
+ pj_lock_acquire(mgr->lock);
+
+ itr = pj_hash_first(mgr->table, &itr_val);
+ while (itr != NULL) {
+ pj_hash_iterator_t *next;
+ pjsip_transport *transport;
+
+ transport = pj_hash_this(mgr->table, itr);
+
+ next = pj_hash_next(mgr->table, itr);
+
+ pj_atomic_set(transport->ref_cnt, 0);
+ pjsip_transport_unregister(mgr, transport);
+
+ itr = next;
+ }
+
+ pj_lock_release(mgr->lock);
+
+ return PJ_SUCCESS;
+}
+
+
+/*
+ * pjsip_tpmgr_receive_packet()
+ *
+ * Called by tranports when they receive a new packet.
+ */
+PJ_DEF(pj_ssize_t) pjsip_tpmgr_receive_packet( pjsip_tpmgr *mgr,
+ pjsip_rx_data *rdata)
+{
+ pjsip_transport *tr = rdata->tp_info.transport;
+ pj_str_t s;
+
+ char *current_pkt;
+ pj_size_t remaining_len;
+ pj_size_t total_processed = 0;
+
+ /* Check size. */
+ pj_assert(rdata->pkt_info.len > 0);
+ if (rdata->pkt_info.len <= 0)
+ return -1;
+
+ current_pkt = rdata->pkt_info.packet;
+ remaining_len = rdata->pkt_info.len;
+
+ /* Must NULL terminate buffer. This is the requirement of the
+ * parser etc.
+ */
+ current_pkt[remaining_len] = '\0';
+
+ /* Process all message fragments. */
+ while (total_processed < remaining_len) {
+
+ pjsip_msg *msg;
+ pj_size_t msg_fragment_size = 0;
+
+ /* Initialize default fragment size. */
+ msg_fragment_size = remaining_len;
+
+ /* Null terminate packet. */
+
+ /* Clear and init msg_info in rdata.
+ * Endpoint might inspect the values there when we call the callback
+ * to report some errors.
+ */
+ pj_memset(&rdata->msg_info, 0, sizeof(rdata->msg_info));
+ pj_list_init(&rdata->msg_info.parse_err);
+ rdata->msg_info.msg_buf = current_pkt;
+ rdata->msg_info.len = remaining_len;
+
+ /* For TCP transport, check if the whole message has been received. */
+ if ((tr->flag & PJSIP_TRANSPORT_DATAGRAM) == 0) {
+ pj_status_t msg_status;
+ msg_status = pjsip_find_msg(current_pkt, remaining_len, PJ_FALSE,
+ &msg_fragment_size);
+ if (msg_status != PJ_SUCCESS) {
+ if (remaining_len == PJSIP_MAX_PKT_LEN) {
+ mgr->msg_cb(mgr->endpt, PJSIP_ERXOVERFLOW, rdata);
+ /* Exhaust all data. */
+ return rdata->pkt_info.len;
+ } else {
+ /* Not enough data in packet. */
+ return total_processed;
+ }
+ }
+ }
+
+ /* Update msg_info. */
+ rdata->msg_info.len = msg_fragment_size;
+
+ /* Parse the message. */
+ rdata->msg_info.msg = msg =
+ pjsip_parse_rdata( current_pkt, msg_fragment_size, rdata);
+ if (msg == NULL) {
+ mgr->msg_cb(mgr->endpt, PJSIP_EINVALIDMSG, rdata);
+ goto finish_process_fragment;
+ }
+
+ /* Perform basic header checking. */
+ if (rdata->msg_info.call_id.ptr == NULL ||
+ rdata->msg_info.from == NULL ||
+ rdata->msg_info.to == NULL ||
+ rdata->msg_info.via == NULL ||
+ rdata->msg_info.cseq == NULL)
+ {
+ mgr->msg_cb(mgr->endpt, PJSIP_EMISSINGHDR, rdata);
+ goto finish_process_fragment;
+ }
+
+ /* If message is received from address that's different from sent-by,
+ * MUST add received parameter to the via.
+ */
+ s = pj_str(pj_inet_ntoa(rdata->pkt_info.addr.sin_addr));
+ if (pj_strcmp(&s, &rdata->msg_info.via->sent_by.host) != 0) {
+ pj_strdup(rdata->tp_info.pool,
+ &rdata->msg_info.via->recvd_param, &s);
+ }
+
+ /* RFC 3581:
+ * If message contains "rport" param, put the received port there.
+ */
+ if (rdata->msg_info.via->rport_param == 0) {
+ rdata->msg_info.via->rport_param =
+ pj_ntohs(rdata->pkt_info.addr.sin_port);
+ }
+
+ /* Drop response message if it has more than one Via.
+ */
+ if (msg->type == PJSIP_RESPONSE_MSG) {
+ pjsip_hdr *hdr;
+ hdr = (pjsip_hdr*)rdata->msg_info.via->next;
+ if (hdr != &msg->hdr) {
+ hdr = pjsip_msg_find_hdr(msg, PJSIP_H_VIA, hdr);
+ if (hdr) {
+ mgr->msg_cb(mgr->endpt, PJSIP_EMULTIPLEVIA, rdata);
+ goto finish_process_fragment;
+ }
+ }
+ }
+
+ /* Call the transport manager's upstream message callback.
+ */
+ mgr->msg_cb(mgr->endpt, PJ_SUCCESS, rdata);
+
+
+finish_process_fragment:
+ total_processed += msg_fragment_size;
+ current_pkt += msg_fragment_size;
+ remaining_len -= msg_fragment_size;
+
+ } /* while (rdata->pkt_info.len > 0) */
+
+
+ return total_processed;
+}
+
+
+/*
+ * pjsip_tpmgr_alloc_transport()
+ *
+ * Get transport suitable to communicate to remote. Create a new one
+ * if necessary.
+ */
+PJ_DEF(pj_status_t) pjsip_tpmgr_alloc_transport( pjsip_tpmgr *mgr,
+ pjsip_transport_type_e type,
+ const pj_sockaddr_in *remote,
+ pjsip_transport **p_transport)
+{
+ transport_key key;
+ pjsip_transport *transport;
+ pjsip_tpfactory *factory;
+ pj_status_t status;
+
+ pj_lock_acquire(mgr->lock);
+
+ /* First try to get exact destination. */
+ key.type = (pj_uint8_t)type;
+ key.zero = 0;
+ key.addr = pj_ntohl(remote->sin_addr.s_addr);
+ key.port = pj_ntohs(remote->sin_port);
+
+ transport = pj_hash_get(mgr->table, &key, sizeof(key));
+ if (transport != NULL) {
+ unsigned flag = pjsip_transport_get_flag_from_type(type);
+
+ /* For datagram transports, try lookup with zero address. */
+ if (flag & PJSIP_TRANSPORT_DATAGRAM) {
+ key.addr = 0;
+ key.port = 0;
+
+ transport = pj_hash_get(mgr->table, &key, sizeof(key));
+ }
+ }
+
+ if (transport != NULL) {
+ /*
+ * Transport found!
+ */
+ pjsip_transport_add_ref(transport);
+ pj_lock_release(mgr->lock);
+ *p_transport = transport;
+ return PJ_SUCCESS;
+ }
+
+ /*
+ * Transport not found!
+ * Find factory that can create such transport.
+ */
+ factory = mgr->factory_list.next;
+ while (factory != &mgr->factory_list) {
+ if (factory->type == type)
+ break;
+ factory = factory->next;
+ }
+
+ if (factory == &mgr->factory_list) {
+ /* No factory can create the transport! */
+ pj_lock_release(mgr->lock);
+ return PJSIP_EUNSUPTRANSPORT;
+ }
+
+ /* Request factory to create transport. */
+ status = factory->create_transport(factory, mgr, mgr->endpt,
+ remote, p_transport);
+
+ pj_lock_release(mgr->lock);
+ return status;
+}
+
+/**
+ * Dump transport info.
+ */
+PJ_DEF(void) pjsip_tpmgr_dump_transports(pjsip_tpmgr *mgr)
+{
+#if PJ_LOG_MAX_LEVEL >= 3
+ pj_hash_iterator_t itr_val;
+ pj_hash_iterator_t *itr;
+
+ pj_lock_acquire(mgr->lock);
+
+ itr = pj_hash_first(mgr->table, &itr_val);
+ if (itr) {
+ PJ_LOG(3, (THIS_FILE, " Dumping transports:"));
+
+ do {
+ char src_addr[128], dst_addr[128];
+ int src_port, dst_port;
+ pjsip_transport *t;
+
+ t = pj_hash_this(mgr->table, itr);
+ pj_native_strcpy(src_addr, pj_inet_ntoa(t->local_addr.sin_addr));
+ src_port = pj_ntohs(t->local_addr.sin_port);
+
+ pj_native_strcpy(dst_addr, pj_inet_ntoa(t->rem_addr.sin_addr));
+ dst_port = pj_ntohs(t->rem_addr.sin_port);
+
+ PJ_LOG(3, (THIS_FILE, " %s %s %s:%d --> %s:%d (refcnt=%d)",
+ t->type_name,
+ t->obj_name,
+ src_addr, src_port,
+ dst_addr, dst_port,
+ pj_atomic_get(t->ref_cnt)));
+
+ itr = pj_hash_next(mgr->table, itr);
+ } while (itr);
+ }
+
+ pj_lock_release(mgr->lock);
+#endif
+}
+
diff --git a/pjsip/src/pjsip/sip_transport_udp.c b/pjsip/src/pjsip/sip_transport_udp.c
index a6729ffa..2dc835a3 100644
--- a/pjsip/src/pjsip/sip_transport_udp.c
+++ b/pjsip/src/pjsip/sip_transport_udp.c
@@ -1,410 +1,410 @@
-/* $Id: $ */
-/*
- * Copyright (C) 2003-2006 Benny Prijono <benny@prijono.org>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
-#include <pjsip/sip_transport.h>
-#include <pjsip/sip_endpoint.h>
-#include <pjsip/sip_errno.h>
-#include <pj/pool.h>
-#include <pj/sock.h>
-#include <pj/os.h>
-#include <pj/lock.h>
-#include <pj/string.h>
-#include <pj/assert.h>
-
-
-/* Struct udp_transport "inherits" struct pjsip_transport */
-struct udp_transport
-{
- pjsip_transport base;
- pj_sock_t sock;
- pj_ioqueue_key_t *key;
- int rdata_cnt;
- pjsip_rx_data **rdata;
- int is_closing;
-};
-
-
-/*
- * on_read_complete()
- *
- * This is callback notification from ioqueue that a pending recvfrom()
- * operation has completed.
- */
-static void on_read_complete( pj_ioqueue_key_t *key,
- pj_ioqueue_op_key_t *op_key,
- pj_ssize_t bytes_read)
-{
- enum { MAX_IMMEDIATE_PACKET = 10 };
- pjsip_rx_data_op_key *rdata_op_key = (pjsip_rx_data_op_key*) op_key;
- pjsip_rx_data *rdata = rdata_op_key->rdata;
- struct udp_transport *tp = (struct udp_transport*)rdata->tp_info.transport;
- int i;
- pj_status_t status;
-
- /* Don't do anything if transport is closing. */
- if (tp->is_closing)
- return;
-
- /*
- * The idea of the loop is to process immediate data received by
- * pj_ioqueue_recvfrom(), as long as i < MAX_IMMEDIATE_PACKET. When
- * i is >= MAX_IMMEDIATE_PACKET, we force the recvfrom() operation to
- * complete asynchronously, to allow other sockets to get their data.
- */
- for (i=0;; ++i) {
- pj_uint32_t flags;
-
- /* Report the packet to transport manager. */
- if (bytes_read > 0) {
- pj_size_t size_eaten;
-
- rdata->pkt_info.len = bytes_read;
- rdata->pkt_info.zero = 0;
- pj_gettimeofday(&rdata->pkt_info.timestamp);
-
- size_eaten =
- pjsip_tpmgr_receive_packet(rdata->tp_info.transport->tpmgr,
- rdata);
-
- if (size_eaten < 0) {
- pj_assert(!"It shouldn't happen!");
- size_eaten = rdata->pkt_info.len;
- }
-
- /* Since this is UDP, the whole buffer is the message. */
- rdata->pkt_info.len = 0;
-
- } else if (bytes_read == 0) {
- /* TODO: */
- } else {
- /* Report error to endpoint. */
- PJSIP_ENDPT_LOG_ERROR((rdata->tp_info.transport->endpt,
- rdata->tp_info.transport->obj_name,
- -bytes_read,
- "Warning: pj_ioqueue_recvfrom()"
- " callback error"));
- }
-
- if (i >= MAX_IMMEDIATE_PACKET) {
- /* Force ioqueue_recvfrom() to return PJ_EPENDING */
- flags = PJ_IOQUEUE_ALWAYS_ASYNC;
- } else {
- flags = 0;
- }
-
- /* Read next packet. */
- bytes_read = sizeof(rdata->pkt_info.packet);
- rdata->pkt_info.addr_len = sizeof(rdata->pkt_info.addr);
- status = pj_ioqueue_recvfrom(key, op_key,
- rdata->pkt_info.packet,
- &bytes_read, flags,
- &rdata->pkt_info.addr,
- &rdata->pkt_info.addr_len);
-
- if (status == PJ_SUCCESS) {
- /* Continue loop. */
- pj_assert(i < MAX_IMMEDIATE_PACKET);
-
- } else if (status == PJ_EPENDING) {
- break;
-
- } else {
-
- if (i < MAX_IMMEDIATE_PACKET) {
- /* Report error to endpoint. */
- PJSIP_ENDPT_LOG_ERROR((rdata->tp_info.transport->endpt,
- rdata->tp_info.transport->obj_name,
- status,
- "Warning: pj_ioqueue_recvfrom error"));
- /* Continue loop. */
- bytes_read = 0;
- } else {
- /* This is fatal error.
- * Ioqueue operation will stop for this transport!
- */
- PJSIP_ENDPT_LOG_ERROR((rdata->tp_info.transport->endpt,
- rdata->tp_info.transport->obj_name,
- status,
- "FATAL: pj_ioqueue_recvfrom() error, "
- "UDP transport stopping! Error"));
- break;
- }
- }
- }
-}
-
-/*
- * on_write_complete()
- *
- * This is callback notification from ioqueue that a pending sendto()
- * operation has completed.
- */
-static void on_write_complete( pj_ioqueue_key_t *key,
- pj_ioqueue_op_key_t *op_key,
- pj_ssize_t bytes_sent)
-{
- struct udp_transport *tp = pj_ioqueue_get_user_data(key);
- pjsip_tx_data_op_key *tdata_op_key = (pjsip_tx_data_op_key*)op_key;
-
- tdata_op_key->tdata = NULL;
-
- if (tdata_op_key->callback) {
- tdata_op_key->callback(&tp->base, tdata_op_key->token, bytes_sent);
- }
-}
-
-/*
- * transport_send_msg()
- *
- * This function is called by transport manager (by transport->send_msg())
- * to send outgoing message.
- */
-static pj_status_t transport_send_msg( pjsip_transport *transport,
- pjsip_tx_data *tdata,
- const pj_sockaddr_in *rem_addr,
- void *token,
- void (*callback)(pjsip_transport*,
- void *token,
- pj_ssize_t))
-{
- struct udp_transport *tp = (struct udp_transport*)transport;
- pj_ssize_t size;
-
- PJ_ASSERT_RETURN(transport && tdata, PJ_EINVAL);
- PJ_ASSERT_RETURN(tdata->op_key.tdata == NULL, PJSIP_EPENDINGTX);
-
- /* Init op key. */
- tdata->op_key.tdata = tdata;
- tdata->op_key.token = token;
- tdata->op_key.callback = callback;
-
- /* Send to ioqueue! */
- size = tdata->buf.cur - tdata->buf.start;
- return pj_ioqueue_sendto(tp->key, (pj_ioqueue_op_key_t*)&tdata->op_key,
- tdata->buf.start, &size, 0,
- rem_addr, (rem_addr ? sizeof(pj_sockaddr_in):0));
-}
-
-/*
- * transport_destroy()
- *
- * This function is called by transport manager (by transport->destroy()).
- */
-static pj_status_t transport_destroy( pjsip_transport *transport )
-{
- struct udp_transport *tp = (struct udp_transport*)transport;
- int i;
-
- /* Mark this transport as closing. */
- tp->is_closing = 1;
-
- /* Cancel all pending operations. */
- for (i=0; i<tp->rdata_cnt; ++i) {
- pj_ioqueue_post_completion(tp->key,
- &tp->rdata[i]->tp_info.op_key.op_key, -1);
- }
-
- /* Unregister from ioqueue. */
- if (tp->key)
- pj_ioqueue_unregister(tp->key);
-
- /* Close socket. */
- if (tp->sock && tp->sock != PJ_INVALID_SOCKET)
- pj_sock_close(tp->sock);
-
- /* Destroy reference counter. */
- if (tp->base.ref_cnt)
- pj_atomic_destroy(tp->base.ref_cnt);
-
- /* Destroy lock */
- if (tp->base.lock)
- pj_lock_destroy(tp->base.lock);
-
- /* Destroy pool. */
- pjsip_endpt_destroy_pool(tp->base.endpt, tp->base.pool);
-
- return PJ_SUCCESS;
-}
-
-
-/*
- * pjsip_udp_transport_attach()
- *
- * Attach UDP socket and start transport.
- */
-PJ_DEF(pj_status_t) pjsip_udp_transport_attach( pjsip_endpoint *endpt,
- pj_sock_t sock,
- const pj_sockaddr_in *pub_addr,
- unsigned async_cnt,
- pjsip_transport **p_transport)
-{
- pj_pool_t *pool;
- struct udp_transport *tp;
- pj_ioqueue_t *ioqueue;
- pj_ioqueue_callback ioqueue_cb;
- unsigned i;
- int addrlen;
- pj_status_t status;
-
- /* Create pool. */
- pool = pjsip_endpt_create_pool(endpt, "udp%p", PJSIP_POOL_LEN_TRANSPORT,
- PJSIP_POOL_INC_TRANSPORT);
- if (!pool)
- return PJ_ENOMEM;
-
- tp = pj_pool_zalloc(pool, sizeof(struct udp_transport));
- tp->base.pool = pool;
- tp->base.endpt = endpt;
-
- /* Init type, type_name, and flag */
- tp->base.type = PJSIP_TRANSPORT_UDP;
- pj_native_strcpy(tp->base.type_name, "UDP");
- tp->base.flag = pjsip_transport_get_flag_from_type(PJSIP_TRANSPORT_UDP);
-
- /* Init addresses. */
- addrlen = sizeof(tp->base.local_addr);
- status = pj_sock_getsockname(sock, &tp->base.local_addr, &addrlen);
- if (status != PJ_SUCCESS) {
- pjsip_endpt_destroy_pool(endpt, pool);
- return status;
- }
- pj_memcpy(&tp->base.public_addr, pub_addr, sizeof(pj_sockaddr_in));
- tp->base.rem_addr.sin_family = PJ_AF_INET;
-
- /* Init reference counter. */
- status = pj_atomic_create(pool, 0, &tp->base.ref_cnt);
- if (status != PJ_SUCCESS)
- goto on_error;
-
- /* Init lock. */
- status = pj_lock_create_recursive_mutex(pool, "udp%p", &tp->base.lock);
- if (status != PJ_SUCCESS)
- goto on_error;
-
- /* Attach socket. */
- tp->sock = sock;
-
- /* Register to ioqueue. */
- ioqueue = pjsip_endpt_get_ioqueue(endpt);
- pj_memset(&ioqueue_cb, 0, sizeof(ioqueue_cb));
- ioqueue_cb.on_read_complete = &on_read_complete;
- ioqueue_cb.on_write_complete = &on_write_complete;
- status = pj_ioqueue_register_sock(pool, ioqueue, tp->sock, tp,
- &ioqueue_cb, &tp->key);
- if (status != PJ_SUCCESS)
- goto on_error;
-
- /* Set functions. */
- tp->base.send_msg = &transport_send_msg;
- tp->base.destroy = &transport_destroy;
-
- /* This is a permanent transport, so we initialize the ref count
- * to one so that transport manager don't destroy this transport
- * when there's no user!
- */
- pj_atomic_inc(tp->base.ref_cnt);
-
- /* Register to transport manager. */
- tp->base.tpmgr = pjsip_endpt_get_tpmgr(endpt);
- status = pjsip_transport_register( tp->base.tpmgr, (pjsip_transport*)tp);
- if (status != PJ_SUCCESS)
- goto on_error;
-
-
- /* Create rdata and put it in the array. */
- tp->rdata_cnt = 0;
- for (i=0; i<async_cnt; ++i) {
- pj_pool_t *rdata_pool = pjsip_endpt_create_pool(endpt, "rtd%p",
- PJSIP_POOL_LEN_RDATA,
- PJSIP_POOL_INC_RDATA);
- if (!rdata_pool) {
- pj_atomic_set(tp->base.ref_cnt, 0);
- pjsip_transport_unregister(tp->base.tpmgr, &tp->base);
- return PJ_ENOMEM;
- }
-
- tp->rdata[i] = pj_pool_zalloc(rdata_pool, sizeof(pjsip_rx_data));
- tp->rdata[i]->tp_info.pool = rdata_pool;
- tp->rdata[i]->tp_info.transport = &tp->base;
- pj_ioqueue_op_key_init(&tp->rdata[i]->tp_info.op_key.op_key,
- sizeof(pj_ioqueue_op_key_t));
-
- tp->rdata_cnt++;
- }
-
- /* Start reading the ioqueue. */
- for (i=0; i<async_cnt; ++i) {
- pj_ssize_t size;
-
- size = sizeof(tp->rdata[i]->pkt_info.packet);
- tp->rdata[i]->pkt_info.addr_len = sizeof(tp->rdata[i]->pkt_info.addr);
- status = pj_ioqueue_recvfrom(tp->key,
- &tp->rdata[i]->tp_info.op_key.op_key,
- tp->rdata[i]->pkt_info.packet,
- &size, PJ_IOQUEUE_ALWAYS_ASYNC,
- &tp->rdata[i]->pkt_info.addr,
- &tp->rdata[i]->pkt_info.addr_len);
- if (status == PJ_SUCCESS) {
- pj_assert(!"Shouldn't happen because PJ_IOQUEUE_ALWAYS_ASYNC!");
- on_read_complete(tp->key, &tp->rdata[i]->tp_info.op_key.op_key,
- size);
- } else if (status != PJ_EPENDING) {
- /* Error! */
- pjsip_transport_unregister(tp->base.tpmgr, &tp->base);
- return status;
- }
- }
-
- /* Done. */
- *p_transport = &tp->base;
- return PJ_SUCCESS;
-
-on_error:
- transport_destroy((pjsip_transport*)tp);
- return status;
-}
-
-/*
- * pjsip_udp_transport_start()
- *
- * Start an UDP transport/listener.
- */
-PJ_DEF(pj_status_t) pjsip_udp_transport_start( pjsip_endpoint *endpt,
- const pj_sockaddr_in *local,
- const pj_sockaddr_in *pub_addr,
- unsigned async_cnt,
- pjsip_transport **p_transport)
-{
- pj_sock_t sock;
- pj_status_t status;
-
- status = pj_sock_socket(PJ_AF_INET, PJ_SOCK_DGRAM, 0, &sock);
- if (status != PJ_SUCCESS)
- return status;
-
- status = pj_sock_bind(sock, local, sizeof(*local));
- if (status != PJ_SUCCESS) {
- pj_sock_close(sock);
- return status;
- }
-
- return pjsip_udp_transport_attach( endpt, sock, pub_addr, async_cnt,
- p_transport );
-}
-
-
+/* $Id: $ */
+/*
+ * Copyright (C) 2003-2006 Benny Prijono <benny@prijono.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#include <pjsip/sip_transport.h>
+#include <pjsip/sip_endpoint.h>
+#include <pjsip/sip_errno.h>
+#include <pj/pool.h>
+#include <pj/sock.h>
+#include <pj/os.h>
+#include <pj/lock.h>
+#include <pj/string.h>
+#include <pj/assert.h>
+
+
+/* Struct udp_transport "inherits" struct pjsip_transport */
+struct udp_transport
+{
+ pjsip_transport base;
+ pj_sock_t sock;
+ pj_ioqueue_key_t *key;
+ int rdata_cnt;
+ pjsip_rx_data **rdata;
+ int is_closing;
+};
+
+
+/*
+ * on_read_complete()
+ *
+ * This is callback notification from ioqueue that a pending recvfrom()
+ * operation has completed.
+ */
+static void on_read_complete( pj_ioqueue_key_t *key,
+ pj_ioqueue_op_key_t *op_key,
+ pj_ssize_t bytes_read)
+{
+ enum { MAX_IMMEDIATE_PACKET = 10 };
+ pjsip_rx_data_op_key *rdata_op_key = (pjsip_rx_data_op_key*) op_key;
+ pjsip_rx_data *rdata = rdata_op_key->rdata;
+ struct udp_transport *tp = (struct udp_transport*)rdata->tp_info.transport;
+ int i;
+ pj_status_t status;
+
+ /* Don't do anything if transport is closing. */
+ if (tp->is_closing)
+ return;
+
+ /*
+ * The idea of the loop is to process immediate data received by
+ * pj_ioqueue_recvfrom(), as long as i < MAX_IMMEDIATE_PACKET. When
+ * i is >= MAX_IMMEDIATE_PACKET, we force the recvfrom() operation to
+ * complete asynchronously, to allow other sockets to get their data.
+ */
+ for (i=0;; ++i) {
+ pj_uint32_t flags;
+
+ /* Report the packet to transport manager. */
+ if (bytes_read > 0) {
+ pj_size_t size_eaten;
+
+ rdata->pkt_info.len = bytes_read;
+ rdata->pkt_info.zero = 0;
+ pj_gettimeofday(&rdata->pkt_info.timestamp);
+
+ size_eaten =
+ pjsip_tpmgr_receive_packet(rdata->tp_info.transport->tpmgr,
+ rdata);
+
+ if (size_eaten < 0) {
+ pj_assert(!"It shouldn't happen!");
+ size_eaten = rdata->pkt_info.len;
+ }
+
+ /* Since this is UDP, the whole buffer is the message. */
+ rdata->pkt_info.len = 0;
+
+ } else if (bytes_read == 0) {
+ /* TODO: */
+ } else {
+ /* Report error to endpoint. */
+ PJSIP_ENDPT_LOG_ERROR((rdata->tp_info.transport->endpt,
+ rdata->tp_info.transport->obj_name,
+ -bytes_read,
+ "Warning: pj_ioqueue_recvfrom()"
+ " callback error"));
+ }
+
+ if (i >= MAX_IMMEDIATE_PACKET) {
+ /* Force ioqueue_recvfrom() to return PJ_EPENDING */
+ flags = PJ_IOQUEUE_ALWAYS_ASYNC;
+ } else {
+ flags = 0;
+ }
+
+ /* Read next packet. */
+ bytes_read = sizeof(rdata->pkt_info.packet);
+ rdata->pkt_info.addr_len = sizeof(rdata->pkt_info.addr);
+ status = pj_ioqueue_recvfrom(key, op_key,
+ rdata->pkt_info.packet,
+ &bytes_read, flags,
+ &rdata->pkt_info.addr,
+ &rdata->pkt_info.addr_len);
+
+ if (status == PJ_SUCCESS) {
+ /* Continue loop. */
+ pj_assert(i < MAX_IMMEDIATE_PACKET);
+
+ } else if (status == PJ_EPENDING) {
+ break;
+
+ } else {
+
+ if (i < MAX_IMMEDIATE_PACKET) {
+ /* Report error to endpoint. */
+ PJSIP_ENDPT_LOG_ERROR((rdata->tp_info.transport->endpt,
+ rdata->tp_info.transport->obj_name,
+ status,
+ "Warning: pj_ioqueue_recvfrom error"));
+ /* Continue loop. */
+ bytes_read = 0;
+ } else {
+ /* This is fatal error.
+ * Ioqueue operation will stop for this transport!
+ */
+ PJSIP_ENDPT_LOG_ERROR((rdata->tp_info.transport->endpt,
+ rdata->tp_info.transport->obj_name,
+ status,
+ "FATAL: pj_ioqueue_recvfrom() error, "
+ "UDP transport stopping! Error"));
+ break;
+ }
+ }
+ }
+}
+
+/*
+ * on_write_complete()
+ *
+ * This is callback notification from ioqueue that a pending sendto()
+ * operation has completed.
+ */
+static void on_write_complete( pj_ioqueue_key_t *key,
+ pj_ioqueue_op_key_t *op_key,
+ pj_ssize_t bytes_sent)
+{
+ struct udp_transport *tp = pj_ioqueue_get_user_data(key);
+ pjsip_tx_data_op_key *tdata_op_key = (pjsip_tx_data_op_key*)op_key;
+
+ tdata_op_key->tdata = NULL;
+
+ if (tdata_op_key->callback) {
+ tdata_op_key->callback(&tp->base, tdata_op_key->token, bytes_sent);
+ }
+}
+
+/*
+ * transport_send_msg()
+ *
+ * This function is called by transport manager (by transport->send_msg())
+ * to send outgoing message.
+ */
+static pj_status_t transport_send_msg( pjsip_transport *transport,
+ pjsip_tx_data *tdata,
+ const pj_sockaddr_in *rem_addr,
+ void *token,
+ void (*callback)(pjsip_transport*,
+ void *token,
+ pj_ssize_t))
+{
+ struct udp_transport *tp = (struct udp_transport*)transport;
+ pj_ssize_t size;
+
+ PJ_ASSERT_RETURN(transport && tdata, PJ_EINVAL);
+ PJ_ASSERT_RETURN(tdata->op_key.tdata == NULL, PJSIP_EPENDINGTX);
+
+ /* Init op key. */
+ tdata->op_key.tdata = tdata;
+ tdata->op_key.token = token;
+ tdata->op_key.callback = callback;
+
+ /* Send to ioqueue! */
+ size = tdata->buf.cur - tdata->buf.start;
+ return pj_ioqueue_sendto(tp->key, (pj_ioqueue_op_key_t*)&tdata->op_key,
+ tdata->buf.start, &size, 0,
+ rem_addr, (rem_addr ? sizeof(pj_sockaddr_in):0));
+}
+
+/*
+ * transport_destroy()
+ *
+ * This function is called by transport manager (by transport->destroy()).
+ */
+static pj_status_t transport_destroy( pjsip_transport *transport )
+{
+ struct udp_transport *tp = (struct udp_transport*)transport;
+ int i;
+
+ /* Mark this transport as closing. */
+ tp->is_closing = 1;
+
+ /* Cancel all pending operations. */
+ for (i=0; i<tp->rdata_cnt; ++i) {
+ pj_ioqueue_post_completion(tp->key,
+ &tp->rdata[i]->tp_info.op_key.op_key, -1);
+ }
+
+ /* Unregister from ioqueue. */
+ if (tp->key)
+ pj_ioqueue_unregister(tp->key);
+
+ /* Close socket. */
+ if (tp->sock && tp->sock != PJ_INVALID_SOCKET)
+ pj_sock_close(tp->sock);
+
+ /* Destroy reference counter. */
+ if (tp->base.ref_cnt)
+ pj_atomic_destroy(tp->base.ref_cnt);
+
+ /* Destroy lock */
+ if (tp->base.lock)
+ pj_lock_destroy(tp->base.lock);
+
+ /* Destroy pool. */
+ pjsip_endpt_destroy_pool(tp->base.endpt, tp->base.pool);
+
+ return PJ_SUCCESS;
+}
+
+
+/*
+ * pjsip_udp_transport_attach()
+ *
+ * Attach UDP socket and start transport.
+ */
+PJ_DEF(pj_status_t) pjsip_udp_transport_attach( pjsip_endpoint *endpt,
+ pj_sock_t sock,
+ const pj_sockaddr_in *pub_addr,
+ unsigned async_cnt,
+ pjsip_transport **p_transport)
+{
+ pj_pool_t *pool;
+ struct udp_transport *tp;
+ pj_ioqueue_t *ioqueue;
+ pj_ioqueue_callback ioqueue_cb;
+ unsigned i;
+ int addrlen;
+ pj_status_t status;
+
+ /* Create pool. */
+ pool = pjsip_endpt_create_pool(endpt, "udp%p", PJSIP_POOL_LEN_TRANSPORT,
+ PJSIP_POOL_INC_TRANSPORT);
+ if (!pool)
+ return PJ_ENOMEM;
+
+ tp = pj_pool_zalloc(pool, sizeof(struct udp_transport));
+ tp->base.pool = pool;
+ tp->base.endpt = endpt;
+
+ /* Init type, type_name, and flag */
+ tp->base.type = PJSIP_TRANSPORT_UDP;
+ pj_native_strcpy(tp->base.type_name, "UDP");
+ tp->base.flag = pjsip_transport_get_flag_from_type(PJSIP_TRANSPORT_UDP);
+
+ /* Init addresses. */
+ addrlen = sizeof(tp->base.local_addr);
+ status = pj_sock_getsockname(sock, &tp->base.local_addr, &addrlen);
+ if (status != PJ_SUCCESS) {
+ pjsip_endpt_destroy_pool(endpt, pool);
+ return status;
+ }
+ pj_memcpy(&tp->base.public_addr, pub_addr, sizeof(pj_sockaddr_in));
+ tp->base.rem_addr.sin_family = PJ_AF_INET;
+
+ /* Init reference counter. */
+ status = pj_atomic_create(pool, 0, &tp->base.ref_cnt);
+ if (status != PJ_SUCCESS)
+ goto on_error;
+
+ /* Init lock. */
+ status = pj_lock_create_recursive_mutex(pool, "udp%p", &tp->base.lock);
+ if (status != PJ_SUCCESS)
+ goto on_error;
+
+ /* Attach socket. */
+ tp->sock = sock;
+
+ /* Register to ioqueue. */
+ ioqueue = pjsip_endpt_get_ioqueue(endpt);
+ pj_memset(&ioqueue_cb, 0, sizeof(ioqueue_cb));
+ ioqueue_cb.on_read_complete = &on_read_complete;
+ ioqueue_cb.on_write_complete = &on_write_complete;
+ status = pj_ioqueue_register_sock(pool, ioqueue, tp->sock, tp,
+ &ioqueue_cb, &tp->key);
+ if (status != PJ_SUCCESS)
+ goto on_error;
+
+ /* Set functions. */
+ tp->base.send_msg = &transport_send_msg;
+ tp->base.destroy = &transport_destroy;
+
+ /* This is a permanent transport, so we initialize the ref count
+ * to one so that transport manager don't destroy this transport
+ * when there's no user!
+ */
+ pj_atomic_inc(tp->base.ref_cnt);
+
+ /* Register to transport manager. */
+ tp->base.tpmgr = pjsip_endpt_get_tpmgr(endpt);
+ status = pjsip_transport_register( tp->base.tpmgr, (pjsip_transport*)tp);
+ if (status != PJ_SUCCESS)
+ goto on_error;
+
+
+ /* Create rdata and put it in the array. */
+ tp->rdata_cnt = 0;
+ for (i=0; i<async_cnt; ++i) {
+ pj_pool_t *rdata_pool = pjsip_endpt_create_pool(endpt, "rtd%p",
+ PJSIP_POOL_LEN_RDATA,
+ PJSIP_POOL_INC_RDATA);
+ if (!rdata_pool) {
+ pj_atomic_set(tp->base.ref_cnt, 0);
+ pjsip_transport_unregister(tp->base.tpmgr, &tp->base);
+ return PJ_ENOMEM;
+ }
+
+ tp->rdata[i] = pj_pool_zalloc(rdata_pool, sizeof(pjsip_rx_data));
+ tp->rdata[i]->tp_info.pool = rdata_pool;
+ tp->rdata[i]->tp_info.transport = &tp->base;
+ pj_ioqueue_op_key_init(&tp->rdata[i]->tp_info.op_key.op_key,
+ sizeof(pj_ioqueue_op_key_t));
+
+ tp->rdata_cnt++;
+ }
+
+ /* Start reading the ioqueue. */
+ for (i=0; i<async_cnt; ++i) {
+ pj_ssize_t size;
+
+ size = sizeof(tp->rdata[i]->pkt_info.packet);
+ tp->rdata[i]->pkt_info.addr_len = sizeof(tp->rdata[i]->pkt_info.addr);
+ status = pj_ioqueue_recvfrom(tp->key,
+ &tp->rdata[i]->tp_info.op_key.op_key,
+ tp->rdata[i]->pkt_info.packet,
+ &size, PJ_IOQUEUE_ALWAYS_ASYNC,
+ &tp->rdata[i]->pkt_info.addr,
+ &tp->rdata[i]->pkt_info.addr_len);
+ if (status == PJ_SUCCESS) {
+ pj_assert(!"Shouldn't happen because PJ_IOQUEUE_ALWAYS_ASYNC!");
+ on_read_complete(tp->key, &tp->rdata[i]->tp_info.op_key.op_key,
+ size);
+ } else if (status != PJ_EPENDING) {
+ /* Error! */
+ pjsip_transport_unregister(tp->base.tpmgr, &tp->base);
+ return status;
+ }
+ }
+
+ /* Done. */
+ *p_transport = &tp->base;
+ return PJ_SUCCESS;
+
+on_error:
+ transport_destroy((pjsip_transport*)tp);
+ return status;
+}
+
+/*
+ * pjsip_udp_transport_start()
+ *
+ * Start an UDP transport/listener.
+ */
+PJ_DEF(pj_status_t) pjsip_udp_transport_start( pjsip_endpoint *endpt,
+ const pj_sockaddr_in *local,
+ const pj_sockaddr_in *pub_addr,
+ unsigned async_cnt,
+ pjsip_transport **p_transport)
+{
+ pj_sock_t sock;
+ pj_status_t status;
+
+ status = pj_sock_socket(PJ_AF_INET, PJ_SOCK_DGRAM, 0, &sock);
+ if (status != PJ_SUCCESS)
+ return status;
+
+ status = pj_sock_bind(sock, local, sizeof(*local));
+ if (status != PJ_SUCCESS) {
+ pj_sock_close(sock);
+ return status;
+ }
+
+ return pjsip_udp_transport_attach( endpt, sock, pub_addr, async_cnt,
+ p_transport );
+}
+
+
diff --git a/pjsip/src/pjsip/sip_uri.c b/pjsip/src/pjsip/sip_uri.c
index b292fea0..ab25880a 100644
--- a/pjsip/src/pjsip/sip_uri.c
+++ b/pjsip/src/pjsip/sip_uri.c
@@ -1,535 +1,570 @@
-/* $Id$ */
-/*
- * Copyright (C) 2003-2006 Benny Prijono <benny@prijono.org>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
-#include <pjsip/sip_uri.h>
-#include <pjsip/sip_msg.h>
-#include <pjsip/sip_parser.h>
-#include <pjsip/print_util.h>
-#include <pjsip/sip_errno.h>
-#include <pjlib-util/string.h>
-#include <pj/string.h>
-#include <pj/pool.h>
-#include <pj/assert.h>
-
-/*
- * Generic parameter manipulation.
- */
-PJ_DEF(pjsip_param*) pjsip_param_find( pjsip_param *param_list,
- const pj_str_t *name )
-{
- pjsip_param *p = param_list->next;
- while (p != param_list) {
- if (pj_stricmp(&p->name, name)==0)
- return p;
- p = p->next;
- }
- return NULL;
-}
-
-PJ_DEF(const pjsip_param*) pjsip_param_cfind( const pjsip_param *param_list,
- const pj_str_t *name )
-{
- const pjsip_param *p = param_list->next;
- while (p != param_list) {
- if (pj_stricmp(&p->name, name)==0)
- return p;
- p = p->next;
- }
- return NULL;
-}
-
-PJ_DEF(void) pjsip_param_clone( pj_pool_t *pool, pjsip_param *dst_list,
- const pjsip_param *src_list)
-{
- const pjsip_param *p = src_list->next;
-
- pj_list_init(dst_list);
- while (p != src_list) {
- pjsip_param *new_param = pj_pool_alloc(pool, sizeof(pjsip_param));
- pj_strdup(pool, &new_param->name, &p->name);
- pj_strdup(pool, &new_param->value, &p->value);
- pj_list_insert_before(dst_list, new_param);
- p = p->next;
- }
-}
-
-/*
- * URI stuffs
- */
-#define IS_SIPS(url) ((url)->vptr==&sips_url_vptr)
-
-static const pj_str_t *pjsip_url_get_scheme( const pjsip_url* );
-static const pj_str_t *pjsips_url_get_scheme( const pjsip_url* );
-static const pj_str_t *pjsip_name_addr_get_scheme( const pjsip_name_addr * );
-static void *pjsip_get_uri( pjsip_uri *uri );
-static void *pjsip_name_addr_get_uri( pjsip_name_addr *name );
-
-static pj_str_t sip_str = { "sip", 3 };
-static pj_str_t sips_str = { "sips", 4 };
-
-#ifdef __GNUC__
-# define HAPPY_FLAG (void*)
-#else
-# define HAPPY_FLAG
-#endif
-
-static pjsip_name_addr* pjsip_name_addr_clone( pj_pool_t *pool,
- const pjsip_name_addr *rhs);
-static int pjsip_name_addr_print( pjsip_uri_context_e context,
- const pjsip_name_addr *name,
- char *buf, pj_size_t size);
-static int pjsip_name_addr_compare( pjsip_uri_context_e context,
- const pjsip_name_addr *naddr1,
- const pjsip_name_addr *naddr2);
-static int pjsip_url_print( pjsip_uri_context_e context,
- const pjsip_url *url,
- char *buf, pj_size_t size);
-static int pjsip_url_compare( pjsip_uri_context_e context,
- const pjsip_url *url1, const pjsip_url *url2);
-static pjsip_url* pjsip_url_clone(pj_pool_t *pool, const pjsip_url *rhs);
-
-static pjsip_uri_vptr sip_url_vptr =
-{
- HAPPY_FLAG &pjsip_url_get_scheme,
- HAPPY_FLAG &pjsip_get_uri,
- HAPPY_FLAG &pjsip_url_print,
- HAPPY_FLAG &pjsip_url_compare,
- HAPPY_FLAG &pjsip_url_clone
-};
-
-static pjsip_uri_vptr sips_url_vptr =
-{
- HAPPY_FLAG &pjsips_url_get_scheme,
- HAPPY_FLAG &pjsip_get_uri,
- HAPPY_FLAG &pjsip_url_print,
- HAPPY_FLAG &pjsip_url_compare,
- HAPPY_FLAG &pjsip_url_clone
-};
-
-static pjsip_uri_vptr name_addr_vptr =
-{
- HAPPY_FLAG &pjsip_name_addr_get_scheme,
- HAPPY_FLAG &pjsip_name_addr_get_uri,
- HAPPY_FLAG &pjsip_name_addr_print,
- HAPPY_FLAG &pjsip_name_addr_compare,
- HAPPY_FLAG &pjsip_name_addr_clone
-};
-
-static const pj_str_t *pjsip_url_get_scheme(const pjsip_url *url)
-{
- PJ_UNUSED_ARG(url);
- return &sip_str;
-}
-
-static const pj_str_t *pjsips_url_get_scheme(const pjsip_url *url)
-{
- PJ_UNUSED_ARG(url);
- return &sips_str;
-}
-
-static void *pjsip_get_uri( pjsip_uri *uri )
-{
- return uri;
-}
-
-static void *pjsip_name_addr_get_uri( pjsip_name_addr *name )
-{
- return name->uri;
-}
-
-PJ_DEF(void) pjsip_url_init(pjsip_url *url, int secure)
-{
- pj_memset(url, 0, sizeof(*url));
- url->ttl_param = -1;
- url->vptr = secure ? &sips_url_vptr : &sip_url_vptr;
- pj_list_init(&url->other_param);
- pj_list_init(&url->header_param);
-}
-
-PJ_DEF(pjsip_url*) pjsip_url_create( pj_pool_t *pool, int secure )
-{
- pjsip_url *url = pj_pool_alloc(pool, sizeof(pjsip_url));
- pjsip_url_init(url, secure);
- return url;
-}
-
-static int pjsip_url_print( pjsip_uri_context_e context,
- const pjsip_url *url,
- char *buf, pj_size_t size)
-{
- int printed;
- char *startbuf = buf;
- char *endbuf = buf+size;
- const pj_str_t *scheme;
- pjsip_param *param;
- char hparam_char = '?';
-
- *buf = '\0';
-
- /* Print scheme ("sip:" or "sips:") */
- scheme = pjsip_uri_get_scheme(url);
- copy_advance_check(buf, *scheme);
- *buf++ = ':';
-
- /* Print "user:password@", if any. */
- if (url->user.slen) {
- copy_advance_escape(buf, url->user, pjsip_USER_SPEC);
- if (url->passwd.slen) {
- *buf++ = ':';
- copy_advance_escape(buf, url->passwd, pjsip_PASSWD_SPEC);
- }
-
- *buf++ = '@';
- }
-
- /* Print host. */
- pj_assert(url->host.slen != 0);
- copy_advance_check(buf, url->host);
-
- /* Only print port if it is explicitly specified.
- * Port is not allowed in To and From header.
- */
- /* Unfortunately some UA requires us to send back the port
- * number exactly as it was sent. We don't remember whether an
- * UA has sent us port, so we'll just send the port indiscrimately
- */
- //PJ_TODO(SHOULD_DISALLOW_URI_PORT_IN_FROM_TO_HEADER)
- if (url->port && context != PJSIP_URI_IN_FROMTO_HDR) {
- *buf++ = ':';
- printed = pj_utoa(url->port, buf);
- buf += printed;
- }
-
- /* User param is allowed in all contexes */
- copy_advance_pair_check(buf, ";user=", 6, url->user_param);
-
- /* Method param is only allowed in external/other context. */
- if (context == PJSIP_URI_IN_OTHER) {
- copy_advance_pair_escape(buf, ";method=", 8, url->method_param,
- pjsip_PARAM_CHAR_SPEC);
- }
-
- /* Transport is not allowed in From/To header. */
- if (context != PJSIP_URI_IN_FROMTO_HDR) {
- copy_advance_pair_escape(buf, ";transport=", 11, url->transport_param,
- pjsip_PARAM_CHAR_SPEC);
- }
-
- /* TTL param is not allowed in From, To, Route, and Record-Route header. */
- if (url->ttl_param >= 0 && context != PJSIP_URI_IN_FROMTO_HDR &&
- context != PJSIP_URI_IN_ROUTING_HDR && (endbuf-buf) > 15)
- {
- pj_memcpy(buf, ";ttl=", 5);
- printed = pj_utoa(url->ttl_param, buf+5);
- buf += printed + 5;
- }
-
- /* maddr param is not allowed in From and To header. */
- if (context != PJSIP_URI_IN_FROMTO_HDR) {
- copy_advance_pair_escape(buf, ";maddr=", 7, url->maddr_param,
- pjsip_PARAM_CHAR_SPEC);
- }
-
- /* lr param is not allowed in From, To, and Contact header. */
- if (url->lr_param && context != PJSIP_URI_IN_FROMTO_HDR &&
- context != PJSIP_URI_IN_CONTACT_HDR)
- {
- pj_str_t lr = { ";lr", 3 };
- copy_advance_check(buf, lr);
- }
-
- /* Other param. */
- param = url->other_param.next;
- while (param != &url->other_param) {
- *buf++ = ';';
- copy_advance_escape(buf, param->name, pjsip_PARAM_CHAR_SPEC);
- if (param->value.slen) {
- *buf++ = '=';
- copy_advance_escape(buf, param->value, pjsip_PARAM_CHAR_SPEC);
- }
- param = param->next;
- }
-
- /* Header param. */
- param = url->header_param.next;
- while (param != &url->header_param) {
- *buf++ = hparam_char;
- copy_advance_escape(buf, param->name, pjsip_HDR_CHAR_SPEC);
- if (param->value.slen) {
- *buf++ = '=';
- copy_advance_escape(buf, param->value, pjsip_HDR_CHAR_SPEC);
- }
- param = param->next;
- hparam_char = '&';
- }
-
- *buf = '\0';
- return buf-startbuf;
-}
-
-static pj_status_t pjsip_url_compare( pjsip_uri_context_e context,
- const pjsip_url *url1,
- const pjsip_url *url2)
-{
- const pjsip_param *p1;
-
- /*
- * Compare two SIP URL's according to Section 19.1.4 of RFC 3261.
- */
-
- /* SIP and SIPS URI are never equivalent.
- * Note: just compare the vptr to avoid string comparison.
- * Pretty neat huh!!
- */
- if (url1->vptr != url2->vptr)
- return PJSIP_ECMPSCHEME;
-
- /* Comparison of the userinfo of SIP and SIPS URIs is case-sensitive.
- * This includes userinfo containing passwords or formatted as
- * telephone-subscribers.
- */
- if (pj_strcmp(&url1->user, &url2->user) != 0)
- return PJSIP_ECMPUSER;
- if (pj_strcmp(&url1->passwd, &url2->passwd) != 0)
- return PJSIP_ECMPPASSWD;
-
- /* Comparison of all other components of the URI is
- * case-insensitive unless explicitly defined otherwise.
- */
-
- /* The ordering of parameters and header fields is not significant
- * in comparing SIP and SIPS URIs.
- */
-
- /* Characters other than those in the “reserved” set (see RFC 2396 [5])
- * are equivalent to their “encoding.
- */
-
- /* An IP address that is the result of a DNS lookup of a host name
- * does not match that host name.
- */
- if (pj_stricmp(&url1->host, &url2->host) != 0)
- return PJSIP_ECMPHOST;
-
- /* A URI omitting any component with a default value will not match a URI
- * explicitly containing that component with its default value.
- * For instance, a URI omitting the optional port component will not match
- * a URI explicitly declaring port 5060.
- * The same is true for the transport-parameter, ttl-parameter,
- * user-parameter, and method components.
- */
-
- /* Port is not allowed in To and From header.
- */
- if (context != PJSIP_URI_IN_FROMTO_HDR) {
- if (url1->port != url2->port)
- return PJSIP_ECMPPORT;
- }
- /* Transport is not allowed in From/To header. */
- if (context != PJSIP_URI_IN_FROMTO_HDR) {
- if (pj_stricmp(&url1->transport_param, &url2->transport_param) != 0)
- return PJSIP_ECMPTRANSPORTPRM;
- }
- /* TTL param is not allowed in From, To, Route, and Record-Route header. */
- if (context != PJSIP_URI_IN_FROMTO_HDR &&
- context != PJSIP_URI_IN_ROUTING_HDR)
- {
- if (url1->ttl_param != url2->ttl_param)
- return PJSIP_ECMPTTLPARAM;
- }
- /* User param is allowed in all contexes */
- if (pj_stricmp(&url1->user_param, &url2->user_param) != 0)
- return PJSIP_ECMPUSERPARAM;
- /* Method param is only allowed in external/other context. */
- if (context == PJSIP_URI_IN_OTHER) {
- if (pj_stricmp(&url1->method_param, &url2->method_param) != 0)
- return PJSIP_ECMPMETHODPARAM;
- }
- /* maddr param is not allowed in From and To header. */
- if (context != PJSIP_URI_IN_FROMTO_HDR) {
- if (pj_stricmp(&url1->maddr_param, &url2->maddr_param) != 0)
- return PJSIP_ECMPMADDRPARAM;
- }
-
- /* lr parameter is ignored (?) */
- /* lr param is not allowed in From, To, and Contact header. */
-
-
- /* All other uri-parameters appearing in only one URI are ignored when
- * comparing the URIs.
- */
- p1 = url1->other_param.next;
- while (p1 != &url1->other_param) {
- const pjsip_param *p2;
- p2 = pjsip_param_cfind(&url2->other_param, &p1->name);
- if (p2 ) {
- if (pj_stricmp(&p1->value, &p2->value) != 0)
- return PJSIP_ECMPOTHERPARAM;
- }
-
- p1 = p1->next;
- }
-
- /* URI header components are never ignored. Any present header component
- * MUST be present in both URIs and match for the URIs to match.
- * The matching rules are defined for each header field in Section 20.
- */
- p1 = url1->header_param.next;
- while (p1 != &url1->header_param) {
- const pjsip_param *p2;
- p2 = pjsip_param_cfind(&url2->header_param, &p1->name);
- if (p2) {
- /* It seems too much to compare two header params according to
- * the rule of each header. We'll just compare them string to
- * string..
- */
- PJ_TODO(MORE_COMPLIANT_HEADER_PARAM_COMPARISON_IN_URL);
-
- if (pj_stricmp(&p1->value, &p2->value) != 0)
- return PJSIP_ECMPHEADERPARAM;
- } else {
- return PJSIP_ECMPHEADERPARAM;
- }
- p1 = p1->next;
- }
-
- /* Equal!! Pheuww.. */
- return PJ_SUCCESS;
-}
-
-
-PJ_DEF(void) pjsip_url_assign(pj_pool_t *pool, pjsip_url *url,
- const pjsip_url *rhs)
-{
- pj_strdup( pool, &url->user, &rhs->user);
- pj_strdup( pool, &url->passwd, &rhs->passwd);
- pj_strdup( pool, &url->host, &rhs->host);
- url->port = rhs->port;
- pj_strdup( pool, &url->user_param, &rhs->user_param);
- pj_strdup( pool, &url->method_param, &rhs->method_param);
- pj_strdup( pool, &url->transport_param, &rhs->transport_param);
- url->ttl_param = rhs->ttl_param;
- pj_strdup( pool, &url->maddr_param, &rhs->maddr_param);
- pjsip_param_clone(pool, &url->other_param, &rhs->other_param);
- pjsip_param_clone(pool, &url->header_param, &rhs->header_param);
- url->lr_param = rhs->lr_param;
-}
-
-static pjsip_url* pjsip_url_clone(pj_pool_t *pool, const pjsip_url *rhs)
-{
- pjsip_url *url = pj_pool_alloc(pool, sizeof(pjsip_url));
- if (!url)
- return NULL;
-
- pjsip_url_init(url, IS_SIPS(rhs));
- pjsip_url_assign(pool, url, rhs);
- return url;
-}
-
-static const pj_str_t *pjsip_name_addr_get_scheme(const pjsip_name_addr *name)
-{
- pj_assert(name->uri != NULL);
- return pjsip_uri_get_scheme(name->uri);
-}
-
-PJ_DEF(void) pjsip_name_addr_init(pjsip_name_addr *name)
-{
- name->vptr = &name_addr_vptr;
- name->uri = NULL;
- name->display.slen = 0;
-}
-
-PJ_DEF(pjsip_name_addr*) pjsip_name_addr_create(pj_pool_t *pool)
-{
- pjsip_name_addr *name_addr = pj_pool_alloc(pool, sizeof(pjsip_name_addr));
- pjsip_name_addr_init(name_addr);
- return name_addr;
-}
-
-static int pjsip_name_addr_print( pjsip_uri_context_e context,
- const pjsip_name_addr *name,
- char *buf, pj_size_t size)
-{
- int printed;
- char *startbuf = buf;
- char *endbuf = buf + size;
-
- pj_assert(name->uri != NULL);
-
- if (context != PJSIP_URI_IN_REQ_URI) {
- copy_advance(buf, name->display);
- if (name->display.slen) {
- *buf++ = ' ';
- }
- *buf++ = '<';
- }
-
- printed = pjsip_uri_print(context,name->uri, buf, size-(buf-startbuf));
- if (printed < 1)
- return -1;
- buf += printed;
-
- if (context != PJSIP_URI_IN_REQ_URI) {
- *buf++ = '>';
- }
-
- *buf = '\0';
- return buf-startbuf;
-}
-
-PJ_DEF(void) pjsip_name_addr_assign(pj_pool_t *pool, pjsip_name_addr *dst,
- const pjsip_name_addr *src)
-{
- pj_strdup( pool, &dst->display, &src->display);
- dst->uri = pjsip_uri_clone(pool, src->uri);
-}
-
-static pjsip_name_addr* pjsip_name_addr_clone( pj_pool_t *pool,
- const pjsip_name_addr *rhs)
-{
- pjsip_name_addr *addr = pj_pool_alloc(pool, sizeof(pjsip_name_addr));
- if (!addr)
- return NULL;
-
- pjsip_name_addr_init(addr);
- pjsip_name_addr_assign(pool, addr, rhs);
- return addr;
-}
-
-static int pjsip_name_addr_compare( pjsip_uri_context_e context,
- const pjsip_name_addr *naddr1,
- const pjsip_name_addr *naddr2)
-{
- int d;
-
- /* I'm not sure whether display name is included in the comparison. */
- if (pj_strcmp(&naddr1->display, &naddr2->display) != 0) {
- return -1;
- }
-
- pj_assert( naddr1->uri != NULL );
- pj_assert( naddr2->uri != NULL );
-
- /* Compare name-addr as URL */
- d = pjsip_uri_cmp( context, naddr1->uri, naddr2->uri);
- if (d)
- return d;
-
- return 0;
-}
-
+/* $Id$ */
+/*
+ * Copyright (C) 2003-2006 Benny Prijono <benny@prijono.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#include <pjsip/sip_uri.h>
+#include <pjsip/sip_msg.h>
+#include <pjsip/sip_parser.h>
+#include <pjsip/print_util.h>
+#include <pjsip/sip_errno.h>
+#include <pjlib-util/string.h>
+#include <pj/string.h>
+#include <pj/pool.h>
+#include <pj/assert.h>
+
+/*
+ * Generic parameter manipulation.
+ */
+PJ_DEF(pjsip_param*) pjsip_param_find( pjsip_param *param_list,
+ const pj_str_t *name )
+{
+ pjsip_param *p = param_list->next;
+ while (p != param_list) {
+ if (pj_stricmp(&p->name, name)==0)
+ return p;
+ p = p->next;
+ }
+ return NULL;
+}
+
+PJ_DEF(const pjsip_param*) pjsip_param_cfind( const pjsip_param *param_list,
+ const pj_str_t *name )
+{
+ const pjsip_param *p = param_list->next;
+ while (p != param_list) {
+ if (pj_stricmp(&p->name, name)==0)
+ return p;
+ p = p->next;
+ }
+ return NULL;
+}
+
+PJ_DEF(void) pjsip_param_clone( pj_pool_t *pool, pjsip_param *dst_list,
+ const pjsip_param *src_list)
+{
+ const pjsip_param *p = src_list->next;
+
+ pj_list_init(dst_list);
+ while (p != src_list) {
+ pjsip_param *new_param = pj_pool_alloc(pool, sizeof(pjsip_param));
+ pj_strdup(pool, &new_param->name, &p->name);
+ pj_strdup(pool, &new_param->value, &p->value);
+ pj_list_insert_before(dst_list, new_param);
+ p = p->next;
+ }
+}
+
+
+PJ_DEF(void) pjsip_param_shallow_clone( pj_pool_t *pool,
+ pjsip_param *dst_list,
+ const pjsip_param *src_list)
+{
+ const pjsip_param *p = src_list->next;
+
+ pj_list_init(dst_list);
+ while (p != src_list) {
+ pjsip_param *new_param = pj_pool_alloc(pool, sizeof(pjsip_param));
+ new_param->name = p->name;
+ new_param->value = p->value;
+ pj_list_insert_before(dst_list, new_param);
+ p = p->next;
+ }
+}
+
+PJ_DEF(pj_ssize_t) pjsip_param_print_on( const pjsip_param *param_list,
+ char *buf, pj_size_t size,
+ int sep)
+{
+ const pjsip_param *p = param_list->next;
+ char *startbuf = buf;
+ char *endbuf = buf + size;
+
+ while (p != param_list) {
+ *buf++ = (char)sep;
+ copy_advance_escape(buf, p->name, pjsip_PARAM_CHAR_SPEC);
+ if (p->value.slen) {
+ *buf++ = '=';
+ copy_advance_escape(buf, p->value, pjsip_PARAM_CHAR_SPEC);
+ }
+ p = p->next;
+ }
+ return buf-startbuf;
+}
+
+
+/*
+ * URI stuffs
+ */
+#define IS_SIPS(url) ((url)->vptr==&sips_url_vptr)
+
+static const pj_str_t *pjsip_url_get_scheme( const pjsip_url* );
+static const pj_str_t *pjsips_url_get_scheme( const pjsip_url* );
+static const pj_str_t *pjsip_name_addr_get_scheme( const pjsip_name_addr * );
+static void *pjsip_get_uri( pjsip_uri *uri );
+static void *pjsip_name_addr_get_uri( pjsip_name_addr *name );
+
+static pj_str_t sip_str = { "sip", 3 };
+static pj_str_t sips_str = { "sips", 4 };
+
+#ifdef __GNUC__
+# define HAPPY_FLAG (void*)
+#else
+# define HAPPY_FLAG
+#endif
+
+static pjsip_name_addr* pjsip_name_addr_clone( pj_pool_t *pool,
+ const pjsip_name_addr *rhs);
+static int pjsip_name_addr_print( pjsip_uri_context_e context,
+ const pjsip_name_addr *name,
+ char *buf, pj_size_t size);
+static int pjsip_name_addr_compare( pjsip_uri_context_e context,
+ const pjsip_name_addr *naddr1,
+ const pjsip_name_addr *naddr2);
+static int pjsip_url_print( pjsip_uri_context_e context,
+ const pjsip_url *url,
+ char *buf, pj_size_t size);
+static int pjsip_url_compare( pjsip_uri_context_e context,
+ const pjsip_url *url1, const pjsip_url *url2);
+static pjsip_url* pjsip_url_clone(pj_pool_t *pool, const pjsip_url *rhs);
+
+static pjsip_uri_vptr sip_url_vptr =
+{
+ HAPPY_FLAG &pjsip_url_get_scheme,
+ HAPPY_FLAG &pjsip_get_uri,
+ HAPPY_FLAG &pjsip_url_print,
+ HAPPY_FLAG &pjsip_url_compare,
+ HAPPY_FLAG &pjsip_url_clone
+};
+
+static pjsip_uri_vptr sips_url_vptr =
+{
+ HAPPY_FLAG &pjsips_url_get_scheme,
+ HAPPY_FLAG &pjsip_get_uri,
+ HAPPY_FLAG &pjsip_url_print,
+ HAPPY_FLAG &pjsip_url_compare,
+ HAPPY_FLAG &pjsip_url_clone
+};
+
+static pjsip_uri_vptr name_addr_vptr =
+{
+ HAPPY_FLAG &pjsip_name_addr_get_scheme,
+ HAPPY_FLAG &pjsip_name_addr_get_uri,
+ HAPPY_FLAG &pjsip_name_addr_print,
+ HAPPY_FLAG &pjsip_name_addr_compare,
+ HAPPY_FLAG &pjsip_name_addr_clone
+};
+
+static const pj_str_t *pjsip_url_get_scheme(const pjsip_url *url)
+{
+ PJ_UNUSED_ARG(url);
+ return &sip_str;
+}
+
+static const pj_str_t *pjsips_url_get_scheme(const pjsip_url *url)
+{
+ PJ_UNUSED_ARG(url);
+ return &sips_str;
+}
+
+static void *pjsip_get_uri( pjsip_uri *uri )
+{
+ return uri;
+}
+
+static void *pjsip_name_addr_get_uri( pjsip_name_addr *name )
+{
+ return name->uri;
+}
+
+PJ_DEF(void) pjsip_url_init(pjsip_url *url, int secure)
+{
+ pj_memset(url, 0, sizeof(*url));
+ url->ttl_param = -1;
+ url->vptr = secure ? &sips_url_vptr : &sip_url_vptr;
+ pj_list_init(&url->other_param);
+ pj_list_init(&url->header_param);
+}
+
+PJ_DEF(pjsip_url*) pjsip_url_create( pj_pool_t *pool, int secure )
+{
+ pjsip_url *url = pj_pool_alloc(pool, sizeof(pjsip_url));
+ pjsip_url_init(url, secure);
+ return url;
+}
+
+static int pjsip_url_print( pjsip_uri_context_e context,
+ const pjsip_url *url,
+ char *buf, pj_size_t size)
+{
+ int printed;
+ char *startbuf = buf;
+ char *endbuf = buf+size;
+ const pj_str_t *scheme;
+ pjsip_param *param;
+ char hparam_char = '?';
+
+ *buf = '\0';
+
+ /* Print scheme ("sip:" or "sips:") */
+ scheme = pjsip_uri_get_scheme(url);
+ copy_advance_check(buf, *scheme);
+ *buf++ = ':';
+
+ /* Print "user:password@", if any. */
+ if (url->user.slen) {
+ copy_advance_escape(buf, url->user, pjsip_USER_SPEC);
+ if (url->passwd.slen) {
+ *buf++ = ':';
+ copy_advance_escape(buf, url->passwd, pjsip_PASSWD_SPEC);
+ }
+
+ *buf++ = '@';
+ }
+
+ /* Print host. */
+ pj_assert(url->host.slen != 0);
+ copy_advance_check(buf, url->host);
+
+ /* Only print port if it is explicitly specified.
+ * Port is not allowed in To and From header.
+ */
+ /* Unfortunately some UA requires us to send back the port
+ * number exactly as it was sent. We don't remember whether an
+ * UA has sent us port, so we'll just send the port indiscrimately
+ */
+ //PJ_TODO(SHOULD_DISALLOW_URI_PORT_IN_FROM_TO_HEADER)
+ if (url->port && context != PJSIP_URI_IN_FROMTO_HDR) {
+ *buf++ = ':';
+ printed = pj_utoa(url->port, buf);
+ buf += printed;
+ }
+
+ /* User param is allowed in all contexes */
+ copy_advance_pair_check(buf, ";user=", 6, url->user_param);
+
+ /* Method param is only allowed in external/other context. */
+ if (context == PJSIP_URI_IN_OTHER) {
+ copy_advance_pair_escape(buf, ";method=", 8, url->method_param,
+ pjsip_PARAM_CHAR_SPEC);
+ }
+
+ /* Transport is not allowed in From/To header. */
+ if (context != PJSIP_URI_IN_FROMTO_HDR) {
+ copy_advance_pair_escape(buf, ";transport=", 11, url->transport_param,
+ pjsip_PARAM_CHAR_SPEC);
+ }
+
+ /* TTL param is not allowed in From, To, Route, and Record-Route header. */
+ if (url->ttl_param >= 0 && context != PJSIP_URI_IN_FROMTO_HDR &&
+ context != PJSIP_URI_IN_ROUTING_HDR && (endbuf-buf) > 15)
+ {
+ pj_memcpy(buf, ";ttl=", 5);
+ printed = pj_utoa(url->ttl_param, buf+5);
+ buf += printed + 5;
+ }
+
+ /* maddr param is not allowed in From and To header. */
+ if (context != PJSIP_URI_IN_FROMTO_HDR) {
+ copy_advance_pair_escape(buf, ";maddr=", 7, url->maddr_param,
+ pjsip_PARAM_CHAR_SPEC);
+ }
+
+ /* lr param is not allowed in From, To, and Contact header. */
+ if (url->lr_param && context != PJSIP_URI_IN_FROMTO_HDR &&
+ context != PJSIP_URI_IN_CONTACT_HDR)
+ {
+ pj_str_t lr = { ";lr", 3 };
+ copy_advance_check(buf, lr);
+ }
+
+ /* Other param. */
+ printed = pjsip_param_print_on(&url->other_param, buf, endbuf-buf, ';');
+ if (printed < 0)
+ return -1;
+ buf += printed;
+
+ /* Header param. */
+ param = url->header_param.next;
+ while (param != &url->header_param) {
+ *buf++ = hparam_char;
+ copy_advance_escape(buf, param->name, pjsip_HDR_CHAR_SPEC);
+ if (param->value.slen) {
+ *buf++ = '=';
+ copy_advance_escape(buf, param->value, pjsip_HDR_CHAR_SPEC);
+ }
+ param = param->next;
+ hparam_char = '&';
+ }
+
+ *buf = '\0';
+ return buf-startbuf;
+}
+
+static pj_status_t pjsip_url_compare( pjsip_uri_context_e context,
+ const pjsip_url *url1,
+ const pjsip_url *url2)
+{
+ const pjsip_param *p1;
+
+ /*
+ * Compare two SIP URL's according to Section 19.1.4 of RFC 3261.
+ */
+
+ /* SIP and SIPS URI are never equivalent.
+ * Note: just compare the vptr to avoid string comparison.
+ * Pretty neat huh!!
+ */
+ if (url1->vptr != url2->vptr)
+ return PJSIP_ECMPSCHEME;
+
+ /* Comparison of the userinfo of SIP and SIPS URIs is case-sensitive.
+ * This includes userinfo containing passwords or formatted as
+ * telephone-subscribers.
+ */
+ if (pj_strcmp(&url1->user, &url2->user) != 0)
+ return PJSIP_ECMPUSER;
+ if (pj_strcmp(&url1->passwd, &url2->passwd) != 0)
+ return PJSIP_ECMPPASSWD;
+
+ /* Comparison of all other components of the URI is
+ * case-insensitive unless explicitly defined otherwise.
+ */
+
+ /* The ordering of parameters and header fields is not significant
+ * in comparing SIP and SIPS URIs.
+ */
+
+ /* Characters other than those in the “reserved” set (see RFC 2396 [5])
+ * are equivalent to their “encoding.
+ */
+
+ /* An IP address that is the result of a DNS lookup of a host name
+ * does not match that host name.
+ */
+ if (pj_stricmp(&url1->host, &url2->host) != 0)
+ return PJSIP_ECMPHOST;
+
+ /* A URI omitting any component with a default value will not match a URI
+ * explicitly containing that component with its default value.
+ * For instance, a URI omitting the optional port component will not match
+ * a URI explicitly declaring port 5060.
+ * The same is true for the transport-parameter, ttl-parameter,
+ * user-parameter, and method components.
+ */
+
+ /* Port is not allowed in To and From header.
+ */
+ if (context != PJSIP_URI_IN_FROMTO_HDR) {
+ if (url1->port != url2->port)
+ return PJSIP_ECMPPORT;
+ }
+ /* Transport is not allowed in From/To header. */
+ if (context != PJSIP_URI_IN_FROMTO_HDR) {
+ if (pj_stricmp(&url1->transport_param, &url2->transport_param) != 0)
+ return PJSIP_ECMPTRANSPORTPRM;
+ }
+ /* TTL param is not allowed in From, To, Route, and Record-Route header. */
+ if (context != PJSIP_URI_IN_FROMTO_HDR &&
+ context != PJSIP_URI_IN_ROUTING_HDR)
+ {
+ if (url1->ttl_param != url2->ttl_param)
+ return PJSIP_ECMPTTLPARAM;
+ }
+ /* User param is allowed in all contexes */
+ if (pj_stricmp(&url1->user_param, &url2->user_param) != 0)
+ return PJSIP_ECMPUSERPARAM;
+ /* Method param is only allowed in external/other context. */
+ if (context == PJSIP_URI_IN_OTHER) {
+ if (pj_stricmp(&url1->method_param, &url2->method_param) != 0)
+ return PJSIP_ECMPMETHODPARAM;
+ }
+ /* maddr param is not allowed in From and To header. */
+ if (context != PJSIP_URI_IN_FROMTO_HDR) {
+ if (pj_stricmp(&url1->maddr_param, &url2->maddr_param) != 0)
+ return PJSIP_ECMPMADDRPARAM;
+ }
+
+ /* lr parameter is ignored (?) */
+ /* lr param is not allowed in From, To, and Contact header. */
+
+
+ /* All other uri-parameters appearing in only one URI are ignored when
+ * comparing the URIs.
+ */
+ p1 = url1->other_param.next;
+ while (p1 != &url1->other_param) {
+ const pjsip_param *p2;
+ p2 = pjsip_param_cfind(&url2->other_param, &p1->name);
+ if (p2 ) {
+ if (pj_stricmp(&p1->value, &p2->value) != 0)
+ return PJSIP_ECMPOTHERPARAM;
+ }
+
+ p1 = p1->next;
+ }
+
+ /* URI header components are never ignored. Any present header component
+ * MUST be present in both URIs and match for the URIs to match.
+ * The matching rules are defined for each header field in Section 20.
+ */
+ p1 = url1->header_param.next;
+ while (p1 != &url1->header_param) {
+ const pjsip_param *p2;
+ p2 = pjsip_param_cfind(&url2->header_param, &p1->name);
+ if (p2) {
+ /* It seems too much to compare two header params according to
+ * the rule of each header. We'll just compare them string to
+ * string..
+ */
+ PJ_TODO(MORE_COMPLIANT_HEADER_PARAM_COMPARISON_IN_URL);
+
+ if (pj_stricmp(&p1->value, &p2->value) != 0)
+ return PJSIP_ECMPHEADERPARAM;
+ } else {
+ return PJSIP_ECMPHEADERPARAM;
+ }
+ p1 = p1->next;
+ }
+
+ /* Equal!! Pheuww.. */
+ return PJ_SUCCESS;
+}
+
+
+PJ_DEF(void) pjsip_url_assign(pj_pool_t *pool, pjsip_url *url,
+ const pjsip_url *rhs)
+{
+ pj_strdup( pool, &url->user, &rhs->user);
+ pj_strdup( pool, &url->passwd, &rhs->passwd);
+ pj_strdup( pool, &url->host, &rhs->host);
+ url->port = rhs->port;
+ pj_strdup( pool, &url->user_param, &rhs->user_param);
+ pj_strdup( pool, &url->method_param, &rhs->method_param);
+ pj_strdup( pool, &url->transport_param, &rhs->transport_param);
+ url->ttl_param = rhs->ttl_param;
+ pj_strdup( pool, &url->maddr_param, &rhs->maddr_param);
+ pjsip_param_clone(pool, &url->other_param, &rhs->other_param);
+ pjsip_param_clone(pool, &url->header_param, &rhs->header_param);
+ url->lr_param = rhs->lr_param;
+}
+
+static pjsip_url* pjsip_url_clone(pj_pool_t *pool, const pjsip_url *rhs)
+{
+ pjsip_url *url = pj_pool_alloc(pool, sizeof(pjsip_url));
+ if (!url)
+ return NULL;
+
+ pjsip_url_init(url, IS_SIPS(rhs));
+ pjsip_url_assign(pool, url, rhs);
+ return url;
+}
+
+static const pj_str_t *pjsip_name_addr_get_scheme(const pjsip_name_addr *name)
+{
+ pj_assert(name->uri != NULL);
+ return pjsip_uri_get_scheme(name->uri);
+}
+
+PJ_DEF(void) pjsip_name_addr_init(pjsip_name_addr *name)
+{
+ name->vptr = &name_addr_vptr;
+ name->uri = NULL;
+ name->display.slen = 0;
+}
+
+PJ_DEF(pjsip_name_addr*) pjsip_name_addr_create(pj_pool_t *pool)
+{
+ pjsip_name_addr *name_addr = pj_pool_alloc(pool, sizeof(pjsip_name_addr));
+ pjsip_name_addr_init(name_addr);
+ return name_addr;
+}
+
+static int pjsip_name_addr_print( pjsip_uri_context_e context,
+ const pjsip_name_addr *name,
+ char *buf, pj_size_t size)
+{
+ int printed;
+ char *startbuf = buf;
+ char *endbuf = buf + size;
+
+ pj_assert(name->uri != NULL);
+
+ if (context != PJSIP_URI_IN_REQ_URI) {
+ if (name->display.slen) {
+ if (endbuf-buf < 8) return -1;
+ *buf++ = '"';
+ copy_advance(buf, name->display);
+ *buf++ = '"';
+ *buf++ = ' ';
+ }
+ *buf++ = '<';
+ }
+
+ printed = pjsip_uri_print(context,name->uri, buf, size-(buf-startbuf));
+ if (printed < 1)
+ return -1;
+ buf += printed;
+
+ if (context != PJSIP_URI_IN_REQ_URI) {
+ *buf++ = '>';
+ }
+
+ *buf = '\0';
+ return buf-startbuf;
+}
+
+PJ_DEF(void) pjsip_name_addr_assign(pj_pool_t *pool, pjsip_name_addr *dst,
+ const pjsip_name_addr *src)
+{
+ pj_strdup( pool, &dst->display, &src->display);
+ dst->uri = pjsip_uri_clone(pool, src->uri);
+}
+
+static pjsip_name_addr* pjsip_name_addr_clone( pj_pool_t *pool,
+ const pjsip_name_addr *rhs)
+{
+ pjsip_name_addr *addr = pj_pool_alloc(pool, sizeof(pjsip_name_addr));
+ if (!addr)
+ return NULL;
+
+ pjsip_name_addr_init(addr);
+ pjsip_name_addr_assign(pool, addr, rhs);
+ return addr;
+}
+
+static int pjsip_name_addr_compare( pjsip_uri_context_e context,
+ const pjsip_name_addr *naddr1,
+ const pjsip_name_addr *naddr2)
+{
+ int d;
+
+ /* I'm not sure whether display name is included in the comparison. */
+ if (pj_strcmp(&naddr1->display, &naddr2->display) != 0) {
+ return -1;
+ }
+
+ pj_assert( naddr1->uri != NULL );
+ pj_assert( naddr2->uri != NULL );
+
+ /* Compare name-addr as URL */
+ d = pjsip_uri_cmp( context, naddr1->uri, naddr2->uri);
+ if (d)
+ return d;
+
+ return 0;
+}
+
diff --git a/pjsip/src/pjsip/sip_util.c b/pjsip/src/pjsip/sip_util.c
index 5f59a12e..4531117f 100644
--- a/pjsip/src/pjsip/sip_util.c
+++ b/pjsip/src/pjsip/sip_util.c
@@ -1,724 +1,724 @@
-/* $Id$ */
-/*
- * Copyright (C) 2003-2006 Benny Prijono <benny@prijono.org>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
-#include <pjsip/sip_util.h>
-#include <pjsip/sip_transport.h>
-#include <pjsip/sip_msg.h>
-#include <pjsip/sip_endpoint.h>
-#include <pjsip/sip_event.h>
-#include <pjsip/sip_transaction.h>
-#include <pjsip/sip_module.h>
-#include <pj/log.h>
-#include <pj/string.h>
-#include <pj/guid.h>
-#include <pj/pool.h>
-#include <pj/except.h>
-#include <pj/rand.h>
-#include <pj/assert.h>
-#include <pj/errno.h>
-
-#define THIS_FILE "endpoint"
-
-static const char *event_str[] =
-{
- "UNIDENTIFIED",
- "TIMER",
- "TX_MSG",
- "RX_MSG",
- "TRANSPORT_ERROR",
- "TSX_STATE",
- "RX_2XX_RESPONSE",
- "RX_ACK",
- "DISCARD_MSG",
- "USER",
- "BEFORE_TX",
-};
-
-static pj_str_t str_TEXT = { "text", 4},
- str_PLAIN = { "plain", 5 };
-static int aux_mod_id;
-
-struct aux_tsx_data
-{
- void *token;
- void (*cb)(void*,pjsip_event*);
-};
-
-static pj_status_t aux_tsx_init( pjsip_endpoint *endpt,
- struct pjsip_module *mod, pj_uint32_t id )
-{
- PJ_UNUSED_ARG(endpt);
- PJ_UNUSED_ARG(mod);
-
- aux_mod_id = id;
- return 0;
-}
-
-static void aux_tsx_handler( struct pjsip_module *mod, pjsip_event *event )
-{
- pjsip_transaction *tsx;
- struct aux_tsx_data *tsx_data;
-
- PJ_UNUSED_ARG(mod);
-
- if (event->type != PJSIP_EVENT_TSX_STATE)
- return;
-
- pj_assert(event->body.tsx_state.tsx != NULL);
- tsx = event->body.tsx_state.tsx;
- if (tsx == NULL)
- return;
- if (tsx->module_data[aux_mod_id] == NULL)
- return;
- if (tsx->status_code < 200)
- return;
-
- /* Call the callback, if any, and prevent the callback to be called again
- * by clearing the transaction's module_data.
- */
- tsx_data = tsx->module_data[aux_mod_id];
- tsx->module_data[aux_mod_id] = NULL;
-
- if (tsx_data->cb) {
- (*tsx_data->cb)(tsx_data->token, event);
- }
-}
-
-pjsip_module aux_tsx_module =
-{
- { "Aux-Tsx", 7}, /* Name. */
- 0, /* Flag */
- 128, /* Priority */
- NULL, /* Arbitrary data. */
- 0, /* Number of methods supported (none). */
- { 0 }, /* Array of methods (none) */
- &aux_tsx_init, /* init_module() */
- NULL, /* start_module() */
- NULL, /* deinit_module() */
- &aux_tsx_handler, /* tsx_handler() */
-};
-
-PJ_DEF(pj_status_t) pjsip_endpt_send_request( pjsip_endpoint *endpt,
- pjsip_tx_data *tdata,
- int timeout,
- void *token,
- void (*cb)(void*,pjsip_event*))
-{
- pjsip_transaction *tsx;
- struct aux_tsx_data *tsx_data;
- pj_status_t status;
-
- status = pjsip_endpt_create_tsx(endpt, &tsx);
- if (!tsx) {
- pjsip_tx_data_dec_ref(tdata);
- return -1;
- }
-
- tsx_data = pj_pool_alloc(tsx->pool, sizeof(struct aux_tsx_data));
- tsx_data->token = token;
- tsx_data->cb = cb;
- tsx->module_data[aux_mod_id] = tsx_data;
-
- if (pjsip_tsx_init_uac(tsx, tdata) != 0) {
- pjsip_endpt_destroy_tsx(endpt, tsx);
- pjsip_tx_data_dec_ref(tdata);
- return -1;
- }
-
- pjsip_endpt_register_tsx(endpt, tsx);
- pjsip_tx_data_invalidate_msg(tdata);
- pjsip_tsx_on_tx_msg(tsx, tdata);
- pjsip_tx_data_dec_ref(tdata);
- return 0;
-}
-
-/*
- * Initialize transmit data (msg) with the headers and optional body.
- * This will just put the headers in the message as it is. Be carefull
- * when calling this function because once a header is put in a message,
- * it CAN NOT be put in other message until the first message is deleted,
- * because the way the header is put in the list.
- * That's why the session will shallow_clone it's headers before calling
- * this function.
- */
-static void init_request_throw( pjsip_endpoint *endpt,
- pjsip_tx_data *tdata,
- pjsip_method *method,
- pjsip_uri *param_target,
- pjsip_from_hdr *param_from,
- pjsip_to_hdr *param_to,
- pjsip_contact_hdr *param_contact,
- pjsip_cid_hdr *param_call_id,
- pjsip_cseq_hdr *param_cseq,
- const pj_str_t *param_text)
-{
- pjsip_msg *msg;
- pjsip_msg_body *body;
- const pjsip_hdr *endpt_hdr;
-
- /* Create the message. */
- msg = tdata->msg = pjsip_msg_create(tdata->pool, PJSIP_REQUEST_MSG);
-
- /* Init request URI. */
- pj_memcpy(&msg->line.req.method, method, sizeof(*method));
- msg->line.req.uri = param_target;
-
- /* Add additional request headers from endpoint. */
- endpt_hdr = pjsip_endpt_get_request_headers(endpt)->next;
- while (endpt_hdr != pjsip_endpt_get_request_headers(endpt)) {
- pjsip_hdr *hdr = pjsip_hdr_shallow_clone(tdata->pool, endpt_hdr);
- pjsip_msg_add_hdr( tdata->msg, hdr );
- endpt_hdr = endpt_hdr->next;
- }
-
- /* Add From header. */
- if (param_from->tag.slen == 0)
- pj_create_unique_string(tdata->pool, &param_from->tag);
- pjsip_msg_add_hdr(msg, (void*)param_from);
-
- /* Add To header. */
- pjsip_msg_add_hdr(msg, (void*)param_to);
-
- /* Add Contact header. */
- if (param_contact) {
- pjsip_msg_add_hdr(msg, (void*)param_contact);
- }
-
- /* Add Call-ID header. */
- pjsip_msg_add_hdr(msg, (void*)param_call_id);
-
- /* Add CSeq header. */
- pjsip_msg_add_hdr(msg, (void*)param_cseq);
-
- /* Create message body. */
- if (param_text) {
- body = pj_pool_calloc(tdata->pool, 1, sizeof(pjsip_msg_body));
- body->content_type.type = str_TEXT;
- body->content_type.subtype = str_PLAIN;
- body->data = pj_pool_alloc(tdata->pool, param_text->slen );
- pj_memcpy(body->data, param_text->ptr, param_text->slen);
- body->len = param_text->slen;
- body->print_body = &pjsip_print_text_body;
- msg->body = body;
- }
-}
-
-/*
- * Create arbitrary request.
- */
-PJ_DEF(pj_status_t) pjsip_endpt_create_request( pjsip_endpoint *endpt,
- const pjsip_method *method,
- const pj_str_t *param_target,
- const pj_str_t *param_from,
- const pj_str_t *param_to,
- const pj_str_t *param_contact,
- const pj_str_t *param_call_id,
- int param_cseq,
- const pj_str_t *param_text,
- pjsip_tx_data **p_tdata)
-{
- pjsip_uri *target;
- pjsip_tx_data *tdata;
- pjsip_from_hdr *from;
- pjsip_to_hdr *to;
- pjsip_contact_hdr *contact;
- pjsip_cseq_hdr *cseq = NULL; /* = NULL, warning in VC6 */
- pjsip_cid_hdr *call_id;
- pj_str_t tmp;
- pj_status_t status;
- PJ_USE_EXCEPTION;
-
- PJ_LOG(5,(THIS_FILE, "Entering pjsip_endpt_create_request()"));
-
- status = pjsip_endpt_create_tdata(endpt, &tdata);
- if (status != PJ_SUCCESS)
- return status;
-
- /* Init reference counter to 1. */
- pjsip_tx_data_add_ref(tdata);
-
- PJ_TRY {
- /* Request target. */
- pj_strdup_with_null(tdata->pool, &tmp, param_target);
- target = pjsip_parse_uri( tdata->pool, tmp.ptr, tmp.slen, 0);
- if (target == NULL) {
- PJ_LOG(4,(THIS_FILE, "Error creating request: invalid target %s",
- tmp.ptr));
- goto on_error;
- }
-
- /* From */
- from = pjsip_from_hdr_create(tdata->pool);
- pj_strdup_with_null(tdata->pool, &tmp, param_from);
- from->uri = pjsip_parse_uri( tdata->pool, tmp.ptr, tmp.slen,
- PJSIP_PARSE_URI_AS_NAMEADDR);
- if (from->uri == NULL) {
- PJ_LOG(4,(THIS_FILE, "Error creating request: invalid 'From' URI '%s'",
- tmp.ptr));
- goto on_error;
- }
- pj_create_unique_string(tdata->pool, &from->tag);
-
- /* To */
- to = pjsip_to_hdr_create(tdata->pool);
- pj_strdup_with_null(tdata->pool, &tmp, param_to);
- to->uri = pjsip_parse_uri( tdata->pool, tmp.ptr, tmp.slen,
- PJSIP_PARSE_URI_AS_NAMEADDR);
- if (to->uri == NULL) {
- PJ_LOG(4,(THIS_FILE, "Error creating request: invalid 'To' URI '%s'",
- tmp.ptr));
- goto on_error;
- }
-
- /* Contact. */
- if (param_contact) {
- contact = pjsip_contact_hdr_create(tdata->pool);
- pj_strdup_with_null(tdata->pool, &tmp, param_contact);
- contact->uri = pjsip_parse_uri( tdata->pool, tmp.ptr, tmp.slen,
- PJSIP_PARSE_URI_AS_NAMEADDR);
- if (contact->uri == NULL) {
- PJ_LOG(4,(THIS_FILE,
- "Error creating request: invalid 'Contact' URI '%s'",
- tmp.ptr));
- goto on_error;
- }
- } else {
- contact = NULL;
- }
-
- /* Call-ID */
- call_id = pjsip_cid_hdr_create(tdata->pool);
- if (param_call_id != NULL && param_call_id->slen)
- pj_strdup(tdata->pool, &call_id->id, param_call_id);
- else
- pj_create_unique_string(tdata->pool, &call_id->id);
-
- /* CSeq */
- cseq = pjsip_cseq_hdr_create(tdata->pool);
- if (param_cseq >= 0)
- cseq->cseq = param_cseq;
- else
- cseq->cseq = pj_rand() & 0xFFFF;
-
- /* Method */
- pjsip_method_copy(tdata->pool, &cseq->method, method);
-
- /* Create the request. */
- init_request_throw( endpt, tdata, &cseq->method, target, from, to,
- contact, call_id, cseq, param_text);
- }
- PJ_DEFAULT {
- status = PJ_ENOMEM;
- goto on_error;
- }
- PJ_END
-
- PJ_LOG(4,(THIS_FILE, "Request %s (%d %.*s) created.",
- tdata->obj_name,
- cseq->cseq,
- cseq->method.name.slen,
- cseq->method.name.ptr));
-
- *p_tdata = tdata;
- return PJ_SUCCESS;
-
-on_error:
- pjsip_tx_data_dec_ref(tdata);
- return status;
-}
-
-PJ_DEF(pj_status_t)
-pjsip_endpt_create_request_from_hdr( pjsip_endpoint *endpt,
- const pjsip_method *method,
- const pjsip_uri *param_target,
- const pjsip_from_hdr *param_from,
- const pjsip_to_hdr *param_to,
- const pjsip_contact_hdr *param_contact,
- const pjsip_cid_hdr *param_call_id,
- int param_cseq,
- const pj_str_t *param_text,
- pjsip_tx_data **p_tdata)
-{
- pjsip_uri *target;
- pjsip_tx_data *tdata;
- pjsip_from_hdr *from;
- pjsip_to_hdr *to;
- pjsip_contact_hdr *contact;
- pjsip_cid_hdr *call_id;
- pjsip_cseq_hdr *cseq = NULL; /* The NULL because warning in VC6 */
- pj_status_t status;
- PJ_USE_EXCEPTION;
-
- PJ_LOG(5,(THIS_FILE, "Entering pjsip_endpt_create_request_from_hdr()"));
-
- status = pjsip_endpt_create_tdata(endpt, &tdata);
- if (status != PJ_SUCCESS)
- return status;
-
- pjsip_tx_data_add_ref(tdata);
-
- PJ_TRY {
- target = pjsip_uri_clone(tdata->pool, param_target);
- from = pjsip_hdr_shallow_clone(tdata->pool, param_from);
- pjsip_fromto_set_from(from);
- to = pjsip_hdr_shallow_clone(tdata->pool, param_to);
- pjsip_fromto_set_to(to);
- if (param_contact)
- contact = pjsip_hdr_shallow_clone(tdata->pool, param_contact);
- else
- contact = NULL;
- call_id = pjsip_hdr_shallow_clone(tdata->pool, param_call_id);
- cseq = pjsip_cseq_hdr_create(tdata->pool);
- if (param_cseq >= 0)
- cseq->cseq = param_cseq;
- else
- cseq->cseq = pj_rand() % 0xFFFF;
- pjsip_method_copy(tdata->pool, &cseq->method, method);
-
- init_request_throw(endpt, tdata, &cseq->method, target, from, to,
- contact, call_id, cseq, param_text);
- }
- PJ_DEFAULT {
- status = PJ_ENOMEM;
- goto on_error;
- }
- PJ_END;
-
- PJ_LOG(4,(THIS_FILE, "Request %s (%d %.*s) created.",
- tdata->obj_name,
- cseq->cseq,
- cseq->method.name.slen,
- cseq->method.name.ptr));
-
- *p_tdata = tdata;
- return PJ_SUCCESS;
-
-on_error:
- pjsip_tx_data_dec_ref(tdata);
- return status;
-}
-
-/*
- * Construct a minimal response message for the received request.
- */
-PJ_DEF(pj_status_t) pjsip_endpt_create_response( pjsip_endpoint *endpt,
- const pjsip_rx_data *rdata,
- int code,
- pjsip_tx_data **p_tdata)
-{
- pjsip_tx_data *tdata;
- pjsip_msg *msg, *req_msg;
- pjsip_hdr *hdr;
- pjsip_via_hdr *via;
- pjsip_rr_hdr *rr;
- pj_status_t status;
-
- /* rdata must be a request message. */
- req_msg = rdata->msg_info.msg;
- pj_assert(req_msg->type == PJSIP_REQUEST_MSG);
-
- /* Log this action. */
- PJ_LOG(5,(THIS_FILE, "pjsip_endpt_create_response(rdata=%p, code=%d)",
- rdata, code));
-
- /* Create a new transmit buffer. */
- status = pjsip_endpt_create_tdata( endpt, &tdata);
- if (status != PJ_SUCCESS)
- return status;
-
- /* Create new response message. */
- tdata->msg = msg = pjsip_msg_create(tdata->pool, PJSIP_RESPONSE_MSG);
-
- /* Set status code and reason text. */
- msg->line.status.code = code;
- msg->line.status.reason = *pjsip_get_status_text(code);
-
- /* Set TX data attributes. */
- tdata->rx_timestamp = rdata->pkt_info.timestamp;
-
- /* Copy all the via headers, in order. */
- via = rdata->msg_info.via;
- while (via) {
- pjsip_msg_add_hdr( msg, pjsip_hdr_clone(tdata->pool, via));
- via = via->next;
- if (via != (void*)&req_msg->hdr)
- via = pjsip_msg_find_hdr(req_msg, PJSIP_H_VIA, via);
- else
- break;
- }
-
- /* Copy all Record-Route headers, in order. */
- rr = pjsip_msg_find_hdr(req_msg, PJSIP_H_RECORD_ROUTE, NULL);
- while (rr) {
- pjsip_msg_add_hdr(msg, pjsip_hdr_clone(tdata->pool, rr));
- rr = rr->next;
- if (rr != (void*)&req_msg->hdr)
- rr = pjsip_msg_find_hdr(req_msg, PJSIP_H_RECORD_ROUTE, rr);
- else
- break;
- }
-
- /* Copy Call-ID header. */
- hdr = pjsip_msg_find_hdr( req_msg, PJSIP_H_CALL_ID, NULL);
- pjsip_msg_add_hdr(msg, pjsip_hdr_clone(tdata->pool, hdr));
-
- /* Copy From header. */
- hdr = pjsip_hdr_clone(tdata->pool, rdata->msg_info.from);
- pjsip_msg_add_hdr( msg, hdr);
-
- /* Copy To header. */
- hdr = pjsip_hdr_clone(tdata->pool, rdata->msg_info.to);
- pjsip_msg_add_hdr( msg, hdr);
-
- /* Copy CSeq header. */
- hdr = pjsip_hdr_clone(tdata->pool, rdata->msg_info.cseq);
- pjsip_msg_add_hdr( msg, hdr);
-
- /* All done. */
- *p_tdata = tdata;
- return PJ_SUCCESS;
-}
-
-
-/*
- * Construct ACK for 3xx-6xx final response (according to chapter 17.1.1 of
- * RFC3261). Note that the generation of ACK for 2xx response is different,
- * and one must not use this function to generate such ACK.
- */
-PJ_DEF(void) pjsip_endpt_create_ack(pjsip_endpoint *endpt,
- pjsip_tx_data *tdata,
- const pjsip_rx_data *rdata )
-{
- pjsip_msg *ack_msg, *invite_msg;
- pjsip_to_hdr *to;
- pjsip_from_hdr *from;
- pjsip_cseq_hdr *cseq;
- pjsip_hdr *hdr;
-
- /* Make compiler happy. */
- PJ_UNUSED_ARG(endpt);
-
- /* rdata must be a final response. */
- pj_assert(rdata->msg_info.msg->type==PJSIP_RESPONSE_MSG &&
- rdata->msg_info.msg->line.status.code >= 300);
-
- /* Log this action. */
- PJ_LOG(5,(THIS_FILE, "pjsip_endpt_create_ack(rdata=%p)", rdata));
-
- /* Create new request message. */
- ack_msg = pjsip_msg_create(tdata->pool, PJSIP_REQUEST_MSG);
- pjsip_method_set( &ack_msg->line.req.method, PJSIP_ACK_METHOD );
-
- /* The original INVITE message. */
- invite_msg = tdata->msg;
-
- /* Copy Request-Uri from the original INVITE. */
- ack_msg->line.req.uri = invite_msg->line.req.uri;
-
- /* Copy Call-ID from the original INVITE */
- hdr = pjsip_msg_find_remove_hdr( invite_msg, PJSIP_H_CALL_ID, NULL);
- pjsip_msg_add_hdr( ack_msg, hdr );
-
- /* Copy From header from the original INVITE. */
- from = (pjsip_from_hdr*)pjsip_msg_find_remove_hdr(invite_msg,
- PJSIP_H_FROM, NULL);
- pjsip_msg_add_hdr( ack_msg, (pjsip_hdr*)from );
-
- /* Copy To header from the original INVITE. */
- to = (pjsip_to_hdr*)pjsip_msg_find_remove_hdr( invite_msg,
- PJSIP_H_TO, NULL);
- pj_strdup(tdata->pool, &to->tag, &rdata->msg_info.to->tag);
- pjsip_msg_add_hdr( ack_msg, (pjsip_hdr*)to );
-
- /* Must contain single Via, just as the original INVITE. */
- hdr = pjsip_msg_find_remove_hdr( invite_msg, PJSIP_H_VIA, NULL);
- pjsip_msg_insert_first_hdr( ack_msg, hdr );
-
- /* Must have the same CSeq value as the original INVITE, but method
- * changed to ACK
- */
- cseq = (pjsip_cseq_hdr*) pjsip_msg_find_remove_hdr( invite_msg,
- PJSIP_H_CSEQ, NULL);
- pjsip_method_set( &cseq->method, PJSIP_ACK_METHOD );
- pjsip_msg_add_hdr( ack_msg, (pjsip_hdr*) cseq );
-
- /* If the original INVITE has Route headers, those header fields MUST
- * appear in the ACK.
- */
- hdr = pjsip_msg_find_remove_hdr( invite_msg, PJSIP_H_ROUTE, NULL);
- while (hdr != NULL) {
- pjsip_msg_add_hdr( ack_msg, hdr );
- hdr = pjsip_msg_find_remove_hdr( invite_msg, PJSIP_H_ROUTE, NULL);
- }
-
- /* Set the message in the "tdata" to point to the ACK message. */
- tdata->msg = ack_msg;
-
- /* Reset transmit packet buffer, to force 're-printing' of message. */
- tdata->buf.cur = tdata->buf.start;
-
- /* We're done.
- * "tdata" parameter now contains the ACK message.
- */
-}
-
-
-/*
- * Construct CANCEL request for the previously sent request, according to
- * chapter 9.1 of RFC3261.
- */
-PJ_DEF(pj_status_t) pjsip_endpt_create_cancel( pjsip_endpoint *endpt,
- pjsip_tx_data *req_tdata,
- pjsip_tx_data **p_tdata)
-{
- pjsip_msg *req_msg; /* the original request. */
- pjsip_tx_data *cancel_tdata;
- pjsip_msg *cancel_msg;
- pjsip_hdr *hdr;
- pjsip_cseq_hdr *req_cseq, *cseq;
- pjsip_uri *req_uri;
- pj_status_t status;
-
- /* Log this action. */
- PJ_LOG(5,(THIS_FILE, "pjsip_endpt_create_cancel(tdata=%p)", req_tdata));
-
- /* Get the original request. */
- req_msg = req_tdata->msg;
-
- /* The transmit buffer must INVITE request. */
- PJ_ASSERT_RETURN(req_msg->type == PJSIP_REQUEST_MSG &&
- req_msg->line.req.method.id == PJSIP_INVITE_METHOD,
- PJ_EINVAL);
-
- /* Create new transmit buffer. */
- status = pjsip_endpt_create_tdata( endpt, &cancel_tdata);
- if (status != PJ_SUCCESS) {
- return status;
- }
-
- /* Create CANCEL request message. */
- cancel_msg = pjsip_msg_create(cancel_tdata->pool, PJSIP_REQUEST_MSG);
- cancel_tdata->msg = cancel_msg;
-
- /* Request-URI, Call-ID, From, To, and the numeric part of the CSeq are
- * copied from the original request.
- */
- /* Set request line. */
- pjsip_method_set(&cancel_msg->line.req.method, PJSIP_CANCEL_METHOD);
- req_uri = req_msg->line.req.uri;
- cancel_msg->line.req.uri = pjsip_uri_clone(cancel_tdata->pool, req_uri);
-
- /* Copy Call-ID */
- hdr = pjsip_msg_find_hdr(req_msg, PJSIP_H_CALL_ID, NULL);
- pjsip_msg_add_hdr(cancel_msg, pjsip_hdr_clone(cancel_tdata->pool, hdr));
-
- /* Copy From header. */
- hdr = pjsip_msg_find_hdr(req_msg, PJSIP_H_FROM, NULL);
- pjsip_msg_add_hdr(cancel_msg, pjsip_hdr_clone(cancel_tdata->pool, hdr));
-
- /* Copy To header. */
- hdr = pjsip_msg_find_hdr(req_msg, PJSIP_H_TO, NULL);
- pjsip_msg_add_hdr(cancel_msg, pjsip_hdr_clone(cancel_tdata->pool, hdr));
-
- /* Create new CSeq with equal number, but method set to CANCEL. */
- req_cseq = (pjsip_cseq_hdr*) pjsip_msg_find_hdr(req_msg, PJSIP_H_CSEQ, NULL);
- cseq = pjsip_cseq_hdr_create(cancel_tdata->pool);
- cseq->cseq = req_cseq->cseq;
- pjsip_method_set(&cseq->method, PJSIP_CANCEL_METHOD);
- pjsip_msg_add_hdr(cancel_msg, (pjsip_hdr*)cseq);
-
- /* Must only have single Via which matches the top-most Via in the
- * request being cancelled.
- */
- hdr = pjsip_msg_find_hdr(req_msg, PJSIP_H_VIA, NULL);
- pjsip_msg_insert_first_hdr(cancel_msg,
- pjsip_hdr_clone(cancel_tdata->pool, hdr));
-
- /* If the original request has Route header, the CANCEL request must also
- * has exactly the same.
- * Copy "Route" header from the request.
- */
- hdr = pjsip_msg_find_hdr(req_msg, PJSIP_H_ROUTE, NULL);
- while (hdr != NULL) {
- pjsip_msg_add_hdr(cancel_msg, pjsip_hdr_clone(cancel_tdata->pool, hdr));
- hdr = hdr->next;
- if (hdr != &cancel_msg->hdr)
- hdr = pjsip_msg_find_hdr(req_msg, PJSIP_H_ROUTE, hdr);
- else
- break;
- }
-
- /* Done.
- * Return the transmit buffer containing the CANCEL request.
- */
- *p_tdata = cancel_tdata;
- return PJ_SUCCESS;
-}
-
-/* Get the address parameters (host, port, flag, TTL, etc) to send the
- * response.
- */
-PJ_DEF(pj_status_t) pjsip_get_response_addr(pj_pool_t *pool,
- const pjsip_transport *req_transport,
- const pjsip_via_hdr *via,
- pjsip_host_port *send_addr)
-{
- /* Determine the destination address (section 18.2.2):
- * - for TCP, SCTP, or TLS, send the response using the transport where
- * the request was received.
- * - if maddr parameter is present, send to this address using the port
- * in sent-by or 5060. If multicast is used, the TTL in the Via must
- * be used, or 1 if ttl parameter is not present.
- * - otherwise if received parameter is present, set to this address.
- * - otherwise send to the address in sent-by.
- */
- send_addr->flag = req_transport->flag;
- send_addr->type = req_transport->type;
-
- if (PJSIP_TRANSPORT_IS_RELIABLE(req_transport)) {
- const pj_sockaddr_in *remote_addr;
- remote_addr = &req_transport->rem_addr;
- pj_strdup2(pool, &send_addr->host,
- pj_inet_ntoa(remote_addr->sin_addr));
- send_addr->port = pj_sockaddr_in_get_port(remote_addr);
-
- } else {
- /* Set the host part */
- if (via->maddr_param.slen) {
- pj_strdup(pool, &send_addr->host, &via->maddr_param);
- } else if (via->recvd_param.slen) {
- pj_strdup(pool, &send_addr->host, &via->recvd_param);
- } else {
- pj_strdup(pool, &send_addr->host, &via->sent_by.host);
- }
-
- /* Set the port */
- send_addr->port = via->sent_by.port;
- }
-
- return PJ_SUCCESS;
-}
-
-/*
- * Get the event string from the event ID.
- */
-PJ_DEF(const char *) pjsip_event_str(pjsip_event_id_e e)
-{
- return event_str[e];
-}
-
+/* $Id$ */
+/*
+ * Copyright (C) 2003-2006 Benny Prijono <benny@prijono.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#include <pjsip/sip_util.h>
+#include <pjsip/sip_transport.h>
+#include <pjsip/sip_msg.h>
+#include <pjsip/sip_endpoint.h>
+#include <pjsip/sip_event.h>
+#include <pjsip/sip_transaction.h>
+#include <pjsip/sip_module.h>
+#include <pj/log.h>
+#include <pj/string.h>
+#include <pj/guid.h>
+#include <pj/pool.h>
+#include <pj/except.h>
+#include <pj/rand.h>
+#include <pj/assert.h>
+#include <pj/errno.h>
+
+#define THIS_FILE "endpoint"
+
+static const char *event_str[] =
+{
+ "UNIDENTIFIED",
+ "TIMER",
+ "TX_MSG",
+ "RX_MSG",
+ "TRANSPORT_ERROR",
+ "TSX_STATE",
+ "RX_2XX_RESPONSE",
+ "RX_ACK",
+ "DISCARD_MSG",
+ "USER",
+ "BEFORE_TX",
+};
+
+static pj_str_t str_TEXT = { "text", 4},
+ str_PLAIN = { "plain", 5 };
+static int aux_mod_id;
+
+struct aux_tsx_data
+{
+ void *token;
+ void (*cb)(void*,pjsip_event*);
+};
+
+static pj_status_t aux_tsx_init( pjsip_endpoint *endpt,
+ struct pjsip_module *mod, pj_uint32_t id )
+{
+ PJ_UNUSED_ARG(endpt);
+ PJ_UNUSED_ARG(mod);
+
+ aux_mod_id = id;
+ return 0;
+}
+
+static void aux_tsx_handler( struct pjsip_module *mod, pjsip_event *event )
+{
+ pjsip_transaction *tsx;
+ struct aux_tsx_data *tsx_data;
+
+ PJ_UNUSED_ARG(mod);
+
+ if (event->type != PJSIP_EVENT_TSX_STATE)
+ return;
+
+ pj_assert(event->body.tsx_state.tsx != NULL);
+ tsx = event->body.tsx_state.tsx;
+ if (tsx == NULL)
+ return;
+ if (tsx->module_data[aux_mod_id] == NULL)
+ return;
+ if (tsx->status_code < 200)
+ return;
+
+ /* Call the callback, if any, and prevent the callback to be called again
+ * by clearing the transaction's module_data.
+ */
+ tsx_data = tsx->module_data[aux_mod_id];
+ tsx->module_data[aux_mod_id] = NULL;
+
+ if (tsx_data->cb) {
+ (*tsx_data->cb)(tsx_data->token, event);
+ }
+}
+
+pjsip_module aux_tsx_module =
+{
+ { "Aux-Tsx", 7}, /* Name. */
+ 0, /* Flag */
+ 128, /* Priority */
+ NULL, /* Arbitrary data. */
+ 0, /* Number of methods supported (none). */
+ { 0 }, /* Array of methods (none) */
+ &aux_tsx_init, /* init_module() */
+ NULL, /* start_module() */
+ NULL, /* deinit_module() */
+ &aux_tsx_handler, /* tsx_handler() */
+};
+
+PJ_DEF(pj_status_t) pjsip_endpt_send_request( pjsip_endpoint *endpt,
+ pjsip_tx_data *tdata,
+ int timeout,
+ void *token,
+ void (*cb)(void*,pjsip_event*))
+{
+ pjsip_transaction *tsx;
+ struct aux_tsx_data *tsx_data;
+ pj_status_t status;
+
+ status = pjsip_endpt_create_tsx(endpt, &tsx);
+ if (!tsx) {
+ pjsip_tx_data_dec_ref(tdata);
+ return -1;
+ }
+
+ tsx_data = pj_pool_alloc(tsx->pool, sizeof(struct aux_tsx_data));
+ tsx_data->token = token;
+ tsx_data->cb = cb;
+ tsx->module_data[aux_mod_id] = tsx_data;
+
+ if (pjsip_tsx_init_uac(tsx, tdata) != 0) {
+ pjsip_endpt_destroy_tsx(endpt, tsx);
+ pjsip_tx_data_dec_ref(tdata);
+ return -1;
+ }
+
+ pjsip_endpt_register_tsx(endpt, tsx);
+ pjsip_tx_data_invalidate_msg(tdata);
+ pjsip_tsx_on_tx_msg(tsx, tdata);
+ pjsip_tx_data_dec_ref(tdata);
+ return 0;
+}
+
+/*
+ * Initialize transmit data (msg) with the headers and optional body.
+ * This will just put the headers in the message as it is. Be carefull
+ * when calling this function because once a header is put in a message,
+ * it CAN NOT be put in other message until the first message is deleted,
+ * because the way the header is put in the list.
+ * That's why the session will shallow_clone it's headers before calling
+ * this function.
+ */
+static void init_request_throw( pjsip_endpoint *endpt,
+ pjsip_tx_data *tdata,
+ pjsip_method *method,
+ pjsip_uri *param_target,
+ pjsip_from_hdr *param_from,
+ pjsip_to_hdr *param_to,
+ pjsip_contact_hdr *param_contact,
+ pjsip_cid_hdr *param_call_id,
+ pjsip_cseq_hdr *param_cseq,
+ const pj_str_t *param_text)
+{
+ pjsip_msg *msg;
+ pjsip_msg_body *body;
+ const pjsip_hdr *endpt_hdr;
+
+ /* Create the message. */
+ msg = tdata->msg = pjsip_msg_create(tdata->pool, PJSIP_REQUEST_MSG);
+
+ /* Init request URI. */
+ pj_memcpy(&msg->line.req.method, method, sizeof(*method));
+ msg->line.req.uri = param_target;
+
+ /* Add additional request headers from endpoint. */
+ endpt_hdr = pjsip_endpt_get_request_headers(endpt)->next;
+ while (endpt_hdr != pjsip_endpt_get_request_headers(endpt)) {
+ pjsip_hdr *hdr = pjsip_hdr_shallow_clone(tdata->pool, endpt_hdr);
+ pjsip_msg_add_hdr( tdata->msg, hdr );
+ endpt_hdr = endpt_hdr->next;
+ }
+
+ /* Add From header. */
+ if (param_from->tag.slen == 0)
+ pj_create_unique_string(tdata->pool, &param_from->tag);
+ pjsip_msg_add_hdr(msg, (void*)param_from);
+
+ /* Add To header. */
+ pjsip_msg_add_hdr(msg, (void*)param_to);
+
+ /* Add Contact header. */
+ if (param_contact) {
+ pjsip_msg_add_hdr(msg, (void*)param_contact);
+ }
+
+ /* Add Call-ID header. */
+ pjsip_msg_add_hdr(msg, (void*)param_call_id);
+
+ /* Add CSeq header. */
+ pjsip_msg_add_hdr(msg, (void*)param_cseq);
+
+ /* Create message body. */
+ if (param_text) {
+ body = pj_pool_calloc(tdata->pool, 1, sizeof(pjsip_msg_body));
+ body->content_type.type = str_TEXT;
+ body->content_type.subtype = str_PLAIN;
+ body->data = pj_pool_alloc(tdata->pool, param_text->slen );
+ pj_memcpy(body->data, param_text->ptr, param_text->slen);
+ body->len = param_text->slen;
+ body->print_body = &pjsip_print_text_body;
+ msg->body = body;
+ }
+}
+
+/*
+ * Create arbitrary request.
+ */
+PJ_DEF(pj_status_t) pjsip_endpt_create_request( pjsip_endpoint *endpt,
+ const pjsip_method *method,
+ const pj_str_t *param_target,
+ const pj_str_t *param_from,
+ const pj_str_t *param_to,
+ const pj_str_t *param_contact,
+ const pj_str_t *param_call_id,
+ int param_cseq,
+ const pj_str_t *param_text,
+ pjsip_tx_data **p_tdata)
+{
+ pjsip_uri *target;
+ pjsip_tx_data *tdata;
+ pjsip_from_hdr *from;
+ pjsip_to_hdr *to;
+ pjsip_contact_hdr *contact;
+ pjsip_cseq_hdr *cseq = NULL; /* = NULL, warning in VC6 */
+ pjsip_cid_hdr *call_id;
+ pj_str_t tmp;
+ pj_status_t status;
+ PJ_USE_EXCEPTION;
+
+ PJ_LOG(5,(THIS_FILE, "Entering pjsip_endpt_create_request()"));
+
+ status = pjsip_endpt_create_tdata(endpt, &tdata);
+ if (status != PJ_SUCCESS)
+ return status;
+
+ /* Init reference counter to 1. */
+ pjsip_tx_data_add_ref(tdata);
+
+ PJ_TRY {
+ /* Request target. */
+ pj_strdup_with_null(tdata->pool, &tmp, param_target);
+ target = pjsip_parse_uri( tdata->pool, tmp.ptr, tmp.slen, 0);
+ if (target == NULL) {
+ PJ_LOG(4,(THIS_FILE, "Error creating request: invalid target %s",
+ tmp.ptr));
+ goto on_error;
+ }
+
+ /* From */
+ from = pjsip_from_hdr_create(tdata->pool);
+ pj_strdup_with_null(tdata->pool, &tmp, param_from);
+ from->uri = pjsip_parse_uri( tdata->pool, tmp.ptr, tmp.slen,
+ PJSIP_PARSE_URI_AS_NAMEADDR);
+ if (from->uri == NULL) {
+ PJ_LOG(4,(THIS_FILE, "Error creating request: invalid 'From' URI '%s'",
+ tmp.ptr));
+ goto on_error;
+ }
+ pj_create_unique_string(tdata->pool, &from->tag);
+
+ /* To */
+ to = pjsip_to_hdr_create(tdata->pool);
+ pj_strdup_with_null(tdata->pool, &tmp, param_to);
+ to->uri = pjsip_parse_uri( tdata->pool, tmp.ptr, tmp.slen,
+ PJSIP_PARSE_URI_AS_NAMEADDR);
+ if (to->uri == NULL) {
+ PJ_LOG(4,(THIS_FILE, "Error creating request: invalid 'To' URI '%s'",
+ tmp.ptr));
+ goto on_error;
+ }
+
+ /* Contact. */
+ if (param_contact) {
+ contact = pjsip_contact_hdr_create(tdata->pool);
+ pj_strdup_with_null(tdata->pool, &tmp, param_contact);
+ contact->uri = pjsip_parse_uri( tdata->pool, tmp.ptr, tmp.slen,
+ PJSIP_PARSE_URI_AS_NAMEADDR);
+ if (contact->uri == NULL) {
+ PJ_LOG(4,(THIS_FILE,
+ "Error creating request: invalid 'Contact' URI '%s'",
+ tmp.ptr));
+ goto on_error;
+ }
+ } else {
+ contact = NULL;
+ }
+
+ /* Call-ID */
+ call_id = pjsip_cid_hdr_create(tdata->pool);
+ if (param_call_id != NULL && param_call_id->slen)
+ pj_strdup(tdata->pool, &call_id->id, param_call_id);
+ else
+ pj_create_unique_string(tdata->pool, &call_id->id);
+
+ /* CSeq */
+ cseq = pjsip_cseq_hdr_create(tdata->pool);
+ if (param_cseq >= 0)
+ cseq->cseq = param_cseq;
+ else
+ cseq->cseq = pj_rand() & 0xFFFF;
+
+ /* Method */
+ pjsip_method_copy(tdata->pool, &cseq->method, method);
+
+ /* Create the request. */
+ init_request_throw( endpt, tdata, &cseq->method, target, from, to,
+ contact, call_id, cseq, param_text);
+ }
+ PJ_DEFAULT {
+ status = PJ_ENOMEM;
+ goto on_error;
+ }
+ PJ_END
+
+ PJ_LOG(4,(THIS_FILE, "Request %s (%d %.*s) created.",
+ tdata->obj_name,
+ cseq->cseq,
+ cseq->method.name.slen,
+ cseq->method.name.ptr));
+
+ *p_tdata = tdata;
+ return PJ_SUCCESS;
+
+on_error:
+ pjsip_tx_data_dec_ref(tdata);
+ return status;
+}
+
+PJ_DEF(pj_status_t)
+pjsip_endpt_create_request_from_hdr( pjsip_endpoint *endpt,
+ const pjsip_method *method,
+ const pjsip_uri *param_target,
+ const pjsip_from_hdr *param_from,
+ const pjsip_to_hdr *param_to,
+ const pjsip_contact_hdr *param_contact,
+ const pjsip_cid_hdr *param_call_id,
+ int param_cseq,
+ const pj_str_t *param_text,
+ pjsip_tx_data **p_tdata)
+{
+ pjsip_uri *target;
+ pjsip_tx_data *tdata;
+ pjsip_from_hdr *from;
+ pjsip_to_hdr *to;
+ pjsip_contact_hdr *contact;
+ pjsip_cid_hdr *call_id;
+ pjsip_cseq_hdr *cseq = NULL; /* The NULL because warning in VC6 */
+ pj_status_t status;
+ PJ_USE_EXCEPTION;
+
+ PJ_LOG(5,(THIS_FILE, "Entering pjsip_endpt_create_request_from_hdr()"));
+
+ status = pjsip_endpt_create_tdata(endpt, &tdata);
+ if (status != PJ_SUCCESS)
+ return status;
+
+ pjsip_tx_data_add_ref(tdata);
+
+ PJ_TRY {
+ target = pjsip_uri_clone(tdata->pool, param_target);
+ from = pjsip_hdr_shallow_clone(tdata->pool, param_from);
+ pjsip_fromto_set_from(from);
+ to = pjsip_hdr_shallow_clone(tdata->pool, param_to);
+ pjsip_fromto_set_to(to);
+ if (param_contact)
+ contact = pjsip_hdr_shallow_clone(tdata->pool, param_contact);
+ else
+ contact = NULL;
+ call_id = pjsip_hdr_shallow_clone(tdata->pool, param_call_id);
+ cseq = pjsip_cseq_hdr_create(tdata->pool);
+ if (param_cseq >= 0)
+ cseq->cseq = param_cseq;
+ else
+ cseq->cseq = pj_rand() % 0xFFFF;
+ pjsip_method_copy(tdata->pool, &cseq->method, method);
+
+ init_request_throw(endpt, tdata, &cseq->method, target, from, to,
+ contact, call_id, cseq, param_text);
+ }
+ PJ_DEFAULT {
+ status = PJ_ENOMEM;
+ goto on_error;
+ }
+ PJ_END;
+
+ PJ_LOG(4,(THIS_FILE, "Request %s (%d %.*s) created.",
+ tdata->obj_name,
+ cseq->cseq,
+ cseq->method.name.slen,
+ cseq->method.name.ptr));
+
+ *p_tdata = tdata;
+ return PJ_SUCCESS;
+
+on_error:
+ pjsip_tx_data_dec_ref(tdata);
+ return status;
+}
+
+/*
+ * Construct a minimal response message for the received request.
+ */
+PJ_DEF(pj_status_t) pjsip_endpt_create_response( pjsip_endpoint *endpt,
+ const pjsip_rx_data *rdata,
+ int code,
+ pjsip_tx_data **p_tdata)
+{
+ pjsip_tx_data *tdata;
+ pjsip_msg *msg, *req_msg;
+ pjsip_hdr *hdr;
+ pjsip_via_hdr *via;
+ pjsip_rr_hdr *rr;
+ pj_status_t status;
+
+ /* rdata must be a request message. */
+ req_msg = rdata->msg_info.msg;
+ pj_assert(req_msg->type == PJSIP_REQUEST_MSG);
+
+ /* Log this action. */
+ PJ_LOG(5,(THIS_FILE, "pjsip_endpt_create_response(rdata=%p, code=%d)",
+ rdata, code));
+
+ /* Create a new transmit buffer. */
+ status = pjsip_endpt_create_tdata( endpt, &tdata);
+ if (status != PJ_SUCCESS)
+ return status;
+
+ /* Create new response message. */
+ tdata->msg = msg = pjsip_msg_create(tdata->pool, PJSIP_RESPONSE_MSG);
+
+ /* Set status code and reason text. */
+ msg->line.status.code = code;
+ msg->line.status.reason = *pjsip_get_status_text(code);
+
+ /* Set TX data attributes. */
+ tdata->rx_timestamp = rdata->pkt_info.timestamp;
+
+ /* Copy all the via headers, in order. */
+ via = rdata->msg_info.via;
+ while (via) {
+ pjsip_msg_add_hdr( msg, pjsip_hdr_clone(tdata->pool, via));
+ via = via->next;
+ if (via != (void*)&req_msg->hdr)
+ via = pjsip_msg_find_hdr(req_msg, PJSIP_H_VIA, via);
+ else
+ break;
+ }
+
+ /* Copy all Record-Route headers, in order. */
+ rr = pjsip_msg_find_hdr(req_msg, PJSIP_H_RECORD_ROUTE, NULL);
+ while (rr) {
+ pjsip_msg_add_hdr(msg, pjsip_hdr_clone(tdata->pool, rr));
+ rr = rr->next;
+ if (rr != (void*)&req_msg->hdr)
+ rr = pjsip_msg_find_hdr(req_msg, PJSIP_H_RECORD_ROUTE, rr);
+ else
+ break;
+ }
+
+ /* Copy Call-ID header. */
+ hdr = pjsip_msg_find_hdr( req_msg, PJSIP_H_CALL_ID, NULL);
+ pjsip_msg_add_hdr(msg, pjsip_hdr_clone(tdata->pool, hdr));
+
+ /* Copy From header. */
+ hdr = pjsip_hdr_clone(tdata->pool, rdata->msg_info.from);
+ pjsip_msg_add_hdr( msg, hdr);
+
+ /* Copy To header. */
+ hdr = pjsip_hdr_clone(tdata->pool, rdata->msg_info.to);
+ pjsip_msg_add_hdr( msg, hdr);
+
+ /* Copy CSeq header. */
+ hdr = pjsip_hdr_clone(tdata->pool, rdata->msg_info.cseq);
+ pjsip_msg_add_hdr( msg, hdr);
+
+ /* All done. */
+ *p_tdata = tdata;
+ return PJ_SUCCESS;
+}
+
+
+/*
+ * Construct ACK for 3xx-6xx final response (according to chapter 17.1.1 of
+ * RFC3261). Note that the generation of ACK for 2xx response is different,
+ * and one must not use this function to generate such ACK.
+ */
+PJ_DEF(void) pjsip_endpt_create_ack(pjsip_endpoint *endpt,
+ pjsip_tx_data *tdata,
+ const pjsip_rx_data *rdata )
+{
+ pjsip_msg *ack_msg, *invite_msg;
+ pjsip_to_hdr *to;
+ pjsip_from_hdr *from;
+ pjsip_cseq_hdr *cseq;
+ pjsip_hdr *hdr;
+
+ /* Make compiler happy. */
+ PJ_UNUSED_ARG(endpt);
+
+ /* rdata must be a final response. */
+ pj_assert(rdata->msg_info.msg->type==PJSIP_RESPONSE_MSG &&
+ rdata->msg_info.msg->line.status.code >= 300);
+
+ /* Log this action. */
+ PJ_LOG(5,(THIS_FILE, "pjsip_endpt_create_ack(rdata=%p)", rdata));
+
+ /* Create new request message. */
+ ack_msg = pjsip_msg_create(tdata->pool, PJSIP_REQUEST_MSG);
+ pjsip_method_set( &ack_msg->line.req.method, PJSIP_ACK_METHOD );
+
+ /* The original INVITE message. */
+ invite_msg = tdata->msg;
+
+ /* Copy Request-Uri from the original INVITE. */
+ ack_msg->line.req.uri = invite_msg->line.req.uri;
+
+ /* Copy Call-ID from the original INVITE */
+ hdr = pjsip_msg_find_remove_hdr( invite_msg, PJSIP_H_CALL_ID, NULL);
+ pjsip_msg_add_hdr( ack_msg, hdr );
+
+ /* Copy From header from the original INVITE. */
+ from = (pjsip_from_hdr*)pjsip_msg_find_remove_hdr(invite_msg,
+ PJSIP_H_FROM, NULL);
+ pjsip_msg_add_hdr( ack_msg, (pjsip_hdr*)from );
+
+ /* Copy To header from the original INVITE. */
+ to = (pjsip_to_hdr*)pjsip_msg_find_remove_hdr( invite_msg,
+ PJSIP_H_TO, NULL);
+ pj_strdup(tdata->pool, &to->tag, &rdata->msg_info.to->tag);
+ pjsip_msg_add_hdr( ack_msg, (pjsip_hdr*)to );
+
+ /* Must contain single Via, just as the original INVITE. */
+ hdr = pjsip_msg_find_remove_hdr( invite_msg, PJSIP_H_VIA, NULL);
+ pjsip_msg_insert_first_hdr( ack_msg, hdr );
+
+ /* Must have the same CSeq value as the original INVITE, but method
+ * changed to ACK
+ */
+ cseq = (pjsip_cseq_hdr*) pjsip_msg_find_remove_hdr( invite_msg,
+ PJSIP_H_CSEQ, NULL);
+ pjsip_method_set( &cseq->method, PJSIP_ACK_METHOD );
+ pjsip_msg_add_hdr( ack_msg, (pjsip_hdr*) cseq );
+
+ /* If the original INVITE has Route headers, those header fields MUST
+ * appear in the ACK.
+ */
+ hdr = pjsip_msg_find_remove_hdr( invite_msg, PJSIP_H_ROUTE, NULL);
+ while (hdr != NULL) {
+ pjsip_msg_add_hdr( ack_msg, hdr );
+ hdr = pjsip_msg_find_remove_hdr( invite_msg, PJSIP_H_ROUTE, NULL);
+ }
+
+ /* Set the message in the "tdata" to point to the ACK message. */
+ tdata->msg = ack_msg;
+
+ /* Reset transmit packet buffer, to force 're-printing' of message. */
+ tdata->buf.cur = tdata->buf.start;
+
+ /* We're done.
+ * "tdata" parameter now contains the ACK message.
+ */
+}
+
+
+/*
+ * Construct CANCEL request for the previously sent request, according to
+ * chapter 9.1 of RFC3261.
+ */
+PJ_DEF(pj_status_t) pjsip_endpt_create_cancel( pjsip_endpoint *endpt,
+ pjsip_tx_data *req_tdata,
+ pjsip_tx_data **p_tdata)
+{
+ pjsip_msg *req_msg; /* the original request. */
+ pjsip_tx_data *cancel_tdata;
+ pjsip_msg *cancel_msg;
+ pjsip_hdr *hdr;
+ pjsip_cseq_hdr *req_cseq, *cseq;
+ pjsip_uri *req_uri;
+ pj_status_t status;
+
+ /* Log this action. */
+ PJ_LOG(5,(THIS_FILE, "pjsip_endpt_create_cancel(tdata=%p)", req_tdata));
+
+ /* Get the original request. */
+ req_msg = req_tdata->msg;
+
+ /* The transmit buffer must INVITE request. */
+ PJ_ASSERT_RETURN(req_msg->type == PJSIP_REQUEST_MSG &&
+ req_msg->line.req.method.id == PJSIP_INVITE_METHOD,
+ PJ_EINVAL);
+
+ /* Create new transmit buffer. */
+ status = pjsip_endpt_create_tdata( endpt, &cancel_tdata);
+ if (status != PJ_SUCCESS) {
+ return status;
+ }
+
+ /* Create CANCEL request message. */
+ cancel_msg = pjsip_msg_create(cancel_tdata->pool, PJSIP_REQUEST_MSG);
+ cancel_tdata->msg = cancel_msg;
+
+ /* Request-URI, Call-ID, From, To, and the numeric part of the CSeq are
+ * copied from the original request.
+ */
+ /* Set request line. */
+ pjsip_method_set(&cancel_msg->line.req.method, PJSIP_CANCEL_METHOD);
+ req_uri = req_msg->line.req.uri;
+ cancel_msg->line.req.uri = pjsip_uri_clone(cancel_tdata->pool, req_uri);
+
+ /* Copy Call-ID */
+ hdr = pjsip_msg_find_hdr(req_msg, PJSIP_H_CALL_ID, NULL);
+ pjsip_msg_add_hdr(cancel_msg, pjsip_hdr_clone(cancel_tdata->pool, hdr));
+
+ /* Copy From header. */
+ hdr = pjsip_msg_find_hdr(req_msg, PJSIP_H_FROM, NULL);
+ pjsip_msg_add_hdr(cancel_msg, pjsip_hdr_clone(cancel_tdata->pool, hdr));
+
+ /* Copy To header. */
+ hdr = pjsip_msg_find_hdr(req_msg, PJSIP_H_TO, NULL);
+ pjsip_msg_add_hdr(cancel_msg, pjsip_hdr_clone(cancel_tdata->pool, hdr));
+
+ /* Create new CSeq with equal number, but method set to CANCEL. */
+ req_cseq = (pjsip_cseq_hdr*) pjsip_msg_find_hdr(req_msg, PJSIP_H_CSEQ, NULL);
+ cseq = pjsip_cseq_hdr_create(cancel_tdata->pool);
+ cseq->cseq = req_cseq->cseq;
+ pjsip_method_set(&cseq->method, PJSIP_CANCEL_METHOD);
+ pjsip_msg_add_hdr(cancel_msg, (pjsip_hdr*)cseq);
+
+ /* Must only have single Via which matches the top-most Via in the
+ * request being cancelled.
+ */
+ hdr = pjsip_msg_find_hdr(req_msg, PJSIP_H_VIA, NULL);
+ pjsip_msg_insert_first_hdr(cancel_msg,
+ pjsip_hdr_clone(cancel_tdata->pool, hdr));
+
+ /* If the original request has Route header, the CANCEL request must also
+ * has exactly the same.
+ * Copy "Route" header from the request.
+ */
+ hdr = pjsip_msg_find_hdr(req_msg, PJSIP_H_ROUTE, NULL);
+ while (hdr != NULL) {
+ pjsip_msg_add_hdr(cancel_msg, pjsip_hdr_clone(cancel_tdata->pool, hdr));
+ hdr = hdr->next;
+ if (hdr != &cancel_msg->hdr)
+ hdr = pjsip_msg_find_hdr(req_msg, PJSIP_H_ROUTE, hdr);
+ else
+ break;
+ }
+
+ /* Done.
+ * Return the transmit buffer containing the CANCEL request.
+ */
+ *p_tdata = cancel_tdata;
+ return PJ_SUCCESS;
+}
+
+/* Get the address parameters (host, port, flag, TTL, etc) to send the
+ * response.
+ */
+PJ_DEF(pj_status_t) pjsip_get_response_addr(pj_pool_t *pool,
+ const pjsip_transport *req_transport,
+ const pjsip_via_hdr *via,
+ pjsip_host_port *send_addr)
+{
+ /* Determine the destination address (section 18.2.2):
+ * - for TCP, SCTP, or TLS, send the response using the transport where
+ * the request was received.
+ * - if maddr parameter is present, send to this address using the port
+ * in sent-by or 5060. If multicast is used, the TTL in the Via must
+ * be used, or 1 if ttl parameter is not present.
+ * - otherwise if received parameter is present, set to this address.
+ * - otherwise send to the address in sent-by.
+ */
+ send_addr->flag = req_transport->flag;
+ send_addr->type = req_transport->type;
+
+ if (PJSIP_TRANSPORT_IS_RELIABLE(req_transport)) {
+ const pj_sockaddr_in *remote_addr;
+ remote_addr = &req_transport->rem_addr;
+ pj_strdup2(pool, &send_addr->host,
+ pj_inet_ntoa(remote_addr->sin_addr));
+ send_addr->port = pj_sockaddr_in_get_port(remote_addr);
+
+ } else {
+ /* Set the host part */
+ if (via->maddr_param.slen) {
+ pj_strdup(pool, &send_addr->host, &via->maddr_param);
+ } else if (via->recvd_param.slen) {
+ pj_strdup(pool, &send_addr->host, &via->recvd_param);
+ } else {
+ pj_strdup(pool, &send_addr->host, &via->sent_by.host);
+ }
+
+ /* Set the port */
+ send_addr->port = via->sent_by.port;
+ }
+
+ return PJ_SUCCESS;
+}
+
+/*
+ * Get the event string from the event ID.
+ */
+PJ_DEF(const char *) pjsip_event_str(pjsip_event_id_e e)
+{
+ return event_str[e];
+}
+
diff --git a/pjsip/src/pjsua/getopt.c b/pjsip/src/pjsua/getopt.c
index d2812f2b..b522c579 100644
--- a/pjsip/src/pjsua/getopt.c
+++ b/pjsip/src/pjsua/getopt.c
@@ -1,1061 +1,1061 @@
-/* $Id$ */
-/*
- * Copyright (C) 2003-2006 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
- */
-
-#ifdef _MSC_VER
-/* in VC this file will generate a lot of warning about old style function
- * declarations.
- */
-# pragma warning(push, 3)
-#endif
-
-/*
- * getopt entry points
- *
- * modified by Mike Borella <mike_borella@mw.3com.com>
- *
- * $Id: getopt.c,v 1.4 2000/10/30 22:06:03 mborella Exp $
- */
-
-#ifdef HAVE_CONFIG_H
-#include "config.h"
-#endif
-
-#ifndef HAVE_GETOPT_LONG
-
-/* getopt_long and getopt_long_only entry points for GNU getopt.
- Copyright (C) 1987,88,89,90,91,92,93,94,96,97 Free Software Foundation, Inc.
- This file is part of the GNU C Library.
-
- The GNU C Library is free software; you can redistribute it and/or
- modify it under the terms of the GNU Library General Public License as
- published by the Free Software Foundation; either version 2 of the
- License, or (at your option) any later version.
-
- The GNU C Library 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
- Library General Public License for more details.
-
- You should have received a copy of the GNU Library General Public
- License along with the GNU C Library; see the file COPYING.LIB. If not,
- write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
- Boston, MA 02111-1307, USA. */
-
-#include "getopt.h"
-
-
-#include <stdio.h>
-
-/* Comment out all this code if we are using the GNU C Library, and are not
- actually compiling the library itself. This code is part of the GNU C
- Library, but also included in many other GNU distributions. Compiling
- and linking in this code is a waste when using the GNU C library
- (especially if it is a shared library). Rather than having every GNU
- program understand `configure --with-gnu-libc' and omit the object files,
- it is simpler to just do this in the source for each such file. */
-
-#define GETOPT_INTERFACE_VERSION 2
-#if !defined (_LIBC) && defined (__GLIBC__) && __GLIBC__ >= 2
-#include <gnu-versions.h>
-#if _GNU_GETOPT_INTERFACE_VERSION == GETOPT_INTERFACE_VERSION
-#define ELIDE_CODE
-#endif
-#endif
-
-#ifndef ELIDE_CODE
-
-
-/* This needs to come after some library #include
- to get __GNU_LIBRARY__ defined. */
-#ifdef __GNU_LIBRARY__
-#include <stdlib.h>
-#endif
-
-#ifndef NULL
-#define NULL 0
-#endif
-
-int
-getopt_long (int argc, char *const *argv, const char *options,
- const struct option *long_options, int *opt_index)
-{
- return _getopt_internal (argc, argv, options, long_options, opt_index, 0);
-}
-
-/* Like getopt_long, but '-' as well as '--' can indicate a long option.
- If an option that starts with '-' (not '--') doesn't match a long option,
- but does match a short option, it is parsed as a short option
- instead. */
-
-int
-getopt_long_only (argc, argv, options, long_options, opt_index)
- int argc;
- char *const *argv;
- const char *options;
- const struct option *long_options;
- int *opt_index;
-{
- return _getopt_internal (argc, argv, options, long_options, opt_index, 1);
-}
-
-int
-getopt (int argc, char * const * argv, const char * optstring)
-{
- return _getopt_internal (argc, argv, optstring,
- (const struct option *) 0,
- (int *) 0,
- 0);
-}
-
-#endif /* Not ELIDE_CODE. */
-
-
-#ifndef _NO_PROTO
-#define _NO_PROTO
-#endif
-
-
-//#include <strings.h>
-
-/* Comment out all this code if we are using the GNU C Library, and are not
- actually compiling the library itself. This code is part of the GNU C
- Library, but also included in many other GNU distributions. Compiling
- and linking in this code is a waste when using the GNU C library
- (especially if it is a shared library). Rather than having every GNU
- program understand `configure --with-gnu-libc' and omit the object files,
- it is simpler to just do this in the source for each such file. */
-
-#define GETOPT_INTERFACE_VERSION 2
-#if !defined (_LIBC) && defined (__GLIBC__) && __GLIBC__ >= 2
-#include <gnu-versions.h>
-#if _GNU_GETOPT_INTERFACE_VERSION == GETOPT_INTERFACE_VERSION
-#define ELIDE_CODE
-#endif
-#endif
-
-#ifndef ELIDE_CODE
-
-
-/* This needs to come after some library #include
- to get __GNU_LIBRARY__ defined. */
-#ifdef __GNU_LIBRARY__
-/* Don't include stdlib.h for non-GNU C libraries because some of them
- contain conflicting prototypes for getopt. */
-#include <stdlib.h>
-#include <unistd.h>
-#endif /* GNU C library. */
-
-#ifdef VMS
-#include <unixlib.h>
-#if HAVE_STRING_H - 0
-#include <string.h>
-#endif
-#endif
-
-#if defined (WIN32) && !defined (__CYGWIN32__)
-/* It's not Unix, really. See? Capital letters. */
-#include <windows.h>
-#define getpid() GetCurrentProcessId()
-#endif
-
-#ifndef _
-/* This is for other GNU distributions with internationalized messages.
- When compiling libc, the _ macro is predefined. */
-#ifdef HAVE_LIBINTL_H
-# include <libintl.h>
-# define _(msgid) gettext (msgid)
-#else
-# define _(msgid) (msgid)
-#endif
-#endif
-
-/* This version of `getopt' appears to the caller like standard Unix `getopt'
- but it behaves differently for the user, since it allows the user
- to intersperse the options with the other arguments.
-
- As `getopt' works, it permutes the elements of ARGV so that,
- when it is done, all the options precede everything else. Thus
- all application programs are extended to handle flexible argument order.
-
- Setting the environment variable POSIXLY_CORRECT disables permutation.
- Then the behavior is completely standard.
-
- GNU application programs can use a third alternative mode in which
- they can distinguish the relative order of options and other arguments. */
-
-#include "getopt.h"
-
-/* For communication from `getopt' to the caller.
- When `getopt' finds an option that takes an argument,
- the argument value is returned here.
- Also, when `ordering' is RETURN_IN_ORDER,
- each non-option ARGV-element is returned here. */
-
-char *optarg = NULL;
-
-/* Index in ARGV of the next element to be scanned.
- This is used for communication to and from the caller
- and for communication between successive calls to `getopt'.
-
- On entry to `getopt', zero means this is the first call; initialize.
-
- When `getopt' returns -1, this is the index of the first of the
- non-option elements that the caller should itself scan.
-
- Otherwise, `optind' communicates from one call to the next
- how much of ARGV has been scanned so far. */
-
-/* 1003.2 says this must be 1 before any call. */
-int optind = 1;
-
-/* Formerly, initialization of getopt depended on optind==0, which
- causes problems with re-calling getopt as programs generally don't
- know that. */
-
-int __getopt_initialized = 0;
-
-/* The next char to be scanned in the option-element
- in which the last option character we returned was found.
- This allows us to pick up the scan where we left off.
-
- If this is zero, or a null string, it means resume the scan
- by advancing to the next ARGV-element. */
-
-static char *nextchar;
-
-/* Callers store zero here to inhibit the error message
- for unrecognized options. */
-
-int opterr = 1;
-
-/* Set to an option character which was unrecognized.
- This must be initialized on some systems to avoid linking in the
- system's own getopt implementation. */
-
-int optopt = '?';
-
-/* Describe how to deal with options that follow non-option ARGV-elements.
-
- If the caller did not specify anything,
- the default is REQUIRE_ORDER if the environment variable
- POSIXLY_CORRECT is defined, PERMUTE otherwise.
-
- REQUIRE_ORDER means don't recognize them as options;
- stop option processing when the first non-option is seen.
- This is what Unix does.
- This mode of operation is selected by either setting the environment
- variable POSIXLY_CORRECT, or using `+' as the first character
- of the list of option characters.
-
- PERMUTE is the default. We permute the contents of ARGV as we scan,
- so that eventually all the non-options are at the end. This allows options
- to be given in any order, even with programs that were not written to
- expect this.
-
- RETURN_IN_ORDER is an option available to programs that were written
- to expect options and other ARGV-elements in any order and that care about
- the ordering of the two. We describe each non-option ARGV-element
- as if it were the argument of an option with character code 1.
- Using `-' as the first character of the list of option characters
- selects this mode of operation.
-
- The special argument `--' forces an end of option-scanning regardless
- of the value of `ordering'. In the case of RETURN_IN_ORDER, only
- `--' can cause `getopt' to return -1 with `optind' != ARGC. */
-
-static enum
-{
- REQUIRE_ORDER, PERMUTE, RETURN_IN_ORDER
-} ordering;
-
-/* Value of POSIXLY_CORRECT environment variable. */
-static char *posixly_correct;
-
-#ifdef __GNU_LIBRARY__
-/* We want to avoid inclusion of string.h with non-GNU libraries
- because there are many ways it can cause trouble.
- On some systems, it contains special magic macros that don't work
- in GCC. */
-#include <string.h>
-#define my_index pj_native_strchr
-#else
-
-static char *
-my_index (const char *str, int chr)
-{
- while (*str)
- {
- if (*str == chr)
- return (char *) str;
- str++;
- }
- return 0;
-}
-
-/* If using GCC, we can safely declare strlen this way.
- If not using GCC, it is ok not to declare it. */
-#ifdef __GNUC__
-/* Note that Motorola Delta 68k R3V7 comes with GCC but not stddef.h.
- That was relevant to code that was here before. */
-#if !defined (__STDC__) || !__STDC__
-/* gcc with -traditional declares the built-in strlen to return int,
- and has done so at least since version 2.4.5. -- rms. */
-extern int strlen (const char *);
-#endif /* not __STDC__ */
-#endif /* __GNUC__ */
-
-#endif /* not __GNU_LIBRARY__ */
-
-/* Handle permutation of arguments. */
-
-/* Describe the part of ARGV that contains non-options that have
- been skipped. `first_nonopt' is the index in ARGV of the first of them;
- `last_nonopt' is the index after the last of them. */
-
-static int first_nonopt;
-static int last_nonopt;
-
-#ifdef _LIBC
-/* Bash 2.0 gives us an environment variable containing flags
- indicating ARGV elements that should not be considered arguments. */
-
-/* Defined in getopt_init.c */
-extern char *__getopt_nonoption_flags;
-
-static int nonoption_flags_max_len;
-static int nonoption_flags_len;
-
-static int original_argc;
-static char *const *original_argv;
-
-extern pid_t __libc_pid;
-
-/* Make sure the environment variable bash 2.0 puts in the environment
- is valid for the getopt call we must make sure that the ARGV passed
- to getopt is that one passed to the process. */
-static void
-__attribute__ ((unused))
-store_args_and_env (int argc, char *const *argv)
-{
- /* XXX This is no good solution. We should rather copy the args so
- that we can compare them later. But we must not use malloc(3). */
- original_argc = argc;
- original_argv = argv;
-}
-text_set_element (__libc_subinit, store_args_and_env);
-
-# define SWAP_FLAGS(ch1, ch2) \
- if (nonoption_flags_len > 0) \
- { \
- char __tmp = __getopt_nonoption_flags[ch1]; \
- __getopt_nonoption_flags[ch1] = __getopt_nonoption_flags[ch2]; \
- __getopt_nonoption_flags[ch2] = __tmp; \
- }
-#else /* !_LIBC */
-# define SWAP_FLAGS(ch1, ch2)
-#endif /* _LIBC */
-
-/* Exchange two adjacent subsequences of ARGV.
- One subsequence is elements [first_nonopt,last_nonopt)
- which contains all the non-options that have been skipped so far.
- The other is elements [last_nonopt,optind), which contains all
- the options processed since those non-options were skipped.
-
- `first_nonopt' and `last_nonopt' are relocated so that they describe
- the new indices of the non-options in ARGV after they are moved. */
-
-#if defined (__STDC__) && __STDC__
-static void exchange (char **);
-#endif
-
-static void
-exchange (argv)
- char **argv;
-{
- int bottom = first_nonopt;
- int middle = last_nonopt;
- int top = optind;
- char *tem;
-
- /* Exchange the shorter segment with the far end of the longer segment.
- That puts the shorter segment into the right place.
- It leaves the longer segment in the right place overall,
- but it consists of two parts that need to be swapped next. */
-
-#ifdef _LIBC
- /* First make sure the handling of the `__getopt_nonoption_flags'
- string can work normally. Our top argument must be in the range
- of the string. */
- if (nonoption_flags_len > 0 && top >= nonoption_flags_max_len)
- {
- /* We must extend the array. The user plays games with us and
- presents new arguments. */
- char *new_str = malloc (top + 1);
- if (new_str == NULL)
- nonoption_flags_len = nonoption_flags_max_len = 0;
- else
- {
- memcpy (new_str, __getopt_nonoption_flags, nonoption_flags_max_len);
- memset (&new_str[nonoption_flags_max_len], '\0',
- top + 1 - nonoption_flags_max_len);
- nonoption_flags_max_len = top + 1;
- __getopt_nonoption_flags = new_str;
- }
- }
-#endif
-
- while (top > middle && middle > bottom)
- {
- if (top - middle > middle - bottom)
- {
- /* Bottom segment is the short one. */
- int len = middle - bottom;
- register int i;
-
- /* Swap it with the top part of the top segment. */
- for (i = 0; i < len; i++)
- {
- tem = argv[bottom + i];
- argv[bottom + i] = argv[top - (middle - bottom) + i];
- argv[top - (middle - bottom) + i] = tem;
- SWAP_FLAGS (bottom + i, top - (middle - bottom) + i);
- }
- /* Exclude the moved bottom segment from further swapping. */
- top -= len;
- }
- else
- {
- /* Top segment is the short one. */
- int len = top - middle;
- register int i;
-
- /* Swap it with the bottom part of the bottom segment. */
- for (i = 0; i < len; i++)
- {
- tem = argv[bottom + i];
- argv[bottom + i] = argv[middle + i];
- argv[middle + i] = tem;
- SWAP_FLAGS (bottom + i, middle + i);
- }
- /* Exclude the moved top segment from further swapping. */
- bottom += len;
- }
- }
-
- /* Update records for the slots the non-options now occupy. */
-
- first_nonopt += (optind - last_nonopt);
- last_nonopt = optind;
-}
-
-/* Initialize the internal data when the first call is made. */
-
-#if defined (__STDC__) && __STDC__
-static const char *_getopt_initialize (int, char *const *, const char *);
-#endif
-static const char *
-_getopt_initialize (argc, argv, optstring)
- int argc;
- char *const *argv;
- const char *optstring;
-{
- /* Start processing options with ARGV-element 1 (since ARGV-element 0
- is the program name); the sequence of previously skipped
- non-option ARGV-elements is empty. */
-
- first_nonopt = last_nonopt = optind;
-
- nextchar = NULL;
-
- posixly_correct = getenv ("POSIXLY_CORRECT");
-
- /* Determine how to handle the ordering of options and nonoptions. */
-
- if (optstring[0] == '-')
- {
- ordering = RETURN_IN_ORDER;
- ++optstring;
- }
- else if (optstring[0] == '+')
- {
- ordering = REQUIRE_ORDER;
- ++optstring;
- }
- else if (posixly_correct != NULL)
- ordering = REQUIRE_ORDER;
- else
- ordering = PERMUTE;
-
-#ifdef _LIBC
- if (posixly_correct == NULL
- && argc == original_argc && argv == original_argv)
- {
- if (nonoption_flags_max_len == 0)
- {
- if (__getopt_nonoption_flags == NULL
- || __getopt_nonoption_flags[0] == '\0')
- nonoption_flags_max_len = -1;
- else
- {
- const char *orig_str = __getopt_nonoption_flags;
- int len = nonoption_flags_max_len = strlen (orig_str);
- if (nonoption_flags_max_len < argc)
- nonoption_flags_max_len = argc;
- __getopt_nonoption_flags =
- (char *) malloc (nonoption_flags_max_len);
- if (__getopt_nonoption_flags == NULL)
- nonoption_flags_max_len = -1;
- else
- {
- memcpy (__getopt_nonoption_flags, orig_str, len);
- memset (&__getopt_nonoption_flags[len], '\0',
- nonoption_flags_max_len - len);
- }
- }
- }
- nonoption_flags_len = nonoption_flags_max_len;
- }
- else
- nonoption_flags_len = 0;
-#endif
-
- return optstring;
-}
-
-/* Scan elements of ARGV (whose length is ARGC) for option characters
- given in OPTSTRING.
-
- If an element of ARGV starts with '-', and is not exactly "-" or "--",
- then it is an option element. The characters of this element
- (aside from the initial '-') are option characters. If `getopt'
- is called repeatedly, it returns successively each of the option characters
- from each of the option elements.
-
- If `getopt' finds another option character, it returns that character,
- updating `optind' and `nextchar' so that the next call to `getopt' can
- resume the scan with the following option character or ARGV-element.
-
- If there are no more option characters, `getopt' returns -1.
- Then `optind' is the index in ARGV of the first ARGV-element
- that is not an option. (The ARGV-elements have been permuted
- so that those that are not options now come last.)
-
- OPTSTRING is a string containing the legitimate option characters.
- If an option character is seen that is not listed in OPTSTRING,
- return '?' after printing an error message. If you set `opterr' to
- zero, the error message is suppressed but we still return '?'.
-
- If a char in OPTSTRING is followed by a colon, that means it wants an arg,
- so the following text in the same ARGV-element, or the text of the following
- ARGV-element, is returned in `optarg'. Two colons mean an option that
- wants an optional arg; if there is text in the current ARGV-element,
- it is returned in `optarg', otherwise `optarg' is set to zero.
-
- If OPTSTRING starts with `-' or `+', it requests different methods of
- handling the non-option ARGV-elements.
- See the comments about RETURN_IN_ORDER and REQUIRE_ORDER, above.
-
- Long-named options begin with `--' instead of `-'.
- Their names may be abbreviated as long as the abbreviation is unique
- or is an exact match for some defined option. If they have an
- argument, it follows the option name in the same ARGV-element, separated
- from the option name by a `=', or else the in next ARGV-element.
- When `getopt' finds a long-named option, it returns 0 if that option's
- `flag' field is nonzero, the value of the option's `val' field
- if the `flag' field is zero.
-
- The elements of ARGV aren't really const, because we permute them.
- But we pretend they're const in the prototype to be compatible
- with other systems.
-
- LONGOPTS is a vector of `struct option' terminated by an
- element containing a name which is zero.
-
- LONGIND returns the index in LONGOPT of the long-named option found.
- It is only valid when a long-named option has been found by the most
- recent call.
-
- If LONG_ONLY is nonzero, '-' as well as '--' can introduce
- long-named options. */
-
-int
-_getopt_internal (argc, argv, optstring, longopts, longind, long_only)
- int argc;
- char *const *argv;
- const char *optstring;
- const struct option *longopts;
- int *longind;
- int long_only;
-{
- optarg = NULL;
-
- if (optind == 0 || !__getopt_initialized)
- {
- if (optind == 0)
- optind = 1; /* Don't scan ARGV[0], the program name. */
- optstring = _getopt_initialize (argc, argv, optstring);
- __getopt_initialized = 1;
- }
-
- /* Test whether ARGV[optind] points to a non-option argument.
- Either it does not have option syntax, or there is an environment flag
- from the shell indicating it is not an option. The later information
- is only used when the used in the GNU libc. */
-#ifdef _LIBC
-#define NONOPTION_P (argv[optind][0] != '-' || argv[optind][1] == '\0' \
- || (optind < nonoption_flags_len \
- && __getopt_nonoption_flags[optind] == '1'))
-#else
-#define NONOPTION_P (argv[optind][0] != '-' || argv[optind][1] == '\0')
-#endif
-
- if (nextchar == NULL || *nextchar == '\0')
- {
- /* Advance to the next ARGV-element. */
-
- /* Give FIRST_NONOPT & LAST_NONOPT rational values if OPTIND has been
- moved back by the user (who may also have changed the arguments). */
- if (last_nonopt > optind)
- last_nonopt = optind;
- if (first_nonopt > optind)
- first_nonopt = optind;
-
- if (ordering == PERMUTE)
- {
- /* If we have just processed some options following some non-options,
- exchange them so that the options come first. */
-
- if (first_nonopt != last_nonopt && last_nonopt != optind)
- exchange ((char **) argv);
- else if (last_nonopt != optind)
- first_nonopt = optind;
-
- /* Skip any additional non-options
- and extend the range of non-options previously skipped. */
-
- while (optind < argc && NONOPTION_P)
- optind++;
- last_nonopt = optind;
- }
-
- /* The special ARGV-element `--' means premature end of options.
- Skip it like a null option,
- then exchange with previous non-options as if it were an option,
- then skip everything else like a non-option. */
-
- if (optind != argc && !pj_native_strcmp(argv[optind], "--"))
- {
- optind++;
-
- if (first_nonopt != last_nonopt && last_nonopt != optind)
- exchange ((char **) argv);
- else if (first_nonopt == last_nonopt)
- first_nonopt = optind;
- last_nonopt = argc;
-
- optind = argc;
- }
-
- /* If we have done all the ARGV-elements, stop the scan
- and back over any non-options that we skipped and permuted. */
-
- if (optind == argc)
- {
- /* Set the next-arg-index to point at the non-options
- that we previously skipped, so the caller will digest them. */
- if (first_nonopt != last_nonopt)
- optind = first_nonopt;
- return -1;
- }
-
- /* If we have come to a non-option and did not permute it,
- either stop the scan or describe it to the caller and pass it by. */
-
- if (NONOPTION_P)
- {
- if (ordering == REQUIRE_ORDER)
- return -1;
- optarg = argv[optind++];
- return 1;
- }
-
- /* We have found another option-ARGV-element.
- Skip the initial punctuation. */
-
- nextchar = (argv[optind] + 1
- + (longopts != NULL && argv[optind][1] == '-'));
- }
-
- /* Decode the current option-ARGV-element. */
-
- /* Check whether the ARGV-element is a long option.
-
- If long_only and the ARGV-element has the form "-f", where f is
- a valid short option, don't consider it an abbreviated form of
- a long option that starts with f. Otherwise there would be no
- way to give the -f short option.
-
- On the other hand, if there's a long option "fubar" and
- the ARGV-element is "-fu", do consider that an abbreviation of
- the long option, just like "--fu", and not "-f" with arg "u".
-
- This distinction seems to be the most useful approach. */
-
- if (longopts != NULL
- && (argv[optind][1] == '-'
- || (long_only && (argv[optind][2] || !my_index (optstring, argv[optind][1])))))
- {
- char *nameend;
- const struct option *p;
- const struct option *pfound = NULL;
- int exact = 0;
- int ambig = 0;
- int indfound = -1;
- int option_index;
-
- for (nameend = nextchar; *nameend && *nameend != '='; nameend++)
- /* Do nothing. */ ;
-
- /* Test all long options for either exact match
- or abbreviated matches. */
- for (p = longopts, option_index = 0; p->name; p++, option_index++)
- if (!strncmp (p->name, nextchar, nameend - nextchar))
- {
- if ((unsigned int) (nameend - nextchar)
- == (unsigned int) strlen (p->name))
- {
- /* Exact match found. */
- pfound = p;
- indfound = option_index;
- exact = 1;
- break;
- }
- else if (pfound == NULL)
- {
- /* First nonexact match found. */
- pfound = p;
- indfound = option_index;
- }
- else
- /* Second or later nonexact match found. */
- ambig = 1;
- }
-
- if (ambig && !exact)
- {
- if (opterr)
- fprintf (stderr, _("%s: option `%s' is ambiguous\n"),
- argv[0], argv[optind]);
- nextchar += strlen (nextchar);
- optind++;
- optopt = 0;
- return '?';
- }
-
- if (pfound != NULL)
- {
- option_index = indfound;
- optind++;
- if (*nameend)
- {
- /* Don't test has_arg with >, because some C compilers don't
- allow it to be used on enums. */
- if (pfound->has_arg)
- optarg = nameend + 1;
- else
- {
- if (opterr)
- {
- if (argv[optind - 1][1] == '-')
- /* --option */
- fprintf (stderr,
- _("%s: option `--%s' doesn't allow an argument\n"),
- argv[0], pfound->name);
- else
- {
- /* +option or -option */
- fprintf (stderr,
- _("%s: option `%c%s' doesn't allow an argument\n"),
- argv[0], argv[optind - 1][0], pfound->name);
- }
- }
- nextchar += strlen (nextchar);
-
- optopt = pfound->val;
- return '?';
- }
- }
- else if (pfound->has_arg == 1)
- {
- if (optind < argc)
- optarg = argv[optind++];
- else
- {
- if (opterr)
- fprintf (stderr,
- _("%s: option `%s' requires an argument\n"),
- argv[0], argv[optind - 1]);
- nextchar += strlen (nextchar);
- optopt = pfound->val;
- return optstring[0] == ':' ? ':' : '?';
- }
- }
- nextchar += strlen (nextchar);
- if (longind != NULL)
- *longind = option_index;
- if (pfound->flag)
- {
- *(pfound->flag) = pfound->val;
- return 0;
- }
- return pfound->val;
- }
-
- /* Can't find it as a long option. If this is not getopt_long_only,
- or the option starts with '--' or is not a valid short
- option, then it's an error.
- Otherwise interpret it as a short option. */
- if (!long_only || argv[optind][1] == '-'
- || my_index (optstring, *nextchar) == NULL)
- {
- if (opterr)
- {
- if (argv[optind][1] == '-')
- /* --option */
- fprintf (stderr, _("%s: unrecognized option `--%s'\n"),
- argv[0], nextchar);
- else
- /* +option or -option */
- fprintf (stderr, _("%s: unrecognized option `%c%s'\n"),
- argv[0], argv[optind][0], nextchar);
- }
- nextchar = (char *) "";
- optind++;
- optopt = 0;
- return '?';
- }
- }
-
- /* Look at and handle the next short option-character. */
-
- {
- char c = *nextchar++;
- char *temp = my_index (optstring, c);
-
- /* Increment `optind' when we start to process its last character. */
- if (*nextchar == '\0')
- ++optind;
-
- if (temp == NULL || c == ':')
- {
- if (opterr)
- {
- if (posixly_correct)
- /* 1003.2 specifies the format of this message. */
- fprintf (stderr, _("%s: illegal option -- %c\n"),
- argv[0], c);
- else
- fprintf (stderr, _("%s: invalid option -- %c\n"),
- argv[0], c);
- }
- optopt = c;
- return '?';
- }
- /* Convenience. Treat POSIX -W foo same as long option --foo */
- if (temp[0] == 'W' && temp[1] == ';')
- {
- char *nameend;
- const struct option *p;
- const struct option *pfound = NULL;
- int exact = 0;
- int ambig = 0;
- int indfound = 0;
- int option_index;
-
- /* This is an option that requires an argument. */
- if (*nextchar != '\0')
- {
- optarg = nextchar;
- /* If we end this ARGV-element by taking the rest as an arg,
- we must advance to the next element now. */
- optind++;
- }
- else if (optind == argc)
- {
- if (opterr)
- {
- /* 1003.2 specifies the format of this message. */
- fprintf (stderr, _("%s: option requires an argument -- %c\n"),
- argv[0], c);
- }
- optopt = c;
- if (optstring[0] == ':')
- c = ':';
- else
- c = '?';
- return c;
- }
- else
- /* We already incremented `optind' once;
- increment it again when taking next ARGV-elt as argument. */
- optarg = argv[optind++];
-
- /* optarg is now the argument, see if it's in the
- table of longopts. */
-
- for (nextchar = nameend = optarg; *nameend && *nameend != '='; nameend++)
- /* Do nothing. */ ;
-
- /* Test all long options for either exact match
- or abbreviated matches. */
- for (p = longopts, option_index = 0; p->name; p++, option_index++)
- if (!strncmp (p->name, nextchar, nameend - nextchar))
- {
- if ((unsigned int) (nameend - nextchar) == strlen (p->name))
- {
- /* Exact match found. */
- pfound = p;
- indfound = option_index;
- exact = 1;
- break;
- }
- else if (pfound == NULL)
- {
- /* First nonexact match found. */
- pfound = p;
- indfound = option_index;
- }
- else
- /* Second or later nonexact match found. */
- ambig = 1;
- }
- if (ambig && !exact)
- {
- if (opterr)
- fprintf (stderr, _("%s: option `-W %s' is ambiguous\n"),
- argv[0], argv[optind]);
- nextchar += strlen (nextchar);
- optind++;
- return '?';
- }
- if (pfound != NULL)
- {
- option_index = indfound;
- if (*nameend)
- {
- /* Don't test has_arg with >, because some C compilers don't
- allow it to be used on enums. */
- if (pfound->has_arg)
- optarg = nameend + 1;
- else
- {
- if (opterr)
- fprintf (stderr, _("\
-%s: option `-W %s' doesn't allow an argument\n"),
- argv[0], pfound->name);
-
- nextchar += strlen (nextchar);
- return '?';
- }
- }
- else if (pfound->has_arg == 1)
- {
- if (optind < argc)
- optarg = argv[optind++];
- else
- {
- if (opterr)
- fprintf (stderr,
- _("%s: option `%s' requires an argument\n"),
- argv[0], argv[optind - 1]);
- nextchar += strlen (nextchar);
- return optstring[0] == ':' ? ':' : '?';
- }
- }
- nextchar += strlen (nextchar);
- if (longind != NULL)
- *longind = option_index;
- if (pfound->flag)
- {
- *(pfound->flag) = pfound->val;
- return 0;
- }
- return pfound->val;
- }
- nextchar = NULL;
- return 'W'; /* Let the application handle it. */
- }
- if (temp[1] == ':')
- {
- if (temp[2] == ':')
- {
- /* This is an option that accepts an argument optionally. */
- if (*nextchar != '\0')
- {
- optarg = nextchar;
- optind++;
- }
- else
- optarg = NULL;
- nextchar = NULL;
- }
- else
- {
- /* This is an option that requires an argument. */
- if (*nextchar != '\0')
- {
- optarg = nextchar;
- /* If we end this ARGV-element by taking the rest as an arg,
- we must advance to the next element now. */
- optind++;
- }
- else if (optind == argc)
- {
- if (opterr)
- {
- /* 1003.2 specifies the format of this message. */
- fprintf (stderr,
- _("%s: option requires an argument -- %c\n"),
- argv[0], c);
- }
- optopt = c;
- if (optstring[0] == ':')
- c = ':';
- else
- c = '?';
- }
- else
- /* We already incremented `optind' once;
- increment it again when taking next ARGV-elt as argument. */
- optarg = argv[optind++];
- nextchar = NULL;
- }
- }
- return c;
- }
-}
-
-#endif /* Not ELIDE_CODE. */
-
-#endif
-
-#ifdef _MSC_VER
-# pragma warning(pop)
-#endif
-
+/* $Id$ */
+/*
+ * Copyright (C) 2003-2006 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
+ */
+
+#ifdef _MSC_VER
+/* in VC this file will generate a lot of warning about old style function
+ * declarations.
+ */
+# pragma warning(push, 3)
+#endif
+
+/*
+ * getopt entry points
+ *
+ * modified by Mike Borella <mike_borella@mw.3com.com>
+ *
+ * $Id: getopt.c,v 1.4 2000/10/30 22:06:03 mborella Exp $
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#ifndef HAVE_GETOPT_LONG
+
+/* getopt_long and getopt_long_only entry points for GNU getopt.
+ Copyright (C) 1987,88,89,90,91,92,93,94,96,97 Free Software Foundation, Inc.
+ This file is part of the GNU C Library.
+
+ The GNU C Library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version.
+
+ The GNU C Library 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
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public
+ License along with the GNU C Library; see the file COPYING.LIB. If not,
+ write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA. */
+
+#include "getopt.h"
+
+
+#include <stdio.h>
+
+/* Comment out all this code if we are using the GNU C Library, and are not
+ actually compiling the library itself. This code is part of the GNU C
+ Library, but also included in many other GNU distributions. Compiling
+ and linking in this code is a waste when using the GNU C library
+ (especially if it is a shared library). Rather than having every GNU
+ program understand `configure --with-gnu-libc' and omit the object files,
+ it is simpler to just do this in the source for each such file. */
+
+#define GETOPT_INTERFACE_VERSION 2
+#if !defined (_LIBC) && defined (__GLIBC__) && __GLIBC__ >= 2
+#include <gnu-versions.h>
+#if _GNU_GETOPT_INTERFACE_VERSION == GETOPT_INTERFACE_VERSION
+#define ELIDE_CODE
+#endif
+#endif
+
+#ifndef ELIDE_CODE
+
+
+/* This needs to come after some library #include
+ to get __GNU_LIBRARY__ defined. */
+#ifdef __GNU_LIBRARY__
+#include <stdlib.h>
+#endif
+
+#ifndef NULL
+#define NULL 0
+#endif
+
+int
+getopt_long (int argc, char *const *argv, const char *options,
+ const struct option *long_options, int *opt_index)
+{
+ return _getopt_internal (argc, argv, options, long_options, opt_index, 0);
+}
+
+/* Like getopt_long, but '-' as well as '--' can indicate a long option.
+ If an option that starts with '-' (not '--') doesn't match a long option,
+ but does match a short option, it is parsed as a short option
+ instead. */
+
+int
+getopt_long_only (argc, argv, options, long_options, opt_index)
+ int argc;
+ char *const *argv;
+ const char *options;
+ const struct option *long_options;
+ int *opt_index;
+{
+ return _getopt_internal (argc, argv, options, long_options, opt_index, 1);
+}
+
+int
+getopt (int argc, char * const * argv, const char * optstring)
+{
+ return _getopt_internal (argc, argv, optstring,
+ (const struct option *) 0,
+ (int *) 0,
+ 0);
+}
+
+#endif /* Not ELIDE_CODE. */
+
+
+#ifndef _NO_PROTO
+#define _NO_PROTO
+#endif
+
+
+//#include <strings.h>
+
+/* Comment out all this code if we are using the GNU C Library, and are not
+ actually compiling the library itself. This code is part of the GNU C
+ Library, but also included in many other GNU distributions. Compiling
+ and linking in this code is a waste when using the GNU C library
+ (especially if it is a shared library). Rather than having every GNU
+ program understand `configure --with-gnu-libc' and omit the object files,
+ it is simpler to just do this in the source for each such file. */
+
+#define GETOPT_INTERFACE_VERSION 2
+#if !defined (_LIBC) && defined (__GLIBC__) && __GLIBC__ >= 2
+#include <gnu-versions.h>
+#if _GNU_GETOPT_INTERFACE_VERSION == GETOPT_INTERFACE_VERSION
+#define ELIDE_CODE
+#endif
+#endif
+
+#ifndef ELIDE_CODE
+
+
+/* This needs to come after some library #include
+ to get __GNU_LIBRARY__ defined. */
+#ifdef __GNU_LIBRARY__
+/* Don't include stdlib.h for non-GNU C libraries because some of them
+ contain conflicting prototypes for getopt. */
+#include <stdlib.h>
+#include <unistd.h>
+#endif /* GNU C library. */
+
+#ifdef VMS
+#include <unixlib.h>
+#if HAVE_STRING_H - 0
+#include <string.h>
+#endif
+#endif
+
+#if defined (WIN32) && !defined (__CYGWIN32__)
+/* It's not Unix, really. See? Capital letters. */
+#include <windows.h>
+#define getpid() GetCurrentProcessId()
+#endif
+
+#ifndef _
+/* This is for other GNU distributions with internationalized messages.
+ When compiling libc, the _ macro is predefined. */
+#ifdef HAVE_LIBINTL_H
+# include <libintl.h>
+# define _(msgid) gettext (msgid)
+#else
+# define _(msgid) (msgid)
+#endif
+#endif
+
+/* This version of `getopt' appears to the caller like standard Unix `getopt'
+ but it behaves differently for the user, since it allows the user
+ to intersperse the options with the other arguments.
+
+ As `getopt' works, it permutes the elements of ARGV so that,
+ when it is done, all the options precede everything else. Thus
+ all application programs are extended to handle flexible argument order.
+
+ Setting the environment variable POSIXLY_CORRECT disables permutation.
+ Then the behavior is completely standard.
+
+ GNU application programs can use a third alternative mode in which
+ they can distinguish the relative order of options and other arguments. */
+
+#include "getopt.h"
+
+/* For communication from `getopt' to the caller.
+ When `getopt' finds an option that takes an argument,
+ the argument value is returned here.
+ Also, when `ordering' is RETURN_IN_ORDER,
+ each non-option ARGV-element is returned here. */
+
+char *optarg = NULL;
+
+/* Index in ARGV of the next element to be scanned.
+ This is used for communication to and from the caller
+ and for communication between successive calls to `getopt'.
+
+ On entry to `getopt', zero means this is the first call; initialize.
+
+ When `getopt' returns -1, this is the index of the first of the
+ non-option elements that the caller should itself scan.
+
+ Otherwise, `optind' communicates from one call to the next
+ how much of ARGV has been scanned so far. */
+
+/* 1003.2 says this must be 1 before any call. */
+int optind = 1;
+
+/* Formerly, initialization of getopt depended on optind==0, which
+ causes problems with re-calling getopt as programs generally don't
+ know that. */
+
+int __getopt_initialized = 0;
+
+/* The next char to be scanned in the option-element
+ in which the last option character we returned was found.
+ This allows us to pick up the scan where we left off.
+
+ If this is zero, or a null string, it means resume the scan
+ by advancing to the next ARGV-element. */
+
+static char *nextchar;
+
+/* Callers store zero here to inhibit the error message
+ for unrecognized options. */
+
+int opterr = 1;
+
+/* Set to an option character which was unrecognized.
+ This must be initialized on some systems to avoid linking in the
+ system's own getopt implementation. */
+
+int optopt = '?';
+
+/* Describe how to deal with options that follow non-option ARGV-elements.
+
+ If the caller did not specify anything,
+ the default is REQUIRE_ORDER if the environment variable
+ POSIXLY_CORRECT is defined, PERMUTE otherwise.
+
+ REQUIRE_ORDER means don't recognize them as options;
+ stop option processing when the first non-option is seen.
+ This is what Unix does.
+ This mode of operation is selected by either setting the environment
+ variable POSIXLY_CORRECT, or using `+' as the first character
+ of the list of option characters.
+
+ PERMUTE is the default. We permute the contents of ARGV as we scan,
+ so that eventually all the non-options are at the end. This allows options
+ to be given in any order, even with programs that were not written to
+ expect this.
+
+ RETURN_IN_ORDER is an option available to programs that were written
+ to expect options and other ARGV-elements in any order and that care about
+ the ordering of the two. We describe each non-option ARGV-element
+ as if it were the argument of an option with character code 1.
+ Using `-' as the first character of the list of option characters
+ selects this mode of operation.
+
+ The special argument `--' forces an end of option-scanning regardless
+ of the value of `ordering'. In the case of RETURN_IN_ORDER, only
+ `--' can cause `getopt' to return -1 with `optind' != ARGC. */
+
+static enum
+{
+ REQUIRE_ORDER, PERMUTE, RETURN_IN_ORDER
+} ordering;
+
+/* Value of POSIXLY_CORRECT environment variable. */
+static char *posixly_correct;
+
+#ifdef __GNU_LIBRARY__
+/* We want to avoid inclusion of string.h with non-GNU libraries
+ because there are many ways it can cause trouble.
+ On some systems, it contains special magic macros that don't work
+ in GCC. */
+#include <string.h>
+#define my_index pj_native_strchr
+#else
+
+static char *
+my_index (const char *str, int chr)
+{
+ while (*str)
+ {
+ if (*str == chr)
+ return (char *) str;
+ str++;
+ }
+ return 0;
+}
+
+/* If using GCC, we can safely declare strlen this way.
+ If not using GCC, it is ok not to declare it. */
+#ifdef __GNUC__
+/* Note that Motorola Delta 68k R3V7 comes with GCC but not stddef.h.
+ That was relevant to code that was here before. */
+#if !defined (__STDC__) || !__STDC__
+/* gcc with -traditional declares the built-in strlen to return int,
+ and has done so at least since version 2.4.5. -- rms. */
+extern int strlen (const char *);
+#endif /* not __STDC__ */
+#endif /* __GNUC__ */
+
+#endif /* not __GNU_LIBRARY__ */
+
+/* Handle permutation of arguments. */
+
+/* Describe the part of ARGV that contains non-options that have
+ been skipped. `first_nonopt' is the index in ARGV of the first of them;
+ `last_nonopt' is the index after the last of them. */
+
+static int first_nonopt;
+static int last_nonopt;
+
+#ifdef _LIBC
+/* Bash 2.0 gives us an environment variable containing flags
+ indicating ARGV elements that should not be considered arguments. */
+
+/* Defined in getopt_init.c */
+extern char *__getopt_nonoption_flags;
+
+static int nonoption_flags_max_len;
+static int nonoption_flags_len;
+
+static int original_argc;
+static char *const *original_argv;
+
+extern pid_t __libc_pid;
+
+/* Make sure the environment variable bash 2.0 puts in the environment
+ is valid for the getopt call we must make sure that the ARGV passed
+ to getopt is that one passed to the process. */
+static void
+__attribute__ ((unused))
+store_args_and_env (int argc, char *const *argv)
+{
+ /* XXX This is no good solution. We should rather copy the args so
+ that we can compare them later. But we must not use malloc(3). */
+ original_argc = argc;
+ original_argv = argv;
+}
+text_set_element (__libc_subinit, store_args_and_env);
+
+# define SWAP_FLAGS(ch1, ch2) \
+ if (nonoption_flags_len > 0) \
+ { \
+ char __tmp = __getopt_nonoption_flags[ch1]; \
+ __getopt_nonoption_flags[ch1] = __getopt_nonoption_flags[ch2]; \
+ __getopt_nonoption_flags[ch2] = __tmp; \
+ }
+#else /* !_LIBC */
+# define SWAP_FLAGS(ch1, ch2)
+#endif /* _LIBC */
+
+/* Exchange two adjacent subsequences of ARGV.
+ One subsequence is elements [first_nonopt,last_nonopt)
+ which contains all the non-options that have been skipped so far.
+ The other is elements [last_nonopt,optind), which contains all
+ the options processed since those non-options were skipped.
+
+ `first_nonopt' and `last_nonopt' are relocated so that they describe
+ the new indices of the non-options in ARGV after they are moved. */
+
+#if defined (__STDC__) && __STDC__
+static void exchange (char **);
+#endif
+
+static void
+exchange (argv)
+ char **argv;
+{
+ int bottom = first_nonopt;
+ int middle = last_nonopt;
+ int top = optind;
+ char *tem;
+
+ /* Exchange the shorter segment with the far end of the longer segment.
+ That puts the shorter segment into the right place.
+ It leaves the longer segment in the right place overall,
+ but it consists of two parts that need to be swapped next. */
+
+#ifdef _LIBC
+ /* First make sure the handling of the `__getopt_nonoption_flags'
+ string can work normally. Our top argument must be in the range
+ of the string. */
+ if (nonoption_flags_len > 0 && top >= nonoption_flags_max_len)
+ {
+ /* We must extend the array. The user plays games with us and
+ presents new arguments. */
+ char *new_str = malloc (top + 1);
+ if (new_str == NULL)
+ nonoption_flags_len = nonoption_flags_max_len = 0;
+ else
+ {
+ memcpy (new_str, __getopt_nonoption_flags, nonoption_flags_max_len);
+ memset (&new_str[nonoption_flags_max_len], '\0',
+ top + 1 - nonoption_flags_max_len);
+ nonoption_flags_max_len = top + 1;
+ __getopt_nonoption_flags = new_str;
+ }
+ }
+#endif
+
+ while (top > middle && middle > bottom)
+ {
+ if (top - middle > middle - bottom)
+ {
+ /* Bottom segment is the short one. */
+ int len = middle - bottom;
+ register int i;
+
+ /* Swap it with the top part of the top segment. */
+ for (i = 0; i < len; i++)
+ {
+ tem = argv[bottom + i];
+ argv[bottom + i] = argv[top - (middle - bottom) + i];
+ argv[top - (middle - bottom) + i] = tem;
+ SWAP_FLAGS (bottom + i, top - (middle - bottom) + i);
+ }
+ /* Exclude the moved bottom segment from further swapping. */
+ top -= len;
+ }
+ else
+ {
+ /* Top segment is the short one. */
+ int len = top - middle;
+ register int i;
+
+ /* Swap it with the bottom part of the bottom segment. */
+ for (i = 0; i < len; i++)
+ {
+ tem = argv[bottom + i];
+ argv[bottom + i] = argv[middle + i];
+ argv[middle + i] = tem;
+ SWAP_FLAGS (bottom + i, middle + i);
+ }
+ /* Exclude the moved top segment from further swapping. */
+ bottom += len;
+ }
+ }
+
+ /* Update records for the slots the non-options now occupy. */
+
+ first_nonopt += (optind - last_nonopt);
+ last_nonopt = optind;
+}
+
+/* Initialize the internal data when the first call is made. */
+
+#if defined (__STDC__) && __STDC__
+static const char *_getopt_initialize (int, char *const *, const char *);
+#endif
+static const char *
+_getopt_initialize (argc, argv, optstring)
+ int argc;
+ char *const *argv;
+ const char *optstring;
+{
+ /* Start processing options with ARGV-element 1 (since ARGV-element 0
+ is the program name); the sequence of previously skipped
+ non-option ARGV-elements is empty. */
+
+ first_nonopt = last_nonopt = optind;
+
+ nextchar = NULL;
+
+ posixly_correct = getenv ("POSIXLY_CORRECT");
+
+ /* Determine how to handle the ordering of options and nonoptions. */
+
+ if (optstring[0] == '-')
+ {
+ ordering = RETURN_IN_ORDER;
+ ++optstring;
+ }
+ else if (optstring[0] == '+')
+ {
+ ordering = REQUIRE_ORDER;
+ ++optstring;
+ }
+ else if (posixly_correct != NULL)
+ ordering = REQUIRE_ORDER;
+ else
+ ordering = PERMUTE;
+
+#ifdef _LIBC
+ if (posixly_correct == NULL
+ && argc == original_argc && argv == original_argv)
+ {
+ if (nonoption_flags_max_len == 0)
+ {
+ if (__getopt_nonoption_flags == NULL
+ || __getopt_nonoption_flags[0] == '\0')
+ nonoption_flags_max_len = -1;
+ else
+ {
+ const char *orig_str = __getopt_nonoption_flags;
+ int len = nonoption_flags_max_len = strlen (orig_str);
+ if (nonoption_flags_max_len < argc)
+ nonoption_flags_max_len = argc;
+ __getopt_nonoption_flags =
+ (char *) malloc (nonoption_flags_max_len);
+ if (__getopt_nonoption_flags == NULL)
+ nonoption_flags_max_len = -1;
+ else
+ {
+ memcpy (__getopt_nonoption_flags, orig_str, len);
+ memset (&__getopt_nonoption_flags[len], '\0',
+ nonoption_flags_max_len - len);
+ }
+ }
+ }
+ nonoption_flags_len = nonoption_flags_max_len;
+ }
+ else
+ nonoption_flags_len = 0;
+#endif
+
+ return optstring;
+}
+
+/* Scan elements of ARGV (whose length is ARGC) for option characters
+ given in OPTSTRING.
+
+ If an element of ARGV starts with '-', and is not exactly "-" or "--",
+ then it is an option element. The characters of this element
+ (aside from the initial '-') are option characters. If `getopt'
+ is called repeatedly, it returns successively each of the option characters
+ from each of the option elements.
+
+ If `getopt' finds another option character, it returns that character,
+ updating `optind' and `nextchar' so that the next call to `getopt' can
+ resume the scan with the following option character or ARGV-element.
+
+ If there are no more option characters, `getopt' returns -1.
+ Then `optind' is the index in ARGV of the first ARGV-element
+ that is not an option. (The ARGV-elements have been permuted
+ so that those that are not options now come last.)
+
+ OPTSTRING is a string containing the legitimate option characters.
+ If an option character is seen that is not listed in OPTSTRING,
+ return '?' after printing an error message. If you set `opterr' to
+ zero, the error message is suppressed but we still return '?'.
+
+ If a char in OPTSTRING is followed by a colon, that means it wants an arg,
+ so the following text in the same ARGV-element, or the text of the following
+ ARGV-element, is returned in `optarg'. Two colons mean an option that
+ wants an optional arg; if there is text in the current ARGV-element,
+ it is returned in `optarg', otherwise `optarg' is set to zero.
+
+ If OPTSTRING starts with `-' or `+', it requests different methods of
+ handling the non-option ARGV-elements.
+ See the comments about RETURN_IN_ORDER and REQUIRE_ORDER, above.
+
+ Long-named options begin with `--' instead of `-'.
+ Their names may be abbreviated as long as the abbreviation is unique
+ or is an exact match for some defined option. If they have an
+ argument, it follows the option name in the same ARGV-element, separated
+ from the option name by a `=', or else the in next ARGV-element.
+ When `getopt' finds a long-named option, it returns 0 if that option's
+ `flag' field is nonzero, the value of the option's `val' field
+ if the `flag' field is zero.
+
+ The elements of ARGV aren't really const, because we permute them.
+ But we pretend they're const in the prototype to be compatible
+ with other systems.
+
+ LONGOPTS is a vector of `struct option' terminated by an
+ element containing a name which is zero.
+
+ LONGIND returns the index in LONGOPT of the long-named option found.
+ It is only valid when a long-named option has been found by the most
+ recent call.
+
+ If LONG_ONLY is nonzero, '-' as well as '--' can introduce
+ long-named options. */
+
+int
+_getopt_internal (argc, argv, optstring, longopts, longind, long_only)
+ int argc;
+ char *const *argv;
+ const char *optstring;
+ const struct option *longopts;
+ int *longind;
+ int long_only;
+{
+ optarg = NULL;
+
+ if (optind == 0 || !__getopt_initialized)
+ {
+ if (optind == 0)
+ optind = 1; /* Don't scan ARGV[0], the program name. */
+ optstring = _getopt_initialize (argc, argv, optstring);
+ __getopt_initialized = 1;
+ }
+
+ /* Test whether ARGV[optind] points to a non-option argument.
+ Either it does not have option syntax, or there is an environment flag
+ from the shell indicating it is not an option. The later information
+ is only used when the used in the GNU libc. */
+#ifdef _LIBC
+#define NONOPTION_P (argv[optind][0] != '-' || argv[optind][1] == '\0' \
+ || (optind < nonoption_flags_len \
+ && __getopt_nonoption_flags[optind] == '1'))
+#else
+#define NONOPTION_P (argv[optind][0] != '-' || argv[optind][1] == '\0')
+#endif
+
+ if (nextchar == NULL || *nextchar == '\0')
+ {
+ /* Advance to the next ARGV-element. */
+
+ /* Give FIRST_NONOPT & LAST_NONOPT rational values if OPTIND has been
+ moved back by the user (who may also have changed the arguments). */
+ if (last_nonopt > optind)
+ last_nonopt = optind;
+ if (first_nonopt > optind)
+ first_nonopt = optind;
+
+ if (ordering == PERMUTE)
+ {
+ /* If we have just processed some options following some non-options,
+ exchange them so that the options come first. */
+
+ if (first_nonopt != last_nonopt && last_nonopt != optind)
+ exchange ((char **) argv);
+ else if (last_nonopt != optind)
+ first_nonopt = optind;
+
+ /* Skip any additional non-options
+ and extend the range of non-options previously skipped. */
+
+ while (optind < argc && NONOPTION_P)
+ optind++;
+ last_nonopt = optind;
+ }
+
+ /* The special ARGV-element `--' means premature end of options.
+ Skip it like a null option,
+ then exchange with previous non-options as if it were an option,
+ then skip everything else like a non-option. */
+
+ if (optind != argc && !pj_native_strcmp(argv[optind], "--"))
+ {
+ optind++;
+
+ if (first_nonopt != last_nonopt && last_nonopt != optind)
+ exchange ((char **) argv);
+ else if (first_nonopt == last_nonopt)
+ first_nonopt = optind;
+ last_nonopt = argc;
+
+ optind = argc;
+ }
+
+ /* If we have done all the ARGV-elements, stop the scan
+ and back over any non-options that we skipped and permuted. */
+
+ if (optind == argc)
+ {
+ /* Set the next-arg-index to point at the non-options
+ that we previously skipped, so the caller will digest them. */
+ if (first_nonopt != last_nonopt)
+ optind = first_nonopt;
+ return -1;
+ }
+
+ /* If we have come to a non-option and did not permute it,
+ either stop the scan or describe it to the caller and pass it by. */
+
+ if (NONOPTION_P)
+ {
+ if (ordering == REQUIRE_ORDER)
+ return -1;
+ optarg = argv[optind++];
+ return 1;
+ }
+
+ /* We have found another option-ARGV-element.
+ Skip the initial punctuation. */
+
+ nextchar = (argv[optind] + 1
+ + (longopts != NULL && argv[optind][1] == '-'));
+ }
+
+ /* Decode the current option-ARGV-element. */
+
+ /* Check whether the ARGV-element is a long option.
+
+ If long_only and the ARGV-element has the form "-f", where f is
+ a valid short option, don't consider it an abbreviated form of
+ a long option that starts with f. Otherwise there would be no
+ way to give the -f short option.
+
+ On the other hand, if there's a long option "fubar" and
+ the ARGV-element is "-fu", do consider that an abbreviation of
+ the long option, just like "--fu", and not "-f" with arg "u".
+
+ This distinction seems to be the most useful approach. */
+
+ if (longopts != NULL
+ && (argv[optind][1] == '-'
+ || (long_only && (argv[optind][2] || !my_index (optstring, argv[optind][1])))))
+ {
+ char *nameend;
+ const struct option *p;
+ const struct option *pfound = NULL;
+ int exact = 0;
+ int ambig = 0;
+ int indfound = -1;
+ int option_index;
+
+ for (nameend = nextchar; *nameend && *nameend != '='; nameend++)
+ /* Do nothing. */ ;
+
+ /* Test all long options for either exact match
+ or abbreviated matches. */
+ for (p = longopts, option_index = 0; p->name; p++, option_index++)
+ if (!strncmp (p->name, nextchar, nameend - nextchar))
+ {
+ if ((unsigned int) (nameend - nextchar)
+ == (unsigned int) strlen (p->name))
+ {
+ /* Exact match found. */
+ pfound = p;
+ indfound = option_index;
+ exact = 1;
+ break;
+ }
+ else if (pfound == NULL)
+ {
+ /* First nonexact match found. */
+ pfound = p;
+ indfound = option_index;
+ }
+ else
+ /* Second or later nonexact match found. */
+ ambig = 1;
+ }
+
+ if (ambig && !exact)
+ {
+ if (opterr)
+ fprintf (stderr, _("%s: option `%s' is ambiguous\n"),
+ argv[0], argv[optind]);
+ nextchar += strlen (nextchar);
+ optind++;
+ optopt = 0;
+ return '?';
+ }
+
+ if (pfound != NULL)
+ {
+ option_index = indfound;
+ optind++;
+ if (*nameend)
+ {
+ /* Don't test has_arg with >, because some C compilers don't
+ allow it to be used on enums. */
+ if (pfound->has_arg)
+ optarg = nameend + 1;
+ else
+ {
+ if (opterr)
+ {
+ if (argv[optind - 1][1] == '-')
+ /* --option */
+ fprintf (stderr,
+ _("%s: option `--%s' doesn't allow an argument\n"),
+ argv[0], pfound->name);
+ else
+ {
+ /* +option or -option */
+ fprintf (stderr,
+ _("%s: option `%c%s' doesn't allow an argument\n"),
+ argv[0], argv[optind - 1][0], pfound->name);
+ }
+ }
+ nextchar += strlen (nextchar);
+
+ optopt = pfound->val;
+ return '?';
+ }
+ }
+ else if (pfound->has_arg == 1)
+ {
+ if (optind < argc)
+ optarg = argv[optind++];
+ else
+ {
+ if (opterr)
+ fprintf (stderr,
+ _("%s: option `%s' requires an argument\n"),
+ argv[0], argv[optind - 1]);
+ nextchar += strlen (nextchar);
+ optopt = pfound->val;
+ return optstring[0] == ':' ? ':' : '?';
+ }
+ }
+ nextchar += strlen (nextchar);
+ if (longind != NULL)
+ *longind = option_index;
+ if (pfound->flag)
+ {
+ *(pfound->flag) = pfound->val;
+ return 0;
+ }
+ return pfound->val;
+ }
+
+ /* Can't find it as a long option. If this is not getopt_long_only,
+ or the option starts with '--' or is not a valid short
+ option, then it's an error.
+ Otherwise interpret it as a short option. */
+ if (!long_only || argv[optind][1] == '-'
+ || my_index (optstring, *nextchar) == NULL)
+ {
+ if (opterr)
+ {
+ if (argv[optind][1] == '-')
+ /* --option */
+ fprintf (stderr, _("%s: unrecognized option `--%s'\n"),
+ argv[0], nextchar);
+ else
+ /* +option or -option */
+ fprintf (stderr, _("%s: unrecognized option `%c%s'\n"),
+ argv[0], argv[optind][0], nextchar);
+ }
+ nextchar = (char *) "";
+ optind++;
+ optopt = 0;
+ return '?';
+ }
+ }
+
+ /* Look at and handle the next short option-character. */
+
+ {
+ char c = *nextchar++;
+ char *temp = my_index (optstring, c);
+
+ /* Increment `optind' when we start to process its last character. */
+ if (*nextchar == '\0')
+ ++optind;
+
+ if (temp == NULL || c == ':')
+ {
+ if (opterr)
+ {
+ if (posixly_correct)
+ /* 1003.2 specifies the format of this message. */
+ fprintf (stderr, _("%s: illegal option -- %c\n"),
+ argv[0], c);
+ else
+ fprintf (stderr, _("%s: invalid option -- %c\n"),
+ argv[0], c);
+ }
+ optopt = c;
+ return '?';
+ }
+ /* Convenience. Treat POSIX -W foo same as long option --foo */
+ if (temp[0] == 'W' && temp[1] == ';')
+ {
+ char *nameend;
+ const struct option *p;
+ const struct option *pfound = NULL;
+ int exact = 0;
+ int ambig = 0;
+ int indfound = 0;
+ int option_index;
+
+ /* This is an option that requires an argument. */
+ if (*nextchar != '\0')
+ {
+ optarg = nextchar;
+ /* If we end this ARGV-element by taking the rest as an arg,
+ we must advance to the next element now. */
+ optind++;
+ }
+ else if (optind == argc)
+ {
+ if (opterr)
+ {
+ /* 1003.2 specifies the format of this message. */
+ fprintf (stderr, _("%s: option requires an argument -- %c\n"),
+ argv[0], c);
+ }
+ optopt = c;
+ if (optstring[0] == ':')
+ c = ':';
+ else
+ c = '?';
+ return c;
+ }
+ else
+ /* We already incremented `optind' once;
+ increment it again when taking next ARGV-elt as argument. */
+ optarg = argv[optind++];
+
+ /* optarg is now the argument, see if it's in the
+ table of longopts. */
+
+ for (nextchar = nameend = optarg; *nameend && *nameend != '='; nameend++)
+ /* Do nothing. */ ;
+
+ /* Test all long options for either exact match
+ or abbreviated matches. */
+ for (p = longopts, option_index = 0; p->name; p++, option_index++)
+ if (!strncmp (p->name, nextchar, nameend - nextchar))
+ {
+ if ((unsigned int) (nameend - nextchar) == strlen (p->name))
+ {
+ /* Exact match found. */
+ pfound = p;
+ indfound = option_index;
+ exact = 1;
+ break;
+ }
+ else if (pfound == NULL)
+ {
+ /* First nonexact match found. */
+ pfound = p;
+ indfound = option_index;
+ }
+ else
+ /* Second or later nonexact match found. */
+ ambig = 1;
+ }
+ if (ambig && !exact)
+ {
+ if (opterr)
+ fprintf (stderr, _("%s: option `-W %s' is ambiguous\n"),
+ argv[0], argv[optind]);
+ nextchar += strlen (nextchar);
+ optind++;
+ return '?';
+ }
+ if (pfound != NULL)
+ {
+ option_index = indfound;
+ if (*nameend)
+ {
+ /* Don't test has_arg with >, because some C compilers don't
+ allow it to be used on enums. */
+ if (pfound->has_arg)
+ optarg = nameend + 1;
+ else
+ {
+ if (opterr)
+ fprintf (stderr, _("\
+%s: option `-W %s' doesn't allow an argument\n"),
+ argv[0], pfound->name);
+
+ nextchar += strlen (nextchar);
+ return '?';
+ }
+ }
+ else if (pfound->has_arg == 1)
+ {
+ if (optind < argc)
+ optarg = argv[optind++];
+ else
+ {
+ if (opterr)
+ fprintf (stderr,
+ _("%s: option `%s' requires an argument\n"),
+ argv[0], argv[optind - 1]);
+ nextchar += strlen (nextchar);
+ return optstring[0] == ':' ? ':' : '?';
+ }
+ }
+ nextchar += strlen (nextchar);
+ if (longind != NULL)
+ *longind = option_index;
+ if (pfound->flag)
+ {
+ *(pfound->flag) = pfound->val;
+ return 0;
+ }
+ return pfound->val;
+ }
+ nextchar = NULL;
+ return 'W'; /* Let the application handle it. */
+ }
+ if (temp[1] == ':')
+ {
+ if (temp[2] == ':')
+ {
+ /* This is an option that accepts an argument optionally. */
+ if (*nextchar != '\0')
+ {
+ optarg = nextchar;
+ optind++;
+ }
+ else
+ optarg = NULL;
+ nextchar = NULL;
+ }
+ else
+ {
+ /* This is an option that requires an argument. */
+ if (*nextchar != '\0')
+ {
+ optarg = nextchar;
+ /* If we end this ARGV-element by taking the rest as an arg,
+ we must advance to the next element now. */
+ optind++;
+ }
+ else if (optind == argc)
+ {
+ if (opterr)
+ {
+ /* 1003.2 specifies the format of this message. */
+ fprintf (stderr,
+ _("%s: option requires an argument -- %c\n"),
+ argv[0], c);
+ }
+ optopt = c;
+ if (optstring[0] == ':')
+ c = ':';
+ else
+ c = '?';
+ }
+ else
+ /* We already incremented `optind' once;
+ increment it again when taking next ARGV-elt as argument. */
+ optarg = argv[optind++];
+ nextchar = NULL;
+ }
+ }
+ return c;
+ }
+}
+
+#endif /* Not ELIDE_CODE. */
+
+#endif
+
+#ifdef _MSC_VER
+# pragma warning(pop)
+#endif
+
diff --git a/pjsip/src/pjsua/getopt.h b/pjsip/src/pjsua/getopt.h
index fbd8a3dd..3acdd5fc 100644
--- a/pjsip/src/pjsua/getopt.h
+++ b/pjsip/src/pjsua/getopt.h
@@ -1,141 +1,141 @@
-/* $Id$ */
-/* This file has now become GPL. */
-/* Declarations for getopt.
- Copyright (C) 1989,90,91,92,93,94,96,97,98 Free Software Foundation, Inc.
- This file is part of the GNU C Library.
-
- The GNU C Library is free software; you can redistribute it and/or
- modify it under the terms of the GNU Library General Public License as
- published by the Free Software Foundation; either version 2 of the
- License, or (at your option) any later version.
-
- The GNU C Library 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
- Library General Public License for more details.
-
- You should have received a copy of the GNU Library General Public
- License along with the GNU C Library; see the file COPYING.LIB. If not,
- write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
- Boston, MA 02111-1307, USA. */
-
-#ifndef _GETOPT_H
-#define _GETOPT_H 1
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-/* For communication from `getopt' to the caller.
- When `getopt' finds an option that takes an argument,
- the argument value is returned here.
- Also, when `ordering' is RETURN_IN_ORDER,
- each non-option ARGV-element is returned here. */
-
-extern char *optarg;
-
-/* Index in ARGV of the next element to be scanned.
- This is used for communication to and from the caller
- and for communication between successive calls to `getopt'.
-
- On entry to `getopt', zero means this is the first call; initialize.
-
- When `getopt' returns -1, this is the index of the first of the
- non-option elements that the caller should itself scan.
-
- Otherwise, `optind' communicates from one call to the next
- how much of ARGV has been scanned so far. */
-
-extern int optind;
-
-/* Callers store zero here to inhibit the error message `getopt' prints
- for unrecognized options. */
-
-extern int opterr;
-
-/* Set to an option character which was unrecognized. */
-
-extern int optopt;
-
-/* Describe the long-named options requested by the application.
- The LONG_OPTIONS argument to getopt_long or getopt_long_only is a vector
- of `struct option' terminated by an element containing a name which is
- zero.
-
- The field `has_arg' is:
- no_argument (or 0) if the option does not take an argument,
- required_argument (or 1) if the option requires an argument,
- optional_argument (or 2) if the option takes an optional argument.
-
- If the field `flag' is not NULL, it points to a variable that is set
- to the value given in the field `val' when the option is found, but
- left unchanged if the option is not found.
-
- To have a long-named option do something other than set an `int' to
- a compiled-in constant, such as set a value from `optarg', set the
- option's `flag' field to zero and its `val' field to a nonzero
- value (the equivalent single-letter option character, if there is
- one). For long options that have a zero `flag' field, `getopt'
- returns the contents of the `val' field. */
-
-struct option
-{
- const char *name;
- /* has_arg can't be an enum because some compilers complain about
- type mismatches in all the code that assumes it is an int. */
- int has_arg;
- int *flag;
- int val;
-};
-
-/* Names for the values of the `has_arg' field of `struct option'. */
-
-# define no_argument 0
-# define required_argument 1
-# define optional_argument 2
-
-
-/* Get definitions and prototypes for functions to process the
- arguments in ARGV (ARGC of them, minus the program name) for
- options given in OPTS.
-
- Return the option character from OPTS just read. Return -1 when
- there are no more options. For unrecognized options, or options
- missing arguments, `optopt' is set to the option letter, and '?' is
- returned.
-
- The OPTS string is a list of characters which are recognized option
- letters, optionally followed by colons, specifying that that letter
- takes an argument, to be placed in `optarg'.
-
- If a letter in OPTS is followed by two colons, its argument is
- optional. This behavior is specific to the GNU `getopt'.
-
- The argument `--' causes premature termination of argument
- scanning, explicitly telling `getopt' that there are no more
- options.
-
- If OPTS begins with `--', then non-option arguments are treated as
- arguments to the option '\0'. This behavior is specific to the GNU
- `getopt'. */
-
-int getopt (int argc, char *const *argv, const char *shortopts);
-
-int getopt_long (int argc, char *const *argv, const char *options,
- const struct option *longopts, int *longind);
-int getopt_long_only (int argc, char *const *argv,
- const char *shortopts,
- const struct option *longopts, int *longind);
-
-/* Internal only. Users should not call this directly. */
-int _getopt_internal (int argc, char *const *argv,
- const char *shortopts,
- const struct option *longopts, int *longind,
- int long_only);
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif /* getopt.h */
-
+/* $Id$ */
+/* This file has now become GPL. */
+/* Declarations for getopt.
+ Copyright (C) 1989,90,91,92,93,94,96,97,98 Free Software Foundation, Inc.
+ This file is part of the GNU C Library.
+
+ The GNU C Library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version.
+
+ The GNU C Library 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
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public
+ License along with the GNU C Library; see the file COPYING.LIB. If not,
+ write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA. */
+
+#ifndef _GETOPT_H
+#define _GETOPT_H 1
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* For communication from `getopt' to the caller.
+ When `getopt' finds an option that takes an argument,
+ the argument value is returned here.
+ Also, when `ordering' is RETURN_IN_ORDER,
+ each non-option ARGV-element is returned here. */
+
+extern char *optarg;
+
+/* Index in ARGV of the next element to be scanned.
+ This is used for communication to and from the caller
+ and for communication between successive calls to `getopt'.
+
+ On entry to `getopt', zero means this is the first call; initialize.
+
+ When `getopt' returns -1, this is the index of the first of the
+ non-option elements that the caller should itself scan.
+
+ Otherwise, `optind' communicates from one call to the next
+ how much of ARGV has been scanned so far. */
+
+extern int optind;
+
+/* Callers store zero here to inhibit the error message `getopt' prints
+ for unrecognized options. */
+
+extern int opterr;
+
+/* Set to an option character which was unrecognized. */
+
+extern int optopt;
+
+/* Describe the long-named options requested by the application.
+ The LONG_OPTIONS argument to getopt_long or getopt_long_only is a vector
+ of `struct option' terminated by an element containing a name which is
+ zero.
+
+ The field `has_arg' is:
+ no_argument (or 0) if the option does not take an argument,
+ required_argument (or 1) if the option requires an argument,
+ optional_argument (or 2) if the option takes an optional argument.
+
+ If the field `flag' is not NULL, it points to a variable that is set
+ to the value given in the field `val' when the option is found, but
+ left unchanged if the option is not found.
+
+ To have a long-named option do something other than set an `int' to
+ a compiled-in constant, such as set a value from `optarg', set the
+ option's `flag' field to zero and its `val' field to a nonzero
+ value (the equivalent single-letter option character, if there is
+ one). For long options that have a zero `flag' field, `getopt'
+ returns the contents of the `val' field. */
+
+struct option
+{
+ const char *name;
+ /* has_arg can't be an enum because some compilers complain about
+ type mismatches in all the code that assumes it is an int. */
+ int has_arg;
+ int *flag;
+ int val;
+};
+
+/* Names for the values of the `has_arg' field of `struct option'. */
+
+# define no_argument 0
+# define required_argument 1
+# define optional_argument 2
+
+
+/* Get definitions and prototypes for functions to process the
+ arguments in ARGV (ARGC of them, minus the program name) for
+ options given in OPTS.
+
+ Return the option character from OPTS just read. Return -1 when
+ there are no more options. For unrecognized options, or options
+ missing arguments, `optopt' is set to the option letter, and '?' is
+ returned.
+
+ The OPTS string is a list of characters which are recognized option
+ letters, optionally followed by colons, specifying that that letter
+ takes an argument, to be placed in `optarg'.
+
+ If a letter in OPTS is followed by two colons, its argument is
+ optional. This behavior is specific to the GNU `getopt'.
+
+ The argument `--' causes premature termination of argument
+ scanning, explicitly telling `getopt' that there are no more
+ options.
+
+ If OPTS begins with `--', then non-option arguments are treated as
+ arguments to the option '\0'. This behavior is specific to the GNU
+ `getopt'. */
+
+int getopt (int argc, char *const *argv, const char *shortopts);
+
+int getopt_long (int argc, char *const *argv, const char *options,
+ const struct option *longopts, int *longind);
+int getopt_long_only (int argc, char *const *argv,
+ const char *shortopts,
+ const struct option *longopts, int *longind);
+
+/* Internal only. Users should not call this directly. */
+int _getopt_internal (int argc, char *const *argv,
+ const char *shortopts,
+ const struct option *longopts, int *longind,
+ int long_only);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* getopt.h */
+
diff --git a/pjsip/src/pjsua/main.c b/pjsip/src/pjsua/main.c
index e4d1e1c8..09f9f8f3 100644
--- a/pjsip/src/pjsua/main.c
+++ b/pjsip/src/pjsua/main.c
@@ -1,1827 +1,1827 @@
-/* $Id$ */
-/*
- * Copyright (C) 2003-2006 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 <pjlib.h>
-#include <pjsip_core.h>
-#include <pjsip_ua.h>
-#include <pjsip_simple.h>
-#include <pjmedia.h>
-#include <ctype.h>
-#include <stdlib.h>
-#include <pj/stun.h>
-
-#define START_PORT 5060
-#define MAX_BUDDIES 32
-#define THIS_FILE "main.c"
-#define MAX_PRESENTITY 32
-#define PRESENCE_TIMEOUT 60
-
-/* By default we'll have one worker thread, except when threading
- * is disabled.
- */
-#if PJ_HAS_THREADS
-# define WORKER_COUNT 1
-#else
-# define WORKER_COUNT 0
-#endif
-
-/* Global variable. */
-static struct
-{
- /* Control. */
- pj_pool_factory *pf;
- pjsip_endpoint *endpt;
- pj_pool_t *pool;
- pjsip_user_agent *user_agent;
- int worker_cnt;
- int worker_quit_flag;
-
- /* User info. */
- char user_id[64];
- pj_str_t local_uri;
- pj_str_t contact;
- pj_str_t real_contact;
-
- /* Dialog. */
- pjsip_dlg *cur_dlg;
-
- /* Authentication. */
- int cred_count;
- pjsip_cred_info cred_info[4];
-
- /* Media stack. */
- pj_bool_t null_audio;
- pj_med_mgr_t *mmgr;
-
- /* Misc. */
- int app_log_level;
- char *log_filename;
- FILE *log_file;
-
- /* Proxy URLs */
- pj_str_t proxy;
- pj_str_t outbound_proxy;
-
- /* UA auto options. */
- int auto_answer; /* -1 to disable. */
- int auto_hangup; /* -1 to disable */
-
- /* Registration. */
- pj_str_t registrar_uri;
- pjsip_regc *regc;
- pj_int32_t reg_timeout;
- pj_timer_entry regc_timer;
-
- /* STUN */
- pj_str_t stun_srv1;
- int stun_port1;
- pj_str_t stun_srv2;
- int stun_port2;
-
- /* UDP sockets and their public address. */
- int sip_port;
- pj_sock_t sip_sock;
- pj_sockaddr_in sip_sock_name;
- pj_sock_t rtp_sock;
- pj_sockaddr_in rtp_sock_name;
- pj_sock_t rtcp_sock;
- pj_sockaddr_in rtcp_sock_name;
-
- /* SIMPLE */
- pj_bool_t hide_status;
- pj_bool_t offer_x_ms_msg;
- int im_counter;
- int buddy_cnt;
- pj_str_t buddy[MAX_BUDDIES];
- pj_bool_t buddy_status[MAX_BUDDIES];
- pj_bool_t no_presence;
- pjsip_presentity *buddy_pres[MAX_BUDDIES];
-
- int pres_cnt;
- pjsip_presentity *pres[MAX_PRESENTITY];
-
-} global;
-
-enum { AUTO_ANSWER, AUTO_HANGUP };
-
-/* This is the data that will be 'attached' on per dialog basis. */
-struct dialog_data
-{
- /* Media session. */
- pj_media_session_t *msession;
-
- /* x-ms-chat session. */
- pj_bool_t x_ms_msg_session;
-
- /* Cached SDP body, updated when media session changed. */
- pjsip_msg_body *body;
-
- /* Timer. */
- pj_bool_t has_auto_timer;
- pj_timer_entry auto_timer;
-};
-
-/*
- * These are the callbacks to be registered to dialog to receive notifications
- * about various events in the dialog.
- */
-static void dlg_on_all_events (pjsip_dlg *dlg, pjsip_dlg_event_e dlg_evt,
- pjsip_event *event );
-static void dlg_on_before_tx (pjsip_dlg *dlg, pjsip_transaction *tsx,
- pjsip_tx_data *tdata, int retransmission);
-static void dlg_on_tx_msg (pjsip_dlg *dlg, pjsip_transaction *tsx,
- pjsip_tx_data *tdata);
-static void dlg_on_rx_msg (pjsip_dlg *dlg, pjsip_transaction *tsx,
- pjsip_rx_data *rdata);
-static void dlg_on_incoming (pjsip_dlg *dlg, pjsip_transaction *tsx,
- pjsip_rx_data *rdata);
-static void dlg_on_calling (pjsip_dlg *dlg, pjsip_transaction *tsx,
- pjsip_tx_data *tdata);
-static void dlg_on_provisional (pjsip_dlg *dlg, pjsip_transaction *tsx,
- pjsip_event *event);
-static void dlg_on_connecting (pjsip_dlg *dlg, pjsip_event *event);
-static void dlg_on_established (pjsip_dlg *dlg, pjsip_event *event);
-static void dlg_on_disconnected (pjsip_dlg *dlg, pjsip_event *event);
-static void dlg_on_terminated (pjsip_dlg *dlg);
-static void dlg_on_mid_call_evt (pjsip_dlg *dlg, pjsip_event *event);
-
-/* The callback structure that will be registered to UA layer. */
-struct pjsip_dlg_callback dlg_callback = {
- &dlg_on_all_events,
- &dlg_on_before_tx,
- &dlg_on_tx_msg,
- &dlg_on_rx_msg,
- &dlg_on_incoming,
- &dlg_on_calling,
- &dlg_on_provisional,
- &dlg_on_connecting,
- &dlg_on_established,
- &dlg_on_disconnected,
- &dlg_on_terminated,
- &dlg_on_mid_call_evt
-};
-
-
-/*
- * Auxiliary things are put in misc.c, so that this main.c file is more
- * readable.
- */
-#include "misc.c"
-
-static void dlg_auto_timer_callback( pj_timer_heap_t *timer_heap,
- struct pj_timer_entry *entry)
-{
- pjsip_dlg *dlg = entry->user_data;
- struct dialog_data *dlg_data = dlg->user_data;
-
- PJ_UNUSED_ARG(timer_heap)
-
- dlg_data->has_auto_timer = 0;
-
- if (entry->id == AUTO_ANSWER) {
- pjsip_tx_data *tdata = pjsip_dlg_answer(dlg, 200);
- if (tdata) {
- struct dialog_data *dlg_data = global.cur_dlg->user_data;
- tdata->msg->body = dlg_data->body;
- pjsip_dlg_send_msg(dlg, tdata);
- }
- } else {
- pjsip_tx_data *tdata = pjsip_dlg_disconnect(dlg, 500);
- if (tdata)
- pjsip_dlg_send_msg(dlg, tdata);
- }
-}
-
-static void update_registration(pjsip_regc *regc, int renew)
-{
- pjsip_tx_data *tdata;
-
- PJ_LOG(3,(THIS_FILE, "Performing SIP registration..."));
-
- if (renew) {
- tdata = pjsip_regc_register(regc, 1);
- } else {
- tdata = pjsip_regc_unregister(regc);
- }
-
- pjsip_regc_send( regc, tdata );
-}
-
-static void regc_cb(struct pjsip_regc_cbparam *param)
-{
- /*
- * Print registration status.
- */
- if (param->code < 0 || param->code >= 300) {
- PJ_LOG(2, (THIS_FILE, "SIP registration failed, status=%d (%s)",
- param->code, pjsip_get_status_text(param->code)->ptr));
- global.regc = NULL;
-
- } else if (PJSIP_IS_STATUS_IN_CLASS(param->code, 200)) {
- PJ_LOG(3, (THIS_FILE, "SIP registration success, status=%d (%s), "
- "will re-register in %d seconds",
- param->code,
- pjsip_get_status_text(param->code)->ptr,
- param->expiration));
-
- } else {
- PJ_LOG(4, (THIS_FILE, "SIP registration updated status=%d", param->code));
- }
-}
-
-static void pres_on_received_request(pjsip_presentity *pres, pjsip_rx_data *rdata,
- int *timeout)
-{
- int state;
- int i;
- char url[PJSIP_MAX_URL_SIZE];
- int urllen;
-
- PJ_UNUSED_ARG(rdata)
-
- if (*timeout > 0) {
- state = PJSIP_EVENT_SUB_STATE_ACTIVE;
- if (*timeout > 300)
- *timeout = 300;
- } else {
- state = PJSIP_EVENT_SUB_STATE_TERMINATED;
- }
-
- urllen = pjsip_uri_print(PJSIP_URI_IN_FROMTO_HDR, rdata->from->uri, url, sizeof(url)-1);
- if (urllen < 1) {
- pj_native_strcpy(url, "<unknown>");
- } else {
- url[urllen] = '\0';
- }
- PJ_LOG(3,(THIS_FILE, "Received presence request from %s, sub_state=%s",
- url,
- (state==PJSIP_EVENT_SUB_STATE_ACTIVE?"active":"terminated")));
-
- for (i=0; i<global.pres_cnt; ++i)
- if (global.pres[i] == pres)
- break;
- if (i == global.pres_cnt)
- global.pres[global.pres_cnt++] = pres;
-
- pjsip_presence_set_credentials( pres, global.cred_count, global.cred_info );
- pjsip_presence_notify(pres, state, !global.hide_status);
-
-}
-
-static void pres_on_received_refresh(pjsip_presentity *pres, pjsip_rx_data *rdata)
-{
- pres_on_received_request(pres, rdata, &pres->sub->default_interval);
-}
-
-/* This is called by presence framework when we receives presence update
- * of a resource (buddy).
- */
-static void pres_on_received_update(pjsip_presentity *pres, pj_bool_t is_open)
-{
- int buddy_index = (int)pres->user_data;
-
- global.buddy_status[buddy_index] = is_open;
- PJ_LOG(3,(THIS_FILE, "Presence update: %s is %s",
- global.buddy[buddy_index].ptr,
- (is_open ? "Online" : "Offline")));
-}
-
-/* This is called when the subscription is terminated. */
-static void pres_on_terminated(pjsip_presentity *pres, const pj_str_t *reason)
-{
- if (pres->sub->role == PJSIP_ROLE_UAC) {
- int buddy_index = (int)pres->user_data;
- PJ_LOG(3,(THIS_FILE, "Presence subscription for %s is terminated (reason=%.*s)",
- global.buddy[buddy_index].ptr,
- reason->slen, reason->ptr));
- global.buddy_pres[buddy_index] = NULL;
- global.buddy_status[buddy_index] = 0;
- } else {
- int i;
- PJ_LOG(3,(THIS_FILE, "Notifier terminated (reason=%.*s)",
- reason->slen, reason->ptr));
- pjsip_presence_notify(pres, PJSIP_EVENT_SUB_STATE_TERMINATED, 1);
- for (i=0; i<global.pres_cnt; ++i) {
- if (global.pres[i] == pres) {
- int j;
- global.pres[i] = NULL;
- for (j=i+1; j<global.pres_cnt; ++j)
- global.pres[j-1] = global.pres[j];
- global.pres_cnt--;
- break;
- }
- }
- }
- pjsip_presence_destroy(pres);
-}
-
-
-/* Callback attached to SIP body to print the body to message buffer. */
-static int print_msg_body(pjsip_msg_body *msg_body, char *buf, pj_size_t size)
-{
- pjsip_msg_body *body = msg_body;
- return pjsdp_print ((pjsdp_session_desc*)body->data, buf, size);
-}
-
-/* When media session has changed, call this function to update the cached body
- * information in the dialog.
- */
-static pjsip_msg_body *create_msg_body (pjsip_dlg *dlg, pj_bool_t is_ack_msg)
-{
- struct dialog_data *dlg_data = dlg->user_data;
- pjsdp_session_desc *sdp;
-
- sdp = pj_media_session_create_sdp (dlg_data->msession, dlg->pool, is_ack_msg);
- if (!sdp) {
- dlg_data->body = NULL;
- return NULL;
- }
-
- /* For outgoing INVITE, if we offer "x-ms-message" line, then add a new
- * "m=" line in the SDP.
- */
- if (dlg_data->x_ms_msg_session >= 0 &&
- dlg_data->x_ms_msg_session >= (int)sdp->media_count)
- {
- pjsdp_media_desc *m = pj_pool_calloc(dlg->pool, 1, sizeof(*m));
- sdp->media[sdp->media_count] = m;
- dlg_data->x_ms_msg_session = sdp->media_count++;
- }
-
- /*
- * For "x-ms-message" line, remove all attributes and connection line etc.
- */
- if (dlg_data->x_ms_msg_session >= 0) {
- pjsdp_media_desc *m = sdp->media[dlg_data->x_ms_msg_session];
- if (m) {
- m->desc.media = pj_str("x-ms-message");
- m->desc.port = 5060;
- m->desc.transport = pj_str("sip");
- m->desc.fmt_count = 1;
- m->desc.fmt[0] = pj_str("null");
- m->attr_count = 0;
- m->conn = NULL;
- }
- }
-
- dlg_data->body = pj_pool_calloc(dlg->pool, 1, sizeof(*dlg_data->body));
- dlg_data->body->content_type.type = pj_str("application");
- dlg_data->body->content_type.subtype = pj_str("sdp");
- dlg_data->body->len = 0; /* ignored */
- dlg_data->body->print_body = &print_msg_body;
-
- dlg_data->body->data = sdp;
- return dlg_data->body;
-}
-
-/* This callback will be called on every occurence of events in dialogs */
-static void dlg_on_all_events(pjsip_dlg *dlg, pjsip_dlg_event_e dlg_evt,
- pjsip_event *event )
-{
- PJ_UNUSED_ARG(dlg_evt)
- PJ_UNUSED_ARG(event)
-
- PJ_LOG(4, (THIS_FILE, "dlg_on_all_events %p", dlg));
-}
-
-/* This callback is called before each outgoing msg is sent (including
- * retransmission). Application can override this notification if it wants
- * to modify the message before transmission or if it wants to do something
- * else for each transmission.
- */
-static void dlg_on_before_tx(pjsip_dlg *dlg, pjsip_transaction *tsx,
- pjsip_tx_data *tdata, int ret_cnt)
-{
- PJ_UNUSED_ARG(tsx)
- PJ_UNUSED_ARG(tdata)
-
- if (ret_cnt > 0) {
- PJ_LOG(3, (THIS_FILE, "Dialog %s: retransmitting message (cnt=%d)",
- dlg->obj_name, ret_cnt));
- }
-}
-
-/* This callback is called after a message is sent. */
-static void dlg_on_tx_msg(pjsip_dlg *dlg, pjsip_transaction *tsx,
- pjsip_tx_data *tdata)
-{
- PJ_UNUSED_ARG(tsx)
- PJ_UNUSED_ARG(tdata)
-
- PJ_LOG(4, (THIS_FILE, "dlg_on_tx_msg %p", dlg));
-}
-
-/* This callback is called on receipt of incoming message. */
-static void dlg_on_rx_msg(pjsip_dlg *dlg, pjsip_transaction *tsx,
- pjsip_rx_data *rdata)
-{
- PJ_UNUSED_ARG(tsx)
- PJ_UNUSED_ARG(rdata)
- PJ_LOG(4, (THIS_FILE, "dlg_on_tx_msg %p", dlg));
-}
-
-/* This callback is called after dialog has sent INVITE */
-static void dlg_on_calling(pjsip_dlg *dlg, pjsip_transaction *tsx,
- pjsip_tx_data *tdata)
-{
- PJ_UNUSED_ARG(tsx)
- PJ_UNUSED_ARG(tdata)
-
- pj_assert(tdata->msg->type == PJSIP_REQUEST_MSG &&
- tdata->msg->line.req.method.id == PJSIP_INVITE_METHOD &&
- tsx->method.id == PJSIP_INVITE_METHOD);
-
- PJ_LOG(3, (THIS_FILE, "Dialog %s: start calling...", dlg->obj_name));
-}
-
-static void create_session_from_sdp( pjsip_dlg *dlg, pjsdp_session_desc *sdp)
-{
- struct dialog_data *dlg_data = dlg->user_data;
- pj_bool_t sdp_x_ms_msg_index = -1;
- int i;
- int mcnt;
- const pj_media_stream_info *mi[PJSDP_MAX_MEDIA];
- int has_active;
- pj_media_sock_info sock_info;
-
- /* Find "m=x-ms-message" line in the SDP. */
- for (i=0; i<(int)sdp->media_count; ++i) {
- if (pj_stricmp2(&sdp->media[i]->desc.media, "x-ms-message")==0)
- sdp_x_ms_msg_index = i;
- }
-
- /*
- * Create media session.
- */
- pj_memset(&sock_info, 0, sizeof(sock_info));
- sock_info.rtp_sock = global.rtp_sock;
- sock_info.rtcp_sock = global.rtcp_sock;
- pj_memcpy(&sock_info.rtp_addr_name, &global.rtp_sock_name, sizeof(pj_sockaddr_in));
-
- dlg_data->msession = pj_media_session_create_from_sdp (global.mmgr, sdp, &sock_info);
-
- /* A session will always be created, unless there is memory
- * alloc problem.
- */
- pj_assert(dlg_data->msession);
-
- /* See if we can take the offer by checking that we have at least
- * one media stream active.
- */
- mcnt = pj_media_session_enum_streams(dlg_data->msession, PJSDP_MAX_MEDIA, mi);
- for (i=0, has_active=0; i<mcnt; ++i) {
- if (mi[i]->fmt_cnt>0 && mi[i]->dir!=PJ_MEDIA_DIR_NONE) {
- has_active = 1;
- break;
- }
- }
-
- if (!has_active && sdp_x_ms_msg_index==-1) {
- pjsip_tx_data *tdata;
-
- /* Unable to accept remote's SDP.
- * Answer with 488 (Not Acceptable Here)
- */
- /* Create 488 response. */
- tdata = pjsip_dlg_answer(dlg, PJSIP_SC_NOT_ACCEPTABLE_HERE);
-
- /* Send response. */
- if (tdata)
- pjsip_dlg_send_msg(dlg, tdata);
- return;
- }
-
- dlg_data->x_ms_msg_session = sdp_x_ms_msg_index;
-
- /* Create msg body to be used later in 2xx/response */
- create_msg_body(dlg, 0);
-
-}
-
-/* This callback is called after an INVITE is received. */
-static void dlg_on_incoming(pjsip_dlg *dlg, pjsip_transaction *tsx,
- pjsip_rx_data *rdata)
-{
- struct dialog_data *dlg_data;
- pjsip_msg *msg;
- pjsip_tx_data *tdata;
- char buf[128];
- int len;
-
- PJ_UNUSED_ARG(tsx)
-
- pj_assert(rdata->msg->type == PJSIP_REQUEST_MSG &&
- rdata->msg->line.req.method.id == PJSIP_INVITE_METHOD &&
- tsx->method.id == PJSIP_INVITE_METHOD);
-
- /*
- * Notify user!
- */
- PJ_LOG(3, (THIS_FILE, ""));
- PJ_LOG(3, (THIS_FILE, "INCOMING CALL ON DIALOG %s!!", dlg->obj_name));
- PJ_LOG(3, (THIS_FILE, ""));
- len = pjsip_uri_print( PJSIP_URI_IN_FROMTO_HDR,
- (pjsip_name_addr*)dlg->remote.info->uri,
- buf, sizeof(buf)-1);
- if (len > 0) {
- buf[len] = '\0';
- PJ_LOG(3,(THIS_FILE, "From:\t%s", buf));
- }
- len = pjsip_uri_print( PJSIP_URI_IN_FROMTO_HDR,
- (pjsip_name_addr*)dlg->local.info->uri,
- buf, sizeof(buf)-1);
- if (len > 0) {
- buf[len] = '\0';
- PJ_LOG(3,(THIS_FILE, "To:\t%s", buf));
- }
- PJ_LOG(3, (THIS_FILE, "Press 'a' to answer, or 'h' to hangup!!", dlg->obj_name));
- PJ_LOG(3, (THIS_FILE, ""));
-
- /*
- * Process incoming dialog.
- */
-
- dlg_data = pj_pool_calloc(dlg->pool, 1, sizeof(struct dialog_data));
- dlg->user_data = dlg_data;
-
- /* Update contact. */
- pjsip_dlg_set_contact(dlg, &global.contact);
-
- /* Initialize credentials. */
- pjsip_dlg_set_credentials(dlg, global.cred_count, global.cred_info);
-
- /* Create media session if the request has "application/sdp" body. */
- msg = rdata->msg;
- if (msg->body &&
- pj_stricmp2(&msg->body->content_type.type, "application")==0 &&
- pj_stricmp2(&msg->body->content_type.subtype, "sdp")==0)
- {
- pjsdp_session_desc *sdp;
-
- /* Parse SDP body, and instantiate media session based on remote's SDP.
- * Then create our SDP body from the session.
- */
- sdp = pjsdp_parse (msg->body->data, msg->body->len, rdata->pool);
- if (!sdp)
- goto send_answer;
-
- create_session_from_sdp(dlg, sdp);
-
- } else if (msg->body) {
- /* The request has a message body other than "application/sdp" */
- pjsip_accept_hdr *accept;
-
- /* Create response. */
- tdata = pjsip_dlg_answer(dlg, PJSIP_SC_UNSUPPORTED_MEDIA_TYPE);
-
- /* Add "Accept" header. */
- accept = pjsip_accept_hdr_create(tdata->pool);
- accept->values[0] = pj_str("application/sdp");
- accept->count = 1;
- pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr*)accept);
-
- /* Send response. */
- pjsip_dlg_send_msg(dlg, tdata);
- return;
-
- } else {
- /* The request has no message body. We can take this request, but
- * no media session will be activated.
- */
- /* Nothing to do here. */
- }
-
-send_answer:
- /* Immediately answer with 100 (or 180? */
- tdata = pjsip_dlg_answer( dlg, PJSIP_SC_RINGING );
- pjsip_dlg_send_msg(dlg, tdata);
-
- /* Set current dialog to this dialog if we don't currently have
- * current dialog.
- */
- if (global.cur_dlg == NULL) {
- global.cur_dlg = dlg;
- }
-
- /* Auto-answer if option is specified. */
- if (global.auto_answer >= 0) {
- pj_time_val delay = { 0, 0};
- struct dialog_data *dlg_data = dlg->user_data;
-
- PJ_LOG(4, (THIS_FILE, "Scheduling auto-answer in %d seconds",
- global.auto_answer));
-
- delay.sec = global.auto_answer;
- dlg_data->auto_timer.user_data = dlg;
- dlg_data->auto_timer.id = AUTO_ANSWER;
- dlg_data->auto_timer.cb = &dlg_auto_timer_callback;
- dlg_data->has_auto_timer = 1;
- pjsip_endpt_schedule_timer(dlg->ua->endpt, &dlg_data->auto_timer, &delay);
- }
-}
-
-/* This callback is called when dialog has sent/received a provisional response
- * to INVITE.
- */
-static void dlg_on_provisional(pjsip_dlg *dlg, pjsip_transaction *tsx,
- pjsip_event *event)
-{
- const char *action;
-
- pj_assert((event->src_type == PJSIP_EVENT_TX_MSG &&
- event->src.tdata->msg->type == PJSIP_RESPONSE_MSG &&
- event->src.tdata->msg->line.status.code/100 == 1 &&
- tsx->method.id == PJSIP_INVITE_METHOD)
- ||
- (event->src_type == PJSIP_EVENT_RX_MSG &&
- event->src.rdata->msg->type == PJSIP_RESPONSE_MSG &&
- event->src.rdata->msg->line.status.code/100 == 1 &&
- tsx->method.id == PJSIP_INVITE_METHOD));
-
- if (event->src_type == PJSIP_EVENT_TX_MSG)
- action = "Sending";
- else
- action = "Received";
-
- PJ_LOG(3, (THIS_FILE, "Dialog %s: %s %d (%s)",
- dlg->obj_name, action, tsx->status_code,
- pjsip_get_status_text(tsx->status_code)->ptr));
-}
-
-/* This callback is called when 200 response to INVITE is sent/received. */
-static void dlg_on_connecting(pjsip_dlg *dlg, pjsip_event *event)
-{
- struct dialog_data *dlg_data = dlg->user_data;
- const char *action;
-
- pj_assert((event->src_type == PJSIP_EVENT_TX_MSG &&
- event->src.tdata->msg->type == PJSIP_RESPONSE_MSG &&
- event->src.tdata->msg->line.status.code/100 == 2)
- ||
- (event->src_type == PJSIP_EVENT_RX_MSG &&
- event->src.rdata->msg->type == PJSIP_RESPONSE_MSG &&
- event->src.rdata->msg->line.status.code/100 == 2));
-
- if (event->src_type == PJSIP_EVENT_RX_MSG)
- action = "Received";
- else
- action = "Sending";
-
- PJ_LOG(3, (THIS_FILE, "Dialog %s: %s 200 (OK)", dlg->obj_name, action));
-
- if (event->src_type == PJSIP_EVENT_RX_MSG) {
- /* On receipt of 2xx response, negotiate our media capability
- * and start media.
- */
- pjsip_msg *msg = event->src.rdata->msg;
- pjsip_msg_body *body;
- pjsdp_session_desc *sdp;
-
- /* Get SDP from message. */
-
- /* Ignore if no SDP body is present. */
- body = msg->body;
- if (!body) {
- PJ_LOG(3, (THIS_FILE, "Dialog %s: the 200/OK response has no body!",
- dlg->obj_name));
- return;
- }
-
- if (pj_stricmp2(&body->content_type.type, "application") != 0 &&
- pj_stricmp2(&body->content_type.subtype, "sdp") != 0)
- {
- PJ_LOG(3, (THIS_FILE, "Dialog %s: the 200/OK response has no SDP body!",
- dlg->obj_name));
- return;
- }
-
- /* Got what seems to be a SDP content. Parse it. */
- sdp = pjsdp_parse (body->data, body->len, event->src.rdata->pool);
- if (!sdp) {
- PJ_LOG(3, (THIS_FILE, "Dialog %s: SDP syntax error!",
- dlg->obj_name));
- return;
- }
-
- /* Negotiate media session with remote's media capability. */
- if (pj_media_session_update (dlg_data->msession, sdp) != 0) {
- PJ_LOG(3, (THIS_FILE, "Dialog %s: media session update error!",
- dlg->obj_name));
- return;
- }
-
- /* Update the saved SDP body because media session has changed.
- * Also set ack flag to '1', because we only want to send one format/
- * codec for each media streams.
- */
- create_msg_body(dlg, 1);
-
- /* Activate media. */
- pj_media_session_activate (dlg_data->msession);
-
- } else {
- pjsip_msg *msg = event->src.tdata->msg;
-
- if (msg->body) {
- /* On transmission of 2xx response, start media session. */
- pj_media_session_activate (dlg_data->msession);
- }
- }
-
-}
-
-/* This callback is called when ACK to initial INVITE is sent/received. */
-static void dlg_on_established(pjsip_dlg *dlg, pjsip_event *event)
-{
- const char *action;
-
- pj_assert((event->src_type == PJSIP_EVENT_TX_MSG &&
- event->src.tdata->msg->type == PJSIP_REQUEST_MSG &&
- event->src.tdata->msg->line.req.method.id == PJSIP_ACK_METHOD)
- ||
- (event->src_type == PJSIP_EVENT_RX_MSG &&
- event->src.rdata->msg->type == PJSIP_REQUEST_MSG &&
- event->src.rdata->msg->line.req.method.id == PJSIP_ACK_METHOD));
-
- if (event->src_type == PJSIP_EVENT_RX_MSG)
- action = "Received";
- else
- action = "Sending";
-
- PJ_LOG(3, (THIS_FILE, "Dialog %s: %s ACK, dialog is ESTABLISHED",
- dlg->obj_name, action));
-
- /* Attach SDP body for outgoing ACK. */
- if (event->src_type == PJSIP_EVENT_TX_MSG &&
- event->src.tdata->msg->line.req.method.id == PJSIP_ACK_METHOD)
- {
- struct dialog_data *dlg_data = dlg->user_data;
- event->src.tdata->msg->body = dlg_data->body;
- }
-
- /* Auto-hangup if option is specified. */
- if (global.auto_hangup >= 0) {
- pj_time_val delay = { 0, 0};
- struct dialog_data *dlg_data = dlg->user_data;
-
- PJ_LOG(4, (THIS_FILE, "Scheduling auto-hangup in %d seconds",
- global.auto_hangup));
-
- delay.sec = global.auto_hangup;
- dlg_data->auto_timer.user_data = dlg;
- dlg_data->auto_timer.id = AUTO_HANGUP;
- dlg_data->auto_timer.cb = &dlg_auto_timer_callback;
- dlg_data->has_auto_timer = 1;
- pjsip_endpt_schedule_timer(dlg->ua->endpt, &dlg_data->auto_timer, &delay);
- }
-}
-
-
-/* This callback is called when dialog is disconnected (because of final
- * response, BYE, or timer).
- */
-static void dlg_on_disconnected(pjsip_dlg *dlg, pjsip_event *event)
-{
- struct dialog_data *dlg_data = dlg->user_data;
- int status_code;
- const pj_str_t *reason;
-
- PJ_UNUSED_ARG(event)
-
- /* Cancel auto-answer/auto-hangup timer. */
- if (dlg_data->has_auto_timer) {
- pjsip_endpt_cancel_timer(dlg->ua->endpt, &dlg_data->auto_timer);
- dlg_data->has_auto_timer = 0;
- }
-
- if (dlg->invite_tsx)
- status_code = dlg->invite_tsx->status_code;
- else
- status_code = 200;
-
- if (event->obj.tsx->method.id == PJSIP_INVITE_METHOD) {
- if (event->src_type == PJSIP_EVENT_RX_MSG)
- reason = &event->src.rdata->msg->line.status.reason;
- else if (event->src_type == PJSIP_EVENT_TX_MSG)
- reason = &event->src.tdata->msg->line.status.reason;
- else
- reason = pjsip_get_status_text(event->obj.tsx->status_code);
- } else {
- reason = &event->obj.tsx->method.name;
- }
-
- PJ_LOG(3, (THIS_FILE, "Dialog %s: DISCONNECTED! Reason=%d (%.*s)",
- dlg->obj_name, status_code,
- reason->slen, reason->ptr));
-
- if (dlg_data->msession) {
- pj_media_session_destroy (dlg_data->msession);
- dlg_data->msession = NULL;
- }
-}
-
-/* This callback is called when dialog is about to be destroyed. */
-static void dlg_on_terminated(pjsip_dlg *dlg)
-{
- PJ_LOG(3, (THIS_FILE, "Dialog %s: terminated!", dlg->obj_name));
-
- /* If current dialog is equal to this dialog, update it. */
- if (global.cur_dlg == dlg) {
- global.cur_dlg = global.cur_dlg->next;
- if (global.cur_dlg == (void*)&global.user_agent->dlg_list) {
- global.cur_dlg = NULL;
- }
- }
-}
-
-/* This callback is called for any requests when dialog is established. */
-static void dlg_on_mid_call_evt (pjsip_dlg *dlg, pjsip_event *event)
-{
- pjsip_transaction *tsx = event->obj.tsx;
-
- if (event->src_type == PJSIP_EVENT_RX_MSG &&
- event->src.rdata->msg->type == PJSIP_REQUEST_MSG)
- {
- if (event->src.rdata->cseq->method.id == PJSIP_INVITE_METHOD) {
- /* Re-invitation. */
- pjsip_tx_data *tdata;
-
- PJ_LOG(3,(THIS_FILE, "Dialog %s: accepting re-invitation (dummy)",
- dlg->obj_name));
- tdata = pjsip_dlg_answer(dlg, 200);
- if (tdata) {
- struct dialog_data *dlg_data = dlg->user_data;
- tdata->msg->body = dlg_data->body;
- pjsip_dlg_send_msg(dlg, tdata);
- }
- } else {
- /* Don't worry, endpoint will answer with 500 or whetever. */
- }
-
- } else if (tsx->status_code/100 == 2) {
- PJ_LOG(3,(THIS_FILE, "Dialog %s: outgoing %.*s success: %d (%s)",
- dlg->obj_name,
- tsx->method.name.slen, tsx->method.name.ptr,
- tsx->status_code,
- pjsip_get_status_text(tsx->status_code)->ptr));
-
-
- } else if (tsx->status_code >= 300) {
- pj_bool_t report_failure = PJ_TRUE;
-
- /* Check for authentication failures. */
- if (tsx->status_code==401 || tsx->status_code==407) {
- pjsip_tx_data *tdata;
- tdata = pjsip_auth_reinit_req( global.endpt,
- dlg->pool, &dlg->auth_sess,
- dlg->cred_count, dlg->cred_info,
- tsx->last_tx, event->src.rdata );
- if (tdata) {
- int rc;
- rc = pjsip_dlg_send_msg( dlg, tdata);
- report_failure = (rc != 0);
- }
- }
- if (report_failure) {
- const pj_str_t *reason;
- if (event->src_type == PJSIP_EVENT_RX_MSG) {
- reason = &event->src.rdata->msg->line.status.reason;
- } else {
- reason = pjsip_get_status_text(tsx->status_code);
- }
- PJ_LOG(2,(THIS_FILE, "Dialog %s: outgoing request failed: %d (%.*s)",
- dlg->obj_name, tsx->status_code,
- reason->slen, reason->ptr));
- }
- }
-}
-
-/* Initialize sockets and optionally get the public address via STUN. */
-static pj_status_t init_sockets()
-{
- enum {
- RTP_START_PORT = 4000,
- RTP_RANDOM_START = 2,
- RTP_RETRY = 10
- };
- enum {
- SIP_SOCK,
- RTP_SOCK,
- RTCP_SOCK,
- };
- int i;
- int rtp_port;
- pj_sock_t sock[3];
- pj_sockaddr_in mapped_addr[3];
-
- for (i=0; i<3; ++i)
- sock[i] = PJ_INVALID_SOCKET;
-
- /* Create and bind SIP UDP socket. */
- sock[SIP_SOCK] = pj_sock_socket(PJ_AF_INET, PJ_SOCK_DGRAM, 0, 0);
- if (sock[SIP_SOCK] == PJ_INVALID_SOCKET) {
- PJ_LOG(2,(THIS_FILE, "Unable to create socket"));
- goto on_error;
- }
- if (pj_sock_bind_in(sock[SIP_SOCK], 0, (pj_uint16_t)global.sip_port) != 0) {
- PJ_LOG(2,(THIS_FILE, "Unable to bind to SIP port"));
- goto on_error;
- }
-
- /* Initialize start of RTP port to try. */
- rtp_port = RTP_START_PORT + (pj_rand() % RTP_RANDOM_START) / 2;
-
- /* Loop retry to bind RTP and RTCP sockets. */
- for (i=0; i<RTP_RETRY; ++i, rtp_port += 2) {
-
- /* Create and bind RTP socket. */
- sock[RTP_SOCK] = pj_sock_socket(PJ_AF_INET, PJ_SOCK_DGRAM, 0, 0);
- if (sock[RTP_SOCK] == PJ_INVALID_SOCKET)
- goto on_error;
- if (pj_sock_bind_in(sock[RTP_SOCK], 0, (pj_uint16_t)rtp_port) != 0) {
- pj_sock_close(sock[RTP_SOCK]); sock[RTP_SOCK] = PJ_INVALID_SOCKET;
- continue;
- }
-
- /* Create and bind RTCP socket. */
- sock[RTCP_SOCK] = pj_sock_socket(PJ_AF_INET, PJ_SOCK_DGRAM, 0, 0);
- if (sock[RTCP_SOCK] == PJ_INVALID_SOCKET)
- goto on_error;
- if (pj_sock_bind_in(sock[RTCP_SOCK], 0, (pj_uint16_t)(rtp_port+1)) != 0) {
- pj_sock_close(sock[RTP_SOCK]); sock[RTP_SOCK] = PJ_INVALID_SOCKET;
- pj_sock_close(sock[RTCP_SOCK]); sock[RTCP_SOCK] = PJ_INVALID_SOCKET;
- continue;
- }
-
- /*
- * If we're configured to use STUN, then find out the mapped address,
- * and make sure that the mapped RTCP port is adjacent with the RTP.
- */
- if (global.stun_port1 == 0) {
- pj_str_t hostname;
- pj_sockaddr_in addr;
-
- /* Get local IP address. */
- char hostname_buf[PJ_MAX_HOSTNAME];
- if (gethostname(hostname_buf, sizeof(hostname_buf)))
- goto on_error;
- hostname = pj_str(hostname_buf);
-
- pj_memset( &addr, 0, sizeof(addr));
- addr.sin_family = PJ_AF_INET;
- if (pj_sockaddr_set_str_addr( &addr, &hostname) != PJ_SUCCESS)
- goto on_error;
-
- for (i=0; i<3; ++i)
- pj_memcpy(&mapped_addr[i], &addr, sizeof(addr));
-
- mapped_addr[SIP_SOCK].sin_port = pj_htons((pj_uint16_t)global.sip_port);
- mapped_addr[RTP_SOCK].sin_port = pj_htons((pj_uint16_t)rtp_port);
- mapped_addr[RTCP_SOCK].sin_port = pj_htons((pj_uint16_t)(rtp_port+1));
- break;
- } else {
- pj_status_t rc;
- rc = pj_stun_get_mapped_addr( global.pf, 3, sock,
- &global.stun_srv1, global.stun_port1,
- &global.stun_srv2, global.stun_port2,
- mapped_addr);
- if (rc != 0) {
- PJ_LOG(3,(THIS_FILE, "Error: %s", pj_stun_get_err_msg(rc)));
- goto on_error;
- }
-
- if (pj_ntohs(mapped_addr[2].sin_port) == pj_ntohs(mapped_addr[1].sin_port)+1)
- break;
-
- pj_sock_close(sock[RTP_SOCK]); sock[RTP_SOCK] = PJ_INVALID_SOCKET;
- pj_sock_close(sock[RTCP_SOCK]); sock[RTCP_SOCK] = PJ_INVALID_SOCKET;
- }
- }
-
- if (sock[RTP_SOCK] == PJ_INVALID_SOCKET) {
- PJ_LOG(2,(THIS_FILE, "Unable to find appropriate RTP/RTCP ports combination"));
- goto on_error;
- }
-
- global.sip_sock = sock[SIP_SOCK];
- pj_memcpy(&global.sip_sock_name, &mapped_addr[SIP_SOCK], sizeof(pj_sockaddr_in));
- global.rtp_sock = sock[RTP_SOCK];
- pj_memcpy(&global.rtp_sock_name, &mapped_addr[RTP_SOCK], sizeof(pj_sockaddr_in));
- global.rtcp_sock = sock[RTCP_SOCK];
- pj_memcpy(&global.rtcp_sock_name, &mapped_addr[RTCP_SOCK], sizeof(pj_sockaddr_in));
-
- PJ_LOG(4,(THIS_FILE, "SIP UDP socket reachable at %s:%d",
- pj_inet_ntoa(global.sip_sock_name.sin_addr),
- pj_ntohs(global.sip_sock_name.sin_port)));
- PJ_LOG(4,(THIS_FILE, "RTP socket reachable at %s:%d",
- pj_inet_ntoa(global.rtp_sock_name.sin_addr),
- pj_ntohs(global.rtp_sock_name.sin_port)));
- PJ_LOG(4,(THIS_FILE, "RTCP UDP socket reachable at %s:%d",
- pj_inet_ntoa(global.rtcp_sock_name.sin_addr),
- pj_ntohs(global.rtcp_sock_name.sin_port)));
- return 0;
-
-on_error:
- for (i=0; i<3; ++i) {
- if (sock[i] != PJ_INVALID_SOCKET)
- pj_sock_close(sock[i]);
- }
- return -1;
-}
-
-static void log_function(int level, const char *buffer, int len)
-{
- /* Write to both stdout and file. */
- if (level <= global.app_log_level)
- pj_log_to_stdout(level, buffer, len);
- if (global.log_file) {
- fwrite(buffer, len, 1, global.log_file);
- fflush(global.log_file);
- }
-}
-
-/* Initialize stack. */
-static pj_status_t init_stack()
-{
- pj_status_t status;
- pj_sockaddr_in bind_addr;
- pj_sockaddr_in bind_name;
- const char *local_addr;
- static char local_uri[128];
-
- /* Optionally set logging file. */
- if (global.log_filename) {
- global.log_file = fopen(global.log_filename, "wt");
- }
-
- /* Initialize endpoint. This will also call initialization to all the
- * modules.
- */
- global.endpt = pjsip_endpt_create(global.pf);
- if (global.endpt == NULL) {
- return -1;
- }
-
- /* Set dialog callback. */
- pjsip_ua_set_dialog_callback(global.user_agent, &dlg_callback);
-
- /* Init listener's bound address and port. */
- pj_sockaddr_init2(&bind_addr, "0.0.0.0", global.sip_port);
- pj_sockaddr_init(&bind_name, pj_gethostname(), global.sip_port);
-
- /* Add UDP transport listener. */
- status = pjsip_endpt_create_udp_listener( global.endpt, global.sip_sock,
- &global.sip_sock_name);
- if (status != 0)
- return -1;
-
- local_addr = pj_inet_ntoa(global.sip_sock_name.sin_addr);
-
-#if PJ_HAS_TCP
- /* Add TCP transport listener. */
- status = pjsip_endpt_create_listener( global.endpt, PJSIP_TRANSPORT_TCP,
- &bind_addr, &bind_name);
- if (status != 0)
- return -1;
-#endif
-
- /* Determine user_id to be put in Contact */
- if (global.local_uri.slen) {
- pj_pool_t *pool = pj_pool_create(global.pf, "parser", 1024, 0, NULL);
- pjsip_uri *uri;
-
- uri = pjsip_parse_uri(pool, global.local_uri.ptr, global.local_uri.slen, 0);
- if (uri) {
- if (pj_stricmp2(pjsip_uri_get_scheme(uri), "sip")==0) {
- pjsip_url *url = (pjsip_url*)pjsip_uri_get_uri(uri);
- if (url->user.slen)
- strncpy(global.user_id, url->user.ptr, url->user.slen);
- }
- }
- pj_pool_release(pool);
- }
-
- if (global.user_id[0]=='\0') {
- pj_native_strcpy(global.user_id, "user");
- }
-
- /* build contact */
- global.real_contact.ptr = local_uri;
- global.real_contact.slen =
- sprintf(local_uri, "<sip:%s@%s:%d>", global.user_id, local_addr, global.sip_port);
-
- if (global.contact.slen == 0)
- global.contact = global.real_contact;
-
- /* initialize local_uri with contact if it's not specified in cmdline */
- if (global.local_uri.slen == 0)
- global.local_uri = global.contact;
-
- /* Init proxy. */
- if (global.proxy.slen || global.outbound_proxy.slen) {
- int count = 0;
- pj_str_t proxy_url[2];
-
- if (global.outbound_proxy.slen) {
- proxy_url[count++] = global.outbound_proxy;
- }
- if (global.proxy.slen) {
- proxy_url[count++] = global.proxy;
- }
-
- if (pjsip_endpt_set_proxies(global.endpt, count, proxy_url) != 0) {
- PJ_LOG(2,(THIS_FILE, "Error setting proxy address!"));
- return -1;
- }
- }
-
- /* initialize SIP registration if registrar is configured */
- if (global.registrar_uri.slen) {
- global.regc = pjsip_regc_create( global.endpt, NULL, &regc_cb);
- pjsip_regc_init( global.regc, &global.registrar_uri,
- &global.local_uri,
- &global.local_uri,
- 1, &global.contact,
- global.reg_timeout);
- pjsip_regc_set_credentials( global.regc, global.cred_count, global.cred_info );
- }
-
- return PJ_SUCCESS;
-}
-
-/* Worker thread function, only used when threading is enabled. */
-static void *PJ_THREAD_FUNC worker_thread(void *unused)
-{
- PJ_UNUSED_ARG(unused)
-
- while (!global.worker_quit_flag) {
- pj_time_val timeout = { 0, 10 };
- pjsip_endpt_handle_events (global.endpt, &timeout);
- }
- return NULL;
-}
-
-
-/* Make call to the specified URI. */
-static pjsip_dlg *make_call(pj_str_t *remote_uri)
-{
- pjsip_dlg *dlg;
- pj_str_t local = global.contact;
- pj_str_t remote = *remote_uri;
- struct dialog_data *dlg_data;
- pjsip_tx_data *tdata;
- pj_media_sock_info sock_info;
-
- /* Create new dialog instance. */
- dlg = pjsip_ua_create_dialog(global.user_agent, PJSIP_ROLE_UAC);
-
- /* Attach our own user data. */
- dlg_data = pj_pool_calloc(dlg->pool, 1, sizeof(struct dialog_data));
- dlg->user_data = dlg_data;
-
- /* Create media session. */
- pj_memset(&sock_info, 0, sizeof(sock_info));
- sock_info.rtp_sock = global.rtp_sock;
- sock_info.rtcp_sock = global.rtcp_sock;
- pj_memcpy(&sock_info.rtp_addr_name, &global.rtp_sock_name, sizeof(pj_sockaddr_in));
-
- dlg_data->msession = pj_media_session_create (global.mmgr, &sock_info);
- dlg_data->x_ms_msg_session = -1;
-
- if (global.offer_x_ms_msg) {
- const pj_media_stream_info *minfo[32];
- unsigned cnt;
-
- cnt = pj_media_session_enum_streams(dlg_data->msession, 32, minfo);
- if (cnt > 0)
- dlg_data->x_ms_msg_session = cnt;
- }
-
- /* Initialize dialog with local and remote URI. */
- if (pjsip_dlg_init(dlg, &local, &remote, NULL) != PJ_SUCCESS) {
- pjsip_ua_destroy_dialog(dlg);
- return NULL;
- }
-
- /* Initialize credentials. */
- pjsip_dlg_set_credentials(dlg, global.cred_count, global.cred_info);
-
- /* Send INVITE! */
- tdata = pjsip_dlg_invite(dlg);
- tdata->msg->body = create_msg_body (dlg, 0);
-
- if (pjsip_dlg_send_msg(dlg, tdata) != PJ_SUCCESS) {
- pjsip_ua_destroy_dialog(dlg);
- return NULL;
- }
-
- return dlg;
-}
-
-/*
- * Callback to receive incoming IM message.
- */
-static int on_incoming_im_msg(pjsip_rx_data *rdata)
-{
- pjsip_msg *msg = rdata->msg;
- pjsip_msg_body *body = msg->body;
- int len;
- char to[128], from[128];
-
-
- len = pjsip_uri_print( PJSIP_URI_IN_CONTACT_HDR,
- rdata->from->uri, from, sizeof(from));
- if (len > 0) from[len] = '\0';
- else pj_native_strcpy(from, "<URL too long..>");
-
- len = pjsip_uri_print( PJSIP_URI_IN_CONTACT_HDR,
- rdata->to->uri, to, sizeof(to));
- if (len > 0) to[len] = '\0';
- else pj_native_strcpy(to, "<URL too long..>");
-
- PJ_LOG(3,(THIS_FILE, "Incoming instant message:"));
-
- printf("----- BEGIN INSTANT MESSAGE ----->\n");
- printf("From:\t%s\n", from);
- printf("To:\t%s\n", to);
- printf("Body:\n%.*s\n", (body ? body->len : 0), (body ? (char*)body->data : ""));
- printf("<------ END INSTANT MESSAGE ------\n");
-
- fflush(stdout);
-
- /* Must answer with final response. */
- return 200;
-}
-
-/*
- * Input URL.
- */
-static pj_str_t *ui_input_url(pj_str_t *out, char *buf, int len, int *selection)
-{
- int i;
-
- *selection = -1;
-
- printf("\nBuddy list:\n");
- printf("---------------------------------------\n");
- for (i=0; i<global.buddy_cnt; ++i) {
- printf(" %d\t%s <%s>\n", i+1, global.buddy[i].ptr,
- (global.buddy_status[i]?"Online":"Offline"));
- }
- printf("-------------------------------------\n");
-
- printf("Choices\n"
- "\t0 For current dialog.\n"
- "\t[1-%02d] Select from buddy list\n"
- "\tURL An URL\n"
- , global.buddy_cnt);
- printf("Input: ");
-
- fflush(stdout);
- fgets(buf, len, stdin);
- buf[strlen(buf)-1] = '\0'; /* remove trailing newline. */
-
- while (isspace(*buf)) ++buf;
-
- if (!*buf || *buf=='\n' || *buf=='\r')
- return NULL;
-
- i = atoi(buf);
-
- if (i == 0) {
- if (isdigit(*buf)) {
- *selection = 0;
- *out = pj_str("0");
- return out;
- } else {
- if (verify_sip_url(buf) != 0) {
- puts("Invalid URL specified!");
- return NULL;
- }
- *out = pj_str(buf);
- return out;
- }
- } else if (i > global.buddy_cnt || i < 0) {
- printf("Error: invalid selection!\n");
- return NULL;
- } else {
- *out = global.buddy[i-1];
- *selection = i;
- return out;
- }
-}
-
-
-static void generic_request_callback( void *token, pjsip_event *event )
-{
- pjsip_transaction *tsx = event->obj.tsx;
-
- PJ_UNUSED_ARG(token)
-
- if (tsx->status_code/100 == 2) {
- PJ_LOG(3,(THIS_FILE, "Outgoing %.*s %d (%s)",
- event->obj.tsx->method.name.slen,
- event->obj.tsx->method.name.ptr,
- tsx->status_code,
- pjsip_get_status_text(tsx->status_code)->ptr));
- } else if (tsx->status_code==401 || tsx->status_code==407) {
- pjsip_tx_data *tdata;
- tdata = pjsip_auth_reinit_req( global.endpt,
- global.pool, NULL, global.cred_count, global.cred_info,
- tsx->last_tx, event->src.rdata);
- if (tdata) {
- int rc;
- pjsip_cseq_hdr *cseq;
- cseq = (pjsip_cseq_hdr*)pjsip_msg_find_hdr(tdata->msg, PJSIP_H_CSEQ, NULL);
- cseq->cseq++;
- rc = pjsip_endpt_send_request( global.endpt, tdata, -1, NULL,
- &generic_request_callback);
- if (rc == 0)
- return;
- }
- PJ_LOG(2,(THIS_FILE, "Outgoing %.*s failed, status=%d (%s)",
- event->obj.tsx->method.name.slen,
- event->obj.tsx->method.name.ptr,
- event->obj.tsx->status_code,
- pjsip_get_status_text(event->obj.tsx->status_code)->ptr));
- } else {
- const pj_str_t *reason;
- if (event->src_type == PJSIP_EVENT_RX_MSG)
- reason = &event->src.rdata->msg->line.status.reason;
- else
- reason = pjsip_get_status_text(tsx->status_code);
- PJ_LOG(2,(THIS_FILE, "Outgoing %.*s failed, status=%d (%.*s)",
- event->obj.tsx->method.name.slen,
- event->obj.tsx->method.name.ptr,
- event->obj.tsx->status_code,
- reason->slen, reason->ptr));
- }
-}
-
-
-static void ui_send_im_message()
-{
- char line[100];
- char text_buf[100];
- pj_str_t str;
- pj_str_t text_msg;
- int selection, rc;
- pjsip_tx_data *tdata;
-
- if (ui_input_url(&str, line, sizeof(line), &selection) == NULL)
- return;
-
-
- printf("Enter text to send (empty to cancel): "); fflush(stdout);
- fgets(text_buf, sizeof(text_buf), stdin);
- text_buf[strlen(text_buf)-1] = '\0';
- if (!*text_buf)
- return;
-
- text_msg = pj_str(text_buf);
-
- if (selection==0) {
- pjsip_method message_method;
- pj_str_t str_MESSAGE = { "MESSAGE", 7 };
-
- /* Send IM to current dialog. */
- if (global.cur_dlg == NULL || global.cur_dlg->state != PJSIP_DIALOG_STATE_ESTABLISHED) {
- printf("No current dialog or dialog state is not ESTABLISHED!\n");
- return;
- }
-
- pjsip_method_init( &message_method, global.cur_dlg->pool, &str_MESSAGE);
- tdata = pjsip_dlg_create_request( global.cur_dlg, &message_method, -1 );
-
- if (tdata) {
- /* Create message body for the text. */
- pjsip_msg_body *body = pj_pool_calloc(tdata->pool, 1, sizeof(*body));
- body->content_type.type = pj_str("text");
- body->content_type.subtype = pj_str("plain");
- body->data = pj_pool_alloc(tdata->pool, text_msg.slen);
- pj_memcpy(body->data, text_msg.ptr, text_msg.slen);
- body->len = text_msg.slen;
- body->print_body = &pjsip_print_text_body;
-
- /* Assign body to message, and send the message! */
- tdata->msg->body = body;
- pjsip_dlg_send_msg( global.cur_dlg, tdata );
- }
-
- } else {
- /* Send IM to buddy list. */
- pjsip_method message;
- static pj_str_t MESSAGE = { "MESSAGE", 7 };
- pjsip_method_init_np(&message, &MESSAGE);
- tdata = pjsip_endpt_create_request(global.endpt, &message,
- &str,
- &global.real_contact,
- &str, &global.real_contact, NULL, -1,
- &text_msg);
- if (!tdata) {
- puts("Error creating request");
- return;
- }
- rc = pjsip_endpt_send_request(global.endpt, tdata, -1, NULL, &generic_request_callback);
- if (rc == 0) {
- printf("Sending IM message %d\n", global.im_counter);
- ++global.im_counter;
- } else {
- printf("Error: unable to send IM message!\n");
- }
- }
-}
-
-static void ui_send_options()
-{
- char line[100];
- pj_str_t str;
- int selection, rc;
- pjsip_tx_data *tdata;
- pjsip_method options;
-
- if (ui_input_url(&str, line, sizeof(line), &selection) == NULL)
- return;
-
- pjsip_method_set( &options, PJSIP_OPTIONS_METHOD );
-
- if (selection == 0) {
- /* Send OPTIONS to current dialog. */
- tdata = pjsip_dlg_create_request(global.cur_dlg, &options, -1);
- if (tdata)
- pjsip_dlg_send_msg( global.cur_dlg, tdata );
- } else {
- /* Send OPTIONS to arbitrary party. */
- tdata = pjsip_endpt_create_request( global.endpt, &options,
- &str,
- &global.local_uri, &str,
- &global.real_contact,
- NULL, -1, NULL);
- if (tdata) {
- rc = pjsip_endpt_send_request( global.endpt, tdata, -1, NULL,
- &generic_request_callback);
- if (rc != 0)
- PJ_LOG(2,(THIS_FILE, "Error sending OPTIONS!"));
- }
- }
-}
-
-static void init_presence()
-{
- const pjsip_presence_cb pres_cb = {
- NULL,
- &pres_on_received_request,
- &pres_on_received_refresh,
- &pres_on_received_update,
- &pres_on_terminated
- };
-
- pjsip_presence_init(&pres_cb);
-}
-
-/* Subscribe presence information for all buddies. */
-static void subscribe_buddies_presence()
-{
- int i;
- for (i=0; i<global.buddy_cnt; ++i) {
- pjsip_presentity *pres;
- if (global.buddy_pres[i])
- continue;
- pres = pjsip_presence_create( global.endpt, &global.local_uri,
- &global.buddy[i], PRESENCE_TIMEOUT, (void*)i);
- if (pres) {
- pjsip_presence_set_credentials( pres, global.cred_count, global.cred_info );
- pjsip_presence_subscribe( pres );
- }
- global.buddy_pres[i] = pres;
- }
-}
-
-/* Unsubscribe presence information for all buddies. */
-static void unsubscribe_buddies_presence()
-{
- int i;
- for (i=0; i<global.buddy_cnt; ++i) {
- pjsip_presentity *pres = global.buddy_pres[i];
- if (pres) {
- pjsip_presence_unsubscribe(pres);
- pjsip_presence_destroy(pres);
- global.buddy_pres[i] = NULL;
- }
- }
-}
-
-/* Unsubscribe presence. */
-static void unsubscribe_presence()
-{
- int i;
-
- unsubscribe_buddies_presence();
- for (i=0; i<global.pres_cnt; ++i) {
- pjsip_presentity *pres = global.pres[i];
- pjsip_presence_notify( pres, PJSIP_EVENT_SUB_STATE_TERMINATED, 0);
- pjsip_presence_destroy( pres );
- }
-}
-
-/* Advertise online status to subscribers. */
-static void update_im_status()
-{
- int i;
- for (i=0; i<global.pres_cnt; ++i) {
- pjsip_presentity *pres = global.pres[i];
- pjsip_presence_notify( pres, PJSIP_EVENT_SUB_STATE_ACTIVE,
- !global.hide_status);
- }
-}
-
-/*
- * Main program.
- */
-int main(int argc, char *argv[])
-{
- /* set to WORKER_COUNT+1 to avoid zero size warning
- * when threading is disabled. */
- pj_thread_t *thread[WORKER_COUNT+1];
- pj_caching_pool cp;
- int i;
-
- global.sip_port = 5060;
- global.auto_answer = -1;
- global.auto_hangup = -1;
- global.app_log_level = 3;
-
- pj_log_set_level(4);
- pj_log_set_log_func(&log_function);
-
- /* Init PJLIB */
- if (pj_init() != PJ_SUCCESS)
- return 1;
-
- /* Init caching pool. */
- pj_caching_pool_init(&cp, &pj_pool_factory_default_policy, 0);
- global.pf = &cp.factory;
-
- /* Create memory pool for application. */
- global.pool = pj_pool_create(global.pf, "main", 1024, 0, NULL);
-
- /* Parse command line arguments. */
- if (parse_args(global.pool, argc, argv) != PJ_SUCCESS) {
- pj_caching_pool_destroy(&cp);
- return 1;
- }
-
- /* Init sockets */
- if (init_sockets() != 0) {
- pj_caching_pool_destroy(&cp);
- return 1;
- }
-
- /* Initialize stack. */
- if (init_stack() != PJ_SUCCESS) {
- pj_caching_pool_destroy(&cp);
- return 1;
- }
-
- /* Set callback to receive incoming IM */
- pjsip_messaging_set_incoming_callback( &on_incoming_im_msg );
-
- /* Set default worker count (can be zero) */
- global.worker_cnt = WORKER_COUNT;
-
- /* Create user worker thread(s), only when threading is enabled. */
- for (i=0; i<global.worker_cnt; ++i) {
- thread[i] = pj_thread_create( global.pool, "sip%p",
- &worker_thread,
- NULL, 0, NULL, 0);
- if (thread == NULL) {
- global.worker_quit_flag = 1;
- for (--i; i>=0; --i) {
- pj_thread_join(thread[i]);
- pj_thread_destroy(thread[i]);
- }
- pj_caching_pool_destroy(&cp);
- return 1;
- }
- }
-
- printf("Worker thread count: %d\n", global.worker_cnt);
-
- /* Perform registration, if required. */
- if (global.regc) {
- update_registration(global.regc, 1);
- }
-
- /* Initialize media manager. */
- global.mmgr = pj_med_mgr_create(global.pf);
-
- /* Init presence. */
- init_presence();
-
- /* Subscribe presence information of all buddies. */
- if (!global.no_presence)
- subscribe_buddies_presence();
-
- /* Initializatio completes, loop waiting for commands. */
- for (;!global.worker_quit_flag;) {
- pj_str_t str;
- char line[128];
-
-#if WORKER_COUNT==0
- /* If worker thread does not exist, main thread must poll for evetns.
- * But this won't work very well since main thread is blocked by
- * fgets(). So keep pressing the ENTER key to get the events!
- */
- pj_time_val timeout = { 0, 100 };
- pjsip_endpt_handle_events(global.endpt, &timeout);
- puts("Keep pressing ENTER key to get the events!");
-#endif
-
- printf("\nCurrent dialog: ");
- print_dialog(global.cur_dlg);
- puts("");
-
- keystroke_help();
-
- fgets(line, sizeof(line), stdin);
-
- switch (*line) {
- case 'm':
- puts("Make outgoing call");
- if (ui_input_url(&str, line, sizeof(line), &i) != NULL) {
- pjsip_dlg *dlg = make_call(&str);
- if (global.cur_dlg == NULL) {
- global.cur_dlg = dlg;
- }
- }
- break;
- case 'i':
- puts("Send Instant Messaging");
- ui_send_im_message();
- break;
- case 'o':
- puts("Send OPTIONS");
- ui_send_options();
- break;
- case 'a':
- if (global.cur_dlg) {
- unsigned code;
- pjsip_tx_data *tdata;
- struct dialog_data *dlg_data = global.cur_dlg->user_data;
-
- printf("Answer with status code (1xx-6xx): ");
- fflush(stdout);
- fgets(line, sizeof(line), stdin);
- str = pj_str(line);
- str.slen -= 1;
-
- code = pj_strtoul(&str);
- tdata = pjsip_dlg_answer(global.cur_dlg, code);
- if (tdata) {
- if (code/100 == 2) {
- tdata->msg->body = dlg_data->body;
- }
- pjsip_dlg_send_msg(global.cur_dlg, tdata);
-
- }
- } else {
- puts("No current dialog");
- }
- break;
- case 'h':
- if (global.cur_dlg) {
- pjsip_tx_data *tdata;
- tdata = pjsip_dlg_disconnect(global.cur_dlg, PJSIP_SC_DECLINE);
- if (tdata) {
- pjsip_dlg_send_msg(global.cur_dlg, tdata);
- }
- } else {
- puts("No current dialog");
- }
- break;
- case ']':
- if (global.cur_dlg) {
- global.cur_dlg = global.cur_dlg->next;
- if (global.cur_dlg == (void*)&global.user_agent->dlg_list) {
- global.cur_dlg = global.cur_dlg->next;
- }
- } else {
- puts("No current dialog");
- }
- break;
- case '[':
- if (global.cur_dlg) {
- global.cur_dlg = global.cur_dlg->prev;
- if (global.cur_dlg == (void*)&global.user_agent->dlg_list) {
- global.cur_dlg = global.cur_dlg->prev;
- }
- } else {
- puts("No current dialog");
- }
- break;
- case 'd':
- pjsip_endpt_dump(global.endpt, *(line+1)=='1');
- pjsip_ua_dump(global.user_agent);
- break;
- case 's':
- if (*(line+1) == 'u')
- subscribe_buddies_presence();
- break;
- case 'u':
- if (*(line+1) == 's')
- unsubscribe_presence();
- break;
- case 't':
- global.hide_status = !global.hide_status;
- update_im_status();
- break;
- case 'q':
- goto on_exit;
- case 'l':
- print_all_dialogs();
- break;
- }
- }
-
-on_exit:
- /* Unregister, if required. */
- if (global.regc) {
- update_registration(global.regc, 0);
- }
-
- /* Unsubscribe presence. */
- unsubscribe_presence();
-
- /* Allow one second to get all events. */
- if (1) {
- pj_time_val end_time;
-
- pj_gettimeofday(&end_time);
- end_time.sec++;
-
- PJ_LOG(3,(THIS_FILE, "Shutting down.."));
- for (;;) {
- pj_time_val timeout = { 0, 20 }, now;
- pjsip_endpt_handle_events (global.endpt, &timeout);
- pj_gettimeofday(&now);
- PJ_TIME_VAL_SUB(now, end_time);
- if (now.sec >= 1)
- break;
- }
- }
-
- global.worker_quit_flag = 1;
-
- pj_med_mgr_destroy(global.mmgr);
-
- /* Wait all threads to quit. */
- for (i=0; i<global.worker_cnt; ++i) {
- pj_thread_join(thread[i]);
- pj_thread_destroy(thread[i]);
- }
-
- /* Destroy endpoint. */
- pjsip_endpt_destroy(global.endpt);
-
- /* Destroy caching pool. */
- pj_caching_pool_destroy(&cp);
-
- /* Close log file, if any. */
- if (global.log_file)
- fclose(global.log_file);
-
- return 0;
-}
-
-/*
- * Register static modules to the endpoint.
- */
-pj_status_t register_static_modules( pj_size_t *count,
- pjsip_module **modules )
-{
- /* Reset count. */
- *count = 0;
-
- /* Register user agent module. */
- modules[(*count)++] = pjsip_ua_get_module();
- global.user_agent = modules[0]->mod_data;
- modules[(*count)++] = pjsip_messaging_get_module();
- modules[(*count)++] = pjsip_event_sub_get_module();
-
- return PJ_SUCCESS;
-}
+/* $Id$ */
+/*
+ * Copyright (C) 2003-2006 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 <pjlib.h>
+#include <pjsip_core.h>
+#include <pjsip_ua.h>
+#include <pjsip_simple.h>
+#include <pjmedia.h>
+#include <ctype.h>
+#include <stdlib.h>
+#include <pj/stun.h>
+
+#define START_PORT 5060
+#define MAX_BUDDIES 32
+#define THIS_FILE "main.c"
+#define MAX_PRESENTITY 32
+#define PRESENCE_TIMEOUT 60
+
+/* By default we'll have one worker thread, except when threading
+ * is disabled.
+ */
+#if PJ_HAS_THREADS
+# define WORKER_COUNT 1
+#else
+# define WORKER_COUNT 0
+#endif
+
+/* Global variable. */
+static struct
+{
+ /* Control. */
+ pj_pool_factory *pf;
+ pjsip_endpoint *endpt;
+ pj_pool_t *pool;
+ pjsip_user_agent *user_agent;
+ int worker_cnt;
+ int worker_quit_flag;
+
+ /* User info. */
+ char user_id[64];
+ pj_str_t local_uri;
+ pj_str_t contact;
+ pj_str_t real_contact;
+
+ /* Dialog. */
+ pjsip_dlg *cur_dlg;
+
+ /* Authentication. */
+ int cred_count;
+ pjsip_cred_info cred_info[4];
+
+ /* Media stack. */
+ pj_bool_t null_audio;
+ pj_med_mgr_t *mmgr;
+
+ /* Misc. */
+ int app_log_level;
+ char *log_filename;
+ FILE *log_file;
+
+ /* Proxy URLs */
+ pj_str_t proxy;
+ pj_str_t outbound_proxy;
+
+ /* UA auto options. */
+ int auto_answer; /* -1 to disable. */
+ int auto_hangup; /* -1 to disable */
+
+ /* Registration. */
+ pj_str_t registrar_uri;
+ pjsip_regc *regc;
+ pj_int32_t reg_timeout;
+ pj_timer_entry regc_timer;
+
+ /* STUN */
+ pj_str_t stun_srv1;
+ int stun_port1;
+ pj_str_t stun_srv2;
+ int stun_port2;
+
+ /* UDP sockets and their public address. */
+ int sip_port;
+ pj_sock_t sip_sock;
+ pj_sockaddr_in sip_sock_name;
+ pj_sock_t rtp_sock;
+ pj_sockaddr_in rtp_sock_name;
+ pj_sock_t rtcp_sock;
+ pj_sockaddr_in rtcp_sock_name;
+
+ /* SIMPLE */
+ pj_bool_t hide_status;
+ pj_bool_t offer_x_ms_msg;
+ int im_counter;
+ int buddy_cnt;
+ pj_str_t buddy[MAX_BUDDIES];
+ pj_bool_t buddy_status[MAX_BUDDIES];
+ pj_bool_t no_presence;
+ pjsip_presentity *buddy_pres[MAX_BUDDIES];
+
+ int pres_cnt;
+ pjsip_presentity *pres[MAX_PRESENTITY];
+
+} global;
+
+enum { AUTO_ANSWER, AUTO_HANGUP };
+
+/* This is the data that will be 'attached' on per dialog basis. */
+struct dialog_data
+{
+ /* Media session. */
+ pj_media_session_t *msession;
+
+ /* x-ms-chat session. */
+ pj_bool_t x_ms_msg_session;
+
+ /* Cached SDP body, updated when media session changed. */
+ pjsip_msg_body *body;
+
+ /* Timer. */
+ pj_bool_t has_auto_timer;
+ pj_timer_entry auto_timer;
+};
+
+/*
+ * These are the callbacks to be registered to dialog to receive notifications
+ * about various events in the dialog.
+ */
+static void dlg_on_all_events (pjsip_dlg *dlg, pjsip_dlg_event_e dlg_evt,
+ pjsip_event *event );
+static void dlg_on_before_tx (pjsip_dlg *dlg, pjsip_transaction *tsx,
+ pjsip_tx_data *tdata, int retransmission);
+static void dlg_on_tx_msg (pjsip_dlg *dlg, pjsip_transaction *tsx,
+ pjsip_tx_data *tdata);
+static void dlg_on_rx_msg (pjsip_dlg *dlg, pjsip_transaction *tsx,
+ pjsip_rx_data *rdata);
+static void dlg_on_incoming (pjsip_dlg *dlg, pjsip_transaction *tsx,
+ pjsip_rx_data *rdata);
+static void dlg_on_calling (pjsip_dlg *dlg, pjsip_transaction *tsx,
+ pjsip_tx_data *tdata);
+static void dlg_on_provisional (pjsip_dlg *dlg, pjsip_transaction *tsx,
+ pjsip_event *event);
+static void dlg_on_connecting (pjsip_dlg *dlg, pjsip_event *event);
+static void dlg_on_established (pjsip_dlg *dlg, pjsip_event *event);
+static void dlg_on_disconnected (pjsip_dlg *dlg, pjsip_event *event);
+static void dlg_on_terminated (pjsip_dlg *dlg);
+static void dlg_on_mid_call_evt (pjsip_dlg *dlg, pjsip_event *event);
+
+/* The callback structure that will be registered to UA layer. */
+struct pjsip_dlg_callback dlg_callback = {
+ &dlg_on_all_events,
+ &dlg_on_before_tx,
+ &dlg_on_tx_msg,
+ &dlg_on_rx_msg,
+ &dlg_on_incoming,
+ &dlg_on_calling,
+ &dlg_on_provisional,
+ &dlg_on_connecting,
+ &dlg_on_established,
+ &dlg_on_disconnected,
+ &dlg_on_terminated,
+ &dlg_on_mid_call_evt
+};
+
+
+/*
+ * Auxiliary things are put in misc.c, so that this main.c file is more
+ * readable.
+ */
+#include "misc.c"
+
+static void dlg_auto_timer_callback( pj_timer_heap_t *timer_heap,
+ struct pj_timer_entry *entry)
+{
+ pjsip_dlg *dlg = entry->user_data;
+ struct dialog_data *dlg_data = dlg->user_data;
+
+ PJ_UNUSED_ARG(timer_heap)
+
+ dlg_data->has_auto_timer = 0;
+
+ if (entry->id == AUTO_ANSWER) {
+ pjsip_tx_data *tdata = pjsip_dlg_answer(dlg, 200);
+ if (tdata) {
+ struct dialog_data *dlg_data = global.cur_dlg->user_data;
+ tdata->msg->body = dlg_data->body;
+ pjsip_dlg_send_msg(dlg, tdata);
+ }
+ } else {
+ pjsip_tx_data *tdata = pjsip_dlg_disconnect(dlg, 500);
+ if (tdata)
+ pjsip_dlg_send_msg(dlg, tdata);
+ }
+}
+
+static void update_registration(pjsip_regc *regc, int renew)
+{
+ pjsip_tx_data *tdata;
+
+ PJ_LOG(3,(THIS_FILE, "Performing SIP registration..."));
+
+ if (renew) {
+ tdata = pjsip_regc_register(regc, 1);
+ } else {
+ tdata = pjsip_regc_unregister(regc);
+ }
+
+ pjsip_regc_send( regc, tdata );
+}
+
+static void regc_cb(struct pjsip_regc_cbparam *param)
+{
+ /*
+ * Print registration status.
+ */
+ if (param->code < 0 || param->code >= 300) {
+ PJ_LOG(2, (THIS_FILE, "SIP registration failed, status=%d (%s)",
+ param->code, pjsip_get_status_text(param->code)->ptr));
+ global.regc = NULL;
+
+ } else if (PJSIP_IS_STATUS_IN_CLASS(param->code, 200)) {
+ PJ_LOG(3, (THIS_FILE, "SIP registration success, status=%d (%s), "
+ "will re-register in %d seconds",
+ param->code,
+ pjsip_get_status_text(param->code)->ptr,
+ param->expiration));
+
+ } else {
+ PJ_LOG(4, (THIS_FILE, "SIP registration updated status=%d", param->code));
+ }
+}
+
+static void pres_on_received_request(pjsip_presentity *pres, pjsip_rx_data *rdata,
+ int *timeout)
+{
+ int state;
+ int i;
+ char url[PJSIP_MAX_URL_SIZE];
+ int urllen;
+
+ PJ_UNUSED_ARG(rdata)
+
+ if (*timeout > 0) {
+ state = PJSIP_EVENT_SUB_STATE_ACTIVE;
+ if (*timeout > 300)
+ *timeout = 300;
+ } else {
+ state = PJSIP_EVENT_SUB_STATE_TERMINATED;
+ }
+
+ urllen = pjsip_uri_print(PJSIP_URI_IN_FROMTO_HDR, rdata->from->uri, url, sizeof(url)-1);
+ if (urllen < 1) {
+ pj_native_strcpy(url, "<unknown>");
+ } else {
+ url[urllen] = '\0';
+ }
+ PJ_LOG(3,(THIS_FILE, "Received presence request from %s, sub_state=%s",
+ url,
+ (state==PJSIP_EVENT_SUB_STATE_ACTIVE?"active":"terminated")));
+
+ for (i=0; i<global.pres_cnt; ++i)
+ if (global.pres[i] == pres)
+ break;
+ if (i == global.pres_cnt)
+ global.pres[global.pres_cnt++] = pres;
+
+ pjsip_presence_set_credentials( pres, global.cred_count, global.cred_info );
+ pjsip_presence_notify(pres, state, !global.hide_status);
+
+}
+
+static void pres_on_received_refresh(pjsip_presentity *pres, pjsip_rx_data *rdata)
+{
+ pres_on_received_request(pres, rdata, &pres->sub->default_interval);
+}
+
+/* This is called by presence framework when we receives presence update
+ * of a resource (buddy).
+ */
+static void pres_on_received_update(pjsip_presentity *pres, pj_bool_t is_open)
+{
+ int buddy_index = (int)pres->user_data;
+
+ global.buddy_status[buddy_index] = is_open;
+ PJ_LOG(3,(THIS_FILE, "Presence update: %s is %s",
+ global.buddy[buddy_index].ptr,
+ (is_open ? "Online" : "Offline")));
+}
+
+/* This is called when the subscription is terminated. */
+static void pres_on_terminated(pjsip_presentity *pres, const pj_str_t *reason)
+{
+ if (pres->sub->role == PJSIP_ROLE_UAC) {
+ int buddy_index = (int)pres->user_data;
+ PJ_LOG(3,(THIS_FILE, "Presence subscription for %s is terminated (reason=%.*s)",
+ global.buddy[buddy_index].ptr,
+ reason->slen, reason->ptr));
+ global.buddy_pres[buddy_index] = NULL;
+ global.buddy_status[buddy_index] = 0;
+ } else {
+ int i;
+ PJ_LOG(3,(THIS_FILE, "Notifier terminated (reason=%.*s)",
+ reason->slen, reason->ptr));
+ pjsip_presence_notify(pres, PJSIP_EVENT_SUB_STATE_TERMINATED, 1);
+ for (i=0; i<global.pres_cnt; ++i) {
+ if (global.pres[i] == pres) {
+ int j;
+ global.pres[i] = NULL;
+ for (j=i+1; j<global.pres_cnt; ++j)
+ global.pres[j-1] = global.pres[j];
+ global.pres_cnt--;
+ break;
+ }
+ }
+ }
+ pjsip_presence_destroy(pres);
+}
+
+
+/* Callback attached to SIP body to print the body to message buffer. */
+static int print_msg_body(pjsip_msg_body *msg_body, char *buf, pj_size_t size)
+{
+ pjsip_msg_body *body = msg_body;
+ return pjsdp_print ((pjsdp_session_desc*)body->data, buf, size);
+}
+
+/* When media session has changed, call this function to update the cached body
+ * information in the dialog.
+ */
+static pjsip_msg_body *create_msg_body (pjsip_dlg *dlg, pj_bool_t is_ack_msg)
+{
+ struct dialog_data *dlg_data = dlg->user_data;
+ pjsdp_session_desc *sdp;
+
+ sdp = pj_media_session_create_sdp (dlg_data->msession, dlg->pool, is_ack_msg);
+ if (!sdp) {
+ dlg_data->body = NULL;
+ return NULL;
+ }
+
+ /* For outgoing INVITE, if we offer "x-ms-message" line, then add a new
+ * "m=" line in the SDP.
+ */
+ if (dlg_data->x_ms_msg_session >= 0 &&
+ dlg_data->x_ms_msg_session >= (int)sdp->media_count)
+ {
+ pjsdp_media_desc *m = pj_pool_calloc(dlg->pool, 1, sizeof(*m));
+ sdp->media[sdp->media_count] = m;
+ dlg_data->x_ms_msg_session = sdp->media_count++;
+ }
+
+ /*
+ * For "x-ms-message" line, remove all attributes and connection line etc.
+ */
+ if (dlg_data->x_ms_msg_session >= 0) {
+ pjsdp_media_desc *m = sdp->media[dlg_data->x_ms_msg_session];
+ if (m) {
+ m->desc.media = pj_str("x-ms-message");
+ m->desc.port = 5060;
+ m->desc.transport = pj_str("sip");
+ m->desc.fmt_count = 1;
+ m->desc.fmt[0] = pj_str("null");
+ m->attr_count = 0;
+ m->conn = NULL;
+ }
+ }
+
+ dlg_data->body = pj_pool_calloc(dlg->pool, 1, sizeof(*dlg_data->body));
+ dlg_data->body->content_type.type = pj_str("application");
+ dlg_data->body->content_type.subtype = pj_str("sdp");
+ dlg_data->body->len = 0; /* ignored */
+ dlg_data->body->print_body = &print_msg_body;
+
+ dlg_data->body->data = sdp;
+ return dlg_data->body;
+}
+
+/* This callback will be called on every occurence of events in dialogs */
+static void dlg_on_all_events(pjsip_dlg *dlg, pjsip_dlg_event_e dlg_evt,
+ pjsip_event *event )
+{
+ PJ_UNUSED_ARG(dlg_evt)
+ PJ_UNUSED_ARG(event)
+
+ PJ_LOG(4, (THIS_FILE, "dlg_on_all_events %p", dlg));
+}
+
+/* This callback is called before each outgoing msg is sent (including
+ * retransmission). Application can override this notification if it wants
+ * to modify the message before transmission or if it wants to do something
+ * else for each transmission.
+ */
+static void dlg_on_before_tx(pjsip_dlg *dlg, pjsip_transaction *tsx,
+ pjsip_tx_data *tdata, int ret_cnt)
+{
+ PJ_UNUSED_ARG(tsx)
+ PJ_UNUSED_ARG(tdata)
+
+ if (ret_cnt > 0) {
+ PJ_LOG(3, (THIS_FILE, "Dialog %s: retransmitting message (cnt=%d)",
+ dlg->obj_name, ret_cnt));
+ }
+}
+
+/* This callback is called after a message is sent. */
+static void dlg_on_tx_msg(pjsip_dlg *dlg, pjsip_transaction *tsx,
+ pjsip_tx_data *tdata)
+{
+ PJ_UNUSED_ARG(tsx)
+ PJ_UNUSED_ARG(tdata)
+
+ PJ_LOG(4, (THIS_FILE, "dlg_on_tx_msg %p", dlg));
+}
+
+/* This callback is called on receipt of incoming message. */
+static void dlg_on_rx_msg(pjsip_dlg *dlg, pjsip_transaction *tsx,
+ pjsip_rx_data *rdata)
+{
+ PJ_UNUSED_ARG(tsx)
+ PJ_UNUSED_ARG(rdata)
+ PJ_LOG(4, (THIS_FILE, "dlg_on_tx_msg %p", dlg));
+}
+
+/* This callback is called after dialog has sent INVITE */
+static void dlg_on_calling(pjsip_dlg *dlg, pjsip_transaction *tsx,
+ pjsip_tx_data *tdata)
+{
+ PJ_UNUSED_ARG(tsx)
+ PJ_UNUSED_ARG(tdata)
+
+ pj_assert(tdata->msg->type == PJSIP_REQUEST_MSG &&
+ tdata->msg->line.req.method.id == PJSIP_INVITE_METHOD &&
+ tsx->method.id == PJSIP_INVITE_METHOD);
+
+ PJ_LOG(3, (THIS_FILE, "Dialog %s: start calling...", dlg->obj_name));
+}
+
+static void create_session_from_sdp( pjsip_dlg *dlg, pjsdp_session_desc *sdp)
+{
+ struct dialog_data *dlg_data = dlg->user_data;
+ pj_bool_t sdp_x_ms_msg_index = -1;
+ int i;
+ int mcnt;
+ const pj_media_stream_info *mi[PJSDP_MAX_MEDIA];
+ int has_active;
+ pj_media_sock_info sock_info;
+
+ /* Find "m=x-ms-message" line in the SDP. */
+ for (i=0; i<(int)sdp->media_count; ++i) {
+ if (pj_stricmp2(&sdp->media[i]->desc.media, "x-ms-message")==0)
+ sdp_x_ms_msg_index = i;
+ }
+
+ /*
+ * Create media session.
+ */
+ pj_memset(&sock_info, 0, sizeof(sock_info));
+ sock_info.rtp_sock = global.rtp_sock;
+ sock_info.rtcp_sock = global.rtcp_sock;
+ pj_memcpy(&sock_info.rtp_addr_name, &global.rtp_sock_name, sizeof(pj_sockaddr_in));
+
+ dlg_data->msession = pj_media_session_create_from_sdp (global.mmgr, sdp, &sock_info);
+
+ /* A session will always be created, unless there is memory
+ * alloc problem.
+ */
+ pj_assert(dlg_data->msession);
+
+ /* See if we can take the offer by checking that we have at least
+ * one media stream active.
+ */
+ mcnt = pj_media_session_enum_streams(dlg_data->msession, PJSDP_MAX_MEDIA, mi);
+ for (i=0, has_active=0; i<mcnt; ++i) {
+ if (mi[i]->fmt_cnt>0 && mi[i]->dir!=PJ_MEDIA_DIR_NONE) {
+ has_active = 1;
+ break;
+ }
+ }
+
+ if (!has_active && sdp_x_ms_msg_index==-1) {
+ pjsip_tx_data *tdata;
+
+ /* Unable to accept remote's SDP.
+ * Answer with 488 (Not Acceptable Here)
+ */
+ /* Create 488 response. */
+ tdata = pjsip_dlg_answer(dlg, PJSIP_SC_NOT_ACCEPTABLE_HERE);
+
+ /* Send response. */
+ if (tdata)
+ pjsip_dlg_send_msg(dlg, tdata);
+ return;
+ }
+
+ dlg_data->x_ms_msg_session = sdp_x_ms_msg_index;
+
+ /* Create msg body to be used later in 2xx/response */
+ create_msg_body(dlg, 0);
+
+}
+
+/* This callback is called after an INVITE is received. */
+static void dlg_on_incoming(pjsip_dlg *dlg, pjsip_transaction *tsx,
+ pjsip_rx_data *rdata)
+{
+ struct dialog_data *dlg_data;
+ pjsip_msg *msg;
+ pjsip_tx_data *tdata;
+ char buf[128];
+ int len;
+
+ PJ_UNUSED_ARG(tsx)
+
+ pj_assert(rdata->msg->type == PJSIP_REQUEST_MSG &&
+ rdata->msg->line.req.method.id == PJSIP_INVITE_METHOD &&
+ tsx->method.id == PJSIP_INVITE_METHOD);
+
+ /*
+ * Notify user!
+ */
+ PJ_LOG(3, (THIS_FILE, ""));
+ PJ_LOG(3, (THIS_FILE, "INCOMING CALL ON DIALOG %s!!", dlg->obj_name));
+ PJ_LOG(3, (THIS_FILE, ""));
+ len = pjsip_uri_print( PJSIP_URI_IN_FROMTO_HDR,
+ (pjsip_name_addr*)dlg->remote.info->uri,
+ buf, sizeof(buf)-1);
+ if (len > 0) {
+ buf[len] = '\0';
+ PJ_LOG(3,(THIS_FILE, "From:\t%s", buf));
+ }
+ len = pjsip_uri_print( PJSIP_URI_IN_FROMTO_HDR,
+ (pjsip_name_addr*)dlg->local.info->uri,
+ buf, sizeof(buf)-1);
+ if (len > 0) {
+ buf[len] = '\0';
+ PJ_LOG(3,(THIS_FILE, "To:\t%s", buf));
+ }
+ PJ_LOG(3, (THIS_FILE, "Press 'a' to answer, or 'h' to hangup!!", dlg->obj_name));
+ PJ_LOG(3, (THIS_FILE, ""));
+
+ /*
+ * Process incoming dialog.
+ */
+
+ dlg_data = pj_pool_calloc(dlg->pool, 1, sizeof(struct dialog_data));
+ dlg->user_data = dlg_data;
+
+ /* Update contact. */
+ pjsip_dlg_set_contact(dlg, &global.contact);
+
+ /* Initialize credentials. */
+ pjsip_dlg_set_credentials(dlg, global.cred_count, global.cred_info);
+
+ /* Create media session if the request has "application/sdp" body. */
+ msg = rdata->msg;
+ if (msg->body &&
+ pj_stricmp2(&msg->body->content_type.type, "application")==0 &&
+ pj_stricmp2(&msg->body->content_type.subtype, "sdp")==0)
+ {
+ pjsdp_session_desc *sdp;
+
+ /* Parse SDP body, and instantiate media session based on remote's SDP.
+ * Then create our SDP body from the session.
+ */
+ sdp = pjsdp_parse (msg->body->data, msg->body->len, rdata->pool);
+ if (!sdp)
+ goto send_answer;
+
+ create_session_from_sdp(dlg, sdp);
+
+ } else if (msg->body) {
+ /* The request has a message body other than "application/sdp" */
+ pjsip_accept_hdr *accept;
+
+ /* Create response. */
+ tdata = pjsip_dlg_answer(dlg, PJSIP_SC_UNSUPPORTED_MEDIA_TYPE);
+
+ /* Add "Accept" header. */
+ accept = pjsip_accept_hdr_create(tdata->pool);
+ accept->values[0] = pj_str("application/sdp");
+ accept->count = 1;
+ pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr*)accept);
+
+ /* Send response. */
+ pjsip_dlg_send_msg(dlg, tdata);
+ return;
+
+ } else {
+ /* The request has no message body. We can take this request, but
+ * no media session will be activated.
+ */
+ /* Nothing to do here. */
+ }
+
+send_answer:
+ /* Immediately answer with 100 (or 180? */
+ tdata = pjsip_dlg_answer( dlg, PJSIP_SC_RINGING );
+ pjsip_dlg_send_msg(dlg, tdata);
+
+ /* Set current dialog to this dialog if we don't currently have
+ * current dialog.
+ */
+ if (global.cur_dlg == NULL) {
+ global.cur_dlg = dlg;
+ }
+
+ /* Auto-answer if option is specified. */
+ if (global.auto_answer >= 0) {
+ pj_time_val delay = { 0, 0};
+ struct dialog_data *dlg_data = dlg->user_data;
+
+ PJ_LOG(4, (THIS_FILE, "Scheduling auto-answer in %d seconds",
+ global.auto_answer));
+
+ delay.sec = global.auto_answer;
+ dlg_data->auto_timer.user_data = dlg;
+ dlg_data->auto_timer.id = AUTO_ANSWER;
+ dlg_data->auto_timer.cb = &dlg_auto_timer_callback;
+ dlg_data->has_auto_timer = 1;
+ pjsip_endpt_schedule_timer(dlg->ua->endpt, &dlg_data->auto_timer, &delay);
+ }
+}
+
+/* This callback is called when dialog has sent/received a provisional response
+ * to INVITE.
+ */
+static void dlg_on_provisional(pjsip_dlg *dlg, pjsip_transaction *tsx,
+ pjsip_event *event)
+{
+ const char *action;
+
+ pj_assert((event->src_type == PJSIP_EVENT_TX_MSG &&
+ event->src.tdata->msg->type == PJSIP_RESPONSE_MSG &&
+ event->src.tdata->msg->line.status.code/100 == 1 &&
+ tsx->method.id == PJSIP_INVITE_METHOD)
+ ||
+ (event->src_type == PJSIP_EVENT_RX_MSG &&
+ event->src.rdata->msg->type == PJSIP_RESPONSE_MSG &&
+ event->src.rdata->msg->line.status.code/100 == 1 &&
+ tsx->method.id == PJSIP_INVITE_METHOD));
+
+ if (event->src_type == PJSIP_EVENT_TX_MSG)
+ action = "Sending";
+ else
+ action = "Received";
+
+ PJ_LOG(3, (THIS_FILE, "Dialog %s: %s %d (%s)",
+ dlg->obj_name, action, tsx->status_code,
+ pjsip_get_status_text(tsx->status_code)->ptr));
+}
+
+/* This callback is called when 200 response to INVITE is sent/received. */
+static void dlg_on_connecting(pjsip_dlg *dlg, pjsip_event *event)
+{
+ struct dialog_data *dlg_data = dlg->user_data;
+ const char *action;
+
+ pj_assert((event->src_type == PJSIP_EVENT_TX_MSG &&
+ event->src.tdata->msg->type == PJSIP_RESPONSE_MSG &&
+ event->src.tdata->msg->line.status.code/100 == 2)
+ ||
+ (event->src_type == PJSIP_EVENT_RX_MSG &&
+ event->src.rdata->msg->type == PJSIP_RESPONSE_MSG &&
+ event->src.rdata->msg->line.status.code/100 == 2));
+
+ if (event->src_type == PJSIP_EVENT_RX_MSG)
+ action = "Received";
+ else
+ action = "Sending";
+
+ PJ_LOG(3, (THIS_FILE, "Dialog %s: %s 200 (OK)", dlg->obj_name, action));
+
+ if (event->src_type == PJSIP_EVENT_RX_MSG) {
+ /* On receipt of 2xx response, negotiate our media capability
+ * and start media.
+ */
+ pjsip_msg *msg = event->src.rdata->msg;
+ pjsip_msg_body *body;
+ pjsdp_session_desc *sdp;
+
+ /* Get SDP from message. */
+
+ /* Ignore if no SDP body is present. */
+ body = msg->body;
+ if (!body) {
+ PJ_LOG(3, (THIS_FILE, "Dialog %s: the 200/OK response has no body!",
+ dlg->obj_name));
+ return;
+ }
+
+ if (pj_stricmp2(&body->content_type.type, "application") != 0 &&
+ pj_stricmp2(&body->content_type.subtype, "sdp") != 0)
+ {
+ PJ_LOG(3, (THIS_FILE, "Dialog %s: the 200/OK response has no SDP body!",
+ dlg->obj_name));
+ return;
+ }
+
+ /* Got what seems to be a SDP content. Parse it. */
+ sdp = pjsdp_parse (body->data, body->len, event->src.rdata->pool);
+ if (!sdp) {
+ PJ_LOG(3, (THIS_FILE, "Dialog %s: SDP syntax error!",
+ dlg->obj_name));
+ return;
+ }
+
+ /* Negotiate media session with remote's media capability. */
+ if (pj_media_session_update (dlg_data->msession, sdp) != 0) {
+ PJ_LOG(3, (THIS_FILE, "Dialog %s: media session update error!",
+ dlg->obj_name));
+ return;
+ }
+
+ /* Update the saved SDP body because media session has changed.
+ * Also set ack flag to '1', because we only want to send one format/
+ * codec for each media streams.
+ */
+ create_msg_body(dlg, 1);
+
+ /* Activate media. */
+ pj_media_session_activate (dlg_data->msession);
+
+ } else {
+ pjsip_msg *msg = event->src.tdata->msg;
+
+ if (msg->body) {
+ /* On transmission of 2xx response, start media session. */
+ pj_media_session_activate (dlg_data->msession);
+ }
+ }
+
+}
+
+/* This callback is called when ACK to initial INVITE is sent/received. */
+static void dlg_on_established(pjsip_dlg *dlg, pjsip_event *event)
+{
+ const char *action;
+
+ pj_assert((event->src_type == PJSIP_EVENT_TX_MSG &&
+ event->src.tdata->msg->type == PJSIP_REQUEST_MSG &&
+ event->src.tdata->msg->line.req.method.id == PJSIP_ACK_METHOD)
+ ||
+ (event->src_type == PJSIP_EVENT_RX_MSG &&
+ event->src.rdata->msg->type == PJSIP_REQUEST_MSG &&
+ event->src.rdata->msg->line.req.method.id == PJSIP_ACK_METHOD));
+
+ if (event->src_type == PJSIP_EVENT_RX_MSG)
+ action = "Received";
+ else
+ action = "Sending";
+
+ PJ_LOG(3, (THIS_FILE, "Dialog %s: %s ACK, dialog is ESTABLISHED",
+ dlg->obj_name, action));
+
+ /* Attach SDP body for outgoing ACK. */
+ if (event->src_type == PJSIP_EVENT_TX_MSG &&
+ event->src.tdata->msg->line.req.method.id == PJSIP_ACK_METHOD)
+ {
+ struct dialog_data *dlg_data = dlg->user_data;
+ event->src.tdata->msg->body = dlg_data->body;
+ }
+
+ /* Auto-hangup if option is specified. */
+ if (global.auto_hangup >= 0) {
+ pj_time_val delay = { 0, 0};
+ struct dialog_data *dlg_data = dlg->user_data;
+
+ PJ_LOG(4, (THIS_FILE, "Scheduling auto-hangup in %d seconds",
+ global.auto_hangup));
+
+ delay.sec = global.auto_hangup;
+ dlg_data->auto_timer.user_data = dlg;
+ dlg_data->auto_timer.id = AUTO_HANGUP;
+ dlg_data->auto_timer.cb = &dlg_auto_timer_callback;
+ dlg_data->has_auto_timer = 1;
+ pjsip_endpt_schedule_timer(dlg->ua->endpt, &dlg_data->auto_timer, &delay);
+ }
+}
+
+
+/* This callback is called when dialog is disconnected (because of final
+ * response, BYE, or timer).
+ */
+static void dlg_on_disconnected(pjsip_dlg *dlg, pjsip_event *event)
+{
+ struct dialog_data *dlg_data = dlg->user_data;
+ int status_code;
+ const pj_str_t *reason;
+
+ PJ_UNUSED_ARG(event)
+
+ /* Cancel auto-answer/auto-hangup timer. */
+ if (dlg_data->has_auto_timer) {
+ pjsip_endpt_cancel_timer(dlg->ua->endpt, &dlg_data->auto_timer);
+ dlg_data->has_auto_timer = 0;
+ }
+
+ if (dlg->invite_tsx)
+ status_code = dlg->invite_tsx->status_code;
+ else
+ status_code = 200;
+
+ if (event->obj.tsx->method.id == PJSIP_INVITE_METHOD) {
+ if (event->src_type == PJSIP_EVENT_RX_MSG)
+ reason = &event->src.rdata->msg->line.status.reason;
+ else if (event->src_type == PJSIP_EVENT_TX_MSG)
+ reason = &event->src.tdata->msg->line.status.reason;
+ else
+ reason = pjsip_get_status_text(event->obj.tsx->status_code);
+ } else {
+ reason = &event->obj.tsx->method.name;
+ }
+
+ PJ_LOG(3, (THIS_FILE, "Dialog %s: DISCONNECTED! Reason=%d (%.*s)",
+ dlg->obj_name, status_code,
+ reason->slen, reason->ptr));
+
+ if (dlg_data->msession) {
+ pj_media_session_destroy (dlg_data->msession);
+ dlg_data->msession = NULL;
+ }
+}
+
+/* This callback is called when dialog is about to be destroyed. */
+static void dlg_on_terminated(pjsip_dlg *dlg)
+{
+ PJ_LOG(3, (THIS_FILE, "Dialog %s: terminated!", dlg->obj_name));
+
+ /* If current dialog is equal to this dialog, update it. */
+ if (global.cur_dlg == dlg) {
+ global.cur_dlg = global.cur_dlg->next;
+ if (global.cur_dlg == (void*)&global.user_agent->dlg_list) {
+ global.cur_dlg = NULL;
+ }
+ }
+}
+
+/* This callback is called for any requests when dialog is established. */
+static void dlg_on_mid_call_evt (pjsip_dlg *dlg, pjsip_event *event)
+{
+ pjsip_transaction *tsx = event->obj.tsx;
+
+ if (event->src_type == PJSIP_EVENT_RX_MSG &&
+ event->src.rdata->msg->type == PJSIP_REQUEST_MSG)
+ {
+ if (event->src.rdata->cseq->method.id == PJSIP_INVITE_METHOD) {
+ /* Re-invitation. */
+ pjsip_tx_data *tdata;
+
+ PJ_LOG(3,(THIS_FILE, "Dialog %s: accepting re-invitation (dummy)",
+ dlg->obj_name));
+ tdata = pjsip_dlg_answer(dlg, 200);
+ if (tdata) {
+ struct dialog_data *dlg_data = dlg->user_data;
+ tdata->msg->body = dlg_data->body;
+ pjsip_dlg_send_msg(dlg, tdata);
+ }
+ } else {
+ /* Don't worry, endpoint will answer with 500 or whetever. */
+ }
+
+ } else if (tsx->status_code/100 == 2) {
+ PJ_LOG(3,(THIS_FILE, "Dialog %s: outgoing %.*s success: %d (%s)",
+ dlg->obj_name,
+ tsx->method.name.slen, tsx->method.name.ptr,
+ tsx->status_code,
+ pjsip_get_status_text(tsx->status_code)->ptr));
+
+
+ } else if (tsx->status_code >= 300) {
+ pj_bool_t report_failure = PJ_TRUE;
+
+ /* Check for authentication failures. */
+ if (tsx->status_code==401 || tsx->status_code==407) {
+ pjsip_tx_data *tdata;
+ tdata = pjsip_auth_reinit_req( global.endpt,
+ dlg->pool, &dlg->auth_sess,
+ dlg->cred_count, dlg->cred_info,
+ tsx->last_tx, event->src.rdata );
+ if (tdata) {
+ int rc;
+ rc = pjsip_dlg_send_msg( dlg, tdata);
+ report_failure = (rc != 0);
+ }
+ }
+ if (report_failure) {
+ const pj_str_t *reason;
+ if (event->src_type == PJSIP_EVENT_RX_MSG) {
+ reason = &event->src.rdata->msg->line.status.reason;
+ } else {
+ reason = pjsip_get_status_text(tsx->status_code);
+ }
+ PJ_LOG(2,(THIS_FILE, "Dialog %s: outgoing request failed: %d (%.*s)",
+ dlg->obj_name, tsx->status_code,
+ reason->slen, reason->ptr));
+ }
+ }
+}
+
+/* Initialize sockets and optionally get the public address via STUN. */
+static pj_status_t init_sockets()
+{
+ enum {
+ RTP_START_PORT = 4000,
+ RTP_RANDOM_START = 2,
+ RTP_RETRY = 10
+ };
+ enum {
+ SIP_SOCK,
+ RTP_SOCK,
+ RTCP_SOCK,
+ };
+ int i;
+ int rtp_port;
+ pj_sock_t sock[3];
+ pj_sockaddr_in mapped_addr[3];
+
+ for (i=0; i<3; ++i)
+ sock[i] = PJ_INVALID_SOCKET;
+
+ /* Create and bind SIP UDP socket. */
+ sock[SIP_SOCK] = pj_sock_socket(PJ_AF_INET, PJ_SOCK_DGRAM, 0, 0);
+ if (sock[SIP_SOCK] == PJ_INVALID_SOCKET) {
+ PJ_LOG(2,(THIS_FILE, "Unable to create socket"));
+ goto on_error;
+ }
+ if (pj_sock_bind_in(sock[SIP_SOCK], 0, (pj_uint16_t)global.sip_port) != 0) {
+ PJ_LOG(2,(THIS_FILE, "Unable to bind to SIP port"));
+ goto on_error;
+ }
+
+ /* Initialize start of RTP port to try. */
+ rtp_port = RTP_START_PORT + (pj_rand() % RTP_RANDOM_START) / 2;
+
+ /* Loop retry to bind RTP and RTCP sockets. */
+ for (i=0; i<RTP_RETRY; ++i, rtp_port += 2) {
+
+ /* Create and bind RTP socket. */
+ sock[RTP_SOCK] = pj_sock_socket(PJ_AF_INET, PJ_SOCK_DGRAM, 0, 0);
+ if (sock[RTP_SOCK] == PJ_INVALID_SOCKET)
+ goto on_error;
+ if (pj_sock_bind_in(sock[RTP_SOCK], 0, (pj_uint16_t)rtp_port) != 0) {
+ pj_sock_close(sock[RTP_SOCK]); sock[RTP_SOCK] = PJ_INVALID_SOCKET;
+ continue;
+ }
+
+ /* Create and bind RTCP socket. */
+ sock[RTCP_SOCK] = pj_sock_socket(PJ_AF_INET, PJ_SOCK_DGRAM, 0, 0);
+ if (sock[RTCP_SOCK] == PJ_INVALID_SOCKET)
+ goto on_error;
+ if (pj_sock_bind_in(sock[RTCP_SOCK], 0, (pj_uint16_t)(rtp_port+1)) != 0) {
+ pj_sock_close(sock[RTP_SOCK]); sock[RTP_SOCK] = PJ_INVALID_SOCKET;
+ pj_sock_close(sock[RTCP_SOCK]); sock[RTCP_SOCK] = PJ_INVALID_SOCKET;
+ continue;
+ }
+
+ /*
+ * If we're configured to use STUN, then find out the mapped address,
+ * and make sure that the mapped RTCP port is adjacent with the RTP.
+ */
+ if (global.stun_port1 == 0) {
+ pj_str_t hostname;
+ pj_sockaddr_in addr;
+
+ /* Get local IP address. */
+ char hostname_buf[PJ_MAX_HOSTNAME];
+ if (gethostname(hostname_buf, sizeof(hostname_buf)))
+ goto on_error;
+ hostname = pj_str(hostname_buf);
+
+ pj_memset( &addr, 0, sizeof(addr));
+ addr.sin_family = PJ_AF_INET;
+ if (pj_sockaddr_set_str_addr( &addr, &hostname) != PJ_SUCCESS)
+ goto on_error;
+
+ for (i=0; i<3; ++i)
+ pj_memcpy(&mapped_addr[i], &addr, sizeof(addr));
+
+ mapped_addr[SIP_SOCK].sin_port = pj_htons((pj_uint16_t)global.sip_port);
+ mapped_addr[RTP_SOCK].sin_port = pj_htons((pj_uint16_t)rtp_port);
+ mapped_addr[RTCP_SOCK].sin_port = pj_htons((pj_uint16_t)(rtp_port+1));
+ break;
+ } else {
+ pj_status_t rc;
+ rc = pj_stun_get_mapped_addr( global.pf, 3, sock,
+ &global.stun_srv1, global.stun_port1,
+ &global.stun_srv2, global.stun_port2,
+ mapped_addr);
+ if (rc != 0) {
+ PJ_LOG(3,(THIS_FILE, "Error: %s", pj_stun_get_err_msg(rc)));
+ goto on_error;
+ }
+
+ if (pj_ntohs(mapped_addr[2].sin_port) == pj_ntohs(mapped_addr[1].sin_port)+1)
+ break;
+
+ pj_sock_close(sock[RTP_SOCK]); sock[RTP_SOCK] = PJ_INVALID_SOCKET;
+ pj_sock_close(sock[RTCP_SOCK]); sock[RTCP_SOCK] = PJ_INVALID_SOCKET;
+ }
+ }
+
+ if (sock[RTP_SOCK] == PJ_INVALID_SOCKET) {
+ PJ_LOG(2,(THIS_FILE, "Unable to find appropriate RTP/RTCP ports combination"));
+ goto on_error;
+ }
+
+ global.sip_sock = sock[SIP_SOCK];
+ pj_memcpy(&global.sip_sock_name, &mapped_addr[SIP_SOCK], sizeof(pj_sockaddr_in));
+ global.rtp_sock = sock[RTP_SOCK];
+ pj_memcpy(&global.rtp_sock_name, &mapped_addr[RTP_SOCK], sizeof(pj_sockaddr_in));
+ global.rtcp_sock = sock[RTCP_SOCK];
+ pj_memcpy(&global.rtcp_sock_name, &mapped_addr[RTCP_SOCK], sizeof(pj_sockaddr_in));
+
+ PJ_LOG(4,(THIS_FILE, "SIP UDP socket reachable at %s:%d",
+ pj_inet_ntoa(global.sip_sock_name.sin_addr),
+ pj_ntohs(global.sip_sock_name.sin_port)));
+ PJ_LOG(4,(THIS_FILE, "RTP socket reachable at %s:%d",
+ pj_inet_ntoa(global.rtp_sock_name.sin_addr),
+ pj_ntohs(global.rtp_sock_name.sin_port)));
+ PJ_LOG(4,(THIS_FILE, "RTCP UDP socket reachable at %s:%d",
+ pj_inet_ntoa(global.rtcp_sock_name.sin_addr),
+ pj_ntohs(global.rtcp_sock_name.sin_port)));
+ return 0;
+
+on_error:
+ for (i=0; i<3; ++i) {
+ if (sock[i] != PJ_INVALID_SOCKET)
+ pj_sock_close(sock[i]);
+ }
+ return -1;
+}
+
+static void log_function(int level, const char *buffer, int len)
+{
+ /* Write to both stdout and file. */
+ if (level <= global.app_log_level)
+ pj_log_to_stdout(level, buffer, len);
+ if (global.log_file) {
+ fwrite(buffer, len, 1, global.log_file);
+ fflush(global.log_file);
+ }
+}
+
+/* Initialize stack. */
+static pj_status_t init_stack()
+{
+ pj_status_t status;
+ pj_sockaddr_in bind_addr;
+ pj_sockaddr_in bind_name;
+ const char *local_addr;
+ static char local_uri[128];
+
+ /* Optionally set logging file. */
+ if (global.log_filename) {
+ global.log_file = fopen(global.log_filename, "wt");
+ }
+
+ /* Initialize endpoint. This will also call initialization to all the
+ * modules.
+ */
+ global.endpt = pjsip_endpt_create(global.pf);
+ if (global.endpt == NULL) {
+ return -1;
+ }
+
+ /* Set dialog callback. */
+ pjsip_ua_set_dialog_callback(global.user_agent, &dlg_callback);
+
+ /* Init listener's bound address and port. */
+ pj_sockaddr_init2(&bind_addr, "0.0.0.0", global.sip_port);
+ pj_sockaddr_init(&bind_name, pj_gethostname(), global.sip_port);
+
+ /* Add UDP transport listener. */
+ status = pjsip_endpt_create_udp_listener( global.endpt, global.sip_sock,
+ &global.sip_sock_name);
+ if (status != 0)
+ return -1;
+
+ local_addr = pj_inet_ntoa(global.sip_sock_name.sin_addr);
+
+#if PJ_HAS_TCP
+ /* Add TCP transport listener. */
+ status = pjsip_endpt_create_listener( global.endpt, PJSIP_TRANSPORT_TCP,
+ &bind_addr, &bind_name);
+ if (status != 0)
+ return -1;
+#endif
+
+ /* Determine user_id to be put in Contact */
+ if (global.local_uri.slen) {
+ pj_pool_t *pool = pj_pool_create(global.pf, "parser", 1024, 0, NULL);
+ pjsip_uri *uri;
+
+ uri = pjsip_parse_uri(pool, global.local_uri.ptr, global.local_uri.slen, 0);
+ if (uri) {
+ if (pj_stricmp2(pjsip_uri_get_scheme(uri), "sip")==0) {
+ pjsip_url *url = (pjsip_url*)pjsip_uri_get_uri(uri);
+ if (url->user.slen)
+ strncpy(global.user_id, url->user.ptr, url->user.slen);
+ }
+ }
+ pj_pool_release(pool);
+ }
+
+ if (global.user_id[0]=='\0') {
+ pj_native_strcpy(global.user_id, "user");
+ }
+
+ /* build contact */
+ global.real_contact.ptr = local_uri;
+ global.real_contact.slen =
+ sprintf(local_uri, "<sip:%s@%s:%d>", global.user_id, local_addr, global.sip_port);
+
+ if (global.contact.slen == 0)
+ global.contact = global.real_contact;
+
+ /* initialize local_uri with contact if it's not specified in cmdline */
+ if (global.local_uri.slen == 0)
+ global.local_uri = global.contact;
+
+ /* Init proxy. */
+ if (global.proxy.slen || global.outbound_proxy.slen) {
+ int count = 0;
+ pj_str_t proxy_url[2];
+
+ if (global.outbound_proxy.slen) {
+ proxy_url[count++] = global.outbound_proxy;
+ }
+ if (global.proxy.slen) {
+ proxy_url[count++] = global.proxy;
+ }
+
+ if (pjsip_endpt_set_proxies(global.endpt, count, proxy_url) != 0) {
+ PJ_LOG(2,(THIS_FILE, "Error setting proxy address!"));
+ return -1;
+ }
+ }
+
+ /* initialize SIP registration if registrar is configured */
+ if (global.registrar_uri.slen) {
+ global.regc = pjsip_regc_create( global.endpt, NULL, &regc_cb);
+ pjsip_regc_init( global.regc, &global.registrar_uri,
+ &global.local_uri,
+ &global.local_uri,
+ 1, &global.contact,
+ global.reg_timeout);
+ pjsip_regc_set_credentials( global.regc, global.cred_count, global.cred_info );
+ }
+
+ return PJ_SUCCESS;
+}
+
+/* Worker thread function, only used when threading is enabled. */
+static void *PJ_THREAD_FUNC worker_thread(void *unused)
+{
+ PJ_UNUSED_ARG(unused)
+
+ while (!global.worker_quit_flag) {
+ pj_time_val timeout = { 0, 10 };
+ pjsip_endpt_handle_events (global.endpt, &timeout);
+ }
+ return NULL;
+}
+
+
+/* Make call to the specified URI. */
+static pjsip_dlg *make_call(pj_str_t *remote_uri)
+{
+ pjsip_dlg *dlg;
+ pj_str_t local = global.contact;
+ pj_str_t remote = *remote_uri;
+ struct dialog_data *dlg_data;
+ pjsip_tx_data *tdata;
+ pj_media_sock_info sock_info;
+
+ /* Create new dialog instance. */
+ dlg = pjsip_ua_create_dialog(global.user_agent, PJSIP_ROLE_UAC);
+
+ /* Attach our own user data. */
+ dlg_data = pj_pool_calloc(dlg->pool, 1, sizeof(struct dialog_data));
+ dlg->user_data = dlg_data;
+
+ /* Create media session. */
+ pj_memset(&sock_info, 0, sizeof(sock_info));
+ sock_info.rtp_sock = global.rtp_sock;
+ sock_info.rtcp_sock = global.rtcp_sock;
+ pj_memcpy(&sock_info.rtp_addr_name, &global.rtp_sock_name, sizeof(pj_sockaddr_in));
+
+ dlg_data->msession = pj_media_session_create (global.mmgr, &sock_info);
+ dlg_data->x_ms_msg_session = -1;
+
+ if (global.offer_x_ms_msg) {
+ const pj_media_stream_info *minfo[32];
+ unsigned cnt;
+
+ cnt = pj_media_session_enum_streams(dlg_data->msession, 32, minfo);
+ if (cnt > 0)
+ dlg_data->x_ms_msg_session = cnt;
+ }
+
+ /* Initialize dialog with local and remote URI. */
+ if (pjsip_dlg_init(dlg, &local, &remote, NULL) != PJ_SUCCESS) {
+ pjsip_ua_destroy_dialog(dlg);
+ return NULL;
+ }
+
+ /* Initialize credentials. */
+ pjsip_dlg_set_credentials(dlg, global.cred_count, global.cred_info);
+
+ /* Send INVITE! */
+ tdata = pjsip_dlg_invite(dlg);
+ tdata->msg->body = create_msg_body (dlg, 0);
+
+ if (pjsip_dlg_send_msg(dlg, tdata) != PJ_SUCCESS) {
+ pjsip_ua_destroy_dialog(dlg);
+ return NULL;
+ }
+
+ return dlg;
+}
+
+/*
+ * Callback to receive incoming IM message.
+ */
+static int on_incoming_im_msg(pjsip_rx_data *rdata)
+{
+ pjsip_msg *msg = rdata->msg;
+ pjsip_msg_body *body = msg->body;
+ int len;
+ char to[128], from[128];
+
+
+ len = pjsip_uri_print( PJSIP_URI_IN_CONTACT_HDR,
+ rdata->from->uri, from, sizeof(from));
+ if (len > 0) from[len] = '\0';
+ else pj_native_strcpy(from, "<URL too long..>");
+
+ len = pjsip_uri_print( PJSIP_URI_IN_CONTACT_HDR,
+ rdata->to->uri, to, sizeof(to));
+ if (len > 0) to[len] = '\0';
+ else pj_native_strcpy(to, "<URL too long..>");
+
+ PJ_LOG(3,(THIS_FILE, "Incoming instant message:"));
+
+ printf("----- BEGIN INSTANT MESSAGE ----->\n");
+ printf("From:\t%s\n", from);
+ printf("To:\t%s\n", to);
+ printf("Body:\n%.*s\n", (body ? body->len : 0), (body ? (char*)body->data : ""));
+ printf("<------ END INSTANT MESSAGE ------\n");
+
+ fflush(stdout);
+
+ /* Must answer with final response. */
+ return 200;
+}
+
+/*
+ * Input URL.
+ */
+static pj_str_t *ui_input_url(pj_str_t *out, char *buf, int len, int *selection)
+{
+ int i;
+
+ *selection = -1;
+
+ printf("\nBuddy list:\n");
+ printf("---------------------------------------\n");
+ for (i=0; i<global.buddy_cnt; ++i) {
+ printf(" %d\t%s <%s>\n", i+1, global.buddy[i].ptr,
+ (global.buddy_status[i]?"Online":"Offline"));
+ }
+ printf("-------------------------------------\n");
+
+ printf("Choices\n"
+ "\t0 For current dialog.\n"
+ "\t[1-%02d] Select from buddy list\n"
+ "\tURL An URL\n"
+ , global.buddy_cnt);
+ printf("Input: ");
+
+ fflush(stdout);
+ fgets(buf, len, stdin);
+ buf[strlen(buf)-1] = '\0'; /* remove trailing newline. */
+
+ while (isspace(*buf)) ++buf;
+
+ if (!*buf || *buf=='\n' || *buf=='\r')
+ return NULL;
+
+ i = atoi(buf);
+
+ if (i == 0) {
+ if (isdigit(*buf)) {
+ *selection = 0;
+ *out = pj_str("0");
+ return out;
+ } else {
+ if (verify_sip_url(buf) != 0) {
+ puts("Invalid URL specified!");
+ return NULL;
+ }
+ *out = pj_str(buf);
+ return out;
+ }
+ } else if (i > global.buddy_cnt || i < 0) {
+ printf("Error: invalid selection!\n");
+ return NULL;
+ } else {
+ *out = global.buddy[i-1];
+ *selection = i;
+ return out;
+ }
+}
+
+
+static void generic_request_callback( void *token, pjsip_event *event )
+{
+ pjsip_transaction *tsx = event->obj.tsx;
+
+ PJ_UNUSED_ARG(token)
+
+ if (tsx->status_code/100 == 2) {
+ PJ_LOG(3,(THIS_FILE, "Outgoing %.*s %d (%s)",
+ event->obj.tsx->method.name.slen,
+ event->obj.tsx->method.name.ptr,
+ tsx->status_code,
+ pjsip_get_status_text(tsx->status_code)->ptr));
+ } else if (tsx->status_code==401 || tsx->status_code==407) {
+ pjsip_tx_data *tdata;
+ tdata = pjsip_auth_reinit_req( global.endpt,
+ global.pool, NULL, global.cred_count, global.cred_info,
+ tsx->last_tx, event->src.rdata);
+ if (tdata) {
+ int rc;
+ pjsip_cseq_hdr *cseq;
+ cseq = (pjsip_cseq_hdr*)pjsip_msg_find_hdr(tdata->msg, PJSIP_H_CSEQ, NULL);
+ cseq->cseq++;
+ rc = pjsip_endpt_send_request( global.endpt, tdata, -1, NULL,
+ &generic_request_callback);
+ if (rc == 0)
+ return;
+ }
+ PJ_LOG(2,(THIS_FILE, "Outgoing %.*s failed, status=%d (%s)",
+ event->obj.tsx->method.name.slen,
+ event->obj.tsx->method.name.ptr,
+ event->obj.tsx->status_code,
+ pjsip_get_status_text(event->obj.tsx->status_code)->ptr));
+ } else {
+ const pj_str_t *reason;
+ if (event->src_type == PJSIP_EVENT_RX_MSG)
+ reason = &event->src.rdata->msg->line.status.reason;
+ else
+ reason = pjsip_get_status_text(tsx->status_code);
+ PJ_LOG(2,(THIS_FILE, "Outgoing %.*s failed, status=%d (%.*s)",
+ event->obj.tsx->method.name.slen,
+ event->obj.tsx->method.name.ptr,
+ event->obj.tsx->status_code,
+ reason->slen, reason->ptr));
+ }
+}
+
+
+static void ui_send_im_message()
+{
+ char line[100];
+ char text_buf[100];
+ pj_str_t str;
+ pj_str_t text_msg;
+ int selection, rc;
+ pjsip_tx_data *tdata;
+
+ if (ui_input_url(&str, line, sizeof(line), &selection) == NULL)
+ return;
+
+
+ printf("Enter text to send (empty to cancel): "); fflush(stdout);
+ fgets(text_buf, sizeof(text_buf), stdin);
+ text_buf[strlen(text_buf)-1] = '\0';
+ if (!*text_buf)
+ return;
+
+ text_msg = pj_str(text_buf);
+
+ if (selection==0) {
+ pjsip_method message_method;
+ pj_str_t str_MESSAGE = { "MESSAGE", 7 };
+
+ /* Send IM to current dialog. */
+ if (global.cur_dlg == NULL || global.cur_dlg->state != PJSIP_DIALOG_STATE_ESTABLISHED) {
+ printf("No current dialog or dialog state is not ESTABLISHED!\n");
+ return;
+ }
+
+ pjsip_method_init( &message_method, global.cur_dlg->pool, &str_MESSAGE);
+ tdata = pjsip_dlg_create_request( global.cur_dlg, &message_method, -1 );
+
+ if (tdata) {
+ /* Create message body for the text. */
+ pjsip_msg_body *body = pj_pool_calloc(tdata->pool, 1, sizeof(*body));
+ body->content_type.type = pj_str("text");
+ body->content_type.subtype = pj_str("plain");
+ body->data = pj_pool_alloc(tdata->pool, text_msg.slen);
+ pj_memcpy(body->data, text_msg.ptr, text_msg.slen);
+ body->len = text_msg.slen;
+ body->print_body = &pjsip_print_text_body;
+
+ /* Assign body to message, and send the message! */
+ tdata->msg->body = body;
+ pjsip_dlg_send_msg( global.cur_dlg, tdata );
+ }
+
+ } else {
+ /* Send IM to buddy list. */
+ pjsip_method message;
+ static pj_str_t MESSAGE = { "MESSAGE", 7 };
+ pjsip_method_init_np(&message, &MESSAGE);
+ tdata = pjsip_endpt_create_request(global.endpt, &message,
+ &str,
+ &global.real_contact,
+ &str, &global.real_contact, NULL, -1,
+ &text_msg);
+ if (!tdata) {
+ puts("Error creating request");
+ return;
+ }
+ rc = pjsip_endpt_send_request(global.endpt, tdata, -1, NULL, &generic_request_callback);
+ if (rc == 0) {
+ printf("Sending IM message %d\n", global.im_counter);
+ ++global.im_counter;
+ } else {
+ printf("Error: unable to send IM message!\n");
+ }
+ }
+}
+
+static void ui_send_options()
+{
+ char line[100];
+ pj_str_t str;
+ int selection, rc;
+ pjsip_tx_data *tdata;
+ pjsip_method options;
+
+ if (ui_input_url(&str, line, sizeof(line), &selection) == NULL)
+ return;
+
+ pjsip_method_set( &options, PJSIP_OPTIONS_METHOD );
+
+ if (selection == 0) {
+ /* Send OPTIONS to current dialog. */
+ tdata = pjsip_dlg_create_request(global.cur_dlg, &options, -1);
+ if (tdata)
+ pjsip_dlg_send_msg( global.cur_dlg, tdata );
+ } else {
+ /* Send OPTIONS to arbitrary party. */
+ tdata = pjsip_endpt_create_request( global.endpt, &options,
+ &str,
+ &global.local_uri, &str,
+ &global.real_contact,
+ NULL, -1, NULL);
+ if (tdata) {
+ rc = pjsip_endpt_send_request( global.endpt, tdata, -1, NULL,
+ &generic_request_callback);
+ if (rc != 0)
+ PJ_LOG(2,(THIS_FILE, "Error sending OPTIONS!"));
+ }
+ }
+}
+
+static void init_presence()
+{
+ const pjsip_presence_cb pres_cb = {
+ NULL,
+ &pres_on_received_request,
+ &pres_on_received_refresh,
+ &pres_on_received_update,
+ &pres_on_terminated
+ };
+
+ pjsip_presence_init(&pres_cb);
+}
+
+/* Subscribe presence information for all buddies. */
+static void subscribe_buddies_presence()
+{
+ int i;
+ for (i=0; i<global.buddy_cnt; ++i) {
+ pjsip_presentity *pres;
+ if (global.buddy_pres[i])
+ continue;
+ pres = pjsip_presence_create( global.endpt, &global.local_uri,
+ &global.buddy[i], PRESENCE_TIMEOUT, (void*)i);
+ if (pres) {
+ pjsip_presence_set_credentials( pres, global.cred_count, global.cred_info );
+ pjsip_presence_subscribe( pres );
+ }
+ global.buddy_pres[i] = pres;
+ }
+}
+
+/* Unsubscribe presence information for all buddies. */
+static void unsubscribe_buddies_presence()
+{
+ int i;
+ for (i=0; i<global.buddy_cnt; ++i) {
+ pjsip_presentity *pres = global.buddy_pres[i];
+ if (pres) {
+ pjsip_presence_unsubscribe(pres);
+ pjsip_presence_destroy(pres);
+ global.buddy_pres[i] = NULL;
+ }
+ }
+}
+
+/* Unsubscribe presence. */
+static void unsubscribe_presence()
+{
+ int i;
+
+ unsubscribe_buddies_presence();
+ for (i=0; i<global.pres_cnt; ++i) {
+ pjsip_presentity *pres = global.pres[i];
+ pjsip_presence_notify( pres, PJSIP_EVENT_SUB_STATE_TERMINATED, 0);
+ pjsip_presence_destroy( pres );
+ }
+}
+
+/* Advertise online status to subscribers. */
+static void update_im_status()
+{
+ int i;
+ for (i=0; i<global.pres_cnt; ++i) {
+ pjsip_presentity *pres = global.pres[i];
+ pjsip_presence_notify( pres, PJSIP_EVENT_SUB_STATE_ACTIVE,
+ !global.hide_status);
+ }
+}
+
+/*
+ * Main program.
+ */
+int main(int argc, char *argv[])
+{
+ /* set to WORKER_COUNT+1 to avoid zero size warning
+ * when threading is disabled. */
+ pj_thread_t *thread[WORKER_COUNT+1];
+ pj_caching_pool cp;
+ int i;
+
+ global.sip_port = 5060;
+ global.auto_answer = -1;
+ global.auto_hangup = -1;
+ global.app_log_level = 3;
+
+ pj_log_set_level(4);
+ pj_log_set_log_func(&log_function);
+
+ /* Init PJLIB */
+ if (pj_init() != PJ_SUCCESS)
+ return 1;
+
+ /* Init caching pool. */
+ pj_caching_pool_init(&cp, &pj_pool_factory_default_policy, 0);
+ global.pf = &cp.factory;
+
+ /* Create memory pool for application. */
+ global.pool = pj_pool_create(global.pf, "main", 1024, 0, NULL);
+
+ /* Parse command line arguments. */
+ if (parse_args(global.pool, argc, argv) != PJ_SUCCESS) {
+ pj_caching_pool_destroy(&cp);
+ return 1;
+ }
+
+ /* Init sockets */
+ if (init_sockets() != 0) {
+ pj_caching_pool_destroy(&cp);
+ return 1;
+ }
+
+ /* Initialize stack. */
+ if (init_stack() != PJ_SUCCESS) {
+ pj_caching_pool_destroy(&cp);
+ return 1;
+ }
+
+ /* Set callback to receive incoming IM */
+ pjsip_messaging_set_incoming_callback( &on_incoming_im_msg );
+
+ /* Set default worker count (can be zero) */
+ global.worker_cnt = WORKER_COUNT;
+
+ /* Create user worker thread(s), only when threading is enabled. */
+ for (i=0; i<global.worker_cnt; ++i) {
+ thread[i] = pj_thread_create( global.pool, "sip%p",
+ &worker_thread,
+ NULL, 0, NULL, 0);
+ if (thread == NULL) {
+ global.worker_quit_flag = 1;
+ for (--i; i>=0; --i) {
+ pj_thread_join(thread[i]);
+ pj_thread_destroy(thread[i]);
+ }
+ pj_caching_pool_destroy(&cp);
+ return 1;
+ }
+ }
+
+ printf("Worker thread count: %d\n", global.worker_cnt);
+
+ /* Perform registration, if required. */
+ if (global.regc) {
+ update_registration(global.regc, 1);
+ }
+
+ /* Initialize media manager. */
+ global.mmgr = pj_med_mgr_create(global.pf);
+
+ /* Init presence. */
+ init_presence();
+
+ /* Subscribe presence information of all buddies. */
+ if (!global.no_presence)
+ subscribe_buddies_presence();
+
+ /* Initializatio completes, loop waiting for commands. */
+ for (;!global.worker_quit_flag;) {
+ pj_str_t str;
+ char line[128];
+
+#if WORKER_COUNT==0
+ /* If worker thread does not exist, main thread must poll for evetns.
+ * But this won't work very well since main thread is blocked by
+ * fgets(). So keep pressing the ENTER key to get the events!
+ */
+ pj_time_val timeout = { 0, 100 };
+ pjsip_endpt_handle_events(global.endpt, &timeout);
+ puts("Keep pressing ENTER key to get the events!");
+#endif
+
+ printf("\nCurrent dialog: ");
+ print_dialog(global.cur_dlg);
+ puts("");
+
+ keystroke_help();
+
+ fgets(line, sizeof(line), stdin);
+
+ switch (*line) {
+ case 'm':
+ puts("Make outgoing call");
+ if (ui_input_url(&str, line, sizeof(line), &i) != NULL) {
+ pjsip_dlg *dlg = make_call(&str);
+ if (global.cur_dlg == NULL) {
+ global.cur_dlg = dlg;
+ }
+ }
+ break;
+ case 'i':
+ puts("Send Instant Messaging");
+ ui_send_im_message();
+ break;
+ case 'o':
+ puts("Send OPTIONS");
+ ui_send_options();
+ break;
+ case 'a':
+ if (global.cur_dlg) {
+ unsigned code;
+ pjsip_tx_data *tdata;
+ struct dialog_data *dlg_data = global.cur_dlg->user_data;
+
+ printf("Answer with status code (1xx-6xx): ");
+ fflush(stdout);
+ fgets(line, sizeof(line), stdin);
+ str = pj_str(line);
+ str.slen -= 1;
+
+ code = pj_strtoul(&str);
+ tdata = pjsip_dlg_answer(global.cur_dlg, code);
+ if (tdata) {
+ if (code/100 == 2) {
+ tdata->msg->body = dlg_data->body;
+ }
+ pjsip_dlg_send_msg(global.cur_dlg, tdata);
+
+ }
+ } else {
+ puts("No current dialog");
+ }
+ break;
+ case 'h':
+ if (global.cur_dlg) {
+ pjsip_tx_data *tdata;
+ tdata = pjsip_dlg_disconnect(global.cur_dlg, PJSIP_SC_DECLINE);
+ if (tdata) {
+ pjsip_dlg_send_msg(global.cur_dlg, tdata);
+ }
+ } else {
+ puts("No current dialog");
+ }
+ break;
+ case ']':
+ if (global.cur_dlg) {
+ global.cur_dlg = global.cur_dlg->next;
+ if (global.cur_dlg == (void*)&global.user_agent->dlg_list) {
+ global.cur_dlg = global.cur_dlg->next;
+ }
+ } else {
+ puts("No current dialog");
+ }
+ break;
+ case '[':
+ if (global.cur_dlg) {
+ global.cur_dlg = global.cur_dlg->prev;
+ if (global.cur_dlg == (void*)&global.user_agent->dlg_list) {
+ global.cur_dlg = global.cur_dlg->prev;
+ }
+ } else {
+ puts("No current dialog");
+ }
+ break;
+ case 'd':
+ pjsip_endpt_dump(global.endpt, *(line+1)=='1');
+ pjsip_ua_dump(global.user_agent);
+ break;
+ case 's':
+ if (*(line+1) == 'u')
+ subscribe_buddies_presence();
+ break;
+ case 'u':
+ if (*(line+1) == 's')
+ unsubscribe_presence();
+ break;
+ case 't':
+ global.hide_status = !global.hide_status;
+ update_im_status();
+ break;
+ case 'q':
+ goto on_exit;
+ case 'l':
+ print_all_dialogs();
+ break;
+ }
+ }
+
+on_exit:
+ /* Unregister, if required. */
+ if (global.regc) {
+ update_registration(global.regc, 0);
+ }
+
+ /* Unsubscribe presence. */
+ unsubscribe_presence();
+
+ /* Allow one second to get all events. */
+ if (1) {
+ pj_time_val end_time;
+
+ pj_gettimeofday(&end_time);
+ end_time.sec++;
+
+ PJ_LOG(3,(THIS_FILE, "Shutting down.."));
+ for (;;) {
+ pj_time_val timeout = { 0, 20 }, now;
+ pjsip_endpt_handle_events (global.endpt, &timeout);
+ pj_gettimeofday(&now);
+ PJ_TIME_VAL_SUB(now, end_time);
+ if (now.sec >= 1)
+ break;
+ }
+ }
+
+ global.worker_quit_flag = 1;
+
+ pj_med_mgr_destroy(global.mmgr);
+
+ /* Wait all threads to quit. */
+ for (i=0; i<global.worker_cnt; ++i) {
+ pj_thread_join(thread[i]);
+ pj_thread_destroy(thread[i]);
+ }
+
+ /* Destroy endpoint. */
+ pjsip_endpt_destroy(global.endpt);
+
+ /* Destroy caching pool. */
+ pj_caching_pool_destroy(&cp);
+
+ /* Close log file, if any. */
+ if (global.log_file)
+ fclose(global.log_file);
+
+ return 0;
+}
+
+/*
+ * Register static modules to the endpoint.
+ */
+pj_status_t register_static_modules( pj_size_t *count,
+ pjsip_module **modules )
+{
+ /* Reset count. */
+ *count = 0;
+
+ /* Register user agent module. */
+ modules[(*count)++] = pjsip_ua_get_module();
+ global.user_agent = modules[0]->mod_data;
+ modules[(*count)++] = pjsip_messaging_get_module();
+ modules[(*count)++] = pjsip_event_sub_get_module();
+
+ return PJ_SUCCESS;
+}
diff --git a/pjsip/src/pjsua/misc.c b/pjsip/src/pjsua/misc.c
index a9b3510b..5c063fc7 100644
--- a/pjsip/src/pjsua/misc.c
+++ b/pjsip/src/pjsua/misc.c
@@ -1,485 +1,485 @@
-/* $Id$ */
-/*
- * Copyright (C) 2003-2006 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
- */
-
-/*
- * THIS FILE IS INCLUDED BY main.c.
- * IT WON'T COMPILE BY ITSELF.
- */
-
-#include "getopt.h"
-#include <stdio.h>
-
-
-/*
- * Display program usage
- */
-static void usage()
-{
- puts("Usage:");
- puts(" pjsua [options] [sip-url]");
- puts("");
- puts(" [sip-url] Default URL to invite.");
- puts("");
- puts("General options:");
- puts(" --config-file=file Read the config/arguments from file.");
- puts(" --log-file=fname Log to filename (default stderr)");
- puts(" --log-level=N Set log max level to N (0(none) to 6(trace))");
- puts(" --app-log-level=N Set log max level for stdout display to N");
- puts(" --help Display this help screen");
- puts(" --version Display version info");
- puts("");
- puts("Media options:");
- puts(" --null-audio Use NULL audio device");
- puts("");
- puts("User Agent options:");
- puts(" --auto-answer=sec Auto-answer all incoming calls after sec seconds.");
- puts(" --auto-hangup=sec Auto-hangup all calls after sec seconds.");
- puts("");
- puts("SIP options:");
- puts(" --local-port=port Set TCP/UDP port");
- puts(" --id=url Set the URL of local ID (used in From header)");
- puts(" --contact=url Override the Contact information");
- puts(" --proxy=url Set the URL of proxy server");
- puts(" --outbound=url Set the URL of outbound proxy server");
- puts(" --registrar=url Set the URL of registrar server");
- puts(" --reg-timeout=secs Set registration interval to secs (default 3600)");
- puts("");
- puts("Authentication options:");
- puts(" --realm=string Set realm");
- puts(" --username=string Set authentication username");
- puts(" --password=string Set authentication password");
- puts("");
- puts("STUN options (all must be specified):");
- puts(" --use-stun1=host[:port]");
- puts(" --use-stun2=host[:port] Use STUN and set host name and port of STUN servers");
- puts("");
- puts("SIMPLE options (may be specified more than once):");
- puts(" --add-buddy url Add the specified URL to the buddy list.");
- puts(" --offer-x-ms-msg Offer \"x-ms-message\" in outgoing INVITE");
- puts(" --no-presence Do not subscribe presence of buddies");
- puts("");
- fflush(stdout);
-}
-
-/* Display keystroke help. */
-static void keystroke_help()
-{
- int i;
-
- printf("Advertise status as: %s\n", (global.hide_status ? "Offline" : "Online"));
- puts("");
- puts("Buddy list:");
- puts("-------------------------------------------------------------------------------");
- for (i=0; i<global.buddy_cnt; ++i) {
- printf(" %d\t%s <%s>\n", i+1, global.buddy[i].ptr,
- (global.buddy_status[i]?"Online":"Offline"));
- }
- //printf("-------------------------------------\n");
- puts("");
- //puts("Commands:");
- puts("+=============================================================================+");
- puts("| Call Commands: | IM & Presence: | Misc: |");
- puts("| | | |");
- puts("| m Make new call | i Send IM | o Send OPTIONS |");
- puts("| a Answer call | su Subscribe presence | d Dump status |");
- puts("| h Hangup call | us Unsubscribe presence | d1 Dump detailed |");
- puts("| ] Select next dialog | t Toggle Online status | |");
- puts("| [ Select previous dialog | | |");
- puts("+-----------------------------------------------------------------------------+");
- puts("| q QUIT |");
- puts("+=============================================================================+");
- puts("");
-
-
- fflush(stdout);
-}
-
-/*
- * Verify that valid SIP url is given.
- */
-static pj_status_t verify_sip_url(char *url)
-{
- pjsip_uri *p;
- pj_pool_t *pool;
- int len = (url ? strlen(url) : 0);
-
- if (!len) return -1;
-
- pool = pj_pool_create(global.pf, "check%p", 1024, 0, NULL);
- if (!pool) return -1;
-
- p = pjsip_parse_uri(pool, url, len, 0);
- if (!p || pj_stricmp2(pjsip_uri_get_scheme(p), "sip") != 0)
- p = NULL;
-
- pj_pool_release(pool);
- return p ? 0 : -1;
-}
-
-/*
- * Read command arguments from config file.
- */
-static int read_config_file(pj_pool_t *pool, const char *filename,
- int *app_argc, char ***app_argv)
-{
- int i;
- FILE *fhnd;
- char line[200];
- int argc = 0;
- char **argv;
- enum { MAX_ARGS = 64 };
-
- /* Allocate MAX_ARGS+1 (argv needs to be terminated with NULL argument) */
- argv = pj_pool_calloc(pool, MAX_ARGS+1, sizeof(char*));
- argv[argc++] = *app_argv[0];
-
- /* Open config file. */
- fhnd = fopen(filename, "rt");
- if (!fhnd) {
- printf("Unable to open config file %s\n", filename);
- return -1;
- }
-
- /* Scan tokens in the file. */
- while (argc < MAX_ARGS && !feof(fhnd)) {
- char *token, *p = line;
-
- if (fgets(line, sizeof(line), fhnd) == NULL) break;
-
- for (token = strtok(p, " \t\r\n"); argc < MAX_ARGS;
- token = strtok(NULL, " \t\r\n"))
- {
- int token_len;
-
- if (!token) break;
- if (*token == '#') break;
-
- token_len = strlen(token);
- if (!token_len)
- continue;
- argv[argc] = pj_pool_alloc(pool, token_len+1);
- pj_memcpy(argv[argc], token, token_len+1);
- ++argc;
- }
- }
-
- /* Copy arguments from command line */
- for (i=1; i<*app_argc && argc < MAX_ARGS; ++i)
- argv[argc++] = (*app_argv)[i];
-
- if (argc == MAX_ARGS && (i!=*app_argc || !feof(fhnd))) {
- printf("Too many arguments specified in cmd line/config file\n");
- fclose(fhnd);
- return -1;
- }
-
- fclose(fhnd);
-
- /* Assign the new command line back to the original command line. */
- *app_argc = argc;
- *app_argv = argv;
- return 0;
-
-}
-
-/*
- * Parse program arguments
- */
-static int parse_args(pj_pool_t *pool, int argc, char *argv[])
-{
- int c;
- int option_index;
- enum { OPT_CONFIG_FILE, OPT_LOG_FILE, OPT_LOG_LEVEL, OPT_APP_LOG_LEVEL,
- OPT_HELP, OPT_VERSION, OPT_NULL_AUDIO,
- OPT_LOCAL_PORT, OPT_PROXY, OPT_OUTBOUND_PROXY, OPT_REGISTRAR,
- OPT_REG_TIMEOUT, OPT_ID, OPT_CONTACT,
- OPT_REALM, OPT_USERNAME, OPT_PASSWORD,
- OPT_USE_STUN1, OPT_USE_STUN2,
- OPT_ADD_BUDDY, OPT_OFFER_X_MS_MSG, OPT_NO_PRESENCE,
- OPT_AUTO_ANSWER, OPT_AUTO_HANGUP};
- struct option long_options[] = {
- { "config-file",1, 0, OPT_CONFIG_FILE},
- { "log-file", 1, 0, OPT_LOG_FILE},
- { "log-level", 1, 0, OPT_LOG_LEVEL},
- { "app-log-level",1,0,OPT_APP_LOG_LEVEL},
- { "help", 0, 0, OPT_HELP},
- { "version", 0, 0, OPT_VERSION},
- { "null-audio", 0, 0, OPT_NULL_AUDIO},
- { "local-port", 1, 0, OPT_LOCAL_PORT},
- { "proxy", 1, 0, OPT_PROXY},
- { "outbound", 1, 0, OPT_OUTBOUND_PROXY},
- { "registrar", 1, 0, OPT_REGISTRAR},
- { "reg-timeout",1, 0, OPT_REG_TIMEOUT},
- { "id", 1, 0, OPT_ID},
- { "contact", 1, 0, OPT_CONTACT},
- { "realm", 1, 0, OPT_REALM},
- { "username", 1, 0, OPT_USERNAME},
- { "password", 1, 0, OPT_PASSWORD},
- { "use-stun1", 1, 0, OPT_USE_STUN1},
- { "use-stun2", 1, 0, OPT_USE_STUN2},
- { "add-buddy", 1, 0, OPT_ADD_BUDDY},
- { "offer-x-ms-msg",0,0,OPT_OFFER_X_MS_MSG},
- { "no-presence", 0, 0, OPT_NO_PRESENCE},
- { "auto-answer",1, 0, OPT_AUTO_ANSWER},
- { "auto-hangup",1, 0, OPT_AUTO_HANGUP},
- { NULL, 0, 0, 0}
- };
- char *config_file = NULL;
-
- /* Run getopt once to see if user specifies config file to read. */
- while ((c=getopt_long(argc, argv, "", long_options, &option_index)) != -1) {
- switch (c) {
- case 0:
- config_file = optarg;
- break;
- }
- if (config_file)
- break;
- }
-
- if (config_file) {
- if (read_config_file(pool, config_file, &argc, &argv) != 0)
- return -1;
- }
-
- /* Reinitialize and re-run getopt again, possibly with new arguments
- * read from config file.
- */
- optind = 0;
- while ((c=getopt_long(argc, argv, "", long_options, &option_index)) != -1) {
- char *err, *p;
-
- switch (c) {
- case OPT_LOG_FILE:
- global.log_filename = optarg;
- break;
- case OPT_LOG_LEVEL:
- c = strtoul(optarg, &err, 10);
- if (*err) {
- printf("Error: expecting integer value 0-6 for --log-level\n");
- return -1;
- }
- pj_log_set_level( c );
- break;
- case OPT_APP_LOG_LEVEL:
- global.app_log_level = strtoul(optarg, &err, 10);
- if (*err) {
- printf("Error: expecting integer value 0-6 for --app-log-level\n");
- return -1;
- }
- break;
- case OPT_HELP:
- usage();
- return -1;
- case OPT_VERSION: /* version */
- pj_dump_config();
- return -1;
- case OPT_NULL_AUDIO:
- global.null_audio = 1;
- break;
- case OPT_LOCAL_PORT: /* local-port */
- global.sip_port = strtoul(optarg, &err, 10);
- if (*err) {
- printf("Error: expecting integer value for --local-port\n");
- return -1;
- }
- break;
- case OPT_PROXY: /* proxy */
- if (verify_sip_url(optarg) != 0) {
- printf("Error: invalid SIP URL '%s' in proxy argument\n", optarg);
- return -1;
- }
- global.proxy = pj_str(optarg);
- break;
- case OPT_OUTBOUND_PROXY: /* outbound proxy */
- if (verify_sip_url(optarg) != 0) {
- printf("Error: invalid SIP URL '%s' in outbound proxy argument\n", optarg);
- return -1;
- }
- global.outbound_proxy = pj_str(optarg);
- break;
- case OPT_REGISTRAR: /* registrar */
- if (verify_sip_url(optarg) != 0) {
- printf("Error: invalid SIP URL '%s' in registrar argument\n", optarg);
- return -1;
- }
- global.registrar_uri = pj_str(optarg);
- break;
- case OPT_REG_TIMEOUT: /* reg-timeout */
- global.reg_timeout = strtoul(optarg, &err, 10);
- if (*err) {
- printf("Error: expecting integer value for --reg-timeout\n");
- return -1;
- }
- break;
- case OPT_ID: /* id */
- if (verify_sip_url(optarg) != 0) {
- printf("Error: invalid SIP URL '%s' in local id argument\n", optarg);
- return -1;
- }
- global.local_uri = pj_str(optarg);
- break;
- case OPT_CONTACT: /* contact */
- if (verify_sip_url(optarg) != 0) {
- printf("Error: invalid SIP URL '%s' in contact argument\n", optarg);
- return -1;
- }
- global.contact = pj_str(optarg);
- break;
- case OPT_USERNAME: /* Default authentication user */
- if (!global.cred_count) global.cred_count = 1;
- global.cred_info[0].username = pj_str(optarg);
- break;
- case OPT_REALM: /* Default authentication realm. */
- if (!global.cred_count) global.cred_count = 1;
- global.cred_info[0].realm = pj_str(optarg);
- break;
- case OPT_PASSWORD: /* authentication password */
- if (!global.cred_count) global.cred_count = 1;
- global.cred_info[0].data_type = 0;
- global.cred_info[0].data = pj_str(optarg);
- break;
- case OPT_USE_STUN1: /* STUN server 1 */
- p = pj_native_strchr(optarg, ':');
- if (p) {
- *p = '\0';
- global.stun_srv1 = pj_str(optarg);
- global.stun_port1 = strtoul(p+1, &err, 10);
- if (*err || global.stun_port1==0) {
- printf("Error: expecting port number with option --use-stun1\n");
- return -1;
- }
- } else {
- global.stun_port1 = 3478;
- global.stun_srv1 = pj_str(optarg);
- }
- break;
- case OPT_USE_STUN2: /* STUN server 2 */
- p = pj_native_strchr(optarg, ':');
- if (p) {
- *p = '\0';
- global.stun_srv2 = pj_str(optarg);
- global.stun_port2 = strtoul(p+1, &err, 10);
- if (*err || global.stun_port2==0) {
- printf("Error: expecting port number with option --use-stun2\n");
- return -1;
- }
- } else {
- global.stun_port2 = 3478;
- global.stun_srv2 = pj_str(optarg);
- }
- break;
- case OPT_ADD_BUDDY: /* Add to buddy list. */
- if (verify_sip_url(optarg) != 0) {
- printf("Error: invalid URL '%s' in --add-buddy option\n", optarg);
- return -1;
- }
- if (global.buddy_cnt == MAX_BUDDIES) {
- printf("Error: too many buddies in buddy list.\n");
- return -1;
- }
- global.buddy[global.buddy_cnt++] = pj_str(optarg);
- break;
- case OPT_OFFER_X_MS_MSG:
- global.offer_x_ms_msg = 1;
- break;
- case OPT_NO_PRESENCE:
- global.no_presence = 1;
- break;
- case OPT_AUTO_ANSWER:
- global.auto_answer = strtoul(optarg, &err, 10);
- if (*err) {
- printf("Error: expecting integer value for --auto-answer option\n");
- return -1;
- }
- break;
- case OPT_AUTO_HANGUP:
- global.auto_hangup = strtoul(optarg, &err, 10);
- if (*err) {
- printf("Error: expecting integer value for --auto-hangup option\n");
- return -1;
- }
- break;
- }
- }
-
- if (optind != argc) {
- printf("Error: unknown options %s\n", argv[optind]);
- return -1;
- }
-
- if (global.reg_timeout == 0)
- global.reg_timeout = 3600;
-
- return 0;
-}
-
-/* Print dialog. */
-static void print_dialog(pjsip_dlg *dlg)
-{
- if (!dlg) {
- puts("none");
- return;
- }
-
- printf("%s: call-id=%.*s", dlg->obj_name,
- (int)dlg->call_id->id.slen,
- dlg->call_id->id.ptr);
-
- printf(" (%s, %s)\n", pjsip_role_name(dlg->role),
- pjsip_dlg_state_str(dlg->state));
-}
-
-/* Dump media statistic */
-void dump_media_statistic(pjsip_dlg *dlg)
-{
- struct dialog_data *dlg_data = dlg->user_data;
- pj_media_stream_stat stat[2];
- const char *statname[2] = { "TX", "RX" };
- int i;
-
- pj_media_session_get_stat (dlg_data->msession, 0, &stat[0], &stat[1]);
-
- printf("Media statistic:\n");
- for (i=0; i<2; ++i) {
- printf(" %s statistics:\n", statname[i]);
- printf(" Pkt TX=%d RX=%d\n", stat[i].pkt_tx, stat[i].pkt_rx);
- printf(" Octets TX=%d RX=%d\n", stat[i].oct_tx, stat[i].oct_rx);
- printf(" Jitter %d ms\n", stat[i].jitter);
- printf(" Pkt lost %d\n", stat[i].pkt_lost);
- }
- printf("\n");
-}
-
-/* Print all dialogs. */
-static void print_all_dialogs()
-{
- pjsip_dlg *dlg = (pjsip_dlg *)global.user_agent->dlg_list.next;
-
- puts("List all dialogs:");
-
- while (dlg != (pjsip_dlg *) &global.user_agent->dlg_list) {
- printf("%c", (dlg==global.cur_dlg ? '*' : ' '));
- print_dialog(dlg);
- dlg = dlg->next;
- }
-
- puts("");
-}
-
+/* $Id$ */
+/*
+ * Copyright (C) 2003-2006 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
+ */
+
+/*
+ * THIS FILE IS INCLUDED BY main.c.
+ * IT WON'T COMPILE BY ITSELF.
+ */
+
+#include "getopt.h"
+#include <stdio.h>
+
+
+/*
+ * Display program usage
+ */
+static void usage()
+{
+ puts("Usage:");
+ puts(" pjsua [options] [sip-url]");
+ puts("");
+ puts(" [sip-url] Default URL to invite.");
+ puts("");
+ puts("General options:");
+ puts(" --config-file=file Read the config/arguments from file.");
+ puts(" --log-file=fname Log to filename (default stderr)");
+ puts(" --log-level=N Set log max level to N (0(none) to 6(trace))");
+ puts(" --app-log-level=N Set log max level for stdout display to N");
+ puts(" --help Display this help screen");
+ puts(" --version Display version info");
+ puts("");
+ puts("Media options:");
+ puts(" --null-audio Use NULL audio device");
+ puts("");
+ puts("User Agent options:");
+ puts(" --auto-answer=sec Auto-answer all incoming calls after sec seconds.");
+ puts(" --auto-hangup=sec Auto-hangup all calls after sec seconds.");
+ puts("");
+ puts("SIP options:");
+ puts(" --local-port=port Set TCP/UDP port");
+ puts(" --id=url Set the URL of local ID (used in From header)");
+ puts(" --contact=url Override the Contact information");
+ puts(" --proxy=url Set the URL of proxy server");
+ puts(" --outbound=url Set the URL of outbound proxy server");
+ puts(" --registrar=url Set the URL of registrar server");
+ puts(" --reg-timeout=secs Set registration interval to secs (default 3600)");
+ puts("");
+ puts("Authentication options:");
+ puts(" --realm=string Set realm");
+ puts(" --username=string Set authentication username");
+ puts(" --password=string Set authentication password");
+ puts("");
+ puts("STUN options (all must be specified):");
+ puts(" --use-stun1=host[:port]");
+ puts(" --use-stun2=host[:port] Use STUN and set host name and port of STUN servers");
+ puts("");
+ puts("SIMPLE options (may be specified more than once):");
+ puts(" --add-buddy url Add the specified URL to the buddy list.");
+ puts(" --offer-x-ms-msg Offer \"x-ms-message\" in outgoing INVITE");
+ puts(" --no-presence Do not subscribe presence of buddies");
+ puts("");
+ fflush(stdout);
+}
+
+/* Display keystroke help. */
+static void keystroke_help()
+{
+ int i;
+
+ printf("Advertise status as: %s\n", (global.hide_status ? "Offline" : "Online"));
+ puts("");
+ puts("Buddy list:");
+ puts("-------------------------------------------------------------------------------");
+ for (i=0; i<global.buddy_cnt; ++i) {
+ printf(" %d\t%s <%s>\n", i+1, global.buddy[i].ptr,
+ (global.buddy_status[i]?"Online":"Offline"));
+ }
+ //printf("-------------------------------------\n");
+ puts("");
+ //puts("Commands:");
+ puts("+=============================================================================+");
+ puts("| Call Commands: | IM & Presence: | Misc: |");
+ puts("| | | |");
+ puts("| m Make new call | i Send IM | o Send OPTIONS |");
+ puts("| a Answer call | su Subscribe presence | d Dump status |");
+ puts("| h Hangup call | us Unsubscribe presence | d1 Dump detailed |");
+ puts("| ] Select next dialog | t Toggle Online status | |");
+ puts("| [ Select previous dialog | | |");
+ puts("+-----------------------------------------------------------------------------+");
+ puts("| q QUIT |");
+ puts("+=============================================================================+");
+ puts("");
+
+
+ fflush(stdout);
+}
+
+/*
+ * Verify that valid SIP url is given.
+ */
+static pj_status_t verify_sip_url(char *url)
+{
+ pjsip_uri *p;
+ pj_pool_t *pool;
+ int len = (url ? strlen(url) : 0);
+
+ if (!len) return -1;
+
+ pool = pj_pool_create(global.pf, "check%p", 1024, 0, NULL);
+ if (!pool) return -1;
+
+ p = pjsip_parse_uri(pool, url, len, 0);
+ if (!p || pj_stricmp2(pjsip_uri_get_scheme(p), "sip") != 0)
+ p = NULL;
+
+ pj_pool_release(pool);
+ return p ? 0 : -1;
+}
+
+/*
+ * Read command arguments from config file.
+ */
+static int read_config_file(pj_pool_t *pool, const char *filename,
+ int *app_argc, char ***app_argv)
+{
+ int i;
+ FILE *fhnd;
+ char line[200];
+ int argc = 0;
+ char **argv;
+ enum { MAX_ARGS = 64 };
+
+ /* Allocate MAX_ARGS+1 (argv needs to be terminated with NULL argument) */
+ argv = pj_pool_calloc(pool, MAX_ARGS+1, sizeof(char*));
+ argv[argc++] = *app_argv[0];
+
+ /* Open config file. */
+ fhnd = fopen(filename, "rt");
+ if (!fhnd) {
+ printf("Unable to open config file %s\n", filename);
+ return -1;
+ }
+
+ /* Scan tokens in the file. */
+ while (argc < MAX_ARGS && !feof(fhnd)) {
+ char *token, *p = line;
+
+ if (fgets(line, sizeof(line), fhnd) == NULL) break;
+
+ for (token = strtok(p, " \t\r\n"); argc < MAX_ARGS;
+ token = strtok(NULL, " \t\r\n"))
+ {
+ int token_len;
+
+ if (!token) break;
+ if (*token == '#') break;
+
+ token_len = strlen(token);
+ if (!token_len)
+ continue;
+ argv[argc] = pj_pool_alloc(pool, token_len+1);
+ pj_memcpy(argv[argc], token, token_len+1);
+ ++argc;
+ }
+ }
+
+ /* Copy arguments from command line */
+ for (i=1; i<*app_argc && argc < MAX_ARGS; ++i)
+ argv[argc++] = (*app_argv)[i];
+
+ if (argc == MAX_ARGS && (i!=*app_argc || !feof(fhnd))) {
+ printf("Too many arguments specified in cmd line/config file\n");
+ fclose(fhnd);
+ return -1;
+ }
+
+ fclose(fhnd);
+
+ /* Assign the new command line back to the original command line. */
+ *app_argc = argc;
+ *app_argv = argv;
+ return 0;
+
+}
+
+/*
+ * Parse program arguments
+ */
+static int parse_args(pj_pool_t *pool, int argc, char *argv[])
+{
+ int c;
+ int option_index;
+ enum { OPT_CONFIG_FILE, OPT_LOG_FILE, OPT_LOG_LEVEL, OPT_APP_LOG_LEVEL,
+ OPT_HELP, OPT_VERSION, OPT_NULL_AUDIO,
+ OPT_LOCAL_PORT, OPT_PROXY, OPT_OUTBOUND_PROXY, OPT_REGISTRAR,
+ OPT_REG_TIMEOUT, OPT_ID, OPT_CONTACT,
+ OPT_REALM, OPT_USERNAME, OPT_PASSWORD,
+ OPT_USE_STUN1, OPT_USE_STUN2,
+ OPT_ADD_BUDDY, OPT_OFFER_X_MS_MSG, OPT_NO_PRESENCE,
+ OPT_AUTO_ANSWER, OPT_AUTO_HANGUP};
+ struct option long_options[] = {
+ { "config-file",1, 0, OPT_CONFIG_FILE},
+ { "log-file", 1, 0, OPT_LOG_FILE},
+ { "log-level", 1, 0, OPT_LOG_LEVEL},
+ { "app-log-level",1,0,OPT_APP_LOG_LEVEL},
+ { "help", 0, 0, OPT_HELP},
+ { "version", 0, 0, OPT_VERSION},
+ { "null-audio", 0, 0, OPT_NULL_AUDIO},
+ { "local-port", 1, 0, OPT_LOCAL_PORT},
+ { "proxy", 1, 0, OPT_PROXY},
+ { "outbound", 1, 0, OPT_OUTBOUND_PROXY},
+ { "registrar", 1, 0, OPT_REGISTRAR},
+ { "reg-timeout",1, 0, OPT_REG_TIMEOUT},
+ { "id", 1, 0, OPT_ID},
+ { "contact", 1, 0, OPT_CONTACT},
+ { "realm", 1, 0, OPT_REALM},
+ { "username", 1, 0, OPT_USERNAME},
+ { "password", 1, 0, OPT_PASSWORD},
+ { "use-stun1", 1, 0, OPT_USE_STUN1},
+ { "use-stun2", 1, 0, OPT_USE_STUN2},
+ { "add-buddy", 1, 0, OPT_ADD_BUDDY},
+ { "offer-x-ms-msg",0,0,OPT_OFFER_X_MS_MSG},
+ { "no-presence", 0, 0, OPT_NO_PRESENCE},
+ { "auto-answer",1, 0, OPT_AUTO_ANSWER},
+ { "auto-hangup",1, 0, OPT_AUTO_HANGUP},
+ { NULL, 0, 0, 0}
+ };
+ char *config_file = NULL;
+
+ /* Run getopt once to see if user specifies config file to read. */
+ while ((c=getopt_long(argc, argv, "", long_options, &option_index)) != -1) {
+ switch (c) {
+ case 0:
+ config_file = optarg;
+ break;
+ }
+ if (config_file)
+ break;
+ }
+
+ if (config_file) {
+ if (read_config_file(pool, config_file, &argc, &argv) != 0)
+ return -1;
+ }
+
+ /* Reinitialize and re-run getopt again, possibly with new arguments
+ * read from config file.
+ */
+ optind = 0;
+ while ((c=getopt_long(argc, argv, "", long_options, &option_index)) != -1) {
+ char *err, *p;
+
+ switch (c) {
+ case OPT_LOG_FILE:
+ global.log_filename = optarg;
+ break;
+ case OPT_LOG_LEVEL:
+ c = strtoul(optarg, &err, 10);
+ if (*err) {
+ printf("Error: expecting integer value 0-6 for --log-level\n");
+ return -1;
+ }
+ pj_log_set_level( c );
+ break;
+ case OPT_APP_LOG_LEVEL:
+ global.app_log_level = strtoul(optarg, &err, 10);
+ if (*err) {
+ printf("Error: expecting integer value 0-6 for --app-log-level\n");
+ return -1;
+ }
+ break;
+ case OPT_HELP:
+ usage();
+ return -1;
+ case OPT_VERSION: /* version */
+ pj_dump_config();
+ return -1;
+ case OPT_NULL_AUDIO:
+ global.null_audio = 1;
+ break;
+ case OPT_LOCAL_PORT: /* local-port */
+ global.sip_port = strtoul(optarg, &err, 10);
+ if (*err) {
+ printf("Error: expecting integer value for --local-port\n");
+ return -1;
+ }
+ break;
+ case OPT_PROXY: /* proxy */
+ if (verify_sip_url(optarg) != 0) {
+ printf("Error: invalid SIP URL '%s' in proxy argument\n", optarg);
+ return -1;
+ }
+ global.proxy = pj_str(optarg);
+ break;
+ case OPT_OUTBOUND_PROXY: /* outbound proxy */
+ if (verify_sip_url(optarg) != 0) {
+ printf("Error: invalid SIP URL '%s' in outbound proxy argument\n", optarg);
+ return -1;
+ }
+ global.outbound_proxy = pj_str(optarg);
+ break;
+ case OPT_REGISTRAR: /* registrar */
+ if (verify_sip_url(optarg) != 0) {
+ printf("Error: invalid SIP URL '%s' in registrar argument\n", optarg);
+ return -1;
+ }
+ global.registrar_uri = pj_str(optarg);
+ break;
+ case OPT_REG_TIMEOUT: /* reg-timeout */
+ global.reg_timeout = strtoul(optarg, &err, 10);
+ if (*err) {
+ printf("Error: expecting integer value for --reg-timeout\n");
+ return -1;
+ }
+ break;
+ case OPT_ID: /* id */
+ if (verify_sip_url(optarg) != 0) {
+ printf("Error: invalid SIP URL '%s' in local id argument\n", optarg);
+ return -1;
+ }
+ global.local_uri = pj_str(optarg);
+ break;
+ case OPT_CONTACT: /* contact */
+ if (verify_sip_url(optarg) != 0) {
+ printf("Error: invalid SIP URL '%s' in contact argument\n", optarg);
+ return -1;
+ }
+ global.contact = pj_str(optarg);
+ break;
+ case OPT_USERNAME: /* Default authentication user */
+ if (!global.cred_count) global.cred_count = 1;
+ global.cred_info[0].username = pj_str(optarg);
+ break;
+ case OPT_REALM: /* Default authentication realm. */
+ if (!global.cred_count) global.cred_count = 1;
+ global.cred_info[0].realm = pj_str(optarg);
+ break;
+ case OPT_PASSWORD: /* authentication password */
+ if (!global.cred_count) global.cred_count = 1;
+ global.cred_info[0].data_type = 0;
+ global.cred_info[0].data = pj_str(optarg);
+ break;
+ case OPT_USE_STUN1: /* STUN server 1 */
+ p = pj_native_strchr(optarg, ':');
+ if (p) {
+ *p = '\0';
+ global.stun_srv1 = pj_str(optarg);
+ global.stun_port1 = strtoul(p+1, &err, 10);
+ if (*err || global.stun_port1==0) {
+ printf("Error: expecting port number with option --use-stun1\n");
+ return -1;
+ }
+ } else {
+ global.stun_port1 = 3478;
+ global.stun_srv1 = pj_str(optarg);
+ }
+ break;
+ case OPT_USE_STUN2: /* STUN server 2 */
+ p = pj_native_strchr(optarg, ':');
+ if (p) {
+ *p = '\0';
+ global.stun_srv2 = pj_str(optarg);
+ global.stun_port2 = strtoul(p+1, &err, 10);
+ if (*err || global.stun_port2==0) {
+ printf("Error: expecting port number with option --use-stun2\n");
+ return -1;
+ }
+ } else {
+ global.stun_port2 = 3478;
+ global.stun_srv2 = pj_str(optarg);
+ }
+ break;
+ case OPT_ADD_BUDDY: /* Add to buddy list. */
+ if (verify_sip_url(optarg) != 0) {
+ printf("Error: invalid URL '%s' in --add-buddy option\n", optarg);
+ return -1;
+ }
+ if (global.buddy_cnt == MAX_BUDDIES) {
+ printf("Error: too many buddies in buddy list.\n");
+ return -1;
+ }
+ global.buddy[global.buddy_cnt++] = pj_str(optarg);
+ break;
+ case OPT_OFFER_X_MS_MSG:
+ global.offer_x_ms_msg = 1;
+ break;
+ case OPT_NO_PRESENCE:
+ global.no_presence = 1;
+ break;
+ case OPT_AUTO_ANSWER:
+ global.auto_answer = strtoul(optarg, &err, 10);
+ if (*err) {
+ printf("Error: expecting integer value for --auto-answer option\n");
+ return -1;
+ }
+ break;
+ case OPT_AUTO_HANGUP:
+ global.auto_hangup = strtoul(optarg, &err, 10);
+ if (*err) {
+ printf("Error: expecting integer value for --auto-hangup option\n");
+ return -1;
+ }
+ break;
+ }
+ }
+
+ if (optind != argc) {
+ printf("Error: unknown options %s\n", argv[optind]);
+ return -1;
+ }
+
+ if (global.reg_timeout == 0)
+ global.reg_timeout = 3600;
+
+ return 0;
+}
+
+/* Print dialog. */
+static void print_dialog(pjsip_dlg *dlg)
+{
+ if (!dlg) {
+ puts("none");
+ return;
+ }
+
+ printf("%s: call-id=%.*s", dlg->obj_name,
+ (int)dlg->call_id->id.slen,
+ dlg->call_id->id.ptr);
+
+ printf(" (%s, %s)\n", pjsip_role_name(dlg->role),
+ pjsip_dlg_state_str(dlg->state));
+}
+
+/* Dump media statistic */
+void dump_media_statistic(pjsip_dlg *dlg)
+{
+ struct dialog_data *dlg_data = dlg->user_data;
+ pj_media_stream_stat stat[2];
+ const char *statname[2] = { "TX", "RX" };
+ int i;
+
+ pj_media_session_get_stat (dlg_data->msession, 0, &stat[0], &stat[1]);
+
+ printf("Media statistic:\n");
+ for (i=0; i<2; ++i) {
+ printf(" %s statistics:\n", statname[i]);
+ printf(" Pkt TX=%d RX=%d\n", stat[i].pkt_tx, stat[i].pkt_rx);
+ printf(" Octets TX=%d RX=%d\n", stat[i].oct_tx, stat[i].oct_rx);
+ printf(" Jitter %d ms\n", stat[i].jitter);
+ printf(" Pkt lost %d\n", stat[i].pkt_lost);
+ }
+ printf("\n");
+}
+
+/* Print all dialogs. */
+static void print_all_dialogs()
+{
+ pjsip_dlg *dlg = (pjsip_dlg *)global.user_agent->dlg_list.next;
+
+ puts("List all dialogs:");
+
+ while (dlg != (pjsip_dlg *) &global.user_agent->dlg_list) {
+ printf("%c", (dlg==global.cur_dlg ? '*' : ' '));
+ print_dialog(dlg);
+ dlg = dlg->next;
+ }
+
+ puts("");
+}
+
diff --git a/pjsip/src/test-pjsip/main.c b/pjsip/src/test-pjsip/main.c
index 3c819be8..3e5270b1 100644
--- a/pjsip/src/test-pjsip/main.c
+++ b/pjsip/src/test-pjsip/main.c
@@ -1,24 +1,24 @@
-/* $Id$ */
-/*
- * Copyright (C) 2003-2006 Benny Prijono <benny@prijono.org>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
-#include "test.h"
-
-int main(void)
-{
- return test_main();
-}
+/* $Id$ */
+/*
+ * Copyright (C) 2003-2006 Benny Prijono <benny@prijono.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#include "test.h"
+
+int main(void)
+{
+ return test_main();
+}
diff --git a/pjsip/src/test-pjsip/msg.c b/pjsip/src/test-pjsip/msg.c
index b546a756..18e5af16 100644
--- a/pjsip/src/test-pjsip/msg.c
+++ b/pjsip/src/test-pjsip/msg.c
@@ -1,369 +1,369 @@
-/* $Id$ */
-/*
- * Copyright (C) 2003-2006 Benny Prijono <benny@prijono.org>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
-#include "test.h"
-#include <pjsip_core.h>
-#include <pjlib.h>
-
-static pjsip_msg *create_msg0(pj_pool_t *pool);
-
-struct test_msg
-{
- char msg[1024];
- pjsip_msg *(*creator)(pj_pool_t *pool);
- pj_size_t len;
-} test_array[] =
-{
- {
- /* 'Normal' message with all headers. */
- "INVITE sip:user@foo SIP/2.0\n"
- "From: Hi I'm Joe <sip:joe.user@bar.otherdomain.com>;tag=1234578901234567890\r"
- "To: Fellow User <sip:user@foo.bar.domain.com>\r\n"
- "Call-ID: 12345678901234567890@bar\r\n"
- "Content-Length: 0\r\n"
- "CSeq: 123456 INVITE\n"
- "Contact: <sip:joe@bar> ; q=0.5;expires=3600,sip:user@host;q=0.500\r"
- " ,sip:user2@host2\n"
- "Content-Type: text/html ; charset=ISO-8859-4\r"
- "Route: <sip:bigbox3.site3.atlanta.com;lr>,\r\n"
- " <sip:server10.biloxi.com;lr>\r"
- "Record-Route: <sip:server10.biloxi.com>,\r\n"
- " <sip:bigbox3.site3.atlanta.com;lr>\n"
- "Via: SIP/2.0/SCTP bigbox3.site3.atlanta.com;branch=z9hG4bK77ef4c2312983.1\n"
- "Via: SIP/2.0/UDP pc33.atlanta.com;branch=z9hG4bKnashds8\n"
- " ;received=192.0.2.1\r\n"
- "Via: SIP/2.0/UDP 10.2.1.1, SIP/2.0/TCP 192.168.1.1\n"
- "Organization: \r"
- "Max-Forwards: 70\n"
- "X-Header: \r\n"
- "\r",
- &create_msg0
- }
-};
-
-static pj_uint32_t parse_len, parse_time, print_time;
-
-static pj_status_t test_entry( pj_pool_t *pool, struct test_msg *entry )
-{
- pjsip_msg *parsed_msg, *ref_msg;
- pj_status_t status = PJ_SUCCESS;
- int len;
- pj_str_t str1, str2;
- pjsip_hdr *hdr1, *hdr2;
- pj_timestamp t1, t2;
- char *msgbuf;
-
- enum { BUFLEN = 512 };
-
- /* Parse message. */
- parse_len += entry->len;
- pj_get_timestamp(&t1);
- parsed_msg = pjsip_parse_msg(pool, entry->msg, entry->len, NULL);
- if (parsed_msg == NULL) {
- status = -10;
- goto on_return;
- }
- pj_get_timestamp(&t2);
- parse_time += t2.u32.lo - t1.u32.lo;
-
- /* Create reference message. */
- ref_msg = entry->creator(pool);
-
- /* Create buffer for comparison. */
- str1.ptr = pj_pool_alloc(pool, BUFLEN);
- str2.ptr = pj_pool_alloc(pool, BUFLEN);
-
- /* Compare message type. */
- if (parsed_msg->type != ref_msg->type) {
- status = -20;
- goto on_return;
- }
-
- /* Compare request or status line. */
- if (parsed_msg->type == PJSIP_REQUEST_MSG) {
- pjsip_method *m1 = &parsed_msg->line.req.method;
- pjsip_method *m2 = &ref_msg->line.req.method;
-
- if (m1->id != m2->id || pj_strcmp(&m1->name, &m2->name)) {
- status = -30;
- goto on_return;
- }
- } else {
-
- }
-
- /* Compare headers. */
- hdr1 = parsed_msg->hdr.next;
- hdr2 = ref_msg->hdr.next;
-
- while (hdr1 != &parsed_msg->hdr && hdr2 != &ref_msg->hdr) {
- len = hdr1->vptr->print_on(hdr1, str1.ptr, BUFLEN);
- if (len < 1) {
- status = -40;
- goto on_return;
- }
- str1.slen = len;
-
- len = hdr2->vptr->print_on(hdr2, str2.ptr, BUFLEN);
- if (len < 1) {
- status = -50;
- goto on_return;
- }
- str2.slen = len;
-
- if (pj_strcmp(&str1, &str2) != 0) {
- status = -60;
- goto on_return;
- }
-
- hdr1 = hdr1->next;
- hdr2 = hdr2->next;
- }
-
- if (hdr1 != &parsed_msg->hdr || hdr2 != &ref_msg->hdr) {
- status = -70;
- goto on_return;
- }
-
- /* Print message. */
- msgbuf = pj_pool_alloc(pool, PJSIP_MAX_PKT_LEN);
- if (msgbuf == NULL) {
- status = -80;
- goto on_return;
- }
- pj_get_timestamp(&t1);
- len = pjsip_msg_print(parsed_msg, msgbuf, PJSIP_MAX_PKT_LEN);
- if (len < 1) {
- status = -90;
- goto on_return;
- }
- pj_get_timestamp(&t2);
- print_time += t2.u32.lo - t1.u32.lo;
- status = PJ_SUCCESS;
-
-on_return:
- return status;
-}
-
-
-pj_status_t msg_test(void)
-{
- pj_status_t status;
- pj_pool_t *pool;
-
- pool = pjsip_endpt_create_pool(endpt, NULL, 4000, 4000);
-
- status = test_entry( pool, &test_array[0] );
-
- pjsip_endpt_destroy(endpt);
- return status;
-}
-
-/*****************************************************************************/
-
-static pjsip_msg *create_msg0(pj_pool_t *pool)
-{
-
- pjsip_msg *msg;
- pjsip_name_addr *name_addr;
- pjsip_url *url;
- pjsip_fromto_hdr *fromto;
- pjsip_cid_hdr *cid;
- pjsip_clen_hdr *clen;
- pjsip_cseq_hdr *cseq;
- pjsip_contact_hdr *contact;
- pjsip_ctype_hdr *ctype;
- pjsip_routing_hdr *routing;
- pjsip_via_hdr *via;
- pjsip_generic_string_hdr *generic;
- pj_str_t str;
-
- msg = pjsip_msg_create(pool, PJSIP_REQUEST_MSG);
-
- /* "INVITE sip:user@foo SIP/2.0\n" */
- pjsip_method_set(&msg->line.req.method, PJSIP_INVITE_METHOD);
- url = pjsip_url_create(pool, 0);
- msg->line.req.uri = (pjsip_uri*)url;
- pj_strdup2(pool, &url->user, "user");
- pj_strdup2(pool, &url->host, "foo");
-
- /* "From: Hi I'm Joe <sip:joe.user@bar.otherdomain.com>;tag=1234578901234567890\r" */
- fromto = pjsip_from_hdr_create(pool);
- pjsip_msg_add_hdr(msg, (pjsip_hdr*)fromto);
- pj_strdup2(pool, &fromto->tag, "1234578901234567890");
- name_addr = pjsip_name_addr_create(pool);
- fromto->uri = (pjsip_uri*)name_addr;
- pj_strdup2(pool, &name_addr->display, "Hi I'm Joe");
- url = pjsip_url_create(pool, 0);
- name_addr->uri = (pjsip_uri*)url;
- pj_strdup2(pool, &url->user, "joe.user");
- pj_strdup2(pool, &url->host, "bar.otherdomain.com");
-
- /* "To: Fellow User <sip:user@foo.bar.domain.com>\r\n" */
- fromto = pjsip_to_hdr_create(pool);
- pjsip_msg_add_hdr(msg, (pjsip_hdr*)fromto);
- name_addr = pjsip_name_addr_create(pool);
- fromto->uri = (pjsip_uri*)name_addr;
- pj_strdup2(pool, &name_addr->display, "Fellow User");
- url = pjsip_url_create(pool, 0);
- name_addr->uri = (pjsip_uri*)url;
- pj_strdup2(pool, &url->user, "user");
- pj_strdup2(pool, &url->host, "foo.bar.domain.com");
-
- /* "Call-ID: 12345678901234567890@bar\r\n" */
- cid = pjsip_cid_hdr_create(pool);
- pjsip_msg_add_hdr(msg, (pjsip_hdr*)cid);
- pj_strdup2(pool, &cid->id, "12345678901234567890@bar");
-
- /* "Content-Length: 0\r\n" */
- clen = pjsip_clen_hdr_create(pool);
- pjsip_msg_add_hdr(msg, (pjsip_hdr*)clen);
- clen->len = 0;
-
- /* "CSeq: 123456 INVITE\n" */
- cseq = pjsip_cseq_hdr_create(pool);
- pjsip_msg_add_hdr(msg, (pjsip_hdr*)cseq);
- cseq->cseq = 123456;
- pjsip_method_set(&cseq->method, PJSIP_INVITE_METHOD);
-
- /* "Contact: <sip:joe@bar>;q=0.5;expires=3600*/
- contact = pjsip_contact_hdr_create(pool);
- pjsip_msg_add_hdr(msg, (pjsip_hdr*)contact);
- contact->q1000 = 500;
- contact->expires = 3600;
- name_addr = pjsip_name_addr_create(pool);
- contact->uri = (pjsip_uri*)name_addr;
- url = pjsip_url_create(pool, 0);
- name_addr->uri = (pjsip_uri*)url;
- pj_strdup2(pool, &url->user, "joe");
- pj_strdup2(pool, &url->host, "bar");
-
- /*, sip:user@host;q=0.500\r" */
- contact = pjsip_contact_hdr_create(pool);
- pjsip_msg_add_hdr(msg, (pjsip_hdr*)contact);
- contact->q1000 = 500;
- url = pjsip_url_create(pool, 0);
- contact->uri = (pjsip_uri*)url;
- pj_strdup2(pool, &url->user, "user");
- pj_strdup2(pool, &url->host, "host");
-
- /* " ,sip:user2@host2\n" */
- contact = pjsip_contact_hdr_create(pool);
- pjsip_msg_add_hdr(msg, (pjsip_hdr*)contact);
- url = pjsip_url_create(pool, 0);
- contact->uri = (pjsip_uri*)url;
- pj_strdup2(pool, &url->user, "user2");
- pj_strdup2(pool, &url->host, "host2");
-
- /* "Content-Type: text/html; charset=ISO-8859-4\r" */
- ctype = pjsip_ctype_hdr_create(pool);
- pjsip_msg_add_hdr(msg, (pjsip_hdr*)ctype);
- pj_strdup2(pool, &ctype->media.type, "text");
- pj_strdup2(pool, &ctype->media.subtype, "html");
- pj_strdup2(pool, &ctype->media.param, ";charset=ISO-8859-4");
-
- /* "Route: <sip:bigbox3.site3.atlanta.com;lr>,\r\n" */
- routing = pjsip_route_hdr_create(pool);
- pjsip_msg_add_hdr(msg, (pjsip_hdr*)routing);
- url = pjsip_url_create(pool, 0);
- routing->name_addr.uri = (pjsip_uri*)url;
- pj_strdup2(pool, &url->host, "bigbox3.site3.atlanta.com");
- url->lr_param = 1;
-
- /* " <sip:server10.biloxi.com;lr>\r" */
- routing = pjsip_route_hdr_create(pool);
- pjsip_msg_add_hdr(msg, (pjsip_hdr*)routing);
- url = pjsip_url_create(pool, 0);
- routing->name_addr.uri = (pjsip_uri*)url;
- pj_strdup2(pool, &url->host, "server10.biloxi.com");
- url->lr_param = 1;
-
- /* "Record-Route: <sip:server10.biloxi.com>,\r\n" */
- routing = pjsip_rr_hdr_create(pool);
- pjsip_msg_add_hdr(msg, (pjsip_hdr*)routing);
- url = pjsip_url_create(pool, 0);
- routing->name_addr.uri = (pjsip_uri*)url;
- pj_strdup2(pool, &url->host, "server10.biloxi.com");
- url->lr_param = 0;
-
- /* " <sip:bigbox3.site3.atlanta.com;lr>\n" */
- routing = pjsip_rr_hdr_create(pool);
- pjsip_msg_add_hdr(msg, (pjsip_hdr*)routing);
- url = pjsip_url_create(pool, 0);
- routing->name_addr.uri = (pjsip_uri*)url;
- pj_strdup2(pool, &url->host, "bigbox3.site3.atlanta.com");
- url->lr_param = 1;
-
- /* "Via: SIP/2.0/SCTP bigbox3.site3.atlanta.com;branch=z9hG4bK77ef4c2312983.1\n" */
- via = pjsip_via_hdr_create(pool);
- pjsip_msg_add_hdr(msg, (pjsip_hdr*)via);
- pj_strdup2(pool, &via->transport, "SCTP");
- pj_strdup2(pool, &via->sent_by.host, "bigbox3.site3.atlanta.com");
- pj_strdup2(pool, &via->branch_param, "z9hG4bK77ef4c2312983.1");
-
- /* "Via: SIP/2.0/UDP pc33.atlanta.com;branch=z9hG4bKnashds8\n"
- " ;received=192.0.2.1\r\n" */
- via = pjsip_via_hdr_create(pool);
- pjsip_msg_add_hdr(msg, (pjsip_hdr*)via);
- pj_strdup2(pool, &via->transport, "UDP");
- pj_strdup2(pool, &via->sent_by.host, "pc33.atlanta.com");
- pj_strdup2(pool, &via->branch_param, "z9hG4bKnashds8");
- pj_strdup2(pool, &via->recvd_param, "192.0.2.1");
-
-
- /* "Via: SIP/2.0/UDP 10.2.1.1, */
- via = pjsip_via_hdr_create(pool);
- pjsip_msg_add_hdr(msg, (pjsip_hdr*)via);
- pj_strdup2(pool, &via->transport, "UDP");
- pj_strdup2(pool, &via->sent_by.host, "10.2.1.1");
-
-
- /*SIP/2.0/TCP 192.168.1.1\n" */
- via = pjsip_via_hdr_create(pool);
- pjsip_msg_add_hdr(msg, (pjsip_hdr*)via);
- pj_strdup2(pool, &via->transport, "TCP");
- pj_strdup2(pool, &via->sent_by.host, "192.168.1.1");
-
- /* "Organization: \r" */
- str.ptr = "Organization";
- str.slen = 12;
- generic = pjsip_generic_string_hdr_create(pool, &str);
- pjsip_msg_add_hdr(msg, (pjsip_hdr*)generic);
- generic->hvalue.ptr = NULL;
- generic->hvalue.slen = 0;
-
- /* "Max-Forwards: 70\n" */
- str.ptr = "Max-Forwards";
- str.slen = 12;
- generic = pjsip_generic_string_hdr_create(pool, &str);
- pjsip_msg_add_hdr(msg, (pjsip_hdr*)generic);
- str.ptr = "70";
- str.slen = 2;
- generic->hvalue = str;
-
- /* "X-Header: \r\n" */
- str.ptr = "X-Header";
- str.slen = 8;
- generic = pjsip_generic_string_hdr_create(pool, &str);
- pjsip_msg_add_hdr(msg, (pjsip_hdr*)generic);
- str.ptr = NULL;
- str.slen = 0;
- generic->hvalue = str;
-
- return msg;
-}
+/* $Id$ */
+/*
+ * Copyright (C) 2003-2006 Benny Prijono <benny@prijono.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#include "test.h"
+#include <pjsip_core.h>
+#include <pjlib.h>
+
+static pjsip_msg *create_msg0(pj_pool_t *pool);
+
+struct test_msg
+{
+ char msg[1024];
+ pjsip_msg *(*creator)(pj_pool_t *pool);
+ pj_size_t len;
+} test_array[] =
+{
+ {
+ /* 'Normal' message with all headers. */
+ "INVITE sip:user@foo SIP/2.0\n"
+ "From: Hi I'm Joe <sip:joe.user@bar.otherdomain.com>;tag=1234578901234567890\r"
+ "To: Fellow User <sip:user@foo.bar.domain.com>\r\n"
+ "Call-ID: 12345678901234567890@bar\r\n"
+ "Content-Length: 0\r\n"
+ "CSeq: 123456 INVITE\n"
+ "Contact: <sip:joe@bar> ; q=0.5;expires=3600,sip:user@host;q=0.500\r"
+ " ,sip:user2@host2\n"
+ "Content-Type: text/html ; charset=ISO-8859-4\r"
+ "Route: <sip:bigbox3.site3.atlanta.com;lr>,\r\n"
+ " <sip:server10.biloxi.com;lr>\r"
+ "Record-Route: <sip:server10.biloxi.com>,\r\n"
+ " <sip:bigbox3.site3.atlanta.com;lr>\n"
+ "Via: SIP/2.0/SCTP bigbox3.site3.atlanta.com;branch=z9hG4bK77ef4c2312983.1\n"
+ "Via: SIP/2.0/UDP pc33.atlanta.com;branch=z9hG4bKnashds8\n"
+ " ;received=192.0.2.1\r\n"
+ "Via: SIP/2.0/UDP 10.2.1.1, SIP/2.0/TCP 192.168.1.1\n"
+ "Organization: \r"
+ "Max-Forwards: 70\n"
+ "X-Header: \r\n"
+ "\r",
+ &create_msg0
+ }
+};
+
+static pj_uint32_t parse_len, parse_time, print_time;
+
+static pj_status_t test_entry( pj_pool_t *pool, struct test_msg *entry )
+{
+ pjsip_msg *parsed_msg, *ref_msg;
+ pj_status_t status = PJ_SUCCESS;
+ int len;
+ pj_str_t str1, str2;
+ pjsip_hdr *hdr1, *hdr2;
+ pj_timestamp t1, t2;
+ char *msgbuf;
+
+ enum { BUFLEN = 512 };
+
+ /* Parse message. */
+ parse_len += entry->len;
+ pj_get_timestamp(&t1);
+ parsed_msg = pjsip_parse_msg(pool, entry->msg, entry->len, NULL);
+ if (parsed_msg == NULL) {
+ status = -10;
+ goto on_return;
+ }
+ pj_get_timestamp(&t2);
+ parse_time += t2.u32.lo - t1.u32.lo;
+
+ /* Create reference message. */
+ ref_msg = entry->creator(pool);
+
+ /* Create buffer for comparison. */
+ str1.ptr = pj_pool_alloc(pool, BUFLEN);
+ str2.ptr = pj_pool_alloc(pool, BUFLEN);
+
+ /* Compare message type. */
+ if (parsed_msg->type != ref_msg->type) {
+ status = -20;
+ goto on_return;
+ }
+
+ /* Compare request or status line. */
+ if (parsed_msg->type == PJSIP_REQUEST_MSG) {
+ pjsip_method *m1 = &parsed_msg->line.req.method;
+ pjsip_method *m2 = &ref_msg->line.req.method;
+
+ if (m1->id != m2->id || pj_strcmp(&m1->name, &m2->name)) {
+ status = -30;
+ goto on_return;
+ }
+ } else {
+
+ }
+
+ /* Compare headers. */
+ hdr1 = parsed_msg->hdr.next;
+ hdr2 = ref_msg->hdr.next;
+
+ while (hdr1 != &parsed_msg->hdr && hdr2 != &ref_msg->hdr) {
+ len = hdr1->vptr->print_on(hdr1, str1.ptr, BUFLEN);
+ if (len < 1) {
+ status = -40;
+ goto on_return;
+ }
+ str1.slen = len;
+
+ len = hdr2->vptr->print_on(hdr2, str2.ptr, BUFLEN);
+ if (len < 1) {
+ status = -50;
+ goto on_return;
+ }
+ str2.slen = len;
+
+ if (pj_strcmp(&str1, &str2) != 0) {
+ status = -60;
+ goto on_return;
+ }
+
+ hdr1 = hdr1->next;
+ hdr2 = hdr2->next;
+ }
+
+ if (hdr1 != &parsed_msg->hdr || hdr2 != &ref_msg->hdr) {
+ status = -70;
+ goto on_return;
+ }
+
+ /* Print message. */
+ msgbuf = pj_pool_alloc(pool, PJSIP_MAX_PKT_LEN);
+ if (msgbuf == NULL) {
+ status = -80;
+ goto on_return;
+ }
+ pj_get_timestamp(&t1);
+ len = pjsip_msg_print(parsed_msg, msgbuf, PJSIP_MAX_PKT_LEN);
+ if (len < 1) {
+ status = -90;
+ goto on_return;
+ }
+ pj_get_timestamp(&t2);
+ print_time += t2.u32.lo - t1.u32.lo;
+ status = PJ_SUCCESS;
+
+on_return:
+ return status;
+}
+
+
+pj_status_t msg_test(void)
+{
+ pj_status_t status;
+ pj_pool_t *pool;
+
+ pool = pjsip_endpt_create_pool(endpt, NULL, 4000, 4000);
+
+ status = test_entry( pool, &test_array[0] );
+
+ pjsip_endpt_destroy(endpt);
+ return status;
+}
+
+/*****************************************************************************/
+
+static pjsip_msg *create_msg0(pj_pool_t *pool)
+{
+
+ pjsip_msg *msg;
+ pjsip_name_addr *name_addr;
+ pjsip_url *url;
+ pjsip_fromto_hdr *fromto;
+ pjsip_cid_hdr *cid;
+ pjsip_clen_hdr *clen;
+ pjsip_cseq_hdr *cseq;
+ pjsip_contact_hdr *contact;
+ pjsip_ctype_hdr *ctype;
+ pjsip_routing_hdr *routing;
+ pjsip_via_hdr *via;
+ pjsip_generic_string_hdr *generic;
+ pj_str_t str;
+
+ msg = pjsip_msg_create(pool, PJSIP_REQUEST_MSG);
+
+ /* "INVITE sip:user@foo SIP/2.0\n" */
+ pjsip_method_set(&msg->line.req.method, PJSIP_INVITE_METHOD);
+ url = pjsip_url_create(pool, 0);
+ msg->line.req.uri = (pjsip_uri*)url;
+ pj_strdup2(pool, &url->user, "user");
+ pj_strdup2(pool, &url->host, "foo");
+
+ /* "From: Hi I'm Joe <sip:joe.user@bar.otherdomain.com>;tag=1234578901234567890\r" */
+ fromto = pjsip_from_hdr_create(pool);
+ pjsip_msg_add_hdr(msg, (pjsip_hdr*)fromto);
+ pj_strdup2(pool, &fromto->tag, "1234578901234567890");
+ name_addr = pjsip_name_addr_create(pool);
+ fromto->uri = (pjsip_uri*)name_addr;
+ pj_strdup2(pool, &name_addr->display, "Hi I'm Joe");
+ url = pjsip_url_create(pool, 0);
+ name_addr->uri = (pjsip_uri*)url;
+ pj_strdup2(pool, &url->user, "joe.user");
+ pj_strdup2(pool, &url->host, "bar.otherdomain.com");
+
+ /* "To: Fellow User <sip:user@foo.bar.domain.com>\r\n" */
+ fromto = pjsip_to_hdr_create(pool);
+ pjsip_msg_add_hdr(msg, (pjsip_hdr*)fromto);
+ name_addr = pjsip_name_addr_create(pool);
+ fromto->uri = (pjsip_uri*)name_addr;
+ pj_strdup2(pool, &name_addr->display, "Fellow User");
+ url = pjsip_url_create(pool, 0);
+ name_addr->uri = (pjsip_uri*)url;
+ pj_strdup2(pool, &url->user, "user");
+ pj_strdup2(pool, &url->host, "foo.bar.domain.com");
+
+ /* "Call-ID: 12345678901234567890@bar\r\n" */
+ cid = pjsip_cid_hdr_create(pool);
+ pjsip_msg_add_hdr(msg, (pjsip_hdr*)cid);
+ pj_strdup2(pool, &cid->id, "12345678901234567890@bar");
+
+ /* "Content-Length: 0\r\n" */
+ clen = pjsip_clen_hdr_create(pool);
+ pjsip_msg_add_hdr(msg, (pjsip_hdr*)clen);
+ clen->len = 0;
+
+ /* "CSeq: 123456 INVITE\n" */
+ cseq = pjsip_cseq_hdr_create(pool);
+ pjsip_msg_add_hdr(msg, (pjsip_hdr*)cseq);
+ cseq->cseq = 123456;
+ pjsip_method_set(&cseq->method, PJSIP_INVITE_METHOD);
+
+ /* "Contact: <sip:joe@bar>;q=0.5;expires=3600*/
+ contact = pjsip_contact_hdr_create(pool);
+ pjsip_msg_add_hdr(msg, (pjsip_hdr*)contact);
+ contact->q1000 = 500;
+ contact->expires = 3600;
+ name_addr = pjsip_name_addr_create(pool);
+ contact->uri = (pjsip_uri*)name_addr;
+ url = pjsip_url_create(pool, 0);
+ name_addr->uri = (pjsip_uri*)url;
+ pj_strdup2(pool, &url->user, "joe");
+ pj_strdup2(pool, &url->host, "bar");
+
+ /*, sip:user@host;q=0.500\r" */
+ contact = pjsip_contact_hdr_create(pool);
+ pjsip_msg_add_hdr(msg, (pjsip_hdr*)contact);
+ contact->q1000 = 500;
+ url = pjsip_url_create(pool, 0);
+ contact->uri = (pjsip_uri*)url;
+ pj_strdup2(pool, &url->user, "user");
+ pj_strdup2(pool, &url->host, "host");
+
+ /* " ,sip:user2@host2\n" */
+ contact = pjsip_contact_hdr_create(pool);
+ pjsip_msg_add_hdr(msg, (pjsip_hdr*)contact);
+ url = pjsip_url_create(pool, 0);
+ contact->uri = (pjsip_uri*)url;
+ pj_strdup2(pool, &url->user, "user2");
+ pj_strdup2(pool, &url->host, "host2");
+
+ /* "Content-Type: text/html; charset=ISO-8859-4\r" */
+ ctype = pjsip_ctype_hdr_create(pool);
+ pjsip_msg_add_hdr(msg, (pjsip_hdr*)ctype);
+ pj_strdup2(pool, &ctype->media.type, "text");
+ pj_strdup2(pool, &ctype->media.subtype, "html");
+ pj_strdup2(pool, &ctype->media.param, ";charset=ISO-8859-4");
+
+ /* "Route: <sip:bigbox3.site3.atlanta.com;lr>,\r\n" */
+ routing = pjsip_route_hdr_create(pool);
+ pjsip_msg_add_hdr(msg, (pjsip_hdr*)routing);
+ url = pjsip_url_create(pool, 0);
+ routing->name_addr.uri = (pjsip_uri*)url;
+ pj_strdup2(pool, &url->host, "bigbox3.site3.atlanta.com");
+ url->lr_param = 1;
+
+ /* " <sip:server10.biloxi.com;lr>\r" */
+ routing = pjsip_route_hdr_create(pool);
+ pjsip_msg_add_hdr(msg, (pjsip_hdr*)routing);
+ url = pjsip_url_create(pool, 0);
+ routing->name_addr.uri = (pjsip_uri*)url;
+ pj_strdup2(pool, &url->host, "server10.biloxi.com");
+ url->lr_param = 1;
+
+ /* "Record-Route: <sip:server10.biloxi.com>,\r\n" */
+ routing = pjsip_rr_hdr_create(pool);
+ pjsip_msg_add_hdr(msg, (pjsip_hdr*)routing);
+ url = pjsip_url_create(pool, 0);
+ routing->name_addr.uri = (pjsip_uri*)url;
+ pj_strdup2(pool, &url->host, "server10.biloxi.com");
+ url->lr_param = 0;
+
+ /* " <sip:bigbox3.site3.atlanta.com;lr>\n" */
+ routing = pjsip_rr_hdr_create(pool);
+ pjsip_msg_add_hdr(msg, (pjsip_hdr*)routing);
+ url = pjsip_url_create(pool, 0);
+ routing->name_addr.uri = (pjsip_uri*)url;
+ pj_strdup2(pool, &url->host, "bigbox3.site3.atlanta.com");
+ url->lr_param = 1;
+
+ /* "Via: SIP/2.0/SCTP bigbox3.site3.atlanta.com;branch=z9hG4bK77ef4c2312983.1\n" */
+ via = pjsip_via_hdr_create(pool);
+ pjsip_msg_add_hdr(msg, (pjsip_hdr*)via);
+ pj_strdup2(pool, &via->transport, "SCTP");
+ pj_strdup2(pool, &via->sent_by.host, "bigbox3.site3.atlanta.com");
+ pj_strdup2(pool, &via->branch_param, "z9hG4bK77ef4c2312983.1");
+
+ /* "Via: SIP/2.0/UDP pc33.atlanta.com;branch=z9hG4bKnashds8\n"
+ " ;received=192.0.2.1\r\n" */
+ via = pjsip_via_hdr_create(pool);
+ pjsip_msg_add_hdr(msg, (pjsip_hdr*)via);
+ pj_strdup2(pool, &via->transport, "UDP");
+ pj_strdup2(pool, &via->sent_by.host, "pc33.atlanta.com");
+ pj_strdup2(pool, &via->branch_param, "z9hG4bKnashds8");
+ pj_strdup2(pool, &via->recvd_param, "192.0.2.1");
+
+
+ /* "Via: SIP/2.0/UDP 10.2.1.1, */
+ via = pjsip_via_hdr_create(pool);
+ pjsip_msg_add_hdr(msg, (pjsip_hdr*)via);
+ pj_strdup2(pool, &via->transport, "UDP");
+ pj_strdup2(pool, &via->sent_by.host, "10.2.1.1");
+
+
+ /*SIP/2.0/TCP 192.168.1.1\n" */
+ via = pjsip_via_hdr_create(pool);
+ pjsip_msg_add_hdr(msg, (pjsip_hdr*)via);
+ pj_strdup2(pool, &via->transport, "TCP");
+ pj_strdup2(pool, &via->sent_by.host, "192.168.1.1");
+
+ /* "Organization: \r" */
+ str.ptr = "Organization";
+ str.slen = 12;
+ generic = pjsip_generic_string_hdr_create(pool, &str);
+ pjsip_msg_add_hdr(msg, (pjsip_hdr*)generic);
+ generic->hvalue.ptr = NULL;
+ generic->hvalue.slen = 0;
+
+ /* "Max-Forwards: 70\n" */
+ str.ptr = "Max-Forwards";
+ str.slen = 12;
+ generic = pjsip_generic_string_hdr_create(pool, &str);
+ pjsip_msg_add_hdr(msg, (pjsip_hdr*)generic);
+ str.ptr = "70";
+ str.slen = 2;
+ generic->hvalue = str;
+
+ /* "X-Header: \r\n" */
+ str.ptr = "X-Header";
+ str.slen = 8;
+ generic = pjsip_generic_string_hdr_create(pool, &str);
+ pjsip_msg_add_hdr(msg, (pjsip_hdr*)generic);
+ str.ptr = NULL;
+ str.slen = 0;
+ generic->hvalue = str;
+
+ return msg;
+}
diff --git a/pjsip/src/test-pjsip/test.c b/pjsip/src/test-pjsip/test.c
index 80d08881..49aa2aa9 100644
--- a/pjsip/src/test-pjsip/test.c
+++ b/pjsip/src/test-pjsip/test.c
@@ -1,104 +1,104 @@
-/* $Id$ */
-/*
- * Copyright (C) 2003-2006 Benny Prijono <benny@prijono.org>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
-
-
-#include "test.h"
-#include <pjlib.h>
-#include <pjsip_core.h>
-
-#define DO_TEST(test) do { \
- PJ_LOG(3, ("test", "Running %s...", #test)); \
- rc = test; \
- PJ_LOG(3, ("test", \
- "%s(%d)", \
- (rc ? "..ERROR" : "..success"), rc)); \
- if (rc!=0) goto on_return; \
- } while (0)
-
-
-
-pjsip_endpoint *endpt;
-
-void app_perror(const char *msg, pj_status_t rc)
-{
- char errbuf[256];
-
- PJ_CHECK_STACK();
-
- pjsip_strerror(rc, errbuf, sizeof(errbuf));
- PJ_LOG(1,("test", "%s: [pj_status_t=%d] %s", msg, rc, errbuf));
-
-}
-
-pj_status_t register_static_modules(pj_size_t *count, pjsip_module **modules)
-{
- *count = 0;
- return PJ_SUCCESS;
-}
-
-int test_main(void)
-{
- pj_status_t rc;
- pj_caching_pool caching_pool;
- const char *filename;
- int line;
-
- pj_log_set_level(3);
- pj_log_set_decor(PJ_LOG_HAS_NEWLINE | PJ_LOG_HAS_TIME |
- PJ_LOG_HAS_MICRO_SEC);
-
- if ((rc=pj_init()) != PJ_SUCCESS) {
- app_perror("pj_init", rc);
- return rc;
- }
-
- pj_dump_config();
-
- pj_caching_pool_init( &caching_pool, &pj_pool_factory_default_policy, 0 );
-
- rc = pjsip_endpt_create(&caching_pool.factory, "endpt", &endpt);
- if (rc != PJ_SUCCESS) {
- app_perror("pjsip_endpt_create", rc);
- pj_caching_pool_destroy(&caching_pool);
- return rc;
- }
-
- PJ_LOG(3,("",""));
-
- DO_TEST(uri_test());
-
-on_return:
-
- pjsip_endpt_destroy(endpt);
- pj_caching_pool_destroy(&caching_pool);
-
- PJ_LOG(3,("test", ""));
-
- pj_thread_get_stack_info(pj_thread_this(), &filename, &line);
- PJ_LOG(3,("test", "Stack max usage: %u, deepest: %s:%u",
- pj_thread_get_stack_max_usage(pj_thread_this()),
- filename, line));
- if (rc == 0)
- PJ_LOG(3,("test", "Looks like everything is okay!.."));
- else
- PJ_LOG(3,("test", "Test completed with error(s)"));
-
- return 0;
-}
-
+/* $Id$ */
+/*
+ * Copyright (C) 2003-2006 Benny Prijono <benny@prijono.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+
+#include "test.h"
+#include <pjlib.h>
+#include <pjsip_core.h>
+
+#define DO_TEST(test) do { \
+ PJ_LOG(3, ("test", "Running %s...", #test)); \
+ rc = test; \
+ PJ_LOG(3, ("test", \
+ "%s(%d)", \
+ (rc ? "..ERROR" : "..success"), rc)); \
+ if (rc!=0) goto on_return; \
+ } while (0)
+
+
+
+pjsip_endpoint *endpt;
+
+void app_perror(const char *msg, pj_status_t rc)
+{
+ char errbuf[256];
+
+ PJ_CHECK_STACK();
+
+ pjsip_strerror(rc, errbuf, sizeof(errbuf));
+ PJ_LOG(1,("test", "%s: [pj_status_t=%d] %s", msg, rc, errbuf));
+
+}
+
+pj_status_t register_static_modules(pj_size_t *count, pjsip_module **modules)
+{
+ *count = 0;
+ return PJ_SUCCESS;
+}
+
+int test_main(void)
+{
+ pj_status_t rc;
+ pj_caching_pool caching_pool;
+ const char *filename;
+ int line;
+
+ pj_log_set_level(3);
+ pj_log_set_decor(PJ_LOG_HAS_NEWLINE | PJ_LOG_HAS_TIME |
+ PJ_LOG_HAS_MICRO_SEC);
+
+ if ((rc=pj_init()) != PJ_SUCCESS) {
+ app_perror("pj_init", rc);
+ return rc;
+ }
+
+ pj_dump_config();
+
+ pj_caching_pool_init( &caching_pool, &pj_pool_factory_default_policy, 0 );
+
+ rc = pjsip_endpt_create(&caching_pool.factory, "endpt", &endpt);
+ if (rc != PJ_SUCCESS) {
+ app_perror("pjsip_endpt_create", rc);
+ pj_caching_pool_destroy(&caching_pool);
+ return rc;
+ }
+
+ PJ_LOG(3,("",""));
+
+ DO_TEST(uri_test());
+
+on_return:
+
+ pjsip_endpt_destroy(endpt);
+ pj_caching_pool_destroy(&caching_pool);
+
+ PJ_LOG(3,("test", ""));
+
+ pj_thread_get_stack_info(pj_thread_this(), &filename, &line);
+ PJ_LOG(3,("test", "Stack max usage: %u, deepest: %s:%u",
+ pj_thread_get_stack_max_usage(pj_thread_this()),
+ filename, line));
+ if (rc == 0)
+ PJ_LOG(3,("test", "Looks like everything is okay!.."));
+ else
+ PJ_LOG(3,("test", "Test completed with error(s)"));
+
+ return 0;
+}
+
diff --git a/pjsip/src/test-pjsip/test.h b/pjsip/src/test-pjsip/test.h
index 60b67c5e..aebf6022 100644
--- a/pjsip/src/test-pjsip/test.h
+++ b/pjsip/src/test-pjsip/test.h
@@ -1,33 +1,33 @@
-/* $Id$ */
-/*
- * Copyright (C) 2003-2006 Benny Prijono <benny@prijono.org>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
-#ifndef __TEST_H__
-#define __TEST_H__
-
-#include <pjsip/sip_types.h>
-
-extern pjsip_endpoint *endpt;
-
-pj_status_t uri_test(void);
-pj_status_t msg_test(void);
-
-int test_main(void);
-void app_perror(const char *msg, pj_status_t status);
-
-
-#endif /* __TEST_H__ */
+/* $Id$ */
+/*
+ * Copyright (C) 2003-2006 Benny Prijono <benny@prijono.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#ifndef __TEST_H__
+#define __TEST_H__
+
+#include <pjsip/sip_types.h>
+
+extern pjsip_endpoint *endpt;
+
+pj_status_t uri_test(void);
+pj_status_t msg_test(void);
+
+int test_main(void);
+void app_perror(const char *msg, pj_status_t status);
+
+
+#endif /* __TEST_H__ */
diff --git a/pjsip/src/test-pjsip/uri.c b/pjsip/src/test-pjsip/uri.c
index a61382f0..4435d1a4 100644
--- a/pjsip/src/test-pjsip/uri.c
+++ b/pjsip/src/test-pjsip/uri.c
@@ -1,560 +1,636 @@
-/* $Id$ */
-/*
- * Copyright (C) 2003-2006 Benny Prijono <benny@prijono.org>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
-#include "test.h"
-#include <pjsip_core.h>
-#include <pjlib.h>
-
-
-#define ALPHANUM "abcdefghijklmnopqrstuvwxyz" \
- "ABCDEFGHIJKLMNOPQRSTUVWXYZ" \
- "0123456789"
-#define MARK "-_.!~*'()"
-#define USER "&=+$,;?/%"
-#define PASS "&=+$,%"
-#define PARAM_CHAR "[]/:&+$" MARK "%"
-
-#define POOL_SIZE 4096
-
-static pj_uint32_t parse_len, parse_time, print_time;
-
-
-/* URI creator functions. */
-static pjsip_uri *create_uri1( pj_pool_t *pool );
-static pjsip_uri *create_uri2( pj_pool_t *pool );
-static pjsip_uri *create_uri3( pj_pool_t *pool );
-static pjsip_uri *create_uri4( pj_pool_t *pool );
-static pjsip_uri *create_uri5( pj_pool_t *pool );
-static pjsip_uri *create_uri6( pj_pool_t *pool );
-static pjsip_uri *create_uri7( pj_pool_t *pool );
-static pjsip_uri *create_uri8( pj_pool_t *pool );
-static pjsip_uri *create_uri9( pj_pool_t *pool );
-static pjsip_uri *create_uri10( pj_pool_t *pool );
-static pjsip_uri *create_uri11( pj_pool_t *pool );
-static pjsip_uri *create_uri12( pj_pool_t *pool );
-static pjsip_uri *create_uri13( pj_pool_t *pool );
-static pjsip_uri *create_uri14( pj_pool_t *pool );
-static pjsip_uri *create_uri15( pj_pool_t *pool );
-static pjsip_uri *create_uri16( pj_pool_t *pool );
-static pjsip_uri *create_uri17( pj_pool_t *pool );
-static pjsip_uri *create_uri18( pj_pool_t *pool );
-static pjsip_uri *create_uri19( pj_pool_t *pool );
-static pjsip_uri *create_dummy( pj_pool_t *pool );
-
-#define ERR_NOT_EQUAL -1001
-#define ERR_SYNTAX_ERR -1002
-
-struct uri_test
-{
- pj_status_t status;
- char str[PJSIP_MAX_URL_SIZE];
- pjsip_uri *(*creator)(pj_pool_t *pool);
- pj_size_t len;
-} uri_test_array[] =
-{
- {
- PJ_SUCCESS,
- "sip:localhost",
- &create_uri1
- },
- {
- PJ_SUCCESS,
- "sip:user@localhost",
- &create_uri2
- },
- {
- PJ_SUCCESS,
- "sip:user:password@localhost:5060",
- &create_uri3,
- },
- {
- /* Port is specified should not match unspecified port. */
- ERR_NOT_EQUAL,
- "sip:localhost:5060",
- &create_uri4
- },
- {
- /* All recognized parameters. */
- PJ_SUCCESS,
- "sip:localhost;transport=tcp;user=ip;ttl=255;lr;maddr=127.0.0.1;method=ACK",
- &create_uri5
- },
- {
- /* Params mixed with other params and header params. */
- PJ_SUCCESS,
- "sip:localhost;pickup=hurry;user=phone;message=I%20am%20sorry"
- "?Subject=Hello%20There&Server=SIP%20Server",
- &create_uri6
- },
- {
- /* SIPS. */
- PJ_SUCCESS,
- "sips:localhost",
- &create_uri7,
- },
- {
- /* Name address */
- PJ_SUCCESS,
- "<sip:localhost>",
- &create_uri8
- },
- {
- /* Name address with display name and SIPS scheme with some redundant
- * whitespaced.
- */
- PJ_SUCCESS,
- " Power Administrator <sips:localhost>",
- &create_uri9
- },
- {
- /* Name address. */
- PJ_SUCCESS,
- " \"User\" <sip:user@localhost:5071>",
- &create_uri10
- },
- {
- /* Escaped sequence in display name (display=Strange User\"\\\"). */
- PJ_SUCCESS,
- " \"Strange User\\\"\\\\\\\"\" <sip:localhost>",
- &create_uri11,
- },
- {
- /* Errorneous escaping in display name. */
- ERR_SYNTAX_ERR,
- " \"Rogue User\\\" <sip:localhost>",
- &create_uri12,
- },
- {
- /* Dangling quote in display name, but that should be OK. */
- PJ_SUCCESS,
- "Strange User\" <sip:localhost>",
- &create_uri13,
- },
- {
- /* Special characters in parameter value must be quoted. */
- PJ_SUCCESS,
- "sip:localhost;pvalue=\"hello world\"",
- &create_uri14,
- },
- {
- /* Excercise strange character sets allowed in display, user, password,
- * host, and port.
- */
- PJ_SUCCESS,
- "This is -. !% *_+`'~ me <sip:a19A&=+$,;?/%2c:%09a&Zz=+$,@"
- "my_proxy09.MY-domain.com:9801>",
- &create_uri15,
- },
- {
- /* Another excercise to the allowed character sets to the hostname. */
- PJ_SUCCESS,
- "sip:" ALPHANUM "-_.com",
- &create_uri16,
- },
- {
- /* Another excercise to the allowed character sets to the username
- * and password.
- */
- PJ_SUCCESS,
- "sip:" ALPHANUM USER ":" ALPHANUM PASS "@host",
- &create_uri17,
- },
- {
- /* Excercise to the pname and pvalue, and mixup of other-param
- * between 'recognized' params.
- */
- PJ_SUCCESS,
- "sip:host;user=ip;" ALPHANUM PARAM_CHAR "=" ALPHANUM PARAM_CHAR
- ";lr;other=1;transport=sctp;other2",
- &create_uri18,
- },
- {
- /* This should trigger syntax error. */
- ERR_SYNTAX_ERR,
- "sip:",
- &create_dummy,
- },
- {
- /* Syntax error: whitespace after scheme. */
- ERR_SYNTAX_ERR,
- "sip :host",
- &create_dummy,
- },
- {
- /* Syntax error: whitespace before hostname. */
- ERR_SYNTAX_ERR,
- "sip: host",
- &create_dummy,
- },
- {
- /* Syntax error: invalid port. */
- ERR_SYNTAX_ERR,
- "sip:user:password",
- &create_dummy,
- },
- {
- /* Syntax error: no host. */
- ERR_SYNTAX_ERR,
- "sip:user@",
- &create_dummy,
- },
- {
- /* Syntax error: no user/host. */
- ERR_SYNTAX_ERR,
- "sip:@",
- &create_dummy,
- },
- {
- /* Syntax error: empty string. */
- ERR_SYNTAX_ERR,
- "",
- &create_dummy,
- }
-};
-
-static pjsip_uri *create_uri1(pj_pool_t *pool)
-{
- /* "sip:localhost" */
- pjsip_url *url = pjsip_url_create(pool, 0);
-
- pj_strdup2(pool, &url->host, "localhost");
- return (pjsip_uri*)url;
-}
-
-static pjsip_uri *create_uri2(pj_pool_t *pool)
-{
- /* "sip:user@localhost" */
- pjsip_url *url = pjsip_url_create(pool, 0);
-
- pj_strdup2( pool, &url->user, "user");
- pj_strdup2( pool, &url->host, "localhost");
-
- return (pjsip_uri*) url;
-}
-
-static pjsip_uri *create_uri3(pj_pool_t *pool)
-{
- /* "sip:user:password@localhost:5060" */
- pjsip_url *url = pjsip_url_create(pool, 0);
-
- pj_strdup2( pool, &url->user, "user");
- pj_strdup2( pool, &url->passwd, "password");
- pj_strdup2( pool, &url->host, "localhost");
- url->port = 5060;
-
- return (pjsip_uri*) url;
-}
-
-static pjsip_uri *create_uri4(pj_pool_t *pool)
-{
- /* Like: "sip:localhost:5060", but without the port. */
- pjsip_url *url = pjsip_url_create(pool, 0);
-
- pj_strdup2(pool, &url->host, "localhost");
- return (pjsip_uri*)url;
-}
-
-static pjsip_uri *create_uri5(pj_pool_t *pool)
-{
- /* "sip:localhost;transport=tcp;user=ip;ttl=255;lr;maddr=127.0.0.1;method=ACK" */
- pjsip_url *url = pjsip_url_create(pool, 0);
-
- pj_strdup2(pool, &url->host, "localhost");
- pj_strdup2(pool, &url->transport_param, "tcp");
- pj_strdup2(pool, &url->user_param, "ip");
- url->ttl_param = 255;
- url->lr_param = 1;
- pj_strdup2(pool, &url->maddr_param, "127.0.0.1");
- pj_strdup2(pool, &url->method_param, "ACK");
-
- return (pjsip_uri*)url;
-}
-
-static pjsip_uri *create_uri6(pj_pool_t *pool)
-{
- /* "sip:localhost;pickup=hurry;user=phone;message=I%20am%20sorry"
- "?Subject=Hello%20There&Server=SIP%20Server"
- */
- pjsip_url *url = pjsip_url_create(pool, 0);
-
- pj_strdup2(pool, &url->host, "localhost");
- pj_strdup2(pool, &url->user_param, "phone");
- pj_strdup2(pool, &url->other_param, ";pickup=hurry;message=I%20am%20sorry");
- pj_strdup2(pool, &url->header_param, "?Subject=Hello%20There&Server=SIP%20Server");
- return (pjsip_uri*)url;
-
-}
-
-static pjsip_uri *create_uri7(pj_pool_t *pool)
-{
- /* "sips:localhost" */
- pjsip_url *url = pjsip_url_create(pool, 1);
-
- pj_strdup2(pool, &url->host, "localhost");
- return (pjsip_uri*)url;
-}
-
-static pjsip_uri *create_uri8(pj_pool_t *pool)
-{
- /* "<sip:localhost>" */
- pjsip_name_addr *name_addr = pjsip_name_addr_create(pool);
- pjsip_url *url;
-
- url = pjsip_url_create(pool, 0);
- name_addr->uri = (pjsip_uri*) url;
-
- pj_strdup2(pool, &url->host, "localhost");
- return (pjsip_uri*)name_addr;
-}
-
-static pjsip_uri *create_uri9(pj_pool_t *pool)
-{
- /* " Power Administrator <sips:localhost>" */
- pjsip_name_addr *name_addr = pjsip_name_addr_create(pool);
- pjsip_url *url;
-
- url = pjsip_url_create(pool, 1);
- name_addr->uri = (pjsip_uri*) url;
-
- pj_strdup2(pool, &name_addr->display, "Power Administrator");
- pj_strdup2(pool, &url->host, "localhost");
- return (pjsip_uri*)name_addr;
-}
-
-static pjsip_uri *create_uri10(pj_pool_t *pool)
-{
- /* " \"User\" <sip:user@localhost:5071>" */
- pjsip_name_addr *name_addr = pjsip_name_addr_create(pool);
- pjsip_url *url;
-
- url = pjsip_url_create(pool, 0);
- name_addr->uri = (pjsip_uri*) url;
-
- pj_strdup2(pool, &name_addr->display, "\"User\"");
- pj_strdup2(pool, &url->user, "user");
- pj_strdup2(pool, &url->host, "localhost");
- url->port = 5071;
- return (pjsip_uri*)name_addr;
-}
-
-static pjsip_uri *create_uri11(pj_pool_t *pool)
-{
- /* " \"Strange User\\\"\\\\\\\"\" <sip:localhost>" */
- pjsip_name_addr *name_addr = pjsip_name_addr_create(pool);
- pjsip_url *url;
-
- url = pjsip_url_create(pool, 0);
- name_addr->uri = (pjsip_uri*) url;
-
- pj_strdup2(pool, &name_addr->display, "\"Strange User\\\"\\\\\\\"\"");
- pj_strdup2(pool, &url->host, "localhost");
- return (pjsip_uri*)name_addr;
-}
-
-static pjsip_uri *create_uri12(pj_pool_t *pool)
-{
- /* " \"Rogue User\\\" <sip:localhost>" */
- pjsip_name_addr *name_addr = pjsip_name_addr_create(pool);
- pjsip_url *url;
-
- url = pjsip_url_create(pool, 0);
- name_addr->uri = (pjsip_uri*) url;
-
- pj_strdup2(pool, &name_addr->display, "\"Rogue User\\\"");
- pj_strdup2(pool, &url->host, "localhost");
- return (pjsip_uri*)name_addr;
-}
-
-static pjsip_uri *create_uri13(pj_pool_t *pool)
-{
- /* "Strange User\" <sip:localhost>" */
- pjsip_name_addr *name_addr = pjsip_name_addr_create(pool);
- pjsip_url *url;
-
- url = pjsip_url_create(pool, 0);
- name_addr->uri = (pjsip_uri*) url;
-
- pj_strdup2(pool, &name_addr->display, "Strange User\"");
- pj_strdup2(pool, &url->host, "localhost");
- return (pjsip_uri*)name_addr;
-}
-
-static pjsip_uri *create_uri14(pj_pool_t *pool)
-{
- /* "sip:localhost;pvalue=\"hello world\"" */
- pjsip_url *url;
- url = pjsip_url_create(pool, 0);
- pj_strdup2(pool, &url->host, "localhost");
- pj_strdup2(pool, &url->other_param, ";pvalue=\"hello world\"");
- return (pjsip_uri*)url;
-}
-
-static pjsip_uri *create_uri15(pj_pool_t *pool)
-{
- /* "This is -. !% *_+`'~ me <sip:a19A&=+$,;?/%2c:%09a&Zz=+$,@my_proxy09.my-domain.com:9801>" */
- pjsip_name_addr *name_addr = pjsip_name_addr_create(pool);
- pjsip_url *url;
-
- url = pjsip_url_create(pool, 0);
- name_addr->uri = (pjsip_uri*) url;
-
- pj_strdup2(pool, &name_addr->display, "This is -. !% *_+`'~ me");
- pj_strdup2(pool, &url->user, "a19A&=+$,;?/%2c");
- pj_strdup2(pool, &url->passwd, "%09a&Zz=+$,");
- pj_strdup2(pool, &url->host, "my_proxy09.MY-domain.com");
- url->port = 9801;
- return (pjsip_uri*)name_addr;
-}
-
-static pjsip_uri *create_uri16(pj_pool_t *pool)
-{
- /* "sip:abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-_.com" */
- pjsip_url *url;
- url = pjsip_url_create(pool, 0);
- pj_strdup2(pool, &url->host, ALPHANUM "-_.com");
- return (pjsip_uri*)url;
-}
-
-static pjsip_uri *create_uri17(pj_pool_t *pool)
-{
- /* "sip:" ALPHANUM USER ":" ALPHANUM PASS "@host" */
- pjsip_url *url;
- url = pjsip_url_create(pool, 0);
- pj_strdup2(pool, &url->user, ALPHANUM USER);
- pj_strdup2(pool, &url->passwd, ALPHANUM PASS);
- pj_strdup2(pool, &url->host, "host");
- return (pjsip_uri*)url;
-}
-
-static pjsip_uri *create_uri18(pj_pool_t *pool)
-{
- /* "sip:host;user=ip;" ALPHANUM PARAM_CHAR "=" ALPHANUM PARAM_CHAR ";lr;other=1;transport=sctp;other2" */
- pjsip_url *url;
- url = pjsip_url_create(pool, 0);
- pj_strdup2(pool, &url->host, "host");
- pj_strdup2(pool, &url->user_param, "ip");
- pj_strdup2(pool, &url->transport_param, "sctp");
- pj_strdup2(pool, &url->other_param, ";" ALPHANUM PARAM_CHAR "=" ALPHANUM PARAM_CHAR ";other=1;other2");
- url->lr_param = 1;
- return (pjsip_uri*)url;
-}
-
-static pjsip_uri *create_dummy(pj_pool_t *pool)
-{
- PJ_UNUSED_ARG(pool);
- return NULL;
-}
-
-/*****************************************************************************/
-
-/*
- * Test one test entry.
- */
-static pj_status_t do_uri_test(pj_pool_t *pool, struct uri_test *entry)
-{
- pj_status_t status;
- int len;
- pjsip_uri *parsed_uri, *ref_uri;
- pj_str_t s1 = {NULL, 0}, s2 = {NULL, 0};
- pj_timestamp t1, t2;
-
- entry->len = pj_native_strlen(entry->str);
-
- /* Parse URI text. */
- pj_get_timestamp(&t1);
- parse_len += entry->len;
- parsed_uri = pjsip_parse_uri(pool, entry->str, entry->len, 0);
- if (!parsed_uri) {
- /* Parsing failed. If the entry says that this is expected, then
- * return OK.
- */
- status = entry->status==ERR_SYNTAX_ERR ? PJ_SUCCESS : -10;
- goto on_return;
- }
- pj_get_timestamp(&t2);
- parse_time += t2.u32.lo - t1.u32.lo;
-
- /* Create the reference URI. */
- ref_uri = entry->creator(pool);
-
- /* Print both URI. */
- s1.ptr = pj_pool_alloc(pool, PJSIP_MAX_URL_SIZE);
- s2.ptr = pj_pool_alloc(pool, PJSIP_MAX_URL_SIZE);
-
- pj_get_timestamp(&t1);
- len = pjsip_uri_print( PJSIP_URI_IN_OTHER, parsed_uri, s1.ptr, PJSIP_MAX_URL_SIZE);
- if (len < 1) {
- status = -20;
- goto on_return;
- }
- s1.slen = len;
-
- len = pjsip_uri_print( PJSIP_URI_IN_OTHER, ref_uri, s2.ptr, PJSIP_MAX_URL_SIZE);
- if (len < 1) {
- status = -30;
- goto on_return;
- }
- s2.slen = len;
- pj_get_timestamp(&t2);
- print_time += t2.u32.lo - t1.u32.lo;
-
- /* Full comparison of parsed URI with reference URI. */
- if (pjsip_uri_cmp(PJSIP_URI_IN_OTHER, parsed_uri, ref_uri) != 0) {
- /* Not equal. See if this is the expected status. */
- status = entry->status==ERR_NOT_EQUAL ? PJ_SUCCESS : -40;
- goto on_return;
-
- } else {
- /* Equal. See if this is the expected status. */
- status = entry->status==PJ_SUCCESS ? PJ_SUCCESS : -50;
- if (status != PJ_SUCCESS) {
- goto on_return;
- }
- }
-
- /* Compare text. */
- if (pj_strcmp(&s1, &s2) != 0) {
- /* Not equal. */
- status = -60;
- }
-
-on_return:
- return status;
-}
-
-pj_status_t uri_test()
-{
- unsigned i;
- pj_pool_t *pool;
- pj_status_t status;
-
- pool = pjsip_endpt_create_pool(endpt, "", 4000, 4000);
-
- for (i=0; i<PJ_ARRAY_SIZE(uri_test_array); ++i) {
- status = do_uri_test(pool, &uri_test_array[i]);
- if (status != PJ_SUCCESS) {
- PJ_LOG(3,("uri_test", " error %d when testing entry %d",
- status, i));
- break;
- }
- }
-
- pjsip_endpt_destroy_pool(endpt, pool);
- return status;
-}
-
+/* $Id$ */
+/*
+ * Copyright (C) 2003-2006 Benny Prijono <benny@prijono.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#include "test.h"
+#include <pjsip_core.h>
+#include <pjlib.h>
+
+
+#define ALPHANUM "abcdefghijklmnopqrstuvwxyz" \
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZ" \
+ "0123456789"
+#define MARK "-_.!~*'()"
+#define USER_CHAR ALPHANUM MARK "&=+$,;?/"
+#define PASS_CHAR ALPHANUM MARK "&=+$,"
+#define PARAM_CHAR ALPHANUM MARK "[]/:&+$"
+
+#define POOL_SIZE 4000
+#define LOOP_COUNT 1000
+#define AVERAGE_URL_LEN 80
+#define THREAD_COUNT 4
+
+static pj_uint32_t parse_len;
+static pj_timestamp parse_time, print_time;
+
+
+/* URI creator functions. */
+static pjsip_uri *create_uri0( pj_pool_t *pool );
+static pjsip_uri *create_uri1( pj_pool_t *pool );
+static pjsip_uri *create_uri2( pj_pool_t *pool );
+static pjsip_uri *create_uri3( pj_pool_t *pool );
+static pjsip_uri *create_uri4( pj_pool_t *pool );
+static pjsip_uri *create_uri5( pj_pool_t *pool );
+static pjsip_uri *create_uri6( pj_pool_t *pool );
+static pjsip_uri *create_uri7( pj_pool_t *pool );
+static pjsip_uri *create_uri8( pj_pool_t *pool );
+static pjsip_uri *create_uri9( pj_pool_t *pool );
+static pjsip_uri *create_uri10( pj_pool_t *pool );
+static pjsip_uri *create_uri11( pj_pool_t *pool );
+static pjsip_uri *create_uri12( pj_pool_t *pool );
+static pjsip_uri *create_uri13( pj_pool_t *pool );
+static pjsip_uri *create_uri14( pj_pool_t *pool );
+static pjsip_uri *create_uri15( pj_pool_t *pool );
+static pjsip_uri *create_uri16( pj_pool_t *pool );
+static pjsip_uri *create_uri17( pj_pool_t *pool );
+static pjsip_uri *create_dummy( pj_pool_t *pool );
+
+#define ERR_NOT_EQUAL -1001
+#define ERR_SYNTAX_ERR -1002
+
+struct uri_test
+{
+ pj_status_t status;
+ char str[PJSIP_MAX_URL_SIZE];
+ pjsip_uri *(*creator)(pj_pool_t *pool);
+ pj_size_t len;
+} uri_test_array[] =
+{
+ {
+ PJ_SUCCESS,
+ "sip:localhost",
+ &create_uri0
+ },
+ {
+ PJ_SUCCESS,
+ "sip:user@localhost",
+ &create_uri1
+ },
+ {
+ PJ_SUCCESS,
+ "sip:user:password@localhost:5060",
+ &create_uri2,
+ },
+ {
+ /* Port is specified should not match unspecified port. */
+ ERR_NOT_EQUAL,
+ "sip:localhost:5060",
+ &create_uri3
+ },
+ {
+ /* All recognized parameters. */
+ PJ_SUCCESS,
+ "sip:localhost;transport=tcp;user=ip;ttl=255;lr;maddr=127.0.0.1;method=ACK",
+ &create_uri4
+ },
+ {
+ /* Params mixed with other params and header params. */
+ PJ_SUCCESS,
+ "sip:localhost;pickup=hurry;user=phone;message=I%20am%20sorry"
+ "?Subject=Hello%20There&Server=SIP%20Server",
+ &create_uri5
+ },
+ {
+ /* SIPS. */
+ PJ_SUCCESS,
+ "sips:localhost",
+ &create_uri6,
+ },
+ {
+ /* Name address */
+ PJ_SUCCESS,
+ "<sip:localhost>",
+ &create_uri7
+ },
+ {
+ /* Name address with display name and SIPS scheme with some redundant
+ * whitespaced.
+ */
+ PJ_SUCCESS,
+ " Power Administrator <sips:localhost>",
+ &create_uri8
+ },
+ {
+ /* Name address. */
+ PJ_SUCCESS,
+ " \"User\" <sip:user@localhost:5071>",
+ &create_uri9
+ },
+ {
+ /* Escaped sequence in display name (display=Strange User\"\\\"). */
+ PJ_SUCCESS,
+ " \"Strange User\\\"\\\\\\\"\" <sip:localhost>",
+ &create_uri10,
+ },
+ {
+ /* Errorneous escaping in display name. */
+ ERR_SYNTAX_ERR,
+ " \"Rogue User\\\" <sip:localhost>",
+ &create_uri11,
+ },
+ {
+ /* Dangling quote in display name, but that should be OK. */
+ PJ_SUCCESS,
+ "Strange User\" <sip:localhost>",
+ &create_uri12,
+ },
+ {
+ /* Special characters in parameter value must be quoted. */
+ PJ_SUCCESS,
+ "sip:localhost;pvalue=\"hello world\"",
+ &create_uri13,
+ },
+ {
+ /* Excercise strange character sets allowed in display, user, password,
+ * host, and port.
+ */
+ PJ_SUCCESS,
+ "This is -. !% *_+`'~ me <sip:a19A&=+$,;?/%2c:%40a&Zz=+$,@"
+ "my_proxy09.MY-domain.com:9801>",
+ &create_uri14,
+ },
+ {
+ /* Another excercise to the allowed character sets to the hostname. */
+ PJ_SUCCESS,
+ "sip:" ALPHANUM "-_.com",
+ &create_uri15,
+ },
+ {
+ /* Another excercise to the allowed character sets to the username
+ * and password.
+ */
+ PJ_SUCCESS,
+ "sip:" USER_CHAR ":" PASS_CHAR "@host",
+ &create_uri16,
+ },
+ {
+ /* Excercise to the pname and pvalue, and mixup of other-param
+ * between 'recognized' params.
+ */
+ PJ_SUCCESS,
+ "sip:host;user=ip;" PARAM_CHAR "%21=" PARAM_CHAR "%21"
+ ";lr;other=1;transport=sctp;other2",
+ &create_uri17,
+ },
+ {
+ /* 18: This should trigger syntax error. */
+ ERR_SYNTAX_ERR,
+ "sip:",
+ &create_dummy,
+ },
+ {
+ /* 19: Syntax error: whitespace after scheme. */
+ ERR_SYNTAX_ERR,
+ "sip :host",
+ &create_dummy,
+ },
+ {
+ /* 20: Syntax error: whitespace before hostname. */
+ ERR_SYNTAX_ERR,
+ "sip: host",
+ &create_dummy,
+ },
+ {
+ /* 21: Syntax error: invalid port. */
+ ERR_SYNTAX_ERR,
+ "sip:user:password",
+ &create_dummy,
+ },
+ {
+ /* 22: Syntax error: no host. */
+ ERR_SYNTAX_ERR,
+ "sip:user@",
+ &create_dummy,
+ },
+ {
+ /* 23: Syntax error: no user/host. */
+ ERR_SYNTAX_ERR,
+ "sip:@",
+ &create_dummy,
+ },
+ {
+ /* 24: Syntax error: empty string. */
+ ERR_SYNTAX_ERR,
+ "",
+ &create_dummy,
+ }
+};
+
+static pjsip_uri *create_uri0(pj_pool_t *pool)
+{
+ /* "sip:localhost" */
+ pjsip_url *url = pjsip_url_create(pool, 0);
+
+ pj_strdup2(pool, &url->host, "localhost");
+ return (pjsip_uri*)url;
+}
+
+static pjsip_uri *create_uri1(pj_pool_t *pool)
+{
+ /* "sip:user@localhost" */
+ pjsip_url *url = pjsip_url_create(pool, 0);
+
+ pj_strdup2( pool, &url->user, "user");
+ pj_strdup2( pool, &url->host, "localhost");
+
+ return (pjsip_uri*) url;
+}
+
+static pjsip_uri *create_uri2(pj_pool_t *pool)
+{
+ /* "sip:user:password@localhost:5060" */
+ pjsip_url *url = pjsip_url_create(pool, 0);
+
+ pj_strdup2( pool, &url->user, "user");
+ pj_strdup2( pool, &url->passwd, "password");
+ pj_strdup2( pool, &url->host, "localhost");
+ url->port = 5060;
+
+ return (pjsip_uri*) url;
+}
+
+static pjsip_uri *create_uri3(pj_pool_t *pool)
+{
+ /* Like: "sip:localhost:5060", but without the port. */
+ pjsip_url *url = pjsip_url_create(pool, 0);
+
+ pj_strdup2(pool, &url->host, "localhost");
+ return (pjsip_uri*)url;
+}
+
+static pjsip_uri *create_uri4(pj_pool_t *pool)
+{
+ /* "sip:localhost;transport=tcp;user=ip;ttl=255;lr;maddr=127.0.0.1;method=ACK" */
+ pjsip_url *url = pjsip_url_create(pool, 0);
+
+ pj_strdup2(pool, &url->host, "localhost");
+ pj_strdup2(pool, &url->transport_param, "tcp");
+ pj_strdup2(pool, &url->user_param, "ip");
+ url->ttl_param = 255;
+ url->lr_param = 1;
+ pj_strdup2(pool, &url->maddr_param, "127.0.0.1");
+ pj_strdup2(pool, &url->method_param, "ACK");
+
+ return (pjsip_uri*)url;
+}
+
+#define param_add(list,pname,pvalue) \
+ do { \
+ pjsip_param *param; \
+ param=pj_pool_alloc(pool, sizeof(pjsip_param)); \
+ param->name = pj_str(pname); \
+ param->value = pj_str(pvalue); \
+ pj_list_insert_before(&list, param); \
+ } while (0)
+
+static pjsip_uri *create_uri5(pj_pool_t *pool)
+{
+ /* "sip:localhost;pickup=hurry;user=phone;message=I%20am%20sorry"
+ "?Subject=Hello%20There&Server=SIP%20Server"
+ */
+ pjsip_url *url = pjsip_url_create(pool, 0);
+
+ pj_strdup2(pool, &url->host, "localhost");
+ pj_strdup2(pool, &url->user_param, "phone");
+
+ //pj_strdup2(pool, &url->other_param, ";pickup=hurry;message=I%20am%20sorry");
+ param_add(url->other_param, "pickup", "hurry");
+ param_add(url->other_param, "message", "I am sorry");
+
+ //pj_strdup2(pool, &url->header_param, "?Subject=Hello%20There&Server=SIP%20Server");
+ param_add(url->header_param, "Subject", "Hello There");
+ param_add(url->header_param, "Server", "SIP Server");
+ return (pjsip_uri*)url;
+
+}
+
+static pjsip_uri *create_uri6(pj_pool_t *pool)
+{
+ /* "sips:localhost" */
+ pjsip_url *url = pjsip_url_create(pool, 1);
+
+ pj_strdup2(pool, &url->host, "localhost");
+ return (pjsip_uri*)url;
+}
+
+static pjsip_uri *create_uri7(pj_pool_t *pool)
+{
+ /* "<sip:localhost>" */
+ pjsip_name_addr *name_addr = pjsip_name_addr_create(pool);
+ pjsip_url *url;
+
+ url = pjsip_url_create(pool, 0);
+ name_addr->uri = (pjsip_uri*) url;
+
+ pj_strdup2(pool, &url->host, "localhost");
+ return (pjsip_uri*)name_addr;
+}
+
+static pjsip_uri *create_uri8(pj_pool_t *pool)
+{
+ /* " Power Administrator <sips:localhost>" */
+ pjsip_name_addr *name_addr = pjsip_name_addr_create(pool);
+ pjsip_url *url;
+
+ url = pjsip_url_create(pool, 1);
+ name_addr->uri = (pjsip_uri*) url;
+
+ pj_strdup2(pool, &name_addr->display, "Power Administrator");
+ pj_strdup2(pool, &url->host, "localhost");
+ return (pjsip_uri*)name_addr;
+}
+
+static pjsip_uri *create_uri9(pj_pool_t *pool)
+{
+ /* " \"User\" <sip:user@localhost:5071>" */
+ pjsip_name_addr *name_addr = pjsip_name_addr_create(pool);
+ pjsip_url *url;
+
+ url = pjsip_url_create(pool, 0);
+ name_addr->uri = (pjsip_uri*) url;
+
+ pj_strdup2(pool, &name_addr->display, "User");
+ pj_strdup2(pool, &url->user, "user");
+ pj_strdup2(pool, &url->host, "localhost");
+ url->port = 5071;
+ return (pjsip_uri*)name_addr;
+}
+
+static pjsip_uri *create_uri10(pj_pool_t *pool)
+{
+ /* " \"Strange User\\\"\\\\\\\"\" <sip:localhost>" */
+ pjsip_name_addr *name_addr = pjsip_name_addr_create(pool);
+ pjsip_url *url;
+
+ url = pjsip_url_create(pool, 0);
+ name_addr->uri = (pjsip_uri*) url;
+
+ pj_strdup2(pool, &name_addr->display, "Strange User\\\"\\\\\\\"");
+ pj_strdup2(pool, &url->host, "localhost");
+ return (pjsip_uri*)name_addr;
+}
+
+static pjsip_uri *create_uri11(pj_pool_t *pool)
+{
+ /* " \"Rogue User\\\" <sip:localhost>" */
+ pjsip_name_addr *name_addr = pjsip_name_addr_create(pool);
+ pjsip_url *url;
+
+ url = pjsip_url_create(pool, 0);
+ name_addr->uri = (pjsip_uri*) url;
+
+ pj_strdup2(pool, &name_addr->display, "Rogue User\\");
+ pj_strdup2(pool, &url->host, "localhost");
+ return (pjsip_uri*)name_addr;
+}
+
+static pjsip_uri *create_uri12(pj_pool_t *pool)
+{
+ /* "Strange User\" <sip:localhost>" */
+ pjsip_name_addr *name_addr = pjsip_name_addr_create(pool);
+ pjsip_url *url;
+
+ url = pjsip_url_create(pool, 0);
+ name_addr->uri = (pjsip_uri*) url;
+
+ pj_strdup2(pool, &name_addr->display, "Strange User\"");
+ pj_strdup2(pool, &url->host, "localhost");
+ return (pjsip_uri*)name_addr;
+}
+
+static pjsip_uri *create_uri13(pj_pool_t *pool)
+{
+ /* "sip:localhost;pvalue=\"hello world\"" */
+ pjsip_url *url;
+ url = pjsip_url_create(pool, 0);
+ pj_strdup2(pool, &url->host, "localhost");
+ //pj_strdup2(pool, &url->other_param, ";pvalue=\"hello world\"");
+ param_add(url->other_param, "pvalue", "hello world");
+ return (pjsip_uri*)url;
+}
+
+static pjsip_uri *create_uri14(pj_pool_t *pool)
+{
+ /* "This is -. !% *_+`'~ me <sip:a19A&=+$,;?/%2c:%40a&Zz=+$,@my_proxy09.my-domain.com:9801>" */
+ pjsip_name_addr *name_addr = pjsip_name_addr_create(pool);
+ pjsip_url *url;
+
+ url = pjsip_url_create(pool, 0);
+ name_addr->uri = (pjsip_uri*) url;
+
+ pj_strdup2(pool, &name_addr->display, "This is -. !% *_+`'~ me");
+ pj_strdup2(pool, &url->user, "a19A&=+$,;?/,");
+ pj_strdup2(pool, &url->passwd, "@a&Zz=+$,");
+ pj_strdup2(pool, &url->host, "my_proxy09.MY-domain.com");
+ url->port = 9801;
+ return (pjsip_uri*)name_addr;
+}
+
+static pjsip_uri *create_uri15(pj_pool_t *pool)
+{
+ /* "sip:abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-_.com" */
+ pjsip_url *url;
+ url = pjsip_url_create(pool, 0);
+ pj_strdup2(pool, &url->host, ALPHANUM "-_.com");
+ return (pjsip_uri*)url;
+}
+
+static pjsip_uri *create_uri16(pj_pool_t *pool)
+{
+ /* "sip:" USER_CHAR ":" PASS_CHAR "@host" */
+ pjsip_url *url;
+ url = pjsip_url_create(pool, 0);
+ pj_strdup2(pool, &url->user, USER_CHAR);
+ pj_strdup2(pool, &url->passwd, PASS_CHAR);
+ pj_strdup2(pool, &url->host, "host");
+ return (pjsip_uri*)url;
+}
+
+static pjsip_uri *create_uri17(pj_pool_t *pool)
+{
+ /* "sip:host;user=ip;" PARAM_CHAR "%21=" PARAM_CHAR "%21;lr;other=1;transport=sctp;other2" */
+ pjsip_url *url;
+ url = pjsip_url_create(pool, 0);
+ pj_strdup2(pool, &url->host, "host");
+ pj_strdup2(pool, &url->user_param, "ip");
+ pj_strdup2(pool, &url->transport_param, "sctp");
+ param_add(url->other_param, PARAM_CHAR "!", PARAM_CHAR "!");
+ param_add(url->other_param, "other", "1");
+ param_add(url->other_param, "other2", "");
+ url->lr_param = 1;
+ return (pjsip_uri*)url;
+}
+
+static pjsip_uri *create_dummy(pj_pool_t *pool)
+{
+ PJ_UNUSED_ARG(pool);
+ return NULL;
+}
+
+/*****************************************************************************/
+
+/*
+ * Test one test entry.
+ */
+static pj_status_t do_uri_test(pj_pool_t *pool, struct uri_test *entry)
+{
+ pj_status_t status;
+ int len;
+ pjsip_uri *parsed_uri, *ref_uri;
+ pj_str_t s1 = {NULL, 0}, s2 = {NULL, 0};
+ pj_timestamp t1, t2;
+
+ entry->len = pj_native_strlen(entry->str);
+
+ /* Parse URI text. */
+ pj_get_timestamp(&t1);
+ parse_len += entry->len;
+ parsed_uri = pjsip_parse_uri(pool, entry->str, entry->len, 0);
+ if (!parsed_uri) {
+ /* Parsing failed. If the entry says that this is expected, then
+ * return OK.
+ */
+ status = entry->status==ERR_SYNTAX_ERR ? PJ_SUCCESS : -10;
+ if (status != 0) {
+ PJ_LOG(3,("", " uri parse error!\n"
+ " uri='%s'\n",
+ entry->str));
+ }
+ goto on_return;
+ }
+ pj_get_timestamp(&t2);
+ pj_sub_timestamp(&t2, &t1);
+ pj_add_timestamp(&parse_time, &t2);
+
+ /* Create the reference URI. */
+ ref_uri = entry->creator(pool);
+
+ /* Print both URI. */
+ s1.ptr = pj_pool_alloc(pool, PJSIP_MAX_URL_SIZE);
+ s2.ptr = pj_pool_alloc(pool, PJSIP_MAX_URL_SIZE);
+
+ pj_get_timestamp(&t1);
+ len = pjsip_uri_print( PJSIP_URI_IN_OTHER, parsed_uri, s1.ptr, PJSIP_MAX_URL_SIZE);
+ if (len < 1) {
+ status = -20;
+ goto on_return;
+ }
+ s1.ptr[len] = '\0';
+ s1.slen = len;
+
+ pj_get_timestamp(&t2);
+ pj_sub_timestamp(&t2, &t1);
+ pj_add_timestamp(&print_time, &t2);
+
+ len = pjsip_uri_print( PJSIP_URI_IN_OTHER, ref_uri, s2.ptr, PJSIP_MAX_URL_SIZE);
+ if (len < 1) {
+ status = -30;
+ goto on_return;
+ }
+ s2.ptr[len] = '\0';
+ s2.slen = len;
+
+ /* Full comparison of parsed URI with reference URI. */
+ status = pjsip_uri_cmp(PJSIP_URI_IN_OTHER, parsed_uri, ref_uri);
+ if (status != 0) {
+ /* Not equal. See if this is the expected status. */
+ status = entry->status==ERR_NOT_EQUAL ? PJ_SUCCESS : -40;
+ if (status != 0) {
+ PJ_LOG(3,("", " uri comparison mismatch, status=%d:\n"
+ " uri1='%s'\n"
+ " uri2='%s'",
+ status, s1.ptr, s2.ptr));
+ }
+ goto on_return;
+
+ } else {
+ /* Equal. See if this is the expected status. */
+ status = entry->status==PJ_SUCCESS ? PJ_SUCCESS : -50;
+ if (status != PJ_SUCCESS) {
+ goto on_return;
+ }
+ }
+
+ /* Compare text. */
+ if (pj_strcmp(&s1, &s2) != 0) {
+ /* Not equal. */
+ status = -60;
+ }
+
+on_return:
+ return status;
+}
+
+pj_status_t uri_test()
+{
+ unsigned i, loop;
+ pj_pool_t *pool;
+ pj_status_t status;
+ pj_timestamp zero;
+ pj_highprec_t avg_parse, avg_print;
+
+ zero.u32.hi = zero.u32.lo = 0;
+
+ PJ_LOG(3,("", " simple test"));
+ pool = pjsip_endpt_create_pool(endpt, "", POOL_SIZE, POOL_SIZE);
+ for (i=0; i<PJ_ARRAY_SIZE(uri_test_array); ++i) {
+ status = do_uri_test(pool, &uri_test_array[i]);
+ if (status != PJ_SUCCESS) {
+ PJ_LOG(3,("uri_test", " error %d when testing entry %d",
+ status, i));
+ goto on_return;
+ }
+ }
+ pjsip_endpt_destroy_pool(endpt, pool);
+
+ PJ_LOG(3,("", " benchmarking..."));
+ parse_len = 0;
+ parse_time.u32.hi = parse_time.u32.lo = 0;
+ print_time.u32.hi = print_time.u32.lo = 0;
+ pool = pjsip_endpt_create_pool(endpt, "", POOL_SIZE, POOL_SIZE);
+ for (loop=0; loop<LOOP_COUNT; ++loop) {
+ for (i=0; i<PJ_ARRAY_SIZE(uri_test_array); ++i) {
+ status = do_uri_test(pool, &uri_test_array[i]);
+ if (status != PJ_SUCCESS) {
+ PJ_LOG(3,("uri_test", " error %d when testing entry %d",
+ status, i));
+ goto on_return;
+ }
+ }
+ }
+
+ avg_parse = pj_elapsed_usec(&zero, &parse_time);
+ pj_highprec_mul(avg_parse, AVERAGE_URL_LEN);
+ pj_highprec_div(avg_parse, parse_len);
+ avg_parse = 1000000 / avg_parse;
+
+ avg_print = pj_elapsed_usec(&zero, &print_time);
+ pj_highprec_mul(avg_print, AVERAGE_URL_LEN);
+ pj_highprec_div(avg_print, parse_len);
+ avg_print = 1000000 / avg_print;
+
+ PJ_LOG(3,("", " done. Average parse=%d url/sec, print=%d url/sec",
+ (unsigned)avg_parse, (unsigned)avg_print));
+
+ PJ_LOG(3,("", " multithreaded test"));
+
+
+on_return:
+ pjsip_endpt_destroy_pool(endpt, pool);
+ return status;
+}
+