* #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, ®1, ®1cnt,
+ ®2, ®2cnt);
+ 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, ®1, ®1cnt,
+ ®2, ®2cnt);
+ 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, ®1, ®1cnt,
+ ®2, ®2cnt);
+ 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, ®1, ®1cnt,
+ ®2, ®2cnt);
+
+ /* 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(¶m->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,
+ ¶m->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 */