From 794db40e526a22d8c8f5a4ebf322bf1de2b5d5fa Mon Sep 17 00:00:00 2001
From: Anthony Minessale <anthm@freeswitch.org>
Date: Tue, 9 Nov 2010 11:49:07 -0600
Subject: [PATCH] refactor STFU a bit

---
 libs/stfu/stfu.c | 142 ++++++++++++++++++++++-------------------------
 libs/stfu/stfu.h |   2 +-
 src/switch_ivr.c |   2 +-
 src/switch_rtp.c |   2 +-
 4 files changed, 70 insertions(+), 78 deletions(-)

diff --git a/libs/stfu/stfu.c b/libs/stfu/stfu.c
index f305a0f3df..7c30ab2ad4 100644
--- a/libs/stfu/stfu.c
+++ b/libs/stfu/stfu.c
@@ -38,7 +38,6 @@ struct stfu_queue {
 	uint32_t array_size;
 	uint32_t array_len;	
 	uint32_t wr_len;
-	uint32_t last_index;
 };
 typedef struct stfu_queue stfu_queue_t;
 
@@ -47,10 +46,12 @@ struct stfu_instance {
 	struct stfu_queue b_queue;
 	struct stfu_queue *in_queue;
 	struct stfu_queue *out_queue;
-	uint32_t last_ts;
+    struct stfu_frame *last_frame;
+	uint32_t last_wr_ts;
+	uint32_t last_rd_ts;
 	uint32_t interval;
 	uint32_t miss_count;
-	uint8_t running;
+	uint32_t max_plc;
 };
 
 
@@ -112,7 +113,7 @@ stfu_status_t stfu_n_resize(stfu_instance_t *i, uint32_t qlen)
     return s;
 }
 
-stfu_instance_t *stfu_n_init(uint32_t qlen)
+stfu_instance_t *stfu_n_init(uint32_t qlen, uint32_t max_plc)
 {
 	struct stfu_instance *i;
 
@@ -125,6 +126,13 @@ stfu_instance_t *stfu_n_init(uint32_t qlen)
 	stfu_n_init_aqueue(&i->b_queue, qlen);
 	i->in_queue = &i->a_queue;
 	i->out_queue = &i->b_queue;
+
+    if (max_plc) {
+        i->max_plc = max_plc;
+    } else {
+        i->max_plc = qlen / 2;
+    }
+
 	return i;
 }
 
@@ -135,10 +143,9 @@ void stfu_n_reset(stfu_instance_t *i)
 	i->in_queue->array_len = 0;
 	i->out_queue->array_len = 0;
 	i->out_queue->wr_len = 0;
-	i->out_queue->last_index = 0;
+	i->last_frame = NULL;
 	i->miss_count = 0;	
-	i->last_ts = 0;
-	i->running = 0;
+	i->last_wr_ts = 0;
 	i->miss_count = 0;
 	i->interval = 0;
 }
