summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorNanang Izzuddin <nanang@teluu.com>2008-05-30 11:30:24 +0000
committerNanang Izzuddin <nanang@teluu.com>2008-05-30 11:30:24 +0000
commitf3c4d699b34f41157d87ddb5903ec19323531d94 (patch)
treedfa862cf87f1a3a36cd9507a4c4d2bf995d87ca5
parent37c57cd786183e214ae07d461f3391a1d612a013 (diff)
Updated delaybuf to learn burst level in realtime instead of only in the beginning, this can optimize the latency and increase adaptivity
git-svn-id: http://svn.pjsip.org/repos/pjproject/trunk@1972 74dad513-b988-da41-8d7b-12977e46ad98
-rw-r--r--pjmedia/include/pjmedia/delaybuf.h26
-rw-r--r--pjmedia/src/pjmedia/conference.c7
-rw-r--r--pjmedia/src/pjmedia/delaybuf.c276
-rw-r--r--pjmedia/src/pjmedia/sound_port.c9
-rw-r--r--pjmedia/src/pjmedia/splitcomb.c11
5 files changed, 113 insertions, 216 deletions
diff --git a/pjmedia/include/pjmedia/delaybuf.h b/pjmedia/include/pjmedia/delaybuf.h
index 2141c481..1108ee23 100644
--- a/pjmedia/include/pjmedia/delaybuf.h
+++ b/pjmedia/include/pjmedia/delaybuf.h
@@ -68,14 +68,11 @@ typedef struct pjmedia_delay_buf pjmedia_delay_buf;
* identification.
* @param clock_rate Number of samples processed per second.
* @param samples_per_frame Number of samples per frame.
- * @param max_frames Maximum number of delay to be accommodated,
- * in number of frames.
- * @param delay The delay to be applied, in number of frames.
- * If the value is -1 or 0, the delay buffer will
- * learn about the delay automatically. If
- * the value is greater than zero, then this
- * value will be used and no learning will be
- * performed.
+ * @param channel_count Number of channel per frame.
+ * @param max_delay Maximum number of delay to be accommodated,
+ * in ms, if this value is negative or less than
+ * one frame time, default maximum delay used is
+ * 400 ms.
* @param option Option flags, must be zero for now.
* @param p_b Pointer to receive the delay buffer instance.
*
@@ -87,8 +84,8 @@ PJ_DECL(pj_status_t) pjmedia_delay_buf_create(pj_pool_t *pool,
const char *name,
unsigned clock_rate,
unsigned samples_per_frame,
- unsigned max_frames,
- int delay,
+ unsigned channel_count,
+ unsigned max_delay,
unsigned options,
pjmedia_delay_buf **p_b);
@@ -127,15 +124,6 @@ PJ_DECL(pj_status_t) pjmedia_delay_buf_get(pjmedia_delay_buf *b,
pj_int16_t frame[]);
/**
- * Reinitiate learning state. This will clear the buffer's content.
- *
- * @param b The delay buffer.
- *
- * @return PJ_SUCCESS on success or the appropriate error.
- */
-PJ_DECL(pj_status_t) pjmedia_delay_buf_learn(pjmedia_delay_buf *b);
-
-/**
* Reset delay buffer. This will clear the buffer's content. But keep
* the learning result.
*
diff --git a/pjmedia/src/pjmedia/conference.c b/pjmedia/src/pjmedia/conference.c
index c766cdf0..9e7cbab3 100644
--- a/pjmedia/src/pjmedia/conference.c
+++ b/pjmedia/src/pjmedia/conference.c
@@ -418,6 +418,7 @@ static pj_status_t create_pasv_port( pjmedia_conf *conf,
{
struct conf_port *conf_port;
pj_status_t status;
+ unsigned ptime;
/* Create port */
status = create_conf_port(pool, conf, port, name, &conf_port);
@@ -425,11 +426,13 @@ static pj_status_t create_pasv_port( pjmedia_conf *conf,
return status;
/* Passive port has delay buf. */
+ ptime = conf->samples_per_frame * 1000 / conf->clock_rate /
+ conf->channel_count;
status = pjmedia_delay_buf_create(pool, name->ptr,
conf->clock_rate,
conf->samples_per_frame,
- RX_BUF_COUNT, /* max */
- -1, /* delay */
+ conf->channel_count,
+ RX_BUF_COUNT * ptime, /* max delay */
0, /* options */
&conf_port->delay_buf);
if (status != PJ_SUCCESS)
diff --git a/pjmedia/src/pjmedia/delaybuf.c b/pjmedia/src/pjmedia/delaybuf.c
index 2c6aa37b..37a50fe4 100644
--- a/pjmedia/src/pjmedia/delaybuf.c
+++ b/pjmedia/src/pjmedia/delaybuf.c
@@ -23,6 +23,7 @@
#include <pj/assert.h>
#include <pj/lock.h>
#include <pj/log.h>
+#include <pj/math.h>
#include <pj/pool.h>
@@ -32,96 +33,74 @@
# define TRACE__(x)
#endif
-
-/* Type of states of delay buffer */
-enum state
-{
- STATE_WAITING,
- STATE_LEARNING,
- STATE_RUNNING
-};
-
-/* Type of operation of delay buffer */
+/* Operation types of delay buffer */
enum OP
{
OP_PUT,
- OP_GET,
- OP_UNDEFINED
+ OP_GET
};
-/* The following macros represent cycles of test. Since there are two
- * operations performed (get & put), these macros minimum value must be 2
- * and should be even number.
+/* Specify time for delaybuf to recalculate effective delay, in ms.
*/
-#define WAITING_COUNT 4
-#define LEARN_COUNT 16
+#define RECALC_TIME 2000
-/* Number of buffers to add to learnt level for additional stability
- * Please note that wsola_discard() needs minimum 3 frames, so max buffer
- * count should be minimally 3, setting SAFE_MARGIN to 2 will guarantees
- * this.
+/* Default value of maximum delay, in ms, this value is used when
+ * maximum delay requested is less than ptime (one frame length).
*/
-#define SAFE_MARGIN 2
+#define DEFAULT_MAX_DELAY 400
-/*
- * Some experimental data (with SAFE_MARGIN=1):
- *
- * System 1:
- * - XP, WMME, 10ms ptime,
- * - Sennheiser Headset+USB sound card
- * - Stable delaybuf level: 6, on WAITING_COUNT=4 and LEARNING_COUNT=48
- *
- * System 2:
- * - XP, WMME, 10ms ptime
- * - Onboard SoundMAX Digital Audio
- * - Stable delaybuf level: 6, on WAITING_COUNT=4 and LEARNING_COUNT=48
- *
- * System 3:
- * - MacBook Core 2 Duo, OSX 10.5, 10ms ptime
- * - Stable delaybuf level: 2, on WAITING_COUNT=4 and LEARNING_COUNT=8
+/* Number of frames to add to learnt level for additional stability.
*/
+#define SAFE_MARGIN 0
+/* This structure describes internal delaybuf settings and states.
+ */
struct pjmedia_delay_buf
{
+ /* Properties and configuration */
char obj_name[PJ_MAX_OBJ_NAME];
-
pj_lock_t *lock; /**< Lock object. */
-
pj_int16_t *frame_buf;
- enum state state; /**< State of delay buffer */
unsigned samples_per_frame; /**< Number of samples in one frame */
- unsigned max_frames; /**< Buffer allocated, in frames */
+ unsigned ptime; /**< Frame time, in ms */
+ unsigned channel_count; /**< Channel count, in ms */
+ unsigned max_cnt; /**< Max buffered samples, in samples*/
+ /* Buffer pointer and counter */
unsigned put_pos; /**< Position for put op, in samples */
unsigned get_pos; /**< Position for get op, in samples */
unsigned buf_cnt; /**< Number of buffered samples */
- unsigned max_cnt; /**< Max number of buffered samples */
-
- struct {
- unsigned level; /**< Burst level storage on learning */
- } op[2];
+ unsigned eff_cnt; /**< Effective count of buffered
+ samples to keep the optimum
+ balance between delay and
+ stability. This is calculated
+ based on burst level. */
+
+ /* Learning vars */
+ unsigned level; /**< Burst level counter */
enum OP last_op; /**< Last op (GET or PUT) of learning*/
- unsigned state_count; /**< Counter of op cycles of learning*/
-
- unsigned max_level; /**< Learning result: burst level */
+ int recalc_timer; /**< Timer for recalculating max_level*/
+ unsigned max_level; /**< Current max burst level */
+ /* Drift handler */
pjmedia_wsola *wsola; /**< Drift handler */
};
+
PJ_DEF(pj_status_t) pjmedia_delay_buf_create( pj_pool_t *pool,
const char *name,
unsigned clock_rate,
unsigned samples_per_frame,
- unsigned max_frames,
- int delay,
+ unsigned channel_count,
+ unsigned max_delay,
unsigned options,
pjmedia_delay_buf **p_b)
{
pjmedia_delay_buf *b;
pj_status_t status;
- PJ_ASSERT_RETURN(pool && samples_per_frame && max_frames && p_b, PJ_EINVAL);
- PJ_ASSERT_RETURN((int)max_frames >= delay, PJ_EINVAL);
+ PJ_ASSERT_RETURN(pool && samples_per_frame && clock_rate && channel_count &&
+ p_b, PJ_EINVAL);
PJ_ASSERT_RETURN(options==0, PJ_EINVAL);
PJ_UNUSED_ARG(options);
@@ -133,8 +112,16 @@ PJ_DEF(pj_status_t) pjmedia_delay_buf_create( pj_pool_t *pool,
b = PJ_POOL_ZALLOC_T(pool, pjmedia_delay_buf);
pj_ansi_strncpy(b->obj_name, name, PJ_MAX_OBJ_NAME-1);
+
b->samples_per_frame = samples_per_frame;
- b->max_frames = max_frames;
+ b->channel_count = channel_count;
+ b->ptime = samples_per_frame * 1000 / clock_rate / channel_count;
+ if (max_delay < b->ptime)
+ max_delay = PJ_MAX(DEFAULT_MAX_DELAY, b->ptime);
+
+ b->max_cnt = samples_per_frame * max_delay / b->ptime;
+ b->eff_cnt = b->max_cnt >> 1;
+ b->recalc_timer = RECALC_TIME;
status = pj_lock_create_recursive_mutex(pool, b->obj_name,
&b->lock);
@@ -142,20 +129,7 @@ PJ_DEF(pj_status_t) pjmedia_delay_buf_create( pj_pool_t *pool,
return status;
b->frame_buf = (pj_int16_t*)
- pj_pool_zalloc(pool, samples_per_frame * max_frames *
- sizeof(pj_int16_t));
-
- if (delay >= 0) {
- if (delay == 0)
- delay = 1;
- b->max_level = delay;
- b->max_cnt = delay * samples_per_frame;
- b->state = STATE_RUNNING;
- } else {
- b->max_cnt = max_frames * samples_per_frame;
- b->last_op = OP_UNDEFINED;
- b->state = STATE_WAITING;
- }
+ pj_pool_zalloc(pool, b->max_cnt * sizeof(pj_int16_t));
status = pjmedia_wsola_create(pool, clock_rate, samples_per_frame, 1,
PJMEDIA_WSOLA_NO_PLC, &b->wsola);
@@ -182,6 +156,7 @@ PJ_DEF(pj_status_t) pjmedia_delay_buf_destroy(pjmedia_delay_buf *b)
b->wsola = NULL;
pj_lock_release(b->lock);
+
pj_lock_destroy(b->lock);
b->lock = NULL;
@@ -262,118 +237,58 @@ static void shrink_buffer(pjmedia_delay_buf *b, unsigned erase_cnt)
}
}
-static void set_max_cnt(pjmedia_delay_buf *b, unsigned new_max_cnt)
-{
- unsigned old_max_cnt = b->max_cnt;
+/* Fast increase, slow decrease */
+#define AGC_UP(cur, target) cur = (cur + target*3) >> 2
+#define AGC_DOWN(cur, target) cur = (cur*3 + target) >> 2
+#define AGC(cur, target) \
+ if (cur < target) AGC_UP(cur, target); \
+ else AGC_DOWN(cur, target)
- /* nothing to adjust */
- if (old_max_cnt == new_max_cnt)
+static void update(pjmedia_delay_buf *b, enum OP op)
+{
+ /* Sequential operation */
+ if (op == b->last_op) {
+ ++b->level;
return;
+ }
- /* For now, only support shrinking */
- pj_assert(old_max_cnt > new_max_cnt);
+ /* Switching operation */
+ if (b->level > b->max_level)
+ b->max_level = b->level;
- /* Buffer empty, only need to reset pointers then set new max directly */
- if (b->buf_cnt == 0) {
- b->put_pos = b->get_pos = 0;
- b->max_cnt = new_max_cnt;
- return;
- }
+ b->recalc_timer -= (b->level * b->ptime) >> 1;
- /* If samples number in the buffer > new_max_cnt, reduce samples first */
- if (b->buf_cnt > new_max_cnt)
- shrink_buffer(b, b->buf_cnt - new_max_cnt);
+ b->last_op = op;
+ b->level = 1;
- /* Adjust buffer to accomodate the new max_cnt so the samples is secured.
- * This is done by make get_pos = 0
- */
- if (b->get_pos <= b->put_pos) {
- /* sssss .. sssss
- * ^ ^
- * G P
- */
- /* Consistency checking */
- pj_assert((b->put_pos - b->get_pos) <= new_max_cnt);
- pj_assert((b->put_pos - b->get_pos) == b->buf_cnt);
-
- pjmedia_move_samples(b->frame_buf, &b->frame_buf[b->get_pos],
- b->get_pos);
- b->put_pos -= b->get_pos;
- b->get_pos = 0;
- } else {
- /* sssss .. sssss
- * ^ ^
- * P G
- */
- unsigned d = old_max_cnt - b->get_pos;
+ /* Recalculate effective count based on max_level */
+ if (b->recalc_timer <= 0) {
+ unsigned new_eff_cnt = (b->max_level+SAFE_MARGIN)*b->samples_per_frame;
- /* Consistency checking */
- pj_assert((b->get_pos - b->put_pos) >= (old_max_cnt - new_max_cnt));
+ /* Smoothening effective count transition */
+ AGC(b->eff_cnt, new_eff_cnt);
+
+ /* Make sure the new effective count is multiplication of
+ * channel_count, so let's round it up.
+ */
+ if (b->eff_cnt % b->channel_count)
+ b->eff_cnt += b->channel_count - (b->eff_cnt % b->channel_count);
- /* Make get_pos = 0, shift right the leftmost block first */
- pjmedia_move_samples(&b->frame_buf[d], b->frame_buf, d);
- pjmedia_copy_samples(b->frame_buf, &b->frame_buf[b->get_pos], d);
- b->put_pos += d;
- b->get_pos = 0;
+ TRACE__((b->obj_name,"Cur eff_cnt=%d", b->eff_cnt));
+
+ b->max_level = 0;
+ b->recalc_timer = RECALC_TIME;
}
- b->max_cnt = new_max_cnt;
-}
+ /* See if we need to shrink the buffer to reduce delay */
+ if (b->buf_cnt > b->samples_per_frame + b->eff_cnt) {
+ unsigned erase_cnt = b->samples_per_frame >> 1;
+ unsigned old_buf_cnt = b->buf_cnt;
-static void update(pjmedia_delay_buf *b, enum OP op)
-{
- enum OP other = (enum OP) !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 SAFE_MARGIN compensation for added stability */
- b->max_level += SAFE_MARGIN;
-
- /* buffer not enough! */
- if (b->max_level > b->max_frames) {
- PJ_LOG(4,(b->obj_name,"Delay buffer learning result (%d)"
- " exceeds the maximum delay allowed (%d)",
- b->max_level,
- b->max_frames));
-
- b->max_level = b->max_frames;
- }
-
- /* we need to set new max_cnt & adjust buffer */
- set_max_cnt(b, b->max_level * b->samples_per_frame);
-
- b->state = STATE_RUNNING;
-
- PJ_LOG(4,(b->obj_name,"Delay buffer start running, level=%u",
- b->max_level));
- }
- }
- b->last_op = op;
- break;
+ shrink_buffer(b, erase_cnt);
+ PJ_LOG(5,(b->obj_name,"Buffer size adjusted from %d to %d",
+ old_buf_cnt, b->buf_cnt));
}
-
-
}
PJ_DEF(pj_status_t) pjmedia_delay_buf_put(pjmedia_delay_buf *b,
@@ -499,27 +414,6 @@ PJ_DEF(pj_status_t) pjmedia_delay_buf_get( pjmedia_delay_buf *b,
return PJ_SUCCESS;
}
-PJ_DEF(pj_status_t) pjmedia_delay_buf_learn(pjmedia_delay_buf *b)
-{
- PJ_ASSERT_RETURN(b, PJ_EINVAL);
-
- pj_lock_acquire(b->lock);
-
- 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;
- b->max_cnt = b->max_frames * b->samples_per_frame;
-
- pjmedia_delay_buf_reset(b);
-
- pj_lock_release(b->lock);
-
- PJ_LOG(5,(b->obj_name,"Delay buffer start learning"));
-
- return PJ_SUCCESS;
-}
PJ_DEF(pj_status_t) pjmedia_delay_buf_reset(pjmedia_delay_buf *b)
{
@@ -527,6 +421,8 @@ PJ_DEF(pj_status_t) pjmedia_delay_buf_reset(pjmedia_delay_buf *b)
pj_lock_acquire(b->lock);
+ b->recalc_timer = RECALC_TIME;
+
/* clean up buffer */
b->buf_cnt = 0;
b->put_pos = b->get_pos = 0;
diff --git a/pjmedia/src/pjmedia/sound_port.c b/pjmedia/src/pjmedia/sound_port.c
index 188ee28c..6682e4a4 100644
--- a/pjmedia/src/pjmedia/sound_port.c
+++ b/pjmedia/src/pjmedia/sound_port.c
@@ -372,6 +372,7 @@ PJ_DEF(pj_status_t) pjmedia_snd_port_create( pj_pool_t *pool,
{
pjmedia_snd_port *snd_port;
pj_status_t status;
+ unsigned ptime;
PJ_ASSERT_RETURN(pool && p_port, PJ_EINVAL);
@@ -388,13 +389,17 @@ PJ_DEF(pj_status_t) pjmedia_snd_port_create( pj_pool_t *pool,
snd_port->bits_per_sample = bits_per_sample;
#if PJMEDIA_SOUND_USE_DELAYBUF
+ ptime = samples_per_frame * 1000 / (clock_rate * channel_count);
+
status = pjmedia_delay_buf_create(pool, "snd_buff",
- clock_rate, samples_per_frame,
- PJMEDIA_SOUND_BUFFER_COUNT, -1,
+ clock_rate, samples_per_frame,
+ channel_count,
+ PJMEDIA_SOUND_BUFFER_COUNT * ptime,
0, &snd_port->delay_buf);
PJ_ASSERT_RETURN(status == PJ_SUCCESS, status);
#else
PJ_UNUSED_ARG(status);
+ PJ_UNUSED_ARG(ptime);
#endif
*p_port = snd_port;
diff --git a/pjmedia/src/pjmedia/splitcomb.c b/pjmedia/src/pjmedia/splitcomb.c
index df1d1316..79dbcde7 100644
--- a/pjmedia/src/pjmedia/splitcomb.c
+++ b/pjmedia/src/pjmedia/splitcomb.c
@@ -307,7 +307,7 @@ PJ_DEF(pj_status_t) pjmedia_splitcomb_create_rev_channel( pj_pool_t *pool,
const pj_str_t name = pj_str("scomb-rev");
struct splitcomb *sc = (struct splitcomb*) splitcomb;
struct reverse_port *rport;
- unsigned buf_cnt;
+ unsigned buf_cnt, ptime;
pjmedia_port *port;
pj_status_t status;
@@ -346,6 +346,9 @@ PJ_DEF(pj_status_t) pjmedia_splitcomb_create_rev_channel( pj_pool_t *pool,
if (buf_cnt == 0)
buf_cnt = MAX_BUF_CNT;
+ ptime = port->info.samples_per_frame * 1000 / port->info.clock_rate /
+ port->info.channel_count;
+
rport->max_burst = MAX_BURST;
rport->max_null_frames = MAX_NULL_FRAMES;
@@ -353,7 +356,8 @@ PJ_DEF(pj_status_t) pjmedia_splitcomb_create_rev_channel( pj_pool_t *pool,
status = pjmedia_delay_buf_create(pool, "scombdb-dn",
port->info.clock_rate,
port->info.samples_per_frame,
- buf_cnt, -1, 0,
+ port->info.channel_count,
+ buf_cnt * ptime, 0,
&rport->buf[DIR_DOWNSTREAM].dbuf);
if (status != PJ_SUCCESS) {
return status;
@@ -363,7 +367,8 @@ PJ_DEF(pj_status_t) pjmedia_splitcomb_create_rev_channel( pj_pool_t *pool,
status = pjmedia_delay_buf_create(pool, "scombdb-up",
port->info.clock_rate,
port->info.samples_per_frame,
- buf_cnt, -1, 0,
+ port->info.channel_count,
+ buf_cnt * ptime, 0,
&rport->buf[DIR_UPSTREAM].dbuf);
if (status != PJ_SUCCESS) {
pjmedia_delay_buf_destroy(rport->buf[DIR_DOWNSTREAM].dbuf);