* #27232: jni: added pjproject checkout as regular git content

We will remove it once the next release of pjsip (with Android support)
comes out and is merged into SFLphone.
diff --git a/jni/pjproject-android/.svn/pristine/05/051fef84896e76c3f43c27daba87fedab537faf5.svn-base b/jni/pjproject-android/.svn/pristine/05/051fef84896e76c3f43c27daba87fedab537faf5.svn-base
new file mode 100644
index 0000000..4298aa3
--- /dev/null
+++ b/jni/pjproject-android/.svn/pristine/05/051fef84896e76c3f43c27daba87fedab537faf5.svn-base
@@ -0,0 +1,766 @@
+/* $Id$ */
+/* 
+ * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
+ * Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA 
+ */
+#include "test.h"
+#include <pjlib-util.h>
+#include <pjlib.h>
+
+
+#if INCLUDE_ENCRYPTION_TEST
+
+/*
+ * Encryption algorithm tests.
+ */
+#define THIS_FILE   "encryption.c"
+
+
+/*
+ * SHA1 test from the original sha1.c source file.
+ */
+static char *sha1_test_data[] = {
+    "abc",
+    "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq",
+    "A million repetitions of 'a'"
+};
+static char *sha1_test_results[] = {
+    "A9993E36 4706816A BA3E2571 7850C26C 9CD0D89D",
+    "84983E44 1C3BD26E BAAE4AA1 F95129E5 E54670F1",
+    "34AA973C D4C4DAA4 F61EEB2B DBAD2731 6534016F"
+};
+
+
+static void digest_to_hex(const pj_uint8_t digest[PJ_SHA1_DIGEST_SIZE], 
+			  char *output)
+{
+    int i,j;
+    char *c = output;
+    
+    for (i = 0; i < PJ_SHA1_DIGEST_SIZE/4; i++) {
+        for (j = 0; j < 4; j++) {
+            sprintf(c,"%02X", digest[i*4+j]);
+            c += 2;
+        }
+        sprintf(c, " ");
+        c += 1;
+    }
+    *(c - 1) = '\0';
+}
+
+static int sha1_test1(void)
+{
+    enum { MILLION = 1000000 };
+    int k;
+    pj_sha1_context context;
+    pj_uint8_t digest[20];
+    char output[80];
+    pj_pool_t *pool;
+    pj_uint8_t *block;
+
+    PJ_LOG(3, (THIS_FILE, "  SHA1 test vector 1 from sha1.c.."));
+    
+    for (k = 0; k < 2; k++){ 
+        pj_sha1_init(&context);
+        pj_sha1_update(&context, (pj_uint8_t*)sha1_test_data[k], 
+		       pj_ansi_strlen(sha1_test_data[k]));
+        pj_sha1_final(&context, digest);
+	digest_to_hex(digest, output);
+
+        if (pj_ansi_strcmp(output, sha1_test_results[k])) {
+	    PJ_LOG(3, (THIS_FILE, "    incorrect hash result on k=%d", k));
+            return -10;
+        }
+    }
+
+    /* million 'a' vector we feed separately */
+    pj_sha1_init(&context);
+    for (k = 0; k < MILLION; k++)
+        pj_sha1_update(&context, (pj_uint8_t*)"a", 1);
+    pj_sha1_final(&context, digest);
+    digest_to_hex(digest, output);
+    if (strcmp(output, sha1_test_results[2])) {
+	PJ_LOG(3, (THIS_FILE, "    incorrect hash result!"));
+        return -20;
+    }
+
+    /* million 'a' test, using block */
+    pool = pj_pool_create(mem, "sha1test", 256, 512, NULL);
+    block = (pj_uint8_t*)pj_pool_alloc(pool, MILLION);
+    pj_memset(block, 'a', MILLION);
+
+    pj_sha1_init(&context);
+    pj_sha1_update(&context, block, MILLION);
+    pj_sha1_final(&context, digest);
+    digest_to_hex(digest, output);
+    if (strcmp(output, sha1_test_results[2])) {
+	pj_pool_release(pool);
+	PJ_LOG(3, (THIS_FILE, "    incorrect hash result for block update!"));
+        return -21;
+    }
+
+    /* verify that original buffer was not modified */
+    for (k=0; k<MILLION; ++k) {
+	if (block[k] != 'a') {
+	    pj_pool_release(pool);
+	    PJ_LOG(3, (THIS_FILE, "    block was modified!"));
+	    return -22;
+	}
+    }
+
+    pj_pool_release(pool);
+
+    /* success */
+    return(0);
+}
+
+
+/*
+ * SHA1 test from RFC 3174
+ */
+/*
+ *  Define patterns for testing
+ */
+#define TEST1   "abc"
+#define TEST2a  "abcdbcdecdefdefgefghfghighijhi"
+#define TEST2b  "jkijkljklmklmnlmnomnopnopq"
+#define TEST2   TEST2a TEST2b
+#define TEST3   "a"
+#define TEST4a  "01234567012345670123456701234567"
+#define TEST4b  "01234567012345670123456701234567"
+    /* an exact multiple of 512 bits */
+#define TEST4   TEST4a TEST4b
+
+static char *testarray[4] =
+{
+    TEST1,
+    TEST2,
+    TEST3,
+    TEST4
+};
+static int repeatcount[4] = { 1, 1, 1000000, 10 };
+static char *resultarray[4] =
+{
+    "A9993E36 4706816A BA3E2571 7850C26C 9CD0D89D",
+    "84983E44 1C3BD26E BAAE4AA1 F95129E5 E54670F1",
+    "34AA973C D4C4DAA4 F61EEB2B DBAD2731 6534016F",
+    "DEA356A2 CDDD90C7 A7ECEDC5 EBB56393 4F460452"
+};
+
+static int sha1_test2(void)
+{
+    pj_sha1_context sha;
+    int i;
+    pj_uint8_t digest[20];
+    char char_digest[64];
+
+    PJ_LOG(3, (THIS_FILE, "  SHA1 test vector 2 from rfc 3174.."));
+
+    for(i = 0; i < 4; ++i) {
+	int j;
+
+        pj_sha1_init(&sha);
+
+        for(j = 0; j < repeatcount[i]; ++j) {
+            pj_sha1_update(&sha,
+			   (const pj_uint8_t *) testarray[i],
+			   pj_ansi_strlen(testarray[i]));
+        }
+
+        pj_sha1_final(&sha, digest);
+
+	digest_to_hex(digest, char_digest);
+	if (pj_ansi_strcmp(char_digest, resultarray[i])) {
+	    PJ_LOG(3, (THIS_FILE, "    digest mismatch in test %d", i));
+	    return -40;
+	}
+    }
+
+    return 0;
+}
+
+
+/*
+ * HMAC-MD5 and HMAC-SHA1 test vectors from RFC 2202
+ */
+struct rfc2202_test
+{
+    char       *key;
+    unsigned	key_len;
+    char       *input;
+    unsigned	input_len;
+    char       *md5_digest;
+    char       *sha1_digest;
+};
+
+
+struct rfc2202_test rfc2202_test_vector[] = 
+{
+    {
+	"\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b",
+	16,
+	"Hi There",
+	8,
+	"\x92\x94\x72\x7a\x36\x38\xbb\x1c\x13\xf4\x8e\xf8\x15\x8b\xfc\x9d",
+	NULL
+    },
+    {
+	"\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b"
+	"\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b",
+	20,
+	"Hi There",
+	8,
+	NULL,
+	"\xb6\x17\x31\x86\x55\x05\x72\x64\xe2\x8b\xc0\xb6\xfb\x37\x8c\x8e\xf1\x46\xbe\x00"
+    },
+    {
+	"Jefe",
+	4,
+	"what do ya want for nothing?",
+	28,
+	"\x75\x0c\x78\x3e\x6a\xb0\xb5\x03\xea\xa8\x6e\x31\x0a\x5d\xb7\x38",
+	"\xef\xfc\xdf\x6a\xe5\xeb\x2f\xa2\xd2\x74\x16\xd5\xf1\x84\xdf\x9c\x25\x9a\x7c\x79"
+    },
+    {
+	"\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa"
+	"\xaa\xaa\xaa\xaa\xaa\xaa",
+	16,
+	"\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd"
+	"\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd"
+	"\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd"
+	"\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd"
+	"\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd",
+	50,
+	"\x56\xbe\x34\x52\x1d\x14\x4c\x88\xdb\xb8\xc7\x33\xf0\xe8\xb3\xf6",
+	NULL
+    },
+    {
+	"\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa"
+	"\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa",
+	20,
+	"\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd"
+	"\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd"
+	"\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd"
+	"\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd"
+	"\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd",
+	50,
+	NULL,
+	"\x12\x5d\x73\x42\xb9\xac\x11\xcd\x91\xa3\x9a\xf4\x8a\xa1\x7b\x4f\x63\xf1\x75\xd3"
+    },
+    {
+	"\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19",
+	25,
+	"\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd"
+	"\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd"
+	"\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd"
+	"\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd"
+	"\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd",
+	50,
+	"\x69\x7e\xaf\x0a\xca\x3a\x3a\xea\x3a\x75\x16\x47\x46\xff\xaa\x79",
+	"\x4c\x90\x07\xf4\x02\x62\x50\xc6\xbc\x84\x14\xf9\xbf\x50\xc8\x6c\x2d\x72\x35\xda"
+    },
+    {
+	"\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c"
+	"\x0c\x0c\x0c\x0c\x0c\x0c",
+	16,
+	"Test With Truncation",
+	20,
+	"\x56\x46\x1e\xf2\x34\x2e\xdc\x00\xf9\xba\xb9\x95\x69\x0e\xfd\x4c",
+	NULL
+    },
+    {
+	"\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c"
+	"\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c",
+	20,
+	"Test With Truncation",
+	20,
+	NULL,
+	"\x4c\x1a\x03\x42\x4b\x55\xe0\x7f\xe7\xf2\x7b\xe1\xd5\x8b\xb9\x32\x4a\x9a\x5a\x04"
+    },
+    {
+	"\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa"
+	"\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa"
+	"\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa"
+	"\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa"
+	"\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa"
+	"\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa"
+	"\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa"
+	"\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa",
+	80,
+	"Test Using Larger Than Block-Size Key - Hash Key First",
+	54,
+	"\x6b\x1a\xb7\xfe\x4b\xd7\xbf\x8f\x0b\x62\xe6\xce\x61\xb9\xd0\xcd",
+	"\xaa\x4a\xe5\xe1\x52\x72\xd0\x0e\x95\x70\x56\x37\xce\x8a\x3b\x55\xed\x40\x21\x12"
+    },
+    {
+	"\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa"
+	"\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa"
+	"\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa"
+	"\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa"
+	"\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa"
+	"\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa"
+	"\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa"
+	"\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa",
+	80,
+	"Test Using Larger Than Block-Size Key and Larger Than One Block-Size Data",
+	73,
+	"\x6f\x63\x0f\xad\x67\xcd\xa0\xee\x1f\xb1\xf5\x62\xdb\x3a\xa5\x3e",
+	"\xe8\xe9\x9d\x0f\x45\x23\x7d\x78\x6d\x6b\xba\xa7\x96\x5c\x78\x08\xbb\xff\x1a\x91"
+    }
+};
+
+
+static int rfc2202_test(void)
+{
+    unsigned i;
+
+    PJ_LOG(3, (THIS_FILE, "  verifying test vectors from rfc 2202.."));
+
+    /* Verify that test vectors are valid */
+    for (i=0; i<PJ_ARRAY_SIZE(rfc2202_test_vector); ++i) {
+	PJ_ASSERT_RETURN(pj_ansi_strlen(rfc2202_test_vector[i].input) == 
+			    rfc2202_test_vector[i].input_len, -50);
+	PJ_ASSERT_RETURN(pj_ansi_strlen(rfc2202_test_vector[i].key) == 
+			    rfc2202_test_vector[i].key_len, -52);
+	PJ_ASSERT_RETURN(rfc2202_test_vector[i].md5_digest==NULL ||
+			    pj_ansi_strlen(rfc2202_test_vector[i].md5_digest)<=16,
+			    -54);
+	PJ_ASSERT_RETURN(rfc2202_test_vector[i].sha1_digest==NULL ||
+			    pj_ansi_strlen(rfc2202_test_vector[i].sha1_digest)<=20,
+			    -56);
+    }
+
+    /* Test HMAC-MD5 */
+    PJ_LOG(3, (THIS_FILE, "  HMAC-MD5 test vectors from rfc 2202.."));
+    for (i=0; i<PJ_ARRAY_SIZE(rfc2202_test_vector); ++i) {
+	pj_uint8_t digest_buf[18], *digest;
+
+	if (rfc2202_test_vector[i].md5_digest == NULL)
+	    continue;
+
+	digest_buf[0] = '\0';
+	digest_buf[17] = '\0';
+
+	digest = digest_buf+1;
+
+	pj_hmac_md5((pj_uint8_t*)rfc2202_test_vector[i].input, 
+		    rfc2202_test_vector[i].input_len,
+		    (pj_uint8_t*)rfc2202_test_vector[i].key, 
+		    rfc2202_test_vector[i].key_len,
+		    digest);
+
+	/* Check for overwrites */
+	if (digest_buf[0] != '\0' || digest_buf[17] != '\0') {
+	    PJ_LOG(3, (THIS_FILE, "    error: overwriting outside buffer on test %d", i));
+	    return -60;
+	}
+
+	/* Compare digest */
+	if (pj_memcmp(rfc2202_test_vector[i].md5_digest, digest, 16)) {
+	    PJ_LOG(3, (THIS_FILE, "    error: digest mismatch on test %d", i));
+	    return -65;
+	}
+    }
+
+    /* Test HMAC-SHA1 */
+    PJ_LOG(3, (THIS_FILE, "  HMAC-SHA1 test vectors from rfc 2202.."));
+    for (i=0; i<PJ_ARRAY_SIZE(rfc2202_test_vector); ++i) {
+	pj_uint8_t digest_buf[22], *digest;
+
+	if (rfc2202_test_vector[i].sha1_digest == NULL)
+	    continue;
+
+	digest_buf[0] = '\0';
+	digest_buf[21] = '\0';
+
+	digest = digest_buf+1;
+
+	pj_hmac_sha1((pj_uint8_t*)rfc2202_test_vector[i].input, 
+		     rfc2202_test_vector[i].input_len,
+		     (pj_uint8_t*)rfc2202_test_vector[i].key, 
+		     rfc2202_test_vector[i].key_len,
+		     digest);
+
+	/* Check for overwrites */
+	if (digest_buf[0] != '\0' || digest_buf[21] != '\0') {
+	    PJ_LOG(3, (THIS_FILE, "    error: overwriting outside buffer on test %d", i));
+	    return -70;
+	}
+
+	/* Compare digest */
+	if (pj_memcmp(rfc2202_test_vector[i].sha1_digest, digest, 20)) {
+	    PJ_LOG(3, (THIS_FILE, "    error: digest mismatch on test %d", i));
+	    return -75;
+	}
+    }
+
+
+    /* Success */
+    return 0;
+}
+
+/* CRC32 test data, generated from crc32 test on a Linux box */
+struct crc32_test_t
+{
+    char	    *input;
+    pj_uint32_t	     crc;
+} crc32_test_data[] = 
+{
+    {
+	"",
+	0x0
+    },
+    {
+	"Hello World",
+	0x4a17b156
+    },
+    {
+	/* Something read from /dev/random */
+	"\x21\x21\x98\x10\x62\x59\xbc\x58\x42\x24\xe5\xf3\x92\x0a\x68\x3c\xa7\x67\x73\xc3",
+	0x506693be
+    },
+    {
+	"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
+	0xcab11777
+    },
+    {
+	"123456789",
+	0xCBF43926
+    }
+};
+
+/*
+ * CRC32 test
+ */
+static int crc32_test(void)
+{
+    unsigned i;
+
+    PJ_LOG(3, (THIS_FILE, "  crc32 test.."));
+
+    /* testing pj_crc32_calc */
+    for (i=0; i<PJ_ARRAY_SIZE(crc32_test_data); ++i) {
+	pj_uint32_t crc;
+
+	crc = pj_crc32_calc((pj_uint8_t*)crc32_test_data[i].input,
+			    pj_ansi_strlen(crc32_test_data[i].input));
+	if (crc != crc32_test_data[i].crc) {
+	    PJ_LOG(3,(THIS_FILE, "    error: crc mismatch on test %d", i));
+	    return -80;
+	}
+    }
+
+    /* testing incremental CRC32 calculation */
+    for (i=0; i<PJ_ARRAY_SIZE(crc32_test_data); ++i) {
+	pj_crc32_context ctx;
+	pj_uint32_t crc0, crc1;
+	pj_size_t len;
+
+	len = pj_ansi_strlen(crc32_test_data[i].input);
+	crc0 = pj_crc32_calc((pj_uint8_t*)crc32_test_data[i].input, len);
+
+	pj_crc32_init(&ctx);
+	pj_crc32_update(&ctx, (pj_uint8_t*)crc32_test_data[i].input,
+			len / 2);
+
+	if (len/2 > 0) {
+	    pj_crc32_update(&ctx, (pj_uint8_t*)crc32_test_data[i].input + len/2,
+			    len - len/2);
+	}
+
+	crc1 = pj_crc32_final(&ctx);
+
+	if (crc0 != crc1) {
+	    PJ_LOG(3,(THIS_FILE, 
+		      "    error: crc algorithm error on test %d", i));
+	    return -85;
+	}
+
+    }
+    return 0;
+}
+
+enum
+{
+    ENCODE = 1,
+    DECODE = 2,
+    ENCODE_DECODE = 3
+};
+
+/*
+ * Base64 test vectors (RFC 4648)
+ */
+static struct base64_test_vec
+{
+    const char *base256;
+    const char *base64;
+    unsigned flag;
+} base64_test_vec[] = 
+{
+    {
+	"",
+	"",
+	ENCODE_DECODE
+    },
+    {
+	"f",
+	"Zg==",
+	ENCODE_DECODE
+    },
+    {
+	"fo",
+	"Zm8=",
+	ENCODE_DECODE
+    },
+    {
+	"foo",
+	"Zm9v",
+	ENCODE_DECODE
+    },
+    {
+	"foob",
+	"Zm9vYg==",
+	ENCODE_DECODE
+    },
+    {
+	"fooba",
+	"Zm9vYmE=",
+	ENCODE_DECODE
+    },
+    {
+	"foobar",
+	"Zm9vYmFy",
+	ENCODE_DECODE
+    },
+    {
+	"\x14\xfb\x9c\x03\xd9\x7e",
+	"FPucA9l+",
+	ENCODE_DECODE
+    },
+    {
+	"\x14\xfb\x9c\x03\xd9",
+	"FPucA9k=",
+	ENCODE_DECODE
+    },
+    {
+	"\x14\xfb\x9c\x03",
+	"FPucAw==",
+	ENCODE_DECODE
+    },
+    /* with whitespaces */
+    {
+	"foobar",
+	"Zm9v\r\nYmFy",
+	DECODE
+    },
+    {
+    	"foobar",
+    	"\nZ\r\nm 9\tv\nYm\nF\ny\n",
+    	DECODE
+    },
+};
+
+
+static int base64_test(void)
+{
+    unsigned i;
+    char output[80];
+    pj_status_t rc;
+
+    PJ_LOG(3, (THIS_FILE, "  base64 test.."));
+
+    for (i=0; i<PJ_ARRAY_SIZE(base64_test_vec); ++i) {
+	pj_str_t input;
+	int out_len;
+
+	/* Encode test */
+	if (base64_test_vec[i].flag & ENCODE) {
+	    out_len = sizeof(output);
+	    rc = pj_base64_encode((pj_uint8_t*)base64_test_vec[i].base256,
+				  (int)strlen(base64_test_vec[i].base256),
+				  output, &out_len);
+	    if (rc != PJ_SUCCESS)
+		return -90;
+
+	    if (out_len != (int)strlen(base64_test_vec[i].base64))
+		return -91;
+
+	    output[out_len] = '\0';
+	    if (strcmp(output, base64_test_vec[i].base64) != 0)
+		return -92;
+	}
+
+	/* Decode test */
+	if (base64_test_vec[i].flag & DECODE) {
+	    out_len = sizeof(output);
+	    input.ptr = (char*)base64_test_vec[i].base64;
+	    input.slen = strlen(base64_test_vec[i].base64);
+	    rc = pj_base64_decode(&input, (pj_uint8_t*)output, &out_len);
+	    if (rc != PJ_SUCCESS)
+		return -95;
+
+	    if (out_len != (int)strlen(base64_test_vec[i].base256))
+		return -96;
+
+	    output[out_len] = '\0';
+
+	    if (strcmp(output, base64_test_vec[i].base256) != 0)
+		return -97;
+	}
+    }
+
+    return 0;
+}
+
+
+int encryption_test()
+{
+    int rc;
+
+    rc = base64_test();
+    if (rc != 0)
+	return rc;
+
+    rc = sha1_test1();
+    if (rc != 0)
+	return rc;
+
+    rc = sha1_test2();
+    if (rc != 0)
+	return rc;
+
+    rc = rfc2202_test();
+    if (rc != 0)
+	return rc;
+
+    rc = crc32_test();
+    if (rc != 0)
+	return rc;
+
+    return 0;
+}
+
+static void crc32_update(pj_crc32_context *c, const pj_uint8_t *data,
+			 pj_size_t nbytes)
+{
+    pj_crc32_update(c, data, nbytes);
+}
+
+static void crc32_final(pj_crc32_context *ctx, pj_uint32_t *digest)
+{
+    *digest = pj_crc32_final(ctx);
+}
+
+int encryption_benchmark()
+{
+    pj_pool_t *pool;
+    pj_uint8_t *input;
+    union {
+	pj_md5_context md5_context;
+	pj_sha1_context sha1_context;
+    } context;
+    pj_uint8_t digest[32];
+    pj_size_t input_len;
+    struct algorithm
+    {
+	const char *name;
+	void (*init_context)(void*);
+	void (*update)(void*, const pj_uint8_t*, unsigned);
+	void (*final)(void*, void*);
+	pj_uint32_t t;
+    } algorithms[] = 
+    {
+	{
+	    "MD5  ",
+	    (void (*)(void*))&pj_md5_init,
+	    (void (*)(void*, const pj_uint8_t*, unsigned))&pj_md5_update,
+	    (void (*)(void*, void*))&pj_md5_final
+	},
+	{
+	    "SHA1 ",
+	    (void (*)(void*))&pj_sha1_init,
+	    (void (*)(void*, const pj_uint8_t*, unsigned))&pj_sha1_update,
+	    (void (*)(void*, void*))&pj_sha1_final
+	},
+	{
+	    "CRC32",
+	    (void (*)(void*))&pj_crc32_init,
+	    (void (*)(void*, const pj_uint8_t*, unsigned))&crc32_update,
+	    (void (*)(void*, void*))&crc32_final
+	}
+    };
+#if defined(PJ_DEBUG) && PJ_DEBUG!=0
+    enum { LOOP = 1000 };
+#else
+    enum { LOOP = 10000 };
+#endif
+    unsigned i;
+    double total_len;
+
+    input_len = 2048;
+    total_len = (unsigned)input_len * LOOP;
+    pool = pj_pool_create(mem, "enc", input_len+256, 0, NULL);
+    if (!pool)
+	return PJ_ENOMEM;
+
+    input = (pj_uint8_t*)pj_pool_alloc(pool, input_len);
+    pj_memset(input, '\xaa', input_len);
+    
+    PJ_LOG(3, (THIS_FILE, "  feeding %d Mbytes of data",
+	       (unsigned)(total_len/1024/1024)));
+
+    /* Dry run */
+    for (i=0; i<PJ_ARRAY_SIZE(algorithms); ++i) {
+	algorithms[i].init_context(&context);
+	algorithms[i].update(&context, input, (unsigned)input_len);
+	algorithms[i].final(&context, digest);
+    }
+
+    /* Run */
+    for (i=0; i<PJ_ARRAY_SIZE(algorithms); ++i) {
+	int j;
+	pj_timestamp t1, t2;
+
+	pj_get_timestamp(&t1);
+	algorithms[i].init_context(&context);
+	for (j=0; j<LOOP; ++j) {
+	    algorithms[i].update(&context, input, (unsigned)input_len);
+	}
+	algorithms[i].final(&context, digest);
+	pj_get_timestamp(&t2);
+
+	algorithms[i].t = pj_elapsed_usec(&t1, &t2);
+    }
+
+    /* Results */
+    for (i=0; i<PJ_ARRAY_SIZE(algorithms); ++i) {
+	double bytes;
+
+	bytes = (total_len * 1000000 / algorithms[i].t);
+	PJ_LOG(3, (THIS_FILE, "    %s:%8d usec (%3d.%03d Mbytes/sec)",
+		   algorithms[i].name, algorithms[i].t,
+		   (unsigned)(bytes / 1024 / 1024),
+		   ((unsigned)(bytes) % (1024 * 1024)) / 1024));
+    }
+
+    return 0;
+}
+
+
+
+#endif /* INCLUDE_ENCRYPTION_TEST */
+
diff --git a/jni/pjproject-android/.svn/pristine/05/054c48feeef77efa640cf763e8e8af1606922d8c.svn-base b/jni/pjproject-android/.svn/pristine/05/054c48feeef77efa640cf763e8e8af1606922d8c.svn-base
new file mode 100644
index 0000000..2d878cc
--- /dev/null
+++ b/jni/pjproject-android/.svn/pristine/05/054c48feeef77efa640cf763e8e8af1606922d8c.svn-base
@@ -0,0 +1,1125 @@
+/***************************************************************************
+**
+**   ITU-T G.722.1 (2005-05) - Fixed point implementation for main body and Annex C
+**   > Software Release 2.1 (2008-06)
+**     (Simple repackaging; no change from 2005-05 Release 2.0 code)
+**
+**   © 2004 Polycom, Inc.
+**
+**   All rights reserved.
+**
+***************************************************************************/
+
+/***************************************************************************
+  Filename:    encoder.c    
+
+  Purpose:     Contains files used to implement the G.722.1 Annex C encoder
+
+  Design Notes:
+
+***************************************************************************/
+
+/***************************************************************************
+ Include files                                                           
+***************************************************************************/
+
+#include <stdio.h>
+#include <math.h>
+#include "defs.h"
+#include "huff_def.h"
+#include "tables.h"
+#include "count.h"
+
+/***************************************************************************
+ Function:    encoder                                                     
+                                                                         
+ Syntax:      void encoder(Word16 number_of_available_bits,                     
+                           Word16 number_of_regions,
+                           Word16 mlt_coefs,                                        
+                           Word16 mag_shift,                                        
+                           Word16 out_words)                                        
+              
+              inputs:   number_of_available_bits
+                        number_of_regions
+                        mag_shift
+                        mlt_coefs[DCT_LENGTH]
+                        
+              outputs:  out_words[MAX_BITS_PER_FRAME/16]
+                                                                         
+                                                                         
+ Description: Encodes the mlt coefs into out_words using G.722.1 Annex C
+                                                           
+                                                                         
+ WMOPS:     7kHz |    24kbit    |     32kbit
+          -------|--------------|----------------
+            AVG  |    0.93      |     1.04
+          -------|--------------|----------------  
+            MAX  |    1.20      |     1.28
+          -------|--------------|---------------- 
+                                                                        
+           14kHz |    24kbit    |     32kbit     |     48kbit
+          -------|--------------|----------------|----------------
+            AVG  |    1.39      |     1.71       |     2.01
+          -------|--------------|----------------|----------------
+            MAX  |    2.00      |     2.30       |     2.52
+          -------|--------------|----------------|----------------
+                                                                         
+***************************************************************************/
+
+void encoder(Word16  number_of_available_bits,
+             Word16  number_of_regions,
+             Word16  *mlt_coefs,
+             Word16  mag_shift,
+             Word16  *out_words)
+{
+
+    Word16  num_categorization_control_bits;
+    Word16  num_categorization_control_possibilities;
+    Word16  number_of_bits_per_frame;
+    Word16  number_of_envelope_bits;
+    Word16  categorization_control;
+    Word16  region;
+    Word16  absolute_region_power_index[MAX_NUMBER_OF_REGIONS];
+    Word16  power_categories[MAX_NUMBER_OF_REGIONS];
+    Word16  category_balances[MAX_NUM_CATEGORIZATION_CONTROL_POSSIBILITIES-1];
+    Word16  drp_num_bits[MAX_NUMBER_OF_REGIONS+1];
+    UWord16 drp_code_bits[MAX_NUMBER_OF_REGIONS+1];
+    Word16  region_mlt_bit_counts[MAX_NUMBER_OF_REGIONS];
+    UWord32 region_mlt_bits[4*MAX_NUMBER_OF_REGIONS];
+    Word16  mag_shift_offset;
+
+    Word16 temp;
+
+    /* initialize variables */
+    test();
+    if (number_of_regions == NUMBER_OF_REGIONS)
+    {
+        num_categorization_control_bits = NUM_CATEGORIZATION_CONTROL_BITS;
+        move16();
+        num_categorization_control_possibilities = NUM_CATEGORIZATION_CONTROL_POSSIBILITIES;
+        move16();
+    } 
+    else
+    {
+        num_categorization_control_bits = MAX_NUM_CATEGORIZATION_CONTROL_BITS;
+        move16();
+        num_categorization_control_possibilities = MAX_NUM_CATEGORIZATION_CONTROL_POSSIBILITIES;
+        move16();
+    }
+
+    number_of_bits_per_frame = number_of_available_bits;
+    move16();
+
+    for (region=0; region<number_of_regions; region++)
+    {
+        region_mlt_bit_counts[region] = 0;
+        move16();
+    }
+    
+    /* Estimate power envelope. */
+    number_of_envelope_bits = compute_region_powers(mlt_coefs,
+                                                    mag_shift,
+                                                    drp_num_bits,
+                                                    drp_code_bits,
+                                                    absolute_region_power_index,
+                                                    number_of_regions);
+
+    /* Adjust number of available bits based on power envelope estimate */
+    temp = sub(number_of_available_bits,number_of_envelope_bits);
+    number_of_available_bits = sub(temp,num_categorization_control_bits);
+
+    /* get categorizations */
+    categorize(number_of_available_bits,
+               number_of_regions,
+               num_categorization_control_possibilities,
+               absolute_region_power_index,
+               power_categories,
+               category_balances);
+
+    /* Adjust absolute_region_category_index[] for mag_shift.
+       This assumes that REGION_POWER_STEPSIZE_DB is defined
+       to be exactly 3.010299957 or 20.0 times log base 10
+       of square root of 2. */
+    temp = shl_nocheck(mag_shift,1);
+    mag_shift_offset = add(temp,REGION_POWER_TABLE_NUM_NEGATIVES);
+    
+    for (region=0; region<number_of_regions; region++)
+    {
+        absolute_region_power_index[region] = add(absolute_region_power_index[region],mag_shift_offset);
+        move16();
+    }
+
+    /* adjust the absolute power region index based on the mlt coefs */
+    adjust_abs_region_power_index(absolute_region_power_index,mlt_coefs,number_of_regions);
+
+
+    /* quantize and code the mlt coefficients based on categorizations */
+    vector_quantize_mlts(number_of_available_bits,
+                         number_of_regions,
+                         num_categorization_control_possibilities,
+                         mlt_coefs,
+                         absolute_region_power_index,
+                         power_categories,
+                         category_balances,
+                         &categorization_control,
+                         region_mlt_bit_counts,
+                         region_mlt_bits);
+
+    /* stuff bits into words */
+    bits_to_words(region_mlt_bits,
+                  region_mlt_bit_counts,
+                  drp_num_bits,
+                  drp_code_bits,
+                  out_words,
+                  categorization_control,
+                  number_of_regions,
+                  num_categorization_control_bits,
+                  number_of_bits_per_frame);
+
+}
+
+/***************************************************************************
+ Function:    bits_to_words
+
+ Syntax:      bits_to_words(UWord32 *region_mlt_bits,              
+                            Word16  *region_mlt_bit_counts,                                                             
+                            Word16  *drp_num_bits,                                                                      
+                            UWord16 *drp_code_bits,                                                                     
+                            Word16  *out_words,                                                                         
+                            Word16  categorization_control,                                                                         
+                            Word16  number_of_regions,
+                            Word16  num_categorization_control_bits,
+                            Word16  number_of_bits_per_frame)                                                           
+                                                                   
+                                                                   
+ Description: Stuffs the bits into words for output
+
+ WMOPS:     7kHz |    24kbit    |      32kbit
+          -------|--------------|----------------
+            AVG  |    0.09      |     0.12
+          -------|--------------|----------------  
+            MAX  |    0.10      |     0.13
+          -------|--------------|---------------- 
+          
+           14kHz |    24kbit    |     32kbit     |     48kbit
+          -------|--------------|----------------|----------------
+            AVG  |    0.12      |     0.15       |     0.19
+          -------|--------------|----------------|----------------
+            MAX  |    0.14      |     0.17       |     0.21
+          -------|--------------|----------------|----------------
+
+***************************************************************************/
+void bits_to_words(UWord32 *region_mlt_bits,
+                   Word16  *region_mlt_bit_counts,
+                   Word16  *drp_num_bits,
+                   UWord16 *drp_code_bits,
+                   Word16  *out_words,
+                   Word16  categorization_control,
+                   Word16  number_of_regions,
+                   Word16  num_categorization_control_bits,
+                   Word16  number_of_bits_per_frame)
+{
+    Word16  out_word_index = 0;
+    Word16  j;
+    Word16  region;
+    Word16  out_word;
+    Word16  region_bit_count;
+    Word16  current_word_bits_left;
+    UWord16 slice;
+    Word16  out_word_bits_free = 16;
+    UWord32 *in_word_ptr;
+    UWord32 current_word;
+    
+    Word32  acca = 0;
+    Word32  accb;
+    Word16  temp;
+
+    /* First set up the categorization control bits to look like one more set of region power bits. */
+    out_word = 0;
+    move16();
+
+    drp_num_bits[number_of_regions] = num_categorization_control_bits;
+    move16();
+    
+    drp_code_bits[number_of_regions] = (UWord16)categorization_control;
+    move16();
+
+    /* These code bits are right justified. */
+    for (region=0; region <= number_of_regions; region++)
+    {
+        current_word_bits_left = drp_num_bits[region];
+        move16();
+        
+        current_word = (UWord32)drp_code_bits[region];
+        move16();
+        
+        j = sub(current_word_bits_left,out_word_bits_free);
+
+        test();
+        if (j >= 0)
+        {
+            temp = extract_l(L_shr_nocheck(current_word,j));
+            out_word = add(out_word,temp);
+
+            out_words[out_word_index++] = out_word;
+            move16();
+            
+            out_word_bits_free = 16;
+            move16();
+            
+            out_word_bits_free = sub(out_word_bits_free,j);
+            
+            acca = (current_word << out_word_bits_free);
+            out_word = extract_l(acca);
+        }
+        else
+        {
+            j = negate(j);
+
+            acca = (current_word << j);
+            accb = L_deposit_l(out_word);
+            acca = L_add(accb,acca);
+            out_word = extract_l(acca);
+            
+            out_word_bits_free = sub(out_word_bits_free,current_word_bits_left);
+        }
+    }
+
+    /* These code bits are left justified. */
+    
+    for (region=0;region<number_of_regions; region++)
+    {
+        accb = L_deposit_l(out_word_index);
+        accb = L_shl_nocheck(accb,4);
+        accb = L_sub(accb,number_of_bits_per_frame);
+        test();
+        if(accb < 0)        
+        {
+            temp = shl_nocheck(region,2);
+            in_word_ptr = &region_mlt_bits[temp];
+            region_bit_count = region_mlt_bit_counts[region];
+            move16();
+
+            temp = sub(32,region_bit_count);
+            test();
+            if(temp > 0)
+                current_word_bits_left = region_bit_count;
+            else
+                current_word_bits_left = 32;
+
+            current_word = *in_word_ptr++;
+    
+            acca = L_deposit_l(out_word_index);
+            acca = L_shl_nocheck(acca,4);
+            acca = L_sub(acca,number_of_bits_per_frame);
+            
+            /* from while loop */
+            test();
+            test();
+            logic16(); 
+            while ((region_bit_count > 0) && (acca < 0))
+            {
+                /* from while loop */
+                test();
+                test();
+                logic16(); 
+                
+                temp = sub(current_word_bits_left,out_word_bits_free);
+                test();
+                if (temp >= 0)
+                {
+                    temp = sub(32,out_word_bits_free);
+                    accb = LU_shr(current_word,temp);
+                    slice = (UWord16)extract_l(accb);
+                    
+                    out_word = add(out_word,slice);
+    
+                    test();
+                    current_word <<= out_word_bits_free;
+
+                    current_word_bits_left = sub(current_word_bits_left,out_word_bits_free);
+                    out_words[out_word_index++] = extract_l(out_word);
+                    move16();
+
+                    out_word = 0;
+                    move16();
+
+                    out_word_bits_free = 16;
+                    move16();
+                }
+                else
+                {
+                    temp = sub(32,current_word_bits_left);
+                    accb = LU_shr(current_word,temp);
+                    slice = (UWord16)extract_l(accb);
+                    
+                    temp = sub(out_word_bits_free,current_word_bits_left);
+                    test();
+                    accb = slice << temp;
+                    acca = L_deposit_l(out_word);
+                    acca = L_add(acca,accb);
+                    out_word = extract_l(acca);
+                    out_word_bits_free = sub(out_word_bits_free,current_word_bits_left);
+                    
+                    current_word_bits_left = 0;
+                    move16();
+                }
+    
+                test();
+                if (current_word_bits_left == 0)
+                {
+                    current_word = *in_word_ptr++;
+                    region_bit_count = sub(region_bit_count,32);
+                    
+                    /* current_word_bits_left = MIN(32,region_bit_count); */
+                    temp = sub(32,region_bit_count);
+                    test();
+                    if(temp > 0)
+                        current_word_bits_left = region_bit_count;
+                    else
+                        current_word_bits_left = 32;
+                    
+                }
+                acca = L_deposit_l(out_word_index);
+                acca = L_shl_nocheck(acca,4);
+                acca = L_sub(acca,number_of_bits_per_frame);
+            }
+            accb = L_deposit_l(out_word_index);
+            accb = L_shl_nocheck(accb,4);
+            accb = L_sub(accb,number_of_bits_per_frame);
+        }
+    }
+
+    /* Fill out with 1's. */
+
+    test();
+    while (acca < 0)
+    {
+        test();
+        current_word = 0x0000ffff;
+        move32();
+
+        temp = sub(16,out_word_bits_free);
+        acca = LU_shr(current_word,temp);
+        slice = (UWord16)extract_l(acca);
+
+        out_word = add(out_word,slice);
+        out_words[out_word_index++] = out_word;
+        move16();
+
+        out_word = 0;
+        move16();
+        
+        out_word_bits_free = 16;
+        move16();
+        
+        acca = L_deposit_l(out_word_index);
+        acca = L_shl_nocheck(acca,4);
+        acca = L_sub(acca,number_of_bits_per_frame);
+    }
+}
+/***************************************************************************
+ Function:    adjust_abs_region_power_index
+
+ Syntax:      adjust_abs_region_power_index(Word16 *absolute_region_power_index,
+                                            Word16 *mlt_coefs,
+                                            Word16 number_of_regions)
+
+              inputs:   *mlt_coefs
+                        *absolute_region_power_index
+                        number_of_regions
+            
+              outputs:  *absolute_region_power_index
+ 
+ Description: Adjusts the absolute power index
+ 
+ 
+ WMOPS:     7kHz |    24kbit    |      32kbit
+          -------|--------------|----------------
+            AVG  |    0.03      |      0.03
+          -------|--------------|----------------  
+            MAX  |    0.12      |      0.12
+          -------|--------------|---------------- 
+
+           14kHz |    24kbit    |     32kbit     |     48kbit
+          -------|--------------|----------------|----------------
+            AVG  |    0.03      |     0.03       |     0.03
+          -------|--------------|----------------|----------------
+            MAX  |    0.14      |     0.14       |     0.14
+          -------|--------------|----------------|----------------
+
+***************************************************************************/
+void adjust_abs_region_power_index(Word16 *absolute_region_power_index,Word16 *mlt_coefs,Word16 number_of_regions)
+{
+    Word16 n,i;
+    Word16 region;
+    Word16 *raw_mlt_ptr;
+    
+    Word32 acca;
+    Word16 temp;
+
+    for (region=0; region<number_of_regions; region++)
+    {
+        n = sub(absolute_region_power_index[region],39);
+        n = shr_nocheck(n,1);
+        
+        test();
+        if (n > 0)
+        {
+            temp = extract_l(L_mult0(region,REGION_SIZE));
+
+            raw_mlt_ptr = &mlt_coefs[temp];
+
+            for (i=0; i<REGION_SIZE; i++)
+            {
+                acca = L_shl_nocheck(*raw_mlt_ptr,16);
+                acca = L_add(acca,32768L);
+                acca = L_shr_nocheck(acca,n);
+                acca = L_shr_nocheck(acca,16);
+                *raw_mlt_ptr++ = extract_l(acca);
+            }
+
+            temp = shl_nocheck(n,1);
+            temp = sub(absolute_region_power_index[region],temp);
+            absolute_region_power_index[region] = temp;
+            move16();
+        }
+    }
+}
+
+/***************************************************************************
+ Function:    compute_region_powers
+
+ Syntax:      Word16 compute_region_powers(Word16  *mlt_coefs,                         
+                                           Word16  mag_shift,                         
+                                           Word16  *drp_num_bits,                      
+                                           UWord16 *drp_code_bits,                     
+                                           Word16  *absolute_region_power_index,       
+                                           Word16  number_of_regions)
+                mlt_coefs[DCT_LENGTH];                      
+                mag_shift;                                          
+                drp_num_bits[MAX_NUMBER_OF_REGIONS];                      
+                drp_code_bits[MAX_NUMBER_OF_REGIONS];                     
+                absolute_region_power_index[MAX_NUMBER_OF_REGIONS];       
+                number_of_regions;
+                                                                   
+ Description: Computes the power for each of the regions
+
+ 
+ WMOPS:     7kHz |    24kbit    |    32kbit
+          -------|--------------|----------------
+            AVG  |    0.09      |    0.09
+          -------|--------------|----------------  
+            MAX  |    0.13      |    0.13
+          -------|--------------|---------------- 
+
+           14kHz |    24kbit    |     32kbit     |     48kbit
+          -------|--------------|----------------|----------------
+            AVG  |    0.20      |     0.20       |     0.20
+          -------|--------------|----------------|----------------
+            MAX  |    0.29      |     0.29       |     0.29
+          -------|--------------|----------------|----------------
+
+***************************************************************************/
+
+Word16 compute_region_powers(Word16  *mlt_coefs,
+                             Word16  mag_shift,
+                             Word16  *drp_num_bits,
+                             UWord16 *drp_code_bits,
+                             Word16  *absolute_region_power_index,
+                             Word16  number_of_regions)
+{
+
+    Word16 *input_ptr;
+    Word32 long_accumulator;
+    Word16 itemp1;
+    Word16 power_shift;
+    Word16 region;
+    Word16 j;
+    Word16 differential_region_power_index[MAX_NUMBER_OF_REGIONS];
+    Word16 number_of_bits;
+    
+    Word32 acca;
+    Word16 temp;
+    Word16 temp1;
+    Word16 temp2;
+
+
+    input_ptr = mlt_coefs;
+    for (region=0; region<number_of_regions; region++)
+    {
+        long_accumulator = L_deposit_l(0);
+
+        for (j=0; j<REGION_SIZE; j++)
+        {
+            itemp1 = *input_ptr++;
+            move16();
+            long_accumulator = L_mac0(long_accumulator,itemp1,itemp1);
+        }
+
+        power_shift = 0;
+        move16();
+
+        acca = (long_accumulator & 0x7fff0000L);
+        logic32();
+
+        test();
+        while (acca > 0)
+        {
+            test();
+            long_accumulator = L_shr_nocheck(long_accumulator,1);
+            
+            acca = (long_accumulator & 0x7fff0000L);
+            logic32();
+
+            power_shift = add(power_shift,1);
+        }
+        
+        acca = L_sub(long_accumulator,32767);
+        
+        temp = add(power_shift,15);
+        test();
+        test();
+        logic16();
+        while ((acca <= 0) && (temp >= 0))
+        {
+            test();
+            test();
+            logic16();
+            
+            long_accumulator = L_shl_nocheck(long_accumulator,1);
+            acca = L_sub(long_accumulator,32767);
+            power_shift--;
+            temp = add(power_shift,15);
+        }
+        long_accumulator = L_shr_nocheck(long_accumulator,1);
+        /* 28963 corresponds to square root of 2 times REGION_SIZE(20). */
+        acca = L_sub(long_accumulator,28963);
+        
+        test();
+        if (acca >= 0)
+            power_shift = add(power_shift,1);
+        
+        acca = L_deposit_l(mag_shift);
+        acca = L_shl_nocheck(acca,1);
+        acca = L_sub(power_shift,acca);
+        acca = L_add(35,acca);
+        acca = L_sub(acca,REGION_POWER_TABLE_NUM_NEGATIVES);
+        absolute_region_power_index[region] = extract_l(acca);
+    }
+
+
+    /* Before we differentially encode the quantized region powers, adjust upward the
+    valleys to make sure all the peaks can be accurately represented. */
+    temp = sub(number_of_regions,2);
+
+    for (region = temp; region >= 0; region--)
+    {
+        temp1 = sub(absolute_region_power_index[region+1],DRP_DIFF_MAX);
+        temp2 = sub(absolute_region_power_index[region],temp1);
+        test();
+        if (temp2 < 0)
+        {
+            absolute_region_power_index[region] = temp1;
+            move16();
+        }
+    }
+
+    /* The MLT is currently scaled too low by the factor
+       ENCODER_SCALE_FACTOR(=18318)/32768 * (1./sqrt(160).
+       This is the ninth power of 1 over the square root of 2.
+       So later we will add ESF_ADJUSTMENT_TO_RMS_INDEX (now 9)
+       to drp_code_bits[0]. */
+
+    /* drp_code_bits[0] can range from 1 to 31. 0 will be used only as an escape sequence. */
+    temp1 = sub(1,ESF_ADJUSTMENT_TO_RMS_INDEX);
+    temp2 = sub(absolute_region_power_index[0],temp1);
+    test();
+    if (temp2 < 0)
+    {
+        absolute_region_power_index[0] = temp1;
+        move16();
+    }
+    
+    temp1 = sub(31,ESF_ADJUSTMENT_TO_RMS_INDEX);
+
+	/*
+	 * The next line was corrected in Release 1.2 
+	 */
+
+    temp2 = sub(absolute_region_power_index[0], temp1);  
+    test();
+    if (temp2 > 0)
+    {
+        absolute_region_power_index[0] = temp1;
+        move16();
+    }
+
+    differential_region_power_index[0] = absolute_region_power_index[0];
+    move16();
+    
+    number_of_bits = 5;
+    move16();
+    
+    drp_num_bits[0] = 5;
+    move16();
+    
+    drp_code_bits[0] = (UWord16)add(absolute_region_power_index[0],ESF_ADJUSTMENT_TO_RMS_INDEX);
+    move16();
+
+    /* Lower limit the absolute region power indices to -8 and upper limit them to 31. Such extremes
+     may be mathematically impossible anyway.*/
+    for (region=1; region<number_of_regions; region++)
+    {
+        temp1 = sub(-8,ESF_ADJUSTMENT_TO_RMS_INDEX);
+        temp2 = sub(absolute_region_power_index[region],temp1);
+        test();
+        if (temp2 < 0)
+        {
+            absolute_region_power_index[region] = temp1;
+            move16();
+        }
+
+        temp1 = sub(31,ESF_ADJUSTMENT_TO_RMS_INDEX);
+        temp2 = sub(absolute_region_power_index[region],temp1);
+        test();
+        if (temp2 > 0)
+        {
+            absolute_region_power_index[region] = temp1;
+            move16();
+        }
+    }
+
+    for (region=1; region<number_of_regions; region++)
+    {
+        j = sub(absolute_region_power_index[region],absolute_region_power_index[region-1]);
+        temp = sub(j,DRP_DIFF_MIN);
+        test();
+        if (temp < 0)
+        {
+            j = DRP_DIFF_MIN;
+        }
+        j = sub(j,DRP_DIFF_MIN);
+        move16();
+        differential_region_power_index[region] = j;
+        move16();
+        
+        temp = add(absolute_region_power_index[region-1],differential_region_power_index[region]);
+        temp = add(temp,DRP_DIFF_MIN);
+        absolute_region_power_index[region] = temp;
+        move16();
+
+        number_of_bits = add(number_of_bits,differential_region_power_bits[region][j]);
+        drp_num_bits[region] = differential_region_power_bits[region][j];
+        move16();
+        drp_code_bits[region] = differential_region_power_codes[region][j];
+        move16();
+    }
+
+    return (number_of_bits);
+}
+
+/***************************************************************************
+ Function:    vector_quantize_mlts
+
+ Syntax:      void vector_quantize_mlts(number_of_available_bits,
+                                        number_of_regions,                     
+                                        num_categorization_control_possibilities,
+                                        mlt_coefs,                                    
+                                        absolute_region_power_index,                  
+                                        power_categories,                             
+                                        category_balances,                            
+                                        p_categorization_control,                               
+                                        region_mlt_bit_counts,                        
+                                        region_mlt_bits)                              
+                                                                                    
+              Word16 number_of_available_bits;                                        
+              Word16 number_of_regions;
+              Word16 num_categorization_control_possibilities;
+              Word16 mlt_coefs[DCT_LENGTH];                                         
+              Word16 absolute_region_power_index[MAX_NUMBER_OF_REGIONS];                  
+              Word16 power_categories[MAX_NUMBER_OF_REGIONS];                             
+              Word16 category_balances[MAX_NUM_CATEGORIZATION_CONTROL_POSSIBILITIES-1];         
+              Word16 *p_categorization_control;                                                 
+              Word16 region_mlt_bit_counts[MAX_NUMBER_OF_REGIONS];                        
+              Word32 region_mlt_bits[4*MAX_NUMBER_OF_REGIONS];                            
+
+ Description: Scalar quantized vector Huffman coding (SQVH)
+
+ 
+ WMOPS:     7kHz |    24kbit    |     32kbit
+          -------|--------------|----------------
+            AVG  |    0.57      |     0.65
+          -------|--------------|----------------  
+            MAX  |    0.78      |     0.83
+          -------|--------------|---------------- 
+
+           14kHz |    24kbit    |     32kbit     |     48kbit
+          -------|--------------|----------------|----------------
+            AVG  |    0.62      |     0.90       |     1.11
+          -------|--------------|----------------|----------------
+            MAX  |    1.16      |     1.39       |     1.54
+          -------|--------------|----------------|----------------
+
+***************************************************************************/
+
+void vector_quantize_mlts(Word16 number_of_available_bits,
+                          Word16 number_of_regions,
+                          Word16 num_categorization_control_possibilities,
+                          Word16 *mlt_coefs,
+                          Word16 *absolute_region_power_index,
+                          Word16 *power_categories,
+                          Word16 *category_balances,
+                          Word16 *p_categorization_control,
+                          Word16 *region_mlt_bit_counts,
+                          UWord32 *region_mlt_bits)
+{
+
+    Word16 *raw_mlt_ptr;
+    Word16 region;
+    Word16 category;
+    Word16 total_mlt_bits = 0;
+
+    Word16 temp;
+    Word16 temp1;
+    Word16 temp2;
+
+    /* Start in the middle of the categorization control range. */
+    temp = shr_nocheck(num_categorization_control_possibilities,1);
+    temp = sub(temp,1);
+    for (*p_categorization_control = 0; *p_categorization_control < temp; (*p_categorization_control)++)
+    {
+        region = category_balances[*p_categorization_control];
+        move16();
+        power_categories[region] = add(power_categories[region],1);
+        move16();
+    }
+
+    for (region=0; region<number_of_regions; region++)
+    {
+        category = power_categories[region];
+        move16();
+        temp = extract_l(L_mult0(region,REGION_SIZE));
+        raw_mlt_ptr = &mlt_coefs[temp];
+        move16();
+        temp = sub(category,(NUM_CATEGORIES-1));
+        test();
+        if (temp < 0)
+        {
+            region_mlt_bit_counts[region] =
+            vector_huffman(category, absolute_region_power_index[region],raw_mlt_ptr,
+                           &region_mlt_bits[shl_nocheck(region,2)]);
+        }
+        else
+        {
+            region_mlt_bit_counts[region] = 0;
+            move16();
+        }
+        total_mlt_bits = add(total_mlt_bits,region_mlt_bit_counts[region]);
+    }
+
+
+    /* If too few bits... */
+    temp = sub(total_mlt_bits,number_of_available_bits);
+    test();
+    test();
+    logic16();
+    while ((temp < 0) && (*p_categorization_control > 0))
+    {
+        test();
+        test();
+        logic16();
+        (*p_categorization_control)--;
+        region = category_balances[*p_categorization_control];
+        move16();
+        
+        power_categories[region] = sub(power_categories[region],1);
+        move16();
+
+        total_mlt_bits = sub(total_mlt_bits,region_mlt_bit_counts[region]);
+        category = power_categories[region];
+        move16();
+        
+        raw_mlt_ptr = &mlt_coefs[region*REGION_SIZE];
+        move16();
+        
+        temp = sub(category,(NUM_CATEGORIES-1));
+        test();
+        if (temp < 0)
+        {
+            region_mlt_bit_counts[region] =
+                vector_huffman(category, absolute_region_power_index[region],raw_mlt_ptr,
+                           &region_mlt_bits[shl_nocheck(region,2)]);
+        }
+        else
+        {
+            region_mlt_bit_counts[region] = 0;
+            move16();
+        }
+        total_mlt_bits = add(total_mlt_bits,region_mlt_bit_counts[region]);
+        temp = sub(total_mlt_bits,number_of_available_bits);
+    }
+
+    /* If too many bits... */
+    /* Set up for while loop test */
+    temp1 = sub(total_mlt_bits,number_of_available_bits);
+    temp2 = sub(*p_categorization_control,sub(num_categorization_control_possibilities,1));
+    test();
+    test();
+    logic16();
+    
+    while ((temp1 > 0) && (temp2 < 0))
+    {
+        /* operations for while contitions */
+        test();
+        test();
+        logic16();
+        
+        region = category_balances[*p_categorization_control];
+        move16();
+        
+        power_categories[region] = add(power_categories[region],1);
+        move16();
+
+        total_mlt_bits = sub(total_mlt_bits,region_mlt_bit_counts[region]);
+        category = power_categories[region];
+        move16();
+        
+        temp = extract_l(L_mult0(region,REGION_SIZE));
+        raw_mlt_ptr = &mlt_coefs[temp];
+        move16();
+        
+        temp = sub(category,(NUM_CATEGORIES-1));
+        test();
+        if (temp < 0)
+        {
+            region_mlt_bit_counts[region] =
+                vector_huffman(category, absolute_region_power_index[region],raw_mlt_ptr,
+                           &region_mlt_bits[shl_nocheck(region,2)]);
+        }
+        else
+        {
+            region_mlt_bit_counts[region] = 0;
+            move16();
+        }
+        total_mlt_bits = add(total_mlt_bits,region_mlt_bit_counts[region]);
+        (*p_categorization_control)++;
+        
+        temp1 = sub(total_mlt_bits,number_of_available_bits);
+        temp2 = sub(*p_categorization_control,sub(num_categorization_control_possibilities,1));
+    }
+}
+
+/***************************************************************************
+ Function:    vector_huffman
+
+ Syntax:      Word16 vector_huffman(Word16  category,     
+                                    Word16  power_index,  
+                                    Word16  *raw_mlt_ptr,  
+                                    UWord32 *word_ptr)     
+                                              
+              inputs:     Word16  category
+                          Word16  power_index
+                          Word16  *raw_mlt_ptr
+             
+              outputs:    number_of_region_bits
+                          *word_ptr
+                                      
+
+ Description: Huffman encoding for each region based on category and power_index  
+
+ WMOPS:     7kHz |    24kbit    |     32kbit
+          -------|--------------|----------------
+            AVG  |    0.03      |     0.03
+          -------|--------------|----------------  
+            MAX  |    0.04      |     0.04
+          -------|--------------|---------------- 
+
+           14kHz |    24kbit    |     32kbit     |     48kbit
+          -------|--------------|----------------|----------------
+            AVG  |    0.03      |     0.03       |     0.03
+          -------|--------------|----------------|----------------
+            MAX  |    0.04      |     0.04       |     0.04
+          -------|--------------|----------------|----------------
+
+***************************************************************************/
+Word16 vector_huffman(Word16 category,
+                      Word16 power_index,
+                      Word16 *raw_mlt_ptr,
+                      UWord32 *word_ptr)
+{
+ 
+
+    Word16  inv_of_step_size_times_std_dev;
+    Word16  j,n;
+    Word16  k;
+    Word16  number_of_region_bits;
+    Word16  number_of_non_zero;
+    Word16  vec_dim;
+    Word16  num_vecs;
+    Word16  kmax, kmax_plus_one;
+    Word16  index,signs_index;
+    Word16  *bitcount_table_ptr;
+    UWord16 *code_table_ptr;
+    Word32  code_bits;
+    Word16  number_of_code_bits;
+    UWord32 current_word;
+    Word16  current_word_bits_free;
+    
+    Word32 acca;
+    Word32 accb;
+    Word16 temp;
+
+    Word16 mytemp;			 /* new variable in Release 1.2 */
+    Word16 myacca;			 /* new variable in Release 1.2 */
+
+
+    /* initialize variables */
+    vec_dim = vector_dimension[category];
+    move16();
+
+    num_vecs = number_of_vectors[category];
+    move16();
+
+    kmax = max_bin[category];
+    move16();
+
+    kmax_plus_one = add(kmax,1);
+    move16();
+
+    current_word = 0L;
+    move16();
+
+    current_word_bits_free = 32;
+    move16();
+
+    number_of_region_bits = 0;
+    move16();
+
+    /* set up table pointers */
+    bitcount_table_ptr = (Word16 *)table_of_bitcount_tables[category];
+    code_table_ptr = (UWord16 *) table_of_code_tables[category];
+
+    /* compute inverse of step size * standard deviation */
+    acca = L_mult(step_size_inverse_table[category],standard_deviation_inverse_table[power_index]);
+    acca = L_shr_nocheck(acca,1);
+    acca = L_add(acca,4096);
+    acca = L_shr_nocheck(acca,13);
+
+	/*
+	 *  The next two lines are new to Release 1.2 
+	 */
+     
+	mytemp = (Word16)(acca & 0x3);
+    acca = L_shr_nocheck(acca,2);
+
+    inv_of_step_size_times_std_dev = extract_l(acca);
+
+
+    for (n=0; n<num_vecs; n++)
+    {
+        index = 0;
+        move16();
+        
+        signs_index = 0;
+        move16();
+        
+        number_of_non_zero = 0;
+        move16();
+        
+        for (j=0; j<vec_dim; j++)
+        {
+            k = abs_s(*raw_mlt_ptr);
+            
+            acca = L_mult(k,inv_of_step_size_times_std_dev);
+            acca = L_shr_nocheck(acca,1);
+		    
+			/*
+			 *  The next four lines are new to Release 1.2
+			 */
+
+			myacca = (Word16)L_mult(k,mytemp);
+			myacca = (Word16)L_shr_nocheck(myacca,1);
+			myacca = (Word16)L_add(myacca,int_dead_zone_low_bits[category]);
+			myacca = (Word16)L_shr_nocheck(myacca,2);
+
+            acca = L_add(acca,int_dead_zone[category]);
+
+			/*
+			 *  The next two lines are new to Release 1.2
+			 */
+
+			acca = L_add(acca,myacca);
+			acca = L_shr_nocheck(acca,13);
+
+            k = extract_l(acca);
+
+            test();
+            if (k != 0)
+            {
+                number_of_non_zero = add(number_of_non_zero,1);
+                signs_index = shl_nocheck(signs_index,1);
+                
+                test();
+                if (*raw_mlt_ptr > 0)
+                {
+                    signs_index = add(signs_index,1);
+                }
+                
+                temp = sub(k,kmax);
+                test();
+                if (temp > 0)
+                {
+                    k = kmax;
+                    move16();
+                }
+            }
+            acca = L_shr_nocheck(L_mult(index,(kmax_plus_one)),1);
+            index = extract_l(acca);
+            index = add(index,k);
+            raw_mlt_ptr++;
+        }
+
+        code_bits = *(code_table_ptr+index);
+        number_of_code_bits = add((*(bitcount_table_ptr+index)),number_of_non_zero);
+        number_of_region_bits = add(number_of_region_bits,number_of_code_bits);
+
+        acca = code_bits << number_of_non_zero;
+        accb = L_deposit_l(signs_index);
+        acca = L_add(acca,accb);
+        code_bits = acca;
+        move32();
+
+        /* msb of codebits is transmitted first. */
+        j = sub(current_word_bits_free,number_of_code_bits);
+        test();
+        if (j >= 0)
+        {
+            test();
+            acca = code_bits << j;
+            current_word = L_add(current_word,acca);
+            current_word_bits_free = j;
+            move16();
+        }
+        else
+        {
+            j = negate(j);
+            acca = L_shr_nocheck(code_bits,j);
+            current_word = L_add(current_word,acca);
+            
+            *word_ptr++ = current_word;
+            move16();
+
+            current_word_bits_free = sub(32,j);
+            test();
+            current_word = code_bits << current_word_bits_free;
+        }
+    }
+
+    *word_ptr++ = current_word;
+    move16();
+
+    return (number_of_region_bits);
+}
+
+
diff --git a/jni/pjproject-android/.svn/pristine/05/054e12b694c21a1058972c51dd19464722ddba22.svn-base b/jni/pjproject-android/.svn/pristine/05/054e12b694c21a1058972c51dd19464722ddba22.svn-base
new file mode 100644
index 0000000..542d266
--- /dev/null
+++ b/jni/pjproject-android/.svn/pristine/05/054e12b694c21a1058972c51dd19464722ddba22.svn-base
@@ -0,0 +1,250 @@
+/* $Id$ */
+/*
+ * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
+ * Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+#include "test.h"
+#include <pjmedia-audiodev/audiodev.h>
+#include <pjmedia-codec/ffmpeg_vid_codecs.h>
+#include <pjmedia/vid_codec.h>
+#include <pjmedia_videodev.h>
+
+
+#if defined(PJMEDIA_HAS_VIDEO) && (PJMEDIA_HAS_VIDEO != 0)
+
+
+#define THIS_FILE 	"vid_dev_test.c"
+#define LOOP_DURATION	6
+
+static pj_bool_t is_quitting = PJ_FALSE;
+
+static pj_status_t vid_event_cb(pjmedia_event *event,
+                                void *user_data)
+{
+    PJ_UNUSED_ARG(user_data);
+
+    if (event->type == PJMEDIA_EVENT_WND_CLOSED)
+        is_quitting = PJ_TRUE;
+
+    return PJ_SUCCESS;
+}
+
+static int capture_render_loopback(pj_bool_t active,
+				   int cap_dev_id, int rend_dev_id,
+                                   const pjmedia_format *fmt)
+{
+    pj_pool_t *pool;
+    pjmedia_vid_port *capture=NULL, *renderer=NULL;
+    pjmedia_vid_dev_info cdi, rdi;
+    pjmedia_vid_port_param param;
+    pjmedia_video_format_detail *vfd;
+    pj_status_t status;
+    int rc = 0, i;
+
+    pool = pj_pool_create(mem, "vidportloop", 1000, 1000, NULL);
+
+    status = pjmedia_vid_dev_get_info(cap_dev_id, &cdi);
+    if (status != PJ_SUCCESS)
+	goto on_return;
+
+    status = pjmedia_vid_dev_get_info(rend_dev_id, &rdi);
+    if (status != PJ_SUCCESS)
+	goto on_return;
+
+    PJ_LOG(3,(THIS_FILE,
+	      "  %s (%s) ===> %s (%s)\t%s\t%dx%d\t@%d:%d fps",
+	      cdi.name, cdi.driver, rdi.name, rdi.driver,
+	      pjmedia_get_video_format_info(NULL, fmt->id)->name,
+	      fmt->det.vid.size.w, fmt->det.vid.size.h,
+	      fmt->det.vid.fps.num, fmt->det.vid.fps.denum));
+
+    pjmedia_vid_port_param_default(&param);
+
+    /* Create capture, set it to active (master) */
+    status = pjmedia_vid_dev_default_param(pool, cap_dev_id,
+					   &param.vidparam);
+    if (status != PJ_SUCCESS) {
+	rc = 100; goto on_return;
+    }
+    param.vidparam.dir = PJMEDIA_DIR_CAPTURE;
+    param.vidparam.fmt = *fmt;
+    param.active = (active? PJ_TRUE: PJ_FALSE);
+
+    if (param.vidparam.fmt.detail_type != PJMEDIA_FORMAT_DETAIL_VIDEO) {
+	rc = 103; goto on_return;
+    }
+
+    vfd = pjmedia_format_get_video_format_detail(&param.vidparam.fmt, PJ_TRUE);
+    if (vfd == NULL) {
+	rc = 105; goto on_return;
+    }
+
+    status = pjmedia_vid_port_create(pool, &param, &capture);
+    if (status != PJ_SUCCESS) {
+	rc = 110; goto on_return;
+    }
+
+    /* Create renderer, set it to passive (slave)  */
+    status = pjmedia_vid_dev_default_param(pool, rend_dev_id,
+					   &param.vidparam);
+    if (status != PJ_SUCCESS) {
+	rc = 120; goto on_return;
+    }
+
+    param.active = (active? PJ_FALSE: PJ_TRUE);
+    param.vidparam.dir = PJMEDIA_DIR_RENDER;
+    param.vidparam.rend_id = rend_dev_id;
+    param.vidparam.fmt = *fmt;
+    param.vidparam.disp_size = vfd->size;
+
+    status = pjmedia_vid_port_create(pool, &param, &renderer);
+    if (status != PJ_SUCCESS) {
+	rc = 130; goto on_return;
+    }
+
+    /* Set event handler */
+    pjmedia_event_subscribe(NULL, &vid_event_cb, NULL, renderer);
+
+    /* Connect capture to renderer */
+    status = pjmedia_vid_port_connect(
+	         (active? capture: renderer),
+		 pjmedia_vid_port_get_passive_port(active? renderer: capture),
+		 PJ_FALSE);
+    if (status != PJ_SUCCESS) {
+	rc = 140; goto on_return;
+    }
+
+    /* Start streaming.. */
+    status = pjmedia_vid_port_start(renderer);
+    if (status != PJ_SUCCESS) {
+	rc = 150; goto on_return;
+    }
+    status = pjmedia_vid_port_start(capture);
+    if (status != PJ_SUCCESS) {
+	rc = 160; goto on_return;
+    }
+
+    /* Sleep while the webcam is being displayed... */
+    for (i = 0; i < LOOP_DURATION*10 && (!is_quitting); i++) {
+        pj_thread_sleep(100);
+    }
+
+on_return:
+    if (status != PJ_SUCCESS)
+	PJ_PERROR(3, (THIS_FILE, status, "   error"));
+
+    if (capture)
+        pjmedia_vid_port_stop(capture);
+    if (renderer)
+        pjmedia_vid_port_stop(renderer);
+    if (capture)
+	pjmedia_vid_port_destroy(capture);
+    if (renderer) {
+        pjmedia_event_unsubscribe(NULL, &vid_event_cb, NULL, renderer);
+	pjmedia_vid_port_destroy(renderer);
+    }
+
+    pj_pool_release(pool);
+    return rc;
+}
+
+static int find_device(pjmedia_dir dir,
+		       pj_bool_t has_callback)
+{
+    unsigned i, count = pjmedia_vid_dev_count();
+ 
+    for (i = 0; i < count; ++i) {
+	pjmedia_vid_dev_info cdi;
+
+	if (pjmedia_vid_dev_get_info(i, &cdi) != PJ_SUCCESS)
+	    continue;
+	if ((cdi.dir & dir) != 0 && cdi.has_callback == has_callback)
+	    return i;
+    }
+    
+    return -999;
+}
+
+static int vidport_test(void)
+{
+    int i, j, k, l;
+    int cap_id, rend_id;
+    pjmedia_format_id test_fmts[] = {
+        PJMEDIA_FORMAT_RGBA,
+        PJMEDIA_FORMAT_I420
+    };
+
+    PJ_LOG(3, (THIS_FILE, " Video port tests:"));
+
+    /* Capturer's role: active/passive. */
+    for (i = 1; i >= 0; i--) {
+	/* Capturer's device has_callback: TRUE/FALSE. */
+	for (j = 1; j >= 0; j--) {
+	    cap_id = find_device(PJMEDIA_DIR_CAPTURE, j);
+	    if (cap_id < 0)
+		continue;
+
+	    /* Renderer's device has callback: TRUE/FALSE. */
+	    for (k = 1; k >= 0; k--) {
+		rend_id = find_device(PJMEDIA_DIR_RENDER, k);
+		if (rend_id < 0)
+		    continue;
+
+		/* Check various formats to test format conversion. */
+		for (l = 0; l < PJ_ARRAY_SIZE(test_fmts); ++l) {
+		    pjmedia_format fmt;
+
+		    PJ_LOG(3,(THIS_FILE,
+			      "capturer %s (stream: %s) ===> "
+			      "renderer %s (stream: %s)",
+			      (i? "active": "passive"),
+			      (j? "active": "passive"),
+			      (i? "passive": "active"),
+			      (k? "active": "passive")));
+
+		    pjmedia_format_init_video(&fmt, test_fmts[l],
+					      640, 480, 25, 1);
+		    capture_render_loopback(i, cap_id, rend_id, &fmt);
+		}
+	    }
+	}
+    }
+
+    return 0;
+}
+
+int vid_port_test(void)
+{
+    int rc = 0;
+    pj_status_t status;
+
+    status = pjmedia_vid_dev_subsys_init(mem);
+    if (status != PJ_SUCCESS)
+        return -10;
+
+    rc = vidport_test();
+    if (rc != 0)
+	goto on_return;
+
+on_return:
+    pjmedia_vid_dev_subsys_shutdown();
+
+    return rc;
+}
+
+
+#endif /* PJMEDIA_HAS_VIDEO */
diff --git a/jni/pjproject-android/.svn/pristine/05/0553654b5b65e8ec17ec825a44fd043f29530dcc.svn-base b/jni/pjproject-android/.svn/pristine/05/0553654b5b65e8ec17ec825a44fd043f29530dcc.svn-base
new file mode 100644
index 0000000..3ce877a
--- /dev/null
+++ b/jni/pjproject-android/.svn/pristine/05/0553654b5b65e8ec17ec825a44fd043f29530dcc.svn-base
@@ -0,0 +1,1476 @@
+/* $Id$ */
+/* 
+ * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
+ * Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA 
+ */
+#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.
+ */
+
+
+/* **************************************************************************/
+/**
+ * @defgroup PJ_SYS_INFO System Information
+ * @ingroup PJ_OS
+ * @{
+ */
+
+/**
+ * These enumeration contains constants to indicate support of miscellaneous
+ * system features. These will go in "flags" field of #pj_sys_info structure.
+ */
+typedef enum pj_sys_info_flag
+{
+    /**
+     * Support for Apple iOS background feature.
+     */
+    PJ_SYS_HAS_IOS_BG = 1
+
+} pj_sys_info_flag;
+
+
+/**
+ * This structure contains information about the system. Use #pj_get_sys_info()
+ * to obtain the system information.
+ */
+typedef struct pj_sys_info
+{
+    /**
+     * Null terminated string containing processor information (e.g. "i386",
+     * "x86_64"). It may contain empty string if the value cannot be obtained.
+     */
+    pj_str_t	machine;
+
+    /**
+     * Null terminated string identifying the system operation (e.g. "Linux",
+     * "win32", "wince"). It may contain empty string if the value cannot be
+     * obtained.
+     */
+    pj_str_t	os_name;
+
+    /**
+     * A number containing the operating system version number. By convention,
+     * this field is divided into four bytes, where the highest order byte
+     * contains the most major version of the OS, the next less significant
+     * byte contains the less major version, and so on. How the OS version
+     * number is mapped into these four bytes would be specific for each OS.
+     * For example, Linux-2.6.32-28 would yield "os_ver" value of 0x0206201c,
+     * while for Windows 7 it will be 0x06010000 (because dwMajorVersion is
+     * 6 and dwMinorVersion is 1 for Windows 7).
+     *
+     * This field may contain zero if the OS version cannot be obtained.
+     */
+    pj_uint32_t	os_ver;
+
+    /**
+     * Null terminated string identifying the SDK name that is used to build
+     * the library (e.g. "glibc", "uclibc", "msvc", "wince"). It may contain
+     * empty string if the value cannot eb obtained.
+     */
+    pj_str_t	sdk_name;
+
+    /**
+     * A number containing the SDK version, using the numbering convention as
+     * the "os_ver" field. The value will be zero if the version cannot be
+     * obtained.
+     */
+    pj_uint32_t	sdk_ver;
+
+    /**
+     * A longer null terminated string identifying the underlying system with
+     * as much information as possible.
+     */
+    pj_str_t	info;
+
+    /**
+     * Other flags containing system specific information. The value is
+     * bitmask of #pj_sys_info_flag constants.
+     */
+    pj_uint32_t	flags;
+
+} pj_sys_info;
+
+
+/**
+ * Obtain the system information.
+ *
+ * @return	System information structure.
+ */
+PJ_DECL(const pj_sys_info*) pj_get_sys_info(void);
+
+/*
+ * @}
+ */
+
+/* **************************************************************************/
+/**
+ * @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;
+
+
+/**
+ * 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	    (64)
+#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);
+
+/**
+ * Check if this thread has been registered to PJLIB.
+ *
+ * @return		Non-zero if it is registered.
+ */
+PJ_DECL(pj_bool_t) pj_thread_is_registered(void);
+
+
+/**
+ * Get thread priority value for the thread.
+ *
+ * @param thread	Thread handle.
+ *
+ * @return		Thread priority value, or -1 on error.
+ */
+PJ_DECL(int) pj_thread_get_prio(pj_thread_t *thread);
+
+
+/**
+ * Set the thread priority. The priority value must be in the priority
+ * value range, which can be retrieved with #pj_thread_get_prio_min() and
+ * #pj_thread_get_prio_max() functions.
+ *
+ * @param thread	Thread handle.
+ * @param prio		New priority to be set to the thread.
+ *
+ * @return		PJ_SUCCESS on success or the error code.
+ */
+PJ_DECL(pj_status_t) pj_thread_set_prio(pj_thread_t *thread,  int prio);
+
+/**
+ * Get the lowest priority value available for this thread.
+ *
+ * @param thread	Thread handle.
+ * @return		Minimum thread priority value, or -1 on error.
+ */
+PJ_DECL(int) pj_thread_get_prio_min(pj_thread_t *thread);
+
+
+/**
+ * Get the highest priority value available for this thread.
+ *
+ * @param thread	Thread handle.
+ * @return		Minimum thread priority value, or -1 on error.
+ */
+PJ_DECL(int) pj_thread_get_prio_max(pj_thread_t *thread);
+
+
+/**
+ * Return native handle from pj_thread_t for manipulation using native
+ * OS APIs.
+ *
+ * @param thread	PJLIB thread descriptor.
+ *
+ * @return		Native thread handle. For example, when the
+ *			backend thread uses pthread, this function will
+ *			return pointer to pthread_t, and on Windows,
+ *			this function will return HANDLE.
+ */
+PJ_DECL(void*) pj_thread_get_os_handle(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, and block the caller thread until the specified thread exits.
+ * If the specified thread has already been dead, or it does not exist,
+ * the function will return immediately with successfull status.
+ *
+ * @param thread    The thread handle.
+ *
+ * @return PJ_SUCCESS 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_SYMBIAN_OS Symbian OS Specific
+ * @ingroup PJ_OS
+ * @{
+ * Functionalities specific to Symbian OS.
+ *
+ * Symbian OS strongly discourages the use of polling since this wastes
+ * CPU power, and instead provides Active Object and Active Scheduler
+ * pattern to allow application (in this case, PJLIB) to register asynchronous
+ * tasks. PJLIB port for Symbian complies to this recommended behavior.
+ * As the result, few things have been changed in PJLIB for Symbian:
+ *	- the timer heap (see @ref PJ_TIMER) is implemented with active
+ *	  object framework, and each timer entry registered to the timer 
+ *	  heap will register an Active Object to the Active Scheduler.
+ *	  Because of this, polling the timer heap with pj_timer_heap_poll()
+ *	  is no longer necessary, and this function will just evaluate
+ *	  to nothing.
+ *	- the ioqueue (see @ref PJ_IOQUEUE) is also implemented with
+ *	  active object framework, with each asynchronous operation will
+ *	  register an Active Object to the Active Scheduler. Because of
+ *	  this, polling the ioqueue with pj_ioqueue_poll() is no longer
+ *	  necessary, and this function will just evaluate to nothing.
+ *
+ * Since timer heap and ioqueue polling are no longer necessary, Symbian
+ * application can now poll for all events by calling 
+ * \a User::WaitForAnyRequest() and \a CActiveScheduler::RunIfReady().
+ * PJLIB provides a thin wrapper which calls these two functions,
+ * called pj_symbianos_poll().
+ */
+ 
+/**
+ * Wait the completion of any Symbian active objects. When the timeout
+ * value is not specified (the \a ms_timeout argument is -1), this 
+ * function is a thin wrapper which calls \a User::WaitForAnyRequest() 
+ * and \a CActiveScheduler::RunIfReady(). If the timeout value is
+ * specified, this function will schedule a timer entry to the timer
+ * heap (which is an Active Object), to limit the wait time for event
+ * occurences. Scheduling a timer entry is an expensive operation,
+ * therefore application should only specify a timeout value when it's
+ * really necessary (for example, when it's not sure there are other
+ * Active Objects currently running in the application).
+ *
+ * @param priority	The minimum priority of the Active Objects to
+ *			poll, which values are from CActive::TPriority
+ *			constants. If -1 is given, CActive::EPriorityStandard.
+ *			priority will be used.
+ * @param ms_timeout	Optional timeout to wait. Application should
+ *			specify -1 to let the function wait indefinitely
+ *			for any events.
+ *
+ * @return		PJ_TRUE if there have been any events executed
+ *			during the polling. This function will only return
+ *			PJ_FALSE if \a ms_timeout argument is specified
+ *			(i.e. the value is not -1) and there was no event
+ *			executed when the timeout timer elapsed.
+ */
+PJ_DECL(pj_bool_t) pj_symbianos_poll(int priority, int ms_timeout);
+
+
+/**
+ * This structure declares Symbian OS specific parameters that can be
+ * specified when calling #pj_symbianos_set_params().
+ */
+typedef struct pj_symbianos_params 
+{
+    /**
+     * Optional RSocketServ instance to be used by PJLIB. If this
+     * value is NULL, PJLIB will create a new RSocketServ instance
+     * when pj_init() is called.
+     */
+    void	*rsocketserv;
+    
+    /**
+     * Optional RConnection instance to be used by PJLIB when creating
+     * sockets. If this value is NULL, no RConnection will be
+     * specified when creating sockets.
+     */
+    void	*rconnection;
+    
+    /**
+     * Optional RHostResolver instance to be used by PJLIB. If this value
+     * is NULL, a new RHostResolver instance will be created when
+     * pj_init() is called.
+     */
+    void 	*rhostresolver;
+     
+    /**
+     * Optional RHostResolver for IPv6 instance to be used by PJLIB. 
+     * If this value is NULL, a new RHostResolver instance will be created
+     * when pj_init() is called.
+     */
+    void 	*rhostresolver6;
+     
+} pj_symbianos_params;
+
+/**
+ * Specify Symbian OS parameters to be used by PJLIB. This function MUST
+ * be called before #pj_init() is called.
+ *
+ * @param prm		Symbian specific parameters.
+ *
+ * @return		PJ_SUCCESS if the parameters can be applied
+ *			successfully.
+ */
+PJ_DECL(pj_status_t) pj_symbianos_set_params(pj_symbianos_params *prm);
+
+/**
+ *  Notify PJLIB that the access point connection has been down or unusable
+ *  and PJLIB should not try to access the Symbian socket API (especially ones
+ *  that send packets). Sending packet when RConnection is reconnected to 
+ *  different access point may cause the WaitForRequest() for the function to 
+ *  block indefinitely.
+ *  
+ *  @param up		If set to PJ_FALSE it will cause PJLIB to not try
+ *  			to access socket API, and error will be returned
+ *  			immediately instead.
+ */
+PJ_DECL(void) pj_symbianos_set_connection_status(pj_bool_t up);
+
+/**
+ * @}
+ */
+ 
+/* **************************************************************************/
+/**
+ * @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.
+ *
+ * @param atomic_var	the atomic variable.
+ *
+ * @return              The incremented value.
+ */
+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 and get the result.
+ *
+ * @param atomic_var	the atomic variable.
+ *
+ * @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 and get the result.
+ *
+ * @param atomic_var	The atomic variable.
+ * @param value		Value to be added.
+ *
+ * @return              The result after the addition.
+ */
+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_RECURSE: 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.
+ */
+PJ_DECL(pj_bool_t) pj_mutex_is_locked(pj_mutex_t *mutex);
+
+/**
+ * @}
+ */
+
+/* **************************************************************************/
+/**
+ * @defgroup PJ_RW_MUTEX Reader/Writer Mutex
+ * @ingroup PJ_OS
+ * @{
+ * Reader/writer mutex is a classic synchronization object where multiple
+ * readers can acquire the mutex, but only a single writer can acquire the 
+ * mutex.
+ */
+
+/**
+ * Opaque declaration for reader/writer mutex.
+ * Reader/writer mutex is a classic synchronization object where multiple
+ * readers can acquire the mutex, but only a single writer can acquire the 
+ * mutex.
+ */
+typedef struct pj_rwmutex_t pj_rwmutex_t;
+
+/**
+ * Create reader/writer mutex.
+ *
+ * @param pool	    Pool to allocate memory for the mutex.
+ * @param name	    Name to be assigned to the mutex.
+ * @param mutex	    Pointer to receive the newly created mutex.
+ *
+ * @return	    PJ_SUCCESS on success, or the error code.
+ */
+PJ_DECL(pj_status_t) pj_rwmutex_create(pj_pool_t *pool, const char *name,
+				       pj_rwmutex_t **mutex);
+
+/**
+ * Lock the mutex for reading.
+ *
+ * @param mutex	    The mutex.
+ * @return	    PJ_SUCCESS on success, or the error code.
+ */
+PJ_DECL(pj_status_t) pj_rwmutex_lock_read(pj_rwmutex_t *mutex);
+
+/**
+ * Lock the mutex for writing.
+ *
+ * @param mutex	    The mutex.
+ * @return	    PJ_SUCCESS on success, or the error code.
+ */
+PJ_DECL(pj_status_t) pj_rwmutex_lock_write(pj_rwmutex_t *mutex);
+
+/**
+ * Release read lock.
+ *
+ * @param mutex	    The mutex.
+ * @return	    PJ_SUCCESS on success, or the error code.
+ */
+PJ_DECL(pj_status_t) pj_rwmutex_unlock_read(pj_rwmutex_t *mutex);
+
+/**
+ * Release write lock.
+ *
+ * @param mutex	    The mutex.
+ * @return	    PJ_SUCCESS on success, or the error code.
+ */
+PJ_DECL(pj_status_t) pj_rwmutex_unlock_write(pj_rwmutex_t *mutex);
+
+/**
+ * Destroy reader/writer mutex.
+ *
+ * @param mutex	    The mutex.
+ * @return	    PJ_SUCCESS on success, or the error code.
+ */
+PJ_DECL(pj_status_t) pj_rwmutex_destroy(pj_rwmutex_t *mutex);
+
+
+/**
+ * @}
+ */
+
+
+/* **************************************************************************/
+/**
+ * @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
+
+/**
+ * Get monotonic time since some unspecified starting point.
+ *
+ * @param tv	Variable to store the result.
+ *
+ * @return PJ_SUCCESS if successful.
+ */
+PJ_DECL(pj_status_t) pj_gettickcount(pj_time_val *tv);
+
+/**
+ * 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);
+
+/**
+ * Set timestamp from 32bit values.
+ * @param t	    The timestamp to be set.
+ * @param hi	    The high 32bit part.
+ * @param lo	    The low 32bit part.
+ */
+PJ_INLINE(void) pj_set_timestamp32(pj_timestamp *t, pj_uint32_t hi,
+				   pj_uint32_t lo)
+{
+    t->u32.hi = hi;
+    t->u32.lo = lo;
+}
+
+
+/**
+ * Compare timestamp t1 and t2.
+ * @param t1	    t1.
+ * @param t2	    t2.
+ * @return	    -1 if (t1 < t2), 1 if (t1 > t2), or 0 if (t1 == t2)
+ */
+PJ_INLINE(int) pj_cmp_timestamp(const pj_timestamp *t1, const pj_timestamp *t2)
+{
+#if PJ_HAS_INT64
+    if (t1->u64 < t2->u64)
+	return -1;
+    else if (t1->u64 > t2->u64)
+	return 1;
+    else
+	return 0;
+#else
+    if (t1->u32.hi < t2->u32.hi ||
+	(t1->u32.hi == t2->u32.hi && t1->u32.lo < t2->u32.lo))
+	return -1;
+    else if (t1->u32.hi > t2->u32.hi ||
+	     (t1->u32.hi == t2->u32.hi && t1->u32.lo > t2->u32.lo))
+	return 1;
+    else
+	return 0;
+#endif
+}
+
+
+/**
+ * Add timestamp t2 to t1.
+ * @param t1	    t1.
+ * @param t2	    t2.
+ */
+PJ_INLINE(void) pj_add_timestamp(pj_timestamp *t1, const pj_timestamp *t2)
+{
+#if PJ_HAS_INT64
+    t1->u64 += t2->u64;
+#else
+    pj_uint32_t old = t1->u32.lo;
+    t1->u32.hi += t2->u32.hi;
+    t1->u32.lo += t2->u32.lo;
+    if (t1->u32.lo < old)
+	++t1->u32.hi;
+#endif
+}
+
+/**
+ * Add timestamp t2 to t1.
+ * @param t1	    t1.
+ * @param t2	    t2.
+ */
+PJ_INLINE(void) pj_add_timestamp32(pj_timestamp *t1, pj_uint32_t t2)
+{
+#if PJ_HAS_INT64
+    t1->u64 += t2;
+#else
+    pj_uint32_t old = t1->u32.lo;
+    t1->u32.lo += t2;
+    if (t1->u32.lo < old)
+	++t1->u32.hi;
+#endif
+}
+
+/**
+ * Substract timestamp t2 from t1.
+ * @param t1	    t1.
+ * @param t2	    t2.
+ */
+PJ_INLINE(void) pj_sub_timestamp(pj_timestamp *t1, const pj_timestamp *t2)
+{
+#if PJ_HAS_INT64
+    t1->u64 -= t2->u64;
+#else
+    t1->u32.hi -= t2->u32.hi;
+    if (t1->u32.lo >= t2->u32.lo)
+	t1->u32.lo -= t2->u32.lo;
+    else {
+	t1->u32.lo -= t2->u32.lo;
+	--t1->u32.hi;
+    }
+#endif
+}
+
+/**
+ * Substract timestamp t2 from t1.
+ * @param t1	    t1.
+ * @param t2	    t2.
+ */
+PJ_INLINE(void) pj_sub_timestamp32(pj_timestamp *t1, pj_uint32_t t2)
+{
+#if PJ_HAS_INT64
+    t1->u64 -= t2;
+#else
+    if (t1->u32.lo >= t2)
+	t1->u32.lo -= t2;
+    else {
+	t1->u32.lo -= t2;
+	--t1->u32.hi;
+    }
+#endif
+}
+
+/**
+ * Get the timestamp difference between t2 and t1 (that is t2 minus t1),
+ * and return a 32bit signed integer difference.
+ */
+PJ_INLINE(pj_int32_t) pj_timestamp_diff32(const pj_timestamp *t1,
+					  const pj_timestamp *t2)
+{
+    /* Be careful with the signess (I think!) */
+#if PJ_HAS_INT64
+    pj_int64_t diff = t2->u64 - t1->u64;
+    return (pj_int32_t) diff;
+#else
+    pj_int32 diff = t2->u32.lo - t1->u32.lo;
+    return diff;
+#endif
+}
+
+
+/**
+ * Calculate the elapsed time, and store it in pj_time_val.
+ * This function calculates the elapsed time using highest precision
+ * calculation that is available for current platform, considering
+ * 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 as 32-bit miliseconds.
+ * 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 milisecond.
+ *
+ * @see pj_elapsed_time(), pj_elapsed_cycle(), pj_elapsed_nanosec()
+ */
+PJ_DECL(pj_uint32_t) pj_elapsed_msec( const pj_timestamp *start,
+                                      const pj_timestamp *stop );
+
+/**
+ * Variant of #pj_elapsed_msec() which returns 64bit value.
+ */
+PJ_DECL(pj_uint64_t) pj_elapsed_msec64(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 */
+
+/** @} */
+
+
+/* **************************************************************************/
+/**
+ * @defgroup PJ_APP_OS Application execution
+ * @ingroup PJ_OS
+ * @{
+ */
+
+/* Type for main function. */
+typedef int (*pj_main_func_ptr)(int argc, char *argv[]);
+
+/**
+ * Run the application. This function has to be called in the main thread
+ * and after doing the necessary initialization according to the flags
+ * provided, it will call main_func() function.
+ *
+ * @param main_func Application's main function.
+ * @param argc	    Number of arguments from the main() function, which
+ * 		    will be passed to main_func() function.
+ * @param argv	    The arguments from the main() function, which will
+ * 		    be passed to main_func() function.
+ * @param flags     Flags for application execution, currently must be 0.
+ *
+ * @return          main_func()'s return value.
+ */
+int pj_run_app(pj_main_func_ptr main_func, int argc, char *argv[],
+	       unsigned flags);
+
+/** @} */
+
+
+/* **************************************************************************/
+/**
+ * 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/jni/pjproject-android/.svn/pristine/05/058c7736b9468d82f98a908d3d3baee93a5a9d1c.svn-base b/jni/pjproject-android/.svn/pristine/05/058c7736b9468d82f98a908d3d3baee93a5a9d1c.svn-base
new file mode 100644
index 0000000..465c0e8
--- /dev/null
+++ b/jni/pjproject-android/.svn/pristine/05/058c7736b9468d82f98a908d3d3baee93a5a9d1c.svn-base
@@ -0,0 +1,158 @@
+/* $Id$ */
+/* 
+ * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
+ * Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA 
+ */
+#ifndef __PJMEDIA_VID_CODEC_UTIL_H__
+#define __PJMEDIA_VID_CODEC_UTIL_H__
+
+
+/**
+ * @file vid_codec_util.h
+ * @brief Video codec utilities.
+ */
+
+#include <pjmedia/vid_codec.h>
+#include <pjmedia/sdp_neg.h>
+
+PJ_BEGIN_DECL
+
+
+/**
+ * Definition of H.263 parameters.
+ */
+typedef struct pjmedia_vid_codec_h263_fmtp
+{
+    unsigned mpi_cnt;		    /**< # of parsed MPI param		    */
+    struct mpi {
+	pjmedia_rect_size   size;   /**< Picture size/resolution	    */
+	unsigned	    val;    /**< MPI value			    */
+    } mpi[32];			    /**< Minimum Picture Interval parameter */
+
+} pjmedia_vid_codec_h263_fmtp;
+
+
+/**
+ * Parse SDP fmtp of H.263.
+ *
+ * @param fmtp		The H.263 SDP fmtp to be parsed.
+ * @param h263_fmtp	The parsing result.
+ *
+ * @return		PJ_SUCCESS on success.
+ */
+PJ_DECL(pj_status_t) pjmedia_vid_codec_h263_parse_fmtp(
+				const pjmedia_codec_fmtp *fmtp,
+				pjmedia_vid_codec_h263_fmtp *h263_fmtp);
+
+
+/**
+ * Parse, negotiate, and apply the encoding and decoding SDP fmtp of H.263
+ * in the specified codec parameter.
+ *
+ * @param param		The codec parameter.
+ *
+ * @return		PJ_SUCCESS on success.
+ */
+PJ_DECL(pj_status_t) pjmedia_vid_codec_h263_apply_fmtp(
+				pjmedia_vid_codec_param *param);
+
+
+/**
+ * Definition of H.264 parameters.
+ */
+typedef struct pjmedia_vid_codec_h264_fmtp
+{
+    /* profile-level-id */
+    pj_uint8_t	    profile_idc;    /**< Profile ID			    */
+    pj_uint8_t	    profile_iop;    /**< Profile constraints bits	    */
+    pj_uint8_t	    level;	    /**< Level				    */
+
+    /* packetization-mode */
+    pj_uint8_t	    packetization_mode;	/**< Packetization mode		    */
+
+    /* max-mbps, max-fs, max-cpb, max-dpb, and max-br */
+    unsigned	    max_mbps;	    /**< Max macroblock processing rate	    */
+    unsigned	    max_fs;	    /**< Max frame size (in macroblocks)    */
+    unsigned	    max_cpb;	    /**< Max coded picture buffer size	    */
+    unsigned	    max_dpb;	    /**< Max decoded picture buffer size    */
+    unsigned	    max_br;	    /**< Max video bit rate		    */
+
+    /* sprop-parameter-sets, in NAL units */
+    pj_size_t	    sprop_param_sets_len;   /**< Parameter set length	    */
+    pj_uint8_t	    sprop_param_sets[256];  /**< Parameter set (SPS & PPS),
+						 in NAL unit bitstream	    */
+
+} pjmedia_vid_codec_h264_fmtp;
+
+
+/**
+ * Parse SDP fmtp of H.264.
+ *
+ * @param fmtp		The H.264 SDP fmtp to be parsed.
+ * @param h264_fmtp	The parsing result.
+ *
+ * @return		PJ_SUCCESS on success.
+ */
+PJ_DECL(pj_status_t) pjmedia_vid_codec_h264_parse_fmtp(
+				const pjmedia_codec_fmtp *fmtp,
+				pjmedia_vid_codec_h264_fmtp *h264_fmtp);
+
+
+/**
+ * Match H.264 format in the SDP media offer and answer. This will compare
+ * H.264 identifier parameters in SDP fmtp, i.e: "profile-level-id" and
+ * "packetization-mode" fields. For better interoperability, when the option
+ * #PJMEDIA_SDP_NEG_FMT_MATCH_ALLOW_MODIFY_ANSWER is set, this function
+ * may update the answer so the parameters in the answer match to ones
+ * in the offer.
+ *
+ * @param pool		The memory pool.
+ * @param offer		The SDP media offer.
+ * @param o_fmt_idx	Index of the H.264 format in the SDP media offer.
+ * @param answer	The SDP media answer.
+ * @param a_fmt_idx	Index of the H.264 format in the SDP media answer.
+ * @param option	The format matching option, see
+ *			#pjmedia_sdp_neg_fmt_match_flag.
+ *
+ * @return		PJ_SUCCESS when the formats in offer and answer match.
+ */
+PJ_DECL(pj_status_t) pjmedia_vid_codec_h264_match_sdp(
+						pj_pool_t *pool,
+						pjmedia_sdp_media *offer,
+						unsigned o_fmt_idx,
+						pjmedia_sdp_media *answer,
+						unsigned a_fmt_idx,
+						unsigned option);
+
+
+/**
+ * Parse and apply the encoding and decoding SDP fmtp of H.264 in the
+ * specified codec parameter. This will validate size and fps to conform
+ * to H.264 level specified in SDP fmtp "profile-level-id".
+ *
+ * @param param		The codec parameter.
+ *
+ * @return		PJ_SUCCESS on success.
+ */
+PJ_DECL(pj_status_t) pjmedia_vid_codec_h264_apply_fmtp(
+				pjmedia_vid_codec_param *param);
+
+
+PJ_END_DECL
+
+
+#endif	/* __PJMEDIA_VID_CODEC_UTIL_H__ */
diff --git a/jni/pjproject-android/.svn/pristine/05/05e3d093260ca88f8e2afdec338efb23e09e1982.svn-base b/jni/pjproject-android/.svn/pristine/05/05e3d093260ca88f8e2afdec338efb23e09e1982.svn-base
new file mode 100644
index 0000000..634fb6a
--- /dev/null
+++ b/jni/pjproject-android/.svn/pristine/05/05e3d093260ca88f8e2afdec338efb23e09e1982.svn-base
@@ -0,0 +1,7 @@
+#ifndef RESAMPLE_HAS_SMALL_FILTER
+#   define RESAMPLE_HAS_SMALL_FILTER	1
+#endif
+
+#ifndef RESAMPLE_HAS_LARGE_FILTER
+#   define RESAMPLE_HAS_LARGE_FILTER	1
+#endif
diff --git a/jni/pjproject-android/.svn/pristine/05/05e7887e9b5853ecd7217162e5820a9d388bed7d.svn-base b/jni/pjproject-android/.svn/pristine/05/05e7887e9b5853ecd7217162e5820a9d388bed7d.svn-base
new file mode 100644
index 0000000..62b413a
--- /dev/null
+++ b/jni/pjproject-android/.svn/pristine/05/05e7887e9b5853ecd7217162e5820a9d388bed7d.svn-base
@@ -0,0 +1,3058 @@
+/* $Id$ */
+/* 
+ * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
+ * Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA 
+ */
+#include <pjsua-lib/pjsua.h>
+#include <pjsua-lib/pjsua_internal.h>
+
+
+#define THIS_FILE		"pjsua_media.c"
+
+#define DEFAULT_RTP_PORT	4000
+
+#ifndef PJSUA_REQUIRE_CONSECUTIVE_RTCP_PORT
+#   define PJSUA_REQUIRE_CONSECUTIVE_RTCP_PORT	0
+#endif
+
+static void pjsua_media_config_dup(pj_pool_t *pool,
+				   pjsua_media_config *dst,
+				   const pjsua_media_config *src)
+{
+    pj_memcpy(dst, src, sizeof(*src));
+    pj_strdup(pool, &dst->turn_server, &src->turn_server);
+    pj_stun_auth_cred_dup(pool, &dst->turn_auth_cred, &src->turn_auth_cred);
+}
+
+
+/**
+ * Init media subsystems.
+ */
+pj_status_t pjsua_media_subsys_init(const pjsua_media_config *cfg)
+{
+    pj_status_t status;
+
+    pj_log_push_indent();
+
+    /* Specify which audio device settings are save-able */
+    pjsua_var.aud_svmask = 0xFFFFFFFF;
+    /* These are not-settable */
+    pjsua_var.aud_svmask &= ~(PJMEDIA_AUD_DEV_CAP_EXT_FORMAT |
+			      PJMEDIA_AUD_DEV_CAP_INPUT_SIGNAL_METER |
+			      PJMEDIA_AUD_DEV_CAP_OUTPUT_SIGNAL_METER);
+    /* EC settings use different API */
+    pjsua_var.aud_svmask &= ~(PJMEDIA_AUD_DEV_CAP_EC |
+			      PJMEDIA_AUD_DEV_CAP_EC_TAIL);
+
+    /* Copy configuration */
+    pjsua_media_config_dup(pjsua_var.pool, &pjsua_var.media_cfg, cfg);
+
+    /* Normalize configuration */
+    if (pjsua_var.media_cfg.snd_clock_rate == 0) {
+	pjsua_var.media_cfg.snd_clock_rate = pjsua_var.media_cfg.clock_rate;
+    }
+
+    if (pjsua_var.media_cfg.has_ioqueue &&
+	pjsua_var.media_cfg.thread_cnt == 0)
+    {
+	pjsua_var.media_cfg.thread_cnt = 1;
+    }
+
+    if (pjsua_var.media_cfg.max_media_ports < pjsua_var.ua_cfg.max_calls) {
+	pjsua_var.media_cfg.max_media_ports = pjsua_var.ua_cfg.max_calls + 2;
+    }
+
+    /* Create media endpoint. */
+    status = pjmedia_endpt_create(&pjsua_var.cp.factory, 
+				  pjsua_var.media_cfg.has_ioqueue? NULL :
+				     pjsip_endpt_get_ioqueue(pjsua_var.endpt),
+				  pjsua_var.media_cfg.thread_cnt,
+				  &pjsua_var.med_endpt);
+    if (status != PJ_SUCCESS) {
+	pjsua_perror(THIS_FILE, 
+		     "Media stack initialization has returned error", 
+		     status);
+	goto on_error;
+    }
+
+    status = pjsua_aud_subsys_init();
+    if (status != PJ_SUCCESS)
+	goto on_error;
+
+#if defined(PJMEDIA_HAS_SRTP) && (PJMEDIA_HAS_SRTP != 0)
+    /* Initialize SRTP library (ticket #788). */
+    status = pjmedia_srtp_init_lib(pjsua_var.med_endpt);
+    if (status != PJ_SUCCESS) {
+	pjsua_perror(THIS_FILE, "Error initializing SRTP library", 
+		     status);
+	goto on_error;
+    }
+#endif
+
+    /* Video */
+#if PJMEDIA_HAS_VIDEO
+    status = pjsua_vid_subsys_init();
+    if (status != PJ_SUCCESS)
+	goto on_error;
+#endif
+
+    pj_log_pop_indent();
+    return PJ_SUCCESS;
+
+on_error:
+    pj_log_pop_indent();
+    return status;
+}
+
+/*
+ * Start pjsua media subsystem.
+ */
+pj_status_t pjsua_media_subsys_start(void)
+{
+    pj_status_t status;
+
+    pj_log_push_indent();
+
+#if DISABLED_FOR_TICKET_1185
+    /* Create media for calls, if none is specified */
+    if (pjsua_var.calls[0].media[0].tp == NULL) {
+	pjsua_transport_config transport_cfg;
+
+	/* Create default transport config */
+	pjsua_transport_config_default(&transport_cfg);
+	transport_cfg.port = DEFAULT_RTP_PORT;
+
+	status = pjsua_media_transports_create(&transport_cfg);
+	if (status != PJ_SUCCESS) {
+	    pj_log_pop_indent();
+	    return status;
+	}
+    }
+#endif
+
+    /* Audio */
+    status = pjsua_aud_subsys_start();
+    if (status != PJ_SUCCESS) {
+	pj_log_pop_indent();
+	return status;
+    }
+
+    /* Video */
+#if PJMEDIA_HAS_VIDEO
+    status = pjsua_vid_subsys_start();
+    if (status != PJ_SUCCESS) {
+	pjsua_aud_subsys_destroy();
+	pj_log_pop_indent();
+	return status;
+    }
+#endif
+
+    /* Perform NAT detection */
+    if (pjsua_var.ua_cfg.stun_srv_cnt) {
+	status = pjsua_detect_nat_type();
+	if (status != PJ_SUCCESS) {
+	    PJ_PERROR(1,(THIS_FILE, status, "NAT type detection failed"));
+	}
+    }
+
+    pj_log_pop_indent();
+    return PJ_SUCCESS;
+}
+
+
+/*
+ * Destroy pjsua media subsystem.
+ */
+pj_status_t pjsua_media_subsys_destroy(unsigned flags)
+{
+    unsigned i;
+
+    PJ_LOG(4,(THIS_FILE, "Shutting down media.."));
+    pj_log_push_indent();
+
+    if (pjsua_var.med_endpt) {
+        /* Wait for media endpoint's worker threads to quit. */
+        pjmedia_endpt_stop_threads(pjsua_var.med_endpt);
+
+	pjsua_aud_subsys_destroy();
+    }
+
+    /* Close media transports */
+    for (i=0; i<pjsua_var.ua_cfg.max_calls; ++i) {
+        /* TODO: check if we're not allowed to send to network in the
+         *       "flags", and if so do not do TURN allocation...
+         */
+	PJ_UNUSED_ARG(flags);
+	pjsua_media_channel_deinit(i);
+    }
+
+    /* Destroy media endpoint. */
+    if (pjsua_var.med_endpt) {
+
+#	if PJMEDIA_HAS_VIDEO
+	    pjsua_vid_subsys_destroy();
+#	endif
+
+	pjmedia_endpt_destroy(pjsua_var.med_endpt);
+	pjsua_var.med_endpt = NULL;
+
+	/* Deinitialize sound subsystem */
+	// Not necessary, as pjmedia_snd_deinit() should have been called
+	// in pjmedia_endpt_destroy().
+	//pjmedia_snd_deinit();
+    }
+
+    pj_log_pop_indent();
+
+    return PJ_SUCCESS;
+}
+
+/*
+ * Create RTP and RTCP socket pair, and possibly resolve their public
+ * address via STUN.
+ */
+static pj_status_t create_rtp_rtcp_sock(pjsua_call_media *call_med,
+					const pjsua_transport_config *cfg,
+					pjmedia_sock_info *skinfo)
+{
+    enum {
+	RTP_RETRY = 100
+    };
+    int i;
+    pj_bool_t use_ipv6;
+    int af;
+    pj_sockaddr bound_addr;
+    pj_sockaddr mapped_addr[2];
+    pj_status_t status = PJ_SUCCESS;
+    char addr_buf[PJ_INET6_ADDRSTRLEN+10];
+    pjsua_acc *acc = &pjsua_var.acc[call_med->call->acc_id];
+    pj_sock_t sock[2];
+
+    use_ipv6 = (acc->cfg.ipv6_media_use != PJSUA_IPV6_DISABLED);
+    af = use_ipv6 ? pj_AF_INET6() : pj_AF_INET();
+
+    /* Make sure STUN server resolution has completed */
+    if (!use_ipv6 && pjsua_sip_acc_is_using_stun(call_med->call->acc_id)) {
+	status = resolve_stun_server(PJ_TRUE);
+	if (status != PJ_SUCCESS) {
+	    pjsua_perror(THIS_FILE, "Error resolving STUN server", status);
+	    return status;
+	}
+    }
+
+    if (acc->next_rtp_port == 0)
+	acc->next_rtp_port = (pj_uint16_t)cfg->port;
+
+    if (acc->next_rtp_port == 0)
+	acc->next_rtp_port = (pj_uint16_t)DEFAULT_RTP_PORT;
+
+    for (i=0; i<2; ++i)
+	sock[i] = PJ_INVALID_SOCKET;
+
+    pj_sockaddr_init(af, &bound_addr, NULL, 0);
+    if (cfg->bound_addr.slen) {
+	status = pj_sockaddr_set_str_addr(af, &bound_addr, &cfg->bound_addr);
+	if (status != PJ_SUCCESS) {
+	    pjsua_perror(THIS_FILE, "Unable to resolve transport bind address",
+			 status);
+	    return status;
+	}
+    }
+
+    /* Loop retry to bind RTP and RTCP sockets. */
+    for (i=0; i<RTP_RETRY; ++i, acc->next_rtp_port += 2) {
+
+        if (cfg->port > 0 && cfg->port_range > 0 &&
+            (acc->next_rtp_port > cfg->port + cfg->port_range ||
+             acc->next_rtp_port < cfg->port))
+        {
+            acc->next_rtp_port = (pj_uint16_t)cfg->port;
+        }
+
+	/* Create RTP socket. */
+	status = pj_sock_socket(af, pj_SOCK_DGRAM(), 0, &sock[0]);
+	if (status != PJ_SUCCESS) {
+	    pjsua_perror(THIS_FILE, "socket() error", status);
+	    return status;
+	}
+
+	/* Apply QoS to RTP socket, if specified */
+	status = pj_sock_apply_qos2(sock[0], cfg->qos_type,
+				    &cfg->qos_params,
+				    2, THIS_FILE, "RTP socket");
+
+	/* Bind RTP socket */
+	pj_sockaddr_set_port(&bound_addr, acc->next_rtp_port);
+	status=pj_sock_bind(sock[0], &bound_addr,
+	                    pj_sockaddr_get_len(&bound_addr));
+	if (status != PJ_SUCCESS) {
+	    pj_sock_close(sock[0]);
+	    sock[0] = PJ_INVALID_SOCKET;
+	    continue;
+	}
+
+	/* Create RTCP socket. */
+	status = pj_sock_socket(af, pj_SOCK_DGRAM(), 0, &sock[1]);
+	if (status != PJ_SUCCESS) {
+	    pjsua_perror(THIS_FILE, "socket() error", status);
+	    pj_sock_close(sock[0]);
+	    return status;
+	}
+
+	/* Apply QoS to RTCP socket, if specified */
+	status = pj_sock_apply_qos2(sock[1], cfg->qos_type,
+				    &cfg->qos_params,
+				    2, THIS_FILE, "RTCP socket");
+
+	/* Bind RTCP socket */
+	pj_sockaddr_set_port(&bound_addr, (pj_uint16_t)(acc->next_rtp_port+1));
+	status=pj_sock_bind(sock[1], &bound_addr,
+	                    pj_sockaddr_get_len(&bound_addr));
+	if (status != PJ_SUCCESS) {
+	    pj_sock_close(sock[0]);
+	    sock[0] = PJ_INVALID_SOCKET;
+
+	    pj_sock_close(sock[1]);
+	    sock[1] = 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 (!use_ipv6 && pjsua_sip_acc_is_using_stun(call_med->call->acc_id) &&
+	    pjsua_var.stun_srv.addr.sa_family != 0)
+	{
+	    char ip_addr[32];
+	    pj_str_t stun_srv;
+	    pj_sockaddr_in resolved_addr[2];
+	    pjstun_setting stun_opt;
+
+	    pj_ansi_strcpy(ip_addr,
+			   pj_inet_ntoa(pjsua_var.stun_srv.ipv4.sin_addr));
+	    stun_srv = pj_str(ip_addr);
+
+	    pj_bzero(&stun_opt, sizeof(stun_opt));
+	    stun_opt.use_stun2 = pjsua_var.ua_cfg.stun_map_use_stun2;
+	    stun_opt.srv1  = stun_opt.srv2  = stun_srv;
+	    stun_opt.port1 = stun_opt.port2 = 
+			     pj_ntohs(pjsua_var.stun_srv.ipv4.sin_port);
+	    status=pjstun_get_mapped_addr2(&pjsua_var.cp.factory, &stun_opt,
+					   2, sock, resolved_addr);
+#if defined(PJ_IPHONE_OS_HAS_MULTITASKING_SUPPORT) && \
+	    PJ_IPHONE_OS_HAS_MULTITASKING_SUPPORT!=0
+	    /* Handle EPIPE (Broken Pipe) error, which happens on UDP socket
+	     * after app wakes up from suspended state. In this case, simply
+	     * just retry.
+	     * P.S.: The magic status is PJ_STATUS_FROM_OS(EPIPE)
+	     */
+	    if (status == 120032) {
+		PJ_LOG(4,(THIS_FILE, "Got EPIPE error, retrying.."));
+		pj_sock_close(sock[0]);
+		sock[0] = PJ_INVALID_SOCKET;
+
+		pj_sock_close(sock[1]);
+		sock[1] = PJ_INVALID_SOCKET;
+
+		continue;
+	    }
+	    else
+#endif
+	    if (status != PJ_SUCCESS) {
+		pjsua_perror(THIS_FILE, "STUN resolve error", status);
+		goto on_error;
+	    }
+
+	    pj_sockaddr_cp(&mapped_addr[0], &resolved_addr[0]);
+	    pj_sockaddr_cp(&mapped_addr[1], &resolved_addr[1]);
+
+#if PJSUA_REQUIRE_CONSECUTIVE_RTCP_PORT
+	    if (pj_sockaddr_get_port(&mapped_addr[1]) ==
+		pj_sockaddr_get_port(&mapped_addr[0])+1)
+	    {
+		/* Success! */
+		break;
+	    }
+
+	    pj_sock_close(sock[0]);
+	    sock[0] = PJ_INVALID_SOCKET;
+
+	    pj_sock_close(sock[1]);
+	    sock[1] = PJ_INVALID_SOCKET;
+#else
+	    if (pj_sockaddr_get_port(&mapped_addr[1]) !=
+		pj_sockaddr_get_port(&mapped_addr[0])+1)
+	    {
+		PJ_LOG(4,(THIS_FILE,
+			  "Note: STUN mapped RTCP port %d is not adjacent"
+			  " to RTP port %d",
+			  pj_sockaddr_get_port(&mapped_addr[1]),
+			  pj_sockaddr_get_port(&mapped_addr[0])));
+	    }
+	    /* Success! */
+	    break;
+#endif
+
+	} else if (cfg->public_addr.slen) {
+
+	    status = pj_sockaddr_init(af, &mapped_addr[0], &cfg->public_addr,
+				      (pj_uint16_t)acc->next_rtp_port);
+	    if (status != PJ_SUCCESS)
+		goto on_error;
+
+	    status = pj_sockaddr_init(af, &mapped_addr[1], &cfg->public_addr,
+				      (pj_uint16_t)(acc->next_rtp_port+1));
+	    if (status != PJ_SUCCESS)
+		goto on_error;
+
+	    break;
+
+	} else {
+	    if (acc->cfg.allow_sdp_nat_rewrite && acc->reg_mapped_addr.slen) {
+		pj_status_t status;
+
+		/* Take the address from mapped addr as seen by registrar */
+		status = pj_sockaddr_set_str_addr(af, &bound_addr,
+		                                  &acc->reg_mapped_addr);
+		if (status != PJ_SUCCESS) {
+		    /* just leave bound_addr with whatever it was
+		    pj_bzero(pj_sockaddr_get_addr(&bound_addr),
+		             pj_sockaddr_get_addr_len(&bound_addr));
+		     */
+		}
+	    }
+
+	    if (!pj_sockaddr_has_addr(&bound_addr)) {
+		pj_sockaddr addr;
+
+		/* Get local IP address. */
+		status = pj_gethostip(af, &addr);
+		if (status != PJ_SUCCESS)
+		    goto on_error;
+
+		pj_sockaddr_copy_addr(&bound_addr, &addr);
+	    }
+
+	    for (i=0; i<2; ++i) {
+		pj_sockaddr_init(af, &mapped_addr[i], NULL, 0);
+		pj_sockaddr_copy_addr(&mapped_addr[i], &bound_addr);
+		pj_sockaddr_set_port(&mapped_addr[i],
+		                     (pj_uint16_t)(acc->next_rtp_port+i));
+	    }
+
+	    break;
+	}
+    }
+
+    if (sock[0] == PJ_INVALID_SOCKET) {
+	PJ_LOG(1,(THIS_FILE,
+		  "Unable to find appropriate RTP/RTCP ports combination"));
+	goto on_error;
+    }
+
+
+    skinfo->rtp_sock = sock[0];
+    pj_sockaddr_cp(&skinfo->rtp_addr_name, &mapped_addr[0]);
+
+    skinfo->rtcp_sock = sock[1];
+    pj_sockaddr_cp(&skinfo->rtcp_addr_name, &mapped_addr[1]);
+
+    PJ_LOG(4,(THIS_FILE, "RTP socket reachable at %s",
+	      pj_sockaddr_print(&skinfo->rtp_addr_name, addr_buf,
+				sizeof(addr_buf), 3)));
+    PJ_LOG(4,(THIS_FILE, "RTCP socket reachable at %s",
+	      pj_sockaddr_print(&skinfo->rtcp_addr_name, addr_buf,
+				sizeof(addr_buf), 3)));
+
+    acc->next_rtp_port += 2;
+    return PJ_SUCCESS;
+
+on_error:
+    for (i=0; i<2; ++i) {
+	if (sock[i] != PJ_INVALID_SOCKET)
+	    pj_sock_close(sock[i]);
+    }
+    return status;
+}
+
+/* Create normal UDP media transports */
+static pj_status_t create_udp_media_transport(const pjsua_transport_config *cfg,
+					      pjsua_call_media *call_med)
+{
+    pjmedia_sock_info skinfo;
+    pj_status_t status;
+
+    status = create_rtp_rtcp_sock(call_med, cfg, &skinfo);
+    if (status != PJ_SUCCESS) {
+	pjsua_perror(THIS_FILE, "Unable to create RTP/RTCP socket",
+		     status);
+	goto on_error;
+    }
+
+    status = pjmedia_transport_udp_attach(pjsua_var.med_endpt, NULL,
+					  &skinfo, 0, &call_med->tp);
+    if (status != PJ_SUCCESS) {
+	pjsua_perror(THIS_FILE, "Unable to create media transport",
+		     status);
+	goto on_error;
+    }
+
+    pjmedia_transport_simulate_lost(call_med->tp, PJMEDIA_DIR_ENCODING,
+				    pjsua_var.media_cfg.tx_drop_pct);
+
+    pjmedia_transport_simulate_lost(call_med->tp, PJMEDIA_DIR_DECODING,
+				    pjsua_var.media_cfg.rx_drop_pct);
+
+    call_med->tp_ready = PJ_SUCCESS;
+
+    return PJ_SUCCESS;
+
+on_error:
+    if (call_med->tp)
+	pjmedia_transport_close(call_med->tp);
+
+    return status;
+}
+
+#if DISABLED_FOR_TICKET_1185
+/* Create normal UDP media transports */
+static pj_status_t create_udp_media_transports(pjsua_transport_config *cfg)
+{
+    unsigned i;
+    pj_status_t status;
+
+    for (i=0; i < pjsua_var.ua_cfg.max_calls; ++i) {
+	pjsua_call *call = &pjsua_var.calls[i];
+	unsigned strm_idx;
+
+	for (strm_idx=0; strm_idx < call->med_cnt; ++strm_idx) {
+	    pjsua_call_media *call_med = &call->media[strm_idx];
+
+	    status = create_udp_media_transport(cfg, &call_med->tp);
+	    if (status != PJ_SUCCESS)
+		goto on_error;
+	}
+    }
+
+    return PJ_SUCCESS;
+
+on_error:
+    for (i=0; i < pjsua_var.ua_cfg.max_calls; ++i) {
+	pjsua_call *call = &pjsua_var.calls[i];
+	unsigned strm_idx;
+
+	for (strm_idx=0; strm_idx < call->med_cnt; ++strm_idx) {
+	    pjsua_call_media *call_med = &call->media[strm_idx];
+
+	    if (call_med->tp) {
+		pjmedia_transport_close(call_med->tp);
+		call_med->tp = NULL;
+	    }
+	}
+    }
+    return status;
+}
+#endif
+
+/* Deferred callback to notify ICE init complete */
+static void ice_init_complete_cb(void *user_data)
+{
+    pjsua_call_media *call_med = (pjsua_call_media*)user_data;
+
+    if (call_med->call == NULL)
+	return;
+
+    /* No need to acquire_call() if we only change the tp_ready flag
+     * (i.e. transport is being created synchronously). Otherwise
+     * calling acquire_call() here may cause deadlock. See
+     * https://trac.pjsip.org/repos/ticket/1578
+     */
+    call_med->tp_ready = call_med->tp_result;
+
+    if (call_med->med_create_cb) {
+	pjsua_call *call = NULL;
+	pjsip_dialog *dlg = NULL;
+
+	if (acquire_call("ice_init_complete_cb", call_med->call->index,
+	                 &call, &dlg) != PJ_SUCCESS)
+	{
+	    /* Call have been terminated */
+	    return;
+	}
+
+        (*call_med->med_create_cb)(call_med, call_med->tp_ready,
+                                   call_med->call->secure_level, NULL);
+
+        if (dlg)
+            pjsip_dlg_dec_lock(dlg);
+    }
+}
+
+/* Deferred callback to notify ICE negotiation failure */
+static void ice_failed_nego_cb(void *user_data)
+{
+    int call_id = (int)(pj_ssize_t)user_data;
+    pjsua_call *call = NULL;
+    pjsip_dialog *dlg = NULL;
+
+    if (acquire_call("ice_failed_nego_cb", call_id,
+                     &call, &dlg) != PJ_SUCCESS)
+    {
+	/* Call have been terminated */
+	return;
+    }
+
+    pjsua_var.ua_cfg.cb.on_call_media_state(call_id);
+
+    if (dlg)
+        pjsip_dlg_dec_lock(dlg);
+
+}
+
+/* This callback is called when ICE negotiation completes */
+static void on_ice_complete(pjmedia_transport *tp, 
+			    pj_ice_strans_op op,
+			    pj_status_t result)
+{
+    pjsua_call_media *call_med = (pjsua_call_media*)tp->user_data;
+    pjsua_call *call;
+
+    if (!call_med)
+	return;
+
+    call = call_med->call;
+    
+    switch (op) {
+    case PJ_ICE_STRANS_OP_INIT:
+        call_med->tp_result = result;
+        pjsua_schedule_timer2(&ice_init_complete_cb, call_med, 1);
+	break;
+    case PJ_ICE_STRANS_OP_NEGOTIATION:
+	if (result == PJ_SUCCESS) {
+            /* Update RTP address */
+            pjmedia_transport_info tpinfo;
+            pjmedia_transport_info_init(&tpinfo);
+            pjmedia_transport_get_info(call_med->tp, &tpinfo);
+            pj_sockaddr_cp(&call_med->rtp_addr, &tpinfo.sock_info.rtp_addr_name);
+        } else {
+	    call_med->state = PJSUA_CALL_MEDIA_ERROR;
+	    call_med->dir = PJMEDIA_DIR_NONE;
+	    if (call && pjsua_var.ua_cfg.cb.on_call_media_state) {
+		/* Defer the callback to a timer */
+		pjsua_schedule_timer2(&ice_failed_nego_cb,
+				      (void*)(pj_ssize_t)call->index, 1);
+	    }
+        }
+	/* Check if default ICE transport address is changed */
+        call->reinv_ice_sent = PJ_FALSE;
+	pjsua_call_schedule_reinvite_check(call, 0);
+	break;
+    case PJ_ICE_STRANS_OP_KEEP_ALIVE:
+	if (result != PJ_SUCCESS) {
+	    PJ_PERROR(4,(THIS_FILE, result,
+		         "ICE keep alive failure for transport %d:%d",
+		         call->index, call_med->idx));
+	}
+        if (pjsua_var.ua_cfg.cb.on_call_media_transport_state) {
+            pjsua_med_tp_state_info info;
+
+            pj_bzero(&info, sizeof(info));
+            info.med_idx = call_med->idx;
+            info.state = call_med->tp_st;
+            info.status = result;
+            info.ext_info = &op;
+	    (*pjsua_var.ua_cfg.cb.on_call_media_transport_state)(
+                call->index, &info);
+        }
+	if (pjsua_var.ua_cfg.cb.on_ice_transport_error) {
+	    pjsua_call_id id = call->index;
+	    (*pjsua_var.ua_cfg.cb.on_ice_transport_error)(id, op, result,
+							  NULL);
+	}
+	break;
+    }
+}
+
+
+/* Parse "HOST:PORT" format */
+static pj_status_t parse_host_port(const pj_str_t *host_port,
+				   pj_str_t *host, pj_uint16_t *port)
+{
+    pj_str_t str_port;
+
+    str_port.ptr = pj_strchr(host_port, ':');
+    if (str_port.ptr != NULL) {
+	int iport;
+
+	host->ptr = host_port->ptr;
+	host->slen = (str_port.ptr - host->ptr);
+	str_port.ptr++;
+	str_port.slen = host_port->slen - host->slen - 1;
+	iport = (int)pj_strtoul(&str_port);
+	if (iport < 1 || iport > 65535)
+	    return PJ_EINVAL;
+	*port = (pj_uint16_t)iport;
+    } else {
+	*host = *host_port;
+	*port = 0;
+    }
+
+    return PJ_SUCCESS;
+}
+
+/* Create ICE media transports (when ice is enabled) */
+static pj_status_t create_ice_media_transport(
+				const pjsua_transport_config *cfg,
+				pjsua_call_media *call_med,
+                                pj_bool_t async)
+{
+    char stunip[PJ_INET6_ADDRSTRLEN];
+    pjsua_acc_config *acc_cfg;
+    pj_ice_strans_cfg ice_cfg;
+    pjmedia_ice_cb ice_cb;
+    char name[32];
+    unsigned comp_cnt;
+    pj_status_t status;
+
+    acc_cfg = &pjsua_var.acc[call_med->call->acc_id].cfg;
+
+    /* Make sure STUN server resolution has completed */
+    status = resolve_stun_server(PJ_TRUE);
+    if (status != PJ_SUCCESS) {
+	pjsua_perror(THIS_FILE, "Error resolving STUN server", status);
+	return status;
+    }
+
+    /* Create ICE stream transport configuration */
+    pj_ice_strans_cfg_default(&ice_cfg);
+    pj_stun_config_init(&ice_cfg.stun_cfg, &pjsua_var.cp.factory, 0,
+		        pjsip_endpt_get_ioqueue(pjsua_var.endpt),
+			pjsip_endpt_get_timer_heap(pjsua_var.endpt));
+    
+    ice_cfg.af = pj_AF_INET();
+    ice_cfg.resolver = pjsua_var.resolver;
+    
+    ice_cfg.opt = acc_cfg->ice_cfg.ice_opt;
+
+    /* Configure STUN settings */
+    if (pj_sockaddr_has_addr(&pjsua_var.stun_srv)) {
+	pj_sockaddr_print(&pjsua_var.stun_srv, stunip, sizeof(stunip), 0);
+	ice_cfg.stun.server = pj_str(stunip);
+	ice_cfg.stun.port = pj_sockaddr_get_port(&pjsua_var.stun_srv);
+    }
+    if (acc_cfg->ice_cfg.ice_max_host_cands >= 0)
+	ice_cfg.stun.max_host_cands = acc_cfg->ice_cfg.ice_max_host_cands;
+
+    /* Copy binding port setting to STUN setting */
+    pj_sockaddr_init(ice_cfg.af, &ice_cfg.stun.cfg.bound_addr,
+		     &cfg->bound_addr, (pj_uint16_t)cfg->port);
+    ice_cfg.stun.cfg.port_range = (pj_uint16_t)cfg->port_range;
+    if (cfg->port != 0 && ice_cfg.stun.cfg.port_range == 0)
+	ice_cfg.stun.cfg.port_range = 
+				 (pj_uint16_t)(pjsua_var.ua_cfg.max_calls * 10);
+
+    /* Copy QoS setting to STUN setting */
+    ice_cfg.stun.cfg.qos_type = cfg->qos_type;
+    pj_memcpy(&ice_cfg.stun.cfg.qos_params, &cfg->qos_params,
+	      sizeof(cfg->qos_params));
+
+    /* Configure TURN settings */
+    if (acc_cfg->turn_cfg.enable_turn) {
+	status = parse_host_port(&acc_cfg->turn_cfg.turn_server,
+				 &ice_cfg.turn.server,
+				 &ice_cfg.turn.port);
+	if (status != PJ_SUCCESS || ice_cfg.turn.server.slen == 0) {
+	    PJ_LOG(1,(THIS_FILE, "Invalid TURN server setting"));
+	    return PJ_EINVAL;
+	}
+	if (ice_cfg.turn.port == 0)
+	    ice_cfg.turn.port = 3479;
+	ice_cfg.turn.conn_type = acc_cfg->turn_cfg.turn_conn_type;
+	pj_memcpy(&ice_cfg.turn.auth_cred, 
+		  &acc_cfg->turn_cfg.turn_auth_cred,
+		  sizeof(ice_cfg.turn.auth_cred));
+
+	/* Copy QoS setting to TURN setting */
+	ice_cfg.turn.cfg.qos_type = cfg->qos_type;
+	pj_memcpy(&ice_cfg.turn.cfg.qos_params, &cfg->qos_params,
+		  sizeof(cfg->qos_params));
+
+	/* Copy binding port setting to TURN setting */
+	pj_sockaddr_init(ice_cfg.af, &ice_cfg.turn.cfg.bound_addr,
+			 &cfg->bound_addr, (pj_uint16_t)cfg->port);
+	ice_cfg.turn.cfg.port_range = (pj_uint16_t)cfg->port_range;
+	if (cfg->port != 0 && ice_cfg.turn.cfg.port_range == 0)
+	    ice_cfg.turn.cfg.port_range = 
+				 (pj_uint16_t)(pjsua_var.ua_cfg.max_calls * 10);
+    }
+
+    /* Configure packet size for STUN and TURN sockets */
+    ice_cfg.stun.cfg.max_pkt_size = PJMEDIA_MAX_MRU;
+    ice_cfg.turn.cfg.max_pkt_size = PJMEDIA_MAX_MRU;
+
+    pj_bzero(&ice_cb, sizeof(pjmedia_ice_cb));
+    ice_cb.on_ice_complete = &on_ice_complete;
+    pj_ansi_snprintf(name, sizeof(name), "icetp%02d", call_med->idx);
+    call_med->tp_ready = PJ_EPENDING;
+
+    comp_cnt = 1;
+    if (PJMEDIA_ADVERTISE_RTCP && !acc_cfg->ice_cfg.ice_no_rtcp)
+	++comp_cnt;
+
+    status = pjmedia_ice_create3(pjsua_var.med_endpt, name, comp_cnt,
+				 &ice_cfg, &ice_cb, 0, call_med,
+				 &call_med->tp);
+    if (status != PJ_SUCCESS) {
+	pjsua_perror(THIS_FILE, "Unable to create ICE media transport",
+		     status);
+	goto on_error;
+    }
+
+    /* Wait until transport is initialized, or time out */
+    if (!async) {
+	pj_bool_t has_pjsua_lock = PJSUA_LOCK_IS_LOCKED();
+        if (has_pjsua_lock)
+	    PJSUA_UNLOCK();
+        while (call_med->tp_ready == PJ_EPENDING) {
+	    pjsua_handle_events(100);
+        }
+	if (has_pjsua_lock)
+	    PJSUA_LOCK();
+    }
+
+    if (async && call_med->tp_ready == PJ_EPENDING) {
+        return PJ_EPENDING;
+    } else if (call_med->tp_ready != PJ_SUCCESS) {
+	pjsua_perror(THIS_FILE, "Error initializing ICE media transport",
+		     call_med->tp_ready);
+	status = call_med->tp_ready;
+	goto on_error;
+    }
+
+    pjmedia_transport_simulate_lost(call_med->tp, PJMEDIA_DIR_ENCODING,
+				    pjsua_var.media_cfg.tx_drop_pct);
+
+    pjmedia_transport_simulate_lost(call_med->tp, PJMEDIA_DIR_DECODING,
+				    pjsua_var.media_cfg.rx_drop_pct);
+    
+    return PJ_SUCCESS;
+
+on_error:
+    if (call_med->tp != NULL) {
+	pjmedia_transport_close(call_med->tp);
+	call_med->tp = NULL;
+    }
+
+    return status;
+}
+
+#if DISABLED_FOR_TICKET_1185
+/* Create ICE media transports (when ice is enabled) */
+static pj_status_t create_ice_media_transports(pjsua_transport_config *cfg)
+{
+    unsigned i;
+    pj_status_t status;
+
+    for (i=0; i < pjsua_var.ua_cfg.max_calls; ++i) {
+	pjsua_call *call = &pjsua_var.calls[i];
+	unsigned strm_idx;
+
+	for (strm_idx=0; strm_idx < call->med_cnt; ++strm_idx) {
+	    pjsua_call_media *call_med = &call->media[strm_idx];
+
+	    status = create_ice_media_transport(cfg, call_med);
+	    if (status != PJ_SUCCESS)
+		goto on_error;
+	}
+    }
+
+    return PJ_SUCCESS;
+
+on_error:
+    for (i=0; i < pjsua_var.ua_cfg.max_calls; ++i) {
+	pjsua_call *call = &pjsua_var.calls[i];
+	unsigned strm_idx;
+
+	for (strm_idx=0; strm_idx < call->med_cnt; ++strm_idx) {
+	    pjsua_call_media *call_med = &call->media[strm_idx];
+
+	    if (call_med->tp) {
+		pjmedia_transport_close(call_med->tp);
+		call_med->tp = NULL;
+	    }
+	}
+    }
+    return status;
+}
+#endif
+
+#if DISABLED_FOR_TICKET_1185
+/*
+ * Create media transports for all the calls. This function creates
+ * one UDP media transport for each call.
+ */
+PJ_DEF(pj_status_t) pjsua_media_transports_create(
+			const pjsua_transport_config *app_cfg)
+{
+    pjsua_transport_config cfg;
+    unsigned i;
+    pj_status_t status;
+
+
+    /* Make sure pjsua_init() has been called */
+    PJ_ASSERT_RETURN(pjsua_var.ua_cfg.max_calls>0, PJ_EINVALIDOP);
+
+    PJSUA_LOCK();
+
+    /* Delete existing media transports */
+    for (i=0; i<pjsua_var.ua_cfg.max_calls; ++i) {
+	pjsua_call *call = &pjsua_var.calls[i];
+	unsigned strm_idx;
+
+	for (strm_idx=0; strm_idx < call->med_cnt; ++strm_idx) {
+	    pjsua_call_media *call_med = &call->media[strm_idx];
+
+	    if (call_med->tp && call_med->tp_auto_del) {
+		pjmedia_transport_close(call_med->tp);
+		call_med->tp = NULL;
+		call_med->tp_orig = NULL;
+	    }
+	}
+    }
+
+    /* Copy config */
+    pjsua_transport_config_dup(pjsua_var.pool, &cfg, app_cfg);
+
+    /* Create the transports */
+    if (pjsua_var.ice_cfg.enable_ice) {
+	status = create_ice_media_transports(&cfg);
+    } else {
+	status = create_udp_media_transports(&cfg);
+    }
+
+    /* Set media transport auto_delete to True */
+    for (i=0; i<pjsua_var.ua_cfg.max_calls; ++i) {
+	pjsua_call *call = &pjsua_var.calls[i];
+	unsigned strm_idx;
+
+	for (strm_idx=0; strm_idx < call->med_cnt; ++strm_idx) {
+	    pjsua_call_media *call_med = &call->media[strm_idx];
+
+	    call_med->tp_auto_del = PJ_TRUE;
+	}
+    }
+
+    PJSUA_UNLOCK();
+
+    return status;
+}
+
+/*
+ * Attach application's created media transports.
+ */
+PJ_DEF(pj_status_t) pjsua_media_transports_attach(pjsua_media_transport tp[],
+						  unsigned count,
+						  pj_bool_t auto_delete)
+{
+    unsigned i;
+
+    PJ_ASSERT_RETURN(tp && count==pjsua_var.ua_cfg.max_calls, PJ_EINVAL);
+
+    /* Assign the media transports */
+    for (i=0; i<pjsua_var.ua_cfg.max_calls; ++i) {
+	pjsua_call *call = &pjsua_var.calls[i];
+	unsigned strm_idx;
+
+	for (strm_idx=0; strm_idx < call->med_cnt; ++strm_idx) {
+	    pjsua_call_media *call_med = &call->media[strm_idx];
+
+	    if (call_med->tp && call_med->tp_auto_del) {
+		pjmedia_transport_close(call_med->tp);
+		call_med->tp = NULL;
+		call_med->tp_orig = NULL;
+	    }
+	}
+
+	PJ_TODO(remove_pjsua_media_transports_attach);
+
+	call->media[0].tp = tp[i].transport;
+	call->media[0].tp_auto_del = auto_delete;
+    }
+
+    return PJ_SUCCESS;
+}
+#endif
+
+/* Go through the list of media in the SDP, find acceptable media, and
+ * sort them based on the "quality" of the media, and store the indexes
+ * in the specified array. Media with the best quality will be listed
+ * first in the array. The quality factors considered currently is
+ * encryption.
+ */
+static void sort_media(const pjmedia_sdp_session *sdp,
+		       const pj_str_t *type,
+		       pjmedia_srtp_use	use_srtp,
+		       pj_uint8_t midx[],
+		       unsigned *p_count,
+		       unsigned *p_total_count)
+{
+    unsigned i;
+    unsigned count = 0;
+    int score[PJSUA_MAX_CALL_MEDIA];
+
+    pj_assert(*p_count >= PJSUA_MAX_CALL_MEDIA);
+    pj_assert(*p_total_count >= PJSUA_MAX_CALL_MEDIA);
+
+    *p_count = 0;
+    *p_total_count = 0;
+    for (i=0; i<PJSUA_MAX_CALL_MEDIA; ++i)
+	score[i] = 1;
+
+    /* Score each media */
+    for (i=0; i<sdp->media_count && count<PJSUA_MAX_CALL_MEDIA; ++i) {
+	const pjmedia_sdp_media *m = sdp->media[i];
+	const pjmedia_sdp_conn *c;
+
+	/* Skip different media */
+	if (pj_stricmp(&m->desc.media, type) != 0) {
+	    score[count++] = -22000;
+	    continue;
+	}
+
+	c = m->conn? m->conn : sdp->conn;
+
+	/* Supported transports */
+	if (pj_stricmp2(&m->desc.transport, "RTP/SAVP")==0) {
+	    switch (use_srtp) {
+	    case PJMEDIA_SRTP_MANDATORY:
+	    case PJMEDIA_SRTP_OPTIONAL:
+		++score[i];
+		break;
+	    case PJMEDIA_SRTP_DISABLED:
+		//--score[i];
+		score[i] -= 5;
+		break;
+	    }
+	} else if (pj_stricmp2(&m->desc.transport, "RTP/AVP")==0) {
+	    switch (use_srtp) {
+	    case PJMEDIA_SRTP_MANDATORY:
+		//--score[i];
+		score[i] -= 5;
+		break;
+	    case PJMEDIA_SRTP_OPTIONAL:
+		/* No change in score */
+		break;
+	    case PJMEDIA_SRTP_DISABLED:
+		++score[i];
+		break;
+	    }
+	} else {
+	    score[i] -= 10;
+	}
+
+	/* Is media disabled? */
+	if (m->desc.port == 0)
+	    score[i] -= 10;
+
+	/* Is media inactive? */
+	if (pjmedia_sdp_media_find_attr2(m, "inactive", NULL) ||
+	    pj_strcmp2(&c->addr, "0.0.0.0") == 0)
+	{
+	    //score[i] -= 10;
+	    score[i] -= 1;
+	}
+
+	++count;
+    }
+
+    /* Created sorted list based on quality */
+    for (i=0; i<count; ++i) {
+	unsigned j;
+	int best = 0;
+
+	for (j=1; j<count; ++j) {
+	    if (score[j] > score[best])
+		best = j;
+	}
+	/* Don't put media with negative score, that media is unacceptable
+	 * for us.
+	 */
+	midx[i] = (pj_uint8_t)best;
+	if (score[best] >= 0)
+	    (*p_count)++;
+	if (score[best] > -22000)
+	    (*p_total_count)++;
+
+	score[best] = -22000;
+
+    }
+}
+
+/* Callback to receive media events */
+pj_status_t call_media_on_event(pjmedia_event *event,
+                                void *user_data)
+{
+    pjsua_call_media *call_med = (pjsua_call_media*)user_data;
+    pjsua_call *call = call_med->call;
+    pj_status_t status = PJ_SUCCESS;
+  
+    switch(event->type) {
+	case PJMEDIA_EVENT_KEYFRAME_MISSING:
+	    if (call->opt.req_keyframe_method & PJSUA_VID_REQ_KEYFRAME_SIP_INFO)
+	    {
+		pj_timestamp now;
+
+		pj_get_timestamp(&now);
+		if (pj_elapsed_msec(&call_med->last_req_keyframe, &now) >=
+		    PJSUA_VID_REQ_KEYFRAME_INTERVAL)
+		{
+		    pjsua_msg_data msg_data;
+		    const pj_str_t SIP_INFO = {"INFO", 4};
+		    const char *BODY_TYPE = "application/media_control+xml";
+		    const char *BODY =
+			"<?xml version=\"1.0\" encoding=\"utf-8\" ?>"
+			"<media_control><vc_primitive><to_encoder>"
+			"<picture_fast_update/>"
+			"</to_encoder></vc_primitive></media_control>";
+
+		    PJ_LOG(4,(THIS_FILE, 
+			      "Sending video keyframe request via SIP INFO"));
+
+		    pjsua_msg_data_init(&msg_data);
+		    pj_cstr(&msg_data.content_type, BODY_TYPE);
+		    pj_cstr(&msg_data.msg_body, BODY);
+		    status = pjsua_call_send_request(call->index, &SIP_INFO, 
+						     &msg_data);
+		    if (status != PJ_SUCCESS) {
+			pj_perror(3, THIS_FILE, status,
+				  "Failed requesting keyframe via SIP INFO");
+		    } else {
+			call_med->last_req_keyframe = now;
+		    }
+		}
+	    }
+	    break;
+
+	default:
+	    break;
+    }
+
+    if (pjsua_var.ua_cfg.cb.on_call_media_event && call) {
+	(*pjsua_var.ua_cfg.cb.on_call_media_event)(call->index,
+						   call_med->idx, event);
+    }
+
+    return status;
+}
+
+/* Set media transport state and notify the application via the callback. */
+void pjsua_set_media_tp_state(pjsua_call_media *call_med,
+                              pjsua_med_tp_st tp_st)
+{
+    if (pjsua_var.ua_cfg.cb.on_call_media_transport_state &&
+        call_med->tp_st != tp_st)
+    {
+        pjsua_med_tp_state_info info;
+
+        pj_bzero(&info, sizeof(info));
+        info.med_idx = call_med->idx;
+        info.state = tp_st;
+        info.status = call_med->tp_ready;
+	(*pjsua_var.ua_cfg.cb.on_call_media_transport_state)(
+            call_med->call->index, &info);
+    }
+
+    call_med->tp_st = tp_st;
+}
+
+/* Callback to resume pjsua_call_media_init() after media transport
+ * creation is completed.
+ */
+static pj_status_t call_media_init_cb(pjsua_call_media *call_med,
+                                      pj_status_t status,
+                                      int security_level,
+                                      int *sip_err_code)
+{
+    pjsua_acc *acc = &pjsua_var.acc[call_med->call->acc_id];
+    pjmedia_transport_info tpinfo;
+    int err_code = 0;
+
+    if (status != PJ_SUCCESS)
+        goto on_return;
+
+    pjmedia_transport_simulate_lost(call_med->tp, PJMEDIA_DIR_ENCODING,
+				    pjsua_var.media_cfg.tx_drop_pct);
+
+    pjmedia_transport_simulate_lost(call_med->tp, PJMEDIA_DIR_DECODING,
+				    pjsua_var.media_cfg.rx_drop_pct);
+
+    if (call_med->tp_st == PJSUA_MED_TP_CREATING)
+        pjsua_set_media_tp_state(call_med, PJSUA_MED_TP_IDLE);
+
+    if (!call_med->tp_orig &&
+        pjsua_var.ua_cfg.cb.on_create_media_transport)
+    {
+        call_med->use_custom_med_tp = PJ_TRUE;
+    } else
+        call_med->use_custom_med_tp = PJ_FALSE;
+
+#if defined(PJMEDIA_HAS_SRTP) && (PJMEDIA_HAS_SRTP != 0)
+    /* This function may be called when SRTP transport already exists
+     * (e.g: in re-invite, update), don't need to destroy/re-create.
+     */
+    if (!call_med->tp_orig) {
+	pjmedia_srtp_setting srtp_opt;
+	pjmedia_transport *srtp = NULL;
+
+	/* Check if SRTP requires secure signaling */
+	if (acc->cfg.use_srtp != PJMEDIA_SRTP_DISABLED) {
+	    if (security_level < acc->cfg.srtp_secure_signaling) {
+		err_code = PJSIP_SC_NOT_ACCEPTABLE;
+		status = PJSIP_ESESSIONINSECURE;
+		goto on_return;
+	    }
+	}
+
+	/* Always create SRTP adapter */
+	pjmedia_srtp_setting_default(&srtp_opt);
+	srtp_opt.close_member_tp = PJ_TRUE;
+
+	/* If media session has been ever established, let's use remote's 
+	 * preference in SRTP usage policy, especially when it is stricter.
+	 */
+	if (call_med->rem_srtp_use > acc->cfg.use_srtp)
+	    srtp_opt.use = call_med->rem_srtp_use;
+	else
+	    srtp_opt.use = acc->cfg.use_srtp;
+
+	status = pjmedia_transport_srtp_create(pjsua_var.med_endpt,
+					       call_med->tp,
+					       &srtp_opt, &srtp);
+	if (status != PJ_SUCCESS) {
+	    err_code = PJSIP_SC_INTERNAL_SERVER_ERROR;
+	    goto on_return;
+	}
+
+	/* Set SRTP as current media transport */
+	call_med->tp_orig = call_med->tp;
+	call_med->tp = srtp;
+    }
+#else
+    call_med->tp_orig = call_med->tp;
+    PJ_UNUSED_ARG(security_level);
+#endif
+
+
+    pjmedia_transport_info_init(&tpinfo);
+    pjmedia_transport_get_info(call_med->tp, &tpinfo);
+
+    pj_sockaddr_cp(&call_med->rtp_addr, &tpinfo.sock_info.rtp_addr_name);
+
+
+on_return:
+    if (status != PJ_SUCCESS && call_med->tp) {
+	pjsua_set_media_tp_state(call_med, PJSUA_MED_TP_NULL);
+	pjmedia_transport_close(call_med->tp);
+	call_med->tp = NULL;
+    }
+
+    if (sip_err_code)
+        *sip_err_code = err_code;
+
+    if (call_med->med_init_cb) {
+        pjsua_med_tp_state_info info;
+
+        pj_bzero(&info, sizeof(info));
+        info.status = status;
+        info.state = call_med->tp_st;
+        info.med_idx = call_med->idx;
+        info.sip_err_code = err_code;
+        (*call_med->med_init_cb)(call_med->call->index, &info);
+    }
+
+    return status;
+}
+
+/* Initialize the media line */
+pj_status_t pjsua_call_media_init(pjsua_call_media *call_med,
+                                  pjmedia_type type,
+				  const pjsua_transport_config *tcfg,
+				  int security_level,
+				  int *sip_err_code,
+                                  pj_bool_t async,
+                                  pjsua_med_tp_state_cb cb)
+{
+    pj_status_t status = PJ_SUCCESS;
+
+    /*
+     * Note: this function may be called when the media already exists
+     * (e.g. in reinvites, updates, etc.)
+     */
+    call_med->type = type;
+
+    /* Create the media transport for initial call. Here are the possible
+     * media transport state and the action needed:
+     * - PJSUA_MED_TP_NULL or call_med->tp==NULL, create one.
+     * - PJSUA_MED_TP_RUNNING, do nothing.
+     * - PJSUA_MED_TP_DISABLED, re-init (media_create(), etc). Currently,
+     *   this won't happen as media_channel_update() will always clean up
+     *   the unused transport of a disabled media.
+     */
+    if (call_med->tp == NULL) {
+#if defined(PJMEDIA_HAS_VIDEO) && (PJMEDIA_HAS_VIDEO != 0)
+	/* While in initial call, set default video devices */
+	if (type == PJMEDIA_TYPE_VIDEO) {
+	    status = pjsua_vid_channel_init(call_med);
+	    if (status != PJ_SUCCESS)
+		return status;
+	}
+#endif
+
+        pjsua_set_media_tp_state(call_med, PJSUA_MED_TP_CREATING);
+
+	if (pjsua_var.acc[call_med->call->acc_id].cfg.ice_cfg.enable_ice) {
+	    status = create_ice_media_transport(tcfg, call_med, async);
+            if (async && status == PJ_EPENDING) {
+	        /* We will resume call media initialization in the
+	         * on_ice_complete() callback.
+	         */
+                call_med->med_create_cb = &call_media_init_cb;
+                call_med->med_init_cb = cb;
+                
+	        return PJ_EPENDING;
+	    }
+	} else {
+	    status = create_udp_media_transport(tcfg, call_med);
+	}
+
+        if (status != PJ_SUCCESS) {
+	    PJ_PERROR(1,(THIS_FILE, status, "Error creating media transport"));
+	    return status;
+	}
+
+        /* Media transport creation completed immediately, so 
+         * we don't need to call the callback.
+         */
+        call_med->med_init_cb = NULL;
+
+    } else if (call_med->tp_st == PJSUA_MED_TP_DISABLED) {
+	/* Media is being reenabled. */
+	//pjsua_set_media_tp_state(call_med, PJSUA_MED_TP_IDLE);
+
+	pj_assert(!"Currently no media transport reuse");
+    }
+
+    return call_media_init_cb(call_med, status, security_level,
+                              sip_err_code);
+}
+
+/* Callback to resume pjsua_media_channel_init() after media transport
+ * initialization is completed.
+ */
+static pj_status_t media_channel_init_cb(pjsua_call_id call_id,
+                                         const pjsua_med_tp_state_info *info)
+{
+    pjsua_call *call = &pjsua_var.calls[call_id];
+    pj_status_t status = (info? info->status : PJ_SUCCESS);
+    unsigned mi;
+
+    if (info) {
+        pj_mutex_lock(call->med_ch_mutex);
+
+        /* Set the callback to NULL to indicate that the async operation
+         * has completed.
+         */
+        call->media_prov[info->med_idx].med_init_cb = NULL;
+
+        /* In case of failure, save the information to be returned
+         * by the last media transport to finish.
+         */
+        if (info->status != PJ_SUCCESS)
+            pj_memcpy(&call->med_ch_info, info, sizeof(info));
+
+        /* Check whether all the call's medias have finished calling their
+         * callbacks.
+         */
+        for (mi=0; mi < call->med_prov_cnt; ++mi) {
+            pjsua_call_media *call_med = &call->media_prov[mi];
+
+            if (call_med->med_init_cb) {
+                pj_mutex_unlock(call->med_ch_mutex);
+                return PJ_SUCCESS;
+            }
+
+            if (call_med->tp_ready != PJ_SUCCESS)
+                status = call_med->tp_ready;
+        }
+
+        /* OK, we are called by the last media transport finished. */
+        pj_mutex_unlock(call->med_ch_mutex);
+    }
+
+    if (call->med_ch_mutex) {
+        pj_mutex_destroy(call->med_ch_mutex);
+        call->med_ch_mutex = NULL;
+    }
+
+    if (status != PJ_SUCCESS) {
+	if (call->med_ch_info.status == PJ_SUCCESS) {
+	    call->med_ch_info.status = status;
+	    call->med_ch_info.sip_err_code = PJSIP_SC_TEMPORARILY_UNAVAILABLE;
+	}
+	pjsua_media_prov_clean_up(call_id);
+        goto on_return;
+    }
+
+    /* Tell the media transport of a new offer/answer session */
+    for (mi=0; mi < call->med_prov_cnt; ++mi) {
+	pjsua_call_media *call_med = &call->media_prov[mi];
+
+	/* Note: tp may be NULL if this media line is disabled */
+	if (call_med->tp && call_med->tp_st == PJSUA_MED_TP_IDLE) {
+            pj_pool_t *tmp_pool = call->async_call.pool_prov;
+            
+            if (!tmp_pool) {
+                tmp_pool = (call->inv? call->inv->pool_prov:
+                            call->async_call.dlg->pool);
+            }
+
+            if (call_med->use_custom_med_tp) {
+                unsigned custom_med_tp_flags = PJSUA_MED_TP_CLOSE_MEMBER;
+
+                /* Use custom media transport returned by the application */
+                call_med->tp =
+                    (*pjsua_var.ua_cfg.cb.on_create_media_transport)
+                        (call_id, mi, call_med->tp,
+                         custom_med_tp_flags);
+                if (!call_med->tp) {
+                    status =
+                        PJSIP_ERRNO_FROM_SIP_STATUS(PJSIP_SC_TEMPORARILY_UNAVAILABLE);
+                }
+            }
+
+            if (call_med->tp) {
+                status = pjmedia_transport_media_create(
+                             call_med->tp, tmp_pool,
+                             0, call->async_call.rem_sdp, mi);
+            }
+	    if (status != PJ_SUCCESS) {
+                call->med_ch_info.status = status;
+                call->med_ch_info.med_idx = mi;
+                call->med_ch_info.state = call_med->tp_st;
+                call->med_ch_info.sip_err_code = PJSIP_SC_TEMPORARILY_UNAVAILABLE;
+		pjsua_media_prov_clean_up(call_id);
+		goto on_return;
+	    }
+
+	    pjsua_set_media_tp_state(call_med, PJSUA_MED_TP_INIT);
+	}
+    }
+
+    call->med_ch_info.status = PJ_SUCCESS;
+
+on_return:
+    if (call->med_ch_cb)
+        (*call->med_ch_cb)(call->index, &call->med_ch_info);
+
+    return status;
+}
+
+
+/* Clean up media transports in provisional media that is not used
+ * by call media.
+ */
+static void media_prov_clean_up(pjsua_call_id call_id, int idx)
+{
+    pjsua_call *call = &pjsua_var.calls[call_id];
+    unsigned i;
+
+    for (i = idx; i < call->med_prov_cnt; ++i) {
+	pjsua_call_media *call_med = &call->media_prov[i];
+	unsigned j;
+	pj_bool_t used = PJ_FALSE;
+
+	if (call_med->tp == NULL)
+	    continue;
+
+	for (j = 0; j < call->med_cnt; ++j) {
+	    if (call->media[j].tp == call_med->tp) {
+		used = PJ_TRUE;
+		break;
+	    }
+	}
+
+	if (!used) {
+	    if (call_med->tp_st > PJSUA_MED_TP_IDLE) {
+		pjsua_set_media_tp_state(call_med, PJSUA_MED_TP_IDLE);
+		pjmedia_transport_media_stop(call_med->tp);
+	    }
+	    pjsua_set_media_tp_state(call_med, PJSUA_MED_TP_NULL);
+	    pjmedia_transport_close(call_med->tp);
+	    call_med->tp = call_med->tp_orig = NULL;
+	}
+    }
+}
+
+void pjsua_media_prov_clean_up(pjsua_call_id call_id)
+{
+    media_prov_clean_up(call_id, 0);
+}
+
+
+pj_status_t pjsua_media_channel_init(pjsua_call_id call_id,
+				     pjsip_role_e role,
+				     int security_level,
+				     pj_pool_t *tmp_pool,
+				     const pjmedia_sdp_session *rem_sdp,
+				     int *sip_err_code,
+                                     pj_bool_t async,
+                                     pjsua_med_tp_state_cb cb)
+{
+    const pj_str_t STR_AUDIO = { "audio", 5 };
+    const pj_str_t STR_VIDEO = { "video", 5 };
+    pjsua_call *call = &pjsua_var.calls[call_id];
+    pjsua_acc *acc = &pjsua_var.acc[call->acc_id];
+    pj_uint8_t maudidx[PJSUA_MAX_CALL_MEDIA];
+    unsigned maudcnt = PJ_ARRAY_SIZE(maudidx);
+    unsigned mtotaudcnt = PJ_ARRAY_SIZE(maudidx);
+    pj_uint8_t mvididx[PJSUA_MAX_CALL_MEDIA];
+    unsigned mvidcnt = PJ_ARRAY_SIZE(mvididx);
+    unsigned mtotvidcnt = PJ_ARRAY_SIZE(mvididx);
+    unsigned mi;
+    pj_bool_t pending_med_tp = PJ_FALSE;
+    pj_bool_t reinit = PJ_FALSE;
+    pj_status_t status;
+
+    PJ_UNUSED_ARG(role);
+
+    /*
+     * Note: this function may be called when the media already exists
+     * (e.g. in reinvites, updates, etc).
+     */
+
+    if (pjsua_get_state() != PJSUA_STATE_RUNNING) {
+        if (sip_err_code) *sip_err_code = PJSIP_SC_SERVICE_UNAVAILABLE;
+	return PJ_EBUSY;
+    }
+
+    if (async) {
+        pj_pool_t *tmppool = (call->inv? call->inv->pool_prov:
+                              call->async_call.dlg->pool);
+
+        status = pj_mutex_create_simple(tmppool, NULL, &call->med_ch_mutex);
+        if (status != PJ_SUCCESS)
+            return status;
+    }
+
+    if (call->inv && call->inv->state == PJSIP_INV_STATE_CONFIRMED)
+	reinit = PJ_TRUE;
+
+    PJ_LOG(4,(THIS_FILE, "Call %d: %sinitializing media..",
+			 call_id, (reinit?"re-":"") ));
+
+    pj_log_push_indent();
+
+    /* Init provisional media state */
+    if (call->med_cnt == 0) {
+	/* New media session, just copy whole from call media state. */
+	pj_memcpy(call->media_prov, call->media, sizeof(call->media));
+    } else {
+	/* Clean up any unused transports. Note that when local SDP reoffer
+	 * is rejected by remote, there may be any initialized transports that
+	 * are not used by call media and currently there is no notification
+	 * from PJSIP level regarding the reoffer rejection.
+	 */
+	pjsua_media_prov_clean_up(call_id);
+
+	/* Updating media session, copy from call media state. */
+	pj_memcpy(call->media_prov, call->media,
+		  sizeof(call->media[0]) * call->med_cnt);
+    }
+    call->med_prov_cnt = call->med_cnt;
+
+#if DISABLED_FOR_TICKET_1185
+    /* Return error if media transport has not been created yet
+     * (e.g. application is starting)
+     */
+    for (i=0; i<call->med_cnt; ++i) {
+	if (call->media[i].tp == NULL) {
+	    status = PJ_EBUSY;
+	    goto on_error;
+	}
+    }
+#endif
+
+    /* Get media count for each media type */
+    if (rem_sdp) {
+	sort_media(rem_sdp, &STR_AUDIO, acc->cfg.use_srtp,
+		   maudidx, &maudcnt, &mtotaudcnt);
+	if (maudcnt==0) {
+	    /* Expecting audio in the offer */
+	    if (sip_err_code) *sip_err_code = PJSIP_SC_NOT_ACCEPTABLE_HERE;
+	    status = PJSIP_ERRNO_FROM_SIP_STATUS(PJSIP_SC_NOT_ACCEPTABLE_HERE);
+	    goto on_error;
+	}
+
+#if PJMEDIA_HAS_VIDEO
+	sort_media(rem_sdp, &STR_VIDEO, acc->cfg.use_srtp,
+		   mvididx, &mvidcnt, &mtotvidcnt);
+#else
+	mvidcnt = mtotvidcnt = 0;
+	PJ_UNUSED_ARG(STR_VIDEO);
+#endif
+
+	/* Update media count only when remote add any media, this media count
+	 * must never decrease. Also note that we shouldn't apply the media
+	 * count setting (of the call setting) before the SDP negotiation.
+	 */
+	if (call->med_prov_cnt < rem_sdp->media_count)
+	    call->med_prov_cnt = PJ_MIN(rem_sdp->media_count,
+					PJSUA_MAX_CALL_MEDIA);
+
+	call->rem_offerer = PJ_TRUE;
+	call->rem_aud_cnt = maudcnt;
+	call->rem_vid_cnt = mvidcnt;
+
+    } else {
+
+	/* If call already established, calculate media count from current 
+	 * local active SDP and call setting. Otherwise, calculate media
+	 * count from the call setting only.
+	 */
+	if (reinit) {
+	    const pjmedia_sdp_session *sdp;
+
+	    status = pjmedia_sdp_neg_get_active_local(call->inv->neg, &sdp);
+	    pj_assert(status == PJ_SUCCESS);
+
+	    sort_media(sdp, &STR_AUDIO, acc->cfg.use_srtp,
+		       maudidx, &maudcnt, &mtotaudcnt);
+	    pj_assert(maudcnt > 0);
+
+	    sort_media(sdp, &STR_VIDEO, acc->cfg.use_srtp,
+		       mvididx, &mvidcnt, &mtotvidcnt);
+
+	    /* Call setting may add or remove media. Adding media is done by
+	     * enabling any disabled/port-zeroed media first, then adding new
+	     * media whenever needed. Removing media is done by disabling
+	     * media with the lowest 'quality'.
+	     */
+
+	    /* Check if we need to add new audio */
+	    if (maudcnt < call->opt.aud_cnt &&
+		mtotaudcnt < call->opt.aud_cnt)
+	    {
+		for (mi = 0; mi < call->opt.aud_cnt - mtotaudcnt; ++mi)
+		    maudidx[maudcnt++] = (pj_uint8_t)call->med_prov_cnt++;
+		
+		mtotaudcnt = call->opt.aud_cnt;
+	    }
+	    maudcnt = call->opt.aud_cnt;
+
+	    /* Check if we need to add new video */
+	    if (mvidcnt < call->opt.vid_cnt &&
+		mtotvidcnt < call->opt.vid_cnt)
+	    {
+		for (mi = 0; mi < call->opt.vid_cnt - mtotvidcnt; ++mi)
+		    mvididx[mvidcnt++] = (pj_uint8_t)call->med_prov_cnt++;
+
+		mtotvidcnt = call->opt.vid_cnt;
+	    }
+	    mvidcnt = call->opt.vid_cnt;
+
+	} else {
+
+	    maudcnt = mtotaudcnt = call->opt.aud_cnt;
+	    for (mi=0; mi<maudcnt; ++mi) {
+		maudidx[mi] = (pj_uint8_t)mi;
+	    }
+	    mvidcnt = mtotvidcnt = call->opt.vid_cnt;
+	    for (mi=0; mi<mvidcnt; ++mi) {
+		mvididx[mi] = (pj_uint8_t)(maudcnt + mi);
+	    }
+	    call->med_prov_cnt = maudcnt + mvidcnt;
+
+	    /* Need to publish supported media? */
+	    if (call->opt.flag & PJSUA_CALL_INCLUDE_DISABLED_MEDIA) {
+		if (mtotaudcnt == 0) {
+		    mtotaudcnt = 1;
+		    maudidx[0] = (pj_uint8_t)call->med_prov_cnt++;
+		}
+#if PJMEDIA_HAS_VIDEO
+		if (mtotvidcnt == 0) {
+		    mtotvidcnt = 1;
+		    mvididx[0] = (pj_uint8_t)call->med_prov_cnt++;
+		}
+#endif
+	    }
+	}
+
+	call->rem_offerer = PJ_FALSE;
+    }
+
+    if (call->med_prov_cnt == 0) {
+	/* Expecting at least one media */
+	if (sip_err_code) *sip_err_code = PJSIP_SC_NOT_ACCEPTABLE_HERE;
+	status = PJSIP_ERRNO_FROM_SIP_STATUS(PJSIP_SC_NOT_ACCEPTABLE_HERE);
+	goto on_error;
+    }
+
+    if (async) {
+        call->med_ch_cb = cb;
+    }
+
+    if (rem_sdp) {
+        call->async_call.rem_sdp =
+            pjmedia_sdp_session_clone(call->inv->pool_prov, rem_sdp);
+    } else {
+	call->async_call.rem_sdp = NULL;
+    }
+
+    call->async_call.pool_prov = tmp_pool;
+
+    /* Initialize each media line */
+    for (mi=0; mi < call->med_prov_cnt; ++mi) {
+	pjsua_call_media *call_med = &call->media_prov[mi];
+	pj_bool_t enabled = PJ_FALSE;
+	pjmedia_type media_type = PJMEDIA_TYPE_UNKNOWN;
+
+	if (pj_memchr(maudidx, mi, mtotaudcnt * sizeof(maudidx[0]))) {
+	    media_type = PJMEDIA_TYPE_AUDIO;
+	    if (call->opt.aud_cnt &&
+		pj_memchr(maudidx, mi, maudcnt * sizeof(maudidx[0])))
+	    {
+		enabled = PJ_TRUE;
+	    }
+	} else if (pj_memchr(mvididx, mi, mtotvidcnt * sizeof(mvididx[0]))) {
+	    media_type = PJMEDIA_TYPE_VIDEO;
+	    if (call->opt.vid_cnt &&
+		pj_memchr(mvididx, mi, mvidcnt * sizeof(mvididx[0])))
+	    {
+		enabled = PJ_TRUE;
+	    }
+	}
+
+	if (enabled) {
+	    status = pjsua_call_media_init(call_med, media_type,
+	                                   &acc->cfg.rtp_cfg,
+					   security_level, sip_err_code,
+                                           async,
+                                           (async? &media_channel_init_cb:
+                                            NULL));
+            if (status == PJ_EPENDING) {
+                pending_med_tp = PJ_TRUE;
+            } else if (status != PJ_SUCCESS) {
+                if (pending_med_tp) {
+                    /* Save failure information. */
+                    call_med->tp_ready = status;
+                    pj_bzero(&call->med_ch_info, sizeof(call->med_ch_info));
+                    call->med_ch_info.status = status;
+                    call->med_ch_info.state = call_med->tp_st;
+                    call->med_ch_info.med_idx = call_med->idx;
+                    if (sip_err_code)
+                        call->med_ch_info.sip_err_code = *sip_err_code;
+
+                    /* We will return failure in the callback later. */
+                    return PJ_EPENDING;
+                }
+
+                pjsua_media_prov_clean_up(call_id);
+		goto on_error;
+	    }
+	} else {
+	    /* By convention, the media is disabled if transport is NULL 
+	     * or transport state is PJSUA_MED_TP_DISABLED.
+	     */
+	    if (call_med->tp) {
+		// Don't close transport here, as SDP negotiation has not been
+		// done and stream may be still active. Once SDP negotiation
+		// is done (channel_update() invoked), this transport will be
+		// closed there.
+		//pjmedia_transport_close(call_med->tp);
+		//call_med->tp = NULL;
+		pj_assert(call_med->tp_st == PJSUA_MED_TP_INIT || 
+			  call_med->tp_st == PJSUA_MED_TP_RUNNING);
+		pjsua_set_media_tp_state(call_med, PJSUA_MED_TP_DISABLED);
+	    }
+
+	    /* Put media type just for info */
+	    call_med->type = media_type;
+	}
+    }
+
+    call->audio_idx = maudidx[0];
+
+    PJ_LOG(4,(THIS_FILE, "Media index %d selected for audio call %d",
+	      call->audio_idx, call->index));
+
+    if (pending_med_tp) {
+        /* We shouldn't use temporary pool anymore. */
+        call->async_call.pool_prov = NULL;
+        /* We have a pending media transport initialization. */
+        pj_log_pop_indent();
+        return PJ_EPENDING;
+    }
+
+    /* Media transport initialization completed immediately, so 
+     * we don't need to call the callback.
+     */
+    call->med_ch_cb = NULL;
+
+    status = media_channel_init_cb(call_id, NULL);
+    if (status != PJ_SUCCESS && sip_err_code)
+        *sip_err_code = call->med_ch_info.sip_err_code;
+
+    pj_log_pop_indent();
+    return status;
+
+on_error:
+    if (call->med_ch_mutex) {
+        pj_mutex_destroy(call->med_ch_mutex);
+        call->med_ch_mutex = NULL;
+    }
+
+    pj_log_pop_indent();
+    return status;
+}
+
+
+/* Create SDP based on the current media channel. Note that, this function
+ * will not modify the media channel, so when receiving new offer or
+ * updating media count (via call setting), media channel must be reinit'd
+ * (using pjsua_media_channel_init()) first before calling this function.
+ */
+pj_status_t pjsua_media_channel_create_sdp(pjsua_call_id call_id, 
+					   pj_pool_t *pool,
+					   const pjmedia_sdp_session *rem_sdp,
+					   pjmedia_sdp_session **p_sdp,
+					   int *sip_err_code)
+{
+    enum { MAX_MEDIA = PJSUA_MAX_CALL_MEDIA };
+    pjmedia_sdp_session *sdp;
+    pj_sockaddr origin;
+    pjsua_call *call = &pjsua_var.calls[call_id];
+    pjmedia_sdp_neg_state sdp_neg_state = PJMEDIA_SDP_NEG_STATE_NULL;
+    unsigned mi;
+    unsigned tot_bandw_tias = 0;
+    pj_status_t status;
+
+    if (pjsua_get_state() != PJSUA_STATE_RUNNING)
+	return PJ_EBUSY;
+
+#if 0
+    // This function should not really change the media channel.
+    if (rem_sdp) {
+	/* If this is a re-offer, let's re-initialize media as remote may
+	 * add or remove media
+	 */
+	if (call->inv && call->inv->state == PJSIP_INV_STATE_CONFIRMED) {
+	    status = pjsua_media_channel_init(call_id, PJSIP_ROLE_UAS,
+					      call->secure_level, pool,
+					      rem_sdp, sip_err_code,
+                                              PJ_FALSE, NULL);
+	    if (status != PJ_SUCCESS)
+		return status;
+	}
+    } else {
+	/* Audio is first in our offer, by convention */
+	// The audio_idx should not be changed here, as this function may be
+	// called in generating re-offer and the current active audio index
+	// can be anywhere.
+	//call->audio_idx = 0;
+    }
+#endif
+
+#if 0
+    // Since r3512, old-style hold should have got transport, created by 
+    // pjsua_media_channel_init() in initial offer/answer or remote reoffer.
+    /* Create media if it's not created. This could happen when call is
+     * currently on-hold (with the old style hold)
+     */
+    if (call->media[call->audio_idx].tp == NULL) {
+	pjsip_role_e role;
+	role = (rem_sdp ? PJSIP_ROLE_UAS : PJSIP_ROLE_UAC);
+	status = pjsua_media_channel_init(call_id, role, call->secure_level, 
+					  pool, rem_sdp, sip_err_code);
+	if (status != PJ_SUCCESS)
+	    return status;
+    }
+#endif
+
+    /* Get SDP negotiator state */
+    if (call->inv && call->inv->neg)
+	sdp_neg_state = pjmedia_sdp_neg_get_state(call->inv->neg);
+
+    /* Get one address to use in the origin field */
+    pj_bzero(&origin, sizeof(origin));
+    for (mi=0; mi<call->med_prov_cnt; ++mi) {
+	pjmedia_transport_info tpinfo;
+
+	if (call->media_prov[mi].tp == NULL)
+	    continue;
+
+	pjmedia_transport_info_init(&tpinfo);
+	pjmedia_transport_get_info(call->media_prov[mi].tp, &tpinfo);
+	pj_sockaddr_cp(&origin, &tpinfo.sock_info.rtp_addr_name);
+	break;
+    }
+
+    /* Create the base (blank) SDP */
+    status = pjmedia_endpt_create_base_sdp(pjsua_var.med_endpt, pool, NULL,
+                                           &origin, &sdp);
+    if (status != PJ_SUCCESS)
+	return status;
+
+    /* Process each media line */
+    for (mi=0; mi<call->med_prov_cnt; ++mi) {
+	pjsua_call_media *call_med = &call->media_prov[mi];
+	pjmedia_sdp_media *m = NULL;
+	pjmedia_transport_info tpinfo;
+	unsigned i;
+
+	if (rem_sdp && mi >= rem_sdp->media_count) {
+	    /* Remote might have removed some media lines. */
+            media_prov_clean_up(call->index, rem_sdp->media_count);
+            call->med_prov_cnt = rem_sdp->media_count;
+	    break;
+	}
+
+	if (call_med->tp == NULL || call_med->tp_st == PJSUA_MED_TP_DISABLED)
+	{
+	    /*
+	     * This media is disabled. Just create a valid SDP with zero
+	     * port.
+	     */
+	    if (rem_sdp) {
+		/* Just clone the remote media and deactivate it */
+		m = pjmedia_sdp_media_clone_deactivate(pool,
+						       rem_sdp->media[mi]);
+	    } else {
+		m = PJ_POOL_ZALLOC_T(pool, pjmedia_sdp_media);
+		m->desc.transport = pj_str("RTP/AVP");
+		m->desc.fmt_count = 1;
+
+		switch (call_med->type) {
+		case PJMEDIA_TYPE_AUDIO:
+		    m->desc.media = pj_str("audio");
+		    m->desc.fmt[0] = pj_str("0");
+		    break;
+		case PJMEDIA_TYPE_VIDEO:
+		    m->desc.media = pj_str("video");
+		    m->desc.fmt[0] = pj_str("31");
+		    break;
+		default:
+		    /* This must be us generating re-offer, and some unknown
+		     * media may exist, so just clone from active local SDP
+		     * (and it should have been deactivated already).
+		     */
+		    pj_assert(call->inv && call->inv->neg &&
+			      sdp_neg_state == PJMEDIA_SDP_NEG_STATE_DONE);
+		    {
+			const pjmedia_sdp_session *s_;
+			pjmedia_sdp_neg_get_active_local(call->inv->neg, &s_);
+
+			pj_assert(mi < s_->media_count);
+			m = pjmedia_sdp_media_clone(pool, s_->media[mi]);
+			m->desc.port = 0;
+		    }
+		    break;
+		}
+	    }
+
+	    /* Add connection line, if none */
+	    if (m->conn == NULL && sdp->conn == NULL) {
+		pj_bool_t use_ipv6;
+
+		use_ipv6 = (pjsua_var.acc[call->acc_id].cfg.ipv6_media_use !=
+			    PJSUA_IPV6_DISABLED);
+
+		m->conn = PJ_POOL_ZALLOC_T(pool, pjmedia_sdp_conn);
+		m->conn->net_type = pj_str("IN");
+		if (use_ipv6) {
+		    m->conn->addr_type = pj_str("IP6");
+		    m->conn->addr = pj_str("::1");
+		} else {
+		    m->conn->addr_type = pj_str("IP4");
+		    m->conn->addr = pj_str("127.0.0.1");
+		}
+	    }
+
+	    sdp->media[sdp->media_count++] = m;
+	    continue;
+	}
+
+	/* Get transport address info */
+	pjmedia_transport_info_init(&tpinfo);
+	pjmedia_transport_get_info(call_med->tp, &tpinfo);
+
+	/* Ask pjmedia endpoint to create SDP media line */
+	switch (call_med->type) {
+	case PJMEDIA_TYPE_AUDIO:
+	    status = pjmedia_endpt_create_audio_sdp(pjsua_var.med_endpt, pool,
+                                                    &tpinfo.sock_info, 0, &m);
+	    break;
+#if defined(PJMEDIA_HAS_VIDEO) && (PJMEDIA_HAS_VIDEO != 0)
+	case PJMEDIA_TYPE_VIDEO:
+	    status = pjmedia_endpt_create_video_sdp(pjsua_var.med_endpt, pool,
+	                                            &tpinfo.sock_info, 0, &m);
+	    break;
+#endif
+	default:
+	    pj_assert(!"Invalid call_med media type");
+	    return PJ_EBUG;
+	}
+
+	if (status != PJ_SUCCESS)
+	    return status;
+
+	sdp->media[sdp->media_count++] = m;
+
+	/* Give to transport */
+	status = pjmedia_transport_encode_sdp(call_med->tp, pool,
+					      sdp, rem_sdp, mi);
+	if (status != PJ_SUCCESS) {
+	    if (sip_err_code) *sip_err_code = PJSIP_SC_NOT_ACCEPTABLE;
+	    return status;
+	}
+
+#if PJSUA_SDP_SESS_HAS_CONN
+	/* Copy c= line of the first media to session level,
+	 * if there's none.
+	 */
+	if (sdp->conn == NULL) {
+	    sdp->conn = pjmedia_sdp_conn_clone(pool, m->conn);
+	}
+#endif
+
+	
+	/* Find media bandwidth info */
+	for (i = 0; i < m->bandw_count; ++i) {
+	    const pj_str_t STR_BANDW_MODIFIER_TIAS = { "TIAS", 4 };
+	    if (!pj_stricmp(&m->bandw[i]->modifier, &STR_BANDW_MODIFIER_TIAS))
+	    {
+		tot_bandw_tias += m->bandw[i]->value;
+		break;
+	    }
+	}
+    }
+
+    /* Add NAT info in the SDP */
+    if (pjsua_var.ua_cfg.nat_type_in_sdp) {
+	pjmedia_sdp_attr *a;
+	pj_str_t value;
+	char nat_info[80];
+
+	value.ptr = nat_info;
+	if (pjsua_var.ua_cfg.nat_type_in_sdp == 1) {
+	    value.slen = pj_ansi_snprintf(nat_info, sizeof(nat_info),
+					  "%d", pjsua_var.nat_type);
+	} else {
+	    const char *type_name = pj_stun_get_nat_name(pjsua_var.nat_type);
+	    value.slen = pj_ansi_snprintf(nat_info, sizeof(nat_info),
+					  "%d %s",
+					  pjsua_var.nat_type,
+					  type_name);
+	}
+
+	a = pjmedia_sdp_attr_create(pool, "X-nat", &value);
+
+	pjmedia_sdp_attr_add(&sdp->attr_count, sdp->attr, a);
+
+    }
+
+
+    /* Add bandwidth info in session level using bandwidth modifier "AS". */
+    if (tot_bandw_tias) {
+	unsigned bandw;
+	const pj_str_t STR_BANDW_MODIFIER_AS = { "AS", 2 };
+	pjmedia_sdp_bandw *b;
+
+	/* AS bandwidth = RTP bitrate + RTCP bitrate.
+	 * RTP bitrate  = payload bitrate (total TIAS) + overheads (~16kbps).
+	 * RTCP bitrate = est. 5% of RTP bitrate.
+	 * Note that AS bandwidth is in kbps.
+	 */
+	bandw = tot_bandw_tias + 16000;
+	bandw += bandw * 5 / 100;
+	b = PJ_POOL_ALLOC_T(pool, pjmedia_sdp_bandw);
+	b->modifier = STR_BANDW_MODIFIER_AS;
+	b->value = bandw / 1000;
+	sdp->bandw[sdp->bandw_count++] = b;
+    }
+
+
+#if DISABLED_FOR_TICKET_1185 && defined(PJMEDIA_HAS_SRTP) && (PJMEDIA_HAS_SRTP != 0)
+    /* Check if SRTP is in optional mode and configured to use duplicated
+     * media, i.e: secured and unsecured version, in the SDP offer.
+     */
+    if (!rem_sdp &&
+	pjsua_var.acc[call->acc_id].cfg.use_srtp == PJMEDIA_SRTP_OPTIONAL &&
+	pjsua_var.acc[call->acc_id].cfg.srtp_optional_dup_offer)
+    {
+	unsigned i;
+
+	for (i = 0; i < sdp->media_count; ++i) {
+	    pjmedia_sdp_media *m = sdp->media[i];
+
+	    /* Check if this media is unsecured but has SDP "crypto"
+	     * attribute.
+	     */
+	    if (pj_stricmp2(&m->desc.transport, "RTP/AVP") == 0 &&
+		pjmedia_sdp_media_find_attr2(m, "crypto", NULL) != NULL)
+	    {
+		if (i == (unsigned)call->audio_idx &&
+		    sdp_neg_state == PJMEDIA_SDP_NEG_STATE_DONE)
+		{
+		    /* This is a session update, and peer has chosen the
+		     * unsecured version, so let's make this unsecured too.
+		     */
+		    pjmedia_sdp_media_remove_all_attr(m, "crypto");
+		} else {
+		    /* This is new offer, duplicate media so we'll have
+		     * secured (with "RTP/SAVP" transport) and and unsecured
+		     * versions.
+		     */
+		    pjmedia_sdp_media *new_m;
+
+		    /* Duplicate this media and apply secured transport */
+		    new_m = pjmedia_sdp_media_clone(pool, m);
+		    pj_strdup2(pool, &new_m->desc.transport, "RTP/SAVP");
+
+		    /* Remove the "crypto" attribute in the unsecured media */
+		    pjmedia_sdp_media_remove_all_attr(m, "crypto");
+
+		    /* Insert the new media before the unsecured media */
+		    if (sdp->media_count < PJMEDIA_MAX_SDP_MEDIA) {
+			pj_array_insert(sdp->media, sizeof(new_m),
+					sdp->media_count, i, &new_m);
+			++sdp->media_count;
+			++i;
+		    }
+		}
+	    }
+	}
+    }
+#endif
+
+    call->rem_offerer = (rem_sdp != NULL);
+
+    /* Notify application */
+    if (pjsua_var.ua_cfg.cb.on_call_sdp_created) {
+	(*pjsua_var.ua_cfg.cb.on_call_sdp_created)(call_id, sdp,
+						   pool, rem_sdp);
+    }
+
+    *p_sdp = sdp;
+    return PJ_SUCCESS;
+}
+
+
+static void stop_media_stream(pjsua_call *call, unsigned med_idx)
+{
+    pjsua_call_media *call_med = &call->media[med_idx];
+
+    /* Check if stream does not exist */
+    if (med_idx >= call->med_cnt)
+	return;
+
+    pj_log_push_indent();
+
+    if (call_med->type == PJMEDIA_TYPE_AUDIO) {
+	pjsua_aud_stop_stream(call_med);
+    }
+
+#if PJMEDIA_HAS_VIDEO
+    else if (call_med->type == PJMEDIA_TYPE_VIDEO) {
+	pjsua_vid_stop_stream(call_med);
+    }
+#endif
+
+    PJ_LOG(4,(THIS_FILE, "Media stream call%02d:%d is destroyed",
+			 call->index, med_idx));
+    call_med->prev_state = call_med->state;
+    call_med->state = PJSUA_CALL_MEDIA_NONE;
+
+    /* Try to sync recent changes to provisional media */
+    if (med_idx < call->med_prov_cnt && 
+	call->media_prov[med_idx].tp == call_med->tp)
+    {
+	pjsua_call_media *prov_med = &call->media_prov[med_idx];
+
+	/* Media state */
+	prov_med->prev_state = call_med->prev_state;
+	prov_med->state	     = call_med->state;
+
+	/* RTP seq/ts */
+	prov_med->rtp_tx_seq_ts_set = call_med->rtp_tx_seq_ts_set;
+	prov_med->rtp_tx_seq	    = call_med->rtp_tx_seq;
+	prov_med->rtp_tx_ts	    = call_med->rtp_tx_ts;
+
+	/* Stream */
+	if (call_med->type == PJMEDIA_TYPE_AUDIO) {
+	    prov_med->strm.a.conf_slot = call_med->strm.a.conf_slot;
+	    prov_med->strm.a.stream    = call_med->strm.a.stream;
+	}
+#if PJMEDIA_HAS_VIDEO
+	else if (call_med->type == PJMEDIA_TYPE_VIDEO) {
+	    prov_med->strm.v.cap_win_id = call_med->strm.v.cap_win_id;
+	    prov_med->strm.v.rdr_win_id = call_med->strm.v.rdr_win_id;
+	    prov_med->strm.v.stream	= call_med->strm.v.stream;
+	}
+#endif
+    }
+
+    pj_log_pop_indent();
+}
+
+static void stop_media_session(pjsua_call_id call_id)
+{
+    pjsua_call *call = &pjsua_var.calls[call_id];
+    unsigned mi;
+
+    for (mi=0; mi<call->med_cnt; ++mi) {
+	stop_media_stream(call, mi);
+    }
+}
+
+pj_status_t pjsua_media_channel_deinit(pjsua_call_id call_id)
+{
+    pjsua_call *call = &pjsua_var.calls[call_id];
+    unsigned mi;
+
+    for (mi=0; mi<call->med_cnt; ++mi) {
+	pjsua_call_media *call_med = &call->media[mi];
+
+        if (call_med->tp_st == PJSUA_MED_TP_CREATING) {
+            /* We will do the deinitialization after media transport
+             * creation is completed.
+             */
+            call->async_call.med_ch_deinit = PJ_TRUE;
+            return PJ_SUCCESS;
+        }
+    }
+
+    PJ_LOG(4,(THIS_FILE, "Call %d: deinitializing media..", call_id));
+    pj_log_push_indent();
+
+    stop_media_session(call_id);
+
+    /* Clean up media transports */
+    pjsua_media_prov_clean_up(call_id);
+    call->med_prov_cnt = 0;
+    for (mi=0; mi<call->med_cnt; ++mi) {
+	pjsua_call_media *call_med = &call->media[mi];
+
+        if (call_med->tp_st > PJSUA_MED_TP_IDLE) {
+	    pjsua_set_media_tp_state(call_med, PJSUA_MED_TP_IDLE);
+	    pjmedia_transport_media_stop(call_med->tp);
+	}
+
+	if (call_med->tp) {
+	    pjsua_set_media_tp_state(call_med, PJSUA_MED_TP_NULL);
+	    pjmedia_transport_close(call_med->tp);
+	    call_med->tp = call_med->tp_orig = NULL;
+	}
+        call_med->tp_orig = NULL;
+    }
+
+    pj_log_pop_indent();
+
+    return PJ_SUCCESS;
+}
+
+
+/* Match codec fmtp. This will compare the values and the order. */
+static pj_bool_t match_codec_fmtp(const pjmedia_codec_fmtp *fmtp1,
+				  const pjmedia_codec_fmtp *fmtp2)
+{
+    unsigned i;
+
+    if (fmtp1->cnt != fmtp2->cnt)
+	return PJ_FALSE;
+
+    for (i = 0; i < fmtp1->cnt; ++i) {
+	if (pj_stricmp(&fmtp1->param[i].name, &fmtp2->param[i].name))
+	    return PJ_FALSE;
+	if (pj_stricmp(&fmtp1->param[i].val, &fmtp2->param[i].val))
+	    return PJ_FALSE;
+    }
+
+    return PJ_TRUE;
+}
+
+#if PJSUA_MEDIA_HAS_PJMEDIA || PJSUA_THIRD_PARTY_STREAM_HAS_GET_INFO
+
+static pj_bool_t is_ice_running(pjmedia_transport *tp)
+{
+    pjmedia_transport_info tpinfo;
+    pjmedia_ice_transport_info *ice_info;
+
+    pjmedia_transport_info_init(&tpinfo);
+    pjmedia_transport_get_info(tp, &tpinfo);
+    ice_info = (pjmedia_ice_transport_info*)
+	       pjmedia_transport_info_get_spc_info(&tpinfo,
+						   PJMEDIA_TRANSPORT_TYPE_ICE);
+    return (ice_info && ice_info->sess_state == PJ_ICE_STRANS_STATE_RUNNING);
+}
+
+
+static pj_bool_t is_media_changed(const pjsua_call *call,
+				  unsigned med_idx,
+				  const pjsua_stream_info *new_si_)
+{
+    const pjsua_call_media *call_med = &call->media[med_idx];
+
+    /* Check for newly added media */
+    if (med_idx >= call->med_cnt)
+	return PJ_TRUE;
+
+    /* Compare media type */
+    if (call_med->type != new_si_->type)
+	return PJ_TRUE;
+
+    /* Audio update checks */
+    if (call_med->type == PJMEDIA_TYPE_AUDIO) {
+	pjmedia_stream_info the_old_si;
+	const pjmedia_stream_info *old_si = NULL;
+	const pjmedia_stream_info *new_si = &new_si_->info.aud;
+	const pjmedia_codec_info *old_ci = NULL;
+	const pjmedia_codec_info *new_ci = &new_si->fmt;
+	const pjmedia_codec_param *old_cp = NULL;
+	const pjmedia_codec_param *new_cp = new_si->param;
+
+	/* Compare media direction */
+	if (call_med->dir != new_si->dir)
+	    return PJ_TRUE;
+
+	/* Get current active stream info */
+	if (call_med->strm.a.stream) {
+	    pjmedia_stream_get_info(call_med->strm.a.stream, &the_old_si);
+	    old_si = &the_old_si;
+	    old_ci = &old_si->fmt;
+	    old_cp = old_si->param;
+	} else {
+	    /* The stream is inactive. */
+	    return (new_si->dir != PJMEDIA_DIR_NONE);
+	}
+
+	/* Compare remote RTP address. If ICE is running, change in default
+	 * address can happen after negotiation, this can be handled
+	 * internally by ICE and does not need to cause media restart.
+	 */
+	if (!is_ice_running(call_med->tp) &&
+	    pj_sockaddr_cmp(&old_si->rem_addr, &new_si->rem_addr))
+	{
+	    return PJ_TRUE;
+	}
+
+	/* Compare codec info */
+	if (pj_stricmp(&old_ci->encoding_name, &new_ci->encoding_name) ||
+	    old_ci->clock_rate != new_ci->clock_rate ||
+	    old_ci->channel_cnt != new_ci->channel_cnt ||
+	    old_si->rx_pt != new_si->rx_pt ||
+	    old_si->tx_pt != new_si->tx_pt ||
+	    old_si->rx_event_pt != new_si->tx_event_pt ||
+	    old_si->tx_event_pt != new_si->tx_event_pt)
+	{
+	    return PJ_TRUE;
+	}
+
+	/* Compare codec param */
+	if (old_cp->setting.frm_per_pkt != new_cp->setting.frm_per_pkt ||
+	    old_cp->setting.vad != new_cp->setting.vad ||
+	    old_cp->setting.cng != new_cp->setting.cng ||
+	    old_cp->setting.plc != new_cp->setting.plc ||
+	    old_cp->setting.penh != new_cp->setting.penh ||
+	    !match_codec_fmtp(&old_cp->setting.dec_fmtp,
+			      &new_cp->setting.dec_fmtp) ||
+	    !match_codec_fmtp(&old_cp->setting.enc_fmtp,
+			      &new_cp->setting.enc_fmtp))
+	{
+	    return PJ_TRUE;
+	}
+    }
+
+#if PJMEDIA_HAS_VIDEO
+    else if (call_med->type == PJMEDIA_TYPE_VIDEO) {
+	pjmedia_vid_stream_info the_old_si;
+	const pjmedia_vid_stream_info *old_si = NULL;
+	const pjmedia_vid_stream_info *new_si = &new_si_->info.vid;
+	const pjmedia_vid_codec_info *old_ci = NULL;
+	const pjmedia_vid_codec_info *new_ci = &new_si->codec_info;
+	const pjmedia_vid_codec_param *old_cp = NULL;
+	const pjmedia_vid_codec_param *new_cp = new_si->codec_param;
+
+	/* Compare media direction */
+	if (call_med->dir != new_si->dir)
+	    return PJ_TRUE;
+
+	/* Get current active stream info */
+	if (call_med->strm.v.stream) {
+	    pjmedia_vid_stream_get_info(call_med->strm.v.stream, &the_old_si);
+	    old_si = &the_old_si;
+	    old_ci = &old_si->codec_info;
+	    old_cp = old_si->codec_param;
+	} else {
+	    /* The stream is inactive. */
+	    return (new_si->dir != PJMEDIA_DIR_NONE);
+	}
+
+	/* Compare remote RTP address. If ICE is running, change in default
+	 * address can happen after negotiation, this can be handled
+	 * internally by ICE and does not need to cause media restart.
+	 */
+	if (!is_ice_running(call_med->tp) &&
+	    pj_sockaddr_cmp(&old_si->rem_addr, &new_si->rem_addr))
+	{
+	    return PJ_TRUE;
+	}
+
+	/* Compare codec info */
+	if (pj_stricmp(&old_ci->encoding_name, &new_ci->encoding_name) ||
+	    old_si->rx_pt != new_si->rx_pt ||
+	    old_si->tx_pt != new_si->tx_pt)
+	{
+	    return PJ_TRUE;
+	}
+
+	/* Compare codec param */
+	if (/* old_cp->enc_mtu != new_cp->enc_mtu || */
+	    pj_memcmp(&old_cp->enc_fmt.det, &new_cp->enc_fmt.det,
+		      sizeof(pjmedia_video_format_detail)) ||
+	    !match_codec_fmtp(&old_cp->dec_fmtp, &new_cp->dec_fmtp) ||
+	    !match_codec_fmtp(&old_cp->enc_fmtp, &new_cp->enc_fmtp))
+	{
+	    return PJ_TRUE;
+	}
+    }
+
+#endif
+
+    else {
+	/* Just return PJ_TRUE for other media type */
+	return PJ_TRUE;
+    }
+
+    return PJ_FALSE;
+}
+
+#else /* PJSUA_MEDIA_HAS_PJMEDIA || PJSUA_THIRD_PARTY_STREAM_HAS_GET_INFO */
+
+static pj_bool_t is_media_changed(const pjsua_call *call,
+				  unsigned med_idx,
+				  const pjsua_stream_info *new_si_)
+{
+    PJ_UNUSED_ARG(call);
+    PJ_UNUSED_ARG(med_idx);
+    PJ_UNUSED_ARG(new_si_);
+    /* Always assume that media has been changed */
+    return PJ_TRUE;
+}
+
+#endif /* PJSUA_MEDIA_HAS_PJMEDIA || PJSUA_THIRD_PARTY_STREAM_HAS_GET_INFO */
+
+
+pj_status_t pjsua_media_channel_update(pjsua_call_id call_id,
+				       const pjmedia_sdp_session *local_sdp,
+				       const pjmedia_sdp_session *remote_sdp)
+{
+    pjsua_call *call = &pjsua_var.calls[call_id];
+    pjsua_acc *acc = &pjsua_var.acc[call->acc_id];
+    pj_pool_t *tmp_pool = call->inv->pool_prov;
+    unsigned mi;
+    pj_bool_t got_media = PJ_FALSE;
+    pj_status_t status = PJ_SUCCESS;
+
+    const pj_str_t STR_AUDIO = { "audio", 5 };
+    const pj_str_t STR_VIDEO = { "video", 5 };
+    pj_uint8_t maudidx[PJSUA_MAX_CALL_MEDIA];
+    unsigned maudcnt = PJ_ARRAY_SIZE(maudidx);
+    unsigned mtotaudcnt = PJ_ARRAY_SIZE(maudidx);
+    pj_uint8_t mvididx[PJSUA_MAX_CALL_MEDIA];
+    unsigned mvidcnt = PJ_ARRAY_SIZE(mvididx);
+    unsigned mtotvidcnt = PJ_ARRAY_SIZE(mvididx);
+    pj_bool_t need_renego_sdp = PJ_FALSE;
+
+    if (pjsua_get_state() != PJSUA_STATE_RUNNING)
+	return PJ_EBUSY;
+
+    PJ_LOG(4,(THIS_FILE, "Call %d: updating media..", call_id));
+    pj_log_push_indent();
+
+    /* Destroy existing media session, if any. */
+    //stop_media_session(call->index);
+
+    /* Call media count must be at least equal to SDP media. Note that
+     * it may not be equal when remote removed any SDP media line.
+     */
+    pj_assert(call->med_prov_cnt >= local_sdp->media_count);
+
+    /* Reset audio_idx first */
+    call->audio_idx = -1;
+
+    /* Sort audio/video based on "quality" */
+    sort_media(local_sdp, &STR_AUDIO, acc->cfg.use_srtp,
+	       maudidx, &maudcnt, &mtotaudcnt);
+#if PJMEDIA_HAS_VIDEO
+    sort_media(local_sdp, &STR_VIDEO, acc->cfg.use_srtp,
+	       mvididx, &mvidcnt, &mtotvidcnt);
+#else
+    PJ_UNUSED_ARG(STR_VIDEO);
+    mvidcnt = mtotvidcnt = 0;
+#endif
+
+    /* Applying media count limitation. Note that in generating SDP answer,
+     * no media count limitation applied, as we didn't know yet which media
+     * would pass the SDP negotiation.
+     */
+    if (maudcnt > call->opt.aud_cnt || mvidcnt > call->opt.vid_cnt)
+    {
+	pjmedia_sdp_session *local_sdp2;
+
+	maudcnt = PJ_MIN(maudcnt, call->opt.aud_cnt);
+	mvidcnt = PJ_MIN(mvidcnt, call->opt.vid_cnt);
+	local_sdp2 = pjmedia_sdp_session_clone(tmp_pool, local_sdp);
+
+	for (mi=0; mi < local_sdp2->media_count; ++mi) {
+	    pjmedia_sdp_media *m = local_sdp2->media[mi];
+
+	    if (m->desc.port == 0 ||
+		pj_memchr(maudidx, mi, maudcnt*sizeof(maudidx[0])) ||
+		pj_memchr(mvididx, mi, mvidcnt*sizeof(mvididx[0])))
+	    {
+		continue;
+	    }
+	    
+	    /* Deactivate this media */
+	    pjmedia_sdp_media_deactivate(tmp_pool, m);
+	}
+
+	local_sdp = local_sdp2;
+	need_renego_sdp = PJ_TRUE;
+    }
+
+    /* Process each media stream */
+    for (mi=0; mi < call->med_prov_cnt; ++mi) {
+	pjsua_call_media *call_med = &call->media_prov[mi];
+	pj_bool_t media_changed = PJ_FALSE;
+
+	if (mi >= local_sdp->media_count ||
+	    mi >= remote_sdp->media_count)
+	{
+	    /* This may happen when remote removed any SDP media lines in
+	     * its re-offer.
+	     */
+
+	    /* Stop stream */
+	    stop_media_stream(call, mi);
+
+	    /* Close the media transport */
+	    if (call_med->tp) {
+		pjsua_set_media_tp_state(call_med, PJSUA_MED_TP_NULL);
+		pjmedia_transport_close(call_med->tp);
+		call_med->tp = call_med->tp_orig = NULL;
+	    }
+	    continue;
+#if 0
+	    /* Something is wrong */
+	    PJ_LOG(1,(THIS_FILE, "Error updating media for call %d: "
+		      "invalid media index %d in SDP", call_id, mi));
+	    status = PJMEDIA_SDP_EINSDP;
+	    goto on_error;
+#endif
+	}
+
+	/* Apply media update action */
+	if (call_med->type==PJMEDIA_TYPE_AUDIO) {
+	    pjmedia_stream_info the_si, *si = &the_si;
+	    pjsua_stream_info stream_info;
+
+	    status = pjmedia_stream_info_from_sdp(
+					si, tmp_pool, pjsua_var.med_endpt,
+	                                local_sdp, remote_sdp, mi);
+	    if (status != PJ_SUCCESS) {
+		PJ_PERROR(1,(THIS_FILE, status,
+			     "pjmedia_stream_info_from_sdp() failed "
+			         "for call_id %d media %d",
+			     call_id, mi));
+		continue;
+	    }
+
+            /* Codec parameter of stream info (si->param) can be NULL if
+             * the stream is rejected or disabled.
+             */
+	    /* Override ptime, if this option is specified. */
+	    if (pjsua_var.media_cfg.ptime != 0 && si->param) {
+	        si->param->setting.frm_per_pkt = (pj_uint8_t)
+		    (pjsua_var.media_cfg.ptime / si->param->info.frm_ptime);
+	        if (si->param->setting.frm_per_pkt == 0)
+		    si->param->setting.frm_per_pkt = 1;
+	    }
+
+	    /* Disable VAD, if this option is specified. */
+	    if (pjsua_var.media_cfg.no_vad && si->param) {
+	        si->param->setting.vad = 0;
+	    }
+
+	    /* Check if this media is changed */
+	    stream_info.type = PJMEDIA_TYPE_AUDIO;
+	    stream_info.info.aud = the_si;
+	    if (pjsua_var.media_cfg.no_smart_media_update ||
+		is_media_changed(call, mi, &stream_info))
+	    {
+		media_changed = PJ_TRUE;
+		/* Stop the media */
+		stop_media_stream(call, mi);
+	    } else {
+		PJ_LOG(4,(THIS_FILE, "Call %d: stream #%d (audio) unchanged.",
+			  call_id, mi));
+	    }
+
+	    /* Check if no media is active */
+	    if (si->dir == PJMEDIA_DIR_NONE) {
+
+		/* Update call media state and direction */
+		call_med->state = PJSUA_CALL_MEDIA_NONE;
+		call_med->dir = PJMEDIA_DIR_NONE;
+
+	    } else {
+		pjmedia_transport_info tp_info;
+		pjmedia_srtp_info *srtp_info;
+
+		/* Start/restart media transport based on info in SDP */
+		status = pjmedia_transport_media_start(call_med->tp,
+						       tmp_pool, local_sdp,
+						       remote_sdp, mi);
+		if (status != PJ_SUCCESS) {
+		    PJ_PERROR(1,(THIS_FILE, status,
+				 "pjmedia_transport_media_start() failed "
+				     "for call_id %d media %d",
+				 call_id, mi));
+		    continue;
+		}
+
+		pjsua_set_media_tp_state(call_med, PJSUA_MED_TP_RUNNING);
+
+		/* Get remote SRTP usage policy */
+		pjmedia_transport_info_init(&tp_info);
+		pjmedia_transport_get_info(call_med->tp, &tp_info);
+		srtp_info = (pjmedia_srtp_info*)
+			    pjmedia_transport_info_get_spc_info(
+				    &tp_info, PJMEDIA_TRANSPORT_TYPE_SRTP);
+		if (srtp_info) {
+		    call_med->rem_srtp_use = srtp_info->peer_use;
+		}
+
+		/* Update audio channel */
+		if (media_changed) {
+		    status = pjsua_aud_channel_update(call_med,
+						      call->inv->pool, si,
+						      local_sdp, remote_sdp);
+		    if (status != PJ_SUCCESS) {
+			PJ_PERROR(1,(THIS_FILE, status,
+				     "pjsua_aud_channel_update() failed "
+					 "for call_id %d media %d",
+				     call_id, mi));
+			continue;
+		    }
+		}
+
+		/* Call media direction */
+		call_med->dir = si->dir;
+
+		/* Call media state */
+		if (call->local_hold)
+		    call_med->state = PJSUA_CALL_MEDIA_LOCAL_HOLD;
+		else if (call_med->dir == PJMEDIA_DIR_DECODING)
+		    call_med->state = PJSUA_CALL_MEDIA_REMOTE_HOLD;
+		else
+		    call_med->state = PJSUA_CALL_MEDIA_ACTIVE;
+	    }
+
+	    /* Print info. */
+	    if (status == PJ_SUCCESS) {
+		char info[80];
+		int info_len = 0;
+		int len;
+		const char *dir;
+
+		switch (si->dir) {
+		case PJMEDIA_DIR_NONE:
+		    dir = "inactive";
+		    break;
+		case PJMEDIA_DIR_ENCODING:
+		    dir = "sendonly";
+		    break;
+		case PJMEDIA_DIR_DECODING:
+		    dir = "recvonly";
+		    break;
+		case PJMEDIA_DIR_ENCODING_DECODING:
+		    dir = "sendrecv";
+		    break;
+		default:
+		    dir = "unknown";
+		    break;
+		}
+		len = pj_ansi_sprintf( info+info_len,
+				       ", stream #%d: %.*s (%s)", mi,
+				       (int)si->fmt.encoding_name.slen,
+				       si->fmt.encoding_name.ptr,
+				       dir);
+		if (len > 0)
+		    info_len += len;
+		PJ_LOG(4,(THIS_FILE,"Audio updated%s", info));
+	    }
+
+
+	    if (call->audio_idx==-1 && status==PJ_SUCCESS &&
+		si->dir != PJMEDIA_DIR_NONE)
+	    {
+		call->audio_idx = mi;
+	    }
+
+#if defined(PJMEDIA_HAS_VIDEO) && (PJMEDIA_HAS_VIDEO != 0)
+	} else if (call_med->type==PJMEDIA_TYPE_VIDEO) {
+	    pjmedia_vid_stream_info the_si, *si = &the_si;
+	    pjsua_stream_info stream_info;
+
+	    status = pjmedia_vid_stream_info_from_sdp(
+					si, tmp_pool, pjsua_var.med_endpt,
+					local_sdp, remote_sdp, mi);
+	    if (status != PJ_SUCCESS) {
+		PJ_PERROR(1,(THIS_FILE, status,
+			     "pjmedia_vid_stream_info_from_sdp() failed "
+			         "for call_id %d media %d",
+			     call_id, mi));
+		continue;
+	    }
+
+	    /* Check if this media is changed */
+	    stream_info.type = PJMEDIA_TYPE_VIDEO;
+	    stream_info.info.vid = the_si;
+	    if (is_media_changed(call, mi, &stream_info)) {
+		media_changed = PJ_TRUE;
+		/* Stop the media */
+		stop_media_stream(call, mi);
+	    } else {
+		PJ_LOG(4,(THIS_FILE, "Call %d: stream #%d (video) unchanged.",
+			  call_id, mi));
+	    }
+
+	    /* Check if no media is active */
+	    if (si->dir == PJMEDIA_DIR_NONE) {
+
+		/* Update call media state and direction */
+		call_med->state = PJSUA_CALL_MEDIA_NONE;
+		call_med->dir = PJMEDIA_DIR_NONE;
+
+	    } else {
+		pjmedia_transport_info tp_info;
+		pjmedia_srtp_info *srtp_info;
+
+		/* Start/restart media transport */
+		status = pjmedia_transport_media_start(call_med->tp,
+						       tmp_pool, local_sdp,
+						       remote_sdp, mi);
+		if (status != PJ_SUCCESS) {
+		    PJ_PERROR(1,(THIS_FILE, status,
+				 "pjmedia_transport_media_start() failed "
+				     "for call_id %d media %d",
+				 call_id, mi));
+		    continue;
+		}
+
+		pjsua_set_media_tp_state(call_med, PJSUA_MED_TP_RUNNING);
+
+		/* Get remote SRTP usage policy */
+		pjmedia_transport_info_init(&tp_info);
+		pjmedia_transport_get_info(call_med->tp, &tp_info);
+		srtp_info = (pjmedia_srtp_info*)
+			    pjmedia_transport_info_get_spc_info(
+				    &tp_info, PJMEDIA_TRANSPORT_TYPE_SRTP);
+		if (srtp_info) {
+		    call_med->rem_srtp_use = srtp_info->peer_use;
+		}
+
+		/* Update audio channel */
+		if (media_changed) {
+		    status = pjsua_vid_channel_update(call_med,
+						      call->inv->pool, si,
+						      local_sdp, remote_sdp);
+		    if (status != PJ_SUCCESS) {
+			PJ_PERROR(1,(THIS_FILE, status,
+				     "pjsua_vid_channel_update() failed "
+					 "for call_id %d media %d",
+				     call_id, mi));
+			continue;
+		    }
+		}
+
+		/* Call media direction */
+		call_med->dir = si->dir;
+
+		/* Call media state */
+		if (call->local_hold)
+		    call_med->state = PJSUA_CALL_MEDIA_LOCAL_HOLD;
+		else if (call_med->dir == PJMEDIA_DIR_DECODING)
+		    call_med->state = PJSUA_CALL_MEDIA_REMOTE_HOLD;
+		else
+		    call_med->state = PJSUA_CALL_MEDIA_ACTIVE;
+	    }
+
+	    /* Print info. */
+	    {
+		char info[80];
+		int info_len = 0;
+		int len;
+		const char *dir;
+
+		switch (si->dir) {
+		case PJMEDIA_DIR_NONE:
+		    dir = "inactive";
+		    break;
+		case PJMEDIA_DIR_ENCODING:
+		    dir = "sendonly";
+		    break;
+		case PJMEDIA_DIR_DECODING:
+		    dir = "recvonly";
+		    break;
+		case PJMEDIA_DIR_ENCODING_DECODING:
+		    dir = "sendrecv";
+		    break;
+		default:
+		    dir = "unknown";
+		    break;
+		}
+		len = pj_ansi_sprintf( info+info_len,
+				       ", stream #%d: %.*s (%s)", mi,
+				       (int)si->codec_info.encoding_name.slen,
+				       si->codec_info.encoding_name.ptr,
+				       dir);
+		if (len > 0)
+		    info_len += len;
+		PJ_LOG(4,(THIS_FILE,"Video updated%s", info));
+	    }
+
+#endif
+	} else {
+	    status = PJMEDIA_EINVALIMEDIATYPE;
+	}
+
+	/* Close the transport of deactivated media, need this here as media
+	 * can be deactivated by the SDP negotiation and the max media count
+	 * (account) setting.
+	 */
+	if (local_sdp->media[mi]->desc.port==0 && call_med->tp) {
+	    pjsua_set_media_tp_state(call_med, PJSUA_MED_TP_NULL);
+	    pjmedia_transport_close(call_med->tp);
+	    call_med->tp = call_med->tp_orig = NULL;
+	}
+
+	if (status != PJ_SUCCESS) {
+	    PJ_PERROR(1,(THIS_FILE, status, "Error updating media call%02d:%d",
+		         call_id, mi));
+	} else {
+	    got_media = PJ_TRUE;
+	}
+    }
+
+    /* Update call media from provisional media */
+    call->med_cnt = call->med_prov_cnt;
+    pj_memcpy(call->media, call->media_prov,
+	      sizeof(call->media_prov[0]) * call->med_prov_cnt);
+
+    /* Perform SDP re-negotiation if needed. */
+    if (got_media && need_renego_sdp) {
+	pjmedia_sdp_neg *neg = call->inv->neg;
+
+	/* This should only happen when we are the answerer. */
+	PJ_ASSERT_RETURN(neg && !pjmedia_sdp_neg_was_answer_remote(neg),
+			 PJMEDIA_SDPNEG_EINSTATE);
+	
+	status = pjmedia_sdp_neg_set_remote_offer(tmp_pool, neg, remote_sdp);
+	if (status != PJ_SUCCESS)
+	    goto on_error;
+
+	status = pjmedia_sdp_neg_set_local_answer(tmp_pool, neg, local_sdp);
+	if (status != PJ_SUCCESS)
+	    goto on_error;
+
+	status = pjmedia_sdp_neg_negotiate(tmp_pool, neg, 0);
+	if (status != PJ_SUCCESS)
+	    goto on_error;
+    }
+
+    pj_log_pop_indent();
+    return (got_media? PJ_SUCCESS : PJMEDIA_SDPNEG_ENOMEDIA);
+
+on_error:
+    pj_log_pop_indent();
+    return status;
+}
+
+/*****************************************************************************
+ * Codecs.
+ */
+
+/*
+ * Enum all supported codecs in the system.
+ */
+PJ_DEF(pj_status_t) pjsua_enum_codecs( pjsua_codec_info id[],
+				       unsigned *p_count )
+{
+    pjmedia_codec_mgr *codec_mgr;
+    pjmedia_codec_info info[32];
+    unsigned i, count, prio[32];
+    pj_status_t status;
+
+    codec_mgr = pjmedia_endpt_get_codec_mgr(pjsua_var.med_endpt);
+    count = PJ_ARRAY_SIZE(info);
+    status = pjmedia_codec_mgr_enum_codecs( codec_mgr, &count, info, prio);
+    if (status != PJ_SUCCESS) {
+	*p_count = 0;
+	return status;
+    }
+
+    if (count > *p_count) count = *p_count;
+
+    for (i=0; i<count; ++i) {
+	pj_bzero(&id[i], sizeof(pjsua_codec_info));
+
+	pjmedia_codec_info_to_id(&info[i], id[i].buf_, sizeof(id[i].buf_));
+	id[i].codec_id = pj_str(id[i].buf_);
+	id[i].priority = (pj_uint8_t) prio[i];
+    }
+
+    *p_count = count;
+
+    return PJ_SUCCESS;
+}
+
+
+/*
+ * Change codec priority.
+ */
+PJ_DEF(pj_status_t) pjsua_codec_set_priority( const pj_str_t *codec_id,
+					      pj_uint8_t priority )
+{
+    const pj_str_t all = { NULL, 0 };
+    pjmedia_codec_mgr *codec_mgr;
+
+    codec_mgr = pjmedia_endpt_get_codec_mgr(pjsua_var.med_endpt);
+
+    if (codec_id->slen==1 && *codec_id->ptr=='*')
+	codec_id = &all;
+
+    return pjmedia_codec_mgr_set_codec_priority(codec_mgr, codec_id, 
+					        priority);
+}
+
+
+/*
+ * Get codec parameters.
+ */
+PJ_DEF(pj_status_t) pjsua_codec_get_param( const pj_str_t *codec_id,
+					   pjmedia_codec_param *param )
+{
+    const pj_str_t all = { NULL, 0 };
+    const pjmedia_codec_info *info;
+    pjmedia_codec_mgr *codec_mgr;
+    unsigned count = 1;
+    pj_status_t status;
+
+    codec_mgr = pjmedia_endpt_get_codec_mgr(pjsua_var.med_endpt);
+
+    if (codec_id->slen==1 && *codec_id->ptr=='*')
+	codec_id = &all;
+
+    status = pjmedia_codec_mgr_find_codecs_by_id(codec_mgr, codec_id,
+						 &count, &info, NULL);
+    if (status != PJ_SUCCESS)
+	return status;
+
+    if (count != 1)
+	return (count > 1? PJ_ETOOMANY : PJ_ENOTFOUND);
+
+    status = pjmedia_codec_mgr_get_default_param( codec_mgr, info, param);
+    return status;
+}
+
+
+/*
+ * Set codec parameters.
+ */
+PJ_DEF(pj_status_t) pjsua_codec_set_param( const pj_str_t *codec_id,
+					   const pjmedia_codec_param *param)
+{
+    const pjmedia_codec_info *info[2];
+    pjmedia_codec_mgr *codec_mgr;
+    unsigned count = 2;
+    pj_status_t status;
+
+    codec_mgr = pjmedia_endpt_get_codec_mgr(pjsua_var.med_endpt);
+
+    status = pjmedia_codec_mgr_find_codecs_by_id(codec_mgr, codec_id,
+						 &count, info, NULL);
+    if (status != PJ_SUCCESS)
+	return status;
+
+    /* Codec ID should be specific, except for G.722.1 */
+    if (count > 1 && 
+	pj_strnicmp2(codec_id, "G7221/16", 8) != 0 &&
+	pj_strnicmp2(codec_id, "G7221/32", 8) != 0)
+    {
+	pj_assert(!"Codec ID is not specific");
+	return PJ_ETOOMANY;
+    }
+
+    status = pjmedia_codec_mgr_set_default_param(codec_mgr, info[0], param);
+    return status;
+}
+
+
+pj_status_t pjsua_media_apply_xml_control(pjsua_call_id call_id,
+					  const pj_str_t *xml_st)
+{
+#if PJMEDIA_HAS_VIDEO
+    pjsua_call *call = &pjsua_var.calls[call_id];
+    const pj_str_t PICT_FAST_UPDATE = {"picture_fast_update", 19};
+
+    if (pj_strstr(xml_st, &PICT_FAST_UPDATE)) {
+	unsigned i;
+
+	PJ_LOG(4,(THIS_FILE, "Received keyframe request via SIP INFO"));
+
+	for (i = 0; i < call->med_cnt; ++i) {
+	    pjsua_call_media *cm = &call->media[i];
+	    if (cm->type != PJMEDIA_TYPE_VIDEO || !cm->strm.v.stream)
+		continue;
+
+	    pjmedia_vid_stream_send_keyframe(cm->strm.v.stream);
+	}
+
+	return PJ_SUCCESS;
+    }
+#endif
+
+    /* Just to avoid compiler warning of unused var */
+    PJ_UNUSED_ARG(call_id);
+    PJ_UNUSED_ARG(xml_st);
+
+    return PJ_ENOTSUP;
+}
+
diff --git a/jni/pjproject-android/.svn/pristine/05/05efcaea973f9b7650d0b67e3eaf326e837de395.svn-base b/jni/pjproject-android/.svn/pristine/05/05efcaea973f9b7650d0b67e3eaf326e837de395.svn-base
new file mode 100644
index 0000000..d9228c8
--- /dev/null
+++ b/jni/pjproject-android/.svn/pristine/05/05efcaea973f9b7650d0b67e3eaf326e837de395.svn-base
@@ -0,0 +1,27 @@
+# $Id$
+import inc_sip as sip
+import inc_sdp as sdp
+
+sdp = \
+"""
+v=0
+o=- 0 0 IN IP4 127.0.0.1
+s=tester
+c=IN IP4 127.0.0.1
+t=0 0
+m=audio 4000 RTP/AVP 0 101
+a=rtpmap:0 PCMU/8000
+a=sendrecv
+a=rtpmap:101 telephone-event/8000
+a=fmtp:101 0-15
+a=crypto:1 AES_CM_128_HMAC_SHA1_80 inline:WnD7c1ksDGs+dIefCEo8omPg4uO8DYIinNGL5yxQ
+a=crypto:1 AES_CM_128_HMAC_SHA1_32 inline:t0r0/apkukU7JjjfR0mY8GEimBq4OiPEm9eKSFOx
+"""
+
+args = "--null-audio --auto-answer 200 --max-calls 1 --use-srtp 1 --srtp-secure 0"
+include = []
+exclude = []
+
+sendto_cfg = sip.SendtoCfg( "caller has used invalid crypto tag, callee must not accept the call", 
+			    pjsua_args=args, sdp=sdp, resp_code=406, 
+			    resp_inc=include, resp_exc=exclude)
diff --git a/jni/pjproject-android/.svn/pristine/05/05f2dfebccf4dddfc0b4ebfe427daad9dc2d25ba.svn-base b/jni/pjproject-android/.svn/pristine/05/05f2dfebccf4dddfc0b4ebfe427daad9dc2d25ba.svn-base
new file mode 100644
index 0000000..93690a2
--- /dev/null
+++ b/jni/pjproject-android/.svn/pristine/05/05f2dfebccf4dddfc0b4ebfe427daad9dc2d25ba.svn-base
@@ -0,0 +1,759 @@
+/* $Id$ */
+/* 
+ * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
+ * Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA 
+ */
+#include <pjmedia/vid_codec.h>
+#include <pjmedia/errno.h>
+#include <pj/array.h>
+#include <pj/assert.h>
+#include <pj/log.h>
+#include <pj/string.h>
+
+
+#if defined(PJMEDIA_HAS_VIDEO) && (PJMEDIA_HAS_VIDEO != 0)
+
+
+#define THIS_FILE   "vid_codec.c"
+
+static pjmedia_vid_codec_mgr *def_vid_codec_mgr;
+
+
+/* Definition of default codecs parameters */
+typedef struct pjmedia_vid_codec_default_param
+{
+    pj_pool_t			*pool;
+    pjmedia_vid_codec_param	*param;
+} pjmedia_vid_codec_default_param;
+
+
+/*
+ * Codec manager maintains array of these structs for each supported
+ * codec.
+ */
+typedef struct pjmedia_vid_codec_desc
+{
+    pjmedia_vid_codec_info	     info;	/**< Codec info.	    */
+    pjmedia_codec_id	             id;        /**< Fully qualified name   */
+    pjmedia_codec_priority           prio;      /**< Priority.		    */
+    pjmedia_vid_codec_factory       *factory;	/**< The factory.	    */
+    pjmedia_vid_codec_default_param *def_param; /**< Default codecs 
+					             parameters.	    */
+} pjmedia_vid_codec_desc;
+
+
+/* The declaration of video codec manager */
+struct pjmedia_vid_codec_mgr
+{
+    /** Pool factory instance. */
+    pj_pool_factory		*pf;
+
+    /** Codec manager mutex. */
+    pj_mutex_t			*mutex;
+
+    /** List of codec factories registered to codec manager. */
+    pjmedia_vid_codec_factory	 factory_list;
+
+    /** Number of supported codecs. */
+    unsigned			 codec_cnt;
+
+    /** Array of codec descriptor. */
+    pjmedia_vid_codec_desc	 codec_desc[PJMEDIA_CODEC_MGR_MAX_CODECS];
+
+};
+
+
+
+/* Sort codecs in codec manager based on priorities */
+static void sort_codecs(pjmedia_vid_codec_mgr *mgr);
+
+
+/*
+ * Duplicate video codec parameter.
+ */
+PJ_DEF(pjmedia_vid_codec_param*) pjmedia_vid_codec_param_clone(
+					pj_pool_t *pool, 
+					const pjmedia_vid_codec_param *src)
+{
+    pjmedia_vid_codec_param *p;
+    unsigned i;
+
+    PJ_ASSERT_RETURN(pool && src, NULL);
+
+    p = PJ_POOL_ZALLOC_T(pool, pjmedia_vid_codec_param);
+
+    /* Update codec param */
+    pj_memcpy(p, src, sizeof(pjmedia_vid_codec_param));
+    for (i = 0; i < src->dec_fmtp.cnt; ++i) {
+	pj_strdup(pool, &p->dec_fmtp.param[i].name, 
+		  &src->dec_fmtp.param[i].name);
+	pj_strdup(pool, &p->dec_fmtp.param[i].val, 
+		  &src->dec_fmtp.param[i].val);
+    }
+    for (i = 0; i < src->enc_fmtp.cnt; ++i) {
+	pj_strdup(pool, &p->enc_fmtp.param[i].name, 
+		  &src->enc_fmtp.param[i].name);
+	pj_strdup(pool, &p->enc_fmtp.param[i].val, 
+		  &src->enc_fmtp.param[i].val);
+    }
+
+    return p;
+}
+
+/*
+ * Initialize codec manager.
+ */
+PJ_DEF(pj_status_t) pjmedia_vid_codec_mgr_create(
+                                            pj_pool_t *pool,
+                                            pjmedia_vid_codec_mgr **p_mgr)
+{
+    pjmedia_vid_codec_mgr *mgr;
+    pj_status_t status;
+
+    PJ_ASSERT_RETURN(pool, PJ_EINVAL);
+
+    mgr = PJ_POOL_ZALLOC_T(pool, pjmedia_vid_codec_mgr);
+    mgr->pf = pool->factory;
+    pj_list_init (&mgr->factory_list);
+    mgr->codec_cnt = 0;
+
+    /* Create mutex */
+    status = pj_mutex_create_recursive(pool, "vid-codec-mgr", &mgr->mutex);
+    if (status != PJ_SUCCESS)
+	return status;
+
+    if (!def_vid_codec_mgr)
+        def_vid_codec_mgr = mgr;
+
+    if (p_mgr)
+        *p_mgr = mgr;
+
+    return PJ_SUCCESS;
+}
+
+/*
+ * Initialize codec manager.
+ */
+PJ_DEF(pj_status_t) pjmedia_vid_codec_mgr_destroy (pjmedia_vid_codec_mgr *mgr)
+{
+    if (!mgr) mgr = def_vid_codec_mgr;
+    PJ_ASSERT_RETURN(mgr, PJ_EINVAL);
+
+    /* Destroy mutex */
+    if (mgr->mutex)
+	pj_mutex_destroy(mgr->mutex);
+
+    /* Just for safety, set codec manager states to zero */
+    pj_bzero(mgr, sizeof(pjmedia_vid_codec_mgr));
+
+    if (mgr == def_vid_codec_mgr)
+        def_vid_codec_mgr = NULL;
+
+    return PJ_SUCCESS;
+}
+
+
+PJ_DEF(pjmedia_vid_codec_mgr*) pjmedia_vid_codec_mgr_instance(void)
+{
+    //pj_assert(def_vid_codec_mgr);
+    return def_vid_codec_mgr;
+}
+
+PJ_DEF(void) pjmedia_vid_codec_mgr_set_instance(pjmedia_vid_codec_mgr* mgr)
+{
+    def_vid_codec_mgr = mgr;
+}
+
+
+/*
+ * Register a codec factory.
+ */
+PJ_DEF(pj_status_t) pjmedia_vid_codec_mgr_register_factory(
+                                    pjmedia_vid_codec_mgr *mgr,
+				    pjmedia_vid_codec_factory *factory)
+{
+    pjmedia_vid_codec_info info[PJMEDIA_CODEC_MGR_MAX_CODECS];
+    unsigned i, count;
+    pj_status_t status;
+
+    PJ_ASSERT_RETURN(factory, PJ_EINVAL);
+
+    if (!mgr) mgr = def_vid_codec_mgr;
+    PJ_ASSERT_RETURN(mgr, PJ_EINVAL);
+
+    /* Enum codecs */
+    count = PJ_ARRAY_SIZE(info);
+    status = factory->op->enum_info(factory, &count, info);
+    if (status != PJ_SUCCESS)
+	return status;
+
+    pj_mutex_lock(mgr->mutex);
+
+    /* Check codec count */
+    if (count + mgr->codec_cnt > PJ_ARRAY_SIZE(mgr->codec_desc)) {
+	pj_mutex_unlock(mgr->mutex);
+	return PJ_ETOOMANY;
+    }
+
+
+    /* Save the codecs */
+    for (i=0; i<count; ++i) {
+	pj_memcpy( &mgr->codec_desc[mgr->codec_cnt+i],
+		   &info[i], sizeof(pjmedia_vid_codec_info));
+	mgr->codec_desc[mgr->codec_cnt+i].prio = PJMEDIA_CODEC_PRIO_NORMAL;
+	mgr->codec_desc[mgr->codec_cnt+i].factory = factory;
+	pjmedia_vid_codec_info_to_id( &info[i],
+				  mgr->codec_desc[mgr->codec_cnt+i].id,
+				  sizeof(pjmedia_codec_id));
+    }
+
+    /* Update count */
+    mgr->codec_cnt += count;
+
+    /* Re-sort codec based on priorities */
+    sort_codecs(mgr);
+
+    /* Add factory to the list */
+    pj_list_push_back(&mgr->factory_list, factory);
+
+    pj_mutex_unlock(mgr->mutex);
+
+    return PJ_SUCCESS;
+}
+
+
+/*
+ * Unregister a codec factory.
+ */
+PJ_DEF(pj_status_t) pjmedia_vid_codec_mgr_unregister_factory(
+				pjmedia_vid_codec_mgr *mgr, 
+				pjmedia_vid_codec_factory *factory)
+{
+    unsigned i;
+    PJ_ASSERT_RETURN(factory, PJ_EINVAL);
+
+    if (!mgr) mgr = def_vid_codec_mgr;
+    PJ_ASSERT_RETURN(mgr, PJ_EINVAL);
+
+    pj_mutex_lock(mgr->mutex);
+
+    /* Factory must be registered. */
+    if (pj_list_find_node(&mgr->factory_list, factory) != factory) {
+	pj_mutex_unlock(mgr->mutex);
+	return PJ_ENOTFOUND;
+    }
+
+    /* Erase factory from the factory list */
+    pj_list_erase(factory);
+
+
+    /* Remove all supported codecs from the codec manager that were created 
+     * by the specified factory.
+     */
+    for (i=0; i<mgr->codec_cnt; ) {
+
+	if (mgr->codec_desc[i].factory == factory) {
+	    /* Remove the codec from array of codec descriptions */
+	    pj_array_erase(mgr->codec_desc, sizeof(mgr->codec_desc[0]), 
+			   mgr->codec_cnt, i);
+	    --mgr->codec_cnt;
+
+	} else {
+	    ++i;
+	}
+    }
+
+    pj_mutex_unlock(mgr->mutex);
+
+    return PJ_SUCCESS;
+}
+
+
+/*
+ * Enum all codecs.
+ */
+PJ_DEF(pj_status_t) pjmedia_vid_codec_mgr_enum_codecs(
+                                pjmedia_vid_codec_mgr *mgr, 
+			        unsigned *count, 
+			        pjmedia_vid_codec_info codecs[],
+			        unsigned *prio)
+{
+    unsigned i;
+
+    PJ_ASSERT_RETURN(count && codecs, PJ_EINVAL);
+
+    if (!mgr) mgr = def_vid_codec_mgr;
+    PJ_ASSERT_RETURN(mgr, PJ_EINVAL);
+
+    pj_mutex_lock(mgr->mutex);
+
+    if (*count > mgr->codec_cnt)
+	*count = mgr->codec_cnt;
+    
+    for (i=0; i<*count; ++i) {
+	pj_memcpy(&codecs[i], 
+		  &mgr->codec_desc[i].info, 
+		  sizeof(pjmedia_vid_codec_info));
+    }
+
+    if (prio) {
+	for (i=0; i < *count; ++i)
+	    prio[i] = mgr->codec_desc[i].prio;
+    }
+
+    pj_mutex_unlock(mgr->mutex);
+
+    return PJ_SUCCESS;
+}
+
+
+/*
+ * Get codec info for the specified payload type.
+ */
+PJ_DEF(pj_status_t) pjmedia_vid_codec_mgr_get_codec_info(
+                                    pjmedia_vid_codec_mgr *mgr,
+				    unsigned pt,
+				    const pjmedia_vid_codec_info **p_info)
+{
+    unsigned i;
+
+    PJ_ASSERT_RETURN(p_info, PJ_EINVAL);
+
+    if (!mgr) mgr = def_vid_codec_mgr;
+    PJ_ASSERT_RETURN(mgr, PJ_EINVAL);
+
+    pj_mutex_lock(mgr->mutex);
+
+    for (i=0; i<mgr->codec_cnt; ++i) {
+	if (mgr->codec_desc[i].info.pt == pt) {
+	    *p_info = &mgr->codec_desc[i].info;
+
+	    pj_mutex_unlock(mgr->mutex);
+	    return PJ_SUCCESS;
+	}
+    }
+
+    pj_mutex_unlock(mgr->mutex);
+
+    return PJMEDIA_CODEC_EUNSUP;
+}
+
+
+PJ_DEF(pj_status_t) pjmedia_vid_codec_mgr_get_codec_info2(
+				    pjmedia_vid_codec_mgr *mgr,
+				    pjmedia_format_id fmt_id,
+				    const pjmedia_vid_codec_info **p_info)
+{
+    unsigned i;
+
+    PJ_ASSERT_RETURN(p_info, PJ_EINVAL);
+
+    if (!mgr) mgr = def_vid_codec_mgr;
+    PJ_ASSERT_RETURN(mgr, PJ_EINVAL);
+
+    pj_mutex_lock(mgr->mutex);
+
+    for (i=0; i<mgr->codec_cnt; ++i) {
+	if (mgr->codec_desc[i].info.fmt_id == fmt_id) {
+	    *p_info = &mgr->codec_desc[i].info;
+
+	    pj_mutex_unlock(mgr->mutex);
+	    return PJ_SUCCESS;
+	}
+    }
+
+    pj_mutex_unlock(mgr->mutex);
+
+    return PJMEDIA_CODEC_EUNSUP;
+}
+
+
+/*
+ * Convert codec info struct into a unique codec identifier.
+ * A codec identifier looks something like "H263/34".
+ */
+PJ_DEF(char*) pjmedia_vid_codec_info_to_id(
+                                        const pjmedia_vid_codec_info *info,
+				        char *id, unsigned max_len )
+{
+    int len;
+
+    PJ_ASSERT_RETURN(info && id && max_len, NULL);
+
+    len = pj_ansi_snprintf(id, max_len, "%.*s/%u",
+			   (int)info->encoding_name.slen,
+			   info->encoding_name.ptr,
+			   info->pt);
+
+    if (len < 1 || len >= (int)max_len) {
+	id[0] = '\0';
+	return NULL;
+    }
+
+    return id;
+}
+
+
+/*
+ * Find codecs by the unique codec identifier. This function will find
+ * all codecs that match the codec identifier prefix. For example, if
+ * "L16" is specified, then it will find "L16/8000/1", "L16/16000/1",
+ * and so on, up to the maximum count specified in the argument.
+ */
+PJ_DEF(pj_status_t) pjmedia_vid_codec_mgr_find_codecs_by_id(
+                                     pjmedia_vid_codec_mgr *mgr,
+				     const pj_str_t *codec_id,
+				     unsigned *count,
+				     const pjmedia_vid_codec_info *p_info[],
+				     unsigned prio[])
+{
+    unsigned i, found = 0;
+
+    PJ_ASSERT_RETURN(codec_id && count && *count, PJ_EINVAL);
+
+    if (!mgr) mgr = def_vid_codec_mgr;
+    PJ_ASSERT_RETURN(mgr, PJ_EINVAL);
+
+    pj_mutex_lock(mgr->mutex);
+
+    for (i=0; i<mgr->codec_cnt; ++i) {
+
+	if (codec_id->slen == 0 ||
+	    pj_strnicmp2(codec_id, mgr->codec_desc[i].id, 
+			 codec_id->slen) == 0) 
+	{
+
+	    if (p_info)
+		p_info[found] = &mgr->codec_desc[i].info;
+	    if (prio)
+		prio[found] = mgr->codec_desc[i].prio;
+
+	    ++found;
+
+	    if (found >= *count)
+		break;
+	}
+
+    }
+
+    pj_mutex_unlock(mgr->mutex);
+
+    *count = found;
+
+    return found ? PJ_SUCCESS : PJ_ENOTFOUND;
+}
+
+
+/* Swap two codecs positions in codec manager */
+static void swap_codec(pjmedia_vid_codec_mgr *mgr, unsigned i, unsigned j)
+{
+    pjmedia_vid_codec_desc tmp;
+
+    pj_memcpy(&tmp, &mgr->codec_desc[i], sizeof(pjmedia_vid_codec_desc));
+
+    pj_memcpy(&mgr->codec_desc[i], &mgr->codec_desc[j], 
+	       sizeof(pjmedia_vid_codec_desc));
+
+    pj_memcpy(&mgr->codec_desc[j], &tmp, sizeof(pjmedia_vid_codec_desc));
+}
+
+
+/* Sort codecs in codec manager based on priorities */
+static void sort_codecs(pjmedia_vid_codec_mgr *mgr)
+{
+    unsigned i;
+
+   /* Re-sort */
+    for (i=0; i<mgr->codec_cnt; ++i) {
+	unsigned j, max;
+
+	for (max=i, j=i+1; j<mgr->codec_cnt; ++j) {
+	    if (mgr->codec_desc[j].prio > mgr->codec_desc[max].prio)
+		max = j;
+	}
+
+	if (max != i)
+	    swap_codec(mgr, i, max);
+    }
+
+    /* Change PJMEDIA_CODEC_PRIO_HIGHEST codecs to NEXT_HIGHER */
+    for (i=0; i<mgr->codec_cnt; ++i) {
+	if (mgr->codec_desc[i].prio == PJMEDIA_CODEC_PRIO_HIGHEST)
+	    mgr->codec_desc[i].prio = PJMEDIA_CODEC_PRIO_NEXT_HIGHER;
+	else
+	    break;
+    }
+}
+
+
+/**
+ * Set codec priority. The codec priority determines the order of
+ * the codec in the SDP created by the endpoint. If more than one codecs
+ * are found with the same codec_id prefix, then the function sets the
+ * priorities of all those codecs.
+ */
+PJ_DEF(pj_status_t) pjmedia_vid_codec_mgr_set_codec_priority(
+				pjmedia_vid_codec_mgr *mgr, 
+				const pj_str_t *codec_id,
+				pj_uint8_t prio)
+{
+    unsigned i, found = 0;
+
+    PJ_ASSERT_RETURN(codec_id, PJ_EINVAL);
+
+    if (!mgr) mgr = def_vid_codec_mgr;
+    PJ_ASSERT_RETURN(mgr, PJ_EINVAL);
+
+    pj_mutex_lock(mgr->mutex);
+
+    /* Update the priorities of affected codecs */
+    for (i=0; i<mgr->codec_cnt; ++i) 
+    {
+	if (codec_id->slen == 0 ||
+	    pj_strnicmp2(codec_id, mgr->codec_desc[i].id, 
+			 codec_id->slen) == 0) 
+	{
+	    mgr->codec_desc[i].prio = (pjmedia_codec_priority) prio;
+	    ++found;
+	}
+    }
+
+    if (!found) {
+	pj_mutex_unlock(mgr->mutex);
+	return PJ_ENOTFOUND;
+    }
+
+    /* Re-sort codecs */
+    sort_codecs(mgr);
+
+    pj_mutex_unlock(mgr->mutex);
+
+    return PJ_SUCCESS;
+}
+
+
+/*
+ * Allocate one codec.
+ */
+PJ_DEF(pj_status_t) pjmedia_vid_codec_mgr_alloc_codec(
+                                        pjmedia_vid_codec_mgr *mgr, 
+					const pjmedia_vid_codec_info *info,
+					pjmedia_vid_codec **p_codec)
+{
+    pjmedia_vid_codec_factory *factory;
+    pj_status_t status;
+
+    PJ_ASSERT_RETURN(info && p_codec, PJ_EINVAL);
+
+    if (!mgr) mgr = def_vid_codec_mgr;
+    PJ_ASSERT_RETURN(mgr, PJ_EINVAL);
+
+    *p_codec = NULL;
+
+    pj_mutex_lock(mgr->mutex);
+
+    factory = mgr->factory_list.next;
+    while (factory != &mgr->factory_list) {
+
+	if ( (*factory->op->test_alloc)(factory, info) == PJ_SUCCESS ) {
+
+	    status = (*factory->op->alloc_codec)(factory, info, p_codec);
+	    if (status == PJ_SUCCESS) {
+		pj_mutex_unlock(mgr->mutex);
+		return PJ_SUCCESS;
+	    }
+
+	}
+
+	factory = factory->next;
+    }
+
+    pj_mutex_unlock(mgr->mutex);
+
+    return PJMEDIA_CODEC_EUNSUP;
+}
+
+
+/*
+ * Get default codec parameter.
+ */
+PJ_DEF(pj_status_t) pjmedia_vid_codec_mgr_get_default_param(
+                                        pjmedia_vid_codec_mgr *mgr,
+					const pjmedia_vid_codec_info *info,
+					pjmedia_vid_codec_param *param )
+{
+    pjmedia_vid_codec_factory *factory;
+    pj_status_t status;
+    pjmedia_codec_id codec_id;
+    pjmedia_vid_codec_desc *codec_desc = NULL;
+    unsigned i;
+
+    PJ_ASSERT_RETURN(info && param, PJ_EINVAL);
+
+    if (!mgr) mgr = def_vid_codec_mgr;
+    PJ_ASSERT_RETURN(mgr, PJ_EINVAL);
+
+    if (!pjmedia_vid_codec_info_to_id(info, (char*)&codec_id, 
+                                      sizeof(codec_id)))
+	return PJ_EINVAL;
+
+    pj_mutex_lock(mgr->mutex);
+
+    /* First, lookup default param in codec desc */
+    for (i=0; i < mgr->codec_cnt; ++i) {
+	if (pj_ansi_stricmp(codec_id, mgr->codec_desc[i].id) == 0) {
+	    codec_desc = &mgr->codec_desc[i];
+	    break;
+	}
+    }
+
+    /* If we found the codec and its default param is set, return it */
+    if (codec_desc && codec_desc->def_param) {
+	pj_memcpy(param, codec_desc->def_param->param, 
+		  sizeof(pjmedia_vid_codec_param));
+
+	pj_mutex_unlock(mgr->mutex);
+	return PJ_SUCCESS;
+    }
+
+    /* Otherwise query the default param from codec factory */
+    factory = mgr->factory_list.next;
+    while (factory != &mgr->factory_list) {
+
+	if ( (*factory->op->test_alloc)(factory, info) == PJ_SUCCESS ) {
+
+	    status = (*factory->op->default_attr)(factory, info, param);
+	    if (status == PJ_SUCCESS) {
+		/* Check for invalid max_bps. */
+		//if (param->info.max_bps < param->info.avg_bps)
+		//    param->info.max_bps = param->info.avg_bps;
+
+		pj_mutex_unlock(mgr->mutex);
+		return PJ_SUCCESS;
+	    }
+
+	}
+
+	factory = factory->next;
+    }
+
+    pj_mutex_unlock(mgr->mutex);
+
+
+    return PJMEDIA_CODEC_EUNSUP;
+}
+
+
+/*
+ * Set default codec parameter.
+ */
+PJ_DEF(pj_status_t) pjmedia_vid_codec_mgr_set_default_param( 
+					    pjmedia_vid_codec_mgr *mgr,
+					    const pjmedia_vid_codec_info *info,
+					    const pjmedia_vid_codec_param *param )
+{
+    unsigned i;
+    pjmedia_codec_id codec_id;
+    pjmedia_vid_codec_desc *codec_desc = NULL;
+    pj_pool_t *pool, *old_pool = NULL;
+    pjmedia_vid_codec_default_param *p;
+
+    PJ_ASSERT_RETURN(info, PJ_EINVAL);
+
+    if (!mgr) mgr = def_vid_codec_mgr;
+    PJ_ASSERT_RETURN(mgr, PJ_EINVAL);
+
+    if (!pjmedia_vid_codec_info_to_id(info, (char*)&codec_id, sizeof(codec_id)))
+	return PJ_EINVAL;
+
+    pj_mutex_lock(mgr->mutex);
+
+    /* Lookup codec desc */
+    for (i=0; i < mgr->codec_cnt; ++i) {
+	if (pj_ansi_stricmp(codec_id, mgr->codec_desc[i].id) == 0) {
+	    codec_desc = &mgr->codec_desc[i];
+	    break;
+	}
+    }
+
+    /* Codec not found */
+    if (!codec_desc) {
+	pj_mutex_unlock(mgr->mutex);
+	return PJMEDIA_CODEC_EUNSUP;
+    }
+
+    /* If codec param is previously set */
+    if (codec_desc->def_param) {
+	pj_assert(codec_desc->def_param->pool);
+        old_pool = codec_desc->def_param->pool;
+	codec_desc->def_param = NULL;
+    }
+
+    /* When param is set to NULL, i.e: setting default codec param to library
+     * default setting, just return PJ_SUCCESS.
+     */
+    if (NULL == param) {
+	pj_mutex_unlock(mgr->mutex);
+        if (old_pool)
+	    pj_pool_release(old_pool);
+	return PJ_SUCCESS;
+    }
+
+    /* Create new default codec param instance */
+    pool = pj_pool_create(mgr->pf, (char*)codec_id, 256, 256, NULL);
+    codec_desc->def_param = PJ_POOL_ZALLOC_T(pool,
+					     pjmedia_vid_codec_default_param);
+    p = codec_desc->def_param;
+    p->pool = pool;
+
+    /* Update codec default param */
+    p->param = pjmedia_vid_codec_param_clone(pool, param);
+    if (!p->param)
+	return PJ_EINVAL;
+
+    codec_desc->def_param = p;
+
+    pj_mutex_unlock(mgr->mutex);
+
+    /* Release old pool at the very end, as application tends to apply changes
+     * to the existing/old codec param fetched using
+     * pjmedia_vid_codec_mgr_get_default_param() which doesn't do deep clone.
+     */
+    if (old_pool)
+	pj_pool_release(old_pool);
+
+    return PJ_SUCCESS;
+}
+
+
+/*
+ * Dealloc codec.
+ */
+PJ_DEF(pj_status_t)
+pjmedia_vid_codec_mgr_dealloc_codec(pjmedia_vid_codec_mgr *mgr,
+				    pjmedia_vid_codec *codec)
+{
+    PJ_ASSERT_RETURN(codec, PJ_EINVAL);
+
+    if (!mgr) mgr = def_vid_codec_mgr;
+    PJ_ASSERT_RETURN(mgr, PJ_EINVAL);
+
+    return (*codec->factory->op->dealloc_codec)(codec->factory, codec);
+}
+
+
+#endif /* PJMEDIA_HAS_VIDEO */