2 * Copyright (C) 2005, Attractel OOD
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.
22 * \brief Jitterbuffering algorithm.
24 * \author Slav Klenov <slav@securax.org>
29 ASTERISK_FILE_VERSION(__FILE__
, "$Revision$")
37 #include "asterisk/utils.h"
38 #include "fixedjitterbuf.h"
45 #define ASSERT(a) assert(a)
48 /*! \brief private fixed_jb structure */
51 struct fixed_jb_frame
*frames
;
52 struct fixed_jb_frame
*tail
;
53 struct fixed_jb_conf conf
;
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
)
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 */
82 jb
->frames
= fr
->next
;
84 jb
->frames
->prev
= NULL
;
86 /* the jb is empty - update tail */
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
)
105 if (!(jb
= ast_calloc(1, sizeof(*jb
))))
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 */
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
;
128 void fixed_jb_destroy(struct fixed_jb
*jb
)
130 /* jitterbuf MUST be empty before it can be destroyed */
131 ASSERT(jb
->frames
== NULL
);
137 static int resynch_jb(struct fixed_jb
*jb
, void *data
, long ms
, long ts
, long now
)
140 struct fixed_jb_frame
*frame
;
142 /* If jb is empty, just reinitialize the jb */
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
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
;
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
;
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
;
204 /* debug check the validity of the input params */
205 ASSERT(data
!= NULL
);
206 /* do not allow frames shorter than 2 ms */
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 */
230 while (frame
&& frame
->delivery
> delivery
) {
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
;
255 newframe
->delivery
= delivery
;
257 /* and insert it right on place */
260 frame
->next
= newframe
;
262 newframe
->next
= next
;
263 next
->prev
= newframe
;
265 /* insert after the last frame - should update tail */
267 newframe
->next
= NULL
;
269 newframe
->prev
= frame
;
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
;
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
;
293 int fixed_jb_get(struct fixed_jb
*jb
, struct fixed_jb_frame
*frame
, long now
, long interpl
)
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? */
305 /* should interpolate a frame */
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 */
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
);
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
)
345 return FIXED_JB_NOFRAME
;
347 get_jb_head(jb
, frameout
);