@@ -197,7 +204,7 @@ stfu_status_t stfu_n_add_data(stfu_instance_t *i, uint32_t ts, uint32_t pt, void
 
 		i->in_queue->array_len = 0;
 		i->out_queue->wr_len = 0;
-		i->out_queue->last_index = 0;
+		i->last_frame = NULL;
 		i->miss_count = 0;
 
 		if (stfu_n_process(i, i->out_queue) < 0) {
@@ -222,6 +229,8 @@ stfu_status_t stfu_n_add_data(stfu_instance_t *i, uint32_t ts, uint32_t pt, void
 		cplen = sizeof(frame->data);
 	}
 
+    i->last_rd_ts = ts;
+
 	memcpy(frame->data, data, cplen);
     frame->pt = pt;
 	frame->ts = ts;
@@ -231,88 +240,71 @@ stfu_status_t stfu_n_add_data(stfu_instance_t *i, uint32_t ts, uint32_t pt, void
 	return STFU_IT_WORKED;
 }
 
+static int stfu_n_find_frame(stfu_queue_t *queue, uint32_t ts, stfu_frame_t **r_frame, uint32_t *index)
+{
+    uint32_t i = 0;
+    stfu_frame_t *frame = NULL;
+
+    assert(r_frame);
+    assert(index);
+    
+    *r_frame = NULL;
+
+    for(i = 0; i < queue->array_len; i++) {
+        frame = &queue->array[i];
+        
+        if (frame->ts == ts) {
+            *r_frame = frame;
+            *index = i;
+            frame->was_read = 1;
+            return 1;
+        }
+    }
+
+    return 0;
+}
+
 stfu_frame_t *stfu_n_read_a_frame(stfu_instance_t *i)
 {
-	uint32_t index, index2;
+	uint32_t index;
 	uint32_t should_have = 0;
-	stfu_frame_t *frame = NULL, *rframe = NULL;
+	stfu_frame_t *rframe = NULL;
 
 	if (((i->out_queue->wr_len == i->out_queue->array_len) || !i->out_queue->array_len)) {
 		return NULL;
 	}
 
-	if (i->running) {
-		should_have = i->last_ts + i->interval;
+	if (i->last_wr_ts) {
+		should_have = i->last_wr_ts + i->interval;
 	} else {
 		should_have = i->out_queue->array[0].ts;
 	}
 
-	for(index = 0; index < i->out_queue->array_len; index++) {
-		if (i->out_queue->array[index].was_read) {
-			continue;
-		}
-
-		frame = &i->out_queue->array[index];
-
-		if (frame->ts != should_have) {
-			unsigned int tried = 0;
-			for (index2 = 0; index2 < i->out_queue->array_len; index2++) {
-				if (i->out_queue->array[index2].was_read) {
-					continue;
-				}
-				tried++;
-				if (i->out_queue->array[index2].ts == should_have) {
-					rframe = &i->out_queue->array[index2];
-					i->out_queue->last_index = index2;
-					goto done;
-				}
-			}
-			for (index2 = 0; index2 < i->in_queue->array_len; index2++) {
-				if (i->in_queue->array[index2].was_read) {
-					continue;
-				}
-				tried++;
-				if (i->in_queue->array[index2].ts == should_have) {
-					rframe = &i->in_queue->array[index2];
-					goto done;
-				}
-			}
-
-			i->miss_count++;
-
-			if (i->miss_count > 10 || (i->in_queue->array_len == i->in_queue->array_size) || 
-                tried >= (i->in_queue->array_size + i->out_queue->array_size)) {
-				i->running = 0;
-				i->interval = 0;
-				i->out_queue->wr_len = i->out_queue->array_size;
-				return NULL;
-			}
-
-			i->last_ts = should_have;
-			rframe = &i->out_queue->int_frame;
-			rframe->dlen = i->out_queue->array[i->out_queue->last_index].dlen;
-			/* poor man's plc..  Copy the last frame, but we flag it so you can use a better one if you wish */
-			memcpy(rframe->data, i->out_queue->array[i->out_queue->last_index].data, rframe->dlen);
-			rframe->ts = should_have;
-			i->out_queue->wr_len++;
-			i->running = 1;
-			return rframe;			
-		} else {
-			rframe = &i->out_queue->array[index];
-			i->out_queue->last_index = index;
-			goto done;
-		}
-	}
-
-done:
-
-	if (rframe) {
+    if (stfu_n_find_frame(i->out_queue, should_have, &rframe, &index) || stfu_n_find_frame(i->in_queue, should_have, &rframe, &index)) {
+        i->last_frame = rframe;
 		i->out_queue->wr_len++;
-		i->last_ts = rframe->ts;
+		i->last_wr_ts = rframe->ts;
 		rframe->was_read = 1;
-		i->running = 1;
 		i->miss_count = 0;
-	}
+    } else {
+        i->last_wr_ts = should_have;
+        rframe = &i->out_queue->int_frame;
+
+        if (i->last_frame && i->last_frame != rframe) {
+            rframe->dlen = i->last_frame->dlen;
+            /* poor man's plc..  Copy the last frame, but we flag it so you can use a better one if you wish */
+            memcpy(rframe->data, i->last_frame->data, rframe->dlen);
+        }
+
+        rframe->ts = should_have;
+
+        if (++i->miss_count > i->max_plc) {
+            i->interval = 0;
+            i->out_queue->wr_len = i->out_queue->array_size;
+            i->last_wr_ts = 0;
+            rframe = NULL;
+        }
+    }
 
 	return rframe;
 }
diff --git a/libs/stfu/stfu.h b/libs/stfu/stfu.h
index 17f01e3256..900db6f9ac 100644
--- a/libs/stfu/stfu.h
+++ b/libs/stfu/stfu.h
@@ -96,7 +96,7 @@ typedef struct {
 
 void stfu_n_report(stfu_instance_t *i, stfu_report_t *r);
 void stfu_n_destroy(stfu_instance_t **i);
-stfu_instance_t *stfu_n_init(uint32_t qlen);
+stfu_instance_t *stfu_n_init(uint32_t qlen, uint32_t max_plc);
 stfu_status_t stfu_n_resize(stfu_instance_t *i, uint32_t qlen);
 stfu_status_t stfu_n_add_data(stfu_instance_t *i, uint32_t ts, uint32_t pt, void *data, size_t datalen, int last);
 stfu_frame_t *stfu_n_read_a_frame(stfu_instance_t *i);
diff --git a/src/switch_ivr.c b/src/switch_ivr.c
index 744072c145..948401e53a 100644
--- a/src/switch_ivr.c
+++ b/src/switch_ivr.c
@@ -2190,7 +2190,7 @@ SWITCH_DECLARE(void) switch_ivr_delay_echo(switch_core_session_t *session, uint3
 
 	qlen = delay_ms / (interval);
 	switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, "Setting delay to %dms (%d frames)\n", delay_ms, qlen);
-	jb = stfu_n_init(qlen);
+	jb = stfu_n_init(qlen, 0);
 
 	write_frame.codec = switch_core_session_get_read_codec(session);
 
diff --git a/src/switch_rtp.c b/src/switch_rtp.c
index 6a2fa51910..1792de08b3 100644
--- a/src/switch_rtp.c
+++ b/src/switch_rtp.c
@@ -1616,7 +1616,7 @@ SWITCH_DECLARE(switch_status_t) switch_rtp_activate_stun_ping(switch_rtp_t *rtp_
 SWITCH_DECLARE(switch_status_t) switch_rtp_activate_jitter_buffer(switch_rtp_t *rtp_session, uint32_t queue_frames)
 {
 
-	rtp_session->jb = stfu_n_init(queue_frames);
+	rtp_session->jb = stfu_n_init(queue_frames, 0);
 
 	return SWITCH_STATUS_SUCCESS;
 }