Issue 9869 - replace malloc and memset with ast_calloc, and other coding guidelines...
[asterisk/asterisk.git] / main / fixedjitterbuf.c
1 /*
2  * Copyright (C) 2005, Attractel OOD
3  *
4  * Contributors:
5  * Slav Klenov <slav@securax.org>
6  *
7  * See http://www.asterisk.org for more information about
8  * the Asterisk project. Please do not directly contact
9  * any of the maintainers of this project for assistance;
10  * the project provides a web site, mailing lists and IRC
11  * channels for your use.
12  *
13  * This program is free software, distributed under the terms of
14  * the GNU General Public License Version 2. See the LICENSE file
15  * at the top of the source tree.
16  *
17  * A license has been granted to Digium (via disclaimer) for the use of
18  * this code.
19  */
20
21 /*! \file 
22  * 
23  * \brief Jitterbuffering algorithm.
24  * 
25  * \author Slav Klenov <slav@securax.org>
26  */
27
28 #include "asterisk.h"
29
30 ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
31
32 #include <stdio.h>
33 #include <stdlib.h>
34 #include <assert.h>
35 #include <string.h>
36 #include <unistd.h>
37
38 #include "asterisk/utils.h"
39 #include "fixedjitterbuf.h"
40
41 #undef FIXED_JB_DEBUG
42
43 #ifdef FIXED_JB_DEBUG
44 #define ASSERT(a)
45 #else
46 #define ASSERT(a) assert(a)
47 #endif
48
49 /*! \brief private fixed_jb structure */
50 struct fixed_jb
51 {
52         struct fixed_jb_frame *frames;
53         struct fixed_jb_frame *tail;
54         struct fixed_jb_conf conf;
55         long rxcore;
56         long delay;
57         long next_delivery;
58         int force_resynch;
59 };
60
61
62 static struct fixed_jb_frame *alloc_jb_frame(struct fixed_jb *jb);
63 static void release_jb_frame(struct fixed_jb *jb, struct fixed_jb_frame *frame);
64 static void get_jb_head(struct fixed_jb *jb, struct fixed_jb_frame *frame);
65 static int resynch_jb(struct fixed_jb *jb, void *data, long ms, long ts, long now);
66
67 static inline struct fixed_jb_frame *alloc_jb_frame(struct fixed_jb *jb)
68 {
69         return ast_calloc(1, sizeof(*jb));
70 }
71
72 static inline void release_jb_frame(struct fixed_jb *jb, struct fixed_jb_frame *frame)
73 {
74         ast_free(frame);
75 }
76
77 static void get_jb_head(struct fixed_jb *jb, struct fixed_jb_frame *frame)
78 {
79         struct fixed_jb_frame *fr;
80         
81         /* unlink the frame */
82         fr = jb->frames;
83         jb->frames = fr->next;
84         if (jb->frames) {
85                 jb->frames->prev = NULL;
86         } else {
87                 /* the jb is empty - update tail */
88                 jb->tail = NULL;
89         }
90         
91         /* update next */
92         jb->next_delivery = fr->delivery + fr->ms;
93         
94         /* copy the destination */
95         memcpy(frame, fr, sizeof(struct fixed_jb_frame));
96         
97         /* and release the frame */
98         release_jb_frame(jb, fr);
99 }
100
101
102 struct fixed_jb *fixed_jb_new(struct fixed_jb_conf *conf)
103 {
104         struct fixed_jb *jb;
105         
106         if (!(jb = ast_calloc(1, sizeof(*jb))))
107                 return NULL;
108         
109         /* First copy our config */
110         memcpy(&jb->conf, conf, sizeof(struct fixed_jb_conf));
111
112         /* we dont need the passed config anymore - continue working with the saved one */
113         conf = &jb->conf;
114         
115         /* validate the configuration */
116         if (conf->jbsize < 1)
117                 conf->jbsize = FIXED_JB_SIZE_DEFAULT;
118
119         if (conf->resync_threshold < 1)
120                 conf->resync_threshold = FIXED_JB_RESYNCH_THRESHOLD_DEFAULT;
121         
122         /* Set the constant delay to the jitterbuf */
123         jb->delay = conf->jbsize;
124         
125         return jb;
126 }
127
128
129 void fixed_jb_destroy(struct fixed_jb *jb)
130 {
131         /* jitterbuf MUST be empty before it can be destroyed */
132         ASSERT(jb->frames == NULL);
133         
134         ast_free(jb);
135 }
136
137
138 static int resynch_jb(struct fixed_jb *jb, void *data, long ms, long ts, long now)
139 {
140         long diff, offset;
141         struct fixed_jb_frame *frame;
142         
143         /* If jb is empty, just reinitialize the jb */
144         if (!jb->frames) {
145                 /* debug check: tail should also be NULL */
146                 ASSERT(jb->tail == NULL);
147                 
148                 return fixed_jb_put_first(jb, data, ms, ts, now);
149         }
150         
151         /* Adjust all jb state just as the new frame is with delivery = the delivery of the last
152            frame (e.g. this one with max delivery) + the length of the last frame. */
153         
154         /* Get the diff in timestamps */
155         diff = ts - jb->tail->ts;
156         
157         /* Ideally this should be just the length of the last frame. The deviation is the desired
158            offset */
159         offset = diff - jb->tail->ms;
160         
161         /* Do we really need to resynch, or this is just a frame for dropping? */
162         if (!jb->force_resynch && (offset < jb->conf.resync_threshold && offset > -jb->conf.resync_threshold))
163                 return FIXED_JB_DROP;
164         
165         /* Reset the force resynch flag */
166         jb->force_resynch = 0;
167         
168         /* apply the offset to the jb state */
169         jb->rxcore -= offset;
170         frame = jb->frames;
171         while (frame) {
172                 frame->ts += offset;
173                 frame = frame->next;
174         }
175         
176         /* now jb_put() should add the frame at a last position */
177         return fixed_jb_put(jb, data, ms, ts, now);
178 }
179
180
181 void fixed_jb_set_force_resynch(struct fixed_jb *jb)
182 {
183         jb->force_resynch = 1;
184 }
185
186
187 int fixed_jb_put_first(struct fixed_jb *jb, void *data, long ms, long ts, long now)
188 {
189         /* this is our first frame - set the base of the receivers time */
190         jb->rxcore = now - ts;
191         
192         /* init next for a first time - it should be the time the first frame should be played */
193         jb->next_delivery = now + jb->delay;
194         
195         /* put the frame */
196         return fixed_jb_put(jb, data, ms, ts, now);
197 }
198
199
200 int fixed_jb_put(struct fixed_jb *jb, void *data, long ms, long ts, long now)
201 {
202         struct fixed_jb_frame *frame, *next, *newframe;
203         long delivery;
204         
205         /* debug check the validity of the input params */
206         ASSERT(data != NULL);
207         /* do not allow frames shorter than 2 ms */
208         ASSERT(ms >= 2);
209         ASSERT(ts >= 0);
210         ASSERT(now >= 0);
211         
212         delivery = jb->rxcore + jb->delay + ts;
213         
214         /* check if the new frame is not too late */
215         if (delivery < jb->next_delivery) {
216                 /* should drop the frame, but let first resynch_jb() check if this is not a jump in ts, or
217                    the force resynch flag was not set. */
218                 return resynch_jb(jb, data, ms, ts, now);
219         }
220         
221         /* what if the delivery time is bigger than next + delay? Seems like a frame for the future.
222            However, allow more resync_threshold ms in advance */
223         if (delivery > jb->next_delivery + jb->delay + jb->conf.resync_threshold) {
224                 /* should drop the frame, but let first resynch_jb() check if this is not a jump in ts, or
225                    the force resynch flag was not set. */
226                 return resynch_jb(jb, data, ms, ts, now);
227         }
228
229         /* find the right place in the frames list, sorted by delivery time */
230         frame = jb->tail;
231         while (frame && frame->delivery > delivery) {
232                 frame = frame->prev;
233         }
234         
235         /* Check if the new delivery time is not covered already by the chosen frame */
236         if (frame && (frame->delivery == delivery ||
237                          delivery < frame->delivery + frame->ms ||
238                          (frame->next && delivery + ms > frame->next->delivery)))
239         {
240                 /* TODO: Should we check for resynch here? Be careful to do not allow threshold smaller than
241                    the size of the jb */
242                 
243                 /* should drop the frame, but let first resynch_jb() check if this is not a jump in ts, or
244                    the force resynch flag was not set. */
245                 return resynch_jb(jb, data, ms, ts, now);
246         }
247         
248         /* Reset the force resynch flag */
249         jb->force_resynch = 0;
250         
251         /* Get a new frame */
252         newframe = alloc_jb_frame(jb);
253         newframe->data = data;
254         newframe->ts = ts;
255         newframe->ms = ms;
256         newframe->delivery = delivery;
257         
258         /* and insert it right on place */
259         if (frame) {
260                 next = frame->next;
261                 frame->next = newframe;
262                 if (next) {
263                         newframe->next = next;
264                         next->prev = newframe;
265                 } else {
266                         /* insert after the last frame - should update tail */
267                         jb->tail = newframe;
268                         newframe->next = NULL;
269                 }
270                 newframe->prev = frame;
271                 
272                 return FIXED_JB_OK;
273         } else if (!jb->frames) {
274                 /* the frame list is empty or thats just the first frame ever */
275                 /* tail should also be NULL is that case */
276                 ASSERT(jb->tail == NULL);
277                 jb->frames = jb->tail = newframe;
278                 newframe->next = NULL;
279                 newframe->prev = NULL;
280                 
281                 return FIXED_JB_OK;
282         } else {
283                 /* insert on a first position - should update frames head */
284                 newframe->next = jb->frames;
285                 newframe->prev = NULL;
286                 jb->frames->prev = newframe;
287                 jb->frames = newframe;
288                 
289                 return FIXED_JB_OK;
290         }
291 }
292
293
294 int fixed_jb_get(struct fixed_jb *jb, struct fixed_jb_frame *frame, long now, long interpl)
295 {
296         ASSERT(now >= 0);
297         ASSERT(interpl >= 2);
298         
299         if (now < jb->next_delivery) {
300                 /* too early for the next frame */
301                 return FIXED_JB_NOFRAME;
302         }
303         
304         /* Is the jb empty? */
305         if (!jb->frames) {
306                 /* should interpolate a frame */
307                 /* update next */
308                 jb->next_delivery += interpl;
309                 
310                 return FIXED_JB_INTERP;
311         }
312         
313         /* Isn't it too late for the first frame available in the jb? */
314         if (now > jb->frames->delivery + jb->frames->ms) {
315                 /* yes - should drop this frame and update next to point the next frame (get_jb_head() does it) */
316                 get_jb_head(jb, frame);
317                 
318                 return FIXED_JB_DROP;
319         }
320         
321         /* isn't it too early to play the first frame available? */
322         if (now < jb->frames->delivery) {
323                 /* yes - should interpolate one frame */
324                 /* update next */
325                 jb->next_delivery += interpl;
326                 
327                 return FIXED_JB_INTERP;
328         }
329         
330         /* we have a frame for playing now (get_jb_head() updates next) */
331         get_jb_head(jb, frame);
332         
333         return FIXED_JB_OK;
334 }
335
336
337 long fixed_jb_next(struct fixed_jb *jb)
338 {
339         return jb->next_delivery;
340 }
341
342
343 int fixed_jb_remove(struct fixed_jb *jb, struct fixed_jb_frame *frameout)
344 {
345         if (!jb->frames)
346                 return FIXED_JB_NOFRAME;
347         
348         get_jb_head(jb, frameout);
349         
350         return FIXED_JB_OK;
351 }