summaryrefslogtreecommitdiff
path: root/pjmedia/src/pjmedia
diff options
context:
space:
mode:
Diffstat (limited to 'pjmedia/src/pjmedia')
-rw-r--r--pjmedia/src/pjmedia/conf_switch.c1466
-rw-r--r--pjmedia/src/pjmedia/conference.c19
-rw-r--r--pjmedia/src/pjmedia/dsound.c1113
-rw-r--r--pjmedia/src/pjmedia/endpoint.c7
-rw-r--r--pjmedia/src/pjmedia/errno.c6
-rw-r--r--pjmedia/src/pjmedia/nullsound.c197
-rw-r--r--pjmedia/src/pjmedia/pasound.c1037
-rw-r--r--pjmedia/src/pjmedia/sound_legacy.c284
-rw-r--r--pjmedia/src/pjmedia/sound_port.c624
-rw-r--r--pjmedia/src/pjmedia/stream.c127
-rw-r--r--pjmedia/src/pjmedia/symbian_sound.cpp944
-rw-r--r--pjmedia/src/pjmedia/symbian_sound_aps.cpp920
-rw-r--r--pjmedia/src/pjmedia/transport_ice.c1
-rw-r--r--pjmedia/src/pjmedia/transport_loop.c1
-rw-r--r--pjmedia/src/pjmedia/transport_udp.c1
-rw-r--r--pjmedia/src/pjmedia/wmme_sound.c1008
16 files changed, 2232 insertions, 5523 deletions
diff --git a/pjmedia/src/pjmedia/conf_switch.c b/pjmedia/src/pjmedia/conf_switch.c
new file mode 100644
index 00000000..bf8ca504
--- /dev/null
+++ b/pjmedia/src/pjmedia/conf_switch.c
@@ -0,0 +1,1466 @@
+/* $Id$ */
+/*
+ * Copyright (C) 2008-2009 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/conference.h>
+#include <pjmedia/alaw_ulaw.h>
+#include <pjmedia/errno.h>
+#include <pjmedia/port.h>
+#include <pjmedia/silencedet.h>
+#include <pjmedia/sound_port.h>
+#include <pjmedia/stream.h>
+#include <pj/array.h>
+#include <pj/assert.h>
+#include <pj/log.h>
+#include <pj/pool.h>
+#include <pj/string.h>
+
+#if defined(PJMEDIA_CONF_USE_SWITCH_BOARD) && PJMEDIA_CONF_USE_SWITCH_BOARD!=0
+
+/* CONF_DEBUG enables detailed operation of the conference bridge.
+ * Beware that it prints large amounts of logs (several lines per frame).
+ */
+//#define CONF_DEBUG
+#ifdef CONF_DEBUG
+# include <stdio.h>
+# define TRACE_(x) PJ_LOG(5,x)
+#else
+# define TRACE_(x)
+#endif
+
+#define THIS_FILE "conf_switch.c"
+
+#define SIGNATURE PJMEDIA_CONF_SWITCH_SIGNATURE
+#define SIGNATURE_PORT PJMEDIA_PORT_SIGNATURE('S', 'W', 'T', 'P')
+#define NORMAL_LEVEL 128
+#define SLOT_TYPE unsigned
+#define INVALID_SLOT ((SLOT_TYPE)-1)
+#define BUFFER_SIZE PJMEDIA_MAX_MTU
+#define MAX_LEVEL (32767)
+#define MIN_LEVEL (-32768)
+
+/*
+ * DON'T GET CONFUSED WITH TX/RX!!
+ *
+ * TX and RX directions are always viewed from the conference bridge's point
+ * of view, and NOT from the port's point of view. So TX means the bridge
+ * is transmitting to the port, RX means the bridge is receiving from the
+ * port.
+ */
+
+
+/**
+ * This is a port connected to conference bridge.
+ */
+struct conf_port
+{
+ SLOT_TYPE slot; /**< Array of listeners. */
+ pj_str_t name; /**< Port name. */
+ pjmedia_port *port; /**< get_frame() and put_frame() */
+ pjmedia_port_op rx_setting; /**< Can we receive from this port */
+ pjmedia_port_op tx_setting; /**< Can we transmit to this port */
+ unsigned listener_cnt; /**< Number of listeners. */
+ SLOT_TYPE *listener_slots;/**< Array of listeners. */
+ unsigned transmitter_cnt;/**<Number of transmitters. */
+
+ /* Shortcut for port info. */
+ pjmedia_port_info *info;
+
+ /* Calculated signal levels: */
+ unsigned tx_level; /**< Last tx level to this port. */
+ unsigned rx_level; /**< Last rx level from this port. */
+
+ /* The normalized signal level adjustment.
+ * A value of 128 (NORMAL_LEVEL) means there's no adjustment.
+ */
+ unsigned tx_adj_level; /**< Adjustment for TX. */
+ unsigned rx_adj_level; /**< Adjustment for RX. */
+
+ pj_timestamp ts_clock;
+ pj_timestamp ts_rx;
+ pj_timestamp ts_tx;
+
+ /* Tx buffer is a temporary buffer to be used when there's mismatch
+ * between port's ptime with conference's ptime. This buffer is used as
+ * the source to buffer the samples until there are enough samples to
+ * fulfill a complete frame to be transmitted to the port.
+ */
+ pj_uint8_t tx_buf[BUFFER_SIZE]; /**< Tx buffer. */
+};
+
+
+/*
+ * Conference bridge.
+ */
+struct pjmedia_conf
+{
+ unsigned options; /**< Bitmask options. */
+ unsigned max_ports; /**< Maximum ports. */
+ unsigned port_cnt; /**< Current number of ports. */
+ unsigned connect_cnt; /**< Total number of connections */
+ pjmedia_port *master_port; /**< Port zero's port. */
+ char master_name_buf[80]; /**< Port0 name buffer. */
+ pj_mutex_t *mutex; /**< Conference mutex. */
+ struct conf_port **ports; /**< Array of ports. */
+ pj_uint8_t buf[BUFFER_SIZE]; /**< Common buffer. */
+};
+
+
+/* Prototypes */
+static pj_status_t put_frame(pjmedia_port *this_port,
+ const pjmedia_frame *frame);
+static pj_status_t get_frame(pjmedia_port *this_port,
+ pjmedia_frame *frame);
+static pj_status_t destroy_port(pjmedia_port *this_port);
+
+
+/*
+ * Create port.
+ */
+static pj_status_t create_conf_port( pj_pool_t *pool,
+ pjmedia_conf *conf,
+ pjmedia_port *port,
+ const pj_str_t *name,
+ struct conf_port **p_conf_port)
+{
+ struct conf_port *conf_port;
+ pjmedia_frame *f;
+
+ PJ_ASSERT_RETURN(pool && conf && port && name && p_conf_port, PJ_EINVAL);
+
+ /* Create port. */
+ conf_port = PJ_POOL_ZALLOC_T(pool, struct conf_port);
+
+ /* Set name */
+ pj_strdup_with_null(pool, &conf_port->name, name);
+
+ /* Default has tx and rx enabled. */
+ conf_port->rx_setting = PJMEDIA_PORT_ENABLE;
+ conf_port->tx_setting = PJMEDIA_PORT_ENABLE;
+
+ /* Default level adjustment is 128 (which means no adjustment) */
+ conf_port->tx_adj_level = NORMAL_LEVEL;
+ conf_port->rx_adj_level = NORMAL_LEVEL;
+
+ /* Create transmit flag array */
+ conf_port->listener_slots = (SLOT_TYPE*)
+ pj_pool_zalloc(pool,
+ conf->max_ports * sizeof(SLOT_TYPE));
+ PJ_ASSERT_RETURN(conf_port->listener_slots, PJ_ENOMEM);
+
+ /* Save some port's infos, for convenience. */
+ conf_port->port = port;
+ conf_port->info = &port->info;
+
+ /* Init pjmedia_frame structure in the TX buffer. */
+ f = (pjmedia_frame*)conf_port->tx_buf;
+ f->buf = conf_port->tx_buf + sizeof(pjmedia_frame);
+
+ /* Done */
+ *p_conf_port = conf_port;
+ return PJ_SUCCESS;
+}
+
+/*
+ * Create port zero for the sound device.
+ */
+static pj_status_t create_sound_port( pj_pool_t *pool,
+ pjmedia_conf *conf )
+{
+ struct conf_port *conf_port;
+ pj_str_t name = { "Master/sound", 12 };
+ pj_status_t status;
+
+ status = create_conf_port(pool, conf, conf->master_port, &name, &conf_port);
+ if (status != PJ_SUCCESS)
+ return status;
+
+ /* Add the port to the bridge */
+ conf_port->slot = 0;
+ conf->ports[0] = conf_port;
+ conf->port_cnt++;
+
+ PJ_LOG(5,(THIS_FILE, "Sound device successfully created for port 0"));
+ return PJ_SUCCESS;
+}
+
+/*
+ * Create conference bridge.
+ */
+PJ_DEF(pj_status_t) pjmedia_conf_create( pj_pool_t *pool,
+ unsigned max_ports,
+ unsigned clock_rate,
+ unsigned channel_count,
+ unsigned samples_per_frame,
+ unsigned bits_per_sample,
+ unsigned options,
+ pjmedia_conf **p_conf )
+{
+ pjmedia_conf *conf;
+ const pj_str_t name = { "Conf", 4 };
+ pj_status_t status;
+
+ /* Can only accept 16bits per sample, for now.. */
+ PJ_ASSERT_RETURN(bits_per_sample == 16, PJ_EINVAL);
+
+ PJ_LOG(5,(THIS_FILE, "Creating conference bridge with %d ports",
+ max_ports));
+
+ /* Create and init conf structure. */
+ conf = PJ_POOL_ZALLOC_T(pool, pjmedia_conf);
+ PJ_ASSERT_RETURN(conf, PJ_ENOMEM);
+
+ conf->ports = (struct conf_port**)
+ pj_pool_zalloc(pool, max_ports*sizeof(void*));
+ PJ_ASSERT_RETURN(conf->ports, PJ_ENOMEM);
+
+ conf->options = options;
+ conf->max_ports = max_ports;
+
+ /* Create and initialize the master port interface. */
+ conf->master_port = PJ_POOL_ZALLOC_T(pool, pjmedia_port);
+ PJ_ASSERT_RETURN(conf->master_port, PJ_ENOMEM);
+
+ pjmedia_port_info_init(&conf->master_port->info, &name, SIGNATURE,
+ clock_rate, channel_count, bits_per_sample,
+ samples_per_frame);
+
+ conf->master_port->port_data.pdata = conf;
+ conf->master_port->port_data.ldata = 0;
+
+ conf->master_port->get_frame = &get_frame;
+ conf->master_port->put_frame = &put_frame;
+ conf->master_port->on_destroy = &destroy_port;
+
+
+ /* Create port zero for sound device. */
+ status = create_sound_port(pool, conf);
+ if (status != PJ_SUCCESS)
+ return status;
+
+ /* Create mutex. */
+ status = pj_mutex_create_recursive(pool, "conf", &conf->mutex);
+ if (status != PJ_SUCCESS)
+ return status;
+
+ /* Done */
+
+ *p_conf = conf;
+
+ return PJ_SUCCESS;
+}
+
+
+/*
+ * Pause sound device.
+ */
+static pj_status_t pause_sound( pjmedia_conf *conf )
+{
+ /* Do nothing. */
+ PJ_UNUSED_ARG(conf);
+ return PJ_SUCCESS;
+}
+
+/*
+ * Resume sound device.
+ */
+static pj_status_t resume_sound( pjmedia_conf *conf )
+{
+ /* Do nothing. */
+ PJ_UNUSED_ARG(conf);
+ return PJ_SUCCESS;
+}
+
+
+/**
+ * Destroy conference bridge.
+ */
+PJ_DEF(pj_status_t) pjmedia_conf_destroy( pjmedia_conf *conf )
+{
+ PJ_ASSERT_RETURN(conf != NULL, PJ_EINVAL);
+
+ /* Destroy mutex */
+ pj_mutex_destroy(conf->mutex);
+
+ return PJ_SUCCESS;
+}
+
+
+/*
+ * Destroy the master port (will destroy the conference)
+ */
+static pj_status_t destroy_port(pjmedia_port *this_port)
+{
+ pjmedia_conf *conf = (pjmedia_conf*) this_port->port_data.pdata;
+ return pjmedia_conf_destroy(conf);
+}
+
+/*
+ * Get port zero interface.
+ */
+PJ_DEF(pjmedia_port*) pjmedia_conf_get_master_port(pjmedia_conf *conf)
+{
+ /* Sanity check. */
+ PJ_ASSERT_RETURN(conf != NULL, NULL);
+
+ /* Can only return port interface when PJMEDIA_CONF_NO_DEVICE was
+ * present in the option.
+ */
+ PJ_ASSERT_RETURN((conf->options & PJMEDIA_CONF_NO_DEVICE) != 0, NULL);
+
+ return conf->master_port;
+}
+
+
+/*
+ * Set master port name.
+ */
+PJ_DEF(pj_status_t) pjmedia_conf_set_port0_name(pjmedia_conf *conf,
+ const pj_str_t *name)
+{
+ unsigned len;
+
+ /* Sanity check. */
+ PJ_ASSERT_RETURN(conf != NULL && name != NULL, PJ_EINVAL);
+
+ len = name->slen;
+ if (len > sizeof(conf->master_name_buf))
+ len = sizeof(conf->master_name_buf);
+
+ if (len > 0) pj_memcpy(conf->master_name_buf, name->ptr, len);
+
+ conf->ports[0]->name.ptr = conf->master_name_buf;
+ conf->ports[0]->name.slen = len;
+
+ conf->master_port->info.name = conf->ports[0]->name;
+
+ return PJ_SUCCESS;
+}
+
+/*
+ * Add stream port to the conference bridge.
+ */
+PJ_DEF(pj_status_t) pjmedia_conf_add_port( pjmedia_conf *conf,
+ pj_pool_t *pool,
+ pjmedia_port *strm_port,
+ const pj_str_t *port_name,
+ unsigned *p_port )
+{
+ struct conf_port *conf_port;
+ unsigned index;
+ pj_status_t status;
+
+ PJ_ASSERT_RETURN(conf && pool && strm_port, PJ_EINVAL);
+ /*
+ PJ_ASSERT_RETURN(conf->clock_rate == strm_port->info.clock_rate,
+ PJMEDIA_ENCCLOCKRATE);
+ PJ_ASSERT_RETURN(conf->channel_count == strm_port->info.channel_count,
+ PJMEDIA_ENCCHANNEL);
+ PJ_ASSERT_RETURN(conf->bits_per_sample == strm_port->info.bits_per_sample,
+ PJMEDIA_ENCBITS);
+ */
+
+ /* Port's samples per frame should be equal to or multiplication of
+ * conference's samples per frame.
+ */
+ /*
+ Not sure if this is needed!
+ PJ_ASSERT_RETURN((conf->samples_per_frame %
+ strm_port->info.samples_per_frame==0) ||
+ (strm_port->info.samples_per_frame %
+ conf->samples_per_frame==0),
+ PJMEDIA_ENCSAMPLESPFRAME);
+ */
+
+ /* If port_name is not specified, use the port's name */
+ if (!port_name)
+ port_name = &strm_port->info.name;
+
+ pj_mutex_lock(conf->mutex);
+
+ if (conf->port_cnt >= conf->max_ports) {
+ pj_assert(!"Too many ports");
+ pj_mutex_unlock(conf->mutex);
+ return PJ_ETOOMANY;
+ }
+
+ /* Find empty port in the conference bridge. */
+ for (index=0; index < conf->max_ports; ++index) {
+ if (conf->ports[index] == NULL)
+ break;
+ }
+
+ pj_assert(index != conf->max_ports);
+
+ /* Create conf port structure. */
+ status = create_conf_port(pool, conf, strm_port, port_name, &conf_port);
+ if (status != PJ_SUCCESS) {
+ pj_mutex_unlock(conf->mutex);
+ return status;
+ }
+
+ /* Put the port. */
+ conf_port->slot = index;
+ conf->ports[index] = conf_port;
+ conf->port_cnt++;
+
+ /* Done. */
+ if (p_port) {
+ *p_port = index;
+ }
+
+ pj_mutex_unlock(conf->mutex);
+
+ return PJ_SUCCESS;
+}
+
+
+/*
+ * Add passive port.
+ */
+PJ_DEF(pj_status_t) pjmedia_conf_add_passive_port( pjmedia_conf *conf,
+ pj_pool_t *pool,
+ const pj_str_t *name,
+ unsigned clock_rate,
+ unsigned channel_count,
+ unsigned samples_per_frame,
+ unsigned bits_per_sample,
+ unsigned options,
+ unsigned *p_slot,
+ pjmedia_port **p_port )
+{
+ PJ_UNUSED_ARG(conf);
+ PJ_UNUSED_ARG(pool);
+ PJ_UNUSED_ARG(name);
+ PJ_UNUSED_ARG(clock_rate);
+ PJ_UNUSED_ARG(channel_count);
+ PJ_UNUSED_ARG(samples_per_frame);
+ PJ_UNUSED_ARG(bits_per_sample);
+ PJ_UNUSED_ARG(options);
+ PJ_UNUSED_ARG(p_slot);
+ PJ_UNUSED_ARG(p_port);
+
+ return PJ_ENOTSUP;
+}
+
+
+
+/*
+ * Change TX and RX settings for the port.
+ */
+PJ_DEF(pj_status_t) pjmedia_conf_configure_port( pjmedia_conf *conf,
+ unsigned slot,
+ pjmedia_port_op tx,
+ pjmedia_port_op rx)
+{
+ struct conf_port *conf_port;
+
+ /* Check arguments */
+ PJ_ASSERT_RETURN(conf && slot<conf->max_ports, PJ_EINVAL);
+
+ /* Port must be valid. */
+ PJ_ASSERT_RETURN(conf->ports[slot] != NULL, PJ_EINVAL);
+
+ conf_port = conf->ports[slot];
+
+ if (tx != PJMEDIA_PORT_NO_CHANGE)
+ conf_port->tx_setting = tx;
+
+ if (rx != PJMEDIA_PORT_NO_CHANGE)
+ conf_port->rx_setting = rx;
+
+ return PJ_SUCCESS;
+}
+
+
+/*
+ * Connect port.
+ */
+PJ_DEF(pj_status_t) pjmedia_conf_connect_port( pjmedia_conf *conf,
+ unsigned src_slot,
+ unsigned sink_slot,
+ int level )
+{
+ struct conf_port *src_port, *dst_port;
+ pj_bool_t start_sound = PJ_FALSE;
+ unsigned i;
+
+ /* Check arguments */
+ PJ_ASSERT_RETURN(conf && src_slot<conf->max_ports &&
+ sink_slot<conf->max_ports, PJ_EINVAL);
+
+ /* Ports must be valid. */
+ PJ_ASSERT_RETURN(conf->ports[src_slot] != NULL, PJ_EINVAL);
+ PJ_ASSERT_RETURN(conf->ports[sink_slot] != NULL, PJ_EINVAL);
+
+ /* For now, level MUST be zero. */
+ PJ_ASSERT_RETURN(level == 0, PJ_EINVAL);
+
+ pj_mutex_lock(conf->mutex);
+
+ src_port = conf->ports[src_slot];
+ dst_port = conf->ports[sink_slot];
+
+ /* Format must match. */
+ if (src_port->info->format.id != dst_port->info->format.id ||
+ src_port->info->format.bitrate != dst_port->info->format.bitrate)
+ {
+ pj_mutex_unlock(conf->mutex);
+ return PJMEDIA_ENOTCOMPATIBLE;
+ }
+
+ /* Clock rate must match. */
+ if (src_port->info->clock_rate != dst_port->info->clock_rate) {
+ pj_mutex_unlock(conf->mutex);
+ return PJMEDIA_ENCCLOCKRATE;
+ }
+
+ /* Channel count must match. */
+ if (src_port->info->channel_count != dst_port->info->channel_count) {
+ pj_mutex_unlock(conf->mutex);
+ return PJMEDIA_ENCCLOCKRATE;
+ }
+
+ /* Source and sink ptime must be equal or a multiplication factor. */
+ if ((src_port->info->samples_per_frame %
+ dst_port->info->samples_per_frame != 0) &&
+ (dst_port->info->samples_per_frame %
+ src_port->info->samples_per_frame != 0))
+ {
+ pj_mutex_unlock(conf->mutex);
+ return PJMEDIA_ENCSAMPLESPFRAME;
+ }
+
+ /* Check if sink is listening to other ports */
+ if (dst_port->transmitter_cnt > 0) {
+ pj_mutex_unlock(conf->mutex);
+ return PJ_ETOOMANYCONN;
+ }
+
+ /* Check if connection has been made */
+ for (i=0; i<src_port->listener_cnt; ++i) {
+ if (src_port->listener_slots[i] == sink_slot)
+ break;
+ }
+
+ if (i == src_port->listener_cnt) {
+ src_port->listener_slots[src_port->listener_cnt] = sink_slot;
+ ++conf->connect_cnt;
+ ++src_port->listener_cnt;
+ ++dst_port->transmitter_cnt;
+
+ if (conf->connect_cnt == 1)
+ start_sound = 1;
+
+ PJ_LOG(4,(THIS_FILE,"Port %d (%.*s) transmitting to port %d (%.*s)",
+ src_slot,
+ (int)src_port->name.slen,
+ src_port->name.ptr,
+ sink_slot,
+ (int)dst_port->name.slen,
+ dst_port->name.ptr));
+ }
+
+ pj_mutex_unlock(conf->mutex);
+
+ /* Sound device must be started without mutex, otherwise the
+ * sound thread will deadlock (?)
+ */
+ if (start_sound)
+ resume_sound(conf);
+
+ return PJ_SUCCESS;
+}
+
+
+/*
+ * Disconnect port
+ */
+PJ_DEF(pj_status_t) pjmedia_conf_disconnect_port( pjmedia_conf *conf,
+ unsigned src_slot,
+ unsigned sink_slot )
+{
+ struct conf_port *src_port, *dst_port;
+ unsigned i;
+
+ /* Check arguments */
+ PJ_ASSERT_RETURN(conf && src_slot<conf->max_ports &&
+ sink_slot<conf->max_ports, PJ_EINVAL);
+
+ /* Ports must be valid. */
+ PJ_ASSERT_RETURN(conf->ports[src_slot] != NULL, PJ_EINVAL);
+ PJ_ASSERT_RETURN(conf->ports[sink_slot] != NULL, PJ_EINVAL);
+
+ pj_mutex_lock(conf->mutex);
+
+ src_port = conf->ports[src_slot];
+ dst_port = conf->ports[sink_slot];
+
+ /* Check if connection has been made */
+ for (i=0; i<src_port->listener_cnt; ++i) {
+ if (src_port->listener_slots[i] == sink_slot)
+ break;
+ }
+
+ if (i != src_port->listener_cnt) {
+ pjmedia_frame_ext *f;
+
+ pj_assert(src_port->listener_cnt > 0 &&
+ src_port->listener_cnt < conf->max_ports);
+ pj_assert(dst_port->transmitter_cnt > 0 &&
+ dst_port->transmitter_cnt < conf->max_ports);
+ pj_array_erase(src_port->listener_slots, sizeof(SLOT_TYPE),
+ src_port->listener_cnt, i);
+ --conf->connect_cnt;
+ --src_port->listener_cnt;
+ --dst_port->transmitter_cnt;
+
+ /* Cleanup listener TX buffer. */
+ f = (pjmedia_frame_ext*)dst_port->tx_buf;
+ f->base.type = PJMEDIA_FRAME_TYPE_NONE;
+ f->base.size = 0;
+ f->samples_cnt = 0;
+ f->subframe_cnt = 0;
+
+ PJ_LOG(4,(THIS_FILE,
+ "Port %d (%.*s) stop transmitting to port %d (%.*s)",
+ src_slot,
+ (int)src_port->name.slen,
+ src_port->name.ptr,
+ sink_slot,
+ (int)dst_port->name.slen,
+ dst_port->name.ptr));
+ }
+
+ pj_mutex_unlock(conf->mutex);
+
+ if (conf->connect_cnt == 0) {
+ pause_sound(conf);
+ }
+
+ return PJ_SUCCESS;
+}
+
+/*
+ * Get number of ports currently registered to the conference bridge.
+ */
+PJ_DEF(unsigned) pjmedia_conf_get_port_count(pjmedia_conf *conf)
+{
+ return conf->port_cnt;
+}
+
+/*
+ * Get total number of ports connections currently set up in the bridge.
+ */
+PJ_DEF(unsigned) pjmedia_conf_get_connect_count(pjmedia_conf *conf)
+{
+ return conf->connect_cnt;
+}
+
+
+/*
+ * Remove the specified port.
+ */
+PJ_DEF(pj_status_t) pjmedia_conf_remove_port( pjmedia_conf *conf,
+ unsigned port )
+{
+ struct conf_port *conf_port;
+ unsigned i;
+
+ /* Check arguments */
+ PJ_ASSERT_RETURN(conf && port < conf->max_ports, PJ_EINVAL);
+
+ /* Port must be valid. */
+ PJ_ASSERT_RETURN(conf->ports[port] != NULL, PJ_EINVAL);
+
+ /* Suspend the sound devices.
+ * Don't want to remove port while port is being accessed by sound
+ * device's threads!
+ */
+
+ pj_mutex_lock(conf->mutex);
+
+ conf_port = conf->ports[port];
+ conf_port->tx_setting = PJMEDIA_PORT_DISABLE;
+ conf_port->rx_setting = PJMEDIA_PORT_DISABLE;
+
+ /* Remove this port from transmit array of other ports. */
+ for (i=0; i<conf->max_ports; ++i) {
+ unsigned j;
+ struct conf_port *src_port;
+
+ src_port = conf->ports[i];
+
+ if (!src_port)
+ continue;
+
+ if (src_port->listener_cnt == 0)
+ continue;
+
+ for (j=0; j<src_port->listener_cnt; ++j) {
+ if (src_port->listener_slots[j] == port) {
+ pj_array_erase(src_port->listener_slots, sizeof(SLOT_TYPE),
+ src_port->listener_cnt, j);
+ pj_assert(conf->connect_cnt > 0);
+ --conf->connect_cnt;
+ --src_port->listener_cnt;
+ break;
+ }
+ }
+ }
+
+ /* Update transmitter_cnt of ports we're transmitting to */
+ while (conf_port->listener_cnt) {
+ unsigned dst_slot;
+ struct conf_port *dst_port;
+ pjmedia_frame_ext *f;
+
+ dst_slot = conf_port->listener_slots[conf_port->listener_cnt-1];
+ dst_port = conf->ports[dst_slot];
+ --dst_port->transmitter_cnt;
+ --conf_port->listener_cnt;
+ pj_assert(conf->connect_cnt > 0);
+ --conf->connect_cnt;
+
+ /* Cleanup & reinit listener TX buffer. */
+ f = (pjmedia_frame_ext*)dst_port->tx_buf;
+ f->base.type = PJMEDIA_FRAME_TYPE_NONE;
+ f->base.size = 0;
+ f->samples_cnt = 0;
+ f->subframe_cnt = 0;
+ }
+
+ /* Remove the port. */
+ conf->ports[port] = NULL;
+ --conf->port_cnt;
+
+ pj_mutex_unlock(conf->mutex);
+
+
+ /* Stop sound if there's no connection. */
+ if (conf->connect_cnt == 0) {
+ pause_sound(conf);
+ }
+
+ return PJ_SUCCESS;
+}
+
+
+/*
+ * Enum ports.
+ */
+PJ_DEF(pj_status_t) pjmedia_conf_enum_ports( pjmedia_conf *conf,
+ unsigned ports[],
+ unsigned *p_count )
+{
+ unsigned i, count=0;
+
+ PJ_ASSERT_RETURN(conf && p_count && ports, PJ_EINVAL);
+
+ for (i=0; i<conf->max_ports && count<*p_count; ++i) {
+ if (!conf->ports[i])
+ continue;
+
+ ports[count++] = i;
+ }
+
+ *p_count = count;
+ return PJ_SUCCESS;
+}
+
+/*
+ * Get port info
+ */
+PJ_DEF(pj_status_t) pjmedia_conf_get_port_info( pjmedia_conf *conf,
+ unsigned slot,
+ pjmedia_conf_port_info *info)
+{
+ struct conf_port *conf_port;
+
+ /* Check arguments */
+ PJ_ASSERT_RETURN(conf && slot<conf->max_ports, PJ_EINVAL);
+
+ /* Port must be valid. */
+ PJ_ASSERT_RETURN(conf->ports[slot] != NULL, PJ_EINVAL);
+
+ conf_port = conf->ports[slot];
+
+ pj_bzero(info, sizeof(pjmedia_conf_port_info));
+
+ info->slot = slot;
+ info->name = conf_port->name;
+ info->tx_setting = conf_port->tx_setting;
+ info->rx_setting = conf_port->rx_setting;
+ info->listener_cnt = conf_port->listener_cnt;
+ info->listener_slots = conf_port->listener_slots;
+ info->transmitter_cnt = conf_port->transmitter_cnt;
+ info->clock_rate = conf_port->info->clock_rate;
+ info->channel_count = conf_port->info->channel_count;
+ info->samples_per_frame = conf_port->info->samples_per_frame;
+ info->bits_per_sample = conf_port->info->bits_per_sample;
+ info->format = conf_port->port->info.format;
+ info->tx_adj_level = conf_port->tx_adj_level - NORMAL_LEVEL;
+ info->rx_adj_level = conf_port->rx_adj_level - NORMAL_LEVEL;
+
+ return PJ_SUCCESS;
+}
+
+
+PJ_DEF(pj_status_t) pjmedia_conf_get_ports_info(pjmedia_conf *conf,
+ unsigned *size,
+ pjmedia_conf_port_info info[])
+{
+ unsigned i, count=0;
+
+ PJ_ASSERT_RETURN(conf && size && info, PJ_EINVAL);
+
+ for (i=0; i<conf->max_ports && count<*size; ++i) {
+ if (!conf->ports[i])
+ continue;
+
+ pjmedia_conf_get_port_info(conf, i, &info[count]);
+ ++count;
+ }
+
+ *size = count;
+ return PJ_SUCCESS;
+}
+
+
+/*
+ * Get signal level.
+ */
+PJ_DEF(pj_status_t) pjmedia_conf_get_signal_level( pjmedia_conf *conf,
+ unsigned slot,
+ unsigned *tx_level,
+ unsigned *rx_level)
+{
+ struct conf_port *conf_port;
+
+ /* Check arguments */
+ PJ_ASSERT_RETURN(conf && slot<conf->max_ports, PJ_EINVAL);
+
+ /* Port must be valid. */
+ PJ_ASSERT_RETURN(conf->ports[slot] != NULL, PJ_EINVAL);
+
+ conf_port = conf->ports[slot];
+
+ if (tx_level != NULL) {
+ *tx_level = conf_port->tx_level;
+ }
+
+ if (rx_level != NULL)
+ *rx_level = conf_port->rx_level;
+
+ return PJ_SUCCESS;
+}
+
+
+/*
+ * Adjust RX level of individual port.
+ */
+PJ_DEF(pj_status_t) pjmedia_conf_adjust_rx_level( pjmedia_conf *conf,
+ unsigned slot,
+ int adj_level )
+{
+ struct conf_port *conf_port;
+
+ /* Check arguments */
+ PJ_ASSERT_RETURN(conf && slot<conf->max_ports, PJ_EINVAL);
+
+ /* Port must be valid. */
+ PJ_ASSERT_RETURN(conf->ports[slot] != NULL, PJ_EINVAL);
+
+ /* Value must be from -128 to +127 */
+ /* Disabled, you can put more than +127, at your own risk:
+ PJ_ASSERT_RETURN(adj_level >= -128 && adj_level <= 127, PJ_EINVAL);
+ */
+ PJ_ASSERT_RETURN(adj_level >= -128, PJ_EINVAL);
+
+ conf_port = conf->ports[slot];
+
+ /* Level adjustment is applicable only for ports that work with raw PCM. */
+ PJ_ASSERT_RETURN(conf_port->info->format.id == PJMEDIA_FORMAT_L16,
+ PJ_EIGNORED);
+
+ /* Set normalized adjustment level. */
+ conf_port->rx_adj_level = adj_level + NORMAL_LEVEL;
+
+ return PJ_SUCCESS;
+}
+
+
+/*
+ * Adjust TX level of individual port.
+ */
+PJ_DEF(pj_status_t) pjmedia_conf_adjust_tx_level( pjmedia_conf *conf,
+ unsigned slot,
+ int adj_level )
+{
+ struct conf_port *conf_port;
+
+ /* Check arguments */
+ PJ_ASSERT_RETURN(conf && slot<conf->max_ports, PJ_EINVAL);
+
+ /* Port must be valid. */
+ PJ_ASSERT_RETURN(conf->ports[slot] != NULL, PJ_EINVAL);
+
+ /* Value must be from -128 to +127 */
+ /* Disabled, you can put more than +127,, at your own risk:
+ PJ_ASSERT_RETURN(adj_level >= -128 && adj_level <= 127, PJ_EINVAL);
+ */
+ PJ_ASSERT_RETURN(adj_level >= -128, PJ_EINVAL);
+
+ conf_port = conf->ports[slot];
+
+ /* Level adjustment is applicable only for ports that work with raw PCM. */
+ PJ_ASSERT_RETURN(conf_port->info->format.id == PJMEDIA_FORMAT_L16,
+ PJ_EIGNORED);
+
+ /* Set normalized adjustment level. */
+ conf_port->tx_adj_level = adj_level + NORMAL_LEVEL;
+
+ return PJ_SUCCESS;
+}
+
+/* Deliver frm_src to a listener port, eventually call port's put_frame()
+ * when samples count in the frm_dst are equal to port's samples_per_frame.
+ */
+static pj_status_t write_frame(struct conf_port *cport_dst,
+ const pjmedia_frame *frm_src)
+{
+ pjmedia_frame *frm_dst = (pjmedia_frame*)cport_dst->tx_buf;
+
+ PJ_TODO(MAKE_SURE_DEST_FRAME_HAS_ENOUGH_SPACE);
+
+ frm_dst->type = frm_src->type;
+ frm_dst->timestamp = cport_dst->ts_tx;
+
+ if (frm_src->type == PJMEDIA_FRAME_TYPE_EXTENDED) {
+
+ pjmedia_frame_ext *f_src = (pjmedia_frame_ext*)frm_src;
+ pjmedia_frame_ext *f_dst = (pjmedia_frame_ext*)frm_dst;
+ unsigned i;
+
+ for (i = 0; i < f_src->subframe_cnt; ++i) {
+ pjmedia_frame_ext_subframe *sf;
+
+ /* Copy frame to listener's TX buffer. */
+ sf = pjmedia_frame_ext_get_subframe(f_src, i);
+ pjmedia_frame_ext_append_subframe(f_dst, sf->data, sf->bitlen,
+ f_src->samples_cnt /
+ f_src->subframe_cnt);
+
+ /* Check if it's time to deliver the TX buffer to listener,
+ * i.e: samples count in TX buffer equal to listener's
+ * samples per frame.
+ */
+ if (f_dst->samples_cnt >= cport_dst->info->samples_per_frame)
+ {
+ if (cport_dst->slot) {
+ pjmedia_port_put_frame(cport_dst->port,
+ (pjmedia_frame*)f_dst);
+
+ /* Reset TX buffer. */
+ f_dst->subframe_cnt = 0;
+ f_dst->samples_cnt = 0;
+ }
+
+ /* Update TX timestamp. */
+ pj_add_timestamp32(&cport_dst->ts_tx,
+ cport_dst->info->samples_per_frame);
+ }
+ }
+
+ } else if (frm_src->type == PJMEDIA_FRAME_TYPE_AUDIO) {
+
+ pj_int16_t *f_start, *f_end;
+
+ f_start = (pj_int16_t*)frm_src->buf;
+ f_end = f_start + (frm_src->size >> 1);
+
+ while (f_start < f_end) {
+ unsigned nsamples_to_copy, nsamples_req;
+
+ /* Copy frame to listener's TX buffer. */
+ nsamples_to_copy = f_end - f_start;
+ nsamples_req = cport_dst->info->samples_per_frame -
+ (frm_dst->size>>1);
+ if (nsamples_to_copy > nsamples_req)
+ nsamples_to_copy = nsamples_req;
+
+ /* Adjust TX level. */
+ if (cport_dst->tx_adj_level != NORMAL_LEVEL) {
+ pj_int16_t *p, *p_end;
+
+ p = f_start;
+ p_end = p + nsamples_to_copy;
+ while (p < p_end) {
+ pj_int32_t itemp = *p;
+
+ /* Adjust the level */
+ itemp = (itemp * cport_dst->tx_adj_level) >> 7;
+
+ /* Clip the signal if it's too loud */
+ if (itemp > MAX_LEVEL) itemp = MAX_LEVEL;
+ else if (itemp < MIN_LEVEL) itemp = MIN_LEVEL;
+
+ /* Put back in the buffer. */
+ *p = (pj_int16_t)itemp;
+ ++p;
+ }
+ }
+
+ pjmedia_copy_samples((pj_int16_t*)frm_dst->buf + (frm_dst->size>>1),
+ f_start,
+ nsamples_to_copy);
+ frm_dst->size += nsamples_to_copy << 1;
+ f_start += nsamples_to_copy;
+
+ /* Check if it's time to deliver the TX buffer to listener,
+ * i.e: samples count in TX buffer equal to listener's
+ * samples per frame.
+ */
+ if ((frm_dst->size >> 1) == cport_dst->info->samples_per_frame)
+ {
+ if (cport_dst->slot) {
+ pjmedia_port_put_frame(cport_dst->port, frm_dst);
+
+ /* Reset TX buffer. */
+ frm_dst->size = 0;
+ }
+
+ /* Update TX timestamp. */
+ pj_add_timestamp32(&cport_dst->ts_tx,
+ cport_dst->info->samples_per_frame);
+ }
+ }
+
+ } else if (frm_src->type == PJMEDIA_FRAME_TYPE_NONE) {
+
+ /* Check port format. */
+ if (cport_dst->port &&
+ cport_dst->port->info.format.id == PJMEDIA_FORMAT_L16)
+ {
+ /* When there is already some samples in listener's TX buffer,
+ * pad the buffer with "zero samples".
+ */
+ if (frm_dst->size != 0) {
+ pjmedia_zero_samples((pj_int16_t*)frm_dst->buf,
+ cport_dst->info->samples_per_frame -
+ (frm_dst->size>>1));
+
+ frm_dst->type = PJMEDIA_FRAME_TYPE_AUDIO;
+ frm_dst->size = cport_dst->info->samples_per_frame << 1;
+ if (cport_dst->slot) {
+ pjmedia_port_put_frame(cport_dst->port, frm_dst);
+
+ /* Reset TX buffer. */
+ frm_dst->size = 0;
+ }
+
+ /* Update TX timestamp. */
+ pj_add_timestamp32(&cport_dst->ts_tx,
+ cport_dst->info->samples_per_frame);
+ }
+ } else {
+ pjmedia_frame_ext *f_dst = (pjmedia_frame_ext*)frm_dst;
+
+ if (f_dst->samples_cnt != 0) {
+ frm_dst->type = PJMEDIA_FRAME_TYPE_EXTENDED;
+ pjmedia_frame_ext_append_subframe(f_dst, NULL, 0, (pj_uint16_t)
+ (cport_dst->info->samples_per_frame - f_dst->samples_cnt));
+ if (cport_dst->slot) {
+ pjmedia_port_put_frame(cport_dst->port, frm_dst);
+
+ /* Reset TX buffer. */
+ f_dst->subframe_cnt = 0;
+ f_dst->samples_cnt = 0;
+ }
+
+ /* Update TX timestamp. */
+ pj_add_timestamp32(&cport_dst->ts_tx,
+ cport_dst->info->samples_per_frame);
+ }
+ }
+
+ /* Synchronize clock. */
+ while (pj_cmp_timestamp(&cport_dst->ts_clock,
+ &cport_dst->ts_tx) > 0)
+ {
+ frm_dst->type = PJMEDIA_FRAME_TYPE_NONE;
+ frm_dst->timestamp = cport_dst->ts_tx;
+ if (cport_dst->slot)
+ pjmedia_port_put_frame(cport_dst->port, frm_dst);
+
+ /* Update TX timestamp. */
+ pj_add_timestamp32(&cport_dst->ts_tx, cport_dst->info->samples_per_frame);
+ }
+ }
+
+ return PJ_SUCCESS;
+}
+
+/*
+ * Player callback.
+ */
+static pj_status_t get_frame(pjmedia_port *this_port,
+ pjmedia_frame *frame)
+{
+ pjmedia_conf *conf = (pjmedia_conf*) this_port->port_data.pdata;
+ unsigned ci, i;
+
+ /* Must lock mutex */
+ pj_mutex_lock(conf->mutex);
+
+ /* Call get_frame() from all ports (except port 0) that has
+ * receiver and distribute the frame (put the frame to the destination
+ * port's buffer to accommodate different ptime, and ultimately call
+ * put_frame() of that port) to ports that are receiving from this port.
+ */
+ for (i=1, ci=1; i<conf->max_ports && ci<conf->port_cnt; ++i) {
+ struct conf_port *cport = conf->ports[i];
+
+ /* Skip empty port. */
+ if (!cport)
+ continue;
+
+ /* Var "ci" is to count how many ports have been visited so far. */
+ ++ci;
+
+ /* Update clock of the port. */
+ pj_add_timestamp32(&cport->ts_clock,
+ conf->master_port->info.samples_per_frame);
+
+ /* Skip if we're not allowed to receive from this port or
+ * the port doesn't have listeners.
+ */
+ if (cport->rx_setting == PJMEDIA_PORT_DISABLE ||
+ cport->listener_cnt == 0)
+ {
+ cport->rx_level = 0;
+ pj_add_timestamp32(&cport->ts_rx,
+ conf->master_port->info.samples_per_frame);
+ continue;
+ }
+
+ /* Get frame from each port, put it to the listener TX buffer,
+ * and eventually call put_frame() of the listener. This loop
+ * will also make sure the ptime between conf & port synchronized.
+ */
+ while (pj_cmp_timestamp(&cport->ts_clock, &cport->ts_rx) > 0) {
+ pjmedia_frame *f = (pjmedia_frame*)conf->buf;
+ pj_status_t status;
+ unsigned j;
+ pj_int32_t level = 0;
+
+ pj_add_timestamp32(&cport->ts_rx, cport->info->samples_per_frame);
+
+ f->buf = &conf->buf[sizeof(pjmedia_frame)];
+ f->size = cport->info->samples_per_frame<<1;
+
+ /* Get frame from port. */
+ status = pjmedia_port_get_frame(cport->port, f);
+ if (status != PJ_SUCCESS)
+ continue;
+
+ /* Calculate & adjust RX level. */
+ if (f->type == PJMEDIA_FRAME_TYPE_AUDIO) {
+ if (cport->rx_adj_level != NORMAL_LEVEL) {
+ pj_int16_t *p = (pj_int16_t*)f->buf;
+ pj_int16_t *end;
+
+ end = p + (f->size >> 1);
+ while (p < end) {
+ pj_int32_t itemp = *p;
+
+ /* Adjust the level */
+ itemp = (itemp * cport->rx_adj_level) >> 7;
+
+ /* Clip the signal if it's too loud */
+ if (itemp > MAX_LEVEL) itemp = MAX_LEVEL;
+ else if (itemp < MIN_LEVEL) itemp = MIN_LEVEL;
+
+ level += PJ_ABS(itemp);
+
+ /* Put back in the buffer. */
+ *p = (pj_int16_t)itemp;
+ ++p;
+ }
+ level /= (f->size >> 1);
+ } else {
+ level = pjmedia_calc_avg_signal((const pj_int16_t*)f->buf,
+ f->size >> 1);
+ }
+ } else if (f->type == PJMEDIA_FRAME_TYPE_EXTENDED) {
+ /* For extended frame, level is unknown, so we just set
+ * it to NORMAL_LEVEL.
+ */
+ level = NORMAL_LEVEL;
+ }
+
+ cport->rx_level = pjmedia_linear2ulaw(level) ^ 0xff;
+
+ /* Put the frame to all listeners. */
+ for (j=0; j < cport->listener_cnt; ++j)
+ {
+ struct conf_port *listener;
+
+ listener = conf->ports[cport->listener_slots[j]];
+
+ /* Skip if this listener doesn't want to receive audio */
+ if (listener->tx_setting == PJMEDIA_PORT_DISABLE) {
+ pj_add_timestamp32(&listener->ts_tx,
+ listener->info->samples_per_frame);
+ listener->tx_level = 0;
+ continue;
+ }
+
+ status = write_frame(listener, f);
+ if (status != PJ_SUCCESS) {
+ listener->tx_level = 0;
+ continue;
+ }
+
+ /* Set listener TX level based on transmitter RX level &
+ * listener TX level.
+ */
+ listener->tx_level = (cport->rx_level * listener->tx_adj_level)
+ >> 8;
+ }
+ }
+ }
+
+ /* Keep alive. Update TX timestamp and send frame type NONE to all
+ * underflow ports at their own clock.
+ */
+ for (i=1, ci=1; i<conf->max_ports && ci<conf->port_cnt; ++i) {
+ struct conf_port *cport = conf->ports[i];
+
+ /* Skip empty port. */
+ if (!cport)
+ continue;
+
+ /* Var "ci" is to count how many ports have been visited so far. */
+ ++ci;
+
+ if (cport->tx_setting==PJMEDIA_PORT_MUTE || cport->transmitter_cnt==0)
+ {
+ pjmedia_frame_ext *f;
+
+ /* Clear left-over samples in tx_buffer, if any, so that it won't
+ * be transmitted next time we have audio signal.
+ */
+ f = (pjmedia_frame_ext*)cport->tx_buf;
+ f->base.type = PJMEDIA_FRAME_TYPE_NONE;
+ f->base.size = 0;
+ f->samples_cnt = 0;
+ f->subframe_cnt = 0;
+
+ cport->tx_level = 0;
+
+ while (pj_cmp_timestamp(&cport->ts_clock, &cport->ts_tx) > 0)
+ {
+ if (cport->tx_setting == PJMEDIA_PORT_ENABLE) {
+ pjmedia_frame tmp_f;
+
+ tmp_f.timestamp = cport->ts_tx;
+ tmp_f.type = PJMEDIA_FRAME_TYPE_NONE;
+ tmp_f.buf = NULL;
+ tmp_f.size = 0;
+
+ pjmedia_port_put_frame(cport->port, &tmp_f);
+ pj_add_timestamp32(&cport->ts_tx, cport->info->samples_per_frame);
+ }
+ }
+ }
+ }
+
+ /* Return sound playback frame. */
+ do {
+ struct conf_port *this_cport = conf->ports[this_port->port_data.ldata];
+ pjmedia_frame *f_src = (pjmedia_frame*) this_cport->tx_buf;
+
+ frame->type = f_src->type;
+
+ if (f_src->type == PJMEDIA_FRAME_TYPE_EXTENDED) {
+ pjmedia_frame_ext *f_src_ = (pjmedia_frame_ext*)f_src;
+ pjmedia_frame_ext *f_dst = (pjmedia_frame_ext*)frame;
+ pjmedia_frame_ext_subframe *sf;
+ unsigned samples_per_subframe;
+
+ if (f_src_->samples_cnt < this_cport->info->samples_per_frame) {
+ f_dst->base.type = PJMEDIA_FRAME_TYPE_NONE;
+ f_dst->samples_cnt = 0;
+ f_dst->subframe_cnt = 0;
+ break;
+ }
+
+ f_dst->samples_cnt = 0;
+ f_dst->subframe_cnt = 0;
+ i = 0;
+ samples_per_subframe = f_src_->samples_cnt / f_src_->subframe_cnt;
+
+
+ while (f_dst->samples_cnt < this_cport->info->samples_per_frame) {
+ sf = pjmedia_frame_ext_get_subframe(f_src_, i++);
+ pj_assert(sf);
+ pjmedia_frame_ext_append_subframe(f_dst, sf->data, sf->bitlen,
+ samples_per_subframe);
+ }
+
+ /* Shift left TX buffer. */
+ pjmedia_frame_ext_pop_subframes(f_src_, i);
+
+ } else if (f_src->type == PJMEDIA_FRAME_TYPE_AUDIO) {
+ if ((f_src->size>>1) < this_cport->info->samples_per_frame) {
+ frame->type = PJMEDIA_FRAME_TYPE_NONE;
+ frame->size = 0;
+ break;
+ }
+
+ pjmedia_copy_samples((pj_int16_t*)frame->buf,
+ (pj_int16_t*)f_src->buf,
+ this_cport->info->samples_per_frame);
+ frame->size = this_cport->info->samples_per_frame << 1;
+
+ /* Shift left TX buffer. */
+ f_src->size -= frame->size;
+ if (f_src->size)
+ pjmedia_move_samples((pj_int16_t*)f_src->buf,
+ (pj_int16_t*)f_src->buf +
+ this_cport->info->samples_per_frame,
+ f_src->size >> 1);
+ } else { /* PJMEDIA_FRAME_TYPE_NONE */
+ pjmedia_frame_ext *f_src_ = (pjmedia_frame_ext*)f_src;
+
+ /* Reset source/TX buffer */
+ f_src_->base.size = 0;
+ f_src_->samples_cnt = 0;
+ f_src_->subframe_cnt = 0;
+ }
+ } while (0);
+
+ /* Unlock mutex */
+ pj_mutex_unlock(conf->mutex);
+
+ return PJ_SUCCESS;
+}
+
+/*
+ * Recorder callback.
+ */
+static pj_status_t put_frame(pjmedia_port *this_port,
+ const pjmedia_frame *f)
+{
+ pjmedia_conf *conf = (pjmedia_conf*) this_port->port_data.pdata;
+ struct conf_port *cport = conf->ports[this_port->port_data.ldata];
+ unsigned j;
+ pj_int32_t level = 0;
+
+ pj_add_timestamp32(&cport->ts_rx, cport->info->samples_per_frame);
+
+ /* Skip if this port is muted/disabled. */
+ if (cport->rx_setting == PJMEDIA_PORT_DISABLE) {
+ cport->rx_level = 0;
+ return PJ_SUCCESS;
+ }
+
+ /* Skip if no port is listening to the microphone */
+ if (cport->listener_cnt == 0) {
+ cport->rx_level = 0;
+ return PJ_SUCCESS;
+ }
+
+ /* Calculate & adjust RX level. */
+ if (f->type == PJMEDIA_FRAME_TYPE_AUDIO) {
+ if (cport->rx_adj_level != NORMAL_LEVEL) {
+ pj_int16_t *p = (pj_int16_t*)f->buf;
+ pj_int16_t *end;
+
+ end = p + (f->size >> 1);
+ while (p < end) {
+ pj_int32_t itemp = *p;
+
+ /* Adjust the level */
+ itemp = (itemp * cport->rx_adj_level) >> 7;
+
+ /* Clip the signal if it's too loud */
+ if (itemp > MAX_LEVEL) itemp = MAX_LEVEL;
+ else if (itemp < MIN_LEVEL) itemp = MIN_LEVEL;
+
+ level += PJ_ABS(itemp);
+
+ /* Put back in the buffer. */
+ *p = (pj_int16_t)itemp;
+ ++p;
+ }
+ level /= (f->size >> 1);
+ } else {
+ level = pjmedia_calc_avg_signal((const pj_int16_t*)f->buf,
+ f->size >> 1);
+ }
+ } else if (f->type == PJMEDIA_FRAME_TYPE_EXTENDED) {
+ /* For extended frame, level is unknown, so we just set
+ * it to NORMAL_LEVEL.
+ */
+ level = NORMAL_LEVEL;
+ }
+
+ cport->rx_level = pjmedia_linear2ulaw(level) ^ 0xff;
+
+ /* Put the frame to all listeners. */
+ for (j=0; j < cport->listener_cnt; ++j)
+ {
+ struct conf_port *listener;
+ pj_status_t status;
+
+ listener = conf->ports[cport->listener_slots[j]];
+
+ /* Skip if this listener doesn't want to receive audio */
+ if (listener->tx_setting == PJMEDIA_PORT_DISABLE) {
+ pj_add_timestamp32(&listener->ts_tx,
+ listener->info->samples_per_frame);
+ listener->tx_level = 0;
+ continue;
+ }
+
+ /* Skip loopback for now. */
+ if (listener == cport) {
+ pj_add_timestamp32(&listener->ts_tx,
+ listener->info->samples_per_frame);
+ listener->tx_level = 0;
+ continue;
+ }
+
+ status = write_frame(listener, f);
+ if (status != PJ_SUCCESS) {
+ listener->tx_level = 0;
+ continue;
+ }
+
+ /* Set listener TX level based on transmitter RX level & listener
+ * TX level.
+ */
+ listener->tx_level = (cport->rx_level * listener->tx_adj_level) >> 8;
+ }
+
+ return PJ_SUCCESS;
+}
+
+#endif
diff --git a/pjmedia/src/pjmedia/conference.c b/pjmedia/src/pjmedia/conference.c
index 9cae6a74..c39ec036 100644
--- a/pjmedia/src/pjmedia/conference.c
+++ b/pjmedia/src/pjmedia/conference.c
@@ -33,6 +33,7 @@
#include <pj/pool.h>
#include <pj/string.h>
+#if !defined(PJMEDIA_CONF_USE_SWITCH_BOARD) || PJMEDIA_CONF_USE_SWITCH_BOARD==0
/* CONF_DEBUG enables detailed operation of the conference bridge.
* Beware that it prints large amounts of logs (several lines per frame).
@@ -63,7 +64,7 @@ static FILE *fhnd_rec;
#define BYTES_PER_SAMPLE 2
-#define SIGNATURE PJMEDIA_PORT_SIGNATURE('C', 'O', 'N', 'F')
+#define SIGNATURE PJMEDIA_CONF_BRIDGE_SIGNATURE
#define SIGNATURE_PORT PJMEDIA_PORT_SIGNATURE('C', 'O', 'N', 'P')
/* Normal level is hardcodec to 128 in all over places */
#define NORMAL_LEVEL 128
@@ -464,8 +465,8 @@ static pj_status_t create_sound_port( pj_pool_t *pool,
/* Create sound device port: */
if ((conf->options & PJMEDIA_CONF_NO_DEVICE) == 0) {
- pjmedia_snd_stream *strm;
- pjmedia_snd_stream_info si;
+ pjmedia_aud_stream *strm;
+ pjmedia_aud_param param;
/*
* If capture is disabled then create player only port.
@@ -493,14 +494,14 @@ static pj_status_t create_sound_port( pj_pool_t *pool,
return status;
strm = pjmedia_snd_port_get_snd_stream(conf->snd_dev_port);
- status = pjmedia_snd_stream_get_info(strm, &si);
+ status = pjmedia_aud_stream_get_param(strm, &param);
if (status == PJ_SUCCESS) {
- const pjmedia_snd_dev_info *snd_dev_info;
+ pjmedia_aud_dev_info snd_dev_info;
if (conf->options & PJMEDIA_CONF_NO_MIC)
- snd_dev_info = pjmedia_snd_get_dev_info(si.play_id);
+ pjmedia_aud_dev_get_info(param.play_id, &snd_dev_info);
else
- snd_dev_info = pjmedia_snd_get_dev_info(si.rec_id);
- pj_strdup2_with_null(pool, &conf_port->name, snd_dev_info->name);
+ pjmedia_aud_dev_get_info(param.rec_id, &snd_dev_info);
+ pj_strdup2_with_null(pool, &conf_port->name, snd_dev_info.name);
}
}
@@ -1170,6 +1171,7 @@ PJ_DEF(pj_status_t) pjmedia_conf_get_port_info( pjmedia_conf *conf,
info->rx_setting = conf_port->rx_setting;
info->listener_cnt = conf_port->listener_cnt;
info->listener_slots = conf_port->listener_slots;
+ info->transmitter_cnt = conf_port->transmitter_cnt;
info->clock_rate = conf_port->clock_rate;
info->channel_count = conf_port->channel_count;
info->samples_per_frame = conf_port->samples_per_frame;
@@ -1987,3 +1989,4 @@ static pj_status_t put_frame(pjmedia_port *this_port,
return status;
}
+#endif
diff --git a/pjmedia/src/pjmedia/dsound.c b/pjmedia/src/pjmedia/dsound.c
deleted file mode 100644
index 367a6974..00000000
--- a/pjmedia/src/pjmedia/dsound.c
+++ /dev/null
@@ -1,1113 +0,0 @@
-/* $Id$ */
-/*
- * Copyright (C) 2008-2009 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/sound.h>
-#include <pjmedia/errno.h>
-#include <pj/assert.h>
-#include <pj/log.h>
-#include <pj/os.h>
-#include <pj/string.h>
-
-#if PJMEDIA_SOUND_IMPLEMENTATION == PJMEDIA_SOUND_WIN32_DIRECT_SOUND
-
-#define PJ_MIN(x, y) ((x < y) ? (x) : (y))
-
-#ifdef _MSC_VER
-# pragma warning(push, 3)
-#endif
-
-#include <windows.h>
-#include <mmsystem.h>
-#include <dsound.h>
-
-#ifdef _MSC_VER
-# pragma warning(pop)
-#endif
-
-
-#define THIS_FILE "dsound.c"
-#define BITS_PER_SAMPLE 16
-#define BYTES_PER_SAMPLE (BITS_PER_SAMPLE/8)
-
-#define MAX_PACKET_BUFFER_COUNT 50
-#define MIN_PACKET_BUFFER_COUNT 2
-
-#define MAX_HARDWARE 16
-
-struct dsound_dev_info
-{
- pjmedia_snd_dev_info info;
- LPGUID lpGuid;
- GUID guid;
-};
-
-static unsigned dev_count;
-static struct dsound_dev_info dev_info[MAX_HARDWARE];
-static int snd_init_count;
-
-/* Latency settings */
-static unsigned snd_input_latency = PJMEDIA_SND_DEFAULT_REC_LATENCY;
-static unsigned snd_output_latency = PJMEDIA_SND_DEFAULT_PLAY_LATENCY;
-
-
-/* Individual DirectSound capture/playback stream descriptor */
-struct dsound_stream
-{
- union
- {
- struct
- {
- LPDIRECTSOUND lpDs;
- LPDIRECTSOUNDBUFFER lpDsBuffer;
- } play;
-
- struct
- {
- LPDIRECTSOUNDCAPTURE lpDs;
- LPDIRECTSOUNDCAPTUREBUFFER lpDsBuffer;
- } capture;
- } ds;
-
- HANDLE hEvent;
- LPDIRECTSOUNDNOTIFY lpDsNotify;
- DWORD dwBytePos;
- DWORD dwDsBufferSize;
- pj_timestamp timestamp;
- unsigned latency;
-};
-
-
-/* Sound stream. */
-struct pjmedia_snd_stream
-{
- pjmedia_dir dir; /**< Sound direction. */
- int play_id; /**< Playback dev id. */
- int rec_id; /**< Recording dev id. */
- pj_pool_t *pool; /**< Memory pool. */
-
- pjmedia_snd_rec_cb rec_cb; /**< Capture callback. */
- pjmedia_snd_play_cb play_cb; /**< Playback callback. */
- void *user_data; /**< Application data. */
-
- struct dsound_stream play_strm; /**< Playback stream. */
- struct dsound_stream rec_strm; /**< Capture stream. */
-
- void *buffer; /**< Temp. frame buffer. */
- unsigned clock_rate; /**< Clock rate. */
- unsigned samples_per_frame; /**< Samples per frame. */
- unsigned bits_per_sample; /**< Bits per sample. */
- unsigned channel_count; /**< Channel count. */
-
- pj_thread_t *thread; /**< Thread handle. */
- HANDLE thread_quit_event; /**< Quit signal to thread */
-};
-
-
-static pj_pool_factory *pool_factory;
-
-
-static void init_waveformatex (PCMWAVEFORMAT *pcmwf,
- unsigned clock_rate,
- unsigned channel_count)
-{
- pj_bzero(pcmwf, sizeof(PCMWAVEFORMAT));
- pcmwf->wf.wFormatTag = WAVE_FORMAT_PCM;
- pcmwf->wf.nChannels = (pj_uint16_t)channel_count;
- pcmwf->wf.nSamplesPerSec = clock_rate;
- pcmwf->wf.nBlockAlign = (pj_uint16_t)(channel_count * BYTES_PER_SAMPLE);
- pcmwf->wf.nAvgBytesPerSec = clock_rate * channel_count * BYTES_PER_SAMPLE;
- pcmwf->wBitsPerSample = BITS_PER_SAMPLE;
-}
-
-
-/*
- * Initialize DirectSound player device.
- */
-static pj_status_t init_player_stream( struct dsound_stream *ds_strm,
- int dev_id,
- unsigned clock_rate,
- unsigned channel_count,
- unsigned samples_per_frame,
- unsigned buffer_count)
-{
- HRESULT hr;
- HWND hwnd;
- PCMWAVEFORMAT pcmwf;
- DSBUFFERDESC dsbdesc;
- DSBPOSITIONNOTIFY dsPosNotify[MAX_PACKET_BUFFER_COUNT];
- unsigned bytes_per_frame;
- unsigned max_latency;
- unsigned i;
-
-
- PJ_ASSERT_RETURN(buffer_count <= MAX_PACKET_BUFFER_COUNT, PJ_EINVAL);
-
- /* Check device ID */
- if (dev_id == -1)
- dev_id = 0;
-
- PJ_ASSERT_RETURN(dev_id>=0 && dev_id < (int)dev_count, PJ_EINVAL);
-
- /*
- * Create DirectSound device.
- */
- hr = DirectSoundCreate(dev_info[dev_id].lpGuid, &ds_strm->ds.play.lpDs,
- NULL);
- if (FAILED(hr))
- return PJ_RETURN_OS_ERROR(hr);
-
- hwnd = GetForegroundWindow();
- if (hwnd == NULL) {
- hwnd = GetDesktopWindow();
- }
- hr = IDirectSound_SetCooperativeLevel( ds_strm->ds.play.lpDs, hwnd,
- DSSCL_PRIORITY);
- if FAILED(hr)
- return PJ_RETURN_OS_ERROR(hr);
-
- /*
- * Set up wave format structure for initialize DirectSound play
- * buffer.
- */
- init_waveformatex(&pcmwf, clock_rate, channel_count);
- bytes_per_frame = samples_per_frame * BYTES_PER_SAMPLE;
-
- /* Set up DSBUFFERDESC structure. */
- pj_bzero(&dsbdesc, sizeof(DSBUFFERDESC));
- dsbdesc.dwSize = sizeof(DSBUFFERDESC);
- dsbdesc.dwFlags = DSBCAPS_CTRLVOLUME | DSBCAPS_CTRLPOSITIONNOTIFY |
- DSBCAPS_GETCURRENTPOSITION2 | DSBCAPS_GLOBALFOCUS;
-
- dsbdesc.dwBufferBytes = buffer_count * bytes_per_frame;
- dsbdesc.lpwfxFormat = (LPWAVEFORMATEX)&pcmwf;
-
- /*
- * Create DirectSound playback buffer.
- */
- hr = IDirectSound_CreateSoundBuffer(ds_strm->ds.play.lpDs, &dsbdesc,
- &ds_strm->ds.play.lpDsBuffer, NULL);
- if (FAILED(hr) )
- return PJ_RETURN_OS_ERROR(hr);
-
- /*
- * Create event for play notification.
- */
- ds_strm->hEvent = CreateEvent( NULL, FALSE, FALSE, NULL);
- if (ds_strm->hEvent == NULL)
- return pj_get_os_error();
-
- /*
- * Setup notification for play.
- */
- hr = IDirectSoundBuffer_QueryInterface( ds_strm->ds.play.lpDsBuffer,
- &IID_IDirectSoundNotify,
- (LPVOID *)&ds_strm->lpDsNotify);
- if (FAILED(hr))
- return PJ_RETURN_OS_ERROR(hr);
-
-
- for (i=0; i<buffer_count; ++i) {
- dsPosNotify[i].dwOffset = i * bytes_per_frame;
- dsPosNotify[i].hEventNotify = ds_strm->hEvent;
- }
-
- hr = IDirectSoundNotify_SetNotificationPositions( ds_strm->lpDsNotify,
- buffer_count,
- dsPosNotify);
- if (FAILED(hr))
- return PJ_RETURN_OS_ERROR(hr);
-
-
- hr = IDirectSoundBuffer_SetCurrentPosition(ds_strm->ds.play.lpDsBuffer, 0);
- if (FAILED(hr))
- return PJ_RETURN_OS_ERROR(hr);
-
-
- ds_strm->dwBytePos = 0;
- ds_strm->dwDsBufferSize = buffer_count * bytes_per_frame;
- ds_strm->timestamp.u64 = 0;
- /*
- * Play latency does not need to be on a frame boundry, it is just how far
- * ahead of the read pointer we set the write pointer. So we should just
- * use the user configured latency. However, if the latency measured in
- * bytes causes more buffers than we are allowed, we must cap the latency
- * at the time contained in 1-buffer_count.
- */
- max_latency = (1 - buffer_count) * samples_per_frame * 1000 / clock_rate /
- channel_count;
- ds_strm->latency = PJ_MIN(max_latency, snd_output_latency);
-
- /* Done setting up play device. */
- PJ_LOG(5,(THIS_FILE,
- " DirectSound player \"%s\" initialized (clock_rate=%d, "
- "channel_count=%d, samples_per_frame=%d (%dms))",
- dev_info[dev_id].info.name,
- clock_rate, channel_count, samples_per_frame,
- samples_per_frame * 1000 / clock_rate));
-
- return PJ_SUCCESS;
-}
-
-
-/*
- * Initialize DirectSound recorder device
- */
-static pj_status_t init_capture_stream( struct dsound_stream *ds_strm,
- int dev_id,
- unsigned clock_rate,
- unsigned channel_count,
- unsigned samples_per_frame,
- unsigned buffer_count)
-{
- HRESULT hr;
- PCMWAVEFORMAT pcmwf;
- DSCBUFFERDESC dscbdesc;
- DSBPOSITIONNOTIFY dsPosNotify[MAX_PACKET_BUFFER_COUNT];
- unsigned bytes_per_frame;
- unsigned i;
-
-
- PJ_ASSERT_RETURN(buffer_count <= MAX_PACKET_BUFFER_COUNT, PJ_EINVAL);
-
-
- /* Check device id */
- if (dev_id == -1)
- dev_id = 0;
-
- PJ_ASSERT_RETURN(dev_id>=0 && dev_id < (int)dev_count, PJ_EINVAL);
-
- /*
- * Creating recorder device.
- */
- hr = DirectSoundCaptureCreate(dev_info[dev_id].lpGuid,
- &ds_strm->ds.capture.lpDs, NULL);
- if (FAILED(hr))
- return PJ_RETURN_OS_ERROR(hr);
-
-
- /* Init wave format to initialize buffer */
- init_waveformatex( &pcmwf, clock_rate, channel_count);
- bytes_per_frame = samples_per_frame * BYTES_PER_SAMPLE;
-
- /*
- * Setup capture buffer using sound buffer structure that was passed
- * to play buffer creation earlier.
- */
- pj_bzero(&dscbdesc, sizeof(DSCBUFFERDESC));
- dscbdesc.dwSize = sizeof(DSCBUFFERDESC);
- dscbdesc.dwFlags = DSCBCAPS_WAVEMAPPED ;
- dscbdesc.dwBufferBytes = buffer_count * bytes_per_frame;
- dscbdesc.lpwfxFormat = (LPWAVEFORMATEX)&pcmwf;
-
- hr = IDirectSoundCapture_CreateCaptureBuffer( ds_strm->ds.capture.lpDs,
- &dscbdesc,
- &ds_strm->ds.capture.lpDsBuffer,
- NULL);
- if (FAILED(hr))
- return PJ_RETURN_OS_ERROR(hr);
-
- /*
- * Create event for play notification.
- */
- ds_strm->hEvent = CreateEvent( NULL, FALSE, FALSE, NULL);
- if (ds_strm->hEvent == NULL)
- return pj_get_os_error();
-
- /*
- * Setup notifications for recording.
- */
- hr = IDirectSoundCaptureBuffer_QueryInterface( ds_strm->ds.capture.lpDsBuffer,
- &IID_IDirectSoundNotify,
- (LPVOID *)&ds_strm->lpDsNotify);
- if (FAILED(hr))
- return PJ_RETURN_OS_ERROR(hr);
-
-
- for (i=0; i<buffer_count; ++i) {
- dsPosNotify[i].dwOffset = i * bytes_per_frame;
- dsPosNotify[i].hEventNotify = ds_strm->hEvent;
- }
-
- hr = IDirectSoundNotify_SetNotificationPositions( ds_strm->lpDsNotify,
- buffer_count,
- dsPosNotify);
- if (FAILED(hr))
- return PJ_RETURN_OS_ERROR(hr);
-
- hr = IDirectSoundCaptureBuffer_GetCurrentPosition( ds_strm->ds.capture.lpDsBuffer,
- NULL, &ds_strm->dwBytePos );
- if (FAILED(hr))
- return PJ_RETURN_OS_ERROR(hr);
-
- ds_strm->timestamp.u64 = 0;
- ds_strm->dwDsBufferSize = buffer_count * bytes_per_frame;
- /*
- * Capture latency must always be on a frame boundry,
- * so compute it based off the calculated buffer_count.
- */
- ds_strm->latency = buffer_count * samples_per_frame * 1000 / clock_rate /
- channel_count;
-
- /* Done setting up recorder device. */
- PJ_LOG(5,(THIS_FILE,
- " DirectSound capture \"%s\" initialized (clock_rate=%d, "
- "channel_count=%d, samples_per_frame=%d (%dms))",
- dev_info[dev_id].info.name,
- clock_rate, channel_count, samples_per_frame,
- samples_per_frame * 1000 / clock_rate));
-
- return PJ_SUCCESS;
-}
-
-
-
-static BOOL AppReadDataFromBuffer(LPDIRECTSOUNDCAPTUREBUFFER lpDsb, // The buffer.
- DWORD dwOffset, // Our own write cursor.
- LPBYTE lpbSoundData, // Start of our data.
- DWORD dwSoundBytes) // Size of block to copy.
-{
- LPVOID lpvPtr1;
- DWORD dwBytes1;
- LPVOID lpvPtr2;
- DWORD dwBytes2;
- HRESULT hr;
-
- // Obtain memory address of write block. This will be in two parts
- // if the block wraps around.
-
- hr = IDirectSoundCaptureBuffer_Lock( lpDsb, dwOffset, dwSoundBytes, &lpvPtr1,
- &dwBytes1, &lpvPtr2, &dwBytes2, 0);
-
- if SUCCEEDED(hr) {
- // Read from pointers.
- pj_memcpy(lpbSoundData, lpvPtr1, dwBytes1);
- if (lpvPtr2 != NULL)
- pj_memcpy(lpbSoundData+dwBytes1, lpvPtr2, dwBytes2);
-
- // Release the data back to DirectSound.
- hr = IDirectSoundCaptureBuffer_Unlock(lpDsb, lpvPtr1, dwBytes1, lpvPtr2, dwBytes2);
- if SUCCEEDED(hr)
- return TRUE;
- }
-
- // Lock, Unlock, or Restore failed.
- return FALSE;
-}
-
-
-static BOOL AppWriteDataToBuffer(LPDIRECTSOUNDBUFFER lpDsb, // The buffer.
- DWORD dwOffset, // Our own write cursor.
- LPBYTE lpbSoundData, // Start of our data.
- DWORD dwSoundBytes) // Size of block to copy.
-{
- LPVOID lpvPtr1;
- DWORD dwBytes1;
- LPVOID lpvPtr2;
- DWORD dwBytes2;
- HRESULT hr;
-
- // Obtain memory address of write block. This will be in two parts
- // if the block wraps around.
-
- hr = IDirectSoundBuffer_Lock( lpDsb, dwOffset, dwSoundBytes, &lpvPtr1,
- &dwBytes1, &lpvPtr2, &dwBytes2, 0);
-
- // If the buffer was lost, restore and retry lock.
- if (DSERR_BUFFERLOST == hr) {
- IDirectSoundBuffer_Restore(lpDsb);
- hr = IDirectSoundBuffer_Lock( lpDsb, dwOffset, dwSoundBytes,
- &lpvPtr1, &dwBytes1, &lpvPtr2, &dwBytes2, 0);
- }
- if SUCCEEDED(hr) {
- pj_memcpy(lpvPtr1, lpbSoundData, dwBytes1);
- if (NULL != lpvPtr2)
- pj_memcpy(lpvPtr2, lpbSoundData+dwBytes1, dwBytes2);
-
- hr = IDirectSoundBuffer_Unlock(lpDsb, lpvPtr1, dwBytes1, lpvPtr2, dwBytes2);
- if SUCCEEDED(hr)
- return TRUE;
- }
-
- return FALSE;
-}
-
-/*
- * Check if there is space in playing buffer.
- */
-static unsigned dsound_play_empty_size(struct dsound_stream *dsound_strm)
-{
- HRESULT hr;
- long size_available;
- DWORD writePos, readPos;
-
- hr = IDirectSoundBuffer_GetCurrentPosition(dsound_strm->ds.play.lpDsBuffer,
- &readPos, &writePos);
- if FAILED(hr)
- return PJ_FALSE;
-
- if (readPos < dsound_strm->dwBytePos)
- size_available = readPos + dsound_strm->dwDsBufferSize -
- dsound_strm->dwBytePos;
- else
- size_available = readPos - dsound_strm->dwBytePos;
-
- return size_available;
-}
-
-
-/*
- * Check if there are captured frames in DirectSound capture buffer.
- */
-static unsigned dsound_captured_size(struct dsound_stream *dsound_strm)
-{
- HRESULT hr;
- long size_available;
- DWORD writePos, readPos;
-
- hr = IDirectSoundCaptureBuffer_GetCurrentPosition(
- dsound_strm->ds.capture.lpDsBuffer,
- &writePos, &readPos);
- if FAILED(hr)
- return PJ_FALSE;
-
- if (readPos < dsound_strm->dwBytePos)
- size_available = readPos +
- (dsound_strm->dwDsBufferSize) - dsound_strm->dwBytePos;
- else
- size_available = readPos - dsound_strm->dwBytePos;
-
- return size_available;
-}
-
-/*
- * DirectSound capture and playback thread.
- */
-static int dsound_dev_thread(void *arg)
-{
- pjmedia_snd_stream *strm = arg;
- HANDLE events[3];
- unsigned eventCount;
- unsigned bytes_per_frame;
- pj_status_t status;
-
-
- eventCount = 0;
- events[eventCount++] = strm->thread_quit_event;
- if (strm->dir & PJMEDIA_DIR_PLAYBACK)
- events[eventCount++] = strm->play_strm.hEvent;
- if (strm->dir & PJMEDIA_DIR_CAPTURE)
- events[eventCount++] = strm->rec_strm.hEvent;
-
-
- /* Raise self priority. We don't want the audio to be distorted by
- * system activity.
- */
- SetThreadPriority( GetCurrentThread(), THREAD_PRIORITY_HIGHEST);
-
- /* Calculate bytes per frame */
- bytes_per_frame = strm->samples_per_frame * BYTES_PER_SAMPLE;
-
- /*
- * Loop while not signalled to quit, wait for event objects to be
- * signalled by DirectSound capture and play buffer.
- */
- while (PJ_TRUE) {
-
- DWORD rc;
- pjmedia_dir signalled_dir;
-
- rc = WaitForMultipleObjects(eventCount, events, FALSE, INFINITE);
- if (rc < WAIT_OBJECT_0 || rc >= WAIT_OBJECT_0+eventCount)
- continue;
-
-
- if (rc == WAIT_OBJECT_0)
- break;
- if (rc == (WAIT_OBJECT_0 + 1)) {
- if (events[1] == strm->play_strm.hEvent)
- signalled_dir = PJMEDIA_DIR_PLAYBACK;
- else
- signalled_dir = PJMEDIA_DIR_CAPTURE;
- } else {
- if (events[2] == strm->play_strm.hEvent)
- signalled_dir = PJMEDIA_DIR_PLAYBACK;
- else
- signalled_dir = PJMEDIA_DIR_CAPTURE;
- }
-
-
- if (signalled_dir == PJMEDIA_DIR_PLAYBACK) {
-
- struct dsound_stream *dsound_strm;
-
- /*
- * DirectSound has requested us to feed some frames to
- * playback buffer.
- */
-
- dsound_strm = &strm->play_strm;
- status = PJ_SUCCESS;
-
- while (dsound_play_empty_size(dsound_strm) > bytes_per_frame) {
- /* Get frame from application. */
- status = (*strm->play_cb)(strm->user_data,
- dsound_strm->timestamp.u32.lo,
- strm->buffer,
- bytes_per_frame);
- if (status != PJ_SUCCESS)
- break;
-
- /* Write to DirectSound buffer. */
- AppWriteDataToBuffer( dsound_strm->ds.play.lpDsBuffer,
- dsound_strm->dwBytePos,
- (LPBYTE)strm->buffer,
- bytes_per_frame);
-
- /* Increment position. */
- dsound_strm->dwBytePos += bytes_per_frame;
- if (dsound_strm->dwBytePos >= dsound_strm->dwDsBufferSize)
- dsound_strm->dwBytePos -= dsound_strm->dwDsBufferSize;
- dsound_strm->timestamp.u64 += strm->samples_per_frame;
- }
-
- } else {
- /*
- * DirectSound has indicated that it has some frames ready
- * in the capture buffer. Get as much frames as possible to
- * prevent overflows.
- */
- struct dsound_stream *dsound_strm;
- BOOL rc;
-
- dsound_strm = &strm->rec_strm;
-
- /* Fetch while we have more than 1 frame */
- while (dsound_captured_size(dsound_strm) > bytes_per_frame) {
-
- /* Capture from DirectSound buffer. */
- rc = AppReadDataFromBuffer(dsound_strm->ds.capture.lpDsBuffer,
- dsound_strm->dwBytePos,
- (LPBYTE)strm->buffer,
- bytes_per_frame);
-
- if (!rc) {
- pj_bzero(strm->buffer, bytes_per_frame);
- }
-
- /* Call callback */
- status = (*strm->rec_cb)(strm->user_data,
- dsound_strm->timestamp.u32.lo,
- strm->buffer,
- bytes_per_frame);
-
- /* Quit thread on error. */
- if (status != PJ_SUCCESS)
- goto on_error;
-
-
- /* Increment position. */
- dsound_strm->dwBytePos += bytes_per_frame;
- if (dsound_strm->dwBytePos >= dsound_strm->dwDsBufferSize)
- dsound_strm->dwBytePos -= dsound_strm->dwDsBufferSize;
- dsound_strm->timestamp.u64 += strm->samples_per_frame;
- }
- }
- }
-
-
-on_error:
- PJ_LOG(5,(THIS_FILE, "DirectSound: thread stopping.."));
- return 0;
-}
-
-
-/* DirectSound enum device callback */
-static BOOL CALLBACK DSEnumCallback( LPGUID lpGuid, LPCTSTR lpcstrDescription,
- LPCTSTR lpcstrModule, LPVOID lpContext)
-{
- unsigned index, max = sizeof(dev_info[index].info.name);
- pj_bool_t is_capture_device = (lpContext != NULL);
-
-
- PJ_UNUSED_ARG(lpcstrModule);
-
-
- /* Put the capture and playback of the same devices to the same
- * dev_info item, by looking at the GUID.
- */
- for (index=0; index<dev_count; ++index) {
- if ((dev_info[index].lpGuid==NULL && lpGuid==NULL) ||
- pj_memcmp(&dev_info[index].guid, lpGuid, sizeof(GUID))==0)
- {
- break;
- }
- }
-
- if (index == dev_count)
- ++dev_count;
- else if (dev_count >= MAX_HARDWARE) {
- pj_assert(!"Too many DirectSound hardware found");
- PJ_LOG(4,(THIS_FILE, "Too many hardware found, some devices will "
- "not be listed"));
- return FALSE;
- }
-
-#ifdef UNICODE
- WideCharToMultiByte(CP_ACP, 0, lpcstrDescription, wcslen(lpcstrDescription),
- dev_info[index].info.name, max, NULL, NULL);
-#else
- strncpy(dev_info[index].info.name, lpcstrDescription, max);
-#endif
-
- dev_info[index].info.name[max-1] = '\0';
- if (lpGuid == NULL) {
- dev_info[index].lpGuid = NULL;
- } else {
- pj_memcpy(&dev_info[index].guid, lpGuid, sizeof(GUID));
- dev_info[index].lpGuid = &dev_info[index].guid;
- }
- dev_info[index].info.default_samples_per_sec = 44100;
-
- /* Just assumed that device supports stereo capture/playback */
- if (is_capture_device)
- dev_info[index].info.input_count+=2;
- else
- dev_info[index].info.output_count+=2;
-
- return TRUE;
-}
-
-
-/*
- * Init sound library.
- */
-PJ_DEF(pj_status_t) pjmedia_snd_init(pj_pool_factory *factory)
-{
- HRESULT hr;
- unsigned i;
-
- if (++snd_init_count != 1)
- return PJ_SUCCESS;
-
- pool_factory = factory;
-
- /* Enumerate sound playback devices */
- hr = DirectSoundEnumerate(&DSEnumCallback, NULL);
- if (FAILED(hr))
- return PJ_RETURN_OS_ERROR(hr);
-
- /* Enumerate sound capture devices */
- hr = DirectSoundCaptureEnumerate(&DSEnumCallback, (void*)1);
- if (FAILED(hr))
- return PJ_RETURN_OS_ERROR(hr);
-
- PJ_LOG(4,(THIS_FILE, "DirectSound initialized, found %d devices:",
- dev_count));
- for (i=0; i<dev_count; ++i) {
- PJ_LOG(4,(THIS_FILE, " dev_id %d: %s (in=%d, out=%d)",
- i, dev_info[i].info.name,
- dev_info[i].info.input_count,
- dev_info[i].info.output_count));
- }
-
- return PJ_SUCCESS;
-}
-
-/*
- * Deinitialize sound library.
- */
-PJ_DEF(pj_status_t) pjmedia_snd_deinit(void)
-{
- --snd_init_count;
- return PJ_SUCCESS;
-}
-
-/*
- * Get device count.
- */
-PJ_DEF(int) pjmedia_snd_get_dev_count(void)
-{
- return dev_count;
-}
-
-/*
- * Get device info.
- */
-PJ_DEF(const pjmedia_snd_dev_info*) pjmedia_snd_get_dev_info(unsigned index)
-{
- if (index == (unsigned)-1)
- index = 0;
-
- PJ_ASSERT_RETURN(index < dev_count, NULL);
-
- return &dev_info[index].info;
-}
-
-
-/*
- * Open stream.
- */
-static pj_status_t open_stream( pjmedia_dir dir,
- int rec_id,
- int play_id,
- unsigned clock_rate,
- unsigned channel_count,
- unsigned samples_per_frame,
- unsigned bits_per_sample,
- pjmedia_snd_rec_cb rec_cb,
- pjmedia_snd_play_cb play_cb,
- void *user_data,
- pjmedia_snd_stream **p_snd_strm)
-{
- pj_pool_t *pool;
- pjmedia_snd_stream *strm;
- pj_status_t status;
-
- /* Make sure sound subsystem has been initialized with
- * pjmedia_snd_init()
- */
- PJ_ASSERT_RETURN( pool_factory != NULL, PJ_EINVALIDOP );
-
-
- /* Can only support 16bits per sample */
- PJ_ASSERT_RETURN(bits_per_sample == BITS_PER_SAMPLE, PJ_EINVAL);
-
- /* Create and Initialize stream descriptor */
- pool = pj_pool_create(pool_factory, "dsound-dev", 1000, 1000, NULL);
- PJ_ASSERT_RETURN(pool != NULL, PJ_ENOMEM);
-
- strm = pj_pool_zalloc(pool, sizeof(pjmedia_snd_stream));
- strm->dir = dir;
- strm->play_id = play_id;
- strm->rec_id = rec_id;
- strm->pool = pool;
- strm->rec_cb = rec_cb;
- strm->play_cb = play_cb;
- strm->user_data = user_data;
- strm->clock_rate = clock_rate;
- strm->samples_per_frame = samples_per_frame;
- strm->bits_per_sample = bits_per_sample;
- strm->channel_count = channel_count;
- strm->buffer = pj_pool_alloc(pool, samples_per_frame * BYTES_PER_SAMPLE);
- if (!strm->buffer) {
- pj_pool_release(pool);
- return PJ_ENOMEM;
- }
-
- /*
- * Create event for stopping the worker thread.
- */
- strm->thread_quit_event = CreateEvent(NULL, FALSE, FALSE, NULL);
- if (strm->thread_quit_event == NULL) {
- status = pj_get_os_error();
- pj_pool_release(pool);
- return status;
- }
-
- /* Create player stream */
- if (dir & PJMEDIA_DIR_PLAYBACK) {
- unsigned buffer_count;
-
- /* Calculate buffer count, in frame unit */
- buffer_count = clock_rate * snd_output_latency / samples_per_frame /
- 1000;
- /* There must always be one more buffer than required for the latency */
- buffer_count += 1;
- if (buffer_count < MIN_PACKET_BUFFER_COUNT)
- buffer_count = MIN_PACKET_BUFFER_COUNT;
- if (buffer_count > MAX_PACKET_BUFFER_COUNT)
- buffer_count = MAX_PACKET_BUFFER_COUNT;
-
- status = init_player_stream( &strm->play_strm, play_id, clock_rate,
- channel_count, samples_per_frame,
- buffer_count );
- if (status != PJ_SUCCESS) {
- pjmedia_snd_stream_close(strm);
- return status;
- }
- }
-
- /* Create capture stream */
- if (dir & PJMEDIA_DIR_CAPTURE) {
- unsigned buffer_count;
-
- /* Calculate buffer count, in frame unit */
- buffer_count = clock_rate * snd_input_latency / samples_per_frame /
- 1000;
- if (buffer_count < MIN_PACKET_BUFFER_COUNT)
- buffer_count = MIN_PACKET_BUFFER_COUNT;
- if (buffer_count > MAX_PACKET_BUFFER_COUNT)
- buffer_count = MAX_PACKET_BUFFER_COUNT;
-
- status = init_capture_stream( &strm->rec_strm, rec_id, clock_rate,
- channel_count, samples_per_frame,
- buffer_count);
- if (status != PJ_SUCCESS) {
- pjmedia_snd_stream_close(strm);
- return status;
- }
- }
-
-
- /* Create and start the thread */
- status = pj_thread_create(pool, "dsound", &dsound_dev_thread, strm,
- 0, 0, &strm->thread);
- if (status != PJ_SUCCESS) {
- pjmedia_snd_stream_close(strm);
- return status;
- }
-
- *p_snd_strm = strm;
-
- return PJ_SUCCESS;
-}
-
-/*
- * Open stream.
- */
-PJ_DEF(pj_status_t) pjmedia_snd_open_rec( int index,
- unsigned clock_rate,
- unsigned channel_count,
- unsigned samples_per_frame,
- unsigned bits_per_sample,
- pjmedia_snd_rec_cb rec_cb,
- void *user_data,
- pjmedia_snd_stream **p_snd_strm)
-{
- PJ_ASSERT_RETURN(rec_cb && p_snd_strm, PJ_EINVAL);
-
- return open_stream( PJMEDIA_DIR_CAPTURE, index, -1,
- clock_rate, channel_count, samples_per_frame,
- bits_per_sample, rec_cb, NULL, user_data,
- p_snd_strm);
-}
-
-PJ_DEF(pj_status_t) pjmedia_snd_open_player( int index,
- unsigned clock_rate,
- unsigned channel_count,
- unsigned samples_per_frame,
- unsigned bits_per_sample,
- pjmedia_snd_play_cb play_cb,
- void *user_data,
- pjmedia_snd_stream **p_snd_strm)
-{
- PJ_ASSERT_RETURN(play_cb && p_snd_strm, PJ_EINVAL);
-
- return open_stream( PJMEDIA_DIR_PLAYBACK, -1, index,
- clock_rate, channel_count, samples_per_frame,
- bits_per_sample, NULL, play_cb, user_data,
- p_snd_strm);
-}
-
-/*
- * Open both player and recorder.
- */
-PJ_DEF(pj_status_t) pjmedia_snd_open( int rec_id,
- int play_id,
- unsigned clock_rate,
- unsigned channel_count,
- unsigned samples_per_frame,
- unsigned bits_per_sample,
- pjmedia_snd_rec_cb rec_cb,
- pjmedia_snd_play_cb play_cb,
- void *user_data,
- pjmedia_snd_stream **p_snd_strm)
-{
- PJ_ASSERT_RETURN(rec_cb && play_cb && p_snd_strm, PJ_EINVAL);
-
- return open_stream( PJMEDIA_DIR_CAPTURE_PLAYBACK, rec_id, play_id,
- clock_rate, channel_count, samples_per_frame,
- bits_per_sample, rec_cb, play_cb, user_data,
- p_snd_strm );
-}
-
-/*
- * Get stream info.
- */
-PJ_DEF(pj_status_t) pjmedia_snd_stream_get_info(pjmedia_snd_stream *strm,
- pjmedia_snd_stream_info *pi)
-{
-
- PJ_ASSERT_RETURN(strm && pi, PJ_EINVAL);
-
- pj_bzero(pi, sizeof(*pi));
- pi->dir = strm->dir;
- pi->play_id = strm->play_id;
- pi->rec_id = strm->rec_id;
- pi->clock_rate = strm->clock_rate;
- pi->channel_count = strm->channel_count;
- pi->samples_per_frame = strm->samples_per_frame;
- pi->bits_per_sample = strm->bits_per_sample;
- pi->rec_latency = strm->rec_strm.latency * strm->clock_rate *
- strm->channel_count/ 1000;
- pi->play_latency = strm->play_strm.latency * strm->clock_rate *
- strm->channel_count/ 1000;
-
- return PJ_SUCCESS;
-}
-
-
-/*
- * Start stream.
- */
-PJ_DEF(pj_status_t) pjmedia_snd_stream_start(pjmedia_snd_stream *stream)
-{
- HRESULT hr;
-
- if (stream->play_strm.ds.play.lpDsBuffer) {
- hr = IDirectSoundBuffer_SetCurrentPosition(
- stream->play_strm.ds.play.lpDsBuffer, 0);
- if (FAILED(hr))
- return PJ_RETURN_OS_ERROR(hr);
-
- /* Set the write pointer ahead of the read pointer by latency bytes */
- stream->play_strm.dwBytePos = BYTES_PER_SAMPLE * stream->clock_rate *
- stream->channel_count * stream->play_strm.latency / 1000;
-
- hr = IDirectSoundBuffer_Play(stream->play_strm.ds.play.lpDsBuffer,
- 0, 0, DSBPLAY_LOOPING);
- if (FAILED(hr))
- return PJ_RETURN_OS_ERROR(hr);
- PJ_LOG(5,(THIS_FILE, "DirectSound playback stream started"));
- }
-
- if (stream->rec_strm.ds.capture.lpDsBuffer) {
- hr = IDirectSoundCaptureBuffer_GetCurrentPosition(
- stream->rec_strm.ds.capture.lpDsBuffer,
- NULL, &stream->rec_strm.dwBytePos );
- if (FAILED(hr))
- return PJ_RETURN_OS_ERROR(hr);
-
- hr = IDirectSoundCaptureBuffer_Start(
- stream->rec_strm.ds.capture.lpDsBuffer,
- DSCBSTART_LOOPING );
- if (FAILED(hr))
- return PJ_RETURN_OS_ERROR(hr);
- PJ_LOG(5,(THIS_FILE, "DirectSound capture stream started"));
- }
-
- return PJ_SUCCESS;
-}
-
-/*
- * Stop stream.
- */
-PJ_DEF(pj_status_t) pjmedia_snd_stream_stop(pjmedia_snd_stream *stream)
-{
- PJ_ASSERT_RETURN(stream != NULL, PJ_EINVAL);
-
- if (stream->play_strm.ds.play.lpDsBuffer) {
- PJ_LOG(5,(THIS_FILE, "Stopping DirectSound playback stream"));
- IDirectSoundBuffer_Stop( stream->play_strm.ds.play.lpDsBuffer );
- }
-
- if (stream->rec_strm.ds.capture.lpDsBuffer) {
- PJ_LOG(5,(THIS_FILE, "Stopping DirectSound capture stream"));
- IDirectSoundCaptureBuffer_Stop(stream->rec_strm.ds.capture.lpDsBuffer);
- }
-
- return PJ_SUCCESS;
-}
-
-
-/*
- * Destroy stream.
- */
-PJ_DEF(pj_status_t) pjmedia_snd_stream_close(pjmedia_snd_stream *stream)
-{
- PJ_ASSERT_RETURN(stream != NULL, PJ_EINVAL);
-
- pjmedia_snd_stream_stop(stream);
-
- if (stream->thread) {
- pj_assert(stream->thread_quit_event);
- SetEvent(stream->thread_quit_event);
- pj_thread_join(stream->thread);
- pj_thread_destroy(stream->thread);
- stream->thread = NULL;
- }
-
- if (stream->thread_quit_event) {
- CloseHandle(stream->thread_quit_event);
- stream->thread_quit_event = NULL;
- }
-
- if (stream->play_strm.lpDsNotify) {
- IDirectSoundNotify_Release( stream->play_strm.lpDsNotify );
- stream->play_strm.lpDsNotify = NULL;
- }
-
- if (stream->play_strm.hEvent) {
- CloseHandle(stream->play_strm.hEvent);
- stream->play_strm.hEvent = NULL;
- }
-
- if (stream->play_strm.ds.play.lpDsBuffer) {
- IDirectSoundBuffer_Release( stream->play_strm.ds.play.lpDsBuffer );
- stream->play_strm.ds.play.lpDsBuffer = NULL;
- }
-
- if (stream->play_strm.ds.play.lpDs) {
- IDirectSound_Release( stream->play_strm.ds.play.lpDs );
- stream->play_strm.ds.play.lpDs = NULL;
- }
-
- if (stream->rec_strm.lpDsNotify) {
- IDirectSoundNotify_Release( stream->rec_strm.lpDsNotify );
- stream->rec_strm.lpDsNotify = NULL;
- }
-
- if (stream->rec_strm.hEvent) {
- CloseHandle(stream->rec_strm.hEvent);
- stream->rec_strm.hEvent = NULL;
- }
-
- if (stream->rec_strm.ds.capture.lpDsBuffer) {
- IDirectSoundCaptureBuffer_Release( stream->rec_strm.ds.capture.lpDsBuffer );
- stream->rec_strm.ds.capture.lpDsBuffer = NULL;
- }
-
- if (stream->rec_strm.ds.capture.lpDs) {
- IDirectSoundCapture_Release( stream->rec_strm.ds.capture.lpDs );
- stream->rec_strm.ds.capture.lpDs = NULL;
- }
-
-
- pj_pool_release(stream->pool);
-
- return PJ_SUCCESS;
-}
-
-
-/*
- * Set sound latency.
- */
-PJ_DEF(pj_status_t) pjmedia_snd_set_latency(unsigned input_latency,
- unsigned output_latency)
-{
- snd_input_latency = (input_latency == 0)?
- PJMEDIA_SND_DEFAULT_REC_LATENCY : input_latency;
- snd_output_latency = (output_latency == 0)?
- PJMEDIA_SND_DEFAULT_PLAY_LATENCY : output_latency;
-
- return PJ_SUCCESS;
-}
-
-#endif /* PJMEDIA_SOUND_IMPLEMENTATION */
-
diff --git a/pjmedia/src/pjmedia/endpoint.c b/pjmedia/src/pjmedia/endpoint.c
index fb66a813..a3e988d5 100644
--- a/pjmedia/src/pjmedia/endpoint.c
+++ b/pjmedia/src/pjmedia/endpoint.c
@@ -20,6 +20,7 @@
#include <pjmedia/endpoint.h>
#include <pjmedia/errno.h>
#include <pjmedia/sdp.h>
+#include <pjmedia-audiodev/audiodev.h>
#include <pj/assert.h>
#include <pj/ioqueue.h>
#include <pj/log.h>
@@ -121,7 +122,7 @@ PJ_DEF(pj_status_t) pjmedia_endpt_create(pj_pool_factory *pf,
endpt->thread_cnt = worker_cnt;
/* Sound */
- status = pjmedia_snd_init(pf);
+ status = pjmedia_aud_subsys_init(pf);
if (status != PJ_SUCCESS)
goto on_error;
@@ -171,7 +172,7 @@ on_error:
if (endpt->ioqueue && endpt->own_ioqueue)
pj_ioqueue_destroy(endpt->ioqueue);
- pjmedia_snd_deinit();
+ pjmedia_aud_subsys_shutdown();
pj_pool_release(pool);
return status;
}
@@ -212,7 +213,7 @@ PJ_DEF(pj_status_t) pjmedia_endpt_destroy (pjmedia_endpt *endpt)
endpt->pf = NULL;
- pjmedia_snd_deinit();
+ pjmedia_aud_subsys_shutdown();
pj_pool_release (endpt->pool);
return PJ_SUCCESS;
diff --git a/pjmedia/src/pjmedia/errno.c b/pjmedia/src/pjmedia/errno.c
index f92e888c..d86eb585 100644
--- a/pjmedia/src/pjmedia/errno.c
+++ b/pjmedia/src/pjmedia/errno.c
@@ -20,7 +20,8 @@
#include <pjmedia/errno.h>
#include <pjmedia/types.h>
#include <pj/string.h>
-#if PJMEDIA_SOUND_IMPLEMENTATION == PJMEDIA_SOUND_PORTAUDIO_SOUND
+#if defined(PJMEDIA_SOUND_IMPLEMENTATION) && \
+ PJMEDIA_SOUND_IMPLEMENTATION == PJMEDIA_SOUND_PORTAUDIO_SOUND
# include <portaudio.h>
#endif
@@ -179,7 +180,8 @@ PJ_DEF(pj_str_t) pjmedia_strerror( pj_status_t statcode,
#if defined(PJ_HAS_ERROR_STRING) && (PJ_HAS_ERROR_STRING != 0)
/* See if the error comes from PortAudio. */
-#if PJMEDIA_SOUND_IMPLEMENTATION==PJMEDIA_SOUND_PORTAUDIO_SOUND
+#if defined(PJMEDIA_SOUND_IMPLEMENTATION) && \
+ PJMEDIA_SOUND_IMPLEMENTATION == PJMEDIA_SOUND_PORTAUDIO_SOUND
if (statcode >= PJMEDIA_PORTAUDIO_ERRNO_START &&
statcode <= PJMEDIA_PORTAUDIO_ERRNO_END)
{
diff --git a/pjmedia/src/pjmedia/nullsound.c b/pjmedia/src/pjmedia/nullsound.c
deleted file mode 100644
index a3ee54a1..00000000
--- a/pjmedia/src/pjmedia/nullsound.c
+++ /dev/null
@@ -1,197 +0,0 @@
-/* $Id$ */
-/*
- * Copyright (C) 2008-2009 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/sound.h>
-#include <pjmedia/errno.h>
-#include <pj/assert.h>
-#include <pj/pool.h>
-
-#if PJMEDIA_SOUND_IMPLEMENTATION==PJMEDIA_SOUND_NULL_SOUND
-
-static pjmedia_snd_dev_info null_info =
-{
- "Null Device",
- 1,
- 1,
- 8000
-};
-
-static pj_pool_factory *pool_factory;
-
-struct pjmedia_snd_stream
-{
- pj_pool_t *pool;
- pjmedia_dir dir;
- int rec_id;
- int play_id;
- unsigned clock_rate;
- unsigned channel_count;
- unsigned samples_per_frame;
- unsigned bits_per_sample;
- pjmedia_snd_rec_cb rec_cb;
- pjmedia_snd_play_cb play_cb;
- void *user_data;
-};
-
-
-PJ_DEF(pj_status_t) pjmedia_snd_init(pj_pool_factory *factory)
-{
- pool_factory = factory;
- return PJ_SUCCESS;
-}
-
-PJ_DEF(pj_status_t) pjmedia_snd_deinit(void)
-{
- return PJ_SUCCESS;
-}
-
-PJ_DEF(int) pjmedia_snd_get_dev_count(void)
-{
- return 1;
-}
-
-PJ_DEF(const pjmedia_snd_dev_info*) pjmedia_snd_get_dev_info(unsigned index)
-{
- PJ_ASSERT_RETURN(index==0 || index==(unsigned)-1, NULL);
- return &null_info;
-}
-
-PJ_DEF(pj_status_t) pjmedia_snd_open_rec( int index,
- unsigned clock_rate,
- unsigned channel_count,
- unsigned samples_per_frame,
- unsigned bits_per_sample,
- pjmedia_snd_rec_cb rec_cb,
- void *user_data,
- pjmedia_snd_stream **p_snd_strm)
-{
- return pjmedia_snd_open(index, -2, clock_rate, channel_count,
- samples_per_frame, bits_per_sample,
- rec_cb, NULL, user_data, p_snd_strm);
-}
-
-PJ_DEF(pj_status_t) pjmedia_snd_open_player( int index,
- unsigned clock_rate,
- unsigned channel_count,
- unsigned samples_per_frame,
- unsigned bits_per_sample,
- pjmedia_snd_play_cb play_cb,
- void *user_data,
- pjmedia_snd_stream **p_snd_strm )
-{
- return pjmedia_snd_open(-2, index, clock_rate, channel_count,
- samples_per_frame, bits_per_sample,
- NULL, play_cb, user_data, p_snd_strm);
-}
-
-PJ_DEF(pj_status_t) pjmedia_snd_open( int rec_id,
- int play_id,
- unsigned clock_rate,
- unsigned channel_count,
- unsigned samples_per_frame,
- unsigned bits_per_sample,
- pjmedia_snd_rec_cb rec_cb,
- pjmedia_snd_play_cb play_cb,
- void *user_data,
- pjmedia_snd_stream **p_snd_strm)
-{
- pj_pool_t *pool;
- pjmedia_snd_stream *snd_strm;
-
- pool = pj_pool_create(pool_factory, NULL, 128, 128, NULL);
- snd_strm = PJ_POOL_ZALLOC_T(pool, pjmedia_snd_stream);
-
- snd_strm->pool = pool;
-
- if (rec_id == -1) rec_id = 0;
- if (play_id == -1) play_id = 0;
-
- if (rec_id != -2 && play_id != -2)
- snd_strm->dir = PJMEDIA_DIR_CAPTURE_PLAYBACK;
- else if (rec_id != -2)
- snd_strm->dir = PJMEDIA_DIR_CAPTURE;
- else if (play_id != -2)
- snd_strm->dir = PJMEDIA_DIR_PLAYBACK;
-
- snd_strm->rec_id = rec_id;
- snd_strm->play_id = play_id;
- snd_strm->clock_rate = clock_rate;
- snd_strm->channel_count = channel_count;
- snd_strm->samples_per_frame = samples_per_frame;
- snd_strm->bits_per_sample = bits_per_sample;
- snd_strm->rec_cb = rec_cb;
- snd_strm->play_cb = play_cb;
- snd_strm->user_data = user_data;
-
- *p_snd_strm = snd_strm;
-
- return PJ_SUCCESS;
-}
-
-
-PJ_DEF(pj_status_t) pjmedia_snd_stream_start(pjmedia_snd_stream *stream)
-{
- PJ_UNUSED_ARG(stream);
- return PJ_SUCCESS;
-}
-
-PJ_DEF(pj_status_t) pjmedia_snd_stream_stop(pjmedia_snd_stream *stream)
-{
- PJ_UNUSED_ARG(stream);
- return PJ_SUCCESS;
-}
-
-PJ_DEF(pj_status_t) pjmedia_snd_stream_get_info(pjmedia_snd_stream *strm,
- pjmedia_snd_stream_info *pi)
-{
-
- pj_bzero(pi, sizeof(pjmedia_snd_stream_info));
- pi->dir = strm->dir;
- pi->play_id = strm->play_id;
- pi->rec_id = strm->rec_id;
- pi->clock_rate = strm->clock_rate;
- pi->channel_count = strm->channel_count;
- pi->samples_per_frame = strm->samples_per_frame;
- pi->bits_per_sample = strm->bits_per_sample;
- pi->rec_latency = 0;
- pi->play_latency = 0;
-
- return PJ_SUCCESS;
-}
-
-
-PJ_DEF(pj_status_t) pjmedia_snd_stream_close(pjmedia_snd_stream *stream)
-{
- pj_pool_release(stream->pool);
- return PJ_SUCCESS;
-}
-
-/*
- * Set sound latency.
- */
-PJ_DEF(pj_status_t) pjmedia_snd_set_latency(unsigned input_latency,
- unsigned output_latency)
-{
- /* Nothing to do */
- PJ_UNUSED_ARG(input_latency);
- PJ_UNUSED_ARG(output_latency);
- return PJ_SUCCESS;
-}
-
-#endif /* PJMEDIA_SOUND_IMPLEMENTATION */
diff --git a/pjmedia/src/pjmedia/pasound.c b/pjmedia/src/pjmedia/pasound.c
deleted file mode 100644
index a004008d..00000000
--- a/pjmedia/src/pjmedia/pasound.c
+++ /dev/null
@@ -1,1037 +0,0 @@
-/* $Id$ */
-/*
- * Copyright (C) 2008-2009 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/sound.h>
-#include <pjmedia/errno.h>
-#include <pj/assert.h>
-#include <pj/log.h>
-#include <pj/os.h>
-#include <pj/string.h>
-#include <portaudio.h>
-
-#if PJMEDIA_SOUND_IMPLEMENTATION==PJMEDIA_SOUND_PORTAUDIO_SOUND
-
-#define THIS_FILE "pasound.c"
-
-static int snd_init_count;
-
-/* Latency settings */
-static unsigned snd_input_latency = PJMEDIA_SND_DEFAULT_REC_LATENCY;
-static unsigned snd_output_latency = PJMEDIA_SND_DEFAULT_PLAY_LATENCY;
-
-static struct snd_mgr
-{
- pj_pool_factory *factory;
-} snd_mgr;
-
-/*
- * Sound stream descriptor.
- * This struct may be used for both unidirectional or bidirectional sound
- * streams.
- */
-struct pjmedia_snd_stream
-{
- pj_pool_t *pool;
- pj_str_t name;
- pjmedia_dir dir;
- int play_id;
- int rec_id;
- int bytes_per_sample;
- pj_uint32_t samples_per_sec;
- unsigned samples_per_frame;
- int channel_count;
-
- PaStream *rec_strm;
- PaStream *play_strm;
-
- void *user_data;
- pjmedia_snd_rec_cb rec_cb;
- pjmedia_snd_play_cb play_cb;
-
- pj_uint32_t play_timestamp;
- pj_uint32_t rec_timestamp;
- pj_uint32_t underflow;
- pj_uint32_t overflow;
-
- pj_bool_t quit_flag;
-
- pj_bool_t rec_thread_exited;
- pj_bool_t rec_thread_initialized;
- pj_thread_desc rec_thread_desc;
- pj_thread_t *rec_thread;
-
- pj_bool_t play_thread_exited;
- pj_bool_t play_thread_initialized;
- pj_thread_desc play_thread_desc;
- pj_thread_t *play_thread;
-
- /* Sometime the record callback does not return framesize as configured
- * (e.g: in OSS), while this module must guarantee returning framesize
- * as configured in the creation settings. In this case, we need a buffer
- * for the recorded samples.
- */
- pj_int16_t *rec_buf;
- unsigned rec_buf_count;
-
- /* Sometime the player callback does not request framesize as configured
- * (e.g: in Linux OSS) while sound device will always get samples from
- * the other component as many as configured samples_per_frame.
- */
- pj_int16_t *play_buf;
- unsigned play_buf_count;
-};
-
-
-static int PaRecorderCallback(const void *input,
- void *output,
- unsigned long frameCount,
- const PaStreamCallbackTimeInfo* timeInfo,
- PaStreamCallbackFlags statusFlags,
- void *userData )
-{
- pjmedia_snd_stream *stream = (pjmedia_snd_stream*) userData;
- pj_status_t status = 0;
- unsigned nsamples;
-
- PJ_UNUSED_ARG(output);
- PJ_UNUSED_ARG(timeInfo);
-
- if (stream->quit_flag)
- goto on_break;
-
- if (input == NULL)
- return paContinue;
-
- /* Known cases of callback's thread:
- * - The thread may be changed in the middle of a session, e.g: in MacOS
- * it happens when plugging/unplugging headphone.
- * - The same thread may be reused in consecutive sessions. The first
- * session will leave TLS set, but release the TLS data address,
- * so the second session must re-register the callback's thread.
- */
- if (stream->rec_thread_initialized == 0 || !pj_thread_is_registered())
- {
- status = pj_thread_register("pa_rec", stream->rec_thread_desc,
- &stream->rec_thread);
- stream->rec_thread_initialized = 1;
- PJ_LOG(5,(THIS_FILE, "Recorder thread started"));
- }
-
- if (statusFlags & paInputUnderflow)
- ++stream->underflow;
- if (statusFlags & paInputOverflow)
- ++stream->overflow;
-
- stream->rec_timestamp += frameCount;
-
- /* Calculate number of samples we've got */
- nsamples = frameCount * stream->channel_count + stream->rec_buf_count;
-
- if (nsamples >= stream->samples_per_frame)
- {
- /* If buffer is not empty, combine the buffer with the just incoming
- * samples, then call put_frame.
- */
- if (stream->rec_buf_count) {
- unsigned chunk_count = 0;
-
- chunk_count = stream->samples_per_frame - stream->rec_buf_count;
- pjmedia_copy_samples(stream->rec_buf + stream->rec_buf_count,
- (pj_int16_t*)input, chunk_count);
- status = (*stream->rec_cb)(stream->user_data,
- stream->rec_timestamp,
- (void*) stream->rec_buf,
- stream->samples_per_frame *
- stream->bytes_per_sample);
-
- input = (pj_int16_t*) input + chunk_count;
- nsamples -= stream->samples_per_frame;
- stream->rec_buf_count = 0;
- }
-
- /* Give all frames we have */
- while (nsamples >= stream->samples_per_frame && status == 0) {
- status = (*stream->rec_cb)(stream->user_data,
- stream->rec_timestamp,
- (void*) input,
- stream->samples_per_frame *
- stream->bytes_per_sample);
- input = (pj_int16_t*) input + stream->samples_per_frame;
- nsamples -= stream->samples_per_frame;
- }
-
- /* Store the remaining samples into the buffer */
- if (nsamples && status == 0) {
- stream->rec_buf_count = nsamples;
- pjmedia_copy_samples(stream->rec_buf, (pj_int16_t*)input,
- nsamples);
- }
-
- } else {
- /* Not enough samples, let's just store them in the buffer */
- pjmedia_copy_samples(stream->rec_buf + stream->rec_buf_count,
- (pj_int16_t*)input,
- frameCount * stream->channel_count);
- stream->rec_buf_count += frameCount * stream->channel_count;
- }
-
- if (status==0)
- return paContinue;
-
-on_break:
- stream->rec_thread_exited = 1;
- return paAbort;
-}
-
-static int PaPlayerCallback( const void *input,
- void *output,
- unsigned long frameCount,
- const PaStreamCallbackTimeInfo* timeInfo,
- PaStreamCallbackFlags statusFlags,
- void *userData )
-{
- pjmedia_snd_stream *stream = (pjmedia_snd_stream*) userData;
- pj_status_t status = 0;
- unsigned nsamples_req = frameCount * stream->channel_count;
-
- PJ_UNUSED_ARG(input);
- PJ_UNUSED_ARG(timeInfo);
-
- if (stream->quit_flag)
- goto on_break;
-
- if (output == NULL)
- return paContinue;
-
- /* Known cases of callback's thread:
- * - The thread may be changed in the middle of a session, e.g: in MacOS
- * it happens when plugging/unplugging headphone.
- * - The same thread may be reused in consecutive sessions. The first
- * session will leave TLS set, but release the TLS data address,
- * so the second session must re-register the callback's thread.
- */
- if (stream->play_thread_initialized == 0 || !pj_thread_is_registered())
- {
- status = pj_thread_register("portaudio", stream->play_thread_desc,
- &stream->play_thread);
- stream->play_thread_initialized = 1;
- PJ_LOG(5,(THIS_FILE, "Player thread started"));
- }
-
- if (statusFlags & paOutputUnderflow)
- ++stream->underflow;
- if (statusFlags & paOutputOverflow)
- ++stream->overflow;
-
- stream->play_timestamp += frameCount;
-
- /* Check if any buffered samples */
- if (stream->play_buf_count) {
- /* samples buffered >= requested by sound device */
- if (stream->play_buf_count >= nsamples_req) {
- pjmedia_copy_samples((pj_int16_t*)output, stream->play_buf,
- nsamples_req);
- stream->play_buf_count -= nsamples_req;
- pjmedia_move_samples(stream->play_buf,
- stream->play_buf + nsamples_req,
- stream->play_buf_count);
- nsamples_req = 0;
-
- return paContinue;
- }
-
- /* samples buffered < requested by sound device */
- pjmedia_copy_samples((pj_int16_t*)output, stream->play_buf,
- stream->play_buf_count);
- nsamples_req -= stream->play_buf_count;
- output = (pj_int16_t*)output + stream->play_buf_count;
- stream->play_buf_count = 0;
- }
-
- /* Fill output buffer as requested */
- while (nsamples_req && status == 0) {
- if (nsamples_req >= stream->samples_per_frame) {
- status = (*stream->play_cb)(stream->user_data,
- stream->play_timestamp,
- output,
- stream->samples_per_frame *
- stream->bytes_per_sample);
- nsamples_req -= stream->samples_per_frame;
- output = (pj_int16_t*)output + stream->samples_per_frame;
- } else {
- status = (*stream->play_cb)(stream->user_data,
- stream->play_timestamp,
- stream->play_buf,
- stream->samples_per_frame *
- stream->bytes_per_sample);
- pjmedia_copy_samples((pj_int16_t*)output, stream->play_buf,
- nsamples_req);
- stream->play_buf_count = stream->samples_per_frame - nsamples_req;
- pjmedia_move_samples(stream->play_buf,
- stream->play_buf+nsamples_req,
- stream->play_buf_count);
- nsamples_req = 0;
- }
- }
-
- if (status==0)
- return paContinue;
-
-on_break:
- stream->play_thread_exited = 1;
- return paAbort;
-}
-
-
-static int PaRecorderPlayerCallback( const void *input,
- void *output,
- unsigned long frameCount,
- const PaStreamCallbackTimeInfo* timeInfo,
- PaStreamCallbackFlags statusFlags,
- void *userData )
-{
- int rc;
-
- rc = PaRecorderCallback(input, output, frameCount, timeInfo,
- statusFlags, userData);
- if (rc != paContinue)
- return rc;
-
- rc = PaPlayerCallback(input, output, frameCount, timeInfo,
- statusFlags, userData);
- return rc;
-}
-
-/* Logging callback from PA */
-static void pa_log_cb(const char *log)
-{
- PJ_LOG(5,(THIS_FILE, "PA message: %s", log));
-}
-
-/* We should include pa_debugprint.h for this, but the header
- * is not available publicly. :(
- */
-typedef void (*PaUtilLogCallback ) (const char *log);
-void PaUtil_SetDebugPrintFunction(PaUtilLogCallback cb);
-
-/*
- * Init sound library.
- */
-PJ_DEF(pj_status_t) pjmedia_snd_init(pj_pool_factory *factory)
-{
- if (++snd_init_count == 1) {
- int err;
-
- PaUtil_SetDebugPrintFunction(&pa_log_cb);
-
- snd_mgr.factory = factory;
- err = Pa_Initialize();
-
- PJ_LOG(4,(THIS_FILE,
- "PortAudio sound library initialized, status=%d", err));
- PJ_LOG(4,(THIS_FILE, "PortAudio host api count=%d",
- Pa_GetHostApiCount()));
- PJ_LOG(4,(THIS_FILE, "Sound device count=%d",
- pjmedia_snd_get_dev_count()));
-
- return err ? PJMEDIA_ERRNO_FROM_PORTAUDIO(err) : PJ_SUCCESS;
- } else {
- return PJ_SUCCESS;
- }
-}
-
-
-/*
- * Get device count.
- */
-PJ_DEF(int) pjmedia_snd_get_dev_count(void)
-{
- int count = Pa_GetDeviceCount();
- return count < 0 ? 0 : count;
-}
-
-
-/*
- * Get device info.
- */
-PJ_DEF(const pjmedia_snd_dev_info*) pjmedia_snd_get_dev_info(unsigned index)
-{
- static pjmedia_snd_dev_info info;
- const PaDeviceInfo *pa_info;
-
- pa_info = Pa_GetDeviceInfo(index);
- if (!pa_info)
- return NULL;
-
- pj_bzero(&info, sizeof(info));
- strncpy(info.name, pa_info->name, sizeof(info.name));
- info.name[sizeof(info.name)-1] = '\0';
- info.input_count = pa_info->maxInputChannels;
- info.output_count = pa_info->maxOutputChannels;
- info.default_samples_per_sec = (unsigned)pa_info->defaultSampleRate;
-
- return &info;
-}
-
-
-/* Get PortAudio default input device ID */
-static int pa_get_default_input_dev(int channel_count)
-{
- int i, count;
-
- /* Special for Windows - try to use the DirectSound implementation
- * first since it provides better latency.
- */
-#if PJMEDIA_PREFER_DIRECT_SOUND
- if (Pa_HostApiTypeIdToHostApiIndex(paDirectSound) >= 0) {
- const PaHostApiInfo *pHI;
- int index = Pa_HostApiTypeIdToHostApiIndex(paDirectSound);
- pHI = Pa_GetHostApiInfo(index);
- if (pHI) {
- const PaDeviceInfo *paDevInfo = NULL;
- paDevInfo = Pa_GetDeviceInfo(pHI->defaultInputDevice);
- if (paDevInfo && paDevInfo->maxInputChannels >= channel_count)
- return pHI->defaultInputDevice;
- }
- }
-#endif
-
- /* Enumerate the host api's for the default devices, and return
- * the device with suitable channels.
- */
- count = Pa_GetHostApiCount();
- for (i=0; i < count; ++i) {
- const PaHostApiInfo *pHAInfo;
-
- pHAInfo = Pa_GetHostApiInfo(i);
- if (!pHAInfo)
- continue;
-
- if (pHAInfo->defaultInputDevice >= 0) {
- const PaDeviceInfo *paDevInfo;
-
- paDevInfo = Pa_GetDeviceInfo(pHAInfo->defaultInputDevice);
-
- if (paDevInfo->maxInputChannels >= channel_count)
- return pHAInfo->defaultInputDevice;
- }
- }
-
- /* If still no device is found, enumerate all devices */
- count = pjmedia_snd_get_dev_count();
- for (i=0; i<count; ++i) {
- const PaDeviceInfo *paDevInfo;
-
- paDevInfo = Pa_GetDeviceInfo(i);
- if (paDevInfo->maxInputChannels >= channel_count)
- return i;
- }
-
- return -1;
-}
-
-/* Get PortAudio default output device ID */
-static int pa_get_default_output_dev(int channel_count)
-{
- int i, count;
-
- /* Special for Windows - try to use the DirectSound implementation
- * first since it provides better latency.
- */
-#if PJMEDIA_PREFER_DIRECT_SOUND
- if (Pa_HostApiTypeIdToHostApiIndex(paDirectSound) >= 0) {
- const PaHostApiInfo *pHI;
- int index = Pa_HostApiTypeIdToHostApiIndex(paDirectSound);
- pHI = Pa_GetHostApiInfo(index);
- if (pHI) {
- const PaDeviceInfo *paDevInfo = NULL;
- paDevInfo = Pa_GetDeviceInfo(pHI->defaultOutputDevice);
- if (paDevInfo && paDevInfo->maxOutputChannels >= channel_count)
- return pHI->defaultOutputDevice;
- }
- }
-#endif
-
- /* Enumerate the host api's for the default devices, and return
- * the device with suitable channels.
- */
- count = Pa_GetHostApiCount();
- for (i=0; i < count; ++i) {
- const PaHostApiInfo *pHAInfo;
-
- pHAInfo = Pa_GetHostApiInfo(i);
- if (!pHAInfo)
- continue;
-
- if (pHAInfo->defaultOutputDevice >= 0) {
- const PaDeviceInfo *paDevInfo;
-
- paDevInfo = Pa_GetDeviceInfo(pHAInfo->defaultOutputDevice);
-
- if (paDevInfo->maxOutputChannels >= channel_count)
- return pHAInfo->defaultOutputDevice;
- }
- }
-
- /* If still no device is found, enumerate all devices */
- count = pjmedia_snd_get_dev_count();
- for (i=0; i<count; ++i) {
- const PaDeviceInfo *paDevInfo;
-
- paDevInfo = Pa_GetDeviceInfo(i);
- if (paDevInfo->maxOutputChannels >= channel_count)
- return i;
- }
-
- return -1;
-}
-
-
-/*
- * Open stream.
- */
-PJ_DEF(pj_status_t) pjmedia_snd_open_rec( int index,
- unsigned clock_rate,
- unsigned channel_count,
- unsigned samples_per_frame,
- unsigned bits_per_sample,
- pjmedia_snd_rec_cb rec_cb,
- void *user_data,
- pjmedia_snd_stream **p_snd_strm)
-{
- pj_pool_t *pool;
- pjmedia_snd_stream *stream;
- PaStreamParameters inputParam;
- int sampleFormat;
- const PaDeviceInfo *paDevInfo = NULL;
- const PaHostApiInfo *paHostApiInfo = NULL;
- unsigned paFrames, paRate, paLatency;
- const PaStreamInfo *paSI;
- PaError err;
-
- if (index < 0) {
- index = pa_get_default_input_dev(channel_count);
- if (index < 0) {
- /* No such device. */
- return PJMEDIA_ENOSNDREC;
- }
- }
-
- paDevInfo = Pa_GetDeviceInfo(index);
- if (!paDevInfo) {
- /* Assumed it is "No such device" error. */
- return PJMEDIA_ESNDINDEVID;
- }
-
- if (bits_per_sample == 8)
- sampleFormat = paUInt8;
- else if (bits_per_sample == 16)
- sampleFormat = paInt16;
- else if (bits_per_sample == 32)
- sampleFormat = paInt32;
- else
- return PJMEDIA_ESNDINSAMPLEFMT;
-
- pool = pj_pool_create( snd_mgr.factory, "sndstream", 1024, 1024, NULL);
- if (!pool)
- return PJ_ENOMEM;
-
- stream = PJ_POOL_ZALLOC_T(pool, pjmedia_snd_stream);
- stream->pool = pool;
- pj_strdup2_with_null(pool, &stream->name, paDevInfo->name);
- stream->dir = PJMEDIA_DIR_CAPTURE;
- stream->rec_id = index;
- stream->play_id = -1;
- stream->user_data = user_data;
- stream->samples_per_sec = clock_rate;
- stream->samples_per_frame = samples_per_frame;
- stream->bytes_per_sample = bits_per_sample / 8;
- stream->channel_count = channel_count;
- stream->rec_cb = rec_cb;
-
- stream->rec_buf = (pj_int16_t*)pj_pool_alloc(pool,
- stream->samples_per_frame * stream->bytes_per_sample);
- stream->rec_buf_count = 0;
-
- pj_bzero(&inputParam, sizeof(inputParam));
- inputParam.device = index;
- inputParam.channelCount = channel_count;
- inputParam.hostApiSpecificStreamInfo = NULL;
- inputParam.sampleFormat = sampleFormat;
- inputParam.suggestedLatency = snd_input_latency / 1000.0;
-
- paHostApiInfo = Pa_GetHostApiInfo(paDevInfo->hostApi);
-
- /* Frames in PortAudio is number of samples in a single channel */
- paFrames = samples_per_frame / channel_count;
-
- err = Pa_OpenStream( &stream->rec_strm, &inputParam, NULL,
- clock_rate, paFrames,
- paClipOff, &PaRecorderCallback, stream );
- if (err != paNoError) {
- pj_pool_release(pool);
- return PJMEDIA_ERRNO_FROM_PORTAUDIO(err);
- }
-
- paSI = Pa_GetStreamInfo(stream->rec_strm);
- paRate = (unsigned)paSI->sampleRate;
- paLatency = (unsigned)(paSI->inputLatency * 1000);
-
- PJ_LOG(5,(THIS_FILE, "Opened device %s (%s) for recording, sample "
- "rate=%d, ch=%d, "
- "bits=%d, %d samples per frame, latency=%d ms",
- paDevInfo->name, paHostApiInfo->name,
- paRate, channel_count,
- bits_per_sample, samples_per_frame,
- paLatency));
-
- *p_snd_strm = stream;
- return PJ_SUCCESS;
-}
-
-
-PJ_DEF(pj_status_t) pjmedia_snd_open_player( int index,
- unsigned clock_rate,
- unsigned channel_count,
- unsigned samples_per_frame,
- unsigned bits_per_sample,
- pjmedia_snd_play_cb play_cb,
- void *user_data,
- pjmedia_snd_stream **p_snd_strm)
-{
- pj_pool_t *pool;
- pjmedia_snd_stream *stream;
- PaStreamParameters outputParam;
- int sampleFormat;
- const PaDeviceInfo *paDevInfo = NULL;
- const PaHostApiInfo *paHostApiInfo = NULL;
- const PaStreamInfo *paSI;
- unsigned paFrames, paRate, paLatency;
- PaError err;
-
- if (index < 0) {
- index = pa_get_default_output_dev(channel_count);
- if (index < 0) {
- /* No such device. */
- return PJMEDIA_ENOSNDPLAY;
- }
- }
-
- paDevInfo = Pa_GetDeviceInfo(index);
- if (!paDevInfo) {
- /* Assumed it is "No such device" error. */
- return PJMEDIA_ESNDINDEVID;
- }
-
- if (bits_per_sample == 8)
- sampleFormat = paUInt8;
- else if (bits_per_sample == 16)
- sampleFormat = paInt16;
- else if (bits_per_sample == 32)
- sampleFormat = paInt32;
- else
- return PJMEDIA_ESNDINSAMPLEFMT;
-
- pool = pj_pool_create( snd_mgr.factory, "sndstream", 1024, 1024, NULL);
- if (!pool)
- return PJ_ENOMEM;
-
- stream = PJ_POOL_ZALLOC_T(pool, pjmedia_snd_stream);
- stream->pool = pool;
- pj_strdup2_with_null(pool, &stream->name, paDevInfo->name);
- stream->dir = PJMEDIA_DIR_PLAYBACK;
- stream->play_id = index;
- stream->rec_id = -1;
- stream->user_data = user_data;
- stream->samples_per_sec = clock_rate;
- stream->samples_per_frame = samples_per_frame;
- stream->bytes_per_sample = bits_per_sample / 8;
- stream->channel_count = channel_count;
- stream->play_cb = play_cb;
-
- stream->play_buf = (pj_int16_t*)pj_pool_alloc(pool,
- stream->samples_per_frame * stream->bytes_per_sample);
- stream->play_buf_count = 0;
-
- pj_bzero(&outputParam, sizeof(outputParam));
- outputParam.device = index;
- outputParam.channelCount = channel_count;
- outputParam.hostApiSpecificStreamInfo = NULL;
- outputParam.sampleFormat = sampleFormat;
- outputParam.suggestedLatency = snd_output_latency / 1000.0;
-
- paHostApiInfo = Pa_GetHostApiInfo(paDevInfo->hostApi);
-
- /* Frames in PortAudio is number of samples in a single channel */
- paFrames = samples_per_frame / channel_count;
-
- err = Pa_OpenStream( &stream->play_strm, NULL, &outputParam,
- clock_rate, paFrames,
- paClipOff, &PaPlayerCallback, stream );
- if (err != paNoError) {
- pj_pool_release(pool);
- return PJMEDIA_ERRNO_FROM_PORTAUDIO(err);
- }
-
- paSI = Pa_GetStreamInfo(stream->play_strm);
- paRate = (unsigned)(paSI->sampleRate);
- paLatency = (unsigned)(paSI->outputLatency * 1000);
-
- PJ_LOG(5,(THIS_FILE, "Opened device %d: %s(%s) for playing, sample rate=%d"
- ", ch=%d, "
- "bits=%d, %d samples per frame, latency=%d ms",
- index, paDevInfo->name, paHostApiInfo->name,
- paRate, channel_count,
- bits_per_sample, samples_per_frame, paLatency));
-
- *p_snd_strm = stream;
-
- return PJ_SUCCESS;
-}
-
-
-/*
- * Open both player and recorder.
- */
-PJ_DEF(pj_status_t) pjmedia_snd_open( int rec_id,
- int play_id,
- unsigned clock_rate,
- unsigned channel_count,
- unsigned samples_per_frame,
- unsigned bits_per_sample,
- pjmedia_snd_rec_cb rec_cb,
- pjmedia_snd_play_cb play_cb,
- void *user_data,
- pjmedia_snd_stream **p_snd_strm)
-{
- pj_pool_t *pool;
- pjmedia_snd_stream *stream;
- PaStream *paStream = NULL;
- PaStreamParameters inputParam;
- PaStreamParameters outputParam;
- int sampleFormat;
- const PaDeviceInfo *paRecDevInfo = NULL;
- const PaDeviceInfo *paPlayDevInfo = NULL;
- const PaHostApiInfo *paRecHostApiInfo = NULL;
- const PaHostApiInfo *paPlayHostApiInfo = NULL;
- const PaStreamInfo *paSI;
- unsigned paFrames, paRate, paInputLatency, paOutputLatency;
- PaError err;
-
- if (rec_id < 0) {
- rec_id = pa_get_default_input_dev(channel_count);
- if (rec_id < 0) {
- /* No such device. */
- return PJMEDIA_ENOSNDREC;
- }
- }
-
- paRecDevInfo = Pa_GetDeviceInfo(rec_id);
- if (!paRecDevInfo) {
- /* Assumed it is "No such device" error. */
- return PJMEDIA_ESNDINDEVID;
- }
-
- if (play_id < 0) {
- play_id = pa_get_default_output_dev(channel_count);
- if (play_id < 0) {
- /* No such device. */
- return PJMEDIA_ENOSNDPLAY;
- }
- }
-
- paPlayDevInfo = Pa_GetDeviceInfo(play_id);
- if (!paPlayDevInfo) {
- /* Assumed it is "No such device" error. */
- return PJMEDIA_ESNDINDEVID;
- }
-
-
- if (bits_per_sample == 8)
- sampleFormat = paUInt8;
- else if (bits_per_sample == 16)
- sampleFormat = paInt16;
- else if (bits_per_sample == 32)
- sampleFormat = paInt32;
- else
- return PJMEDIA_ESNDINSAMPLEFMT;
-
- pool = pj_pool_create( snd_mgr.factory, "sndstream", 1024, 1024, NULL);
- if (!pool)
- return PJ_ENOMEM;
-
- stream = PJ_POOL_ZALLOC_T(pool, pjmedia_snd_stream);
- stream->pool = pool;
- pj_strdup2_with_null(pool, &stream->name, paRecDevInfo->name);
- stream->dir = PJMEDIA_DIR_CAPTURE_PLAYBACK;
- stream->play_id = play_id;
- stream->rec_id = rec_id;
- stream->user_data = user_data;
- stream->samples_per_sec = clock_rate;
- stream->samples_per_frame = samples_per_frame;
- stream->bytes_per_sample = bits_per_sample / 8;
- stream->channel_count = channel_count;
- stream->rec_cb = rec_cb;
- stream->play_cb = play_cb;
-
- stream->rec_buf = (pj_int16_t*)pj_pool_alloc(pool,
- stream->samples_per_frame * stream->bytes_per_sample);
- stream->rec_buf_count = 0;
-
- stream->play_buf = (pj_int16_t*)pj_pool_alloc(pool,
- stream->samples_per_frame * stream->bytes_per_sample);
- stream->play_buf_count = 0;
-
- pj_bzero(&inputParam, sizeof(inputParam));
- inputParam.device = rec_id;
- inputParam.channelCount = channel_count;
- inputParam.hostApiSpecificStreamInfo = NULL;
- inputParam.sampleFormat = sampleFormat;
- inputParam.suggestedLatency = snd_input_latency / 1000.0;
-
- paRecHostApiInfo = Pa_GetHostApiInfo(paRecDevInfo->hostApi);
-
- pj_bzero(&outputParam, sizeof(outputParam));
- outputParam.device = play_id;
- outputParam.channelCount = channel_count;
- outputParam.hostApiSpecificStreamInfo = NULL;
- outputParam.sampleFormat = sampleFormat;
- outputParam.suggestedLatency = snd_output_latency / 1000.0;
-
- paPlayHostApiInfo = Pa_GetHostApiInfo(paPlayDevInfo->hostApi);
-
- /* Frames in PortAudio is number of samples in a single channel */
- paFrames = samples_per_frame / channel_count;
-
- /* If both input and output are on the same device, open a single stream
- * for both input and output.
- */
- if (rec_id == play_id) {
- err = Pa_OpenStream( &paStream, &inputParam, &outputParam,
- clock_rate, paFrames,
- paClipOff, &PaRecorderPlayerCallback, stream );
- if (err == paNoError) {
- /* Set play stream and record stream to the same stream */
- stream->play_strm = stream->rec_strm = paStream;
- }
- } else {
- err = -1;
- }
-
- /* .. otherwise if input and output are on the same device, OR if we're
- * unable to open a bidirectional stream, then open two separate
- * input and output stream.
- */
- if (paStream == NULL) {
- /* Open input stream */
- err = Pa_OpenStream( &stream->rec_strm, &inputParam, NULL,
- clock_rate, paFrames,
- paClipOff, &PaRecorderCallback, stream );
- if (err == paNoError) {
- /* Open output stream */
- err = Pa_OpenStream( &stream->play_strm, NULL, &outputParam,
- clock_rate, paFrames,
- paClipOff, &PaPlayerCallback, stream );
- if (err != paNoError)
- Pa_CloseStream(stream->rec_strm);
- }
- }
-
- if (err != paNoError) {
- pj_pool_release(pool);
- return PJMEDIA_ERRNO_FROM_PORTAUDIO(err);
- }
-
- paSI = Pa_GetStreamInfo(stream->rec_strm);
- paRate = (unsigned)(paSI->sampleRate);
- paInputLatency = (unsigned)(paSI->inputLatency * 1000);
- paSI = Pa_GetStreamInfo(stream->play_strm);
- paOutputLatency = (unsigned)(paSI->outputLatency * 1000);
-
- PJ_LOG(5,(THIS_FILE, "Opened device %s(%s)/%s(%s) for recording and "
- "playback, sample rate=%d, ch=%d, "
- "bits=%d, %d samples per frame, input latency=%d ms, "
- "output latency=%d ms",
- paRecDevInfo->name, paRecHostApiInfo->name,
- paPlayDevInfo->name, paPlayHostApiInfo->name,
- paRate, channel_count,
- bits_per_sample, samples_per_frame,
- paInputLatency, paOutputLatency));
-
- *p_snd_strm = stream;
-
-
- return PJ_SUCCESS;
-}
-
-
-/*
- * Get stream info.
- */
-PJ_DEF(pj_status_t) pjmedia_snd_stream_get_info(pjmedia_snd_stream *strm,
- pjmedia_snd_stream_info *pi)
-{
- const PaStreamInfo *paPlaySI = NULL, *paRecSI = NULL;
-
- PJ_ASSERT_RETURN(strm && pi, PJ_EINVAL);
- PJ_ASSERT_RETURN(strm->play_strm || strm->rec_strm, PJ_EINVALIDOP);
-
- if (strm->play_strm) {
- paPlaySI = Pa_GetStreamInfo(strm->play_strm);
- }
- if (strm->rec_strm) {
- paRecSI = Pa_GetStreamInfo(strm->rec_strm);
- }
-
- pj_bzero(pi, sizeof(*pi));
- pi->dir = strm->dir;
- pi->play_id = strm->play_id;
- pi->rec_id = strm->rec_id;
- pi->clock_rate = (unsigned)(paPlaySI ? paPlaySI->sampleRate :
- paRecSI->sampleRate);
- pi->channel_count = strm->channel_count;
- pi->samples_per_frame = strm->samples_per_frame;
- pi->bits_per_sample = strm->bytes_per_sample * 8;
- pi->rec_latency = (unsigned)(paRecSI ? paRecSI->inputLatency *
- paRecSI->sampleRate : 0);
- pi->play_latency = (unsigned)(paPlaySI ? paPlaySI->outputLatency *
- paPlaySI->sampleRate : 0);
-
- return PJ_SUCCESS;
-}
-
-
-/*
- * Start stream.
- */
-PJ_DEF(pj_status_t) pjmedia_snd_stream_start(pjmedia_snd_stream *stream)
-{
- int err = 0;
-
- PJ_LOG(5,(THIS_FILE, "Starting %s stream..", stream->name.ptr));
-
- if (stream->play_strm)
- err = Pa_StartStream(stream->play_strm);
-
- if (err==0 && stream->rec_strm && stream->rec_strm != stream->play_strm) {
- err = Pa_StartStream(stream->rec_strm);
- if (err != 0)
- Pa_StopStream(stream->play_strm);
- }
-
- PJ_LOG(5,(THIS_FILE, "Done, status=%d", err));
-
- return err ? PJMEDIA_ERRNO_FROM_PORTAUDIO(err) : PJ_SUCCESS;
-}
-
-/*
- * Stop stream.
- */
-PJ_DEF(pj_status_t) pjmedia_snd_stream_stop(pjmedia_snd_stream *stream)
-{
- int i, err = 0;
-
- stream->quit_flag = 1;
- for (i=0; !stream->rec_thread_exited && i<100; ++i)
- pj_thread_sleep(10);
- for (i=0; !stream->play_thread_exited && i<100; ++i)
- pj_thread_sleep(10);
-
- pj_thread_sleep(1);
-
- PJ_LOG(5,(THIS_FILE, "Stopping stream.."));
-
- if (stream->play_strm)
- err = Pa_StopStream(stream->play_strm);
-
- if (stream->rec_strm && stream->rec_strm != stream->play_strm)
- err = Pa_StopStream(stream->rec_strm);
-
- stream->play_thread_initialized = 0;
- stream->rec_thread_initialized = 0;
-
- PJ_LOG(5,(THIS_FILE, "Done, status=%d", err));
-
- return err ? PJMEDIA_ERRNO_FROM_PORTAUDIO(err) : PJ_SUCCESS;
-}
-
-/*
- * Destroy stream.
- */
-PJ_DEF(pj_status_t) pjmedia_snd_stream_close(pjmedia_snd_stream *stream)
-{
- int i, err = 0;
-
- stream->quit_flag = 1;
- for (i=0; !stream->rec_thread_exited && i<100; ++i) {
- pj_thread_sleep(1);
- }
- for (i=0; !stream->play_thread_exited && i<100; ++i) {
- pj_thread_sleep(1);
- }
-
- PJ_LOG(5,(THIS_FILE, "Closing %.*s: %lu underflow, %lu overflow",
- (int)stream->name.slen,
- stream->name.ptr,
- stream->underflow, stream->overflow));
-
- if (stream->play_strm)
- err = Pa_CloseStream(stream->play_strm);
-
- if (stream->rec_strm && stream->rec_strm != stream->play_strm)
- err = Pa_CloseStream(stream->rec_strm);
-
- pj_pool_release(stream->pool);
-
- return err ? PJMEDIA_ERRNO_FROM_PORTAUDIO(err) : PJ_SUCCESS;
-}
-
-/*
- * Deinitialize sound library.
- */
-PJ_DEF(pj_status_t) pjmedia_snd_deinit(void)
-{
- if (--snd_init_count == 0) {
- int err;
-
- PJ_LOG(4,(THIS_FILE, "PortAudio sound library shutting down.."));
-
- err = Pa_Terminate();
-
- return err ? PJMEDIA_ERRNO_FROM_PORTAUDIO(err) : PJ_SUCCESS;
- } else {
- return PJ_SUCCESS;
- }
-}
-
-/*
- * Set sound latency.
- */
-PJ_DEF(pj_status_t) pjmedia_snd_set_latency(unsigned input_latency,
- unsigned output_latency)
-{
- snd_input_latency = (input_latency == 0)?
- PJMEDIA_SND_DEFAULT_REC_LATENCY : input_latency;
- snd_output_latency = (output_latency == 0)?
- PJMEDIA_SND_DEFAULT_PLAY_LATENCY : output_latency;
-
- return PJ_SUCCESS;
-}
-
-#endif /* PJMEDIA_SOUND_IMPLEMENTATION */
diff --git a/pjmedia/src/pjmedia/sound_legacy.c b/pjmedia/src/pjmedia/sound_legacy.c
new file mode 100644
index 00000000..3500588c
--- /dev/null
+++ b/pjmedia/src/pjmedia/sound_legacy.c
@@ -0,0 +1,284 @@
+/* $Id$ */
+/*
+ * Copyright (C) 2008-2009 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
+ */
+
+/*
+ * This is implementation of legacy sound device API, for applications
+ * that still use the old/deprecated sound device API. This implementation
+ * uses the new Audio Device API.
+ *
+ * Please see http://trac.pjsip.org/repos/wiki/Audio_Dev_API for more
+ * information.
+ */
+
+#include <pjmedia/sound.h>
+#include <pjmedia-audiodev/errno.h>
+#include <pj/assert.h>
+
+#if PJMEDIA_HAS_LEGACY_SOUND_API
+
+static struct legacy_subsys
+{
+ pjmedia_snd_dev_info info[4];
+ unsigned info_counter;
+ unsigned user_rec_latency;
+ unsigned user_play_latency;
+} g_sys;
+
+struct pjmedia_snd_stream
+{
+ pj_pool_t *pool;
+ pjmedia_aud_stream *aud_strm;
+ pjmedia_snd_rec_cb user_rec_cb;
+ pjmedia_snd_play_cb user_play_cb;
+ void *user_user_data;
+};
+
+PJ_DEF(pj_status_t) pjmedia_snd_init(pj_pool_factory *factory)
+{
+ return pjmedia_aud_subsys_init(factory);
+}
+
+PJ_DEF(pj_status_t) pjmedia_snd_deinit(void)
+{
+ return pjmedia_aud_subsys_shutdown();
+}
+
+PJ_DEF(int) pjmedia_snd_get_dev_count(void)
+{
+ return pjmedia_aud_dev_count();
+}
+
+PJ_DEF(const pjmedia_snd_dev_info*) pjmedia_snd_get_dev_info(unsigned index)
+{
+ pjmedia_snd_dev_info *oi = &g_sys.info[g_sys.info_counter];
+ pjmedia_aud_dev_info di;
+
+ g_sys.info_counter = (g_sys.info_counter+1) % PJ_ARRAY_SIZE(g_sys.info);
+
+ if (pjmedia_aud_dev_get_info(index, &di) != PJ_SUCCESS)
+ return NULL;
+
+ pj_bzero(oi, sizeof(*oi));
+ pj_ansi_strncpy(oi->name, di.name, sizeof(oi->name));
+ oi->name[sizeof(oi->name)-1] = '\0';
+ oi->input_count = di.input_count;
+ oi->output_count = di.output_count;
+ oi->default_samples_per_sec = di.default_samples_per_sec;
+
+ return oi;
+}
+
+
+static pj_status_t snd_play_cb(void *user_data,
+ pjmedia_frame *frame)
+{
+ pjmedia_snd_stream *strm = (pjmedia_snd_stream*)user_data;
+
+ frame->type = PJMEDIA_FRAME_TYPE_AUDIO;
+ return strm->user_play_cb(strm->user_user_data,
+ frame->timestamp.u32.lo,
+ frame->buf,
+ frame->size);
+}
+
+static pj_status_t snd_rec_cb(void *user_data,
+ pjmedia_frame *frame)
+{
+ pjmedia_snd_stream *strm = (pjmedia_snd_stream*)user_data;
+ return strm->user_rec_cb(strm->user_user_data,
+ frame->timestamp.u32.lo,
+ frame->buf,
+ frame->size);
+}
+
+static pj_status_t open_stream( pjmedia_dir dir,
+ int rec_id,
+ int play_id,
+ unsigned clock_rate,
+ unsigned channel_count,
+ unsigned samples_per_frame,
+ unsigned bits_per_sample,
+ pjmedia_snd_rec_cb rec_cb,
+ pjmedia_snd_play_cb play_cb,
+ void *user_data,
+ pjmedia_snd_stream **p_snd_strm)
+{
+ pj_pool_t *pool;
+ pjmedia_snd_stream *snd_strm;
+ pjmedia_aud_param param;
+ pj_status_t status;
+
+ /* Initialize parameters */
+ if (dir & PJMEDIA_DIR_CAPTURE) {
+ status = pjmedia_aud_dev_default_param(rec_id, &param);
+ } else {
+ status = pjmedia_aud_dev_default_param(play_id, &param);
+ }
+ if (status != PJ_SUCCESS)
+ return status;
+
+ param.dir = dir;
+ param.rec_id = rec_id;
+ param.play_id = play_id;
+ param.clock_rate = clock_rate;
+ param.channel_count = channel_count;
+ param.samples_per_frame = samples_per_frame;
+ param.bits_per_sample = bits_per_sample;
+
+ /* Latencies setting */
+ if ((dir & PJMEDIA_DIR_CAPTURE) && g_sys.user_rec_latency) {
+ param.flags |= PJMEDIA_AUD_DEV_CAP_INPUT_LATENCY;
+ param.input_latency_ms = g_sys.user_rec_latency;
+ }
+ if ((dir & PJMEDIA_DIR_PLAYBACK) && g_sys.user_play_latency) {
+ param.flags |= PJMEDIA_AUD_DEV_CAP_OUTPUT_LATENCY;
+ param.output_latency_ms = g_sys.user_play_latency;
+ }
+
+ /* Create sound wrapper */
+ pool = pj_pool_create(pjmedia_aud_subsys_get_pool_factory(),
+ "legacy-snd", 512, 512, NULL);
+ snd_strm = PJ_POOL_ZALLOC_T(pool, pjmedia_snd_stream);
+ snd_strm->pool = pool;
+ snd_strm->user_rec_cb = rec_cb;
+ snd_strm->user_play_cb = play_cb;
+ snd_strm->user_user_data = user_data;
+
+ /* Create the stream */
+ status = pjmedia_aud_stream_create(&param, &snd_rec_cb,
+ &snd_play_cb, snd_strm,
+ &snd_strm->aud_strm);
+ if (status != PJ_SUCCESS) {
+ pj_pool_release(pool);
+ return status;
+ }
+
+ *p_snd_strm = snd_strm;
+ return PJ_SUCCESS;
+}
+
+PJ_DEF(pj_status_t) pjmedia_snd_open_rec( int index,
+ unsigned clock_rate,
+ unsigned channel_count,
+ unsigned samples_per_frame,
+ unsigned bits_per_sample,
+ pjmedia_snd_rec_cb rec_cb,
+ void *user_data,
+ pjmedia_snd_stream **p_snd_strm)
+{
+ return open_stream(PJMEDIA_DIR_CAPTURE, index, PJMEDIA_AUD_INVALID_DEV,
+ clock_rate, channel_count, samples_per_frame,
+ bits_per_sample, rec_cb, NULL,
+ user_data, p_snd_strm);
+}
+
+PJ_DEF(pj_status_t) pjmedia_snd_open_player( int index,
+ unsigned clock_rate,
+ unsigned channel_count,
+ unsigned samples_per_frame,
+ unsigned bits_per_sample,
+ pjmedia_snd_play_cb play_cb,
+ void *user_data,
+ pjmedia_snd_stream **p_snd_strm )
+{
+ return open_stream(PJMEDIA_DIR_PLAYBACK, PJMEDIA_AUD_INVALID_DEV, index,
+ clock_rate, channel_count, samples_per_frame,
+ bits_per_sample, NULL, play_cb,
+ user_data, p_snd_strm);
+}
+
+PJ_DEF(pj_status_t) pjmedia_snd_open( int rec_id,
+ int play_id,
+ unsigned clock_rate,
+ unsigned channel_count,
+ unsigned samples_per_frame,
+ unsigned bits_per_sample,
+ pjmedia_snd_rec_cb rec_cb,
+ pjmedia_snd_play_cb play_cb,
+ void *user_data,
+ pjmedia_snd_stream **p_snd_strm)
+{
+ return open_stream(PJMEDIA_DIR_CAPTURE_PLAYBACK, rec_id, play_id,
+ clock_rate, channel_count, samples_per_frame,
+ bits_per_sample, rec_cb, play_cb,
+ user_data, p_snd_strm);
+}
+
+PJ_DEF(pj_status_t) pjmedia_snd_stream_start(pjmedia_snd_stream *stream)
+{
+ return pjmedia_aud_stream_start(stream->aud_strm);
+}
+
+PJ_DEF(pj_status_t) pjmedia_snd_stream_stop(pjmedia_snd_stream *stream)
+{
+ return pjmedia_aud_stream_stop(stream->aud_strm);
+}
+
+PJ_DEF(pj_status_t) pjmedia_snd_stream_get_info(pjmedia_snd_stream *strm,
+ pjmedia_snd_stream_info *pi)
+{
+ pjmedia_aud_param param;
+ pj_status_t status;
+
+ status = pjmedia_aud_stream_get_param(strm->aud_strm, &param);
+ if (status != PJ_SUCCESS)
+ return status;
+
+ pj_bzero(pi, sizeof(*pi));
+ pi->dir = param.dir;
+ pi->play_id = param.play_id;
+ pi->rec_id = param.rec_id;
+ pi->clock_rate = param.clock_rate;
+ pi->channel_count = param.channel_count;
+ pi->samples_per_frame = param.samples_per_frame;
+ pi->bits_per_sample = param.bits_per_sample;
+
+ if (param.flags & PJMEDIA_AUD_DEV_CAP_INPUT_LATENCY) {
+ pi->rec_latency = param.input_latency_ms;
+ }
+ if (param.flags & PJMEDIA_AUD_DEV_CAP_OUTPUT_LATENCY) {
+ pi->play_latency = param.output_latency_ms;
+ }
+
+ return PJ_SUCCESS;
+}
+
+
+PJ_DEF(pj_status_t) pjmedia_snd_stream_close(pjmedia_snd_stream *stream)
+{
+ pj_status_t status;
+
+ status = pjmedia_aud_stream_destroy(stream->aud_strm);
+ if (status != PJ_SUCCESS)
+ return status;
+
+ pj_pool_release(stream->pool);
+ return PJ_SUCCESS;
+}
+
+PJ_DEF(pj_status_t) pjmedia_snd_set_latency(unsigned input_latency,
+ unsigned output_latency)
+{
+ g_sys.user_rec_latency = input_latency;
+ g_sys.user_play_latency = output_latency;
+ return PJ_SUCCESS;
+}
+
+#endif /* PJMEDIA_HAS_LEGACY_SOUND_API */
+
diff --git a/pjmedia/src/pjmedia/sound_port.c b/pjmedia/src/pjmedia/sound_port.c
index 3a21ee5d..7117c82b 100644
--- a/pjmedia/src/pjmedia/sound_port.c
+++ b/pjmedia/src/pjmedia/sound_port.c
@@ -18,16 +18,15 @@
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include <pjmedia/sound_port.h>
+#include <pjmedia/alaw_ulaw.h>
#include <pjmedia/delaybuf.h>
#include <pjmedia/echo.h>
#include <pjmedia/errno.h>
-#include <pjmedia/plc.h>
#include <pj/assert.h>
#include <pj/log.h>
#include <pj/rand.h>
#include <pj/string.h> /* pj_memset() */
-//#define SIMULATE_LOST_PCT 20
#define AEC_TAIL 128 /* default AEC length in ms */
#define AEC_SUSPEND_LIMIT 5 /* seconds of no activity */
@@ -35,119 +34,55 @@
//#define TEST_OVERFLOW_UNDERFLOW
-enum
-{
- PJMEDIA_PLC_ENABLED = 1,
-};
-
-//#define DEFAULT_OPTIONS PJMEDIA_PLC_ENABLED
-#define DEFAULT_OPTIONS 0
-
-
struct pjmedia_snd_port
{
int rec_id;
int play_id;
- pjmedia_snd_stream *snd_stream;
+ pj_uint32_t aud_caps;
+ pjmedia_aud_param aud_param;
+ pjmedia_aud_stream *aud_stream;
pjmedia_dir dir;
pjmedia_port *port;
- unsigned options;
-
- pjmedia_echo_state *ec_state;
- unsigned aec_tail_len;
-
- pj_bool_t ec_suspended;
- unsigned ec_suspend_count;
- unsigned ec_suspend_limit;
-
- pjmedia_plc *plc;
unsigned clock_rate;
unsigned channel_count;
unsigned samples_per_frame;
unsigned bits_per_sample;
-#if PJMEDIA_SOUND_USE_DELAYBUF
- pjmedia_delay_buf *delay_buf;
-#endif
+ /* software ec */
+ pjmedia_echo_state *ec_state;
+ unsigned ec_options;
+ unsigned ec_tail_len;
+ pj_bool_t ec_suspended;
+ unsigned ec_suspend_count;
+ unsigned ec_suspend_limit;
};
/*
* The callback called by sound player when it needs more samples to be
* played.
*/
-static pj_status_t play_cb(/* in */ void *user_data,
- /* in */ pj_uint32_t timestamp,
- /* out */ void *output,
- /* out */ unsigned size)
+static pj_status_t play_cb(void *user_data, pjmedia_frame *frame)
{
pjmedia_snd_port *snd_port = (pjmedia_snd_port*) user_data;
pjmedia_port *port;
- pjmedia_frame frame;
+ unsigned required_size = frame->size;
pj_status_t status;
- /* We're risking accessing the port without holding any mutex.
- * It's possible that port is disconnected then destroyed while
- * we're trying to access it.
- * But in the name of performance, we'll try this approach until
- * someone complains when it crashes.
- */
port = snd_port->port;
if (port == NULL)
goto no_frame;
- frame.buf = output;
- frame.size = size;
- frame.timestamp.u32.hi = 0;
- frame.timestamp.u32.lo = timestamp;
-
-#if PJMEDIA_SOUND_USE_DELAYBUF
- if (snd_port->delay_buf) {
- status = pjmedia_delay_buf_get(snd_port->delay_buf, (pj_int16_t*)output);
- if (status != PJ_SUCCESS)
- pj_bzero(output, size);
-
- frame.type = PJMEDIA_FRAME_TYPE_AUDIO;
- pjmedia_port_put_frame(port, &frame);
-
-#ifdef TEST_OVERFLOW_UNDERFLOW
- {
- static int count = 1;
- if (++count % 10 == 0) {
- status = pjmedia_delay_buf_get(snd_port->delay_buf,
- (pj_int16_t*)output);
- if (status != PJ_SUCCESS)
- pj_bzero(output, size);
-
- frame.type = PJMEDIA_FRAME_TYPE_AUDIO;
- pjmedia_port_put_frame(port, &frame);
- }
- }
-#endif
-
- }
-#endif
-
- status = pjmedia_port_get_frame(port, &frame);
+ status = pjmedia_port_get_frame(port, frame);
if (status != PJ_SUCCESS)
goto no_frame;
- if (frame.type != PJMEDIA_FRAME_TYPE_AUDIO)
+ if (frame->type != PJMEDIA_FRAME_TYPE_AUDIO)
goto no_frame;
/* Must supply the required samples */
- pj_assert(frame.size == size);
-
-#ifdef SIMULATE_LOST_PCT
- /* Simulate packet lost */
- if (pj_rand() % 100 < SIMULATE_LOST_PCT) {
- PJ_LOG(4,(THIS_FILE, "Frame dropped"));
- goto no_frame;
- }
-#endif
-
- if (snd_port->plc)
- pjmedia_plc_save(snd_port->plc, (pj_int16_t*) output);
+ PJ_UNUSED_ARG(required_size);
+ pj_assert(frame->size == required_size);
if (snd_port->ec_state) {
if (snd_port->ec_suspended) {
@@ -156,7 +91,7 @@ static pj_status_t play_cb(/* in */ void *user_data,
PJ_LOG(4,(THIS_FILE, "EC activated"));
}
snd_port->ec_suspend_count = 0;
- pjmedia_echo_playback(snd_port->ec_state, (pj_int16_t*)output);
+ pjmedia_echo_playback(snd_port->ec_state, (pj_int16_t*)frame->buf);
}
@@ -172,22 +107,10 @@ no_frame:
}
if (snd_port->ec_state) {
/* To maintain correct delay in EC */
- pjmedia_echo_playback(snd_port->ec_state, (pj_int16_t*)output);
+ pjmedia_echo_playback(snd_port->ec_state, (pj_int16_t*)frame->buf);
}
}
- /* Apply PLC */
- if (snd_port->plc) {
-
- pjmedia_plc_generate(snd_port->plc, (pj_int16_t*) output);
-#ifdef SIMULATE_LOST_PCT
- PJ_LOG(4,(THIS_FILE, "Lost frame generated"));
-#endif
- } else {
- pj_bzero(output, size);
- }
-
-
return PJ_SUCCESS;
}
@@ -196,49 +119,59 @@ no_frame:
* The callback called by sound recorder when it has finished capturing a
* frame.
*/
-static pj_status_t rec_cb(/* in */ void *user_data,
- /* in */ pj_uint32_t timestamp,
- /* in */ void *input,
- /* in*/ unsigned size)
+static pj_status_t rec_cb(void *user_data, pjmedia_frame *frame)
{
pjmedia_snd_port *snd_port = (pjmedia_snd_port*) user_data;
pjmedia_port *port;
- pjmedia_frame frame;
- /* We're risking accessing the port without holding any mutex.
- * It's possible that port is disconnected then destroyed while
- * we're trying to access it.
- * But in the name of performance, we'll try this approach until
- * someone complains when it crashes.
- */
port = snd_port->port;
if (port == NULL)
return PJ_SUCCESS;
/* Cancel echo */
if (snd_port->ec_state && !snd_port->ec_suspended) {
- pjmedia_echo_capture(snd_port->ec_state, (pj_int16_t*) input, 0);
+ pjmedia_echo_capture(snd_port->ec_state, (pj_int16_t*) frame->buf, 0);
}
-#if PJMEDIA_SOUND_USE_DELAYBUF
- if (snd_port->delay_buf) {
- pjmedia_delay_buf_put(snd_port->delay_buf, (pj_int16_t*)input);
- } else {
- frame.buf = (void*)input;
- frame.size = size;
- frame.type = PJMEDIA_FRAME_TYPE_AUDIO;
- frame.timestamp.u32.lo = timestamp;
+ pjmedia_port_put_frame(port, frame);
+
+ return PJ_SUCCESS;
+}
- pjmedia_port_put_frame(port, &frame);
+/*
+ * The callback called by sound player when it needs more samples to be
+ * played. This version is for non-PCM data.
+ */
+static pj_status_t play_cb_ext(void *user_data, pjmedia_frame *frame)
+{
+ pjmedia_snd_port *snd_port = (pjmedia_snd_port*) user_data;
+ pjmedia_port *port = snd_port->port;
+
+ if (port == NULL) {
+ frame->type = PJMEDIA_FRAME_TYPE_NONE;
+ return PJ_SUCCESS;
}
-#else
- frame.buf = (void*)input;
- frame.size = size;
- frame.type = PJMEDIA_FRAME_TYPE_AUDIO;
- frame.timestamp.u32.lo = timestamp;
- pjmedia_port_put_frame(port, &frame);
-#endif
+ pjmedia_port_get_frame(port, frame);
+
+ return PJ_SUCCESS;
+}
+
+
+/*
+ * The callback called by sound recorder when it has finished capturing a
+ * frame. This version is for non-PCM data.
+ */
+static pj_status_t rec_cb_ext(void *user_data, pjmedia_frame *frame)
+{
+ pjmedia_snd_port *snd_port = (pjmedia_snd_port*) user_data;
+ pjmedia_port *port;
+
+ port = snd_port->port;
+ if (port == NULL)
+ return PJ_SUCCESS;
+
+ pjmedia_port_put_frame(port, frame);
return PJ_SUCCESS;
}
@@ -250,84 +183,102 @@ static pj_status_t rec_cb(/* in */ void *user_data,
static pj_status_t start_sound_device( pj_pool_t *pool,
pjmedia_snd_port *snd_port )
{
+ pjmedia_aud_rec_cb snd_rec_cb;
+ pjmedia_aud_play_cb snd_play_cb;
+ pjmedia_aud_param param_copy;
pj_status_t status;
/* Check if sound has been started. */
- if (snd_port->snd_stream != NULL)
+ if (snd_port->aud_stream != NULL)
return PJ_SUCCESS;
- /* Open sound stream. */
- if (snd_port->dir == PJMEDIA_DIR_CAPTURE) {
- status = pjmedia_snd_open_rec( snd_port->rec_id,
- snd_port->clock_rate,
- snd_port->channel_count,
- snd_port->samples_per_frame,
- snd_port->bits_per_sample,
- &rec_cb,
- snd_port,
- &snd_port->snd_stream);
+ PJ_ASSERT_RETURN(snd_port->dir == PJMEDIA_DIR_CAPTURE ||
+ snd_port->dir == PJMEDIA_DIR_PLAYBACK ||
+ snd_port->dir == PJMEDIA_DIR_CAPTURE_PLAYBACK,
+ PJ_EBUG);
- } else if (snd_port->dir == PJMEDIA_DIR_PLAYBACK) {
- status = pjmedia_snd_open_player( snd_port->play_id,
- snd_port->clock_rate,
- snd_port->channel_count,
- snd_port->samples_per_frame,
- snd_port->bits_per_sample,
- &play_cb,
- snd_port,
- &snd_port->snd_stream);
-
- } else if (snd_port->dir == PJMEDIA_DIR_CAPTURE_PLAYBACK) {
- status = pjmedia_snd_open( snd_port->rec_id,
- snd_port->play_id,
- snd_port->clock_rate,
- snd_port->channel_count,
- snd_port->samples_per_frame,
- snd_port->bits_per_sample,
- &rec_cb,
- &play_cb,
- snd_port,
- &snd_port->snd_stream);
+ /* Get device caps */
+ if (snd_port->aud_param.dir & PJMEDIA_DIR_CAPTURE) {
+ pjmedia_aud_dev_info dev_info;
+
+ status = pjmedia_aud_dev_get_info(snd_port->aud_param.rec_id,
+ &dev_info);
+ if (status != PJ_SUCCESS)
+ return status;
+
+ snd_port->aud_caps = dev_info.caps;
+ } else {
+ snd_port->aud_caps = 0;
+ }
+
+ /* Process EC settings */
+ pj_memcpy(&param_copy, &snd_port->aud_param, sizeof(param_copy));
+ if (param_copy.flags & PJMEDIA_AUD_DEV_CAP_EC) {
+ /* EC is wanted */
+ if (snd_port->aud_caps & PJMEDIA_AUD_DEV_CAP_EC) {
+ /* Device supports EC */
+ /* Nothing to do */
+ } else {
+ /* Device doesn't support EC, remove EC settings from
+ * device parameters
+ */
+ param_copy.flags &= ~(PJMEDIA_AUD_DEV_CAP_EC |
+ PJMEDIA_AUD_DEV_CAP_EC_TAIL);
+ }
+ }
+
+ /* Use different callback if format is not PCM */
+ if (snd_port->aud_param.ext_fmt.id == PJMEDIA_FORMAT_L16) {
+ snd_rec_cb = &rec_cb;
+ snd_play_cb = &play_cb;
} else {
- pj_assert(!"Invalid dir");
- status = PJ_EBUG;
+ snd_rec_cb = &rec_cb_ext;
+ snd_play_cb = &play_cb_ext;
}
+ /* Open the device */
+ status = pjmedia_aud_stream_create(&param_copy,
+ snd_rec_cb,
+ snd_play_cb,
+ snd_port,
+ &snd_port->aud_stream);
+
if (status != PJ_SUCCESS)
return status;
+ /* Inactivity limit before EC is suspended. */
+ snd_port->ec_suspend_limit = AEC_SUSPEND_LIMIT *
+ (snd_port->clock_rate /
+ snd_port->samples_per_frame);
-#ifdef SIMULATE_LOST_PCT
- snd_port->options |= PJMEDIA_PLC_ENABLED;
-#endif
-
- /* If we have player components, allocate buffer to save the last
- * frame played to the speaker. The last frame is used for packet
- * lost concealment (PLC) algorithm.
+ /* Create software EC if parameter specifies EC but device
+ * doesn't support EC. Only do this if the format is PCM!
*/
- if ((snd_port->dir & PJMEDIA_DIR_PLAYBACK) &&
- (snd_port->options & PJMEDIA_PLC_ENABLED))
+ if ((snd_port->aud_param.flags & PJMEDIA_AUD_DEV_CAP_EC) &&
+ (snd_port->aud_caps & PJMEDIA_AUD_DEV_CAP_EC)==0 &&
+ param_copy.ext_fmt.id == PJMEDIA_FORMAT_PCM)
{
- status = pjmedia_plc_create(pool, snd_port->clock_rate,
- snd_port->samples_per_frame *
- snd_port->channel_count,
- 0, &snd_port->plc);
+ if ((snd_port->aud_param.flags & PJMEDIA_AUD_DEV_CAP_EC_TAIL)==0) {
+ snd_port->aud_param.flags |= PJMEDIA_AUD_DEV_CAP_EC_TAIL;
+ snd_port->aud_param.ec_tail_ms = AEC_TAIL;
+ PJ_LOG(4,(THIS_FILE, "AEC tail is set to default %u ms",
+ snd_port->aud_param.ec_tail_ms));
+ }
+
+ status = pjmedia_snd_port_set_ec(snd_port, pool,
+ snd_port->aud_param.ec_tail_ms, 0);
if (status != PJ_SUCCESS) {
- PJ_LOG(4,(THIS_FILE, "Unable to create PLC"));
- snd_port->plc = NULL;
+ pjmedia_aud_stream_destroy(snd_port->aud_stream);
+ snd_port->aud_stream = NULL;
+ return status;
}
}
- /* Inactivity limit before EC is suspended. */
- snd_port->ec_suspend_limit = AEC_SUSPEND_LIMIT *
- (snd_port->clock_rate /
- snd_port->samples_per_frame);
-
/* Start sound stream. */
- status = pjmedia_snd_stream_start(snd_port->snd_stream);
+ status = pjmedia_aud_stream_start(snd_port->aud_stream);
if (status != PJ_SUCCESS) {
- pjmedia_snd_stream_close(snd_port->snd_stream);
- snd_port->snd_stream = NULL;
+ pjmedia_aud_stream_destroy(snd_port->aud_stream);
+ snd_port->aud_stream = NULL;
return status;
}
@@ -342,10 +293,10 @@ static pj_status_t start_sound_device( pj_pool_t *pool,
static pj_status_t stop_sound_device( pjmedia_snd_port *snd_port )
{
/* Check if we have sound stream device. */
- if (snd_port->snd_stream) {
- pjmedia_snd_stream_stop(snd_port->snd_stream);
- pjmedia_snd_stream_close(snd_port->snd_stream);
- snd_port->snd_stream = NULL;
+ if (snd_port->aud_stream) {
+ pjmedia_aud_stream_stop(snd_port->aud_stream);
+ pjmedia_aud_stream_destroy(snd_port->aud_stream);
+ snd_port->aud_stream = NULL;
}
/* Destroy AEC */
@@ -371,47 +322,24 @@ PJ_DEF(pj_status_t) pjmedia_snd_port_create( pj_pool_t *pool,
unsigned options,
pjmedia_snd_port **p_port)
{
- pjmedia_snd_port *snd_port;
-
- PJ_ASSERT_RETURN(pool && p_port, PJ_EINVAL);
-
- snd_port = PJ_POOL_ZALLOC_T(pool, pjmedia_snd_port);
- PJ_ASSERT_RETURN(snd_port, PJ_ENOMEM);
+ pjmedia_aud_param param;
+ pj_status_t status;
- snd_port->rec_id = rec_id;
- snd_port->play_id = play_id;
- snd_port->dir = PJMEDIA_DIR_CAPTURE_PLAYBACK;
- snd_port->options = options | DEFAULT_OPTIONS;
- snd_port->clock_rate = clock_rate;
- snd_port->channel_count = channel_count;
- snd_port->samples_per_frame = samples_per_frame;
- snd_port->bits_per_sample = bits_per_sample;
-
-#if PJMEDIA_SOUND_USE_DELAYBUF
- do {
- pj_status_t status;
- unsigned ptime;
-
- ptime = samples_per_frame * 1000 / (clock_rate * channel_count);
-
- status = pjmedia_delay_buf_create(pool, "snd_buff",
- clock_rate, samples_per_frame,
- channel_count,
- PJMEDIA_SOUND_BUFFER_COUNT * ptime,
- 0, &snd_port->delay_buf);
- PJ_ASSERT_RETURN(status == PJ_SUCCESS, status);
- } while (0);
-#endif
+ PJ_UNUSED_ARG(options);
- *p_port = snd_port;
+ status = pjmedia_aud_dev_default_param(rec_id, &param);
+ if (status != PJ_SUCCESS)
+ return status;
+ param.dir = PJMEDIA_DIR_CAPTURE_PLAYBACK;
+ param.rec_id = rec_id;
+ param.play_id = play_id;
+ param.clock_rate = clock_rate;
+ param.channel_count = channel_count;
+ param.samples_per_frame = samples_per_frame;
+ param.bits_per_sample = bits_per_sample;
- /* Start sound device immediately.
- * If there's no port connected, the sound callback will return
- * empty signal.
- */
- return start_sound_device( pool, snd_port );
-
+ return pjmedia_snd_port_create2(pool, &param, p_port);
}
/*
@@ -426,28 +354,23 @@ PJ_DEF(pj_status_t) pjmedia_snd_port_create_rec( pj_pool_t *pool,
unsigned options,
pjmedia_snd_port **p_port)
{
- pjmedia_snd_port *snd_port;
-
- PJ_ASSERT_RETURN(pool && p_port, PJ_EINVAL);
+ pjmedia_aud_param param;
+ pj_status_t status;
- snd_port = PJ_POOL_ZALLOC_T(pool, pjmedia_snd_port);
- PJ_ASSERT_RETURN(snd_port, PJ_ENOMEM);
+ PJ_UNUSED_ARG(options);
- snd_port->rec_id = dev_id;
- snd_port->dir = PJMEDIA_DIR_CAPTURE;
- snd_port->options = options | DEFAULT_OPTIONS;
- snd_port->clock_rate = clock_rate;
- snd_port->channel_count = channel_count;
- snd_port->samples_per_frame = samples_per_frame;
- snd_port->bits_per_sample = bits_per_sample;
+ status = pjmedia_aud_dev_default_param(dev_id, &param);
+ if (status != PJ_SUCCESS)
+ return status;
- *p_port = snd_port;
+ param.dir = PJMEDIA_DIR_CAPTURE;
+ param.rec_id = dev_id;
+ param.clock_rate = clock_rate;
+ param.channel_count = channel_count;
+ param.samples_per_frame = samples_per_frame;
+ param.bits_per_sample = bits_per_sample;
- /* Start sound device immediately.
- * If there's no port connected, the sound callback will return
- * empty signal.
- */
- return start_sound_device( pool, snd_port );
+ return pjmedia_snd_port_create2(pool, &param, p_port);
}
@@ -463,28 +386,63 @@ PJ_DEF(pj_status_t) pjmedia_snd_port_create_player( pj_pool_t *pool,
unsigned options,
pjmedia_snd_port **p_port)
{
+ pjmedia_aud_param param;
+ pj_status_t status;
+
+ PJ_UNUSED_ARG(options);
+
+ status = pjmedia_aud_dev_default_param(dev_id, &param);
+ if (status != PJ_SUCCESS)
+ return status;
+
+ param.dir = PJMEDIA_DIR_PLAYBACK;
+ param.play_id = dev_id;
+ param.clock_rate = clock_rate;
+ param.channel_count = channel_count;
+ param.samples_per_frame = samples_per_frame;
+ param.bits_per_sample = bits_per_sample;
+
+ return pjmedia_snd_port_create2(pool, &param, p_port);
+}
+
+
+/*
+ * Create sound port.
+ */
+PJ_DEF(pj_status_t) pjmedia_snd_port_create2(pj_pool_t *pool,
+ const pjmedia_aud_param *prm,
+ pjmedia_snd_port **p_port)
+{
pjmedia_snd_port *snd_port;
+ pj_status_t status;
- PJ_ASSERT_RETURN(pool && p_port, PJ_EINVAL);
+ PJ_ASSERT_RETURN(pool && prm && p_port, PJ_EINVAL);
snd_port = PJ_POOL_ZALLOC_T(pool, pjmedia_snd_port);
PJ_ASSERT_RETURN(snd_port, PJ_ENOMEM);
- snd_port->play_id = dev_id;
- snd_port->dir = PJMEDIA_DIR_PLAYBACK;
- snd_port->options = options | DEFAULT_OPTIONS;
- snd_port->clock_rate = clock_rate;
- snd_port->channel_count = channel_count;
- snd_port->samples_per_frame = samples_per_frame;
- snd_port->bits_per_sample = bits_per_sample;
-
- *p_port = snd_port;
-
+ snd_port->dir = prm->dir;
+ snd_port->rec_id = prm->rec_id;
+ snd_port->play_id = prm->play_id;
+ snd_port->dir = PJMEDIA_DIR_CAPTURE_PLAYBACK;
+ snd_port->clock_rate = prm->clock_rate;
+ snd_port->channel_count = prm->channel_count;
+ snd_port->samples_per_frame = prm->samples_per_frame;
+ snd_port->bits_per_sample = prm->bits_per_sample;
+ pj_memcpy(&snd_port->aud_param, prm, sizeof(*prm));
+
/* Start sound device immediately.
* If there's no port connected, the sound callback will return
* empty signal.
*/
- return start_sound_device( pool, snd_port );
+ status = start_sound_device( pool, snd_port );
+ if (status != PJ_SUCCESS) {
+ pjmedia_snd_port_destroy(snd_port);
+ return status;
+ }
+
+ *p_port = snd_port;
+ return PJ_SUCCESS;
}
@@ -502,23 +460,23 @@ PJ_DEF(pj_status_t) pjmedia_snd_port_destroy(pjmedia_snd_port *snd_port)
/*
* Retrieve the sound stream associated by this sound device port.
*/
-PJ_DEF(pjmedia_snd_stream*) pjmedia_snd_port_get_snd_stream(
+PJ_DEF(pjmedia_aud_stream*) pjmedia_snd_port_get_snd_stream(
pjmedia_snd_port *snd_port)
{
PJ_ASSERT_RETURN(snd_port, NULL);
- return snd_port->snd_stream;
+ return snd_port->aud_stream;
}
/*
- * Enable AEC
+ * Change EC settings.
*/
PJ_DEF(pj_status_t) pjmedia_snd_port_set_ec( pjmedia_snd_port *snd_port,
pj_pool_t *pool,
unsigned tail_ms,
unsigned options)
{
- pjmedia_snd_stream_info si;
+ pjmedia_aud_param prm;
pj_status_t status;
/* Sound must be opened in full-duplex mode */
@@ -526,43 +484,101 @@ PJ_DEF(pj_status_t) pjmedia_snd_port_set_ec( pjmedia_snd_port *snd_port,
snd_port->dir == PJMEDIA_DIR_CAPTURE_PLAYBACK,
PJ_EINVALIDOP);
- /* Sound port must have 16bits per sample */
- PJ_ASSERT_RETURN(snd_port->bits_per_sample == 16,
- PJ_EINVALIDOP);
+ /* Determine whether we use device or software EC */
+ if (snd_port->aud_caps & PJMEDIA_AUD_DEV_CAP_EC) {
+ /* We use device EC */
+ pj_bool_t ec_enabled;
- /* Destroy AEC */
- if (snd_port->ec_state) {
- pjmedia_echo_destroy(snd_port->ec_state);
- snd_port->ec_state = NULL;
- }
+ /* Query EC status */
+ status = pjmedia_aud_stream_get_cap(snd_port->aud_stream,
+ PJMEDIA_AUD_DEV_CAP_EC,
+ &ec_enabled);
+ if (status != PJ_SUCCESS)
+ return status;
- snd_port->aec_tail_len = tail_ms;
+ if (tail_ms != 0) {
+ /* Change EC setting */
- if (tail_ms != 0) {
- unsigned delay_ms;
+ if (!ec_enabled) {
+ /* Enable EC first */
+ pj_bool_t value = PJ_TRUE;
+ status = pjmedia_aud_stream_set_cap(snd_port->aud_stream,
+ PJMEDIA_AUD_DEV_CAP_EC,
+ &value);
+ if (status != PJ_SUCCESS)
+ return status;
+ }
- status = pjmedia_snd_stream_get_info(snd_port->snd_stream, &si);
- if (status != PJ_SUCCESS)
- si.rec_latency = si.play_latency = 0;
-
- //No need to add input latency in the latency calculation,
- //since actual input latency should be zero.
- //delay_ms = (si.rec_latency + si.play_latency) * 1000 /
- // snd_port->clock_rate;
- delay_ms = si.play_latency * 1000 / snd_port->clock_rate;
- status = pjmedia_echo_create2(pool, snd_port->clock_rate,
- snd_port->channel_count,
- snd_port->samples_per_frame,
- tail_ms, delay_ms,
- options, &snd_port->ec_state);
+ if ((snd_port->aud_caps & PJMEDIA_AUD_DEV_CAP_EC_TAIL)==0) {
+ /* Device does not support setting EC tail */
+ return PJMEDIA_EAUD_INVCAP;
+ }
+
+ return pjmedia_aud_stream_set_cap(snd_port->aud_stream,
+ PJMEDIA_AUD_DEV_CAP_EC_TAIL,
+ &tail_ms);
+
+ } else if (ec_enabled) {
+ /* Disable EC */
+ pj_bool_t value = PJ_FALSE;
+ return pjmedia_aud_stream_set_cap(snd_port->aud_stream,
+ PJMEDIA_AUD_DEV_CAP_EC,
+ &value);
+ } else {
+ /* Request to disable EC but EC has been disabled */
+ /* Do nothing */
+ return PJ_SUCCESS;
+ }
+
+ } else {
+ /* We use software EC */
+
+ /* Check if there is change in parameters */
+ if (tail_ms==snd_port->ec_tail_len && options==snd_port->ec_options) {
+ PJ_LOG(5,(THIS_FILE, "pjmedia_snd_port_set_ec() ignored, no "
+ "change in settings"));
+ return PJ_SUCCESS;
+ }
+
+ status = pjmedia_aud_stream_get_param(snd_port->aud_stream, &prm);
if (status != PJ_SUCCESS)
+ return status;
+
+ /* Audio stream must be in PCM format */
+ PJ_ASSERT_RETURN(prm.ext_fmt.id == PJMEDIA_FORMAT_PCM,
+ PJ_EINVALIDOP);
+
+ /* Destroy AEC */
+ if (snd_port->ec_state) {
+ pjmedia_echo_destroy(snd_port->ec_state);
snd_port->ec_state = NULL;
- else
- snd_port->ec_suspended = PJ_FALSE;
- } else {
- PJ_LOG(4,(THIS_FILE, "Echo canceller is now disabled in the "
- "sound port"));
- status = PJ_SUCCESS;
+ }
+
+ if (tail_ms != 0) {
+ unsigned delay_ms;
+
+ //No need to add input latency in the latency calculation,
+ //since actual input latency should be zero.
+ //delay_ms = (si.rec_latency + si.play_latency) * 1000 /
+ // snd_port->clock_rate;
+ delay_ms = prm.output_latency_ms;
+ status = pjmedia_echo_create2(pool, snd_port->clock_rate,
+ snd_port->channel_count,
+ snd_port->samples_per_frame,
+ tail_ms, delay_ms,
+ options, &snd_port->ec_state);
+ if (status != PJ_SUCCESS)
+ snd_port->ec_state = NULL;
+ else
+ snd_port->ec_suspended = PJ_FALSE;
+ } else {
+ PJ_LOG(4,(THIS_FILE, "Echo canceller is now disabled in the "
+ "sound port"));
+ status = PJ_SUCCESS;
+ }
+
+ snd_port->ec_options = options;
+ snd_port->ec_tail_len = tail_ms;
}
return status;
@@ -574,12 +590,42 @@ PJ_DEF(pj_status_t) pjmedia_snd_port_get_ec_tail( pjmedia_snd_port *snd_port,
unsigned *p_length)
{
PJ_ASSERT_RETURN(snd_port && p_length, PJ_EINVAL);
- *p_length = snd_port->ec_state ? snd_port->aec_tail_len : 0;
+
+ /* Determine whether we use device or software EC */
+ if (snd_port->aud_caps & PJMEDIA_AUD_DEV_CAP_EC) {
+ /* We use device EC */
+ pj_bool_t ec_enabled;
+ pj_status_t status;
+
+ /* Query EC status */
+ status = pjmedia_aud_stream_get_cap(snd_port->aud_stream,
+ PJMEDIA_AUD_DEV_CAP_EC,
+ &ec_enabled);
+ if (status != PJ_SUCCESS)
+ return status;
+
+ if (!ec_enabled) {
+ *p_length = 0;
+ } else if (snd_port->aud_caps & PJMEDIA_AUD_DEV_CAP_EC_TAIL) {
+ /* Get device EC tail */
+ status = pjmedia_aud_stream_get_cap(snd_port->aud_stream,
+ PJMEDIA_AUD_DEV_CAP_EC_TAIL,
+ p_length);
+ if (status != PJ_SUCCESS)
+ return status;
+ } else {
+ /* Just use default */
+ *p_length = AEC_TAIL;
+ }
+
+ } else {
+ /* We use software EC */
+ *p_length = snd_port->ec_state ? snd_port->ec_tail_len : 0;
+ }
return PJ_SUCCESS;
}
-
/*
* Connect a port.
*/
diff --git a/pjmedia/src/pjmedia/stream.c b/pjmedia/src/pjmedia/stream.c
index f1d01c27..1e289e98 100644
--- a/pjmedia/src/pjmedia/stream.c
+++ b/pjmedia/src/pjmedia/stream.c
@@ -408,6 +408,116 @@ static pj_status_t get_frame( pjmedia_port *port, pjmedia_frame *frame)
}
+/* The other version of get_frame callback used when stream port format
+ * is non linear PCM.
+ */
+static pj_status_t get_frame_ext( pjmedia_port *port, pjmedia_frame *frame)
+{
+ pjmedia_stream *stream = (pjmedia_stream*) port->port_data.pdata;
+ pjmedia_channel *channel = stream->dec;
+ pjmedia_frame_ext *f = (pjmedia_frame_ext*)frame;
+ unsigned samples_per_frame, samples_required;
+ pj_status_t status;
+
+ /* Return no frame if channel is paused */
+ if (channel->paused) {
+ frame->type = PJMEDIA_FRAME_TYPE_NONE;
+ return PJ_SUCCESS;
+ }
+
+ /* Repeat get frame from the jitter buffer and decode the frame
+ * until we have enough frames according to codec's ptime.
+ */
+
+ samples_required = stream->port.info.samples_per_frame;
+ samples_per_frame = stream->codec_param.info.frm_ptime *
+ stream->codec_param.info.clock_rate *
+ stream->codec_param.info.channel_cnt /
+ 1000;
+
+ pj_bzero(f, sizeof(pjmedia_frame_ext));
+ f->base.type = PJMEDIA_FRAME_TYPE_EXTENDED;
+
+ while (f->samples_cnt < samples_required) {
+ char frame_type;
+ pj_size_t frame_size;
+ pj_uint32_t bit_info;
+
+ /* Lock jitter buffer mutex first */
+ pj_mutex_lock( stream->jb_mutex );
+
+ /* Get frame from jitter buffer. */
+ pjmedia_jbuf_get_frame2(stream->jb, channel->out_pkt, &frame_size,
+ &frame_type, &bit_info);
+
+ /* Unlock jitter buffer mutex. */
+ pj_mutex_unlock( stream->jb_mutex );
+
+ if (frame_type == PJMEDIA_JB_NORMAL_FRAME) {
+ /* Got "NORMAL" frame from jitter buffer */
+ pjmedia_frame frame_in;
+
+ /* Decode */
+ frame_in.buf = channel->out_pkt;
+ frame_in.size = frame_size;
+ frame_in.bit_info = bit_info;
+ frame_in.type = PJMEDIA_FRAME_TYPE_AUDIO;
+
+ status = stream->codec->op->decode( stream->codec, &frame_in,
+ 0, frame);
+ if (status != PJ_SUCCESS) {
+ LOGERR_((port->info.name.ptr, "codec decode() error",
+ status));
+ pjmedia_frame_ext_append_subframe(f, NULL, 0,
+ (pj_uint16_t)samples_per_frame);
+ }
+ } else {
+ status = (*stream->codec->op->recover)(stream->codec,
+ 0, frame);
+ if (status != PJ_SUCCESS) {
+ pjmedia_frame_ext_append_subframe(f, NULL, 0,
+ (pj_uint16_t)samples_per_frame);
+ }
+
+ if (frame_type == PJMEDIA_JB_MISSING_FRAME) {
+ PJ_LOG(5,(stream->port.info.name.ptr, "Frame lost!"));
+ } else if (frame_type == PJMEDIA_JB_ZERO_EMPTY_FRAME) {
+ /* Jitter buffer is empty. Check if this is the first "empty"
+ * state.
+ */
+ if (frame_type != stream->jb_last_frm) {
+ pjmedia_jb_state jb_state;
+
+ /* Report the state of jitter buffer */
+ pjmedia_jbuf_get_state(stream->jb, &jb_state);
+ PJ_LOG(5,(stream->port.info.name.ptr,
+ "Jitter buffer empty (prefetch=%d)",
+ jb_state.prefetch));
+ }
+ } else {
+ pjmedia_jb_state jb_state;
+
+ /* It can only be PJMEDIA_JB_ZERO_PREFETCH frame */
+ pj_assert(frame_type == PJMEDIA_JB_ZERO_PREFETCH_FRAME);
+
+ /* Get the state of jitter buffer */
+ pjmedia_jbuf_get_state(stream->jb, &jb_state);
+
+ if (stream->jb_last_frm != frame_type) {
+ PJ_LOG(5,(stream->port.info.name.ptr,
+ "Jitter buffer is bufferring (prefetch=%d)",
+ jb_state.prefetch));
+ }
+ }
+ }
+
+ stream->jb_last_frm = frame_type;
+ }
+
+ return PJ_SUCCESS;
+}
+
+
/*
* Transmit DTMF
*/
@@ -686,6 +796,9 @@ static pj_status_t put_frame_imp( pjmedia_port *port,
/* Number of samples in the frame */
if (frame->type == PJMEDIA_FRAME_TYPE_AUDIO)
ts_len = (frame->size >> 1) / stream->codec_param.info.channel_cnt;
+ else if (frame->type == PJMEDIA_FRAME_TYPE_EXTENDED)
+ ts_len = stream->port.info.samples_per_frame /
+ stream->port.info.channel_count;
else
ts_len = 0;
@@ -752,6 +865,7 @@ static pj_status_t put_frame_imp( pjmedia_port *port,
*/
} else if (frame->type == PJMEDIA_FRAME_TYPE_AUDIO &&
frame->buf == NULL &&
+ stream->port.info.format.id == PJMEDIA_FORMAT_L16 &&
(stream->dir & PJMEDIA_DIR_ENCODING) &&
stream->codec_param.info.frm_ptime *
stream->codec_param.info.channel_cnt *
@@ -1483,9 +1597,18 @@ PJ_DEF(pj_status_t) pjmedia_stream_create( pjmedia_endpt *endpt,
pj_strdup(pool, &stream->port.info.encoding_name, &info->fmt.encoding_name);
stream->port.info.clock_rate = info->fmt.clock_rate;
stream->port.info.channel_count = info->fmt.channel_cnt;
+ stream->port.info.format.id = info->param->info.fmt_id;
stream->port.port_data.pdata = stream;
- stream->port.put_frame = &put_frame;
- stream->port.get_frame = &get_frame;
+ if (stream->port.info.format.id == PJMEDIA_FORMAT_L16) {
+ stream->port.put_frame = &put_frame;
+ stream->port.get_frame = &get_frame;
+ } else {
+ stream->port.info.format.bitrate = info->param->info.avg_bps;
+ stream->port.info.format.vad = (info->param->setting.vad != 0);
+
+ stream->port.put_frame = &put_frame;
+ stream->port.get_frame = &get_frame_ext;
+ }
/* Init stream: */
diff --git a/pjmedia/src/pjmedia/symbian_sound.cpp b/pjmedia/src/pjmedia/symbian_sound.cpp
deleted file mode 100644
index b7c92f9f..00000000
--- a/pjmedia/src/pjmedia/symbian_sound.cpp
+++ /dev/null
@@ -1,944 +0,0 @@
-/* $Id$ */
-/*
- * Copyright (C) 2008-2009 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/sound.h>
-#include <pjmedia/errno.h>
-#include <pj/assert.h>
-#include <pj/log.h>
-#include <pj/os.h>
-
-
-/*
- * This file provides sound implementation for Symbian Audio Streaming
- * device. Application using this sound abstraction must link with:
- * - mediaclientaudiostream.lib, and
- * - mediaclientaudioinputstream.lib
- */
-#include <mda/common/audio.h>
-#include <mdaaudiooutputstream.h>
-#include <mdaaudioinputstream.h>
-
-
-
-//////////////////////////////////////////////////////////////////////////////
-//
-
-#define THIS_FILE "symbian_sound.cpp"
-#define BYTES_PER_SAMPLE 2
-#define POOL_NAME "SymbianSound"
-#define POOL_SIZE 512
-#define POOL_INC 512
-
-static pjmedia_snd_dev_info symbian_snd_dev_info =
-{
- "Symbian Sound Device",
- 1,
- 1,
- 8000
-};
-
-class CPjAudioInputEngine;
-class CPjAudioOutputEngine;
-
-/*
- * PJMEDIA Sound Stream instance
- */
-struct pjmedia_snd_stream
-{
- // Pool
- pj_pool_t *pool;
-
- // Common settings.
- pjmedia_dir dir;
- unsigned clock_rate;
- unsigned channel_count;
- unsigned samples_per_frame;
-
- // Input stream
- CPjAudioInputEngine *inEngine;
-
- // Output stream
- CPjAudioOutputEngine *outEngine;
-};
-
-static pj_pool_factory *snd_pool_factory;
-
-
-/*
- * Convert clock rate to Symbian's TMdaAudioDataSettings capability.
- */
-static TInt get_clock_rate_cap(unsigned clock_rate)
-{
- switch (clock_rate) {
- case 8000: return TMdaAudioDataSettings::ESampleRate8000Hz;
- case 11025: return TMdaAudioDataSettings::ESampleRate11025Hz;
- case 12000: return TMdaAudioDataSettings::ESampleRate12000Hz;
- case 16000: return TMdaAudioDataSettings::ESampleRate16000Hz;
- case 22050: return TMdaAudioDataSettings::ESampleRate22050Hz;
- case 24000: return TMdaAudioDataSettings::ESampleRate24000Hz;
- case 32000: return TMdaAudioDataSettings::ESampleRate32000Hz;
- case 44100: return TMdaAudioDataSettings::ESampleRate44100Hz;
- case 48000: return TMdaAudioDataSettings::ESampleRate48000Hz;
- case 64000: return TMdaAudioDataSettings::ESampleRate64000Hz;
- case 96000: return TMdaAudioDataSettings::ESampleRate96000Hz;
- default:
- return 0;
- }
-}
-
-
-/*
- * Convert number of channels into Symbian's TMdaAudioDataSettings capability.
- */
-static TInt get_channel_cap(unsigned channel_count)
-{
- switch (channel_count) {
- case 1: return TMdaAudioDataSettings::EChannelsMono;
- case 2: return TMdaAudioDataSettings::EChannelsStereo;
- default:
- return 0;
- }
-}
-
-
-/*
- * Utility: print sound device error
- */
-static void snd_perror(const char *title, TInt rc)
-{
- PJ_LOG(1,(THIS_FILE, "%s: error code %d", title, rc));
-}
-
-//////////////////////////////////////////////////////////////////////////////
-//
-
-/*
- * Implementation: Symbian Input Stream.
- */
-class CPjAudioInputEngine : public CBase, MMdaAudioInputStreamCallback
-{
-public:
- enum State
- {
- STATE_INACTIVE,
- STATE_ACTIVE,
- };
-
- ~CPjAudioInputEngine();
-
- static CPjAudioInputEngine *NewL(pjmedia_snd_stream *parent_strm,
- pjmedia_snd_rec_cb rec_cb,
- void *user_data);
-
- static CPjAudioInputEngine *NewLC(pjmedia_snd_stream *parent_strm,
- pjmedia_snd_rec_cb rec_cb,
- void *user_data);
-
- pj_status_t StartRecord();
- void Stop();
-
-private:
- State state_;
- pjmedia_snd_stream *parentStrm_;
- pjmedia_snd_rec_cb recCb_;
- void *userData_;
- CMdaAudioInputStream *iInputStream_;
- HBufC8 *iStreamBuffer_;
- TPtr8 iFramePtr_;
- TInt lastError_;
- pj_uint32_t timeStamp_;
-
- // cache variable
- // to avoid calculating frame length repeatedly
- TInt frameLen_;
-
- // in some SymbianOS (e.g: OSv9.1), sometimes recorded size != requested framesize
- // so let's provide a buffer to make sure the rec callback returns framesize as requested.
- TUint8 *frameRecBuf_;
- TInt frameRecBufLen_;
-
- CPjAudioInputEngine(pjmedia_snd_stream *parent_strm,
- pjmedia_snd_rec_cb rec_cb,
- void *user_data);
- void ConstructL();
- TPtr8 & GetFrame();
-
-public:
- virtual void MaiscOpenComplete(TInt aError);
- virtual void MaiscBufferCopied(TInt aError, const TDesC8 &aBuffer);
- virtual void MaiscRecordComplete(TInt aError);
-
-};
-
-
-CPjAudioInputEngine::CPjAudioInputEngine(pjmedia_snd_stream *parent_strm,
- pjmedia_snd_rec_cb rec_cb,
- void *user_data)
- : state_(STATE_INACTIVE), parentStrm_(parent_strm),
- recCb_(rec_cb), userData_(user_data),
- iInputStream_(NULL), iStreamBuffer_(NULL), iFramePtr_(0, 0),
- lastError_(KErrNone), timeStamp_(0),
- frameLen_(parent_strm->samples_per_frame * parent_strm->channel_count * BYTES_PER_SAMPLE),
- frameRecBuf_(NULL), frameRecBufLen_(0)
-{
-}
-
-CPjAudioInputEngine::~CPjAudioInputEngine()
-{
- Stop();
-
- delete iStreamBuffer_;
- iStreamBuffer_ = NULL;
-
- delete [] frameRecBuf_;
- frameRecBuf_ = NULL;
- frameRecBufLen_ = 0;
-}
-
-void CPjAudioInputEngine::ConstructL()
-{
- iStreamBuffer_ = HBufC8::NewL(frameLen_);
- CleanupStack::PushL(iStreamBuffer_);
-
- frameRecBuf_ = new TUint8[frameLen_*2];
- CleanupStack::PushL(frameRecBuf_);
-}
-
-CPjAudioInputEngine *CPjAudioInputEngine::NewLC(pjmedia_snd_stream *parent,
- pjmedia_snd_rec_cb rec_cb,
- void *user_data)
-{
- CPjAudioInputEngine* self = new (ELeave) CPjAudioInputEngine(parent,
- rec_cb,
- user_data);
- CleanupStack::PushL(self);
- self->ConstructL();
- return self;
-}
-
-CPjAudioInputEngine *CPjAudioInputEngine::NewL(pjmedia_snd_stream *parent,
- pjmedia_snd_rec_cb rec_cb,
- void *user_data)
-{
- CPjAudioInputEngine *self = NewLC(parent, rec_cb, user_data);
- CleanupStack::Pop(self->frameRecBuf_);
- CleanupStack::Pop(self->iStreamBuffer_);
- CleanupStack::Pop(self);
- return self;
-}
-
-
-pj_status_t CPjAudioInputEngine::StartRecord()
-{
-
- // Ignore command if recording is in progress.
- if (state_ == STATE_ACTIVE)
- return PJ_SUCCESS;
-
- // According to Nokia's AudioStream example, some 2nd Edition, FP2 devices
- // (such as Nokia 6630) require the stream to be reconstructed each time
- // before calling Open() - otherwise the callback never gets called.
- // For uniform behavior, lets just delete/re-create the stream for all
- // devices.
-
- // Destroy existing stream.
- if (iInputStream_) delete iInputStream_;
- iInputStream_ = NULL;
-
- // Create the stream.
- TRAPD(err, iInputStream_ = CMdaAudioInputStream::NewL(*this));
- if (err != KErrNone)
- return PJ_RETURN_OS_ERROR(err);
-
- // Initialize settings.
- TMdaAudioDataSettings iStreamSettings;
- iStreamSettings.iChannels = get_channel_cap(parentStrm_->channel_count);
- iStreamSettings.iSampleRate = get_clock_rate_cap(parentStrm_->clock_rate);
-
- pj_assert(iStreamSettings.iChannels != 0 &&
- iStreamSettings.iSampleRate != 0);
-
- PJ_LOG(4,(THIS_FILE, "Opening sound device for capture, "
- "clock rate=%d, channel count=%d..",
- parentStrm_->clock_rate,
- parentStrm_->channel_count));
-
- // Open stream.
- lastError_ = KRequestPending;
- iInputStream_->Open(&iStreamSettings);
-
- // Success
- PJ_LOG(4,(THIS_FILE, "Sound capture started."));
- return PJ_SUCCESS;
-}
-
-
-void CPjAudioInputEngine::Stop()
-{
- // If capture is in progress, stop it.
- if (iInputStream_ && state_ == STATE_ACTIVE) {
- lastError_ = KRequestPending;
- iInputStream_->Stop();
-
- // Wait until it's actually stopped
- while (lastError_ == KRequestPending)
- pj_symbianos_poll(-1, 100);
- }
-
- if (iInputStream_) {
- delete iInputStream_;
- iInputStream_ = NULL;
- }
-
- state_ = STATE_INACTIVE;
-}
-
-
-TPtr8 & CPjAudioInputEngine::GetFrame()
-{
- //iStreamBuffer_->Des().FillZ(frameLen_);
- iFramePtr_.Set((TUint8*)(iStreamBuffer_->Ptr()), frameLen_, frameLen_);
- return iFramePtr_;
-}
-
-void CPjAudioInputEngine::MaiscOpenComplete(TInt aError)
-{
- lastError_ = aError;
- if (aError != KErrNone) {
- snd_perror("Error in MaiscOpenComplete()", aError);
- return;
- }
-
- // set stream priority to normal and time sensitive
- iInputStream_->SetPriority(EPriorityNormal,
- EMdaPriorityPreferenceTime);
-
- // Read the first frame.
- TPtr8 & frm = GetFrame();
- TRAPD(err2, iInputStream_->ReadL(frm));
- if (err2) {
- PJ_LOG(4,(THIS_FILE, "Exception in iInputStream_->ReadL()"));
- }
-}
-
-void CPjAudioInputEngine::MaiscBufferCopied(TInt aError,
- const TDesC8 &aBuffer)
-{
- lastError_ = aError;
- if (aError != KErrNone) {
- snd_perror("Error in MaiscBufferCopied()", aError);
- return;
- }
-
- if (frameRecBufLen_ || aBuffer.Size() < frameLen_) {
- pj_memcpy(frameRecBuf_ + frameRecBufLen_, (void*) aBuffer.Ptr(), aBuffer.Size());
- frameRecBufLen_ += aBuffer.Size();
- }
-
- if (frameRecBufLen_) {
- while (frameRecBufLen_ >= frameLen_) {
- // Call the callback.
- recCb_(userData_, timeStamp_, frameRecBuf_, frameLen_);
- // Increment timestamp.
- timeStamp_ += parentStrm_->samples_per_frame;
-
- frameRecBufLen_ -= frameLen_;
- pj_memmove(frameRecBuf_, frameRecBuf_+frameLen_, frameRecBufLen_);
- }
- } else {
- // Call the callback.
- recCb_(userData_, timeStamp_, (void*) aBuffer.Ptr(), aBuffer.Size());
- // Increment timestamp.
- timeStamp_ += parentStrm_->samples_per_frame;
- }
-
- // Record next frame
- TPtr8 & frm = GetFrame();
- TRAPD(err2, iInputStream_->ReadL(frm));
- if (err2) {
- PJ_LOG(4,(THIS_FILE, "Exception in iInputStream_->ReadL()"));
- }
-}
-
-
-void CPjAudioInputEngine::MaiscRecordComplete(TInt aError)
-{
- lastError_ = aError;
- state_ = STATE_INACTIVE;
- if (aError != KErrNone) {
- snd_perror("Error in MaiscRecordComplete()", aError);
- }
-}
-
-
-
-//////////////////////////////////////////////////////////////////////////////
-//
-
-/*
- * Implementation: Symbian Output Stream.
- */
-
-class CPjAudioOutputEngine : public CBase, MMdaAudioOutputStreamCallback
-{
-public:
- enum State
- {
- STATE_INACTIVE,
- STATE_ACTIVE,
- };
-
- ~CPjAudioOutputEngine();
-
- static CPjAudioOutputEngine *NewL(pjmedia_snd_stream *parent_strm,
- pjmedia_snd_play_cb play_cb,
- void *user_data);
-
- static CPjAudioOutputEngine *NewLC(pjmedia_snd_stream *parent_strm,
- pjmedia_snd_play_cb rec_cb,
- void *user_data);
-
- pj_status_t StartPlay();
- void Stop();
-
-private:
- State state_;
- pjmedia_snd_stream *parentStrm_;
- pjmedia_snd_play_cb playCb_;
- void *userData_;
- CMdaAudioOutputStream *iOutputStream_;
- TUint8 *frameBuf_;
- unsigned frameBufSize_;
- TPtrC8 frame_;
- TInt lastError_;
- unsigned timestamp_;
-
- CPjAudioOutputEngine(pjmedia_snd_stream *parent_strm,
- pjmedia_snd_play_cb play_cb,
- void *user_data);
- void ConstructL();
-
- virtual void MaoscOpenComplete(TInt aError);
- virtual void MaoscBufferCopied(TInt aError, const TDesC8& aBuffer);
- virtual void MaoscPlayComplete(TInt aError);
-};
-
-
-CPjAudioOutputEngine::CPjAudioOutputEngine(pjmedia_snd_stream *parent_strm,
- pjmedia_snd_play_cb play_cb,
- void *user_data)
-: state_(STATE_INACTIVE), parentStrm_(parent_strm), playCb_(play_cb),
- userData_(user_data), iOutputStream_(NULL), frameBuf_(NULL),
- lastError_(KErrNone), timestamp_(0)
-{
-}
-
-
-void CPjAudioOutputEngine::ConstructL()
-{
- frameBufSize_ = parentStrm_->samples_per_frame *
- parentStrm_->channel_count *
- BYTES_PER_SAMPLE;
- frameBuf_ = new TUint8[frameBufSize_];
-}
-
-CPjAudioOutputEngine::~CPjAudioOutputEngine()
-{
- Stop();
- delete [] frameBuf_;
-}
-
-CPjAudioOutputEngine *
-CPjAudioOutputEngine::NewLC(pjmedia_snd_stream *parent_strm,
- pjmedia_snd_play_cb rec_cb,
- void *user_data)
-{
- CPjAudioOutputEngine* self = new (ELeave) CPjAudioOutputEngine(parent_strm,
- rec_cb,
- user_data);
- CleanupStack::PushL(self);
- self->ConstructL();
- return self;
-}
-
-CPjAudioOutputEngine *
-CPjAudioOutputEngine::NewL(pjmedia_snd_stream *parent_strm,
- pjmedia_snd_play_cb play_cb,
- void *user_data)
-{
- CPjAudioOutputEngine *self = NewLC(parent_strm, play_cb, user_data);
- CleanupStack::Pop(self);
- return self;
-}
-
-pj_status_t CPjAudioOutputEngine::StartPlay()
-{
- // Ignore command if playing is in progress.
- if (state_ == STATE_ACTIVE)
- return PJ_SUCCESS;
-
- // Destroy existing stream.
- if (iOutputStream_) delete iOutputStream_;
- iOutputStream_ = NULL;
-
- // Create the stream
- TRAPD(err, iOutputStream_ = CMdaAudioOutputStream::NewL(*this));
- if (err != KErrNone)
- return PJ_RETURN_OS_ERROR(err);
-
- // Initialize settings.
- TMdaAudioDataSettings iStreamSettings;
- iStreamSettings.iChannels = get_channel_cap(parentStrm_->channel_count);
- iStreamSettings.iSampleRate = get_clock_rate_cap(parentStrm_->clock_rate);
-
- pj_assert(iStreamSettings.iChannels != 0 &&
- iStreamSettings.iSampleRate != 0);
-
- PJ_LOG(4,(THIS_FILE, "Opening sound device for playback, "
- "clock rate=%d, channel count=%d..",
- parentStrm_->clock_rate,
- parentStrm_->channel_count));
-
- // Open stream.
- lastError_ = KRequestPending;
- iOutputStream_->Open(&iStreamSettings);
-
- // Success
- PJ_LOG(4,(THIS_FILE, "Sound playback started"));
- return PJ_SUCCESS;
-
-}
-
-void CPjAudioOutputEngine::Stop()
-{
- // Stop stream if it's playing
- if (iOutputStream_ && state_ != STATE_INACTIVE) {
- lastError_ = KRequestPending;
- iOutputStream_->Stop();
-
- // Wait until it's actually stopped
- while (lastError_ == KRequestPending)
- pj_symbianos_poll(-1, 100);
- }
-
- if (iOutputStream_) {
- delete iOutputStream_;
- iOutputStream_ = NULL;
- }
-
- state_ = STATE_INACTIVE;
-}
-
-void CPjAudioOutputEngine::MaoscOpenComplete(TInt aError)
-{
- lastError_ = aError;
-
- if (aError==KErrNone) {
- // output stream opened succesfully, set status to Active
- state_ = STATE_ACTIVE;
-
- // set stream properties, 16bit 8KHz mono
- TMdaAudioDataSettings iSettings;
- iSettings.iChannels = get_channel_cap(parentStrm_->channel_count);
- iSettings.iSampleRate = get_clock_rate_cap(parentStrm_->clock_rate);
-
- iOutputStream_->SetAudioPropertiesL(iSettings.iSampleRate,
- iSettings.iChannels);
-
- // set volume to 1/2th of stream max volume
- iOutputStream_->SetVolume(iOutputStream_->MaxVolume()/2);
-
- // set stream priority to normal and time sensitive
- iOutputStream_->SetPriority(EPriorityNormal,
- EMdaPriorityPreferenceTime);
-
- // Call callback to retrieve frame from upstream.
- pj_status_t status;
- status = playCb_(this->userData_, timestamp_, frameBuf_,
- frameBufSize_);
- if (status != PJ_SUCCESS) {
- this->Stop();
- return;
- }
-
- // Increment timestamp.
- timestamp_ += (frameBufSize_ / BYTES_PER_SAMPLE);
-
- // issue WriteL() to write the first audio data block,
- // subsequent calls to WriteL() will be issued in
- // MMdaAudioOutputStreamCallback::MaoscBufferCopied()
- // until whole data buffer is written.
- frame_.Set(frameBuf_, frameBufSize_);
- iOutputStream_->WriteL(frame_);
- } else {
- snd_perror("Error in MaoscOpenComplete()", aError);
- }
-}
-
-void CPjAudioOutputEngine::MaoscBufferCopied(TInt aError,
- const TDesC8& aBuffer)
-{
- PJ_UNUSED_ARG(aBuffer);
-
- if (aError==KErrNone) {
- // Buffer successfully written, feed another one.
-
- // Call callback to retrieve frame from upstream.
- pj_status_t status;
- status = playCb_(this->userData_, timestamp_, frameBuf_,
- frameBufSize_);
- if (status != PJ_SUCCESS) {
- this->Stop();
- return;
- }
-
- // Increment timestamp.
- timestamp_ += (frameBufSize_ / BYTES_PER_SAMPLE);
-
- // Write to playback stream.
- frame_.Set(frameBuf_, frameBufSize_);
- iOutputStream_->WriteL(frame_);
-
- } else if (aError==KErrAbort) {
- // playing was aborted, due to call to CMdaAudioOutputStream::Stop()
- state_ = STATE_INACTIVE;
- } else {
- // error writing data to output
- lastError_ = aError;
- state_ = STATE_INACTIVE;
- snd_perror("Error in MaoscBufferCopied()", aError);
- }
-}
-
-void CPjAudioOutputEngine::MaoscPlayComplete(TInt aError)
-{
- lastError_ = aError;
- state_ = STATE_INACTIVE;
- if (aError != KErrNone) {
- snd_perror("Error in MaoscPlayComplete()", aError);
- }
-}
-
-
-//////////////////////////////////////////////////////////////////////////////
-//
-
-
-/*
- * Initialize sound subsystem.
- */
-PJ_DEF(pj_status_t) pjmedia_snd_init(pj_pool_factory *factory)
-{
- snd_pool_factory = factory;
- return PJ_SUCCESS;
-}
-
-/*
- * Get device count.
- */
-PJ_DEF(int) pjmedia_snd_get_dev_count(void)
-{
- /* Always return 1 */
- return 1;
-}
-
-/*
- * Get device info.
- */
-PJ_DEF(const pjmedia_snd_dev_info*) pjmedia_snd_get_dev_info(unsigned index)
-{
- /* Always return the default sound device */
- if (index == (unsigned)-1)
- index = 0;
-
- PJ_ASSERT_RETURN(index==0, NULL);
- return &symbian_snd_dev_info;
-}
-
-
-
-/*
- * Open sound recorder stream.
- */
-PJ_DEF(pj_status_t) pjmedia_snd_open_rec( int index,
- unsigned clock_rate,
- unsigned channel_count,
- unsigned samples_per_frame,
- unsigned bits_per_sample,
- pjmedia_snd_rec_cb rec_cb,
- void *user_data,
- pjmedia_snd_stream **p_snd_strm)
-{
- pj_pool_t *pool;
- pjmedia_snd_stream *strm;
-
- if (index==-1) index = 0;
-
- PJ_ASSERT_RETURN(index == 0, PJ_EINVAL);
- PJ_ASSERT_RETURN(clock_rate && channel_count && samples_per_frame &&
- bits_per_sample && rec_cb && p_snd_strm, PJ_EINVAL);
-
- pool = pj_pool_create(snd_pool_factory, POOL_NAME, POOL_SIZE, POOL_INC,
- NULL);
- if (!pool)
- return PJ_ENOMEM;
-
- strm = (pjmedia_snd_stream*) pj_pool_zalloc(pool,
- sizeof(pjmedia_snd_stream));
- strm->dir = PJMEDIA_DIR_CAPTURE;
- strm->pool = pool;
- strm->clock_rate = clock_rate;
- strm->channel_count = channel_count;
- strm->samples_per_frame = samples_per_frame;
-
- PJ_ASSERT_RETURN(bits_per_sample == 16, PJ_EINVAL);
- PJ_ASSERT_RETURN(get_clock_rate_cap(clock_rate) != 0, PJ_EINVAL);
- PJ_ASSERT_RETURN(get_channel_cap(channel_count) != 0, PJ_EINVAL);
-
- // Create the input stream.
- TRAPD(err, strm->inEngine = CPjAudioInputEngine::NewL(strm, rec_cb,
- user_data));
- if (err != KErrNone) {
- pj_pool_release(pool);
- return PJ_RETURN_OS_ERROR(err);
- }
-
- // Done.
- *p_snd_strm = strm;
- return PJ_SUCCESS;
-}
-
-PJ_DEF(pj_status_t) pjmedia_snd_open_player( int index,
- unsigned clock_rate,
- unsigned channel_count,
- unsigned samples_per_frame,
- unsigned bits_per_sample,
- pjmedia_snd_play_cb play_cb,
- void *user_data,
- pjmedia_snd_stream **p_snd_strm )
-{
- pj_pool_t *pool;
- pjmedia_snd_stream *strm;
-
- if (index == -1) index = 0;
-
- PJ_ASSERT_RETURN(index == 0, PJ_EINVAL);
- PJ_ASSERT_RETURN(clock_rate && channel_count && samples_per_frame &&
- bits_per_sample && play_cb && p_snd_strm, PJ_EINVAL);
-
- pool = pj_pool_create(snd_pool_factory, POOL_NAME, POOL_SIZE, POOL_INC,
- NULL);
- if (!pool)
- return PJ_ENOMEM;
-
- strm = (pjmedia_snd_stream*) pj_pool_zalloc(pool,
- sizeof(pjmedia_snd_stream));
- strm->dir = PJMEDIA_DIR_PLAYBACK;
- strm->pool = pool;
- strm->clock_rate = clock_rate;
- strm->channel_count = channel_count;
- strm->samples_per_frame = samples_per_frame;
-
- PJ_ASSERT_RETURN(bits_per_sample == 16, PJ_EINVAL);
- PJ_ASSERT_RETURN(get_clock_rate_cap(clock_rate) != 0, PJ_EINVAL);
- PJ_ASSERT_RETURN(get_channel_cap(channel_count) != 0, PJ_EINVAL);
-
- // Create the output stream.
- TRAPD(err, strm->outEngine = CPjAudioOutputEngine::NewL(strm, play_cb,
- user_data));
- if (err != KErrNone) {
- pj_pool_release(pool);
- return PJ_RETURN_OS_ERROR(err);
- }
-
- // Done.
- *p_snd_strm = strm;
- return PJ_SUCCESS;
-}
-
-PJ_DEF(pj_status_t) pjmedia_snd_open( int rec_id,
- int play_id,
- unsigned clock_rate,
- unsigned channel_count,
- unsigned samples_per_frame,
- unsigned bits_per_sample,
- pjmedia_snd_rec_cb rec_cb,
- pjmedia_snd_play_cb play_cb,
- void *user_data,
- pjmedia_snd_stream **p_snd_strm)
-{
- pj_pool_t *pool;
- pjmedia_snd_stream *strm;
-
- if (rec_id == -1) rec_id = 0;
- if (play_id == -1) play_id = 0;
-
- PJ_ASSERT_RETURN(rec_id == 0 && play_id == 0, PJ_EINVAL);
- PJ_ASSERT_RETURN(clock_rate && channel_count && samples_per_frame &&
- bits_per_sample && rec_cb && play_cb && p_snd_strm,
- PJ_EINVAL);
-
- pool = pj_pool_create(snd_pool_factory, POOL_NAME, POOL_SIZE, POOL_INC,
- NULL);
- if (!pool)
- return PJ_ENOMEM;
-
- strm = (pjmedia_snd_stream*) pj_pool_zalloc(pool,
- sizeof(pjmedia_snd_stream));
- strm->dir = PJMEDIA_DIR_CAPTURE_PLAYBACK;
- strm->pool = pool;
- strm->clock_rate = clock_rate;
- strm->channel_count = channel_count;
- strm->samples_per_frame = samples_per_frame;
-
- PJ_ASSERT_RETURN(bits_per_sample == 16, PJ_EINVAL);
- PJ_ASSERT_RETURN(get_clock_rate_cap(clock_rate) != 0, PJ_EINVAL);
- PJ_ASSERT_RETURN(get_channel_cap(channel_count) != 0, PJ_EINVAL);
-
- // Create the output stream.
- TRAPD(err, strm->outEngine = CPjAudioOutputEngine::NewL(strm, play_cb,
- user_data));
- if (err != KErrNone) {
- pj_pool_release(pool);
- return PJ_RETURN_OS_ERROR(err);
- }
-
- // Create the input stream.
- TRAPD(err1, strm->inEngine = CPjAudioInputEngine::NewL(strm, rec_cb,
- user_data));
- if (err1 != KErrNone) {
- strm->inEngine = NULL;
- delete strm->outEngine;
- strm->outEngine = NULL;
- pj_pool_release(pool);
- return PJ_RETURN_OS_ERROR(err1);
- }
-
- // Done.
- *p_snd_strm = strm;
- return PJ_SUCCESS;
-}
-
-/*
- * Get stream info.
- */
-PJ_DEF(pj_status_t) pjmedia_snd_stream_get_info(pjmedia_snd_stream *strm,
- pjmedia_snd_stream_info *pi)
-{
- PJ_ASSERT_RETURN(strm && pi, PJ_EINVAL);
-
- pj_bzero(pi, sizeof(*pi));
- pi->dir = strm->dir;
- pi->play_id = 0;
- pi->rec_id = 0;
- pi->clock_rate = strm->clock_rate;
- pi->channel_count = strm->channel_count;
- pi->samples_per_frame = strm->samples_per_frame;
- pi->bits_per_sample = BYTES_PER_SAMPLE * 8;
- // Symbian uses 4096 bytes buffer (~2048 samples/256 ms) for PCM rec & play.
- // The latencies below are rounded up to be a multiplication of 80.
- pi->rec_latency = 2080;
- pi->play_latency = 2080;
-
- return PJ_SUCCESS;
-}
-
-
-PJ_DEF(pj_status_t) pjmedia_snd_stream_start(pjmedia_snd_stream *stream)
-{
- pj_status_t status;
-
- PJ_ASSERT_RETURN(stream != NULL, PJ_EINVAL);
-
- if (stream->outEngine) {
- status = stream->outEngine->StartPlay();
- if (status != PJ_SUCCESS)
- return status;
- }
-
- if (stream->inEngine) {
- status = stream->inEngine->StartRecord();
- if (status != PJ_SUCCESS)
- return status;
- }
-
- return PJ_SUCCESS;
-}
-
-
-PJ_DEF(pj_status_t) pjmedia_snd_stream_stop(pjmedia_snd_stream *stream)
-{
- PJ_ASSERT_RETURN(stream != NULL, PJ_EINVAL);
-
- if (stream->inEngine) {
- stream->inEngine->Stop();
- }
-
- if (stream->outEngine) {
- stream->outEngine->Stop();
- }
-
- return PJ_SUCCESS;
-}
-
-
-PJ_DEF(pj_status_t) pjmedia_snd_stream_close(pjmedia_snd_stream *stream)
-{
- pj_pool_t *pool;
-
- PJ_ASSERT_RETURN(stream != NULL, PJ_EINVAL);
-
- if (stream->inEngine) {
- delete stream->inEngine;
- stream->inEngine = NULL;
- }
-
- if (stream->outEngine) {
- delete stream->outEngine;
- stream->outEngine = NULL;
- }
-
- pool = stream->pool;
- if (pool) {
- stream->pool = NULL;
- pj_pool_release(pool);
- }
-
- return PJ_SUCCESS;
-}
-
-
-PJ_DEF(pj_status_t) pjmedia_snd_deinit(void)
-{
- /* Nothing to do */
- return PJ_SUCCESS;
-}
-
-
-/*
- * Set sound latency.
- */
-PJ_DEF(pj_status_t) pjmedia_snd_set_latency(unsigned input_latency,
- unsigned output_latency)
-{
- /* Nothing to do */
- PJ_UNUSED_ARG(input_latency);
- PJ_UNUSED_ARG(output_latency);
- return PJ_SUCCESS;
-}
diff --git a/pjmedia/src/pjmedia/symbian_sound_aps.cpp b/pjmedia/src/pjmedia/symbian_sound_aps.cpp
deleted file mode 100644
index ea419c51..00000000
--- a/pjmedia/src/pjmedia/symbian_sound_aps.cpp
+++ /dev/null
@@ -1,920 +0,0 @@
-/* $Id$ */
-/*
- * Copyright (C) 2008-2009 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/sound.h>
-#include <pjmedia/alaw_ulaw.h>
-#include <pjmedia/errno.h>
-#include <pjmedia/symbian_sound_aps.h>
-#include <pj/assert.h>
-#include <pj/log.h>
-#include <pj/math.h>
-#include <pj/os.h>
-
-#include <e32msgqueue.h>
-#include <sounddevice.h>
-#include <APSClientSession.h>
-
-//////////////////////////////////////////////////////////////////////////////
-//
-
-#define THIS_FILE "symbian_sound_aps.cpp"
-
-#define BYTES_PER_SAMPLE 2
-#define POOL_NAME "SymbianSoundAps"
-#define POOL_SIZE 512
-#define POOL_INC 512
-
-#if 1
-# define TRACE_(st) PJ_LOG(3, st)
-#else
-# define TRACE_(st)
-#endif
-
-static pjmedia_snd_dev_info symbian_snd_dev_info =
-{
- "Symbian Sound Device (APS)",
- 1,
- 1,
- 8000
-};
-
-/* App UID to open global APS queues to communicate with the APS server. */
-extern TPtrC APP_UID;
-
-/* Default setting for loudspeaker */
-static pj_bool_t act_loudspeaker = PJ_FALSE;
-
-/* Forward declaration of CPjAudioEngine */
-class CPjAudioEngine;
-
-/*
- * PJMEDIA Sound Stream instance
- */
-struct pjmedia_snd_stream
-{
- // Pool
- pj_pool_t *pool;
-
- // Common settings.
- pjmedia_dir dir;
- unsigned clock_rate;
- unsigned channel_count;
- unsigned samples_per_frame;
-
- // Audio engine
- CPjAudioEngine *engine;
-};
-
-static pj_pool_factory *snd_pool_factory;
-
-
-/*
- * Utility: print sound device error
- */
-static void snd_perror(const char *title, TInt rc)
-{
- PJ_LOG(1,(THIS_FILE, "%s (error code=%d)", title, rc));
-}
-
-//////////////////////////////////////////////////////////////////////////////
-//
-
-/**
- * Abstract class for handler of callbacks from APS client.
- */
-class MQueueHandlerObserver
-{
-public:
- virtual void InputStreamInitialized(const TInt aStatus) = 0;
- virtual void OutputStreamInitialized(const TInt aStatus) = 0;
- virtual void NotifyError(const TInt aError) = 0;
-
- virtual void RecCb(TAPSCommBuffer &buffer) = 0;
- virtual void PlayCb(TAPSCommBuffer &buffer) = 0;
-};
-
-/**
- * Handler for communication and data queue.
- */
-class CQueueHandler : public CActive
-{
-public:
- // Types of queue handler
- enum TQueueHandlerType {
- ERecordCommQueue,
- EPlayCommQueue,
- ERecordQueue,
- EPlayQueue
- };
-
- // The order corresponds to the APS Server state, do not change!
- enum TState {
- EAPSPlayerInitialize = 1,
- EAPSRecorderInitialize = 2,
- EAPSPlayData = 3,
- EAPSRecordData = 4,
- EAPSPlayerInitComplete = 5,
- EAPSRecorderInitComplete = 6
- };
-
- static CQueueHandler* NewL(MQueueHandlerObserver* aObserver,
- RMsgQueue<TAPSCommBuffer>* aQ,
- TQueueHandlerType aType)
- {
- CQueueHandler* self = new (ELeave) CQueueHandler(aObserver, aQ, aType);
- CleanupStack::PushL(self);
- self->ConstructL();
- CleanupStack::Pop(self);
- return self;
- }
-
- // Destructor
- ~CQueueHandler() { Cancel(); }
-
- // Start listening queue event
- void Start() {
- iQ->NotifyDataAvailable(iStatus);
- SetActive();
- }
-
-private:
- // Constructor
- CQueueHandler(MQueueHandlerObserver* aObserver,
- RMsgQueue<TAPSCommBuffer>* aQ,
- TQueueHandlerType aType)
- : CActive(CActive::EPriorityHigh),
- iQ(aQ), iObserver(aObserver), iType(aType)
- {
- CActiveScheduler::Add(this);
-
- // use lower priority for comm queues
- if ((iType == ERecordCommQueue) || (iType == EPlayCommQueue))
- SetPriority(CActive::EPriorityStandard);
- }
-
- // Second phase constructor
- void ConstructL() {}
-
- // Inherited from CActive
- void DoCancel() { iQ->CancelDataAvailable(); }
-
- void RunL() {
- if (iStatus != KErrNone) {
- iObserver->NotifyError(iStatus.Int());
- return;
- }
-
- TAPSCommBuffer buffer;
- TInt ret = iQ->Receive(buffer);
-
- if (ret != KErrNone) {
- iObserver->NotifyError(ret);
- return;
- }
-
- switch (iType) {
- case ERecordQueue:
- if (buffer.iCommand == EAPSRecordData) {
- iObserver->RecCb(buffer);
- }
- break;
-
- // Callbacks from the APS main thread
- case EPlayCommQueue:
- switch (buffer.iCommand) {
- case EAPSPlayData:
- if (buffer.iStatus == KErrUnderflow) {
- iObserver->PlayCb(buffer);
- }
- break;
- case EAPSPlayerInitialize:
- iObserver->NotifyError(buffer.iStatus);
- break;
- case EAPSPlayerInitComplete:
- iObserver->OutputStreamInitialized(buffer.iStatus);
- break;
- case EAPSRecorderInitComplete:
- iObserver->InputStreamInitialized(buffer.iStatus);
- break;
- default:
- iObserver->NotifyError(buffer.iStatus);
- break;
- }
- break;
-
- // Callbacks from the APS recorder thread
- case ERecordCommQueue:
- switch (buffer.iCommand) {
- // The APS recorder thread will only report errors
- // through this handler. All other callbacks will be
- // sent from the APS main thread through EPlayCommQueue
- case EAPSRecorderInitialize:
- if (buffer.iStatus == KErrNone) {
- iObserver->InputStreamInitialized(buffer.iStatus);
- break;
- }
- case EAPSRecordData:
- iObserver->NotifyError(buffer.iStatus);
- break;
- default:
- break;
- }
- break;
-
- default:
- break;
- }
-
- // issue next request
- iQ->NotifyDataAvailable(iStatus);
- SetActive();
- }
-
- // Data
- RMsgQueue<TAPSCommBuffer> *iQ; // (not owned)
- MQueueHandlerObserver *iObserver; // (not owned)
- TQueueHandlerType iType;
-};
-
-
-/*
- * Implementation: Symbian Input & Output Stream.
- */
-class CPjAudioEngine : public CBase, MQueueHandlerObserver
-{
-public:
- enum State
- {
- STATE_NULL,
- STATE_READY,
- STATE_STREAMING
- };
-
- ~CPjAudioEngine();
-
- static CPjAudioEngine *NewL(pjmedia_snd_stream *parent_strm,
- pjmedia_snd_rec_cb rec_cb,
- pjmedia_snd_play_cb play_cb,
- void *user_data);
-
- TInt StartL();
- void Stop();
-
- TInt ActivateSpeaker(TBool active);
-
-private:
- CPjAudioEngine(pjmedia_snd_stream *parent_strm,
- pjmedia_snd_rec_cb rec_cb,
- pjmedia_snd_play_cb play_cb,
- void *user_data);
- void ConstructL();
-
- TInt InitPlayL();
- TInt InitRecL();
- TInt StartStreamL();
-
- // Inherited from MQueueHandlerObserver
- virtual void InputStreamInitialized(const TInt aStatus);
- virtual void OutputStreamInitialized(const TInt aStatus);
- virtual void NotifyError(const TInt aError);
-
- virtual void RecCb(TAPSCommBuffer &buffer);
- virtual void PlayCb(TAPSCommBuffer &buffer);
-
- State state_;
- pjmedia_snd_stream *parentStrm_;
- pjmedia_snd_rec_cb recCb_;
- pjmedia_snd_play_cb playCb_;
- void *userData_;
- pj_uint32_t TsPlay_;
- pj_uint32_t TsRec_;
-
- RAPSSession iSession;
- TAPSInitSettings iSettings;
- RMsgQueue<TAPSCommBuffer> iReadQ;
- RMsgQueue<TAPSCommBuffer> iReadCommQ;
- RMsgQueue<TAPSCommBuffer> iWriteQ;
- RMsgQueue<TAPSCommBuffer> iWriteCommQ;
-
- CQueueHandler *iPlayCommHandler;
- CQueueHandler *iRecCommHandler;
- CQueueHandler *iRecHandler;
-
- static pj_uint8_t aps_samples_per_frame;
-
- pj_int16_t *play_buf;
- pj_uint16_t play_buf_len;
- pj_uint16_t play_buf_start;
- pj_int16_t *rec_buf;
- pj_uint16_t rec_buf_len;
-};
-
-
-pj_uint8_t CPjAudioEngine::aps_samples_per_frame = 0;
-
-
-CPjAudioEngine* CPjAudioEngine::NewL(pjmedia_snd_stream *parent_strm,
- pjmedia_snd_rec_cb rec_cb,
- pjmedia_snd_play_cb play_cb,
- void *user_data)
-{
- CPjAudioEngine* self = new (ELeave) CPjAudioEngine(parent_strm,
- rec_cb, play_cb,
- user_data);
- CleanupStack::PushL(self);
- self->ConstructL();
- CleanupStack::Pop(self);
- return self;
-}
-
-CPjAudioEngine::CPjAudioEngine(pjmedia_snd_stream *parent_strm,
- pjmedia_snd_rec_cb rec_cb,
- pjmedia_snd_play_cb play_cb,
- void *user_data)
- : state_(STATE_NULL),
- parentStrm_(parent_strm),
- recCb_(rec_cb),
- playCb_(play_cb),
- userData_(user_data),
- iPlayCommHandler(0),
- iRecCommHandler(0),
- iRecHandler(0)
-{
-}
-
-CPjAudioEngine::~CPjAudioEngine()
-{
- Stop();
-
- delete iPlayCommHandler;
- iPlayCommHandler = NULL;
- delete iRecCommHandler;
- iRecCommHandler = NULL;
-
- // On some devices, immediate closing after stopping may cause APS server
- // panic KERN-EXEC 0, so let's wait for sometime before really closing
- // the client session.
- TTime start, now;
- enum { APS_CLOSE_WAIT_TIME = 200 }; /* in msecs */
-
- start.UniversalTime();
- do {
- pj_symbianos_poll(-1, APS_CLOSE_WAIT_TIME);
- now.UniversalTime();
- } while (now.MicroSecondsFrom(start) < APS_CLOSE_WAIT_TIME * 1000);
-
- iSession.Close();
-
- if (state_ == STATE_READY) {
- if (parentStrm_->dir != PJMEDIA_DIR_PLAYBACK) {
- iReadQ.Close();
- iReadCommQ.Close();
- }
- iWriteQ.Close();
- iWriteCommQ.Close();
- }
-}
-
-TInt CPjAudioEngine::InitPlayL()
-{
- if (state_ == STATE_STREAMING || state_ == STATE_READY)
- return 0;
-
- TInt err = iSession.InitializePlayer(iSettings);
- if (err != KErrNone) {
- snd_perror("Failed to initialize player", err);
- return err;
- }
-
- // Open message queues for the output stream
- TBuf<128> buf2 = iSettings.iGlobal;
- buf2.Append(_L("PlayQueue"));
- TBuf<128> buf3 = iSettings.iGlobal;
- buf3.Append(_L("PlayCommQueue"));
-
- while (iWriteQ.OpenGlobal(buf2))
- User::After(10);
- while (iWriteCommQ.OpenGlobal(buf3))
- User::After(10);
-
- // Construct message queue handler
- iPlayCommHandler = CQueueHandler::NewL(this,
- &iWriteCommQ,
- CQueueHandler::EPlayCommQueue);
-
- // Start observing APS callbacks on output stream message queue
- iPlayCommHandler->Start();
-
- return 0;
-}
-
-TInt CPjAudioEngine::InitRecL()
-{
- if (state_ == STATE_STREAMING || state_ == STATE_READY)
- return 0;
-
- // Initialize input stream device
- TInt err = iSession.InitializeRecorder(iSettings);
- if (err != KErrNone) {
- snd_perror("Failed to initialize recorder", err);
- return err;
- }
-
- TBuf<128> buf1 = iSettings.iGlobal;
- buf1.Append(_L("RecordQueue"));
- TBuf<128> buf4 = iSettings.iGlobal;
- buf4.Append(_L("RecordCommQueue"));
-
- // Must wait for APS thread to finish creating message queues
- // before we can open and use them.
- while (iReadQ.OpenGlobal(buf1))
- User::After(10);
- while (iReadCommQ.OpenGlobal(buf4))
- User::After(10);
-
- // Construct message queue handlers
- iRecCommHandler = CQueueHandler::NewL(this,
- &iReadCommQ,
- CQueueHandler::ERecordCommQueue);
-
- // Start observing APS callbacks from on input stream message queue
- iRecCommHandler->Start();
-
- return 0;
-}
-
-TInt CPjAudioEngine::StartL()
-{
- TInt err = iSession.Connect();
- if (err != KErrNone && err != KErrAlreadyExists)
- return err;
-
- if (state_ == STATE_READY)
- return StartStreamL();
-
- // Even if only capturer are opened, playback thread of APS Server need
- // to be run(?). Since some messages will be delivered via play comm queue.
- return InitPlayL();
-}
-
-void CPjAudioEngine::Stop()
-{
- iSession.Stop();
-
- delete iRecHandler;
- iRecHandler = NULL;
-
- state_ = STATE_READY;
-}
-
-void CPjAudioEngine::ConstructL()
-{
- iSettings.iFourCC = TFourCC(KMCPFourCCIdG711);
- iSettings.iGlobal = APP_UID;
- iSettings.iPriority = TMdaPriority(100);
- iSettings.iPreference = TMdaPriorityPreference(0x05210001);
- iSettings.iSettings.iChannels = EMMFMono;
- iSettings.iSettings.iSampleRate = EMMFSampleRate8000Hz;
- iSettings.iSettings.iVolume = 0;
-
- /* play_buf size is samples per frame of parent stream. */
- play_buf = (pj_int16_t*)pj_pool_alloc(parentStrm_->pool,
- parentStrm_->samples_per_frame << 1);
- play_buf_len = 0;
- play_buf_start = 0;
-
- /* rec_buf size is samples per frame of parent stream. */
- rec_buf = (pj_int16_t*)pj_pool_alloc(parentStrm_->pool,
- parentStrm_->samples_per_frame << 1);
- rec_buf_len = 0;
-}
-
-TInt CPjAudioEngine::StartStreamL()
-{
- if (state_ == STATE_STREAMING)
- return 0;
-
- iSession.SetCng(EFalse);
- iSession.SetVadMode(EFalse);
- iSession.SetPlc(EFalse);
- iSession.SetEncoderMode(EULawOr30ms);
- iSession.SetDecoderMode(EULawOr30ms);
- iSession.ActivateLoudspeaker(act_loudspeaker);
-
- // Not only playback
- if (parentStrm_->dir != PJMEDIA_DIR_PLAYBACK) {
- iRecHandler = CQueueHandler::NewL(this, &iReadQ,
- CQueueHandler::ERecordQueue);
- iRecHandler->Start();
- iSession.Read();
- TRACE_((THIS_FILE, "APS recorder started"));
- }
-
- // Not only capture
- if (parentStrm_->dir != PJMEDIA_DIR_CAPTURE) {
- iSession.Write();
- TRACE_((THIS_FILE, "APS player started"));
- }
-
- state_ = STATE_STREAMING;
- return 0;
-}
-
-///////////////////////////////////////////////////////////
-// Inherited from MQueueHandlerObserver
-//
-
-void CPjAudioEngine::InputStreamInitialized(const TInt aStatus)
-{
- TRACE_((THIS_FILE, "InputStreamInitialized %d", aStatus));
-
- state_ = STATE_READY;
- if (aStatus == KErrNone) {
- StartStreamL();
- }
-}
-
-void CPjAudioEngine::OutputStreamInitialized(const TInt aStatus)
-{
- TRACE_((THIS_FILE, "OutputStreamInitialized %d", aStatus));
-
- if (aStatus == KErrNone) {
- if (parentStrm_->dir == PJMEDIA_DIR_PLAYBACK) {
- state_ = STATE_READY;
- // Only playback, start directly
- StartStreamL();
- } else
- InitRecL();
- }
-}
-
-void CPjAudioEngine::NotifyError(const TInt aError)
-{
- snd_perror("Error from CQueueHandler", aError);
-}
-
-void CPjAudioEngine::RecCb(TAPSCommBuffer &buffer)
-{
- pj_assert(buffer.iBuffer[0] == 1 && buffer.iBuffer[1] == 0);
-
- /* Detect the recorder G.711 frame size, player frame size will follow
- * this recorder frame size.
- */
- if (CPjAudioEngine::aps_samples_per_frame == 0) {
- CPjAudioEngine::aps_samples_per_frame = buffer.iBuffer.Length() < 160?
- 80 : 160;
- TRACE_((THIS_FILE, "Detected APS G.711 frame size = %u samples",
- CPjAudioEngine::aps_samples_per_frame));
- }
-
- /* Decode APS buffer (coded in G.711) and put the PCM result into rec_buf.
- * Whenever rec_buf is full, call parent stream callback.
- */
- unsigned dec_len = 0;
-
- while (dec_len < CPjAudioEngine::aps_samples_per_frame) {
- unsigned tmp;
-
- tmp = PJ_MIN(parentStrm_->samples_per_frame - rec_buf_len,
- CPjAudioEngine::aps_samples_per_frame - dec_len);
- pjmedia_ulaw_decode(&rec_buf[rec_buf_len],
- buffer.iBuffer.Ptr() + 2 + dec_len,
- tmp);
- rec_buf_len += tmp;
- dec_len += tmp;
-
- pj_assert(rec_buf_len <= parentStrm_->samples_per_frame);
-
- if (rec_buf_len == parentStrm_->samples_per_frame) {
- recCb_(userData_, 0, rec_buf, rec_buf_len << 1);
- rec_buf_len = 0;
- }
- }
-}
-
-void CPjAudioEngine::PlayCb(TAPSCommBuffer &buffer)
-{
- buffer.iCommand = CQueueHandler::EAPSPlayData;
- buffer.iStatus = 0;
- buffer.iBuffer.Zero();
- buffer.iBuffer.Append(1);
- buffer.iBuffer.Append(0);
-
- /* Send 10ms silence frame if frame size hasn't been known. */
- if (CPjAudioEngine::aps_samples_per_frame == 0) {
- pjmedia_zero_samples(play_buf, 80);
- pjmedia_ulaw_encode((pj_uint8_t*)play_buf, play_buf, 80);
- buffer.iBuffer.Append((TUint8*)play_buf, 80);
- iWriteQ.Send(buffer);
- return;
- }
-
- unsigned enc_len = 0;
-
- /* Call parent stream callback to get PCM samples to play,
- * encode the PCM samples into G.711 and put it into APS buffer.
- */
- while (enc_len < CPjAudioEngine::aps_samples_per_frame) {
- if (play_buf_len == 0) {
- playCb_(userData_, 0, play_buf, parentStrm_->samples_per_frame<<1);
- play_buf_len = parentStrm_->samples_per_frame;
- play_buf_start = 0;
- }
-
- unsigned tmp;
-
- tmp = PJ_MIN(play_buf_len,
- CPjAudioEngine::aps_samples_per_frame - enc_len);
- pjmedia_ulaw_encode((pj_uint8_t*)&play_buf[play_buf_start],
- &play_buf[play_buf_start],
- tmp);
- buffer.iBuffer.Append((TUint8*)&play_buf[play_buf_start], tmp);
- enc_len += tmp;
- play_buf_len -= tmp;
- play_buf_start += tmp;
- }
-
- iWriteQ.Send(buffer);
-}
-
-//
-// End of inherited from MQueueHandlerObserver
-/////////////////////////////////////////////////////////////
-
-
-TInt CPjAudioEngine::ActivateSpeaker(TBool active)
-{
- if (state_ == STATE_READY || state_ == STATE_STREAMING) {
- iSession.ActivateLoudspeaker(active);
- return KErrNone;
- }
- return KErrNotReady;
-}
-//////////////////////////////////////////////////////////////////////////////
-//
-
-
-/*
- * Initialize sound subsystem.
- */
-PJ_DEF(pj_status_t) pjmedia_snd_init(pj_pool_factory *factory)
-{
- snd_pool_factory = factory;
- return PJ_SUCCESS;
-}
-
-/*
- * Get device count.
- */
-PJ_DEF(int) pjmedia_snd_get_dev_count(void)
-{
- /* Always return 1 */
- return 1;
-}
-
-/*
- * Get device info.
- */
-PJ_DEF(const pjmedia_snd_dev_info*) pjmedia_snd_get_dev_info(unsigned index)
-{
- /* Always return the default sound device */
- if (index == (unsigned)-1)
- index = 0;
-
- PJ_ASSERT_RETURN(index==0, NULL);
- return &symbian_snd_dev_info;
-}
-
-static pj_status_t sound_open(pjmedia_dir dir,
- unsigned clock_rate,
- unsigned channel_count,
- unsigned samples_per_frame,
- unsigned bits_per_sample,
- pjmedia_snd_rec_cb rec_cb,
- pjmedia_snd_play_cb play_cb,
- void *user_data,
- pjmedia_snd_stream **p_snd_strm)
-{
- pj_pool_t *pool;
- pjmedia_snd_stream *strm;
-
- PJ_ASSERT_RETURN(p_snd_strm, PJ_EINVAL);
- PJ_ASSERT_RETURN(clock_rate == 8000 && channel_count == 1 &&
- bits_per_sample == 16, PJ_ENOTSUP);
- PJ_ASSERT_RETURN((dir == PJMEDIA_DIR_CAPTURE_PLAYBACK && rec_cb && play_cb)
- || (dir == PJMEDIA_DIR_CAPTURE && rec_cb && !play_cb)
- || (dir == PJMEDIA_DIR_PLAYBACK && !rec_cb && play_cb),
- PJ_EINVAL);
-
- pool = pj_pool_create(snd_pool_factory, POOL_NAME, POOL_SIZE, POOL_INC,
- NULL);
- if (!pool)
- return PJ_ENOMEM;
-
- strm = (pjmedia_snd_stream*) pj_pool_zalloc(pool,
- sizeof(pjmedia_snd_stream));
- strm->dir = dir;
- strm->pool = pool;
- strm->clock_rate = clock_rate;
- strm->channel_count = channel_count;
- strm->samples_per_frame = samples_per_frame;
-
- // Create the audio engine.
- TRAPD(err, strm->engine = CPjAudioEngine::NewL(strm, rec_cb, play_cb,
- user_data));
- if (err != KErrNone) {
- pj_pool_release(pool);
- return PJ_RETURN_OS_ERROR(err);
- }
-
- // Done.
- *p_snd_strm = strm;
- return PJ_SUCCESS;
-}
-
-
-
-/*
- * Open sound recorder stream.
- */
-PJ_DEF(pj_status_t) pjmedia_snd_open_rec( int index,
- unsigned clock_rate,
- unsigned channel_count,
- unsigned samples_per_frame,
- unsigned bits_per_sample,
- pjmedia_snd_rec_cb rec_cb,
- void *user_data,
- pjmedia_snd_stream **p_snd_strm)
-{
- if (index < 0) index = 0;
- PJ_ASSERT_RETURN(index == 0, PJ_EINVAL);
-
- return sound_open(PJMEDIA_DIR_CAPTURE, clock_rate, channel_count,
- samples_per_frame, bits_per_sample, rec_cb, NULL,
- user_data, p_snd_strm);
-}
-
-PJ_DEF(pj_status_t) pjmedia_snd_open_player( int index,
- unsigned clock_rate,
- unsigned channel_count,
- unsigned samples_per_frame,
- unsigned bits_per_sample,
- pjmedia_snd_play_cb play_cb,
- void *user_data,
- pjmedia_snd_stream **p_snd_strm )
-{
- if (index < 0) index = 0;
- PJ_ASSERT_RETURN(index == 0, PJ_EINVAL);
-
- return sound_open(PJMEDIA_DIR_PLAYBACK, clock_rate, channel_count,
- samples_per_frame, bits_per_sample, NULL, play_cb,
- user_data, p_snd_strm);
-}
-
-PJ_DEF(pj_status_t) pjmedia_snd_open( int rec_id,
- int play_id,
- unsigned clock_rate,
- unsigned channel_count,
- unsigned samples_per_frame,
- unsigned bits_per_sample,
- pjmedia_snd_rec_cb rec_cb,
- pjmedia_snd_play_cb play_cb,
- void *user_data,
- pjmedia_snd_stream **p_snd_strm)
-{
- if (rec_id < 0) rec_id = 0;
- if (play_id < 0) play_id = 0;
- PJ_ASSERT_RETURN(play_id == 0 && rec_id == 0, PJ_EINVAL);
-
- return sound_open(PJMEDIA_DIR_CAPTURE_PLAYBACK, clock_rate, channel_count,
- samples_per_frame, bits_per_sample, rec_cb, play_cb,
- user_data, p_snd_strm);
-}
-
-/*
- * Get stream info.
- */
-PJ_DEF(pj_status_t) pjmedia_snd_stream_get_info(pjmedia_snd_stream *strm,
- pjmedia_snd_stream_info *pi)
-{
- PJ_ASSERT_RETURN(strm && pi, PJ_EINVAL);
-
- pj_bzero(pi, sizeof(*pi));
- pi->dir = strm->dir;
- pi->play_id = 0;
- pi->rec_id = 0;
- pi->clock_rate = strm->clock_rate;
- pi->channel_count = strm->channel_count;
- pi->samples_per_frame = strm->samples_per_frame;
- pi->bits_per_sample = BYTES_PER_SAMPLE * 8;
- // latencies approximation (in samples)
- pi->rec_latency = strm->samples_per_frame * 2;
- pi->play_latency = strm->samples_per_frame * 2;
-
- return PJ_SUCCESS;
-}
-
-
-PJ_DEF(pj_status_t) pjmedia_snd_stream_start(pjmedia_snd_stream *stream)
-{
- PJ_ASSERT_RETURN(stream != NULL, PJ_EINVAL);
-
- if (stream->engine) {
- TInt err = stream->engine->StartL();
- if (err != KErrNone)
- return PJ_RETURN_OS_ERROR(err);
- }
-
- return PJ_SUCCESS;
-}
-
-
-PJ_DEF(pj_status_t) pjmedia_snd_stream_stop(pjmedia_snd_stream *stream)
-{
- PJ_ASSERT_RETURN(stream != NULL, PJ_EINVAL);
-
- if (stream->engine) {
- stream->engine->Stop();
- }
-
- return PJ_SUCCESS;
-}
-
-
-PJ_DEF(pj_status_t) pjmedia_snd_stream_close(pjmedia_snd_stream *stream)
-{
- pj_pool_t *pool;
-
- PJ_ASSERT_RETURN(stream != NULL, PJ_EINVAL);
-
- if (stream->engine) {
- delete stream->engine;
- stream->engine = NULL;
- }
-
- pool = stream->pool;
- if (pool) {
- stream->pool = NULL;
- pj_pool_release(pool);
- }
-
- return PJ_SUCCESS;
-}
-
-
-PJ_DEF(pj_status_t) pjmedia_snd_deinit(void)
-{
- /* Nothing to do */
- return PJ_SUCCESS;
-}
-
-
-/*
- * Set sound latency.
- */
-PJ_DEF(pj_status_t) pjmedia_snd_set_latency(unsigned input_latency,
- unsigned output_latency)
-{
- /* Nothing to do */
- PJ_UNUSED_ARG(input_latency);
- PJ_UNUSED_ARG(output_latency);
- return PJ_SUCCESS;
-}
-
-
-/*
- * Activate/deactivate loudspeaker.
- */
-PJ_DEF(pj_status_t) pjmedia_snd_aps_activate_loudspeaker(
- pjmedia_snd_stream *stream,
- pj_bool_t active)
-{
- if (stream == NULL) {
- act_loudspeaker = active;
- } else {
- if (stream->engine == NULL)
- return PJ_EINVAL;
-
- TInt err = stream->engine->ActivateSpeaker(active);
- if (err != KErrNone)
- return PJ_RETURN_OS_ERROR(err);
- }
-
- return PJ_SUCCESS;
-}
diff --git a/pjmedia/src/pjmedia/transport_ice.c b/pjmedia/src/pjmedia/transport_ice.c
index 7e0b6b75..77a2e346 100644
--- a/pjmedia/src/pjmedia/transport_ice.c
+++ b/pjmedia/src/pjmedia/transport_ice.c
@@ -21,6 +21,7 @@
#include <pjnath/errno.h>
#include <pj/assert.h>
#include <pj/log.h>
+#include <pj/pool.h>
#include <pj/rand.h>
#define THIS_FILE "transport_ice.c"
diff --git a/pjmedia/src/pjmedia/transport_loop.c b/pjmedia/src/pjmedia/transport_loop.c
index d9742d26..418e04a6 100644
--- a/pjmedia/src/pjmedia/transport_loop.c
+++ b/pjmedia/src/pjmedia/transport_loop.c
@@ -22,6 +22,7 @@
#include <pj/errno.h>
#include <pj/ioqueue.h>
#include <pj/log.h>
+#include <pj/pool.h>
#include <pj/rand.h>
#include <pj/string.h>
diff --git a/pjmedia/src/pjmedia/transport_udp.c b/pjmedia/src/pjmedia/transport_udp.c
index 11702713..2236392c 100644
--- a/pjmedia/src/pjmedia/transport_udp.c
+++ b/pjmedia/src/pjmedia/transport_udp.c
@@ -23,6 +23,7 @@
#include <pj/errno.h>
#include <pj/ioqueue.h>
#include <pj/log.h>
+#include <pj/pool.h>
#include <pj/rand.h>
#include <pj/string.h>
diff --git a/pjmedia/src/pjmedia/wmme_sound.c b/pjmedia/src/pjmedia/wmme_sound.c
deleted file mode 100644
index 8f94660c..00000000
--- a/pjmedia/src/pjmedia/wmme_sound.c
+++ /dev/null
@@ -1,1008 +0,0 @@
-#include <pjmedia/sound.h>
-#include <pjmedia/errno.h>
-#include <pj/assert.h>
-#include <pj/log.h>
-#include <pj/os.h>
-#include <pj/string.h>
-
-#if PJMEDIA_SOUND_IMPLEMENTATION == PJMEDIA_SOUND_WIN32_MME_SOUND
-
-#ifdef _MSC_VER
-# pragma warning(push, 3)
-#endif
-
-#include <windows.h>
-#include <mmsystem.h>
-
-#ifdef _MSC_VER
-# pragma warning(pop)
-#endif
-
-#if defined(PJ_WIN32_WINCE) && PJ_WIN32_WINCE!=0
-# pragma comment(lib, "Coredll.lib")
-#elif defined(_MSC_VER)
-# pragma comment(lib, "winmm.lib")
-#endif
-
-
-#define THIS_FILE "wmme_sound.c"
-#define BITS_PER_SAMPLE 16
-#define BYTES_PER_SAMPLE (BITS_PER_SAMPLE/8)
-
-#define MAX_PACKET_BUFFER_COUNT 32
-#define MAX_HARDWARE 16
-
-struct wmme_dev_info
-{
- pjmedia_snd_dev_info info;
- unsigned deviceId;
-};
-
-static unsigned dev_count;
-static struct wmme_dev_info dev_info[MAX_HARDWARE];
-static pj_bool_t snd_initialized = PJ_FALSE;
-
-/* Latency settings */
-static unsigned snd_input_latency = PJMEDIA_SND_DEFAULT_REC_LATENCY;
-static unsigned snd_output_latency = PJMEDIA_SND_DEFAULT_PLAY_LATENCY;
-
-
-/* Individual WMME capture/playback stream descriptor */
-struct wmme_stream
-{
- union
- {
- HWAVEIN In;
- HWAVEOUT Out;
- } hWave;
-
- WAVEHDR *WaveHdr;
- HANDLE hEvent;
- DWORD dwBufIdx;
- DWORD dwMaxBufIdx;
- pj_timestamp timestamp;
-};
-
-
-/* Sound stream. */
-struct pjmedia_snd_stream
-{
- pjmedia_dir dir; /**< Sound direction. */
- int play_id; /**< Playback dev id. */
- int rec_id; /**< Recording dev id. */
- pj_pool_t *pool; /**< Memory pool. */
-
- pjmedia_snd_rec_cb rec_cb; /**< Capture callback. */
- pjmedia_snd_play_cb play_cb; /**< Playback callback. */
- void *user_data; /**< Application data. */
-
- struct wmme_stream play_strm; /**< Playback stream. */
- struct wmme_stream rec_strm; /**< Capture stream. */
-
- void *buffer; /**< Temp. frame buffer. */
- unsigned clock_rate; /**< Clock rate. */
- unsigned samples_per_frame; /**< Samples per frame. */
- unsigned bits_per_sample; /**< Bits per sample. */
- unsigned channel_count; /**< Channel count. */
-
- pj_thread_t *thread; /**< Thread handle. */
- HANDLE thread_quit_event; /**< Quit signal to thread */
-};
-
-
-static pj_pool_factory *pool_factory;
-
-static void init_waveformatex (LPWAVEFORMATEX pcmwf,
- unsigned clock_rate,
- unsigned channel_count)
-{
- pj_bzero(pcmwf, sizeof(PCMWAVEFORMAT));
- pcmwf->wFormatTag = WAVE_FORMAT_PCM;
- pcmwf->nChannels = (pj_uint16_t)channel_count;
- pcmwf->nSamplesPerSec = clock_rate;
- pcmwf->nBlockAlign = (pj_uint16_t)(channel_count * BYTES_PER_SAMPLE);
- pcmwf->nAvgBytesPerSec = clock_rate * channel_count * BYTES_PER_SAMPLE;
- pcmwf->wBitsPerSample = BITS_PER_SAMPLE;
-}
-
-
-/*
- * Initialize WMME player device.
- */
-static pj_status_t init_player_stream( pj_pool_t *pool,
- struct wmme_stream *wmme_strm,
- int dev_id,
- unsigned clock_rate,
- unsigned channel_count,
- unsigned samples_per_frame,
- unsigned buffer_count)
-{
- MMRESULT mr;
- WAVEFORMATEX pcmwf;
- unsigned bytes_per_frame;
- unsigned i;
-
- PJ_ASSERT_RETURN(buffer_count <= MAX_PACKET_BUFFER_COUNT, PJ_EINVAL);
-
- /* Check device ID */
- if (dev_id == -1)
- dev_id = 0;
-
- PJ_ASSERT_RETURN(dev_id >= 0 && dev_id < (int)dev_count, PJ_EINVAL);
-
- /*
- * Create a wait event.
- */
- wmme_strm->hEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
- if (NULL == wmme_strm->hEvent)
- return pj_get_os_error();
-
- /*
- * Set up wave format structure for opening the device.
- */
- init_waveformatex(&pcmwf, clock_rate, channel_count);
- bytes_per_frame = samples_per_frame * BYTES_PER_SAMPLE;
-
- /*
- * Open wave device.
- */
- mr = waveOutOpen(&wmme_strm->hWave.Out, dev_info[dev_id].deviceId, &pcmwf,
- (DWORD)wmme_strm->hEvent, 0, CALLBACK_EVENT);
- if (mr != MMSYSERR_NOERROR)
- /* TODO: This is for HRESULT/GetLastError() */
- PJ_RETURN_OS_ERROR(mr);
-
- /* Pause the wave out device */
- mr = waveOutPause(wmme_strm->hWave.Out);
- if (mr != MMSYSERR_NOERROR)
- /* TODO: This is for HRESULT/GetLastError() */
- PJ_RETURN_OS_ERROR(mr);
-
- /*
- * Create the buffers.
- */
- wmme_strm->WaveHdr = pj_pool_zalloc(pool, sizeof(WAVEHDR) * buffer_count);
- for (i = 0; i < buffer_count; ++i)
- {
- wmme_strm->WaveHdr[i].lpData = pj_pool_zalloc(pool, bytes_per_frame);
- wmme_strm->WaveHdr[i].dwBufferLength = bytes_per_frame;
- mr = waveOutPrepareHeader(wmme_strm->hWave.Out,
- &(wmme_strm->WaveHdr[i]),
- sizeof(WAVEHDR));
- if (mr != MMSYSERR_NOERROR)
- /* TODO: This is for HRESULT/GetLastError() */
- PJ_RETURN_OS_ERROR(mr);
- mr = waveOutWrite(wmme_strm->hWave.Out, &(wmme_strm->WaveHdr[i]),
- sizeof(WAVEHDR));
- if (mr != MMSYSERR_NOERROR)
- /* TODO: This is for HRESULT/GetLastError() */
- PJ_RETURN_OS_ERROR(mr);
- }
-
- wmme_strm->dwBufIdx = 0;
- wmme_strm->dwMaxBufIdx = buffer_count;
- wmme_strm->timestamp.u64 = 0;
-
- /* Done setting up play device. */
- PJ_LOG(5, (THIS_FILE,
- " WaveAPI Sound player \"%s\" initialized (clock_rate=%d, "
- "channel_count=%d, samples_per_frame=%d (%dms))",
- dev_info[dev_id].info.name,
- clock_rate, channel_count, samples_per_frame,
- samples_per_frame * 1000 / clock_rate));
-
- return PJ_SUCCESS;
-}
-
-
-/*
- * Initialize Windows Multimedia recorder device
- */
-static pj_status_t init_capture_stream( pj_pool_t *pool,
- struct wmme_stream *wmme_strm,
- int dev_id,
- unsigned clock_rate,
- unsigned channel_count,
- unsigned samples_per_frame,
- unsigned buffer_count)
-{
- MMRESULT mr;
- WAVEFORMATEX pcmwf;
- unsigned bytes_per_frame;
- unsigned i;
-
- PJ_ASSERT_RETURN(buffer_count <= MAX_PACKET_BUFFER_COUNT, PJ_EINVAL);
-
- /* Check device ID */
- if (dev_id == -1)
- dev_id = 0;
-
- PJ_ASSERT_RETURN(dev_id >= 0 && dev_id < (int)dev_count, PJ_EINVAL);
-
- /*
- * Create a wait event.
- */
- wmme_strm->hEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
- if (NULL == wmme_strm->hEvent)
- return pj_get_os_error();
-
- /*
- * Set up wave format structure for opening the device.
- */
- init_waveformatex(&pcmwf, clock_rate, channel_count);
- bytes_per_frame = samples_per_frame * BYTES_PER_SAMPLE;
-
- /*
- * Open wave device.
- */
- mr = waveInOpen(&wmme_strm->hWave.In, dev_info[dev_id].deviceId, &pcmwf,
- (DWORD)wmme_strm->hEvent, 0, CALLBACK_EVENT);
- if (mr != MMSYSERR_NOERROR)
- /* TODO: This is for HRESULT/GetLastError() */
- PJ_RETURN_OS_ERROR(mr);
-
- /*
- * Create the buffers.
- */
- wmme_strm->WaveHdr = pj_pool_zalloc(pool, sizeof(WAVEHDR) * buffer_count);
- for (i = 0; i < buffer_count; ++i)
- {
- wmme_strm->WaveHdr[i].lpData = pj_pool_zalloc(pool, bytes_per_frame);
- wmme_strm->WaveHdr[i].dwBufferLength = bytes_per_frame;
- mr = waveInPrepareHeader(wmme_strm->hWave.In, &(wmme_strm->WaveHdr[i]),
- sizeof(WAVEHDR));
- if (mr != MMSYSERR_NOERROR)
- /* TODO: This is for HRESULT/GetLastError() */
- PJ_RETURN_OS_ERROR(mr);
- mr = waveInAddBuffer(wmme_strm->hWave.In, &(wmme_strm->WaveHdr[i]),
- sizeof(WAVEHDR));
- if (mr != MMSYSERR_NOERROR)
- /* TODO: This is for HRESULT/GetLastError() */
- PJ_RETURN_OS_ERROR(mr);
- }
-
- wmme_strm->dwBufIdx = 0;
- wmme_strm->dwMaxBufIdx = buffer_count;
- wmme_strm->timestamp.u64 = 0;
-
- /* Done setting up play device. */
- PJ_LOG(5,(THIS_FILE,
- " WaveAPI Sound recorder \"%s\" initialized (clock_rate=%d, "
- "channel_count=%d, samples_per_frame=%d (%dms))",
- dev_info[dev_id].info.name,
- clock_rate, channel_count, samples_per_frame,
- samples_per_frame * 1000 / clock_rate));
-
- return PJ_SUCCESS;
-}
-
-
-
-/*
-* WMME capture and playback thread.
-*/
-static int PJ_THREAD_FUNC wmme_dev_thread(void *arg)
-{
- pjmedia_snd_stream *strm = arg;
- HANDLE events[3];
- unsigned eventCount;
- unsigned bytes_per_frame;
- pj_status_t status = PJ_SUCCESS;
-
-
- eventCount = 0;
- events[eventCount++] = strm->thread_quit_event;
- if (strm->dir & PJMEDIA_DIR_PLAYBACK)
- events[eventCount++] = strm->play_strm.hEvent;
- if (strm->dir & PJMEDIA_DIR_CAPTURE)
- events[eventCount++] = strm->rec_strm.hEvent;
-
-
- /* Raise self priority. We don't want the audio to be distorted by
- * system activity.
- */
-#if defined(PJ_WIN32_WINCE) && PJ_WIN32_WINCE != 0
- if (strm->dir & PJMEDIA_DIR_PLAYBACK)
- CeSetThreadPriority(GetCurrentThread(), 153);
- else
- CeSetThreadPriority(GetCurrentThread(), 247);
-#else
- SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_TIME_CRITICAL);
-#endif
-
- /* Calculate bytes per frame */
- bytes_per_frame = strm->samples_per_frame * BYTES_PER_SAMPLE;
-
- /*
- * Loop while not signalled to quit, wait for event objects to be
- * signalled by WMME capture and play buffer.
- */
- while (status == PJ_SUCCESS)
- {
-
- DWORD rc;
- pjmedia_dir signalled_dir;
-
- rc = WaitForMultipleObjects(eventCount, events, FALSE, INFINITE);
- if (rc < WAIT_OBJECT_0 || rc >= WAIT_OBJECT_0 + eventCount)
- continue;
-
- if (rc == WAIT_OBJECT_0)
- break;
-
- if (rc == (WAIT_OBJECT_0 + 1))
- {
- if (events[1] == strm->play_strm.hEvent)
- signalled_dir = PJMEDIA_DIR_PLAYBACK;
- else
- signalled_dir = PJMEDIA_DIR_CAPTURE;
- }
- else
- {
- if (events[2] == strm->play_strm.hEvent)
- signalled_dir = PJMEDIA_DIR_PLAYBACK;
- else
- signalled_dir = PJMEDIA_DIR_CAPTURE;
- }
-
-
- if (signalled_dir == PJMEDIA_DIR_PLAYBACK)
- {
- struct wmme_stream *wmme_strm = &strm->play_strm;
- MMRESULT mr = MMSYSERR_NOERROR;
- status = PJ_SUCCESS;
-
- /*
- * Windows Multimedia has requested us to feed some frames to
- * playback buffer.
- */
-
- while (wmme_strm->WaveHdr[wmme_strm->dwBufIdx].dwFlags & WHDR_DONE)
- {
- void* buffer = wmme_strm->WaveHdr[wmme_strm->dwBufIdx].lpData;
-
- PJ_LOG(5,(THIS_FILE, "Finished writing buffer %d",
- wmme_strm->dwBufIdx));
-
- /* Get frame from application. */
- status = (*strm->play_cb)(strm->user_data,
- wmme_strm->timestamp.u32.lo,
- buffer,
- bytes_per_frame);
-
- if (status != PJ_SUCCESS)
- break;
-
- /* Write to the device. */
- mr = waveOutWrite(wmme_strm->hWave.Out,
- &(wmme_strm->WaveHdr[wmme_strm->dwBufIdx]),
- sizeof(WAVEHDR));
- if (mr != MMSYSERR_NOERROR)
- {
- status = PJ_STATUS_FROM_OS(mr);
- break;
- }
-
- /* Increment position. */
- if (++wmme_strm->dwBufIdx >= wmme_strm->dwMaxBufIdx)
- wmme_strm->dwBufIdx = 0;
- wmme_strm->timestamp.u64 += strm->samples_per_frame /
- strm->channel_count;
- }
- }
- else
- {
- struct wmme_stream *wmme_strm = &strm->rec_strm;
- MMRESULT mr = MMSYSERR_NOERROR;
- status = PJ_SUCCESS;
-
- /*
- * Windows Multimedia has indicated that it has some frames ready
- * in the capture buffer. Get as much frames as possible to
- * prevent overflows.
- */
-#if 0
- {
- static DWORD tc = 0;
- DWORD now = GetTickCount();
- DWORD i = 0;
- DWORD bits = 0;
-
- if (tc == 0) tc = now;
-
- for (i = 0; i < wmme_strm->dwMaxBufIdx; ++i)
- {
- bits = bits << 4;
- bits |= wmme_strm->WaveHdr[i].dwFlags & WHDR_DONE;
- }
- PJ_LOG(5,(THIS_FILE, "Record Signal> Index: %d, Delta: %4.4d, "
- "Flags: %6.6x\n",
- wmme_strm->dwBufIdx,
- now - tc,
- bits));
- tc = now;
- }
-#endif
-
- while (wmme_strm->WaveHdr[wmme_strm->dwBufIdx].dwFlags & WHDR_DONE)
- {
- char* buffer = (char*)
- wmme_strm->WaveHdr[wmme_strm->dwBufIdx].lpData;
- unsigned cap_len =
- wmme_strm->WaveHdr[wmme_strm->dwBufIdx].dwBytesRecorded;
-
- /*
- PJ_LOG(5,(THIS_FILE, "Read %d bytes from buffer %d", cap_len,
- wmme_strm->dwBufIdx));
- */
-
- if (cap_len < bytes_per_frame)
- pj_bzero(buffer + cap_len, bytes_per_frame - cap_len);
-
- /* Copy the audio data out of the wave buffer. */
- pj_memcpy(strm->buffer, buffer, bytes_per_frame);
-
- /* Re-add the buffer to the device. */
- mr = waveInAddBuffer(wmme_strm->hWave.In,
- &(wmme_strm->WaveHdr[wmme_strm->dwBufIdx]),
- sizeof(WAVEHDR));
- if (mr != MMSYSERR_NOERROR) {
- status = PJ_STATUS_FROM_OS(mr);
- break;
- }
-
- /* Call callback */
- status = (*strm->rec_cb)(strm->user_data,
- wmme_strm->timestamp.u32.lo,
- strm->buffer,
- bytes_per_frame);
-
- if (status != PJ_SUCCESS)
- break;
-
- /* Increment position. */
- if (++wmme_strm->dwBufIdx >= wmme_strm->dwMaxBufIdx)
- wmme_strm->dwBufIdx = 0;
- wmme_strm->timestamp.u64 += strm->samples_per_frame /
- strm->channel_count;
- }
- }
- }
-
- PJ_LOG(5,(THIS_FILE, "WMME: thread stopping.."));
- return 0;
-}
-
-
-/*
-* Init sound library.
-*/
-PJ_DEF(pj_status_t) pjmedia_snd_init(pj_pool_factory *factory)
-{
- unsigned c;
- int i;
- int inputDeviceCount, outputDeviceCount, maximumPossibleDeviceCount;
-
- if (snd_initialized)
- return PJ_SUCCESS;
-
- pj_bzero(&dev_info, sizeof(dev_info));
-
- dev_count = 0;
- pool_factory = factory;
-
- /* Enumerate sound playback devices */
- maximumPossibleDeviceCount = 0;
-
- inputDeviceCount = waveInGetNumDevs();
- if (inputDeviceCount > 0)
- /* assume there is a WAVE_MAPPER */
- maximumPossibleDeviceCount += inputDeviceCount + 1;
-
- outputDeviceCount = waveOutGetNumDevs();
- if (outputDeviceCount > 0)
- /* assume there is a WAVE_MAPPER */
- maximumPossibleDeviceCount += outputDeviceCount + 1;
-
- if (maximumPossibleDeviceCount >= MAX_HARDWARE)
- {
- pj_assert(!"Too many hardware found");
- PJ_LOG(3,(THIS_FILE, "Too many hardware found, "
- "some devices will not be listed"));
- }
-
- if (inputDeviceCount > 0)
- {
- /* -1 is the WAVE_MAPPER */
- for (i = -1; i < inputDeviceCount && dev_count < MAX_HARDWARE; ++i)
- {
- UINT uDeviceID = (UINT)((i==-1) ? WAVE_MAPPER : i);
- WAVEINCAPS wic;
- MMRESULT mr;
-
- pj_bzero(&wic, sizeof(WAVEINCAPS));
-
- mr = waveInGetDevCaps(uDeviceID, &wic, sizeof(WAVEINCAPS));
-
- if (mr == MMSYSERR_NOMEM)
- return PJ_ENOMEM;
-
- if (mr != MMSYSERR_NOERROR)
- continue;
-
-#ifdef UNICODE
- WideCharToMultiByte(CP_ACP, 0, wic.szPname, wcslen(wic.szPname),
- dev_info[dev_count].info.name, 64, NULL, NULL);
-#else
- strncpy(dev_info[dev_count].info.name, wic.szPname, MAXPNAMELEN);
-#endif
- if (uDeviceID == WAVE_MAPPER)
- strcat(dev_info[dev_count].info.name, " - Input");
-
- dev_info[dev_count].info.input_count = wic.wChannels;
- dev_info[dev_count].info.output_count = 0;
- dev_info[dev_count].info.default_samples_per_sec = 44100;
- dev_info[dev_count].deviceId = uDeviceID;
-
- /* Sometimes a device can return a rediculously large number of
- * channels. This happened with an SBLive card on a Windows ME box.
- * It also happens on Win XP!
- */
- if ((dev_info[dev_count].info.input_count < 1) ||
- (dev_info[dev_count].info.input_count > 256))
- dev_info[dev_count].info.input_count = 2;
-
- ++dev_count;
- }
- }
-
- if( outputDeviceCount > 0 )
- {
- /* -1 is the WAVE_MAPPER */
- for (i = -1; i < outputDeviceCount && dev_count < MAX_HARDWARE; ++i)
- {
- UINT uDeviceID = (UINT)((i==-1) ? WAVE_MAPPER : i);
- WAVEOUTCAPS woc;
- MMRESULT mr;
-
- pj_bzero(&woc, sizeof(WAVEOUTCAPS));
-
- mr = waveOutGetDevCaps(uDeviceID, &woc, sizeof(WAVEOUTCAPS));
-
- if (mr == MMSYSERR_NOMEM)
- return PJ_ENOMEM;
-
- if (mr != MMSYSERR_NOERROR)
- continue;
-
-#ifdef UNICODE
- WideCharToMultiByte(CP_ACP, 0, woc.szPname, wcslen(woc.szPname),
- dev_info[dev_count].info.name, 64, NULL, NULL);
-#else
- strncpy(dev_info[dev_count].info.name, woc.szPname, MAXPNAMELEN);
-#endif
- if (uDeviceID == WAVE_MAPPER)
- strcat(dev_info[dev_count].info.name, " - Output");
-
- dev_info[dev_count].info.output_count = woc.wChannels;
- dev_info[dev_count].info.input_count = 0;
- dev_info[dev_count].deviceId = uDeviceID;
- /* TODO: Perform a search! */
- dev_info[dev_count].info.default_samples_per_sec = 44100;
-
- /* Sometimes a device can return a rediculously large number of channels.
- * This happened with an SBLive card on a Windows ME box.
- * It also happens on Win XP!
- */
- if ((dev_info[dev_count].info.output_count < 1) ||
- (dev_info[dev_count].info.output_count > 256))
- dev_info[dev_count].info.output_count = 2;
-
- ++dev_count;
- }
- }
-
- PJ_LOG(4, (THIS_FILE, "WMME initialized, found %d devices:", dev_count));
- for (c = 0; c < dev_count; ++c)
- {
- PJ_LOG(4, (THIS_FILE, " dev_id %d: %s (in=%d, out=%d)",
- c,
- dev_info[c].info.name,
- dev_info[c].info.input_count,
- dev_info[c].info.output_count));
- }
- return PJ_SUCCESS;
-}
-
-/*
- * Deinitialize sound library.
- */
-PJ_DEF(pj_status_t) pjmedia_snd_deinit(void)
-{
- snd_initialized = PJ_FALSE;
- return PJ_SUCCESS;
-}
-
-/*
- * Get device count.
- */
-PJ_DEF(int) pjmedia_snd_get_dev_count(void)
-{
- return dev_count;
-}
-
-/*
- * Get device info.
- */
-PJ_DEF(const pjmedia_snd_dev_info*) pjmedia_snd_get_dev_info(unsigned index)
-{
- if (index == (unsigned)-1)
- index = 0;
-
- PJ_ASSERT_RETURN(index < dev_count, NULL);
-
- return &dev_info[index].info;
-}
-
-
-/*
- * Open stream.
- */
-static pj_status_t open_stream(pjmedia_dir dir,
- int rec_id,
- int play_id,
- unsigned clock_rate,
- unsigned channel_count,
- unsigned samples_per_frame,
- unsigned bits_per_sample,
- pjmedia_snd_rec_cb rec_cb,
- pjmedia_snd_play_cb play_cb,
- void *user_data,
- pjmedia_snd_stream **p_snd_strm)
-{
- pj_pool_t *pool;
- pjmedia_snd_stream *strm;
- pj_status_t status;
-
-
- /* Make sure sound subsystem has been initialized with
- * pjmedia_snd_init()
- */
- PJ_ASSERT_RETURN(pool_factory != NULL, PJ_EINVALIDOP);
-
-
- /* Can only support 16bits per sample */
- PJ_ASSERT_RETURN(bits_per_sample == BITS_PER_SAMPLE, PJ_EINVAL);
-
- /* Create and Initialize stream descriptor */
- pool = pj_pool_create(pool_factory, "wmme-dev", 1000, 1000, NULL);
- PJ_ASSERT_RETURN(pool != NULL, PJ_ENOMEM);
-
- strm = pj_pool_zalloc(pool, sizeof(pjmedia_snd_stream));
- strm->dir = dir;
- strm->play_id = play_id;
- strm->rec_id = rec_id;
- strm->pool = pool;
- strm->rec_cb = rec_cb;
- strm->play_cb = play_cb;
- strm->user_data = user_data;
- strm->clock_rate = clock_rate;
- strm->samples_per_frame = samples_per_frame;
- strm->bits_per_sample = bits_per_sample;
- strm->channel_count = channel_count;
- strm->buffer = pj_pool_alloc(pool, samples_per_frame * BYTES_PER_SAMPLE);
- if (!strm->buffer)
- {
- pj_pool_release(pool);
- return PJ_ENOMEM;
- }
-
- /* Create player stream */
- if (dir & PJMEDIA_DIR_PLAYBACK)
- {
- unsigned buf_count;
-
- buf_count = snd_output_latency * clock_rate * channel_count /
- samples_per_frame / 1000;
-
- status = init_player_stream(strm->pool,
- &strm->play_strm,
- play_id,
- clock_rate,
- channel_count,
- samples_per_frame,
- buf_count);
-
- if (status != PJ_SUCCESS)
- {
- pjmedia_snd_stream_close(strm);
- return status;
- }
- }
-
- /* Create capture stream */
- if (dir & PJMEDIA_DIR_CAPTURE)
- {
- unsigned buf_count;
-
- buf_count = snd_input_latency * clock_rate * channel_count /
- samples_per_frame / 1000;
-
- status = init_capture_stream(strm->pool,
- &strm->rec_strm,
- rec_id,
- clock_rate,
- channel_count,
- samples_per_frame,
- buf_count);
-
- if (status != PJ_SUCCESS)
- {
- pjmedia_snd_stream_close(strm);
- return status;
- }
- }
-
- /* Create the stop event */
- strm->thread_quit_event = CreateEvent(NULL, FALSE, FALSE, NULL);
- if (strm->thread_quit_event == NULL)
- return pj_get_os_error();
-
- /* Create and start the thread */
- status = pj_thread_create(pool, "wmme", &wmme_dev_thread, strm, 0, 0,
- &strm->thread);
- if (status != PJ_SUCCESS)
- {
- pjmedia_snd_stream_close(strm);
- return status;
- }
-
- *p_snd_strm = strm;
-
- return PJ_SUCCESS;
-}
-
-/*
- * Open stream.
- */
-PJ_DEF(pj_status_t) pjmedia_snd_open_rec(int index,
- unsigned clock_rate,
- unsigned channel_count,
- unsigned samples_per_frame,
- unsigned bits_per_sample,
- pjmedia_snd_rec_cb rec_cb,
- void *user_data,
- pjmedia_snd_stream **p_snd_strm)
-{
- PJ_ASSERT_RETURN(rec_cb && p_snd_strm, PJ_EINVAL);
-
- return open_stream( PJMEDIA_DIR_CAPTURE,
- index,
- -1,
- clock_rate,
- channel_count,
- samples_per_frame,
- bits_per_sample,
- rec_cb,
- NULL,
- user_data,
- p_snd_strm);
-}
-
-PJ_DEF(pj_status_t) pjmedia_snd_open_player(int index,
- unsigned clock_rate,
- unsigned channel_count,
- unsigned samples_per_frame,
- unsigned bits_per_sample,
- pjmedia_snd_play_cb play_cb,
- void *user_data,
- pjmedia_snd_stream **p_snd_strm)
-{
- PJ_ASSERT_RETURN(play_cb && p_snd_strm, PJ_EINVAL);
-
- return open_stream( PJMEDIA_DIR_PLAYBACK,
- -1,
- index,
- clock_rate,
- channel_count,
- samples_per_frame,
- bits_per_sample,
- NULL,
- play_cb,
- user_data,
- p_snd_strm);
-}
-
-/*
- * Open both player and recorder.
- */
-PJ_DEF(pj_status_t) pjmedia_snd_open(int rec_id,
- int play_id,
- unsigned clock_rate,
- unsigned channel_count,
- unsigned samples_per_frame,
- unsigned bits_per_sample,
- pjmedia_snd_rec_cb rec_cb,
- pjmedia_snd_play_cb play_cb,
- void *user_data,
- pjmedia_snd_stream **p_snd_strm)
-{
- PJ_ASSERT_RETURN(rec_cb && play_cb && p_snd_strm, PJ_EINVAL);
-
- return open_stream( PJMEDIA_DIR_CAPTURE_PLAYBACK,
- rec_id,
- play_id,
- clock_rate,
- channel_count,
- samples_per_frame,
- bits_per_sample,
- rec_cb,
- play_cb,
- user_data,
- p_snd_strm);
-}
-
-/*
- * Get stream info.
- */
-PJ_DEF(pj_status_t) pjmedia_snd_stream_get_info(pjmedia_snd_stream *strm,
- pjmedia_snd_stream_info *pi)
-{
- PJ_ASSERT_RETURN(strm && pi, PJ_EINVAL);
-
- pj_bzero(pi, sizeof(*pi));
- pi->dir = strm->dir;
- pi->play_id = strm->play_id;
- pi->rec_id = strm->rec_id;
- pi->clock_rate = strm->clock_rate;
- pi->channel_count = strm->channel_count;
- pi->samples_per_frame = strm->samples_per_frame;
- pi->bits_per_sample = strm->bits_per_sample;
- pi->rec_latency = snd_input_latency * strm->clock_rate *
- strm->channel_count / 1000;
- pi->play_latency = snd_output_latency * strm->clock_rate *
- strm->channel_count / 1000;
-
- return PJ_SUCCESS;
-}
-
-
-/*
-* Start stream.
-*/
-PJ_DEF(pj_status_t) pjmedia_snd_stream_start(pjmedia_snd_stream *stream)
-{
- MMRESULT mr;
-
- PJ_UNUSED_ARG(stream);
-
- if (stream->play_strm.hWave.Out != NULL)
- {
- mr = waveOutRestart(stream->play_strm.hWave.Out);
- if (mr != MMSYSERR_NOERROR)
- /* TODO: This macro is supposed to be used for HRESULT, fix. */
- PJ_RETURN_OS_ERROR(mr);
- PJ_LOG(5,(THIS_FILE, "WMME playback stream started"));
- }
-
- if (stream->rec_strm.hWave.In != NULL)
- {
- mr = waveInStart(stream->rec_strm.hWave.In);
- if (mr != MMSYSERR_NOERROR)
- /* TODO: This macro is supposed to be used for HRESULT, fix. */
- PJ_RETURN_OS_ERROR(mr);
- PJ_LOG(5,(THIS_FILE, "WMME capture stream started"));
- }
-
- return PJ_SUCCESS;
-}
-
-/*
- * Stop stream.
- */
-PJ_DEF(pj_status_t) pjmedia_snd_stream_stop(pjmedia_snd_stream *stream)
-{
- MMRESULT mr;
-
- PJ_ASSERT_RETURN(stream != NULL, PJ_EINVAL);
-
- if (stream->play_strm.hWave.Out != NULL)
- {
- mr = waveOutPause(stream->play_strm.hWave.Out);
- if (mr != MMSYSERR_NOERROR)
- /* TODO: This macro is supposed to be used for HRESULT, fix. */
- PJ_RETURN_OS_ERROR(mr);
- PJ_LOG(5,(THIS_FILE, "Stopped WMME playback stream"));
- }
-
- if (stream->rec_strm.hWave.In != NULL)
- {
- mr = waveInStop(stream->rec_strm.hWave.In);
- if (mr != MMSYSERR_NOERROR)
- /* TODO: This macro is supposed to be used for HRESULT, fix. */
- PJ_RETURN_OS_ERROR(mr);
- PJ_LOG(5,(THIS_FILE, "Stopped WMME capture stream"));
- }
-
- return PJ_SUCCESS;
-}
-
-
-/*
- * Destroy stream.
- */
-PJ_DEF(pj_status_t) pjmedia_snd_stream_close(pjmedia_snd_stream *stream)
-{
- unsigned i;
-
- PJ_ASSERT_RETURN(stream != NULL, PJ_EINVAL);
-
- pjmedia_snd_stream_stop(stream);
-
- if (stream->thread)
- {
- SetEvent(stream->thread_quit_event);
- pj_thread_join(stream->thread);
- pj_thread_destroy(stream->thread);
- stream->thread = NULL;
- }
-
- /* Unprepare the headers and close the play device */
- if (stream->play_strm.hWave.Out)
- {
- waveOutReset(stream->play_strm.hWave.Out);
- for (i = 0; i < stream->play_strm.dwMaxBufIdx; ++i)
- waveOutUnprepareHeader(stream->play_strm.hWave.Out,
- &(stream->play_strm.WaveHdr[i]),
- sizeof(WAVEHDR));
- waveOutClose(stream->play_strm.hWave.Out);
- stream->play_strm.hWave.Out = NULL;
- }
-
- /* Close the play event */
- if (stream->play_strm.hEvent)
- {
- CloseHandle(stream->play_strm.hEvent);
- stream->play_strm.hEvent = NULL;
- }
-
- /* Unprepare the headers and close the record device */
- if (stream->rec_strm.hWave.In)
- {
- waveInReset(stream->rec_strm.hWave.In);
- for (i = 0; i < stream->play_strm.dwMaxBufIdx; ++i)
- waveInUnprepareHeader(stream->rec_strm.hWave.In,
- &(stream->rec_strm.WaveHdr[i]),
- sizeof(WAVEHDR));
- waveInClose(stream->rec_strm.hWave.In);
- stream->rec_strm.hWave.In = NULL;
- }
-
- /* Close the record event */
- if (stream->rec_strm.hEvent)
- {
- CloseHandle(stream->rec_strm.hEvent);
- stream->rec_strm.hEvent = NULL;
- }
-
- pj_pool_release(stream->pool);
-
- return PJ_SUCCESS;
-}
-
-/*
- * Set sound latency.
- */
-PJ_DEF(pj_status_t) pjmedia_snd_set_latency(unsigned input_latency,
- unsigned output_latency)
-{
- snd_input_latency = (input_latency == 0)?
- PJMEDIA_SND_DEFAULT_REC_LATENCY : input_latency;
- snd_output_latency = (output_latency == 0)?
- PJMEDIA_SND_DEFAULT_PLAY_LATENCY : output_latency;
-
- return PJ_SUCCESS;
-}
-
-#endif /* PJMEDIA_SOUND_IMPLEMENTATION */
-