summaryrefslogtreecommitdiff
path: root/pjmedia/src
diff options
context:
space:
mode:
authorNanang Izzuddin <nanang@teluu.com>2009-06-04 18:48:49 +0000
committerNanang Izzuddin <nanang@teluu.com>2009-06-04 18:48:49 +0000
commit0fb94a0354d037db6c49992a29b7bb97da4bdb53 (patch)
tree262daf9a93d919c7d5ef6d9d2003e0a9a8c1404b /pjmedia/src
parent79a95cd0bbe6183c90e267686f9f9e5f23735ebe (diff)
Ticket #879:
- Added pjmedia synchronizer port. - Updated affected components, i.e: sound port, AEC, conference bridge. git-svn-id: http://svn.pjsip.org/repos/pjproject/trunk@2747 74dad513-b988-da41-8d7b-12977e46ad98
Diffstat (limited to 'pjmedia/src')
-rw-r--r--pjmedia/src/pjmedia/conference.c34
-rw-r--r--pjmedia/src/pjmedia/echo_common.c81
-rw-r--r--pjmedia/src/pjmedia/sound_port.c201
-rw-r--r--pjmedia/src/pjmedia/sync_port.c274
4 files changed, 410 insertions, 180 deletions
diff --git a/pjmedia/src/pjmedia/conference.c b/pjmedia/src/pjmedia/conference.c
index fd237aa4..5a23d11d 100644
--- a/pjmedia/src/pjmedia/conference.c
+++ b/pjmedia/src/pjmedia/conference.c
@@ -236,6 +236,7 @@ struct pjmedia_conf
unsigned channel_count;/**< Number of channels (1=mono). */
unsigned samples_per_frame; /**< Samples per frame. */
unsigned bits_per_sample; /**< Bits per sample. */
+ pj_int16_t *master_port_buf;
};
@@ -457,7 +458,7 @@ static pj_status_t create_sound_port( pj_pool_t *pool,
pj_status_t status;
- status = create_pasv_port(conf, pool, &name, NULL, &conf_port);
+ status = create_conf_port(conf, pool, &name, NULL, &conf_port);
if (status != PJ_SUCCESS)
return status;
@@ -556,6 +557,10 @@ PJ_DEF(pj_status_t) pjmedia_conf_create( pj_pool_t *pool,
/* 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);
+
+ conf->master_port_buf = (pj_int16_t*)
+ pj_pool_zalloc(pool, conf->samples_per_frame<<1);
+ PJ_ASSERT_RETURN(conf->master_port_buf, PJ_ENOMEM);
pjmedia_port_info_init(&conf->master_port->info, &name, SIGNATURE,
clock_rate, channel_count, bits_per_sample,
@@ -1051,8 +1056,13 @@ PJ_DEF(pj_status_t) pjmedia_conf_disconnect_port( pjmedia_conf *conf,
dst_port->name.ptr));
/* if source port is passive port and has no listener, reset delaybuf */
- if (src_port->delay_buf && src_port->listener_cnt == 0)
- pjmedia_delay_buf_reset(src_port->delay_buf);
+ if (src_port->listener_cnt == 0) {
+ if (src_port->delay_buf)
+ pjmedia_delay_buf_reset(src_port->delay_buf);
+ if (src_port->port == NULL)
+ pjmedia_zero_samples(conf->master_port_buf,
+ conf->samples_per_frame);
+ }
}
pj_mutex_unlock(conf->mutex);
@@ -1841,10 +1851,17 @@ static pj_status_t get_frame(pjmedia_port *this_port,
}
/* Get frame from this port.
+ * For port 0 (master port), get the frame from master_port_buf.
* For passive ports, get the frame from the delay_buf.
* For other ports, get the frame from the port.
*/
- if (conf_port->delay_buf != NULL) {
+ if (conf_port->port == NULL) {
+
+ pjmedia_copy_samples((pj_int16_t*)frame->buf, conf->master_port_buf,
+ conf->samples_per_frame);
+
+ } else if (conf_port->delay_buf != NULL) {
+
pj_status_t status;
status = pjmedia_delay_buf_get(conf_port->delay_buf,
@@ -2054,16 +2071,12 @@ static pj_status_t put_frame(pjmedia_port *this_port,
{
pjmedia_conf *conf = (pjmedia_conf*) this_port->port_data.pdata;
struct conf_port *port = conf->ports[this_port->port_data.ldata];
- pj_status_t status;
/* Check for correct size. */
PJ_ASSERT_RETURN( frame->size == conf->samples_per_frame *
conf->bits_per_sample / 8,
PJMEDIA_ENCSAMPLESPFRAME);
- /* Check existance of delay_buf instance */
- PJ_ASSERT_RETURN( port->delay_buf, PJ_EBUG );
-
/* Skip if this port is muted/disabled. */
if (port->rx_setting != PJMEDIA_PORT_ENABLE) {
return PJ_SUCCESS;
@@ -2074,9 +2087,10 @@ static pj_status_t put_frame(pjmedia_port *this_port,
return PJ_SUCCESS;
}
- status = pjmedia_delay_buf_put(port->delay_buf, (pj_int16_t*)frame->buf);
+ pjmedia_copy_samples(conf->master_port_buf, (pj_int16_t*)frame->buf,
+ conf->samples_per_frame);
- return status;
+ return PJ_SUCCESS;
}
#endif
diff --git a/pjmedia/src/pjmedia/echo_common.c b/pjmedia/src/pjmedia/echo_common.c
index 920988d2..2b016b9f 100644
--- a/pjmedia/src/pjmedia/echo_common.c
+++ b/pjmedia/src/pjmedia/echo_common.c
@@ -19,7 +19,6 @@
*/
#include <pjmedia/echo.h>
-#include <pjmedia/delaybuf.h>
#include <pjmedia/errno.h>
#include <pj/assert.h>
#include <pj/list.h>
@@ -50,8 +49,6 @@ struct pjmedia_echo_state
unsigned lat_buf_cnt; /* Actual number of frames in lat_buf */
struct frame lat_buf; /* Frame queue for delayed playback */
struct frame lat_free; /* Free frame list. */
-
- pjmedia_delay_buf *delay_buf;
};
@@ -146,6 +143,7 @@ PJ_DEF(pj_status_t) pjmedia_echo_create2(pj_pool_t *pool,
unsigned ptime;
pjmedia_echo_state *ec;
pj_status_t status;
+ unsigned i;
/* Create new pool and instantiate and init the EC */
pool = pj_pool_create(pool->factory, "ec%p", 256, 256, NULL);
@@ -191,32 +189,17 @@ PJ_DEF(pj_status_t) pjmedia_echo_create2(pj_pool_t *pool,
/* Create latency buffers */
ptime = samples_per_frame * 1000 / clock_rate;
- if (latency_ms == 0) {
- /* Give at least one frame delay to simplify programming */
+ /* Give at least one frame delay to simplify programming */
+ if (latency_ms < ptime) {
latency_ms = ptime;
}
ec->lat_target_cnt = latency_ms / ptime;
- if (ec->lat_target_cnt != 0) {
- unsigned i;
- for (i=0; i < ec->lat_target_cnt; ++i) {
- struct frame *frm;
-
- frm = (struct frame*) pj_pool_alloc(pool, (samples_per_frame<<1) +
- sizeof(struct frame));
- pj_list_push_back(&ec->lat_free, frm);
- }
- } else {
- ec->lat_ready = PJ_TRUE;
- }
+ for (i=0; i < ec->lat_target_cnt; ++i) {
+ struct frame *frm;
- /* Create delay buffer to compensate drifts */
- status = pjmedia_delay_buf_create(ec->pool, ec->obj_name, clock_rate,
- samples_per_frame, channel_count,
- (PJMEDIA_SOUND_BUFFER_COUNT+1) * ptime,
- 0, &ec->delay_buf);
- if (status != PJ_SUCCESS) {
- pj_pool_release(pool);
- return status;
+ frm = (struct frame*) pj_pool_alloc(pool, (samples_per_frame<<1) +
+ sizeof(struct frame));
+ pj_list_push_back(&ec->lat_free, frm);
}
PJ_LOG(4,(ec->obj_name,
@@ -240,11 +223,6 @@ PJ_DEF(pj_status_t) pjmedia_echo_destroy(pjmedia_echo_state *echo )
{
(*echo->op->ec_destroy)(echo->state);
- if (echo->delay_buf) {
- pjmedia_delay_buf_destroy(echo->delay_buf);
- echo->delay_buf = NULL;
- }
-
pj_pool_release(echo->pool);
return PJ_SUCCESS;
}
@@ -262,7 +240,6 @@ PJ_DEF(pj_status_t) pjmedia_echo_reset(pjmedia_echo_state *echo )
pj_list_push_back(&echo->lat_free, frm);
}
echo->lat_ready = PJ_FALSE;
- pjmedia_delay_buf_reset(echo->delay_buf);
echo->op->ec_reset(echo->state);
return PJ_SUCCESS;
}
@@ -274,32 +251,27 @@ PJ_DEF(pj_status_t) pjmedia_echo_reset(pjmedia_echo_state *echo )
PJ_DEF(pj_status_t) pjmedia_echo_playback( pjmedia_echo_state *echo,
pj_int16_t *play_frm )
{
- if (!echo->lat_ready) {
+ struct frame *frm;
+
+ if (echo->lat_ready) {
+ frm = echo->lat_buf.next;
+ pj_list_erase(frm);
+ } else {
/* We've not built enough latency in the buffer, so put this frame
* in the latency buffer list.
*/
- struct frame *frm;
-
+ frm = echo->lat_free.prev;
+ pj_list_erase(frm);
if (pj_list_empty(&echo->lat_free)) {
echo->lat_ready = PJ_TRUE;
PJ_LOG(5,(echo->obj_name, "Latency bufferring complete"));
- pjmedia_delay_buf_put(echo->delay_buf, play_frm);
- return PJ_SUCCESS;
}
-
- frm = echo->lat_free.prev;
- pj_list_erase(frm);
-
- pjmedia_copy_samples(frm->buf, play_frm, echo->samples_per_frame);
- pj_list_push_back(&echo->lat_buf, frm);
-
- } else {
- /* Latency buffer is ready (full), so we put this frame in the
- * delay buffer.
- */
- pjmedia_delay_buf_put(echo->delay_buf, play_frm);
}
+ /* Put the incoming frame into the end of latency buffer */
+ pjmedia_copy_samples(frm->buf, play_frm, echo->samples_per_frame);
+ pj_list_push_back(&echo->lat_buf, frm);
+
return PJ_SUCCESS;
}
@@ -313,7 +285,7 @@ PJ_DEF(pj_status_t) pjmedia_echo_capture( pjmedia_echo_state *echo,
unsigned options )
{
struct frame *oldest_frm;
- pj_status_t status, rc;
+ pj_status_t status;
if (!echo->lat_ready) {
/* Prefetching to fill in the desired latency */
@@ -323,22 +295,11 @@ PJ_DEF(pj_status_t) pjmedia_echo_capture( pjmedia_echo_state *echo,
/* Retrieve oldest frame from the latency buffer */
oldest_frm = echo->lat_buf.next;
- pj_list_erase(oldest_frm);
/* Cancel echo using this reference frame */
status = pjmedia_echo_cancel(echo, rec_frm, oldest_frm->buf,
options, NULL);
- /* Move one frame from delay buffer to the latency buffer. */
- rc = pjmedia_delay_buf_get(echo->delay_buf, oldest_frm->buf);
- if (rc != PJ_SUCCESS) {
- /* Ooops.. no frame! */
- PJ_LOG(5,(echo->obj_name,
- "No frame from delay buffer. This will upset EC later"));
- pjmedia_zero_samples(oldest_frm->buf, echo->samples_per_frame);
- }
- pj_list_push_back(&echo->lat_buf, oldest_frm);
-
return status;
}
diff --git a/pjmedia/src/pjmedia/sound_port.c b/pjmedia/src/pjmedia/sound_port.c
index 70c71215..db554b48 100644
--- a/pjmedia/src/pjmedia/sound_port.c
+++ b/pjmedia/src/pjmedia/sound_port.c
@@ -19,23 +19,21 @@
*/
#include <pjmedia/sound_port.h>
#include <pjmedia/alaw_ulaw.h>
-#include <pjmedia/delaybuf.h>
-#include <pjmedia/echo.h>
+#include <pjmedia/echo_port.h>
#include <pjmedia/errno.h>
+#include <pjmedia/sync_port.h>
#include <pj/assert.h>
#include <pj/log.h>
#include <pj/rand.h>
#include <pj/string.h> /* pj_memset() */
#define AEC_TAIL 128 /* default AEC length in ms */
-#define AEC_SUSPEND_LIMIT 5 /* seconds of no activity */
#define THIS_FILE "sound_port.c"
-//#define TEST_OVERFLOW_UNDERFLOW
-
struct pjmedia_snd_port
{
+ pj_pool_t *pool;
int rec_id;
int play_id;
pj_uint32_t aud_caps;
@@ -43,6 +41,8 @@ struct pjmedia_snd_port
pjmedia_aud_stream *aud_stream;
pjmedia_dir dir;
pjmedia_port *port;
+ pjmedia_port *dn_port;
+ pjmedia_port *sync_port;
unsigned clock_rate;
unsigned channel_count;
@@ -50,12 +50,9 @@ struct pjmedia_snd_port
unsigned bits_per_sample;
/* software ec */
- pjmedia_echo_state *ec_state;
+ pjmedia_port *echo_port;
unsigned ec_options;
unsigned ec_tail_len;
- pj_bool_t ec_suspended;
- unsigned ec_suspend_count;
- unsigned ec_suspend_limit;
};
/*
@@ -69,7 +66,7 @@ static pj_status_t play_cb(void *user_data, pjmedia_frame *frame)
const unsigned required_size = frame->size;
pj_status_t status;
- port = snd_port->port;
+ port = snd_port->dn_port;
if (port == NULL)
goto no_frame;
@@ -83,16 +80,6 @@ static pj_status_t play_cb(void *user_data, pjmedia_frame *frame)
/* Must supply the required samples */
pj_assert(frame->size == required_size);
- if (snd_port->ec_state) {
- if (snd_port->ec_suspended) {
- snd_port->ec_suspended = PJ_FALSE;
- //pjmedia_echo_state_reset(snd_port->ec_state);
- PJ_LOG(4,(THIS_FILE, "EC activated"));
- }
- snd_port->ec_suspend_count = 0;
- pjmedia_echo_playback(snd_port->ec_state, (pj_int16_t*)frame->buf);
- }
-
return PJ_SUCCESS;
@@ -101,18 +88,6 @@ no_frame:
frame->size = required_size;
pj_bzero(frame->buf, frame->size);
- if (snd_port->ec_state && !snd_port->ec_suspended) {
- ++snd_port->ec_suspend_count;
- if (snd_port->ec_suspend_count > snd_port->ec_suspend_limit) {
- snd_port->ec_suspended = PJ_TRUE;
- PJ_LOG(4,(THIS_FILE, "EC suspended because of inactivity"));
- }
- if (snd_port->ec_state) {
- /* To maintain correct delay in EC */
- pjmedia_echo_playback(snd_port->ec_state, (pj_int16_t*)frame->buf);
- }
- }
-
return PJ_SUCCESS;
}
@@ -126,15 +101,10 @@ 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;
- port = snd_port->port;
+ port = snd_port->dn_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*) frame->buf, 0);
- }
-
pjmedia_port_put_frame(port, frame);
return PJ_SUCCESS;
@@ -182,12 +152,11 @@ static pj_status_t rec_cb_ext(void *user_data, pjmedia_frame *frame)
* Start the sound stream.
* This may be called even when the sound stream has already been started.
*/
-static pj_status_t start_sound_device( pj_pool_t *pool,
- pjmedia_snd_port *snd_port )
+static pj_status_t start_sound_device( pjmedia_snd_port *snd_port )
{
pjmedia_aud_rec_cb snd_rec_cb;
pjmedia_aud_play_cb snd_play_cb;
- pjmedia_aud_param param_copy;
+ pjmedia_aud_param *param;
pj_status_t status;
/* Check if sound has been started. */
@@ -199,12 +168,13 @@ static pj_status_t start_sound_device( pj_pool_t *pool,
snd_port->dir == PJMEDIA_DIR_CAPTURE_PLAYBACK,
PJ_EBUG);
+ param = &snd_port->aud_param;
+
/* Get device caps */
- if (snd_port->aud_param.dir & PJMEDIA_DIR_CAPTURE) {
+ if (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);
+ status = pjmedia_aud_dev_get_info(param->rec_id, &dev_info);
if (status != PJ_SUCCESS)
return status;
@@ -213,24 +183,8 @@ static pj_status_t start_sound_device( pj_pool_t *pool,
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) {
+ if (param->ext_fmt.id == PJMEDIA_FORMAT_L16) {
snd_rec_cb = &rec_cb;
snd_play_cb = &play_cb;
} else {
@@ -239,7 +193,7 @@ static pj_status_t start_sound_device( pj_pool_t *pool,
}
/* Open the device */
- status = pjmedia_aud_stream_create(&param_copy,
+ status = pjmedia_aud_stream_create(param,
snd_rec_cb,
snd_play_cb,
snd_port,
@@ -248,34 +202,6 @@ static pj_status_t start_sound_device( pj_pool_t *pool,
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);
-
- /* Create software EC if parameter specifies EC but device
- * doesn't support EC. Only do this if the format is PCM!
- */
- 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)
- {
- 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) {
- pjmedia_aud_stream_destroy(snd_port->aud_stream);
- snd_port->aud_stream = NULL;
- return status;
- }
- }
-
/* Start sound stream. */
status = pjmedia_aud_stream_start(snd_port->aud_stream);
if (status != PJ_SUCCESS) {
@@ -301,12 +227,6 @@ static pj_status_t stop_sound_device( pjmedia_snd_port *snd_port )
snd_port->aud_stream = NULL;
}
- /* Destroy AEC */
- if (snd_port->ec_state) {
- pjmedia_echo_destroy(snd_port->ec_state);
- snd_port->ec_state = NULL;
- }
-
return PJ_SUCCESS;
}
@@ -420,9 +340,11 @@ PJ_DEF(pj_status_t) pjmedia_snd_port_create2(pj_pool_t *pool,
PJ_ASSERT_RETURN(pool && prm && p_port, PJ_EINVAL);
+ pool = pj_pool_create(pool->factory, pool->obj_name, 256, 256, NULL);
snd_port = PJ_POOL_ZALLOC_T(pool, pjmedia_snd_port);
PJ_ASSERT_RETURN(snd_port, PJ_ENOMEM);
+ snd_port->pool = pool;
snd_port->dir = prm->dir;
snd_port->rec_id = prm->rec_id;
snd_port->play_id = prm->play_id;
@@ -437,7 +359,7 @@ PJ_DEF(pj_status_t) pjmedia_snd_port_create2(pj_pool_t *pool,
* If there's no port connected, the sound callback will return
* empty signal.
*/
- status = start_sound_device( pool, snd_port );
+ status = start_sound_device( snd_port );
if (status != PJ_SUCCESS) {
pjmedia_snd_port_destroy(snd_port);
return status;
@@ -453,9 +375,19 @@ PJ_DEF(pj_status_t) pjmedia_snd_port_create2(pj_pool_t *pool,
*/
PJ_DEF(pj_status_t) pjmedia_snd_port_destroy(pjmedia_snd_port *snd_port)
{
+ pj_status_t status;
+
PJ_ASSERT_RETURN(snd_port, PJ_EINVAL);
- return stop_sound_device(snd_port);
+ /* Stop sound port */
+ status = stop_sound_device(snd_port);
+
+ /* Disconnect sound port */
+ pjmedia_snd_port_disconnect(snd_port);
+
+ pj_pool_release(snd_port->pool);
+
+ return status;
}
@@ -551,9 +483,9 @@ PJ_DEF(pj_status_t) pjmedia_snd_port_set_ec( pjmedia_snd_port *snd_port,
PJ_EINVALIDOP);
/* Destroy AEC */
- if (snd_port->ec_state) {
- pjmedia_echo_destroy(snd_port->ec_state);
- snd_port->ec_state = NULL;
+ if (snd_port->echo_port) {
+ pjmedia_port_destroy(snd_port->echo_port);
+ snd_port->echo_port = NULL;
}
if (tail_ms != 0) {
@@ -564,15 +496,11 @@ PJ_DEF(pj_status_t) pjmedia_snd_port_set_ec( pjmedia_snd_port *snd_port,
//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);
+ status = pjmedia_echo_port_create(pool, snd_port->port,
+ tail_ms, delay_ms,
+ options, &snd_port->echo_port);
if (status != PJ_SUCCESS)
- snd_port->ec_state = NULL;
- else
- snd_port->ec_suspended = PJ_FALSE;
+ snd_port->echo_port = NULL;
} else {
PJ_LOG(4,(THIS_FILE, "Echo canceller is now disabled in the "
"sound port"));
@@ -622,7 +550,7 @@ PJ_DEF(pj_status_t) pjmedia_snd_port_get_ec_tail( pjmedia_snd_port *snd_port,
} else {
/* We use software EC */
- *p_length = snd_port->ec_state ? snd_port->ec_tail_len : 0;
+ *p_length = snd_port->echo_port ? snd_port->ec_tail_len : 0;
}
return PJ_SUCCESS;
}
@@ -635,6 +563,9 @@ PJ_DEF(pj_status_t) pjmedia_snd_port_connect( pjmedia_snd_port *snd_port,
pjmedia_port *port)
{
pjmedia_port_info *pinfo;
+ pjmedia_aud_param *param;
+ pjmedia_sync_param sync_param;
+ pj_status_t status;
PJ_ASSERT_RETURN(snd_port && port, PJ_EINVAL);
@@ -656,6 +587,41 @@ PJ_DEF(pj_status_t) pjmedia_snd_port_connect( pjmedia_snd_port *snd_port,
/* Port is okay. */
snd_port->port = port;
+
+ /* Create software EC if parameter specifies EC but device
+ * doesn't support EC. Only do this if the format is PCM!
+ */
+ param = &snd_port->aud_param;
+ if ((param->flags & PJMEDIA_AUD_DEV_CAP_EC) &&
+ (snd_port->aud_caps & PJMEDIA_AUD_DEV_CAP_EC)==0 &&
+ param->ext_fmt.id == PJMEDIA_FORMAT_PCM)
+ {
+ if ((param->flags & PJMEDIA_AUD_DEV_CAP_EC_TAIL)==0) {
+ param->flags |= PJMEDIA_AUD_DEV_CAP_EC_TAIL;
+ param->ec_tail_ms = AEC_TAIL;
+ PJ_LOG(4,(THIS_FILE, "AEC tail is set to default %u ms",
+ param->ec_tail_ms));
+ }
+
+ status = pjmedia_snd_port_set_ec(snd_port, snd_port->pool,
+ param->ec_tail_ms, 0);
+ if (status != PJ_SUCCESS)
+ return status;
+ }
+
+ /* Create sync port */
+ pj_bzero(&sync_param, sizeof(sync_param));
+ sync_param.options = PJMEDIA_SYNC_DONT_DESTROY_DN;
+ status = pjmedia_sync_port_create(snd_port->pool,
+ (snd_port->echo_port?
+ snd_port->echo_port:snd_port->port),
+ &sync_param, &snd_port->sync_port);
+ if (status != PJ_SUCCESS)
+ return status;
+
+ /* Update down port of sound port */
+ snd_port->dn_port = snd_port->sync_port;
+
return PJ_SUCCESS;
}
@@ -678,6 +644,21 @@ PJ_DEF(pj_status_t) pjmedia_snd_port_disconnect(pjmedia_snd_port *snd_port)
PJ_ASSERT_RETURN(snd_port, PJ_EINVAL);
snd_port->port = NULL;
+ snd_port->dn_port = NULL;
+
+ /* Destroy sync port */
+ if (snd_port->sync_port) {
+ pjmedia_port_destroy(snd_port->sync_port);
+ snd_port->sync_port = NULL;
+ }
+
+ /* Destroy EC port */
+ if (snd_port->echo_port) {
+ pjmedia_port_destroy(snd_port->echo_port);
+ snd_port->echo_port = NULL;
+ snd_port->ec_tail_len = 0;
+ snd_port->ec_options = 0;
+ }
return PJ_SUCCESS;
}
diff --git a/pjmedia/src/pjmedia/sync_port.c b/pjmedia/src/pjmedia/sync_port.c
new file mode 100644
index 00000000..1508df22
--- /dev/null
+++ b/pjmedia/src/pjmedia/sync_port.c
@@ -0,0 +1,274 @@
+/* $Id$ */
+/*
+ * Copyright (C) 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
+ */
+
+#include <pjmedia/sync_port.h>
+#include <pjmedia/clock.h>
+#include <pjmedia/delaybuf.h>
+#include <pjmedia/errno.h>
+#include <pj/assert.h>
+#include <pj/log.h>
+#include <pj/pool.h>
+
+#define THIS_FILE "sync_port.c"
+#define SIGNATURE PJMEDIA_PORT_SIGNATURE('S', 'Y', 'N', 'C')
+
+typedef struct pjmedia_sync_port
+{
+ pjmedia_port base;
+ pjmedia_sync_param param;
+ pjmedia_port *dn_port;
+ pj_int16_t *framebuf;
+ pjmedia_clock *clock;
+ pjmedia_delay_buf *delay_buf;
+ pjmedia_delay_buf *delay_buf2;
+
+} pjmedia_sync_port;
+
+
+/*
+ * CALLBACKS
+ */
+
+/* Clock callback */
+static void clock_cb(const pj_timestamp *ts, void *user_data)
+{
+ pjmedia_sync_port *port = (pjmedia_sync_port*)user_data;
+ pjmedia_frame f;
+ pj_status_t status;
+
+ /* call dn_port.put_frame() */
+ pj_bzero(&f, sizeof(f));
+ f.buf = port->framebuf;
+ f.size = port->base.info.samples_per_frame << 1;
+ f.type = PJMEDIA_TYPE_AUDIO;
+ f.timestamp = *ts;
+
+ status = pjmedia_delay_buf_get(port->delay_buf, (pj_int16_t*)f.buf);
+ if (status == PJ_SUCCESS) {
+ pjmedia_port_put_frame(port->dn_port, &f);
+ }
+
+ /* call dn_port.get_frame() */
+ pj_bzero(&f, sizeof(f));
+ f.buf = port->framebuf;
+ f.size = port->base.info.samples_per_frame << 1;
+ f.type = PJMEDIA_TYPE_AUDIO;
+ f.timestamp = *ts;
+
+ status = pjmedia_port_get_frame(port->dn_port, &f);
+ if (status != PJ_SUCCESS || f.type != PJMEDIA_FRAME_TYPE_AUDIO) {
+ pjmedia_zero_samples((pj_int16_t*)f.buf,
+ port->base.info.samples_per_frame);
+ }
+ pjmedia_delay_buf_put(port->delay_buf2, (pj_int16_t*)f.buf);
+}
+
+/* Get frame (from this port) when port is using own clock. */
+static pj_status_t get_frame_clock(pjmedia_port *this_port,
+ pjmedia_frame *frame)
+{
+ pjmedia_sync_port *port = (pjmedia_sync_port*)this_port;
+ pj_status_t status;
+
+ PJ_ASSERT_RETURN(port, PJ_EINVAL);
+
+ /* get frame from delay buf */
+ status = pjmedia_delay_buf_get(port->delay_buf2, (pj_int16_t*)frame->buf);
+ if (status != PJ_SUCCESS) {
+ return status;
+ }
+
+ frame->size = this_port->info.samples_per_frame << 1;
+ frame->type = PJMEDIA_TYPE_AUDIO;
+
+ return PJ_SUCCESS;
+}
+
+/* Get frame (from this port). */
+static pj_status_t get_frame(pjmedia_port *this_port,
+ pjmedia_frame *frame)
+{
+ pjmedia_sync_port *port = (pjmedia_sync_port*)this_port;
+ pjmedia_frame f;
+ pj_status_t status;
+
+ PJ_ASSERT_RETURN(port, PJ_EINVAL);
+
+ /* get frame */
+ status = pjmedia_port_get_frame(port->dn_port, frame);
+ if (status != PJ_SUCCESS)
+ return status;
+
+ /* put frame from delay buf */
+ pj_bzero(&f, sizeof(f));
+ f.buf = port->framebuf;
+ f.size = this_port->info.samples_per_frame << 1;
+ f.type = PJMEDIA_TYPE_AUDIO;
+ f.timestamp = frame->timestamp;
+
+ status = pjmedia_delay_buf_get(port->delay_buf, (pj_int16_t*)f.buf);
+ if (status != PJ_SUCCESS)
+ return status;
+
+ status = pjmedia_port_put_frame(port->dn_port, &f);
+ if (status != PJ_SUCCESS)
+ return status;
+
+ return PJ_SUCCESS;
+}
+
+/* Put frame (to this port). */
+static pj_status_t put_frame(pjmedia_port *this_port,
+ const pjmedia_frame *frame)
+{
+ pjmedia_sync_port *port = (pjmedia_sync_port*)this_port;
+
+ PJ_ASSERT_RETURN(port, PJ_EINVAL);
+
+ /* put frame to delay buf */
+ if (frame->type != PJMEDIA_FRAME_TYPE_AUDIO) {
+ pjmedia_zero_samples((pj_int16_t*)frame->buf,
+ this_port->info.samples_per_frame);
+ }
+
+ return pjmedia_delay_buf_put(port->delay_buf, (pj_int16_t*)frame->buf);
+}
+
+/* Destroy the port. */
+static pj_status_t on_destroy(pjmedia_port *this_port)
+{
+ pjmedia_sync_port *port = (pjmedia_sync_port*)this_port;
+
+ PJ_ASSERT_RETURN(port, PJ_EINVAL);
+
+ if (port->clock) {
+ pjmedia_clock_stop(port->clock);
+ pjmedia_clock_destroy(port->clock);
+ port->clock = NULL;
+ }
+
+ if (port->delay_buf) {
+ pjmedia_delay_buf_destroy(port->delay_buf);
+ port->delay_buf = NULL;
+ }
+
+ if (port->delay_buf2) {
+ pjmedia_delay_buf_destroy(port->delay_buf2);
+ port->delay_buf2 = NULL;
+ }
+
+ if (port->dn_port &&
+ (port->param.options & PJMEDIA_SYNC_DONT_DESTROY_DN) == 0)
+ {
+ pjmedia_port_destroy(port->dn_port);
+ }
+
+ return PJ_SUCCESS;
+}
+
+
+/*
+ * API FUNCTIONS
+ */
+
+/* Create the sync port. */
+PJ_DEF(pj_status_t) pjmedia_sync_port_create(pj_pool_t *pool,
+ pjmedia_port *dn_port,
+ const pjmedia_sync_param *param,
+ pjmedia_port **p_port )
+{
+ pjmedia_sync_port *sync;
+ pj_str_t name;
+ unsigned ptime;
+ pj_status_t status;
+
+ PJ_ASSERT_RETURN(pool && dn_port && p_port, PJ_EINVAL);
+ PJ_ASSERT_RETURN(dn_port->info.bits_per_sample==16, PJ_EINVAL);
+
+ sync = PJ_POOL_ZALLOC_T(pool, pjmedia_sync_port);
+ sync->framebuf = (pj_int16_t*)
+ pj_pool_zalloc(pool, dn_port->info.samples_per_frame<<1);
+
+ /* Init port */
+ if (param)
+ sync->param = *param;
+ sync->dn_port = dn_port;
+
+ /* Init port info */
+ name = pj_str(pool->obj_name);
+ pjmedia_port_info_init(&sync->base.info,
+ &name, SIGNATURE,
+ dn_port->info.clock_rate,
+ dn_port->info.channel_count,
+ dn_port->info.bits_per_sample,
+ dn_port->info.samples_per_frame);
+
+ /* Init port op */
+ sync->base.get_frame = &get_frame;
+ sync->base.put_frame = &put_frame;
+ sync->base.on_destroy = &on_destroy;
+
+ /* Create delay buffer to compensate drifts */
+ ptime = dn_port->info.samples_per_frame * 1000 /
+ dn_port->info.channel_count /
+ dn_port->info.clock_rate;
+ status = pjmedia_delay_buf_create(pool, name.ptr,
+ dn_port->info.clock_rate,
+ dn_port->info.samples_per_frame,
+ dn_port->info.channel_count,
+ PJMEDIA_SOUND_BUFFER_COUNT * ptime,
+ 0,
+ &sync->delay_buf);
+ if (status != PJ_SUCCESS)
+ goto on_error;
+
+ /* Create clock if specified */
+ if (sync->param.options & PJMEDIA_SYNC_USE_EXT_CLOCK) {
+ status = pjmedia_delay_buf_create(pool, name.ptr,
+ dn_port->info.clock_rate,
+ dn_port->info.samples_per_frame,
+ dn_port->info.channel_count,
+ PJMEDIA_SOUND_BUFFER_COUNT * ptime,
+ 0,
+ &sync->delay_buf2);
+ if (status != PJ_SUCCESS)
+ goto on_error;
+
+ status = pjmedia_clock_create(pool,
+ dn_port->info.clock_rate,
+ dn_port->info.channel_count,
+ dn_port->info.samples_per_frame,
+ 0,
+ &clock_cb,
+ sync,
+ &sync->clock);
+ if (status != PJ_SUCCESS)
+ goto on_error;
+
+ sync->base.get_frame = &get_frame_clock;
+ }
+
+ /* Done */
+ *p_port = &sync->base;
+ return PJ_SUCCESS;
+
+on_error:
+ on_destroy(&sync->base);
+ return status;
+}