summaryrefslogtreecommitdiff
path: root/pjmedia/src/pjmedia/delaybuf.c
diff options
context:
space:
mode:
Diffstat (limited to 'pjmedia/src/pjmedia/delaybuf.c')
-rw-r--r--pjmedia/src/pjmedia/delaybuf.c231
1 files changed, 231 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;
+}