summaryrefslogtreecommitdiff
path: root/pjmedia/src
diff options
context:
space:
mode:
authorBenny Prijono <bennylp@teluu.com>2008-01-04 18:19:40 +0000
committerBenny Prijono <bennylp@teluu.com>2008-01-04 18:19:40 +0000
commitc828a3b26763bdfae04032c4962098e8343b8d92 (patch)
tree51e49c3389c48e965be396d41e902a21c1fb0738 /pjmedia/src
parent16ada6b2083d10fac21bdf022fe4231dc9009421 (diff)
Ticket #438 (Workaround for frame bursts from audio devices): added delay buffer implementation in pjmedia
git-svn-id: http://svn.pjsip.org/repos/pjproject/trunk@1664 74dad513-b988-da41-8d7b-12977e46ad98
Diffstat (limited to 'pjmedia/src')
-rw-r--r--pjmedia/src/pjmedia/delaybuf.c231
-rw-r--r--pjmedia/src/pjmedia/sound_port.c30
2 files changed, 261 insertions, 0 deletions
diff --git a/pjmedia/src/pjmedia/delaybuf.c b/pjmedia/src/pjmedia/delaybuf.c
new file mode 100644
index 00000000..8ea95cb6
--- /dev/null
+++ b/pjmedia/src/pjmedia/delaybuf.c
@@ -0,0 +1,231 @@
+/* $Id$ */
+/*
+ * Copyright (C) 2003-2007 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/delaybuf.h>
+#include <pjmedia/errno.h>
+#include <pj/assert.h>
+#include <pj/log.h>
+#include <pj/pool.h>
+
+enum state
+{
+ STATE_WAITING,
+ STATE_LEARNING,
+ STATE_RUNNING
+};
+
+enum OP
+{
+ OP_PUT,
+ OP_GET,
+ OP_UNDEFINED
+};
+
+/* The following macros represent cycles of test. */
+/* Since there are two operations performed (get & put), */
+/* these macros value must be minimum 2 and should be even number */
+#define WAITING_COUNT 8
+#define LEARN_COUNT 8
+
+struct pjmedia_delay_buf
+{
+ char obj_name[PJ_MAX_OBJ_NAME];
+
+ pj_int16_t *frame_buf;
+ unsigned put_pos;
+ unsigned get_pos;
+ unsigned buf_cnt;
+
+ unsigned samples_per_frame;
+ unsigned max_cnt;
+ enum state state;
+
+ struct {
+ unsigned level;
+ } op[2];
+
+ enum OP last_op;
+ unsigned max_level;
+ unsigned state_count;
+};
+
+PJ_DEF(pj_status_t) pjmedia_delay_buf_create( pj_pool_t *pool,
+ const char *name,
+ unsigned samples_per_frame,
+ unsigned max_cnt,
+ int delay,
+ pjmedia_delay_buf **p_b)
+{
+ pjmedia_delay_buf *b;
+
+ PJ_ASSERT_RETURN(pool && samples_per_frame && max_cnt && p_b, PJ_EINVAL);
+
+ if (!name) {
+ name = "delaybuf";
+ }
+
+ b = PJ_POOL_ZALLOC_T(pool, pjmedia_delay_buf);
+
+ pj_ansi_strncpy(b->obj_name, name, PJ_MAX_OBJ_NAME-1);
+ b->frame_buf = pj_pool_zalloc(pool, samples_per_frame * max_cnt *
+ sizeof(pj_int16_t));
+ b->samples_per_frame = samples_per_frame;
+ b->max_cnt = max_cnt;
+
+ if (delay >= 0) {
+ PJ_ASSERT_RETURN(delay <= (int)max_cnt, PJ_EINVAL);
+ b->max_level = delay;
+ b->state = STATE_RUNNING;
+ } else {
+ b->last_op = OP_UNDEFINED;
+ b->state = STATE_WAITING;
+ b->buf_cnt = 0;
+ }
+
+ *p_b = b;
+
+ PJ_LOG(5,(b->obj_name,"Delay buffer created"));
+
+ return PJ_SUCCESS;
+}
+
+static void update(pjmedia_delay_buf *b, enum OP op)
+{
+ enum OP other = !op;
+
+ switch (b->state) {
+ case STATE_RUNNING:
+ break;
+ case STATE_WAITING:
+ ++b->op[op].level;
+ if (b->op[other].level != 0) {
+ ++b->state_count;
+ if (b->state_count == WAITING_COUNT) {
+ /* Start learning */
+ pjmedia_delay_buf_learn(b);
+ }
+ }
+ b->last_op = op;
+ break;
+ case STATE_LEARNING:
+ ++b->op[op].level;
+ if (b->last_op == other) {
+ unsigned last_level = b->op[other].level;
+ if (last_level > b->max_level)
+ b->max_level = last_level;
+ b->op[other].level = 0;
+ b->state_count++;
+ if (b->state_count == LEARN_COUNT) {
+ /* give ONE frame compensation */
+ b->max_level += 1;
+
+ PJ_LOG(5,(b->obj_name,"Delay buffer start running, level=%u",
+ b->max_level));
+
+ /* buffer not enough! */
+ if (b->max_level > b->max_cnt) {
+ b->max_level = b->max_cnt;
+ PJ_LOG(2,(b->obj_name,"Delay buffer %s learning result " \
+ "exceeds the maximum delay allowed",
+ b->max_level));
+ }
+
+ b->state = STATE_RUNNING;
+ }
+ }
+ b->last_op = op;
+ break;
+ }
+
+
+}
+
+PJ_DEF(pj_status_t) pjmedia_delay_buf_put(pjmedia_delay_buf *b,
+ pj_int16_t frame[])
+{
+ update(b, OP_PUT);
+
+ if (b->state != STATE_RUNNING)
+ return PJ_EPENDING;
+
+ pj_memcpy(&b->frame_buf[b->put_pos * b->samples_per_frame], frame,
+ b->samples_per_frame*sizeof(pj_int16_t));
+
+ /* overflow case */
+ if (b->put_pos == b->get_pos && b->buf_cnt) {
+ if (++b->get_pos == b->max_level)
+ b->get_pos = 0;
+
+ b->put_pos = b->get_pos;
+
+ PJ_LOG(5,(b->obj_name,"Warning: buffer overflow"));
+
+ return PJ_ETOOMANY;
+ }
+
+ ++b->buf_cnt;
+
+ if (++b->put_pos == b->max_level)
+ b->put_pos = 0;
+
+ return PJ_SUCCESS;
+}
+
+PJ_DEF(pj_status_t) pjmedia_delay_buf_get(pjmedia_delay_buf *b,
+ pj_int16_t frame[])
+{
+ update(b, OP_GET);
+
+ if (b->state != STATE_RUNNING || !b->buf_cnt) {
+ if (b->state == STATE_RUNNING)
+ PJ_LOG(5,(b->obj_name,"Warning: delay buffer empty"));
+
+ pj_bzero(frame, b->samples_per_frame*sizeof(pj_int16_t));
+ return PJ_EPENDING;
+ }
+
+ pj_memcpy(frame, &b->frame_buf[b->get_pos * b->samples_per_frame],
+ b->samples_per_frame*sizeof(pj_int16_t));
+
+ if (++b->get_pos == b->max_level)
+ b->get_pos = 0;
+
+ --b->buf_cnt;
+
+ return PJ_SUCCESS;
+}
+
+PJ_DECL(pj_status_t) pjmedia_delay_buf_learn(pjmedia_delay_buf *b)
+{
+ PJ_ASSERT_RETURN(b, PJ_EINVAL);
+
+ b->last_op = OP_UNDEFINED;
+ b->op[OP_GET].level = b->op[OP_PUT].level = 0;
+ b->state = STATE_LEARNING;
+ b->state_count = 0;
+ b->max_level = 0;
+
+ /* clean up buffer */
+ b->buf_cnt = 0;
+ b->put_pos = b->get_pos = 0;
+
+ PJ_LOG(5,(b->obj_name,"Delay buffer start learning"));
+
+ return PJ_SUCCESS;
+}
diff --git a/pjmedia/src/pjmedia/sound_port.c b/pjmedia/src/pjmedia/sound_port.c
index 5fbbaab5..50ea18c6 100644
--- a/pjmedia/src/pjmedia/sound_port.c
+++ b/pjmedia/src/pjmedia/sound_port.c
@@ -17,6 +17,7 @@
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include <pjmedia/sound_port.h>
+#include <pjmedia/delaybuf.h>
#include <pjmedia/echo.h>
#include <pjmedia/errno.h>
#include <pjmedia/plc.h>
@@ -62,6 +63,10 @@ struct pjmedia_snd_port
unsigned channel_count;
unsigned samples_per_frame;
unsigned bits_per_sample;
+
+#if PJMEDIA_SOUND_USE_DELAYBUF
+ pjmedia_delay_buf *delay_buf;
+#endif
};
/*
@@ -93,6 +98,15 @@ static pj_status_t play_cb(/* in */ void *user_data,
frame.timestamp.u32.hi = 0;
frame.timestamp.u32.lo = timestamp;
+#if PJMEDIA_SOUND_USE_DELAYBUF
+ status = pjmedia_delay_buf_get(snd_port->delay_buf, (pj_int16_t*)output);
+ if (status != PJ_SUCCESS) {
+ pj_bzero(output, size);
+ }
+
+ pjmedia_port_put_frame(port, &frame);
+#endif
+
status = pjmedia_port_get_frame(port, &frame);
if (status != PJ_SUCCESS)
goto no_frame;
@@ -185,12 +199,19 @@ static pj_status_t rec_cb(/* in */ void *user_data,
pjmedia_echo_capture(snd_port->ec_state, (pj_int16_t*) input, 0);
}
+#if PJMEDIA_SOUND_USE_DELAYBUF
+ PJ_UNUSED_ARG(size);
+ PJ_UNUSED_ARG(timestamp);
+ PJ_UNUSED_ARG(frame);
+ 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);
+#endif
return PJ_SUCCESS;
}
@@ -324,6 +345,7 @@ PJ_DEF(pj_status_t) pjmedia_snd_port_create( pj_pool_t *pool,
pjmedia_snd_port **p_port)
{
pjmedia_snd_port *snd_port;
+ pj_status_t status;
PJ_ASSERT_RETURN(pool && p_port, PJ_EINVAL);
@@ -338,6 +360,14 @@ PJ_DEF(pj_status_t) pjmedia_snd_port_create( pj_pool_t *pool,
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
+ status = pjmedia_delay_buf_create(pool, "snd_buff", samples_per_frame,
+ 16, &snd_port->delay_buf);
+ PJ_ASSERT_RETURN(status == PJ_SUCCESS, status);
+#else
+ PJ_UNUSED_ARG(status);
+#endif
*p_port = snd_port;