Applying license to pjproject
git-svn-id: https://svn.pjsip.org/repos/pjproject/trunk@49 74dad513-b988-da41-8d7b-12977e46ad98
diff --git a/pjlib-util/include/pjlib-util.h b/pjlib-util/include/pjlib-util.h
index 69989ca..8efac78 100644
--- a/pjlib-util/include/pjlib-util.h
+++ b/pjlib-util/include/pjlib-util.h
@@ -1,3 +1,24 @@
+/*
+ * PJLIB - PJ Foundation Library
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ * Benny Prijono <bennylp@bulukucing.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; 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 <pjlib-util/scanner.h>
#include <pjlib-util/stun.h>
diff --git a/pjlib-util/include/pjlib-util/md5.h b/pjlib-util/include/pjlib-util/md5.h
index 8f3f614..ce27b6a 100644
--- a/pjlib-util/include/pjlib-util/md5.h
+++ b/pjlib-util/include/pjlib-util/md5.h
@@ -1,94 +1,115 @@
-/* $Id$
- *
- */
-/*
- Copyright (C) 1999, 2002 Aladdin Enterprises. All rights reserved.
-
- This software is provided 'as-is', without any express or implied
- warranty. In no event will the authors be held liable for any damages
- arising from the use of this software.
-
- Permission is granted to anyone to use this software for any purpose,
- including commercial applications, and to alter it and redistribute it
- freely, subject to the following restrictions:
-
- 1. The origin of this software must not be misrepresented; you must not
- claim that you wrote the original software. If you use this software
- in a product, an acknowledgment in the product documentation would be
- appreciated but is not required.
- 2. Altered source versions must be plainly marked as such, and must not be
- misrepresented as being the original software.
- 3. This notice may not be removed or altered from any source distribution.
-
- L. Peter Deutsch
- ghost@aladdin.com
-
- */
-/* $Id$ */
-/*
- Independent implementation of MD5 (RFC 1321).
-
- This code implements the MD5 Algorithm defined in RFC 1321, whose
- text is available at
- http://www.ietf.org/rfc/rfc1321.txt
- The code is derived from the text of the RFC, including the test suite
- (section A.5) but excluding the rest of Appendix A. It does not include
- any code or documentation that is identified in the RFC as being
- copyrighted.
-
- The original and principal author of md5.h is L. Peter Deutsch
- <ghost@aladdin.com>. Other authors are noted in the change history
- that follows (in reverse chronological order):
-
- 2002-04-13 lpd Removed support for non-ANSI compilers; removed
- references to Ghostscript; clarified derivation from RFC 1321;
- now handles byte order either statically or dynamically.
- 1999-11-04 lpd Edited comments slightly for automatic TOC extraction.
- 1999-10-18 lpd Fixed typo in header comment (ansi2knr rather than md5);
- added conditionalization for C++ compilation from Martin
- Purschke <purschke@bnl.gov>.
- 1999-05-03 lpd Original version.
- */
-
-#ifndef md5_INCLUDED
-# define md5_INCLUDED
-
-/*
- * This package supports both compile-time and run-time determination of CPU
- * byte order. If ARCH_IS_BIG_ENDIAN is defined as 0, the code will be
- * compiled to run only on little-endian CPUs; if ARCH_IS_BIG_ENDIAN is
- * defined as non-zero, the code will be compiled to run only on big-endian
- * CPUs; if ARCH_IS_BIG_ENDIAN is not defined, the code will be compiled to
- * run on either big- or little-endian CPUs, but will run slightly less
- * efficiently on either one than if ARCH_IS_BIG_ENDIAN is defined.
- */
-
-typedef unsigned char md5_byte_t; /* 8-bit byte */
-typedef unsigned long md5_word_t; /* 32-bit word */
-
-/** Define the state of the MD5 Algorithm. */
-typedef struct md5_state_s {
- md5_word_t count[2]; /**< message length in bits, lsw first */
- md5_word_t abcd[4]; /**< digest buffer */
- md5_byte_t buf[64]; /**< accumulate block */
-} md5_state_t;
-
-#ifdef __cplusplus
-extern "C"
-{
-#endif
-
-/** Initialize the algorithm. */
-void md5_init(md5_state_t *pms);
-
-/** Append a string to the message. */
-void md5_append(md5_state_t *pms, const md5_byte_t *data, int nbytes);
-
-/** Finish the message and return the digest. */
-void md5_finish(md5_state_t *pms, md5_byte_t digest[16]);
-
-#ifdef __cplusplus
-} /* end extern "C" */
-#endif
-
-#endif /* md5_INCLUDED */
+/* $Id$
+ *
+ */
+/*
+ * PJLIB - PJ Foundation Library
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ * Benny Prijono <bennylp@bulukucing.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+/*
+ Copyright (C) 1999, 2002 Aladdin Enterprises. All rights reserved.
+
+ This software is provided 'as-is', without any express or implied
+ warranty. In no event will the authors be held liable for any damages
+ arising from the use of this software.
+
+ Permission is granted to anyone to use this software for any purpose,
+ including commercial applications, and to alter it and redistribute it
+ freely, subject to the following restrictions:
+
+ 1. The origin of this software must not be misrepresented; you must not
+ claim that you wrote the original software. If you use this software
+ in a product, an acknowledgment in the product documentation would be
+ appreciated but is not required.
+ 2. Altered source versions must be plainly marked as such, and must not be
+ misrepresented as being the original software.
+ 3. This notice may not be removed or altered from any source distribution.
+
+ L. Peter Deutsch
+ ghost@aladdin.com
+
+ */
+/* $Id$ */
+/*
+ Independent implementation of MD5 (RFC 1321).
+
+ This code implements the MD5 Algorithm defined in RFC 1321, whose
+ text is available at
+ http://www.ietf.org/rfc/rfc1321.txt
+ The code is derived from the text of the RFC, including the test suite
+ (section A.5) but excluding the rest of Appendix A. It does not include
+ any code or documentation that is identified in the RFC as being
+ copyrighted.
+
+ The original and principal author of md5.h is L. Peter Deutsch
+ <ghost@aladdin.com>. Other authors are noted in the change history
+ that follows (in reverse chronological order):
+
+ 2002-04-13 lpd Removed support for non-ANSI compilers; removed
+ references to Ghostscript; clarified derivation from RFC 1321;
+ now handles byte order either statically or dynamically.
+ 1999-11-04 lpd Edited comments slightly for automatic TOC extraction.
+ 1999-10-18 lpd Fixed typo in header comment (ansi2knr rather than md5);
+ added conditionalization for C++ compilation from Martin
+ Purschke <purschke@bnl.gov>.
+ 1999-05-03 lpd Original version.
+ */
+
+#ifndef md5_INCLUDED
+# define md5_INCLUDED
+
+/*
+ * This package supports both compile-time and run-time determination of CPU
+ * byte order. If ARCH_IS_BIG_ENDIAN is defined as 0, the code will be
+ * compiled to run only on little-endian CPUs; if ARCH_IS_BIG_ENDIAN is
+ * defined as non-zero, the code will be compiled to run only on big-endian
+ * CPUs; if ARCH_IS_BIG_ENDIAN is not defined, the code will be compiled to
+ * run on either big- or little-endian CPUs, but will run slightly less
+ * efficiently on either one than if ARCH_IS_BIG_ENDIAN is defined.
+ */
+
+typedef unsigned char md5_byte_t; /* 8-bit byte */
+typedef unsigned long md5_word_t; /* 32-bit word */
+
+/** Define the state of the MD5 Algorithm. */
+typedef struct md5_state_s {
+ md5_word_t count[2]; /**< message length in bits, lsw first */
+ md5_word_t abcd[4]; /**< digest buffer */
+ md5_byte_t buf[64]; /**< accumulate block */
+} md5_state_t;
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+/** Initialize the algorithm. */
+void md5_init(md5_state_t *pms);
+
+/** Append a string to the message. */
+void md5_append(md5_state_t *pms, const md5_byte_t *data, int nbytes);
+
+/** Finish the message and return the digest. */
+void md5_finish(md5_state_t *pms, md5_byte_t digest[16]);
+
+#ifdef __cplusplus
+} /* end extern "C" */
+#endif
+
+#endif /* md5_INCLUDED */
diff --git a/pjlib-util/include/pjlib-util/scanner.h b/pjlib-util/include/pjlib-util/scanner.h
index 711eeed..a949143 100644
--- a/pjlib-util/include/pjlib-util/scanner.h
+++ b/pjlib-util/include/pjlib-util/scanner.h
@@ -1,26 +1,47 @@
-/* $Id$
- */
-#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
+/* $Id$
+ */
+/*
+ * PJLIB - PJ Foundation Library
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ * Benny Prijono <bennylp@bulukucing.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; 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
@@ -28,28 +49,28 @@
#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;
+
+/**
+ * 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.
- */
+
+/**
+ * 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.
*/
@@ -58,13 +79,13 @@
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);
+ *
+ * @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.
@@ -90,15 +111,15 @@
* 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))
+ *
+ * @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.
@@ -117,395 +138,395 @@
* @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
+
+/**
+ * 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);
-
-/**
+ *
+ * @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);
-
-/**
+ *
+ * @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);
-
-/**
+ *
+ * @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);
-
-/**
+ *
+ * @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);
-
-/**
+ *
+ * @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);
-
-/**
+ *
+ * @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 );
-
-/**
+ *
+ * @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.
- */
-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
+ *
+ * @param cis The scanner character specification.
+ * @param c The character to check for matching.
+ */
+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
-
+ *
+ * @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/stun.h b/pjlib-util/include/pjlib-util/stun.h
index 90c20ec..d2065e7 100644
--- a/pjlib-util/include/pjlib-util/stun.h
+++ b/pjlib-util/include/pjlib-util/stun.h
@@ -1,123 +1,144 @@
-#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__ */
-
+/*
+ * PJLIB - PJ Foundation Library
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ * Benny Prijono <bennylp@bulukucing.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; 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 fd7978a..e0ebb4c 100644
--- a/pjlib-util/include/pjlib-util/xml.h
+++ b/pjlib-util/include/pjlib-util/xml.h
@@ -1,157 +1,178 @@
-/* $Id$
- *
- */
-
-#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$
+ *
+ */
+/*
+ * PJLIB - PJ Foundation Library
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ * Benny Prijono <bennylp@bulukucing.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; 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 3a57258..beb0c06 100644
--- a/pjlib-util/src/pjlib-util-test/main.c
+++ b/pjlib-util/src/pjlib-util-test/main.c
@@ -1,3 +1,24 @@
+/*
+ * PJLIB - PJ Foundation Library
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ * Benny Prijono <bennylp@bulukucing.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; 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
diff --git a/pjlib-util/src/pjlib-util-test/test.c b/pjlib-util/src/pjlib-util-test/test.c
index 4502728..06194ad 100644
--- a/pjlib-util/src/pjlib-util-test/test.c
+++ b/pjlib-util/src/pjlib-util-test/test.c
@@ -1,3 +1,24 @@
+/*
+ * PJLIB - PJ Foundation Library
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ * Benny Prijono <bennylp@bulukucing.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; 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>
diff --git a/pjlib-util/src/pjlib-util-test/test.h b/pjlib-util/src/pjlib-util-test/test.h
index 2545bd7..3b098dc 100644
--- a/pjlib-util/src/pjlib-util-test/test.h
+++ b/pjlib-util/src/pjlib-util-test/test.h
@@ -1,3 +1,24 @@
+/*
+ * PJLIB - PJ Foundation Library
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ * Benny Prijono <bennylp@bulukucing.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; 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
diff --git a/pjlib-util/src/pjlib-util-test/xml.c b/pjlib-util/src/pjlib-util-test/xml.c
index d4e2274..440e0ae 100644
--- a/pjlib-util/src/pjlib-util-test/xml.c
+++ b/pjlib-util/src/pjlib-util-test/xml.c
@@ -1,129 +1,150 @@
-/* $Id$
- */
-#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$
+ */
+/*
+ * PJLIB - PJ Foundation Library
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ * Benny Prijono <bennylp@bulukucing.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; 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 10a903d..b5a1cbc 100644
--- a/pjlib-util/src/pjlib-util/md5.c
+++ b/pjlib-util/src/pjlib-util/md5.c
@@ -1,406 +1,427 @@
-/* $Id$
- */
-/*
- Copyright (C) 1999, 2000, 2002 Aladdin Enterprises. All rights reserved.
-
- This software is provided 'as-is', without any express or implied
- warranty. In no event will the authors be held liable for any damages
- arising from the use of this software.
-
- Permission is granted to anyone to use this software for any purpose,
- including commercial applications, and to alter it and redistribute it
- freely, subject to the following restrictions:
-
- 1. The origin of this software must not be misrepresented; you must not
- claim that you wrote the original software. If you use this software
- in a product, an acknowledgment in the product documentation would be
- appreciated but is not required.
- 2. Altered source versions must be plainly marked as such, and must not be
- misrepresented as being the original software.
- 3. This notice may not be removed or altered from any source distribution.
-
- L. Peter Deutsch
- ghost@aladdin.com
-
- */
-/* Id: md5.c,v 1.6 2002/04/13 19:20:28 lpd Exp */
-/*
- Independent implementation of MD5 (RFC 1321).
-
- This code implements the MD5 Algorithm defined in RFC 1321, whose
- text is available at
- http://www.ietf.org/rfc/rfc1321.txt
- The code is derived from the text of the RFC, including the test suite
- (section A.5) but excluding the rest of Appendix A. It does not include
- any code or documentation that is identified in the RFC as being
- copyrighted.
-
- The original and principal author of md5.c is L. Peter Deutsch
- <ghost@aladdin.com>. Other authors are noted in the change history
- that follows (in reverse chronological order):
-
- 2002-04-13 lpd Clarified derivation from RFC 1321; now handles byte order
- either statically or dynamically; added missing #include <string.h>
- in library.
- 2002-03-11 lpd Corrected argument list for main(), and added int return
- type, in test program and T value program.
- 2002-02-21 lpd Added missing #include <stdio.h> in test program.
- 2000-07-03 lpd Patched to eliminate warnings about "constant is
- unsigned in ANSI C, signed in traditional"; made test program
- self-checking.
- 1999-11-04 lpd Edited comments slightly for automatic TOC extraction.
- 1999-10-18 lpd Fixed typo in header comment (ansi2knr rather than md5).
- 1999-05-03 lpd Original version.
- */
-
-#include <pjlib-util/md5.h>
-#include <pj/string.h>
-#include <pj/os.h>
-
-#undef BYTE_ORDER /* 1 = big-endian, -1 = little-endian, 0 = unknown */
-
-/*
-#ifdef ARCH_IS_BIG_ENDIAN
-# define BYTE_ORDER (ARCH_IS_BIG_ENDIAN ? 1 : -1)
-#else
-# define BYTE_ORDER 0
-#endif
-*/
-/* pjlib: */
-#include <pj/config.h>
-#if PJ_IS_LITTLE_ENDIAN
-# define BYTE_ORDER -1
-#elif PJ_IS_BIG_ENDIAN
-# define BYTE_ORDER 1
-#else
-# error Endianess is not known!
-#endif
-
-
-#define T_MASK ((md5_word_t)~0)
-#define T1 /* 0xd76aa478 */ (T_MASK ^ 0x28955b87)
-#define T2 /* 0xe8c7b756 */ (T_MASK ^ 0x173848a9)
-#define T3 0x242070db
-#define T4 /* 0xc1bdceee */ (T_MASK ^ 0x3e423111)
-#define T5 /* 0xf57c0faf */ (T_MASK ^ 0x0a83f050)
-#define T6 0x4787c62a
-#define T7 /* 0xa8304613 */ (T_MASK ^ 0x57cfb9ec)
-#define T8 /* 0xfd469501 */ (T_MASK ^ 0x02b96afe)
-#define T9 0x698098d8
-#define T10 /* 0x8b44f7af */ (T_MASK ^ 0x74bb0850)
-#define T11 /* 0xffff5bb1 */ (T_MASK ^ 0x0000a44e)
-#define T12 /* 0x895cd7be */ (T_MASK ^ 0x76a32841)
-#define T13 0x6b901122
-#define T14 /* 0xfd987193 */ (T_MASK ^ 0x02678e6c)
-#define T15 /* 0xa679438e */ (T_MASK ^ 0x5986bc71)
-#define T16 0x49b40821
-#define T17 /* 0xf61e2562 */ (T_MASK ^ 0x09e1da9d)
-#define T18 /* 0xc040b340 */ (T_MASK ^ 0x3fbf4cbf)
-#define T19 0x265e5a51
-#define T20 /* 0xe9b6c7aa */ (T_MASK ^ 0x16493855)
-#define T21 /* 0xd62f105d */ (T_MASK ^ 0x29d0efa2)
-#define T22 0x02441453
-#define T23 /* 0xd8a1e681 */ (T_MASK ^ 0x275e197e)
-#define T24 /* 0xe7d3fbc8 */ (T_MASK ^ 0x182c0437)
-#define T25 0x21e1cde6
-#define T26 /* 0xc33707d6 */ (T_MASK ^ 0x3cc8f829)
-#define T27 /* 0xf4d50d87 */ (T_MASK ^ 0x0b2af278)
-#define T28 0x455a14ed
-#define T29 /* 0xa9e3e905 */ (T_MASK ^ 0x561c16fa)
-#define T30 /* 0xfcefa3f8 */ (T_MASK ^ 0x03105c07)
-#define T31 0x676f02d9
-#define T32 /* 0x8d2a4c8a */ (T_MASK ^ 0x72d5b375)
-#define T33 /* 0xfffa3942 */ (T_MASK ^ 0x0005c6bd)
-#define T34 /* 0x8771f681 */ (T_MASK ^ 0x788e097e)
-#define T35 0x6d9d6122
-#define T36 /* 0xfde5380c */ (T_MASK ^ 0x021ac7f3)
-#define T37 /* 0xa4beea44 */ (T_MASK ^ 0x5b4115bb)
-#define T38 0x4bdecfa9
-#define T39 /* 0xf6bb4b60 */ (T_MASK ^ 0x0944b49f)
-#define T40 /* 0xbebfbc70 */ (T_MASK ^ 0x4140438f)
-#define T41 0x289b7ec6
-#define T42 /* 0xeaa127fa */ (T_MASK ^ 0x155ed805)
-#define T43 /* 0xd4ef3085 */ (T_MASK ^ 0x2b10cf7a)
-#define T44 0x04881d05
-#define T45 /* 0xd9d4d039 */ (T_MASK ^ 0x262b2fc6)
-#define T46 /* 0xe6db99e5 */ (T_MASK ^ 0x1924661a)
-#define T47 0x1fa27cf8
-#define T48 /* 0xc4ac5665 */ (T_MASK ^ 0x3b53a99a)
-#define T49 /* 0xf4292244 */ (T_MASK ^ 0x0bd6ddbb)
-#define T50 0x432aff97
-#define T51 /* 0xab9423a7 */ (T_MASK ^ 0x546bdc58)
-#define T52 /* 0xfc93a039 */ (T_MASK ^ 0x036c5fc6)
-#define T53 0x655b59c3
-#define T54 /* 0x8f0ccc92 */ (T_MASK ^ 0x70f3336d)
-#define T55 /* 0xffeff47d */ (T_MASK ^ 0x00100b82)
-#define T56 /* 0x85845dd1 */ (T_MASK ^ 0x7a7ba22e)
-#define T57 0x6fa87e4f
-#define T58 /* 0xfe2ce6e0 */ (T_MASK ^ 0x01d3191f)
-#define T59 /* 0xa3014314 */ (T_MASK ^ 0x5cfebceb)
-#define T60 0x4e0811a1
-#define T61 /* 0xf7537e82 */ (T_MASK ^ 0x08ac817d)
-#define T62 /* 0xbd3af235 */ (T_MASK ^ 0x42c50dca)
-#define T63 0x2ad7d2bb
-#define T64 /* 0xeb86d391 */ (T_MASK ^ 0x14792c6e)
-
-
-static void
-md5_process(md5_state_t *pms, const md5_byte_t *data /*[64]*/)
-{
- md5_word_t
- a = pms->abcd[0], b = pms->abcd[1],
- c = pms->abcd[2], d = pms->abcd[3];
- md5_word_t t;
-#if BYTE_ORDER > 0
- /* Define storage only for big-endian CPUs. */
- md5_word_t X[16];
-#else
- /* Define storage for little-endian or both types of CPUs. */
- md5_word_t xbuf[16];
- const md5_word_t *X;
-#endif
-
- PJ_CHECK_STACK();
-
- {
-#if BYTE_ORDER == 0
- /*
- * Determine dynamically whether this is a big-endian or
- * little-endian machine, since we can use a more efficient
- * algorithm on the latter.
- */
- static const int w = 1;
-
- if (*((const md5_byte_t *)&w)) /* dynamic little-endian */
-#endif
-#if BYTE_ORDER <= 0 /* little-endian */
- {
- /*
- * On little-endian machines, we can process properly aligned
- * data without copying it.
- */
- if (!((data - (const md5_byte_t *)0) & 3)) {
- /* data are properly aligned */
- X = (const md5_word_t *)data;
- } else {
- /* not aligned */
- memcpy(xbuf, data, 64);
- X = xbuf;
- }
- }
-#endif
-#if BYTE_ORDER == 0
- else /* dynamic big-endian */
-#endif
-#if BYTE_ORDER >= 0 /* big-endian */
- {
- /*
- * On big-endian machines, we must arrange the bytes in the
- * right order.
- */
- const md5_byte_t *xp = data;
- int i;
-
-# if BYTE_ORDER == 0
- X = xbuf; /* (dynamic only) */
-# else
-# define xbuf X /* (static only) */
-# endif
- for (i = 0; i < 16; ++i, xp += 4)
- xbuf[i] = xp[0] + (xp[1] << 8) + (xp[2] << 16) + (xp[3] << 24);
- }
-#endif
- }
-
-#define ROTATE_LEFT(x, n) (((x) << (n)) | ((x) >> (32 - (n))))
-
- /* Round 1. */
- /* Let [abcd k s i] denote the operation
- a = b + ((a + F(b,c,d) + X[k] + T[i]) <<< s). */
-#define F(x, y, z) (((x) & (y)) | (~(x) & (z)))
-#define SET(a, b, c, d, k, s, Ti)\
- t = a + F(b,c,d) + X[k] + Ti;\
- a = ROTATE_LEFT(t, s) + b
- /* Do the following 16 operations. */
- SET(a, b, c, d, 0, 7, T1);
- SET(d, a, b, c, 1, 12, T2);
- SET(c, d, a, b, 2, 17, T3);
- SET(b, c, d, a, 3, 22, T4);
- SET(a, b, c, d, 4, 7, T5);
- SET(d, a, b, c, 5, 12, T6);
- SET(c, d, a, b, 6, 17, T7);
- SET(b, c, d, a, 7, 22, T8);
- SET(a, b, c, d, 8, 7, T9);
- SET(d, a, b, c, 9, 12, T10);
- SET(c, d, a, b, 10, 17, T11);
- SET(b, c, d, a, 11, 22, T12);
- SET(a, b, c, d, 12, 7, T13);
- SET(d, a, b, c, 13, 12, T14);
- SET(c, d, a, b, 14, 17, T15);
- SET(b, c, d, a, 15, 22, T16);
-#undef SET
-
- /* Round 2. */
- /* Let [abcd k s i] denote the operation
- a = b + ((a + G(b,c,d) + X[k] + T[i]) <<< s). */
-#define G(x, y, z) (((x) & (z)) | ((y) & ~(z)))
-#define SET(a, b, c, d, k, s, Ti)\
- t = a + G(b,c,d) + X[k] + Ti;\
- a = ROTATE_LEFT(t, s) + b
- /* Do the following 16 operations. */
- SET(a, b, c, d, 1, 5, T17);
- SET(d, a, b, c, 6, 9, T18);
- SET(c, d, a, b, 11, 14, T19);
- SET(b, c, d, a, 0, 20, T20);
- SET(a, b, c, d, 5, 5, T21);
- SET(d, a, b, c, 10, 9, T22);
- SET(c, d, a, b, 15, 14, T23);
- SET(b, c, d, a, 4, 20, T24);
- SET(a, b, c, d, 9, 5, T25);
- SET(d, a, b, c, 14, 9, T26);
- SET(c, d, a, b, 3, 14, T27);
- SET(b, c, d, a, 8, 20, T28);
- SET(a, b, c, d, 13, 5, T29);
- SET(d, a, b, c, 2, 9, T30);
- SET(c, d, a, b, 7, 14, T31);
- SET(b, c, d, a, 12, 20, T32);
-#undef SET
-
- /* Round 3. */
- /* Let [abcd k s t] denote the operation
- a = b + ((a + H(b,c,d) + X[k] + T[i]) <<< s). */
-#define H(x, y, z) ((x) ^ (y) ^ (z))
-#define SET(a, b, c, d, k, s, Ti)\
- t = a + H(b,c,d) + X[k] + Ti;\
- a = ROTATE_LEFT(t, s) + b
- /* Do the following 16 operations. */
- SET(a, b, c, d, 5, 4, T33);
- SET(d, a, b, c, 8, 11, T34);
- SET(c, d, a, b, 11, 16, T35);
- SET(b, c, d, a, 14, 23, T36);
- SET(a, b, c, d, 1, 4, T37);
- SET(d, a, b, c, 4, 11, T38);
- SET(c, d, a, b, 7, 16, T39);
- SET(b, c, d, a, 10, 23, T40);
- SET(a, b, c, d, 13, 4, T41);
- SET(d, a, b, c, 0, 11, T42);
- SET(c, d, a, b, 3, 16, T43);
- SET(b, c, d, a, 6, 23, T44);
- SET(a, b, c, d, 9, 4, T45);
- SET(d, a, b, c, 12, 11, T46);
- SET(c, d, a, b, 15, 16, T47);
- SET(b, c, d, a, 2, 23, T48);
-#undef SET
-
- /* Round 4. */
- /* Let [abcd k s t] denote the operation
- a = b + ((a + I(b,c,d) + X[k] + T[i]) <<< s). */
-#define I(x, y, z) ((y) ^ ((x) | ~(z)))
-#define SET(a, b, c, d, k, s, Ti)\
- t = a + I(b,c,d) + X[k] + Ti;\
- a = ROTATE_LEFT(t, s) + b
- /* Do the following 16 operations. */
- SET(a, b, c, d, 0, 6, T49);
- SET(d, a, b, c, 7, 10, T50);
- SET(c, d, a, b, 14, 15, T51);
- SET(b, c, d, a, 5, 21, T52);
- SET(a, b, c, d, 12, 6, T53);
- SET(d, a, b, c, 3, 10, T54);
- SET(c, d, a, b, 10, 15, T55);
- SET(b, c, d, a, 1, 21, T56);
- SET(a, b, c, d, 8, 6, T57);
- SET(d, a, b, c, 15, 10, T58);
- SET(c, d, a, b, 6, 15, T59);
- SET(b, c, d, a, 13, 21, T60);
- SET(a, b, c, d, 4, 6, T61);
- SET(d, a, b, c, 11, 10, T62);
- SET(c, d, a, b, 2, 15, T63);
- SET(b, c, d, a, 9, 21, T64);
-#undef SET
-
- /* Then perform the following additions. (That is increment each
- of the four registers by the value it had before this block
- was started.) */
- pms->abcd[0] += a;
- pms->abcd[1] += b;
- pms->abcd[2] += c;
- pms->abcd[3] += d;
-}
-
-void
-md5_init(md5_state_t *pms)
-{
- PJ_CHECK_STACK();
-
- pms->count[0] = pms->count[1] = 0;
- pms->abcd[0] = 0x67452301;
- pms->abcd[1] = /*0xefcdab89*/ T_MASK ^ 0x10325476;
- pms->abcd[2] = /*0x98badcfe*/ T_MASK ^ 0x67452301;
- pms->abcd[3] = 0x10325476;
-}
-
-void
-md5_append(md5_state_t *pms, const md5_byte_t *data, int nbytes)
-{
- const md5_byte_t *p = data;
- int left = nbytes;
- int offset = (pms->count[0] >> 3) & 63;
- md5_word_t nbits = (md5_word_t)(nbytes << 3);
-
- PJ_CHECK_STACK();
-
- if (nbytes <= 0)
- return;
-
- /* Update the message length. */
- pms->count[1] += nbytes >> 29;
- pms->count[0] += nbits;
- if (pms->count[0] < nbits)
- pms->count[1]++;
-
- /* Process an initial partial block. */
- if (offset) {
- int copy = (offset + nbytes > 64 ? 64 - offset : nbytes);
-
- memcpy(pms->buf + offset, p, copy);
- if (offset + copy < 64)
- return;
- p += copy;
- left -= copy;
- md5_process(pms, pms->buf);
- }
-
- /* Process full blocks. */
- for (; left >= 64; p += 64, left -= 64)
- md5_process(pms, p);
-
- /* Process a final partial block. */
- if (left)
- memcpy(pms->buf, p, left);
-}
-
-void
-md5_finish(md5_state_t *pms, md5_byte_t digest[16])
-{
- static const md5_byte_t pad[64] = {
- 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
- };
- md5_byte_t data[8];
- int i;
-
- PJ_CHECK_STACK();
-
- /* Save the length before padding. */
- for (i = 0; i < 8; ++i)
- data[i] = (md5_byte_t)(pms->count[i >> 2] >> ((i & 3) << 3));
- /* Pad to 56 bytes mod 64. */
- md5_append(pms, pad, ((55 - (pms->count[0] >> 3)) & 63) + 1);
- /* Append the length. */
- md5_append(pms, data, 8);
- for (i = 0; i < 16; ++i)
- digest[i] = (md5_byte_t)(pms->abcd[i >> 2] >> ((i & 3) << 3));
-}
-
+/* $Id$
+ */
+/*
+ * PJLIB - PJ Foundation Library
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ * Benny Prijono <bennylp@bulukucing.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+/*
+ Copyright (C) 1999, 2000, 2002 Aladdin Enterprises. All rights reserved.
+
+ This software is provided 'as-is', without any express or implied
+ warranty. In no event will the authors be held liable for any damages
+ arising from the use of this software.
+
+ Permission is granted to anyone to use this software for any purpose,
+ including commercial applications, and to alter it and redistribute it
+ freely, subject to the following restrictions:
+
+ 1. The origin of this software must not be misrepresented; you must not
+ claim that you wrote the original software. If you use this software
+ in a product, an acknowledgment in the product documentation would be
+ appreciated but is not required.
+ 2. Altered source versions must be plainly marked as such, and must not be
+ misrepresented as being the original software.
+ 3. This notice may not be removed or altered from any source distribution.
+
+ L. Peter Deutsch
+ ghost@aladdin.com
+
+ */
+/* Id: md5.c,v 1.6 2002/04/13 19:20:28 lpd Exp */
+/*
+ Independent implementation of MD5 (RFC 1321).
+
+ This code implements the MD5 Algorithm defined in RFC 1321, whose
+ text is available at
+ http://www.ietf.org/rfc/rfc1321.txt
+ The code is derived from the text of the RFC, including the test suite
+ (section A.5) but excluding the rest of Appendix A. It does not include
+ any code or documentation that is identified in the RFC as being
+ copyrighted.
+
+ The original and principal author of md5.c is L. Peter Deutsch
+ <ghost@aladdin.com>. Other authors are noted in the change history
+ that follows (in reverse chronological order):
+
+ 2002-04-13 lpd Clarified derivation from RFC 1321; now handles byte order
+ either statically or dynamically; added missing #include <string.h>
+ in library.
+ 2002-03-11 lpd Corrected argument list for main(), and added int return
+ type, in test program and T value program.
+ 2002-02-21 lpd Added missing #include <stdio.h> in test program.
+ 2000-07-03 lpd Patched to eliminate warnings about "constant is
+ unsigned in ANSI C, signed in traditional"; made test program
+ self-checking.
+ 1999-11-04 lpd Edited comments slightly for automatic TOC extraction.
+ 1999-10-18 lpd Fixed typo in header comment (ansi2knr rather than md5).
+ 1999-05-03 lpd Original version.
+ */
+
+#include <pjlib-util/md5.h>
+#include <pj/string.h>
+#include <pj/os.h>
+
+#undef BYTE_ORDER /* 1 = big-endian, -1 = little-endian, 0 = unknown */
+
+/*
+#ifdef ARCH_IS_BIG_ENDIAN
+# define BYTE_ORDER (ARCH_IS_BIG_ENDIAN ? 1 : -1)
+#else
+# define BYTE_ORDER 0
+#endif
+*/
+/* pjlib: */
+#include <pj/config.h>
+#if PJ_IS_LITTLE_ENDIAN
+# define BYTE_ORDER -1
+#elif PJ_IS_BIG_ENDIAN
+# define BYTE_ORDER 1
+#else
+# error Endianess is not known!
+#endif
+
+
+#define T_MASK ((md5_word_t)~0)
+#define T1 /* 0xd76aa478 */ (T_MASK ^ 0x28955b87)
+#define T2 /* 0xe8c7b756 */ (T_MASK ^ 0x173848a9)
+#define T3 0x242070db
+#define T4 /* 0xc1bdceee */ (T_MASK ^ 0x3e423111)
+#define T5 /* 0xf57c0faf */ (T_MASK ^ 0x0a83f050)
+#define T6 0x4787c62a
+#define T7 /* 0xa8304613 */ (T_MASK ^ 0x57cfb9ec)
+#define T8 /* 0xfd469501 */ (T_MASK ^ 0x02b96afe)
+#define T9 0x698098d8
+#define T10 /* 0x8b44f7af */ (T_MASK ^ 0x74bb0850)
+#define T11 /* 0xffff5bb1 */ (T_MASK ^ 0x0000a44e)
+#define T12 /* 0x895cd7be */ (T_MASK ^ 0x76a32841)
+#define T13 0x6b901122
+#define T14 /* 0xfd987193 */ (T_MASK ^ 0x02678e6c)
+#define T15 /* 0xa679438e */ (T_MASK ^ 0x5986bc71)
+#define T16 0x49b40821
+#define T17 /* 0xf61e2562 */ (T_MASK ^ 0x09e1da9d)
+#define T18 /* 0xc040b340 */ (T_MASK ^ 0x3fbf4cbf)
+#define T19 0x265e5a51
+#define T20 /* 0xe9b6c7aa */ (T_MASK ^ 0x16493855)
+#define T21 /* 0xd62f105d */ (T_MASK ^ 0x29d0efa2)
+#define T22 0x02441453
+#define T23 /* 0xd8a1e681 */ (T_MASK ^ 0x275e197e)
+#define T24 /* 0xe7d3fbc8 */ (T_MASK ^ 0x182c0437)
+#define T25 0x21e1cde6
+#define T26 /* 0xc33707d6 */ (T_MASK ^ 0x3cc8f829)
+#define T27 /* 0xf4d50d87 */ (T_MASK ^ 0x0b2af278)
+#define T28 0x455a14ed
+#define T29 /* 0xa9e3e905 */ (T_MASK ^ 0x561c16fa)
+#define T30 /* 0xfcefa3f8 */ (T_MASK ^ 0x03105c07)
+#define T31 0x676f02d9
+#define T32 /* 0x8d2a4c8a */ (T_MASK ^ 0x72d5b375)
+#define T33 /* 0xfffa3942 */ (T_MASK ^ 0x0005c6bd)
+#define T34 /* 0x8771f681 */ (T_MASK ^ 0x788e097e)
+#define T35 0x6d9d6122
+#define T36 /* 0xfde5380c */ (T_MASK ^ 0x021ac7f3)
+#define T37 /* 0xa4beea44 */ (T_MASK ^ 0x5b4115bb)
+#define T38 0x4bdecfa9
+#define T39 /* 0xf6bb4b60 */ (T_MASK ^ 0x0944b49f)
+#define T40 /* 0xbebfbc70 */ (T_MASK ^ 0x4140438f)
+#define T41 0x289b7ec6
+#define T42 /* 0xeaa127fa */ (T_MASK ^ 0x155ed805)
+#define T43 /* 0xd4ef3085 */ (T_MASK ^ 0x2b10cf7a)
+#define T44 0x04881d05
+#define T45 /* 0xd9d4d039 */ (T_MASK ^ 0x262b2fc6)
+#define T46 /* 0xe6db99e5 */ (T_MASK ^ 0x1924661a)
+#define T47 0x1fa27cf8
+#define T48 /* 0xc4ac5665 */ (T_MASK ^ 0x3b53a99a)
+#define T49 /* 0xf4292244 */ (T_MASK ^ 0x0bd6ddbb)
+#define T50 0x432aff97
+#define T51 /* 0xab9423a7 */ (T_MASK ^ 0x546bdc58)
+#define T52 /* 0xfc93a039 */ (T_MASK ^ 0x036c5fc6)
+#define T53 0x655b59c3
+#define T54 /* 0x8f0ccc92 */ (T_MASK ^ 0x70f3336d)
+#define T55 /* 0xffeff47d */ (T_MASK ^ 0x00100b82)
+#define T56 /* 0x85845dd1 */ (T_MASK ^ 0x7a7ba22e)
+#define T57 0x6fa87e4f
+#define T58 /* 0xfe2ce6e0 */ (T_MASK ^ 0x01d3191f)
+#define T59 /* 0xa3014314 */ (T_MASK ^ 0x5cfebceb)
+#define T60 0x4e0811a1
+#define T61 /* 0xf7537e82 */ (T_MASK ^ 0x08ac817d)
+#define T62 /* 0xbd3af235 */ (T_MASK ^ 0x42c50dca)
+#define T63 0x2ad7d2bb
+#define T64 /* 0xeb86d391 */ (T_MASK ^ 0x14792c6e)
+
+
+static void
+md5_process(md5_state_t *pms, const md5_byte_t *data /*[64]*/)
+{
+ md5_word_t
+ a = pms->abcd[0], b = pms->abcd[1],
+ c = pms->abcd[2], d = pms->abcd[3];
+ md5_word_t t;
+#if BYTE_ORDER > 0
+ /* Define storage only for big-endian CPUs. */
+ md5_word_t X[16];
+#else
+ /* Define storage for little-endian or both types of CPUs. */
+ md5_word_t xbuf[16];
+ const md5_word_t *X;
+#endif
+
+ PJ_CHECK_STACK();
+
+ {
+#if BYTE_ORDER == 0
+ /*
+ * Determine dynamically whether this is a big-endian or
+ * little-endian machine, since we can use a more efficient
+ * algorithm on the latter.
+ */
+ static const int w = 1;
+
+ if (*((const md5_byte_t *)&w)) /* dynamic little-endian */
+#endif
+#if BYTE_ORDER <= 0 /* little-endian */
+ {
+ /*
+ * On little-endian machines, we can process properly aligned
+ * data without copying it.
+ */
+ if (!((data - (const md5_byte_t *)0) & 3)) {
+ /* data are properly aligned */
+ X = (const md5_word_t *)data;
+ } else {
+ /* not aligned */
+ memcpy(xbuf, data, 64);
+ X = xbuf;
+ }
+ }
+#endif
+#if BYTE_ORDER == 0
+ else /* dynamic big-endian */
+#endif
+#if BYTE_ORDER >= 0 /* big-endian */
+ {
+ /*
+ * On big-endian machines, we must arrange the bytes in the
+ * right order.
+ */
+ const md5_byte_t *xp = data;
+ int i;
+
+# if BYTE_ORDER == 0
+ X = xbuf; /* (dynamic only) */
+# else
+# define xbuf X /* (static only) */
+# endif
+ for (i = 0; i < 16; ++i, xp += 4)
+ xbuf[i] = xp[0] + (xp[1] << 8) + (xp[2] << 16) + (xp[3] << 24);
+ }
+#endif
+ }
+
+#define ROTATE_LEFT(x, n) (((x) << (n)) | ((x) >> (32 - (n))))
+
+ /* Round 1. */
+ /* Let [abcd k s i] denote the operation
+ a = b + ((a + F(b,c,d) + X[k] + T[i]) <<< s). */
+#define F(x, y, z) (((x) & (y)) | (~(x) & (z)))
+#define SET(a, b, c, d, k, s, Ti)\
+ t = a + F(b,c,d) + X[k] + Ti;\
+ a = ROTATE_LEFT(t, s) + b
+ /* Do the following 16 operations. */
+ SET(a, b, c, d, 0, 7, T1);
+ SET(d, a, b, c, 1, 12, T2);
+ SET(c, d, a, b, 2, 17, T3);
+ SET(b, c, d, a, 3, 22, T4);
+ SET(a, b, c, d, 4, 7, T5);
+ SET(d, a, b, c, 5, 12, T6);
+ SET(c, d, a, b, 6, 17, T7);
+ SET(b, c, d, a, 7, 22, T8);
+ SET(a, b, c, d, 8, 7, T9);
+ SET(d, a, b, c, 9, 12, T10);
+ SET(c, d, a, b, 10, 17, T11);
+ SET(b, c, d, a, 11, 22, T12);
+ SET(a, b, c, d, 12, 7, T13);
+ SET(d, a, b, c, 13, 12, T14);
+ SET(c, d, a, b, 14, 17, T15);
+ SET(b, c, d, a, 15, 22, T16);
+#undef SET
+
+ /* Round 2. */
+ /* Let [abcd k s i] denote the operation
+ a = b + ((a + G(b,c,d) + X[k] + T[i]) <<< s). */
+#define G(x, y, z) (((x) & (z)) | ((y) & ~(z)))
+#define SET(a, b, c, d, k, s, Ti)\
+ t = a + G(b,c,d) + X[k] + Ti;\
+ a = ROTATE_LEFT(t, s) + b
+ /* Do the following 16 operations. */
+ SET(a, b, c, d, 1, 5, T17);
+ SET(d, a, b, c, 6, 9, T18);
+ SET(c, d, a, b, 11, 14, T19);
+ SET(b, c, d, a, 0, 20, T20);
+ SET(a, b, c, d, 5, 5, T21);
+ SET(d, a, b, c, 10, 9, T22);
+ SET(c, d, a, b, 15, 14, T23);
+ SET(b, c, d, a, 4, 20, T24);
+ SET(a, b, c, d, 9, 5, T25);
+ SET(d, a, b, c, 14, 9, T26);
+ SET(c, d, a, b, 3, 14, T27);
+ SET(b, c, d, a, 8, 20, T28);
+ SET(a, b, c, d, 13, 5, T29);
+ SET(d, a, b, c, 2, 9, T30);
+ SET(c, d, a, b, 7, 14, T31);
+ SET(b, c, d, a, 12, 20, T32);
+#undef SET
+
+ /* Round 3. */
+ /* Let [abcd k s t] denote the operation
+ a = b + ((a + H(b,c,d) + X[k] + T[i]) <<< s). */
+#define H(x, y, z) ((x) ^ (y) ^ (z))
+#define SET(a, b, c, d, k, s, Ti)\
+ t = a + H(b,c,d) + X[k] + Ti;\
+ a = ROTATE_LEFT(t, s) + b
+ /* Do the following 16 operations. */
+ SET(a, b, c, d, 5, 4, T33);
+ SET(d, a, b, c, 8, 11, T34);
+ SET(c, d, a, b, 11, 16, T35);
+ SET(b, c, d, a, 14, 23, T36);
+ SET(a, b, c, d, 1, 4, T37);
+ SET(d, a, b, c, 4, 11, T38);
+ SET(c, d, a, b, 7, 16, T39);
+ SET(b, c, d, a, 10, 23, T40);
+ SET(a, b, c, d, 13, 4, T41);
+ SET(d, a, b, c, 0, 11, T42);
+ SET(c, d, a, b, 3, 16, T43);
+ SET(b, c, d, a, 6, 23, T44);
+ SET(a, b, c, d, 9, 4, T45);
+ SET(d, a, b, c, 12, 11, T46);
+ SET(c, d, a, b, 15, 16, T47);
+ SET(b, c, d, a, 2, 23, T48);
+#undef SET
+
+ /* Round 4. */
+ /* Let [abcd k s t] denote the operation
+ a = b + ((a + I(b,c,d) + X[k] + T[i]) <<< s). */
+#define I(x, y, z) ((y) ^ ((x) | ~(z)))
+#define SET(a, b, c, d, k, s, Ti)\
+ t = a + I(b,c,d) + X[k] + Ti;\
+ a = ROTATE_LEFT(t, s) + b
+ /* Do the following 16 operations. */
+ SET(a, b, c, d, 0, 6, T49);
+ SET(d, a, b, c, 7, 10, T50);
+ SET(c, d, a, b, 14, 15, T51);
+ SET(b, c, d, a, 5, 21, T52);
+ SET(a, b, c, d, 12, 6, T53);
+ SET(d, a, b, c, 3, 10, T54);
+ SET(c, d, a, b, 10, 15, T55);
+ SET(b, c, d, a, 1, 21, T56);
+ SET(a, b, c, d, 8, 6, T57);
+ SET(d, a, b, c, 15, 10, T58);
+ SET(c, d, a, b, 6, 15, T59);
+ SET(b, c, d, a, 13, 21, T60);
+ SET(a, b, c, d, 4, 6, T61);
+ SET(d, a, b, c, 11, 10, T62);
+ SET(c, d, a, b, 2, 15, T63);
+ SET(b, c, d, a, 9, 21, T64);
+#undef SET
+
+ /* Then perform the following additions. (That is increment each
+ of the four registers by the value it had before this block
+ was started.) */
+ pms->abcd[0] += a;
+ pms->abcd[1] += b;
+ pms->abcd[2] += c;
+ pms->abcd[3] += d;
+}
+
+void
+md5_init(md5_state_t *pms)
+{
+ PJ_CHECK_STACK();
+
+ pms->count[0] = pms->count[1] = 0;
+ pms->abcd[0] = 0x67452301;
+ pms->abcd[1] = /*0xefcdab89*/ T_MASK ^ 0x10325476;
+ pms->abcd[2] = /*0x98badcfe*/ T_MASK ^ 0x67452301;
+ pms->abcd[3] = 0x10325476;
+}
+
+void
+md5_append(md5_state_t *pms, const md5_byte_t *data, int nbytes)
+{
+ const md5_byte_t *p = data;
+ int left = nbytes;
+ int offset = (pms->count[0] >> 3) & 63;
+ md5_word_t nbits = (md5_word_t)(nbytes << 3);
+
+ PJ_CHECK_STACK();
+
+ if (nbytes <= 0)
+ return;
+
+ /* Update the message length. */
+ pms->count[1] += nbytes >> 29;
+ pms->count[0] += nbits;
+ if (pms->count[0] < nbits)
+ pms->count[1]++;
+
+ /* Process an initial partial block. */
+ if (offset) {
+ int copy = (offset + nbytes > 64 ? 64 - offset : nbytes);
+
+ memcpy(pms->buf + offset, p, copy);
+ if (offset + copy < 64)
+ return;
+ p += copy;
+ left -= copy;
+ md5_process(pms, pms->buf);
+ }
+
+ /* Process full blocks. */
+ for (; left >= 64; p += 64, left -= 64)
+ md5_process(pms, p);
+
+ /* Process a final partial block. */
+ if (left)
+ memcpy(pms->buf, p, left);
+}
+
+void
+md5_finish(md5_state_t *pms, md5_byte_t digest[16])
+{
+ static const md5_byte_t pad[64] = {
+ 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+ };
+ md5_byte_t data[8];
+ int i;
+
+ PJ_CHECK_STACK();
+
+ /* Save the length before padding. */
+ for (i = 0; i < 8; ++i)
+ data[i] = (md5_byte_t)(pms->count[i >> 2] >> ((i & 3) << 3));
+ /* Pad to 56 bytes mod 64. */
+ md5_append(pms, pad, ((55 - (pms->count[0] >> 3)) & 63) + 1);
+ /* Append the length. */
+ md5_append(pms, data, 8);
+ for (i = 0; i < 16; ++i)
+ digest[i] = (md5_byte_t)(pms->abcd[i >> 2] >> ((i & 3) << 3));
+}
+
diff --git a/pjlib-util/src/pjlib-util/scanner.c b/pjlib-util/src/pjlib-util/scanner.c
index 0e753df..edd8de5 100644
--- a/pjlib-util/src/pjlib-util/scanner.c
+++ b/pjlib-util/src/pjlib-util/scanner.c
@@ -1,26 +1,47 @@
-/* $Id$
- */
-#include <pjlib-util/scanner.h>
-#include <pj/string.h>
-#include <pj/except.h>
-#include <pj/os.h>
+/* $Id$
+ */
+/*
+ * PJLIB - PJ Foundation Library
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ * Benny Prijono <bennylp@bulukucing.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; 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)
-{
+
+#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;
-}
+ cis_buf->use_mask = 0;
+}
PJ_DEF(pj_status_t) pj_cis_init(pj_cis_buf_t *cis_buf, pj_cis_t *cis)
{
@@ -57,521 +78,521 @@
return PJ_SUCCESS;
}
-
-PJ_DEF(void) pj_cis_add_range(pj_cis_t *cis, int cstart, int cend)
-{
+
+PJ_DEF(void) pj_cis_add_range(pj_cis_t *cis, int cstart, int cend)
+{
while (cstart != cend) {
- PJ_CIS_SET(cis, cstart);
+ 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_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) {
+ }
+}
+
+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;
-}
-
-
+ 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/stun.c b/pjlib-util/src/pjlib-util/stun.c
index 4b41753..40566a6 100644
--- a/pjlib-util/src/pjlib-util/stun.c
+++ b/pjlib-util/src/pjlib-util/stun.c
@@ -1,113 +1,134 @@
-/* $Id$
- */
-#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$
+ */
+/*
+ * PJLIB - PJ Foundation Library
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ * Benny Prijono <bennylp@bulukucing.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; 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 6bfca13..5718532 100644
--- a/pjlib-util/src/pjlib-util/stun_client.c
+++ b/pjlib-util/src/pjlib-util/stun_client.c
@@ -1,261 +1,282 @@
-/* $Id$
- */
-#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$
+ */
+/*
+ * PJLIB - PJ Foundation Library
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ * Benny Prijono <bennylp@bulukucing.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; 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 adf7219..b25fa0d 100644
--- a/pjlib-util/src/pjlib-util/symbols.c
+++ b/pjlib-util/src/pjlib-util/symbols.c
@@ -1,4 +1,25 @@
/* $Id$ */
+/*
+ * PJLIB - PJ Foundation Library
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ * Benny Prijono <bennylp@bulukucing.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; 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>
diff --git a/pjlib-util/src/pjlib-util/xml.c b/pjlib-util/src/pjlib-util/xml.c
index 600666d..dd9fb4d 100644
--- a/pjlib-util/src/pjlib-util/xml.c
+++ b/pjlib-util/src/pjlib-util/xml.c
@@ -1,380 +1,401 @@
-/* $Id$
- */
-#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$
+ */
+/*
+ * PJLIB - PJ Foundation Library
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ * Benny Prijono <bennylp@bulukucing.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; 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 e45810c..1c35123 100644
--- a/pjlib/include/pj++/file.hpp
+++ b/pjlib/include/pj++/file.hpp
@@ -1,171 +1,192 @@
-/* $Id$
- */
-#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$
+ */
+/*
+ * PJLIB - PJ Foundation Library
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ * Benny Prijono <bennylp@bulukucing.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; 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 691ac3a..c362625 100644
--- a/pjlib/include/pj++/hash.hpp
+++ b/pjlib/include/pj++/hash.hpp
@@ -1,139 +1,160 @@
-/* $Id$
- */
-#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$
+ */
+/*
+ * PJLIB - PJ Foundation Library
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ * Benny Prijono <bennylp@bulukucing.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; 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 c3a8f38..363c1fa 100644
--- a/pjlib/include/pj++/list.hpp
+++ b/pjlib/include/pj++/list.hpp
@@ -1,311 +1,332 @@
-/* $Id$
- */
-#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$
+ */
+/*
+ * PJLIB - PJ Foundation Library
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ * Benny Prijono <bennylp@bulukucing.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; 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 95ea63b..cf5a57d 100644
--- a/pjlib/include/pj++/lock.hpp
+++ b/pjlib/include/pj++/lock.hpp
@@ -1,132 +1,153 @@
-/* $Id$
- */
-#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$
+ */
+/*
+ * PJLIB - PJ Foundation Library
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ * Benny Prijono <bennylp@bulukucing.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; 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 461a36e..65553b0 100644
--- a/pjlib/include/pj++/os.hpp
+++ b/pjlib/include/pj++/os.hpp
@@ -1,788 +1,809 @@
-/* $Id$
- */
-#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$
+ */
+/*
+ * PJLIB - PJ Foundation Library
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ * Benny Prijono <bennylp@bulukucing.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; 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 1fa7575..2236511 100644
--- a/pjlib/include/pj++/pool.hpp
+++ b/pjlib/include/pj++/pool.hpp
@@ -1,254 +1,275 @@
-/* $Id$
- */
-#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$
+ */
+/*
+ * PJLIB - PJ Foundation Library
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ * Benny Prijono <bennylp@bulukucing.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; 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 73be85f..7c4f992 100644
--- a/pjlib/include/pj++/proactor.hpp
+++ b/pjlib/include/pj++/proactor.hpp
@@ -1,502 +1,523 @@
-/* $Id$
- */
-#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$
+ */
+/*
+ * PJLIB - PJ Foundation Library
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ * Benny Prijono <bennylp@bulukucing.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; 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 8a3d989..2131d4d 100644
--- a/pjlib/include/pj++/scanner.hpp
+++ b/pjlib/include/pj++/scanner.hpp
@@ -1,173 +1,194 @@
-/* $Id$
- */
-#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$
+ */
+/*
+ * PJLIB - PJ Foundation Library
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ * Benny Prijono <bennylp@bulukucing.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; 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 f1a1e45..3a401b0 100644
--- a/pjlib/include/pj++/sock.hpp
+++ b/pjlib/include/pj++/sock.hpp
@@ -1,427 +1,448 @@
-/* $Id$
- */
-#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$
+ */
+/*
+ * PJLIB - PJ Foundation Library
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ * Benny Prijono <bennylp@bulukucing.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; 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 6be1e50..dbb2d57 100644
--- a/pjlib/include/pj++/string.hpp
+++ b/pjlib/include/pj++/string.hpp
@@ -1,409 +1,430 @@
-/* $Id$
- */
-#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$
+ */
+/*
+ * PJLIB - PJ Foundation Library
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ * Benny Prijono <bennylp@bulukucing.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; 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 fbb15f7..7a5618d 100644
--- a/pjlib/include/pj++/timer.hpp
+++ b/pjlib/include/pj++/timer.hpp
@@ -1,181 +1,202 @@
-/* $Id$
- */
-#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$
+ */
+/*
+ * PJLIB - PJ Foundation Library
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ * Benny Prijono <bennylp@bulukucing.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; 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 f4fe8fc..d58d6a0 100644
--- a/pjlib/include/pj++/tree.hpp
+++ b/pjlib/include/pj++/tree.hpp
@@ -1,112 +1,133 @@
-/* $Id$
- */
-#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$
+ */
+/*
+ * PJLIB - PJ Foundation Library
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ * Benny Prijono <bennylp@bulukucing.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; 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 3155eb0..18a2f52 100644
--- a/pjlib/include/pj++/types.hpp
+++ b/pjlib/include/pj++/types.hpp
@@ -1,144 +1,165 @@
-/* $Id$
- */
-#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$
+ */
+/*
+ * PJLIB - PJ Foundation Library
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ * Benny Prijono <bennylp@bulukucing.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; 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 1da9208..3a5c1eb 100644
--- a/pjlib/include/pj/addr_resolv.h
+++ b/pjlib/include/pj/addr_resolv.h
@@ -1,77 +1,98 @@
-/* $Id$
- */
-
-#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$
+ */
+/*
+ * PJLIB - PJ Foundation Library
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ * Benny Prijono <bennylp@bulukucing.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; 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 eaec726..dc896c2 100644
--- a/pjlib/include/pj/array.h
+++ b/pjlib/include/pj/array.h
@@ -1,80 +1,101 @@
-/* $Id$
- *
- */
-#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$
+ *
+ */
+/*
+ * PJLIB - PJ Foundation Library
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ * Benny Prijono <bennylp@bulukucing.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; 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 d10c864..c6d3770 100644
--- a/pjlib/include/pj/assert.h
+++ b/pjlib/include/pj/assert.h
@@ -1,58 +1,79 @@
-/* $Id$
- *
- */
-#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$
+ *
+ */
+/*
+ * PJLIB - PJ Foundation Library
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ * Benny Prijono <bennylp@bulukucing.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; 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 586cafb..2cc1794 100644
--- a/pjlib/include/pj/compat/assert.h
+++ b/pjlib/include/pj/compat/assert.h
@@ -1,40 +1,61 @@
-/* $Id$
- *
- */
-/* $Log: /pjproject-0.3/pjlib/src/pj/compat/assert.h $
- *
- * 3 9/22/05 10:31a Bennylp
- * Moving all *.h files to include/.
- *
- * 2 9/21/05 1:39p Bennylp
- * Periodic checkin for backup.
- *
- * 1 9/17/05 10:36a Bennylp
- * Created.
- *
- */
-#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$
+ *
+ */
+/*
+ * PJLIB - PJ Foundation Library
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ * Benny Prijono <bennylp@bulukucing.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+/* $Log: /pjproject-0.3/pjlib/src/pj/compat/assert.h $
+ *
+ * 3 9/22/05 10:31a Bennylp
+ * Moving all *.h files to include/.
+ *
+ * 2 9/21/05 1:39p Bennylp
+ * Periodic checkin for backup.
+ *
+ * 1 9/17/05 10:36a Bennylp
+ * Created.
+ *
+ */
+#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 913f691..96e9157 100644
--- a/pjlib/include/pj/compat/cc_gcc.h
+++ b/pjlib/include/pj/compat/cc_gcc.h
@@ -1,34 +1,55 @@
-/* $Id$
- *
- */
-/* $Log: /pjproject-0.3/pjlib/src/pj/compat/cc_gcc.h $
- *
- * 2 9/17/05 10:37a Bennylp
- * Major reorganization towards version 0.3.
- *
- */
-#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$
+ *
+ */
+/*
+ * PJLIB - PJ Foundation Library
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ * Benny Prijono <bennylp@bulukucing.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+/* $Log: /pjproject-0.3/pjlib/src/pj/compat/cc_gcc.h $
+ *
+ * 2 9/17/05 10:37a Bennylp
+ * Major reorganization towards version 0.3.
+ *
+ */
+#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 f8407e3..d67210b 100644
--- a/pjlib/include/pj/compat/cc_msvc.h
+++ b/pjlib/include/pj/compat/cc_msvc.h
@@ -1,42 +1,63 @@
-/* $Id$
- *
- */
-/* $Log: /pjproject-0.3/pjlib/include/pj/compat/cc_msvc.h $
- *
- * 3 10/14/05 12:26a Bennylp
- * Finished error code framework, some fixes in ioqueue, etc. Pretty
- * major.
- *
- * 2 9/17/05 10:37a Bennylp
- * Major reorganization towards version 0.3.
- *
- */
-#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$
+ *
+ */
+/*
+ * PJLIB - PJ Foundation Library
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ * Benny Prijono <bennylp@bulukucing.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+/* $Log: /pjproject-0.3/pjlib/include/pj/compat/cc_msvc.h $
+ *
+ * 3 10/14/05 12:26a Bennylp
+ * Finished error code framework, some fixes in ioqueue, etc. Pretty
+ * major.
+ *
+ * 2 9/17/05 10:37a Bennylp
+ * Major reorganization towards version 0.3.
+ *
+ */
+#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 d01cc19..d8ddac1 100644
--- a/pjlib/include/pj/compat/ctype.h
+++ b/pjlib/include/pj/compat/ctype.h
@@ -1,44 +1,65 @@
-/* $Id$
- *
- */
-/* $Log: /pjproject-0.3/pjlib/include/pj/compat/ctype.h $
- *
- * 3 10/14/05 12:26a Bennylp
- * Finished error code framework, some fixes in ioqueue, etc. Pretty
- * major.
- *
- * 2 9/22/05 10:31a Bennylp
- * Moving all *.h files to include/.
- *
- * 1 9/17/05 10:36a Bennylp
- * Created.
- *
- */
-#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$
+ *
+ */
+/*
+ * PJLIB - PJ Foundation Library
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ * Benny Prijono <bennylp@bulukucing.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+/* $Log: /pjproject-0.3/pjlib/include/pj/compat/ctype.h $
+ *
+ * 3 10/14/05 12:26a Bennylp
+ * Finished error code framework, some fixes in ioqueue, etc. Pretty
+ * major.
+ *
+ * 2 9/22/05 10:31a Bennylp
+ * Moving all *.h files to include/.
+ *
+ * 1 9/17/05 10:36a Bennylp
+ * Created.
+ *
+ */
+#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 7ee8324..be7a753 100644
--- a/pjlib/include/pj/compat/errno.h
+++ b/pjlib/include/pj/compat/errno.h
@@ -1,29 +1,50 @@
-/* $Id$
- *
- */
-#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$
+ *
+ */
+/*
+ * PJLIB - PJ Foundation Library
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ * Benny Prijono <bennylp@bulukucing.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; 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 431283d..ba48e43 100644
--- a/pjlib/include/pj/compat/high_precision.h
+++ b/pjlib/include/pj/compat/high_precision.h
@@ -1,87 +1,106 @@
-/* $Id$
- *
- */
-#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$ */
+/*
+ * PJLIB - PJ Foundation Library
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ * Benny Prijono <bennylp@bulukucing.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; 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 38569dc..3a1376e 100644
--- a/pjlib/include/pj/compat/m_alpha.h
+++ b/pjlib/include/pj/compat/m_alpha.h
@@ -1,25 +1,46 @@
-/* $Id$
- *
- */
-/*
- * $Log: /pjproject-0.3/pjlib/include/pj/compat/m_alpha.h $
- *
- * 1 10/29/05 5:23p Bennylp
- * Created.
- *
- */
-#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$
+ *
+ */
+/*
+ * PJLIB - PJ Foundation Library
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ * Benny Prijono <bennylp@bulukucing.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+/*
+ * $Log: /pjproject-0.3/pjlib/include/pj/compat/m_alpha.h $
+ *
+ * 1 10/29/05 5:23p Bennylp
+ * Created.
+ *
+ */
+#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 8eaf19e..0239c34 100644
--- a/pjlib/include/pj/compat/m_i386.h
+++ b/pjlib/include/pj/compat/m_i386.h
@@ -1,28 +1,49 @@
-/* $Id$
- *
- */
-/* $Log: /pjproject-0.3/pjlib/src/pj/compat/m_i386.h $
- *
- * 3 9/21/05 1:39p Bennylp
- * Periodic checkin for backup.
- *
- * 2 9/17/05 10:37a Bennylp
- * Major reorganization towards version 0.3.
- *
- */
-#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$
+ *
+ */
+/*
+ * PJLIB - PJ Foundation Library
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ * Benny Prijono <bennylp@bulukucing.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+/* $Log: /pjproject-0.3/pjlib/src/pj/compat/m_i386.h $
+ *
+ * 3 9/21/05 1:39p Bennylp
+ * Periodic checkin for backup.
+ *
+ * 2 9/17/05 10:37a Bennylp
+ * Major reorganization towards version 0.3.
+ *
+ */
+#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 eb8db6e..caed75e 100644
--- a/pjlib/include/pj/compat/m_m68k.h
+++ b/pjlib/include/pj/compat/m_m68k.h
@@ -1,23 +1,44 @@
-/* $Id$
- *
- */
-/* $Log: /pjproject-0.3/pjlib/src/pj/compat/m_m68k.h $
- *
- * 2 9/17/05 10:37a Bennylp
- * Major reorganization towards version 0.3.
- *
- */
-#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$
+ *
+ */
+/*
+ * PJLIB - PJ Foundation Library
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ * Benny Prijono <bennylp@bulukucing.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+/* $Log: /pjproject-0.3/pjlib/src/pj/compat/m_m68k.h $
+ *
+ * 2 9/17/05 10:37a Bennylp
+ * Major reorganization towards version 0.3.
+ *
+ */
+#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 5fd6240..3cb5d0c 100644
--- a/pjlib/include/pj/compat/m_sparc.h
+++ b/pjlib/include/pj/compat/m_sparc.h
@@ -1,23 +1,44 @@
-/* $Id$
- *
- */
-/*
- *$Log: $
- *
- */
-#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$
+ *
+ */
+/*
+ * PJLIB - PJ Foundation Library
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ * Benny Prijono <bennylp@bulukucing.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+/*
+ *$Log: $
+ *
+ */
+#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 78ed9c9..4b5da78 100644
--- a/pjlib/include/pj/compat/malloc.h
+++ b/pjlib/include/pj/compat/malloc.h
@@ -1,27 +1,48 @@
-/* $Id$
- *
- */
-/* $Log: /pjproject-0.3/pjlib/src/pj/compat/malloc.h $
- *
- * 2 9/17/05 10:37a Bennylp
- * Major reorganization towards version 0.3.
- *
- * 1 9/16/05 10:02p Bennylp
- * Created.
- *
- */
-#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$
+ *
+ */
+/*
+ * PJLIB - PJ Foundation Library
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ * Benny Prijono <bennylp@bulukucing.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+/* $Log: /pjproject-0.3/pjlib/src/pj/compat/malloc.h $
+ *
+ * 2 9/17/05 10:37a Bennylp
+ * Major reorganization towards version 0.3.
+ *
+ * 1 9/16/05 10:02p Bennylp
+ * Created.
+ *
+ */
+#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 efb661a..8c4d74b 100644
--- a/pjlib/include/pj/compat/os_linux.h
+++ b/pjlib/include/pj/compat/os_linux.h
@@ -1,86 +1,107 @@
-/* $Id$
- *
- */
-/* $Log: /pjproject-0.3/pjlib/include/pj/compat/os_linux.h $
- *
- * 6 10/29/05 11:51a Bennylp
- * Version 0.3-pre2.
- *
- * 5 10/14/05 12:26a Bennylp
- * Finished error code framework, some fixes in ioqueue, etc. Pretty
- * major.
- *
- * 4 9/22/05 10:31a Bennylp
- * Moving all *.h files to include/.
- *
- * 3 9/21/05 1:39p Bennylp
- * Periodic checkin for backup.
- *
- * 2 9/17/05 10:37a Bennylp
- * Major reorganization towards version 0.3.
- *
- */
-#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$
+ *
+ */
+/*
+ * PJLIB - PJ Foundation Library
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ * Benny Prijono <bennylp@bulukucing.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+/* $Log: /pjproject-0.3/pjlib/include/pj/compat/os_linux.h $
+ *
+ * 6 10/29/05 11:51a Bennylp
+ * Version 0.3-pre2.
+ *
+ * 5 10/14/05 12:26a Bennylp
+ * Finished error code framework, some fixes in ioqueue, etc. Pretty
+ * major.
+ *
+ * 4 9/22/05 10:31a Bennylp
+ * Moving all *.h files to include/.
+ *
+ * 3 9/21/05 1:39p Bennylp
+ * Periodic checkin for backup.
+ *
+ * 2 9/17/05 10:37a Bennylp
+ * Major reorganization towards version 0.3.
+ *
+ */
+#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 ccae341..5f78456 100644
--- a/pjlib/include/pj/compat/os_linux_kernel.h
+++ b/pjlib/include/pj/compat/os_linux_kernel.h
@@ -1,103 +1,124 @@
-/* $Id$
- *
- */
-/* $Log: /pjproject-0.3/pjlib/include/pj/compat/os_linux_kernel.h $
- *
- * 4 10/29/05 11:51a Bennylp
- * Version 0.3-pre2.
- *
- * 3 10/14/05 12:26a Bennylp
- * Finished error code framework, some fixes in ioqueue, etc. Pretty
- * major.
- *
- * 2 9/22/05 10:31a Bennylp
- * Moving all *.h files to include/.
- *
- * 1 9/21/05 1:38p Bennylp
- * Created.
- *
- */
-#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$
+ *
+ */
+/*
+ * PJLIB - PJ Foundation Library
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ * Benny Prijono <bennylp@bulukucing.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+/* $Log: /pjproject-0.3/pjlib/include/pj/compat/os_linux_kernel.h $
+ *
+ * 4 10/29/05 11:51a Bennylp
+ * Version 0.3-pre2.
+ *
+ * 3 10/14/05 12:26a Bennylp
+ * Finished error code framework, some fixes in ioqueue, etc. Pretty
+ * major.
+ *
+ * 2 9/22/05 10:31a Bennylp
+ * Moving all *.h files to include/.
+ *
+ * 1 9/21/05 1:38p Bennylp
+ * Created.
+ *
+ */
+#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 e2ac4b5..7fb5647 100644
--- a/pjlib/include/pj/compat/os_palmos.h
+++ b/pjlib/include/pj/compat/os_palmos.h
@@ -1,49 +1,70 @@
-/* $Id$
- *
- */
-/* $Log: /pjproject-0.3/pjlib/src/pj/compat/os_palmos.h $
- *
- * 3 9/21/05 1:39p Bennylp
- * Periodic checkin for backup.
- *
- * 2 9/17/05 10:37a Bennylp
- * Major reorganization towards version 0.3.
- *
- */
-#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
+/* $Id$
+ *
+ */
+/*
+ * PJLIB - PJ Foundation Library
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ * Benny Prijono <bennylp@bulukucing.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+/* $Log: /pjproject-0.3/pjlib/src/pj/compat/os_palmos.h $
+ *
+ * 3 9/21/05 1:39p Bennylp
+ * Periodic checkin for backup.
+ *
+ * 2 9/17/05 10:37a Bennylp
+ * Major reorganization towards version 0.3.
+ *
+ */
+#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.
@@ -59,13 +80,13 @@
* 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__ */
+
+/* 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 87c408a..d2406e5 100644
--- a/pjlib/include/pj/compat/os_sunos.h
+++ b/pjlib/include/pj/compat/os_sunos.h
@@ -1,73 +1,94 @@
-/* $Id$
- *
- */
-/* $Log: $
- *
- */
-#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$
+ *
+ */
+/*
+ * PJLIB - PJ Foundation Library
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ * Benny Prijono <bennylp@bulukucing.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+/* $Log: $
+ *
+ */
+#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 e8391b9..b128060 100644
--- a/pjlib/include/pj/compat/os_win32.h
+++ b/pjlib/include/pj/compat/os_win32.h
@@ -1,89 +1,110 @@
-/* $Id$
- *
- */
-/* $Log: /pjproject-0.3/pjlib/include/pj/compat/os_win32.h $
- *
- * 6 10/29/05 11:51a Bennylp
- * Version 0.3-pre2.
- *
- * 5 10/14/05 12:26a Bennylp
- * Finished error code framework, some fixes in ioqueue, etc. Pretty
- * major.
- *
- * 4 9/22/05 10:31a Bennylp
- * Moving all *.h files to include/.
- *
- * 3 9/21/05 1:39p Bennylp
- * Periodic checkin for backup.
- *
- * 2 9/17/05 10:37a Bennylp
- * Major reorganization towards version 0.3.
- *
- */
-#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$
+ *
+ */
+/*
+ * PJLIB - PJ Foundation Library
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ * Benny Prijono <bennylp@bulukucing.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+/* $Log: /pjproject-0.3/pjlib/include/pj/compat/os_win32.h $
+ *
+ * 6 10/29/05 11:51a Bennylp
+ * Version 0.3-pre2.
+ *
+ * 5 10/14/05 12:26a Bennylp
+ * Finished error code framework, some fixes in ioqueue, etc. Pretty
+ * major.
+ *
+ * 4 9/22/05 10:31a Bennylp
+ * Moving all *.h files to include/.
+ *
+ * 3 9/21/05 1:39p Bennylp
+ * Periodic checkin for backup.
+ *
+ * 2 9/17/05 10:37a Bennylp
+ * Major reorganization towards version 0.3.
+ *
+ */
+#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 b65bf10..af4d915 100644
--- a/pjlib/include/pj/compat/rand.h
+++ b/pjlib/include/pj/compat/rand.h
@@ -1,67 +1,88 @@
-/* $Id$
- *
- */
-/* $Log: /pjproject-0.3/pjlib/include/pj/compat/rand.h $
- *
- * 3 10/14/05 12:26a Bennylp
- * Finished error code framework, some fixes in ioqueue, etc. Pretty
- * major.
- *
- * 2 9/21/05 1:39p Bennylp
- * Periodic checkin for backup.
- *
- * 1 9/17/05 10:36a Bennylp
- * Created.
- *
- */
-#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$
+ *
+ */
+/*
+ * PJLIB - PJ Foundation Library
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ * Benny Prijono <bennylp@bulukucing.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+/* $Log: /pjproject-0.3/pjlib/include/pj/compat/rand.h $
+ *
+ * 3 10/14/05 12:26a Bennylp
+ * Finished error code framework, some fixes in ioqueue, etc. Pretty
+ * major.
+ *
+ * 2 9/21/05 1:39p Bennylp
+ * Periodic checkin for backup.
+ *
+ * 1 9/17/05 10:36a Bennylp
+ * Created.
+ *
+ */
+#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 fb0c7d6..f018cd9 100644
--- a/pjlib/include/pj/compat/setjmp.h
+++ b/pjlib/include/pj/compat/setjmp.h
@@ -1,91 +1,112 @@
-/* $Id$
- *
- */
-/* $Log: /pjproject-0.3/pjlib/include/pj/compat/setjmp.h $
- *
- * 4 10/14/05 12:26a Bennylp
- * Finished error code framework, some fixes in ioqueue, etc. Pretty
- * major.
- *
- * 3 9/22/05 10:31a Bennylp
- * Moving all *.h files to include/.
- *
- * 2 9/21/05 1:39p Bennylp
- * Periodic checkin for backup.
- *
- * 1 9/17/05 10:36a Bennylp
- * Created.
- *
- */
-#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$
+ *
+ */
+/*
+ * PJLIB - PJ Foundation Library
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ * Benny Prijono <bennylp@bulukucing.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+/* $Log: /pjproject-0.3/pjlib/include/pj/compat/setjmp.h $
+ *
+ * 4 10/14/05 12:26a Bennylp
+ * Finished error code framework, some fixes in ioqueue, etc. Pretty
+ * major.
+ *
+ * 3 9/22/05 10:31a Bennylp
+ * Moving all *.h files to include/.
+ *
+ * 2 9/21/05 1:39p Bennylp
+ * Periodic checkin for backup.
+ *
+ * 1 9/17/05 10:36a Bennylp
+ * Created.
+ *
+ */
+#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 bdb7e02..70ae068 100644
--- a/pjlib/include/pj/compat/size_t.h
+++ b/pjlib/include/pj/compat/size_t.h
@@ -1,25 +1,46 @@
-/* $Id$
- *
- */
-/* $Log: /pjproject-0.3/pjlib/src/pj/compat/size_t.h $
- *
- * 2 9/21/05 1:39p Bennylp
- * Periodic checkin for backup.
- *
- * 1 9/17/05 10:36a Bennylp
- * Created.
- *
- */
-#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$
+ *
+ */
+/*
+ * PJLIB - PJ Foundation Library
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ * Benny Prijono <bennylp@bulukucing.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+/* $Log: /pjproject-0.3/pjlib/src/pj/compat/size_t.h $
+ *
+ * 2 9/21/05 1:39p Bennylp
+ * Periodic checkin for backup.
+ *
+ * 1 9/17/05 10:36a Bennylp
+ * Created.
+ *
+ */
+#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 f2de0cb..2a989d1 100644
--- a/pjlib/include/pj/compat/socket.h
+++ b/pjlib/include/pj/compat/socket.h
@@ -1,131 +1,152 @@
-/* $Id$
- *
-*/
-/* $Log: /pjproject-0.3/pjlib/include/pj/compat/socket.h $
- *
- * 5 10/29/05 11:51a Bennylp
- * Version 0.3-pre2.
- *
- * 4 10/14/05 12:26a Bennylp
- * Finished error code framework, some fixes in ioqueue, etc. Pretty
- * major.
- *
- * 3 9/21/05 1:39p Bennylp
- * Periodic checkin for backup.
- *
- * 2 9/17/05 10:37a Bennylp
- * Major reorganization towards version 0.3.
- *
- */
-#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$
+ *
+*/
+/*
+ * PJLIB - PJ Foundation Library
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ * Benny Prijono <bennylp@bulukucing.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+/* $Log: /pjproject-0.3/pjlib/include/pj/compat/socket.h $
+ *
+ * 5 10/29/05 11:51a Bennylp
+ * Version 0.3-pre2.
+ *
+ * 4 10/14/05 12:26a Bennylp
+ * Finished error code framework, some fixes in ioqueue, etc. Pretty
+ * major.
+ *
+ * 3 9/21/05 1:39p Bennylp
+ * Periodic checkin for backup.
+ *
+ * 2 9/17/05 10:37a Bennylp
+ * Major reorganization towards version 0.3.
+ *
+ */
+#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 a398770..9028d85 100644
--- a/pjlib/include/pj/compat/sprintf.h
+++ b/pjlib/include/pj/compat/sprintf.h
@@ -1,33 +1,54 @@
-/* $Id$
- *
- */
-/* $Log: /pjproject-0.3/pjlib/include/pj/compat/sprintf.h $
- *
- * 2 10/14/05 12:26a Bennylp
- * Finished error code framework, some fixes in ioqueue, etc. Pretty
- * major.
- *
- * 1 9/17/05 10:36a Bennylp
- * Created.
- *
- */
-#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$
+ *
+ */
+/*
+ * PJLIB - PJ Foundation Library
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ * Benny Prijono <bennylp@bulukucing.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+/* $Log: /pjproject-0.3/pjlib/include/pj/compat/sprintf.h $
+ *
+ * 2 10/14/05 12:26a Bennylp
+ * Finished error code framework, some fixes in ioqueue, etc. Pretty
+ * major.
+ *
+ * 1 9/17/05 10:36a Bennylp
+ * Created.
+ *
+ */
+#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 87b3f2c..a4a3add 100644
--- a/pjlib/include/pj/compat/stdarg.h
+++ b/pjlib/include/pj/compat/stdarg.h
@@ -1,22 +1,43 @@
-/* $Id$
- *
- */
-/* $Log: /pjproject-0.3/pjlib/src/pj/compat/stdarg.h $
- *
- * 1 9/17/05 10:36a Bennylp
- * Created.
- *
- */
-#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$
+ *
+ */
+/*
+ * PJLIB - PJ Foundation Library
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ * Benny Prijono <bennylp@bulukucing.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+/* $Log: /pjproject-0.3/pjlib/src/pj/compat/stdarg.h $
+ *
+ * 1 9/17/05 10:36a Bennylp
+ * Created.
+ *
+ */
+#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 620b170..e9915f7 100644
--- a/pjlib/include/pj/compat/stdfileio.h
+++ b/pjlib/include/pj/compat/stdfileio.h
@@ -1,22 +1,43 @@
-/* $Id$
- *
- */
-/* $Log: /pjproject-0.3/pjlib/src/pj/compat/stdfileio.h $
- *
- * 1 9/17/05 10:36a Bennylp
- * Created.
- *
- */
-#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$
+ *
+ */
+/*
+ * PJLIB - PJ Foundation Library
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ * Benny Prijono <bennylp@bulukucing.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+/* $Log: /pjproject-0.3/pjlib/src/pj/compat/stdfileio.h $
+ *
+ * 1 9/17/05 10:36a Bennylp
+ * Created.
+ *
+ */
+#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 b891738..59d82c7 100644
--- a/pjlib/include/pj/compat/string.h
+++ b/pjlib/include/pj/compat/string.h
@@ -1,44 +1,65 @@
-/* $Id$
- *
- */
-/* $Log: /pjproject-0.3/pjlib/src/pj/compat/string.h $
- *
- * 3 9/22/05 10:31a Bennylp
- * Moving all *.h files to include/.
- *
- * 2 9/21/05 1:39p Bennylp
- * Periodic checkin for backup.
- *
- * 1 9/17/05 10:36a Bennylp
- * Created.
- *
- */
-#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
-
+/* $Id$
+ *
+ */
+/*
+ * PJLIB - PJ Foundation Library
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ * Benny Prijono <bennylp@bulukucing.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+/* $Log: /pjproject-0.3/pjlib/src/pj/compat/string.h $
+ *
+ * 3 9/22/05 10:31a Bennylp
+ * Moving all *.h files to include/.
+ *
+ * 2 9/21/05 1:39p Bennylp
+ * Periodic checkin for backup.
+ *
+ * 1 9/17/05 10:36a Bennylp
+ * Created.
+ *
+ */
+#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_strlen strlen
@@ -48,5 +69,5 @@
#define pj_native_strcasecmp strcasecmp
#define pj_native_strncasecmp strncasecmp
-
-#endif /* __PJ_COMPAT_STRING_H__ */
+
+#endif /* __PJ_COMPAT_STRING_H__ */
diff --git a/pjlib/include/pj/compat/time.h b/pjlib/include/pj/compat/time.h
index 79d0f27..9303604 100644
--- a/pjlib/include/pj/compat/time.h
+++ b/pjlib/include/pj/compat/time.h
@@ -1,27 +1,48 @@
-/* $Id$
- *
- */
-/* $Log: /pjproject-0.3/pjlib/src/pj/compat/time.h $
- *
- * 1 9/17/05 10:36a Bennylp
- * Created.
- *
- */
-#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$
+ *
+ */
+/*
+ * PJLIB - PJ Foundation Library
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ * Benny Prijono <bennylp@bulukucing.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+/* $Log: /pjproject-0.3/pjlib/src/pj/compat/time.h $
+ *
+ * 1 9/17/05 10:36a Bennylp
+ * Created.
+ *
+ */
+#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 79be197..644f365 100644
--- a/pjlib/include/pj/compat/vsprintf.h
+++ b/pjlib/include/pj/compat/vsprintf.h
@@ -1,28 +1,49 @@
-/* $Id$
- *
- */
-/* $Log: /pjproject-0.3/pjlib/src/pj/compat/vsprintf.h $
- *
- * 1 9/17/05 10:36a Bennylp
- * Created.
- *
- */
-#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$
+ *
+ */
+/*
+ * PJLIB - PJ Foundation Library
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ * Benny Prijono <bennylp@bulukucing.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+/* $Log: /pjproject-0.3/pjlib/src/pj/compat/vsprintf.h $
+ *
+ * 1 9/17/05 10:36a Bennylp
+ * Created.
+ *
+ */
+#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/config.h b/pjlib/include/pj/config.h
index 0465ac9..cb7cd59 100644
--- a/pjlib/include/pj/config.h
+++ b/pjlib/include/pj/config.h
@@ -1,408 +1,429 @@
-/* $Id$
- *
- */
-
-#ifndef __PJ_CONFIG_H__
-#define __PJ_CONFIG_H__
-
-/**
- * @file config.h
- * @brief PJLIB Main configuration settings.
- */
-
-/********************************************************************
- * Include compiler specific configuration.
- */
-#if defined(_MSC_VER)
-# include <pj/compat/cc_msvc.h>
-#elif defined(__GNUC__)
-# include <pj/compat/cc_gcc.h>
-#else
-# error "Unknown compiler."
-#endif
-
-
-/********************************************************************
- * Include target OS specific configuration.
- */
-#if defined(PJ_WIN32) && PJ_WIN32!=0
-# include <pj/compat/os_win32.h>
-#elif defined(PJ_LINUX) && PJ_LINUX!=0
-# include <pj/compat/os_linux.h>
-#elif defined(PJ_LINUX_KERNEL) && PJ_LINUX_KERNEL!=0
-# include <pj/compat/os_linux_kernel.h>
-#elif defined(PJ_PALMOS) && PJ_PALMOS!=0
-# include <pj/compat/os_palmos.h>
-#elif defined(PJ_SUNOS) && PJ_SUNOS!=0
-# include <pj/compat/os_sunos.h>
-#else
-# error "Please specify target os."
-#endif
-
-
-/********************************************************************
- * Target machine specific configuration.
- */
-#if defined (PJ_M_I386) && PJ_M_I386 != 0
-# include <pj/compat/m_i386.h>
-#elif defined (PJ_M_M68K) && PJ_M_M68K != 0
-# include <pj/compat/m_m68k.h>
-#elif defined (PJ_M_ALPHA) && PJ_M_ALPHA != 0
-# include <pj/compat/m_alpha.h>
-#elif defined (PJ_M_SPARC) && PJ_M_SPARC != 0
-# include <pj/compat/m_sparc.h>
-#else
-# error "Please specify target machine."
-#endif
-
-/* Include size_t definition. */
-#include <pj/compat/size_t.h>
-
-/* Include site/user specific configuration to control PJLIB features.
- * YOU MUST CREATE THIS FILE YOURSELF!!
- */
-#include <pj/config_site.h>
-
-/********************************************************************
- * PJLIB Features.
- */
-
-/* Overrides for DOXYGEN */
-#ifdef DOXYGEN
-# undef PJ_FUNCTIONS_ARE_INLINED
-# undef PJ_HAS_FLOATING_POINT
-# undef PJ_LOG_MAX_LEVEL
-# undef PJ_LOG_MAX_SIZE
-# undef PJ_LOG_USE_STACK_BUFFER
-# undef PJ_TERM_HAS_COLOR
-# undef PJ_POOL_DEBUG
-# undef PJ_HAS_TCP
-# undef PJ_MAX_HOSTNAME
-# undef PJ_IOQUEUE_MAX_HANDLES
-# undef FD_SETSIZE
-# undef PJ_HAS_SEMAPHORE
-# undef PJ_HAS_EVENT_OBJ
-# undef PJ_ENABLE_EXTRA_CHECK
-#endif
-
-/**
- * @defgroup pj_config Build Configuration
- * @ingroup PJ
- * @{
- *
- * This section contains macros that can set during PJLIB build process
- * to controll various aspects of the library.
- *
- * <b>Note</b>: the values in this page does NOT necessarily reflect to the
- * macro values during the build process.
- */
-
-/**
- * If this macro is set to 1, it will enable some debugging checking
- * in the library.
- *
- * Default: equal to (NOT NDEBUG).
- */
-#ifndef PJ_DEBUG
-# ifndef NDEBUG
-# define PJ_DEBUG 1
-# else
-# define PJ_DEBUG 0
-# endif
-#endif
-
-/**
- * Expand functions in *_i.h header files as inline.
- *
- * Default: 0.
- */
-#ifndef PJ_FUNCTIONS_ARE_INLINED
-# define PJ_FUNCTIONS_ARE_INLINED 0
-#endif
-
-/**
- * Use floating point computations in the library.
- *
- * Default: 1.
- */
-#ifndef PJ_HAS_FLOATING_POINT
-# define PJ_HAS_FLOATING_POINT 1
-#endif
-
-/**
- * Declare maximum logging level/verbosity. Lower number indicates higher
- * importance, with the highest importance has level zero. The least
- * important level is five in this implementation, but this can be extended
- * by supplying the appropriate implementation.
- *
- * The level conventions:
- * - 0: fatal error
- * - 1: error
- * - 2: warning
- * - 3: info
- * - 4: debug
- * - 5: trace
- * - 6: more detailed trace
- *
- * Default: 4
- */
-#ifndef PJ_LOG_MAX_LEVEL
-# define PJ_LOG_MAX_LEVEL 5
-#endif
-
-/**
- * Maximum message size that can be sent to output device for each call
- * to PJ_LOG(). If the message size is longer than this value, it will be cut.
- * This may affect the stack usage, depending whether PJ_LOG_USE_STACK_BUFFER
- * flag is set.
- *
- * Default: 800
- */
-#ifndef PJ_LOG_MAX_SIZE
-# define PJ_LOG_MAX_SIZE 800
-#endif
-
-/**
- * Log buffer.
- * Does the log get the buffer from the stack? (default is yes).
- * If the value is set to NO, then the buffer will be taken from static
- * buffer, which in this case will make the log function non-reentrant.
- *
- * Default: 1
- */
-#ifndef PJ_LOG_USE_STACK_BUFFER
-# define PJ_LOG_USE_STACK_BUFFER 1
-#endif
-
-
-/**
- * Colorfull terminal (for logging etc).
- *
- * Default: 1
- */
-#ifndef PJ_TERM_HAS_COLOR
-# define PJ_TERM_HAS_COLOR 1
-#endif
-
-/**
- * Pool debugging.
- *
- * Default: 0
- */
-#ifndef PJ_POOL_DEBUG
-# define PJ_POOL_DEBUG 0
-#endif
-
-/**
- * \def PJ_HAS_TCP
- * Support TCP in the library.
- * Disabling TCP will reduce the footprint slightly (about 6KB).
- *
- * Default: 1
- */
-#ifndef PJ_HAS_TCP
-# define PJ_HAS_TCP 1
-#endif
-
-/**
- * Maximum hostname length.
- * Libraries sometimes needs to make copy of an address to stack buffer;
- * the value here affects the stack usage.
- *
- * Default: 128
- */
-#ifndef PJ_MAX_HOSTNAME
-# define PJ_MAX_HOSTNAME (128)
-#endif
-
-/**
- * Constants for declaring the maximum handles that can be supported by
- * a single IOQ framework. This constant might not be relevant to the
- * underlying I/O queue impelementation, but still, developers should be
- * aware of this constant, to make sure that the program will not break when
- * the underlying implementation changes.
- *
- * For implementation based on select(), the value here will be used as the
- * maximum number of socket handles passed to select() (i.e. FD_SETSIZE will
- * be set to this value).
- *
- * Default: 256
- */
-#ifndef PJ_IOQUEUE_MAX_HANDLES
-# define PJ_IOQUEUE_MAX_HANDLES (256)
-#endif
-
-/**
- * Overrides FD_SETSIZE so it is consistent throughout the library.
- * OS specific configuration header (compat/os_*) might have declared
- * FD_SETSIZE, thus we only set if it hasn't been declared.
- *
- * Default: #PJ_IOQUEUE_MAX_HANDLES
- */
-#ifndef FD_SETSIZE
-# define FD_SETSIZE PJ_IOQUEUE_MAX_HANDLES
-#endif
-
-/**
- * Has semaphore functionality?
- *
- * Default: 1
- */
-#ifndef PJ_HAS_SEMAPHORE
-# define PJ_HAS_SEMAPHORE 1
-#endif
-
-
-/**
- * Event object (for synchronization, e.g. in Win32)
- *
- * Default: 1
- */
-#ifndef PJ_HAS_EVENT_OBJ
-# define PJ_HAS_EVENT_OBJ 1
-#endif
-
-
-/**
- * Enable library's extra check.
- * If this macro is enabled, #PJ_ASSERT_RETURN macro will expand to
- * run-time checking. If this macro is disabled, #PJ_ASSERT_RETURN
- * will simply evaluate to #pj_assert().
- *
- * You can disable this macro to reduce size, at the risk of crashes
- * if invalid value (e.g. NULL) is passed to the library.
- *
- * Default: 1
- */
-#ifndef PJ_ENABLE_EXTRA_CHECK
-# define PJ_ENABLE_EXTRA_CHECK 1
-#endif
-
-
-/**
- * Enable name registration for exceptions with #pj_exception_id_alloc().
- * If this feature is enabled, then the library will keep track of
- * names associated with each exception ID requested by application via
- * #pj_exception_id_alloc().
- *
- * Disabling this macro will reduce the code and .bss size by a tad bit.
- * See also #PJ_MAX_EXCEPTION_ID.
- *
- * Default: 1
- */
-#ifndef PJ_HAS_EXCEPTION_NAMES
-# define PJ_HAS_EXCEPTION_NAMES 1
-#endif
-
-/**
- * Maximum number of unique exception IDs that can be requested
- * with #pj_exception_id_alloc(). For each entry, a small record will
- * be allocated in the .bss segment.
- *
- * Default: 16
- */
-#ifndef PJ_MAX_EXCEPTION_ID
-# define PJ_MAX_EXCEPTION_ID 16
-#endif
-
-/** @} */
-
-/********************************************************************
- * General macros.
- */
-
-/**
- * @def PJ_INLINE(type)
- * @param type The return type of the function.
- * Expand the function as inline.
- */
-#define PJ_INLINE(type) PJ_INLINE_SPECIFIER type
-
-/**
- * @def PJ_DECL(type)
- * @param type The return type of the function.
- * Declare a function.
- */
-/**
- * @def PJ_DECL_NO_RETURN(type)
- * @param type The return type of the function.
- * Declare a function that will not return.
- */
-/**
- * @def PJ_BEGIN_DECL
- * Mark beginning of declaration section in a header file.
- */
-/**
- * @def PJ_END_DECL
- * Mark end of declaration section in a header file.
- */
-#ifdef __cplusplus
-# define PJ_DECL(type) type
-# define PJ_DECL_NO_RETURN(type) type PJ_NORETURN
-# define PJ_BEGIN_DECL extern "C" {
-# define PJ_END_DECL }
-#else
-# define PJ_DECL(type) extern type
-# define PJ_DECL_NO_RETURN(type) PJ_NORETURN type
-# define PJ_BEGIN_DECL
-# define PJ_END_DECL
-#endif
-
-/**
- * @def PJ_DEF(type)
- * @param type The return type of the function.
- * Define a function.
- */
-#define PJ_DEF(type) type
-
-/**
- * @def PJ_EXPORT_SYMBOL(sym)
- * @param sym The symbol to export.
- * Export the specified symbol in compilation type that requires export
- * (e.g. Linux kernel).
- */
-#ifdef __PJ_EXPORT_SYMBOL
-# define PJ_EXPORT_SYMBOL(sym) __PJ_EXPORT_SYMBOL(sym)
-#else
-# define PJ_EXPORT_SYMBOL(sym)
-#endif
-
-/**
- * @def PJ_IDECL(type)
- * @param type The function's return type.
- * Declare a function that may be expanded as inline.
- */
-/**
- * @def PJ_IDEF(type)
- * @param type The function's return type.
- * Define a function that may be expanded as inline.
- */
-
-#if PJ_FUNCTIONS_ARE_INLINED
-# define PJ_IDECL(type) PJ_INLINE(type)
-# define PJ_IDEF(type) PJ_INLINE(type)
-#else
-# define PJ_IDECL(type) PJ_DECL(type)
-# define PJ_IDEF(type) PJ_DEF(type)
-#endif
-
-/**
- * @def PJ_UNUSED_ARG(arg)
- * @param arg The argument name.
- * PJ_UNUSED_ARG prevents warning about unused argument in a function.
- */
-#define PJ_UNUSED_ARG(arg) (void)arg
-
-/**
- * @def PJ_TODO(id)
- * @param id Any identifier that will be printed as TODO message.
- * PJ_TODO macro will display TODO message as warning during compilation.
- * Example: PJ_TODO(CLEAN_UP_ERROR);
- */
-#ifndef PJ_TODO
-# define PJ_TODO(id) TODO___##id:
-#endif
-
+/* $Id$
+ *
+ */
+/*
+ * PJLIB - PJ Foundation Library
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ * Benny Prijono <bennylp@bulukucing.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef __PJ_CONFIG_H__
+#define __PJ_CONFIG_H__
+
+/**
+ * @file config.h
+ * @brief PJLIB Main configuration settings.
+ */
+
+/********************************************************************
+ * Include compiler specific configuration.
+ */
+#if defined(_MSC_VER)
+# include <pj/compat/cc_msvc.h>
+#elif defined(__GNUC__)
+# include <pj/compat/cc_gcc.h>
+#else
+# error "Unknown compiler."
+#endif
+
+
+/********************************************************************
+ * Include target OS specific configuration.
+ */
+#if defined(PJ_WIN32) && PJ_WIN32!=0
+# include <pj/compat/os_win32.h>
+#elif defined(PJ_LINUX) && PJ_LINUX!=0
+# include <pj/compat/os_linux.h>
+#elif defined(PJ_LINUX_KERNEL) && PJ_LINUX_KERNEL!=0
+# include <pj/compat/os_linux_kernel.h>
+#elif defined(PJ_PALMOS) && PJ_PALMOS!=0
+# include <pj/compat/os_palmos.h>
+#elif defined(PJ_SUNOS) && PJ_SUNOS!=0
+# include <pj/compat/os_sunos.h>
+#else
+# error "Please specify target os."
+#endif
+
+
+/********************************************************************
+ * Target machine specific configuration.
+ */
+#if defined (PJ_M_I386) && PJ_M_I386 != 0
+# include <pj/compat/m_i386.h>
+#elif defined (PJ_M_M68K) && PJ_M_M68K != 0
+# include <pj/compat/m_m68k.h>
+#elif defined (PJ_M_ALPHA) && PJ_M_ALPHA != 0
+# include <pj/compat/m_alpha.h>
+#elif defined (PJ_M_SPARC) && PJ_M_SPARC != 0
+# include <pj/compat/m_sparc.h>
+#else
+# error "Please specify target machine."
+#endif
+
+/* Include size_t definition. */
+#include <pj/compat/size_t.h>
+
+/* Include site/user specific configuration to control PJLIB features.
+ * YOU MUST CREATE THIS FILE YOURSELF!!
+ */
+#include <pj/config_site.h>
+
+/********************************************************************
+ * PJLIB Features.
+ */
+
+/* Overrides for DOXYGEN */
+#ifdef DOXYGEN
+# undef PJ_FUNCTIONS_ARE_INLINED
+# undef PJ_HAS_FLOATING_POINT
+# undef PJ_LOG_MAX_LEVEL
+# undef PJ_LOG_MAX_SIZE
+# undef PJ_LOG_USE_STACK_BUFFER
+# undef PJ_TERM_HAS_COLOR
+# undef PJ_POOL_DEBUG
+# undef PJ_HAS_TCP
+# undef PJ_MAX_HOSTNAME
+# undef PJ_IOQUEUE_MAX_HANDLES
+# undef FD_SETSIZE
+# undef PJ_HAS_SEMAPHORE
+# undef PJ_HAS_EVENT_OBJ
+# undef PJ_ENABLE_EXTRA_CHECK
+#endif
+
+/**
+ * @defgroup pj_config Build Configuration
+ * @ingroup PJ
+ * @{
+ *
+ * This section contains macros that can set during PJLIB build process
+ * to controll various aspects of the library.
+ *
+ * <b>Note</b>: the values in this page does NOT necessarily reflect to the
+ * macro values during the build process.
+ */
+
+/**
+ * If this macro is set to 1, it will enable some debugging checking
+ * in the library.
+ *
+ * Default: equal to (NOT NDEBUG).
+ */
+#ifndef PJ_DEBUG
+# ifndef NDEBUG
+# define PJ_DEBUG 1
+# else
+# define PJ_DEBUG 0
+# endif
+#endif
+
+/**
+ * Expand functions in *_i.h header files as inline.
+ *
+ * Default: 0.
+ */
+#ifndef PJ_FUNCTIONS_ARE_INLINED
+# define PJ_FUNCTIONS_ARE_INLINED 0
+#endif
+
+/**
+ * Use floating point computations in the library.
+ *
+ * Default: 1.
+ */
+#ifndef PJ_HAS_FLOATING_POINT
+# define PJ_HAS_FLOATING_POINT 1
+#endif
+
+/**
+ * Declare maximum logging level/verbosity. Lower number indicates higher
+ * importance, with the highest importance has level zero. The least
+ * important level is five in this implementation, but this can be extended
+ * by supplying the appropriate implementation.
+ *
+ * The level conventions:
+ * - 0: fatal error
+ * - 1: error
+ * - 2: warning
+ * - 3: info
+ * - 4: debug
+ * - 5: trace
+ * - 6: more detailed trace
+ *
+ * Default: 4
+ */
+#ifndef PJ_LOG_MAX_LEVEL
+# define PJ_LOG_MAX_LEVEL 5
+#endif
+
+/**
+ * Maximum message size that can be sent to output device for each call
+ * to PJ_LOG(). If the message size is longer than this value, it will be cut.
+ * This may affect the stack usage, depending whether PJ_LOG_USE_STACK_BUFFER
+ * flag is set.
+ *
+ * Default: 800
+ */
+#ifndef PJ_LOG_MAX_SIZE
+# define PJ_LOG_MAX_SIZE 800
+#endif
+
+/**
+ * Log buffer.
+ * Does the log get the buffer from the stack? (default is yes).
+ * If the value is set to NO, then the buffer will be taken from static
+ * buffer, which in this case will make the log function non-reentrant.
+ *
+ * Default: 1
+ */
+#ifndef PJ_LOG_USE_STACK_BUFFER
+# define PJ_LOG_USE_STACK_BUFFER 1
+#endif
+
+
+/**
+ * Colorfull terminal (for logging etc).
+ *
+ * Default: 1
+ */
+#ifndef PJ_TERM_HAS_COLOR
+# define PJ_TERM_HAS_COLOR 1
+#endif
+
+/**
+ * Pool debugging.
+ *
+ * Default: 0
+ */
+#ifndef PJ_POOL_DEBUG
+# define PJ_POOL_DEBUG 0
+#endif
+
+/**
+ * \def PJ_HAS_TCP
+ * Support TCP in the library.
+ * Disabling TCP will reduce the footprint slightly (about 6KB).
+ *
+ * Default: 1
+ */
+#ifndef PJ_HAS_TCP
+# define PJ_HAS_TCP 1
+#endif
+
+/**
+ * Maximum hostname length.
+ * Libraries sometimes needs to make copy of an address to stack buffer;
+ * the value here affects the stack usage.
+ *
+ * Default: 128
+ */
+#ifndef PJ_MAX_HOSTNAME
+# define PJ_MAX_HOSTNAME (128)
+#endif
+
+/**
+ * Constants for declaring the maximum handles that can be supported by
+ * a single IOQ framework. This constant might not be relevant to the
+ * underlying I/O queue impelementation, but still, developers should be
+ * aware of this constant, to make sure that the program will not break when
+ * the underlying implementation changes.
+ *
+ * For implementation based on select(), the value here will be used as the
+ * maximum number of socket handles passed to select() (i.e. FD_SETSIZE will
+ * be set to this value).
+ *
+ * Default: 256
+ */
+#ifndef PJ_IOQUEUE_MAX_HANDLES
+# define PJ_IOQUEUE_MAX_HANDLES (256)
+#endif
+
+/**
+ * Overrides FD_SETSIZE so it is consistent throughout the library.
+ * OS specific configuration header (compat/os_*) might have declared
+ * FD_SETSIZE, thus we only set if it hasn't been declared.
+ *
+ * Default: #PJ_IOQUEUE_MAX_HANDLES
+ */
+#ifndef FD_SETSIZE
+# define FD_SETSIZE PJ_IOQUEUE_MAX_HANDLES
+#endif
+
+/**
+ * Has semaphore functionality?
+ *
+ * Default: 1
+ */
+#ifndef PJ_HAS_SEMAPHORE
+# define PJ_HAS_SEMAPHORE 1
+#endif
+
+
+/**
+ * Event object (for synchronization, e.g. in Win32)
+ *
+ * Default: 1
+ */
+#ifndef PJ_HAS_EVENT_OBJ
+# define PJ_HAS_EVENT_OBJ 1
+#endif
+
+
+/**
+ * Enable library's extra check.
+ * If this macro is enabled, #PJ_ASSERT_RETURN macro will expand to
+ * run-time checking. If this macro is disabled, #PJ_ASSERT_RETURN
+ * will simply evaluate to #pj_assert().
+ *
+ * You can disable this macro to reduce size, at the risk of crashes
+ * if invalid value (e.g. NULL) is passed to the library.
+ *
+ * Default: 1
+ */
+#ifndef PJ_ENABLE_EXTRA_CHECK
+# define PJ_ENABLE_EXTRA_CHECK 1
+#endif
+
+
+/**
+ * Enable name registration for exceptions with #pj_exception_id_alloc().
+ * If this feature is enabled, then the library will keep track of
+ * names associated with each exception ID requested by application via
+ * #pj_exception_id_alloc().
+ *
+ * Disabling this macro will reduce the code and .bss size by a tad bit.
+ * See also #PJ_MAX_EXCEPTION_ID.
+ *
+ * Default: 1
+ */
+#ifndef PJ_HAS_EXCEPTION_NAMES
+# define PJ_HAS_EXCEPTION_NAMES 1
+#endif
+
+/**
+ * Maximum number of unique exception IDs that can be requested
+ * with #pj_exception_id_alloc(). For each entry, a small record will
+ * be allocated in the .bss segment.
+ *
+ * Default: 16
+ */
+#ifndef PJ_MAX_EXCEPTION_ID
+# define PJ_MAX_EXCEPTION_ID 16
+#endif
+
+/** @} */
+
+/********************************************************************
+ * General macros.
+ */
+
+/**
+ * @def PJ_INLINE(type)
+ * @param type The return type of the function.
+ * Expand the function as inline.
+ */
+#define PJ_INLINE(type) PJ_INLINE_SPECIFIER type
+
+/**
+ * @def PJ_DECL(type)
+ * @param type The return type of the function.
+ * Declare a function.
+ */
+/**
+ * @def PJ_DECL_NO_RETURN(type)
+ * @param type The return type of the function.
+ * Declare a function that will not return.
+ */
+/**
+ * @def PJ_BEGIN_DECL
+ * Mark beginning of declaration section in a header file.
+ */
+/**
+ * @def PJ_END_DECL
+ * Mark end of declaration section in a header file.
+ */
+#ifdef __cplusplus
+# define PJ_DECL(type) type
+# define PJ_DECL_NO_RETURN(type) type PJ_NORETURN
+# define PJ_BEGIN_DECL extern "C" {
+# define PJ_END_DECL }
+#else
+# define PJ_DECL(type) extern type
+# define PJ_DECL_NO_RETURN(type) PJ_NORETURN type
+# define PJ_BEGIN_DECL
+# define PJ_END_DECL
+#endif
+
+/**
+ * @def PJ_DEF(type)
+ * @param type The return type of the function.
+ * Define a function.
+ */
+#define PJ_DEF(type) type
+
+/**
+ * @def PJ_EXPORT_SYMBOL(sym)
+ * @param sym The symbol to export.
+ * Export the specified symbol in compilation type that requires export
+ * (e.g. Linux kernel).
+ */
+#ifdef __PJ_EXPORT_SYMBOL
+# define PJ_EXPORT_SYMBOL(sym) __PJ_EXPORT_SYMBOL(sym)
+#else
+# define PJ_EXPORT_SYMBOL(sym)
+#endif
+
+/**
+ * @def PJ_IDECL(type)
+ * @param type The function's return type.
+ * Declare a function that may be expanded as inline.
+ */
+/**
+ * @def PJ_IDEF(type)
+ * @param type The function's return type.
+ * Define a function that may be expanded as inline.
+ */
+
+#if PJ_FUNCTIONS_ARE_INLINED
+# define PJ_IDECL(type) PJ_INLINE(type)
+# define PJ_IDEF(type) PJ_INLINE(type)
+#else
+# define PJ_IDECL(type) PJ_DECL(type)
+# define PJ_IDEF(type) PJ_DEF(type)
+#endif
+
+/**
+ * @def PJ_UNUSED_ARG(arg)
+ * @param arg The argument name.
+ * PJ_UNUSED_ARG prevents warning about unused argument in a function.
+ */
+#define PJ_UNUSED_ARG(arg) (void)arg
+
+/**
+ * @def PJ_TODO(id)
+ * @param id Any identifier that will be printed as TODO message.
+ * PJ_TODO macro will display TODO message as warning during compilation.
+ * Example: PJ_TODO(CLEAN_UP_ERROR);
+ */
+#ifndef PJ_TODO
+# define PJ_TODO(id) TODO___##id:
+#endif
+
/**
* Function attributes to inform that the function may throw exception.
*
@@ -410,42 +431,42 @@
*/
#define __pj_throw__(x)
-
-/********************************************************************
- * Sanity Checks
- */
-#ifndef PJ_HAS_HIGH_RES_TIMER
-# error "PJ_HAS_HIGH_RES_TIMER is not defined!"
-#endif
-
-#if !defined(PJ_HAS_PENTIUM)
-# error "PJ_HAS_PENTIUM is not defined!"
-#endif
-
-#if !defined(PJ_IS_LITTLE_ENDIAN)
-# error "PJ_IS_LITTLE_ENDIAN is not defined!"
-#endif
-
-#if !defined(PJ_IS_BIG_ENDIAN)
-# error "PJ_IS_BIG_ENDIAN is not defined!"
-#endif
-
-
-
-PJ_BEGIN_DECL
-
-/**
- * PJLIB version string.
- */
-extern const char *PJ_VERSION;
-
-/**
- * Dump configuration to log with verbosity equal to info(3).
- */
-PJ_DECL(void) pj_dump_config(void);
-
-PJ_END_DECL
-
-
-#endif /* __PJ_CONFIG_H__ */
-
+
+/********************************************************************
+ * Sanity Checks
+ */
+#ifndef PJ_HAS_HIGH_RES_TIMER
+# error "PJ_HAS_HIGH_RES_TIMER is not defined!"
+#endif
+
+#if !defined(PJ_HAS_PENTIUM)
+# error "PJ_HAS_PENTIUM is not defined!"
+#endif
+
+#if !defined(PJ_IS_LITTLE_ENDIAN)
+# error "PJ_IS_LITTLE_ENDIAN is not defined!"
+#endif
+
+#if !defined(PJ_IS_BIG_ENDIAN)
+# error "PJ_IS_BIG_ENDIAN is not defined!"
+#endif
+
+
+
+PJ_BEGIN_DECL
+
+/**
+ * PJLIB version string.
+ */
+extern const char *PJ_VERSION;
+
+/**
+ * Dump configuration to log with verbosity equal to info(3).
+ */
+PJ_DECL(void) pj_dump_config(void);
+
+PJ_END_DECL
+
+
+#endif /* __PJ_CONFIG_H__ */
+
diff --git a/pjlib/include/pj/ctype.h b/pjlib/include/pj/ctype.h
index 4307fbb..39b45f8 100644
--- a/pjlib/include/pj/ctype.h
+++ b/pjlib/include/pj/ctype.h
@@ -1,121 +1,142 @@
-/* $Id$
- *
- */
-#ifndef __PJ_CTYPE_H__
-#define __PJ_CTYPE_H__
-
-/**
- * @file ctype.h
- * @brief C type helper macros.
- */
-
-#include <pj/compat/ctype.h>
-
-/**
- * @defgroup pj_ctype ctype - Character Type
- * @ingroup PJ_MISC
- * @{
- *
- * This module contains several inline functions/macros for testing or
- * manipulating character types. It is provided in PJLIB because PJLIB
- * must not depend to LIBC.
- */
-
-/**
- * Returns a non-zero value if either isalpha or isdigit is true for c.
- * @param c The integer character to test.
- * @return Non-zero value if either isalpha or isdigit is true for c.
- */
-PJ_INLINE(int) pj_isalnum(int c) { return isalnum(c); }
-
-/**
- * Returns a non-zero value if c is a particular representation of an
- * alphabetic character.
- * @param c The integer character to test.
- * @return Non-zero value if c is a particular representation of an
- * alphabetic character.
- */
-PJ_INLINE(int) pj_isalpha(int c) { return isalpha(c); }
-
-/**
- * Returns a non-zero value if c is a particular representation of an
- * ASCII character.
- * @param c The integer character to test.
- * @return Non-zero value if c is a particular representation of
- * an ASCII character.
- */
-PJ_INLINE(int) pj_isascii(int c) { return isascii(c); }
-
-/**
- * Returns a non-zero value if c is a particular representation of
- * a decimal-digit character.
- * @param c The integer character to test.
- * @return Non-zero value if c is a particular representation of
- * a decimal-digit character.
- */
-PJ_INLINE(int) pj_isdigit(int c) { return isdigit(c); }
-
-/**
- * Returns a non-zero value if c is a particular representation of
- * a space character (0x09 - 0x0D or 0x20).
- * @param c The integer character to test.
- * @return Non-zero value if c is a particular representation of
- * a space character (0x09 - 0x0D or 0x20).
- */
-PJ_INLINE(int) pj_isspace(int c) { return isspace(c); }
-
-/**
- * Returns a non-zero value if c is a particular representation of
- * a lowercase character.
- * @param c The integer character to test.
- * @return Non-zero value if c is a particular representation of
- * a lowercase character.
- */
-PJ_INLINE(int) pj_islower(int c) { return islower(c); }
-
-
-/**
- * Returns a non-zero value if c is a particular representation of
- * a uppercase character.
- * @param c The integer character to test.
- * @return Non-zero value if c is a particular representation of
- * a uppercase character.
- */
-PJ_INLINE(int) pj_isupper(int c) { return isupper(c); }
-
-/**
- * Returns a non-zero value if c is a particular representation of
- * an hexadecimal digit character.
- * @param c The integer character to test.
- * @return Non-zero value if c is a particular representation of
- * an hexadecimal digit character.
- */
-PJ_INLINE(int) pj_isxdigit(int c){ return isxdigit(c); }
-
-/**
- * Returns a non-zero value if c is a either a space (' ') or horizontal
- * tab ('\\t') character.
- * @param c The integer character to test.
- * @return Non-zero value if c is a either a space (' ') or horizontal
- * tab ('\\t') character.
- */
-PJ_INLINE(int) pj_isblank(int c) { return isblank(c); }
-
-/**
- * Converts character to lowercase.
- * @param c The integer character to convert.
- * @return Lowercase character of c.
- */
-PJ_INLINE(int) pj_tolower(int c) { return tolower(c); }
-
-/**
- * Converts character to uppercase.
- * @param c The integer character to convert.
- * @return Uppercase character of c.
- */
-PJ_INLINE(int) pj_toupper(int c) { return toupper(c); }
-
-/** @} */
-
-#endif /* __PJ_CTYPE_H__ */
-
+/* $Id$
+ *
+ */
+/*
+ * PJLIB - PJ Foundation Library
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ * Benny Prijono <bennylp@bulukucing.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#ifndef __PJ_CTYPE_H__
+#define __PJ_CTYPE_H__
+
+/**
+ * @file ctype.h
+ * @brief C type helper macros.
+ */
+
+#include <pj/compat/ctype.h>
+
+/**
+ * @defgroup pj_ctype ctype - Character Type
+ * @ingroup PJ_MISC
+ * @{
+ *
+ * This module contains several inline functions/macros for testing or
+ * manipulating character types. It is provided in PJLIB because PJLIB
+ * must not depend to LIBC.
+ */
+
+/**
+ * Returns a non-zero value if either isalpha or isdigit is true for c.
+ * @param c The integer character to test.
+ * @return Non-zero value if either isalpha or isdigit is true for c.
+ */
+PJ_INLINE(int) pj_isalnum(int c) { return isalnum(c); }
+
+/**
+ * Returns a non-zero value if c is a particular representation of an
+ * alphabetic character.
+ * @param c The integer character to test.
+ * @return Non-zero value if c is a particular representation of an
+ * alphabetic character.
+ */
+PJ_INLINE(int) pj_isalpha(int c) { return isalpha(c); }
+
+/**
+ * Returns a non-zero value if c is a particular representation of an
+ * ASCII character.
+ * @param c The integer character to test.
+ * @return Non-zero value if c is a particular representation of
+ * an ASCII character.
+ */
+PJ_INLINE(int) pj_isascii(int c) { return isascii(c); }
+
+/**
+ * Returns a non-zero value if c is a particular representation of
+ * a decimal-digit character.
+ * @param c The integer character to test.
+ * @return Non-zero value if c is a particular representation of
+ * a decimal-digit character.
+ */
+PJ_INLINE(int) pj_isdigit(int c) { return isdigit(c); }
+
+/**
+ * Returns a non-zero value if c is a particular representation of
+ * a space character (0x09 - 0x0D or 0x20).
+ * @param c The integer character to test.
+ * @return Non-zero value if c is a particular representation of
+ * a space character (0x09 - 0x0D or 0x20).
+ */
+PJ_INLINE(int) pj_isspace(int c) { return isspace(c); }
+
+/**
+ * Returns a non-zero value if c is a particular representation of
+ * a lowercase character.
+ * @param c The integer character to test.
+ * @return Non-zero value if c is a particular representation of
+ * a lowercase character.
+ */
+PJ_INLINE(int) pj_islower(int c) { return islower(c); }
+
+
+/**
+ * Returns a non-zero value if c is a particular representation of
+ * a uppercase character.
+ * @param c The integer character to test.
+ * @return Non-zero value if c is a particular representation of
+ * a uppercase character.
+ */
+PJ_INLINE(int) pj_isupper(int c) { return isupper(c); }
+
+/**
+ * Returns a non-zero value if c is a particular representation of
+ * an hexadecimal digit character.
+ * @param c The integer character to test.
+ * @return Non-zero value if c is a particular representation of
+ * an hexadecimal digit character.
+ */
+PJ_INLINE(int) pj_isxdigit(int c){ return isxdigit(c); }
+
+/**
+ * Returns a non-zero value if c is a either a space (' ') or horizontal
+ * tab ('\\t') character.
+ * @param c The integer character to test.
+ * @return Non-zero value if c is a either a space (' ') or horizontal
+ * tab ('\\t') character.
+ */
+PJ_INLINE(int) pj_isblank(int c) { return isblank(c); }
+
+/**
+ * Converts character to lowercase.
+ * @param c The integer character to convert.
+ * @return Lowercase character of c.
+ */
+PJ_INLINE(int) pj_tolower(int c) { return tolower(c); }
+
+/**
+ * Converts character to uppercase.
+ * @param c The integer character to convert.
+ * @return Uppercase character of c.
+ */
+PJ_INLINE(int) pj_toupper(int c) { return toupper(c); }
+
+/** @} */
+
+#endif /* __PJ_CTYPE_H__ */
+
diff --git a/pjlib/include/pj/doxygen.h b/pjlib/include/pj/doxygen.h
index f7b2d14..891a192 100644
--- a/pjlib/include/pj/doxygen.h
+++ b/pjlib/include/pj/doxygen.h
@@ -1,398 +1,419 @@
-/* $Id$
- *
- */
-#ifndef __PJ_DOXYGEN_H__
-#define __PJ_DOXYGEN_H__
-
-/**
- * @file doxygen.h
- * @brief Doxygen's mainpage.
- */
-
-/*////////////////////////////////////////////////////////////////////////// */
-/*
- INTRODUCTION PAGE
- */
-
-/**
- * @mainpage Welcome to PJLIB!
- *
- * @section intro_sec What is PJLIB
- *
- * PJLIB is a small foundation library written in C for making scalable
- * applications. Because of its small footprint, it can be used in embedded
- * applications (we hope so!), but yet the library is also aimed for
- * facilitating high performance protocol stacks.
- *
- * PJLIB is released under LGPL terms.
- *
- * @section download_sec Download
- *
- * PJLIB and all documentation can be downloaded from
- * http://www.pjproject.net.
- *
- *
- * @section how_to_use_sec About This Documentation
- *
- * This document is generated directly from PJLIB source file using
- * \a doxygen (http://www.doxygen.org). Doxygen is a great (and free!)
- * tools for generating such documentation.
- *
- * @subsection doc_ver_subsec Version
- *
- * This document corresponds to PJLIB version 0.3-pre2.
- *
- *
- * @subsection find_samples_subsec How to Read This Document
- *
- * This documentation is laid out more to be a reference guide instead
- * of tutorial, therefore first time users may find it difficult to
- * grasp PJLIB by reading this document alone.
- *
- * However, we've tried our best to make this document easy to follow.
- * For first time users, we would suggest that you follow these steps
- * when reading this documentation:
- *
- * - continue reading this introduction chapter. At the end of this
- * chapter, you'll find section called \ref pjlib_fundamentals_sec
- * which should guide you to understand basic things about PJLIB.
- *
- * - find information about specific features that you want to use
- * in PJLIB. Use the <b>Module Index</b> to find out about all
- * features in PJLIB (if you're browsing the HTML documentation,
- * click on the \a Module link on top of the page, or if you're
- * reading the PDF documentation, click on \a Module \a Documentation
- * on the navigation pane on the left).
- *
- * @subsection doc_organize_sec How To's
- *
- * Please find below links to specific tasks that you probably
- * want to do:
- *
- * - <b>How to Build PJLIB</b>
- *\n
- * Please refer to \ref pjlib_build_sys_pg page for more information.
- *
- * - <b>How to Use PJLIB in My Application</b>
- *\n
- * Please refer to \ref configure_app_sec for more information.
- *
- * - <b>How to Port PJLIB</b>
- *\n
- * Please refer to \ref porting_pjlib_pg page.
- *
- * - <b>Where to Read Samples Documentation</b>
- *\n
- * Most of the modules provide link to the corresponding sample file.
- * Alternatively, to get the list of all examples, you can click on
- * <b>Related Pages</b> on the top of HTML document or on
- * <b>PJLIB Page Documentation</b> on navigation pane of your PDF reader.
+/* $Id$
*
- * - <b>How to Submit Code to PJLIB Project</b>
- *\n
+ */
+/*
+ * PJLIB - PJ Foundation Library
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ * Benny Prijono <bennylp@bulukucing.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#ifndef __PJ_DOXYGEN_H__
+#define __PJ_DOXYGEN_H__
+
+/**
+ * @file doxygen.h
+ * @brief Doxygen's mainpage.
+ */
+
+/*////////////////////////////////////////////////////////////////////////// */
+/*
+ INTRODUCTION PAGE
+ */
+
+/**
+ * @mainpage Welcome to PJLIB!
+ *
+ * @section intro_sec What is PJLIB
+ *
+ * PJLIB is a small foundation library written in C for making scalable
+ * applications. Because of its small footprint, it can be used in embedded
+ * applications (we hope so!), but yet the library is also aimed for
+ * facilitating high performance protocol stacks.
+ *
+ * PJLIB is released under LGPL terms.
+ *
+ * @section download_sec Download
+ *
+ * PJLIB and all documentation can be downloaded from
+ * http://www.pjproject.net.
+ *
+ *
+ * @section how_to_use_sec About This Documentation
+ *
+ * This document is generated directly from PJLIB source file using
+ * \a doxygen (http://www.doxygen.org). Doxygen is a great (and free!)
+ * tools for generating such documentation.
+ *
+ * @subsection doc_ver_subsec Version
+ *
+ * This document corresponds to PJLIB version 0.3-pre2.
+ *
+ *
+ * @subsection find_samples_subsec How to Read This Document
+ *
+ * This documentation is laid out more to be a reference guide instead
+ * of tutorial, therefore first time users may find it difficult to
+ * grasp PJLIB by reading this document alone.
+ *
+ * However, we've tried our best to make this document easy to follow.
+ * For first time users, we would suggest that you follow these steps
+ * when reading this documentation:
+ *
+ * - continue reading this introduction chapter. At the end of this
+ * chapter, you'll find section called \ref pjlib_fundamentals_sec
+ * which should guide you to understand basic things about PJLIB.
+ *
+ * - find information about specific features that you want to use
+ * in PJLIB. Use the <b>Module Index</b> to find out about all
+ * features in PJLIB (if you're browsing the HTML documentation,
+ * click on the \a Module link on top of the page, or if you're
+ * reading the PDF documentation, click on \a Module \a Documentation
+ * on the navigation pane on the left).
+ *
+ * @subsection doc_organize_sec How To's
+ *
+ * Please find below links to specific tasks that you probably
+ * want to do:
+ *
+ * - <b>How to Build PJLIB</b>
+ *\n
+ * Please refer to \ref pjlib_build_sys_pg page for more information.
+ *
+ * - <b>How to Use PJLIB in My Application</b>
+ *\n
+ * Please refer to \ref configure_app_sec for more information.
+ *
+ * - <b>How to Port PJLIB</b>
+ *\n
+ * Please refer to \ref porting_pjlib_pg page.
+ *
+ * - <b>Where to Read Samples Documentation</b>
+ *\n
+ * Most of the modules provide link to the corresponding sample file.
+ * Alternatively, to get the list of all examples, you can click on
+ * <b>Related Pages</b> on the top of HTML document or on
+ * <b>PJLIB Page Documentation</b> on navigation pane of your PDF reader.
+ *
+ * - <b>How to Submit Code to PJLIB Project</b>
+ *\n
* Please read \ref pjlib_coding_convention_page before submitting
- * your code. Send your code as patch against current Subversion tree
+ * your code. Send your code as patch against current Subversion tree
* to the appropriate mailing list.
- *
- *
- * @section features_sec Features
- *
- * @subsection open_source_feat It's Open Source!
- *
- * PJLIB is currently released on LGPL license. We may release PJLIB under
- * additional schemes in the future (such as GPL or MPL) to incorporate
- * linking with specific application, however, one thing for sure is
- * we will NEVER be able to make PJLIB a proprietary software.
- *
- * @subsection extreme_portable_feat Extreme Portability
- *
- * PJLIB is designed to be extremely portable. It can run on any kind
- * of processors (16-bit, 32-bit, or 64-bit, big or little endian, single
- * or multi-processors) and operating systems. Floating point or no
- * floating point. Multi-threading or not.
- * It can even run in environment where no ANSI LIBC is available.
- *
- * Currently PJLIB is being ported to:
- * - x86, Win32 (Win95/98/ME, NT/2000/XP/2003, mingw).
- * - x86, Linux (user mode and as <b>kernel module</b>(!)).
- * - alpha, Linux
- * And coming up:
- * - x86, eCos
- * - ultra-II, Solaris.
- * - powerpc, MacOS
- * - m68k, PalmOS.
- * - arm, PocketPC
- *
- * No other library is known to have this extreme portability!
- *
- * @subsection small_size_feat Small in Size
- *
- * One of the primary objectives is to have library that is small in size for
- * typical embedded applications. As a rough guidance, we aim to keep the
- * library size below 100KB for it to be considered as small.
- * As the result, most of the functionalities in the library can be tailored
- * to meet the requirements; user can enable/disable specific functionalities
- * to get the desired size/performance/functionality balance.
- *
- * For more info, please see @ref pj_config.
- *
- * @subsection no_dyn_mem No Dynamic Memory Allocations
- *
- * The central idea of PJLIB is that for applications to run as fast as it can,
- * it should not use \a malloc() at all, but instead should get the memory
- * from a preallocated storage pool. There are few things that can be
- * optimized with this approach:
- *
- * - \a alloc() is a O(1) operation.
- * - no mutex is used inside alloc(). It is assumed that synchronization
- * will be used in higher abstraction by application anyway.
- * - no \a free() is required. All chunks will be deleted when the pool is
- * destroyed.
- *
- * The performance gained on some systems can be as high as 10x speed up
- * against \a malloc() and \a free().
- *
- * For more information, see \ref PJ_POOL_GROUP
- *
- *
- * @subsection os_abstract_feat Operating System Abstraction
- *
- * PJLIB has abstractions for features that are normally not portable
- * across operating systems:
- * - @ref PJ_THREAD
- *\n
- * Portable thread manipulation.
- * - @ref PJ_TLS
- *\n
- * Storing data in thread's private data.
- * - @ref PJ_MUTEX
- *\n
- * Mutual exclusion protection.
- * - @ref PJ_SEM
- *\n
- * Semaphores.
- * - @ref PJ_ATOMIC
- *\n
- * Atomic variables and their operations.
- * - @ref PJ_CRIT_SEC
- *\n
- * Fast locking of critical sections.
- * - @ref PJ_LOCK
- *\n
- * High level abstraction for lock objects.
- * - @ref PJ_EVENT
- *\n
- * Event object.
- * - @ref PJ_TIME
- *\n
- * Portable time manipulation.
- * - @ref PJ_TIMESTAMP
- *\n
- * High resolution time value.
- * - etc.
- *
- *
- * @subsection ll_network_io_sec Low-Level Network I/O
- *
- * PJLIB has very portable abstraction and fairly complete set of API for
- * doing network I/O communications. At the lowest level, PJLIB provides:
- *
- * - @ref PJ_SOCK
- *\n
- * A highly portable socket abstraction, runs on all kind of
- * network APIs such as standard BSD socket, Windows socket, Linux
- * \b kernel socket, PalmOS networking API, etc.
- *
- * - @ref pj_addr_resolve
- *\n
- * Portable address resolution, which implements #pj_gethostbyname().
- *
- * - @ref PJ_SOCK_SELECT
- *\n
- * A portable \a select() like API (#pj_sock_select()) which can be
- * implemented with various back-end.
- *
- *
- * @subsection hl_network_io_sec High-Level Network I/O
- *
- * At higher abstraction, PJLIB provides @ref PJ_IOQUEUE,
- * which promotes creating high performance network
- * applications by managing asynchronous I/O. This is a passive framework
- * that utilizes the most effective way to manage asynchronous I/O
- * on a given platform, such as:
- * - IoCompletionPort on WinNT,
- * - on Linux it can use either /dev/epoll or aio.
- * - or to fall back to use @a select()
- *
- * At even a higher abstraction, PJLIB provides @ref PJ_EQUEUE, which
- * combines asynchronous I/O with timer management and thread management
- * to fasilitate creating trully high performance, event driven
- * application.
- *
- *
- * @subsection timer_mgmt_sec Timer Management
- *
- * A passive framework for managing timer, see @ref PJ_TIMER for more info.
- * There is also function to retrieve high resolution timestamp
- * from the system (see @ref PJ_TIMESTAMP).
- *
- *
- * @subsection data_struct_sec Various Data Structures
- *
- * Various data structures are provided in the library:
- *
- * - @ref PJ_PSTR
- * - @ref PJ_ARRAY
- * - @ref PJ_HASH
- * - @ref PJ_LIST
- * - @ref PJ_RBTREE
- *
- *
- * @subsection exception_sec Exception Construct
- *
- * A convenient TRY/CATCH like construct to propagate errors, which by
- * default are used by the @ref PJ_POOL_GROUP "memory pool" and
- * the lexical scanner in pjlib-util. The exception
- * construct can be used to write programs like below:
- *
- * <pre>
- * #define SYNTAX_ERROR 1
- *
- * PJ_TRY {
- * msg = NULL;
- * msg = parse_msg(buf, len);
- * }
- * PJ_CATCH ( SYNTAX_ERROR ) {
- * .. handle error ..
- * }
- * PJ_END;
- * </pre>
- *
- * Please see @ref PJ_EXCEPT for more information.
- *
- *
- * @subsection logging_sec Logging Facility
- *
- * PJLIB @ref PJ_LOG consists of macros to write logging information to
- * some output device. Some of the features of the logging facility:
- *
- * - the verbosity can be fine-tuned both at compile time (to control
- * the library size) or run-time (to control the verbosity of the
- * information).
- * - output device is configurable (e.g. stdout, printk, file, etc.)
- * - log decoration is configurable.
- *
- * See @ref PJ_LOG for more information.
- *
- *
- * @subsection guid_gen_sec Random and GUID Generation
- *
- * PJLIB provides facility to create random string
- * (#pj_create_random_string()) or globally unique identifier
- * (see @ref PJ_GUID).
- *
- *
- *
- * @section configure_app_sec Configuring Application to use PJLIB
- *
- * @subsection pjlib_compil_sec Building PJLIB
- *
- * Follow the instructions in \ref pjlib_build_sys_pg to build
- * PJLIB.
- *
- * @subsection pjlib_compil_app_sec Building Applications with PJLIB
- *
- * Use the following settings when building applications with PJLIB.
- *
- * @subsubsection compil_inc_dir_sec Include Search Path
- *
- * Add this to your include search path ($PJLIB is PJLIB root directory):
- * <pre>
- * $PJLIB/include
- * </pre>
- *
- * @subsubsection compil_inc_file_sec Include PJLIB Header
- *
- * To include all PJLIB headers:
- * \verbatim
- #include <pjlib.h>
- \endverbatim
- *
- * Alternatively, you can include individual PJLIB headers like this:
- * \verbatim
- #include <pj/log.h>
- #include <pj/os.h>
- \endverbatim
- *
- *
- * @subsubsection compil_lib_dir_sec Library Path
- *
- * Add this to your library search path:
- * <pre>
- * $PJLIB/lib
- * </pre>
- *
- * Then add the appropriate PJLIB library to your link specification. For
- * example, you would add \c libpj-i386-linux-gcc.a when you're building
- * applications in Linux.
- *
- *
- * @subsection pjlib_fundamentals_sec Principles in Using PJLIB
- *
- * Few things that you \b MUST do when using PJLIB, to make sure that
- * you create trully portable applications.
- *
- * @subsubsection call_pjlib_init_sec Call pj_init()
- *
- * Before you do anything else, call \c pj_init(). This would make sure that
- * PJLIB system is properly set up.
- *
- * @subsubsection no_ansi_subsec Do NOT Use ANSI C
- *
- * Contrary to popular teaching, ANSI C (and LIBC) is not the most portable
- * library in the world, nor it's the most ubiquitous. For example, LIBC
- * is not available in Linux kernel. Also normally LIBC will be excluded
- * from compilation of RTOSes to reduce size.
- *
- * So for maximum portability, do NOT use ANSI C. Do not even try to include
- * any other header files outside <include/pj>. Stick with the functionalities
- * provided by PJLIB.
- *
- *
- * @subsubsection string_rep_subsubsec Use pj_str_t instead of C Strings
- *
- * PJLIB uses pj_str_t instead of normal C strings. You SHOULD follow this
- * convention too. Remember, ANSI string-h is not always available. And
- * PJLIB string is faster!
- *
- * @subsubsection mem_alloc_subsubsec Use Pool for Memory Allocations
- *
- * You MUST NOT use \a malloc() or any other memory allocation functions.
- * Use PJLIB pool instead! It's faster and most portable.
- *
- * @subsection logging_subsubsec Use Logging for Text Display
- *
- * DO NOT use <stdio.h> for text output. Use PJLIB logging instead.
- *
- *
- * @section porting_pjlib_sec0 Porting PJLIB
- *
- * Please see \ref porting_pjlib_pg page on more information to port
- * PJLIB to new target.
- *
- * @section enjoy_sec Enjoy Using PJLIB!
- *
- * We hope that you find PJLIB usefull for your application. If you
- * have any questions, suggestions, critics, bug fixes, or anything
- * else, we would be happy to hear it.
- *
- * Enjoy using PJLIB!
- *
- * Benny Prijono < bennylp at pjproject dot net >
- */
-
-
-
+ *
+ *
+ * @section features_sec Features
+ *
+ * @subsection open_source_feat It's Open Source!
+ *
+ * PJLIB is currently released on LGPL license. We may release PJLIB under
+ * additional schemes in the future (such as GPL or MPL) to incorporate
+ * linking with specific application, however, one thing for sure is
+ * we will NEVER be able to make PJLIB a proprietary software.
+ *
+ * @subsection extreme_portable_feat Extreme Portability
+ *
+ * PJLIB is designed to be extremely portable. It can run on any kind
+ * of processors (16-bit, 32-bit, or 64-bit, big or little endian, single
+ * or multi-processors) and operating systems. Floating point or no
+ * floating point. Multi-threading or not.
+ * It can even run in environment where no ANSI LIBC is available.
+ *
+ * Currently PJLIB is being ported to:
+ * - x86, Win32 (Win95/98/ME, NT/2000/XP/2003, mingw).
+ * - x86, Linux (user mode and as <b>kernel module</b>(!)).
+ * - alpha, Linux
+ * And coming up:
+ * - x86, eCos
+ * - ultra-II, Solaris.
+ * - powerpc, MacOS
+ * - m68k, PalmOS.
+ * - arm, PocketPC
+ *
+ * No other library is known to have this extreme portability!
+ *
+ * @subsection small_size_feat Small in Size
+ *
+ * One of the primary objectives is to have library that is small in size for
+ * typical embedded applications. As a rough guidance, we aim to keep the
+ * library size below 100KB for it to be considered as small.
+ * As the result, most of the functionalities in the library can be tailored
+ * to meet the requirements; user can enable/disable specific functionalities
+ * to get the desired size/performance/functionality balance.
+ *
+ * For more info, please see @ref pj_config.
+ *
+ * @subsection no_dyn_mem No Dynamic Memory Allocations
+ *
+ * The central idea of PJLIB is that for applications to run as fast as it can,
+ * it should not use \a malloc() at all, but instead should get the memory
+ * from a preallocated storage pool. There are few things that can be
+ * optimized with this approach:
+ *
+ * - \a alloc() is a O(1) operation.
+ * - no mutex is used inside alloc(). It is assumed that synchronization
+ * will be used in higher abstraction by application anyway.
+ * - no \a free() is required. All chunks will be deleted when the pool is
+ * destroyed.
+ *
+ * The performance gained on some systems can be as high as 10x speed up
+ * against \a malloc() and \a free().
+ *
+ * For more information, see \ref PJ_POOL_GROUP
+ *
+ *
+ * @subsection os_abstract_feat Operating System Abstraction
+ *
+ * PJLIB has abstractions for features that are normally not portable
+ * across operating systems:
+ * - @ref PJ_THREAD
+ *\n
+ * Portable thread manipulation.
+ * - @ref PJ_TLS
+ *\n
+ * Storing data in thread's private data.
+ * - @ref PJ_MUTEX
+ *\n
+ * Mutual exclusion protection.
+ * - @ref PJ_SEM
+ *\n
+ * Semaphores.
+ * - @ref PJ_ATOMIC
+ *\n
+ * Atomic variables and their operations.
+ * - @ref PJ_CRIT_SEC
+ *\n
+ * Fast locking of critical sections.
+ * - @ref PJ_LOCK
+ *\n
+ * High level abstraction for lock objects.
+ * - @ref PJ_EVENT
+ *\n
+ * Event object.
+ * - @ref PJ_TIME
+ *\n
+ * Portable time manipulation.
+ * - @ref PJ_TIMESTAMP
+ *\n
+ * High resolution time value.
+ * - etc.
+ *
+ *
+ * @subsection ll_network_io_sec Low-Level Network I/O
+ *
+ * PJLIB has very portable abstraction and fairly complete set of API for
+ * doing network I/O communications. At the lowest level, PJLIB provides:
+ *
+ * - @ref PJ_SOCK
+ *\n
+ * A highly portable socket abstraction, runs on all kind of
+ * network APIs such as standard BSD socket, Windows socket, Linux
+ * \b kernel socket, PalmOS networking API, etc.
+ *
+ * - @ref pj_addr_resolve
+ *\n
+ * Portable address resolution, which implements #pj_gethostbyname().
+ *
+ * - @ref PJ_SOCK_SELECT
+ *\n
+ * A portable \a select() like API (#pj_sock_select()) which can be
+ * implemented with various back-end.
+ *
+ *
+ * @subsection hl_network_io_sec High-Level Network I/O
+ *
+ * At higher abstraction, PJLIB provides @ref PJ_IOQUEUE,
+ * which promotes creating high performance network
+ * applications by managing asynchronous I/O. This is a passive framework
+ * that utilizes the most effective way to manage asynchronous I/O
+ * on a given platform, such as:
+ * - IoCompletionPort on WinNT,
+ * - on Linux it can use either /dev/epoll or aio.
+ * - or to fall back to use @a select()
+ *
+ * At even a higher abstraction, PJLIB provides @ref PJ_EQUEUE, which
+ * combines asynchronous I/O with timer management and thread management
+ * to fasilitate creating trully high performance, event driven
+ * application.
+ *
+ *
+ * @subsection timer_mgmt_sec Timer Management
+ *
+ * A passive framework for managing timer, see @ref PJ_TIMER for more info.
+ * There is also function to retrieve high resolution timestamp
+ * from the system (see @ref PJ_TIMESTAMP).
+ *
+ *
+ * @subsection data_struct_sec Various Data Structures
+ *
+ * Various data structures are provided in the library:
+ *
+ * - @ref PJ_PSTR
+ * - @ref PJ_ARRAY
+ * - @ref PJ_HASH
+ * - @ref PJ_LIST
+ * - @ref PJ_RBTREE
+ *
+ *
+ * @subsection exception_sec Exception Construct
+ *
+ * A convenient TRY/CATCH like construct to propagate errors, which by
+ * default are used by the @ref PJ_POOL_GROUP "memory pool" and
+ * the lexical scanner in pjlib-util. The exception
+ * construct can be used to write programs like below:
+ *
+ * <pre>
+ * #define SYNTAX_ERROR 1
+ *
+ * PJ_TRY {
+ * msg = NULL;
+ * msg = parse_msg(buf, len);
+ * }
+ * PJ_CATCH ( SYNTAX_ERROR ) {
+ * .. handle error ..
+ * }
+ * PJ_END;
+ * </pre>
+ *
+ * Please see @ref PJ_EXCEPT for more information.
+ *
+ *
+ * @subsection logging_sec Logging Facility
+ *
+ * PJLIB @ref PJ_LOG consists of macros to write logging information to
+ * some output device. Some of the features of the logging facility:
+ *
+ * - the verbosity can be fine-tuned both at compile time (to control
+ * the library size) or run-time (to control the verbosity of the
+ * information).
+ * - output device is configurable (e.g. stdout, printk, file, etc.)
+ * - log decoration is configurable.
+ *
+ * See @ref PJ_LOG for more information.
+ *
+ *
+ * @subsection guid_gen_sec Random and GUID Generation
+ *
+ * PJLIB provides facility to create random string
+ * (#pj_create_random_string()) or globally unique identifier
+ * (see @ref PJ_GUID).
+ *
+ *
+ *
+ * @section configure_app_sec Configuring Application to use PJLIB
+ *
+ * @subsection pjlib_compil_sec Building PJLIB
+ *
+ * Follow the instructions in \ref pjlib_build_sys_pg to build
+ * PJLIB.
+ *
+ * @subsection pjlib_compil_app_sec Building Applications with PJLIB
+ *
+ * Use the following settings when building applications with PJLIB.
+ *
+ * @subsubsection compil_inc_dir_sec Include Search Path
+ *
+ * Add this to your include search path ($PJLIB is PJLIB root directory):
+ * <pre>
+ * $PJLIB/include
+ * </pre>
+ *
+ * @subsubsection compil_inc_file_sec Include PJLIB Header
+ *
+ * To include all PJLIB headers:
+ * \verbatim
+ #include <pjlib.h>
+ \endverbatim
+ *
+ * Alternatively, you can include individual PJLIB headers like this:
+ * \verbatim
+ #include <pj/log.h>
+ #include <pj/os.h>
+ \endverbatim
+ *
+ *
+ * @subsubsection compil_lib_dir_sec Library Path
+ *
+ * Add this to your library search path:
+ * <pre>
+ * $PJLIB/lib
+ * </pre>
+ *
+ * Then add the appropriate PJLIB library to your link specification. For
+ * example, you would add \c libpj-i386-linux-gcc.a when you're building
+ * applications in Linux.
+ *
+ *
+ * @subsection pjlib_fundamentals_sec Principles in Using PJLIB
+ *
+ * Few things that you \b MUST do when using PJLIB, to make sure that
+ * you create trully portable applications.
+ *
+ * @subsubsection call_pjlib_init_sec Call pj_init()
+ *
+ * Before you do anything else, call \c pj_init(). This would make sure that
+ * PJLIB system is properly set up.
+ *
+ * @subsubsection no_ansi_subsec Do NOT Use ANSI C
+ *
+ * Contrary to popular teaching, ANSI C (and LIBC) is not the most portable
+ * library in the world, nor it's the most ubiquitous. For example, LIBC
+ * is not available in Linux kernel. Also normally LIBC will be excluded
+ * from compilation of RTOSes to reduce size.
+ *
+ * So for maximum portability, do NOT use ANSI C. Do not even try to include
+ * any other header files outside <include/pj>. Stick with the functionalities
+ * provided by PJLIB.
+ *
+ *
+ * @subsubsection string_rep_subsubsec Use pj_str_t instead of C Strings
+ *
+ * PJLIB uses pj_str_t instead of normal C strings. You SHOULD follow this
+ * convention too. Remember, ANSI string-h is not always available. And
+ * PJLIB string is faster!
+ *
+ * @subsubsection mem_alloc_subsubsec Use Pool for Memory Allocations
+ *
+ * You MUST NOT use \a malloc() or any other memory allocation functions.
+ * Use PJLIB pool instead! It's faster and most portable.
+ *
+ * @subsection logging_subsubsec Use Logging for Text Display
+ *
+ * DO NOT use <stdio.h> for text output. Use PJLIB logging instead.
+ *
+ *
+ * @section porting_pjlib_sec0 Porting PJLIB
+ *
+ * Please see \ref porting_pjlib_pg page on more information to port
+ * PJLIB to new target.
+ *
+ * @section enjoy_sec Enjoy Using PJLIB!
+ *
+ * We hope that you find PJLIB usefull for your application. If you
+ * have any questions, suggestions, critics, bug fixes, or anything
+ * else, we would be happy to hear it.
+ *
+ * Enjoy using PJLIB!
+ *
+ * Benny Prijono < bennylp at pjproject dot net >
+ */
+
+
+
/*////////////////////////////////////////////////////////////////////////// */
/*
CODING CONVENTION
@@ -439,566 +460,566 @@
*
*/
-
-/*////////////////////////////////////////////////////////////////////////// */
-/*
- BUILDING AND INSTALLING PJLIB
- */
-
-
-
-/**
- * @page pjlib_build_sys_pg Building, and Installing PJLIB
- *
- * @section build_sys_install_sec Build and Installation
- *
- * @subsection build_sys_install_win32_sec Visual Studio
- *
- * The PJLIB Visual Studio workspace supports the building of PJLIB
- * for Win32 target. Although currently only the Visual Studio 6 Workspace is
- * actively maintained, developers with later version of Visual Studio
- * can easily imports VS6 workspace into their IDE.
- *
- * To start building PJLIB projects with Visual Studio 6 or later, open
- * the \a workspace file in the corresponding \b \c build directory. You have
- * several choices on which \a dsw file to open:
- \verbatim
- $PJPROJECT/build/pjproject.dsw
- $PJPROJECT/pjlib/build/pjlib.dsw
- $PJPROJECT/pjsip/build/pjsip.dsw
- ..etc
- \endverbatim
- *
- * The easiest way is to open <tt>pjproject.dsw</tt> file in \b \c $PJPROJECT/build
- * directory. However this will only build the required projects, not
- * the complete projects. For example, the PJLIB test and samples projects
- * are not included in this workspace. To build the complete projects, you must
- * open and build each \a dsw file in \c build directory in each
- * subprojects. For example, to open the complete PJLIB workspace, open
- * <tt>pjlib.dsw</tt> in <tt>$PJPROJECT/pjlib/build</tt> directory.
- *
- *
- * @subsubsection config_site_create_vc_sec Create config_site.h
- *
- * The file <tt><b>$PJPROJECT/pjlib/include/pj/config_site.h</b></tt>
- * is supposed to contain configuration that is specific to your site/target.
- * This file is not part of PJLIB, so you must create it yourself. Normally
- * you just need to create a blank file.
- *
- * The reason why it's not included in PJLIB is so that you would not accidently
- * overwrite your site configuration.
- *
- * If you fail to do this, Visual C will complain with error like:
- *
- * <b>"fatal error C1083: Cannot open include file: 'pj/config_site.h': No such file
- * or directory"</b>.
- *
- * @subsubsection build_vc_subsubsec Build the Projects
- *
- * Just hit the build button!
- *
- *
- * @subsection build_sys_install_unix_sec Make System
- *
- * For other targets, PJLIB provides a rather comprehensive build system
- * that uses GNU \a make (and only GNU \a make will work).
- * Currently, the build system supports building * PJLIB for these targets:
- * - i386/Win32/mingw
- * - i386/Linux
- * - i386/Linux (kernel)
- * - alpha/linux
- * - sparc/SunOS
- * - etc..
- *
- *
- * @subsubsection build_req_sec Requirements
- *
- * In order to use the \c make based build system, you MUST have:
- *
- * - <b>GNU make</b>
- *\n
- * The Makefiles heavily utilize GNU make commands which most likely
- * are not available in other \c make system.
- * - <b>bash</b> shell is recommended.
- *\n
- * Specificly, there is a command <tt>"echo -n"</tt> which may not work
- * in other shells. This command is used when generating dependencies
- * (<tt>make dep</tt>) and it's located in
- * <tt>$PJPROJECT/build/rules.mak</tt>.
- * - <b>ar</b>, <b>ranlib</b> from GNU binutils
- *\n
- * In your system has different <tt>ar</tt> or <tt>ranlib</tt> (e.g. they
- * may have been installed as <tt>gar</tt> and <tt>granlib</tt>), then
- * either you create the relevant symbolic links, <b>or</b> modify
- * <tt>$PJPROJECT/build/cc-gcc.mak</tt> and rename <tt>ar</tt> and
- * <tt>ranlib</tt> to the appropriate names.
- * - <b>gcc</b> to generate dependency.
- *\n
- * Currently the build system uses <tt>"gcc -MM"</tt> to generate build
- * dependencies. If <tt>gcc</tt> is not desired to generate dependency,
- * then either you don't run <tt>make dep</tt>, <b>or</b> edit
- * <tt>$PJPROJECT/build/rules.mak</tt> to calculate dependency using
- * your prefered method. (And let me know when you do so so that I can
- * update the file. :) )
- *
- * @subsubsection build_overview_sec Building the Project
- *
- * Generally, steps required to build the PJLIB are:
- *
- \verbatim
- $ cd /home/user/pjproject # <-- go to $PJPROJECT
- $ vi build.mak # <-- set build target etc
- $ touch pjlib/include/pj/config_site.h
- $ cd pjlib/build # <-- go to projet's build dir
- $ make # <-- build the project
- \endverbatim
- *
- * For other project, \a cd to <tt>build</tt> directory in the project
- * and execute \a make from there.
- *
- * \note For Linux kernel target, there are additional steps required, which
- * will be explained in section \ref linux_kern_target_subsec.
- *
- * @subsubsection build_mak_sec Editing build.mak
- *
- * The \c build.mak file in \c $PJPROJECT root directory is used to
- * specify the build configuration. This file is expected to export
- * the following \a make variables:
- *
- * - <tt><b>MACHINE_NAME</b></tt>
- *\n
- * Target machine/processor, one of: <b>{ i386 | alpha | sparc }</b>.
- *
- * - <tt><b>OS_NAME</b></tt>
- *\n
- * Target operating system, one of: <b>{ win32 | linux |
- * linux-kernel | sunos }</b>.
- *
- * - <tt><b>CC_NAME</b></tt>
- *\n
- * Compiler name: <b>{ gcc | vc }</b>\n
- * (Note that support for Visual C (vc) compiler with the \c make system is
- * experimental, and it will only work when run inside a DOS shell
- * (i.e. <tt>"HOST_NAME=win32"</tt>)).
- *
- * - <tt><b>HOST_NAME</b></tt>
- *\n
- * Build host: <b>{ unix | mingw | win32 }</b>\n
- * (Note: win32 host means a DOS command prompt. Support for this type
- * of development host is experimental).
- *
- * These variables will cause the correct configuration file in
- * \c $PJPROJECT/build directory to be executed by \a make. For
- * example, specifying \c OS_NAME=linux will cause file \c os-linux.mak
- * in \c build directory to be executed. These files contain specific
- * configuration for the option that is selected.
- *
- * For Linux kernel target, you are also required to declare the following
- * variables in this file:
- * - \c KERNEL_DIR: full path of kernel source tree.
- * - \c KERNEL_ARCH: kernel ARCH options (e.g. "ARCH=um"), or leave blank
- * for default.
- * - \c PJPROJECT_DIR: full path of PJPROJECT source tree.
- *
- * Apart from these, there are also additional steps required to build
- * Linux kernel target, which will be explained in \ref linux_kern_target_subsec.
- *
- * @subsubsection build_dir_sec Files in "build" Directory
- *
- * The <tt>*.mak</tt> files in \c $PJPROJECT/build directory are used to specify
- * the configuration for the specified compiler, target machine target
- * operating system, and host options. These files will be executed
- * (included) by \a make during building process, depending on the values
- * specified in <b>$PJPROJECT/build.mak</b> file.
- *
- * Normally you don't need to edit these files, except when you're porting
- * PJLIB to new target.
- *
- * Below are the description of some files in this directory:
- *
- * - <tt>rules.mak</tt>: contains generic rules always included during make.
- * - <tt>cc-gcc.mak</tt>: rules when gcc is used for compiler.
- * - <tt>cc-vc.mak</tt>: rules when MSVC compiler is used.
- * - <tt>host-mingw.mak</tt>: rules for building in mingw host.
- * - <tt>host-unix.mak</tt>: rules for building in Unix/Posix host.
- * - <tt>host-win32.mak</tt>: rules for building in Win32 command console
- * (only valid when VC is used).
- * - <tt>m-i386.mak</tt>: rules when target machine is an i386 processor.
- * - <tt>m-m68k.mak</tt>: rules when target machine is an m68k processor.
- * - <tt>os-linux.mak</tt>: rules when target OS is Linux.
- * - <tt>os-linux-kernel.mak</tt>: rules when PJLIB is to be build as
- * part of Linux kernel.
- * - <tt>os-win32.mak</tt>: rules when target OS is Win32.
- *
- *
- * @subsubsection config_site_create_sec Create config_site.h
- *
- * The file <tt><b>$PJPROJECT/pjlib/include/pj/config_site.h</b></tt>
- * is supposed to contain configuration that is specific to your site/target.
- * This file is not part of PJLIB, so you must create it yourself.
- *
- * The reason why it's not included in PJLIB is so that you would not accidently
- * overwrite your site configuration.
- *
- *
- * @subsubsection invoking_make_sec Invoking make
- *
- * Normally, \a make is invoked in \c build directory under each project.
- * For example, to build PJLIB, you would invoke \a make in
- * \c $PJPROJECT/pjlib/build directory like below:
- *
- \verbatim
- $ cd pjlib/build
- $ make
- \endverbatim
- *
- * Alternatively you may invoke <tt>make</tt> in <tt>$PJPROJECT</tt>
- * directory, to build all projects under that directory (e.g.
- * PJLIB, PJSIP, etc.).
- *
- *
- * @subsubsection linux_kern_target_subsec Linux Kernel Target
- *
- * \note
- * <b>BUILDING APPLICATIONS IN LINUX KERNEL MODE IS A VERY DANGEROUS BUSINESS.
- * YOU MAY CRASH THE WHOLE OF YOUR SYSTEM, CORRUPT YOUR HARDISK, ETC. PJLIB
- * KERNEL MODULES ARE STILL IN EXPERIMENTAL PHASE. DO NOT RUN IT IN PRODUCTION
- * SYSTEMS OR OTHER SYSTEMS WHERE RISK OF LOSS OF DATA IS NOT ACCEPTABLE.
- * YOU HAVE BEEN WARNED.</b>
- *
- * \note
- * <b>User Mode Linux (UML)</b> provides excellent way to experiment with Linux
- * kernel without risking the stability of the host system. See
- * http://user-mode-linux.sourceforge.net for details.
- *
- * \note
- * I only use <b>UML</b> to experiment with PJLIB kernel modules.
- * <b>I wouldn't be so foolish to use my host Linux machine to experiment
- * with this.</b>
- *
- * \note
- * You have been warned.
- *
- * For building PJLIB for Linux kernel target, there are additional steps required.
- * In general, the additional tasks are:
- * - Declare some more variables in <b><tt>build.mak</tt></b> file (this
- * has been explained in \ref build_mak_sec above).
- * - Perform these two small modifications in kernel source tree.
- *
- * There are two small modification need to be applied to the kernel tree.
- *
- * <b>1. Edit <tt>Makefile</tt> in kernel root source tree.</b>
- *
- * Add the following lines at the end of the <tt>Makefile</tt> in your
- * <tt>$KERNEL_SRC</tt> dir:
- \verbatim
-script:
- $(SCRIPT)
- \endverbatim
- *
- * \note Remember to replace spaces with <b>tab</b> in the Makefile.
- *
- * The modification above is needed to capture kernel's \c $CFLAGS and
- * \c $CFLAGS_MODULE which will be used for PJLIB's compilation.
- *
- * <b>2. Add Additional Exports.</b>
- *
- * We need the kernel to export some more symbols for our use. So we declare
- * the additional symbols to be exported in <tt>extra-exports.c</tt> file, and add
- * a this file to be compiled into the kernel:
- *
- * - Copy the file <tt>extra-exports.c</tt> from <tt>pjlib/src/pj</tt>
- * directory to <tt>$KERNEL_SRC/kernel/</tt> directory.
- * - Edit <tt>Makefile</tt> in that directory, and add this line
- * somewhere after the declaration of that variable:
- \verbatim
-obj-y += extra-exports.o
- \endverbatim
- *
- * To illustrate what have been done in your kernel source tree, below
- * is screenshot of my kernel source tree _after_ the modification.
- *
- \verbatim
-[root@vpc-linux linux-2.6.7]# pwd
-/usr/src/linux-2.6.7
-[root@vpc-linux linux-2.6.7]#
-[root@vpc-linux linux-2.6.7]#
-[root@vpc-linux linux-2.6.7]# tail Makefile
-
-endif # skip-makefile
-
-FORCE:
-
-.PHONY: script
-
-script:
- $(SCRIPT)
-
-[root@vpc-linux linux-2.6.7]#
-[root@vpc-linux linux-2.6.7]#
-[root@vpc-linux linux-2.6.7]# head kernel/extra-exports.c
-#include <linux/module.h>
-#include <linux/syscalls.h>
-
-EXPORT_SYMBOL(sys_select);
-
-EXPORT_SYMBOL(sys_epoll_create);
-EXPORT_SYMBOL(sys_epoll_ctl);
-EXPORT_SYMBOL(sys_epoll_wait);
-
-EXPORT_SYMBOL(sys_socket);
-[root@vpc-linux linux-2.6.7]#
-[root@vpc-linux linux-2.6.7]#
-[root@vpc-linux linux-2.6.7]# head -15 kernel/Makefile
-#
-# Makefile for the linux kernel.
-#
-
-obj-y = sched.o fork.o exec_domain.o panic.o printk.o profile.o \
- exit.o itimer.o time.o softirq.o resource.o \
- sysctl.o capability.o ptrace.o timer.o user.o \
- signal.o sys.o kmod.o workqueue.o pid.o \
- rcupdate.o intermodule.o extable.o params.o posix-timers.o \
- kthread.o
-
-obj-y += extra-exports.o
-
-obj-$(CONFIG_FUTEX) += futex.o
-obj-$(CONFIG_GENERIC_ISA_DMA) += dma.o
-[root@vpc-linux linux-2.6.7]#
-
- \endverbatim
- *
- * Then you must rebuild the kernel.
- * If you fail to do this, you won't be able to <b>insmod</b> pjlib.
- *
- * \note You will see a lots of warning messages during pjlib-test compilation.
- * The warning messages complain about unresolved symbols which are defined
- * in pjlib module. You can safely ignore these warnings. However, you can not
- * ignore warnings about non-pjlib unresolved symbols.
- *
- *
- * @subsection makefile_explained_sec Makefile Explained
- *
- * The \a Makefile for each project (e.g. PJLIB, PJSIP, etc) should be
- * very similar in the contents. The Makefile is located under \c build
- * directory in each project subdir.
- *
- * @subsubsection pjlib_makefile_subsec PJLIB Makefile.
- *
- * Below is PJLIB's Makefile:
- *
- * \include build/Makefile
- *
- * @subsubsection pjlib_os_makefile_subsec PJLIB os-linux.mak.
- *
- * Below is file <tt><b>os-linux.mak</b></tt> file in
- * <tt>$PJPROJECT/pjlib/build</tt> directory,
- * which is OS specific configuration file for Linux target that is specific
- * for PJLIB project. For \b global OS specific configuration, please see
- * <tt>$PJPROJECT/build/os-*.mak</tt>.
- *
- * \include build/os-linux.mak
- *
- */
-
-
-/*////////////////////////////////////////////////////////////////////////// */
-/*
- PORTING PJLIB
- */
-
-
-
-/**
- * @page porting_pjlib_pg Porting PJLIB
- *
- *
- * @section new_arch_sec Porting to New CPU Architecture
- *
- * Below is step-by-step guide to add support for new CPU architecture.
- * This sample is based on porting to Alpha architecture; however steps for
- * porting to other CPU architectures should be pretty similar.
- *
- * Also note that in this example, the operating system used is <b>Linux</b>.
- * Should you wish to add support for new operating system, then follow
- * the next section \ref porting_os_sec.
- *
- * Step-by-step guide to port to new CPU architecture:
- * - decide the name for the new architecture. In this case, we choose
- * <tt><b>alpha</b></tt>.
- * - edit file <tt>$PJPROJECT/build.mak</tt>, and add new section for
- * the new target:
- * <pre>
- * #
- * # Linux alpha, gcc
- * #
- * export MACHINE_NAME := <b>alpha</b>
- * export OS_NAME := linux
- * export CC_NAME := gcc
- * export HOST_NAME := unix
- * </pre>
- *
- * - create a new file <tt>$PJPROJECT/build/<b>m-alpha</b>.mak</tt>.
- * Alternatively create a copy from other file in this directory.
- * The contents of this file will look something like:
- * <pre>
- * export M_CFLAGS := $(CC_DEF)<b>PJ_M_ALPHA=1</b>
- * export M_CXXFLAGS :=
- * export M_LDFLAGS :=
- * export M_SOURCES :=
- * </pre>
- * - create a new file <tt>$PJPROJECT/pjlib/include/pj/compat/<b>m_alpha.h</b></tt>.
- * Alternatively create a copy from other header file in this directory.
- * The contents of this file will look something like:
- * <pre>
- * #define PJ_HAS_PENTIUM 0
- * #define PJ_IS_LITTLE_ENDIAN 1
- * #define PJ_IS_BIG_ENDIAN 0
- * </pre>
- * - edit <tt>pjlib/include/pj/<b>config.h</b></tt>. Add new processor
- * configuration in this header file, like follows:
- * <pre>
- * ...
- * #elif defined (PJ_M_ALPHA) && PJ_M_ALPHA != 0
- * # include <pj/compat/m_alpha.h>
- * ...
- * </pre>
- * - done. Build PJLIB with:
- * <pre>
- * $ cd $PJPROJECT/pjlib/build
- * $ make dep
- * $ make clean
- * $ make
- * </pre>
- *
- * @section porting_os_sec Porting to New Operating System Target
- *
- * This section will try to give you rough guideline on how to
- * port PJLIB to a new target. As a sample, we give the target a name tag,
- * for example <tt><b>xos</b></tt> (for X OS).
- *
- * @subsection new_compat_os_h_file_sec Create New Compat Header File
- *
- * You'll need to create a new header file
- * <b><tt>include/pj/compat/os_xos.h</tt></b>. You can copy as a
- * template other header file and edit it accordingly.
- *
- * @subsection modify_config_h_file_sec Modify config.h
- *
- * Then modify file <b><tt>include/pj/config.h</tt></b> to include
- * this file accordingly (e.g. when macro <tt><b>PJ_XOS</b></tt> is
- * defined):
- *
- \verbatim
- ...
- #elif defined(PJ_XOS)
- # include <pj/compat/os_xos.h>
- #else
- #...
- \endverbatim
- *
- * @subsection new_target_mak_file_sec Create New Global Make Config File
- *
- * Then you'll need to create global configuration file that
- * is specific for this OS, i.e. <tt><b>os-xos.mak</b></tt> in
- * <tt><b>$PJPROJECT/build</b></tt> directory.
- *
- * At very minimum, the file will normally need to define
- * <tt><b>PJ_XOS=1</b></tt> in the \c CFLAGS section:
- *
- \verbatim
-#
-# $PJPROJECT/build/os-xos.mak:
-#
-export OS_CFLAGS := $(CC_DEF)PJ_XOS=1
-export OS_CXXFLAGS :=
-export OS_LDFLAGS :=
-export OS_SOURCES :=
- \endverbatim
- *
- *
- * @subsection new_target_prj_mak_file_sec Create New Project's Make Config File
- *
- * Then you'll need to create xos-specific configuration file
- * for PJLIB. This file is also named <tt><b>os-xos.mak</b></tt>,
- * but its located in <tt><b>pjlib/build</b></tt> directory.
- * This file will specify source files that are specific to
- * this OS to be included in the build process.
- *
- * Below is a sample:
- \verbatim
-#
-# pjlib/build/os-xos.mak:
-# XOS specific configuration for PJLIB.
-#
-export PJLIB_OBJS += os_core_xos.o \
- os_error_unix.o \
- os_time_ansi.o
-export TEST_OBJS += main.o
-export TARGETS = pjlib pjlib-test
- \endverbatim
- *
- * @subsection new_target_src_sec Create and Edit Source Files
- *
- * You'll normally need to create at least these files:
- * - <tt><b>os_core_xos.c</b></tt>: core OS specific
- * functionality.
- * - <tt><b>os_timestamp_xos.c</b></tt>: how to get timestamp
- * in this OS.
- *
- * Depending on how things are done in your OS, you may need
- * to create these files:
- * - <tt><b>os_error_*.c</b></tt>: how to manipulate
- * OS error codes. Alternatively you may use existing
- * <tt>os_error_unix.c</tt> if the OS has \c errno and
- * \c strerror() function.
- * - <tt><b>ioqueue_*.c</b></tt>: if the OS has specific method
- * to perform asynchronous I/O. Alternatively you may
- * use existing <tt>ioqueue_select.c</tt> if the OS supports
- * \c select() function call.
- * - <tt><b>sock_*.c</b></tt>: if the OS has specific method
- * to perform socket communication. Alternatively you may
- * use existing <tt>sock_bsd.c</tt> if the OS supports
- * BSD socket API, and edit <tt>include/pj/compat/socket.h</tt>
- * file accordingly.
- *
- * You will also need to check various files in
- * <tt><b>include/pj/compat/*.h</b></tt>, to see if they're
- * compatible with your OS.
- *
- * @subsection new_target_build_file_sec Build The Project
- *
- * After basic building blocks have been created for the OS, then
- * the easiest way to see which parts need to be fixed is by building
- * the project and see the error messages.
- *
- * @subsection new_target_edit_vs_new_file_sec Editing Existing Files vs Creating New File
- *
- * When you encounter compatibility errors in PJLIB during porting,
- * you have three options on how to fix the error:
- * - edit the existing <tt>*.c</tt> file, and give it <tt>#ifdef</tt>
- * switch for the new OS, or
- * - edit <tt>include/pj/compat/*.h</tt> instead, or
- * - create a totally new file.
- *
- * Basicly there is no strict rule on which approach is the best
- * to use, however the following guidelines may be used:
- * - if the file is expected to be completely different than
- * any existing file, then perhaps you should create a completely
- * new file. For example, file <tt>os_core_xxx.c</tt> will
- * normally be different for each OS flavour.
- * - if the difference can be localized in <tt>include/compat</tt>
- * header file, and existing <tt>#ifdef</tt> switch is there,
- * then preferably you should edit this <tt>include/compat</tt>
- * header file.
- * - if the existing <tt>*.c</tt> file has <tt>#ifdef</tt> switch,
- * then you may add another <tt>#elif</tt> switch there. This
- * normally is used for behaviors that are not totally
- * different on each platform.
- * - other than that above, use your own judgement on whether
- * to edit the file or create new file etc.
- */
-
-#endif /* __PJ_DOXYGEN_H__ */
-
+
+/*////////////////////////////////////////////////////////////////////////// */
+/*
+ BUILDING AND INSTALLING PJLIB
+ */
+
+
+
+/**
+ * @page pjlib_build_sys_pg Building, and Installing PJLIB
+ *
+ * @section build_sys_install_sec Build and Installation
+ *
+ * @subsection build_sys_install_win32_sec Visual Studio
+ *
+ * The PJLIB Visual Studio workspace supports the building of PJLIB
+ * for Win32 target. Although currently only the Visual Studio 6 Workspace is
+ * actively maintained, developers with later version of Visual Studio
+ * can easily imports VS6 workspace into their IDE.
+ *
+ * To start building PJLIB projects with Visual Studio 6 or later, open
+ * the \a workspace file in the corresponding \b \c build directory. You have
+ * several choices on which \a dsw file to open:
+ \verbatim
+ $PJPROJECT/build/pjproject.dsw
+ $PJPROJECT/pjlib/build/pjlib.dsw
+ $PJPROJECT/pjsip/build/pjsip.dsw
+ ..etc
+ \endverbatim
+ *
+ * The easiest way is to open <tt>pjproject.dsw</tt> file in \b \c $PJPROJECT/build
+ * directory. However this will only build the required projects, not
+ * the complete projects. For example, the PJLIB test and samples projects
+ * are not included in this workspace. To build the complete projects, you must
+ * open and build each \a dsw file in \c build directory in each
+ * subprojects. For example, to open the complete PJLIB workspace, open
+ * <tt>pjlib.dsw</tt> in <tt>$PJPROJECT/pjlib/build</tt> directory.
+ *
+ *
+ * @subsubsection config_site_create_vc_sec Create config_site.h
+ *
+ * The file <tt><b>$PJPROJECT/pjlib/include/pj/config_site.h</b></tt>
+ * is supposed to contain configuration that is specific to your site/target.
+ * This file is not part of PJLIB, so you must create it yourself. Normally
+ * you just need to create a blank file.
+ *
+ * The reason why it's not included in PJLIB is so that you would not accidently
+ * overwrite your site configuration.
+ *
+ * If you fail to do this, Visual C will complain with error like:
+ *
+ * <b>"fatal error C1083: Cannot open include file: 'pj/config_site.h': No such file
+ * or directory"</b>.
+ *
+ * @subsubsection build_vc_subsubsec Build the Projects
+ *
+ * Just hit the build button!
+ *
+ *
+ * @subsection build_sys_install_unix_sec Make System
+ *
+ * For other targets, PJLIB provides a rather comprehensive build system
+ * that uses GNU \a make (and only GNU \a make will work).
+ * Currently, the build system supports building * PJLIB for these targets:
+ * - i386/Win32/mingw
+ * - i386/Linux
+ * - i386/Linux (kernel)
+ * - alpha/linux
+ * - sparc/SunOS
+ * - etc..
+ *
+ *
+ * @subsubsection build_req_sec Requirements
+ *
+ * In order to use the \c make based build system, you MUST have:
+ *
+ * - <b>GNU make</b>
+ *\n
+ * The Makefiles heavily utilize GNU make commands which most likely
+ * are not available in other \c make system.
+ * - <b>bash</b> shell is recommended.
+ *\n
+ * Specificly, there is a command <tt>"echo -n"</tt> which may not work
+ * in other shells. This command is used when generating dependencies
+ * (<tt>make dep</tt>) and it's located in
+ * <tt>$PJPROJECT/build/rules.mak</tt>.
+ * - <b>ar</b>, <b>ranlib</b> from GNU binutils
+ *\n
+ * In your system has different <tt>ar</tt> or <tt>ranlib</tt> (e.g. they
+ * may have been installed as <tt>gar</tt> and <tt>granlib</tt>), then
+ * either you create the relevant symbolic links, <b>or</b> modify
+ * <tt>$PJPROJECT/build/cc-gcc.mak</tt> and rename <tt>ar</tt> and
+ * <tt>ranlib</tt> to the appropriate names.
+ * - <b>gcc</b> to generate dependency.
+ *\n
+ * Currently the build system uses <tt>"gcc -MM"</tt> to generate build
+ * dependencies. If <tt>gcc</tt> is not desired to generate dependency,
+ * then either you don't run <tt>make dep</tt>, <b>or</b> edit
+ * <tt>$PJPROJECT/build/rules.mak</tt> to calculate dependency using
+ * your prefered method. (And let me know when you do so so that I can
+ * update the file. :) )
+ *
+ * @subsubsection build_overview_sec Building the Project
+ *
+ * Generally, steps required to build the PJLIB are:
+ *
+ \verbatim
+ $ cd /home/user/pjproject # <-- go to $PJPROJECT
+ $ vi build.mak # <-- set build target etc
+ $ touch pjlib/include/pj/config_site.h
+ $ cd pjlib/build # <-- go to projet's build dir
+ $ make # <-- build the project
+ \endverbatim
+ *
+ * For other project, \a cd to <tt>build</tt> directory in the project
+ * and execute \a make from there.
+ *
+ * \note For Linux kernel target, there are additional steps required, which
+ * will be explained in section \ref linux_kern_target_subsec.
+ *
+ * @subsubsection build_mak_sec Editing build.mak
+ *
+ * The \c build.mak file in \c $PJPROJECT root directory is used to
+ * specify the build configuration. This file is expected to export
+ * the following \a make variables:
+ *
+ * - <tt><b>MACHINE_NAME</b></tt>
+ *\n
+ * Target machine/processor, one of: <b>{ i386 | alpha | sparc }</b>.
+ *
+ * - <tt><b>OS_NAME</b></tt>
+ *\n
+ * Target operating system, one of: <b>{ win32 | linux |
+ * linux-kernel | sunos }</b>.
+ *
+ * - <tt><b>CC_NAME</b></tt>
+ *\n
+ * Compiler name: <b>{ gcc | vc }</b>\n
+ * (Note that support for Visual C (vc) compiler with the \c make system is
+ * experimental, and it will only work when run inside a DOS shell
+ * (i.e. <tt>"HOST_NAME=win32"</tt>)).
+ *
+ * - <tt><b>HOST_NAME</b></tt>
+ *\n
+ * Build host: <b>{ unix | mingw | win32 }</b>\n
+ * (Note: win32 host means a DOS command prompt. Support for this type
+ * of development host is experimental).
+ *
+ * These variables will cause the correct configuration file in
+ * \c $PJPROJECT/build directory to be executed by \a make. For
+ * example, specifying \c OS_NAME=linux will cause file \c os-linux.mak
+ * in \c build directory to be executed. These files contain specific
+ * configuration for the option that is selected.
+ *
+ * For Linux kernel target, you are also required to declare the following
+ * variables in this file:
+ * - \c KERNEL_DIR: full path of kernel source tree.
+ * - \c KERNEL_ARCH: kernel ARCH options (e.g. "ARCH=um"), or leave blank
+ * for default.
+ * - \c PJPROJECT_DIR: full path of PJPROJECT source tree.
+ *
+ * Apart from these, there are also additional steps required to build
+ * Linux kernel target, which will be explained in \ref linux_kern_target_subsec.
+ *
+ * @subsubsection build_dir_sec Files in "build" Directory
+ *
+ * The <tt>*.mak</tt> files in \c $PJPROJECT/build directory are used to specify
+ * the configuration for the specified compiler, target machine target
+ * operating system, and host options. These files will be executed
+ * (included) by \a make during building process, depending on the values
+ * specified in <b>$PJPROJECT/build.mak</b> file.
+ *
+ * Normally you don't need to edit these files, except when you're porting
+ * PJLIB to new target.
+ *
+ * Below are the description of some files in this directory:
+ *
+ * - <tt>rules.mak</tt>: contains generic rules always included during make.
+ * - <tt>cc-gcc.mak</tt>: rules when gcc is used for compiler.
+ * - <tt>cc-vc.mak</tt>: rules when MSVC compiler is used.
+ * - <tt>host-mingw.mak</tt>: rules for building in mingw host.
+ * - <tt>host-unix.mak</tt>: rules for building in Unix/Posix host.
+ * - <tt>host-win32.mak</tt>: rules for building in Win32 command console
+ * (only valid when VC is used).
+ * - <tt>m-i386.mak</tt>: rules when target machine is an i386 processor.
+ * - <tt>m-m68k.mak</tt>: rules when target machine is an m68k processor.
+ * - <tt>os-linux.mak</tt>: rules when target OS is Linux.
+ * - <tt>os-linux-kernel.mak</tt>: rules when PJLIB is to be build as
+ * part of Linux kernel.
+ * - <tt>os-win32.mak</tt>: rules when target OS is Win32.
+ *
+ *
+ * @subsubsection config_site_create_sec Create config_site.h
+ *
+ * The file <tt><b>$PJPROJECT/pjlib/include/pj/config_site.h</b></tt>
+ * is supposed to contain configuration that is specific to your site/target.
+ * This file is not part of PJLIB, so you must create it yourself.
+ *
+ * The reason why it's not included in PJLIB is so that you would not accidently
+ * overwrite your site configuration.
+ *
+ *
+ * @subsubsection invoking_make_sec Invoking make
+ *
+ * Normally, \a make is invoked in \c build directory under each project.
+ * For example, to build PJLIB, you would invoke \a make in
+ * \c $PJPROJECT/pjlib/build directory like below:
+ *
+ \verbatim
+ $ cd pjlib/build
+ $ make
+ \endverbatim
+ *
+ * Alternatively you may invoke <tt>make</tt> in <tt>$PJPROJECT</tt>
+ * directory, to build all projects under that directory (e.g.
+ * PJLIB, PJSIP, etc.).
+ *
+ *
+ * @subsubsection linux_kern_target_subsec Linux Kernel Target
+ *
+ * \note
+ * <b>BUILDING APPLICATIONS IN LINUX KERNEL MODE IS A VERY DANGEROUS BUSINESS.
+ * YOU MAY CRASH THE WHOLE OF YOUR SYSTEM, CORRUPT YOUR HARDISK, ETC. PJLIB
+ * KERNEL MODULES ARE STILL IN EXPERIMENTAL PHASE. DO NOT RUN IT IN PRODUCTION
+ * SYSTEMS OR OTHER SYSTEMS WHERE RISK OF LOSS OF DATA IS NOT ACCEPTABLE.
+ * YOU HAVE BEEN WARNED.</b>
+ *
+ * \note
+ * <b>User Mode Linux (UML)</b> provides excellent way to experiment with Linux
+ * kernel without risking the stability of the host system. See
+ * http://user-mode-linux.sourceforge.net for details.
+ *
+ * \note
+ * I only use <b>UML</b> to experiment with PJLIB kernel modules.
+ * <b>I wouldn't be so foolish to use my host Linux machine to experiment
+ * with this.</b>
+ *
+ * \note
+ * You have been warned.
+ *
+ * For building PJLIB for Linux kernel target, there are additional steps required.
+ * In general, the additional tasks are:
+ * - Declare some more variables in <b><tt>build.mak</tt></b> file (this
+ * has been explained in \ref build_mak_sec above).
+ * - Perform these two small modifications in kernel source tree.
+ *
+ * There are two small modification need to be applied to the kernel tree.
+ *
+ * <b>1. Edit <tt>Makefile</tt> in kernel root source tree.</b>
+ *
+ * Add the following lines at the end of the <tt>Makefile</tt> in your
+ * <tt>$KERNEL_SRC</tt> dir:
+ \verbatim
+script:
+ $(SCRIPT)
+ \endverbatim
+ *
+ * \note Remember to replace spaces with <b>tab</b> in the Makefile.
+ *
+ * The modification above is needed to capture kernel's \c $CFLAGS and
+ * \c $CFLAGS_MODULE which will be used for PJLIB's compilation.
+ *
+ * <b>2. Add Additional Exports.</b>
+ *
+ * We need the kernel to export some more symbols for our use. So we declare
+ * the additional symbols to be exported in <tt>extra-exports.c</tt> file, and add
+ * a this file to be compiled into the kernel:
+ *
+ * - Copy the file <tt>extra-exports.c</tt> from <tt>pjlib/src/pj</tt>
+ * directory to <tt>$KERNEL_SRC/kernel/</tt> directory.
+ * - Edit <tt>Makefile</tt> in that directory, and add this line
+ * somewhere after the declaration of that variable:
+ \verbatim
+obj-y += extra-exports.o
+ \endverbatim
+ *
+ * To illustrate what have been done in your kernel source tree, below
+ * is screenshot of my kernel source tree _after_ the modification.
+ *
+ \verbatim
+[root@vpc-linux linux-2.6.7]# pwd
+/usr/src/linux-2.6.7
+[root@vpc-linux linux-2.6.7]#
+[root@vpc-linux linux-2.6.7]#
+[root@vpc-linux linux-2.6.7]# tail Makefile
+
+endif # skip-makefile
+
+FORCE:
+
+.PHONY: script
+
+script:
+ $(SCRIPT)
+
+[root@vpc-linux linux-2.6.7]#
+[root@vpc-linux linux-2.6.7]#
+[root@vpc-linux linux-2.6.7]# head kernel/extra-exports.c
+#include <linux/module.h>
+#include <linux/syscalls.h>
+
+EXPORT_SYMBOL(sys_select);
+
+EXPORT_SYMBOL(sys_epoll_create);
+EXPORT_SYMBOL(sys_epoll_ctl);
+EXPORT_SYMBOL(sys_epoll_wait);
+
+EXPORT_SYMBOL(sys_socket);
+[root@vpc-linux linux-2.6.7]#
+[root@vpc-linux linux-2.6.7]#
+[root@vpc-linux linux-2.6.7]# head -15 kernel/Makefile
+#
+# Makefile for the linux kernel.
+#
+
+obj-y = sched.o fork.o exec_domain.o panic.o printk.o profile.o \
+ exit.o itimer.o time.o softirq.o resource.o \
+ sysctl.o capability.o ptrace.o timer.o user.o \
+ signal.o sys.o kmod.o workqueue.o pid.o \
+ rcupdate.o intermodule.o extable.o params.o posix-timers.o \
+ kthread.o
+
+obj-y += extra-exports.o
+
+obj-$(CONFIG_FUTEX) += futex.o
+obj-$(CONFIG_GENERIC_ISA_DMA) += dma.o
+[root@vpc-linux linux-2.6.7]#
+
+ \endverbatim
+ *
+ * Then you must rebuild the kernel.
+ * If you fail to do this, you won't be able to <b>insmod</b> pjlib.
+ *
+ * \note You will see a lots of warning messages during pjlib-test compilation.
+ * The warning messages complain about unresolved symbols which are defined
+ * in pjlib module. You can safely ignore these warnings. However, you can not
+ * ignore warnings about non-pjlib unresolved symbols.
+ *
+ *
+ * @subsection makefile_explained_sec Makefile Explained
+ *
+ * The \a Makefile for each project (e.g. PJLIB, PJSIP, etc) should be
+ * very similar in the contents. The Makefile is located under \c build
+ * directory in each project subdir.
+ *
+ * @subsubsection pjlib_makefile_subsec PJLIB Makefile.
+ *
+ * Below is PJLIB's Makefile:
+ *
+ * \include build/Makefile
+ *
+ * @subsubsection pjlib_os_makefile_subsec PJLIB os-linux.mak.
+ *
+ * Below is file <tt><b>os-linux.mak</b></tt> file in
+ * <tt>$PJPROJECT/pjlib/build</tt> directory,
+ * which is OS specific configuration file for Linux target that is specific
+ * for PJLIB project. For \b global OS specific configuration, please see
+ * <tt>$PJPROJECT/build/os-*.mak</tt>.
+ *
+ * \include build/os-linux.mak
+ *
+ */
+
+
+/*////////////////////////////////////////////////////////////////////////// */
+/*
+ PORTING PJLIB
+ */
+
+
+
+/**
+ * @page porting_pjlib_pg Porting PJLIB
+ *
+ *
+ * @section new_arch_sec Porting to New CPU Architecture
+ *
+ * Below is step-by-step guide to add support for new CPU architecture.
+ * This sample is based on porting to Alpha architecture; however steps for
+ * porting to other CPU architectures should be pretty similar.
+ *
+ * Also note that in this example, the operating system used is <b>Linux</b>.
+ * Should you wish to add support for new operating system, then follow
+ * the next section \ref porting_os_sec.
+ *
+ * Step-by-step guide to port to new CPU architecture:
+ * - decide the name for the new architecture. In this case, we choose
+ * <tt><b>alpha</b></tt>.
+ * - edit file <tt>$PJPROJECT/build.mak</tt>, and add new section for
+ * the new target:
+ * <pre>
+ * #
+ * # Linux alpha, gcc
+ * #
+ * export MACHINE_NAME := <b>alpha</b>
+ * export OS_NAME := linux
+ * export CC_NAME := gcc
+ * export HOST_NAME := unix
+ * </pre>
+ *
+ * - create a new file <tt>$PJPROJECT/build/<b>m-alpha</b>.mak</tt>.
+ * Alternatively create a copy from other file in this directory.
+ * The contents of this file will look something like:
+ * <pre>
+ * export M_CFLAGS := $(CC_DEF)<b>PJ_M_ALPHA=1</b>
+ * export M_CXXFLAGS :=
+ * export M_LDFLAGS :=
+ * export M_SOURCES :=
+ * </pre>
+ * - create a new file <tt>$PJPROJECT/pjlib/include/pj/compat/<b>m_alpha.h</b></tt>.
+ * Alternatively create a copy from other header file in this directory.
+ * The contents of this file will look something like:
+ * <pre>
+ * #define PJ_HAS_PENTIUM 0
+ * #define PJ_IS_LITTLE_ENDIAN 1
+ * #define PJ_IS_BIG_ENDIAN 0
+ * </pre>
+ * - edit <tt>pjlib/include/pj/<b>config.h</b></tt>. Add new processor
+ * configuration in this header file, like follows:
+ * <pre>
+ * ...
+ * #elif defined (PJ_M_ALPHA) && PJ_M_ALPHA != 0
+ * # include <pj/compat/m_alpha.h>
+ * ...
+ * </pre>
+ * - done. Build PJLIB with:
+ * <pre>
+ * $ cd $PJPROJECT/pjlib/build
+ * $ make dep
+ * $ make clean
+ * $ make
+ * </pre>
+ *
+ * @section porting_os_sec Porting to New Operating System Target
+ *
+ * This section will try to give you rough guideline on how to
+ * port PJLIB to a new target. As a sample, we give the target a name tag,
+ * for example <tt><b>xos</b></tt> (for X OS).
+ *
+ * @subsection new_compat_os_h_file_sec Create New Compat Header File
+ *
+ * You'll need to create a new header file
+ * <b><tt>include/pj/compat/os_xos.h</tt></b>. You can copy as a
+ * template other header file and edit it accordingly.
+ *
+ * @subsection modify_config_h_file_sec Modify config.h
+ *
+ * Then modify file <b><tt>include/pj/config.h</tt></b> to include
+ * this file accordingly (e.g. when macro <tt><b>PJ_XOS</b></tt> is
+ * defined):
+ *
+ \verbatim
+ ...
+ #elif defined(PJ_XOS)
+ # include <pj/compat/os_xos.h>
+ #else
+ #...
+ \endverbatim
+ *
+ * @subsection new_target_mak_file_sec Create New Global Make Config File
+ *
+ * Then you'll need to create global configuration file that
+ * is specific for this OS, i.e. <tt><b>os-xos.mak</b></tt> in
+ * <tt><b>$PJPROJECT/build</b></tt> directory.
+ *
+ * At very minimum, the file will normally need to define
+ * <tt><b>PJ_XOS=1</b></tt> in the \c CFLAGS section:
+ *
+ \verbatim
+#
+# $PJPROJECT/build/os-xos.mak:
+#
+export OS_CFLAGS := $(CC_DEF)PJ_XOS=1
+export OS_CXXFLAGS :=
+export OS_LDFLAGS :=
+export OS_SOURCES :=
+ \endverbatim
+ *
+ *
+ * @subsection new_target_prj_mak_file_sec Create New Project's Make Config File
+ *
+ * Then you'll need to create xos-specific configuration file
+ * for PJLIB. This file is also named <tt><b>os-xos.mak</b></tt>,
+ * but its located in <tt><b>pjlib/build</b></tt> directory.
+ * This file will specify source files that are specific to
+ * this OS to be included in the build process.
+ *
+ * Below is a sample:
+ \verbatim
+#
+# pjlib/build/os-xos.mak:
+# XOS specific configuration for PJLIB.
+#
+export PJLIB_OBJS += os_core_xos.o \
+ os_error_unix.o \
+ os_time_ansi.o
+export TEST_OBJS += main.o
+export TARGETS = pjlib pjlib-test
+ \endverbatim
+ *
+ * @subsection new_target_src_sec Create and Edit Source Files
+ *
+ * You'll normally need to create at least these files:
+ * - <tt><b>os_core_xos.c</b></tt>: core OS specific
+ * functionality.
+ * - <tt><b>os_timestamp_xos.c</b></tt>: how to get timestamp
+ * in this OS.
+ *
+ * Depending on how things are done in your OS, you may need
+ * to create these files:
+ * - <tt><b>os_error_*.c</b></tt>: how to manipulate
+ * OS error codes. Alternatively you may use existing
+ * <tt>os_error_unix.c</tt> if the OS has \c errno and
+ * \c strerror() function.
+ * - <tt><b>ioqueue_*.c</b></tt>: if the OS has specific method
+ * to perform asynchronous I/O. Alternatively you may
+ * use existing <tt>ioqueue_select.c</tt> if the OS supports
+ * \c select() function call.
+ * - <tt><b>sock_*.c</b></tt>: if the OS has specific method
+ * to perform socket communication. Alternatively you may
+ * use existing <tt>sock_bsd.c</tt> if the OS supports
+ * BSD socket API, and edit <tt>include/pj/compat/socket.h</tt>
+ * file accordingly.
+ *
+ * You will also need to check various files in
+ * <tt><b>include/pj/compat/*.h</b></tt>, to see if they're
+ * compatible with your OS.
+ *
+ * @subsection new_target_build_file_sec Build The Project
+ *
+ * After basic building blocks have been created for the OS, then
+ * the easiest way to see which parts need to be fixed is by building
+ * the project and see the error messages.
+ *
+ * @subsection new_target_edit_vs_new_file_sec Editing Existing Files vs Creating New File
+ *
+ * When you encounter compatibility errors in PJLIB during porting,
+ * you have three options on how to fix the error:
+ * - edit the existing <tt>*.c</tt> file, and give it <tt>#ifdef</tt>
+ * switch for the new OS, or
+ * - edit <tt>include/pj/compat/*.h</tt> instead, or
+ * - create a totally new file.
+ *
+ * Basicly there is no strict rule on which approach is the best
+ * to use, however the following guidelines may be used:
+ * - if the file is expected to be completely different than
+ * any existing file, then perhaps you should create a completely
+ * new file. For example, file <tt>os_core_xxx.c</tt> will
+ * normally be different for each OS flavour.
+ * - if the difference can be localized in <tt>include/compat</tt>
+ * header file, and existing <tt>#ifdef</tt> switch is there,
+ * then preferably you should edit this <tt>include/compat</tt>
+ * header file.
+ * - if the existing <tt>*.c</tt> file has <tt>#ifdef</tt> switch,
+ * then you may add another <tt>#elif</tt> switch there. This
+ * normally is used for behaviors that are not totally
+ * different on each platform.
+ * - other than that above, use your own judgement on whether
+ * to edit the file or create new file etc.
+ */
+
+#endif /* __PJ_DOXYGEN_H__ */
+
diff --git a/pjlib/include/pj/equeue.h b/pjlib/include/pj/equeue.h
index cc751e2..44979b0 100644
--- a/pjlib/include/pj/equeue.h
+++ b/pjlib/include/pj/equeue.h
@@ -1,321 +1,342 @@
-/* $Id$
- *
- */
-#ifndef __PJ_EQUEUE_H__
-#define __PJ_EQUEUE_H__
-
-/**
- * @file equeue.h
- * @brief Event Queue
- */
-#include <pj/types.h>
-
-
-PJ_BEGIN_DECL
-
-/**
- * @defgroup PJ_EQUEUE Event Queue
- * @brief Event Queue
- * @ingroup PJ_OS
- * @{
- */
-
-
-/**
- * Opaque data type for Event Queue.
- */
-typedef struct pj_equeue_t pj_equeue_t;
-
-/**
- * Opaque data type for Event Queue key.
- */
-typedef struct pj_equeue_key_t pj_equeue_key_t;
-
-
-/**
- * This structure describes the callbacks to be called when I/O operation
- * completes.
- */
-typedef struct pj_io_callback
-{
- /**
- * This callback is called when #pj_equeue_read, #pj_equeue_recv or
- * #pj_equeue_recvfrom completes.
- *
- * @param key The key.
- * @param bytes_read The size of data that has just been read.
- */
- void (*on_read_complete)(pj_equeue_key_t *key, pj_ssize_t bytes_read);
-
- /**
- * This callback is called when #pj_equeue_write, #pj_equeue_send, or
- * #pj_equeue_sendto completes.
- *
- * @param key The key.
- * @param bytes_read The size of data that has just been written.
- */
- void (*on_write_complete)(pj_equeue_key_t *key, pj_ssize_t bytes_sent);
-
- /**
- * This callback is called when #pj_equeue_accept completes.
- *
- * @param key The key.
- * @param status Zero if the operation completes successfully.
- */
- void (*on_accept_complete)(pj_equeue_key_t *key, int status);
-
- /**
- * This callback is called when #pj_equeue_connect completes.
- *
- * @param key The key.
- * @param status Zero if the operation completes successfully.
- */
- void (*on_connect_complete)(pj_equeue_key_t *key, int status);
-
-} pj_io_callback;
-
-/**
- * Event Queue options.
- */
-typedef struct pj_equeue_options
-{
- /** Maximum number of threads that are allowed to access Event Queue
- * simulteneously.
- */
- unsigned nb_threads;
-
- /** If non-zero, then no mutex protection will be used. */
- pj_bool_t no_lock;
-
- /** Interval of the busy loop inside the event queue.
- * The time resolution here determines the accuracy of the
- * timer in the Event Queue.
- */
- pj_time_val poll_interval;
-
-} pj_equeue_options;
-
-
-/**
- * Error value returned by I/O operations to indicate that the operation
- * can't complete immediately and will complete later.
- */
-#define PJ_EQUEUE_PENDING (-2)
-
-/**
- * Types of Event Queue operation.
- */
-typedef enum pj_equeue_op
-{
- PJ_EQUEUE_OP_NONE = 0, /**< No operation. */
- PJ_EQUEUE_OP_READ = 1, /**< read() operation. */
- PJ_EQUEUE_OP_RECV_FROM = 2, /**< recvfrom() operation. */
- PJ_EQUEUE_OP_WRITE = 4, /**< write() operation. */
- PJ_EQUEUE_OP_SEND_TO = 8, /**< sendto() operation. */
-#if defined(PJ_HAS_TCP) && PJ_HAS_TCP != 0
- PJ_EQUEUE_OP_ACCEPT = 16, /**< accept() operation. */
- PJ_EQUEUE_OP_CONNECT = 32, /**< connect() operation. */
-#endif /* PJ_HAS_TCP */
-} pj_equeue_op;
-
-
-
-/**
- * Initialize Event Queue options with default values.
- *
- * @param options Event Queue options.
- */
-PJ_DECL(void) pj_equeue_options_init(pj_equeue_options *options);
-
-/**
- * Create a new Event Queue framework.
- *
- * @param pool The pool to allocate the event queue structure.
- * @param options Event queue options, or if NULL is given, then
- * default options will be used.
- * @param equeue Pointer to receive event queue structure.
- *
- * @return zero on success.
- */
-PJ_DECL(pj_status_t) pj_equeue_create( pj_pool_t *pool,
- const pj_equeue_options *options,
- pj_equeue_t **equeue);
-
-/**
- * Get the first instance of Event Queue, or NULL if no Event Queue
- * instance has been created in the application.
- *
- * @return The first instance of Event Queue created, or NULL.
- */
-PJ_DECL(pj_equeue_t*) pj_equeue_instance(void);
-
-/**
- * Destroy the Event Queue.
- *
- * @param equeue The Event Queue instance to be destroyed.
- */
-PJ_DECL(pj_status_t) pj_equeue_destroy( pj_equeue_t *equeue );
-
-/**
- * Customize the lock object that is used by the Event Queue.
- *
- * @param equeue The Event Queue instance.
- * @param lock The lock object.
- * @param auto_del If non-zero, the lock will be destroyed by
- * Event Queue.
- *
- * @return Zero on success.
- */
-PJ_DECL(pj_status_t) pj_equeue_set_lock( pj_equeue_t *equeue,
- pj_lock_t *lock,
- pj_bool_t auto_del);
-
-/**
- * Associate an Event Queue key to particular handle. The key is also
- * associated with the callback and user data, which will be used by
- * the Event Queue framework when signalling event back to application.
- *
- * @param pool To allocate the resource for the specified handle, which
- * must be valid until the handle/key is unregistered
- * from Event Queue.
- * @param equeue The Event Queue.
- * @param hnd The OS handle to be registered, which can be a socket
- * descriptor (pj_sock_t), file descriptor, etc.
- * @param cb Callback to be called when I/O operation completes.
- * @param user_data User data to be associated with the key.
- * @param key Pointer to receive the key.
- *
- * @return Zero on success.
- */
-PJ_DECL(pj_status_t) pj_equeue_register( pj_pool_t *pool,
- pj_equeue_t *equeue,
- pj_oshandle_t hnd,
- pj_io_callback *cb,
- void *user_data,
- pj_equeue_key_t **key);
-
-/**
- * Retrieve user data associated with a key.
- *
- * @param key The Event Queue key.
- *
- * @return User data associated with the key.
- */
-PJ_DECL(void*) pj_equeue_get_user_data( pj_equeue_key_t *key );
-
-
-/**
- * Unregister Event Queue key from the Event Queue.
- *
- * @param equeue The Event Queue.
- * @param key The key.
- *
- * @return Zero on success.
- */
-PJ_DECL(pj_status_t) pj_equeue_unregister( pj_equeue_t *equeue,
- pj_equeue_key_t *key);
-
-/**
- * Instruct the Event Queue to read from the specified handle. This function
- * returns immediately (i.e. non-blocking) regardless whether some data has
- * been transfered. If the operation can't complete immediately, caller will
- * be notified about the completion when it calls pj_equeue_poll().
- *
- * @param key The key that uniquely identifies the handle.
- * @param buffer The buffer to hold the read data. The caller MUST make sure
- * that this buffer remain valid until the framework completes
- * reading the handle.
- * @param size The maximum size to be read.
- *
- * @return
- * - zero or positive number to indicate the number of bytes has been
- * read, and in this case the operation was not queued.
- * - (-1) on error, which in this case operation was not queued.
- * - PJ_EQUEUE_PENDING if the operation has been queued.
- */
-PJ_DECL(pj_ssize_t) pj_equeue_read( pj_equeue_key_t *key,
- void *buffer,
- pj_size_t size);
-
-/**
- * Start recv() operation on the specified handle.
- *
- * @see ::pj_ioqueue_read
- */
-PJ_DECL(pj_ssize_t) pj_equeue_recv( pj_equeue_key_t *key,
- void *buf,
- pj_size_t size,
- unsigned flags);
-
-/**
- * Start recvfrom() operation on the specified handle.
- *
- * @see ::pj_equeue_read
- */
-PJ_DECL(pj_ssize_t) pj_equeue_recvfrom( pj_equeue_key_t *key,
- void *buf,
- pj_size_t size,
- unsigned flags,
- pj_sockaddr_t *addr,
- int *addrlen );
-
-/**
- * Write.
- */
-PJ_DECL(pj_ssize_t) pj_equeue_write( pj_equeue_key_t *key,
- const void *buf,
- pj_size_t size);
-
-/**
- * Send.
- */
-PJ_DECL(pj_ssize_t) pj_equeue_send( pj_equeue_key_t *key,
- const void *buf,
- pj_size_t size,
- unsigned flags);
-
-/**
- * Sendto.
- */
-PJ_DECL(pj_ssize_t) pj_equeue_sendto( pj_equeue_key_t *key,
- const void *buf,
- pj_size_t size,
- unsigned flags,
- const pj_sockaddr_t *addr,
- int addrlen);
-
-/**
- * Schedule timer.
- */
-PJ_DECL(pj_status_t) pj_equeue_schedule_timer( pj_equeue_t *equeue,
- const pj_time_val *timeout,
- pj_timer_entry *entry);
-
-/**
- * Cancel timer.
- */
-PJ_DECL(pj_status_t) pj_equeue_cancel_timer( pj_equeue_t *equeue,
- pj_timer_entry *entry);
-
-/**
- * Poll for events.
- */
-PJ_DECL(pj_status_t) pj_equeue_poll( pj_equeue_t *equeue,
- const pj_time_val *timeout );
-
-/**
- * Run.
- */
-PJ_DECL(pj_status_t) pj_equeue_run( pj_equeue_t *equeue );
-
-/**
- * Stop all running threads.
- */
-PJ_DECL(pj_status_t) pj_equeue_stop( pj_equeue_t *equeue );
-
-
-/** @} */
-
-PJ_END_DECL
-
-#endif /* __PJ_EQUEUE_H__ */
+/* $Id$
+ *
+ */
+/*
+ * PJLIB - PJ Foundation Library
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ * Benny Prijono <bennylp@bulukucing.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#ifndef __PJ_EQUEUE_H__
+#define __PJ_EQUEUE_H__
+
+/**
+ * @file equeue.h
+ * @brief Event Queue
+ */
+#include <pj/types.h>
+
+
+PJ_BEGIN_DECL
+
+/**
+ * @defgroup PJ_EQUEUE Event Queue
+ * @brief Event Queue
+ * @ingroup PJ_OS
+ * @{
+ */
+
+
+/**
+ * Opaque data type for Event Queue.
+ */
+typedef struct pj_equeue_t pj_equeue_t;
+
+/**
+ * Opaque data type for Event Queue key.
+ */
+typedef struct pj_equeue_key_t pj_equeue_key_t;
+
+
+/**
+ * This structure describes the callbacks to be called when I/O operation
+ * completes.
+ */
+typedef struct pj_io_callback
+{
+ /**
+ * This callback is called when #pj_equeue_read, #pj_equeue_recv or
+ * #pj_equeue_recvfrom completes.
+ *
+ * @param key The key.
+ * @param bytes_read The size of data that has just been read.
+ */
+ void (*on_read_complete)(pj_equeue_key_t *key, pj_ssize_t bytes_read);
+
+ /**
+ * This callback is called when #pj_equeue_write, #pj_equeue_send, or
+ * #pj_equeue_sendto completes.
+ *
+ * @param key The key.
+ * @param bytes_read The size of data that has just been written.
+ */
+ void (*on_write_complete)(pj_equeue_key_t *key, pj_ssize_t bytes_sent);
+
+ /**
+ * This callback is called when #pj_equeue_accept completes.
+ *
+ * @param key The key.
+ * @param status Zero if the operation completes successfully.
+ */
+ void (*on_accept_complete)(pj_equeue_key_t *key, int status);
+
+ /**
+ * This callback is called when #pj_equeue_connect completes.
+ *
+ * @param key The key.
+ * @param status Zero if the operation completes successfully.
+ */
+ void (*on_connect_complete)(pj_equeue_key_t *key, int status);
+
+} pj_io_callback;
+
+/**
+ * Event Queue options.
+ */
+typedef struct pj_equeue_options
+{
+ /** Maximum number of threads that are allowed to access Event Queue
+ * simulteneously.
+ */
+ unsigned nb_threads;
+
+ /** If non-zero, then no mutex protection will be used. */
+ pj_bool_t no_lock;
+
+ /** Interval of the busy loop inside the event queue.
+ * The time resolution here determines the accuracy of the
+ * timer in the Event Queue.
+ */
+ pj_time_val poll_interval;
+
+} pj_equeue_options;
+
+
+/**
+ * Error value returned by I/O operations to indicate that the operation
+ * can't complete immediately and will complete later.
+ */
+#define PJ_EQUEUE_PENDING (-2)
+
+/**
+ * Types of Event Queue operation.
+ */
+typedef enum pj_equeue_op
+{
+ PJ_EQUEUE_OP_NONE = 0, /**< No operation. */
+ PJ_EQUEUE_OP_READ = 1, /**< read() operation. */
+ PJ_EQUEUE_OP_RECV_FROM = 2, /**< recvfrom() operation. */
+ PJ_EQUEUE_OP_WRITE = 4, /**< write() operation. */
+ PJ_EQUEUE_OP_SEND_TO = 8, /**< sendto() operation. */
+#if defined(PJ_HAS_TCP) && PJ_HAS_TCP != 0
+ PJ_EQUEUE_OP_ACCEPT = 16, /**< accept() operation. */
+ PJ_EQUEUE_OP_CONNECT = 32, /**< connect() operation. */
+#endif /* PJ_HAS_TCP */
+} pj_equeue_op;
+
+
+
+/**
+ * Initialize Event Queue options with default values.
+ *
+ * @param options Event Queue options.
+ */
+PJ_DECL(void) pj_equeue_options_init(pj_equeue_options *options);
+
+/**
+ * Create a new Event Queue framework.
+ *
+ * @param pool The pool to allocate the event queue structure.
+ * @param options Event queue options, or if NULL is given, then
+ * default options will be used.
+ * @param equeue Pointer to receive event queue structure.
+ *
+ * @return zero on success.
+ */
+PJ_DECL(pj_status_t) pj_equeue_create( pj_pool_t *pool,
+ const pj_equeue_options *options,
+ pj_equeue_t **equeue);
+
+/**
+ * Get the first instance of Event Queue, or NULL if no Event Queue
+ * instance has been created in the application.
+ *
+ * @return The first instance of Event Queue created, or NULL.
+ */
+PJ_DECL(pj_equeue_t*) pj_equeue_instance(void);
+
+/**
+ * Destroy the Event Queue.
+ *
+ * @param equeue The Event Queue instance to be destroyed.
+ */
+PJ_DECL(pj_status_t) pj_equeue_destroy( pj_equeue_t *equeue );
+
+/**
+ * Customize the lock object that is used by the Event Queue.
+ *
+ * @param equeue The Event Queue instance.
+ * @param lock The lock object.
+ * @param auto_del If non-zero, the lock will be destroyed by
+ * Event Queue.
+ *
+ * @return Zero on success.
+ */
+PJ_DECL(pj_status_t) pj_equeue_set_lock( pj_equeue_t *equeue,
+ pj_lock_t *lock,
+ pj_bool_t auto_del);
+
+/**
+ * Associate an Event Queue key to particular handle. The key is also
+ * associated with the callback and user data, which will be used by
+ * the Event Queue framework when signalling event back to application.
+ *
+ * @param pool To allocate the resource for the specified handle, which
+ * must be valid until the handle/key is unregistered
+ * from Event Queue.
+ * @param equeue The Event Queue.
+ * @param hnd The OS handle to be registered, which can be a socket
+ * descriptor (pj_sock_t), file descriptor, etc.
+ * @param cb Callback to be called when I/O operation completes.
+ * @param user_data User data to be associated with the key.
+ * @param key Pointer to receive the key.
+ *
+ * @return Zero on success.
+ */
+PJ_DECL(pj_status_t) pj_equeue_register( pj_pool_t *pool,
+ pj_equeue_t *equeue,
+ pj_oshandle_t hnd,
+ pj_io_callback *cb,
+ void *user_data,
+ pj_equeue_key_t **key);
+
+/**
+ * Retrieve user data associated with a key.
+ *
+ * @param key The Event Queue key.
+ *
+ * @return User data associated with the key.
+ */
+PJ_DECL(void*) pj_equeue_get_user_data( pj_equeue_key_t *key );
+
+
+/**
+ * Unregister Event Queue key from the Event Queue.
+ *
+ * @param equeue The Event Queue.
+ * @param key The key.
+ *
+ * @return Zero on success.
+ */
+PJ_DECL(pj_status_t) pj_equeue_unregister( pj_equeue_t *equeue,
+ pj_equeue_key_t *key);
+
+/**
+ * Instruct the Event Queue to read from the specified handle. This function
+ * returns immediately (i.e. non-blocking) regardless whether some data has
+ * been transfered. If the operation can't complete immediately, caller will
+ * be notified about the completion when it calls pj_equeue_poll().
+ *
+ * @param key The key that uniquely identifies the handle.
+ * @param buffer The buffer to hold the read data. The caller MUST make sure
+ * that this buffer remain valid until the framework completes
+ * reading the handle.
+ * @param size The maximum size to be read.
+ *
+ * @return
+ * - zero or positive number to indicate the number of bytes has been
+ * read, and in this case the operation was not queued.
+ * - (-1) on error, which in this case operation was not queued.
+ * - PJ_EQUEUE_PENDING if the operation has been queued.
+ */
+PJ_DECL(pj_ssize_t) pj_equeue_read( pj_equeue_key_t *key,
+ void *buffer,
+ pj_size_t size);
+
+/**
+ * Start recv() operation on the specified handle.
+ *
+ * @see ::pj_ioqueue_read
+ */
+PJ_DECL(pj_ssize_t) pj_equeue_recv( pj_equeue_key_t *key,
+ void *buf,
+ pj_size_t size,
+ unsigned flags);
+
+/**
+ * Start recvfrom() operation on the specified handle.
+ *
+ * @see ::pj_equeue_read
+ */
+PJ_DECL(pj_ssize_t) pj_equeue_recvfrom( pj_equeue_key_t *key,
+ void *buf,
+ pj_size_t size,
+ unsigned flags,
+ pj_sockaddr_t *addr,
+ int *addrlen );
+
+/**
+ * Write.
+ */
+PJ_DECL(pj_ssize_t) pj_equeue_write( pj_equeue_key_t *key,
+ const void *buf,
+ pj_size_t size);
+
+/**
+ * Send.
+ */
+PJ_DECL(pj_ssize_t) pj_equeue_send( pj_equeue_key_t *key,
+ const void *buf,
+ pj_size_t size,
+ unsigned flags);
+
+/**
+ * Sendto.
+ */
+PJ_DECL(pj_ssize_t) pj_equeue_sendto( pj_equeue_key_t *key,
+ const void *buf,
+ pj_size_t size,
+ unsigned flags,
+ const pj_sockaddr_t *addr,
+ int addrlen);
+
+/**
+ * Schedule timer.
+ */
+PJ_DECL(pj_status_t) pj_equeue_schedule_timer( pj_equeue_t *equeue,
+ const pj_time_val *timeout,
+ pj_timer_entry *entry);
+
+/**
+ * Cancel timer.
+ */
+PJ_DECL(pj_status_t) pj_equeue_cancel_timer( pj_equeue_t *equeue,
+ pj_timer_entry *entry);
+
+/**
+ * Poll for events.
+ */
+PJ_DECL(pj_status_t) pj_equeue_poll( pj_equeue_t *equeue,
+ const pj_time_val *timeout );
+
+/**
+ * Run.
+ */
+PJ_DECL(pj_status_t) pj_equeue_run( pj_equeue_t *equeue );
+
+/**
+ * Stop all running threads.
+ */
+PJ_DECL(pj_status_t) pj_equeue_stop( pj_equeue_t *equeue );
+
+
+/** @} */
+
+PJ_END_DECL
+
+#endif /* __PJ_EQUEUE_H__ */
diff --git a/pjlib/include/pj/errno.h b/pjlib/include/pj/errno.h
index b7c6253..741475f 100644
--- a/pjlib/include/pj/errno.h
+++ b/pjlib/include/pj/errno.h
@@ -1,215 +1,236 @@
-/* $Id$
- *
- */
-#ifndef __PJ_ERRNO_H__
-#define __PJ_ERRNO_H__
-
-/**
- * @file errno.h
- * @brief PJLIB Error Codes
- */
-#include <pj/types.h>
-#include <pj/compat/errno.h>
-
-PJ_BEGIN_DECL
-
-/**
- * @defgroup pj_errno Error Codes
- * @ingroup PJ
- * @{
- *
- * In PJLIB, error/status codes from operating system are translated
- * into PJLIB error namespace, and stored in @a pj_status_t. All functions
- * that work with @a pj_status_t expect to get PJLIB error code instead
- * of native codes.
- *
- * @section pj_errno_retval Return Values
- *
- * All functions that returns @a pj_status_t returns @a PJ_SUCCESS if the
- * operation was completed successfully, or non-zero value to indicate
- * error. If the error came from operating system, then the native error
- * code is translated/folded into PJLIB's error namespace by using
- * #PJ_STATUS_FROM_OS() macro. The function will do this automatically
- * before returning the error to caller.
- *
- * @section pj_errno_errmsg Error Message
- *
- * To get the error message corresponding to a particular code, use function
- * #pj_strerror(). This function expects error code in PJLIB error namespace,
- * not the native error code. Application can pass the value from the
- * following sources to this function:
- * - #pj_get_os_error()
- * - #pj_get_netos_error()
- * - any return value from function returning @a pj_status_t.
- *
- * Application MUST NOT pass native error code (such as error code from
- * functions like GetLastError() or errno) to PJLIB functions expecting
- * @a pj_status_t.
- *
- */
-
-/**
- * Get the last platform error/status, folded into pj_status_t.
- * @return OS dependent error code, folded into pj_status_t.
- * @remark This function gets errno, or calls GetLastError() function and
- * convert the code into pj_status_t with PJ_STATUS_FROM_OS. Do
- * not call this for socket functions!
- * @see pj_get_netos_error()
- */
-PJ_DECL(pj_status_t) pj_get_os_error(void);
-
-/**
- * Set last error.
- * @param code pj_status_t
- */
-PJ_DECL(void) pj_set_os_error(pj_status_t code);
-
-/**
- * Get the last error from socket operations.
- * @return Last socket error, folded into pj_status_t.
- */
-PJ_DECL(pj_status_t) pj_get_netos_error(void);
-
-/**
- * Set error code.
- * @param code pj_status_t.
- */
-PJ_DECL(void) pj_set_netos_error(pj_status_t code);
-
-
-/**
- * Get the error message for the specified error code. The message
- * string will be NULL terminated.
- *
- * @param statcode The error code.
- * @param buf Buffer to hold the error message string.
- * @param bufsize Size of the buffer.
- *
- * @return The error message as NULL terminated string,
- * wrapped with pj_str_t.
- */
-PJ_DECL(pj_str_t) pj_strerror( pj_status_t statcode,
- char *buf, pj_size_t bufsize);
-
-
-/**
- * @hideinitializer
- * Return platform os error code folded into pj_status_t code. This is
- * the macro that is used throughout the library for all PJLIB's functions
- * that returns error from operating system. Application may override
- * this macro to reduce size (e.g. by defining it to always return
- * #PJ_EUNKNOWN).
- *
- * Note:
- * This macro MUST return non-zero value regardless whether zero is
- * passed as the argument. The reason is to protect logic error when
- * the operating system doesn't report error codes properly.
- *
- * @param os_code Platform OS error code. This value may be evaluated
- * more than once.
- * @return The platform os error code folded into pj_status_t.
- */
-#ifndef PJ_RETURN_OS_ERROR
-# define PJ_RETURN_OS_ERROR(os_code) (os_code ? \
- PJ_STATUS_FROM_OS(os_code) : -1)
-#endif
-
-
-/**
- * @hideinitializer
- * Fold a platform specific error into an pj_status_t code.
- *
- * @param e The platform os error code.
- * @return pj_status_t
- * @warning Macro implementation; the syserr argument may be evaluated
- * multiple times.
- */
-#define PJ_STATUS_FROM_OS(e) (e == 0 ? PJ_SUCCESS : e + PJ_ERRNO_START_SYS)
-
-/**
- * @hideinitializer
- * Fold an pj_status_t code back to the native platform defined error.
- *
- * @param e The pj_status_t folded platform os error code.
- * @return pj_os_err_type
- * @warning macro implementation; the statcode argument may be evaluated
- * multiple times. If the statcode was not created by
- * pj_get_os_error or PJ_STATUS_FROM_OS, the results are undefined.
- */
-#define PJ_STATUS_TO_OS(e) (e == 0 ? PJ_SUCCESS : e - PJ_ERRNO_START_SYS)
-
-
-/**
- * @defgroup pj_errnum PJLIB's Own Error Codes
- * @ingroup pj_errno
- * @{
- */
-
-/**
- * @hideinitializer
- * Unknown error has been reported.
- */
-#define PJ_EUNKNOWN (PJ_ERRNO_START_STATUS + 1)
-/**
- * @hideinitializer
- * The operation is pending and will be completed later.
- */
-#define PJ_EPENDING (PJ_ERRNO_START_STATUS + 2)
-/**
- * @hideinitializer
- * Too many connecting sockets.
- */
-#define PJ_ETOOMANYCONN (PJ_ERRNO_START_STATUS + 3)
-/**
- * @hideinitializer
- * Invalid argument.
- */
-#define PJ_EINVAL (PJ_ERRNO_START_STATUS + 4)
-/**
- * @hideinitializer
- * Name too long (eg. hostname too long).
- */
-#define PJ_ENAMETOOLONG (PJ_ERRNO_START_STATUS + 5)
-/**
- * @hideinitializer
- * Not found.
- */
-#define PJ_ENOTFOUND (PJ_ERRNO_START_STATUS + 6)
-/**
- * @hideinitializer
- * Not enough memory.
- */
-#define PJ_ENOMEM (PJ_ERRNO_START_STATUS + 7)
-/**
- * @hideinitializer
- * Bug detected!
- */
-#define PJ_EBUG (PJ_ERRNO_START_STATUS + 8)
-/**
- * @hideinitializer
- * Operation timed out.
- */
-#define PJ_ETIMEDOUT (PJ_ERRNO_START_STATUS + 9)
-/**
- * @hideinitializer
- * Too many objects.
- */
-#define PJ_ETOOMANY (PJ_ERRNO_START_STATUS + 10)
-/**
- * @hideinitializer
- * Object is busy.
- */
-#define PJ_EBUSY (PJ_ERRNO_START_STATUS + 11)
-/**
- * @hideinitializer
- * The specified option is not supported.
- */
-#define PJ_ENOTSUP (PJ_ERRNO_START_STATUS + 12)
-/**
- * @hideinitializer
- * Invalid operation.
- */
-#define PJ_EINVALIDOP (PJ_ERRNO_START_STATUS + 13)
+/* $Id$
+ *
+ */
+/*
+ * PJLIB - PJ Foundation Library
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ * Benny Prijono <bennylp@bulukucing.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#ifndef __PJ_ERRNO_H__
+#define __PJ_ERRNO_H__
+
+/**
+ * @file errno.h
+ * @brief PJLIB Error Codes
+ */
+#include <pj/types.h>
+#include <pj/compat/errno.h>
+
+PJ_BEGIN_DECL
+
+/**
+ * @defgroup pj_errno Error Codes
+ * @ingroup PJ
+ * @{
+ *
+ * In PJLIB, error/status codes from operating system are translated
+ * into PJLIB error namespace, and stored in @a pj_status_t. All functions
+ * that work with @a pj_status_t expect to get PJLIB error code instead
+ * of native codes.
+ *
+ * @section pj_errno_retval Return Values
+ *
+ * All functions that returns @a pj_status_t returns @a PJ_SUCCESS if the
+ * operation was completed successfully, or non-zero value to indicate
+ * error. If the error came from operating system, then the native error
+ * code is translated/folded into PJLIB's error namespace by using
+ * #PJ_STATUS_FROM_OS() macro. The function will do this automatically
+ * before returning the error to caller.
+ *
+ * @section pj_errno_errmsg Error Message
+ *
+ * To get the error message corresponding to a particular code, use function
+ * #pj_strerror(). This function expects error code in PJLIB error namespace,
+ * not the native error code. Application can pass the value from the
+ * following sources to this function:
+ * - #pj_get_os_error()
+ * - #pj_get_netos_error()
+ * - any return value from function returning @a pj_status_t.
+ *
+ * Application MUST NOT pass native error code (such as error code from
+ * functions like GetLastError() or errno) to PJLIB functions expecting
+ * @a pj_status_t.
+ *
+ */
+
+/**
+ * Get the last platform error/status, folded into pj_status_t.
+ * @return OS dependent error code, folded into pj_status_t.
+ * @remark This function gets errno, or calls GetLastError() function and
+ * convert the code into pj_status_t with PJ_STATUS_FROM_OS. Do
+ * not call this for socket functions!
+ * @see pj_get_netos_error()
+ */
+PJ_DECL(pj_status_t) pj_get_os_error(void);
+
+/**
+ * Set last error.
+ * @param code pj_status_t
+ */
+PJ_DECL(void) pj_set_os_error(pj_status_t code);
+
+/**
+ * Get the last error from socket operations.
+ * @return Last socket error, folded into pj_status_t.
+ */
+PJ_DECL(pj_status_t) pj_get_netos_error(void);
+
+/**
+ * Set error code.
+ * @param code pj_status_t.
+ */
+PJ_DECL(void) pj_set_netos_error(pj_status_t code);
+
+
+/**
+ * Get the error message for the specified error code. The message
+ * string will be NULL terminated.
+ *
+ * @param statcode The error code.
+ * @param buf Buffer to hold the error message string.
+ * @param bufsize Size of the buffer.
+ *
+ * @return The error message as NULL terminated string,
+ * wrapped with pj_str_t.
+ */
+PJ_DECL(pj_str_t) pj_strerror( pj_status_t statcode,
+ char *buf, pj_size_t bufsize);
+
+
+/**
+ * @hideinitializer
+ * Return platform os error code folded into pj_status_t code. This is
+ * the macro that is used throughout the library for all PJLIB's functions
+ * that returns error from operating system. Application may override
+ * this macro to reduce size (e.g. by defining it to always return
+ * #PJ_EUNKNOWN).
+ *
+ * Note:
+ * This macro MUST return non-zero value regardless whether zero is
+ * passed as the argument. The reason is to protect logic error when
+ * the operating system doesn't report error codes properly.
+ *
+ * @param os_code Platform OS error code. This value may be evaluated
+ * more than once.
+ * @return The platform os error code folded into pj_status_t.
+ */
+#ifndef PJ_RETURN_OS_ERROR
+# define PJ_RETURN_OS_ERROR(os_code) (os_code ? \
+ PJ_STATUS_FROM_OS(os_code) : -1)
+#endif
+
+
+/**
+ * @hideinitializer
+ * Fold a platform specific error into an pj_status_t code.
+ *
+ * @param e The platform os error code.
+ * @return pj_status_t
+ * @warning Macro implementation; the syserr argument may be evaluated
+ * multiple times.
+ */
+#define PJ_STATUS_FROM_OS(e) (e == 0 ? PJ_SUCCESS : e + PJ_ERRNO_START_SYS)
+
+/**
+ * @hideinitializer
+ * Fold an pj_status_t code back to the native platform defined error.
+ *
+ * @param e The pj_status_t folded platform os error code.
+ * @return pj_os_err_type
+ * @warning macro implementation; the statcode argument may be evaluated
+ * multiple times. If the statcode was not created by
+ * pj_get_os_error or PJ_STATUS_FROM_OS, the results are undefined.
+ */
+#define PJ_STATUS_TO_OS(e) (e == 0 ? PJ_SUCCESS : e - PJ_ERRNO_START_SYS)
+
+
+/**
+ * @defgroup pj_errnum PJLIB's Own Error Codes
+ * @ingroup pj_errno
+ * @{
+ */
+
+/**
+ * @hideinitializer
+ * Unknown error has been reported.
+ */
+#define PJ_EUNKNOWN (PJ_ERRNO_START_STATUS + 1)
+/**
+ * @hideinitializer
+ * The operation is pending and will be completed later.
+ */
+#define PJ_EPENDING (PJ_ERRNO_START_STATUS + 2)
+/**
+ * @hideinitializer
+ * Too many connecting sockets.
+ */
+#define PJ_ETOOMANYCONN (PJ_ERRNO_START_STATUS + 3)
+/**
+ * @hideinitializer
+ * Invalid argument.
+ */
+#define PJ_EINVAL (PJ_ERRNO_START_STATUS + 4)
+/**
+ * @hideinitializer
+ * Name too long (eg. hostname too long).
+ */
+#define PJ_ENAMETOOLONG (PJ_ERRNO_START_STATUS + 5)
+/**
+ * @hideinitializer
+ * Not found.
+ */
+#define PJ_ENOTFOUND (PJ_ERRNO_START_STATUS + 6)
+/**
+ * @hideinitializer
+ * Not enough memory.
+ */
+#define PJ_ENOMEM (PJ_ERRNO_START_STATUS + 7)
+/**
+ * @hideinitializer
+ * Bug detected!
+ */
+#define PJ_EBUG (PJ_ERRNO_START_STATUS + 8)
+/**
+ * @hideinitializer
+ * Operation timed out.
+ */
+#define PJ_ETIMEDOUT (PJ_ERRNO_START_STATUS + 9)
+/**
+ * @hideinitializer
+ * Too many objects.
+ */
+#define PJ_ETOOMANY (PJ_ERRNO_START_STATUS + 10)
+/**
+ * @hideinitializer
+ * Object is busy.
+ */
+#define PJ_EBUSY (PJ_ERRNO_START_STATUS + 11)
+/**
+ * @hideinitializer
+ * The specified option is not supported.
+ */
+#define PJ_ENOTSUP (PJ_ERRNO_START_STATUS + 12)
+/**
+ * @hideinitializer
+ * Invalid operation.
+ */
+#define PJ_EINVALIDOP (PJ_ERRNO_START_STATUS + 13)
/**
* @hideinitializer
* Operation is cancelled.
@@ -220,42 +241,42 @@
* Object already exists.
*/
#define PJ_EEXISTS (PJ_ERRNO_START_STATUS + 14)
-
-/** @} */ /* pj_errnum */
-
-/** @} */ /* pj_errno */
-
-
-/**
- * PJ_ERRNO_START is where PJLIB specific error values start.
- */
-#define PJ_ERRNO_START 20000
-
-/**
- * PJ_ERRNO_SPACE_SIZE is the maximum number of errors in one of
- * the error/status range below.
- */
-#define PJ_ERRNO_SPACE_SIZE 50000
-
-/**
- * PJ_ERRNO_START_STATUS is where PJLIB specific status codes start.
- */
-#define PJ_ERRNO_START_STATUS (PJ_ERRNO_START + PJ_ERRNO_SPACE_SIZE)
-
-/**
- * PJ_ERRNO_START_SYS converts platform specific error codes into
- * pj_status_t values.
- */
-#define PJ_ERRNO_START_SYS (PJ_ERRNO_START_STATUS + PJ_ERRNO_SPACE_SIZE)
-
-/**
- * PJ_ERRNO_START_USER are reserved for applications that use error
- * codes along with PJLIB codes.
- */
-#define PJ_ERRNO_START_USER (PJ_ERRNO_START_SYS + PJ_ERRNO_SPACE_SIZE)
-
-
-PJ_END_DECL
-
-#endif /* __PJ_ERRNO_H__ */
-
+
+/** @} */ /* pj_errnum */
+
+/** @} */ /* pj_errno */
+
+
+/**
+ * PJ_ERRNO_START is where PJLIB specific error values start.
+ */
+#define PJ_ERRNO_START 20000
+
+/**
+ * PJ_ERRNO_SPACE_SIZE is the maximum number of errors in one of
+ * the error/status range below.
+ */
+#define PJ_ERRNO_SPACE_SIZE 50000
+
+/**
+ * PJ_ERRNO_START_STATUS is where PJLIB specific status codes start.
+ */
+#define PJ_ERRNO_START_STATUS (PJ_ERRNO_START + PJ_ERRNO_SPACE_SIZE)
+
+/**
+ * PJ_ERRNO_START_SYS converts platform specific error codes into
+ * pj_status_t values.
+ */
+#define PJ_ERRNO_START_SYS (PJ_ERRNO_START_STATUS + PJ_ERRNO_SPACE_SIZE)
+
+/**
+ * PJ_ERRNO_START_USER are reserved for applications that use error
+ * codes along with PJLIB codes.
+ */
+#define PJ_ERRNO_START_USER (PJ_ERRNO_START_SYS + PJ_ERRNO_SPACE_SIZE)
+
+
+PJ_END_DECL
+
+#endif /* __PJ_ERRNO_H__ */
+
diff --git a/pjlib/include/pj/except.h b/pjlib/include/pj/except.h
index 8bdda48..0405c69 100644
--- a/pjlib/include/pj/except.h
+++ b/pjlib/include/pj/except.h
@@ -1,270 +1,291 @@
-/* $Id$
- *
- */
-
-#ifndef __PJ_EXCEPTION_H__
-#define __PJ_EXCEPTION_H__
-
-/**
- * @file except.h
- * @brief Exception Handling in C.
- */
-
-#include <pj/types.h>
-#include <pj/compat/setjmp.h>
-
-
-PJ_BEGIN_DECL
-
-
-/**
- * @defgroup PJ_EXCEPT Exception Handling
- * @ingroup PJ_MISC
- * @{
- *
- * \section pj_except_sample_sec Quick Example
- *
- * For the impatient, take a look at some examples:
- * - @ref page_pjlib_samples_except_c
- * - @ref page_pjlib_exception_test
- *
- * \section pj_except_except Exception Handling
- *
- * This module provides exception handling syntactically similar to C++ in
- * C language. The underlying mechanism use setjmp() and longjmp(), and since
- * these constructs are ANSI standard, the mechanism here should be available
- * on most platforms/compilers which are ANSI compliant.
- *
- * If ANSI libc is not available, then setjmp()/longjmp() implementation will
- * be provided. See <pj/compat/setjmp.h> for compatibility.
- *
- * The exception handling mechanism is completely thread safe, so the exception
- * thrown by one thread will not interfere with other thread.
- *
- * CAVEATS:
- * - unlike C++ exception, the scheme here won't call destructors of local
- * objects if exception is thrown. Care must be taken when a function
- * hold some resorce such as pool or mutex etc.
- * - You CAN NOT make nested exception in one single function without using
- * a nested PJ_USE_EXCEPTION.
- * - Exceptions will always be caught by the first handle (unlike C++ where
- * exception is only caught if the type matches.
- *
- * The exception handling constructs are similar to C++. The blocks will be
- * constructed similar to the following sample:
- *
- * \verbatim
- #define NO_MEMORY 1
- #define SYNTAX_ERROR 2
-
- int main()
- {
- PJ_USE_EXCEPTION; // declare local exception stack.
-
- PJ_TRY {
- ...// do something..
- }
- PJ_CATCH(NO_MEMORY) {
- ... // handle exception 1
- }
- PJ_CATCH(SYNTAX_ERROR) {
- ... // handle exception 2
- }
- PJ_DEFAULT {
- ... // handle other exceptions.
- }
- PJ_END;
- }
- \endverbatim
- *
- * The above sample uses hard coded exception ID. It is @b strongly
- * recommended that applications request a unique exception ID instead
- * of hard coded value like above.
- *
- * \section pj_except_reg Exception ID Allocation
- *
- * To ensure that exception ID (number) are used consistently and to
- * prevent ID collisions in an application, it is strongly suggested that
- * applications allocate an exception ID for each possible exception
- * type. As a bonus of this process, the application can identify
- * the name of the exception when the particular exception is thrown.
- *
- * Exception ID management are performed with the following APIs:
- * - #pj_exception_id_alloc().
- * - #pj_exception_id_free().
- * - #pj_exception_id_name().
- *
- *
- * PJLIB itself automatically allocates one exception id, i.e.
- * #PJ_NO_MEMORY_EXCEPTION which is declared in <pj/pool.h>. This exception
- * ID is raised by default pool policy when it fails to allocate memory.
- *
- * \section PJ_EX_KEYWORDS Keywords
- *
- * \subsection PJ_THROW PJ_THROW(expression)
- * Throw an exception. The expression thrown is an integer as the result of
- * the \a expression. This keyword can be specified anywhere within the
- * program.
- *
- * \subsection PJ_USE_EXCEPTION PJ_USE_EXCEPTION
- * Specify this in the variable definition section of the function block
- * (or any blocks) to specify that the block has \a PJ_TRY/PJ_CATCH exception
- * block.
- * Actually, this is just a macro to declare local variable which is used to
- * push the exception state to the exception stack.
- *
- * \subsection PJ_TRY PJ_TRY
- * The \a PJ_TRY keyword is typically followed by a block. If an exception is
- * thrown in this block, then the execution will resume to the \a PJ_CATCH
- * handler.
- *
- * \subsection PJ_CATCH PJ_CATCH(expression)
- * The \a PJ_CATCH is normally followed by a block. This block will be executed
- * if the exception being thrown is equal to the expression specified in the
- * \a PJ_CATCH.
- *
- * \subsection PJ_DEFAULT PJ_DEFAULT
- * The \a PJ_DEFAULT keyword is normally followed by a block. This block will
- * be executed if the exception being thrown doesn't match any of the \a
- * PJ_CATCH specification. The \a PJ_DEFAULT block \b MUST be placed as the
- * last block of the handlers.
- *
- * \subsection PJ_END PJ_END
- * Specify this keyword to mark the end of \a PJ_TRY / \a PJ_CATCH blocks.
- *
- * \subsection PJ_GET_EXCEPTION PJ_GET_EXCEPTION(void)
- * Get the last exception thrown. This macro is normally called inside the
- * \a PJ_CATCH or \a PJ_DEFAULT block, altough it can be used anywhere where
- * the \a PJ_USE_EXCEPTION definition is in scope.
- *
- *
- * \section pj_except_examples_sec Examples
- *
- * For some examples on how to use the exception construct, please see:
- * - @ref page_pjlib_samples_except_c
- * - @ref page_pjlib_exception_test
- */
-
-/**
- * Allocate a unique exception id.
- * Applications don't have to allocate a unique exception ID before using
- * the exception construct. However, by doing so it ensures that there is
- * no collisions of exception ID.
- *
- * As a bonus, when exception number is acquired through this function,
- * the library can assign name to the exception (only if
- * PJ_HAS_EXCEPTION_NAMES is enabled (default is yes)) and find out the
- * exception name when it catches an exception.
- *
- * @param name Name to be associated with the exception ID.
- * @param id Pointer to receive the ID.
- *
- * @return PJ_SUCCESS on success or PJ_ETOOMANY if the library
- * is running out out ids.
- */
-PJ_DECL(pj_status_t) pj_exception_id_alloc(const char *name,
- pj_exception_id_t *id);
-
-/**
- * Free an exception id.
- *
- * @param id The exception ID.
- *
- * @return PJ_SUCCESS or the appropriate error code.
- */
-PJ_DECL(pj_status_t) pj_exception_id_free(pj_exception_id_t id);
-
-/**
- * Retrieve name associated with the exception id.
- *
- * @param id The exception ID.
- *
- * @return The name associated with the specified ID.
- */
-PJ_DECL(const char*) pj_exception_id_name(pj_exception_id_t id);
+/* $Id$
+ *
+ */
+/*
+ * PJLIB - PJ Foundation Library
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ * Benny Prijono <bennylp@bulukucing.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
-
-/** @} */
-
-/**
- * This structure (which should be invisible to user) manages the TRY handler
- * stack.
- */
-struct pj_exception_state_t
-{
- struct pj_exception_state_t *prev; /**< Previous state in the list. */
- pj_jmp_buf state; /**< jmp_buf. */
-};
-
-/**
- * Throw exception.
- * @param id Exception Id.
- */
-PJ_DECL_NO_RETURN(void)
-pj_throw_exception_(pj_exception_id_t id) PJ_ATTR_NORETURN;
-
-/**
- * Push exception handler.
- */
-PJ_DECL(void) pj_push_exception_handler_(struct pj_exception_state_t *rec);
-
-/**
- * Pop exception handler.
- */
-PJ_DECL(void) pj_pop_exception_handler_(void);
-
-/**
- * Declare that the function will use exception.
- * @hideinitializer
- */
-#define PJ_USE_EXCEPTION struct pj_exception_state_t pj_x_except__; int pj_x_code__
-
-/**
- * Start exception specification block.
- * @hideinitializer
- */
-#define PJ_TRY if (1) { \
- pj_push_exception_handler_(&pj_x_except__); \
- pj_x_code__ = pj_setjmp(pj_x_except__.state); \
- if (pj_x_code__ == 0)
-/**
- * Catch the specified exception Id.
- * @param id The exception number to catch.
- * @hideinitializer
- */
-#define PJ_CATCH(id) else if (pj_x_code__ == (id))
-
-/**
- * Catch any exception number.
- * @hideinitializer
- */
-#define PJ_DEFAULT else
-
-/**
- * End of exception specification block.
- * @hideinitializer
- */
-#define PJ_END pj_pop_exception_handler_(); \
- } else {}
-
-/**
- * Throw exception.
- * @param exception_id The exception number.
- * @hideinitializer
- */
-#define PJ_THROW(exception_id) pj_throw_exception_(exception_id)
-
-/**
- * Get current exception.
- * @return Current exception code.
- * @hideinitializer
- */
-#define PJ_GET_EXCEPTION() (pj_x_code__)
-
-PJ_END_DECL
-
-
-
-#endif /* __PJ_EXCEPTION_H__ */
-
-
+#ifndef __PJ_EXCEPTION_H__
+#define __PJ_EXCEPTION_H__
+
+/**
+ * @file except.h
+ * @brief Exception Handling in C.
+ */
+
+#include <pj/types.h>
+#include <pj/compat/setjmp.h>
+
+
+PJ_BEGIN_DECL
+
+
+/**
+ * @defgroup PJ_EXCEPT Exception Handling
+ * @ingroup PJ_MISC
+ * @{
+ *
+ * \section pj_except_sample_sec Quick Example
+ *
+ * For the impatient, take a look at some examples:
+ * - @ref page_pjlib_samples_except_c
+ * - @ref page_pjlib_exception_test
+ *
+ * \section pj_except_except Exception Handling
+ *
+ * This module provides exception handling syntactically similar to C++ in
+ * C language. The underlying mechanism use setjmp() and longjmp(), and since
+ * these constructs are ANSI standard, the mechanism here should be available
+ * on most platforms/compilers which are ANSI compliant.
+ *
+ * If ANSI libc is not available, then setjmp()/longjmp() implementation will
+ * be provided. See <pj/compat/setjmp.h> for compatibility.
+ *
+ * The exception handling mechanism is completely thread safe, so the exception
+ * thrown by one thread will not interfere with other thread.
+ *
+ * CAVEATS:
+ * - unlike C++ exception, the scheme here won't call destructors of local
+ * objects if exception is thrown. Care must be taken when a function
+ * hold some resorce such as pool or mutex etc.
+ * - You CAN NOT make nested exception in one single function without using
+ * a nested PJ_USE_EXCEPTION.
+ * - Exceptions will always be caught by the first handle (unlike C++ where
+ * exception is only caught if the type matches.
+ *
+ * The exception handling constructs are similar to C++. The blocks will be
+ * constructed similar to the following sample:
+ *
+ * \verbatim
+ #define NO_MEMORY 1
+ #define SYNTAX_ERROR 2
+
+ int main()
+ {
+ PJ_USE_EXCEPTION; // declare local exception stack.
+
+ PJ_TRY {
+ ...// do something..
+ }
+ PJ_CATCH(NO_MEMORY) {
+ ... // handle exception 1
+ }
+ PJ_CATCH(SYNTAX_ERROR) {
+ ... // handle exception 2
+ }
+ PJ_DEFAULT {
+ ... // handle other exceptions.
+ }
+ PJ_END;
+ }
+ \endverbatim
+ *
+ * The above sample uses hard coded exception ID. It is @b strongly
+ * recommended that applications request a unique exception ID instead
+ * of hard coded value like above.
+ *
+ * \section pj_except_reg Exception ID Allocation
+ *
+ * To ensure that exception ID (number) are used consistently and to
+ * prevent ID collisions in an application, it is strongly suggested that
+ * applications allocate an exception ID for each possible exception
+ * type. As a bonus of this process, the application can identify
+ * the name of the exception when the particular exception is thrown.
+ *
+ * Exception ID management are performed with the following APIs:
+ * - #pj_exception_id_alloc().
+ * - #pj_exception_id_free().
+ * - #pj_exception_id_name().
+ *
+ *
+ * PJLIB itself automatically allocates one exception id, i.e.
+ * #PJ_NO_MEMORY_EXCEPTION which is declared in <pj/pool.h>. This exception
+ * ID is raised by default pool policy when it fails to allocate memory.
+ *
+ * \section PJ_EX_KEYWORDS Keywords
+ *
+ * \subsection PJ_THROW PJ_THROW(expression)
+ * Throw an exception. The expression thrown is an integer as the result of
+ * the \a expression. This keyword can be specified anywhere within the
+ * program.
+ *
+ * \subsection PJ_USE_EXCEPTION PJ_USE_EXCEPTION
+ * Specify this in the variable definition section of the function block
+ * (or any blocks) to specify that the block has \a PJ_TRY/PJ_CATCH exception
+ * block.
+ * Actually, this is just a macro to declare local variable which is used to
+ * push the exception state to the exception stack.
+ *
+ * \subsection PJ_TRY PJ_TRY
+ * The \a PJ_TRY keyword is typically followed by a block. If an exception is
+ * thrown in this block, then the execution will resume to the \a PJ_CATCH
+ * handler.
+ *
+ * \subsection PJ_CATCH PJ_CATCH(expression)
+ * The \a PJ_CATCH is normally followed by a block. This block will be executed
+ * if the exception being thrown is equal to the expression specified in the
+ * \a PJ_CATCH.
+ *
+ * \subsection PJ_DEFAULT PJ_DEFAULT
+ * The \a PJ_DEFAULT keyword is normally followed by a block. This block will
+ * be executed if the exception being thrown doesn't match any of the \a
+ * PJ_CATCH specification. The \a PJ_DEFAULT block \b MUST be placed as the
+ * last block of the handlers.
+ *
+ * \subsection PJ_END PJ_END
+ * Specify this keyword to mark the end of \a PJ_TRY / \a PJ_CATCH blocks.
+ *
+ * \subsection PJ_GET_EXCEPTION PJ_GET_EXCEPTION(void)
+ * Get the last exception thrown. This macro is normally called inside the
+ * \a PJ_CATCH or \a PJ_DEFAULT block, altough it can be used anywhere where
+ * the \a PJ_USE_EXCEPTION definition is in scope.
+ *
+ *
+ * \section pj_except_examples_sec Examples
+ *
+ * For some examples on how to use the exception construct, please see:
+ * - @ref page_pjlib_samples_except_c
+ * - @ref page_pjlib_exception_test
+ */
+
+/**
+ * Allocate a unique exception id.
+ * Applications don't have to allocate a unique exception ID before using
+ * the exception construct. However, by doing so it ensures that there is
+ * no collisions of exception ID.
+ *
+ * As a bonus, when exception number is acquired through this function,
+ * the library can assign name to the exception (only if
+ * PJ_HAS_EXCEPTION_NAMES is enabled (default is yes)) and find out the
+ * exception name when it catches an exception.
+ *
+ * @param name Name to be associated with the exception ID.
+ * @param id Pointer to receive the ID.
+ *
+ * @return PJ_SUCCESS on success or PJ_ETOOMANY if the library
+ * is running out out ids.
+ */
+PJ_DECL(pj_status_t) pj_exception_id_alloc(const char *name,
+ pj_exception_id_t *id);
+
+/**
+ * Free an exception id.
+ *
+ * @param id The exception ID.
+ *
+ * @return PJ_SUCCESS or the appropriate error code.
+ */
+PJ_DECL(pj_status_t) pj_exception_id_free(pj_exception_id_t id);
+
+/**
+ * Retrieve name associated with the exception id.
+ *
+ * @param id The exception ID.
+ *
+ * @return The name associated with the specified ID.
+ */
+PJ_DECL(const char*) pj_exception_id_name(pj_exception_id_t id);
+
+
+/** @} */
+
+/**
+ * This structure (which should be invisible to user) manages the TRY handler
+ * stack.
+ */
+struct pj_exception_state_t
+{
+ struct pj_exception_state_t *prev; /**< Previous state in the list. */
+ pj_jmp_buf state; /**< jmp_buf. */
+};
+
+/**
+ * Throw exception.
+ * @param id Exception Id.
+ */
+PJ_DECL_NO_RETURN(void)
+pj_throw_exception_(pj_exception_id_t id) PJ_ATTR_NORETURN;
+
+/**
+ * Push exception handler.
+ */
+PJ_DECL(void) pj_push_exception_handler_(struct pj_exception_state_t *rec);
+
+/**
+ * Pop exception handler.
+ */
+PJ_DECL(void) pj_pop_exception_handler_(void);
+
+/**
+ * Declare that the function will use exception.
+ * @hideinitializer
+ */
+#define PJ_USE_EXCEPTION struct pj_exception_state_t pj_x_except__; int pj_x_code__
+
+/**
+ * Start exception specification block.
+ * @hideinitializer
+ */
+#define PJ_TRY if (1) { \
+ pj_push_exception_handler_(&pj_x_except__); \
+ pj_x_code__ = pj_setjmp(pj_x_except__.state); \
+ if (pj_x_code__ == 0)
+/**
+ * Catch the specified exception Id.
+ * @param id The exception number to catch.
+ * @hideinitializer
+ */
+#define PJ_CATCH(id) else if (pj_x_code__ == (id))
+
+/**
+ * Catch any exception number.
+ * @hideinitializer
+ */
+#define PJ_DEFAULT else
+
+/**
+ * End of exception specification block.
+ * @hideinitializer
+ */
+#define PJ_END pj_pop_exception_handler_(); \
+ } else {}
+
+/**
+ * Throw exception.
+ * @param exception_id The exception number.
+ * @hideinitializer
+ */
+#define PJ_THROW(exception_id) pj_throw_exception_(exception_id)
+
+/**
+ * Get current exception.
+ * @return Current exception code.
+ * @hideinitializer
+ */
+#define PJ_GET_EXCEPTION() (pj_x_code__)
+
+PJ_END_DECL
+
+
+
+#endif /* __PJ_EXCEPTION_H__ */
+
+
diff --git a/pjlib/include/pj/fifobuf.h b/pjlib/include/pj/fifobuf.h
index 7f0b33d..8c770e0 100644
--- a/pjlib/include/pj/fifobuf.h
+++ b/pjlib/include/pj/fifobuf.h
@@ -1,29 +1,50 @@
-/* $Id$
- *
- */
-
-#ifndef __PJ_FIFOBUF_H__
-#define __PJ_FIFOBUF_H__
-
-#include <pj/types.h>
-
-PJ_BEGIN_DECL
-
-typedef struct pj_fifobuf_t pj_fifobuf_t;
-struct pj_fifobuf_t
-{
- char *first, *last;
- char *ubegin, *uend;
- int full;
-};
-
-PJ_DECL(void) pj_fifobuf_init (pj_fifobuf_t *fb, void *buffer, unsigned size);
-PJ_DECL(unsigned) pj_fifobuf_max_size (pj_fifobuf_t *fb);
-PJ_DECL(void*) pj_fifobuf_alloc (pj_fifobuf_t *fb, unsigned size);
-PJ_DECL(pj_status_t) pj_fifobuf_unalloc (pj_fifobuf_t *fb, void *buf);
-PJ_DECL(pj_status_t) pj_fifobuf_free (pj_fifobuf_t *fb, void *buf);
-
-PJ_END_DECL
-
-#endif /* __PJ_FIFOBUF_H__ */
-
+/* $Id$
+ *
+ */
+/*
+ * PJLIB - PJ Foundation Library
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ * Benny Prijono <bennylp@bulukucing.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef __PJ_FIFOBUF_H__
+#define __PJ_FIFOBUF_H__
+
+#include <pj/types.h>
+
+PJ_BEGIN_DECL
+
+typedef struct pj_fifobuf_t pj_fifobuf_t;
+struct pj_fifobuf_t
+{
+ char *first, *last;
+ char *ubegin, *uend;
+ int full;
+};
+
+PJ_DECL(void) pj_fifobuf_init (pj_fifobuf_t *fb, void *buffer, unsigned size);
+PJ_DECL(unsigned) pj_fifobuf_max_size (pj_fifobuf_t *fb);
+PJ_DECL(void*) pj_fifobuf_alloc (pj_fifobuf_t *fb, unsigned size);
+PJ_DECL(pj_status_t) pj_fifobuf_unalloc (pj_fifobuf_t *fb, void *buf);
+PJ_DECL(pj_status_t) pj_fifobuf_free (pj_fifobuf_t *fb, void *buf);
+
+PJ_END_DECL
+
+#endif /* __PJ_FIFOBUF_H__ */
+
diff --git a/pjlib/include/pj/file_access.h b/pjlib/include/pj/file_access.h
index 473484d..1034aa8 100644
--- a/pjlib/include/pj/file_access.h
+++ b/pjlib/include/pj/file_access.h
@@ -1,4 +1,25 @@
/* $Id */
+/*
+ * PJLIB - PJ Foundation Library
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ * Benny Prijono <bennylp@bulukucing.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
#ifndef __PJ_FILE_ACCESS_H__
#define __PJ_FILE_ACCESS_H__
diff --git a/pjlib/include/pj/file_io.h b/pjlib/include/pj/file_io.h
index 7e5fa3e..708950b 100644
--- a/pjlib/include/pj/file_io.h
+++ b/pjlib/include/pj/file_io.h
@@ -1,155 +1,176 @@
-/* $Id$ */
-#ifndef __PJ_FILE_IO_H__
-#define __PJ_FILE_IO_H__
-
-/**
- * @file file_io.h
- * @brief Simple file I/O abstraction.
- */
-#include <pj/types.h>
-
-PJ_BEGIN_DECL
-
-/**
- * @defgroup PJ_FILE_IO File I/O
- * @ingroup PJ_IO
- * @{
- *
- * This file contains functionalities to perform file I/O. The file
- * I/O can be implemented with various back-end, either using native
- * file API or ANSI stream.
- *
- * @section pj_file_size_limit_sec Size Limits
- *
- * There may be limitation on the size that can be handled by the
- * #pj_file_setpos() or #pj_file_getpos() functions. The API itself
- * uses 64-bit integer for the file offset/position (where available);
- * however some backends (such as ANSI) may only support signed 32-bit
- * offset resolution.
- *
- * Reading and writing operation uses signed 32-bit integer to indicate
- * the size.
- *
- *
- */
-
-/**
- * These enumerations are used when opening file. Values PJ_O_RDONLY,
- * PJ_O_WRONLY, and PJ_O_RDWR are mutually exclusive. Value PJ_O_APPEND
- * can only be used when the file is opened for writing.
- */
-enum pj_file_access
-{
- PJ_O_RDONLY = 0x1101, /**< Open file for reading. */
- PJ_O_WRONLY = 0x1102, /**< Open file for writing. */
+/* $Id$ */
+/*
+ * PJLIB - PJ Foundation Library
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ * Benny Prijono <bennylp@bulukucing.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#ifndef __PJ_FILE_IO_H__
+#define __PJ_FILE_IO_H__
+
+/**
+ * @file file_io.h
+ * @brief Simple file I/O abstraction.
+ */
+#include <pj/types.h>
+
+PJ_BEGIN_DECL
+
+/**
+ * @defgroup PJ_FILE_IO File I/O
+ * @ingroup PJ_IO
+ * @{
+ *
+ * This file contains functionalities to perform file I/O. The file
+ * I/O can be implemented with various back-end, either using native
+ * file API or ANSI stream.
+ *
+ * @section pj_file_size_limit_sec Size Limits
+ *
+ * There may be limitation on the size that can be handled by the
+ * #pj_file_setpos() or #pj_file_getpos() functions. The API itself
+ * uses 64-bit integer for the file offset/position (where available);
+ * however some backends (such as ANSI) may only support signed 32-bit
+ * offset resolution.
+ *
+ * Reading and writing operation uses signed 32-bit integer to indicate
+ * the size.
+ *
+ *
+ */
+
+/**
+ * These enumerations are used when opening file. Values PJ_O_RDONLY,
+ * PJ_O_WRONLY, and PJ_O_RDWR are mutually exclusive. Value PJ_O_APPEND
+ * can only be used when the file is opened for writing.
+ */
+enum pj_file_access
+{
+ PJ_O_RDONLY = 0x1101, /**< Open file for reading. */
+ PJ_O_WRONLY = 0x1102, /**< Open file for writing. */
PJ_O_RDWR = 0x1103, /**< Open file for reading and writing.
- File will be truncated. */
+ File will be truncated. */
PJ_O_APPEND = 0x1108, /**< Append to existing file. */
-};
-
-/**
- * The seek directive when setting the file position with #pj_file_setpos.
- */
-enum pj_file_seek_type
-{
- PJ_SEEK_SET = 0x1201, /**< Offset from beginning of the file. */
- PJ_SEEK_CUR = 0x1202, /**< Offset from current position. */
- PJ_SEEK_END = 0x1203, /**< Size of the file plus offset. */
-};
-
-/**
- * Open the file as specified in \c pathname with the specified
- * mode, and return the handle in \c fd. All files will be opened
- * as binary.
- *
- * @param pool Pool to allocate memory for the new file descriptor.
- * @param pathname The file name to open.
- * @param flags Open flags, which is bitmask combination of
- * #pj_file_access enum. The flag must be either
- * PJ_O_RDONLY, PJ_O_WRONLY, or PJ_O_RDWR. When file
- * writing is specified, existing file will be
+};
+
+/**
+ * The seek directive when setting the file position with #pj_file_setpos.
+ */
+enum pj_file_seek_type
+{
+ PJ_SEEK_SET = 0x1201, /**< Offset from beginning of the file. */
+ PJ_SEEK_CUR = 0x1202, /**< Offset from current position. */
+ PJ_SEEK_END = 0x1203, /**< Size of the file plus offset. */
+};
+
+/**
+ * Open the file as specified in \c pathname with the specified
+ * mode, and return the handle in \c fd. All files will be opened
+ * as binary.
+ *
+ * @param pool Pool to allocate memory for the new file descriptor.
+ * @param pathname The file name to open.
+ * @param flags Open flags, which is bitmask combination of
+ * #pj_file_access enum. The flag must be either
+ * PJ_O_RDONLY, PJ_O_WRONLY, or PJ_O_RDWR. When file
+ * writing is specified, existing file will be
* truncated unless PJ_O_APPEND is specified.
- * @param fd The returned descriptor.
- *
- * @return PJ_SUCCESS or the appropriate error code on error.
- */
-PJ_DECL(pj_status_t) pj_file_open(pj_pool_t *pool,
- const char *pathname,
- unsigned flags,
- pj_oshandle_t *fd);
-
-/**
- * Close an opened file descriptor.
- *
- * @param fd The file descriptor.
- *
- * @return PJ_SUCCESS or the appropriate error code on error.
- */
-PJ_DECL(pj_status_t) pj_file_close(pj_oshandle_t fd);
-
-/**
- * Write data with the specified size to an opened file.
- *
- * @param fd The file descriptor.
- * @param data Data to be written to the file.
- * @param size On input, specifies the size of data to be written.
- * On return, it contains the number of data actually
- * written to the file.
- *
- * @return PJ_SUCCESS or the appropriate error code on error.
- */
-PJ_DECL(pj_status_t) pj_file_write(pj_oshandle_t fd,
- const void *data,
- pj_ssize_t *size);
-
-/**
+ * @param fd The returned descriptor.
+ *
+ * @return PJ_SUCCESS or the appropriate error code on error.
+ */
+PJ_DECL(pj_status_t) pj_file_open(pj_pool_t *pool,
+ const char *pathname,
+ unsigned flags,
+ pj_oshandle_t *fd);
+
+/**
+ * Close an opened file descriptor.
+ *
+ * @param fd The file descriptor.
+ *
+ * @return PJ_SUCCESS or the appropriate error code on error.
+ */
+PJ_DECL(pj_status_t) pj_file_close(pj_oshandle_t fd);
+
+/**
+ * Write data with the specified size to an opened file.
+ *
+ * @param fd The file descriptor.
+ * @param data Data to be written to the file.
+ * @param size On input, specifies the size of data to be written.
+ * On return, it contains the number of data actually
+ * written to the file.
+ *
+ * @return PJ_SUCCESS or the appropriate error code on error.
+ */
+PJ_DECL(pj_status_t) pj_file_write(pj_oshandle_t fd,
+ const void *data,
+ pj_ssize_t *size);
+
+/**
* Read data from the specified file. When end-of-file condition is set,
- * this function will return PJ_SUCCESS but the size will contain zero.
- *
- * @param fd The file descriptor.
- * @param data Pointer to buffer to receive the data.
- * @param size On input, specifies the maximum number of data to
- * read from the file. On output, it contains the size
+ * this function will return PJ_SUCCESS but the size will contain zero.
+ *
+ * @param fd The file descriptor.
+ * @param data Pointer to buffer to receive the data.
+ * @param size On input, specifies the maximum number of data to
+ * read from the file. On output, it contains the size
* of data actually read from the file. It will contain
- * zero when EOF occurs.
- *
+ * zero when EOF occurs.
+ *
* @return PJ_SUCCESS or the appropriate error code on error.
* When EOF occurs, the return is PJ_SUCCESS but size
- * will report zero.
- */
-PJ_DECL(pj_status_t) pj_file_read(pj_oshandle_t fd,
- void *data,
- pj_ssize_t *size);
+ * will report zero.
+ */
+PJ_DECL(pj_status_t) pj_file_read(pj_oshandle_t fd,
+ void *data,
+ pj_ssize_t *size);
-/**
- * Set file position to new offset according to directive \c whence.
- *
- * @param fd The file descriptor.
- * @param offset The new file position to set.
- * @param whence The directive.
- *
- * @return PJ_SUCCESS or the appropriate error code on error.
- */
-PJ_DECL(pj_status_t) pj_file_setpos(pj_oshandle_t fd,
- pj_off_t offset,
- enum pj_file_seek_type whence);
-
-/**
- * Get current file position.
- *
- * @param fd The file descriptor.
- * @param pos On return contains the file position as measured
- * from the beginning of the file.
- *
- * @return PJ_SUCCESS or the appropriate error code on error.
- */
-PJ_DECL(pj_status_t) pj_file_getpos(pj_oshandle_t fd,
- pj_off_t *pos);
-
-/** @} */
-
-
-PJ_END_DECL
-
-#endif /* __PJ_FILE_IO_H__ */
-
+/**
+ * Set file position to new offset according to directive \c whence.
+ *
+ * @param fd The file descriptor.
+ * @param offset The new file position to set.
+ * @param whence The directive.
+ *
+ * @return PJ_SUCCESS or the appropriate error code on error.
+ */
+PJ_DECL(pj_status_t) pj_file_setpos(pj_oshandle_t fd,
+ pj_off_t offset,
+ enum pj_file_seek_type whence);
+
+/**
+ * Get current file position.
+ *
+ * @param fd The file descriptor.
+ * @param pos On return contains the file position as measured
+ * from the beginning of the file.
+ *
+ * @return PJ_SUCCESS or the appropriate error code on error.
+ */
+PJ_DECL(pj_status_t) pj_file_getpos(pj_oshandle_t fd,
+ pj_off_t *pos);
+
+/** @} */
+
+
+PJ_END_DECL
+
+#endif /* __PJ_FILE_IO_H__ */
+
diff --git a/pjlib/include/pj/guid.h b/pjlib/include/pj/guid.h
index 652b547..230ae1b 100644
--- a/pjlib/include/pj/guid.h
+++ b/pjlib/include/pj/guid.h
@@ -1,75 +1,96 @@
-/* $header: $ */
-
-#ifndef __PJ_GUID_H__
-#define __PJ_GUID_H__
-
-
-/**
- * @file guid.h
- * @brief GUID Globally Unique Identifier.
- */
-#include <pj/types.h>
-
-
-PJ_BEGIN_DECL
-
-
-/**
- * @defgroup PJ_DS Data Structure.
- * @ingroup PJ
- */
-/**
- * @defgroup PJ_GUID Globally Unique Identifier
- * @ingroup PJ_DS
- * @{
- *
- * This module provides API to create string that is globally unique.
- * If application doesn't require that strong requirement, it can just
- * use #pj_create_random_string() instead.
- */
-
-
-/**
- * PJ_GUID_STRING_LENGTH specifies length of GUID string. The value is
- * dependent on the algorithm used internally to generate the GUID string.
- * If real GUID generator is used, then the length will be 128bit or
- * 32 bytes. If shadow GUID generator is used, then the length
- * will be 20 bytes. Application should not assume which algorithm will
- * be used by GUID generator.
- */
-extern const unsigned PJ_GUID_STRING_LENGTH;
-
-/**
- * PJ_GUID_MAX_LENGTH specifies the maximum length of GUID string,
- * regardless of which algorithm to use.
- */
-#define PJ_GUID_MAX_LENGTH 32
-
-/**
- * Create a globally unique string, which length is PJ_GUID_STRING_LENGTH
- * characters. Caller is responsible for preallocating the storage used
- * in the string.
- *
- * @param str The string to store the result.
- *
- * @return The string.
- */
-PJ_DECL(pj_str_t*) pj_generate_unique_string(pj_str_t *str);
-
-/**
- * Generate a unique string.
- *
- * @param pool Pool to allocate memory from.
- * @param str The string.
- */
-PJ_DECL(void) pj_create_unique_string(pj_pool_t *pool, pj_str_t *str);
-
-
-/**
- * @}
- */
-
-PJ_END_DECL
-
-#endif/* __PJ_GUID_H__ */
-
+/* $header: $ */
+/*
+ * PJLIB - PJ Foundation Library
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ * Benny Prijono <bennylp@bulukucing.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef __PJ_GUID_H__
+#define __PJ_GUID_H__
+
+
+/**
+ * @file guid.h
+ * @brief GUID Globally Unique Identifier.
+ */
+#include <pj/types.h>
+
+
+PJ_BEGIN_DECL
+
+
+/**
+ * @defgroup PJ_DS Data Structure.
+ * @ingroup PJ
+ */
+/**
+ * @defgroup PJ_GUID Globally Unique Identifier
+ * @ingroup PJ_DS
+ * @{
+ *
+ * This module provides API to create string that is globally unique.
+ * If application doesn't require that strong requirement, it can just
+ * use #pj_create_random_string() instead.
+ */
+
+
+/**
+ * PJ_GUID_STRING_LENGTH specifies length of GUID string. The value is
+ * dependent on the algorithm used internally to generate the GUID string.
+ * If real GUID generator is used, then the length will be 128bit or
+ * 32 bytes. If shadow GUID generator is used, then the length
+ * will be 20 bytes. Application should not assume which algorithm will
+ * be used by GUID generator.
+ */
+extern const unsigned PJ_GUID_STRING_LENGTH;
+
+/**
+ * PJ_GUID_MAX_LENGTH specifies the maximum length of GUID string,
+ * regardless of which algorithm to use.
+ */
+#define PJ_GUID_MAX_LENGTH 32
+
+/**
+ * Create a globally unique string, which length is PJ_GUID_STRING_LENGTH
+ * characters. Caller is responsible for preallocating the storage used
+ * in the string.
+ *
+ * @param str The string to store the result.
+ *
+ * @return The string.
+ */
+PJ_DECL(pj_str_t*) pj_generate_unique_string(pj_str_t *str);
+
+/**
+ * Generate a unique string.
+ *
+ * @param pool Pool to allocate memory from.
+ * @param str The string.
+ */
+PJ_DECL(void) pj_create_unique_string(pj_pool_t *pool, pj_str_t *str);
+
+
+/**
+ * @}
+ */
+
+PJ_END_DECL
+
+#endif/* __PJ_GUID_H__ */
+
diff --git a/pjlib/include/pj/hash.h b/pjlib/include/pj/hash.h
index b1eb9d9..2c4fca4 100644
--- a/pjlib/include/pj/hash.h
+++ b/pjlib/include/pj/hash.h
@@ -1,49 +1,70 @@
-/* $Id$
- *
- */
-
-#ifndef __PJ_HASH_H__
-#define __PJ_HASH_H__
-
-/**
- * @file hash.h
- * @brief Hash Table.
- */
-
-#include <pj/types.h>
-
-PJ_BEGIN_DECL
-
-/**
- * @defgroup PJ_HASH Hash Table
- * @ingroup PJ_DS
- * @{
- * A hash table is a dictionary in which keys are mapped to array positions by
- * hash functions. Having the keys of more than one item map to the same
- * position is called a collision. In this library, we will chain the nodes
- * that have the same key in a list.
- */
-
-/**
- * If this constant is used as keylen, then the key is interpreted as
- * NULL terminated string.
- */
-#define PJ_HASH_KEY_STRING ((unsigned)-1)
-
-/**
- * This is the function that is used by the hash table to calculate hash value
- * of the specified key.
- *
- * @param hval the initial hash value, or zero.
- * @param key the key to calculate.
- * @param keylen the length of the key, or PJ_HASH_KEY_STRING to treat
- * the key as null terminated string.
- *
- * @return the hash value.
- */
-PJ_DECL(pj_uint32_t) pj_hash_calc(pj_uint32_t hval,
- const void *key, unsigned keylen);
-
+/* $Id$
+ *
+ */
+/*
+ * PJLIB - PJ Foundation Library
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ * Benny Prijono <bennylp@bulukucing.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef __PJ_HASH_H__
+#define __PJ_HASH_H__
+
+/**
+ * @file hash.h
+ * @brief Hash Table.
+ */
+
+#include <pj/types.h>
+
+PJ_BEGIN_DECL
+
+/**
+ * @defgroup PJ_HASH Hash Table
+ * @ingroup PJ_DS
+ * @{
+ * A hash table is a dictionary in which keys are mapped to array positions by
+ * hash functions. Having the keys of more than one item map to the same
+ * position is called a collision. In this library, we will chain the nodes
+ * that have the same key in a list.
+ */
+
+/**
+ * If this constant is used as keylen, then the key is interpreted as
+ * NULL terminated string.
+ */
+#define PJ_HASH_KEY_STRING ((unsigned)-1)
+
+/**
+ * This is the function that is used by the hash table to calculate hash value
+ * of the specified key.
+ *
+ * @param hval the initial hash value, or zero.
+ * @param key the key to calculate.
+ * @param keylen the length of the key, or PJ_HASH_KEY_STRING to treat
+ * the key as null terminated string.
+ *
+ * @return the hash value.
+ */
+PJ_DECL(pj_uint32_t) pj_hash_calc(pj_uint32_t hval,
+ const void *key, unsigned keylen);
+
/**
* Convert the key to lowercase and calculate the hash value. The resulting
@@ -59,99 +80,99 @@
PJ_DECL(pj_uint32_t) pj_hash_calc_tolower(pj_uint32_t hval,
char *result,
const pj_str_t *key);
-
-/**
- * Create a hash table with the specified 'bucket' size.
- *
- * @param pool the pool from which the hash table will be allocated from.
- * @param size the bucket size, which will be round-up to the nearest 2^n+1
- *
- * @return the hash table.
- */
-PJ_DECL(pj_hash_table_t*) pj_hash_create(pj_pool_t *pool, unsigned size);
-
-
-/**
- * Get the value associated with the specified key.
- *
- * @param ht the hash table.
- * @param key the key to look for.
- * @param keylen the length of the key, or PJ_HASH_KEY_STRING to use the
- * string length of the key.
- *
- * @return the value associated with the key, or NULL if the key is not found.
- */
-PJ_DECL(void *) pj_hash_get( pj_hash_table_t *ht,
- const void *key, unsigned keylen );
-
-
-/**
- * Associate/disassociate a value with the specified key.
- *
- * @param pool the pool to allocate the new entry if a new entry has to be
- * created.
- * @param ht the hash table.
- * @param key the key.
- * @param keylen the length of the key, or PJ_HASH_KEY_STRING to use the
- * string length of the key.
- * @param value value to be associated, or NULL to delete the entry with
- * the specified key.
- */
-PJ_DECL(void) pj_hash_set( pj_pool_t *pool, pj_hash_table_t *ht,
- const void *key, unsigned keylen,
- void *value );
-
-/**
- * Get the total number of entries in the hash table.
- *
- * @param ht the hash table.
- *
- * @return the number of entries in the hash table.
- */
-PJ_DECL(unsigned) pj_hash_count( pj_hash_table_t *ht );
-
-
-/**
- * Get the iterator to the first element in the hash table.
- *
- * @param ht the hash table.
- * @param it the iterator for iterating hash elements.
- *
- * @return the iterator to the hash element, or NULL if no element presents.
- */
-PJ_DECL(pj_hash_iterator_t*) pj_hash_first( pj_hash_table_t *ht,
- pj_hash_iterator_t *it );
-
-
-/**
- * Get the next element from the iterator.
- *
- * @param ht the hash table.
- * @param it the hash iterator.
- *
- * @return the next iterator, or NULL if there's no more element.
- */
-PJ_DECL(pj_hash_iterator_t*) pj_hash_next( pj_hash_table_t *ht,
- pj_hash_iterator_t *it );
-
-/**
- * Get the value associated with a hash iterator.
- *
- * @param ht the hash table.
- * @param it the hash iterator.
- *
- * @return the value associated with the current element in iterator.
- */
-PJ_DECL(void*) pj_hash_this( pj_hash_table_t *ht,
- pj_hash_iterator_t *it );
-
-
-/**
- * @}
- */
-
-PJ_END_DECL
-
-#endif
-
-
+
+/**
+ * Create a hash table with the specified 'bucket' size.
+ *
+ * @param pool the pool from which the hash table will be allocated from.
+ * @param size the bucket size, which will be round-up to the nearest 2^n+1
+ *
+ * @return the hash table.
+ */
+PJ_DECL(pj_hash_table_t*) pj_hash_create(pj_pool_t *pool, unsigned size);
+
+
+/**
+ * Get the value associated with the specified key.
+ *
+ * @param ht the hash table.
+ * @param key the key to look for.
+ * @param keylen the length of the key, or PJ_HASH_KEY_STRING to use the
+ * string length of the key.
+ *
+ * @return the value associated with the key, or NULL if the key is not found.
+ */
+PJ_DECL(void *) pj_hash_get( pj_hash_table_t *ht,
+ const void *key, unsigned keylen );
+
+
+/**
+ * Associate/disassociate a value with the specified key.
+ *
+ * @param pool the pool to allocate the new entry if a new entry has to be
+ * created.
+ * @param ht the hash table.
+ * @param key the key.
+ * @param keylen the length of the key, or PJ_HASH_KEY_STRING to use the
+ * string length of the key.
+ * @param value value to be associated, or NULL to delete the entry with
+ * the specified key.
+ */
+PJ_DECL(void) pj_hash_set( pj_pool_t *pool, pj_hash_table_t *ht,
+ const void *key, unsigned keylen,
+ void *value );
+
+/**
+ * Get the total number of entries in the hash table.
+ *
+ * @param ht the hash table.
+ *
+ * @return the number of entries in the hash table.
+ */
+PJ_DECL(unsigned) pj_hash_count( pj_hash_table_t *ht );
+
+
+/**
+ * Get the iterator to the first element in the hash table.
+ *
+ * @param ht the hash table.
+ * @param it the iterator for iterating hash elements.
+ *
+ * @return the iterator to the hash element, or NULL if no element presents.
+ */
+PJ_DECL(pj_hash_iterator_t*) pj_hash_first( pj_hash_table_t *ht,
+ pj_hash_iterator_t *it );
+
+
+/**
+ * Get the next element from the iterator.
+ *
+ * @param ht the hash table.
+ * @param it the hash iterator.
+ *
+ * @return the next iterator, or NULL if there's no more element.
+ */
+PJ_DECL(pj_hash_iterator_t*) pj_hash_next( pj_hash_table_t *ht,
+ pj_hash_iterator_t *it );
+
+/**
+ * Get the value associated with a hash iterator.
+ *
+ * @param ht the hash table.
+ * @param it the hash iterator.
+ *
+ * @return the value associated with the current element in iterator.
+ */
+PJ_DECL(void*) pj_hash_this( pj_hash_table_t *ht,
+ pj_hash_iterator_t *it );
+
+
+/**
+ * @}
+ */
+
+PJ_END_DECL
+
+#endif
+
+
diff --git a/pjlib/include/pj/ioqueue.h b/pjlib/include/pj/ioqueue.h
index d1aea69..87f9c10 100644
--- a/pjlib/include/pj/ioqueue.h
+++ b/pjlib/include/pj/ioqueue.h
@@ -1,344 +1,365 @@
-/* $Id$
- */
-
-#ifndef __PJ_IOQUEUE_H__
-#define __PJ_IOQUEUE_H__
-
-/**
- * @file ioqueue.h
- * @brief I/O Dispatching Mechanism
- */
-
-#include <pj/types.h>
-
-PJ_BEGIN_DECL
-
-/**
- * @defgroup PJ_IO Input/Output
- * @brief Input/Output
- * @ingroup PJ_OS
- *
- * This section contains API building blocks to perform network I/O and
- * communications. If provides:
- * - @ref PJ_SOCK
- *\n
- * A highly portable socket abstraction, runs on all kind of
- * network APIs such as standard BSD socket, Windows socket, Linux
- * \b kernel socket, PalmOS networking API, etc.
- *
- * - @ref pj_addr_resolve
- *\n
- * Portable address resolution, which implements #pj_gethostbyname().
- *
- * - @ref PJ_SOCK_SELECT
- *\n
- * A portable \a select() like API (#pj_sock_select()) which can be
- * implemented with various back-ends.
- *
- * - @ref PJ_IOQUEUE
- *\n
- * Framework for dispatching network events.
- *
- * For more information see the modules below.
- */
-
-/**
- * @defgroup PJ_IOQUEUE I/O Event Dispatching Queue
- * @ingroup PJ_IO
- * @{
- *
- * I/O Queue provides API for performing asynchronous I/O operations. It
- * conforms to proactor pattern, which allows application to submit an
- * asynchronous operation and to be notified later when the operation has
- * completed.
- *
- * The I/O Queue can work on both socket and file descriptors. For
- * asynchronous file operations however, one must make sure that the correct
- * file I/O back-end is used, because not all file I/O back-end can be
- * used with the ioqueue. Please see \ref PJ_FILE_IO for more details.
- *
- * The framework works natively in platforms where asynchronous operation API
- * exists, such as in Windows NT with IoCompletionPort/IOCP. In other
- * platforms, the I/O queue abstracts the operating system's event poll API
- * to provide semantics similar to IoCompletionPort with minimal penalties
- * (i.e. per ioqueue and per handle mutex protection).
- *
- * The I/O queue provides more than just unified abstraction. It also:
- * - makes sure that the operation uses the most effective way to utilize
- * the underlying mechanism, to achieve the maximum theoritical
- * throughput possible on a given platform.
- * - choose the most efficient mechanism for event polling on a given
- * platform.
- *
- * Currently, the I/O Queue is implemented using:
- * - <tt><b>select()</b></tt>, as the common denominator, but the least
- * efficient. Also the number of descriptor is limited to
- * \c PJ_IOQUEUE_MAX_HANDLES (which by default is 64).
- * - <tt><b>/dev/epoll</b></tt> on Linux (user mode and kernel mode),
- * a much faster replacement for select() on Linux (and more importantly
- * doesn't have limitation on number of descriptors).
- * - <b>I/O Completion ports</b> on Windows NT/2000/XP, which is the most
- * efficient way to dispatch events in Windows NT based OSes, and most
- * importantly, it doesn't have the limit on how many handles to monitor.
- * And it works with files (not only sockets) as well.
- *
- *
- * \section pj_ioqueue_concurrency_sec Concurrency Rules
- *
- * The items below describe rules that must be obeyed when using the I/O
- * queue, with regard to concurrency:
- * - simultaneous operations (by different threads) to different key is safe.
- * - simultaneous operations to the same key is also safe, except
- * <b>unregistration</b>, which is described below.
- * - <b>care must be taken when unregistering a key</b> from the
- * ioqueue. Application must take care that when one thread is issuing
- * an unregistration, other thread is not simultaneously invoking an
- * operation <b>to the same key</b>.
- *\n
- * This happens because the ioqueue functions are working with a pointer
- * to the key, and there is a possible race condition where the pointer
- * has been rendered invalid by other threads before the ioqueue has a
- * chance to acquire mutex on it.
- *
- * \section pj_ioqeuue_examples_sec Examples
- *
- * For some examples on how to use the I/O Queue, please see:
- *
- * - \ref page_pjlib_ioqueue_tcp_test
- * - \ref page_pjlib_ioqueue_udp_test
- * - \ref page_pjlib_ioqueue_perf_test
- */
-
-
-/**
- * This structure describes operation specific key to be submitted to
- * I/O Queue when performing the asynchronous operation. This key will
- * be returned to the application when completion callback is called.
- *
- * Application normally wants to attach it's specific data in the
- * \c user_data field so that it can keep track of which operation has
- * completed when the callback is called. Alternatively, application can
- * also extend this struct to include its data, because the pointer that
- * is returned in the completion callback will be exactly the same as
- * the pointer supplied when the asynchronous function is called.
- */
-typedef struct pj_ioqueue_op_key_t
-{
- void *internal__[32]; /**< Internal I/O Queue data. */
- void *user_data; /**< Application data. */
-} pj_ioqueue_op_key_t;
-
-/**
- * This structure describes the callbacks to be called when I/O operation
- * completes.
- */
-typedef struct pj_ioqueue_callback
-{
- /**
- * This callback is called when #pj_ioqueue_recv or #pj_ioqueue_recvfrom
- * completes.
- *
- * @param key The key.
- * @param op_key Operation key.
- * @param bytes_read >= 0 to indicate the amount of data read,
- * otherwise negative value containing the error
- * code. To obtain the pj_status_t error code, use
- * (pj_status_t code = -bytes_read).
- */
- void (*on_read_complete)(pj_ioqueue_key_t *key,
- pj_ioqueue_op_key_t *op_key,
- pj_ssize_t bytes_read);
-
- /**
- * This callback is called when #pj_ioqueue_write or #pj_ioqueue_sendto
- * completes.
- *
- * @param key The key.
- * @param op_key Operation key.
- * @param bytes_sent >= 0 to indicate the amount of data written,
- * otherwise negative value containing the error
- * code. To obtain the pj_status_t error code, use
- * (pj_status_t code = -bytes_sent).
- */
- void (*on_write_complete)(pj_ioqueue_key_t *key,
- pj_ioqueue_op_key_t *op_key,
- pj_ssize_t bytes_sent);
-
- /**
- * This callback is called when #pj_ioqueue_accept completes.
- *
- * @param key The key.
- * @param op_key Operation key.
- * @param sock Newly connected socket.
- * @param status Zero if the operation completes successfully.
- */
- void (*on_accept_complete)(pj_ioqueue_key_t *key,
- pj_ioqueue_op_key_t *op_key,
- pj_sock_t sock,
- pj_status_t status);
-
- /**
- * This callback is called when #pj_ioqueue_connect completes.
- *
- * @param key The key.
- * @param status PJ_SUCCESS if the operation completes successfully.
- */
- void (*on_connect_complete)(pj_ioqueue_key_t *key,
- pj_status_t status);
-} pj_ioqueue_callback;
-
-
-/**
- * Types of pending I/O Queue operation. This enumeration is only used
- * internally within the ioqueue.
- */
-typedef enum pj_ioqueue_operation_e
-{
- PJ_IOQUEUE_OP_NONE = 0, /**< No operation. */
- PJ_IOQUEUE_OP_READ = 1, /**< read() operation. */
- PJ_IOQUEUE_OP_RECV = 2, /**< recv() operation. */
- PJ_IOQUEUE_OP_RECV_FROM = 4, /**< recvfrom() operation. */
- PJ_IOQUEUE_OP_WRITE = 8, /**< write() operation. */
- PJ_IOQUEUE_OP_SEND = 16, /**< send() operation. */
- PJ_IOQUEUE_OP_SEND_TO = 32, /**< sendto() operation. */
-#if defined(PJ_HAS_TCP) && PJ_HAS_TCP != 0
- PJ_IOQUEUE_OP_ACCEPT = 64, /**< accept() operation. */
- PJ_IOQUEUE_OP_CONNECT = 128, /**< connect() operation. */
-#endif /* PJ_HAS_TCP */
-} pj_ioqueue_operation_e;
-
-
-/**
- * This macro specifies the maximum number of events that can be
- * processed by the ioqueue on a single poll cycle, on implementation
- * that supports it. The value is only meaningfull when specified
- * during PJLIB build.
- */
-#ifndef PJ_IOQUEUE_MAX_EVENTS_IN_SINGLE_POLL
-# define PJ_IOQUEUE_MAX_EVENTS_IN_SINGLE_POLL (16)
-#endif
-
-/**
- * Return the name of the ioqueue implementation.
- *
- * @return Implementation name.
- */
-PJ_DECL(const char*) pj_ioqueue_name(void);
-
-
-/**
- * Create a new I/O Queue framework.
- *
- * @param pool The pool to allocate the I/O queue structure.
- * @param max_fd The maximum number of handles to be supported, which
- * should not exceed PJ_IOQUEUE_MAX_HANDLES.
- * @param ioqueue Pointer to hold the newly created I/O Queue.
- *
- * @return PJ_SUCCESS on success.
- */
-PJ_DECL(pj_status_t) pj_ioqueue_create( pj_pool_t *pool,
- pj_size_t max_fd,
- pj_ioqueue_t **ioqueue);
-
-/**
- * Destroy the I/O queue.
- *
- * @param ioque The I/O Queue to be destroyed.
- *
- * @return PJ_SUCCESS if success.
- */
-PJ_DECL(pj_status_t) pj_ioqueue_destroy( pj_ioqueue_t *ioque );
-
-/**
- * Set the lock object to be used by the I/O Queue. This function can only
- * be called right after the I/O queue is created, before any handle is
- * registered to the I/O queue.
- *
- * Initially the I/O queue is created with non-recursive mutex protection.
- * Applications can supply alternative lock to be used by calling this
- * function.
- *
- * @param ioque The ioqueue instance.
- * @param lock The lock to be used by the ioqueue.
- * @param auto_delete In non-zero, the lock will be deleted by the ioqueue.
- *
- * @return PJ_SUCCESS or the appropriate error code.
- */
-PJ_DECL(pj_status_t) pj_ioqueue_set_lock( pj_ioqueue_t *ioque,
- pj_lock_t *lock,
- pj_bool_t auto_delete );
-
-/**
- * Register a socket to the I/O queue framework.
- * When a socket is registered to the IOQueue, it may be modified to use
- * non-blocking IO. If it is modified, there is no guarantee that this
- * modification will be restored after the socket is unregistered.
- *
- * @param pool To allocate the resource for the specified handle,
- * which must be valid until the handle/key is unregistered
- * from I/O Queue.
- * @param ioque The I/O Queue.
- * @param sock The socket.
- * @param user_data User data to be associated with the key, which can be
- * retrieved later.
- * @param cb Callback to be called when I/O operation completes.
- * @param key Pointer to receive the key to be associated with this
- * socket. Subsequent I/O queue operation will need this
- * key.
- *
- * @return PJ_SUCCESS on success, or the error code.
- */
-PJ_DECL(pj_status_t) pj_ioqueue_register_sock( pj_pool_t *pool,
- pj_ioqueue_t *ioque,
- pj_sock_t sock,
- void *user_data,
- const pj_ioqueue_callback *cb,
- pj_ioqueue_key_t **key );
-
-/**
- * Unregister from the I/O Queue framework. Caller must make sure that
- * the key doesn't have any pending operations before calling this function,
+/* $Id$
+ */
+/*
+ * PJLIB - PJ Foundation Library
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ * Benny Prijono <bennylp@bulukucing.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef __PJ_IOQUEUE_H__
+#define __PJ_IOQUEUE_H__
+
+/**
+ * @file ioqueue.h
+ * @brief I/O Dispatching Mechanism
+ */
+
+#include <pj/types.h>
+
+PJ_BEGIN_DECL
+
+/**
+ * @defgroup PJ_IO Input/Output
+ * @brief Input/Output
+ * @ingroup PJ_OS
+ *
+ * This section contains API building blocks to perform network I/O and
+ * communications. If provides:
+ * - @ref PJ_SOCK
+ *\n
+ * A highly portable socket abstraction, runs on all kind of
+ * network APIs such as standard BSD socket, Windows socket, Linux
+ * \b kernel socket, PalmOS networking API, etc.
+ *
+ * - @ref pj_addr_resolve
+ *\n
+ * Portable address resolution, which implements #pj_gethostbyname().
+ *
+ * - @ref PJ_SOCK_SELECT
+ *\n
+ * A portable \a select() like API (#pj_sock_select()) which can be
+ * implemented with various back-ends.
+ *
+ * - @ref PJ_IOQUEUE
+ *\n
+ * Framework for dispatching network events.
+ *
+ * For more information see the modules below.
+ */
+
+/**
+ * @defgroup PJ_IOQUEUE I/O Event Dispatching Queue
+ * @ingroup PJ_IO
+ * @{
+ *
+ * I/O Queue provides API for performing asynchronous I/O operations. It
+ * conforms to proactor pattern, which allows application to submit an
+ * asynchronous operation and to be notified later when the operation has
+ * completed.
+ *
+ * The I/O Queue can work on both socket and file descriptors. For
+ * asynchronous file operations however, one must make sure that the correct
+ * file I/O back-end is used, because not all file I/O back-end can be
+ * used with the ioqueue. Please see \ref PJ_FILE_IO for more details.
+ *
+ * The framework works natively in platforms where asynchronous operation API
+ * exists, such as in Windows NT with IoCompletionPort/IOCP. In other
+ * platforms, the I/O queue abstracts the operating system's event poll API
+ * to provide semantics similar to IoCompletionPort with minimal penalties
+ * (i.e. per ioqueue and per handle mutex protection).
+ *
+ * The I/O queue provides more than just unified abstraction. It also:
+ * - makes sure that the operation uses the most effective way to utilize
+ * the underlying mechanism, to achieve the maximum theoritical
+ * throughput possible on a given platform.
+ * - choose the most efficient mechanism for event polling on a given
+ * platform.
+ *
+ * Currently, the I/O Queue is implemented using:
+ * - <tt><b>select()</b></tt>, as the common denominator, but the least
+ * efficient. Also the number of descriptor is limited to
+ * \c PJ_IOQUEUE_MAX_HANDLES (which by default is 64).
+ * - <tt><b>/dev/epoll</b></tt> on Linux (user mode and kernel mode),
+ * a much faster replacement for select() on Linux (and more importantly
+ * doesn't have limitation on number of descriptors).
+ * - <b>I/O Completion ports</b> on Windows NT/2000/XP, which is the most
+ * efficient way to dispatch events in Windows NT based OSes, and most
+ * importantly, it doesn't have the limit on how many handles to monitor.
+ * And it works with files (not only sockets) as well.
+ *
+ *
+ * \section pj_ioqueue_concurrency_sec Concurrency Rules
+ *
+ * The items below describe rules that must be obeyed when using the I/O
+ * queue, with regard to concurrency:
+ * - simultaneous operations (by different threads) to different key is safe.
+ * - simultaneous operations to the same key is also safe, except
+ * <b>unregistration</b>, which is described below.
+ * - <b>care must be taken when unregistering a key</b> from the
+ * ioqueue. Application must take care that when one thread is issuing
+ * an unregistration, other thread is not simultaneously invoking an
+ * operation <b>to the same key</b>.
+ *\n
+ * This happens because the ioqueue functions are working with a pointer
+ * to the key, and there is a possible race condition where the pointer
+ * has been rendered invalid by other threads before the ioqueue has a
+ * chance to acquire mutex on it.
+ *
+ * \section pj_ioqeuue_examples_sec Examples
+ *
+ * For some examples on how to use the I/O Queue, please see:
+ *
+ * - \ref page_pjlib_ioqueue_tcp_test
+ * - \ref page_pjlib_ioqueue_udp_test
+ * - \ref page_pjlib_ioqueue_perf_test
+ */
+
+
+/**
+ * This structure describes operation specific key to be submitted to
+ * I/O Queue when performing the asynchronous operation. This key will
+ * be returned to the application when completion callback is called.
+ *
+ * Application normally wants to attach it's specific data in the
+ * \c user_data field so that it can keep track of which operation has
+ * completed when the callback is called. Alternatively, application can
+ * also extend this struct to include its data, because the pointer that
+ * is returned in the completion callback will be exactly the same as
+ * the pointer supplied when the asynchronous function is called.
+ */
+typedef struct pj_ioqueue_op_key_t
+{
+ void *internal__[32]; /**< Internal I/O Queue data. */
+ void *user_data; /**< Application data. */
+} pj_ioqueue_op_key_t;
+
+/**
+ * This structure describes the callbacks to be called when I/O operation
+ * completes.
+ */
+typedef struct pj_ioqueue_callback
+{
+ /**
+ * This callback is called when #pj_ioqueue_recv or #pj_ioqueue_recvfrom
+ * completes.
+ *
+ * @param key The key.
+ * @param op_key Operation key.
+ * @param bytes_read >= 0 to indicate the amount of data read,
+ * otherwise negative value containing the error
+ * code. To obtain the pj_status_t error code, use
+ * (pj_status_t code = -bytes_read).
+ */
+ void (*on_read_complete)(pj_ioqueue_key_t *key,
+ pj_ioqueue_op_key_t *op_key,
+ pj_ssize_t bytes_read);
+
+ /**
+ * This callback is called when #pj_ioqueue_write or #pj_ioqueue_sendto
+ * completes.
+ *
+ * @param key The key.
+ * @param op_key Operation key.
+ * @param bytes_sent >= 0 to indicate the amount of data written,
+ * otherwise negative value containing the error
+ * code. To obtain the pj_status_t error code, use
+ * (pj_status_t code = -bytes_sent).
+ */
+ void (*on_write_complete)(pj_ioqueue_key_t *key,
+ pj_ioqueue_op_key_t *op_key,
+ pj_ssize_t bytes_sent);
+
+ /**
+ * This callback is called when #pj_ioqueue_accept completes.
+ *
+ * @param key The key.
+ * @param op_key Operation key.
+ * @param sock Newly connected socket.
+ * @param status Zero if the operation completes successfully.
+ */
+ void (*on_accept_complete)(pj_ioqueue_key_t *key,
+ pj_ioqueue_op_key_t *op_key,
+ pj_sock_t sock,
+ pj_status_t status);
+
+ /**
+ * This callback is called when #pj_ioqueue_connect completes.
+ *
+ * @param key The key.
+ * @param status PJ_SUCCESS if the operation completes successfully.
+ */
+ void (*on_connect_complete)(pj_ioqueue_key_t *key,
+ pj_status_t status);
+} pj_ioqueue_callback;
+
+
+/**
+ * Types of pending I/O Queue operation. This enumeration is only used
+ * internally within the ioqueue.
+ */
+typedef enum pj_ioqueue_operation_e
+{
+ PJ_IOQUEUE_OP_NONE = 0, /**< No operation. */
+ PJ_IOQUEUE_OP_READ = 1, /**< read() operation. */
+ PJ_IOQUEUE_OP_RECV = 2, /**< recv() operation. */
+ PJ_IOQUEUE_OP_RECV_FROM = 4, /**< recvfrom() operation. */
+ PJ_IOQUEUE_OP_WRITE = 8, /**< write() operation. */
+ PJ_IOQUEUE_OP_SEND = 16, /**< send() operation. */
+ PJ_IOQUEUE_OP_SEND_TO = 32, /**< sendto() operation. */
+#if defined(PJ_HAS_TCP) && PJ_HAS_TCP != 0
+ PJ_IOQUEUE_OP_ACCEPT = 64, /**< accept() operation. */
+ PJ_IOQUEUE_OP_CONNECT = 128, /**< connect() operation. */
+#endif /* PJ_HAS_TCP */
+} pj_ioqueue_operation_e;
+
+
+/**
+ * This macro specifies the maximum number of events that can be
+ * processed by the ioqueue on a single poll cycle, on implementation
+ * that supports it. The value is only meaningfull when specified
+ * during PJLIB build.
+ */
+#ifndef PJ_IOQUEUE_MAX_EVENTS_IN_SINGLE_POLL
+# define PJ_IOQUEUE_MAX_EVENTS_IN_SINGLE_POLL (16)
+#endif
+
+/**
+ * Return the name of the ioqueue implementation.
+ *
+ * @return Implementation name.
+ */
+PJ_DECL(const char*) pj_ioqueue_name(void);
+
+
+/**
+ * Create a new I/O Queue framework.
+ *
+ * @param pool The pool to allocate the I/O queue structure.
+ * @param max_fd The maximum number of handles to be supported, which
+ * should not exceed PJ_IOQUEUE_MAX_HANDLES.
+ * @param ioqueue Pointer to hold the newly created I/O Queue.
+ *
+ * @return PJ_SUCCESS on success.
+ */
+PJ_DECL(pj_status_t) pj_ioqueue_create( pj_pool_t *pool,
+ pj_size_t max_fd,
+ pj_ioqueue_t **ioqueue);
+
+/**
+ * Destroy the I/O queue.
+ *
+ * @param ioque The I/O Queue to be destroyed.
+ *
+ * @return PJ_SUCCESS if success.
+ */
+PJ_DECL(pj_status_t) pj_ioqueue_destroy( pj_ioqueue_t *ioque );
+
+/**
+ * Set the lock object to be used by the I/O Queue. This function can only
+ * be called right after the I/O queue is created, before any handle is
+ * registered to the I/O queue.
+ *
+ * Initially the I/O queue is created with non-recursive mutex protection.
+ * Applications can supply alternative lock to be used by calling this
+ * function.
+ *
+ * @param ioque The ioqueue instance.
+ * @param lock The lock to be used by the ioqueue.
+ * @param auto_delete In non-zero, the lock will be deleted by the ioqueue.
+ *
+ * @return PJ_SUCCESS or the appropriate error code.
+ */
+PJ_DECL(pj_status_t) pj_ioqueue_set_lock( pj_ioqueue_t *ioque,
+ pj_lock_t *lock,
+ pj_bool_t auto_delete );
+
+/**
+ * Register a socket to the I/O queue framework.
+ * When a socket is registered to the IOQueue, it may be modified to use
+ * non-blocking IO. If it is modified, there is no guarantee that this
+ * modification will be restored after the socket is unregistered.
+ *
+ * @param pool To allocate the resource for the specified handle,
+ * which must be valid until the handle/key is unregistered
+ * from I/O Queue.
+ * @param ioque The I/O Queue.
+ * @param sock The socket.
+ * @param user_data User data to be associated with the key, which can be
+ * retrieved later.
+ * @param cb Callback to be called when I/O operation completes.
+ * @param key Pointer to receive the key to be associated with this
+ * socket. Subsequent I/O queue operation will need this
+ * key.
+ *
+ * @return PJ_SUCCESS on success, or the error code.
+ */
+PJ_DECL(pj_status_t) pj_ioqueue_register_sock( pj_pool_t *pool,
+ pj_ioqueue_t *ioque,
+ pj_sock_t sock,
+ void *user_data,
+ const pj_ioqueue_callback *cb,
+ pj_ioqueue_key_t **key );
+
+/**
+ * Unregister from the I/O Queue framework. Caller must make sure that
+ * the key doesn't have any pending operations before calling this function,
* by calling #pj_ioqueue_is_pending() for all previously submitted
* operations except asynchronous connect, and if necessary call
* #pj_ioqueue_post_completion() to cancel the pending operations.
*
* Note that asynchronous connect operation will automatically be
- * cancelled during the unregistration.
- *
- * @param key The key that was previously obtained from registration.
- *
+ * cancelled during the unregistration.
+ *
+ * @param key The key that was previously obtained from registration.
+ *
* @return PJ_SUCCESS on success or the error code.
*
- * @see pj_ioqueue_is_pending
- */
-PJ_DECL(pj_status_t) pj_ioqueue_unregister( pj_ioqueue_key_t *key );
-
-
-/**
- * Get user data associated with an ioqueue key.
- *
- * @param key The key that was previously obtained from registration.
- *
- * @return The user data associated with the descriptor, or NULL
- * on error or if no data is associated with the key during
- * registration.
- */
-PJ_DECL(void*) pj_ioqueue_get_user_data( pj_ioqueue_key_t *key );
-
-/**
- * Set or change the user data to be associated with the file descriptor or
- * handle or socket descriptor.
- *
- * @param key The key that was previously obtained from registration.
- * @param user_data User data to be associated with the descriptor.
- * @param old_data Optional parameter to retrieve the old user data.
- *
- * @return PJ_SUCCESS on success or the error code.
- */
-PJ_DECL(pj_status_t) pj_ioqueue_set_user_data( pj_ioqueue_key_t *key,
- void *user_data,
- void **old_data);
-
+ * @see pj_ioqueue_is_pending
+ */
+PJ_DECL(pj_status_t) pj_ioqueue_unregister( pj_ioqueue_key_t *key );
+
+
+/**
+ * Get user data associated with an ioqueue key.
+ *
+ * @param key The key that was previously obtained from registration.
+ *
+ * @return The user data associated with the descriptor, or NULL
+ * on error or if no data is associated with the key during
+ * registration.
+ */
+PJ_DECL(void*) pj_ioqueue_get_user_data( pj_ioqueue_key_t *key );
+
+/**
+ * Set or change the user data to be associated with the file descriptor or
+ * handle or socket descriptor.
+ *
+ * @param key The key that was previously obtained from registration.
+ * @param user_data User data to be associated with the descriptor.
+ * @param old_data Optional parameter to retrieve the old user data.
+ *
+ * @return PJ_SUCCESS on success or the error code.
+ */
+PJ_DECL(pj_status_t) pj_ioqueue_set_user_data( pj_ioqueue_key_t *key,
+ void *user_data,
+ void **old_data);
+
/**
* Initialize operation key.
@@ -384,256 +405,256 @@
pj_ssize_t bytes_status );
-
-#if defined(PJ_HAS_TCP) && PJ_HAS_TCP != 0
-/**
- * Instruct I/O Queue to accept incoming connection on the specified
- * listening socket. This function will return immediately (i.e. non-blocking)
- * regardless whether a connection is immediately available. If the function
- * can't complete immediately, the caller will be notified about the incoming
- * connection when it calls pj_ioqueue_poll(). If a new connection is
- * immediately available, the function returns PJ_SUCCESS with the new
- * connection; in this case, the callback WILL NOT be called.
- *
- * @param key The key which registered to the server socket.
- * @param op_key An operation specific key to be associated with the
- * pending operation, so that application can keep track of
- * which operation has been completed when the callback is
- * called.
- * @param new_sock Argument which contain pointer to receive the new socket
- * for the incoming connection.
- * @param local Optional argument which contain pointer to variable to
- * receive local address.
- * @param remote Optional argument which contain pointer to variable to
- * receive the remote address.
- * @param addrlen On input, contains the length of the buffer for the
- * address, and on output, contains the actual length of the
- * address. This argument is optional.
- * @return
- * - PJ_SUCCESS When connection is available immediately, and the
- * parameters will be updated to contain information about
- * the new connection. In this case, a completion callback
- * WILL NOT be called.
- * - PJ_EPENDING If no connection is available immediately. When a new
- * connection arrives, the callback will be called.
- * - non-zero which indicates the appropriate error code.
- */
-PJ_DECL(pj_status_t) pj_ioqueue_accept( pj_ioqueue_key_t *key,
- pj_ioqueue_op_key_t *op_key,
- pj_sock_t *sock,
- pj_sockaddr_t *local,
- pj_sockaddr_t *remote,
- int *addrlen );
-
-/**
- * Initiate non-blocking socket connect. If the socket can NOT be connected
- * immediately, asynchronous connect() will be scheduled and caller will be
- * notified via completion callback when it calls pj_ioqueue_poll(). If
- * socket is connected immediately, the function returns PJ_SUCCESS and
- * completion callback WILL NOT be called.
- *
- * @param key The key associated with TCP socket
- * @param addr The remote address.
- * @param addrlen The remote address length.
- *
- * @return
- * - PJ_SUCCESS If socket is connected immediately. In this case, the
- * completion callback WILL NOT be called.
- * - PJ_EPENDING If operation is queued, or
- * - non-zero Indicates the error code.
- */
-PJ_DECL(pj_status_t) pj_ioqueue_connect( pj_ioqueue_key_t *key,
- const pj_sockaddr_t *addr,
- int addrlen );
-
-#endif /* PJ_HAS_TCP */
-
-/**
- * Poll the I/O Queue for completed events.
- *
- * @param ioque the I/O Queue.
- * @param timeout polling timeout, or NULL if the thread wishes to wait
- * indefinetely for the event.
- *
- * @return
- * - zero if timed out (no event).
- * - (<0) if error occured during polling. Callback will NOT be called.
- * - (>1) to indicate numbers of events. Callbacks have been called.
- */
-PJ_DECL(int) pj_ioqueue_poll( pj_ioqueue_t *ioque,
- const pj_time_val *timeout);
-
-
-/**
- * Instruct the I/O Queue to read from the specified handle. This function
- * returns immediately (i.e. non-blocking) regardless whether some data has
- * been transfered. If the operation can't complete immediately, caller will
- * be notified about the completion when it calls pj_ioqueue_poll(). If data
- * is immediately available, the function will return PJ_SUCCESS and the
- * callback WILL NOT be called.
- *
- * @param key The key that uniquely identifies the handle.
- * @param op_key An operation specific key to be associated with the
- * pending operation, so that application can keep track of
- * which operation has been completed when the callback is
- * called. Caller must make sure that this key remains
- * valid until the function completes.
- * @param buffer The buffer to hold the read data. The caller MUST make sure
- * that this buffer remain valid until the framework completes
- * reading the handle.
- * @param length On input, it specifies the size of the buffer. If data is
- * available to be read immediately, the function returns
- * PJ_SUCCESS and this argument will be filled with the
- * amount of data read. If the function is pending, caller
- * will be notified about the amount of data read in the
- * callback. This parameter can point to local variable in
- * caller's stack and doesn't have to remain valid for the
- * duration of pending operation.
- * @param flags Recv flag.
- *
- * @return
- * - PJ_SUCCESS If immediate data has been received in the buffer. In this
- * case, the callback WILL NOT be called.
- * - PJ_EPENDING If the operation has been queued, and the callback will be
- * called when data has been received.
- * - non-zero The return value indicates the error code.
- */
-PJ_DECL(pj_status_t) pj_ioqueue_recv( pj_ioqueue_key_t *key,
- pj_ioqueue_op_key_t *op_key,
- void *buffer,
- pj_ssize_t *length,
- unsigned flags );
-
-/**
- * This function behaves similarly as #pj_ioqueue_recv(), except that it is
- * normally called for socket, and the remote address will also be returned
- * along with the data. Caller MUST make sure that both buffer and addr
- * remain valid until the framework completes reading the data.
- *
- * @param key The key that uniquely identifies the handle.
- * @param op_key An operation specific key to be associated with the
- * pending operation, so that application can keep track of
- * which operation has been completed when the callback is
- * called.
- * @param buffer The buffer to hold the read data. The caller MUST make sure
- * that this buffer remain valid until the framework completes
- * reading the handle.
- * @param length On input, it specifies the size of the buffer. If data is
- * available to be read immediately, the function returns
- * PJ_SUCCESS and this argument will be filled with the
- * amount of data read. If the function is pending, caller
- * will be notified about the amount of data read in the
- * callback. This parameter can point to local variable in
- * caller's stack and doesn't have to remain valid for the
- * duration of pending operation.
- * @param flags Recv flag.
- * @param addr Optional Pointer to buffer to receive the address.
- * @param addrlen On input, specifies the length of the address buffer.
- * On output, it will be filled with the actual length of
- * the address. This argument can be NULL if \c addr is not
- * specified.
- *
- * @return
- * - PJ_SUCCESS If immediate data has been received. In this case, the
- * callback must have been called before this function
- * returns, and no pending operation is scheduled.
- * - PJ_EPENDING If the operation has been queued.
- * - non-zero The return value indicates the error code.
- */
-PJ_DECL(pj_status_t) pj_ioqueue_recvfrom( pj_ioqueue_key_t *key,
- pj_ioqueue_op_key_t *op_key,
- void *buffer,
- pj_ssize_t *length,
- unsigned flags,
- pj_sockaddr_t *addr,
- int *addrlen);
-
-/**
- * Instruct the I/O Queue to write to the handle. This function will return
- * immediately (i.e. non-blocking) regardless whether some data has been
- * transfered. If the function can't complete immediately, the caller will
- * be notified about the completion when it calls pj_ioqueue_poll(). If
- * operation completes immediately and data has been transfered, the function
- * returns PJ_SUCCESS and the callback will NOT be called.
- *
- * @param key The key that identifies the handle.
- * @param op_key An operation specific key to be associated with the
- * pending operation, so that application can keep track of
- * which operation has been completed when the callback is
- * called.
- * @param data The data to send. Caller MUST make sure that this buffer
- * remains valid until the write operation completes.
- * @param length On input, it specifies the length of data to send. When
- * data was sent immediately, this function returns PJ_SUCCESS
- * and this parameter contains the length of data sent. If
- * data can not be sent immediately, an asynchronous operation
- * is scheduled and caller will be notified via callback the
- * number of bytes sent. This parameter can point to local
- * variable on caller's stack and doesn't have to remain
- * valid until the operation has completed.
- * @param flags Send flags.
- *
- * @return
- * - PJ_SUCCESS If data was immediately transfered. In this case, no
- * pending operation has been scheduled and the callback
- * WILL NOT be called.
- * - PJ_EPENDING If the operation has been queued. Once data base been
- * transfered, the callback will be called.
- * - non-zero The return value indicates the error code.
- */
-PJ_DECL(pj_status_t) pj_ioqueue_send( pj_ioqueue_key_t *key,
- pj_ioqueue_op_key_t *op_key,
- const void *data,
- pj_ssize_t *length,
- unsigned flags );
-
-
-/**
- * Instruct the I/O Queue to write to the handle. This function will return
- * immediately (i.e. non-blocking) regardless whether some data has been
- * transfered. If the function can't complete immediately, the caller will
- * be notified about the completion when it calls pj_ioqueue_poll(). If
- * operation completes immediately and data has been transfered, the function
- * returns PJ_SUCCESS and the callback will NOT be called.
- *
- * @param key the key that identifies the handle.
- * @param op_key An operation specific key to be associated with the
- * pending operation, so that application can keep track of
- * which operation has been completed when the callback is
- * called.
- * @param data the data to send. Caller MUST make sure that this buffer
- * remains valid until the write operation completes.
- * @param length On input, it specifies the length of data to send. When
- * data was sent immediately, this function returns PJ_SUCCESS
- * and this parameter contains the length of data sent. If
- * data can not be sent immediately, an asynchronous operation
- * is scheduled and caller will be notified via callback the
- * number of bytes sent. This parameter can point to local
- * variable on caller's stack and doesn't have to remain
- * valid until the operation has completed.
- * @param flags send flags.
- * @param addr Optional remote address.
- * @param addrlen Remote address length, \c addr is specified.
- *
- * @return
- * - PJ_SUCCESS If data was immediately written.
- * - PJ_EPENDING If the operation has been queued.
- * - non-zero The return value indicates the error code.
- */
-PJ_DECL(pj_status_t) pj_ioqueue_sendto( pj_ioqueue_key_t *key,
- pj_ioqueue_op_key_t *op_key,
- const void *data,
- pj_ssize_t *length,
- unsigned flags,
- const pj_sockaddr_t *addr,
- int addrlen);
-
-
-/**
- * !}
- */
-
-PJ_END_DECL
-
-#endif /* __PJ_IOQUEUE_H__ */
-
+
+#if defined(PJ_HAS_TCP) && PJ_HAS_TCP != 0
+/**
+ * Instruct I/O Queue to accept incoming connection on the specified
+ * listening socket. This function will return immediately (i.e. non-blocking)
+ * regardless whether a connection is immediately available. If the function
+ * can't complete immediately, the caller will be notified about the incoming
+ * connection when it calls pj_ioqueue_poll(). If a new connection is
+ * immediately available, the function returns PJ_SUCCESS with the new
+ * connection; in this case, the callback WILL NOT be called.
+ *
+ * @param key The key which registered to the server socket.
+ * @param op_key An operation specific key to be associated with the
+ * pending operation, so that application can keep track of
+ * which operation has been completed when the callback is
+ * called.
+ * @param new_sock Argument which contain pointer to receive the new socket
+ * for the incoming connection.
+ * @param local Optional argument which contain pointer to variable to
+ * receive local address.
+ * @param remote Optional argument which contain pointer to variable to
+ * receive the remote address.
+ * @param addrlen On input, contains the length of the buffer for the
+ * address, and on output, contains the actual length of the
+ * address. This argument is optional.
+ * @return
+ * - PJ_SUCCESS When connection is available immediately, and the
+ * parameters will be updated to contain information about
+ * the new connection. In this case, a completion callback
+ * WILL NOT be called.
+ * - PJ_EPENDING If no connection is available immediately. When a new
+ * connection arrives, the callback will be called.
+ * - non-zero which indicates the appropriate error code.
+ */
+PJ_DECL(pj_status_t) pj_ioqueue_accept( pj_ioqueue_key_t *key,
+ pj_ioqueue_op_key_t *op_key,
+ pj_sock_t *sock,
+ pj_sockaddr_t *local,
+ pj_sockaddr_t *remote,
+ int *addrlen );
+
+/**
+ * Initiate non-blocking socket connect. If the socket can NOT be connected
+ * immediately, asynchronous connect() will be scheduled and caller will be
+ * notified via completion callback when it calls pj_ioqueue_poll(). If
+ * socket is connected immediately, the function returns PJ_SUCCESS and
+ * completion callback WILL NOT be called.
+ *
+ * @param key The key associated with TCP socket
+ * @param addr The remote address.
+ * @param addrlen The remote address length.
+ *
+ * @return
+ * - PJ_SUCCESS If socket is connected immediately. In this case, the
+ * completion callback WILL NOT be called.
+ * - PJ_EPENDING If operation is queued, or
+ * - non-zero Indicates the error code.
+ */
+PJ_DECL(pj_status_t) pj_ioqueue_connect( pj_ioqueue_key_t *key,
+ const pj_sockaddr_t *addr,
+ int addrlen );
+
+#endif /* PJ_HAS_TCP */
+
+/**
+ * Poll the I/O Queue for completed events.
+ *
+ * @param ioque the I/O Queue.
+ * @param timeout polling timeout, or NULL if the thread wishes to wait
+ * indefinetely for the event.
+ *
+ * @return
+ * - zero if timed out (no event).
+ * - (<0) if error occured during polling. Callback will NOT be called.
+ * - (>1) to indicate numbers of events. Callbacks have been called.
+ */
+PJ_DECL(int) pj_ioqueue_poll( pj_ioqueue_t *ioque,
+ const pj_time_val *timeout);
+
+
+/**
+ * Instruct the I/O Queue to read from the specified handle. This function
+ * returns immediately (i.e. non-blocking) regardless whether some data has
+ * been transfered. If the operation can't complete immediately, caller will
+ * be notified about the completion when it calls pj_ioqueue_poll(). If data
+ * is immediately available, the function will return PJ_SUCCESS and the
+ * callback WILL NOT be called.
+ *
+ * @param key The key that uniquely identifies the handle.
+ * @param op_key An operation specific key to be associated with the
+ * pending operation, so that application can keep track of
+ * which operation has been completed when the callback is
+ * called. Caller must make sure that this key remains
+ * valid until the function completes.
+ * @param buffer The buffer to hold the read data. The caller MUST make sure
+ * that this buffer remain valid until the framework completes
+ * reading the handle.
+ * @param length On input, it specifies the size of the buffer. If data is
+ * available to be read immediately, the function returns
+ * PJ_SUCCESS and this argument will be filled with the
+ * amount of data read. If the function is pending, caller
+ * will be notified about the amount of data read in the
+ * callback. This parameter can point to local variable in
+ * caller's stack and doesn't have to remain valid for the
+ * duration of pending operation.
+ * @param flags Recv flag.
+ *
+ * @return
+ * - PJ_SUCCESS If immediate data has been received in the buffer. In this
+ * case, the callback WILL NOT be called.
+ * - PJ_EPENDING If the operation has been queued, and the callback will be
+ * called when data has been received.
+ * - non-zero The return value indicates the error code.
+ */
+PJ_DECL(pj_status_t) pj_ioqueue_recv( pj_ioqueue_key_t *key,
+ pj_ioqueue_op_key_t *op_key,
+ void *buffer,
+ pj_ssize_t *length,
+ unsigned flags );
+
+/**
+ * This function behaves similarly as #pj_ioqueue_recv(), except that it is
+ * normally called for socket, and the remote address will also be returned
+ * along with the data. Caller MUST make sure that both buffer and addr
+ * remain valid until the framework completes reading the data.
+ *
+ * @param key The key that uniquely identifies the handle.
+ * @param op_key An operation specific key to be associated with the
+ * pending operation, so that application can keep track of
+ * which operation has been completed when the callback is
+ * called.
+ * @param buffer The buffer to hold the read data. The caller MUST make sure
+ * that this buffer remain valid until the framework completes
+ * reading the handle.
+ * @param length On input, it specifies the size of the buffer. If data is
+ * available to be read immediately, the function returns
+ * PJ_SUCCESS and this argument will be filled with the
+ * amount of data read. If the function is pending, caller
+ * will be notified about the amount of data read in the
+ * callback. This parameter can point to local variable in
+ * caller's stack and doesn't have to remain valid for the
+ * duration of pending operation.
+ * @param flags Recv flag.
+ * @param addr Optional Pointer to buffer to receive the address.
+ * @param addrlen On input, specifies the length of the address buffer.
+ * On output, it will be filled with the actual length of
+ * the address. This argument can be NULL if \c addr is not
+ * specified.
+ *
+ * @return
+ * - PJ_SUCCESS If immediate data has been received. In this case, the
+ * callback must have been called before this function
+ * returns, and no pending operation is scheduled.
+ * - PJ_EPENDING If the operation has been queued.
+ * - non-zero The return value indicates the error code.
+ */
+PJ_DECL(pj_status_t) pj_ioqueue_recvfrom( pj_ioqueue_key_t *key,
+ pj_ioqueue_op_key_t *op_key,
+ void *buffer,
+ pj_ssize_t *length,
+ unsigned flags,
+ pj_sockaddr_t *addr,
+ int *addrlen);
+
+/**
+ * Instruct the I/O Queue to write to the handle. This function will return
+ * immediately (i.e. non-blocking) regardless whether some data has been
+ * transfered. If the function can't complete immediately, the caller will
+ * be notified about the completion when it calls pj_ioqueue_poll(). If
+ * operation completes immediately and data has been transfered, the function
+ * returns PJ_SUCCESS and the callback will NOT be called.
+ *
+ * @param key The key that identifies the handle.
+ * @param op_key An operation specific key to be associated with the
+ * pending operation, so that application can keep track of
+ * which operation has been completed when the callback is
+ * called.
+ * @param data The data to send. Caller MUST make sure that this buffer
+ * remains valid until the write operation completes.
+ * @param length On input, it specifies the length of data to send. When
+ * data was sent immediately, this function returns PJ_SUCCESS
+ * and this parameter contains the length of data sent. If
+ * data can not be sent immediately, an asynchronous operation
+ * is scheduled and caller will be notified via callback the
+ * number of bytes sent. This parameter can point to local
+ * variable on caller's stack and doesn't have to remain
+ * valid until the operation has completed.
+ * @param flags Send flags.
+ *
+ * @return
+ * - PJ_SUCCESS If data was immediately transfered. In this case, no
+ * pending operation has been scheduled and the callback
+ * WILL NOT be called.
+ * - PJ_EPENDING If the operation has been queued. Once data base been
+ * transfered, the callback will be called.
+ * - non-zero The return value indicates the error code.
+ */
+PJ_DECL(pj_status_t) pj_ioqueue_send( pj_ioqueue_key_t *key,
+ pj_ioqueue_op_key_t *op_key,
+ const void *data,
+ pj_ssize_t *length,
+ unsigned flags );
+
+
+/**
+ * Instruct the I/O Queue to write to the handle. This function will return
+ * immediately (i.e. non-blocking) regardless whether some data has been
+ * transfered. If the function can't complete immediately, the caller will
+ * be notified about the completion when it calls pj_ioqueue_poll(). If
+ * operation completes immediately and data has been transfered, the function
+ * returns PJ_SUCCESS and the callback will NOT be called.
+ *
+ * @param key the key that identifies the handle.
+ * @param op_key An operation specific key to be associated with the
+ * pending operation, so that application can keep track of
+ * which operation has been completed when the callback is
+ * called.
+ * @param data the data to send. Caller MUST make sure that this buffer
+ * remains valid until the write operation completes.
+ * @param length On input, it specifies the length of data to send. When
+ * data was sent immediately, this function returns PJ_SUCCESS
+ * and this parameter contains the length of data sent. If
+ * data can not be sent immediately, an asynchronous operation
+ * is scheduled and caller will be notified via callback the
+ * number of bytes sent. This parameter can point to local
+ * variable on caller's stack and doesn't have to remain
+ * valid until the operation has completed.
+ * @param flags send flags.
+ * @param addr Optional remote address.
+ * @param addrlen Remote address length, \c addr is specified.
+ *
+ * @return
+ * - PJ_SUCCESS If data was immediately written.
+ * - PJ_EPENDING If the operation has been queued.
+ * - non-zero The return value indicates the error code.
+ */
+PJ_DECL(pj_status_t) pj_ioqueue_sendto( pj_ioqueue_key_t *key,
+ pj_ioqueue_op_key_t *op_key,
+ const void *data,
+ pj_ssize_t *length,
+ unsigned flags,
+ const pj_sockaddr_t *addr,
+ int addrlen);
+
+
+/**
+ * !}
+ */
+
+PJ_END_DECL
+
+#endif /* __PJ_IOQUEUE_H__ */
+
diff --git a/pjlib/include/pj/list.h b/pjlib/include/pj/list.h
index f6662eb..72dd009 100644
--- a/pjlib/include/pj/list.h
+++ b/pjlib/include/pj/list.h
@@ -1,222 +1,243 @@
-/* $Id$
- *
- */
-
-#ifndef __PJ_LIST_H__
-#define __PJ_LIST_H__
-
-/**
- * @file list.h
- * @brief Linked List data structure.
- */
-
-#include <pj/types.h>
-
-PJ_BEGIN_DECL
-
-/*
- * @defgroup PJ_DS Data Structure.
- * @ingroup PJ
- */
-
-/**
- * @defgroup PJ_LIST Linked List
- * @ingroup PJ_DS
- * @{
- *
- * List in PJLIB is implemented as doubly-linked list, and it won't require
- * dynamic memory allocation (just as all PJLIB data structures). The list here
- * should be viewed more like a low level C list instead of high level C++ list
- * (which normally are easier to use but require dynamic memory allocations),
- * therefore all caveats with C list apply here too (such as you can NOT put
- * a node in more than one lists).
- *
- * \section pj_list_example_sec Examples
- *
- * See below for examples on how to manipulate linked list:
- * - @ref page_pjlib_samples_list_c
- * - @ref page_pjlib_list_test
- */
-
-
-/**
- * Use this macro in the start of the structure declaration to declare that
- * the structure can be used in the linked list operation. This macro simply
- * declares additional member @a prev and @a next to the structure.
- * @hideinitializer
- */
+/* $Id$
+ *
+ */
+/*
+ * PJLIB - PJ Foundation Library
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ * Benny Prijono <bennylp@bulukucing.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef __PJ_LIST_H__
+#define __PJ_LIST_H__
+
+/**
+ * @file list.h
+ * @brief Linked List data structure.
+ */
+
+#include <pj/types.h>
+
+PJ_BEGIN_DECL
+
+/*
+ * @defgroup PJ_DS Data Structure.
+ * @ingroup PJ
+ */
+
+/**
+ * @defgroup PJ_LIST Linked List
+ * @ingroup PJ_DS
+ * @{
+ *
+ * List in PJLIB is implemented as doubly-linked list, and it won't require
+ * dynamic memory allocation (just as all PJLIB data structures). The list here
+ * should be viewed more like a low level C list instead of high level C++ list
+ * (which normally are easier to use but require dynamic memory allocations),
+ * therefore all caveats with C list apply here too (such as you can NOT put
+ * a node in more than one lists).
+ *
+ * \section pj_list_example_sec Examples
+ *
+ * See below for examples on how to manipulate linked list:
+ * - @ref page_pjlib_samples_list_c
+ * - @ref page_pjlib_list_test
+ */
+
+
+/**
+ * Use this macro in the start of the structure declaration to declare that
+ * the structure can be used in the linked list operation. This macro simply
+ * declares additional member @a prev and @a next to the structure.
+ * @hideinitializer
+ */
#define PJ_DECL_LIST_MEMBER(type) \
/** List @a prev. */ \
type *prev; \
- /** List @a next. */ \
- type *next
-
-
-/**
- * This structure describes generic list node and list. The owner of this list
- * must initialize the 'value' member to an appropriate value (typically the
- * owner itself).
- */
-struct pj_list
-{
- PJ_DECL_LIST_MEMBER(void);
-};
-
-
-/**
- * Initialize the list.
- * Initially, the list will have no member, and function pj_list_empty() will
- * always return nonzero (which indicates TRUE) for the newly initialized
- * list.
- *
- * @param node The list head.
- */
-PJ_INLINE(void) pj_list_init(pj_list_type * node)
-{
- ((pj_list*)node)->next = ((pj_list*)node)->prev = node;
-}
-
-
-/**
- * Check that the list is empty.
- *
- * @param node The list head.
- *
- * @return Non-zero if the list is not-empty, or zero if it is empty.
- *
- */
-PJ_INLINE(int) pj_list_empty(const pj_list_type * node)
-{
- return ((pj_list*)node)->next == node;
-}
-
-
-/**
- * Insert the node to the list before the specified element position.
- *
- * @param pos The element to which the node will be inserted before.
- * @param node The element to be inserted.
- *
- * @return void.
- */
-PJ_IDECL(void) pj_list_insert_before(pj_list_type *pos, pj_list_type *node);
-
-
-/**
- * Inserts all nodes in \a nodes to the target list.
- *
- * @param lst The target list.
- * @param nodes Nodes list.
- */
-PJ_IDECL(void) pj_list_insert_nodes_before(pj_list_type *lst,
- pj_list_type *nodes);
-
-/**
- * Insert a node to the list after the specified element position.
- *
- * @param pos The element in the list which will precede the inserted
- * element.
- * @param node The element to be inserted after the position element.
- *
- * @return void.
- */
-PJ_IDECL(void) pj_list_insert_after(pj_list_type *pos, pj_list_type *node);
-
-/**
- * Insert all nodes in \a nodes to the target list.
- *
- * @param lst The target list.
- * @param nodes Nodes list.
- */
-PJ_IDECL(void) pj_list_insert_nodes_after(pj_list_type *lst,
- pj_list_type *nodes);
-
-
-/**
- * Remove elements from the source list, and insert them to the destination
- * list. The elements of the source list will occupy the
- * front elements of the target list. Note that the node pointed by \a list2
- * itself is not considered as a node, but rather as the list descriptor, so
- * it will not be inserted to the \a list1. The elements to be inserted starts
- * at \a list2->next. If \a list2 is to be included in the operation, use
- * \a pj_list_insert_nodes_before.
- *
- * @param list1 The destination list.
- * @param list2 The source list.
- *
- * @return void.
- */
-PJ_IDECL(void) pj_list_merge_first(pj_list_type *list1, pj_list_type *list2);
-
-
-/**
- * Remove elements from the second list argument, and insert them to the list
- * in the first argument. The elements from the second list will be appended
- * to the first list. Note that the node pointed by \a list2
- * itself is not considered as a node, but rather as the list descriptor, so
- * it will not be inserted to the \a list1. The elements to be inserted starts
- * at \a list2->next. If \a list2 is to be included in the operation, use
- * \a pj_list_insert_nodes_before.
- *
- * @param list1 The element in the list which will precede the inserted
- * element.
- * @param list2 The element in the list to be inserted.
- *
- * @return void.
- */
-PJ_IDECL(void) pj_list_merge_last( pj_list_type *list1, pj_list_type *list2);
-
-
-/**
- * Erase the node from the list it currently belongs.
- *
- * @param node The element to be erased.
- */
-PJ_IDECL(void) pj_list_erase(pj_list_type *node);
-
-
-/**
- * Find node in the list.
- *
- * @param list The list head.
- * @param node The node element to be searched.
- *
- * @return The node itself if it is found in the list, or NULL if it is not
- * found in the list.
- */
-PJ_IDECL(pj_list_type*) pj_list_find_node(pj_list_type *list,
- pj_list_type *node);
-
-
-/**
- * Search the list for the specified value, using the specified comparison
- * function. This function iterates on nodes in the list, started with the
- * first node, and call the user supplied comparison function until the
- * comparison function returns ZERO.
- *
- * @param list The list head.
- * @param value The user defined value to be passed in the comparison
- * function
- * @param comp The comparison function, which should return ZERO to
- * indicate that the searched value is found.
- *
- * @return The first node that matched, or NULL if it is not found.
- */
-PJ_IDECL(pj_list_type*) pj_list_search(pj_list_type *list, void *value,
- int (*comp)(void *value,
- const pj_list_type *node)
- );
-
-
-/**
- * @}
- */
-
-#if PJ_FUNCTIONS_ARE_INLINED
-# include "list_i.h"
-#endif
-
-PJ_END_DECL
-
-#endif /* __PJ_LIST_H__ */
-
+ /** List @a next. */ \
+ type *next
+
+
+/**
+ * This structure describes generic list node and list. The owner of this list
+ * must initialize the 'value' member to an appropriate value (typically the
+ * owner itself).
+ */
+struct pj_list
+{
+ PJ_DECL_LIST_MEMBER(void);
+};
+
+
+/**
+ * Initialize the list.
+ * Initially, the list will have no member, and function pj_list_empty() will
+ * always return nonzero (which indicates TRUE) for the newly initialized
+ * list.
+ *
+ * @param node The list head.
+ */
+PJ_INLINE(void) pj_list_init(pj_list_type * node)
+{
+ ((pj_list*)node)->next = ((pj_list*)node)->prev = node;
+}
+
+
+/**
+ * Check that the list is empty.
+ *
+ * @param node The list head.
+ *
+ * @return Non-zero if the list is not-empty, or zero if it is empty.
+ *
+ */
+PJ_INLINE(int) pj_list_empty(const pj_list_type * node)
+{
+ return ((pj_list*)node)->next == node;
+}
+
+
+/**
+ * Insert the node to the list before the specified element position.
+ *
+ * @param pos The element to which the node will be inserted before.
+ * @param node The element to be inserted.
+ *
+ * @return void.
+ */
+PJ_IDECL(void) pj_list_insert_before(pj_list_type *pos, pj_list_type *node);
+
+
+/**
+ * Inserts all nodes in \a nodes to the target list.
+ *
+ * @param lst The target list.
+ * @param nodes Nodes list.
+ */
+PJ_IDECL(void) pj_list_insert_nodes_before(pj_list_type *lst,
+ pj_list_type *nodes);
+
+/**
+ * Insert a node to the list after the specified element position.
+ *
+ * @param pos The element in the list which will precede the inserted
+ * element.
+ * @param node The element to be inserted after the position element.
+ *
+ * @return void.
+ */
+PJ_IDECL(void) pj_list_insert_after(pj_list_type *pos, pj_list_type *node);
+
+/**
+ * Insert all nodes in \a nodes to the target list.
+ *
+ * @param lst The target list.
+ * @param nodes Nodes list.
+ */
+PJ_IDECL(void) pj_list_insert_nodes_after(pj_list_type *lst,
+ pj_list_type *nodes);
+
+
+/**
+ * Remove elements from the source list, and insert them to the destination
+ * list. The elements of the source list will occupy the
+ * front elements of the target list. Note that the node pointed by \a list2
+ * itself is not considered as a node, but rather as the list descriptor, so
+ * it will not be inserted to the \a list1. The elements to be inserted starts
+ * at \a list2->next. If \a list2 is to be included in the operation, use
+ * \a pj_list_insert_nodes_before.
+ *
+ * @param list1 The destination list.
+ * @param list2 The source list.
+ *
+ * @return void.
+ */
+PJ_IDECL(void) pj_list_merge_first(pj_list_type *list1, pj_list_type *list2);
+
+
+/**
+ * Remove elements from the second list argument, and insert them to the list
+ * in the first argument. The elements from the second list will be appended
+ * to the first list. Note that the node pointed by \a list2
+ * itself is not considered as a node, but rather as the list descriptor, so
+ * it will not be inserted to the \a list1. The elements to be inserted starts
+ * at \a list2->next. If \a list2 is to be included in the operation, use
+ * \a pj_list_insert_nodes_before.
+ *
+ * @param list1 The element in the list which will precede the inserted
+ * element.
+ * @param list2 The element in the list to be inserted.
+ *
+ * @return void.
+ */
+PJ_IDECL(void) pj_list_merge_last( pj_list_type *list1, pj_list_type *list2);
+
+
+/**
+ * Erase the node from the list it currently belongs.
+ *
+ * @param node The element to be erased.
+ */
+PJ_IDECL(void) pj_list_erase(pj_list_type *node);
+
+
+/**
+ * Find node in the list.
+ *
+ * @param list The list head.
+ * @param node The node element to be searched.
+ *
+ * @return The node itself if it is found in the list, or NULL if it is not
+ * found in the list.
+ */
+PJ_IDECL(pj_list_type*) pj_list_find_node(pj_list_type *list,
+ pj_list_type *node);
+
+
+/**
+ * Search the list for the specified value, using the specified comparison
+ * function. This function iterates on nodes in the list, started with the
+ * first node, and call the user supplied comparison function until the
+ * comparison function returns ZERO.
+ *
+ * @param list The list head.
+ * @param value The user defined value to be passed in the comparison
+ * function
+ * @param comp The comparison function, which should return ZERO to
+ * indicate that the searched value is found.
+ *
+ * @return The first node that matched, or NULL if it is not found.
+ */
+PJ_IDECL(pj_list_type*) pj_list_search(pj_list_type *list, void *value,
+ int (*comp)(void *value,
+ const pj_list_type *node)
+ );
+
+
+/**
+ * @}
+ */
+
+#if PJ_FUNCTIONS_ARE_INLINED
+# include "list_i.h"
+#endif
+
+PJ_END_DECL
+
+#endif /* __PJ_LIST_H__ */
+
diff --git a/pjlib/include/pj/list_i.h b/pjlib/include/pj/list_i.h
index 9ca6e1d..4f2fd00 100644
--- a/pjlib/include/pj/list_i.h
+++ b/pjlib/include/pj/list_i.h
@@ -1,103 +1,124 @@
-/* $Id$
- *
- */
-
-
-/* Internal */
-PJ_IDEF(void) pj_link_node(pj_list_type *prev, pj_list_type *next)
-{
- ((pj_list*)prev)->next = next;
- ((pj_list*)next)->prev = prev;
-}
-
-/*
-PJ_IDEF(void)
-pj_list_init(pj_list_type * node)
-{
- ((pj_list*)node)->next = ((pj_list*)node)->prev = node;
-}
-
-PJ_IDEF(int) pj_list_empty(const pj_list_type * node)
-{
- return ((pj_list*)node)->next == node;
-}
-*/
-
-PJ_IDEF(void)
-pj_list_insert_after(pj_list_type *pos, pj_list_type *node)
-{
- ((pj_list*)node)->prev = pos;
- ((pj_list*)node)->next = ((pj_list*)pos)->next;
- ((pj_list*) ((pj_list*)pos)->next) ->prev = node;
- ((pj_list*)pos)->next = node;
-}
-
-
-PJ_IDEF(void)
-pj_list_insert_before(pj_list_type *pos, pj_list_type *node)
-{
- pj_list_insert_after(((pj_list*)pos)->prev, node);
-}
-
-
-PJ_IDEF(void)
-pj_list_insert_nodes_after(pj_list_type *pos, pj_list_type *lst)
-{
- pj_list *lst_last = (pj_list *) ((pj_list*)lst)->prev;
- pj_list *pos_next = (pj_list *) ((pj_list*)pos)->next;
-
- pj_link_node(pos, lst);
- pj_link_node(lst_last, pos_next);
-}
-
-PJ_IDEF(void)
-pj_list_insert_nodes_before(pj_list_type *pos, pj_list_type *lst)
-{
- pj_list_insert_nodes_after(((pj_list*)pos)->prev, lst);
-}
-
-PJ_IDEF(void)
-pj_list_merge_last(pj_list_type *lst1, pj_list_type *lst2)
-{
- pj_link_node(((pj_list*)lst1)->prev, ((pj_list*)lst2)->next);
- pj_link_node(((pj_list*)lst2)->prev, lst1);
- pj_list_init(lst2);
-}
-
-PJ_IDEF(void)
-pj_list_merge_first(pj_list_type *lst1, pj_list_type *lst2)
-{
- pj_link_node(((pj_list*)lst2)->prev, ((pj_list*)lst1)->next);
- pj_link_node(((pj_list*)lst1), ((pj_list*)lst2)->next);
- pj_list_init(lst2);
-}
-
-PJ_IDEF(void)
-pj_list_erase(pj_list_type *node)
-{
- pj_link_node( ((pj_list*)node)->prev, ((pj_list*)node)->next);
-}
-
-
-PJ_IDEF(pj_list_type*)
-pj_list_find_node(pj_list_type *list, pj_list_type *node)
-{
- pj_list *p = (pj_list *) ((pj_list*)list)->next;
- while (p != list && p != node)
- p = (pj_list *) p->next;
-
- return p==node ? p : NULL;
-}
-
-
-PJ_IDEF(pj_list_type*)
-pj_list_search(pj_list_type *list, void *value,
- int (*comp)(void *value, const pj_list_type *node))
-{
- pj_list *p = (pj_list *) ((pj_list*)list)->next;
- while (p != list && (*comp)(value, p) != 0)
- p = (pj_list *) p->next;
-
- return p==list ? NULL : p;
-}
-
+/* $Id$
+ *
+ */
+/*
+ * PJLIB - PJ Foundation Library
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ * Benny Prijono <bennylp@bulukucing.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+
+/* Internal */
+PJ_IDEF(void) pj_link_node(pj_list_type *prev, pj_list_type *next)
+{
+ ((pj_list*)prev)->next = next;
+ ((pj_list*)next)->prev = prev;
+}
+
+/*
+PJ_IDEF(void)
+pj_list_init(pj_list_type * node)
+{
+ ((pj_list*)node)->next = ((pj_list*)node)->prev = node;
+}
+
+PJ_IDEF(int) pj_list_empty(const pj_list_type * node)
+{
+ return ((pj_list*)node)->next == node;
+}
+*/
+
+PJ_IDEF(void)
+pj_list_insert_after(pj_list_type *pos, pj_list_type *node)
+{
+ ((pj_list*)node)->prev = pos;
+ ((pj_list*)node)->next = ((pj_list*)pos)->next;
+ ((pj_list*) ((pj_list*)pos)->next) ->prev = node;
+ ((pj_list*)pos)->next = node;
+}
+
+
+PJ_IDEF(void)
+pj_list_insert_before(pj_list_type *pos, pj_list_type *node)
+{
+ pj_list_insert_after(((pj_list*)pos)->prev, node);
+}
+
+
+PJ_IDEF(void)
+pj_list_insert_nodes_after(pj_list_type *pos, pj_list_type *lst)
+{
+ pj_list *lst_last = (pj_list *) ((pj_list*)lst)->prev;
+ pj_list *pos_next = (pj_list *) ((pj_list*)pos)->next;
+
+ pj_link_node(pos, lst);
+ pj_link_node(lst_last, pos_next);
+}
+
+PJ_IDEF(void)
+pj_list_insert_nodes_before(pj_list_type *pos, pj_list_type *lst)
+{
+ pj_list_insert_nodes_after(((pj_list*)pos)->prev, lst);
+}
+
+PJ_IDEF(void)
+pj_list_merge_last(pj_list_type *lst1, pj_list_type *lst2)
+{
+ pj_link_node(((pj_list*)lst1)->prev, ((pj_list*)lst2)->next);
+ pj_link_node(((pj_list*)lst2)->prev, lst1);
+ pj_list_init(lst2);
+}
+
+PJ_IDEF(void)
+pj_list_merge_first(pj_list_type *lst1, pj_list_type *lst2)
+{
+ pj_link_node(((pj_list*)lst2)->prev, ((pj_list*)lst1)->next);
+ pj_link_node(((pj_list*)lst1), ((pj_list*)lst2)->next);
+ pj_list_init(lst2);
+}
+
+PJ_IDEF(void)
+pj_list_erase(pj_list_type *node)
+{
+ pj_link_node( ((pj_list*)node)->prev, ((pj_list*)node)->next);
+}
+
+
+PJ_IDEF(pj_list_type*)
+pj_list_find_node(pj_list_type *list, pj_list_type *node)
+{
+ pj_list *p = (pj_list *) ((pj_list*)list)->next;
+ while (p != list && p != node)
+ p = (pj_list *) p->next;
+
+ return p==node ? p : NULL;
+}
+
+
+PJ_IDEF(pj_list_type*)
+pj_list_search(pj_list_type *list, void *value,
+ int (*comp)(void *value, const pj_list_type *node))
+{
+ pj_list *p = (pj_list *) ((pj_list*)list)->next;
+ while (p != list && (*comp)(value, p) != 0)
+ p = (pj_list *) p->next;
+
+ return p==list ? NULL : p;
+}
+
diff --git a/pjlib/include/pj/lock.h b/pjlib/include/pj/lock.h
index 8c29204..e15eaf6 100644
--- a/pjlib/include/pj/lock.h
+++ b/pjlib/include/pj/lock.h
@@ -1,138 +1,159 @@
-/* $Id$
- *
- */
-#ifndef __PJ_LOCK_H__
-#define __PJ_LOCK_H__
-
-/**
- * @file lock.h
- * @brief Higher abstraction for locking objects.
- */
-#include <pj/types.h>
-
-PJ_BEGIN_DECL
-
-/**
- * @defgroup PJ_LOCK Lock Objects
- * @ingroup PJ_OS
- * @{
- *
- * <b>Lock Objects</b> are higher abstraction for different lock mechanisms.
- * It offers the same API for manipulating different lock types (e.g.
- * @ref PJ_MUTEX "mutex", @ref PJ_SEM "semaphores", or null locks).
- * Because Lock Objects have the same API for different types of lock
- * implementation, it can be passed around in function arguments. As the
- * result, it can be used to control locking policy for a particular
- * feature.
- */
-
-
-/**
- * Create simple, non recursive mutex lock object.
- *
- * @param pool Memory pool.
- * @param name Lock object's name.
- * @param lock Pointer to store the returned handle.
- *
- * @return PJ_SUCCESS or the appropriate error code.
- */
-PJ_DECL(pj_status_t) pj_lock_create_simple_mutex( pj_pool_t *pool,
- const char *name,
- pj_lock_t **lock );
-
-/**
- * Create recursive mutex lock object.
- *
- * @param pool Memory pool.
- * @param name Lock object's name.
- * @param lock Pointer to store the returned handle.
- *
- * @return PJ_SUCCESS or the appropriate error code.
- */
-PJ_DECL(pj_status_t) pj_lock_create_recursive_mutex( pj_pool_t *pool,
- const char *name,
- pj_lock_t **lock );
-
-
-/**
- * Create NULL mutex. A NULL mutex doesn't actually have any synchronization
- * object attached to it.
- *
- * @param pool Memory pool.
- * @param name Lock object's name.
- * @param lock Pointer to store the returned handle.
- *
- * @return PJ_SUCCESS or the appropriate error code.
- */
-PJ_DECL(pj_status_t) pj_lock_create_null_mutex( pj_pool_t *pool,
- const char *name,
- pj_lock_t **lock );
-
-
-#if defined(PJ_HAS_SEMAPHORE) && PJ_HAS_SEMAPHORE != 0
-/**
- * Create semaphore lock object.
- *
- * @param pool Memory pool.
- * @param name Lock object's name.
- * @param initial Initial value of the semaphore.
- * @param max Maximum value of the semaphore.
- * @param lock Pointer to store the returned handle.
- *
- * @return PJ_SUCCESS or the appropriate error code.
- */
-PJ_DECL(pj_status_t) pj_lock_create_semaphore( pj_pool_t *pool,
- const char *name,
- unsigned initial,
- unsigned max,
- pj_lock_t **lock );
-
-#endif /* PJ_HAS_SEMAPHORE */
-
-/**
- * Acquire lock on the specified lock object.
- *
- * @param lock The lock object.
- *
- * @return PJ_SUCCESS or the appropriate error code.
- */
-PJ_DECL(pj_status_t) pj_lock_acquire( pj_lock_t *lock );
-
-
-/**
- * Try to acquire lock on the specified lock object.
- *
- * @param lock The lock object.
- *
- * @return PJ_SUCCESS or the appropriate error code.
- */
-PJ_DECL(pj_status_t) pj_lock_tryacquire( pj_lock_t *lock );
-
-
-/**
- * Release lock on the specified lock object.
- *
- * @param lock The lock object.
- *
- * @return PJ_SUCCESS or the appropriate error code.
- */
-PJ_DECL(pj_status_t) pj_lock_release( pj_lock_t *lock );
-
-
-/**
- * Destroy the lock object.
- *
- * @param lock The lock object.
- *
- * @return PJ_SUCCESS or the appropriate error code.
- */
-PJ_DECL(pj_status_t) pj_lock_destroy( pj_lock_t *lock );
-
-
-/** @} */
-
-PJ_END_DECL
-
-
-#endif /* __PJ_LOCK_H__ */
-
+/* $Id$
+ *
+ */
+/*
+ * PJLIB - PJ Foundation Library
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ * Benny Prijono <bennylp@bulukucing.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#ifndef __PJ_LOCK_H__
+#define __PJ_LOCK_H__
+
+/**
+ * @file lock.h
+ * @brief Higher abstraction for locking objects.
+ */
+#include <pj/types.h>
+
+PJ_BEGIN_DECL
+
+/**
+ * @defgroup PJ_LOCK Lock Objects
+ * @ingroup PJ_OS
+ * @{
+ *
+ * <b>Lock Objects</b> are higher abstraction for different lock mechanisms.
+ * It offers the same API for manipulating different lock types (e.g.
+ * @ref PJ_MUTEX "mutex", @ref PJ_SEM "semaphores", or null locks).
+ * Because Lock Objects have the same API for different types of lock
+ * implementation, it can be passed around in function arguments. As the
+ * result, it can be used to control locking policy for a particular
+ * feature.
+ */
+
+
+/**
+ * Create simple, non recursive mutex lock object.
+ *
+ * @param pool Memory pool.
+ * @param name Lock object's name.
+ * @param lock Pointer to store the returned handle.
+ *
+ * @return PJ_SUCCESS or the appropriate error code.
+ */
+PJ_DECL(pj_status_t) pj_lock_create_simple_mutex( pj_pool_t *pool,
+ const char *name,
+ pj_lock_t **lock );
+
+/**
+ * Create recursive mutex lock object.
+ *
+ * @param pool Memory pool.
+ * @param name Lock object's name.
+ * @param lock Pointer to store the returned handle.
+ *
+ * @return PJ_SUCCESS or the appropriate error code.
+ */
+PJ_DECL(pj_status_t) pj_lock_create_recursive_mutex( pj_pool_t *pool,
+ const char *name,
+ pj_lock_t **lock );
+
+
+/**
+ * Create NULL mutex. A NULL mutex doesn't actually have any synchronization
+ * object attached to it.
+ *
+ * @param pool Memory pool.
+ * @param name Lock object's name.
+ * @param lock Pointer to store the returned handle.
+ *
+ * @return PJ_SUCCESS or the appropriate error code.
+ */
+PJ_DECL(pj_status_t) pj_lock_create_null_mutex( pj_pool_t *pool,
+ const char *name,
+ pj_lock_t **lock );
+
+
+#if defined(PJ_HAS_SEMAPHORE) && PJ_HAS_SEMAPHORE != 0
+/**
+ * Create semaphore lock object.
+ *
+ * @param pool Memory pool.
+ * @param name Lock object's name.
+ * @param initial Initial value of the semaphore.
+ * @param max Maximum value of the semaphore.
+ * @param lock Pointer to store the returned handle.
+ *
+ * @return PJ_SUCCESS or the appropriate error code.
+ */
+PJ_DECL(pj_status_t) pj_lock_create_semaphore( pj_pool_t *pool,
+ const char *name,
+ unsigned initial,
+ unsigned max,
+ pj_lock_t **lock );
+
+#endif /* PJ_HAS_SEMAPHORE */
+
+/**
+ * Acquire lock on the specified lock object.
+ *
+ * @param lock The lock object.
+ *
+ * @return PJ_SUCCESS or the appropriate error code.
+ */
+PJ_DECL(pj_status_t) pj_lock_acquire( pj_lock_t *lock );
+
+
+/**
+ * Try to acquire lock on the specified lock object.
+ *
+ * @param lock The lock object.
+ *
+ * @return PJ_SUCCESS or the appropriate error code.
+ */
+PJ_DECL(pj_status_t) pj_lock_tryacquire( pj_lock_t *lock );
+
+
+/**
+ * Release lock on the specified lock object.
+ *
+ * @param lock The lock object.
+ *
+ * @return PJ_SUCCESS or the appropriate error code.
+ */
+PJ_DECL(pj_status_t) pj_lock_release( pj_lock_t *lock );
+
+
+/**
+ * Destroy the lock object.
+ *
+ * @param lock The lock object.
+ *
+ * @return PJ_SUCCESS or the appropriate error code.
+ */
+PJ_DECL(pj_status_t) pj_lock_destroy( pj_lock_t *lock );
+
+
+/** @} */
+
+PJ_END_DECL
+
+
+#endif /* __PJ_LOCK_H__ */
+
diff --git a/pjlib/include/pj/log.h b/pjlib/include/pj/log.h
index 5dfe17c..6e89d72 100644
--- a/pjlib/include/pj/log.h
+++ b/pjlib/include/pj/log.h
@@ -1,306 +1,327 @@
-/* $Id$
- *
- */
-
-#ifndef __PJ_LOG_H__
-#define __PJ_LOG_H__
-
-/**
- * @file log.h
- * @brief Logging Utility.
- */
-
-#include <pj/types.h>
-
-
-PJ_BEGIN_DECL
-
-/**
- * @defgroup PJ_MISC Miscelaneous
- * @ingroup PJ
- */
-
-/**
- * @defgroup PJ_LOG Logging Facility
- * @ingroup PJ_MISC
- * @{
- *
- * The PJLIB logging facility is a configurable, flexible, and convenient
- * way to write logging or trace information.
- *
- * To write to the log, one uses construct like below:
- *
- * <pre>
- * ...
- * PJ_LOG(3, ("main.c", "Starting hello..."));
- * ...
- * PJ_LOG(3, ("main.c", "Hello world from process %d", pj_getpid()));
- * ...
- * </pre>
- *
- * In the above example, the number @b 3 controls the verbosity level of
- * the information (which means "information", by convention). The string
- * "main.c" specifies the source or sender of the message.
- *
- *
- * \section pj_log_quick_sample_sec Examples
- *
- * For examples, see:
- * - @ref page_pjlib_samples_log_c.
- *
- */
-
-/**
- * Log decoration flag, to be specified with #pj_log_set_decor().
- */
-enum pj_log_decoration
-{
- PJ_LOG_HAS_DAY_NAME = 1, /**< Include day name [default: no]. */
- PJ_LOG_HAS_YEAR = 2, /**< Include year digit [default: no] */
- PJ_LOG_HAS_MONTH = 4, /**< Include month [default: no] */
- PJ_LOG_HAS_DAY_OF_MON = 8, /**< Include day of month [default: no] */
- PJ_LOG_HAS_TIME = 16, /**< Include time [default: yes]. */
- PJ_LOG_HAS_MICRO_SEC = 32, /**< Include microseconds [yes] */
- PJ_LOG_HAS_SENDER = 64, /**< Include sender in the log [yes]. */
- PJ_LOG_HAS_NEWLINE = 128, /**< Terminate each call with newline [yes].*/
-};
-
-/**
- * Write log message.
- * This is the main macro used to write text to the logging backend.
- *
- * @param level The logging verbosity level. Lower number indicates higher
- * importance, with level zero indicates fatal error. Only
- * numeral argument is permitted (e.g. not variable).
- * @param arg Enclosed 'printf' like arguments, with the first
- * argument is the sender, the second argument is format
- * string and the following arguments are variable number of
- * arguments suitable for the format string.
- *
- * Sample:
- * \verbatim
- PJ_LOG(2, (__FILE__, "current value is %d", value));
- \endverbatim
- * @hideinitializer
- */
-#define PJ_LOG(level,arg) pj_log_wrapper_##level(arg)
-
-/**
- * Signature for function to be registered to the logging subsystem to
- * write the actual log message to some output device.
- *
- * @param level Log level.
- * @param data Log message.
- * @param len Message length.
- */
-typedef void pj_log_func(int level, const char *data, int len);
-
-/**
- * Default logging writer function used by front end logger function.
- * Application normally should NOT need to call this function, but
- * rather use the PJ_LOG macro.
- *
- * @param level Log level.
- * @param buffer Log message.
- * @param len Message length.
- */
-PJ_DECL(void) pj_log_write(int level, const char *buffer, int len);
-
-
-#if PJ_LOG_MAX_LEVEL >= 1
-
-/**
- * Change log output function. The front-end logging functions will call
- * this function to write the actual message to the desired device.
- * By default, the front-end functions use pj_log_write() to write
- * the messages, unless it's changed by calling this function.
- *
- * @param func The function that will be called to write the log
- * messages to the desired device.
- */
-PJ_DECL(void) pj_log_set_log_func( pj_log_func *func );
-
-/**
- * Get the current log output function that is used to write log messages.
- *
- * @return Current log output function.
- */
-PJ_DECL(pj_log_func*) pj_log_get_log_func(void);
-
-/**
- * Set maximum log level. Application can call this function to set
- * the desired level of verbosity of the logging messages. The bigger the
- * value, the more verbose the logging messages will be printed. However,
- * the maximum level of verbosity can not exceed compile time value of
- * PJ_LOG_MAX_LEVEL.
- *
- * @param level The maximum level of verbosity of the logging
- * messages (6=very detailed..1=error only, 0=disabled)
- */
-PJ_DECL(void) pj_log_set_level(int level);
-
-/**
- * Get current maximum log verbositylevel.
- *
- * @return Current log maximum level.
- */
-PJ_DECL(int) pj_log_get_level(void);
-
-/**
- * Set log decoration. The log decoration flag controls what are printed
- * to output device alongside the actual message. For example, application
- * can specify that date/time information should be displayed with each
- * log message.
- *
- * @param decor Bitmask combination of #pj_log_decoration to control
- * the layout of the log message.
- */
-PJ_DECL(void) pj_log_set_decor(unsigned decor);
-
-/**
- * Get current log decoration flag.
- *
- * @return Log decoration flag.
- */
-PJ_DECL(unsigned) pj_log_get_decor(void);
-
-
-#else /* #if PJ_LOG_MAX_LEVEL >= 1 */
-
-/**
- * Change log output function. The front-end logging functions will call
- * this function to write the actual message to the desired device.
- * By default, the front-end functions use pj_log_write() to write
- * the messages, unless it's changed by calling this function.
- *
- * @param func The function that will be called to write the log
- * messages to the desired device.
- */
-# define pj_log_set_log_func(func)
-
-/**
- * Set maximum log level. Application can call this function to set
- * the desired level of verbosity of the logging messages. The bigger the
- * value, the more verbose the logging messages will be printed. However,
- * the maximum level of verbosity can not exceed compile time value of
- * PJ_LOG_MAX_LEVEL.
- *
- * @param level The maximum level of verbosity of the logging
- * messages (6=very detailed..1=error only, 0=disabled)
- */
-# define pj_log_set_level(level)
-
-/**
- * Set log decoration. The log decoration flag controls what are printed
- * to output device alongside the actual message. For example, application
- * can specify that date/time information should be displayed with each
- * log message.
- *
- * @param decor Bitmask combination of #pj_log_decoration to control
- * the layout of the log message.
- */
-# define pj_log_set_decor(decor)
-
-#endif /* #if PJ_LOG_MAX_LEVEL >= 1 */
-
-/**
- * @}
- */
-
-///////////////////////////////////////////////////////////////////////////////
-/*
- * Log functions implementation prototypes.
- * These functions are called by PJ_LOG macros according to verbosity
- * level specified when calling the macro. Applications should not normally
- * need to call these functions directly.
- */
-
-/**
- * @def pj_log_wrapper_1(arg)
- * Internal function to write log with verbosity 1. Will evaluate to
- * empty expression if PJ_LOG_MAX_LEVEL is below 1.
- * @param arg Log expression.
- */
-#if PJ_LOG_MAX_LEVEL >= 1
- #define pj_log_wrapper_1(arg) pj_log_1 arg
- /** Internal function. */
- PJ_DECL(void) pj_log_1(const char *src, const char *format, ...);
-#else
- #define pj_log_wrapper_1(arg)
-#endif
-
-/**
- * @def pj_log_wrapper_2(arg)
- * Internal function to write log with verbosity 2. Will evaluate to
- * empty expression if PJ_LOG_MAX_LEVEL is below 2.
- * @param arg Log expression.
- */
-#if PJ_LOG_MAX_LEVEL >= 2
- #define pj_log_wrapper_2(arg) pj_log_2 arg
- /** Internal function. */
- PJ_DECL(void) pj_log_2(const char *src, const char *format, ...);
-#else
- #define pj_log_wrapper_2(arg)
-#endif
-
-/**
- * @def pj_log_wrapper_3(arg)
- * Internal function to write log with verbosity 3. Will evaluate to
- * empty expression if PJ_LOG_MAX_LEVEL is below 3.
- * @param arg Log expression.
- */
-#if PJ_LOG_MAX_LEVEL >= 3
- #define pj_log_wrapper_3(arg) pj_log_3 arg
- /** Internal function. */
- PJ_DECL(void) pj_log_3(const char *src, const char *format, ...);
-#else
- #define pj_log_wrapper_3(arg)
-#endif
-
-/**
- * @def pj_log_wrapper_4(arg)
- * Internal function to write log with verbosity 4. Will evaluate to
- * empty expression if PJ_LOG_MAX_LEVEL is below 4.
- * @param arg Log expression.
- */
-#if PJ_LOG_MAX_LEVEL >= 4
- #define pj_log_wrapper_4(arg) pj_log_4 arg
- /** Internal function. */
- PJ_DECL(void) pj_log_4(const char *src, const char *format, ...);
-#else
- #define pj_log_wrapper_4(arg)
-#endif
-
-/**
- * @def pj_log_wrapper_5(arg)
- * Internal function to write log with verbosity 5. Will evaluate to
- * empty expression if PJ_LOG_MAX_LEVEL is below 5.
- * @param arg Log expression.
- */
-#if PJ_LOG_MAX_LEVEL >= 5
- #define pj_log_wrapper_5(arg) pj_log_5 arg
- /** Internal function. */
- PJ_DECL(void) pj_log_5(const char *src, const char *format, ...);
-#else
- #define pj_log_wrapper_5(arg)
-#endif
-
-/**
- * @def pj_log_wrapper_6(arg)
- * Internal function to write log with verbosity 6. Will evaluate to
- * empty expression if PJ_LOG_MAX_LEVEL is below 6.
- * @param arg Log expression.
- */
-#if PJ_LOG_MAX_LEVEL >= 6
- #define pj_log_wrapper_6(arg) pj_log_6 arg
- /** Internal function. */
- PJ_DECL(void) pj_log_6(const char *src, const char *format, ...);
-#else
- #define pj_log_wrapper_6(arg)
-#endif
-
-
-PJ_END_DECL
-
-#endif /* __PJ_LOG_H__ */
-
+/* $Id$
+ *
+ */
+/*
+ * PJLIB - PJ Foundation Library
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ * Benny Prijono <bennylp@bulukucing.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef __PJ_LOG_H__
+#define __PJ_LOG_H__
+
+/**
+ * @file log.h
+ * @brief Logging Utility.
+ */
+
+#include <pj/types.h>
+
+
+PJ_BEGIN_DECL
+
+/**
+ * @defgroup PJ_MISC Miscelaneous
+ * @ingroup PJ
+ */
+
+/**
+ * @defgroup PJ_LOG Logging Facility
+ * @ingroup PJ_MISC
+ * @{
+ *
+ * The PJLIB logging facility is a configurable, flexible, and convenient
+ * way to write logging or trace information.
+ *
+ * To write to the log, one uses construct like below:
+ *
+ * <pre>
+ * ...
+ * PJ_LOG(3, ("main.c", "Starting hello..."));
+ * ...
+ * PJ_LOG(3, ("main.c", "Hello world from process %d", pj_getpid()));
+ * ...
+ * </pre>
+ *
+ * In the above example, the number @b 3 controls the verbosity level of
+ * the information (which means "information", by convention). The string
+ * "main.c" specifies the source or sender of the message.
+ *
+ *
+ * \section pj_log_quick_sample_sec Examples
+ *
+ * For examples, see:
+ * - @ref page_pjlib_samples_log_c.
+ *
+ */
+
+/**
+ * Log decoration flag, to be specified with #pj_log_set_decor().
+ */
+enum pj_log_decoration
+{
+ PJ_LOG_HAS_DAY_NAME = 1, /**< Include day name [default: no]. */
+ PJ_LOG_HAS_YEAR = 2, /**< Include year digit [default: no] */
+ PJ_LOG_HAS_MONTH = 4, /**< Include month [default: no] */
+ PJ_LOG_HAS_DAY_OF_MON = 8, /**< Include day of month [default: no] */
+ PJ_LOG_HAS_TIME = 16, /**< Include time [default: yes]. */
+ PJ_LOG_HAS_MICRO_SEC = 32, /**< Include microseconds [yes] */
+ PJ_LOG_HAS_SENDER = 64, /**< Include sender in the log [yes]. */
+ PJ_LOG_HAS_NEWLINE = 128, /**< Terminate each call with newline [yes].*/
+};
+
+/**
+ * Write log message.
+ * This is the main macro used to write text to the logging backend.
+ *
+ * @param level The logging verbosity level. Lower number indicates higher
+ * importance, with level zero indicates fatal error. Only
+ * numeral argument is permitted (e.g. not variable).
+ * @param arg Enclosed 'printf' like arguments, with the first
+ * argument is the sender, the second argument is format
+ * string and the following arguments are variable number of
+ * arguments suitable for the format string.
+ *
+ * Sample:
+ * \verbatim
+ PJ_LOG(2, (__FILE__, "current value is %d", value));
+ \endverbatim
+ * @hideinitializer
+ */
+#define PJ_LOG(level,arg) pj_log_wrapper_##level(arg)
+
+/**
+ * Signature for function to be registered to the logging subsystem to
+ * write the actual log message to some output device.
+ *
+ * @param level Log level.
+ * @param data Log message.
+ * @param len Message length.
+ */
+typedef void pj_log_func(int level, const char *data, int len);
+
+/**
+ * Default logging writer function used by front end logger function.
+ * Application normally should NOT need to call this function, but
+ * rather use the PJ_LOG macro.
+ *
+ * @param level Log level.
+ * @param buffer Log message.
+ * @param len Message length.
+ */
+PJ_DECL(void) pj_log_write(int level, const char *buffer, int len);
+
+
+#if PJ_LOG_MAX_LEVEL >= 1
+
+/**
+ * Change log output function. The front-end logging functions will call
+ * this function to write the actual message to the desired device.
+ * By default, the front-end functions use pj_log_write() to write
+ * the messages, unless it's changed by calling this function.
+ *
+ * @param func The function that will be called to write the log
+ * messages to the desired device.
+ */
+PJ_DECL(void) pj_log_set_log_func( pj_log_func *func );
+
+/**
+ * Get the current log output function that is used to write log messages.
+ *
+ * @return Current log output function.
+ */
+PJ_DECL(pj_log_func*) pj_log_get_log_func(void);
+
+/**
+ * Set maximum log level. Application can call this function to set
+ * the desired level of verbosity of the logging messages. The bigger the
+ * value, the more verbose the logging messages will be printed. However,
+ * the maximum level of verbosity can not exceed compile time value of
+ * PJ_LOG_MAX_LEVEL.
+ *
+ * @param level The maximum level of verbosity of the logging
+ * messages (6=very detailed..1=error only, 0=disabled)
+ */
+PJ_DECL(void) pj_log_set_level(int level);
+
+/**
+ * Get current maximum log verbositylevel.
+ *
+ * @return Current log maximum level.
+ */
+PJ_DECL(int) pj_log_get_level(void);
+
+/**
+ * Set log decoration. The log decoration flag controls what are printed
+ * to output device alongside the actual message. For example, application
+ * can specify that date/time information should be displayed with each
+ * log message.
+ *
+ * @param decor Bitmask combination of #pj_log_decoration to control
+ * the layout of the log message.
+ */
+PJ_DECL(void) pj_log_set_decor(unsigned decor);
+
+/**
+ * Get current log decoration flag.
+ *
+ * @return Log decoration flag.
+ */
+PJ_DECL(unsigned) pj_log_get_decor(void);
+
+
+#else /* #if PJ_LOG_MAX_LEVEL >= 1 */
+
+/**
+ * Change log output function. The front-end logging functions will call
+ * this function to write the actual message to the desired device.
+ * By default, the front-end functions use pj_log_write() to write
+ * the messages, unless it's changed by calling this function.
+ *
+ * @param func The function that will be called to write the log
+ * messages to the desired device.
+ */
+# define pj_log_set_log_func(func)
+
+/**
+ * Set maximum log level. Application can call this function to set
+ * the desired level of verbosity of the logging messages. The bigger the
+ * value, the more verbose the logging messages will be printed. However,
+ * the maximum level of verbosity can not exceed compile time value of
+ * PJ_LOG_MAX_LEVEL.
+ *
+ * @param level The maximum level of verbosity of the logging
+ * messages (6=very detailed..1=error only, 0=disabled)
+ */
+# define pj_log_set_level(level)
+
+/**
+ * Set log decoration. The log decoration flag controls what are printed
+ * to output device alongside the actual message. For example, application
+ * can specify that date/time information should be displayed with each
+ * log message.
+ *
+ * @param decor Bitmask combination of #pj_log_decoration to control
+ * the layout of the log message.
+ */
+# define pj_log_set_decor(decor)
+
+#endif /* #if PJ_LOG_MAX_LEVEL >= 1 */
+
+/**
+ * @}
+ */
+
+///////////////////////////////////////////////////////////////////////////////
+/*
+ * Log functions implementation prototypes.
+ * These functions are called by PJ_LOG macros according to verbosity
+ * level specified when calling the macro. Applications should not normally
+ * need to call these functions directly.
+ */
+
+/**
+ * @def pj_log_wrapper_1(arg)
+ * Internal function to write log with verbosity 1. Will evaluate to
+ * empty expression if PJ_LOG_MAX_LEVEL is below 1.
+ * @param arg Log expression.
+ */
+#if PJ_LOG_MAX_LEVEL >= 1
+ #define pj_log_wrapper_1(arg) pj_log_1 arg
+ /** Internal function. */
+ PJ_DECL(void) pj_log_1(const char *src, const char *format, ...);
+#else
+ #define pj_log_wrapper_1(arg)
+#endif
+
+/**
+ * @def pj_log_wrapper_2(arg)
+ * Internal function to write log with verbosity 2. Will evaluate to
+ * empty expression if PJ_LOG_MAX_LEVEL is below 2.
+ * @param arg Log expression.
+ */
+#if PJ_LOG_MAX_LEVEL >= 2
+ #define pj_log_wrapper_2(arg) pj_log_2 arg
+ /** Internal function. */
+ PJ_DECL(void) pj_log_2(const char *src, const char *format, ...);
+#else
+ #define pj_log_wrapper_2(arg)
+#endif
+
+/**
+ * @def pj_log_wrapper_3(arg)
+ * Internal function to write log with verbosity 3. Will evaluate to
+ * empty expression if PJ_LOG_MAX_LEVEL is below 3.
+ * @param arg Log expression.
+ */
+#if PJ_LOG_MAX_LEVEL >= 3
+ #define pj_log_wrapper_3(arg) pj_log_3 arg
+ /** Internal function. */
+ PJ_DECL(void) pj_log_3(const char *src, const char *format, ...);
+#else
+ #define pj_log_wrapper_3(arg)
+#endif
+
+/**
+ * @def pj_log_wrapper_4(arg)
+ * Internal function to write log with verbosity 4. Will evaluate to
+ * empty expression if PJ_LOG_MAX_LEVEL is below 4.
+ * @param arg Log expression.
+ */
+#if PJ_LOG_MAX_LEVEL >= 4
+ #define pj_log_wrapper_4(arg) pj_log_4 arg
+ /** Internal function. */
+ PJ_DECL(void) pj_log_4(const char *src, const char *format, ...);
+#else
+ #define pj_log_wrapper_4(arg)
+#endif
+
+/**
+ * @def pj_log_wrapper_5(arg)
+ * Internal function to write log with verbosity 5. Will evaluate to
+ * empty expression if PJ_LOG_MAX_LEVEL is below 5.
+ * @param arg Log expression.
+ */
+#if PJ_LOG_MAX_LEVEL >= 5
+ #define pj_log_wrapper_5(arg) pj_log_5 arg
+ /** Internal function. */
+ PJ_DECL(void) pj_log_5(const char *src, const char *format, ...);
+#else
+ #define pj_log_wrapper_5(arg)
+#endif
+
+/**
+ * @def pj_log_wrapper_6(arg)
+ * Internal function to write log with verbosity 6. Will evaluate to
+ * empty expression if PJ_LOG_MAX_LEVEL is below 6.
+ * @param arg Log expression.
+ */
+#if PJ_LOG_MAX_LEVEL >= 6
+ #define pj_log_wrapper_6(arg) pj_log_6 arg
+ /** Internal function. */
+ PJ_DECL(void) pj_log_6(const char *src, const char *format, ...);
+#else
+ #define pj_log_wrapper_6(arg)
+#endif
+
+
+PJ_END_DECL
+
+#endif /* __PJ_LOG_H__ */
+
diff --git a/pjlib/include/pj/os.h b/pjlib/include/pj/os.h
index a1fef6b..5e24450 100644
--- a/pjlib/include/pj/os.h
+++ b/pjlib/include/pj/os.h
@@ -1,329 +1,350 @@
-/* $Id$
- *
- */
-
-#ifndef __PJ_OS_H__
-#define __PJ_OS_H__
-
-/**
- * @file os.h
- * @brief OS dependent functions
- */
-#include <pj/types.h>
-
-PJ_BEGIN_DECL
-
-/**
- * @defgroup PJ_OS Operating System Dependent Functionality.
- * @ingroup PJ
- */
-
-
-///////////////////////////////////////////////////////////////////////////////
-/**
- * @defgroup PJ_THREAD Threads
- * @ingroup PJ_OS
- * @{
- * This module provides multithreading API.
- *
- * \section pj_thread_examples_sec Examples
- *
- * For examples, please see:
- * - \ref page_pjlib_thread_test
- * - \ref page_pjlib_sleep_test
- *
- */
-
-/**
- * Thread creation flags:
- * - PJ_THREAD_SUSPENDED: specify that the thread should be created suspended.
- */
-typedef enum pj_thread_create_flags
-{
- PJ_THREAD_SUSPENDED = 1
-} pj_thread_create_flags;
-
-
-/**
- * Specify this as \a stack_size argument in #pj_thread_create() to specify
- * that thread should use default stack size for the current platform.
- */
-#define PJ_THREAD_DEFAULT_STACK_SIZE 0
-
-/**
- * Type of thread entry function.
- */
-typedef int (PJ_THREAD_FUNC pj_thread_proc)(void*);
-
-/**
- * Size of thread struct.
- */
-#if !defined(PJ_THREAD_DESC_SIZE)
-# define PJ_THREAD_DESC_SIZE (16)
-#endif
-
-/**
- * Thread structure, to thread's state when the thread is created by external
- * or native API.
- */
-typedef long pj_thread_desc[PJ_THREAD_DESC_SIZE];
-
-/**
- * Get process ID.
- * @return process ID.
- */
-PJ_DECL(pj_uint32_t) pj_getpid(void);
-
-/**
- * Create a new thread.
- *
- * @param pool The memory pool from which the thread record
- * will be allocated from.
- * @param thread_name The optional name to be assigned to the thread.
- * @param proc Thread entry function.
- * @param arg Argument to be passed to the thread entry function.
- * @param stack_size The size of the stack for the new thread, or ZERO or
- * PJ_THREAD_DEFAULT_STACK_SIZE to let the
- * library choose the reasonable size for the stack.
- * For some systems, the stack will be allocated from
- * the pool, so the pool must have suitable capacity.
- * @param flags Flags for thread creation, which is bitmask combination
- * from enum pj_thread_create_flags.
- * @param thread Pointer to hold the newly created thread.
- *
- * @return PJ_SUCCESS on success, or the error code.
- */
-PJ_DECL(pj_status_t) pj_thread_create( pj_pool_t *pool,
- const char *thread_name,
- pj_thread_proc *proc,
- void *arg,
- pj_size_t stack_size,
- unsigned flags,
- pj_thread_t **thread );
-
-/**
- * Register a thread that was created by external or native API to PJLIB.
- * This function must be called in the context of the thread being registered.
- * When the thread is created by external function or API call,
- * it must be 'registered' to PJLIB using pj_thread_register(), so that it can
- * cooperate with PJLIB's framework. During registration, some data needs to
- * be maintained, and this data must remain available during the thread's
- * lifetime.
- *
- * @param thread_name The optional name to be assigned to the thread.
- * @param desc Thread descriptor, which must be available throughout
- * the lifetime of the thread.
- * @param thread Pointer to hold the created thread handle.
- *
- * @return PJ_SUCCESS on success, or the error code.
- */
-PJ_DECL(pj_status_t) pj_thread_register ( const char *thread_name,
- pj_thread_desc desc,
- pj_thread_t **thread);
-
-/**
- * Get thread name.
- *
- * @param thread The thread handle.
- *
- * @return Thread name as null terminated string.
- */
-PJ_DECL(const char*) pj_thread_get_name(pj_thread_t *thread);
-
-/**
- * Resume a suspended thread.
- *
- * @param thread The thread handle.
- *
- * @return zero on success.
- */
-PJ_DECL(pj_status_t) pj_thread_resume(pj_thread_t *thread);
-
-/**
- * Get the current thread.
- *
- * @return Thread handle of current thread.
- */
-PJ_DECL(pj_thread_t*) pj_thread_this(void);
-
-/**
- * Join thread.
- * This function will block the caller thread until the specified thread exits.
- *
- * @param thread The thread handle.
- *
- * @return zero on success.
- */
-PJ_DECL(pj_status_t) pj_thread_join(pj_thread_t *thread);
-
-
-/**
- * Destroy thread and release resources allocated for the thread.
- * However, the memory allocated for the pj_thread_t itself will only be released
- * when the pool used to create the thread is destroyed.
- *
- * @param thread The thread handle.
- *
- * @return zero on success.
- */
-PJ_DECL(pj_status_t) pj_thread_destroy(pj_thread_t *thread);
-
-
-/**
- * Put the current thread to sleep for the specified miliseconds.
- *
- * @param msec Miliseconds delay.
- *
- * @return zero if successfull.
- */
-PJ_DECL(pj_status_t) pj_thread_sleep(unsigned msec);
-
-/**
- * @def PJ_CHECK_STACK()
- * PJ_CHECK_STACK() macro is used to check the sanity of the stack.
- * The OS implementation may check that no stack overflow occurs, and
- * it also may collect statistic about stack usage.
- */
-#if defined(PJ_OS_HAS_CHECK_STACK) && PJ_OS_HAS_CHECK_STACK!=0
-
-# define PJ_CHECK_STACK() pj_thread_check_stack(__FILE__, __LINE__)
-
-/** @internal
- * The implementation of stack checking.
- */
-PJ_DECL(void) pj_thread_check_stack(const char *file, int line);
-
-/** @internal
- * Get maximum stack usage statistic.
- */
-PJ_DECL(pj_uint32_t) pj_thread_get_stack_max_usage(pj_thread_t *thread);
-
-/** @internal
- * Dump thread stack status.
- */
-PJ_DECL(pj_status_t) pj_thread_get_stack_info(pj_thread_t *thread,
- const char **file,
- int *line);
-#else
-
-# define PJ_CHECK_STACK()
-/** pj_thread_get_stack_max_usage() for the thread */
-# define pj_thread_get_stack_max_usage(thread) 0
-/** pj_thread_get_stack_info() for the thread */
-# define pj_thread_get_stack_info(thread,f,l) (*(f)="",*(l)=0)
-#endif /* PJ_OS_HAS_CHECK_STACK */
-
-/**
- * @}
- */
-
-///////////////////////////////////////////////////////////////////////////////
-/**
- * @defgroup PJ_TLS Thread Local Storage.
- * @ingroup PJ_OS
- * @{
- */
-
-/**
- * Allocate thread local storage index. The initial value of the variable at
- * the index is zero.
- *
- * @param index Pointer to hold the return value.
- * @return PJ_SUCCESS on success, or the error code.
- */
-PJ_DECL(pj_status_t) pj_thread_local_alloc(long *index);
-
-/**
- * Deallocate thread local variable.
- *
- * @param index The variable index.
- */
-PJ_DECL(void) pj_thread_local_free(long index);
-
-/**
- * Set the value of thread local variable.
- *
- * @param index The index of the variable.
- * @param value The value.
- */
-PJ_DECL(pj_status_t) pj_thread_local_set(long index, void *value);
-
-/**
- * Get the value of thread local variable.
- *
- * @param index The index of the variable.
- * @return The value.
- */
-PJ_DECL(void*) pj_thread_local_get(long index);
-
-
-/**
- * @}
- */
-
-
-///////////////////////////////////////////////////////////////////////////////
-/**
- * @defgroup PJ_ATOMIC Atomic Variables
- * @ingroup PJ_OS
- * @{
- *
- * This module provides API to manipulate atomic variables.
- *
- * \section pj_atomic_examples_sec Examples
- *
- * For some example codes, please see:
- * - @ref page_pjlib_atomic_test
- */
-
-
-/**
- * Create atomic variable.
- *
- * @param pool The pool.
- * @param initial The initial value of the atomic variable.
- * @param atomic Pointer to hold the atomic variable upon return.
- *
- * @return PJ_SUCCESS on success, or the error code.
- */
-PJ_DECL(pj_status_t) pj_atomic_create( pj_pool_t *pool,
- pj_atomic_value_t initial,
- pj_atomic_t **atomic );
-
-/**
- * Destroy atomic variable.
- *
- * @param atomic_var the atomic variable.
- *
- * @return PJ_SUCCESS if success.
- */
-PJ_DECL(pj_status_t) pj_atomic_destroy( pj_atomic_t *atomic_var );
-
-/**
- * Set the value of an atomic type, and return the previous value.
- *
- * @param atomic_var the atomic variable.
- * @param value value to be set to the variable.
- */
-PJ_DECL(void) pj_atomic_set( pj_atomic_t *atomic_var,
- pj_atomic_value_t value);
-
-/**
- * Get the value of an atomic type.
- *
- * @param atomic_var the atomic variable.
- *
- * @return the value of the atomic variable.
- */
-PJ_DECL(pj_atomic_value_t) pj_atomic_get(pj_atomic_t *atomic_var);
-
-/**
- * Increment the value of an atomic type.
- *
- * @param atomic_var the atomic variable.
- */
-PJ_DECL(void) pj_atomic_inc(pj_atomic_t *atomic_var);
-
+/* $Id$
+ *
+ */
+/*
+ * PJLIB - PJ Foundation Library
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ * Benny Prijono <bennylp@bulukucing.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef __PJ_OS_H__
+#define __PJ_OS_H__
+
+/**
+ * @file os.h
+ * @brief OS dependent functions
+ */
+#include <pj/types.h>
+
+PJ_BEGIN_DECL
+
+/**
+ * @defgroup PJ_OS Operating System Dependent Functionality.
+ * @ingroup PJ
+ */
+
+
+///////////////////////////////////////////////////////////////////////////////
+/**
+ * @defgroup PJ_THREAD Threads
+ * @ingroup PJ_OS
+ * @{
+ * This module provides multithreading API.
+ *
+ * \section pj_thread_examples_sec Examples
+ *
+ * For examples, please see:
+ * - \ref page_pjlib_thread_test
+ * - \ref page_pjlib_sleep_test
+ *
+ */
+
+/**
+ * Thread creation flags:
+ * - PJ_THREAD_SUSPENDED: specify that the thread should be created suspended.
+ */
+typedef enum pj_thread_create_flags
+{
+ PJ_THREAD_SUSPENDED = 1
+} pj_thread_create_flags;
+
+
+/**
+ * Specify this as \a stack_size argument in #pj_thread_create() to specify
+ * that thread should use default stack size for the current platform.
+ */
+#define PJ_THREAD_DEFAULT_STACK_SIZE 0
+
+/**
+ * Type of thread entry function.
+ */
+typedef int (PJ_THREAD_FUNC pj_thread_proc)(void*);
+
+/**
+ * Size of thread struct.
+ */
+#if !defined(PJ_THREAD_DESC_SIZE)
+# define PJ_THREAD_DESC_SIZE (16)
+#endif
+
+/**
+ * Thread structure, to thread's state when the thread is created by external
+ * or native API.
+ */
+typedef long pj_thread_desc[PJ_THREAD_DESC_SIZE];
+
+/**
+ * Get process ID.
+ * @return process ID.
+ */
+PJ_DECL(pj_uint32_t) pj_getpid(void);
+
+/**
+ * Create a new thread.
+ *
+ * @param pool The memory pool from which the thread record
+ * will be allocated from.
+ * @param thread_name The optional name to be assigned to the thread.
+ * @param proc Thread entry function.
+ * @param arg Argument to be passed to the thread entry function.
+ * @param stack_size The size of the stack for the new thread, or ZERO or
+ * PJ_THREAD_DEFAULT_STACK_SIZE to let the
+ * library choose the reasonable size for the stack.
+ * For some systems, the stack will be allocated from
+ * the pool, so the pool must have suitable capacity.
+ * @param flags Flags for thread creation, which is bitmask combination
+ * from enum pj_thread_create_flags.
+ * @param thread Pointer to hold the newly created thread.
+ *
+ * @return PJ_SUCCESS on success, or the error code.
+ */
+PJ_DECL(pj_status_t) pj_thread_create( pj_pool_t *pool,
+ const char *thread_name,
+ pj_thread_proc *proc,
+ void *arg,
+ pj_size_t stack_size,
+ unsigned flags,
+ pj_thread_t **thread );
+
+/**
+ * Register a thread that was created by external or native API to PJLIB.
+ * This function must be called in the context of the thread being registered.
+ * When the thread is created by external function or API call,
+ * it must be 'registered' to PJLIB using pj_thread_register(), so that it can
+ * cooperate with PJLIB's framework. During registration, some data needs to
+ * be maintained, and this data must remain available during the thread's
+ * lifetime.
+ *
+ * @param thread_name The optional name to be assigned to the thread.
+ * @param desc Thread descriptor, which must be available throughout
+ * the lifetime of the thread.
+ * @param thread Pointer to hold the created thread handle.
+ *
+ * @return PJ_SUCCESS on success, or the error code.
+ */
+PJ_DECL(pj_status_t) pj_thread_register ( const char *thread_name,
+ pj_thread_desc desc,
+ pj_thread_t **thread);
+
+/**
+ * Get thread name.
+ *
+ * @param thread The thread handle.
+ *
+ * @return Thread name as null terminated string.
+ */
+PJ_DECL(const char*) pj_thread_get_name(pj_thread_t *thread);
+
+/**
+ * Resume a suspended thread.
+ *
+ * @param thread The thread handle.
+ *
+ * @return zero on success.
+ */
+PJ_DECL(pj_status_t) pj_thread_resume(pj_thread_t *thread);
+
+/**
+ * Get the current thread.
+ *
+ * @return Thread handle of current thread.
+ */
+PJ_DECL(pj_thread_t*) pj_thread_this(void);
+
+/**
+ * Join thread.
+ * This function will block the caller thread until the specified thread exits.
+ *
+ * @param thread The thread handle.
+ *
+ * @return zero on success.
+ */
+PJ_DECL(pj_status_t) pj_thread_join(pj_thread_t *thread);
+
+
+/**
+ * Destroy thread and release resources allocated for the thread.
+ * However, the memory allocated for the pj_thread_t itself will only be released
+ * when the pool used to create the thread is destroyed.
+ *
+ * @param thread The thread handle.
+ *
+ * @return zero on success.
+ */
+PJ_DECL(pj_status_t) pj_thread_destroy(pj_thread_t *thread);
+
+
+/**
+ * Put the current thread to sleep for the specified miliseconds.
+ *
+ * @param msec Miliseconds delay.
+ *
+ * @return zero if successfull.
+ */
+PJ_DECL(pj_status_t) pj_thread_sleep(unsigned msec);
+
+/**
+ * @def PJ_CHECK_STACK()
+ * PJ_CHECK_STACK() macro is used to check the sanity of the stack.
+ * The OS implementation may check that no stack overflow occurs, and
+ * it also may collect statistic about stack usage.
+ */
+#if defined(PJ_OS_HAS_CHECK_STACK) && PJ_OS_HAS_CHECK_STACK!=0
+
+# define PJ_CHECK_STACK() pj_thread_check_stack(__FILE__, __LINE__)
+
+/** @internal
+ * The implementation of stack checking.
+ */
+PJ_DECL(void) pj_thread_check_stack(const char *file, int line);
+
+/** @internal
+ * Get maximum stack usage statistic.
+ */
+PJ_DECL(pj_uint32_t) pj_thread_get_stack_max_usage(pj_thread_t *thread);
+
+/** @internal
+ * Dump thread stack status.
+ */
+PJ_DECL(pj_status_t) pj_thread_get_stack_info(pj_thread_t *thread,
+ const char **file,
+ int *line);
+#else
+
+# define PJ_CHECK_STACK()
+/** pj_thread_get_stack_max_usage() for the thread */
+# define pj_thread_get_stack_max_usage(thread) 0
+/** pj_thread_get_stack_info() for the thread */
+# define pj_thread_get_stack_info(thread,f,l) (*(f)="",*(l)=0)
+#endif /* PJ_OS_HAS_CHECK_STACK */
+
+/**
+ * @}
+ */
+
+///////////////////////////////////////////////////////////////////////////////
+/**
+ * @defgroup PJ_TLS Thread Local Storage.
+ * @ingroup PJ_OS
+ * @{
+ */
+
+/**
+ * Allocate thread local storage index. The initial value of the variable at
+ * the index is zero.
+ *
+ * @param index Pointer to hold the return value.
+ * @return PJ_SUCCESS on success, or the error code.
+ */
+PJ_DECL(pj_status_t) pj_thread_local_alloc(long *index);
+
+/**
+ * Deallocate thread local variable.
+ *
+ * @param index The variable index.
+ */
+PJ_DECL(void) pj_thread_local_free(long index);
+
+/**
+ * Set the value of thread local variable.
+ *
+ * @param index The index of the variable.
+ * @param value The value.
+ */
+PJ_DECL(pj_status_t) pj_thread_local_set(long index, void *value);
+
+/**
+ * Get the value of thread local variable.
+ *
+ * @param index The index of the variable.
+ * @return The value.
+ */
+PJ_DECL(void*) pj_thread_local_get(long index);
+
+
+/**
+ * @}
+ */
+
+
+///////////////////////////////////////////////////////////////////////////////
+/**
+ * @defgroup PJ_ATOMIC Atomic Variables
+ * @ingroup PJ_OS
+ * @{
+ *
+ * This module provides API to manipulate atomic variables.
+ *
+ * \section pj_atomic_examples_sec Examples
+ *
+ * For some example codes, please see:
+ * - @ref page_pjlib_atomic_test
+ */
+
+
+/**
+ * Create atomic variable.
+ *
+ * @param pool The pool.
+ * @param initial The initial value of the atomic variable.
+ * @param atomic Pointer to hold the atomic variable upon return.
+ *
+ * @return PJ_SUCCESS on success, or the error code.
+ */
+PJ_DECL(pj_status_t) pj_atomic_create( pj_pool_t *pool,
+ pj_atomic_value_t initial,
+ pj_atomic_t **atomic );
+
+/**
+ * Destroy atomic variable.
+ *
+ * @param atomic_var the atomic variable.
+ *
+ * @return PJ_SUCCESS if success.
+ */
+PJ_DECL(pj_status_t) pj_atomic_destroy( pj_atomic_t *atomic_var );
+
+/**
+ * Set the value of an atomic type, and return the previous value.
+ *
+ * @param atomic_var the atomic variable.
+ * @param value value to be set to the variable.
+ */
+PJ_DECL(void) pj_atomic_set( pj_atomic_t *atomic_var,
+ pj_atomic_value_t value);
+
+/**
+ * Get the value of an atomic type.
+ *
+ * @param atomic_var the atomic variable.
+ *
+ * @return the value of the atomic variable.
+ */
+PJ_DECL(pj_atomic_value_t) pj_atomic_get(pj_atomic_t *atomic_var);
+
+/**
+ * Increment the value of an atomic type.
+ *
+ * @param atomic_var the atomic variable.
+ */
+PJ_DECL(void) pj_atomic_inc(pj_atomic_t *atomic_var);
+
/**
* Increment the value of an atomic type and get the result.
*
@@ -333,12 +354,12 @@
*/
PJ_DECL(pj_atomic_value_t) pj_atomic_inc_and_get(pj_atomic_t *atomic_var);
-/**
- * Decrement the value of an atomic type.
- *
- * @param atomic_var the atomic variable.
- */
-PJ_DECL(void) pj_atomic_dec(pj_atomic_t *atomic_var);
+/**
+ * Decrement the value of an atomic type.
+ *
+ * @param atomic_var the atomic variable.
+ */
+PJ_DECL(void) pj_atomic_dec(pj_atomic_t *atomic_var);
/**
* Decrement the value of an atomic type and get the result.
@@ -348,15 +369,15 @@
* @return The decremented value.
*/
PJ_DECL(pj_atomic_value_t) pj_atomic_dec_and_get(pj_atomic_t *atomic_var);
-
-/**
- * Add a value to an atomic type.
- *
- * @param atomic_var The atomic variable.
- * @param value Value to be added.
- */
-PJ_DECL(void) pj_atomic_add( pj_atomic_t *atomic_var,
- pj_atomic_value_t value);
+
+/**
+ * Add a value to an atomic type.
+ *
+ * @param atomic_var The atomic variable.
+ * @param value Value to be added.
+ */
+PJ_DECL(void) pj_atomic_add( pj_atomic_t *atomic_var,
+ pj_atomic_value_t value);
/**
* Add a value to an atomic type and get the result.
@@ -368,576 +389,576 @@
*/
PJ_DECL(pj_atomic_value_t) pj_atomic_add_and_get( pj_atomic_t *atomic_var,
pj_atomic_value_t value);
-
-/**
- * @}
- */
-
-///////////////////////////////////////////////////////////////////////////////
-/**
- * @defgroup PJ_MUTEX Mutexes.
- * @ingroup PJ_OS
- * @{
- *
- * Mutex manipulation. Alternatively, application can use higher abstraction
- * for lock objects, which provides uniform API for all kinds of lock
- * mechanisms, including mutex. See @ref PJ_LOCK for more information.
- */
-
-/**
- * Mutex types:
- * - PJ_MUTEX_DEFAULT: default mutex type, which is system dependent.
- * - PJ_MUTEX_SIMPLE: non-recursive mutex.
- * - PJ_MUTEX_RECURSIVE: recursive mutex.
- */
-typedef enum pj_mutex_type_e
-{
- PJ_MUTEX_DEFAULT,
- PJ_MUTEX_SIMPLE,
- PJ_MUTEX_RECURSE,
-} pj_mutex_type_e;
-
-
-/**
- * Create mutex of the specified type.
- *
- * @param pool The pool.
- * @param name Name to be associated with the mutex (for debugging).
- * @param type The type of the mutex, of type #pj_mutex_type_e.
- * @param mutex Pointer to hold the returned mutex instance.
- *
- * @return PJ_SUCCESS on success, or the error code.
- */
-PJ_DECL(pj_status_t) pj_mutex_create(pj_pool_t *pool,
- const char *name,
- int type,
- pj_mutex_t **mutex);
-
-/**
- * Create simple, non-recursive mutex.
- * This function is a simple wrapper for #pj_mutex_create to create
- * non-recursive mutex.
- *
- * @param pool The pool.
- * @param name Mutex name.
- * @param mutex Pointer to hold the returned mutex instance.
- *
- * @return PJ_SUCCESS on success, or the error code.
- */
-PJ_DECL(pj_status_t) pj_mutex_create_simple( pj_pool_t *pool, const char *name,
- pj_mutex_t **mutex );
-
-/**
- * Create recursive mutex.
- * This function is a simple wrapper for #pj_mutex_create to create
- * recursive mutex.
- *
- * @param pool The pool.
- * @param name Mutex name.
- * @param mutex Pointer to hold the returned mutex instance.
- *
- * @return PJ_SUCCESS on success, or the error code.
- */
-PJ_DECL(pj_status_t) pj_mutex_create_recursive( pj_pool_t *pool,
- const char *name,
- pj_mutex_t **mutex );
-
-/**
- * Acquire mutex lock.
- *
- * @param mutex The mutex.
- * @return PJ_SUCCESS on success, or the error code.
- */
-PJ_DECL(pj_status_t) pj_mutex_lock(pj_mutex_t *mutex);
-
-/**
- * Release mutex lock.
- *
- * @param mutex The mutex.
- * @return PJ_SUCCESS on success, or the error code.
- */
-PJ_DECL(pj_status_t) pj_mutex_unlock(pj_mutex_t *mutex);
-
-/**
- * Try to acquire mutex lock.
- *
- * @param mutex The mutex.
- * @return PJ_SUCCESS on success, or the error code if the
- * lock couldn't be acquired.
- */
-PJ_DECL(pj_status_t) pj_mutex_trylock(pj_mutex_t *mutex);
-
-/**
- * Destroy mutex.
- *
- * @param mutex Te mutex.
- * @return PJ_SUCCESS on success, or the error code.
- */
-PJ_DECL(pj_status_t) pj_mutex_destroy(pj_mutex_t *mutex);
-
-/**
- * Determine whether calling thread is owning the mutex (only available when
- * PJ_DEBUG is set).
- * @param mutex The mutex.
- * @return Non-zero if yes.
- */
-#if defined(PJ_DEBUG) && PJ_DEBUG != 0
- PJ_DECL(pj_bool_t) pj_mutex_is_locked(pj_mutex_t *mutex);
-#else
-# define pj_mutex_is_locked(mutex) 1
-#endif
-
-/**
- * @}
- */
-
-///////////////////////////////////////////////////////////////////////////////
-/**
- * @defgroup PJ_CRIT_SEC Critical sections.
- * @ingroup PJ_OS
- * @{
- * Critical section protection can be used to protect regions where:
- * - mutual exclusion protection is needed.
- * - it's rather too expensive to create a mutex.
- * - the time spent in the region is very very brief.
- *
- * Critical section is a global object, and it prevents any threads from
- * entering any regions that are protected by critical section once a thread
- * is already in the section.
- *
- * Critial section is \a not recursive!
- *
- * Application <b>MUST NOT</b> call any functions that may cause current
- * thread to block (such as allocating memory, performing I/O, locking mutex,
- * etc.) while holding the critical section.
- */
-/**
- * Enter critical section.
- */
-PJ_DECL(void) pj_enter_critical_section(void);
-
-/**
- * Leave critical section.
- */
-PJ_DECL(void) pj_leave_critical_section(void);
-
-/**
- * @}
- */
-
-///////////////////////////////////////////////////////////////////////////////
-#if defined(PJ_HAS_SEMAPHORE) && PJ_HAS_SEMAPHORE != 0
-/**
- * @defgroup PJ_SEM Semaphores.
- * @ingroup PJ_OS
- * @{
- *
- * This module provides abstraction for semaphores, where available.
- */
-
-/**
- * Create semaphore.
- *
- * @param pool The pool.
- * @param name Name to be assigned to the semaphore (for logging purpose)
- * @param initial The initial count of the semaphore.
- * @param max The maximum count of the semaphore.
- * @param sem Pointer to hold the semaphore created.
- *
- * @return PJ_SUCCESS on success, or the error code.
- */
-PJ_DECL(pj_status_t) pj_sem_create( pj_pool_t *pool,
- const char *name,
- unsigned initial,
- unsigned max,
- pj_sem_t **sem);
-
-/**
- * Wait for semaphore.
- *
- * @param sem The semaphore.
- *
- * @return PJ_SUCCESS on success, or the error code.
- */
-PJ_DECL(pj_status_t) pj_sem_wait(pj_sem_t *sem);
-
-/**
- * Try wait for semaphore.
- *
- * @param sem The semaphore.
- *
- * @return PJ_SUCCESS on success, or the error code.
- */
-PJ_DECL(pj_status_t) pj_sem_trywait(pj_sem_t *sem);
-
-/**
- * Release semaphore.
- *
- * @param sem The semaphore.
- *
- * @return PJ_SUCCESS on success, or the error code.
- */
-PJ_DECL(pj_status_t) pj_sem_post(pj_sem_t *sem);
-
-/**
- * Destroy semaphore.
- *
- * @param sem The semaphore.
- *
- * @return PJ_SUCCESS on success, or the error code.
- */
-PJ_DECL(pj_status_t) pj_sem_destroy(pj_sem_t *sem);
-
-/**
- * @}
- */
-#endif /* PJ_HAS_SEMAPHORE */
-
-
-///////////////////////////////////////////////////////////////////////////////
-#if defined(PJ_HAS_EVENT_OBJ) && PJ_HAS_EVENT_OBJ != 0
-/**
- * @defgroup PJ_EVENT Event Object.
- * @ingroup PJ_OS
- * @{
- *
- * This module provides abstraction to event object (e.g. Win32 Event) where
- * available. Event objects can be used for synchronization among threads.
- */
-
-/**
- * Create event object.
- *
- * @param pool The pool.
- * @param name The name of the event object (for logging purpose).
- * @param manual_reset Specify whether the event is manual-reset
- * @param initial Specify the initial state of the event object.
- * @param event Pointer to hold the returned event object.
- *
- * @return event handle, or NULL if failed.
- */
-PJ_DECL(pj_status_t) pj_event_create(pj_pool_t *pool, const char *name,
- pj_bool_t manual_reset, pj_bool_t initial,
- pj_event_t **event);
-
-/**
- * Wait for event to be signaled.
- *
- * @param event The event object.
- *
- * @return zero if successfull.
- */
-PJ_DECL(pj_status_t) pj_event_wait(pj_event_t *event);
-
-/**
- * Try wait for event object to be signalled.
- *
- * @param event The event object.
- *
- * @return zero if successfull.
- */
-PJ_DECL(pj_status_t) pj_event_trywait(pj_event_t *event);
-
-/**
- * Set the event object state to signaled. For auto-reset event, this
- * will only release the first thread that are waiting on the event. For
- * manual reset event, the state remains signaled until the event is reset.
- * If there is no thread waiting on the event, the event object state
- * remains signaled.
- *
- * @param event The event object.
- *
- * @return zero if successfull.
- */
-PJ_DECL(pj_status_t) pj_event_set(pj_event_t *event);
-
-/**
- * Set the event object to signaled state to release appropriate number of
- * waiting threads and then reset the event object to non-signaled. For
- * manual-reset event, this function will release all waiting threads. For
- * auto-reset event, this function will only release one waiting thread.
- *
- * @param event The event object.
- *
- * @return zero if successfull.
- */
-PJ_DECL(pj_status_t) pj_event_pulse(pj_event_t *event);
-
-/**
- * Set the event object state to non-signaled.
- *
- * @param event The event object.
- *
- * @return zero if successfull.
- */
-PJ_DECL(pj_status_t) pj_event_reset(pj_event_t *event);
-
-/**
- * Destroy the event object.
- *
- * @param event The event object.
- *
- * @return zero if successfull.
- */
-PJ_DECL(pj_status_t) pj_event_destroy(pj_event_t *event);
-
-/**
- * @}
- */
-#endif /* PJ_HAS_EVENT_OBJ */
-
-///////////////////////////////////////////////////////////////////////////////
-/**
- * @addtogroup PJ_TIME Time Data Type and Manipulation.
- * @ingroup PJ_OS
- * @{
- * This module provides API for manipulating time.
- *
- * \section pj_time_examples_sec Examples
- *
- * For examples, please see:
- * - \ref page_pjlib_sleep_test
- */
-
-/**
- * Get current time of day in local representation.
- *
- * @param tv Variable to store the result.
- *
- * @return zero if successfull.
- */
-PJ_DECL(pj_status_t) pj_gettimeofday(pj_time_val *tv);
-
-
-/**
- * Parse time value into date/time representation.
- *
- * @param tv The time.
- * @param pt Variable to store the date time result.
- *
- * @return zero if successfull.
- */
-PJ_DECL(pj_status_t) pj_time_decode(const pj_time_val *tv, pj_parsed_time *pt);
-
-/**
- * Encode date/time to time value.
- *
- * @param pt The date/time.
- * @param tv Variable to store time value result.
- *
- * @return zero if successfull.
- */
-PJ_DECL(pj_status_t) pj_time_encode(const pj_parsed_time *pt, pj_time_val *tv);
-
-/**
- * Convert local time to GMT.
- *
- * @param tv Time to convert.
- *
- * @return zero if successfull.
- */
-PJ_DECL(pj_status_t) pj_time_local_to_gmt(pj_time_val *tv);
-
-/**
- * Convert GMT to local time.
- *
- * @param tv Time to convert.
- *
- * @return zero if successfull.
- */
-PJ_DECL(pj_status_t) pj_time_gmt_to_local(pj_time_val *tv);
-
-/**
- * @}
- */
-
-///////////////////////////////////////////////////////////////////////////////
-#if defined(PJ_TERM_HAS_COLOR) && PJ_TERM_HAS_COLOR != 0
-
-/**
- * @defgroup PJ_TERM Terminal
- * @ingroup PJ_OS
- * @{
- */
-
-/**
- * Set current terminal color.
- *
- * @param color The RGB color.
- *
- * @return zero on success.
- */
-PJ_DECL(pj_status_t) pj_term_set_color(pj_color_t color);
-
-/**
- * Get current terminal foreground color.
- *
- * @return RGB color.
- */
-PJ_DECL(pj_color_t) pj_term_get_color(void);
-
-/**
- * @}
- */
-
-#endif /* PJ_TERM_HAS_COLOR */
-
-///////////////////////////////////////////////////////////////////////////////
-/**
- * @defgroup PJ_TIMESTAMP High Resolution Timestamp
- * @ingroup PJ_OS
- * @{
- *
- * PJLIB provides <b>High Resolution Timestamp</b> API to access highest
- * resolution timestamp value provided by the platform. The API is usefull
- * to measure precise elapsed time, and can be used in applications such
- * as profiling.
- *
- * The timestamp value is represented in cycles, and can be related to
- * normal time (in seconds or sub-seconds) using various functions provided.
- *
- * \section pj_timestamp_examples_sec Examples
- *
- * For examples, please see:
- * - \ref page_pjlib_sleep_test
- * - \ref page_pjlib_timestamp_test
- */
-
-/*
- * High resolution timer.
- */
-#if defined(PJ_HAS_HIGH_RES_TIMER) && PJ_HAS_HIGH_RES_TIMER != 0
-
-/**
- * This structure represents high resolution (64bit) time value. The time
- * values represent time in cycles, which is retrieved by calling
- * #pj_get_timestamp().
- */
-typedef union pj_timestamp
-{
- struct
- {
-#if defined(PJ_IS_LITTLE_ENDIAN) && PJ_IS_LITTLE_ENDIAN!=0
- pj_uint32_t lo; /**< Low 32-bit value of the 64-bit value. */
- pj_uint32_t hi; /**< high 32-bit value of the 64-bit value. */
-#else
- pj_uint32_t hi; /**< high 32-bit value of the 64-bit value. */
- pj_uint32_t lo; /**< Low 32-bit value of the 64-bit value. */
-#endif
- } u32; /**< The 64-bit value as two 32-bit values. */
-
-#if PJ_HAS_INT64
- pj_uint64_t u64; /**< The whole 64-bit value, where available. */
-#endif
-} pj_timestamp;
-
-
-/**
- * Acquire high resolution timer value. The time value are stored
- * in cycles.
- *
- * @param ts High resolution timer value.
- * @return PJ_SUCCESS or the appropriate error code.
- *
- * @see pj_get_timestamp_freq().
- */
-PJ_DECL(pj_status_t) pj_get_timestamp(pj_timestamp *ts);
-
-/**
- * Get high resolution timer frequency, in cycles per second.
- *
- * @param freq Timer frequency, in cycles per second.
- * @return PJ_SUCCESS or the appropriate error code.
- */
-PJ_DECL(pj_status_t) pj_get_timestamp_freq(pj_timestamp *freq);
-
-/**
- * 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
- * whether floating point or 64-bit precision arithmetic is available.
- * For maximum portability, application should prefer to use this function
- * rather than calculating the elapsed time by itself.
- *
- * @param start The starting timestamp.
- * @param stop The end timestamp.
- *
- * @return Elapsed time as #pj_time_val.
- *
- * @see pj_elapsed_usec(), pj_elapsed_cycle(), pj_elapsed_nanosec()
- */
-PJ_DECL(pj_time_val) pj_elapsed_time( const pj_timestamp *start,
- const pj_timestamp *stop );
-
-/**
- * Calculate the elapsed time in 32-bit microseconds.
- * This function calculates the elapsed time using highest precision
- * calculation that is available for current platform, considering
- * whether floating point or 64-bit precision arithmetic is available.
- * For maximum portability, application should prefer to use this function
- * rather than calculating the elapsed time by itself.
- *
- * @param start The starting timestamp.
- * @param stop The end timestamp.
- *
- * @return Elapsed time in microsecond.
- *
- * @see pj_elapsed_time(), pj_elapsed_cycle(), pj_elapsed_nanosec()
- */
-PJ_DECL(pj_uint32_t) pj_elapsed_usec( const pj_timestamp *start,
- const pj_timestamp *stop );
-
-/**
- * Calculate the elapsed time in 32-bit nanoseconds.
- * This function calculates the elapsed time using highest precision
- * calculation that is available for current platform, considering
- * whether floating point or 64-bit precision arithmetic is available.
- * For maximum portability, application should prefer to use this function
- * rather than calculating the elapsed time by itself.
- *
- * @param start The starting timestamp.
- * @param stop The end timestamp.
- *
- * @return Elapsed time in nanoseconds.
- *
- * @see pj_elapsed_time(), pj_elapsed_cycle(), pj_elapsed_usec()
- */
-PJ_DECL(pj_uint32_t) pj_elapsed_nanosec( const pj_timestamp *start,
- const pj_timestamp *stop );
-
-/**
- * Calculate the elapsed time in 32-bit cycles.
- * This function calculates the elapsed time using highest precision
- * calculation that is available for current platform, considering
- * whether floating point or 64-bit precision arithmetic is available.
- * For maximum portability, application should prefer to use this function
- * rather than calculating the elapsed time by itself.
- *
- * @param start The starting timestamp.
- * @param stop The end timestamp.
- *
- * @return Elapsed time in cycles.
- *
- * @see pj_elapsed_usec(), pj_elapsed_time(), pj_elapsed_nanosec()
- */
-PJ_DECL(pj_uint32_t) pj_elapsed_cycle( const pj_timestamp *start,
- const pj_timestamp *stop );
-
-
-#endif /* PJ_HAS_HIGH_RES_TIMER */
-
-/** @} */
-
-
-///////////////////////////////////////////////////////////////////////////////
-/**
- * Internal PJLIB function to initialize the threading subsystem.
- * @return PJ_SUCCESS or the appropriate error code.
- */
-pj_status_t pj_thread_init(void);
-
-
-PJ_END_DECL
-
-#endif /* __PJ_OS_H__ */
-
+
+/**
+ * @}
+ */
+
+///////////////////////////////////////////////////////////////////////////////
+/**
+ * @defgroup PJ_MUTEX Mutexes.
+ * @ingroup PJ_OS
+ * @{
+ *
+ * Mutex manipulation. Alternatively, application can use higher abstraction
+ * for lock objects, which provides uniform API for all kinds of lock
+ * mechanisms, including mutex. See @ref PJ_LOCK for more information.
+ */
+
+/**
+ * Mutex types:
+ * - PJ_MUTEX_DEFAULT: default mutex type, which is system dependent.
+ * - PJ_MUTEX_SIMPLE: non-recursive mutex.
+ * - PJ_MUTEX_RECURSIVE: recursive mutex.
+ */
+typedef enum pj_mutex_type_e
+{
+ PJ_MUTEX_DEFAULT,
+ PJ_MUTEX_SIMPLE,
+ PJ_MUTEX_RECURSE,
+} pj_mutex_type_e;
+
+
+/**
+ * Create mutex of the specified type.
+ *
+ * @param pool The pool.
+ * @param name Name to be associated with the mutex (for debugging).
+ * @param type The type of the mutex, of type #pj_mutex_type_e.
+ * @param mutex Pointer to hold the returned mutex instance.
+ *
+ * @return PJ_SUCCESS on success, or the error code.
+ */
+PJ_DECL(pj_status_t) pj_mutex_create(pj_pool_t *pool,
+ const char *name,
+ int type,
+ pj_mutex_t **mutex);
+
+/**
+ * Create simple, non-recursive mutex.
+ * This function is a simple wrapper for #pj_mutex_create to create
+ * non-recursive mutex.
+ *
+ * @param pool The pool.
+ * @param name Mutex name.
+ * @param mutex Pointer to hold the returned mutex instance.
+ *
+ * @return PJ_SUCCESS on success, or the error code.
+ */
+PJ_DECL(pj_status_t) pj_mutex_create_simple( pj_pool_t *pool, const char *name,
+ pj_mutex_t **mutex );
+
+/**
+ * Create recursive mutex.
+ * This function is a simple wrapper for #pj_mutex_create to create
+ * recursive mutex.
+ *
+ * @param pool The pool.
+ * @param name Mutex name.
+ * @param mutex Pointer to hold the returned mutex instance.
+ *
+ * @return PJ_SUCCESS on success, or the error code.
+ */
+PJ_DECL(pj_status_t) pj_mutex_create_recursive( pj_pool_t *pool,
+ const char *name,
+ pj_mutex_t **mutex );
+
+/**
+ * Acquire mutex lock.
+ *
+ * @param mutex The mutex.
+ * @return PJ_SUCCESS on success, or the error code.
+ */
+PJ_DECL(pj_status_t) pj_mutex_lock(pj_mutex_t *mutex);
+
+/**
+ * Release mutex lock.
+ *
+ * @param mutex The mutex.
+ * @return PJ_SUCCESS on success, or the error code.
+ */
+PJ_DECL(pj_status_t) pj_mutex_unlock(pj_mutex_t *mutex);
+
+/**
+ * Try to acquire mutex lock.
+ *
+ * @param mutex The mutex.
+ * @return PJ_SUCCESS on success, or the error code if the
+ * lock couldn't be acquired.
+ */
+PJ_DECL(pj_status_t) pj_mutex_trylock(pj_mutex_t *mutex);
+
+/**
+ * Destroy mutex.
+ *
+ * @param mutex Te mutex.
+ * @return PJ_SUCCESS on success, or the error code.
+ */
+PJ_DECL(pj_status_t) pj_mutex_destroy(pj_mutex_t *mutex);
+
+/**
+ * Determine whether calling thread is owning the mutex (only available when
+ * PJ_DEBUG is set).
+ * @param mutex The mutex.
+ * @return Non-zero if yes.
+ */
+#if defined(PJ_DEBUG) && PJ_DEBUG != 0
+ PJ_DECL(pj_bool_t) pj_mutex_is_locked(pj_mutex_t *mutex);
+#else
+# define pj_mutex_is_locked(mutex) 1
+#endif
+
+/**
+ * @}
+ */
+
+///////////////////////////////////////////////////////////////////////////////
+/**
+ * @defgroup PJ_CRIT_SEC Critical sections.
+ * @ingroup PJ_OS
+ * @{
+ * Critical section protection can be used to protect regions where:
+ * - mutual exclusion protection is needed.
+ * - it's rather too expensive to create a mutex.
+ * - the time spent in the region is very very brief.
+ *
+ * Critical section is a global object, and it prevents any threads from
+ * entering any regions that are protected by critical section once a thread
+ * is already in the section.
+ *
+ * Critial section is \a not recursive!
+ *
+ * Application <b>MUST NOT</b> call any functions that may cause current
+ * thread to block (such as allocating memory, performing I/O, locking mutex,
+ * etc.) while holding the critical section.
+ */
+/**
+ * Enter critical section.
+ */
+PJ_DECL(void) pj_enter_critical_section(void);
+
+/**
+ * Leave critical section.
+ */
+PJ_DECL(void) pj_leave_critical_section(void);
+
+/**
+ * @}
+ */
+
+///////////////////////////////////////////////////////////////////////////////
+#if defined(PJ_HAS_SEMAPHORE) && PJ_HAS_SEMAPHORE != 0
+/**
+ * @defgroup PJ_SEM Semaphores.
+ * @ingroup PJ_OS
+ * @{
+ *
+ * This module provides abstraction for semaphores, where available.
+ */
+
+/**
+ * Create semaphore.
+ *
+ * @param pool The pool.
+ * @param name Name to be assigned to the semaphore (for logging purpose)
+ * @param initial The initial count of the semaphore.
+ * @param max The maximum count of the semaphore.
+ * @param sem Pointer to hold the semaphore created.
+ *
+ * @return PJ_SUCCESS on success, or the error code.
+ */
+PJ_DECL(pj_status_t) pj_sem_create( pj_pool_t *pool,
+ const char *name,
+ unsigned initial,
+ unsigned max,
+ pj_sem_t **sem);
+
+/**
+ * Wait for semaphore.
+ *
+ * @param sem The semaphore.
+ *
+ * @return PJ_SUCCESS on success, or the error code.
+ */
+PJ_DECL(pj_status_t) pj_sem_wait(pj_sem_t *sem);
+
+/**
+ * Try wait for semaphore.
+ *
+ * @param sem The semaphore.
+ *
+ * @return PJ_SUCCESS on success, or the error code.
+ */
+PJ_DECL(pj_status_t) pj_sem_trywait(pj_sem_t *sem);
+
+/**
+ * Release semaphore.
+ *
+ * @param sem The semaphore.
+ *
+ * @return PJ_SUCCESS on success, or the error code.
+ */
+PJ_DECL(pj_status_t) pj_sem_post(pj_sem_t *sem);
+
+/**
+ * Destroy semaphore.
+ *
+ * @param sem The semaphore.
+ *
+ * @return PJ_SUCCESS on success, or the error code.
+ */
+PJ_DECL(pj_status_t) pj_sem_destroy(pj_sem_t *sem);
+
+/**
+ * @}
+ */
+#endif /* PJ_HAS_SEMAPHORE */
+
+
+///////////////////////////////////////////////////////////////////////////////
+#if defined(PJ_HAS_EVENT_OBJ) && PJ_HAS_EVENT_OBJ != 0
+/**
+ * @defgroup PJ_EVENT Event Object.
+ * @ingroup PJ_OS
+ * @{
+ *
+ * This module provides abstraction to event object (e.g. Win32 Event) where
+ * available. Event objects can be used for synchronization among threads.
+ */
+
+/**
+ * Create event object.
+ *
+ * @param pool The pool.
+ * @param name The name of the event object (for logging purpose).
+ * @param manual_reset Specify whether the event is manual-reset
+ * @param initial Specify the initial state of the event object.
+ * @param event Pointer to hold the returned event object.
+ *
+ * @return event handle, or NULL if failed.
+ */
+PJ_DECL(pj_status_t) pj_event_create(pj_pool_t *pool, const char *name,
+ pj_bool_t manual_reset, pj_bool_t initial,
+ pj_event_t **event);
+
+/**
+ * Wait for event to be signaled.
+ *
+ * @param event The event object.
+ *
+ * @return zero if successfull.
+ */
+PJ_DECL(pj_status_t) pj_event_wait(pj_event_t *event);
+
+/**
+ * Try wait for event object to be signalled.
+ *
+ * @param event The event object.
+ *
+ * @return zero if successfull.
+ */
+PJ_DECL(pj_status_t) pj_event_trywait(pj_event_t *event);
+
+/**
+ * Set the event object state to signaled. For auto-reset event, this
+ * will only release the first thread that are waiting on the event. For
+ * manual reset event, the state remains signaled until the event is reset.
+ * If there is no thread waiting on the event, the event object state
+ * remains signaled.
+ *
+ * @param event The event object.
+ *
+ * @return zero if successfull.
+ */
+PJ_DECL(pj_status_t) pj_event_set(pj_event_t *event);
+
+/**
+ * Set the event object to signaled state to release appropriate number of
+ * waiting threads and then reset the event object to non-signaled. For
+ * manual-reset event, this function will release all waiting threads. For
+ * auto-reset event, this function will only release one waiting thread.
+ *
+ * @param event The event object.
+ *
+ * @return zero if successfull.
+ */
+PJ_DECL(pj_status_t) pj_event_pulse(pj_event_t *event);
+
+/**
+ * Set the event object state to non-signaled.
+ *
+ * @param event The event object.
+ *
+ * @return zero if successfull.
+ */
+PJ_DECL(pj_status_t) pj_event_reset(pj_event_t *event);
+
+/**
+ * Destroy the event object.
+ *
+ * @param event The event object.
+ *
+ * @return zero if successfull.
+ */
+PJ_DECL(pj_status_t) pj_event_destroy(pj_event_t *event);
+
+/**
+ * @}
+ */
+#endif /* PJ_HAS_EVENT_OBJ */
+
+///////////////////////////////////////////////////////////////////////////////
+/**
+ * @addtogroup PJ_TIME Time Data Type and Manipulation.
+ * @ingroup PJ_OS
+ * @{
+ * This module provides API for manipulating time.
+ *
+ * \section pj_time_examples_sec Examples
+ *
+ * For examples, please see:
+ * - \ref page_pjlib_sleep_test
+ */
+
+/**
+ * Get current time of day in local representation.
+ *
+ * @param tv Variable to store the result.
+ *
+ * @return zero if successfull.
+ */
+PJ_DECL(pj_status_t) pj_gettimeofday(pj_time_val *tv);
+
+
+/**
+ * Parse time value into date/time representation.
+ *
+ * @param tv The time.
+ * @param pt Variable to store the date time result.
+ *
+ * @return zero if successfull.
+ */
+PJ_DECL(pj_status_t) pj_time_decode(const pj_time_val *tv, pj_parsed_time *pt);
+
+/**
+ * Encode date/time to time value.
+ *
+ * @param pt The date/time.
+ * @param tv Variable to store time value result.
+ *
+ * @return zero if successfull.
+ */
+PJ_DECL(pj_status_t) pj_time_encode(const pj_parsed_time *pt, pj_time_val *tv);
+
+/**
+ * Convert local time to GMT.
+ *
+ * @param tv Time to convert.
+ *
+ * @return zero if successfull.
+ */
+PJ_DECL(pj_status_t) pj_time_local_to_gmt(pj_time_val *tv);
+
+/**
+ * Convert GMT to local time.
+ *
+ * @param tv Time to convert.
+ *
+ * @return zero if successfull.
+ */
+PJ_DECL(pj_status_t) pj_time_gmt_to_local(pj_time_val *tv);
+
+/**
+ * @}
+ */
+
+///////////////////////////////////////////////////////////////////////////////
+#if defined(PJ_TERM_HAS_COLOR) && PJ_TERM_HAS_COLOR != 0
+
+/**
+ * @defgroup PJ_TERM Terminal
+ * @ingroup PJ_OS
+ * @{
+ */
+
+/**
+ * Set current terminal color.
+ *
+ * @param color The RGB color.
+ *
+ * @return zero on success.
+ */
+PJ_DECL(pj_status_t) pj_term_set_color(pj_color_t color);
+
+/**
+ * Get current terminal foreground color.
+ *
+ * @return RGB color.
+ */
+PJ_DECL(pj_color_t) pj_term_get_color(void);
+
+/**
+ * @}
+ */
+
+#endif /* PJ_TERM_HAS_COLOR */
+
+///////////////////////////////////////////////////////////////////////////////
+/**
+ * @defgroup PJ_TIMESTAMP High Resolution Timestamp
+ * @ingroup PJ_OS
+ * @{
+ *
+ * PJLIB provides <b>High Resolution Timestamp</b> API to access highest
+ * resolution timestamp value provided by the platform. The API is usefull
+ * to measure precise elapsed time, and can be used in applications such
+ * as profiling.
+ *
+ * The timestamp value is represented in cycles, and can be related to
+ * normal time (in seconds or sub-seconds) using various functions provided.
+ *
+ * \section pj_timestamp_examples_sec Examples
+ *
+ * For examples, please see:
+ * - \ref page_pjlib_sleep_test
+ * - \ref page_pjlib_timestamp_test
+ */
+
+/*
+ * High resolution timer.
+ */
+#if defined(PJ_HAS_HIGH_RES_TIMER) && PJ_HAS_HIGH_RES_TIMER != 0
+
+/**
+ * This structure represents high resolution (64bit) time value. The time
+ * values represent time in cycles, which is retrieved by calling
+ * #pj_get_timestamp().
+ */
+typedef union pj_timestamp
+{
+ struct
+ {
+#if defined(PJ_IS_LITTLE_ENDIAN) && PJ_IS_LITTLE_ENDIAN!=0
+ pj_uint32_t lo; /**< Low 32-bit value of the 64-bit value. */
+ pj_uint32_t hi; /**< high 32-bit value of the 64-bit value. */
+#else
+ pj_uint32_t hi; /**< high 32-bit value of the 64-bit value. */
+ pj_uint32_t lo; /**< Low 32-bit value of the 64-bit value. */
+#endif
+ } u32; /**< The 64-bit value as two 32-bit values. */
+
+#if PJ_HAS_INT64
+ pj_uint64_t u64; /**< The whole 64-bit value, where available. */
+#endif
+} pj_timestamp;
+
+
+/**
+ * Acquire high resolution timer value. The time value are stored
+ * in cycles.
+ *
+ * @param ts High resolution timer value.
+ * @return PJ_SUCCESS or the appropriate error code.
+ *
+ * @see pj_get_timestamp_freq().
+ */
+PJ_DECL(pj_status_t) pj_get_timestamp(pj_timestamp *ts);
+
+/**
+ * Get high resolution timer frequency, in cycles per second.
+ *
+ * @param freq Timer frequency, in cycles per second.
+ * @return PJ_SUCCESS or the appropriate error code.
+ */
+PJ_DECL(pj_status_t) pj_get_timestamp_freq(pj_timestamp *freq);
+
+/**
+ * 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
+ * whether floating point or 64-bit precision arithmetic is available.
+ * For maximum portability, application should prefer to use this function
+ * rather than calculating the elapsed time by itself.
+ *
+ * @param start The starting timestamp.
+ * @param stop The end timestamp.
+ *
+ * @return Elapsed time as #pj_time_val.
+ *
+ * @see pj_elapsed_usec(), pj_elapsed_cycle(), pj_elapsed_nanosec()
+ */
+PJ_DECL(pj_time_val) pj_elapsed_time( const pj_timestamp *start,
+ const pj_timestamp *stop );
+
+/**
+ * Calculate the elapsed time in 32-bit microseconds.
+ * This function calculates the elapsed time using highest precision
+ * calculation that is available for current platform, considering
+ * whether floating point or 64-bit precision arithmetic is available.
+ * For maximum portability, application should prefer to use this function
+ * rather than calculating the elapsed time by itself.
+ *
+ * @param start The starting timestamp.
+ * @param stop The end timestamp.
+ *
+ * @return Elapsed time in microsecond.
+ *
+ * @see pj_elapsed_time(), pj_elapsed_cycle(), pj_elapsed_nanosec()
+ */
+PJ_DECL(pj_uint32_t) pj_elapsed_usec( const pj_timestamp *start,
+ const pj_timestamp *stop );
+
+/**
+ * Calculate the elapsed time in 32-bit nanoseconds.
+ * This function calculates the elapsed time using highest precision
+ * calculation that is available for current platform, considering
+ * whether floating point or 64-bit precision arithmetic is available.
+ * For maximum portability, application should prefer to use this function
+ * rather than calculating the elapsed time by itself.
+ *
+ * @param start The starting timestamp.
+ * @param stop The end timestamp.
+ *
+ * @return Elapsed time in nanoseconds.
+ *
+ * @see pj_elapsed_time(), pj_elapsed_cycle(), pj_elapsed_usec()
+ */
+PJ_DECL(pj_uint32_t) pj_elapsed_nanosec( const pj_timestamp *start,
+ const pj_timestamp *stop );
+
+/**
+ * Calculate the elapsed time in 32-bit cycles.
+ * This function calculates the elapsed time using highest precision
+ * calculation that is available for current platform, considering
+ * whether floating point or 64-bit precision arithmetic is available.
+ * For maximum portability, application should prefer to use this function
+ * rather than calculating the elapsed time by itself.
+ *
+ * @param start The starting timestamp.
+ * @param stop The end timestamp.
+ *
+ * @return Elapsed time in cycles.
+ *
+ * @see pj_elapsed_usec(), pj_elapsed_time(), pj_elapsed_nanosec()
+ */
+PJ_DECL(pj_uint32_t) pj_elapsed_cycle( const pj_timestamp *start,
+ const pj_timestamp *stop );
+
+
+#endif /* PJ_HAS_HIGH_RES_TIMER */
+
+/** @} */
+
+
+///////////////////////////////////////////////////////////////////////////////
+/**
+ * Internal PJLIB function to initialize the threading subsystem.
+ * @return PJ_SUCCESS or the appropriate error code.
+ */
+pj_status_t pj_thread_init(void);
+
+
+PJ_END_DECL
+
+#endif /* __PJ_OS_H__ */
+
diff --git a/pjlib/include/pj/pool.h b/pjlib/include/pj/pool.h
index 4be4d24..a8ad7e8 100644
--- a/pjlib/include/pj/pool.h
+++ b/pjlib/include/pj/pool.h
@@ -1,572 +1,593 @@
-/* $Id$
- *
- */
-
-#ifndef __PJ_POOL_H__
-#define __PJ_POOL_H__
-
-/**
- * @file pool.h
- * @brief Memory Pool.
- */
-
-#include <pj/list.h>
-
-PJ_BEGIN_DECL
-
-/**
- * @defgroup PJ_POOL_GROUP Memory Pool Management
- * @ingroup PJ
- * @brief
- * Memory pool management provides API to allocate and deallocate memory from
- * memory pool and to manage and establish policy for pool creation and
- * destruction in pool factory.
- *
- * \section PJ_POOL_FACTORY_SEC Pool Factory
- * See: \ref PJ_POOL_FACTORY "Pool Factory"
- *
- * A memory pool must be created through a factory. A factory not only provides
- * generic interface functions to create and release pool, but also provides
- * strategy to manage the life time of pools. One sample implementation,
- * \a pj_caching_pool, can be set to keep the pools released by application for
- * future use as long as the total memory is below the limit.
- *
- * The pool factory interface declared in PJLIB is designed to be extensible.
- * Application can define its own strategy by creating it's own pool factory
- * implementation, and this strategy can be used even by existing library
- * without recompilation.
- *
- *
- * \section PJ_POOL_POLICY_SEC Pool Factory Policy
- * See: \ref PJ_POOL_FACTORY "Pool Factory Policy"
- *
- * A pool factory only defines functions to create and release pool and how
- * to manage pools, but the rest of the functionalities are controlled by
- * policy. A pool policy defines:
- * - how memory block is allocated and deallocated (the default implementation
- * allocates and deallocate memory by calling malloc() and free()).
- * - callback to be called when memory allocation inside a pool fails (the
- * default implementation will throw PJ_NO_MEMORY_EXCEPTION exception).
- * - concurrency when creating and releasing pool from/to the factory.
- *
- * A pool factory can be given different policy during creation to make
- * it behave differently. For example, caching pool factory can be configured
- * to allocate and deallocate from a static/contiguous/preallocated memory
- * instead of using malloc()/free().
- *
- * What strategy/factory and what policy to use is not defined by PJLIB, but
- * instead is left to application to make use whichever is most efficient for
- * itself.
- *
- *
- * \section PJ_POOL_POOL_SEC The Pool
- * See: \ref PJ_POOL "Pool"
- *
- * The memory pool is an opaque object created by pool factory.
- * Application uses this object to request a memory chunk, by calling
- * #pj_pool_alloc or #pj_pool_calloc. When the application has finished using
- * the pool, it must call #pj_pool_release to free all the chunks previously
- * allocated and release the pool back to the factory.
- *
- * \section PJ_POOL_THREADING_SEC More on Threading Policies:
- * - By design, memory allocation from a pool is not thread safe. We assumed
- * that a pool will be owned by an object, and thread safety should be
- * handled by that object. Thus these functions are not thread safe:
- * - #pj_pool_alloc,
- * - #pj_pool_calloc,
- * - and other pool statistic functions.
- * - Threading in the pool factory is decided by the policy set for the
- * factory when it was created.
- *
- * \section PJ_POOL_EXAMPLES_SEC Examples
- *
- * For some sample codes on how to use the pool, please see:
- * - @ref page_pjlib_pool_test
- */
-
-/**
- * @defgroup PJ_POOL Memory Pool.
- * @ingroup PJ_POOL_GROUP
- * @brief
- * A memory pool is initialized with an initial amount of memory, which is
- * called a block. Pool can be configured to dynamically allocate more memory
- * blocks when it runs out of memory. Subsequent memory allocations by user
- * will use up portions of these block.
- * The pool doesn't keep track of individual memory allocations
- * by user, and the user doesn't have to free these indidual allocations. This
- * makes memory allocation simple and very fast. All the memory allocated from
- * the pool will be destroyed when the pool itself is destroyed.
- * @{
- */
-
-/**
- * The type for function to receive callback from the pool when it is unable
- * to allocate memory. The elegant way to handle this condition is to throw
- * exception, and this is what is expected by most of this library
- * components.
- */
-typedef void pj_pool_callback(pj_pool_t *pool, pj_size_t size);
-
-/**
- * This class, which is used internally by the pool, describes a single
- * block of memory from which user memory allocations will be allocated from.
- */
-typedef struct pj_pool_block
-{
- PJ_DECL_LIST_MEMBER(struct pj_pool_block); /**< List's prev and next. */
- unsigned char *buf; /**< Start of buffer. */
- unsigned char *cur; /**< Current alloc ptr. */
- unsigned char *end; /**< End of buffer. */
-} pj_pool_block;
-
-
-/**
- * This structure describes the memory pool. Only implementors of pool factory
- * need to care about the contents of this structure.
- */
-struct pj_pool_t
-{
- PJ_DECL_LIST_MEMBER(struct pj_pool_t); /**< Standard list elements. */
-
- /** Pool name */
- char obj_name[PJ_MAX_OBJ_NAME];
-
- /** Pool factory. */
- pj_pool_factory *factory;
-
- /** Current capacity allocated by the pool. */
- pj_size_t capacity;
-
- /** Number of memory used/allocated. */
- pj_size_t used_size;
-
- /** Size of memory block to be allocated when the pool runs out of memory */
- pj_size_t increment_size;
-
- /** List of memory blocks allcoated by the pool. */
- pj_pool_block block_list;
-
- /** The callback to be called when the pool is unable to allocate memory. */
- pj_pool_callback *callback;
-
-};
-
-
-/**
- * Guidance on how much memory required for initial pool administrative data.
- */
-#define PJ_POOL_SIZE (sizeof(struct pj_pool_t))
-
-/**
- * Pool memory alignment (must be power of 2).
- */
-#ifndef PJ_POOL_ALIGNMENT
-# define PJ_POOL_ALIGNMENT 4
-#endif
-
-/**
- * Create a new pool from the pool factory. This wrapper will call create_pool
- * member of the pool factory.
- *
- * @param factory The pool factory.
- * @param name The name to be assigned to the pool. The name should
- * not be longer than PJ_MAX_OBJ_NAME (32 chars), or
- * otherwise it will be truncated.
- * @param initial_size The size of initial memory blocks taken by the pool.
- * Note that the pool will take 68+20 bytes for
- * administrative area from this block.
- * @param increment_size the size of each additional blocks to be allocated
- * when the pool is running out of memory. If user
- * requests memory which is larger than this size, then
- * an error occurs.
- * Note that each time a pool allocates additional block,
- * it needs PJ_POOL_SIZE more to store some
- * administrative info.
- * @param callback Callback to be called when error occurs in the pool.
- * If this value is NULL, then the callback from pool
- * factory policy will be used.
- * Note that when an error occurs during pool creation,
- * the callback itself is not called. Instead, NULL
- * will be returned.
- *
- * @return The memory pool, or NULL.
- */
-PJ_IDECL(pj_pool_t*) pj_pool_create(pj_pool_factory *factory,
- const char *name,
- pj_size_t initial_size,
- pj_size_t increment_size,
- pj_pool_callback *callback);
-
-/**
- * Release the pool back to pool factory.
- *
- * @param pool Memory pool.
- */
-PJ_IDECL(void) pj_pool_release( pj_pool_t *pool );
-
-/**
- * Get pool object name.
- *
- * @param pool the pool.
- *
- * @return pool name as NULL terminated string.
- */
-PJ_IDECL(const char *) pj_pool_getobjname( const pj_pool_t *pool );
-
-/**
- * Reset the pool to its state when it was initialized.
- * This means that if additional blocks have been allocated during runtime,
- * then they will be freed. Only the original block allocated during
- * initialization is retained. This function will also reset the internal
- * counters, such as pool capacity and used size.
- *
- * @param pool the pool.
- */
-PJ_DECL(void) pj_pool_reset( pj_pool_t *pool );
-
-
-/**
- * Get the pool capacity, that is, the system storage that have been allocated
- * by the pool, and have been used/will be used to allocate user requests.
- * There's no guarantee that the returned value represent a single
- * contiguous block, because the capacity may be spread in several blocks.
- *
- * @param pool the pool.
- *
- * @return the capacity.
- */
-PJ_IDECL(pj_size_t) pj_pool_get_capacity( pj_pool_t *pool );
-
-/**
- * Get the total size of user allocation request.
- *
- * @param pool the pool.
- *
- * @return the total size.
- */
-PJ_IDECL(pj_size_t) pj_pool_get_used_size( pj_pool_t *pool );
-
-/**
- * Allocate storage with the specified size from the pool.
- * If there's no storage available in the pool, then the pool can allocate more
- * blocks if the increment size is larger than the requested size.
- *
- * @param pool the pool.
- * @param size the requested size.
- *
- * @return pointer to the allocated memory.
- */
-PJ_IDECL(void*) pj_pool_alloc( pj_pool_t *pool, pj_size_t size);
-
-/**
- * Allocate storage from the pool, and initialize it to zero.
- * This function behaves like pj_pool_alloc(), except that the storage will
- * be initialized to zero.
- *
- * @param pool the pool.
- * @param count the number of elements in the array.
- * @param elem the size of individual element.
- *
- * @return pointer to the allocated memory.
- */
-PJ_IDECL(void*) pj_pool_calloc( pj_pool_t *pool, pj_size_t count,
- pj_size_t elem);
-
-
-/**
- * @def pj_pool_zalloc(pj_pool_t *pool, pj_size_t size)
- * Allocate storage from the pool and initialize it to zero.
- *
- * @param pool The pool.
- * @param size The size to be allocated.
- *
- * @return Pointer to the allocated memory.
- */
-#define pj_pool_zalloc(pool, size) pj_pool_calloc(pool, 1, size)
-
-
-/**
- * @} // PJ_POOL
- */
-
-///////////////////////////////////////////////////////////////////////////////
-/**
- * @defgroup PJ_POOL_FACTORY Pool Factory and Policy.
- * @ingroup PJ_POOL_GROUP
- * @brief
- * Pool factory declares an interface to create and destroy pool. There may
- * be several strategies for pool creation, and these strategies should
- * implement the interface defined by pool factory.
- *
- * \section PJ_POOL_FACTORY_ITF Pool Factory Interface
- * The pool factory defines the following interface:
- * - \a policy: the memory pool factory policy.
- * - \a create_pool(): create a new memory pool.
- * - \a release_pool(): release memory pool back to factory.
- *
- * \section PJ_POOL_FACTORY_POL Pool Factory Policy.
- * The pool factory policy controls the behaviour of memory factories, and
- * defines the following interface:
- * - \a block_alloc(): allocate memory block from backend memory mgmt/system.
- * - \a block_free(): free memory block back to backend memory mgmt/system.
- * @{
- */
-
-/* We unfortunately don't have support for factory policy options as now,
- so we keep this commented at the moment.
-enum PJ_POOL_FACTORY_OPTION
-{
- PJ_POOL_FACTORY_SERIALIZE = 1
-};
-*/
-
-/**
- * This structure declares pool factory interface.
- */
-typedef struct pj_pool_factory_policy
-{
- /**
- * Allocate memory block (for use by pool). This function is called
- * by memory pool to allocate memory block.
- *
- * @param factory Pool factory.
- * @param size The size of memory block to allocate.
- *
- * @return Memory block.
- */
- void* (*block_alloc)(pj_pool_factory *factory, pj_size_t size);
-
- /**
- * Free memory block.
- *
- * @param factory Pool factory.
- * @param mem Memory block previously allocated by block_alloc().
- * @param size The size of memory block.
- */
- void (*block_free)(pj_pool_factory *factory, void *mem, pj_size_t size);
-
- /**
- * Default callback to be called when memory allocation fails.
- */
- pj_pool_callback *callback;
-
- /**
- * Option flags.
- */
- unsigned flags;
-
-} pj_pool_factory_policy;
-
-/**
- * This constant denotes the exception number that will be thrown by default
- * memory factory policy when memory allocation fails.
- */
-extern int PJ_NO_MEMORY_EXCEPTION;
-
-/**
- * This global variable points to default memory pool factory policy.
- * The behaviour of the default policy is:
- * - block allocation and deallocation use malloc() and free().
- * - callback will raise PJ_NO_MEMORY_EXCEPTION exception.
- * - access to pool factory is not serialized (i.e. not thread safe).
- */
-extern pj_pool_factory_policy pj_pool_factory_default_policy;
-
-/**
- * This structure contains the declaration for pool factory interface.
- */
-struct pj_pool_factory
-{
- /**
- * Memory pool policy.
- */
- pj_pool_factory_policy policy;
-
- /**
- * Create a new pool from the pool factory.
- *
- * @param factory The pool factory.
- * @param name the name to be assigned to the pool. The name should
- * not be longer than PJ_MAX_OBJ_NAME (32 chars), or
- * otherwise it will be truncated.
- * @param initial_size the size of initial memory blocks taken by the pool.
- * Note that the pool will take 68+20 bytes for
- * administrative area from this block.
- * @param increment_size the size of each additional blocks to be allocated
- * when the pool is running out of memory. If user
- * requests memory which is larger than this size, then
- * an error occurs.
- * Note that each time a pool allocates additional block,
- * it needs 20 bytes (equal to sizeof(pj_pool_block)) to
- * store some administrative info.
- * @param callback Cllback to be called when error occurs in the pool.
- * Note that when an error occurs during pool creation,
- * the callback itself is not called. Instead, NULL
- * will be returned.
- *
- * @return the memory pool, or NULL.
- */
- pj_pool_t* (*create_pool)( pj_pool_factory *factory,
- const char *name,
- pj_size_t initial_size,
- pj_size_t increment_size,
- pj_pool_callback *callback);
-
- /**
- * Release the pool to the pool factory.
- *
- * @param factory The pool factory.
- * @param pool The pool to be released.
- */
- void (*release_pool)( pj_pool_factory *factory, pj_pool_t *pool );
-
- /**
- * Dump pool status to log.
- *
- * @param factory The pool factory.
- */
- void (*dump_status)( pj_pool_factory *factory, pj_bool_t detail );
-};
-
-/**
- * This function is intended to be used by pool factory implementors.
- * @param factory Pool factory.
- * @param name Pool name.
- * @param initial_size Initial size.
- * @param increment_size Increment size.
- * @param callback Callback.
- * @return The pool object, or NULL.
- */
-PJ_DECL(pj_pool_t*) pj_pool_create_int( pj_pool_factory *factory,
- const char *name,
- pj_size_t initial_size,
- pj_size_t increment_size,
- pj_pool_callback *callback);
-
-/**
- * This function is intended to be used by pool factory implementors.
- * @param pool The pool.
- * @param name Pool name.
- * @param increment_size Increment size.
- * @param callback Callback function.
- */
-PJ_DECL(void) pj_pool_init_int( pj_pool_t *pool,
- const char *name,
- pj_size_t increment_size,
- pj_pool_callback *callback);
-
-/**
- * This function is intended to be used by pool factory implementors.
- * @param pool The memory pool.
- */
-PJ_DECL(void) pj_pool_destroy_int( pj_pool_t *pool );
-
-
-/**
- * @} // PJ_POOL_FACTORY
- */
-
-///////////////////////////////////////////////////////////////////////////////
-
-/**
- * @defgroup PJ_CACHING_POOL Caching Pool Factory.
- * @ingroup PJ_POOL_GROUP
- * @brief
- * Caching pool is one sample implementation of pool factory where the
- * factory can reuse memory to create a pool. Application defines what the
- * maximum memory the factory can hold, and when a pool is released the
- * factory decides whether to destroy the pool or to keep it for future use.
- * If the total amount of memory in the internal cache is still within the
- * limit, the factory will keep the pool in the internal cache, otherwise the
- * pool will be destroyed, thus releasing the memory back to the system.
- *
- * @{
- */
-
-/**
- * Number of unique sizes, to be used as index to the free list.
- * Each pool in the free list is organized by it's size.
- */
-#define PJ_CACHING_POOL_ARRAY_SIZE 16
-
-/**
- * Declaration for caching pool. Application doesn't normally need to
- * care about the contents of this struct, it is only provided here because
- * application need to define an instance of this struct (we can not allocate
- * the struct from a pool since there is no pool factory yet!).
- */
-struct pj_caching_pool
-{
- /** Pool factory interface, must be declared first. */
- pj_pool_factory factory;
-
- /** Current factory's capacity, i.e. number of bytes that are allocated
- * and available for application in this factory. The factory's
- * capacity represents the size of all pools kept by this factory
- * in it's free list, which will be returned to application when it
- * requests to create a new pool.
- */
- pj_size_t capacity;
-
- /** Maximum size that can be held by this factory. Once the capacity
- * has exceeded @a max_capacity, further #pj_pool_release() will
- * flush the pool. If the capacity is still below the @a max_capacity,
- * #pj_pool_release() will save the pool to the factory's free list.
- */
- pj_size_t max_capacity;
-
- /**
- * Number of pools currently held by applications. This number gets
- * incremented everytime #pj_pool_create() is called, and gets
- * decremented when #pj_pool_release() is called.
- */
- pj_size_t used_count;
-
- /**
- * Lists of pools in the cache, indexed by pool size.
- */
- pj_list free_list[PJ_CACHING_POOL_ARRAY_SIZE];
-
- /**
- * List of pools currently allocated by applications.
- */
- pj_list used_list;
-};
-
-
-
-/**
- * Initialize caching pool.
- *
- * @param ch_pool The caching pool factory to be initialized.
- * @param policy Pool factory policy.
- * @param max_capacity The total capacity to be retained in the cache. When
- * the pool is returned to the cache, it will be kept in
- * recycling list if the total capacity of pools in this
- * list plus the capacity of the pool is still below this
- * value.
- */
-PJ_DECL(void) pj_caching_pool_init( pj_caching_pool *ch_pool,
- const pj_pool_factory_policy *policy,
- pj_size_t max_capacity);
-
-
-/**
- * Destroy caching pool, and release all the pools in the recycling list.
- *
- * @param ch_pool The caching pool.
- */
-PJ_DECL(void) pj_caching_pool_destroy( pj_caching_pool *ch_pool );
-
-/**
- * @} // PJ_CACHING_POOL
- */
-
-# if PJ_FUNCTIONS_ARE_INLINED
-# include "pool_i.h"
-# endif
-
-PJ_END_DECL
-
-#endif /* __PJ_POOL_H__ */
-
+/* $Id$
+ *
+ */
+/*
+ * PJLIB - PJ Foundation Library
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ * Benny Prijono <bennylp@bulukucing.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef __PJ_POOL_H__
+#define __PJ_POOL_H__
+
+/**
+ * @file pool.h
+ * @brief Memory Pool.
+ */
+
+#include <pj/list.h>
+
+PJ_BEGIN_DECL
+
+/**
+ * @defgroup PJ_POOL_GROUP Memory Pool Management
+ * @ingroup PJ
+ * @brief
+ * Memory pool management provides API to allocate and deallocate memory from
+ * memory pool and to manage and establish policy for pool creation and
+ * destruction in pool factory.
+ *
+ * \section PJ_POOL_FACTORY_SEC Pool Factory
+ * See: \ref PJ_POOL_FACTORY "Pool Factory"
+ *
+ * A memory pool must be created through a factory. A factory not only provides
+ * generic interface functions to create and release pool, but also provides
+ * strategy to manage the life time of pools. One sample implementation,
+ * \a pj_caching_pool, can be set to keep the pools released by application for
+ * future use as long as the total memory is below the limit.
+ *
+ * The pool factory interface declared in PJLIB is designed to be extensible.
+ * Application can define its own strategy by creating it's own pool factory
+ * implementation, and this strategy can be used even by existing library
+ * without recompilation.
+ *
+ *
+ * \section PJ_POOL_POLICY_SEC Pool Factory Policy
+ * See: \ref PJ_POOL_FACTORY "Pool Factory Policy"
+ *
+ * A pool factory only defines functions to create and release pool and how
+ * to manage pools, but the rest of the functionalities are controlled by
+ * policy. A pool policy defines:
+ * - how memory block is allocated and deallocated (the default implementation
+ * allocates and deallocate memory by calling malloc() and free()).
+ * - callback to be called when memory allocation inside a pool fails (the
+ * default implementation will throw PJ_NO_MEMORY_EXCEPTION exception).
+ * - concurrency when creating and releasing pool from/to the factory.
+ *
+ * A pool factory can be given different policy during creation to make
+ * it behave differently. For example, caching pool factory can be configured
+ * to allocate and deallocate from a static/contiguous/preallocated memory
+ * instead of using malloc()/free().
+ *
+ * What strategy/factory and what policy to use is not defined by PJLIB, but
+ * instead is left to application to make use whichever is most efficient for
+ * itself.
+ *
+ *
+ * \section PJ_POOL_POOL_SEC The Pool
+ * See: \ref PJ_POOL "Pool"
+ *
+ * The memory pool is an opaque object created by pool factory.
+ * Application uses this object to request a memory chunk, by calling
+ * #pj_pool_alloc or #pj_pool_calloc. When the application has finished using
+ * the pool, it must call #pj_pool_release to free all the chunks previously
+ * allocated and release the pool back to the factory.
+ *
+ * \section PJ_POOL_THREADING_SEC More on Threading Policies:
+ * - By design, memory allocation from a pool is not thread safe. We assumed
+ * that a pool will be owned by an object, and thread safety should be
+ * handled by that object. Thus these functions are not thread safe:
+ * - #pj_pool_alloc,
+ * - #pj_pool_calloc,
+ * - and other pool statistic functions.
+ * - Threading in the pool factory is decided by the policy set for the
+ * factory when it was created.
+ *
+ * \section PJ_POOL_EXAMPLES_SEC Examples
+ *
+ * For some sample codes on how to use the pool, please see:
+ * - @ref page_pjlib_pool_test
+ */
+
+/**
+ * @defgroup PJ_POOL Memory Pool.
+ * @ingroup PJ_POOL_GROUP
+ * @brief
+ * A memory pool is initialized with an initial amount of memory, which is
+ * called a block. Pool can be configured to dynamically allocate more memory
+ * blocks when it runs out of memory. Subsequent memory allocations by user
+ * will use up portions of these block.
+ * The pool doesn't keep track of individual memory allocations
+ * by user, and the user doesn't have to free these indidual allocations. This
+ * makes memory allocation simple and very fast. All the memory allocated from
+ * the pool will be destroyed when the pool itself is destroyed.
+ * @{
+ */
+
+/**
+ * The type for function to receive callback from the pool when it is unable
+ * to allocate memory. The elegant way to handle this condition is to throw
+ * exception, and this is what is expected by most of this library
+ * components.
+ */
+typedef void pj_pool_callback(pj_pool_t *pool, pj_size_t size);
+
+/**
+ * This class, which is used internally by the pool, describes a single
+ * block of memory from which user memory allocations will be allocated from.
+ */
+typedef struct pj_pool_block
+{
+ PJ_DECL_LIST_MEMBER(struct pj_pool_block); /**< List's prev and next. */
+ unsigned char *buf; /**< Start of buffer. */
+ unsigned char *cur; /**< Current alloc ptr. */
+ unsigned char *end; /**< End of buffer. */
+} pj_pool_block;
+
+
+/**
+ * This structure describes the memory pool. Only implementors of pool factory
+ * need to care about the contents of this structure.
+ */
+struct pj_pool_t
+{
+ PJ_DECL_LIST_MEMBER(struct pj_pool_t); /**< Standard list elements. */
+
+ /** Pool name */
+ char obj_name[PJ_MAX_OBJ_NAME];
+
+ /** Pool factory. */
+ pj_pool_factory *factory;
+
+ /** Current capacity allocated by the pool. */
+ pj_size_t capacity;
+
+ /** Number of memory used/allocated. */
+ pj_size_t used_size;
+
+ /** Size of memory block to be allocated when the pool runs out of memory */
+ pj_size_t increment_size;
+
+ /** List of memory blocks allcoated by the pool. */
+ pj_pool_block block_list;
+
+ /** The callback to be called when the pool is unable to allocate memory. */
+ pj_pool_callback *callback;
+
+};
+
+
+/**
+ * Guidance on how much memory required for initial pool administrative data.
+ */
+#define PJ_POOL_SIZE (sizeof(struct pj_pool_t))
+
+/**
+ * Pool memory alignment (must be power of 2).
+ */
+#ifndef PJ_POOL_ALIGNMENT
+# define PJ_POOL_ALIGNMENT 4
+#endif
+
+/**
+ * Create a new pool from the pool factory. This wrapper will call create_pool
+ * member of the pool factory.
+ *
+ * @param factory The pool factory.
+ * @param name The name to be assigned to the pool. The name should
+ * not be longer than PJ_MAX_OBJ_NAME (32 chars), or
+ * otherwise it will be truncated.
+ * @param initial_size The size of initial memory blocks taken by the pool.
+ * Note that the pool will take 68+20 bytes for
+ * administrative area from this block.
+ * @param increment_size the size of each additional blocks to be allocated
+ * when the pool is running out of memory. If user
+ * requests memory which is larger than this size, then
+ * an error occurs.
+ * Note that each time a pool allocates additional block,
+ * it needs PJ_POOL_SIZE more to store some
+ * administrative info.
+ * @param callback Callback to be called when error occurs in the pool.
+ * If this value is NULL, then the callback from pool
+ * factory policy will be used.
+ * Note that when an error occurs during pool creation,
+ * the callback itself is not called. Instead, NULL
+ * will be returned.
+ *
+ * @return The memory pool, or NULL.
+ */
+PJ_IDECL(pj_pool_t*) pj_pool_create(pj_pool_factory *factory,
+ const char *name,
+ pj_size_t initial_size,
+ pj_size_t increment_size,
+ pj_pool_callback *callback);
+
+/**
+ * Release the pool back to pool factory.
+ *
+ * @param pool Memory pool.
+ */
+PJ_IDECL(void) pj_pool_release( pj_pool_t *pool );
+
+/**
+ * Get pool object name.
+ *
+ * @param pool the pool.
+ *
+ * @return pool name as NULL terminated string.
+ */
+PJ_IDECL(const char *) pj_pool_getobjname( const pj_pool_t *pool );
+
+/**
+ * Reset the pool to its state when it was initialized.
+ * This means that if additional blocks have been allocated during runtime,
+ * then they will be freed. Only the original block allocated during
+ * initialization is retained. This function will also reset the internal
+ * counters, such as pool capacity and used size.
+ *
+ * @param pool the pool.
+ */
+PJ_DECL(void) pj_pool_reset( pj_pool_t *pool );
+
+
+/**
+ * Get the pool capacity, that is, the system storage that have been allocated
+ * by the pool, and have been used/will be used to allocate user requests.
+ * There's no guarantee that the returned value represent a single
+ * contiguous block, because the capacity may be spread in several blocks.
+ *
+ * @param pool the pool.
+ *
+ * @return the capacity.
+ */
+PJ_IDECL(pj_size_t) pj_pool_get_capacity( pj_pool_t *pool );
+
+/**
+ * Get the total size of user allocation request.
+ *
+ * @param pool the pool.
+ *
+ * @return the total size.
+ */
+PJ_IDECL(pj_size_t) pj_pool_get_used_size( pj_pool_t *pool );
+
+/**
+ * Allocate storage with the specified size from the pool.
+ * If there's no storage available in the pool, then the pool can allocate more
+ * blocks if the increment size is larger than the requested size.
+ *
+ * @param pool the pool.
+ * @param size the requested size.
+ *
+ * @return pointer to the allocated memory.
+ */
+PJ_IDECL(void*) pj_pool_alloc( pj_pool_t *pool, pj_size_t size);
+
+/**
+ * Allocate storage from the pool, and initialize it to zero.
+ * This function behaves like pj_pool_alloc(), except that the storage will
+ * be initialized to zero.
+ *
+ * @param pool the pool.
+ * @param count the number of elements in the array.
+ * @param elem the size of individual element.
+ *
+ * @return pointer to the allocated memory.
+ */
+PJ_IDECL(void*) pj_pool_calloc( pj_pool_t *pool, pj_size_t count,
+ pj_size_t elem);
+
+
+/**
+ * @def pj_pool_zalloc(pj_pool_t *pool, pj_size_t size)
+ * Allocate storage from the pool and initialize it to zero.
+ *
+ * @param pool The pool.
+ * @param size The size to be allocated.
+ *
+ * @return Pointer to the allocated memory.
+ */
+#define pj_pool_zalloc(pool, size) pj_pool_calloc(pool, 1, size)
+
+
+/**
+ * @} // PJ_POOL
+ */
+
+///////////////////////////////////////////////////////////////////////////////
+/**
+ * @defgroup PJ_POOL_FACTORY Pool Factory and Policy.
+ * @ingroup PJ_POOL_GROUP
+ * @brief
+ * Pool factory declares an interface to create and destroy pool. There may
+ * be several strategies for pool creation, and these strategies should
+ * implement the interface defined by pool factory.
+ *
+ * \section PJ_POOL_FACTORY_ITF Pool Factory Interface
+ * The pool factory defines the following interface:
+ * - \a policy: the memory pool factory policy.
+ * - \a create_pool(): create a new memory pool.
+ * - \a release_pool(): release memory pool back to factory.
+ *
+ * \section PJ_POOL_FACTORY_POL Pool Factory Policy.
+ * The pool factory policy controls the behaviour of memory factories, and
+ * defines the following interface:
+ * - \a block_alloc(): allocate memory block from backend memory mgmt/system.
+ * - \a block_free(): free memory block back to backend memory mgmt/system.
+ * @{
+ */
+
+/* We unfortunately don't have support for factory policy options as now,
+ so we keep this commented at the moment.
+enum PJ_POOL_FACTORY_OPTION
+{
+ PJ_POOL_FACTORY_SERIALIZE = 1
+};
+*/
+
+/**
+ * This structure declares pool factory interface.
+ */
+typedef struct pj_pool_factory_policy
+{
+ /**
+ * Allocate memory block (for use by pool). This function is called
+ * by memory pool to allocate memory block.
+ *
+ * @param factory Pool factory.
+ * @param size The size of memory block to allocate.
+ *
+ * @return Memory block.
+ */
+ void* (*block_alloc)(pj_pool_factory *factory, pj_size_t size);
+
+ /**
+ * Free memory block.
+ *
+ * @param factory Pool factory.
+ * @param mem Memory block previously allocated by block_alloc().
+ * @param size The size of memory block.
+ */
+ void (*block_free)(pj_pool_factory *factory, void *mem, pj_size_t size);
+
+ /**
+ * Default callback to be called when memory allocation fails.
+ */
+ pj_pool_callback *callback;
+
+ /**
+ * Option flags.
+ */
+ unsigned flags;
+
+} pj_pool_factory_policy;
+
+/**
+ * This constant denotes the exception number that will be thrown by default
+ * memory factory policy when memory allocation fails.
+ */
+extern int PJ_NO_MEMORY_EXCEPTION;
+
+/**
+ * This global variable points to default memory pool factory policy.
+ * The behaviour of the default policy is:
+ * - block allocation and deallocation use malloc() and free().
+ * - callback will raise PJ_NO_MEMORY_EXCEPTION exception.
+ * - access to pool factory is not serialized (i.e. not thread safe).
+ */
+extern pj_pool_factory_policy pj_pool_factory_default_policy;
+
+/**
+ * This structure contains the declaration for pool factory interface.
+ */
+struct pj_pool_factory
+{
+ /**
+ * Memory pool policy.
+ */
+ pj_pool_factory_policy policy;
+
+ /**
+ * Create a new pool from the pool factory.
+ *
+ * @param factory The pool factory.
+ * @param name the name to be assigned to the pool. The name should
+ * not be longer than PJ_MAX_OBJ_NAME (32 chars), or
+ * otherwise it will be truncated.
+ * @param initial_size the size of initial memory blocks taken by the pool.
+ * Note that the pool will take 68+20 bytes for
+ * administrative area from this block.
+ * @param increment_size the size of each additional blocks to be allocated
+ * when the pool is running out of memory. If user
+ * requests memory which is larger than this size, then
+ * an error occurs.
+ * Note that each time a pool allocates additional block,
+ * it needs 20 bytes (equal to sizeof(pj_pool_block)) to
+ * store some administrative info.
+ * @param callback Cllback to be called when error occurs in the pool.
+ * Note that when an error occurs during pool creation,
+ * the callback itself is not called. Instead, NULL
+ * will be returned.
+ *
+ * @return the memory pool, or NULL.
+ */
+ pj_pool_t* (*create_pool)( pj_pool_factory *factory,
+ const char *name,
+ pj_size_t initial_size,
+ pj_size_t increment_size,
+ pj_pool_callback *callback);
+
+ /**
+ * Release the pool to the pool factory.
+ *
+ * @param factory The pool factory.
+ * @param pool The pool to be released.
+ */
+ void (*release_pool)( pj_pool_factory *factory, pj_pool_t *pool );
+
+ /**
+ * Dump pool status to log.
+ *
+ * @param factory The pool factory.
+ */
+ void (*dump_status)( pj_pool_factory *factory, pj_bool_t detail );
+};
+
+/**
+ * This function is intended to be used by pool factory implementors.
+ * @param factory Pool factory.
+ * @param name Pool name.
+ * @param initial_size Initial size.
+ * @param increment_size Increment size.
+ * @param callback Callback.
+ * @return The pool object, or NULL.
+ */
+PJ_DECL(pj_pool_t*) pj_pool_create_int( pj_pool_factory *factory,
+ const char *name,
+ pj_size_t initial_size,
+ pj_size_t increment_size,
+ pj_pool_callback *callback);
+
+/**
+ * This function is intended to be used by pool factory implementors.
+ * @param pool The pool.
+ * @param name Pool name.
+ * @param increment_size Increment size.
+ * @param callback Callback function.
+ */
+PJ_DECL(void) pj_pool_init_int( pj_pool_t *pool,
+ const char *name,
+ pj_size_t increment_size,
+ pj_pool_callback *callback);
+
+/**
+ * This function is intended to be used by pool factory implementors.
+ * @param pool The memory pool.
+ */
+PJ_DECL(void) pj_pool_destroy_int( pj_pool_t *pool );
+
+
+/**
+ * @} // PJ_POOL_FACTORY
+ */
+
+///////////////////////////////////////////////////////////////////////////////
+
+/**
+ * @defgroup PJ_CACHING_POOL Caching Pool Factory.
+ * @ingroup PJ_POOL_GROUP
+ * @brief
+ * Caching pool is one sample implementation of pool factory where the
+ * factory can reuse memory to create a pool. Application defines what the
+ * maximum memory the factory can hold, and when a pool is released the
+ * factory decides whether to destroy the pool or to keep it for future use.
+ * If the total amount of memory in the internal cache is still within the
+ * limit, the factory will keep the pool in the internal cache, otherwise the
+ * pool will be destroyed, thus releasing the memory back to the system.
+ *
+ * @{
+ */
+
+/**
+ * Number of unique sizes, to be used as index to the free list.
+ * Each pool in the free list is organized by it's size.
+ */
+#define PJ_CACHING_POOL_ARRAY_SIZE 16
+
+/**
+ * Declaration for caching pool. Application doesn't normally need to
+ * care about the contents of this struct, it is only provided here because
+ * application need to define an instance of this struct (we can not allocate
+ * the struct from a pool since there is no pool factory yet!).
+ */
+struct pj_caching_pool
+{
+ /** Pool factory interface, must be declared first. */
+ pj_pool_factory factory;
+
+ /** Current factory's capacity, i.e. number of bytes that are allocated
+ * and available for application in this factory. The factory's
+ * capacity represents the size of all pools kept by this factory
+ * in it's free list, which will be returned to application when it
+ * requests to create a new pool.
+ */
+ pj_size_t capacity;
+
+ /** Maximum size that can be held by this factory. Once the capacity
+ * has exceeded @a max_capacity, further #pj_pool_release() will
+ * flush the pool. If the capacity is still below the @a max_capacity,
+ * #pj_pool_release() will save the pool to the factory's free list.
+ */
+ pj_size_t max_capacity;
+
+ /**
+ * Number of pools currently held by applications. This number gets
+ * incremented everytime #pj_pool_create() is called, and gets
+ * decremented when #pj_pool_release() is called.
+ */
+ pj_size_t used_count;
+
+ /**
+ * Lists of pools in the cache, indexed by pool size.
+ */
+ pj_list free_list[PJ_CACHING_POOL_ARRAY_SIZE];
+
+ /**
+ * List of pools currently allocated by applications.
+ */
+ pj_list used_list;
+};
+
+
+
+/**
+ * Initialize caching pool.
+ *
+ * @param ch_pool The caching pool factory to be initialized.
+ * @param policy Pool factory policy.
+ * @param max_capacity The total capacity to be retained in the cache. When
+ * the pool is returned to the cache, it will be kept in
+ * recycling list if the total capacity of pools in this
+ * list plus the capacity of the pool is still below this
+ * value.
+ */
+PJ_DECL(void) pj_caching_pool_init( pj_caching_pool *ch_pool,
+ const pj_pool_factory_policy *policy,
+ pj_size_t max_capacity);
+
+
+/**
+ * Destroy caching pool, and release all the pools in the recycling list.
+ *
+ * @param ch_pool The caching pool.
+ */
+PJ_DECL(void) pj_caching_pool_destroy( pj_caching_pool *ch_pool );
+
+/**
+ * @} // PJ_CACHING_POOL
+ */
+
+# if PJ_FUNCTIONS_ARE_INLINED
+# include "pool_i.h"
+# endif
+
+PJ_END_DECL
+
+#endif /* __PJ_POOL_H__ */
+
diff --git a/pjlib/include/pj/pool_i.h b/pjlib/include/pj/pool_i.h
index 4a99908..f23b52a 100644
--- a/pjlib/include/pj/pool_i.h
+++ b/pjlib/include/pj/pool_i.h
@@ -1,76 +1,97 @@
-/* $Id$
- *
- */
-
-
-#include <pj/string.h>
-
-PJ_DECL(void*) pj_pool_allocate_find(pj_pool_t *pool, unsigned size);
-
-PJ_IDEF(pj_size_t) pj_pool_get_capacity( pj_pool_t *pool )
-{
- return pool->capacity;
-}
-
-PJ_IDEF(pj_size_t) pj_pool_get_used_size( pj_pool_t *pool )
-{
- return pool->used_size;
-}
-
-PJ_IDEF(void*) pj_pool_alloc_from_block( pj_pool_t *pool,
- pj_pool_block *block, pj_size_t size )
-{
- /* The operation below is valid for size==0.
- * When size==0, the function will return the pointer to the pool
- * memory address, but no memory will be allocated.
- */
- if (size & (PJ_POOL_ALIGNMENT-1)) {
- size &= ~(PJ_POOL_ALIGNMENT-1);
- size += PJ_POOL_ALIGNMENT;
- }
- if ((unsigned)(block->end - block->cur) >= size) {
- void *ptr = block->cur;
- block->cur += size;
- pool->used_size += size;
- return ptr;
- }
- return NULL;
-}
-
-PJ_IDEF(void*) pj_pool_alloc( pj_pool_t *pool, pj_size_t size)
-{
- pj_pool_block *block = pool->block_list.next;
- void *ptr = pj_pool_alloc_from_block(pool, block, size);
- if (!ptr)
- ptr = pj_pool_allocate_find(pool, size);
- return ptr;
-}
-
-
-PJ_IDEF(void*) pj_pool_calloc( pj_pool_t *pool, pj_size_t count, pj_size_t size)
-{
- void *buf = pj_pool_alloc( pool, size*count);
- if (buf)
- pj_memset(buf, 0, size * count);
- return buf;
-}
-
-PJ_IDEF(const char *) pj_pool_getobjname( const pj_pool_t *pool )
-{
- return pool->obj_name;
-}
-
-PJ_IDEF(pj_pool_t*) pj_pool_create( pj_pool_factory *f,
- const char *name,
- pj_size_t initial_size,
- pj_size_t increment_size,
- pj_pool_callback *callback)
-{
- return (*f->create_pool)(f, name, initial_size, increment_size, callback);
-}
-
-PJ_IDEF(void) pj_pool_release( pj_pool_t *pool )
-{
- (*pool->factory->release_pool)(pool->factory, pool);
-}
-
+/* $Id$
+ *
+ */
+/*
+ * PJLIB - PJ Foundation Library
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ * Benny Prijono <bennylp@bulukucing.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+
+#include <pj/string.h>
+
+PJ_DECL(void*) pj_pool_allocate_find(pj_pool_t *pool, unsigned size);
+
+PJ_IDEF(pj_size_t) pj_pool_get_capacity( pj_pool_t *pool )
+{
+ return pool->capacity;
+}
+
+PJ_IDEF(pj_size_t) pj_pool_get_used_size( pj_pool_t *pool )
+{
+ return pool->used_size;
+}
+
+PJ_IDEF(void*) pj_pool_alloc_from_block( pj_pool_t *pool,
+ pj_pool_block *block, pj_size_t size )
+{
+ /* The operation below is valid for size==0.
+ * When size==0, the function will return the pointer to the pool
+ * memory address, but no memory will be allocated.
+ */
+ if (size & (PJ_POOL_ALIGNMENT-1)) {
+ size &= ~(PJ_POOL_ALIGNMENT-1);
+ size += PJ_POOL_ALIGNMENT;
+ }
+ if ((unsigned)(block->end - block->cur) >= size) {
+ void *ptr = block->cur;
+ block->cur += size;
+ pool->used_size += size;
+ return ptr;
+ }
+ return NULL;
+}
+
+PJ_IDEF(void*) pj_pool_alloc( pj_pool_t *pool, pj_size_t size)
+{
+ pj_pool_block *block = pool->block_list.next;
+ void *ptr = pj_pool_alloc_from_block(pool, block, size);
+ if (!ptr)
+ ptr = pj_pool_allocate_find(pool, size);
+ return ptr;
+}
+
+
+PJ_IDEF(void*) pj_pool_calloc( pj_pool_t *pool, pj_size_t count, pj_size_t size)
+{
+ void *buf = pj_pool_alloc( pool, size*count);
+ if (buf)
+ pj_memset(buf, 0, size * count);
+ return buf;
+}
+
+PJ_IDEF(const char *) pj_pool_getobjname( const pj_pool_t *pool )
+{
+ return pool->obj_name;
+}
+
+PJ_IDEF(pj_pool_t*) pj_pool_create( pj_pool_factory *f,
+ const char *name,
+ pj_size_t initial_size,
+ pj_size_t increment_size,
+ pj_pool_callback *callback)
+{
+ return (*f->create_pool)(f, name, initial_size, increment_size, callback);
+}
+
+PJ_IDEF(void) pj_pool_release( pj_pool_t *pool )
+{
+ (*pool->factory->release_pool)(pool->factory, pool);
+}
+
diff --git a/pjlib/include/pj/rand.h b/pjlib/include/pj/rand.h
index 0549c72..f431cd8 100644
--- a/pjlib/include/pj/rand.h
+++ b/pjlib/include/pj/rand.h
@@ -1,62 +1,83 @@
-/* $Id$
- *
- */
-/* $Log: /pjproject-0.3/pjlib/include/pj/rand.h $
- *
- * 3 10/14/05 12:26a Bennylp
- * Finished error code framework, some fixes in ioqueue, etc. Pretty
- * major.
- *
- * 2 9/17/05 10:37a Bennylp
- * Major reorganization towards version 0.3.
- *
- * 1 9/15/05 8:40p Bennylp
- * Created.
- */
-#ifndef __PJ_RAND_H__
-#define __PJ_RAND_H__
-
-/**
- * @file rand.h
- * @brief Random Number Generator.
- */
-
-#include <pj/config.h>
-
-PJ_BEGIN_DECL
-
-
-/**
- * @defgroup PJ_RAND Random Number Generator
- * @ingroup PJ_MISC
- * @{
- * This module contains functions for generating random numbers.
- * This abstraction is needed not only because not all platforms have
- * \a rand() and \a srand(), but also on some platforms \a rand()
- * only has 16-bit randomness, which is not good enough.
- */
-
-/**
- * Put in seed to random number generator.
- *
- * @param seed Seed value.
- */
-PJ_DECL(void) pj_srand(unsigned int seed);
-
-
-/**
- * Generate random integer with 32bit randomness.
- *
- * @return a random integer.
- */
-PJ_DECL(int) pj_rand(void);
-
-
-/** @} */
-
-
-PJ_END_DECL
-
-
-#endif /* __PJ_RAND_H__ */
-
+/* $Id$
+ *
+ */
+/*
+ * PJLIB - PJ Foundation Library
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ * Benny Prijono <bennylp@bulukucing.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+/* $Log: /pjproject-0.3/pjlib/include/pj/rand.h $
+ *
+ * 3 10/14/05 12:26a Bennylp
+ * Finished error code framework, some fixes in ioqueue, etc. Pretty
+ * major.
+ *
+ * 2 9/17/05 10:37a Bennylp
+ * Major reorganization towards version 0.3.
+ *
+ * 1 9/15/05 8:40p Bennylp
+ * Created.
+ */
+#ifndef __PJ_RAND_H__
+#define __PJ_RAND_H__
+
+/**
+ * @file rand.h
+ * @brief Random Number Generator.
+ */
+
+#include <pj/config.h>
+
+PJ_BEGIN_DECL
+
+
+/**
+ * @defgroup PJ_RAND Random Number Generator
+ * @ingroup PJ_MISC
+ * @{
+ * This module contains functions for generating random numbers.
+ * This abstraction is needed not only because not all platforms have
+ * \a rand() and \a srand(), but also on some platforms \a rand()
+ * only has 16-bit randomness, which is not good enough.
+ */
+
+/**
+ * Put in seed to random number generator.
+ *
+ * @param seed Seed value.
+ */
+PJ_DECL(void) pj_srand(unsigned int seed);
+
+
+/**
+ * Generate random integer with 32bit randomness.
+ *
+ * @return a random integer.
+ */
+PJ_DECL(int) pj_rand(void);
+
+
+/** @} */
+
+
+PJ_END_DECL
+
+
+#endif /* __PJ_RAND_H__ */
+
diff --git a/pjlib/include/pj/rbtree.h b/pjlib/include/pj/rbtree.h
index 67efe2e..06523a4 100644
--- a/pjlib/include/pj/rbtree.h
+++ b/pjlib/include/pj/rbtree.h
@@ -1,195 +1,216 @@
-/* $Id$
- *
- */
-
-#ifndef __PJ_RBTREE_H__
-#define __PJ_RBTREE_H__
-
-/**
- * @file rbtree.h
- * @brief Red/Black Tree
- */
-
-#include <pj/types.h>
-
-PJ_BEGIN_DECL
-
-/**
- * @defgroup PJ_RBTREE Red/Black Balanced Tree
- * @ingroup PJ_DS
- * @brief
- * Red/Black tree is the variant of balanced tree, where the search, insert,
- * and delete operation is \b guaranteed to take at most \a O( lg(n) ).
- * @{
- */
-/**
- * Color type for Red-Black tree.
- */
-typedef enum pj_rbcolor_t
-{
- PJ_RBCOLOR_BLACK,
- PJ_RBCOLOR_RED
-} pj_rbcolor_t;
-
-/**
- * The type of the node of the R/B Tree.
- */
-typedef struct pj_rbtree_node
-{
- /** Pointers to the node's parent, and left and right siblings. */
- struct pj_rbtree_node *parent, *left, *right;
-
- /** Key associated with the node. */
- const void *key;
-
- /** User data associated with the node. */
- void *user_data;
-
- /** The R/B Tree node color. */
- pj_rbcolor_t color;
-
-} pj_rbtree_node;
-
-
-/**
- * The type of function use to compare key value of tree node.
- * @return
- * 0 if the keys are equal
- * <0 if key1 is lower than key2
- * >0 if key1 is greater than key2.
- */
-typedef int pj_rbtree_comp(const void *key1, const void *key2);
-
-
-/**
- * Declaration of a red-black tree. All elements in the tree must have UNIQUE
- * key.
- * A red black tree always maintains the balance of the tree, so that the
- * tree height will not be greater than lg(N). Insert, search, and delete
- * operation will take lg(N) on the worst case. But for insert and delete,
- * there is additional time needed to maintain the balance of the tree.
- */
-typedef struct pj_rbtree
-{
- pj_rbtree_node null_node; /**< Constant to indicate NULL node. */
- pj_rbtree_node *null; /**< Constant to indicate NULL node. */
- pj_rbtree_node *root; /**< Root tree node. */
- unsigned size; /**< Number of elements in the tree. */
- pj_rbtree_comp *comp; /**< Key comparison function. */
-} pj_rbtree;
-
-
-/**
- * Guidance on how much memory required for each of the node.
- */
-#define PJ_RBTREE_NODE_SIZE (sizeof(pj_rbtree_node))
-
-
-/**
- * Guidance on memory required for the tree.
- */
-#define PJ_RBTREE_SIZE (sizeof(pj_rbtree))
-
-
-/**
- * Initialize the tree.
- * @param tree the tree to be initialized.
- * @param comp key comparison function to be used for this tree.
- */
-PJ_DECL(void) pj_rbtree_init( pj_rbtree *tree, pj_rbtree_comp *comp);
-
-/**
- * Get the first element in the tree.
- * The first element always has the least value for the key, according to
- * the comparison function.
- * @param tree the tree.
- * @return the tree node, or NULL if the tree has no element.
- */
-PJ_DECL(pj_rbtree_node*) pj_rbtree_first( pj_rbtree *tree );
-
-/**
- * Get the last element in the tree.
- * The last element always has the greatest key value, according to the
- * comparison function defined for the tree.
- * @param tree the tree.
- * @return the tree node, or NULL if the tree has no element.
- */
-PJ_DECL(pj_rbtree_node*) pj_rbtree_last( pj_rbtree *tree );
-
-/**
- * Get the successive element for the specified node.
- * The successive element is an element with greater key value.
- * @param tree the tree.
- * @param node the node.
- * @return the successive node, or NULL if the node has no successor.
- */
-PJ_DECL(pj_rbtree_node*) pj_rbtree_next( pj_rbtree *tree,
- pj_rbtree_node *node );
-
-/**
- * The the previous node for the specified node.
- * The previous node is an element with less key value.
- * @param tree the tree.
- * @param node the node.
- * @return the previous node, or NULL if the node has no previous node.
- */
-PJ_DECL(pj_rbtree_node*) pj_rbtree_prev( pj_rbtree *tree,
- pj_rbtree_node *node );
-
-/**
- * Insert a new node.
- * The node will be inserted at sorted location. The key of the node must
- * be UNIQUE, i.e. it hasn't existed in the tree.
- * @param tree the tree.
- * @param node the node to be inserted.
- * @return zero on success, or -1 if the key already exist.
- */
-PJ_DECL(int) pj_rbtree_insert( pj_rbtree *tree,
- pj_rbtree_node *node );
-
-/**
- * Find a node which has the specified key.
- * @param tree the tree.
- * @param key the key to search.
- * @return the tree node with the specified key, or NULL if the key can not
- * be found.
- */
-PJ_DECL(pj_rbtree_node*) pj_rbtree_find( pj_rbtree *tree,
- const void *key );
-
-/**
- * Erase a node from the tree.
- * @param tree the tree.
- * @param node the node to be erased.
- * @return the tree node itself.
- */
-PJ_DECL(pj_rbtree_node*) pj_rbtree_erase( pj_rbtree *tree,
- pj_rbtree_node *node );
-
-/**
- * Get the maximum tree height from the specified node.
- * @param tree the tree.
- * @param node the node, or NULL to get the root of the tree.
- * @return the maximum height, which should be at most lg(N)
- */
-PJ_DECL(unsigned) pj_rbtree_max_height( pj_rbtree *tree,
- pj_rbtree_node *node );
-
-/**
- * Get the minumum tree height from the specified node.
- * @param tree the tree.
- * @param node the node, or NULL to get the root of the tree.
- * @return the height
- */
-PJ_DECL(unsigned) pj_rbtree_min_height( pj_rbtree *tree,
- pj_rbtree_node *node );
-
-
-/**
- * @}
- */
-
-PJ_END_DECL
-
-#endif /* __PJ_RBTREE_H__ */
-
+/* $Id$
+ *
+ */
+/*
+ * PJLIB - PJ Foundation Library
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ * Benny Prijono <bennylp@bulukucing.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef __PJ_RBTREE_H__
+#define __PJ_RBTREE_H__
+
+/**
+ * @file rbtree.h
+ * @brief Red/Black Tree
+ */
+
+#include <pj/types.h>
+
+PJ_BEGIN_DECL
+
+/**
+ * @defgroup PJ_RBTREE Red/Black Balanced Tree
+ * @ingroup PJ_DS
+ * @brief
+ * Red/Black tree is the variant of balanced tree, where the search, insert,
+ * and delete operation is \b guaranteed to take at most \a O( lg(n) ).
+ * @{
+ */
+/**
+ * Color type for Red-Black tree.
+ */
+typedef enum pj_rbcolor_t
+{
+ PJ_RBCOLOR_BLACK,
+ PJ_RBCOLOR_RED
+} pj_rbcolor_t;
+
+/**
+ * The type of the node of the R/B Tree.
+ */
+typedef struct pj_rbtree_node
+{
+ /** Pointers to the node's parent, and left and right siblings. */
+ struct pj_rbtree_node *parent, *left, *right;
+
+ /** Key associated with the node. */
+ const void *key;
+
+ /** User data associated with the node. */
+ void *user_data;
+
+ /** The R/B Tree node color. */
+ pj_rbcolor_t color;
+
+} pj_rbtree_node;
+
+
+/**
+ * The type of function use to compare key value of tree node.
+ * @return
+ * 0 if the keys are equal
+ * <0 if key1 is lower than key2
+ * >0 if key1 is greater than key2.
+ */
+typedef int pj_rbtree_comp(const void *key1, const void *key2);
+
+
+/**
+ * Declaration of a red-black tree. All elements in the tree must have UNIQUE
+ * key.
+ * A red black tree always maintains the balance of the tree, so that the
+ * tree height will not be greater than lg(N). Insert, search, and delete
+ * operation will take lg(N) on the worst case. But for insert and delete,
+ * there is additional time needed to maintain the balance of the tree.
+ */
+typedef struct pj_rbtree
+{
+ pj_rbtree_node null_node; /**< Constant to indicate NULL node. */
+ pj_rbtree_node *null; /**< Constant to indicate NULL node. */
+ pj_rbtree_node *root; /**< Root tree node. */
+ unsigned size; /**< Number of elements in the tree. */
+ pj_rbtree_comp *comp; /**< Key comparison function. */
+} pj_rbtree;
+
+
+/**
+ * Guidance on how much memory required for each of the node.
+ */
+#define PJ_RBTREE_NODE_SIZE (sizeof(pj_rbtree_node))
+
+
+/**
+ * Guidance on memory required for the tree.
+ */
+#define PJ_RBTREE_SIZE (sizeof(pj_rbtree))
+
+
+/**
+ * Initialize the tree.
+ * @param tree the tree to be initialized.
+ * @param comp key comparison function to be used for this tree.
+ */
+PJ_DECL(void) pj_rbtree_init( pj_rbtree *tree, pj_rbtree_comp *comp);
+
+/**
+ * Get the first element in the tree.
+ * The first element always has the least value for the key, according to
+ * the comparison function.
+ * @param tree the tree.
+ * @return the tree node, or NULL if the tree has no element.
+ */
+PJ_DECL(pj_rbtree_node*) pj_rbtree_first( pj_rbtree *tree );
+
+/**
+ * Get the last element in the tree.
+ * The last element always has the greatest key value, according to the
+ * comparison function defined for the tree.
+ * @param tree the tree.
+ * @return the tree node, or NULL if the tree has no element.
+ */
+PJ_DECL(pj_rbtree_node*) pj_rbtree_last( pj_rbtree *tree );
+
+/**
+ * Get the successive element for the specified node.
+ * The successive element is an element with greater key value.
+ * @param tree the tree.
+ * @param node the node.
+ * @return the successive node, or NULL if the node has no successor.
+ */
+PJ_DECL(pj_rbtree_node*) pj_rbtree_next( pj_rbtree *tree,
+ pj_rbtree_node *node );
+
+/**
+ * The the previous node for the specified node.
+ * The previous node is an element with less key value.
+ * @param tree the tree.
+ * @param node the node.
+ * @return the previous node, or NULL if the node has no previous node.
+ */
+PJ_DECL(pj_rbtree_node*) pj_rbtree_prev( pj_rbtree *tree,
+ pj_rbtree_node *node );
+
+/**
+ * Insert a new node.
+ * The node will be inserted at sorted location. The key of the node must
+ * be UNIQUE, i.e. it hasn't existed in the tree.
+ * @param tree the tree.
+ * @param node the node to be inserted.
+ * @return zero on success, or -1 if the key already exist.
+ */
+PJ_DECL(int) pj_rbtree_insert( pj_rbtree *tree,
+ pj_rbtree_node *node );
+
+/**
+ * Find a node which has the specified key.
+ * @param tree the tree.
+ * @param key the key to search.
+ * @return the tree node with the specified key, or NULL if the key can not
+ * be found.
+ */
+PJ_DECL(pj_rbtree_node*) pj_rbtree_find( pj_rbtree *tree,
+ const void *key );
+
+/**
+ * Erase a node from the tree.
+ * @param tree the tree.
+ * @param node the node to be erased.
+ * @return the tree node itself.
+ */
+PJ_DECL(pj_rbtree_node*) pj_rbtree_erase( pj_rbtree *tree,
+ pj_rbtree_node *node );
+
+/**
+ * Get the maximum tree height from the specified node.
+ * @param tree the tree.
+ * @param node the node, or NULL to get the root of the tree.
+ * @return the maximum height, which should be at most lg(N)
+ */
+PJ_DECL(unsigned) pj_rbtree_max_height( pj_rbtree *tree,
+ pj_rbtree_node *node );
+
+/**
+ * Get the minumum tree height from the specified node.
+ * @param tree the tree.
+ * @param node the node, or NULL to get the root of the tree.
+ * @return the height
+ */
+PJ_DECL(unsigned) pj_rbtree_min_height( pj_rbtree *tree,
+ pj_rbtree_node *node );
+
+
+/**
+ * @}
+ */
+
+PJ_END_DECL
+
+#endif /* __PJ_RBTREE_H__ */
+
diff --git a/pjlib/include/pj/sock.h b/pjlib/include/pj/sock.h
index 6dfbaf2..28d40b4 100644
--- a/pjlib/include/pj/sock.h
+++ b/pjlib/include/pj/sock.h
@@ -1,83 +1,104 @@
-/* $Id$
- *
- */
-
-#ifndef __PJ_SOCK_H__
-#define __PJ_SOCK_H__
-
-/**
- * @file sock.h
- * @brief Socket Abstraction.
- */
-
-#include <pj/types.h>
-
-PJ_BEGIN_DECL
-
-
-/**
- * @defgroup PJ_SOCK Socket Abstraction
- * @ingroup PJ_IO
- * @{
- *
- * The PJLIB socket abstraction layer is a thin and very portable abstraction
- * for socket API. It provides API similar to BSD socket API. The abstraction
- * is needed because BSD socket API is not always available on all platforms,
- * therefore it wouldn't be possible to create a trully portable network
- * programs unless we provide such abstraction.
- *
- * Applications can use this API directly in their application, just
- * as they would when using traditional BSD socket API, provided they
- * call #pj_init() first.
- *
- * \section pj_sock_examples_sec Examples
- *
- * For some examples on how to use the socket API, please see:
- *
- * - \ref page_pjlib_sock_test
- * - \ref page_pjlib_select_test
- * - \ref page_pjlib_sock_perf_test
- */
-
-
-/**
- * Supported address families.
- * APPLICATION MUST USE THESE VALUES INSTEAD OF NORMAL AF_*, BECAUSE
- * THE LIBRARY WILL DO TRANSLATION TO THE NATIVE VALUE.
- */
-extern const pj_uint16_t PJ_AF_UNIX; /**< Unix domain socket. */
-#define PJ_AF_LOCAL PJ_AF_UNIX; /**< POSIX name for AF_UNIX */
-extern const pj_uint16_t PJ_AF_INET; /**< Internet IP protocol. */
-extern const pj_uint16_t PJ_AF_INET6; /**< IP version 6. */
-extern const pj_uint16_t PJ_AF_PACKET; /**< Packet family. */
-extern const pj_uint16_t PJ_AF_IRDA; /**< IRDA sockets. */
-
-
-/**
- * Supported types of sockets.
- * APPLICATION MUST USE THESE VALUES INSTEAD OF NORMAL SOCK_*, BECAUSE
- * THE LIBRARY WILL TRANSLATE THE VALUE TO THE NATIVE VALUE.
- */
-
-extern const pj_uint16_t PJ_SOCK_STREAM; /**< Sequenced, reliable, connection-
- based byte streams. */
-extern const pj_uint16_t PJ_SOCK_DGRAM; /**< Connectionless, unreliable
- datagrams of fixed maximum
- lengths. */
-extern const pj_uint16_t PJ_SOCK_RAW; /**< Raw protocol interface. */
-extern const pj_uint16_t PJ_SOCK_RDM; /**< Reliably-delivered messages. */
-
-
-/**
- * Socket level specified in #pj_sock_setsockopt() or #pj_sock_getsockopt().
- * APPLICATION MUST USE THESE VALUES INSTEAD OF NORMAL SOL_*, BECAUSE
- * THE LIBRARY WILL TRANSLATE THE VALUE TO THE NATIVE VALUE.
- */
-extern const pj_uint16_t PJ_SOL_SOCKET; /**< Socket level. */
-extern const pj_uint16_t PJ_SOL_IP; /**< IP level. */
-extern const pj_uint16_t PJ_SOL_TCP; /**< TCP level. */
-extern const pj_uint16_t PJ_SOL_UDP; /**< UDP level. */
-extern const pj_uint16_t PJ_SOL_IPV6; /**< IP version 6 */
+/* $Id$
+ *
+ */
+/*
+ * PJLIB - PJ Foundation Library
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ * Benny Prijono <bennylp@bulukucing.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef __PJ_SOCK_H__
+#define __PJ_SOCK_H__
+
+/**
+ * @file sock.h
+ * @brief Socket Abstraction.
+ */
+
+#include <pj/types.h>
+
+PJ_BEGIN_DECL
+
+
+/**
+ * @defgroup PJ_SOCK Socket Abstraction
+ * @ingroup PJ_IO
+ * @{
+ *
+ * The PJLIB socket abstraction layer is a thin and very portable abstraction
+ * for socket API. It provides API similar to BSD socket API. The abstraction
+ * is needed because BSD socket API is not always available on all platforms,
+ * therefore it wouldn't be possible to create a trully portable network
+ * programs unless we provide such abstraction.
+ *
+ * Applications can use this API directly in their application, just
+ * as they would when using traditional BSD socket API, provided they
+ * call #pj_init() first.
+ *
+ * \section pj_sock_examples_sec Examples
+ *
+ * For some examples on how to use the socket API, please see:
+ *
+ * - \ref page_pjlib_sock_test
+ * - \ref page_pjlib_select_test
+ * - \ref page_pjlib_sock_perf_test
+ */
+
+
+/**
+ * Supported address families.
+ * APPLICATION MUST USE THESE VALUES INSTEAD OF NORMAL AF_*, BECAUSE
+ * THE LIBRARY WILL DO TRANSLATION TO THE NATIVE VALUE.
+ */
+extern const pj_uint16_t PJ_AF_UNIX; /**< Unix domain socket. */
+#define PJ_AF_LOCAL PJ_AF_UNIX; /**< POSIX name for AF_UNIX */
+extern const pj_uint16_t PJ_AF_INET; /**< Internet IP protocol. */
+extern const pj_uint16_t PJ_AF_INET6; /**< IP version 6. */
+extern const pj_uint16_t PJ_AF_PACKET; /**< Packet family. */
+extern const pj_uint16_t PJ_AF_IRDA; /**< IRDA sockets. */
+
+
+/**
+ * Supported types of sockets.
+ * APPLICATION MUST USE THESE VALUES INSTEAD OF NORMAL SOCK_*, BECAUSE
+ * THE LIBRARY WILL TRANSLATE THE VALUE TO THE NATIVE VALUE.
+ */
+
+extern const pj_uint16_t PJ_SOCK_STREAM; /**< Sequenced, reliable, connection-
+ based byte streams. */
+extern const pj_uint16_t PJ_SOCK_DGRAM; /**< Connectionless, unreliable
+ datagrams of fixed maximum
+ lengths. */
+extern const pj_uint16_t PJ_SOCK_RAW; /**< Raw protocol interface. */
+extern const pj_uint16_t PJ_SOCK_RDM; /**< Reliably-delivered messages. */
+
+
+/**
+ * Socket level specified in #pj_sock_setsockopt() or #pj_sock_getsockopt().
+ * APPLICATION MUST USE THESE VALUES INSTEAD OF NORMAL SOL_*, BECAUSE
+ * THE LIBRARY WILL TRANSLATE THE VALUE TO THE NATIVE VALUE.
+ */
+extern const pj_uint16_t PJ_SOL_SOCKET; /**< Socket level. */
+extern const pj_uint16_t PJ_SOL_IP; /**< IP level. */
+extern const pj_uint16_t PJ_SOL_TCP; /**< TCP level. */
+extern const pj_uint16_t PJ_SOL_UDP; /**< UDP level. */
+extern const pj_uint16_t PJ_SOL_IPV6; /**< IP version 6 */
/**
* Values to be specified as \c optname when calling #pj_sock_setsockopt()
@@ -87,600 +108,600 @@
extern const pj_uint16_t PJ_SO_RCVBUF; /**< Buffer size for receive. */
extern const pj_uint16_t PJ_SO_SNDBUF; /**< Buffer size for send. */
-
-/**
- * Flags to be specified in #pj_sock_recv, #pj_sock_send, etc.
- */
-typedef enum pj_sock_msg_flag
-{
- PJ_MSG_OOB = 0x01, /**< Out-of-band messages. */
- PJ_MSG_PEEK = 0x02, /**< Peek, don't remove from buffer. */
- PJ_MSG_DONTROUTE = 0x04, /**< Don't route. */
-} pj_sock_msg_flag;
-
-
-/**
- * Flag to be specified in #pj_sock_shutdown.
- */
-typedef enum pj_socket_sd_type
-{
- PJ_SD_RECEIVE = 0, /**< No more receive. */
- PJ_SHUT_RD = 0, /**< Alias for SD_RECEIVE. */
- PJ_SD_SEND = 1, /**< No more sending. */
- PJ_SHUT_WR = 1, /**< Alias for SD_SEND. */
- PJ_SD_BOTH = 2, /**< No more send and receive. */
- PJ_SHUT_RDWR = 2, /**< Alias for SD_BOTH. */
-} pj_socket_sd_type;
-
-
-
-/** Address to accept any incoming messages. */
-#define PJ_INADDR_ANY ((pj_uint32_t)0)
-
-/** Address indicating an error return */
-#define PJ_INADDR_NONE ((pj_uint32_t)0xffffffff)
-
-/** Address to send to all hosts. */
-#define PJ_INADDR_BROADCAST ((pj_uint32_t)0xffffffff)
-
-
-/**
- * Maximum length specifiable by #pj_sock_listen().
- * If the build system doesn't override this value, then the lowest
- * denominator (five, in Win32 systems) will be used.
- */
-#if !defined(PJ_SOMAXCONN)
-# define PJ_SOMAXCONN 5
-#endif
-
-
-/**
- * Constant for invalid socket returned by #pj_sock_socket() and
- * #pj_sock_accept().
- */
-#define PJ_INVALID_SOCKET (-1)
-
-/**
- * Structure describing a generic socket address.
- */
-typedef struct pj_sockaddr
-{
- pj_uint16_t sa_family; /**< Common data: address family. */
- char sa_data[14]; /**< Address data. */
-} pj_sockaddr;
-
-
-/**
- * This structure describes Internet address.
- */
-typedef struct pj_in_addr
-{
- pj_uint32_t s_addr; /**< The 32bit IP address. */
-} pj_in_addr;
-
-
-/**
- * This structure describes Internet socket address.
- */
-typedef struct pj_sockaddr_in
-{
- pj_uint16_t sin_family; /**< Address family. */
- pj_uint16_t sin_port; /**< Transport layer port number. */
- pj_in_addr sin_addr; /**< IP address. */
- char sin_zero[8]; /**< Padding. */
-} pj_sockaddr_in;
-
-
-/**
- * This structure describes IPv6 address.
- */
-typedef struct pj_in6_addr
-{
- /** Union of address formats. */
- union {
- pj_uint8_t u6_addr8[16]; /**< u6_addr8 */
- pj_uint16_t u6_addr16[8]; /**< u6_addr16 */
- pj_uint32_t u6_addr32[4]; /**< u6_addr32 */
- } in6_u;
-/** Shortcut to access in6_u.u6_addr8. */
-#define s6_addr in6_u.u6_addr8
-/** Shortcut to access in6_u.u6_addr16. */
-#define s6_addr16 in6_u.u6_addr16
-/** Shortcut to access in6_u.u6_addr32. */
-#define s6_addr32 in6_u.u6_addr32
-} pj_in6_addr;
-
-/** Initializer value for pj_in6_addr. */
-#define PJ_IN6ADDR_ANY_INIT { { { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 } } }
-
-/** Initializer value for pj_in6_addr. */
-#define PJ_IN6ADDR_LOOPBACK_INIT { { { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1 } } }
-
-/**
- * This structure describes IPv6 socket address.
- */
-typedef struct pj_sockaddr_in6
-{
- pj_uint16_t sin6_family; /**< Address family */
- pj_uint16_t sin6_port; /**< Transport layer port number. */
- pj_uint32_t sin6_flowinfo; /**< IPv6 flow information */
- pj_in6_addr sin6_addr; /**< IPv6 address. */
- pj_uint32_t sin6_scope_id; /**< IPv6 scope-id */
-} pj_sockaddr_in6;
-
-
-/*****************************************************************************
- *
- * SOCKET ADDRESS MANIPULATION.
- *
- *****************************************************************************
- */
-
-/**
- * Convert 16-bit value from network byte order to host byte order.
- *
- * @param netshort 16-bit network value.
- * @return 16-bit host value.
- */
-PJ_DECL(pj_uint16_t) pj_ntohs(pj_uint16_t netshort);
-
-/**
- * Convert 16-bit value from host byte order to network byte order.
- *
- * @param hostshort 16-bit host value.
- * @return 16-bit network value.
- */
-PJ_DECL(pj_uint16_t) pj_htons(pj_uint16_t hostshort);
-
-/**
- * Convert 32-bit value from network byte order to host byte order.
- *
- * @param netlong 32-bit network value.
- * @return 32-bit host value.
- */
-PJ_DECL(pj_uint32_t) pj_ntohl(pj_uint32_t netlong);
-
-/**
- * Convert 32-bit value from host byte order to network byte order.
- *
- * @param hostlong 32-bit host value.
- * @return 32-bit network value.
- */
-PJ_DECL(pj_uint32_t) pj_htonl(pj_uint32_t hostlong);
-
-/**
- * Convert an Internet host address given in network byte order
- * to string in standard numbers and dots notation.
- *
- * @param inaddr The host address.
- * @return The string address.
- */
-PJ_DECL(char*) pj_inet_ntoa(pj_in_addr inaddr);
-
-/**
- * This function converts the Internet host address cp from the standard
- * numbers-and-dots notation into binary data and stores it in the structure
- * that inp points to.
- *
- * @param cp IP address in standard numbers-and-dots notation.
- * @param inp Structure that holds the output of the conversion.
- *
- * @return nonzero if the address is valid, zero if not.
- */
-PJ_DECL(int) pj_inet_aton(const pj_str_t *cp, struct pj_in_addr *inp);
-
-/**
- * Convert address string with numbers and dots to binary IP address.
- *
- * @param cp The IP address in numbers and dots notation.
- * @return If success, the IP address is returned in network
- * byte order. If failed, PJ_INADDR_NONE will be
- * returned.
- * @remark
- * This is an obsolete interface to #pj_inet_aton(); it is obsolete
- * because -1 is a valid address (255.255.255.255), and #pj_inet_aton()
- * provides a cleaner way to indicate error return.
- */
-PJ_DECL(pj_in_addr) pj_inet_addr(const pj_str_t *cp);
-
-
-/**
- * Get the transport layer port number of an Internet socket address.
- * The port is returned in host byte order.
- *
- * @param addr The IP socket address.
- * @return Port number, in host byte order.
- */
-PJ_INLINE(pj_uint16_t) pj_sockaddr_in_get_port(const pj_sockaddr_in *addr)
-{
- return pj_ntohs(addr->sin_port);
-}
-
-/**
- * Set the port number of an Internet socket address.
- *
- * @param addr The IP socket address.
- * @param hostport The port number, in host byte order.
- */
-PJ_INLINE(void) pj_sockaddr_in_set_port(pj_sockaddr_in *addr,
- pj_uint16_t hostport)
-{
- addr->sin_port = pj_htons(hostport);
-}
-
-/**
- * Get the IP address of an Internet socket address.
- * The address is returned as 32bit value in host byte order.
- *
- * @param addr The IP socket address.
- * @return 32bit address, in host byte order.
- */
-PJ_INLINE(pj_in_addr) pj_sockaddr_in_get_addr(const pj_sockaddr_in *addr)
-{
- pj_in_addr in_addr;
- in_addr.s_addr = pj_ntohl(addr->sin_addr.s_addr);
- return in_addr;
-};
-
-/**
- * Set the IP address of an Internet socket address.
- *
- * @param addr The IP socket address.
- * @param hostaddr The host address, in host byte order.
- */
-PJ_INLINE(void) pj_sockaddr_in_set_addr(pj_sockaddr_in *addr,
- pj_uint32_t hostaddr)
-{
- addr->sin_addr.s_addr = pj_htonl(hostaddr);
-}
-
-/**
- * Set the IP address of an IP socket address from string address,
- * with resolving the host if necessary. The string address may be in a
- * standard numbers and dots notation or may be a hostname. If hostname
- * is specified, then the function will resolve the host into the IP
- * address.
- *
- * @param addr The IP socket address to be set.
- * @param cp The address string, which can be in a standard
- * dotted numbers or a hostname to be resolved.
- *
- * @return Zero on success.
- */
-PJ_DECL(pj_status_t) pj_sockaddr_in_set_str_addr( pj_sockaddr_in *addr,
- const pj_str_t *cp);
-
-/**
- * Set the IP address and port of an IP socket address.
- * The string address may be in a standard numbers and dots notation or
- * may be a hostname. If hostname is specified, then the function will
- * resolve the host into the IP address.
- *
- * @param addr The IP socket address to be set.
- * @param cp The address string, which can be in a standard
- * dotted numbers or a hostname to be resolved.
- * @param port The port number, in host byte order.
- *
- * @return Zero on success.
- */
-PJ_DECL(pj_status_t) pj_sockaddr_in_init( pj_sockaddr_in *addr,
- const pj_str_t *cp,
- pj_uint16_t port);
-
-
-/*****************************************************************************
- *
- * HOST NAME AND ADDRESS.
- *
- *****************************************************************************
- */
-
-/**
- * Get system's host name.
- *
- * @return The hostname, or empty string if the hostname can not
- * be identified.
- */
-PJ_DECL(const pj_str_t*) pj_gethostname(void);
-
-/**
- * Get host's IP address, which the the first IP address that is resolved
- * from the hostname.
- *
- * @return The host's IP address, PJ_INADDR_NONE if the host
- * IP address can not be identified.
- */
-PJ_DECL(pj_in_addr) pj_gethostaddr(void);
-
-
-/*****************************************************************************
- *
- * SOCKET API.
- *
- *****************************************************************************
- */
-
-/**
- * Create new socket/endpoint for communication.
- *
- * @param family Specifies a communication domain; this selects the
- * protocol family which will be used for communication.
- * @param type The socket has the indicated type, which specifies the
- * communication semantics.
- * @param protocol Specifies a particular protocol to be used with the
- * socket. Normally only a single protocol exists to support
- * a particular socket type within a given protocol family,
- * in which a case protocol can be specified as 0.
- * @param sock New socket descriptor, or PJ_INVALID_SOCKET on error.
- *
- * @return Zero on success.
- */
-PJ_DECL(pj_status_t) pj_sock_socket(int family,
- int type,
- int protocol,
- pj_sock_t *sock);
-
-/**
- * Close the socket descriptor.
- *
- * @param sockfd The socket descriptor.
- *
- * @return Zero on success.
- */
-PJ_DECL(pj_status_t) pj_sock_close(pj_sock_t sockfd);
-
-/**
- * This function gives the socket sockfd the local address my_addr. my_addr is
- * addrlen bytes long. Traditionally, this is called assigning a name to
- * a socket. When a socket is created with #pj_sock_socket(), it exists in a
- * name space (address family) but has no name assigned.
- *
- * @param sockfd The socket desriptor.
- * @param my_addr The local address to bind the socket to.
- * @param addrlen The length of the address.
- *
- * @return Zero on success.
- */
-PJ_DECL(pj_status_t) pj_sock_bind( pj_sock_t sockfd,
- const pj_sockaddr_t *my_addr,
- int addrlen);
-
-/**
- * Bind the IP socket sockfd to the given address and port.
- *
- * @param sockfd The socket descriptor.
- * @param addr Local address to bind the socket to, in host byte order.
- * @param port The local port to bind the socket to, in host byte order.
- *
- * @return Zero on success.
- */
-PJ_DECL(pj_status_t) pj_sock_bind_in( pj_sock_t sockfd,
- pj_uint32_t addr,
- pj_uint16_t port);
-
-#if PJ_HAS_TCP
-/**
- * Listen for incoming connection. This function only applies to connection
- * oriented sockets (such as PJ_SOCK_STREAM or PJ_SOCK_SEQPACKET), and it
- * indicates the willingness to accept incoming connections.
- *
- * @param sockfd The socket descriptor.
- * @param backlog Defines the maximum length the queue of pending
- * connections may grow to.
- *
- * @return Zero on success.
- */
-PJ_DECL(pj_status_t) pj_sock_listen( pj_sock_t sockfd,
- int backlog );
-
-/**
- * Accept new connection on the specified connection oriented server socket.
- *
- * @param serverfd The server socket.
- * @param newsock New socket on success, of PJ_INVALID_SOCKET if failed.
- * @param addr A pointer to sockaddr type. If the argument is not NULL,
- * it will be filled by the address of connecting entity.
- * @param addrlen Initially specifies the length of the address, and upon
- * return will be filled with the exact address length.
- *
- * @return Zero on success, or the error number.
- */
-PJ_DECL(pj_status_t) pj_sock_accept( pj_sock_t serverfd,
- pj_sock_t *newsock,
- pj_sockaddr_t *addr,
- int *addrlen);
-#endif
-
-/**
- * The file descriptor sockfd must refer to a socket. If the socket is of
- * type PJ_SOCK_DGRAM then the serv_addr address is the address to which
- * datagrams are sent by default, and the only address from which datagrams
- * are received. If the socket is of type PJ_SOCK_STREAM or PJ_SOCK_SEQPACKET,
- * this call attempts to make a connection to another socket. The
- * other socket is specified by serv_addr, which is an address (of length
- * addrlen) in the communications space of the socket. Each communications
- * space interprets the serv_addr parameter in its own way.
- *
- * @param sockfd The socket descriptor.
- * @param serv_addr Server address to connect to.
- * @param addrlen The length of server address.
- *
- * @return Zero on success.
- */
-PJ_DECL(pj_status_t) pj_sock_connect( pj_sock_t sockfd,
- const pj_sockaddr_t *serv_addr,
- int addrlen);
-
-/**
- * Return the address of peer which is connected to socket sockfd.
- *
- * @param sockfd The socket descriptor.
- * @param addr Pointer to sockaddr structure to which the address
- * will be returned.
- * @param namelen Initially the length of the addr. Upon return the value
- * will be set to the actual length of the address.
- *
- * @return Zero on success.
- */
-PJ_DECL(pj_status_t) pj_sock_getpeername(pj_sock_t sockfd,
- pj_sockaddr_t *addr,
- int *namelen);
-
-/**
- * Return the current name of the specified socket.
- *
- * @param sockfd The socket descriptor.
- * @param addr Pointer to sockaddr structure to which the address
- * will be returned.
- * @param namelen Initially the length of the addr. Upon return the value
- * will be set to the actual length of the address.
- *
- * @return Zero on success.
- */
-PJ_DECL(pj_status_t) pj_sock_getsockname( pj_sock_t sockfd,
- pj_sockaddr_t *addr,
- int *namelen);
-
-/**
- * Get socket option associated with a socket. Options may exist at multiple
- * protocol levels; they are always present at the uppermost socket level.
- *
- * @param sockfd The socket descriptor.
- * @param level The level which to get the option from.
- * @param optname The option name.
- * @param optval Identifies the buffer which the value will be
- * returned.
- * @param optlen Initially contains the length of the buffer, upon
- * return will be set to the actual size of the value.
- *
- * @return Zero on success.
- */
-PJ_DECL(pj_status_t) pj_sock_getsockopt( pj_sock_t sockfd,
- pj_uint16_t level,
- pj_uint16_t optname,
- void *optval,
- int *optlen);
-/**
- * Manipulate the options associated with a socket. Options may exist at
- * multiple protocol levels; they are always present at the uppermost socket
- * level.
- *
- * @param sockfd The socket descriptor.
- * @param level The level which to get the option from.
- * @param optname The option name.
- * @param optval Identifies the buffer which contain the value.
- * @param optlen The length of the value.
- *
- * @return PJ_SUCCESS or the status code.
- */
-PJ_DECL(pj_status_t) pj_sock_setsockopt( pj_sock_t sockfd,
- pj_uint16_t level,
- pj_uint16_t optname,
- const void *optval,
- int optlen);
-
-
-/**
- * Receives data stream or message coming to the specified socket.
- *
- * @param sockfd The socket descriptor.
- * @param buf The buffer to receive the data or message.
- * @param len On input, the length of the buffer. On return,
- * contains the length of data received.
- * @param flags Combination of #pj_sock_msg_flag.
- *
- * @return PJ_SUCCESS or the error code.
- */
-PJ_DECL(pj_status_t) pj_sock_recv(pj_sock_t sockfd,
- void *buf,
- pj_ssize_t *len,
- unsigned flags);
-
-/**
- * Receives data stream or message coming to the specified socket.
- *
- * @param sockfd The socket descriptor.
- * @param buf The buffer to receive the data or message.
- * @param len On input, the length of the buffer. On return,
- * contains the length of data received.
- * @param flags Bitmask combination of #pj_sock_msg_flag.
- * @param from If not NULL, it will be filled with the source
- * address of the connection.
- * @param fromlen Initially contains the length of from address,
- * and upon return will be filled with the actual
- * length of the address.
- *
- * @return PJ_SUCCESS or the error code.
- */
-PJ_DECL(pj_status_t) pj_sock_recvfrom( pj_sock_t sockfd,
- void *buf,
- pj_ssize_t *len,
- unsigned flags,
- pj_sockaddr_t *from,
- int *fromlen);
-
-/**
- * Transmit data to the socket.
- *
- * @param sockfd Socket descriptor.
- * @param buf Buffer containing data to be sent.
- * @param len On input, the length of the data in the buffer.
- * Upon return, it will be filled with the length
- * of data sent.
- * @param flags Bitmask combination of #pj_sock_msg_flag.
- *
- * @return PJ_SUCCESS or the status code.
- */
-PJ_DECL(pj_status_t) pj_sock_send(pj_sock_t sockfd,
- const void *buf,
- pj_ssize_t *len,
- unsigned flags);
-
-/**
- * Transmit data to the socket to the specified address.
- *
- * @param sockfd Socket descriptor.
- * @param buf Buffer containing data to be sent.
- * @param len On input, the length of the data in the buffer.
- * Upon return, it will be filled with the length
- * of data sent.
- * @param flags Bitmask combination of #pj_sock_msg_flag.
- * @param to The address to send.
- * @param tolen The length of the address in bytes.
- *
- * @return The length of data successfully sent.
- */
-PJ_DECL(pj_status_t) pj_sock_sendto(pj_sock_t sockfd,
- const void *buf,
- pj_ssize_t *len,
- unsigned flags,
- const pj_sockaddr_t *to,
- int tolen);
-
-#if PJ_HAS_TCP
-/**
- * The shutdown call causes all or part of a full-duplex connection on the
- * socket associated with sockfd to be shut down.
- *
- * @param sockfd The socket descriptor.
- * @param how If how is PJ_SHUT_RD, further receptions will be
- * disallowed. If how is PJ_SHUT_WR, further transmissions
- * will be disallowed. If how is PJ_SHUT_RDWR, further
- * receptions andtransmissions will be disallowed.
- *
- * @return Zero on success.
- */
-PJ_DECL(pj_status_t) pj_sock_shutdown( pj_sock_t sockfd,
- int how);
-#endif
-
-/**
- * @}
- */
-
-
-PJ_END_DECL
-
-#endif /* __PJ_SOCK_H__ */
-
+/**
+ * Flags to be specified in #pj_sock_recv, #pj_sock_send, etc.
+ */
+typedef enum pj_sock_msg_flag
+{
+ PJ_MSG_OOB = 0x01, /**< Out-of-band messages. */
+ PJ_MSG_PEEK = 0x02, /**< Peek, don't remove from buffer. */
+ PJ_MSG_DONTROUTE = 0x04, /**< Don't route. */
+} pj_sock_msg_flag;
+
+
+/**
+ * Flag to be specified in #pj_sock_shutdown.
+ */
+typedef enum pj_socket_sd_type
+{
+ PJ_SD_RECEIVE = 0, /**< No more receive. */
+ PJ_SHUT_RD = 0, /**< Alias for SD_RECEIVE. */
+ PJ_SD_SEND = 1, /**< No more sending. */
+ PJ_SHUT_WR = 1, /**< Alias for SD_SEND. */
+ PJ_SD_BOTH = 2, /**< No more send and receive. */
+ PJ_SHUT_RDWR = 2, /**< Alias for SD_BOTH. */
+} pj_socket_sd_type;
+
+
+
+/** Address to accept any incoming messages. */
+#define PJ_INADDR_ANY ((pj_uint32_t)0)
+
+/** Address indicating an error return */
+#define PJ_INADDR_NONE ((pj_uint32_t)0xffffffff)
+
+/** Address to send to all hosts. */
+#define PJ_INADDR_BROADCAST ((pj_uint32_t)0xffffffff)
+
+
+/**
+ * Maximum length specifiable by #pj_sock_listen().
+ * If the build system doesn't override this value, then the lowest
+ * denominator (five, in Win32 systems) will be used.
+ */
+#if !defined(PJ_SOMAXCONN)
+# define PJ_SOMAXCONN 5
+#endif
+
+
+/**
+ * Constant for invalid socket returned by #pj_sock_socket() and
+ * #pj_sock_accept().
+ */
+#define PJ_INVALID_SOCKET (-1)
+
+/**
+ * Structure describing a generic socket address.
+ */
+typedef struct pj_sockaddr
+{
+ pj_uint16_t sa_family; /**< Common data: address family. */
+ char sa_data[14]; /**< Address data. */
+} pj_sockaddr;
+
+
+/**
+ * This structure describes Internet address.
+ */
+typedef struct pj_in_addr
+{
+ pj_uint32_t s_addr; /**< The 32bit IP address. */
+} pj_in_addr;
+
+
+/**
+ * This structure describes Internet socket address.
+ */
+typedef struct pj_sockaddr_in
+{
+ pj_uint16_t sin_family; /**< Address family. */
+ pj_uint16_t sin_port; /**< Transport layer port number. */
+ pj_in_addr sin_addr; /**< IP address. */
+ char sin_zero[8]; /**< Padding. */
+} pj_sockaddr_in;
+
+
+/**
+ * This structure describes IPv6 address.
+ */
+typedef struct pj_in6_addr
+{
+ /** Union of address formats. */
+ union {
+ pj_uint8_t u6_addr8[16]; /**< u6_addr8 */
+ pj_uint16_t u6_addr16[8]; /**< u6_addr16 */
+ pj_uint32_t u6_addr32[4]; /**< u6_addr32 */
+ } in6_u;
+/** Shortcut to access in6_u.u6_addr8. */
+#define s6_addr in6_u.u6_addr8
+/** Shortcut to access in6_u.u6_addr16. */
+#define s6_addr16 in6_u.u6_addr16
+/** Shortcut to access in6_u.u6_addr32. */
+#define s6_addr32 in6_u.u6_addr32
+} pj_in6_addr;
+
+/** Initializer value for pj_in6_addr. */
+#define PJ_IN6ADDR_ANY_INIT { { { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 } } }
+
+/** Initializer value for pj_in6_addr. */
+#define PJ_IN6ADDR_LOOPBACK_INIT { { { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1 } } }
+
+/**
+ * This structure describes IPv6 socket address.
+ */
+typedef struct pj_sockaddr_in6
+{
+ pj_uint16_t sin6_family; /**< Address family */
+ pj_uint16_t sin6_port; /**< Transport layer port number. */
+ pj_uint32_t sin6_flowinfo; /**< IPv6 flow information */
+ pj_in6_addr sin6_addr; /**< IPv6 address. */
+ pj_uint32_t sin6_scope_id; /**< IPv6 scope-id */
+} pj_sockaddr_in6;
+
+
+/*****************************************************************************
+ *
+ * SOCKET ADDRESS MANIPULATION.
+ *
+ *****************************************************************************
+ */
+
+/**
+ * Convert 16-bit value from network byte order to host byte order.
+ *
+ * @param netshort 16-bit network value.
+ * @return 16-bit host value.
+ */
+PJ_DECL(pj_uint16_t) pj_ntohs(pj_uint16_t netshort);
+
+/**
+ * Convert 16-bit value from host byte order to network byte order.
+ *
+ * @param hostshort 16-bit host value.
+ * @return 16-bit network value.
+ */
+PJ_DECL(pj_uint16_t) pj_htons(pj_uint16_t hostshort);
+
+/**
+ * Convert 32-bit value from network byte order to host byte order.
+ *
+ * @param netlong 32-bit network value.
+ * @return 32-bit host value.
+ */
+PJ_DECL(pj_uint32_t) pj_ntohl(pj_uint32_t netlong);
+
+/**
+ * Convert 32-bit value from host byte order to network byte order.
+ *
+ * @param hostlong 32-bit host value.
+ * @return 32-bit network value.
+ */
+PJ_DECL(pj_uint32_t) pj_htonl(pj_uint32_t hostlong);
+
+/**
+ * Convert an Internet host address given in network byte order
+ * to string in standard numbers and dots notation.
+ *
+ * @param inaddr The host address.
+ * @return The string address.
+ */
+PJ_DECL(char*) pj_inet_ntoa(pj_in_addr inaddr);
+
+/**
+ * This function converts the Internet host address cp from the standard
+ * numbers-and-dots notation into binary data and stores it in the structure
+ * that inp points to.
+ *
+ * @param cp IP address in standard numbers-and-dots notation.
+ * @param inp Structure that holds the output of the conversion.
+ *
+ * @return nonzero if the address is valid, zero if not.
+ */
+PJ_DECL(int) pj_inet_aton(const pj_str_t *cp, struct pj_in_addr *inp);
+
+/**
+ * Convert address string with numbers and dots to binary IP address.
+ *
+ * @param cp The IP address in numbers and dots notation.
+ * @return If success, the IP address is returned in network
+ * byte order. If failed, PJ_INADDR_NONE will be
+ * returned.
+ * @remark
+ * This is an obsolete interface to #pj_inet_aton(); it is obsolete
+ * because -1 is a valid address (255.255.255.255), and #pj_inet_aton()
+ * provides a cleaner way to indicate error return.
+ */
+PJ_DECL(pj_in_addr) pj_inet_addr(const pj_str_t *cp);
+
+
+/**
+ * Get the transport layer port number of an Internet socket address.
+ * The port is returned in host byte order.
+ *
+ * @param addr The IP socket address.
+ * @return Port number, in host byte order.
+ */
+PJ_INLINE(pj_uint16_t) pj_sockaddr_in_get_port(const pj_sockaddr_in *addr)
+{
+ return pj_ntohs(addr->sin_port);
+}
+
+/**
+ * Set the port number of an Internet socket address.
+ *
+ * @param addr The IP socket address.
+ * @param hostport The port number, in host byte order.
+ */
+PJ_INLINE(void) pj_sockaddr_in_set_port(pj_sockaddr_in *addr,
+ pj_uint16_t hostport)
+{
+ addr->sin_port = pj_htons(hostport);
+}
+
+/**
+ * Get the IP address of an Internet socket address.
+ * The address is returned as 32bit value in host byte order.
+ *
+ * @param addr The IP socket address.
+ * @return 32bit address, in host byte order.
+ */
+PJ_INLINE(pj_in_addr) pj_sockaddr_in_get_addr(const pj_sockaddr_in *addr)
+{
+ pj_in_addr in_addr;
+ in_addr.s_addr = pj_ntohl(addr->sin_addr.s_addr);
+ return in_addr;
+};
+
+/**
+ * Set the IP address of an Internet socket address.
+ *
+ * @param addr The IP socket address.
+ * @param hostaddr The host address, in host byte order.
+ */
+PJ_INLINE(void) pj_sockaddr_in_set_addr(pj_sockaddr_in *addr,
+ pj_uint32_t hostaddr)
+{
+ addr->sin_addr.s_addr = pj_htonl(hostaddr);
+}
+
+/**
+ * Set the IP address of an IP socket address from string address,
+ * with resolving the host if necessary. The string address may be in a
+ * standard numbers and dots notation or may be a hostname. If hostname
+ * is specified, then the function will resolve the host into the IP
+ * address.
+ *
+ * @param addr The IP socket address to be set.
+ * @param cp The address string, which can be in a standard
+ * dotted numbers or a hostname to be resolved.
+ *
+ * @return Zero on success.
+ */
+PJ_DECL(pj_status_t) pj_sockaddr_in_set_str_addr( pj_sockaddr_in *addr,
+ const pj_str_t *cp);
+
+/**
+ * Set the IP address and port of an IP socket address.
+ * The string address may be in a standard numbers and dots notation or
+ * may be a hostname. If hostname is specified, then the function will
+ * resolve the host into the IP address.
+ *
+ * @param addr The IP socket address to be set.
+ * @param cp The address string, which can be in a standard
+ * dotted numbers or a hostname to be resolved.
+ * @param port The port number, in host byte order.
+ *
+ * @return Zero on success.
+ */
+PJ_DECL(pj_status_t) pj_sockaddr_in_init( pj_sockaddr_in *addr,
+ const pj_str_t *cp,
+ pj_uint16_t port);
+
+
+/*****************************************************************************
+ *
+ * HOST NAME AND ADDRESS.
+ *
+ *****************************************************************************
+ */
+
+/**
+ * Get system's host name.
+ *
+ * @return The hostname, or empty string if the hostname can not
+ * be identified.
+ */
+PJ_DECL(const pj_str_t*) pj_gethostname(void);
+
+/**
+ * Get host's IP address, which the the first IP address that is resolved
+ * from the hostname.
+ *
+ * @return The host's IP address, PJ_INADDR_NONE if the host
+ * IP address can not be identified.
+ */
+PJ_DECL(pj_in_addr) pj_gethostaddr(void);
+
+
+/*****************************************************************************
+ *
+ * SOCKET API.
+ *
+ *****************************************************************************
+ */
+
+/**
+ * Create new socket/endpoint for communication.
+ *
+ * @param family Specifies a communication domain; this selects the
+ * protocol family which will be used for communication.
+ * @param type The socket has the indicated type, which specifies the
+ * communication semantics.
+ * @param protocol Specifies a particular protocol to be used with the
+ * socket. Normally only a single protocol exists to support
+ * a particular socket type within a given protocol family,
+ * in which a case protocol can be specified as 0.
+ * @param sock New socket descriptor, or PJ_INVALID_SOCKET on error.
+ *
+ * @return Zero on success.
+ */
+PJ_DECL(pj_status_t) pj_sock_socket(int family,
+ int type,
+ int protocol,
+ pj_sock_t *sock);
+
+/**
+ * Close the socket descriptor.
+ *
+ * @param sockfd The socket descriptor.
+ *
+ * @return Zero on success.
+ */
+PJ_DECL(pj_status_t) pj_sock_close(pj_sock_t sockfd);
+
+
+/**
+ * This function gives the socket sockfd the local address my_addr. my_addr is
+ * addrlen bytes long. Traditionally, this is called assigning a name to
+ * a socket. When a socket is created with #pj_sock_socket(), it exists in a
+ * name space (address family) but has no name assigned.
+ *
+ * @param sockfd The socket desriptor.
+ * @param my_addr The local address to bind the socket to.
+ * @param addrlen The length of the address.
+ *
+ * @return Zero on success.
+ */
+PJ_DECL(pj_status_t) pj_sock_bind( pj_sock_t sockfd,
+ const pj_sockaddr_t *my_addr,
+ int addrlen);
+
+/**
+ * Bind the IP socket sockfd to the given address and port.
+ *
+ * @param sockfd The socket descriptor.
+ * @param addr Local address to bind the socket to, in host byte order.
+ * @param port The local port to bind the socket to, in host byte order.
+ *
+ * @return Zero on success.
+ */
+PJ_DECL(pj_status_t) pj_sock_bind_in( pj_sock_t sockfd,
+ pj_uint32_t addr,
+ pj_uint16_t port);
+
+#if PJ_HAS_TCP
+/**
+ * Listen for incoming connection. This function only applies to connection
+ * oriented sockets (such as PJ_SOCK_STREAM or PJ_SOCK_SEQPACKET), and it
+ * indicates the willingness to accept incoming connections.
+ *
+ * @param sockfd The socket descriptor.
+ * @param backlog Defines the maximum length the queue of pending
+ * connections may grow to.
+ *
+ * @return Zero on success.
+ */
+PJ_DECL(pj_status_t) pj_sock_listen( pj_sock_t sockfd,
+ int backlog );
+
+/**
+ * Accept new connection on the specified connection oriented server socket.
+ *
+ * @param serverfd The server socket.
+ * @param newsock New socket on success, of PJ_INVALID_SOCKET if failed.
+ * @param addr A pointer to sockaddr type. If the argument is not NULL,
+ * it will be filled by the address of connecting entity.
+ * @param addrlen Initially specifies the length of the address, and upon
+ * return will be filled with the exact address length.
+ *
+ * @return Zero on success, or the error number.
+ */
+PJ_DECL(pj_status_t) pj_sock_accept( pj_sock_t serverfd,
+ pj_sock_t *newsock,
+ pj_sockaddr_t *addr,
+ int *addrlen);
+#endif
+
+/**
+ * The file descriptor sockfd must refer to a socket. If the socket is of
+ * type PJ_SOCK_DGRAM then the serv_addr address is the address to which
+ * datagrams are sent by default, and the only address from which datagrams
+ * are received. If the socket is of type PJ_SOCK_STREAM or PJ_SOCK_SEQPACKET,
+ * this call attempts to make a connection to another socket. The
+ * other socket is specified by serv_addr, which is an address (of length
+ * addrlen) in the communications space of the socket. Each communications
+ * space interprets the serv_addr parameter in its own way.
+ *
+ * @param sockfd The socket descriptor.
+ * @param serv_addr Server address to connect to.
+ * @param addrlen The length of server address.
+ *
+ * @return Zero on success.
+ */
+PJ_DECL(pj_status_t) pj_sock_connect( pj_sock_t sockfd,
+ const pj_sockaddr_t *serv_addr,
+ int addrlen);
+
+/**
+ * Return the address of peer which is connected to socket sockfd.
+ *
+ * @param sockfd The socket descriptor.
+ * @param addr Pointer to sockaddr structure to which the address
+ * will be returned.
+ * @param namelen Initially the length of the addr. Upon return the value
+ * will be set to the actual length of the address.
+ *
+ * @return Zero on success.
+ */
+PJ_DECL(pj_status_t) pj_sock_getpeername(pj_sock_t sockfd,
+ pj_sockaddr_t *addr,
+ int *namelen);
+
+/**
+ * Return the current name of the specified socket.
+ *
+ * @param sockfd The socket descriptor.
+ * @param addr Pointer to sockaddr structure to which the address
+ * will be returned.
+ * @param namelen Initially the length of the addr. Upon return the value
+ * will be set to the actual length of the address.
+ *
+ * @return Zero on success.
+ */
+PJ_DECL(pj_status_t) pj_sock_getsockname( pj_sock_t sockfd,
+ pj_sockaddr_t *addr,
+ int *namelen);
+
+/**
+ * Get socket option associated with a socket. Options may exist at multiple
+ * protocol levels; they are always present at the uppermost socket level.
+ *
+ * @param sockfd The socket descriptor.
+ * @param level The level which to get the option from.
+ * @param optname The option name.
+ * @param optval Identifies the buffer which the value will be
+ * returned.
+ * @param optlen Initially contains the length of the buffer, upon
+ * return will be set to the actual size of the value.
+ *
+ * @return Zero on success.
+ */
+PJ_DECL(pj_status_t) pj_sock_getsockopt( pj_sock_t sockfd,
+ pj_uint16_t level,
+ pj_uint16_t optname,
+ void *optval,
+ int *optlen);
+/**
+ * Manipulate the options associated with a socket. Options may exist at
+ * multiple protocol levels; they are always present at the uppermost socket
+ * level.
+ *
+ * @param sockfd The socket descriptor.
+ * @param level The level which to get the option from.
+ * @param optname The option name.
+ * @param optval Identifies the buffer which contain the value.
+ * @param optlen The length of the value.
+ *
+ * @return PJ_SUCCESS or the status code.
+ */
+PJ_DECL(pj_status_t) pj_sock_setsockopt( pj_sock_t sockfd,
+ pj_uint16_t level,
+ pj_uint16_t optname,
+ const void *optval,
+ int optlen);
+
+
+/**
+ * Receives data stream or message coming to the specified socket.
+ *
+ * @param sockfd The socket descriptor.
+ * @param buf The buffer to receive the data or message.
+ * @param len On input, the length of the buffer. On return,
+ * contains the length of data received.
+ * @param flags Combination of #pj_sock_msg_flag.
+ *
+ * @return PJ_SUCCESS or the error code.
+ */
+PJ_DECL(pj_status_t) pj_sock_recv(pj_sock_t sockfd,
+ void *buf,
+ pj_ssize_t *len,
+ unsigned flags);
+
+/**
+ * Receives data stream or message coming to the specified socket.
+ *
+ * @param sockfd The socket descriptor.
+ * @param buf The buffer to receive the data or message.
+ * @param len On input, the length of the buffer. On return,
+ * contains the length of data received.
+ * @param flags Bitmask combination of #pj_sock_msg_flag.
+ * @param from If not NULL, it will be filled with the source
+ * address of the connection.
+ * @param fromlen Initially contains the length of from address,
+ * and upon return will be filled with the actual
+ * length of the address.
+ *
+ * @return PJ_SUCCESS or the error code.
+ */
+PJ_DECL(pj_status_t) pj_sock_recvfrom( pj_sock_t sockfd,
+ void *buf,
+ pj_ssize_t *len,
+ unsigned flags,
+ pj_sockaddr_t *from,
+ int *fromlen);
+
+/**
+ * Transmit data to the socket.
+ *
+ * @param sockfd Socket descriptor.
+ * @param buf Buffer containing data to be sent.
+ * @param len On input, the length of the data in the buffer.
+ * Upon return, it will be filled with the length
+ * of data sent.
+ * @param flags Bitmask combination of #pj_sock_msg_flag.
+ *
+ * @return PJ_SUCCESS or the status code.
+ */
+PJ_DECL(pj_status_t) pj_sock_send(pj_sock_t sockfd,
+ const void *buf,
+ pj_ssize_t *len,
+ unsigned flags);
+
+/**
+ * Transmit data to the socket to the specified address.
+ *
+ * @param sockfd Socket descriptor.
+ * @param buf Buffer containing data to be sent.
+ * @param len On input, the length of the data in the buffer.
+ * Upon return, it will be filled with the length
+ * of data sent.
+ * @param flags Bitmask combination of #pj_sock_msg_flag.
+ * @param to The address to send.
+ * @param tolen The length of the address in bytes.
+ *
+ * @return The length of data successfully sent.
+ */
+PJ_DECL(pj_status_t) pj_sock_sendto(pj_sock_t sockfd,
+ const void *buf,
+ pj_ssize_t *len,
+ unsigned flags,
+ const pj_sockaddr_t *to,
+ int tolen);
+
+#if PJ_HAS_TCP
+/**
+ * The shutdown call causes all or part of a full-duplex connection on the
+ * socket associated with sockfd to be shut down.
+ *
+ * @param sockfd The socket descriptor.
+ * @param how If how is PJ_SHUT_RD, further receptions will be
+ * disallowed. If how is PJ_SHUT_WR, further transmissions
+ * will be disallowed. If how is PJ_SHUT_RDWR, further
+ * receptions andtransmissions will be disallowed.
+ *
+ * @return Zero on success.
+ */
+PJ_DECL(pj_status_t) pj_sock_shutdown( pj_sock_t sockfd,
+ int how);
+#endif
+
+/**
+ * @}
+ */
+
+
+PJ_END_DECL
+
+#endif /* __PJ_SOCK_H__ */
+
diff --git a/pjlib/include/pj/sock_select.h b/pjlib/include/pj/sock_select.h
index 5b0c700..e515984 100644
--- a/pjlib/include/pj/sock_select.h
+++ b/pjlib/include/pj/sock_select.h
@@ -1,133 +1,154 @@
-/* $Id$
- *
- */
-/* $Log: /pjproject-0.3/pjlib/include/pj/sock_select.h $
- *
- * 3 10/14/05 12:26a Bennylp
- * Finished error code framework, some fixes in ioqueue, etc. Pretty
- * major.
- *
- * 2 9/21/05 1:39p Bennylp
- * Periodic checkin for backup.
- *
- * 1 9/15/05 8:40p Bennylp
- * Created.
- */
-#ifndef __PJ_SELECT_H__
-#define __PJ_SELECT_H__
-
-/**
- * @file sock_select.h
- * @brief Socket select().
- */
-
-#include <pj/types.h>
-
-PJ_BEGIN_DECL
-
-/**
- * @defgroup PJ_SOCK_SELECT Socket select() API.
- * @ingroup PJ_IO
- * @{
- * This module provides portable abstraction for \a select() like API.
- * The abstraction is needed so that it can utilize various event
- * dispatching mechanisms that are available across platforms.
- *
- * The API is very similar to normal \a select() usage.
- *
- * \section pj_sock_select_examples_sec Examples
- *
- * For some examples on how to use the select API, please see:
- *
- * - \ref page_pjlib_select_test
- */
-
-/**
- * Portable structure declarations for pj_fd_set.
- * The implementation of pj_sock_select() does not use this structure
- * per-se, but instead it will use the native fd_set structure. However,
- * we must make sure that the size of pj_fd_set_t can accomodate the
- * native fd_set structure.
- */
-typedef struct pj_fd_set_t
-{
- pj_sock_t data[FD_SETSIZE + 4]; /**< Opaque buffer for fd_set */
-} pj_fd_set_t;
-
-
-/**
- * Initialize the descriptor set pointed to by fdsetp to the null set.
- *
- * @param fdsetp The descriptor set.
- */
-PJ_DECL(void) PJ_FD_ZERO(pj_fd_set_t *fdsetp);
-
-
-/**
- * Add the file descriptor fd to the set pointed to by fdsetp.
- * If the file descriptor fd is already in this set, there shall be no effect
- * on the set, nor will an error be returned.
- *
- * @param fd The socket descriptor.
- * @param fdsetp The descriptor set.
- */
-PJ_DECL(void) PJ_FD_SET(pj_sock_t fd, pj_fd_set_t *fdsetp);
-
-/**
- * Remove the file descriptor fd from the set pointed to by fdsetp.
- * If fd is not a member of this set, there shall be no effect on the set,
- * nor will an error be returned.
- *
- * @param fd The socket descriptor.
- * @param fdsetp The descriptor set.
- */
-PJ_DECL(void) PJ_FD_CLR(pj_sock_t fd, pj_fd_set_t *fdsetp);
-
-
-/**
- * Evaluate to non-zero if the file descriptor fd is a member of the set
- * pointed to by fdsetp, and shall evaluate to zero otherwise.
- *
- * @param fd The socket descriptor.
- * @param fdsetp The descriptor set.
- *
- * @return Nonzero if fd is member of the descriptor set.
- */
-PJ_DECL(pj_bool_t) PJ_FD_ISSET(pj_sock_t fd, const pj_fd_set_t *fdsetp);
-
-
-/**
- * This function wait for a number of file descriptors to change status.
- * The behaviour is the same as select() function call which appear in
- * standard BSD socket libraries.
- *
- * @param n On Unices, this specifies the highest-numbered
- * descriptor in any of the three set, plus 1. On Windows,
- * the value is ignored.
- * @param readfds Optional pointer to a set of sockets to be checked for
- * readability.
- * @param writefds Optional pointer to a set of sockets to be checked for
- * writability.
- * @param exceptfds Optional pointer to a set of sockets to be checked for
- * errors.
- * @param timeout Maximum time for select to wait, or null for blocking
- * operations.
- *
- * @return The total number of socket handles that are ready, or
- * zero if the time limit expired, or -1 if an error occurred.
- */
-PJ_DECL(int) pj_sock_select( int n,
- pj_fd_set_t *readfds,
- pj_fd_set_t *writefds,
- pj_fd_set_t *exceptfds,
- const pj_time_val *timeout);
-
-
-/**
- * @}
- */
-
-
-PJ_END_DECL
-
-#endif /* __PJ_SELECT_H__ */
+/* $Id$
+ *
+ */
+/*
+ * PJLIB - PJ Foundation Library
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ * Benny Prijono <bennylp@bulukucing.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+/* $Log: /pjproject-0.3/pjlib/include/pj/sock_select.h $
+ *
+ * 3 10/14/05 12:26a Bennylp
+ * Finished error code framework, some fixes in ioqueue, etc. Pretty
+ * major.
+ *
+ * 2 9/21/05 1:39p Bennylp
+ * Periodic checkin for backup.
+ *
+ * 1 9/15/05 8:40p Bennylp
+ * Created.
+ */
+#ifndef __PJ_SELECT_H__
+#define __PJ_SELECT_H__
+
+/**
+ * @file sock_select.h
+ * @brief Socket select().
+ */
+
+#include <pj/types.h>
+
+PJ_BEGIN_DECL
+
+/**
+ * @defgroup PJ_SOCK_SELECT Socket select() API.
+ * @ingroup PJ_IO
+ * @{
+ * This module provides portable abstraction for \a select() like API.
+ * The abstraction is needed so that it can utilize various event
+ * dispatching mechanisms that are available across platforms.
+ *
+ * The API is very similar to normal \a select() usage.
+ *
+ * \section pj_sock_select_examples_sec Examples
+ *
+ * For some examples on how to use the select API, please see:
+ *
+ * - \ref page_pjlib_select_test
+ */
+
+/**
+ * Portable structure declarations for pj_fd_set.
+ * The implementation of pj_sock_select() does not use this structure
+ * per-se, but instead it will use the native fd_set structure. However,
+ * we must make sure that the size of pj_fd_set_t can accomodate the
+ * native fd_set structure.
+ */
+typedef struct pj_fd_set_t
+{
+ pj_sock_t data[FD_SETSIZE + 4]; /**< Opaque buffer for fd_set */
+} pj_fd_set_t;
+
+
+/**
+ * Initialize the descriptor set pointed to by fdsetp to the null set.
+ *
+ * @param fdsetp The descriptor set.
+ */
+PJ_DECL(void) PJ_FD_ZERO(pj_fd_set_t *fdsetp);
+
+
+/**
+ * Add the file descriptor fd to the set pointed to by fdsetp.
+ * If the file descriptor fd is already in this set, there shall be no effect
+ * on the set, nor will an error be returned.
+ *
+ * @param fd The socket descriptor.
+ * @param fdsetp The descriptor set.
+ */
+PJ_DECL(void) PJ_FD_SET(pj_sock_t fd, pj_fd_set_t *fdsetp);
+
+/**
+ * Remove the file descriptor fd from the set pointed to by fdsetp.
+ * If fd is not a member of this set, there shall be no effect on the set,
+ * nor will an error be returned.
+ *
+ * @param fd The socket descriptor.
+ * @param fdsetp The descriptor set.
+ */
+PJ_DECL(void) PJ_FD_CLR(pj_sock_t fd, pj_fd_set_t *fdsetp);
+
+
+/**
+ * Evaluate to non-zero if the file descriptor fd is a member of the set
+ * pointed to by fdsetp, and shall evaluate to zero otherwise.
+ *
+ * @param fd The socket descriptor.
+ * @param fdsetp The descriptor set.
+ *
+ * @return Nonzero if fd is member of the descriptor set.
+ */
+PJ_DECL(pj_bool_t) PJ_FD_ISSET(pj_sock_t fd, const pj_fd_set_t *fdsetp);
+
+
+/**
+ * This function wait for a number of file descriptors to change status.
+ * The behaviour is the same as select() function call which appear in
+ * standard BSD socket libraries.
+ *
+ * @param n On Unices, this specifies the highest-numbered
+ * descriptor in any of the three set, plus 1. On Windows,
+ * the value is ignored.
+ * @param readfds Optional pointer to a set of sockets to be checked for
+ * readability.
+ * @param writefds Optional pointer to a set of sockets to be checked for
+ * writability.
+ * @param exceptfds Optional pointer to a set of sockets to be checked for
+ * errors.
+ * @param timeout Maximum time for select to wait, or null for blocking
+ * operations.
+ *
+ * @return The total number of socket handles that are ready, or
+ * zero if the time limit expired, or -1 if an error occurred.
+ */
+PJ_DECL(int) pj_sock_select( int n,
+ pj_fd_set_t *readfds,
+ pj_fd_set_t *writefds,
+ pj_fd_set_t *exceptfds,
+ const pj_time_val *timeout);
+
+
+/**
+ * @}
+ */
+
+
+PJ_END_DECL
+
+#endif /* __PJ_SELECT_H__ */
diff --git a/pjlib/include/pj/string.h b/pjlib/include/pj/string.h
index 5b7ac13..22f107b 100644
--- a/pjlib/include/pj/string.h
+++ b/pjlib/include/pj/string.h
@@ -1,520 +1,541 @@
-/* $Id$
- *
- */
-
-#ifndef __PJ_STRING_H__
-#define __PJ_STRING_H__
-
-/**
- * @file string.h
- * @brief PJLIB String Operations.
- */
-
-#include <pj/types.h>
-#include <pj/compat/string.h>
+/* $Id$
+ *
+ */
+/*
+ * PJLIB - PJ Foundation Library
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ * Benny Prijono <bennylp@bulukucing.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef __PJ_STRING_H__
+#define __PJ_STRING_H__
+
+/**
+ * @file string.h
+ * @brief PJLIB String Operations.
+ */
+
+#include <pj/types.h>
+#include <pj/compat/string.h>
#include <pj/compat/sprintf.h>
#include <pj/compat/vsprintf.h>
-
-PJ_BEGIN_DECL
-
-/**
- * @defgroup PJ_PSTR String Operations
- * @ingroup PJ_DS
- * @{
- * This module provides string manipulation API.
- *
- * \section pj_pstr_not_null_sec PJLIB String is NOT Null Terminated!
- *
- * That is the first information that developers need to know. Instead
- * of using normal C string, strings in PJLIB are represented as
- * pj_str_t structure below:
- *
- * <pre>
- * typedef struct pj_str_t
- * {
- * char *ptr;
- * pj_size_t slen;
- * } pj_str_t;
- * </pre>
- *
- * There are some advantages of using this approach:
- * - the string can point to arbitrary location in memory even
- * if the string in that location is not null terminated. This is
- * most usefull for text parsing, where the parsed text can just
- * point to the original text in the input. If we use C string,
- * then we will have to copy the text portion from the input
- * to a string variable.
- * - because the length of the string is known, string copy operation
- * can be made more efficient.
- *
- * Most of APIs in PJLIB that expect or return string will represent
- * the string as pj_str_t instead of normal C string.
- *
- * \section pj_pstr_examples_sec Examples
- *
- * For some examples, please see:
- * - @ref page_pjlib_string_test
- */
-
-/**
- * Create string initializer from a normal C string.
- *
- * @param str Null terminated string to be stored.
- *
- * @return pj_str_t.
- */
-PJ_IDECL(pj_str_t) pj_str(char *str);
-
-/**
- * Create constant string from normal C string.
- *
- * @param str The string to be initialized.
- * @param s Null terminated string.
- *
- * @return pj_str_t.
- */
-PJ_INLINE(const pj_str_t*) pj_cstr(pj_str_t *str, const char *s)
-{
- str->ptr = (char*)s;
- str->slen = s ? strlen(s) : 0;
- return str;
-}
-
-/**
- * Set the pointer and length to the specified value.
- *
- * @param str the string.
- * @param ptr pointer to set.
- * @param length length to set.
- *
- * @return the string.
- */
-PJ_INLINE(pj_str_t*) pj_strset( pj_str_t *str, char *ptr, pj_size_t length)
-{
- str->ptr = ptr;
- str->slen = length;
- return str;
-}
-
-/**
- * Set the pointer and length of the string to the source string, which
- * must be NULL terminated.
- *
- * @param str the string.
- * @param src pointer to set.
- *
- * @return the string.
- */
-PJ_INLINE(pj_str_t*) pj_strset2( pj_str_t *str, char *src)
-{
- str->ptr = src;
- str->slen = src ? strlen(src) : 0;
- return str;
-}
-
-/**
- * Set the pointer and the length of the string.
- *
- * @param str The target string.
- * @param begin The start of the string.
- * @param end The end of the string.
- *
- * @return the target string.
- */
-PJ_INLINE(pj_str_t*) pj_strset3( pj_str_t *str, char *begin, char *end )
-{
- str->ptr = begin;
- str->slen = end-begin;
- return str;
-}
-
-/**
- * Assign string.
- *
- * @param dst The target string.
- * @param src The source string.
- *
- * @return the target string.
- */
-PJ_IDECL(pj_str_t*) pj_strassign( pj_str_t *dst, pj_str_t *src );
-
-/**
- * Copy string contents.
- *
- * @param dst The target string.
- * @param src The source string.
- *
- * @return the target string.
- */
-PJ_IDECL(pj_str_t*) pj_strcpy(pj_str_t *dst, const pj_str_t *src);
-
-/**
- * Copy string contents.
- *
- * @param dst The target string.
- * @param src The source string.
- *
- * @return the target string.
- */
-PJ_IDECL(pj_str_t*) pj_strcpy2(pj_str_t *dst, const char *src);
-
-/**
- * Duplicate string.
- *
- * @param pool The pool.
- * @param dst The string result.
- * @param src The string to duplicate.
- *
- * @return the string result.
- */
-PJ_IDECL(pj_str_t*) pj_strdup(pj_pool_t *pool,
- pj_str_t *dst,
- const pj_str_t *src);
-
-/**
- * Duplicate string and NULL terminate the destination string.
- *
- * @param pool
- * @param dst
- * @param src
- */
-PJ_IDECL(pj_str_t*) pj_strdup_with_null(pj_pool_t *pool,
- pj_str_t *dst,
- const pj_str_t *src);
-
-/**
- * Duplicate string.
- *
- * @param pool The pool.
- * @param dst The string result.
- * @param src The string to duplicate.
- *
- * @return the string result.
- */
-PJ_IDECL(pj_str_t*) pj_strdup2(pj_pool_t *pool,
- pj_str_t *dst,
- const char *src);
-
-/**
- * Duplicate string.
- *
- * @param pool The pool.
- * @param src The string to duplicate.
- *
- * @return the string result.
- */
-PJ_IDECL(pj_str_t) pj_strdup3(pj_pool_t *pool, const char *src);
-
-/**
- * Return the length of the string.
- *
- * @param str The string.
- *
- * @return the length of the string.
- */
-PJ_INLINE(pj_size_t) pj_strlen( const pj_str_t *str )
-{
- return str->slen;
-}
-
-/**
- * Return the pointer to the string data.
- *
- * @param str The string.
- *
- * @return the pointer to the string buffer.
- */
-PJ_INLINE(const char*) pj_strbuf( const pj_str_t *str )
-{
- return str->ptr;
-}
-
-/**
- * Compare strings.
- *
- * @param str1 The string to compare.
- * @param str2 The string to compare.
- *
- * @return
- * - < 0 if str1 is less than str2
- * - 0 if str1 is identical to str2
- * - > 0 if str1 is greater than str2
- */
-PJ_IDECL(int) pj_strcmp( const pj_str_t *str1, const pj_str_t *str2);
-
-/**
- * Compare strings.
- *
- * @param str1 The string to compare.
- * @param str2 The string to compare.
- *
- * @return
- * - < 0 if str1 is less than str2
- * - 0 if str1 is identical to str2
- * - > 0 if str1 is greater than str2
- */
-PJ_IDECL(int) pj_strcmp2( const pj_str_t *str1, const char *str2 );
-
-/**
- * Compare strings.
- *
- * @param str1 The string to compare.
- * @param str2 The string to compare.
- * @param len The maximum number of characters to compare.
- *
- * @return
- * - < 0 if str1 is less than str2
- * - 0 if str1 is identical to str2
- * - > 0 if str1 is greater than str2
- */
-PJ_IDECL(int) pj_strncmp( const pj_str_t *str1, const pj_str_t *str2,
- pj_size_t len);
-
-/**
- * Compare strings.
- *
- * @param str1 The string to compare.
- * @param str2 The string to compare.
- * @param len The maximum number of characters to compare.
- *
- * @return
- * - < 0 if str1 is less than str2
- * - 0 if str1 is identical to str2
- * - > 0 if str1 is greater than str2
- */
-PJ_IDECL(int) pj_strncmp2( const pj_str_t *str1, const char *str2,
- pj_size_t len);
-
-/**
- * Perform lowercase comparison to the strings.
- *
- * @param str1 The string to compare.
- * @param str2 The string to compare.
- *
- * @return
- * - < 0 if str1 is less than str2
- * - 0 if str1 is identical to str2
- * - > 0 if str1 is greater than str2
- */
-PJ_IDECL(int) pj_stricmp( const pj_str_t *str1, const pj_str_t *str2);
-
-/**
- * Perform lowercase comparison to the strings.
- *
- * @param str1 The string to compare.
- * @param str2 The string to compare.
- *
- * @return
- * - < 0 if str1 is less than str2
- * - 0 if str1 is identical to str2
- * - > 0 if str1 is greater than str2
- */
-PJ_IDECL(int) pj_stricmp2( const pj_str_t *str1, const char *str2);
-
-/**
- * Perform lowercase comparison to the strings.
- *
- * @param str1 The string to compare.
- * @param str2 The string to compare.
- * @param len The maximum number of characters to compare.
- *
- * @return
- * - < 0 if str1 is less than str2
- * - 0 if str1 is identical to str2
- * - > 0 if str1 is greater than str2
- */
-PJ_IDECL(int) pj_strnicmp( const pj_str_t *str1, const pj_str_t *str2,
- pj_size_t len);
-
-/**
- * Perform lowercase comparison to the strings.
- *
- * @param str1 The string to compare.
- * @param str2 The string to compare.
- * @param len The maximum number of characters to compare.
- *
- * @return
- * - < 0 if str1 is less than str2
- * - 0 if str1 is identical to str2
- * - > 0 if str1 is greater than str2
- */
-PJ_IDECL(int) pj_strnicmp2( const pj_str_t *str1, const char *str2,
- pj_size_t len);
-
-/**
- * Concatenate strings.
- *
- * @param dst The destination string.
- * @param src The source string.
- */
-PJ_IDECL(void) pj_strcat(pj_str_t *dst, const pj_str_t *src);
-
-/**
- * Finds a character in a string.
- *
- * @param str The string.
- * @param chr The character to find.
- *
- * @return the pointer to first character found, or NULL.
- */
-PJ_INLINE(char*) pj_strchr( pj_str_t *str, int chr)
-{
- return (char*) memchr(str->ptr, chr, str->slen);
-}
-
-/**
- * Remove (trim) leading whitespaces from the string.
- *
- * @param str The string.
- *
- * @return the string.
- */
-PJ_DECL(pj_str_t*) pj_strltrim( pj_str_t *str );
-
-/**
- * Remove (trim) the trailing whitespaces from the string.
- *
- * @param str The string.
- *
- * @return the string.
- */
-PJ_DECL(pj_str_t*) pj_strrtrim( pj_str_t *str );
-
-/**
- * Remove (trim) leading and trailing whitespaces from the string.
- *
- * @param str The string.
- *
- * @return the string.
- */
-PJ_IDECL(pj_str_t*) pj_strtrim( pj_str_t *str );
-
-/**
- * Initialize the buffer with some random string.
- *
- * @param str the string to store the result.
- * @param length the length of the random string to generate.
- *
- * @return the string.
- */
-PJ_DECL(char*) pj_create_random_string(char *str, pj_size_t length);
-
-/**
- * Convert string to unsigned integer.
- *
- * @param str the string.
- *
- * @return the unsigned integer.
- */
-PJ_DECL(unsigned long) pj_strtoul(const pj_str_t *str);
-
-/**
- * Utility to convert unsigned integer to string. Note that the
- * string will be NULL terminated.
- *
- * @param val the unsigned integer value.
- * @param buf the buffer
- *
- * @return the number of characters written
- */
-PJ_DECL(int) pj_utoa(unsigned long val, char *buf);
-
-/**
- * Convert unsigned integer to string with minimum digits. Note that the
- * string will be NULL terminated.
- *
- * @param val The unsigned integer value.
- * @param buf The buffer.
- * @param min_dig Minimum digits to be printed, or zero to specify no
- * minimum digit.
- * @param pad The padding character to be put in front of the string
- * when the digits is less than minimum.
- *
- * @return the number of characters written.
- */
-PJ_DECL(int) pj_utoa_pad( unsigned long val, char *buf, int min_dig, int pad);
-
-/**
- * Fill the memory location with value.
- *
- * @param dst The destination buffer.
- * @param c Character to set.
- * @param size The number of characters.
- *
- * @return the value of dst.
- */
-PJ_INLINE(void*) pj_memset(void *dst, int c, pj_size_t size)
-{
- return memset(dst, c, size);
-}
-
-/**
- * Copy buffer.
- *
- * @param dst The destination buffer.
- * @param src The source buffer.
- * @param size The size to copy.
- *
- * @return the destination buffer.
- */
-PJ_INLINE(void*) pj_memcpy(void *dst, const void *src, pj_size_t size)
-{
- return memcpy(dst, src, size);
-}
-
-/**
- * Move memory.
- *
- * @param dst The destination buffer.
- * @param src The source buffer.
- * @param size The size to copy.
- *
- * @return the destination buffer.
- */
-PJ_INLINE(void*) pj_memmove(void *dst, const void *src, pj_size_t size)
-{
- return memmove(dst, src, size);
-}
-
-/**
- * Compare buffers.
- *
- * @param buf1 The first buffer.
- * @param buf2 The second buffer.
- * @param size The size to compare.
- *
- * @return negative, zero, or positive value.
- */
-PJ_INLINE(int) pj_memcmp(const void *buf1, const void *buf2, pj_size_t size)
-{
- return memcmp(buf1, buf2, size);
-}
-
-/**
- * Find character in the buffer.
- *
- * @param buf The buffer.
- * @param c The character to find.
- * @param size The size to check.
- *
- * @return the pointer to location where the character is found, or NULL if
- * not found.
- */
-PJ_INLINE(void*) pj_memchr(const void *buf, int c, pj_size_t size)
-{
- return memchr(buf, c, size);
-}
-
-/**
- * @}
- */
-
-#if PJ_FUNCTIONS_ARE_INLINED
-# include <pj/string_i.h>
-#endif
-
-PJ_END_DECL
-
-#endif /* __PJ_STRING_H__ */
-
+
+PJ_BEGIN_DECL
+
+/**
+ * @defgroup PJ_PSTR String Operations
+ * @ingroup PJ_DS
+ * @{
+ * This module provides string manipulation API.
+ *
+ * \section pj_pstr_not_null_sec PJLIB String is NOT Null Terminated!
+ *
+ * That is the first information that developers need to know. Instead
+ * of using normal C string, strings in PJLIB are represented as
+ * pj_str_t structure below:
+ *
+ * <pre>
+ * typedef struct pj_str_t
+ * {
+ * char *ptr;
+ * pj_size_t slen;
+ * } pj_str_t;
+ * </pre>
+ *
+ * There are some advantages of using this approach:
+ * - the string can point to arbitrary location in memory even
+ * if the string in that location is not null terminated. This is
+ * most usefull for text parsing, where the parsed text can just
+ * point to the original text in the input. If we use C string,
+ * then we will have to copy the text portion from the input
+ * to a string variable.
+ * - because the length of the string is known, string copy operation
+ * can be made more efficient.
+ *
+ * Most of APIs in PJLIB that expect or return string will represent
+ * the string as pj_str_t instead of normal C string.
+ *
+ * \section pj_pstr_examples_sec Examples
+ *
+ * For some examples, please see:
+ * - @ref page_pjlib_string_test
+ */
+
+/**
+ * Create string initializer from a normal C string.
+ *
+ * @param str Null terminated string to be stored.
+ *
+ * @return pj_str_t.
+ */
+PJ_IDECL(pj_str_t) pj_str(char *str);
+
+/**
+ * Create constant string from normal C string.
+ *
+ * @param str The string to be initialized.
+ * @param s Null terminated string.
+ *
+ * @return pj_str_t.
+ */
+PJ_INLINE(const pj_str_t*) pj_cstr(pj_str_t *str, const char *s)
+{
+ str->ptr = (char*)s;
+ str->slen = s ? strlen(s) : 0;
+ return str;
+}
+
+/**
+ * Set the pointer and length to the specified value.
+ *
+ * @param str the string.
+ * @param ptr pointer to set.
+ * @param length length to set.
+ *
+ * @return the string.
+ */
+PJ_INLINE(pj_str_t*) pj_strset( pj_str_t *str, char *ptr, pj_size_t length)
+{
+ str->ptr = ptr;
+ str->slen = length;
+ return str;
+}
+
+/**
+ * Set the pointer and length of the string to the source string, which
+ * must be NULL terminated.
+ *
+ * @param str the string.
+ * @param src pointer to set.
+ *
+ * @return the string.
+ */
+PJ_INLINE(pj_str_t*) pj_strset2( pj_str_t *str, char *src)
+{
+ str->ptr = src;
+ str->slen = src ? strlen(src) : 0;
+ return str;
+}
+
+/**
+ * Set the pointer and the length of the string.
+ *
+ * @param str The target string.
+ * @param begin The start of the string.
+ * @param end The end of the string.
+ *
+ * @return the target string.
+ */
+PJ_INLINE(pj_str_t*) pj_strset3( pj_str_t *str, char *begin, char *end )
+{
+ str->ptr = begin;
+ str->slen = end-begin;
+ return str;
+}
+
+/**
+ * Assign string.
+ *
+ * @param dst The target string.
+ * @param src The source string.
+ *
+ * @return the target string.
+ */
+PJ_IDECL(pj_str_t*) pj_strassign( pj_str_t *dst, pj_str_t *src );
+
+/**
+ * Copy string contents.
+ *
+ * @param dst The target string.
+ * @param src The source string.
+ *
+ * @return the target string.
+ */
+PJ_IDECL(pj_str_t*) pj_strcpy(pj_str_t *dst, const pj_str_t *src);
+
+/**
+ * Copy string contents.
+ *
+ * @param dst The target string.
+ * @param src The source string.
+ *
+ * @return the target string.
+ */
+PJ_IDECL(pj_str_t*) pj_strcpy2(pj_str_t *dst, const char *src);
+
+/**
+ * Duplicate string.
+ *
+ * @param pool The pool.
+ * @param dst The string result.
+ * @param src The string to duplicate.
+ *
+ * @return the string result.
+ */
+PJ_IDECL(pj_str_t*) pj_strdup(pj_pool_t *pool,
+ pj_str_t *dst,
+ const pj_str_t *src);
+
+/**
+ * Duplicate string and NULL terminate the destination string.
+ *
+ * @param pool
+ * @param dst
+ * @param src
+ */
+PJ_IDECL(pj_str_t*) pj_strdup_with_null(pj_pool_t *pool,
+ pj_str_t *dst,
+ const pj_str_t *src);
+
+/**
+ * Duplicate string.
+ *
+ * @param pool The pool.
+ * @param dst The string result.
+ * @param src The string to duplicate.
+ *
+ * @return the string result.
+ */
+PJ_IDECL(pj_str_t*) pj_strdup2(pj_pool_t *pool,
+ pj_str_t *dst,
+ const char *src);
+
+/**
+ * Duplicate string.
+ *
+ * @param pool The pool.
+ * @param src The string to duplicate.
+ *
+ * @return the string result.
+ */
+PJ_IDECL(pj_str_t) pj_strdup3(pj_pool_t *pool, const char *src);
+
+/**
+ * Return the length of the string.
+ *
+ * @param str The string.
+ *
+ * @return the length of the string.
+ */
+PJ_INLINE(pj_size_t) pj_strlen( const pj_str_t *str )
+{
+ return str->slen;
+}
+
+/**
+ * Return the pointer to the string data.
+ *
+ * @param str The string.
+ *
+ * @return the pointer to the string buffer.
+ */
+PJ_INLINE(const char*) pj_strbuf( const pj_str_t *str )
+{
+ return str->ptr;
+}
+
+/**
+ * Compare strings.
+ *
+ * @param str1 The string to compare.
+ * @param str2 The string to compare.
+ *
+ * @return
+ * - < 0 if str1 is less than str2
+ * - 0 if str1 is identical to str2
+ * - > 0 if str1 is greater than str2
+ */
+PJ_IDECL(int) pj_strcmp( const pj_str_t *str1, const pj_str_t *str2);
+
+/**
+ * Compare strings.
+ *
+ * @param str1 The string to compare.
+ * @param str2 The string to compare.
+ *
+ * @return
+ * - < 0 if str1 is less than str2
+ * - 0 if str1 is identical to str2
+ * - > 0 if str1 is greater than str2
+ */
+PJ_IDECL(int) pj_strcmp2( const pj_str_t *str1, const char *str2 );
+
+/**
+ * Compare strings.
+ *
+ * @param str1 The string to compare.
+ * @param str2 The string to compare.
+ * @param len The maximum number of characters to compare.
+ *
+ * @return
+ * - < 0 if str1 is less than str2
+ * - 0 if str1 is identical to str2
+ * - > 0 if str1 is greater than str2
+ */
+PJ_IDECL(int) pj_strncmp( const pj_str_t *str1, const pj_str_t *str2,
+ pj_size_t len);
+
+/**
+ * Compare strings.
+ *
+ * @param str1 The string to compare.
+ * @param str2 The string to compare.
+ * @param len The maximum number of characters to compare.
+ *
+ * @return
+ * - < 0 if str1 is less than str2
+ * - 0 if str1 is identical to str2
+ * - > 0 if str1 is greater than str2
+ */
+PJ_IDECL(int) pj_strncmp2( const pj_str_t *str1, const char *str2,
+ pj_size_t len);
+
+/**
+ * Perform lowercase comparison to the strings.
+ *
+ * @param str1 The string to compare.
+ * @param str2 The string to compare.
+ *
+ * @return
+ * - < 0 if str1 is less than str2
+ * - 0 if str1 is identical to str2
+ * - > 0 if str1 is greater than str2
+ */
+PJ_IDECL(int) pj_stricmp( const pj_str_t *str1, const pj_str_t *str2);
+
+/**
+ * Perform lowercase comparison to the strings.
+ *
+ * @param str1 The string to compare.
+ * @param str2 The string to compare.
+ *
+ * @return
+ * - < 0 if str1 is less than str2
+ * - 0 if str1 is identical to str2
+ * - > 0 if str1 is greater than str2
+ */
+PJ_IDECL(int) pj_stricmp2( const pj_str_t *str1, const char *str2);
+
+/**
+ * Perform lowercase comparison to the strings.
+ *
+ * @param str1 The string to compare.
+ * @param str2 The string to compare.
+ * @param len The maximum number of characters to compare.
+ *
+ * @return
+ * - < 0 if str1 is less than str2
+ * - 0 if str1 is identical to str2
+ * - > 0 if str1 is greater than str2
+ */
+PJ_IDECL(int) pj_strnicmp( const pj_str_t *str1, const pj_str_t *str2,
+ pj_size_t len);
+
+/**
+ * Perform lowercase comparison to the strings.
+ *
+ * @param str1 The string to compare.
+ * @param str2 The string to compare.
+ * @param len The maximum number of characters to compare.
+ *
+ * @return
+ * - < 0 if str1 is less than str2
+ * - 0 if str1 is identical to str2
+ * - > 0 if str1 is greater than str2
+ */
+PJ_IDECL(int) pj_strnicmp2( const pj_str_t *str1, const char *str2,
+ pj_size_t len);
+
+/**
+ * Concatenate strings.
+ *
+ * @param dst The destination string.
+ * @param src The source string.
+ */
+PJ_IDECL(void) pj_strcat(pj_str_t *dst, const pj_str_t *src);
+
+/**
+ * Finds a character in a string.
+ *
+ * @param str The string.
+ * @param chr The character to find.
+ *
+ * @return the pointer to first character found, or NULL.
+ */
+PJ_INLINE(char*) pj_strchr( pj_str_t *str, int chr)
+{
+ return (char*) memchr(str->ptr, chr, str->slen);
+}
+
+/**
+ * Remove (trim) leading whitespaces from the string.
+ *
+ * @param str The string.
+ *
+ * @return the string.
+ */
+PJ_DECL(pj_str_t*) pj_strltrim( pj_str_t *str );
+
+/**
+ * Remove (trim) the trailing whitespaces from the string.
+ *
+ * @param str The string.
+ *
+ * @return the string.
+ */
+PJ_DECL(pj_str_t*) pj_strrtrim( pj_str_t *str );
+
+/**
+ * Remove (trim) leading and trailing whitespaces from the string.
+ *
+ * @param str The string.
+ *
+ * @return the string.
+ */
+PJ_IDECL(pj_str_t*) pj_strtrim( pj_str_t *str );
+
+/**
+ * Initialize the buffer with some random string.
+ *
+ * @param str the string to store the result.
+ * @param length the length of the random string to generate.
+ *
+ * @return the string.
+ */
+PJ_DECL(char*) pj_create_random_string(char *str, pj_size_t length);
+
+/**
+ * Convert string to unsigned integer.
+ *
+ * @param str the string.
+ *
+ * @return the unsigned integer.
+ */
+PJ_DECL(unsigned long) pj_strtoul(const pj_str_t *str);
+
+/**
+ * Utility to convert unsigned integer to string. Note that the
+ * string will be NULL terminated.
+ *
+ * @param val the unsigned integer value.
+ * @param buf the buffer
+ *
+ * @return the number of characters written
+ */
+PJ_DECL(int) pj_utoa(unsigned long val, char *buf);
+
+/**
+ * Convert unsigned integer to string with minimum digits. Note that the
+ * string will be NULL terminated.
+ *
+ * @param val The unsigned integer value.
+ * @param buf The buffer.
+ * @param min_dig Minimum digits to be printed, or zero to specify no
+ * minimum digit.
+ * @param pad The padding character to be put in front of the string
+ * when the digits is less than minimum.
+ *
+ * @return the number of characters written.
+ */
+PJ_DECL(int) pj_utoa_pad( unsigned long val, char *buf, int min_dig, int pad);
+
+/**
+ * Fill the memory location with value.
+ *
+ * @param dst The destination buffer.
+ * @param c Character to set.
+ * @param size The number of characters.
+ *
+ * @return the value of dst.
+ */
+PJ_INLINE(void*) pj_memset(void *dst, int c, pj_size_t size)
+{
+ return memset(dst, c, size);
+}
+
+/**
+ * Copy buffer.
+ *
+ * @param dst The destination buffer.
+ * @param src The source buffer.
+ * @param size The size to copy.
+ *
+ * @return the destination buffer.
+ */
+PJ_INLINE(void*) pj_memcpy(void *dst, const void *src, pj_size_t size)
+{
+ return memcpy(dst, src, size);
+}
+
+/**
+ * Move memory.
+ *
+ * @param dst The destination buffer.
+ * @param src The source buffer.
+ * @param size The size to copy.
+ *
+ * @return the destination buffer.
+ */
+PJ_INLINE(void*) pj_memmove(void *dst, const void *src, pj_size_t size)
+{
+ return memmove(dst, src, size);
+}
+
+/**
+ * Compare buffers.
+ *
+ * @param buf1 The first buffer.
+ * @param buf2 The second buffer.
+ * @param size The size to compare.
+ *
+ * @return negative, zero, or positive value.
+ */
+PJ_INLINE(int) pj_memcmp(const void *buf1, const void *buf2, pj_size_t size)
+{
+ return memcmp(buf1, buf2, size);
+}
+
+/**
+ * Find character in the buffer.
+ *
+ * @param buf The buffer.
+ * @param c The character to find.
+ * @param size The size to check.
+ *
+ * @return the pointer to location where the character is found, or NULL if
+ * not found.
+ */
+PJ_INLINE(void*) pj_memchr(const void *buf, int c, pj_size_t size)
+{
+ return memchr(buf, c, size);
+}
+
+/**
+ * @}
+ */
+
+#if PJ_FUNCTIONS_ARE_INLINED
+# include <pj/string_i.h>
+#endif
+
+PJ_END_DECL
+
+#endif /* __PJ_STRING_H__ */
+
diff --git a/pjlib/include/pj/string_i.h b/pjlib/include/pj/string_i.h
index 46408b3..1d93c18 100644
--- a/pjlib/include/pj/string_i.h
+++ b/pjlib/include/pj/string_i.h
@@ -1,164 +1,185 @@
-/* $Id$
- *
- */
-
-PJ_IDEF(pj_str_t) pj_str(char *str)
-{
- pj_str_t dst;
- dst.ptr = str;
- dst.slen = str ? strlen(str) : 0;
- return dst;
-}
-
-PJ_IDEF(pj_str_t*) pj_strdup(pj_pool_t *pool,
- pj_str_t *dst,
- const pj_str_t *src)
-{
- if (src->slen) {
- dst->ptr = (char*)pj_pool_alloc(pool, src->slen);
- pj_memcpy(dst->ptr, src->ptr, src->slen);
- }
- dst->slen = src->slen;
- return dst;
-}
-
-PJ_IDEF(pj_str_t*) pj_strdup_with_null( pj_pool_t *pool,
- pj_str_t *dst,
- const pj_str_t *src)
-{
- if (src->slen) {
- dst->ptr = (char*)pj_pool_alloc(pool, src->slen+1);
- pj_memcpy(dst->ptr, src->ptr, src->slen);
- } else {
- dst->ptr = (char*)pj_pool_alloc(pool, 1);
- }
- dst->slen = src->slen;
- dst->ptr[dst->slen] = '\0';
- return dst;
-}
-
-PJ_IDEF(pj_str_t*) pj_strdup2(pj_pool_t *pool,
- pj_str_t *dst,
- const char *src)
-{
- dst->slen = src ? strlen(src) : 0;
- if (dst->slen) {
- dst->ptr = (char*)pj_pool_alloc(pool, dst->slen);
- pj_memcpy(dst->ptr, src, dst->slen);
- } else {
- dst->ptr = NULL;
- }
- return dst;
-}
-
-
-PJ_IDEF(pj_str_t) pj_strdup3(pj_pool_t *pool, const char *src)
-{
- pj_str_t temp;
- pj_strdup2(pool, &temp, src);
- return temp;
-}
-
-PJ_IDEF(pj_str_t*) pj_strassign( pj_str_t *dst, pj_str_t *src )
-{
- dst->ptr = src->ptr;
- dst->slen = src->slen;
- return dst;
-}
-
-PJ_IDEF(pj_str_t*) pj_strcpy(pj_str_t *dst, const pj_str_t *src)
-{
- dst->slen = src->slen;
- if (src->slen > 0)
- pj_memcpy(dst->ptr, src->ptr, src->slen);
- return dst;
-}
-
-PJ_IDEF(pj_str_t*) pj_strcpy2(pj_str_t *dst, const char *src)
-{
- dst->slen = src ? strlen(src) : 0;
- if (dst->slen > 0)
- pj_memcpy(dst->ptr, src, dst->slen);
- return dst;
-}
-
-PJ_IDEF(int) pj_strcmp( const pj_str_t *str1, const pj_str_t *str2)
-{
- pj_ssize_t diff;
-
- diff = str1->slen - str2->slen;
- if (diff) {
- return (int)diff;
- } else if (str1->ptr) {
- return strncmp(str1->ptr, str2->ptr, str1->slen);
- } else {
- return 0;
- }
-}
-
-PJ_IDEF(int) pj_strncmp( const pj_str_t *str1, const pj_str_t *str2,
- pj_size_t len)
-{
- return (str1->ptr && str2->ptr) ? strncmp(str1->ptr, str2->ptr, len) :
- (str1->ptr == str2->ptr ? 0 : 1);
-}
-
-PJ_IDEF(int) pj_strncmp2( const pj_str_t *str1, const char *str2,
- pj_size_t len)
-{
- return (str1->ptr && str2) ? strncmp(str1->ptr, str2, len) :
- (str1->ptr==str2 ? 0 : 1);
-}
-
-PJ_IDEF(int) pj_strcmp2( const pj_str_t *str1, const char *str2 )
-{
- return pj_strncmp2( str1, str2, str1->slen);
-}
-
-PJ_IDEF(int) pj_stricmp( const pj_str_t *str1, const pj_str_t *str2)
-{
- pj_ssize_t diff;
-
- diff = str1->slen - str2->slen;
- if (diff) {
- return (int)diff;
- } else {
- return strnicmp(str1->ptr, str2->ptr, str1->slen);
- }
-}
-
-PJ_IDEF(int) pj_stricmp2( const pj_str_t *str1, const char *str2)
-{
- return (str1->ptr && str2) ? strnicmp(str1->ptr, str2, str1->slen) :
- (str1->ptr==str2 ? 0 : 1);
-}
-
-PJ_IDEF(int) pj_strnicmp( const pj_str_t *str1, const pj_str_t *str2,
- pj_size_t len)
-{
- return (str1->ptr && str2->ptr) ? strnicmp(str1->ptr, str2->ptr, len) :
- (str1->ptr == str2->ptr ? 0 : 1);
-}
-
-PJ_IDEF(int) pj_strnicmp2( const pj_str_t *str1, const char *str2,
- pj_size_t len)
-{
- return (str1->ptr && str2) ? strnicmp(str1->ptr, str2, len) :
- (str1->ptr == str2 ? 0 : 1);
-}
-
-PJ_IDEF(void) pj_strcat(pj_str_t *dst, const pj_str_t *src)
-{
- if (src->slen) {
- pj_memcpy(dst->ptr + dst->slen, src->ptr, src->slen);
- dst->slen += src->slen;
- }
-}
-
-PJ_IDEF(pj_str_t*) pj_strtrim( pj_str_t *str )
-{
- pj_strltrim(str);
- pj_strrtrim(str);
- return str;
-}
-
+/* $Id$
+ *
+ */
+/*
+ * PJLIB - PJ Foundation Library
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ * Benny Prijono <bennylp@bulukucing.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+PJ_IDEF(pj_str_t) pj_str(char *str)
+{
+ pj_str_t dst;
+ dst.ptr = str;
+ dst.slen = str ? strlen(str) : 0;
+ return dst;
+}
+
+PJ_IDEF(pj_str_t*) pj_strdup(pj_pool_t *pool,
+ pj_str_t *dst,
+ const pj_str_t *src)
+{
+ if (src->slen) {
+ dst->ptr = (char*)pj_pool_alloc(pool, src->slen);
+ pj_memcpy(dst->ptr, src->ptr, src->slen);
+ }
+ dst->slen = src->slen;
+ return dst;
+}
+
+PJ_IDEF(pj_str_t*) pj_strdup_with_null( pj_pool_t *pool,
+ pj_str_t *dst,
+ const pj_str_t *src)
+{
+ if (src->slen) {
+ dst->ptr = (char*)pj_pool_alloc(pool, src->slen+1);
+ pj_memcpy(dst->ptr, src->ptr, src->slen);
+ } else {
+ dst->ptr = (char*)pj_pool_alloc(pool, 1);
+ }
+ dst->slen = src->slen;
+ dst->ptr[dst->slen] = '\0';
+ return dst;
+}
+
+PJ_IDEF(pj_str_t*) pj_strdup2(pj_pool_t *pool,
+ pj_str_t *dst,
+ const char *src)
+{
+ dst->slen = src ? strlen(src) : 0;
+ if (dst->slen) {
+ dst->ptr = (char*)pj_pool_alloc(pool, dst->slen);
+ pj_memcpy(dst->ptr, src, dst->slen);
+ } else {
+ dst->ptr = NULL;
+ }
+ return dst;
+}
+
+
+PJ_IDEF(pj_str_t) pj_strdup3(pj_pool_t *pool, const char *src)
+{
+ pj_str_t temp;
+ pj_strdup2(pool, &temp, src);
+ return temp;
+}
+
+PJ_IDEF(pj_str_t*) pj_strassign( pj_str_t *dst, pj_str_t *src )
+{
+ dst->ptr = src->ptr;
+ dst->slen = src->slen;
+ return dst;
+}
+
+PJ_IDEF(pj_str_t*) pj_strcpy(pj_str_t *dst, const pj_str_t *src)
+{
+ dst->slen = src->slen;
+ if (src->slen > 0)
+ pj_memcpy(dst->ptr, src->ptr, src->slen);
+ return dst;
+}
+
+PJ_IDEF(pj_str_t*) pj_strcpy2(pj_str_t *dst, const char *src)
+{
+ dst->slen = src ? strlen(src) : 0;
+ if (dst->slen > 0)
+ pj_memcpy(dst->ptr, src, dst->slen);
+ return dst;
+}
+
+PJ_IDEF(int) pj_strcmp( const pj_str_t *str1, const pj_str_t *str2)
+{
+ pj_ssize_t diff;
+
+ diff = str1->slen - str2->slen;
+ if (diff) {
+ return (int)diff;
+ } else if (str1->ptr) {
+ return strncmp(str1->ptr, str2->ptr, str1->slen);
+ } else {
+ return 0;
+ }
+}
+
+PJ_IDEF(int) pj_strncmp( const pj_str_t *str1, const pj_str_t *str2,
+ pj_size_t len)
+{
+ return (str1->ptr && str2->ptr) ? strncmp(str1->ptr, str2->ptr, len) :
+ (str1->ptr == str2->ptr ? 0 : 1);
+}
+
+PJ_IDEF(int) pj_strncmp2( const pj_str_t *str1, const char *str2,
+ pj_size_t len)
+{
+ return (str1->ptr && str2) ? strncmp(str1->ptr, str2, len) :
+ (str1->ptr==str2 ? 0 : 1);
+}
+
+PJ_IDEF(int) pj_strcmp2( const pj_str_t *str1, const char *str2 )
+{
+ return pj_strncmp2( str1, str2, str1->slen);
+}
+
+PJ_IDEF(int) pj_stricmp( const pj_str_t *str1, const pj_str_t *str2)
+{
+ pj_ssize_t diff;
+
+ diff = str1->slen - str2->slen;
+ if (diff) {
+ return (int)diff;
+ } else {
+ return strnicmp(str1->ptr, str2->ptr, str1->slen);
+ }
+}
+
+PJ_IDEF(int) pj_stricmp2( const pj_str_t *str1, const char *str2)
+{
+ return (str1->ptr && str2) ? strnicmp(str1->ptr, str2, str1->slen) :
+ (str1->ptr==str2 ? 0 : 1);
+}
+
+PJ_IDEF(int) pj_strnicmp( const pj_str_t *str1, const pj_str_t *str2,
+ pj_size_t len)
+{
+ return (str1->ptr && str2->ptr) ? strnicmp(str1->ptr, str2->ptr, len) :
+ (str1->ptr == str2->ptr ? 0 : 1);
+}
+
+PJ_IDEF(int) pj_strnicmp2( const pj_str_t *str1, const char *str2,
+ pj_size_t len)
+{
+ return (str1->ptr && str2) ? strnicmp(str1->ptr, str2, len) :
+ (str1->ptr == str2 ? 0 : 1);
+}
+
+PJ_IDEF(void) pj_strcat(pj_str_t *dst, const pj_str_t *src)
+{
+ if (src->slen) {
+ pj_memcpy(dst->ptr + dst->slen, src->ptr, src->slen);
+ dst->slen += src->slen;
+ }
+}
+
+PJ_IDEF(pj_str_t*) pj_strtrim( pj_str_t *str )
+{
+ pj_strltrim(str);
+ pj_strrtrim(str);
+ return str;
+}
+
diff --git a/pjlib/include/pj/timer.h b/pjlib/include/pj/timer.h
index e8772f2..7ad5e4f 100644
--- a/pjlib/include/pj/timer.h
+++ b/pjlib/include/pj/timer.h
@@ -1,140 +1,161 @@
-/* $Id$
- *
- */
-/* (C)1993-2003 Douglas C. Schmidt
- *
- * This file is originaly from ACE library by Doug Schmidt
- * ACE(TM), TAO(TM) and CIAO(TM) are copyrighted by Douglas C. Schmidt and his research
- * group at Washington University, University of California, Irvine, and Vanderbilt
- * University Copyright (c) 1993-2003, all rights reserved.
- *
- */
-
-#ifndef __PJ_TIMER_H__
-#define __PJ_TIMER_H__
-
-/**
- * @file timer.h
- * @brief Timer Heap
- */
-
-#include <pj/types.h>
-
-PJ_BEGIN_DECL
-
-/**
- * @defgroup PJ_TIMER Timer Heap Management.
- * @ingroup PJ_MISC
- * @brief
- * The timer scheduling implementation here is based on ACE library's
- * ACE_Timer_Heap, with only little modification to suit our library's style
- * (I even left most of the comments in the original source).
- *
- * To quote the original quote in ACE_Timer_Heap_T class:
- *
- * This implementation uses a heap-based callout queue of
- * absolute times. Therefore, in the average and worst case,
- * scheduling, canceling, and expiring timers is O(log N) (where
- * N is the total number of timers). In addition, we can also
- * preallocate as many \a ACE_Timer_Nodes as there are slots in
- * the heap. This allows us to completely remove the need for
- * dynamic memory allocation, which is important for real-time
- * systems.
- * @{
- *
- * \section pj_timer_examples_sec Examples
- *
- * For some examples on how to use the timer heap, please see the link below.
- *
- * - \ref page_pjlib_timer_test
- */
-
-
-/**
- * The type for internal timer ID.
- */
-typedef int pj_timer_id_t;
-
-/**
- * Forward declaration for pj_timer_entry.
- */
-struct pj_timer_entry;
-
-/**
- * The type of callback function to be called by timer scheduler when a timer
- * has expired.
- *
- * @param timer_heap The timer heap.
- * @param entry Timer entry which timer's has expired.
- */
-typedef void pj_timer_heap_callback(pj_timer_heap_t *timer_heap,
- struct pj_timer_entry *entry);
-
-
-/**
- * This structure represents an entry to the timer.
- */
-struct pj_timer_entry
-{
- /**
- * User data to be associated with this entry.
- * Applications normally will put the instance of object that
- * owns the timer entry in this field.
- */
- void *user_data;
-
- /**
- * Arbitrary ID assigned by the user/owner of this entry.
- * Applications can use this ID to distinguish multiple
- * timer entries that share the same callback and user_data.
- */
- int id;
-
- /**
- * Callback to be called when the timer expires.
- */
- pj_timer_heap_callback *cb;
-
- /**
- * Internal unique timer ID, which is assigned by the timer heap.
- * Application should not touch this ID.
- */
- pj_timer_id_t _timer_id;
-
- /**
- * The future time when the timer expires, which the value is updated
- * by timer heap when the timer is scheduled.
- */
- pj_time_val _timer_value;
-};
-
-
-/**
- * Calculate memory size required to create a timer heap.
- *
- * @param count Number of timer entries to be supported.
- * @return Memory size requirement in bytes.
- */
-PJ_DECL(pj_size_t) pj_timer_heap_mem_size(pj_size_t count);
-
-/**
- * Create a timer heap.
- *
- * @param pool The pool where allocations in the timer heap will be
- * allocated. The timer heap will dynamicly allocate
- * more storate from the pool if the number of timer
- * entries registered is more than the size originally
- * requested when calling this function.
- * @param count The maximum number of timer entries to be supported
- * initially. If the application registers more entries
- * during runtime, then the timer heap will resize.
- * @param ht Pointer to receive the created timer heap.
- *
- * @return PJ_SUCCESS, or the appropriate error code.
- */
-PJ_DECL(pj_status_t) pj_timer_heap_create( pj_pool_t *pool,
- pj_size_t count,
- pj_timer_heap_t **ht);
+/* $Id$
+ *
+ */
+/*
+ * PJLIB - PJ Foundation Library
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ * Benny Prijono <bennylp@bulukucing.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+/* (C)1993-2003 Douglas C. Schmidt
+ *
+ * This file is originaly from ACE library by Doug Schmidt
+ * ACE(TM), TAO(TM) and CIAO(TM) are copyrighted by Douglas C. Schmidt and his research
+ * group at Washington University, University of California, Irvine, and Vanderbilt
+ * University Copyright (c) 1993-2003, all rights reserved.
+ *
+ */
+
+#ifndef __PJ_TIMER_H__
+#define __PJ_TIMER_H__
+
+/**
+ * @file timer.h
+ * @brief Timer Heap
+ */
+
+#include <pj/types.h>
+
+PJ_BEGIN_DECL
+
+/**
+ * @defgroup PJ_TIMER Timer Heap Management.
+ * @ingroup PJ_MISC
+ * @brief
+ * The timer scheduling implementation here is based on ACE library's
+ * ACE_Timer_Heap, with only little modification to suit our library's style
+ * (I even left most of the comments in the original source).
+ *
+ * To quote the original quote in ACE_Timer_Heap_T class:
+ *
+ * This implementation uses a heap-based callout queue of
+ * absolute times. Therefore, in the average and worst case,
+ * scheduling, canceling, and expiring timers is O(log N) (where
+ * N is the total number of timers). In addition, we can also
+ * preallocate as many \a ACE_Timer_Nodes as there are slots in
+ * the heap. This allows us to completely remove the need for
+ * dynamic memory allocation, which is important for real-time
+ * systems.
+ * @{
+ *
+ * \section pj_timer_examples_sec Examples
+ *
+ * For some examples on how to use the timer heap, please see the link below.
+ *
+ * - \ref page_pjlib_timer_test
+ */
+
+
+/**
+ * The type for internal timer ID.
+ */
+typedef int pj_timer_id_t;
+
+/**
+ * Forward declaration for pj_timer_entry.
+ */
+struct pj_timer_entry;
+
+/**
+ * The type of callback function to be called by timer scheduler when a timer
+ * has expired.
+ *
+ * @param timer_heap The timer heap.
+ * @param entry Timer entry which timer's has expired.
+ */
+typedef void pj_timer_heap_callback(pj_timer_heap_t *timer_heap,
+ struct pj_timer_entry *entry);
+
+
+/**
+ * This structure represents an entry to the timer.
+ */
+struct pj_timer_entry
+{
+ /**
+ * User data to be associated with this entry.
+ * Applications normally will put the instance of object that
+ * owns the timer entry in this field.
+ */
+ void *user_data;
+
+ /**
+ * Arbitrary ID assigned by the user/owner of this entry.
+ * Applications can use this ID to distinguish multiple
+ * timer entries that share the same callback and user_data.
+ */
+ int id;
+
+ /**
+ * Callback to be called when the timer expires.
+ */
+ pj_timer_heap_callback *cb;
+
+ /**
+ * Internal unique timer ID, which is assigned by the timer heap.
+ * Application should not touch this ID.
+ */
+ pj_timer_id_t _timer_id;
+
+ /**
+ * The future time when the timer expires, which the value is updated
+ * by timer heap when the timer is scheduled.
+ */
+ pj_time_val _timer_value;
+};
+
+
+/**
+ * Calculate memory size required to create a timer heap.
+ *
+ * @param count Number of timer entries to be supported.
+ * @return Memory size requirement in bytes.
+ */
+PJ_DECL(pj_size_t) pj_timer_heap_mem_size(pj_size_t count);
+
+/**
+ * Create a timer heap.
+ *
+ * @param pool The pool where allocations in the timer heap will be
+ * allocated. The timer heap will dynamicly allocate
+ * more storate from the pool if the number of timer
+ * entries registered is more than the size originally
+ * requested when calling this function.
+ * @param count The maximum number of timer entries to be supported
+ * initially. If the application registers more entries
+ * during runtime, then the timer heap will resize.
+ * @param ht Pointer to receive the created timer heap.
+ *
+ * @return PJ_SUCCESS, or the appropriate error code.
+ */
+PJ_DECL(pj_status_t) pj_timer_heap_create( pj_pool_t *pool,
+ pj_size_t count,
+ pj_timer_heap_t **ht);
/**
* Destroy the timer heap.
@@ -167,92 +188,92 @@
*/
PJ_DECL(unsigned) pj_timer_heap_set_max_timed_out_per_poll(pj_timer_heap_t *ht,
unsigned count );
-
-/**
- * Initialize a timer entry. Application should call this function at least
- * once before scheduling the entry to the timer heap, to properly initialize
- * the timer entry.
- *
- * @param entry The timer entry to be initialized.
- * @param id Arbitrary ID assigned by the user/owner of this entry.
- * Applications can use this ID to distinguish multiple
- * timer entries that share the same callback and user_data.
- * @param user_data User data to be associated with this entry.
- * Applications normally will put the instance of object that
- * owns the timer entry in this field.
- * @param cb Callback function to be called when the timer elapses.
- *
- * @return The timer entry itself.
- */
-PJ_DECL(pj_timer_entry*) pj_timer_entry_init( pj_timer_entry *entry,
- int id,
- void *user_data,
- pj_timer_heap_callback *cb );
-
-/**
- * Schedule a timer entry which will expire AFTER the specified delay.
- *
- * @param ht The timer heap.
- * @param entry The entry to be registered.
- * @param delay The interval to expire.
- * @return PJ_SUCCESS, or the appropriate error code.
- */
-PJ_DECL(pj_status_t) pj_timer_heap_schedule( pj_timer_heap_t *ht,
- pj_timer_entry *entry,
- const pj_time_val *delay);
-
-/**
- * Cancel a previously registered timer.
- *
- * @param ht The timer heap.
- * @param entry The entry to be cancelled.
- * @return The number of timer cancelled, which should be one if the
- * entry has really been registered, or zero if no timer was
- * cancelled.
- */
-PJ_DECL(int) pj_timer_heap_cancel( pj_timer_heap_t *ht,
- pj_timer_entry *entry);
-
-/**
- * Get the number of timer entries.
- *
- * @param ht The timer heap.
- * @return The number of timer entries.
- */
-PJ_DECL(pj_size_t) pj_timer_heap_count( pj_timer_heap_t *ht );
-
-/**
- * Get the earliest time registered in the timer heap. The timer heap
- * MUST have at least one timer being scheduled (application should use
- * #pj_timer_heap_count() before calling this function).
- *
- * @param ht The timer heap.
- * @param timeval The time deadline of the earliest timer entry.
- *
- * @return PJ_SUCCESS, or PJ_ENOTFOUND if no entry is scheduled.
- */
-PJ_DECL(pj_status_t) pj_timer_heap_earliest_time( pj_timer_heap_t *ht,
- pj_time_val *timeval);
-
-/**
- * Poll the timer heap, check for expired timers and call the callback for
- * each of the expired timers.
- *
- * @param ht The timer heap.
- * @param next_delay If this parameter is not NULL, it will be filled up with
- * the time delay until the next timer elapsed, or -1 in
+
+/**
+ * Initialize a timer entry. Application should call this function at least
+ * once before scheduling the entry to the timer heap, to properly initialize
+ * the timer entry.
+ *
+ * @param entry The timer entry to be initialized.
+ * @param id Arbitrary ID assigned by the user/owner of this entry.
+ * Applications can use this ID to distinguish multiple
+ * timer entries that share the same callback and user_data.
+ * @param user_data User data to be associated with this entry.
+ * Applications normally will put the instance of object that
+ * owns the timer entry in this field.
+ * @param cb Callback function to be called when the timer elapses.
+ *
+ * @return The timer entry itself.
+ */
+PJ_DECL(pj_timer_entry*) pj_timer_entry_init( pj_timer_entry *entry,
+ int id,
+ void *user_data,
+ pj_timer_heap_callback *cb );
+
+/**
+ * Schedule a timer entry which will expire AFTER the specified delay.
+ *
+ * @param ht The timer heap.
+ * @param entry The entry to be registered.
+ * @param delay The interval to expire.
+ * @return PJ_SUCCESS, or the appropriate error code.
+ */
+PJ_DECL(pj_status_t) pj_timer_heap_schedule( pj_timer_heap_t *ht,
+ pj_timer_entry *entry,
+ const pj_time_val *delay);
+
+/**
+ * Cancel a previously registered timer.
+ *
+ * @param ht The timer heap.
+ * @param entry The entry to be cancelled.
+ * @return The number of timer cancelled, which should be one if the
+ * entry has really been registered, or zero if no timer was
+ * cancelled.
+ */
+PJ_DECL(int) pj_timer_heap_cancel( pj_timer_heap_t *ht,
+ pj_timer_entry *entry);
+
+/**
+ * Get the number of timer entries.
+ *
+ * @param ht The timer heap.
+ * @return The number of timer entries.
+ */
+PJ_DECL(pj_size_t) pj_timer_heap_count( pj_timer_heap_t *ht );
+
+/**
+ * Get the earliest time registered in the timer heap. The timer heap
+ * MUST have at least one timer being scheduled (application should use
+ * #pj_timer_heap_count() before calling this function).
+ *
+ * @param ht The timer heap.
+ * @param timeval The time deadline of the earliest timer entry.
+ *
+ * @return PJ_SUCCESS, or PJ_ENOTFOUND if no entry is scheduled.
+ */
+PJ_DECL(pj_status_t) pj_timer_heap_earliest_time( pj_timer_heap_t *ht,
+ pj_time_val *timeval);
+
+/**
+ * Poll the timer heap, check for expired timers and call the callback for
+ * each of the expired timers.
+ *
+ * @param ht The timer heap.
+ * @param next_delay If this parameter is not NULL, it will be filled up with
+ * the time delay until the next timer elapsed, or -1 in
* the sec part if no entry exist.
- *
- * @return The number of timers expired.
- */
+ *
+ * @return The number of timers expired.
+ */
PJ_DECL(unsigned) pj_timer_heap_poll( pj_timer_heap_t *ht,
- pj_time_val *next_delay);
-
-/**
- * @}
- */
-
-PJ_END_DECL
-
-#endif /* __PJ_TIMER_H__ */
-
+ pj_time_val *next_delay);
+
+/**
+ * @}
+ */
+
+PJ_END_DECL
+
+#endif /* __PJ_TIMER_H__ */
+
diff --git a/pjlib/include/pj/types.h b/pjlib/include/pj/types.h
index ec3df2c..acdfbd2 100644
--- a/pjlib/include/pj/types.h
+++ b/pjlib/include/pj/types.h
@@ -1,68 +1,87 @@
-/* $Id$
- *
- */
-
-#ifndef __PJ_TYPES_H__
-#define __PJ_TYPES_H__
-
-
-/**
- * @defgroup PJ PJ Library
- */
-/**
- * @file types.h
- * @brief Declaration of basic types and utility.
- */
-/**
- * @defgroup PJ_BASIC Basic Data Types and Library Functionality.
- * @ingroup PJ_DS
- * @{
- */
-#include <pj/config.h>
-
-PJ_BEGIN_DECL
-
-///////////////////////////////////////////////////////////////////////////////
-
-/** Unsigned 32bit integer. */
-typedef int pj_int32_t;
-
-/** Signed 32bit integer. */
-typedef unsigned int pj_uint32_t;
-
-/** Unsigned 16bit integer. */
-typedef short pj_int16_t;
-
-/** Signed 16bit integer. */
-typedef unsigned short pj_uint16_t;
-
-/** Unsigned 8bit integer. */
-typedef signed char pj_int8_t;
-
-/** Signed 16bit integer. */
-typedef unsigned char pj_uint8_t;
-
-/** Large unsigned integer. */
-typedef size_t pj_size_t;
-
-/** Large signed integer. */
-typedef long pj_ssize_t;
-
-/** Status code. */
-typedef int pj_status_t;
-
-/** Boolean. */
-typedef int pj_bool_t;
-
-/** Status is OK. */
-#define PJ_SUCCESS 0
-
-/** True value. */
-#define PJ_TRUE 1
-
-/** False value. */
-#define PJ_FALSE 0
-
+/* $Id$ */
+/*
+ * PJLIB - PJ Foundation Library
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ * Benny Prijono <bennylp@bulukucing.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef __PJ_TYPES_H__
+#define __PJ_TYPES_H__
+
+
+/**
+ * @defgroup PJ PJ Library
+ */
+/**
+ * @file types.h
+ * @brief Declaration of basic types and utility.
+ */
+/**
+ * @defgroup PJ_BASIC Basic Data Types and Library Functionality.
+ * @ingroup PJ_DS
+ * @{
+ */
+#include <pj/config.h>
+
+PJ_BEGIN_DECL
+
+///////////////////////////////////////////////////////////////////////////////
+
+/** Unsigned 32bit integer. */
+typedef int pj_int32_t;
+
+/** Signed 32bit integer. */
+typedef unsigned int pj_uint32_t;
+
+/** Unsigned 16bit integer. */
+typedef short pj_int16_t;
+
+/** Signed 16bit integer. */
+typedef unsigned short pj_uint16_t;
+
+/** Unsigned 8bit integer. */
+typedef signed char pj_int8_t;
+
+/** Signed 16bit integer. */
+typedef unsigned char pj_uint8_t;
+
+/** Large unsigned integer. */
+typedef size_t pj_size_t;
+
+/** Large signed integer. */
+typedef long pj_ssize_t;
+
+/** Status code. */
+typedef int pj_status_t;
+
+/** Boolean. */
+typedef int pj_bool_t;
+
+/** Status is OK. */
+#define PJ_SUCCESS 0
+
+/** True value. */
+#define PJ_TRUE 1
+
+/** False value. */
+#define PJ_FALSE 0
+
/**
* File offset type.
*/
@@ -71,358 +90,358 @@
#else
typedef pj_ssize_t pj_off_t;
#endif
-
-///////////////////////////////////////////////////////////////////////////////
-/*
- * Data structure types.
- */
-/**
- * This type is used as replacement to legacy C string, and used throughout
- * the library. By convention, the string is NOT null terminated.
- */
-struct pj_str_t
-{
- /** Buffer pointer, which is by convention NOT null terminated. */
- char *ptr;
-
- /** The length of the string. */
- pj_ssize_t slen;
-};
-
-
-/**
- * The opaque data type for linked list, which is used as arguments throughout
- * the linked list operations.
- */
-typedef void pj_list_type;
-
-/**
- * List.
- */
-typedef struct pj_list pj_list;
-
-/**
- * Opaque data type for hash tables.
- */
-typedef struct pj_hash_table_t pj_hash_table_t;
-
-/**
- * Opaque data type for hash entry (only used internally by hash table).
- */
-typedef struct pj_hash_entry pj_hash_entry;
-
-/**
- * Data type for hash search iterator.
- * This structure should be opaque, however applications need to declare
- * concrete variable of this type, that's why the declaration is visible here.
- */
-typedef struct pj_hash_iterator_t
-{
- pj_uint32_t index; /**< Internal index. */
- pj_hash_entry *entry; /**< Internal entry. */
-} pj_hash_iterator_t;
-
-
-/**
- * Forward declaration for memory pool factory.
- */
-typedef struct pj_pool_factory pj_pool_factory;
-
-/**
- * Opaque data type for memory pool.
- */
-typedef struct pj_pool_t pj_pool_t;
-
-/**
- * Forward declaration for caching pool, a pool factory implementation.
- */
-typedef struct pj_caching_pool pj_caching_pool;
-
-/**
- * This type is used as replacement to legacy C string, and used throughout
- * the library.
- */
-typedef struct pj_str_t pj_str_t;
-
-/**
- * Opaque data type for I/O Queue structure.
- */
-typedef struct pj_ioqueue_t pj_ioqueue_t;
-
-/**
- * Opaque data type for key that identifies a handle registered to the
- * I/O queue framework.
- */
-typedef struct pj_ioqueue_key_t pj_ioqueue_key_t;
-
-/**
- * Opaque data to identify timer heap.
- */
-typedef struct pj_timer_heap_t pj_timer_heap_t;
-
-/**
- * Forward declaration for timer entry.
- */
-typedef struct pj_timer_entry pj_timer_entry;
-
-/**
- * Opaque data type for atomic operations.
- */
-typedef struct pj_atomic_t pj_atomic_t;
-
-/**
- * Value type of an atomic variable.
- */
-typedef PJ_ATOMIC_VALUE_TYPE pj_atomic_value_t;
-
-///////////////////////////////////////////////////////////////////////////////
-
-/** Thread handle. */
-typedef struct pj_thread_t pj_thread_t;
-
-/** Lock object. */
-typedef struct pj_lock_t pj_lock_t;
-
-/** Mutex handle. */
-typedef struct pj_mutex_t pj_mutex_t;
-
-/** Semaphore handle. */
-typedef struct pj_sem_t pj_sem_t;
-
-/** Event object. */
-typedef struct pj_event_t pj_event_t;
-
-/** Unidirectional stream pipe object. */
-typedef struct pj_pipe_t pj_pipe_t;
-
-/** Operating system handle. */
-typedef void *pj_oshandle_t;
-
-/** Socket handle. */
-typedef long pj_sock_t;
-
-/** Generic socket address. */
-typedef void pj_sockaddr_t;
-
-/** Color type. */
-typedef unsigned int pj_color_t;
-
-/** Exception id. */
-typedef int pj_exception_id_t;
-
-///////////////////////////////////////////////////////////////////////////////
-
-/** Utility macro to compute the number of elements in static array. */
-#define PJ_ARRAY_SIZE(a) (sizeof(a)/sizeof(a[0]))
-
-/** Maximum value for signed 32-bit integer. */
-#define PJ_MAXINT32 0x7FFFFFFFL
-
-/**
- * Length of object names.
- */
-#define PJ_MAX_OBJ_NAME 16
-
-///////////////////////////////////////////////////////////////////////////////
-/*
- * General.
- */
-/**
- * Initialize the PJ Library.
- * This function must be called before using the library. The purpose of this
- * function is to initialize static library data, such as character table used
- * in random string generation, and to initialize operating system dependent
- * functionality (such as WSAStartup() in Windows).
- */
-PJ_DECL(pj_status_t) pj_init(void);
-
-
-/**
- * @}
- */
-/**
- * @addtogroup PJ_TIME Time Data Type and Manipulation.
- * @ingroup PJ_MISC
- * @{
- */
-
-/**
- * Representation of time value in this library.
- * This type can be used to represent either an interval or a specific time
- * or date.
- */
-typedef struct pj_time_val
-{
- /** The seconds part of the time. */
- long sec;
-
- /** The miliseconds fraction of the time. */
- long msec;
-
-} pj_time_val;
-
-/**
- * Normalize the value in time value.
- * @param t Time value to be normalized.
- */
-PJ_DECL(void) pj_time_val_normalize(pj_time_val *t);
-
-/**
- * Get the total time value in miliseconds. This is the same as
- * multiplying the second part with 1000 and then add the miliseconds
- * part to the result.
- *
- * @param t The time value.
- * @return Total time in miliseconds.
- * @hideinitializer
- */
-#define PJ_TIME_VAL_MSEC(t) ((t).sec * 1000 + (t).msec)
-
-/**
- * This macro will check if \a t1 is equal to \a t2.
- *
- * @param t1 The first time value to compare.
- * @param t2 The second time value to compare.
- * @return Non-zero if both time values are equal.
- * @hideinitializer
- */
-#define PJ_TIME_VAL_EQ(t1, t2) ((t1).sec==(t2).sec && (t1).msec==(t2).msec)
-
-/**
- * This macro will check if \a t1 is greater than \a t2
- *
- * @param t1 The first time value to compare.
- * @param t2 The second time value to compare.
- * @return Non-zero if t1 is greater than t2.
- * @hideinitializer
- */
-#define PJ_TIME_VAL_GT(t1, t2) ((t1).sec>(t2).sec || \
- ((t1).sec==(t2).sec && (t1).msec>(t2).msec))
-
-/**
- * This macro will check if \a t1 is greater than or equal to \a t2
- *
- * @param t1 The first time value to compare.
- * @param t2 The second time value to compare.
- * @return Non-zero if t1 is greater than or equal to t2.
- * @hideinitializer
- */
-#define PJ_TIME_VAL_GTE(t1, t2) (PJ_TIME_VAL_GT(t1,t2) || \
- PJ_TIME_VAL_EQ(t1,t2))
-
-/**
- * This macro will check if \a t1 is less than \a t2
- *
- * @param t1 The first time value to compare.
- * @param t2 The second time value to compare.
- * @return Non-zero if t1 is less than t2.
- * @hideinitializer
- */
-#define PJ_TIME_VAL_LT(t1, t2) (!(PJ_TIME_VAL_GTE(t1,t2)))
-
-/**
- * This macro will check if \a t1 is less than or equal to \a t2.
- *
- * @param t1 The first time value to compare.
- * @param t2 The second time value to compare.
- * @return Non-zero if t1 is less than or equal to t2.
- * @hideinitializer
- */
-#define PJ_TIME_VAL_LTE(t1, t2) (!PJ_TIME_VAL_GT(t1, t2))
-
-/**
- * Add \a t2 to \a t1 and store the result in \a t1. Effectively
- *
- * this macro will expand as: (\a t1 += \a t2).
- * @param t1 The time value to add.
- * @param t2 The time value to be added to \a t1.
- * @hideinitializer
- */
-#define PJ_TIME_VAL_ADD(t1, t2) do { \
- (t1).sec += (t2).sec; \
- (t1).msec += (t2).msec; \
- pj_time_val_normalize(&(t1)); \
- } while (0)
-
-
-/**
- * Substract \a t2 from \a t1 and store the result in \a t1. Effectively
- * this macro will expand as (\a t1 -= \a t2).
- *
- * @param t1 The time value to subsctract.
- * @param t2 The time value to be substracted from \a t1.
- * @hideinitializer
- */
-#define PJ_TIME_VAL_SUB(t1, t2) do { \
- (t1).sec -= (t2).sec; \
- (t1).msec -= (t2).msec; \
- pj_time_val_normalize(&(t1)); \
- } while (0)
-
-
-/**
- * This structure represent the parsed representation of time.
- * It is acquired by calling #pj_time_decode().
- */
-typedef struct pj_parsed_time
-{
- /** This represents day of week where value zero means Sunday */
- int wday;
-
- /** This represents day of the year, 0-365, where zero means
- * 1st of January.
- */
- int yday;
-
- /** This represents day of month: 1-31 */
- int day;
-
- /** This represents month, with the value is 0 - 11 (zero is January) */
- int mon;
-
- /** This represent the actual year (unlike in ANSI libc where
- * the value must be added by 1900).
- */
- int year;
-
- /** This represents the second part, with the value is 0-59 */
- int sec;
-
- /** This represents the minute part, with the value is: 0-59 */
- int min;
-
- /** This represents the hour part, with the value is 0-23 */
- int hour;
-
- /** This represents the milisecond part, with the value is 0-999 */
- int msec;
-
-} pj_parsed_time;
-
-
-/**
- * @} // Time Management
- */
-
-///////////////////////////////////////////////////////////////////////////////
-/*
- * Terminal.
- */
-/**
- * Color code combination.
- */
-enum {
- PJ_TERM_COLOR_R = 2, /**< Red */
- PJ_TERM_COLOR_G = 4, /**< Green */
- PJ_TERM_COLOR_B = 1, /**< Blue. */
- PJ_TERM_COLOR_BRIGHT = 8 /**< Bright mask. */
-};
-
-
-
-
-PJ_END_DECL
-
-
-#endif /* __PJ_TYPES_H__ */
-
+
+///////////////////////////////////////////////////////////////////////////////
+/*
+ * Data structure types.
+ */
+/**
+ * This type is used as replacement to legacy C string, and used throughout
+ * the library. By convention, the string is NOT null terminated.
+ */
+struct pj_str_t
+{
+ /** Buffer pointer, which is by convention NOT null terminated. */
+ char *ptr;
+
+ /** The length of the string. */
+ pj_ssize_t slen;
+};
+
+
+/**
+ * The opaque data type for linked list, which is used as arguments throughout
+ * the linked list operations.
+ */
+typedef void pj_list_type;
+
+/**
+ * List.
+ */
+typedef struct pj_list pj_list;
+
+/**
+ * Opaque data type for hash tables.
+ */
+typedef struct pj_hash_table_t pj_hash_table_t;
+
+/**
+ * Opaque data type for hash entry (only used internally by hash table).
+ */
+typedef struct pj_hash_entry pj_hash_entry;
+
+/**
+ * Data type for hash search iterator.
+ * This structure should be opaque, however applications need to declare
+ * concrete variable of this type, that's why the declaration is visible here.
+ */
+typedef struct pj_hash_iterator_t
+{
+ pj_uint32_t index; /**< Internal index. */
+ pj_hash_entry *entry; /**< Internal entry. */
+} pj_hash_iterator_t;
+
+
+/**
+ * Forward declaration for memory pool factory.
+ */
+typedef struct pj_pool_factory pj_pool_factory;
+
+/**
+ * Opaque data type for memory pool.
+ */
+typedef struct pj_pool_t pj_pool_t;
+
+/**
+ * Forward declaration for caching pool, a pool factory implementation.
+ */
+typedef struct pj_caching_pool pj_caching_pool;
+
+/**
+ * This type is used as replacement to legacy C string, and used throughout
+ * the library.
+ */
+typedef struct pj_str_t pj_str_t;
+
+/**
+ * Opaque data type for I/O Queue structure.
+ */
+typedef struct pj_ioqueue_t pj_ioqueue_t;
+
+/**
+ * Opaque data type for key that identifies a handle registered to the
+ * I/O queue framework.
+ */
+typedef struct pj_ioqueue_key_t pj_ioqueue_key_t;
+
+/**
+ * Opaque data to identify timer heap.
+ */
+typedef struct pj_timer_heap_t pj_timer_heap_t;
+
+/**
+ * Forward declaration for timer entry.
+ */
+typedef struct pj_timer_entry pj_timer_entry;
+
+/**
+ * Opaque data type for atomic operations.
+ */
+typedef struct pj_atomic_t pj_atomic_t;
+
+/**
+ * Value type of an atomic variable.
+ */
+typedef PJ_ATOMIC_VALUE_TYPE pj_atomic_value_t;
+
+///////////////////////////////////////////////////////////////////////////////
+
+/** Thread handle. */
+typedef struct pj_thread_t pj_thread_t;
+
+/** Lock object. */
+typedef struct pj_lock_t pj_lock_t;
+
+/** Mutex handle. */
+typedef struct pj_mutex_t pj_mutex_t;
+
+/** Semaphore handle. */
+typedef struct pj_sem_t pj_sem_t;
+
+/** Event object. */
+typedef struct pj_event_t pj_event_t;
+
+/** Unidirectional stream pipe object. */
+typedef struct pj_pipe_t pj_pipe_t;
+
+/** Operating system handle. */
+typedef void *pj_oshandle_t;
+
+/** Socket handle. */
+typedef long pj_sock_t;
+
+/** Generic socket address. */
+typedef void pj_sockaddr_t;
+
+/** Color type. */
+typedef unsigned int pj_color_t;
+
+/** Exception id. */
+typedef int pj_exception_id_t;
+
+///////////////////////////////////////////////////////////////////////////////
+
+/** Utility macro to compute the number of elements in static array. */
+#define PJ_ARRAY_SIZE(a) (sizeof(a)/sizeof(a[0]))
+
+/** Maximum value for signed 32-bit integer. */
+#define PJ_MAXINT32 0x7FFFFFFFL
+
+/**
+ * Length of object names.
+ */
+#define PJ_MAX_OBJ_NAME 16
+
+///////////////////////////////////////////////////////////////////////////////
+/*
+ * General.
+ */
+/**
+ * Initialize the PJ Library.
+ * This function must be called before using the library. The purpose of this
+ * function is to initialize static library data, such as character table used
+ * in random string generation, and to initialize operating system dependent
+ * functionality (such as WSAStartup() in Windows).
+ */
+PJ_DECL(pj_status_t) pj_init(void);
+
+
+/**
+ * @}
+ */
+/**
+ * @addtogroup PJ_TIME Time Data Type and Manipulation.
+ * @ingroup PJ_MISC
+ * @{
+ */
+
+/**
+ * Representation of time value in this library.
+ * This type can be used to represent either an interval or a specific time
+ * or date.
+ */
+typedef struct pj_time_val
+{
+ /** The seconds part of the time. */
+ long sec;
+
+ /** The miliseconds fraction of the time. */
+ long msec;
+
+} pj_time_val;
+
+/**
+ * Normalize the value in time value.
+ * @param t Time value to be normalized.
+ */
+PJ_DECL(void) pj_time_val_normalize(pj_time_val *t);
+
+/**
+ * Get the total time value in miliseconds. This is the same as
+ * multiplying the second part with 1000 and then add the miliseconds
+ * part to the result.
+ *
+ * @param t The time value.
+ * @return Total time in miliseconds.
+ * @hideinitializer
+ */
+#define PJ_TIME_VAL_MSEC(t) ((t).sec * 1000 + (t).msec)
+
+/**
+ * This macro will check if \a t1 is equal to \a t2.
+ *
+ * @param t1 The first time value to compare.
+ * @param t2 The second time value to compare.
+ * @return Non-zero if both time values are equal.
+ * @hideinitializer
+ */
+#define PJ_TIME_VAL_EQ(t1, t2) ((t1).sec==(t2).sec && (t1).msec==(t2).msec)
+
+/**
+ * This macro will check if \a t1 is greater than \a t2
+ *
+ * @param t1 The first time value to compare.
+ * @param t2 The second time value to compare.
+ * @return Non-zero if t1 is greater than t2.
+ * @hideinitializer
+ */
+#define PJ_TIME_VAL_GT(t1, t2) ((t1).sec>(t2).sec || \
+ ((t1).sec==(t2).sec && (t1).msec>(t2).msec))
+
+/**
+ * This macro will check if \a t1 is greater than or equal to \a t2
+ *
+ * @param t1 The first time value to compare.
+ * @param t2 The second time value to compare.
+ * @return Non-zero if t1 is greater than or equal to t2.
+ * @hideinitializer
+ */
+#define PJ_TIME_VAL_GTE(t1, t2) (PJ_TIME_VAL_GT(t1,t2) || \
+ PJ_TIME_VAL_EQ(t1,t2))
+
+/**
+ * This macro will check if \a t1 is less than \a t2
+ *
+ * @param t1 The first time value to compare.
+ * @param t2 The second time value to compare.
+ * @return Non-zero if t1 is less than t2.
+ * @hideinitializer
+ */
+#define PJ_TIME_VAL_LT(t1, t2) (!(PJ_TIME_VAL_GTE(t1,t2)))
+
+/**
+ * This macro will check if \a t1 is less than or equal to \a t2.
+ *
+ * @param t1 The first time value to compare.
+ * @param t2 The second time value to compare.
+ * @return Non-zero if t1 is less than or equal to t2.
+ * @hideinitializer
+ */
+#define PJ_TIME_VAL_LTE(t1, t2) (!PJ_TIME_VAL_GT(t1, t2))
+
+/**
+ * Add \a t2 to \a t1 and store the result in \a t1. Effectively
+ *
+ * this macro will expand as: (\a t1 += \a t2).
+ * @param t1 The time value to add.
+ * @param t2 The time value to be added to \a t1.
+ * @hideinitializer
+ */
+#define PJ_TIME_VAL_ADD(t1, t2) do { \
+ (t1).sec += (t2).sec; \
+ (t1).msec += (t2).msec; \
+ pj_time_val_normalize(&(t1)); \
+ } while (0)
+
+
+/**
+ * Substract \a t2 from \a t1 and store the result in \a t1. Effectively
+ * this macro will expand as (\a t1 -= \a t2).
+ *
+ * @param t1 The time value to subsctract.
+ * @param t2 The time value to be substracted from \a t1.
+ * @hideinitializer
+ */
+#define PJ_TIME_VAL_SUB(t1, t2) do { \
+ (t1).sec -= (t2).sec; \
+ (t1).msec -= (t2).msec; \
+ pj_time_val_normalize(&(t1)); \
+ } while (0)
+
+
+/**
+ * This structure represent the parsed representation of time.
+ * It is acquired by calling #pj_time_decode().
+ */
+typedef struct pj_parsed_time
+{
+ /** This represents day of week where value zero means Sunday */
+ int wday;
+
+ /** This represents day of the year, 0-365, where zero means
+ * 1st of January.
+ */
+ int yday;
+
+ /** This represents day of month: 1-31 */
+ int day;
+
+ /** This represents month, with the value is 0 - 11 (zero is January) */
+ int mon;
+
+ /** This represent the actual year (unlike in ANSI libc where
+ * the value must be added by 1900).
+ */
+ int year;
+
+ /** This represents the second part, with the value is 0-59 */
+ int sec;
+
+ /** This represents the minute part, with the value is: 0-59 */
+ int min;
+
+ /** This represents the hour part, with the value is 0-23 */
+ int hour;
+
+ /** This represents the milisecond part, with the value is 0-999 */
+ int msec;
+
+} pj_parsed_time;
+
+
+/**
+ * @} // Time Management
+ */
+
+///////////////////////////////////////////////////////////////////////////////
+/*
+ * Terminal.
+ */
+/**
+ * Color code combination.
+ */
+enum {
+ PJ_TERM_COLOR_R = 2, /**< Red */
+ PJ_TERM_COLOR_G = 4, /**< Green */
+ PJ_TERM_COLOR_B = 1, /**< Blue. */
+ PJ_TERM_COLOR_BRIGHT = 8 /**< Bright mask. */
+};
+
+
+
+
+PJ_END_DECL
+
+
+#endif /* __PJ_TYPES_H__ */
+
diff --git a/pjlib/include/pjlib++.hpp b/pjlib/include/pjlib++.hpp
index fefd3d0..81db220 100644
--- a/pjlib/include/pjlib++.hpp
+++ b/pjlib/include/pjlib++.hpp
@@ -1,5 +1,26 @@
-/* $Id$
- *
+/* $Id$
+ *
+ */
+/*
+ * PJLIB - PJ Foundation Library
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ * Benny Prijono <bennylp@bulukucing.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#ifndef __PJLIBPP_H__
#define __PJLIBPP_H__
diff --git a/pjlib/include/pjlib.h b/pjlib/include/pjlib.h
index 7e4bf36..63af44b 100644
--- a/pjlib/include/pjlib.h
+++ b/pjlib/include/pjlib.h
@@ -1,39 +1,60 @@
-/* $Id$
- *
- */
-#ifndef __PJLIB_H__
-#define __PJLIB_H__
-
-/**
- * @file pjlib.h
- * @brief Include all PJLIB header files.
- */
-
-#include <pj/addr_resolv.h>
-#include <pj/array.h>
-#include <pj/assert.h>
-#include <pj/ctype.h>
-#include <pj/errno.h>
-#include <pj/except.h>
-#include <pj/fifobuf.h>
-#include <pj/file_access.h>
-#include <pj/file_io.h>
-#include <pj/guid.h>
-#include <pj/hash.h>
-#include <pj/ioqueue.h>
-#include <pj/list.h>
-#include <pj/lock.h>
-#include <pj/log.h>
-#include <pj/os.h>
-#include <pj/pool.h>
-#include <pj/rand.h>
-#include <pj/rbtree.h>
-#include <pj/sock.h>
-#include <pj/sock_select.h>
-#include <pj/string.h>
-#include <pj/timer.h>
-
-#include <pj/compat/high_precision.h>
-
-#endif /* __PJLIB_H__ */
-
+/* $Id$
+ *
+ */
+/*
+ * PJLIB - PJ Foundation Library
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ * Benny Prijono <bennylp@bulukucing.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#ifndef __PJLIB_H__
+#define __PJLIB_H__
+
+/**
+ * @file pjlib.h
+ * @brief Include all PJLIB header files.
+ */
+
+#include <pj/addr_resolv.h>
+#include <pj/array.h>
+#include <pj/assert.h>
+#include <pj/ctype.h>
+#include <pj/errno.h>
+#include <pj/except.h>
+#include <pj/fifobuf.h>
+#include <pj/file_access.h>
+#include <pj/file_io.h>
+#include <pj/guid.h>
+#include <pj/hash.h>
+#include <pj/ioqueue.h>
+#include <pj/list.h>
+#include <pj/lock.h>
+#include <pj/log.h>
+#include <pj/os.h>
+#include <pj/pool.h>
+#include <pj/rand.h>
+#include <pj/rbtree.h>
+#include <pj/sock.h>
+#include <pj/sock_select.h>
+#include <pj/string.h>
+#include <pj/timer.h>
+
+#include <pj/compat/high_precision.h>
+
+#endif /* __PJLIB_H__ */
+
diff --git a/pjlib/src/pj/addr_resolv_linux_kernel.c b/pjlib/src/pj/addr_resolv_linux_kernel.c
index 996c631..4d587ac 100644
--- a/pjlib/src/pj/addr_resolv_linux_kernel.c
+++ b/pjlib/src/pj/addr_resolv_linux_kernel.c
@@ -1,10 +1,31 @@
-/* $Id$
- *
- */
-#include <pj/addr_resolv.h>
-
-PJ_DEF(pj_status_t) pj_gethostbyname(const pj_str_t *hostname, pj_hostent *phe)
-{
- return -1;
-}
-
+/* $Id$
+ *
+ */
+/*
+ * PJLIB - PJ Foundation Library
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ * Benny Prijono <bennylp@bulukucing.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; 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 ee49db2..5e2676d 100644
--- a/pjlib/src/pj/addr_resolv_sock.c
+++ b/pjlib/src/pj/addr_resolv_sock.c
@@ -1,35 +1,56 @@
-/* $Id$
- */
-#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$
+ */
+/*
+ * PJLIB - PJ Foundation Library
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ * Benny Prijono <bennylp@bulukucing.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; 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 563b7bf..76fea95 100644
--- a/pjlib/src/pj/array.c
+++ b/pjlib/src/pj/array.c
@@ -1,54 +1,74 @@
-/* $Id$
- */
-#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$ */
+/*
+ * PJLIB - PJ Foundation Library
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ * Benny Prijono <bennylp@bulukucing.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; 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 d3fb0d7..ccdec59 100644
--- a/pjlib/src/pj/compat/sigjmp.c
+++ b/pjlib/src/pj/compat/sigjmp.c
@@ -1,24 +1,43 @@
-/* $Id$
- *
- */
-#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$ */
+/*
+ * PJLIB - PJ Foundation Library
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ * Benny Prijono <bennylp@bulukucing.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; 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 766857e..a1cab0b 100644
--- a/pjlib/src/pj/compat/string.c
+++ b/pjlib/src/pj/compat/string.c
@@ -1,29 +1,48 @@
-/* $Id$
- *
- */
-#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$ */
+/*
+ * PJLIB - PJ Foundation Library
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ * Benny Prijono <bennylp@bulukucing.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; 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/pj/config.c b/pjlib/src/pj/config.c
index 7923088..329fd98 100644
--- a/pjlib/src/pj/config.c
+++ b/pjlib/src/pj/config.c
@@ -1,32 +1,53 @@
-/* $Id$
- */
-#include <pj/config.h>
-#include <pj/log.h>
-#include <pj/ioqueue.h>
-
-static const char *id = "config.c";
-const char *PJ_VERSION = "0.3.0-pre4";
-
-PJ_DEF(void) pj_dump_config(void)
-{
- PJ_LOG(3, (id, "PJLIB (c)2005 Benny Prijono"));
- PJ_LOG(3, (id, "Dumping configurations:"));
- PJ_LOG(3, (id, " PJ_VERSION : %s", PJ_VERSION));
- PJ_LOG(3, (id, " PJ_DEBUG : %d", PJ_DEBUG));
- PJ_LOG(3, (id, " PJ_FUNCTIONS_ARE_INLINED : %d", PJ_FUNCTIONS_ARE_INLINED));
- PJ_LOG(3, (id, " PJ_POOL_DEBUG : %d", PJ_POOL_DEBUG));
- PJ_LOG(3, (id, " PJ_HAS_THREADS : %d", PJ_HAS_THREADS));
- PJ_LOG(3, (id, " PJ_LOG_MAX_LEVEL : %d", PJ_LOG_MAX_LEVEL));
- PJ_LOG(3, (id, " PJ_LOG_MAX_SIZE : %d", PJ_LOG_MAX_SIZE));
- PJ_LOG(3, (id, " PJ_LOG_USE_STACK_BUFFER : %d", PJ_LOG_USE_STACK_BUFFER));
- PJ_LOG(3, (id, " PJ_HAS_TCP : %d", PJ_HAS_TCP));
- PJ_LOG(3, (id, " PJ_MAX_HOSTNAME : %d", PJ_MAX_HOSTNAME));
- PJ_LOG(3, (id, " PJ_HAS_SEMAPHORE : %d", PJ_HAS_SEMAPHORE));
- PJ_LOG(3, (id, " PJ_HAS_EVENT_OBJ : %d", PJ_HAS_EVENT_OBJ));
- PJ_LOG(3, (id, " PJ_HAS_HIGH_RES_TIMER : %d", PJ_HAS_HIGH_RES_TIMER));
- PJ_LOG(3, (id, " PJ_(endianness) : %s",
- (PJ_IS_BIG_ENDIAN?"big-endian":"little-endian")));
- PJ_LOG(3, (id, " ioqueue type : %s", pj_ioqueue_name()));
- PJ_LOG(3, (id, " PJ_IOQUEUE_MAX_HANDLES : %d", PJ_IOQUEUE_MAX_HANDLES));
-}
-
+/* $Id$
+ */
+/*
+ * PJLIB - PJ Foundation Library
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ * Benny Prijono <bennylp@bulukucing.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; 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/log.h>
+#include <pj/ioqueue.h>
+
+static const char *id = "config.c";
+const char *PJ_VERSION = "0.3.0-pre4";
+
+PJ_DEF(void) pj_dump_config(void)
+{
+ PJ_LOG(3, (id, "PJLIB (c)2005 Benny Prijono"));
+ PJ_LOG(3, (id, "Dumping configurations:"));
+ PJ_LOG(3, (id, " PJ_VERSION : %s", PJ_VERSION));
+ PJ_LOG(3, (id, " PJ_DEBUG : %d", PJ_DEBUG));
+ PJ_LOG(3, (id, " PJ_FUNCTIONS_ARE_INLINED : %d", PJ_FUNCTIONS_ARE_INLINED));
+ PJ_LOG(3, (id, " PJ_POOL_DEBUG : %d", PJ_POOL_DEBUG));
+ PJ_LOG(3, (id, " PJ_HAS_THREADS : %d", PJ_HAS_THREADS));
+ PJ_LOG(3, (id, " PJ_LOG_MAX_LEVEL : %d", PJ_LOG_MAX_LEVEL));
+ PJ_LOG(3, (id, " PJ_LOG_MAX_SIZE : %d", PJ_LOG_MAX_SIZE));
+ PJ_LOG(3, (id, " PJ_LOG_USE_STACK_BUFFER : %d", PJ_LOG_USE_STACK_BUFFER));
+ PJ_LOG(3, (id, " PJ_HAS_TCP : %d", PJ_HAS_TCP));
+ PJ_LOG(3, (id, " PJ_MAX_HOSTNAME : %d", PJ_MAX_HOSTNAME));
+ PJ_LOG(3, (id, " PJ_HAS_SEMAPHORE : %d", PJ_HAS_SEMAPHORE));
+ PJ_LOG(3, (id, " PJ_HAS_EVENT_OBJ : %d", PJ_HAS_EVENT_OBJ));
+ PJ_LOG(3, (id, " PJ_HAS_HIGH_RES_TIMER : %d", PJ_HAS_HIGH_RES_TIMER));
+ PJ_LOG(3, (id, " PJ_(endianness) : %s",
+ (PJ_IS_BIG_ENDIAN?"big-endian":"little-endian")));
+ PJ_LOG(3, (id, " ioqueue type : %s", pj_ioqueue_name()));
+ PJ_LOG(3, (id, " PJ_IOQUEUE_MAX_HANDLES : %d", PJ_IOQUEUE_MAX_HANDLES));
+}
+
diff --git a/pjlib/src/pj/equeue_winnt.c b/pjlib/src/pj/equeue_winnt.c
index 8357dce..1565b9a 100644
--- a/pjlib/src/pj/equeue_winnt.c
+++ b/pjlib/src/pj/equeue_winnt.c
@@ -1,3 +1,24 @@
-/* $Id$
- */
-#include <pj/equeue.h>
+/* $Id$
+ */
+/*
+ * PJLIB - PJ Foundation Library
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ * Benny Prijono <bennylp@bulukucing.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#include <pj/equeue.h>
diff --git a/pjlib/src/pj/errno.c b/pjlib/src/pj/errno.c
index 5a98651..6d05576 100644
--- a/pjlib/src/pj/errno.c
+++ b/pjlib/src/pj/errno.c
@@ -1,99 +1,120 @@
-/* $Id$
- */
-#include <pj/errno.h>
-#include <pj/string.h>
-#include <pj/compat/sprintf.h>
-
-/* Prototype for platform specific error message, which will be defined
- * in separate file.
- */
-extern int platform_strerror( pj_os_err_type code,
- char *buf, pj_size_t bufsize );
-
-/* PJLIB's own error codes/messages */
-static const struct
-{
- int code;
- const char *msg;
-} err_str[] =
-{
- { PJ_EUNKNOWN, "Unknown Error" },
- { PJ_EPENDING, "Pending operation" },
- { PJ_ETOOMANYCONN, "Too many connecting sockets" },
- { PJ_EINVAL, "Invalid value or argument" },
- { PJ_ENAMETOOLONG, "Name too long" },
- { PJ_ENOTFOUND, "Not found" },
- { PJ_ENOMEM, "Not enough memory" },
- { PJ_EBUG, "BUG DETECTED!" },
- { PJ_ETIMEDOUT, "Operation timed out" },
- { PJ_ETOOMANY, "Too many objects of the specified type"},
- { PJ_EBUSY, "Object is busy"},
- { PJ_ENOTSUP, "Option/operation is not supported"},
+/* $Id$
+ */
+/*
+ * PJLIB - PJ Foundation Library
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ * Benny Prijono <bennylp@bulukucing.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#include <pj/errno.h>
+#include <pj/string.h>
+#include <pj/compat/sprintf.h>
+
+/* Prototype for platform specific error message, which will be defined
+ * in separate file.
+ */
+extern int platform_strerror( pj_os_err_type code,
+ char *buf, pj_size_t bufsize );
+
+/* PJLIB's own error codes/messages */
+static const struct
+{
+ int code;
+ const char *msg;
+} err_str[] =
+{
+ { PJ_EUNKNOWN, "Unknown Error" },
+ { PJ_EPENDING, "Pending operation" },
+ { PJ_ETOOMANYCONN, "Too many connecting sockets" },
+ { PJ_EINVAL, "Invalid value or argument" },
+ { PJ_ENAMETOOLONG, "Name too long" },
+ { PJ_ENOTFOUND, "Not found" },
+ { PJ_ENOMEM, "Not enough memory" },
+ { PJ_EBUG, "BUG DETECTED!" },
+ { PJ_ETIMEDOUT, "Operation timed out" },
+ { PJ_ETOOMANY, "Too many objects of the specified type"},
+ { PJ_EBUSY, "Object is busy"},
+ { PJ_ENOTSUP, "Option/operation is not supported"},
{ PJ_EINVALIDOP, "Invalid operation"},
{ PJ_ECANCELLED, "Operation cancelled"},
- { PJ_EEXISTS, "Object already exists" }
-};
-
-/*
- * pjlib_error()
- *
- * Retrieve message string for PJLIB's own error code.
- */
-static int pjlib_error(pj_status_t code, char *buf, pj_size_t size)
-{
- unsigned i;
-
- for (i=0; i<sizeof(err_str)/sizeof(err_str[0]); ++i) {
- if (err_str[i].code == code) {
- pj_size_t len = strlen(err_str[i].msg);
- if (len >= size) len = size-1;
- pj_memcpy(buf, err_str[i].msg, len);
- buf[len] = '\0';
- return len;
- }
- }
-
- *buf++ = '?';
- *buf++ = '?';
- *buf++ = '?';
- *buf++ = '\0';
- return 3;
-}
-
-/*
- * pj_strerror()
- */
-PJ_DEF(pj_str_t) pj_strerror( pj_status_t statcode,
- char *buf, pj_size_t bufsize )
-{
- int len = -1;
- pj_str_t errstr;
-
- if (statcode < PJ_ERRNO_START + PJ_ERRNO_SPACE_SIZE) {
- len = pj_snprintf( buf, bufsize, "Unknown error %d", statcode);
-
- } else if (statcode < PJ_ERRNO_START_STATUS + PJ_ERRNO_SPACE_SIZE) {
- len = pjlib_error(statcode, buf, bufsize);
-
- } else if (statcode < PJ_ERRNO_START_SYS + PJ_ERRNO_SPACE_SIZE) {
- len = platform_strerror(PJ_STATUS_TO_OS(statcode), buf, bufsize);
-
- } else if (statcode < PJ_ERRNO_START_USER + PJ_ERRNO_SPACE_SIZE) {
- len = pj_snprintf( buf, bufsize, "User error %d", statcode);
-
- } else {
- len = pj_snprintf( buf, bufsize, "Invalid error %d", statcode);
-
- }
-
- if (len < 1) {
- *buf = '\0';
- len = 0;
- }
-
- errstr.ptr = buf;
- errstr.slen = len;
-
- return errstr;
-}
-
+ { PJ_EEXISTS, "Object already exists" }
+};
+
+/*
+ * pjlib_error()
+ *
+ * Retrieve message string for PJLIB's own error code.
+ */
+static int pjlib_error(pj_status_t code, char *buf, pj_size_t size)
+{
+ unsigned i;
+
+ for (i=0; i<sizeof(err_str)/sizeof(err_str[0]); ++i) {
+ if (err_str[i].code == code) {
+ pj_size_t len = strlen(err_str[i].msg);
+ if (len >= size) len = size-1;
+ pj_memcpy(buf, err_str[i].msg, len);
+ buf[len] = '\0';
+ return len;
+ }
+ }
+
+ *buf++ = '?';
+ *buf++ = '?';
+ *buf++ = '?';
+ *buf++ = '\0';
+ return 3;
+}
+
+/*
+ * pj_strerror()
+ */
+PJ_DEF(pj_str_t) pj_strerror( pj_status_t statcode,
+ char *buf, pj_size_t bufsize )
+{
+ int len = -1;
+ pj_str_t errstr;
+
+ if (statcode < PJ_ERRNO_START + PJ_ERRNO_SPACE_SIZE) {
+ len = pj_snprintf( buf, bufsize, "Unknown error %d", statcode);
+
+ } else if (statcode < PJ_ERRNO_START_STATUS + PJ_ERRNO_SPACE_SIZE) {
+ len = pjlib_error(statcode, buf, bufsize);
+
+ } else if (statcode < PJ_ERRNO_START_SYS + PJ_ERRNO_SPACE_SIZE) {
+ len = platform_strerror(PJ_STATUS_TO_OS(statcode), buf, bufsize);
+
+ } else if (statcode < PJ_ERRNO_START_USER + PJ_ERRNO_SPACE_SIZE) {
+ len = pj_snprintf( buf, bufsize, "User error %d", statcode);
+
+ } else {
+ len = pj_snprintf( buf, bufsize, "Invalid error %d", statcode);
+
+ }
+
+ if (len < 1) {
+ *buf = '\0';
+ len = 0;
+ }
+
+ errstr.ptr = buf;
+ errstr.slen = len;
+
+ return errstr;
+}
+
diff --git a/pjlib/src/pj/except.c b/pjlib/src/pj/except.c
index 84fb93c..024a417 100644
--- a/pjlib/src/pj/except.c
+++ b/pjlib/src/pj/except.c
@@ -1,135 +1,156 @@
-/* $Id$
- */
-#include <pj/except.h>
-#include <pj/os.h>
-#include <pj/assert.h>
-#include <pj/log.h>
-#include <pj/errno.h>
-
-static long thread_local_id = -1;
-
-#if defined(PJ_HAS_EXCEPTION_NAMES) && PJ_HAS_EXCEPTION_NAMES != 0
- static const char *exception_id_names[PJ_MAX_EXCEPTION_ID];
-#else
- /*
- * Start from 1 (not 0)!!!
- * Exception 0 is reserved for normal path of setjmp()!!!
- */
- static int last_exception_id = 1;
-#endif /* PJ_HAS_EXCEPTION_NAMES */
-
-
-PJ_DEF(void) pj_throw_exception_(int exception_id)
-{
- struct pj_exception_state_t *handler;
-
- handler = pj_thread_local_get(thread_local_id);
- if (handler == NULL) {
- PJ_LOG(1,("except.c", "!!!FATAL: unhandled exception %d!\n", exception_id));
- pj_assert(handler != NULL);
- /* This will crash the system! */
- }
- pj_longjmp(handler->state, exception_id);
-}
-
-PJ_DEF(void) pj_push_exception_handler_(struct pj_exception_state_t *rec)
-{
- struct pj_exception_state_t *parent_handler = NULL;
-
- if (thread_local_id == -1) {
- pj_thread_local_alloc(&thread_local_id);
- pj_assert(thread_local_id != -1);
- }
- parent_handler = pj_thread_local_get(thread_local_id);
- rec->prev = parent_handler;
- pj_thread_local_set(thread_local_id, rec);
-}
-
-PJ_DEF(void) pj_pop_exception_handler_(void)
-{
- struct pj_exception_state_t *handler;
-
- handler = pj_thread_local_get(thread_local_id);
- pj_assert(handler != NULL);
- pj_thread_local_set(thread_local_id, handler->prev);
-}
-
-#if defined(PJ_HAS_EXCEPTION_NAMES) && PJ_HAS_EXCEPTION_NAMES != 0
-PJ_DEF(pj_status_t) pj_exception_id_alloc( const char *name,
- pj_exception_id_t *id)
-{
- unsigned i;
-
- pj_enter_critical_section();
-
- /*
- * Start from 1 (not 0)!!!
- * Exception 0 is reserved for normal path of setjmp()!!!
- */
- for (i=1; i<PJ_MAX_EXCEPTION_ID; ++i) {
- if (exception_id_names[i] == NULL) {
- exception_id_names[i] = name;
- *id = i;
- pj_leave_critical_section();
- return PJ_SUCCESS;
- }
- }
-
- pj_leave_critical_section();
- return PJ_ETOOMANY;
-}
-
-PJ_DEF(pj_status_t) pj_exception_id_free( pj_exception_id_t id )
-{
- /*
- * Start from 1 (not 0)!!!
- * Exception 0 is reserved for normal path of setjmp()!!!
- */
- PJ_ASSERT_RETURN(id>0 && id<PJ_MAX_EXCEPTION_ID, PJ_EINVAL);
-
- pj_enter_critical_section();
- exception_id_names[id] = NULL;
- pj_leave_critical_section();
-
- return PJ_SUCCESS;
-
-}
-
-PJ_DEF(const char*) pj_exception_id_name(pj_exception_id_t id)
-{
- /*
- * Start from 1 (not 0)!!!
- * Exception 0 is reserved for normal path of setjmp()!!!
- */
- PJ_ASSERT_RETURN(id>0 && id<PJ_MAX_EXCEPTION_ID, "<Invalid ID>");
-
- if (exception_id_names[id] == NULL)
- return "<Unallocated ID>";
-
- return exception_id_names[id];
-}
-
-#else /* PJ_HAS_EXCEPTION_NAMES */
-PJ_DEF(pj_status_t) pj_exception_id_alloc( const char *name,
- pj_exception_id_t *id)
-{
- PJ_ASSERT_RETURN(last_exception_id < PJ_MAX_EXCEPTION_ID-1, PJ_ETOOMANY);
-
- *id = last_exception_id++
- return PJ_SUCCESS;
-}
-
-PJ_DEF(pj_status_t) pj_exception_id_free( pj_exception_id_t id )
-{
- return PJ_SUCCESS;
-}
-
-PJ_DEF(const char*) pj_exception_id_name(pj_exception_id_t id)
-{
- return "";
-}
-
-#endif /* PJ_HAS_EXCEPTION_NAMES */
-
-
-
+/* $Id$
+ */
+/*
+ * PJLIB - PJ Foundation Library
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ * Benny Prijono <bennylp@bulukucing.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; 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/os.h>
+#include <pj/assert.h>
+#include <pj/log.h>
+#include <pj/errno.h>
+
+static long thread_local_id = -1;
+
+#if defined(PJ_HAS_EXCEPTION_NAMES) && PJ_HAS_EXCEPTION_NAMES != 0
+ static const char *exception_id_names[PJ_MAX_EXCEPTION_ID];
+#else
+ /*
+ * Start from 1 (not 0)!!!
+ * Exception 0 is reserved for normal path of setjmp()!!!
+ */
+ static int last_exception_id = 1;
+#endif /* PJ_HAS_EXCEPTION_NAMES */
+
+
+PJ_DEF(void) pj_throw_exception_(int exception_id)
+{
+ struct pj_exception_state_t *handler;
+
+ handler = pj_thread_local_get(thread_local_id);
+ if (handler == NULL) {
+ PJ_LOG(1,("except.c", "!!!FATAL: unhandled exception %d!\n", exception_id));
+ pj_assert(handler != NULL);
+ /* This will crash the system! */
+ }
+ pj_longjmp(handler->state, exception_id);
+}
+
+PJ_DEF(void) pj_push_exception_handler_(struct pj_exception_state_t *rec)
+{
+ struct pj_exception_state_t *parent_handler = NULL;
+
+ if (thread_local_id == -1) {
+ pj_thread_local_alloc(&thread_local_id);
+ pj_assert(thread_local_id != -1);
+ }
+ parent_handler = pj_thread_local_get(thread_local_id);
+ rec->prev = parent_handler;
+ pj_thread_local_set(thread_local_id, rec);
+}
+
+PJ_DEF(void) pj_pop_exception_handler_(void)
+{
+ struct pj_exception_state_t *handler;
+
+ handler = pj_thread_local_get(thread_local_id);
+ pj_assert(handler != NULL);
+ pj_thread_local_set(thread_local_id, handler->prev);
+}
+
+#if defined(PJ_HAS_EXCEPTION_NAMES) && PJ_HAS_EXCEPTION_NAMES != 0
+PJ_DEF(pj_status_t) pj_exception_id_alloc( const char *name,
+ pj_exception_id_t *id)
+{
+ unsigned i;
+
+ pj_enter_critical_section();
+
+ /*
+ * Start from 1 (not 0)!!!
+ * Exception 0 is reserved for normal path of setjmp()!!!
+ */
+ for (i=1; i<PJ_MAX_EXCEPTION_ID; ++i) {
+ if (exception_id_names[i] == NULL) {
+ exception_id_names[i] = name;
+ *id = i;
+ pj_leave_critical_section();
+ return PJ_SUCCESS;
+ }
+ }
+
+ pj_leave_critical_section();
+ return PJ_ETOOMANY;
+}
+
+PJ_DEF(pj_status_t) pj_exception_id_free( pj_exception_id_t id )
+{
+ /*
+ * Start from 1 (not 0)!!!
+ * Exception 0 is reserved for normal path of setjmp()!!!
+ */
+ PJ_ASSERT_RETURN(id>0 && id<PJ_MAX_EXCEPTION_ID, PJ_EINVAL);
+
+ pj_enter_critical_section();
+ exception_id_names[id] = NULL;
+ pj_leave_critical_section();
+
+ return PJ_SUCCESS;
+
+}
+
+PJ_DEF(const char*) pj_exception_id_name(pj_exception_id_t id)
+{
+ /*
+ * Start from 1 (not 0)!!!
+ * Exception 0 is reserved for normal path of setjmp()!!!
+ */
+ PJ_ASSERT_RETURN(id>0 && id<PJ_MAX_EXCEPTION_ID, "<Invalid ID>");
+
+ if (exception_id_names[id] == NULL)
+ return "<Unallocated ID>";
+
+ return exception_id_names[id];
+}
+
+#else /* PJ_HAS_EXCEPTION_NAMES */
+PJ_DEF(pj_status_t) pj_exception_id_alloc( const char *name,
+ pj_exception_id_t *id)
+{
+ PJ_ASSERT_RETURN(last_exception_id < PJ_MAX_EXCEPTION_ID-1, PJ_ETOOMANY);
+
+ *id = last_exception_id++
+ return PJ_SUCCESS;
+}
+
+PJ_DEF(pj_status_t) pj_exception_id_free( pj_exception_id_t id )
+{
+ return PJ_SUCCESS;
+}
+
+PJ_DEF(const char*) pj_exception_id_name(pj_exception_id_t id)
+{
+ return "";
+}
+
+#endif /* PJ_HAS_EXCEPTION_NAMES */
+
+
+
diff --git a/pjlib/src/pj/extra-exports.c b/pjlib/src/pj/extra-exports.c
index 29e1921..af7276c 100644
--- a/pjlib/src/pj/extra-exports.c
+++ b/pjlib/src/pj/extra-exports.c
@@ -1,32 +1,53 @@
-/* $Id$
- *
- */
-/*
- * This file contains code to export extra symbols from Linux kernel.
- * It should be copied to Linux kernel source tree and added to
- * Linux kernel combilation.
- *
- * This file is part of PJLIB project.
- */
-#include <linux/module.h>
-#include <linux/syscalls.h>
-
-EXPORT_SYMBOL(sys_select);
-
-EXPORT_SYMBOL(sys_epoll_create);
-EXPORT_SYMBOL(sys_epoll_ctl);
-EXPORT_SYMBOL(sys_epoll_wait);
-
-EXPORT_SYMBOL(sys_socket);
-EXPORT_SYMBOL(sys_bind);
-EXPORT_SYMBOL(sys_getpeername);
-EXPORT_SYMBOL(sys_getsockname);
-EXPORT_SYMBOL(sys_sendto);
-EXPORT_SYMBOL(sys_recvfrom);
-EXPORT_SYMBOL(sys_getsockopt);
-EXPORT_SYMBOL(sys_setsockopt);
-EXPORT_SYMBOL(sys_listen);
-EXPORT_SYMBOL(sys_shutdown);
-EXPORT_SYMBOL(sys_connect);
-EXPORT_SYMBOL(sys_accept);
-
+/* $Id$
+ *
+ */
+/*
+ * PJLIB - PJ Foundation Library
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ * Benny Prijono <bennylp@bulukucing.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+/*
+ * This file contains code to export extra symbols from Linux kernel.
+ * It should be copied to Linux kernel source tree and added to
+ * Linux kernel combilation.
+ *
+ * This file is part of PJLIB project.
+ */
+#include <linux/module.h>
+#include <linux/syscalls.h>
+
+EXPORT_SYMBOL(sys_select);
+
+EXPORT_SYMBOL(sys_epoll_create);
+EXPORT_SYMBOL(sys_epoll_ctl);
+EXPORT_SYMBOL(sys_epoll_wait);
+
+EXPORT_SYMBOL(sys_socket);
+EXPORT_SYMBOL(sys_bind);
+EXPORT_SYMBOL(sys_getpeername);
+EXPORT_SYMBOL(sys_getsockname);
+EXPORT_SYMBOL(sys_sendto);
+EXPORT_SYMBOL(sys_recvfrom);
+EXPORT_SYMBOL(sys_getsockopt);
+EXPORT_SYMBOL(sys_setsockopt);
+EXPORT_SYMBOL(sys_listen);
+EXPORT_SYMBOL(sys_shutdown);
+EXPORT_SYMBOL(sys_connect);
+EXPORT_SYMBOL(sys_accept);
+
diff --git a/pjlib/src/pj/fifobuf.c b/pjlib/src/pj/fifobuf.c
index 0368ef3..2902363 100644
--- a/pjlib/src/pj/fifobuf.c
+++ b/pjlib/src/pj/fifobuf.c
@@ -1,177 +1,198 @@
-/* $Id$
- */
-#include <pj/fifobuf.h>
-#include <pj/log.h>
-#include <pj/assert.h>
-#include <pj/os.h>
-
-#define THIS_FILE "fifobuf"
-
-#define SZ sizeof(unsigned)
-
-PJ_DEF(void)
-pj_fifobuf_init (pj_fifobuf_t *fifobuf, void *buffer, unsigned size)
-{
- PJ_CHECK_STACK();
-
- PJ_LOG(6, (THIS_FILE,
- "fifobuf_init fifobuf=%p buffer=%p, size=%d",
- fifobuf, buffer, size));
-
- fifobuf->first = buffer;
- fifobuf->last = fifobuf->first + size;
- fifobuf->ubegin = fifobuf->uend = fifobuf->first;
- fifobuf->full = 0;
-}
-
-PJ_DEF(unsigned)
-pj_fifobuf_max_size (pj_fifobuf_t *fifobuf)
-{
- unsigned s1, s2;
-
- PJ_CHECK_STACK();
-
- if (fifobuf->uend >= fifobuf->ubegin) {
- s1 = fifobuf->last - fifobuf->uend;
- s2 = fifobuf->ubegin - fifobuf->first;
- } else {
- s1 = s2 = fifobuf->ubegin - fifobuf->uend;
- }
-
- return s1<s2 ? s2 : s1;
-}
-
-PJ_DEF(void*)
-pj_fifobuf_alloc (pj_fifobuf_t *fifobuf, unsigned size)
-{
- unsigned available;
- char *start;
-
- PJ_CHECK_STACK();
-
- if (fifobuf->full) {
- PJ_LOG(6, (THIS_FILE,
- "fifobuf_alloc fifobuf=%p, size=%d: full!",
- fifobuf, size));
- return NULL;
- }
-
- /* try to allocate from the end part of the fifo */
- if (fifobuf->uend >= fifobuf->ubegin) {
- available = fifobuf->last - fifobuf->uend;
- if (available >= size+SZ) {
- char *ptr = fifobuf->uend;
- fifobuf->uend += (size+SZ);
- if (fifobuf->uend == fifobuf->last)
- fifobuf->uend = fifobuf->first;
- if (fifobuf->uend == fifobuf->ubegin)
- fifobuf->full = 1;
- *(unsigned*)ptr = size+SZ;
- ptr += SZ;
-
- PJ_LOG(6, (THIS_FILE,
- "fifobuf_alloc fifobuf=%p, size=%d: returning %p, p1=%p, p2=%p",
- fifobuf, size, ptr, fifobuf->ubegin, fifobuf->uend));
- return ptr;
- }
- }
-
- /* try to allocate from the start part of the fifo */
- start = (fifobuf->uend <= fifobuf->ubegin) ? fifobuf->uend : fifobuf->first;
- available = fifobuf->ubegin - start;
- if (available >= size+SZ) {
- char *ptr = start;
- fifobuf->uend = start + size + SZ;
- if (fifobuf->uend == fifobuf->ubegin)
- fifobuf->full = 1;
- *(unsigned*)ptr = size+SZ;
- ptr += SZ;
-
- PJ_LOG(6, (THIS_FILE,
- "fifobuf_alloc fifobuf=%p, size=%d: returning %p, p1=%p, p2=%p",
- fifobuf, size, ptr, fifobuf->ubegin, fifobuf->uend));
- return ptr;
- }
-
- PJ_LOG(6, (THIS_FILE,
- "fifobuf_alloc fifobuf=%p, size=%d: no space left! p1=%p, p2=%p",
- fifobuf, size, fifobuf->ubegin, fifobuf->uend));
- return NULL;
-}
-
-PJ_DEF(pj_status_t)
-pj_fifobuf_unalloc (pj_fifobuf_t *fifobuf, void *buf)
-{
- char *ptr = buf;
- char *endptr;
- unsigned sz;
-
- PJ_CHECK_STACK();
-
- ptr -= SZ;
- sz = *(unsigned*)ptr;
-
- endptr = fifobuf->uend;
- if (endptr == fifobuf->first)
- endptr = fifobuf->last;
-
- if (ptr+sz != endptr) {
- pj_assert(!"Invalid pointer to undo alloc");
- return -1;
- }
-
- fifobuf->uend = ptr;
- fifobuf->full = 0;
-
- PJ_LOG(6, (THIS_FILE,
- "fifobuf_unalloc fifobuf=%p, ptr=%p, size=%d, p1=%p, p2=%p",
- fifobuf, buf, sz, fifobuf->ubegin, fifobuf->uend));
-
- return 0;
-}
-
-PJ_DEF(pj_status_t)
-pj_fifobuf_free (pj_fifobuf_t *fifobuf, void *buf)
-{
- char *ptr = buf;
- char *end;
- unsigned sz;
-
- PJ_CHECK_STACK();
-
- ptr -= SZ;
- if (ptr < fifobuf->first || ptr >= fifobuf->last) {
- pj_assert(!"Invalid pointer to free");
- return -1;
- }
-
- if (ptr != fifobuf->ubegin && ptr != fifobuf->first) {
- pj_assert(!"Invalid free() sequence!");
- return -1;
- }
-
- end = (fifobuf->uend > fifobuf->ubegin) ? fifobuf->uend : fifobuf->last;
- sz = *(unsigned*)ptr;
- if (ptr+sz > end) {
- pj_assert(!"Invalid size!");
- return -1;
- }
-
- fifobuf->ubegin = ptr + sz;
-
- /* Rollover */
- if (fifobuf->ubegin == fifobuf->last)
- fifobuf->ubegin = fifobuf->first;
-
- /* Reset if fifobuf is empty */
- if (fifobuf->ubegin == fifobuf->uend)
- fifobuf->ubegin = fifobuf->uend = fifobuf->first;
-
- fifobuf->full = 0;
-
- PJ_LOG(6, (THIS_FILE,
- "fifobuf_free fifobuf=%p, ptr=%p, size=%d, p1=%p, p2=%p",
- fifobuf, buf, sz, fifobuf->ubegin, fifobuf->uend));
-
- return 0;
-}
+/* $Id$
+ */
+/*
+ * PJLIB - PJ Foundation Library
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ * Benny Prijono <bennylp@bulukucing.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#include <pj/fifobuf.h>
+#include <pj/log.h>
+#include <pj/assert.h>
+#include <pj/os.h>
+
+#define THIS_FILE "fifobuf"
+
+#define SZ sizeof(unsigned)
+
+PJ_DEF(void)
+pj_fifobuf_init (pj_fifobuf_t *fifobuf, void *buffer, unsigned size)
+{
+ PJ_CHECK_STACK();
+
+ PJ_LOG(6, (THIS_FILE,
+ "fifobuf_init fifobuf=%p buffer=%p, size=%d",
+ fifobuf, buffer, size));
+
+ fifobuf->first = buffer;
+ fifobuf->last = fifobuf->first + size;
+ fifobuf->ubegin = fifobuf->uend = fifobuf->first;
+ fifobuf->full = 0;
+}
+
+PJ_DEF(unsigned)
+pj_fifobuf_max_size (pj_fifobuf_t *fifobuf)
+{
+ unsigned s1, s2;
+
+ PJ_CHECK_STACK();
+
+ if (fifobuf->uend >= fifobuf->ubegin) {
+ s1 = fifobuf->last - fifobuf->uend;
+ s2 = fifobuf->ubegin - fifobuf->first;
+ } else {
+ s1 = s2 = fifobuf->ubegin - fifobuf->uend;
+ }
+
+ return s1<s2 ? s2 : s1;
+}
+
+PJ_DEF(void*)
+pj_fifobuf_alloc (pj_fifobuf_t *fifobuf, unsigned size)
+{
+ unsigned available;
+ char *start;
+
+ PJ_CHECK_STACK();
+
+ if (fifobuf->full) {
+ PJ_LOG(6, (THIS_FILE,
+ "fifobuf_alloc fifobuf=%p, size=%d: full!",
+ fifobuf, size));
+ return NULL;
+ }
+
+ /* try to allocate from the end part of the fifo */
+ if (fifobuf->uend >= fifobuf->ubegin) {
+ available = fifobuf->last - fifobuf->uend;
+ if (available >= size+SZ) {
+ char *ptr = fifobuf->uend;
+ fifobuf->uend += (size+SZ);
+ if (fifobuf->uend == fifobuf->last)
+ fifobuf->uend = fifobuf->first;
+ if (fifobuf->uend == fifobuf->ubegin)
+ fifobuf->full = 1;
+ *(unsigned*)ptr = size+SZ;
+ ptr += SZ;
+
+ PJ_LOG(6, (THIS_FILE,
+ "fifobuf_alloc fifobuf=%p, size=%d: returning %p, p1=%p, p2=%p",
+ fifobuf, size, ptr, fifobuf->ubegin, fifobuf->uend));
+ return ptr;
+ }
+ }
+
+ /* try to allocate from the start part of the fifo */
+ start = (fifobuf->uend <= fifobuf->ubegin) ? fifobuf->uend : fifobuf->first;
+ available = fifobuf->ubegin - start;
+ if (available >= size+SZ) {
+ char *ptr = start;
+ fifobuf->uend = start + size + SZ;
+ if (fifobuf->uend == fifobuf->ubegin)
+ fifobuf->full = 1;
+ *(unsigned*)ptr = size+SZ;
+ ptr += SZ;
+
+ PJ_LOG(6, (THIS_FILE,
+ "fifobuf_alloc fifobuf=%p, size=%d: returning %p, p1=%p, p2=%p",
+ fifobuf, size, ptr, fifobuf->ubegin, fifobuf->uend));
+ return ptr;
+ }
+
+ PJ_LOG(6, (THIS_FILE,
+ "fifobuf_alloc fifobuf=%p, size=%d: no space left! p1=%p, p2=%p",
+ fifobuf, size, fifobuf->ubegin, fifobuf->uend));
+ return NULL;
+}
+
+PJ_DEF(pj_status_t)
+pj_fifobuf_unalloc (pj_fifobuf_t *fifobuf, void *buf)
+{
+ char *ptr = buf;
+ char *endptr;
+ unsigned sz;
+
+ PJ_CHECK_STACK();
+
+ ptr -= SZ;
+ sz = *(unsigned*)ptr;
+
+ endptr = fifobuf->uend;
+ if (endptr == fifobuf->first)
+ endptr = fifobuf->last;
+
+ if (ptr+sz != endptr) {
+ pj_assert(!"Invalid pointer to undo alloc");
+ return -1;
+ }
+
+ fifobuf->uend = ptr;
+ fifobuf->full = 0;
+
+ PJ_LOG(6, (THIS_FILE,
+ "fifobuf_unalloc fifobuf=%p, ptr=%p, size=%d, p1=%p, p2=%p",
+ fifobuf, buf, sz, fifobuf->ubegin, fifobuf->uend));
+
+ return 0;
+}
+
+PJ_DEF(pj_status_t)
+pj_fifobuf_free (pj_fifobuf_t *fifobuf, void *buf)
+{
+ char *ptr = buf;
+ char *end;
+ unsigned sz;
+
+ PJ_CHECK_STACK();
+
+ ptr -= SZ;
+ if (ptr < fifobuf->first || ptr >= fifobuf->last) {
+ pj_assert(!"Invalid pointer to free");
+ return -1;
+ }
+
+ if (ptr != fifobuf->ubegin && ptr != fifobuf->first) {
+ pj_assert(!"Invalid free() sequence!");
+ return -1;
+ }
+
+ end = (fifobuf->uend > fifobuf->ubegin) ? fifobuf->uend : fifobuf->last;
+ sz = *(unsigned*)ptr;
+ if (ptr+sz > end) {
+ pj_assert(!"Invalid size!");
+ return -1;
+ }
+
+ fifobuf->ubegin = ptr + sz;
+
+ /* Rollover */
+ if (fifobuf->ubegin == fifobuf->last)
+ fifobuf->ubegin = fifobuf->first;
+
+ /* Reset if fifobuf is empty */
+ if (fifobuf->ubegin == fifobuf->uend)
+ fifobuf->ubegin = fifobuf->uend = fifobuf->first;
+
+ fifobuf->full = 0;
+
+ PJ_LOG(6, (THIS_FILE,
+ "fifobuf_free fifobuf=%p, ptr=%p, size=%d, p1=%p, p2=%p",
+ fifobuf, buf, sz, fifobuf->ubegin, fifobuf->uend));
+
+ return 0;
+}
diff --git a/pjlib/src/pj/file_access_unistd.c b/pjlib/src/pj/file_access_unistd.c
index 8e46e0e..b4e0ab4 100644
--- a/pjlib/src/pj/file_access_unistd.c
+++ b/pjlib/src/pj/file_access_unistd.c
@@ -1,96 +1,117 @@
-/* $Id$ */
-#include <pj/file_access.h>
-#include <pj/assert.h>
-#include <pj/errno.h>
-
-#include <sys/types.h>
-#include <sys/stat.h>
-#include <unistd.h>
-#include <stdio.h> /* rename() */
-#include <errno.h>
-
-/*
- * pj_file_exists()
- */
-PJ_DEF(pj_bool_t) pj_file_exists(const char *filename)
-{
- struct stat buf;
-
- PJ_ASSERT_RETURN(filename, 0);
-
- if (stat(filename, &buf) != 0)
- return 0;
-
- return PJ_TRUE;
-}
-
-
-/*
- * pj_file_size()
- */
-PJ_DEF(pj_off_t) pj_file_size(const char *filename)
-{
- struct stat buf;
-
- PJ_ASSERT_RETURN(filename, -1);
-
- if (stat(filename, &buf) != 0)
- return -1;
-
- return buf.st_size;
-}
-
-
-/*
- * pj_file_delete()
- */
-PJ_DEF(pj_status_t) pj_file_delete(const char *filename)
-{
- PJ_ASSERT_RETURN(filename, PJ_EINVAL);
-
- if (unlink(filename)!=0) {
- return PJ_RETURN_OS_ERROR(errno);
- }
- return PJ_SUCCESS;
-}
-
-
-/*
- * pj_file_move()
- */
-PJ_DEF(pj_status_t) pj_file_move( const char *oldname, const char *newname)
-{
- PJ_ASSERT_RETURN(oldname && newname, PJ_EINVAL);
-
- if (rename(oldname, newname) != 0) {
- return PJ_RETURN_OS_ERROR(errno);
- }
- return PJ_SUCCESS;
-}
-
-
-/*
- * pj_file_getstat()
- */
-PJ_DEF(pj_status_t) pj_file_getstat(const char *filename,
- pj_file_stat *statbuf)
-{
- struct stat buf;
-
- PJ_ASSERT_RETURN(filename && statbuf, PJ_EINVAL);
-
- if (stat(filename, &buf) != 0) {
- return PJ_RETURN_OS_ERROR(errno);
- }
-
- statbuf->size = buf.st_size;
- statbuf->ctime.sec = buf.st_ctime;
- statbuf->ctime.msec = 0;
- statbuf->mtime.sec = buf.st_mtime;
- statbuf->mtime.msec = 0;
- statbuf->atime.sec = buf.st_atime;
- statbuf->atime.msec = 0;
-
- return PJ_SUCCESS;
-}
-
+/* $Id$ */
+/*
+ * PJLIB - PJ Foundation Library
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ * Benny Prijono <bennylp@bulukucing.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#include <pj/file_access.h>
+#include <pj/assert.h>
+#include <pj/errno.h>
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <stdio.h> /* rename() */
+#include <errno.h>
+
+/*
+ * pj_file_exists()
+ */
+PJ_DEF(pj_bool_t) pj_file_exists(const char *filename)
+{
+ struct stat buf;
+
+ PJ_ASSERT_RETURN(filename, 0);
+
+ if (stat(filename, &buf) != 0)
+ return 0;
+
+ return PJ_TRUE;
+}
+
+
+/*
+ * pj_file_size()
+ */
+PJ_DEF(pj_off_t) pj_file_size(const char *filename)
+{
+ struct stat buf;
+
+ PJ_ASSERT_RETURN(filename, -1);
+
+ if (stat(filename, &buf) != 0)
+ return -1;
+
+ return buf.st_size;
+}
+
+
+/*
+ * pj_file_delete()
+ */
+PJ_DEF(pj_status_t) pj_file_delete(const char *filename)
+{
+ PJ_ASSERT_RETURN(filename, PJ_EINVAL);
+
+ if (unlink(filename)!=0) {
+ return PJ_RETURN_OS_ERROR(errno);
+ }
+ return PJ_SUCCESS;
+}
+
+
+/*
+ * pj_file_move()
+ */
+PJ_DEF(pj_status_t) pj_file_move( const char *oldname, const char *newname)
+{
+ PJ_ASSERT_RETURN(oldname && newname, PJ_EINVAL);
+
+ if (rename(oldname, newname) != 0) {
+ return PJ_RETURN_OS_ERROR(errno);
+ }
+ return PJ_SUCCESS;
+}
+
+
+/*
+ * pj_file_getstat()
+ */
+PJ_DEF(pj_status_t) pj_file_getstat(const char *filename,
+ pj_file_stat *statbuf)
+{
+ struct stat buf;
+
+ PJ_ASSERT_RETURN(filename && statbuf, PJ_EINVAL);
+
+ if (stat(filename, &buf) != 0) {
+ return PJ_RETURN_OS_ERROR(errno);
+ }
+
+ statbuf->size = buf.st_size;
+ statbuf->ctime.sec = buf.st_ctime;
+ statbuf->ctime.msec = 0;
+ statbuf->mtime.sec = buf.st_mtime;
+ statbuf->mtime.msec = 0;
+ statbuf->atime.sec = buf.st_atime;
+ statbuf->atime.msec = 0;
+
+ return PJ_SUCCESS;
+}
+
diff --git a/pjlib/src/pj/file_access_win32.c b/pjlib/src/pj/file_access_win32.c
index 3640565..7aeceb8 100644
--- a/pjlib/src/pj/file_access_win32.c
+++ b/pjlib/src/pj/file_access_win32.c
@@ -1,4 +1,25 @@
/* $Id$ */
+/*
+ * PJLIB - PJ Foundation Library
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ * Benny Prijono <bennylp@bulukucing.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
#include <pj/file_access.h>
#include <pj/assert.h>
#include <pj/errno.h>
diff --git a/pjlib/src/pj/file_io_ansi.c b/pjlib/src/pj/file_io_ansi.c
index 0946edd..3acb91e 100644
--- a/pjlib/src/pj/file_io_ansi.c
+++ b/pjlib/src/pj/file_io_ansi.c
@@ -1,140 +1,161 @@
-/* $Id$ */
-#include <pj/file_io.h>
-#include <pj/assert.h>
-#include <pj/errno.h>
-#include <stdio.h>
-#include <errno.h>
-
-PJ_DEF(pj_status_t) pj_file_open( pj_pool_t *pool,
- const char *pathname,
- unsigned flags,
- pj_oshandle_t *fd)
-{
- char mode[8];
- char *p = mode;
-
- PJ_ASSERT_RETURN(pathname && fd, PJ_EINVAL);
- PJ_UNUSED_ARG(pool);
-
- if ((flags & PJ_O_APPEND) == PJ_O_APPEND) {
- if ((flags & PJ_O_WRONLY) == PJ_O_WRONLY) {
- *p++ = 'a';
- if ((flags & PJ_O_RDONLY) == PJ_O_RDONLY)
- *p++ = '+';
- } else {
- /* This is invalid.
- * Can not specify PJ_O_RDONLY with PJ_O_APPEND!
- */
- }
- } else {
- if ((flags & PJ_O_RDONLY) == PJ_O_RDONLY) {
- *p++ = 'r';
- if ((flags & PJ_O_WRONLY) == PJ_O_WRONLY)
- *p++ = '+';
- } else {
- *p++ = 'w';
- }
- }
-
- if (p==mode)
- return PJ_EINVAL;
-
- *p++ = 'b';
- *p++ = '\0';
-
- *fd = fopen(pathname, mode);
- if (*fd == NULL)
- return PJ_RETURN_OS_ERROR(errno);
-
- return PJ_SUCCESS;
-}
-
-PJ_DEF(pj_status_t) pj_file_close(pj_oshandle_t fd)
-{
- PJ_ASSERT_RETURN(fd, PJ_EINVAL);
- if (fclose((FILE*)fd) != 0)
- return PJ_RETURN_OS_ERROR(errno);
-
- return PJ_SUCCESS;
-}
-
-PJ_DEF(pj_status_t) pj_file_write( pj_oshandle_t fd,
- const void *data,
- pj_ssize_t *size)
-{
- size_t written;
-
- clearerr((FILE*)fd);
- written = fwrite(data, 1, *size, (FILE*)fd);
+/* $Id$ */
+/*
+ * PJLIB - PJ Foundation Library
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ * Benny Prijono <bennylp@bulukucing.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#include <pj/file_io.h>
+#include <pj/assert.h>
+#include <pj/errno.h>
+#include <stdio.h>
+#include <errno.h>
+
+PJ_DEF(pj_status_t) pj_file_open( pj_pool_t *pool,
+ const char *pathname,
+ unsigned flags,
+ pj_oshandle_t *fd)
+{
+ char mode[8];
+ char *p = mode;
+
+ PJ_ASSERT_RETURN(pathname && fd, PJ_EINVAL);
+ PJ_UNUSED_ARG(pool);
+
+ if ((flags & PJ_O_APPEND) == PJ_O_APPEND) {
+ if ((flags & PJ_O_WRONLY) == PJ_O_WRONLY) {
+ *p++ = 'a';
+ if ((flags & PJ_O_RDONLY) == PJ_O_RDONLY)
+ *p++ = '+';
+ } else {
+ /* This is invalid.
+ * Can not specify PJ_O_RDONLY with PJ_O_APPEND!
+ */
+ }
+ } else {
+ if ((flags & PJ_O_RDONLY) == PJ_O_RDONLY) {
+ *p++ = 'r';
+ if ((flags & PJ_O_WRONLY) == PJ_O_WRONLY)
+ *p++ = '+';
+ } else {
+ *p++ = 'w';
+ }
+ }
+
+ if (p==mode)
+ return PJ_EINVAL;
+
+ *p++ = 'b';
+ *p++ = '\0';
+
+ *fd = fopen(pathname, mode);
+ if (*fd == NULL)
+ return PJ_RETURN_OS_ERROR(errno);
+
+ return PJ_SUCCESS;
+}
+
+PJ_DEF(pj_status_t) pj_file_close(pj_oshandle_t fd)
+{
+ PJ_ASSERT_RETURN(fd, PJ_EINVAL);
+ if (fclose((FILE*)fd) != 0)
+ return PJ_RETURN_OS_ERROR(errno);
+
+ return PJ_SUCCESS;
+}
+
+PJ_DEF(pj_status_t) pj_file_write( pj_oshandle_t fd,
+ const void *data,
+ pj_ssize_t *size)
+{
+ size_t written;
+
+ clearerr((FILE*)fd);
+ written = fwrite(data, 1, *size, (FILE*)fd);
if (ferror((FILE*)fd)) {
- *size = -1;
- return PJ_RETURN_OS_ERROR(errno);
- }
-
- *size = written;
- return PJ_SUCCESS;
-}
-
-PJ_DEF(pj_status_t) pj_file_read( pj_oshandle_t fd,
- void *data,
- pj_ssize_t *size)
-{
- size_t bytes;
-
- clearerr((FILE*)fd);
- bytes = fread(data, 1, *size, (FILE*)fd);
+ *size = -1;
+ return PJ_RETURN_OS_ERROR(errno);
+ }
+
+ *size = written;
+ return PJ_SUCCESS;
+}
+
+PJ_DEF(pj_status_t) pj_file_read( pj_oshandle_t fd,
+ void *data,
+ pj_ssize_t *size)
+{
+ size_t bytes;
+
+ clearerr((FILE*)fd);
+ bytes = fread(data, 1, *size, (FILE*)fd);
if (ferror((FILE*)fd)) {
- *size = -1;
- return PJ_RETURN_OS_ERROR(errno);
- }
-
- *size = bytes;
- return PJ_SUCCESS;
-}
+ *size = -1;
+ return PJ_RETURN_OS_ERROR(errno);
+ }
+
+ *size = bytes;
+ return PJ_SUCCESS;
+}
PJ_DEF(pj_bool_t) pj_file_eof(pj_oshandle_t fd, enum pj_file_access access)
{
PJ_UNUSED_ARG(access);
return feof((FILE*)fd) ? PJ_TRUE : 0;
}
-
-PJ_DEF(pj_status_t) pj_file_setpos( pj_oshandle_t fd,
- pj_off_t offset,
- enum pj_file_seek_type whence)
-{
- int mode;
-
- switch (whence) {
- case PJ_SEEK_SET:
- mode = SEEK_SET; break;
- case PJ_SEEK_CUR:
- mode = SEEK_CUR; break;
- case PJ_SEEK_END:
- mode = SEEK_END; break;
- default:
- pj_assert(!"Invalid whence in file_setpos");
- return PJ_EINVAL;
- }
-
- if (fseek((FILE*)fd, (long)offset, mode) != 0)
- return PJ_RETURN_OS_ERROR(errno);
-
- return PJ_SUCCESS;
-}
-
-PJ_DEF(pj_status_t) pj_file_getpos( pj_oshandle_t fd,
- pj_off_t *pos)
-{
- long offset;
-
- offset = ftell((FILE*)fd);
- if (offset == -1) {
- *pos = -1;
- return PJ_RETURN_OS_ERROR(errno);
- }
-
- *pos = offset;
- return PJ_SUCCESS;
-}
-
-
+
+PJ_DEF(pj_status_t) pj_file_setpos( pj_oshandle_t fd,
+ pj_off_t offset,
+ enum pj_file_seek_type whence)
+{
+ int mode;
+
+ switch (whence) {
+ case PJ_SEEK_SET:
+ mode = SEEK_SET; break;
+ case PJ_SEEK_CUR:
+ mode = SEEK_CUR; break;
+ case PJ_SEEK_END:
+ mode = SEEK_END; break;
+ default:
+ pj_assert(!"Invalid whence in file_setpos");
+ return PJ_EINVAL;
+ }
+
+ if (fseek((FILE*)fd, (long)offset, mode) != 0)
+ return PJ_RETURN_OS_ERROR(errno);
+
+ return PJ_SUCCESS;
+}
+
+PJ_DEF(pj_status_t) pj_file_getpos( pj_oshandle_t fd,
+ pj_off_t *pos)
+{
+ long offset;
+
+ offset = ftell((FILE*)fd);
+ if (offset == -1) {
+ *pos = -1;
+ return PJ_RETURN_OS_ERROR(errno);
+ }
+
+ *pos = offset;
+ return PJ_SUCCESS;
+}
+
+
diff --git a/pjlib/src/pj/file_io_win32.c b/pjlib/src/pj/file_io_win32.c
index d990841..d90f5f4 100644
--- a/pjlib/src/pj/file_io_win32.c
+++ b/pjlib/src/pj/file_io_win32.c
@@ -1,13 +1,34 @@
-/* $Id$ */
-#include <pj/file_io.h>
-#include <pj/errno.h>
-#include <pj/assert.h>
-
-#include <windows.h>
-
-#ifndef INVALID_SET_FILE_POINTER
-# define INVALID_SET_FILE_POINTER ((DWORD)-1)
-#endif
+/* $Id$ */
+/*
+ * PJLIB - PJ Foundation Library
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ * Benny Prijono <bennylp@bulukucing.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#include <pj/file_io.h>
+#include <pj/errno.h>
+#include <pj/assert.h>
+
+#include <windows.h>
+
+#ifndef INVALID_SET_FILE_POINTER
+# define INVALID_SET_FILE_POINTER ((DWORD)-1)
+#endif
/**
* Check for end-of-file condition on the specified descriptor.
@@ -19,97 +40,97 @@
*/
PJ_DECL(pj_bool_t) pj_file_eof(pj_oshandle_t fd,
enum pj_file_access access);
-
-
-PJ_DEF(pj_status_t) pj_file_open( pj_pool_t *pool,
- const char *pathname,
- unsigned flags,
- pj_oshandle_t *fd)
-{
- HANDLE hFile;
- DWORD dwDesiredAccess = 0;
- DWORD dwShareMode = 0;
- DWORD dwCreationDisposition = 0;
- DWORD dwFlagsAndAttributes = 0;
-
- PJ_UNUSED_ARG(pool);
-
- PJ_ASSERT_RETURN(pathname!=NULL, PJ_EINVAL);
-
- if ((flags & PJ_O_WRONLY) == PJ_O_WRONLY) {
- dwDesiredAccess |= GENERIC_WRITE;
- if ((flags & PJ_O_APPEND) == PJ_O_APPEND) {
- dwDesiredAccess |= FILE_APPEND_DATA;
- } else {
+
+
+PJ_DEF(pj_status_t) pj_file_open( pj_pool_t *pool,
+ const char *pathname,
+ unsigned flags,
+ pj_oshandle_t *fd)
+{
+ HANDLE hFile;
+ DWORD dwDesiredAccess = 0;
+ DWORD dwShareMode = 0;
+ DWORD dwCreationDisposition = 0;
+ DWORD dwFlagsAndAttributes = 0;
+
+ PJ_UNUSED_ARG(pool);
+
+ PJ_ASSERT_RETURN(pathname!=NULL, PJ_EINVAL);
+
+ if ((flags & PJ_O_WRONLY) == PJ_O_WRONLY) {
+ dwDesiredAccess |= GENERIC_WRITE;
+ if ((flags & PJ_O_APPEND) == PJ_O_APPEND) {
+ dwDesiredAccess |= FILE_APPEND_DATA;
+ } else {
dwDesiredAccess &= ~(FILE_APPEND_DATA);
dwCreationDisposition |= CREATE_ALWAYS;
- }
- }
+ }
+ }
if ((flags & PJ_O_RDONLY) == PJ_O_RDONLY) {
dwDesiredAccess |= GENERIC_READ;
if (flags == PJ_O_RDONLY)
dwCreationDisposition |= OPEN_EXISTING;
}
-
- if (dwDesiredAccess == 0) {
- pj_assert(!"Invalid file open flags");
- return PJ_EINVAL;
- }
-
- dwShareMode = FILE_SHARE_READ | FILE_SHARE_WRITE;
- dwFlagsAndAttributes = FILE_ATTRIBUTE_NORMAL;
- hFile = CreateFile(pathname, dwDesiredAccess, dwShareMode, NULL,
- dwCreationDisposition, dwFlagsAndAttributes, NULL);
- if (hFile == INVALID_HANDLE_VALUE) {
- *fd = 0;
- return PJ_RETURN_OS_ERROR(GetLastError());
- }
-
- *fd = hFile;
- return PJ_SUCCESS;
-}
-
-PJ_DEF(pj_status_t) pj_file_close(pj_oshandle_t fd)
-{
- if (CloseHandle(fd)==0)
- return PJ_RETURN_OS_ERROR(GetLastError());
- return PJ_SUCCESS;
-}
-
-PJ_DEF(pj_status_t) pj_file_write( pj_oshandle_t fd,
- const void *data,
- pj_ssize_t *size)
-{
- BOOL rc;
- DWORD bytesWritten;
-
- rc = WriteFile(fd, data, *size, &bytesWritten, NULL);
- if (!rc) {
- *size = -1;
- return PJ_RETURN_OS_ERROR(GetLastError());
- }
-
- *size = bytesWritten;
- return PJ_SUCCESS;
-}
-
-PJ_DEF(pj_status_t) pj_file_read( pj_oshandle_t fd,
- void *data,
- pj_ssize_t *size)
-{
- BOOL rc;
- DWORD bytesRead;
-
- rc = ReadFile(fd, data, *size, &bytesRead, NULL);
- if (!rc) {
- *size = -1;
- return PJ_RETURN_OS_ERROR(GetLastError());
- }
-
- *size = bytesRead;
- return PJ_SUCCESS;
-}
+ if (dwDesiredAccess == 0) {
+ pj_assert(!"Invalid file open flags");
+ return PJ_EINVAL;
+ }
+
+ dwShareMode = FILE_SHARE_READ | FILE_SHARE_WRITE;
+ dwFlagsAndAttributes = FILE_ATTRIBUTE_NORMAL;
+
+ hFile = CreateFile(pathname, dwDesiredAccess, dwShareMode, NULL,
+ dwCreationDisposition, dwFlagsAndAttributes, NULL);
+ if (hFile == INVALID_HANDLE_VALUE) {
+ *fd = 0;
+ return PJ_RETURN_OS_ERROR(GetLastError());
+ }
+
+ *fd = hFile;
+ return PJ_SUCCESS;
+}
+
+PJ_DEF(pj_status_t) pj_file_close(pj_oshandle_t fd)
+{
+ if (CloseHandle(fd)==0)
+ return PJ_RETURN_OS_ERROR(GetLastError());
+ return PJ_SUCCESS;
+}
+
+PJ_DEF(pj_status_t) pj_file_write( pj_oshandle_t fd,
+ const void *data,
+ pj_ssize_t *size)
+{
+ BOOL rc;
+ DWORD bytesWritten;
+
+ rc = WriteFile(fd, data, *size, &bytesWritten, NULL);
+ if (!rc) {
+ *size = -1;
+ return PJ_RETURN_OS_ERROR(GetLastError());
+ }
+
+ *size = bytesWritten;
+ return PJ_SUCCESS;
+}
+
+PJ_DEF(pj_status_t) pj_file_read( pj_oshandle_t fd,
+ void *data,
+ pj_ssize_t *size)
+{
+ BOOL rc;
+ DWORD bytesRead;
+
+ rc = ReadFile(fd, data, *size, &bytesRead, NULL);
+ if (!rc) {
+ *size = -1;
+ return PJ_RETURN_OS_ERROR(GetLastError());
+ }
+
+ *size = bytesRead;
+ return PJ_SUCCESS;
+}
/*
PJ_DEF(pj_bool_t) pj_file_eof(pj_oshandle_t fd, enum pj_file_access access)
@@ -134,53 +155,53 @@
return 0;
}
*/
-
-PJ_DEF(pj_status_t) pj_file_setpos( pj_oshandle_t fd,
- pj_off_t offset,
- enum pj_file_seek_type whence)
-{
- DWORD dwMoveMethod;
- DWORD dwNewPos;
- LONG hi32;
-
- if (whence == PJ_SEEK_SET)
- dwMoveMethod = FILE_BEGIN;
- else if (whence == PJ_SEEK_CUR)
- dwMoveMethod = FILE_CURRENT;
- else if (whence == PJ_SEEK_END)
- dwMoveMethod = FILE_END;
- else {
- pj_assert(!"Invalid whence in file_setpos");
- return PJ_EINVAL;
- }
-
- hi32 = (LONG)(offset >> 32);
- dwNewPos = SetFilePointer(fd, (long)offset, &hi32, dwMoveMethod);
- if (dwNewPos == (DWORD)INVALID_SET_FILE_POINTER) {
- DWORD dwStatus = GetLastError();
- if (dwStatus != 0)
- return PJ_RETURN_OS_ERROR(dwStatus);
- /* dwNewPos actually is not an error. */
- }
-
- return PJ_SUCCESS;
-}
-
-PJ_DEF(pj_status_t) pj_file_getpos( pj_oshandle_t fd,
- pj_off_t *pos)
-{
- LONG hi32 = 0;
- DWORD lo32;
-
- lo32 = SetFilePointer(fd, 0, &hi32, FILE_CURRENT);
- if (lo32 == (DWORD)INVALID_SET_FILE_POINTER) {
- DWORD dwStatus = GetLastError();
- if (dwStatus != 0)
- return PJ_RETURN_OS_ERROR(dwStatus);
- }
-
- *pos = hi32;
- *pos = (*pos << 32) + lo32;
- return PJ_SUCCESS;
-}
-
+
+PJ_DEF(pj_status_t) pj_file_setpos( pj_oshandle_t fd,
+ pj_off_t offset,
+ enum pj_file_seek_type whence)
+{
+ DWORD dwMoveMethod;
+ DWORD dwNewPos;
+ LONG hi32;
+
+ if (whence == PJ_SEEK_SET)
+ dwMoveMethod = FILE_BEGIN;
+ else if (whence == PJ_SEEK_CUR)
+ dwMoveMethod = FILE_CURRENT;
+ else if (whence == PJ_SEEK_END)
+ dwMoveMethod = FILE_END;
+ else {
+ pj_assert(!"Invalid whence in file_setpos");
+ return PJ_EINVAL;
+ }
+
+ hi32 = (LONG)(offset >> 32);
+ dwNewPos = SetFilePointer(fd, (long)offset, &hi32, dwMoveMethod);
+ if (dwNewPos == (DWORD)INVALID_SET_FILE_POINTER) {
+ DWORD dwStatus = GetLastError();
+ if (dwStatus != 0)
+ return PJ_RETURN_OS_ERROR(dwStatus);
+ /* dwNewPos actually is not an error. */
+ }
+
+ return PJ_SUCCESS;
+}
+
+PJ_DEF(pj_status_t) pj_file_getpos( pj_oshandle_t fd,
+ pj_off_t *pos)
+{
+ LONG hi32 = 0;
+ DWORD lo32;
+
+ lo32 = SetFilePointer(fd, 0, &hi32, FILE_CURRENT);
+ if (lo32 == (DWORD)INVALID_SET_FILE_POINTER) {
+ DWORD dwStatus = GetLastError();
+ if (dwStatus != 0)
+ return PJ_RETURN_OS_ERROR(dwStatus);
+ }
+
+ *pos = hi32;
+ *pos = (*pos << 32) + lo32;
+ return PJ_SUCCESS;
+}
+
diff --git a/pjlib/src/pj/guid.c b/pjlib/src/pj/guid.c
index a99b3a8..735ea88 100644
--- a/pjlib/src/pj/guid.c
+++ b/pjlib/src/pj/guid.c
@@ -1,10 +1,31 @@
-/* $Id$
- */
-#include <pj/guid.h>
-#include <pj/pool.h>
-
-PJ_DEF(void) pj_create_unique_string(pj_pool_t *pool, pj_str_t *str)
-{
- str->ptr = pj_pool_alloc(pool, PJ_GUID_STRING_LENGTH);
- pj_generate_unique_string(str);
-}
+/* $Id$
+ */
+/*
+ * PJLIB - PJ Foundation Library
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ * Benny Prijono <bennylp@bulukucing.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#include <pj/guid.h>
+#include <pj/pool.h>
+
+PJ_DEF(void) pj_create_unique_string(pj_pool_t *pool, pj_str_t *str)
+{
+ str->ptr = pj_pool_alloc(pool, PJ_GUID_STRING_LENGTH);
+ pj_generate_unique_string(str);
+}
diff --git a/pjlib/src/pj/guid_simple.c b/pjlib/src/pj/guid_simple.c
index 6e41672..4fb0a67 100644
--- a/pjlib/src/pj/guid_simple.c
+++ b/pjlib/src/pj/guid_simple.c
@@ -1,52 +1,73 @@
-/* $Id$
- *
- */
-#include <pj/guid.h>
-#include <pj/os.h>
-#include <pj/rand.h>
-#include <pj/string.h>
-#include <pj/compat/sprintf.h>
-
-const unsigned PJ_GUID_STRING_LENGTH=20;
-
-static void init_mac_address(unsigned char mac_addr[16])
-{
- unsigned long *ulval1 = (unsigned long*) &mac_addr[0];
- unsigned short *usval1 = (unsigned short*) &mac_addr[4];
-
- *ulval1 = pj_rand();
- *usval1 = (unsigned short) pj_rand();
-}
-
-PJ_DEF(pj_str_t*) pj_generate_unique_string(pj_str_t *str)
-{
- static int guid_initialized;
- static unsigned pid;
- static char str_pid[5];
- static unsigned char mac_addr[6];
- static char str_mac_addr[16];
- static unsigned clock_seq;
-
- PJ_CHECK_STACK();
-
- if (guid_initialized == 0) {
- pid = pj_getpid();
- init_mac_address(mac_addr);
- clock_seq = 0;
-
- sprintf(str_pid, "%04x", pid);
- sprintf(str_mac_addr, "%02x%02x%02x%02x%02x%02x",
- mac_addr[0], mac_addr[1], mac_addr[2],
- mac_addr[3], mac_addr[4], mac_addr[5]);
-
- guid_initialized = 1;
- }
-
- strcpy(str->ptr, str_pid);
- sprintf(str->ptr+4, "%04x", clock_seq++);
- pj_memcpy(str->ptr+8, str_mac_addr, 12);
- str->slen = 20;
-
- return str;
-}
-
+/* $Id$
+ *
+ */
+/*
+ * PJLIB - PJ Foundation Library
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ * Benny Prijono <bennylp@bulukucing.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#include <pj/guid.h>
+#include <pj/os.h>
+#include <pj/rand.h>
+#include <pj/string.h>
+#include <pj/compat/sprintf.h>
+
+const unsigned PJ_GUID_STRING_LENGTH=20;
+
+static void init_mac_address(unsigned char mac_addr[16])
+{
+ unsigned long *ulval1 = (unsigned long*) &mac_addr[0];
+ unsigned short *usval1 = (unsigned short*) &mac_addr[4];
+
+ *ulval1 = pj_rand();
+ *usval1 = (unsigned short) pj_rand();
+}
+
+PJ_DEF(pj_str_t*) pj_generate_unique_string(pj_str_t *str)
+{
+ static int guid_initialized;
+ static unsigned pid;
+ static char str_pid[5];
+ static unsigned char mac_addr[6];
+ static char str_mac_addr[16];
+ static unsigned clock_seq;
+
+ PJ_CHECK_STACK();
+
+ if (guid_initialized == 0) {
+ pid = pj_getpid();
+ init_mac_address(mac_addr);
+ clock_seq = 0;
+
+ sprintf(str_pid, "%04x", pid);
+ sprintf(str_mac_addr, "%02x%02x%02x%02x%02x%02x",
+ mac_addr[0], mac_addr[1], mac_addr[2],
+ mac_addr[3], mac_addr[4], mac_addr[5]);
+
+ guid_initialized = 1;
+ }
+
+ strcpy(str->ptr, str_pid);
+ sprintf(str->ptr+4, "%04x", clock_seq++);
+ pj_memcpy(str->ptr+8, str_mac_addr, 12);
+ str->slen = 20;
+
+ return str;
+}
+
diff --git a/pjlib/src/pj/guid_win32.c b/pjlib/src/pj/guid_win32.c
index 0832b8c..b34b6e1 100644
--- a/pjlib/src/pj/guid_win32.c
+++ b/pjlib/src/pj/guid_win32.c
@@ -1,52 +1,73 @@
-/* $Id$
- */
-#include <pj/guid.h>
-#include <pj/string.h>
-#include <pj/sock.h>
-#include <windows.h>
-#include <objbase.h>
-#include <pj/os.h>
-
-
-const unsigned PJ_GUID_STRING_LENGTH=32;
-
-PJ_INLINE(void) hex2digit(unsigned value, char *p)
-{
- static char hex[] = {'0', '1', '2', '3', '4', '5', '6', '7',
- '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' };
- *p++ = hex[ (value & 0xF0) >> 4 ];
- *p++ = hex[ (value & 0x0F) ];
-}
-
-static void guid_to_str( const GUID *guid, pj_str_t *str )
-{
- unsigned i;
- GUID guid_copy;
- const unsigned char *src = (const unsigned char*)&guid_copy;
- char *dst = str->ptr;
-
- pj_memcpy(&guid_copy, guid, sizeof(*guid));
- guid_copy.Data1 = pj_ntohl(guid_copy.Data1);
- guid_copy.Data2 = pj_ntohs(guid_copy.Data2);
- guid_copy.Data3 = pj_ntohs(guid_copy.Data3);
-
- for (i=0; i<16; ++i) {
- hex2digit( *src, dst );
- dst += 2;
- ++src;
- }
- str->slen = 32;
-}
-
-
-PJ_DEF(pj_str_t*) pj_generate_unique_string(pj_str_t *str)
-{
- GUID guid;
-
- PJ_CHECK_STACK();
-
- CoCreateGuid(&guid);
- guid_to_str( &guid, str );
- return str;
-}
-
+/* $Id$
+ */
+/*
+ * PJLIB - PJ Foundation Library
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ * Benny Prijono <bennylp@bulukucing.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#include <pj/guid.h>
+#include <pj/string.h>
+#include <pj/sock.h>
+#include <windows.h>
+#include <objbase.h>
+#include <pj/os.h>
+
+
+const unsigned PJ_GUID_STRING_LENGTH=32;
+
+PJ_INLINE(void) hex2digit(unsigned value, char *p)
+{
+ static char hex[] = {'0', '1', '2', '3', '4', '5', '6', '7',
+ '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' };
+ *p++ = hex[ (value & 0xF0) >> 4 ];
+ *p++ = hex[ (value & 0x0F) ];
+}
+
+static void guid_to_str( const GUID *guid, pj_str_t *str )
+{
+ unsigned i;
+ GUID guid_copy;
+ const unsigned char *src = (const unsigned char*)&guid_copy;
+ char *dst = str->ptr;
+
+ pj_memcpy(&guid_copy, guid, sizeof(*guid));
+ guid_copy.Data1 = pj_ntohl(guid_copy.Data1);
+ guid_copy.Data2 = pj_ntohs(guid_copy.Data2);
+ guid_copy.Data3 = pj_ntohs(guid_copy.Data3);
+
+ for (i=0; i<16; ++i) {
+ hex2digit( *src, dst );
+ dst += 2;
+ ++src;
+ }
+ str->slen = 32;
+}
+
+
+PJ_DEF(pj_str_t*) pj_generate_unique_string(pj_str_t *str)
+{
+ GUID guid;
+
+ PJ_CHECK_STACK();
+
+ CoCreateGuid(&guid);
+ guid_to_str( &guid, str );
+ return str;
+}
+
diff --git a/pjlib/src/pj/hash.c b/pjlib/src/pj/hash.c
index 195c881..499032c 100644
--- a/pjlib/src/pj/hash.c
+++ b/pjlib/src/pj/hash.c
@@ -1,55 +1,77 @@
-/* $Id$
- */
-#include <pj/hash.h>
-#include <pj/log.h>
-#include <pj/string.h>
-#include <pj/pool.h>
-#include <pj/os.h>
-
-/**
- * The hash multiplier used to calculate hash value.
- */
-#define PJ_HASH_MULTIPLIER 33
-
-
-struct pj_hash_entry
-{
- struct pj_hash_entry *next;
- const void *key;
- pj_uint32_t hash;
- pj_uint32_t keylen;
- void *value;
-};
-
-
-struct pj_hash_table_t
-{
- pj_hash_entry **table;
- unsigned count, rows;
- pj_hash_iterator_t iterator;
-};
-
-
-
-PJ_DEF(pj_uint32_t) pj_hash_calc(pj_uint32_t hash, const void *key, unsigned keylen)
-{
- PJ_CHECK_STACK();
-
- if (keylen==PJ_HASH_KEY_STRING) {
- const unsigned char *p = key;
- for ( ; *p; ++p ) {
- hash = hash * PJ_HASH_MULTIPLIER + *p;
- }
- keylen = p - (const unsigned char*)key;
- } else {
- const unsigned char *p = key,
- *end = p + keylen;
- for ( ; p!=end; ++p) {
- hash = hash * PJ_HASH_MULTIPLIER + *p;
- }
- }
- return hash;
-}
+/* $Id$
+ */
+/*
+ * PJLIB - PJ Foundation Library
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ * Benny Prijono <bennylp@bulukucing.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#include <pj/hash.h>
+#include <pj/log.h>
+#include <pj/string.h>
+#include <pj/pool.h>
+#include <pj/os.h>
+#include <pj/ctype.h>
+
+/**
+ * The hash multiplier used to calculate hash value.
+ */
+#define PJ_HASH_MULTIPLIER 33
+
+
+struct pj_hash_entry
+{
+ struct pj_hash_entry *next;
+ const void *key;
+ pj_uint32_t hash;
+ pj_uint32_t keylen;
+ void *value;
+};
+
+
+struct pj_hash_table_t
+{
+ pj_hash_entry **table;
+ unsigned count, rows;
+ pj_hash_iterator_t iterator;
+};
+
+
+
+PJ_DEF(pj_uint32_t) pj_hash_calc(pj_uint32_t hash, const void *key, unsigned keylen)
+{
+ PJ_CHECK_STACK();
+
+ if (keylen==PJ_HASH_KEY_STRING) {
+ const unsigned char *p = key;
+ for ( ; *p; ++p ) {
+ hash = hash * PJ_HASH_MULTIPLIER + *p;
+ }
+ keylen = p - (const unsigned char*)key;
+ } else {
+ const unsigned char *p = key,
+ *end = p + keylen;
+ for ( ; p!=end; ++p) {
+ hash = hash * PJ_HASH_MULTIPLIER + *p;
+ }
+ }
+ return hash;
+}
PJ_DEF(pj_uint32_t) pj_hash_calc_tolower( pj_uint32_t hval,
char *result,
@@ -64,194 +86,194 @@
return hval;
}
-
-
-PJ_DEF(pj_hash_table_t*) pj_hash_create(pj_pool_t *pool, unsigned size)
-{
- pj_hash_table_t *h;
- unsigned table_size;
-
- h = pj_pool_alloc(pool, sizeof(pj_hash_table_t));
- h->count = 0;
-
- PJ_LOG( 5, ("hashtbl", "hash table %p created from pool %s", h, pj_pool_getobjname(pool)));
-
- /* size must be 2^n - 1.
- round-up the size to this rule, except when size is 2^n, then size
- will be round-down to 2^n-1.
- */
- table_size = 8;
- do {
- table_size <<= 1;
- } while (table_size <= size);
- table_size -= 1;
-
- h->rows = table_size;
- h->table = pj_pool_calloc(pool, table_size+1, sizeof(pj_hash_entry*));
- return h;
-}
-
-static pj_hash_entry **find_entry( pj_pool_t *pool, pj_hash_table_t *ht,
- const void *key, unsigned keylen,
- void *val)
-{
- pj_uint32_t hash;
- pj_hash_entry **p_entry, *entry;
-
- hash=0;
- if (keylen==PJ_HASH_KEY_STRING) {
- const unsigned char *p = key;
- for ( ; *p; ++p ) {
- hash = hash * PJ_HASH_MULTIPLIER + *p;
- }
- keylen = p - (const unsigned char*)key;
- } else {
- const unsigned char *p = key,
- *end = p + keylen;
- for ( ; p!=end; ++p) {
- hash = hash * PJ_HASH_MULTIPLIER + *p;
- }
- }
-
- /* scan the linked list */
- for (p_entry = &ht->table[hash & ht->rows], entry=*p_entry;
- entry;
- p_entry = &entry->next, entry = *p_entry)
- {
- if (entry->hash==hash && entry->keylen==keylen &&
- memcmp(entry->key, key, keylen)==0)
- {
- break;
- }
- }
-
- if (entry || val==NULL)
- return p_entry;
-
- /* create a new entry */
- entry = pj_pool_alloc(pool, sizeof(pj_hash_entry));
- PJ_LOG(5, ("hashtbl", "%p: New p_entry %p created, pool used=%u, cap=%u", ht, entry,
- pj_pool_get_used_size(pool), pj_pool_get_capacity(pool)));
- entry->next = NULL;
- entry->hash = hash;
- entry->key = key;
- entry->keylen = keylen;
- entry->value = val;
- *p_entry = entry;
-
- ++ht->count;
-
- return p_entry;
-}
-
-PJ_DEF(void *) pj_hash_get( pj_hash_table_t *ht,
- const void *key, unsigned keylen )
-{
- pj_hash_entry *entry;
- entry = *find_entry( NULL, ht, key, keylen, NULL);
- return entry ? entry->value : NULL;
-}
-
-PJ_DEF(void) pj_hash_set( pj_pool_t *pool, pj_hash_table_t *ht,
- const void *key, unsigned keylen,
- void *value )
-{
- pj_hash_entry **p_entry;
-
- p_entry = find_entry( pool, ht, key, keylen, value );
- if (*p_entry) {
- if (value == NULL) {
- /* delete entry */
- PJ_LOG(5, ("hashtbl", "%p: p_entry %p deleted", ht, *p_entry));
- *p_entry = (*p_entry)->next;
- --ht->count;
-
- } else {
- /* overwrite */
- (*p_entry)->value = value;
- PJ_LOG(5, ("hashtbl", "%p: p_entry %p value set to %p", ht, *p_entry, value));
- }
- }
-}
-
-PJ_DEF(unsigned) pj_hash_count( pj_hash_table_t *ht )
-{
- return ht->count;
-}
-
-PJ_DEF(pj_hash_iterator_t*) pj_hash_first( pj_hash_table_t *ht,
- pj_hash_iterator_t *it )
-{
- it->index = 0;
- it->entry = NULL;
-
- for (; it->index < ht->rows; ++it->index) {
- it->entry = ht->table[it->index];
- if (it->entry) {
- break;
- }
- }
-
- return it->entry ? it : NULL;
-}
-
-PJ_DEF(pj_hash_iterator_t*) pj_hash_next( pj_hash_table_t *ht,
- pj_hash_iterator_t *it )
-{
- it->entry = it->entry->next;
- if (it->entry) {
- return it;
- }
-
- for (++it->index; it->index < ht->rows; ++it->index) {
- it->entry = ht->table[it->index];
- if (it->entry) {
- break;
- }
- }
-
- return it->entry ? it : NULL;
-}
-
-PJ_DEF(void*) pj_hash_this( pj_hash_table_t *ht, pj_hash_iterator_t *it )
-{
- PJ_CHECK_STACK();
- PJ_UNUSED_ARG(ht);
- return it->entry->value;
-}
-
-#if 0
-void pj_hash_dump_collision( pj_hash_table_t *ht )
-{
- unsigned min=0xFFFFFFFF, max=0;
- unsigned i;
- char line[120];
- int len, totlen = 0;
-
- for (i=0; i<ht->rows; ++i) {
- unsigned count = 0;
- pj_hash_entry *entry = ht->table[i];
- while (entry) {
- ++count;
- entry = entry->next;
- }
- if (count < min)
- min = count;
- if (count > max)
- max = count;
- len = pj_snprintf( line+totlen, sizeof(line)-totlen, "%3d:%3d ", i, count);
- if (len < 1)
- break;
- totlen += len;
-
- if ((i+1) % 10 == 0) {
- line[totlen] = '\0';
- PJ_LOG(4,(__FILE__, line));
- }
- }
-
- PJ_LOG(4,(__FILE__,"Count: %d, min: %d, max: %d\n", ht->count, min, max));
-}
-#endif
-
-
+
+
+PJ_DEF(pj_hash_table_t*) pj_hash_create(pj_pool_t *pool, unsigned size)
+{
+ pj_hash_table_t *h;
+ unsigned table_size;
+
+ h = pj_pool_alloc(pool, sizeof(pj_hash_table_t));
+ h->count = 0;
+
+ PJ_LOG( 5, ("hashtbl", "hash table %p created from pool %s", h, pj_pool_getobjname(pool)));
+
+ /* size must be 2^n - 1.
+ round-up the size to this rule, except when size is 2^n, then size
+ will be round-down to 2^n-1.
+ */
+ table_size = 8;
+ do {
+ table_size <<= 1;
+ } while (table_size <= size);
+ table_size -= 1;
+
+ h->rows = table_size;
+ h->table = pj_pool_calloc(pool, table_size+1, sizeof(pj_hash_entry*));
+ return h;
+}
+
+static pj_hash_entry **find_entry( pj_pool_t *pool, pj_hash_table_t *ht,
+ const void *key, unsigned keylen,
+ void *val)
+{
+ pj_uint32_t hash;
+ pj_hash_entry **p_entry, *entry;
+
+ hash=0;
+ if (keylen==PJ_HASH_KEY_STRING) {
+ const unsigned char *p = key;
+ for ( ; *p; ++p ) {
+ hash = hash * PJ_HASH_MULTIPLIER + *p;
+ }
+ keylen = p - (const unsigned char*)key;
+ } else {
+ const unsigned char *p = key,
+ *end = p + keylen;
+ for ( ; p!=end; ++p) {
+ hash = hash * PJ_HASH_MULTIPLIER + *p;
+ }
+ }
+
+ /* scan the linked list */
+ for (p_entry = &ht->table[hash & ht->rows], entry=*p_entry;
+ entry;
+ p_entry = &entry->next, entry = *p_entry)
+ {
+ if (entry->hash==hash && entry->keylen==keylen &&
+ memcmp(entry->key, key, keylen)==0)
+ {
+ break;
+ }
+ }
+
+ if (entry || val==NULL)
+ return p_entry;
+
+ /* create a new entry */
+ entry = pj_pool_alloc(pool, sizeof(pj_hash_entry));
+ PJ_LOG(5, ("hashtbl", "%p: New p_entry %p created, pool used=%u, cap=%u", ht, entry,
+ pj_pool_get_used_size(pool), pj_pool_get_capacity(pool)));
+ entry->next = NULL;
+ entry->hash = hash;
+ entry->key = key;
+ entry->keylen = keylen;
+ entry->value = val;
+ *p_entry = entry;
+
+ ++ht->count;
+
+ return p_entry;
+}
+
+PJ_DEF(void *) pj_hash_get( pj_hash_table_t *ht,
+ const void *key, unsigned keylen )
+{
+ pj_hash_entry *entry;
+ entry = *find_entry( NULL, ht, key, keylen, NULL);
+ return entry ? entry->value : NULL;
+}
+
+PJ_DEF(void) pj_hash_set( pj_pool_t *pool, pj_hash_table_t *ht,
+ const void *key, unsigned keylen,
+ void *value )
+{
+ pj_hash_entry **p_entry;
+
+ p_entry = find_entry( pool, ht, key, keylen, value );
+ if (*p_entry) {
+ if (value == NULL) {
+ /* delete entry */
+ PJ_LOG(5, ("hashtbl", "%p: p_entry %p deleted", ht, *p_entry));
+ *p_entry = (*p_entry)->next;
+ --ht->count;
+
+ } else {
+ /* overwrite */
+ (*p_entry)->value = value;
+ PJ_LOG(5, ("hashtbl", "%p: p_entry %p value set to %p", ht, *p_entry, value));
+ }
+ }
+}
+
+PJ_DEF(unsigned) pj_hash_count( pj_hash_table_t *ht )
+{
+ return ht->count;
+}
+
+PJ_DEF(pj_hash_iterator_t*) pj_hash_first( pj_hash_table_t *ht,
+ pj_hash_iterator_t *it )
+{
+ it->index = 0;
+ it->entry = NULL;
+
+ for (; it->index < ht->rows; ++it->index) {
+ it->entry = ht->table[it->index];
+ if (it->entry) {
+ break;
+ }
+ }
+
+ return it->entry ? it : NULL;
+}
+
+PJ_DEF(pj_hash_iterator_t*) pj_hash_next( pj_hash_table_t *ht,
+ pj_hash_iterator_t *it )
+{
+ it->entry = it->entry->next;
+ if (it->entry) {
+ return it;
+ }
+
+ for (++it->index; it->index < ht->rows; ++it->index) {
+ it->entry = ht->table[it->index];
+ if (it->entry) {
+ break;
+ }
+ }
+
+ return it->entry ? it : NULL;
+}
+
+PJ_DEF(void*) pj_hash_this( pj_hash_table_t *ht, pj_hash_iterator_t *it )
+{
+ PJ_CHECK_STACK();
+ PJ_UNUSED_ARG(ht);
+ return it->entry->value;
+}
+
+#if 0
+void pj_hash_dump_collision( pj_hash_table_t *ht )
+{
+ unsigned min=0xFFFFFFFF, max=0;
+ unsigned i;
+ char line[120];
+ int len, totlen = 0;
+
+ for (i=0; i<ht->rows; ++i) {
+ unsigned count = 0;
+ pj_hash_entry *entry = ht->table[i];
+ while (entry) {
+ ++count;
+ entry = entry->next;
+ }
+ if (count < min)
+ min = count;
+ if (count > max)
+ max = count;
+ len = pj_snprintf( line+totlen, sizeof(line)-totlen, "%3d:%3d ", i, count);
+ if (len < 1)
+ break;
+ totlen += len;
+
+ if ((i+1) % 10 == 0) {
+ line[totlen] = '\0';
+ PJ_LOG(4,(__FILE__, line));
+ }
+ }
+
+ PJ_LOG(4,(__FILE__,"Count: %d, min: %d, max: %d\n", ht->count, min, max));
+}
+#endif
+
+
diff --git a/pjlib/src/pj/ioqueue_common_abs.c b/pjlib/src/pj/ioqueue_common_abs.c
index c2e1121..ed71772 100644
--- a/pjlib/src/pj/ioqueue_common_abs.c
+++ b/pjlib/src/pj/ioqueue_common_abs.c
@@ -1,837 +1,858 @@
-/* $Id$ */
-
-/*
- * ioqueue_common_abs.c
- *
- * This contains common functionalities to emulate proactor pattern with
- * various event dispatching mechanisms (e.g. select, epoll).
- *
- * This file will be included by the appropriate ioqueue implementation.
- * This file is NOT supposed to be compiled as stand-alone source.
- */
-
-static void ioqueue_init( pj_ioqueue_t *ioqueue )
-{
- ioqueue->lock = NULL;
- ioqueue->auto_delete_lock = 0;
-}
-
-static pj_status_t ioqueue_destroy(pj_ioqueue_t *ioqueue)
-{
- if (ioqueue->auto_delete_lock && ioqueue->lock ) {
- pj_lock_release(ioqueue->lock);
- return pj_lock_destroy(ioqueue->lock);
- } else
- return PJ_SUCCESS;
-}
-
-/*
- * pj_ioqueue_set_lock()
- */
-PJ_DEF(pj_status_t) pj_ioqueue_set_lock( pj_ioqueue_t *ioqueue,
- pj_lock_t *lock,
- pj_bool_t auto_delete )
-{
- PJ_ASSERT_RETURN(ioqueue && lock, PJ_EINVAL);
-
- if (ioqueue->auto_delete_lock && ioqueue->lock) {
- pj_lock_destroy(ioqueue->lock);
- }
-
- ioqueue->lock = lock;
- ioqueue->auto_delete_lock = auto_delete;
-
- return PJ_SUCCESS;
-}
-
-static pj_status_t ioqueue_init_key( pj_pool_t *pool,
- pj_ioqueue_t *ioqueue,
- pj_ioqueue_key_t *key,
- pj_sock_t sock,
- void *user_data,
- const pj_ioqueue_callback *cb)
-{
- pj_status_t rc;
- int optlen;
-
- key->ioqueue = ioqueue;
- key->fd = sock;
- key->user_data = user_data;
- pj_list_init(&key->read_list);
- pj_list_init(&key->write_list);
-#if PJ_HAS_TCP
- pj_list_init(&key->accept_list);
-#endif
-
- /* Save callback. */
- pj_memcpy(&key->cb, cb, sizeof(pj_ioqueue_callback));
-
- /* Get socket type. When socket type is datagram, some optimization
- * will be performed during send to allow parallel send operations.
- */
- optlen = sizeof(key->fd_type);
- rc = pj_sock_getsockopt(sock, PJ_SOL_SOCKET, PJ_SO_TYPE,
- &key->fd_type, &optlen);
- if (rc != PJ_SUCCESS)
- key->fd_type = PJ_SOCK_STREAM;
-
- /* Create mutex for the key. */
- rc = pj_mutex_create_simple(pool, NULL, &key->mutex);
-
- return rc;
-}
-
-static void ioqueue_destroy_key( pj_ioqueue_key_t *key )
-{
- pj_mutex_destroy(key->mutex);
-}
-
-/*
- * pj_ioqueue_get_user_data()
- *
- * Obtain value associated with a key.
- */
-PJ_DEF(void*) pj_ioqueue_get_user_data( pj_ioqueue_key_t *key )
-{
- PJ_ASSERT_RETURN(key != NULL, NULL);
- return key->user_data;
-}
-
-/*
- * pj_ioqueue_set_user_data()
- */
-PJ_DEF(pj_status_t) pj_ioqueue_set_user_data( pj_ioqueue_key_t *key,
- void *user_data,
- void **old_data)
-{
- PJ_ASSERT_RETURN(key, PJ_EINVAL);
-
- if (old_data)
- *old_data = key->user_data;
- key->user_data = user_data;
-
- return PJ_SUCCESS;
-}
-
-PJ_INLINE(int) key_has_pending_write(pj_ioqueue_key_t *key)
-{
- return !pj_list_empty(&key->write_list);
-}
-
-PJ_INLINE(int) key_has_pending_read(pj_ioqueue_key_t *key)
-{
- return !pj_list_empty(&key->read_list);
-}
-
-PJ_INLINE(int) key_has_pending_accept(pj_ioqueue_key_t *key)
-{
-#if PJ_HAS_TCP
- return !pj_list_empty(&key->accept_list);
-#else
- return 0;
-#endif
-}
-
-PJ_INLINE(int) key_has_pending_connect(pj_ioqueue_key_t *key)
-{
- return key->connecting;
-}
-
-
-/*
- * ioqueue_dispatch_event()
- *
- * Report occurence of an event in the key to be processed by the
- * framework.
- */
-void ioqueue_dispatch_write_event(pj_ioqueue_t *ioqueue, pj_ioqueue_key_t *h)
-{
- /* Lock the key. */
- pj_mutex_lock(h->mutex);
-
-#if defined(PJ_HAS_TCP) && PJ_HAS_TCP!=0
- if (h->connecting) {
- /* Completion of connect() operation */
- pj_ssize_t bytes_transfered;
-
- /* Clear operation. */
- h->connecting = 0;
-
- ioqueue_remove_from_set(ioqueue, h->fd, WRITEABLE_EVENT);
- ioqueue_remove_from_set(ioqueue, h->fd, EXCEPTION_EVENT);
-
- /* Unlock; from this point we don't need to hold key's mutex. */
- pj_mutex_unlock(h->mutex);
-
-#if (defined(PJ_HAS_SO_ERROR) && PJ_HAS_SO_ERROR!=0)
- /* from connect(2):
- * On Linux, use getsockopt to read the SO_ERROR option at
- * level SOL_SOCKET to determine whether connect() completed
- * successfully (if SO_ERROR is zero).
- */
- {
- int value;
- socklen_t vallen = sizeof(value);
- int gs_rc = getsockopt(h->fd, SOL_SOCKET, SO_ERROR,
- &value, &vallen);
- if (gs_rc != 0) {
- /* Argh!! What to do now???
- * Just indicate that the socket is connected. The
- * application will get error as soon as it tries to use
- * the socket to send/receive.
- */
- bytes_transfered = 0;
- } else {
- bytes_transfered = value;
- }
- }
-#elif defined(PJ_WIN32) && PJ_WIN32!=0
- bytes_transfered = 0; /* success */
-#else
- /* Excellent information in D.J. Bernstein page:
- * http://cr.yp.to/docs/connect.html
- *
- * Seems like the most portable way of detecting connect()
- * failure is to call getpeername(). If socket is connected,
- * getpeername() will return 0. If the socket is not connected,
- * it will return ENOTCONN, and read(fd, &ch, 1) will produce
- * the right errno through error slippage. This is a combination
- * of suggestions from Douglas C. Schmidt and Ken Keys.
- */
- int gp_rc;
- struct sockaddr_in addr;
- socklen_t addrlen = sizeof(addr);
-
- gp_rc = getpeername(h->fd, (struct sockaddr*)&addr, &addrlen);
- bytes_transfered = gp_rc;
-#endif
-
- /* Call callback. */
- if (h->cb.on_connect_complete)
- (*h->cb.on_connect_complete)(h, bytes_transfered);
-
- /* Done. */
-
- } else
-#endif /* PJ_HAS_TCP */
- if (key_has_pending_write(h)) {
- /* Socket is writable. */
- struct write_operation *write_op;
- pj_ssize_t sent;
- pj_status_t send_rc;
-
- /* Get the first in the queue. */
- write_op = h->write_list.next;
-
- /* For datagrams, we can remove the write_op from the list
- * so that send() can work in parallel.
- */
- if (h->fd_type == PJ_SOCK_DGRAM) {
+/* $Id$ */
+/*
+ * PJLIB - PJ Foundation Library
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ * Benny Prijono <bennylp@bulukucing.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+/*
+ * ioqueue_common_abs.c
+ *
+ * This contains common functionalities to emulate proactor pattern with
+ * various event dispatching mechanisms (e.g. select, epoll).
+ *
+ * This file will be included by the appropriate ioqueue implementation.
+ * This file is NOT supposed to be compiled as stand-alone source.
+ */
+
+static void ioqueue_init( pj_ioqueue_t *ioqueue )
+{
+ ioqueue->lock = NULL;
+ ioqueue->auto_delete_lock = 0;
+}
+
+static pj_status_t ioqueue_destroy(pj_ioqueue_t *ioqueue)
+{
+ if (ioqueue->auto_delete_lock && ioqueue->lock ) {
+ pj_lock_release(ioqueue->lock);
+ return pj_lock_destroy(ioqueue->lock);
+ } else
+ return PJ_SUCCESS;
+}
+
+/*
+ * pj_ioqueue_set_lock()
+ */
+PJ_DEF(pj_status_t) pj_ioqueue_set_lock( pj_ioqueue_t *ioqueue,
+ pj_lock_t *lock,
+ pj_bool_t auto_delete )
+{
+ PJ_ASSERT_RETURN(ioqueue && lock, PJ_EINVAL);
+
+ if (ioqueue->auto_delete_lock && ioqueue->lock) {
+ pj_lock_destroy(ioqueue->lock);
+ }
+
+ ioqueue->lock = lock;
+ ioqueue->auto_delete_lock = auto_delete;
+
+ return PJ_SUCCESS;
+}
+
+static pj_status_t ioqueue_init_key( pj_pool_t *pool,
+ pj_ioqueue_t *ioqueue,
+ pj_ioqueue_key_t *key,
+ pj_sock_t sock,
+ void *user_data,
+ const pj_ioqueue_callback *cb)
+{
+ pj_status_t rc;
+ int optlen;
+
+ key->ioqueue = ioqueue;
+ key->fd = sock;
+ key->user_data = user_data;
+ pj_list_init(&key->read_list);
+ pj_list_init(&key->write_list);
+#if PJ_HAS_TCP
+ pj_list_init(&key->accept_list);
+#endif
+
+ /* Save callback. */
+ pj_memcpy(&key->cb, cb, sizeof(pj_ioqueue_callback));
+
+ /* Get socket type. When socket type is datagram, some optimization
+ * will be performed during send to allow parallel send operations.
+ */
+ optlen = sizeof(key->fd_type);
+ rc = pj_sock_getsockopt(sock, PJ_SOL_SOCKET, PJ_SO_TYPE,
+ &key->fd_type, &optlen);
+ if (rc != PJ_SUCCESS)
+ key->fd_type = PJ_SOCK_STREAM;
+
+ /* Create mutex for the key. */
+ rc = pj_mutex_create_simple(pool, NULL, &key->mutex);
+
+ return rc;
+}
+
+static void ioqueue_destroy_key( pj_ioqueue_key_t *key )
+{
+ pj_mutex_destroy(key->mutex);
+}
+
+/*
+ * pj_ioqueue_get_user_data()
+ *
+ * Obtain value associated with a key.
+ */
+PJ_DEF(void*) pj_ioqueue_get_user_data( pj_ioqueue_key_t *key )
+{
+ PJ_ASSERT_RETURN(key != NULL, NULL);
+ return key->user_data;
+}
+
+/*
+ * pj_ioqueue_set_user_data()
+ */
+PJ_DEF(pj_status_t) pj_ioqueue_set_user_data( pj_ioqueue_key_t *key,
+ void *user_data,
+ void **old_data)
+{
+ PJ_ASSERT_RETURN(key, PJ_EINVAL);
+
+ if (old_data)
+ *old_data = key->user_data;
+ key->user_data = user_data;
+
+ return PJ_SUCCESS;
+}
+
+PJ_INLINE(int) key_has_pending_write(pj_ioqueue_key_t *key)
+{
+ return !pj_list_empty(&key->write_list);
+}
+
+PJ_INLINE(int) key_has_pending_read(pj_ioqueue_key_t *key)
+{
+ return !pj_list_empty(&key->read_list);
+}
+
+PJ_INLINE(int) key_has_pending_accept(pj_ioqueue_key_t *key)
+{
+#if PJ_HAS_TCP
+ return !pj_list_empty(&key->accept_list);
+#else
+ return 0;
+#endif
+}
+
+PJ_INLINE(int) key_has_pending_connect(pj_ioqueue_key_t *key)
+{
+ return key->connecting;
+}
+
+
+/*
+ * ioqueue_dispatch_event()
+ *
+ * Report occurence of an event in the key to be processed by the
+ * framework.
+ */
+void ioqueue_dispatch_write_event(pj_ioqueue_t *ioqueue, pj_ioqueue_key_t *h)
+{
+ /* Lock the key. */
+ pj_mutex_lock(h->mutex);
+
+#if defined(PJ_HAS_TCP) && PJ_HAS_TCP!=0
+ if (h->connecting) {
+ /* Completion of connect() operation */
+ pj_ssize_t bytes_transfered;
+
+ /* Clear operation. */
+ h->connecting = 0;
+
+ ioqueue_remove_from_set(ioqueue, h->fd, WRITEABLE_EVENT);
+ ioqueue_remove_from_set(ioqueue, h->fd, EXCEPTION_EVENT);
+
+ /* Unlock; from this point we don't need to hold key's mutex. */
+ pj_mutex_unlock(h->mutex);
+
+#if (defined(PJ_HAS_SO_ERROR) && PJ_HAS_SO_ERROR!=0)
+ /* from connect(2):
+ * On Linux, use getsockopt to read the SO_ERROR option at
+ * level SOL_SOCKET to determine whether connect() completed
+ * successfully (if SO_ERROR is zero).
+ */
+ {
+ int value;
+ socklen_t vallen = sizeof(value);
+ int gs_rc = getsockopt(h->fd, SOL_SOCKET, SO_ERROR,
+ &value, &vallen);
+ if (gs_rc != 0) {
+ /* Argh!! What to do now???
+ * Just indicate that the socket is connected. The
+ * application will get error as soon as it tries to use
+ * the socket to send/receive.
+ */
+ bytes_transfered = 0;
+ } else {
+ bytes_transfered = value;
+ }
+ }
+#elif defined(PJ_WIN32) && PJ_WIN32!=0
+ bytes_transfered = 0; /* success */
+#else
+ /* Excellent information in D.J. Bernstein page:
+ * http://cr.yp.to/docs/connect.html
+ *
+ * Seems like the most portable way of detecting connect()
+ * failure is to call getpeername(). If socket is connected,
+ * getpeername() will return 0. If the socket is not connected,
+ * it will return ENOTCONN, and read(fd, &ch, 1) will produce
+ * the right errno through error slippage. This is a combination
+ * of suggestions from Douglas C. Schmidt and Ken Keys.
+ */
+ int gp_rc;
+ struct sockaddr_in addr;
+ socklen_t addrlen = sizeof(addr);
+
+ gp_rc = getpeername(h->fd, (struct sockaddr*)&addr, &addrlen);
+ bytes_transfered = gp_rc;
+#endif
+
+ /* Call callback. */
+ if (h->cb.on_connect_complete)
+ (*h->cb.on_connect_complete)(h, bytes_transfered);
+
+ /* Done. */
+
+ } else
+#endif /* PJ_HAS_TCP */
+ if (key_has_pending_write(h)) {
+ /* Socket is writable. */
+ struct write_operation *write_op;
+ pj_ssize_t sent;
+ pj_status_t send_rc;
+
+ /* Get the first in the queue. */
+ write_op = h->write_list.next;
+
+ /* For datagrams, we can remove the write_op from the list
+ * so that send() can work in parallel.
+ */
+ if (h->fd_type == PJ_SOCK_DGRAM) {
pj_list_erase(write_op);
write_op->op = 0;
-
- if (pj_list_empty(&h->write_list))
- ioqueue_remove_from_set(ioqueue, h->fd, WRITEABLE_EVENT);
-
- pj_mutex_unlock(h->mutex);
- }
-
- /* Send the data.
- * Unfortunately we must do this while holding key's mutex, thus
- * preventing parallel write on a single key.. :-((
- */
- sent = write_op->size - write_op->written;
- if (write_op->op == PJ_IOQUEUE_OP_SEND) {
- send_rc = pj_sock_send(h->fd, write_op->buf+write_op->written,
- &sent, write_op->flags);
- } else if (write_op->op == PJ_IOQUEUE_OP_SEND_TO) {
- send_rc = pj_sock_sendto(h->fd,
- write_op->buf+write_op->written,
- &sent, write_op->flags,
- &write_op->rmt_addr,
- write_op->rmt_addrlen);
- } else {
- pj_assert(!"Invalid operation type!");
- send_rc = PJ_EBUG;
- }
-
- if (send_rc == PJ_SUCCESS) {
- write_op->written += sent;
- } else {
- pj_assert(send_rc > 0);
- write_op->written = -send_rc;
- }
-
- /* Are we finished with this buffer? */
- if (send_rc!=PJ_SUCCESS ||
- write_op->written == (pj_ssize_t)write_op->size ||
- h->fd_type == PJ_SOCK_DGRAM)
- {
- if (h->fd_type != PJ_SOCK_DGRAM) {
- /* Write completion of the whole stream. */
+
+ if (pj_list_empty(&h->write_list))
+ ioqueue_remove_from_set(ioqueue, h->fd, WRITEABLE_EVENT);
+
+ pj_mutex_unlock(h->mutex);
+ }
+
+ /* Send the data.
+ * Unfortunately we must do this while holding key's mutex, thus
+ * preventing parallel write on a single key.. :-((
+ */
+ sent = write_op->size - write_op->written;
+ if (write_op->op == PJ_IOQUEUE_OP_SEND) {
+ send_rc = pj_sock_send(h->fd, write_op->buf+write_op->written,
+ &sent, write_op->flags);
+ } else if (write_op->op == PJ_IOQUEUE_OP_SEND_TO) {
+ send_rc = pj_sock_sendto(h->fd,
+ write_op->buf+write_op->written,
+ &sent, write_op->flags,
+ &write_op->rmt_addr,
+ write_op->rmt_addrlen);
+ } else {
+ pj_assert(!"Invalid operation type!");
+ send_rc = PJ_EBUG;
+ }
+
+ if (send_rc == PJ_SUCCESS) {
+ write_op->written += sent;
+ } else {
+ pj_assert(send_rc > 0);
+ write_op->written = -send_rc;
+ }
+
+ /* Are we finished with this buffer? */
+ if (send_rc!=PJ_SUCCESS ||
+ write_op->written == (pj_ssize_t)write_op->size ||
+ h->fd_type == PJ_SOCK_DGRAM)
+ {
+ if (h->fd_type != PJ_SOCK_DGRAM) {
+ /* Write completion of the whole stream. */
pj_list_erase(write_op);
- write_op->op = 0;
-
- /* Clear operation if there's no more data to send. */
- if (pj_list_empty(&h->write_list))
- ioqueue_remove_from_set(ioqueue, h->fd, WRITEABLE_EVENT);
-
- /* No need to hold mutex anymore */
- pj_mutex_unlock(h->mutex);
- }
-
- /* Call callback. */
- if (h->cb.on_write_complete) {
- (*h->cb.on_write_complete)(h,
- (pj_ioqueue_op_key_t*)write_op,
- write_op->written);
- }
-
- } else {
- pj_mutex_unlock(h->mutex);
- }
-
- /* Done. */
- } else {
- /*
- * This is normal; execution may fall here when multiple threads
- * are signalled for the same event, but only one thread eventually
- * able to process the event.
- */
- pj_mutex_unlock(h->mutex);
- }
-}
-
-void ioqueue_dispatch_read_event( pj_ioqueue_t *ioqueue, pj_ioqueue_key_t *h )
-{
- pj_status_t rc;
-
- /* Lock the key. */
- pj_mutex_lock(h->mutex);
-
-# if PJ_HAS_TCP
- if (!pj_list_empty(&h->accept_list)) {
-
- struct accept_operation *accept_op;
-
- /* Get one accept operation from the list. */
- accept_op = h->accept_list.next;
- pj_list_erase(accept_op);
- accept_op->op = 0;
-
- /* Clear bit in fdset if there is no more pending accept */
- if (pj_list_empty(&h->accept_list))
- ioqueue_remove_from_set(ioqueue, h->fd, READABLE_EVENT);
-
- /* Unlock; from this point we don't need to hold key's mutex. */
- pj_mutex_unlock(h->mutex);
-
- rc=pj_sock_accept(h->fd, accept_op->accept_fd,
- accept_op->rmt_addr, accept_op->addrlen);
- if (rc==PJ_SUCCESS && accept_op->local_addr) {
- rc = pj_sock_getsockname(*accept_op->accept_fd,
- accept_op->local_addr,
- accept_op->addrlen);
- }
-
- /* Call callback. */
- if (h->cb.on_accept_complete) {
- (*h->cb.on_accept_complete)(h,
- (pj_ioqueue_op_key_t*)accept_op,
- *accept_op->accept_fd, rc);
- }
-
- }
- else
-# endif
- if (key_has_pending_read(h)) {
- struct read_operation *read_op;
- pj_ssize_t bytes_read;
-
- /* Get one pending read operation from the list. */
- read_op = h->read_list.next;
- pj_list_erase(read_op);
- read_op->op = 0;
-
- /* Clear fdset if there is no pending read. */
- if (pj_list_empty(&h->read_list))
- ioqueue_remove_from_set(ioqueue, h->fd, READABLE_EVENT);
-
- /* Unlock; from this point we don't need to hold key's mutex. */
- pj_mutex_unlock(h->mutex);
-
- bytes_read = read_op->size;
-
- if ((read_op->op == PJ_IOQUEUE_OP_RECV_FROM)) {
- rc = pj_sock_recvfrom(h->fd, read_op->buf, &bytes_read, 0,
- read_op->rmt_addr,
- read_op->rmt_addrlen);
- } else if ((read_op->op == PJ_IOQUEUE_OP_RECV)) {
- rc = pj_sock_recv(h->fd, read_op->buf, &bytes_read, 0);
- } else {
- pj_assert(read_op->op == PJ_IOQUEUE_OP_READ);
- /*
- * User has specified pj_ioqueue_read().
- * On Win32, we should do ReadFile(). But because we got
- * here because of select() anyway, user must have put a
- * socket descriptor on h->fd, which in this case we can
- * just call pj_sock_recv() instead of ReadFile().
- * On Unix, user may put a file in h->fd, so we'll have
- * to call read() here.
- * This may not compile on systems which doesn't have
- * read(). That's why we only specify PJ_LINUX here so
- * that error is easier to catch.
- */
-# if defined(PJ_WIN32) && PJ_WIN32 != 0
- rc = pj_sock_recv(h->fd, read_op->buf, &bytes_read, 0);
- //rc = ReadFile((HANDLE)h->fd, read_op->buf, read_op->size,
- // &bytes_read, NULL);
-# elif (defined(PJ_HAS_UNISTD_H) && PJ_HAS_UNISTD_H != 0)
- bytes_read = read(h->fd, read_op->buf, bytes_read);
- rc = (bytes_read >= 0) ? PJ_SUCCESS : pj_get_os_error();
-# elif defined(PJ_LINUX_KERNEL) && PJ_LINUX_KERNEL != 0
- bytes_read = sys_read(h->fd, read_op->buf, bytes_read);
- rc = (bytes_read >= 0) ? PJ_SUCCESS : -bytes_read;
-# else
-# error "Implement read() for this platform!"
-# endif
- }
-
- if (rc != PJ_SUCCESS) {
-# if defined(PJ_WIN32) && PJ_WIN32 != 0
- /* On Win32, for UDP, WSAECONNRESET on the receive side
- * indicates that previous sending has triggered ICMP Port
- * Unreachable message.
- * But we wouldn't know at this point which one of previous
- * key that has triggered the error, since UDP socket can
- * be shared!
- * So we'll just ignore it!
- */
-
- if (rc == PJ_STATUS_FROM_OS(WSAECONNRESET)) {
- //PJ_LOG(4,(THIS_FILE,
- // "Ignored ICMP port unreach. on key=%p", h));
- }
-# endif
-
- /* In any case we would report this to caller. */
- bytes_read = -rc;
- }
-
- /* Call callback. */
- if (h->cb.on_read_complete) {
- (*h->cb.on_read_complete)(h,
- (pj_ioqueue_op_key_t*)read_op,
- bytes_read);
- }
-
- } else {
+ write_op->op = 0;
+
+ /* Clear operation if there's no more data to send. */
+ if (pj_list_empty(&h->write_list))
+ ioqueue_remove_from_set(ioqueue, h->fd, WRITEABLE_EVENT);
+
+ /* No need to hold mutex anymore */
+ pj_mutex_unlock(h->mutex);
+ }
+
+ /* Call callback. */
+ if (h->cb.on_write_complete) {
+ (*h->cb.on_write_complete)(h,
+ (pj_ioqueue_op_key_t*)write_op,
+ write_op->written);
+ }
+
+ } else {
+ pj_mutex_unlock(h->mutex);
+ }
+
+ /* Done. */
+ } else {
/*
* This is normal; execution may fall here when multiple threads
* are signalled for the same event, but only one thread eventually
* able to process the event.
*/
- pj_mutex_unlock(h->mutex);
- }
-}
-
-
-void ioqueue_dispatch_exception_event( pj_ioqueue_t *ioqueue,
- pj_ioqueue_key_t *h )
-{
- pj_mutex_lock(h->mutex);
-
- if (!h->connecting) {
- /* It is possible that more than one thread was woken up, thus
- * the remaining thread will see h->connecting as zero because
- * it has been processed by other thread.
- */
- pj_mutex_unlock(h->mutex);
- return;
- }
-
- /* Clear operation. */
- h->connecting = 0;
-
- pj_mutex_unlock(h->mutex);
-
- ioqueue_remove_from_set(ioqueue, h->fd, WRITEABLE_EVENT);
- ioqueue_remove_from_set(ioqueue, h->fd, EXCEPTION_EVENT);
-
- /* Call callback. */
- if (h->cb.on_connect_complete)
- (*h->cb.on_connect_complete)(h, -1);
-}
-
-/*
- * pj_ioqueue_recv()
- *
- * Start asynchronous recv() from the socket.
- */
-PJ_DEF(pj_status_t) pj_ioqueue_recv( pj_ioqueue_key_t *key,
- pj_ioqueue_op_key_t *op_key,
- void *buffer,
- pj_ssize_t *length,
- unsigned flags )
-{
- pj_status_t status;
- pj_ssize_t size;
- struct read_operation *read_op;
-
- PJ_ASSERT_RETURN(key && op_key && buffer && length, PJ_EINVAL);
- PJ_CHECK_STACK();
+ pj_mutex_unlock(h->mutex);
+ }
+}
+
+void ioqueue_dispatch_read_event( pj_ioqueue_t *ioqueue, pj_ioqueue_key_t *h )
+{
+ pj_status_t rc;
+
+ /* Lock the key. */
+ pj_mutex_lock(h->mutex);
+
+# if PJ_HAS_TCP
+ if (!pj_list_empty(&h->accept_list)) {
+
+ struct accept_operation *accept_op;
+
+ /* Get one accept operation from the list. */
+ accept_op = h->accept_list.next;
+ pj_list_erase(accept_op);
+ accept_op->op = 0;
+
+ /* Clear bit in fdset if there is no more pending accept */
+ if (pj_list_empty(&h->accept_list))
+ ioqueue_remove_from_set(ioqueue, h->fd, READABLE_EVENT);
+
+ /* Unlock; from this point we don't need to hold key's mutex. */
+ pj_mutex_unlock(h->mutex);
+
+ rc=pj_sock_accept(h->fd, accept_op->accept_fd,
+ accept_op->rmt_addr, accept_op->addrlen);
+ if (rc==PJ_SUCCESS && accept_op->local_addr) {
+ rc = pj_sock_getsockname(*accept_op->accept_fd,
+ accept_op->local_addr,
+ accept_op->addrlen);
+ }
+
+ /* Call callback. */
+ if (h->cb.on_accept_complete) {
+ (*h->cb.on_accept_complete)(h,
+ (pj_ioqueue_op_key_t*)accept_op,
+ *accept_op->accept_fd, rc);
+ }
+
+ }
+ else
+# endif
+ if (key_has_pending_read(h)) {
+ struct read_operation *read_op;
+ pj_ssize_t bytes_read;
+
+ /* Get one pending read operation from the list. */
+ read_op = h->read_list.next;
+ pj_list_erase(read_op);
+ read_op->op = 0;
+
+ /* Clear fdset if there is no pending read. */
+ if (pj_list_empty(&h->read_list))
+ ioqueue_remove_from_set(ioqueue, h->fd, READABLE_EVENT);
+
+ /* Unlock; from this point we don't need to hold key's mutex. */
+ pj_mutex_unlock(h->mutex);
+
+ bytes_read = read_op->size;
+
+ if ((read_op->op == PJ_IOQUEUE_OP_RECV_FROM)) {
+ rc = pj_sock_recvfrom(h->fd, read_op->buf, &bytes_read, 0,
+ read_op->rmt_addr,
+ read_op->rmt_addrlen);
+ } else if ((read_op->op == PJ_IOQUEUE_OP_RECV)) {
+ rc = pj_sock_recv(h->fd, read_op->buf, &bytes_read, 0);
+ } else {
+ pj_assert(read_op->op == PJ_IOQUEUE_OP_READ);
+ /*
+ * User has specified pj_ioqueue_read().
+ * On Win32, we should do ReadFile(). But because we got
+ * here because of select() anyway, user must have put a
+ * socket descriptor on h->fd, which in this case we can
+ * just call pj_sock_recv() instead of ReadFile().
+ * On Unix, user may put a file in h->fd, so we'll have
+ * to call read() here.
+ * This may not compile on systems which doesn't have
+ * read(). That's why we only specify PJ_LINUX here so
+ * that error is easier to catch.
+ */
+# if defined(PJ_WIN32) && PJ_WIN32 != 0
+ rc = pj_sock_recv(h->fd, read_op->buf, &bytes_read, 0);
+ //rc = ReadFile((HANDLE)h->fd, read_op->buf, read_op->size,
+ // &bytes_read, NULL);
+# elif (defined(PJ_HAS_UNISTD_H) && PJ_HAS_UNISTD_H != 0)
+ bytes_read = read(h->fd, read_op->buf, bytes_read);
+ rc = (bytes_read >= 0) ? PJ_SUCCESS : pj_get_os_error();
+# elif defined(PJ_LINUX_KERNEL) && PJ_LINUX_KERNEL != 0
+ bytes_read = sys_read(h->fd, read_op->buf, bytes_read);
+ rc = (bytes_read >= 0) ? PJ_SUCCESS : -bytes_read;
+# else
+# error "Implement read() for this platform!"
+# endif
+ }
+
+ if (rc != PJ_SUCCESS) {
+# if defined(PJ_WIN32) && PJ_WIN32 != 0
+ /* On Win32, for UDP, WSAECONNRESET on the receive side
+ * indicates that previous sending has triggered ICMP Port
+ * Unreachable message.
+ * But we wouldn't know at this point which one of previous
+ * key that has triggered the error, since UDP socket can
+ * be shared!
+ * So we'll just ignore it!
+ */
+
+ if (rc == PJ_STATUS_FROM_OS(WSAECONNRESET)) {
+ //PJ_LOG(4,(THIS_FILE,
+ // "Ignored ICMP port unreach. on key=%p", h));
+ }
+# endif
+
+ /* In any case we would report this to caller. */
+ bytes_read = -rc;
+ }
+
+ /* Call callback. */
+ if (h->cb.on_read_complete) {
+ (*h->cb.on_read_complete)(h,
+ (pj_ioqueue_op_key_t*)read_op,
+ bytes_read);
+ }
+
+ } else {
+ /*
+ * This is normal; execution may fall here when multiple threads
+ * are signalled for the same event, but only one thread eventually
+ * able to process the event.
+ */
+ pj_mutex_unlock(h->mutex);
+ }
+}
+
+
+void ioqueue_dispatch_exception_event( pj_ioqueue_t *ioqueue,
+ pj_ioqueue_key_t *h )
+{
+ pj_mutex_lock(h->mutex);
+
+ if (!h->connecting) {
+ /* It is possible that more than one thread was woken up, thus
+ * the remaining thread will see h->connecting as zero because
+ * it has been processed by other thread.
+ */
+ pj_mutex_unlock(h->mutex);
+ return;
+ }
+
+ /* Clear operation. */
+ h->connecting = 0;
+
+ pj_mutex_unlock(h->mutex);
+
+ ioqueue_remove_from_set(ioqueue, h->fd, WRITEABLE_EVENT);
+ ioqueue_remove_from_set(ioqueue, h->fd, EXCEPTION_EVENT);
+
+ /* Call callback. */
+ if (h->cb.on_connect_complete)
+ (*h->cb.on_connect_complete)(h, -1);
+}
+
+/*
+ * pj_ioqueue_recv()
+ *
+ * Start asynchronous recv() from the socket.
+ */
+PJ_DEF(pj_status_t) pj_ioqueue_recv( pj_ioqueue_key_t *key,
+ pj_ioqueue_op_key_t *op_key,
+ void *buffer,
+ pj_ssize_t *length,
+ unsigned flags )
+{
+ pj_status_t status;
+ pj_ssize_t size;
+ struct read_operation *read_op;
+
+ PJ_ASSERT_RETURN(key && op_key && buffer && length, PJ_EINVAL);
+ PJ_CHECK_STACK();
read_op = (struct read_operation*)op_key;
read_op->op = 0;
-
- /* Try to see if there's data immediately available.
- */
- size = *length;
- status = pj_sock_recv(key->fd, buffer, &size, flags);
- if (status == PJ_SUCCESS) {
- /* Yes! Data is available! */
- *length = size;
- return PJ_SUCCESS;
- } else {
- /* If error is not EWOULDBLOCK (or EAGAIN on Linux), report
- * the error to caller.
- */
- if (status != PJ_STATUS_FROM_OS(PJ_BLOCKING_ERROR_VAL))
- return status;
- }
-
- /*
- * No data is immediately available.
- * Must schedule asynchronous operation to the ioqueue.
- */
- read_op->op = PJ_IOQUEUE_OP_RECV;
- read_op->buf = buffer;
- read_op->size = *length;
- read_op->flags = flags;
-
- pj_mutex_lock(key->mutex);
- pj_list_insert_before(&key->read_list, read_op);
- ioqueue_add_to_set(key->ioqueue, key->fd, READABLE_EVENT);
- pj_mutex_unlock(key->mutex);
-
- return PJ_EPENDING;
-}
-
-/*
- * pj_ioqueue_recvfrom()
- *
- * Start asynchronous recvfrom() from the socket.
- */
-PJ_DEF(pj_status_t) pj_ioqueue_recvfrom( pj_ioqueue_key_t *key,
- pj_ioqueue_op_key_t *op_key,
- void *buffer,
- pj_ssize_t *length,
- unsigned flags,
- pj_sockaddr_t *addr,
- int *addrlen)
-{
- pj_status_t status;
- pj_ssize_t size;
- struct read_operation *read_op;
-
- PJ_ASSERT_RETURN(key && op_key && buffer && length, PJ_EINVAL);
- PJ_CHECK_STACK();
+
+ /* Try to see if there's data immediately available.
+ */
+ size = *length;
+ status = pj_sock_recv(key->fd, buffer, &size, flags);
+ if (status == PJ_SUCCESS) {
+ /* Yes! Data is available! */
+ *length = size;
+ return PJ_SUCCESS;
+ } else {
+ /* If error is not EWOULDBLOCK (or EAGAIN on Linux), report
+ * the error to caller.
+ */
+ if (status != PJ_STATUS_FROM_OS(PJ_BLOCKING_ERROR_VAL))
+ return status;
+ }
+
+ /*
+ * No data is immediately available.
+ * Must schedule asynchronous operation to the ioqueue.
+ */
+ read_op->op = PJ_IOQUEUE_OP_RECV;
+ read_op->buf = buffer;
+ read_op->size = *length;
+ read_op->flags = flags;
+
+ pj_mutex_lock(key->mutex);
+ pj_list_insert_before(&key->read_list, read_op);
+ ioqueue_add_to_set(key->ioqueue, key->fd, READABLE_EVENT);
+ pj_mutex_unlock(key->mutex);
+
+ return PJ_EPENDING;
+}
+
+/*
+ * pj_ioqueue_recvfrom()
+ *
+ * Start asynchronous recvfrom() from the socket.
+ */
+PJ_DEF(pj_status_t) pj_ioqueue_recvfrom( pj_ioqueue_key_t *key,
+ pj_ioqueue_op_key_t *op_key,
+ void *buffer,
+ pj_ssize_t *length,
+ unsigned flags,
+ pj_sockaddr_t *addr,
+ int *addrlen)
+{
+ pj_status_t status;
+ pj_ssize_t size;
+ struct read_operation *read_op;
+
+ PJ_ASSERT_RETURN(key && op_key && buffer && length, PJ_EINVAL);
+ PJ_CHECK_STACK();
read_op = (struct read_operation*)op_key;
read_op->op = 0;
-
- /* Try to see if there's data immediately available.
- */
- size = *length;
- status = pj_sock_recvfrom(key->fd, buffer, &size, flags,
- addr, addrlen);
- if (status == PJ_SUCCESS) {
- /* Yes! Data is available! */
- *length = size;
- return PJ_SUCCESS;
- } else {
- /* If error is not EWOULDBLOCK (or EAGAIN on Linux), report
- * the error to caller.
- */
- if (status != PJ_STATUS_FROM_OS(PJ_BLOCKING_ERROR_VAL))
- return status;
- }
-
- /*
- * No data is immediately available.
- * Must schedule asynchronous operation to the ioqueue.
- */
- read_op->op = PJ_IOQUEUE_OP_RECV_FROM;
- read_op->buf = buffer;
- read_op->size = *length;
- read_op->flags = flags;
- read_op->rmt_addr = addr;
- read_op->rmt_addrlen = addrlen;
-
- pj_mutex_lock(key->mutex);
- pj_list_insert_before(&key->read_list, read_op);
- ioqueue_add_to_set(key->ioqueue, key->fd, READABLE_EVENT);
- pj_mutex_unlock(key->mutex);
-
- return PJ_EPENDING;
-}
-
-/*
- * pj_ioqueue_send()
- *
- * Start asynchronous send() to the descriptor.
- */
-PJ_DEF(pj_status_t) pj_ioqueue_send( pj_ioqueue_key_t *key,
- pj_ioqueue_op_key_t *op_key,
- const void *data,
- pj_ssize_t *length,
- unsigned flags)
-{
- struct write_operation *write_op;
- pj_status_t status;
- pj_ssize_t sent;
-
- PJ_ASSERT_RETURN(key && op_key && data && length, PJ_EINVAL);
- PJ_CHECK_STACK();
+
+ /* Try to see if there's data immediately available.
+ */
+ size = *length;
+ status = pj_sock_recvfrom(key->fd, buffer, &size, flags,
+ addr, addrlen);
+ if (status == PJ_SUCCESS) {
+ /* Yes! Data is available! */
+ *length = size;
+ return PJ_SUCCESS;
+ } else {
+ /* If error is not EWOULDBLOCK (or EAGAIN on Linux), report
+ * the error to caller.
+ */
+ if (status != PJ_STATUS_FROM_OS(PJ_BLOCKING_ERROR_VAL))
+ return status;
+ }
+
+ /*
+ * No data is immediately available.
+ * Must schedule asynchronous operation to the ioqueue.
+ */
+ read_op->op = PJ_IOQUEUE_OP_RECV_FROM;
+ read_op->buf = buffer;
+ read_op->size = *length;
+ read_op->flags = flags;
+ read_op->rmt_addr = addr;
+ read_op->rmt_addrlen = addrlen;
+
+ pj_mutex_lock(key->mutex);
+ pj_list_insert_before(&key->read_list, read_op);
+ ioqueue_add_to_set(key->ioqueue, key->fd, READABLE_EVENT);
+ pj_mutex_unlock(key->mutex);
+
+ return PJ_EPENDING;
+}
+
+/*
+ * pj_ioqueue_send()
+ *
+ * Start asynchronous send() to the descriptor.
+ */
+PJ_DEF(pj_status_t) pj_ioqueue_send( pj_ioqueue_key_t *key,
+ pj_ioqueue_op_key_t *op_key,
+ const void *data,
+ pj_ssize_t *length,
+ unsigned flags)
+{
+ struct write_operation *write_op;
+ pj_status_t status;
+ pj_ssize_t sent;
+
+ PJ_ASSERT_RETURN(key && op_key && data && length, PJ_EINVAL);
+ PJ_CHECK_STACK();
write_op = (struct write_operation*)op_key;
write_op->op = 0;
-
- /* Fast track:
- * Try to send data immediately, only if there's no pending write!
- * Note:
- * We are speculating that the list is empty here without properly
- * acquiring ioqueue's mutex first. This is intentional, to maximize
- * performance via parallelism.
- *
- * This should be safe, because:
- * - by convention, we require caller to make sure that the
- * key is not unregistered while other threads are invoking
- * an operation on the same key.
- * - pj_list_empty() is safe to be invoked by multiple threads,
- * even when other threads are modifying the list.
- */
- if (pj_list_empty(&key->write_list)) {
- /*
- * See if data can be sent immediately.
- */
- sent = *length;
- status = pj_sock_send(key->fd, data, &sent, flags);
- if (status == PJ_SUCCESS) {
- /* Success! */
- *length = sent;
- return PJ_SUCCESS;
- } else {
- /* If error is not EWOULDBLOCK (or EAGAIN on Linux), report
- * the error to caller.
- */
- if (status != PJ_STATUS_FROM_OS(PJ_BLOCKING_ERROR_VAL)) {
- return status;
- }
- }
- }
-
- /*
- * Schedule asynchronous send.
- */
- write_op->op = PJ_IOQUEUE_OP_SEND;
- write_op->buf = (void*)data;
- write_op->size = *length;
- write_op->written = 0;
- write_op->flags = flags;
-
- pj_mutex_lock(key->mutex);
- pj_list_insert_before(&key->write_list, write_op);
- ioqueue_add_to_set(key->ioqueue, key->fd, WRITEABLE_EVENT);
- pj_mutex_unlock(key->mutex);
-
- return PJ_EPENDING;
-}
-
-
-/*
- * pj_ioqueue_sendto()
- *
- * Start asynchronous write() to the descriptor.
- */
-PJ_DEF(pj_status_t) pj_ioqueue_sendto( pj_ioqueue_key_t *key,
- pj_ioqueue_op_key_t *op_key,
- const void *data,
- pj_ssize_t *length,
- unsigned flags,
- const pj_sockaddr_t *addr,
- int addrlen)
-{
- struct write_operation *write_op;
- pj_status_t status;
- pj_ssize_t sent;
-
- PJ_ASSERT_RETURN(key && op_key && data && length, PJ_EINVAL);
- PJ_CHECK_STACK();
+
+ /* Fast track:
+ * Try to send data immediately, only if there's no pending write!
+ * Note:
+ * We are speculating that the list is empty here without properly
+ * acquiring ioqueue's mutex first. This is intentional, to maximize
+ * performance via parallelism.
+ *
+ * This should be safe, because:
+ * - by convention, we require caller to make sure that the
+ * key is not unregistered while other threads are invoking
+ * an operation on the same key.
+ * - pj_list_empty() is safe to be invoked by multiple threads,
+ * even when other threads are modifying the list.
+ */
+ if (pj_list_empty(&key->write_list)) {
+ /*
+ * See if data can be sent immediately.
+ */
+ sent = *length;
+ status = pj_sock_send(key->fd, data, &sent, flags);
+ if (status == PJ_SUCCESS) {
+ /* Success! */
+ *length = sent;
+ return PJ_SUCCESS;
+ } else {
+ /* If error is not EWOULDBLOCK (or EAGAIN on Linux), report
+ * the error to caller.
+ */
+ if (status != PJ_STATUS_FROM_OS(PJ_BLOCKING_ERROR_VAL)) {
+ return status;
+ }
+ }
+ }
+
+ /*
+ * Schedule asynchronous send.
+ */
+ write_op->op = PJ_IOQUEUE_OP_SEND;
+ write_op->buf = (void*)data;
+ write_op->size = *length;
+ write_op->written = 0;
+ write_op->flags = flags;
+
+ pj_mutex_lock(key->mutex);
+ pj_list_insert_before(&key->write_list, write_op);
+ ioqueue_add_to_set(key->ioqueue, key->fd, WRITEABLE_EVENT);
+ pj_mutex_unlock(key->mutex);
+
+ return PJ_EPENDING;
+}
+
+
+/*
+ * pj_ioqueue_sendto()
+ *
+ * Start asynchronous write() to the descriptor.
+ */
+PJ_DEF(pj_status_t) pj_ioqueue_sendto( pj_ioqueue_key_t *key,
+ pj_ioqueue_op_key_t *op_key,
+ const void *data,
+ pj_ssize_t *length,
+ unsigned flags,
+ const pj_sockaddr_t *addr,
+ int addrlen)
+{
+ struct write_operation *write_op;
+ pj_status_t status;
+ pj_ssize_t sent;
+
+ PJ_ASSERT_RETURN(key && op_key && data && length, PJ_EINVAL);
+ PJ_CHECK_STACK();
write_op = (struct write_operation*)op_key;
write_op->op = 0;
-
- /* Fast track:
- * Try to send data immediately, only if there's no pending write!
- * Note:
- * We are speculating that the list is empty here without properly
- * acquiring ioqueue's mutex first. This is intentional, to maximize
- * performance via parallelism.
- *
- * This should be safe, because:
- * - by convention, we require caller to make sure that the
- * key is not unregistered while other threads are invoking
- * an operation on the same key.
- * - pj_list_empty() is safe to be invoked by multiple threads,
- * even when other threads are modifying the list.
- */
- if (pj_list_empty(&key->write_list)) {
- /*
- * See if data can be sent immediately.
- */
- sent = *length;
- status = pj_sock_sendto(key->fd, data, &sent, flags, addr, addrlen);
- if (status == PJ_SUCCESS) {
- /* Success! */
- *length = sent;
- return PJ_SUCCESS;
- } else {
- /* If error is not EWOULDBLOCK (or EAGAIN on Linux), report
- * the error to caller.
- */
- if (status != PJ_STATUS_FROM_OS(PJ_BLOCKING_ERROR_VAL)) {
- return status;
- }
- }
- }
-
- /*
- * Check that address storage can hold the address parameter.
- */
- PJ_ASSERT_RETURN(addrlen <= sizeof(pj_sockaddr_in), PJ_EBUG);
-
- /*
- * Schedule asynchronous send.
- */
- write_op->op = PJ_IOQUEUE_OP_SEND_TO;
- write_op->buf = (void*)data;
- write_op->size = *length;
- write_op->written = 0;
- write_op->flags = flags;
- pj_memcpy(&write_op->rmt_addr, addr, addrlen);
- write_op->rmt_addrlen = addrlen;
-
- pj_mutex_lock(key->mutex);
- pj_list_insert_before(&key->write_list, write_op);
- ioqueue_add_to_set(key->ioqueue, key->fd, WRITEABLE_EVENT);
- pj_mutex_unlock(key->mutex);
-
- return PJ_EPENDING;
-}
-
-#if PJ_HAS_TCP
-/*
- * Initiate overlapped accept() operation.
- */
-PJ_DEF(pj_status_t) pj_ioqueue_accept( pj_ioqueue_key_t *key,
- pj_ioqueue_op_key_t *op_key,
- pj_sock_t *new_sock,
- pj_sockaddr_t *local,
- pj_sockaddr_t *remote,
- int *addrlen)
-{
- struct accept_operation *accept_op;
- pj_status_t status;
-
- /* check parameters. All must be specified! */
- PJ_ASSERT_RETURN(key && op_key && new_sock, PJ_EINVAL);
+
+ /* Fast track:
+ * Try to send data immediately, only if there's no pending write!
+ * Note:
+ * We are speculating that the list is empty here without properly
+ * acquiring ioqueue's mutex first. This is intentional, to maximize
+ * performance via parallelism.
+ *
+ * This should be safe, because:
+ * - by convention, we require caller to make sure that the
+ * key is not unregistered while other threads are invoking
+ * an operation on the same key.
+ * - pj_list_empty() is safe to be invoked by multiple threads,
+ * even when other threads are modifying the list.
+ */
+ if (pj_list_empty(&key->write_list)) {
+ /*
+ * See if data can be sent immediately.
+ */
+ sent = *length;
+ status = pj_sock_sendto(key->fd, data, &sent, flags, addr, addrlen);
+ if (status == PJ_SUCCESS) {
+ /* Success! */
+ *length = sent;
+ return PJ_SUCCESS;
+ } else {
+ /* If error is not EWOULDBLOCK (or EAGAIN on Linux), report
+ * the error to caller.
+ */
+ if (status != PJ_STATUS_FROM_OS(PJ_BLOCKING_ERROR_VAL)) {
+ return status;
+ }
+ }
+ }
+
+ /*
+ * Check that address storage can hold the address parameter.
+ */
+ PJ_ASSERT_RETURN(addrlen <= sizeof(pj_sockaddr_in), PJ_EBUG);
+
+ /*
+ * Schedule asynchronous send.
+ */
+ write_op->op = PJ_IOQUEUE_OP_SEND_TO;
+ write_op->buf = (void*)data;
+ write_op->size = *length;
+ write_op->written = 0;
+ write_op->flags = flags;
+ pj_memcpy(&write_op->rmt_addr, addr, addrlen);
+ write_op->rmt_addrlen = addrlen;
+
+ pj_mutex_lock(key->mutex);
+ pj_list_insert_before(&key->write_list, write_op);
+ ioqueue_add_to_set(key->ioqueue, key->fd, WRITEABLE_EVENT);
+ pj_mutex_unlock(key->mutex);
+
+ return PJ_EPENDING;
+}
+
+#if PJ_HAS_TCP
+/*
+ * Initiate overlapped accept() operation.
+ */
+PJ_DEF(pj_status_t) pj_ioqueue_accept( pj_ioqueue_key_t *key,
+ pj_ioqueue_op_key_t *op_key,
+ pj_sock_t *new_sock,
+ pj_sockaddr_t *local,
+ pj_sockaddr_t *remote,
+ int *addrlen)
+{
+ struct accept_operation *accept_op;
+ pj_status_t status;
+
+ /* check parameters. All must be specified! */
+ PJ_ASSERT_RETURN(key && op_key && new_sock, PJ_EINVAL);
accept_op = (struct accept_operation*)op_key;
accept_op->op = 0;
-
- /* Fast track:
- * See if there's new connection available immediately.
- */
- if (pj_list_empty(&key->accept_list)) {
- status = pj_sock_accept(key->fd, new_sock, remote, addrlen);
- if (status == PJ_SUCCESS) {
- /* Yes! New connection is available! */
- if (local && addrlen) {
- status = pj_sock_getsockname(*new_sock, local, addrlen);
- if (status != PJ_SUCCESS) {
- pj_sock_close(*new_sock);
- *new_sock = PJ_INVALID_SOCKET;
- return status;
- }
- }
- return PJ_SUCCESS;
- } else {
- /* If error is not EWOULDBLOCK (or EAGAIN on Linux), report
- * the error to caller.
- */
- if (status != PJ_STATUS_FROM_OS(PJ_BLOCKING_ERROR_VAL)) {
- return status;
- }
- }
- }
-
- /*
- * No connection is available immediately.
- * Schedule accept() operation to be completed when there is incoming
- * connection available.
- */
- accept_op->op = PJ_IOQUEUE_OP_ACCEPT;
- accept_op->accept_fd = new_sock;
- accept_op->rmt_addr = remote;
- accept_op->addrlen= addrlen;
- accept_op->local_addr = local;
-
- pj_mutex_lock(key->mutex);
- pj_list_insert_before(&key->accept_list, accept_op);
- ioqueue_add_to_set(key->ioqueue, key->fd, READABLE_EVENT);
- pj_mutex_unlock(key->mutex);
-
- return PJ_EPENDING;
-}
-
-/*
- * Initiate overlapped connect() operation (well, it's non-blocking actually,
- * since there's no overlapped version of connect()).
- */
-PJ_DEF(pj_status_t) pj_ioqueue_connect( pj_ioqueue_key_t *key,
- const pj_sockaddr_t *addr,
- int addrlen )
-{
- pj_status_t status;
-
- /* check parameters. All must be specified! */
- PJ_ASSERT_RETURN(key && addr && addrlen, PJ_EINVAL);
-
- /* Check if socket has not been marked for connecting */
- if (key->connecting != 0)
- return PJ_EPENDING;
-
- status = pj_sock_connect(key->fd, addr, addrlen);
- if (status == PJ_SUCCESS) {
- /* Connected! */
- return PJ_SUCCESS;
- } else {
- if (status == PJ_STATUS_FROM_OS(PJ_BLOCKING_CONNECT_ERROR_VAL)) {
- /* Pending! */
- pj_mutex_lock(key->mutex);
- key->connecting = PJ_TRUE;
- ioqueue_add_to_set(key->ioqueue, key->fd, WRITEABLE_EVENT);
- ioqueue_add_to_set(key->ioqueue, key->fd, EXCEPTION_EVENT);
- pj_mutex_unlock(key->mutex);
- return PJ_EPENDING;
- } else {
- /* Error! */
- return status;
- }
- }
-}
-#endif /* PJ_HAS_TCP */
+
+ /* Fast track:
+ * See if there's new connection available immediately.
+ */
+ if (pj_list_empty(&key->accept_list)) {
+ status = pj_sock_accept(key->fd, new_sock, remote, addrlen);
+ if (status == PJ_SUCCESS) {
+ /* Yes! New connection is available! */
+ if (local && addrlen) {
+ status = pj_sock_getsockname(*new_sock, local, addrlen);
+ if (status != PJ_SUCCESS) {
+ pj_sock_close(*new_sock);
+ *new_sock = PJ_INVALID_SOCKET;
+ return status;
+ }
+ }
+ return PJ_SUCCESS;
+ } else {
+ /* If error is not EWOULDBLOCK (or EAGAIN on Linux), report
+ * the error to caller.
+ */
+ if (status != PJ_STATUS_FROM_OS(PJ_BLOCKING_ERROR_VAL)) {
+ return status;
+ }
+ }
+ }
+
+ /*
+ * No connection is available immediately.
+ * Schedule accept() operation to be completed when there is incoming
+ * connection available.
+ */
+ accept_op->op = PJ_IOQUEUE_OP_ACCEPT;
+ accept_op->accept_fd = new_sock;
+ accept_op->rmt_addr = remote;
+ accept_op->addrlen= addrlen;
+ accept_op->local_addr = local;
+
+ pj_mutex_lock(key->mutex);
+ pj_list_insert_before(&key->accept_list, accept_op);
+ ioqueue_add_to_set(key->ioqueue, key->fd, READABLE_EVENT);
+ pj_mutex_unlock(key->mutex);
+
+ return PJ_EPENDING;
+}
+
+/*
+ * Initiate overlapped connect() operation (well, it's non-blocking actually,
+ * since there's no overlapped version of connect()).
+ */
+PJ_DEF(pj_status_t) pj_ioqueue_connect( pj_ioqueue_key_t *key,
+ const pj_sockaddr_t *addr,
+ int addrlen )
+{
+ pj_status_t status;
+
+ /* check parameters. All must be specified! */
+ PJ_ASSERT_RETURN(key && addr && addrlen, PJ_EINVAL);
+
+ /* Check if socket has not been marked for connecting */
+ if (key->connecting != 0)
+ return PJ_EPENDING;
+
+ status = pj_sock_connect(key->fd, addr, addrlen);
+ if (status == PJ_SUCCESS) {
+ /* Connected! */
+ return PJ_SUCCESS;
+ } else {
+ if (status == PJ_STATUS_FROM_OS(PJ_BLOCKING_CONNECT_ERROR_VAL)) {
+ /* Pending! */
+ pj_mutex_lock(key->mutex);
+ key->connecting = PJ_TRUE;
+ ioqueue_add_to_set(key->ioqueue, key->fd, WRITEABLE_EVENT);
+ ioqueue_add_to_set(key->ioqueue, key->fd, EXCEPTION_EVENT);
+ pj_mutex_unlock(key->mutex);
+ return PJ_EPENDING;
+ } else {
+ /* Error! */
+ return status;
+ }
+ }
+}
+#endif /* PJ_HAS_TCP */
PJ_DEF(void) pj_ioqueue_op_key_init( pj_ioqueue_op_key_t *op_key,
@@ -840,7 +861,7 @@
pj_memset(op_key, 0, size);
}
-
+
/*
* pj_ioqueue_is_pending()
*/
diff --git a/pjlib/src/pj/ioqueue_common_abs.h b/pjlib/src/pj/ioqueue_common_abs.h
index 1902ff4..85f6f37 100644
--- a/pjlib/src/pj/ioqueue_common_abs.h
+++ b/pjlib/src/pj/ioqueue_common_abs.h
@@ -1,108 +1,129 @@
-/* $Id */
-
-/* ioqueue_common_abs.h
- *
- * This file contains private declarations for abstracting various
- * event polling/dispatching mechanisms (e.g. select, poll, epoll)
- * to the ioqueue.
- */
-
-#include <pj/list.h>
-
-/*
- * The select ioqueue relies on socket functions (pj_sock_xxx()) to return
- * the correct error code.
- */
-#if PJ_RETURN_OS_ERROR(100) != PJ_STATUS_FROM_OS(100)
-# error "Proper error reporting must be enabled for ioqueue to work!"
-#endif
-
-
-struct generic_operation
-{
- PJ_DECL_LIST_MEMBER(struct generic_operation);
- pj_ioqueue_operation_e op;
-};
-
-struct read_operation
-{
- PJ_DECL_LIST_MEMBER(struct read_operation);
- pj_ioqueue_operation_e op;
-
- void *buf;
- pj_size_t size;
- unsigned flags;
- pj_sockaddr_t *rmt_addr;
- int *rmt_addrlen;
-};
-
-struct write_operation
-{
- PJ_DECL_LIST_MEMBER(struct write_operation);
- pj_ioqueue_operation_e op;
-
- char *buf;
- pj_size_t size;
- pj_ssize_t written;
- unsigned flags;
- pj_sockaddr_in rmt_addr;
- int rmt_addrlen;
-};
-
-#if PJ_HAS_TCP
-struct accept_operation
-{
- PJ_DECL_LIST_MEMBER(struct accept_operation);
- pj_ioqueue_operation_e op;
-
- pj_sock_t *accept_fd;
- pj_sockaddr_t *local_addr;
- pj_sockaddr_t *rmt_addr;
- int *addrlen;
-};
-#endif
-
-union operation_key
-{
- struct generic_operation generic;
- struct read_operation read;
- struct write_operation write;
-#if PJ_HAS_TCP
- struct accept_operation accept;
-#endif
-};
-
-#define DECLARE_COMMON_KEY \
- PJ_DECL_LIST_MEMBER(struct pj_ioqueue_key_t); \
- pj_ioqueue_t *ioqueue; \
- pj_mutex_t *mutex; \
- pj_sock_t fd; \
- int fd_type; \
- void *user_data; \
- pj_ioqueue_callback cb; \
- int connecting; \
- struct read_operation read_list; \
- struct write_operation write_list; \
- struct accept_operation accept_list;
-
-
-#define DECLARE_COMMON_IOQUEUE \
- pj_lock_t *lock; \
- pj_bool_t auto_delete_lock;
-
-
-enum ioqueue_event_type
-{
- NO_EVENT,
- READABLE_EVENT,
- WRITEABLE_EVENT,
- EXCEPTION_EVENT,
-};
-
-static void ioqueue_add_to_set( pj_ioqueue_t *ioqueue,
- pj_sock_t fd,
- enum ioqueue_event_type event_type );
-static void ioqueue_remove_from_set( pj_ioqueue_t *ioqueue,
- pj_sock_t fd,
- enum ioqueue_event_type event_type);
-
+/* $Id */
+/*
+ * PJLIB - PJ Foundation Library
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ * Benny Prijono <bennylp@bulukucing.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+/* ioqueue_common_abs.h
+ *
+ * This file contains private declarations for abstracting various
+ * event polling/dispatching mechanisms (e.g. select, poll, epoll)
+ * to the ioqueue.
+ */
+
+#include <pj/list.h>
+
+/*
+ * The select ioqueue relies on socket functions (pj_sock_xxx()) to return
+ * the correct error code.
+ */
+#if PJ_RETURN_OS_ERROR(100) != PJ_STATUS_FROM_OS(100)
+# error "Proper error reporting must be enabled for ioqueue to work!"
+#endif
+
+
+struct generic_operation
+{
+ PJ_DECL_LIST_MEMBER(struct generic_operation);
+ pj_ioqueue_operation_e op;
+};
+
+struct read_operation
+{
+ PJ_DECL_LIST_MEMBER(struct read_operation);
+ pj_ioqueue_operation_e op;
+
+ void *buf;
+ pj_size_t size;
+ unsigned flags;
+ pj_sockaddr_t *rmt_addr;
+ int *rmt_addrlen;
+};
+
+struct write_operation
+{
+ PJ_DECL_LIST_MEMBER(struct write_operation);
+ pj_ioqueue_operation_e op;
+
+ char *buf;
+ pj_size_t size;
+ pj_ssize_t written;
+ unsigned flags;
+ pj_sockaddr_in rmt_addr;
+ int rmt_addrlen;
+};
+
+#if PJ_HAS_TCP
+struct accept_operation
+{
+ PJ_DECL_LIST_MEMBER(struct accept_operation);
+ pj_ioqueue_operation_e op;
+
+ pj_sock_t *accept_fd;
+ pj_sockaddr_t *local_addr;
+ pj_sockaddr_t *rmt_addr;
+ int *addrlen;
+};
+#endif
+
+union operation_key
+{
+ struct generic_operation generic;
+ struct read_operation read;
+ struct write_operation write;
+#if PJ_HAS_TCP
+ struct accept_operation accept;
+#endif
+};
+
+#define DECLARE_COMMON_KEY \
+ PJ_DECL_LIST_MEMBER(struct pj_ioqueue_key_t); \
+ pj_ioqueue_t *ioqueue; \
+ pj_mutex_t *mutex; \
+ pj_sock_t fd; \
+ int fd_type; \
+ void *user_data; \
+ pj_ioqueue_callback cb; \
+ int connecting; \
+ struct read_operation read_list; \
+ struct write_operation write_list; \
+ struct accept_operation accept_list;
+
+
+#define DECLARE_COMMON_IOQUEUE \
+ pj_lock_t *lock; \
+ pj_bool_t auto_delete_lock;
+
+
+enum ioqueue_event_type
+{
+ NO_EVENT,
+ READABLE_EVENT,
+ WRITEABLE_EVENT,
+ EXCEPTION_EVENT,
+};
+
+static void ioqueue_add_to_set( pj_ioqueue_t *ioqueue,
+ pj_sock_t fd,
+ enum ioqueue_event_type event_type );
+static void ioqueue_remove_from_set( pj_ioqueue_t *ioqueue,
+ pj_sock_t fd,
+ enum ioqueue_event_type event_type);
+
diff --git a/pjlib/src/pj/ioqueue_dummy.c b/pjlib/src/pj/ioqueue_dummy.c
index 3a9927f..b09e5ed 100644
--- a/pjlib/src/pj/ioqueue_dummy.c
+++ b/pjlib/src/pj/ioqueue_dummy.c
@@ -1,178 +1,199 @@
-/* $Id$
- *
- */
-#include <pj/ioqueue.h>
-#include <pj/os.h>
-#include <pj/log.h>
-#include <pj/list.h>
-#include <pj/pool.h>
-#include <pj/string.h>
-#include <pj/assert.h>
-#include <pj/sock.h>
-#include <pj/errno.h>
-
-#define THIS_FILE "ioqueue"
-
-#define PJ_IOQUEUE_IS_READ_OP(op) \
- ((op & PJ_IOQUEUE_OP_READ) || (op & PJ_IOQUEUE_OP_RECV_FROM))
-#define PJ_IOQUEUE_IS_WRITE_OP(op) \
- ((op & PJ_IOQUEUE_OP_WRITE) || (op & PJ_IOQUEUE_OP_SEND_TO))
-
-
-#if PJ_HAS_TCP
-# define PJ_IOQUEUE_IS_ACCEPT_OP(op) (op & PJ_IOQUEUE_OP_ACCEPT)
-# define PJ_IOQUEUE_IS_CONNECT_OP(op) (op & PJ_IOQUEUE_OP_CONNECT)
-#else
-# define PJ_IOQUEUE_IS_ACCEPT_OP(op) 0
-# define PJ_IOQUEUE_IS_CONNECT_OP(op) 0
-#endif
-
-#if defined(PJ_DEBUG) && PJ_DEBUG != 0
-# define VALIDATE_FD_SET 1
-#else
-# define VALIDATE_FD_SET 0
-#endif
-
-struct pj_ioqueue_key_t
-{
- PJ_DECL_LIST_MEMBER(struct pj_ioqueue_key_t)
- pj_sock_t fd;
- pj_ioqueue_operation_e op;
- void *user_data;
- pj_ioqueue_callback cb;
-};
-
-struct pj_ioqueue_t
-{
-};
-
-PJ_DEF(pj_status_t) pj_ioqueue_create( pj_pool_t *pool,
- pj_size_t max_fd,
- int max_threads,
- pj_ioqueue_t **ptr_ioqueue)
-{
- return PJ_ENOTSUP;
-}
-
-PJ_DEF(pj_status_t) pj_ioqueue_destroy(pj_ioqueue_t *ioque)
-{
- return PJ_ENOTSUP;
-}
-
-PJ_DEF(pj_status_t) pj_ioqueue_set_lock( pj_ioqueue_t *ioque,
- pj_lock_t *lock,
- pj_bool_t auto_delete )
-{
- return PJ_ENOTSUP;
-}
-
-PJ_DEF(pj_status_t) pj_ioqueue_register_sock( pj_pool_t *pool,
- pj_ioqueue_t *ioque,
- pj_sock_t sock,
- void *user_data,
- const pj_ioqueue_callback *cb,
- pj_ioqueue_key_t **ptr_key)
-{
- return PJ_ENOTSUP;
-}
-
-PJ_DEF(pj_status_t) pj_ioqueue_unregister( pj_ioqueue_t *ioque,
- pj_ioqueue_key_t *key)
-{
- return PJ_ENOTSUP;
-}
-
-PJ_DEF(void*) pj_ioqueue_get_user_data( pj_ioqueue_key_t *key )
-{
- return NULL;
-}
-
-
-PJ_DEF(int) pj_ioqueue_poll( pj_ioqueue_t *ioque, const pj_time_val *timeout)
-{
- return -1;
-}
-
-PJ_DEF(pj_status_t) pj_ioqueue_read( pj_ioqueue_t *ioque,
- pj_ioqueue_key_t *key,
- void *buffer,
- pj_size_t buflen)
-{
- return -1;
-}
-
-PJ_DEF(pj_status_t) pj_ioqueue_recv( pj_ioqueue_t *ioque,
- pj_ioqueue_key_t *key,
- void *buffer,
- pj_size_t buflen,
- unsigned flags)
-{
- return -1;
-}
-
-PJ_DEF(pj_status_t) pj_ioqueue_recvfrom( pj_ioqueue_t *ioque,
- pj_ioqueue_key_t *key,
- void *buffer,
- pj_size_t buflen,
- unsigned flags,
- pj_sockaddr_t *addr,
- int *addrlen)
-{
- return -1;
-}
-
-PJ_DEF(pj_status_t) pj_ioqueue_write( pj_ioqueue_t *ioque,
- pj_ioqueue_key_t *key,
- const void *data,
- pj_size_t datalen)
-{
- return -1;
-}
-
-PJ_DEF(pj_status_t) pj_ioqueue_send( pj_ioqueue_t *ioque,
- pj_ioqueue_key_t *key,
- const void *data,
- pj_size_t datalen,
- unsigned flags)
-{
- return -1;
-}
-
-PJ_DEF(pj_status_t) pj_ioqueue_sendto( pj_ioqueue_t *ioque,
- pj_ioqueue_key_t *key,
- const void *data,
- pj_size_t datalen,
- unsigned flags,
- const pj_sockaddr_t *addr,
- int addrlen)
-{
- return -1;
-}
-
-#if PJ_HAS_TCP
-/*
- * Initiate overlapped accept() operation.
- */
-PJ_DEF(pj_status_t) pj_ioqueue_accept( pj_ioqueue_t *ioqueue,
- pj_ioqueue_key_t *key,
- pj_sock_t *new_sock,
- pj_sockaddr_t *local,
- pj_sockaddr_t *remote,
- int *addrlen)
-{
- return -1;
-}
-
-/*
- * Initiate overlapped connect() operation (well, it's non-blocking actually,
- * since there's no overlapped version of connect()).
- */
-PJ_DEF(pj_status_t) pj_ioqueue_connect( pj_ioqueue_t *ioqueue,
- pj_ioqueue_key_t *key,
- const pj_sockaddr_t *addr,
- int addrlen )
-{
- return -1;
-}
-#endif /* PJ_HAS_TCP */
-
+/* $Id$
+ *
+ */
+/*
+ * PJLIB - PJ Foundation Library
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ * Benny Prijono <bennylp@bulukucing.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#include <pj/ioqueue.h>
+#include <pj/os.h>
+#include <pj/log.h>
+#include <pj/list.h>
+#include <pj/pool.h>
+#include <pj/string.h>
+#include <pj/assert.h>
+#include <pj/sock.h>
+#include <pj/errno.h>
+
+#define THIS_FILE "ioqueue"
+
+#define PJ_IOQUEUE_IS_READ_OP(op) \
+ ((op & PJ_IOQUEUE_OP_READ) || (op & PJ_IOQUEUE_OP_RECV_FROM))
+#define PJ_IOQUEUE_IS_WRITE_OP(op) \
+ ((op & PJ_IOQUEUE_OP_WRITE) || (op & PJ_IOQUEUE_OP_SEND_TO))
+
+
+#if PJ_HAS_TCP
+# define PJ_IOQUEUE_IS_ACCEPT_OP(op) (op & PJ_IOQUEUE_OP_ACCEPT)
+# define PJ_IOQUEUE_IS_CONNECT_OP(op) (op & PJ_IOQUEUE_OP_CONNECT)
+#else
+# define PJ_IOQUEUE_IS_ACCEPT_OP(op) 0
+# define PJ_IOQUEUE_IS_CONNECT_OP(op) 0
+#endif
+
+#if defined(PJ_DEBUG) && PJ_DEBUG != 0
+# define VALIDATE_FD_SET 1
+#else
+# define VALIDATE_FD_SET 0
+#endif
+
+struct pj_ioqueue_key_t
+{
+ PJ_DECL_LIST_MEMBER(struct pj_ioqueue_key_t)
+ pj_sock_t fd;
+ pj_ioqueue_operation_e op;
+ void *user_data;
+ pj_ioqueue_callback cb;
+};
+
+struct pj_ioqueue_t
+{
+};
+
+PJ_DEF(pj_status_t) pj_ioqueue_create( pj_pool_t *pool,
+ pj_size_t max_fd,
+ int max_threads,
+ pj_ioqueue_t **ptr_ioqueue)
+{
+ return PJ_ENOTSUP;
+}
+
+PJ_DEF(pj_status_t) pj_ioqueue_destroy(pj_ioqueue_t *ioque)
+{
+ return PJ_ENOTSUP;
+}
+
+PJ_DEF(pj_status_t) pj_ioqueue_set_lock( pj_ioqueue_t *ioque,
+ pj_lock_t *lock,
+ pj_bool_t auto_delete )
+{
+ return PJ_ENOTSUP;
+}
+
+PJ_DEF(pj_status_t) pj_ioqueue_register_sock( pj_pool_t *pool,
+ pj_ioqueue_t *ioque,
+ pj_sock_t sock,
+ void *user_data,
+ const pj_ioqueue_callback *cb,
+ pj_ioqueue_key_t **ptr_key)
+{
+ return PJ_ENOTSUP;
+}
+
+PJ_DEF(pj_status_t) pj_ioqueue_unregister( pj_ioqueue_t *ioque,
+ pj_ioqueue_key_t *key)
+{
+ return PJ_ENOTSUP;
+}
+
+PJ_DEF(void*) pj_ioqueue_get_user_data( pj_ioqueue_key_t *key )
+{
+ return NULL;
+}
+
+
+PJ_DEF(int) pj_ioqueue_poll( pj_ioqueue_t *ioque, const pj_time_val *timeout)
+{
+ return -1;
+}
+
+PJ_DEF(pj_status_t) pj_ioqueue_read( pj_ioqueue_t *ioque,
+ pj_ioqueue_key_t *key,
+ void *buffer,
+ pj_size_t buflen)
+{
+ return -1;
+}
+
+PJ_DEF(pj_status_t) pj_ioqueue_recv( pj_ioqueue_t *ioque,
+ pj_ioqueue_key_t *key,
+ void *buffer,
+ pj_size_t buflen,
+ unsigned flags)
+{
+ return -1;
+}
+
+PJ_DEF(pj_status_t) pj_ioqueue_recvfrom( pj_ioqueue_t *ioque,
+ pj_ioqueue_key_t *key,
+ void *buffer,
+ pj_size_t buflen,
+ unsigned flags,
+ pj_sockaddr_t *addr,
+ int *addrlen)
+{
+ return -1;
+}
+
+PJ_DEF(pj_status_t) pj_ioqueue_write( pj_ioqueue_t *ioque,
+ pj_ioqueue_key_t *key,
+ const void *data,
+ pj_size_t datalen)
+{
+ return -1;
+}
+
+PJ_DEF(pj_status_t) pj_ioqueue_send( pj_ioqueue_t *ioque,
+ pj_ioqueue_key_t *key,
+ const void *data,
+ pj_size_t datalen,
+ unsigned flags)
+{
+ return -1;
+}
+
+PJ_DEF(pj_status_t) pj_ioqueue_sendto( pj_ioqueue_t *ioque,
+ pj_ioqueue_key_t *key,
+ const void *data,
+ pj_size_t datalen,
+ unsigned flags,
+ const pj_sockaddr_t *addr,
+ int addrlen)
+{
+ return -1;
+}
+
+#if PJ_HAS_TCP
+/*
+ * Initiate overlapped accept() operation.
+ */
+PJ_DEF(pj_status_t) pj_ioqueue_accept( pj_ioqueue_t *ioqueue,
+ pj_ioqueue_key_t *key,
+ pj_sock_t *new_sock,
+ pj_sockaddr_t *local,
+ pj_sockaddr_t *remote,
+ int *addrlen)
+{
+ return -1;
+}
+
+/*
+ * Initiate overlapped connect() operation (well, it's non-blocking actually,
+ * since there's no overlapped version of connect()).
+ */
+PJ_DEF(pj_status_t) pj_ioqueue_connect( pj_ioqueue_t *ioqueue,
+ pj_ioqueue_key_t *key,
+ const pj_sockaddr_t *addr,
+ int addrlen )
+{
+ return -1;
+}
+#endif /* PJ_HAS_TCP */
+
diff --git a/pjlib/src/pj/ioqueue_epoll.c b/pjlib/src/pj/ioqueue_epoll.c
index 140bd13..8649042 100644
--- a/pjlib/src/pj/ioqueue_epoll.c
+++ b/pjlib/src/pj/ioqueue_epoll.c
@@ -1,458 +1,479 @@
-/* $Id$
- */
-/*
- * ioqueue_epoll.c
- *
- * This is the implementation of IOQueue framework using /dev/epoll
- * API in _both_ Linux user-mode and kernel-mode.
- */
-
-#include <pj/ioqueue.h>
-#include <pj/os.h>
-#include <pj/lock.h>
-#include <pj/log.h>
-#include <pj/list.h>
-#include <pj/pool.h>
-#include <pj/string.h>
-#include <pj/assert.h>
-#include <pj/errno.h>
-#include <pj/sock.h>
-#include <pj/compat/socket.h>
-
-#if !defined(PJ_LINUX_KERNEL) || PJ_LINUX_KERNEL==0
- /*
- * Linux user mode
- */
-# include <sys/epoll.h>
-# include <errno.h>
-# include <unistd.h>
-
-# define epoll_data data.ptr
-# define epoll_data_type void*
-# define ioctl_val_type unsigned long
-# define getsockopt_val_ptr int*
-# define os_getsockopt getsockopt
-# define os_ioctl ioctl
-# define os_read read
-# define os_close close
-# define os_epoll_create epoll_create
-# define os_epoll_ctl epoll_ctl
-# define os_epoll_wait epoll_wait
-#else
- /*
- * Linux kernel mode.
- */
-# include <linux/config.h>
-# include <linux/version.h>
-# if defined(MODVERSIONS)
-# include <linux/modversions.h>
-# endif
-# include <linux/kernel.h>
-# include <linux/poll.h>
-# include <linux/eventpoll.h>
-# include <linux/syscalls.h>
-# include <linux/errno.h>
-# include <linux/unistd.h>
-# include <asm/ioctls.h>
- enum EPOLL_EVENTS
- {
- EPOLLIN = 0x001,
- EPOLLOUT = 0x004,
- EPOLLERR = 0x008,
- };
-# define os_epoll_create sys_epoll_create
- static int os_epoll_ctl(int epfd, int op, int fd, struct epoll_event *event)
- {
- long rc;
- mm_segment_t oldfs = get_fs();
- set_fs(KERNEL_DS);
- rc = sys_epoll_ctl(epfd, op, fd, event);
- set_fs(oldfs);
- if (rc) {
- errno = -rc;
- return -1;
- } else {
- return 0;
- }
- }
- static int os_epoll_wait(int epfd, struct epoll_event *events,
- int maxevents, int timeout)
- {
- int count;
- mm_segment_t oldfs = get_fs();
- set_fs(KERNEL_DS);
- count = sys_epoll_wait(epfd, events, maxevents, timeout);
- set_fs(oldfs);
- return count;
- }
-# define os_close sys_close
-# define os_getsockopt pj_sock_getsockopt
- static int os_read(int fd, void *buf, size_t len)
- {
- long rc;
- mm_segment_t oldfs = get_fs();
- set_fs(KERNEL_DS);
- rc = sys_read(fd, buf, len);
- set_fs(oldfs);
- if (rc) {
- errno = -rc;
- return -1;
- } else {
- return 0;
- }
- }
-# define socklen_t unsigned
-# define ioctl_val_type unsigned long
- int ioctl(int fd, int opt, ioctl_val_type value);
- static int os_ioctl(int fd, int opt, ioctl_val_type value)
- {
- int rc;
- mm_segment_t oldfs = get_fs();
- set_fs(KERNEL_DS);
- rc = ioctl(fd, opt, value);
- set_fs(oldfs);
- if (rc < 0) {
- errno = -rc;
- return rc;
- } else
- return rc;
- }
-# define getsockopt_val_ptr char*
-
-# define epoll_data data
-# define epoll_data_type __u32
-#endif
-
-#define THIS_FILE "ioq_epoll"
-
-//#define TRACE_(expr) PJ_LOG(3,expr)
-#define TRACE_(expr)
-
-/*
- * Include common ioqueue abstraction.
- */
-#include "ioqueue_common_abs.h"
-
-/*
- * This describes each key.
- */
-struct pj_ioqueue_key_t
-{
- DECLARE_COMMON_KEY
-};
-
-/*
- * This describes the I/O queue.
- */
-struct pj_ioqueue_t
-{
- DECLARE_COMMON_IOQUEUE
-
- unsigned max, count;
- pj_ioqueue_key_t hlist;
- int epfd;
-};
-
-/* Include implementation for common abstraction after we declare
- * pj_ioqueue_key_t and pj_ioqueue_t.
- */
-#include "ioqueue_common_abs.c"
-
-/*
- * pj_ioqueue_name()
- */
-PJ_DEF(const char*) pj_ioqueue_name(void)
-{
-#if defined(PJ_LINUX_KERNEL) && PJ_LINUX_KERNEL!=0
- return "epoll-kernel";
-#else
- return "epoll";
-#endif
-}
-
-/*
- * pj_ioqueue_create()
- *
- * Create select ioqueue.
- */
-PJ_DEF(pj_status_t) pj_ioqueue_create( pj_pool_t *pool,
- pj_size_t max_fd,
- pj_ioqueue_t **p_ioqueue)
-{
- pj_ioqueue_t *ioqueue;
- pj_status_t rc;
- pj_lock_t *lock;
-
- /* Check that arguments are valid. */
- PJ_ASSERT_RETURN(pool != NULL && p_ioqueue != NULL &&
- max_fd > 0, PJ_EINVAL);
-
- /* Check that size of pj_ioqueue_op_key_t is sufficient */
- PJ_ASSERT_RETURN(sizeof(pj_ioqueue_op_key_t)-sizeof(void*) >=
- sizeof(union operation_key), PJ_EBUG);
-
- ioqueue = pj_pool_alloc(pool, sizeof(pj_ioqueue_t));
-
- ioqueue_init(ioqueue);
-
- ioqueue->max = max_fd;
- ioqueue->count = 0;
- pj_list_init(&ioqueue->hlist);
-
- rc = pj_lock_create_simple_mutex(pool, "ioq%p", &lock);
- if (rc != PJ_SUCCESS)
- return rc;
-
- rc = pj_ioqueue_set_lock(ioqueue, lock, PJ_TRUE);
- if (rc != PJ_SUCCESS)
- return rc;
-
- ioqueue->epfd = os_epoll_create(max_fd);
- if (ioqueue->epfd < 0) {
- ioqueue_destroy(ioqueue);
- return PJ_RETURN_OS_ERROR(pj_get_native_os_error());
- }
-
- PJ_LOG(4, ("pjlib", "epoll I/O Queue created (%p)", ioqueue));
-
- *p_ioqueue = ioqueue;
- return PJ_SUCCESS;
-}
-
-/*
- * pj_ioqueue_destroy()
- *
- * Destroy ioqueue.
- */
-PJ_DEF(pj_status_t) pj_ioqueue_destroy(pj_ioqueue_t *ioqueue)
-{
- PJ_ASSERT_RETURN(ioqueue, PJ_EINVAL);
- PJ_ASSERT_RETURN(ioqueue->epfd > 0, PJ_EINVALIDOP);
-
- pj_lock_acquire(ioqueue->lock);
- os_close(ioqueue->epfd);
- ioqueue->epfd = 0;
- return ioqueue_destroy(ioqueue);
-}
-
-/*
- * pj_ioqueue_register_sock()
- *
- * Register a socket to ioqueue.
- */
-PJ_DEF(pj_status_t) pj_ioqueue_register_sock( pj_pool_t *pool,
- pj_ioqueue_t *ioqueue,
- pj_sock_t sock,
- void *user_data,
- const pj_ioqueue_callback *cb,
- pj_ioqueue_key_t **p_key)
-{
- pj_ioqueue_key_t *key = NULL;
- pj_uint32_t value;
- struct epoll_event ev;
- int status;
- pj_status_t rc = PJ_SUCCESS;
-
- PJ_ASSERT_RETURN(pool && ioqueue && sock != PJ_INVALID_SOCKET &&
- cb && p_key, PJ_EINVAL);
-
- pj_lock_acquire(ioqueue->lock);
-
- if (ioqueue->count >= ioqueue->max) {
- rc = PJ_ETOOMANY;
- TRACE_((THIS_FILE, "pj_ioqueue_register_sock error: too many files"));
- goto on_return;
- }
-
- /* Set socket to nonblocking. */
- value = 1;
- if ((rc=os_ioctl(sock, FIONBIO, (ioctl_val_type)&value))) {
- TRACE_((THIS_FILE, "pj_ioqueue_register_sock error: ioctl rc=%d",
- rc));
- rc = pj_get_netos_error();
- goto on_return;
- }
-
- /* Create key. */
- key = (pj_ioqueue_key_t*)pj_pool_zalloc(pool, sizeof(pj_ioqueue_key_t));
- rc = ioqueue_init_key(pool, ioqueue, key, sock, user_data, cb);
- if (rc != PJ_SUCCESS) {
- key = NULL;
- goto on_return;
- }
-
- /* os_epoll_ctl. */
- ev.events = EPOLLIN | EPOLLOUT | EPOLLERR;
- ev.epoll_data = (epoll_data_type)key;
- status = os_epoll_ctl(ioqueue->epfd, EPOLL_CTL_ADD, sock, &ev);
- if (status < 0) {
- rc = pj_get_os_error();
- key = NULL;
- TRACE_((THIS_FILE,
- "pj_ioqueue_register_sock error: os_epoll_ctl rc=%d",
- status));
- goto on_return;
- }
-
- /* Register */
- pj_list_insert_before(&ioqueue->hlist, key);
- ++ioqueue->count;
-
-on_return:
- *p_key = key;
- pj_lock_release(ioqueue->lock);
-
- return rc;
-}
-
-/*
- * pj_ioqueue_unregister()
- *
- * Unregister handle from ioqueue.
- */
-PJ_DEF(pj_status_t) pj_ioqueue_unregister( pj_ioqueue_key_t *key)
-{
- pj_ioqueue_t *ioqueue;
- struct epoll_event ev;
- int status;
-
- PJ_ASSERT_RETURN(key != NULL, PJ_EINVAL);
-
- ioqueue = key->ioqueue;
- pj_lock_acquire(ioqueue->lock);
-
- pj_assert(ioqueue->count > 0);
- --ioqueue->count;
- pj_list_erase(key);
-
- ev.events = 0;
- ev.epoll_data = (epoll_data_type)key;
- status = os_epoll_ctl( ioqueue->epfd, EPOLL_CTL_DEL, key->fd, &ev);
- if (status != 0) {
- pj_status_t rc = pj_get_os_error();
- pj_lock_release(ioqueue->lock);
- return rc;
- }
-
- pj_lock_release(ioqueue->lock);
-
- /* Destroy the key. */
- ioqueue_destroy_key(key);
-
- return PJ_SUCCESS;
-}
-
-/* ioqueue_remove_from_set()
- * This function is called from ioqueue_dispatch_event() to instruct
- * the ioqueue to remove the specified descriptor from ioqueue's descriptor
- * set for the specified event.
- */
-static void ioqueue_remove_from_set( pj_ioqueue_t *ioqueue,
- pj_sock_t fd,
- enum ioqueue_event_type event_type)
-{
-}
-
-/*
- * ioqueue_add_to_set()
- * This function is called from pj_ioqueue_recv(), pj_ioqueue_send() etc
- * to instruct the ioqueue to add the specified handle to ioqueue's descriptor
- * set for the specified event.
- */
-static void ioqueue_add_to_set( pj_ioqueue_t *ioqueue,
- pj_sock_t fd,
- enum ioqueue_event_type event_type )
-{
-}
-
-/*
- * pj_ioqueue_poll()
- *
- */
-PJ_DEF(int) pj_ioqueue_poll( pj_ioqueue_t *ioqueue, const pj_time_val *timeout)
-{
- int i, count, processed;
- struct epoll_event events[PJ_IOQUEUE_MAX_EVENTS_IN_SINGLE_POLL];
- int msec;
- struct queue {
- pj_ioqueue_key_t *key;
- enum ioqueue_event_type event_type;
- } queue[PJ_IOQUEUE_MAX_EVENTS_IN_SINGLE_POLL];
-
- PJ_CHECK_STACK();
-
- msec = timeout ? PJ_TIME_VAL_MSEC(*timeout) : 9000;
-
- count = os_epoll_wait( ioqueue->epfd, events, PJ_ARRAY_SIZE(events), msec);
- if (count <= 0)
- return count;
-
- /* Lock ioqueue. */
- pj_lock_acquire(ioqueue->lock);
-
- for (processed=0, i=0; i<count; ++i) {
- pj_ioqueue_key_t *h = (pj_ioqueue_key_t*)(epoll_data_type)
- events[i].epoll_data;
-
- /*
- * Check readability.
- */
- if ((events[i].events & EPOLLIN) &&
- (key_has_pending_read(h) || key_has_pending_accept(h))) {
- queue[processed].key = h;
- queue[processed].event_type = READABLE_EVENT;
- ++processed;
- }
-
- /*
- * Check for writeability.
- */
- if ((events[i].events & EPOLLOUT) && key_has_pending_write(h)) {
- queue[processed].key = h;
- queue[processed].event_type = WRITEABLE_EVENT;
- ++processed;
- }
-
-#if PJ_HAS_TCP
- /*
- * Check for completion of connect() operation.
- */
- if ((events[i].events & EPOLLOUT) && (h->connecting)) {
- queue[processed].key = h;
- queue[processed].event_type = WRITEABLE_EVENT;
- ++processed;
- }
-#endif /* PJ_HAS_TCP */
-
- /*
- * Check for error condition.
- */
- if (events[i].events & EPOLLERR && (h->connecting)) {
- queue[processed].key = h;
- queue[processed].event_type = EXCEPTION_EVENT;
- ++processed;
- }
- }
- pj_lock_release(ioqueue->lock);
-
- /* Now process the events. */
- for (i=0; i<processed; ++i) {
- switch (queue[i].event_type) {
- case READABLE_EVENT:
- ioqueue_dispatch_read_event(ioqueue, queue[i].key);
- break;
- case WRITEABLE_EVENT:
- ioqueue_dispatch_write_event(ioqueue, queue[i].key);
- break;
- case EXCEPTION_EVENT:
- ioqueue_dispatch_exception_event(ioqueue, queue[i].key);
- break;
- case NO_EVENT:
- pj_assert(!"Invalid event!");
- break;
- }
- }
-
- return processed;
-}
-
+/* $Id$
+ */
+/*
+ * PJLIB - PJ Foundation Library
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ * Benny Prijono <bennylp@bulukucing.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+/*
+ * ioqueue_epoll.c
+ *
+ * This is the implementation of IOQueue framework using /dev/epoll
+ * API in _both_ Linux user-mode and kernel-mode.
+ */
+
+#include <pj/ioqueue.h>
+#include <pj/os.h>
+#include <pj/lock.h>
+#include <pj/log.h>
+#include <pj/list.h>
+#include <pj/pool.h>
+#include <pj/string.h>
+#include <pj/assert.h>
+#include <pj/errno.h>
+#include <pj/sock.h>
+#include <pj/compat/socket.h>
+
+#if !defined(PJ_LINUX_KERNEL) || PJ_LINUX_KERNEL==0
+ /*
+ * Linux user mode
+ */
+# include <sys/epoll.h>
+# include <errno.h>
+# include <unistd.h>
+
+# define epoll_data data.ptr
+# define epoll_data_type void*
+# define ioctl_val_type unsigned long
+# define getsockopt_val_ptr int*
+# define os_getsockopt getsockopt
+# define os_ioctl ioctl
+# define os_read read
+# define os_close close
+# define os_epoll_create epoll_create
+# define os_epoll_ctl epoll_ctl
+# define os_epoll_wait epoll_wait
+#else
+ /*
+ * Linux kernel mode.
+ */
+# include <linux/config.h>
+# include <linux/version.h>
+# if defined(MODVERSIONS)
+# include <linux/modversions.h>
+# endif
+# include <linux/kernel.h>
+# include <linux/poll.h>
+# include <linux/eventpoll.h>
+# include <linux/syscalls.h>
+# include <linux/errno.h>
+# include <linux/unistd.h>
+# include <asm/ioctls.h>
+ enum EPOLL_EVENTS
+ {
+ EPOLLIN = 0x001,
+ EPOLLOUT = 0x004,
+ EPOLLERR = 0x008,
+ };
+# define os_epoll_create sys_epoll_create
+ static int os_epoll_ctl(int epfd, int op, int fd, struct epoll_event *event)
+ {
+ long rc;
+ mm_segment_t oldfs = get_fs();
+ set_fs(KERNEL_DS);
+ rc = sys_epoll_ctl(epfd, op, fd, event);
+ set_fs(oldfs);
+ if (rc) {
+ errno = -rc;
+ return -1;
+ } else {
+ return 0;
+ }
+ }
+ static int os_epoll_wait(int epfd, struct epoll_event *events,
+ int maxevents, int timeout)
+ {
+ int count;
+ mm_segment_t oldfs = get_fs();
+ set_fs(KERNEL_DS);
+ count = sys_epoll_wait(epfd, events, maxevents, timeout);
+ set_fs(oldfs);
+ return count;
+ }
+# define os_close sys_close
+# define os_getsockopt pj_sock_getsockopt
+ static int os_read(int fd, void *buf, size_t len)
+ {
+ long rc;
+ mm_segment_t oldfs = get_fs();
+ set_fs(KERNEL_DS);
+ rc = sys_read(fd, buf, len);
+ set_fs(oldfs);
+ if (rc) {
+ errno = -rc;
+ return -1;
+ } else {
+ return 0;
+ }
+ }
+# define socklen_t unsigned
+# define ioctl_val_type unsigned long
+ int ioctl(int fd, int opt, ioctl_val_type value);
+ static int os_ioctl(int fd, int opt, ioctl_val_type value)
+ {
+ int rc;
+ mm_segment_t oldfs = get_fs();
+ set_fs(KERNEL_DS);
+ rc = ioctl(fd, opt, value);
+ set_fs(oldfs);
+ if (rc < 0) {
+ errno = -rc;
+ return rc;
+ } else
+ return rc;
+ }
+# define getsockopt_val_ptr char*
+
+# define epoll_data data
+# define epoll_data_type __u32
+#endif
+
+#define THIS_FILE "ioq_epoll"
+
+//#define TRACE_(expr) PJ_LOG(3,expr)
+#define TRACE_(expr)
+
+/*
+ * Include common ioqueue abstraction.
+ */
+#include "ioqueue_common_abs.h"
+
+/*
+ * This describes each key.
+ */
+struct pj_ioqueue_key_t
+{
+ DECLARE_COMMON_KEY
+};
+
+/*
+ * This describes the I/O queue.
+ */
+struct pj_ioqueue_t
+{
+ DECLARE_COMMON_IOQUEUE
+
+ unsigned max, count;
+ pj_ioqueue_key_t hlist;
+ int epfd;
+};
+
+/* Include implementation for common abstraction after we declare
+ * pj_ioqueue_key_t and pj_ioqueue_t.
+ */
+#include "ioqueue_common_abs.c"
+
+/*
+ * pj_ioqueue_name()
+ */
+PJ_DEF(const char*) pj_ioqueue_name(void)
+{
+#if defined(PJ_LINUX_KERNEL) && PJ_LINUX_KERNEL!=0
+ return "epoll-kernel";
+#else
+ return "epoll";
+#endif
+}
+
+/*
+ * pj_ioqueue_create()
+ *
+ * Create select ioqueue.
+ */
+PJ_DEF(pj_status_t) pj_ioqueue_create( pj_pool_t *pool,
+ pj_size_t max_fd,
+ pj_ioqueue_t **p_ioqueue)
+{
+ pj_ioqueue_t *ioqueue;
+ pj_status_t rc;
+ pj_lock_t *lock;
+
+ /* Check that arguments are valid. */
+ PJ_ASSERT_RETURN(pool != NULL && p_ioqueue != NULL &&
+ max_fd > 0, PJ_EINVAL);
+
+ /* Check that size of pj_ioqueue_op_key_t is sufficient */
+ PJ_ASSERT_RETURN(sizeof(pj_ioqueue_op_key_t)-sizeof(void*) >=
+ sizeof(union operation_key), PJ_EBUG);
+
+ ioqueue = pj_pool_alloc(pool, sizeof(pj_ioqueue_t));
+
+ ioqueue_init(ioqueue);
+
+ ioqueue->max = max_fd;
+ ioqueue->count = 0;
+ pj_list_init(&ioqueue->hlist);
+
+ rc = pj_lock_create_simple_mutex(pool, "ioq%p", &lock);
+ if (rc != PJ_SUCCESS)
+ return rc;
+
+ rc = pj_ioqueue_set_lock(ioqueue, lock, PJ_TRUE);
+ if (rc != PJ_SUCCESS)
+ return rc;
+
+ ioqueue->epfd = os_epoll_create(max_fd);
+ if (ioqueue->epfd < 0) {
+ ioqueue_destroy(ioqueue);
+ return PJ_RETURN_OS_ERROR(pj_get_native_os_error());
+ }
+
+ PJ_LOG(4, ("pjlib", "epoll I/O Queue created (%p)", ioqueue));
+
+ *p_ioqueue = ioqueue;
+ return PJ_SUCCESS;
+}
+
+/*
+ * pj_ioqueue_destroy()
+ *
+ * Destroy ioqueue.
+ */
+PJ_DEF(pj_status_t) pj_ioqueue_destroy(pj_ioqueue_t *ioqueue)
+{
+ PJ_ASSERT_RETURN(ioqueue, PJ_EINVAL);
+ PJ_ASSERT_RETURN(ioqueue->epfd > 0, PJ_EINVALIDOP);
+
+ pj_lock_acquire(ioqueue->lock);
+ os_close(ioqueue->epfd);
+ ioqueue->epfd = 0;
+ return ioqueue_destroy(ioqueue);
+}
+
+/*
+ * pj_ioqueue_register_sock()
+ *
+ * Register a socket to ioqueue.
+ */
+PJ_DEF(pj_status_t) pj_ioqueue_register_sock( pj_pool_t *pool,
+ pj_ioqueue_t *ioqueue,
+ pj_sock_t sock,
+ void *user_data,
+ const pj_ioqueue_callback *cb,
+ pj_ioqueue_key_t **p_key)
+{
+ pj_ioqueue_key_t *key = NULL;
+ pj_uint32_t value;
+ struct epoll_event ev;
+ int status;
+ pj_status_t rc = PJ_SUCCESS;
+
+ PJ_ASSERT_RETURN(pool && ioqueue && sock != PJ_INVALID_SOCKET &&
+ cb && p_key, PJ_EINVAL);
+
+ pj_lock_acquire(ioqueue->lock);
+
+ if (ioqueue->count >= ioqueue->max) {
+ rc = PJ_ETOOMANY;
+ TRACE_((THIS_FILE, "pj_ioqueue_register_sock error: too many files"));
+ goto on_return;
+ }
+
+ /* Set socket to nonblocking. */
+ value = 1;
+ if ((rc=os_ioctl(sock, FIONBIO, (ioctl_val_type)&value))) {
+ TRACE_((THIS_FILE, "pj_ioqueue_register_sock error: ioctl rc=%d",
+ rc));
+ rc = pj_get_netos_error();
+ goto on_return;
+ }
+
+ /* Create key. */
+ key = (pj_ioqueue_key_t*)pj_pool_zalloc(pool, sizeof(pj_ioqueue_key_t));
+ rc = ioqueue_init_key(pool, ioqueue, key, sock, user_data, cb);
+ if (rc != PJ_SUCCESS) {
+ key = NULL;
+ goto on_return;
+ }
+
+ /* os_epoll_ctl. */
+ ev.events = EPOLLIN | EPOLLOUT | EPOLLERR;
+ ev.epoll_data = (epoll_data_type)key;
+ status = os_epoll_ctl(ioqueue->epfd, EPOLL_CTL_ADD, sock, &ev);
+ if (status < 0) {
+ rc = pj_get_os_error();
+ key = NULL;
+ TRACE_((THIS_FILE,
+ "pj_ioqueue_register_sock error: os_epoll_ctl rc=%d",
+ status));
+ goto on_return;
+ }
+
+ /* Register */
+ pj_list_insert_before(&ioqueue->hlist, key);
+ ++ioqueue->count;
+
+on_return:
+ *p_key = key;
+ pj_lock_release(ioqueue->lock);
+
+ return rc;
+}
+
+/*
+ * pj_ioqueue_unregister()
+ *
+ * Unregister handle from ioqueue.
+ */
+PJ_DEF(pj_status_t) pj_ioqueue_unregister( pj_ioqueue_key_t *key)
+{
+ pj_ioqueue_t *ioqueue;
+ struct epoll_event ev;
+ int status;
+
+ PJ_ASSERT_RETURN(key != NULL, PJ_EINVAL);
+
+ ioqueue = key->ioqueue;
+ pj_lock_acquire(ioqueue->lock);
+
+ pj_assert(ioqueue->count > 0);
+ --ioqueue->count;
+ pj_list_erase(key);
+
+ ev.events = 0;
+ ev.epoll_data = (epoll_data_type)key;
+ status = os_epoll_ctl( ioqueue->epfd, EPOLL_CTL_DEL, key->fd, &ev);
+ if (status != 0) {
+ pj_status_t rc = pj_get_os_error();
+ pj_lock_release(ioqueue->lock);
+ return rc;
+ }
+
+ pj_lock_release(ioqueue->lock);
+
+ /* Destroy the key. */
+ ioqueue_destroy_key(key);
+
+ return PJ_SUCCESS;
+}
+
+/* ioqueue_remove_from_set()
+ * This function is called from ioqueue_dispatch_event() to instruct
+ * the ioqueue to remove the specified descriptor from ioqueue's descriptor
+ * set for the specified event.
+ */
+static void ioqueue_remove_from_set( pj_ioqueue_t *ioqueue,
+ pj_sock_t fd,
+ enum ioqueue_event_type event_type)
+{
+}
+
+/*
+ * ioqueue_add_to_set()
+ * This function is called from pj_ioqueue_recv(), pj_ioqueue_send() etc
+ * to instruct the ioqueue to add the specified handle to ioqueue's descriptor
+ * set for the specified event.
+ */
+static void ioqueue_add_to_set( pj_ioqueue_t *ioqueue,
+ pj_sock_t fd,
+ enum ioqueue_event_type event_type )
+{
+}
+
+/*
+ * pj_ioqueue_poll()
+ *
+ */
+PJ_DEF(int) pj_ioqueue_poll( pj_ioqueue_t *ioqueue, const pj_time_val *timeout)
+{
+ int i, count, processed;
+ struct epoll_event events[PJ_IOQUEUE_MAX_EVENTS_IN_SINGLE_POLL];
+ int msec;
+ struct queue {
+ pj_ioqueue_key_t *key;
+ enum ioqueue_event_type event_type;
+ } queue[PJ_IOQUEUE_MAX_EVENTS_IN_SINGLE_POLL];
+
+ PJ_CHECK_STACK();
+
+ msec = timeout ? PJ_TIME_VAL_MSEC(*timeout) : 9000;
+
+ count = os_epoll_wait( ioqueue->epfd, events, PJ_ARRAY_SIZE(events), msec);
+ if (count <= 0)
+ return count;
+
+ /* Lock ioqueue. */
+ pj_lock_acquire(ioqueue->lock);
+
+ for (processed=0, i=0; i<count; ++i) {
+ pj_ioqueue_key_t *h = (pj_ioqueue_key_t*)(epoll_data_type)
+ events[i].epoll_data;
+
+ /*
+ * Check readability.
+ */
+ if ((events[i].events & EPOLLIN) &&
+ (key_has_pending_read(h) || key_has_pending_accept(h))) {
+ queue[processed].key = h;
+ queue[processed].event_type = READABLE_EVENT;
+ ++processed;
+ }
+
+ /*
+ * Check for writeability.
+ */
+ if ((events[i].events & EPOLLOUT) && key_has_pending_write(h)) {
+ queue[processed].key = h;
+ queue[processed].event_type = WRITEABLE_EVENT;
+ ++processed;
+ }
+
+#if PJ_HAS_TCP
+ /*
+ * Check for completion of connect() operation.
+ */
+ if ((events[i].events & EPOLLOUT) && (h->connecting)) {
+ queue[processed].key = h;
+ queue[processed].event_type = WRITEABLE_EVENT;
+ ++processed;
+ }
+#endif /* PJ_HAS_TCP */
+
+ /*
+ * Check for error condition.
+ */
+ if (events[i].events & EPOLLERR && (h->connecting)) {
+ queue[processed].key = h;
+ queue[processed].event_type = EXCEPTION_EVENT;
+ ++processed;
+ }
+ }
+ pj_lock_release(ioqueue->lock);
+
+ /* Now process the events. */
+ for (i=0; i<processed; ++i) {
+ switch (queue[i].event_type) {
+ case READABLE_EVENT:
+ ioqueue_dispatch_read_event(ioqueue, queue[i].key);
+ break;
+ case WRITEABLE_EVENT:
+ ioqueue_dispatch_write_event(ioqueue, queue[i].key);
+ break;
+ case EXCEPTION_EVENT:
+ ioqueue_dispatch_exception_event(ioqueue, queue[i].key);
+ break;
+ case NO_EVENT:
+ pj_assert(!"Invalid event!");
+ break;
+ }
+ }
+
+ return processed;
+}
+
diff --git a/pjlib/src/pj/ioqueue_linux_kernel.c b/pjlib/src/pj/ioqueue_linux_kernel.c
index 9ac8dda..deaca1e 100644
--- a/pjlib/src/pj/ioqueue_linux_kernel.c
+++ b/pjlib/src/pj/ioqueue_linux_kernel.c
@@ -1,146 +1,167 @@
-/* $Id$
- *
- */
-#include <pj/ioqueue.h>
-#include <pj/os.h>
-#include <pj/log.h>
-#include <pj/list.h>
-#include <pj/pool.h>
-#include <pj/string.h>
-#include <pj/assert.h>
-#include <pj/sock.h>
-
-#define THIS_FILE "ioqueue"
-
-#define PJ_IOQUEUE_IS_READ_OP(op) \
- ((op & PJ_IOQUEUE_OP_READ) || (op & PJ_IOQUEUE_OP_RECV_FROM))
-#define PJ_IOQUEUE_IS_WRITE_OP(op) \
- ((op & PJ_IOQUEUE_OP_WRITE) || (op & PJ_IOQUEUE_OP_SEND_TO))
-
-
-#if PJ_HAS_TCP
-# define PJ_IOQUEUE_IS_ACCEPT_OP(op) (op & PJ_IOQUEUE_OP_ACCEPT)
-# define PJ_IOQUEUE_IS_CONNECT_OP(op) (op & PJ_IOQUEUE_OP_CONNECT)
-#else
-# define PJ_IOQUEUE_IS_ACCEPT_OP(op) 0
-# define PJ_IOQUEUE_IS_CONNECT_OP(op) 0
-#endif
-
-#if defined(PJ_DEBUG) && PJ_DEBUG != 0
-# define VALIDATE_FD_SET 1
-#else
-# define VALIDATE_FD_SET 0
-#endif
-
-struct pj_ioqueue_key_t
-{
- PJ_DECL_LIST_MEMBER(struct pj_ioqueue_key_t)
- pj_sock_t fd;
- pj_ioqueue_operation_e op;
- void *user_data;
- pj_ioqueue_callback cb;
-};
-
-struct pj_ioqueue_t
-{
-};
-
-PJ_DEF(pj_ioqueue_t*) pj_ioqueue_create(pj_pool_t *pool, pj_size_t max_fd)
-{
- return NULL;
-}
-
-PJ_DEF(pj_status_t) pj_ioqueue_destroy(pj_ioqueue_t *ioque)
-{
- return 0;
-}
-
-PJ_DEF(pj_ioqueue_key_t*) pj_ioqueue_register( pj_pool_t *pool,
- pj_ioqueue_t *ioque,
- pj_oshandle_t sock,
- void *user_data,
- const pj_ioqueue_callback *cb)
-{
- return NULL;
-}
-
-PJ_DEF(pj_status_t) pj_ioqueue_unregister( pj_ioqueue_t *ioque,
- pj_ioqueue_key_t *key)
-{
- return -1;
-}
-
-PJ_DEF(void*) pj_ioqueue_get_user_data( pj_ioqueue_key_t *key )
-{
- return NULL;
-}
-
-
-PJ_DEF(int) pj_ioqueue_poll( pj_ioqueue_t *ioque, const pj_time_val *timeout)
-{
- return -1;
-}
-
-PJ_DEF(int) pj_ioqueue_read( pj_ioqueue_t *ioque,
- pj_ioqueue_key_t *key,
- void *buffer,
- pj_size_t buflen)
-{
- return -1;
-}
-
-PJ_DEF(int) pj_ioqueue_recvfrom( pj_ioqueue_t *ioque,
- pj_ioqueue_key_t *key,
- void *buffer,
- pj_size_t buflen,
- pj_sockaddr_t *addr,
- int *addrlen)
-{
- return -1;
-}
-
-PJ_DEF(int) pj_ioqueue_write( pj_ioqueue_t *ioque,
- pj_ioqueue_key_t *key,
- const void *data,
- pj_size_t datalen)
-{
- return -1;
-}
-
-PJ_DEF(int) pj_ioqueue_sendto( pj_ioqueue_t *ioque,
- pj_ioqueue_key_t *key,
- const void *data,
- pj_size_t datalen,
- const pj_sockaddr_t *addr,
- int addrlen)
-{
- return -1;
-}
-
-#if PJ_HAS_TCP
-/*
- * Initiate overlapped accept() operation.
- */
-PJ_DEF(int) pj_ioqueue_accept( pj_ioqueue_t *ioqueue,
- pj_ioqueue_key_t *key,
- pj_sock_t *new_sock,
- pj_sockaddr_t *local,
- pj_sockaddr_t *remote,
- int *addrlen)
-{
- return -1;
-}
-
-/*
- * Initiate overlapped connect() operation (well, it's non-blocking actually,
- * since there's no overlapped version of connect()).
- */
-PJ_DEF(pj_status_t) pj_ioqueue_connect( pj_ioqueue_t *ioqueue,
- pj_ioqueue_key_t *key,
- const pj_sockaddr_t *addr,
- int addrlen )
-{
- return -1;
-}
-#endif /* PJ_HAS_TCP */
-
+/* $Id$
+ *
+ */
+/*
+ * PJLIB - PJ Foundation Library
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ * Benny Prijono <bennylp@bulukucing.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#include <pj/ioqueue.h>
+#include <pj/os.h>
+#include <pj/log.h>
+#include <pj/list.h>
+#include <pj/pool.h>
+#include <pj/string.h>
+#include <pj/assert.h>
+#include <pj/sock.h>
+
+#define THIS_FILE "ioqueue"
+
+#define PJ_IOQUEUE_IS_READ_OP(op) \
+ ((op & PJ_IOQUEUE_OP_READ) || (op & PJ_IOQUEUE_OP_RECV_FROM))
+#define PJ_IOQUEUE_IS_WRITE_OP(op) \
+ ((op & PJ_IOQUEUE_OP_WRITE) || (op & PJ_IOQUEUE_OP_SEND_TO))
+
+
+#if PJ_HAS_TCP
+# define PJ_IOQUEUE_IS_ACCEPT_OP(op) (op & PJ_IOQUEUE_OP_ACCEPT)
+# define PJ_IOQUEUE_IS_CONNECT_OP(op) (op & PJ_IOQUEUE_OP_CONNECT)
+#else
+# define PJ_IOQUEUE_IS_ACCEPT_OP(op) 0
+# define PJ_IOQUEUE_IS_CONNECT_OP(op) 0
+#endif
+
+#if defined(PJ_DEBUG) && PJ_DEBUG != 0
+# define VALIDATE_FD_SET 1
+#else
+# define VALIDATE_FD_SET 0
+#endif
+
+struct pj_ioqueue_key_t
+{
+ PJ_DECL_LIST_MEMBER(struct pj_ioqueue_key_t)
+ pj_sock_t fd;
+ pj_ioqueue_operation_e op;
+ void *user_data;
+ pj_ioqueue_callback cb;
+};
+
+struct pj_ioqueue_t
+{
+};
+
+PJ_DEF(pj_ioqueue_t*) pj_ioqueue_create(pj_pool_t *pool, pj_size_t max_fd)
+{
+ return NULL;
+}
+
+PJ_DEF(pj_status_t) pj_ioqueue_destroy(pj_ioqueue_t *ioque)
+{
+ return 0;
+}
+
+PJ_DEF(pj_ioqueue_key_t*) pj_ioqueue_register( pj_pool_t *pool,
+ pj_ioqueue_t *ioque,
+ pj_oshandle_t sock,
+ void *user_data,
+ const pj_ioqueue_callback *cb)
+{
+ return NULL;
+}
+
+PJ_DEF(pj_status_t) pj_ioqueue_unregister( pj_ioqueue_t *ioque,
+ pj_ioqueue_key_t *key)
+{
+ return -1;
+}
+
+PJ_DEF(void*) pj_ioqueue_get_user_data( pj_ioqueue_key_t *key )
+{
+ return NULL;
+}
+
+
+PJ_DEF(int) pj_ioqueue_poll( pj_ioqueue_t *ioque, const pj_time_val *timeout)
+{
+ return -1;
+}
+
+PJ_DEF(int) pj_ioqueue_read( pj_ioqueue_t *ioque,
+ pj_ioqueue_key_t *key,
+ void *buffer,
+ pj_size_t buflen)
+{
+ return -1;
+}
+
+PJ_DEF(int) pj_ioqueue_recvfrom( pj_ioqueue_t *ioque,
+ pj_ioqueue_key_t *key,
+ void *buffer,
+ pj_size_t buflen,
+ pj_sockaddr_t *addr,
+ int *addrlen)
+{
+ return -1;
+}
+
+PJ_DEF(int) pj_ioqueue_write( pj_ioqueue_t *ioque,
+ pj_ioqueue_key_t *key,
+ const void *data,
+ pj_size_t datalen)
+{
+ return -1;
+}
+
+PJ_DEF(int) pj_ioqueue_sendto( pj_ioqueue_t *ioque,
+ pj_ioqueue_key_t *key,
+ const void *data,
+ pj_size_t datalen,
+ const pj_sockaddr_t *addr,
+ int addrlen)
+{
+ return -1;
+}
+
+#if PJ_HAS_TCP
+/*
+ * Initiate overlapped accept() operation.
+ */
+PJ_DEF(int) pj_ioqueue_accept( pj_ioqueue_t *ioqueue,
+ pj_ioqueue_key_t *key,
+ pj_sock_t *new_sock,
+ pj_sockaddr_t *local,
+ pj_sockaddr_t *remote,
+ int *addrlen)
+{
+ return -1;
+}
+
+/*
+ * Initiate overlapped connect() operation (well, it's non-blocking actually,
+ * since there's no overlapped version of connect()).
+ */
+PJ_DEF(pj_status_t) pj_ioqueue_connect( pj_ioqueue_t *ioqueue,
+ pj_ioqueue_key_t *key,
+ const pj_sockaddr_t *addr,
+ int addrlen )
+{
+ return -1;
+}
+#endif /* PJ_HAS_TCP */
+
diff --git a/pjlib/src/pj/ioqueue_select.c b/pjlib/src/pj/ioqueue_select.c
index 09318d4..de1e97c 100644
--- a/pjlib/src/pj/ioqueue_select.c
+++ b/pjlib/src/pj/ioqueue_select.c
@@ -1,514 +1,535 @@
-/* $Id$
- */
-/*
- * sock_select.c
- *
- * This is the implementation of IOQueue using pj_sock_select().
- * It runs anywhere where pj_sock_select() is available (currently
- * Win32, Linux, Linux kernel, etc.).
- */
-
-#include <pj/ioqueue.h>
-#include <pj/os.h>
-#include <pj/lock.h>
-#include <pj/log.h>
-#include <pj/list.h>
-#include <pj/pool.h>
-#include <pj/string.h>
-#include <pj/assert.h>
-#include <pj/sock.h>
-#include <pj/compat/socket.h>
-#include <pj/sock_select.h>
-#include <pj/errno.h>
-
-/*
- * Include declaration from common abstraction.
- */
-#include "ioqueue_common_abs.h"
-
-/*
- * ISSUES with ioqueue_select()
- *
- * EAGAIN/EWOULDBLOCK error in recv():
- * - when multiple threads are working with the ioqueue, application
- * may receive EAGAIN or EWOULDBLOCK in the receive callback.
- * This error happens because more than one thread is watching for
- * the same descriptor set, so when all of them call recv() or recvfrom()
- * simultaneously, only one will succeed and the rest will get the error.
- *
- */
-#define THIS_FILE "ioq_select"
-
-/*
- * The select ioqueue relies on socket functions (pj_sock_xxx()) to return
- * the correct error code.
- */
-#if PJ_RETURN_OS_ERROR(100) != PJ_STATUS_FROM_OS(100)
-# error "Error reporting must be enabled for this function to work!"
-#endif
-
-/**
- * Get the number of descriptors in the set. This is defined in sock_select.c
- * This function will only return the number of sockets set from PJ_FD_SET
- * operation. When the set is modified by other means (such as by select()),
- * the count will not be reflected here.
- *
- * That's why don't export this function in the header file, to avoid
- * misunderstanding.
- *
- * @param fdsetp The descriptor set.
- *
- * @return Number of descriptors in the set.
- */
-PJ_DECL(pj_size_t) PJ_FD_COUNT(const pj_fd_set_t *fdsetp);
-
-
-/*
- * During debugging build, VALIDATE_FD_SET is set.
- * This will check the validity of the fd_sets.
- */
-/*
-#if defined(PJ_DEBUG) && PJ_DEBUG != 0
-# define VALIDATE_FD_SET 1
-#else
-# define VALIDATE_FD_SET 0
-#endif
-*/
-#define VALIDATE_FD_SET 0
-
-/*
- * This describes each key.
- */
-struct pj_ioqueue_key_t
-{
- DECLARE_COMMON_KEY
-};
-
-/*
- * This describes the I/O queue itself.
- */
-struct pj_ioqueue_t
-{
- DECLARE_COMMON_IOQUEUE
-
- unsigned max, count;
- pj_ioqueue_key_t key_list;
- pj_fd_set_t rfdset;
- pj_fd_set_t wfdset;
-#if PJ_HAS_TCP
- pj_fd_set_t xfdset;
-#endif
-};
-
-/* Include implementation for common abstraction after we declare
- * pj_ioqueue_key_t and pj_ioqueue_t.
- */
-#include "ioqueue_common_abs.c"
-
-/*
- * pj_ioqueue_name()
- */
-PJ_DEF(const char*) pj_ioqueue_name(void)
-{
- return "select";
-}
-
-/*
- * pj_ioqueue_create()
- *
- * Create select ioqueue.
- */
-PJ_DEF(pj_status_t) pj_ioqueue_create( pj_pool_t *pool,
- pj_size_t max_fd,
- pj_ioqueue_t **p_ioqueue)
-{
- pj_ioqueue_t *ioqueue;
- pj_lock_t *lock;
- pj_status_t rc;
-
- /* Check that arguments are valid. */
- PJ_ASSERT_RETURN(pool != NULL && p_ioqueue != NULL &&
- max_fd > 0 && max_fd <= PJ_IOQUEUE_MAX_HANDLES,
- PJ_EINVAL);
-
- /* Check that size of pj_ioqueue_op_key_t is sufficient */
- PJ_ASSERT_RETURN(sizeof(pj_ioqueue_op_key_t)-sizeof(void*) >=
- sizeof(union operation_key), PJ_EBUG);
-
- ioqueue = pj_pool_alloc(pool, sizeof(pj_ioqueue_t));
-
- ioqueue_init(ioqueue);
-
- ioqueue->max = max_fd;
- ioqueue->count = 0;
- PJ_FD_ZERO(&ioqueue->rfdset);
- PJ_FD_ZERO(&ioqueue->wfdset);
-#if PJ_HAS_TCP
- PJ_FD_ZERO(&ioqueue->xfdset);
-#endif
- pj_list_init(&ioqueue->key_list);
-
- rc = pj_lock_create_simple_mutex(pool, "ioq%p", &lock);
- if (rc != PJ_SUCCESS)
- return rc;
-
- rc = pj_ioqueue_set_lock(ioqueue, lock, PJ_TRUE);
- if (rc != PJ_SUCCESS)
- return rc;
-
- PJ_LOG(4, ("pjlib", "select() I/O Queue created (%p)", ioqueue));
-
- *p_ioqueue = ioqueue;
- return PJ_SUCCESS;
-}
-
-/*
- * pj_ioqueue_destroy()
- *
- * Destroy ioqueue.
- */
-PJ_DEF(pj_status_t) pj_ioqueue_destroy(pj_ioqueue_t *ioqueue)
-{
- PJ_ASSERT_RETURN(ioqueue, PJ_EINVAL);
-
- pj_lock_acquire(ioqueue->lock);
- return ioqueue_destroy(ioqueue);
-}
-
-
-/*
- * pj_ioqueue_register_sock()
- *
- * Register a handle to ioqueue.
- */
-PJ_DEF(pj_status_t) pj_ioqueue_register_sock( pj_pool_t *pool,
- pj_ioqueue_t *ioqueue,
- pj_sock_t sock,
- void *user_data,
- const pj_ioqueue_callback *cb,
- pj_ioqueue_key_t **p_key)
-{
- pj_ioqueue_key_t *key = NULL;
- pj_uint32_t value;
- pj_status_t rc = PJ_SUCCESS;
-
- PJ_ASSERT_RETURN(pool && ioqueue && sock != PJ_INVALID_SOCKET &&
- cb && p_key, PJ_EINVAL);
-
- pj_lock_acquire(ioqueue->lock);
-
- if (ioqueue->count >= ioqueue->max) {
- rc = PJ_ETOOMANY;
- goto on_return;
- }
-
- /* Set socket to nonblocking. */
- value = 1;
-#ifdef PJ_WIN32
- if (ioctlsocket(sock, FIONBIO, (u_long*)&value)) {
-#else
- if (ioctl(sock, FIONBIO, &value)) {
-#endif
- rc = pj_get_netos_error();
- goto on_return;
- }
-
- /* Create key. */
- key = (pj_ioqueue_key_t*)pj_pool_zalloc(pool, sizeof(pj_ioqueue_key_t));
- rc = ioqueue_init_key(pool, ioqueue, key, sock, user_data, cb);
- if (rc != PJ_SUCCESS) {
- key = NULL;
- goto on_return;
- }
-
- /* Register */
- pj_list_insert_before(&ioqueue->key_list, key);
- ++ioqueue->count;
-
-on_return:
- /* On error, socket may be left in non-blocking mode. */
- *p_key = key;
- pj_lock_release(ioqueue->lock);
-
- return rc;
-}
-
-/*
- * pj_ioqueue_unregister()
- *
- * Unregister handle from ioqueue.
- */
-PJ_DEF(pj_status_t) pj_ioqueue_unregister( pj_ioqueue_key_t *key)
-{
- pj_ioqueue_t *ioqueue;
-
- PJ_ASSERT_RETURN(key, PJ_EINVAL);
-
- ioqueue = key->ioqueue;
-
- pj_lock_acquire(ioqueue->lock);
-
- pj_assert(ioqueue->count > 0);
- --ioqueue->count;
- pj_list_erase(key);
- PJ_FD_CLR(key->fd, &ioqueue->rfdset);
- PJ_FD_CLR(key->fd, &ioqueue->wfdset);
-#if PJ_HAS_TCP
- PJ_FD_CLR(key->fd, &ioqueue->xfdset);
-#endif
-
- /* ioqueue_destroy may try to acquire key's mutex.
- * Since normally the order of locking is to lock key's mutex first
- * then ioqueue's mutex, ioqueue_destroy may deadlock unless we
- * release ioqueue's mutex first.
- */
- pj_lock_release(ioqueue->lock);
-
- /* Destroy the key. */
- ioqueue_destroy_key(key);
-
- return PJ_SUCCESS;
-}
-
-
-/* This supposed to check whether the fd_set values are consistent
- * with the operation currently set in each key.
- */
-#if VALIDATE_FD_SET
-static void validate_sets(const pj_ioqueue_t *ioqueue,
- const pj_fd_set_t *rfdset,
- const pj_fd_set_t *wfdset,
- const pj_fd_set_t *xfdset)
-{
- pj_ioqueue_key_t *key;
-
- /*
- * This basicly would not work anymore.
- * We need to lock key before performing the check, but we can't do
- * so because we're holding ioqueue mutex. If we acquire key's mutex
- * now, the will cause deadlock.
- */
- pj_assert(0);
-
- key = ioqueue->key_list.next;
- while (key != &ioqueue->key_list) {
- if (!pj_list_empty(&key->read_list)
-#if defined(PJ_HAS_TCP) && PJ_HAS_TCP != 0
- || !pj_list_empty(&key->accept_list)
-#endif
- )
- {
- pj_assert(PJ_FD_ISSET(key->fd, rfdset));
- }
- else {
- pj_assert(PJ_FD_ISSET(key->fd, rfdset) == 0);
- }
- if (!pj_list_empty(&key->write_list)
-#if defined(PJ_HAS_TCP) && PJ_HAS_TCP != 0
- || key->connecting
-#endif
- )
- {
- pj_assert(PJ_FD_ISSET(key->fd, wfdset));
- }
- else {
- pj_assert(PJ_FD_ISSET(key->fd, wfdset) == 0);
- }
-#if defined(PJ_HAS_TCP) && PJ_HAS_TCP != 0
- if (key->connecting)
- {
- pj_assert(PJ_FD_ISSET(key->fd, xfdset));
- }
- else {
- pj_assert(PJ_FD_ISSET(key->fd, xfdset) == 0);
- }
-#endif /* PJ_HAS_TCP */
-
- key = key->next;
- }
-}
-#endif /* VALIDATE_FD_SET */
-
-
-/* ioqueue_remove_from_set()
- * This function is called from ioqueue_dispatch_event() to instruct
- * the ioqueue to remove the specified descriptor from ioqueue's descriptor
- * set for the specified event.
- */
-static void ioqueue_remove_from_set( pj_ioqueue_t *ioqueue,
- pj_sock_t fd,
- enum ioqueue_event_type event_type)
-{
- pj_lock_acquire(ioqueue->lock);
-
- if (event_type == READABLE_EVENT)
- PJ_FD_CLR((pj_sock_t)fd, &ioqueue->rfdset);
- else if (event_type == WRITEABLE_EVENT)
- PJ_FD_CLR((pj_sock_t)fd, &ioqueue->wfdset);
- else if (event_type == EXCEPTION_EVENT)
- PJ_FD_CLR((pj_sock_t)fd, &ioqueue->xfdset);
- else
- pj_assert(0);
-
- pj_lock_release(ioqueue->lock);
-}
-
-/*
- * ioqueue_add_to_set()
- * This function is called from pj_ioqueue_recv(), pj_ioqueue_send() etc
- * to instruct the ioqueue to add the specified handle to ioqueue's descriptor
- * set for the specified event.
- */
-static void ioqueue_add_to_set( pj_ioqueue_t *ioqueue,
- pj_sock_t fd,
- enum ioqueue_event_type event_type )
-{
- pj_lock_acquire(ioqueue->lock);
-
- if (event_type == READABLE_EVENT)
- PJ_FD_SET((pj_sock_t)fd, &ioqueue->rfdset);
- else if (event_type == WRITEABLE_EVENT)
- PJ_FD_SET((pj_sock_t)fd, &ioqueue->wfdset);
- else if (event_type == EXCEPTION_EVENT)
- PJ_FD_SET((pj_sock_t)fd, &ioqueue->xfdset);
- else
- pj_assert(0);
-
- pj_lock_release(ioqueue->lock);
-}
-
-/*
- * pj_ioqueue_poll()
- *
- * Few things worth written:
- *
- * - we used to do only one callback called per poll, but it didn't go
- * very well. The reason is because on some situation, the write
- * callback gets called all the time, thus doesn't give the read
- * callback to get called. This happens, for example, when user
- * submit write operation inside the write callback.
- * As the result, we changed the behaviour so that now multiple
- * callbacks are called in a single poll. It should be fast too,
- * just that we need to be carefull with the ioqueue data structs.
- *
- * - to guarantee preemptiveness etc, the poll function must strictly
- * work on fd_set copy of the ioqueue (not the original one).
- */
-PJ_DEF(int) pj_ioqueue_poll( pj_ioqueue_t *ioqueue, const pj_time_val *timeout)
-{
- pj_fd_set_t rfdset, wfdset, xfdset;
- int count, counter;
- pj_ioqueue_key_t *h;
- struct event
- {
- pj_ioqueue_key_t *key;
- enum ioqueue_event_type event_type;
- } event[PJ_IOQUEUE_MAX_EVENTS_IN_SINGLE_POLL];
-
- PJ_ASSERT_RETURN(ioqueue, PJ_EINVAL);
-
- /* Lock ioqueue before making fd_set copies */
- pj_lock_acquire(ioqueue->lock);
-
- /* We will only do select() when there are sockets to be polled.
- * Otherwise select() will return error.
- */
- if (PJ_FD_COUNT(&ioqueue->rfdset)==0 &&
- PJ_FD_COUNT(&ioqueue->wfdset)==0 &&
- PJ_FD_COUNT(&ioqueue->xfdset)==0)
- {
- pj_lock_release(ioqueue->lock);
- if (timeout)
- pj_thread_sleep(PJ_TIME_VAL_MSEC(*timeout));
- return 0;
- }
-
- /* Copy ioqueue's pj_fd_set_t to local variables. */
- pj_memcpy(&rfdset, &ioqueue->rfdset, sizeof(pj_fd_set_t));
- pj_memcpy(&wfdset, &ioqueue->wfdset, sizeof(pj_fd_set_t));
-#if PJ_HAS_TCP
- pj_memcpy(&xfdset, &ioqueue->xfdset, sizeof(pj_fd_set_t));
-#else
- PJ_FD_ZERO(&xfdset);
-#endif
-
-#if VALIDATE_FD_SET
- validate_sets(ioqueue, &rfdset, &wfdset, &xfdset);
-#endif
-
- /* Unlock ioqueue before select(). */
- pj_lock_release(ioqueue->lock);
-
- count = pj_sock_select(FD_SETSIZE, &rfdset, &wfdset, &xfdset, timeout);
-
- if (count <= 0)
- return count;
- else if (count > PJ_IOQUEUE_MAX_EVENTS_IN_SINGLE_POLL)
- count = PJ_IOQUEUE_MAX_EVENTS_IN_SINGLE_POLL;
-
- /* Scan descriptor sets for event and add the events in the event
- * array to be processed later in this function. We do this so that
- * events can be processed in parallel without holding ioqueue lock.
- */
- pj_lock_acquire(ioqueue->lock);
-
- counter = 0;
-
- /* Scan for writable sockets first to handle piggy-back data
- * coming with accept().
- */
- h = ioqueue->key_list.next;
- for ( ; h!=&ioqueue->key_list && counter<count; h = h->next) {
- if ( (key_has_pending_write(h) || key_has_pending_connect(h))
- && PJ_FD_ISSET(h->fd, &wfdset))
- {
- event[counter].key = h;
- event[counter].event_type = WRITEABLE_EVENT;
- ++counter;
- }
-
- /* Scan for readable socket. */
- if ((key_has_pending_read(h) || key_has_pending_accept(h))
- && PJ_FD_ISSET(h->fd, &rfdset))
- {
- event[counter].key = h;
- event[counter].event_type = READABLE_EVENT;
- ++counter;
- }
-
-#if PJ_HAS_TCP
- if (key_has_pending_connect(h) && PJ_FD_ISSET(h->fd, &xfdset)) {
- event[counter].key = h;
- event[counter].event_type = EXCEPTION_EVENT;
- ++counter;
- }
-#endif
- }
-
- pj_lock_release(ioqueue->lock);
-
- count = counter;
-
- /* Now process all events. The dispatch functions will take care
- * of locking in each of the key
- */
- for (counter=0; counter<count; ++counter) {
- switch (event[counter].event_type) {
- case READABLE_EVENT:
- ioqueue_dispatch_read_event(ioqueue, event[counter].key);
- break;
- case WRITEABLE_EVENT:
- ioqueue_dispatch_write_event(ioqueue, event[counter].key);
- break;
- case EXCEPTION_EVENT:
- ioqueue_dispatch_exception_event(ioqueue, event[counter].key);
- break;
- case NO_EVENT:
- pj_assert(!"Invalid event!");
- break;
- }
- }
-
- return count;
-}
-
+/* $Id$
+ */
+/*
+ * PJLIB - PJ Foundation Library
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ * Benny Prijono <bennylp@bulukucing.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+/*
+ * sock_select.c
+ *
+ * This is the implementation of IOQueue using pj_sock_select().
+ * It runs anywhere where pj_sock_select() is available (currently
+ * Win32, Linux, Linux kernel, etc.).
+ */
+
+#include <pj/ioqueue.h>
+#include <pj/os.h>
+#include <pj/lock.h>
+#include <pj/log.h>
+#include <pj/list.h>
+#include <pj/pool.h>
+#include <pj/string.h>
+#include <pj/assert.h>
+#include <pj/sock.h>
+#include <pj/compat/socket.h>
+#include <pj/sock_select.h>
+#include <pj/errno.h>
+
+/*
+ * Include declaration from common abstraction.
+ */
+#include "ioqueue_common_abs.h"
+
+/*
+ * ISSUES with ioqueue_select()
+ *
+ * EAGAIN/EWOULDBLOCK error in recv():
+ * - when multiple threads are working with the ioqueue, application
+ * may receive EAGAIN or EWOULDBLOCK in the receive callback.
+ * This error happens because more than one thread is watching for
+ * the same descriptor set, so when all of them call recv() or recvfrom()
+ * simultaneously, only one will succeed and the rest will get the error.
+ *
+ */
+#define THIS_FILE "ioq_select"
+
+/*
+ * The select ioqueue relies on socket functions (pj_sock_xxx()) to return
+ * the correct error code.
+ */
+#if PJ_RETURN_OS_ERROR(100) != PJ_STATUS_FROM_OS(100)
+# error "Error reporting must be enabled for this function to work!"
+#endif
+
+/**
+ * Get the number of descriptors in the set. This is defined in sock_select.c
+ * This function will only return the number of sockets set from PJ_FD_SET
+ * operation. When the set is modified by other means (such as by select()),
+ * the count will not be reflected here.
+ *
+ * That's why don't export this function in the header file, to avoid
+ * misunderstanding.
+ *
+ * @param fdsetp The descriptor set.
+ *
+ * @return Number of descriptors in the set.
+ */
+PJ_DECL(pj_size_t) PJ_FD_COUNT(const pj_fd_set_t *fdsetp);
+
+
+/*
+ * During debugging build, VALIDATE_FD_SET is set.
+ * This will check the validity of the fd_sets.
+ */
+/*
+#if defined(PJ_DEBUG) && PJ_DEBUG != 0
+# define VALIDATE_FD_SET 1
+#else
+# define VALIDATE_FD_SET 0
+#endif
+*/
+#define VALIDATE_FD_SET 0
+
+/*
+ * This describes each key.
+ */
+struct pj_ioqueue_key_t
+{
+ DECLARE_COMMON_KEY
+};
+
+/*
+ * This describes the I/O queue itself.
+ */
+struct pj_ioqueue_t
+{
+ DECLARE_COMMON_IOQUEUE
+
+ unsigned max, count;
+ pj_ioqueue_key_t key_list;
+ pj_fd_set_t rfdset;
+ pj_fd_set_t wfdset;
+#if PJ_HAS_TCP
+ pj_fd_set_t xfdset;
+#endif
+};
+
+/* Include implementation for common abstraction after we declare
+ * pj_ioqueue_key_t and pj_ioqueue_t.
+ */
+#include "ioqueue_common_abs.c"
+
+/*
+ * pj_ioqueue_name()
+ */
+PJ_DEF(const char*) pj_ioqueue_name(void)
+{
+ return "select";
+}
+
+/*
+ * pj_ioqueue_create()
+ *
+ * Create select ioqueue.
+ */
+PJ_DEF(pj_status_t) pj_ioqueue_create( pj_pool_t *pool,
+ pj_size_t max_fd,
+ pj_ioqueue_t **p_ioqueue)
+{
+ pj_ioqueue_t *ioqueue;
+ pj_lock_t *lock;
+ pj_status_t rc;
+
+ /* Check that arguments are valid. */
+ PJ_ASSERT_RETURN(pool != NULL && p_ioqueue != NULL &&
+ max_fd > 0 && max_fd <= PJ_IOQUEUE_MAX_HANDLES,
+ PJ_EINVAL);
+
+ /* Check that size of pj_ioqueue_op_key_t is sufficient */
+ PJ_ASSERT_RETURN(sizeof(pj_ioqueue_op_key_t)-sizeof(void*) >=
+ sizeof(union operation_key), PJ_EBUG);
+
+ ioqueue = pj_pool_alloc(pool, sizeof(pj_ioqueue_t));
+
+ ioqueue_init(ioqueue);
+
+ ioqueue->max = max_fd;
+ ioqueue->count = 0;
+ PJ_FD_ZERO(&ioqueue->rfdset);
+ PJ_FD_ZERO(&ioqueue->wfdset);
+#if PJ_HAS_TCP
+ PJ_FD_ZERO(&ioqueue->xfdset);
+#endif
+ pj_list_init(&ioqueue->key_list);
+
+ rc = pj_lock_create_simple_mutex(pool, "ioq%p", &lock);
+ if (rc != PJ_SUCCESS)
+ return rc;
+
+ rc = pj_ioqueue_set_lock(ioqueue, lock, PJ_TRUE);
+ if (rc != PJ_SUCCESS)
+ return rc;
+
+ PJ_LOG(4, ("pjlib", "select() I/O Queue created (%p)", ioqueue));
+
+ *p_ioqueue = ioqueue;
+ return PJ_SUCCESS;
+}
+
+/*
+ * pj_ioqueue_destroy()
+ *
+ * Destroy ioqueue.
+ */
+PJ_DEF(pj_status_t) pj_ioqueue_destroy(pj_ioqueue_t *ioqueue)
+{
+ PJ_ASSERT_RETURN(ioqueue, PJ_EINVAL);
+
+ pj_lock_acquire(ioqueue->lock);
+ return ioqueue_destroy(ioqueue);
+}
+
+
+/*
+ * pj_ioqueue_register_sock()
+ *
+ * Register a handle to ioqueue.
+ */
+PJ_DEF(pj_status_t) pj_ioqueue_register_sock( pj_pool_t *pool,
+ pj_ioqueue_t *ioqueue,
+ pj_sock_t sock,
+ void *user_data,
+ const pj_ioqueue_callback *cb,
+ pj_ioqueue_key_t **p_key)
+{
+ pj_ioqueue_key_t *key = NULL;
+ pj_uint32_t value;
+ pj_status_t rc = PJ_SUCCESS;
+
+ PJ_ASSERT_RETURN(pool && ioqueue && sock != PJ_INVALID_SOCKET &&
+ cb && p_key, PJ_EINVAL);
+
+ pj_lock_acquire(ioqueue->lock);
+
+ if (ioqueue->count >= ioqueue->max) {
+ rc = PJ_ETOOMANY;
+ goto on_return;
+ }
+
+ /* Set socket to nonblocking. */
+ value = 1;
+#ifdef PJ_WIN32
+ if (ioctlsocket(sock, FIONBIO, (u_long*)&value)) {
+#else
+ if (ioctl(sock, FIONBIO, &value)) {
+#endif
+ rc = pj_get_netos_error();
+ goto on_return;
+ }
+
+ /* Create key. */
+ key = (pj_ioqueue_key_t*)pj_pool_zalloc(pool, sizeof(pj_ioqueue_key_t));
+ rc = ioqueue_init_key(pool, ioqueue, key, sock, user_data, cb);
+ if (rc != PJ_SUCCESS) {
+ key = NULL;
+ goto on_return;
+ }
+
+ /* Register */
+ pj_list_insert_before(&ioqueue->key_list, key);
+ ++ioqueue->count;
+
+on_return:
+ /* On error, socket may be left in non-blocking mode. */
+ *p_key = key;
+ pj_lock_release(ioqueue->lock);
+
+ return rc;
+}
+
+/*
+ * pj_ioqueue_unregister()
+ *
+ * Unregister handle from ioqueue.
+ */
+PJ_DEF(pj_status_t) pj_ioqueue_unregister( pj_ioqueue_key_t *key)
+{
+ pj_ioqueue_t *ioqueue;
+
+ PJ_ASSERT_RETURN(key, PJ_EINVAL);
+
+ ioqueue = key->ioqueue;
+
+ pj_lock_acquire(ioqueue->lock);
+
+ pj_assert(ioqueue->count > 0);
+ --ioqueue->count;
+ pj_list_erase(key);
+ PJ_FD_CLR(key->fd, &ioqueue->rfdset);
+ PJ_FD_CLR(key->fd, &ioqueue->wfdset);
+#if PJ_HAS_TCP
+ PJ_FD_CLR(key->fd, &ioqueue->xfdset);
+#endif
+
+ /* ioqueue_destroy may try to acquire key's mutex.
+ * Since normally the order of locking is to lock key's mutex first
+ * then ioqueue's mutex, ioqueue_destroy may deadlock unless we
+ * release ioqueue's mutex first.
+ */
+ pj_lock_release(ioqueue->lock);
+
+ /* Destroy the key. */
+ ioqueue_destroy_key(key);
+
+ return PJ_SUCCESS;
+}
+
+
+/* This supposed to check whether the fd_set values are consistent
+ * with the operation currently set in each key.
+ */
+#if VALIDATE_FD_SET
+static void validate_sets(const pj_ioqueue_t *ioqueue,
+ const pj_fd_set_t *rfdset,
+ const pj_fd_set_t *wfdset,
+ const pj_fd_set_t *xfdset)
+{
+ pj_ioqueue_key_t *key;
+
+ /*
+ * This basicly would not work anymore.
+ * We need to lock key before performing the check, but we can't do
+ * so because we're holding ioqueue mutex. If we acquire key's mutex
+ * now, the will cause deadlock.
+ */
+ pj_assert(0);
+
+ key = ioqueue->key_list.next;
+ while (key != &ioqueue->key_list) {
+ if (!pj_list_empty(&key->read_list)
+#if defined(PJ_HAS_TCP) && PJ_HAS_TCP != 0
+ || !pj_list_empty(&key->accept_list)
+#endif
+ )
+ {
+ pj_assert(PJ_FD_ISSET(key->fd, rfdset));
+ }
+ else {
+ pj_assert(PJ_FD_ISSET(key->fd, rfdset) == 0);
+ }
+ if (!pj_list_empty(&key->write_list)
+#if defined(PJ_HAS_TCP) && PJ_HAS_TCP != 0
+ || key->connecting
+#endif
+ )
+ {
+ pj_assert(PJ_FD_ISSET(key->fd, wfdset));
+ }
+ else {
+ pj_assert(PJ_FD_ISSET(key->fd, wfdset) == 0);
+ }
+#if defined(PJ_HAS_TCP) && PJ_HAS_TCP != 0
+ if (key->connecting)
+ {
+ pj_assert(PJ_FD_ISSET(key->fd, xfdset));
+ }
+ else {
+ pj_assert(PJ_FD_ISSET(key->fd, xfdset) == 0);
+ }
+#endif /* PJ_HAS_TCP */
+
+ key = key->next;
+ }
+}
+#endif /* VALIDATE_FD_SET */
+
+
+/* ioqueue_remove_from_set()
+ * This function is called from ioqueue_dispatch_event() to instruct
+ * the ioqueue to remove the specified descriptor from ioqueue's descriptor
+ * set for the specified event.
+ */
+static void ioqueue_remove_from_set( pj_ioqueue_t *ioqueue,
+ pj_sock_t fd,
+ enum ioqueue_event_type event_type)
+{
+ pj_lock_acquire(ioqueue->lock);
+
+ if (event_type == READABLE_EVENT)
+ PJ_FD_CLR((pj_sock_t)fd, &ioqueue->rfdset);
+ else if (event_type == WRITEABLE_EVENT)
+ PJ_FD_CLR((pj_sock_t)fd, &ioqueue->wfdset);
+ else if (event_type == EXCEPTION_EVENT)
+ PJ_FD_CLR((pj_sock_t)fd, &ioqueue->xfdset);
+ else
+ pj_assert(0);
+
+ pj_lock_release(ioqueue->lock);
+}
+
+/*
+ * ioqueue_add_to_set()
+ * This function is called from pj_ioqueue_recv(), pj_ioqueue_send() etc
+ * to instruct the ioqueue to add the specified handle to ioqueue's descriptor
+ * set for the specified event.
+ */
+static void ioqueue_add_to_set( pj_ioqueue_t *ioqueue,
+ pj_sock_t fd,
+ enum ioqueue_event_type event_type )
+{
+ pj_lock_acquire(ioqueue->lock);
+
+ if (event_type == READABLE_EVENT)
+ PJ_FD_SET((pj_sock_t)fd, &ioqueue->rfdset);
+ else if (event_type == WRITEABLE_EVENT)
+ PJ_FD_SET((pj_sock_t)fd, &ioqueue->wfdset);
+ else if (event_type == EXCEPTION_EVENT)
+ PJ_FD_SET((pj_sock_t)fd, &ioqueue->xfdset);
+ else
+ pj_assert(0);
+
+ pj_lock_release(ioqueue->lock);
+}
+
+/*
+ * pj_ioqueue_poll()
+ *
+ * Few things worth written:
+ *
+ * - we used to do only one callback called per poll, but it didn't go
+ * very well. The reason is because on some situation, the write
+ * callback gets called all the time, thus doesn't give the read
+ * callback to get called. This happens, for example, when user
+ * submit write operation inside the write callback.
+ * As the result, we changed the behaviour so that now multiple
+ * callbacks are called in a single poll. It should be fast too,
+ * just that we need to be carefull with the ioqueue data structs.
+ *
+ * - to guarantee preemptiveness etc, the poll function must strictly
+ * work on fd_set copy of the ioqueue (not the original one).
+ */
+PJ_DEF(int) pj_ioqueue_poll( pj_ioqueue_t *ioqueue, const pj_time_val *timeout)
+{
+ pj_fd_set_t rfdset, wfdset, xfdset;
+ int count, counter;
+ pj_ioqueue_key_t *h;
+ struct event
+ {
+ pj_ioqueue_key_t *key;
+ enum ioqueue_event_type event_type;
+ } event[PJ_IOQUEUE_MAX_EVENTS_IN_SINGLE_POLL];
+
+ PJ_ASSERT_RETURN(ioqueue, PJ_EINVAL);
+
+ /* Lock ioqueue before making fd_set copies */
+ pj_lock_acquire(ioqueue->lock);
+
+ /* We will only do select() when there are sockets to be polled.
+ * Otherwise select() will return error.
+ */
+ if (PJ_FD_COUNT(&ioqueue->rfdset)==0 &&
+ PJ_FD_COUNT(&ioqueue->wfdset)==0 &&
+ PJ_FD_COUNT(&ioqueue->xfdset)==0)
+ {
+ pj_lock_release(ioqueue->lock);
+ if (timeout)
+ pj_thread_sleep(PJ_TIME_VAL_MSEC(*timeout));
+ return 0;
+ }
+
+ /* Copy ioqueue's pj_fd_set_t to local variables. */
+ pj_memcpy(&rfdset, &ioqueue->rfdset, sizeof(pj_fd_set_t));
+ pj_memcpy(&wfdset, &ioqueue->wfdset, sizeof(pj_fd_set_t));
+#if PJ_HAS_TCP
+ pj_memcpy(&xfdset, &ioqueue->xfdset, sizeof(pj_fd_set_t));
+#else
+ PJ_FD_ZERO(&xfdset);
+#endif
+
+#if VALIDATE_FD_SET
+ validate_sets(ioqueue, &rfdset, &wfdset, &xfdset);
+#endif
+
+ /* Unlock ioqueue before select(). */
+ pj_lock_release(ioqueue->lock);
+
+ count = pj_sock_select(FD_SETSIZE, &rfdset, &wfdset, &xfdset, timeout);
+
+ if (count <= 0)
+ return count;
+ else if (count > PJ_IOQUEUE_MAX_EVENTS_IN_SINGLE_POLL)
+ count = PJ_IOQUEUE_MAX_EVENTS_IN_SINGLE_POLL;
+
+ /* Scan descriptor sets for event and add the events in the event
+ * array to be processed later in this function. We do this so that
+ * events can be processed in parallel without holding ioqueue lock.
+ */
+ pj_lock_acquire(ioqueue->lock);
+
+ counter = 0;
+
+ /* Scan for writable sockets first to handle piggy-back data
+ * coming with accept().
+ */
+ h = ioqueue->key_list.next;
+ for ( ; h!=&ioqueue->key_list && counter<count; h = h->next) {
+ if ( (key_has_pending_write(h) || key_has_pending_connect(h))
+ && PJ_FD_ISSET(h->fd, &wfdset))
+ {
+ event[counter].key = h;
+ event[counter].event_type = WRITEABLE_EVENT;
+ ++counter;
+ }
+
+ /* Scan for readable socket. */
+ if ((key_has_pending_read(h) || key_has_pending_accept(h))
+ && PJ_FD_ISSET(h->fd, &rfdset))
+ {
+ event[counter].key = h;
+ event[counter].event_type = READABLE_EVENT;
+ ++counter;
+ }
+
+#if PJ_HAS_TCP
+ if (key_has_pending_connect(h) && PJ_FD_ISSET(h->fd, &xfdset)) {
+ event[counter].key = h;
+ event[counter].event_type = EXCEPTION_EVENT;
+ ++counter;
+ }
+#endif
+ }
+
+ pj_lock_release(ioqueue->lock);
+
+ count = counter;
+
+ /* Now process all events. The dispatch functions will take care
+ * of locking in each of the key
+ */
+ for (counter=0; counter<count; ++counter) {
+ switch (event[counter].event_type) {
+ case READABLE_EVENT:
+ ioqueue_dispatch_read_event(ioqueue, event[counter].key);
+ break;
+ case WRITEABLE_EVENT:
+ ioqueue_dispatch_write_event(ioqueue, event[counter].key);
+ break;
+ case EXCEPTION_EVENT:
+ ioqueue_dispatch_exception_event(ioqueue, event[counter].key);
+ break;
+ case NO_EVENT:
+ pj_assert(!"Invalid event!");
+ break;
+ }
+ }
+
+ return count;
+}
+
diff --git a/pjlib/src/pj/ioqueue_winnt.c b/pjlib/src/pj/ioqueue_winnt.c
index 777cb6b..7e3e31b 100644
--- a/pjlib/src/pj/ioqueue_winnt.c
+++ b/pjlib/src/pj/ioqueue_winnt.c
@@ -1,896 +1,917 @@
-/* $Id$
- */
-#include <pj/ioqueue.h>
-#include <pj/os.h>
-#include <pj/lock.h>
-#include <pj/pool.h>
-#include <pj/string.h>
-#include <pj/sock.h>
-#include <pj/array.h>
-#include <pj/log.h>
-#include <pj/assert.h>
-#include <pj/errno.h>
-
-
-#if defined(PJ_HAS_WINSOCK2_H) && PJ_HAS_WINSOCK2_H != 0
-# include <winsock2.h>
-#elif defined(PJ_HAS_WINSOCK_H) && PJ_HAS_WINSOCK_H != 0
-# include <winsock.h>
-#endif
-
-#if defined(PJ_HAS_MSWSOCK_H) && PJ_HAS_MSWSOCK_H != 0
-# include <mswsock.h>
-#endif
-
-
-/* The address specified in AcceptEx() must be 16 more than the size of
- * SOCKADDR (source: MSDN).
- */
-#define ACCEPT_ADDR_LEN (sizeof(pj_sockaddr_in)+16)
-
-typedef struct generic_overlapped
-{
- WSAOVERLAPPED overlapped;
- pj_ioqueue_operation_e operation;
-} generic_overlapped;
-
-/*
- * OVERLAPPPED structure for send and receive.
- */
-typedef struct ioqueue_overlapped
-{
- WSAOVERLAPPED overlapped;
- pj_ioqueue_operation_e operation;
- WSABUF wsabuf;
- pj_sockaddr_in dummy_addr;
- int dummy_addrlen;
-} ioqueue_overlapped;
-
-#if PJ_HAS_TCP
-/*
- * OVERLAP structure for accept.
- */
-typedef struct ioqueue_accept_rec
-{
- WSAOVERLAPPED overlapped;
- pj_ioqueue_operation_e operation;
- pj_sock_t newsock;
- pj_sock_t *newsock_ptr;
- int *addrlen;
- void *remote;
- void *local;
- char accept_buf[2 * ACCEPT_ADDR_LEN];
-} ioqueue_accept_rec;
-#endif
-
-/*
- * Structure to hold pending operation key.
- */
-union operation_key
-{
- generic_overlapped generic;
- ioqueue_overlapped overlapped;
-#if PJ_HAS_TCP
- ioqueue_accept_rec accept;
-#endif
-};
-
-/* Type of handle in the key. */
-enum handle_type
-{
- HND_IS_UNKNOWN,
- HND_IS_FILE,
- HND_IS_SOCKET,
-};
-
-/*
- * Structure for individual socket.
- */
-struct pj_ioqueue_key_t
-{
- pj_ioqueue_t *ioqueue;
- HANDLE hnd;
- void *user_data;
- enum handle_type hnd_type;
-#if PJ_HAS_TCP
- int connecting;
-#endif
- pj_ioqueue_callback cb;
-};
-
-/*
- * IO Queue structure.
- */
-struct pj_ioqueue_t
-{
- HANDLE iocp;
- pj_lock_t *lock;
- pj_bool_t auto_delete_lock;
- unsigned event_count;
- HANDLE event_pool[MAXIMUM_WAIT_OBJECTS+1];
-#if PJ_HAS_TCP
- unsigned connecting_count;
- HANDLE connecting_handles[MAXIMUM_WAIT_OBJECTS+1];
- pj_ioqueue_key_t *connecting_keys[MAXIMUM_WAIT_OBJECTS+1];
-#endif
-};
-
-
-#if PJ_HAS_TCP
-/*
- * Process the socket when the overlapped accept() completed.
- */
-static void ioqueue_on_accept_complete(ioqueue_accept_rec *accept_overlapped)
-{
- struct sockaddr *local;
- struct sockaddr *remote;
- int locallen, remotelen;
-
- PJ_CHECK_STACK();
-
- /* Operation complete immediately. */
- GetAcceptExSockaddrs( accept_overlapped->accept_buf,
- 0,
- ACCEPT_ADDR_LEN,
- ACCEPT_ADDR_LEN,
- &local,
- &locallen,
- &remote,
- &remotelen);
- if (*accept_overlapped->addrlen > locallen) {
- pj_memcpy(accept_overlapped->local, local, locallen);
- pj_memcpy(accept_overlapped->remote, remote, locallen);
- } else {
- pj_memset(accept_overlapped->local, 0, *accept_overlapped->addrlen);
- pj_memset(accept_overlapped->remote, 0, *accept_overlapped->addrlen);
- }
- *accept_overlapped->addrlen = locallen;
- if (accept_overlapped->newsock_ptr)
- *accept_overlapped->newsock_ptr = accept_overlapped->newsock;
- accept_overlapped->operation = 0;
- accept_overlapped->newsock = PJ_INVALID_SOCKET;
-}
-
-static void erase_connecting_socket( pj_ioqueue_t *ioqueue, unsigned pos)
-{
- pj_ioqueue_key_t *key = ioqueue->connecting_keys[pos];
- HANDLE hEvent = ioqueue->connecting_handles[pos];
-
- /* Remove key from array of connecting handles. */
- pj_array_erase(ioqueue->connecting_keys, sizeof(key),
- ioqueue->connecting_count, pos);
- pj_array_erase(ioqueue->connecting_handles, sizeof(HANDLE),
- ioqueue->connecting_count, pos);
- --ioqueue->connecting_count;
-
- /* Disassociate the socket from the event. */
- WSAEventSelect((pj_sock_t)key->hnd, hEvent, 0);
-
- /* Put event object to pool. */
- if (ioqueue->event_count < MAXIMUM_WAIT_OBJECTS) {
- ioqueue->event_pool[ioqueue->event_count++] = hEvent;
- } else {
- /* Shouldn't happen. There should be no more pending connections
- * than max.
- */
- pj_assert(0);
- CloseHandle(hEvent);
- }
-
-}
-
-/*
- * Poll for the completion of non-blocking connect().
- * If there's a completion, the function return the key of the completed
- * socket, and 'result' argument contains the connect() result. If connect()
- * succeeded, 'result' will have value zero, otherwise will have the error
- * code.
- */
-static pj_ioqueue_key_t *check_connecting( pj_ioqueue_t *ioqueue,
- pj_ssize_t *connect_err )
-{
- pj_ioqueue_key_t *key = NULL;
-
- if (ioqueue->connecting_count) {
- DWORD result;
-
- pj_lock_acquire(ioqueue->lock);
- result = WaitForMultipleObjects(ioqueue->connecting_count,
- ioqueue->connecting_handles,
- FALSE, 0);
- if (result >= WAIT_OBJECT_0 &&
- result < WAIT_OBJECT_0+ioqueue->connecting_count)
- {
- WSANETWORKEVENTS net_events;
-
- /* Got completed connect(). */
- unsigned pos = result - WAIT_OBJECT_0;
- key = ioqueue->connecting_keys[pos];
-
- /* See whether connect has succeeded. */
- WSAEnumNetworkEvents((pj_sock_t)key->hnd,
- ioqueue->connecting_handles[pos],
- &net_events);
- *connect_err =
- PJ_STATUS_FROM_OS(net_events.iErrorCode[FD_CONNECT_BIT]);
-
- /* Erase socket from pending connect. */
- erase_connecting_socket(ioqueue, pos);
- }
- pj_lock_release(ioqueue->lock);
- }
- return key;
-}
-#endif
-
-/*
- * pj_ioqueue_name()
- */
-PJ_DEF(const char*) pj_ioqueue_name(void)
-{
- return "iocp";
-}
-
-/*
- * pj_ioqueue_create()
- */
-PJ_DEF(pj_status_t) pj_ioqueue_create( pj_pool_t *pool,
- pj_size_t max_fd,
- pj_ioqueue_t **p_ioqueue)
-{
- pj_ioqueue_t *ioqueue;
- pj_status_t rc;
-
- PJ_UNUSED_ARG(max_fd);
- PJ_ASSERT_RETURN(pool && p_ioqueue, PJ_EINVAL);
-
- rc = sizeof(union operation_key);
-
- /* Check that sizeof(pj_ioqueue_op_key_t) makes sense. */
- PJ_ASSERT_RETURN(sizeof(pj_ioqueue_op_key_t)-sizeof(void*) >=
- sizeof(union operation_key), PJ_EBUG);
-
- ioqueue = pj_pool_zalloc(pool, sizeof(*ioqueue));
- ioqueue->iocp = CreateIoCompletionPort(INVALID_HANDLE_VALUE, NULL, 0, 0);
- if (ioqueue->iocp == NULL)
- return PJ_RETURN_OS_ERROR(GetLastError());
-
- rc = pj_lock_create_simple_mutex(pool, NULL, &ioqueue->lock);
- if (rc != PJ_SUCCESS) {
- CloseHandle(ioqueue->iocp);
- return rc;
- }
-
- ioqueue->auto_delete_lock = PJ_TRUE;
-
- *p_ioqueue = ioqueue;
-
- PJ_LOG(4, ("pjlib", "WinNT IOCP I/O Queue created (%p)", ioqueue));
- return PJ_SUCCESS;
-}
-
-/*
- * pj_ioqueue_destroy()
- */
-PJ_DEF(pj_status_t) pj_ioqueue_destroy( pj_ioqueue_t *ioqueue )
-{
- unsigned i;
-
- PJ_CHECK_STACK();
- PJ_ASSERT_RETURN(ioqueue, PJ_EINVAL);
-
- /* Destroy events in the pool */
- for (i=0; i<ioqueue->event_count; ++i) {
- CloseHandle(ioqueue->event_pool[i]);
- }
- ioqueue->event_count = 0;
-
- if (CloseHandle(ioqueue->iocp) != TRUE)
- return PJ_RETURN_OS_ERROR(GetLastError());
-
- if (ioqueue->auto_delete_lock)
- pj_lock_destroy(ioqueue->lock);
-
- return PJ_SUCCESS;
-}
-
-/*
- * pj_ioqueue_set_lock()
- */
-PJ_DEF(pj_status_t) pj_ioqueue_set_lock( pj_ioqueue_t *ioqueue,
- pj_lock_t *lock,
- pj_bool_t auto_delete )
-{
- PJ_ASSERT_RETURN(ioqueue && lock, PJ_EINVAL);
-
- if (ioqueue->auto_delete_lock) {
- pj_lock_destroy(ioqueue->lock);
- }
-
- ioqueue->lock = lock;
- ioqueue->auto_delete_lock = auto_delete;
-
- return PJ_SUCCESS;
-}
-
-/*
- * pj_ioqueue_register_sock()
- */
-PJ_DEF(pj_status_t) pj_ioqueue_register_sock( pj_pool_t *pool,
- pj_ioqueue_t *ioqueue,
- pj_sock_t sock,
- void *user_data,
- const pj_ioqueue_callback *cb,
- pj_ioqueue_key_t **key )
-{
- HANDLE hioq;
- pj_ioqueue_key_t *rec;
- u_long value;
- int rc;
-
- PJ_ASSERT_RETURN(pool && ioqueue && cb && key, PJ_EINVAL);
-
- /* Build the key for this socket. */
- rec = pj_pool_zalloc(pool, sizeof(pj_ioqueue_key_t));
- rec->ioqueue = ioqueue;
- rec->hnd = (HANDLE)sock;
- rec->hnd_type = HND_IS_SOCKET;
- rec->user_data = user_data;
- pj_memcpy(&rec->cb, cb, sizeof(pj_ioqueue_callback));
-
- /* Set socket to nonblocking. */
- value = 1;
- rc = ioctlsocket(sock, FIONBIO, &value);
- if (rc != 0) {
- return PJ_RETURN_OS_ERROR(WSAGetLastError());
- }
-
- /* Associate with IOCP */
- hioq = CreateIoCompletionPort((HANDLE)sock, ioqueue->iocp, (DWORD)rec, 0);
- if (!hioq) {
- return PJ_RETURN_OS_ERROR(GetLastError());
- }
-
- *key = rec;
- return PJ_SUCCESS;
-}
-
-/*
- * pj_ioqueue_unregister()
- */
-PJ_DEF(pj_status_t) pj_ioqueue_unregister( pj_ioqueue_key_t *key )
-{
- PJ_ASSERT_RETURN(key, PJ_EINVAL);
-
-#if PJ_HAS_TCP
- if (key->connecting) {
- unsigned pos;
- pj_ioqueue_t *ioqueue;
-
- ioqueue = key->ioqueue;
-
- /* Erase from connecting_handles */
- pj_lock_acquire(ioqueue->lock);
- for (pos=0; pos < ioqueue->connecting_count; ++pos) {
- if (ioqueue->connecting_keys[pos] == key) {
- erase_connecting_socket(ioqueue, pos);
- break;
- }
- }
- key->connecting = 0;
- pj_lock_release(ioqueue->lock);
- }
-#endif
- if (key->hnd_type == HND_IS_FILE) {
- CloseHandle(key->hnd);
- }
- return PJ_SUCCESS;
-}
-
-/*
- * pj_ioqueue_get_user_data()
- */
-PJ_DEF(void*) pj_ioqueue_get_user_data( pj_ioqueue_key_t *key )
-{
- PJ_ASSERT_RETURN(key, NULL);
- return key->user_data;
-}
-
-/*
- * pj_ioqueue_set_user_data()
- */
-PJ_DEF(pj_status_t) pj_ioqueue_set_user_data( pj_ioqueue_key_t *key,
- void *user_data,
- void **old_data )
-{
- PJ_ASSERT_RETURN(key, PJ_EINVAL);
-
- if (old_data)
- *old_data = key->user_data;
-
- key->user_data = user_data;
- return PJ_SUCCESS;
-}
-
-/*
- * pj_ioqueue_poll()
- *
- * Poll for events.
- */
-PJ_DEF(int) pj_ioqueue_poll( pj_ioqueue_t *ioqueue, const pj_time_val *timeout)
-{
- DWORD dwMsec, dwBytesTransfered, dwKey;
- generic_overlapped *pOv;
- pj_ioqueue_key_t *key;
- pj_ssize_t size_status;
- BOOL rc;
-
- PJ_ASSERT_RETURN(ioqueue, -PJ_EINVAL);
-
- /* Check the connecting array. */
-#if PJ_HAS_TCP
- key = check_connecting(ioqueue, &size_status);
- if (key != NULL) {
- key->cb.on_connect_complete(key, (int)size_status);
- return 1;
- }
-#endif
-
- /* Calculate miliseconds timeout for GetQueuedCompletionStatus */
- dwMsec = timeout ? timeout->sec*1000 + timeout->msec : INFINITE;
-
- /* Poll for completion status. */
- rc = GetQueuedCompletionStatus(ioqueue->iocp, &dwBytesTransfered, &dwKey,
- (OVERLAPPED**)&pOv, dwMsec);
-
- /* The return value is:
- * - nonzero if event was dequeued.
- * - zero and pOv==NULL if no event was dequeued.
- * - zero and pOv!=NULL if event for failed I/O was dequeued.
- */
- if (pOv) {
- /* Event was dequeued for either successfull or failed I/O */
- key = (pj_ioqueue_key_t*)dwKey;
- size_status = dwBytesTransfered;
- switch (pOv->operation) {
- case PJ_IOQUEUE_OP_READ:
- case PJ_IOQUEUE_OP_RECV:
- case PJ_IOQUEUE_OP_RECV_FROM:
- pOv->operation = 0;
- if (key->cb.on_read_complete)
- key->cb.on_read_complete(key, (pj_ioqueue_op_key_t*)pOv,
- size_status);
- break;
- case PJ_IOQUEUE_OP_WRITE:
- case PJ_IOQUEUE_OP_SEND:
- case PJ_IOQUEUE_OP_SEND_TO:
- pOv->operation = 0;
- if (key->cb.on_write_complete)
- key->cb.on_write_complete(key, (pj_ioqueue_op_key_t*)pOv,
- size_status);
- break;
-#if PJ_HAS_TCP
- case PJ_IOQUEUE_OP_ACCEPT:
- /* special case for accept. */
- ioqueue_on_accept_complete((ioqueue_accept_rec*)pOv);
- if (key->cb.on_accept_complete) {
- ioqueue_accept_rec *accept_rec = (ioqueue_accept_rec*)pOv;
- key->cb.on_accept_complete(key,
- (pj_ioqueue_op_key_t*)pOv,
- accept_rec->newsock,
- PJ_SUCCESS);
- }
- break;
- case PJ_IOQUEUE_OP_CONNECT:
-#endif
- case PJ_IOQUEUE_OP_NONE:
- pj_assert(0);
- break;
- }
- return 1;
- }
-
- if (GetLastError()==WAIT_TIMEOUT) {
- /* Check the connecting array (again). */
-#if PJ_HAS_TCP
- key = check_connecting(ioqueue, &size_status);
- if (key != NULL) {
- key->cb.on_connect_complete(key, (int)size_status);
- return 1;
- }
-#endif
- return 0;
- }
- return -1;
-}
-
-/*
- * pj_ioqueue_recv()
- *
- * Initiate overlapped WSARecv() operation.
- */
-PJ_DEF(pj_status_t) pj_ioqueue_recv( pj_ioqueue_key_t *key,
- pj_ioqueue_op_key_t *op_key,
- void *buffer,
- pj_ssize_t *length,
- unsigned flags )
-{
- /*
- * Ideally we should just call pj_ioqueue_recvfrom() with NULL addr and
- * addrlen here. But unfortunately it generates EINVAL... :-(
- * -bennylp
- */
- int rc;
- DWORD bytesRead;
- DWORD dwFlags = 0;
- union operation_key *op_key_rec;
-
- PJ_CHECK_STACK();
- PJ_ASSERT_RETURN(key && op_key && buffer && length, PJ_EINVAL);
-
- op_key_rec = (union operation_key*)op_key->internal__;
- op_key_rec->overlapped.wsabuf.buf = buffer;
- op_key_rec->overlapped.wsabuf.len = *length;
-
- dwFlags = flags;
-
- /* Try non-overlapped received first to see if data is
- * immediately available.
- */
- rc = WSARecv((SOCKET)key->hnd, &op_key_rec->overlapped.wsabuf, 1,
- &bytesRead, &dwFlags, NULL, NULL);
- if (rc == 0) {
- *length = bytesRead;
- return PJ_SUCCESS;
- } else {
- DWORD dwError = WSAGetLastError();
- if (dwError != WSAEWOULDBLOCK) {
- *length = -1;
- return PJ_RETURN_OS_ERROR(dwError);
- }
- }
-
- /*
- * No immediate data available.
- * Register overlapped Recv() operation.
- */
- pj_memset(&op_key_rec->overlapped.overlapped, 0,
- sizeof(op_key_rec->overlapped.overlapped));
- op_key_rec->overlapped.operation = PJ_IOQUEUE_OP_RECV;
-
- rc = WSARecv((SOCKET)key->hnd, &op_key_rec->overlapped.wsabuf, 1,
- &bytesRead, &dwFlags,
- &op_key_rec->overlapped.overlapped, NULL);
- if (rc == SOCKET_ERROR) {
- DWORD dwStatus = WSAGetLastError();
- if (dwStatus!=WSA_IO_PENDING) {
- *length = -1;
- return PJ_STATUS_FROM_OS(dwStatus);
- }
- }
-
- /* Pending operation has been scheduled. */
- return PJ_EPENDING;
-}
-
-/*
- * pj_ioqueue_recvfrom()
- *
- * Initiate overlapped RecvFrom() operation.
- */
-PJ_DEF(pj_status_t) pj_ioqueue_recvfrom( pj_ioqueue_key_t *key,
- pj_ioqueue_op_key_t *op_key,
- void *buffer,
- pj_ssize_t *length,
- unsigned flags,
- pj_sockaddr_t *addr,
- int *addrlen)
-{
- int rc;
- DWORD bytesRead;
- DWORD dwFlags = 0;
- union operation_key *op_key_rec;
-
- PJ_CHECK_STACK();
- PJ_ASSERT_RETURN(key && op_key && buffer, PJ_EINVAL);
-
- op_key_rec = (union operation_key*)op_key->internal__;
- op_key_rec->overlapped.wsabuf.buf = buffer;
- op_key_rec->overlapped.wsabuf.len = *length;
-
- dwFlags = flags;
-
- /* Try non-overlapped received first to see if data is
- * immediately available.
- */
- rc = WSARecvFrom((SOCKET)key->hnd, &op_key_rec->overlapped.wsabuf, 1,
- &bytesRead, &dwFlags, addr, addrlen, NULL, NULL);
- if (rc == 0) {
- *length = bytesRead;
- return PJ_SUCCESS;
- } else {
- DWORD dwError = WSAGetLastError();
- if (dwError != WSAEWOULDBLOCK) {
- *length = -1;
- return PJ_RETURN_OS_ERROR(dwError);
- }
- }
-
- /*
- * No immediate data available.
- * Register overlapped Recv() operation.
- */
- pj_memset(&op_key_rec->overlapped.overlapped, 0,
- sizeof(op_key_rec->overlapped.overlapped));
- op_key_rec->overlapped.operation = PJ_IOQUEUE_OP_RECV;
-
- rc = WSARecvFrom((SOCKET)key->hnd, &op_key_rec->overlapped.wsabuf, 1,
- &bytesRead, &dwFlags, addr, addrlen,
- &op_key_rec->overlapped.overlapped, NULL);
- if (rc == SOCKET_ERROR) {
- DWORD dwStatus = WSAGetLastError();
- if (dwStatus!=WSA_IO_PENDING) {
- *length = -1;
- return PJ_STATUS_FROM_OS(dwStatus);
- }
- }
-
- /* Pending operation has been scheduled. */
- return PJ_EPENDING;
-}
-
-/*
- * pj_ioqueue_send()
- *
- * Initiate overlapped Send operation.
- */
-PJ_DEF(pj_status_t) pj_ioqueue_send( pj_ioqueue_key_t *key,
- pj_ioqueue_op_key_t *op_key,
- const void *data,
- pj_ssize_t *length,
- unsigned flags )
-{
- return pj_ioqueue_sendto(key, op_key, data, length, flags, NULL, 0);
-}
-
-
-/*
- * pj_ioqueue_sendto()
- *
- * Initiate overlapped SendTo operation.
- */
-PJ_DEF(pj_status_t) pj_ioqueue_sendto( pj_ioqueue_key_t *key,
- pj_ioqueue_op_key_t *op_key,
- const void *data,
- pj_ssize_t *length,
- unsigned flags,
- const pj_sockaddr_t *addr,
- int addrlen)
-{
- int rc;
- DWORD bytesWritten;
- DWORD dwFlags;
- union operation_key *op_key_rec;
-
- PJ_CHECK_STACK();
- PJ_ASSERT_RETURN(key && op_key && data, PJ_EINVAL);
-
- op_key_rec = (union operation_key*)op_key->internal__;
-
- dwFlags = flags;
-
- /*
- * First try blocking write.
- */
- op_key_rec->overlapped.wsabuf.buf = (void*)data;
- op_key_rec->overlapped.wsabuf.len = *length;
-
- rc = WSASendTo((SOCKET)key->hnd, &op_key_rec->overlapped.wsabuf, 1,
- &bytesWritten, dwFlags, addr, addrlen,
- NULL, NULL);
- if (rc == 0) {
- *length = bytesWritten;
- return PJ_SUCCESS;
- } else {
- DWORD dwStatus = WSAGetLastError();
- if (dwStatus != WSAEWOULDBLOCK) {
- *length = -1;
- return PJ_RETURN_OS_ERROR(dwStatus);
- }
- }
-
- /*
- * Data can't be sent immediately.
- * Schedule asynchronous WSASend().
- */
- pj_memset(&op_key_rec->overlapped.overlapped, 0,
- sizeof(op_key_rec->overlapped.overlapped));
- op_key_rec->overlapped.operation = PJ_IOQUEUE_OP_SEND;
-
- rc = WSASendTo((SOCKET)key->hnd, &op_key_rec->overlapped.wsabuf, 1,
- &bytesWritten, dwFlags, addr, addrlen,
- &op_key_rec->overlapped.overlapped, NULL);
- if (rc == SOCKET_ERROR) {
- DWORD dwStatus = WSAGetLastError();
- if (dwStatus!=WSA_IO_PENDING)
- return PJ_STATUS_FROM_OS(dwStatus);
- }
-
- /* Asynchronous operation successfully submitted. */
- return PJ_EPENDING;
-}
-
-#if PJ_HAS_TCP
-
-/*
- * pj_ioqueue_accept()
- *
- * Initiate overlapped accept() operation.
- */
-PJ_DEF(pj_status_t) pj_ioqueue_accept( pj_ioqueue_key_t *key,
- pj_ioqueue_op_key_t *op_key,
- pj_sock_t *new_sock,
- pj_sockaddr_t *local,
- pj_sockaddr_t *remote,
- int *addrlen)
-{
- BOOL rc;
- DWORD bytesReceived;
- pj_status_t status;
- union operation_key *op_key_rec;
- SOCKET sock;
-
- PJ_CHECK_STACK();
- PJ_ASSERT_RETURN(key && op_key && new_sock, PJ_EINVAL);
-
- /*
- * See if there is a new connection immediately available.
- */
- sock = WSAAccept((SOCKET)key->hnd, remote, addrlen, NULL, 0);
- if (sock != INVALID_SOCKET) {
- /* Yes! New socket is available! */
- int status;
-
- status = getsockname(sock, local, addrlen);
- if (status != 0) {
- DWORD dwError = WSAGetLastError();
- closesocket(sock);
- return PJ_RETURN_OS_ERROR(dwError);
- }
-
- *new_sock = sock;
- return PJ_SUCCESS;
-
- } else {
- DWORD dwError = WSAGetLastError();
- if (dwError != WSAEWOULDBLOCK) {
- return PJ_RETURN_OS_ERROR(dwError);
- }
- }
-
- /*
- * No connection is immediately available.
- * Must schedule an asynchronous operation.
- */
- op_key_rec = (union operation_key*)op_key->internal__;
-
- status = pj_sock_socket(PJ_AF_INET, PJ_SOCK_STREAM, 0,
- &op_key_rec->accept.newsock);
- if (status != PJ_SUCCESS)
- return status;
-
- /* On WinXP or later, use SO_UPDATE_ACCEPT_CONTEXT so that socket
- * addresses can be obtained with getsockname() and getpeername().
- */
- status = setsockopt(op_key_rec->accept.newsock, SOL_SOCKET,
- SO_UPDATE_ACCEPT_CONTEXT,
- (char*)&key->hnd, sizeof(SOCKET));
- /* SO_UPDATE_ACCEPT_CONTEXT is for WinXP or later.
- * So ignore the error status.
- */
-
- op_key_rec->accept.operation = PJ_IOQUEUE_OP_ACCEPT;
- op_key_rec->accept.addrlen = addrlen;
- op_key_rec->accept.local = local;
- op_key_rec->accept.remote = remote;
- op_key_rec->accept.newsock_ptr = new_sock;
- pj_memset(&op_key_rec->accept.overlapped, 0,
- sizeof(op_key_rec->accept.overlapped));
-
- rc = AcceptEx( (SOCKET)key->hnd, (SOCKET)op_key_rec->accept.newsock,
- op_key_rec->accept.accept_buf,
- 0, ACCEPT_ADDR_LEN, ACCEPT_ADDR_LEN,
- &bytesReceived,
- &op_key_rec->accept.overlapped );
-
- if (rc == TRUE) {
- ioqueue_on_accept_complete(&op_key_rec->accept);
- return PJ_SUCCESS;
- } else {
- DWORD dwStatus = WSAGetLastError();
- if (dwStatus!=WSA_IO_PENDING)
- return PJ_STATUS_FROM_OS(dwStatus);
- }
-
- /* Asynchronous Accept() has been submitted. */
- return PJ_EPENDING;
-}
-
-
-/*
- * pj_ioqueue_connect()
- *
- * Initiate overlapped connect() operation (well, it's non-blocking actually,
- * since there's no overlapped version of connect()).
- */
-PJ_DEF(pj_status_t) pj_ioqueue_connect( pj_ioqueue_key_t *key,
- const pj_sockaddr_t *addr,
- int addrlen )
-{
- HANDLE hEvent;
- pj_ioqueue_t *ioqueue;
-
- PJ_CHECK_STACK();
- PJ_ASSERT_RETURN(key && addr && addrlen, PJ_EINVAL);
-
- /* Initiate connect() */
- if (connect((pj_sock_t)key->hnd, addr, addrlen) != 0) {
- DWORD dwStatus;
- dwStatus = WSAGetLastError();
- if (dwStatus != WSAEWOULDBLOCK) {
- return PJ_RETURN_OS_ERROR(dwStatus);
- }
- } else {
- /* Connect has completed immediately! */
- return PJ_SUCCESS;
- }
-
- ioqueue = key->ioqueue;
-
- /* Add to the array of connecting socket to be polled */
- pj_lock_acquire(ioqueue->lock);
-
- if (ioqueue->connecting_count >= MAXIMUM_WAIT_OBJECTS) {
- pj_lock_release(ioqueue->lock);
- return PJ_ETOOMANYCONN;
- }
-
- /* Get or create event object. */
- if (ioqueue->event_count) {
- hEvent = ioqueue->event_pool[ioqueue->event_count - 1];
- --ioqueue->event_count;
- } else {
- hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
- if (hEvent == NULL) {
- DWORD dwStatus = GetLastError();
- pj_lock_release(ioqueue->lock);
- return PJ_STATUS_FROM_OS(dwStatus);
- }
- }
-
- /* Mark key as connecting.
- * We can't use array index since key can be removed dynamically.
- */
- key->connecting = 1;
-
- /* Associate socket events to the event object. */
- if (WSAEventSelect((pj_sock_t)key->hnd, hEvent, FD_CONNECT) != 0) {
- CloseHandle(hEvent);
- pj_lock_release(ioqueue->lock);
- return PJ_RETURN_OS_ERROR(WSAGetLastError());
- }
-
- /* Add to array. */
- ioqueue->connecting_keys[ ioqueue->connecting_count ] = key;
- ioqueue->connecting_handles[ ioqueue->connecting_count ] = hEvent;
- ioqueue->connecting_count++;
-
- pj_lock_release(ioqueue->lock);
-
- return PJ_EPENDING;
-}
-#endif /* #if PJ_HAS_TCP */
-
+/* $Id$
+ */
+/*
+ * PJLIB - PJ Foundation Library
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ * Benny Prijono <bennylp@bulukucing.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#include <pj/ioqueue.h>
+#include <pj/os.h>
+#include <pj/lock.h>
+#include <pj/pool.h>
+#include <pj/string.h>
+#include <pj/sock.h>
+#include <pj/array.h>
+#include <pj/log.h>
+#include <pj/assert.h>
+#include <pj/errno.h>
+
+
+#if defined(PJ_HAS_WINSOCK2_H) && PJ_HAS_WINSOCK2_H != 0
+# include <winsock2.h>
+#elif defined(PJ_HAS_WINSOCK_H) && PJ_HAS_WINSOCK_H != 0
+# include <winsock.h>
+#endif
+
+#if defined(PJ_HAS_MSWSOCK_H) && PJ_HAS_MSWSOCK_H != 0
+# include <mswsock.h>
+#endif
+
+
+/* The address specified in AcceptEx() must be 16 more than the size of
+ * SOCKADDR (source: MSDN).
+ */
+#define ACCEPT_ADDR_LEN (sizeof(pj_sockaddr_in)+16)
+
+typedef struct generic_overlapped
+{
+ WSAOVERLAPPED overlapped;
+ pj_ioqueue_operation_e operation;
+} generic_overlapped;
+
+/*
+ * OVERLAPPPED structure for send and receive.
+ */
+typedef struct ioqueue_overlapped
+{
+ WSAOVERLAPPED overlapped;
+ pj_ioqueue_operation_e operation;
+ WSABUF wsabuf;
+ pj_sockaddr_in dummy_addr;
+ int dummy_addrlen;
+} ioqueue_overlapped;
+
+#if PJ_HAS_TCP
+/*
+ * OVERLAP structure for accept.
+ */
+typedef struct ioqueue_accept_rec
+{
+ WSAOVERLAPPED overlapped;
+ pj_ioqueue_operation_e operation;
+ pj_sock_t newsock;
+ pj_sock_t *newsock_ptr;
+ int *addrlen;
+ void *remote;
+ void *local;
+ char accept_buf[2 * ACCEPT_ADDR_LEN];
+} ioqueue_accept_rec;
+#endif
+
+/*
+ * Structure to hold pending operation key.
+ */
+union operation_key
+{
+ generic_overlapped generic;
+ ioqueue_overlapped overlapped;
+#if PJ_HAS_TCP
+ ioqueue_accept_rec accept;
+#endif
+};
+
+/* Type of handle in the key. */
+enum handle_type
+{
+ HND_IS_UNKNOWN,
+ HND_IS_FILE,
+ HND_IS_SOCKET,
+};
+
+/*
+ * Structure for individual socket.
+ */
+struct pj_ioqueue_key_t
+{
+ pj_ioqueue_t *ioqueue;
+ HANDLE hnd;
+ void *user_data;
+ enum handle_type hnd_type;
+#if PJ_HAS_TCP
+ int connecting;
+#endif
+ pj_ioqueue_callback cb;
+};
+
+/*
+ * IO Queue structure.
+ */
+struct pj_ioqueue_t
+{
+ HANDLE iocp;
+ pj_lock_t *lock;
+ pj_bool_t auto_delete_lock;
+ unsigned event_count;
+ HANDLE event_pool[MAXIMUM_WAIT_OBJECTS+1];
+#if PJ_HAS_TCP
+ unsigned connecting_count;
+ HANDLE connecting_handles[MAXIMUM_WAIT_OBJECTS+1];
+ pj_ioqueue_key_t *connecting_keys[MAXIMUM_WAIT_OBJECTS+1];
+#endif
+};
+
+
+#if PJ_HAS_TCP
+/*
+ * Process the socket when the overlapped accept() completed.
+ */
+static void ioqueue_on_accept_complete(ioqueue_accept_rec *accept_overlapped)
+{
+ struct sockaddr *local;
+ struct sockaddr *remote;
+ int locallen, remotelen;
+
+ PJ_CHECK_STACK();
+
+ /* Operation complete immediately. */
+ GetAcceptExSockaddrs( accept_overlapped->accept_buf,
+ 0,
+ ACCEPT_ADDR_LEN,
+ ACCEPT_ADDR_LEN,
+ &local,
+ &locallen,
+ &remote,
+ &remotelen);
+ if (*accept_overlapped->addrlen > locallen) {
+ pj_memcpy(accept_overlapped->local, local, locallen);
+ pj_memcpy(accept_overlapped->remote, remote, locallen);
+ } else {
+ pj_memset(accept_overlapped->local, 0, *accept_overlapped->addrlen);
+ pj_memset(accept_overlapped->remote, 0, *accept_overlapped->addrlen);
+ }
+ *accept_overlapped->addrlen = locallen;
+ if (accept_overlapped->newsock_ptr)
+ *accept_overlapped->newsock_ptr = accept_overlapped->newsock;
+ accept_overlapped->operation = 0;
+ accept_overlapped->newsock = PJ_INVALID_SOCKET;
+}
+
+static void erase_connecting_socket( pj_ioqueue_t *ioqueue, unsigned pos)
+{
+ pj_ioqueue_key_t *key = ioqueue->connecting_keys[pos];
+ HANDLE hEvent = ioqueue->connecting_handles[pos];
+
+ /* Remove key from array of connecting handles. */
+ pj_array_erase(ioqueue->connecting_keys, sizeof(key),
+ ioqueue->connecting_count, pos);
+ pj_array_erase(ioqueue->connecting_handles, sizeof(HANDLE),
+ ioqueue->connecting_count, pos);
+ --ioqueue->connecting_count;
+
+ /* Disassociate the socket from the event. */
+ WSAEventSelect((pj_sock_t)key->hnd, hEvent, 0);
+
+ /* Put event object to pool. */
+ if (ioqueue->event_count < MAXIMUM_WAIT_OBJECTS) {
+ ioqueue->event_pool[ioqueue->event_count++] = hEvent;
+ } else {
+ /* Shouldn't happen. There should be no more pending connections
+ * than max.
+ */
+ pj_assert(0);
+ CloseHandle(hEvent);
+ }
+
+}
+
+/*
+ * Poll for the completion of non-blocking connect().
+ * If there's a completion, the function return the key of the completed
+ * socket, and 'result' argument contains the connect() result. If connect()
+ * succeeded, 'result' will have value zero, otherwise will have the error
+ * code.
+ */
+static pj_ioqueue_key_t *check_connecting( pj_ioqueue_t *ioqueue,
+ pj_ssize_t *connect_err )
+{
+ pj_ioqueue_key_t *key = NULL;
+
+ if (ioqueue->connecting_count) {
+ DWORD result;
+
+ pj_lock_acquire(ioqueue->lock);
+ result = WaitForMultipleObjects(ioqueue->connecting_count,
+ ioqueue->connecting_handles,
+ FALSE, 0);
+ if (result >= WAIT_OBJECT_0 &&
+ result < WAIT_OBJECT_0+ioqueue->connecting_count)
+ {
+ WSANETWORKEVENTS net_events;
+
+ /* Got completed connect(). */
+ unsigned pos = result - WAIT_OBJECT_0;
+ key = ioqueue->connecting_keys[pos];
+
+ /* See whether connect has succeeded. */
+ WSAEnumNetworkEvents((pj_sock_t)key->hnd,
+ ioqueue->connecting_handles[pos],
+ &net_events);
+ *connect_err =
+ PJ_STATUS_FROM_OS(net_events.iErrorCode[FD_CONNECT_BIT]);
+
+ /* Erase socket from pending connect. */
+ erase_connecting_socket(ioqueue, pos);
+ }
+ pj_lock_release(ioqueue->lock);
+ }
+ return key;
+}
+#endif
+
+/*
+ * pj_ioqueue_name()
+ */
+PJ_DEF(const char*) pj_ioqueue_name(void)
+{
+ return "iocp";
+}
+
+/*
+ * pj_ioqueue_create()
+ */
+PJ_DEF(pj_status_t) pj_ioqueue_create( pj_pool_t *pool,
+ pj_size_t max_fd,
+ pj_ioqueue_t **p_ioqueue)
+{
+ pj_ioqueue_t *ioqueue;
+ pj_status_t rc;
+
+ PJ_UNUSED_ARG(max_fd);
+ PJ_ASSERT_RETURN(pool && p_ioqueue, PJ_EINVAL);
+
+ rc = sizeof(union operation_key);
+
+ /* Check that sizeof(pj_ioqueue_op_key_t) makes sense. */
+ PJ_ASSERT_RETURN(sizeof(pj_ioqueue_op_key_t)-sizeof(void*) >=
+ sizeof(union operation_key), PJ_EBUG);
+
+ ioqueue = pj_pool_zalloc(pool, sizeof(*ioqueue));
+ ioqueue->iocp = CreateIoCompletionPort(INVALID_HANDLE_VALUE, NULL, 0, 0);
+ if (ioqueue->iocp == NULL)
+ return PJ_RETURN_OS_ERROR(GetLastError());
+
+ rc = pj_lock_create_simple_mutex(pool, NULL, &ioqueue->lock);
+ if (rc != PJ_SUCCESS) {
+ CloseHandle(ioqueue->iocp);
+ return rc;
+ }
+
+ ioqueue->auto_delete_lock = PJ_TRUE;
+
+ *p_ioqueue = ioqueue;
+
+ PJ_LOG(4, ("pjlib", "WinNT IOCP I/O Queue created (%p)", ioqueue));
+ return PJ_SUCCESS;
+}
+
+/*
+ * pj_ioqueue_destroy()
+ */
+PJ_DEF(pj_status_t) pj_ioqueue_destroy( pj_ioqueue_t *ioqueue )
+{
+ unsigned i;
+
+ PJ_CHECK_STACK();
+ PJ_ASSERT_RETURN(ioqueue, PJ_EINVAL);
+
+ /* Destroy events in the pool */
+ for (i=0; i<ioqueue->event_count; ++i) {
+ CloseHandle(ioqueue->event_pool[i]);
+ }
+ ioqueue->event_count = 0;
+
+ if (CloseHandle(ioqueue->iocp) != TRUE)
+ return PJ_RETURN_OS_ERROR(GetLastError());
+
+ if (ioqueue->auto_delete_lock)
+ pj_lock_destroy(ioqueue->lock);
+
+ return PJ_SUCCESS;
+}
+
+/*
+ * pj_ioqueue_set_lock()
+ */
+PJ_DEF(pj_status_t) pj_ioqueue_set_lock( pj_ioqueue_t *ioqueue,
+ pj_lock_t *lock,
+ pj_bool_t auto_delete )
+{
+ PJ_ASSERT_RETURN(ioqueue && lock, PJ_EINVAL);
+
+ if (ioqueue->auto_delete_lock) {
+ pj_lock_destroy(ioqueue->lock);
+ }
+
+ ioqueue->lock = lock;
+ ioqueue->auto_delete_lock = auto_delete;
+
+ return PJ_SUCCESS;
+}
+
+/*
+ * pj_ioqueue_register_sock()
+ */
+PJ_DEF(pj_status_t) pj_ioqueue_register_sock( pj_pool_t *pool,
+ pj_ioqueue_t *ioqueue,
+ pj_sock_t sock,
+ void *user_data,
+ const pj_ioqueue_callback *cb,
+ pj_ioqueue_key_t **key )
+{
+ HANDLE hioq;
+ pj_ioqueue_key_t *rec;
+ u_long value;
+ int rc;
+
+ PJ_ASSERT_RETURN(pool && ioqueue && cb && key, PJ_EINVAL);
+
+ /* Build the key for this socket. */
+ rec = pj_pool_zalloc(pool, sizeof(pj_ioqueue_key_t));
+ rec->ioqueue = ioqueue;
+ rec->hnd = (HANDLE)sock;
+ rec->hnd_type = HND_IS_SOCKET;
+ rec->user_data = user_data;
+ pj_memcpy(&rec->cb, cb, sizeof(pj_ioqueue_callback));
+
+ /* Set socket to nonblocking. */
+ value = 1;
+ rc = ioctlsocket(sock, FIONBIO, &value);
+ if (rc != 0) {
+ return PJ_RETURN_OS_ERROR(WSAGetLastError());
+ }
+
+ /* Associate with IOCP */
+ hioq = CreateIoCompletionPort((HANDLE)sock, ioqueue->iocp, (DWORD)rec, 0);
+ if (!hioq) {
+ return PJ_RETURN_OS_ERROR(GetLastError());
+ }
+
+ *key = rec;
+ return PJ_SUCCESS;
+}
+
+/*
+ * pj_ioqueue_unregister()
+ */
+PJ_DEF(pj_status_t) pj_ioqueue_unregister( pj_ioqueue_key_t *key )
+{
+ PJ_ASSERT_RETURN(key, PJ_EINVAL);
+
+#if PJ_HAS_TCP
+ if (key->connecting) {
+ unsigned pos;
+ pj_ioqueue_t *ioqueue;
+
+ ioqueue = key->ioqueue;
+
+ /* Erase from connecting_handles */
+ pj_lock_acquire(ioqueue->lock);
+ for (pos=0; pos < ioqueue->connecting_count; ++pos) {
+ if (ioqueue->connecting_keys[pos] == key) {
+ erase_connecting_socket(ioqueue, pos);
+ break;
+ }
+ }
+ key->connecting = 0;
+ pj_lock_release(ioqueue->lock);
+ }
+#endif
+ if (key->hnd_type == HND_IS_FILE) {
+ CloseHandle(key->hnd);
+ }
+ return PJ_SUCCESS;
+}
+
+/*
+ * pj_ioqueue_get_user_data()
+ */
+PJ_DEF(void*) pj_ioqueue_get_user_data( pj_ioqueue_key_t *key )
+{
+ PJ_ASSERT_RETURN(key, NULL);
+ return key->user_data;
+}
+
+/*
+ * pj_ioqueue_set_user_data()
+ */
+PJ_DEF(pj_status_t) pj_ioqueue_set_user_data( pj_ioqueue_key_t *key,
+ void *user_data,
+ void **old_data )
+{
+ PJ_ASSERT_RETURN(key, PJ_EINVAL);
+
+ if (old_data)
+ *old_data = key->user_data;
+
+ key->user_data = user_data;
+ return PJ_SUCCESS;
+}
+
+/*
+ * pj_ioqueue_poll()
+ *
+ * Poll for events.
+ */
+PJ_DEF(int) pj_ioqueue_poll( pj_ioqueue_t *ioqueue, const pj_time_val *timeout)
+{
+ DWORD dwMsec, dwBytesTransfered, dwKey;
+ generic_overlapped *pOv;
+ pj_ioqueue_key_t *key;
+ pj_ssize_t size_status;
+ BOOL rc;
+
+ PJ_ASSERT_RETURN(ioqueue, -PJ_EINVAL);
+
+ /* Check the connecting array. */
+#if PJ_HAS_TCP
+ key = check_connecting(ioqueue, &size_status);
+ if (key != NULL) {
+ key->cb.on_connect_complete(key, (int)size_status);
+ return 1;
+ }
+#endif
+
+ /* Calculate miliseconds timeout for GetQueuedCompletionStatus */
+ dwMsec = timeout ? timeout->sec*1000 + timeout->msec : INFINITE;
+
+ /* Poll for completion status. */
+ rc = GetQueuedCompletionStatus(ioqueue->iocp, &dwBytesTransfered, &dwKey,
+ (OVERLAPPED**)&pOv, dwMsec);
+
+ /* The return value is:
+ * - nonzero if event was dequeued.
+ * - zero and pOv==NULL if no event was dequeued.
+ * - zero and pOv!=NULL if event for failed I/O was dequeued.
+ */
+ if (pOv) {
+ /* Event was dequeued for either successfull or failed I/O */
+ key = (pj_ioqueue_key_t*)dwKey;
+ size_status = dwBytesTransfered;
+ switch (pOv->operation) {
+ case PJ_IOQUEUE_OP_READ:
+ case PJ_IOQUEUE_OP_RECV:
+ case PJ_IOQUEUE_OP_RECV_FROM:
+ pOv->operation = 0;
+ if (key->cb.on_read_complete)
+ key->cb.on_read_complete(key, (pj_ioqueue_op_key_t*)pOv,
+ size_status);
+ break;
+ case PJ_IOQUEUE_OP_WRITE:
+ case PJ_IOQUEUE_OP_SEND:
+ case PJ_IOQUEUE_OP_SEND_TO:
+ pOv->operation = 0;
+ if (key->cb.on_write_complete)
+ key->cb.on_write_complete(key, (pj_ioqueue_op_key_t*)pOv,
+ size_status);
+ break;
+#if PJ_HAS_TCP
+ case PJ_IOQUEUE_OP_ACCEPT:
+ /* special case for accept. */
+ ioqueue_on_accept_complete((ioqueue_accept_rec*)pOv);
+ if (key->cb.on_accept_complete) {
+ ioqueue_accept_rec *accept_rec = (ioqueue_accept_rec*)pOv;
+ key->cb.on_accept_complete(key,
+ (pj_ioqueue_op_key_t*)pOv,
+ accept_rec->newsock,
+ PJ_SUCCESS);
+ }
+ break;
+ case PJ_IOQUEUE_OP_CONNECT:
+#endif
+ case PJ_IOQUEUE_OP_NONE:
+ pj_assert(0);
+ break;
+ }
+ return 1;
+ }
+
+ if (GetLastError()==WAIT_TIMEOUT) {
+ /* Check the connecting array (again). */
+#if PJ_HAS_TCP
+ key = check_connecting(ioqueue, &size_status);
+ if (key != NULL) {
+ key->cb.on_connect_complete(key, (int)size_status);
+ return 1;
+ }
+#endif
+ return 0;
+ }
+ return -1;
+}
+
+/*
+ * pj_ioqueue_recv()
+ *
+ * Initiate overlapped WSARecv() operation.
+ */
+PJ_DEF(pj_status_t) pj_ioqueue_recv( pj_ioqueue_key_t *key,
+ pj_ioqueue_op_key_t *op_key,
+ void *buffer,
+ pj_ssize_t *length,
+ unsigned flags )
+{
+ /*
+ * Ideally we should just call pj_ioqueue_recvfrom() with NULL addr and
+ * addrlen here. But unfortunately it generates EINVAL... :-(
+ * -bennylp
+ */
+ int rc;
+ DWORD bytesRead;
+ DWORD dwFlags = 0;
+ union operation_key *op_key_rec;
+
+ PJ_CHECK_STACK();
+ PJ_ASSERT_RETURN(key && op_key && buffer && length, PJ_EINVAL);
+
+ op_key_rec = (union operation_key*)op_key->internal__;
+ op_key_rec->overlapped.wsabuf.buf = buffer;
+ op_key_rec->overlapped.wsabuf.len = *length;
+
+ dwFlags = flags;
+
+ /* Try non-overlapped received first to see if data is
+ * immediately available.
+ */
+ rc = WSARecv((SOCKET)key->hnd, &op_key_rec->overlapped.wsabuf, 1,
+ &bytesRead, &dwFlags, NULL, NULL);
+ if (rc == 0) {
+ *length = bytesRead;
+ return PJ_SUCCESS;
+ } else {
+ DWORD dwError = WSAGetLastError();
+ if (dwError != WSAEWOULDBLOCK) {
+ *length = -1;
+ return PJ_RETURN_OS_ERROR(dwError);
+ }
+ }
+
+ /*
+ * No immediate data available.
+ * Register overlapped Recv() operation.
+ */
+ pj_memset(&op_key_rec->overlapped.overlapped, 0,
+ sizeof(op_key_rec->overlapped.overlapped));
+ op_key_rec->overlapped.operation = PJ_IOQUEUE_OP_RECV;
+
+ rc = WSARecv((SOCKET)key->hnd, &op_key_rec->overlapped.wsabuf, 1,
+ &bytesRead, &dwFlags,
+ &op_key_rec->overlapped.overlapped, NULL);
+ if (rc == SOCKET_ERROR) {
+ DWORD dwStatus = WSAGetLastError();
+ if (dwStatus!=WSA_IO_PENDING) {
+ *length = -1;
+ return PJ_STATUS_FROM_OS(dwStatus);
+ }
+ }
+
+ /* Pending operation has been scheduled. */
+ return PJ_EPENDING;
+}
+
+/*
+ * pj_ioqueue_recvfrom()
+ *
+ * Initiate overlapped RecvFrom() operation.
+ */
+PJ_DEF(pj_status_t) pj_ioqueue_recvfrom( pj_ioqueue_key_t *key,
+ pj_ioqueue_op_key_t *op_key,
+ void *buffer,
+ pj_ssize_t *length,
+ unsigned flags,
+ pj_sockaddr_t *addr,
+ int *addrlen)
+{
+ int rc;
+ DWORD bytesRead;
+ DWORD dwFlags = 0;
+ union operation_key *op_key_rec;
+
+ PJ_CHECK_STACK();
+ PJ_ASSERT_RETURN(key && op_key && buffer, PJ_EINVAL);
+
+ op_key_rec = (union operation_key*)op_key->internal__;
+ op_key_rec->overlapped.wsabuf.buf = buffer;
+ op_key_rec->overlapped.wsabuf.len = *length;
+
+ dwFlags = flags;
+
+ /* Try non-overlapped received first to see if data is
+ * immediately available.
+ */
+ rc = WSARecvFrom((SOCKET)key->hnd, &op_key_rec->overlapped.wsabuf, 1,
+ &bytesRead, &dwFlags, addr, addrlen, NULL, NULL);
+ if (rc == 0) {
+ *length = bytesRead;
+ return PJ_SUCCESS;
+ } else {
+ DWORD dwError = WSAGetLastError();
+ if (dwError != WSAEWOULDBLOCK) {
+ *length = -1;
+ return PJ_RETURN_OS_ERROR(dwError);
+ }
+ }
+
+ /*
+ * No immediate data available.
+ * Register overlapped Recv() operation.
+ */
+ pj_memset(&op_key_rec->overlapped.overlapped, 0,
+ sizeof(op_key_rec->overlapped.overlapped));
+ op_key_rec->overlapped.operation = PJ_IOQUEUE_OP_RECV;
+
+ rc = WSARecvFrom((SOCKET)key->hnd, &op_key_rec->overlapped.wsabuf, 1,
+ &bytesRead, &dwFlags, addr, addrlen,
+ &op_key_rec->overlapped.overlapped, NULL);
+ if (rc == SOCKET_ERROR) {
+ DWORD dwStatus = WSAGetLastError();
+ if (dwStatus!=WSA_IO_PENDING) {
+ *length = -1;
+ return PJ_STATUS_FROM_OS(dwStatus);
+ }
+ }
+
+ /* Pending operation has been scheduled. */
+ return PJ_EPENDING;
+}
+
+/*
+ * pj_ioqueue_send()
+ *
+ * Initiate overlapped Send operation.
+ */
+PJ_DEF(pj_status_t) pj_ioqueue_send( pj_ioqueue_key_t *key,
+ pj_ioqueue_op_key_t *op_key,
+ const void *data,
+ pj_ssize_t *length,
+ unsigned flags )
+{
+ return pj_ioqueue_sendto(key, op_key, data, length, flags, NULL, 0);
+}
+
+
+/*
+ * pj_ioqueue_sendto()
+ *
+ * Initiate overlapped SendTo operation.
+ */
+PJ_DEF(pj_status_t) pj_ioqueue_sendto( pj_ioqueue_key_t *key,
+ pj_ioqueue_op_key_t *op_key,
+ const void *data,
+ pj_ssize_t *length,
+ unsigned flags,
+ const pj_sockaddr_t *addr,
+ int addrlen)
+{
+ int rc;
+ DWORD bytesWritten;
+ DWORD dwFlags;
+ union operation_key *op_key_rec;
+
+ PJ_CHECK_STACK();
+ PJ_ASSERT_RETURN(key && op_key && data, PJ_EINVAL);
+
+ op_key_rec = (union operation_key*)op_key->internal__;
+
+ dwFlags = flags;
+
+ /*
+ * First try blocking write.
+ */
+ op_key_rec->overlapped.wsabuf.buf = (void*)data;
+ op_key_rec->overlapped.wsabuf.len = *length;
+
+ rc = WSASendTo((SOCKET)key->hnd, &op_key_rec->overlapped.wsabuf, 1,
+ &bytesWritten, dwFlags, addr, addrlen,
+ NULL, NULL);
+ if (rc == 0) {
+ *length = bytesWritten;
+ return PJ_SUCCESS;
+ } else {
+ DWORD dwStatus = WSAGetLastError();
+ if (dwStatus != WSAEWOULDBLOCK) {
+ *length = -1;
+ return PJ_RETURN_OS_ERROR(dwStatus);
+ }
+ }
+
+ /*
+ * Data can't be sent immediately.
+ * Schedule asynchronous WSASend().
+ */
+ pj_memset(&op_key_rec->overlapped.overlapped, 0,
+ sizeof(op_key_rec->overlapped.overlapped));
+ op_key_rec->overlapped.operation = PJ_IOQUEUE_OP_SEND;
+
+ rc = WSASendTo((SOCKET)key->hnd, &op_key_rec->overlapped.wsabuf, 1,
+ &bytesWritten, dwFlags, addr, addrlen,
+ &op_key_rec->overlapped.overlapped, NULL);
+ if (rc == SOCKET_ERROR) {
+ DWORD dwStatus = WSAGetLastError();
+ if (dwStatus!=WSA_IO_PENDING)
+ return PJ_STATUS_FROM_OS(dwStatus);
+ }
+
+ /* Asynchronous operation successfully submitted. */
+ return PJ_EPENDING;
+}
+
+#if PJ_HAS_TCP
+
+/*
+ * pj_ioqueue_accept()
+ *
+ * Initiate overlapped accept() operation.
+ */
+PJ_DEF(pj_status_t) pj_ioqueue_accept( pj_ioqueue_key_t *key,
+ pj_ioqueue_op_key_t *op_key,
+ pj_sock_t *new_sock,
+ pj_sockaddr_t *local,
+ pj_sockaddr_t *remote,
+ int *addrlen)
+{
+ BOOL rc;
+ DWORD bytesReceived;
+ pj_status_t status;
+ union operation_key *op_key_rec;
+ SOCKET sock;
+
+ PJ_CHECK_STACK();
+ PJ_ASSERT_RETURN(key && op_key && new_sock, PJ_EINVAL);
+
+ /*
+ * See if there is a new connection immediately available.
+ */
+ sock = WSAAccept((SOCKET)key->hnd, remote, addrlen, NULL, 0);
+ if (sock != INVALID_SOCKET) {
+ /* Yes! New socket is available! */
+ int status;
+
+ status = getsockname(sock, local, addrlen);
+ if (status != 0) {
+ DWORD dwError = WSAGetLastError();
+ closesocket(sock);
+ return PJ_RETURN_OS_ERROR(dwError);
+ }
+
+ *new_sock = sock;
+ return PJ_SUCCESS;
+
+ } else {
+ DWORD dwError = WSAGetLastError();
+ if (dwError != WSAEWOULDBLOCK) {
+ return PJ_RETURN_OS_ERROR(dwError);
+ }
+ }
+
+ /*
+ * No connection is immediately available.
+ * Must schedule an asynchronous operation.
+ */
+ op_key_rec = (union operation_key*)op_key->internal__;
+
+ status = pj_sock_socket(PJ_AF_INET, PJ_SOCK_STREAM, 0,
+ &op_key_rec->accept.newsock);
+ if (status != PJ_SUCCESS)
+ return status;
+
+ /* On WinXP or later, use SO_UPDATE_ACCEPT_CONTEXT so that socket
+ * addresses can be obtained with getsockname() and getpeername().
+ */
+ status = setsockopt(op_key_rec->accept.newsock, SOL_SOCKET,
+ SO_UPDATE_ACCEPT_CONTEXT,
+ (char*)&key->hnd, sizeof(SOCKET));
+ /* SO_UPDATE_ACCEPT_CONTEXT is for WinXP or later.
+ * So ignore the error status.
+ */
+
+ op_key_rec->accept.operation = PJ_IOQUEUE_OP_ACCEPT;
+ op_key_rec->accept.addrlen = addrlen;
+ op_key_rec->accept.local = local;
+ op_key_rec->accept.remote = remote;
+ op_key_rec->accept.newsock_ptr = new_sock;
+ pj_memset(&op_key_rec->accept.overlapped, 0,
+ sizeof(op_key_rec->accept.overlapped));
+
+ rc = AcceptEx( (SOCKET)key->hnd, (SOCKET)op_key_rec->accept.newsock,
+ op_key_rec->accept.accept_buf,
+ 0, ACCEPT_ADDR_LEN, ACCEPT_ADDR_LEN,
+ &bytesReceived,
+ &op_key_rec->accept.overlapped );
+
+ if (rc == TRUE) {
+ ioqueue_on_accept_complete(&op_key_rec->accept);
+ return PJ_SUCCESS;
+ } else {
+ DWORD dwStatus = WSAGetLastError();
+ if (dwStatus!=WSA_IO_PENDING)
+ return PJ_STATUS_FROM_OS(dwStatus);
+ }
+
+ /* Asynchronous Accept() has been submitted. */
+ return PJ_EPENDING;
+}
+
+
+/*
+ * pj_ioqueue_connect()
+ *
+ * Initiate overlapped connect() operation (well, it's non-blocking actually,
+ * since there's no overlapped version of connect()).
+ */
+PJ_DEF(pj_status_t) pj_ioqueue_connect( pj_ioqueue_key_t *key,
+ const pj_sockaddr_t *addr,
+ int addrlen )
+{
+ HANDLE hEvent;
+ pj_ioqueue_t *ioqueue;
+
+ PJ_CHECK_STACK();
+ PJ_ASSERT_RETURN(key && addr && addrlen, PJ_EINVAL);
+
+ /* Initiate connect() */
+ if (connect((pj_sock_t)key->hnd, addr, addrlen) != 0) {
+ DWORD dwStatus;
+ dwStatus = WSAGetLastError();
+ if (dwStatus != WSAEWOULDBLOCK) {
+ return PJ_RETURN_OS_ERROR(dwStatus);
+ }
+ } else {
+ /* Connect has completed immediately! */
+ return PJ_SUCCESS;
+ }
+
+ ioqueue = key->ioqueue;
+
+ /* Add to the array of connecting socket to be polled */
+ pj_lock_acquire(ioqueue->lock);
+
+ if (ioqueue->connecting_count >= MAXIMUM_WAIT_OBJECTS) {
+ pj_lock_release(ioqueue->lock);
+ return PJ_ETOOMANYCONN;
+ }
+
+ /* Get or create event object. */
+ if (ioqueue->event_count) {
+ hEvent = ioqueue->event_pool[ioqueue->event_count - 1];
+ --ioqueue->event_count;
+ } else {
+ hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
+ if (hEvent == NULL) {
+ DWORD dwStatus = GetLastError();
+ pj_lock_release(ioqueue->lock);
+ return PJ_STATUS_FROM_OS(dwStatus);
+ }
+ }
+
+ /* Mark key as connecting.
+ * We can't use array index since key can be removed dynamically.
+ */
+ key->connecting = 1;
+
+ /* Associate socket events to the event object. */
+ if (WSAEventSelect((pj_sock_t)key->hnd, hEvent, FD_CONNECT) != 0) {
+ CloseHandle(hEvent);
+ pj_lock_release(ioqueue->lock);
+ return PJ_RETURN_OS_ERROR(WSAGetLastError());
+ }
+
+ /* Add to array. */
+ ioqueue->connecting_keys[ ioqueue->connecting_count ] = key;
+ ioqueue->connecting_handles[ ioqueue->connecting_count ] = hEvent;
+ ioqueue->connecting_count++;
+
+ pj_lock_release(ioqueue->lock);
+
+ return PJ_EPENDING;
+}
+#endif /* #if PJ_HAS_TCP */
+
PJ_DEF(void) pj_ioqueue_op_key_init( pj_ioqueue_op_key_t *op_key,
pj_size_t size )
diff --git a/pjlib/src/pj/list.c b/pjlib/src/pj/list.c
index a85108c..3ffbfdc 100644
--- a/pjlib/src/pj/list.c
+++ b/pjlib/src/pj/list.c
@@ -1,9 +1,30 @@
-/* $Id$
- */
-#include <pj/list.h>
-
-#if !PJ_FUNCTIONS_ARE_INLINED
-# include <pj/list_i.h>
-#endif
-
-
+/* $Id$
+ */
+/*
+ * PJLIB - PJ Foundation Library
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ * Benny Prijono <bennylp@bulukucing.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#include <pj/list.h>
+
+#if !PJ_FUNCTIONS_ARE_INLINED
+# include <pj/list_i.h>
+#endif
+
+
diff --git a/pjlib/src/pj/lock.c b/pjlib/src/pj/lock.c
index a81e132..ff6236a 100644
--- a/pjlib/src/pj/lock.c
+++ b/pjlib/src/pj/lock.c
@@ -1,178 +1,199 @@
-/* $Id$
- */
-#include <pj/lock.h>
-#include <pj/os.h>
-#include <pj/assert.h>
-#include <pj/pool.h>
-#include <pj/string.h>
-#include <pj/errno.h>
-
-
-typedef void LOCK_OBJ;
-
-/*
- * Lock structure.
- */
-struct pj_lock_t
-{
- LOCK_OBJ *lock_object;
-
- pj_status_t (*acquire) (LOCK_OBJ*);
- pj_status_t (*tryacquire) (LOCK_OBJ*);
- pj_status_t (*release) (LOCK_OBJ*);
- pj_status_t (*destroy) (LOCK_OBJ*);
-};
-
-typedef pj_status_t (*FPTR)(LOCK_OBJ*);
-
-/******************************************************************************
- * Implementation of lock object with mutex.
- */
-static pj_lock_t mutex_lock_template =
-{
- NULL,
- (FPTR) &pj_mutex_lock,
- (FPTR) &pj_mutex_trylock,
- (FPTR) &pj_mutex_unlock,
- (FPTR) &pj_mutex_destroy
-};
-
-static pj_status_t create_mutex_lock( pj_pool_t *pool,
- const char *name,
- int type,
- pj_lock_t **lock )
-{
- pj_lock_t *p_lock;
- pj_status_t rc;
-
- PJ_ASSERT_RETURN(pool && lock, PJ_EINVAL);
-
- p_lock = pj_pool_alloc(pool, sizeof(pj_lock_t));
- if (!p_lock)
- return PJ_ENOMEM;
-
- pj_memcpy(p_lock, &mutex_lock_template, sizeof(pj_lock_t));
- rc = pj_mutex_create(pool, name, type, (pj_mutex_t**)&p_lock->lock_object);
- if (rc != PJ_SUCCESS)
- return rc;
-
- *lock = p_lock;
- return PJ_SUCCESS;
-}
-
-
-PJ_DEF(pj_status_t) pj_lock_create_simple_mutex( pj_pool_t *pool,
- const char *name,
- pj_lock_t **lock )
-{
- return create_mutex_lock(pool, name, PJ_MUTEX_SIMPLE, lock);
-}
-
-PJ_DEF(pj_status_t) pj_lock_create_recursive_mutex( pj_pool_t *pool,
- const char *name,
- pj_lock_t **lock )
-{
- return create_mutex_lock(pool, name, PJ_MUTEX_RECURSE, lock);
-}
-
-
-/******************************************************************************
- * Implementation of NULL lock object.
- */
-static pj_status_t null_op(void *arg)
-{
- PJ_UNUSED_ARG(arg);
- return PJ_SUCCESS;
-}
-
-static pj_lock_t null_lock_template =
-{
- NULL,
- &null_op,
- &null_op,
- &null_op,
- &null_op
-};
-
-PJ_DEF(pj_status_t) pj_lock_create_null_mutex( pj_pool_t *pool,
- const char *name,
- pj_lock_t **lock )
-{
- PJ_UNUSED_ARG(name);
- PJ_UNUSED_ARG(pool);
-
- PJ_ASSERT_RETURN(lock, PJ_EINVAL);
-
- *lock = &null_lock_template;
- return PJ_SUCCESS;
-}
-
-
-/******************************************************************************
- * Implementation of semaphore lock object.
- */
-#if defined(PJ_HAS_SEMAPHORE) && PJ_HAS_SEMAPHORE != 0
-
-static pj_lock_t sem_lock_template =
-{
- NULL,
- (FPTR) &pj_sem_wait,
- (FPTR) &pj_sem_trywait,
- (FPTR) &pj_sem_post,
- (FPTR) &pj_sem_destroy
-};
-
-PJ_DEF(pj_status_t) pj_lock_create_semaphore( pj_pool_t *pool,
- const char *name,
- unsigned initial,
- unsigned max,
- pj_lock_t **lock )
-{
- pj_lock_t *p_lock;
- pj_status_t rc;
-
- PJ_ASSERT_RETURN(pool && lock, PJ_EINVAL);
-
- p_lock = pj_pool_alloc(pool, sizeof(pj_lock_t));
- if (!p_lock)
- return PJ_ENOMEM;
-
- pj_memcpy(p_lock, &sem_lock_template, sizeof(pj_lock_t));
- rc = pj_sem_create( pool, name, initial, max,
- (pj_sem_t**)&p_lock->lock_object);
- if (rc != PJ_SUCCESS)
- return rc;
-
- *lock = p_lock;
-
- return PJ_SUCCESS;
-}
-
-
-#endif /* PJ_HAS_SEMAPHORE */
-
-
-PJ_DEF(pj_status_t) pj_lock_acquire( pj_lock_t *lock )
-{
- PJ_ASSERT_RETURN(lock != NULL, PJ_EINVAL);
- return (*lock->acquire)(lock->lock_object);
-}
-
-PJ_DEF(pj_status_t) pj_lock_tryacquire( pj_lock_t *lock )
-{
- PJ_ASSERT_RETURN(lock != NULL, PJ_EINVAL);
- return (*lock->tryacquire)(lock->lock_object);
-}
-
-PJ_DEF(pj_status_t) pj_lock_release( pj_lock_t *lock )
-{
- PJ_ASSERT_RETURN(lock != NULL, PJ_EINVAL);
- return (*lock->release)(lock->lock_object);
-}
-
-PJ_DEF(pj_status_t) pj_lock_destroy( pj_lock_t *lock )
-{
- PJ_ASSERT_RETURN(lock != NULL, PJ_EINVAL);
- return (*lock->destroy)(lock->lock_object);
-}
-
+/* $Id$
+ */
+/*
+ * PJLIB - PJ Foundation Library
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ * Benny Prijono <bennylp@bulukucing.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#include <pj/lock.h>
+#include <pj/os.h>
+#include <pj/assert.h>
+#include <pj/pool.h>
+#include <pj/string.h>
+#include <pj/errno.h>
+
+
+typedef void LOCK_OBJ;
+
+/*
+ * Lock structure.
+ */
+struct pj_lock_t
+{
+ LOCK_OBJ *lock_object;
+
+ pj_status_t (*acquire) (LOCK_OBJ*);
+ pj_status_t (*tryacquire) (LOCK_OBJ*);
+ pj_status_t (*release) (LOCK_OBJ*);
+ pj_status_t (*destroy) (LOCK_OBJ*);
+};
+
+typedef pj_status_t (*FPTR)(LOCK_OBJ*);
+
+/******************************************************************************
+ * Implementation of lock object with mutex.
+ */
+static pj_lock_t mutex_lock_template =
+{
+ NULL,
+ (FPTR) &pj_mutex_lock,
+ (FPTR) &pj_mutex_trylock,
+ (FPTR) &pj_mutex_unlock,
+ (FPTR) &pj_mutex_destroy
+};
+
+static pj_status_t create_mutex_lock( pj_pool_t *pool,
+ const char *name,
+ int type,
+ pj_lock_t **lock )
+{
+ pj_lock_t *p_lock;
+ pj_status_t rc;
+
+ PJ_ASSERT_RETURN(pool && lock, PJ_EINVAL);
+
+ p_lock = pj_pool_alloc(pool, sizeof(pj_lock_t));
+ if (!p_lock)
+ return PJ_ENOMEM;
+
+ pj_memcpy(p_lock, &mutex_lock_template, sizeof(pj_lock_t));
+ rc = pj_mutex_create(pool, name, type, (pj_mutex_t**)&p_lock->lock_object);
+ if (rc != PJ_SUCCESS)
+ return rc;
+
+ *lock = p_lock;
+ return PJ_SUCCESS;
+}
+
+
+PJ_DEF(pj_status_t) pj_lock_create_simple_mutex( pj_pool_t *pool,
+ const char *name,
+ pj_lock_t **lock )
+{
+ return create_mutex_lock(pool, name, PJ_MUTEX_SIMPLE, lock);
+}
+
+PJ_DEF(pj_status_t) pj_lock_create_recursive_mutex( pj_pool_t *pool,
+ const char *name,
+ pj_lock_t **lock )
+{
+ return create_mutex_lock(pool, name, PJ_MUTEX_RECURSE, lock);
+}
+
+
+/******************************************************************************
+ * Implementation of NULL lock object.
+ */
+static pj_status_t null_op(void *arg)
+{
+ PJ_UNUSED_ARG(arg);
+ return PJ_SUCCESS;
+}
+
+static pj_lock_t null_lock_template =
+{
+ NULL,
+ &null_op,
+ &null_op,
+ &null_op,
+ &null_op
+};
+
+PJ_DEF(pj_status_t) pj_lock_create_null_mutex( pj_pool_t *pool,
+ const char *name,
+ pj_lock_t **lock )
+{
+ PJ_UNUSED_ARG(name);
+ PJ_UNUSED_ARG(pool);
+
+ PJ_ASSERT_RETURN(lock, PJ_EINVAL);
+
+ *lock = &null_lock_template;
+ return PJ_SUCCESS;
+}
+
+
+/******************************************************************************
+ * Implementation of semaphore lock object.
+ */
+#if defined(PJ_HAS_SEMAPHORE) && PJ_HAS_SEMAPHORE != 0
+
+static pj_lock_t sem_lock_template =
+{
+ NULL,
+ (FPTR) &pj_sem_wait,
+ (FPTR) &pj_sem_trywait,
+ (FPTR) &pj_sem_post,
+ (FPTR) &pj_sem_destroy
+};
+
+PJ_DEF(pj_status_t) pj_lock_create_semaphore( pj_pool_t *pool,
+ const char *name,
+ unsigned initial,
+ unsigned max,
+ pj_lock_t **lock )
+{
+ pj_lock_t *p_lock;
+ pj_status_t rc;
+
+ PJ_ASSERT_RETURN(pool && lock, PJ_EINVAL);
+
+ p_lock = pj_pool_alloc(pool, sizeof(pj_lock_t));
+ if (!p_lock)
+ return PJ_ENOMEM;
+
+ pj_memcpy(p_lock, &sem_lock_template, sizeof(pj_lock_t));
+ rc = pj_sem_create( pool, name, initial, max,
+ (pj_sem_t**)&p_lock->lock_object);
+ if (rc != PJ_SUCCESS)
+ return rc;
+
+ *lock = p_lock;
+
+ return PJ_SUCCESS;
+}
+
+
+#endif /* PJ_HAS_SEMAPHORE */
+
+
+PJ_DEF(pj_status_t) pj_lock_acquire( pj_lock_t *lock )
+{
+ PJ_ASSERT_RETURN(lock != NULL, PJ_EINVAL);
+ return (*lock->acquire)(lock->lock_object);
+}
+
+PJ_DEF(pj_status_t) pj_lock_tryacquire( pj_lock_t *lock )
+{
+ PJ_ASSERT_RETURN(lock != NULL, PJ_EINVAL);
+ return (*lock->tryacquire)(lock->lock_object);
+}
+
+PJ_DEF(pj_status_t) pj_lock_release( pj_lock_t *lock )
+{
+ PJ_ASSERT_RETURN(lock != NULL, PJ_EINVAL);
+ return (*lock->release)(lock->lock_object);
+}
+
+PJ_DEF(pj_status_t) pj_lock_destroy( pj_lock_t *lock )
+{
+ PJ_ASSERT_RETURN(lock != NULL, PJ_EINVAL);
+ return (*lock->destroy)(lock->lock_object);
+}
+
diff --git a/pjlib/src/pj/log.c b/pjlib/src/pj/log.c
index adbf98f..92802ee 100644
--- a/pjlib/src/pj/log.c
+++ b/pjlib/src/pj/log.c
@@ -1,208 +1,229 @@
-/* $Id$
- */
-#include <pj/types.h>
-#include <pj/log.h>
-#include <pj/string.h>
-#include <pj/os.h>
-#include <pj/compat/vsprintf.h>
-#include <pj/compat/stdarg.h>
-
-#if PJ_LOG_MAX_LEVEL >= 1
-
-static int log_max_level = PJ_LOG_MAX_LEVEL;
-static pj_log_func *log_writer = &pj_log_write;
-static unsigned log_decor = PJ_LOG_HAS_TIME | PJ_LOG_HAS_MICRO_SEC |
- PJ_LOG_HAS_SENDER | PJ_LOG_HAS_NEWLINE;
-
-#if PJ_LOG_USE_STACK_BUFFER==0
-static char log_buffer[PJ_LOG_MAX_SIZE];
-#endif
-
-PJ_DEF(void) pj_log_set_decor(unsigned decor)
-{
- log_decor = decor;
-}
-
-PJ_DEF(unsigned) pj_log_get_decor(void)
-{
- return log_decor;
-}
-
-PJ_DEF(void) pj_log_set_level(int level)
-{
- log_max_level = level;
-}
-
-PJ_DEF(int) pj_log_get_level(void)
-{
- return log_max_level;
-}
-
-PJ_DEF(void) pj_log_set_log_func( pj_log_func *func )
-{
- log_writer = func;
-}
-
-PJ_DEF(pj_log_func*) pj_log_get_log_func(void)
-{
- return log_writer;
-}
-
-static void pj_log(const char *sender, int level,
- const char *format, va_list marker)
-{
- pj_time_val now;
- pj_parsed_time ptime;
- char *pre;
-#if PJ_LOG_USE_STACK_BUFFER
- char log_buffer[PJ_LOG_MAX_SIZE];
-#endif
- int len;
-
- PJ_CHECK_STACK();
-
- if (level > log_max_level)
- return;
-
- /* Get current date/time. */
- pj_gettimeofday(&now);
- pj_time_decode(&now, &ptime);
-
- pre = log_buffer;
- if (log_decor & PJ_LOG_HAS_DAY_NAME) {
- static const char *wdays[] = { "Sun", "Mon", "Tue", "Wed",
- "Thu", "Fri", "Sat"};
- strcpy(pre, wdays[ptime.wday]);
- pre += 3;
- }
- if (log_decor & PJ_LOG_HAS_YEAR) {
- *pre++ = ' ';
- pre += pj_utoa(ptime.year, pre);
- }
- if (log_decor & PJ_LOG_HAS_MONTH) {
- *pre++ = '-';
- pre += pj_utoa_pad(ptime.mon, pre, 2, '0');
- }
- if (log_decor & PJ_LOG_HAS_DAY_OF_MON) {
- *pre++ = ' ';
- pre += pj_utoa_pad(ptime.day, pre, 2, '0');
- }
- if (log_decor & PJ_LOG_HAS_TIME) {
- *pre++ = ' ';
- pre += pj_utoa_pad(ptime.hour, pre, 2, '0');
- *pre++ = ':';
- pre += pj_utoa_pad(ptime.min, pre, 2, '0');
- *pre++ = ':';
- pre += pj_utoa_pad(ptime.sec, pre, 2, '0');
- }
- if (log_decor & PJ_LOG_HAS_MICRO_SEC) {
- *pre++ = '.';
- pre += pj_utoa_pad(ptime.msec, pre, 3, '0');
- }
- if (log_decor & PJ_LOG_HAS_SENDER) {
- enum { SENDER_WIDTH = 12 };
- int sender_len = strlen(sender);
- *pre++ = ' ';
- if (sender_len <= SENDER_WIDTH) {
- while (sender_len < SENDER_WIDTH)
- *pre++ = ' ', ++sender_len;
- while (*sender)
- *pre++ = *sender++;
- } else {
- int i;
- for (i=0; i<SENDER_WIDTH; ++i)
- *pre++ = *sender++;
- }
- }
-
- if (log_decor != 0 && log_decor != PJ_LOG_HAS_NEWLINE)
- *pre++ = ' ';
-
- len = pre - log_buffer;
-
- /* Print the whole message to the string log_buffer. */
- len = len + vsnprintf(pre, sizeof(log_buffer)-len, format, marker);
- if (len > 0 && len < sizeof(log_buffer)-1) {
- if (log_decor & PJ_LOG_HAS_NEWLINE) {
- log_buffer[len++] = '\n';
- }
- log_buffer[len++] = '\0';
- } else {
- len = sizeof(log_buffer)-1;
- if (log_decor & PJ_LOG_HAS_NEWLINE) {
- log_buffer[sizeof(log_buffer)-2] = '\n';
- }
- log_buffer[sizeof(log_buffer)-1] = '\0';
- }
-
- if (log_writer)
- (*log_writer)(level, log_buffer, len);
-}
-
-PJ_DEF(void) pj_log_0(const char *obj, const char *format, ...)
-{
- va_list arg;
- va_start(arg, format);
- pj_log(obj, 0, format, arg);
- va_end(arg);
-}
-
-PJ_DEF(void) pj_log_1(const char *obj, const char *format, ...)
-{
- va_list arg;
- va_start(arg, format);
- pj_log(obj, 1, format, arg);
- va_end(arg);
-}
-#endif /* PJ_LOG_MAX_LEVEL >= 1 */
-
-#if PJ_LOG_MAX_LEVEL >= 2
-PJ_DEF(void) pj_log_2(const char *obj, const char *format, ...)
-{
- va_list arg;
- va_start(arg, format);
- pj_log(obj, 2, format, arg);
- va_end(arg);
-}
-#endif
-
-#if PJ_LOG_MAX_LEVEL >= 3
-PJ_DEF(void) pj_log_3(const char *obj, const char *format, ...)
-{
- va_list arg;
- va_start(arg, format);
- pj_log(obj, 3, format, arg);
- va_end(arg);
-}
-#endif
-
-#if PJ_LOG_MAX_LEVEL >= 4
-PJ_DEF(void) pj_log_4(const char *obj, const char *format, ...)
-{
- va_list arg;
- va_start(arg, format);
- pj_log(obj, 4, format, arg);
- va_end(arg);
-}
-#endif
-
-#if PJ_LOG_MAX_LEVEL >= 5
-PJ_DEF(void) pj_log_5(const char *obj, const char *format, ...)
-{
- va_list arg;
- va_start(arg, format);
- pj_log(obj, 5, format, arg);
- va_end(arg);
-}
-#endif
-
-#if PJ_LOG_MAX_LEVEL >= 6
-PJ_DEF(void) pj_log_6(const char *obj, const char *format, ...)
-{
- va_list arg;
- va_start(arg, format);
- pj_log(obj, 6, format, arg);
- va_end(arg);
-}
-#endif
-
+/* $Id$
+ */
+/*
+ * PJLIB - PJ Foundation Library
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ * Benny Prijono <bennylp@bulukucing.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; 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/log.h>
+#include <pj/string.h>
+#include <pj/os.h>
+#include <pj/compat/vsprintf.h>
+#include <pj/compat/stdarg.h>
+
+#if PJ_LOG_MAX_LEVEL >= 1
+
+static int log_max_level = PJ_LOG_MAX_LEVEL;
+static pj_log_func *log_writer = &pj_log_write;
+static unsigned log_decor = PJ_LOG_HAS_TIME | PJ_LOG_HAS_MICRO_SEC |
+ PJ_LOG_HAS_SENDER | PJ_LOG_HAS_NEWLINE;
+
+#if PJ_LOG_USE_STACK_BUFFER==0
+static char log_buffer[PJ_LOG_MAX_SIZE];
+#endif
+
+PJ_DEF(void) pj_log_set_decor(unsigned decor)
+{
+ log_decor = decor;
+}
+
+PJ_DEF(unsigned) pj_log_get_decor(void)
+{
+ return log_decor;
+}
+
+PJ_DEF(void) pj_log_set_level(int level)
+{
+ log_max_level = level;
+}
+
+PJ_DEF(int) pj_log_get_level(void)
+{
+ return log_max_level;
+}
+
+PJ_DEF(void) pj_log_set_log_func( pj_log_func *func )
+{
+ log_writer = func;
+}
+
+PJ_DEF(pj_log_func*) pj_log_get_log_func(void)
+{
+ return log_writer;
+}
+
+static void pj_log(const char *sender, int level,
+ const char *format, va_list marker)
+{
+ pj_time_val now;
+ pj_parsed_time ptime;
+ char *pre;
+#if PJ_LOG_USE_STACK_BUFFER
+ char log_buffer[PJ_LOG_MAX_SIZE];
+#endif
+ int len;
+
+ PJ_CHECK_STACK();
+
+ if (level > log_max_level)
+ return;
+
+ /* Get current date/time. */
+ pj_gettimeofday(&now);
+ pj_time_decode(&now, &ptime);
+
+ pre = log_buffer;
+ if (log_decor & PJ_LOG_HAS_DAY_NAME) {
+ static const char *wdays[] = { "Sun", "Mon", "Tue", "Wed",
+ "Thu", "Fri", "Sat"};
+ strcpy(pre, wdays[ptime.wday]);
+ pre += 3;
+ }
+ if (log_decor & PJ_LOG_HAS_YEAR) {
+ *pre++ = ' ';
+ pre += pj_utoa(ptime.year, pre);
+ }
+ if (log_decor & PJ_LOG_HAS_MONTH) {
+ *pre++ = '-';
+ pre += pj_utoa_pad(ptime.mon, pre, 2, '0');
+ }
+ if (log_decor & PJ_LOG_HAS_DAY_OF_MON) {
+ *pre++ = ' ';
+ pre += pj_utoa_pad(ptime.day, pre, 2, '0');
+ }
+ if (log_decor & PJ_LOG_HAS_TIME) {
+ *pre++ = ' ';
+ pre += pj_utoa_pad(ptime.hour, pre, 2, '0');
+ *pre++ = ':';
+ pre += pj_utoa_pad(ptime.min, pre, 2, '0');
+ *pre++ = ':';
+ pre += pj_utoa_pad(ptime.sec, pre, 2, '0');
+ }
+ if (log_decor & PJ_LOG_HAS_MICRO_SEC) {
+ *pre++ = '.';
+ pre += pj_utoa_pad(ptime.msec, pre, 3, '0');
+ }
+ if (log_decor & PJ_LOG_HAS_SENDER) {
+ enum { SENDER_WIDTH = 12 };
+ int sender_len = strlen(sender);
+ *pre++ = ' ';
+ if (sender_len <= SENDER_WIDTH) {
+ while (sender_len < SENDER_WIDTH)
+ *pre++ = ' ', ++sender_len;
+ while (*sender)
+ *pre++ = *sender++;
+ } else {
+ int i;
+ for (i=0; i<SENDER_WIDTH; ++i)
+ *pre++ = *sender++;
+ }
+ }
+
+ if (log_decor != 0 && log_decor != PJ_LOG_HAS_NEWLINE)
+ *pre++ = ' ';
+
+ len = pre - log_buffer;
+
+ /* Print the whole message to the string log_buffer. */
+ len = len + vsnprintf(pre, sizeof(log_buffer)-len, format, marker);
+ if (len > 0 && len < sizeof(log_buffer)-1) {
+ if (log_decor & PJ_LOG_HAS_NEWLINE) {
+ log_buffer[len++] = '\n';
+ }
+ log_buffer[len++] = '\0';
+ } else {
+ len = sizeof(log_buffer)-1;
+ if (log_decor & PJ_LOG_HAS_NEWLINE) {
+ log_buffer[sizeof(log_buffer)-2] = '\n';
+ }
+ log_buffer[sizeof(log_buffer)-1] = '\0';
+ }
+
+ if (log_writer)
+ (*log_writer)(level, log_buffer, len);
+}
+
+PJ_DEF(void) pj_log_0(const char *obj, const char *format, ...)
+{
+ va_list arg;
+ va_start(arg, format);
+ pj_log(obj, 0, format, arg);
+ va_end(arg);
+}
+
+PJ_DEF(void) pj_log_1(const char *obj, const char *format, ...)
+{
+ va_list arg;
+ va_start(arg, format);
+ pj_log(obj, 1, format, arg);
+ va_end(arg);
+}
+#endif /* PJ_LOG_MAX_LEVEL >= 1 */
+
+#if PJ_LOG_MAX_LEVEL >= 2
+PJ_DEF(void) pj_log_2(const char *obj, const char *format, ...)
+{
+ va_list arg;
+ va_start(arg, format);
+ pj_log(obj, 2, format, arg);
+ va_end(arg);
+}
+#endif
+
+#if PJ_LOG_MAX_LEVEL >= 3
+PJ_DEF(void) pj_log_3(const char *obj, const char *format, ...)
+{
+ va_list arg;
+ va_start(arg, format);
+ pj_log(obj, 3, format, arg);
+ va_end(arg);
+}
+#endif
+
+#if PJ_LOG_MAX_LEVEL >= 4
+PJ_DEF(void) pj_log_4(const char *obj, const char *format, ...)
+{
+ va_list arg;
+ va_start(arg, format);
+ pj_log(obj, 4, format, arg);
+ va_end(arg);
+}
+#endif
+
+#if PJ_LOG_MAX_LEVEL >= 5
+PJ_DEF(void) pj_log_5(const char *obj, const char *format, ...)
+{
+ va_list arg;
+ va_start(arg, format);
+ pj_log(obj, 5, format, arg);
+ va_end(arg);
+}
+#endif
+
+#if PJ_LOG_MAX_LEVEL >= 6
+PJ_DEF(void) pj_log_6(const char *obj, const char *format, ...)
+{
+ va_list arg;
+ va_start(arg, format);
+ pj_log(obj, 6, format, arg);
+ va_end(arg);
+}
+#endif
+
diff --git a/pjlib/src/pj/log_writer_printk.c b/pjlib/src/pj/log_writer_printk.c
index 2581ef2..da20598 100644
--- a/pjlib/src/pj/log_writer_printk.c
+++ b/pjlib/src/pj/log_writer_printk.c
@@ -1,12 +1,33 @@
-/* $Id$
- *
- */
-#include <pj/log.h>
-#include <pj/os.h>
-
-PJ_DEF(void) pj_log_write(int level, const char *buffer, int len)
-{
- PJ_CHECK_STACK();
- printk(KERN_INFO "%s", buffer);
-}
-
+/* $Id$
+ *
+ */
+/*
+ * PJLIB - PJ Foundation Library
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ * Benny Prijono <bennylp@bulukucing.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#include <pj/log.h>
+#include <pj/os.h>
+
+PJ_DEF(void) pj_log_write(int level, const char *buffer, int len)
+{
+ PJ_CHECK_STACK();
+ printk(KERN_INFO "%s", buffer);
+}
+
diff --git a/pjlib/src/pj/log_writer_stdout.c b/pjlib/src/pj/log_writer_stdout.c
index 196f1c3..d301002 100644
--- a/pjlib/src/pj/log_writer_stdout.c
+++ b/pjlib/src/pj/log_writer_stdout.c
@@ -1,54 +1,75 @@
-/* $Id$
- */
-#include <pj/log.h>
-#include <pj/os.h>
-#include <pj/compat/stdfileio.h>
-
-#define CLR_FATAL (PJ_TERM_COLOR_BRIGHT | PJ_TERM_COLOR_R)
-#define CLR_WARNING (PJ_TERM_COLOR_BRIGHT | PJ_TERM_COLOR_R | PJ_TERM_COLOR_G)
-#define CLR_INFO (PJ_TERM_COLOR_BRIGHT | PJ_TERM_COLOR_R | PJ_TERM_COLOR_G | \
- PJ_TERM_COLOR_B)
-#define CLR_DEFAULT (PJ_TERM_COLOR_R | PJ_TERM_COLOR_G | PJ_TERM_COLOR_B)
-
-static void term_set_color(int level)
-{
-#if defined(PJ_TERM_HAS_COLOR) && PJ_TERM_HAS_COLOR != 0
- unsigned attr = 0;
- switch (level) {
- case 0:
- case 1: attr = CLR_FATAL;
- break;
- case 2: attr = CLR_WARNING;
- break;
- case 3: attr = CLR_INFO;
- break;
- default:
- attr = CLR_DEFAULT;
- break;
- }
-
- pj_term_set_color(attr);
-#endif
-}
-
-static void term_restore_color(void)
-{
-#if defined(PJ_TERM_HAS_COLOR) && PJ_TERM_HAS_COLOR != 0
- pj_term_set_color(CLR_DEFAULT);
-#endif
-}
-
-
-PJ_DEF(void) pj_log_write(int level, const char *buffer, int len)
-{
- PJ_CHECK_STACK();
- PJ_UNUSED_ARG(len);
-
- /* Copy to terminal/file. */
- term_set_color(level);
- fputs(buffer, stdout);
- term_restore_color();
-
- fflush(stdout);
-}
-
+/* $Id$
+ */
+/*
+ * PJLIB - PJ Foundation Library
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ * Benny Prijono <bennylp@bulukucing.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#include <pj/log.h>
+#include <pj/os.h>
+#include <pj/compat/stdfileio.h>
+
+#define CLR_FATAL (PJ_TERM_COLOR_BRIGHT | PJ_TERM_COLOR_R)
+#define CLR_WARNING (PJ_TERM_COLOR_BRIGHT | PJ_TERM_COLOR_R | PJ_TERM_COLOR_G)
+#define CLR_INFO (PJ_TERM_COLOR_BRIGHT | PJ_TERM_COLOR_R | PJ_TERM_COLOR_G | \
+ PJ_TERM_COLOR_B)
+#define CLR_DEFAULT (PJ_TERM_COLOR_R | PJ_TERM_COLOR_G | PJ_TERM_COLOR_B)
+
+static void term_set_color(int level)
+{
+#if defined(PJ_TERM_HAS_COLOR) && PJ_TERM_HAS_COLOR != 0
+ unsigned attr = 0;
+ switch (level) {
+ case 0:
+ case 1: attr = CLR_FATAL;
+ break;
+ case 2: attr = CLR_WARNING;
+ break;
+ case 3: attr = CLR_INFO;
+ break;
+ default:
+ attr = CLR_DEFAULT;
+ break;
+ }
+
+ pj_term_set_color(attr);
+#endif
+}
+
+static void term_restore_color(void)
+{
+#if defined(PJ_TERM_HAS_COLOR) && PJ_TERM_HAS_COLOR != 0
+ pj_term_set_color(CLR_DEFAULT);
+#endif
+}
+
+
+PJ_DEF(void) pj_log_write(int level, const char *buffer, int len)
+{
+ PJ_CHECK_STACK();
+ PJ_UNUSED_ARG(len);
+
+ /* Copy to terminal/file. */
+ term_set_color(level);
+ fputs(buffer, stdout);
+ term_restore_color();
+
+ fflush(stdout);
+}
+
diff --git a/pjlib/src/pj/os_core_linux_kernel.c b/pjlib/src/pj/os_core_linux_kernel.c
index 1433708..2bb2acf 100644
--- a/pjlib/src/pj/os_core_linux_kernel.c
+++ b/pjlib/src/pj/os_core_linux_kernel.c
@@ -1,674 +1,695 @@
-/* $Id$
- *
- */
-#include <pj/os.h>
-#include <pj/assert.h>
-#include <pj/pool.h>
-#include <pj/log.h>
-#include <pj/except.h>
-#include <pj/errno.h>
-#include <pj/string.h>
-#include <pj/compat/high_precision.h>
-#include <pj/compat/sprintf.h>
-
-#include <linux/config.h>
-#include <linux/version.h>
-#if defined(MODVERSIONS)
-#include <linux/modversions.h>
-#endif
-#include <linux/kernel.h>
-#include <linux/sched.h>
-//#include <linux/tqueue.h>
-#include <linux/wait.h>
-#include <linux/signal.h>
-
-#include <asm/atomic.h>
-#include <asm/unistd.h>
-#include <asm/semaphore.h>
-
-#define THIS_FILE "oslinuxkern"
-
-struct pj_thread_t
-{
- /** Thread's name. */
- char obj_name[PJ_MAX_OBJ_NAME];
-
- /** Linux task structure for thread. */
- struct task_struct *thread;
-
- /** Flags (specified in pj_thread_create) */
- unsigned flags;
-
- /** Task queue needed to launch thread. */
- //struct tq_struct tq;
-
- /** Semaphore needed to control thread startup. */
- struct semaphore startstop_sem;
-
- /** Semaphore to suspend thread during startup. */
- struct semaphore suspend_sem;
-
- /** Queue thread is waiting on. Gets initialized by
- thread_initialize, can be used by thread itself.
- */
- wait_queue_head_t queue;
-
- /** Flag to tell thread whether to die or not.
- When the thread receives a signal, it must check
- the value of terminate and call thread_deinitialize and terminate
- if set.
- */
- int terminate;
-
- /** Thread's entry. */
- pj_thread_proc *func;
-
- /** Argument. */
- void *arg;
-};
-
-struct pj_atomic_t
-{
- atomic_t atom;
-};
-
-struct pj_mutex_t
-{
- struct semaphore sem;
- pj_bool_t recursive;
- pj_thread_t *owner;
- int own_count;
-};
-
-struct pj_sem_t
-{
- struct semaphore sem;
-};
-
-/*
- * Static global variables.
- */
-#define MAX_TLS_ID 32
-static void *tls_values[MAX_TLS_ID];
-static int tls_id;
-static long thread_tls_id;
-static spinlock_t critical_section = SPIN_LOCK_UNLOCKED;
-static unsigned long spinlock_flags;
-static pj_thread_t main_thread;
-
-/* private functions */
-//#define TRACE_(expr) PJ_LOG(3,expr)
-#define TRACE_(x)
-
-
-/* This must be called in the context of the new thread. */
-static void thread_initialize( pj_thread_t *thread )
-{
- TRACE_((THIS_FILE, "---new thread initializing..."));
-
- /* Set TLS */
- pj_thread_local_set(thread_tls_id, thread);
-
- /* fill in thread structure */
- thread->thread = current;
- pj_assert(thread->thread != NULL);
-
- /* set signal mask to what we want to respond */
- siginitsetinv(¤t->blocked,
- sigmask(SIGKILL)|sigmask(SIGINT)|sigmask(SIGTERM));
-
- /* initialise wait queue */
- init_waitqueue_head(&thread->queue);
-
- /* initialise termination flag */
- thread->terminate = 0;
-
- /* set name of this process (max 15 chars + 0 !) */
- thread->obj_name[15] = '\0';
- sprintf(current->comm, thread->obj_name);
-
- /* tell the creator that we are ready and let him continue */
- up(&thread->startstop_sem);
-}
-
-/* cleanup of thread. Called by the exiting thread. */
-static void thread_deinitialize(pj_thread_t *thread)
-{
- /* we are terminating */
-
- /* lock the kernel, the exit will unlock it */
- thread->thread = NULL;
- mb();
-
- /* notify the stop_kthread() routine that we are terminating. */
- up(&thread->startstop_sem);
-
- /* the kernel_thread that called clone() does a do_exit here. */
-
- /* there is no race here between execution of the "killer" and
- real termination of the thread (race window between up and do_exit),
- since both the thread and the "killer" function are running with
- the kernel lock held.
- The kernel lock will be freed after the thread exited, so the code
- is really not executed anymore as soon as the unload functions gets
- the kernel lock back.
- The init process may not have made the cleanup of the process here,
- but the cleanup can be done safely with the module unloaded.
- */
-
-}
-
-static int thread_proc(void *arg)
-{
- pj_thread_t *thread = arg;
-
- TRACE_((THIS_FILE, "---new thread starting!"));
-
- /* Initialize thread. */
- thread_initialize( thread );
-
- /* Wait if created suspended. */
- if (thread->flags & PJ_THREAD_SUSPENDED) {
- TRACE_((THIS_FILE, "---new thread suspended..."));
- down(&thread->suspend_sem);
- }
-
- TRACE_((THIS_FILE, "---new thread running..."));
-
- pj_assert(thread->func != NULL);
-
- /* Call thread's entry. */
- (*thread->func)(thread->arg);
-
- TRACE_((THIS_FILE, "---thread exiting..."));
-
- /* Cleanup thread. */
- thread_deinitialize(thread);
-
- return 0;
-}
-
-/* The very task entry. */
-static void kthread_launcher(void *arg)
-{
- TRACE_((THIS_FILE, "...launching thread!..."));
- kernel_thread(&thread_proc, arg, 0);
-}
-
-PJ_DEF(pj_status_t) pj_init(void)
-{
- pj_status_t rc;
-
- PJ_LOG(5, ("pj_init", "Initializing PJ Library.."));
-
- rc = pj_thread_init();
- if (rc != PJ_SUCCESS)
- return rc;
-
- /* Initialize exception ID for the pool.
- * Must do so after critical section is configured.
- */
- rc = pj_exception_id_alloc("PJLIB/No memory", &PJ_NO_MEMORY_EXCEPTION);
- if (rc != PJ_SUCCESS)
- return rc;
-
- return PJ_SUCCESS;
-}
-
-PJ_DEF(pj_uint32_t) pj_getpid(void)
-{
- return 1;
-}
-
-PJ_DEF(pj_status_t) pj_thread_register ( const char *cstr_thread_name,
- pj_thread_desc desc,
- pj_thread_t **ptr_thread)
-{
- char stack_ptr;
- pj_thread_t *thread = (pj_thread_t *)desc;
- pj_str_t thread_name = pj_str((char*)cstr_thread_name);
-
- /* Size sanity check. */
- if (sizeof(pj_thread_desc) < sizeof(pj_thread_t)) {
- pj_assert(!"Not enough pj_thread_desc size!");
- return PJ_EBUG;
- }
-
- /* If a thread descriptor has been registered before, just return it. */
- if (pj_thread_local_get (thread_tls_id) != 0) {
- *ptr_thread = (pj_thread_t*)pj_thread_local_get (thread_tls_id);
- return PJ_SUCCESS;
- }
-
- /* Initialize and set the thread entry. */
- pj_memset(desc, 0, sizeof(struct pj_thread_t));
-
- if(cstr_thread_name && pj_strlen(&thread_name) < sizeof(thread->obj_name)-1)
- pj_sprintf(thread->obj_name, cstr_thread_name, thread->thread);
- else
- pj_sprintf(thread->obj_name, "thr%p", (void*)thread->thread);
-
- /* Initialize. */
- thread_initialize(thread);
-
- /* Eat semaphore. */
- down(&thread->startstop_sem);
-
-#if defined(PJ_OS_HAS_CHECK_STACK) && PJ_OS_HAS_CHECK_STACK!=0
- thread->stk_start = &stack_ptr;
- thread->stk_size = 0xFFFFFFFFUL;
- thread->stk_max_usage = 0;
-#else
- stack_ptr = '\0';
-#endif
-
- *ptr_thread = thread;
- return PJ_SUCCESS;
-}
-
-
-pj_status_t pj_thread_init(void)
-{
- pj_status_t rc;
- pj_thread_t *dummy;
-
- rc = pj_thread_local_alloc(&thread_tls_id);
- if (rc != PJ_SUCCESS)
- return rc;
-
- return pj_thread_register("pjlib-main", (long*)&main_thread, &dummy);
-}
-
-PJ_DEF(pj_status_t) pj_thread_create( pj_pool_t *pool, const char *thread_name,
- pj_thread_proc *proc, void *arg,
- pj_size_t stack_size, unsigned flags,
- pj_thread_t **ptr_thread)
-{
- pj_thread_t *thread;
-
- TRACE_((THIS_FILE, "pj_thread_create()"));
-
- PJ_ASSERT_RETURN(pool && proc && ptr_thread, PJ_EINVAL);
-
- thread = pj_pool_zalloc(pool, sizeof(pj_thread_t));
- if (!thread)
- return PJ_ENOMEM;
-
- PJ_UNUSED_ARG(stack_size);
-
- /* Thread name. */
- if (!thread_name)
- thread_name = "thr%p";
-
- if (strchr(thread_name, '%')) {
- pj_snprintf(thread->obj_name, PJ_MAX_OBJ_NAME, thread_name, thread);
- } else {
- strncpy(thread->obj_name, thread_name, PJ_MAX_OBJ_NAME);
- thread->obj_name[PJ_MAX_OBJ_NAME-1] = '\0';
- }
-
- /* Init thread's semaphore. */
- TRACE_((THIS_FILE, "...init semaphores..."));
- init_MUTEX_LOCKED(&thread->startstop_sem);
- init_MUTEX_LOCKED(&thread->suspend_sem);
-
- thread->flags = flags;
-
- if ((flags & PJ_THREAD_SUSPENDED) == 0) {
- up(&thread->suspend_sem);
- }
-
- /* Store the functions and argument. */
- thread->func = proc;
- thread->arg = arg;
-
- /* Save return value. */
- *ptr_thread = thread;
-
- /* Create the new thread by running a task through keventd. */
-
-#if 0
- /* Initialize the task queue struct. */
- thread->tq.sync = 0;
- INIT_LIST_HEAD(&thread->tq.list);
- thread->tq.routine = kthread_launcher;
- thread->tq.data = thread;
-
- /* and schedule it for execution. */
- schedule_task(&thread->tq);
-#endif
- kthread_launcher(thread);
-
- /* Wait until thread has reached the setup_thread routine. */
- TRACE_((THIS_FILE, "...wait for the new thread..."));
- down(&thread->startstop_sem);
-
- TRACE_((THIS_FILE, "...main thread resumed..."));
- return PJ_SUCCESS;
-}
-
-PJ_DEF(const char*) pj_thread_get_name(pj_thread_t *thread)
-{
- return thread->obj_name;
-}
-
-PJ_DEF(pj_status_t) pj_thread_resume(pj_thread_t *thread)
-{
- up(&thread->suspend_sem);
- return PJ_SUCCESS;
-}
-
-PJ_DEF(pj_thread_t*) pj_thread_this(void)
-{
- return (pj_thread_t*)pj_thread_local_get(thread_tls_id);
-}
-
-PJ_DEF(pj_status_t) pj_thread_join(pj_thread_t *p)
-{
- TRACE_((THIS_FILE, "pj_thread_join()"));
- down(&p->startstop_sem);
- TRACE_((THIS_FILE, " joined!"));
- return PJ_SUCCESS;
-}
-
-PJ_DEF(pj_status_t) pj_thread_destroy(pj_thread_t *thread)
-{
- PJ_ASSERT_RETURN(thread != NULL, PJ_EINVALIDOP);
- return PJ_SUCCESS;
-}
-
-PJ_DEF(pj_status_t) pj_thread_sleep(unsigned msec)
-{
- pj_highprec_t ticks;
- pj_thread_t *thread = pj_thread_this();
-
- PJ_ASSERT_RETURN(thread != NULL, PJ_EBUG);
-
- /* Use high precision calculation to make sure we don't
- * crop values:
- *
- * ticks = HZ * msec / 1000
- */
- ticks = HZ;
- pj_highprec_mul(ticks, msec);
- pj_highprec_div(ticks, 1000);
-
- TRACE_((THIS_FILE, "this thread will sleep for %u ticks", ticks));
- interruptible_sleep_on_timeout( &thread->queue, ticks);
- return PJ_SUCCESS;
-}
-
-
-///////////////////////////////////////////////////////////////////////////////
-PJ_DEF(pj_status_t) pj_atomic_create( pj_pool_t *pool,
- pj_atomic_value_t value,
- pj_atomic_t **ptr_var)
-{
- pj_atomic_t *t = pj_pool_calloc(pool, 1, sizeof(pj_atomic_t));
- if (!t) return PJ_ENOMEM;
-
- atomic_set(&t->atom, value);
- *ptr_var = t;
-
- return PJ_SUCCESS;
-}
-
-PJ_DEF(pj_status_t) pj_atomic_destroy( pj_atomic_t *var )
-{
- return PJ_SUCCESS;
-}
-
-PJ_DEF(void) pj_atomic_set(pj_atomic_t *var, pj_atomic_value_t value)
-{
- atomic_set(&var->atom, value);
-}
-
-PJ_DEF(pj_atomic_value_t) pj_atomic_get(pj_atomic_t *var)
-{
- return atomic_read(&var->atom);
-}
-
-PJ_DEF(void) pj_atomic_inc(pj_atomic_t *var)
-{
- atomic_inc(&var->atom);
-}
-
-PJ_DEF(void) pj_atomic_dec(pj_atomic_t *var)
-{
- atomic_dec(&var->atom);
-}
-
-PJ_DEF(void) pj_atomic_add( pj_atomic_t *var, pj_atomic_value_t value )
-{
- atomic_add(value, &var->atom);
-}
-
-
-///////////////////////////////////////////////////////////////////////////////
-PJ_DEF(pj_status_t) pj_thread_local_alloc(long *index)
-{
- if (tls_id >= MAX_TLS_ID)
- return PJ_ETOOMANY;
-
- *index = tls_id++;
-
- return PJ_SUCCESS;
-}
-
-PJ_DEF(void) pj_thread_local_free(long index)
-{
- pj_assert(index >= 0 && index < MAX_TLS_ID);
-}
-
-PJ_DEF(pj_status_t) pj_thread_local_set(long index, void *value)
-{
- pj_assert(index >= 0 && index < MAX_TLS_ID);
- tls_values[index] = value;
- return PJ_SUCCESS;
-}
-
-PJ_DEF(void*) pj_thread_local_get(long index)
-{
- pj_assert(index >= 0 && index < MAX_TLS_ID);
- return tls_values[index];
-}
-
-
-///////////////////////////////////////////////////////////////////////////////
-PJ_DEF(void) pj_enter_critical_section(void)
-{
- spin_lock_irqsave(&critical_section, spinlock_flags);
-}
-
-PJ_DEF(void) pj_leave_critical_section(void)
-{
- spin_unlock_irqrestore(&critical_section, spinlock_flags);
-}
-
-
-///////////////////////////////////////////////////////////////////////////////
-PJ_DEF(pj_status_t) pj_mutex_create( pj_pool_t *pool,
- const char *name,
- int type,
- pj_mutex_t **ptr_mutex)
-{
- pj_mutex_t *mutex;
-
- PJ_UNUSED_ARG(name);
-
- mutex = pj_pool_alloc(pool, sizeof(pj_mutex_t));
- if (!mutex)
- return PJ_ENOMEM;
-
- init_MUTEX(&mutex->sem);
-
- mutex->recursive = (type == PJ_MUTEX_RECURSE);
- mutex->owner = NULL;
- mutex->own_count = 0;
-
- /* Done. */
- *ptr_mutex = mutex;
- return PJ_SUCCESS;
-}
-
-PJ_DEF(pj_status_t) pj_mutex_create_simple( pj_pool_t *pool, const char *name,
- pj_mutex_t **mutex )
-{
- return pj_mutex_create(pool, name, PJ_MUTEX_SIMPLE, mutex);
-}
-
-PJ_DEF(pj_status_t) pj_mutex_create_recursive( pj_pool_t *pool,
- const char *name,
- pj_mutex_t **mutex )
-{
- return pj_mutex_create( pool, name, PJ_MUTEX_RECURSE, mutex);
-}
-
-PJ_DEF(pj_status_t) pj_mutex_lock(pj_mutex_t *mutex)
-{
- PJ_ASSERT_RETURN(mutex, PJ_EINVAL);
-
- if (mutex->recursive) {
- pj_thread_t *this_thread = pj_thread_this();
- if (mutex->owner == this_thread) {
- ++mutex->own_count;
- } else {
- down(&mutex->sem);
- pj_assert(mutex->own_count == 0);
- mutex->owner = this_thread;
- mutex->own_count = 1;
- }
- } else {
- down(&mutex->sem);
- }
- return PJ_SUCCESS;
-}
-
-PJ_DEF(pj_status_t) pj_mutex_trylock(pj_mutex_t *mutex)
-{
- long rc;
-
- PJ_ASSERT_RETURN(mutex, PJ_EINVAL);
-
- if (mutex->recursive) {
- pj_thread_t *this_thread = pj_thread_this();
- if (mutex->owner == this_thread) {
- ++mutex->own_count;
- } else {
- rc = down_interruptible(&mutex->sem);
- if (rc != 0)
- return PJ_RETURN_OS_ERROR(-rc);
- pj_assert(mutex->own_count == 0);
- mutex->owner = this_thread;
- mutex->own_count = 1;
- }
- } else {
- int rc = down_trylock(&mutex->sem);
- if (rc != 0)
- return PJ_RETURN_OS_ERROR(-rc);
- }
- return PJ_SUCCESS;
-}
-
-PJ_DEF(pj_status_t) pj_mutex_unlock(pj_mutex_t *mutex)
-{
- PJ_ASSERT_RETURN(mutex, PJ_EINVAL);
-
- if (mutex->recursive) {
- pj_thread_t *this_thread = pj_thread_this();
- if (mutex->owner == this_thread) {
- pj_assert(mutex->own_count > 0);
- --mutex->own_count;
- if (mutex->own_count == 0) {
- mutex->owner = NULL;
- up(&mutex->sem);
- }
- } else {
- pj_assert(!"Not owner!");
- return PJ_EINVALIDOP;
- }
- } else {
- up(&mutex->sem);
- }
- return PJ_SUCCESS;
-}
-
-PJ_DEF(pj_status_t) pj_mutex_destroy(pj_mutex_t *mutex)
-{
- PJ_ASSERT_RETURN(mutex != NULL, PJ_EINVAL);
-
- return PJ_SUCCESS;
-}
-
-#if defined(PJ_DEBUG) && PJ_DEBUG != 0
-PJ_DEF(pj_bool_t) pj_mutex_is_locked(pj_mutex_t *mutex)
-{
- if (mutex->recursive)
- return mutex->owner == pj_thread_this();
- else
- return 1;
-}
-#endif /* PJ_DEBUG */
-
-
-#if defined(PJ_HAS_SEMAPHORE) && PJ_HAS_SEMAPHORE != 0
-
-PJ_DEF(pj_status_t) pj_sem_create( pj_pool_t *pool,
- const char *name,
- unsigned initial,
- unsigned max,
- pj_sem_t **sem)
-{
- pj_sem_t *sem;
-
- PJ_UNUSED_ARG(max);
-
- PJ_ASSERT_RETURN(pool && sem, PJ_EINVAL);
-
- sem = pj_pool_alloc(pool, sizeof(pj_sem_t));
- sema_init(&sem->sem, initial);
- return PJ_SUCCESS;
-}
-
-PJ_DEF(pj_status_t) pj_sem_wait(pj_sem_t *sem)
-{
- PJ_ASSERT_RETURN(pool && sem, PJ_EINVAL);
-
- down(&sem->sem);
- return PJ_SUCCESS;
-}
-
-PJ_DEF(pj_status_t) pj_sem_trywait(pj_sem_t *sem)
-{
- int rc;
-
- PJ_ASSERT_RETURN(pool && sem, PJ_EINVAL);
-
- rc = down_trylock(&sem->sem);
- if (rc != 0) {
- return PJ_RETURN_OS_ERROR(-rc);
- } else {
- return PJ_SUCCESS;
- }
-}
-
-PJ_DEF(pj_status_t) pj_sem_post(pj_sem_t *sem)
-{
- PJ_ASSERT_RETURN(pool && sem, PJ_EINVAL);
-
- up(&sem->sem);
- return PJ_SUCCESS;
-}
-
-PJ_DEF(pj_status_t) pj_sem_destroy(pj_sem_t *sem)
-{
- PJ_ASSERT_RETURN(pool && sem, PJ_EINVAL);
-
- return PJ_SUCCESS;
-}
-
-#endif /* PJ_HAS_SEMAPHORE */
-
-
-
-
+/* $Id$
+ *
+ */
+/*
+ * PJLIB - PJ Foundation Library
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ * Benny Prijono <bennylp@bulukucing.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; 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/assert.h>
+#include <pj/pool.h>
+#include <pj/log.h>
+#include <pj/except.h>
+#include <pj/errno.h>
+#include <pj/string.h>
+#include <pj/compat/high_precision.h>
+#include <pj/compat/sprintf.h>
+
+#include <linux/config.h>
+#include <linux/version.h>
+#if defined(MODVERSIONS)
+#include <linux/modversions.h>
+#endif
+#include <linux/kernel.h>
+#include <linux/sched.h>
+//#include <linux/tqueue.h>
+#include <linux/wait.h>
+#include <linux/signal.h>
+
+#include <asm/atomic.h>
+#include <asm/unistd.h>
+#include <asm/semaphore.h>
+
+#define THIS_FILE "oslinuxkern"
+
+struct pj_thread_t
+{
+ /** Thread's name. */
+ char obj_name[PJ_MAX_OBJ_NAME];
+
+ /** Linux task structure for thread. */
+ struct task_struct *thread;
+
+ /** Flags (specified in pj_thread_create) */
+ unsigned flags;
+
+ /** Task queue needed to launch thread. */
+ //struct tq_struct tq;
+
+ /** Semaphore needed to control thread startup. */
+ struct semaphore startstop_sem;
+
+ /** Semaphore to suspend thread during startup. */
+ struct semaphore suspend_sem;
+
+ /** Queue thread is waiting on. Gets initialized by
+ thread_initialize, can be used by thread itself.
+ */
+ wait_queue_head_t queue;
+
+ /** Flag to tell thread whether to die or not.
+ When the thread receives a signal, it must check
+ the value of terminate and call thread_deinitialize and terminate
+ if set.
+ */
+ int terminate;
+
+ /** Thread's entry. */
+ pj_thread_proc *func;
+
+ /** Argument. */
+ void *arg;
+};
+
+struct pj_atomic_t
+{
+ atomic_t atom;
+};
+
+struct pj_mutex_t
+{
+ struct semaphore sem;
+ pj_bool_t recursive;
+ pj_thread_t *owner;
+ int own_count;
+};
+
+struct pj_sem_t
+{
+ struct semaphore sem;
+};
+
+/*
+ * Static global variables.
+ */
+#define MAX_TLS_ID 32
+static void *tls_values[MAX_TLS_ID];
+static int tls_id;
+static long thread_tls_id;
+static spinlock_t critical_section = SPIN_LOCK_UNLOCKED;
+static unsigned long spinlock_flags;
+static pj_thread_t main_thread;
+
+/* private functions */
+//#define TRACE_(expr) PJ_LOG(3,expr)
+#define TRACE_(x)
+
+
+/* This must be called in the context of the new thread. */
+static void thread_initialize( pj_thread_t *thread )
+{
+ TRACE_((THIS_FILE, "---new thread initializing..."));
+
+ /* Set TLS */
+ pj_thread_local_set(thread_tls_id, thread);
+
+ /* fill in thread structure */
+ thread->thread = current;
+ pj_assert(thread->thread != NULL);
+
+ /* set signal mask to what we want to respond */
+ siginitsetinv(¤t->blocked,
+ sigmask(SIGKILL)|sigmask(SIGINT)|sigmask(SIGTERM));
+
+ /* initialise wait queue */
+ init_waitqueue_head(&thread->queue);
+
+ /* initialise termination flag */
+ thread->terminate = 0;
+
+ /* set name of this process (max 15 chars + 0 !) */
+ thread->obj_name[15] = '\0';
+ sprintf(current->comm, thread->obj_name);
+
+ /* tell the creator that we are ready and let him continue */
+ up(&thread->startstop_sem);
+}
+
+/* cleanup of thread. Called by the exiting thread. */
+static void thread_deinitialize(pj_thread_t *thread)
+{
+ /* we are terminating */
+
+ /* lock the kernel, the exit will unlock it */
+ thread->thread = NULL;
+ mb();
+
+ /* notify the stop_kthread() routine that we are terminating. */
+ up(&thread->startstop_sem);
+
+ /* the kernel_thread that called clone() does a do_exit here. */
+
+ /* there is no race here between execution of the "killer" and
+ real termination of the thread (race window between up and do_exit),
+ since both the thread and the "killer" function are running with
+ the kernel lock held.
+ The kernel lock will be freed after the thread exited, so the code
+ is really not executed anymore as soon as the unload functions gets
+ the kernel lock back.
+ The init process may not have made the cleanup of the process here,
+ but the cleanup can be done safely with the module unloaded.
+ */
+
+}
+
+static int thread_proc(void *arg)
+{
+ pj_thread_t *thread = arg;
+
+ TRACE_((THIS_FILE, "---new thread starting!"));
+
+ /* Initialize thread. */
+ thread_initialize( thread );
+
+ /* Wait if created suspended. */
+ if (thread->flags & PJ_THREAD_SUSPENDED) {
+ TRACE_((THIS_FILE, "---new thread suspended..."));
+ down(&thread->suspend_sem);
+ }
+
+ TRACE_((THIS_FILE, "---new thread running..."));
+
+ pj_assert(thread->func != NULL);
+
+ /* Call thread's entry. */
+ (*thread->func)(thread->arg);
+
+ TRACE_((THIS_FILE, "---thread exiting..."));
+
+ /* Cleanup thread. */
+ thread_deinitialize(thread);
+
+ return 0;
+}
+
+/* The very task entry. */
+static void kthread_launcher(void *arg)
+{
+ TRACE_((THIS_FILE, "...launching thread!..."));
+ kernel_thread(&thread_proc, arg, 0);
+}
+
+PJ_DEF(pj_status_t) pj_init(void)
+{
+ pj_status_t rc;
+
+ PJ_LOG(5, ("pj_init", "Initializing PJ Library.."));
+
+ rc = pj_thread_init();
+ if (rc != PJ_SUCCESS)
+ return rc;
+
+ /* Initialize exception ID for the pool.
+ * Must do so after critical section is configured.
+ */
+ rc = pj_exception_id_alloc("PJLIB/No memory", &PJ_NO_MEMORY_EXCEPTION);
+ if (rc != PJ_SUCCESS)
+ return rc;
+
+ return PJ_SUCCESS;
+}
+
+PJ_DEF(pj_uint32_t) pj_getpid(void)
+{
+ return 1;
+}
+
+PJ_DEF(pj_status_t) pj_thread_register ( const char *cstr_thread_name,
+ pj_thread_desc desc,
+ pj_thread_t **ptr_thread)
+{
+ char stack_ptr;
+ pj_thread_t *thread = (pj_thread_t *)desc;
+ pj_str_t thread_name = pj_str((char*)cstr_thread_name);
+
+ /* Size sanity check. */
+ if (sizeof(pj_thread_desc) < sizeof(pj_thread_t)) {
+ pj_assert(!"Not enough pj_thread_desc size!");
+ return PJ_EBUG;
+ }
+
+ /* If a thread descriptor has been registered before, just return it. */
+ if (pj_thread_local_get (thread_tls_id) != 0) {
+ *ptr_thread = (pj_thread_t*)pj_thread_local_get (thread_tls_id);
+ return PJ_SUCCESS;
+ }
+
+ /* Initialize and set the thread entry. */
+ pj_memset(desc, 0, sizeof(struct pj_thread_t));
+
+ if(cstr_thread_name && pj_strlen(&thread_name) < sizeof(thread->obj_name)-1)
+ pj_sprintf(thread->obj_name, cstr_thread_name, thread->thread);
+ else
+ pj_sprintf(thread->obj_name, "thr%p", (void*)thread->thread);
+
+ /* Initialize. */
+ thread_initialize(thread);
+
+ /* Eat semaphore. */
+ down(&thread->startstop_sem);
+
+#if defined(PJ_OS_HAS_CHECK_STACK) && PJ_OS_HAS_CHECK_STACK!=0
+ thread->stk_start = &stack_ptr;
+ thread->stk_size = 0xFFFFFFFFUL;
+ thread->stk_max_usage = 0;
+#else
+ stack_ptr = '\0';
+#endif
+
+ *ptr_thread = thread;
+ return PJ_SUCCESS;
+}
+
+
+pj_status_t pj_thread_init(void)
+{
+ pj_status_t rc;
+ pj_thread_t *dummy;
+
+ rc = pj_thread_local_alloc(&thread_tls_id);
+ if (rc != PJ_SUCCESS)
+ return rc;
+
+ return pj_thread_register("pjlib-main", (long*)&main_thread, &dummy);
+}
+
+PJ_DEF(pj_status_t) pj_thread_create( pj_pool_t *pool, const char *thread_name,
+ pj_thread_proc *proc, void *arg,
+ pj_size_t stack_size, unsigned flags,
+ pj_thread_t **ptr_thread)
+{
+ pj_thread_t *thread;
+
+ TRACE_((THIS_FILE, "pj_thread_create()"));
+
+ PJ_ASSERT_RETURN(pool && proc && ptr_thread, PJ_EINVAL);
+
+ thread = pj_pool_zalloc(pool, sizeof(pj_thread_t));
+ if (!thread)
+ return PJ_ENOMEM;
+
+ PJ_UNUSED_ARG(stack_size);
+
+ /* Thread name. */
+ if (!thread_name)
+ thread_name = "thr%p";
+
+ if (strchr(thread_name, '%')) {
+ pj_snprintf(thread->obj_name, PJ_MAX_OBJ_NAME, thread_name, thread);
+ } else {
+ strncpy(thread->obj_name, thread_name, PJ_MAX_OBJ_NAME);
+ thread->obj_name[PJ_MAX_OBJ_NAME-1] = '\0';
+ }
+
+ /* Init thread's semaphore. */
+ TRACE_((THIS_FILE, "...init semaphores..."));
+ init_MUTEX_LOCKED(&thread->startstop_sem);
+ init_MUTEX_LOCKED(&thread->suspend_sem);
+
+ thread->flags = flags;
+
+ if ((flags & PJ_THREAD_SUSPENDED) == 0) {
+ up(&thread->suspend_sem);
+ }
+
+ /* Store the functions and argument. */
+ thread->func = proc;
+ thread->arg = arg;
+
+ /* Save return value. */
+ *ptr_thread = thread;
+
+ /* Create the new thread by running a task through keventd. */
+
+#if 0
+ /* Initialize the task queue struct. */
+ thread->tq.sync = 0;
+ INIT_LIST_HEAD(&thread->tq.list);
+ thread->tq.routine = kthread_launcher;
+ thread->tq.data = thread;
+
+ /* and schedule it for execution. */
+ schedule_task(&thread->tq);
+#endif
+ kthread_launcher(thread);
+
+ /* Wait until thread has reached the setup_thread routine. */
+ TRACE_((THIS_FILE, "...wait for the new thread..."));
+ down(&thread->startstop_sem);
+
+ TRACE_((THIS_FILE, "...main thread resumed..."));
+ return PJ_SUCCESS;
+}
+
+PJ_DEF(const char*) pj_thread_get_name(pj_thread_t *thread)
+{
+ return thread->obj_name;
+}
+
+PJ_DEF(pj_status_t) pj_thread_resume(pj_thread_t *thread)
+{
+ up(&thread->suspend_sem);
+ return PJ_SUCCESS;
+}
+
+PJ_DEF(pj_thread_t*) pj_thread_this(void)
+{
+ return (pj_thread_t*)pj_thread_local_get(thread_tls_id);
+}
+
+PJ_DEF(pj_status_t) pj_thread_join(pj_thread_t *p)
+{
+ TRACE_((THIS_FILE, "pj_thread_join()"));
+ down(&p->startstop_sem);
+ TRACE_((THIS_FILE, " joined!"));
+ return PJ_SUCCESS;
+}
+
+PJ_DEF(pj_status_t) pj_thread_destroy(pj_thread_t *thread)
+{
+ PJ_ASSERT_RETURN(thread != NULL, PJ_EINVALIDOP);
+ return PJ_SUCCESS;
+}
+
+PJ_DEF(pj_status_t) pj_thread_sleep(unsigned msec)
+{
+ pj_highprec_t ticks;
+ pj_thread_t *thread = pj_thread_this();
+
+ PJ_ASSERT_RETURN(thread != NULL, PJ_EBUG);
+
+ /* Use high precision calculation to make sure we don't
+ * crop values:
+ *
+ * ticks = HZ * msec / 1000
+ */
+ ticks = HZ;
+ pj_highprec_mul(ticks, msec);
+ pj_highprec_div(ticks, 1000);
+
+ TRACE_((THIS_FILE, "this thread will sleep for %u ticks", ticks));
+ interruptible_sleep_on_timeout( &thread->queue, ticks);
+ return PJ_SUCCESS;
+}
+
+
+///////////////////////////////////////////////////////////////////////////////
+PJ_DEF(pj_status_t) pj_atomic_create( pj_pool_t *pool,
+ pj_atomic_value_t value,
+ pj_atomic_t **ptr_var)
+{
+ pj_atomic_t *t = pj_pool_calloc(pool, 1, sizeof(pj_atomic_t));
+ if (!t) return PJ_ENOMEM;
+
+ atomic_set(&t->atom, value);
+ *ptr_var = t;
+
+ return PJ_SUCCESS;
+}
+
+PJ_DEF(pj_status_t) pj_atomic_destroy( pj_atomic_t *var )
+{
+ return PJ_SUCCESS;
+}
+
+PJ_DEF(void) pj_atomic_set(pj_atomic_t *var, pj_atomic_value_t value)
+{
+ atomic_set(&var->atom, value);
+}
+
+PJ_DEF(pj_atomic_value_t) pj_atomic_get(pj_atomic_t *var)
+{
+ return atomic_read(&var->atom);
+}
+
+PJ_DEF(void) pj_atomic_inc(pj_atomic_t *var)
+{
+ atomic_inc(&var->atom);
+}
+
+PJ_DEF(void) pj_atomic_dec(pj_atomic_t *var)
+{
+ atomic_dec(&var->atom);
+}
+
+PJ_DEF(void) pj_atomic_add( pj_atomic_t *var, pj_atomic_value_t value )
+{
+ atomic_add(value, &var->atom);
+}
+
+
+///////////////////////////////////////////////////////////////////////////////
+PJ_DEF(pj_status_t) pj_thread_local_alloc(long *index)
+{
+ if (tls_id >= MAX_TLS_ID)
+ return PJ_ETOOMANY;
+
+ *index = tls_id++;
+
+ return PJ_SUCCESS;
+}
+
+PJ_DEF(void) pj_thread_local_free(long index)
+{
+ pj_assert(index >= 0 && index < MAX_TLS_ID);
+}
+
+PJ_DEF(pj_status_t) pj_thread_local_set(long index, void *value)
+{
+ pj_assert(index >= 0 && index < MAX_TLS_ID);
+ tls_values[index] = value;
+ return PJ_SUCCESS;
+}
+
+PJ_DEF(void*) pj_thread_local_get(long index)
+{
+ pj_assert(index >= 0 && index < MAX_TLS_ID);
+ return tls_values[index];
+}
+
+
+///////////////////////////////////////////////////////////////////////////////
+PJ_DEF(void) pj_enter_critical_section(void)
+{
+ spin_lock_irqsave(&critical_section, spinlock_flags);
+}
+
+PJ_DEF(void) pj_leave_critical_section(void)
+{
+ spin_unlock_irqrestore(&critical_section, spinlock_flags);
+}
+
+
+///////////////////////////////////////////////////////////////////////////////
+PJ_DEF(pj_status_t) pj_mutex_create( pj_pool_t *pool,
+ const char *name,
+ int type,
+ pj_mutex_t **ptr_mutex)
+{
+ pj_mutex_t *mutex;
+
+ PJ_UNUSED_ARG(name);
+
+ mutex = pj_pool_alloc(pool, sizeof(pj_mutex_t));
+ if (!mutex)
+ return PJ_ENOMEM;
+
+ init_MUTEX(&mutex->sem);
+
+ mutex->recursive = (type == PJ_MUTEX_RECURSE);
+ mutex->owner = NULL;
+ mutex->own_count = 0;
+
+ /* Done. */
+ *ptr_mutex = mutex;
+ return PJ_SUCCESS;
+}
+
+PJ_DEF(pj_status_t) pj_mutex_create_simple( pj_pool_t *pool, const char *name,
+ pj_mutex_t **mutex )
+{
+ return pj_mutex_create(pool, name, PJ_MUTEX_SIMPLE, mutex);
+}
+
+PJ_DEF(pj_status_t) pj_mutex_create_recursive( pj_pool_t *pool,
+ const char *name,
+ pj_mutex_t **mutex )
+{
+ return pj_mutex_create( pool, name, PJ_MUTEX_RECURSE, mutex);
+}
+
+PJ_DEF(pj_status_t) pj_mutex_lock(pj_mutex_t *mutex)
+{
+ PJ_ASSERT_RETURN(mutex, PJ_EINVAL);
+
+ if (mutex->recursive) {
+ pj_thread_t *this_thread = pj_thread_this();
+ if (mutex->owner == this_thread) {
+ ++mutex->own_count;
+ } else {
+ down(&mutex->sem);
+ pj_assert(mutex->own_count == 0);
+ mutex->owner = this_thread;
+ mutex->own_count = 1;
+ }
+ } else {
+ down(&mutex->sem);
+ }
+ return PJ_SUCCESS;
+}
+
+PJ_DEF(pj_status_t) pj_mutex_trylock(pj_mutex_t *mutex)
+{
+ long rc;
+
+ PJ_ASSERT_RETURN(mutex, PJ_EINVAL);
+
+ if (mutex->recursive) {
+ pj_thread_t *this_thread = pj_thread_this();
+ if (mutex->owner == this_thread) {
+ ++mutex->own_count;
+ } else {
+ rc = down_interruptible(&mutex->sem);
+ if (rc != 0)
+ return PJ_RETURN_OS_ERROR(-rc);
+ pj_assert(mutex->own_count == 0);
+ mutex->owner = this_thread;
+ mutex->own_count = 1;
+ }
+ } else {
+ int rc = down_trylock(&mutex->sem);
+ if (rc != 0)
+ return PJ_RETURN_OS_ERROR(-rc);
+ }
+ return PJ_SUCCESS;
+}
+
+PJ_DEF(pj_status_t) pj_mutex_unlock(pj_mutex_t *mutex)
+{
+ PJ_ASSERT_RETURN(mutex, PJ_EINVAL);
+
+ if (mutex->recursive) {
+ pj_thread_t *this_thread = pj_thread_this();
+ if (mutex->owner == this_thread) {
+ pj_assert(mutex->own_count > 0);
+ --mutex->own_count;
+ if (mutex->own_count == 0) {
+ mutex->owner = NULL;
+ up(&mutex->sem);
+ }
+ } else {
+ pj_assert(!"Not owner!");
+ return PJ_EINVALIDOP;
+ }
+ } else {
+ up(&mutex->sem);
+ }
+ return PJ_SUCCESS;
+}
+
+PJ_DEF(pj_status_t) pj_mutex_destroy(pj_mutex_t *mutex)
+{
+ PJ_ASSERT_RETURN(mutex != NULL, PJ_EINVAL);
+
+ return PJ_SUCCESS;
+}
+
+#if defined(PJ_DEBUG) && PJ_DEBUG != 0
+PJ_DEF(pj_bool_t) pj_mutex_is_locked(pj_mutex_t *mutex)
+{
+ if (mutex->recursive)
+ return mutex->owner == pj_thread_this();
+ else
+ return 1;
+}
+#endif /* PJ_DEBUG */
+
+
+#if defined(PJ_HAS_SEMAPHORE) && PJ_HAS_SEMAPHORE != 0
+
+PJ_DEF(pj_status_t) pj_sem_create( pj_pool_t *pool,
+ const char *name,
+ unsigned initial,
+ unsigned max,
+ pj_sem_t **sem)
+{
+ pj_sem_t *sem;
+
+ PJ_UNUSED_ARG(max);
+
+ PJ_ASSERT_RETURN(pool && sem, PJ_EINVAL);
+
+ sem = pj_pool_alloc(pool, sizeof(pj_sem_t));
+ sema_init(&sem->sem, initial);
+ return PJ_SUCCESS;
+}
+
+PJ_DEF(pj_status_t) pj_sem_wait(pj_sem_t *sem)
+{
+ PJ_ASSERT_RETURN(pool && sem, PJ_EINVAL);
+
+ down(&sem->sem);
+ return PJ_SUCCESS;
+}
+
+PJ_DEF(pj_status_t) pj_sem_trywait(pj_sem_t *sem)
+{
+ int rc;
+
+ PJ_ASSERT_RETURN(pool && sem, PJ_EINVAL);
+
+ rc = down_trylock(&sem->sem);
+ if (rc != 0) {
+ return PJ_RETURN_OS_ERROR(-rc);
+ } else {
+ return PJ_SUCCESS;
+ }
+}
+
+PJ_DEF(pj_status_t) pj_sem_post(pj_sem_t *sem)
+{
+ PJ_ASSERT_RETURN(pool && sem, PJ_EINVAL);
+
+ up(&sem->sem);
+ return PJ_SUCCESS;
+}
+
+PJ_DEF(pj_status_t) pj_sem_destroy(pj_sem_t *sem)
+{
+ PJ_ASSERT_RETURN(pool && sem, PJ_EINVAL);
+
+ return PJ_SUCCESS;
+}
+
+#endif /* PJ_HAS_SEMAPHORE */
+
+
+
+
diff --git a/pjlib/src/pj/os_core_unix.c b/pjlib/src/pj/os_core_unix.c
index cc57aab..5956adb 100644
--- a/pjlib/src/pj/os_core_unix.c
+++ b/pjlib/src/pj/os_core_unix.c
@@ -1,563 +1,584 @@
-/* $Id$
- *
- */
-#include <pj/os.h>
-#include <pj/assert.h>
-#include <pj/pool.h>
-#include <pj/log.h>
-#include <pj/rand.h>
-#include <pj/string.h>
-#include <pj/guid.h>
-#include <pj/compat/sprintf.h>
-#include <pj/except.h>
-#include <pj/errno.h>
-
-#if defined(PJ_HAS_SEMAPHORE) && PJ_HAS_SEMAPHORE != 0
-# include <semaphore.h>
-#endif
-
-#include <unistd.h> // getpid()
-#include <errno.h> // errno
-
-#define __USE_GNU
-//uncomment this to get pthread_mutexattr_settype declaration.
-//unfortunately this causes syntax error in pthread.h! :(
-//#define __USE_UNIX98
-#include <pthread.h>
-
-#define THIS_FILE "osunix"
-
-struct pj_thread_t
-{
- char obj_name[PJ_MAX_OBJ_NAME];
- pthread_t thread;
- pj_thread_proc *proc;
- void *arg;
-
- pj_mutex_t *suspended_mutex;
-
-#if defined(PJ_OS_HAS_CHECK_STACK) && PJ_OS_HAS_CHECK_STACK!=0
- pj_uint32_t stk_size;
- pj_uint32_t stk_max_usage;
- char *stk_start;
- const char *caller_file;
- int caller_line;
-#endif
-};
-
-struct pj_atomic_t
-{
- pj_mutex_t *mutex;
- pj_atomic_value_t value;
-};
-
-struct pj_mutex_t
-{
- pthread_mutex_t mutex;
- char obj_name[PJ_MAX_OBJ_NAME];
-#if PJ_DEBUG
- int nesting_level;
- pj_thread_t *owner;
-#endif
-};
-
-#if defined(PJ_HAS_SEMAPHORE) && PJ_HAS_SEMAPHORE != 0
-struct pj_sem_t
-{
- sem_t sem;
- char obj_name[PJ_MAX_OBJ_NAME];
-};
-#endif /* PJ_HAS_SEMAPHORE */
-
-#if defined(PJ_HAS_EVENT_OBJ) && PJ_HAS_EVENT_OBJ != 0
-struct pj_event_t
-{
- char obj_name[PJ_MAX_OBJ_NAME];
-};
-#endif /* PJ_HAS_EVENT_OBJ */
-
-
-#if PJ_HAS_THREADS
- static pj_thread_t main_thread;
- static long thread_tls_id;
- static pj_mutex_t critical_section;
-#else
-# define MAX_THREADS 32
- static int tls_flag[MAX_THREADS];
- static void *tls[MAX_THREADS];
-#endif
-
-static pj_status_t init_mutex(pj_mutex_t *mutex, const char *name, int type);
-
-/*
- * pj_init(void).
- * Init PJLIB!
- */
-PJ_DEF(pj_status_t) pj_init(void)
-{
- char dummy_guid[PJ_GUID_MAX_LENGTH];
- pj_str_t guid;
- pj_status_t rc;
-
- PJ_LOG(5, ("pj_init", "Initializing PJ Library.."));
-
-#if PJ_HAS_THREADS
- /* Init this thread's TLS. */
- if ((rc=pj_thread_init()) != 0) {
- return rc;
- }
-
- /* Critical section. */
- if ((rc=init_mutex(&critical_section, "critsec", PJ_MUTEX_SIMPLE)) != 0)
- return rc;
-
-#endif
-
- /* Initialize exception ID for the pool.
- * Must do so after critical section is configured.
- */
- rc = pj_exception_id_alloc("PJLIB/No memory", &PJ_NO_MEMORY_EXCEPTION);
- if (rc != PJ_SUCCESS)
- return rc;
-
- /* Init random seed. */
- pj_srand( clock() );
-
- /* Startup GUID. */
- guid.ptr = dummy_guid;
- pj_generate_unique_string( &guid );
-
- /* Initialize exception ID for the pool.
- * Must do so after critical section is configured.
- */
- rc = pj_exception_id_alloc("PJLIB/No memory", &PJ_NO_MEMORY_EXCEPTION);
- if (rc != PJ_SUCCESS)
- return rc;
-
- /* Startup timestamp */
-#if defined(PJ_HAS_HIGH_RES_TIMER) && PJ_HAS_HIGH_RES_TIMER != 0
- {
- pj_timestamp dummy_ts;
- if ((rc=pj_get_timestamp(&dummy_ts)) != 0) {
- return rc;
- }
- }
-#endif
-
- return PJ_SUCCESS;
-}
-
-/*
- * pj_getpid(void)
- */
-PJ_DEF(pj_uint32_t) pj_getpid(void)
-{
- PJ_CHECK_STACK();
- return getpid();
-}
-
-/*
- * pj_thread_register(..)
- */
-PJ_DEF(pj_status_t) pj_thread_register ( const char *cstr_thread_name,
- pj_thread_desc desc,
- pj_thread_t **ptr_thread)
-{
-#if PJ_HAS_THREADS
- char stack_ptr;
- pj_status_t rc;
- pj_thread_t *thread = (pj_thread_t *)desc;
- pj_str_t thread_name = pj_str((char*)cstr_thread_name);
-
- /* Size sanity check. */
- if (sizeof(pj_thread_desc) < sizeof(pj_thread_t)) {
- pj_assert(!"Not enough pj_thread_desc size!");
- return PJ_EBUG;
- }
-
- /* If a thread descriptor has been registered before, just return it. */
- if (pj_thread_local_get (thread_tls_id) != 0) {
- *ptr_thread = (pj_thread_t*)pj_thread_local_get (thread_tls_id);
- return PJ_SUCCESS;
- }
-
- /* Initialize and set the thread entry. */
- pj_memset(desc, 0, sizeof(struct pj_thread_t));
- thread->thread = pthread_self();
-
- if(cstr_thread_name && pj_strlen(&thread_name) < sizeof(thread->obj_name)-1)
- pj_sprintf(thread->obj_name, cstr_thread_name, thread->thread);
- else
- pj_sprintf(thread->obj_name, "thr%p", (void*)thread->thread);
-
- rc = pj_thread_local_set(thread_tls_id, thread);
- if (rc != PJ_SUCCESS)
- return rc;
-
-#if defined(PJ_OS_HAS_CHECK_STACK) && PJ_OS_HAS_CHECK_STACK!=0
- thread->stk_start = &stack_ptr;
- thread->stk_size = 0xFFFFFFFFUL;
- thread->stk_max_usage = 0;
-#else
- stack_ptr = '\0';
-#endif
-
- *ptr_thread = thread;
- return PJ_SUCCESS;
-#else
- pj_thread_t *thread = (pj_thread_t*)desc;
- *ptr_thread = thread;
- return PJ_SUCCESS;
-#endif
-}
-
-/*
- * pj_thread_init(void)
- */
-pj_status_t pj_thread_init(void)
-{
-#if PJ_HAS_THREADS
- pj_status_t rc;
- pj_thread_t *dummy;
-
- rc = pj_thread_local_alloc(&thread_tls_id );
- if (rc != PJ_SUCCESS) {
- return rc;
- }
- return pj_thread_register("thr%p", (long*)&main_thread, &dummy);
-#else
- PJ_LOG(2,(THIS_FILE, "Thread init error. Threading is not enabled!"));
- return PJ_EINVALIDOP;
-#endif
-}
-
-#if PJ_HAS_THREADS
-/*
- * thread_main()
- *
- * This is the main entry for all threads.
- */
-static void *thread_main(void *param)
-{
- pj_thread_t *rec = param;
- void *result;
- pj_status_t rc;
-
-#if defined(PJ_OS_HAS_CHECK_STACK) && PJ_OS_HAS_CHECK_STACK!=0
- rec->stk_start = (char*)&rec;
-#endif
-
- /* Set current thread id. */
- rc = pj_thread_local_set(thread_tls_id, rec);
- if (rc != PJ_SUCCESS) {
- pj_assert(!"Thread TLS ID is not set (pj_init() error?)");
- }
-
- /* Check if suspension is required. */
- if (rec->suspended_mutex)
- pj_mutex_lock(rec->suspended_mutex);
-
- PJ_LOG(6,(rec->obj_name, "Thread started"));
-
- /* Call user's entry! */
- result = (void*)(long)(*rec->proc)(rec->arg);
-
- /* Done. */
- PJ_LOG(6,(rec->obj_name, "Thread quitting"));
- return result;
-}
-#endif
-
-/*
- * pj_thread_create(...)
- */
-PJ_DEF(pj_status_t) pj_thread_create( pj_pool_t *pool,
- const char *thread_name,
- pj_thread_proc *proc,
- void *arg,
- pj_size_t stack_size,
- unsigned flags,
- pj_thread_t **ptr_thread)
-{
-#if PJ_HAS_THREADS
- pj_thread_t *rec;
- int rc;
-
- PJ_CHECK_STACK();
- PJ_ASSERT_RETURN(pool && proc && ptr_thread, PJ_EINVAL);
-
- /* Create thread record and assign name for the thread */
- rec = (struct pj_thread_t*) pj_pool_zalloc(pool, sizeof(pj_thread_t));
- if (!rec)
- return PJ_ENOMEM;
-
- /* Set name. */
- if (!thread_name)
- thread_name = "thr%p";
-
- if (strchr(thread_name, '%')) {
- pj_snprintf(rec->obj_name, PJ_MAX_OBJ_NAME, thread_name, rec);
- } else {
- strncpy(rec->obj_name, thread_name, PJ_MAX_OBJ_NAME);
- rec->obj_name[PJ_MAX_OBJ_NAME-1] = '\0';
- }
-
-#if defined(PJ_OS_HAS_CHECK_STACK) && PJ_OS_HAS_CHECK_STACK!=0
- rec->stk_size = stack_size ? stack_size : 0xFFFFFFFFUL;
- rec->stk_max_usage = 0;
-#endif
-
- /* Emulate suspended thread with mutex. */
- if (flags & PJ_THREAD_SUSPENDED) {
- rc = pj_mutex_create_simple(pool, NULL, &rec->suspended_mutex);
- if (rc != PJ_SUCCESS)
- return rc;
-
- pj_mutex_lock(rec->suspended_mutex);
- } else {
- pj_assert(rec->suspended_mutex == NULL);
- }
-
- PJ_LOG(6, (rec->obj_name, "Thread created"));
-
- /* Create the thread. */
- rec->proc = proc;
- rec->arg = arg;
- rc = pthread_create( &rec->thread, NULL, thread_main, rec);
- if (rc != 0)
- return PJ_RETURN_OS_ERROR(rc);
-
- *ptr_thread = rec;
- return PJ_SUCCESS;
-#else
- pj_assert(!"Threading is disabled!");
- return PJ_EINVALIDOP;
-#endif
-}
-
-/*
- * pj_thread-get_name()
- */
-PJ_DEF(const char*) pj_thread_get_name(pj_thread_t *p)
-{
-#if PJ_HAS_THREADS
- pj_thread_t *rec = (pj_thread_t*)p;
-
- PJ_CHECK_STACK();
- PJ_ASSERT_RETURN(p, "");
-
- return rec->obj_name;
-#else
- return "";
-#endif
-}
-
-/*
- * pj_thread_resume()
- */
-PJ_DEF(pj_status_t) pj_thread_resume(pj_thread_t *p)
-{
- pj_status_t rc;
-
- PJ_CHECK_STACK();
- PJ_ASSERT_RETURN(p, PJ_EINVAL);
-
- rc = pj_mutex_unlock(p->suspended_mutex);
-
- return rc;
-}
-
-/*
- * pj_thread_this()
- */
-PJ_DEF(pj_thread_t*) pj_thread_this(void)
-{
-#if PJ_HAS_THREADS
- pj_thread_t *rec = pj_thread_local_get(thread_tls_id);
- pj_assert(rec != NULL);
-
- /*
- * MUST NOT check stack because this function is called
- * by PJ_CHECK_STACK() itself!!!
- *
- */
-
- return rec;
-#else
- pj_assert(!"Threading is not enabled!");
- return NULL;
-#endif
-}
-
-/*
- * pj_thread_join()
- */
-PJ_DEF(pj_status_t) pj_thread_join(pj_thread_t *p)
-{
-#if PJ_HAS_THREADS
- pj_thread_t *rec = (pj_thread_t *)p;
- void *ret;
- int result;
-
- PJ_CHECK_STACK();
-
- PJ_LOG(6, (pj_thread_this()->obj_name, "Joining thread %s", p->obj_name));
- result = pthread_join( rec->thread, &ret);
-
- if (result == 0)
- return PJ_SUCCESS;
- else
- return PJ_RETURN_OS_ERROR(result);
-#else
- PJ_CHECK_STACK();
- pj_assert(!"No multithreading support!");
- return PJ_EINVALIDOP;
-#endif
-}
-
-/*
- * pj_thread_destroy()
- */
-PJ_DEF(pj_status_t) pj_thread_destroy(pj_thread_t *p)
-{
- /* This function is used to destroy thread handle in other platforms.
- * I suppose there's nothing to do here..
- */
- PJ_CHECK_STACK();
- return PJ_SUCCESS;
-}
-
-/*
- * pj_thread_sleep()
- */
-PJ_DEF(pj_status_t) pj_thread_sleep(unsigned msec)
-{
- PJ_CHECK_STACK();
- return usleep(msec * 1000);
-}
-
-#if defined(PJ_OS_HAS_CHECK_STACK) && PJ_OS_HAS_CHECK_STACK!=0
-/*
- * pj_thread_check_stack()
- * Implementation for PJ_CHECK_STACK()
- */
-PJ_DEF(void) pj_thread_check_stack(const char *file, int line)
-{
- char stk_ptr;
- pj_uint32_t usage;
- pj_thread_t *thread = pj_thread_this();
-
- /* Calculate current usage. */
- usage = (&stk_ptr > thread->stk_start) ? &stk_ptr - thread->stk_start :
- thread->stk_start - &stk_ptr;
-
- /* Assert if stack usage is dangerously high. */
- pj_assert("STACK OVERFLOW!! " && (usage <= thread->stk_size - 128));
-
- /* Keep statistic. */
- if (usage > thread->stk_max_usage) {
- thread->stk_max_usage = usage;
- thread->caller_file = file;
- thread->caller_line = line;
- }
-}
-
-/*
- * pj_thread_get_stack_max_usage()
- */
-PJ_DEF(pj_uint32_t) pj_thread_get_stack_max_usage(pj_thread_t *thread)
-{
- return thread->stk_max_usage;
-}
-
-/*
- * pj_thread_get_stack_info()
- */
-PJ_DEF(pj_status_t) pj_thread_get_stack_info( pj_thread_t *thread,
- const char **file,
- int *line )
-{
- pj_assert(thread);
-
- *file = thread->caller_file;
- *line = thread->caller_line;
- return 0;
-}
-
-#endif /* PJ_OS_HAS_CHECK_STACK */
-
-///////////////////////////////////////////////////////////////////////////////
-/*
- * pj_atomic_create()
- */
-PJ_DEF(pj_status_t) pj_atomic_create( pj_pool_t *pool,
- pj_atomic_value_t initial,
- pj_atomic_t **ptr_atomic)
-{
- pj_status_t rc;
- pj_atomic_t *atomic_var = pj_pool_calloc(pool, 1, sizeof(pj_atomic_t));
- if (!atomic_var)
- return PJ_ENOMEM;
-
-#if PJ_HAS_THREADS
- rc = pj_mutex_create(pool, "atm%p", PJ_MUTEX_SIMPLE, &atomic_var->mutex);
- if (rc != PJ_SUCCESS)
- return rc;
-#endif
- atomic_var->value = initial;
-
- *ptr_atomic = atomic_var;
- return PJ_SUCCESS;
-}
-
-/*
- * pj_atomic_destroy()
- */
-PJ_DEF(pj_status_t) pj_atomic_destroy( pj_atomic_t *atomic_var )
-{
- PJ_ASSERT_RETURN(atomic_var, PJ_EINVAL);
-#if PJ_HAS_THREADS
- return pj_mutex_destroy( atomic_var->mutex );
-#else
- return 0;
-#endif
-}
-
-/*
- * pj_atomic_set()
- */
-PJ_DEF(void) pj_atomic_set(pj_atomic_t *atomic_var, pj_atomic_value_t value)
-{
- PJ_CHECK_STACK();
-
-#if PJ_HAS_THREADS
- pj_mutex_lock( atomic_var->mutex );
-#endif
- atomic_var->value = value;
-#if PJ_HAS_THREADS
- pj_mutex_unlock( atomic_var->mutex);
-#endif
-}
-
-/*
- * pj_atomic_get()
- */
-PJ_DEF(pj_atomic_value_t) pj_atomic_get(pj_atomic_t *atomic_var)
-{
- pj_atomic_value_t oldval;
-
- PJ_CHECK_STACK();
-
-#if PJ_HAS_THREADS
- pj_mutex_lock( atomic_var->mutex );
-#endif
- oldval = atomic_var->value;
-#if PJ_HAS_THREADS
- pj_mutex_unlock( atomic_var->mutex);
-#endif
- return oldval;
-}
-
+/* $Id$
+ *
+ */
+/*
+ * PJLIB - PJ Foundation Library
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ * Benny Prijono <bennylp@bulukucing.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; 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/assert.h>
+#include <pj/pool.h>
+#include <pj/log.h>
+#include <pj/rand.h>
+#include <pj/string.h>
+#include <pj/guid.h>
+#include <pj/compat/sprintf.h>
+#include <pj/except.h>
+#include <pj/errno.h>
+
+#if defined(PJ_HAS_SEMAPHORE) && PJ_HAS_SEMAPHORE != 0
+# include <semaphore.h>
+#endif
+
+#include <unistd.h> // getpid()
+#include <errno.h> // errno
+
+#define __USE_GNU
+//uncomment this to get pthread_mutexattr_settype declaration.
+//unfortunately this causes syntax error in pthread.h! :(
+//#define __USE_UNIX98
+#include <pthread.h>
+
+#define THIS_FILE "osunix"
+
+struct pj_thread_t
+{
+ char obj_name[PJ_MAX_OBJ_NAME];
+ pthread_t thread;
+ pj_thread_proc *proc;
+ void *arg;
+
+ pj_mutex_t *suspended_mutex;
+
+#if defined(PJ_OS_HAS_CHECK_STACK) && PJ_OS_HAS_CHECK_STACK!=0
+ pj_uint32_t stk_size;
+ pj_uint32_t stk_max_usage;
+ char *stk_start;
+ const char *caller_file;
+ int caller_line;
+#endif
+};
+
+struct pj_atomic_t
+{
+ pj_mutex_t *mutex;
+ pj_atomic_value_t value;
+};
+
+struct pj_mutex_t
+{
+ pthread_mutex_t mutex;
+ char obj_name[PJ_MAX_OBJ_NAME];
+#if PJ_DEBUG
+ int nesting_level;
+ pj_thread_t *owner;
+#endif
+};
+
+#if defined(PJ_HAS_SEMAPHORE) && PJ_HAS_SEMAPHORE != 0
+struct pj_sem_t
+{
+ sem_t sem;
+ char obj_name[PJ_MAX_OBJ_NAME];
+};
+#endif /* PJ_HAS_SEMAPHORE */
+
+#if defined(PJ_HAS_EVENT_OBJ) && PJ_HAS_EVENT_OBJ != 0
+struct pj_event_t
+{
+ char obj_name[PJ_MAX_OBJ_NAME];
+};
+#endif /* PJ_HAS_EVENT_OBJ */
+
+
+#if PJ_HAS_THREADS
+ static pj_thread_t main_thread;
+ static long thread_tls_id;
+ static pj_mutex_t critical_section;
+#else
+# define MAX_THREADS 32
+ static int tls_flag[MAX_THREADS];
+ static void *tls[MAX_THREADS];
+#endif
+
+static pj_status_t init_mutex(pj_mutex_t *mutex, const char *name, int type);
+
+/*
+ * pj_init(void).
+ * Init PJLIB!
+ */
+PJ_DEF(pj_status_t) pj_init(void)
+{
+ char dummy_guid[PJ_GUID_MAX_LENGTH];
+ pj_str_t guid;
+ pj_status_t rc;
+
+ PJ_LOG(5, ("pj_init", "Initializing PJ Library.."));
+
+#if PJ_HAS_THREADS
+ /* Init this thread's TLS. */
+ if ((rc=pj_thread_init()) != 0) {
+ return rc;
+ }
+
+ /* Critical section. */
+ if ((rc=init_mutex(&critical_section, "critsec", PJ_MUTEX_SIMPLE)) != 0)
+ return rc;
+
+#endif
+
+ /* Initialize exception ID for the pool.
+ * Must do so after critical section is configured.
+ */
+ rc = pj_exception_id_alloc("PJLIB/No memory", &PJ_NO_MEMORY_EXCEPTION);
+ if (rc != PJ_SUCCESS)
+ return rc;
+
+ /* Init random seed. */
+ pj_srand( clock() );
+
+ /* Startup GUID. */
+ guid.ptr = dummy_guid;
+ pj_generate_unique_string( &guid );
+
+ /* Initialize exception ID for the pool.
+ * Must do so after critical section is configured.
+ */
+ rc = pj_exception_id_alloc("PJLIB/No memory", &PJ_NO_MEMORY_EXCEPTION);
+ if (rc != PJ_SUCCESS)
+ return rc;
+
+ /* Startup timestamp */
+#if defined(PJ_HAS_HIGH_RES_TIMER) && PJ_HAS_HIGH_RES_TIMER != 0
+ {
+ pj_timestamp dummy_ts;
+ if ((rc=pj_get_timestamp(&dummy_ts)) != 0) {
+ return rc;
+ }
+ }
+#endif
+
+ return PJ_SUCCESS;
+}
+
+/*
+ * pj_getpid(void)
+ */
+PJ_DEF(pj_uint32_t) pj_getpid(void)
+{
+ PJ_CHECK_STACK();
+ return getpid();
+}
+
+/*
+ * pj_thread_register(..)
+ */
+PJ_DEF(pj_status_t) pj_thread_register ( const char *cstr_thread_name,
+ pj_thread_desc desc,
+ pj_thread_t **ptr_thread)
+{
+#if PJ_HAS_THREADS
+ char stack_ptr;
+ pj_status_t rc;
+ pj_thread_t *thread = (pj_thread_t *)desc;
+ pj_str_t thread_name = pj_str((char*)cstr_thread_name);
+
+ /* Size sanity check. */
+ if (sizeof(pj_thread_desc) < sizeof(pj_thread_t)) {
+ pj_assert(!"Not enough pj_thread_desc size!");
+ return PJ_EBUG;
+ }
+
+ /* If a thread descriptor has been registered before, just return it. */
+ if (pj_thread_local_get (thread_tls_id) != 0) {
+ *ptr_thread = (pj_thread_t*)pj_thread_local_get (thread_tls_id);
+ return PJ_SUCCESS;
+ }
+
+ /* Initialize and set the thread entry. */
+ pj_memset(desc, 0, sizeof(struct pj_thread_t));
+ thread->thread = pthread_self();
+
+ if(cstr_thread_name && pj_strlen(&thread_name) < sizeof(thread->obj_name)-1)
+ pj_sprintf(thread->obj_name, cstr_thread_name, thread->thread);
+ else
+ pj_sprintf(thread->obj_name, "thr%p", (void*)thread->thread);
+
+ rc = pj_thread_local_set(thread_tls_id, thread);
+ if (rc != PJ_SUCCESS)
+ return rc;
+
+#if defined(PJ_OS_HAS_CHECK_STACK) && PJ_OS_HAS_CHECK_STACK!=0
+ thread->stk_start = &stack_ptr;
+ thread->stk_size = 0xFFFFFFFFUL;
+ thread->stk_max_usage = 0;
+#else
+ stack_ptr = '\0';
+#endif
+
+ *ptr_thread = thread;
+ return PJ_SUCCESS;
+#else
+ pj_thread_t *thread = (pj_thread_t*)desc;
+ *ptr_thread = thread;
+ return PJ_SUCCESS;
+#endif
+}
+
+/*
+ * pj_thread_init(void)
+ */
+pj_status_t pj_thread_init(void)
+{
+#if PJ_HAS_THREADS
+ pj_status_t rc;
+ pj_thread_t *dummy;
+
+ rc = pj_thread_local_alloc(&thread_tls_id );
+ if (rc != PJ_SUCCESS) {
+ return rc;
+ }
+ return pj_thread_register("thr%p", (long*)&main_thread, &dummy);
+#else
+ PJ_LOG(2,(THIS_FILE, "Thread init error. Threading is not enabled!"));
+ return PJ_EINVALIDOP;
+#endif
+}
+
+#if PJ_HAS_THREADS
+/*
+ * thread_main()
+ *
+ * This is the main entry for all threads.
+ */
+static void *thread_main(void *param)
+{
+ pj_thread_t *rec = param;
+ void *result;
+ pj_status_t rc;
+
+#if defined(PJ_OS_HAS_CHECK_STACK) && PJ_OS_HAS_CHECK_STACK!=0
+ rec->stk_start = (char*)&rec;
+#endif
+
+ /* Set current thread id. */
+ rc = pj_thread_local_set(thread_tls_id, rec);
+ if (rc != PJ_SUCCESS) {
+ pj_assert(!"Thread TLS ID is not set (pj_init() error?)");
+ }
+
+ /* Check if suspension is required. */
+ if (rec->suspended_mutex)
+ pj_mutex_lock(rec->suspended_mutex);
+
+ PJ_LOG(6,(rec->obj_name, "Thread started"));
+
+ /* Call user's entry! */
+ result = (void*)(long)(*rec->proc)(rec->arg);
+
+ /* Done. */
+ PJ_LOG(6,(rec->obj_name, "Thread quitting"));
+ return result;
+}
+#endif
+
+/*
+ * pj_thread_create(...)
+ */
+PJ_DEF(pj_status_t) pj_thread_create( pj_pool_t *pool,
+ const char *thread_name,
+ pj_thread_proc *proc,
+ void *arg,
+ pj_size_t stack_size,
+ unsigned flags,
+ pj_thread_t **ptr_thread)
+{
+#if PJ_HAS_THREADS
+ pj_thread_t *rec;
+ int rc;
+
+ PJ_CHECK_STACK();
+ PJ_ASSERT_RETURN(pool && proc && ptr_thread, PJ_EINVAL);
+
+ /* Create thread record and assign name for the thread */
+ rec = (struct pj_thread_t*) pj_pool_zalloc(pool, sizeof(pj_thread_t));
+ if (!rec)
+ return PJ_ENOMEM;
+
+ /* Set name. */
+ if (!thread_name)
+ thread_name = "thr%p";
+
+ if (strchr(thread_name, '%')) {
+ pj_snprintf(rec->obj_name, PJ_MAX_OBJ_NAME, thread_name, rec);
+ } else {
+ strncpy(rec->obj_name, thread_name, PJ_MAX_OBJ_NAME);
+ rec->obj_name[PJ_MAX_OBJ_NAME-1] = '\0';
+ }
+
+#if defined(PJ_OS_HAS_CHECK_STACK) && PJ_OS_HAS_CHECK_STACK!=0
+ rec->stk_size = stack_size ? stack_size : 0xFFFFFFFFUL;
+ rec->stk_max_usage = 0;
+#endif
+
+ /* Emulate suspended thread with mutex. */
+ if (flags & PJ_THREAD_SUSPENDED) {
+ rc = pj_mutex_create_simple(pool, NULL, &rec->suspended_mutex);
+ if (rc != PJ_SUCCESS)
+ return rc;
+
+ pj_mutex_lock(rec->suspended_mutex);
+ } else {
+ pj_assert(rec->suspended_mutex == NULL);
+ }
+
+ PJ_LOG(6, (rec->obj_name, "Thread created"));
+
+ /* Create the thread. */
+ rec->proc = proc;
+ rec->arg = arg;
+ rc = pthread_create( &rec->thread, NULL, thread_main, rec);
+ if (rc != 0)
+ return PJ_RETURN_OS_ERROR(rc);
+
+ *ptr_thread = rec;
+ return PJ_SUCCESS;
+#else
+ pj_assert(!"Threading is disabled!");
+ return PJ_EINVALIDOP;
+#endif
+}
+
+/*
+ * pj_thread-get_name()
+ */
+PJ_DEF(const char*) pj_thread_get_name(pj_thread_t *p)
+{
+#if PJ_HAS_THREADS
+ pj_thread_t *rec = (pj_thread_t*)p;
+
+ PJ_CHECK_STACK();
+ PJ_ASSERT_RETURN(p, "");
+
+ return rec->obj_name;
+#else
+ return "";
+#endif
+}
+
+/*
+ * pj_thread_resume()
+ */
+PJ_DEF(pj_status_t) pj_thread_resume(pj_thread_t *p)
+{
+ pj_status_t rc;
+
+ PJ_CHECK_STACK();
+ PJ_ASSERT_RETURN(p, PJ_EINVAL);
+
+ rc = pj_mutex_unlock(p->suspended_mutex);
+
+ return rc;
+}
+
+/*
+ * pj_thread_this()
+ */
+PJ_DEF(pj_thread_t*) pj_thread_this(void)
+{
+#if PJ_HAS_THREADS
+ pj_thread_t *rec = pj_thread_local_get(thread_tls_id);
+ pj_assert(rec != NULL);
+
+ /*
+ * MUST NOT check stack because this function is called
+ * by PJ_CHECK_STACK() itself!!!
+ *
+ */
+
+ return rec;
+#else
+ pj_assert(!"Threading is not enabled!");
+ return NULL;
+#endif
+}
+
+/*
+ * pj_thread_join()
+ */
+PJ_DEF(pj_status_t) pj_thread_join(pj_thread_t *p)
+{
+#if PJ_HAS_THREADS
+ pj_thread_t *rec = (pj_thread_t *)p;
+ void *ret;
+ int result;
+
+ PJ_CHECK_STACK();
+
+ PJ_LOG(6, (pj_thread_this()->obj_name, "Joining thread %s", p->obj_name));
+ result = pthread_join( rec->thread, &ret);
+
+ if (result == 0)
+ return PJ_SUCCESS;
+ else
+ return PJ_RETURN_OS_ERROR(result);
+#else
+ PJ_CHECK_STACK();
+ pj_assert(!"No multithreading support!");
+ return PJ_EINVALIDOP;
+#endif
+}
+
+/*
+ * pj_thread_destroy()
+ */
+PJ_DEF(pj_status_t) pj_thread_destroy(pj_thread_t *p)
+{
+ /* This function is used to destroy thread handle in other platforms.
+ * I suppose there's nothing to do here..
+ */
+ PJ_CHECK_STACK();
+ return PJ_SUCCESS;
+}
+
+/*
+ * pj_thread_sleep()
+ */
+PJ_DEF(pj_status_t) pj_thread_sleep(unsigned msec)
+{
+ PJ_CHECK_STACK();
+ return usleep(msec * 1000);
+}
+
+#if defined(PJ_OS_HAS_CHECK_STACK) && PJ_OS_HAS_CHECK_STACK!=0
+/*
+ * pj_thread_check_stack()
+ * Implementation for PJ_CHECK_STACK()
+ */
+PJ_DEF(void) pj_thread_check_stack(const char *file, int line)
+{
+ char stk_ptr;
+ pj_uint32_t usage;
+ pj_thread_t *thread = pj_thread_this();
+
+ /* Calculate current usage. */
+ usage = (&stk_ptr > thread->stk_start) ? &stk_ptr - thread->stk_start :
+ thread->stk_start - &stk_ptr;
+
+ /* Assert if stack usage is dangerously high. */
+ pj_assert("STACK OVERFLOW!! " && (usage <= thread->stk_size - 128));
+
+ /* Keep statistic. */
+ if (usage > thread->stk_max_usage) {
+ thread->stk_max_usage = usage;
+ thread->caller_file = file;
+ thread->caller_line = line;
+ }
+}
+
+/*
+ * pj_thread_get_stack_max_usage()
+ */
+PJ_DEF(pj_uint32_t) pj_thread_get_stack_max_usage(pj_thread_t *thread)
+{
+ return thread->stk_max_usage;
+}
+
+/*
+ * pj_thread_get_stack_info()
+ */
+PJ_DEF(pj_status_t) pj_thread_get_stack_info( pj_thread_t *thread,
+ const char **file,
+ int *line )
+{
+ pj_assert(thread);
+
+ *file = thread->caller_file;
+ *line = thread->caller_line;
+ return 0;
+}
+
+#endif /* PJ_OS_HAS_CHECK_STACK */
+
+///////////////////////////////////////////////////////////////////////////////
+/*
+ * pj_atomic_create()
+ */
+PJ_DEF(pj_status_t) pj_atomic_create( pj_pool_t *pool,
+ pj_atomic_value_t initial,
+ pj_atomic_t **ptr_atomic)
+{
+ pj_status_t rc;
+ pj_atomic_t *atomic_var = pj_pool_calloc(pool, 1, sizeof(pj_atomic_t));
+ if (!atomic_var)
+ return PJ_ENOMEM;
+
+#if PJ_HAS_THREADS
+ rc = pj_mutex_create(pool, "atm%p", PJ_MUTEX_SIMPLE, &atomic_var->mutex);
+ if (rc != PJ_SUCCESS)
+ return rc;
+#endif
+ atomic_var->value = initial;
+
+ *ptr_atomic = atomic_var;
+ return PJ_SUCCESS;
+}
+
+/*
+ * pj_atomic_destroy()
+ */
+PJ_DEF(pj_status_t) pj_atomic_destroy( pj_atomic_t *atomic_var )
+{
+ PJ_ASSERT_RETURN(atomic_var, PJ_EINVAL);
+#if PJ_HAS_THREADS
+ return pj_mutex_destroy( atomic_var->mutex );
+#else
+ return 0;
+#endif
+}
+
+/*
+ * pj_atomic_set()
+ */
+PJ_DEF(void) pj_atomic_set(pj_atomic_t *atomic_var, pj_atomic_value_t value)
+{
+ PJ_CHECK_STACK();
+
+#if PJ_HAS_THREADS
+ pj_mutex_lock( atomic_var->mutex );
+#endif
+ atomic_var->value = value;
+#if PJ_HAS_THREADS
+ pj_mutex_unlock( atomic_var->mutex);
+#endif
+}
+
+/*
+ * pj_atomic_get()
+ */
+PJ_DEF(pj_atomic_value_t) pj_atomic_get(pj_atomic_t *atomic_var)
+{
+ pj_atomic_value_t oldval;
+
+ PJ_CHECK_STACK();
+
+#if PJ_HAS_THREADS
+ pj_mutex_lock( atomic_var->mutex );
+#endif
+ oldval = atomic_var->value;
+#if PJ_HAS_THREADS
+ pj_mutex_unlock( atomic_var->mutex);
+#endif
+ return oldval;
+}
+
/*
* pj_atomic_inc_and_get()
*/
@@ -577,33 +598,33 @@
return new_value;
}
-/*
- * pj_atomic_inc()
- */
-PJ_DEF(void) pj_atomic_inc(pj_atomic_t *atomic_var)
-{
- pj_atomic_inc_and_get(atomic_var);
-}
-
-/*
- * pj_atomic_dec_and_get()
- */
-PJ_DEF(pj_atomic_value_t) pj_atomic_dec_and_get(pj_atomic_t *atomic_var)
+/*
+ * pj_atomic_inc()
+ */
+PJ_DEF(void) pj_atomic_inc(pj_atomic_t *atomic_var)
+{
+ pj_atomic_inc_and_get(atomic_var);
+}
+
+/*
+ * pj_atomic_dec_and_get()
+ */
+PJ_DEF(pj_atomic_value_t) pj_atomic_dec_and_get(pj_atomic_t *atomic_var)
{
pj_atomic_value_t new_value;
-
- PJ_CHECK_STACK();
-
-#if PJ_HAS_THREADS
- pj_mutex_lock( atomic_var->mutex );
-#endif
- new_value = --atomic_var->value;
-#if PJ_HAS_THREADS
- pj_mutex_unlock( atomic_var->mutex);
+
+ PJ_CHECK_STACK();
+
+#if PJ_HAS_THREADS
+ pj_mutex_lock( atomic_var->mutex );
+#endif
+ new_value = --atomic_var->value;
+#if PJ_HAS_THREADS
+ pj_mutex_unlock( atomic_var->mutex);
#endif
- return new_value;
-}
+ return new_value;
+}
/*
* pj_atomic_dec()
@@ -612,29 +633,29 @@
{
pj_atomic_dec_and_get(atomic_var);
}
-
-/*
- * pj_atomic_add_and_get()
- */
+
+/*
+ * pj_atomic_add_and_get()
+ */
PJ_DEF(pj_atomic_value_t) pj_atomic_add_and_get( pj_atomic_t *atomic_var,
- pj_atomic_value_t value )
+ pj_atomic_value_t value )
{
pj_atomic_value_t new_value;
-
-#if PJ_HAS_THREADS
- pj_mutex_lock(atomic_var->mutex);
-#endif
-
+
+#if PJ_HAS_THREADS
+ pj_mutex_lock(atomic_var->mutex);
+#endif
+
atomic_var->value += value;
- new_value = atomic_var->value;
-
-#if PJ_HAS_THREADS
- pj_mutex_unlock(atomic_var->mutex);
+ new_value = atomic_var->value;
+
+#if PJ_HAS_THREADS
+ pj_mutex_unlock(atomic_var->mutex);
#endif
- return new_value;
-}
-
+ return new_value;
+}
+
/*
* pj_atomic_add()
*/
@@ -643,598 +664,598 @@
{
pj_atomic_add_and_get(atomic_var, value);
}
-
-///////////////////////////////////////////////////////////////////////////////
-/*
- * pj_thread_local_alloc()
- */
-PJ_DEF(pj_status_t) pj_thread_local_alloc(long *p_index)
-{
-#if PJ_HAS_THREADS
- pthread_key_t key;
- int rc;
-
- PJ_ASSERT_RETURN(p_index != NULL, PJ_EINVAL);
-
- pj_assert( sizeof(pthread_key_t) <= sizeof(long));
- if ((rc=pthread_key_create(&key, NULL)) != 0)
- return PJ_RETURN_OS_ERROR(rc);
-
- *p_index = key;
- return PJ_SUCCESS;
-#else
- int i;
- for (i=0; i<MAX_THREADS; ++i) {
- if (tls_flag[i] == 0)
- break;
- }
- if (i == MAX_THREADS)
- return PJ_ETOOMANY;
-
- tls_flag[i] = 1;
- tls[i] = NULL;
-
- *p_index = i;
- return PJ_SUCCESS;
-#endif
-}
-
-/*
- * pj_thread_local_free()
- */
-PJ_DEF(void) pj_thread_local_free(long index)
-{
- PJ_CHECK_STACK();
-#if PJ_HAS_THREADS
- pthread_key_delete(index);
-#else
- tls_flag[index] = 0;
-#endif
-}
-
-/*
- * pj_thread_local_set()
- */
-PJ_DEF(pj_status_t) pj_thread_local_set(long index, void *value)
-{
- //Can't check stack because this function is called in the
- //beginning before main thread is initialized.
- //PJ_CHECK_STACK();
-#if PJ_HAS_THREADS
- int rc=pthread_setspecific(index, value);
- return rc==0 ? PJ_SUCCESS : PJ_RETURN_OS_ERROR(rc);
-#else
- pj_assert(index >= 0 && index < MAX_THREADS);
- tls[index] = value;
- return PJ_SUCCESS;
-#endif
-}
-
-PJ_DEF(void*) pj_thread_local_get(long index)
-{
- //Can't check stack because this function is called
- //by PJ_CHECK_STACK() itself!!!
- //PJ_CHECK_STACK();
-#if PJ_HAS_THREADS
- return pthread_getspecific(index);
-#else
- pj_assert(index >= 0 && index < MAX_THREADS);
- return tls[index];
-#endif
-}
-
-///////////////////////////////////////////////////////////////////////////////
-PJ_DEF(void) pj_enter_critical_section(void)
-{
-#if PJ_HAS_THREADS
- pj_mutex_lock(&critical_section);
-#endif
-}
-
-PJ_DEF(void) pj_leave_critical_section(void)
-{
-#if PJ_HAS_THREADS
- pj_mutex_unlock(&critical_section);
-#endif
-}
-
-
-///////////////////////////////////////////////////////////////////////////////
-static pj_status_t init_mutex(pj_mutex_t *mutex, const char *name, int type)
-{
-#if PJ_HAS_THREADS
- pthread_mutexattr_t attr;
- int rc;
-
- PJ_CHECK_STACK();
-
- pthread_mutexattr_init(&attr);
-
- if (type == PJ_MUTEX_SIMPLE) {
-#if defined(PJ_LINUX) && PJ_LINUX!=0
- extern int pthread_mutexattr_settype(pthread_mutexattr_t*,int);
- rc = pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_FAST_NP);
-#else
- rc = pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_NORMAL);
-#endif
- } else {
-#if defined(PJ_LINUX) && PJ_LINUX!=0
- extern int pthread_mutexattr_settype(pthread_mutexattr_t*,int);
- rc = pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE_NP);
-#else
- rc = pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE);
-#endif
- }
-
- if (rc != 0) {
- return PJ_RETURN_OS_ERROR(rc);
- }
-
- rc = pthread_mutex_init(&mutex->mutex, &attr);
- if (rc != 0) {
- return PJ_RETURN_OS_ERROR(rc);
- }
-
-#if PJ_DEBUG
- /* Set owner. */
- mutex->nesting_level = 0;
- mutex->owner = NULL;
-#endif
-
- /* Set name. */
- if (!name) {
- name = "mtx%p";
- }
- if (strchr(name, '%')) {
- pj_snprintf(mutex->obj_name, PJ_MAX_OBJ_NAME, name, mutex);
- } else {
- strncpy(mutex->obj_name, name, PJ_MAX_OBJ_NAME);
- mutex->obj_name[PJ_MAX_OBJ_NAME-1] = '\0';
- }
-
- PJ_LOG(6, (mutex->obj_name, "Mutex created"));
- return PJ_SUCCESS;
-#else /* PJ_HAS_THREADS */
- return PJ_SUCCESS;
-#endif
-}
-
-/*
- * pj_mutex_create()
- */
-PJ_DEF(pj_status_t) pj_mutex_create(pj_pool_t *pool,
- const char *name,
- int type,
- pj_mutex_t **ptr_mutex)
-{
-#if PJ_HAS_THREADS
- pj_status_t rc;
- pj_mutex_t *mutex;
-
- PJ_ASSERT_RETURN(pool && ptr_mutex, PJ_EINVAL);
-
- mutex = pj_pool_alloc(pool, sizeof(*mutex));
- if (!mutex) return PJ_ENOMEM;
-
- if ((rc=init_mutex(mutex, name, type)) != PJ_SUCCESS)
- return rc;
-
- *ptr_mutex = mutex;
- return PJ_SUCCESS;
-#else /* PJ_HAS_THREADS */
- return (pj_mutex_t*)1;
-#endif
-}
-
-/*
- * pj_mutex_create_simple()
- */
-PJ_DEF(pj_status_t) pj_mutex_create_simple( pj_pool_t *pool,
- const char *name,
- pj_mutex_t **mutex )
-{
- return pj_mutex_create(pool, name, PJ_MUTEX_SIMPLE, mutex);
-}
-
-/*
- * pj_mutex_create_recursive()
- */
-PJ_DEF(pj_status_t) pj_mutex_create_recursive( pj_pool_t *pool,
- const char *name,
- pj_mutex_t **mutex )
-{
- return pj_mutex_create(pool, name, PJ_MUTEX_RECURSE, mutex);
-}
-
-/*
- * pj_mutex_lock()
- */
-PJ_DEF(pj_status_t) pj_mutex_lock(pj_mutex_t *mutex)
-{
-#if PJ_HAS_THREADS
- pj_status_t status;
-
- PJ_CHECK_STACK();
- PJ_ASSERT_RETURN(mutex, PJ_EINVAL);
-
- PJ_LOG(6,(mutex->obj_name, "Mutex: thread %s is waiting",
- pj_thread_this()->obj_name));
-
- status = pthread_mutex_lock( &mutex->mutex );
-
- PJ_LOG(6,(mutex->obj_name,
- (status==0 ? "Mutex acquired by thread %s" : "FAILED by %s"),
- pj_thread_this()->obj_name));
-
-#if PJ_DEBUG
- if (status == PJ_SUCCESS) {
- mutex->owner = pj_thread_this();
- ++mutex->nesting_level;
- }
-#endif
-
- if (status == 0)
- return PJ_SUCCESS;
- else
- return PJ_RETURN_OS_ERROR(status);
-#else /* PJ_HAS_THREADS */
- pj_assert( mutex == (pj_mutex_t*)1 );
- return PJ_SUCCESS;
-#endif
-}
-
-/*
- * pj_mutex_unlock()
- */
-PJ_DEF(pj_status_t) pj_mutex_unlock(pj_mutex_t *mutex)
-{
-#if PJ_HAS_THREADS
- pj_status_t status;
-
- PJ_CHECK_STACK();
- PJ_ASSERT_RETURN(mutex, PJ_EINVAL);
-
-#if PJ_DEBUG
- pj_assert(mutex->owner == pj_thread_this());
- if (--mutex->nesting_level == 0) {
- mutex->owner = NULL;
- }
-#endif
-
- PJ_LOG(6,(mutex->obj_name, "Mutex released by thread %s",
- pj_thread_this()->obj_name));
-
- status = pthread_mutex_unlock( &mutex->mutex );
- if (status == 0)
- return PJ_SUCCESS;
- else
- return PJ_RETURN_OS_ERROR(status);
-
-#else /* PJ_HAS_THREADS */
- pj_assert( mutex == (pj_mutex_t*)1 );
- return PJ_SUCCESS;
-#endif
-}
-
-/*
- * pj_mutex_trylock()
- */
-PJ_DEF(pj_status_t) pj_mutex_trylock(pj_mutex_t *mutex)
-{
-#if PJ_HAS_THREADS
- int status;
-
- PJ_CHECK_STACK();
- PJ_ASSERT_RETURN(mutex, PJ_EINVAL);
-
- status = pthread_mutex_trylock( &mutex->mutex );
-
- if (status==0) {
- PJ_LOG(6,(mutex->obj_name, "Mutex acquired by thread %s",
- pj_thread_this()->obj_name));
-
-#if PJ_DEBUG
- mutex->owner = pj_thread_this();
- ++mutex->nesting_level;
-#endif
- }
-
- if (status==0)
- return PJ_SUCCESS;
- else
- return PJ_RETURN_OS_ERROR(status);
-#else /* PJ_HAS_THREADS */
- pj_assert( mutex == (pj_mutex_t*)1);
- return PJ_SUCCESS;
-#endif
-}
-
-/*
- * pj_mutex_destroy()
- */
-PJ_DEF(pj_status_t) pj_mutex_destroy(pj_mutex_t *mutex)
-{
- int status;
-
- PJ_CHECK_STACK();
- PJ_ASSERT_RETURN(mutex, PJ_EINVAL);
-
-#if PJ_HAS_THREADS
- PJ_LOG(6,(mutex->obj_name, "Mutex destroyed"));
- status = pthread_mutex_destroy( &mutex->mutex );
- if (status == 0)
- return PJ_SUCCESS;
- else
- return PJ_RETURN_OS_ERROR(status);
-#else
- pj_assert( mutex == (pj_mutex_t*)1 );
- status = PJ_SUCCESS;
- return status;
-#endif
-}
-
-#if PJ_DEBUG
-PJ_DEF(pj_bool_t) pj_mutex_is_locked(pj_mutex_t *mutex)
-{
-#if PJ_HAS_THREADS
- return mutex->owner == pj_thread_this();
-#else
- return 1;
-#endif
-}
-#endif
-
-///////////////////////////////////////////////////////////////////////////////
-#if defined(PJ_HAS_SEMAPHORE) && PJ_HAS_SEMAPHORE != 0
-
-/*
- * pj_sem_create()
- */
-PJ_DEF(pj_status_t) pj_sem_create( pj_pool_t *pool,
- const char *name,
- unsigned initial,
- unsigned max,
- pj_sem_t **ptr_sem)
-{
-#if PJ_HAS_THREADS
- pj_sem_t *sem;
-
- PJ_CHECK_STACK();
- PJ_ASSERT_RETURN(pool != NULL && ptr_sem != NULL, PJ_EINVAL);
-
- sem = pj_pool_alloc(pool, sizeof(*sem));
- if (!sem) return PJ_ENOMEM;
-
- if (sem_init( &sem->sem, 0, initial) != 0)
- return PJ_RETURN_OS_ERROR(pj_get_native_os_error());
-
- /* Set name. */
- if (!name) {
- name = "sem%p";
- }
- if (strchr(name, '%')) {
- pj_snprintf(sem->obj_name, PJ_MAX_OBJ_NAME, name, sem);
- } else {
- strncpy(sem->obj_name, name, PJ_MAX_OBJ_NAME);
- sem->obj_name[PJ_MAX_OBJ_NAME-1] = '\0';
- }
-
- PJ_LOG(6, (sem->obj_name, "Semaphore created"));
-
- *ptr_sem = sem;
- return PJ_SUCCESS;
-#else
- *ptr_sem = (pj_sem_t*)1;
- return PJ_SUCCESS;
-#endif
-}
-
-/*
- * pj_sem_wait()
- */
-PJ_DEF(pj_status_t) pj_sem_wait(pj_sem_t *sem)
-{
-#if PJ_HAS_THREADS
- int result;
-
- PJ_CHECK_STACK();
- PJ_ASSERT_RETURN(sem, PJ_EINVAL);
-
- PJ_LOG(6, (sem->obj_name, "Semaphore: thread %s is waiting",
- pj_thread_this()->obj_name));
-
- result = sem_wait( &sem->sem );
-
- if (result == 0) {
- PJ_LOG(6, (sem->obj_name, "Semaphore acquired by thread %s",
- pj_thread_this()->obj_name));
- } else {
- PJ_LOG(6, (sem->obj_name, "Semaphore: thread %s FAILED to acquire",
- pj_thread_this()->obj_name));
- }
-
- if (result == 0)
- return PJ_SUCCESS;
- else
- return PJ_RETURN_OS_ERROR(pj_get_native_os_error());
-#else
- pj_assert( sem == (pj_sem_t*) 1 );
- return PJ_SUCCESS;
-#endif
-}
-
-/*
- * pj_sem_trywait()
- */
-PJ_DEF(pj_status_t) pj_sem_trywait(pj_sem_t *sem)
-{
-#if PJ_HAS_THREADS
- int result;
-
- PJ_CHECK_STACK();
- PJ_ASSERT_RETURN(sem, PJ_EINVAL);
-
- result = sem_trywait( &sem->sem );
-
- if (result == 0) {
- PJ_LOG(6, (sem->obj_name, "Semaphore acquired by thread %s",
- pj_thread_this()->obj_name));
- }
- if (result == 0)
- return PJ_SUCCESS;
- else
- return PJ_RETURN_OS_ERROR(pj_get_native_os_error());
-#else
- pj_assert( sem == (pj_sem_t*)1 );
- return PJ_SUCCESS;
-#endif
-}
-
-/*
- * pj_sem_post()
- */
-PJ_DEF(pj_status_t) pj_sem_post(pj_sem_t *sem)
-{
-#if PJ_HAS_THREADS
- int result;
- PJ_LOG(6, (sem->obj_name, "Semaphore released by thread %s",
- pj_thread_this()->obj_name));
- result = sem_post( &sem->sem );
-
- if (result == 0)
- return PJ_SUCCESS;
- else
- return PJ_RETURN_OS_ERROR(pj_get_native_os_error());
-#else
- pj_assert( sem == (pj_sem_t*) 1);
- return PJ_SUCCESS;
-#endif
-}
-
-/*
- * pj_sem_destroy()
- */
-PJ_DEF(pj_status_t) pj_sem_destroy(pj_sem_t *sem)
-{
-#if PJ_HAS_THREADS
- int result;
-
- PJ_CHECK_STACK();
- PJ_ASSERT_RETURN(sem, PJ_EINVAL);
-
- PJ_LOG(6, (sem->obj_name, "Semaphore destroyed by thread %s",
- pj_thread_this()->obj_name));
- result = sem_destroy( &sem->sem );
-
- if (result == 0)
- return PJ_SUCCESS;
- else
- return PJ_RETURN_OS_ERROR(pj_get_native_os_error());
-#else
- pj_assert( sem == (pj_sem_t*) 1 );
- return PJ_SUCCESS;
-#endif
-}
-
-#endif /* PJ_HAS_SEMAPHORE */
-
-///////////////////////////////////////////////////////////////////////////////
-#if defined(PJ_HAS_EVENT_OBJ) && PJ_HAS_EVENT_OBJ != 0
-
-/*
- * pj_event_create()
- */
-PJ_DEF(pj_status_t) pj_event_create(pj_pool_t *pool, const char *name,
- pj_bool_t manual_reset, pj_bool_t initial,
- pj_event_t **ptr_event)
-{
- pj_assert(!"Not supported!");
- PJ_UNUSED_ARG(pool);
- PJ_UNUSED_ARG(name);
- PJ_UNUSED_ARG(manual_reset);
- PJ_UNUSED_ARG(initial);
- PJ_UNUSED_ARG(ptr_event);
- return PJ_EINVALIDOP;
-}
-
-/*
- * pj_event_wait()
- */
-PJ_DEF(pj_status_t) pj_event_wait(pj_event_t *event)
-{
- PJ_UNUSED_ARG(event);
- return PJ_EINVALIDOP;
-}
-
-/*
- * pj_event_trywait()
- */
-PJ_DEF(pj_status_t) pj_event_trywait(pj_event_t *event)
-{
- PJ_UNUSED_ARG(event);
- return PJ_EINVALIDOP;
-}
-
-/*
- * pj_event_set()
- */
-PJ_DEF(pj_status_t) pj_event_set(pj_event_t *event)
-{
- PJ_UNUSED_ARG(event);
- return PJ_EINVALIDOP;
-}
-
-/*
- * pj_event_pulse()
- */
-PJ_DEF(pj_status_t) pj_event_pulse(pj_event_t *event)
-{
- PJ_UNUSED_ARG(event);
- return PJ_EINVALIDOP;
-}
-
-/*
- * pj_event_reset()
- */
-PJ_DEF(pj_status_t) pj_event_reset(pj_event_t *event)
-{
- PJ_UNUSED_ARG(event);
- return PJ_EINVALIDOP;
-}
-
-/*
- * pj_event_destroy()
- */
-PJ_DEF(pj_status_t) pj_event_destroy(pj_event_t *event)
-{
- PJ_UNUSED_ARG(event);
- return PJ_EINVALIDOP;
-}
-
-#endif /* PJ_HAS_EVENT_OBJ */
-
-///////////////////////////////////////////////////////////////////////////////
-#if defined(PJ_TERM_HAS_COLOR) && PJ_TERM_HAS_COLOR != 0
-/*
- * Terminal
- */
-
-/**
- * Set terminal color.
- */
-PJ_DEF(pj_status_t) pj_term_set_color(pj_color_t color)
-{
- PJ_UNUSED_ARG(color);
- return PJ_EINVALIDOP;
-}
-
-/**
- * Get current terminal foreground color.
- */
-PJ_DEF(pj_color_t) pj_term_get_color(void)
-{
- return 0;
-}
-
-#endif /* PJ_TERM_HAS_COLOR */
-
+
+///////////////////////////////////////////////////////////////////////////////
+/*
+ * pj_thread_local_alloc()
+ */
+PJ_DEF(pj_status_t) pj_thread_local_alloc(long *p_index)
+{
+#if PJ_HAS_THREADS
+ pthread_key_t key;
+ int rc;
+
+ PJ_ASSERT_RETURN(p_index != NULL, PJ_EINVAL);
+
+ pj_assert( sizeof(pthread_key_t) <= sizeof(long));
+ if ((rc=pthread_key_create(&key, NULL)) != 0)
+ return PJ_RETURN_OS_ERROR(rc);
+
+ *p_index = key;
+ return PJ_SUCCESS;
+#else
+ int i;
+ for (i=0; i<MAX_THREADS; ++i) {
+ if (tls_flag[i] == 0)
+ break;
+ }
+ if (i == MAX_THREADS)
+ return PJ_ETOOMANY;
+
+ tls_flag[i] = 1;
+ tls[i] = NULL;
+
+ *p_index = i;
+ return PJ_SUCCESS;
+#endif
+}
+
+/*
+ * pj_thread_local_free()
+ */
+PJ_DEF(void) pj_thread_local_free(long index)
+{
+ PJ_CHECK_STACK();
+#if PJ_HAS_THREADS
+ pthread_key_delete(index);
+#else
+ tls_flag[index] = 0;
+#endif
+}
+
+/*
+ * pj_thread_local_set()
+ */
+PJ_DEF(pj_status_t) pj_thread_local_set(long index, void *value)
+{
+ //Can't check stack because this function is called in the
+ //beginning before main thread is initialized.
+ //PJ_CHECK_STACK();
+#if PJ_HAS_THREADS
+ int rc=pthread_setspecific(index, value);
+ return rc==0 ? PJ_SUCCESS : PJ_RETURN_OS_ERROR(rc);
+#else
+ pj_assert(index >= 0 && index < MAX_THREADS);
+ tls[index] = value;
+ return PJ_SUCCESS;
+#endif
+}
+
+PJ_DEF(void*) pj_thread_local_get(long index)
+{
+ //Can't check stack because this function is called
+ //by PJ_CHECK_STACK() itself!!!
+ //PJ_CHECK_STACK();
+#if PJ_HAS_THREADS
+ return pthread_getspecific(index);
+#else
+ pj_assert(index >= 0 && index < MAX_THREADS);
+ return tls[index];
+#endif
+}
+
+///////////////////////////////////////////////////////////////////////////////
+PJ_DEF(void) pj_enter_critical_section(void)
+{
+#if PJ_HAS_THREADS
+ pj_mutex_lock(&critical_section);
+#endif
+}
+
+PJ_DEF(void) pj_leave_critical_section(void)
+{
+#if PJ_HAS_THREADS
+ pj_mutex_unlock(&critical_section);
+#endif
+}
+
+
+///////////////////////////////////////////////////////////////////////////////
+static pj_status_t init_mutex(pj_mutex_t *mutex, const char *name, int type)
+{
+#if PJ_HAS_THREADS
+ pthread_mutexattr_t attr;
+ int rc;
+
+ PJ_CHECK_STACK();
+
+ pthread_mutexattr_init(&attr);
+
+ if (type == PJ_MUTEX_SIMPLE) {
+#if defined(PJ_LINUX) && PJ_LINUX!=0
+ extern int pthread_mutexattr_settype(pthread_mutexattr_t*,int);
+ rc = pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_FAST_NP);
+#else
+ rc = pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_NORMAL);
+#endif
+ } else {
+#if defined(PJ_LINUX) && PJ_LINUX!=0
+ extern int pthread_mutexattr_settype(pthread_mutexattr_t*,int);
+ rc = pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE_NP);
+#else
+ rc = pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE);
+#endif
+ }
+
+ if (rc != 0) {
+ return PJ_RETURN_OS_ERROR(rc);
+ }
+
+ rc = pthread_mutex_init(&mutex->mutex, &attr);
+ if (rc != 0) {
+ return PJ_RETURN_OS_ERROR(rc);
+ }
+
+#if PJ_DEBUG
+ /* Set owner. */
+ mutex->nesting_level = 0;
+ mutex->owner = NULL;
+#endif
+
+ /* Set name. */
+ if (!name) {
+ name = "mtx%p";
+ }
+ if (strchr(name, '%')) {
+ pj_snprintf(mutex->obj_name, PJ_MAX_OBJ_NAME, name, mutex);
+ } else {
+ strncpy(mutex->obj_name, name, PJ_MAX_OBJ_NAME);
+ mutex->obj_name[PJ_MAX_OBJ_NAME-1] = '\0';
+ }
+
+ PJ_LOG(6, (mutex->obj_name, "Mutex created"));
+ return PJ_SUCCESS;
+#else /* PJ_HAS_THREADS */
+ return PJ_SUCCESS;
+#endif
+}
+
+/*
+ * pj_mutex_create()
+ */
+PJ_DEF(pj_status_t) pj_mutex_create(pj_pool_t *pool,
+ const char *name,
+ int type,
+ pj_mutex_t **ptr_mutex)
+{
+#if PJ_HAS_THREADS
+ pj_status_t rc;
+ pj_mutex_t *mutex;
+
+ PJ_ASSERT_RETURN(pool && ptr_mutex, PJ_EINVAL);
+
+ mutex = pj_pool_alloc(pool, sizeof(*mutex));
+ if (!mutex) return PJ_ENOMEM;
+
+ if ((rc=init_mutex(mutex, name, type)) != PJ_SUCCESS)
+ return rc;
+
+ *ptr_mutex = mutex;
+ return PJ_SUCCESS;
+#else /* PJ_HAS_THREADS */
+ return (pj_mutex_t*)1;
+#endif
+}
+
+/*
+ * pj_mutex_create_simple()
+ */
+PJ_DEF(pj_status_t) pj_mutex_create_simple( pj_pool_t *pool,
+ const char *name,
+ pj_mutex_t **mutex )
+{
+ return pj_mutex_create(pool, name, PJ_MUTEX_SIMPLE, mutex);
+}
+
+/*
+ * pj_mutex_create_recursive()
+ */
+PJ_DEF(pj_status_t) pj_mutex_create_recursive( pj_pool_t *pool,
+ const char *name,
+ pj_mutex_t **mutex )
+{
+ return pj_mutex_create(pool, name, PJ_MUTEX_RECURSE, mutex);
+}
+
+/*
+ * pj_mutex_lock()
+ */
+PJ_DEF(pj_status_t) pj_mutex_lock(pj_mutex_t *mutex)
+{
+#if PJ_HAS_THREADS
+ pj_status_t status;
+
+ PJ_CHECK_STACK();
+ PJ_ASSERT_RETURN(mutex, PJ_EINVAL);
+
+ PJ_LOG(6,(mutex->obj_name, "Mutex: thread %s is waiting",
+ pj_thread_this()->obj_name));
+
+ status = pthread_mutex_lock( &mutex->mutex );
+
+ PJ_LOG(6,(mutex->obj_name,
+ (status==0 ? "Mutex acquired by thread %s" : "FAILED by %s"),
+ pj_thread_this()->obj_name));
+
+#if PJ_DEBUG
+ if (status == PJ_SUCCESS) {
+ mutex->owner = pj_thread_this();
+ ++mutex->nesting_level;
+ }
+#endif
+
+ if (status == 0)
+ return PJ_SUCCESS;
+ else
+ return PJ_RETURN_OS_ERROR(status);
+#else /* PJ_HAS_THREADS */
+ pj_assert( mutex == (pj_mutex_t*)1 );
+ return PJ_SUCCESS;
+#endif
+}
+
+/*
+ * pj_mutex_unlock()
+ */
+PJ_DEF(pj_status_t) pj_mutex_unlock(pj_mutex_t *mutex)
+{
+#if PJ_HAS_THREADS
+ pj_status_t status;
+
+ PJ_CHECK_STACK();
+ PJ_ASSERT_RETURN(mutex, PJ_EINVAL);
+
+#if PJ_DEBUG
+ pj_assert(mutex->owner == pj_thread_this());
+ if (--mutex->nesting_level == 0) {
+ mutex->owner = NULL;
+ }
+#endif
+
+ PJ_LOG(6,(mutex->obj_name, "Mutex released by thread %s",
+ pj_thread_this()->obj_name));
+
+ status = pthread_mutex_unlock( &mutex->mutex );
+ if (status == 0)
+ return PJ_SUCCESS;
+ else
+ return PJ_RETURN_OS_ERROR(status);
+
+#else /* PJ_HAS_THREADS */
+ pj_assert( mutex == (pj_mutex_t*)1 );
+ return PJ_SUCCESS;
+#endif
+}
+
+/*
+ * pj_mutex_trylock()
+ */
+PJ_DEF(pj_status_t) pj_mutex_trylock(pj_mutex_t *mutex)
+{
+#if PJ_HAS_THREADS
+ int status;
+
+ PJ_CHECK_STACK();
+ PJ_ASSERT_RETURN(mutex, PJ_EINVAL);
+
+ status = pthread_mutex_trylock( &mutex->mutex );
+
+ if (status==0) {
+ PJ_LOG(6,(mutex->obj_name, "Mutex acquired by thread %s",
+ pj_thread_this()->obj_name));
+
+#if PJ_DEBUG
+ mutex->owner = pj_thread_this();
+ ++mutex->nesting_level;
+#endif
+ }
+
+ if (status==0)
+ return PJ_SUCCESS;
+ else
+ return PJ_RETURN_OS_ERROR(status);
+#else /* PJ_HAS_THREADS */
+ pj_assert( mutex == (pj_mutex_t*)1);
+ return PJ_SUCCESS;
+#endif
+}
+
+/*
+ * pj_mutex_destroy()
+ */
+PJ_DEF(pj_status_t) pj_mutex_destroy(pj_mutex_t *mutex)
+{
+ int status;
+
+ PJ_CHECK_STACK();
+ PJ_ASSERT_RETURN(mutex, PJ_EINVAL);
+
+#if PJ_HAS_THREADS
+ PJ_LOG(6,(mutex->obj_name, "Mutex destroyed"));
+ status = pthread_mutex_destroy( &mutex->mutex );
+ if (status == 0)
+ return PJ_SUCCESS;
+ else
+ return PJ_RETURN_OS_ERROR(status);
+#else
+ pj_assert( mutex == (pj_mutex_t*)1 );
+ status = PJ_SUCCESS;
+ return status;
+#endif
+}
+
+#if PJ_DEBUG
+PJ_DEF(pj_bool_t) pj_mutex_is_locked(pj_mutex_t *mutex)
+{
+#if PJ_HAS_THREADS
+ return mutex->owner == pj_thread_this();
+#else
+ return 1;
+#endif
+}
+#endif
+
+///////////////////////////////////////////////////////////////////////////////
+#if defined(PJ_HAS_SEMAPHORE) && PJ_HAS_SEMAPHORE != 0
+
+/*
+ * pj_sem_create()
+ */
+PJ_DEF(pj_status_t) pj_sem_create( pj_pool_t *pool,
+ const char *name,
+ unsigned initial,
+ unsigned max,
+ pj_sem_t **ptr_sem)
+{
+#if PJ_HAS_THREADS
+ pj_sem_t *sem;
+
+ PJ_CHECK_STACK();
+ PJ_ASSERT_RETURN(pool != NULL && ptr_sem != NULL, PJ_EINVAL);
+
+ sem = pj_pool_alloc(pool, sizeof(*sem));
+ if (!sem) return PJ_ENOMEM;
+
+ if (sem_init( &sem->sem, 0, initial) != 0)
+ return PJ_RETURN_OS_ERROR(pj_get_native_os_error());
+
+ /* Set name. */
+ if (!name) {
+ name = "sem%p";
+ }
+ if (strchr(name, '%')) {
+ pj_snprintf(sem->obj_name, PJ_MAX_OBJ_NAME, name, sem);
+ } else {
+ strncpy(sem->obj_name, name, PJ_MAX_OBJ_NAME);
+ sem->obj_name[PJ_MAX_OBJ_NAME-1] = '\0';
+ }
+
+ PJ_LOG(6, (sem->obj_name, "Semaphore created"));
+
+ *ptr_sem = sem;
+ return PJ_SUCCESS;
+#else
+ *ptr_sem = (pj_sem_t*)1;
+ return PJ_SUCCESS;
+#endif
+}
+
+/*
+ * pj_sem_wait()
+ */
+PJ_DEF(pj_status_t) pj_sem_wait(pj_sem_t *sem)
+{
+#if PJ_HAS_THREADS
+ int result;
+
+ PJ_CHECK_STACK();
+ PJ_ASSERT_RETURN(sem, PJ_EINVAL);
+
+ PJ_LOG(6, (sem->obj_name, "Semaphore: thread %s is waiting",
+ pj_thread_this()->obj_name));
+
+ result = sem_wait( &sem->sem );
+
+ if (result == 0) {
+ PJ_LOG(6, (sem->obj_name, "Semaphore acquired by thread %s",
+ pj_thread_this()->obj_name));
+ } else {
+ PJ_LOG(6, (sem->obj_name, "Semaphore: thread %s FAILED to acquire",
+ pj_thread_this()->obj_name));
+ }
+
+ if (result == 0)
+ return PJ_SUCCESS;
+ else
+ return PJ_RETURN_OS_ERROR(pj_get_native_os_error());
+#else
+ pj_assert( sem == (pj_sem_t*) 1 );
+ return PJ_SUCCESS;
+#endif
+}
+
+/*
+ * pj_sem_trywait()
+ */
+PJ_DEF(pj_status_t) pj_sem_trywait(pj_sem_t *sem)
+{
+#if PJ_HAS_THREADS
+ int result;
+
+ PJ_CHECK_STACK();
+ PJ_ASSERT_RETURN(sem, PJ_EINVAL);
+
+ result = sem_trywait( &sem->sem );
+
+ if (result == 0) {
+ PJ_LOG(6, (sem->obj_name, "Semaphore acquired by thread %s",
+ pj_thread_this()->obj_name));
+ }
+ if (result == 0)
+ return PJ_SUCCESS;
+ else
+ return PJ_RETURN_OS_ERROR(pj_get_native_os_error());
+#else
+ pj_assert( sem == (pj_sem_t*)1 );
+ return PJ_SUCCESS;
+#endif
+}
+
+/*
+ * pj_sem_post()
+ */
+PJ_DEF(pj_status_t) pj_sem_post(pj_sem_t *sem)
+{
+#if PJ_HAS_THREADS
+ int result;
+ PJ_LOG(6, (sem->obj_name, "Semaphore released by thread %s",
+ pj_thread_this()->obj_name));
+ result = sem_post( &sem->sem );
+
+ if (result == 0)
+ return PJ_SUCCESS;
+ else
+ return PJ_RETURN_OS_ERROR(pj_get_native_os_error());
+#else
+ pj_assert( sem == (pj_sem_t*) 1);
+ return PJ_SUCCESS;
+#endif
+}
+
+/*
+ * pj_sem_destroy()
+ */
+PJ_DEF(pj_status_t) pj_sem_destroy(pj_sem_t *sem)
+{
+#if PJ_HAS_THREADS
+ int result;
+
+ PJ_CHECK_STACK();
+ PJ_ASSERT_RETURN(sem, PJ_EINVAL);
+
+ PJ_LOG(6, (sem->obj_name, "Semaphore destroyed by thread %s",
+ pj_thread_this()->obj_name));
+ result = sem_destroy( &sem->sem );
+
+ if (result == 0)
+ return PJ_SUCCESS;
+ else
+ return PJ_RETURN_OS_ERROR(pj_get_native_os_error());
+#else
+ pj_assert( sem == (pj_sem_t*) 1 );
+ return PJ_SUCCESS;
+#endif
+}
+
+#endif /* PJ_HAS_SEMAPHORE */
+
+///////////////////////////////////////////////////////////////////////////////
+#if defined(PJ_HAS_EVENT_OBJ) && PJ_HAS_EVENT_OBJ != 0
+
+/*
+ * pj_event_create()
+ */
+PJ_DEF(pj_status_t) pj_event_create(pj_pool_t *pool, const char *name,
+ pj_bool_t manual_reset, pj_bool_t initial,
+ pj_event_t **ptr_event)
+{
+ pj_assert(!"Not supported!");
+ PJ_UNUSED_ARG(pool);
+ PJ_UNUSED_ARG(name);
+ PJ_UNUSED_ARG(manual_reset);
+ PJ_UNUSED_ARG(initial);
+ PJ_UNUSED_ARG(ptr_event);
+ return PJ_EINVALIDOP;
+}
+
+/*
+ * pj_event_wait()
+ */
+PJ_DEF(pj_status_t) pj_event_wait(pj_event_t *event)
+{
+ PJ_UNUSED_ARG(event);
+ return PJ_EINVALIDOP;
+}
+
+/*
+ * pj_event_trywait()
+ */
+PJ_DEF(pj_status_t) pj_event_trywait(pj_event_t *event)
+{
+ PJ_UNUSED_ARG(event);
+ return PJ_EINVALIDOP;
+}
+
+/*
+ * pj_event_set()
+ */
+PJ_DEF(pj_status_t) pj_event_set(pj_event_t *event)
+{
+ PJ_UNUSED_ARG(event);
+ return PJ_EINVALIDOP;
+}
+
+/*
+ * pj_event_pulse()
+ */
+PJ_DEF(pj_status_t) pj_event_pulse(pj_event_t *event)
+{
+ PJ_UNUSED_ARG(event);
+ return PJ_EINVALIDOP;
+}
+
+/*
+ * pj_event_reset()
+ */
+PJ_DEF(pj_status_t) pj_event_reset(pj_event_t *event)
+{
+ PJ_UNUSED_ARG(event);
+ return PJ_EINVALIDOP;
+}
+
+/*
+ * pj_event_destroy()
+ */
+PJ_DEF(pj_status_t) pj_event_destroy(pj_event_t *event)
+{
+ PJ_UNUSED_ARG(event);
+ return PJ_EINVALIDOP;
+}
+
+#endif /* PJ_HAS_EVENT_OBJ */
+
+///////////////////////////////////////////////////////////////////////////////
+#if defined(PJ_TERM_HAS_COLOR) && PJ_TERM_HAS_COLOR != 0
+/*
+ * Terminal
+ */
+
+/**
+ * Set terminal color.
+ */
+PJ_DEF(pj_status_t) pj_term_set_color(pj_color_t color)
+{
+ PJ_UNUSED_ARG(color);
+ return PJ_EINVALIDOP;
+}
+
+/**
+ * Get current terminal foreground color.
+ */
+PJ_DEF(pj_color_t) pj_term_get_color(void)
+{
+ return 0;
+}
+
+#endif /* PJ_TERM_HAS_COLOR */
+
diff --git a/pjlib/src/pj/os_core_win32.c b/pjlib/src/pj/os_core_win32.c
index be770d5..c4c4304 100644
--- a/pjlib/src/pj/os_core_win32.c
+++ b/pjlib/src/pj/os_core_win32.c
@@ -1,517 +1,538 @@
-/* $Id$
- */
-#include <pj/os.h>
-#include <pj/pool.h>
-#include <pj/log.h>
-#include <pj/string.h>
-#include <pj/guid.h>
-#include <pj/rand.h>
-#include <pj/assert.h>
-#include <pj/compat/vsprintf.h>
-#include <pj/compat/sprintf.h>
-#include <pj/errno.h>
-#include <pj/except.h>
-#include <stddef.h>
-#include <stdlib.h>
-#include <stdio.h>
-
-#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
-
-/*
- * Implementation of pj_thread_t.
- */
-struct pj_thread_t
-{
- char obj_name[PJ_MAX_OBJ_NAME];
- HANDLE hthread;
- DWORD idthread;
- pj_thread_proc *proc;
- void *arg;
-
-#if defined(PJ_OS_HAS_CHECK_STACK) && PJ_OS_HAS_CHECK_STACK!=0
- pj_uint32_t stk_size;
- pj_uint32_t stk_max_usage;
- char *stk_start;
- const char *caller_file;
- int caller_line;
-#endif
-};
-
-
-/*
- * Implementation of pj_mutex_t.
- */
-struct pj_mutex_t
-{
-#if PJ_WIN32_WINNT >= 0x0400
- CRITICAL_SECTION crit;
-#else
- HANDLE hMutex;
-#endif
- char obj_name[PJ_MAX_OBJ_NAME];
-#if PJ_DEBUG
- int nesting_level;
- pj_thread_t *owner;
-#endif
-};
-
-/*
- * Implementation of pj_sem_t.
- */
-typedef struct pj_sem_t
-{
- HANDLE hSemaphore;
- char obj_name[PJ_MAX_OBJ_NAME];
-} pj_mem_t;
-
-
-/*
- * Implementation of pj_event_t.
- */
-struct pj_event_t
-{
- HANDLE hEvent;
- char obj_name[PJ_MAX_OBJ_NAME];
-};
-
-/*
- * Implementation of pj_atomic_t.
- */
-struct pj_atomic_t
-{
- long value;
-};
-
-/*
- * Static global variables.
- */
-static pj_thread_desc main_thread;
-static long thread_tls_id;
-static pj_mutex_t critical_section_mutex;
-
-
-/*
- * Some static prototypes.
- */
-static pj_status_t init_mutex(pj_mutex_t *mutex, const char *name);
-
-
-/*
- * pj_init(void).
- * Init PJLIB!
- */
-PJ_DEF(pj_status_t) pj_init(void)
-{
- WSADATA wsa;
- char dummy_guid[32]; /* use maximum GUID length */
- pj_str_t guid;
- pj_status_t rc;
-
- PJ_LOG(5, ("pj_init", "Initializing PJ Library.."));
-
- /* Init Winsock.. */
- if (WSAStartup(MAKEWORD(2,0), &wsa) != 0) {
- PJ_LOG(1, ("pj_init", "Winsock initialization has returned an error"));
- return PJ_RETURN_OS_ERROR(WSAGetLastError());
- }
-
- /* Init this thread's TLS. */
- if ((rc=pj_thread_init()) != PJ_SUCCESS) {
- PJ_LOG(1, ("pj_init", "Thread initialization has returned an error"));
- return rc;
- }
-
- /* Init random seed. */
- pj_srand( GetCurrentProcessId() );
-
- /* Startup GUID. */
- guid.ptr = dummy_guid;
- pj_generate_unique_string( &guid );
-
- /* Initialize critical section. */
- if ((rc=init_mutex(&critical_section_mutex, "pj%p")) != PJ_SUCCESS)
- return rc;
-
- /* Initialize exception ID for the pool.
- * Must do so after critical section is configured.
- */
- rc = pj_exception_id_alloc("PJLIB/No memory", &PJ_NO_MEMORY_EXCEPTION);
- if (rc != PJ_SUCCESS)
- return rc;
-
- /* Startup timestamp */
-#if defined(PJ_HAS_HIGH_RES_TIMER) && PJ_HAS_HIGH_RES_TIMER != 0
- {
- pj_timestamp dummy_ts;
- if ((rc=pj_get_timestamp(&dummy_ts)) != PJ_SUCCESS) {
- PJ_LOG(1, ("pj_init", "Unable to initialize timestamp"));
- return rc;
- }
- }
-#endif
-
- return PJ_SUCCESS;
-}
-
-/*
- * pj_getpid(void)
- */
-PJ_DEF(pj_uint32_t) pj_getpid(void)
-{
- PJ_CHECK_STACK();
- return GetCurrentProcessId();
-}
-
-/*
- * pj_thread_register(..)
- */
-PJ_DEF(pj_status_t) pj_thread_register ( const char *cstr_thread_name,
- pj_thread_desc desc,
- pj_thread_t **thread_ptr)
-{
- char stack_ptr;
- pj_status_t rc;
- pj_thread_t *thread = (pj_thread_t *)desc;
- pj_str_t thread_name = pj_str((char*)cstr_thread_name);
-
- /* Size sanity check. */
- if (sizeof(pj_thread_desc) < sizeof(pj_thread_t)) {
- pj_assert(!"Not enough pj_thread_desc size!");
- return PJ_EBUG;
- }
-
- /* If a thread descriptor has been registered before, just return it. */
- if (pj_thread_local_get (thread_tls_id) != 0) {
- *thread_ptr = (pj_thread_t*)pj_thread_local_get (thread_tls_id);
- return PJ_SUCCESS;
- }
-
- /* Initialize and set the thread entry. */
- pj_memset(desc, 0, sizeof(struct pj_thread_t));
- thread->hthread = GetCurrentThread();
- thread->idthread = GetCurrentThreadId();
-
-#if defined(PJ_OS_HAS_CHECK_STACK) && PJ_OS_HAS_CHECK_STACK!=0
- thread->stk_start = &stack_ptr;
- thread->stk_size = 0xFFFFFFFFUL;
- thread->stk_max_usage = 0;
-#else
- stack_ptr = '\0';
-#endif
-
- if (cstr_thread_name && pj_strlen(&thread_name) < sizeof(thread->obj_name)-1)
- pj_sprintf(thread->obj_name, cstr_thread_name, thread->idthread);
- else
- pj_sprintf(thread->obj_name, "thr%p", (void*)thread->idthread);
-
- rc = pj_thread_local_set(thread_tls_id, thread);
- if (rc != PJ_SUCCESS)
- return rc;
-
- *thread_ptr = thread;
- return PJ_SUCCESS;
-}
-
-/*
- * pj_thread_init(void)
- */
-pj_status_t pj_thread_init(void)
-{
- pj_status_t rc;
- pj_thread_t *thread;
-
- rc = pj_thread_local_alloc(&thread_tls_id);
- if (rc != PJ_SUCCESS)
- return rc;
-
- return pj_thread_register("thr%p", main_thread, &thread);
-}
-
-static DWORD WINAPI thread_main(void *param)
-{
- pj_thread_t *rec = param;
- DWORD result;
-
-#if defined(PJ_OS_HAS_CHECK_STACK) && PJ_OS_HAS_CHECK_STACK!=0
- rec->stk_start = (char*)&rec;
-#endif
-
- PJ_LOG(6,(rec->obj_name, "Thread started"));
-
- if (pj_thread_local_set(thread_tls_id, rec) != PJ_SUCCESS) {
- pj_assert(!"TLS is not set (pj_init() error?)");
- }
-
- result = (*rec->proc)(rec->arg);
-
- PJ_LOG(6,(rec->obj_name, "Thread quitting"));
- return (DWORD)result;
-}
-
-/*
- * pj_thread_create(...)
- */
-PJ_DEF(pj_status_t) pj_thread_create( pj_pool_t *pool,
- const char *thread_name,
- pj_thread_proc *proc,
- void *arg,
- pj_size_t stack_size,
- unsigned flags,
- pj_thread_t **thread_ptr)
-{
- DWORD dwflags = 0;
- pj_thread_t *rec;
-
- PJ_CHECK_STACK();
- PJ_ASSERT_RETURN(pool && proc && thread_ptr, PJ_EINVAL);
-
- /* Set flags */
- if (flags & PJ_THREAD_SUSPENDED)
- dwflags |= CREATE_SUSPENDED;
-
- /* Create thread record and assign name for the thread */
- rec = (struct pj_thread_t*) pj_pool_calloc(pool, 1, sizeof(pj_thread_t));
- if (!rec)
- return PJ_ENOMEM;
-
- /* Set name. */
- if (!thread_name)
- thread_name = "thr%p";
-
- if (strchr(thread_name, '%')) {
- pj_snprintf(rec->obj_name, PJ_MAX_OBJ_NAME, thread_name, rec);
- } else {
- strncpy(rec->obj_name, thread_name, PJ_MAX_OBJ_NAME);
- rec->obj_name[PJ_MAX_OBJ_NAME-1] = '\0';
- }
-
- PJ_LOG(6, (rec->obj_name, "Thread created"));
-
-#if defined(PJ_OS_HAS_CHECK_STACK) && PJ_OS_HAS_CHECK_STACK!=0
- rec->stk_size = stack_size ? stack_size : 0xFFFFFFFFUL;
- rec->stk_max_usage = 0;
-#endif
-
- /* Create the thread. */
- rec->proc = proc;
- rec->arg = arg;
- rec->hthread = CreateThread(NULL, stack_size,
- thread_main, rec,
- dwflags, &rec->idthread);
- if (rec->hthread == NULL)
- return PJ_RETURN_OS_ERROR(GetLastError());
-
- /* Success! */
- *thread_ptr = rec;
- return PJ_SUCCESS;
-}
-
-/*
- * pj_thread-get_name()
- */
-PJ_DEF(const char*) pj_thread_get_name(pj_thread_t *p)
-{
- pj_thread_t *rec = (pj_thread_t*)p;
-
- PJ_CHECK_STACK();
- PJ_ASSERT_RETURN(p, "");
-
- return rec->obj_name;
-}
-
-/*
- * pj_thread_resume()
- */
-PJ_DEF(pj_status_t) pj_thread_resume(pj_thread_t *p)
-{
- pj_thread_t *rec = (pj_thread_t*)p;
-
- PJ_CHECK_STACK();
- PJ_ASSERT_RETURN(p, PJ_EINVAL);
-
- if (ResumeThread(rec->hthread) == (DWORD)-1)
- return PJ_RETURN_OS_ERROR(GetLastError());
- else
- return PJ_SUCCESS;
-}
-
-/*
- * pj_thread_this()
- */
-PJ_DEF(pj_thread_t*) pj_thread_this(void)
-{
- pj_thread_t *rec = pj_thread_local_get(thread_tls_id);
- pj_assert(rec != NULL);
-
- /*
- * MUST NOT check stack because this function is called
- * by PJ_CHECK_STACK() itself!!!
- *
- */
-
- return rec;
-}
-
-/*
- * pj_thread_join()
- */
-PJ_DEF(pj_status_t) pj_thread_join(pj_thread_t *p)
-{
- pj_thread_t *rec = (pj_thread_t *)p;
- DWORD rc;
-
- PJ_CHECK_STACK();
- PJ_ASSERT_RETURN(p, PJ_EINVAL);
-
- PJ_LOG(6, (pj_thread_this()->obj_name, "Joining thread %s", p->obj_name));
-
- rc = WaitForSingleObject(rec->hthread, INFINITE);
-
- if (rc==WAIT_OBJECT_0)
- return PJ_SUCCESS;
- else if (rc==WAIT_TIMEOUT)
- return PJ_ETIMEDOUT;
- else
- return PJ_RETURN_OS_ERROR(GetLastError());
-}
-
-/*
- * pj_thread_destroy()
- */
-PJ_DEF(pj_status_t) pj_thread_destroy(pj_thread_t *p)
-{
- pj_thread_t *rec = (pj_thread_t *)p;
-
- PJ_CHECK_STACK();
- PJ_ASSERT_RETURN(p, PJ_EINVAL);
-
- if (CloseHandle(rec->hthread) == TRUE)
- return PJ_SUCCESS;
- else
- return PJ_RETURN_OS_ERROR(GetLastError());
-}
-
-/*
- * pj_thread_sleep()
- */
-PJ_DEF(pj_status_t) pj_thread_sleep(unsigned msec)
-{
- PJ_CHECK_STACK();
- Sleep(msec);
- return PJ_SUCCESS;
-}
-
-#if defined(PJ_OS_HAS_CHECK_STACK) && PJ_OS_HAS_CHECK_STACK != 0
-/*
- * pj_thread_check_stack()
- * Implementation for PJ_CHECK_STACK()
- */
-PJ_DEF(void) pj_thread_check_stack(const char *file, int line)
-{
- char stk_ptr;
- pj_uint32_t usage;
- pj_thread_t *thread = pj_thread_this();
-
- pj_assert(thread);
-
- /* Calculate current usage. */
- usage = (&stk_ptr > thread->stk_start) ? &stk_ptr - thread->stk_start :
- thread->stk_start - &stk_ptr;
-
- /* Assert if stack usage is dangerously high. */
- pj_assert("STACK OVERFLOW!! " && (usage <= thread->stk_size - 128));
-
- /* Keep statistic. */
- if (usage > thread->stk_max_usage) {
- thread->stk_max_usage = usage;
- thread->caller_file = file;
- thread->caller_line = line;
- }
-
-}
-
-/*
- * pj_thread_get_stack_max_usage()
- */
-PJ_DEF(pj_uint32_t) pj_thread_get_stack_max_usage(pj_thread_t *thread)
-{
- return thread->stk_max_usage;
-}
-
-/*
- * pj_thread_get_stack_info()
- */
-PJ_DEF(pj_status_t) pj_thread_get_stack_info( pj_thread_t *thread,
- const char **file,
- int *line )
-{
- pj_assert(thread);
-
- *file = thread->caller_file;
- *line = thread->caller_line;
- return 0;
-}
-
-#endif /* PJ_OS_HAS_CHECK_STACK */
-
-
-///////////////////////////////////////////////////////////////////////////////
-
-/*
- * pj_atomic_create()
- */
-PJ_DEF(pj_status_t) pj_atomic_create( pj_pool_t *pool,
- pj_atomic_value_t initial,
- pj_atomic_t **atomic_ptr)
-{
- pj_atomic_t *atomic_var = pj_pool_alloc(pool, sizeof(pj_atomic_t));
- if (!atomic_var)
- return PJ_ENOMEM;
-
- atomic_var->value = initial;
- *atomic_ptr = atomic_var;
-
- return PJ_SUCCESS;
-}
-
-/*
- * pj_atomic_destroy()
- */
-PJ_DEF(pj_status_t) pj_atomic_destroy( pj_atomic_t *var )
-{
- PJ_UNUSED_ARG(var);
- PJ_ASSERT_RETURN(var, PJ_EINVAL);
-
- return 0;
-}
-
-/*
- * pj_atomic_set()
- */
-PJ_DEF(void) pj_atomic_set( pj_atomic_t *atomic_var, pj_atomic_value_t value)
-{
- PJ_CHECK_STACK();
-
- InterlockedExchange(&atomic_var->value, value);
-}
-
-/*
- * pj_atomic_get()
- */
-PJ_DEF(pj_atomic_value_t) pj_atomic_get(pj_atomic_t *atomic_var)
-{
- PJ_CHECK_STACK();
- PJ_ASSERT_RETURN(atomic_var, 0);
-
- return atomic_var->value;
-}
-
+/* $Id$
+ */
+/*
+ * PJLIB - PJ Foundation Library
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ * Benny Prijono <bennylp@bulukucing.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; 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 <pj/log.h>
+#include <pj/string.h>
+#include <pj/guid.h>
+#include <pj/rand.h>
+#include <pj/assert.h>
+#include <pj/compat/vsprintf.h>
+#include <pj/compat/sprintf.h>
+#include <pj/errno.h>
+#include <pj/except.h>
+#include <stddef.h>
+#include <stdlib.h>
+#include <stdio.h>
+
+#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
+
+/*
+ * Implementation of pj_thread_t.
+ */
+struct pj_thread_t
+{
+ char obj_name[PJ_MAX_OBJ_NAME];
+ HANDLE hthread;
+ DWORD idthread;
+ pj_thread_proc *proc;
+ void *arg;
+
+#if defined(PJ_OS_HAS_CHECK_STACK) && PJ_OS_HAS_CHECK_STACK!=0
+ pj_uint32_t stk_size;
+ pj_uint32_t stk_max_usage;
+ char *stk_start;
+ const char *caller_file;
+ int caller_line;
+#endif
+};
+
+
+/*
+ * Implementation of pj_mutex_t.
+ */
+struct pj_mutex_t
+{
+#if PJ_WIN32_WINNT >= 0x0400
+ CRITICAL_SECTION crit;
+#else
+ HANDLE hMutex;
+#endif
+ char obj_name[PJ_MAX_OBJ_NAME];
+#if PJ_DEBUG
+ int nesting_level;
+ pj_thread_t *owner;
+#endif
+};
+
+/*
+ * Implementation of pj_sem_t.
+ */
+typedef struct pj_sem_t
+{
+ HANDLE hSemaphore;
+ char obj_name[PJ_MAX_OBJ_NAME];
+} pj_mem_t;
+
+
+/*
+ * Implementation of pj_event_t.
+ */
+struct pj_event_t
+{
+ HANDLE hEvent;
+ char obj_name[PJ_MAX_OBJ_NAME];
+};
+
+/*
+ * Implementation of pj_atomic_t.
+ */
+struct pj_atomic_t
+{
+ long value;
+};
+
+/*
+ * Static global variables.
+ */
+static pj_thread_desc main_thread;
+static long thread_tls_id;
+static pj_mutex_t critical_section_mutex;
+
+
+/*
+ * Some static prototypes.
+ */
+static pj_status_t init_mutex(pj_mutex_t *mutex, const char *name);
+
+
+/*
+ * pj_init(void).
+ * Init PJLIB!
+ */
+PJ_DEF(pj_status_t) pj_init(void)
+{
+ WSADATA wsa;
+ char dummy_guid[32]; /* use maximum GUID length */
+ pj_str_t guid;
+ pj_status_t rc;
+
+ PJ_LOG(5, ("pj_init", "Initializing PJ Library.."));
+
+ /* Init Winsock.. */
+ if (WSAStartup(MAKEWORD(2,0), &wsa) != 0) {
+ PJ_LOG(1, ("pj_init", "Winsock initialization has returned an error"));
+ return PJ_RETURN_OS_ERROR(WSAGetLastError());
+ }
+
+ /* Init this thread's TLS. */
+ if ((rc=pj_thread_init()) != PJ_SUCCESS) {
+ PJ_LOG(1, ("pj_init", "Thread initialization has returned an error"));
+ return rc;
+ }
+
+ /* Init random seed. */
+ pj_srand( GetCurrentProcessId() );
+
+ /* Startup GUID. */
+ guid.ptr = dummy_guid;
+ pj_generate_unique_string( &guid );
+
+ /* Initialize critical section. */
+ if ((rc=init_mutex(&critical_section_mutex, "pj%p")) != PJ_SUCCESS)
+ return rc;
+
+ /* Initialize exception ID for the pool.
+ * Must do so after critical section is configured.
+ */
+ rc = pj_exception_id_alloc("PJLIB/No memory", &PJ_NO_MEMORY_EXCEPTION);
+ if (rc != PJ_SUCCESS)
+ return rc;
+
+ /* Startup timestamp */
+#if defined(PJ_HAS_HIGH_RES_TIMER) && PJ_HAS_HIGH_RES_TIMER != 0
+ {
+ pj_timestamp dummy_ts;
+ if ((rc=pj_get_timestamp(&dummy_ts)) != PJ_SUCCESS) {
+ PJ_LOG(1, ("pj_init", "Unable to initialize timestamp"));
+ return rc;
+ }
+ }
+#endif
+
+ return PJ_SUCCESS;
+}
+
+/*
+ * pj_getpid(void)
+ */
+PJ_DEF(pj_uint32_t) pj_getpid(void)
+{
+ PJ_CHECK_STACK();
+ return GetCurrentProcessId();
+}
+
+/*
+ * pj_thread_register(..)
+ */
+PJ_DEF(pj_status_t) pj_thread_register ( const char *cstr_thread_name,
+ pj_thread_desc desc,
+ pj_thread_t **thread_ptr)
+{
+ char stack_ptr;
+ pj_status_t rc;
+ pj_thread_t *thread = (pj_thread_t *)desc;
+ pj_str_t thread_name = pj_str((char*)cstr_thread_name);
+
+ /* Size sanity check. */
+ if (sizeof(pj_thread_desc) < sizeof(pj_thread_t)) {
+ pj_assert(!"Not enough pj_thread_desc size!");
+ return PJ_EBUG;
+ }
+
+ /* If a thread descriptor has been registered before, just return it. */
+ if (pj_thread_local_get (thread_tls_id) != 0) {
+ *thread_ptr = (pj_thread_t*)pj_thread_local_get (thread_tls_id);
+ return PJ_SUCCESS;
+ }
+
+ /* Initialize and set the thread entry. */
+ pj_memset(desc, 0, sizeof(struct pj_thread_t));
+ thread->hthread = GetCurrentThread();
+ thread->idthread = GetCurrentThreadId();
+
+#if defined(PJ_OS_HAS_CHECK_STACK) && PJ_OS_HAS_CHECK_STACK!=0
+ thread->stk_start = &stack_ptr;
+ thread->stk_size = 0xFFFFFFFFUL;
+ thread->stk_max_usage = 0;
+#else
+ stack_ptr = '\0';
+#endif
+
+ if (cstr_thread_name && pj_strlen(&thread_name) < sizeof(thread->obj_name)-1)
+ pj_sprintf(thread->obj_name, cstr_thread_name, thread->idthread);
+ else
+ pj_sprintf(thread->obj_name, "thr%p", (void*)thread->idthread);
+
+ rc = pj_thread_local_set(thread_tls_id, thread);
+ if (rc != PJ_SUCCESS)
+ return rc;
+
+ *thread_ptr = thread;
+ return PJ_SUCCESS;
+}
+
+/*
+ * pj_thread_init(void)
+ */
+pj_status_t pj_thread_init(void)
+{
+ pj_status_t rc;
+ pj_thread_t *thread;
+
+ rc = pj_thread_local_alloc(&thread_tls_id);
+ if (rc != PJ_SUCCESS)
+ return rc;
+
+ return pj_thread_register("thr%p", main_thread, &thread);
+}
+
+static DWORD WINAPI thread_main(void *param)
+{
+ pj_thread_t *rec = param;
+ DWORD result;
+
+#if defined(PJ_OS_HAS_CHECK_STACK) && PJ_OS_HAS_CHECK_STACK!=0
+ rec->stk_start = (char*)&rec;
+#endif
+
+ PJ_LOG(6,(rec->obj_name, "Thread started"));
+
+ if (pj_thread_local_set(thread_tls_id, rec) != PJ_SUCCESS) {
+ pj_assert(!"TLS is not set (pj_init() error?)");
+ }
+
+ result = (*rec->proc)(rec->arg);
+
+ PJ_LOG(6,(rec->obj_name, "Thread quitting"));
+ return (DWORD)result;
+}
+
+/*
+ * pj_thread_create(...)
+ */
+PJ_DEF(pj_status_t) pj_thread_create( pj_pool_t *pool,
+ const char *thread_name,
+ pj_thread_proc *proc,
+ void *arg,
+ pj_size_t stack_size,
+ unsigned flags,
+ pj_thread_t **thread_ptr)
+{
+ DWORD dwflags = 0;
+ pj_thread_t *rec;
+
+ PJ_CHECK_STACK();
+ PJ_ASSERT_RETURN(pool && proc && thread_ptr, PJ_EINVAL);
+
+ /* Set flags */
+ if (flags & PJ_THREAD_SUSPENDED)
+ dwflags |= CREATE_SUSPENDED;
+
+ /* Create thread record and assign name for the thread */
+ rec = (struct pj_thread_t*) pj_pool_calloc(pool, 1, sizeof(pj_thread_t));
+ if (!rec)
+ return PJ_ENOMEM;
+
+ /* Set name. */
+ if (!thread_name)
+ thread_name = "thr%p";
+
+ if (strchr(thread_name, '%')) {
+ pj_snprintf(rec->obj_name, PJ_MAX_OBJ_NAME, thread_name, rec);
+ } else {
+ strncpy(rec->obj_name, thread_name, PJ_MAX_OBJ_NAME);
+ rec->obj_name[PJ_MAX_OBJ_NAME-1] = '\0';
+ }
+
+ PJ_LOG(6, (rec->obj_name, "Thread created"));
+
+#if defined(PJ_OS_HAS_CHECK_STACK) && PJ_OS_HAS_CHECK_STACK!=0
+ rec->stk_size = stack_size ? stack_size : 0xFFFFFFFFUL;
+ rec->stk_max_usage = 0;
+#endif
+
+ /* Create the thread. */
+ rec->proc = proc;
+ rec->arg = arg;
+ rec->hthread = CreateThread(NULL, stack_size,
+ thread_main, rec,
+ dwflags, &rec->idthread);
+ if (rec->hthread == NULL)
+ return PJ_RETURN_OS_ERROR(GetLastError());
+
+ /* Success! */
+ *thread_ptr = rec;
+ return PJ_SUCCESS;
+}
+
+/*
+ * pj_thread-get_name()
+ */
+PJ_DEF(const char*) pj_thread_get_name(pj_thread_t *p)
+{
+ pj_thread_t *rec = (pj_thread_t*)p;
+
+ PJ_CHECK_STACK();
+ PJ_ASSERT_RETURN(p, "");
+
+ return rec->obj_name;
+}
+
+/*
+ * pj_thread_resume()
+ */
+PJ_DEF(pj_status_t) pj_thread_resume(pj_thread_t *p)
+{
+ pj_thread_t *rec = (pj_thread_t*)p;
+
+ PJ_CHECK_STACK();
+ PJ_ASSERT_RETURN(p, PJ_EINVAL);
+
+ if (ResumeThread(rec->hthread) == (DWORD)-1)
+ return PJ_RETURN_OS_ERROR(GetLastError());
+ else
+ return PJ_SUCCESS;
+}
+
+/*
+ * pj_thread_this()
+ */
+PJ_DEF(pj_thread_t*) pj_thread_this(void)
+{
+ pj_thread_t *rec = pj_thread_local_get(thread_tls_id);
+ pj_assert(rec != NULL);
+
+ /*
+ * MUST NOT check stack because this function is called
+ * by PJ_CHECK_STACK() itself!!!
+ *
+ */
+
+ return rec;
+}
+
+/*
+ * pj_thread_join()
+ */
+PJ_DEF(pj_status_t) pj_thread_join(pj_thread_t *p)
+{
+ pj_thread_t *rec = (pj_thread_t *)p;
+ DWORD rc;
+
+ PJ_CHECK_STACK();
+ PJ_ASSERT_RETURN(p, PJ_EINVAL);
+
+ PJ_LOG(6, (pj_thread_this()->obj_name, "Joining thread %s", p->obj_name));
+
+ rc = WaitForSingleObject(rec->hthread, INFINITE);
+
+ if (rc==WAIT_OBJECT_0)
+ return PJ_SUCCESS;
+ else if (rc==WAIT_TIMEOUT)
+ return PJ_ETIMEDOUT;
+ else
+ return PJ_RETURN_OS_ERROR(GetLastError());
+}
+
+/*
+ * pj_thread_destroy()
+ */
+PJ_DEF(pj_status_t) pj_thread_destroy(pj_thread_t *p)
+{
+ pj_thread_t *rec = (pj_thread_t *)p;
+
+ PJ_CHECK_STACK();
+ PJ_ASSERT_RETURN(p, PJ_EINVAL);
+
+ if (CloseHandle(rec->hthread) == TRUE)
+ return PJ_SUCCESS;
+ else
+ return PJ_RETURN_OS_ERROR(GetLastError());
+}
+
+/*
+ * pj_thread_sleep()
+ */
+PJ_DEF(pj_status_t) pj_thread_sleep(unsigned msec)
+{
+ PJ_CHECK_STACK();
+ Sleep(msec);
+ return PJ_SUCCESS;
+}
+
+#if defined(PJ_OS_HAS_CHECK_STACK) && PJ_OS_HAS_CHECK_STACK != 0
+/*
+ * pj_thread_check_stack()
+ * Implementation for PJ_CHECK_STACK()
+ */
+PJ_DEF(void) pj_thread_check_stack(const char *file, int line)
+{
+ char stk_ptr;
+ pj_uint32_t usage;
+ pj_thread_t *thread = pj_thread_this();
+
+ pj_assert(thread);
+
+ /* Calculate current usage. */
+ usage = (&stk_ptr > thread->stk_start) ? &stk_ptr - thread->stk_start :
+ thread->stk_start - &stk_ptr;
+
+ /* Assert if stack usage is dangerously high. */
+ pj_assert("STACK OVERFLOW!! " && (usage <= thread->stk_size - 128));
+
+ /* Keep statistic. */
+ if (usage > thread->stk_max_usage) {
+ thread->stk_max_usage = usage;
+ thread->caller_file = file;
+ thread->caller_line = line;
+ }
+
+}
+
+/*
+ * pj_thread_get_stack_max_usage()
+ */
+PJ_DEF(pj_uint32_t) pj_thread_get_stack_max_usage(pj_thread_t *thread)
+{
+ return thread->stk_max_usage;
+}
+
+/*
+ * pj_thread_get_stack_info()
+ */
+PJ_DEF(pj_status_t) pj_thread_get_stack_info( pj_thread_t *thread,
+ const char **file,
+ int *line )
+{
+ pj_assert(thread);
+
+ *file = thread->caller_file;
+ *line = thread->caller_line;
+ return 0;
+}
+
+#endif /* PJ_OS_HAS_CHECK_STACK */
+
+
+///////////////////////////////////////////////////////////////////////////////
+
+/*
+ * pj_atomic_create()
+ */
+PJ_DEF(pj_status_t) pj_atomic_create( pj_pool_t *pool,
+ pj_atomic_value_t initial,
+ pj_atomic_t **atomic_ptr)
+{
+ pj_atomic_t *atomic_var = pj_pool_alloc(pool, sizeof(pj_atomic_t));
+ if (!atomic_var)
+ return PJ_ENOMEM;
+
+ atomic_var->value = initial;
+ *atomic_ptr = atomic_var;
+
+ return PJ_SUCCESS;
+}
+
+/*
+ * pj_atomic_destroy()
+ */
+PJ_DEF(pj_status_t) pj_atomic_destroy( pj_atomic_t *var )
+{
+ PJ_UNUSED_ARG(var);
+ PJ_ASSERT_RETURN(var, PJ_EINVAL);
+
+ return 0;
+}
+
+/*
+ * pj_atomic_set()
+ */
+PJ_DEF(void) pj_atomic_set( pj_atomic_t *atomic_var, pj_atomic_value_t value)
+{
+ PJ_CHECK_STACK();
+
+ InterlockedExchange(&atomic_var->value, value);
+}
+
+/*
+ * pj_atomic_get()
+ */
+PJ_DEF(pj_atomic_value_t) pj_atomic_get(pj_atomic_t *atomic_var)
+{
+ PJ_CHECK_STACK();
+ PJ_ASSERT_RETURN(atomic_var, 0);
+
+ return atomic_var->value;
+}
+
/*
* pj_atomic_inc_and_get()
*/
@@ -526,13 +547,13 @@
#endif
}
-/*
- * pj_atomic_inc()
- */
-PJ_DEF(void) pj_atomic_inc(pj_atomic_t *atomic_var)
+/*
+ * pj_atomic_inc()
+ */
+PJ_DEF(void) pj_atomic_inc(pj_atomic_t *atomic_var)
{
- pj_atomic_inc_and_get(atomic_var);
-}
+ pj_atomic_inc_and_get(atomic_var);
+}
/*
* pj_atomic_dec_and_get()
@@ -548,26 +569,26 @@
#endif
}
-/*
- * pj_atomic_dec()
- */
-PJ_DEF(void) pj_atomic_dec(pj_atomic_t *atomic_var)
-{
- pj_atomic_dec_and_get(atomic_var);
-}
-
-/*
- * pj_atomic_add()
- */
-PJ_DEF(void) pj_atomic_add( pj_atomic_t *atomic_var,
- pj_atomic_value_t value )
-{
+/*
+ * pj_atomic_dec()
+ */
+PJ_DEF(void) pj_atomic_dec(pj_atomic_t *atomic_var)
+{
+ pj_atomic_dec_and_get(atomic_var);
+}
+
+/*
+ * pj_atomic_add()
+ */
+PJ_DEF(void) pj_atomic_add( pj_atomic_t *atomic_var,
+ pj_atomic_value_t value )
+{
#if defined(PJ_WIN32_WINNT) && PJ_WIN32_WINNT >= 0x0400
InterlockedExchangeAdd( &atomic_var->value, value );
#else
# error Fix me
-#endif
-}
+#endif
+}
/*
* pj_atomic_add_and_get()
@@ -581,635 +602,635 @@
#else
# error Fix me
#endif
-}
-
-///////////////////////////////////////////////////////////////////////////////
-/*
- * pj_thread_local_alloc()
- */
-PJ_DEF(pj_status_t) pj_thread_local_alloc(long *index)
-{
- PJ_ASSERT_RETURN(index != NULL, PJ_EINVAL);
-
- //Can't check stack because this function is called in the
- //beginning before main thread is initialized.
- //PJ_CHECK_STACK();
-
- *index = TlsAlloc();
-
- if (*index == TLS_OUT_OF_INDEXES)
- return PJ_RETURN_OS_ERROR(GetLastError());
- else
- return PJ_SUCCESS;
-}
-
-/*
- * pj_thread_local_free()
- */
-PJ_DEF(void) pj_thread_local_free(long index)
-{
- PJ_CHECK_STACK();
- TlsFree(index);
-}
-
-/*
- * pj_thread_local_set()
- */
-PJ_DEF(pj_status_t) pj_thread_local_set(long index, void *value)
-{
- BOOL rc;
-
- //Can't check stack because this function is called in the
- //beginning before main thread is initialized.
- //PJ_CHECK_STACK();
- rc = TlsSetValue(index, value);
- return rc!=0 ? PJ_SUCCESS : PJ_RETURN_OS_ERROR(GetLastError());
-}
-
-/*
- * pj_thread_local_get()
- */
-PJ_DEF(void*) pj_thread_local_get(long index)
-{
- //Can't check stack because this function is called
- //by PJ_CHECK_STACK() itself!!!
- //PJ_CHECK_STACK();
- return TlsGetValue(index);
-}
-
-///////////////////////////////////////////////////////////////////////////////
-static pj_status_t init_mutex(pj_mutex_t *mutex, const char *name)
-{
-
- PJ_CHECK_STACK();
-
-#if PJ_WIN32_WINNT >= 0x0400
- InitializeCriticalSection(&mutex->crit);
-#else
- mutex->hMutex = CreateMutex(NULL, FALSE, NULL);
- if (!mutex->hMutex) {
- return PJ_RETURN_OS_ERROR(GetLastError());
- }
-#endif
-
-#if PJ_DEBUG
- /* Set owner. */
- mutex->nesting_level = 0;
- mutex->owner = NULL;
-#endif
-
- /* Set name. */
- if (!name) {
- name = "mtx%p";
- }
- if (strchr(name, '%')) {
- pj_snprintf(mutex->obj_name, PJ_MAX_OBJ_NAME, name, mutex);
- } else {
- strncpy(mutex->obj_name, name, PJ_MAX_OBJ_NAME);
- mutex->obj_name[PJ_MAX_OBJ_NAME-1] = '\0';
- }
-
- PJ_LOG(6, (mutex->obj_name, "Mutex created"));
- return PJ_SUCCESS;
-}
-
-/*
- * pj_mutex_create()
- */
-PJ_DEF(pj_status_t) pj_mutex_create(pj_pool_t *pool,
- const char *name,
- int type,
- pj_mutex_t **mutex_ptr)
-{
- pj_status_t rc;
- pj_mutex_t *mutex;
-
- PJ_UNUSED_ARG(type);
- PJ_ASSERT_RETURN(pool && mutex_ptr, PJ_EINVAL);
-
- mutex = pj_pool_alloc(pool, sizeof(*mutex));
- if (!mutex)
- return PJ_ENOMEM;
-
- rc = init_mutex(mutex, name);
- if (rc != PJ_SUCCESS)
- return rc;
-
- *mutex_ptr = mutex;
-
- return PJ_SUCCESS;
-}
-
-/*
- * pj_mutex_create_simple()
- */
-PJ_DEF(pj_status_t) pj_mutex_create_simple( pj_pool_t *pool,
- const char *name,
- pj_mutex_t **mutex )
-{
- return pj_mutex_create(pool, name, PJ_MUTEX_SIMPLE, mutex);
-}
-
-/*
- * pj_mutex_create_recursive()
- */
-PJ_DEF(pj_status_t) pj_mutex_create_recursive( pj_pool_t *pool,
- const char *name,
- pj_mutex_t **mutex )
-{
- return pj_mutex_create(pool, name, PJ_MUTEX_RECURSE, mutex);
-}
-
-/*
- * pj_mutex_lock()
- */
-PJ_DEF(pj_status_t) pj_mutex_lock(pj_mutex_t *mutex)
-{
- pj_status_t status;
-
- PJ_CHECK_STACK();
- PJ_ASSERT_RETURN(mutex, PJ_EINVAL);
-
- PJ_LOG(6,(mutex->obj_name, "Mutex: thread %s is waiting",
- pj_thread_this()->obj_name));
-
-#if PJ_WIN32_WINNT >= 0x0400
- EnterCriticalSection(&mutex->crit);
- status=PJ_SUCCESS;
-#else
- if (WaitForSingleObject(mutex->hMutex, INFINITE)==WAIT_OBJECT_0)
- status = PJ_SUCCESS;
- else
- status = PJ_STATUS_FROM_OS(GetLastError());
-
-#endif
- PJ_LOG(6,(mutex->obj_name,
- (status==PJ_SUCCESS ? "Mutex acquired by thread %s" : "FAILED by %s"),
- pj_thread_this()->obj_name));
-
-#if PJ_DEBUG
- if (status == PJ_SUCCESS) {
- mutex->owner = pj_thread_this();
- ++mutex->nesting_level;
- }
-#endif
-
- return status;
-}
-
-/*
- * pj_mutex_unlock()
- */
-PJ_DEF(pj_status_t) pj_mutex_unlock(pj_mutex_t *mutex)
-{
- pj_status_t status;
-
- PJ_CHECK_STACK();
- PJ_ASSERT_RETURN(mutex, PJ_EINVAL);
-
-#if PJ_DEBUG
- pj_assert(mutex->owner == pj_thread_this());
- if (--mutex->nesting_level == 0) {
- mutex->owner = NULL;
- }
-#endif
-
- PJ_LOG(6,(mutex->obj_name, "Mutex released by thread %s",
- pj_thread_this()->obj_name));
-
-#if PJ_WIN32_WINNT >= 0x0400
- LeaveCriticalSection(&mutex->crit);
- status=PJ_SUCCESS;
-#else
- status = ReleaseMutex(mutex->hMutex) ? PJ_SUCCESS :
- PJ_STATUS_FROM_OS(GetLastError());
-#endif
- return status;
-}
-
-/*
- * pj_mutex_trylock()
- */
-PJ_DEF(pj_status_t) pj_mutex_trylock(pj_mutex_t *mutex)
-{
- pj_status_t status;
-
- PJ_CHECK_STACK();
- PJ_ASSERT_RETURN(mutex, PJ_EINVAL);
-
-#if PJ_WIN32_WINNT >= 0x0400
- status=TryEnterCriticalSection(&mutex->crit) ? PJ_SUCCESS : PJ_EUNKNOWN;
-#else
- status = WaitForSingleObject(mutex->hMutex, 0)==WAIT_OBJECT_0 ?
- PJ_SUCCESS : PJ_ETIMEDOUT;
-#endif
- if (status==PJ_SUCCESS) {
- PJ_LOG(6,(mutex->obj_name, "Mutex acquired by thread %s",
- pj_thread_this()->obj_name));
-
-#if PJ_DEBUG
- mutex->owner = pj_thread_this();
- ++mutex->nesting_level;
-#endif
- }
- return status;
-}
-
-/*
- * pj_mutex_destroy()
- */
-PJ_DEF(pj_status_t) pj_mutex_destroy(pj_mutex_t *mutex)
-{
- PJ_CHECK_STACK();
- PJ_ASSERT_RETURN(mutex, PJ_EINVAL);
-
- PJ_LOG(6,(mutex->obj_name, "Mutex destroyed"));
-
-#if PJ_WIN32_WINNT >= 0x0400
- DeleteCriticalSection(&mutex->crit);
- return PJ_SUCCESS;
-#else
- return CloseHandle(mutex->hMutex) ? PJ_SUCCESS :
- PJ_RETURN_OS_ERROR(GetLastError());
-#endif
-}
-
-#if PJ_DEBUG
-/*
- * pj_mutex_is_locked()
- */
-PJ_DEF(pj_bool_t) pj_mutex_is_locked(pj_mutex_t *mutex)
-{
- return mutex->owner == pj_thread_this();
-}
-#endif
-
-///////////////////////////////////////////////////////////////////////////////
-/*
- * pj_enter_critical_section()
- */
-PJ_DEF(void) pj_enter_critical_section(void)
-{
- pj_mutex_lock(&critical_section_mutex);
-}
-
-
-/*
- * pj_leave_critical_section()
- */
-PJ_DEF(void) pj_leave_critical_section(void)
-{
- pj_mutex_unlock(&critical_section_mutex);
-}
-
-///////////////////////////////////////////////////////////////////////////////
-#if defined(PJ_HAS_SEMAPHORE) && PJ_HAS_SEMAPHORE != 0
-
-/*
- * pj_sem_create()
- */
-PJ_DEF(pj_status_t) pj_sem_create( pj_pool_t *pool,
- const char *name,
- unsigned initial,
- unsigned max,
- pj_sem_t **sem_ptr)
-{
- pj_sem_t *sem;
-
- PJ_CHECK_STACK();
- PJ_ASSERT_RETURN(pool && sem_ptr, PJ_EINVAL);
-
- sem = pj_pool_alloc(pool, sizeof(*sem));
- sem->hSemaphore = CreateSemaphore(NULL, initial, max, NULL);
- if (!sem->hSemaphore)
- return PJ_RETURN_OS_ERROR(GetLastError());
-
- /* Set name. */
- if (!name) {
- name = "sem%p";
- }
- if (strchr(name, '%')) {
- pj_snprintf(sem->obj_name, PJ_MAX_OBJ_NAME, name, sem);
- } else {
- strncpy(sem->obj_name, name, PJ_MAX_OBJ_NAME);
- sem->obj_name[PJ_MAX_OBJ_NAME-1] = '\0';
- }
-
- PJ_LOG(6, (sem->obj_name, "Semaphore created"));
-
- *sem_ptr = sem;
- return PJ_SUCCESS;
-}
-
-static pj_status_t pj_sem_wait_for(pj_sem_t *sem, unsigned timeout)
-{
- DWORD result;
-
- PJ_CHECK_STACK();
- PJ_ASSERT_RETURN(sem, PJ_EINVAL);
-
- PJ_LOG(6, (sem->obj_name, "Semaphore: thread %s is waiting",
- pj_thread_this()->obj_name));
-
- result = WaitForSingleObject(sem->hSemaphore, timeout);
- if (result == WAIT_OBJECT_0) {
- PJ_LOG(6, (sem->obj_name, "Semaphore acquired by thread %s",
- pj_thread_this()->obj_name));
- } else {
- PJ_LOG(6, (sem->obj_name, "Semaphore: thread %s FAILED to acquire",
- pj_thread_this()->obj_name));
- }
-
- if (result==WAIT_OBJECT_0)
- return PJ_SUCCESS;
- else if (result==WAIT_TIMEOUT)
- return PJ_ETIMEDOUT;
- else
- return PJ_RETURN_OS_ERROR(GetLastError());
-}
-
-/*
- * pj_sem_wait()
- */
-PJ_DEF(pj_status_t) pj_sem_wait(pj_sem_t *sem)
-{
- PJ_CHECK_STACK();
- PJ_ASSERT_RETURN(sem, PJ_EINVAL);
-
- return pj_sem_wait_for(sem, INFINITE);
-}
-
-/*
- * pj_sem_trywait()
- */
-PJ_DEF(pj_status_t) pj_sem_trywait(pj_sem_t *sem)
-{
- PJ_CHECK_STACK();
- PJ_ASSERT_RETURN(sem, PJ_EINVAL);
-
- return pj_sem_wait_for(sem, 0);
-}
-
-/*
- * pj_sem_post()
- */
-PJ_DEF(pj_status_t) pj_sem_post(pj_sem_t *sem)
-{
- PJ_CHECK_STACK();
- PJ_ASSERT_RETURN(sem, PJ_EINVAL);
-
- PJ_LOG(6, (sem->obj_name, "Semaphore released by thread %s",
- pj_thread_this()->obj_name));
-
- if (ReleaseSemaphore(sem->hSemaphore, 1, NULL))
- return PJ_SUCCESS;
- else
- return PJ_RETURN_OS_ERROR(GetLastError());
-}
-
-/*
- * pj_sem_destroy()
- */
-PJ_DEF(pj_status_t) pj_sem_destroy(pj_sem_t *sem)
-{
- PJ_CHECK_STACK();
- PJ_ASSERT_RETURN(sem, PJ_EINVAL);
-
- PJ_LOG(6, (sem->obj_name, "Semaphore destroyed by thread %s",
- pj_thread_this()->obj_name));
-
- if (CloseHandle(sem->hSemaphore))
- return PJ_SUCCESS;
- else
- return PJ_RETURN_OS_ERROR(GetLastError());
-}
-
-#endif /* PJ_HAS_SEMAPHORE */
-///////////////////////////////////////////////////////////////////////////////
-
-
-#if defined(PJ_HAS_EVENT_OBJ) && PJ_HAS_EVENT_OBJ != 0
-
-/*
- * pj_event_create()
- */
-PJ_DEF(pj_status_t) pj_event_create( pj_pool_t *pool,
- const char *name,
- pj_bool_t manual_reset,
- pj_bool_t initial,
- pj_event_t **event_ptr)
-{
- pj_event_t *event;
-
- PJ_CHECK_STACK();
- PJ_ASSERT_RETURN(pool && event_ptr, PJ_EINVAL);
-
- event = pj_pool_alloc(pool, sizeof(*event));
- if (!event)
- return PJ_ENOMEM;
-
- event->hEvent = CreateEvent(NULL, manual_reset?TRUE:FALSE,
- initial?TRUE:FALSE, NULL);
-
- if (!event->hEvent)
- return PJ_RETURN_OS_ERROR(GetLastError());
-
- /* Set name. */
- if (!name) {
- name = "evt%p";
- }
- if (strchr(name, '%')) {
- pj_snprintf(event->obj_name, PJ_MAX_OBJ_NAME, name, event);
- } else {
- strncpy(event->obj_name, name, PJ_MAX_OBJ_NAME);
- event->obj_name[PJ_MAX_OBJ_NAME-1] = '\0';
- }
-
- PJ_LOG(6, (event->obj_name, "Event created"));
-
- *event_ptr = event;
- return PJ_SUCCESS;
-}
-
-static pj_status_t pj_event_wait_for(pj_event_t *event, unsigned timeout)
-{
- DWORD result;
-
- PJ_CHECK_STACK();
- PJ_ASSERT_RETURN(event, PJ_EINVAL);
-
- PJ_LOG(6, (event->obj_name, "Event: thread %s is waiting",
- pj_thread_this()->obj_name));
-
- result = WaitForSingleObject(event->hEvent, timeout);
- if (result == WAIT_OBJECT_0) {
- PJ_LOG(6, (event->obj_name, "Event: thread %s is released",
- pj_thread_this()->obj_name));
- } else {
- PJ_LOG(6, (event->obj_name, "Event: thread %s FAILED to acquire",
- pj_thread_this()->obj_name));
- }
-
- if (result==WAIT_OBJECT_0)
- return PJ_SUCCESS;
- else if (result==WAIT_TIMEOUT)
- return PJ_ETIMEDOUT;
- else
- return PJ_RETURN_OS_ERROR(GetLastError());
-}
-
-/*
- * pj_event_wait()
- */
-PJ_DEF(pj_status_t) pj_event_wait(pj_event_t *event)
-{
- PJ_ASSERT_RETURN(event, PJ_EINVAL);
-
- return pj_event_wait_for(event, INFINITE);
-}
-
-/*
- * pj_event_trywait()
- */
-PJ_DEF(pj_status_t) pj_event_trywait(pj_event_t *event)
-{
- PJ_ASSERT_RETURN(event, PJ_EINVAL);
-
- return pj_event_wait_for(event, 0);
-}
-
-/*
- * pj_event_set()
- */
-PJ_DEF(pj_status_t) pj_event_set(pj_event_t *event)
-{
- PJ_CHECK_STACK();
- PJ_ASSERT_RETURN(event, PJ_EINVAL);
-
- PJ_LOG(6, (event->obj_name, "Setting event"));
-
- if (SetEvent(event->hEvent))
- return PJ_SUCCESS;
- else
- return PJ_RETURN_OS_ERROR(GetLastError());
-}
-
-/*
- * pj_event_pulse()
- */
-PJ_DEF(pj_status_t) pj_event_pulse(pj_event_t *event)
-{
- PJ_CHECK_STACK();
- PJ_ASSERT_RETURN(event, PJ_EINVAL);
-
- PJ_LOG(6, (event->obj_name, "Pulsing event"));
-
- if (PulseEvent(event->hEvent))
- return PJ_SUCCESS;
- else
- return PJ_RETURN_OS_ERROR(GetLastError());
-}
-
-/*
- * pj_event_reset()
- */
-PJ_DEF(pj_status_t) pj_event_reset(pj_event_t *event)
-{
- PJ_CHECK_STACK();
- PJ_ASSERT_RETURN(event, PJ_EINVAL);
-
- PJ_LOG(6, (event->obj_name, "Event is reset"));
-
- if (ResetEvent(event->hEvent))
- return PJ_SUCCESS;
- else
- return PJ_RETURN_OS_ERROR(GetLastError());
-}
-
-/*
- * pj_event_destroy()
- */
-PJ_DEF(pj_status_t) pj_event_destroy(pj_event_t *event)
-{
- PJ_CHECK_STACK();
- PJ_ASSERT_RETURN(event, PJ_EINVAL);
-
- PJ_LOG(6, (event->obj_name, "Event is destroying"));
-
- if (CloseHandle(event->hEvent))
- return PJ_SUCCESS;
- else
- return PJ_RETURN_OS_ERROR(GetLastError());
-}
-
-#endif /* PJ_HAS_EVENT_OBJ */
-
-///////////////////////////////////////////////////////////////////////////////
-#if defined(PJ_TERM_HAS_COLOR) && PJ_TERM_HAS_COLOR != 0
-/*
- * Terminal color
- */
-
-static WORD pj_color_to_os_attr(pj_color_t color)
-{
- WORD attr = 0;
-
- if (color & PJ_TERM_COLOR_R)
- attr |= FOREGROUND_RED;
- if (color & PJ_TERM_COLOR_G)
- attr |= FOREGROUND_GREEN;
- if (color & PJ_TERM_COLOR_B)
- attr |= FOREGROUND_BLUE;
- if (color & PJ_TERM_COLOR_BRIGHT)
- attr |= FOREGROUND_INTENSITY;
-
- return attr;
-}
-
-static pj_color_t os_attr_to_pj_color(WORD attr)
-{
- int color = 0;
-
- if (attr & FOREGROUND_RED)
- color |= PJ_TERM_COLOR_R;
- if (attr & FOREGROUND_GREEN)
- color |= PJ_TERM_COLOR_G;
- if (attr & FOREGROUND_BLUE)
- color |= PJ_TERM_COLOR_B;
- if (attr & FOREGROUND_INTENSITY)
- color |= PJ_TERM_COLOR_BRIGHT;
-
- return color;
-}
-
-
-/*
- * pj_term_set_color()
- */
-PJ_DEF(pj_status_t) pj_term_set_color(pj_color_t color)
-{
- BOOL rc;
- WORD attr = 0;
-
- PJ_CHECK_STACK();
-
- attr = pj_color_to_os_attr(color);
- rc = SetConsoleTextAttribute( GetStdHandle(STD_OUTPUT_HANDLE), attr);
- return rc ? PJ_SUCCESS : PJ_RETURN_OS_ERROR(GetLastError());
-}
-
-/*
- * pj_term_get_color()
- * Get current terminal foreground color.
- */
-PJ_DEF(pj_color_t) pj_term_get_color(void)
-{
- CONSOLE_SCREEN_BUFFER_INFO info;
-
- PJ_CHECK_STACK();
-
- GetConsoleScreenBufferInfo( GetStdHandle(STD_OUTPUT_HANDLE), &info);
- return os_attr_to_pj_color(info.wAttributes);
-}
-
-#endif /* PJ_TERM_HAS_COLOR */
+}
+
+///////////////////////////////////////////////////////////////////////////////
+/*
+ * pj_thread_local_alloc()
+ */
+PJ_DEF(pj_status_t) pj_thread_local_alloc(long *index)
+{
+ PJ_ASSERT_RETURN(index != NULL, PJ_EINVAL);
+
+ //Can't check stack because this function is called in the
+ //beginning before main thread is initialized.
+ //PJ_CHECK_STACK();
+
+ *index = TlsAlloc();
+
+ if (*index == TLS_OUT_OF_INDEXES)
+ return PJ_RETURN_OS_ERROR(GetLastError());
+ else
+ return PJ_SUCCESS;
+}
+
+/*
+ * pj_thread_local_free()
+ */
+PJ_DEF(void) pj_thread_local_free(long index)
+{
+ PJ_CHECK_STACK();
+ TlsFree(index);
+}
+
+/*
+ * pj_thread_local_set()
+ */
+PJ_DEF(pj_status_t) pj_thread_local_set(long index, void *value)
+{
+ BOOL rc;
+
+ //Can't check stack because this function is called in the
+ //beginning before main thread is initialized.
+ //PJ_CHECK_STACK();
+ rc = TlsSetValue(index, value);
+ return rc!=0 ? PJ_SUCCESS : PJ_RETURN_OS_ERROR(GetLastError());
+}
+
+/*
+ * pj_thread_local_get()
+ */
+PJ_DEF(void*) pj_thread_local_get(long index)
+{
+ //Can't check stack because this function is called
+ //by PJ_CHECK_STACK() itself!!!
+ //PJ_CHECK_STACK();
+ return TlsGetValue(index);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+static pj_status_t init_mutex(pj_mutex_t *mutex, const char *name)
+{
+
+ PJ_CHECK_STACK();
+
+#if PJ_WIN32_WINNT >= 0x0400
+ InitializeCriticalSection(&mutex->crit);
+#else
+ mutex->hMutex = CreateMutex(NULL, FALSE, NULL);
+ if (!mutex->hMutex) {
+ return PJ_RETURN_OS_ERROR(GetLastError());
+ }
+#endif
+
+#if PJ_DEBUG
+ /* Set owner. */
+ mutex->nesting_level = 0;
+ mutex->owner = NULL;
+#endif
+
+ /* Set name. */
+ if (!name) {
+ name = "mtx%p";
+ }
+ if (strchr(name, '%')) {
+ pj_snprintf(mutex->obj_name, PJ_MAX_OBJ_NAME, name, mutex);
+ } else {
+ strncpy(mutex->obj_name, name, PJ_MAX_OBJ_NAME);
+ mutex->obj_name[PJ_MAX_OBJ_NAME-1] = '\0';
+ }
+
+ PJ_LOG(6, (mutex->obj_name, "Mutex created"));
+ return PJ_SUCCESS;
+}
+
+/*
+ * pj_mutex_create()
+ */
+PJ_DEF(pj_status_t) pj_mutex_create(pj_pool_t *pool,
+ const char *name,
+ int type,
+ pj_mutex_t **mutex_ptr)
+{
+ pj_status_t rc;
+ pj_mutex_t *mutex;
+
+ PJ_UNUSED_ARG(type);
+ PJ_ASSERT_RETURN(pool && mutex_ptr, PJ_EINVAL);
+
+ mutex = pj_pool_alloc(pool, sizeof(*mutex));
+ if (!mutex)
+ return PJ_ENOMEM;
+
+ rc = init_mutex(mutex, name);
+ if (rc != PJ_SUCCESS)
+ return rc;
+
+ *mutex_ptr = mutex;
+
+ return PJ_SUCCESS;
+}
+
+/*
+ * pj_mutex_create_simple()
+ */
+PJ_DEF(pj_status_t) pj_mutex_create_simple( pj_pool_t *pool,
+ const char *name,
+ pj_mutex_t **mutex )
+{
+ return pj_mutex_create(pool, name, PJ_MUTEX_SIMPLE, mutex);
+}
+
+/*
+ * pj_mutex_create_recursive()
+ */
+PJ_DEF(pj_status_t) pj_mutex_create_recursive( pj_pool_t *pool,
+ const char *name,
+ pj_mutex_t **mutex )
+{
+ return pj_mutex_create(pool, name, PJ_MUTEX_RECURSE, mutex);
+}
+
+/*
+ * pj_mutex_lock()
+ */
+PJ_DEF(pj_status_t) pj_mutex_lock(pj_mutex_t *mutex)
+{
+ pj_status_t status;
+
+ PJ_CHECK_STACK();
+ PJ_ASSERT_RETURN(mutex, PJ_EINVAL);
+
+ PJ_LOG(6,(mutex->obj_name, "Mutex: thread %s is waiting",
+ pj_thread_this()->obj_name));
+
+#if PJ_WIN32_WINNT >= 0x0400
+ EnterCriticalSection(&mutex->crit);
+ status=PJ_SUCCESS;
+#else
+ if (WaitForSingleObject(mutex->hMutex, INFINITE)==WAIT_OBJECT_0)
+ status = PJ_SUCCESS;
+ else
+ status = PJ_STATUS_FROM_OS(GetLastError());
+
+#endif
+ PJ_LOG(6,(mutex->obj_name,
+ (status==PJ_SUCCESS ? "Mutex acquired by thread %s" : "FAILED by %s"),
+ pj_thread_this()->obj_name));
+
+#if PJ_DEBUG
+ if (status == PJ_SUCCESS) {
+ mutex->owner = pj_thread_this();
+ ++mutex->nesting_level;
+ }
+#endif
+
+ return status;
+}
+
+/*
+ * pj_mutex_unlock()
+ */
+PJ_DEF(pj_status_t) pj_mutex_unlock(pj_mutex_t *mutex)
+{
+ pj_status_t status;
+
+ PJ_CHECK_STACK();
+ PJ_ASSERT_RETURN(mutex, PJ_EINVAL);
+
+#if PJ_DEBUG
+ pj_assert(mutex->owner == pj_thread_this());
+ if (--mutex->nesting_level == 0) {
+ mutex->owner = NULL;
+ }
+#endif
+
+ PJ_LOG(6,(mutex->obj_name, "Mutex released by thread %s",
+ pj_thread_this()->obj_name));
+
+#if PJ_WIN32_WINNT >= 0x0400
+ LeaveCriticalSection(&mutex->crit);
+ status=PJ_SUCCESS;
+#else
+ status = ReleaseMutex(mutex->hMutex) ? PJ_SUCCESS :
+ PJ_STATUS_FROM_OS(GetLastError());
+#endif
+ return status;
+}
+
+/*
+ * pj_mutex_trylock()
+ */
+PJ_DEF(pj_status_t) pj_mutex_trylock(pj_mutex_t *mutex)
+{
+ pj_status_t status;
+
+ PJ_CHECK_STACK();
+ PJ_ASSERT_RETURN(mutex, PJ_EINVAL);
+
+#if PJ_WIN32_WINNT >= 0x0400
+ status=TryEnterCriticalSection(&mutex->crit) ? PJ_SUCCESS : PJ_EUNKNOWN;
+#else
+ status = WaitForSingleObject(mutex->hMutex, 0)==WAIT_OBJECT_0 ?
+ PJ_SUCCESS : PJ_ETIMEDOUT;
+#endif
+ if (status==PJ_SUCCESS) {
+ PJ_LOG(6,(mutex->obj_name, "Mutex acquired by thread %s",
+ pj_thread_this()->obj_name));
+
+#if PJ_DEBUG
+ mutex->owner = pj_thread_this();
+ ++mutex->nesting_level;
+#endif
+ }
+ return status;
+}
+
+/*
+ * pj_mutex_destroy()
+ */
+PJ_DEF(pj_status_t) pj_mutex_destroy(pj_mutex_t *mutex)
+{
+ PJ_CHECK_STACK();
+ PJ_ASSERT_RETURN(mutex, PJ_EINVAL);
+
+ PJ_LOG(6,(mutex->obj_name, "Mutex destroyed"));
+
+#if PJ_WIN32_WINNT >= 0x0400
+ DeleteCriticalSection(&mutex->crit);
+ return PJ_SUCCESS;
+#else
+ return CloseHandle(mutex->hMutex) ? PJ_SUCCESS :
+ PJ_RETURN_OS_ERROR(GetLastError());
+#endif
+}
+
+#if PJ_DEBUG
+/*
+ * pj_mutex_is_locked()
+ */
+PJ_DEF(pj_bool_t) pj_mutex_is_locked(pj_mutex_t *mutex)
+{
+ return mutex->owner == pj_thread_this();
+}
+#endif
+
+///////////////////////////////////////////////////////////////////////////////
+/*
+ * pj_enter_critical_section()
+ */
+PJ_DEF(void) pj_enter_critical_section(void)
+{
+ pj_mutex_lock(&critical_section_mutex);
+}
+
+
+/*
+ * pj_leave_critical_section()
+ */
+PJ_DEF(void) pj_leave_critical_section(void)
+{
+ pj_mutex_unlock(&critical_section_mutex);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+#if defined(PJ_HAS_SEMAPHORE) && PJ_HAS_SEMAPHORE != 0
+
+/*
+ * pj_sem_create()
+ */
+PJ_DEF(pj_status_t) pj_sem_create( pj_pool_t *pool,
+ const char *name,
+ unsigned initial,
+ unsigned max,
+ pj_sem_t **sem_ptr)
+{
+ pj_sem_t *sem;
+
+ PJ_CHECK_STACK();
+ PJ_ASSERT_RETURN(pool && sem_ptr, PJ_EINVAL);
+
+ sem = pj_pool_alloc(pool, sizeof(*sem));
+ sem->hSemaphore = CreateSemaphore(NULL, initial, max, NULL);
+ if (!sem->hSemaphore)
+ return PJ_RETURN_OS_ERROR(GetLastError());
+
+ /* Set name. */
+ if (!name) {
+ name = "sem%p";
+ }
+ if (strchr(name, '%')) {
+ pj_snprintf(sem->obj_name, PJ_MAX_OBJ_NAME, name, sem);
+ } else {
+ strncpy(sem->obj_name, name, PJ_MAX_OBJ_NAME);
+ sem->obj_name[PJ_MAX_OBJ_NAME-1] = '\0';
+ }
+
+ PJ_LOG(6, (sem->obj_name, "Semaphore created"));
+
+ *sem_ptr = sem;
+ return PJ_SUCCESS;
+}
+
+static pj_status_t pj_sem_wait_for(pj_sem_t *sem, unsigned timeout)
+{
+ DWORD result;
+
+ PJ_CHECK_STACK();
+ PJ_ASSERT_RETURN(sem, PJ_EINVAL);
+
+ PJ_LOG(6, (sem->obj_name, "Semaphore: thread %s is waiting",
+ pj_thread_this()->obj_name));
+
+ result = WaitForSingleObject(sem->hSemaphore, timeout);
+ if (result == WAIT_OBJECT_0) {
+ PJ_LOG(6, (sem->obj_name, "Semaphore acquired by thread %s",
+ pj_thread_this()->obj_name));
+ } else {
+ PJ_LOG(6, (sem->obj_name, "Semaphore: thread %s FAILED to acquire",
+ pj_thread_this()->obj_name));
+ }
+
+ if (result==WAIT_OBJECT_0)
+ return PJ_SUCCESS;
+ else if (result==WAIT_TIMEOUT)
+ return PJ_ETIMEDOUT;
+ else
+ return PJ_RETURN_OS_ERROR(GetLastError());
+}
+
+/*
+ * pj_sem_wait()
+ */
+PJ_DEF(pj_status_t) pj_sem_wait(pj_sem_t *sem)
+{
+ PJ_CHECK_STACK();
+ PJ_ASSERT_RETURN(sem, PJ_EINVAL);
+
+ return pj_sem_wait_for(sem, INFINITE);
+}
+
+/*
+ * pj_sem_trywait()
+ */
+PJ_DEF(pj_status_t) pj_sem_trywait(pj_sem_t *sem)
+{
+ PJ_CHECK_STACK();
+ PJ_ASSERT_RETURN(sem, PJ_EINVAL);
+
+ return pj_sem_wait_for(sem, 0);
+}
+
+/*
+ * pj_sem_post()
+ */
+PJ_DEF(pj_status_t) pj_sem_post(pj_sem_t *sem)
+{
+ PJ_CHECK_STACK();
+ PJ_ASSERT_RETURN(sem, PJ_EINVAL);
+
+ PJ_LOG(6, (sem->obj_name, "Semaphore released by thread %s",
+ pj_thread_this()->obj_name));
+
+ if (ReleaseSemaphore(sem->hSemaphore, 1, NULL))
+ return PJ_SUCCESS;
+ else
+ return PJ_RETURN_OS_ERROR(GetLastError());
+}
+
+/*
+ * pj_sem_destroy()
+ */
+PJ_DEF(pj_status_t) pj_sem_destroy(pj_sem_t *sem)
+{
+ PJ_CHECK_STACK();
+ PJ_ASSERT_RETURN(sem, PJ_EINVAL);
+
+ PJ_LOG(6, (sem->obj_name, "Semaphore destroyed by thread %s",
+ pj_thread_this()->obj_name));
+
+ if (CloseHandle(sem->hSemaphore))
+ return PJ_SUCCESS;
+ else
+ return PJ_RETURN_OS_ERROR(GetLastError());
+}
+
+#endif /* PJ_HAS_SEMAPHORE */
+///////////////////////////////////////////////////////////////////////////////
+
+
+#if defined(PJ_HAS_EVENT_OBJ) && PJ_HAS_EVENT_OBJ != 0
+
+/*
+ * pj_event_create()
+ */
+PJ_DEF(pj_status_t) pj_event_create( pj_pool_t *pool,
+ const char *name,
+ pj_bool_t manual_reset,
+ pj_bool_t initial,
+ pj_event_t **event_ptr)
+{
+ pj_event_t *event;
+
+ PJ_CHECK_STACK();
+ PJ_ASSERT_RETURN(pool && event_ptr, PJ_EINVAL);
+
+ event = pj_pool_alloc(pool, sizeof(*event));
+ if (!event)
+ return PJ_ENOMEM;
+
+ event->hEvent = CreateEvent(NULL, manual_reset?TRUE:FALSE,
+ initial?TRUE:FALSE, NULL);
+
+ if (!event->hEvent)
+ return PJ_RETURN_OS_ERROR(GetLastError());
+
+ /* Set name. */
+ if (!name) {
+ name = "evt%p";
+ }
+ if (strchr(name, '%')) {
+ pj_snprintf(event->obj_name, PJ_MAX_OBJ_NAME, name, event);
+ } else {
+ strncpy(event->obj_name, name, PJ_MAX_OBJ_NAME);
+ event->obj_name[PJ_MAX_OBJ_NAME-1] = '\0';
+ }
+
+ PJ_LOG(6, (event->obj_name, "Event created"));
+
+ *event_ptr = event;
+ return PJ_SUCCESS;
+}
+
+static pj_status_t pj_event_wait_for(pj_event_t *event, unsigned timeout)
+{
+ DWORD result;
+
+ PJ_CHECK_STACK();
+ PJ_ASSERT_RETURN(event, PJ_EINVAL);
+
+ PJ_LOG(6, (event->obj_name, "Event: thread %s is waiting",
+ pj_thread_this()->obj_name));
+
+ result = WaitForSingleObject(event->hEvent, timeout);
+ if (result == WAIT_OBJECT_0) {
+ PJ_LOG(6, (event->obj_name, "Event: thread %s is released",
+ pj_thread_this()->obj_name));
+ } else {
+ PJ_LOG(6, (event->obj_name, "Event: thread %s FAILED to acquire",
+ pj_thread_this()->obj_name));
+ }
+
+ if (result==WAIT_OBJECT_0)
+ return PJ_SUCCESS;
+ else if (result==WAIT_TIMEOUT)
+ return PJ_ETIMEDOUT;
+ else
+ return PJ_RETURN_OS_ERROR(GetLastError());
+}
+
+/*
+ * pj_event_wait()
+ */
+PJ_DEF(pj_status_t) pj_event_wait(pj_event_t *event)
+{
+ PJ_ASSERT_RETURN(event, PJ_EINVAL);
+
+ return pj_event_wait_for(event, INFINITE);
+}
+
+/*
+ * pj_event_trywait()
+ */
+PJ_DEF(pj_status_t) pj_event_trywait(pj_event_t *event)
+{
+ PJ_ASSERT_RETURN(event, PJ_EINVAL);
+
+ return pj_event_wait_for(event, 0);
+}
+
+/*
+ * pj_event_set()
+ */
+PJ_DEF(pj_status_t) pj_event_set(pj_event_t *event)
+{
+ PJ_CHECK_STACK();
+ PJ_ASSERT_RETURN(event, PJ_EINVAL);
+
+ PJ_LOG(6, (event->obj_name, "Setting event"));
+
+ if (SetEvent(event->hEvent))
+ return PJ_SUCCESS;
+ else
+ return PJ_RETURN_OS_ERROR(GetLastError());
+}
+
+/*
+ * pj_event_pulse()
+ */
+PJ_DEF(pj_status_t) pj_event_pulse(pj_event_t *event)
+{
+ PJ_CHECK_STACK();
+ PJ_ASSERT_RETURN(event, PJ_EINVAL);
+
+ PJ_LOG(6, (event->obj_name, "Pulsing event"));
+
+ if (PulseEvent(event->hEvent))
+ return PJ_SUCCESS;
+ else
+ return PJ_RETURN_OS_ERROR(GetLastError());
+}
+
+/*
+ * pj_event_reset()
+ */
+PJ_DEF(pj_status_t) pj_event_reset(pj_event_t *event)
+{
+ PJ_CHECK_STACK();
+ PJ_ASSERT_RETURN(event, PJ_EINVAL);
+
+ PJ_LOG(6, (event->obj_name, "Event is reset"));
+
+ if (ResetEvent(event->hEvent))
+ return PJ_SUCCESS;
+ else
+ return PJ_RETURN_OS_ERROR(GetLastError());
+}
+
+/*
+ * pj_event_destroy()
+ */
+PJ_DEF(pj_status_t) pj_event_destroy(pj_event_t *event)
+{
+ PJ_CHECK_STACK();
+ PJ_ASSERT_RETURN(event, PJ_EINVAL);
+
+ PJ_LOG(6, (event->obj_name, "Event is destroying"));
+
+ if (CloseHandle(event->hEvent))
+ return PJ_SUCCESS;
+ else
+ return PJ_RETURN_OS_ERROR(GetLastError());
+}
+
+#endif /* PJ_HAS_EVENT_OBJ */
+
+///////////////////////////////////////////////////////////////////////////////
+#if defined(PJ_TERM_HAS_COLOR) && PJ_TERM_HAS_COLOR != 0
+/*
+ * Terminal color
+ */
+
+static WORD pj_color_to_os_attr(pj_color_t color)
+{
+ WORD attr = 0;
+
+ if (color & PJ_TERM_COLOR_R)
+ attr |= FOREGROUND_RED;
+ if (color & PJ_TERM_COLOR_G)
+ attr |= FOREGROUND_GREEN;
+ if (color & PJ_TERM_COLOR_B)
+ attr |= FOREGROUND_BLUE;
+ if (color & PJ_TERM_COLOR_BRIGHT)
+ attr |= FOREGROUND_INTENSITY;
+
+ return attr;
+}
+
+static pj_color_t os_attr_to_pj_color(WORD attr)
+{
+ int color = 0;
+
+ if (attr & FOREGROUND_RED)
+ color |= PJ_TERM_COLOR_R;
+ if (attr & FOREGROUND_GREEN)
+ color |= PJ_TERM_COLOR_G;
+ if (attr & FOREGROUND_BLUE)
+ color |= PJ_TERM_COLOR_B;
+ if (attr & FOREGROUND_INTENSITY)
+ color |= PJ_TERM_COLOR_BRIGHT;
+
+ return color;
+}
+
+
+/*
+ * pj_term_set_color()
+ */
+PJ_DEF(pj_status_t) pj_term_set_color(pj_color_t color)
+{
+ BOOL rc;
+ WORD attr = 0;
+
+ PJ_CHECK_STACK();
+
+ attr = pj_color_to_os_attr(color);
+ rc = SetConsoleTextAttribute( GetStdHandle(STD_OUTPUT_HANDLE), attr);
+ return rc ? PJ_SUCCESS : PJ_RETURN_OS_ERROR(GetLastError());
+}
+
+/*
+ * pj_term_get_color()
+ * Get current terminal foreground color.
+ */
+PJ_DEF(pj_color_t) pj_term_get_color(void)
+{
+ CONSOLE_SCREEN_BUFFER_INFO info;
+
+ PJ_CHECK_STACK();
+
+ GetConsoleScreenBufferInfo( GetStdHandle(STD_OUTPUT_HANDLE), &info);
+ return os_attr_to_pj_color(info.wAttributes);
+}
+
+#endif /* PJ_TERM_HAS_COLOR */
diff --git a/pjlib/src/pj/os_error_linux_kernel.c b/pjlib/src/pj/os_error_linux_kernel.c
index cd5dfe7..a6ecb1a 100644
--- a/pjlib/src/pj/os_error_linux_kernel.c
+++ b/pjlib/src/pj/os_error_linux_kernel.c
@@ -1,65 +1,86 @@
-/* $Id$
- *
- */
-#include <pj/string.h>
-#include <pj/compat/errno.h>
-#include <linux/config.h>
-#include <linux/version.h>
-#if defined(MODVERSIONS)
-#include <linux/modversions.h>
-#endif
-#include <linux/kernel.h>
-#include <linux/errno.h>
-
-int kernel_errno;
-
-PJ_DEF(pj_status_t) pj_get_os_error(void)
-{
- return errno;
-}
-
-PJ_DEF(void) pj_set_os_error(pj_status_t code)
-{
- errno = code;
-}
-
-PJ_DEF(pj_status_t) pj_get_netos_error(void)
-{
- return errno;
-}
-
-PJ_DEF(void) pj_set_netos_error(pj_status_t code)
-{
- errno = code;
-}
-
-/*
- * platform_strerror()
- *
- * Platform specific error message. This file is called by pj_strerror()
- * in errno.c
- */
-int platform_strerror( pj_os_err_type os_errcode,
- char *buf, pj_size_t bufsize)
-{
- char errmsg[32];
- int len;
-
- /* Handle EINVAL as special case so that it'll pass errno test. */
- if (os_errcode==EINVAL)
- strcpy(errmsg, "Invalid value");
- else
- sprintf(errmsg, "errno=%d", os_errcode);
-
- len = strlen(errmsg);
-
- if (len >= bufsize)
- len = bufsize-1;
-
- pj_memcpy(buf, errmsg, len);
- buf[len] = '\0';
-
- return len;
-}
-
-
+/* $Id$
+ *
+ */
+/*
+ * PJLIB - PJ Foundation Library
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ * Benny Prijono <bennylp@bulukucing.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; 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/compat/errno.h>
+#include <linux/config.h>
+#include <linux/version.h>
+#if defined(MODVERSIONS)
+#include <linux/modversions.h>
+#endif
+#include <linux/kernel.h>
+#include <linux/errno.h>
+
+int kernel_errno;
+
+PJ_DEF(pj_status_t) pj_get_os_error(void)
+{
+ return errno;
+}
+
+PJ_DEF(void) pj_set_os_error(pj_status_t code)
+{
+ errno = code;
+}
+
+PJ_DEF(pj_status_t) pj_get_netos_error(void)
+{
+ return errno;
+}
+
+PJ_DEF(void) pj_set_netos_error(pj_status_t code)
+{
+ errno = code;
+}
+
+/*
+ * platform_strerror()
+ *
+ * Platform specific error message. This file is called by pj_strerror()
+ * in errno.c
+ */
+int platform_strerror( pj_os_err_type os_errcode,
+ char *buf, pj_size_t bufsize)
+{
+ char errmsg[32];
+ int len;
+
+ /* Handle EINVAL as special case so that it'll pass errno test. */
+ if (os_errcode==EINVAL)
+ strcpy(errmsg, "Invalid value");
+ else
+ sprintf(errmsg, "errno=%d", os_errcode);
+
+ len = strlen(errmsg);
+
+ if (len >= bufsize)
+ len = bufsize-1;
+
+ pj_memcpy(buf, errmsg, len);
+ buf[len] = '\0';
+
+ return len;
+}
+
+
diff --git a/pjlib/src/pj/os_error_unix.c b/pjlib/src/pj/os_error_unix.c
index acb1f80..fa902b6 100644
--- a/pjlib/src/pj/os_error_unix.c
+++ b/pjlib/src/pj/os_error_unix.c
@@ -1,54 +1,75 @@
-/* $Id$
- *
- */
-/*
- * $Log: /pjproject-0.3/pjlib/src/pj/os_error_unix.c $
- *
- * 1 10/14/05 12:19a Bennylp
- * Created.
- *
- */
-#include <pj/errno.h>
-#include <pj/string.h>
-#include <errno.h>
-
-PJ_DEF(pj_status_t) pj_get_os_error(void)
-{
- return PJ_STATUS_FROM_OS(errno);
-}
-
-PJ_DEF(void) pj_set_os_error(pj_status_t code)
-{
- errno = PJ_STATUS_TO_OS(code);
-}
-
-PJ_DEF(pj_status_t) pj_get_netos_error(void)
-{
- return PJ_STATUS_FROM_OS(errno);
-}
-
-PJ_DEF(void) pj_set_netos_error(pj_status_t code)
-{
- errno = PJ_STATUS_TO_OS(code);
-}
-
-/*
- * platform_strerror()
- *
- * Platform specific error message. This file is called by pj_strerror()
- * in errno.c
- */
-int platform_strerror( pj_os_err_type os_errcode,
- char *buf, pj_size_t bufsize)
-{
- const char *syserr = strerror(os_errcode);
- pj_size_t len = syserr ? strlen(syserr) : 0;
-
- if (len >= bufsize) len = bufsize - 1;
- if (len > 0)
- pj_memcpy(buf, syserr, len);
- buf[len] = '\0';
- return len;
-}
-
-
+/* $Id$
+ *
+ */
+/*
+ * PJLIB - PJ Foundation Library
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ * Benny Prijono <bennylp@bulukucing.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+/*
+ * $Log: /pjproject-0.3/pjlib/src/pj/os_error_unix.c $
+ *
+ * 1 10/14/05 12:19a Bennylp
+ * Created.
+ *
+ */
+#include <pj/errno.h>
+#include <pj/string.h>
+#include <errno.h>
+
+PJ_DEF(pj_status_t) pj_get_os_error(void)
+{
+ return PJ_STATUS_FROM_OS(errno);
+}
+
+PJ_DEF(void) pj_set_os_error(pj_status_t code)
+{
+ errno = PJ_STATUS_TO_OS(code);
+}
+
+PJ_DEF(pj_status_t) pj_get_netos_error(void)
+{
+ return PJ_STATUS_FROM_OS(errno);
+}
+
+PJ_DEF(void) pj_set_netos_error(pj_status_t code)
+{
+ errno = PJ_STATUS_TO_OS(code);
+}
+
+/*
+ * platform_strerror()
+ *
+ * Platform specific error message. This file is called by pj_strerror()
+ * in errno.c
+ */
+int platform_strerror( pj_os_err_type os_errcode,
+ char *buf, pj_size_t bufsize)
+{
+ const char *syserr = strerror(os_errcode);
+ pj_size_t len = syserr ? strlen(syserr) : 0;
+
+ if (len >= bufsize) len = bufsize - 1;
+ if (len > 0)
+ pj_memcpy(buf, syserr, len);
+ buf[len] = '\0';
+ return len;
+}
+
+
diff --git a/pjlib/src/pj/os_error_win32.c b/pjlib/src/pj/os_error_win32.c
index 23da7cf..8274467 100644
--- a/pjlib/src/pj/os_error_win32.c
+++ b/pjlib/src/pj/os_error_win32.c
@@ -1,149 +1,170 @@
-/* $Id$
- */
-#include <pj/errno.h>
-#include <pj/assert.h>
-#include <pj/compat/stdarg.h>
-#include <pj/compat/sprintf.h>
-#include <pj/compat/vsprintf.h>
-#include <pj/string.h>
-
-
-#if defined(PJ_HAS_WINSOCK2_H) && PJ_HAS_WINSOCK2_H != 0
-# include <winsock2.h>
-#elif defined(PJ_HAS_WINSOCK_H) && PJ_HAS_WINSOCK_H != 0
-# include <winsock.h>
-#endif
-
-
-/*
- * From Apache's APR:
- */
-static const struct {
- pj_os_err_type code;
- const char *msg;
-} gaErrorList[] = {
- {WSAEINTR, "Interrupted system call"},
- {WSAEBADF, "Bad file number"},
- {WSAEACCES, "Permission denied"},
- {WSAEFAULT, "Bad address"},
- {WSAEINVAL, "Invalid argument"},
- {WSAEMFILE, "Too many open sockets"},
- {WSAEWOULDBLOCK, "Operation would block"},
- {WSAEINPROGRESS, "Operation now in progress"},
- {WSAEALREADY, "Operation already in progress"},
- {WSAENOTSOCK, "Socket operation on non-socket"},
- {WSAEDESTADDRREQ, "Destination address required"},
- {WSAEMSGSIZE, "Message too long"},
- {WSAEPROTOTYPE, "Protocol wrong type for socket"},
- {WSAENOPROTOOPT, "Bad protocol option"},
- {WSAEPROTONOSUPPORT, "Protocol not supported"},
- {WSAESOCKTNOSUPPORT, "Socket type not supported"},
- {WSAEOPNOTSUPP, "Operation not supported on socket"},
- {WSAEPFNOSUPPORT, "Protocol family not supported"},
- {WSAEAFNOSUPPORT, "Address family not supported"},
- {WSAEADDRINUSE, "Address already in use"},
- {WSAEADDRNOTAVAIL, "Can't assign requested address"},
- {WSAENETDOWN, "Network is down"},
- {WSAENETUNREACH, "Network is unreachable"},
- {WSAENETRESET, "Net connection reset"},
- {WSAECONNABORTED, "Software caused connection abort"},
- {WSAECONNRESET, "Connection reset by peer"},
- {WSAENOBUFS, "No buffer space available"},
- {WSAEISCONN, "Socket is already connected"},
- {WSAENOTCONN, "Socket is not connected"},
- {WSAESHUTDOWN, "Can't send after socket shutdown"},
- {WSAETOOMANYREFS, "Too many references, can't splice"},
- {WSAETIMEDOUT, "Connection timed out"},
- {WSAECONNREFUSED, "Connection refused"},
- {WSAELOOP, "Too many levels of symbolic links"},
- {WSAENAMETOOLONG, "File name too long"},
- {WSAEHOSTDOWN, "Host is down"},
- {WSAEHOSTUNREACH, "No route to host"},
- {WSAENOTEMPTY, "Directory not empty"},
- {WSAEPROCLIM, "Too many processes"},
- {WSAEUSERS, "Too many users"},
- {WSAEDQUOT, "Disc quota exceeded"},
- {WSAESTALE, "Stale NFS file handle"},
- {WSAEREMOTE, "Too many levels of remote in path"},
- {WSASYSNOTREADY, "Network system is unavailable"},
- {WSAVERNOTSUPPORTED, "Winsock version out of range"},
- {WSANOTINITIALISED, "WSAStartup not yet called"},
- {WSAEDISCON, "Graceful shutdown in progress"},
- {WSAHOST_NOT_FOUND, "Host not found"},
- {WSANO_DATA, "No host data of that type was found"},
- {0, NULL}
-};
-
-
-PJ_DEF(pj_status_t) pj_get_os_error(void)
-{
- return PJ_STATUS_FROM_OS(GetLastError());
-}
-
-PJ_DEF(void) pj_set_os_error(pj_status_t code)
-{
- SetLastError(PJ_STATUS_TO_OS(code));
-}
-
-PJ_DEF(pj_status_t) pj_get_netos_error(void)
-{
- return PJ_STATUS_FROM_OS(WSAGetLastError());
-}
-
-PJ_DEF(void) pj_set_netos_error(pj_status_t code)
-{
- WSASetLastError(PJ_STATUS_TO_OS(code));
-}
-
-/*
- * platform_strerror()
- *
- * Platform specific error message. This file is called by pj_strerror()
- * in errno.c
- */
-int platform_strerror( pj_os_err_type os_errcode,
- char *buf, pj_size_t bufsize)
-{
- int len;
-
- pj_assert(buf != NULL);
- pj_assert(bufsize >= 0);
-
- /*
- * MUST NOT check stack here.
- * This function might be called from PJ_CHECK_STACK() itself!
- //PJ_CHECK_STACK();
- */
-
- len = FormatMessage( FORMAT_MESSAGE_FROM_SYSTEM
- | FORMAT_MESSAGE_IGNORE_INSERTS,
- NULL,
- os_errcode,
- MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
- (LPTSTR)buf,
- (DWORD)bufsize,
- NULL);
-
- if (!len) {
- int i;
- for (i = 0; gaErrorList[i].msg; ++i) {
- if (gaErrorList[i].code == os_errcode) {
- len = strlen(gaErrorList[i].msg);
- if ((pj_size_t)len >= bufsize) {
- len = bufsize-1;
- }
- pj_memcpy(buf, gaErrorList[i].msg, len);
- buf[len] = '\0';
- break;
- }
- }
- }
-
- if (!len) {
- len = snprintf( buf, bufsize, "Unknown native error %u", (unsigned)os_errcode);
- buf[len] = '\0';
- }
-
- return len;
-}
-
+/* $Id$
+ */
+/*
+ * PJLIB - PJ Foundation Library
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ * Benny Prijono <bennylp@bulukucing.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#include <pj/errno.h>
+#include <pj/assert.h>
+#include <pj/compat/stdarg.h>
+#include <pj/compat/sprintf.h>
+#include <pj/compat/vsprintf.h>
+#include <pj/string.h>
+
+
+#if defined(PJ_HAS_WINSOCK2_H) && PJ_HAS_WINSOCK2_H != 0
+# include <winsock2.h>
+#elif defined(PJ_HAS_WINSOCK_H) && PJ_HAS_WINSOCK_H != 0
+# include <winsock.h>
+#endif
+
+
+/*
+ * From Apache's APR:
+ */
+static const struct {
+ pj_os_err_type code;
+ const char *msg;
+} gaErrorList[] = {
+ {WSAEINTR, "Interrupted system call"},
+ {WSAEBADF, "Bad file number"},
+ {WSAEACCES, "Permission denied"},
+ {WSAEFAULT, "Bad address"},
+ {WSAEINVAL, "Invalid argument"},
+ {WSAEMFILE, "Too many open sockets"},
+ {WSAEWOULDBLOCK, "Operation would block"},
+ {WSAEINPROGRESS, "Operation now in progress"},
+ {WSAEALREADY, "Operation already in progress"},
+ {WSAENOTSOCK, "Socket operation on non-socket"},
+ {WSAEDESTADDRREQ, "Destination address required"},
+ {WSAEMSGSIZE, "Message too long"},
+ {WSAEPROTOTYPE, "Protocol wrong type for socket"},
+ {WSAENOPROTOOPT, "Bad protocol option"},
+ {WSAEPROTONOSUPPORT, "Protocol not supported"},
+ {WSAESOCKTNOSUPPORT, "Socket type not supported"},
+ {WSAEOPNOTSUPP, "Operation not supported on socket"},
+ {WSAEPFNOSUPPORT, "Protocol family not supported"},
+ {WSAEAFNOSUPPORT, "Address family not supported"},
+ {WSAEADDRINUSE, "Address already in use"},
+ {WSAEADDRNOTAVAIL, "Can't assign requested address"},
+ {WSAENETDOWN, "Network is down"},
+ {WSAENETUNREACH, "Network is unreachable"},
+ {WSAENETRESET, "Net connection reset"},
+ {WSAECONNABORTED, "Software caused connection abort"},
+ {WSAECONNRESET, "Connection reset by peer"},
+ {WSAENOBUFS, "No buffer space available"},
+ {WSAEISCONN, "Socket is already connected"},
+ {WSAENOTCONN, "Socket is not connected"},
+ {WSAESHUTDOWN, "Can't send after socket shutdown"},
+ {WSAETOOMANYREFS, "Too many references, can't splice"},
+ {WSAETIMEDOUT, "Connection timed out"},
+ {WSAECONNREFUSED, "Connection refused"},
+ {WSAELOOP, "Too many levels of symbolic links"},
+ {WSAENAMETOOLONG, "File name too long"},
+ {WSAEHOSTDOWN, "Host is down"},
+ {WSAEHOSTUNREACH, "No route to host"},
+ {WSAENOTEMPTY, "Directory not empty"},
+ {WSAEPROCLIM, "Too many processes"},
+ {WSAEUSERS, "Too many users"},
+ {WSAEDQUOT, "Disc quota exceeded"},
+ {WSAESTALE, "Stale NFS file handle"},
+ {WSAEREMOTE, "Too many levels of remote in path"},
+ {WSASYSNOTREADY, "Network system is unavailable"},
+ {WSAVERNOTSUPPORTED, "Winsock version out of range"},
+ {WSANOTINITIALISED, "WSAStartup not yet called"},
+ {WSAEDISCON, "Graceful shutdown in progress"},
+ {WSAHOST_NOT_FOUND, "Host not found"},
+ {WSANO_DATA, "No host data of that type was found"},
+ {0, NULL}
+};
+
+
+PJ_DEF(pj_status_t) pj_get_os_error(void)
+{
+ return PJ_STATUS_FROM_OS(GetLastError());
+}
+
+PJ_DEF(void) pj_set_os_error(pj_status_t code)
+{
+ SetLastError(PJ_STATUS_TO_OS(code));
+}
+
+PJ_DEF(pj_status_t) pj_get_netos_error(void)
+{
+ return PJ_STATUS_FROM_OS(WSAGetLastError());
+}
+
+PJ_DEF(void) pj_set_netos_error(pj_status_t code)
+{
+ WSASetLastError(PJ_STATUS_TO_OS(code));
+}
+
+/*
+ * platform_strerror()
+ *
+ * Platform specific error message. This file is called by pj_strerror()
+ * in errno.c
+ */
+int platform_strerror( pj_os_err_type os_errcode,
+ char *buf, pj_size_t bufsize)
+{
+ int len;
+
+ pj_assert(buf != NULL);
+ pj_assert(bufsize >= 0);
+
+ /*
+ * MUST NOT check stack here.
+ * This function might be called from PJ_CHECK_STACK() itself!
+ //PJ_CHECK_STACK();
+ */
+
+ len = FormatMessage( FORMAT_MESSAGE_FROM_SYSTEM
+ | FORMAT_MESSAGE_IGNORE_INSERTS,
+ NULL,
+ os_errcode,
+ MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
+ (LPTSTR)buf,
+ (DWORD)bufsize,
+ NULL);
+
+ if (!len) {
+ int i;
+ for (i = 0; gaErrorList[i].msg; ++i) {
+ if (gaErrorList[i].code == os_errcode) {
+ len = strlen(gaErrorList[i].msg);
+ if ((pj_size_t)len >= bufsize) {
+ len = bufsize-1;
+ }
+ pj_memcpy(buf, gaErrorList[i].msg, len);
+ buf[len] = '\0';
+ break;
+ }
+ }
+ }
+
+ if (!len) {
+ len = snprintf( buf, bufsize, "Unknown native error %u", (unsigned)os_errcode);
+ buf[len] = '\0';
+ }
+
+ return len;
+}
+
diff --git a/pjlib/src/pj/os_time_ansi.c b/pjlib/src/pj/os_time_ansi.c
index 20fc7f6..f4fd486 100644
--- a/pjlib/src/pj/os_time_ansi.c
+++ b/pjlib/src/pj/os_time_ansi.c
@@ -1,56 +1,77 @@
-/* $Id$
- */
-#include <pj/os.h>
-#include <pj/compat/time.h>
-
-///////////////////////////////////////////////////////////////////////////////
-
-PJ_DEF(pj_status_t) pj_gettimeofday(pj_time_val *tv)
-{
- struct timeb tb;
-
- PJ_CHECK_STACK();
-
- ftime(&tb);
- tv->sec = tb.time;
- tv->msec = tb.millitm;
- return PJ_SUCCESS;
-}
-
-PJ_DEF(pj_status_t) pj_time_decode(const pj_time_val *tv, pj_parsed_time *pt)
-{
- struct tm *local_time;
-
- PJ_CHECK_STACK();
-
- local_time = localtime((time_t*)&tv->sec);
-
- pt->year = local_time->tm_year+1900;
- pt->mon = local_time->tm_mon;
- pt->day = local_time->tm_mday;
- pt->hour = local_time->tm_hour;
- pt->min = local_time->tm_min;
- pt->sec = local_time->tm_sec;
- pt->wday = local_time->tm_wday;
- pt->yday = local_time->tm_yday;
- pt->msec = tv->msec;
-
- return PJ_SUCCESS;
-}
-
-/**
- * Encode parsed time to time value.
- */
-PJ_DEF(pj_status_t) pj_time_encode(const pj_parsed_time *pt, pj_time_val *tv);
-
-/**
- * Convert local time to GMT.
- */
-PJ_DEF(pj_status_t) pj_time_local_to_gmt(pj_time_val *tv);
-
-/**
- * Convert GMT to local time.
- */
-PJ_DEF(pj_status_t) pj_time_gmt_to_local(pj_time_val *tv);
-
-
+/* $Id$
+ */
+/*
+ * PJLIB - PJ Foundation Library
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ * Benny Prijono <bennylp@bulukucing.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; 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/compat/time.h>
+
+///////////////////////////////////////////////////////////////////////////////
+
+PJ_DEF(pj_status_t) pj_gettimeofday(pj_time_val *tv)
+{
+ struct timeb tb;
+
+ PJ_CHECK_STACK();
+
+ ftime(&tb);
+ tv->sec = tb.time;
+ tv->msec = tb.millitm;
+ return PJ_SUCCESS;
+}
+
+PJ_DEF(pj_status_t) pj_time_decode(const pj_time_val *tv, pj_parsed_time *pt)
+{
+ struct tm *local_time;
+
+ PJ_CHECK_STACK();
+
+ local_time = localtime((time_t*)&tv->sec);
+
+ pt->year = local_time->tm_year+1900;
+ pt->mon = local_time->tm_mon;
+ pt->day = local_time->tm_mday;
+ pt->hour = local_time->tm_hour;
+ pt->min = local_time->tm_min;
+ pt->sec = local_time->tm_sec;
+ pt->wday = local_time->tm_wday;
+ pt->yday = local_time->tm_yday;
+ pt->msec = tv->msec;
+
+ return PJ_SUCCESS;
+}
+
+/**
+ * Encode parsed time to time value.
+ */
+PJ_DEF(pj_status_t) pj_time_encode(const pj_parsed_time *pt, pj_time_val *tv);
+
+/**
+ * Convert local time to GMT.
+ */
+PJ_DEF(pj_status_t) pj_time_local_to_gmt(pj_time_val *tv);
+
+/**
+ * Convert GMT to local time.
+ */
+PJ_DEF(pj_status_t) pj_time_gmt_to_local(pj_time_val *tv);
+
+
diff --git a/pjlib/src/pj/os_time_linux_kernel.c b/pjlib/src/pj/os_time_linux_kernel.c
index 03c0cc2..d9d5ed8 100644
--- a/pjlib/src/pj/os_time_linux_kernel.c
+++ b/pjlib/src/pj/os_time_linux_kernel.c
@@ -1,60 +1,81 @@
-/* $Id$
- *
- */
-/* $Log: /pjproject-0.3/pjlib/src/pj/os_time_linux_kernel.c $
- *
- * 2 10/14/05 12:26a Bennylp
- * Finished error code framework, some fixes in ioqueue, etc. Pretty
- * major.
- *
- * 1 9/22/05 10:39a Bennylp
- * Created.
- *
- */
-#include <pj/os.h>
-#include <linux/time.h>
-
-///////////////////////////////////////////////////////////////////////////////
-
-PJ_DEF(pj_status_t) pj_gettimeofday(pj_time_val *tv)
-{
- struct timeval tval;
-
- do_gettimeofday(&tval);
- tv->sec = tval.tv_sec;
- tv->msec = tval.tv_usec / 1000;
-
- return 0;
-}
-
-PJ_DEF(pj_status_t) pj_time_decode(const pj_time_val *tv, pj_parsed_time *pt)
-{
- pt->year = 2005;
- pt->mon = 8;
- pt->day = 20;
- pt->hour = 16;
- pt->min = 30;
- pt->sec = 30;
- pt->wday = 3;
- pt->yday = 200;
- pt->msec = 777;
-
- return -1;
-}
-
-/**
- * Encode parsed time to time value.
- */
-PJ_DEF(pj_status_t) pj_time_encode(const pj_parsed_time *pt, pj_time_val *tv);
-
-/**
- * Convert local time to GMT.
- */
-PJ_DEF(pj_status_t) pj_time_local_to_gmt(pj_time_val *tv);
-
-/**
- * Convert GMT to local time.
- */
-PJ_DEF(pj_status_t) pj_time_gmt_to_local(pj_time_val *tv);
-
-
+/* $Id$
+ *
+ */
+/*
+ * PJLIB - PJ Foundation Library
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ * Benny Prijono <bennylp@bulukucing.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+/* $Log: /pjproject-0.3/pjlib/src/pj/os_time_linux_kernel.c $
+ *
+ * 2 10/14/05 12:26a Bennylp
+ * Finished error code framework, some fixes in ioqueue, etc. Pretty
+ * major.
+ *
+ * 1 9/22/05 10:39a Bennylp
+ * Created.
+ *
+ */
+#include <pj/os.h>
+#include <linux/time.h>
+
+///////////////////////////////////////////////////////////////////////////////
+
+PJ_DEF(pj_status_t) pj_gettimeofday(pj_time_val *tv)
+{
+ struct timeval tval;
+
+ do_gettimeofday(&tval);
+ tv->sec = tval.tv_sec;
+ tv->msec = tval.tv_usec / 1000;
+
+ return 0;
+}
+
+PJ_DEF(pj_status_t) pj_time_decode(const pj_time_val *tv, pj_parsed_time *pt)
+{
+ pt->year = 2005;
+ pt->mon = 8;
+ pt->day = 20;
+ pt->hour = 16;
+ pt->min = 30;
+ pt->sec = 30;
+ pt->wday = 3;
+ pt->yday = 200;
+ pt->msec = 777;
+
+ return -1;
+}
+
+/**
+ * Encode parsed time to time value.
+ */
+PJ_DEF(pj_status_t) pj_time_encode(const pj_parsed_time *pt, pj_time_val *tv);
+
+/**
+ * Convert local time to GMT.
+ */
+PJ_DEF(pj_status_t) pj_time_local_to_gmt(pj_time_val *tv);
+
+/**
+ * Convert GMT to local time.
+ */
+PJ_DEF(pj_status_t) pj_time_gmt_to_local(pj_time_val *tv);
+
+
diff --git a/pjlib/src/pj/os_timestamp_common.c b/pjlib/src/pj/os_timestamp_common.c
index f9824d5..ddba4cf 100644
--- a/pjlib/src/pj/os_timestamp_common.c
+++ b/pjlib/src/pj/os_timestamp_common.c
@@ -1,119 +1,140 @@
-/* $Id$
- */
-#include <pj/os.h>
-#include <pj/compat/high_precision.h>
-
-#if defined(PJ_HAS_HIGH_RES_TIMER) && PJ_HAS_HIGH_RES_TIMER != 0
-
-#define U32MAX (0xFFFFFFFFUL)
-#define NANOSEC (1000000000UL)
-#define USEC (1000000UL)
-#define MSEC (1000)
-
-static pj_highprec_t get_elapsed( const pj_timestamp *start,
- const pj_timestamp *stop )
-{
- pj_highprec_t elapsed_hi, elapsed_lo;
-
- elapsed_hi = stop->u32.hi - start->u32.hi;
- elapsed_lo = stop->u32.lo - start->u32.lo;
-
- /* elapsed_hi = elapsed_hi * U32MAX */
- pj_highprec_mul(elapsed_hi, U32MAX);
-
- return elapsed_hi + elapsed_lo;
-}
-
-static pj_highprec_t elapsed_usec( const pj_timestamp *start,
- const pj_timestamp *stop )
-{
- pj_timestamp ts_freq;
- pj_highprec_t freq, elapsed;
-
- if (pj_get_timestamp_freq(&ts_freq) != PJ_SUCCESS)
- return 0;
-
- /* Convert frequency timestamp */
- freq = ts_freq.u32.hi;
- pj_highprec_mul(freq, U32MAX);
- freq += ts_freq.u32.lo;
-
- /* Avoid division by zero. */
- if (freq == 0) freq = 1;
-
- /* Get elapsed time in cycles. */
- elapsed = get_elapsed(start, stop);
-
- /* usec = elapsed * USEC / freq */
- pj_highprec_mul(elapsed, USEC);
- pj_highprec_div(elapsed, freq);
-
- return elapsed;
-}
-
-PJ_DEF(pj_uint32_t) pj_elapsed_nanosec( const pj_timestamp *start,
- const pj_timestamp *stop )
-{
- pj_timestamp ts_freq;
- pj_highprec_t freq, elapsed;
-
- if (pj_get_timestamp_freq(&ts_freq) != PJ_SUCCESS)
- return 0;
-
- /* Convert frequency timestamp */
- freq = ts_freq.u32.hi;
- pj_highprec_mul(freq, U32MAX);
- freq += ts_freq.u32.lo;
-
- /* Avoid division by zero. */
- if (freq == 0) freq = 1;
-
- /* Get elapsed time in cycles. */
- elapsed = get_elapsed(start, stop);
-
- /* usec = elapsed * USEC / freq */
- pj_highprec_mul(elapsed, NANOSEC);
- pj_highprec_div(elapsed, freq);
-
- return (pj_uint32_t)elapsed;
-}
-
-PJ_DEF(pj_uint32_t) pj_elapsed_usec( const pj_timestamp *start,
- const pj_timestamp *stop )
-{
- return (pj_uint32_t)elapsed_usec(start, stop);
-}
-
-PJ_DEF(pj_time_val) pj_elapsed_time( const pj_timestamp *start,
- const pj_timestamp *stop )
-{
- pj_highprec_t elapsed = elapsed_usec(start, stop);
- pj_time_val tv_elapsed;
-
- if (PJ_HIGHPREC_VALUE_IS_ZERO(elapsed)) {
- tv_elapsed.sec = tv_elapsed.msec = 0;
- return tv_elapsed;
- } else {
- pj_highprec_t sec, msec;
-
- sec = elapsed;
- pj_highprec_div(sec, USEC);
- tv_elapsed.sec = (long)sec;
-
- msec = elapsed;
- pj_highprec_mod(msec, USEC);
- pj_highprec_div(msec, 1000);
- tv_elapsed.msec = (long)msec;
-
- return tv_elapsed;
- }
-}
-
-PJ_DEF(pj_uint32_t) pj_elapsed_cycle( const pj_timestamp *start,
- const pj_timestamp *stop )
-{
- return stop->u32.lo - start->u32.lo;
-}
-
-#endif /* PJ_HAS_HIGH_RES_TIMER */
-
+/* $Id$
+ */
+/*
+ * PJLIB - PJ Foundation Library
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ * Benny Prijono <bennylp@bulukucing.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; 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/compat/high_precision.h>
+
+#if defined(PJ_HAS_HIGH_RES_TIMER) && PJ_HAS_HIGH_RES_TIMER != 0
+
+#define U32MAX (0xFFFFFFFFUL)
+#define NANOSEC (1000000000UL)
+#define USEC (1000000UL)
+#define MSEC (1000)
+
+static pj_highprec_t get_elapsed( const pj_timestamp *start,
+ const pj_timestamp *stop )
+{
+ pj_highprec_t elapsed_hi, elapsed_lo;
+
+ elapsed_hi = stop->u32.hi - start->u32.hi;
+ elapsed_lo = stop->u32.lo - start->u32.lo;
+
+ /* elapsed_hi = elapsed_hi * U32MAX */
+ pj_highprec_mul(elapsed_hi, U32MAX);
+
+ return elapsed_hi + elapsed_lo;
+}
+
+static pj_highprec_t elapsed_usec( const pj_timestamp *start,
+ const pj_timestamp *stop )
+{
+ pj_timestamp ts_freq;
+ pj_highprec_t freq, elapsed;
+
+ if (pj_get_timestamp_freq(&ts_freq) != PJ_SUCCESS)
+ return 0;
+
+ /* Convert frequency timestamp */
+ freq = ts_freq.u32.hi;
+ pj_highprec_mul(freq, U32MAX);
+ freq += ts_freq.u32.lo;
+
+ /* Avoid division by zero. */
+ if (freq == 0) freq = 1;
+
+ /* Get elapsed time in cycles. */
+ elapsed = get_elapsed(start, stop);
+
+ /* usec = elapsed * USEC / freq */
+ pj_highprec_mul(elapsed, USEC);
+ pj_highprec_div(elapsed, freq);
+
+ return elapsed;
+}
+
+PJ_DEF(pj_uint32_t) pj_elapsed_nanosec( const pj_timestamp *start,
+ const pj_timestamp *stop )
+{
+ pj_timestamp ts_freq;
+ pj_highprec_t freq, elapsed;
+
+ if (pj_get_timestamp_freq(&ts_freq) != PJ_SUCCESS)
+ return 0;
+
+ /* Convert frequency timestamp */
+ freq = ts_freq.u32.hi;
+ pj_highprec_mul(freq, U32MAX);
+ freq += ts_freq.u32.lo;
+
+ /* Avoid division by zero. */
+ if (freq == 0) freq = 1;
+
+ /* Get elapsed time in cycles. */
+ elapsed = get_elapsed(start, stop);
+
+ /* usec = elapsed * USEC / freq */
+ pj_highprec_mul(elapsed, NANOSEC);
+ pj_highprec_div(elapsed, freq);
+
+ return (pj_uint32_t)elapsed;
+}
+
+PJ_DEF(pj_uint32_t) pj_elapsed_usec( const pj_timestamp *start,
+ const pj_timestamp *stop )
+{
+ return (pj_uint32_t)elapsed_usec(start, stop);
+}
+
+PJ_DEF(pj_time_val) pj_elapsed_time( const pj_timestamp *start,
+ const pj_timestamp *stop )
+{
+ pj_highprec_t elapsed = elapsed_usec(start, stop);
+ pj_time_val tv_elapsed;
+
+ if (PJ_HIGHPREC_VALUE_IS_ZERO(elapsed)) {
+ tv_elapsed.sec = tv_elapsed.msec = 0;
+ return tv_elapsed;
+ } else {
+ pj_highprec_t sec, msec;
+
+ sec = elapsed;
+ pj_highprec_div(sec, USEC);
+ tv_elapsed.sec = (long)sec;
+
+ msec = elapsed;
+ pj_highprec_mod(msec, USEC);
+ pj_highprec_div(msec, 1000);
+ tv_elapsed.msec = (long)msec;
+
+ return tv_elapsed;
+ }
+}
+
+PJ_DEF(pj_uint32_t) pj_elapsed_cycle( const pj_timestamp *start,
+ const pj_timestamp *stop )
+{
+ return stop->u32.lo - start->u32.lo;
+}
+
+#endif /* PJ_HAS_HIGH_RES_TIMER */
+
diff --git a/pjlib/src/pj/os_timestamp_linux.c b/pjlib/src/pj/os_timestamp_linux.c
index ada9076..15921f9 100644
--- a/pjlib/src/pj/os_timestamp_linux.c
+++ b/pjlib/src/pj/os_timestamp_linux.c
@@ -1,124 +1,145 @@
-/* $Id$
- *
- */
-#include <pj/os.h>
-#include <pj/errno.h>
-#include <stdio.h>
-#include <string.h>
-#include <stdlib.h>
-#include <ctype.h>
-
-#if defined(PJ_HAS_PENTIUM) && PJ_HAS_PENTIUM!=0
-static int machine_speed_mhz;
-static pj_timestamp machine_speed;
-
-static __inline__ unsigned long long int rdtsc()
-{
- unsigned long long int x;
- __asm__ volatile (".byte 0x0f, 0x31" : "=A" (x));
- return x;
-}
-
-/* Determine machine's CPU MHz to get the counter's frequency.
- */
-static int get_machine_speed_mhz()
-{
- FILE *strm;
- char buf[512];
- int len;
- char *pos, *end;
-
- PJ_CHECK_STACK();
-
- /* Open /proc/cpuinfo and read the file */
- strm = fopen("/proc/cpuinfo", "r");
- if (!strm)
- return -1;
- len = fread(buf, 1, sizeof(buf), strm);
- fclose(strm);
- if (len < 1) {
- return -1;
- }
- buf[len] = '\0';
-
- /* Locate the MHz digit. */
- pos = strstr(buf, "cpu MHz");
- if (!pos)
- return -1;
- pos = strchr(pos, ':');
- if (!pos)
- return -1;
- end = (pos += 2);
- while (isdigit(*end)) ++end;
- *end = '\0';
-
- /* Return the Mhz part, and give it a +1. */
- return atoi(pos)+1;
-}
-
-PJ_DEF(pj_status_t) pj_get_timestamp(pj_timestamp *ts)
-{
- if (machine_speed_mhz == 0) {
- machine_speed_mhz = get_machine_speed_mhz();
- if (machine_speed_mhz > 0) {
- machine_speed.u64 = machine_speed_mhz * 1000000.0;
- }
- }
-
- if (machine_speed_mhz == -1) {
- ts->u64 = 0;
- return -1;
- }
- ts->u64 = rdtsc();
- return 0;
-}
-
-PJ_DEF(pj_status_t) pj_get_timestamp_freq(pj_timestamp *freq)
-{
- if (machine_speed_mhz == 0) {
- machine_speed_mhz = get_machine_speed_mhz();
- if (machine_speed_mhz > 0) {
- machine_speed.u64 = machine_speed_mhz * 1000000.0;
- }
- }
-
- if (machine_speed_mhz == -1) {
- freq->u64 = 1; /* return 1 to prevent division by zero in apps. */
- return -1;
- }
-
- freq->u64 = machine_speed.u64;
- return 0;
-}
-
-#else
-#include <sys/time.h>
-#include <errno.h>
-
-#define USEC_PER_SEC 1000000
-
-PJ_DEF(pj_status_t) pj_get_timestamp(pj_timestamp *ts)
-{
- struct timeval tv;
-
- if (gettimeofday(&tv, NULL) != 0) {
- return PJ_RETURN_OS_ERROR(pj_get_native_os_error());
- }
-
- ts->u64 = tv.tv_sec;
- ts->u64 *= USEC_PER_SEC;
- ts->u64 += tv.tv_usec;
-
- return PJ_SUCCESS;
-}
-
-PJ_DEF(pj_status_t) pj_get_timestamp_freq(pj_timestamp *freq)
-{
- freq->u32.hi = 0;
- freq->u32.lo = USEC_PER_SEC;
-
- return PJ_SUCCESS;
-}
-
-#endif
-
+/* $Id$
+ *
+ */
+/*
+ * PJLIB - PJ Foundation Library
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ * Benny Prijono <bennylp@bulukucing.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; 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/errno.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <ctype.h>
+
+#if defined(PJ_HAS_PENTIUM) && PJ_HAS_PENTIUM!=0
+static int machine_speed_mhz;
+static pj_timestamp machine_speed;
+
+static __inline__ unsigned long long int rdtsc()
+{
+ unsigned long long int x;
+ __asm__ volatile (".byte 0x0f, 0x31" : "=A" (x));
+ return x;
+}
+
+/* Determine machine's CPU MHz to get the counter's frequency.
+ */
+static int get_machine_speed_mhz()
+{
+ FILE *strm;
+ char buf[512];
+ int len;
+ char *pos, *end;
+
+ PJ_CHECK_STACK();
+
+ /* Open /proc/cpuinfo and read the file */
+ strm = fopen("/proc/cpuinfo", "r");
+ if (!strm)
+ return -1;
+ len = fread(buf, 1, sizeof(buf), strm);
+ fclose(strm);
+ if (len < 1) {
+ return -1;
+ }
+ buf[len] = '\0';
+
+ /* Locate the MHz digit. */
+ pos = strstr(buf, "cpu MHz");
+ if (!pos)
+ return -1;
+ pos = strchr(pos, ':');
+ if (!pos)
+ return -1;
+ end = (pos += 2);
+ while (isdigit(*end)) ++end;
+ *end = '\0';
+
+ /* Return the Mhz part, and give it a +1. */
+ return atoi(pos)+1;
+}
+
+PJ_DEF(pj_status_t) pj_get_timestamp(pj_timestamp *ts)
+{
+ if (machine_speed_mhz == 0) {
+ machine_speed_mhz = get_machine_speed_mhz();
+ if (machine_speed_mhz > 0) {
+ machine_speed.u64 = machine_speed_mhz * 1000000.0;
+ }
+ }
+
+ if (machine_speed_mhz == -1) {
+ ts->u64 = 0;
+ return -1;
+ }
+ ts->u64 = rdtsc();
+ return 0;
+}
+
+PJ_DEF(pj_status_t) pj_get_timestamp_freq(pj_timestamp *freq)
+{
+ if (machine_speed_mhz == 0) {
+ machine_speed_mhz = get_machine_speed_mhz();
+ if (machine_speed_mhz > 0) {
+ machine_speed.u64 = machine_speed_mhz * 1000000.0;
+ }
+ }
+
+ if (machine_speed_mhz == -1) {
+ freq->u64 = 1; /* return 1 to prevent division by zero in apps. */
+ return -1;
+ }
+
+ freq->u64 = machine_speed.u64;
+ return 0;
+}
+
+#else
+#include <sys/time.h>
+#include <errno.h>
+
+#define USEC_PER_SEC 1000000
+
+PJ_DEF(pj_status_t) pj_get_timestamp(pj_timestamp *ts)
+{
+ struct timeval tv;
+
+ if (gettimeofday(&tv, NULL) != 0) {
+ return PJ_RETURN_OS_ERROR(pj_get_native_os_error());
+ }
+
+ ts->u64 = tv.tv_sec;
+ ts->u64 *= USEC_PER_SEC;
+ ts->u64 += tv.tv_usec;
+
+ return PJ_SUCCESS;
+}
+
+PJ_DEF(pj_status_t) pj_get_timestamp_freq(pj_timestamp *freq)
+{
+ freq->u32.hi = 0;
+ freq->u32.lo = USEC_PER_SEC;
+
+ return PJ_SUCCESS;
+}
+
+#endif
+
diff --git a/pjlib/src/pj/os_timestamp_linux_kernel.c b/pjlib/src/pj/os_timestamp_linux_kernel.c
index d70bcfd..0b75c76 100644
--- a/pjlib/src/pj/os_timestamp_linux_kernel.c
+++ b/pjlib/src/pj/os_timestamp_linux_kernel.c
@@ -1,63 +1,84 @@
-/* $Id$
- *
- */
-#include <pj/os.h>
-#include <linux/time.h>
-
-#if 0
-PJ_DEF(pj_status_t) pj_get_timestamp(pj_timestamp *ts)
-{
- ts->u32.hi = 0;
- ts->u32.lo = jiffies;
- return 0;
-}
-
-PJ_DEF(pj_status_t) pj_get_timestamp_freq(pj_timestamp *freq)
-{
- freq->u32.hi = 0;
- freq->u32.lo = HZ;
- return 0;
-}
-#elif 0
-PJ_DEF(pj_status_t) pj_get_timestamp(pj_timestamp *ts)
-{
- struct timespec tv;
-
- tv = CURRENT_TIME;
-
- ts->u64 = tv.tv_sec;
- ts->u64 *= NSEC_PER_SEC;
- ts->u64 += tv.tv_nsec;
-
- return PJ_SUCCESS;
-}
-
-PJ_DEF(pj_status_t) pj_get_timestamp_freq(pj_timestamp *freq)
-{
- freq->u32.hi = 0;
- freq->u32.lo = NSEC_PER_SEC;
- return 0;
-}
-#else
-PJ_DEF(pj_status_t) pj_get_timestamp(pj_timestamp *ts)
-{
- struct timeval tv;
-
- do_gettimeofday(&tv);
-
- ts->u64 = tv.tv_sec;
- ts->u64 *= USEC_PER_SEC;
- ts->u64 += tv.tv_usec;
-
- return PJ_SUCCESS;
-}
-
-PJ_DEF(pj_status_t) pj_get_timestamp_freq(pj_timestamp *freq)
-{
- freq->u32.hi = 0;
- freq->u32.lo = USEC_PER_SEC;
- return 0;
-}
-
-#endif
-
+/* $Id$
+ *
+ */
+/*
+ * PJLIB - PJ Foundation Library
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ * Benny Prijono <bennylp@bulukucing.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#include <pj/os.h>
+#include <linux/time.h>
+
+#if 0
+PJ_DEF(pj_status_t) pj_get_timestamp(pj_timestamp *ts)
+{
+ ts->u32.hi = 0;
+ ts->u32.lo = jiffies;
+ return 0;
+}
+
+PJ_DEF(pj_status_t) pj_get_timestamp_freq(pj_timestamp *freq)
+{
+ freq->u32.hi = 0;
+ freq->u32.lo = HZ;
+ return 0;
+}
+#elif 0
+PJ_DEF(pj_status_t) pj_get_timestamp(pj_timestamp *ts)
+{
+ struct timespec tv;
+
+ tv = CURRENT_TIME;
+
+ ts->u64 = tv.tv_sec;
+ ts->u64 *= NSEC_PER_SEC;
+ ts->u64 += tv.tv_nsec;
+
+ return PJ_SUCCESS;
+}
+
+PJ_DEF(pj_status_t) pj_get_timestamp_freq(pj_timestamp *freq)
+{
+ freq->u32.hi = 0;
+ freq->u32.lo = NSEC_PER_SEC;
+ return 0;
+}
+#else
+PJ_DEF(pj_status_t) pj_get_timestamp(pj_timestamp *ts)
+{
+ struct timeval tv;
+
+ do_gettimeofday(&tv);
+
+ ts->u64 = tv.tv_sec;
+ ts->u64 *= USEC_PER_SEC;
+ ts->u64 += tv.tv_usec;
+
+ return PJ_SUCCESS;
+}
+
+PJ_DEF(pj_status_t) pj_get_timestamp_freq(pj_timestamp *freq)
+{
+ freq->u32.hi = 0;
+ freq->u32.lo = USEC_PER_SEC;
+ return 0;
+}
+
+#endif
+
diff --git a/pjlib/src/pj/os_timestamp_win32.c b/pjlib/src/pj/os_timestamp_win32.c
index 1cfd81d..1626ca6 100644
--- a/pjlib/src/pj/os_timestamp_win32.c
+++ b/pjlib/src/pj/os_timestamp_win32.c
@@ -1,28 +1,49 @@
-/* $Id$
- */
-#include <pj/os.h>
-#include <pj/errno.h>
-#include <windows.h>
-
-PJ_DEF(pj_status_t) pj_get_timestamp(pj_timestamp *ts)
-{
- LARGE_INTEGER val;
-
- if (!QueryPerformanceCounter(&val))
- return PJ_RETURN_OS_ERROR(GetLastError());
-
- ts->u64 = val.QuadPart;
- return PJ_SUCCESS;
-}
-
-PJ_DEF(pj_status_t) pj_get_timestamp_freq(pj_timestamp *freq)
-{
- LARGE_INTEGER val;
-
- if (!QueryPerformanceFrequency(&val))
- return PJ_RETURN_OS_ERROR(GetLastError());
-
- freq->u64 = val.QuadPart;
- return PJ_SUCCESS;
-}
-
+/* $Id$
+ */
+/*
+ * PJLIB - PJ Foundation Library
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ * Benny Prijono <bennylp@bulukucing.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; 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/errno.h>
+#include <windows.h>
+
+PJ_DEF(pj_status_t) pj_get_timestamp(pj_timestamp *ts)
+{
+ LARGE_INTEGER val;
+
+ if (!QueryPerformanceCounter(&val))
+ return PJ_RETURN_OS_ERROR(GetLastError());
+
+ ts->u64 = val.QuadPart;
+ return PJ_SUCCESS;
+}
+
+PJ_DEF(pj_status_t) pj_get_timestamp_freq(pj_timestamp *freq)
+{
+ LARGE_INTEGER val;
+
+ if (!QueryPerformanceFrequency(&val))
+ return PJ_RETURN_OS_ERROR(GetLastError());
+
+ freq->u64 = val.QuadPart;
+ return PJ_SUCCESS;
+}
+
diff --git a/pjlib/src/pj/pool.c b/pjlib/src/pj/pool.c
index 5561612..c5a3192 100644
--- a/pjlib/src/pj/pool.c
+++ b/pjlib/src/pj/pool.c
@@ -1,256 +1,277 @@
-/* $Id$
- */
-#include <pj/pool.h>
-#include <pj/log.h>
-#include <pj/except.h>
-#include <pj/assert.h>
-#include <pj/os.h>
-#include <pj/compat/sprintf.h>
-
-/* Include inline definitions when inlining is disabled. */
-#if !PJ_FUNCTIONS_ARE_INLINED
-# include <pj/pool_i.h>
-#endif
-
-#define LOG(expr) PJ_LOG(5,expr)
-
-int PJ_NO_MEMORY_EXCEPTION;
-
-/*
- * Create new block.
- * Create a new big chunk of memory block, from which user allocation will be
- * taken from.
- */
-static pj_pool_block *pj_pool_create_block( pj_pool_t *pool, pj_size_t size)
-{
- pj_pool_block *block;
-
- PJ_CHECK_STACK();
- pj_assert(size >= sizeof(pj_pool_block));
-
- LOG((pool->obj_name, "create_block(sz=%u), cur.cap=%u, cur.used=%u",
- size, pool->capacity, pool->used_size));
-
- /* Request memory from allocator. */
- block = (pj_pool_block*)
- (*pool->factory->policy.block_alloc)(pool->factory, size);
- if (block == NULL) {
- (*pool->callback)(pool, size);
- return NULL;
- }
-
- /* Add capacity. */
- pool->capacity += size;
- pool->used_size += sizeof(pj_pool_block);
-
- /* Set block attribytes. */
- block->cur = block->buf = ((unsigned char*)block) + sizeof(pj_pool_block);
- block->end = ((unsigned char*)block) + size;
-
- /* Insert in the front of the list. */
- pj_list_insert_after(&pool->block_list, block);
-
- LOG((pool->obj_name," block created, buffer=%p-%p",block->buf, block->end));
-
- return block;
-}
-
-/*
- * Allocate memory chunk for user from available blocks.
- * This will iterate through block list to find space to allocate the chunk.
- * If no space is available in all the blocks, a new block might be created
- * (depending on whether the pool is allowed to resize).
- */
-PJ_DEF(void*) pj_pool_allocate_find(pj_pool_t *pool, unsigned size)
-{
- pj_pool_block *block = pool->block_list.next;
- void *p;
- unsigned block_size;
-
- PJ_CHECK_STACK();
-
- while (block != &pool->block_list) {
- p = pj_pool_alloc_from_block(pool, block, size);
- if (p != NULL)
- return p;
- block = block->next;
- }
- /* No available space in all blocks. */
-
- /* If pool is configured NOT to expand, return error. */
- if (pool->increment_size == 0) {
- LOG((pool->obj_name, "Can't expand pool to allocate %u bytes "
- "(used=%u, cap=%u)",
- size, pool->used_size, pool->capacity));
- (*pool->callback)(pool, size);
- return NULL;
- }
-
- /* If pool is configured to expand, but the increment size
- * is less than the required size, expand the pool by multiple
- * increment size
- */
- if (pool->increment_size < size + sizeof(pj_pool_block)) {
- unsigned count;
- count = (size + pool->increment_size + sizeof(pj_pool_block)) /
- pool->increment_size;
- block_size = count * pool->increment_size;
-
- } else {
- block_size = pool->increment_size;
- }
-
- LOG((pool->obj_name,
- "%u bytes requested, resizing pool by %u bytes (used=%u, cap=%u)",
- size, block_size, pool->used_size, pool->capacity));
-
- block = pj_pool_create_block(pool, block_size);
- if (!block)
- return NULL;
-
- p = pj_pool_alloc_from_block(pool, block, size);
- pj_assert(p != NULL);
-#if PJ_DEBUG
- if (p == NULL) {
- p = p;
- }
-#endif
- return p;
-}
-
-/*
- * Internal function to initialize pool.
- */
-PJ_DEF(void) pj_pool_init_int( pj_pool_t *pool,
- const char *name,
- pj_size_t increment_size,
- pj_pool_callback *callback)
-{
- pj_pool_block *block;
-
- PJ_CHECK_STACK();
-
- pool->increment_size = increment_size;
- pool->callback = callback;
- pool->used_size = sizeof(*pool);
- block = pool->block_list.next;
- while (block != &pool->block_list) {
- pool->used_size += sizeof(pj_pool_block);
- block = block->next;
- }
-
- if (name) {
- if (strchr(name, '%') != NULL) {
- sprintf(pool->obj_name, name, pool);
- } else {
- strncpy(pool->obj_name, name, PJ_MAX_OBJ_NAME);
- }
- } else {
- pool->obj_name[0] = '\0';
- }
-}
-
-/*
- * Create new memory pool.
- */
-PJ_DEF(pj_pool_t*) pj_pool_create_int( pj_pool_factory *f, const char *name,
- pj_size_t initial_size,
- pj_size_t increment_size,
- pj_pool_callback *callback)
-{
- pj_pool_t *pool;
- pj_pool_block *block;
- unsigned char *buffer;
-
- PJ_CHECK_STACK();
-
- buffer = (*f->policy.block_alloc)(f, initial_size);
- if (!buffer)
- return NULL;
-
- /* Set pool administrative data. */
- pool = (pj_pool_t*)buffer;
- pj_memset(pool, 0, sizeof(*pool));
-
- pj_list_init(&pool->block_list);
- pool->factory = f;
-
- /* Create the first block from the memory. */
- block = (pj_pool_block*) (buffer + sizeof(*pool));
- block->cur = block->buf = ((unsigned char*)block) + sizeof(pj_pool_block);
- block->end = buffer + initial_size;
- pj_list_insert_after(&pool->block_list, block);
-
- pj_pool_init_int(pool, name, increment_size, callback);
-
- /* Pool initial capacity and used size */
- pool->capacity = initial_size;
-
- LOG((pool->obj_name, "pool created, size=%u", pool->capacity));
- return pool;
-}
-
-/*
- * Reset the pool to the state when it was created.
- * All blocks will be deallocated except the first block. All memory areas
- * are marked as free.
- */
-static void reset_pool(pj_pool_t *pool)
-{
- pj_pool_block *block;
-
- PJ_CHECK_STACK();
-
- block = pool->block_list.prev;
- if (block == &pool->block_list)
- return;
-
- /* Skip the first block because it is occupying the same memory
- as the pool itself.
- */
- block = block->prev;
-
- while (block != &pool->block_list) {
- pj_pool_block *prev = block->prev;
- pj_list_erase(block);
- (*pool->factory->policy.block_free)(pool->factory, block,
- block->end - (unsigned char*)block);
- block = prev;
- }
-
- block = pool->block_list.next;
- block->cur = block->buf;
- pool->capacity = block->end - (unsigned char*)pool;
- pool->used_size = 0;
-}
-
-/*
- * The public function to reset pool.
- */
-PJ_DEF(void) pj_pool_reset(pj_pool_t *pool)
-{
- LOG((pool->obj_name, "reset(): cap=%d, used=%d(%d%%)",
- pool->capacity, pool->used_size, pool->used_size*100/pool->capacity));
-
- reset_pool(pool);
-}
-
-/*
- * Destroy the pool.
- */
-PJ_DEF(void) pj_pool_destroy_int(pj_pool_t *pool)
-{
- pj_size_t initial_size;
-
- LOG((pool->obj_name, "destroy(): cap=%d, used=%d(%d%%), block0=%p-%p",
- pool->capacity, pool->used_size, pool->used_size*100/pool->capacity,
- ((pj_pool_block*)pool->block_list.next)->buf,
- ((pj_pool_block*)pool->block_list.next)->end));
-
- reset_pool(pool);
- initial_size = ((pj_pool_block*)pool->block_list.next)->end -
- (unsigned char*)pool;
- (*pool->factory->policy.block_free)(pool->factory, pool, initial_size);
-}
-
-
+/* $Id$
+ */
+/*
+ * PJLIB - PJ Foundation Library
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ * Benny Prijono <bennylp@bulukucing.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; 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/log.h>
+#include <pj/except.h>
+#include <pj/assert.h>
+#include <pj/os.h>
+#include <pj/compat/sprintf.h>
+
+/* Include inline definitions when inlining is disabled. */
+#if !PJ_FUNCTIONS_ARE_INLINED
+# include <pj/pool_i.h>
+#endif
+
+#define LOG(expr) PJ_LOG(5,expr)
+
+int PJ_NO_MEMORY_EXCEPTION;
+
+/*
+ * Create new block.
+ * Create a new big chunk of memory block, from which user allocation will be
+ * taken from.
+ */
+static pj_pool_block *pj_pool_create_block( pj_pool_t *pool, pj_size_t size)
+{
+ pj_pool_block *block;
+
+ PJ_CHECK_STACK();
+ pj_assert(size >= sizeof(pj_pool_block));
+
+ LOG((pool->obj_name, "create_block(sz=%u), cur.cap=%u, cur.used=%u",
+ size, pool->capacity, pool->used_size));
+
+ /* Request memory from allocator. */
+ block = (pj_pool_block*)
+ (*pool->factory->policy.block_alloc)(pool->factory, size);
+ if (block == NULL) {
+ (*pool->callback)(pool, size);
+ return NULL;
+ }
+
+ /* Add capacity. */
+ pool->capacity += size;
+ pool->used_size += sizeof(pj_pool_block);
+
+ /* Set block attribytes. */
+ block->cur = block->buf = ((unsigned char*)block) + sizeof(pj_pool_block);
+ block->end = ((unsigned char*)block) + size;
+
+ /* Insert in the front of the list. */
+ pj_list_insert_after(&pool->block_list, block);
+
+ LOG((pool->obj_name," block created, buffer=%p-%p",block->buf, block->end));
+
+ return block;
+}
+
+/*
+ * Allocate memory chunk for user from available blocks.
+ * This will iterate through block list to find space to allocate the chunk.
+ * If no space is available in all the blocks, a new block might be created
+ * (depending on whether the pool is allowed to resize).
+ */
+PJ_DEF(void*) pj_pool_allocate_find(pj_pool_t *pool, unsigned size)
+{
+ pj_pool_block *block = pool->block_list.next;
+ void *p;
+ unsigned block_size;
+
+ PJ_CHECK_STACK();
+
+ while (block != &pool->block_list) {
+ p = pj_pool_alloc_from_block(pool, block, size);
+ if (p != NULL)
+ return p;
+ block = block->next;
+ }
+ /* No available space in all blocks. */
+
+ /* If pool is configured NOT to expand, return error. */
+ if (pool->increment_size == 0) {
+ LOG((pool->obj_name, "Can't expand pool to allocate %u bytes "
+ "(used=%u, cap=%u)",
+ size, pool->used_size, pool->capacity));
+ (*pool->callback)(pool, size);
+ return NULL;
+ }
+
+ /* If pool is configured to expand, but the increment size
+ * is less than the required size, expand the pool by multiple
+ * increment size
+ */
+ if (pool->increment_size < size + sizeof(pj_pool_block)) {
+ unsigned count;
+ count = (size + pool->increment_size + sizeof(pj_pool_block)) /
+ pool->increment_size;
+ block_size = count * pool->increment_size;
+
+ } else {
+ block_size = pool->increment_size;
+ }
+
+ LOG((pool->obj_name,
+ "%u bytes requested, resizing pool by %u bytes (used=%u, cap=%u)",
+ size, block_size, pool->used_size, pool->capacity));
+
+ block = pj_pool_create_block(pool, block_size);
+ if (!block)
+ return NULL;
+
+ p = pj_pool_alloc_from_block(pool, block, size);
+ pj_assert(p != NULL);
+#if PJ_DEBUG
+ if (p == NULL) {
+ p = p;
+ }
+#endif
+ return p;
+}
+
+/*
+ * Internal function to initialize pool.
+ */
+PJ_DEF(void) pj_pool_init_int( pj_pool_t *pool,
+ const char *name,
+ pj_size_t increment_size,
+ pj_pool_callback *callback)
+{
+ pj_pool_block *block;
+
+ PJ_CHECK_STACK();
+
+ pool->increment_size = increment_size;
+ pool->callback = callback;
+ pool->used_size = sizeof(*pool);
+ block = pool->block_list.next;
+ while (block != &pool->block_list) {
+ pool->used_size += sizeof(pj_pool_block);
+ block = block->next;
+ }
+
+ if (name) {
+ if (strchr(name, '%') != NULL) {
+ sprintf(pool->obj_name, name, pool);
+ } else {
+ strncpy(pool->obj_name, name, PJ_MAX_OBJ_NAME);
+ }
+ } else {
+ pool->obj_name[0] = '\0';
+ }
+}
+
+/*
+ * Create new memory pool.
+ */
+PJ_DEF(pj_pool_t*) pj_pool_create_int( pj_pool_factory *f, const char *name,
+ pj_size_t initial_size,
+ pj_size_t increment_size,
+ pj_pool_callback *callback)
+{
+ pj_pool_t *pool;
+ pj_pool_block *block;
+ unsigned char *buffer;
+
+ PJ_CHECK_STACK();
+
+ buffer = (*f->policy.block_alloc)(f, initial_size);
+ if (!buffer)
+ return NULL;
+
+ /* Set pool administrative data. */
+ pool = (pj_pool_t*)buffer;
+ pj_memset(pool, 0, sizeof(*pool));
+
+ pj_list_init(&pool->block_list);
+ pool->factory = f;
+
+ /* Create the first block from the memory. */
+ block = (pj_pool_block*) (buffer + sizeof(*pool));
+ block->cur = block->buf = ((unsigned char*)block) + sizeof(pj_pool_block);
+ block->end = buffer + initial_size;
+ pj_list_insert_after(&pool->block_list, block);
+
+ pj_pool_init_int(pool, name, increment_size, callback);
+
+ /* Pool initial capacity and used size */
+ pool->capacity = initial_size;
+
+ LOG((pool->obj_name, "pool created, size=%u", pool->capacity));
+ return pool;
+}
+
+/*
+ * Reset the pool to the state when it was created.
+ * All blocks will be deallocated except the first block. All memory areas
+ * are marked as free.
+ */
+static void reset_pool(pj_pool_t *pool)
+{
+ pj_pool_block *block;
+
+ PJ_CHECK_STACK();
+
+ block = pool->block_list.prev;
+ if (block == &pool->block_list)
+ return;
+
+ /* Skip the first block because it is occupying the same memory
+ as the pool itself.
+ */
+ block = block->prev;
+
+ while (block != &pool->block_list) {
+ pj_pool_block *prev = block->prev;
+ pj_list_erase(block);
+ (*pool->factory->policy.block_free)(pool->factory, block,
+ block->end - (unsigned char*)block);
+ block = prev;
+ }
+
+ block = pool->block_list.next;
+ block->cur = block->buf;
+ pool->capacity = block->end - (unsigned char*)pool;
+ pool->used_size = 0;
+}
+
+/*
+ * The public function to reset pool.
+ */
+PJ_DEF(void) pj_pool_reset(pj_pool_t *pool)
+{
+ LOG((pool->obj_name, "reset(): cap=%d, used=%d(%d%%)",
+ pool->capacity, pool->used_size, pool->used_size*100/pool->capacity));
+
+ reset_pool(pool);
+}
+
+/*
+ * Destroy the pool.
+ */
+PJ_DEF(void) pj_pool_destroy_int(pj_pool_t *pool)
+{
+ pj_size_t initial_size;
+
+ LOG((pool->obj_name, "destroy(): cap=%d, used=%d(%d%%), block0=%p-%p",
+ pool->capacity, pool->used_size, pool->used_size*100/pool->capacity,
+ ((pj_pool_block*)pool->block_list.next)->buf,
+ ((pj_pool_block*)pool->block_list.next)->end));
+
+ reset_pool(pool);
+ initial_size = ((pj_pool_block*)pool->block_list.next)->end -
+ (unsigned char*)pool;
+ (*pool->factory->policy.block_free)(pool->factory, pool, initial_size);
+}
+
+
diff --git a/pjlib/src/pj/pool_caching.c b/pjlib/src/pj/pool_caching.c
index d59ed28..20c55dd 100644
--- a/pjlib/src/pj/pool_caching.c
+++ b/pjlib/src/pj/pool_caching.c
@@ -1,205 +1,226 @@
-/* $Id$
- */
-#include <pj/pool.h>
-#include <pj/log.h>
-#include <pj/string.h>
-#include <pj/assert.h>
-#include <pj/os.h>
-
-static pj_pool_t* cpool_create_pool(pj_pool_factory *pf,
- const char *name,
- pj_size_t initial_size,
- pj_size_t increment_sz,
- pj_pool_callback *callback);
-static void cpool_release_pool(pj_pool_factory *pf, pj_pool_t *pool);
-static void cpool_dump_status(pj_pool_factory *factory, pj_bool_t detail );
-
-static pj_size_t pool_sizes[PJ_CACHING_POOL_ARRAY_SIZE] =
-{
- 256, 512, 1024, 2048, 4096, 8192, 12288, 16384,
- 20480, 24576, 28672, 32768, 40960, 49152, 57344, 65536
-};
-
-
-PJ_DEF(void) pj_caching_pool_init( pj_caching_pool *cp,
- const pj_pool_factory_policy *policy,
- pj_size_t max_capacity)
-{
- int i;
-
- PJ_CHECK_STACK();
-
- pj_memset(cp, 0, sizeof(*cp));
-
- cp->max_capacity = max_capacity;
- pj_list_init(&cp->used_list);
- for (i=0; i<PJ_CACHING_POOL_ARRAY_SIZE; ++i)
- pj_list_init(&cp->free_list[i]);
-
- pj_memcpy(&cp->factory.policy, policy, sizeof(pj_pool_factory_policy));
- cp->factory.create_pool = &cpool_create_pool;
- cp->factory.release_pool = &cpool_release_pool;
- cp->factory.dump_status = &cpool_dump_status;
-}
-
-PJ_DEF(void) pj_caching_pool_destroy( pj_caching_pool *cp )
-{
- int i;
- pj_pool_t *pool;
-
- PJ_CHECK_STACK();
-
- /* Delete all pool in free list */
- for (i=0; i < PJ_CACHING_POOL_ARRAY_SIZE; ++i) {
- pj_pool_t *pool = cp->free_list[i].next;
- pj_pool_t *next;
- for (; pool != (void*)&cp->free_list[i]; pool = next) {
- next = pool->next;
- pj_list_erase(pool);
- pj_pool_destroy_int(pool);
- }
- }
-
- /* Delete all pools in used list */
- pool = cp->used_list.next;
- while (pool != (pj_pool_t*) &cp->used_list) {
- pj_pool_t *next = pool->next;
- pj_list_erase(pool);
- pj_pool_destroy_int(pool);
- pool = next;
- }
-}
-
-static pj_pool_t* cpool_create_pool(pj_pool_factory *pf,
- const char *name,
- pj_size_t initial_size,
- pj_size_t increment_sz,
- pj_pool_callback *callback)
-{
- pj_caching_pool *cp = (pj_caching_pool*)pf;
- pj_pool_t *pool;
- int idx;
-
- PJ_CHECK_STACK();
-
- /* Use pool factory's policy when callback is NULL */
- if (callback == NULL) {
- callback = pf->policy.callback;
- }
-
- /* Search the suitable size for the pool.
- * We'll just do linear search to the size array, as the array size itself
- * is only a few elements. Binary search I suspect will be less efficient
- * for this purpose.
- */
- for (idx=0;
- idx < PJ_CACHING_POOL_ARRAY_SIZE && pool_sizes[idx] < initial_size;
- ++idx)
- ;
-
- /* Check whether there's a pool in the list. */
- if (idx==PJ_CACHING_POOL_ARRAY_SIZE || pj_list_empty(&cp->free_list[idx])) {
- /* No pool is available. */
- /* Set minimum size. */
- if (idx < PJ_CACHING_POOL_ARRAY_SIZE)
- initial_size = pool_sizes[idx];
-
- /* Create new pool */
- pool = pj_pool_create_int(&cp->factory, name, initial_size,
- increment_sz, callback);
- if (!pool)
- return NULL;
-
- } else {
- /* Get one pool from the list. */
- pool = cp->free_list[idx].next;
- pj_list_erase(pool);
-
- /* Initialize the pool. */
- pj_pool_init_int(pool, name, increment_sz, callback);
-
- /* Update pool manager's free capacity. */
- cp->capacity -= pj_pool_get_capacity(pool);
-
- PJ_LOG(5, (pool->obj_name, "pool reused, size=%u", pool->capacity));
- }
-
- /* Put in used list. */
- pj_list_insert_before( &cp->used_list, pool );
-
- /* Increment used count. */
- ++cp->used_count;
- return pool;
-}
-
-static void cpool_release_pool( pj_pool_factory *pf, pj_pool_t *pool)
-{
- pj_caching_pool *cp = (pj_caching_pool*)pf;
- int i;
-
- PJ_CHECK_STACK();
-
- /* Erase from the used list. */
- pj_list_erase(pool);
-
- /* Decrement used count. */
- --cp->used_count;
-
- /* Destroy the pool if the size is greater than our size or if the total
- * capacity in our recycle list (plus the size of the pool) exceeds
- * maximum capacity.
- . */
- if (pool->capacity > pool_sizes[PJ_CACHING_POOL_ARRAY_SIZE-1] ||
- cp->capacity + pool->capacity > cp->max_capacity)
- {
- pj_pool_destroy_int(pool);
- return;
- }
-
- /* Reset pool. */
- PJ_LOG(4, (pool->obj_name, "recycle(): cap=%d, used=%d(%d%%)",
- pool->capacity, pool->used_size, pool->used_size*100/pool->capacity));
- pj_pool_reset(pool);
-
- /*
- * Otherwise put the pool in our recycle list.
- */
- for (i=0; i < PJ_CACHING_POOL_ARRAY_SIZE && pool_sizes[i] != pool->capacity; ++i)
- ;
-
- pj_assert( i != PJ_CACHING_POOL_ARRAY_SIZE );
- if (i == PJ_CACHING_POOL_ARRAY_SIZE) {
- /* Something has gone wrong with the pool. */
- pj_pool_destroy_int(pool);
- return;
- }
-
- pj_list_insert_after(&cp->free_list[i], pool);
- cp->capacity += pool->capacity;
-}
-
-static void cpool_dump_status(pj_pool_factory *factory, pj_bool_t detail )
-{
-#if PJ_LOG_MAX_LEVEL >= 3
- pj_caching_pool *cp = (pj_caching_pool*)factory;
- PJ_LOG(3,("cachpool", " Dumping caching pool:"));
- PJ_LOG(3,("cachpool", " Capacity=%u, max_capacity=%u, used_cnt=%u", \
- cp->capacity, cp->max_capacity, cp->used_count));
- if (detail) {
- pj_pool_t *pool = cp->used_list.next;
- pj_uint32_t total_used = 0, total_capacity = 0;
- PJ_LOG(3,("cachpool", " Dumping all active pools:"));
- while (pool != (void*)&cp->used_list) {
- PJ_LOG(3,("cachpool", " %12s: %8d of %8d (%d%%) used", pool->obj_name,
- pool->used_size, pool->capacity,
- pool->used_size*100/pool->capacity));
- total_used += pool->used_size;
- total_capacity += pool->capacity;
- pool = pool->next;
- }
- PJ_LOG(3,("cachpool", " Total %9d of %9d (%d %%) used!",
- total_used, total_capacity,
- total_used * 100 / total_capacity));
- }
-#endif
-}
+/* $Id$
+ */
+/*
+ * PJLIB - PJ Foundation Library
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ * Benny Prijono <bennylp@bulukucing.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; 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/log.h>
+#include <pj/string.h>
+#include <pj/assert.h>
+#include <pj/os.h>
+
+static pj_pool_t* cpool_create_pool(pj_pool_factory *pf,
+ const char *name,
+ pj_size_t initial_size,
+ pj_size_t increment_sz,
+ pj_pool_callback *callback);
+static void cpool_release_pool(pj_pool_factory *pf, pj_pool_t *pool);
+static void cpool_dump_status(pj_pool_factory *factory, pj_bool_t detail );
+
+static pj_size_t pool_sizes[PJ_CACHING_POOL_ARRAY_SIZE] =
+{
+ 256, 512, 1024, 2048, 4096, 8192, 12288, 16384,
+ 20480, 24576, 28672, 32768, 40960, 49152, 57344, 65536
+};
+
+
+PJ_DEF(void) pj_caching_pool_init( pj_caching_pool *cp,
+ const pj_pool_factory_policy *policy,
+ pj_size_t max_capacity)
+{
+ int i;
+
+ PJ_CHECK_STACK();
+
+ pj_memset(cp, 0, sizeof(*cp));
+
+ cp->max_capacity = max_capacity;
+ pj_list_init(&cp->used_list);
+ for (i=0; i<PJ_CACHING_POOL_ARRAY_SIZE; ++i)
+ pj_list_init(&cp->free_list[i]);
+
+ pj_memcpy(&cp->factory.policy, policy, sizeof(pj_pool_factory_policy));
+ cp->factory.create_pool = &cpool_create_pool;
+ cp->factory.release_pool = &cpool_release_pool;
+ cp->factory.dump_status = &cpool_dump_status;
+}
+
+PJ_DEF(void) pj_caching_pool_destroy( pj_caching_pool *cp )
+{
+ int i;
+ pj_pool_t *pool;
+
+ PJ_CHECK_STACK();
+
+ /* Delete all pool in free list */
+ for (i=0; i < PJ_CACHING_POOL_ARRAY_SIZE; ++i) {
+ pj_pool_t *pool = cp->free_list[i].next;
+ pj_pool_t *next;
+ for (; pool != (void*)&cp->free_list[i]; pool = next) {
+ next = pool->next;
+ pj_list_erase(pool);
+ pj_pool_destroy_int(pool);
+ }
+ }
+
+ /* Delete all pools in used list */
+ pool = cp->used_list.next;
+ while (pool != (pj_pool_t*) &cp->used_list) {
+ pj_pool_t *next = pool->next;
+ pj_list_erase(pool);
+ pj_pool_destroy_int(pool);
+ pool = next;
+ }
+}
+
+static pj_pool_t* cpool_create_pool(pj_pool_factory *pf,
+ const char *name,
+ pj_size_t initial_size,
+ pj_size_t increment_sz,
+ pj_pool_callback *callback)
+{
+ pj_caching_pool *cp = (pj_caching_pool*)pf;
+ pj_pool_t *pool;
+ int idx;
+
+ PJ_CHECK_STACK();
+
+ /* Use pool factory's policy when callback is NULL */
+ if (callback == NULL) {
+ callback = pf->policy.callback;
+ }
+
+ /* Search the suitable size for the pool.
+ * We'll just do linear search to the size array, as the array size itself
+ * is only a few elements. Binary search I suspect will be less efficient
+ * for this purpose.
+ */
+ for (idx=0;
+ idx < PJ_CACHING_POOL_ARRAY_SIZE && pool_sizes[idx] < initial_size;
+ ++idx)
+ ;
+
+ /* Check whether there's a pool in the list. */
+ if (idx==PJ_CACHING_POOL_ARRAY_SIZE || pj_list_empty(&cp->free_list[idx])) {
+ /* No pool is available. */
+ /* Set minimum size. */
+ if (idx < PJ_CACHING_POOL_ARRAY_SIZE)
+ initial_size = pool_sizes[idx];
+
+ /* Create new pool */
+ pool = pj_pool_create_int(&cp->factory, name, initial_size,
+ increment_sz, callback);
+ if (!pool)
+ return NULL;
+
+ } else {
+ /* Get one pool from the list. */
+ pool = cp->free_list[idx].next;
+ pj_list_erase(pool);
+
+ /* Initialize the pool. */
+ pj_pool_init_int(pool, name, increment_sz, callback);
+
+ /* Update pool manager's free capacity. */
+ cp->capacity -= pj_pool_get_capacity(pool);
+
+ PJ_LOG(5, (pool->obj_name, "pool reused, size=%u", pool->capacity));
+ }
+
+ /* Put in used list. */
+ pj_list_insert_before( &cp->used_list, pool );
+
+ /* Increment used count. */
+ ++cp->used_count;
+ return pool;
+}
+
+static void cpool_release_pool( pj_pool_factory *pf, pj_pool_t *pool)
+{
+ pj_caching_pool *cp = (pj_caching_pool*)pf;
+ int i;
+
+ PJ_CHECK_STACK();
+
+ /* Erase from the used list. */
+ pj_list_erase(pool);
+
+ /* Decrement used count. */
+ --cp->used_count;
+
+ /* Destroy the pool if the size is greater than our size or if the total
+ * capacity in our recycle list (plus the size of the pool) exceeds
+ * maximum capacity.
+ . */
+ if (pool->capacity > pool_sizes[PJ_CACHING_POOL_ARRAY_SIZE-1] ||
+ cp->capacity + pool->capacity > cp->max_capacity)
+ {
+ pj_pool_destroy_int(pool);
+ return;
+ }
+
+ /* Reset pool. */
+ PJ_LOG(4, (pool->obj_name, "recycle(): cap=%d, used=%d(%d%%)",
+ pool->capacity, pool->used_size, pool->used_size*100/pool->capacity));
+ pj_pool_reset(pool);
+
+ /*
+ * Otherwise put the pool in our recycle list.
+ */
+ for (i=0; i < PJ_CACHING_POOL_ARRAY_SIZE && pool_sizes[i] != pool->capacity; ++i)
+ ;
+
+ pj_assert( i != PJ_CACHING_POOL_ARRAY_SIZE );
+ if (i == PJ_CACHING_POOL_ARRAY_SIZE) {
+ /* Something has gone wrong with the pool. */
+ pj_pool_destroy_int(pool);
+ return;
+ }
+
+ pj_list_insert_after(&cp->free_list[i], pool);
+ cp->capacity += pool->capacity;
+}
+
+static void cpool_dump_status(pj_pool_factory *factory, pj_bool_t detail )
+{
+#if PJ_LOG_MAX_LEVEL >= 3
+ pj_caching_pool *cp = (pj_caching_pool*)factory;
+ PJ_LOG(3,("cachpool", " Dumping caching pool:"));
+ PJ_LOG(3,("cachpool", " Capacity=%u, max_capacity=%u, used_cnt=%u", \
+ cp->capacity, cp->max_capacity, cp->used_count));
+ if (detail) {
+ pj_pool_t *pool = cp->used_list.next;
+ pj_uint32_t total_used = 0, total_capacity = 0;
+ PJ_LOG(3,("cachpool", " Dumping all active pools:"));
+ while (pool != (void*)&cp->used_list) {
+ PJ_LOG(3,("cachpool", " %12s: %8d of %8d (%d%%) used", pool->obj_name,
+ pool->used_size, pool->capacity,
+ pool->used_size*100/pool->capacity));
+ total_used += pool->used_size;
+ total_capacity += pool->capacity;
+ pool = pool->next;
+ }
+ PJ_LOG(3,("cachpool", " Total %9d of %9d (%d %%) used!",
+ total_used, total_capacity,
+ total_used * 100 / total_capacity));
+ }
+#endif
+}
diff --git a/pjlib/src/pj/pool_dbg_win32.c b/pjlib/src/pj/pool_dbg_win32.c
index 9e451d7..33aa80d 100644
--- a/pjlib/src/pj/pool_dbg_win32.c
+++ b/pjlib/src/pj/pool_dbg_win32.c
@@ -1,221 +1,242 @@
-/* $Id$
- */
-#include <pj/pool.h>
-
-/* Only if we ARE debugging memory allocations. */
-#if PJ_POOL_DEBUG
-
-#include <pj/list.h>
-#include <pj/log.h>
-
-#include <stdlib.h>
-#include <stdio.h>
-#define WIN32_LEAN_AND_MEAN
-#include <windows.h>
-
-typedef struct memory_entry
-{
- PJ_DECL_LIST_MEMBER(struct memory_entry)
- void *ptr;
- char *file;
- int line;
-} memory_entry;
-
-struct pj_pool_t
-{
- char obj_name[32];
- HANDLE hHeap;
- memory_entry first;
- pj_size_t initial_size;
- pj_size_t increment;
- pj_size_t used_size;
- char *file;
- int line;
-};
-
-PJ_DEF(void) pj_pool_set_functions( void *(*malloc_func)(pj_size_t),
- void (*free_func)(void *ptr, pj_size_t))
-{
- /* Ignored. */
- PJ_CHECK_STACK();
- PJ_UNUSED_ARG(malloc_func)
- PJ_UNUSED_ARG(free_func)
-}
-
-PJ_DEF(pj_pool_t*) pj_pool_create_dbg( const char *name,
- pj_size_t initial_size,
- pj_size_t increment_size,
- pj_pool_callback *callback,
- char *file, int line)
-{
- pj_pool_t *pool;
- HANDLE hHeap;
-
- PJ_CHECK_STACK();
- PJ_UNUSED_ARG(callback)
-
- /* Create Win32 heap for the pool. */
- hHeap = HeapCreate(HEAP_GENERATE_EXCEPTIONS|HEAP_NO_SERIALIZE,
- initial_size, 0);
- if (!hHeap) {
- return NULL;
- }
-
-
- /* Create and initialize the pool structure. */
- pool = HeapAlloc(hHeap, HEAP_GENERATE_EXCEPTIONS|HEAP_NO_SERIALIZE,
- sizeof(*pool));
- memset(pool, 0, sizeof(*pool));
- pool->file = file;
- pool->line = line;
- pool->hHeap = hHeap;
- pool->initial_size = initial_size;
- pool->increment = increment_size;
- pool->used_size = 0;
-
- /* Set name. */
- if (name) {
- if (strchr(name, '%') != NULL) {
- sprintf(pool->obj_name, name, pool);
- } else {
- strncpy(pool->obj_name, name, PJ_MAX_OBJ_NAME);
- }
- } else {
- pool->obj_name[0] = '\0';
- }
-
- /* List pool's entry. */
- pj_list_init(&pool->first);
-
- PJ_LOG(3,(pool->obj_name, "Pool created"));
- return pool;
-}
-
-PJ_DEF(void) pj_pool_destroy( pj_pool_t *pool )
-{
- memory_entry *entry;
-
- PJ_CHECK_STACK();
-
- PJ_LOG(3,(pool->obj_name, "Destoying pool, init_size=%u, used=%u",
- pool->initial_size, pool->used_size));
-
- if (!HeapValidate( pool->hHeap, HEAP_NO_SERIALIZE, pool)) {
- PJ_LOG(2,(pool->obj_name, "Corrupted pool structure, allocated in %s:%d",
- pool->file, pool->line));
- }
-
- /* Validate all memory entries in the pool. */
- for (entry=pool->first.next; entry != &pool->first; entry = entry->next) {
- if (!HeapValidate( pool->hHeap, HEAP_NO_SERIALIZE, entry)) {
- PJ_LOG(2,(pool->obj_name, "Corrupted pool entry, allocated in %s:%d",
- entry->file, entry->line));
- }
-
- if (!HeapValidate( pool->hHeap, HEAP_NO_SERIALIZE, entry->ptr)) {
- PJ_LOG(2,(pool->obj_name, "Corrupted pool memory, allocated in %s:%d",
- entry->file, entry->line));
- }
- }
-
- /* Destroy heap. */
- HeapDestroy(pool->hHeap);
-}
-
-PJ_DEF(void) pj_pool_reset( pj_pool_t *pool )
-{
- /* Do nothing. */
- PJ_CHECK_STACK();
- PJ_UNUSED_ARG(pool)
-}
-
-PJ_DEF(pj_size_t) pj_pool_get_capacity( pj_pool_t *pool )
-{
- PJ_CHECK_STACK();
- PJ_UNUSED_ARG(pool)
- return 0;
-}
-
-PJ_DEF(pj_size_t) pj_pool_get_used_size( pj_pool_t *pool )
-{
- PJ_CHECK_STACK();
- PJ_UNUSED_ARG(pool)
- return 0;
-}
-
-PJ_DEF(pj_size_t) pj_pool_get_request_count( pj_pool_t *pool )
-{
- PJ_CHECK_STACK();
- PJ_UNUSED_ARG(pool)
- return 0;
-}
-
-PJ_DEF(void*) pj_pool_alloc_dbg( pj_pool_t *pool, pj_size_t size,
- char *file, int line)
-{
- memory_entry *entry;
- int entry_size;
-
- PJ_CHECK_STACK();
-
- entry_size = sizeof(*entry);
- entry = HeapAlloc(pool->hHeap, HEAP_GENERATE_EXCEPTIONS|HEAP_NO_SERIALIZE,
- entry_size);
- entry->file = file;
- entry->line = line;
- entry->ptr = HeapAlloc(pool->hHeap, HEAP_GENERATE_EXCEPTIONS|HEAP_NO_SERIALIZE,
- size);
- pj_list_insert_before( &pool->first, entry);
-
- pool->used_size += size;
- return entry->ptr;
-}
-
-PJ_DEF(void*) pj_pool_calloc_dbg( pj_pool_t *pool, pj_size_t count, pj_size_t elem,
- char *file, int line)
-{
- void *ptr;
-
- PJ_CHECK_STACK();
-
- ptr = pj_pool_alloc_dbg(pool, count*elem, file, line);
- memset(ptr, 0, count*elem);
- return ptr;
-}
-
-
-PJ_DEF(void) pj_pool_pool_init( pj_pool_pool_t *pool_pool,
- pj_size_t max_capacity)
-{
- PJ_CHECK_STACK();
- PJ_UNUSED_ARG(pool_pool)
- PJ_UNUSED_ARG(max_capacity)
-}
-
-PJ_DEF(void) pj_pool_pool_destroy( pj_pool_pool_t *pool_pool )
-{
- PJ_CHECK_STACK();
- PJ_UNUSED_ARG(pool_pool)
-}
-
-PJ_DEF(pj_pool_t*) pj_pool_pool_create_pool( pj_pool_pool_t *pool_pool,
- const char *name,
- pj_size_t initial_size,
- pj_size_t increment_size,
- pj_pool_callback *callback)
-{
- PJ_CHECK_STACK();
- PJ_UNUSED_ARG(pool_pool)
- return pj_pool_create(name, initial_size, increment_size, callback);
-}
-
-PJ_DEF(void) pj_pool_pool_release_pool( pj_pool_pool_t *pool_pool,
- pj_pool_t *pool )
-{
- PJ_CHECK_STACK();
- PJ_UNUSED_ARG(pool_pool)
- pj_pool_destroy(pool);
-}
-
-
-#endif /* PJ_POOL_DEBUG */
+/* $Id$
+ */
+/*
+ * PJLIB - PJ Foundation Library
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ * Benny Prijono <bennylp@bulukucing.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#include <pj/pool.h>
+
+/* Only if we ARE debugging memory allocations. */
+#if PJ_POOL_DEBUG
+
+#include <pj/list.h>
+#include <pj/log.h>
+
+#include <stdlib.h>
+#include <stdio.h>
+#define WIN32_LEAN_AND_MEAN
+#include <windows.h>
+
+typedef struct memory_entry
+{
+ PJ_DECL_LIST_MEMBER(struct memory_entry)
+ void *ptr;
+ char *file;
+ int line;
+} memory_entry;
+
+struct pj_pool_t
+{
+ char obj_name[32];
+ HANDLE hHeap;
+ memory_entry first;
+ pj_size_t initial_size;
+ pj_size_t increment;
+ pj_size_t used_size;
+ char *file;
+ int line;
+};
+
+PJ_DEF(void) pj_pool_set_functions( void *(*malloc_func)(pj_size_t),
+ void (*free_func)(void *ptr, pj_size_t))
+{
+ /* Ignored. */
+ PJ_CHECK_STACK();
+ PJ_UNUSED_ARG(malloc_func)
+ PJ_UNUSED_ARG(free_func)
+}
+
+PJ_DEF(pj_pool_t*) pj_pool_create_dbg( const char *name,
+ pj_size_t initial_size,
+ pj_size_t increment_size,
+ pj_pool_callback *callback,
+ char *file, int line)
+{
+ pj_pool_t *pool;
+ HANDLE hHeap;
+
+ PJ_CHECK_STACK();
+ PJ_UNUSED_ARG(callback)
+
+ /* Create Win32 heap for the pool. */
+ hHeap = HeapCreate(HEAP_GENERATE_EXCEPTIONS|HEAP_NO_SERIALIZE,
+ initial_size, 0);
+ if (!hHeap) {
+ return NULL;
+ }
+
+
+ /* Create and initialize the pool structure. */
+ pool = HeapAlloc(hHeap, HEAP_GENERATE_EXCEPTIONS|HEAP_NO_SERIALIZE,
+ sizeof(*pool));
+ memset(pool, 0, sizeof(*pool));
+ pool->file = file;
+ pool->line = line;
+ pool->hHeap = hHeap;
+ pool->initial_size = initial_size;
+ pool->increment = increment_size;
+ pool->used_size = 0;
+
+ /* Set name. */
+ if (name) {
+ if (strchr(name, '%') != NULL) {
+ sprintf(pool->obj_name, name, pool);
+ } else {
+ strncpy(pool->obj_name, name, PJ_MAX_OBJ_NAME);
+ }
+ } else {
+ pool->obj_name[0] = '\0';
+ }
+
+ /* List pool's entry. */
+ pj_list_init(&pool->first);
+
+ PJ_LOG(3,(pool->obj_name, "Pool created"));
+ return pool;
+}
+
+PJ_DEF(void) pj_pool_destroy( pj_pool_t *pool )
+{
+ memory_entry *entry;
+
+ PJ_CHECK_STACK();
+
+ PJ_LOG(3,(pool->obj_name, "Destoying pool, init_size=%u, used=%u",
+ pool->initial_size, pool->used_size));
+
+ if (!HeapValidate( pool->hHeap, HEAP_NO_SERIALIZE, pool)) {
+ PJ_LOG(2,(pool->obj_name, "Corrupted pool structure, allocated in %s:%d",
+ pool->file, pool->line));
+ }
+
+ /* Validate all memory entries in the pool. */
+ for (entry=pool->first.next; entry != &pool->first; entry = entry->next) {
+ if (!HeapValidate( pool->hHeap, HEAP_NO_SERIALIZE, entry)) {
+ PJ_LOG(2,(pool->obj_name, "Corrupted pool entry, allocated in %s:%d",
+ entry->file, entry->line));
+ }
+
+ if (!HeapValidate( pool->hHeap, HEAP_NO_SERIALIZE, entry->ptr)) {
+ PJ_LOG(2,(pool->obj_name, "Corrupted pool memory, allocated in %s:%d",
+ entry->file, entry->line));
+ }
+ }
+
+ /* Destroy heap. */
+ HeapDestroy(pool->hHeap);
+}
+
+PJ_DEF(void) pj_pool_reset( pj_pool_t *pool )
+{
+ /* Do nothing. */
+ PJ_CHECK_STACK();
+ PJ_UNUSED_ARG(pool)
+}
+
+PJ_DEF(pj_size_t) pj_pool_get_capacity( pj_pool_t *pool )
+{
+ PJ_CHECK_STACK();
+ PJ_UNUSED_ARG(pool)
+ return 0;
+}
+
+PJ_DEF(pj_size_t) pj_pool_get_used_size( pj_pool_t *pool )
+{
+ PJ_CHECK_STACK();
+ PJ_UNUSED_ARG(pool)
+ return 0;
+}
+
+PJ_DEF(pj_size_t) pj_pool_get_request_count( pj_pool_t *pool )
+{
+ PJ_CHECK_STACK();
+ PJ_UNUSED_ARG(pool)
+ return 0;
+}
+
+PJ_DEF(void*) pj_pool_alloc_dbg( pj_pool_t *pool, pj_size_t size,
+ char *file, int line)
+{
+ memory_entry *entry;
+ int entry_size;
+
+ PJ_CHECK_STACK();
+
+ entry_size = sizeof(*entry);
+ entry = HeapAlloc(pool->hHeap, HEAP_GENERATE_EXCEPTIONS|HEAP_NO_SERIALIZE,
+ entry_size);
+ entry->file = file;
+ entry->line = line;
+ entry->ptr = HeapAlloc(pool->hHeap, HEAP_GENERATE_EXCEPTIONS|HEAP_NO_SERIALIZE,
+ size);
+ pj_list_insert_before( &pool->first, entry);
+
+ pool->used_size += size;
+ return entry->ptr;
+}
+
+PJ_DEF(void*) pj_pool_calloc_dbg( pj_pool_t *pool, pj_size_t count, pj_size_t elem,
+ char *file, int line)
+{
+ void *ptr;
+
+ PJ_CHECK_STACK();
+
+ ptr = pj_pool_alloc_dbg(pool, count*elem, file, line);
+ memset(ptr, 0, count*elem);
+ return ptr;
+}
+
+
+PJ_DEF(void) pj_pool_pool_init( pj_pool_pool_t *pool_pool,
+ pj_size_t max_capacity)
+{
+ PJ_CHECK_STACK();
+ PJ_UNUSED_ARG(pool_pool)
+ PJ_UNUSED_ARG(max_capacity)
+}
+
+PJ_DEF(void) pj_pool_pool_destroy( pj_pool_pool_t *pool_pool )
+{
+ PJ_CHECK_STACK();
+ PJ_UNUSED_ARG(pool_pool)
+}
+
+PJ_DEF(pj_pool_t*) pj_pool_pool_create_pool( pj_pool_pool_t *pool_pool,
+ const char *name,
+ pj_size_t initial_size,
+ pj_size_t increment_size,
+ pj_pool_callback *callback)
+{
+ PJ_CHECK_STACK();
+ PJ_UNUSED_ARG(pool_pool)
+ return pj_pool_create(name, initial_size, increment_size, callback);
+}
+
+PJ_DEF(void) pj_pool_pool_release_pool( pj_pool_pool_t *pool_pool,
+ pj_pool_t *pool )
+{
+ PJ_CHECK_STACK();
+ PJ_UNUSED_ARG(pool_pool)
+ pj_pool_destroy(pool);
+}
+
+
+#endif /* PJ_POOL_DEBUG */
diff --git a/pjlib/src/pj/pool_policy_kmalloc.c b/pjlib/src/pj/pool_policy_kmalloc.c
index 267f72f..6b40d31 100644
--- a/pjlib/src/pj/pool_policy_kmalloc.c
+++ b/pjlib/src/pj/pool_policy_kmalloc.c
@@ -1,43 +1,64 @@
-/* $Id$
- *
- */
-#include <pj/pool.h>
-#include <pj/except.h>
-#include <pj/os.h>
-
-
-static void *default_block_alloc(pj_pool_factory *factory, pj_size_t size)
-{
- PJ_CHECK_STACK();
- PJ_UNUSED_ARG(factory);
-
- return kmalloc(size, GFP_ATOMIC);
-}
-
-static void default_block_free(pj_pool_factory *factory,
- void *mem, pj_size_t size)
-{
- PJ_CHECK_STACK();
- PJ_UNUSED_ARG(factory);
- PJ_UNUSED_ARG(size);
-
- kfree(mem);
-}
-
-static void default_pool_callback(pj_pool_t *pool, pj_size_t size)
-{
- PJ_CHECK_STACK();
- PJ_UNUSED_ARG(pool);
- PJ_UNUSED_ARG(size);
-
- PJ_THROW(PJ_NO_MEMORY_EXCEPTION);
-}
-
-pj_pool_factory_policy pj_pool_factory_default_policy =
-{
- &default_block_alloc,
- &default_block_free,
- &default_pool_callback,
- 0
-};
-
+/* $Id$
+ *
+ */
+/*
+ * PJLIB - PJ Foundation Library
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ * Benny Prijono <bennylp@bulukucing.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; 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/except.h>
+#include <pj/os.h>
+
+
+static void *default_block_alloc(pj_pool_factory *factory, pj_size_t size)
+{
+ PJ_CHECK_STACK();
+ PJ_UNUSED_ARG(factory);
+
+ return kmalloc(size, GFP_ATOMIC);
+}
+
+static void default_block_free(pj_pool_factory *factory,
+ void *mem, pj_size_t size)
+{
+ PJ_CHECK_STACK();
+ PJ_UNUSED_ARG(factory);
+ PJ_UNUSED_ARG(size);
+
+ kfree(mem);
+}
+
+static void default_pool_callback(pj_pool_t *pool, pj_size_t size)
+{
+ PJ_CHECK_STACK();
+ PJ_UNUSED_ARG(pool);
+ PJ_UNUSED_ARG(size);
+
+ PJ_THROW(PJ_NO_MEMORY_EXCEPTION);
+}
+
+pj_pool_factory_policy pj_pool_factory_default_policy =
+{
+ &default_block_alloc,
+ &default_block_free,
+ &default_pool_callback,
+ 0
+};
+
diff --git a/pjlib/src/pj/pool_policy_malloc.c b/pjlib/src/pj/pool_policy_malloc.c
index bb82cbb..4baf98b 100644
--- a/pjlib/src/pj/pool_policy_malloc.c
+++ b/pjlib/src/pj/pool_policy_malloc.c
@@ -1,46 +1,67 @@
-/* $Id$
- */
-#include <pj/pool.h>
-#include <pj/except.h>
-#include <pj/os.h>
-#include <pj/compat/malloc.h>
-
-/*
- * This file contains pool default policy definition and implementation.
- */
-
-
-static void *default_block_alloc(pj_pool_factory *factory, pj_size_t size)
-{
- PJ_CHECK_STACK();
- PJ_UNUSED_ARG(factory);
- PJ_UNUSED_ARG(size);
-
- return malloc(size);
-}
-
-static void default_block_free(pj_pool_factory *factory, void *mem, pj_size_t size)
-{
- PJ_CHECK_STACK();
- PJ_UNUSED_ARG(factory);
- PJ_UNUSED_ARG(size);
-
- free(mem);
-}
-
-static void default_pool_callback(pj_pool_t *pool, pj_size_t size)
-{
- PJ_CHECK_STACK();
- PJ_UNUSED_ARG(pool);
- PJ_UNUSED_ARG(size);
-
- PJ_THROW(PJ_NO_MEMORY_EXCEPTION);
-}
-
-pj_pool_factory_policy pj_pool_factory_default_policy =
-{
- &default_block_alloc,
- &default_block_free,
- &default_pool_callback,
- 0
-};
+/* $Id$
+ */
+/*
+ * PJLIB - PJ Foundation Library
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ * Benny Prijono <bennylp@bulukucing.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; 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/except.h>
+#include <pj/os.h>
+#include <pj/compat/malloc.h>
+
+/*
+ * This file contains pool default policy definition and implementation.
+ */
+
+
+static void *default_block_alloc(pj_pool_factory *factory, pj_size_t size)
+{
+ PJ_CHECK_STACK();
+ PJ_UNUSED_ARG(factory);
+ PJ_UNUSED_ARG(size);
+
+ return malloc(size);
+}
+
+static void default_block_free(pj_pool_factory *factory, void *mem, pj_size_t size)
+{
+ PJ_CHECK_STACK();
+ PJ_UNUSED_ARG(factory);
+ PJ_UNUSED_ARG(size);
+
+ free(mem);
+}
+
+static void default_pool_callback(pj_pool_t *pool, pj_size_t size)
+{
+ PJ_CHECK_STACK();
+ PJ_UNUSED_ARG(pool);
+ PJ_UNUSED_ARG(size);
+
+ PJ_THROW(PJ_NO_MEMORY_EXCEPTION);
+}
+
+pj_pool_factory_policy pj_pool_factory_default_policy =
+{
+ &default_block_alloc,
+ &default_block_free,
+ &default_pool_callback,
+ 0
+};
diff --git a/pjlib/src/pj/rand.c b/pjlib/src/pj/rand.c
index a8a2878..df549d5 100644
--- a/pjlib/src/pj/rand.c
+++ b/pjlib/src/pj/rand.c
@@ -1,18 +1,39 @@
-/* $Id$
- */
-#include <pj/rand.h>
-#include <pj/os.h>
-#include <pj/compat/rand.h>
-
-PJ_DEF(void) pj_srand(unsigned int seed)
-{
- PJ_CHECK_STACK();
- platform_srand(seed);
-}
-
-PJ_DEF(int) pj_rand(void)
-{
- PJ_CHECK_STACK();
- return platform_rand();
-}
-
+/* $Id$
+ */
+/*
+ * PJLIB - PJ Foundation Library
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ * Benny Prijono <bennylp@bulukucing.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; 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/os.h>
+#include <pj/compat/rand.h>
+
+PJ_DEF(void) pj_srand(unsigned int seed)
+{
+ PJ_CHECK_STACK();
+ platform_srand(seed);
+}
+
+PJ_DEF(int) pj_rand(void)
+{
+ PJ_CHECK_STACK();
+ return platform_rand();
+}
+
diff --git a/pjlib/src/pj/rbtree.c b/pjlib/src/pj/rbtree.c
index a0babe0..2f548c2 100644
--- a/pjlib/src/pj/rbtree.c
+++ b/pjlib/src/pj/rbtree.c
@@ -1,411 +1,432 @@
-/* $Id$
- */
-#include <pj/rbtree.h>
-#include <pj/os.h>
-
-static void left_rotate( pj_rbtree *tree, pj_rbtree_node *node )
-{
- pj_rbtree_node *rnode, *parent;
-
- PJ_CHECK_STACK();
-
- rnode = node->right;
- if (rnode == tree->null)
- return;
-
- node->right = rnode->left;
- if (rnode->left != tree->null)
- rnode->left->parent = node;
- parent = node->parent;
- rnode->parent = parent;
- if (parent != tree->null) {
- if (parent->left == node)
- parent->left = rnode;
- else
- parent->right = rnode;
- } else {
- tree->root = rnode;
- }
- rnode->left = node;
- node->parent = rnode;
-}
-
-static void right_rotate( pj_rbtree *tree, pj_rbtree_node *node )
-{
- pj_rbtree_node *lnode, *parent;
-
- PJ_CHECK_STACK();
-
- lnode = node->left;
- if (lnode == tree->null)
- return;
-
- node->left = lnode->right;
- if (lnode->right != tree->null)
- lnode->right->parent = node;
- parent = node->parent;
- lnode->parent = parent;
-
- if (parent != tree->null) {
- if (parent->left == node)
- parent->left = lnode;
- else
- parent->right = lnode;
- } else {
- tree->root = lnode;
- }
- lnode->right = node;
- node->parent = lnode;
-}
-
-static void insert_fixup( pj_rbtree *tree, pj_rbtree_node *node )
-{
- pj_rbtree_node *temp, *parent;
-
- PJ_CHECK_STACK();
-
- while (node != tree->root && node->parent->color == PJ_RBCOLOR_RED) {
- parent = node->parent;
- if (parent == parent->parent->left) {
- temp = parent->parent->right;
- if (temp->color == PJ_RBCOLOR_RED) {
- temp->color = PJ_RBCOLOR_BLACK;
- node = parent;
- node->color = PJ_RBCOLOR_BLACK;
- node = node->parent;
- node->color = PJ_RBCOLOR_RED;
- } else {
- if (node == parent->right) {
- node = parent;
- left_rotate(tree, node);
- }
- temp = node->parent;
- temp->color = PJ_RBCOLOR_BLACK;
- temp = temp->parent;
- temp->color = PJ_RBCOLOR_RED;
- right_rotate( tree, temp);
- }
- } else {
- temp = parent->parent->left;
- if (temp->color == PJ_RBCOLOR_RED) {
- temp->color = PJ_RBCOLOR_BLACK;
- node = parent;
- node->color = PJ_RBCOLOR_BLACK;
- node = node->parent;
- node->color = PJ_RBCOLOR_RED;
- } else {
- if (node == parent->left) {
- node = parent;
- right_rotate(tree, node);
- }
- temp = node->parent;
- temp->color = PJ_RBCOLOR_BLACK;
- temp = temp->parent;
- temp->color = PJ_RBCOLOR_RED;
- left_rotate(tree, temp);
- }
- }
- }
-
- tree->root->color = PJ_RBCOLOR_BLACK;
-}
-
-
-static void delete_fixup( pj_rbtree *tree, pj_rbtree_node *node )
-{
- pj_rbtree_node *temp;
-
- PJ_CHECK_STACK();
-
- while (node != tree->root && node->color == PJ_RBCOLOR_BLACK) {
- if (node->parent->left == node) {
- temp = node->parent->right;
- if (temp->color == PJ_RBCOLOR_RED) {
- temp->color = PJ_RBCOLOR_BLACK;
- node->parent->color = PJ_RBCOLOR_RED;
- left_rotate(tree, node->parent);
- temp = node->parent->right;
- }
- if (temp->left->color == PJ_RBCOLOR_BLACK &&
- temp->right->color == PJ_RBCOLOR_BLACK)
- {
- temp->color = PJ_RBCOLOR_RED;
- node = node->parent;
- } else {
- if (temp->right->color == PJ_RBCOLOR_BLACK) {
- temp->left->color = PJ_RBCOLOR_BLACK;
- temp->color = PJ_RBCOLOR_RED;
- right_rotate( tree, temp);
- temp = node->parent->right;
- }
- temp->color = node->parent->color;
- temp->right->color = PJ_RBCOLOR_BLACK;
- node->parent->color = PJ_RBCOLOR_BLACK;
- left_rotate(tree, node->parent);
- node = tree->root;
- }
- } else {
- temp = node->parent->left;
- if (temp->color == PJ_RBCOLOR_RED) {
- temp->color = PJ_RBCOLOR_BLACK;
- node->parent->color = PJ_RBCOLOR_RED;
- right_rotate( tree, node->parent);
- temp = node->parent->left;
- }
- if (temp->right->color == PJ_RBCOLOR_BLACK &&
- temp->left->color == PJ_RBCOLOR_BLACK)
- {
- temp->color = PJ_RBCOLOR_RED;
- node = node->parent;
- } else {
- if (temp->left->color == PJ_RBCOLOR_BLACK) {
- temp->right->color = PJ_RBCOLOR_BLACK;
- temp->color = PJ_RBCOLOR_RED;
- left_rotate( tree, temp);
- temp = node->parent->left;
- }
- temp->color = node->parent->color;
- node->parent->color = PJ_RBCOLOR_BLACK;
- temp->left->color = PJ_RBCOLOR_BLACK;
- right_rotate(tree, node->parent);
- node = tree->root;
- }
- }
- }
-
- node->color = PJ_RBCOLOR_BLACK;
-}
-
-
-PJ_DEF(void) pj_rbtree_init( pj_rbtree *tree, pj_rbtree_comp *comp )
-{
- PJ_CHECK_STACK();
-
- tree->null = tree->root = &tree->null_node;
- tree->null->key = NULL;
- tree->null->user_data = NULL;
- tree->size = 0;
- tree->null->left = tree->null->right = tree->null->parent = tree->null;
- tree->null->color = PJ_RBCOLOR_BLACK;
- tree->comp = comp;
-}
-
-PJ_DEF(pj_rbtree_node*) pj_rbtree_first( pj_rbtree *tree )
-{
- register pj_rbtree_node *node = tree->root;
- register pj_rbtree_node *null = tree->null;
-
- PJ_CHECK_STACK();
-
- while (node->left != null)
- node = node->left;
- return node != null ? node : NULL;
-}
-
-PJ_DEF(pj_rbtree_node*) pj_rbtree_last( pj_rbtree *tree )
-{
- register pj_rbtree_node *node = tree->root;
- register pj_rbtree_node *null = tree->null;
-
- PJ_CHECK_STACK();
-
- while (node->right != null)
- node = node->right;
- return node != null ? node : NULL;
-}
-
-PJ_DEF(pj_rbtree_node*) pj_rbtree_next( pj_rbtree *tree,
- register pj_rbtree_node *node )
-{
- register pj_rbtree_node *null = tree->null;
-
- PJ_CHECK_STACK();
-
- if (node->right != null) {
- for (node=node->right; node->left!=null; node = node->left)
- /* void */;
- } else {
- register pj_rbtree_node *temp = node->parent;
- while (temp!=null && temp->right==node) {
- node = temp;
- temp = temp->parent;
- }
- node = temp;
- }
- return node != null ? node : NULL;
-}
-
-PJ_DEF(pj_rbtree_node*) pj_rbtree_prev( pj_rbtree *tree,
- register pj_rbtree_node *node )
-{
- register pj_rbtree_node *null = tree->null;
-
- PJ_CHECK_STACK();
-
- if (node->left != null) {
- for (node=node->left; node->right!=null; node=node->right)
- /* void */;
- } else {
- register pj_rbtree_node *temp = node->parent;
- while (temp!=null && temp->left==node) {
- node = temp;
- temp = temp->parent;
- }
- node = temp;
- }
- return node != null ? node : NULL;
-}
-
-PJ_DEF(int) pj_rbtree_insert( pj_rbtree *tree,
- pj_rbtree_node *element )
-{
- int rv = 0;
- pj_rbtree_node *node, *parent = tree->null,
- *null = tree->null;
- pj_rbtree_comp *comp = tree->comp;
-
- PJ_CHECK_STACK();
-
- node = tree->root;
- while (node != null) {
- rv = (*comp)(element->key, node->key);
- if (rv == 0) {
- /* found match, i.e. entry with equal key already exist */
- return -1;
- }
- parent = node;
- node = rv < 0 ? node->left : node->right;
- }
-
- element->color = PJ_RBCOLOR_RED;
- element->left = element->right = null;
-
- node = element;
- if (parent != null) {
- node->parent = parent;
- if (rv < 0)
- parent->left = node;
- else
- parent->right = node;
- insert_fixup( tree, node);
- } else {
- tree->root = node;
- node->parent = null;
- node->color = PJ_RBCOLOR_BLACK;
- }
-
- ++tree->size;
- return 0;
-}
-
-
-PJ_DEF(pj_rbtree_node*) pj_rbtree_find( pj_rbtree *tree,
- const void *key )
-{
- int rv;
- pj_rbtree_node *node = tree->root;
- pj_rbtree_node *null = tree->null;
- pj_rbtree_comp *comp = tree->comp;
-
- while (node != null) {
- rv = (*comp)(key, node->key);
- if (rv == 0)
- return node;
- node = rv < 0 ? node->left : node->right;
- }
- return node != null ? node : NULL;
-}
-
-PJ_DEF(pj_rbtree_node*) pj_rbtree_erase( pj_rbtree *tree,
- pj_rbtree_node *node )
-{
- pj_rbtree_node *succ;
- pj_rbtree_node *null = tree->null;
- pj_rbtree_node *child;
- pj_rbtree_node *parent;
-
- PJ_CHECK_STACK();
-
- if (node->left == null || node->right == null) {
- succ = node;
- } else {
- for (succ=node->right; succ->left!=null; succ=succ->left)
- /* void */;
- }
-
- child = succ->left != null ? succ->left : succ->right;
- parent = succ->parent;
- child->parent = parent;
-
- if (parent != null) {
- if (parent->left == succ)
- parent->left = child;
- else
- parent->right = child;
- } else
- tree->root = child;
-
- if (succ != node) {
- succ->parent = node->parent;
- succ->left = node->left;
- succ->right = node->right;
- succ->color = node->color;
-
- parent = node->parent;
- if (parent != null) {
- if (parent->left==node)
- parent->left=succ;
- else
- parent->right=succ;
- }
- if (node->left != null)
- node->left->parent = succ;;
- if (node->right != null)
- node->right->parent = succ;
-
- if (tree->root == node)
- tree->root = succ;
- }
-
- if (succ->color == PJ_RBCOLOR_BLACK) {
- if (child != null)
- delete_fixup(tree, child);
- tree->null->color = PJ_RBCOLOR_BLACK;
- }
-
- --tree->size;
- return node;
-}
-
-
-PJ_DEF(unsigned) pj_rbtree_max_height( pj_rbtree *tree,
- pj_rbtree_node *node )
-{
- unsigned l, r;
-
- PJ_CHECK_STACK();
-
- if (node==NULL)
- node = tree->root;
-
- l = node->left != tree->null ? pj_rbtree_max_height(tree,node->left)+1 : 0;
- r = node->right != tree->null ? pj_rbtree_max_height(tree,node->right)+1 : 0;
- return l > r ? l : r;
-}
-
-PJ_DEF(unsigned) pj_rbtree_min_height( pj_rbtree *tree,
- pj_rbtree_node *node )
-{
- unsigned l, r;
-
- PJ_CHECK_STACK();
-
- if (node==NULL)
- node=tree->root;
-
- l = (node->left != tree->null) ? pj_rbtree_max_height(tree,node->left)+1 : 0;
- r = (node->right != tree->null) ? pj_rbtree_max_height(tree,node->right)+1 : 0;
- return l > r ? r : l;
-}
-
-
+/* $Id$
+ */
+/*
+ * PJLIB - PJ Foundation Library
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ * Benny Prijono <bennylp@bulukucing.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#include <pj/rbtree.h>
+#include <pj/os.h>
+
+static void left_rotate( pj_rbtree *tree, pj_rbtree_node *node )
+{
+ pj_rbtree_node *rnode, *parent;
+
+ PJ_CHECK_STACK();
+
+ rnode = node->right;
+ if (rnode == tree->null)
+ return;
+
+ node->right = rnode->left;
+ if (rnode->left != tree->null)
+ rnode->left->parent = node;
+ parent = node->parent;
+ rnode->parent = parent;
+ if (parent != tree->null) {
+ if (parent->left == node)
+ parent->left = rnode;
+ else
+ parent->right = rnode;
+ } else {
+ tree->root = rnode;
+ }
+ rnode->left = node;
+ node->parent = rnode;
+}
+
+static void right_rotate( pj_rbtree *tree, pj_rbtree_node *node )
+{
+ pj_rbtree_node *lnode, *parent;
+
+ PJ_CHECK_STACK();
+
+ lnode = node->left;
+ if (lnode == tree->null)
+ return;
+
+ node->left = lnode->right;
+ if (lnode->right != tree->null)
+ lnode->right->parent = node;
+ parent = node->parent;
+ lnode->parent = parent;
+
+ if (parent != tree->null) {
+ if (parent->left == node)
+ parent->left = lnode;
+ else
+ parent->right = lnode;
+ } else {
+ tree->root = lnode;
+ }
+ lnode->right = node;
+ node->parent = lnode;
+}
+
+static void insert_fixup( pj_rbtree *tree, pj_rbtree_node *node )
+{
+ pj_rbtree_node *temp, *parent;
+
+ PJ_CHECK_STACK();
+
+ while (node != tree->root && node->parent->color == PJ_RBCOLOR_RED) {
+ parent = node->parent;
+ if (parent == parent->parent->left) {
+ temp = parent->parent->right;
+ if (temp->color == PJ_RBCOLOR_RED) {
+ temp->color = PJ_RBCOLOR_BLACK;
+ node = parent;
+ node->color = PJ_RBCOLOR_BLACK;
+ node = node->parent;
+ node->color = PJ_RBCOLOR_RED;
+ } else {
+ if (node == parent->right) {
+ node = parent;
+ left_rotate(tree, node);
+ }
+ temp = node->parent;
+ temp->color = PJ_RBCOLOR_BLACK;
+ temp = temp->parent;
+ temp->color = PJ_RBCOLOR_RED;
+ right_rotate( tree, temp);
+ }
+ } else {
+ temp = parent->parent->left;
+ if (temp->color == PJ_RBCOLOR_RED) {
+ temp->color = PJ_RBCOLOR_BLACK;
+ node = parent;
+ node->color = PJ_RBCOLOR_BLACK;
+ node = node->parent;
+ node->color = PJ_RBCOLOR_RED;
+ } else {
+ if (node == parent->left) {
+ node = parent;
+ right_rotate(tree, node);
+ }
+ temp = node->parent;
+ temp->color = PJ_RBCOLOR_BLACK;
+ temp = temp->parent;
+ temp->color = PJ_RBCOLOR_RED;
+ left_rotate(tree, temp);
+ }
+ }
+ }
+
+ tree->root->color = PJ_RBCOLOR_BLACK;
+}
+
+
+static void delete_fixup( pj_rbtree *tree, pj_rbtree_node *node )
+{
+ pj_rbtree_node *temp;
+
+ PJ_CHECK_STACK();
+
+ while (node != tree->root && node->color == PJ_RBCOLOR_BLACK) {
+ if (node->parent->left == node) {
+ temp = node->parent->right;
+ if (temp->color == PJ_RBCOLOR_RED) {
+ temp->color = PJ_RBCOLOR_BLACK;
+ node->parent->color = PJ_RBCOLOR_RED;
+ left_rotate(tree, node->parent);
+ temp = node->parent->right;
+ }
+ if (temp->left->color == PJ_RBCOLOR_BLACK &&
+ temp->right->color == PJ_RBCOLOR_BLACK)
+ {
+ temp->color = PJ_RBCOLOR_RED;
+ node = node->parent;
+ } else {
+ if (temp->right->color == PJ_RBCOLOR_BLACK) {
+ temp->left->color = PJ_RBCOLOR_BLACK;
+ temp->color = PJ_RBCOLOR_RED;
+ right_rotate( tree, temp);
+ temp = node->parent->right;
+ }
+ temp->color = node->parent->color;
+ temp->right->color = PJ_RBCOLOR_BLACK;
+ node->parent->color = PJ_RBCOLOR_BLACK;
+ left_rotate(tree, node->parent);
+ node = tree->root;
+ }
+ } else {
+ temp = node->parent->left;
+ if (temp->color == PJ_RBCOLOR_RED) {
+ temp->color = PJ_RBCOLOR_BLACK;
+ node->parent->color = PJ_RBCOLOR_RED;
+ right_rotate( tree, node->parent);
+ temp = node->parent->left;
+ }
+ if (temp->right->color == PJ_RBCOLOR_BLACK &&
+ temp->left->color == PJ_RBCOLOR_BLACK)
+ {
+ temp->color = PJ_RBCOLOR_RED;
+ node = node->parent;
+ } else {
+ if (temp->left->color == PJ_RBCOLOR_BLACK) {
+ temp->right->color = PJ_RBCOLOR_BLACK;
+ temp->color = PJ_RBCOLOR_RED;
+ left_rotate( tree, temp);
+ temp = node->parent->left;
+ }
+ temp->color = node->parent->color;
+ node->parent->color = PJ_RBCOLOR_BLACK;
+ temp->left->color = PJ_RBCOLOR_BLACK;
+ right_rotate(tree, node->parent);
+ node = tree->root;
+ }
+ }
+ }
+
+ node->color = PJ_RBCOLOR_BLACK;
+}
+
+
+PJ_DEF(void) pj_rbtree_init( pj_rbtree *tree, pj_rbtree_comp *comp )
+{
+ PJ_CHECK_STACK();
+
+ tree->null = tree->root = &tree->null_node;
+ tree->null->key = NULL;
+ tree->null->user_data = NULL;
+ tree->size = 0;
+ tree->null->left = tree->null->right = tree->null->parent = tree->null;
+ tree->null->color = PJ_RBCOLOR_BLACK;
+ tree->comp = comp;
+}
+
+PJ_DEF(pj_rbtree_node*) pj_rbtree_first( pj_rbtree *tree )
+{
+ register pj_rbtree_node *node = tree->root;
+ register pj_rbtree_node *null = tree->null;
+
+ PJ_CHECK_STACK();
+
+ while (node->left != null)
+ node = node->left;
+ return node != null ? node : NULL;
+}
+
+PJ_DEF(pj_rbtree_node*) pj_rbtree_last( pj_rbtree *tree )
+{
+ register pj_rbtree_node *node = tree->root;
+ register pj_rbtree_node *null = tree->null;
+
+ PJ_CHECK_STACK();
+
+ while (node->right != null)
+ node = node->right;
+ return node != null ? node : NULL;
+}
+
+PJ_DEF(pj_rbtree_node*) pj_rbtree_next( pj_rbtree *tree,
+ register pj_rbtree_node *node )
+{
+ register pj_rbtree_node *null = tree->null;
+
+ PJ_CHECK_STACK();
+
+ if (node->right != null) {
+ for (node=node->right; node->left!=null; node = node->left)
+ /* void */;
+ } else {
+ register pj_rbtree_node *temp = node->parent;
+ while (temp!=null && temp->right==node) {
+ node = temp;
+ temp = temp->parent;
+ }
+ node = temp;
+ }
+ return node != null ? node : NULL;
+}
+
+PJ_DEF(pj_rbtree_node*) pj_rbtree_prev( pj_rbtree *tree,
+ register pj_rbtree_node *node )
+{
+ register pj_rbtree_node *null = tree->null;
+
+ PJ_CHECK_STACK();
+
+ if (node->left != null) {
+ for (node=node->left; node->right!=null; node=node->right)
+ /* void */;
+ } else {
+ register pj_rbtree_node *temp = node->parent;
+ while (temp!=null && temp->left==node) {
+ node = temp;
+ temp = temp->parent;
+ }
+ node = temp;
+ }
+ return node != null ? node : NULL;
+}
+
+PJ_DEF(int) pj_rbtree_insert( pj_rbtree *tree,
+ pj_rbtree_node *element )
+{
+ int rv = 0;
+ pj_rbtree_node *node, *parent = tree->null,
+ *null = tree->null;
+ pj_rbtree_comp *comp = tree->comp;
+
+ PJ_CHECK_STACK();
+
+ node = tree->root;
+ while (node != null) {
+ rv = (*comp)(element->key, node->key);
+ if (rv == 0) {
+ /* found match, i.e. entry with equal key already exist */
+ return -1;
+ }
+ parent = node;
+ node = rv < 0 ? node->left : node->right;
+ }
+
+ element->color = PJ_RBCOLOR_RED;
+ element->left = element->right = null;
+
+ node = element;
+ if (parent != null) {
+ node->parent = parent;
+ if (rv < 0)
+ parent->left = node;
+ else
+ parent->right = node;
+ insert_fixup( tree, node);
+ } else {
+ tree->root = node;
+ node->parent = null;
+ node->color = PJ_RBCOLOR_BLACK;
+ }
+
+ ++tree->size;
+ return 0;
+}
+
+
+PJ_DEF(pj_rbtree_node*) pj_rbtree_find( pj_rbtree *tree,
+ const void *key )
+{
+ int rv;
+ pj_rbtree_node *node = tree->root;
+ pj_rbtree_node *null = tree->null;
+ pj_rbtree_comp *comp = tree->comp;
+
+ while (node != null) {
+ rv = (*comp)(key, node->key);
+ if (rv == 0)
+ return node;
+ node = rv < 0 ? node->left : node->right;
+ }
+ return node != null ? node : NULL;
+}
+
+PJ_DEF(pj_rbtree_node*) pj_rbtree_erase( pj_rbtree *tree,
+ pj_rbtree_node *node )
+{
+ pj_rbtree_node *succ;
+ pj_rbtree_node *null = tree->null;
+ pj_rbtree_node *child;
+ pj_rbtree_node *parent;
+
+ PJ_CHECK_STACK();
+
+ if (node->left == null || node->right == null) {
+ succ = node;
+ } else {
+ for (succ=node->right; succ->left!=null; succ=succ->left)
+ /* void */;
+ }
+
+ child = succ->left != null ? succ->left : succ->right;
+ parent = succ->parent;
+ child->parent = parent;
+
+ if (parent != null) {
+ if (parent->left == succ)
+ parent->left = child;
+ else
+ parent->right = child;
+ } else
+ tree->root = child;
+
+ if (succ != node) {
+ succ->parent = node->parent;
+ succ->left = node->left;
+ succ->right = node->right;
+ succ->color = node->color;
+
+ parent = node->parent;
+ if (parent != null) {
+ if (parent->left==node)
+ parent->left=succ;
+ else
+ parent->right=succ;
+ }
+ if (node->left != null)
+ node->left->parent = succ;;
+ if (node->right != null)
+ node->right->parent = succ;
+
+ if (tree->root == node)
+ tree->root = succ;
+ }
+
+ if (succ->color == PJ_RBCOLOR_BLACK) {
+ if (child != null)
+ delete_fixup(tree, child);
+ tree->null->color = PJ_RBCOLOR_BLACK;
+ }
+
+ --tree->size;
+ return node;
+}
+
+
+PJ_DEF(unsigned) pj_rbtree_max_height( pj_rbtree *tree,
+ pj_rbtree_node *node )
+{
+ unsigned l, r;
+
+ PJ_CHECK_STACK();
+
+ if (node==NULL)
+ node = tree->root;
+
+ l = node->left != tree->null ? pj_rbtree_max_height(tree,node->left)+1 : 0;
+ r = node->right != tree->null ? pj_rbtree_max_height(tree,node->right)+1 : 0;
+ return l > r ? l : r;
+}
+
+PJ_DEF(unsigned) pj_rbtree_min_height( pj_rbtree *tree,
+ pj_rbtree_node *node )
+{
+ unsigned l, r;
+
+ PJ_CHECK_STACK();
+
+ if (node==NULL)
+ node=tree->root;
+
+ l = (node->left != tree->null) ? pj_rbtree_max_height(tree,node->left)+1 : 0;
+ r = (node->right != tree->null) ? pj_rbtree_max_height(tree,node->right)+1 : 0;
+ return l > r ? r : l;
+}
+
+
diff --git a/pjlib/src/pj/sock_bsd.c b/pjlib/src/pj/sock_bsd.c
index 8f1fa1d..1904a2e 100644
--- a/pjlib/src/pj/sock_bsd.c
+++ b/pjlib/src/pj/sock_bsd.c
@@ -1,568 +1,589 @@
-/* $Id$
- */
-#include <pj/sock.h>
-#include <pj/os.h>
-#include <pj/assert.h>
-#include <pj/string.h>
-#include <pj/compat/socket.h>
-#include <pj/addr_resolv.h>
-#include <pj/errno.h>
-
-/*
- * Address families conversion.
- * The values here are indexed based on pj_addr_family-0xFF00.
- */
-const pj_uint16_t PJ_AF_UNIX = AF_UNIX;
-const pj_uint16_t PJ_AF_INET = AF_INET;
-const pj_uint16_t PJ_AF_INET6 = AF_INET6;
-#ifdef AF_PACKET
-const pj_uint16_t PJ_AF_PACKET = AF_PACKET;
-#else
-const pj_uint16_t PJ_AF_PACKET = 0xFFFF;
-#endif
-#ifdef AF_IRDA
-const pj_uint16_t PJ_AF_IRDA = AF_IRDA;
-#else
-const pj_uint16_t PJ_AF_IRDA = 0xFFFF;
-#endif
-
-/*
- * Socket types conversion.
- * The values here are indexed based on pj_sock_type-0xFF00
- */
-const pj_uint16_t PJ_SOCK_STREAM = SOCK_STREAM;
-const pj_uint16_t PJ_SOCK_DGRAM = SOCK_DGRAM;
-const pj_uint16_t PJ_SOCK_RAW = SOCK_RAW;
-const pj_uint16_t PJ_SOCK_RDM = SOCK_RDM;
-
-/*
- * Socket level values.
- */
-const pj_uint16_t PJ_SOL_SOCKET = SOL_SOCKET;
-#ifdef SOL_IP
-const pj_uint16_t PJ_SOL_IP = SOL_IP;
-#else
-const pj_uint16_t PJ_SOL_IP = 0xFFFF;
-#endif /* SOL_IP */
-#if defined(SOL_TCP)
-const pj_uint16_t PJ_SOL_TCP = SOL_TCP;
-#elif defined(IPPROTO_TCP)
-const pj_uint16_t PJ_SOL_TCP = IPPROTO_TCP;
-#endif /* SOL_TCP */
-#ifdef SOL_UDP
-const pj_uint16_t PJ_SOL_UDP = SOL_UDP;
-#else
-const pj_uint16_t PJ_SOL_UDP = 0xFFFF;
-#endif
-#ifdef SOL_IPV6
-const pj_uint16_t PJ_SOL_IPV6 = SOL_IPV6;
-#else
-const pj_uint16_t PJ_SOL_IPV6 = 0xFFFF;
-#endif
+/* $Id$
+ */
+/*
+ * PJLIB - PJ Foundation Library
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ * Benny Prijono <bennylp@bulukucing.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#include <pj/sock.h>
+#include <pj/os.h>
+#include <pj/assert.h>
+#include <pj/string.h>
+#include <pj/compat/socket.h>
+#include <pj/addr_resolv.h>
+#include <pj/errno.h>
-/* optname values. */
+/*
+ * Address families conversion.
+ * The values here are indexed based on pj_addr_family-0xFF00.
+ */
+const pj_uint16_t PJ_AF_UNIX = AF_UNIX;
+const pj_uint16_t PJ_AF_INET = AF_INET;
+const pj_uint16_t PJ_AF_INET6 = AF_INET6;
+#ifdef AF_PACKET
+const pj_uint16_t PJ_AF_PACKET = AF_PACKET;
+#else
+const pj_uint16_t PJ_AF_PACKET = 0xFFFF;
+#endif
+#ifdef AF_IRDA
+const pj_uint16_t PJ_AF_IRDA = AF_IRDA;
+#else
+const pj_uint16_t PJ_AF_IRDA = 0xFFFF;
+#endif
+
+/*
+ * Socket types conversion.
+ * The values here are indexed based on pj_sock_type-0xFF00
+ */
+const pj_uint16_t PJ_SOCK_STREAM = SOCK_STREAM;
+const pj_uint16_t PJ_SOCK_DGRAM = SOCK_DGRAM;
+const pj_uint16_t PJ_SOCK_RAW = SOCK_RAW;
+const pj_uint16_t PJ_SOCK_RDM = SOCK_RDM;
+
+/*
+ * Socket level values.
+ */
+const pj_uint16_t PJ_SOL_SOCKET = SOL_SOCKET;
+#ifdef SOL_IP
+const pj_uint16_t PJ_SOL_IP = SOL_IP;
+#else
+const pj_uint16_t PJ_SOL_IP = 0xFFFF;
+#endif /* SOL_IP */
+#if defined(SOL_TCP)
+const pj_uint16_t PJ_SOL_TCP = SOL_TCP;
+#elif defined(IPPROTO_TCP)
+const pj_uint16_t PJ_SOL_TCP = IPPROTO_TCP;
+#endif /* SOL_TCP */
+#ifdef SOL_UDP
+const pj_uint16_t PJ_SOL_UDP = SOL_UDP;
+#else
+const pj_uint16_t PJ_SOL_UDP = 0xFFFF;
+#endif
+#ifdef SOL_IPV6
+const pj_uint16_t PJ_SOL_IPV6 = SOL_IPV6;
+#else
+const pj_uint16_t PJ_SOL_IPV6 = 0xFFFF;
+#endif
+
+/* optname values. */
const pj_uint16_t PJ_SO_TYPE = SO_TYPE;
const pj_uint16_t PJ_SO_RCVBUF = SO_RCVBUF;
const pj_uint16_t PJ_SO_SNDBUF = SO_SNDBUF;
-
-/*
- * Convert 16-bit value from network byte order to host byte order.
- */
-PJ_DEF(pj_uint16_t) pj_ntohs(pj_uint16_t netshort)
-{
- return ntohs(netshort);
-}
-
-/*
- * Convert 16-bit value from host byte order to network byte order.
- */
-PJ_DEF(pj_uint16_t) pj_htons(pj_uint16_t hostshort)
-{
- return htons(hostshort);
-}
-
-/*
- * Convert 32-bit value from network byte order to host byte order.
- */
-PJ_DEF(pj_uint32_t) pj_ntohl(pj_uint32_t netlong)
-{
- return ntohl(netlong);
-}
-
-/*
- * Convert 32-bit value from host byte order to network byte order.
- */
-PJ_DEF(pj_uint32_t) pj_htonl(pj_uint32_t hostlong)
-{
- return htonl(hostlong);
-}
-
-/*
- * Convert an Internet host address given in network byte order
- * to string in standard numbers and dots notation.
- */
-PJ_DEF(char*) pj_inet_ntoa(pj_in_addr inaddr)
+
+/*
+ * Convert 16-bit value from network byte order to host byte order.
+ */
+PJ_DEF(pj_uint16_t) pj_ntohs(pj_uint16_t netshort)
+{
+ return ntohs(netshort);
+}
+
+/*
+ * Convert 16-bit value from host byte order to network byte order.
+ */
+PJ_DEF(pj_uint16_t) pj_htons(pj_uint16_t hostshort)
+{
+ return htons(hostshort);
+}
+
+/*
+ * Convert 32-bit value from network byte order to host byte order.
+ */
+PJ_DEF(pj_uint32_t) pj_ntohl(pj_uint32_t netlong)
+{
+ return ntohl(netlong);
+}
+
+/*
+ * Convert 32-bit value from host byte order to network byte order.
+ */
+PJ_DEF(pj_uint32_t) pj_htonl(pj_uint32_t hostlong)
+{
+ return htonl(hostlong);
+}
+
+/*
+ * Convert an Internet host address given in network byte order
+ * to string in standard numbers and dots notation.
+ */
+PJ_DEF(char*) pj_inet_ntoa(pj_in_addr inaddr)
{
#if !defined(PJ_LINUX) && !defined(PJ_LINUX_KERNEL)
return inet_ntoa(*(struct in_addr*)&inaddr);
-#else
- struct in_addr addr;
- addr.s_addr = inaddr.s_addr;
+#else
+ struct in_addr addr;
+ addr.s_addr = inaddr.s_addr;
return inet_ntoa(addr);
-#endif
-}
-
-/*
- * This function converts the Internet host address cp from the standard
- * numbers-and-dots notation into binary data and stores it in the structure
- * that inp points to.
- */
-PJ_DEF(int) pj_inet_aton(const pj_str_t *cp, struct pj_in_addr *inp)
-{
- char tempaddr[16];
-
- /* Initialize output with PJ_INADDR_NONE.
- * Some apps relies on this instead of the return value
- * (and anyway the return value is quite confusing!)
- */
- inp->s_addr = PJ_INADDR_NONE;
-
- /* Caution:
- * this function might be called with cp->slen >= 16
- * (i.e. when called with hostname to check if it's an IP addr).
- */
- PJ_ASSERT_RETURN(cp && cp->slen && inp, 0);
- if (cp->slen >= 16) {
- return 0;
- }
-
- pj_memcpy(tempaddr, cp->ptr, cp->slen);
- tempaddr[cp->slen] = '\0';
-
-#if defined(PJ_SOCK_HAS_INET_ATON) && PJ_SOCK_HAS_INET_ATON != 0
- return inet_aton(tempaddr, (struct in_addr*)inp);
-#else
- inp->s_addr = inet_addr(tempaddr);
- return inp->s_addr == PJ_INADDR_NONE ? 0 : 1;
-#endif
-}
-
-/*
- * Convert address string with numbers and dots to binary IP address.
- */
-PJ_DEF(pj_in_addr) pj_inet_addr(const pj_str_t *cp)
-{
- pj_in_addr addr;
-
- pj_inet_aton(cp, &addr);
- return addr;
-}
-
-/*
- * Set the IP address of an IP socket address from string address,
- * with resolving the host if necessary. The string address may be in a
- * standard numbers and dots notation or may be a hostname. If hostname
- * is specified, then the function will resolve the host into the IP
- * address.
- */
-PJ_DEF(pj_status_t) pj_sockaddr_in_set_str_addr( pj_sockaddr_in *addr,
- const pj_str_t *str_addr)
-{
- PJ_CHECK_STACK();
-
- PJ_ASSERT_RETURN(str_addr && str_addr->slen < PJ_MAX_HOSTNAME,
- (addr->sin_addr.s_addr=PJ_INADDR_NONE, PJ_EINVAL));
-
- addr->sin_family = AF_INET;
-
- if (str_addr && str_addr->slen) {
- addr->sin_addr = pj_inet_addr(str_addr);
- if (addr->sin_addr.s_addr == PJ_INADDR_NONE) {
- pj_hostent he;
- pj_status_t rc;
-
- rc = pj_gethostbyname(str_addr, &he);
- if (rc == 0) {
- addr->sin_addr.s_addr = *(pj_uint32_t*)he.h_addr;
- } else {
- addr->sin_addr.s_addr = PJ_INADDR_NONE;
- return rc;
- }
- }
-
- } else {
- addr->sin_addr.s_addr = 0;
- }
-
- return PJ_SUCCESS;
-}
-
-/*
- * Set the IP address and port of an IP socket address.
- * The string address may be in a standard numbers and dots notation or
- * may be a hostname. If hostname is specified, then the function will
- * resolve the host into the IP address.
- */
-PJ_DEF(pj_status_t) pj_sockaddr_in_init( pj_sockaddr_in *addr,
- const pj_str_t *str_addr,
- pj_uint16_t port)
-{
- PJ_ASSERT_RETURN(addr && str_addr,
- (addr->sin_addr.s_addr=PJ_INADDR_NONE, PJ_EINVAL));
-
- addr->sin_family = PJ_AF_INET;
- pj_sockaddr_in_set_port(addr, port);
- return pj_sockaddr_in_set_str_addr(addr, str_addr);
-}
-
-
-/*
- * Get hostname.
- */
-PJ_DEF(const pj_str_t*) pj_gethostname(void)
-{
- static char buf[PJ_MAX_HOSTNAME];
- static pj_str_t hostname;
-
- PJ_CHECK_STACK();
-
- if (hostname.ptr == NULL) {
- hostname.ptr = buf;
- if (gethostname(buf, sizeof(buf)) != 0) {
- hostname.ptr[0] = '\0';
- hostname.slen = 0;
- } else {
- hostname.slen = strlen(buf);
- }
- }
- return &hostname;
-}
-
-/*
- * Get first IP address associated with the hostname.
- */
-PJ_DEF(pj_in_addr) pj_gethostaddr(void)
-{
- pj_sockaddr_in addr;
- const pj_str_t *hostname = pj_gethostname();
-
- pj_sockaddr_in_set_str_addr(&addr, hostname);
- return addr.sin_addr;
-}
-
-
-#if defined(PJ_WIN32)
-/*
- * Create new socket/endpoint for communication and returns a descriptor.
- */
-PJ_DEF(pj_status_t) pj_sock_socket(int af,
- int type,
- int proto,
- pj_sock_t *sock)
-{
- PJ_CHECK_STACK();
-
- /* Sanity checks. */
- PJ_ASSERT_RETURN(sock!=NULL, PJ_EINVAL);
- PJ_ASSERT_RETURN((unsigned)PJ_INVALID_SOCKET==INVALID_SOCKET,
- (*sock=PJ_INVALID_SOCKET, PJ_EINVAL));
-
- *sock = WSASocket(af, type, proto, NULL, 0, WSA_FLAG_OVERLAPPED);
-
- if (*sock == PJ_INVALID_SOCKET)
- return PJ_RETURN_OS_ERROR(pj_get_native_netos_error());
- else
- return PJ_SUCCESS;
-}
-
-#else
-/*
- * Create new socket/endpoint for communication and returns a descriptor.
- */
-PJ_DEF(pj_status_t) pj_sock_socket(int af,
- int type,
- int proto,
- pj_sock_t *sock)
-{
-
- PJ_CHECK_STACK();
-
- /* Sanity checks. */
- PJ_ASSERT_RETURN(sock!=NULL, PJ_EINVAL);
- PJ_ASSERT_RETURN(PJ_INVALID_SOCKET==-1,
- (*sock=PJ_INVALID_SOCKET, PJ_EINVAL));
-
- *sock = socket(af, type, proto);
- if (*sock == PJ_INVALID_SOCKET)
- return PJ_RETURN_OS_ERROR(pj_get_native_netos_error());
- else
- return PJ_SUCCESS;
-}
-#endif
-
-
-/*
- * Bind socket.
- */
-PJ_DEF(pj_status_t) pj_sock_bind( pj_sock_t sock,
- const pj_sockaddr_t *addr,
- int len)
-{
- PJ_CHECK_STACK();
-
- PJ_ASSERT_RETURN(addr && len > 0, PJ_EINVAL);
-
- if (bind(sock, (struct sockaddr*)addr, len) != 0)
- return PJ_RETURN_OS_ERROR(pj_get_native_netos_error());
- else
- return PJ_SUCCESS;
-}
-
-
-/*
- * Bind socket.
- */
-PJ_DEF(pj_status_t) pj_sock_bind_in( pj_sock_t sock,
- pj_uint32_t addr32,
- pj_uint16_t port)
-{
- pj_sockaddr_in addr;
-
- PJ_CHECK_STACK();
-
- addr.sin_family = PJ_AF_INET;
- addr.sin_addr.s_addr = pj_htonl(addr32);
- addr.sin_port = pj_htons(port);
-
- return pj_sock_bind(sock, &addr, sizeof(pj_sockaddr_in));
-}
-
-
-/*
- * Close socket.
- */
-PJ_DEF(pj_status_t) pj_sock_close(pj_sock_t sock)
-{
- int rc;
-
- PJ_CHECK_STACK();
-#if defined(PJ_WIN32) && PJ_WIN32==1
- rc = closesocket(sock);
-#else
- rc = close(sock);
-#endif
-
- if (rc != 0)
- return PJ_RETURN_OS_ERROR(pj_get_native_netos_error());
- else
- return PJ_SUCCESS;
-}
-
-/*
- * Get remote's name.
- */
-PJ_DEF(pj_status_t) pj_sock_getpeername( pj_sock_t sock,
- pj_sockaddr_t *addr,
- int *namelen)
-{
- PJ_CHECK_STACK();
- if (getpeername(sock, (struct sockaddr*)addr, (socklen_t*)namelen) != 0)
- return PJ_RETURN_OS_ERROR(pj_get_native_netos_error());
- else
- return PJ_SUCCESS;
-}
-
-/*
- * Get socket name.
- */
-PJ_DEF(pj_status_t) pj_sock_getsockname( pj_sock_t sock,
- pj_sockaddr_t *addr,
- int *namelen)
-{
- PJ_CHECK_STACK();
- if (getsockname(sock, (struct sockaddr*)addr, (socklen_t*)namelen) != 0)
- return PJ_RETURN_OS_ERROR(pj_get_native_netos_error());
- else
- return PJ_SUCCESS;
-}
-
-/*
- * Send data
- */
-PJ_DEF(pj_status_t) pj_sock_send(pj_sock_t sock,
- const void *buf,
- pj_ssize_t *len,
- unsigned flags)
-{
- PJ_CHECK_STACK();
- PJ_ASSERT_RETURN(len, PJ_EINVAL);
-
- *len = send(sock, (const char*)buf, *len, flags);
-
- if (*len < 0)
- return PJ_RETURN_OS_ERROR(pj_get_native_netos_error());
- else
- return PJ_SUCCESS;
-}
-
-
-/*
- * Send data.
- */
-PJ_DEF(pj_status_t) pj_sock_sendto(pj_sock_t sock,
- const void *buf,
- pj_ssize_t *len,
- unsigned flags,
- const pj_sockaddr_t *to,
- int tolen)
-{
- PJ_CHECK_STACK();
- PJ_ASSERT_RETURN(len, PJ_EINVAL);
-
- *len = sendto(sock, (const char*)buf, *len, flags,
- (const struct sockaddr*)to, tolen);
-
- if (*len < 0)
- return PJ_RETURN_OS_ERROR(pj_get_native_netos_error());
- else
- return PJ_SUCCESS;
-}
-
-/*
- * Receive data.
- */
-PJ_DEF(pj_status_t) pj_sock_recv(pj_sock_t sock,
- void *buf,
- pj_ssize_t *len,
- unsigned flags)
-{
- PJ_CHECK_STACK();
- PJ_ASSERT_RETURN(buf && len, PJ_EINVAL);
-
- *len = recv(sock, (char*)buf, *len, flags);
-
- if (*len < 0)
- return PJ_RETURN_OS_ERROR(pj_get_native_netos_error());
- else
- return PJ_SUCCESS;
-}
-
-/*
- * Receive data.
- */
-PJ_DEF(pj_status_t) pj_sock_recvfrom(pj_sock_t sock,
- void *buf,
- pj_ssize_t *len,
- unsigned flags,
- pj_sockaddr_t *from,
- int *fromlen)
-{
- PJ_CHECK_STACK();
- PJ_ASSERT_RETURN(buf && len, PJ_EINVAL);
- PJ_ASSERT_RETURN(from && fromlen, (*len=-1, PJ_EINVAL));
-
- *len = recvfrom(sock, (char*)buf, *len, flags,
- (struct sockaddr*)from, (socklen_t*)fromlen);
-
- if (*len < 0)
- return PJ_RETURN_OS_ERROR(pj_get_native_netos_error());
- else
- return PJ_SUCCESS;
-}
-
-/*
- * Get socket option.
- */
-PJ_DEF(pj_status_t) pj_sock_getsockopt( pj_sock_t sock,
- pj_uint16_t level,
- pj_uint16_t optname,
- void *optval,
- int *optlen)
-{
- PJ_CHECK_STACK();
- PJ_ASSERT_RETURN(optval && optlen, PJ_EINVAL);
-
- if (getsockopt(sock, level, optname, (char*)optval, (socklen_t*)optlen)!=0)
- return PJ_RETURN_OS_ERROR(pj_get_native_netos_error());
- else
- return PJ_SUCCESS;
-}
-
-/*
- * Set socket option.
- */
-PJ_DEF(pj_status_t) pj_sock_setsockopt( pj_sock_t sock,
- pj_uint16_t level,
- pj_uint16_t optname,
- const void *optval,
- int optlen)
-{
- PJ_CHECK_STACK();
- if (setsockopt(sock, level, optname, (const char*)optval, optlen) != 0)
- return PJ_RETURN_OS_ERROR(pj_get_native_netos_error());
- else
- return PJ_SUCCESS;
-}
-
-/*
- * Shutdown socket.
- */
-#if PJ_HAS_TCP
-PJ_DEF(pj_status_t) pj_sock_shutdown( pj_sock_t sock,
- int how)
-{
- PJ_CHECK_STACK();
- if (shutdown(sock, how) != 0)
- return PJ_RETURN_OS_ERROR(pj_get_native_netos_error());
- else
- return PJ_SUCCESS;
-}
-
-/*
- * Start listening to incoming connections.
- */
-PJ_DEF(pj_status_t) pj_sock_listen( pj_sock_t sock,
- int backlog)
-{
- PJ_CHECK_STACK();
- if (listen(sock, backlog) != 0)
- return PJ_RETURN_OS_ERROR(pj_get_native_netos_error());
- else
- return PJ_SUCCESS;
-}
-
-/*
- * Connect socket.
- */
-PJ_DEF(pj_status_t) pj_sock_connect( pj_sock_t sock,
- const pj_sockaddr_t *addr,
- int namelen)
-{
- PJ_CHECK_STACK();
- if (connect(sock, (struct sockaddr*)addr, namelen) != 0)
- return PJ_RETURN_OS_ERROR(pj_get_native_netos_error());
- else
- return PJ_SUCCESS;
-}
-
-/*
- * Accept incoming connections
- */
-PJ_DEF(pj_status_t) pj_sock_accept( pj_sock_t serverfd,
- pj_sock_t *newsock,
- pj_sockaddr_t *addr,
- int *addrlen)
-{
- PJ_CHECK_STACK();
- PJ_ASSERT_RETURN(newsock != NULL, PJ_EINVAL);
-
- *newsock = accept(serverfd, (struct sockaddr*)addr, (socklen_t*)addrlen);
- if (*newsock==PJ_INVALID_SOCKET)
- return PJ_RETURN_OS_ERROR(pj_get_native_netos_error());
- else
- return PJ_SUCCESS;
-}
-#endif /* PJ_HAS_TCP */
-
-
+#endif
+}
+
+/*
+ * This function converts the Internet host address cp from the standard
+ * numbers-and-dots notation into binary data and stores it in the structure
+ * that inp points to.
+ */
+PJ_DEF(int) pj_inet_aton(const pj_str_t *cp, struct pj_in_addr *inp)
+{
+ char tempaddr[16];
+
+ /* Initialize output with PJ_INADDR_NONE.
+ * Some apps relies on this instead of the return value
+ * (and anyway the return value is quite confusing!)
+ */
+ inp->s_addr = PJ_INADDR_NONE;
+
+ /* Caution:
+ * this function might be called with cp->slen >= 16
+ * (i.e. when called with hostname to check if it's an IP addr).
+ */
+ PJ_ASSERT_RETURN(cp && cp->slen && inp, 0);
+ if (cp->slen >= 16) {
+ return 0;
+ }
+
+ pj_memcpy(tempaddr, cp->ptr, cp->slen);
+ tempaddr[cp->slen] = '\0';
+
+#if defined(PJ_SOCK_HAS_INET_ATON) && PJ_SOCK_HAS_INET_ATON != 0
+ return inet_aton(tempaddr, (struct in_addr*)inp);
+#else
+ inp->s_addr = inet_addr(tempaddr);
+ return inp->s_addr == PJ_INADDR_NONE ? 0 : 1;
+#endif
+}
+
+/*
+ * Convert address string with numbers and dots to binary IP address.
+ */
+PJ_DEF(pj_in_addr) pj_inet_addr(const pj_str_t *cp)
+{
+ pj_in_addr addr;
+
+ pj_inet_aton(cp, &addr);
+ return addr;
+}
+
+/*
+ * Set the IP address of an IP socket address from string address,
+ * with resolving the host if necessary. The string address may be in a
+ * standard numbers and dots notation or may be a hostname. If hostname
+ * is specified, then the function will resolve the host into the IP
+ * address.
+ */
+PJ_DEF(pj_status_t) pj_sockaddr_in_set_str_addr( pj_sockaddr_in *addr,
+ const pj_str_t *str_addr)
+{
+ PJ_CHECK_STACK();
+
+ PJ_ASSERT_RETURN(str_addr && str_addr->slen < PJ_MAX_HOSTNAME,
+ (addr->sin_addr.s_addr=PJ_INADDR_NONE, PJ_EINVAL));
+
+ addr->sin_family = AF_INET;
+
+ if (str_addr && str_addr->slen) {
+ addr->sin_addr = pj_inet_addr(str_addr);
+ if (addr->sin_addr.s_addr == PJ_INADDR_NONE) {
+ pj_hostent he;
+ pj_status_t rc;
+
+ rc = pj_gethostbyname(str_addr, &he);
+ if (rc == 0) {
+ addr->sin_addr.s_addr = *(pj_uint32_t*)he.h_addr;
+ } else {
+ addr->sin_addr.s_addr = PJ_INADDR_NONE;
+ return rc;
+ }
+ }
+
+ } else {
+ addr->sin_addr.s_addr = 0;
+ }
+
+ return PJ_SUCCESS;
+}
+
+/*
+ * Set the IP address and port of an IP socket address.
+ * The string address may be in a standard numbers and dots notation or
+ * may be a hostname. If hostname is specified, then the function will
+ * resolve the host into the IP address.
+ */
+PJ_DEF(pj_status_t) pj_sockaddr_in_init( pj_sockaddr_in *addr,
+ const pj_str_t *str_addr,
+ pj_uint16_t port)
+{
+ PJ_ASSERT_RETURN(addr && str_addr,
+ (addr->sin_addr.s_addr=PJ_INADDR_NONE, PJ_EINVAL));
+
+ addr->sin_family = PJ_AF_INET;
+ pj_sockaddr_in_set_port(addr, port);
+ return pj_sockaddr_in_set_str_addr(addr, str_addr);
+}
+
+
+/*
+ * Get hostname.
+ */
+PJ_DEF(const pj_str_t*) pj_gethostname(void)
+{
+ static char buf[PJ_MAX_HOSTNAME];
+ static pj_str_t hostname;
+
+ PJ_CHECK_STACK();
+
+ if (hostname.ptr == NULL) {
+ hostname.ptr = buf;
+ if (gethostname(buf, sizeof(buf)) != 0) {
+ hostname.ptr[0] = '\0';
+ hostname.slen = 0;
+ } else {
+ hostname.slen = strlen(buf);
+ }
+ }
+ return &hostname;
+}
+
+/*
+ * Get first IP address associated with the hostname.
+ */
+PJ_DEF(pj_in_addr) pj_gethostaddr(void)
+{
+ pj_sockaddr_in addr;
+ const pj_str_t *hostname = pj_gethostname();
+
+ pj_sockaddr_in_set_str_addr(&addr, hostname);
+ return addr.sin_addr;
+}
+
+
+#if defined(PJ_WIN32)
+/*
+ * Create new socket/endpoint for communication and returns a descriptor.
+ */
+PJ_DEF(pj_status_t) pj_sock_socket(int af,
+ int type,
+ int proto,
+ pj_sock_t *sock)
+{
+ PJ_CHECK_STACK();
+
+ /* Sanity checks. */
+ PJ_ASSERT_RETURN(sock!=NULL, PJ_EINVAL);
+ PJ_ASSERT_RETURN((unsigned)PJ_INVALID_SOCKET==INVALID_SOCKET,
+ (*sock=PJ_INVALID_SOCKET, PJ_EINVAL));
+
+ *sock = WSASocket(af, type, proto, NULL, 0, WSA_FLAG_OVERLAPPED);
+
+ if (*sock == PJ_INVALID_SOCKET)
+ return PJ_RETURN_OS_ERROR(pj_get_native_netos_error());
+ else
+ return PJ_SUCCESS;
+}
+
+#else
+/*
+ * Create new socket/endpoint for communication and returns a descriptor.
+ */
+PJ_DEF(pj_status_t) pj_sock_socket(int af,
+ int type,
+ int proto,
+ pj_sock_t *sock)
+{
+
+ PJ_CHECK_STACK();
+
+ /* Sanity checks. */
+ PJ_ASSERT_RETURN(sock!=NULL, PJ_EINVAL);
+ PJ_ASSERT_RETURN(PJ_INVALID_SOCKET==-1,
+ (*sock=PJ_INVALID_SOCKET, PJ_EINVAL));
+
+ *sock = socket(af, type, proto);
+ if (*sock == PJ_INVALID_SOCKET)
+ return PJ_RETURN_OS_ERROR(pj_get_native_netos_error());
+ else
+ return PJ_SUCCESS;
+}
+#endif
+
+
+/*
+ * Bind socket.
+ */
+PJ_DEF(pj_status_t) pj_sock_bind( pj_sock_t sock,
+ const pj_sockaddr_t *addr,
+ int len)
+{
+ PJ_CHECK_STACK();
+
+ PJ_ASSERT_RETURN(addr && len > 0, PJ_EINVAL);
+
+ if (bind(sock, (struct sockaddr*)addr, len) != 0)
+ return PJ_RETURN_OS_ERROR(pj_get_native_netos_error());
+ else
+ return PJ_SUCCESS;
+}
+
+
+/*
+ * Bind socket.
+ */
+PJ_DEF(pj_status_t) pj_sock_bind_in( pj_sock_t sock,
+ pj_uint32_t addr32,
+ pj_uint16_t port)
+{
+ pj_sockaddr_in addr;
+
+ PJ_CHECK_STACK();
+
+ addr.sin_family = PJ_AF_INET;
+ addr.sin_addr.s_addr = pj_htonl(addr32);
+ addr.sin_port = pj_htons(port);
+
+ return pj_sock_bind(sock, &addr, sizeof(pj_sockaddr_in));
+}
+
+
+/*
+ * Close socket.
+ */
+PJ_DEF(pj_status_t) pj_sock_close(pj_sock_t sock)
+{
+ int rc;
+
+ PJ_CHECK_STACK();
+#if defined(PJ_WIN32) && PJ_WIN32==1
+ rc = closesocket(sock);
+#else
+ rc = close(sock);
+#endif
+
+ if (rc != 0)
+ return PJ_RETURN_OS_ERROR(pj_get_native_netos_error());
+ else
+ return PJ_SUCCESS;
+}
+
+/*
+ * Get remote's name.
+ */
+PJ_DEF(pj_status_t) pj_sock_getpeername( pj_sock_t sock,
+ pj_sockaddr_t *addr,
+ int *namelen)
+{
+ PJ_CHECK_STACK();
+ if (getpeername(sock, (struct sockaddr*)addr, (socklen_t*)namelen) != 0)
+ return PJ_RETURN_OS_ERROR(pj_get_native_netos_error());
+ else
+ return PJ_SUCCESS;
+}
+
+/*
+ * Get socket name.
+ */
+PJ_DEF(pj_status_t) pj_sock_getsockname( pj_sock_t sock,
+ pj_sockaddr_t *addr,
+ int *namelen)
+{
+ PJ_CHECK_STACK();
+ if (getsockname(sock, (struct sockaddr*)addr, (socklen_t*)namelen) != 0)
+ return PJ_RETURN_OS_ERROR(pj_get_native_netos_error());
+ else
+ return PJ_SUCCESS;
+}
+
+/*
+ * Send data
+ */
+PJ_DEF(pj_status_t) pj_sock_send(pj_sock_t sock,
+ const void *buf,
+ pj_ssize_t *len,
+ unsigned flags)
+{
+ PJ_CHECK_STACK();
+ PJ_ASSERT_RETURN(len, PJ_EINVAL);
+
+ *len = send(sock, (const char*)buf, *len, flags);
+
+ if (*len < 0)
+ return PJ_RETURN_OS_ERROR(pj_get_native_netos_error());
+ else
+ return PJ_SUCCESS;
+}
+
+
+/*
+ * Send data.
+ */
+PJ_DEF(pj_status_t) pj_sock_sendto(pj_sock_t sock,
+ const void *buf,
+ pj_ssize_t *len,
+ unsigned flags,
+ const pj_sockaddr_t *to,
+ int tolen)
+{
+ PJ_CHECK_STACK();
+ PJ_ASSERT_RETURN(len, PJ_EINVAL);
+
+ *len = sendto(sock, (const char*)buf, *len, flags,
+ (const struct sockaddr*)to, tolen);
+
+ if (*len < 0)
+ return PJ_RETURN_OS_ERROR(pj_get_native_netos_error());
+ else
+ return PJ_SUCCESS;
+}
+
+/*
+ * Receive data.
+ */
+PJ_DEF(pj_status_t) pj_sock_recv(pj_sock_t sock,
+ void *buf,
+ pj_ssize_t *len,
+ unsigned flags)
+{
+ PJ_CHECK_STACK();
+ PJ_ASSERT_RETURN(buf && len, PJ_EINVAL);
+
+ *len = recv(sock, (char*)buf, *len, flags);
+
+ if (*len < 0)
+ return PJ_RETURN_OS_ERROR(pj_get_native_netos_error());
+ else
+ return PJ_SUCCESS;
+}
+
+/*
+ * Receive data.
+ */
+PJ_DEF(pj_status_t) pj_sock_recvfrom(pj_sock_t sock,
+ void *buf,
+ pj_ssize_t *len,
+ unsigned flags,
+ pj_sockaddr_t *from,
+ int *fromlen)
+{
+ PJ_CHECK_STACK();
+ PJ_ASSERT_RETURN(buf && len, PJ_EINVAL);
+ PJ_ASSERT_RETURN(from && fromlen, (*len=-1, PJ_EINVAL));
+
+ *len = recvfrom(sock, (char*)buf, *len, flags,
+ (struct sockaddr*)from, (socklen_t*)fromlen);
+
+ if (*len < 0)
+ return PJ_RETURN_OS_ERROR(pj_get_native_netos_error());
+ else
+ return PJ_SUCCESS;
+}
+
+/*
+ * Get socket option.
+ */
+PJ_DEF(pj_status_t) pj_sock_getsockopt( pj_sock_t sock,
+ pj_uint16_t level,
+ pj_uint16_t optname,
+ void *optval,
+ int *optlen)
+{
+ PJ_CHECK_STACK();
+ PJ_ASSERT_RETURN(optval && optlen, PJ_EINVAL);
+
+ if (getsockopt(sock, level, optname, (char*)optval, (socklen_t*)optlen)!=0)
+ return PJ_RETURN_OS_ERROR(pj_get_native_netos_error());
+ else
+ return PJ_SUCCESS;
+}
+
+/*
+ * Set socket option.
+ */
+PJ_DEF(pj_status_t) pj_sock_setsockopt( pj_sock_t sock,
+ pj_uint16_t level,
+ pj_uint16_t optname,
+ const void *optval,
+ int optlen)
+{
+ PJ_CHECK_STACK();
+ if (setsockopt(sock, level, optname, (const char*)optval, optlen) != 0)
+ return PJ_RETURN_OS_ERROR(pj_get_native_netos_error());
+ else
+ return PJ_SUCCESS;
+}
+
+/*
+ * Shutdown socket.
+ */
+#if PJ_HAS_TCP
+PJ_DEF(pj_status_t) pj_sock_shutdown( pj_sock_t sock,
+ int how)
+{
+ PJ_CHECK_STACK();
+ if (shutdown(sock, how) != 0)
+ return PJ_RETURN_OS_ERROR(pj_get_native_netos_error());
+ else
+ return PJ_SUCCESS;
+}
+
+/*
+ * Start listening to incoming connections.
+ */
+PJ_DEF(pj_status_t) pj_sock_listen( pj_sock_t sock,
+ int backlog)
+{
+ PJ_CHECK_STACK();
+ if (listen(sock, backlog) != 0)
+ return PJ_RETURN_OS_ERROR(pj_get_native_netos_error());
+ else
+ return PJ_SUCCESS;
+}
+
+/*
+ * Connect socket.
+ */
+PJ_DEF(pj_status_t) pj_sock_connect( pj_sock_t sock,
+ const pj_sockaddr_t *addr,
+ int namelen)
+{
+ PJ_CHECK_STACK();
+ if (connect(sock, (struct sockaddr*)addr, namelen) != 0)
+ return PJ_RETURN_OS_ERROR(pj_get_native_netos_error());
+ else
+ return PJ_SUCCESS;
+}
+
+/*
+ * Accept incoming connections
+ */
+PJ_DEF(pj_status_t) pj_sock_accept( pj_sock_t serverfd,
+ pj_sock_t *newsock,
+ pj_sockaddr_t *addr,
+ int *addrlen)
+{
+ PJ_CHECK_STACK();
+ PJ_ASSERT_RETURN(newsock != NULL, PJ_EINVAL);
+
+ *newsock = accept(serverfd, (struct sockaddr*)addr, (socklen_t*)addrlen);
+ if (*newsock==PJ_INVALID_SOCKET)
+ return PJ_RETURN_OS_ERROR(pj_get_native_netos_error());
+ else
+ return PJ_SUCCESS;
+}
+#endif /* PJ_HAS_TCP */
+
+
diff --git a/pjlib/src/pj/sock_linux_kernel.c b/pjlib/src/pj/sock_linux_kernel.c
index d7924f9..75e272d 100644
--- a/pjlib/src/pj/sock_linux_kernel.c
+++ b/pjlib/src/pj/sock_linux_kernel.c
@@ -1,739 +1,760 @@
-/* $Id$
- *
- */
-#include <pj/sock.h>
-#include <pj/assert.h>
-#include <pj/string.h> /* pj_memcpy() */
-#include <pj/os.h> /* PJ_CHECK_STACK() */
-#include <pj/addr_resolv.h> /* pj_gethostbyname() */
-#include <pj/ctype.h>
-#include <pj/compat/sprintf.h>
-#include <pj/log.h>
-#include <pj/errno.h>
-
-/* Linux kernel specific. */
-#include <linux/socket.h>
-#include <linux/net.h>
-//#include <net/sock.h>
-#include <linux/security.h>
-#include <linux/syscalls.h> /* sys_xxx() */
-#include <asm/ioctls.h> /* FIONBIO */
-#include <linux/utsname.h> /* for pj_gethostname() */
-
-/*
- * Address families conversion.
- * The values here are indexed based on pj_addr_family-0xFF00.
- */
-const pj_uint16_t PJ_AF_UNIX = AF_UNIX;
-const pj_uint16_t PJ_AF_INET = AF_INET;
-const pj_uint16_t PJ_AF_INET6 = AF_INET6;
-#ifdef AF_PACKET
-const pj_uint16_t PJ_AF_PACKET = AF_PACKET;
-#else
-# error "AF_PACKET undeclared!"
-#endif
-#ifdef AF_IRDA
-const pj_uint16_t PJ_AF_IRDA = AF_IRDA;
-#else
-# error "AF_IRDA undeclared!"
-#endif
-
-/*
- * Socket types conversion.
- * The values here are indexed based on pj_sock_type-0xFF00
- */
-const pj_uint16_t PJ_SOCK_STREAM= SOCK_STREAM;
-const pj_uint16_t PJ_SOCK_DGRAM = SOCK_DGRAM;
-const pj_uint16_t PJ_SOCK_RAW = SOCK_RAW;
-const pj_uint16_t PJ_SOCK_RDM = SOCK_RDM;
-
-/*
- * Socket level values.
- */
-const pj_uint16_t PJ_SOL_SOCKET = SOL_SOCKET;
-#ifdef SOL_IP
-const pj_uint16_t PJ_SOL_IP = SOL_IP;
-#else
-# error "SOL_IP undeclared!"
-#endif /* SOL_IP */
-#if defined(SOL_TCP)
-const pj_uint16_t PJ_SOL_TCP = SOL_TCP;
-#else
-# error "SOL_TCP undeclared!"
-#endif /* SOL_TCP */
-#ifdef SOL_UDP
-const pj_uint16_t PJ_SOL_UDP = SOL_UDP;
-#else
-# error "SOL_UDP undeclared!"
-#endif
-#ifdef SOL_IPV6
-const pj_uint16_t PJ_SOL_IPV6 = SOL_IPV6;
-#else
-# error "SOL_IPV6 undeclared!"
-#endif
+/* $Id$
+ *
+ */
+/*
+ * PJLIB - PJ Foundation Library
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ * Benny Prijono <bennylp@bulukucing.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#include <pj/sock.h>
+#include <pj/assert.h>
+#include <pj/string.h> /* pj_memcpy() */
+#include <pj/os.h> /* PJ_CHECK_STACK() */
+#include <pj/addr_resolv.h> /* pj_gethostbyname() */
+#include <pj/ctype.h>
+#include <pj/compat/sprintf.h>
+#include <pj/log.h>
+#include <pj/errno.h>
+
+/* Linux kernel specific. */
+#include <linux/socket.h>
+#include <linux/net.h>
+//#include <net/sock.h>
+#include <linux/security.h>
+#include <linux/syscalls.h> /* sys_xxx() */
+#include <asm/ioctls.h> /* FIONBIO */
+#include <linux/utsname.h> /* for pj_gethostname() */
+
+/*
+ * Address families conversion.
+ * The values here are indexed based on pj_addr_family-0xFF00.
+ */
+const pj_uint16_t PJ_AF_UNIX = AF_UNIX;
+const pj_uint16_t PJ_AF_INET = AF_INET;
+const pj_uint16_t PJ_AF_INET6 = AF_INET6;
+#ifdef AF_PACKET
+const pj_uint16_t PJ_AF_PACKET = AF_PACKET;
+#else
+# error "AF_PACKET undeclared!"
+#endif
+#ifdef AF_IRDA
+const pj_uint16_t PJ_AF_IRDA = AF_IRDA;
+#else
+# error "AF_IRDA undeclared!"
+#endif
+
+/*
+ * Socket types conversion.
+ * The values here are indexed based on pj_sock_type-0xFF00
+ */
+const pj_uint16_t PJ_SOCK_STREAM= SOCK_STREAM;
+const pj_uint16_t PJ_SOCK_DGRAM = SOCK_DGRAM;
+const pj_uint16_t PJ_SOCK_RAW = SOCK_RAW;
+const pj_uint16_t PJ_SOCK_RDM = SOCK_RDM;
+
+/*
+ * Socket level values.
+ */
+const pj_uint16_t PJ_SOL_SOCKET = SOL_SOCKET;
+#ifdef SOL_IP
+const pj_uint16_t PJ_SOL_IP = SOL_IP;
+#else
+# error "SOL_IP undeclared!"
+#endif /* SOL_IP */
+#if defined(SOL_TCP)
+const pj_uint16_t PJ_SOL_TCP = SOL_TCP;
+#else
+# error "SOL_TCP undeclared!"
+#endif /* SOL_TCP */
+#ifdef SOL_UDP
+const pj_uint16_t PJ_SOL_UDP = SOL_UDP;
+#else
+# error "SOL_UDP undeclared!"
+#endif
+#ifdef SOL_IPV6
+const pj_uint16_t PJ_SOL_IPV6 = SOL_IPV6;
+#else
+# error "SOL_IPV6 undeclared!"
+#endif
/* optname values. */
const pj_uint16_t PJ_SO_TYPE = SO_TYPE;
const pj_uint16_t PJ_SO_RCVBUF = SO_RCVBUF;
const pj_uint16_t PJ_SO_SNDBUF = SO_SNDBUF;
-
-/*
- * Convert 16-bit value from network byte order to host byte order.
- */
-PJ_DEF(pj_uint16_t) pj_ntohs(pj_uint16_t netshort)
-{
- return ntohs(netshort);
-}
-
-/*
- * Convert 16-bit value from host byte order to network byte order.
- */
-PJ_DEF(pj_uint16_t) pj_htons(pj_uint16_t hostshort)
-{
- return htons(hostshort);
-}
-
-/*
- * Convert 32-bit value from network byte order to host byte order.
- */
-PJ_DEF(pj_uint32_t) pj_ntohl(pj_uint32_t netlong)
-{
- return ntohl(netlong);
-}
-
-/*
- * Convert 32-bit value from host byte order to network byte order.
- */
-PJ_DEF(pj_uint32_t) pj_htonl(pj_uint32_t hostlong)
-{
- return htonl(hostlong);
-}
-
-/*
- * Convert an Internet host address given in network byte order
- * to string in standard numbers and dots notation.
- */
-PJ_DEF(char*) pj_inet_ntoa(pj_in_addr in)
-{
-#define UC(b) (((int)b)&0xff)
- static char b[18];
- char *p;
-
- p = (char *)∈
- pj_snprintf(b, sizeof(b), "%d.%d.%d.%d",
- UC(p[0]), UC(p[1]), UC(p[2]), UC(p[3]));
-
- return b;
-}
-
-/*
- * This function converts the Internet host address ccp from the standard
- * numbers-and-dots notation into binary data and stores it in the structure
- * that inp points to.
- */
-PJ_DEF(int) pj_inet_aton(const pj_str_t *ccp, struct pj_in_addr *addr)
-{
- pj_uint32_t val;
- int base, n;
- char c;
- unsigned parts[4];
- unsigned *pp = parts;
- char cp_copy[18];
- char *cp = cp_copy;
-
- addr->s_addr = PJ_INADDR_NONE;
-
- if (ccp->slen > 15) return 0;
-
- pj_memcpy(cp, ccp->ptr, ccp->slen);
- cp[ccp->slen] = '\0';
-
- c = *cp;
- for (;;) {
- /*
- * Collect number up to ``.''.
- * Values are specified as for C:
- * 0x=hex, 0=octal, isdigit=decimal.
- */
- if (!pj_isdigit((int)c))
- return (0);
- val = 0; base = 10;
- if (c == '0') {
- c = *++cp;
- if (c == 'x' || c == 'X')
- base = 16, c = *++cp;
- else
- base = 8;
- }
-
- for (;;) {
- if (pj_isascii((int)c) && pj_isdigit((int)c)) {
- val = (val * base) + (c - '0');
- c = *++cp;
- } else if (base==16 && pj_isascii((int)c) && pj_isxdigit((int)c)) {
- val = (val << 4) |
- (c + 10 - (pj_islower((int)c) ? 'a' : 'A'));
- c = *++cp;
- } else
- break;
- }
-
- if (c == '.') {
- /*
- * Internet format:
- * a.b.c.d
- * a.b.c (with c treated as 16 bits)
- * a.b (with b treated as 24 bits)
- */
- if (pp >= parts + 3)
- return (0);
- *pp++ = val;
- c = *++cp;
- } else
- break;
- }
-
- /*
- * Check for trailing characters.
- */
- if (c != '\0' && (!pj_isascii((int)c) || !pj_isspace((int)c)))
- return (0);
- /*
- * Concoct the address according to
- * the number of parts specified.
- */
- n = pp - parts + 1;
- switch (n) {
- case 0:
- return (0); /* initial nondigit */
- case 1: /* a -- 32 bits */
- break;
- case 2: /* a.b -- 8.24 bits */
- if (val > 0xffffff)
- return (0);
- val |= parts[0] << 24;
- break;
- case 3: /* a.b.c -- 8.8.16 bits */
- if (val > 0xffff)
- return (0);
- val |= (parts[0] << 24) | (parts[1] << 16);
- break;
- case 4: /* a.b.c.d -- 8.8.8.8 bits */
- if (val > 0xff)
- return (0);
- val |= (parts[0] << 24) | (parts[1] << 16) | (parts[2] << 8);
- break;
- }
-
- if (addr)
- addr->s_addr = pj_htonl(val);
- return (1);
-}
-
-/*
- * Convert address string with numbers and dots to binary IP address.
- */
-PJ_DEF(pj_in_addr) pj_inet_addr(const pj_str_t *cp)
-{
- pj_in_addr addr;
- pj_inet_aton(cp, &addr);
- return addr;
-}
-
-/*
- * Set the IP address of an IP socket address from string address,
- * with resolving the host if necessary. The string address may be in a
- * standard numbers and dots notation or may be a hostname. If hostname
- * is specified, then the function will resolve the host into the IP
- * address.
- */
-PJ_DEF(pj_status_t) pj_sockaddr_in_set_str_addr( pj_sockaddr_in *addr,
- const pj_str_t *str_addr)
-{
- PJ_CHECK_STACK();
-
- pj_assert(str_addr && str_addr->slen < PJ_MAX_HOSTNAME);
-
- addr->sin_family = AF_INET;
-
- if (str_addr && str_addr->slen) {
- addr->sin_addr = pj_inet_addr(str_addr);
- if (addr->sin_addr.s_addr == PJ_INADDR_NONE) {
- pj_hostent he;
- if (pj_gethostbyname(str_addr, &he) == 0) {
- addr->sin_addr.s_addr = *(pj_uint32_t*)he.h_addr;
- } else {
- addr->sin_addr.s_addr = PJ_INADDR_NONE;
- return -1;
- }
- }
-
- } else {
- addr->sin_addr.s_addr = 0;
- }
-
- return PJ_SUCCESS;
-}
-
-/*
- * Set the IP address and port of an IP socket address.
- * The string address may be in a standard numbers and dots notation or
- * may be a hostname. If hostname is specified, then the function will
- * resolve the host into the IP address.
- */
-PJ_DEF(pj_status_t) pj_sockaddr_in_init( pj_sockaddr_in *addr,
- const pj_str_t *str_addr,
- pj_uint16_t port)
-{
- pj_assert(addr && str_addr);
-
- addr->sin_family = PJ_AF_INET;
- pj_sockaddr_in_set_port(addr, port);
- return pj_sockaddr_in_set_str_addr(addr, str_addr);
-}
-
-
-/*
- * Get hostname.
- */
-PJ_DEF(const pj_str_t*) pj_gethostname(void)
-{
- static char buf[PJ_MAX_HOSTNAME];
- static pj_str_t hostname;
-
- PJ_CHECK_STACK();
-
- if (hostname.ptr == NULL) {
- hostname.ptr = buf;
- down_read(&uts_sem);
- hostname.slen = strlen(system_utsname.nodename);
- if (hostname.slen > PJ_MAX_HOSTNAME) {
- hostname.ptr[0] = '\0';
- hostname.slen = 0;
- } else {
- pj_memcpy(hostname.ptr, system_utsname.nodename, hostname.slen);
- }
- up_read(&uts_sem);
- }
- return &hostname;
-}
-
-/*
- * Get first IP address associated with the hostname.
- */
-PJ_DEF(pj_in_addr) pj_gethostaddr(void)
-{
- pj_sockaddr_in addr;
- const pj_str_t *hostname = pj_gethostname();
-
- pj_sockaddr_in_set_str_addr(&addr, hostname);
- return addr.sin_addr;
-}
-
-
-/*
- * Create new socket/endpoint for communication and returns a descriptor.
- */
-PJ_DEF(pj_status_t) pj_sock_socket(int af, int type, int proto,
- pj_sock_t *sock_fd)
-{
- long result;
-
- PJ_CHECK_STACK();
-
- /* Sanity checks. */
- PJ_ASSERT_RETURN(PJ_INVALID_SOCKET == -1 && sock_fd != NULL, PJ_EINVAL);
-
- /* Initialize returned socket */
- *sock_fd = PJ_INVALID_SOCKET;
-
- /* Create socket. */
- result = sys_socket(af, type, proto);
- if (result < 0) {
- return PJ_RETURN_OS_ERROR((-result));
- }
-
- *sock_fd = result;
-
- return PJ_SUCCESS;
-}
-
-/*
- * Bind socket.
- */
-PJ_DEF(pj_status_t) pj_sock_bind( pj_sock_t sockfd,
- const pj_sockaddr_t *addr,
- int len)
-{
- long err;
- mm_segment_t oldfs;
-
- PJ_CHECK_STACK();
-
- PJ_ASSERT_RETURN(addr!=NULL && len >= sizeof(struct pj_sockaddr),
- PJ_EINVAL);
-
- oldfs = get_fs();
- set_fs(KERNEL_DS);
-
- err = sys_bind(sockfd, (struct sockaddr*)addr, len);
-
- set_fs(oldfs);
-
- if (err)
- return PJ_RETURN_OS_ERROR(-err);
- else
- return PJ_SUCCESS;
-}
-
-
-/*
- * Bind socket.
- */
-PJ_DEF(pj_status_t) pj_sock_bind_in( pj_sock_t sockfd,
- pj_uint32_t addr32,
- pj_uint16_t port)
-{
- pj_sockaddr_in addr;
-
- PJ_CHECK_STACK();
-
- addr.sin_family = PJ_AF_INET;
- addr.sin_addr.s_addr = pj_htonl(addr32);
- addr.sin_port = pj_htons(port);
-
- return pj_sock_bind(sockfd, &addr, sizeof(pj_sockaddr_in));
-}
-
-/*
- * Close socket.
- */
-PJ_DEF(pj_status_t) pj_sock_close(pj_sock_t sockfd)
-{
- long err;
-
- err = sys_close(sockfd);
-
- if (err != 0)
- return PJ_RETURN_OS_ERROR(-err);
- else
- return PJ_SUCCESS;
-}
-
-/*
- * Get remote's name.
- */
-PJ_DEF(pj_status_t) pj_sock_getpeername( pj_sock_t sockfd,
- pj_sockaddr_t *addr,
- int *namelen)
-{
- mm_segment_t oldfs;
- long err;
-
- PJ_CHECK_STACK();
-
- oldfs = get_fs();
- set_fs(KERNEL_DS);
-
- err = sys_getpeername( sockfd, addr, namelen);
-
- set_fs(oldfs);
-
- if (err)
- return PJ_RETURN_OS_ERROR(-err);
- else
- return PJ_SUCCESS;
-}
-
-/*
- * Get socket name.
- */
-PJ_DEF(pj_status_t) pj_sock_getsockname( pj_sock_t sockfd,
- pj_sockaddr_t *addr,
- int *namelen)
-{
- mm_segment_t oldfs;
- int err;
-
- PJ_CHECK_STACK();
-
- oldfs = get_fs();
- set_fs(KERNEL_DS);
-
- err = sys_getsockname( sockfd, addr, namelen );
-
- set_fs(oldfs);
-
- if (err)
- return PJ_RETURN_OS_ERROR(-err);
- else
- return PJ_SUCCESS;
-}
-
-/*
- * Send data
- */
-PJ_DEF(pj_status_t) pj_sock_send( pj_sock_t sockfd,
- const void *buf,
- pj_ssize_t *len,
- unsigned flags)
-{
- return pj_sock_sendto(sockfd, buf, len, flags, NULL, 0);
-}
-
-
-/*
- * Send data.
- */
-PJ_DEF(pj_status_t) pj_sock_sendto( pj_sock_t sockfd,
- const void *buff,
- pj_ssize_t *len,
- unsigned flags,
- const pj_sockaddr_t *addr,
- int addr_len)
-{
- long err;
- mm_segment_t oldfs;
-
- PJ_CHECK_STACK();
-
- oldfs = get_fs();
- set_fs(KERNEL_DS);
-
- err = *len = sys_sendto( sockfd, (void*)buff, *len, flags,
- (void*)addr, addr_len );
-
- set_fs(oldfs);
-
- if (err >= 0) {
- return PJ_SUCCESS;
- }
- else {
- return PJ_RETURN_OS_ERROR(-err);
- }
-}
-
-/*
- * Receive data.
- */
-PJ_DEF(pj_status_t) pj_sock_recv( pj_sock_t sockfd,
- void *buf,
- pj_ssize_t *len,
- unsigned flags)
-{
- return pj_sock_recvfrom(sockfd, buf, len, flags, NULL, NULL);
-}
-
-/*
- * Receive data.
- */
-PJ_DEF(pj_status_t) pj_sock_recvfrom( pj_sock_t sockfd,
- void *buff,
- pj_ssize_t *size,
- unsigned flags,
- pj_sockaddr_t *from,
- int *fromlen)
-{
- mm_segment_t oldfs;
- long err;
-
- PJ_CHECK_STACK();
-
- oldfs = get_fs();
- set_fs(KERNEL_DS);
-
- err = *size = sys_recvfrom( sockfd, buff, *size, flags, from, fromlen);
-
- set_fs(oldfs);
-
- if (err >= 0) {
- return PJ_SUCCESS;
- }
- else {
- return PJ_RETURN_OS_ERROR(-err);
- }
-}
-
-/*
- * Get socket option.
- */
-PJ_DEF(pj_status_t) pj_sock_getsockopt( pj_sock_t sockfd,
- pj_uint16_t level,
- pj_uint16_t optname,
- void *optval,
- int *optlen)
-{
- mm_segment_t oldfs;
- long err;
-
- PJ_CHECK_STACK();
-
- oldfs = get_fs();
- set_fs(KERNEL_DS);
-
- err = sys_getsockopt( sockfd, level, optname, optval, optlen);
-
- set_fs(oldfs);
-
- if (err)
- return PJ_RETURN_OS_ERROR(-err);
- else
- return PJ_SUCCESS;
-}
-
-/*
- * Set socket option.
- */
-PJ_DEF(pj_status_t) pj_sock_setsockopt( pj_sock_t sockfd,
- pj_uint16_t level,
- pj_uint16_t optname,
- const void *optval,
- int optlen)
-{
- long err;
- mm_segment_t oldfs;
-
- PJ_CHECK_STACK();
-
-
- oldfs = get_fs();
- set_fs(KERNEL_DS);
-
- err = sys_setsockopt( sockfd, level, optname, (void*)optval, optlen);
-
- set_fs(oldfs);
-
- if (err)
- return PJ_RETURN_OS_ERROR(-err);
- else
- return PJ_SUCCESS;
-}
-
-/*
- * Shutdown socket.
- */
-#if PJ_HAS_TCP
-PJ_DEF(pj_status_t) pj_sock_shutdown( pj_sock_t sockfd,
- int how)
-{
- long err;
-
- PJ_CHECK_STACK();
-
- err = sys_shutdown(sockfd, how);
-
- if (err)
- return PJ_RETURN_OS_ERROR(-err);
- else
- return PJ_SUCCESS;
-}
-
-/*
- * Start listening to incoming connections.
- */
-PJ_DEF(pj_status_t) pj_sock_listen( pj_sock_t sockfd,
- int backlog)
-{
- long err;
-
- PJ_CHECK_STACK();
-
- err = sys_listen( sockfd, backlog );
-
- if (err)
- return PJ_RETURN_OS_ERROR(-err);
- else
- return PJ_SUCCESS;
-}
-
-/*
- * Connect socket.
- */
-PJ_DEF(pj_status_t) pj_sock_connect( pj_sock_t sockfd,
- const pj_sockaddr_t *addr,
- int namelen)
-{
- long err;
- mm_segment_t oldfs;
-
- PJ_CHECK_STACK();
-
- oldfs = get_fs();
- set_fs(KERNEL_DS);
-
- err = sys_connect( sockfd, (void*)addr, namelen );
-
- set_fs(oldfs);
-
- if (err)
- return PJ_RETURN_OS_ERROR(-err);
- else
- return PJ_SUCCESS;
-}
-
-/*
- * Accept incoming connections
- */
-PJ_DEF(pj_status_t) pj_sock_accept( pj_sock_t sockfd,
- pj_sock_t *newsockfd,
- pj_sockaddr_t *addr,
- int *addrlen)
-{
- long err;
-
- PJ_CHECK_STACK();
-
- PJ_ASSERT_RETURN(newsockfd != NULL, PJ_EINVAL);
-
- err = sys_accept( sockfd, addr, addrlen);
-
- if (err < 0) {
- *newsockfd = PJ_INVALID_SOCKET;
- return PJ_RETURN_OS_ERROR(-err);
- }
- else {
- *newsockfd = err;
- return PJ_SUCCESS;
- }
-}
-#endif /* PJ_HAS_TCP */
-
-
-
-/*
- * Permission to steal inet_ntoa() and inet_aton() as long as this notice below
- * is included:
- */
-/*
- * Copyright (c) 1983, 1993
- * The Regents of the University of California. All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- * 3. All advertising materials mentioning features or use of this software
- * must display the following acknowledgement:
- * This product includes software developed by the University of
- * California, Berkeley and its contributors.
- * 4. Neither the name of the University nor the names of its contributors
- * may be used to endorse or promote products derived from this software
- * without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
- * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
- * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
- * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
- * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
- * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
- * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
- * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
- * SUCH DAMAGE.
- */
-
+
+/*
+ * Convert 16-bit value from network byte order to host byte order.
+ */
+PJ_DEF(pj_uint16_t) pj_ntohs(pj_uint16_t netshort)
+{
+ return ntohs(netshort);
+}
+
+/*
+ * Convert 16-bit value from host byte order to network byte order.
+ */
+PJ_DEF(pj_uint16_t) pj_htons(pj_uint16_t hostshort)
+{
+ return htons(hostshort);
+}
+
+/*
+ * Convert 32-bit value from network byte order to host byte order.
+ */
+PJ_DEF(pj_uint32_t) pj_ntohl(pj_uint32_t netlong)
+{
+ return ntohl(netlong);
+}
+
+/*
+ * Convert 32-bit value from host byte order to network byte order.
+ */
+PJ_DEF(pj_uint32_t) pj_htonl(pj_uint32_t hostlong)
+{
+ return htonl(hostlong);
+}
+
+/*
+ * Convert an Internet host address given in network byte order
+ * to string in standard numbers and dots notation.
+ */
+PJ_DEF(char*) pj_inet_ntoa(pj_in_addr in)
+{
+#define UC(b) (((int)b)&0xff)
+ static char b[18];
+ char *p;
+
+ p = (char *)∈
+ pj_snprintf(b, sizeof(b), "%d.%d.%d.%d",
+ UC(p[0]), UC(p[1]), UC(p[2]), UC(p[3]));
+
+ return b;
+}
+
+/*
+ * This function converts the Internet host address ccp from the standard
+ * numbers-and-dots notation into binary data and stores it in the structure
+ * that inp points to.
+ */
+PJ_DEF(int) pj_inet_aton(const pj_str_t *ccp, struct pj_in_addr *addr)
+{
+ pj_uint32_t val;
+ int base, n;
+ char c;
+ unsigned parts[4];
+ unsigned *pp = parts;
+ char cp_copy[18];
+ char *cp = cp_copy;
+
+ addr->s_addr = PJ_INADDR_NONE;
+
+ if (ccp->slen > 15) return 0;
+
+ pj_memcpy(cp, ccp->ptr, ccp->slen);
+ cp[ccp->slen] = '\0';
+
+ c = *cp;
+ for (;;) {
+ /*
+ * Collect number up to ``.''.
+ * Values are specified as for C:
+ * 0x=hex, 0=octal, isdigit=decimal.
+ */
+ if (!pj_isdigit((int)c))
+ return (0);
+ val = 0; base = 10;
+ if (c == '0') {
+ c = *++cp;
+ if (c == 'x' || c == 'X')
+ base = 16, c = *++cp;
+ else
+ base = 8;
+ }
+
+ for (;;) {
+ if (pj_isascii((int)c) && pj_isdigit((int)c)) {
+ val = (val * base) + (c - '0');
+ c = *++cp;
+ } else if (base==16 && pj_isascii((int)c) && pj_isxdigit((int)c)) {
+ val = (val << 4) |
+ (c + 10 - (pj_islower((int)c) ? 'a' : 'A'));
+ c = *++cp;
+ } else
+ break;
+ }
+
+ if (c == '.') {
+ /*
+ * Internet format:
+ * a.b.c.d
+ * a.b.c (with c treated as 16 bits)
+ * a.b (with b treated as 24 bits)
+ */
+ if (pp >= parts + 3)
+ return (0);
+ *pp++ = val;
+ c = *++cp;
+ } else
+ break;
+ }
+
+ /*
+ * Check for trailing characters.
+ */
+ if (c != '\0' && (!pj_isascii((int)c) || !pj_isspace((int)c)))
+ return (0);
+ /*
+ * Concoct the address according to
+ * the number of parts specified.
+ */
+ n = pp - parts + 1;
+ switch (n) {
+ case 0:
+ return (0); /* initial nondigit */
+ case 1: /* a -- 32 bits */
+ break;
+ case 2: /* a.b -- 8.24 bits */
+ if (val > 0xffffff)
+ return (0);
+ val |= parts[0] << 24;
+ break;
+ case 3: /* a.b.c -- 8.8.16 bits */
+ if (val > 0xffff)
+ return (0);
+ val |= (parts[0] << 24) | (parts[1] << 16);
+ break;
+ case 4: /* a.b.c.d -- 8.8.8.8 bits */
+ if (val > 0xff)
+ return (0);
+ val |= (parts[0] << 24) | (parts[1] << 16) | (parts[2] << 8);
+ break;
+ }
+
+ if (addr)
+ addr->s_addr = pj_htonl(val);
+ return (1);
+}
+
+/*
+ * Convert address string with numbers and dots to binary IP address.
+ */
+PJ_DEF(pj_in_addr) pj_inet_addr(const pj_str_t *cp)
+{
+ pj_in_addr addr;
+ pj_inet_aton(cp, &addr);
+ return addr;
+}
+
+/*
+ * Set the IP address of an IP socket address from string address,
+ * with resolving the host if necessary. The string address may be in a
+ * standard numbers and dots notation or may be a hostname. If hostname
+ * is specified, then the function will resolve the host into the IP
+ * address.
+ */
+PJ_DEF(pj_status_t) pj_sockaddr_in_set_str_addr( pj_sockaddr_in *addr,
+ const pj_str_t *str_addr)
+{
+ PJ_CHECK_STACK();
+
+ pj_assert(str_addr && str_addr->slen < PJ_MAX_HOSTNAME);
+
+ addr->sin_family = AF_INET;
+
+ if (str_addr && str_addr->slen) {
+ addr->sin_addr = pj_inet_addr(str_addr);
+ if (addr->sin_addr.s_addr == PJ_INADDR_NONE) {
+ pj_hostent he;
+ if (pj_gethostbyname(str_addr, &he) == 0) {
+ addr->sin_addr.s_addr = *(pj_uint32_t*)he.h_addr;
+ } else {
+ addr->sin_addr.s_addr = PJ_INADDR_NONE;
+ return -1;
+ }
+ }
+
+ } else {
+ addr->sin_addr.s_addr = 0;
+ }
+
+ return PJ_SUCCESS;
+}
+
+/*
+ * Set the IP address and port of an IP socket address.
+ * The string address may be in a standard numbers and dots notation or
+ * may be a hostname. If hostname is specified, then the function will
+ * resolve the host into the IP address.
+ */
+PJ_DEF(pj_status_t) pj_sockaddr_in_init( pj_sockaddr_in *addr,
+ const pj_str_t *str_addr,
+ pj_uint16_t port)
+{
+ pj_assert(addr && str_addr);
+
+ addr->sin_family = PJ_AF_INET;
+ pj_sockaddr_in_set_port(addr, port);
+ return pj_sockaddr_in_set_str_addr(addr, str_addr);
+}
+
+
+/*
+ * Get hostname.
+ */
+PJ_DEF(const pj_str_t*) pj_gethostname(void)
+{
+ static char buf[PJ_MAX_HOSTNAME];
+ static pj_str_t hostname;
+
+ PJ_CHECK_STACK();
+
+ if (hostname.ptr == NULL) {
+ hostname.ptr = buf;
+ down_read(&uts_sem);
+ hostname.slen = strlen(system_utsname.nodename);
+ if (hostname.slen > PJ_MAX_HOSTNAME) {
+ hostname.ptr[0] = '\0';
+ hostname.slen = 0;
+ } else {
+ pj_memcpy(hostname.ptr, system_utsname.nodename, hostname.slen);
+ }
+ up_read(&uts_sem);
+ }
+ return &hostname;
+}
+
+/*
+ * Get first IP address associated with the hostname.
+ */
+PJ_DEF(pj_in_addr) pj_gethostaddr(void)
+{
+ pj_sockaddr_in addr;
+ const pj_str_t *hostname = pj_gethostname();
+
+ pj_sockaddr_in_set_str_addr(&addr, hostname);
+ return addr.sin_addr;
+}
+
+
+/*
+ * Create new socket/endpoint for communication and returns a descriptor.
+ */
+PJ_DEF(pj_status_t) pj_sock_socket(int af, int type, int proto,
+ pj_sock_t *sock_fd)
+{
+ long result;
+
+ PJ_CHECK_STACK();
+
+ /* Sanity checks. */
+ PJ_ASSERT_RETURN(PJ_INVALID_SOCKET == -1 && sock_fd != NULL, PJ_EINVAL);
+
+ /* Initialize returned socket */
+ *sock_fd = PJ_INVALID_SOCKET;
+
+ /* Create socket. */
+ result = sys_socket(af, type, proto);
+ if (result < 0) {
+ return PJ_RETURN_OS_ERROR((-result));
+ }
+
+ *sock_fd = result;
+
+ return PJ_SUCCESS;
+}
+
+/*
+ * Bind socket.
+ */
+PJ_DEF(pj_status_t) pj_sock_bind( pj_sock_t sockfd,
+ const pj_sockaddr_t *addr,
+ int len)
+{
+ long err;
+ mm_segment_t oldfs;
+
+ PJ_CHECK_STACK();
+
+ PJ_ASSERT_RETURN(addr!=NULL && len >= sizeof(struct pj_sockaddr),
+ PJ_EINVAL);
+
+ oldfs = get_fs();
+ set_fs(KERNEL_DS);
+
+ err = sys_bind(sockfd, (struct sockaddr*)addr, len);
+
+ set_fs(oldfs);
+
+ if (err)
+ return PJ_RETURN_OS_ERROR(-err);
+ else
+ return PJ_SUCCESS;
+}
+
+
+/*
+ * Bind socket.
+ */
+PJ_DEF(pj_status_t) pj_sock_bind_in( pj_sock_t sockfd,
+ pj_uint32_t addr32,
+ pj_uint16_t port)
+{
+ pj_sockaddr_in addr;
+
+ PJ_CHECK_STACK();
+
+ addr.sin_family = PJ_AF_INET;
+ addr.sin_addr.s_addr = pj_htonl(addr32);
+ addr.sin_port = pj_htons(port);
+
+ return pj_sock_bind(sockfd, &addr, sizeof(pj_sockaddr_in));
+}
+
+/*
+ * Close socket.
+ */
+PJ_DEF(pj_status_t) pj_sock_close(pj_sock_t sockfd)
+{
+ long err;
+
+ err = sys_close(sockfd);
+
+ if (err != 0)
+ return PJ_RETURN_OS_ERROR(-err);
+ else
+ return PJ_SUCCESS;
+}
+
+/*
+ * Get remote's name.
+ */
+PJ_DEF(pj_status_t) pj_sock_getpeername( pj_sock_t sockfd,
+ pj_sockaddr_t *addr,
+ int *namelen)
+{
+ mm_segment_t oldfs;
+ long err;
+
+ PJ_CHECK_STACK();
+
+ oldfs = get_fs();
+ set_fs(KERNEL_DS);
+
+ err = sys_getpeername( sockfd, addr, namelen);
+
+ set_fs(oldfs);
+
+ if (err)
+ return PJ_RETURN_OS_ERROR(-err);
+ else
+ return PJ_SUCCESS;
+}
+
+/*
+ * Get socket name.
+ */
+PJ_DEF(pj_status_t) pj_sock_getsockname( pj_sock_t sockfd,
+ pj_sockaddr_t *addr,
+ int *namelen)
+{
+ mm_segment_t oldfs;
+ int err;
+
+ PJ_CHECK_STACK();
+
+ oldfs = get_fs();
+ set_fs(KERNEL_DS);
+
+ err = sys_getsockname( sockfd, addr, namelen );
+
+ set_fs(oldfs);
+
+ if (err)
+ return PJ_RETURN_OS_ERROR(-err);
+ else
+ return PJ_SUCCESS;
+}
+
+/*
+ * Send data
+ */
+PJ_DEF(pj_status_t) pj_sock_send( pj_sock_t sockfd,
+ const void *buf,
+ pj_ssize_t *len,
+ unsigned flags)
+{
+ return pj_sock_sendto(sockfd, buf, len, flags, NULL, 0);
+}
+
+
+/*
+ * Send data.
+ */
+PJ_DEF(pj_status_t) pj_sock_sendto( pj_sock_t sockfd,
+ const void *buff,
+ pj_ssize_t *len,
+ unsigned flags,
+ const pj_sockaddr_t *addr,
+ int addr_len)
+{
+ long err;
+ mm_segment_t oldfs;
+
+ PJ_CHECK_STACK();
+
+ oldfs = get_fs();
+ set_fs(KERNEL_DS);
+
+ err = *len = sys_sendto( sockfd, (void*)buff, *len, flags,
+ (void*)addr, addr_len );
+
+ set_fs(oldfs);
+
+ if (err >= 0) {
+ return PJ_SUCCESS;
+ }
+ else {
+ return PJ_RETURN_OS_ERROR(-err);
+ }
+}
+
+/*
+ * Receive data.
+ */
+PJ_DEF(pj_status_t) pj_sock_recv( pj_sock_t sockfd,
+ void *buf,
+ pj_ssize_t *len,
+ unsigned flags)
+{
+ return pj_sock_recvfrom(sockfd, buf, len, flags, NULL, NULL);
+}
+
+/*
+ * Receive data.
+ */
+PJ_DEF(pj_status_t) pj_sock_recvfrom( pj_sock_t sockfd,
+ void *buff,
+ pj_ssize_t *size,
+ unsigned flags,
+ pj_sockaddr_t *from,
+ int *fromlen)
+{
+ mm_segment_t oldfs;
+ long err;
+
+ PJ_CHECK_STACK();
+
+ oldfs = get_fs();
+ set_fs(KERNEL_DS);
+
+ err = *size = sys_recvfrom( sockfd, buff, *size, flags, from, fromlen);
+
+ set_fs(oldfs);
+
+ if (err >= 0) {
+ return PJ_SUCCESS;
+ }
+ else {
+ return PJ_RETURN_OS_ERROR(-err);
+ }
+}
+
+/*
+ * Get socket option.
+ */
+PJ_DEF(pj_status_t) pj_sock_getsockopt( pj_sock_t sockfd,
+ pj_uint16_t level,
+ pj_uint16_t optname,
+ void *optval,
+ int *optlen)
+{
+ mm_segment_t oldfs;
+ long err;
+
+ PJ_CHECK_STACK();
+
+ oldfs = get_fs();
+ set_fs(KERNEL_DS);
+
+ err = sys_getsockopt( sockfd, level, optname, optval, optlen);
+
+ set_fs(oldfs);
+
+ if (err)
+ return PJ_RETURN_OS_ERROR(-err);
+ else
+ return PJ_SUCCESS;
+}
+
+/*
+ * Set socket option.
+ */
+PJ_DEF(pj_status_t) pj_sock_setsockopt( pj_sock_t sockfd,
+ pj_uint16_t level,
+ pj_uint16_t optname,
+ const void *optval,
+ int optlen)
+{
+ long err;
+ mm_segment_t oldfs;
+
+ PJ_CHECK_STACK();
+
+
+ oldfs = get_fs();
+ set_fs(KERNEL_DS);
+
+ err = sys_setsockopt( sockfd, level, optname, (void*)optval, optlen);
+
+ set_fs(oldfs);
+
+ if (err)
+ return PJ_RETURN_OS_ERROR(-err);
+ else
+ return PJ_SUCCESS;
+}
+
+/*
+ * Shutdown socket.
+ */
+#if PJ_HAS_TCP
+PJ_DEF(pj_status_t) pj_sock_shutdown( pj_sock_t sockfd,
+ int how)
+{
+ long err;
+
+ PJ_CHECK_STACK();
+
+ err = sys_shutdown(sockfd, how);
+
+ if (err)
+ return PJ_RETURN_OS_ERROR(-err);
+ else
+ return PJ_SUCCESS;
+}
+
+/*
+ * Start listening to incoming connections.
+ */
+PJ_DEF(pj_status_t) pj_sock_listen( pj_sock_t sockfd,
+ int backlog)
+{
+ long err;
+
+ PJ_CHECK_STACK();
+
+ err = sys_listen( sockfd, backlog );
+
+ if (err)
+ return PJ_RETURN_OS_ERROR(-err);
+ else
+ return PJ_SUCCESS;
+}
+
+/*
+ * Connect socket.
+ */
+PJ_DEF(pj_status_t) pj_sock_connect( pj_sock_t sockfd,
+ const pj_sockaddr_t *addr,
+ int namelen)
+{
+ long err;
+ mm_segment_t oldfs;
+
+ PJ_CHECK_STACK();
+
+ oldfs = get_fs();
+ set_fs(KERNEL_DS);
+
+ err = sys_connect( sockfd, (void*)addr, namelen );
+
+ set_fs(oldfs);
+
+ if (err)
+ return PJ_RETURN_OS_ERROR(-err);
+ else
+ return PJ_SUCCESS;
+}
+
+/*
+ * Accept incoming connections
+ */
+PJ_DEF(pj_status_t) pj_sock_accept( pj_sock_t sockfd,
+ pj_sock_t *newsockfd,
+ pj_sockaddr_t *addr,
+ int *addrlen)
+{
+ long err;
+
+ PJ_CHECK_STACK();
+
+ PJ_ASSERT_RETURN(newsockfd != NULL, PJ_EINVAL);
+
+ err = sys_accept( sockfd, addr, addrlen);
+
+ if (err < 0) {
+ *newsockfd = PJ_INVALID_SOCKET;
+ return PJ_RETURN_OS_ERROR(-err);
+ }
+ else {
+ *newsockfd = err;
+ return PJ_SUCCESS;
+ }
+}
+#endif /* PJ_HAS_TCP */
+
+
+
+/*
+ * Permission to steal inet_ntoa() and inet_aton() as long as this notice below
+ * is included:
+ */
+/*
+ * Copyright (c) 1983, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
diff --git a/pjlib/src/pj/sock_select.c b/pjlib/src/pj/sock_select.c
index 5b3d0a6..57d2f8e 100644
--- a/pjlib/src/pj/sock_select.c
+++ b/pjlib/src/pj/sock_select.c
@@ -1,92 +1,113 @@
-/* $Id$
- */
-#include <pj/sock_select.h>
-#include <pj/compat/socket.h>
-#include <pj/os.h>
-#include <pj/assert.h>
-#include <pj/errno.h>
-
-#if defined(PJ_HAS_STRING_H) && PJ_HAS_STRING_H!=0
-# include <string.h>
-#endif
-
-#ifdef _MSC_VER
-# pragma warning(disable: 4018) // Signed/unsigned mismatch in FD_*
-# pragma warning(disable: 4389) // Signed/unsigned mismatch in FD_*
-#endif
-
-#define PART_FDSET(ps) ((fd_set*)&ps->data[1])
-#define PART_FDSET_OR_NULL(ps) (ps ? PART_FDSET(ps) : NULL)
-#define PART_COUNT(ps) (ps->data[0])
-
-PJ_DEF(void) PJ_FD_ZERO(pj_fd_set_t *fdsetp)
-{
- PJ_CHECK_STACK();
- pj_assert(sizeof(pj_fd_set_t)-sizeof(pj_sock_t) >= sizeof(fd_set));
-
- FD_ZERO(PART_FDSET(fdsetp));
- PART_COUNT(fdsetp) = 0;
-}
-
-
-PJ_DEF(void) PJ_FD_SET(pj_sock_t fd, pj_fd_set_t *fdsetp)
-{
- PJ_CHECK_STACK();
- pj_assert(sizeof(pj_fd_set_t)-sizeof(pj_sock_t) >= sizeof(fd_set));
-
- if (!PJ_FD_ISSET(fd, fdsetp))
- ++PART_COUNT(fdsetp);
- FD_SET(fd, PART_FDSET(fdsetp));
-}
-
-
-PJ_DEF(void) PJ_FD_CLR(pj_sock_t fd, pj_fd_set_t *fdsetp)
-{
- PJ_CHECK_STACK();
- pj_assert(sizeof(pj_fd_set_t)-sizeof(pj_sock_t) >= sizeof(fd_set));
-
- if (PJ_FD_ISSET(fd, fdsetp))
- --PART_COUNT(fdsetp);
- FD_CLR(fd, PART_FDSET(fdsetp));
-}
-
-
-PJ_DEF(pj_bool_t) PJ_FD_ISSET(pj_sock_t fd, const pj_fd_set_t *fdsetp)
-{
- PJ_CHECK_STACK();
- PJ_ASSERT_RETURN(sizeof(pj_fd_set_t)-sizeof(pj_sock_t) >= sizeof(fd_set),
- 0);
-
- return FD_ISSET(fd, PART_FDSET(fdsetp));
-}
-
-PJ_DEF(pj_size_t) PJ_FD_COUNT(const pj_fd_set_t *fdsetp)
-{
- return PART_COUNT(fdsetp);
-}
-
-PJ_DEF(int) pj_sock_select( int n,
- pj_fd_set_t *readfds,
- pj_fd_set_t *writefds,
- pj_fd_set_t *exceptfds,
- const pj_time_val *timeout)
-{
- struct timeval os_timeout, *p_os_timeout;
-
- PJ_CHECK_STACK();
-
- PJ_ASSERT_RETURN(sizeof(pj_fd_set_t)-sizeof(pj_sock_t) >= sizeof(fd_set),
- PJ_EBUG);
-
- if (timeout) {
- os_timeout.tv_sec = timeout->sec;
- os_timeout.tv_usec = timeout->msec * 1000;
- p_os_timeout = &os_timeout;
- } else {
- p_os_timeout = NULL;
- }
-
- return select(n, PART_FDSET_OR_NULL(readfds), PART_FDSET_OR_NULL(writefds),
- PART_FDSET_OR_NULL(exceptfds), p_os_timeout);
-}
-
+/* $Id$
+ */
+/*
+ * PJLIB - PJ Foundation Library
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ * Benny Prijono <bennylp@bulukucing.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#include <pj/sock_select.h>
+#include <pj/compat/socket.h>
+#include <pj/os.h>
+#include <pj/assert.h>
+#include <pj/errno.h>
+
+#if defined(PJ_HAS_STRING_H) && PJ_HAS_STRING_H!=0
+# include <string.h>
+#endif
+
+#ifdef _MSC_VER
+# pragma warning(disable: 4018) // Signed/unsigned mismatch in FD_*
+# pragma warning(disable: 4389) // Signed/unsigned mismatch in FD_*
+#endif
+
+#define PART_FDSET(ps) ((fd_set*)&ps->data[1])
+#define PART_FDSET_OR_NULL(ps) (ps ? PART_FDSET(ps) : NULL)
+#define PART_COUNT(ps) (ps->data[0])
+
+PJ_DEF(void) PJ_FD_ZERO(pj_fd_set_t *fdsetp)
+{
+ PJ_CHECK_STACK();
+ pj_assert(sizeof(pj_fd_set_t)-sizeof(pj_sock_t) >= sizeof(fd_set));
+
+ FD_ZERO(PART_FDSET(fdsetp));
+ PART_COUNT(fdsetp) = 0;
+}
+
+
+PJ_DEF(void) PJ_FD_SET(pj_sock_t fd, pj_fd_set_t *fdsetp)
+{
+ PJ_CHECK_STACK();
+ pj_assert(sizeof(pj_fd_set_t)-sizeof(pj_sock_t) >= sizeof(fd_set));
+
+ if (!PJ_FD_ISSET(fd, fdsetp))
+ ++PART_COUNT(fdsetp);
+ FD_SET(fd, PART_FDSET(fdsetp));
+}
+
+
+PJ_DEF(void) PJ_FD_CLR(pj_sock_t fd, pj_fd_set_t *fdsetp)
+{
+ PJ_CHECK_STACK();
+ pj_assert(sizeof(pj_fd_set_t)-sizeof(pj_sock_t) >= sizeof(fd_set));
+
+ if (PJ_FD_ISSET(fd, fdsetp))
+ --PART_COUNT(fdsetp);
+ FD_CLR(fd, PART_FDSET(fdsetp));
+}
+
+
+PJ_DEF(pj_bool_t) PJ_FD_ISSET(pj_sock_t fd, const pj_fd_set_t *fdsetp)
+{
+ PJ_CHECK_STACK();
+ PJ_ASSERT_RETURN(sizeof(pj_fd_set_t)-sizeof(pj_sock_t) >= sizeof(fd_set),
+ 0);
+
+ return FD_ISSET(fd, PART_FDSET(fdsetp));
+}
+
+PJ_DEF(pj_size_t) PJ_FD_COUNT(const pj_fd_set_t *fdsetp)
+{
+ return PART_COUNT(fdsetp);
+}
+
+PJ_DEF(int) pj_sock_select( int n,
+ pj_fd_set_t *readfds,
+ pj_fd_set_t *writefds,
+ pj_fd_set_t *exceptfds,
+ const pj_time_val *timeout)
+{
+ struct timeval os_timeout, *p_os_timeout;
+
+ PJ_CHECK_STACK();
+
+ PJ_ASSERT_RETURN(sizeof(pj_fd_set_t)-sizeof(pj_sock_t) >= sizeof(fd_set),
+ PJ_EBUG);
+
+ if (timeout) {
+ os_timeout.tv_sec = timeout->sec;
+ os_timeout.tv_usec = timeout->msec * 1000;
+ p_os_timeout = &os_timeout;
+ } else {
+ p_os_timeout = NULL;
+ }
+
+ return select(n, PART_FDSET_OR_NULL(readfds), PART_FDSET_OR_NULL(writefds),
+ PART_FDSET_OR_NULL(exceptfds), p_os_timeout);
+}
+
diff --git a/pjlib/src/pj/string.c b/pjlib/src/pj/string.c
index 73313f9..9c87d15 100644
--- a/pjlib/src/pj/string.c
+++ b/pjlib/src/pj/string.c
@@ -1,115 +1,136 @@
-/* $Id$
- */
-#include <pj/string.h>
-#include <pj/pool.h>
-#include <pj/ctype.h>
-#include <pj/rand.h>
-#include <pj/os.h>
-
-#if PJ_FUNCTIONS_ARE_INLINED==0
-# include <pj/string_i.h>
-#endif
-
-
-static char hex[] = {'0', '1', '2', '3', '4', '5', '6', '7',
- '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' };
-
-PJ_DEF(pj_str_t*) pj_strltrim( pj_str_t *str )
-{
- register char *p = str->ptr;
- while (pj_isspace(*p))
- ++p;
- str->slen -= (p - str->ptr);
- str->ptr = p;
- return str;
-}
-
-PJ_DEF(pj_str_t*) pj_strrtrim( pj_str_t *str )
-{
- char *end = str->ptr + str->slen;
- register char *p = end - 1;
- while (p >= str->ptr && pj_isspace(*p))
- --p;
- str->slen -= ((end - p) - 1);
- return str;
-}
-
-PJ_INLINE(void) pj_val_to_hex_digit(unsigned value, char *p)
-{
- *p++ = hex[ (value & 0xF0) >> 4 ];
- *p++ = hex[ (value & 0x0F) ];
-}
-
-PJ_DEF(char*) pj_create_random_string(char *str, pj_size_t len)
-{
- unsigned i;
- char *p = str;
-
- PJ_CHECK_STACK();
-
- for (i=0; i<len/8; ++i) {
- unsigned val = pj_rand();
- pj_val_to_hex_digit( (val & 0xFF000000) >> 24, p+0 );
- pj_val_to_hex_digit( (val & 0x00FF0000) >> 16, p+2 );
- pj_val_to_hex_digit( (val & 0x0000FF00) >> 8, p+4 );
- pj_val_to_hex_digit( (val & 0x000000FF) >> 0, p+6 );
- p += 8;
- }
- for (i=i * 8; i<len; ++i) {
- *p++ = hex[ pj_rand() & 0x0F ];
- }
- return str;
-}
-
-
-PJ_DEF(unsigned long) pj_strtoul(const pj_str_t *str)
-{
- unsigned long value;
- unsigned i;
-
- PJ_CHECK_STACK();
-
- value = 0;
- for (i=0; i<(unsigned)str->slen; ++i) {
- value = value * 10 + (str->ptr[i] - '0');
- }
- return value;
-}
-
-PJ_DEF(int) pj_utoa(unsigned long val, char *buf)
-{
- return pj_utoa_pad(val, buf, 0, 0);
-}
-
-PJ_DEF(int) pj_utoa_pad( unsigned long val, char *buf, int min_dig, int pad)
-{
- char *p;
- int len;
-
- PJ_CHECK_STACK();
-
- p = buf;
- do {
- unsigned long digval = (unsigned long) (val % 10);
- val /= 10;
- *p++ = (char) (digval + '0');
- } while (val > 0);
-
- len = p-buf;
- while (len < min_dig) {
- *p++ = (char)pad;
- ++len;
- }
- *p-- = '\0';
-
- do {
- char temp = *p;
- *p = *buf;
- *buf = temp;
- --p;
- ++buf;
- } while (buf < p);
-
- return len;
-}
-
+/* $Id$
+ */
+/*
+ * PJLIB - PJ Foundation Library
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ * Benny Prijono <bennylp@bulukucing.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; 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/ctype.h>
+#include <pj/rand.h>
+#include <pj/os.h>
+
+#if PJ_FUNCTIONS_ARE_INLINED==0
+# include <pj/string_i.h>
+#endif
+
+
+static char hex[] = {'0', '1', '2', '3', '4', '5', '6', '7',
+ '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' };
+
+PJ_DEF(pj_str_t*) pj_strltrim( pj_str_t *str )
+{
+ register char *p = str->ptr;
+ while (pj_isspace(*p))
+ ++p;
+ str->slen -= (p - str->ptr);
+ str->ptr = p;
+ return str;
+}
+
+PJ_DEF(pj_str_t*) pj_strrtrim( pj_str_t *str )
+{
+ char *end = str->ptr + str->slen;
+ register char *p = end - 1;
+ while (p >= str->ptr && pj_isspace(*p))
+ --p;
+ str->slen -= ((end - p) - 1);
+ return str;
+}
+
+PJ_INLINE(void) pj_val_to_hex_digit(unsigned value, char *p)
+{
+ *p++ = hex[ (value & 0xF0) >> 4 ];
+ *p++ = hex[ (value & 0x0F) ];
+}
+
+PJ_DEF(char*) pj_create_random_string(char *str, pj_size_t len)
+{
+ unsigned i;
+ char *p = str;
+
+ PJ_CHECK_STACK();
+
+ for (i=0; i<len/8; ++i) {
+ unsigned val = pj_rand();
+ pj_val_to_hex_digit( (val & 0xFF000000) >> 24, p+0 );
+ pj_val_to_hex_digit( (val & 0x00FF0000) >> 16, p+2 );
+ pj_val_to_hex_digit( (val & 0x0000FF00) >> 8, p+4 );
+ pj_val_to_hex_digit( (val & 0x000000FF) >> 0, p+6 );
+ p += 8;
+ }
+ for (i=i * 8; i<len; ++i) {
+ *p++ = hex[ pj_rand() & 0x0F ];
+ }
+ return str;
+}
+
+
+PJ_DEF(unsigned long) pj_strtoul(const pj_str_t *str)
+{
+ unsigned long value;
+ unsigned i;
+
+ PJ_CHECK_STACK();
+
+ value = 0;
+ for (i=0; i<(unsigned)str->slen; ++i) {
+ value = value * 10 + (str->ptr[i] - '0');
+ }
+ return value;
+}
+
+PJ_DEF(int) pj_utoa(unsigned long val, char *buf)
+{
+ return pj_utoa_pad(val, buf, 0, 0);
+}
+
+PJ_DEF(int) pj_utoa_pad( unsigned long val, char *buf, int min_dig, int pad)
+{
+ char *p;
+ int len;
+
+ PJ_CHECK_STACK();
+
+ p = buf;
+ do {
+ unsigned long digval = (unsigned long) (val % 10);
+ val /= 10;
+ *p++ = (char) (digval + '0');
+ } while (val > 0);
+
+ len = p-buf;
+ while (len < min_dig) {
+ *p++ = (char)pad;
+ ++len;
+ }
+ *p-- = '\0';
+
+ do {
+ char temp = *p;
+ *p = *buf;
+ *buf = temp;
+ --p;
+ ++buf;
+ } while (buf < p);
+
+ return len;
+}
+
diff --git a/pjlib/src/pj/symbols.c b/pjlib/src/pj/symbols.c
index fc434ad..8d0ba55 100644
--- a/pjlib/src/pj/symbols.c
+++ b/pjlib/src/pj/symbols.c
@@ -1,330 +1,351 @@
-/* $Id$
- */
-#include <pjlib.h>
-
-/*
- * addr_resolv.h
- */
-PJ_EXPORT_SYMBOL(pj_gethostbyname)
-
-/*
- * array.h
- */
-PJ_EXPORT_SYMBOL(pj_array_insert)
-PJ_EXPORT_SYMBOL(pj_array_erase)
-PJ_EXPORT_SYMBOL(pj_array_find)
-
-/*
- * config.h
- */
-PJ_EXPORT_SYMBOL(pj_dump_config)
-
-/*
- * errno.h
- */
-PJ_EXPORT_SYMBOL(pj_get_os_error)
-PJ_EXPORT_SYMBOL(pj_set_os_error)
-PJ_EXPORT_SYMBOL(pj_get_netos_error)
-PJ_EXPORT_SYMBOL(pj_set_netos_error)
-PJ_EXPORT_SYMBOL(pj_strerror)
-
-/*
- * except.h
- */
-PJ_EXPORT_SYMBOL(pj_throw_exception_)
-PJ_EXPORT_SYMBOL(pj_push_exception_handler_)
-PJ_EXPORT_SYMBOL(pj_pop_exception_handler_)
-PJ_EXPORT_SYMBOL(pj_setjmp)
-PJ_EXPORT_SYMBOL(pj_longjmp)
-PJ_EXPORT_SYMBOL(pj_exception_id_alloc)
-PJ_EXPORT_SYMBOL(pj_exception_id_free)
-PJ_EXPORT_SYMBOL(pj_exception_id_name)
-
-
-/*
- * fifobuf.h
- */
-PJ_EXPORT_SYMBOL(pj_fifobuf_init)
-PJ_EXPORT_SYMBOL(pj_fifobuf_max_size)
-PJ_EXPORT_SYMBOL(pj_fifobuf_alloc)
-PJ_EXPORT_SYMBOL(pj_fifobuf_unalloc)
-PJ_EXPORT_SYMBOL(pj_fifobuf_free)
-
-/*
- * guid.h
- */
-PJ_EXPORT_SYMBOL(pj_generate_unique_string)
-PJ_EXPORT_SYMBOL(pj_create_unique_string)
-
-/*
- * hash.h
- */
-PJ_EXPORT_SYMBOL(pj_hash_calc)
-PJ_EXPORT_SYMBOL(pj_hash_create)
-PJ_EXPORT_SYMBOL(pj_hash_get)
-PJ_EXPORT_SYMBOL(pj_hash_set)
-PJ_EXPORT_SYMBOL(pj_hash_count)
-PJ_EXPORT_SYMBOL(pj_hash_first)
-PJ_EXPORT_SYMBOL(pj_hash_next)
-PJ_EXPORT_SYMBOL(pj_hash_this)
-
-/*
- * ioqueue.h
- */
-PJ_EXPORT_SYMBOL(pj_ioqueue_create)
-PJ_EXPORT_SYMBOL(pj_ioqueue_destroy)
-PJ_EXPORT_SYMBOL(pj_ioqueue_set_lock)
-PJ_EXPORT_SYMBOL(pj_ioqueue_register_sock)
-PJ_EXPORT_SYMBOL(pj_ioqueue_unregister)
-PJ_EXPORT_SYMBOL(pj_ioqueue_get_user_data)
-PJ_EXPORT_SYMBOL(pj_ioqueue_poll)
-PJ_EXPORT_SYMBOL(pj_ioqueue_read)
-PJ_EXPORT_SYMBOL(pj_ioqueue_recv)
-PJ_EXPORT_SYMBOL(pj_ioqueue_recvfrom)
-PJ_EXPORT_SYMBOL(pj_ioqueue_write)
-PJ_EXPORT_SYMBOL(pj_ioqueue_send)
-PJ_EXPORT_SYMBOL(pj_ioqueue_sendto)
-#if defined(PJ_HAS_TCP) && PJ_HAS_TCP != 0
-PJ_EXPORT_SYMBOL(pj_ioqueue_accept)
-PJ_EXPORT_SYMBOL(pj_ioqueue_connect)
-#endif
-
-/*
- * list.h
- */
-PJ_EXPORT_SYMBOL(pj_list_insert_before)
-PJ_EXPORT_SYMBOL(pj_list_insert_nodes_before)
-PJ_EXPORT_SYMBOL(pj_list_insert_after)
-PJ_EXPORT_SYMBOL(pj_list_insert_nodes_after)
-PJ_EXPORT_SYMBOL(pj_list_merge_first)
-PJ_EXPORT_SYMBOL(pj_list_merge_last)
-PJ_EXPORT_SYMBOL(pj_list_erase)
-PJ_EXPORT_SYMBOL(pj_list_find_node)
-PJ_EXPORT_SYMBOL(pj_list_search)
-
-
-/*
- * log.h
- */
-PJ_EXPORT_SYMBOL(pj_log_write)
-#if PJ_LOG_MAX_LEVEL >= 1
-PJ_EXPORT_SYMBOL(pj_log_set_log_func)
-PJ_EXPORT_SYMBOL(pj_log_get_log_func)
-PJ_EXPORT_SYMBOL(pj_log_set_level)
-PJ_EXPORT_SYMBOL(pj_log_get_level)
-PJ_EXPORT_SYMBOL(pj_log_set_decor)
-PJ_EXPORT_SYMBOL(pj_log_get_decor)
-PJ_EXPORT_SYMBOL(pj_log_1)
-#endif
-#if PJ_LOG_MAX_LEVEL >= 2
-PJ_EXPORT_SYMBOL(pj_log_2)
-#endif
-#if PJ_LOG_MAX_LEVEL >= 3
-PJ_EXPORT_SYMBOL(pj_log_3)
-#endif
-#if PJ_LOG_MAX_LEVEL >= 4
-PJ_EXPORT_SYMBOL(pj_log_4)
-#endif
-#if PJ_LOG_MAX_LEVEL >= 5
-PJ_EXPORT_SYMBOL(pj_log_5)
-#endif
-#if PJ_LOG_MAX_LEVEL >= 6
-PJ_EXPORT_SYMBOL(pj_log_6)
-#endif
-
-/*
- * os.h
- */
-PJ_EXPORT_SYMBOL(pj_init)
-PJ_EXPORT_SYMBOL(pj_getpid)
-PJ_EXPORT_SYMBOL(pj_thread_register)
-PJ_EXPORT_SYMBOL(pj_thread_create)
-PJ_EXPORT_SYMBOL(pj_thread_get_name)
-PJ_EXPORT_SYMBOL(pj_thread_resume)
-PJ_EXPORT_SYMBOL(pj_thread_this)
-PJ_EXPORT_SYMBOL(pj_thread_join)
-PJ_EXPORT_SYMBOL(pj_thread_destroy)
-PJ_EXPORT_SYMBOL(pj_thread_sleep)
-#if defined(PJ_OS_HAS_CHECK_STACK) && PJ_OS_HAS_CHECK_STACK != 0
-PJ_EXPORT_SYMBOL(pj_thread_check_stack)
-PJ_EXPORT_SYMBOL(pj_thread_get_stack_max_usage)
-PJ_EXPORT_SYMBOL(pj_thread_get_stack_info)
-#endif
-PJ_EXPORT_SYMBOL(pj_atomic_create)
-PJ_EXPORT_SYMBOL(pj_atomic_destroy)
-PJ_EXPORT_SYMBOL(pj_atomic_set)
-PJ_EXPORT_SYMBOL(pj_atomic_get)
-PJ_EXPORT_SYMBOL(pj_atomic_inc)
-PJ_EXPORT_SYMBOL(pj_atomic_dec)
-PJ_EXPORT_SYMBOL(pj_thread_local_alloc)
-PJ_EXPORT_SYMBOL(pj_thread_local_free)
-PJ_EXPORT_SYMBOL(pj_thread_local_set)
-PJ_EXPORT_SYMBOL(pj_thread_local_get)
-PJ_EXPORT_SYMBOL(pj_enter_critical_section)
-PJ_EXPORT_SYMBOL(pj_leave_critical_section)
-PJ_EXPORT_SYMBOL(pj_mutex_create)
-PJ_EXPORT_SYMBOL(pj_mutex_lock)
-PJ_EXPORT_SYMBOL(pj_mutex_unlock)
-PJ_EXPORT_SYMBOL(pj_mutex_trylock)
-PJ_EXPORT_SYMBOL(pj_mutex_destroy)
-#if defined(PJ_DEBUG) && PJ_DEBUG != 0
-PJ_EXPORT_SYMBOL(pj_mutex_is_locked)
-#endif
-#if defined(PJ_HAS_SEMAPHORE) && PJ_HAS_SEMAPHORE != 0
-PJ_EXPORT_SYMBOL(pj_sem_create)
-PJ_EXPORT_SYMBOL(pj_sem_wait)
-PJ_EXPORT_SYMBOL(pj_sem_trywait)
-PJ_EXPORT_SYMBOL(pj_sem_post)
-PJ_EXPORT_SYMBOL(pj_sem_destroy)
-#endif
-PJ_EXPORT_SYMBOL(pj_gettimeofday)
-PJ_EXPORT_SYMBOL(pj_time_decode)
-#if defined(PJ_HAS_HIGH_RES_TIMER) && PJ_HAS_HIGH_RES_TIMER != 0
-PJ_EXPORT_SYMBOL(pj_get_timestamp)
-PJ_EXPORT_SYMBOL(pj_get_timestamp_freq)
-PJ_EXPORT_SYMBOL(pj_elapsed_time)
-PJ_EXPORT_SYMBOL(pj_elapsed_usec)
-PJ_EXPORT_SYMBOL(pj_elapsed_nanosec)
-PJ_EXPORT_SYMBOL(pj_elapsed_cycle)
-#endif
-
-
-/*
- * pool.h
- */
-PJ_EXPORT_SYMBOL(pj_pool_create)
-PJ_EXPORT_SYMBOL(pj_pool_release)
-PJ_EXPORT_SYMBOL(pj_pool_getobjname)
-PJ_EXPORT_SYMBOL(pj_pool_reset)
-PJ_EXPORT_SYMBOL(pj_pool_get_capacity)
-PJ_EXPORT_SYMBOL(pj_pool_get_used_size)
-PJ_EXPORT_SYMBOL(pj_pool_alloc)
-PJ_EXPORT_SYMBOL(pj_pool_calloc)
-PJ_EXPORT_SYMBOL(pj_pool_factory_default_policy)
-PJ_EXPORT_SYMBOL(pj_pool_create_int)
-PJ_EXPORT_SYMBOL(pj_pool_init_int)
-PJ_EXPORT_SYMBOL(pj_pool_destroy_int)
-PJ_EXPORT_SYMBOL(pj_caching_pool_init)
-PJ_EXPORT_SYMBOL(pj_caching_pool_destroy)
-
-/*
- * rand.h
- */
-PJ_EXPORT_SYMBOL(pj_rand)
-PJ_EXPORT_SYMBOL(pj_srand)
-
-/*
- * rbtree.h
- */
-PJ_EXPORT_SYMBOL(pj_rbtree_init)
-PJ_EXPORT_SYMBOL(pj_rbtree_first)
-PJ_EXPORT_SYMBOL(pj_rbtree_last)
-PJ_EXPORT_SYMBOL(pj_rbtree_next)
-PJ_EXPORT_SYMBOL(pj_rbtree_prev)
-PJ_EXPORT_SYMBOL(pj_rbtree_insert)
-PJ_EXPORT_SYMBOL(pj_rbtree_find)
-PJ_EXPORT_SYMBOL(pj_rbtree_erase)
-PJ_EXPORT_SYMBOL(pj_rbtree_max_height)
-PJ_EXPORT_SYMBOL(pj_rbtree_min_height)
-
-/*
- * sock.h
- */
-PJ_EXPORT_SYMBOL(PJ_AF_UNIX)
-PJ_EXPORT_SYMBOL(PJ_AF_INET)
-PJ_EXPORT_SYMBOL(PJ_AF_INET6)
-PJ_EXPORT_SYMBOL(PJ_AF_PACKET)
-PJ_EXPORT_SYMBOL(PJ_AF_IRDA)
-PJ_EXPORT_SYMBOL(PJ_SOCK_STREAM)
-PJ_EXPORT_SYMBOL(PJ_SOCK_DGRAM)
-PJ_EXPORT_SYMBOL(PJ_SOCK_RAW)
-PJ_EXPORT_SYMBOL(PJ_SOCK_RDM)
-PJ_EXPORT_SYMBOL(PJ_SOL_SOCKET)
-PJ_EXPORT_SYMBOL(PJ_SOL_IP)
-PJ_EXPORT_SYMBOL(PJ_SOL_TCP)
-PJ_EXPORT_SYMBOL(PJ_SOL_UDP)
-PJ_EXPORT_SYMBOL(PJ_SOL_IPV6)
-PJ_EXPORT_SYMBOL(pj_ntohs)
-PJ_EXPORT_SYMBOL(pj_htons)
-PJ_EXPORT_SYMBOL(pj_ntohl)
-PJ_EXPORT_SYMBOL(pj_htonl)
-PJ_EXPORT_SYMBOL(pj_inet_ntoa)
-PJ_EXPORT_SYMBOL(pj_inet_aton)
-PJ_EXPORT_SYMBOL(pj_inet_addr)
-PJ_EXPORT_SYMBOL(pj_sockaddr_in_set_str_addr)
-PJ_EXPORT_SYMBOL(pj_sockaddr_in_init)
-PJ_EXPORT_SYMBOL(pj_gethostname)
-PJ_EXPORT_SYMBOL(pj_gethostaddr)
-PJ_EXPORT_SYMBOL(pj_sock_socket)
-PJ_EXPORT_SYMBOL(pj_sock_close)
-PJ_EXPORT_SYMBOL(pj_sock_bind)
-PJ_EXPORT_SYMBOL(pj_sock_bind_in)
-#if defined(PJ_HAS_TCP) && PJ_HAS_TCP != 0
-PJ_EXPORT_SYMBOL(pj_sock_listen)
-PJ_EXPORT_SYMBOL(pj_sock_accept)
-PJ_EXPORT_SYMBOL(pj_sock_shutdown)
-#endif
-PJ_EXPORT_SYMBOL(pj_sock_connect)
-PJ_EXPORT_SYMBOL(pj_sock_getpeername)
-PJ_EXPORT_SYMBOL(pj_sock_getsockname)
-PJ_EXPORT_SYMBOL(pj_sock_getsockopt)
-PJ_EXPORT_SYMBOL(pj_sock_setsockopt)
-PJ_EXPORT_SYMBOL(pj_sock_recv)
-PJ_EXPORT_SYMBOL(pj_sock_recvfrom)
-PJ_EXPORT_SYMBOL(pj_sock_send)
-PJ_EXPORT_SYMBOL(pj_sock_sendto)
-
-/*
- * sock_select.h
- */
-PJ_EXPORT_SYMBOL(PJ_FD_ZERO)
-PJ_EXPORT_SYMBOL(PJ_FD_SET)
-PJ_EXPORT_SYMBOL(PJ_FD_CLR)
-PJ_EXPORT_SYMBOL(PJ_FD_ISSET)
-PJ_EXPORT_SYMBOL(pj_sock_select)
-
-/*
- * string.h
- */
-PJ_EXPORT_SYMBOL(pj_str)
-PJ_EXPORT_SYMBOL(pj_strassign)
-PJ_EXPORT_SYMBOL(pj_strcpy)
-PJ_EXPORT_SYMBOL(pj_strcpy2)
-PJ_EXPORT_SYMBOL(pj_strdup)
-PJ_EXPORT_SYMBOL(pj_strdup_with_null)
-PJ_EXPORT_SYMBOL(pj_strdup2)
-PJ_EXPORT_SYMBOL(pj_strdup3)
-PJ_EXPORT_SYMBOL(pj_strcmp)
-PJ_EXPORT_SYMBOL(pj_strcmp2)
-PJ_EXPORT_SYMBOL(pj_strncmp)
-PJ_EXPORT_SYMBOL(pj_strncmp2)
-PJ_EXPORT_SYMBOL(pj_stricmp)
-PJ_EXPORT_SYMBOL(pj_stricmp2)
-PJ_EXPORT_SYMBOL(pj_strnicmp)
-PJ_EXPORT_SYMBOL(pj_strnicmp2)
-PJ_EXPORT_SYMBOL(pj_strcat)
-PJ_EXPORT_SYMBOL(pj_strltrim)
-PJ_EXPORT_SYMBOL(pj_strrtrim)
-PJ_EXPORT_SYMBOL(pj_strtrim)
-PJ_EXPORT_SYMBOL(pj_create_random_string)
-PJ_EXPORT_SYMBOL(pj_strtoul)
-PJ_EXPORT_SYMBOL(pj_utoa)
-PJ_EXPORT_SYMBOL(pj_utoa_pad)
-
-/*
- * timer.h
- */
-PJ_EXPORT_SYMBOL(pj_timer_heap_mem_size)
-PJ_EXPORT_SYMBOL(pj_timer_heap_create)
-PJ_EXPORT_SYMBOL(pj_timer_entry_init)
-PJ_EXPORT_SYMBOL(pj_timer_heap_schedule)
-PJ_EXPORT_SYMBOL(pj_timer_heap_cancel)
-PJ_EXPORT_SYMBOL(pj_timer_heap_count)
-PJ_EXPORT_SYMBOL(pj_timer_heap_earliest_time)
-PJ_EXPORT_SYMBOL(pj_timer_heap_poll)
-
-/*
- * types.h
- */
-PJ_EXPORT_SYMBOL(pj_time_val_normalize)
-
+/* $Id$
+ */
+/*
+ * PJLIB - PJ Foundation Library
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ * Benny Prijono <bennylp@bulukucing.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#include <pjlib.h>
+
+/*
+ * addr_resolv.h
+ */
+PJ_EXPORT_SYMBOL(pj_gethostbyname)
+
+/*
+ * array.h
+ */
+PJ_EXPORT_SYMBOL(pj_array_insert)
+PJ_EXPORT_SYMBOL(pj_array_erase)
+PJ_EXPORT_SYMBOL(pj_array_find)
+
+/*
+ * config.h
+ */
+PJ_EXPORT_SYMBOL(pj_dump_config)
+
+/*
+ * errno.h
+ */
+PJ_EXPORT_SYMBOL(pj_get_os_error)
+PJ_EXPORT_SYMBOL(pj_set_os_error)
+PJ_EXPORT_SYMBOL(pj_get_netos_error)
+PJ_EXPORT_SYMBOL(pj_set_netos_error)
+PJ_EXPORT_SYMBOL(pj_strerror)
+
+/*
+ * except.h
+ */
+PJ_EXPORT_SYMBOL(pj_throw_exception_)
+PJ_EXPORT_SYMBOL(pj_push_exception_handler_)
+PJ_EXPORT_SYMBOL(pj_pop_exception_handler_)
+PJ_EXPORT_SYMBOL(pj_setjmp)
+PJ_EXPORT_SYMBOL(pj_longjmp)
+PJ_EXPORT_SYMBOL(pj_exception_id_alloc)
+PJ_EXPORT_SYMBOL(pj_exception_id_free)
+PJ_EXPORT_SYMBOL(pj_exception_id_name)
+
+
+/*
+ * fifobuf.h
+ */
+PJ_EXPORT_SYMBOL(pj_fifobuf_init)
+PJ_EXPORT_SYMBOL(pj_fifobuf_max_size)
+PJ_EXPORT_SYMBOL(pj_fifobuf_alloc)
+PJ_EXPORT_SYMBOL(pj_fifobuf_unalloc)
+PJ_EXPORT_SYMBOL(pj_fifobuf_free)
+
+/*
+ * guid.h
+ */
+PJ_EXPORT_SYMBOL(pj_generate_unique_string)
+PJ_EXPORT_SYMBOL(pj_create_unique_string)
+
+/*
+ * hash.h
+ */
+PJ_EXPORT_SYMBOL(pj_hash_calc)
+PJ_EXPORT_SYMBOL(pj_hash_create)
+PJ_EXPORT_SYMBOL(pj_hash_get)
+PJ_EXPORT_SYMBOL(pj_hash_set)
+PJ_EXPORT_SYMBOL(pj_hash_count)
+PJ_EXPORT_SYMBOL(pj_hash_first)
+PJ_EXPORT_SYMBOL(pj_hash_next)
+PJ_EXPORT_SYMBOL(pj_hash_this)
+
+/*
+ * ioqueue.h
+ */
+PJ_EXPORT_SYMBOL(pj_ioqueue_create)
+PJ_EXPORT_SYMBOL(pj_ioqueue_destroy)
+PJ_EXPORT_SYMBOL(pj_ioqueue_set_lock)
+PJ_EXPORT_SYMBOL(pj_ioqueue_register_sock)
+PJ_EXPORT_SYMBOL(pj_ioqueue_unregister)
+PJ_EXPORT_SYMBOL(pj_ioqueue_get_user_data)
+PJ_EXPORT_SYMBOL(pj_ioqueue_poll)
+PJ_EXPORT_SYMBOL(pj_ioqueue_read)
+PJ_EXPORT_SYMBOL(pj_ioqueue_recv)
+PJ_EXPORT_SYMBOL(pj_ioqueue_recvfrom)
+PJ_EXPORT_SYMBOL(pj_ioqueue_write)
+PJ_EXPORT_SYMBOL(pj_ioqueue_send)
+PJ_EXPORT_SYMBOL(pj_ioqueue_sendto)
+#if defined(PJ_HAS_TCP) && PJ_HAS_TCP != 0
+PJ_EXPORT_SYMBOL(pj_ioqueue_accept)
+PJ_EXPORT_SYMBOL(pj_ioqueue_connect)
+#endif
+
+/*
+ * list.h
+ */
+PJ_EXPORT_SYMBOL(pj_list_insert_before)
+PJ_EXPORT_SYMBOL(pj_list_insert_nodes_before)
+PJ_EXPORT_SYMBOL(pj_list_insert_after)
+PJ_EXPORT_SYMBOL(pj_list_insert_nodes_after)
+PJ_EXPORT_SYMBOL(pj_list_merge_first)
+PJ_EXPORT_SYMBOL(pj_list_merge_last)
+PJ_EXPORT_SYMBOL(pj_list_erase)
+PJ_EXPORT_SYMBOL(pj_list_find_node)
+PJ_EXPORT_SYMBOL(pj_list_search)
+
+
+/*
+ * log.h
+ */
+PJ_EXPORT_SYMBOL(pj_log_write)
+#if PJ_LOG_MAX_LEVEL >= 1
+PJ_EXPORT_SYMBOL(pj_log_set_log_func)
+PJ_EXPORT_SYMBOL(pj_log_get_log_func)
+PJ_EXPORT_SYMBOL(pj_log_set_level)
+PJ_EXPORT_SYMBOL(pj_log_get_level)
+PJ_EXPORT_SYMBOL(pj_log_set_decor)
+PJ_EXPORT_SYMBOL(pj_log_get_decor)
+PJ_EXPORT_SYMBOL(pj_log_1)
+#endif
+#if PJ_LOG_MAX_LEVEL >= 2
+PJ_EXPORT_SYMBOL(pj_log_2)
+#endif
+#if PJ_LOG_MAX_LEVEL >= 3
+PJ_EXPORT_SYMBOL(pj_log_3)
+#endif
+#if PJ_LOG_MAX_LEVEL >= 4
+PJ_EXPORT_SYMBOL(pj_log_4)
+#endif
+#if PJ_LOG_MAX_LEVEL >= 5
+PJ_EXPORT_SYMBOL(pj_log_5)
+#endif
+#if PJ_LOG_MAX_LEVEL >= 6
+PJ_EXPORT_SYMBOL(pj_log_6)
+#endif
+
+/*
+ * os.h
+ */
+PJ_EXPORT_SYMBOL(pj_init)
+PJ_EXPORT_SYMBOL(pj_getpid)
+PJ_EXPORT_SYMBOL(pj_thread_register)
+PJ_EXPORT_SYMBOL(pj_thread_create)
+PJ_EXPORT_SYMBOL(pj_thread_get_name)
+PJ_EXPORT_SYMBOL(pj_thread_resume)
+PJ_EXPORT_SYMBOL(pj_thread_this)
+PJ_EXPORT_SYMBOL(pj_thread_join)
+PJ_EXPORT_SYMBOL(pj_thread_destroy)
+PJ_EXPORT_SYMBOL(pj_thread_sleep)
+#if defined(PJ_OS_HAS_CHECK_STACK) && PJ_OS_HAS_CHECK_STACK != 0
+PJ_EXPORT_SYMBOL(pj_thread_check_stack)
+PJ_EXPORT_SYMBOL(pj_thread_get_stack_max_usage)
+PJ_EXPORT_SYMBOL(pj_thread_get_stack_info)
+#endif
+PJ_EXPORT_SYMBOL(pj_atomic_create)
+PJ_EXPORT_SYMBOL(pj_atomic_destroy)
+PJ_EXPORT_SYMBOL(pj_atomic_set)
+PJ_EXPORT_SYMBOL(pj_atomic_get)
+PJ_EXPORT_SYMBOL(pj_atomic_inc)
+PJ_EXPORT_SYMBOL(pj_atomic_dec)
+PJ_EXPORT_SYMBOL(pj_thread_local_alloc)
+PJ_EXPORT_SYMBOL(pj_thread_local_free)
+PJ_EXPORT_SYMBOL(pj_thread_local_set)
+PJ_EXPORT_SYMBOL(pj_thread_local_get)
+PJ_EXPORT_SYMBOL(pj_enter_critical_section)
+PJ_EXPORT_SYMBOL(pj_leave_critical_section)
+PJ_EXPORT_SYMBOL(pj_mutex_create)
+PJ_EXPORT_SYMBOL(pj_mutex_lock)
+PJ_EXPORT_SYMBOL(pj_mutex_unlock)
+PJ_EXPORT_SYMBOL(pj_mutex_trylock)
+PJ_EXPORT_SYMBOL(pj_mutex_destroy)
+#if defined(PJ_DEBUG) && PJ_DEBUG != 0
+PJ_EXPORT_SYMBOL(pj_mutex_is_locked)
+#endif
+#if defined(PJ_HAS_SEMAPHORE) && PJ_HAS_SEMAPHORE != 0
+PJ_EXPORT_SYMBOL(pj_sem_create)
+PJ_EXPORT_SYMBOL(pj_sem_wait)
+PJ_EXPORT_SYMBOL(pj_sem_trywait)
+PJ_EXPORT_SYMBOL(pj_sem_post)
+PJ_EXPORT_SYMBOL(pj_sem_destroy)
+#endif
+PJ_EXPORT_SYMBOL(pj_gettimeofday)
+PJ_EXPORT_SYMBOL(pj_time_decode)
+#if defined(PJ_HAS_HIGH_RES_TIMER) && PJ_HAS_HIGH_RES_TIMER != 0
+PJ_EXPORT_SYMBOL(pj_get_timestamp)
+PJ_EXPORT_SYMBOL(pj_get_timestamp_freq)
+PJ_EXPORT_SYMBOL(pj_elapsed_time)
+PJ_EXPORT_SYMBOL(pj_elapsed_usec)
+PJ_EXPORT_SYMBOL(pj_elapsed_nanosec)
+PJ_EXPORT_SYMBOL(pj_elapsed_cycle)
+#endif
+
+
+/*
+ * pool.h
+ */
+PJ_EXPORT_SYMBOL(pj_pool_create)
+PJ_EXPORT_SYMBOL(pj_pool_release)
+PJ_EXPORT_SYMBOL(pj_pool_getobjname)
+PJ_EXPORT_SYMBOL(pj_pool_reset)
+PJ_EXPORT_SYMBOL(pj_pool_get_capacity)
+PJ_EXPORT_SYMBOL(pj_pool_get_used_size)
+PJ_EXPORT_SYMBOL(pj_pool_alloc)
+PJ_EXPORT_SYMBOL(pj_pool_calloc)
+PJ_EXPORT_SYMBOL(pj_pool_factory_default_policy)
+PJ_EXPORT_SYMBOL(pj_pool_create_int)
+PJ_EXPORT_SYMBOL(pj_pool_init_int)
+PJ_EXPORT_SYMBOL(pj_pool_destroy_int)
+PJ_EXPORT_SYMBOL(pj_caching_pool_init)
+PJ_EXPORT_SYMBOL(pj_caching_pool_destroy)
+
+/*
+ * rand.h
+ */
+PJ_EXPORT_SYMBOL(pj_rand)
+PJ_EXPORT_SYMBOL(pj_srand)
+
+/*
+ * rbtree.h
+ */
+PJ_EXPORT_SYMBOL(pj_rbtree_init)
+PJ_EXPORT_SYMBOL(pj_rbtree_first)
+PJ_EXPORT_SYMBOL(pj_rbtree_last)
+PJ_EXPORT_SYMBOL(pj_rbtree_next)
+PJ_EXPORT_SYMBOL(pj_rbtree_prev)
+PJ_EXPORT_SYMBOL(pj_rbtree_insert)
+PJ_EXPORT_SYMBOL(pj_rbtree_find)
+PJ_EXPORT_SYMBOL(pj_rbtree_erase)
+PJ_EXPORT_SYMBOL(pj_rbtree_max_height)
+PJ_EXPORT_SYMBOL(pj_rbtree_min_height)
+
+/*
+ * sock.h
+ */
+PJ_EXPORT_SYMBOL(PJ_AF_UNIX)
+PJ_EXPORT_SYMBOL(PJ_AF_INET)
+PJ_EXPORT_SYMBOL(PJ_AF_INET6)
+PJ_EXPORT_SYMBOL(PJ_AF_PACKET)
+PJ_EXPORT_SYMBOL(PJ_AF_IRDA)
+PJ_EXPORT_SYMBOL(PJ_SOCK_STREAM)
+PJ_EXPORT_SYMBOL(PJ_SOCK_DGRAM)
+PJ_EXPORT_SYMBOL(PJ_SOCK_RAW)
+PJ_EXPORT_SYMBOL(PJ_SOCK_RDM)
+PJ_EXPORT_SYMBOL(PJ_SOL_SOCKET)
+PJ_EXPORT_SYMBOL(PJ_SOL_IP)
+PJ_EXPORT_SYMBOL(PJ_SOL_TCP)
+PJ_EXPORT_SYMBOL(PJ_SOL_UDP)
+PJ_EXPORT_SYMBOL(PJ_SOL_IPV6)
+PJ_EXPORT_SYMBOL(pj_ntohs)
+PJ_EXPORT_SYMBOL(pj_htons)
+PJ_EXPORT_SYMBOL(pj_ntohl)
+PJ_EXPORT_SYMBOL(pj_htonl)
+PJ_EXPORT_SYMBOL(pj_inet_ntoa)
+PJ_EXPORT_SYMBOL(pj_inet_aton)
+PJ_EXPORT_SYMBOL(pj_inet_addr)
+PJ_EXPORT_SYMBOL(pj_sockaddr_in_set_str_addr)
+PJ_EXPORT_SYMBOL(pj_sockaddr_in_init)
+PJ_EXPORT_SYMBOL(pj_gethostname)
+PJ_EXPORT_SYMBOL(pj_gethostaddr)
+PJ_EXPORT_SYMBOL(pj_sock_socket)
+PJ_EXPORT_SYMBOL(pj_sock_close)
+PJ_EXPORT_SYMBOL(pj_sock_bind)
+PJ_EXPORT_SYMBOL(pj_sock_bind_in)
+#if defined(PJ_HAS_TCP) && PJ_HAS_TCP != 0
+PJ_EXPORT_SYMBOL(pj_sock_listen)
+PJ_EXPORT_SYMBOL(pj_sock_accept)
+PJ_EXPORT_SYMBOL(pj_sock_shutdown)
+#endif
+PJ_EXPORT_SYMBOL(pj_sock_connect)
+PJ_EXPORT_SYMBOL(pj_sock_getpeername)
+PJ_EXPORT_SYMBOL(pj_sock_getsockname)
+PJ_EXPORT_SYMBOL(pj_sock_getsockopt)
+PJ_EXPORT_SYMBOL(pj_sock_setsockopt)
+PJ_EXPORT_SYMBOL(pj_sock_recv)
+PJ_EXPORT_SYMBOL(pj_sock_recvfrom)
+PJ_EXPORT_SYMBOL(pj_sock_send)
+PJ_EXPORT_SYMBOL(pj_sock_sendto)
+
+/*
+ * sock_select.h
+ */
+PJ_EXPORT_SYMBOL(PJ_FD_ZERO)
+PJ_EXPORT_SYMBOL(PJ_FD_SET)
+PJ_EXPORT_SYMBOL(PJ_FD_CLR)
+PJ_EXPORT_SYMBOL(PJ_FD_ISSET)
+PJ_EXPORT_SYMBOL(pj_sock_select)
+
+/*
+ * string.h
+ */
+PJ_EXPORT_SYMBOL(pj_str)
+PJ_EXPORT_SYMBOL(pj_strassign)
+PJ_EXPORT_SYMBOL(pj_strcpy)
+PJ_EXPORT_SYMBOL(pj_strcpy2)
+PJ_EXPORT_SYMBOL(pj_strdup)
+PJ_EXPORT_SYMBOL(pj_strdup_with_null)
+PJ_EXPORT_SYMBOL(pj_strdup2)
+PJ_EXPORT_SYMBOL(pj_strdup3)
+PJ_EXPORT_SYMBOL(pj_strcmp)
+PJ_EXPORT_SYMBOL(pj_strcmp2)
+PJ_EXPORT_SYMBOL(pj_strncmp)
+PJ_EXPORT_SYMBOL(pj_strncmp2)
+PJ_EXPORT_SYMBOL(pj_stricmp)
+PJ_EXPORT_SYMBOL(pj_stricmp2)
+PJ_EXPORT_SYMBOL(pj_strnicmp)
+PJ_EXPORT_SYMBOL(pj_strnicmp2)
+PJ_EXPORT_SYMBOL(pj_strcat)
+PJ_EXPORT_SYMBOL(pj_strltrim)
+PJ_EXPORT_SYMBOL(pj_strrtrim)
+PJ_EXPORT_SYMBOL(pj_strtrim)
+PJ_EXPORT_SYMBOL(pj_create_random_string)
+PJ_EXPORT_SYMBOL(pj_strtoul)
+PJ_EXPORT_SYMBOL(pj_utoa)
+PJ_EXPORT_SYMBOL(pj_utoa_pad)
+
+/*
+ * timer.h
+ */
+PJ_EXPORT_SYMBOL(pj_timer_heap_mem_size)
+PJ_EXPORT_SYMBOL(pj_timer_heap_create)
+PJ_EXPORT_SYMBOL(pj_timer_entry_init)
+PJ_EXPORT_SYMBOL(pj_timer_heap_schedule)
+PJ_EXPORT_SYMBOL(pj_timer_heap_cancel)
+PJ_EXPORT_SYMBOL(pj_timer_heap_count)
+PJ_EXPORT_SYMBOL(pj_timer_heap_earliest_time)
+PJ_EXPORT_SYMBOL(pj_timer_heap_poll)
+
+/*
+ * types.h
+ */
+PJ_EXPORT_SYMBOL(pj_time_val_normalize)
+
diff --git a/pjlib/src/pj/timer.c b/pjlib/src/pj/timer.c
index ffec1f4..c3e4f95 100644
--- a/pjlib/src/pj/timer.c
+++ b/pjlib/src/pj/timer.c
@@ -1,390 +1,411 @@
-/* $Id$
- */
-/* (C)1993-2003 Douglas C. Schmidt
- *
- * This file is originaly from ACE library by Doug Schmidt
- * ACE(TM), TAO(TM) and CIAO(TM) are copyrighted by Douglas C. Schmidt and
- * his research group at Washington University, University of California,
- * Irvine, and Vanderbilt University Copyright
- * (c) 1993-2003, all rights reserved.
- */
-#include <pj/timer.h>
-#include <pj/pool.h>
-#include <pj/os.h>
-#include <pj/string.h>
-#include <pj/assert.h>
-#include <pj/errno.h>
+/* $Id$
+ */
+/*
+ * PJLIB - PJ Foundation Library
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ * Benny Prijono <bennylp@bulukucing.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+/* (C)1993-2003 Douglas C. Schmidt
+ *
+ * This file is originaly from ACE library by Doug Schmidt
+ * ACE(TM), TAO(TM) and CIAO(TM) are copyrighted by Douglas C. Schmidt and
+ * his research group at Washington University, University of California,
+ * Irvine, and Vanderbilt University Copyright
+ * (c) 1993-2003, all rights reserved.
+ */
+#include <pj/timer.h>
+#include <pj/pool.h>
+#include <pj/os.h>
+#include <pj/string.h>
+#include <pj/assert.h>
+#include <pj/errno.h>
#include <pj/lock.h>
-
-#define HEAP_PARENT(X) (X == 0 ? 0 : (((X) - 1) / 2))
-#define HEAP_LEFT(X) (((X)+(X))+1)
+
+#define HEAP_PARENT(X) (X == 0 ? 0 : (((X) - 1) / 2))
+#define HEAP_LEFT(X) (((X)+(X))+1)
#define DEFAULT_MAX_TIMED_OUT_PER_POLL (64)
-
-
-/**
- * The implementation of timer heap.
- */
-struct pj_timer_heap_t
-{
- /** Pool from which the timer heap resize will get the storage from */
- pj_pool_t *pool;
-
- /** Maximum size of the heap. */
- pj_size_t max_size;
-
- /** Current size of the heap. */
- pj_size_t cur_size;
+
+
+/**
+ * The implementation of timer heap.
+ */
+struct pj_timer_heap_t
+{
+ /** Pool from which the timer heap resize will get the storage from */
+ pj_pool_t *pool;
+
+ /** Maximum size of the heap. */
+ pj_size_t max_size;
+
+ /** Current size of the heap. */
+ pj_size_t cur_size;
/** Max timed out entries to process per poll. */
unsigned max_entries_per_poll;
-
- /** Lock object. */
- pj_lock_t *lock;
+
+ /** Lock object. */
+ pj_lock_t *lock;
/** Autodelete lock. */
pj_bool_t auto_delete_lock;
-
- /**
- * Current contents of the Heap, which is organized as a "heap" of
- * pj_timer_entry *'s. In this context, a heap is a "partially
- * ordered, almost complete" binary tree, which is stored in an
- * array.
- */
- pj_timer_entry **heap;
-
- /**
- * An array of "pointers" that allows each pj_timer_entry in the
- * <heap_> to be located in O(1) time. Basically, <timer_id_[i]>
- * contains the slot in the <heap_> array where an pj_timer_entry
- * with timer id <i> resides. Thus, the timer id passed back from
- * <schedule_entry> is really an slot into the <timer_ids> array. The
- * <timer_ids_> array serves two purposes: negative values are
- * treated as "pointers" for the <freelist_>, whereas positive
- * values are treated as "pointers" into the <heap_> array.
- */
- pj_timer_id_t *timer_ids;
-
- /**
- * "Pointer" to the first element in the freelist contained within
- * the <timer_ids_> array, which is organized as a stack.
- */
- pj_timer_id_t timer_ids_freelist;
-
- /** Callback to be called when a timer expires. */
- pj_timer_heap_callback *callback;
-
-};
-
-
-
-PJ_INLINE(void) lock_timer_heap( pj_timer_heap_t *ht )
-{
- if (ht->lock) {
- pj_lock_acquire(ht->lock);
- }
-}
-
-PJ_INLINE(void) unlock_timer_heap( pj_timer_heap_t *ht )
-{
- if (ht->lock) {
- pj_lock_release(ht->lock);
- }
-}
-
-
-static void copy_node( pj_timer_heap_t *ht, int slot, pj_timer_entry *moved_node )
-{
- PJ_CHECK_STACK();
-
- // Insert <moved_node> into its new location in the heap.
- ht->heap[slot] = moved_node;
-
- // Update the corresponding slot in the parallel <timer_ids_> array.
- ht->timer_ids[moved_node->_timer_id] = slot;
-}
-
-static pj_timer_id_t pop_freelist( pj_timer_heap_t *ht )
-{
- // We need to truncate this to <int> for backwards compatibility.
- pj_timer_id_t new_id = ht->timer_ids_freelist;
-
- PJ_CHECK_STACK();
-
- // The freelist values in the <timer_ids_> are negative, so we need
- // to negate them to get the next freelist "pointer."
- ht->timer_ids_freelist =
- -ht->timer_ids[ht->timer_ids_freelist];
-
- return new_id;
-
-}
-
-static void push_freelist (pj_timer_heap_t *ht, pj_timer_id_t old_id)
-{
- PJ_CHECK_STACK();
-
- // The freelist values in the <timer_ids_> are negative, so we need
- // to negate them to get the next freelist "pointer."
- ht->timer_ids[old_id] = -ht->timer_ids_freelist;
- ht->timer_ids_freelist = old_id;
-}
-
-
-static void reheap_down(pj_timer_heap_t *ht, pj_timer_entry *moved_node,
- size_t slot, size_t child)
-{
- PJ_CHECK_STACK();
-
- // Restore the heap property after a deletion.
-
- while (child < ht->cur_size)
- {
- // Choose the smaller of the two children.
- if (child + 1 < ht->cur_size
- && PJ_TIME_VAL_LT(ht->heap[child + 1]->_timer_value, ht->heap[child]->_timer_value))
- child++;
-
- // Perform a <copy> if the child has a larger timeout value than
- // the <moved_node>.
- if (PJ_TIME_VAL_LT(ht->heap[child]->_timer_value, moved_node->_timer_value))
- {
- copy_node( ht, slot, ht->heap[child]);
- slot = child;
- child = HEAP_LEFT(child);
- }
- else
- // We've found our location in the heap.
- break;
- }
-
- copy_node( ht, slot, moved_node);
-}
-
-static void reheap_up( pj_timer_heap_t *ht, pj_timer_entry *moved_node,
- size_t slot, size_t parent)
-{
- // Restore the heap property after an insertion.
-
- while (slot > 0)
- {
- // If the parent node is greater than the <moved_node> we need
- // to copy it down.
- if (PJ_TIME_VAL_LT(moved_node->_timer_value, ht->heap[parent]->_timer_value))
- {
- copy_node(ht, slot, ht->heap[parent]);
- slot = parent;
- parent = HEAP_PARENT(slot);
- }
- else
- break;
- }
-
- // Insert the new node into its proper resting place in the heap and
- // update the corresponding slot in the parallel <timer_ids> array.
- copy_node(ht, slot, moved_node);
-}
-
-
-static pj_timer_entry * remove_node( pj_timer_heap_t *ht, size_t slot)
-{
- pj_timer_entry *removed_node = ht->heap[slot];
-
- // Return this timer id to the freelist.
- push_freelist( ht, removed_node->_timer_id );
-
- // Decrement the size of the heap by one since we're removing the
- // "slot"th node.
- ht->cur_size--;
-
- // Set the ID
- removed_node->_timer_id = -1;
-
- // Only try to reheapify if we're not deleting the last entry.
-
- if (slot < ht->cur_size)
- {
- int parent;
- pj_timer_entry *moved_node = ht->heap[ht->cur_size];
-
- // Move the end node to the location being removed and update
- // the corresponding slot in the parallel <timer_ids> array.
- copy_node( ht, slot, moved_node);
-
- // If the <moved_node->time_value_> is great than or equal its
- // parent it needs be moved down the heap.
- parent = HEAP_PARENT (slot);
-
- if (PJ_TIME_VAL_GTE(moved_node->_timer_value, ht->heap[parent]->_timer_value))
- reheap_down( ht, moved_node, slot, HEAP_LEFT(slot));
- else
- reheap_up( ht, moved_node, slot, parent);
- }
-
- return removed_node;
-}
-
-static void grow_heap(pj_timer_heap_t *ht)
-{
- // All the containers will double in size from max_size_
- size_t new_size = ht->max_size * 2;
- pj_timer_id_t *new_timer_ids;
- pj_size_t i;
-
- // First grow the heap itself.
-
- pj_timer_entry **new_heap = 0;
-
- new_heap = pj_pool_alloc(ht->pool, sizeof(pj_timer_entry*) * new_size);
- memcpy(new_heap, ht->heap, ht->max_size * sizeof(pj_timer_entry*));
- //delete [] this->heap_;
- ht->heap = new_heap;
-
- // Grow the array of timer ids.
-
- new_timer_ids = 0;
- new_timer_ids = pj_pool_alloc(ht->pool, new_size * sizeof(pj_timer_id_t));
-
- memcpy( new_timer_ids, ht->timer_ids, ht->max_size * sizeof(pj_timer_id_t));
-
- //delete [] timer_ids_;
- ht->timer_ids = new_timer_ids;
-
- // And add the new elements to the end of the "freelist".
- for (i = ht->max_size; i < new_size; i++)
- ht->timer_ids[i] = -((pj_timer_id_t) (i + 1));
-
- ht->max_size = new_size;
-}
-
-static void insert_node(pj_timer_heap_t *ht, pj_timer_entry *new_node)
-{
- if (ht->cur_size + 2 >= ht->max_size)
- grow_heap(ht);
-
- reheap_up( ht, new_node, ht->cur_size, HEAP_PARENT(ht->cur_size));
- ht->cur_size++;
-}
-
-
-static pj_status_t schedule_entry( pj_timer_heap_t *ht,
- pj_timer_entry *entry,
- const pj_time_val *future_time )
-{
- if (ht->cur_size < ht->max_size)
- {
- // Obtain the next unique sequence number.
- // Set the entry
- entry->_timer_id = pop_freelist(ht);
- entry->_timer_value = *future_time;
- insert_node( ht, entry);
- return 0;
- }
- else
- return -1;
-}
-
-
-static int cancel( pj_timer_heap_t *ht,
- pj_timer_entry *entry,
- int dont_call)
-{
- long timer_node_slot;
-
- PJ_CHECK_STACK();
-
- // Check to see if the timer_id is out of range
- if (entry->_timer_id < 0 || (pj_size_t)entry->_timer_id > ht->max_size)
- return 0;
-
- timer_node_slot = ht->timer_ids[entry->_timer_id];
-
- if (timer_node_slot < 0) // Check to see if timer_id is still valid.
- return 0;
-
- if (entry != ht->heap[timer_node_slot])
- {
- pj_assert(entry == ht->heap[timer_node_slot]);
- return 0;
- }
- else
- {
- remove_node( ht, timer_node_slot);
-
- if (dont_call == 0)
- // Call the close hook.
- (*ht->callback)(ht, entry);
- return 1;
- }
-}
-
-
-/*
- * Calculate memory size required to create a timer heap.
- */
-PJ_DEF(pj_size_t) pj_timer_heap_mem_size(pj_size_t count)
-{
- return /* size of the timer heap itself: */
- sizeof(pj_timer_heap_t) +
- /* size of each entry: */
- (count+2) * (sizeof(pj_timer_entry*)+sizeof(pj_timer_id_t)) +
- /* lock, pool etc: */
- 132;
-}
-
-/*
- * Create a new timer heap.
- */
-PJ_DEF(pj_status_t) pj_timer_heap_create( pj_pool_t *pool,
- pj_size_t size,
- pj_timer_heap_t **p_heap)
-{
- pj_timer_heap_t *ht;
- pj_size_t i;
-
- PJ_ASSERT_RETURN(pool && p_heap, PJ_EINVAL);
-
- *p_heap = NULL;
-
- /* Magic? */
- size += 2;
-
- /* Allocate timer heap data structure from the pool */
- ht = pj_pool_alloc(pool, sizeof(pj_timer_heap_t));
- if (!ht)
- return PJ_ENOMEM;
-
- /* Initialize timer heap sizes */
- ht->max_size = size;
+
+ /**
+ * Current contents of the Heap, which is organized as a "heap" of
+ * pj_timer_entry *'s. In this context, a heap is a "partially
+ * ordered, almost complete" binary tree, which is stored in an
+ * array.
+ */
+ pj_timer_entry **heap;
+
+ /**
+ * An array of "pointers" that allows each pj_timer_entry in the
+ * <heap_> to be located in O(1) time. Basically, <timer_id_[i]>
+ * contains the slot in the <heap_> array where an pj_timer_entry
+ * with timer id <i> resides. Thus, the timer id passed back from
+ * <schedule_entry> is really an slot into the <timer_ids> array. The
+ * <timer_ids_> array serves two purposes: negative values are
+ * treated as "pointers" for the <freelist_>, whereas positive
+ * values are treated as "pointers" into the <heap_> array.
+ */
+ pj_timer_id_t *timer_ids;
+
+ /**
+ * "Pointer" to the first element in the freelist contained within
+ * the <timer_ids_> array, which is organized as a stack.
+ */
+ pj_timer_id_t timer_ids_freelist;
+
+ /** Callback to be called when a timer expires. */
+ pj_timer_heap_callback *callback;
+
+};
+
+
+
+PJ_INLINE(void) lock_timer_heap( pj_timer_heap_t *ht )
+{
+ if (ht->lock) {
+ pj_lock_acquire(ht->lock);
+ }
+}
+
+PJ_INLINE(void) unlock_timer_heap( pj_timer_heap_t *ht )
+{
+ if (ht->lock) {
+ pj_lock_release(ht->lock);
+ }
+}
+
+
+static void copy_node( pj_timer_heap_t *ht, int slot, pj_timer_entry *moved_node )
+{
+ PJ_CHECK_STACK();
+
+ // Insert <moved_node> into its new location in the heap.
+ ht->heap[slot] = moved_node;
+
+ // Update the corresponding slot in the parallel <timer_ids_> array.
+ ht->timer_ids[moved_node->_timer_id] = slot;
+}
+
+static pj_timer_id_t pop_freelist( pj_timer_heap_t *ht )
+{
+ // We need to truncate this to <int> for backwards compatibility.
+ pj_timer_id_t new_id = ht->timer_ids_freelist;
+
+ PJ_CHECK_STACK();
+
+ // The freelist values in the <timer_ids_> are negative, so we need
+ // to negate them to get the next freelist "pointer."
+ ht->timer_ids_freelist =
+ -ht->timer_ids[ht->timer_ids_freelist];
+
+ return new_id;
+
+}
+
+static void push_freelist (pj_timer_heap_t *ht, pj_timer_id_t old_id)
+{
+ PJ_CHECK_STACK();
+
+ // The freelist values in the <timer_ids_> are negative, so we need
+ // to negate them to get the next freelist "pointer."
+ ht->timer_ids[old_id] = -ht->timer_ids_freelist;
+ ht->timer_ids_freelist = old_id;
+}
+
+
+static void reheap_down(pj_timer_heap_t *ht, pj_timer_entry *moved_node,
+ size_t slot, size_t child)
+{
+ PJ_CHECK_STACK();
+
+ // Restore the heap property after a deletion.
+
+ while (child < ht->cur_size)
+ {
+ // Choose the smaller of the two children.
+ if (child + 1 < ht->cur_size
+ && PJ_TIME_VAL_LT(ht->heap[child + 1]->_timer_value, ht->heap[child]->_timer_value))
+ child++;
+
+ // Perform a <copy> if the child has a larger timeout value than
+ // the <moved_node>.
+ if (PJ_TIME_VAL_LT(ht->heap[child]->_timer_value, moved_node->_timer_value))
+ {
+ copy_node( ht, slot, ht->heap[child]);
+ slot = child;
+ child = HEAP_LEFT(child);
+ }
+ else
+ // We've found our location in the heap.
+ break;
+ }
+
+ copy_node( ht, slot, moved_node);
+}
+
+static void reheap_up( pj_timer_heap_t *ht, pj_timer_entry *moved_node,
+ size_t slot, size_t parent)
+{
+ // Restore the heap property after an insertion.
+
+ while (slot > 0)
+ {
+ // If the parent node is greater than the <moved_node> we need
+ // to copy it down.
+ if (PJ_TIME_VAL_LT(moved_node->_timer_value, ht->heap[parent]->_timer_value))
+ {
+ copy_node(ht, slot, ht->heap[parent]);
+ slot = parent;
+ parent = HEAP_PARENT(slot);
+ }
+ else
+ break;
+ }
+
+ // Insert the new node into its proper resting place in the heap and
+ // update the corresponding slot in the parallel <timer_ids> array.
+ copy_node(ht, slot, moved_node);
+}
+
+
+static pj_timer_entry * remove_node( pj_timer_heap_t *ht, size_t slot)
+{
+ pj_timer_entry *removed_node = ht->heap[slot];
+
+ // Return this timer id to the freelist.
+ push_freelist( ht, removed_node->_timer_id );
+
+ // Decrement the size of the heap by one since we're removing the
+ // "slot"th node.
+ ht->cur_size--;
+
+ // Set the ID
+ removed_node->_timer_id = -1;
+
+ // Only try to reheapify if we're not deleting the last entry.
+
+ if (slot < ht->cur_size)
+ {
+ int parent;
+ pj_timer_entry *moved_node = ht->heap[ht->cur_size];
+
+ // Move the end node to the location being removed and update
+ // the corresponding slot in the parallel <timer_ids> array.
+ copy_node( ht, slot, moved_node);
+
+ // If the <moved_node->time_value_> is great than or equal its
+ // parent it needs be moved down the heap.
+ parent = HEAP_PARENT (slot);
+
+ if (PJ_TIME_VAL_GTE(moved_node->_timer_value, ht->heap[parent]->_timer_value))
+ reheap_down( ht, moved_node, slot, HEAP_LEFT(slot));
+ else
+ reheap_up( ht, moved_node, slot, parent);
+ }
+
+ return removed_node;
+}
+
+static void grow_heap(pj_timer_heap_t *ht)
+{
+ // All the containers will double in size from max_size_
+ size_t new_size = ht->max_size * 2;
+ pj_timer_id_t *new_timer_ids;
+ pj_size_t i;
+
+ // First grow the heap itself.
+
+ pj_timer_entry **new_heap = 0;
+
+ new_heap = pj_pool_alloc(ht->pool, sizeof(pj_timer_entry*) * new_size);
+ memcpy(new_heap, ht->heap, ht->max_size * sizeof(pj_timer_entry*));
+ //delete [] this->heap_;
+ ht->heap = new_heap;
+
+ // Grow the array of timer ids.
+
+ new_timer_ids = 0;
+ new_timer_ids = pj_pool_alloc(ht->pool, new_size * sizeof(pj_timer_id_t));
+
+ memcpy( new_timer_ids, ht->timer_ids, ht->max_size * sizeof(pj_timer_id_t));
+
+ //delete [] timer_ids_;
+ ht->timer_ids = new_timer_ids;
+
+ // And add the new elements to the end of the "freelist".
+ for (i = ht->max_size; i < new_size; i++)
+ ht->timer_ids[i] = -((pj_timer_id_t) (i + 1));
+
+ ht->max_size = new_size;
+}
+
+static void insert_node(pj_timer_heap_t *ht, pj_timer_entry *new_node)
+{
+ if (ht->cur_size + 2 >= ht->max_size)
+ grow_heap(ht);
+
+ reheap_up( ht, new_node, ht->cur_size, HEAP_PARENT(ht->cur_size));
+ ht->cur_size++;
+}
+
+
+static pj_status_t schedule_entry( pj_timer_heap_t *ht,
+ pj_timer_entry *entry,
+ const pj_time_val *future_time )
+{
+ if (ht->cur_size < ht->max_size)
+ {
+ // Obtain the next unique sequence number.
+ // Set the entry
+ entry->_timer_id = pop_freelist(ht);
+ entry->_timer_value = *future_time;
+ insert_node( ht, entry);
+ return 0;
+ }
+ else
+ return -1;
+}
+
+
+static int cancel( pj_timer_heap_t *ht,
+ pj_timer_entry *entry,
+ int dont_call)
+{
+ long timer_node_slot;
+
+ PJ_CHECK_STACK();
+
+ // Check to see if the timer_id is out of range
+ if (entry->_timer_id < 0 || (pj_size_t)entry->_timer_id > ht->max_size)
+ return 0;
+
+ timer_node_slot = ht->timer_ids[entry->_timer_id];
+
+ if (timer_node_slot < 0) // Check to see if timer_id is still valid.
+ return 0;
+
+ if (entry != ht->heap[timer_node_slot])
+ {
+ pj_assert(entry == ht->heap[timer_node_slot]);
+ return 0;
+ }
+ else
+ {
+ remove_node( ht, timer_node_slot);
+
+ if (dont_call == 0)
+ // Call the close hook.
+ (*ht->callback)(ht, entry);
+ return 1;
+ }
+}
+
+
+/*
+ * Calculate memory size required to create a timer heap.
+ */
+PJ_DEF(pj_size_t) pj_timer_heap_mem_size(pj_size_t count)
+{
+ return /* size of the timer heap itself: */
+ sizeof(pj_timer_heap_t) +
+ /* size of each entry: */
+ (count+2) * (sizeof(pj_timer_entry*)+sizeof(pj_timer_id_t)) +
+ /* lock, pool etc: */
+ 132;
+}
+
+/*
+ * Create a new timer heap.
+ */
+PJ_DEF(pj_status_t) pj_timer_heap_create( pj_pool_t *pool,
+ pj_size_t size,
+ pj_timer_heap_t **p_heap)
+{
+ pj_timer_heap_t *ht;
+ pj_size_t i;
+
+ PJ_ASSERT_RETURN(pool && p_heap, PJ_EINVAL);
+
+ *p_heap = NULL;
+
+ /* Magic? */
+ size += 2;
+
+ /* Allocate timer heap data structure from the pool */
+ ht = pj_pool_alloc(pool, sizeof(pj_timer_heap_t));
+ if (!ht)
+ return PJ_ENOMEM;
+
+ /* Initialize timer heap sizes */
+ ht->max_size = size;
ht->cur_size = 0;
- ht->max_entries_per_poll = DEFAULT_MAX_TIMED_OUT_PER_POLL;
- ht->timer_ids_freelist = 1;
+ ht->max_entries_per_poll = DEFAULT_MAX_TIMED_OUT_PER_POLL;
+ ht->timer_ids_freelist = 1;
ht->pool = pool;
-
- /* Lock. */
+
+ /* Lock. */
ht->lock = NULL;
ht->auto_delete_lock = 0;
-
- // Create the heap array.
- ht->heap = pj_pool_alloc(pool, sizeof(pj_timer_entry*) * size);
- if (!ht->heap)
- return PJ_ENOMEM;
-
- // Create the parallel
- ht->timer_ids = pj_pool_alloc( pool, sizeof(pj_timer_id_t) * size);
- if (!ht->timer_ids)
- return PJ_ENOMEM;
-
- // Initialize the "freelist," which uses negative values to
- // distinguish freelist elements from "pointers" into the <heap_>
- // array.
- for (i=0; i<size; ++i)
- ht->timer_ids[i] = -((pj_timer_id_t) (i + 1));
-
- *p_heap = ht;
- return PJ_SUCCESS;
-}
+
+ // Create the heap array.
+ ht->heap = pj_pool_alloc(pool, sizeof(pj_timer_entry*) * size);
+ if (!ht->heap)
+ return PJ_ENOMEM;
+
+ // Create the parallel
+ ht->timer_ids = pj_pool_alloc( pool, sizeof(pj_timer_id_t) * size);
+ if (!ht->timer_ids)
+ return PJ_ENOMEM;
+
+ // Initialize the "freelist," which uses negative values to
+ // distinguish freelist elements from "pointers" into the <heap_>
+ // array.
+ for (i=0; i<size; ++i)
+ ht->timer_ids[i] = -((pj_timer_id_t) (i + 1));
+
+ *p_heap = ht;
+ return PJ_SUCCESS;
+}
PJ_DEF(void) pj_timer_heap_destroy( pj_timer_heap_t *ht )
{
@@ -413,111 +434,111 @@
ht->max_entries_per_poll = count;
return old_count;
}
-
-PJ_DEF(pj_timer_entry*) pj_timer_entry_init( pj_timer_entry *entry,
- int id,
- void *user_data,
- pj_timer_heap_callback *cb )
-{
- pj_assert(entry && cb);
-
- entry->id = id;
- entry->user_data = user_data;
- entry->cb = cb;
-
- return entry;
-}
-
-PJ_DEF(pj_status_t) pj_timer_heap_schedule( pj_timer_heap_t *ht,
- pj_timer_entry *entry,
- const pj_time_val *delay)
-{
- pj_status_t status;
- pj_time_val expires;
-
- PJ_ASSERT_RETURN(ht && entry && delay, PJ_EINVAL);
-
- pj_gettimeofday(&expires);
- PJ_TIME_VAL_ADD(expires, *delay);
-
- lock_timer_heap(ht);
- status = schedule_entry(ht, entry, &expires);
- unlock_timer_heap(ht);
-
- return status;
-}
-
-PJ_DEF(int) pj_timer_heap_cancel( pj_timer_heap_t *ht,
- pj_timer_entry *entry)
-{
- int count;
-
- PJ_ASSERT_RETURN(ht && entry, PJ_EINVAL);
-
- lock_timer_heap(ht);
- count = cancel(ht, entry, 1);
- unlock_timer_heap(ht);
-
- return count;
-}
-
+
+PJ_DEF(pj_timer_entry*) pj_timer_entry_init( pj_timer_entry *entry,
+ int id,
+ void *user_data,
+ pj_timer_heap_callback *cb )
+{
+ pj_assert(entry && cb);
+
+ entry->id = id;
+ entry->user_data = user_data;
+ entry->cb = cb;
+
+ return entry;
+}
+
+PJ_DEF(pj_status_t) pj_timer_heap_schedule( pj_timer_heap_t *ht,
+ pj_timer_entry *entry,
+ const pj_time_val *delay)
+{
+ pj_status_t status;
+ pj_time_val expires;
+
+ PJ_ASSERT_RETURN(ht && entry && delay, PJ_EINVAL);
+
+ pj_gettimeofday(&expires);
+ PJ_TIME_VAL_ADD(expires, *delay);
+
+ lock_timer_heap(ht);
+ status = schedule_entry(ht, entry, &expires);
+ unlock_timer_heap(ht);
+
+ return status;
+}
+
+PJ_DEF(int) pj_timer_heap_cancel( pj_timer_heap_t *ht,
+ pj_timer_entry *entry)
+{
+ int count;
+
+ PJ_ASSERT_RETURN(ht && entry, PJ_EINVAL);
+
+ lock_timer_heap(ht);
+ count = cancel(ht, entry, 1);
+ unlock_timer_heap(ht);
+
+ return count;
+}
+
PJ_DEF(unsigned) pj_timer_heap_poll( pj_timer_heap_t *ht,
- pj_time_val *next_delay )
-{
- pj_time_val now;
- unsigned count;
-
- PJ_ASSERT_RETURN(ht, 0);
-
- if (!ht->cur_size && next_delay) {
- next_delay->sec = next_delay->msec = PJ_MAXINT32;
- return 0;
- }
-
- count = 0;
- pj_gettimeofday(&now);
-
- lock_timer_heap(ht);
- while ( ht->cur_size &&
+ pj_time_val *next_delay )
+{
+ pj_time_val now;
+ unsigned count;
+
+ PJ_ASSERT_RETURN(ht, 0);
+
+ if (!ht->cur_size && next_delay) {
+ next_delay->sec = next_delay->msec = PJ_MAXINT32;
+ return 0;
+ }
+
+ count = 0;
+ pj_gettimeofday(&now);
+
+ lock_timer_heap(ht);
+ while ( ht->cur_size &&
PJ_TIME_VAL_LTE(ht->heap[0]->_timer_value, now) &&
- count < ht->max_entries_per_poll )
- {
- pj_timer_entry *node = remove_node(ht, 0);
- ++count;
-
- unlock_timer_heap(ht);
- (*node->cb)(ht, node);
- lock_timer_heap(ht);
- }
- if (ht->cur_size && next_delay) {
- *next_delay = ht->heap[0]->_timer_value;
- PJ_TIME_VAL_SUB(*next_delay, now);
- } else if (next_delay) {
- next_delay->sec = next_delay->msec = PJ_MAXINT32;
- }
- unlock_timer_heap(ht);
-
- return count;
-}
-
-PJ_DEF(pj_size_t) pj_timer_heap_count( pj_timer_heap_t *ht )
+ count < ht->max_entries_per_poll )
+ {
+ pj_timer_entry *node = remove_node(ht, 0);
+ ++count;
+
+ unlock_timer_heap(ht);
+ (*node->cb)(ht, node);
+ lock_timer_heap(ht);
+ }
+ if (ht->cur_size && next_delay) {
+ *next_delay = ht->heap[0]->_timer_value;
+ PJ_TIME_VAL_SUB(*next_delay, now);
+ } else if (next_delay) {
+ next_delay->sec = next_delay->msec = PJ_MAXINT32;
+ }
+ unlock_timer_heap(ht);
+
+ return count;
+}
+
+PJ_DEF(pj_size_t) pj_timer_heap_count( pj_timer_heap_t *ht )
{
PJ_ASSERT_RETURN(ht, 0);
-
- return ht->cur_size;
-}
-
-PJ_DEF(pj_status_t) pj_timer_heap_earliest_time( pj_timer_heap_t * ht,
- pj_time_val *timeval)
-{
- pj_assert(ht->cur_size != 0);
- if (ht->cur_size == 0)
- return PJ_ENOTFOUND;
-
- lock_timer_heap(ht);
- *timeval = ht->heap[0]->_timer_value;
- unlock_timer_heap(ht);
-
- return PJ_SUCCESS;
-}
-
+
+ return ht->cur_size;
+}
+
+PJ_DEF(pj_status_t) pj_timer_heap_earliest_time( pj_timer_heap_t * ht,
+ pj_time_val *timeval)
+{
+ pj_assert(ht->cur_size != 0);
+ if (ht->cur_size == 0)
+ return PJ_ENOTFOUND;
+
+ lock_timer_heap(ht);
+ *timeval = ht->heap[0]->_timer_value;
+ unlock_timer_heap(ht);
+
+ return PJ_SUCCESS;
+}
+
diff --git a/pjlib/src/pj/types.c b/pjlib/src/pj/types.c
index c45e740..7ca146e 100644
--- a/pjlib/src/pj/types.c
+++ b/pjlib/src/pj/types.c
@@ -1,31 +1,52 @@
-/* $Id$
- */
-#include <pj/types.h>
-#include <pj/os.h>
-
-void pj_time_val_normalize(pj_time_val *t)
-{
- PJ_CHECK_STACK();
-
- if (t->msec >= 1000) {
- do {
- t->sec++;
- t->msec -= 1000;
- } while (t->msec >= 1000);
- }
- else if (t->msec <= -1000) {
- do {
- t->sec--;
- t->msec += 1000;
- } while (t->msec <= -1000);
- }
-
- if (t->sec >= 1 && t->msec < 0) {
- t->sec--;
- t->msec += 1000;
-
- } else if (t->sec < 0 && t->msec > 0) {
- t->sec++;
- t->msec -= 1000;
- }
-}
+/* $Id$
+ */
+/*
+ * PJLIB - PJ Foundation Library
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ * Benny Prijono <bennylp@bulukucing.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; 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/os.h>
+
+void pj_time_val_normalize(pj_time_val *t)
+{
+ PJ_CHECK_STACK();
+
+ if (t->msec >= 1000) {
+ do {
+ t->sec++;
+ t->msec -= 1000;
+ } while (t->msec >= 1000);
+ }
+ else if (t->msec <= -1000) {
+ do {
+ t->sec--;
+ t->msec += 1000;
+ } while (t->msec <= -1000);
+ }
+
+ if (t->sec >= 1 && t->msec < 0) {
+ t->sec--;
+ t->msec += 1000;
+
+ } else if (t->sec < 0 && t->msec > 0) {
+ t->sec++;
+ t->msec -= 1000;
+ }
+}
diff --git a/pjlib/src/pjlib++-test/main.cpp b/pjlib/src/pjlib++-test/main.cpp
index 99b7f8d..1b41dbe 100644
--- a/pjlib/src/pjlib++-test/main.cpp
+++ b/pjlib/src/pjlib++-test/main.cpp
@@ -1,3 +1,24 @@
+/*
+ * PJLIB - PJ Foundation Library
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ * Benny Prijono <bennylp@bulukucing.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; 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>
diff --git a/pjlib/src/pjlib-samples/except.c b/pjlib/src/pjlib-samples/except.c
index ab936c3..36509e4 100644
--- a/pjlib/src/pjlib-samples/except.c
+++ b/pjlib/src/pjlib-samples/except.c
@@ -1,69 +1,89 @@
-/* $Id$
- */
-#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$ */
+/*
+ * PJLIB - PJ Foundation Library
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ * Benny Prijono <bennylp@bulukucing.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; 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 127d8dd..e478d22 100644
--- a/pjlib/src/pjlib-samples/list.c
+++ b/pjlib/src/pjlib-samples/list.c
@@ -1,55 +1,75 @@
-/* $Id$
- */
-#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$ */
+/*
+ * PJLIB - PJ Foundation Library
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ * Benny Prijono <bennylp@bulukucing.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; 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 2e28976..cf1b7c8 100644
--- a/pjlib/src/pjlib-samples/log.c
+++ b/pjlib/src/pjlib-samples/log.c
@@ -1,26 +1,46 @@
-/* $Id$
- */
-#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$ */
+/*
+ * PJLIB - PJ Foundation Library
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ * Benny Prijono <bennylp@bulukucing.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; 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 09bdfdb..a6d55d2 100644
--- a/pjlib/src/pjlib-test/atomic.c
+++ b/pjlib/src/pjlib-test/atomic.c
@@ -1,92 +1,113 @@
-/* $Id$
- */
-#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;
+/* $Id$
+ */
+/*
+ * PJLIB - PJ Foundation Library
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ * Benny Prijono <bennylp@bulukucing.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; 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;
-
+ 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;
-
+ 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;
+ 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 */
-
+
+ /* 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 afded4f..ae84b1f 100644
--- a/pjlib/src/pjlib-test/echo_clt.c
+++ b/pjlib/src/pjlib-test/echo_clt.c
@@ -1,252 +1,273 @@
-/* $Id$
- */
-#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$
+ */
+/*
+ * PJLIB - PJ Foundation Library
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ * Benny Prijono <bennylp@bulukucing.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; 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 7bf2887..6fa3e93 100644
--- a/pjlib/src/pjlib-test/errno.c
+++ b/pjlib/src/pjlib-test/errno.c
@@ -1,146 +1,166 @@
-/* $Id$
- */
-#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$ */
+/*
+ * PJLIB - PJ Foundation Library
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ * Benny Prijono <bennylp@bulukucing.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; 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 31a4a54..be0886e 100644
--- a/pjlib/src/pjlib-test/exception.c
+++ b/pjlib/src/pjlib-test/exception.c
@@ -1,161 +1,182 @@
-/* $Id$
- */
-#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$
+ */
+/*
+ * PJLIB - PJ Foundation Library
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ * Benny Prijono <bennylp@bulukucing.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; 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 2f59473..55d4c5a 100644
--- a/pjlib/src/pjlib-test/fifobuf.c
+++ b/pjlib/src/pjlib-test/fifobuf.c
@@ -1,100 +1,121 @@
-/* $Id$
- */
-#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$
+ */
+/*
+ * PJLIB - PJ Foundation Library
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ * Benny Prijono <bennylp@bulukucing.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; 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 b3ab6e3..6e5611a 100644
--- a/pjlib/src/pjlib-test/file.c
+++ b/pjlib/src/pjlib-test/file.c
@@ -1,4 +1,25 @@
/* $Id$ */
+/*
+ * PJLIB - PJ Foundation Library
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ * Benny Prijono <bennylp@bulukucing.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; 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>
diff --git a/pjlib/src/pjlib-test/ioq_perf.c b/pjlib/src/pjlib-test/ioq_perf.c
index cfeaab7..2d76a01 100644
--- a/pjlib/src/pjlib-test/ioq_perf.c
+++ b/pjlib/src/pjlib-test/ioq_perf.c
@@ -1,486 +1,507 @@
-/* $Id$
- */
-#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$
+ */
+/*
+ * PJLIB - PJ Foundation Library
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ * Benny Prijono <bennylp@bulukucing.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; 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 2282794..30d594b 100644
--- a/pjlib/src/pjlib-test/ioq_tcp.c
+++ b/pjlib/src/pjlib-test/ioq_tcp.c
@@ -1,524 +1,545 @@
-/* $Id$
- */
-#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$
+ */
+/*
+ * PJLIB - PJ Foundation Library
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ * Benny Prijono <bennylp@bulukucing.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; 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 c6a5c5f..8e8b6d1 100644
--- a/pjlib/src/pjlib-test/ioq_udp.c
+++ b/pjlib/src/pjlib-test/ioq_udp.c
@@ -1,647 +1,668 @@
-/* $Id$
- */
-#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$
+ */
+/*
+ * PJLIB - PJ Foundation Library
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ * Benny Prijono <bennylp@bulukucing.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; 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 4184e80..2a2dafb 100644
--- a/pjlib/src/pjlib-test/list.c
+++ b/pjlib/src/pjlib-test/list.c
@@ -1,209 +1,230 @@
-/* $Id$
- */
-#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$
+ */
+/*
+ * PJLIB - PJ Foundation Library
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ * Benny Prijono <bennylp@bulukucing.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; 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 6a764e6..bf8e99c 100644
--- a/pjlib/src/pjlib-test/main.c
+++ b/pjlib/src/pjlib-test/main.c
@@ -1,77 +1,98 @@
-/* $Id$
- */
-#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;
-
-
+/* $Id$
+ */
+/*
+ * PJLIB - PJ Foundation Library
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ * Benny Prijono <bennylp@bulukucing.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; 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;
-}
-
+#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 7978e36..34dfc70 100644
--- a/pjlib/src/pjlib-test/main_mod.c
+++ b/pjlib/src/pjlib-test/main_mod.c
@@ -1,23 +1,44 @@
-/* $Id$
- */
-#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$
+ */
+/*
+ * PJLIB - PJ Foundation Library
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ * Benny Prijono <bennylp@bulukucing.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; 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 c7af51b..f82987c 100644
--- a/pjlib/src/pjlib-test/mutex.c
+++ b/pjlib/src/pjlib-test/mutex.c
@@ -1,158 +1,179 @@
-/* $Id$
- */
-#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$
+ */
+/*
+ * PJLIB - PJ Foundation Library
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ * Benny Prijono <bennylp@bulukucing.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; 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 6ae733c..da46682 100644
--- a/pjlib/src/pjlib-test/os.c
+++ b/pjlib/src/pjlib-test/os.c
@@ -1,3 +1,24 @@
-/* $Id$
- */
-int dummy_os_var;
+/* $Id$
+ */
+/*
+ * PJLIB - PJ Foundation Library
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ * Benny Prijono <bennylp@bulukucing.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; 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 c07d6a7..7541475 100644
--- a/pjlib/src/pjlib-test/pool.c
+++ b/pjlib/src/pjlib-test/pool.c
@@ -1,156 +1,177 @@
-/* $Id$
- */
-#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$
+ */
+/*
+ * PJLIB - PJ Foundation Library
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ * Benny Prijono <bennylp@bulukucing.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; 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 7482710..d5c55a6 100644
--- a/pjlib/src/pjlib-test/pool_perf.c
+++ b/pjlib/src/pjlib-test/pool_perf.c
@@ -1,124 +1,145 @@
-/* $Id$
- */
-#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$
+ */
+/*
+ * PJLIB - PJ Foundation Library
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ * Benny Prijono <bennylp@bulukucing.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; 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 55efa86..d39089d 100644
--- a/pjlib/src/pjlib-test/rand.c
+++ b/pjlib/src/pjlib-test/rand.c
@@ -1,37 +1,58 @@
-/* $Id$
- */
-#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$
+ */
+/*
+ * PJLIB - PJ Foundation Library
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ * Benny Prijono <bennylp@bulukucing.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; 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 475ce0c..c7324ca 100644
--- a/pjlib/src/pjlib-test/rbtree.c
+++ b/pjlib/src/pjlib-test/rbtree.c
@@ -1,151 +1,172 @@
-/* $Id$
- */
-#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$
+ */
+/*
+ * PJLIB - PJ Foundation Library
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ * Benny Prijono <bennylp@bulukucing.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; 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 4d1fe1b..6ebc76a 100644
--- a/pjlib/src/pjlib-test/select.c
+++ b/pjlib/src/pjlib-test/select.c
@@ -1,201 +1,222 @@
-/* $Id$
- */
-#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$
+ */
+/*
+ * PJLIB - PJ Foundation Library
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ * Benny Prijono <bennylp@bulukucing.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; 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 6281e4d..07b5696 100644
--- a/pjlib/src/pjlib-test/sleep.c
+++ b/pjlib/src/pjlib-test/sleep.c
@@ -1,184 +1,205 @@
-/* $Id$
- */
-#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$
+ */
+/*
+ * PJLIB - PJ Foundation Library
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ * Benny Prijono <bennylp@bulukucing.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; 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 56da4be..3a635b1 100644
--- a/pjlib/src/pjlib-test/sock.c
+++ b/pjlib/src/pjlib-test/sock.c
@@ -1,440 +1,461 @@
-/* $Id$
- */
-#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$
+ */
+/*
+ * PJLIB - PJ Foundation Library
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ * Benny Prijono <bennylp@bulukucing.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; 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 dfc480c..c19b8ab 100644
--- a/pjlib/src/pjlib-test/sock_perf.c
+++ b/pjlib/src/pjlib-test/sock_perf.c
@@ -1,167 +1,188 @@
-/* $Id$
- */
-#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$
+ */
+/*
+ * PJLIB - PJ Foundation Library
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ * Benny Prijono <bennylp@bulukucing.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; 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 5f1f13f..99358d3 100644
--- a/pjlib/src/pjlib-test/string.c
+++ b/pjlib/src/pjlib-test/string.c
@@ -1,158 +1,179 @@
-/* $Id$
- */
-#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$
+ */
+/*
+ * PJLIB - PJ Foundation Library
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ * Benny Prijono <bennylp@bulukucing.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; 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 c9349a2..9c69a77 100644
--- a/pjlib/src/pjlib-test/test.c
+++ b/pjlib/src/pjlib-test/test.c
@@ -1,183 +1,204 @@
-/* $Id$
- */
-#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$
+ */
+/*
+ * PJLIB - PJ Foundation Library
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ * Benny Prijono <bennylp@bulukucing.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; 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 1759290..0ae010c 100644
--- a/pjlib/src/pjlib-test/test.h
+++ b/pjlib/src/pjlib-test/test.h
@@ -1,95 +1,116 @@
-/* $Id$
- */
-#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$
+ */
+/*
+ * PJLIB - PJ Foundation Library
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ * Benny Prijono <bennylp@bulukucing.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; 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 b35d9a5..a1c1bef 100644
--- a/pjlib/src/pjlib-test/thread.c
+++ b/pjlib/src/pjlib-test/thread.c
@@ -1,273 +1,294 @@
-/* $Id$
- */
-#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$
+ */
+/*
+ * PJLIB - PJ Foundation Library
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ * Benny Prijono <bennylp@bulukucing.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; 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 c6922cf..4f3d4b8 100644
--- a/pjlib/src/pjlib-test/timer.c
+++ b/pjlib/src/pjlib-test/timer.c
@@ -1,170 +1,191 @@
-/* $Id$
- */
-#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, 0, &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$
+ */
+/*
+ * PJLIB - PJ Foundation Library
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ * Benny Prijono <bennylp@bulukucing.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; 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 deeab05..347d256 100644
--- a/pjlib/src/pjlib-test/timestamp.c
+++ b/pjlib/src/pjlib-test/timestamp.c
@@ -1,127 +1,148 @@
-/* $Id$
- */
-#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$
+ */
+/*
+ * PJLIB - PJ Foundation Library
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ * Benny Prijono <bennylp@bulukucing.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; 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 981ff30..d9b746d 100644
--- a/pjlib/src/pjlib-test/udp_echo_srv_ioqueue.c
+++ b/pjlib/src/pjlib-test/udp_echo_srv_ioqueue.c
@@ -1,5 +1,26 @@
/* $Id$
*/
+/*
+ * PJLIB - PJ Foundation Library
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ * Benny Prijono <bennylp@bulukucing.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; 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"
diff --git a/pjlib/src/pjlib-test/udp_echo_srv_sync.c b/pjlib/src/pjlib-test/udp_echo_srv_sync.c
index b1a59e4..c229e61 100644
--- a/pjlib/src/pjlib-test/udp_echo_srv_sync.c
+++ b/pjlib/src/pjlib-test/udp_echo_srv_sync.c
@@ -1,147 +1,168 @@
-/* $Id$
- */
-#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$
+ */
+/*
+ * PJLIB - PJ Foundation Library
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ * Benny Prijono <bennylp@bulukucing.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; 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 2d98571..44281e2 100644
--- a/pjlib/src/pjlib-test/util.c
+++ b/pjlib/src/pjlib-test/util.c
@@ -1,115 +1,136 @@
-/* $Id$
- */
-#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$
+ */
+/*
+ * PJLIB - PJ Foundation Library
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ * Benny Prijono <bennylp@bulukucing.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; 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/src/pjmedia.h b/pjmedia/src/pjmedia.h
index 627d15c..534e0f2 100644
--- a/pjmedia/src/pjmedia.h
+++ b/pjmedia/src/pjmedia.h
@@ -1,17 +1,38 @@
-/* $Id$
- *
- */
-#ifndef __PJMEDIA_H__
-#define __PJMEDIA_H__
-
-#include <pjmedia/codec.h>
-#include <pjmedia/jbuf.h>
-#include <pjmedia/mediamgr.h>
-#include <pjmedia/rtcp.h>
-#include <pjmedia/rtp.h>
-#include <pjmedia/session.h>
-#include <pjmedia/sound.h>
-#include <pjmedia/sdp.h>
-
-#endif /* __PJMEDIA_H__ */
-
+/* $Id$
+ *
+ */
+/*
+ * PJMEDIA - Multimedia over IP Stack
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ * Benny Prijono <bennylp@bulukucing.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#ifndef __PJMEDIA_H__
+#define __PJMEDIA_H__
+
+#include <pjmedia/codec.h>
+#include <pjmedia/jbuf.h>
+#include <pjmedia/mediamgr.h>
+#include <pjmedia/rtcp.h>
+#include <pjmedia/rtp.h>
+#include <pjmedia/session.h>
+#include <pjmedia/sound.h>
+#include <pjmedia/sdp.h>
+
+#endif /* __PJMEDIA_H__ */
+
diff --git a/pjmedia/src/pjmedia/codec.c b/pjmedia/src/pjmedia/codec.c
index d64ac05..c1e12f9 100644
--- a/pjmedia/src/pjmedia/codec.c
+++ b/pjmedia/src/pjmedia/codec.c
@@ -1,91 +1,112 @@
-/* $Id$
- *
- */
-#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$
+ *
+ */
+/*
+ * PJMEDIA - Multimedia over IP Stack
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ * Benny Prijono <bennylp@bulukucing.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; 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 fcf2384..bf20d19 100644
--- a/pjmedia/src/pjmedia/codec.h
+++ b/pjmedia/src/pjmedia/codec.h
@@ -1,339 +1,360 @@
-/* $Id$
- *
- */
-
-#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$
+ *
+ */
+/*
+ * PJMEDIA - Multimedia over IP Stack
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ * Benny Prijono <bennylp@bulukucing.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; 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 cc38892..757a8dc 100644
--- a/pjmedia/src/pjmedia/config.h
+++ b/pjmedia/src/pjmedia/config.h
@@ -1,13 +1,34 @@
-/* $Id$
- *
- */
-
-#ifndef __PJMED_CONFIG_H__
-#define __PJMED_CONFIG_H__
-
-/**
- * @defgroup PJMEDIA Media Stack
- */
-
-
-#endif /* __PJMED_CONFIG_H__ */
+/* $Id$
+ *
+ */
+/*
+ * PJMEDIA - Multimedia over IP Stack
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ * Benny Prijono <bennylp@bulukucing.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; 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 6f3b67d..22b0a5d 100644
--- a/pjmedia/src/pjmedia/dsound.c
+++ b/pjmedia/src/pjmedia/dsound.c
@@ -1,863 +1,884 @@
-/* $Id$
- *
- */
-
-#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, ¶m,
- 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$
+ *
+ */
+/*
+ * PJMEDIA - Multimedia over IP Stack
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ * Benny Prijono <bennylp@bulukucing.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; 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, ¶m,
+ 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 e8ff968..e953170 100644
--- a/pjmedia/src/pjmedia/g711.c
+++ b/pjmedia/src/pjmedia/g711.c
@@ -1,602 +1,623 @@
-/* $Id$
- *
- */
-/* This file contains file from Sun Microsystems, Inc, with the complete
- * copyright 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$
+ *
+ */
+/*
+ * PJMEDIA - Multimedia over IP Stack
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ * Benny Prijono <bennylp@bulukucing.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; 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
+ * copyright 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 0ed2200..d35fad3 100644
--- a/pjmedia/src/pjmedia/jbuf.c
+++ b/pjmedia/src/pjmedia/jbuf.c
@@ -1,406 +1,427 @@
-/* $Id$
- *
- */
-
-#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$
+ *
+ */
+/*
+ * PJMEDIA - Multimedia over IP Stack
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ * Benny Prijono <bennylp@bulukucing.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; 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 fc4325b..9229b41 100644
--- a/pjmedia/src/pjmedia/jbuf.h
+++ b/pjmedia/src/pjmedia/jbuf.h
@@ -1,128 +1,149 @@
-/* $Id$
- *
- */
-
-#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$
+ *
+ */
+/*
+ * PJMEDIA - Multimedia over IP Stack
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ * Benny Prijono <bennylp@bulukucing.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; 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 79fd108..b87d650 100644
--- a/pjmedia/src/pjmedia/mediamgr.c
+++ b/pjmedia/src/pjmedia/mediamgr.c
@@ -1,97 +1,118 @@
-/* $Id$
- *
- */
-#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$
+ *
+ */
+/*
+ * PJMEDIA - Multimedia over IP Stack
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ * Benny Prijono <bennylp@bulukucing.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; 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 df4ee63..834cf36 100644
--- a/pjmedia/src/pjmedia/mediamgr.h
+++ b/pjmedia/src/pjmedia/mediamgr.h
@@ -1,86 +1,107 @@
-/* $Id$
- *
- */
-
-#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$
+ *
+ */
+/*
+ * PJMEDIA - Multimedia over IP Stack
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ * Benny Prijono <bennylp@bulukucing.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; 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 959b800..dc0b373 100644
--- a/pjmedia/src/pjmedia/nullsound.c
+++ b/pjmedia/src/pjmedia/nullsound.c
@@ -1,112 +1,133 @@
-/* $Id$
- *
- */
-#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$
+ *
+ */
+/*
+ * PJMEDIA - Multimedia over IP Stack
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ * Benny Prijono <bennylp@bulukucing.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; 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 60065b3..9eb2f1a 100644
--- a/pjmedia/src/pjmedia/pasound.c
+++ b/pjmedia/src/pjmedia/pasound.c
@@ -1,389 +1,410 @@
-/* $Id$
- *
- */
-#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$
+ *
+ */
+/*
+ * PJMEDIA - Multimedia over IP Stack
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ * Benny Prijono <bennylp@bulukucing.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; 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/dsound_wrapper.c b/pjmedia/src/pjmedia/portaudio/dsound_wrapper.c
index 207d287..782e824 100644
--- a/pjmedia/src/pjmedia/portaudio/dsound_wrapper.c
+++ b/pjmedia/src/pjmedia/portaudio/dsound_wrapper.c
@@ -1,616 +1,637 @@
-/*
- * $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( ¤tTime );
- 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.
+ *
+ */
+/*
+ * PJMEDIA - Multimedia over IP Stack
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ * Benny Prijono <bennylp@bulukucing.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#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( ¤tTime );
+ 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 9e3f565..e8954c1 100644
--- a/pjmedia/src/pjmedia/portaudio/dsound_wrapper.h
+++ b/pjmedia/src/pjmedia/portaudio/dsound_wrapper.h
@@ -1,130 +1,151 @@
-#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 */
+/*
+ * PJMEDIA - Multimedia over IP Stack
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ * Benny Prijono <bennylp@bulukucing.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#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 035b4d0..d9f068c 100644
--- a/pjmedia/src/pjmedia/portaudio/pa_allocation.c
+++ b/pjmedia/src/pjmedia/portaudio/pa_allocation.c
@@ -1,234 +1,255 @@
-/*
- * $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.
+ */
+/*
+ * PJMEDIA - Multimedia over IP Stack
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ * Benny Prijono <bennylp@bulukucing.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+/** @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 fb9321a..788a8ae 100644
--- a/pjmedia/src/pjmedia/portaudio/pa_allocation.h
+++ b/pjmedia/src/pjmedia/portaudio/pa_allocation.h
@@ -1,95 +1,116 @@
-#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 */
+/*
+ * PJMEDIA - Multimedia over IP Stack
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ * Benny Prijono <bennylp@bulukucing.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#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 4ee73c9..33fe6a8 100644
--- a/pjmedia/src/pjmedia/portaudio/pa_converters.c
+++ b/pjmedia/src/pjmedia/portaudio/pa_converters.c
@@ -1,1926 +1,1947 @@
-/*
- * $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.
+ */
+/*
+ * PJMEDIA - Multimedia over IP Stack
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ * Benny Prijono <bennylp@bulukucing.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+/** @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 831c9c6..b196293 100644
--- a/pjmedia/src/pjmedia/portaudio/pa_converters.h
+++ b/pjmedia/src/pjmedia/portaudio/pa_converters.h
@@ -1,254 +1,275 @@
-#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 */
+/*
+ * PJMEDIA - Multimedia over IP Stack
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ * Benny Prijono <bennylp@bulukucing.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#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 e70fbf4..2def09d 100644
--- a/pjmedia/src/pjmedia/portaudio/pa_cpuload.c
+++ b/pjmedia/src/pjmedia/portaudio/pa_cpuload.c
@@ -1,96 +1,117 @@
-/*
- * $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.
+ */
+/*
+ * PJMEDIA - Multimedia over IP Stack
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ * Benny Prijono <bennylp@bulukucing.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+/** @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 f77d919..fd7988b 100644
--- a/pjmedia/src/pjmedia/portaudio/pa_cpuload.h
+++ b/pjmedia/src/pjmedia/portaudio/pa_cpuload.h
@@ -1,63 +1,84 @@
-#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 */
+/*
+ * PJMEDIA - Multimedia over IP Stack
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ * Benny Prijono <bennylp@bulukucing.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#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 0600db6..0c20a95 100644
--- a/pjmedia/src/pjmedia/portaudio/pa_dither.c
+++ b/pjmedia/src/pjmedia/portaudio/pa_dither.c
@@ -1,204 +1,225 @@
-/*
- * $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.
+ */
+/*
+ * PJMEDIA - Multimedia over IP Stack
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ * Benny Prijono <bennylp@bulukucing.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+/** @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 70369e1..24bbe2a 100644
--- a/pjmedia/src/pjmedia/portaudio/pa_dither.h
+++ b/pjmedia/src/pjmedia/portaudio/pa_dither.h
@@ -1,91 +1,112 @@
-#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 */
+/*
+ * PJMEDIA - Multimedia over IP Stack
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ * Benny Prijono <bennylp@bulukucing.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#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 052bced..2b546c3 100644
--- a/pjmedia/src/pjmedia/portaudio/pa_endianness.h
+++ b/pjmedia/src/pjmedia/portaudio/pa_endianness.h
@@ -1,111 +1,132 @@
-#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 */
+/*
+ * PJMEDIA - Multimedia over IP Stack
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ * Benny Prijono <bennylp@bulukucing.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#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 3a267bb..1790582 100644
--- a/pjmedia/src/pjmedia/portaudio/pa_front.c
+++ b/pjmedia/src/pjmedia/portaudio/pa_front.c
@@ -1,1976 +1,1997 @@
-/*
- * $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.
+ */
+/*
+ * PJMEDIA - Multimedia over IP Stack
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ * Benny Prijono <bennylp@bulukucing.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+/* 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 d055070..aee0635 100644
--- a/pjmedia/src/pjmedia/portaudio/pa_hostapi.h
+++ b/pjmedia/src/pjmedia/portaudio/pa_hostapi.h
@@ -1,244 +1,265 @@
-#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 */
+/*
+ * PJMEDIA - Multimedia over IP Stack
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ * Benny Prijono <bennylp@bulukucing.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#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 3b04716..405ada1 100644
--- a/pjmedia/src/pjmedia/portaudio/pa_linux_alsa.c
+++ b/pjmedia/src/pjmedia/portaudio/pa_linux_alsa.c
@@ -1,3272 +1,3293 @@
-/*
- * $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, ×tamp );
- 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.
+ */
+/*
+ * PJMEDIA - Multimedia over IP Stack
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ * Benny Prijono <bennylp@bulukucing.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#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, ×tamp );
+ 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 e6f44b1..a8d811b 100644
--- a/pjmedia/src/pjmedia/portaudio/pa_linux_alsa.h
+++ b/pjmedia/src/pjmedia/portaudio/pa_linux_alsa.h
@@ -1,64 +1,85 @@
-#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
+/*
+ * PJMEDIA - Multimedia over IP Stack
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ * Benny Prijono <bennylp@bulukucing.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#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 cf711d4..041593b 100644
--- a/pjmedia/src/pjmedia/portaudio/pa_process.c
+++ b/pjmedia/src/pjmedia/portaudio/pa_process.c
@@ -1,1756 +1,1777 @@
-/*
- * $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.
+ */
+/*
+ * PJMEDIA - Multimedia over IP Stack
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ * Benny Prijono <bennylp@bulukucing.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+/** @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 c52e9ea..21ebece 100644
--- a/pjmedia/src/pjmedia/portaudio/pa_process.h
+++ b/pjmedia/src/pjmedia/portaudio/pa_process.h
@@ -1,741 +1,762 @@
-#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 */
+/*
+ * PJMEDIA - Multimedia over IP Stack
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ * Benny Prijono <bennylp@bulukucing.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#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 ebc1f50..5df50c9 100644
--- a/pjmedia/src/pjmedia/portaudio/pa_skeleton.c
+++ b/pjmedia/src/pjmedia/portaudio/pa_skeleton.c
@@ -1,807 +1,828 @@
-/*
- * $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.
+ */
+/*
+ * PJMEDIA - Multimedia over IP Stack
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ * Benny Prijono <bennylp@bulukucing.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+/** @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 044dde2..0df0d06 100644
--- a/pjmedia/src/pjmedia/portaudio/pa_stream.c
+++ b/pjmedia/src/pjmedia/portaudio/pa_stream.c
@@ -1,141 +1,162 @@
-/*
- * $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.
+ */
+/*
+ * PJMEDIA - Multimedia over IP Stack
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ * Benny Prijono <bennylp@bulukucing.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+/** @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 8b86943..9e93429 100644
--- a/pjmedia/src/pjmedia/portaudio/pa_stream.h
+++ b/pjmedia/src/pjmedia/portaudio/pa_stream.h
@@ -1,196 +1,217 @@
-#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 */
+/*
+ * PJMEDIA - Multimedia over IP Stack
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ * Benny Prijono <bennylp@bulukucing.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#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 83514a0..e980e24 100644
--- a/pjmedia/src/pjmedia/portaudio/pa_trace.c
+++ b/pjmedia/src/pjmedia/portaudio/pa_trace.c
@@ -1,88 +1,109 @@
-/*
- * $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.
+ */
+/*
+ * PJMEDIA - Multimedia over IP Stack
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ * Benny Prijono <bennylp@bulukucing.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+/** @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 f72a78b..0ef6794 100644
--- a/pjmedia/src/pjmedia/portaudio/pa_trace.h
+++ b/pjmedia/src/pjmedia/portaudio/pa_trace.h
@@ -1,70 +1,91 @@
-#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 */
+/*
+ * PJMEDIA - Multimedia over IP Stack
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ * Benny Prijono <bennylp@bulukucing.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#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 343dc8c..e8d924f 100644
--- a/pjmedia/src/pjmedia/portaudio/pa_types.h
+++ b/pjmedia/src/pjmedia/portaudio/pa_types.h
@@ -1,65 +1,86 @@
-#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 */
+/*
+ * PJMEDIA - Multimedia over IP Stack
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ * Benny Prijono <bennylp@bulukucing.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#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 9bddc2e..5817af1 100644
--- a/pjmedia/src/pjmedia/portaudio/pa_unix_hostapis.c
+++ b/pjmedia/src/pjmedia/portaudio/pa_unix_hostapis.c
@@ -1,64 +1,85 @@
-/*
- * $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.
+ */
+/*
+ * PJMEDIA - Multimedia over IP Stack
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ * Benny Prijono <bennylp@bulukucing.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+
+#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 2490e68..6597657 100644
--- a/pjmedia/src/pjmedia/portaudio/pa_unix_oss.c
+++ b/pjmedia/src/pjmedia/portaudio/pa_unix_oss.c
@@ -1,1918 +1,1939 @@
-/*
- * $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.
+ */
+/*
+ * PJMEDIA - Multimedia over IP Stack
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ * Benny Prijono <bennylp@bulukucing.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#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 f45848f..55f83f7 100644
--- a/pjmedia/src/pjmedia/portaudio/pa_unix_util.c
+++ b/pjmedia/src/pjmedia/portaudio/pa_unix_util.c
@@ -1,175 +1,196 @@
-/*
- * $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.
+ */
+/*
+ * PJMEDIA - Multimedia over IP Stack
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ * Benny Prijono <bennylp@bulukucing.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+
+#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 01dda01..c51f3bb 100644
--- a/pjmedia/src/pjmedia/portaudio/pa_unix_util.h
+++ b/pjmedia/src/pjmedia/portaudio/pa_unix_util.h
@@ -1,73 +1,94 @@
-#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
+/*
+ * PJMEDIA - Multimedia over IP Stack
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ * Benny Prijono <bennylp@bulukucing.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#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 149fbca..a311d7e 100644
--- a/pjmedia/src/pjmedia/portaudio/pa_util.h
+++ b/pjmedia/src/pjmedia/portaudio/pa_util.h
@@ -1,167 +1,188 @@
-#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 */
+/*
+ * PJMEDIA - Multimedia over IP Stack
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ * Benny Prijono <bennylp@bulukucing.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#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 940867d..eb9d409 100644
--- a/pjmedia/src/pjmedia/portaudio/pa_win_ds.c
+++ b/pjmedia/src/pjmedia/portaudio/pa_win_ds.c
@@ -1,1828 +1,1849 @@
-/*
- * $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.
+ */
+/*
+ * PJMEDIA - Multimedia over IP Stack
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ * Benny Prijono <bennylp@bulukucing.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+/** @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 4e07fee..b95af51 100644
--- a/pjmedia/src/pjmedia/portaudio/pa_win_hostapis.c
+++ b/pjmedia/src/pjmedia/portaudio/pa_win_hostapis.c
@@ -1,86 +1,107 @@
-/*
- * $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.
+ */
+/*
+ * PJMEDIA - Multimedia over IP Stack
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ * Benny Prijono <bennylp@bulukucing.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+/** @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 0395e5c..9f7fd33 100644
--- a/pjmedia/src/pjmedia/portaudio/pa_win_util.c
+++ b/pjmedia/src/pjmedia/portaudio/pa_win_util.c
@@ -1,134 +1,155 @@
-/*
- * $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.
+ */
+/*
+ * PJMEDIA - Multimedia over IP Stack
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ * Benny Prijono <bennylp@bulukucing.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+/** @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 c3d7fe6..82c9078 100644
--- a/pjmedia/src/pjmedia/portaudio/pa_win_wmme.c
+++ b/pjmedia/src/pjmedia/portaudio/pa_win_wmme.c
@@ -1,3623 +1,3644 @@
-/*
- * $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.
+ *
+ */
+/*
+ * PJMEDIA - Multimedia over IP Stack
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ * Benny Prijono <bennylp@bulukucing.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+/* 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 1a71633..ae807e6 100644
--- a/pjmedia/src/pjmedia/portaudio/pa_win_wmme.h
+++ b/pjmedia/src/pjmedia/portaudio/pa_win_wmme.h
@@ -1,160 +1,181 @@
-#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 */
+/*
+ * PJMEDIA - Multimedia over IP Stack
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ * Benny Prijono <bennylp@bulukucing.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#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 3a33540..cbd441f 100644
--- a/pjmedia/src/pjmedia/portaudio/pa_x86_plain_converters.c
+++ b/pjmedia/src/pjmedia/portaudio/pa_x86_plain_converters.c
@@ -1,1167 +1,1188 @@
-#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;
-}
-
-/* -------------------------------------------------------------------------- */
+/*
+ * PJMEDIA - Multimedia over IP Stack
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ * Benny Prijono <bennylp@bulukucing.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#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 f56c710..5aedc51 100644
--- a/pjmedia/src/pjmedia/portaudio/pa_x86_plain_converters.h
+++ b/pjmedia/src/pjmedia/portaudio/pa_x86_plain_converters.h
@@ -1,19 +1,40 @@
-#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 */
+/*
+ * PJMEDIA - Multimedia over IP Stack
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ * Benny Prijono <bennylp@bulukucing.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#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 341d92c..aaf5c80 100644
--- a/pjmedia/src/pjmedia/portaudio/portaudio.h
+++ b/pjmedia/src/pjmedia/portaudio/portaudio.h
@@ -1,1123 +1,1144 @@
-
-#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 */
+/*
+ * PJMEDIA - Multimedia over IP Stack
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ * Benny Prijono <bennylp@bulukucing.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#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/pjmedia/rtcp.c b/pjmedia/src/pjmedia/rtcp.c
index 04a4de5..baa05be 100644
--- a/pjmedia/src/pjmedia/rtcp.c
+++ b/pjmedia/src/pjmedia/rtcp.c
@@ -1,202 +1,223 @@
-/* $Id$
- *
- */
-
-#include <pjmedia/rtcp.h>
-#include <pj/os.h> /* pj_gettimeofday */
-#include <pj/sock.h> /* pj_htonx, pj_ntohx */
-#include <string.h> /* memset */
-
-#define RTCP_SR 200
-#define RTCP_RR 201
-
-
-
-/*
- * Get NTP time.
- */
-static void rtcp_get_ntp_time(struct pj_rtcp_ntp_rec *ntp)
-{
- pj_time_val tv;
-
- pj_gettimeofday(&tv);
-
- ntp->hi = tv.sec;
- tv.msec = tv.msec % 1000;
- ntp->lo = tv.msec * 0xFFFF / 1000;
- ntp->lo <<= 16;
-}
-
-
-PJ_DEF(void) pj_rtcp_init(pj_rtcp_session *s, pj_uint32_t ssrc)
-{
- pj_rtcp_pkt *rtcp_pkt = &s->rtcp_pkt;
-
- memset(rtcp_pkt, 0, sizeof(pj_rtcp_pkt));
-
- /* Init time */
- s->rtcp_lsr.hi = s->rtcp_lsr.lo = 0;
- s->rtcp_lsr_time = 0;
-
- /* Init common RTCP header */
- rtcp_pkt->common.version = 2;
- rtcp_pkt->common.count = 1;
- rtcp_pkt->common.pt = RTCP_SR;
- rtcp_pkt->common.length = pj_htons(12);
-
- /* Init SR */
- rtcp_pkt->sr.ssrc = pj_htonl(ssrc);
-
- /* RR will be initialized on receipt of the first RTP packet. */
-}
-
-PJ_DEF(void) pj_rtcp_fini(pj_rtcp_session *session)
-{
- /* Nothing to do. */
- PJ_UNUSED_ARG(session)
-}
-
-static void rtcp_init_seq(pj_rtcp_session *s, pj_uint16_t seq)
-{
- s->received = 0;
- s->expected_prior = 0;
- s->received_prior = 0;
- s->transit = 0;
- s->jitter = 0;
-
- pj_rtp_seq_restart(&s->seq_ctrl, seq);
-}
-
-PJ_DEF(void) pj_rtcp_rx_rtp(pj_rtcp_session *s, pj_uint16_t seq, pj_uint32_t rtp_ts)
-{
- pj_uint32_t arrival;
- pj_int32_t transit;
- unsigned long timer_tick;
- pj_time_val tv;
- int status;
-
- /* Update sequence numbers (received, lost, etc). */
- status = pj_rtp_seq_update(&s->seq_ctrl, seq);
- if (status == PJ_RTP_ERR_SESSION_RESTARTED) {
- rtcp_init_seq(s, seq);
- status = 0;
- }
-
- if (status != 0)
- return;
-
- ++s->received;
-
- pj_gettimeofday(&tv);
- timer_tick = tv.sec * 1000 + tv.msec;
-
- /*
- * Calculate jitter (s->jitter is in timer tick unit)
- */
- PJ_TODO(SUPPORT_JITTER_CALCULATION_FOR_NON_8KHZ_SAMPLE_RATE)
-
- arrival = timer_tick << 3; // 8 samples per ms.
- transit = arrival - rtp_ts;
-
- if (s->transit == 0) {
- s->transit = transit;
- } else {
- pj_int32_t d, jitter = s->jitter;
-
- d = transit - s->transit;
- s->transit = transit;
- if (d < 0)
- d = -d;
-
- jitter += d - ((jitter + 8) >> 4);
- s->jitter = jitter;
- }
-}
-
-PJ_DEF(void) pj_rtcp_tx_rtp(pj_rtcp_session *s, pj_uint16_t bytes_payload_size)
-{
- pj_rtcp_pkt *rtcp_pkt = &s->rtcp_pkt;
- rtcp_pkt->sr.sender_pcount = pj_htonl( pj_ntohl(rtcp_pkt->sr.sender_pcount) + 1);
- rtcp_pkt->sr.sender_bcount = pj_htonl( pj_ntohl(rtcp_pkt->sr.sender_bcount) + bytes_payload_size );
-}
-
-static void rtcp_build_rtcp(pj_rtcp_session *s, pj_uint32_t receiver_ssrc)
-{
- pj_uint32_t expected;
- pj_uint32_t u32;
- pj_uint32_t expected_interval, received_interval, lost_interval;
- pj_rtcp_pkt *rtcp_pkt = &s->rtcp_pkt;
-
- /* SSRC and last_seq */
- rtcp_pkt->rr.ssrc = pj_htonl(receiver_ssrc);
- rtcp_pkt->rr.last_seq = (s->seq_ctrl.cycles & 0xFFFF0000L);
- rtcp_pkt->rr.last_seq += s->seq_ctrl.max_seq;
- rtcp_pkt->rr.last_seq = pj_htonl(rtcp_pkt->rr.last_seq);
-
- /* Jitter */
- rtcp_pkt->rr.jitter = pj_htonl(s->jitter >> 4);
-
- /* Total lost. */
- expected = pj_ntohl(rtcp_pkt->rr.last_seq) - s->seq_ctrl.base_seq + 1;
- u32 = expected - s->received;
- rtcp_pkt->rr.total_lost_2 = (u32 >> 16) & 0x00FF;
- rtcp_pkt->rr.total_lost_1 = (u32 >> 8) & 0x00FF;
- rtcp_pkt->rr.total_lost_0 = u32 & 0x00FF;
-
- /* Fraction lost calculation */
- expected_interval = expected - s->expected_prior;
- s->expected_prior = expected;
-
- received_interval = s->received - s->received_prior;
- s->received_prior = s->received;
-
- lost_interval = expected_interval - received_interval;
-
- if (expected_interval==0 || lost_interval == 0) {
- rtcp_pkt->rr.fract_lost = 0;
- } else {
- rtcp_pkt->rr.fract_lost = (lost_interval << 8) / expected_interval;
- }
-}
-
-PJ_DEF(void) pj_rtcp_build_rtcp(pj_rtcp_session *session, pj_rtcp_pkt **ret_p_pkt, int *len)
-{
- pj_rtcp_pkt *rtcp_pkt = &session->rtcp_pkt;
- pj_rtcp_ntp_rec ntp;
- pj_time_val now;
-
- rtcp_build_rtcp(session, session->peer_ssrc);
-
- /* Get current NTP time. */
- rtcp_get_ntp_time(&ntp);
-
- /* Fill in NTP timestamp in SR. */
- rtcp_pkt->sr.ntp_sec = pj_htonl(ntp.hi);
- rtcp_pkt->sr.ntp_frac = pj_htonl(ntp.lo);
-
- if (session->rtcp_lsr_time == 0 || session->rtcp_lsr.lo == 0) {
- rtcp_pkt->rr.lsr = 0;
- rtcp_pkt->rr.dlsr = 0;
- } else {
- unsigned msec_elapsed;
-
- /* Fill in LSR.
- LSR is the middle 32bit of the last SR NTP time received.
- */
- rtcp_pkt->rr.lsr = ((session->rtcp_lsr.hi & 0x0000FFFF) << 16) |
- ((session->rtcp_lsr.lo >> 16) & 0xFFFF);
- rtcp_pkt->rr.lsr = pj_htonl(rtcp_pkt->rr.lsr);
-
- /* Fill in DLSR.
- DLSR is Delay since Last SR, in 1/65536 seconds.
- */
- pj_gettimeofday(&now);
- msec_elapsed = (now.msec - session->rtcp_lsr_time);
- rtcp_pkt->rr.dlsr = pj_htonl((msec_elapsed * 65536) / 1000);
- }
-
- /* Return pointer. */
- *ret_p_pkt = rtcp_pkt;
- *len = sizeof(pj_rtcp_pkt);
-}
-
+/* $Id$
+ *
+ */
+/*
+ * PJMEDIA - Multimedia over IP Stack
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ * Benny Prijono <bennylp@bulukucing.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <pjmedia/rtcp.h>
+#include <pj/os.h> /* pj_gettimeofday */
+#include <pj/sock.h> /* pj_htonx, pj_ntohx */
+#include <string.h> /* memset */
+
+#define RTCP_SR 200
+#define RTCP_RR 201
+
+
+
+/*
+ * Get NTP time.
+ */
+static void rtcp_get_ntp_time(struct pj_rtcp_ntp_rec *ntp)
+{
+ pj_time_val tv;
+
+ pj_gettimeofday(&tv);
+
+ ntp->hi = tv.sec;
+ tv.msec = tv.msec % 1000;
+ ntp->lo = tv.msec * 0xFFFF / 1000;
+ ntp->lo <<= 16;
+}
+
+
+PJ_DEF(void) pj_rtcp_init(pj_rtcp_session *s, pj_uint32_t ssrc)
+{
+ pj_rtcp_pkt *rtcp_pkt = &s->rtcp_pkt;
+
+ memset(rtcp_pkt, 0, sizeof(pj_rtcp_pkt));
+
+ /* Init time */
+ s->rtcp_lsr.hi = s->rtcp_lsr.lo = 0;
+ s->rtcp_lsr_time = 0;
+
+ /* Init common RTCP header */
+ rtcp_pkt->common.version = 2;
+ rtcp_pkt->common.count = 1;
+ rtcp_pkt->common.pt = RTCP_SR;
+ rtcp_pkt->common.length = pj_htons(12);
+
+ /* Init SR */
+ rtcp_pkt->sr.ssrc = pj_htonl(ssrc);
+
+ /* RR will be initialized on receipt of the first RTP packet. */
+}
+
+PJ_DEF(void) pj_rtcp_fini(pj_rtcp_session *session)
+{
+ /* Nothing to do. */
+ PJ_UNUSED_ARG(session)
+}
+
+static void rtcp_init_seq(pj_rtcp_session *s, pj_uint16_t seq)
+{
+ s->received = 0;
+ s->expected_prior = 0;
+ s->received_prior = 0;
+ s->transit = 0;
+ s->jitter = 0;
+
+ pj_rtp_seq_restart(&s->seq_ctrl, seq);
+}
+
+PJ_DEF(void) pj_rtcp_rx_rtp(pj_rtcp_session *s, pj_uint16_t seq, pj_uint32_t rtp_ts)
+{
+ pj_uint32_t arrival;
+ pj_int32_t transit;
+ unsigned long timer_tick;
+ pj_time_val tv;
+ int status;
+
+ /* Update sequence numbers (received, lost, etc). */
+ status = pj_rtp_seq_update(&s->seq_ctrl, seq);
+ if (status == PJ_RTP_ERR_SESSION_RESTARTED) {
+ rtcp_init_seq(s, seq);
+ status = 0;
+ }
+
+ if (status != 0)
+ return;
+
+ ++s->received;
+
+ pj_gettimeofday(&tv);
+ timer_tick = tv.sec * 1000 + tv.msec;
+
+ /*
+ * Calculate jitter (s->jitter is in timer tick unit)
+ */
+ PJ_TODO(SUPPORT_JITTER_CALCULATION_FOR_NON_8KHZ_SAMPLE_RATE)
+
+ arrival = timer_tick << 3; // 8 samples per ms.
+ transit = arrival - rtp_ts;
+
+ if (s->transit == 0) {
+ s->transit = transit;
+ } else {
+ pj_int32_t d, jitter = s->jitter;
+
+ d = transit - s->transit;
+ s->transit = transit;
+ if (d < 0)
+ d = -d;
+
+ jitter += d - ((jitter + 8) >> 4);
+ s->jitter = jitter;
+ }
+}
+
+PJ_DEF(void) pj_rtcp_tx_rtp(pj_rtcp_session *s, pj_uint16_t bytes_payload_size)
+{
+ pj_rtcp_pkt *rtcp_pkt = &s->rtcp_pkt;
+ rtcp_pkt->sr.sender_pcount = pj_htonl( pj_ntohl(rtcp_pkt->sr.sender_pcount) + 1);
+ rtcp_pkt->sr.sender_bcount = pj_htonl( pj_ntohl(rtcp_pkt->sr.sender_bcount) + bytes_payload_size );
+}
+
+static void rtcp_build_rtcp(pj_rtcp_session *s, pj_uint32_t receiver_ssrc)
+{
+ pj_uint32_t expected;
+ pj_uint32_t u32;
+ pj_uint32_t expected_interval, received_interval, lost_interval;
+ pj_rtcp_pkt *rtcp_pkt = &s->rtcp_pkt;
+
+ /* SSRC and last_seq */
+ rtcp_pkt->rr.ssrc = pj_htonl(receiver_ssrc);
+ rtcp_pkt->rr.last_seq = (s->seq_ctrl.cycles & 0xFFFF0000L);
+ rtcp_pkt->rr.last_seq += s->seq_ctrl.max_seq;
+ rtcp_pkt->rr.last_seq = pj_htonl(rtcp_pkt->rr.last_seq);
+
+ /* Jitter */
+ rtcp_pkt->rr.jitter = pj_htonl(s->jitter >> 4);
+
+ /* Total lost. */
+ expected = pj_ntohl(rtcp_pkt->rr.last_seq) - s->seq_ctrl.base_seq + 1;
+ u32 = expected - s->received;
+ rtcp_pkt->rr.total_lost_2 = (u32 >> 16) & 0x00FF;
+ rtcp_pkt->rr.total_lost_1 = (u32 >> 8) & 0x00FF;
+ rtcp_pkt->rr.total_lost_0 = u32 & 0x00FF;
+
+ /* Fraction lost calculation */
+ expected_interval = expected - s->expected_prior;
+ s->expected_prior = expected;
+
+ received_interval = s->received - s->received_prior;
+ s->received_prior = s->received;
+
+ lost_interval = expected_interval - received_interval;
+
+ if (expected_interval==0 || lost_interval == 0) {
+ rtcp_pkt->rr.fract_lost = 0;
+ } else {
+ rtcp_pkt->rr.fract_lost = (lost_interval << 8) / expected_interval;
+ }
+}
+
+PJ_DEF(void) pj_rtcp_build_rtcp(pj_rtcp_session *session, pj_rtcp_pkt **ret_p_pkt, int *len)
+{
+ pj_rtcp_pkt *rtcp_pkt = &session->rtcp_pkt;
+ pj_rtcp_ntp_rec ntp;
+ pj_time_val now;
+
+ rtcp_build_rtcp(session, session->peer_ssrc);
+
+ /* Get current NTP time. */
+ rtcp_get_ntp_time(&ntp);
+
+ /* Fill in NTP timestamp in SR. */
+ rtcp_pkt->sr.ntp_sec = pj_htonl(ntp.hi);
+ rtcp_pkt->sr.ntp_frac = pj_htonl(ntp.lo);
+
+ if (session->rtcp_lsr_time == 0 || session->rtcp_lsr.lo == 0) {
+ rtcp_pkt->rr.lsr = 0;
+ rtcp_pkt->rr.dlsr = 0;
+ } else {
+ unsigned msec_elapsed;
+
+ /* Fill in LSR.
+ LSR is the middle 32bit of the last SR NTP time received.
+ */
+ rtcp_pkt->rr.lsr = ((session->rtcp_lsr.hi & 0x0000FFFF) << 16) |
+ ((session->rtcp_lsr.lo >> 16) & 0xFFFF);
+ rtcp_pkt->rr.lsr = pj_htonl(rtcp_pkt->rr.lsr);
+
+ /* Fill in DLSR.
+ DLSR is Delay since Last SR, in 1/65536 seconds.
+ */
+ pj_gettimeofday(&now);
+ msec_elapsed = (now.msec - session->rtcp_lsr_time);
+ rtcp_pkt->rr.dlsr = pj_htonl((msec_elapsed * 65536) / 1000);
+ }
+
+ /* Return pointer. */
+ *ret_p_pkt = rtcp_pkt;
+ *len = sizeof(pj_rtcp_pkt);
+}
+
diff --git a/pjmedia/src/pjmedia/rtcp.h b/pjmedia/src/pjmedia/rtcp.h
index d3d2baa..fbe42d9 100644
--- a/pjmedia/src/pjmedia/rtcp.h
+++ b/pjmedia/src/pjmedia/rtcp.h
@@ -1,178 +1,199 @@
-/* $Id$
- *
- */
-
-#ifndef __PJMEDIA_RTCP_H__
-#define __PJMEDIA_RTCP_H__
-
-/**
- * @file rtcp.h
- * @brief RTCP implementation.
- */
-
-#include <pj/types.h>
-#include <pjmedia/rtp.h>
-
-PJ_BEGIN_DECL
-
-
-/**
- * @defgroup PJMED_RTCP RTCP
- * @ingroup PJMEDIA
- * @{
- */
-
-/**
- * RTCP sender report.
- */
-struct pj_rtcp_sr
-{
- pj_uint32_t ssrc;
- pj_uint32_t ntp_sec;
- pj_uint32_t ntp_frac;
- pj_uint32_t rtp_ts;
- pj_uint32_t sender_pcount;
- pj_uint32_t sender_bcount;
-};
-
-typedef struct pj_rtcp_sr pj_rtcp_sr;
-
-/**
- * RTCP receiver report.
- */
-struct pj_rtcp_rr
-{
- pj_uint32_t ssrc;
-#if defined(PJ_IS_BIG_ENDIAN) && PJ_IS_BIG_ENDIAN!=0
- pj_uint32_t fract_lost:8;
- pj_uint32_t total_lost_2:8;
- pj_uint32_t total_lost_1:8;
- pj_uint32_t total_lost_0:8;
-#else
- pj_uint32_t fract_lost:8;
- pj_uint32_t total_lost_0:8;
- pj_uint32_t total_lost_1:8;
- pj_uint32_t total_lost_2:8;
-#endif
- pj_uint32_t last_seq;
- pj_uint32_t jitter;
- pj_uint32_t lsr;
- pj_uint32_t dlsr;
-};
-
-typedef struct pj_rtcp_rr pj_rtcp_rr;
-
-/**
- * RTCP common header.
- */
-struct pj_rtcp_common
-{
-#if defined(PJ_IS_BIG_ENDIAN) && PJ_IS_BIG_ENDIAN!=0
- unsigned version:2; /* packet type */
- unsigned p:1; /* padding flag */
- unsigned count:5; /* varies by payload type */
- unsigned pt:8; /* payload type */
-#else
- unsigned count:5; /* varies by payload type */
- unsigned p:1; /* padding flag */
- unsigned version:2; /* packet type */
- unsigned pt:8; /* payload type */
-#endif
- pj_uint16_t length; /* packet length */
-};
-
-typedef struct pj_rtcp_common pj_rtcp_common;
-
-/**
- * RTCP packet.
- */
-struct pj_rtcp_pkt
-{
- pj_rtcp_common common;
- pj_rtcp_sr sr;
- pj_rtcp_rr rr; /* variable-length list */
-};
-
-typedef struct pj_rtcp_pkt pj_rtcp_pkt;
-
-/**
- * NTP time representation.
- */
-struct pj_rtcp_ntp_rec
-{
- pj_uint32_t hi;
- pj_uint32_t lo;
-};
-
-typedef struct pj_rtcp_ntp_rec pj_rtcp_ntp_rec;
-
-/**
- * RTCP session.
- */
-struct pj_rtcp_session
-{
- pj_rtcp_pkt rtcp_pkt;
-
- pj_rtp_seq_session seq_ctrl;
-
- pj_uint32_t received; /* packets received */
- pj_uint32_t expected_prior; /* packet expected at last interval */
- pj_uint32_t received_prior; /* packet received at last interval */
- pj_int32_t transit; /* relative trans time for prev pkt */
- pj_uint32_t jitter; /* estimated jitter */
-
- pj_rtcp_ntp_rec rtcp_lsr; /* NTP timestamp in last sender report received */
- unsigned rtcp_lsr_time; /* Time when last RTCP SR is received. */
- unsigned peer_ssrc; /* Peer SSRC */
-
-};
-
-typedef struct pj_rtcp_session pj_rtcp_session;
-
-/**
- * Init RTCP session.
- * @param session The session
- * @param ssrc The SSRC used in to identify the session.
- */
-PJ_DECL(void) pj_rtcp_init( pj_rtcp_session *session, pj_uint32_t ssrc );
-
-/**
- * Deinit RTCP session.
- * @param session The session.
- */
-PJ_DECL(void) pj_rtcp_fini( pj_rtcp_session *session);
-
-/**
- * Call this function everytime an RTP packet is received to let the RTCP
- * session do its internal calculations.
- * @param session The session.
- * @param seq The RTP packet sequence number, in host byte order.
- * @param ts The RTP packet timestamp, in host byte order.
- */
-PJ_DECL(void) pj_rtcp_rx_rtp( pj_rtcp_session *session, pj_uint16_t seq, pj_uint32_t ts );
-
-/**
- * Call this function everytime an RTP packet is sent to let the RTCP session
- * do its internal calculations.
- * @param session The session.
- * @param bytes_payload_size The payload size of the RTP packet (ie packet minus
- * RTP header).
- */
-PJ_DECL(void) pj_rtcp_tx_rtp( pj_rtcp_session *session, pj_uint16_t bytes_payload_size );
-
-/**
- * Build a RTCP SR/RR packet to be transmitted to remote RTP peer.
- * @param session The session.
- * @param rtcp_pkt [output] Upon return, it will contain pointer to the RTCP packet.
- * @param len [output] Upon return, it will indicate the size of the RTCP packet.
- */
-PJ_DECL(void) pj_rtcp_build_rtcp( pj_rtcp_session *session, pj_rtcp_pkt **rtcp_pkt, int *len );
-
-/**
- * @}
- */
-
-PJ_END_DECL
-
-
-#endif /* __PJMEDIA_RTCP_H__ */
+/* $Id$
+ *
+ */
+/*
+ * PJMEDIA - Multimedia over IP Stack
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ * Benny Prijono <bennylp@bulukucing.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef __PJMEDIA_RTCP_H__
+#define __PJMEDIA_RTCP_H__
+
+/**
+ * @file rtcp.h
+ * @brief RTCP implementation.
+ */
+
+#include <pj/types.h>
+#include <pjmedia/rtp.h>
+
+PJ_BEGIN_DECL
+
+
+/**
+ * @defgroup PJMED_RTCP RTCP
+ * @ingroup PJMEDIA
+ * @{
+ */
+
+/**
+ * RTCP sender report.
+ */
+struct pj_rtcp_sr
+{
+ pj_uint32_t ssrc;
+ pj_uint32_t ntp_sec;
+ pj_uint32_t ntp_frac;
+ pj_uint32_t rtp_ts;
+ pj_uint32_t sender_pcount;
+ pj_uint32_t sender_bcount;
+};
+
+typedef struct pj_rtcp_sr pj_rtcp_sr;
+
+/**
+ * RTCP receiver report.
+ */
+struct pj_rtcp_rr
+{
+ pj_uint32_t ssrc;
+#if defined(PJ_IS_BIG_ENDIAN) && PJ_IS_BIG_ENDIAN!=0
+ pj_uint32_t fract_lost:8;
+ pj_uint32_t total_lost_2:8;
+ pj_uint32_t total_lost_1:8;
+ pj_uint32_t total_lost_0:8;
+#else
+ pj_uint32_t fract_lost:8;
+ pj_uint32_t total_lost_0:8;
+ pj_uint32_t total_lost_1:8;
+ pj_uint32_t total_lost_2:8;
+#endif
+ pj_uint32_t last_seq;
+ pj_uint32_t jitter;
+ pj_uint32_t lsr;
+ pj_uint32_t dlsr;
+};
+
+typedef struct pj_rtcp_rr pj_rtcp_rr;
+
+/**
+ * RTCP common header.
+ */
+struct pj_rtcp_common
+{
+#if defined(PJ_IS_BIG_ENDIAN) && PJ_IS_BIG_ENDIAN!=0
+ unsigned version:2; /* packet type */
+ unsigned p:1; /* padding flag */
+ unsigned count:5; /* varies by payload type */
+ unsigned pt:8; /* payload type */
+#else
+ unsigned count:5; /* varies by payload type */
+ unsigned p:1; /* padding flag */
+ unsigned version:2; /* packet type */
+ unsigned pt:8; /* payload type */
+#endif
+ pj_uint16_t length; /* packet length */
+};
+
+typedef struct pj_rtcp_common pj_rtcp_common;
+
+/**
+ * RTCP packet.
+ */
+struct pj_rtcp_pkt
+{
+ pj_rtcp_common common;
+ pj_rtcp_sr sr;
+ pj_rtcp_rr rr; /* variable-length list */
+};
+
+typedef struct pj_rtcp_pkt pj_rtcp_pkt;
+
+/**
+ * NTP time representation.
+ */
+struct pj_rtcp_ntp_rec
+{
+ pj_uint32_t hi;
+ pj_uint32_t lo;
+};
+
+typedef struct pj_rtcp_ntp_rec pj_rtcp_ntp_rec;
+
+/**
+ * RTCP session.
+ */
+struct pj_rtcp_session
+{
+ pj_rtcp_pkt rtcp_pkt;
+
+ pj_rtp_seq_session seq_ctrl;
+
+ pj_uint32_t received; /* packets received */
+ pj_uint32_t expected_prior; /* packet expected at last interval */
+ pj_uint32_t received_prior; /* packet received at last interval */
+ pj_int32_t transit; /* relative trans time for prev pkt */
+ pj_uint32_t jitter; /* estimated jitter */
+
+ pj_rtcp_ntp_rec rtcp_lsr; /* NTP timestamp in last sender report received */
+ unsigned rtcp_lsr_time; /* Time when last RTCP SR is received. */
+ unsigned peer_ssrc; /* Peer SSRC */
+
+};
+
+typedef struct pj_rtcp_session pj_rtcp_session;
+
+/**
+ * Init RTCP session.
+ * @param session The session
+ * @param ssrc The SSRC used in to identify the session.
+ */
+PJ_DECL(void) pj_rtcp_init( pj_rtcp_session *session, pj_uint32_t ssrc );
+
+/**
+ * Deinit RTCP session.
+ * @param session The session.
+ */
+PJ_DECL(void) pj_rtcp_fini( pj_rtcp_session *session);
+
+/**
+ * Call this function everytime an RTP packet is received to let the RTCP
+ * session do its internal calculations.
+ * @param session The session.
+ * @param seq The RTP packet sequence number, in host byte order.
+ * @param ts The RTP packet timestamp, in host byte order.
+ */
+PJ_DECL(void) pj_rtcp_rx_rtp( pj_rtcp_session *session, pj_uint16_t seq, pj_uint32_t ts );
+
+/**
+ * Call this function everytime an RTP packet is sent to let the RTCP session
+ * do its internal calculations.
+ * @param session The session.
+ * @param bytes_payload_size The payload size of the RTP packet (ie packet minus
+ * RTP header).
+ */
+PJ_DECL(void) pj_rtcp_tx_rtp( pj_rtcp_session *session, pj_uint16_t bytes_payload_size );
+
+/**
+ * Build a RTCP SR/RR packet to be transmitted to remote RTP peer.
+ * @param session The session.
+ * @param rtcp_pkt [output] Upon return, it will contain pointer to the RTCP packet.
+ * @param len [output] Upon return, it will indicate the size of the RTCP packet.
+ */
+PJ_DECL(void) pj_rtcp_build_rtcp( pj_rtcp_session *session, pj_rtcp_pkt **rtcp_pkt, int *len );
+
+/**
+ * @}
+ */
+
+PJ_END_DECL
+
+
+#endif /* __PJMEDIA_RTCP_H__ */
diff --git a/pjmedia/src/pjmedia/rtp.c b/pjmedia/src/pjmedia/rtp.c
index bbb867b..c1b9286 100644
--- a/pjmedia/src/pjmedia/rtp.c
+++ b/pjmedia/src/pjmedia/rtp.c
@@ -1,247 +1,268 @@
-/* $Id$
- *
- */
-
-#include <pjmedia/rtp.h>
-#include <pj/log.h>
-#include <pj/os.h> /* pj_gettimeofday() */
-#include <pj/sock.h> /* pj_htonx, pj_htonx */
-#include <string.h> /* memset() */
-
-#define THIS_FILE "rtp.c"
-
-#define RTP_VERSION 2
-
-#define RTP_SEQ_MOD (1 << 16)
-#define MAX_DROPOUT ((pj_int16_t)3000)
-#define MAX_MISORDER ((pj_int16_t)100)
-#define MIN_SEQUENTIAL ((pj_int16_t)2)
-
-
-PJ_DEF(pj_status_t) pj_rtp_session_init( pj_rtp_session *ses,
- int default_pt, pj_uint32_t sender_ssrc )
-{
- PJ_LOG(4, (THIS_FILE, "pj_rtp_session_init: ses=%p, default_pt=%d, ssrc=0x%x",
- ses, default_pt, sender_ssrc));
-
- /* Check RTP header packing. */
- if (sizeof(struct pj_rtp_hdr) != 12) {
- pj_assert(!"Wrong RTP header packing!");
- return PJ_RTP_ERR_RTP_PACKING;
- }
-
- /* If sender_ssrc is not specified, create from time value. */
- if (sender_ssrc == 0 || sender_ssrc == (pj_uint32_t)-1) {
- pj_time_val tv;
-
- pj_gettimeofday(&tv);
- sender_ssrc = (pj_uint32_t) pj_htonl(tv.sec);
- } else {
- sender_ssrc = pj_htonl(sender_ssrc);
- }
-
- /* Initialize session. */
- ses->out_extseq = 0;
- ses->peer_ssrc = 0;
-
- /* Sequence number will be initialized when the first RTP packet is receieved. */
-
- /* Build default header for outgoing RTP packet. */
- memset(ses, 0, sizeof(*ses));
- ses->out_hdr.v = RTP_VERSION;
- ses->out_hdr.p = 0;
- ses->out_hdr.x = 0;
- ses->out_hdr.cc = 0;
- ses->out_hdr.m = 0;
- ses->out_hdr.pt = (pj_uint8_t) default_pt;
- ses->out_hdr.seq = (pj_uint16_t) pj_htons( (pj_uint16_t)ses->out_extseq );
- ses->out_hdr.ts = 0;
- ses->out_hdr.ssrc = sender_ssrc;
-
- /* Keep some arguments as session defaults. */
- ses->out_pt = (pj_uint16_t) default_pt;
-
- return PJ_SUCCESS;
-}
-
-
-PJ_DEF(pj_status_t) pj_rtp_encode_rtp( pj_rtp_session *ses, int pt, int m,
- int payload_len, int ts_len,
- const void **rtphdr, int *hdrlen )
-{
- PJ_UNUSED_ARG(payload_len)
-
- PJ_LOG(6, (THIS_FILE,
- "pj_rtp_encode_rtp: ses=%p, pt=%d, m=%d, pt_len=%d, ts_len=%d",
- ses, pt, m, payload_len, ts_len));
-
- /* Update session. */
- ses->out_extseq++;
- ses->out_hdr.ts = pj_htonl(pj_ntohl(ses->out_hdr.ts)+ts_len);
-
- /* Create outgoing header. */
- ses->out_hdr.pt = (pj_uint8_t) ((pt == -1) ? ses->out_pt : pt);
- ses->out_hdr.m = (pj_uint16_t) m;
- ses->out_hdr.seq = pj_htons( (pj_uint16_t) ses->out_extseq);
-
- /* Return values */
- *rtphdr = &ses->out_hdr;
- *hdrlen = sizeof(pj_rtp_hdr);
-
- return PJ_SUCCESS;
-}
-
-
-PJ_DEF(pj_status_t) pj_rtp_decode_rtp( pj_rtp_session *ses,
- const void *pkt, int pkt_len,
- const pj_rtp_hdr **hdr,
- const void **payload,
- unsigned *payloadlen)
-{
- int offset;
-
- PJ_UNUSED_ARG(ses)
-
- PJ_LOG(6, (THIS_FILE,
- "pj_rtp_decode_rtp: ses=%p, pkt=%p, pkt_len=%d",
- ses, pkt, pkt_len));
-
- /* Assume RTP header at the start of packet. We'll verify this later. */
- *hdr = (pj_rtp_hdr*)pkt;
-
- /* Check RTP header sanity. */
- if ((*hdr)->v != RTP_VERSION) {
- PJ_LOG(4, (THIS_FILE, " invalid RTP version!"));
- return PJ_RTP_ERR_INVALID_VERSION;
- }
-
- /* Payload is located right after header plus CSRC */
- offset = sizeof(pj_rtp_hdr) + ((*hdr)->cc * sizeof(pj_uint32_t));
-
- /* Adjust offset if RTP extension is used. */
- if ((*hdr)->x) {
- pj_rtp_ext_hdr *ext = (pj_rtp_ext_hdr*) (((pj_uint8_t*)pkt) + offset);
- offset += (pj_ntohs(ext->length) * sizeof(pj_uint32_t));
- }
-
- /* Check that offset is less than packet size */
- if (offset >= pkt_len)
- return PJ_RTP_ERR_INVALID_PACKET;
-
- /* Find and set payload. */
- *payload = ((pj_uint8_t*)pkt) + offset;
- *payloadlen = pkt_len - offset;
-
- return PJ_SUCCESS;
-}
-
-
-PJ_DEF(pj_status_t) pj_rtp_session_update( pj_rtp_session *ses, const pj_rtp_hdr *hdr)
-{
- int status;
-
- /* Check SSRC. */
- if (ses->peer_ssrc == 0) ses->peer_ssrc = pj_ntohl(hdr->ssrc);
- /*
- if (pj_ntohl(ses->peer_ssrc) != hdr->ssrc) {
- PJ_LOG(4, (THIS_FILE, "pj_rtp_session_update: ses=%p, invalid ssrc 0x%p (!=0x%p)",
- ses, pj_ntohl(hdr->ssrc), ses->peer_ssrc));
- return PJ_RTP_ERR_INVALID_SSRC;
- }
- */
-
- /* Check payload type. */
- if (hdr->pt != ses->out_pt) {
- PJ_LOG(4, (THIS_FILE, "pj_rtp_session_update: ses=%p, invalid payload type %d (!=%d)",
- ses, hdr->pt, ses->out_pt));
- return PJ_RTP_ERR_INVALID_PT;
- }
-
- /* Initialize sequence number on first packet received. */
- if (ses->received == 0)
- pj_rtp_seq_init( &ses->seq_ctrl, pj_ntohs(hdr->seq) );
-
- /* Check sequence number to see if remote session has been restarted. */
- status = pj_rtp_seq_update( &ses->seq_ctrl, pj_ntohs(hdr->seq));
- if (status == PJ_RTP_ERR_SESSION_RESTARTED) {
- pj_rtp_seq_restart( &ses->seq_ctrl, pj_ntohs(hdr->seq));
- ++ses->received;
- } else if (status == 0 || status == PJ_RTP_ERR_SESSION_PROBATION) {
- ++ses->received;
- }
-
-
- return status;
-}
-
-
-void pj_rtp_seq_restart(pj_rtp_seq_session *sctrl, pj_uint16_t seq)
-{
- sctrl->base_seq = seq;
- sctrl->max_seq = seq;
- sctrl->bad_seq = RTP_SEQ_MOD + 1;
- sctrl->cycles = 0;
-}
-
-
-void pj_rtp_seq_init(pj_rtp_seq_session *sctrl, pj_uint16_t seq)
-{
- pj_rtp_seq_restart(sctrl, seq);
-
- sctrl->max_seq = (pj_uint16_t) (seq - 1);
- sctrl->probation = MIN_SEQUENTIAL;
-}
-
-
-int pj_rtp_seq_update(pj_rtp_seq_session *sctrl, pj_uint16_t seq)
-{
- pj_uint16_t udelta = (pj_uint16_t) (seq - sctrl->max_seq);
-
- /*
- * Source is not valid until MIN_SEQUENTIAL packets with
- * sequential sequence numbers have been received.
- */
- if (sctrl->probation) {
- /* packet is in sequence */
- if (seq == sctrl->max_seq+ 1) {
- sctrl->probation--;
- sctrl->max_seq = seq;
- if (sctrl->probation == 0) {
- return PJ_RTP_ERR_SESSION_RESTARTED;
- }
- } else {
- sctrl->probation = MIN_SEQUENTIAL - 1;
- sctrl->max_seq = seq;
- }
- return PJ_RTP_ERR_SESSION_PROBATION;
-
- } else if (udelta < MAX_DROPOUT) {
- /* in order, with permissible gap */
- if (seq < sctrl->max_seq) {
- /* Sequence number wrapped - count another 64K cycle. */
- sctrl->cycles += RTP_SEQ_MOD;
- }
- sctrl->max_seq = seq;
-
- } else if (udelta <= (RTP_SEQ_MOD - MAX_MISORDER)) {
- /* the sequence number made a very large jump */
- if (seq == sctrl->bad_seq) {
- /*
- * Two sequential packets -- assume that the other side
- * restarted without telling us so just re-sync
- * (i.e., pretend this was the first packet).
- */
- return PJ_RTP_ERR_SESSION_RESTARTED;
- }
- else {
- sctrl->bad_seq = (seq + 1) & (RTP_SEQ_MOD-1);
- return PJ_RTP_ERR_BAD_SEQUENCE;
- }
- } else {
- /* duplicate or reordered packet */
- }
-
- return 0;
-}
-
-
+/* $Id$
+ *
+ */
+/*
+ * PJMEDIA - Multimedia over IP Stack
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ * Benny Prijono <bennylp@bulukucing.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <pjmedia/rtp.h>
+#include <pj/log.h>
+#include <pj/os.h> /* pj_gettimeofday() */
+#include <pj/sock.h> /* pj_htonx, pj_htonx */
+#include <string.h> /* memset() */
+
+#define THIS_FILE "rtp.c"
+
+#define RTP_VERSION 2
+
+#define RTP_SEQ_MOD (1 << 16)
+#define MAX_DROPOUT ((pj_int16_t)3000)
+#define MAX_MISORDER ((pj_int16_t)100)
+#define MIN_SEQUENTIAL ((pj_int16_t)2)
+
+
+PJ_DEF(pj_status_t) pj_rtp_session_init( pj_rtp_session *ses,
+ int default_pt, pj_uint32_t sender_ssrc )
+{
+ PJ_LOG(4, (THIS_FILE, "pj_rtp_session_init: ses=%p, default_pt=%d, ssrc=0x%x",
+ ses, default_pt, sender_ssrc));
+
+ /* Check RTP header packing. */
+ if (sizeof(struct pj_rtp_hdr) != 12) {
+ pj_assert(!"Wrong RTP header packing!");
+ return PJ_RTP_ERR_RTP_PACKING;
+ }
+
+ /* If sender_ssrc is not specified, create from time value. */
+ if (sender_ssrc == 0 || sender_ssrc == (pj_uint32_t)-1) {
+ pj_time_val tv;
+
+ pj_gettimeofday(&tv);
+ sender_ssrc = (pj_uint32_t) pj_htonl(tv.sec);
+ } else {
+ sender_ssrc = pj_htonl(sender_ssrc);
+ }
+
+ /* Initialize session. */
+ ses->out_extseq = 0;
+ ses->peer_ssrc = 0;
+
+ /* Sequence number will be initialized when the first RTP packet is receieved. */
+
+ /* Build default header for outgoing RTP packet. */
+ memset(ses, 0, sizeof(*ses));
+ ses->out_hdr.v = RTP_VERSION;
+ ses->out_hdr.p = 0;
+ ses->out_hdr.x = 0;
+ ses->out_hdr.cc = 0;
+ ses->out_hdr.m = 0;
+ ses->out_hdr.pt = (pj_uint8_t) default_pt;
+ ses->out_hdr.seq = (pj_uint16_t) pj_htons( (pj_uint16_t)ses->out_extseq );
+ ses->out_hdr.ts = 0;
+ ses->out_hdr.ssrc = sender_ssrc;
+
+ /* Keep some arguments as session defaults. */
+ ses->out_pt = (pj_uint16_t) default_pt;
+
+ return PJ_SUCCESS;
+}
+
+
+PJ_DEF(pj_status_t) pj_rtp_encode_rtp( pj_rtp_session *ses, int pt, int m,
+ int payload_len, int ts_len,
+ const void **rtphdr, int *hdrlen )
+{
+ PJ_UNUSED_ARG(payload_len)
+
+ PJ_LOG(6, (THIS_FILE,
+ "pj_rtp_encode_rtp: ses=%p, pt=%d, m=%d, pt_len=%d, ts_len=%d",
+ ses, pt, m, payload_len, ts_len));
+
+ /* Update session. */
+ ses->out_extseq++;
+ ses->out_hdr.ts = pj_htonl(pj_ntohl(ses->out_hdr.ts)+ts_len);
+
+ /* Create outgoing header. */
+ ses->out_hdr.pt = (pj_uint8_t) ((pt == -1) ? ses->out_pt : pt);
+ ses->out_hdr.m = (pj_uint16_t) m;
+ ses->out_hdr.seq = pj_htons( (pj_uint16_t) ses->out_extseq);
+
+ /* Return values */
+ *rtphdr = &ses->out_hdr;
+ *hdrlen = sizeof(pj_rtp_hdr);
+
+ return PJ_SUCCESS;
+}
+
+
+PJ_DEF(pj_status_t) pj_rtp_decode_rtp( pj_rtp_session *ses,
+ const void *pkt, int pkt_len,
+ const pj_rtp_hdr **hdr,
+ const void **payload,
+ unsigned *payloadlen)
+{
+ int offset;
+
+ PJ_UNUSED_ARG(ses)
+
+ PJ_LOG(6, (THIS_FILE,
+ "pj_rtp_decode_rtp: ses=%p, pkt=%p, pkt_len=%d",
+ ses, pkt, pkt_len));
+
+ /* Assume RTP header at the start of packet. We'll verify this later. */
+ *hdr = (pj_rtp_hdr*)pkt;
+
+ /* Check RTP header sanity. */
+ if ((*hdr)->v != RTP_VERSION) {
+ PJ_LOG(4, (THIS_FILE, " invalid RTP version!"));
+ return PJ_RTP_ERR_INVALID_VERSION;
+ }
+
+ /* Payload is located right after header plus CSRC */
+ offset = sizeof(pj_rtp_hdr) + ((*hdr)->cc * sizeof(pj_uint32_t));
+
+ /* Adjust offset if RTP extension is used. */
+ if ((*hdr)->x) {
+ pj_rtp_ext_hdr *ext = (pj_rtp_ext_hdr*) (((pj_uint8_t*)pkt) + offset);
+ offset += (pj_ntohs(ext->length) * sizeof(pj_uint32_t));
+ }
+
+ /* Check that offset is less than packet size */
+ if (offset >= pkt_len)
+ return PJ_RTP_ERR_INVALID_PACKET;
+
+ /* Find and set payload. */
+ *payload = ((pj_uint8_t*)pkt) + offset;
+ *payloadlen = pkt_len - offset;
+
+ return PJ_SUCCESS;
+}
+
+
+PJ_DEF(pj_status_t) pj_rtp_session_update( pj_rtp_session *ses, const pj_rtp_hdr *hdr)
+{
+ int status;
+
+ /* Check SSRC. */
+ if (ses->peer_ssrc == 0) ses->peer_ssrc = pj_ntohl(hdr->ssrc);
+ /*
+ if (pj_ntohl(ses->peer_ssrc) != hdr->ssrc) {
+ PJ_LOG(4, (THIS_FILE, "pj_rtp_session_update: ses=%p, invalid ssrc 0x%p (!=0x%p)",
+ ses, pj_ntohl(hdr->ssrc), ses->peer_ssrc));
+ return PJ_RTP_ERR_INVALID_SSRC;
+ }
+ */
+
+ /* Check payload type. */
+ if (hdr->pt != ses->out_pt) {
+ PJ_LOG(4, (THIS_FILE, "pj_rtp_session_update: ses=%p, invalid payload type %d (!=%d)",
+ ses, hdr->pt, ses->out_pt));
+ return PJ_RTP_ERR_INVALID_PT;
+ }
+
+ /* Initialize sequence number on first packet received. */
+ if (ses->received == 0)
+ pj_rtp_seq_init( &ses->seq_ctrl, pj_ntohs(hdr->seq) );
+
+ /* Check sequence number to see if remote session has been restarted. */
+ status = pj_rtp_seq_update( &ses->seq_ctrl, pj_ntohs(hdr->seq));
+ if (status == PJ_RTP_ERR_SESSION_RESTARTED) {
+ pj_rtp_seq_restart( &ses->seq_ctrl, pj_ntohs(hdr->seq));
+ ++ses->received;
+ } else if (status == 0 || status == PJ_RTP_ERR_SESSION_PROBATION) {
+ ++ses->received;
+ }
+
+
+ return status;
+}
+
+
+void pj_rtp_seq_restart(pj_rtp_seq_session *sctrl, pj_uint16_t seq)
+{
+ sctrl->base_seq = seq;
+ sctrl->max_seq = seq;
+ sctrl->bad_seq = RTP_SEQ_MOD + 1;
+ sctrl->cycles = 0;
+}
+
+
+void pj_rtp_seq_init(pj_rtp_seq_session *sctrl, pj_uint16_t seq)
+{
+ pj_rtp_seq_restart(sctrl, seq);
+
+ sctrl->max_seq = (pj_uint16_t) (seq - 1);
+ sctrl->probation = MIN_SEQUENTIAL;
+}
+
+
+int pj_rtp_seq_update(pj_rtp_seq_session *sctrl, pj_uint16_t seq)
+{
+ pj_uint16_t udelta = (pj_uint16_t) (seq - sctrl->max_seq);
+
+ /*
+ * Source is not valid until MIN_SEQUENTIAL packets with
+ * sequential sequence numbers have been received.
+ */
+ if (sctrl->probation) {
+ /* packet is in sequence */
+ if (seq == sctrl->max_seq+ 1) {
+ sctrl->probation--;
+ sctrl->max_seq = seq;
+ if (sctrl->probation == 0) {
+ return PJ_RTP_ERR_SESSION_RESTARTED;
+ }
+ } else {
+ sctrl->probation = MIN_SEQUENTIAL - 1;
+ sctrl->max_seq = seq;
+ }
+ return PJ_RTP_ERR_SESSION_PROBATION;
+
+ } else if (udelta < MAX_DROPOUT) {
+ /* in order, with permissible gap */
+ if (seq < sctrl->max_seq) {
+ /* Sequence number wrapped - count another 64K cycle. */
+ sctrl->cycles += RTP_SEQ_MOD;
+ }
+ sctrl->max_seq = seq;
+
+ } else if (udelta <= (RTP_SEQ_MOD - MAX_MISORDER)) {
+ /* the sequence number made a very large jump */
+ if (seq == sctrl->bad_seq) {
+ /*
+ * Two sequential packets -- assume that the other side
+ * restarted without telling us so just re-sync
+ * (i.e., pretend this was the first packet).
+ */
+ return PJ_RTP_ERR_SESSION_RESTARTED;
+ }
+ else {
+ sctrl->bad_seq = (seq + 1) & (RTP_SEQ_MOD-1);
+ return PJ_RTP_ERR_BAD_SEQUENCE;
+ }
+ } else {
+ /* duplicate or reordered packet */
+ }
+
+ return 0;
+}
+
+
diff --git a/pjmedia/src/pjmedia/rtp.h b/pjmedia/src/pjmedia/rtp.h
index e705aa415..1615b2d 100644
--- a/pjmedia/src/pjmedia/rtp.h
+++ b/pjmedia/src/pjmedia/rtp.h
@@ -1,242 +1,263 @@
-/* $Id$
- *
- */
-
-#ifndef __PJMEDIA_RTP_H__
-#define __PJMEDIA_RTP_H__
-
-#include <pj/types.h>
-
-/**
- * @file rtp.h
- * @brief RTP implementation.
- */
-
-PJ_BEGIN_DECL
-
-
-/**
- * @defgroup PJMED_RTP RTP
- * @ingroup PJMEDIA
- * @{
- *
- * The RTP module is designed to be dependent only to PJLIB, it does not depend
- * on any other parts of PJMEDIA library. The RTP module does not even depend
- * on any transports (sockets), to promote even more use.
- *
- * An RTCP implementation is also separated from this module.
- *
- * The functions that are provided by this module:
- * - creating RTP header for each outgoing packet.
- * - decoding RTP packet into RTP header and payload.
- * - provide simple RTP session management (sequence number, etc.)
- *
- * The RTP module does not use any dynamic memory at all.
- *
- * \section P1 How to Use the RTP Module
- *
- * First application must call #pj_rtp_session_init to initialize the RTP
- * session.
- *
- * When application wants to send RTP packet, it needs to call
- * #pj_rtp_encode_rtp to build the RTP header. Note that this WILL NOT build
- * the complete RTP packet, but instead only the header. Application can
- * then either concatenate the header with the payload, or send the two
- * fragments (the header and the payload) using scatter-gather transport API
- * (e.g. \a sendv()).
- *
- * When application receives an RTP packet, first it should call
- * #pj_rtp_decode_rtp to decode RTP header and payload, then it should call
- * #pj_rtp_session_update to check whether we can process the RTP payload,
- * and to let the RTP session updates its internal status. The decode function
- * is guaranteed to point the payload to the correct position regardless of
- * any options present in the RTP packet.
- *
- */
-
-
-#ifdef _MSC_VER
-# pragma warning ( disable : 4214 )
-#endif
-
-
-/**
- * Error codes.
- */
-enum pj_rtp_error_t
-{
- PJ_RTP_ERR_RTP_PACKING, /**< Invalid RTP packet. */
- PJ_RTP_ERR_INVALID_VERSION, /**< Invalid RTP version. */
- PJ_RTP_ERR_INVALID_SSRC, /**< Invalid SSRC. */
- PJ_RTP_ERR_INVALID_PT, /**< Invalid payload type. */
- PJ_RTP_ERR_INVALID_PACKET, /**< Invalid packet. */
- PJ_RTP_ERR_SESSION_RESTARTED, /**< Session has just been restarted. */
- PJ_RTP_ERR_SESSION_PROBATION, /**< Session in probation. */
- PJ_RTP_ERR_BAD_SEQUENCE, /**< Bad RTP sequence number. */
-};
-
-#pragma pack(1)
-/**
- * RTP packet header.
- */
-struct pj_rtp_hdr
-{
-#if defined(PJ_IS_BIG_ENDIAN) && (PJ_IS_BIG_ENDIAN!=0)
- pj_uint16_t v:2; /**< packet type/version */
- pj_uint16_t p:1; /**< padding flag */
- pj_uint16_t x:1; /**< extension flag */
- pj_uint16_t cc:4; /**< CSRC count */
- pj_uint16_t m:1; /**< marker bit */
- pj_uint16_t pt:7; /**< payload type */
-#else
- pj_uint16_t cc:4; /**< CSRC count */
- pj_uint16_t x:1; /**< header extension flag */
- pj_uint16_t p:1; /**< padding flag */
- pj_uint16_t v:2; /**< packet type/version */
- pj_uint16_t pt:7; /**< payload type */
- pj_uint16_t m:1; /**< marker bit */
-#endif
- pj_uint16_t seq; /**< sequence number */
- pj_uint32_t ts; /**< timestamp */
- pj_uint32_t ssrc; /**< synchronization source */
-};
-#pragma pack()
-
-typedef struct pj_rtp_hdr pj_rtp_hdr;
-
-/**
- * RTP extendsion header.
- */
-struct pj_rtp_ext_hdr
-{
- pj_uint16_t profile_data;
- pj_uint16_t length;
-};
-
-typedef struct pj_rtp_ext_hdr pj_rtp_ext_hdr;
-
-/**
- * A generic sequence number management, used by both RTP and RTCP.
- */
-struct pj_rtp_seq_session
-{
- pj_uint16_t max_seq; /**< highest sequence number heard */
- pj_uint32_t cycles; /**< shifted count of seq. number cycles */
- pj_uint32_t base_seq; /**< base seq number */
- pj_uint32_t bad_seq; /**< last 'bad' seq number + 1 */
- pj_uint32_t probation; /**< sequ. packets till source is valid */
-};
-
-typedef struct pj_rtp_seq_session pj_rtp_seq_session;
-
-/**
- * RTP session descriptor.
- */
-struct pj_rtp_session
-{
- pj_rtp_hdr out_hdr; /**< Saved header for outgoing packets. */
- pj_rtp_seq_session seq_ctrl; /**< Sequence number management. */
- pj_uint16_t out_pt; /**< Default outgoing payload type. */
- pj_uint32_t out_extseq; /**< Outgoing extended sequence number. */
- pj_uint32_t peer_ssrc; /**< Peer SSRC. */
- pj_uint32_t received; /**< Number of received packets. */
-};
-
-typedef struct pj_rtp_session pj_rtp_session;
-
-/**
- * \brief Initialize RTP session.
- * This function will initialize the RTP session according to given parameters.
- *
- * @param ses The session.
- * @param default_pt Default payload type.
- * @param sender_ssrc SSRC used for outgoing packets.
- *
- * @return zero if successfull.
- */
-PJ_DECL(pj_status_t) pj_rtp_session_init( pj_rtp_session *ses,
- int default_pt, pj_uint32_t sender_ssrc );
-
-/**
- * \brief Encode outgoing RTP packet header.
- * Create the RTP header based on arguments and current state of the RTP
- * session.
- *
- * @param ses The session.
- * @param pt Payload type.
- * @param m Marker flag.
- * @param payload_len Payload length in bytes.
- * @param ts_len Timestamp length.
- * @param rtphdr Upon return will point to RTP packet header.
- * @param hdrlen Upon return will indicate the size of RTP packet header
- *
- * @return zero if successfull.
- */
-PJ_DECL(pj_status_t) pj_rtp_encode_rtp( pj_rtp_session *ses, int pt, int m,
- int payload_len, int ts_len,
- const void **rtphdr, int *hdrlen );
-
-/**
- * \brief Decode an incoming RTP packet.
- * This function will decode incoming packet into RTP header and payload.
- * The decode function is guaranteed to point the payload to the correct
- * position regardless of any options present in the RTP packet.
- *
- * @param ses The session.
- * @param pkt The received RTP packet.
- * @param pkt_len The length of the packet.
- * @param hdr Upon return will point to the location of the RTP header
- * inside the packet.
- * @param payload Upon return will point to the location of the
- * payload inside the packet.
- * @param payloadlen Upon return will indicate the size of the payload.
- *
- * @return zero if successfull.
- */
-PJ_DECL(pj_status_t) pj_rtp_decode_rtp( pj_rtp_session *ses,
- const void *pkt, int pkt_len,
- const pj_rtp_hdr **hdr,
- const void **payload,
- unsigned *payloadlen);
-
-/**
- * \brief Update RTP session with an incoming RTP packet.
- * Call this function everytime
- * an RTP packet is received to check whether the packet can be received and to
- * let the RTP session performs its internal calculations.
- *
- * @param ses The session.
- * @param hdr The RTP header of the incoming packet.
- *
- * @return zero if the packet is valid and can be processed, otherwise will
- * return one of the error in #pj_rtp_error_t.
- */
-PJ_DECL(pj_status_t) pj_rtp_session_update( pj_rtp_session *ses,
- const pj_rtp_hdr *hdr);
-
-/**
-* \brief Internal.
- * Internal function for sequence control, shared by RTCP implementation.
- */
-void pj_rtp_seq_init(pj_rtp_seq_session *seq_ctrl, pj_uint16_t seq);
-
-/**
-* \brief Internal.
- * Internal function for sequence control, shared by RTCP implementation.
- */
-void pj_rtp_seq_restart(pj_rtp_seq_session *seq_ctrl, pj_uint16_t seq);
-
-/**
-* \brief Internal.
- * Internal function for sequence control, shared by RTCP implementation.
- */
-int pj_rtp_seq_update(pj_rtp_seq_session *seq_ctrl, pj_uint16_t seq);
-
-/**
- * @}
- */
-
-PJ_END_DECL
-
-
-#endif /* __PJMEDIA_RTP_H__ */
+/* $Id$
+ *
+ */
+/*
+ * PJMEDIA - Multimedia over IP Stack
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ * Benny Prijono <bennylp@bulukucing.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef __PJMEDIA_RTP_H__
+#define __PJMEDIA_RTP_H__
+
+#include <pj/types.h>
+
+/**
+ * @file rtp.h
+ * @brief RTP implementation.
+ */
+
+PJ_BEGIN_DECL
+
+
+/**
+ * @defgroup PJMED_RTP RTP
+ * @ingroup PJMEDIA
+ * @{
+ *
+ * The RTP module is designed to be dependent only to PJLIB, it does not depend
+ * on any other parts of PJMEDIA library. The RTP module does not even depend
+ * on any transports (sockets), to promote even more use.
+ *
+ * An RTCP implementation is also separated from this module.
+ *
+ * The functions that are provided by this module:
+ * - creating RTP header for each outgoing packet.
+ * - decoding RTP packet into RTP header and payload.
+ * - provide simple RTP session management (sequence number, etc.)
+ *
+ * The RTP module does not use any dynamic memory at all.
+ *
+ * \section P1 How to Use the RTP Module
+ *
+ * First application must call #pj_rtp_session_init to initialize the RTP
+ * session.
+ *
+ * When application wants to send RTP packet, it needs to call
+ * #pj_rtp_encode_rtp to build the RTP header. Note that this WILL NOT build
+ * the complete RTP packet, but instead only the header. Application can
+ * then either concatenate the header with the payload, or send the two
+ * fragments (the header and the payload) using scatter-gather transport API
+ * (e.g. \a sendv()).
+ *
+ * When application receives an RTP packet, first it should call
+ * #pj_rtp_decode_rtp to decode RTP header and payload, then it should call
+ * #pj_rtp_session_update to check whether we can process the RTP payload,
+ * and to let the RTP session updates its internal status. The decode function
+ * is guaranteed to point the payload to the correct position regardless of
+ * any options present in the RTP packet.
+ *
+ */
+
+
+#ifdef _MSC_VER
+# pragma warning ( disable : 4214 )
+#endif
+
+
+/**
+ * Error codes.
+ */
+enum pj_rtp_error_t
+{
+ PJ_RTP_ERR_RTP_PACKING, /**< Invalid RTP packet. */
+ PJ_RTP_ERR_INVALID_VERSION, /**< Invalid RTP version. */
+ PJ_RTP_ERR_INVALID_SSRC, /**< Invalid SSRC. */
+ PJ_RTP_ERR_INVALID_PT, /**< Invalid payload type. */
+ PJ_RTP_ERR_INVALID_PACKET, /**< Invalid packet. */
+ PJ_RTP_ERR_SESSION_RESTARTED, /**< Session has just been restarted. */
+ PJ_RTP_ERR_SESSION_PROBATION, /**< Session in probation. */
+ PJ_RTP_ERR_BAD_SEQUENCE, /**< Bad RTP sequence number. */
+};
+
+#pragma pack(1)
+/**
+ * RTP packet header.
+ */
+struct pj_rtp_hdr
+{
+#if defined(PJ_IS_BIG_ENDIAN) && (PJ_IS_BIG_ENDIAN!=0)
+ pj_uint16_t v:2; /**< packet type/version */
+ pj_uint16_t p:1; /**< padding flag */
+ pj_uint16_t x:1; /**< extension flag */
+ pj_uint16_t cc:4; /**< CSRC count */
+ pj_uint16_t m:1; /**< marker bit */
+ pj_uint16_t pt:7; /**< payload type */
+#else
+ pj_uint16_t cc:4; /**< CSRC count */
+ pj_uint16_t x:1; /**< header extension flag */
+ pj_uint16_t p:1; /**< padding flag */
+ pj_uint16_t v:2; /**< packet type/version */
+ pj_uint16_t pt:7; /**< payload type */
+ pj_uint16_t m:1; /**< marker bit */
+#endif
+ pj_uint16_t seq; /**< sequence number */
+ pj_uint32_t ts; /**< timestamp */
+ pj_uint32_t ssrc; /**< synchronization source */
+};
+#pragma pack()
+
+typedef struct pj_rtp_hdr pj_rtp_hdr;
+
+/**
+ * RTP extendsion header.
+ */
+struct pj_rtp_ext_hdr
+{
+ pj_uint16_t profile_data;
+ pj_uint16_t length;
+};
+
+typedef struct pj_rtp_ext_hdr pj_rtp_ext_hdr;
+
+/**
+ * A generic sequence number management, used by both RTP and RTCP.
+ */
+struct pj_rtp_seq_session
+{
+ pj_uint16_t max_seq; /**< highest sequence number heard */
+ pj_uint32_t cycles; /**< shifted count of seq. number cycles */
+ pj_uint32_t base_seq; /**< base seq number */
+ pj_uint32_t bad_seq; /**< last 'bad' seq number + 1 */
+ pj_uint32_t probation; /**< sequ. packets till source is valid */
+};
+
+typedef struct pj_rtp_seq_session pj_rtp_seq_session;
+
+/**
+ * RTP session descriptor.
+ */
+struct pj_rtp_session
+{
+ pj_rtp_hdr out_hdr; /**< Saved header for outgoing packets. */
+ pj_rtp_seq_session seq_ctrl; /**< Sequence number management. */
+ pj_uint16_t out_pt; /**< Default outgoing payload type. */
+ pj_uint32_t out_extseq; /**< Outgoing extended sequence number. */
+ pj_uint32_t peer_ssrc; /**< Peer SSRC. */
+ pj_uint32_t received; /**< Number of received packets. */
+};
+
+typedef struct pj_rtp_session pj_rtp_session;
+
+/**
+ * \brief Initialize RTP session.
+ * This function will initialize the RTP session according to given parameters.
+ *
+ * @param ses The session.
+ * @param default_pt Default payload type.
+ * @param sender_ssrc SSRC used for outgoing packets.
+ *
+ * @return zero if successfull.
+ */
+PJ_DECL(pj_status_t) pj_rtp_session_init( pj_rtp_session *ses,
+ int default_pt, pj_uint32_t sender_ssrc );
+
+/**
+ * \brief Encode outgoing RTP packet header.
+ * Create the RTP header based on arguments and current state of the RTP
+ * session.
+ *
+ * @param ses The session.
+ * @param pt Payload type.
+ * @param m Marker flag.
+ * @param payload_len Payload length in bytes.
+ * @param ts_len Timestamp length.
+ * @param rtphdr Upon return will point to RTP packet header.
+ * @param hdrlen Upon return will indicate the size of RTP packet header
+ *
+ * @return zero if successfull.
+ */
+PJ_DECL(pj_status_t) pj_rtp_encode_rtp( pj_rtp_session *ses, int pt, int m,
+ int payload_len, int ts_len,
+ const void **rtphdr, int *hdrlen );
+
+/**
+ * \brief Decode an incoming RTP packet.
+ * This function will decode incoming packet into RTP header and payload.
+ * The decode function is guaranteed to point the payload to the correct
+ * position regardless of any options present in the RTP packet.
+ *
+ * @param ses The session.
+ * @param pkt The received RTP packet.
+ * @param pkt_len The length of the packet.
+ * @param hdr Upon return will point to the location of the RTP header
+ * inside the packet.
+ * @param payload Upon return will point to the location of the
+ * payload inside the packet.
+ * @param payloadlen Upon return will indicate the size of the payload.
+ *
+ * @return zero if successfull.
+ */
+PJ_DECL(pj_status_t) pj_rtp_decode_rtp( pj_rtp_session *ses,
+ const void *pkt, int pkt_len,
+ const pj_rtp_hdr **hdr,
+ const void **payload,
+ unsigned *payloadlen);
+
+/**
+ * \brief Update RTP session with an incoming RTP packet.
+ * Call this function everytime
+ * an RTP packet is received to check whether the packet can be received and to
+ * let the RTP session performs its internal calculations.
+ *
+ * @param ses The session.
+ * @param hdr The RTP header of the incoming packet.
+ *
+ * @return zero if the packet is valid and can be processed, otherwise will
+ * return one of the error in #pj_rtp_error_t.
+ */
+PJ_DECL(pj_status_t) pj_rtp_session_update( pj_rtp_session *ses,
+ const pj_rtp_hdr *hdr);
+
+/**
+* \brief Internal.
+ * Internal function for sequence control, shared by RTCP implementation.
+ */
+void pj_rtp_seq_init(pj_rtp_seq_session *seq_ctrl, pj_uint16_t seq);
+
+/**
+* \brief Internal.
+ * Internal function for sequence control, shared by RTCP implementation.
+ */
+void pj_rtp_seq_restart(pj_rtp_seq_session *seq_ctrl, pj_uint16_t seq);
+
+/**
+* \brief Internal.
+ * Internal function for sequence control, shared by RTCP implementation.
+ */
+int pj_rtp_seq_update(pj_rtp_seq_session *seq_ctrl, pj_uint16_t seq);
+
+/**
+ * @}
+ */
+
+PJ_END_DECL
+
+
+#endif /* __PJMEDIA_RTP_H__ */
diff --git a/pjmedia/src/pjmedia/sdp.c b/pjmedia/src/pjmedia/sdp.c
index aea3a43..c3b20a9 100644
--- a/pjmedia/src/pjmedia/sdp.c
+++ b/pjmedia/src/pjmedia/sdp.c
@@ -1,936 +1,957 @@
-/* $Id$
- *
- */
-
-
-#include <pjmedia/sdp.h>
-#include <pj/scanner.h>
-#include <pj/except.h>
-#include <pj/log.h>
-#include <pj/os.h>
-#include <pj/string.h>
-#include <pj/pool.h>
-
-enum {
- SKIP_WS = 0,
- SYNTAX_ERROR = 1,
-};
-#define TOKEN "-.!%*_=`'~"
-#define NTP_OFFSET ((pj_uint32_t)2208988800)
-#define LOG_THIS "sdp"
-
-/*
- * Prototypes for line parser.
- */
-static void parse_version(pj_scanner *scanner);
-static void parse_origin(pj_scanner *scanner, pjsdp_session_desc *ses);
-static void parse_time(pj_scanner *scanner, pjsdp_session_desc *ses);
-static void parse_generic_line(pj_scanner *scanner, pj_str_t *str);
-static void parse_connection_info(pj_scanner *scanner, pjsdp_conn_info *conn);
-static pjsdp_attr *parse_attr(pj_pool_t *pool, pj_scanner *scanner);
-static void parse_media(pj_scanner *scanner, pjsdp_media_desc *med);
-
-/*
- * Prototypes for attribute parsers.
- */
-static pjsdp_rtpmap_attr * parse_rtpmap_attr( pj_pool_t *pool, pj_scanner *scanner );
-static pjsdp_attr_string * parse_generic_string_attr( pj_pool_t *pool, pj_scanner *scanner );
-static pjsdp_attr_num * parse_generic_num_attr( pj_pool_t *pool, pj_scanner *scanner );
-static pjsdp_attr * parse_name_only_attr( pj_pool_t *pool, pj_scanner *scanner );
-static pjsdp_fmtp_attr * parse_fmtp_attr( pj_pool_t *pool, pj_scanner *scanner );
-
-
-/*
- * Prototypes for functions to print attribute.
- * All of them returns integer for the length printed, or -1 on error.
- */
-static int print_rtpmap_attr(const pjsdp_rtpmap_attr *attr,
- char *buf, int length);
-static int print_generic_string_attr(const pjsdp_attr_string *attr,
- char *buf, int length);
-static int print_generic_num_attr(const pjsdp_attr_num *attr,
- char *buf, int length);
-static int print_name_only_attr(const pjsdp_attr *attr,
- char *buf, int length);
-static int print_fmtp_attr(const pjsdp_fmtp_attr *attr,
- char *buf, int length);
-
-/*
- * Prototypes for cloning attributes.
- */
-static pjsdp_attr* clone_rtpmap_attr (pj_pool_t *pool, const pjsdp_attr *rhs);
-static pjsdp_attr* clone_generic_string_attr (pj_pool_t *pool, const pjsdp_attr *rhs);
-static pjsdp_attr* clone_generic_num_attr (pj_pool_t *pool, const pjsdp_attr *rhs);
-static pjsdp_attr* clone_name_only_attr (pj_pool_t *pool, const pjsdp_attr *rhs);
-static pjsdp_attr* clone_fmtp_attr (pj_pool_t *pool, const pjsdp_attr *rhs);
-
-
-/*
- * Prototypes
- */
-static void init_sdp_parser(void);
-
-
-typedef void * (*FPARSE)(pj_pool_t *pool, pj_scanner *scanner);
-typedef int (*FPRINT)(const void *attr, char *buf, int length);
-typedef pjsdp_attr* (*FCLONE)(pj_pool_t *pool, const pjsdp_attr *rhs);
-
-/*
- * Array of functions to print attribute.
- */
-static struct attr_map_rec
-{
- pj_str_t name;
- FPARSE parse_attr;
- FPRINT print_attr;
- FCLONE clone;
-} attr_map[] =
-{
- {{"rtpmap", 6}, (FPARSE)&parse_rtpmap_attr, (FPRINT)&print_rtpmap_attr, (FCLONE)&clone_rtpmap_attr},
- {{"cat", 3}, (FPARSE)&parse_generic_string_attr, (FPRINT)&print_generic_string_attr, (FCLONE)&clone_generic_string_attr},
- {{"keywds", 6}, (FPARSE)&parse_generic_string_attr, (FPRINT)&print_generic_string_attr, (FCLONE)&clone_generic_string_attr},
- {{"tool", 4}, (FPARSE)&parse_generic_string_attr, (FPRINT)&print_generic_string_attr, (FCLONE)&clone_generic_string_attr},
- {{"ptime", 5}, (FPARSE)&parse_generic_num_attr, (FPRINT)&print_generic_num_attr, (FCLONE)&clone_generic_num_attr},
- {{"recvonly", 8}, (FPARSE)&parse_name_only_attr, (FPRINT)&print_name_only_attr, (FCLONE)&clone_name_only_attr},
- {{"sendonly", 8}, (FPARSE)&parse_name_only_attr, (FPRINT)&print_name_only_attr, (FCLONE)&clone_name_only_attr},
- {{"sendrecv", 8}, (FPARSE)&parse_name_only_attr, (FPRINT)&print_name_only_attr, (FCLONE)&clone_name_only_attr},
- {{"orient", 6}, (FPARSE)&parse_generic_string_attr, (FPRINT)&print_generic_string_attr, (FCLONE)&clone_generic_string_attr},
- {{"type", 4}, (FPARSE)&parse_generic_string_attr, (FPRINT)&print_generic_string_attr, (FCLONE)&clone_generic_string_attr},
- {{"charset", 7}, (FPARSE)&parse_generic_string_attr, (FPRINT)&print_generic_string_attr, (FCLONE)&clone_generic_string_attr},
- {{"sdplang", 7}, (FPARSE)&parse_generic_string_attr, (FPRINT)&print_generic_string_attr, (FCLONE)&clone_generic_string_attr},
- {{"lang", 4}, (FPARSE)&parse_generic_string_attr, (FPRINT)&print_generic_string_attr, (FCLONE)&clone_generic_string_attr},
- {{"framerate", 9}, (FPARSE)&parse_generic_string_attr, (FPRINT)&print_generic_string_attr, (FCLONE)&clone_generic_string_attr},
- {{"quality", 7}, (FPARSE)&parse_generic_num_attr, (FPRINT)&print_generic_num_attr, (FCLONE)&clone_generic_num_attr},
- {{"fmtp", 4}, (FPARSE)&parse_fmtp_attr, (FPRINT)&print_fmtp_attr, (FCLONE)&clone_fmtp_attr},
- {{"inactive", 8}, (FPARSE)&parse_name_only_attr, (FPRINT)&print_name_only_attr, (FCLONE)&clone_name_only_attr},
- {{"", 0}, NULL, (FPRINT)&print_generic_string_attr, (FCLONE)&clone_generic_string_attr}
-};
-
-/*
- * Scanner character specification.
- */
-static int is_initialized;
-static pj_char_spec cs_token;
-
-static void init_sdp_parser(void)
-{
- if (is_initialized == 0) {
- is_initialized = 1;
- if (is_initialized != 1) {
- return;
- }
- }
- pj_cs_add_alpha(cs_token);
- pj_cs_add_num(cs_token);
- pj_cs_add_str( cs_token, TOKEN);
-}
-
-static int print_rtpmap_attr(const pjsdp_rtpmap_attr *rtpmap,
- char *buf, int len)
-{
- char *p = buf;
-
- if (len < 16+rtpmap->encoding_name.slen+rtpmap->parameter.slen) {
- return -1;
- }
-
- /* colon and payload type. */
- *p++ = ':';
- len = pj_utoa(rtpmap->payload_type, p);
- p += len;
-
- /* space, encoding name */
- *p++ = ' ';
- pj_memcpy(p, rtpmap->encoding_name.ptr, rtpmap->encoding_name.slen);
- p += rtpmap->encoding_name.slen;
-
- /* slash, clock-rate. */
- *p++ = '/';
- len = pj_utoa(rtpmap->clock_rate, p);
- p += len;
-
- /* optionally add encoding parameter. */
- if (rtpmap->parameter.slen) {
- *p++ = '/';
- pj_memcpy(p, rtpmap->parameter.ptr, rtpmap->parameter.slen);
- p += rtpmap->parameter.slen;
- }
-
- return p-buf;
-}
-
-static int print_generic_string_attr(const pjsdp_attr_string *attr,
- char *buf, int len)
-{
- char *p = buf;
-
- if (len < attr->value.slen + 4) {
- return -1;
- }
-
- /* colon and attribute value. */
- *p++ = ':';
- pj_memcpy(p, attr->value.ptr, attr->value.slen);
- p += attr->value.slen;
-
- return p-buf;
-}
-
-static int print_generic_num_attr(const pjsdp_attr_num *attr, char *buf, int len)
-{
- char *p = buf;
-
- if (len < 10) {
- return -1;
- }
- *p++ = ':';
- return pj_utoa(attr->value, p);
-}
-
-static int print_name_only_attr(const pjsdp_attr *attr, char *buf, int len)
-{
- PJ_UNUSED_ARG(attr)
- PJ_UNUSED_ARG(buf)
- PJ_UNUSED_ARG(len)
- return 0;
-}
-
-static int print_fmtp_attr(const pjsdp_fmtp_attr *fmtp, char *buf, int len)
-{
- char *p = buf;
-
- if (len < 4+fmtp->format.slen+fmtp->param.slen) {
- return -1;
- }
-
- /* colon and format. */
- *p++ = ':';
- pj_memcpy(p, fmtp->format.ptr, fmtp->format.slen);
- p += fmtp->format.slen;
-
- /* space and parameter. */
- *p++ = ' ';
- pj_memcpy(p, fmtp->param.ptr, fmtp->param.slen);
- p += fmtp->param.slen;
-
- return p-buf;
-}
-
-
-static int print_attr(const pjsdp_attr *attr, char *buf, int len)
-{
- char *p = buf;
- struct attr_map_rec *desc = &attr_map[attr->type];
-
- if (len < 16) {
- return -1;
- }
-
- *p++ = 'a';
- *p++ = '=';
- pj_memcpy(p, desc->name.ptr, desc->name.slen);
- p += desc->name.slen;
-
- len = (*desc->print_attr)(attr, p, (buf+len)-p);
- if (len < 0) {
- return -1;
- }
- p += len;
- *p++ = '\r';
- *p++ = '\n';
- return p-buf;
-}
-
-static pjsdp_attr* clone_rtpmap_attr (pj_pool_t *pool, const pjsdp_attr *p)
-{
- const pjsdp_rtpmap_attr *rhs = (const pjsdp_rtpmap_attr*)p;
- pjsdp_rtpmap_attr *attr = pj_pool_alloc (pool, sizeof(pjsdp_rtpmap_attr));
- if (!attr)
- return NULL;
-
- attr->type = rhs->type;
- attr->payload_type = rhs->payload_type;
- if (!pj_strdup (pool, &attr->encoding_name, &rhs->encoding_name)) return NULL;
- attr->clock_rate = rhs->clock_rate;
- if (!pj_strdup (pool, &attr->parameter, &rhs->parameter)) return NULL;
-
- return (pjsdp_attr*)attr;
-}
-
-static pjsdp_attr* clone_generic_string_attr (pj_pool_t *pool, const pjsdp_attr *p)
-{
- const pjsdp_attr_string* rhs = (const pjsdp_attr_string*) p;
- pjsdp_attr_string *attr = pj_pool_alloc (pool, sizeof(pjsdp_attr_string));
- if (!attr)
- return NULL;
-
- attr->type = rhs->type;
- if (!pj_strdup (pool, &attr->value, &rhs->value)) return NULL;
-
- return (pjsdp_attr*)attr;
-}
-
-static pjsdp_attr* clone_generic_num_attr (pj_pool_t *pool, const pjsdp_attr *p)
-{
- const pjsdp_attr_num* rhs = (const pjsdp_attr_num*) p;
- pjsdp_attr_num *attr = pj_pool_alloc (pool, sizeof(pjsdp_attr_num));
- if (!attr)
- return NULL;
-
- attr->type = rhs->type;
- attr->value = rhs->value;
-
- return (pjsdp_attr*)attr;
-}
-
-static pjsdp_attr* clone_name_only_attr (pj_pool_t *pool, const pjsdp_attr *rhs)
-{
- pjsdp_attr *attr = pj_pool_alloc (pool, sizeof(pjsdp_attr));
- if (!attr)
- return NULL;
-
- attr->type = rhs->type;
- return attr;
-}
-
-static pjsdp_attr* clone_fmtp_attr (pj_pool_t *pool, const pjsdp_attr *p)
-{
- const pjsdp_fmtp_attr* rhs = (const pjsdp_fmtp_attr*) p;
- pjsdp_fmtp_attr *attr = pj_pool_alloc (pool, sizeof(pjsdp_fmtp_attr));
- if (!attr)
- return NULL;
-
- attr->type = rhs->type;
- if (!pj_strdup (pool, &attr->format, &rhs->format)) return NULL;
- if (!pj_strdup (pool, &attr->param, &rhs->param)) return NULL;
-
- return (pjsdp_attr*)attr;
-}
-
-PJ_DEF(pjsdp_attr*) pjsdp_attr_clone (pj_pool_t *pool, const pjsdp_attr *rhs)
-{
- struct attr_map_rec *desc;
-
- if (rhs->type >= PJSDP_END_OF_ATTR) {
- pj_assert(0);
- return NULL;
- }
-
- desc = &attr_map[rhs->type];
- return (*desc->clone) (pool, rhs);
-}
-
-PJ_DEF(const pjsdp_attr*) pjsdp_attr_find (int count, const pjsdp_attr *attr_array[], int type)
-{
- int i;
-
- for (i=0; i<count; ++i) {
- if (attr_array[i]->type == type)
- return attr_array[i];
- }
- return NULL;
-}
-
-static int print_connection_info( pjsdp_conn_info *c, char *buf, int len)
-{
- char *p = buf;
-
- if (len < 8+c->net_type.slen+c->addr_type.slen+c->addr.slen) {
- return -1;
- }
- *p++ = 'c';
- *p++ = '=';
- pj_memcpy(p, c->net_type.ptr, c->net_type.slen);
- p += c->net_type.slen;
- *p++ = ' ';
- pj_memcpy(p, c->addr_type.ptr, c->addr_type.slen);
- p += c->addr_type.slen;
- *p++ = ' ';
- pj_memcpy(p, c->addr.ptr, c->addr.slen);
- p += c->addr.slen;
- *p++ = '\r';
- *p++ = '\n';
-
- return p-buf;
-}
-
-PJ_DEF(pjsdp_conn_info*) pjsdp_conn_info_clone (pj_pool_t *pool, const pjsdp_conn_info *rhs)
-{
- pjsdp_conn_info *c = pj_pool_alloc (pool, sizeof(pjsdp_conn_info));
- if (!c) return NULL;
-
- if (!pj_strdup (pool, &c->net_type, &rhs->net_type)) return NULL;
- if (!pj_strdup (pool, &c->addr_type, &rhs->addr_type)) return NULL;
- if (!pj_strdup (pool, &c->addr, &rhs->addr)) return NULL;
-
- return c;
-}
-
-static int print_media_desc( pjsdp_media_desc *m, char *buf, int len)
-{
- char *p = buf;
- char *end = buf+len;
- unsigned i;
- int printed;
-
- /* check length for the "m=" line. */
- if (len < m->desc.media.slen+m->desc.transport.slen+12+24) {
- return -1;
- }
- *p++ = 'm'; /* m= */
- *p++ = '=';
- pj_memcpy(p, m->desc.media.ptr, m->desc.media.slen);
- p += m->desc.media.slen;
- *p++ = ' ';
- printed = pj_utoa(m->desc.port, p);
- p += printed;
- if (m->desc.port_count > 1) {
- *p++ = '/';
- printed = pj_utoa(m->desc.port_count, p);
- p += printed;
- }
- *p++ = ' ';
- pj_memcpy(p, m->desc.transport.ptr, m->desc.transport.slen);
- p += m->desc.transport.slen;
- for (i=0; i<m->desc.fmt_count; ++i) {
- *p++ = ' ';
- pj_memcpy(p, m->desc.fmt[i].ptr, m->desc.fmt[i].slen);
- p += m->desc.fmt[i].slen;
- }
- *p++ = '\r';
- *p++ = '\n';
-
- /* print connection info, if present. */
- if (m->conn) {
- printed = print_connection_info(m->conn, p, end-p);
- if (printed < 0) {
- return -1;
- }
- p += printed;
- }
-
- /* print attributes. */
- for (i=0; i<m->attr_count; ++i) {
- printed = print_attr(m->attr[i], p, end-p);
- if (printed < 0) {
- return -1;
- }
- p += printed;
- }
-
- return p-buf;
-}
-
-PJ_DEF(pjsdp_media_desc*) pjsdp_media_desc_clone (pj_pool_t *pool,
- const pjsdp_media_desc *rhs)
-{
- unsigned int i;
- pjsdp_media_desc *m = pj_pool_alloc (pool, sizeof(pjsdp_media_desc));
- if (!m)
- return NULL;
-
- pj_strdup (pool, &m->desc.media, &rhs->desc.media);
- m->desc.port = rhs->desc.port;
- m->desc.port_count = rhs->desc.port_count;
- pj_strdup (pool, &m->desc.transport, &rhs->desc.transport);
- m->desc.fmt_count = rhs->desc.fmt_count;
- for (i=0; i<rhs->desc.fmt_count; ++i)
- m->desc.fmt[i] = rhs->desc.fmt[i];
-
- if (rhs->conn) {
- m->conn = pjsdp_conn_info_clone (pool, rhs->conn);
- if (!m->conn)
- return NULL;
- } else {
- m->conn = NULL;
- }
-
- m->attr_count = rhs->attr_count;
- for (i=0; i < rhs->attr_count; ++i) {
- m->attr[i] = pjsdp_attr_clone (pool, rhs->attr[i]);
- if (!m->attr[i])
- return NULL;
- }
-
- return m;
-}
-
-/** Check if the media description has the specified attribute. */
-PJ_DEF(pj_bool_t) pjsdp_media_desc_has_attr (const pjsdp_media_desc *m,
- pjsdp_attr_type_e attr_type)
-{
- unsigned i;
- for (i=0; i<m->attr_count; ++i) {
- pjsdp_attr *attr = m->attr[i];
- if (attr->type == attr_type)
- return 1;
- }
- return 0;
-}
-
-/** Find rtpmap attribute for the specified payload type. */
-PJ_DEF(const pjsdp_rtpmap_attr*)
-pjsdp_media_desc_find_rtpmap (const pjsdp_media_desc *m, unsigned pt)
-{
- unsigned i;
- for (i=0; i<m->attr_count; ++i) {
- pjsdp_attr *attr = m->attr[i];
- if (attr->type == PJSDP_ATTR_RTPMAP) {
- const pjsdp_rtpmap_attr* rtpmap = (const pjsdp_rtpmap_attr*)attr;
- if (rtpmap->payload_type == pt)
- return rtpmap;
- }
- }
- return NULL;
-}
-
-
-static int print_session(const pjsdp_session_desc *ses, char *buf, pj_ssize_t len)
-{
- char *p = buf;
- char *end = buf+len;
- unsigned i;
- int printed;
-
- /* Check length for v= and o= lines. */
- if (len < 5+
- 2+ses->origin.user.slen+18+
- ses->origin.net_type.slen+ses->origin.addr.slen + 2)
- {
- return -1;
- }
-
- /* SDP version (v= line) */
- pj_memcpy(p, "v=0\r\n", 5);
- p += 5;
-
- /* Owner (o=) line. */
- *p++ = 'o';
- *p++ = '=';
- pj_memcpy(p, ses->origin.user.ptr, ses->origin.user.slen);
- p += ses->origin.user.slen;
- *p++ = ' ';
- printed = pj_utoa(ses->origin.id, p);
- p += printed;
- *p++ = ' ';
- printed = pj_utoa(ses->origin.version, p);
- p += printed;
- *p++ = ' ';
- pj_memcpy(p, ses->origin.net_type.ptr, ses->origin.net_type.slen);
- p += ses->origin.net_type.slen;
- *p++ = ' ';
- pj_memcpy(p, ses->origin.addr_type.ptr, ses->origin.addr_type.slen);
- p += ses->origin.addr_type.slen;
- *p++ = ' ';
- pj_memcpy(p, ses->origin.addr.ptr, ses->origin.addr.slen);
- p += ses->origin.addr.slen;
- *p++ = '\r';
- *p++ = '\n';
-
- /* Session name (s=) line. */
- if ((end-p) < 8+ses->name.slen) {
- return -1;
- }
- *p++ = 's';
- *p++ = '=';
- pj_memcpy(p, ses->name.ptr, ses->name.slen);
- p += ses->name.slen;
- *p++ = '\r';
- *p++ = '\n';
-
- /* Time */
- if ((end-p) < 24) {
- return -1;
- }
- *p++ = 't';
- *p++ = '=';
- printed = pj_utoa(ses->time.start, p);
- p += printed;
- *p++ = ' ';
- printed = pj_utoa(ses->time.stop, p);
- p += printed;
- *p++ = '\r';
- *p++ = '\n';
-
- /* Connection line (c=) if exist. */
- if (ses->conn) {
- printed = print_connection_info(ses->conn, p, end-p);
- if (printed < 1) {
- return -1;
- }
- p += printed;
- }
-
- /* Print all attribute (a=) lines. */
- for (i=0; i<ses->attr_count; ++i) {
- printed = print_attr(ses->attr[i], p, end-p);
- if (printed < 0) {
- return -1;
- }
- p += printed;
- }
-
- /* Print media (m=) lines. */
- for (i=0; i<ses->media_count; ++i) {
- printed = print_media_desc(ses->media[i], p, end-p);
- if (printed < 0) {
- return -1;
- }
- p += printed;
- }
-
- return p-buf;
-}
-
-/******************************************************************************
- * PARSERS
- */
-
-static void parse_version(pj_scanner *scanner)
-{
- pj_scan_advance_n(scanner, 3, SKIP_WS);
- pj_scan_get_newline(scanner);
-}
-
-static void parse_origin(pj_scanner *scanner, pjsdp_session_desc *ses)
-{
- pj_str_t str;
-
- /* o= */
- pj_scan_advance_n(scanner, 2, SKIP_WS);
-
- /* username. */
- pj_scan_get_until_ch(scanner, ' ', &ses->origin.user);
- pj_scan_get_char(scanner);
-
- /* id */
- pj_scan_get_until_ch(scanner, ' ', &str);
- ses->origin.id = pj_strtoul(&str);
- pj_scan_get_char(scanner);
-
- /* version */
- pj_scan_get_until_ch(scanner, ' ', &str);
- ses->origin.version = pj_strtoul(&str);
- pj_scan_get_char(scanner);
-
- /* network-type */
- pj_scan_get_until_ch(scanner, ' ', &ses->origin.net_type);
- pj_scan_get_char(scanner);
-
- /* addr-type */
- pj_scan_get_until_ch(scanner, ' ', &ses->origin.addr_type);
- pj_scan_get_char(scanner);
-
- /* address */
- pj_scan_get_until_ch(scanner, '\r', &ses->origin.addr);
-
- /* newline */
- pj_scan_get_newline(scanner);
-}
-
-static void parse_time(pj_scanner *scanner, pjsdp_session_desc *ses)
-{
- pj_str_t str;
-
- /* t= */
- pj_scan_advance_n(scanner, 2, SKIP_WS);
-
- /* start time */
- pj_scan_get_until_ch(scanner, ' ', &str);
- ses->time.start = pj_strtoul(&str);
-
- pj_scan_get_char(scanner);
-
- /* stop time */
- pj_scan_get_until_ch(scanner, '\r', &str);
- ses->time.stop = pj_strtoul(&str);
-
- /* newline */
- pj_scan_get_newline(scanner);
-}
-
-static void parse_generic_line(pj_scanner *scanner, pj_str_t *str)
-{
- /* x= */
- pj_scan_advance_n(scanner, 2, SKIP_WS);
-
- /* get anything until newline. */
- pj_scan_get_until_ch(scanner, '\r', str);
-
- /* newline. */
- pj_scan_get_newline(scanner);
-}
-
-static void parse_connection_info(pj_scanner *scanner, pjsdp_conn_info *conn)
-{
- /* c= */
- pj_scan_advance_n(scanner, 2, SKIP_WS);
-
- /* network-type */
- pj_scan_get_until_ch(scanner, ' ', &conn->net_type);
- pj_scan_get_char(scanner);
-
- /* addr-type */
- pj_scan_get_until_ch(scanner, ' ', &conn->addr_type);
- pj_scan_get_char(scanner);
-
- /* address. */
- pj_scan_get_until_ch(scanner, '\r', &conn->addr);
-
- /* newline */
- pj_scan_get_newline(scanner);
-}
-
-static void parse_media(pj_scanner *scanner, pjsdp_media_desc *med)
-{
- pj_str_t str;
-
- /* m= */
- pj_scan_advance_n(scanner, 2, SKIP_WS);
-
- /* type */
- pj_scan_get_until_ch(scanner, ' ', &med->desc.media);
- pj_scan_get_char(scanner);
-
- /* port */
- pj_scan_get(scanner, cs_token, &str);
- med->desc.port = (unsigned short)pj_strtoul(&str);
- if (*scanner->current == '/') {
- /* port count */
- pj_scan_get_char(scanner);
- pj_scan_get(scanner, cs_token, &str);
- med->desc.port_count = pj_strtoul(&str);
-
- } else {
- med->desc.port_count = 0;
- }
-
- if (pj_scan_get_char(scanner) != ' ') {
- PJ_THROW(SYNTAX_ERROR);
- }
-
- /* transport */
- pj_scan_get_until_ch(scanner, ' ', &med->desc.transport);
-
- /* format list */
- med->desc.fmt_count = 0;
- while (*scanner->current == ' ') {
- pj_scan_get_char(scanner);
- pj_scan_get(scanner, cs_token, &med->desc.fmt[med->desc.fmt_count++]);
- }
-
- /* newline */
- pj_scan_get_newline(scanner);
-}
-
-static pjsdp_rtpmap_attr * parse_rtpmap_attr( pj_pool_t *pool, pj_scanner *scanner )
-{
- pjsdp_rtpmap_attr *rtpmap;
- pj_str_t str;
-
- rtpmap = pj_pool_calloc(pool, 1, sizeof(*rtpmap));
- if (pj_scan_get_char(scanner) != ':') {
- PJ_THROW(SYNTAX_ERROR);
- }
- pj_scan_get_until_ch(scanner, ' ', &str);
- rtpmap->payload_type = pj_strtoul(&str);
- pj_scan_get_char(scanner);
-
- pj_scan_get_until_ch(scanner, '/', &rtpmap->encoding_name);
- pj_scan_get_char(scanner);
- pj_scan_get(scanner, cs_token, &str);
- rtpmap->clock_rate = pj_strtoul(&str);
-
- if (*scanner->current == '/') {
- pj_scan_get_char(scanner);
- pj_scan_get_until_ch(scanner, '\r', &rtpmap->parameter);
- }
-
- return rtpmap;
-}
-
-static pjsdp_attr_string * parse_generic_string_attr( pj_pool_t *pool, pj_scanner *scanner )
-{
- pjsdp_attr_string *attr;
- attr = pj_pool_calloc(pool, 1, sizeof(*attr));
-
- if (pj_scan_get_char(scanner) != ':') {
- PJ_THROW(SYNTAX_ERROR);
- }
- pj_scan_get_until_ch(scanner, '\r', &attr->value);
- return attr;
-}
-
-static pjsdp_attr_num * parse_generic_num_attr( pj_pool_t *pool, pj_scanner *scanner )
-{
- pjsdp_attr_num *attr;
- pj_str_t str;
-
- attr = pj_pool_calloc(pool, 1, sizeof(*attr));
-
- if (pj_scan_get_char(scanner) != ':') {
- PJ_THROW(SYNTAX_ERROR);
- }
- pj_scan_get_until_ch(scanner, '\r', &str);
- attr->value = pj_strtoul(&str);
- return attr;
-}
-
-static pjsdp_attr * parse_name_only_attr( pj_pool_t *pool, pj_scanner *scanner )
-{
- pjsdp_attr *attr;
-
- PJ_UNUSED_ARG(scanner)
- attr = pj_pool_calloc(pool, 1, sizeof(*attr));
- return attr;
-}
-
-static pjsdp_fmtp_attr * parse_fmtp_attr( pj_pool_t *pool, pj_scanner *scanner )
-{
- pjsdp_fmtp_attr *fmtp;
-
- fmtp = pj_pool_calloc(pool, 1, sizeof(*fmtp));
-
- if (pj_scan_get_char(scanner) != ':') {
- PJ_THROW(SYNTAX_ERROR);
- }
- pj_scan_get_until_ch(scanner, ' ', &fmtp->format);
- pj_scan_get_char(scanner);
- pj_scan_get_until_ch(scanner, '\r', &fmtp->param);
- return fmtp;
-}
-
-static pjsdp_attr *parse_attr( pj_pool_t *pool, pj_scanner *scanner)
-{
- void * (*parse_func)(pj_pool_t *pool, pj_scanner *scanner) = NULL;
- pj_str_t attrname;
- unsigned i;
- pjsdp_attr *attr;
-
- /* skip a= */
- pj_scan_advance_n(scanner, 2, SKIP_WS);
-
- /* get attr name. */
- pj_scan_get(scanner, cs_token, &attrname);
-
- /* find entry to handle attrname */
- for (i=0; i<PJ_ARRAY_SIZE(attr_map); ++i) {
- struct attr_map_rec *p = &attr_map[i];
- if (pj_strcmp(&attrname, &p->name) == 0) {
- parse_func = p->parse_attr;
- break;
- }
- }
-
- /* fallback to generic string parser. */
- if (parse_func == NULL) {
- parse_func = &parse_generic_string_attr;
- }
-
- attr = (*parse_func)(pool, scanner);
- attr->type = i;
-
- /* newline */
- pj_scan_get_newline(scanner);
-
- return attr;
-}
-
-static void on_scanner_error(pj_scanner *scanner)
-{
- PJ_UNUSED_ARG(scanner)
-
- PJ_THROW(SYNTAX_ERROR);
-}
-
-/*
- * Parse SDP message.
- */
-PJ_DEF(pjsdp_session_desc*) pjsdp_parse( char *buf, pj_size_t len,
- pj_pool_t *pool)
-{
- pj_scanner scanner;
- pjsdp_session_desc *session;
- pjsdp_media_desc *media = NULL;
- void *attr;
- pjsdp_conn_info *conn;
- pj_str_t dummy;
- int cur_name = 254;
- PJ_USE_EXCEPTION;
-
- init_sdp_parser();
-
- pj_scan_init(&scanner, buf, len, 0, &on_scanner_error);
- session = pj_pool_calloc(pool, 1, sizeof(*session));
-
- PJ_TRY {
- while (!pj_scan_is_eof(&scanner)) {
- cur_name = *scanner.current;
- switch (cur_name) {
- case 'a':
- attr = parse_attr(pool, &scanner);
- if (attr) {
- if (media) {
- media->attr[media->attr_count++] = attr;
- } else {
- session->attr[session->attr_count++] = attr;
- }
- }
- break;
- case 'o':
- parse_origin(&scanner, session);
- break;
- case 's':
- parse_generic_line(&scanner, &session->name);
- break;
- case 'c':
- conn = pj_pool_calloc(pool, 1, sizeof(*conn));
- parse_connection_info(&scanner, conn);
- if (media) {
- media->conn = conn;
- } else {
- session->conn = conn;
- }
- break;
- case 't':
- parse_time(&scanner, session);
- break;
- case 'm':
- media = pj_pool_calloc(pool, 1, sizeof(*media));
- parse_media(&scanner, media);
- session->media[ session->media_count++ ] = media;
- break;
- case 'v':
- parse_version(&scanner);
- break;
- default:
- parse_generic_line(&scanner, &dummy);
- break;
- }
- }
- }
- PJ_CATCH(SYNTAX_ERROR) {
- PJ_LOG(2, (LOG_THIS, "Syntax error in SDP parser '%c' line %d col %d",
- cur_name, scanner.line, scanner.col));
- if (!pj_scan_is_eof(&scanner)) {
- if (*scanner.current != '\r') {
- pj_scan_get_until_ch(&scanner, '\r', &dummy);
- }
- pj_scan_get_newline(&scanner);
- }
- }
- PJ_END;
-
- pj_scan_fini(&scanner);
- return session;
-}
-
-/*
- * Print SDP description.
- */
-PJ_DEF(int) pjsdp_print( const pjsdp_session_desc *desc, char *buf, pj_size_t size)
-{
- return print_session(desc, buf, size);
-}
-
-
+/* $Id$
+ *
+ */
+/*
+ * PJMEDIA - Multimedia over IP Stack
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ * Benny Prijono <bennylp@bulukucing.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; 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/scanner.h>
+#include <pj/except.h>
+#include <pj/log.h>
+#include <pj/os.h>
+#include <pj/string.h>
+#include <pj/pool.h>
+
+enum {
+ SKIP_WS = 0,
+ SYNTAX_ERROR = 1,
+};
+#define TOKEN "-.!%*_=`'~"
+#define NTP_OFFSET ((pj_uint32_t)2208988800)
+#define LOG_THIS "sdp"
+
+/*
+ * Prototypes for line parser.
+ */
+static void parse_version(pj_scanner *scanner);
+static void parse_origin(pj_scanner *scanner, pjsdp_session_desc *ses);
+static void parse_time(pj_scanner *scanner, pjsdp_session_desc *ses);
+static void parse_generic_line(pj_scanner *scanner, pj_str_t *str);
+static void parse_connection_info(pj_scanner *scanner, pjsdp_conn_info *conn);
+static pjsdp_attr *parse_attr(pj_pool_t *pool, pj_scanner *scanner);
+static void parse_media(pj_scanner *scanner, pjsdp_media_desc *med);
+
+/*
+ * Prototypes for attribute parsers.
+ */
+static pjsdp_rtpmap_attr * parse_rtpmap_attr( pj_pool_t *pool, pj_scanner *scanner );
+static pjsdp_attr_string * parse_generic_string_attr( pj_pool_t *pool, pj_scanner *scanner );
+static pjsdp_attr_num * parse_generic_num_attr( pj_pool_t *pool, pj_scanner *scanner );
+static pjsdp_attr * parse_name_only_attr( pj_pool_t *pool, pj_scanner *scanner );
+static pjsdp_fmtp_attr * parse_fmtp_attr( pj_pool_t *pool, pj_scanner *scanner );
+
+
+/*
+ * Prototypes for functions to print attribute.
+ * All of them returns integer for the length printed, or -1 on error.
+ */
+static int print_rtpmap_attr(const pjsdp_rtpmap_attr *attr,
+ char *buf, int length);
+static int print_generic_string_attr(const pjsdp_attr_string *attr,
+ char *buf, int length);
+static int print_generic_num_attr(const pjsdp_attr_num *attr,
+ char *buf, int length);
+static int print_name_only_attr(const pjsdp_attr *attr,
+ char *buf, int length);
+static int print_fmtp_attr(const pjsdp_fmtp_attr *attr,
+ char *buf, int length);
+
+/*
+ * Prototypes for cloning attributes.
+ */
+static pjsdp_attr* clone_rtpmap_attr (pj_pool_t *pool, const pjsdp_attr *rhs);
+static pjsdp_attr* clone_generic_string_attr (pj_pool_t *pool, const pjsdp_attr *rhs);
+static pjsdp_attr* clone_generic_num_attr (pj_pool_t *pool, const pjsdp_attr *rhs);
+static pjsdp_attr* clone_name_only_attr (pj_pool_t *pool, const pjsdp_attr *rhs);
+static pjsdp_attr* clone_fmtp_attr (pj_pool_t *pool, const pjsdp_attr *rhs);
+
+
+/*
+ * Prototypes
+ */
+static void init_sdp_parser(void);
+
+
+typedef void * (*FPARSE)(pj_pool_t *pool, pj_scanner *scanner);
+typedef int (*FPRINT)(const void *attr, char *buf, int length);
+typedef pjsdp_attr* (*FCLONE)(pj_pool_t *pool, const pjsdp_attr *rhs);
+
+/*
+ * Array of functions to print attribute.
+ */
+static struct attr_map_rec
+{
+ pj_str_t name;
+ FPARSE parse_attr;
+ FPRINT print_attr;
+ FCLONE clone;
+} attr_map[] =
+{
+ {{"rtpmap", 6}, (FPARSE)&parse_rtpmap_attr, (FPRINT)&print_rtpmap_attr, (FCLONE)&clone_rtpmap_attr},
+ {{"cat", 3}, (FPARSE)&parse_generic_string_attr, (FPRINT)&print_generic_string_attr, (FCLONE)&clone_generic_string_attr},
+ {{"keywds", 6}, (FPARSE)&parse_generic_string_attr, (FPRINT)&print_generic_string_attr, (FCLONE)&clone_generic_string_attr},
+ {{"tool", 4}, (FPARSE)&parse_generic_string_attr, (FPRINT)&print_generic_string_attr, (FCLONE)&clone_generic_string_attr},
+ {{"ptime", 5}, (FPARSE)&parse_generic_num_attr, (FPRINT)&print_generic_num_attr, (FCLONE)&clone_generic_num_attr},
+ {{"recvonly", 8}, (FPARSE)&parse_name_only_attr, (FPRINT)&print_name_only_attr, (FCLONE)&clone_name_only_attr},
+ {{"sendonly", 8}, (FPARSE)&parse_name_only_attr, (FPRINT)&print_name_only_attr, (FCLONE)&clone_name_only_attr},
+ {{"sendrecv", 8}, (FPARSE)&parse_name_only_attr, (FPRINT)&print_name_only_attr, (FCLONE)&clone_name_only_attr},
+ {{"orient", 6}, (FPARSE)&parse_generic_string_attr, (FPRINT)&print_generic_string_attr, (FCLONE)&clone_generic_string_attr},
+ {{"type", 4}, (FPARSE)&parse_generic_string_attr, (FPRINT)&print_generic_string_attr, (FCLONE)&clone_generic_string_attr},
+ {{"charset", 7}, (FPARSE)&parse_generic_string_attr, (FPRINT)&print_generic_string_attr, (FCLONE)&clone_generic_string_attr},
+ {{"sdplang", 7}, (FPARSE)&parse_generic_string_attr, (FPRINT)&print_generic_string_attr, (FCLONE)&clone_generic_string_attr},
+ {{"lang", 4}, (FPARSE)&parse_generic_string_attr, (FPRINT)&print_generic_string_attr, (FCLONE)&clone_generic_string_attr},
+ {{"framerate", 9}, (FPARSE)&parse_generic_string_attr, (FPRINT)&print_generic_string_attr, (FCLONE)&clone_generic_string_attr},
+ {{"quality", 7}, (FPARSE)&parse_generic_num_attr, (FPRINT)&print_generic_num_attr, (FCLONE)&clone_generic_num_attr},
+ {{"fmtp", 4}, (FPARSE)&parse_fmtp_attr, (FPRINT)&print_fmtp_attr, (FCLONE)&clone_fmtp_attr},
+ {{"inactive", 8}, (FPARSE)&parse_name_only_attr, (FPRINT)&print_name_only_attr, (FCLONE)&clone_name_only_attr},
+ {{"", 0}, NULL, (FPRINT)&print_generic_string_attr, (FCLONE)&clone_generic_string_attr}
+};
+
+/*
+ * Scanner character specification.
+ */
+static int is_initialized;
+static pj_char_spec cs_token;
+
+static void init_sdp_parser(void)
+{
+ if (is_initialized == 0) {
+ is_initialized = 1;
+ if (is_initialized != 1) {
+ return;
+ }
+ }
+ pj_cs_add_alpha(cs_token);
+ pj_cs_add_num(cs_token);
+ pj_cs_add_str( cs_token, TOKEN);
+}
+
+static int print_rtpmap_attr(const pjsdp_rtpmap_attr *rtpmap,
+ char *buf, int len)
+{
+ char *p = buf;
+
+ if (len < 16+rtpmap->encoding_name.slen+rtpmap->parameter.slen) {
+ return -1;
+ }
+
+ /* colon and payload type. */
+ *p++ = ':';
+ len = pj_utoa(rtpmap->payload_type, p);
+ p += len;
+
+ /* space, encoding name */
+ *p++ = ' ';
+ pj_memcpy(p, rtpmap->encoding_name.ptr, rtpmap->encoding_name.slen);
+ p += rtpmap->encoding_name.slen;
+
+ /* slash, clock-rate. */
+ *p++ = '/';
+ len = pj_utoa(rtpmap->clock_rate, p);
+ p += len;
+
+ /* optionally add encoding parameter. */
+ if (rtpmap->parameter.slen) {
+ *p++ = '/';
+ pj_memcpy(p, rtpmap->parameter.ptr, rtpmap->parameter.slen);
+ p += rtpmap->parameter.slen;
+ }
+
+ return p-buf;
+}
+
+static int print_generic_string_attr(const pjsdp_attr_string *attr,
+ char *buf, int len)
+{
+ char *p = buf;
+
+ if (len < attr->value.slen + 4) {
+ return -1;
+ }
+
+ /* colon and attribute value. */
+ *p++ = ':';
+ pj_memcpy(p, attr->value.ptr, attr->value.slen);
+ p += attr->value.slen;
+
+ return p-buf;
+}
+
+static int print_generic_num_attr(const pjsdp_attr_num *attr, char *buf, int len)
+{
+ char *p = buf;
+
+ if (len < 10) {
+ return -1;
+ }
+ *p++ = ':';
+ return pj_utoa(attr->value, p);
+}
+
+static int print_name_only_attr(const pjsdp_attr *attr, char *buf, int len)
+{
+ PJ_UNUSED_ARG(attr)
+ PJ_UNUSED_ARG(buf)
+ PJ_UNUSED_ARG(len)
+ return 0;
+}
+
+static int print_fmtp_attr(const pjsdp_fmtp_attr *fmtp, char *buf, int len)
+{
+ char *p = buf;
+
+ if (len < 4+fmtp->format.slen+fmtp->param.slen) {
+ return -1;
+ }
+
+ /* colon and format. */
+ *p++ = ':';
+ pj_memcpy(p, fmtp->format.ptr, fmtp->format.slen);
+ p += fmtp->format.slen;
+
+ /* space and parameter. */
+ *p++ = ' ';
+ pj_memcpy(p, fmtp->param.ptr, fmtp->param.slen);
+ p += fmtp->param.slen;
+
+ return p-buf;
+}
+
+
+static int print_attr(const pjsdp_attr *attr, char *buf, int len)
+{
+ char *p = buf;
+ struct attr_map_rec *desc = &attr_map[attr->type];
+
+ if (len < 16) {
+ return -1;
+ }
+
+ *p++ = 'a';
+ *p++ = '=';
+ pj_memcpy(p, desc->name.ptr, desc->name.slen);
+ p += desc->name.slen;
+
+ len = (*desc->print_attr)(attr, p, (buf+len)-p);
+ if (len < 0) {
+ return -1;
+ }
+ p += len;
+ *p++ = '\r';
+ *p++ = '\n';
+ return p-buf;
+}
+
+static pjsdp_attr* clone_rtpmap_attr (pj_pool_t *pool, const pjsdp_attr *p)
+{
+ const pjsdp_rtpmap_attr *rhs = (const pjsdp_rtpmap_attr*)p;
+ pjsdp_rtpmap_attr *attr = pj_pool_alloc (pool, sizeof(pjsdp_rtpmap_attr));
+ if (!attr)
+ return NULL;
+
+ attr->type = rhs->type;
+ attr->payload_type = rhs->payload_type;
+ if (!pj_strdup (pool, &attr->encoding_name, &rhs->encoding_name)) return NULL;
+ attr->clock_rate = rhs->clock_rate;
+ if (!pj_strdup (pool, &attr->parameter, &rhs->parameter)) return NULL;
+
+ return (pjsdp_attr*)attr;
+}
+
+static pjsdp_attr* clone_generic_string_attr (pj_pool_t *pool, const pjsdp_attr *p)
+{
+ const pjsdp_attr_string* rhs = (const pjsdp_attr_string*) p;
+ pjsdp_attr_string *attr = pj_pool_alloc (pool, sizeof(pjsdp_attr_string));
+ if (!attr)
+ return NULL;
+
+ attr->type = rhs->type;
+ if (!pj_strdup (pool, &attr->value, &rhs->value)) return NULL;
+
+ return (pjsdp_attr*)attr;
+}
+
+static pjsdp_attr* clone_generic_num_attr (pj_pool_t *pool, const pjsdp_attr *p)
+{
+ const pjsdp_attr_num* rhs = (const pjsdp_attr_num*) p;
+ pjsdp_attr_num *attr = pj_pool_alloc (pool, sizeof(pjsdp_attr_num));
+ if (!attr)
+ return NULL;
+
+ attr->type = rhs->type;
+ attr->value = rhs->value;
+
+ return (pjsdp_attr*)attr;
+}
+
+static pjsdp_attr* clone_name_only_attr (pj_pool_t *pool, const pjsdp_attr *rhs)
+{
+ pjsdp_attr *attr = pj_pool_alloc (pool, sizeof(pjsdp_attr));
+ if (!attr)
+ return NULL;
+
+ attr->type = rhs->type;
+ return attr;
+}
+
+static pjsdp_attr* clone_fmtp_attr (pj_pool_t *pool, const pjsdp_attr *p)
+{
+ const pjsdp_fmtp_attr* rhs = (const pjsdp_fmtp_attr*) p;
+ pjsdp_fmtp_attr *attr = pj_pool_alloc (pool, sizeof(pjsdp_fmtp_attr));
+ if (!attr)
+ return NULL;
+
+ attr->type = rhs->type;
+ if (!pj_strdup (pool, &attr->format, &rhs->format)) return NULL;
+ if (!pj_strdup (pool, &attr->param, &rhs->param)) return NULL;
+
+ return (pjsdp_attr*)attr;
+}
+
+PJ_DEF(pjsdp_attr*) pjsdp_attr_clone (pj_pool_t *pool, const pjsdp_attr *rhs)
+{
+ struct attr_map_rec *desc;
+
+ if (rhs->type >= PJSDP_END_OF_ATTR) {
+ pj_assert(0);
+ return NULL;
+ }
+
+ desc = &attr_map[rhs->type];
+ return (*desc->clone) (pool, rhs);
+}
+
+PJ_DEF(const pjsdp_attr*) pjsdp_attr_find (int count, const pjsdp_attr *attr_array[], int type)
+{
+ int i;
+
+ for (i=0; i<count; ++i) {
+ if (attr_array[i]->type == type)
+ return attr_array[i];
+ }
+ return NULL;
+}
+
+static int print_connection_info( pjsdp_conn_info *c, char *buf, int len)
+{
+ char *p = buf;
+
+ if (len < 8+c->net_type.slen+c->addr_type.slen+c->addr.slen) {
+ return -1;
+ }
+ *p++ = 'c';
+ *p++ = '=';
+ pj_memcpy(p, c->net_type.ptr, c->net_type.slen);
+ p += c->net_type.slen;
+ *p++ = ' ';
+ pj_memcpy(p, c->addr_type.ptr, c->addr_type.slen);
+ p += c->addr_type.slen;
+ *p++ = ' ';
+ pj_memcpy(p, c->addr.ptr, c->addr.slen);
+ p += c->addr.slen;
+ *p++ = '\r';
+ *p++ = '\n';
+
+ return p-buf;
+}
+
+PJ_DEF(pjsdp_conn_info*) pjsdp_conn_info_clone (pj_pool_t *pool, const pjsdp_conn_info *rhs)
+{
+ pjsdp_conn_info *c = pj_pool_alloc (pool, sizeof(pjsdp_conn_info));
+ if (!c) return NULL;
+
+ if (!pj_strdup (pool, &c->net_type, &rhs->net_type)) return NULL;
+ if (!pj_strdup (pool, &c->addr_type, &rhs->addr_type)) return NULL;
+ if (!pj_strdup (pool, &c->addr, &rhs->addr)) return NULL;
+
+ return c;
+}
+
+static int print_media_desc( pjsdp_media_desc *m, char *buf, int len)
+{
+ char *p = buf;
+ char *end = buf+len;
+ unsigned i;
+ int printed;
+
+ /* check length for the "m=" line. */
+ if (len < m->desc.media.slen+m->desc.transport.slen+12+24) {
+ return -1;
+ }
+ *p++ = 'm'; /* m= */
+ *p++ = '=';
+ pj_memcpy(p, m->desc.media.ptr, m->desc.media.slen);
+ p += m->desc.media.slen;
+ *p++ = ' ';
+ printed = pj_utoa(m->desc.port, p);
+ p += printed;
+ if (m->desc.port_count > 1) {
+ *p++ = '/';
+ printed = pj_utoa(m->desc.port_count, p);
+ p += printed;
+ }
+ *p++ = ' ';
+ pj_memcpy(p, m->desc.transport.ptr, m->desc.transport.slen);
+ p += m->desc.transport.slen;
+ for (i=0; i<m->desc.fmt_count; ++i) {
+ *p++ = ' ';
+ pj_memcpy(p, m->desc.fmt[i].ptr, m->desc.fmt[i].slen);
+ p += m->desc.fmt[i].slen;
+ }
+ *p++ = '\r';
+ *p++ = '\n';
+
+ /* print connection info, if present. */
+ if (m->conn) {
+ printed = print_connection_info(m->conn, p, end-p);
+ if (printed < 0) {
+ return -1;
+ }
+ p += printed;
+ }
+
+ /* print attributes. */
+ for (i=0; i<m->attr_count; ++i) {
+ printed = print_attr(m->attr[i], p, end-p);
+ if (printed < 0) {
+ return -1;
+ }
+ p += printed;
+ }
+
+ return p-buf;
+}
+
+PJ_DEF(pjsdp_media_desc*) pjsdp_media_desc_clone (pj_pool_t *pool,
+ const pjsdp_media_desc *rhs)
+{
+ unsigned int i;
+ pjsdp_media_desc *m = pj_pool_alloc (pool, sizeof(pjsdp_media_desc));
+ if (!m)
+ return NULL;
+
+ pj_strdup (pool, &m->desc.media, &rhs->desc.media);
+ m->desc.port = rhs->desc.port;
+ m->desc.port_count = rhs->desc.port_count;
+ pj_strdup (pool, &m->desc.transport, &rhs->desc.transport);
+ m->desc.fmt_count = rhs->desc.fmt_count;
+ for (i=0; i<rhs->desc.fmt_count; ++i)
+ m->desc.fmt[i] = rhs->desc.fmt[i];
+
+ if (rhs->conn) {
+ m->conn = pjsdp_conn_info_clone (pool, rhs->conn);
+ if (!m->conn)
+ return NULL;
+ } else {
+ m->conn = NULL;
+ }
+
+ m->attr_count = rhs->attr_count;
+ for (i=0; i < rhs->attr_count; ++i) {
+ m->attr[i] = pjsdp_attr_clone (pool, rhs->attr[i]);
+ if (!m->attr[i])
+ return NULL;
+ }
+
+ return m;
+}
+
+/** Check if the media description has the specified attribute. */
+PJ_DEF(pj_bool_t) pjsdp_media_desc_has_attr (const pjsdp_media_desc *m,
+ pjsdp_attr_type_e attr_type)
+{
+ unsigned i;
+ for (i=0; i<m->attr_count; ++i) {
+ pjsdp_attr *attr = m->attr[i];
+ if (attr->type == attr_type)
+ return 1;
+ }
+ return 0;
+}
+
+/** Find rtpmap attribute for the specified payload type. */
+PJ_DEF(const pjsdp_rtpmap_attr*)
+pjsdp_media_desc_find_rtpmap (const pjsdp_media_desc *m, unsigned pt)
+{
+ unsigned i;
+ for (i=0; i<m->attr_count; ++i) {
+ pjsdp_attr *attr = m->attr[i];
+ if (attr->type == PJSDP_ATTR_RTPMAP) {
+ const pjsdp_rtpmap_attr* rtpmap = (const pjsdp_rtpmap_attr*)attr;
+ if (rtpmap->payload_type == pt)
+ return rtpmap;
+ }
+ }
+ return NULL;
+}
+
+
+static int print_session(const pjsdp_session_desc *ses, char *buf, pj_ssize_t len)
+{
+ char *p = buf;
+ char *end = buf+len;
+ unsigned i;
+ int printed;
+
+ /* Check length for v= and o= lines. */
+ if (len < 5+
+ 2+ses->origin.user.slen+18+
+ ses->origin.net_type.slen+ses->origin.addr.slen + 2)
+ {
+ return -1;
+ }
+
+ /* SDP version (v= line) */
+ pj_memcpy(p, "v=0\r\n", 5);
+ p += 5;
+
+ /* Owner (o=) line. */
+ *p++ = 'o';
+ *p++ = '=';
+ pj_memcpy(p, ses->origin.user.ptr, ses->origin.user.slen);
+ p += ses->origin.user.slen;
+ *p++ = ' ';
+ printed = pj_utoa(ses->origin.id, p);
+ p += printed;
+ *p++ = ' ';
+ printed = pj_utoa(ses->origin.version, p);
+ p += printed;
+ *p++ = ' ';
+ pj_memcpy(p, ses->origin.net_type.ptr, ses->origin.net_type.slen);
+ p += ses->origin.net_type.slen;
+ *p++ = ' ';
+ pj_memcpy(p, ses->origin.addr_type.ptr, ses->origin.addr_type.slen);
+ p += ses->origin.addr_type.slen;
+ *p++ = ' ';
+ pj_memcpy(p, ses->origin.addr.ptr, ses->origin.addr.slen);
+ p += ses->origin.addr.slen;
+ *p++ = '\r';
+ *p++ = '\n';
+
+ /* Session name (s=) line. */
+ if ((end-p) < 8+ses->name.slen) {
+ return -1;
+ }
+ *p++ = 's';
+ *p++ = '=';
+ pj_memcpy(p, ses->name.ptr, ses->name.slen);
+ p += ses->name.slen;
+ *p++ = '\r';
+ *p++ = '\n';
+
+ /* Time */
+ if ((end-p) < 24) {
+ return -1;
+ }
+ *p++ = 't';
+ *p++ = '=';
+ printed = pj_utoa(ses->time.start, p);
+ p += printed;
+ *p++ = ' ';
+ printed = pj_utoa(ses->time.stop, p);
+ p += printed;
+ *p++ = '\r';
+ *p++ = '\n';
+
+ /* Connection line (c=) if exist. */
+ if (ses->conn) {
+ printed = print_connection_info(ses->conn, p, end-p);
+ if (printed < 1) {
+ return -1;
+ }
+ p += printed;
+ }
+
+ /* Print all attribute (a=) lines. */
+ for (i=0; i<ses->attr_count; ++i) {
+ printed = print_attr(ses->attr[i], p, end-p);
+ if (printed < 0) {
+ return -1;
+ }
+ p += printed;
+ }
+
+ /* Print media (m=) lines. */
+ for (i=0; i<ses->media_count; ++i) {
+ printed = print_media_desc(ses->media[i], p, end-p);
+ if (printed < 0) {
+ return -1;
+ }
+ p += printed;
+ }
+
+ return p-buf;
+}
+
+/******************************************************************************
+ * PARSERS
+ */
+
+static void parse_version(pj_scanner *scanner)
+{
+ pj_scan_advance_n(scanner, 3, SKIP_WS);
+ pj_scan_get_newline(scanner);
+}
+
+static void parse_origin(pj_scanner *scanner, pjsdp_session_desc *ses)
+{
+ pj_str_t str;
+
+ /* o= */
+ pj_scan_advance_n(scanner, 2, SKIP_WS);
+
+ /* username. */
+ pj_scan_get_until_ch(scanner, ' ', &ses->origin.user);
+ pj_scan_get_char(scanner);
+
+ /* id */
+ pj_scan_get_until_ch(scanner, ' ', &str);
+ ses->origin.id = pj_strtoul(&str);
+ pj_scan_get_char(scanner);
+
+ /* version */
+ pj_scan_get_until_ch(scanner, ' ', &str);
+ ses->origin.version = pj_strtoul(&str);
+ pj_scan_get_char(scanner);
+
+ /* network-type */
+ pj_scan_get_until_ch(scanner, ' ', &ses->origin.net_type);
+ pj_scan_get_char(scanner);
+
+ /* addr-type */
+ pj_scan_get_until_ch(scanner, ' ', &ses->origin.addr_type);
+ pj_scan_get_char(scanner);
+
+ /* address */
+ pj_scan_get_until_ch(scanner, '\r', &ses->origin.addr);
+
+ /* newline */
+ pj_scan_get_newline(scanner);
+}
+
+static void parse_time(pj_scanner *scanner, pjsdp_session_desc *ses)
+{
+ pj_str_t str;
+
+ /* t= */
+ pj_scan_advance_n(scanner, 2, SKIP_WS);
+
+ /* start time */
+ pj_scan_get_until_ch(scanner, ' ', &str);
+ ses->time.start = pj_strtoul(&str);
+
+ pj_scan_get_char(scanner);
+
+ /* stop time */
+ pj_scan_get_until_ch(scanner, '\r', &str);
+ ses->time.stop = pj_strtoul(&str);
+
+ /* newline */
+ pj_scan_get_newline(scanner);
+}
+
+static void parse_generic_line(pj_scanner *scanner, pj_str_t *str)
+{
+ /* x= */
+ pj_scan_advance_n(scanner, 2, SKIP_WS);
+
+ /* get anything until newline. */
+ pj_scan_get_until_ch(scanner, '\r', str);
+
+ /* newline. */
+ pj_scan_get_newline(scanner);
+}
+
+static void parse_connection_info(pj_scanner *scanner, pjsdp_conn_info *conn)
+{
+ /* c= */
+ pj_scan_advance_n(scanner, 2, SKIP_WS);
+
+ /* network-type */
+ pj_scan_get_until_ch(scanner, ' ', &conn->net_type);
+ pj_scan_get_char(scanner);
+
+ /* addr-type */
+ pj_scan_get_until_ch(scanner, ' ', &conn->addr_type);
+ pj_scan_get_char(scanner);
+
+ /* address. */
+ pj_scan_get_until_ch(scanner, '\r', &conn->addr);
+
+ /* newline */
+ pj_scan_get_newline(scanner);
+}
+
+static void parse_media(pj_scanner *scanner, pjsdp_media_desc *med)
+{
+ pj_str_t str;
+
+ /* m= */
+ pj_scan_advance_n(scanner, 2, SKIP_WS);
+
+ /* type */
+ pj_scan_get_until_ch(scanner, ' ', &med->desc.media);
+ pj_scan_get_char(scanner);
+
+ /* port */
+ pj_scan_get(scanner, cs_token, &str);
+ med->desc.port = (unsigned short)pj_strtoul(&str);
+ if (*scanner->current == '/') {
+ /* port count */
+ pj_scan_get_char(scanner);
+ pj_scan_get(scanner, cs_token, &str);
+ med->desc.port_count = pj_strtoul(&str);
+
+ } else {
+ med->desc.port_count = 0;
+ }
+
+ if (pj_scan_get_char(scanner) != ' ') {
+ PJ_THROW(SYNTAX_ERROR);
+ }
+
+ /* transport */
+ pj_scan_get_until_ch(scanner, ' ', &med->desc.transport);
+
+ /* format list */
+ med->desc.fmt_count = 0;
+ while (*scanner->current == ' ') {
+ pj_scan_get_char(scanner);
+ pj_scan_get(scanner, cs_token, &med->desc.fmt[med->desc.fmt_count++]);
+ }
+
+ /* newline */
+ pj_scan_get_newline(scanner);
+}
+
+static pjsdp_rtpmap_attr * parse_rtpmap_attr( pj_pool_t *pool, pj_scanner *scanner )
+{
+ pjsdp_rtpmap_attr *rtpmap;
+ pj_str_t str;
+
+ rtpmap = pj_pool_calloc(pool, 1, sizeof(*rtpmap));
+ if (pj_scan_get_char(scanner) != ':') {
+ PJ_THROW(SYNTAX_ERROR);
+ }
+ pj_scan_get_until_ch(scanner, ' ', &str);
+ rtpmap->payload_type = pj_strtoul(&str);
+ pj_scan_get_char(scanner);
+
+ pj_scan_get_until_ch(scanner, '/', &rtpmap->encoding_name);
+ pj_scan_get_char(scanner);
+ pj_scan_get(scanner, cs_token, &str);
+ rtpmap->clock_rate = pj_strtoul(&str);
+
+ if (*scanner->current == '/') {
+ pj_scan_get_char(scanner);
+ pj_scan_get_until_ch(scanner, '\r', &rtpmap->parameter);
+ }
+
+ return rtpmap;
+}
+
+static pjsdp_attr_string * parse_generic_string_attr( pj_pool_t *pool, pj_scanner *scanner )
+{
+ pjsdp_attr_string *attr;
+ attr = pj_pool_calloc(pool, 1, sizeof(*attr));
+
+ if (pj_scan_get_char(scanner) != ':') {
+ PJ_THROW(SYNTAX_ERROR);
+ }
+ pj_scan_get_until_ch(scanner, '\r', &attr->value);
+ return attr;
+}
+
+static pjsdp_attr_num * parse_generic_num_attr( pj_pool_t *pool, pj_scanner *scanner )
+{
+ pjsdp_attr_num *attr;
+ pj_str_t str;
+
+ attr = pj_pool_calloc(pool, 1, sizeof(*attr));
+
+ if (pj_scan_get_char(scanner) != ':') {
+ PJ_THROW(SYNTAX_ERROR);
+ }
+ pj_scan_get_until_ch(scanner, '\r', &str);
+ attr->value = pj_strtoul(&str);
+ return attr;
+}
+
+static pjsdp_attr * parse_name_only_attr( pj_pool_t *pool, pj_scanner *scanner )
+{
+ pjsdp_attr *attr;
+
+ PJ_UNUSED_ARG(scanner)
+ attr = pj_pool_calloc(pool, 1, sizeof(*attr));
+ return attr;
+}
+
+static pjsdp_fmtp_attr * parse_fmtp_attr( pj_pool_t *pool, pj_scanner *scanner )
+{
+ pjsdp_fmtp_attr *fmtp;
+
+ fmtp = pj_pool_calloc(pool, 1, sizeof(*fmtp));
+
+ if (pj_scan_get_char(scanner) != ':') {
+ PJ_THROW(SYNTAX_ERROR);
+ }
+ pj_scan_get_until_ch(scanner, ' ', &fmtp->format);
+ pj_scan_get_char(scanner);
+ pj_scan_get_until_ch(scanner, '\r', &fmtp->param);
+ return fmtp;
+}
+
+static pjsdp_attr *parse_attr( pj_pool_t *pool, pj_scanner *scanner)
+{
+ void * (*parse_func)(pj_pool_t *pool, pj_scanner *scanner) = NULL;
+ pj_str_t attrname;
+ unsigned i;
+ pjsdp_attr *attr;
+
+ /* skip a= */
+ pj_scan_advance_n(scanner, 2, SKIP_WS);
+
+ /* get attr name. */
+ pj_scan_get(scanner, cs_token, &attrname);
+
+ /* find entry to handle attrname */
+ for (i=0; i<PJ_ARRAY_SIZE(attr_map); ++i) {
+ struct attr_map_rec *p = &attr_map[i];
+ if (pj_strcmp(&attrname, &p->name) == 0) {
+ parse_func = p->parse_attr;
+ break;
+ }
+ }
+
+ /* fallback to generic string parser. */
+ if (parse_func == NULL) {
+ parse_func = &parse_generic_string_attr;
+ }
+
+ attr = (*parse_func)(pool, scanner);
+ attr->type = i;
+
+ /* newline */
+ pj_scan_get_newline(scanner);
+
+ return attr;
+}
+
+static void on_scanner_error(pj_scanner *scanner)
+{
+ PJ_UNUSED_ARG(scanner)
+
+ PJ_THROW(SYNTAX_ERROR);
+}
+
+/*
+ * Parse SDP message.
+ */
+PJ_DEF(pjsdp_session_desc*) pjsdp_parse( char *buf, pj_size_t len,
+ pj_pool_t *pool)
+{
+ pj_scanner scanner;
+ pjsdp_session_desc *session;
+ pjsdp_media_desc *media = NULL;
+ void *attr;
+ pjsdp_conn_info *conn;
+ pj_str_t dummy;
+ int cur_name = 254;
+ PJ_USE_EXCEPTION;
+
+ init_sdp_parser();
+
+ pj_scan_init(&scanner, buf, len, 0, &on_scanner_error);
+ session = pj_pool_calloc(pool, 1, sizeof(*session));
+
+ PJ_TRY {
+ while (!pj_scan_is_eof(&scanner)) {
+ cur_name = *scanner.current;
+ switch (cur_name) {
+ case 'a':
+ attr = parse_attr(pool, &scanner);
+ if (attr) {
+ if (media) {
+ media->attr[media->attr_count++] = attr;
+ } else {
+ session->attr[session->attr_count++] = attr;
+ }
+ }
+ break;
+ case 'o':
+ parse_origin(&scanner, session);
+ break;
+ case 's':
+ parse_generic_line(&scanner, &session->name);
+ break;
+ case 'c':
+ conn = pj_pool_calloc(pool, 1, sizeof(*conn));
+ parse_connection_info(&scanner, conn);
+ if (media) {
+ media->conn = conn;
+ } else {
+ session->conn = conn;
+ }
+ break;
+ case 't':
+ parse_time(&scanner, session);
+ break;
+ case 'm':
+ media = pj_pool_calloc(pool, 1, sizeof(*media));
+ parse_media(&scanner, media);
+ session->media[ session->media_count++ ] = media;
+ break;
+ case 'v':
+ parse_version(&scanner);
+ break;
+ default:
+ parse_generic_line(&scanner, &dummy);
+ break;
+ }
+ }
+ }
+ PJ_CATCH(SYNTAX_ERROR) {
+ PJ_LOG(2, (LOG_THIS, "Syntax error in SDP parser '%c' line %d col %d",
+ cur_name, scanner.line, scanner.col));
+ if (!pj_scan_is_eof(&scanner)) {
+ if (*scanner.current != '\r') {
+ pj_scan_get_until_ch(&scanner, '\r', &dummy);
+ }
+ pj_scan_get_newline(&scanner);
+ }
+ }
+ PJ_END;
+
+ pj_scan_fini(&scanner);
+ return session;
+}
+
+/*
+ * Print SDP description.
+ */
+PJ_DEF(int) pjsdp_print( const pjsdp_session_desc *desc, char *buf, pj_size_t size)
+{
+ return print_session(desc, buf, size);
+}
+
+
diff --git a/pjmedia/src/pjmedia/sdp.h b/pjmedia/src/pjmedia/sdp.h
index 6fc4ec4..5d537c9 100644
--- a/pjmedia/src/pjmedia/sdp.h
+++ b/pjmedia/src/pjmedia/sdp.h
@@ -1,318 +1,339 @@
-/* $Id$
- *
- */
-
-#ifndef __PJSDP_SDP_H__
-#define __PJSDP_SDP_H__
-
-/**
- * @defgroup PJSDP SDP Library
- */
-/**
- * @file sdp.h
- * @brief SDP header file.
- */
-/**
- * @defgroup PJ_SDP_H SDP stack.
- * @ingroup PJSDP
- * @{
- *
- * This SDP module consists of SDP parser, SDP structure, and function to
- * print back the structure as SDP message.
- */
-
-#include <pj/types.h>
-
-PJ_BEGIN_DECL
-
-#define PJSDP_MAX_FMT 32
-#define PJSDP_MAX_ATTR 32
-#define PJSDP_MAX_MEDIA 16
-
-/**
- * This enumeration describes the attribute type.
- */
-typedef enum pjsdp_attr_type_e
-{
- PJSDP_ATTR_RTPMAP,
- PJSDP_ATTR_CAT,
- PJSDP_ATTR_KEYWORDS,
- PJSDP_ATTR_TOOL,
- PJSDP_ATTR_PTIME,
- PJSDP_ATTR_RECV_ONLY,
- PJSDP_ATTR_SEND_ONLY,
- PJSDP_ATTR_SEND_RECV,
- PJSDP_ATTR_ORIENT,
- PJSDP_ATTR_TYPE,
- PJSDP_ATTR_CHARSET,
- PJSDP_ATTR_SDP_LANG,
- PJSDP_ATTR_LANG,
- PJSDP_ATTR_FRAME_RATE,
- PJSDP_ATTR_QUALITY,
- PJSDP_ATTR_FMTP,
- PJSDP_ATTR_INACTIVE,
- PJSDP_ATTR_GENERIC,
- PJSDP_END_OF_ATTR,
-} pjsdp_attr_type_e;
-
-
-/**
- * This structure keeps the common attributes that all 'descendants'
- * will have.
- */
-typedef struct pjsdp_attr
-{
- pjsdp_attr_type_e type; /**< Attribute type. */
-} pjsdp_attr;
-
-
-/**
- * This is the structure to represent generic attribute which has a
- * string value.
- */
-typedef struct pjsdp_attr_string
-{
- pjsdp_attr_type_e type;
- pj_str_t value;
-} pjsdp_attr_string;
-
-
-/**
- * This is the structure to represent generic SDP attribute which has
- * a numeric value.
- */
-typedef struct pjsdp_attr_num
-{
- pjsdp_attr_type_e type;
- pj_uint32_t value;
-} pjsdp_attr_num;
-
-
-/**
- * SDP \a rtpmap attribute.
- */
-typedef struct pjsdp_rtpmap_attr
-{
- pjsdp_attr_type_e type;
- unsigned payload_type;
- pj_str_t encoding_name;
- unsigned clock_rate;
- pj_str_t parameter;
-} pjsdp_rtpmap_attr;
-
-
-/**
- * SDP \a fmtp attribute.
- */
-typedef struct pjsdp_fmtp_attr
-{
- pjsdp_attr_type_e type;
- pj_str_t format;
- pj_str_t param;
-} pjsdp_fmtp_attr;
-
-
-/**
- * SDP generic attribute.
- */
-typedef struct pjsdp_generic_attr
-{
- pjsdp_attr_type_e type;
- pj_str_t name;
- pj_str_t value;
-} pjsdp_generic_attr;
-
-
-/** SDP \a cat attribute. */
-typedef struct pjsdp_attr_string pjsdp_cat_attr;
-
-/** SDP \a keywds attribute. */
-typedef struct pjsdp_attr_string pjsdp_keywds_attr;
-
-/** SDP \a tool attribute. */
-typedef struct pjsdp_attr_string pjsdp_tool_attr;
-
-/** SDP \a ptime attribute. */
-typedef struct pjsdp_attr_num pjsdp_ptime_attr;
-
-/** SDP \a recvonly attribute. */
-typedef struct pjsdp_attr pjsdp_recv_only_attr;
-
-/** SDP \a sendonly attribute. */
-typedef struct pjsdp_attr pjsdp_send_only_attr;
-
-/** SDP \a sendrecv attribute. */
-typedef struct pjsdp_attr pjsdp_send_recv_attr;
-
-/** SDP \a orient attribute. */
-typedef struct pjsdp_attr_string pjsdp_orient_attr;
-
-/** SDP \a type attribute. */
-typedef struct pjsdp_attr_string pjsdp_type_attr;
-
-/** SDP \a charset attribute. */
-typedef struct pjsdp_attr_string pjsdp_charset_attr;
-
-/** SDP \a sdplang attribute. */
-typedef struct pjsdp_attr_string pjsdp_sdp_lang_attr;
-
-/** SDP \a lang attribute. */
-typedef struct pjsdp_attr_string pjsdp_lang_attr;
-
-/** SDP \a framerate attribute. */
-typedef struct pjsdp_attr_string pjsdp_frame_rate_attr;
-
-/** SDP \a quality attribute. */
-typedef struct pjsdp_attr_num pjsdp_quality_attr;
-
-/** SDP \a inactive attribute. */
-typedef struct pjsdp_attr pjsdp_inactive_attr;
-
-/** Clone attribute */
-PJ_DECL(pjsdp_attr*) pjsdp_attr_clone (pj_pool_t *pool, const pjsdp_attr *rhs);
-
-/** Find attribute */
-PJ_DECL(const pjsdp_attr*) pjsdp_attr_find (int count, const pjsdp_attr *attr_array[], int type);
-
-/**
- * SDP connection info.
- */
-typedef struct pjsdp_conn_info
-{
- pj_str_t net_type;
- pj_str_t addr_type;
- pj_str_t addr;
-} pjsdp_conn_info;
-
-/**
- *Clone connection info.
- *
- * @param pool Pool to allocate memory for the new connection info.
- * @param rhs The connection into to clone.
- *
- * @return the new connection info.
- */
-PJ_DECL(pjsdp_conn_info*) pjsdp_conn_info_clone (pj_pool_t *pool,
- const pjsdp_conn_info *rhs);
-
-/**
- * SDP media description.
- */
-typedef struct pjsdp_media_desc
-{
- struct
- {
- pj_str_t media;
- pj_uint16_t port;
- unsigned port_count;
- pj_str_t transport;
- unsigned fmt_count;
- pj_str_t fmt[PJSDP_MAX_FMT];
- } desc;
-
- pjsdp_conn_info *conn;
- unsigned attr_count;
- pjsdp_attr *attr[PJSDP_MAX_ATTR];
-
-} pjsdp_media_desc;
-
-/**
- * Clone SDP media description.
- *
- * @param pool Pool to allocate memory for the new media description.
- * @param rhs The media descriptin to clone.
- *
- * @return a new media description.
- */
-PJ_DECL(pjsdp_media_desc*) pjsdp_media_desc_clone (pj_pool_t *pool,
- const pjsdp_media_desc *rhs);
-
-/**
- * Check if the media description has the specified attribute.
- *
- * @param m The SDP media description.
- * @param attr_type The attribute type.
- *
- * @return nonzero if true.
- */
-PJ_DECL(pj_bool_t) pjsdp_media_desc_has_attr (const pjsdp_media_desc *m,
- pjsdp_attr_type_e attr_type);
-
-/**
- * Find rtpmap attribute for the specified payload type.
- *
- * @param m The SDP media description.
- * @param pt RTP payload type.
- *
- * @return the SDP rtpmap attribute for the payload type, or NULL if not found.
- */
-PJ_DECL(const pjsdp_rtpmap_attr*)
-pjsdp_media_desc_find_rtpmap (const pjsdp_media_desc *m, unsigned pt);
-
-
-/**
- * This structure describes SDP session description.
- */
-typedef struct pjsdp_session_desc
-{
- struct
- {
- pj_str_t user;
- pj_uint32_t id;
- pj_uint32_t version;
- pj_str_t net_type;
- pj_str_t addr_type;
- pj_str_t addr;
- } origin;
-
- pj_str_t name;
- pjsdp_conn_info *conn;
-
- struct
- {
- pj_uint32_t start;
- pj_uint32_t stop;
- } time;
-
- unsigned attr_count;
- pjsdp_attr *attr[PJSDP_MAX_ATTR];
-
- unsigned media_count;
- pjsdp_media_desc *media[PJSDP_MAX_MEDIA];
-
-} pjsdp_session_desc;
-
-
-/**
- * Parse SDP message.
- *
- * @param buf The message buffer.
- * @param len The length of the message.
- * @param pool The pool to allocate SDP session description.
- *
- * @return SDP session description.
- */
-PJ_DECL(pjsdp_session_desc*) pjsdp_parse( char *buf, pj_size_t len,
- pj_pool_t *pool);
-
-/**
- * Print SDP description to a buffer.
- *
- * @param buf The buffer.
- * @param size The buffer length.
- * @param desc The SDP session description.
- *
- * @return the length printed, or -1.
- */
-PJ_DECL(int) pjsdp_print( const pjsdp_session_desc *desc,
- char *buf, pj_size_t size);
-
-
-/**
- * @}
- */
-
-PJ_END_DECL
-
-#endif /* __PJSDP_SDP_H__ */
-
+/* $Id$
+ *
+ */
+/*
+ * PJMEDIA - Multimedia over IP Stack
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ * Benny Prijono <bennylp@bulukucing.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef __PJSDP_SDP_H__
+#define __PJSDP_SDP_H__
+
+/**
+ * @defgroup PJSDP SDP Library
+ */
+/**
+ * @file sdp.h
+ * @brief SDP header file.
+ */
+/**
+ * @defgroup PJ_SDP_H SDP stack.
+ * @ingroup PJSDP
+ * @{
+ *
+ * This SDP module consists of SDP parser, SDP structure, and function to
+ * print back the structure as SDP message.
+ */
+
+#include <pj/types.h>
+
+PJ_BEGIN_DECL
+
+#define PJSDP_MAX_FMT 32
+#define PJSDP_MAX_ATTR 32
+#define PJSDP_MAX_MEDIA 16
+
+/**
+ * This enumeration describes the attribute type.
+ */
+typedef enum pjsdp_attr_type_e
+{
+ PJSDP_ATTR_RTPMAP,
+ PJSDP_ATTR_CAT,
+ PJSDP_ATTR_KEYWORDS,
+ PJSDP_ATTR_TOOL,
+ PJSDP_ATTR_PTIME,
+ PJSDP_ATTR_RECV_ONLY,
+ PJSDP_ATTR_SEND_ONLY,
+ PJSDP_ATTR_SEND_RECV,
+ PJSDP_ATTR_ORIENT,
+ PJSDP_ATTR_TYPE,
+ PJSDP_ATTR_CHARSET,
+ PJSDP_ATTR_SDP_LANG,
+ PJSDP_ATTR_LANG,
+ PJSDP_ATTR_FRAME_RATE,
+ PJSDP_ATTR_QUALITY,
+ PJSDP_ATTR_FMTP,
+ PJSDP_ATTR_INACTIVE,
+ PJSDP_ATTR_GENERIC,
+ PJSDP_END_OF_ATTR,
+} pjsdp_attr_type_e;
+
+
+/**
+ * This structure keeps the common attributes that all 'descendants'
+ * will have.
+ */
+typedef struct pjsdp_attr
+{
+ pjsdp_attr_type_e type; /**< Attribute type. */
+} pjsdp_attr;
+
+
+/**
+ * This is the structure to represent generic attribute which has a
+ * string value.
+ */
+typedef struct pjsdp_attr_string
+{
+ pjsdp_attr_type_e type;
+ pj_str_t value;
+} pjsdp_attr_string;
+
+
+/**
+ * This is the structure to represent generic SDP attribute which has
+ * a numeric value.
+ */
+typedef struct pjsdp_attr_num
+{
+ pjsdp_attr_type_e type;
+ pj_uint32_t value;
+} pjsdp_attr_num;
+
+
+/**
+ * SDP \a rtpmap attribute.
+ */
+typedef struct pjsdp_rtpmap_attr
+{
+ pjsdp_attr_type_e type;
+ unsigned payload_type;
+ pj_str_t encoding_name;
+ unsigned clock_rate;
+ pj_str_t parameter;
+} pjsdp_rtpmap_attr;
+
+
+/**
+ * SDP \a fmtp attribute.
+ */
+typedef struct pjsdp_fmtp_attr
+{
+ pjsdp_attr_type_e type;
+ pj_str_t format;
+ pj_str_t param;
+} pjsdp_fmtp_attr;
+
+
+/**
+ * SDP generic attribute.
+ */
+typedef struct pjsdp_generic_attr
+{
+ pjsdp_attr_type_e type;
+ pj_str_t name;
+ pj_str_t value;
+} pjsdp_generic_attr;
+
+
+/** SDP \a cat attribute. */
+typedef struct pjsdp_attr_string pjsdp_cat_attr;
+
+/** SDP \a keywds attribute. */
+typedef struct pjsdp_attr_string pjsdp_keywds_attr;
+
+/** SDP \a tool attribute. */
+typedef struct pjsdp_attr_string pjsdp_tool_attr;
+
+/** SDP \a ptime attribute. */
+typedef struct pjsdp_attr_num pjsdp_ptime_attr;
+
+/** SDP \a recvonly attribute. */
+typedef struct pjsdp_attr pjsdp_recv_only_attr;
+
+/** SDP \a sendonly attribute. */
+typedef struct pjsdp_attr pjsdp_send_only_attr;
+
+/** SDP \a sendrecv attribute. */
+typedef struct pjsdp_attr pjsdp_send_recv_attr;
+
+/** SDP \a orient attribute. */
+typedef struct pjsdp_attr_string pjsdp_orient_attr;
+
+/** SDP \a type attribute. */
+typedef struct pjsdp_attr_string pjsdp_type_attr;
+
+/** SDP \a charset attribute. */
+typedef struct pjsdp_attr_string pjsdp_charset_attr;
+
+/** SDP \a sdplang attribute. */
+typedef struct pjsdp_attr_string pjsdp_sdp_lang_attr;
+
+/** SDP \a lang attribute. */
+typedef struct pjsdp_attr_string pjsdp_lang_attr;
+
+/** SDP \a framerate attribute. */
+typedef struct pjsdp_attr_string pjsdp_frame_rate_attr;
+
+/** SDP \a quality attribute. */
+typedef struct pjsdp_attr_num pjsdp_quality_attr;
+
+/** SDP \a inactive attribute. */
+typedef struct pjsdp_attr pjsdp_inactive_attr;
+
+/** Clone attribute */
+PJ_DECL(pjsdp_attr*) pjsdp_attr_clone (pj_pool_t *pool, const pjsdp_attr *rhs);
+
+/** Find attribute */
+PJ_DECL(const pjsdp_attr*) pjsdp_attr_find (int count, const pjsdp_attr *attr_array[], int type);
+
+/**
+ * SDP connection info.
+ */
+typedef struct pjsdp_conn_info
+{
+ pj_str_t net_type;
+ pj_str_t addr_type;
+ pj_str_t addr;
+} pjsdp_conn_info;
+
+/**
+ *Clone connection info.
+ *
+ * @param pool Pool to allocate memory for the new connection info.
+ * @param rhs The connection into to clone.
+ *
+ * @return the new connection info.
+ */
+PJ_DECL(pjsdp_conn_info*) pjsdp_conn_info_clone (pj_pool_t *pool,
+ const pjsdp_conn_info *rhs);
+
+/**
+ * SDP media description.
+ */
+typedef struct pjsdp_media_desc
+{
+ struct
+ {
+ pj_str_t media;
+ pj_uint16_t port;
+ unsigned port_count;
+ pj_str_t transport;
+ unsigned fmt_count;
+ pj_str_t fmt[PJSDP_MAX_FMT];
+ } desc;
+
+ pjsdp_conn_info *conn;
+ unsigned attr_count;
+ pjsdp_attr *attr[PJSDP_MAX_ATTR];
+
+} pjsdp_media_desc;
+
+/**
+ * Clone SDP media description.
+ *
+ * @param pool Pool to allocate memory for the new media description.
+ * @param rhs The media descriptin to clone.
+ *
+ * @return a new media description.
+ */
+PJ_DECL(pjsdp_media_desc*) pjsdp_media_desc_clone (pj_pool_t *pool,
+ const pjsdp_media_desc *rhs);
+
+/**
+ * Check if the media description has the specified attribute.
+ *
+ * @param m The SDP media description.
+ * @param attr_type The attribute type.
+ *
+ * @return nonzero if true.
+ */
+PJ_DECL(pj_bool_t) pjsdp_media_desc_has_attr (const pjsdp_media_desc *m,
+ pjsdp_attr_type_e attr_type);
+
+/**
+ * Find rtpmap attribute for the specified payload type.
+ *
+ * @param m The SDP media description.
+ * @param pt RTP payload type.
+ *
+ * @return the SDP rtpmap attribute for the payload type, or NULL if not found.
+ */
+PJ_DECL(const pjsdp_rtpmap_attr*)
+pjsdp_media_desc_find_rtpmap (const pjsdp_media_desc *m, unsigned pt);
+
+
+/**
+ * This structure describes SDP session description.
+ */
+typedef struct pjsdp_session_desc
+{
+ struct
+ {
+ pj_str_t user;
+ pj_uint32_t id;
+ pj_uint32_t version;
+ pj_str_t net_type;
+ pj_str_t addr_type;
+ pj_str_t addr;
+ } origin;
+
+ pj_str_t name;
+ pjsdp_conn_info *conn;
+
+ struct
+ {
+ pj_uint32_t start;
+ pj_uint32_t stop;
+ } time;
+
+ unsigned attr_count;
+ pjsdp_attr *attr[PJSDP_MAX_ATTR];
+
+ unsigned media_count;
+ pjsdp_media_desc *media[PJSDP_MAX_MEDIA];
+
+} pjsdp_session_desc;
+
+
+/**
+ * Parse SDP message.
+ *
+ * @param buf The message buffer.
+ * @param len The length of the message.
+ * @param pool The pool to allocate SDP session description.
+ *
+ * @return SDP session description.
+ */
+PJ_DECL(pjsdp_session_desc*) pjsdp_parse( char *buf, pj_size_t len,
+ pj_pool_t *pool);
+
+/**
+ * Print SDP description to a buffer.
+ *
+ * @param buf The buffer.
+ * @param size The buffer length.
+ * @param desc The SDP session description.
+ *
+ * @return the length printed, or -1.
+ */
+PJ_DECL(int) pjsdp_print( const pjsdp_session_desc *desc,
+ char *buf, pj_size_t size);
+
+
+/**
+ * @}
+ */
+
+PJ_END_DECL
+
+#endif /* __PJSDP_SDP_H__ */
+
diff --git a/pjmedia/src/pjmedia/session.c b/pjmedia/src/pjmedia/session.c
index d8e5b67..56a5e9f 100644
--- a/pjmedia/src/pjmedia/session.c
+++ b/pjmedia/src/pjmedia/session.c
@@ -1,818 +1,839 @@
-/* $Id$
- *
- */
-#include <pjmedia/session.h>
-#include <pj/log.h>
-#include <pj/os.h>
-#include <pj/pool.h>
-#include <pj/string.h>
-
-typedef struct pj_media_stream_desc
-{
- pj_media_stream_info info;
- pj_media_stream_t *enc_stream, *dec_stream;
-} pj_media_stream_desc;
-
-struct pj_media_session_t
-{
- pj_pool_t *pool;
- pj_med_mgr_t *mediamgr;
- unsigned stream_cnt;
- pj_media_stream_desc *stream_desc[PJSDP_MAX_MEDIA];
-};
-
-#define THIS_FILE "session.c"
-
-#define PJ_MEDIA_SESSION_SIZE (48*1024)
-#define PJ_MEDIA_SESSION_INC 1024
-
-static const pj_str_t ID_AUDIO = { "audio", 5};
-static const pj_str_t ID_VIDEO = { "video", 5};
-static const pj_str_t ID_IN = { "IN", 2 };
-static const pj_str_t ID_IP4 = { "IP4", 3};
-static const pj_str_t ID_RTP_AVP = { "RTP/AVP", 7 };
-static const pj_str_t ID_SDP_NAME = { "pjmedia", 7 };
-
-static void session_init (pj_media_session_t *ses)
-{
- pj_memset (ses, 0, sizeof(pj_media_session_t));
-}
-
-
-/**
- * Create new session offering.
- */
-PJ_DEF(pj_media_session_t*)
-pj_media_session_create (pj_med_mgr_t *mgr, const pj_media_sock_info *sock_info)
-{
- pj_pool_factory *pf;
- pj_pool_t *pool;
- pj_media_session_t *session;
- pj_media_stream_desc *sd;
- unsigned i, codec_cnt;
- pj_codec_mgr *cm;
- const pj_codec_id *codecs[PJSDP_MAX_FMT];
-
- pf = pj_med_mgr_get_pool_factory(mgr);
-
- pool = pj_pool_create( pf, "session", PJ_MEDIA_SESSION_SIZE, PJ_MEDIA_SESSION_INC, NULL);
- if (!pool)
- return NULL;
-
- session = pj_pool_alloc(pool, sizeof(pj_media_session_t));
- if (!session)
- return NULL;
-
- session_init (session);
-
- session->pool = pool;
- session->mediamgr = mgr;
-
- /* Create first stream */
- sd = pj_pool_calloc (pool, 1, sizeof(pj_media_stream_desc));
- if (!sd)
- return NULL;
-
- sd->info.type = ID_AUDIO;
- sd->info.dir = PJ_MEDIA_DIR_ENCODING_DECODING;
- sd->info.transport = ID_RTP_AVP;
- pj_memcpy(&sd->info.sock_info, sock_info, sizeof(*sock_info));
-
- /* Enum audio codecs. */
- sd->info.fmt_cnt = 0;
- cm = pj_med_mgr_get_codec_mgr (mgr);
- codec_cnt = pj_codec_mgr_enum_codecs(cm, PJSDP_MAX_FMT, codecs);
- if (codec_cnt > PJSDP_MAX_FMT) codec_cnt = PJSDP_MAX_FMT;
- for (i=0; i<codec_cnt; ++i) {
- if (codecs[i]->type != PJ_MEDIA_TYPE_AUDIO)
- continue;
-
- sd->info.fmt[sd->info.fmt_cnt].pt = codecs[i]->pt;
- sd->info.fmt[sd->info.fmt_cnt].sample_rate = codecs[i]->sample_rate;
- pj_strdup (pool, &sd->info.fmt[sd->info.fmt_cnt].encoding_name, &codecs[i]->encoding_name);
- ++sd->info.fmt_cnt;
- }
-
- session->stream_desc[session->stream_cnt++] = sd;
-
- return session;
-}
-
-static int sdp_check (const pjsdp_session_desc *sdp)
-{
- int has_conn = 0;
- unsigned i;
-
- if (sdp->conn)
- has_conn = 1;
-
- if (sdp->media_count == 0) {
- PJ_LOG(4,(THIS_FILE, "SDP check failed: no media stream definition"));
- return -1;
- }
-
- for (i=0; i<sdp->media_count; ++i) {
- pjsdp_media_desc *m = sdp->media[i];
-
- if (!m) {
- pj_assert(0);
- return -1;
- }
-
- if (m->desc.fmt_count == 0) {
- PJ_LOG(4,(THIS_FILE, "SDP check failed: no format listed in media stream"));
- return -1;
- }
-
- if (!has_conn && m->conn == NULL) {
- PJ_LOG(4,(THIS_FILE, "SDP check failed: no connection information for media"));
- return -1;
- }
- }
-
- return 0;
-}
-
-/*
- * Create local stream definition that matches SDP received from peer.
- */
-static pj_media_stream_desc*
-create_stream_from_sdp (pj_pool_t *pool, pj_med_mgr_t *mgr, const pjsdp_conn_info *conn,
- const pjsdp_media_desc *m, const pj_media_sock_info *sock_info)
-{
- pj_media_stream_desc *sd;
-
- sd = pj_pool_calloc (pool, 1, sizeof(pj_media_stream_desc));
- if (!sd) {
- PJ_LOG(2,(THIS_FILE, "No memory to allocate stream descriptor"));
- return NULL;
- }
-
- if (pj_stricmp(&conn->net_type, &ID_IN)==0 &&
- pj_stricmp(&conn->addr_type, &ID_IP4)==0 &&
- pj_stricmp(&m->desc.media, &ID_AUDIO)==0 &&
- pj_stricmp(&m->desc.transport, &ID_RTP_AVP) == 0)
- {
- /*
- * Got audio stream.
- */
- unsigned i, codec_cnt;
- pj_codec_mgr *cm;
- const pj_codec_id *codecs[PJSDP_MAX_FMT];
-
- sd->info.type = ID_AUDIO;
- sd->info.transport = ID_RTP_AVP;
- pj_memcpy(&sd->info.sock_info, sock_info, sizeof(*sock_info));
- sd->info.rem_port = m->desc.port;
- pj_strdup (pool, &sd->info.rem_addr, &conn->addr);
-
- /* Enum audio codecs. */
- sd->info.fmt_cnt = 0;
- cm = pj_med_mgr_get_codec_mgr (mgr);
- codec_cnt = pj_codec_mgr_enum_codecs (cm, PJSDP_MAX_FMT, codecs);
- if (codec_cnt > PJSDP_MAX_FMT) codec_cnt = PJSDP_MAX_FMT;
-
- /* Find just one codec which we can support. */
- for (i=0; i<m->desc.fmt_count && sd->info.fmt_cnt == 0; ++i) {
- unsigned j, fmt_i;
-
- /* For static payload, just match payload type. */
- /* Else match clock rate and encoding name. */
- fmt_i = pj_strtoul(&m->desc.fmt[i]);
- if (fmt_i < PJ_RTP_PT_DYNAMIC) {
- for (j=0; j<codec_cnt; ++j) {
- if (codecs[j]->pt == fmt_i) {
- sd->info.fmt_cnt = 1;
- sd->info.fmt[0].type = PJ_MEDIA_TYPE_AUDIO;
- sd->info.fmt[0].pt = codecs[j]->pt;
- sd->info.fmt[0].sample_rate = codecs[j]->sample_rate;
- pj_strdup (pool, &sd->info.fmt[0].encoding_name, &codecs[j]->encoding_name);
- break;
- }
- }
- } else {
-
- /* Find the rtpmap for the payload type. */
- const pjsdp_rtpmap_attr *rtpmap = pjsdp_media_desc_find_rtpmap (m, fmt_i);
-
- /* Don't accept the media if no rtpmap for dynamic PT. */
- if (rtpmap == NULL) {
- PJ_LOG(4,(THIS_FILE, "SDP: No rtpmap found for payload id %d", m->desc.fmt[i]));
- continue;
- }
-
- /* Check whether we can take this codec. */
- for (j=0; j<codec_cnt; ++j) {
- if (rtpmap->clock_rate == codecs[j]->sample_rate &&
- pj_stricmp(&rtpmap->encoding_name, &codecs[j]->encoding_name) == 0)
- {
- sd->info.fmt_cnt = 1;
- sd->info.fmt[0].type = PJ_MEDIA_TYPE_AUDIO;
- sd->info.fmt[0].pt = codecs[j]->pt;
- sd->info.fmt[0].sample_rate = codecs[j]->sample_rate;
- sd->info.fmt[0].encoding_name = codecs[j]->encoding_name;
- break;
- }
- }
- }
- }
-
- /* Match codec and direction. */
- if (sd->info.fmt_cnt == 0 || m->desc.port == 0 ||
- pjsdp_media_desc_has_attr(m, PJSDP_ATTR_INACTIVE))
- {
- sd->info.dir = PJ_MEDIA_DIR_NONE;
- }
- else if (pjsdp_media_desc_has_attr(m, PJSDP_ATTR_RECV_ONLY)) {
- sd->info.dir = PJ_MEDIA_DIR_ENCODING;
- }
- else if (pjsdp_media_desc_has_attr(m, PJSDP_ATTR_SEND_ONLY)) {
- sd->info.dir = PJ_MEDIA_DIR_DECODING;
- }
- else {
- sd->info.dir = PJ_MEDIA_DIR_ENCODING_DECODING;
- }
-
- } else {
- /* Unsupported media stream. */
- unsigned fmt_num;
- const pjsdp_rtpmap_attr *rtpmap = NULL;
-
- pj_strdup(pool, &sd->info.type, &m->desc.media);
- pj_strdup(pool, &sd->info.transport, &m->desc.transport);
- pj_memset(&sd->info.sock_info, 0, sizeof(*sock_info));
- pj_strdup (pool, &sd->info.rem_addr, &conn->addr);
- sd->info.rem_port = m->desc.port;
-
- /* Just put one format and rtpmap, so that we don't have to make
- * special exception when we convert this stream to SDP.
- */
-
- /* Find the rtpmap for the payload type. */
- fmt_num = pj_strtoul(&m->desc.fmt[0]);
- rtpmap = pjsdp_media_desc_find_rtpmap (m, fmt_num);
-
- sd->info.fmt_cnt = 1;
- if (pj_stricmp(&m->desc.media, &ID_VIDEO)==0) {
- sd->info.fmt[0].type = PJ_MEDIA_TYPE_VIDEO;
- sd->info.fmt[0].pt = fmt_num;
- if (rtpmap) {
- pj_strdup (pool, &sd->info.fmt[0].encoding_name,
- &rtpmap->encoding_name);
- sd->info.fmt[0].sample_rate = rtpmap->clock_rate;
- }
- } else {
- sd->info.fmt[0].type = PJ_MEDIA_TYPE_UNKNOWN;
- pj_strdup(pool, &sd->info.fmt[0].encoding_name, &m->desc.fmt[0]);
- }
-
- sd->info.dir = PJ_MEDIA_DIR_NONE;
- }
-
- return sd;
-}
-
-/**
- * Create new session based on peer's offering.
- */
-PJ_DEF(pj_media_session_t*)
-pj_media_session_create_from_sdp (pj_med_mgr_t *mgr, const pjsdp_session_desc *sdp,
- const pj_media_sock_info *sock_info)
-{
- pj_pool_factory *pf;
- pj_pool_t *pool;
- pj_media_session_t *session;
- unsigned i;
-
- if (sdp_check(sdp) != 0)
- return NULL;
-
- pf = pj_med_mgr_get_pool_factory(mgr);
- pool = pj_pool_create( pf, "session", PJ_MEDIA_SESSION_SIZE, PJ_MEDIA_SESSION_INC, NULL);
- if (!pool)
- return NULL;
-
- session = pj_pool_alloc(pool, sizeof(pj_media_session_t));
- if (!session) {
- PJ_LOG(3,(THIS_FILE, "No memory to create media session descriptor"));
- pj_pool_release (pool);
- return NULL;
- }
-
- session_init (session);
-
- session->pool = pool;
- session->mediamgr = mgr;
-
- /* Enumerate each media stream and create our peer. */
- for (i=0; i<sdp->media_count; ++i) {
- const pjsdp_conn_info *conn;
- const pjsdp_media_desc *m;
- pj_media_stream_desc *sd;
-
- m = sdp->media[i];
- conn = m->conn ? m->conn : sdp->conn;
-
- /*
- * Bug:
- * the sock_info below is used by more than one 'm' lines
- */
- PJ_TODO(SUPPORT_MORE_THAN_ONE_SDP_M_LINES)
-
- sd = create_stream_from_sdp (pool, mgr, conn, m, sock_info);
- pj_assert (sd);
-
- session->stream_desc[session->stream_cnt++] = sd;
- }
-
- return session;
-}
-
-/**
- * Duplicate session. The new session is inactive.
- */
-PJ_DEF(pj_media_session_t*)
-pj_media_session_clone (const pj_media_session_t *rhs)
-{
- pj_pool_factory *pf;
- pj_pool_t *pool;
- pj_media_session_t *session;
- unsigned i;
-
- pf = pj_med_mgr_get_pool_factory(rhs->mediamgr);
- pool = pj_pool_create( pf, "session", PJ_MEDIA_SESSION_SIZE, PJ_MEDIA_SESSION_INC, NULL);
- if (!pool) {
- return NULL;
- }
-
- session = pj_pool_alloc (pool, sizeof(*session));
- if (!session) {
- PJ_LOG(3,(THIS_FILE, "No memory to create media session descriptor"));
- pj_pool_release (pool);
- return NULL;
- }
-
- session->pool = pool;
- session->mediamgr = rhs->mediamgr;
- session->stream_cnt = rhs->stream_cnt;
-
- for (i=0; i<rhs->stream_cnt; ++i) {
- pj_media_stream_desc *sd1 = pj_pool_alloc (session->pool, sizeof(pj_media_stream_desc));
- const pj_media_stream_desc *sd2 = rhs->stream_desc[i];
-
- if (!sd1) {
- PJ_LOG(3,(THIS_FILE, "No memory to create media stream descriptor"));
- pj_pool_release (pool);
- return NULL;
- }
-
- session->stream_desc[i] = sd1;
- sd1->enc_stream = sd1->dec_stream = NULL;
- pj_strdup (pool, &sd1->info.type, &sd2->info.type);
- sd1->info.dir = sd2->info.dir;
- pj_strdup (pool, &sd1->info.transport, &sd2->info.transport);
- pj_memcpy(&sd1->info.sock_info, &sd2->info.sock_info, sizeof(pj_media_sock_info));
- pj_strdup (pool, &sd1->info.rem_addr, &sd2->info.rem_addr);
- sd1->info.rem_port = sd2->info.rem_port;
- sd1->info.fmt_cnt = sd2->info.fmt_cnt;
- pj_memcpy (sd1->info.fmt, sd2->info.fmt, sizeof(sd2->info.fmt));
- }
-
- return session;
-}
-
-/**
- * Create SDP description from the session.
- */
-PJ_DEF(pjsdp_session_desc*)
-pj_media_session_create_sdp (const pj_media_session_t *session, pj_pool_t *pool,
- pj_bool_t only_first_fmt)
-{
- pjsdp_session_desc *sdp;
- pj_time_val tv;
- unsigned i;
- pj_media_sock_info *c_addr = NULL;
-
- if (session->stream_cnt == 0) {
- pj_assert(0);
- return NULL;
- }
-
- sdp = pj_pool_calloc (pool, 1, sizeof(pjsdp_session_desc));
- if (!sdp) {
- PJ_LOG(3,(THIS_FILE, "No memory to allocate SDP session descriptor"));
- return NULL;
- }
-
- pj_gettimeofday(&tv);
-
- sdp->origin.user = pj_str("-");
- sdp->origin.version = sdp->origin.id = tv.sec + 2208988800UL;
- sdp->origin.net_type = ID_IN;
- sdp->origin.addr_type = ID_IP4;
- sdp->origin.addr = *pj_gethostname();
-
- sdp->name = ID_SDP_NAME;
-
- /* If all media addresses are the same, then put the connection
- * info in the session level, otherwise put it in media stream
- * level.
- */
- for (i=0; i<session->stream_cnt; ++i) {
- if (c_addr == NULL) {
- c_addr = &session->stream_desc[i]->info.sock_info;
- } else if (c_addr->rtp_addr_name.sin_addr.s_addr != session->stream_desc[i]->info.sock_info.rtp_addr_name.sin_addr.s_addr)
- {
- c_addr = NULL;
- break;
- }
- }
-
- if (c_addr) {
- /* All addresses are the same, put connection info in session level. */
- sdp->conn = pj_pool_alloc (pool, sizeof(pjsdp_conn_info));
- if (!sdp->conn) {
- PJ_LOG(2,(THIS_FILE, "No memory to allocate SDP connection info"));
- return NULL;
- }
-
- sdp->conn->net_type = ID_IN;
- sdp->conn->addr_type = ID_IP4;
- pj_strdup2 (pool, &sdp->conn->addr, pj_inet_ntoa(c_addr->rtp_addr_name.sin_addr));
- }
-
- sdp->time.start = sdp->time.stop = 0;
- sdp->attr_count = 0;
-
- /* Create each media. */
- sdp->media_count = 0;
- for (i=0; i<session->stream_cnt; ++i) {
- const pj_media_stream_desc *sd = session->stream_desc[i];
- pjsdp_media_desc *m;
- unsigned j;
- unsigned fmt_cnt;
- pjsdp_attr *attr;
-
- m = pj_pool_calloc (pool, 1, sizeof(pjsdp_media_desc));
- if (!m) {
- PJ_LOG(3,(THIS_FILE, "No memory to allocate SDP media stream descriptor"));
- return NULL;
- }
-
- sdp->media[sdp->media_count++] = m;
-
- pj_strdup (pool, &m->desc.media, &sd->info.type);
- m->desc.port = pj_ntohs(sd->info.sock_info.rtp_addr_name.sin_port);
- m->desc.port_count = 1;
- pj_strdup (pool, &m->desc.transport, &sd->info.transport);
-
- /* Add format and rtpmap for each codec. */
- m->desc.fmt_count = 0;
- m->attr_count = 0;
- fmt_cnt = sd->info.fmt_cnt;
- if (fmt_cnt > 0 && only_first_fmt)
- fmt_cnt = 1;
- for (j=0; j<fmt_cnt; ++j) {
- pjsdp_rtpmap_attr *rtpmap;
- pj_str_t *fmt = &m->desc.fmt[m->desc.fmt_count++];
-
- if (sd->info.fmt[j].type==PJ_MEDIA_TYPE_UNKNOWN) {
- pj_strdup(pool, fmt, &sd->info.fmt[j].encoding_name);
- } else {
- fmt->ptr = pj_pool_alloc(pool, 8);
- fmt->slen = pj_utoa(sd->info.fmt[j].pt, fmt->ptr);
-
- rtpmap = pj_pool_calloc(pool, 1, sizeof(pjsdp_rtpmap_attr));
- if (rtpmap) {
- m->attr[m->attr_count++] = (pjsdp_attr*)rtpmap;
- rtpmap->type = PJSDP_ATTR_RTPMAP;
- rtpmap->payload_type = sd->info.fmt[j].pt;
- rtpmap->clock_rate = sd->info.fmt[j].sample_rate;
- pj_strdup (pool, &rtpmap->encoding_name, &sd->info.fmt[j].encoding_name);
- } else {
- PJ_LOG(3,(THIS_FILE, "No memory to allocate SDP rtpmap descriptor"));
- }
- }
- }
-
- /* If we don't have connection info in session level, create one. */
- if (sdp->conn == NULL) {
- m->conn = pj_pool_alloc (pool, sizeof(pjsdp_conn_info));
- if (m->conn) {
- m->conn->net_type = ID_IN;
- m->conn->addr_type = ID_IP4;
- pj_strdup2 (pool, &m->conn->addr, pj_inet_ntoa(sd->info.sock_info.rtp_addr_name.sin_addr));
- } else {
- PJ_LOG(3,(THIS_FILE, "No memory to allocate SDP media connection info"));
- return NULL;
- }
- }
-
- /* Add additional attribute to the media stream. */
- attr = pj_pool_alloc(pool, sizeof(pjsdp_attr));
- if (!attr) {
- PJ_LOG(3,(THIS_FILE, "No memory to allocate SDP attribute"));
- return NULL;
- }
- m->attr[m->attr_count++] = attr;
-
- switch (sd->info.dir) {
- case PJ_MEDIA_DIR_NONE:
- attr->type = PJSDP_ATTR_INACTIVE;
- break;
- case PJ_MEDIA_DIR_ENCODING:
- attr->type = PJSDP_ATTR_SEND_ONLY;
- break;
- case PJ_MEDIA_DIR_DECODING:
- attr->type = PJSDP_ATTR_RECV_ONLY;
- break;
- case PJ_MEDIA_DIR_ENCODING_DECODING:
- attr->type = PJSDP_ATTR_SEND_RECV;
- break;
- }
- }
-
- return sdp;
-}
-
-/**
- * Update session with SDP answer from peer.
- */
-PJ_DEF(pj_status_t)
-pj_media_session_update (pj_media_session_t *session,
- const pjsdp_session_desc *sdp)
-{
- unsigned i;
- unsigned count;
-
- /* Check SDP */
- if (sdp_check (sdp) != 0) {
- return -1;
- }
-
- /* If the media stream count doesn't match, only update one. */
- if (session->stream_cnt != sdp->media_count) {
- PJ_LOG(3,(THIS_FILE, "pj_media_session_update : "
- "SDP media count mismatch! (rmt=%d, lcl=%d)",
- sdp->media_count, session->stream_cnt));
- count = (session->stream_cnt < sdp->media_count) ?
- session->stream_cnt : sdp->media_count;
- } else {
- count = session->stream_cnt;
- }
-
- for (i=0; i<count; ++i) {
- pj_media_stream_desc *sd = session->stream_desc[i];
- const pjsdp_media_desc *m = sdp->media[i];
- const pjsdp_conn_info *conn;
- unsigned j;
-
- /* Check that the session is not active. */
- pj_assert (sd->enc_stream == NULL && sd->dec_stream == NULL);
-
- conn = m->conn ? m->conn : sdp->conn;
- pj_assert(conn);
-
- /* Update remote address. */
- sd->info.rem_port = m->desc.port;
- pj_strdup (session->pool, &sd->info.rem_addr, &conn->addr);
-
- /* Select one active codec according to what peer wants. */
- for (j=0; j<sd->info.fmt_cnt; ++j) {
- unsigned fmt_0 = pj_strtoul(&m->desc.fmt[0]);
- if (sd->info.fmt[j].pt == fmt_0) {
- pj_codec_id temp;
-
- /* Put active format to the front. */
- if (j == 0)
- break;
-
- pj_memcpy(&temp, &sd->info.fmt[0], sizeof(temp));
- pj_memcpy(&sd->info.fmt[0], &sd->info.fmt[j], sizeof(temp));
- pj_memcpy(&sd->info.fmt[j], &temp, sizeof(temp));
- break;
- }
- }
-
- if (j == sd->info.fmt_cnt) {
- /* Peer has answered SDP with new codec, which doesn't exist
- * in the offer!
- * Mute this media.
- */
- PJ_LOG(3,(THIS_FILE, "Peer has answered SDP with new codec!"));
- sd->info.dir = PJ_MEDIA_DIR_NONE;
- continue;
- }
-
- /* Check direction. */
- if (m->desc.port == 0 || pjsdp_media_desc_has_attr(m, PJSDP_ATTR_INACTIVE)) {
- sd->info.dir = PJ_MEDIA_DIR_NONE;
- }
- else if (pjsdp_media_desc_has_attr(m, PJSDP_ATTR_RECV_ONLY)) {
- sd->info.dir = PJ_MEDIA_DIR_ENCODING;
- }
- else if (pjsdp_media_desc_has_attr(m, PJSDP_ATTR_SEND_ONLY)) {
- sd->info.dir = PJ_MEDIA_DIR_DECODING;
- }
- else {
- sd->info.dir = PJ_MEDIA_DIR_ENCODING_DECODING;
- }
- }
-
- return 0;
-}
-
-/**
- * Enumerate media streams in the session.
- */
-PJ_DEF(unsigned)
-pj_media_session_enum_streams (const pj_media_session_t *session,
- unsigned count, const pj_media_stream_info *info[])
-{
- unsigned i;
-
- if (count > session->stream_cnt)
- count = session->stream_cnt;
-
- for (i=0; i<count; ++i) {
- info[i] = &session->stream_desc[i]->info;
- }
-
- return session->stream_cnt;
-}
-
-/**
- * Get statistics
- */
-PJ_DEF(pj_status_t)
-pj_media_session_get_stat (const pj_media_session_t *session, unsigned index,
- pj_media_stream_stat *tx_stat,
- pj_media_stream_stat *rx_stat)
-{
- pj_media_stream_desc *sd;
- int stat_cnt = 0;
-
- if (index >= session->stream_cnt) {
- pj_assert(0);
- return -1;
- }
-
- sd = session->stream_desc[index];
-
- if (sd->enc_stream && tx_stat) {
- pj_media_stream_get_stat (sd->enc_stream, tx_stat);
- ++stat_cnt;
- } else if (tx_stat) {
- pj_memset (tx_stat, 0, sizeof(*tx_stat));
- }
-
- if (sd->dec_stream && rx_stat) {
- pj_media_stream_get_stat (sd->dec_stream, rx_stat);
- ++stat_cnt;
- } else if (rx_stat) {
- pj_memset (rx_stat, 0, sizeof(*rx_stat));
- }
-
- return stat_cnt ? 0 : -1;
-}
-
-/**
- * Modify stream, only when stream is inactive.
- */
-PJ_DEF(pj_status_t)
-pj_media_session_modify_stream (pj_media_session_t *session, unsigned index,
- unsigned modify_flag, const pj_media_stream_info *info)
-{
- pj_media_stream_desc *sd;
-
- if (index >= session->stream_cnt) {
- pj_assert(0);
- return -1;
- }
-
- sd = session->stream_desc[index];
-
- if (sd->enc_stream || sd->dec_stream) {
- pj_assert(0);
- return -1;
- }
-
- if (modify_flag & PJ_MEDIA_STREAM_MODIFY_DIR) {
- sd->info.dir = info->dir;
- }
-
- return 0;
-}
-
-/**
- * Activate media session.
- */
-PJ_DEF(pj_status_t)
-pj_media_session_activate (pj_media_session_t *session)
-{
- unsigned i;
- pj_status_t status = 0;
-
- for (i=0; i<session->stream_cnt; ++i) {
- pj_status_t rc;
- rc = pj_media_session_activate_stream (session, i);
- if (status == 0)
- status = rc;
- }
- return status;
-}
-
-/**
- * Activate individual stream in media session.
- */
-PJ_DEF(pj_status_t)
-pj_media_session_activate_stream (pj_media_session_t *session, unsigned index)
-{
- pj_media_stream_desc *sd;
- pj_media_stream_create_param scp;
- pj_status_t status;
- pj_time_val tv;
-
- if (index < 0 || index >= session->stream_cnt) {
- pj_assert(0);
- return -1;
- }
-
- sd = session->stream_desc[index];
-
- if (sd->enc_stream || sd->dec_stream) {
- /* Stream already active. */
- pj_assert(0);
- return 0;
- }
-
- pj_gettimeofday(&tv);
-
- /* Initialize parameter to create stream. */
- pj_memset (&scp, 0, sizeof(scp));
- scp.codec_id = &sd->info.fmt[0];
- scp.mediamgr = session->mediamgr;
- scp.dir = sd->info.dir;
- scp.rtp_sock = sd->info.sock_info.rtp_sock;
- scp.rtcp_sock = sd->info.sock_info.rtcp_sock;
- scp.remote_addr = pj_pool_calloc (session->pool, 1, sizeof(pj_sockaddr_in));
- pj_sockaddr_init (scp.remote_addr, &sd->info.rem_addr, sd->info.rem_port);
- scp.ssrc = tv.sec;
- scp.jb_min = 1;
- scp.jb_max = 15;
- scp.jb_maxcnt = 16;
-
- /* Build the stream! */
- status = pj_media_stream_create (session->pool, &sd->enc_stream, &sd->dec_stream, &scp);
-
- if (status==0 && sd->enc_stream) {
- status = pj_media_stream_start (sd->enc_stream);
- if (status != 0)
- goto on_error;
- }
- if (status==0 && sd->dec_stream) {
- status = pj_media_stream_start (sd->dec_stream);
- if (status != 0)
- goto on_error;
- }
- return status;
-
-on_error:
- if (sd->enc_stream) {
- pj_media_stream_destroy (sd->enc_stream);
- sd->enc_stream = NULL;
- }
- if (sd->dec_stream) {
- pj_media_stream_destroy (sd->dec_stream);
- sd->dec_stream = NULL;
- }
- return status;
-}
-
-/**
- * Destroy media session.
- */
-PJ_DEF(pj_status_t)
-pj_media_session_destroy (pj_media_session_t *session)
-{
- unsigned i;
-
- if (!session)
- return -1;
-
- for (i=0; i<session->stream_cnt; ++i) {
- pj_media_stream_desc *sd = session->stream_desc[i];
-
- if (sd->enc_stream) {
- pj_media_stream_destroy (sd->enc_stream);
- sd->enc_stream = NULL;
- }
- if (sd->dec_stream) {
- pj_media_stream_destroy (sd->dec_stream);
- sd->dec_stream = NULL;
- }
- }
- pj_pool_release (session->pool);
- return 0;
-}
-
+/* $Id$
+ *
+ */
+/*
+ * PJMEDIA - Multimedia over IP Stack
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ * Benny Prijono <bennylp@bulukucing.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#include <pjmedia/session.h>
+#include <pj/log.h>
+#include <pj/os.h>
+#include <pj/pool.h>
+#include <pj/string.h>
+
+typedef struct pj_media_stream_desc
+{
+ pj_media_stream_info info;
+ pj_media_stream_t *enc_stream, *dec_stream;
+} pj_media_stream_desc;
+
+struct pj_media_session_t
+{
+ pj_pool_t *pool;
+ pj_med_mgr_t *mediamgr;
+ unsigned stream_cnt;
+ pj_media_stream_desc *stream_desc[PJSDP_MAX_MEDIA];
+};
+
+#define THIS_FILE "session.c"
+
+#define PJ_MEDIA_SESSION_SIZE (48*1024)
+#define PJ_MEDIA_SESSION_INC 1024
+
+static const pj_str_t ID_AUDIO = { "audio", 5};
+static const pj_str_t ID_VIDEO = { "video", 5};
+static const pj_str_t ID_IN = { "IN", 2 };
+static const pj_str_t ID_IP4 = { "IP4", 3};
+static const pj_str_t ID_RTP_AVP = { "RTP/AVP", 7 };
+static const pj_str_t ID_SDP_NAME = { "pjmedia", 7 };
+
+static void session_init (pj_media_session_t *ses)
+{
+ pj_memset (ses, 0, sizeof(pj_media_session_t));
+}
+
+
+/**
+ * Create new session offering.
+ */
+PJ_DEF(pj_media_session_t*)
+pj_media_session_create (pj_med_mgr_t *mgr, const pj_media_sock_info *sock_info)
+{
+ pj_pool_factory *pf;
+ pj_pool_t *pool;
+ pj_media_session_t *session;
+ pj_media_stream_desc *sd;
+ unsigned i, codec_cnt;
+ pj_codec_mgr *cm;
+ const pj_codec_id *codecs[PJSDP_MAX_FMT];
+
+ pf = pj_med_mgr_get_pool_factory(mgr);
+
+ pool = pj_pool_create( pf, "session", PJ_MEDIA_SESSION_SIZE, PJ_MEDIA_SESSION_INC, NULL);
+ if (!pool)
+ return NULL;
+
+ session = pj_pool_alloc(pool, sizeof(pj_media_session_t));
+ if (!session)
+ return NULL;
+
+ session_init (session);
+
+ session->pool = pool;
+ session->mediamgr = mgr;
+
+ /* Create first stream */
+ sd = pj_pool_calloc (pool, 1, sizeof(pj_media_stream_desc));
+ if (!sd)
+ return NULL;
+
+ sd->info.type = ID_AUDIO;
+ sd->info.dir = PJ_MEDIA_DIR_ENCODING_DECODING;
+ sd->info.transport = ID_RTP_AVP;
+ pj_memcpy(&sd->info.sock_info, sock_info, sizeof(*sock_info));
+
+ /* Enum audio codecs. */
+ sd->info.fmt_cnt = 0;
+ cm = pj_med_mgr_get_codec_mgr (mgr);
+ codec_cnt = pj_codec_mgr_enum_codecs(cm, PJSDP_MAX_FMT, codecs);
+ if (codec_cnt > PJSDP_MAX_FMT) codec_cnt = PJSDP_MAX_FMT;
+ for (i=0; i<codec_cnt; ++i) {
+ if (codecs[i]->type != PJ_MEDIA_TYPE_AUDIO)
+ continue;
+
+ sd->info.fmt[sd->info.fmt_cnt].pt = codecs[i]->pt;
+ sd->info.fmt[sd->info.fmt_cnt].sample_rate = codecs[i]->sample_rate;
+ pj_strdup (pool, &sd->info.fmt[sd->info.fmt_cnt].encoding_name, &codecs[i]->encoding_name);
+ ++sd->info.fmt_cnt;
+ }
+
+ session->stream_desc[session->stream_cnt++] = sd;
+
+ return session;
+}
+
+static int sdp_check (const pjsdp_session_desc *sdp)
+{
+ int has_conn = 0;
+ unsigned i;
+
+ if (sdp->conn)
+ has_conn = 1;
+
+ if (sdp->media_count == 0) {
+ PJ_LOG(4,(THIS_FILE, "SDP check failed: no media stream definition"));
+ return -1;
+ }
+
+ for (i=0; i<sdp->media_count; ++i) {
+ pjsdp_media_desc *m = sdp->media[i];
+
+ if (!m) {
+ pj_assert(0);
+ return -1;
+ }
+
+ if (m->desc.fmt_count == 0) {
+ PJ_LOG(4,(THIS_FILE, "SDP check failed: no format listed in media stream"));
+ return -1;
+ }
+
+ if (!has_conn && m->conn == NULL) {
+ PJ_LOG(4,(THIS_FILE, "SDP check failed: no connection information for media"));
+ return -1;
+ }
+ }
+
+ return 0;
+}
+
+/*
+ * Create local stream definition that matches SDP received from peer.
+ */
+static pj_media_stream_desc*
+create_stream_from_sdp (pj_pool_t *pool, pj_med_mgr_t *mgr, const pjsdp_conn_info *conn,
+ const pjsdp_media_desc *m, const pj_media_sock_info *sock_info)
+{
+ pj_media_stream_desc *sd;
+
+ sd = pj_pool_calloc (pool, 1, sizeof(pj_media_stream_desc));
+ if (!sd) {
+ PJ_LOG(2,(THIS_FILE, "No memory to allocate stream descriptor"));
+ return NULL;
+ }
+
+ if (pj_stricmp(&conn->net_type, &ID_IN)==0 &&
+ pj_stricmp(&conn->addr_type, &ID_IP4)==0 &&
+ pj_stricmp(&m->desc.media, &ID_AUDIO)==0 &&
+ pj_stricmp(&m->desc.transport, &ID_RTP_AVP) == 0)
+ {
+ /*
+ * Got audio stream.
+ */
+ unsigned i, codec_cnt;
+ pj_codec_mgr *cm;
+ const pj_codec_id *codecs[PJSDP_MAX_FMT];
+
+ sd->info.type = ID_AUDIO;
+ sd->info.transport = ID_RTP_AVP;
+ pj_memcpy(&sd->info.sock_info, sock_info, sizeof(*sock_info));
+ sd->info.rem_port = m->desc.port;
+ pj_strdup (pool, &sd->info.rem_addr, &conn->addr);
+
+ /* Enum audio codecs. */
+ sd->info.fmt_cnt = 0;
+ cm = pj_med_mgr_get_codec_mgr (mgr);
+ codec_cnt = pj_codec_mgr_enum_codecs (cm, PJSDP_MAX_FMT, codecs);
+ if (codec_cnt > PJSDP_MAX_FMT) codec_cnt = PJSDP_MAX_FMT;
+
+ /* Find just one codec which we can support. */
+ for (i=0; i<m->desc.fmt_count && sd->info.fmt_cnt == 0; ++i) {
+ unsigned j, fmt_i;
+
+ /* For static payload, just match payload type. */
+ /* Else match clock rate and encoding name. */
+ fmt_i = pj_strtoul(&m->desc.fmt[i]);
+ if (fmt_i < PJ_RTP_PT_DYNAMIC) {
+ for (j=0; j<codec_cnt; ++j) {
+ if (codecs[j]->pt == fmt_i) {
+ sd->info.fmt_cnt = 1;
+ sd->info.fmt[0].type = PJ_MEDIA_TYPE_AUDIO;
+ sd->info.fmt[0].pt = codecs[j]->pt;
+ sd->info.fmt[0].sample_rate = codecs[j]->sample_rate;
+ pj_strdup (pool, &sd->info.fmt[0].encoding_name, &codecs[j]->encoding_name);
+ break;
+ }
+ }
+ } else {
+
+ /* Find the rtpmap for the payload type. */
+ const pjsdp_rtpmap_attr *rtpmap = pjsdp_media_desc_find_rtpmap (m, fmt_i);
+
+ /* Don't accept the media if no rtpmap for dynamic PT. */
+ if (rtpmap == NULL) {
+ PJ_LOG(4,(THIS_FILE, "SDP: No rtpmap found for payload id %d", m->desc.fmt[i]));
+ continue;
+ }
+
+ /* Check whether we can take this codec. */
+ for (j=0; j<codec_cnt; ++j) {
+ if (rtpmap->clock_rate == codecs[j]->sample_rate &&
+ pj_stricmp(&rtpmap->encoding_name, &codecs[j]->encoding_name) == 0)
+ {
+ sd->info.fmt_cnt = 1;
+ sd->info.fmt[0].type = PJ_MEDIA_TYPE_AUDIO;
+ sd->info.fmt[0].pt = codecs[j]->pt;
+ sd->info.fmt[0].sample_rate = codecs[j]->sample_rate;
+ sd->info.fmt[0].encoding_name = codecs[j]->encoding_name;
+ break;
+ }
+ }
+ }
+ }
+
+ /* Match codec and direction. */
+ if (sd->info.fmt_cnt == 0 || m->desc.port == 0 ||
+ pjsdp_media_desc_has_attr(m, PJSDP_ATTR_INACTIVE))
+ {
+ sd->info.dir = PJ_MEDIA_DIR_NONE;
+ }
+ else if (pjsdp_media_desc_has_attr(m, PJSDP_ATTR_RECV_ONLY)) {
+ sd->info.dir = PJ_MEDIA_DIR_ENCODING;
+ }
+ else if (pjsdp_media_desc_has_attr(m, PJSDP_ATTR_SEND_ONLY)) {
+ sd->info.dir = PJ_MEDIA_DIR_DECODING;
+ }
+ else {
+ sd->info.dir = PJ_MEDIA_DIR_ENCODING_DECODING;
+ }
+
+ } else {
+ /* Unsupported media stream. */
+ unsigned fmt_num;
+ const pjsdp_rtpmap_attr *rtpmap = NULL;
+
+ pj_strdup(pool, &sd->info.type, &m->desc.media);
+ pj_strdup(pool, &sd->info.transport, &m->desc.transport);
+ pj_memset(&sd->info.sock_info, 0, sizeof(*sock_info));
+ pj_strdup (pool, &sd->info.rem_addr, &conn->addr);
+ sd->info.rem_port = m->desc.port;
+
+ /* Just put one format and rtpmap, so that we don't have to make
+ * special exception when we convert this stream to SDP.
+ */
+
+ /* Find the rtpmap for the payload type. */
+ fmt_num = pj_strtoul(&m->desc.fmt[0]);
+ rtpmap = pjsdp_media_desc_find_rtpmap (m, fmt_num);
+
+ sd->info.fmt_cnt = 1;
+ if (pj_stricmp(&m->desc.media, &ID_VIDEO)==0) {
+ sd->info.fmt[0].type = PJ_MEDIA_TYPE_VIDEO;
+ sd->info.fmt[0].pt = fmt_num;
+ if (rtpmap) {
+ pj_strdup (pool, &sd->info.fmt[0].encoding_name,
+ &rtpmap->encoding_name);
+ sd->info.fmt[0].sample_rate = rtpmap->clock_rate;
+ }
+ } else {
+ sd->info.fmt[0].type = PJ_MEDIA_TYPE_UNKNOWN;
+ pj_strdup(pool, &sd->info.fmt[0].encoding_name, &m->desc.fmt[0]);
+ }
+
+ sd->info.dir = PJ_MEDIA_DIR_NONE;
+ }
+
+ return sd;
+}
+
+/**
+ * Create new session based on peer's offering.
+ */
+PJ_DEF(pj_media_session_t*)
+pj_media_session_create_from_sdp (pj_med_mgr_t *mgr, const pjsdp_session_desc *sdp,
+ const pj_media_sock_info *sock_info)
+{
+ pj_pool_factory *pf;
+ pj_pool_t *pool;
+ pj_media_session_t *session;
+ unsigned i;
+
+ if (sdp_check(sdp) != 0)
+ return NULL;
+
+ pf = pj_med_mgr_get_pool_factory(mgr);
+ pool = pj_pool_create( pf, "session", PJ_MEDIA_SESSION_SIZE, PJ_MEDIA_SESSION_INC, NULL);
+ if (!pool)
+ return NULL;
+
+ session = pj_pool_alloc(pool, sizeof(pj_media_session_t));
+ if (!session) {
+ PJ_LOG(3,(THIS_FILE, "No memory to create media session descriptor"));
+ pj_pool_release (pool);
+ return NULL;
+ }
+
+ session_init (session);
+
+ session->pool = pool;
+ session->mediamgr = mgr;
+
+ /* Enumerate each media stream and create our peer. */
+ for (i=0; i<sdp->media_count; ++i) {
+ const pjsdp_conn_info *conn;
+ const pjsdp_media_desc *m;
+ pj_media_stream_desc *sd;
+
+ m = sdp->media[i];
+ conn = m->conn ? m->conn : sdp->conn;
+
+ /*
+ * Bug:
+ * the sock_info below is used by more than one 'm' lines
+ */
+ PJ_TODO(SUPPORT_MORE_THAN_ONE_SDP_M_LINES)
+
+ sd = create_stream_from_sdp (pool, mgr, conn, m, sock_info);
+ pj_assert (sd);
+
+ session->stream_desc[session->stream_cnt++] = sd;
+ }
+
+ return session;
+}
+
+/**
+ * Duplicate session. The new session is inactive.
+ */
+PJ_DEF(pj_media_session_t*)
+pj_media_session_clone (const pj_media_session_t *rhs)
+{
+ pj_pool_factory *pf;
+ pj_pool_t *pool;
+ pj_media_session_t *session;
+ unsigned i;
+
+ pf = pj_med_mgr_get_pool_factory(rhs->mediamgr);
+ pool = pj_pool_create( pf, "session", PJ_MEDIA_SESSION_SIZE, PJ_MEDIA_SESSION_INC, NULL);
+ if (!pool) {
+ return NULL;
+ }
+
+ session = pj_pool_alloc (pool, sizeof(*session));
+ if (!session) {
+ PJ_LOG(3,(THIS_FILE, "No memory to create media session descriptor"));
+ pj_pool_release (pool);
+ return NULL;
+ }
+
+ session->pool = pool;
+ session->mediamgr = rhs->mediamgr;
+ session->stream_cnt = rhs->stream_cnt;
+
+ for (i=0; i<rhs->stream_cnt; ++i) {
+ pj_media_stream_desc *sd1 = pj_pool_alloc (session->pool, sizeof(pj_media_stream_desc));
+ const pj_media_stream_desc *sd2 = rhs->stream_desc[i];
+
+ if (!sd1) {
+ PJ_LOG(3,(THIS_FILE, "No memory to create media stream descriptor"));
+ pj_pool_release (pool);
+ return NULL;
+ }
+
+ session->stream_desc[i] = sd1;
+ sd1->enc_stream = sd1->dec_stream = NULL;
+ pj_strdup (pool, &sd1->info.type, &sd2->info.type);
+ sd1->info.dir = sd2->info.dir;
+ pj_strdup (pool, &sd1->info.transport, &sd2->info.transport);
+ pj_memcpy(&sd1->info.sock_info, &sd2->info.sock_info, sizeof(pj_media_sock_info));
+ pj_strdup (pool, &sd1->info.rem_addr, &sd2->info.rem_addr);
+ sd1->info.rem_port = sd2->info.rem_port;
+ sd1->info.fmt_cnt = sd2->info.fmt_cnt;
+ pj_memcpy (sd1->info.fmt, sd2->info.fmt, sizeof(sd2->info.fmt));
+ }
+
+ return session;
+}
+
+/**
+ * Create SDP description from the session.
+ */
+PJ_DEF(pjsdp_session_desc*)
+pj_media_session_create_sdp (const pj_media_session_t *session, pj_pool_t *pool,
+ pj_bool_t only_first_fmt)
+{
+ pjsdp_session_desc *sdp;
+ pj_time_val tv;
+ unsigned i;
+ pj_media_sock_info *c_addr = NULL;
+
+ if (session->stream_cnt == 0) {
+ pj_assert(0);
+ return NULL;
+ }
+
+ sdp = pj_pool_calloc (pool, 1, sizeof(pjsdp_session_desc));
+ if (!sdp) {
+ PJ_LOG(3,(THIS_FILE, "No memory to allocate SDP session descriptor"));
+ return NULL;
+ }
+
+ pj_gettimeofday(&tv);
+
+ sdp->origin.user = pj_str("-");
+ sdp->origin.version = sdp->origin.id = tv.sec + 2208988800UL;
+ sdp->origin.net_type = ID_IN;
+ sdp->origin.addr_type = ID_IP4;
+ sdp->origin.addr = *pj_gethostname();
+
+ sdp->name = ID_SDP_NAME;
+
+ /* If all media addresses are the same, then put the connection
+ * info in the session level, otherwise put it in media stream
+ * level.
+ */
+ for (i=0; i<session->stream_cnt; ++i) {
+ if (c_addr == NULL) {
+ c_addr = &session->stream_desc[i]->info.sock_info;
+ } else if (c_addr->rtp_addr_name.sin_addr.s_addr != session->stream_desc[i]->info.sock_info.rtp_addr_name.sin_addr.s_addr)
+ {
+ c_addr = NULL;
+ break;
+ }
+ }
+
+ if (c_addr) {
+ /* All addresses are the same, put connection info in session level. */
+ sdp->conn = pj_pool_alloc (pool, sizeof(pjsdp_conn_info));
+ if (!sdp->conn) {
+ PJ_LOG(2,(THIS_FILE, "No memory to allocate SDP connection info"));
+ return NULL;
+ }
+
+ sdp->conn->net_type = ID_IN;
+ sdp->conn->addr_type = ID_IP4;
+ pj_strdup2 (pool, &sdp->conn->addr, pj_inet_ntoa(c_addr->rtp_addr_name.sin_addr));
+ }
+
+ sdp->time.start = sdp->time.stop = 0;
+ sdp->attr_count = 0;
+
+ /* Create each media. */
+ sdp->media_count = 0;
+ for (i=0; i<session->stream_cnt; ++i) {
+ const pj_media_stream_desc *sd = session->stream_desc[i];
+ pjsdp_media_desc *m;
+ unsigned j;
+ unsigned fmt_cnt;
+ pjsdp_attr *attr;
+
+ m = pj_pool_calloc (pool, 1, sizeof(pjsdp_media_desc));
+ if (!m) {
+ PJ_LOG(3,(THIS_FILE, "No memory to allocate SDP media stream descriptor"));
+ return NULL;
+ }
+
+ sdp->media[sdp->media_count++] = m;
+
+ pj_strdup (pool, &m->desc.media, &sd->info.type);
+ m->desc.port = pj_ntohs(sd->info.sock_info.rtp_addr_name.sin_port);
+ m->desc.port_count = 1;
+ pj_strdup (pool, &m->desc.transport, &sd->info.transport);
+
+ /* Add format and rtpmap for each codec. */
+ m->desc.fmt_count = 0;
+ m->attr_count = 0;
+ fmt_cnt = sd->info.fmt_cnt;
+ if (fmt_cnt > 0 && only_first_fmt)
+ fmt_cnt = 1;
+ for (j=0; j<fmt_cnt; ++j) {
+ pjsdp_rtpmap_attr *rtpmap;
+ pj_str_t *fmt = &m->desc.fmt[m->desc.fmt_count++];
+
+ if (sd->info.fmt[j].type==PJ_MEDIA_TYPE_UNKNOWN) {
+ pj_strdup(pool, fmt, &sd->info.fmt[j].encoding_name);
+ } else {
+ fmt->ptr = pj_pool_alloc(pool, 8);
+ fmt->slen = pj_utoa(sd->info.fmt[j].pt, fmt->ptr);
+
+ rtpmap = pj_pool_calloc(pool, 1, sizeof(pjsdp_rtpmap_attr));
+ if (rtpmap) {
+ m->attr[m->attr_count++] = (pjsdp_attr*)rtpmap;
+ rtpmap->type = PJSDP_ATTR_RTPMAP;
+ rtpmap->payload_type = sd->info.fmt[j].pt;
+ rtpmap->clock_rate = sd->info.fmt[j].sample_rate;
+ pj_strdup (pool, &rtpmap->encoding_name, &sd->info.fmt[j].encoding_name);
+ } else {
+ PJ_LOG(3,(THIS_FILE, "No memory to allocate SDP rtpmap descriptor"));
+ }
+ }
+ }
+
+ /* If we don't have connection info in session level, create one. */
+ if (sdp->conn == NULL) {
+ m->conn = pj_pool_alloc (pool, sizeof(pjsdp_conn_info));
+ if (m->conn) {
+ m->conn->net_type = ID_IN;
+ m->conn->addr_type = ID_IP4;
+ pj_strdup2 (pool, &m->conn->addr, pj_inet_ntoa(sd->info.sock_info.rtp_addr_name.sin_addr));
+ } else {
+ PJ_LOG(3,(THIS_FILE, "No memory to allocate SDP media connection info"));
+ return NULL;
+ }
+ }
+
+ /* Add additional attribute to the media stream. */
+ attr = pj_pool_alloc(pool, sizeof(pjsdp_attr));
+ if (!attr) {
+ PJ_LOG(3,(THIS_FILE, "No memory to allocate SDP attribute"));
+ return NULL;
+ }
+ m->attr[m->attr_count++] = attr;
+
+ switch (sd->info.dir) {
+ case PJ_MEDIA_DIR_NONE:
+ attr->type = PJSDP_ATTR_INACTIVE;
+ break;
+ case PJ_MEDIA_DIR_ENCODING:
+ attr->type = PJSDP_ATTR_SEND_ONLY;
+ break;
+ case PJ_MEDIA_DIR_DECODING:
+ attr->type = PJSDP_ATTR_RECV_ONLY;
+ break;
+ case PJ_MEDIA_DIR_ENCODING_DECODING:
+ attr->type = PJSDP_ATTR_SEND_RECV;
+ break;
+ }
+ }
+
+ return sdp;
+}
+
+/**
+ * Update session with SDP answer from peer.
+ */
+PJ_DEF(pj_status_t)
+pj_media_session_update (pj_media_session_t *session,
+ const pjsdp_session_desc *sdp)
+{
+ unsigned i;
+ unsigned count;
+
+ /* Check SDP */
+ if (sdp_check (sdp) != 0) {
+ return -1;
+ }
+
+ /* If the media stream count doesn't match, only update one. */
+ if (session->stream_cnt != sdp->media_count) {
+ PJ_LOG(3,(THIS_FILE, "pj_media_session_update : "
+ "SDP media count mismatch! (rmt=%d, lcl=%d)",
+ sdp->media_count, session->stream_cnt));
+ count = (session->stream_cnt < sdp->media_count) ?
+ session->stream_cnt : sdp->media_count;
+ } else {
+ count = session->stream_cnt;
+ }
+
+ for (i=0; i<count; ++i) {
+ pj_media_stream_desc *sd = session->stream_desc[i];
+ const pjsdp_media_desc *m = sdp->media[i];
+ const pjsdp_conn_info *conn;
+ unsigned j;
+
+ /* Check that the session is not active. */
+ pj_assert (sd->enc_stream == NULL && sd->dec_stream == NULL);
+
+ conn = m->conn ? m->conn : sdp->conn;
+ pj_assert(conn);
+
+ /* Update remote address. */
+ sd->info.rem_port = m->desc.port;
+ pj_strdup (session->pool, &sd->info.rem_addr, &conn->addr);
+
+ /* Select one active codec according to what peer wants. */
+ for (j=0; j<sd->info.fmt_cnt; ++j) {
+ unsigned fmt_0 = pj_strtoul(&m->desc.fmt[0]);
+ if (sd->info.fmt[j].pt == fmt_0) {
+ pj_codec_id temp;
+
+ /* Put active format to the front. */
+ if (j == 0)
+ break;
+
+ pj_memcpy(&temp, &sd->info.fmt[0], sizeof(temp));
+ pj_memcpy(&sd->info.fmt[0], &sd->info.fmt[j], sizeof(temp));
+ pj_memcpy(&sd->info.fmt[j], &temp, sizeof(temp));
+ break;
+ }
+ }
+
+ if (j == sd->info.fmt_cnt) {
+ /* Peer has answered SDP with new codec, which doesn't exist
+ * in the offer!
+ * Mute this media.
+ */
+ PJ_LOG(3,(THIS_FILE, "Peer has answered SDP with new codec!"));
+ sd->info.dir = PJ_MEDIA_DIR_NONE;
+ continue;
+ }
+
+ /* Check direction. */
+ if (m->desc.port == 0 || pjsdp_media_desc_has_attr(m, PJSDP_ATTR_INACTIVE)) {
+ sd->info.dir = PJ_MEDIA_DIR_NONE;
+ }
+ else if (pjsdp_media_desc_has_attr(m, PJSDP_ATTR_RECV_ONLY)) {
+ sd->info.dir = PJ_MEDIA_DIR_ENCODING;
+ }
+ else if (pjsdp_media_desc_has_attr(m, PJSDP_ATTR_SEND_ONLY)) {
+ sd->info.dir = PJ_MEDIA_DIR_DECODING;
+ }
+ else {
+ sd->info.dir = PJ_MEDIA_DIR_ENCODING_DECODING;
+ }
+ }
+
+ return 0;
+}
+
+/**
+ * Enumerate media streams in the session.
+ */
+PJ_DEF(unsigned)
+pj_media_session_enum_streams (const pj_media_session_t *session,
+ unsigned count, const pj_media_stream_info *info[])
+{
+ unsigned i;
+
+ if (count > session->stream_cnt)
+ count = session->stream_cnt;
+
+ for (i=0; i<count; ++i) {
+ info[i] = &session->stream_desc[i]->info;
+ }
+
+ return session->stream_cnt;
+}
+
+/**
+ * Get statistics
+ */
+PJ_DEF(pj_status_t)
+pj_media_session_get_stat (const pj_media_session_t *session, unsigned index,
+ pj_media_stream_stat *tx_stat,
+ pj_media_stream_stat *rx_stat)
+{
+ pj_media_stream_desc *sd;
+ int stat_cnt = 0;
+
+ if (index >= session->stream_cnt) {
+ pj_assert(0);
+ return -1;
+ }
+
+ sd = session->stream_desc[index];
+
+ if (sd->enc_stream && tx_stat) {
+ pj_media_stream_get_stat (sd->enc_stream, tx_stat);
+ ++stat_cnt;
+ } else if (tx_stat) {
+ pj_memset (tx_stat, 0, sizeof(*tx_stat));
+ }
+
+ if (sd->dec_stream && rx_stat) {
+ pj_media_stream_get_stat (sd->dec_stream, rx_stat);
+ ++stat_cnt;
+ } else if (rx_stat) {
+ pj_memset (rx_stat, 0, sizeof(*rx_stat));
+ }
+
+ return stat_cnt ? 0 : -1;
+}
+
+/**
+ * Modify stream, only when stream is inactive.
+ */
+PJ_DEF(pj_status_t)
+pj_media_session_modify_stream (pj_media_session_t *session, unsigned index,
+ unsigned modify_flag, const pj_media_stream_info *info)
+{
+ pj_media_stream_desc *sd;
+
+ if (index >= session->stream_cnt) {
+ pj_assert(0);
+ return -1;
+ }
+
+ sd = session->stream_desc[index];
+
+ if (sd->enc_stream || sd->dec_stream) {
+ pj_assert(0);
+ return -1;
+ }
+
+ if (modify_flag & PJ_MEDIA_STREAM_MODIFY_DIR) {
+ sd->info.dir = info->dir;
+ }
+
+ return 0;
+}
+
+/**
+ * Activate media session.
+ */
+PJ_DEF(pj_status_t)
+pj_media_session_activate (pj_media_session_t *session)
+{
+ unsigned i;
+ pj_status_t status = 0;
+
+ for (i=0; i<session->stream_cnt; ++i) {
+ pj_status_t rc;
+ rc = pj_media_session_activate_stream (session, i);
+ if (status == 0)
+ status = rc;
+ }
+ return status;
+}
+
+/**
+ * Activate individual stream in media session.
+ */
+PJ_DEF(pj_status_t)
+pj_media_session_activate_stream (pj_media_session_t *session, unsigned index)
+{
+ pj_media_stream_desc *sd;
+ pj_media_stream_create_param scp;
+ pj_status_t status;
+ pj_time_val tv;
+
+ if (index < 0 || index >= session->stream_cnt) {
+ pj_assert(0);
+ return -1;
+ }
+
+ sd = session->stream_desc[index];
+
+ if (sd->enc_stream || sd->dec_stream) {
+ /* Stream already active. */
+ pj_assert(0);
+ return 0;
+ }
+
+ pj_gettimeofday(&tv);
+
+ /* Initialize parameter to create stream. */
+ pj_memset (&scp, 0, sizeof(scp));
+ scp.codec_id = &sd->info.fmt[0];
+ scp.mediamgr = session->mediamgr;
+ scp.dir = sd->info.dir;
+ scp.rtp_sock = sd->info.sock_info.rtp_sock;
+ scp.rtcp_sock = sd->info.sock_info.rtcp_sock;
+ scp.remote_addr = pj_pool_calloc (session->pool, 1, sizeof(pj_sockaddr_in));
+ pj_sockaddr_init (scp.remote_addr, &sd->info.rem_addr, sd->info.rem_port);
+ scp.ssrc = tv.sec;
+ scp.jb_min = 1;
+ scp.jb_max = 15;
+ scp.jb_maxcnt = 16;
+
+ /* Build the stream! */
+ status = pj_media_stream_create (session->pool, &sd->enc_stream, &sd->dec_stream, &scp);
+
+ if (status==0 && sd->enc_stream) {
+ status = pj_media_stream_start (sd->enc_stream);
+ if (status != 0)
+ goto on_error;
+ }
+ if (status==0 && sd->dec_stream) {
+ status = pj_media_stream_start (sd->dec_stream);
+ if (status != 0)
+ goto on_error;
+ }
+ return status;
+
+on_error:
+ if (sd->enc_stream) {
+ pj_media_stream_destroy (sd->enc_stream);
+ sd->enc_stream = NULL;
+ }
+ if (sd->dec_stream) {
+ pj_media_stream_destroy (sd->dec_stream);
+ sd->dec_stream = NULL;
+ }
+ return status;
+}
+
+/**
+ * Destroy media session.
+ */
+PJ_DEF(pj_status_t)
+pj_media_session_destroy (pj_media_session_t *session)
+{
+ unsigned i;
+
+ if (!session)
+ return -1;
+
+ for (i=0; i<session->stream_cnt; ++i) {
+ pj_media_stream_desc *sd = session->stream_desc[i];
+
+ if (sd->enc_stream) {
+ pj_media_stream_destroy (sd->enc_stream);
+ sd->enc_stream = NULL;
+ }
+ if (sd->dec_stream) {
+ pj_media_stream_destroy (sd->dec_stream);
+ sd->dec_stream = NULL;
+ }
+ }
+ pj_pool_release (session->pool);
+ return 0;
+}
+
diff --git a/pjmedia/src/pjmedia/session.h b/pjmedia/src/pjmedia/session.h
index 3ae9d40..411b7f8 100644
--- a/pjmedia/src/pjmedia/session.h
+++ b/pjmedia/src/pjmedia/session.h
@@ -1,138 +1,159 @@
-/* $Id$
- *
- */
-
-#ifndef __PJMEDIA_SESSION_H__
-#define __PJMEDIA_SESSION_H__
-
-
-/**
- * @file session.h
- * @brief Media Session.
- */
-
-#include <pj/types.h>
-#include <pjmedia/mediamgr.h>
-#include <pjmedia/stream.h>
-#include <pjmedia/sdp.h>
-
-PJ_BEGIN_DECL
-
-/**
- * @defgroup PJMED_SES Media session
- * @ingroup PJMEDIA
- * @{
- */
-
-/** Opaque declaration of media session. */
-typedef struct pj_media_session_t pj_media_session_t;
-
-/** Media socket info. */
-typedef struct pj_media_sock_info
-{
- pj_sock_t rtp_sock, rtcp_sock;
- pj_sockaddr_in rtp_addr_name;
-} pj_media_sock_info;
-
-/** Stream info. */
-typedef struct pj_media_stream_info
-{
- pj_str_t type;
- pj_media_dir_t dir;
- pj_str_t transport;
- pj_media_sock_info sock_info;
- pj_str_t rem_addr;
- unsigned short rem_port;
- unsigned fmt_cnt;
- pj_codec_id fmt[PJSDP_MAX_FMT];
-
-} pj_media_stream_info;
-
-/** Flag for modifying stream. */
-enum
-{
- PJ_MEDIA_STREAM_MODIFY_DIR = 1,
-};
-
-/**
- * Create new session offering.
- */
-PJ_DECL(pj_media_session_t*)
-pj_media_session_create ( pj_med_mgr_t *mgr, const pj_media_sock_info *skinfo );
-
-/**
- * Create new session based on peer's offering.
- */
-PJ_DECL(pj_media_session_t*)
-pj_media_session_create_from_sdp ( pj_med_mgr_t *mgr, const pjsdp_session_desc *sdp,
- const pj_media_sock_info *skinfo);
-
-/**
- * Duplicate session. The new session is inactive.
- */
-PJ_DECL(pj_media_session_t*)
-pj_media_session_clone (const pj_media_session_t *session);
-
-/**
- * Create SDP description from the session.
- */
-PJ_DECL(pjsdp_session_desc*)
-pj_media_session_create_sdp ( const pj_media_session_t *session, pj_pool_t *pool,
- pj_bool_t only_first_fmt);
-
-/**
- * Update session with SDP answer from peer. The session must NOT active.
- */
-PJ_DECL(pj_status_t)
-pj_media_session_update ( pj_media_session_t *session,
- const pjsdp_session_desc *sdp);
-
-/**
- * Enumerate media streams in the session.
- * @return the actual number of streams.
- */
-PJ_DECL(unsigned)
-pj_media_session_enum_streams (const pj_media_session_t *session,
- unsigned count, const pj_media_stream_info *info[]);
-
-/**
- * Get stream statistics.
- */
-PJ_DECL(pj_status_t)
-pj_media_session_get_stat (const pj_media_session_t *session, unsigned index,
- pj_media_stream_stat *tx_stat,
- pj_media_stream_stat *rx_stat);
-
-/**
- * Modify stream, only when stream is inactive.
- */
-PJ_DECL(pj_status_t)
-pj_media_session_modify_stream (pj_media_session_t *session, unsigned index,
- unsigned modify_flag, const pj_media_stream_info *info);
-
-/**
- * Activate all streams in media session.
- */
-PJ_DECL(pj_status_t)
-pj_media_session_activate (pj_media_session_t *session);
-
-/**
- * Activate individual stream in media session.
- */
-PJ_DECL(pj_status_t)
-pj_media_session_activate_stream (pj_media_session_t *session, unsigned index);
-
-/**
- * Destroy media session.
- */
-PJ_DECL(pj_status_t)
-pj_media_session_destroy (pj_media_session_t *session);
-
-
-/**
- * @}
- */
-
-PJ_END_DECL
-
-#endif /* __PJMEDIA_SESSION_H__ */
+/* $Id$
+ *
+ */
+/*
+ * PJMEDIA - Multimedia over IP Stack
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ * Benny Prijono <bennylp@bulukucing.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef __PJMEDIA_SESSION_H__
+#define __PJMEDIA_SESSION_H__
+
+
+/**
+ * @file session.h
+ * @brief Media Session.
+ */
+
+#include <pj/types.h>
+#include <pjmedia/mediamgr.h>
+#include <pjmedia/stream.h>
+#include <pjmedia/sdp.h>
+
+PJ_BEGIN_DECL
+
+/**
+ * @defgroup PJMED_SES Media session
+ * @ingroup PJMEDIA
+ * @{
+ */
+
+/** Opaque declaration of media session. */
+typedef struct pj_media_session_t pj_media_session_t;
+
+/** Media socket info. */
+typedef struct pj_media_sock_info
+{
+ pj_sock_t rtp_sock, rtcp_sock;
+ pj_sockaddr_in rtp_addr_name;
+} pj_media_sock_info;
+
+/** Stream info. */
+typedef struct pj_media_stream_info
+{
+ pj_str_t type;
+ pj_media_dir_t dir;
+ pj_str_t transport;
+ pj_media_sock_info sock_info;
+ pj_str_t rem_addr;
+ unsigned short rem_port;
+ unsigned fmt_cnt;
+ pj_codec_id fmt[PJSDP_MAX_FMT];
+
+} pj_media_stream_info;
+
+/** Flag for modifying stream. */
+enum
+{
+ PJ_MEDIA_STREAM_MODIFY_DIR = 1,
+};
+
+/**
+ * Create new session offering.
+ */
+PJ_DECL(pj_media_session_t*)
+pj_media_session_create ( pj_med_mgr_t *mgr, const pj_media_sock_info *skinfo );
+
+/**
+ * Create new session based on peer's offering.
+ */
+PJ_DECL(pj_media_session_t*)
+pj_media_session_create_from_sdp ( pj_med_mgr_t *mgr, const pjsdp_session_desc *sdp,
+ const pj_media_sock_info *skinfo);
+
+/**
+ * Duplicate session. The new session is inactive.
+ */
+PJ_DECL(pj_media_session_t*)
+pj_media_session_clone (const pj_media_session_t *session);
+
+/**
+ * Create SDP description from the session.
+ */
+PJ_DECL(pjsdp_session_desc*)
+pj_media_session_create_sdp ( const pj_media_session_t *session, pj_pool_t *pool,
+ pj_bool_t only_first_fmt);
+
+/**
+ * Update session with SDP answer from peer. The session must NOT active.
+ */
+PJ_DECL(pj_status_t)
+pj_media_session_update ( pj_media_session_t *session,
+ const pjsdp_session_desc *sdp);
+
+/**
+ * Enumerate media streams in the session.
+ * @return the actual number of streams.
+ */
+PJ_DECL(unsigned)
+pj_media_session_enum_streams (const pj_media_session_t *session,
+ unsigned count, const pj_media_stream_info *info[]);
+
+/**
+ * Get stream statistics.
+ */
+PJ_DECL(pj_status_t)
+pj_media_session_get_stat (const pj_media_session_t *session, unsigned index,
+ pj_media_stream_stat *tx_stat,
+ pj_media_stream_stat *rx_stat);
+
+/**
+ * Modify stream, only when stream is inactive.
+ */
+PJ_DECL(pj_status_t)
+pj_media_session_modify_stream (pj_media_session_t *session, unsigned index,
+ unsigned modify_flag, const pj_media_stream_info *info);
+
+/**
+ * Activate all streams in media session.
+ */
+PJ_DECL(pj_status_t)
+pj_media_session_activate (pj_media_session_t *session);
+
+/**
+ * Activate individual stream in media session.
+ */
+PJ_DECL(pj_status_t)
+pj_media_session_activate_stream (pj_media_session_t *session, unsigned index);
+
+/**
+ * Destroy media session.
+ */
+PJ_DECL(pj_status_t)
+pj_media_session_destroy (pj_media_session_t *session);
+
+
+/**
+ * @}
+ */
+
+PJ_END_DECL
+
+#endif /* __PJMEDIA_SESSION_H__ */
diff --git a/pjmedia/src/pjmedia/sound.h b/pjmedia/src/pjmedia/sound.h
index 997885a..a3f5163 100644
--- a/pjmedia/src/pjmedia/sound.h
+++ b/pjmedia/src/pjmedia/sound.h
@@ -1,187 +1,208 @@
-/* $Id$
- *
- */
-
-#ifndef __PJMEDIA_SOUND_H__
-#define __PJMEDIA_SOUND_H__
-
-
-/**
- * @file sound.h
- * @brief Sound player and recorder device framework.
- */
-
-#include <pj/pool.h>
-
-PJ_BEGIN_DECL
-
-/**
- * @defgroup PJMED_SND Sound device abstraction.
- * @ingroup PJMEDIA
- * @{
- */
-
-/** Opaque data type for audio stream. */
-typedef struct pj_snd_stream pj_snd_stream;
-
-/**
- * Device information structure returned by #pj_snd_get_dev_info.
- */
-typedef struct pj_snd_dev_info
-{
- char name[64]; /**< Device name. */
- unsigned input_count; /**< Max number of input channels. */
- unsigned output_count; /**< Max number of output channels. */
- unsigned default_samples_per_sec;/**< Default sampling rate. */
-} pj_snd_dev_info;
-
-/**
- * Sound device parameter, to be specified when calling #pj_snd_open_recorder
- * or #pj_snd_open_player.
- */
-typedef struct pj_snd_stream_info
-{
- unsigned samples_per_sec; /* Sampling rate. */
- unsigned bits_per_sample; /* No of bits per sample. */
- unsigned samples_per_frame; /* No of samples per frame. */
- unsigned bytes_per_frame; /* No of bytes per frame. */
- unsigned frames_per_packet; /* No of frames per packet. */
-} pj_snd_stream_info;
-
-/**
- * This callback is called by player stream when it needs additional data
- * to be played by the device. Application must fill in the whole of output
- * buffer with sound samples.
- *
- * @param user_data User data associated with the stream.
- * @param timestamp Timestamp, in samples.
- * @param output Buffer to be filled out by application.
- * @param size The size requested in bytes, which will be equal to
- * the size of one whole packet.
- *
- * @return Non-zero to stop the stream.
- */
-typedef pj_status_t (*pj_snd_play_cb)(/* in */ void *user_data,
- /* in */ pj_uint32_t timestamp,
- /* out */ void *output,
- /* out */ unsigned size);
-
-/**
- * This callback is called by recorder stream when it has captured the whole
- * packet worth of audio samples.
- *
- * @param user_data User data associated with the stream.
- * @param timestamp Timestamp, in samples.
- * @param output Buffer containing the captured audio samples.
- * @param size The size of the data in the buffer, in bytes.
- *
- * @return Non-zero to stop the stream.
- */
-typedef pj_status_t (*pj_snd_rec_cb)(/* in */ void *user_data,
- /* in */ pj_uint32_t timestamp,
- /* in */ const void *input,
- /* in*/ unsigned size);
-
-/**
- * Init the sound library.
- *
- * @param factory The sound factory.
- *
- * @return Zero on success.
- */
-PJ_DECL(pj_status_t) pj_snd_init(pj_pool_factory *factory);
-
-
-/**
- * Get the number of devices detected by the library.
- *
- * @return Number of devices.
- */
-PJ_DECL(int) pj_snd_get_dev_count(void);
-
-
-/**
- * Get device info.
- *
- * @param index The index of the device, which should be in the range
- * from zero to #pj_snd_get_dev_count - 1.
- */
-PJ_DECL(const pj_snd_dev_info*) pj_snd_get_dev_info(unsigned index);
-
-
-/**
- * Create a new audio stream for audio capture purpose.
- *
- * @param index Device index, or -1 to let the library choose the first
- * available device, or -2 to use NULL device.
- * @param param Stream parameters.
- * @param rec_cb Callback to handle captured audio samples.
- * @param user_data User data to be associated with the stream.
- *
- * @return Audio stream, or NULL if failed.
- */
-PJ_DECL(pj_snd_stream*) pj_snd_open_recorder(int index,
- const pj_snd_stream_info *param,
- pj_snd_rec_cb rec_cb,
- void *user_data);
-
-/**
- * Create a new audio stream for playing audio samples.
- *
- * @param index Device index, or -1 to let the library choose the first
- * available device, or -2 to use NULL device.
- * @param param Stream parameters.
- * @param play_cb Callback to supply audio samples.
- * @param user_data User data to be associated with the stream.
- *
- * @return Audio stream, or NULL if failed.
- */
-PJ_DECL(pj_snd_stream*) pj_snd_open_player(int index,
- const pj_snd_stream_info *param,
- pj_snd_play_cb play_cb,
- void *user_data);
-
-/**
- * Start the stream.
- *
- * @param stream The recorder or player stream.
- *
- * @return Zero on success.
- */
-PJ_DECL(pj_status_t) pj_snd_stream_start(pj_snd_stream *stream);
-
-/**
- * Stop the stream.
- *
- * @param stream The recorder or player stream.
- *
- * @return Zero on success.
- */
-PJ_DECL(pj_status_t) pj_snd_stream_stop(pj_snd_stream *stream);
-
-/**
- * Destroy the stream.
- *
- * @param stream The recorder of player stream.
- *
- * @return Zero on success.
- */
-PJ_DECL(pj_status_t) pj_snd_stream_close(pj_snd_stream *stream);
-
-/**
- * Deinitialize sound library.
- *
- * @return Zero on success.
- */
-PJ_DECL(pj_status_t) pj_snd_deinit(void);
-
-
-
-/**
- * @}
- */
-
-PJ_END_DECL
-
-
-#endif /* __PJMEDIA_SOUND_H__ */
+/* $Id$
+ *
+ */
+/*
+ * PJMEDIA - Multimedia over IP Stack
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ * Benny Prijono <bennylp@bulukucing.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef __PJMEDIA_SOUND_H__
+#define __PJMEDIA_SOUND_H__
+
+
+/**
+ * @file sound.h
+ * @brief Sound player and recorder device framework.
+ */
+
+#include <pj/pool.h>
+
+PJ_BEGIN_DECL
+
+/**
+ * @defgroup PJMED_SND Sound device abstraction.
+ * @ingroup PJMEDIA
+ * @{
+ */
+
+/** Opaque data type for audio stream. */
+typedef struct pj_snd_stream pj_snd_stream;
+
+/**
+ * Device information structure returned by #pj_snd_get_dev_info.
+ */
+typedef struct pj_snd_dev_info
+{
+ char name[64]; /**< Device name. */
+ unsigned input_count; /**< Max number of input channels. */
+ unsigned output_count; /**< Max number of output channels. */
+ unsigned default_samples_per_sec;/**< Default sampling rate. */
+} pj_snd_dev_info;
+
+/**
+ * Sound device parameter, to be specified when calling #pj_snd_open_recorder
+ * or #pj_snd_open_player.
+ */
+typedef struct pj_snd_stream_info
+{
+ unsigned samples_per_sec; /* Sampling rate. */
+ unsigned bits_per_sample; /* No of bits per sample. */
+ unsigned samples_per_frame; /* No of samples per frame. */
+ unsigned bytes_per_frame; /* No of bytes per frame. */
+ unsigned frames_per_packet; /* No of frames per packet. */
+} pj_snd_stream_info;
+
+/**
+ * This callback is called by player stream when it needs additional data
+ * to be played by the device. Application must fill in the whole of output
+ * buffer with sound samples.
+ *
+ * @param user_data User data associated with the stream.
+ * @param timestamp Timestamp, in samples.
+ * @param output Buffer to be filled out by application.
+ * @param size The size requested in bytes, which will be equal to
+ * the size of one whole packet.
+ *
+ * @return Non-zero to stop the stream.
+ */
+typedef pj_status_t (*pj_snd_play_cb)(/* in */ void *user_data,
+ /* in */ pj_uint32_t timestamp,
+ /* out */ void *output,
+ /* out */ unsigned size);
+
+/**
+ * This callback is called by recorder stream when it has captured the whole
+ * packet worth of audio samples.
+ *
+ * @param user_data User data associated with the stream.
+ * @param timestamp Timestamp, in samples.
+ * @param output Buffer containing the captured audio samples.
+ * @param size The size of the data in the buffer, in bytes.
+ *
+ * @return Non-zero to stop the stream.
+ */
+typedef pj_status_t (*pj_snd_rec_cb)(/* in */ void *user_data,
+ /* in */ pj_uint32_t timestamp,
+ /* in */ const void *input,
+ /* in*/ unsigned size);
+
+/**
+ * Init the sound library.
+ *
+ * @param factory The sound factory.
+ *
+ * @return Zero on success.
+ */
+PJ_DECL(pj_status_t) pj_snd_init(pj_pool_factory *factory);
+
+
+/**
+ * Get the number of devices detected by the library.
+ *
+ * @return Number of devices.
+ */
+PJ_DECL(int) pj_snd_get_dev_count(void);
+
+
+/**
+ * Get device info.
+ *
+ * @param index The index of the device, which should be in the range
+ * from zero to #pj_snd_get_dev_count - 1.
+ */
+PJ_DECL(const pj_snd_dev_info*) pj_snd_get_dev_info(unsigned index);
+
+
+/**
+ * Create a new audio stream for audio capture purpose.
+ *
+ * @param index Device index, or -1 to let the library choose the first
+ * available device, or -2 to use NULL device.
+ * @param param Stream parameters.
+ * @param rec_cb Callback to handle captured audio samples.
+ * @param user_data User data to be associated with the stream.
+ *
+ * @return Audio stream, or NULL if failed.
+ */
+PJ_DECL(pj_snd_stream*) pj_snd_open_recorder(int index,
+ const pj_snd_stream_info *param,
+ pj_snd_rec_cb rec_cb,
+ void *user_data);
+
+/**
+ * Create a new audio stream for playing audio samples.
+ *
+ * @param index Device index, or -1 to let the library choose the first
+ * available device, or -2 to use NULL device.
+ * @param param Stream parameters.
+ * @param play_cb Callback to supply audio samples.
+ * @param user_data User data to be associated with the stream.
+ *
+ * @return Audio stream, or NULL if failed.
+ */
+PJ_DECL(pj_snd_stream*) pj_snd_open_player(int index,
+ const pj_snd_stream_info *param,
+ pj_snd_play_cb play_cb,
+ void *user_data);
+
+/**
+ * Start the stream.
+ *
+ * @param stream The recorder or player stream.
+ *
+ * @return Zero on success.
+ */
+PJ_DECL(pj_status_t) pj_snd_stream_start(pj_snd_stream *stream);
+
+/**
+ * Stop the stream.
+ *
+ * @param stream The recorder or player stream.
+ *
+ * @return Zero on success.
+ */
+PJ_DECL(pj_status_t) pj_snd_stream_stop(pj_snd_stream *stream);
+
+/**
+ * Destroy the stream.
+ *
+ * @param stream The recorder of player stream.
+ *
+ * @return Zero on success.
+ */
+PJ_DECL(pj_status_t) pj_snd_stream_close(pj_snd_stream *stream);
+
+/**
+ * Deinitialize sound library.
+ *
+ * @return Zero on success.
+ */
+PJ_DECL(pj_status_t) pj_snd_deinit(void);
+
+
+
+/**
+ * @}
+ */
+
+PJ_END_DECL
+
+
+#endif /* __PJMEDIA_SOUND_H__ */
diff --git a/pjmedia/src/pjmedia/stream.c b/pjmedia/src/pjmedia/stream.c
index 1420a67..7419984 100644
--- a/pjmedia/src/pjmedia/stream.c
+++ b/pjmedia/src/pjmedia/stream.c
@@ -1,630 +1,651 @@
-/* $Id$
- *
- */
-
-#include <pjmedia/stream.h>
-#include <pjmedia/rtp.h>
-#include <pjmedia/rtcp.h>
-#include <pjmedia/jbuf.h>
-#include <pj/os.h>
-#include <pj/log.h>
-#include <pj/string.h> /* memcpy() */
-#include <pj/pool.h>
-#include <stdlib.h>
-
-#define THISFILE "stream.c"
-#define ERRLEVEL 1
-
-#define PJ_MAX_FRAME_DURATION_MS 200
-#define PJ_MAX_BUFFER_SIZE_MS 2000
-#define PJ_MAX_MTU 1500
-
-struct jb_frame
-{
- unsigned size;
- void *buf;
-};
-
-#define pj_fifobuf_alloc(fifo,size) malloc(size)
-#define pj_fifobuf_unalloc(fifo,buf) free(buf)
-#define pj_fifobuf_free(fifo, buf) free(buf)
-
-enum stream_state
-{
- STREAM_STOPPED,
- STREAM_STARTED,
-};
-
-struct pj_media_stream_t
-{
- pj_media_dir_t dir;
- int pt;
- int state;
- pj_media_stream_stat stat;
- pj_media_stream_t *peer;
- pj_snd_stream_info snd_info;
- pj_snd_stream *snd_stream;
- pj_mutex_t *mutex;
- unsigned in_pkt_size;
- void *in_pkt;
- unsigned out_pkt_size;
- void *out_pkt;
- unsigned pcm_buf_size;
- void *pcm_buf;
- //pj_fifobuf_t fifobuf;
- pj_codec_mgr *codec_mgr;
- pj_codec *codec;
- pj_rtp_session rtp;
- pj_rtcp_session *rtcp;
- pj_jitter_buffer *jb;
- pj_sock_t rtp_sock;
- pj_sock_t rtcp_sock;
- pj_sockaddr_in dst_addr;
- pj_thread_t *transport_thread;
- int thread_quit_flag;
-};
-
-
-static pj_status_t play_callback(/* in */ void *user_data,
- /* in */ pj_uint32_t timestamp,
- /* out */ void *frame,
- /*inout*/ unsigned size)
-{
- pj_media_stream_t *channel = user_data;
- struct jb_frame *jb_frame;
- void *p;
- pj_uint32_t extseq;
- pj_status_t status;
- struct pj_audio_frame frame_in, frame_out;
-
- PJ_UNUSED_ARG(timestamp)
-
- /* Lock mutex */
- pj_mutex_lock (channel->mutex);
-
- if (!channel->codec) {
- pj_mutex_unlock (channel->mutex);
- return -1;
- }
-
- /* Get frame from jitter buffer. */
- status = pj_jb_get (channel->jb, &extseq, &p);
- jb_frame = p;
- if (status != 0 || jb_frame == NULL) {
- pj_memset(frame, 0, size);
- pj_mutex_unlock(channel->mutex);
- return 0;
- }
-
- /* Decode */
- frame_in.buf = jb_frame->buf;
- frame_in.size = jb_frame->size;
- frame_in.type = PJ_AUDIO_FRAME_AUDIO; /* ignored */
- frame_out.buf = channel->pcm_buf;
- status = channel->codec->op->decode (channel->codec, &frame_in,
- channel->pcm_buf_size, &frame_out);
- if (status != 0) {
- PJ_LOG(3, (THISFILE, "decode() has return error status %d",
- status));
-
- pj_memset(frame, 0, size);
- pj_fifobuf_free (&channel->fifobuf, jb_frame);
- pj_mutex_unlock(channel->mutex);
- return 0;
- }
-
- /* Put in sound buffer. */
- if (frame_out.size > size) {
- PJ_LOG(3, (THISFILE, "Sound playout buffer truncated %d bytes",
- frame_out.size - size));
- frame_out.size = size;
- }
-
- pj_memcpy(frame, frame_out.buf, size);
-
- pj_fifobuf_free (&channel->fifobuf, jb_frame);
- pj_mutex_unlock(channel->mutex);
- return 0;
-}
-
-static pj_status_t rec_callback( /* in */ void *user_data,
- /* in */ pj_uint32_t timestamp,
- /* in */ const void *frame,
- /* in */ unsigned size)
-{
- pj_media_stream_t *channel = user_data;
- pj_status_t status = 0;
- struct pj_audio_frame frame_in, frame_out;
- int ts_len;
- void *rtphdr;
- int rtphdrlen;
- int sent;
-#if 0
- static FILE *fhnd = NULL;
-#endif
-
- PJ_UNUSED_ARG(timestamp)
-
- /* Start locking channel mutex */
- pj_mutex_lock (channel->mutex);
-
- if (!channel->codec) {
- status = -1;
- goto on_return;
- }
-
- /* Encode. */
- frame_in.type = PJ_MEDIA_TYPE_AUDIO;
- frame_in.buf = (void*)frame;
- frame_in.size = size;
- frame_out.buf = ((char*)channel->out_pkt) + sizeof(pj_rtp_hdr);
- status = channel->codec->op->encode (channel->codec, &frame_in,
- channel->out_pkt_size - sizeof(pj_rtp_hdr),
- &frame_out);
- if (status != 0) {
- PJ_LOG(3,(THISFILE, "Codec encode() has returned error status %d",
- status));
- goto on_return;
- }
-
- /* Encapsulate. */
- ts_len = size / (channel->snd_info.bits_per_sample / 8);
- status = pj_rtp_encode_rtp (&channel->rtp, channel->pt, 0,
- frame_out.size, ts_len,
- (const void**)&rtphdr, &rtphdrlen);
- if (status != 0) {
- PJ_LOG(3,(THISFILE, "RTP encode_rtp() has returned error status %d",
- status));
- goto on_return;
- }
-
- if (rtphdrlen != sizeof(pj_rtp_hdr)) {
- /* We don't support RTP with extended header yet. */
- PJ_TODO(SUPPORT_SENDING_RTP_WITH_EXTENDED_HEADER);
- PJ_LOG(3,(THISFILE, "Unsupported extended RTP header for transmission"));
- goto on_return;
- }
-
- pj_memcpy(channel->out_pkt, rtphdr, sizeof(pj_rtp_hdr));
-
- /* Send. */
- sent = pj_sock_sendto (channel->rtp_sock, channel->out_pkt, frame_out.size+sizeof(pj_rtp_hdr), 0,
- &channel->dst_addr, sizeof(channel->dst_addr));
- if (sent != (int)frame_out.size + (int)sizeof(pj_rtp_hdr)) {
- pj_perror(THISFILE, "Error sending RTP packet to %s:%d",
- pj_sockaddr_get_str_addr(&channel->dst_addr),
- pj_sockaddr_get_port(&channel->dst_addr));
- goto on_return;
- }
-
- /* Update stat */
- channel->stat.pkt_tx++;
- channel->stat.oct_tx += frame_out.size+sizeof(pj_rtp_hdr);
-
-#if 0
- if (fhnd == NULL) {
- fhnd = fopen("RTP.DAT", "wb");
- if (fhnd) {
- fwrite (channel->out_pkt, frame_out.size+sizeof(pj_rtp_hdr), 1, fhnd);
- fclose(fhnd);
- }
- }
-#endif
-
-on_return:
- pj_mutex_unlock (channel->mutex);
- return status;
-}
-
-
-static void* PJ_THREAD_FUNC stream_decoder_transport_thread (void*arg)
-{
- pj_media_stream_t *channel = arg;
-
- while (!channel->thread_quit_flag) {
- int len, size;
- const pj_rtp_hdr *hdr;
- const void *payload;
- unsigned payloadlen;
- int status;
- struct jb_frame *jb_frame;
-
- /* Wait for packet. */
- fd_set fds;
- pj_time_val timeout;
-
- PJ_FD_ZERO (&fds);
- PJ_FD_SET (channel->rtp_sock, &fds);
- timeout.sec = 0;
- timeout.msec = 100;
-
- /* Wait with timeout. */
- status = pj_sock_select(channel->rtp_sock, &fds, NULL, NULL, &timeout);
- if (status != 1)
- continue;
-
- /* Get packet from socket. */
- len = pj_sock_recv (channel->rtp_sock, channel->in_pkt, channel->in_pkt_size, 0);
- if (len < 1) {
- if (pj_getlasterror() == PJ_ECONNRESET) {
- /* On Win2K SP2 (or above) and WinXP, recv() will get WSAECONNRESET
- when the sending side receives ICMP port unreachable.
- */
- continue;
- }
- pj_perror(THISFILE, "Error receiving packet from socket (len=%d)", len);
- pj_thread_sleep(1);
- continue;
- }
-
- if (channel->state != STREAM_STARTED)
- continue;
-
- if (channel->thread_quit_flag)
- break;
-
- /* Start locking the channel. */
- pj_mutex_lock (channel->mutex);
-
- /* Update RTP and RTCP session. */
- status = pj_rtp_decode_rtp (&channel->rtp, channel->in_pkt, len, &hdr, &payload, &payloadlen);
- if (status != 0) {
- pj_mutex_unlock (channel->mutex);
- PJ_LOG(4,(THISFILE, "RTP decode_rtp() has returned error status %d", status));
- continue;
- }
- status = pj_rtp_session_update (&channel->rtp, hdr);
- if (status != 0 && status != PJ_RTP_ERR_SESSION_PROBATION && status != PJ_RTP_ERR_SESSION_RESTARTED) {
- pj_mutex_unlock (channel->mutex);
- PJ_LOG(4,(THISFILE, "RTP session_update() has returned error status %d", status));
- continue;
- }
- pj_rtcp_rx_rtp (channel->rtcp, pj_ntohs(hdr->seq), pj_ntohl(hdr->ts));
-
- /* Update stat */
- channel->stat.pkt_rx++;
- channel->stat.oct_rx += len;
-
- /* Copy to FIFO buffer. */
- size = payloadlen+sizeof(struct jb_frame);
- jb_frame = pj_fifobuf_alloc (&channel->fifobuf, size);
- if (jb_frame == NULL) {
- pj_mutex_unlock (channel->mutex);
- PJ_LOG(4,(THISFILE, "Unable to allocate %d bytes FIFO buffer", size));
- continue;
- }
-
- /* Copy the payload */
- jb_frame->size = payloadlen;
- jb_frame->buf = ((char*)jb_frame) + sizeof(struct jb_frame);
- pj_memcpy (jb_frame->buf, payload, payloadlen);
-
- /* Put to jitter buffer. */
- status = pj_jb_put (channel->jb, pj_ntohs(hdr->seq), jb_frame);
- if (status != 0) {
- pj_fifobuf_unalloc (&channel->fifobuf, jb_frame);
- pj_mutex_unlock (channel->mutex);
- PJ_LOG(4,(THISFILE, "Jitter buffer put() has returned error status %d", status));
- continue;
- }
-
- pj_mutex_unlock (channel->mutex);
- }
-
- return NULL;
-}
-
-static void init_snd_param_from_codec_attr (pj_snd_stream_info *param,
- const pj_codec_attr *attr)
-{
- param->bits_per_sample = attr->pcm_bits_per_sample;
- param->bytes_per_frame = 2;
- param->frames_per_packet = attr->sample_rate * attr->ptime / 1000;
- param->samples_per_frame = 1;
- param->samples_per_sec = attr->sample_rate;
-}
-
-static pj_media_stream_t *create_channel ( pj_pool_t *pool,
- pj_media_dir_t dir,
- pj_media_stream_t *peer,
- pj_codec_id *codec_id,
- pj_media_stream_create_param *param)
-{
- pj_media_stream_t *channel;
- pj_codec_attr codec_attr;
- void *ptr;
- unsigned size;
- int status;
-
- /* Allocate memory for channel descriptor */
- size = sizeof(pj_media_stream_t);
- channel = pj_pool_calloc(pool, 1, size);
- if (!channel) {
- PJ_LOG(1,(THISFILE, "Unable to allocate %u bytes channel descriptor",
- size));
- return NULL;
- }
-
- channel->dir = dir;
- channel->pt = codec_id->pt;
- channel->peer = peer;
- channel->codec_mgr = pj_med_mgr_get_codec_mgr (param->mediamgr);
- channel->rtp_sock = param->rtp_sock;
- channel->rtcp_sock = param->rtcp_sock;
- channel->dst_addr = *param->remote_addr;
- channel->state = STREAM_STOPPED;
-
- /* Create mutex for the channel. */
- channel->mutex = pj_mutex_create(pool, NULL, PJ_MUTEX_SIMPLE);
- if (channel->mutex == NULL)
- goto err_cleanup;
-
- /* Create and initialize codec, only if peer is not present.
- We only use one codec instance for both encoder and decoder.
- */
- if (peer && peer->codec) {
- channel->codec = peer->codec;
- status = channel->codec->factory->op->default_attr(channel->codec->factory, codec_id,
- &codec_attr);
- if (status != 0) {
- goto err_cleanup;
- }
-
- } else {
- channel->codec = pj_codec_mgr_alloc_codec(channel->codec_mgr, codec_id);
- if (channel->codec == NULL) {
- goto err_cleanup;
- }
-
- status = channel->codec->factory->op->default_attr(channel->codec->factory, codec_id,
- &codec_attr);
- if (status != 0) {
- goto err_cleanup;
- }
-
- codec_attr.pt = codec_id->pt;
- status = channel->codec->op->open(channel->codec, &codec_attr);
- if (status != 0) {
- goto err_cleanup;
- }
- }
-
- /* Allocate buffer for incoming packet. */
- channel->in_pkt_size = PJ_MAX_MTU;
- channel->in_pkt = pj_pool_alloc(pool, channel->in_pkt_size);
- if (!channel->in_pkt) {
- PJ_LOG(1, (THISFILE, "Unable to allocate %u bytes incoming packet buffer",
- channel->in_pkt_size));
- goto err_cleanup;
- }
-
- /* Allocate buffer for outgoing packet. */
- channel->out_pkt_size = sizeof(pj_rtp_hdr) +
- codec_attr.avg_bps / 8 * PJ_MAX_FRAME_DURATION_MS / 1000;
- if (channel->out_pkt_size > PJ_MAX_MTU)
- channel->out_pkt_size = PJ_MAX_MTU;
- channel->out_pkt = pj_pool_alloc(pool, channel->out_pkt_size);
- if (!channel->out_pkt) {
- PJ_LOG(1, (THISFILE, "Unable to allocate %u bytes encoding buffer",
- channel->out_pkt_size));
- goto err_cleanup;
- }
-
- /* Allocate buffer for decoding to PCM */
- channel->pcm_buf_size = codec_attr.sample_rate *
- codec_attr.pcm_bits_per_sample / 8 *
- PJ_MAX_FRAME_DURATION_MS / 1000;
- channel->pcm_buf = pj_pool_alloc (pool, channel->pcm_buf_size);
- if (!channel->pcm_buf) {
- PJ_LOG(1, (THISFILE, "Unable to allocate %u bytes PCM buffer",
- channel->pcm_buf_size));
- goto err_cleanup;
- }
-
- /* Allocate buffer for frames put in jitter buffer. */
- size = codec_attr.avg_bps / 8 * PJ_MAX_BUFFER_SIZE_MS / 1000;
- ptr = pj_pool_alloc(pool, size);
- if (!ptr) {
- PJ_LOG(1, (THISFILE, "Unable to allocate %u bytes jitter buffer",
- channel->pcm_buf_size));
- goto err_cleanup;
- }
- //pj_fifobuf_init (&channel->fifobuf, ptr, size);
-
- /* Create and initialize sound device */
- init_snd_param_from_codec_attr (&channel->snd_info, &codec_attr);
-
- if (dir == PJ_MEDIA_DIR_ENCODING)
- channel->snd_stream = pj_snd_open_recorder(-1, &channel->snd_info,
- &rec_callback, channel);
- else
- channel->snd_stream = pj_snd_open_player(-1, &channel->snd_info,
- &play_callback, channel);
-
- if (!channel->snd_stream)
- goto err_cleanup;
-
- /* Create RTP and RTCP sessions. */
- if (pj_rtp_session_init(&channel->rtp, codec_id->pt, param->ssrc) != 0) {
- PJ_LOG(1, (THISFILE, "RTP session initialization error"));
- goto err_cleanup;
- }
-
- /* For decoder, create RTCP session, jitter buffer, and transport thread. */
- if (dir == PJ_MEDIA_DIR_DECODING) {
- channel->rtcp = pj_pool_calloc(pool, 1, sizeof(pj_rtcp_session));
- if (!channel->rtcp) {
- PJ_LOG(1, (THISFILE, "Unable to allocate RTCP session"));
- goto err_cleanup;
- }
-
- pj_rtcp_init(channel->rtcp, param->ssrc);
-
- channel->jb = pj_pool_calloc(pool, 1, sizeof(pj_jitter_buffer));
- if (!channel->jb) {
- PJ_LOG(1, (THISFILE, "Unable to allocate jitter buffer descriptor"));
- goto err_cleanup;
- }
- if (pj_jb_init(channel->jb, pool, param->jb_min, param->jb_max, param->jb_maxcnt)) {
- PJ_LOG(1, (THISFILE, "Unable to allocate jitter buffer"));
- goto err_cleanup;
- }
-
- channel->transport_thread = pj_thread_create(pool, "decode",
- &stream_decoder_transport_thread, channel,
- 0, NULL, 0);
- if (!channel->transport_thread) {
- pj_perror(THISFILE, "Unable to create transport thread");
- goto err_cleanup;
- }
- }
-
- /* Done. */
- return channel;
-
-err_cleanup:
- pj_media_stream_destroy(channel);
- return NULL;
-}
-
-
-PJ_DEF(pj_status_t) pj_media_stream_create (pj_pool_t *pool,
- pj_media_stream_t **enc_stream,
- pj_media_stream_t **dec_stream,
- pj_media_stream_create_param *param)
-{
- *dec_stream = *enc_stream = NULL;
-
- if (param->dir & PJ_MEDIA_DIR_DECODING) {
- *dec_stream =
- create_channel(pool, PJ_MEDIA_DIR_DECODING, NULL, param->codec_id, param);
- if (!*dec_stream)
- return -1;
- }
-
- if (param->dir & PJ_MEDIA_DIR_ENCODING) {
- *enc_stream =
- create_channel(pool, PJ_MEDIA_DIR_ENCODING, *dec_stream, param->codec_id, param);
- if (!*enc_stream) {
- if (*dec_stream) {
- pj_media_stream_destroy(*dec_stream);
- *dec_stream = NULL;
- }
- return -1;
- }
-
- if (*dec_stream) {
- (*dec_stream)->peer = *enc_stream;
- }
- }
-
- return 0;
-}
-
-PJ_DEF(pj_status_t) pj_media_stream_start (pj_media_stream_t *channel)
-{
- pj_status_t status;
-
- status = pj_snd_stream_start(channel->snd_stream);
-
- if (status == 0)
- channel->state = STREAM_STARTED;
- return status;
-}
-
-PJ_DEF(pj_status_t) pj_media_stream_get_stat (const pj_media_stream_t *stream,
- pj_media_stream_stat *stat)
-{
- if (stream->dir == PJ_MEDIA_DIR_ENCODING) {
- pj_memcpy (stat, &stream->stat, sizeof(*stat));
- } else {
- pj_rtcp_pkt *rtcp_pkt;
- int len;
-
- pj_memset (stat, 0, sizeof(*stat));
- pj_assert (stream->rtcp != 0);
- pj_rtcp_build_rtcp (stream->rtcp, &rtcp_pkt, &len);
-
- stat->pkt_rx = stream->stat.pkt_rx;
- stat->oct_rx = stream->stat.oct_rx;
-
- PJ_TODO(SUPPORT_JITTER_CALCULATION_FOR_NON_8KHZ_SAMPLE_RATE)
- stat->jitter = pj_ntohl(rtcp_pkt->rr.jitter) / 8;
- stat->pkt_lost = (rtcp_pkt->rr.total_lost_2 << 16) +
- (rtcp_pkt->rr.total_lost_1 << 8) +
- rtcp_pkt->rr.total_lost_0;
- }
- return 0;
-}
-
-PJ_DEF(pj_status_t) pj_media_stream_pause (pj_media_stream_t *channel)
-{
- PJ_UNUSED_ARG(channel)
- return -1;
-}
-
-PJ_DEF(pj_status_t) pj_media_stream_resume (pj_media_stream_t *channel)
-{
- PJ_UNUSED_ARG(channel)
- return -1;
-}
-
-PJ_DEF(pj_status_t) pj_media_stream_destroy (pj_media_stream_t *channel)
-{
- channel->thread_quit_flag = 1;
-
- pj_mutex_lock (channel->mutex);
- if (channel->peer)
- pj_mutex_lock (channel->peer->mutex);
-
- if (channel->jb) {
- /* No need to deinitialize jitter buffer. */
- }
- if (channel->transport_thread) {
- pj_thread_join(channel->transport_thread);
- pj_thread_destroy(channel->transport_thread);
- channel->transport_thread = NULL;
- }
- if (channel->snd_stream != NULL) {
- pj_mutex_unlock (channel->mutex);
- pj_snd_stream_stop(channel->snd_stream);
- pj_mutex_lock (channel->mutex);
- pj_snd_stream_close(channel->snd_stream);
- channel->snd_stream = NULL;
- }
- if (channel->codec) {
- channel->codec->op->close(channel->codec);
- pj_codec_mgr_dealloc_codec(channel->codec_mgr, channel->codec);
- channel->codec = NULL;
- }
- if (channel->peer) {
- pj_media_stream_t *peer = channel->peer;
- peer->peer = NULL;
- peer->codec = NULL;
- peer->thread_quit_flag = 1;
- if (peer->transport_thread) {
- pj_mutex_unlock (peer->mutex);
- pj_thread_join(peer->transport_thread);
- pj_mutex_lock (peer->mutex);
- pj_thread_destroy(peer->transport_thread);
- peer->transport_thread = NULL;
- }
- if (peer->snd_stream) {
- pj_mutex_unlock (peer->mutex);
- pj_snd_stream_stop(peer->snd_stream);
- pj_mutex_lock (peer->mutex);
- pj_snd_stream_close(peer->snd_stream);
- peer->snd_stream = NULL;
- }
- }
-
- channel->state = STREAM_STOPPED;
-
- if (channel->peer)
- pj_mutex_unlock (channel->peer->mutex);
- pj_mutex_unlock(channel->mutex);
- pj_mutex_destroy(channel->mutex);
-
- return 0;
-}
-
+/* $Id$
+ *
+ */
+/*
+ * PJMEDIA - Multimedia over IP Stack
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ * Benny Prijono <bennylp@bulukucing.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <pjmedia/stream.h>
+#include <pjmedia/rtp.h>
+#include <pjmedia/rtcp.h>
+#include <pjmedia/jbuf.h>
+#include <pj/os.h>
+#include <pj/log.h>
+#include <pj/string.h> /* memcpy() */
+#include <pj/pool.h>
+#include <stdlib.h>
+
+#define THISFILE "stream.c"
+#define ERRLEVEL 1
+
+#define PJ_MAX_FRAME_DURATION_MS 200
+#define PJ_MAX_BUFFER_SIZE_MS 2000
+#define PJ_MAX_MTU 1500
+
+struct jb_frame
+{
+ unsigned size;
+ void *buf;
+};
+
+#define pj_fifobuf_alloc(fifo,size) malloc(size)
+#define pj_fifobuf_unalloc(fifo,buf) free(buf)
+#define pj_fifobuf_free(fifo, buf) free(buf)
+
+enum stream_state
+{
+ STREAM_STOPPED,
+ STREAM_STARTED,
+};
+
+struct pj_media_stream_t
+{
+ pj_media_dir_t dir;
+ int pt;
+ int state;
+ pj_media_stream_stat stat;
+ pj_media_stream_t *peer;
+ pj_snd_stream_info snd_info;
+ pj_snd_stream *snd_stream;
+ pj_mutex_t *mutex;
+ unsigned in_pkt_size;
+ void *in_pkt;
+ unsigned out_pkt_size;
+ void *out_pkt;
+ unsigned pcm_buf_size;
+ void *pcm_buf;
+ //pj_fifobuf_t fifobuf;
+ pj_codec_mgr *codec_mgr;
+ pj_codec *codec;
+ pj_rtp_session rtp;
+ pj_rtcp_session *rtcp;
+ pj_jitter_buffer *jb;
+ pj_sock_t rtp_sock;
+ pj_sock_t rtcp_sock;
+ pj_sockaddr_in dst_addr;
+ pj_thread_t *transport_thread;
+ int thread_quit_flag;
+};
+
+
+static pj_status_t play_callback(/* in */ void *user_data,
+ /* in */ pj_uint32_t timestamp,
+ /* out */ void *frame,
+ /*inout*/ unsigned size)
+{
+ pj_media_stream_t *channel = user_data;
+ struct jb_frame *jb_frame;
+ void *p;
+ pj_uint32_t extseq;
+ pj_status_t status;
+ struct pj_audio_frame frame_in, frame_out;
+
+ PJ_UNUSED_ARG(timestamp)
+
+ /* Lock mutex */
+ pj_mutex_lock (channel->mutex);
+
+ if (!channel->codec) {
+ pj_mutex_unlock (channel->mutex);
+ return -1;
+ }
+
+ /* Get frame from jitter buffer. */
+ status = pj_jb_get (channel->jb, &extseq, &p);
+ jb_frame = p;
+ if (status != 0 || jb_frame == NULL) {
+ pj_memset(frame, 0, size);
+ pj_mutex_unlock(channel->mutex);
+ return 0;
+ }
+
+ /* Decode */
+ frame_in.buf = jb_frame->buf;
+ frame_in.size = jb_frame->size;
+ frame_in.type = PJ_AUDIO_FRAME_AUDIO; /* ignored */
+ frame_out.buf = channel->pcm_buf;
+ status = channel->codec->op->decode (channel->codec, &frame_in,
+ channel->pcm_buf_size, &frame_out);
+ if (status != 0) {
+ PJ_LOG(3, (THISFILE, "decode() has return error status %d",
+ status));
+
+ pj_memset(frame, 0, size);
+ pj_fifobuf_free (&channel->fifobuf, jb_frame);
+ pj_mutex_unlock(channel->mutex);
+ return 0;
+ }
+
+ /* Put in sound buffer. */
+ if (frame_out.size > size) {
+ PJ_LOG(3, (THISFILE, "Sound playout buffer truncated %d bytes",
+ frame_out.size - size));
+ frame_out.size = size;
+ }
+
+ pj_memcpy(frame, frame_out.buf, size);
+
+ pj_fifobuf_free (&channel->fifobuf, jb_frame);
+ pj_mutex_unlock(channel->mutex);
+ return 0;
+}
+
+static pj_status_t rec_callback( /* in */ void *user_data,
+ /* in */ pj_uint32_t timestamp,
+ /* in */ const void *frame,
+ /* in */ unsigned size)
+{
+ pj_media_stream_t *channel = user_data;
+ pj_status_t status = 0;
+ struct pj_audio_frame frame_in, frame_out;
+ int ts_len;
+ void *rtphdr;
+ int rtphdrlen;
+ int sent;
+#if 0
+ static FILE *fhnd = NULL;
+#endif
+
+ PJ_UNUSED_ARG(timestamp)
+
+ /* Start locking channel mutex */
+ pj_mutex_lock (channel->mutex);
+
+ if (!channel->codec) {
+ status = -1;
+ goto on_return;
+ }
+
+ /* Encode. */
+ frame_in.type = PJ_MEDIA_TYPE_AUDIO;
+ frame_in.buf = (void*)frame;
+ frame_in.size = size;
+ frame_out.buf = ((char*)channel->out_pkt) + sizeof(pj_rtp_hdr);
+ status = channel->codec->op->encode (channel->codec, &frame_in,
+ channel->out_pkt_size - sizeof(pj_rtp_hdr),
+ &frame_out);
+ if (status != 0) {
+ PJ_LOG(3,(THISFILE, "Codec encode() has returned error status %d",
+ status));
+ goto on_return;
+ }
+
+ /* Encapsulate. */
+ ts_len = size / (channel->snd_info.bits_per_sample / 8);
+ status = pj_rtp_encode_rtp (&channel->rtp, channel->pt, 0,
+ frame_out.size, ts_len,
+ (const void**)&rtphdr, &rtphdrlen);
+ if (status != 0) {
+ PJ_LOG(3,(THISFILE, "RTP encode_rtp() has returned error status %d",
+ status));
+ goto on_return;
+ }
+
+ if (rtphdrlen != sizeof(pj_rtp_hdr)) {
+ /* We don't support RTP with extended header yet. */
+ PJ_TODO(SUPPORT_SENDING_RTP_WITH_EXTENDED_HEADER);
+ PJ_LOG(3,(THISFILE, "Unsupported extended RTP header for transmission"));
+ goto on_return;
+ }
+
+ pj_memcpy(channel->out_pkt, rtphdr, sizeof(pj_rtp_hdr));
+
+ /* Send. */
+ sent = pj_sock_sendto (channel->rtp_sock, channel->out_pkt, frame_out.size+sizeof(pj_rtp_hdr), 0,
+ &channel->dst_addr, sizeof(channel->dst_addr));
+ if (sent != (int)frame_out.size + (int)sizeof(pj_rtp_hdr)) {
+ pj_perror(THISFILE, "Error sending RTP packet to %s:%d",
+ pj_sockaddr_get_str_addr(&channel->dst_addr),
+ pj_sockaddr_get_port(&channel->dst_addr));
+ goto on_return;
+ }
+
+ /* Update stat */
+ channel->stat.pkt_tx++;
+ channel->stat.oct_tx += frame_out.size+sizeof(pj_rtp_hdr);
+
+#if 0
+ if (fhnd == NULL) {
+ fhnd = fopen("RTP.DAT", "wb");
+ if (fhnd) {
+ fwrite (channel->out_pkt, frame_out.size+sizeof(pj_rtp_hdr), 1, fhnd);
+ fclose(fhnd);
+ }
+ }
+#endif
+
+on_return:
+ pj_mutex_unlock (channel->mutex);
+ return status;
+}
+
+
+static void* PJ_THREAD_FUNC stream_decoder_transport_thread (void*arg)
+{
+ pj_media_stream_t *channel = arg;
+
+ while (!channel->thread_quit_flag) {
+ int len, size;
+ const pj_rtp_hdr *hdr;
+ const void *payload;
+ unsigned payloadlen;
+ int status;
+ struct jb_frame *jb_frame;
+
+ /* Wait for packet. */
+ fd_set fds;
+ pj_time_val timeout;
+
+ PJ_FD_ZERO (&fds);
+ PJ_FD_SET (channel->rtp_sock, &fds);
+ timeout.sec = 0;
+ timeout.msec = 100;
+
+ /* Wait with timeout. */
+ status = pj_sock_select(channel->rtp_sock, &fds, NULL, NULL, &timeout);
+ if (status != 1)
+ continue;
+
+ /* Get packet from socket. */
+ len = pj_sock_recv (channel->rtp_sock, channel->in_pkt, channel->in_pkt_size, 0);
+ if (len < 1) {
+ if (pj_getlasterror() == PJ_ECONNRESET) {
+ /* On Win2K SP2 (or above) and WinXP, recv() will get WSAECONNRESET
+ when the sending side receives ICMP port unreachable.
+ */
+ continue;
+ }
+ pj_perror(THISFILE, "Error receiving packet from socket (len=%d)", len);
+ pj_thread_sleep(1);
+ continue;
+ }
+
+ if (channel->state != STREAM_STARTED)
+ continue;
+
+ if (channel->thread_quit_flag)
+ break;
+
+ /* Start locking the channel. */
+ pj_mutex_lock (channel->mutex);
+
+ /* Update RTP and RTCP session. */
+ status = pj_rtp_decode_rtp (&channel->rtp, channel->in_pkt, len, &hdr, &payload, &payloadlen);
+ if (status != 0) {
+ pj_mutex_unlock (channel->mutex);
+ PJ_LOG(4,(THISFILE, "RTP decode_rtp() has returned error status %d", status));
+ continue;
+ }
+ status = pj_rtp_session_update (&channel->rtp, hdr);
+ if (status != 0 && status != PJ_RTP_ERR_SESSION_PROBATION && status != PJ_RTP_ERR_SESSION_RESTARTED) {
+ pj_mutex_unlock (channel->mutex);
+ PJ_LOG(4,(THISFILE, "RTP session_update() has returned error status %d", status));
+ continue;
+ }
+ pj_rtcp_rx_rtp (channel->rtcp, pj_ntohs(hdr->seq), pj_ntohl(hdr->ts));
+
+ /* Update stat */
+ channel->stat.pkt_rx++;
+ channel->stat.oct_rx += len;
+
+ /* Copy to FIFO buffer. */
+ size = payloadlen+sizeof(struct jb_frame);
+ jb_frame = pj_fifobuf_alloc (&channel->fifobuf, size);
+ if (jb_frame == NULL) {
+ pj_mutex_unlock (channel->mutex);
+ PJ_LOG(4,(THISFILE, "Unable to allocate %d bytes FIFO buffer", size));
+ continue;
+ }
+
+ /* Copy the payload */
+ jb_frame->size = payloadlen;
+ jb_frame->buf = ((char*)jb_frame) + sizeof(struct jb_frame);
+ pj_memcpy (jb_frame->buf, payload, payloadlen);
+
+ /* Put to jitter buffer. */
+ status = pj_jb_put (channel->jb, pj_ntohs(hdr->seq), jb_frame);
+ if (status != 0) {
+ pj_fifobuf_unalloc (&channel->fifobuf, jb_frame);
+ pj_mutex_unlock (channel->mutex);
+ PJ_LOG(4,(THISFILE, "Jitter buffer put() has returned error status %d", status));
+ continue;
+ }
+
+ pj_mutex_unlock (channel->mutex);
+ }
+
+ return NULL;
+}
+
+static void init_snd_param_from_codec_attr (pj_snd_stream_info *param,
+ const pj_codec_attr *attr)
+{
+ param->bits_per_sample = attr->pcm_bits_per_sample;
+ param->bytes_per_frame = 2;
+ param->frames_per_packet = attr->sample_rate * attr->ptime / 1000;
+ param->samples_per_frame = 1;
+ param->samples_per_sec = attr->sample_rate;
+}
+
+static pj_media_stream_t *create_channel ( pj_pool_t *pool,
+ pj_media_dir_t dir,
+ pj_media_stream_t *peer,
+ pj_codec_id *codec_id,
+ pj_media_stream_create_param *param)
+{
+ pj_media_stream_t *channel;
+ pj_codec_attr codec_attr;
+ void *ptr;
+ unsigned size;
+ int status;
+
+ /* Allocate memory for channel descriptor */
+ size = sizeof(pj_media_stream_t);
+ channel = pj_pool_calloc(pool, 1, size);
+ if (!channel) {
+ PJ_LOG(1,(THISFILE, "Unable to allocate %u bytes channel descriptor",
+ size));
+ return NULL;
+ }
+
+ channel->dir = dir;
+ channel->pt = codec_id->pt;
+ channel->peer = peer;
+ channel->codec_mgr = pj_med_mgr_get_codec_mgr (param->mediamgr);
+ channel->rtp_sock = param->rtp_sock;
+ channel->rtcp_sock = param->rtcp_sock;
+ channel->dst_addr = *param->remote_addr;
+ channel->state = STREAM_STOPPED;
+
+ /* Create mutex for the channel. */
+ channel->mutex = pj_mutex_create(pool, NULL, PJ_MUTEX_SIMPLE);
+ if (channel->mutex == NULL)
+ goto err_cleanup;
+
+ /* Create and initialize codec, only if peer is not present.
+ We only use one codec instance for both encoder and decoder.
+ */
+ if (peer && peer->codec) {
+ channel->codec = peer->codec;
+ status = channel->codec->factory->op->default_attr(channel->codec->factory, codec_id,
+ &codec_attr);
+ if (status != 0) {
+ goto err_cleanup;
+ }
+
+ } else {
+ channel->codec = pj_codec_mgr_alloc_codec(channel->codec_mgr, codec_id);
+ if (channel->codec == NULL) {
+ goto err_cleanup;
+ }
+
+ status = channel->codec->factory->op->default_attr(channel->codec->factory, codec_id,
+ &codec_attr);
+ if (status != 0) {
+ goto err_cleanup;
+ }
+
+ codec_attr.pt = codec_id->pt;
+ status = channel->codec->op->open(channel->codec, &codec_attr);
+ if (status != 0) {
+ goto err_cleanup;
+ }
+ }
+
+ /* Allocate buffer for incoming packet. */
+ channel->in_pkt_size = PJ_MAX_MTU;
+ channel->in_pkt = pj_pool_alloc(pool, channel->in_pkt_size);
+ if (!channel->in_pkt) {
+ PJ_LOG(1, (THISFILE, "Unable to allocate %u bytes incoming packet buffer",
+ channel->in_pkt_size));
+ goto err_cleanup;
+ }
+
+ /* Allocate buffer for outgoing packet. */
+ channel->out_pkt_size = sizeof(pj_rtp_hdr) +
+ codec_attr.avg_bps / 8 * PJ_MAX_FRAME_DURATION_MS / 1000;
+ if (channel->out_pkt_size > PJ_MAX_MTU)
+ channel->out_pkt_size = PJ_MAX_MTU;
+ channel->out_pkt = pj_pool_alloc(pool, channel->out_pkt_size);
+ if (!channel->out_pkt) {
+ PJ_LOG(1, (THISFILE, "Unable to allocate %u bytes encoding buffer",
+ channel->out_pkt_size));
+ goto err_cleanup;
+ }
+
+ /* Allocate buffer for decoding to PCM */
+ channel->pcm_buf_size = codec_attr.sample_rate *
+ codec_attr.pcm_bits_per_sample / 8 *
+ PJ_MAX_FRAME_DURATION_MS / 1000;
+ channel->pcm_buf = pj_pool_alloc (pool, channel->pcm_buf_size);
+ if (!channel->pcm_buf) {
+ PJ_LOG(1, (THISFILE, "Unable to allocate %u bytes PCM buffer",
+ channel->pcm_buf_size));
+ goto err_cleanup;
+ }
+
+ /* Allocate buffer for frames put in jitter buffer. */
+ size = codec_attr.avg_bps / 8 * PJ_MAX_BUFFER_SIZE_MS / 1000;
+ ptr = pj_pool_alloc(pool, size);
+ if (!ptr) {
+ PJ_LOG(1, (THISFILE, "Unable to allocate %u bytes jitter buffer",
+ channel->pcm_buf_size));
+ goto err_cleanup;
+ }
+ //pj_fifobuf_init (&channel->fifobuf, ptr, size);
+
+ /* Create and initialize sound device */
+ init_snd_param_from_codec_attr (&channel->snd_info, &codec_attr);
+
+ if (dir == PJ_MEDIA_DIR_ENCODING)
+ channel->snd_stream = pj_snd_open_recorder(-1, &channel->snd_info,
+ &rec_callback, channel);
+ else
+ channel->snd_stream = pj_snd_open_player(-1, &channel->snd_info,
+ &play_callback, channel);
+
+ if (!channel->snd_stream)
+ goto err_cleanup;
+
+ /* Create RTP and RTCP sessions. */
+ if (pj_rtp_session_init(&channel->rtp, codec_id->pt, param->ssrc) != 0) {
+ PJ_LOG(1, (THISFILE, "RTP session initialization error"));
+ goto err_cleanup;
+ }
+
+ /* For decoder, create RTCP session, jitter buffer, and transport thread. */
+ if (dir == PJ_MEDIA_DIR_DECODING) {
+ channel->rtcp = pj_pool_calloc(pool, 1, sizeof(pj_rtcp_session));
+ if (!channel->rtcp) {
+ PJ_LOG(1, (THISFILE, "Unable to allocate RTCP session"));
+ goto err_cleanup;
+ }
+
+ pj_rtcp_init(channel->rtcp, param->ssrc);
+
+ channel->jb = pj_pool_calloc(pool, 1, sizeof(pj_jitter_buffer));
+ if (!channel->jb) {
+ PJ_LOG(1, (THISFILE, "Unable to allocate jitter buffer descriptor"));
+ goto err_cleanup;
+ }
+ if (pj_jb_init(channel->jb, pool, param->jb_min, param->jb_max, param->jb_maxcnt)) {
+ PJ_LOG(1, (THISFILE, "Unable to allocate jitter buffer"));
+ goto err_cleanup;
+ }
+
+ channel->transport_thread = pj_thread_create(pool, "decode",
+ &stream_decoder_transport_thread, channel,
+ 0, NULL, 0);
+ if (!channel->transport_thread) {
+ pj_perror(THISFILE, "Unable to create transport thread");
+ goto err_cleanup;
+ }
+ }
+
+ /* Done. */
+ return channel;
+
+err_cleanup:
+ pj_media_stream_destroy(channel);
+ return NULL;
+}
+
+
+PJ_DEF(pj_status_t) pj_media_stream_create (pj_pool_t *pool,
+ pj_media_stream_t **enc_stream,
+ pj_media_stream_t **dec_stream,
+ pj_media_stream_create_param *param)
+{
+ *dec_stream = *enc_stream = NULL;
+
+ if (param->dir & PJ_MEDIA_DIR_DECODING) {
+ *dec_stream =
+ create_channel(pool, PJ_MEDIA_DIR_DECODING, NULL, param->codec_id, param);
+ if (!*dec_stream)
+ return -1;
+ }
+
+ if (param->dir & PJ_MEDIA_DIR_ENCODING) {
+ *enc_stream =
+ create_channel(pool, PJ_MEDIA_DIR_ENCODING, *dec_stream, param->codec_id, param);
+ if (!*enc_stream) {
+ if (*dec_stream) {
+ pj_media_stream_destroy(*dec_stream);
+ *dec_stream = NULL;
+ }
+ return -1;
+ }
+
+ if (*dec_stream) {
+ (*dec_stream)->peer = *enc_stream;
+ }
+ }
+
+ return 0;
+}
+
+PJ_DEF(pj_status_t) pj_media_stream_start (pj_media_stream_t *channel)
+{
+ pj_status_t status;
+
+ status = pj_snd_stream_start(channel->snd_stream);
+
+ if (status == 0)
+ channel->state = STREAM_STARTED;
+ return status;
+}
+
+PJ_DEF(pj_status_t) pj_media_stream_get_stat (const pj_media_stream_t *stream,
+ pj_media_stream_stat *stat)
+{
+ if (stream->dir == PJ_MEDIA_DIR_ENCODING) {
+ pj_memcpy (stat, &stream->stat, sizeof(*stat));
+ } else {
+ pj_rtcp_pkt *rtcp_pkt;
+ int len;
+
+ pj_memset (stat, 0, sizeof(*stat));
+ pj_assert (stream->rtcp != 0);
+ pj_rtcp_build_rtcp (stream->rtcp, &rtcp_pkt, &len);
+
+ stat->pkt_rx = stream->stat.pkt_rx;
+ stat->oct_rx = stream->stat.oct_rx;
+
+ PJ_TODO(SUPPORT_JITTER_CALCULATION_FOR_NON_8KHZ_SAMPLE_RATE)
+ stat->jitter = pj_ntohl(rtcp_pkt->rr.jitter) / 8;
+ stat->pkt_lost = (rtcp_pkt->rr.total_lost_2 << 16) +
+ (rtcp_pkt->rr.total_lost_1 << 8) +
+ rtcp_pkt->rr.total_lost_0;
+ }
+ return 0;
+}
+
+PJ_DEF(pj_status_t) pj_media_stream_pause (pj_media_stream_t *channel)
+{
+ PJ_UNUSED_ARG(channel)
+ return -1;
+}
+
+PJ_DEF(pj_status_t) pj_media_stream_resume (pj_media_stream_t *channel)
+{
+ PJ_UNUSED_ARG(channel)
+ return -1;
+}
+
+PJ_DEF(pj_status_t) pj_media_stream_destroy (pj_media_stream_t *channel)
+{
+ channel->thread_quit_flag = 1;
+
+ pj_mutex_lock (channel->mutex);
+ if (channel->peer)
+ pj_mutex_lock (channel->peer->mutex);
+
+ if (channel->jb) {
+ /* No need to deinitialize jitter buffer. */
+ }
+ if (channel->transport_thread) {
+ pj_thread_join(channel->transport_thread);
+ pj_thread_destroy(channel->transport_thread);
+ channel->transport_thread = NULL;
+ }
+ if (channel->snd_stream != NULL) {
+ pj_mutex_unlock (channel->mutex);
+ pj_snd_stream_stop(channel->snd_stream);
+ pj_mutex_lock (channel->mutex);
+ pj_snd_stream_close(channel->snd_stream);
+ channel->snd_stream = NULL;
+ }
+ if (channel->codec) {
+ channel->codec->op->close(channel->codec);
+ pj_codec_mgr_dealloc_codec(channel->codec_mgr, channel->codec);
+ channel->codec = NULL;
+ }
+ if (channel->peer) {
+ pj_media_stream_t *peer = channel->peer;
+ peer->peer = NULL;
+ peer->codec = NULL;
+ peer->thread_quit_flag = 1;
+ if (peer->transport_thread) {
+ pj_mutex_unlock (peer->mutex);
+ pj_thread_join(peer->transport_thread);
+ pj_mutex_lock (peer->mutex);
+ pj_thread_destroy(peer->transport_thread);
+ peer->transport_thread = NULL;
+ }
+ if (peer->snd_stream) {
+ pj_mutex_unlock (peer->mutex);
+ pj_snd_stream_stop(peer->snd_stream);
+ pj_mutex_lock (peer->mutex);
+ pj_snd_stream_close(peer->snd_stream);
+ peer->snd_stream = NULL;
+ }
+ }
+
+ channel->state = STREAM_STOPPED;
+
+ if (channel->peer)
+ pj_mutex_unlock (channel->peer->mutex);
+ pj_mutex_unlock(channel->mutex);
+ pj_mutex_destroy(channel->mutex);
+
+ return 0;
+}
+
diff --git a/pjmedia/src/pjmedia/stream.h b/pjmedia/src/pjmedia/stream.h
index d9f38a0..a9d43a2 100644
--- a/pjmedia/src/pjmedia/stream.h
+++ b/pjmedia/src/pjmedia/stream.h
@@ -1,85 +1,106 @@
-/* $Id$
- *
- */
-
-#ifndef __PJMEDIA_STREAM_H__
-#define __PJMEDIA_STREAM_H__
-
-
-/**
- * @file stream.h
- * @brief Stream of media.
- */
-
-#include <pjmedia/sound.h>
-#include <pjmedia/codec.h>
-#include <pjmedia/mediamgr.h>
-#include <pj/sock.h>
-
-PJ_BEGIN_DECL
-
-
-/**
- * @defgroup PJMED_SES Media session
- * @ingroup PJMEDIA
- * @{
- */
-
-typedef struct pj_media_stream_t pj_media_stream_t;
-
-/** Parameter for creating channel. */
-typedef struct pj_media_stream_create_param
-{
- /** Codec ID, must NOT be NULL. */
- pj_codec_id *codec_id;
-
- /** Media manager, must NOT be NULL. */
- pj_med_mgr_t *mediamgr;
-
- /** Direction: IN_OUT, or IN only, or OUT only. */
- pj_media_dir_t dir;
-
- /** RTP socket. */
- pj_sock_t rtp_sock;
-
- /** RTCP socket. */
- pj_sock_t rtcp_sock;
-
- /** Address of remote */
- pj_sockaddr_in *remote_addr;
-
- /** RTP SSRC */
- pj_uint32_t ssrc;
-
- /** Jitter buffer parameters. */
- int jb_min, jb_max, jb_maxcnt;
-
-} pj_media_stream_create_param;
-
-typedef struct pj_media_stream_stat
-{
- pj_uint32_t pkt_tx, pkt_rx; /* packets transmitted/received */
- pj_uint32_t oct_tx, oct_rx; /* octets transmitted/received */
- pj_uint32_t jitter; /* receive jitter in ms */
- pj_uint32_t pkt_lost; /* total packet lost count */
-} pj_media_stream_stat;
-
-PJ_DECL(pj_status_t) pj_media_stream_create (pj_pool_t *pool,
- pj_media_stream_t **enc_stream,
- pj_media_stream_t **dec_stream,
- pj_media_stream_create_param *param);
-PJ_DECL(pj_status_t) pj_media_stream_start (pj_media_stream_t *stream);
-PJ_DECL(pj_status_t) pj_media_stream_get_stat (const pj_media_stream_t *stream,
- pj_media_stream_stat *stat);
-PJ_DECL(pj_status_t) pj_media_stream_pause (pj_media_stream_t *stream);
-PJ_DECL(pj_status_t) pj_media_stream_resume (pj_media_stream_t *stream);
-PJ_DECL(pj_status_t) pj_media_stream_destroy (pj_media_stream_t *stream);
-
-/**
- * @}
- */
-
-PJ_END_DECL
-
-
-#endif /* __PJMEDIA_STREAM_H__ */
+/* $Id$
+ *
+ */
+/*
+ * PJMEDIA - Multimedia over IP Stack
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ * Benny Prijono <bennylp@bulukucing.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef __PJMEDIA_STREAM_H__
+#define __PJMEDIA_STREAM_H__
+
+
+/**
+ * @file stream.h
+ * @brief Stream of media.
+ */
+
+#include <pjmedia/sound.h>
+#include <pjmedia/codec.h>
+#include <pjmedia/mediamgr.h>
+#include <pj/sock.h>
+
+PJ_BEGIN_DECL
+
+
+/**
+ * @defgroup PJMED_SES Media session
+ * @ingroup PJMEDIA
+ * @{
+ */
+
+typedef struct pj_media_stream_t pj_media_stream_t;
+
+/** Parameter for creating channel. */
+typedef struct pj_media_stream_create_param
+{
+ /** Codec ID, must NOT be NULL. */
+ pj_codec_id *codec_id;
+
+ /** Media manager, must NOT be NULL. */
+ pj_med_mgr_t *mediamgr;
+
+ /** Direction: IN_OUT, or IN only, or OUT only. */
+ pj_media_dir_t dir;
+
+ /** RTP socket. */
+ pj_sock_t rtp_sock;
+
+ /** RTCP socket. */
+ pj_sock_t rtcp_sock;
+
+ /** Address of remote */
+ pj_sockaddr_in *remote_addr;
+
+ /** RTP SSRC */
+ pj_uint32_t ssrc;
+
+ /** Jitter buffer parameters. */
+ int jb_min, jb_max, jb_maxcnt;
+
+} pj_media_stream_create_param;
+
+typedef struct pj_media_stream_stat
+{
+ pj_uint32_t pkt_tx, pkt_rx; /* packets transmitted/received */
+ pj_uint32_t oct_tx, oct_rx; /* octets transmitted/received */
+ pj_uint32_t jitter; /* receive jitter in ms */
+ pj_uint32_t pkt_lost; /* total packet lost count */
+} pj_media_stream_stat;
+
+PJ_DECL(pj_status_t) pj_media_stream_create (pj_pool_t *pool,
+ pj_media_stream_t **enc_stream,
+ pj_media_stream_t **dec_stream,
+ pj_media_stream_create_param *param);
+PJ_DECL(pj_status_t) pj_media_stream_start (pj_media_stream_t *stream);
+PJ_DECL(pj_status_t) pj_media_stream_get_stat (const pj_media_stream_t *stream,
+ pj_media_stream_stat *stat);
+PJ_DECL(pj_status_t) pj_media_stream_pause (pj_media_stream_t *stream);
+PJ_DECL(pj_status_t) pj_media_stream_resume (pj_media_stream_t *stream);
+PJ_DECL(pj_status_t) pj_media_stream_destroy (pj_media_stream_t *stream);
+
+/**
+ * @}
+ */
+
+PJ_END_DECL
+
+
+#endif /* __PJMEDIA_STREAM_H__ */
diff --git a/pjmedia/src/test/audio_tool.c b/pjmedia/src/test/audio_tool.c
index 8282258..5e046b8 100644
--- a/pjmedia/src/test/audio_tool.c
+++ b/pjmedia/src/test/audio_tool.c
@@ -1,394 +1,415 @@
-/* $Id$
- *
- */
-#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$
+ *
+ */
+/*
+ * PJMEDIA - Multimedia over IP Stack
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ * Benny Prijono <bennylp@bulukucing.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; 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 d2ca95a..e5c5eea 100644
--- a/pjmedia/src/test/jbuf_test.c
+++ b/pjmedia/src/test/jbuf_test.c
@@ -1,139 +1,160 @@
-/* $Id$
- *
- */
-#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$
+ *
+ */
+/*
+ * PJMEDIA - Multimedia over IP Stack
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ * Benny Prijono <bennylp@bulukucing.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; 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 c6aeb9b..06eedf7 100644
--- a/pjmedia/src/test/main.c
+++ b/pjmedia/src/test/main.c
@@ -1,27 +1,48 @@
-/* $Id$
- *
- */
-#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$
+ *
+ */
+/*
+ * PJMEDIA - Multimedia over IP Stack
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ * Benny Prijono <bennylp@bulukucing.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; 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 702c6f3..d88d63a 100644
--- a/pjmedia/src/test/rtp_test.c
+++ b/pjmedia/src/test/rtp_test.c
@@ -1,22 +1,43 @@
-/* $Id$
- *
- */
-#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$
+ *
+ */
+/*
+ * PJMEDIA - Multimedia over IP Stack
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ * Benny Prijono <bennylp@bulukucing.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; 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 1b80e51..07c9a06 100644
--- a/pjmedia/src/test/sdptest.c
+++ b/pjmedia/src/test/sdptest.c
@@ -1,106 +1,127 @@
-/* $Id$
- *
- */
-#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$
+ *
+ */
+/*
+ * PJMEDIA - Multimedia over IP Stack
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ * Benny Prijono <bennylp@bulukucing.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; 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 cf13ada..cfb1ee2 100644
--- a/pjmedia/src/test/session_test.c
+++ b/pjmedia/src/test/session_test.c
@@ -1,115 +1,136 @@
-/* $Id$
- *
- */
-#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$
+ *
+ */
+/*
+ * PJMEDIA - Multimedia over IP Stack
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ * Benny Prijono <bennylp@bulukucing.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; 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 91b5365..6cfee4e 100644
--- a/pjsip/include/pjsip-simple/event_notify.h
+++ b/pjsip/include/pjsip-simple/event_notify.h
@@ -1,315 +1,337 @@
-/* $Id$
- *
- */
-#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$
+ *
+ */
+/*
+ * PJSIP - SIP Stack
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ * Benny Prijono <bennylp@bulukucing.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; 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 ab66a05..c88bab6 100644
--- a/pjsip/include/pjsip-simple/event_notify_msg.h
+++ b/pjsip/include/pjsip-simple/event_notify_msg.h
@@ -1,102 +1,124 @@
-/* $Id$
- *
- */
-#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$
+ *
+ */
+/*
+ * PJSIP - SIP Stack
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ * Benny Prijono <bennylp@bulukucing.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; 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 ef7484b..22ef467 100644
--- a/pjsip/include/pjsip-simple/messaging.h
+++ b/pjsip/include/pjsip-simple/messaging.h
@@ -1,253 +1,275 @@
-/* $Id$
- *
- */
-#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$
+ *
+ */
+/*
+ * PJSIP - SIP Stack
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ * Benny Prijono <bennylp@bulukucing.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; 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 a6caeb6..f95bdc8 100644
--- a/pjsip/include/pjsip-simple/pidf.h
+++ b/pjsip/include/pjsip-simple/pidf.h
@@ -1,161 +1,183 @@
-/* $Id$
- *
- */
-#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$
+ *
+ */
+/*
+ * PJSIP - SIP Stack
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ * Benny Prijono <bennylp@bulukucing.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; 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 80ed93c..b694d85 100644
--- a/pjsip/include/pjsip-simple/presence.h
+++ b/pjsip/include/pjsip-simple/presence.h
@@ -1,214 +1,236 @@
-/* $Id$
- *
- */
-#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$
+ *
+ */
+/*
+ * PJSIP - SIP Stack
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ * Benny Prijono <bennylp@bulukucing.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; 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 27254fa..4ca1d70 100644
--- a/pjsip/include/pjsip-simple/xpidf.h
+++ b/pjsip/include/pjsip-simple/xpidf.h
@@ -1,118 +1,140 @@
-/* $Id$
- *
- */
-#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$
+ *
+ */
+/*
+ * PJSIP - SIP Stack
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ * Benny Prijono <bennylp@bulukucing.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; 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 14f0159..07e98f5 100644
--- a/pjsip/include/pjsip-ua/sip_dialog.h
+++ b/pjsip/include/pjsip-ua/sip_dialog.h
@@ -1,620 +1,642 @@
-/* $Id$
- *
- */
-#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$
+ *
+ */
+/*
+ * PJSIP - SIP Stack
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ * Benny Prijono <bennylp@bulukucing.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; 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 7570c73..0f019e5 100644
--- a/pjsip/include/pjsip-ua/sip_regc.h
+++ b/pjsip/include/pjsip-ua/sip_regc.h
@@ -1,195 +1,217 @@
-/* $Id$
- *
- */
-#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$
+ *
+ */
+/*
+ * PJSIP - SIP Stack
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ * Benny Prijono <bennylp@bulukucing.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; 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 87e6f3a..307bdc4 100644
--- a/pjsip/include/pjsip-ua/sip_ua.h
+++ b/pjsip/include/pjsip-ua/sip_ua.h
@@ -1,82 +1,104 @@
-/* $Id$
- *
- */
-#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$
+ *
+ */
+/*
+ * PJSIP - SIP Stack
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ * Benny Prijono <bennylp@bulukucing.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; 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 b23d45f..0683eec 100644
--- a/pjsip/include/pjsip/print_util.h
+++ b/pjsip/include/pjsip/print_util.h
@@ -1,99 +1,121 @@
-/* $Id$
- */
-#ifndef __PJSIP_PRINT_H__
-#define __PJSIP_PRINT_H__
-
-#define copy_advance_check(buf,str) \
- do { \
- if ((str).slen+10 >= (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+10 >= (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+10 >= (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+10 >= (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_no_check(buf,str) \
- pj_memcpy(buf, (str).ptr, (str).slen); \
- buf += (str).slen;
-
-#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$
+ */
+/*
+ * PJSIP - SIP Stack
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ * Benny Prijono <bennylp@bulukucing.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; 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__
+
+#define copy_advance_check(buf,str) \
+ do { \
+ if ((str).slen+10 >= (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+10 >= (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+10 >= (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+10 >= (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_no_check(buf,str) \
+ pj_memcpy(buf, (str).ptr, (str).slen); \
+ buf += (str).slen;
+
+#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 c34ca03..71bbfd6 100644
--- a/pjsip/include/pjsip/sip_auth.h
+++ b/pjsip/include/pjsip/sip_auth.h
@@ -1,231 +1,253 @@
-/* $Id$
- */
-#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$
+ */
+/*
+ * PJSIP - SIP Stack
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ * Benny Prijono <bennylp@bulukucing.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; 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 8e68331..e2e703b 100644
--- a/pjsip/include/pjsip/sip_auth_msg.h
+++ b/pjsip/include/pjsip/sip_auth_msg.h
@@ -1,193 +1,215 @@
-/* $Id$
- */
-#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$
+ */
+/*
+ * PJSIP - SIP Stack
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ * Benny Prijono <bennylp@bulukucing.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; 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__ */
diff --git a/pjsip/include/pjsip/sip_auth_parser.h b/pjsip/include/pjsip/sip_auth_parser.h
index c1d4f23..5794818 100644
--- a/pjsip/include/pjsip/sip_auth_parser.h
+++ b/pjsip/include/pjsip/sip_auth_parser.h
@@ -1,71 +1,93 @@
-/* $Id$
- */
-#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
+/* $Id$
+ */
+/*
+ * PJSIP - SIP Stack
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ * Benny Prijono <bennylp@bulukucing.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; 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__ */
-
+ * @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 bba037c..10a6e25 100644
--- a/pjsip/include/pjsip/sip_config.h
+++ b/pjsip/include/pjsip/sip_config.h
@@ -1,138 +1,160 @@
-/* $Id$
- */
-#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
-
-/* 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$
+ */
+/*
+ * PJSIP - SIP Stack
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ * Benny Prijono <bennylp@bulukucing.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; 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
+
+/* 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 e67e286..d789672 100644
--- a/pjsip/include/pjsip/sip_endpoint.h
+++ b/pjsip/include/pjsip/sip_endpoint.h
@@ -1,79 +1,101 @@
-/* $Id$
- */
-#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.
+/* $Id$
+ */
+/*
+ * PJSIP - SIP Stack
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ * Benny Prijono <bennylp@bulukucing.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; 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.
+ * 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.
+ * 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.
- *
+ * 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.
- */
+ * @param endpt Pointer to receive endpoint instance.
+ *
+ * @return PJ_SUCCESS on success.
+ */
PJ_DECL(pj_status_t) pjsip_endpt_create(pj_pool_factory *pf,
- pjsip_endpoint **endpt);
+ 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);
+/**
+ * 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.
@@ -84,276 +106,276 @@
* 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.
- *
+
+/**
+ * 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.
- */
+ *
+ * @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.
- *
+ 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.
- */
+ *
+ * @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);
-
-/**
- * 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(void) pjsip_endpt_get_transport( pjsip_endpoint *endpt,
- pj_pool_t *pool,
- pjsip_transport_type_e type,
- const pj_sockaddr_in *remote,
- void *token,
- pjsip_transport_completion_callback *cb);
-
-/**
- * Create listener a new transport listener. A listener is transport object
- * that is capable of receiving SIP messages. For UDP listener, normally
- * application should use #pjsip_endpt_create_udp_listener instead if the
- * application has already created the socket.
- * This function, like all other endpoint functions, is thread safe.
- *
- * @param endpt The endpoint instance.
- * @param type Transport type (eg. UDP, TCP, etc.)
- * @param addr The bound address of the transport.
- * @param addr_name The address to be advertised in SIP messages. For example,
- * the bound address can be 0.0.0.0, but the advertised address
- * normally will be the IP address of the host.
- *
- * @return Zero if listener is created successfully.
- */
-PJ_DECL(pj_status_t) pjsip_endpt_create_listener( pjsip_endpoint *endpt,
- pjsip_transport_type_e type,
- pj_sockaddr_in *addr,
- const pj_sockaddr_in *addr_name);
-
-/**
- * Create UDP listener. For UDP, normally the application would create the
- * socket by itself (for STUN purpose), then it can register the socket as
- * listener by calling this function.
- * This function, like all other endpoint functions, is thread safe.
- *
- * @param endpt The endpoint instance.
- * @param sock The socket handle.
- * @param addr_name The address to be advertised in SIP message. If the socket
- * has been resolved with STUN, then application may specify
- * the mapped address in this parameter.
- *
- * @return Zero if listener is created successfully.
- */
-PJ_DECL(pj_status_t) pjsip_endpt_create_udp_listener( pjsip_endpoint *endpt,
- pj_sock_t sock,
- const pj_sockaddr_in *addr_name);
-
-/**
- * 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 );
+ 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);
+
+/**
+ * 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(void) pjsip_endpt_get_transport( pjsip_endpoint *endpt,
+ pj_pool_t *pool,
+ pjsip_transport_type_e type,
+ const pj_sockaddr_in *remote,
+ void *token,
+ pjsip_transport_completion_callback *cb);
+
+/**
+ * Create listener a new transport listener. A listener is transport object
+ * that is capable of receiving SIP messages. For UDP listener, normally
+ * application should use #pjsip_endpt_create_udp_listener instead if the
+ * application has already created the socket.
+ * This function, like all other endpoint functions, is thread safe.
+ *
+ * @param endpt The endpoint instance.
+ * @param type Transport type (eg. UDP, TCP, etc.)
+ * @param addr The bound address of the transport.
+ * @param addr_name The address to be advertised in SIP messages. For example,
+ * the bound address can be 0.0.0.0, but the advertised address
+ * normally will be the IP address of the host.
+ *
+ * @return Zero if listener is created successfully.
+ */
+PJ_DECL(pj_status_t) pjsip_endpt_create_listener( pjsip_endpoint *endpt,
+ pjsip_transport_type_e type,
+ pj_sockaddr_in *addr,
+ const pj_sockaddr_in *addr_name);
+
+/**
+ * Create UDP listener. For UDP, normally the application would create the
+ * socket by itself (for STUN purpose), then it can register the socket as
+ * listener by calling this function.
+ * This function, like all other endpoint functions, is thread safe.
+ *
+ * @param endpt The endpoint instance.
+ * @param sock The socket handle.
+ * @param addr_name The address to be advertised in SIP message. If the socket
+ * has been resolved with STUN, then application may specify
+ * the mapped address in this parameter.
+ *
+ * @return Zero if listener is created successfully.
+ */
+PJ_DECL(pj_status_t) pjsip_endpt_create_udp_listener( pjsip_endpoint *endpt,
+ pj_sock_t sock,
+ const pj_sockaddr_in *addr_name);
+
+/**
+ * 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.
@@ -372,21 +394,21 @@
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__ */
-
+
+/**
+ * @}
+ */
+
+/*
+ * 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 6e4a6f7..6fca735 100644
--- a/pjsip/include/pjsip/sip_errno.h
+++ b/pjsip/include/pjsip/sip_errno.h
@@ -1,5 +1,27 @@
/* $Id$
*/
+/*
+ * PJSIP - SIP Stack
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ * Benny Prijono <bennylp@bulukucing.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; 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__
diff --git a/pjsip/include/pjsip/sip_event.h b/pjsip/include/pjsip/sip_event.h
index 98ac385..2b0cc04 100644
--- a/pjsip/include/pjsip/sip_event.h
+++ b/pjsip/include/pjsip/sip_event.h
@@ -1,85 +1,107 @@
-/* $Id$
- */
-#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;
+/* $Id$
+ */
+/*
+ * PJSIP - SIP Stack
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ * Benny Prijono <bennylp@bulukucing.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; 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.
@@ -174,7 +196,7 @@
} user;
} body;
-};
+};
/**
* Init timer event.
@@ -277,18 +299,18 @@
(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_misc.c
- */
-PJ_DEF(const char *) pjsip_event_str(pjsip_event_id_e e);
-
-/**
- * @}
- */
-
-PJ_END_DECL
-
-#endif /* __PJSIP_SIP_EVENT_H__ */
+
+/**
+ * Get the event string from the event ID.
+ * @param e the event ID.
+ * @notes defined in sip_misc.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_misc.h b/pjsip/include/pjsip/sip_misc.h
index 4101dbc..d81af9c 100644
--- a/pjsip/include/pjsip/sip_misc.h
+++ b/pjsip/include/pjsip/sip_misc.h
@@ -1,176 +1,198 @@
-/* $Id$
- */
-#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).
+/* $Id$
+ */
+/*
+ * PJSIP - SIP Stack
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ * Benny Prijono <bennylp@bulukucing.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; 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,
+ *
+ * @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_t *tr,
- const pjsip_via_hdr *via,
- pjsip_host_port *addr);
-
-/**
- * @}
- */
-
-PJ_END_DECL
-
-#endif /* __PJSIP_SIP_MISC_H__ */
-
+ 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_t *tr,
+ const pjsip_via_hdr *via,
+ pjsip_host_port *addr);
+
+/**
+ * @}
+ */
+
+PJ_END_DECL
+
+#endif /* __PJSIP_SIP_MISC_H__ */
+
diff --git a/pjsip/include/pjsip/sip_module.h b/pjsip/include/pjsip/sip_module.h
index 7bf53ab..aecc1d4 100644
--- a/pjsip/include/pjsip/sip_module.h
+++ b/pjsip/include/pjsip/sip_module.h
@@ -1,124 +1,146 @@
-/* $Id$
- */
-#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$
+ */
+/*
+ * PJSIP - SIP Stack
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ * Benny Prijono <bennylp@bulukucing.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; 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 ed9450b..6b78462 100644
--- a/pjsip/include/pjsip/sip_msg.h
+++ b/pjsip/include/pjsip/sip_msg.h
@@ -1,1511 +1,1533 @@
-/* $Id$
- */
-#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$
+ */
+/*
+ * PJSIP - SIP Stack
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ * Benny Prijono <bennylp@bulukucing.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; 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__ */
+
diff --git a/pjsip/include/pjsip/sip_msg_i.h b/pjsip/include/pjsip/sip_msg_i.h
index 7aa5095..11a584f 100644
--- a/pjsip/include/pjsip/sip_msg_i.h
+++ b/pjsip/include/pjsip/sip_msg_i.h
@@ -1,14 +1,36 @@
-/* $Id$
- *
- */
-
-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$
+ *
+ */
+/*
+ * PJSIP - SIP Stack
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ * Benny Prijono <bennylp@bulukucing.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; 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 5ae2f80..fd5ae37 100644
--- a/pjsip/include/pjsip/sip_parser.h
+++ b/pjsip/include/pjsip/sip_parser.h
@@ -1,62 +1,84 @@
-/* $Id$
- */
-#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;
-
+/* $Id$
+ */
+/*
+ * PJSIP - SIP Stack
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ * Benny Prijono <bennylp@bulukucing.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; 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.
@@ -68,130 +90,130 @@
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);
-
+
+/**
+ * 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
@@ -213,123 +235,123 @@
*/
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.
- */
+
+/**
+ * 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_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_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 in URL. */
-
-/*
- * 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__ */
-
+ 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_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 in URL. */
+
+/*
+ * 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__ */
+
diff --git a/pjsip/include/pjsip/sip_private.h b/pjsip/include/pjsip/sip_private.h
index b266135..813b76b 100644
--- a/pjsip/include/pjsip/sip_private.h
+++ b/pjsip/include/pjsip/sip_private.h
@@ -1,85 +1,107 @@
-/* $Id$
- */
-#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
- * @{
- */
-
-
-/**
- * Create a new transport manager.
- * @param pool The pool
- * @param endpt The endpoint
- * @param cb Callback to be called to receive messages from transport.
- */
-PJ_DECL(pj_status_t) pjsip_transport_mgr_create( pj_pool_t *pool,
- pjsip_endpoint *endpt,
+/* $Id$
+ */
+/*
+ * PJSIP - SIP Stack
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ * Benny Prijono <bennylp@bulukucing.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; 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
+ * @{
+ */
+
+
+/**
+ * Create a new transport manager.
+ * @param pool The pool
+ * @param endpt The endpoint
+ * @param cb Callback to be called to receive messages from transport.
+ */
+PJ_DECL(pj_status_t) pjsip_transport_mgr_create( pj_pool_t *pool,
+ pjsip_endpoint *endpt,
void (*cb)(pjsip_endpoint *,
pjsip_rx_data *),
- pjsip_transport_mgr **);
-
-
-/**
- * Destroy transport manager and release all transports.
- * @param mgr Transport manager to be destroyed.
- */
-PJ_DECL(pj_status_t) pjsip_transport_mgr_destroy( pjsip_transport_mgr *mgr );
-
-/**
- * Poll for transport events.
- * Incoming messages will be parsed by the transport manager, and the callback
- * will be called for each of this message.
- * @param endpt The endpoint.
- * @param timeout Timeout value, or NULL to wait forever.
- */
-PJ_DECL(int) pjsip_transport_mgr_handle_events( pjsip_transport_mgr *mgr,
- const pj_time_val *timeout );
-
-/**
- * Get the pointer to the first transport iterator.
- * @param mgr The transport manager.
- * @param it The iterator used for iterating the hash element.
- * @return the iterator to the first transport, or NULL.
- */
-PJ_DECL(pj_hash_iterator_t*) pjsip_transport_first( pjsip_transport_mgr *mgr,
- pj_hash_iterator_t *it );
-
-
-/**
- * Get the next transport iterator.
- * @param itr the iterator to the transport.
- * @return the iterator pointed to the next transport, or NULL.
- */
-PJ_DECL(pj_hash_iterator_t*) pjsip_transport_next( pjsip_transport_mgr *mgr,
- pj_hash_iterator_t *itr );
-
-/**
- * Get the value of transport iterator.
- * @param mgr the transport manager.
- * @param itr the transport iterator.
- * @return the transport associated with the iterator.
- */
-PJ_DECL(pjsip_transport_t*) pjsip_transport_this( pjsip_transport_mgr *mgr,
- pj_hash_iterator_t *itr );
-
-/**
- * @}
- */
-
-PJ_END_DECL
-
-#endif /* __PJSIP_PRIVATE_I_H__ */
-
+ pjsip_transport_mgr **);
+
+
+/**
+ * Destroy transport manager and release all transports.
+ * @param mgr Transport manager to be destroyed.
+ */
+PJ_DECL(pj_status_t) pjsip_transport_mgr_destroy( pjsip_transport_mgr *mgr );
+
+/**
+ * Poll for transport events.
+ * Incoming messages will be parsed by the transport manager, and the callback
+ * will be called for each of this message.
+ * @param endpt The endpoint.
+ * @param timeout Timeout value, or NULL to wait forever.
+ */
+PJ_DECL(int) pjsip_transport_mgr_handle_events( pjsip_transport_mgr *mgr,
+ const pj_time_val *timeout );
+
+/**
+ * Get the pointer to the first transport iterator.
+ * @param mgr The transport manager.
+ * @param it The iterator used for iterating the hash element.
+ * @return the iterator to the first transport, or NULL.
+ */
+PJ_DECL(pj_hash_iterator_t*) pjsip_transport_first( pjsip_transport_mgr *mgr,
+ pj_hash_iterator_t *it );
+
+
+/**
+ * Get the next transport iterator.
+ * @param itr the iterator to the transport.
+ * @return the iterator pointed to the next transport, or NULL.
+ */
+PJ_DECL(pj_hash_iterator_t*) pjsip_transport_next( pjsip_transport_mgr *mgr,
+ pj_hash_iterator_t *itr );
+
+/**
+ * Get the value of transport iterator.
+ * @param mgr the transport manager.
+ * @param itr the transport iterator.
+ * @return the transport associated with the iterator.
+ */
+PJ_DECL(pjsip_transport_t*) pjsip_transport_this( pjsip_transport_mgr *mgr,
+ pj_hash_iterator_t *itr );
+
+/**
+ * @}
+ */
+
+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 5464db9..15e09bb 100644
--- a/pjsip/include/pjsip/sip_resolve.h
+++ b/pjsip/include/pjsip/sip_resolve.h
@@ -1,104 +1,126 @@
-/* $Id$
- */
-#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$
+ */
+/*
+ * PJSIP - SIP Stack
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ * Benny Prijono <bennylp@bulukucing.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; 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 cdb1123..2b7c364 100644
--- a/pjsip/include/pjsip/sip_transaction.h
+++ b/pjsip/include/pjsip/sip_transaction.h
@@ -1,63 +1,85 @@
-/* $Id$
- */
-#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
- * @{
- */
+/* $Id$
+ */
+/*
+ * PJSIP - SIP Stack
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ * Benny Prijono <bennylp@bulukucing.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; 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__
-/* 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
+/**
+ * @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
@@ -85,17 +107,17 @@
int handle_ack; /**< Should we handle ACK? */
/** Handler according to current state. */
- pj_status_t (*state_handler)(struct pjsip_transaction *, pjsip_event *);
+ 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_t *transport; /**< Transport to use. */
+ pjsip_transport_t *transport; /**< Transport to use. */
/*
* Messages and timer.
@@ -104,16 +126,16 @@
int has_unsent_msg; /**< Non-zero if tsx need to
transmit msg once resolver
completes. */
- int retransmit_count;/**< Retransmission count. */
+ 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];
-};
-
-
-/**
+ /** Module specific data. */
+ void *module_data[PJSIP_MAX_MODULE];
+};
+
+
+/**
* 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
@@ -129,110 +151,110 @@
* 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 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);
-
-/**
+ *
+ * @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 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);
-
-/**
+ *
+ * @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);
-
-/**
+ *
+ * @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
+ *
+ * @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);
-
-/**
+ *
+ * @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
+ *
+ * @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 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 );
+ */
+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__ */
-
+
+/**
+ * @}
+ */
+
+/*
+ * 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 dd3baea..abe71bf 100644
--- a/pjsip/include/pjsip/sip_transport.h
+++ b/pjsip/include/pjsip/sip_transport.h
@@ -1,89 +1,111 @@
-/* $Id$
- */
-#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>
+/* $Id$
+ */
+/*
+ * PJSIP - SIP Stack
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ * Benny Prijono <bennylp@bulukucing.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; 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>
-
-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.
- *
- * @{
- */
-
-/**
- * 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
-{
- //PJ_DECL_LIST_MEMBER(struct pjsip_rx_data);
-
- /** Memory pool for this buffer. */
- pj_pool_t *pool;
+
+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.
+ *
+ * @{
+ */
+
+/**
+ * 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
+{
+ //PJ_DECL_LIST_MEMBER(struct pjsip_rx_data);
+
+ /** Memory pool for this buffer. */
+ pj_pool_t *pool;
/** Ioqueue op key. */
pj_ioqueue_op_key_t op_key;
-
- /** Time when the message was received. */
- pj_time_val timestamp;
-
- /** The packet buffer. */
- char packet[PJSIP_MAX_PKT_LEN];
-
- /** 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;
-
- /** The transport object which received this packet. */
- pjsip_transport_t *transport;
-
- /** The parsed message, if any. */
- pjsip_msg *msg;
-
- /** This the transaction key generated from the message. This key is only
- * available after the rdata has reached the endpoint.
- */
- pj_str_t key;
-
- /** 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;
+
+ /** Time when the message was received. */
+ pj_time_val timestamp;
+
+ /** The packet buffer. */
+ char packet[PJSIP_MAX_PKT_LEN];
+
+ /** 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;
+
+ /** The transport object which received this packet. */
+ pjsip_transport_t *transport;
+
+ /** The parsed message, if any. */
+ pjsip_msg *msg;
+
+ /** This the transaction key generated from the message. This key is only
+ * available after the rdata has reached the endpoint.
+ */
+ pj_str_t key;
+
+ /** 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;
@@ -103,378 +125,378 @@
/** 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;
-};
-
-
-/**
- * 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
-{
- 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_transport_mgr *mgr;
+ /** The list of error generated by the parser when parsing this message. */
+ pjsip_parser_err_report parse_err;
+};
+
+
+/**
+ * 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
+{
+ 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_transport_mgr *mgr;
/** Ioqueue asynchronous operation key. */
pj_ioqueue_op_key_t op_key;
-
- /** 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;
-};
-
-
-/**
- * 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 );
-
-/**
- * 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 );
-
-
-/**
- * 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_IOQUEUE_BUSY = 4, /**< WTH?? */
-};
-
-/**
- * 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 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);
-
-
-/**
- * Add reference to transport.
- * Transactions or dialogs that uses a particular transport must call this
- * function to indicate that the transport is being used, thus preventing the
- * transport from being closed.
- *
- * @param transport The transport.
- */
-PJ_DECL(void)
-pjsip_transport_add_ref( pjsip_transport_t *transport );
-
-/**
- * Decrease reference to transport.
- * When the transport reference counter becomes zero, a timer will be started
- * and when this timer expires and the reference counter is still zero, the
- * transport will be released.
- *
- * @param transport The transport
- */
-PJ_DECL(void)
-pjsip_transport_dec_ref( pjsip_transport_t *transport );
-
-
-/**
- * Macro to check whether the transport is reliable.
- *
- * @param transport The transport
- *
- * @return non-zero (not necessarily 1) if transport is reliable.
- */
-#define PJSIP_TRANSPORT_IS_RELIABLE(transport) \
- (pjsip_transport_get_flag(transport) & PJSIP_TRANSPORT_RELIABLE)
-
-
-/**
- * Macro to check whether the transport is secure.
- *
- * @param transport The transport
- *
- * @return non-zero (not necessarily one) if transport is secure.
- */
-#define PJSIP_TRANSPORT_IS_SECURE(transport) \
- (pjsip_transport_get_flag(transport) & PJSIP_TRANSPORT_SECURE)
-
-/**
- * Get the transport type.
- *
- * @param tr The transport.
- *
- * @return Transport type.
- */
-PJ_DECL(pjsip_transport_type_e)
-pjsip_transport_get_type( const pjsip_transport_t * tr);
-
-/**
- * Get the transport type name (ie "UDP", or "TCP").
- *
- * @param tr The transport.
- * @return The string type.
- */
-PJ_DECL(const char *)
-pjsip_transport_get_type_name( const pjsip_transport_t * tr);
-
-/**
- * Get the transport's object name.
- *
- * @param tr The transport.
- * @return The object name.
- */
-PJ_DECL(const char*)
-pjsip_transport_get_obj_name( const pjsip_transport_t *tr );
-
-/**
- * Get the transport's reference counter.
- *
- * @param tr The transport.
- * @return The reference count value.
- */
-PJ_DECL(int)
-pjsip_transport_get_ref_cnt( const pjsip_transport_t *tr );
-
-/**
- * Get transport flag.
- *
- * @param tr The transport.
- * @return Transport flag.
- */
-PJ_DECL(unsigned)
-pjsip_transport_get_flag( const pjsip_transport_t * tr );
-
-/**
- * Get the local address of the transport, ie. the address which the socket
- * is bound.
- *
- * @param tr The transport.
- * @return The address.
- */
-PJ_DECL(const pj_sockaddr_in *)
-pjsip_transport_get_local_addr( pjsip_transport_t * tr );
-
-/**
- * Get the address name of the transport. Address name can be an arbitrary
- * address assigned to a transport. This is usefull for example when STUN
- * is used, then the address name of an UDP transport can specify the public
- * address of the transport. When the address name is not set, then value
- * will be equal to the local/bound address. Application should normally
- * prefer to use the address name instead of the local address.
- *
- * @param tr The transport.
- * @return The address name.
- */
-PJ_DECL(const pj_sockaddr_in*)
-pjsip_transport_get_addr_name (pjsip_transport_t *tr);
-
-/**
- * Get the remote address of the transport. Not all transports will have
- * a valid remote address. UDP transports, for example, will likely to have
- * zero has their remote address, because UDP transport can be used to send
- * and receive messages from multiple destinations.
- *
- * @param tr The transport.
- * @return The address.
- */
-PJ_DECL(const pj_sockaddr_in *)
-pjsip_transport_get_remote_addr( const pjsip_transport_t * tr );
-
-/**
- * Send a SIP message using the specified transport, to the address specified
- * in the outgoing data. This function is only usefull for application when it
- * wants to handle the message statelessly, because otherwise it should create
- * a transaction and let the transaction handles the transmission of the
- * message.
- *
- * This function will send the message immediately, so application must be
- * sure that the transport is ready to do so before calling this function.
- *
- * @param tr The transport to send the message.
- * @param tdata The outgoing message buffer.
+
+ /** 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;
+};
+
+
+/**
+ * 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 );
+
+/**
+ * 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 );
+
+
+/**
+ * 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_IOQUEUE_BUSY = 4, /**< WTH?? */
+};
+
+/**
+ * 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 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);
+
+
+/**
+ * Add reference to transport.
+ * Transactions or dialogs that uses a particular transport must call this
+ * function to indicate that the transport is being used, thus preventing the
+ * transport from being closed.
+ *
+ * @param transport The transport.
+ */
+PJ_DECL(void)
+pjsip_transport_add_ref( pjsip_transport_t *transport );
+
+/**
+ * Decrease reference to transport.
+ * When the transport reference counter becomes zero, a timer will be started
+ * and when this timer expires and the reference counter is still zero, the
+ * transport will be released.
+ *
+ * @param transport The transport
+ */
+PJ_DECL(void)
+pjsip_transport_dec_ref( pjsip_transport_t *transport );
+
+
+/**
+ * Macro to check whether the transport is reliable.
+ *
+ * @param transport The transport
+ *
+ * @return non-zero (not necessarily 1) if transport is reliable.
+ */
+#define PJSIP_TRANSPORT_IS_RELIABLE(transport) \
+ (pjsip_transport_get_flag(transport) & PJSIP_TRANSPORT_RELIABLE)
+
+
+/**
+ * Macro to check whether the transport is secure.
+ *
+ * @param transport The transport
+ *
+ * @return non-zero (not necessarily one) if transport is secure.
+ */
+#define PJSIP_TRANSPORT_IS_SECURE(transport) \
+ (pjsip_transport_get_flag(transport) & PJSIP_TRANSPORT_SECURE)
+
+/**
+ * Get the transport type.
+ *
+ * @param tr The transport.
+ *
+ * @return Transport type.
+ */
+PJ_DECL(pjsip_transport_type_e)
+pjsip_transport_get_type( const pjsip_transport_t * tr);
+
+/**
+ * Get the transport type name (ie "UDP", or "TCP").
+ *
+ * @param tr The transport.
+ * @return The string type.
+ */
+PJ_DECL(const char *)
+pjsip_transport_get_type_name( const pjsip_transport_t * tr);
+
+/**
+ * Get the transport's object name.
+ *
+ * @param tr The transport.
+ * @return The object name.
+ */
+PJ_DECL(const char*)
+pjsip_transport_get_obj_name( const pjsip_transport_t *tr );
+
+/**
+ * Get the transport's reference counter.
+ *
+ * @param tr The transport.
+ * @return The reference count value.
+ */
+PJ_DECL(int)
+pjsip_transport_get_ref_cnt( const pjsip_transport_t *tr );
+
+/**
+ * Get transport flag.
+ *
+ * @param tr The transport.
+ * @return Transport flag.
+ */
+PJ_DECL(unsigned)
+pjsip_transport_get_flag( const pjsip_transport_t * tr );
+
+/**
+ * Get the local address of the transport, ie. the address which the socket
+ * is bound.
+ *
+ * @param tr The transport.
+ * @return The address.
+ */
+PJ_DECL(const pj_sockaddr_in *)
+pjsip_transport_get_local_addr( pjsip_transport_t * tr );
+
+/**
+ * Get the address name of the transport. Address name can be an arbitrary
+ * address assigned to a transport. This is usefull for example when STUN
+ * is used, then the address name of an UDP transport can specify the public
+ * address of the transport. When the address name is not set, then value
+ * will be equal to the local/bound address. Application should normally
+ * prefer to use the address name instead of the local address.
+ *
+ * @param tr The transport.
+ * @return The address name.
+ */
+PJ_DECL(const pj_sockaddr_in*)
+pjsip_transport_get_addr_name (pjsip_transport_t *tr);
+
+/**
+ * Get the remote address of the transport. Not all transports will have
+ * a valid remote address. UDP transports, for example, will likely to have
+ * zero has their remote address, because UDP transport can be used to send
+ * and receive messages from multiple destinations.
+ *
+ * @param tr The transport.
+ * @return The address.
+ */
+PJ_DECL(const pj_sockaddr_in *)
+pjsip_transport_get_remote_addr( const pjsip_transport_t * tr );
+
+/**
+ * Send a SIP message using the specified transport, to the address specified
+ * in the outgoing data. This function is only usefull for application when it
+ * wants to handle the message statelessly, because otherwise it should create
+ * a transaction and let the transaction handles the transmission of the
+ * message.
+ *
+ * This function will send the message immediately, so application must be
+ * sure that the transport is ready to do so before calling this function.
+ *
+ * @param tr The transport to send the message.
+ * @param tdata The outgoing message buffer.
* @param addr The remote address.
* @param sent If not null, it will be filled up with the length of
- * data sent.
- *
- * @return PJ_SUCCESS on success, or the appropriate error code.
- */
-PJ_DECL(pj_status_t) pjsip_transport_send_msg( pjsip_transport_t *tr,
- pjsip_tx_data *tdata,
+ * data sent.
+ *
+ * @return PJ_SUCCESS on success, or the appropriate error code.
+ */
+PJ_DECL(pj_status_t) pjsip_transport_send_msg( pjsip_transport_t *tr,
+ pjsip_tx_data *tdata,
const pj_sockaddr_in *addr,
- pj_ssize_t *sent);
-
-
-/**
- * @}
- */
-
-/*
- * PRIVATE FUNCTIONS!!!
- *
- * These functions are normally to be used by endpoint. Application should
- * use the variant provided by the endpoint instance.
- *
- * Application normally wouldn't be able to call these functions because it
- * has no reference of the transport manager (the instance of the transport
- * manager is hidden by endpoint!).
- */
-
-/*
- * Create a new transmit buffer.
- *
- * @param mgr The transport manager.
- * @return The transmit buffer data, or NULL on error.
- */
+ pj_ssize_t *sent);
+
+
+/**
+ * @}
+ */
+
+/*
+ * PRIVATE FUNCTIONS!!!
+ *
+ * These functions are normally to be used by endpoint. Application should
+ * use the variant provided by the endpoint instance.
+ *
+ * Application normally wouldn't be able to call these functions because it
+ * has no reference of the transport manager (the instance of the transport
+ * manager is hidden by endpoint!).
+ */
+
+/*
+ * Create a new transmit buffer.
+ *
+ * @param mgr The transport manager.
+ * @return The transmit buffer data, or NULL on error.
+ */
pj_status_t pjsip_tx_data_create( pjsip_transport_mgr *mgr,
- pjsip_tx_data **tdata );
-
-
-/**
- * Create listener.
- *
- * @param mgr The transport manager.
- * @param type Transport type.
- * @param local_addr The address to bind.
- * @param addr_name If not null, sets the address name. If NULL,
- * then the local address will be used.
- *
- * @return PJ_SUCCESS if listener was successfully created.
- */
-PJ_DECL(pj_status_t) pjsip_create_listener( pjsip_transport_mgr *mgr,
- pjsip_transport_type_e type,
- pj_sockaddr_in *local_addr,
- const pj_sockaddr_in *addr_name);
-
-
-/**
- * Create UDP listener.
- *
- * @param mgr The transport manager.
- * @param sock The UDP socket.
- * @param addr_name If not null, sets the address name. If NULL,
- * then the local address will be used.
- *
- * @return PJ_SUCCESS if listener was successfully created.
- */
-PJ_DECL(pj_status_t) pjsip_create_udp_listener( pjsip_transport_mgr *mgr,
- pj_sock_t sock,
- const pj_sockaddr_in *addr_name);
-
-/**
- * Type of function to receive asynchronous transport completion for
- * pjsip_transport_get() operation.
- *
- * @param tr The transport.
- * @param token Token registered previously.
- * @param status Status of operation.
- */
-typedef void pjsip_transport_completion_callback(pjsip_transport_t *tr,
- void *token,
- pj_status_t status);
-
-/**
- * Find transport to be used to send message to remote destination. If no
- * suitable transport is found, a new one will be created. If transport
- * can not be available immediately (for example, an outgoing TCP connec()),
- * then the caller will be notified later via the callback.
- *
- * @param mgr The transport manager.
- * @param pool Pool to allocate asychronous job (if required).
- * @param type The transport type.
- * @param remote The remote address.
- * @param token The token that will be passed to the callback.
- * @param cb The callback to be called to report the completion of
- * the operation.
- */
-PJ_DECL(void) pjsip_transport_get( pjsip_transport_mgr *mgr,
- pj_pool_t *pool,
- pjsip_transport_type_e type,
- const pj_sockaddr_in *remote,
- void *token,
- pjsip_transport_completion_callback *cb);
-
-PJ_END_DECL
-
-#endif /* __PJSIP_SIP_TRANSPORT_H__ */
-
+ pjsip_tx_data **tdata );
+
+
+/**
+ * Create listener.
+ *
+ * @param mgr The transport manager.
+ * @param type Transport type.
+ * @param local_addr The address to bind.
+ * @param addr_name If not null, sets the address name. If NULL,
+ * then the local address will be used.
+ *
+ * @return PJ_SUCCESS if listener was successfully created.
+ */
+PJ_DECL(pj_status_t) pjsip_create_listener( pjsip_transport_mgr *mgr,
+ pjsip_transport_type_e type,
+ pj_sockaddr_in *local_addr,
+ const pj_sockaddr_in *addr_name);
+
+
+/**
+ * Create UDP listener.
+ *
+ * @param mgr The transport manager.
+ * @param sock The UDP socket.
+ * @param addr_name If not null, sets the address name. If NULL,
+ * then the local address will be used.
+ *
+ * @return PJ_SUCCESS if listener was successfully created.
+ */
+PJ_DECL(pj_status_t) pjsip_create_udp_listener( pjsip_transport_mgr *mgr,
+ pj_sock_t sock,
+ const pj_sockaddr_in *addr_name);
+
+/**
+ * Type of function to receive asynchronous transport completion for
+ * pjsip_transport_get() operation.
+ *
+ * @param tr The transport.
+ * @param token Token registered previously.
+ * @param status Status of operation.
+ */
+typedef void pjsip_transport_completion_callback(pjsip_transport_t *tr,
+ void *token,
+ pj_status_t status);
+
+/**
+ * Find transport to be used to send message to remote destination. If no
+ * suitable transport is found, a new one will be created. If transport
+ * can not be available immediately (for example, an outgoing TCP connec()),
+ * then the caller will be notified later via the callback.
+ *
+ * @param mgr The transport manager.
+ * @param pool Pool to allocate asychronous job (if required).
+ * @param type The transport type.
+ * @param remote The remote address.
+ * @param token The token that will be passed to the callback.
+ * @param cb The callback to be called to report the completion of
+ * the operation.
+ */
+PJ_DECL(void) pjsip_transport_get( pjsip_transport_mgr *mgr,
+ pj_pool_t *pool,
+ pjsip_transport_type_e type,
+ const pj_sockaddr_in *remote,
+ void *token,
+ pjsip_transport_completion_callback *cb);
+
+PJ_END_DECL
+
+#endif /* __PJSIP_SIP_TRANSPORT_H__ */
+
diff --git a/pjsip/include/pjsip/sip_types.h b/pjsip/include/pjsip/sip_types.h
index 64f5f3a..0ba3a47 100644
--- a/pjsip/include/pjsip/sip_types.h
+++ b/pjsip/include/pjsip/sip_types.h
@@ -1,141 +1,163 @@
-/* $Id$
- */
-#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_t pjsip_transport_t;
-
-/**
- * Opaque data type for transport manager (sip_transport.h).
- */
-typedef struct pjsip_transport_mgr pjsip_transport_mgr;
-
-/**
- * Transport types.
- */
-typedef enum pjsip_transport_type_e
-{
- /** Unspecified. */
- PJSIP_TRANSPORT_UNSPECIFIED,
-
- /** UDP. */
- PJSIP_TRANSPORT_UDP,
-
-#if PJ_HAS_TCP
- /** TCP. */
- PJSIP_TRANSPORT_TCP,
-
- /** TLS. */
- PJSIP_TRANSPORT_TLS,
-
- /** SCTP. */
- PJSIP_TRANSPORT_SCTP,
-#endif
-
-} 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;
+/* $Id$
+ */
+/*
+ * PJSIP - SIP Stack
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ * Benny Prijono <bennylp@bulukucing.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; 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_t pjsip_transport_t;
+
+/**
+ * Opaque data type for transport manager (sip_transport.h).
+ */
+typedef struct pjsip_transport_mgr pjsip_transport_mgr;
+
+/**
+ * Transport types.
+ */
+typedef enum pjsip_transport_type_e
+{
+ /** Unspecified. */
+ PJSIP_TRANSPORT_UNSPECIFIED,
+
+ /** UDP. */
+ PJSIP_TRANSPORT_UDP,
+
+#if PJ_HAS_TCP
+ /** TCP. */
+ PJSIP_TRANSPORT_TCP,
+
+ /** TLS. */
+ PJSIP_TRANSPORT_TLS,
+
+ /** SCTP. */
+ PJSIP_TRANSPORT_SCTP,
+#endif
+
+} 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;
+
+/**
+ * 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;
/**
@@ -156,6 +178,6 @@
* Attributes to inform that the function may throw exceptions.
*/
#define PJSIP_THROW_SPEC(list)
-
-#endif /* __PJSIP_SIP_TYPES_H__ */
-
+
+#endif /* __PJSIP_SIP_TYPES_H__ */
+
diff --git a/pjsip/include/pjsip/sip_uri.h b/pjsip/include/pjsip/sip_uri.h
index 8d72d0a..24b2edd 100644
--- a/pjsip/include/pjsip/sip_uri.h
+++ b/pjsip/include/pjsip/sip_uri.h
@@ -1,294 +1,316 @@
-/* $Id$
- */
-#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>
-
-PJ_BEGIN_DECL
-
-
-/**
- * @defgroup PJSIP_URL URL Structures
- * @brief SIP Url, tel: Url, and generic URI.
- * @ingroup PJSIP_MSG
- * @{
- */
-
-/**
- * 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 zero if equal.
- */
- int (*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 */
- pj_str_t other_param; /**< Other parameters grouped together. */
- pj_str_t 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 Zero if equal.
- */
-PJ_INLINE(int) 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$
+ */
+/*
+ * PJSIP - SIP Stack
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ * Benny Prijono <bennylp@bulukucing.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; 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>
+
+PJ_BEGIN_DECL
+
+
+/**
+ * @defgroup PJSIP_URL URL Structures
+ * @brief SIP Url, tel: Url, and generic URI.
+ * @ingroup PJSIP_MSG
+ * @{
+ */
+
+/**
+ * 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 zero if equal.
+ */
+ int (*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 */
+ pj_str_t other_param; /**< Other parameters grouped together. */
+ pj_str_t 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 Zero if equal.
+ */
+PJ_INLINE(int) 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_auth.h b/pjsip/include/pjsip_auth.h
index 9c0d33b..a538bb9 100644
--- a/pjsip/include/pjsip_auth.h
+++ b/pjsip/include/pjsip_auth.h
@@ -1,22 +1,44 @@
-/* $Id$
- *
- */
-#ifndef __PJSIP_AUTH_H__
-#define __PJSIP_AUTH_H__
-
-/**
- * @defgroup PJSIP_AUTH SIP Authorization module
- */
-
-/**
- * @file pjsip_auth.h
- * @brief SIP Authorization Module.
- */
-
-
-#include <pjsip_auth/sip_auth.h>
-#include <pjsip_auth/sip_auth_msg.h>
-#include <pjsip_auth/sip_auth_parser.h>
-
-#endif /* __PJSIP_AUTH_H__ */
-
+/* $Id$
+ *
+ */
+/*
+ * PJSIP - SIP Stack
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ * Benny Prijono <bennylp@bulukucing.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+#ifndef __PJSIP_AUTH_H__
+#define __PJSIP_AUTH_H__
+
+/**
+ * @defgroup PJSIP_AUTH SIP Authorization module
+ */
+
+/**
+ * @file pjsip_auth.h
+ * @brief SIP Authorization Module.
+ */
+
+
+#include <pjsip_auth/sip_auth.h>
+#include <pjsip_auth/sip_auth_msg.h>
+#include <pjsip_auth/sip_auth_parser.h>
+
+#endif /* __PJSIP_AUTH_H__ */
+
diff --git a/pjsip/include/pjsip_core.h b/pjsip/include/pjsip_core.h
index 96b3948..8b4f6b9 100644
--- a/pjsip/include/pjsip_core.h
+++ b/pjsip/include/pjsip_core.h
@@ -1,20 +1,42 @@
-/* $Id$
- */
-#ifndef __PJSIP_CORE_H__
-#define __PJSIP_CORE_H__
-
-#include <pjsip/sip_types.h>
-#include <pjsip/sip_endpoint.h>
-#include <pjsip/sip_event.h>
-#include <pjsip/sip_misc.h>
-#include <pjsip/sip_module.h>
-#include <pjsip/sip_msg.h>
-#include <pjsip/sip_parser.h>
-#include <pjsip/sip_resolve.h>
-#include <pjsip/sip_transaction.h>
-#include <pjsip/sip_transport.h>
-#include <pjsip/sip_uri.h>
-#include <pjsip/sip_auth.h>
-
-#endif /* __PJSIP_CORE_H__ */
-
+/* $Id$
+ */
+/*
+ * PJSIP - SIP Stack
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ * Benny Prijono <bennylp@bulukucing.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+#ifndef __PJSIP_CORE_H__
+#define __PJSIP_CORE_H__
+
+#include <pjsip/sip_types.h>
+#include <pjsip/sip_endpoint.h>
+#include <pjsip/sip_event.h>
+#include <pjsip/sip_misc.h>
+#include <pjsip/sip_module.h>
+#include <pjsip/sip_msg.h>
+#include <pjsip/sip_parser.h>
+#include <pjsip/sip_resolve.h>
+#include <pjsip/sip_transaction.h>
+#include <pjsip/sip_transport.h>
+#include <pjsip/sip_uri.h>
+#include <pjsip/sip_auth.h>
+
+#endif /* __PJSIP_CORE_H__ */
+
diff --git a/pjsip/include/pjsip_simple.h b/pjsip/include/pjsip_simple.h
index 3ede039..15c2001 100644
--- a/pjsip/include/pjsip_simple.h
+++ b/pjsip/include/pjsip_simple.h
@@ -1,26 +1,48 @@
-/* $Id$
- *
- */
-
-/**
- * @defgroup PJSIP_SIMPLE SIP Event, Instant Messaging and Presence Extension (SIMPLE)
- */
-
-/**
- * @file pjsip_simple.h
- * @brief SIP SIMPLE Extension
- */
-
-/*
- * Include this header file to get all functionalities for SIMPLE extension
- * (SIP for Instant Messaging and Presence Leveraging Extension).
- */
-#ifndef __PJSIP_SIMPLE_H__
-#define __PJSIP_SIMPLE_H__
-
-#include <pjsip_simple/messaging.h>
-#include <pjsip_simple/event_notify.h>
-#include <pjsip_simple/pidf.h>
-#include <pjsip_simple/presence.h>
-
-#endif /* __PJSIP_SIMPLE_H__ */
+/* $Id$
+ *
+ */
+/*
+ * PJSIP - SIP Stack
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ * Benny Prijono <bennylp@bulukucing.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+/**
+ * @defgroup PJSIP_SIMPLE SIP Event, Instant Messaging and Presence Extension (SIMPLE)
+ */
+
+/**
+ * @file pjsip_simple.h
+ * @brief SIP SIMPLE Extension
+ */
+
+/*
+ * Include this header file to get all functionalities for SIMPLE extension
+ * (SIP for Instant Messaging and Presence Leveraging Extension).
+ */
+#ifndef __PJSIP_SIMPLE_H__
+#define __PJSIP_SIMPLE_H__
+
+#include <pjsip_simple/messaging.h>
+#include <pjsip_simple/event_notify.h>
+#include <pjsip_simple/pidf.h>
+#include <pjsip_simple/presence.h>
+
+#endif /* __PJSIP_SIMPLE_H__ */
diff --git a/pjsip/include/pjsip_ua.h b/pjsip/include/pjsip_ua.h
index 883faa5..a8facd2 100644
--- a/pjsip/include/pjsip_ua.h
+++ b/pjsip/include/pjsip_ua.h
@@ -1,13 +1,35 @@
-/* $Id$
- *
- */
-
-#ifndef __PJSIP_UA_H__
-#define __PJSIP_UA_H__
-
-#include <pjsip_mod_ua/sip_dialog.h>
-#include <pjsip_mod_ua/sip_reg.h>
-#include <pjsip_mod_ua/sip_ua.h>
-
-#endif /* __PJSIP_UA_H__ */
-
+/* $Id$
+ *
+ */
+/*
+ * PJSIP - SIP Stack
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ * Benny Prijono <bennylp@bulukucing.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#ifndef __PJSIP_UA_H__
+#define __PJSIP_UA_H__
+
+#include <pjsip_mod_ua/sip_dialog.h>
+#include <pjsip_mod_ua/sip_reg.h>
+#include <pjsip_mod_ua/sip_ua.h>
+
+#endif /* __PJSIP_UA_H__ */
+
diff --git a/pjsip/src/pjsip-simple/event_notify.c b/pjsip/src/pjsip-simple/event_notify.c
index 24aa68a..42d8503 100644
--- a/pjsip/src/pjsip-simple/event_notify.c
+++ b/pjsip/src/pjsip-simple/event_notify.c
@@ -1,1629 +1,1651 @@
-/* $Id$
- *
- */
-#include <pjsip_simple/event_notify.h>
-#include <pjsip/sip_msg.h>
-#include <pjsip/sip_misc.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$
+ *
+ */
+/*
+ * PJSIP - SIP Stack
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ * Benny Prijono <bennylp@bulukucing.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; 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_misc.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 9d0f0cd..2b69aef 100644
--- a/pjsip/src/pjsip-simple/event_notify_msg.c
+++ b/pjsip/src/pjsip-simple/event_notify_msg.c
@@ -1,307 +1,329 @@
-/* $Id$
- *
- */
-#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$
+ *
+ */
+/*
+ * PJSIP - SIP Stack
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ * Benny Prijono <bennylp@bulukucing.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; 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 c3992b4..3d08ba2 100644
--- a/pjsip/src/pjsip-simple/messaging.c
+++ b/pjsip/src/pjsip-simple/messaging.c
@@ -1,337 +1,359 @@
-/* $Id$
- *
- */
-#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_misc.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$
+ *
+ */
+/*
+ * PJSIP - SIP Stack
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ * Benny Prijono <bennylp@bulukucing.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; 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_misc.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 f1de9c9..0c76b34 100644
--- a/pjsip/src/pjsip-simple/pidf.c
+++ b/pjsip/src/pjsip-simple/pidf.c
@@ -1,335 +1,357 @@
-/* $Id$
- *
- */
-#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$
+ *
+ */
+/*
+ * PJSIP - SIP Stack
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ * Benny Prijono <bennylp@bulukucing.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; 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 32ed8e3..5f5893a 100644
--- a/pjsip/src/pjsip-simple/presence.c
+++ b/pjsip/src/pjsip-simple/presence.c
@@ -1,384 +1,406 @@
-/* $Id$
- *
- */
-#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$
+ *
+ */
+/*
+ * PJSIP - SIP Stack
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ * Benny Prijono <bennylp@bulukucing.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; 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 5787f67..cc9aaf4 100644
--- a/pjsip/src/pjsip-simple/xpidf.c
+++ b/pjsip/src/pjsip-simple/xpidf.c
@@ -1,279 +1,301 @@
-/* $Id$
- *
- */
-#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$
+ *
+ */
+/*
+ * PJSIP - SIP Stack
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ * Benny Prijono <bennylp@bulukucing.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; 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 0c9db5e..ade94b2 100644
--- a/pjsip/src/pjsip-ua/sip_dialog.c
+++ b/pjsip/src/pjsip-ua/sip_dialog.c
@@ -1,1786 +1,1808 @@
-/* $Id$
- *
- */
-#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_misc.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.
- */
- 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$
+ *
+ */
+/*
+ * PJSIP - SIP Stack
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ * Benny Prijono <bennylp@bulukucing.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; 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_misc.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.
+ */
+ 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 1167975..508b5d9 100644
--- a/pjsip/src/pjsip-ua/sip_reg.c
+++ b/pjsip/src/pjsip-ua/sip_reg.c
@@ -1,496 +1,518 @@
-/* $Id$
- *
- */
-#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_misc.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(®c->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, ®c->str_srv_url, srv_url);
-
- /* Set server URL. */
- tmp = regc->str_srv_url;
- regc->srv_url = pjsip_parse_uri( regc->pool, tmp.ptr, tmp.slen, 0);
- if (regc->srv_url == NULL) {
- return -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, ®c->cid_hdr->id);
-
- /* Set "CSeq" header. */
- regc->cseq_hdr = pjsip_cseq_hdr_create(regc->pool);
- regc->cseq_hdr->cseq = 0;
- pjsip_method_set( ®c->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, ®c->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, ®c->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, ®c->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, ®c->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 = ®c_refresh_timer_cb;
- regc->timer.id = REFRESH_TIMER;
- regc->timer.user_data = regc;
- pjsip_endpt_schedule_timer( regc->endpt, ®c->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$
+ *
+ */
+/*
+ * PJSIP - SIP Stack
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ * Benny Prijono <bennylp@bulukucing.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; 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_misc.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(®c->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, ®c->str_srv_url, srv_url);
+
+ /* Set server URL. */
+ tmp = regc->str_srv_url;
+ regc->srv_url = pjsip_parse_uri( regc->pool, tmp.ptr, tmp.slen, 0);
+ if (regc->srv_url == NULL) {
+ return -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, ®c->cid_hdr->id);
+
+ /* Set "CSeq" header. */
+ regc->cseq_hdr = pjsip_cseq_hdr_create(regc->pool);
+ regc->cseq_hdr->cseq = 0;
+ pjsip_method_set( ®c->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, ®c->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, ®c->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, ®c->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, ®c->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 = ®c_refresh_timer_cb;
+ regc->timer.id = REFRESH_TIMER;
+ regc->timer.user_data = regc;
+ pjsip_endpt_schedule_timer( regc->endpt, ®c->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 6502a5e..fa77934 100644
--- a/pjsip/src/pjsip-ua/sip_ua.c
+++ b/pjsip/src/pjsip-ua/sip_ua.c
@@ -1,491 +1,513 @@
-/* $Id$
- *
- */
-#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_misc.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$
+ *
+ */
+/*
+ * PJSIP - SIP Stack
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ * Benny Prijono <bennylp@bulukucing.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; 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_misc.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 471c4db..cc6dfcf 100644
--- a/pjsip/src/pjsip-ua/sip_ua_private.h
+++ b/pjsip/src/pjsip-ua/sip_ua_private.h
@@ -1,22 +1,44 @@
-/* $Id$
- *
- */
-
-#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$
+ *
+ */
+/*
+ * PJSIP - SIP Stack
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ * Benny Prijono <bennylp@bulukucing.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; 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 030b4a4..bf161a1 100644
--- a/pjsip/src/pjsip/sip_auth.c
+++ b/pjsip/src/pjsip/sip_auth.c
@@ -1,788 +1,810 @@
-/* $Id$
- */
-#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>
+/* $Id$
+ */
+/*
+ * PJSIP - SIP Stack
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ * Benny Prijono <bennylp@bulukucing.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; 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/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) md5_append(pms, (const unsigned char*)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];
- md5_state_t 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)
- ***/
- 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);
- md5_finish(&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)
- ***/
- md5_init(&pms);
- MD5_APPEND( &pms, method->ptr, method->slen);
- MD5_APPEND( &pms, ":", 1);
- MD5_APPEND( &pms, uri->ptr, uri->slen);
- md5_finish(&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)
- ***/
- 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. */
- md5_finish(&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->type == PJSIP_RESPONSE_MSG);
- pj_assert(rdata->msg->line.status.code == 401 ||
- rdata->msg->line.status.code == 407 );
-
- /*
- * Respond to each authentication challenge.
- */
- hdr = rdata->msg->hdr.next;
- while (hdr != &rdata->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->hdr)
- {
- hdr = hdr->next;
- }
- if (hdr == &rdata->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;
-}
-
+
+/* 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) md5_append(pms, (const unsigned char*)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];
+ md5_state_t 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)
+ ***/
+ 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);
+ md5_finish(&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)
+ ***/
+ md5_init(&pms);
+ MD5_APPEND( &pms, method->ptr, method->slen);
+ MD5_APPEND( &pms, ":", 1);
+ MD5_APPEND( &pms, uri->ptr, uri->slen);
+ md5_finish(&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)
+ ***/
+ 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. */
+ md5_finish(&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->type == PJSIP_RESPONSE_MSG);
+ pj_assert(rdata->msg->line.status.code == 401 ||
+ rdata->msg->line.status.code == 407 );
+
+ /*
+ * Respond to each authentication challenge.
+ */
+ hdr = rdata->msg->hdr.next;
+ while (hdr != &rdata->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->hdr)
+ {
+ hdr = hdr->next;
+ }
+ if (hdr == &rdata->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 9e88ef4..b9fe296 100644
--- a/pjsip/src/pjsip/sip_auth_msg.c
+++ b/pjsip/src/pjsip/sip_auth_msg.c
@@ -1,294 +1,316 @@
-/* $Id$
- */
-#include <pjsip/sip_auth_msg.h>
-#include <pjsip/sip_auth_parser.h>
-#include <pj/pool.h>
-#include <pj/list.h>
+/* $Id$
+ */
+/*
+ * PJSIP - SIP Stack
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ * Benny Prijono <bennylp@bulukucing.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; 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;
-}
-
-
+#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;
+}
+
+
diff --git a/pjsip/src/pjsip/sip_auth_parser.c b/pjsip/src/pjsip/sip_auth_parser.c
index 5e121b5..b6c8172 100644
--- a/pjsip/src/pjsip/sip_auth_parser.c
+++ b/pjsip/src/pjsip/sip_auth_parser.c
@@ -1,275 +1,297 @@
-/* $Id$
- */
-#include <pjsip/sip_auth_parser.h>
-#include <pjsip/sip_auth_msg.h>
+/* $Id$
+ */
+/*
+ * PJSIP - SIP Stack
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ * Benny Prijono <bennylp@bulukucing.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; 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 );
-
+#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);
+ pjsip_digest_credential *cred);
static void parse_pgp_credential ( pj_scanner *scanner, pj_pool_t *pool,
- pjsip_pgp_credential *cred);
+ pjsip_pgp_credential *cred);
static void parse_digest_challenge ( pj_scanner *scanner, pj_pool_t *pool,
- pjsip_digest_challenge *chal);
+ 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 };
-
-
+ 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;
- }
-}
-
+ 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);
-}
-
+ 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)) {
+ 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 {
+ }
+
+ } 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;
- }
-}
-
+ &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_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_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_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()
+ 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);
+ 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);
+ 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);
+ 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()
-{
-}
-
+ 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 49c70b1..08d6827 100644
--- a/pjsip/src/pjsip/sip_endpoint.c
+++ b/pjsip/src/pjsip/sip_endpoint.c
@@ -1,841 +1,863 @@
-/* $Id$
- */
-#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>
+/* $Id$
+ */
+/*
+ * PJSIP - SIP Stack
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ * Benny Prijono <bennylp@bulukucing.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; 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_misc.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 <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>
-
-
-#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;
-
- /** 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_transport_mgr *transport_mgr;
-
- /** 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 *, pjsip_rx_data *rdata );
-
-
-/*
- * Create transaction.
- * Defined in sip_transaction.c
- */
-pj_status_t pjsip_tsx_create( pj_pool_t *pool, pjsip_endpoint *endpt,
- pjsip_transaction **tsx );
-
-/*
- * 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 )
+
+
+#define PJSIP_EX_NO_MEMORY PJ_NO_MEMORY_EXCEPTION
+#define THIS_FILE "endpoint"
+
+#define MAX_METHODS 32
+
+/**
+ * The SIP endpoint.
+ */
+struct pjsip_endpoint
{
- // Need to protect this with try/catch?
- endpt_do_event(endpt, evt);
-}
-
-/*
- * Get "Allow" header.
- */
-PJ_DECL(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.
- */
+ /** 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;
+
+ /** 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_transport_mgr *transport_mgr;
+
+ /** 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 *, pjsip_rx_data *rdata );
+
+
+/*
+ * Create transaction.
+ * Defined in sip_transaction.c
+ */
+pj_status_t pjsip_tsx_create( pj_pool_t *pool, pjsip_endpoint *endpt,
+ pjsip_transaction **tsx );
+
+/*
+ * 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_DECL(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,
- pjsip_endpoint **p_endpt)
-{
- pj_status_t status;
- pj_pool_t *pool;
- pjsip_endpoint *endpt;
- pjsip_max_forwards_hdr *mf_hdr;
-
- PJ_LOG(5, (THIS_FILE, "pjsip_endpt_create()"));
+ pjsip_endpoint **p_endpt)
+{
+ pj_status_t status;
+ pj_pool_t *pool;
+ pjsip_endpoint *endpt;
+ pjsip_max_forwards_hdr *mf_hdr;
+
+ 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;
-
- /* 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. */
+
+ /* 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;
+
+ /* 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. */
+ &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;
- }
-
- /* Create transport manager. */
- status = pjsip_transport_mgr_create( endpt->pool,
- endpt,
+ &endpt->timer_heap);
+ if (status != PJ_SUCCESS) {
+ goto on_error;
+ }
+
+ /* Create transport manager. */
+ status = pjsip_transport_mgr_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;
- }
-
+ &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_transport_mgr_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_transport_mgr_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);
-}
-
-/*
- * 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)
-{
- pj_time_val timeout;
- int i;
-
- 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. We also keep
- * polling the timer while we have events.
- */
- timeout.sec = timeout.msec = 0; /* timeout is 'out' var. This just to make compiler happy. */
- for (i=0; i<10; ++i) {
- if (pj_timer_heap_poll( endpt->timer_heap, &timeout ) < 1)
- break;
- }
-
- /* 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 events in the transport manager. */
- pjsip_transport_mgr_handle_events( endpt->transport_mgr, &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.
- */
+ *p_endpt = endpt;
+ return status;
+
+on_error:
+ if (endpt->transport_mgr) {
+ pjsip_transport_mgr_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_transport_mgr_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);
+}
+
+/*
+ * 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)
+{
+ pj_time_val timeout;
+ int i;
+
+ 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. We also keep
+ * polling the timer while we have events.
+ */
+ timeout.sec = timeout.msec = 0; /* timeout is 'out' var. This just to make compiler happy. */
+ for (i=0; i<10; ++i) {
+ if (pj_timer_heap_poll( endpt->timer_heap, &timeout ) < 1)
+ break;
+ }
+
+ /* 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 events in the transport manager. */
+ pjsip_transport_mgr_handle_events( endpt->transport_mgr, &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;
+ 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_DECL(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->type == PJSIP_REQUEST_MSG) {
- role = PJSIP_ROLE_UAS;
- } else {
- role = PJSIP_ROLE_UAC;
- }
- pjsip_tsx_create_key(rdata->pool, &rdata->key, role,
- &rdata->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,
- pjsip_rx_data *rdata )
-{
- pjsip_msg *msg = rdata->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));
-
- /* 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->via->sent_by.port;
- pj_bool_t mismatch = PJ_FALSE;
- if (port == 0) {
- int type;
- type = pjsip_transport_get_type(rdata->transport);
- port = pjsip_transport_get_default_port_for_type(type);
- }
- addr = pjsip_transport_get_addr_name(rdata->transport);
- addr_addr = pj_inet_ntoa(addr->sin_addr);
- if (pj_strcmp2(&rdata->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->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->key.slen, rdata->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->key.ptr, rdata->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->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;
-
+
+ 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_DECL(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->type == PJSIP_REQUEST_MSG) {
+ role = PJSIP_ROLE_UAS;
+ } else {
+ role = PJSIP_ROLE_UAC;
+ }
+ pjsip_tsx_create_key(rdata->pool, &rdata->key, role,
+ &rdata->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,
+ pjsip_rx_data *rdata )
+{
+ pjsip_msg *msg = rdata->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));
+
+ /* 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->via->sent_by.port;
+ pj_bool_t mismatch = PJ_FALSE;
+ if (port == 0) {
+ int type;
+ type = pjsip_transport_get_type(rdata->transport);
+ port = pjsip_transport_get_default_port_for_type(type);
+ }
+ addr = pjsip_transport_get_addr_name(rdata->transport);
+ addr_addr = pj_inet_ntoa(addr->sin_addr);
+ if (pj_strcmp2(&rdata->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->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->key.slen, rdata->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->key.ptr, rdata->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->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.
- */
+ 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->line.req.method.id != PJSIP_ACK_METHOD) {
pj_status_t status;
-
- /* Create transaction, mutex is locked there. */
- status = pjsip_endpt_create_tsx(endpt, &tsx);
+
+ /* 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"));
+ "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->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.
- */
+ }
+
+ /* 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->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) {
+ 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,
+ &tdata);
+ } else {
+ status = pjsip_endpt_create_response(endpt, rdata,
PJSIP_SC_METHOD_NOT_ALLOWED,
- &tdata);
+ &tdata);
}
if (status != PJ_SUCCESS) {
@@ -843,202 +865,202 @@
"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.
- */
+
+ 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.
- */
+
+ 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);
-}
-
-/*
- * Find/create transport.
- */
-PJ_DEF(void) pjsip_endpt_get_transport( pjsip_endpoint *endpt,
- pj_pool_t *pool,
- pjsip_transport_type_e type,
- const pj_sockaddr_in *remote,
- void *token,
- pjsip_transport_completion_callback *cb)
-{
- PJ_LOG(5, (THIS_FILE, "pjsip_endpt_get_transport()"));
- pjsip_transport_get( endpt->transport_mgr, pool, type,
- remote, token, cb);
-}
-
-
-PJ_DEF(pj_status_t) pjsip_endpt_create_listener( pjsip_endpoint *endpt,
- pjsip_transport_type_e type,
- pj_sockaddr_in *addr,
- const pj_sockaddr_in *addr_name)
-{
- PJ_LOG(5, (THIS_FILE, "pjsip_endpt_create_listener()"));
- return pjsip_create_listener( endpt->transport_mgr, type, addr, addr_name );
-}
-
-PJ_DEF(pj_status_t) pjsip_endpt_create_udp_listener( pjsip_endpoint *endpt,
- pj_sock_t sock,
- const pj_sockaddr_in *addr_name)
-{
- PJ_LOG(5, (THIS_FILE, "pjsip_endpt_create_udp_listener()"));
- return pjsip_create_udp_listener( endpt->transport_mgr, sock, addr_name );
-}
-
-PJ_DEF(void) pjsip_endpt_dump( pjsip_endpoint *endpt, pj_bool_t detail )
-{
-#if PJ_LOG_MAX_LEVEL >= 3
- unsigned count;
- pj_hash_iterator_t itr_val;
- pj_hash_iterator_t *itr;
-
- 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.
- * Note: transport is not properly locked in this function.
- * See pjsip_transport_first, pjsip_transport_next.
- */
- itr = pjsip_transport_first( endpt->transport_mgr, &itr_val );
- if (itr) {
- PJ_LOG(3, (THIS_FILE, " Dumping transports:"));
-
- do {
- char src_addr[128], dst_addr[128];
- int src_port, dst_port;
- const pj_sockaddr_in *addr;
- pjsip_transport_t *t;
-
- t = pjsip_transport_this(endpt->transport_mgr, itr);
- addr = pjsip_transport_get_local_addr(t);
- pj_native_strcpy(src_addr, pj_inet_ntoa(addr->sin_addr));
- src_port = pj_ntohs(addr->sin_port);
-
- addr = pjsip_transport_get_remote_addr(t);
- pj_native_strcpy(dst_addr, pj_inet_ntoa(addr->sin_addr));
- dst_port = pj_ntohs(addr->sin_port);
-
- PJ_LOG(3, (THIS_FILE, " %s %s %s:%d --> %s:%d (refcnt=%d)",
- pjsip_transport_get_type_name(t),
- pjsip_transport_get_obj_name(t),
- src_addr, src_port,
- dst_addr, dst_port,
- pjsip_transport_get_ref_cnt(t)));
-
- itr = pjsip_transport_next(endpt->transport_mgr, itr);
- } while (itr);
- }
-
- /* 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
-}
-
+{
+ 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);
+}
+
+/*
+ * Find/create transport.
+ */
+PJ_DEF(void) pjsip_endpt_get_transport( pjsip_endpoint *endpt,
+ pj_pool_t *pool,
+ pjsip_transport_type_e type,
+ const pj_sockaddr_in *remote,
+ void *token,
+ pjsip_transport_completion_callback *cb)
+{
+ PJ_LOG(5, (THIS_FILE, "pjsip_endpt_get_transport()"));
+ pjsip_transport_get( endpt->transport_mgr, pool, type,
+ remote, token, cb);
+}
+
+
+PJ_DEF(pj_status_t) pjsip_endpt_create_listener( pjsip_endpoint *endpt,
+ pjsip_transport_type_e type,
+ pj_sockaddr_in *addr,
+ const pj_sockaddr_in *addr_name)
+{
+ PJ_LOG(5, (THIS_FILE, "pjsip_endpt_create_listener()"));
+ return pjsip_create_listener( endpt->transport_mgr, type, addr, addr_name );
+}
+
+PJ_DEF(pj_status_t) pjsip_endpt_create_udp_listener( pjsip_endpoint *endpt,
+ pj_sock_t sock,
+ const pj_sockaddr_in *addr_name)
+{
+ PJ_LOG(5, (THIS_FILE, "pjsip_endpt_create_udp_listener()"));
+ return pjsip_create_udp_listener( endpt->transport_mgr, sock, addr_name );
+}
+
+PJ_DEF(void) pjsip_endpt_dump( pjsip_endpoint *endpt, pj_bool_t detail )
+{
+#if PJ_LOG_MAX_LEVEL >= 3
+ unsigned count;
+ pj_hash_iterator_t itr_val;
+ pj_hash_iterator_t *itr;
+
+ 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.
+ * Note: transport is not properly locked in this function.
+ * See pjsip_transport_first, pjsip_transport_next.
+ */
+ itr = pjsip_transport_first( endpt->transport_mgr, &itr_val );
+ if (itr) {
+ PJ_LOG(3, (THIS_FILE, " Dumping transports:"));
+
+ do {
+ char src_addr[128], dst_addr[128];
+ int src_port, dst_port;
+ const pj_sockaddr_in *addr;
+ pjsip_transport_t *t;
+
+ t = pjsip_transport_this(endpt->transport_mgr, itr);
+ addr = pjsip_transport_get_local_addr(t);
+ pj_native_strcpy(src_addr, pj_inet_ntoa(addr->sin_addr));
+ src_port = pj_ntohs(addr->sin_port);
+
+ addr = pjsip_transport_get_remote_addr(t);
+ pj_native_strcpy(dst_addr, pj_inet_ntoa(addr->sin_addr));
+ dst_port = pj_ntohs(addr->sin_port);
+
+ PJ_LOG(3, (THIS_FILE, " %s %s %s:%d --> %s:%d (refcnt=%d)",
+ pjsip_transport_get_type_name(t),
+ pjsip_transport_get_obj_name(t),
+ src_addr, src_port,
+ dst_addr, dst_port,
+ pjsip_transport_get_ref_cnt(t)));
+
+ itr = pjsip_transport_next(endpt->transport_mgr, itr);
+ } while (itr);
+ }
+
+ /* 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_misc.c b/pjsip/src/pjsip/sip_misc.c
index a956239..ce1ceb8 100644
--- a/pjsip/src/pjsip/sip_misc.c
+++ b/pjsip/src/pjsip/sip_misc.c
@@ -1,166 +1,188 @@
-/* $Id$
- */
-#include <pjsip/sip_misc.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>
+/* $Id$
+ */
+/*
+ * PJSIP - SIP Stack
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ * Benny Prijono <bennylp@bulukucing.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+#include <pjsip/sip_misc.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)
+
+#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;
+ 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.
- */
+ 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;
+ 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;
+
+ /* 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;
@@ -170,539 +192,539 @@
endpt_hdr = endpt_hdr->next;
}
- /* Add From header. */
- if (param_from->tag.slen == 0)
- pj_create_unique_string(tdata->pool, ¶m_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));
+ /* Add From header. */
+ if (param_from->tag.slen == 0)
+ pj_create_unique_string(tdata->pool, ¶m_from->tag);
+ pjsip_msg_add_hdr(msg, (void*)param_from);
- *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,
+ /* 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;
-}
-
-/*
- * 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,
+ *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_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;
- 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->timestamp;
-
- /* Copy all the via headers, in order. */
- via = rdata->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->from);
- pjsip_msg_add_hdr( msg, hdr);
-
- /* Copy To header. */
- hdr = pjsip_hdr_clone(tdata->pool, rdata->to);
- pjsip_msg_add_hdr( msg, hdr);
-
- /* Copy CSeq header. */
- hdr = pjsip_hdr_clone(tdata->pool, rdata->cseq);
- pjsip_msg_add_hdr( msg, hdr);
-
+ pj_status_t status;
+
+ /* rdata must be a request message. */
+ req_msg = rdata->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->timestamp;
+
+ /* Copy all the via headers, in order. */
+ via = rdata->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->from);
+ pjsip_msg_add_hdr( msg, hdr);
+
+ /* Copy To header. */
+ hdr = pjsip_hdr_clone(tdata->pool, rdata->to);
+ pjsip_msg_add_hdr( msg, hdr);
+
+ /* Copy CSeq header. */
+ hdr = pjsip_hdr_clone(tdata->pool, rdata->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->type==PJSIP_RESPONSE_MSG &&
- rdata->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->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 = 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->type==PJSIP_RESPONSE_MSG &&
+ rdata->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->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
*/
- *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_t *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 = pjsip_transport_get_flag(req_transport);
- send_addr->type = pjsip_transport_get_type(req_transport);
-
- if (PJSIP_TRANSPORT_IS_RELIABLE(req_transport)) {
- const pj_sockaddr_in *remote_addr;
- remote_addr = pjsip_transport_get_remote_addr(req_transport);
- 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];
-}
-
+ 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_t *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 = pjsip_transport_get_flag(req_transport);
+ send_addr->type = pjsip_transport_get_type(req_transport);
+
+ if (PJSIP_TRANSPORT_IS_RELIABLE(req_transport)) {
+ const pj_sockaddr_in *remote_addr;
+ remote_addr = pjsip_transport_get_remote_addr(req_transport);
+ 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/pjsip/sip_msg.c b/pjsip/src/pjsip/sip_msg.c
index 587df86..6fd32f4 100644
--- a/pjsip/src/pjsip/sip_msg.c
+++ b/pjsip/src/pjsip/sip_msg.c
@@ -1,1416 +1,1438 @@
-/* $Id$
- */
-#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$
+ */
+/*
+ * PJSIP - SIP Stack
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ * Benny Prijono <bennylp@bulukucing.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; 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;
+}
diff --git a/pjsip/src/pjsip/sip_parser.c b/pjsip/src/pjsip/sip_parser.c
index 63cc3b5..fc502e8 100644
--- a/pjsip/src/pjsip/sip_parser.c
+++ b/pjsip/src/pjsip/sip_parser.c
@@ -1,532 +1,554 @@
-/* $Id$
- */
-#include <pjsip/sip_parser.h>
-#include <pjsip/sip_uri.h>
-#include <pjsip/sip_msg.h>
+/* $Id$
+ */
+/*
+ * PJSIP - SIP Stack
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ * Benny Prijono <bennylp@bulukucing.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; 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 <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 <pjsip/sip_transport.h> /* rdata structure */
+#include <pjlib-util/scanner.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 RESERVED ";/?:@&=+$,"
-#define MARK "-_.!~*'()"
-#define ESCAPED "%"
-#define USER "&=+$,;?/"
-#define PASS "&=+$,"
+
+#define RESERVED ";/?:@&=+$,"
+#define MARK "-_.!~*'()"
+#define ESCAPED "%"
+#define USER "&=+$,;?/"
+#define PASS "&=+$,"
#define TOKEN "-.!%*_=`'~+" /* '+' is because of application/pidf+xml
- * in Content-Type! */
-#define HOST "_-."
-#define HEX_DIGIT "abcdefABCDEF"
-#define PARAM_CHAR "[]/:&+$" MARK "%"
-
-#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};
+ * in Content-Type! */
+#define HOST "_-."
+#define HEX_DIGIT "abcdefABCDEF"
+#define PARAM_CHAR "[]/:&+$" MARK "%"
+
+#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. */
+/* 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_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.*/
+ * it's not quoted.) */
+ 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_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;
+ * in URL. */
- if (initialized)
- return PJ_SUCCESS;
-
- initialized = 1;
+
+/*
+ * 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_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);
+ 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 );
+ 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 );
+ 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);
+ 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");
+ 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);
+ 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);
+ 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);
+ 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);
+ PJ_ASSERT_RETURN(status == PJ_SUCCESS, status);
+ pj_cis_add_str(&pjsip_PARAM_CHAR_SPEC, PARAM_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, MARK ESCAPED USER );
+ PJ_ASSERT_RETURN(status == PJ_SUCCESS, status);
+ pj_cis_add_str( &pjsip_USER_SPEC, MARK ESCAPED USER );
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);
+ 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 );
+ 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<");
-
+ 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. */
+ 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;
+ 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) {
+ 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)
+ (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;
- }
+ 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 (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.
- */
+
+ /* 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. */
+ 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;
+ 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();
-
+ pjsip_parse_ctx context;
+ PJ_USE_EXCEPTION;
+
+ init_sip_parser();
+
pj_scan_init(&scanner, buf, size, PJ_SCAN_AUTOSKIP_WS_HEADER,
- &on_syntax_error);
+ &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;
-}
+
+ 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,
@@ -556,1151 +578,1151 @@
pj_scan_fini(&scanner);
return rdata->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;
+
+/* 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) ||
+ 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. */
+ (*(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.
- */
+ {
+ /* 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;
+ 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;
-
+
+ /* 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);
-
- /* 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);
- }
- } 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 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);
- 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_str_t *user, pj_str_t *pass)
-{
- pj_scan_get( scanner, &pjsip_USER_SPEC, user);
- if ( *scanner->curptr == ':') {
- pj_scan_get_char( scanner );
- pj_scan_get( scanner, &pjsip_PASSWD_SPEC, 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;
-
- 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);
-
+ // "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);
+
+ /* 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);
+ }
+ } 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 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);
+ 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_str_t *user, pj_str_t *pass)
+{
+ pj_scan_get( scanner, &pjsip_USER_SPEC, user);
+ if ( *scanner->curptr == ':') {
+ pj_scan_get_char( scanner );
+ pj_scan_get( scanner, &pjsip_PASSWD_SPEC, 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;
+
+ 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))
- {
+ 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);
- }
-
+ 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;
- 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 {
- concat_param(&url->other_param, pool, &pname, &pvalue);
- }
- }
-
- /* Get header params. */
- if (parse_params && *scanner->curptr == '?') {
+ 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;
+ 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 {
+ concat_param(&url->other_param, pool, &pname, &pvalue);
+ }
+ }
+
+ /* Get header params. */
+ if (parse_params && *scanner->curptr == '?') {
pj_scan_get_until(scanner, &pjsip_NEWLINE_OR_EOF_SPEC,
- &url->header_param);
- }
-
- 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);
+ &url->header_param);
+ }
+
+ 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);
+ &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);
+ &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->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 )
-{
+
+ 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;
+ 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);
-
+ 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);
+
+ 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->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;
+
+ 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);
- }
-
+
+ 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->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);
-
+
+ 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->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);
+
+ 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->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);
+
+ 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->require == NULL)
ctx->rdata->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);
+
+ 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->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);
+
+ 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->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 == ';') {
+
+ 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)
-{
+ &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);
+ 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->record_route==NULL)
ctx->rdata->record_route = first;
-
- return (pjsip_hdr*)first;
-}
-
-/* Parse Route: header. */
-static pjsip_hdr* parse_hdr_route( pjsip_parse_ctx *ctx )
-{
+
+ 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);
+ 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->route==NULL)
ctx->rdata->route = first;
-
- return (pjsip_hdr*)first;
-}
-
-/* Parse Via: header. */
-static pjsip_hdr* parse_hdr_via( pjsip_parse_ctx *ctx )
-{
+
+ 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);
-
+ 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->via == NULL)
ctx->rdata->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;
+
+ 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();
-
+ pjsip_parse_ctx context;
+ PJ_USE_EXCEPTION;
+
+ init_sip_parser();
+
pj_scan_init(&scanner, buf, size, PJ_SCAN_AUTOSKIP_WS_HEADER,
- &on_syntax_error);
+ &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;
-}
-
+
+ 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 b199eab..74d7e88 100644
--- a/pjsip/src/pjsip/sip_resolve.c
+++ b/pjsip/src/pjsip/sip_resolve.c
@@ -1,110 +1,132 @@
-/* $Id$
- */
-
-#include <pjsip/sip_resolve.h>
-#include <pjsip/sip_transport.h>
-#include <pj/pool.h>
-#include <pj/ctype.h>
+/* $Id$
+ */
+/*
+ * PJSIP - SIP Stack
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ * Benny Prijono <bennylp@bulukucing.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; 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) {
+
+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 {
+ (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);
-}
-
+ (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 ebc0a80..9e9b483 100644
--- a/pjsip/src/pjsip/sip_transaction.c
+++ b/pjsip/src/pjsip/sip_transaction.c
@@ -1,1966 +1,1988 @@
-/* $Id$
- */
-#include <pjsip/sip_transaction.h>
-#include <pjsip/sip_transport.h>
-#include <pjsip/sip_config.h>
-#include <pjsip/sip_misc.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,
+/* $Id$
+ */
+/*
+ * PJSIP - SIP Stack
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ * Benny Prijono <bennylp@bulukucing.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; 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_misc.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 void tsx_timer_callback( pj_timer_heap_t *theap,
- pj_timer_entry *entry);
+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);
+ 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, PJ_EINVAL);
- PJ_ASSERT_RETURN(rdata->via, PJSIP_EMISSINGHDR);
- PJ_ASSERT_RETURN(rdata->cseq, PJSIP_EMISSINGHDR);
- PJ_ASSERT_RETURN(rdata->from, PJSIP_EMISSINGHDR);
-
- host = &rdata->via->sent_by.host;
- req_uri = (pjsip_uri*)rdata->msg->line.req.uri;
-
- /* Calculate length required. */
- len_required = 9 + /* CSeq number */
- rdata->from->tag.slen + /* From tag. */
- rdata->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->cseq->cseq, p);
- p += len;
- *p++ = SEPARATOR;
-
- /* Add From tag. */
- len = rdata->from->tag.slen;
- pj_memcpy( p, rdata->from->tag.ptr, len);
- p += len;
- *p++ = SEPARATOR;
-
- /* Add Call-ID. */
- len = rdata->call_id.slen;
- pj_memcpy( p, rdata->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->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->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_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_t *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",
- pjsip_transport_get_type_name(tr),
- addr, tsx->dest_name.port));
- } else {
- PJ_LOG(4, (tsx->obj_name, "%s unable to connect to %s:%d, status=%d",
- pjsip_transport_get_type_name(tr),
- 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;
-
- 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;
- pjsip_endpt_get_transport(tsx->endpt, tsx->pool,
- addr->entry[0].type, &addr->entry[0].addr,
- tsx,
- &tsx_transport_callback);
-
- /* 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;
- 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;
-
- /* 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->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->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->transport) ||
- rdata->via->rport_param >= 0)
- {
- tsx->transport = rdata->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 =
- pjsip_transport_get_type(tsx->transport);
- pj_memcpy(&tsx->remote_addr.entry[0].addr,
- &rdata->addr, rdata->addr_len);
-
- } else {
- pj_status_t status;
-
- status = pjsip_get_response_addr(tsx->pool, rdata->transport,
- rdata->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);
-}
-
-/*
- * 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) {
- pj_ssize_t sent;
- 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 = pjsip_transport_get_addr_name(tsx->transport);
- pj_strdup2(tdata->pool, &via->transport,
- pjsip_transport_get_type_name(tsx->transport));
- 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_msg(
- tsx->transport, tdata,
- &tsx->remote_addr.entry[tsx->current_addr].addr,
- &sent);
- if (status != PJ_SUCCESS) {
- 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->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->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;
- 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;
- 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->line.req.method.id;
-
- /* Must be a request message. */
- pj_assert(event->body.rx_msg.rdata->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;
-}
-
+ 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, PJ_EINVAL);
+ PJ_ASSERT_RETURN(rdata->via, PJSIP_EMISSINGHDR);
+ PJ_ASSERT_RETURN(rdata->cseq, PJSIP_EMISSINGHDR);
+ PJ_ASSERT_RETURN(rdata->from, PJSIP_EMISSINGHDR);
+
+ host = &rdata->via->sent_by.host;
+ req_uri = (pjsip_uri*)rdata->msg->line.req.uri;
+
+ /* Calculate length required. */
+ len_required = 9 + /* CSeq number */
+ rdata->from->tag.slen + /* From tag. */
+ rdata->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->cseq->cseq, p);
+ p += len;
+ *p++ = SEPARATOR;
+
+ /* Add From tag. */
+ len = rdata->from->tag.slen;
+ pj_memcpy( p, rdata->from->tag.ptr, len);
+ p += len;
+ *p++ = SEPARATOR;
+
+ /* Add Call-ID. */
+ len = rdata->call_id.slen;
+ pj_memcpy( p, rdata->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->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->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_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_t *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",
+ pjsip_transport_get_type_name(tr),
+ addr, tsx->dest_name.port));
+ } else {
+ PJ_LOG(4, (tsx->obj_name, "%s unable to connect to %s:%d, status=%d",
+ pjsip_transport_get_type_name(tr),
+ 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;
+
+ 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;
+ pjsip_endpt_get_transport(tsx->endpt, tsx->pool,
+ addr->entry[0].type, &addr->entry[0].addr,
+ tsx,
+ &tsx_transport_callback);
+
+ /* 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;
+ 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;
+
+ /* 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->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->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->transport) ||
+ rdata->via->rport_param >= 0)
+ {
+ tsx->transport = rdata->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 =
+ pjsip_transport_get_type(tsx->transport);
+ pj_memcpy(&tsx->remote_addr.entry[0].addr,
+ &rdata->addr, rdata->addr_len);
+
+ } else {
+ pj_status_t status;
+
+ status = pjsip_get_response_addr(tsx->pool, rdata->transport,
+ rdata->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);
+}
+
+/*
+ * 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) {
+ pj_ssize_t sent;
+ 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 = pjsip_transport_get_addr_name(tsx->transport);
+ pj_strdup2(tdata->pool, &via->transport,
+ pjsip_transport_get_type_name(tsx->transport));
+ 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_msg(
+ tsx->transport, tdata,
+ &tsx->remote_addr.entry[tsx->current_addr].addr,
+ &sent);
+ if (status != PJ_SUCCESS) {
+ 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->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->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;
+ 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;
+ 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->line.req.method.id;
+
+ /* Must be a request message. */
+ pj_assert(event->body.rx_msg.rdata->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 09eb6cf..477ddf2 100644
--- a/pjsip/src/pjsip/sip_transport.c
+++ b/pjsip/src/pjsip/sip_transport.c
@@ -1,1667 +1,1689 @@
-/* $Id$
- */
-#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>
+/* $Id$
+ */
+/*
+ * PJSIP - SIP Stack
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ * Benny Prijono <bennylp@bulukucing.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; 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/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>
-
-#define MGR_IDLE_CHECK_INTERVAL 30
-#define MGR_HASH_TABLE_SIZE PJSIP_MAX_DIALOG_COUNT
-#define BACKLOG 5
-#define DEFAULT_SO_SNDBUF (8 * 1024 * 1024)
-#define DEFAULT_SO_RCVBUF (8 * 1024 * 1024)
-
-#define LOG_TRANSPORT_MGR "trmgr"
-#define THIS_FILE "sip_transport"
-
-static void destroy_transport( pjsip_transport_mgr *mgr, pjsip_transport_t *tr );
-
-
-/**
- * New TCP socket for accept.
- */
-typedef struct incoming_socket_rec
-{
- pj_sock_t sock;
- pj_sockaddr_in remote;
- pj_sockaddr_in local;
- int addrlen;
-} incoming_socket_rec;
-
-/**
- * SIP Transport.
- */
-struct pjsip_transport_t
-{
- /** Standard list members, for chaining the transport in the
- * listener list.
- */
- PJ_DECL_LIST_MEMBER(struct pjsip_transport_t);
-
- /** Transport's pool. */
- pj_pool_t *pool;
-
- /** Mutex */
- pj_mutex_t *tr_mutex;
-
- /** Transport name for logging purpose */
- char obj_name[PJ_MAX_OBJ_NAME];
-
- /** Socket handle */
- pj_sock_t sock;
-
- /** Transport type. */
- pjsip_transport_type_e type;
-
- /** Flags to keep various states (see pjsip_transport_flags_e). */
- pj_uint32_t flag;
-
- /** I/O Queue key */
- pj_ioqueue_key_t *key;
+
+#define MGR_IDLE_CHECK_INTERVAL 30
+#define MGR_HASH_TABLE_SIZE PJSIP_MAX_DIALOG_COUNT
+#define BACKLOG 5
+#define DEFAULT_SO_SNDBUF (8 * 1024 * 1024)
+#define DEFAULT_SO_RCVBUF (8 * 1024 * 1024)
+
+#define LOG_TRANSPORT_MGR "trmgr"
+#define THIS_FILE "sip_transport"
+
+static void destroy_transport( pjsip_transport_mgr *mgr, pjsip_transport_t *tr );
+
+
+/**
+ * New TCP socket for accept.
+ */
+typedef struct incoming_socket_rec
+{
+ pj_sock_t sock;
+ pj_sockaddr_in remote;
+ pj_sockaddr_in local;
+ int addrlen;
+} incoming_socket_rec;
+
+/**
+ * SIP Transport.
+ */
+struct pjsip_transport_t
+{
+ /** Standard list members, for chaining the transport in the
+ * listener list.
+ */
+ PJ_DECL_LIST_MEMBER(struct pjsip_transport_t);
+
+ /** Transport's pool. */
+ pj_pool_t *pool;
+
+ /** Mutex */
+ pj_mutex_t *tr_mutex;
+
+ /** Transport name for logging purpose */
+ char obj_name[PJ_MAX_OBJ_NAME];
+
+ /** Socket handle */
+ pj_sock_t sock;
+
+ /** Transport type. */
+ pjsip_transport_type_e type;
+
+ /** Flags to keep various states (see pjsip_transport_flags_e). */
+ pj_uint32_t flag;
+
+ /** I/O Queue key */
+ pj_ioqueue_key_t *key;
/** Accept key. */
pj_ioqueue_op_key_t accept_op;
- /** Receive data buffer */
- pjsip_rx_data *rdata;
+ /** Receive data buffer */
+ pjsip_rx_data *rdata;
- /** Pointer to transport manager */
- pjsip_transport_mgr *mgr;
-
- /** Reference counter, to prevent this transport from being closed while
- * it's being used.
- */
- pj_atomic_t *ref_cnt;
-
- /** Local address. */
- pj_sockaddr_in local_addr;
-
- /** Address name (what to put in Via address field). */
- pj_sockaddr_in addr_name;
-
- /** Remote address (can be zero for UDP and for listeners). UDP listener
- * bound to local loopback interface (127.0.0.1) has remote address set
- * to 127.0.0.1 to prevent client from using it to send to remote hosts,
- * because remote host then will receive 127.0.0.1 as the packet's
- * source address.
- */
- pj_sockaddr_in remote_addr;
-
- /** Struct to save incoming socket information. */
- incoming_socket_rec accept_data;
-
- /** When this transport should be closed. */
- pj_time_val close_time;
-
- /** List of callbacks to be called when client attempt to use this
- * transport while it's not connected (i.e. still connecting).
- */
- pj_list cb_list;
-};
-
-
-/*
- * Transport manager.
- */
-struct pjsip_transport_mgr
-{
- pj_hash_table_t *transport_table;
- pj_mutex_t *mutex;
- pjsip_endpoint *endpt;
- pj_ioqueue_t *ioqueue;
+ /** Pointer to transport manager */
+ pjsip_transport_mgr *mgr;
+
+ /** Reference counter, to prevent this transport from being closed while
+ * it's being used.
+ */
+ pj_atomic_t *ref_cnt;
+
+ /** Local address. */
+ pj_sockaddr_in local_addr;
+
+ /** Address name (what to put in Via address field). */
+ pj_sockaddr_in addr_name;
+
+ /** Remote address (can be zero for UDP and for listeners). UDP listener
+ * bound to local loopback interface (127.0.0.1) has remote address set
+ * to 127.0.0.1 to prevent client from using it to send to remote hosts,
+ * because remote host then will receive 127.0.0.1 as the packet's
+ * source address.
+ */
+ pj_sockaddr_in remote_addr;
+
+ /** Struct to save incoming socket information. */
+ incoming_socket_rec accept_data;
+
+ /** When this transport should be closed. */
+ pj_time_val close_time;
+
+ /** List of callbacks to be called when client attempt to use this
+ * transport while it's not connected (i.e. still connecting).
+ */
+ pj_list cb_list;
+};
+
+
+/*
+ * Transport manager.
+ */
+struct pjsip_transport_mgr
+{
+ pj_hash_table_t *transport_table;
+ pj_mutex_t *mutex;
+ pjsip_endpoint *endpt;
+ pj_ioqueue_t *ioqueue;
pj_time_val next_idle_check;
pj_size_t send_buf_size;
- pj_size_t recv_buf_size;
- void (*message_callback)(pjsip_endpoint*, pjsip_rx_data *rdata);
-};
-
-/*
- * Transport role.
- */
-typedef enum transport_role_e
-{
- TRANSPORT_ROLE_LISTENER,
- TRANSPORT_ROLE_TRANSPORT,
-} transport_role_e;
-
-/*
- * Transport key for indexing in the hash table.
- * WATCH OUT FOR ALIGNMENT PROBLEM HERE!
- */
-typedef struct transport_key
-{
- pj_uint8_t type;
- pj_uint8_t zero;
- pj_uint16_t port;
- pj_uint32_t addr;
-} transport_key;
-
-/*
- * Transport callback.
- */
-struct transport_callback
-{
- PJ_DECL_LIST_MEMBER(struct transport_callback);
-
- /** User defined token to be passed to the callback. */
- void *token;
-
- /** The callback function. */
- void (*cb)(pjsip_transport_t *tr, void *token, pj_status_t status);
-
-};
-
-/*
- * Transport names.
- */
-const struct
-{
- pjsip_transport_type_e type;
- pj_uint16_t port;
- pj_str_t name;
-} transport_names[] =
-{
- { PJSIP_TRANSPORT_UNSPECIFIED, 0, {NULL, 0}},
- { PJSIP_TRANSPORT_UDP, 5060, {"UDP", 3}},
-#if PJ_HAS_TCP
- { PJSIP_TRANSPORT_TCP, 5060, {"TCP", 3}},
- { PJSIP_TRANSPORT_TLS, 5061, {"TLS", 3}},
- { PJSIP_TRANSPORT_SCTP, 5060, {"SCTP", 4}}
-#endif
-};
-
+ pj_size_t recv_buf_size;
+ void (*message_callback)(pjsip_endpoint*, pjsip_rx_data *rdata);
+};
+
+/*
+ * Transport role.
+ */
+typedef enum transport_role_e
+{
+ TRANSPORT_ROLE_LISTENER,
+ TRANSPORT_ROLE_TRANSPORT,
+} transport_role_e;
+
+/*
+ * Transport key for indexing in the hash table.
+ * WATCH OUT FOR ALIGNMENT PROBLEM HERE!
+ */
+typedef struct transport_key
+{
+ pj_uint8_t type;
+ pj_uint8_t zero;
+ pj_uint16_t port;
+ pj_uint32_t addr;
+} transport_key;
+
+/*
+ * Transport callback.
+ */
+struct transport_callback
+{
+ PJ_DECL_LIST_MEMBER(struct transport_callback);
+
+ /** User defined token to be passed to the callback. */
+ void *token;
+
+ /** The callback function. */
+ void (*cb)(pjsip_transport_t *tr, void *token, pj_status_t status);
+
+};
+
+/*
+ * Transport names.
+ */
+const struct
+{
+ pjsip_transport_type_e type;
+ pj_uint16_t port;
+ pj_str_t name;
+} transport_names[] =
+{
+ { PJSIP_TRANSPORT_UNSPECIFIED, 0, {NULL, 0}},
+ { PJSIP_TRANSPORT_UDP, 5060, {"UDP", 3}},
+#if PJ_HAS_TCP
+ { PJSIP_TRANSPORT_TCP, 5060, {"TCP", 3}},
+ { PJSIP_TRANSPORT_TLS, 5061, {"TLS", 3}},
+ { PJSIP_TRANSPORT_SCTP, 5060, {"SCTP", 4}}
+#endif
+};
+
static void on_ioqueue_read(pj_ioqueue_key_t *key,
pj_ioqueue_op_key_t *op_key,
- pj_ssize_t bytes_read);
+ pj_ssize_t bytes_read);
static void on_ioqueue_write(pj_ioqueue_key_t *key,
pj_ioqueue_op_key_t *op_key,
- pj_ssize_t bytes_sent);
+ pj_ssize_t bytes_sent);
static void on_ioqueue_accept(pj_ioqueue_key_t *key,
pj_ioqueue_op_key_t *op_key,
pj_sock_t newsock,
- int status);
+ int status);
static void on_ioqueue_connect(pj_ioqueue_key_t *key,
- int status);
-
-static pj_ioqueue_callback ioqueue_transport_callback =
-{
- &on_ioqueue_read,
- &on_ioqueue_write,
- &on_ioqueue_accept,
- &on_ioqueue_connect
-};
-
-static void init_key_from_transport(transport_key *key,
- const pjsip_transport_t *tr)
-{
- /* This is to detect alignment problems. */
- pj_assert(sizeof(transport_key) == 8);
-
- key->type = (pj_uint8_t)tr->type;
- key->zero = 0;
- key->addr = pj_sockaddr_in_get_addr(&tr->remote_addr).s_addr;
- key->port = pj_sockaddr_in_get_port(&tr->remote_addr);
- /*
- if (key->port == 0) {
- key->port = pj_sockaddr_in_get_port(&tr->local_addr);
- }
- */
-}
-
-#if PJ_HAS_TCP
-static void init_tcp_key(transport_key *key, pjsip_transport_type_e type,
- const pj_sockaddr_in *addr)
-{
- /* This is to detect alignment problems. */
- pj_assert(sizeof(transport_key) == 8);
-
- key->type = (pj_uint8_t)type;
- key->zero = 0;
- key->addr = pj_sockaddr_in_get_addr(addr).s_addr;
- key->port = pj_sockaddr_in_get_port(addr);
-}
-#endif
-
-static void init_udp_key(transport_key *key, pjsip_transport_type_e type,
- const pj_sockaddr_in *addr)
-{
- PJ_UNUSED_ARG(addr);
-
- /* This is to detect alignment problems. */
- pj_assert(sizeof(transport_key) == 8);
-
- pj_memset(key, 0, sizeof(*key));
- key->type = (pj_uint8_t)type;
-
-#if 0 /* Not sure why we need to make 127.0.0.1 a special case */
- if (addr->sin_addr.s_addr == inet_addr("127.0.0.1")) {
- /* This looks more complicated than it is because key->addr uses
- * the host version of the address (i.e. converted with ntohl()).
- */
- pj_str_t localaddr = pj_str("127.0.0.1");
- pj_sockaddr_in addr;
- pj_sockaddr_set_str_addr(&addr, &localaddr);
- key->addr = pj_sockaddr_in_get_addr(&addr);
- }
-#endif
-}
-
-/*
- * Get type format name (for pool name).
- */
-static const char *transport_get_name_format( int type )
-{
- switch (type) {
- case PJSIP_TRANSPORT_UDP:
- return " udp%p";
-#if PJ_HAS_TCP
- case PJSIP_TRANSPORT_TCP:
- return " tcp%p";
- case PJSIP_TRANSPORT_TLS:
- return " tls%p";
- case PJSIP_TRANSPORT_SCTP:
- return "sctp%p";
-#endif
- }
- pj_assert(0);
- return 0;
-}
-
-/*
- * Get the default SIP port number for the specified type.
- */
-PJ_DEF(int) pjsip_transport_get_default_port_for_type(pjsip_transport_type_e type)
-{
- return transport_names[type].port;
-}
-
-/*
- * Get transport name.
- */
-static const char *get_type_name(int type)
-{
- return transport_names[type].name.ptr;
-}
-
-/*
- * 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;
- }
- }
- return PJSIP_TRANSPORT_UNSPECIFIED;
-}
-
-/*
- * Create new transmit buffer.
- */
+ int status);
+
+static pj_ioqueue_callback ioqueue_transport_callback =
+{
+ &on_ioqueue_read,
+ &on_ioqueue_write,
+ &on_ioqueue_accept,
+ &on_ioqueue_connect
+};
+
+static void init_key_from_transport(transport_key *key,
+ const pjsip_transport_t *tr)
+{
+ /* This is to detect alignment problems. */
+ pj_assert(sizeof(transport_key) == 8);
+
+ key->type = (pj_uint8_t)tr->type;
+ key->zero = 0;
+ key->addr = pj_sockaddr_in_get_addr(&tr->remote_addr).s_addr;
+ key->port = pj_sockaddr_in_get_port(&tr->remote_addr);
+ /*
+ if (key->port == 0) {
+ key->port = pj_sockaddr_in_get_port(&tr->local_addr);
+ }
+ */
+}
+
+#if PJ_HAS_TCP
+static void init_tcp_key(transport_key *key, pjsip_transport_type_e type,
+ const pj_sockaddr_in *addr)
+{
+ /* This is to detect alignment problems. */
+ pj_assert(sizeof(transport_key) == 8);
+
+ key->type = (pj_uint8_t)type;
+ key->zero = 0;
+ key->addr = pj_sockaddr_in_get_addr(addr).s_addr;
+ key->port = pj_sockaddr_in_get_port(addr);
+}
+#endif
+
+static void init_udp_key(transport_key *key, pjsip_transport_type_e type,
+ const pj_sockaddr_in *addr)
+{
+ PJ_UNUSED_ARG(addr);
+
+ /* This is to detect alignment problems. */
+ pj_assert(sizeof(transport_key) == 8);
+
+ pj_memset(key, 0, sizeof(*key));
+ key->type = (pj_uint8_t)type;
+
+#if 0 /* Not sure why we need to make 127.0.0.1 a special case */
+ if (addr->sin_addr.s_addr == inet_addr("127.0.0.1")) {
+ /* This looks more complicated than it is because key->addr uses
+ * the host version of the address (i.e. converted with ntohl()).
+ */
+ pj_str_t localaddr = pj_str("127.0.0.1");
+ pj_sockaddr_in addr;
+ pj_sockaddr_set_str_addr(&addr, &localaddr);
+ key->addr = pj_sockaddr_in_get_addr(&addr);
+ }
+#endif
+}
+
+/*
+ * Get type format name (for pool name).
+ */
+static const char *transport_get_name_format( int type )
+{
+ switch (type) {
+ case PJSIP_TRANSPORT_UDP:
+ return " udp%p";
+#if PJ_HAS_TCP
+ case PJSIP_TRANSPORT_TCP:
+ return " tcp%p";
+ case PJSIP_TRANSPORT_TLS:
+ return " tls%p";
+ case PJSIP_TRANSPORT_SCTP:
+ return "sctp%p";
+#endif
+ }
+ pj_assert(0);
+ return 0;
+}
+
+/*
+ * Get the default SIP port number for the specified type.
+ */
+PJ_DEF(int) pjsip_transport_get_default_port_for_type(pjsip_transport_type_e type)
+{
+ return transport_names[type].port;
+}
+
+/*
+ * Get transport name.
+ */
+static const char *get_type_name(int type)
+{
+ return transport_names[type].name.ptr;
+}
+
+/*
+ * 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;
+ }
+ }
+ return PJSIP_TRANSPORT_UNSPECIFIED;
+}
+
+/*
+ * Create new transmit buffer.
+ */
pj_status_t pjsip_tx_data_create( pjsip_transport_mgr *mgr,
- pjsip_tx_data **p_tdata )
-{
- pj_pool_t *pool;
- pjsip_tx_data *tdata;
+ pjsip_tx_data **p_tdata )
+{
+ pj_pool_t *pool;
+ pjsip_tx_data *tdata;
pj_status_t status;
-
- PJ_LOG(5, ("", "pjsip_tx_data_create"));
+
+ PJ_LOG(5, ("", "pjsip_tx_data_create"));
PJ_ASSERT_RETURN(mgr && p_tdata, PJ_EINVAL);
-
- pool = pjsip_endpt_create_pool( mgr->endpt, "ptdt%p",
- PJSIP_POOL_LEN_TDATA,
- PJSIP_POOL_INC_TDATA );
- if (!pool) {
- return PJ_ENOMEM;
- }
- tdata = pj_pool_calloc(pool, 1, sizeof(pjsip_tx_data));
- tdata->pool = pool;
- tdata->mgr = mgr;
- pj_sprintf(tdata->obj_name,"txd%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;
- }
+
+ pool = pjsip_endpt_create_pool( mgr->endpt, "ptdt%p",
+ PJSIP_POOL_LEN_TDATA,
+ PJSIP_POOL_INC_TDATA );
+ if (!pool) {
+ return PJ_ENOMEM;
+ }
+ tdata = pj_pool_calloc(pool, 1, sizeof(pjsip_tx_data));
+ tdata->pool = pool;
+ tdata->mgr = mgr;
+ pj_sprintf(tdata->obj_name,"txd%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;
+ }
- *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(6,(tdata->obj_name, "destroying txdata"));
- pj_atomic_destroy( tdata->ref_cnt );
- 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;
-}
-
-/*
- * Get the transport type.
- */
-PJ_DEF(pjsip_transport_type_e) pjsip_transport_get_type( const pjsip_transport_t * tr)
-{
- return tr->type;
-}
-
-/*
- * Get transport type from transport flag.
- */
-PJ_DEF(pjsip_transport_type_e) pjsip_get_transport_type_from_flag(unsigned flag)
-{
-#if PJ_HAS_TCP
- if (flag & PJSIP_TRANSPORT_SECURE) {
- return PJSIP_TRANSPORT_TLS;
- } else if (flag & PJSIP_TRANSPORT_RELIABLE) {
- return PJSIP_TRANSPORT_TCP;
- } else
-#else
- PJ_UNUSED_ARG(flag);
-#endif
- {
- return PJSIP_TRANSPORT_UDP;
- }
-}
-
-/*
- * Get the transport type name.
- */
-PJ_DEF(const char *) pjsip_transport_get_type_name( const pjsip_transport_t * tr)
-{
- return get_type_name(tr->type);
-}
-
-/*
- * Get the transport's object name.
- */
-PJ_DEF(const char*) pjsip_transport_get_obj_name( const pjsip_transport_t *tr )
-{
- return tr->obj_name;
-}
-
-/*
- * Get the transport's reference counter.
- */
-PJ_DEF(int) pjsip_transport_get_ref_cnt( const pjsip_transport_t *tr )
-{
- return pj_atomic_get(tr->ref_cnt);
-}
-
-/*
- * Get transport local address.
- */
-PJ_DEF(const pj_sockaddr_in*) pjsip_transport_get_local_addr( pjsip_transport_t *tr )
-{
- return &tr->local_addr;
-}
-
-/*
- * Get address name.
- */
-PJ_DEF(const pj_sockaddr_in*) pjsip_transport_get_addr_name (pjsip_transport_t *tr)
-{
- return &tr->addr_name;
-}
-
-/*
- * Get transport remote address.
- */
-PJ_DEF(const pj_sockaddr_in*) pjsip_transport_get_remote_addr( const pjsip_transport_t *tr )
-{
- return &tr->remote_addr;
-}
-
-/*
- * Get transport flag.
- */
-PJ_DEF(unsigned) pjsip_transport_get_flag( const pjsip_transport_t * tr )
-{
- return tr->flag;
-}
-
-/*
- * Add reference to the specified transport.
- */
-PJ_DEF(void) pjsip_transport_add_ref( pjsip_transport_t * tr )
-{
- pj_atomic_inc(tr->ref_cnt);
-}
-
-/*
- * Decrease the reference time of the transport.
- */
-PJ_DEF(void) pjsip_transport_dec_ref( pjsip_transport_t *tr )
-{
- pj_assert(tr->ref_cnt > 0);
- if (pj_atomic_dec_and_get(tr->ref_cnt) == 0) {
- pj_gettimeofday(&tr->close_time);
- tr->close_time.sec += PJSIP_TRANSPORT_CLOSE_TIMEOUT;
- }
-}
-
-/*
- * Open the underlying transport.
- */
-static pj_status_t create_socket( pjsip_transport_type_e type,
+ *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(6,(tdata->obj_name, "destroying txdata"));
+ pj_atomic_destroy( tdata->ref_cnt );
+ 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;
+}
+
+/*
+ * Get the transport type.
+ */
+PJ_DEF(pjsip_transport_type_e) pjsip_transport_get_type( const pjsip_transport_t * tr)
+{
+ return tr->type;
+}
+
+/*
+ * Get transport type from transport flag.
+ */
+PJ_DEF(pjsip_transport_type_e) pjsip_get_transport_type_from_flag(unsigned flag)
+{
+#if PJ_HAS_TCP
+ if (flag & PJSIP_TRANSPORT_SECURE) {
+ return PJSIP_TRANSPORT_TLS;
+ } else if (flag & PJSIP_TRANSPORT_RELIABLE) {
+ return PJSIP_TRANSPORT_TCP;
+ } else
+#else
+ PJ_UNUSED_ARG(flag);
+#endif
+ {
+ return PJSIP_TRANSPORT_UDP;
+ }
+}
+
+/*
+ * Get the transport type name.
+ */
+PJ_DEF(const char *) pjsip_transport_get_type_name( const pjsip_transport_t * tr)
+{
+ return get_type_name(tr->type);
+}
+
+/*
+ * Get the transport's object name.
+ */
+PJ_DEF(const char*) pjsip_transport_get_obj_name( const pjsip_transport_t *tr )
+{
+ return tr->obj_name;
+}
+
+/*
+ * Get the transport's reference counter.
+ */
+PJ_DEF(int) pjsip_transport_get_ref_cnt( const pjsip_transport_t *tr )
+{
+ return pj_atomic_get(tr->ref_cnt);
+}
+
+/*
+ * Get transport local address.
+ */
+PJ_DEF(const pj_sockaddr_in*) pjsip_transport_get_local_addr( pjsip_transport_t *tr )
+{
+ return &tr->local_addr;
+}
+
+/*
+ * Get address name.
+ */
+PJ_DEF(const pj_sockaddr_in*) pjsip_transport_get_addr_name (pjsip_transport_t *tr)
+{
+ return &tr->addr_name;
+}
+
+/*
+ * Get transport remote address.
+ */
+PJ_DEF(const pj_sockaddr_in*) pjsip_transport_get_remote_addr( const pjsip_transport_t *tr )
+{
+ return &tr->remote_addr;
+}
+
+/*
+ * Get transport flag.
+ */
+PJ_DEF(unsigned) pjsip_transport_get_flag( const pjsip_transport_t * tr )
+{
+ return tr->flag;
+}
+
+/*
+ * Add reference to the specified transport.
+ */
+PJ_DEF(void) pjsip_transport_add_ref( pjsip_transport_t * tr )
+{
+ pj_atomic_inc(tr->ref_cnt);
+}
+
+/*
+ * Decrease the reference time of the transport.
+ */
+PJ_DEF(void) pjsip_transport_dec_ref( pjsip_transport_t *tr )
+{
+ pj_assert(tr->ref_cnt > 0);
+ if (pj_atomic_dec_and_get(tr->ref_cnt) == 0) {
+ pj_gettimeofday(&tr->close_time);
+ tr->close_time.sec += PJSIP_TRANSPORT_CLOSE_TIMEOUT;
+ }
+}
+
+/*
+ * Open the underlying transport.
+ */
+static pj_status_t create_socket( pjsip_transport_type_e type,
pj_sockaddr_in *local,
- pj_sock_t *p_sock)
-{
- int sock_family;
- int sock_type;
- int sock_proto;
+ pj_sock_t *p_sock)
+{
+ int sock_family;
+ int sock_type;
+ int sock_proto;
int len;
- pj_status_t status;
- pj_sock_t sock;
-
- /* Set socket parameters */
- if (type == PJSIP_TRANSPORT_UDP) {
- sock_family = PJ_AF_INET;
- sock_type = PJ_SOCK_DGRAM;
- sock_proto = 0;
-
-#if PJ_HAS_TCP
- } else if (type == PJSIP_TRANSPORT_TCP) {
- sock_family = PJ_AF_INET;
- sock_type = PJ_SOCK_STREAM;
- sock_proto = 0;
-#endif
- } else {
- return PJ_EINVAL;
- }
-
- /* Create socket. */
- status = pj_sock_socket( sock_family, sock_type, sock_proto, &sock);
- if (status != PJ_SUCCESS)
- return status;
-
- /* Bind the socket to the requested address, or if no address is
- * specified, let the operating system chooses the address.
- */
- if (/*local->sin_addr.s_addr != 0 &&*/ local->sin_port != 0) {
+ pj_status_t status;
+ pj_sock_t sock;
+
+ /* Set socket parameters */
+ if (type == PJSIP_TRANSPORT_UDP) {
+ sock_family = PJ_AF_INET;
+ sock_type = PJ_SOCK_DGRAM;
+ sock_proto = 0;
+
+#if PJ_HAS_TCP
+ } else if (type == PJSIP_TRANSPORT_TCP) {
+ sock_family = PJ_AF_INET;
+ sock_type = PJ_SOCK_STREAM;
+ sock_proto = 0;
+#endif
+ } else {
+ return PJ_EINVAL;
+ }
+
+ /* Create socket. */
+ status = pj_sock_socket( sock_family, sock_type, sock_proto, &sock);
+ if (status != PJ_SUCCESS)
+ return status;
+
+ /* Bind the socket to the requested address, or if no address is
+ * specified, let the operating system chooses the address.
+ */
+ if (/*local->sin_addr.s_addr != 0 &&*/ local->sin_port != 0) {
/* Bind to the requested address. */
- status = pj_sock_bind(sock, local, sizeof(*local));
- if (status != PJ_SUCCESS) {
- pj_sock_close(sock);
- return status;
- }
- } else if (type == PJSIP_TRANSPORT_UDP) {
- /* Only for UDP sockets: bind to any address so that the operating
- * system allocates the port for us. For TCP, let the OS implicitly
- * bind the socket with connect() syscall (if we bind now, then we'll
- * get 0.0.0.0 as local address).
- */
- pj_memset(local, 0, sizeof(*local));
+ status = pj_sock_bind(sock, local, sizeof(*local));
+ if (status != PJ_SUCCESS) {
+ pj_sock_close(sock);
+ return status;
+ }
+ } else if (type == PJSIP_TRANSPORT_UDP) {
+ /* Only for UDP sockets: bind to any address so that the operating
+ * system allocates the port for us. For TCP, let the OS implicitly
+ * bind the socket with connect() syscall (if we bind now, then we'll
+ * get 0.0.0.0 as local address).
+ */
+ pj_memset(local, 0, sizeof(*local));
local->sin_family = PJ_AF_INET;
- status = pj_sock_bind(sock, local, sizeof(*local));
- if (status != PJ_SUCCESS) {
- pj_sock_close(sock);
- return status;
- }
-
- /* Get the local address. */
+ status = pj_sock_bind(sock, local, sizeof(*local));
+ if (status != PJ_SUCCESS) {
+ pj_sock_close(sock);
+ return status;
+ }
+
+ /* Get the local address. */
len = sizeof(pj_sockaddr_in);
- status = pj_sock_getsockname(sock, local, &len);
- if (status != PJ_SUCCESS) {
- pj_sock_close(sock);
- return status;
- }
- }
+ status = pj_sock_getsockname(sock, local, &len);
+ if (status != PJ_SUCCESS) {
+ pj_sock_close(sock);
+ return status;
+ }
+ }
- *p_sock = sock;
- return PJ_SUCCESS;
-}
-
-/*
- * Close the transport.
- */
-static void destroy_socket( pjsip_transport_t * tr)
-{
- pj_assert( pj_atomic_get(tr->ref_cnt) == 0);
- pj_sock_close(tr->sock);
- tr->sock = -1;
-}
-
-/*
- * Create a new transport object.
- */
-static pj_status_t create_transport( pjsip_transport_mgr *mgr,
- pjsip_transport_type_e type,
- pj_sock_t sock_hnd,
- const pj_sockaddr_in *local_addr,
+ *p_sock = sock;
+ return PJ_SUCCESS;
+}
+
+/*
+ * Close the transport.
+ */
+static void destroy_socket( pjsip_transport_t * tr)
+{
+ pj_assert( pj_atomic_get(tr->ref_cnt) == 0);
+ pj_sock_close(tr->sock);
+ tr->sock = -1;
+}
+
+/*
+ * Create a new transport object.
+ */
+static pj_status_t create_transport( pjsip_transport_mgr *mgr,
+ pjsip_transport_type_e type,
+ pj_sock_t sock_hnd,
+ const pj_sockaddr_in *local_addr,
const pj_sockaddr_in *addr_name,
- pjsip_transport_t **p_transport )
-{
- pj_pool_t *tr_pool=NULL, *rdata_pool=NULL;
+ pjsip_transport_t **p_transport )
+{
+ pj_pool_t *tr_pool=NULL, *rdata_pool=NULL;
pjsip_transport_t *tr = NULL;
- pj_status_t status;
-
- /* Allocate pool for transport from endpoint. */
- tr_pool = pjsip_endpt_create_pool( mgr->endpt,
- transport_get_name_format(type),
- PJSIP_POOL_LEN_TRANSPORT,
- PJSIP_POOL_INC_TRANSPORT );
- if (!tr_pool) {
- status = PJ_ENOMEM;
- goto on_error;
- }
-
- /* Allocate pool for rdata from endpoint. */
- rdata_pool = pjsip_endpt_create_pool( mgr->endpt,
- "prdt%p",
- PJSIP_POOL_LEN_RDATA,
- PJSIP_POOL_INC_RDATA );
- if (!rdata_pool) {
- status = PJ_ENOMEM;
- goto on_error;
- }
-
- /* Allocate and initialize the transport. */
- tr = pj_pool_calloc(tr_pool, 1, sizeof(*tr));
- tr->pool = tr_pool;
- tr->type = type;
- tr->mgr = mgr;
- tr->sock = sock_hnd;
- pj_memcpy(&tr->local_addr, local_addr, sizeof(pj_sockaddr_in));
- pj_list_init(&tr->cb_list);
- pj_sprintf(tr->obj_name, transport_get_name_format(type), tr);
-
- if (type != PJSIP_TRANSPORT_UDP) {
- tr->flag |= PJSIP_TRANSPORT_RELIABLE;
- }
-
- /* Address name. */
- if (addr_name == NULL) {
- addr_name = &tr->local_addr;
- }
- pj_memcpy(&tr->addr_name, addr_name, sizeof(*addr_name));
-
- /* Create atomic */
- status = pj_atomic_create(tr_pool, 0, &tr->ref_cnt);
- if (status != PJ_SUCCESS)
- goto on_error;
-
- /* Init rdata in the transport. */
- tr->rdata = pj_pool_alloc(rdata_pool, sizeof(*tr->rdata));
- tr->rdata->pool = rdata_pool;
- tr->rdata->len = 0;
- tr->rdata->transport = tr;
-
- /* Init transport mutex. */
- status = pj_mutex_create_recursive(tr_pool, "mtr%p", &tr->tr_mutex);
- if (status != PJ_SUCCESS)
- goto on_error;
-
- /* Register to I/O Queue */
- status = pj_ioqueue_register_sock( tr_pool, mgr->ioqueue,
- tr->sock, tr,
- &ioqueue_transport_callback,
- &tr->key);
- if (status != PJ_SUCCESS)
- goto on_error;
+ pj_status_t status;
- *p_transport = tr;
- return PJ_SUCCESS;
-
-on_error:
- if (tr && tr->tr_mutex) {
- pj_mutex_destroy(tr->tr_mutex);
- }
- if (tr_pool) {
- pjsip_endpt_destroy_pool(mgr->endpt, tr_pool);
- }
- if (rdata_pool) {
- pjsip_endpt_destroy_pool(mgr->endpt, rdata_pool);
- }
- return status;
-}
-
-/*
- * Destroy transport.
- */
-static void destroy_transport( pjsip_transport_mgr *mgr, pjsip_transport_t *tr)
-{
- transport_key hash_key;
-
- /* Remove from I/O queue. */
- pj_ioqueue_unregister( tr->key );
-
- /* Remove from hash table */
- init_key_from_transport(&hash_key, tr);
- pj_hash_set(NULL, mgr->transport_table, &hash_key, sizeof(hash_key), NULL);
-
- /* Close transport. */
- destroy_socket(tr);
-
- /* Destroy the transport mutex. */
- pj_mutex_destroy(tr->tr_mutex);
-
- /* Destroy atomic */
- pj_atomic_destroy( tr->ref_cnt );
-
- /* Release the pool associated with the rdata. */
- pjsip_endpt_destroy_pool(mgr->endpt, tr->rdata->pool );
-
- /* Release the pool associated with the transport. */
- pjsip_endpt_destroy_pool(mgr->endpt, tr->pool );
-}
-
-
+ /* Allocate pool for transport from endpoint. */
+ tr_pool = pjsip_endpt_create_pool( mgr->endpt,
+ transport_get_name_format(type),
+ PJSIP_POOL_LEN_TRANSPORT,
+ PJSIP_POOL_INC_TRANSPORT );
+ if (!tr_pool) {
+ status = PJ_ENOMEM;
+ goto on_error;
+ }
+
+ /* Allocate pool for rdata from endpoint. */
+ rdata_pool = pjsip_endpt_create_pool( mgr->endpt,
+ "prdt%p",
+ PJSIP_POOL_LEN_RDATA,
+ PJSIP_POOL_INC_RDATA );
+ if (!rdata_pool) {
+ status = PJ_ENOMEM;
+ goto on_error;
+ }
+
+ /* Allocate and initialize the transport. */
+ tr = pj_pool_calloc(tr_pool, 1, sizeof(*tr));
+ tr->pool = tr_pool;
+ tr->type = type;
+ tr->mgr = mgr;
+ tr->sock = sock_hnd;
+ pj_memcpy(&tr->local_addr, local_addr, sizeof(pj_sockaddr_in));
+ pj_list_init(&tr->cb_list);
+ pj_sprintf(tr->obj_name, transport_get_name_format(type), tr);
+
+ if (type != PJSIP_TRANSPORT_UDP) {
+ tr->flag |= PJSIP_TRANSPORT_RELIABLE;
+ }
+
+ /* Address name. */
+ if (addr_name == NULL) {
+ addr_name = &tr->local_addr;
+ }
+ pj_memcpy(&tr->addr_name, addr_name, sizeof(*addr_name));
+
+ /* Create atomic */
+ status = pj_atomic_create(tr_pool, 0, &tr->ref_cnt);
+ if (status != PJ_SUCCESS)
+ goto on_error;
+
+ /* Init rdata in the transport. */
+ tr->rdata = pj_pool_alloc(rdata_pool, sizeof(*tr->rdata));
+ tr->rdata->pool = rdata_pool;
+ tr->rdata->len = 0;
+ tr->rdata->transport = tr;
+
+ /* Init transport mutex. */
+ status = pj_mutex_create_recursive(tr_pool, "mtr%p", &tr->tr_mutex);
+ if (status != PJ_SUCCESS)
+ goto on_error;
+
+ /* Register to I/O Queue */
+ status = pj_ioqueue_register_sock( tr_pool, mgr->ioqueue,
+ tr->sock, tr,
+ &ioqueue_transport_callback,
+ &tr->key);
+ if (status != PJ_SUCCESS)
+ goto on_error;
+
+ *p_transport = tr;
+ return PJ_SUCCESS;
+
+on_error:
+ if (tr && tr->tr_mutex) {
+ pj_mutex_destroy(tr->tr_mutex);
+ }
+ if (tr_pool) {
+ pjsip_endpt_destroy_pool(mgr->endpt, tr_pool);
+ }
+ if (rdata_pool) {
+ pjsip_endpt_destroy_pool(mgr->endpt, rdata_pool);
+ }
+ return status;
+}
+
+/*
+ * Destroy transport.
+ */
+static void destroy_transport( pjsip_transport_mgr *mgr, pjsip_transport_t *tr)
+{
+ transport_key hash_key;
+
+ /* Remove from I/O queue. */
+ pj_ioqueue_unregister( tr->key );
+
+ /* Remove from hash table */
+ init_key_from_transport(&hash_key, tr);
+ pj_hash_set(NULL, mgr->transport_table, &hash_key, sizeof(hash_key), NULL);
+
+ /* Close transport. */
+ destroy_socket(tr);
+
+ /* Destroy the transport mutex. */
+ pj_mutex_destroy(tr->tr_mutex);
+
+ /* Destroy atomic */
+ pj_atomic_destroy( tr->ref_cnt );
+
+ /* Release the pool associated with the rdata. */
+ pjsip_endpt_destroy_pool(mgr->endpt, tr->rdata->pool );
+
+ /* Release the pool associated with the transport. */
+ pjsip_endpt_destroy_pool(mgr->endpt, tr->pool );
+}
+
+
static pj_status_t transport_send_msg( pjsip_transport_t *tr,
- pjsip_tx_data *tdata,
+ pjsip_tx_data *tdata,
const pj_sockaddr_in *addr,
- pj_ssize_t *p_sent)
-{
- const char *buf = tdata->buf.start;
- pj_ssize_t size;
- pj_status_t status;
+ pj_ssize_t *p_sent)
+{
+ const char *buf = tdata->buf.start;
+ pj_ssize_t size;
+ pj_status_t status;
/* Can only send if tdata is not being sent! */
if (pj_ioqueue_is_pending(tr->key, &tdata->op_key))
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;
- }
-
- /* Print the message if it's not printed */
- if (tdata->buf.cur <= tdata->buf.start) {
- size = pjsip_msg_print( tdata->msg, tdata->buf.start,
- tdata->buf.end - tdata->buf.start);
+
+ /* 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;
+ }
+
+ /* Print the message if it's not printed */
+ if (tdata->buf.cur <= tdata->buf.start) {
+ size = pjsip_msg_print( tdata->msg, tdata->buf.start,
+ tdata->buf.end - tdata->buf.start);
if (size < 0) {
- return PJSIP_EMSGTOOLONG;
+ return PJSIP_EMSGTOOLONG;
}
- pj_assert(size != 0);
- tdata->buf.cur += size;
- tdata->buf.cur[size] = '\0';
- }
-
- /* Send the message. */
- buf = tdata->buf.start;
- size = tdata->buf.cur - tdata->buf.start;
-
- if (tr->type == PJSIP_TRANSPORT_UDP) {
- PJ_LOG(4,(tr->obj_name, "sendto %s:%d, %d bytes, data:\n"
- "----------- begin msg ------------\n"
- "%s"
- "------------ end msg -------------",
- pj_inet_ntoa(addr->sin_addr),
- pj_sockaddr_in_get_port(addr),
- size, buf));
+ pj_assert(size != 0);
+ tdata->buf.cur += size;
+ tdata->buf.cur[size] = '\0';
+ }
- status = pj_ioqueue_sendto( tr->key, &tdata->op_key,
- buf, &size, 0, addr, sizeof(*addr));
- }
-#if PJ_HAS_TCP
- else {
- PJ_LOG(4,(tr->obj_name, "sending %d bytes, data:\n"
- "----------- begin msg ------------\n"
- "%s"
- "------------ end msg -------------",
- size, buf));
-
- status = pj_ioqueue_send(tr->key, &tdata->op_key, buf, &size, 0);
- }
-#else
- else {
+ /* Send the message. */
+ buf = tdata->buf.start;
+ size = tdata->buf.cur - tdata->buf.start;
+
+ if (tr->type == PJSIP_TRANSPORT_UDP) {
+ PJ_LOG(4,(tr->obj_name, "sendto %s:%d, %d bytes, data:\n"
+ "----------- begin msg ------------\n"
+ "%s"
+ "------------ end msg -------------",
+ pj_inet_ntoa(addr->sin_addr),
+ pj_sockaddr_in_get_port(addr),
+ size, buf));
+
+ status = pj_ioqueue_sendto( tr->key, &tdata->op_key,
+ buf, &size, 0, addr, sizeof(*addr));
+ }
+#if PJ_HAS_TCP
+ else {
+ PJ_LOG(4,(tr->obj_name, "sending %d bytes, data:\n"
+ "----------- begin msg ------------\n"
+ "%s"
+ "------------ end msg -------------",
+ size, buf));
+
+ status = pj_ioqueue_send(tr->key, &tdata->op_key, buf, &size, 0);
+ }
+#else
+ else {
pj_assert(!"Unsupported transport");
- status = PJSIP_EUNSUPTRANSPORT;
- }
-#endif
+ status = PJSIP_EUNSUPTRANSPORT;
+ }
+#endif
- *p_sent = size;
- return status;
-}
-
-/*
- * Send a SIP message using the specified transport, to the address specified
- * in the outgoing data.
- */
-PJ_DEF(pj_status_t) pjsip_transport_send_msg( pjsip_transport_t *tr,
- pjsip_tx_data *tdata,
+ *p_sent = size;
+ return status;
+}
+
+/*
+ * Send a SIP message using the specified transport, to the address specified
+ * in the outgoing data.
+ */
+PJ_DEF(pj_status_t) pjsip_transport_send_msg( pjsip_transport_t *tr,
+ pjsip_tx_data *tdata,
const pj_sockaddr_in *addr,
- pj_ssize_t *sent)
-{
- PJ_LOG(5, (tr->obj_name, "pjsip_transport_send_msg(tdata=%s)", tdata->obj_name));
-
- return transport_send_msg(tr, tdata, addr, sent );
-}
-
-///////////////////////////////////////////////////////////////////////////////
-
-/*
- * Create a new transport manager.
- */
-PJ_DEF(pj_status_t) pjsip_transport_mgr_create( pj_pool_t *pool,
- pjsip_endpoint * endpt,
+ pj_ssize_t *sent)
+{
+ PJ_LOG(5, (tr->obj_name, "pjsip_transport_send_msg(tdata=%s)", tdata->obj_name));
+
+ return transport_send_msg(tr, tdata, addr, sent );
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+/*
+ * Create a new transport manager.
+ */
+PJ_DEF(pj_status_t) pjsip_transport_mgr_create( pj_pool_t *pool,
+ pjsip_endpoint * endpt,
void (*cb)(pjsip_endpoint*,
pjsip_rx_data *),
- pjsip_transport_mgr **p_mgr)
-{
- pjsip_transport_mgr *mgr;
+ pjsip_transport_mgr **p_mgr)
+{
+ pjsip_transport_mgr *mgr;
pj_status_t status;
-
- PJ_LOG(5, (LOG_TRANSPORT_MGR, "pjsip_transport_mgr_create()"));
-
- mgr = pj_pool_alloc(pool, sizeof(*mgr));
- mgr->endpt = endpt;
- mgr->message_callback = cb;
+
+ PJ_LOG(5, (LOG_TRANSPORT_MGR, "pjsip_transport_mgr_create()"));
+
+ mgr = pj_pool_alloc(pool, sizeof(*mgr));
+ mgr->endpt = endpt;
+ mgr->message_callback = cb;
mgr->send_buf_size = DEFAULT_SO_SNDBUF;
mgr->recv_buf_size = DEFAULT_SO_RCVBUF;
-
- mgr->transport_table = pj_hash_create(pool, MGR_HASH_TABLE_SIZE);
- if (!mgr->transport_table) {
- return PJ_ENOMEM;
- }
- status = pj_ioqueue_create(pool, PJSIP_MAX_TRANSPORTS, &mgr->ioqueue);
- if (status != PJ_SUCCESS) {
- return status;
- }
- status = pj_mutex_create_recursive(pool, "tmgr%p", &mgr->mutex);
- if (status != PJ_SUCCESS) {
- pj_ioqueue_destroy(mgr->ioqueue);
- return status;
- }
- pj_gettimeofday(&mgr->next_idle_check);
+
+ mgr->transport_table = pj_hash_create(pool, MGR_HASH_TABLE_SIZE);
+ if (!mgr->transport_table) {
+ return PJ_ENOMEM;
+ }
+ status = pj_ioqueue_create(pool, PJSIP_MAX_TRANSPORTS, &mgr->ioqueue);
+ if (status != PJ_SUCCESS) {
+ return status;
+ }
+ status = pj_mutex_create_recursive(pool, "tmgr%p", &mgr->mutex);
+ if (status != PJ_SUCCESS) {
+ pj_ioqueue_destroy(mgr->ioqueue);
+ return status;
+ }
+ pj_gettimeofday(&mgr->next_idle_check);
mgr->next_idle_check.sec += MGR_IDLE_CHECK_INTERVAL;
- *p_mgr = mgr;
- return status;
-}
-
-/*
- * Destroy transport manager.
- */
-PJ_DEF(pj_status_t) pjsip_transport_mgr_destroy( pjsip_transport_mgr *mgr )
-{
- pj_hash_iterator_t itr_val;
- pj_hash_iterator_t *itr;
-
- PJ_LOG(5, (LOG_TRANSPORT_MGR, "pjsip_transport_mgr_destroy()"));
-
- pj_mutex_lock(mgr->mutex);
-
- itr = pjsip_transport_first(mgr, &itr_val);
- while (itr != NULL) {
- pj_hash_iterator_t *next;
- pjsip_transport_t *transport;
-
- transport = pjsip_transport_this(mgr, itr);
-
- next = pjsip_transport_next(mgr, itr);
-
- pj_atomic_set(transport->ref_cnt, 0);
- destroy_transport( mgr, transport);
-
- itr = next;
- }
- pj_ioqueue_destroy(mgr->ioqueue);
-
+ *p_mgr = mgr;
+ return status;
+}
+
+/*
+ * Destroy transport manager.
+ */
+PJ_DEF(pj_status_t) pjsip_transport_mgr_destroy( pjsip_transport_mgr *mgr )
+{
+ pj_hash_iterator_t itr_val;
+ pj_hash_iterator_t *itr;
+
+ PJ_LOG(5, (LOG_TRANSPORT_MGR, "pjsip_transport_mgr_destroy()"));
+
+ pj_mutex_lock(mgr->mutex);
+
+ itr = pjsip_transport_first(mgr, &itr_val);
+ while (itr != NULL) {
+ pj_hash_iterator_t *next;
+ pjsip_transport_t *transport;
+
+ transport = pjsip_transport_this(mgr, itr);
+
+ next = pjsip_transport_next(mgr, itr);
+
+ pj_atomic_set(transport->ref_cnt, 0);
+ destroy_transport( mgr, transport);
+
+ itr = next;
+ }
+ pj_ioqueue_destroy(mgr->ioqueue);
+
pj_mutex_unlock(mgr->mutex);
- return PJ_SUCCESS;
-}
-
-/*
- * Create listener
- */
-static pj_status_t create_listener( pjsip_transport_mgr *mgr,
- pjsip_transport_type_e type,
- pj_sock_t sock_hnd,
- pj_sockaddr_in *local_addr,
- const pj_sockaddr_in *addr_name)
-{
- pjsip_transport_t *tr;
- struct transport_key *hash_key;
- const pj_str_t loopback_addr = { "127.0.0.1", 9 };
- pj_status_t status;
+ return PJ_SUCCESS;
+}
- if (mgr->send_buf_size != 0) {
+/*
+ * Create listener
+ */
+static pj_status_t create_listener( pjsip_transport_mgr *mgr,
+ pjsip_transport_type_e type,
+ pj_sock_t sock_hnd,
+ pj_sockaddr_in *local_addr,
+ const pj_sockaddr_in *addr_name)
+{
+ pjsip_transport_t *tr;
+ struct transport_key *hash_key;
+ const pj_str_t loopback_addr = { "127.0.0.1", 9 };
+ pj_status_t status;
+
+ if (mgr->send_buf_size != 0) {
int opt_val = mgr->send_buf_size;
status = pj_sock_setsockopt( sock_hnd, PJ_SOL_SOCKET,
PJ_SO_SNDBUF,
&opt_val, sizeof(opt_val));
-
- if (status != PJ_SUCCESS) {
- return status;
- }
- }
- if (mgr->recv_buf_size != 0) {
+ if (status != PJ_SUCCESS) {
+ return status;
+ }
+ }
+
+ if (mgr->recv_buf_size != 0) {
int opt_val = mgr->recv_buf_size;
status = pj_sock_setsockopt( sock_hnd, PJ_SOL_SOCKET,
PJ_SO_RCVBUF,
- &opt_val, sizeof(opt_val));
- if (status != PJ_SUCCESS) {
- return status;
+ &opt_val, sizeof(opt_val));
+ if (status != PJ_SUCCESS) {
+ return status;
}
- }
-
- status = create_transport(mgr, type, sock_hnd, local_addr, addr_name, &tr);
- if (status != PJ_SUCCESS) {
- pj_sock_close(sock_hnd);
- return status;
- }
-#if PJ_HAS_TCP
- if (type == PJSIP_TRANSPORT_TCP) {
+ }
- status = pj_sock_listen(tr->sock, BACKLOG);
- if (status != 0) {
- destroy_transport(mgr, tr);
- return status;
- }
+ status = create_transport(mgr, type, sock_hnd, local_addr, addr_name, &tr);
+ if (status != PJ_SUCCESS) {
+ pj_sock_close(sock_hnd);
+ return status;
+ }
+#if PJ_HAS_TCP
+ if (type == PJSIP_TRANSPORT_TCP) {
+
+ status = pj_sock_listen(tr->sock, BACKLOG);
+ if (status != 0) {
+ destroy_transport(mgr, tr);
+ return status;
+ }
/* Discard immediate connections. */
do {
- tr->accept_data.addrlen = sizeof(tr->accept_data.local);
- status = pj_ioqueue_accept(tr->key, &tr->accept_op,
- &tr->accept_data.sock,
- &tr->accept_data.local,
- &tr->accept_data.remote,
+ tr->accept_data.addrlen = sizeof(tr->accept_data.local);
+ status = pj_ioqueue_accept(tr->key, &tr->accept_op,
+ &tr->accept_data.sock,
+ &tr->accept_data.local,
+ &tr->accept_data.remote,
&tr->accept_data.addrlen);
if (status==PJ_SUCCESS) {
pj_sock_close(tr->accept_data.sock);
- } else if (status != PJ_EPENDING) {
- destroy_transport(mgr, tr);
- return status;
+ } else if (status != PJ_EPENDING) {
+ destroy_transport(mgr, tr);
+ return status;
}
- } while (status==PJ_SUCCESS);
-
- } else
-#endif
+ } while (status==PJ_SUCCESS);
+
+ } else
+#endif
if (type == PJSIP_TRANSPORT_UDP) {
pj_ssize_t bytes;
/* Discard immediate data. */
- do {
+ do {
tr->rdata->addr_len = sizeof(tr->rdata->addr);
- bytes = PJSIP_MAX_PKT_LEN;
- status = pj_ioqueue_recvfrom( tr->key, &tr->rdata->op_key,
- tr->rdata->packet, &bytes, 0,
- &tr->rdata->addr,
+ bytes = PJSIP_MAX_PKT_LEN;
+ status = pj_ioqueue_recvfrom( tr->key, &tr->rdata->op_key,
+ tr->rdata->packet, &bytes, 0,
+ &tr->rdata->addr,
&tr->rdata->addr_len);
if (status == PJ_SUCCESS) {
- ;
- } else if (status != PJ_EPENDING) {
- destroy_transport(mgr, tr);
- return status;
+ ;
+ } else if (status != PJ_EPENDING) {
+ destroy_transport(mgr, tr);
+ return status;
}
- } while (status == PJ_SUCCESS);
- }
-
- pj_atomic_set(tr->ref_cnt, 1);
-
- /* Listeners normally have no remote address */
- pj_memset(&tr->remote_addr, 0, sizeof(tr->remote_addr));
-
- /* Set remote address to 127.0.0.1 for UDP socket bound to 127.0.0.1.
- * See further comments on struct pjsip_transport_t definition.
+ } while (status == PJ_SUCCESS);
+ }
+
+ pj_atomic_set(tr->ref_cnt, 1);
+
+ /* Listeners normally have no remote address */
+ pj_memset(&tr->remote_addr, 0, sizeof(tr->remote_addr));
+
+ /* Set remote address to 127.0.0.1 for UDP socket bound to 127.0.0.1.
+ * See further comments on struct pjsip_transport_t definition.
*/
if (type == PJSIP_TRANSPORT_UDP &&
local_addr->sin_addr.s_addr == pj_inet_addr(&loopback_addr).s_addr)
- {
- pj_str_t localaddr = pj_str("127.0.0.1");
- pj_sockaddr_in_set_str_addr( &tr->remote_addr, &localaddr);
- }
- hash_key = pj_pool_alloc(tr->pool, sizeof(transport_key));
- init_key_from_transport(hash_key, tr);
-
- pj_mutex_lock(mgr->mutex);
- pj_hash_set(tr->pool, mgr->transport_table,
- hash_key, sizeof(transport_key), tr);
- pj_mutex_unlock(mgr->mutex);
-
- PJ_LOG(4,(tr->obj_name, "Listening at %s %s:%d",
- get_type_name(tr->type),
- pj_inet_ntoa(tr->local_addr.sin_addr),
- pj_sockaddr_in_get_port(&tr->local_addr)));
- PJ_LOG(4,(tr->obj_name, "Listener public address is at %s %s:%d",
- get_type_name(tr->type),
- pj_inet_ntoa(tr->addr_name.sin_addr),
- pj_sockaddr_in_get_port(&tr->addr_name)));
- return PJ_SUCCESS;
-}
-
-/*
- * Create listener.
- */
-PJ_DEF(pj_status_t) pjsip_create_listener( pjsip_transport_mgr *mgr,
- pjsip_transport_type_e type,
- pj_sockaddr_in *local_addr,
- const pj_sockaddr_in *addr_name)
-{
- pj_sock_t sock_hnd;
- pj_status_t status;
-
- PJ_LOG(5, (LOG_TRANSPORT_MGR, "pjsip_create_listener(type=%d)", type));
-
- status = create_socket(type, local_addr, &sock_hnd);
- if (status != PJ_SUCCESS) {
- return status;
- }
-
- return create_listener(mgr, type, sock_hnd, local_addr, addr_name);
-}
-
-/*
- * Create UDP listener.
- */
-PJ_DEF(pj_status_t) pjsip_create_udp_listener( pjsip_transport_mgr *mgr,
- pj_sock_t sock,
- const pj_sockaddr_in *addr_name)
-{
- pj_sockaddr_in local_addr;
- pj_status_t status;
- int addrlen = sizeof(local_addr);
+ {
+ pj_str_t localaddr = pj_str("127.0.0.1");
+ pj_sockaddr_in_set_str_addr( &tr->remote_addr, &localaddr);
+ }
+ hash_key = pj_pool_alloc(tr->pool, sizeof(transport_key));
+ init_key_from_transport(hash_key, tr);
- status = pj_sock_getsockname(sock, (pj_sockaddr_t*)&local_addr, &addrlen);
- if (status != PJ_SUCCESS)
- return status;
-
+ pj_mutex_lock(mgr->mutex);
+ pj_hash_set(tr->pool, mgr->transport_table,
+ hash_key, sizeof(transport_key), tr);
+ pj_mutex_unlock(mgr->mutex);
+
+ PJ_LOG(4,(tr->obj_name, "Listening at %s %s:%d",
+ get_type_name(tr->type),
+ pj_inet_ntoa(tr->local_addr.sin_addr),
+ pj_sockaddr_in_get_port(&tr->local_addr)));
+ PJ_LOG(4,(tr->obj_name, "Listener public address is at %s %s:%d",
+ get_type_name(tr->type),
+ pj_inet_ntoa(tr->addr_name.sin_addr),
+ pj_sockaddr_in_get_port(&tr->addr_name)));
+ return PJ_SUCCESS;
+}
+
+/*
+ * Create listener.
+ */
+PJ_DEF(pj_status_t) pjsip_create_listener( pjsip_transport_mgr *mgr,
+ pjsip_transport_type_e type,
+ pj_sockaddr_in *local_addr,
+ const pj_sockaddr_in *addr_name)
+{
+ pj_sock_t sock_hnd;
+ pj_status_t status;
+
+ PJ_LOG(5, (LOG_TRANSPORT_MGR, "pjsip_create_listener(type=%d)", type));
+
+ status = create_socket(type, local_addr, &sock_hnd);
+ if (status != PJ_SUCCESS) {
+ return status;
+ }
+
+ return create_listener(mgr, type, sock_hnd, local_addr, addr_name);
+}
+
+/*
+ * Create UDP listener.
+ */
+PJ_DEF(pj_status_t) pjsip_create_udp_listener( pjsip_transport_mgr *mgr,
+ pj_sock_t sock,
+ const pj_sockaddr_in *addr_name)
+{
+ pj_sockaddr_in local_addr;
+ pj_status_t status;
+ int addrlen = sizeof(local_addr);
+
+ status = pj_sock_getsockname(sock, (pj_sockaddr_t*)&local_addr, &addrlen);
+ if (status != PJ_SUCCESS)
+ return status;
+
return create_listener(mgr, PJSIP_TRANSPORT_UDP, sock,
- &local_addr, addr_name);
-}
-
-/*
- * Find transport to be used to send message to remote destination. If no
- * suitable transport is found, a new one will be created.
- */
-PJ_DEF(void) pjsip_transport_get( pjsip_transport_mgr *mgr,
- pj_pool_t *pool,
- pjsip_transport_type_e type,
- const pj_sockaddr_in *remote,
- void *token,
- pjsip_transport_completion_callback *cb)
-{
- transport_key search_key, *hash_key;
- pjsip_transport_t *tr;
- pj_sockaddr_in local;
- pj_sock_t sock_hnd;
- pj_status_t status;
- struct transport_callback *cb_rec;
-
- PJ_LOG(5, (LOG_TRANSPORT_MGR, "pjsip_transport_get()"));
-
- /* Create the callback record.
- */
- cb_rec = pj_pool_calloc(pool, 1, sizeof(*cb_rec));
- cb_rec->token = token;
- cb_rec->cb = cb;
-
- /* Create key for hash table look-up.
- * The key creation is different for TCP and UDP.
- */
-#if PJ_HAS_TCP
- if (type==PJSIP_TRANSPORT_TCP) {
- init_tcp_key(&search_key, type, remote);
- } else
-#endif
- if (type==PJSIP_TRANSPORT_UDP) {
- init_udp_key(&search_key, type, remote);
- }
-
- /* Start lock the manager. */
- pj_mutex_lock(mgr->mutex);
-
- /* Lookup the transport in the hash table. */
- tr = pj_hash_get(mgr->transport_table, &search_key, sizeof(transport_key));
-
- if (tr) {
- /* Transport found. If the transport is still busy (i.e. connecting
- * is in progress), then just register the callback. Otherwise
- * report via the callback if callback is specified.
- */
- pj_mutex_unlock(mgr->mutex);
- pj_mutex_lock(tr->tr_mutex);
-
- if (tr->flag & PJSIP_TRANSPORT_IOQUEUE_BUSY) {
- /* Transport is busy. Just register the callback. */
- pj_list_insert_before(&tr->cb_list, cb_rec);
-
- } else {
- /* Transport is ready. Call callback now.
- */
- (*cb_rec->cb)(tr, cb_rec->token, PJ_SUCCESS);
- }
- pj_mutex_unlock(tr->tr_mutex);
-
- return;
- }
-
-
- /* Transport not found. Create new one. */
- pj_memset(&local, 0, sizeof(local));
- local.sin_family = PJ_AF_INET;
- status = create_socket(type, &local, &sock_hnd);
- if (status != PJ_SUCCESS) {
- pj_mutex_unlock(mgr->mutex);
- (*cb_rec->cb)(NULL, cb_rec->token, status);
- return;
- }
- status = create_transport(mgr, type, sock_hnd, &local, NULL, &tr);
- if (status != PJ_SUCCESS) {
- pj_mutex_unlock(mgr->mutex);
- (*cb_rec->cb)(NULL, cb_rec->token, status);
- return;
- }
-
-#if PJ_HAS_TCP
- if (type == PJSIP_TRANSPORT_TCP) {
- pj_memcpy(&tr->remote_addr, remote, sizeof(pj_sockaddr_in));
+ &local_addr, addr_name);
+}
+
+/*
+ * Find transport to be used to send message to remote destination. If no
+ * suitable transport is found, a new one will be created.
+ */
+PJ_DEF(void) pjsip_transport_get( pjsip_transport_mgr *mgr,
+ pj_pool_t *pool,
+ pjsip_transport_type_e type,
+ const pj_sockaddr_in *remote,
+ void *token,
+ pjsip_transport_completion_callback *cb)
+{
+ transport_key search_key, *hash_key;
+ pjsip_transport_t *tr;
+ pj_sockaddr_in local;
+ pj_sock_t sock_hnd;
+ pj_status_t status;
+ struct transport_callback *cb_rec;
+
+ PJ_LOG(5, (LOG_TRANSPORT_MGR, "pjsip_transport_get()"));
+
+ /* Create the callback record.
+ */
+ cb_rec = pj_pool_calloc(pool, 1, sizeof(*cb_rec));
+ cb_rec->token = token;
+ cb_rec->cb = cb;
+
+ /* Create key for hash table look-up.
+ * The key creation is different for TCP and UDP.
+ */
+#if PJ_HAS_TCP
+ if (type==PJSIP_TRANSPORT_TCP) {
+ init_tcp_key(&search_key, type, remote);
+ } else
+#endif
+ if (type==PJSIP_TRANSPORT_UDP) {
+ init_udp_key(&search_key, type, remote);
+ }
+
+ /* Start lock the manager. */
+ pj_mutex_lock(mgr->mutex);
+
+ /* Lookup the transport in the hash table. */
+ tr = pj_hash_get(mgr->transport_table, &search_key, sizeof(transport_key));
+
+ if (tr) {
+ /* Transport found. If the transport is still busy (i.e. connecting
+ * is in progress), then just register the callback. Otherwise
+ * report via the callback if callback is specified.
+ */
+ pj_mutex_unlock(mgr->mutex);
+ pj_mutex_lock(tr->tr_mutex);
+
+ if (tr->flag & PJSIP_TRANSPORT_IOQUEUE_BUSY) {
+ /* Transport is busy. Just register the callback. */
+ pj_list_insert_before(&tr->cb_list, cb_rec);
+
+ } else {
+ /* Transport is ready. Call callback now.
+ */
+ (*cb_rec->cb)(tr, cb_rec->token, PJ_SUCCESS);
+ }
+ pj_mutex_unlock(tr->tr_mutex);
+
+ return;
+ }
+
+
+ /* Transport not found. Create new one. */
+ pj_memset(&local, 0, sizeof(local));
+ local.sin_family = PJ_AF_INET;
+ status = create_socket(type, &local, &sock_hnd);
+ if (status != PJ_SUCCESS) {
+ pj_mutex_unlock(mgr->mutex);
+ (*cb_rec->cb)(NULL, cb_rec->token, status);
+ return;
+ }
+ status = create_transport(mgr, type, sock_hnd, &local, NULL, &tr);
+ if (status != PJ_SUCCESS) {
+ pj_mutex_unlock(mgr->mutex);
+ (*cb_rec->cb)(NULL, cb_rec->token, status);
+ return;
+ }
+
+#if PJ_HAS_TCP
+ if (type == PJSIP_TRANSPORT_TCP) {
+ pj_memcpy(&tr->remote_addr, remote, sizeof(pj_sockaddr_in));
status = pj_ioqueue_connect(tr->key, &tr->remote_addr,
- sizeof(pj_sockaddr_in));
- pj_assert(status != 0);
+ sizeof(pj_sockaddr_in));
+ pj_assert(status != 0);
if (status != PJ_EPENDING) {
- PJ_TODO(HANDLE_IMMEDIATE_CONNECT);
- destroy_transport(mgr, tr);
- pj_mutex_unlock(mgr->mutex);
- (*cb_rec->cb)(NULL, cb_rec->token, status);
- return;
- }
- } else
-#endif
- if (type == PJSIP_TRANSPORT_UDP) {
- pj_ssize_t size;
-
- do {
+ PJ_TODO(HANDLE_IMMEDIATE_CONNECT);
+ destroy_transport(mgr, tr);
+ pj_mutex_unlock(mgr->mutex);
+ (*cb_rec->cb)(NULL, cb_rec->token, status);
+ return;
+ }
+ } else
+#endif
+ if (type == PJSIP_TRANSPORT_UDP) {
+ pj_ssize_t size;
+
+ do {
tr->rdata->addr_len = sizeof(tr->rdata->addr);
- size = PJSIP_MAX_PKT_LEN;
- status = pj_ioqueue_recvfrom( tr->key, &tr->rdata->op_key,
- tr->rdata->packet, &size, 0,
- &tr->rdata->addr,
+ size = PJSIP_MAX_PKT_LEN;
+ status = pj_ioqueue_recvfrom( tr->key, &tr->rdata->op_key,
+ tr->rdata->packet, &size, 0,
+ &tr->rdata->addr,
&tr->rdata->addr_len);
if (status == PJ_SUCCESS)
- ;
- else if (status != PJ_EPENDING) {
- destroy_transport(mgr, tr);
- pj_mutex_unlock(mgr->mutex);
- (*cb_rec->cb)(NULL, cb_rec->token, status);
- return;
- }
-
- /* Bug here.
- * If data is immediately available, although not likely, it will
- * be dropped because we don't expect to have data right after
- * the socket is created, do we ?!
- */
- PJ_TODO(FIXED_BUG_ON_IMMEDIATE_TRANSPORT_DATA);
-
- } while (status == PJ_SUCCESS);
-
- //Bug: cb will never be called!
- // Must force status to PJ_SUCCESS;
- //status = PJ_IOQUEUE_PENDING;
-
- status = PJ_SUCCESS;
-
- } else {
- pj_mutex_unlock(mgr->mutex);
- (*cb_rec->cb)(NULL, cb_rec->token, PJSIP_EUNSUPTRANSPORT);
- return;
- }
-
- pj_assert(status==PJ_EPENDING || status==PJ_SUCCESS);
- pj_mutex_lock(tr->tr_mutex);
- hash_key = pj_pool_alloc(tr->pool, sizeof(transport_key));
- pj_memcpy(hash_key, &search_key, sizeof(transport_key));
+ ;
+ else if (status != PJ_EPENDING) {
+ destroy_transport(mgr, tr);
+ pj_mutex_unlock(mgr->mutex);
+ (*cb_rec->cb)(NULL, cb_rec->token, status);
+ return;
+ }
+
+ /* Bug here.
+ * If data is immediately available, although not likely, it will
+ * be dropped because we don't expect to have data right after
+ * the socket is created, do we ?!
+ */
+ PJ_TODO(FIXED_BUG_ON_IMMEDIATE_TRANSPORT_DATA);
+
+ } while (status == PJ_SUCCESS);
+
+ //Bug: cb will never be called!
+ // Must force status to PJ_SUCCESS;
+ //status = PJ_IOQUEUE_PENDING;
+
+ status = PJ_SUCCESS;
+
+ } else {
+ pj_mutex_unlock(mgr->mutex);
+ (*cb_rec->cb)(NULL, cb_rec->token, PJSIP_EUNSUPTRANSPORT);
+ return;
+ }
+
+ pj_assert(status==PJ_EPENDING || status==PJ_SUCCESS);
+ pj_mutex_lock(tr->tr_mutex);
+ hash_key = pj_pool_alloc(tr->pool, sizeof(transport_key));
+ pj_memcpy(hash_key, &search_key, sizeof(transport_key));
pj_hash_set(tr->pool, mgr->transport_table,
- hash_key, sizeof(transport_key), tr);
- if (status == PJ_SUCCESS) {
- pj_mutex_unlock(tr->tr_mutex);
- pj_mutex_unlock(mgr->mutex);
- (*cb_rec->cb)(tr, cb_rec->token, PJ_SUCCESS);
- } else {
- pj_list_insert_before(&tr->cb_list, cb_rec);
- pj_mutex_unlock(tr->tr_mutex);
- pj_mutex_unlock(mgr->mutex);
- }
-
-}
-
-#if PJ_HAS_TCP
-/*
- * Handle completion of asynchronous accept() operation.
- * This function is called by handle_events() function.
- */
-static void handle_new_connection( pjsip_transport_mgr *mgr,
- pjsip_transport_t *listener,
- pj_status_t status )
-{
- pjsip_transport_t *tr;
+ hash_key, sizeof(transport_key), tr);
+ if (status == PJ_SUCCESS) {
+ pj_mutex_unlock(tr->tr_mutex);
+ pj_mutex_unlock(mgr->mutex);
+ (*cb_rec->cb)(tr, cb_rec->token, PJ_SUCCESS);
+ } else {
+ pj_list_insert_before(&tr->cb_list, cb_rec);
+ pj_mutex_unlock(tr->tr_mutex);
+ pj_mutex_unlock(mgr->mutex);
+ }
+
+}
+
+#if PJ_HAS_TCP
+/*
+ * Handle completion of asynchronous accept() operation.
+ * This function is called by handle_events() function.
+ */
+static void handle_new_connection( pjsip_transport_mgr *mgr,
+ pjsip_transport_t *listener,
+ pj_status_t status )
+{
+ pjsip_transport_t *tr;
transport_key *hash_key;
pj_ssize_t size;
-
- pj_assert (listener->type == PJSIP_TRANSPORT_TCP);
-
- if (status != PJ_SUCCESS) {
+
+ pj_assert (listener->type == PJSIP_TRANSPORT_TCP);
+
+ if (status != PJ_SUCCESS) {
PJSIP_ENDPT_LOG_ERROR((mgr->endpt, listener->obj_name, status,
"Error in accept() completion"));
- goto on_return;
- }
-
- PJ_LOG(4,(listener->obj_name, "incoming tcp connection from %s:%d",
- pj_inet_ntoa(listener->accept_data.remote.sin_addr),
- pj_sockaddr_in_get_port(&listener->accept_data.remote)));
-
- status = create_transport(mgr, listener->type,
- listener->accept_data.sock,
- &listener->accept_data.local,
- NULL, &tr);
- if (status != PJ_SUCCESS) {
+ goto on_return;
+ }
+
+ PJ_LOG(4,(listener->obj_name, "incoming tcp connection from %s:%d",
+ pj_inet_ntoa(listener->accept_data.remote.sin_addr),
+ pj_sockaddr_in_get_port(&listener->accept_data.remote)));
+
+ status = create_transport(mgr, listener->type,
+ listener->accept_data.sock,
+ &listener->accept_data.local,
+ NULL, &tr);
+ if (status != PJ_SUCCESS) {
PJSIP_ENDPT_LOG_ERROR((mgr->endpt, listener->obj_name, status,
"Error in creating new incoming TCP"));
- goto on_return;
- }
-
- /*
- tr->rdata->addr_len = sizeof(tr->rdata->addr);
- status = pj_ioqueue_recvfrom( mgr->ioqueue, tr->key,
- tr->rdata->packet, PJSIP_MAX_PKT_LEN,
- &tr->rdata->addr,
- &tr->rdata->addr_len);
- */
- tr->rdata->addr = listener->accept_data.remote;
- tr->rdata->addr_len = listener->accept_data.addrlen;
+ goto on_return;
+ }
- size = PJSIP_MAX_PKT_LEN;
+ /*
+ tr->rdata->addr_len = sizeof(tr->rdata->addr);
+ status = pj_ioqueue_recvfrom( mgr->ioqueue, tr->key,
+ tr->rdata->packet, PJSIP_MAX_PKT_LEN,
+ &tr->rdata->addr,
+ &tr->rdata->addr_len);
+ */
+ tr->rdata->addr = listener->accept_data.remote;
+ tr->rdata->addr_len = listener->accept_data.addrlen;
+
+ size = PJSIP_MAX_PKT_LEN;
status = pj_ioqueue_recv(tr->key, &tr->rdata->op_key,
- tr->rdata->packet, &size, 0);
- if (status != PJ_EPENDING) {
+ tr->rdata->packet, &size, 0);
+ if (status != PJ_EPENDING) {
PJSIP_ENDPT_LOG_ERROR((mgr->endpt, listener->obj_name, status,
"Error in receiving data"));
- PJ_TODO(IMMEDIATE_DATA);
- destroy_transport(mgr, tr);
- goto on_return;
- }
-
+ PJ_TODO(IMMEDIATE_DATA);
+ destroy_transport(mgr, tr);
+ goto on_return;
+ }
+
pj_memcpy(&tr->remote_addr, &listener->accept_data.remote,
- listener->accept_data.addrlen);
- hash_key = pj_pool_alloc(tr->pool, sizeof(transport_key));
- init_key_from_transport(hash_key, tr);
-
- pj_mutex_lock(mgr->mutex);
+ listener->accept_data.addrlen);
+ hash_key = pj_pool_alloc(tr->pool, sizeof(transport_key));
+ init_key_from_transport(hash_key, tr);
+
+ pj_mutex_lock(mgr->mutex);
pj_hash_set(tr->pool, mgr->transport_table, hash_key,
- sizeof(transport_key), tr);
- pj_mutex_unlock(mgr->mutex);
-
-on_return:
- /* Re-initiate asynchronous accept() */
- listener->accept_data.addrlen = sizeof(listener->accept_data.local);
- status = pj_ioqueue_accept(listener->key, &listener->accept_op,
- &listener->accept_data.sock,
- &listener->accept_data.local,
- &listener->accept_data.remote,
- &listener->accept_data.addrlen);
- if (status != PJ_EPENDING) {
+ sizeof(transport_key), tr);
+ pj_mutex_unlock(mgr->mutex);
+
+on_return:
+ /* Re-initiate asynchronous accept() */
+ listener->accept_data.addrlen = sizeof(listener->accept_data.local);
+ status = pj_ioqueue_accept(listener->key, &listener->accept_op,
+ &listener->accept_data.sock,
+ &listener->accept_data.local,
+ &listener->accept_data.remote,
+ &listener->accept_data.addrlen);
+ if (status != PJ_EPENDING) {
PJSIP_ENDPT_LOG_ERROR((mgr->endpt, listener->obj_name, status,
"Error in receiving data"));
- PJ_TODO(IMMEDIATE_ACCEPT);
- return;
- }
-}
-
-/*
- * Handle completion of asynchronous connect() function.
- * This function is called by the handle_events() function.
- */
-static void handle_connect_completion( pjsip_transport_mgr *mgr,
- pjsip_transport_t *tr,
- pj_status_t status )
-{
- struct transport_callback new_list;
- struct transport_callback *cb_rec;
+ PJ_TODO(IMMEDIATE_ACCEPT);
+ return;
+ }
+}
+
+/*
+ * Handle completion of asynchronous connect() function.
+ * This function is called by the handle_events() function.
+ */
+static void handle_connect_completion( pjsip_transport_mgr *mgr,
+ pjsip_transport_t *tr,
+ pj_status_t status )
+{
+ struct transport_callback new_list;
+ struct transport_callback *cb_rec;
pj_ssize_t recv_size;
-
- PJ_UNUSED_ARG(mgr);
-
- /* On connect completion, we must call all registered callbacks in
- * the transport.
- */
-
- /* Initialize new list. */
- pj_list_init(&new_list);
-
- /* Hold transport's mutex. We don't want other thread to register a
- * callback while we're dealing with it.
- */
- pj_mutex_lock(tr->tr_mutex);
-
- /* Copy callback list to new list so that we can call the callbacks
- * without holding the mutex.
- */
- pj_list_merge_last(&new_list, &tr->cb_list);
-
- /* Clear transport's busy flag. */
- tr->flag &= ~PJSIP_TRANSPORT_IOQUEUE_BUSY;
-
- /* If success, update local address.
- * Local address is only available after connect() has returned.
- */
- if (status == PJ_SUCCESS) {
- int addrlen = sizeof(tr->local_addr);
+
+ PJ_UNUSED_ARG(mgr);
+
+ /* On connect completion, we must call all registered callbacks in
+ * the transport.
+ */
+
+ /* Initialize new list. */
+ pj_list_init(&new_list);
+
+ /* Hold transport's mutex. We don't want other thread to register a
+ * callback while we're dealing with it.
+ */
+ pj_mutex_lock(tr->tr_mutex);
+
+ /* Copy callback list to new list so that we can call the callbacks
+ * without holding the mutex.
+ */
+ pj_list_merge_last(&new_list, &tr->cb_list);
+
+ /* Clear transport's busy flag. */
+ tr->flag &= ~PJSIP_TRANSPORT_IOQUEUE_BUSY;
+
+ /* If success, update local address.
+ * Local address is only available after connect() has returned.
+ */
+ if (status == PJ_SUCCESS) {
+ int addrlen = sizeof(tr->local_addr);
status = pj_sock_getsockname(tr->sock,
(pj_sockaddr_t*)&tr->local_addr,
- &addrlen);
- if (status == PJ_SUCCESS) {
- pj_memcpy(&tr->addr_name, &tr->local_addr, sizeof(tr->addr_name));
- }
- }
-
- /* Unlock mutex. */
- pj_mutex_unlock(tr->tr_mutex);
-
- /* Call all registered callbacks. */
- cb_rec = new_list.next;
- while (cb_rec != &new_list) {
- struct transport_callback *next;
- next = cb_rec->next;
- (*cb_rec->cb)(tr, cb_rec->token, status);
- cb_rec = next;
- }
-
- /* Success? */
- if (status != PJ_SUCCESS) {
+ &addrlen);
+ if (status == PJ_SUCCESS) {
+ pj_memcpy(&tr->addr_name, &tr->local_addr, sizeof(tr->addr_name));
+ }
+ }
+
+ /* Unlock mutex. */
+ pj_mutex_unlock(tr->tr_mutex);
+
+ /* Call all registered callbacks. */
+ cb_rec = new_list.next;
+ while (cb_rec != &new_list) {
+ struct transport_callback *next;
+ next = cb_rec->next;
+ (*cb_rec->cb)(tr, cb_rec->token, status);
+ cb_rec = next;
+ }
+
+ /* Success? */
+ if (status != PJ_SUCCESS) {
destroy_transport(mgr, tr);
- PJ_TODO(WTF);
- return;
- }
-
+ PJ_TODO(WTF);
+ return;
+ }
+
/* Initiate read operation to socket. */
- recv_size = PJSIP_MAX_PKT_LEN;
+ recv_size = PJSIP_MAX_PKT_LEN;
status = pj_ioqueue_recv( tr->key, &tr->rdata->op_key, tr->rdata->packet,
- &recv_size, 0);
- if (status != PJ_EPENDING) {
+ &recv_size, 0);
+ if (status != PJ_EPENDING) {
destroy_transport(mgr, tr);
- PJ_TODO(IMMEDIATE_DATA);
- return;
- }
-}
-#endif /* PJ_HAS_TCP */
-
-/*
- * Handle incoming data.
- * This function is called when the transport manager receives 'notification'
- * from the I/O Queue that the receive operation has completed.
- * This function will then attempt to parse the message, and hands over the
- * message to the endpoint.
- */
-static void handle_received_data( pjsip_transport_mgr *mgr,
- pjsip_transport_t *tr,
- pj_ssize_t size )
-{
- pjsip_msg *msg;
- pjsip_rx_data *rdata = tr->rdata;
- pj_pool_t *rdata_pool;
- pjsip_hdr *hdr;
- pj_str_t s;
- char *src_addr;
- int src_port;
- pj_size_t msg_fragment_size = 0;
-
- /* Check size. */
- if (size < 1) {
- if (tr->type != PJSIP_TRANSPORT_UDP) {
- /* zero bytes indicates transport has been closed for TCP.
- * But alas, we can't destroy it now since transactions may still
- * have reference to it. In that case, just do nothing, the
- * transaction will receive error when it tries to send anything.
- * But alas!! UAC transactions wont send anything!!.
- * So this is a bug!
- */
- if (pj_atomic_get(tr->ref_cnt)==0) {
- PJ_LOG(4,(tr->obj_name, "connection closed"));
- destroy_transport(mgr, tr);
- } else {
- PJ_TODO(HANDLE_TCP_TRANSPORT_CLOSED);
- //PJ_TODO(SIGNAL_TRANSACTIONS_ON_TRANSPORT_CLOSED);
- }
- return;
- } else {
- /* On Windows machines, UDP recv() will return zero upon receiving
- * ICMP port unreachable message.
- */
- PJ_LOG(4,(tr->obj_name, "Ignored zero length UDP packet (port unreachable?)"));
- goto on_return;
- }
- }
-
- /* Save received time. */
- pj_gettimeofday(&rdata->timestamp);
-
- /* Update length. */
- rdata->len += size;
-
- /* Null terminate packet, this is the requirement of the parser. */
- rdata->packet[rdata->len] = '\0';
-
- /* Get source address and port for logging purpose. */
- src_addr = pj_inet_ntoa(rdata->addr.sin_addr);
- src_port = pj_sockaddr_in_get_port(&rdata->addr);
-
- /* Print the whole data to the log. */
- PJ_LOG(4,(tr->obj_name, "%d bytes recvfrom %s:%d:\n"
- "----------- begin msg ------------\n"
- "%s"
- "------------ end msg -------------",
- rdata->len, src_addr, src_port, rdata->packet));
-
-
- /* Process all message fragments. */
- while (rdata->len > 0) {
-
- msg_fragment_size = rdata->len;
-#if PJ_HAS_TCP
- /* For TCP transport, check if the whole message has been received. */
- if (tr->type != PJSIP_TRANSPORT_UDP) {
- pj_status_t msg_status;
+ PJ_TODO(IMMEDIATE_DATA);
+ return;
+ }
+}
+#endif /* PJ_HAS_TCP */
+
+/*
+ * Handle incoming data.
+ * This function is called when the transport manager receives 'notification'
+ * from the I/O Queue that the receive operation has completed.
+ * This function will then attempt to parse the message, and hands over the
+ * message to the endpoint.
+ */
+static void handle_received_data( pjsip_transport_mgr *mgr,
+ pjsip_transport_t *tr,
+ pj_ssize_t size )
+{
+ pjsip_msg *msg;
+ pjsip_rx_data *rdata = tr->rdata;
+ pj_pool_t *rdata_pool;
+ pjsip_hdr *hdr;
+ pj_str_t s;
+ char *src_addr;
+ int src_port;
+ pj_size_t msg_fragment_size = 0;
+
+ /* Check size. */
+ if (size < 1) {
+ if (tr->type != PJSIP_TRANSPORT_UDP) {
+ /* zero bytes indicates transport has been closed for TCP.
+ * But alas, we can't destroy it now since transactions may still
+ * have reference to it. In that case, just do nothing, the
+ * transaction will receive error when it tries to send anything.
+ * But alas!! UAC transactions wont send anything!!.
+ * So this is a bug!
+ */
+ if (pj_atomic_get(tr->ref_cnt)==0) {
+ PJ_LOG(4,(tr->obj_name, "connection closed"));
+ destroy_transport(mgr, tr);
+ } else {
+ PJ_TODO(HANDLE_TCP_TRANSPORT_CLOSED);
+ //PJ_TODO(SIGNAL_TRANSACTIONS_ON_TRANSPORT_CLOSED);
+ }
+ return;
+ } else {
+ /* On Windows machines, UDP recv() will return zero upon receiving
+ * ICMP port unreachable message.
+ */
+ PJ_LOG(4,(tr->obj_name, "Ignored zero length UDP packet (port unreachable?)"));
+ goto on_return;
+ }
+ }
+
+ /* Save received time. */
+ pj_gettimeofday(&rdata->timestamp);
+
+ /* Update length. */
+ rdata->len += size;
+
+ /* Null terminate packet, this is the requirement of the parser. */
+ rdata->packet[rdata->len] = '\0';
+
+ /* Get source address and port for logging purpose. */
+ src_addr = pj_inet_ntoa(rdata->addr.sin_addr);
+ src_port = pj_sockaddr_in_get_port(&rdata->addr);
+
+ /* Print the whole data to the log. */
+ PJ_LOG(4,(tr->obj_name, "%d bytes recvfrom %s:%d:\n"
+ "----------- begin msg ------------\n"
+ "%s"
+ "------------ end msg -------------",
+ rdata->len, src_addr, src_port, rdata->packet));
+
+
+ /* Process all message fragments. */
+ while (rdata->len > 0) {
+
+ msg_fragment_size = rdata->len;
+#if PJ_HAS_TCP
+ /* For TCP transport, check if the whole message has been received. */
+ if (tr->type != PJSIP_TRANSPORT_UDP) {
+ pj_status_t msg_status;
msg_status = pjsip_find_msg(rdata->packet, rdata->len, PJ_FALSE,
- &msg_fragment_size);
- if (msg_status != PJ_SUCCESS) {
+ &msg_fragment_size);
+ if (msg_status != PJ_SUCCESS) {
if (rdata->len == PJSIP_MAX_PKT_LEN) {
PJSIP_ENDPT_LOG_ERROR((mgr->endpt, tr->obj_name,
PJSIP_EOVERFLOW,
- "Buffer discarded for %s:%d",
- src_addr, src_port));
- goto on_return;
- } else {
- goto tcp_read_packet;
- }
- }
- }
-#endif
-
- /* Clear parser error report */
- pj_list_init(&rdata->parse_err);
-
- /* Parse the message. */
- PJ_LOG(5,(tr->obj_name, "Parsing %d bytes from %s:%d", msg_fragment_size,
- src_addr, src_port));
-
- msg = pjsip_parse_rdata( rdata->packet, msg_fragment_size, rdata);
- if (msg == NULL) {
- PJ_LOG(3,(tr->obj_name, "Bad message (%d bytes from %s:%d)", msg_fragment_size,
- src_addr, src_port));
- goto finish_process_fragment;
- }
+ "Buffer discarded for %s:%d",
+ src_addr, src_port));
+ goto on_return;
+ } else {
+ goto tcp_read_packet;
+ }
+ }
+ }
+#endif
- /* Perform basic header checking. */
+ /* Clear parser error report */
+ pj_list_init(&rdata->parse_err);
+
+ /* Parse the message. */
+ PJ_LOG(5,(tr->obj_name, "Parsing %d bytes from %s:%d", msg_fragment_size,
+ src_addr, src_port));
+
+ msg = pjsip_parse_rdata( rdata->packet, msg_fragment_size, rdata);
+ if (msg == NULL) {
+ PJ_LOG(3,(tr->obj_name, "Bad message (%d bytes from %s:%d)", msg_fragment_size,
+ src_addr, src_port));
+ goto finish_process_fragment;
+ }
+
+ /* Perform basic header checking. */
if (rdata->call_id.ptr == NULL || rdata->from == NULL ||
- rdata->to == NULL || rdata->via == NULL || rdata->cseq == NULL)
- {
- PJ_LOG(3,(tr->obj_name, "Bad message from %s:%d: missing some header",
- src_addr, src_port));
- goto finish_process_fragment;
- }
-
- /* If message is received from address that's different from the sent-by,
- * MUST add received parameter to the via.
- * In our case, we add Via receive param for EVERY received message,
- * because it saves us from resolving the host HERE in case sent-by is in
- * FQDN format. And it doesn't hurt either.
- */
- s = pj_str(src_addr);
- pj_strdup(rdata->pool, &rdata->via->recvd_param, &s);
-
- /* RFC 3581:
- * If message contains "rport" param, put the received port there.
- */
- if (rdata->via->rport_param == 0) {
- rdata->via->rport_param = pj_sockaddr_in_get_port(&rdata->addr);
- }
-
- /* Drop response message if it has more than one Via.
- */
- if (msg->type == PJSIP_RESPONSE_MSG) {
- hdr = (pjsip_hdr*)rdata->via->next;
- if (hdr != &rdata->msg->hdr) {
- hdr = pjsip_msg_find_hdr(msg, PJSIP_H_VIA, hdr);
- if (hdr) {
- PJ_LOG(3,(tr->obj_name, "Bad message from %s:%d: "
- "multiple Via in response message",
- src_addr, src_port));
- goto finish_process_fragment;
- }
- }
- }
-
- /* Call the transport manager's upstream message callback.
- */
- (*mgr->message_callback)(mgr->endpt, rdata);
-
-finish_process_fragment:
- rdata->len -= msg_fragment_size;
- if (rdata->len > 0) {
- pj_memmove(rdata->packet, rdata->packet+msg_fragment_size, rdata->len);
- PJ_LOG(4,(tr->obj_name, "Processing next fragment, size=%d bytes", rdata->len));
- }
-
- } /* while (rdata->len > 0) */
-
-on_return:
- /* Reset the pool and rdata */
- rdata_pool = rdata->pool;
- pj_pool_reset(rdata_pool);
- rdata = pj_pool_alloc( rdata_pool, sizeof(*rdata) );
- rdata->len = 0;
- rdata->transport = tr;
- rdata->pool = rdata_pool;
- tr->rdata = rdata;
-
- /* Read the next packet. */
- rdata->addr_len = sizeof(rdata->addr);
+ rdata->to == NULL || rdata->via == NULL || rdata->cseq == NULL)
+ {
+ PJ_LOG(3,(tr->obj_name, "Bad message from %s:%d: missing some header",
+ src_addr, src_port));
+ goto finish_process_fragment;
+ }
+
+ /* If message is received from address that's different from the sent-by,
+ * MUST add received parameter to the via.
+ * In our case, we add Via receive param for EVERY received message,
+ * because it saves us from resolving the host HERE in case sent-by is in
+ * FQDN format. And it doesn't hurt either.
+ */
+ s = pj_str(src_addr);
+ pj_strdup(rdata->pool, &rdata->via->recvd_param, &s);
+
+ /* RFC 3581:
+ * If message contains "rport" param, put the received port there.
+ */
+ if (rdata->via->rport_param == 0) {
+ rdata->via->rport_param = pj_sockaddr_in_get_port(&rdata->addr);
+ }
+
+ /* Drop response message if it has more than one Via.
+ */
+ if (msg->type == PJSIP_RESPONSE_MSG) {
+ hdr = (pjsip_hdr*)rdata->via->next;
+ if (hdr != &rdata->msg->hdr) {
+ hdr = pjsip_msg_find_hdr(msg, PJSIP_H_VIA, hdr);
+ if (hdr) {
+ PJ_LOG(3,(tr->obj_name, "Bad message from %s:%d: "
+ "multiple Via in response message",
+ src_addr, src_port));
+ goto finish_process_fragment;
+ }
+ }
+ }
+
+ /* Call the transport manager's upstream message callback.
+ */
+ (*mgr->message_callback)(mgr->endpt, rdata);
+
+finish_process_fragment:
+ rdata->len -= msg_fragment_size;
+ if (rdata->len > 0) {
+ pj_memmove(rdata->packet, rdata->packet+msg_fragment_size, rdata->len);
+ PJ_LOG(4,(tr->obj_name, "Processing next fragment, size=%d bytes", rdata->len));
+ }
+
+ } /* while (rdata->len > 0) */
+
+on_return:
+ /* Reset the pool and rdata */
+ rdata_pool = rdata->pool;
+ pj_pool_reset(rdata_pool);
+ rdata = pj_pool_alloc( rdata_pool, sizeof(*rdata) );
+ rdata->len = 0;
+ rdata->transport = tr;
+ rdata->pool = rdata_pool;
+ tr->rdata = rdata;
+
+ /* Read the next packet. */
+ rdata->addr_len = sizeof(rdata->addr);
if (tr->type == PJSIP_TRANSPORT_UDP) {
- pj_ssize_t size = PJSIP_MAX_PKT_LEN;
- pj_ioqueue_recvfrom(tr->key, &tr->rdata->op_key,
- tr->rdata->packet, &size, 0,
+ pj_ssize_t size = PJSIP_MAX_PKT_LEN;
+ pj_ioqueue_recvfrom(tr->key, &tr->rdata->op_key,
+ tr->rdata->packet, &size, 0,
&rdata->addr, &rdata->addr_len);
- PJ_TODO(HANDLE_IMMEDIATE_DATA);
- }
-
-#if PJ_HAS_TCP
- /* The next 'if' should have been 'else if', but we need to put the
- label inside the '#if PJ_HAS_TCP' block to avoid 'unreferenced label' warning.
- */
-tcp_read_packet:
+ PJ_TODO(HANDLE_IMMEDIATE_DATA);
+ }
+
+#if PJ_HAS_TCP
+ /* The next 'if' should have been 'else if', but we need to put the
+ label inside the '#if PJ_HAS_TCP' block to avoid 'unreferenced label' warning.
+ */
+tcp_read_packet:
if (tr->type == PJSIP_TRANSPORT_TCP) {
pj_ssize_t size = PJSIP_MAX_PKT_LEN - tr->rdata->len;
- pj_ioqueue_recv( tr->key, &tr->rdata->op_key,
- tr->rdata->packet + tr->rdata->len,
+ pj_ioqueue_recv( tr->key, &tr->rdata->op_key,
+ tr->rdata->packet + tr->rdata->len,
&size, 0);
- PJ_TODO(HANDLE_IMMEDIATE_DATA_1);
- }
-#endif
-}
-
-static void transport_mgr_on_idle( pjsip_transport_mgr *mgr )
-{
- pj_time_val now;
- pj_hash_iterator_t itr_val;
- pj_hash_iterator_t *itr;
-
-
- /* Get time for comparing transport's close time. */
- pj_gettimeofday(&now);
- if (now.sec < mgr->next_idle_check.sec) {
- return;
- }
-
- /* Acquire transport manager's lock. */
- pj_mutex_lock(mgr->mutex);
-
- /* Update next idle check. */
- mgr->next_idle_check.sec += MGR_IDLE_CHECK_INTERVAL;
-
- /* Iterate all transports, and close transports that are not used for
- some periods.
- */
- itr = pjsip_transport_first(mgr, &itr_val);
- while (itr != NULL) {
- pj_hash_iterator_t *next;
- pjsip_transport_t *transport;
-
- transport = pjsip_transport_this(mgr, itr);
-
- next = pjsip_transport_next(mgr, itr);
-
- if (pj_atomic_get(transport->ref_cnt)==0 &&
- PJ_TIME_VAL_LTE(transport->close_time, now))
- {
- destroy_transport(mgr, transport);
- }
-
- itr = next;
- }
-
- /* Release transport manager's lock. */
- pj_mutex_unlock(mgr->mutex);
-}
-
+ PJ_TODO(HANDLE_IMMEDIATE_DATA_1);
+ }
+#endif
+}
+
+static void transport_mgr_on_idle( pjsip_transport_mgr *mgr )
+{
+ pj_time_val now;
+ pj_hash_iterator_t itr_val;
+ pj_hash_iterator_t *itr;
+
+
+ /* Get time for comparing transport's close time. */
+ pj_gettimeofday(&now);
+ if (now.sec < mgr->next_idle_check.sec) {
+ return;
+ }
+
+ /* Acquire transport manager's lock. */
+ pj_mutex_lock(mgr->mutex);
+
+ /* Update next idle check. */
+ mgr->next_idle_check.sec += MGR_IDLE_CHECK_INTERVAL;
+
+ /* Iterate all transports, and close transports that are not used for
+ some periods.
+ */
+ itr = pjsip_transport_first(mgr, &itr_val);
+ while (itr != NULL) {
+ pj_hash_iterator_t *next;
+ pjsip_transport_t *transport;
+
+ transport = pjsip_transport_this(mgr, itr);
+
+ next = pjsip_transport_next(mgr, itr);
+
+ if (pj_atomic_get(transport->ref_cnt)==0 &&
+ PJ_TIME_VAL_LTE(transport->close_time, now))
+ {
+ destroy_transport(mgr, transport);
+ }
+
+ itr = next;
+ }
+
+ /* Release transport manager's lock. */
+ pj_mutex_unlock(mgr->mutex);
+}
+
static void on_ioqueue_read(pj_ioqueue_key_t *key,
pj_ioqueue_op_key_t *op_key,
- pj_ssize_t bytes_read)
-{
- pjsip_transport_t *t;
- t = pj_ioqueue_get_user_data(key);
-
- handle_received_data( t->mgr, t, bytes_read );
-}
-
+ pj_ssize_t bytes_read)
+{
+ pjsip_transport_t *t;
+ t = pj_ioqueue_get_user_data(key);
+
+ handle_received_data( t->mgr, t, bytes_read );
+}
+
static void on_ioqueue_write(pj_ioqueue_key_t *key,
pj_ioqueue_op_key_t *op_key,
- pj_ssize_t bytes_sent)
-{
- PJ_UNUSED_ARG(key);
- PJ_UNUSED_ARG(bytes_sent);
-
- /* Completion of write operation.
- * Do nothing.
- */
-}
-
+ pj_ssize_t bytes_sent)
+{
+ PJ_UNUSED_ARG(key);
+ PJ_UNUSED_ARG(bytes_sent);
+
+ /* Completion of write operation.
+ * Do nothing.
+ */
+}
+
static void on_ioqueue_accept(pj_ioqueue_key_t *key,
pj_ioqueue_op_key_t *op_key,
pj_sock_t newsock,
- int status)
-{
-#if PJ_HAS_TCP
- pjsip_transport_t *t;
- t = pj_ioqueue_get_user_data(key);
-
- handle_new_connection( t->mgr, t, status );
-#else
- PJ_UNUSED_ARG(key);
- PJ_UNUSED_ARG(status);
-#endif
-}
-
-static void on_ioqueue_connect(pj_ioqueue_key_t *key, int status)
-{
-#if PJ_HAS_TCP
- pjsip_transport_t *t;
- t = pj_ioqueue_get_user_data(key);
-
- handle_connect_completion( t->mgr, t, status);
-#else
- PJ_UNUSED_ARG(key);
- PJ_UNUSED_ARG(status);
-#endif
-}
-
-
-/*
- * Poll for events.
- */
-PJ_DEF(int) pjsip_transport_mgr_handle_events( pjsip_transport_mgr *mgr,
- const pj_time_val *req_timeout )
-{
- int event_count;
- int break_loop;
- int result;
- pj_time_val timeout;
-
- PJ_LOG(5, (LOG_TRANSPORT_MGR, "pjsip_transport_mgr_handle_events()"));
-
- event_count = 0;
- break_loop = 0;
- timeout = *req_timeout;
- do {
- result = pj_ioqueue_poll( mgr->ioqueue, &timeout);
- if (result == 1) {
- ++event_count;
-
- /* Break the loop. */
- //if (timeout.msec==0 && timeout.sec==0) {
- break_loop = 1;
- //}
-
- } else {
- /* On idle, cleanup transport. */
- transport_mgr_on_idle(mgr);
-
- break_loop = 1;
- }
- timeout.sec = timeout.msec = 0;
- } while (!break_loop);
-
- return event_count;
-}
-
-
-PJ_DEF(pj_hash_iterator_t*) pjsip_transport_first( pjsip_transport_mgr *mgr,
- pj_hash_iterator_t *it )
-{
- return pj_hash_first(mgr->transport_table, it);
-}
-
-PJ_DEF(pj_hash_iterator_t*) pjsip_transport_next( pjsip_transport_mgr *mgr,
- pj_hash_iterator_t *itr )
-{
- return pj_hash_next(mgr->transport_table, itr);
-}
-
-PJ_DEF(pjsip_transport_t*) pjsip_transport_this( pjsip_transport_mgr *mgr,
- pj_hash_iterator_t *itr )
-{
- return pj_hash_this(mgr->transport_table, itr);
-}
+ int status)
+{
+#if PJ_HAS_TCP
+ pjsip_transport_t *t;
+ t = pj_ioqueue_get_user_data(key);
+
+ handle_new_connection( t->mgr, t, status );
+#else
+ PJ_UNUSED_ARG(key);
+ PJ_UNUSED_ARG(status);
+#endif
+}
+
+static void on_ioqueue_connect(pj_ioqueue_key_t *key, int status)
+{
+#if PJ_HAS_TCP
+ pjsip_transport_t *t;
+ t = pj_ioqueue_get_user_data(key);
+
+ handle_connect_completion( t->mgr, t, status);
+#else
+ PJ_UNUSED_ARG(key);
+ PJ_UNUSED_ARG(status);
+#endif
+}
+
+
+/*
+ * Poll for events.
+ */
+PJ_DEF(int) pjsip_transport_mgr_handle_events( pjsip_transport_mgr *mgr,
+ const pj_time_val *req_timeout )
+{
+ int event_count;
+ int break_loop;
+ int result;
+ pj_time_val timeout;
+
+ PJ_LOG(5, (LOG_TRANSPORT_MGR, "pjsip_transport_mgr_handle_events()"));
+
+ event_count = 0;
+ break_loop = 0;
+ timeout = *req_timeout;
+ do {
+ result = pj_ioqueue_poll( mgr->ioqueue, &timeout);
+ if (result == 1) {
+ ++event_count;
+
+ /* Break the loop. */
+ //if (timeout.msec==0 && timeout.sec==0) {
+ break_loop = 1;
+ //}
+
+ } else {
+ /* On idle, cleanup transport. */
+ transport_mgr_on_idle(mgr);
+
+ break_loop = 1;
+ }
+ timeout.sec = timeout.msec = 0;
+ } while (!break_loop);
+
+ return event_count;
+}
+
+
+PJ_DEF(pj_hash_iterator_t*) pjsip_transport_first( pjsip_transport_mgr *mgr,
+ pj_hash_iterator_t *it )
+{
+ return pj_hash_first(mgr->transport_table, it);
+}
+
+PJ_DEF(pj_hash_iterator_t*) pjsip_transport_next( pjsip_transport_mgr *mgr,
+ pj_hash_iterator_t *itr )
+{
+ return pj_hash_next(mgr->transport_table, itr);
+}
+
+PJ_DEF(pjsip_transport_t*) pjsip_transport_this( pjsip_transport_mgr *mgr,
+ pj_hash_iterator_t *itr )
+{
+ return pj_hash_this(mgr->transport_table, itr);
+}
diff --git a/pjsip/src/pjsip/sip_uri.c b/pjsip/src/pjsip/sip_uri.c
index 8085f7f..e6db870 100644
--- a/pjsip/src/pjsip/sip_uri.c
+++ b/pjsip/src/pjsip/sip_uri.c
@@ -1,398 +1,420 @@
-/* $Id$
- */
-#include <pjsip/sip_uri.h>
-#include <pjsip/sip_msg.h>
-#include <pjsip/print_util.h>
-#include <pj/string.h>
-#include <pj/pool.h>
+/* $Id$
+ */
+/*
+ * PJSIP - SIP Stack
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ * Benny Prijono <bennylp@bulukucing.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; 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/print_util.h>
+#include <pj/string.h>
+#include <pj/pool.h>
#include <pj/assert.h>
-
-#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_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;
- pj_size_t size_required;
- char *startbuf = buf;
- const pj_str_t *scheme;
- *buf = '\0';
-
- /* Check the buffer length. */
- size_required = 6 + url->host.slen + 10 +
- url->user.slen + url->passwd.slen + 2 +
- url->user_param.slen + 6 +
- url->method_param.slen + 8 +
- url->transport_param.slen + 11 +
- 9 + 5 +
- url->maddr_param.slen + 7 +
- 3 +
- url->other_param.slen +
- url->header_param.slen;
- if (size < size_required) {
- return -1;
- }
-
- /* Print scheme ("sip:" or "sips:") */
- scheme = pjsip_uri_get_scheme(url);
- copy_advance_no_check(buf, *scheme);
- *buf++ = ':';
-
- /* Print "user:password@", if any. */
- if (url->user.slen) {
- copy_advance_no_check(buf, url->user);
- if (url->passwd.slen) {
- *buf++ = ':';
- copy_advance_no_check(buf, url->passwd);
- }
-
- *buf++ = '@';
- }
-
- /* Print host. */
- pj_assert(url->host.slen != 0);
- copy_advance_no_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_no_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_no_check(buf, ";method=", 8, url->method_param);
- }
-
- /* Transport is not allowed in From/To header. */
- if (context != PJSIP_URI_IN_FROMTO_HDR) {
- copy_advance_pair_no_check(buf, ";transport=", 11, url->transport_param);
- }
-
- /* 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)
- {
- 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_no_check(buf, ";maddr=", 7, url->maddr_param);
- }
-
- /* 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_no_check(buf, lr);
- }
-
- /* Other param. */
- if (url->other_param.slen) {
- copy_advance_no_check(buf, url->other_param);
- }
-
- /* Header param. */
- if (url->header_param.slen) {
- copy_advance_no_check(buf, url->header_param);
- }
-
- *buf = '\0';
- return buf-startbuf;
-}
-
-static int pjsip_url_compare( pjsip_uri_context_e context,
- const pjsip_url *url1, const pjsip_url *url2)
-{
- /* The easiest (and probably the most efficient) way to compare two URLs
- are to print them, and compare them bytes per bytes. This technique
- works quite well with RFC3261, as the RFC (unlike RFC2543) defines that
- components specified in one URL does NOT match its default value if
- it is not specified in the second URL. For example, parameter "user=ip"
- does NOT match if it is omited in second URL.
-
- HOWEVER, THE SAME CAN NOT BE APPLIED FOR other-param NOR header-param.
- For these, each of the parameters must be compared one by one. Parameter
- that exists in one URL will match the comparison. But parameter that
- exists in both URLs and doesn't match wont match the URL comparison.
-
- The solution for this is to compare 'standard' URL components with
- bytes-to-bytes comparison, and compare other-param and header-param with
- more intelligent comparison.
- */
- char str_url1[PJSIP_MAX_URL_SIZE];
- char str_url2[PJSIP_MAX_URL_SIZE];
- int len1, len2;
-
- /* Must compare scheme first, as the second URI may not be SIP URL. */
- if (pj_stricmp(pjsip_uri_get_scheme(url1), pjsip_uri_get_scheme(url2)))
- return -1;
-
- len1 = pjsip_url_print(context, url1, str_url1, sizeof(str_url1));
- if (len1 < 1) {
- pj_assert(0);
- return -1;
- }
- len2 = pjsip_url_print(context, url2, str_url2, sizeof(str_url2));
- if (len2 < 1) {
- pj_assert(0);
- return -1;
- }
-
- if (len1 != len2) {
- /* Not equal. */
- return -1;
- }
-
- if (pj_native_strcmp(str_url1, str_url2)) {
- /* Not equal */
- return -1;
- }
-
- /* TODO: compare other-param and header-param in more intelligent manner. */
- PJ_TODO(HPARAM_AND_OTHER_PARAM_COMPARISON_IN_URL_COMPARISON)
-
- if (pj_strcmp(&url1->other_param, &url2->other_param)) {
- /* Not equal. */
- return -1;
- }
- if (pj_strcmp(&url1->header_param, &url2->header_param)) {
- /* Not equal. */
- return -1;
- }
-
- /* Seems to be equal, isn't it. */
- return 0;
-
-}
-
-
-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);
- pj_strdup( pool, &url->other_param, &rhs->other_param);
- pj_strdup( 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;
-}
-
+
+#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_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;
+ pj_size_t size_required;
+ char *startbuf = buf;
+ const pj_str_t *scheme;
+ *buf = '\0';
+
+ /* Check the buffer length. */
+ size_required = 6 + url->host.slen + 10 +
+ url->user.slen + url->passwd.slen + 2 +
+ url->user_param.slen + 6 +
+ url->method_param.slen + 8 +
+ url->transport_param.slen + 11 +
+ 9 + 5 +
+ url->maddr_param.slen + 7 +
+ 3 +
+ url->other_param.slen +
+ url->header_param.slen;
+ if (size < size_required) {
+ return -1;
+ }
+
+ /* Print scheme ("sip:" or "sips:") */
+ scheme = pjsip_uri_get_scheme(url);
+ copy_advance_no_check(buf, *scheme);
+ *buf++ = ':';
+
+ /* Print "user:password@", if any. */
+ if (url->user.slen) {
+ copy_advance_no_check(buf, url->user);
+ if (url->passwd.slen) {
+ *buf++ = ':';
+ copy_advance_no_check(buf, url->passwd);
+ }
+
+ *buf++ = '@';
+ }
+
+ /* Print host. */
+ pj_assert(url->host.slen != 0);
+ copy_advance_no_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_no_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_no_check(buf, ";method=", 8, url->method_param);
+ }
+
+ /* Transport is not allowed in From/To header. */
+ if (context != PJSIP_URI_IN_FROMTO_HDR) {
+ copy_advance_pair_no_check(buf, ";transport=", 11, url->transport_param);
+ }
+
+ /* 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)
+ {
+ 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_no_check(buf, ";maddr=", 7, url->maddr_param);
+ }
+
+ /* 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_no_check(buf, lr);
+ }
+
+ /* Other param. */
+ if (url->other_param.slen) {
+ copy_advance_no_check(buf, url->other_param);
+ }
+
+ /* Header param. */
+ if (url->header_param.slen) {
+ copy_advance_no_check(buf, url->header_param);
+ }
+
+ *buf = '\0';
+ return buf-startbuf;
+}
+
+static int pjsip_url_compare( pjsip_uri_context_e context,
+ const pjsip_url *url1, const pjsip_url *url2)
+{
+ /* The easiest (and probably the most efficient) way to compare two URLs
+ are to print them, and compare them bytes per bytes. This technique
+ works quite well with RFC3261, as the RFC (unlike RFC2543) defines that
+ components specified in one URL does NOT match its default value if
+ it is not specified in the second URL. For example, parameter "user=ip"
+ does NOT match if it is omited in second URL.
+
+ HOWEVER, THE SAME CAN NOT BE APPLIED FOR other-param NOR header-param.
+ For these, each of the parameters must be compared one by one. Parameter
+ that exists in one URL will match the comparison. But parameter that
+ exists in both URLs and doesn't match wont match the URL comparison.
+
+ The solution for this is to compare 'standard' URL components with
+ bytes-to-bytes comparison, and compare other-param and header-param with
+ more intelligent comparison.
+ */
+ char str_url1[PJSIP_MAX_URL_SIZE];
+ char str_url2[PJSIP_MAX_URL_SIZE];
+ int len1, len2;
+
+ /* Must compare scheme first, as the second URI may not be SIP URL. */
+ if (pj_stricmp(pjsip_uri_get_scheme(url1), pjsip_uri_get_scheme(url2)))
+ return -1;
+
+ len1 = pjsip_url_print(context, url1, str_url1, sizeof(str_url1));
+ if (len1 < 1) {
+ pj_assert(0);
+ return -1;
+ }
+ len2 = pjsip_url_print(context, url2, str_url2, sizeof(str_url2));
+ if (len2 < 1) {
+ pj_assert(0);
+ return -1;
+ }
+
+ if (len1 != len2) {
+ /* Not equal. */
+ return -1;
+ }
+
+ if (pj_native_strcmp(str_url1, str_url2)) {
+ /* Not equal */
+ return -1;
+ }
+
+ /* TODO: compare other-param and header-param in more intelligent manner. */
+ PJ_TODO(HPARAM_AND_OTHER_PARAM_COMPARISON_IN_URL_COMPARISON)
+
+ if (pj_strcmp(&url1->other_param, &url2->other_param)) {
+ /* Not equal. */
+ return -1;
+ }
+ if (pj_strcmp(&url1->header_param, &url2->header_param)) {
+ /* Not equal. */
+ return -1;
+ }
+
+ /* Seems to be equal, isn't it. */
+ return 0;
+
+}
+
+
+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);
+ pj_strdup( pool, &url->other_param, &rhs->other_param);
+ pj_strdup( 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;
+}
+
diff --git a/pjsip/src/pjsua/getopt.c b/pjsip/src/pjsua/getopt.c
index 5d4689c..ce7891d 100644
--- a/pjsip/src/pjsua/getopt.c
+++ b/pjsip/src/pjsua/getopt.c
@@ -1,1046 +1,1068 @@
-/* $Id$
- *
- */
-
-#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$
+ *
+ */
+/*
+ * PJSIP - SIP Stack
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ * Benny Prijono <bennylp@bulukucing.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; 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 b5c91d0..d9778f1 100644
--- a/pjsip/src/pjsua/getopt.h
+++ b/pjsip/src/pjsua/getopt.h
@@ -1,142 +1,164 @@
-/* $Id$
- *
- */
-/* 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$
+ *
+ */
+/*
+ * PJSIP - SIP Stack
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ * Benny Prijono <bennylp@bulukucing.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+/* 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 a8a0f49..35a89c8 100644
--- a/pjsip/src/pjsua/main.c
+++ b/pjsip/src/pjsua/main.c
@@ -1,1813 +1,1835 @@
-/* $Id$
- *
- */
-
-#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, ®c_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$
+ *
+ */
+/*
+ * PJSIP - SIP Stack
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ * Benny Prijono <bennylp@bulukucing.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; 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, ®c_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 58cc795..d5a1c99 100644
--- a/pjsip/src/pjsua/misc.c
+++ b/pjsip/src/pjsua/misc.c
@@ -1,470 +1,492 @@
-/* $Id$
- *
- */
-
-/*
- * 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$
+ *
+ */
+/*
+ * PJSIP - SIP Stack
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ * Benny Prijono <bennylp@bulukucing.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; 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/tests/pjsip-core/main.c b/pjsip/src/tests/pjsip-core/main.c
index 6212ba4..4a450dd 100644
--- a/pjsip/src/tests/pjsip-core/main.c
+++ b/pjsip/src/tests/pjsip-core/main.c
@@ -1,17 +1,39 @@
-/* $Id$
- *
- */
-#include "test.h"
-#include <stdio.h>
-
-int main()
-{
- test_uri();
- test_msg();
-
-#if !IS_PROFILING
- puts("Press <ENTER> to quit.");
- fgets( s, sizeof(s), stdin);
-#endif
- return 0;
-}
+/* $Id$
+ *
+ */
+/*
+ * PJSIP - SIP Stack
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ * Benny Prijono <bennylp@bulukucing.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+#include "test.h"
+#include <stdio.h>
+
+int main()
+{
+ test_uri();
+ test_msg();
+
+#if !IS_PROFILING
+ puts("Press <ENTER> to quit.");
+ fgets( s, sizeof(s), stdin);
+#endif
+ return 0;
+}
diff --git a/pjsip/src/tests/pjsip-core/test.h b/pjsip/src/tests/pjsip-core/test.h
index 8bb57b2..7708313 100644
--- a/pjsip/src/tests/pjsip-core/test.h
+++ b/pjsip/src/tests/pjsip-core/test.h
@@ -1,11 +1,33 @@
-/* $Id$
- *
- */
-#include <pj/types.h>
-
-pj_status_t test_uri(void);
-pj_status_t test_msg(void);
-
-#define SILENT 1
-#define IS_PROFILING 1
-#define LOOP 2000
+/* $Id$
+ *
+ */
+/*
+ * PJSIP - SIP Stack
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ * Benny Prijono <bennylp@bulukucing.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+#include <pj/types.h>
+
+pj_status_t test_uri(void);
+pj_status_t test_msg(void);
+
+#define SILENT 1
+#define IS_PROFILING 1
+#define LOOP 2000
diff --git a/pjsip/src/tests/pjsip-core/test_msg.c b/pjsip/src/tests/pjsip-core/test_msg.c
index 7326e37..395d1ea 100644
--- a/pjsip/src/tests/pjsip-core/test_msg.c
+++ b/pjsip/src/tests/pjsip-core/test_msg.c
@@ -1,425 +1,447 @@
-/* $Id$
- *
- */
-#include <pjsip/sip_msg.h>
-#include <pjsip/sip_parser.h>
-#include <pj/os.h>
-#include <pj/pool.h>
-#include <pj/string.h>
-#include <stdlib.h>
-#include <stdio.h>
-#include "test.h"
-
-#define ERR_SYNTAX_ERR (-2)
-#define ERR_NOT_EQUAL (-3)
-#define ERR_SYSTEM (-4)
-
-
-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_caching_pool cp;
-static pj_pool_factory *pf = &cp.factory;
-static pj_uint32_t parse_len, parse_time, print_time;
-
-static void pool_error(pj_pool_t *pool, pj_size_t sz)
-{
- PJ_UNUSED_ARG(pool)
- PJ_UNUSED_ARG(sz)
-
- pj_assert(0);
- exit(1);
-}
-
-static const char *STATUS_STR(pj_status_t status)
-{
- switch (status) {
- case 0: return "OK";
- case ERR_SYNTAX_ERR: return "Syntax Error";
- case ERR_NOT_EQUAL: return "Not Equal";
- case ERR_SYSTEM: return "System Error";
- }
- return "???";
-}
-
-static pj_status_t test_entry( struct test_msg *entry )
-{
- pjsip_msg *parsed_msg, *ref_msg;
- pj_pool_t *pool;
- pj_status_t status = PJ_SUCCESS;
- int len;
- pj_str_t str1, str2;
- pjsip_hdr *hdr1, *hdr2;
- pj_hr_timestamp t1, t2;
- char *msgbuf;
-
- enum { BUFLEN = 512 };
-
- pool = pj_pool_create( pf, "",
- PJSIP_POOL_LEN_RDATA*2, PJSIP_POOL_INC_RDATA,
- &pool_error);
-
- if (entry->len == 0) {
- entry->len = strlen(entry->msg);
- }
-
- /* Parse message. */
- parse_len += entry->len;
- pj_hr_gettimestamp(&t1);
- parsed_msg = pjsip_parse_msg(pool, entry->msg, entry->len, NULL);
- if (parsed_msg == NULL) {
- status = ERR_SYNTAX_ERR;
- goto on_return;
- }
- pj_hr_gettimestamp(&t2);
- parse_time += t2.u32.lo - t1.u32.lo;
-
-#if IS_PROFILING
- goto print_msg;
-#endif
-
- /* 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 = ERR_NOT_EQUAL;
- 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 = ERR_NOT_EQUAL;
- 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 = ERR_SYSTEM;
- goto on_return;
- }
- str1.slen = len;
-
- len = hdr2->vptr->print_on(hdr2, str2.ptr, BUFLEN);
- if (len < 1) {
- status = ERR_SYSTEM;
- goto on_return;
- }
- str2.slen = len;
-
- if (!SILENT) {
- printf("hdr1='%.*s'\n"
- "hdr2='%.*s'\n\n",
- str1.slen, str1.ptr,
- str2.slen, str2.ptr);
- }
- if (pj_strcmp(&str1, &str2) != 0) {
- status = ERR_NOT_EQUAL;
- goto on_return;
- }
-
- hdr1 = hdr1->next;
- hdr2 = hdr2->next;
- }
-
- if (hdr1 != &parsed_msg->hdr || hdr2 != &ref_msg->hdr) {
- status = ERR_NOT_EQUAL;
- goto on_return;
- }
-
- /* Print message. */
-#if IS_PROFILING
-print_msg:
-#endif
- msgbuf = pj_pool_alloc(pool, PJSIP_MAX_PKT_LEN);
- if (msgbuf == NULL) {
- status = ERR_SYSTEM;
- goto on_return;
- }
- pj_hr_gettimestamp(&t1);
- len = pjsip_msg_print(parsed_msg, msgbuf, PJSIP_MAX_PKT_LEN);
- if (len < 1) {
- status = ERR_SYSTEM;
- goto on_return;
- }
- pj_hr_gettimestamp(&t2);
- print_time += t2.u32.lo - t1.u32.lo;
- status = PJ_SUCCESS;
-
-on_return:
- pj_pool_release(pool);
- return status;
-}
-
-static void warm_up()
-{
- pj_pool_t *pool;
- pool = pj_pool_create( pf, "",
- PJSIP_POOL_LEN_RDATA*2, PJSIP_POOL_INC_RDATA,
- &pool_error);
- pj_pool_release(pool);
-}
-
-
-pj_status_t test_msg(void)
-{
- pj_status_t status;
- unsigned i;
-
- pj_caching_pool_init(&cp, &pj_pool_factory_default_policy, 0);
- warm_up();
-
- for (i=0; i<LOOP; ++i) {
- status = test_entry( &test_array[0] );
- }
- printf("%s\n", STATUS_STR(status));
-
- printf("Total bytes: %u, parse time=%f/char, print time=%f/char\n",
- parse_len,
- parse_time*1.0/parse_len,
- print_time*1.0/parse_len);
- return PJ_SUCCESS;
-}
-
-/*****************************************************************************/
-
-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$
+ *
+ */
+/*
+ * PJSIP - SIP Stack
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ * Benny Prijono <bennylp@bulukucing.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; 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/sip_parser.h>
+#include <pj/os.h>
+#include <pj/pool.h>
+#include <pj/string.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include "test.h"
+
+#define ERR_SYNTAX_ERR (-2)
+#define ERR_NOT_EQUAL (-3)
+#define ERR_SYSTEM (-4)
+
+
+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_caching_pool cp;
+static pj_pool_factory *pf = &cp.factory;
+static pj_uint32_t parse_len, parse_time, print_time;
+
+static void pool_error(pj_pool_t *pool, pj_size_t sz)
+{
+ PJ_UNUSED_ARG(pool)
+ PJ_UNUSED_ARG(sz)
+
+ pj_assert(0);
+ exit(1);
+}
+
+static const char *STATUS_STR(pj_status_t status)
+{
+ switch (status) {
+ case 0: return "OK";
+ case ERR_SYNTAX_ERR: return "Syntax Error";
+ case ERR_NOT_EQUAL: return "Not Equal";
+ case ERR_SYSTEM: return "System Error";
+ }
+ return "???";
+}
+
+static pj_status_t test_entry( struct test_msg *entry )
+{
+ pjsip_msg *parsed_msg, *ref_msg;
+ pj_pool_t *pool;
+ pj_status_t status = PJ_SUCCESS;
+ int len;
+ pj_str_t str1, str2;
+ pjsip_hdr *hdr1, *hdr2;
+ pj_hr_timestamp t1, t2;
+ char *msgbuf;
+
+ enum { BUFLEN = 512 };
+
+ pool = pj_pool_create( pf, "",
+ PJSIP_POOL_LEN_RDATA*2, PJSIP_POOL_INC_RDATA,
+ &pool_error);
+
+ if (entry->len == 0) {
+ entry->len = strlen(entry->msg);
+ }
+
+ /* Parse message. */
+ parse_len += entry->len;
+ pj_hr_gettimestamp(&t1);
+ parsed_msg = pjsip_parse_msg(pool, entry->msg, entry->len, NULL);
+ if (parsed_msg == NULL) {
+ status = ERR_SYNTAX_ERR;
+ goto on_return;
+ }
+ pj_hr_gettimestamp(&t2);
+ parse_time += t2.u32.lo - t1.u32.lo;
+
+#if IS_PROFILING
+ goto print_msg;
+#endif
+
+ /* 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 = ERR_NOT_EQUAL;
+ 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 = ERR_NOT_EQUAL;
+ 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 = ERR_SYSTEM;
+ goto on_return;
+ }
+ str1.slen = len;
+
+ len = hdr2->vptr->print_on(hdr2, str2.ptr, BUFLEN);
+ if (len < 1) {
+ status = ERR_SYSTEM;
+ goto on_return;
+ }
+ str2.slen = len;
+
+ if (!SILENT) {
+ printf("hdr1='%.*s'\n"
+ "hdr2='%.*s'\n\n",
+ str1.slen, str1.ptr,
+ str2.slen, str2.ptr);
+ }
+ if (pj_strcmp(&str1, &str2) != 0) {
+ status = ERR_NOT_EQUAL;
+ goto on_return;
+ }
+
+ hdr1 = hdr1->next;
+ hdr2 = hdr2->next;
+ }
+
+ if (hdr1 != &parsed_msg->hdr || hdr2 != &ref_msg->hdr) {
+ status = ERR_NOT_EQUAL;
+ goto on_return;
+ }
+
+ /* Print message. */
+#if IS_PROFILING
+print_msg:
+#endif
+ msgbuf = pj_pool_alloc(pool, PJSIP_MAX_PKT_LEN);
+ if (msgbuf == NULL) {
+ status = ERR_SYSTEM;
+ goto on_return;
+ }
+ pj_hr_gettimestamp(&t1);
+ len = pjsip_msg_print(parsed_msg, msgbuf, PJSIP_MAX_PKT_LEN);
+ if (len < 1) {
+ status = ERR_SYSTEM;
+ goto on_return;
+ }
+ pj_hr_gettimestamp(&t2);
+ print_time += t2.u32.lo - t1.u32.lo;
+ status = PJ_SUCCESS;
+
+on_return:
+ pj_pool_release(pool);
+ return status;
+}
+
+static void warm_up()
+{
+ pj_pool_t *pool;
+ pool = pj_pool_create( pf, "",
+ PJSIP_POOL_LEN_RDATA*2, PJSIP_POOL_INC_RDATA,
+ &pool_error);
+ pj_pool_release(pool);
+}
+
+
+pj_status_t test_msg(void)
+{
+ pj_status_t status;
+ unsigned i;
+
+ pj_caching_pool_init(&cp, &pj_pool_factory_default_policy, 0);
+ warm_up();
+
+ for (i=0; i<LOOP; ++i) {
+ status = test_entry( &test_array[0] );
+ }
+ printf("%s\n", STATUS_STR(status));
+
+ printf("Total bytes: %u, parse time=%f/char, print time=%f/char\n",
+ parse_len,
+ parse_time*1.0/parse_len,
+ print_time*1.0/parse_len);
+ return PJ_SUCCESS;
+}
+
+/*****************************************************************************/
+
+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/tests/pjsip-core/test_uri.c b/pjsip/src/tests/pjsip-core/test_uri.c
index 0c78660..bdbe8f1 100644
--- a/pjsip/src/tests/pjsip-core/test_uri.c
+++ b/pjsip/src/tests/pjsip-core/test_uri.c
@@ -1,645 +1,667 @@
-/* $Id$
- *
- */
-#include <pjsip/sip_parser.h>
-#include <pjsip/sip_uri.h>
-#include <pj/os.h>
-#include <pj/pool.h>
-#include <pj/string.h>
-#include <stdlib.h>
-#include <stdio.h>
-#include "test.h"
-
-#define ERR_SYNTAX_ERR (-2)
-#define ERR_NOT_EQUAL (-3)
-
-#define ALPHANUM "abcdefghijklmnopqrstuvwxyz" \
- "ABCDEFGHIJKLMNOPQRSTUVWXYZ" \
- "0123456789"
-#define MARK "-_.!~*'()"
-#define USER "&=+$,;?/%"
-#define PASS "&=+$,%"
-#define PARAM_CHAR "[]/:&+$" MARK "%"
-
-#define POOL_SIZE 4096
-
-static const char *STATUS_STR(pj_status_t status)
-{
- switch (status) {
- case 0: return "OK";
- case ERR_SYNTAX_ERR: return "Syntax Error";
- case ERR_NOT_EQUAL: return "Not Equal";
- }
- return "???";
-}
-
-static pj_uint32_t parse_len, parse_time, print_time;
-static pj_caching_pool cp;
-
-
-/* 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 );
-
-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,
- },
- {
- PJ_SUCCESS,
- "",
- NULL,
- },
-};
-
-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;
-}
-
-/*****************************************************************************/
-
-static void pool_error(pj_pool_t *pool, pj_size_t sz)
-{
- PJ_UNUSED_ARG(pool)
- PJ_UNUSED_ARG(sz)
-
- pj_assert(0);
- exit(1);
-}
-
-/*
- * Test one test entry.
- */
-static pj_status_t test_entry(struct uri_test *entry)
-{
- pj_status_t status;
- pj_pool_t *pool;
- int len;
- pjsip_uri *parsed_uri, *ref_uri;
- pj_str_t s1 = {NULL, 0}, s2 = {NULL, 0};
- pj_hr_timestamp t1, t2;
-
- pool = (*cp.factory.create_pool)( &cp.factory, "", POOL_SIZE, 0, &pool_error);
-
- /* Parse URI text. */
- pj_hr_gettimestamp(&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 : ERR_SYNTAX_ERR;
- goto on_return;
- }
- pj_hr_gettimestamp(&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_hr_gettimestamp(&t1);
- len = pjsip_uri_print( PJSIP_URI_IN_OTHER, parsed_uri, s1.ptr, PJSIP_MAX_URL_SIZE);
- if (len < 1) {
- status = -1;
- 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 = -1;
- goto on_return;
- }
- s2.slen = len;
- pj_hr_gettimestamp(&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 : ERR_NOT_EQUAL;
- goto on_return;
-
- } else {
- /* Equal. See if this is the expected status. */
- status = entry->status==PJ_SUCCESS ? PJ_SUCCESS : -1;
- if (status != PJ_SUCCESS) {
- goto on_return;
- }
- }
-
- /* Compare text. */
- if (pj_strcmp(&s1, &s2) != 0) {
- /* Not equal. */
- status = ERR_NOT_EQUAL;
- }
-
-on_return:
- if (!SILENT) {
- printf("%.2d %s (expected status=%s)\n"
- " str=%s\n"
- " uri=%.*s\n"
- " ref=%.*s\n\n",
- entry-uri_test_array,
- STATUS_STR(status),
- STATUS_STR(entry->status),
- entry->str,
- (int)s1.slen, s1.ptr, (int)s2.slen, s2.ptr);
- }
-
- pj_pool_release(pool);
- return status;
-}
-
-static void warm_up(pj_pool_factory *pf)
-{
- pj_pool_t *pool;
- struct uri_test *entry;
-
- pool = pj_pool_create(pf, "", POOL_SIZE, 0, &pool_error);
- pjsip_parse_uri(pool, "sip:host", 8, 0);
- entry = &uri_test_array[0];
- while (entry->creator) {
- entry->len = strlen(entry->str);
- ++entry;
- }
- pj_pool_release(pool);
-}
-
-//#if !IS_PROFILING
-#if 1
-pj_status_t test_uri()
-{
- struct uri_test *entry;
- int i=0, err=0;
- pj_status_t status;
- pj_hr_timestamp t1, t2;
- pj_uint32_t total_time;
-
- pj_caching_pool_init(&cp, &pj_pool_factory_default_policy, 0);
- warm_up(&cp.factory);
-
- pj_hr_gettimestamp(&t1);
- for (i=0; i<LOOP; ++i) {
- entry = &uri_test_array[0];
- while (entry->creator) {
- status = test_entry(entry);
- if (status != PJ_SUCCESS) {
- ++err;
- }
- ++entry;
- }
- }
- pj_hr_gettimestamp(&t2);
- total_time = t2.u32.lo - t1.u32.lo;
-
- printf("Error=%d\n", err);
- printf("Total parse len: %u bytes\n", parse_len);
- printf("Total parse time: %u (%f/char), print time: %u (%f/char)\n",
- parse_time, parse_time*1.0/parse_len,
- print_time, print_time*1.0/parse_len);
- printf("Total time: %u (%f/char)\n", total_time, total_time*1.0/parse_len);
- return err;
-}
-
-#else
-
-pj_status_t test_uri()
-{
- struct uri_test *entry;
- unsigned i;
-
- warm_up();
- pj_caching_pool_init(&cp, 1024*1024);
-
- for (i=0; i<LOOP; ++i) {
- entry = &uri_test_array[0];
- while (entry->creator) {
- pj_pool_t *pool;
- pjsip_uri *uri1, *uri2;
-
- pool = pj_pool_create( &cp.factory, "", POOL_SIZE, 0, &pool_error);
- uri1 = pjsip_parse_uri(pool, entry->str, strlen(entry->str));
- pj_pool_release(pool);
- ++entry;
- }
- }
-
- return 0;
-}
-
-#endif
+/* $Id$
+ *
+ */
+/*
+ * PJSIP - SIP Stack
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ * Benny Prijono <bennylp@bulukucing.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; 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 <pj/os.h>
+#include <pj/pool.h>
+#include <pj/string.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include "test.h"
+
+#define ERR_SYNTAX_ERR (-2)
+#define ERR_NOT_EQUAL (-3)
+
+#define ALPHANUM "abcdefghijklmnopqrstuvwxyz" \
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZ" \
+ "0123456789"
+#define MARK "-_.!~*'()"
+#define USER "&=+$,;?/%"
+#define PASS "&=+$,%"
+#define PARAM_CHAR "[]/:&+$" MARK "%"
+
+#define POOL_SIZE 4096
+
+static const char *STATUS_STR(pj_status_t status)
+{
+ switch (status) {
+ case 0: return "OK";
+ case ERR_SYNTAX_ERR: return "Syntax Error";
+ case ERR_NOT_EQUAL: return "Not Equal";
+ }
+ return "???";
+}
+
+static pj_uint32_t parse_len, parse_time, print_time;
+static pj_caching_pool cp;
+
+
+/* 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 );
+
+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,
+ },
+ {
+ PJ_SUCCESS,
+ "",
+ NULL,
+ },
+};
+
+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;
+}
+
+/*****************************************************************************/
+
+static void pool_error(pj_pool_t *pool, pj_size_t sz)
+{
+ PJ_UNUSED_ARG(pool)
+ PJ_UNUSED_ARG(sz)
+
+ pj_assert(0);
+ exit(1);
+}
+
+/*
+ * Test one test entry.
+ */
+static pj_status_t test_entry(struct uri_test *entry)
+{
+ pj_status_t status;
+ pj_pool_t *pool;
+ int len;
+ pjsip_uri *parsed_uri, *ref_uri;
+ pj_str_t s1 = {NULL, 0}, s2 = {NULL, 0};
+ pj_hr_timestamp t1, t2;
+
+ pool = (*cp.factory.create_pool)( &cp.factory, "", POOL_SIZE, 0, &pool_error);
+
+ /* Parse URI text. */
+ pj_hr_gettimestamp(&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 : ERR_SYNTAX_ERR;
+ goto on_return;
+ }
+ pj_hr_gettimestamp(&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_hr_gettimestamp(&t1);
+ len = pjsip_uri_print( PJSIP_URI_IN_OTHER, parsed_uri, s1.ptr, PJSIP_MAX_URL_SIZE);
+ if (len < 1) {
+ status = -1;
+ 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 = -1;
+ goto on_return;
+ }
+ s2.slen = len;
+ pj_hr_gettimestamp(&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 : ERR_NOT_EQUAL;
+ goto on_return;
+
+ } else {
+ /* Equal. See if this is the expected status. */
+ status = entry->status==PJ_SUCCESS ? PJ_SUCCESS : -1;
+ if (status != PJ_SUCCESS) {
+ goto on_return;
+ }
+ }
+
+ /* Compare text. */
+ if (pj_strcmp(&s1, &s2) != 0) {
+ /* Not equal. */
+ status = ERR_NOT_EQUAL;
+ }
+
+on_return:
+ if (!SILENT) {
+ printf("%.2d %s (expected status=%s)\n"
+ " str=%s\n"
+ " uri=%.*s\n"
+ " ref=%.*s\n\n",
+ entry-uri_test_array,
+ STATUS_STR(status),
+ STATUS_STR(entry->status),
+ entry->str,
+ (int)s1.slen, s1.ptr, (int)s2.slen, s2.ptr);
+ }
+
+ pj_pool_release(pool);
+ return status;
+}
+
+static void warm_up(pj_pool_factory *pf)
+{
+ pj_pool_t *pool;
+ struct uri_test *entry;
+
+ pool = pj_pool_create(pf, "", POOL_SIZE, 0, &pool_error);
+ pjsip_parse_uri(pool, "sip:host", 8, 0);
+ entry = &uri_test_array[0];
+ while (entry->creator) {
+ entry->len = strlen(entry->str);
+ ++entry;
+ }
+ pj_pool_release(pool);
+}
+
+//#if !IS_PROFILING
+#if 1
+pj_status_t test_uri()
+{
+ struct uri_test *entry;
+ int i=0, err=0;
+ pj_status_t status;
+ pj_hr_timestamp t1, t2;
+ pj_uint32_t total_time;
+
+ pj_caching_pool_init(&cp, &pj_pool_factory_default_policy, 0);
+ warm_up(&cp.factory);
+
+ pj_hr_gettimestamp(&t1);
+ for (i=0; i<LOOP; ++i) {
+ entry = &uri_test_array[0];
+ while (entry->creator) {
+ status = test_entry(entry);
+ if (status != PJ_SUCCESS) {
+ ++err;
+ }
+ ++entry;
+ }
+ }
+ pj_hr_gettimestamp(&t2);
+ total_time = t2.u32.lo - t1.u32.lo;
+
+ printf("Error=%d\n", err);
+ printf("Total parse len: %u bytes\n", parse_len);
+ printf("Total parse time: %u (%f/char), print time: %u (%f/char)\n",
+ parse_time, parse_time*1.0/parse_len,
+ print_time, print_time*1.0/parse_len);
+ printf("Total time: %u (%f/char)\n", total_time, total_time*1.0/parse_len);
+ return err;
+}
+
+#else
+
+pj_status_t test_uri()
+{
+ struct uri_test *entry;
+ unsigned i;
+
+ warm_up();
+ pj_caching_pool_init(&cp, 1024*1024);
+
+ for (i=0; i<LOOP; ++i) {
+ entry = &uri_test_array[0];
+ while (entry->creator) {
+ pj_pool_t *pool;
+ pjsip_uri *uri1, *uri2;
+
+ pool = pj_pool_create( &cp.factory, "", POOL_SIZE, 0, &pool_error);
+ uri1 = pjsip_parse_uri(pool, entry->str, strlen(entry->str));
+ pj_pool_release(pool);
+ ++entry;
+ }
+ }
+
+ return 0;
+}
+
+#endif