* #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 = ®ion_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,
+ ®ion_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,
+ ®ion_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,
+ ®ion_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(¶m);
+
+ /* Create capture, set it to active (master) */
+ status = pjmedia_vid_dev_default_param(pool, cap_dev_id,
+ ¶m.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(¶m.vidparam.fmt, PJ_TRUE);
+ if (vfd == NULL) {
+ rc = 105; goto on_return;
+ }
+
+ status = pjmedia_vid_port_create(pool, ¶m, &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,
+ ¶m.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, ¶m, &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 */