Wed Oct 28 11:51:03 2009

Asterisk developer's documentation


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

Generated on Wed Oct 28 11:51:03 2009 for Asterisk - the Open Source PBX by  doxygen 1.5.6