Sat Aug 6 00:39:28 2011

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: 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 }

Generated on Sat Aug 6 00:39:28 2011 for Asterisk - the Open Source PBX by  doxygen 1.4.7