* #36737: switch back to svn repo, remove assert in sip_transaction.c
diff --git a/jni/pjproject-android/.svn/pristine/6f/6f0004a582fd76b83ff69a123a7420827ace9cc9.svn-base b/jni/pjproject-android/.svn/pristine/6f/6f0004a582fd76b83ff69a123a7420827ace9cc9.svn-base
new file mode 100644
index 0000000..369a77a
--- /dev/null
+++ b/jni/pjproject-android/.svn/pristine/6f/6f0004a582fd76b83ff69a123a7420827ace9cc9.svn-base
@@ -0,0 +1,209 @@
+/*
+ * replay_driver.c
+ *
+ * A driver for the replay_database implementation
+ *
+ * David A. McGrew
+ * Cisco Systems, Inc.
+ */
+
+/*
+ *	
+ * Copyright (c) 2001-2006, Cisco Systems, Inc.
+ * All rights reserved.
+ * 
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 
+ *   Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ * 
+ *   Redistributions in binary form must reproduce the above
+ *   copyright notice, this list of conditions and the following
+ *   disclaimer in the documentation and/or other materials provided
+ *   with the distribution.
+ * 
+ *   Neither the name of the Cisco Systems, Inc. nor the names of its
+ *   contributors may be used to endorse or promote products derived
+ *   from this software without specific prior written permission.
+ * 
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+#include <stdio.h>
+
+#include "rdb.h"
+#include "ut_sim.h"
+
+/*
+ * num_trials defines the number of trials that are used in the
+ * validation functions below
+ */
+
+unsigned num_trials = 1 << 16;
+
+err_status_t
+test_rdb_db(void);
+
+double
+rdb_check_adds_per_second(void);
+
+int
+main (void) {
+  err_status_t err;
+  
+  printf("testing anti-replay database (rdb_t)...\n");
+  err = test_rdb_db();
+  if (err) {
+    printf("failed\n");
+    exit(1);
+  }
+  printf("done\n");
+
+  printf("rdb_check/rdb_adds per second: %e\n",
+	 rdb_check_adds_per_second());
+  
+  return 0;
+}
+
+
+void
+print_rdb(rdb_t *rdb) {
+  printf("rdb: {%u, %s}\n", rdb->window_start, v128_bit_string(&rdb->bitmask));
+}
+
+err_status_t
+rdb_check_add(rdb_t *rdb, uint32_t idx) {
+
+  if (rdb_check(rdb, idx) != err_status_ok) {
+    printf("rdb_check failed at index %u\n", idx);
+    return err_status_fail;
+  }
+  if (rdb_add_index(rdb, idx) != err_status_ok) {
+    printf("rdb_add_index failed at index %u\n", idx);
+    return err_status_fail;
+  }
+
+  return err_status_ok;
+}
+
+err_status_t
+rdb_check_expect_failure(rdb_t *rdb, uint32_t idx) {
+  err_status_t err;
+  
+  err = rdb_check(rdb, idx);
+  if ((err != err_status_replay_old) && (err != err_status_replay_fail)) {
+    printf("rdb_check failed at index %u (false positive)\n", idx);
+    return err_status_fail;
+  }
+
+  return err_status_ok;
+}
+
+err_status_t
+rdb_check_unordered(rdb_t *rdb, uint32_t idx) {
+  err_status_t rstat;
+
+ /* printf("index: %u\n", idx); */
+  rstat = rdb_check(rdb, idx);
+  if ((rstat != err_status_ok) && (rstat != err_status_replay_old)) {
+    printf("rdb_check_unordered failed at index %u\n", idx);
+    return rstat;
+  }
+  return err_status_ok;
+}
+
+err_status_t
+test_rdb_db() {
+  rdb_t rdb;
+  uint32_t idx, ircvd;
+  ut_connection utc;
+  err_status_t err;
+
+  if (rdb_init(&rdb) != err_status_ok) {
+    printf("rdb_init failed\n");
+    return err_status_init_fail;
+  }
+
+  /* test sequential insertion */
+  for (idx=0; idx < num_trials; idx++) {
+    err = rdb_check_add(&rdb, idx);
+    if (err) 
+      return err;
+  }
+
+  /* test for false positives */
+  for (idx=0; idx < num_trials; idx++) {
+    err = rdb_check_expect_failure(&rdb, idx);
+    if (err) 
+      return err;
+  }
+
+  /* re-initialize */
+  if (rdb_init(&rdb) != err_status_ok) {
+    printf("rdb_init failed\n");
+    return err_status_fail;
+  }
+
+  /* test non-sequential insertion */
+  ut_init(&utc);
+  
+  for (idx=0; idx < num_trials; idx++) {
+    ircvd = ut_next_index(&utc);
+    err = rdb_check_unordered(&rdb, ircvd);
+    if (err) 
+      return err;
+  }
+
+  return err_status_ok;
+}
+
+#include <time.h>       /* for clock()  */
+#include <stdlib.h>     /* for random() */
+
+#define REPLAY_NUM_TRIALS 10000000
+
+double
+rdb_check_adds_per_second(void) {
+  uint32_t i;
+  rdb_t rdb;
+  clock_t timer;
+  int failures;                    /* count number of failures        */
+  
+  if (rdb_init(&rdb) != err_status_ok) {
+    printf("rdb_init failed\n");
+    exit(1);
+  }  
+
+  timer = clock();
+  for(i=0; i < REPLAY_NUM_TRIALS; i+=3) {
+    if (rdb_check(&rdb, i+2) != err_status_ok)
+      ++failures;
+    if (rdb_add_index(&rdb, i+2) != err_status_ok)
+      ++failures;
+    if (rdb_check(&rdb, i+1) != err_status_ok)
+      ++failures;
+    if (rdb_add_index(&rdb, i+1) != err_status_ok)
+      ++failures;
+    if (rdb_check(&rdb, i) != err_status_ok)
+      ++failures;
+    if (rdb_add_index(&rdb, i) != err_status_ok)
+      ++failures;
+  }
+  timer = clock() - timer;
+
+  return (double) CLOCKS_PER_SEC * REPLAY_NUM_TRIALS / timer;
+}
diff --git a/jni/pjproject-android/.svn/pristine/6f/6f022f529d50e94a22bc2b6870728abe2a75e513.svn-base b/jni/pjproject-android/.svn/pristine/6f/6f022f529d50e94a22bc2b6870728abe2a75e513.svn-base
new file mode 100644
index 0000000..de23e6c
--- /dev/null
+++ b/jni/pjproject-android/.svn/pristine/6f/6f022f529d50e94a22bc2b6870728abe2a75e513.svn-base
@@ -0,0 +1,437 @@
+/* $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_CIRC_BUF_H__
+#define __PJMEDIA_CIRC_BUF_H__
+
+/**
+ * @file circbuf.h
+ * @brief Circular Buffer.
+ */
+
+#include <pj/assert.h>
+#include <pj/errno.h>
+#include <pj/pool.h>
+#include <pjmedia/frame.h>
+
+/**
+ * @defgroup PJMED_CIRCBUF Circular Buffer
+ * @ingroup PJMEDIA_FRAME_OP
+ * @brief Circular buffer manages read and write contiguous audio samples in a 
+ * non-contiguous buffer as if the buffer were contiguous. This should give
+ * better performance than keeping contiguous samples in a contiguous buffer,
+ * since read/write operations will only update the pointers, instead of 
+ * shifting audio samples.
+ *
+ * @{
+ *
+ * This section describes PJMEDIA's implementation of circular buffer.
+ */
+
+/* Algorithm checkings, for development purpose only */
+#if 0
+#   define PJMEDIA_CIRC_BUF_CHECK(x) pj_assert(x)
+#else
+#   define PJMEDIA_CIRC_BUF_CHECK(x)
+#endif
+
+PJ_BEGIN_DECL
+
+/** 
+ * Circular buffer structure
+ */
+typedef struct pjmedia_circ_buf {
+    pj_int16_t	    *buf;	    /**< The buffer			*/
+    unsigned	     capacity;	    /**< Buffer capacity, in samples	*/
+
+    pj_int16_t	    *start;	    /**< Pointer to the first sample	*/
+    unsigned	     len;	    /**< Audio samples length, 
+					 in samples			*/
+} pjmedia_circ_buf;
+
+
+/**
+ * Create the circular buffer.
+ *
+ * @param pool		    Pool where the circular buffer will be allocated
+ *			    from.
+ * @param capacity	    Capacity of the buffer, in samples.
+ * @param p_cb		    Pointer to receive the circular buffer instance.
+ *
+ * @return		    PJ_SUCCESS if the circular buffer has been
+ *			    created successfully, otherwise the appropriate
+ *			    error will be returned.
+ */
+PJ_INLINE(pj_status_t) pjmedia_circ_buf_create(pj_pool_t *pool, 
+					       unsigned capacity, 
+					       pjmedia_circ_buf **p_cb)
+{
+    pjmedia_circ_buf *cbuf;
+
+    cbuf = PJ_POOL_ZALLOC_T(pool, pjmedia_circ_buf);
+    cbuf->buf = (pj_int16_t*) pj_pool_calloc(pool, capacity, 
+					     sizeof(pj_int16_t));
+    cbuf->capacity = capacity;
+    cbuf->start = cbuf->buf;
+    cbuf->len = 0;
+
+    *p_cb = cbuf;
+
+    return PJ_SUCCESS;
+}
+
+
+/**
+ * Reset the circular buffer.
+ *
+ * @param circbuf	    The circular buffer.
+ *
+ * @return		    PJ_SUCCESS when successful.
+ */
+PJ_INLINE(pj_status_t) pjmedia_circ_buf_reset(pjmedia_circ_buf *circbuf)
+{
+    circbuf->start = circbuf->buf;
+    circbuf->len = 0;
+
+    return PJ_SUCCESS;
+}
+
+
+/**
+ * Get the circular buffer length, it is number of samples buffered in the 
+ * circular buffer.
+ *
+ * @param circbuf	    The circular buffer.
+ *
+ * @return		    The buffer length.
+ */
+PJ_INLINE(unsigned) pjmedia_circ_buf_get_len(pjmedia_circ_buf *circbuf)
+{
+    return circbuf->len;
+}
+
+
+/**
+ * Set circular buffer length. This is useful when audio buffer is manually 
+ * manipulated by the user, e.g: shrinked, expanded.
+ *
+ * @param circbuf	    The circular buffer.
+ * @param len		    The new buffer length.
+ */
+PJ_INLINE(void) pjmedia_circ_buf_set_len(pjmedia_circ_buf *circbuf,
+					 unsigned len)
+{
+    PJMEDIA_CIRC_BUF_CHECK(len <= circbuf->capacity);
+    circbuf->len = len;
+}
+
+
+/**
+ * Advance the read pointer of circular buffer. This function will discard
+ * the skipped samples while advancing the read pointer, thus reducing 
+ * the buffer length.
+ *
+ * @param circbuf	    The circular buffer.
+ * @param count		    Distance from current read pointer, can only be
+ *			    possitive number, in samples.
+ *
+ * @return		    PJ_SUCCESS when successful, otherwise 
+ *			    the appropriate error will be returned.
+ */
+PJ_INLINE(pj_status_t) pjmedia_circ_buf_adv_read_ptr(pjmedia_circ_buf *circbuf, 
+						     unsigned count)
+{
+    if (count >= circbuf->len)
+	return pjmedia_circ_buf_reset(circbuf);
+
+    PJMEDIA_CIRC_BUF_CHECK(count <= circbuf->len);
+
+    circbuf->start += count;
+    if (circbuf->start >= circbuf->buf + circbuf->capacity) 
+	circbuf->start -= circbuf->capacity;
+    circbuf->len -= count;
+
+    return PJ_SUCCESS;
+}
+
+
+/**
+ * Advance the write pointer of circular buffer. Since write pointer is always
+ * pointing to a sample after the end of sample, so this function also means
+ * increasing the buffer length.
+ *
+ * @param circbuf	    The circular buffer.
+ * @param count		    Distance from current write pointer, can only be
+ *			    possitive number, in samples.
+ *
+ * @return		    PJ_SUCCESS when successful, otherwise 
+ *			    the appropriate error will be returned.
+ */
+PJ_INLINE(pj_status_t) pjmedia_circ_buf_adv_write_ptr(pjmedia_circ_buf *circbuf,
+						      unsigned count)
+{
+    if (count + circbuf->len > circbuf->capacity)
+	return PJ_ETOOBIG;
+
+    circbuf->len += count;
+
+    return PJ_SUCCESS;
+}
+
+
+/**
+ * Get the real buffer addresses containing the audio samples.
+ *
+ * @param circbuf	    The circular buffer.
+ * @param reg1		    Pointer to store the first buffer address.
+ * @param reg1_len	    Pointer to store the length of the first buffer, 
+ *			    in samples.
+ * @param reg2		    Pointer to store the second buffer address.
+ * @param reg2_len	    Pointer to store the length of the second buffer, 
+ *			    in samples.
+ */
+PJ_INLINE(void) pjmedia_circ_buf_get_read_regions(pjmedia_circ_buf *circbuf, 
+						  pj_int16_t **reg1, 
+						  unsigned *reg1_len, 
+						  pj_int16_t **reg2, 
+						  unsigned *reg2_len)
+{
+    *reg1 = circbuf->start;
+    *reg1_len = circbuf->len;
+    if (*reg1 + *reg1_len > circbuf->buf + circbuf->capacity) {
+	*reg1_len = (unsigned)(circbuf->buf + circbuf->capacity - 
+			       circbuf->start);
+	*reg2 = circbuf->buf;
+	*reg2_len = circbuf->len - *reg1_len;
+    } else {
+	*reg2 = NULL;
+	*reg2_len = 0;
+    }
+
+    PJMEDIA_CIRC_BUF_CHECK(*reg1_len != 0 || (*reg1_len == 0 && 
+					      circbuf->len == 0));
+    PJMEDIA_CIRC_BUF_CHECK(*reg1_len + *reg2_len == circbuf->len);
+}
+
+
+/**
+ * Get the real buffer addresses that is empty or writeable.
+ *
+ * @param circbuf	    The circular buffer.
+ * @param reg1		    Pointer to store the first buffer address.
+ * @param reg1_len	    Pointer to store the length of the first buffer, 
+ *			    in samples.
+ * @param reg2		    Pointer to store the second buffer address.
+ * @param reg2_len	    Pointer to store the length of the second buffer, 
+ *			    in samples.
+ */
+PJ_INLINE(void) pjmedia_circ_buf_get_write_regions(pjmedia_circ_buf *circbuf, 
+						   pj_int16_t **reg1, 
+						   unsigned *reg1_len, 
+						   pj_int16_t **reg2, 
+						   unsigned *reg2_len)
+{
+    *reg1 = circbuf->start + circbuf->len;
+    if (*reg1 >= circbuf->buf + circbuf->capacity)
+	*reg1 -= circbuf->capacity;
+    *reg1_len = circbuf->capacity - circbuf->len;
+    if (*reg1 + *reg1_len > circbuf->buf + circbuf->capacity) {
+	*reg1_len = (unsigned)(circbuf->buf + circbuf->capacity - *reg1);
+	*reg2 = circbuf->buf;
+	*reg2_len = (unsigned)(circbuf->start - circbuf->buf);
+    } else {
+	*reg2 = NULL;
+	*reg2_len = 0;
+    }
+
+    PJMEDIA_CIRC_BUF_CHECK(*reg1_len != 0 || (*reg1_len == 0 && 
+					      circbuf->len == 0));
+    PJMEDIA_CIRC_BUF_CHECK(*reg1_len + *reg2_len == circbuf->capacity - 
+			   circbuf->len);
+}
+
+
+/**
+ * Read audio samples from the circular buffer.
+ *
+ * @param circbuf	    The circular buffer.
+ * @param data		    Buffer to store the read audio samples.
+ * @param count		    Number of samples being read.
+ *
+ * @return		    PJ_SUCCESS when successful, otherwise 
+ *			    the appropriate error will be returned.
+ */
+PJ_INLINE(pj_status_t) pjmedia_circ_buf_read(pjmedia_circ_buf *circbuf, 
+					     pj_int16_t *data, 
+					     unsigned count)
+{
+    pj_int16_t *reg1, *reg2;
+    unsigned reg1cnt, reg2cnt;
+
+    /* Data in the buffer is less than requested */
+    if (count > circbuf->len)
+	return PJ_ETOOBIG;
+
+    pjmedia_circ_buf_get_read_regions(circbuf, &reg1, &reg1cnt, 
+				      &reg2, &reg2cnt);
+    if (reg1cnt >= count) {
+	pjmedia_copy_samples(data, reg1, count);
+    } else {
+	pjmedia_copy_samples(data, reg1, reg1cnt);
+	pjmedia_copy_samples(data + reg1cnt, reg2, count - reg1cnt);
+    }
+
+    return pjmedia_circ_buf_adv_read_ptr(circbuf, count);
+}
+
+
+/**
+ * Write audio samples to the circular buffer.
+ *
+ * @param circbuf	    The circular buffer.
+ * @param data		    Audio samples to be written.
+ * @param count		    Number of samples being written.
+ *
+ * @return		    PJ_SUCCESS when successful, otherwise
+ *			    the appropriate error will be returned.
+ */
+PJ_INLINE(pj_status_t) pjmedia_circ_buf_write(pjmedia_circ_buf *circbuf, 
+					      pj_int16_t *data, 
+					      unsigned count)
+{
+    pj_int16_t *reg1, *reg2;
+    unsigned reg1cnt, reg2cnt;
+
+    /* Data to write is larger than buffer can store */
+    if (count > circbuf->capacity - circbuf->len)
+	return PJ_ETOOBIG;
+
+    pjmedia_circ_buf_get_write_regions(circbuf, &reg1, &reg1cnt, 
+				       &reg2, &reg2cnt);
+    if (reg1cnt >= count) {
+	pjmedia_copy_samples(reg1, data, count);
+    } else {
+	pjmedia_copy_samples(reg1, data, reg1cnt);
+	pjmedia_copy_samples(reg2, data + reg1cnt, count - reg1cnt);
+    }
+
+    return pjmedia_circ_buf_adv_write_ptr(circbuf, count);
+}
+
+
+/**
+ * Copy audio samples from the circular buffer without changing its state. 
+ *
+ * @param circbuf	    The circular buffer.
+ * @param start_idx	    Starting sample index to be copied.
+ * @param data		    Buffer to store the read audio samples.
+ * @param count		    Number of samples being read.
+ *
+ * @return		    PJ_SUCCESS when successful, otherwise 
+ *			    the appropriate error will be returned.
+ */
+PJ_INLINE(pj_status_t) pjmedia_circ_buf_copy(pjmedia_circ_buf *circbuf, 
+					     unsigned start_idx,
+					     pj_int16_t *data, 
+					     unsigned count)
+{
+    pj_int16_t *reg1, *reg2;
+    unsigned reg1cnt, reg2cnt;
+
+    /* Data in the buffer is less than requested */
+    if (count + start_idx > circbuf->len)
+	return PJ_ETOOBIG;
+
+    pjmedia_circ_buf_get_read_regions(circbuf, &reg1, &reg1cnt, 
+				      &reg2, &reg2cnt);
+    if (reg1cnt > start_idx) {
+	unsigned tmp_len;
+	tmp_len = reg1cnt - start_idx;
+	if (tmp_len > count)
+	    tmp_len = count;
+	pjmedia_copy_samples(data, reg1 + start_idx, tmp_len);
+	if (tmp_len < count)
+	    pjmedia_copy_samples(data + tmp_len, reg2, count - tmp_len);
+    } else {
+	pjmedia_copy_samples(data, reg2 + start_idx - reg1cnt, count);
+    }
+
+    return PJ_SUCCESS;
+}
+
+
+/**
+ * Pack the buffer so the first sample will be in the beginning of the buffer.
+ * This will also make the buffer contiguous.
+ *
+ * @param circbuf	    The circular buffer.
+ *
+ * @return		    PJ_SUCCESS when successful, otherwise 
+ *			    the appropriate error will be returned.
+ */
+PJ_INLINE(pj_status_t) pjmedia_circ_buf_pack_buffer(pjmedia_circ_buf *circbuf)
+{
+    pj_int16_t *reg1, *reg2;
+    unsigned reg1cnt, reg2cnt;
+    unsigned gap;
+
+    pjmedia_circ_buf_get_read_regions(circbuf, &reg1, &reg1cnt, 
+				      &reg2, &reg2cnt);
+
+    /* Check if not contigue */
+    if (reg2cnt != 0) {
+	/* Check if no space left to roll the buffer 
+	 * (or should this function provide temporary buffer?)
+	 */
+	gap = circbuf->capacity - pjmedia_circ_buf_get_len(circbuf);
+	if (gap == 0)
+	    return PJ_ETOOBIG;
+
+	/* Roll buffer left using the gap until reg2cnt == 0 */
+	do {
+	    if (gap > reg2cnt)
+		gap = reg2cnt;
+	    pjmedia_move_samples(reg1 - gap, reg1, reg1cnt);
+	    pjmedia_copy_samples(reg1 + reg1cnt - gap, reg2, gap);
+	    if (gap < reg2cnt)
+		pjmedia_move_samples(reg2, reg2 + gap, reg2cnt - gap);
+	    reg1 -= gap;
+	    reg1cnt += gap;
+	    reg2cnt -= gap;
+	} while (reg2cnt > 0);
+    }
+
+    /* Finally, Shift samples to the left edge */
+    if (reg1 != circbuf->buf)
+	pjmedia_move_samples(circbuf->buf, reg1, 
+			     pjmedia_circ_buf_get_len(circbuf));
+    circbuf->start = circbuf->buf;
+
+    return PJ_SUCCESS;
+}
+
+
+PJ_END_DECL
+
+/**
+ * @}
+ */
+
+#endif
diff --git a/jni/pjproject-android/.svn/pristine/6f/6f2d6f879b334bd469d520664e8a54a27ec30b2f.svn-base b/jni/pjproject-android/.svn/pristine/6f/6f2d6f879b334bd469d520664e8a54a27ec30b2f.svn-base
new file mode 100644
index 0000000..ac516bd
--- /dev/null
+++ b/jni/pjproject-android/.svn/pristine/6f/6f2d6f879b334bd469d520664e8a54a27ec30b2f.svn-base
@@ -0,0 +1,105 @@
+/* $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_WAV_PLAYLIST_H__
+#define __PJMEDIA_WAV_PLAYLIST_H__
+
+/**
+ * @file wav_playlist.h
+ * @brief WAV file playlist.
+ */
+#include <pjmedia/wav_port.h>
+
+
+
+PJ_BEGIN_DECL
+
+
+/**
+ * @defgroup PJMEDIA_WAV_PLAYLIST WAV File Play List
+ * @ingroup PJMEDIA_PORT
+ * @brief Audio playback of multiple WAV files
+ * @{
+ *
+ * The WAV play list port enables application to play back multiple
+ * WAV files in a playlist.
+ */
+
+/**
+ * Create a WAV playlist from the array of WAV file names. The WAV
+ * files must have the same clock rate, number of channels, and bits
+ * per sample, or otherwise this function will return error.
+ *
+ * @param pool		Pool to create memory buffers for this port.
+ * @param port_label	Optional label to set as the port name.
+ * @param file_list	Array of WAV file names.
+ * @param file_count	Number of files in the array.
+ * @param ptime		The duration (in miliseconds) of each frame read
+ *			from this port. If the value is zero, the default
+ *			duration (20ms) will be used.
+ * @param options	Optional options. Application may specify 
+ *			PJMEDIA_FILE_NO_LOOP to prevent play back loop.
+ * @param buff_size	Buffer size to be allocated. If the value is zero or
+ *			negative, the port will use default buffer size (which
+ *			is about 4KB).
+ * @param p_port	Pointer to receive the file port instance.
+ *
+ * @return		PJ_SUCCESS on success, or the appropriate error code.
+ */
+PJ_DECL(pj_status_t) pjmedia_wav_playlist_create(pj_pool_t *pool,
+						 const pj_str_t *port_label,
+						 const pj_str_t file_list[],
+						 int file_count,
+						 unsigned ptime,
+						 unsigned options,
+						 pj_ssize_t buff_size,
+						 pjmedia_port **p_port);
+
+
+/**
+ * Register a callback to be called when the file reading has reached the
+ * end of file of the last file. If the file is set to play repeatedly, 
+ * then the callback will be called multiple times. Note that only one 
+ * callback can be registered for each file port.
+ *
+ * @param port		The WAV play list port.
+ * @param user_data	User data to be specified in the callback
+ * @param cb		Callback to be called. If the callback returns non-
+ *			PJ_SUCCESS, the playback will stop. Note that if
+ *			application destroys the file port in the callback,
+ *			it must return non-PJ_SUCCESS here.
+ *
+ * @return		PJ_SUCCESS on success.
+ */
+PJ_DECL(pj_status_t)
+pjmedia_wav_playlist_set_eof_cb(pjmedia_port *port,
+			        void *user_data,
+			        pj_status_t (*cb)(pjmedia_port *port,
+						  void *usr_data));
+
+
+/**
+ * @}
+ */
+
+
+PJ_END_DECL
+
+
+#endif	/* __PJMEDIA_WAV_PLAYLIST_H__ */
diff --git a/jni/pjproject-android/.svn/pristine/6f/6f73d69dfa17061cdb9a89020f97c700ac51d0b5.svn-base b/jni/pjproject-android/.svn/pristine/6f/6f73d69dfa17061cdb9a89020f97c700ac51d0b5.svn-base
new file mode 100644
index 0000000..cef1f36
--- /dev/null
+++ b/jni/pjproject-android/.svn/pristine/6f/6f73d69dfa17061cdb9a89020f97c700ac51d0b5.svn-base
@@ -0,0 +1,697 @@
+/* $Id$ */
+/*
+ * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public 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-videodev/videodev_imp.h>
+#include <pj/assert.h>
+#include <pj/log.h>
+#include <pj/os.h>
+
+#if PJMEDIA_VIDEO_DEV_HAS_QT
+
+#include <Foundation/NSAutoreleasePool.h>
+#include <QTKit/QTKit.h>
+
+#define THIS_FILE		"qt_dev.c"
+#define DEFAULT_CLOCK_RATE	90000
+#define DEFAULT_WIDTH		640
+#define DEFAULT_HEIGHT		480
+#define DEFAULT_FPS		15
+
+#define kCVPixelFormatType_422YpCbCr8_yuvs 'yuvs'
+
+typedef struct qt_fmt_info
+{
+    pjmedia_format_id   pjmedia_format;
+    unsigned		qt_format;
+} qt_fmt_info;
+
+static qt_fmt_info qt_fmts[] =
+{
+    {PJMEDIA_FORMAT_YUY2, kCVPixelFormatType_422YpCbCr8_yuvs},
+    {PJMEDIA_FORMAT_UYVY, kCVPixelFormatType_422YpCbCr8},
+};
+
+/* qt device info */
+struct qt_dev_info
+{
+    pjmedia_vid_dev_info	 info;
+    char			 dev_id[192];
+};
+
+/* qt factory */
+struct qt_factory
+{
+    pjmedia_vid_dev_factory	 base;
+    pj_pool_t			*pool;
+    pj_pool_t			*dev_pool;
+    pj_pool_factory		*pf;
+
+    unsigned			 dev_count;
+    struct qt_dev_info		*dev_info;
+};
+
+struct qt_stream;
+typedef void (*func_ptr)(struct qt_stream *strm);
+
+@interface QTDelegate: NSObject
+{
+@public
+    struct qt_stream *strm;
+    func_ptr          func;
+}
+
+- (void)run_func;
+@end
+
+/* Video stream. */
+struct qt_stream
+{
+    pjmedia_vid_dev_stream  base;	    /**< Base stream	       */
+    pjmedia_vid_dev_param   param;	    /**< Settings	       */
+    pj_pool_t		   *pool;           /**< Memory pool.          */
+
+    pj_timestamp	    cap_frame_ts;   /**< Captured frame tstamp */
+    unsigned		    cap_ts_inc;	    /**< Increment	       */
+    
+    pjmedia_vid_dev_cb	    vid_cb;         /**< Stream callback.      */
+    void		   *user_data;      /**< Application data.     */
+
+    pj_bool_t		    cap_thread_exited;
+    pj_bool_t		    cap_thread_initialized;
+    pj_thread_desc	    cap_thread_desc;
+    pj_thread_t		   *cap_thread;
+    
+    struct qt_factory      *qf;
+    pj_status_t             status;
+    pj_bool_t               is_running;
+    pj_bool_t               cap_exited;
+    
+    QTCaptureSession			*cap_session;
+    QTCaptureDeviceInput		*dev_input;
+    QTCaptureDecompressedVideoOutput	*video_output;
+    QTDelegate                          *qt_delegate;
+};
+
+
+/* Prototypes */
+static pj_status_t qt_factory_init(pjmedia_vid_dev_factory *f);
+static pj_status_t qt_factory_destroy(pjmedia_vid_dev_factory *f);
+static pj_status_t qt_factory_refresh(pjmedia_vid_dev_factory *f);
+static unsigned    qt_factory_get_dev_count(pjmedia_vid_dev_factory *f);
+static pj_status_t qt_factory_get_dev_info(pjmedia_vid_dev_factory *f,
+					   unsigned index,
+					   pjmedia_vid_dev_info *info);
+static pj_status_t qt_factory_default_param(pj_pool_t *pool,
+					    pjmedia_vid_dev_factory *f,
+					    unsigned index,
+					    pjmedia_vid_dev_param *param);
+static pj_status_t qt_factory_create_stream(
+					pjmedia_vid_dev_factory *f,
+					pjmedia_vid_dev_param *param,
+					const pjmedia_vid_dev_cb *cb,
+					void *user_data,
+					pjmedia_vid_dev_stream **p_vid_strm);
+
+static pj_status_t qt_stream_get_param(pjmedia_vid_dev_stream *strm,
+				       pjmedia_vid_dev_param *param);
+static pj_status_t qt_stream_get_cap(pjmedia_vid_dev_stream *strm,
+				     pjmedia_vid_dev_cap cap,
+				     void *value);
+static pj_status_t qt_stream_set_cap(pjmedia_vid_dev_stream *strm,
+				     pjmedia_vid_dev_cap cap,
+				     const void *value);
+static pj_status_t qt_stream_start(pjmedia_vid_dev_stream *strm);
+static pj_status_t qt_stream_stop(pjmedia_vid_dev_stream *strm);
+static pj_status_t qt_stream_destroy(pjmedia_vid_dev_stream *strm);
+
+/* Operations */
+static pjmedia_vid_dev_factory_op factory_op =
+{
+    &qt_factory_init,
+    &qt_factory_destroy,
+    &qt_factory_get_dev_count,
+    &qt_factory_get_dev_info,
+    &qt_factory_default_param,
+    &qt_factory_create_stream,
+    &qt_factory_refresh
+};
+
+static pjmedia_vid_dev_stream_op stream_op =
+{
+    &qt_stream_get_param,
+    &qt_stream_get_cap,
+    &qt_stream_set_cap,
+    &qt_stream_start,
+    NULL,
+    NULL,
+    &qt_stream_stop,
+    &qt_stream_destroy
+};
+
+
+/****************************************************************************
+ * Factory operations
+ */
+/*
+ * Init qt_ video driver.
+ */
+pjmedia_vid_dev_factory* pjmedia_qt_factory(pj_pool_factory *pf)
+{
+    struct qt_factory *f;
+    pj_pool_t *pool;
+
+    pool = pj_pool_create(pf, "qt video", 4000, 4000, NULL);
+    f = PJ_POOL_ZALLOC_T(pool, struct qt_factory);
+    f->pf = pf;
+    f->pool = pool;
+    f->base.op = &factory_op;
+
+    return &f->base;
+}
+
+
+/* API: init factory */
+static pj_status_t qt_factory_init(pjmedia_vid_dev_factory *f)
+{
+    return qt_factory_refresh(f);
+}
+
+/* API: destroy factory */
+static pj_status_t qt_factory_destroy(pjmedia_vid_dev_factory *f)
+{
+    struct qt_factory *qf = (struct qt_factory*)f;
+    pj_pool_t *pool = qf->pool;
+
+    if (qf->dev_pool)
+        pj_pool_release(qf->dev_pool);
+    qf->pool = NULL;
+    if (pool)
+        pj_pool_release(pool);
+
+    return PJ_SUCCESS;
+}
+
+/* API: refresh the list of devices */
+static pj_status_t qt_factory_refresh(pjmedia_vid_dev_factory *f)
+{
+    struct qt_factory *qf = (struct qt_factory*)f;
+    struct qt_dev_info *qdi;
+    unsigned i, dev_count = 0;
+    NSAutoreleasePool *apool = [[NSAutoreleasePool alloc]init];
+    NSArray *dev_array;
+    
+    if (qf->dev_pool) {
+        pj_pool_release(qf->dev_pool);
+        qf->dev_pool = NULL;
+    }
+    
+    dev_array = [QTCaptureDevice inputDevices];
+    for (i = 0; i < [dev_array count]; i++) {
+	QTCaptureDevice *dev = [dev_array objectAtIndex:i];
+	if ([dev hasMediaType:QTMediaTypeVideo] ||
+	    [dev hasMediaType:QTMediaTypeMuxed])
+	{
+	    dev_count++;
+	}
+    }
+    
+    /* Initialize input and output devices here */
+    qf->dev_count = 0;
+    qf->dev_pool = pj_pool_create(qf->pf, "qt video", 500, 500, NULL);
+    
+    qf->dev_info = (struct qt_dev_info*)
+    pj_pool_calloc(qf->dev_pool, dev_count,
+                   sizeof(struct qt_dev_info));
+    for (i = 0; i < [dev_array count]; i++) {
+	QTCaptureDevice *dev = [dev_array objectAtIndex:i];
+	if ([dev hasMediaType:QTMediaTypeVideo] ||
+	    [dev hasMediaType:QTMediaTypeMuxed])
+	{
+	    unsigned k;
+	    
+	    qdi = &qf->dev_info[qf->dev_count++];
+	    pj_bzero(qdi, sizeof(*qdi));
+	    [[dev localizedDisplayName] getCString:qdi->info.name
+                                        maxLength:sizeof(qdi->info.name)
+                                        encoding:
+                                        [NSString defaultCStringEncoding]];
+	    [[dev uniqueID] getCString:qdi->dev_id
+                            maxLength:sizeof(qdi->dev_id)
+                            encoding:[NSString defaultCStringEncoding]];
+	    strcpy(qdi->info.driver, "QT");	    
+	    qdi->info.dir = PJMEDIA_DIR_CAPTURE;
+	    qdi->info.has_callback = PJ_TRUE;
+            
+	    qdi->info.fmt_cnt = 0;
+	    qdi->info.caps = PJMEDIA_VID_DEV_CAP_FORMAT;
+	    for (k = 0; k < [[dev formatDescriptions] count]; k++) {
+		unsigned l;
+		QTFormatDescription *desc = [[dev formatDescriptions]
+					     objectAtIndex:k];
+		for (l = 0; l < PJ_ARRAY_SIZE(qt_fmts); l++) {
+		    if ([desc formatType] == qt_fmts[l].qt_format) {
+			pjmedia_format *fmt = 
+                            &qdi->info.fmt[qdi->info.fmt_cnt++];
+			pjmedia_format_init_video(fmt,
+						  qt_fmts[l].pjmedia_format,
+						  DEFAULT_WIDTH,
+						  DEFAULT_HEIGHT,
+						  DEFAULT_FPS, 1);
+			break;
+		    }
+		}
+	    }
+            
+	    PJ_LOG(4, (THIS_FILE, " dev_id %d: %s", i, qdi->info.name));    
+	}
+    }
+    
+    [apool release];
+    
+    PJ_LOG(4, (THIS_FILE, "qt video has %d devices",
+	       qf->dev_count));
+    
+    return PJ_SUCCESS;
+}
+
+/* API: get number of devices */
+static unsigned qt_factory_get_dev_count(pjmedia_vid_dev_factory *f)
+{
+    struct qt_factory *qf = (struct qt_factory*)f;
+    return qf->dev_count;
+}
+
+/* API: get device info */
+static pj_status_t qt_factory_get_dev_info(pjmedia_vid_dev_factory *f,
+					   unsigned index,
+					   pjmedia_vid_dev_info *info)
+{
+    struct qt_factory *qf = (struct qt_factory*)f;
+
+    PJ_ASSERT_RETURN(index < qf->dev_count, PJMEDIA_EVID_INVDEV);
+
+    pj_memcpy(info, &qf->dev_info[index].info, sizeof(*info));
+
+    return PJ_SUCCESS;
+}
+
+/* API: create default device parameter */
+static pj_status_t qt_factory_default_param(pj_pool_t *pool,
+					    pjmedia_vid_dev_factory *f,
+					    unsigned index,
+					    pjmedia_vid_dev_param *param)
+{
+    struct qt_factory *qf = (struct qt_factory*)f;
+    struct qt_dev_info *di = &qf->dev_info[index];
+
+    PJ_ASSERT_RETURN(index < qf->dev_count, PJMEDIA_EVID_INVDEV);
+
+    PJ_UNUSED_ARG(pool);
+
+    pj_bzero(param, sizeof(*param));
+    param->dir = PJMEDIA_DIR_CAPTURE;
+    param->cap_id = index;
+    param->rend_id = PJMEDIA_VID_INVALID_DEV;
+    param->flags = PJMEDIA_VID_DEV_CAP_FORMAT;
+    param->clock_rate = DEFAULT_CLOCK_RATE;
+    pj_memcpy(&param->fmt, &di->info.fmt[0], sizeof(param->fmt));
+
+    return PJ_SUCCESS;
+}
+
+static qt_fmt_info* get_qt_format_info(pjmedia_format_id id)
+{
+    unsigned i;
+    
+    for (i = 0; i < PJ_ARRAY_SIZE(qt_fmts); i++) {
+        if (qt_fmts[i].pjmedia_format == id)
+            return &qt_fmts[i];
+    }
+    
+    return NULL;
+}
+
+@implementation QTDelegate
+- (void)captureOutput:(QTCaptureOutput *)captureOutput
+		      didOutputVideoFrame:(CVImageBufferRef)videoFrame
+		      withSampleBuffer:(QTSampleBuffer *)sampleBuffer
+		      fromConnection:(QTCaptureConnection *)connection
+{
+    unsigned size = [sampleBuffer lengthForAllSamples];
+    pjmedia_frame frame;
+
+    if (!strm->is_running) {
+        strm->cap_exited = PJ_TRUE;
+        return;
+    }
+    
+    if (strm->cap_thread_initialized == 0 || !pj_thread_is_registered())
+    {
+	pj_thread_register("qt_cap", strm->cap_thread_desc,
+			   &strm->cap_thread);
+	strm->cap_thread_initialized = 1;
+	PJ_LOG(5,(THIS_FILE, "Capture thread started"));
+    }
+    
+    if (!videoFrame)
+	return;
+    
+    frame.type = PJMEDIA_FRAME_TYPE_VIDEO;
+    frame.buf = [sampleBuffer bytesForAllSamples];
+    frame.size = size;
+    frame.bit_info = 0;
+    frame.timestamp.u64 = strm->cap_frame_ts.u64;
+    
+    if (strm->vid_cb.capture_cb)
+        (*strm->vid_cb.capture_cb)(&strm->base, strm->user_data, &frame);
+    
+    strm->cap_frame_ts.u64 += strm->cap_ts_inc;
+}
+
+- (void)run_func
+{
+    (*func)(strm);
+}
+
+@end
+
+static void init_qt(struct qt_stream *strm)
+{
+    const pjmedia_video_format_detail *vfd;
+    qt_fmt_info *qfi = get_qt_format_info(strm->param.fmt.id);
+    BOOL success = NO;
+    NSError *error;
+    
+    if (!qfi) {
+        strm->status = PJMEDIA_EVID_BADFORMAT;
+        return;
+    }
+    
+    strm->cap_session = [[QTCaptureSession alloc] init];
+    if (!strm->cap_session) {
+        strm->status = PJ_ENOMEM;
+        return;
+    }
+    
+    /* Open video device */
+    QTCaptureDevice *videoDevice = 
+        [QTCaptureDevice deviceWithUniqueID:
+                         [NSString stringWithCString:
+                                   strm->qf->dev_info[strm->param.cap_id].dev_id
+                                   encoding:
+                                   [NSString defaultCStringEncoding]]];
+    if (!videoDevice || ![videoDevice open:&error]) {
+        strm->status = PJMEDIA_EVID_SYSERR;
+        return;
+    }
+    
+    /* Add the video device to the session as a device input */	
+    strm->dev_input = [[QTCaptureDeviceInput alloc] 
+                       initWithDevice:videoDevice];
+    success = [strm->cap_session addInput:strm->dev_input error:&error];
+    if (!success) {
+        strm->status = PJMEDIA_EVID_SYSERR;
+        return;
+    }
+    
+    strm->video_output = [[QTCaptureDecompressedVideoOutput alloc] init];
+    success = [strm->cap_session addOutput:strm->video_output
+                                 error:&error];
+    if (!success) {
+        strm->status = PJMEDIA_EVID_SYSERR;
+        return;
+    }
+    
+    vfd = pjmedia_format_get_video_format_detail(&strm->param.fmt,
+                                                 PJ_TRUE);
+    [strm->video_output setPixelBufferAttributes:
+                        [NSDictionary dictionaryWithObjectsAndKeys:
+                                      [NSNumber numberWithInt:qfi->qt_format],
+                                      kCVPixelBufferPixelFormatTypeKey,
+                                      [NSNumber numberWithInt:vfd->size.w],
+                                      kCVPixelBufferWidthKey,
+                                      [NSNumber numberWithInt:vfd->size.h],
+                                      kCVPixelBufferHeightKey, nil]];
+    
+    pj_assert(vfd->fps.num);
+    strm->cap_ts_inc = PJMEDIA_SPF2(strm->param.clock_rate, &vfd->fps, 1);
+    
+    if ([strm->video_output
+         respondsToSelector:@selector(setMinimumVideoFrameInterval)])
+    {
+        [strm->video_output setMinimumVideoFrameInterval:
+                            (1.0f * vfd->fps.denum / (double)vfd->fps.num)];
+    }
+    
+    strm->qt_delegate = [[QTDelegate alloc]init];
+    strm->qt_delegate->strm = strm;
+    [strm->video_output setDelegate:strm->qt_delegate];
+}    
+
+static void run_func_on_main_thread(struct qt_stream *strm, func_ptr func)
+{
+    NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
+    QTDelegate *delg = [[QTDelegate alloc] init];
+    
+    delg->strm = strm;
+    delg->func = func;
+    [delg performSelectorOnMainThread:@selector(run_func)
+                           withObject:nil waitUntilDone:YES];
+    
+    CFRunLoopRunInMode(kCFRunLoopDefaultMode, 0, false);
+
+    [delg release];
+    [pool release];    
+}
+
+/* API: create stream */
+static pj_status_t qt_factory_create_stream(
+					pjmedia_vid_dev_factory *f,
+					pjmedia_vid_dev_param *param,
+					const pjmedia_vid_dev_cb *cb,
+					void *user_data,
+					pjmedia_vid_dev_stream **p_vid_strm)
+{
+    struct qt_factory *qf = (struct qt_factory*)f;
+    pj_pool_t *pool;
+    struct qt_stream *strm;
+    const pjmedia_video_format_info *vfi;
+    pj_status_t status = PJ_SUCCESS;
+
+    PJ_ASSERT_RETURN(f && param && p_vid_strm, PJ_EINVAL);
+    PJ_ASSERT_RETURN(param->fmt.type == PJMEDIA_TYPE_VIDEO &&
+		     param->fmt.detail_type == PJMEDIA_FORMAT_DETAIL_VIDEO &&
+                     param->dir == PJMEDIA_DIR_CAPTURE,
+		     PJ_EINVAL);
+
+    vfi = pjmedia_get_video_format_info(NULL, param->fmt.id);
+    if (!vfi)
+        return PJMEDIA_EVID_BADFORMAT;
+
+    /* Create and Initialize stream descriptor */
+    pool = pj_pool_create(qf->pf, "qt-dev", 4000, 4000, NULL);
+    PJ_ASSERT_RETURN(pool != NULL, PJ_ENOMEM);
+
+    strm = PJ_POOL_ZALLOC_T(pool, struct qt_stream);
+    pj_memcpy(&strm->param, param, sizeof(*param));
+    strm->pool = pool;
+    pj_memcpy(&strm->vid_cb, cb, sizeof(*cb));
+    strm->user_data = user_data;
+    strm->qf = qf;
+
+    /* Create capture stream here */
+    if (param->dir & PJMEDIA_DIR_CAPTURE) {        
+        strm->status = PJ_SUCCESS;
+        run_func_on_main_thread(strm, init_qt);
+        if ((status = strm->status) != PJ_SUCCESS)
+            goto on_error;
+    }
+    
+    /* Apply the remaining settings */
+    /*    
+     if (param->flags & PJMEDIA_VID_DEV_CAP_INPUT_SCALE) {
+	qt_stream_set_cap(&strm->base,
+			  PJMEDIA_VID_DEV_CAP_INPUT_SCALE,
+			  &param->fmt);
+     }
+     */
+    /* Done */
+    strm->base.op = &stream_op;
+    *p_vid_strm = &strm->base;
+    
+    return PJ_SUCCESS;
+    
+on_error:
+    qt_stream_destroy((pjmedia_vid_dev_stream *)strm);
+    
+    return status;
+}
+
+/* API: Get stream info. */
+static pj_status_t qt_stream_get_param(pjmedia_vid_dev_stream *s,
+				       pjmedia_vid_dev_param *pi)
+{
+    struct qt_stream *strm = (struct qt_stream*)s;
+
+    PJ_ASSERT_RETURN(strm && pi, PJ_EINVAL);
+
+    pj_memcpy(pi, &strm->param, sizeof(*pi));
+
+/*    if (qt_stream_get_cap(s, PJMEDIA_VID_DEV_CAP_INPUT_SCALE,
+                            &pi->fmt.info_size) == PJ_SUCCESS)
+    {
+        pi->flags |= PJMEDIA_VID_DEV_CAP_INPUT_SCALE;
+    }
+*/
+    return PJ_SUCCESS;
+}
+
+/* API: get capability */
+static pj_status_t qt_stream_get_cap(pjmedia_vid_dev_stream *s,
+				     pjmedia_vid_dev_cap cap,
+				     void *pval)
+{
+    struct qt_stream *strm = (struct qt_stream*)s;
+
+    PJ_UNUSED_ARG(strm);
+
+    PJ_ASSERT_RETURN(s && pval, PJ_EINVAL);
+
+    if (cap==PJMEDIA_VID_DEV_CAP_INPUT_SCALE)
+    {
+        return PJMEDIA_EVID_INVCAP;
+//	return PJ_SUCCESS;
+    } else {
+	return PJMEDIA_EVID_INVCAP;
+    }
+}
+
+/* API: set capability */
+static pj_status_t qt_stream_set_cap(pjmedia_vid_dev_stream *s,
+				     pjmedia_vid_dev_cap cap,
+				     const void *pval)
+{
+    struct qt_stream *strm = (struct qt_stream*)s;
+
+    PJ_UNUSED_ARG(strm);
+
+    PJ_ASSERT_RETURN(s && pval, PJ_EINVAL);
+
+    if (cap==PJMEDIA_VID_DEV_CAP_INPUT_SCALE)
+    {
+	return PJ_SUCCESS;
+    }
+
+    return PJMEDIA_EVID_INVCAP;
+}
+
+static void start_qt(struct qt_stream *strm)
+{
+    [strm->cap_session startRunning];
+}
+
+static void stop_qt(struct qt_stream *strm)
+{
+    [strm->cap_session stopRunning];
+}
+
+/* API: Start stream. */
+static pj_status_t qt_stream_start(pjmedia_vid_dev_stream *strm)
+{
+    struct qt_stream *stream = (struct qt_stream*)strm;
+
+    PJ_UNUSED_ARG(stream);
+
+    PJ_LOG(4, (THIS_FILE, "Starting qt video stream"));
+
+    if (stream->cap_session) {
+        run_func_on_main_thread(stream, start_qt);
+    
+	if (![stream->cap_session isRunning])
+	    return PJMEDIA_EVID_NOTREADY;
+        
+        stream->is_running = PJ_TRUE;
+    }
+
+    return PJ_SUCCESS;
+}
+
+/* API: Stop stream. */
+static pj_status_t qt_stream_stop(pjmedia_vid_dev_stream *strm)
+{
+    struct qt_stream *stream = (struct qt_stream*)strm;
+
+    PJ_UNUSED_ARG(stream);
+
+    PJ_LOG(4, (THIS_FILE, "Stopping qt video stream"));
+
+    if (stream->cap_session && [stream->cap_session isRunning]) {
+        int i;
+        
+        stream->cap_exited = PJ_FALSE;
+        run_func_on_main_thread(stream, stop_qt);
+        
+        stream->is_running = PJ_FALSE;
+        for (i = 50; i >= 0 && !stream->cap_exited; i--) {
+            pj_thread_sleep(10);
+        }
+    }
+    
+    return PJ_SUCCESS;
+}
+
+static void destroy_qt(struct qt_stream *strm)
+{
+    if (strm->dev_input && [[strm->dev_input device] isOpen])
+	[[strm->dev_input device] close];
+    
+    if (strm->cap_session) {
+	[strm->cap_session release];
+	strm->cap_session = NULL;
+    }
+    if (strm->dev_input) {
+	[strm->dev_input release];
+	strm->dev_input = NULL;
+    }
+    if (strm->qt_delegate) {
+	[strm->qt_delegate release];
+	strm->qt_delegate = NULL;
+    }
+    if (strm->video_output) {
+	[strm->video_output release];
+	strm->video_output = NULL;
+    }
+}
+
+/* API: Destroy stream. */
+static pj_status_t qt_stream_destroy(pjmedia_vid_dev_stream *strm)
+{
+    struct qt_stream *stream = (struct qt_stream*)strm;
+
+    PJ_ASSERT_RETURN(stream != NULL, PJ_EINVAL);
+
+    qt_stream_stop(strm);
+
+    run_func_on_main_thread(stream, destroy_qt);
+    
+    pj_pool_release(stream->pool);
+
+    return PJ_SUCCESS;
+}
+
+#endif	/* PJMEDIA_VIDEO_DEV_HAS_QT */