various minor portability fixes (mostly from tholo for OpenBSD)
[asterisk-bristuff.git] / fixedjitterbuf.c
blobd2f4a95dc39d2437a1cf4b0ff7f44a641ac54609
1 /*
2 * Copyright (C) 2005, Attractel OOD
4 * Contributors:
5 * Slav Klenov <slav@securax.org>
7 * Copyright on this file is disclaimed to Digium for inclusion in Asterisk
9 * See http://www.asterisk.org for more information about
10 * the Asterisk project. Please do not directly contact
11 * any of the maintainers of this project for assistance;
12 * the project provides a web site, mailing lists and IRC
13 * channels for your use.
15 * This program is free software, distributed under the terms of
16 * the GNU General Public License Version 2. See the LICENSE file
17 * at the top of the source tree.
20 /*! \file
22 * \brief Jitterbuffering algorithm.
24 * \author Slav Klenov <slav@securax.org>
27 #include "asterisk.h"
29 ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
31 #include <stdio.h>
32 #include <stdlib.h>
33 #include <assert.h>
34 #include <string.h>
35 #include <unistd.h>
37 #include "asterisk/utils.h"
38 #include "fixedjitterbuf.h"
40 #undef FIXED_JB_DEBUG
42 #ifdef FIXED_JB_DEBUG
43 #define ASSERT(a)
44 #else
45 #define ASSERT(a) assert(a)
46 #endif
48 /*! \brief private fixed_jb structure */
49 struct fixed_jb
51 struct fixed_jb_frame *frames;
52 struct fixed_jb_frame *tail;
53 struct fixed_jb_conf conf;
54 long rxcore;
55 long delay;
56 long next_delivery;
57 int force_resynch;
61 static struct fixed_jb_frame *alloc_jb_frame(struct fixed_jb *jb);
62 static void release_jb_frame(struct fixed_jb *jb, struct fixed_jb_frame *frame);
63 static void get_jb_head(struct fixed_jb *jb, struct fixed_jb_frame *frame);
64 static int resynch_jb(struct fixed_jb *jb, void *data, long ms, long ts, long now);
66 static inline struct fixed_jb_frame *alloc_jb_frame(struct fixed_jb *jb)
68 return ast_calloc(1, sizeof(struct fixed_jb_frame));
71 static inline void release_jb_frame(struct fixed_jb *jb, struct fixed_jb_frame *frame)
73 free(frame);
76 static void get_jb_head(struct fixed_jb *jb, struct fixed_jb_frame *frame)
78 struct fixed_jb_frame *fr;
80 /* unlink the frame */
81 fr = jb->frames;
82 jb->frames = fr->next;
83 if (jb->frames) {
84 jb->frames->prev = NULL;
85 } else {
86 /* the jb is empty - update tail */
87 jb->tail = NULL;
90 /* update next */
91 jb->next_delivery = fr->delivery + fr->ms;
93 /* copy the destination */
94 memcpy(frame, fr, sizeof(struct fixed_jb_frame));
96 /* and release the frame */
97 release_jb_frame(jb, fr);
101 struct fixed_jb *fixed_jb_new(struct fixed_jb_conf *conf)
103 struct fixed_jb *jb;
105 if (!(jb = ast_calloc(1, sizeof(*jb))))
106 return NULL;
108 /* First copy our config */
109 memcpy(&jb->conf, conf, sizeof(struct fixed_jb_conf));
111 /* we dont need the passed config anymore - continue working with the saved one */
112 conf = &jb->conf;
114 /* validate the configuration */
115 if (conf->jbsize < 1)
116 conf->jbsize = FIXED_JB_SIZE_DEFAULT;
118 if (conf->resync_threshold < 1)
119 conf->resync_threshold = FIXED_JB_RESYNCH_THRESHOLD_DEFAULT;
121 /* Set the constant delay to the jitterbuf */
122 jb->delay = conf->jbsize;
124 return jb;
128 void fixed_jb_destroy(struct fixed_jb *jb)
130 /* jitterbuf MUST be empty before it can be destroyed */
131 ASSERT(jb->frames == NULL);
133 free(jb);
137 static int resynch_jb(struct fixed_jb *jb, void *data, long ms, long ts, long now)
139 long diff, offset;
140 struct fixed_jb_frame *frame;
142 /* If jb is empty, just reinitialize the jb */
143 if (!jb->frames) {
144 /* debug check: tail should also be NULL */
145 ASSERT(jb->tail == NULL);
147 return fixed_jb_put_first(jb, data, ms, ts, now);
150 /* Adjust all jb state just as the new frame is with delivery = the delivery of the last
151 frame (e.g. this one with max delivery) + the length of the last frame. */
153 /* Get the diff in timestamps */
154 diff = ts - jb->tail->ts;
156 /* Ideally this should be just the length of the last frame. The deviation is the desired
157 offset */
158 offset = diff - jb->tail->ms;
160 /* Do we really need to resynch, or this is just a frame for dropping? */
161 if (!jb->force_resynch && (offset < jb->conf.resync_threshold && offset > -jb->conf.resync_threshold))
162 return FIXED_JB_DROP;
164 /* Reset the force resynch flag */
165 jb->force_resynch = 0;
167 /* apply the offset to the jb state */
168 jb->rxcore -= offset;
169 frame = jb->frames;
170 while (frame) {
171 frame->ts += offset;
172 frame = frame->next;
175 /* now jb_put() should add the frame at a last position */
176 return fixed_jb_put(jb, data, ms, ts, now);
180 void fixed_jb_set_force_resynch(struct fixed_jb *jb)
182 jb->force_resynch = 1;
186 int fixed_jb_put_first(struct fixed_jb *jb, void *data, long ms, long ts, long now)
188 /* this is our first frame - set the base of the receivers time */
189 jb->rxcore = now - ts;
191 /* init next for a first time - it should be the time the first frame should be played */
192 jb->next_delivery = now + jb->delay;
194 /* put the frame */
195 return fixed_jb_put(jb, data, ms, ts, now);
199 int fixed_jb_put(struct fixed_jb *jb, void *data, long ms, long ts, long now)
201 struct fixed_jb_frame *frame, *next, *newframe;
202 long delivery;
204 /* debug check the validity of the input params */
205 ASSERT(data != NULL);
206 /* do not allow frames shorter than 2 ms */
207 ASSERT(ms >= 2);
208 ASSERT(ts >= 0);
209 ASSERT(now >= 0);
211 delivery = jb->rxcore + jb->delay + ts;
213 /* check if the new frame is not too late */
214 if (delivery < jb->next_delivery) {
215 /* should drop the frame, but let first resynch_jb() check if this is not a jump in ts, or
216 the force resynch flag was not set. */
217 return resynch_jb(jb, data, ms, ts, now);
220 /* what if the delivery time is bigger than next + delay? Seems like a frame for the future.
221 However, allow more resync_threshold ms in advance */
222 if (delivery > jb->next_delivery + jb->delay + jb->conf.resync_threshold) {
223 /* should drop the frame, but let first resynch_jb() check if this is not a jump in ts, or
224 the force resynch flag was not set. */
225 return resynch_jb(jb, data, ms, ts, now);
228 /* find the right place in the frames list, sorted by delivery time */
229 frame = jb->tail;
230 while (frame && frame->delivery > delivery) {
231 frame = frame->prev;
234 /* Check if the new delivery time is not covered already by the chosen frame */
235 if (frame && (frame->delivery == delivery ||
236 delivery < frame->delivery + frame->ms ||
237 (frame->next && delivery + ms > frame->next->delivery)))
239 /* TODO: Should we check for resynch here? Be careful to do not allow threshold smaller than
240 the size of the jb */
242 /* should drop the frame, but let first resynch_jb() check if this is not a jump in ts, or
243 the force resynch flag was not set. */
244 return resynch_jb(jb, data, ms, ts, now);
247 /* Reset the force resynch flag */
248 jb->force_resynch = 0;
250 /* Get a new frame */
251 newframe = alloc_jb_frame(jb);
252 newframe->data = data;
253 newframe->ts = ts;
254 newframe->ms = ms;
255 newframe->delivery = delivery;
257 /* and insert it right on place */
258 if (frame) {
259 next = frame->next;
260 frame->next = newframe;
261 if (next) {
262 newframe->next = next;
263 next->prev = newframe;
264 } else {
265 /* insert after the last frame - should update tail */
266 jb->tail = newframe;
267 newframe->next = NULL;
269 newframe->prev = frame;
271 return FIXED_JB_OK;
272 } else if (!jb->frames) {
273 /* the frame list is empty or thats just the first frame ever */
274 /* tail should also be NULL is that case */
275 ASSERT(jb->tail == NULL);
276 jb->frames = jb->tail = newframe;
277 newframe->next = NULL;
278 newframe->prev = NULL;
280 return FIXED_JB_OK;
281 } else {
282 /* insert on a first position - should update frames head */
283 newframe->next = jb->frames;
284 newframe->prev = NULL;
285 jb->frames->prev = newframe;
286 jb->frames = newframe;
288 return FIXED_JB_OK;
293 int fixed_jb_get(struct fixed_jb *jb, struct fixed_jb_frame *frame, long now, long interpl)
295 ASSERT(now >= 0);
296 ASSERT(interpl >= 2);
298 if (now < jb->next_delivery) {
299 /* too early for the next frame */
300 return FIXED_JB_NOFRAME;
303 /* Is the jb empty? */
304 if (!jb->frames) {
305 /* should interpolate a frame */
306 /* update next */
307 jb->next_delivery += interpl;
309 return FIXED_JB_INTERP;
312 /* Isn't it too late for the first frame available in the jb? */
313 if (now > jb->frames->delivery + jb->frames->ms) {
314 /* yes - should drop this frame and update next to point the next frame (get_jb_head() does it) */
315 get_jb_head(jb, frame);
317 return FIXED_JB_DROP;
320 /* isn't it too early to play the first frame available? */
321 if (now < jb->frames->delivery) {
322 /* yes - should interpolate one frame */
323 /* update next */
324 jb->next_delivery += interpl;
326 return FIXED_JB_INTERP;
329 /* we have a frame for playing now (get_jb_head() updates next) */
330 get_jb_head(jb, frame);
332 return FIXED_JB_OK;
336 long fixed_jb_next(struct fixed_jb *jb)
338 return jb->next_delivery;
342 int fixed_jb_remove(struct fixed_jb *jb, struct fixed_jb_frame *frameout)
344 if (!jb->frames)
345 return FIXED_JB_NOFRAME;
347 get_jb_head(jb, frameout);
349 return FIXED_JB_OK;