Don't force context for domain= setting (imported from 1.2)
[asterisk/asterisk.git] / fixedjitterbuf.c
1 /*
2  * Copyright (C) 2005, Attractel OOD
3  *
4  * Contributors:
5  * Slav Klenov <slav@securax.org>
6  *
7  * Copyright on this file is disclaimed to Digium for inclusion in Asterisk
8  *
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.
14  *
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.
18  */
19
20 /*! \file 
21  * 
22  * \brief Jitterbuffering algorithm.
23  * 
24  * \author Slav Klenov <slav@securax.org>
25  */
26
27 #include "asterisk.h"
28
29 ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
30
31 #include <stdio.h>
32 #include <stdlib.h>
33 #include <assert.h>
34 #include <string.h>
35 #include <unistd.h>
36
37 #include "asterisk/utils.h"
38 #include "fixedjitterbuf.h"
39
40 #undef FIXED_JB_DEBUG
41
42 #ifdef FIXED_JB_DEBUG
43 #define ASSERT(a)
44 #else
45 #define ASSERT(a) assert(a)
46 #endif
47
48 /*! \brief private fixed_jb structure */
49 struct fixed_jb
50 {
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;
58 };
59
60
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);
65
66 static inline struct fixed_jb_frame *alloc_jb_frame(struct fixed_jb *jb)
67 {
68         return ast_calloc(1, sizeof(struct fixed_jb_frame));
69 }
70
71 static inline void release_jb_frame(struct fixed_jb *jb, struct fixed_jb_frame *frame)
72 {
73         free(frame);
74 }
75
76 static void get_jb_head(struct fixed_jb *jb, struct fixed_jb_frame *frame)
77 {
78         struct fixed_jb_frame *fr;
79         
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;
88         }
89         
90         /* update next */
91         jb->next_delivery = fr->delivery + fr->ms;
92         
93         /* copy the destination */
94         memcpy(frame, fr, sizeof(struct fixed_jb_frame));
95         
96         /* and release the frame */
97         release_jb_frame(jb, fr);
98 }
99
100
101 struct fixed_jb *fixed_jb_new(struct fixed_jb_conf *conf)
102 {
103         struct fixed_jb *jb;
104         
105         if (!(jb = ast_calloc(1, sizeof(*jb))))
106                 return NULL;
107         
108         /* First copy our config */
109         memcpy(&jb->conf, conf, sizeof(struct fixed_jb_conf));
110
111         /* we dont need the passed config anymore - continue working with the saved one */
112         conf = &jb->conf;
113         
114         /* validate the configuration */
115         if (conf->jbsize < 1)
116                 conf->jbsize = FIXED_JB_SIZE_DEFAULT;
117
118         if (conf->resync_threshold < 1)
119                 conf->resync_threshold = FIXED_JB_RESYNCH_THRESHOLD_DEFAULT;
120         
121         /* Set the constant delay to the jitterbuf */
122         jb->delay = conf->jbsize;
123         
124         return jb;
125 }
126
127
128 void fixed_jb_destroy(struct fixed_jb *jb)
129 {
130         /* jitterbuf MUST be empty before it can be destroyed */
131         ASSERT(jb->frames == NULL);
132         
133         free(jb);
134 }
135
136
137 static int resynch_jb(struct fixed_jb *jb, void *data, long ms, long ts, long now)
138 {
139         long diff, offset;
140         struct fixed_jb_frame *frame;
141         
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);
146                 
147                 return fixed_jb_put_first(jb, data, ms, ts, now);
148         }
149         
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. */
152         
153         /* Get the diff in timestamps */
154         diff = ts - jb->tail->ts;
155         
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;
159         
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;
163         
164         /* Reset the force resynch flag */
165         jb->force_resynch = 0;
166         
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;
173         }
174         
175         /* now jb_put() should add the frame at a last position */
176         return fixed_jb_put(jb, data, ms, ts, now);
177 }
178
179
180 void fixed_jb_set_force_resynch(struct fixed_jb *jb)
181 {
182         jb->force_resynch = 1;
183 }
184
185
186 int fixed_jb_put_first(struct fixed_jb *jb, void *data, long ms, long ts, long now)
187 {
188         /* this is our first frame - set the base of the receivers time */
189         jb->rxcore = now - ts;
190         
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;
193         
194         /* put the frame */
195         return fixed_jb_put(jb, data, ms, ts, now);
196 }
197
198
199 int fixed_jb_put(struct fixed_jb *jb, void *data, long ms, long ts, long now)
200 {
201         struct fixed_jb_frame *frame, *next, *newframe;
202         long delivery;
203         
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);
210         
211         delivery = jb->rxcore + jb->delay + ts;
212         
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);
218         }
219         
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);
226         }
227
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;
232         }
233         
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)))
238         {
239                 /* TODO: Should we check for resynch here? Be careful to do not allow threshold smaller than
240                    the size of the jb */
241                 
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);
245         }
246         
247         /* Reset the force resynch flag */
248         jb->force_resynch = 0;
249         
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;
256         
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;
268                 }
269                 newframe->prev = frame;
270                 
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;
279                 
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;
287                 
288                 return FIXED_JB_OK;
289         }
290 }
291
292
293 int fixed_jb_get(struct fixed_jb *jb, struct fixed_jb_frame *frame, long now, long interpl)
294 {
295         ASSERT(now >= 0);
296         ASSERT(interpl >= 2);
297         
298         if (now < jb->next_delivery) {
299                 /* too early for the next frame */
300                 return FIXED_JB_NOFRAME;
301         }
302         
303         /* Is the jb empty? */
304         if (!jb->frames) {
305                 /* should interpolate a frame */
306                 /* update next */
307                 jb->next_delivery += interpl;
308                 
309                 return FIXED_JB_INTERP;
310         }
311         
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);
316                 
317                 return FIXED_JB_DROP;
318         }
319         
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;
325                 
326                 return FIXED_JB_INTERP;
327         }
328         
329         /* we have a frame for playing now (get_jb_head() updates next) */
330         get_jb_head(jb, frame);
331         
332         return FIXED_JB_OK;
333 }
334
335
336 long fixed_jb_next(struct fixed_jb *jb)
337 {
338         return jb->next_delivery;
339 }
340
341
342 int fixed_jb_remove(struct fixed_jb *jb, struct fixed_jb_frame *frameout)
343 {
344         if (!jb->frames)
345                 return FIXED_JB_NOFRAME;
346         
347         get_jb_head(jb, frameout);
348         
349         return FIXED_JB_OK;
350 }