Wed Jan 8 2020 09:49:47

Asterisk developer's documentation


fixedjitterbuf.c
Go to the documentation of this file.
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 /*** MODULEINFO
29  <support_level>core</support_level>
30  ***/
31 
32 #include "asterisk.h"
33 
34 ASTERISK_FILE_VERSION(__FILE__, "$Revision: 369001 $")
35 
36 #include <assert.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 {
55  long rxcore;
56  long delay;
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 
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 don't 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)
118 
119  if (conf->resync_threshold < 1)
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 
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 }
#define ASSERT(a)
static int resynch_jb(struct fixed_jb *jb, void *data, long ms, long ts, long now)
Asterisk main include file. File version handling, generic pbx functions.
private fixed_jb structure
static void get_jb_head(struct fixed_jb *jb, struct fixed_jb_frame *frame)
int fixed_jb_remove(struct fixed_jb *jb, struct fixed_jb_frame *frameout)
static struct fixed_jb_frame * alloc_jb_frame(struct fixed_jb *jb)
struct fixed_jb_frame * next
#define FIXED_JB_SIZE_DEFAULT
static int frames
Definition: iax2-parser.c:49
Utility functions.
struct fixed_jb_frame * frames
struct fixed_jb_conf conf
struct fixed_jb_frame * tail
int force_resynch
int fixed_jb_get(struct fixed_jb *jb, struct fixed_jb_frame *frame, long now, long interpl)
#define FIXED_JB_RESYNCH_THRESHOLD_DEFAULT
struct fixed_jb_frame * prev
void fixed_jb_destroy(struct fixed_jb *jb)
int fixed_jb_put_first(struct fixed_jb *jb, void *data, long ms, long ts, long now)
long fixed_jb_next(struct fixed_jb *jb)
#define ast_free(a)
Definition: astmm.h:97
Jitterbuffering algorithm.
void fixed_jb_set_force_resynch(struct fixed_jb *jb)
#define ast_calloc(a, b)
Definition: astmm.h:82
struct fixed_jb * fixed_jb_new(struct fixed_jb_conf *conf)
int fixed_jb_put(struct fixed_jb *jb, void *data, long ms, long ts, long now)
long next_delivery
static void release_jb_frame(struct fixed_jb *jb, struct fixed_jb_frame *frame)
#define ASTERISK_FILE_VERSION(file, version)
Register/unregister a source code file with the core.
Definition: asterisk.h:180