![]() |
Home page |
Mailing list |
Docs
Asterisk developer's documentation :: Codename Pineapple
fixedjitterbuf.c
Go to the documentation of this file.
00001 /* 00002 * Copyright (C) 2005, Attractel OOD 00003 * 00004 * Contributors: 00005 * Slav Klenov <slav@securax.org> 00006 * 00007 * See http://www.asterisk.org for more information about 00008 * the Asterisk project. Please do not directly contact 00009 * any of the maintainers of this project for assistance; 00010 * the project provides a web site, mailing lists and IRC 00011 * channels for your use. 00012 * 00013 * This program is free software, distributed under the terms of 00014 * the GNU General Public License Version 2. See the LICENSE file 00015 * at the top of the source tree. 00016 * 00017 * A license has been granted to Digium (via disclaimer) for the use of 00018 * this code. 00019 */ 00020 00021 /*! \file 00022 * 00023 * \brief Jitterbuffering algorithm. 00024 * 00025 * \author Slav Klenov <slav@securax.org> 00026 */ 00027 00028 #include "asterisk.h" 00029 00030 ASTERISK_FILE_VERSION(__FILE__, "$Revision: 40722 $") 00031 00032 #include <stdio.h> 00033 #include <stdlib.h> 00034 #include <assert.h> 00035 #include <string.h> 00036 #include <unistd.h> 00037 00038 #include "asterisk/utils.h" 00039 #include "fixedjitterbuf.h" 00040 00041 #undef FIXED_JB_DEBUG 00042 00043 #ifdef FIXED_JB_DEBUG 00044 #define ASSERT(a) 00045 #else 00046 #define ASSERT(a) assert(a) 00047 #endif 00048 00049 /*! \brief private fixed_jb structure */ 00050 struct fixed_jb 00051 { 00052 struct fixed_jb_frame *frames; 00053 struct fixed_jb_frame *tail; 00054 struct fixed_jb_conf conf; 00055 long rxcore; 00056 long delay; 00057 long next_delivery; 00058 int force_resynch; 00059 }; 00060 00061 00062 static struct fixed_jb_frame *alloc_jb_frame(struct fixed_jb *jb); 00063 static void release_jb_frame(struct fixed_jb *jb, struct fixed_jb_frame *frame); 00064 static void get_jb_head(struct fixed_jb *jb, struct fixed_jb_frame *frame); 00065 static int resynch_jb(struct fixed_jb *jb, void *data, long ms, long ts, long now); 00066 00067 static inline struct fixed_jb_frame *alloc_jb_frame(struct fixed_jb *jb) 00068 { 00069 return ast_calloc(1, sizeof(struct fixed_jb_frame)); 00070 } 00071 00072 static inline void release_jb_frame(struct fixed_jb *jb, struct fixed_jb_frame *frame) 00073 { 00074 free(frame); 00075 } 00076 00077 static void get_jb_head(struct fixed_jb *jb, struct fixed_jb_frame *frame) 00078 { 00079 struct fixed_jb_frame *fr; 00080 00081 /* unlink the frame */ 00082 fr = jb->frames; 00083 jb->frames = fr->next; 00084 if (jb->frames) { 00085 jb->frames->prev = NULL; 00086 } else { 00087 /* the jb is empty - update tail */ 00088 jb->tail = NULL; 00089 } 00090 00091 /* update next */ 00092 jb->next_delivery = fr->delivery + fr->ms; 00093 00094 /* copy the destination */ 00095 memcpy(frame, fr, sizeof(struct fixed_jb_frame)); 00096 00097 /* and release the frame */ 00098 release_jb_frame(jb, fr); 00099 } 00100 00101 00102 struct fixed_jb *fixed_jb_new(struct fixed_jb_conf *conf) 00103 { 00104 struct fixed_jb *jb; 00105 00106 if (!(jb = ast_calloc(1, sizeof(*jb)))) 00107 return NULL; 00108 00109 /* First copy our config */ 00110 memcpy(&jb->conf, conf, sizeof(struct fixed_jb_conf)); 00111 00112 /* we dont need the passed config anymore - continue working with the saved one */ 00113 conf = &jb->conf; 00114 00115 /* validate the configuration */ 00116 if (conf->jbsize < 1) 00117 conf->jbsize = FIXED_JB_SIZE_DEFAULT; 00118 00119 if (conf->resync_threshold < 1) 00120 conf->resync_threshold = FIXED_JB_RESYNCH_THRESHOLD_DEFAULT; 00121 00122 /* Set the constant delay to the jitterbuf */ 00123 jb->delay = conf->jbsize; 00124 00125 return jb; 00126 } 00127 00128 00129 void fixed_jb_destroy(struct fixed_jb *jb) 00130 { 00131 /* jitterbuf MUST be empty before it can be destroyed */ 00132 ASSERT(jb->frames == NULL); 00133 00134 free(jb); 00135 } 00136 00137 00138 static int resynch_jb(struct fixed_jb *jb, void *data, long ms, long ts, long now) 00139 { 00140 long diff, offset; 00141 struct fixed_jb_frame *frame; 00142 00143 /* If jb is empty, just reinitialize the jb */ 00144 if (!jb->frames) { 00145 /* debug check: tail should also be NULL */ 00146 ASSERT(jb->tail == NULL); 00147 00148 return fixed_jb_put_first(jb, data, ms, ts, now); 00149 } 00150 00151 /* Adjust all jb state just as the new frame is with delivery = the delivery of the last 00152 frame (e.g. this one with max delivery) + the length of the last frame. */ 00153 00154 /* Get the diff in timestamps */ 00155 diff = ts - jb->tail->ts; 00156 00157 /* Ideally this should be just the length of the last frame. The deviation is the desired 00158 offset */ 00159 offset = diff - jb->tail->ms; 00160 00161 /* Do we really need to resynch, or this is just a frame for dropping? */ 00162 if (!jb->force_resynch && (offset < jb->conf.resync_threshold && offset > -jb->conf.resync_threshold)) 00163 return FIXED_JB_DROP; 00164 00165 /* Reset the force resynch flag */ 00166 jb->force_resynch = 0; 00167 00168 /* apply the offset to the jb state */ 00169 jb->rxcore -= offset; 00170 frame = jb->frames; 00171 while (frame) { 00172 frame->ts += offset; 00173 frame = frame->next; 00174 } 00175 00176 /* now jb_put() should add the frame at a last position */ 00177 return fixed_jb_put(jb, data, ms, ts, now); 00178 } 00179 00180 00181 void fixed_jb_set_force_resynch(struct fixed_jb *jb) 00182 { 00183 jb->force_resynch = 1; 00184 } 00185 00186 00187 int fixed_jb_put_first(struct fixed_jb *jb, void *data, long ms, long ts, long now) 00188 { 00189 /* this is our first frame - set the base of the receivers time */ 00190 jb->rxcore = now - ts; 00191 00192 /* init next for a first time - it should be the time the first frame should be played */ 00193 jb->next_delivery = now + jb->delay; 00194 00195 /* put the frame */ 00196 return fixed_jb_put(jb, data, ms, ts, now); 00197 } 00198 00199 00200 int fixed_jb_put(struct fixed_jb *jb, void *data, long ms, long ts, long now) 00201 { 00202 struct fixed_jb_frame *frame, *next, *newframe; 00203 long delivery; 00204 00205 /* debug check the validity of the input params */ 00206 ASSERT(data != NULL); 00207 /* do not allow frames shorter than 2 ms */ 00208 ASSERT(ms >= 2); 00209 ASSERT(ts >= 0); 00210 ASSERT(now >= 0); 00211 00212 delivery = jb->rxcore + jb->delay + ts; 00213 00214 /* check if the new frame is not too late */ 00215 if (delivery < jb->next_delivery) { 00216 /* should drop the frame, but let first resynch_jb() check if this is not a jump in ts, or 00217 the force resynch flag was not set. */ 00218 return resynch_jb(jb, data, ms, ts, now); 00219 } 00220 00221 /* what if the delivery time is bigger than next + delay? Seems like a frame for the future. 00222 However, allow more resync_threshold ms in advance */ 00223 if (delivery > jb->next_delivery + jb->delay + jb->conf.resync_threshold) { 00224 /* should drop the frame, but let first resynch_jb() check if this is not a jump in ts, or 00225 the force resynch flag was not set. */ 00226 return resynch_jb(jb, data, ms, ts, now); 00227 } 00228 00229 /* find the right place in the frames list, sorted by delivery time */ 00230 frame = jb->tail; 00231 while (frame && frame->delivery > delivery) { 00232 frame = frame->prev; 00233 } 00234 00235 /* Check if the new delivery time is not covered already by the chosen frame */ 00236 if (frame && (frame->delivery == delivery || 00237 delivery < frame->delivery + frame->ms || 00238 (frame->next && delivery + ms > frame->next->delivery))) 00239 { 00240 /* TODO: Should we check for resynch here? Be careful to do not allow threshold smaller than 00241 the size of the jb */ 00242 00243 /* should drop the frame, but let first resynch_jb() check if this is not a jump in ts, or 00244 the force resynch flag was not set. */ 00245 return resynch_jb(jb, data, ms, ts, now); 00246 } 00247 00248 /* Reset the force resynch flag */ 00249 jb->force_resynch = 0; 00250 00251 /* Get a new frame */ 00252 newframe = alloc_jb_frame(jb); 00253 newframe->data = data; 00254 newframe->ts = ts; 00255 newframe->ms = ms; 00256 newframe->delivery = delivery; 00257 00258 /* and insert it right on place */ 00259 if (frame) { 00260 next = frame->next; 00261 frame->next = newframe; 00262 if (next) { 00263 newframe->next = next; 00264 next->prev = newframe; 00265 } else { 00266 /* insert after the last frame - should update tail */ 00267 jb->tail = newframe; 00268 newframe->next = NULL; 00269 } 00270 newframe->prev = frame; 00271 00272 return FIXED_JB_OK; 00273 } else if (!jb->frames) { 00274 /* the frame list is empty or thats just the first frame ever */ 00275 /* tail should also be NULL is that case */ 00276 ASSERT(jb->tail == NULL); 00277 jb->frames = jb->tail = newframe; 00278 newframe->next = NULL; 00279 newframe->prev = NULL; 00280 00281 return FIXED_JB_OK; 00282 } else { 00283 /* insert on a first position - should update frames head */ 00284 newframe->next = jb->frames; 00285 newframe->prev = NULL; 00286 jb->frames->prev = newframe; 00287 jb->frames = newframe; 00288 00289 return FIXED_JB_OK; 00290 } 00291 } 00292 00293 00294 int fixed_jb_get(struct fixed_jb *jb, struct fixed_jb_frame *frame, long now, long interpl) 00295 { 00296 ASSERT(now >= 0); 00297 ASSERT(interpl >= 2); 00298 00299 if (now < jb->next_delivery) { 00300 /* too early for the next frame */ 00301 return FIXED_JB_NOFRAME; 00302 } 00303 00304 /* Is the jb empty? */ 00305 if (!jb->frames) { 00306 /* should interpolate a frame */ 00307 /* update next */ 00308 jb->next_delivery += interpl; 00309 00310 return FIXED_JB_INTERP; 00311 } 00312 00313 /* Isn't it too late for the first frame available in the jb? */ 00314 if (now > jb->frames->delivery + jb->frames->ms) { 00315 /* yes - should drop this frame and update next to point the next frame (get_jb_head() does it) */ 00316 get_jb_head(jb, frame); 00317 00318 return FIXED_JB_DROP; 00319 } 00320 00321 /* isn't it too early to play the first frame available? */ 00322 if (now < jb->frames->delivery) { 00323 /* yes - should interpolate one frame */ 00324 /* update next */ 00325 jb->next_delivery += interpl; 00326 00327 return FIXED_JB_INTERP; 00328 } 00329 00330 /* we have a frame for playing now (get_jb_head() updates next) */ 00331 get_jb_head(jb, frame); 00332 00333 return FIXED_JB_OK; 00334 } 00335 00336 00337 long fixed_jb_next(struct fixed_jb *jb) 00338 { 00339 return jb->next_delivery; 00340 } 00341 00342 00343 int fixed_jb_remove(struct fixed_jb *jb, struct fixed_jb_frame *frameout) 00344 { 00345 if (!jb->frames) 00346 return FIXED_JB_NOFRAME; 00347 00348 get_jb_head(jb, frameout); 00349 00350 return FIXED_JB_OK; 00351 }