Thu Apr 28 2011 16:57:10

Asterisk developer's documentation


fixedjitterbuf.c File Reference

Jitterbuffering algorithm. More...

#include "asterisk.h"
#include <assert.h>
#include "asterisk/utils.h"
#include "fixedjitterbuf.h"
Include dependency graph for fixedjitterbuf.c:

Go to the source code of this file.

Data Structures

struct  fixed_jb
 private fixed_jb structure More...

Defines

#define ASSERT(a)   assert(a)

Functions

static struct fixed_jb_framealloc_jb_frame (struct fixed_jb *jb)
void fixed_jb_destroy (struct fixed_jb *jb)
int fixed_jb_get (struct fixed_jb *jb, struct fixed_jb_frame *frame, long now, long interpl)
struct fixed_jbfixed_jb_new (struct fixed_jb_conf *conf)
long fixed_jb_next (struct fixed_jb *jb)
int fixed_jb_put (struct fixed_jb *jb, void *data, long ms, long ts, long now)
int fixed_jb_put_first (struct fixed_jb *jb, void *data, long ms, long ts, long now)
int fixed_jb_remove (struct fixed_jb *jb, struct fixed_jb_frame *frameout)
void fixed_jb_set_force_resynch (struct fixed_jb *jb)
static void get_jb_head (struct fixed_jb *jb, struct fixed_jb_frame *frame)
static void release_jb_frame (struct fixed_jb *jb, struct fixed_jb_frame *frame)
static int resynch_jb (struct fixed_jb *jb, void *data, long ms, long ts, long now)

Detailed Description

Jitterbuffering algorithm.

Author:
Slav Klenov <slav@securax.org>

Definition in file fixedjitterbuf.c.


Define Documentation

#define ASSERT (   a)    assert(a)

Definition at line 42 of file fixedjitterbuf.c.

Referenced by fixed_jb_destroy(), fixed_jb_get(), fixed_jb_put(), and resynch_jb().


Function Documentation

static struct fixed_jb_frame * alloc_jb_frame ( struct fixed_jb jb) [static, read]

Definition at line 63 of file fixedjitterbuf.c.

References ast_calloc.

Referenced by fixed_jb_put().

{
   return ast_calloc(1, sizeof(*jb));
}
void fixed_jb_destroy ( struct fixed_jb jb)

Definition at line 125 of file fixedjitterbuf.c.

References ASSERT, ast_free, and fixed_jb::frames.

Referenced by jb_destroy_fixed().

{
   /* jitterbuf MUST be empty before it can be destroyed */
   ASSERT(jb->frames == NULL);
   
   ast_free(jb);
}
int fixed_jb_get ( struct fixed_jb jb,
struct fixed_jb_frame frame,
long  now,
long  interpl 
)

Definition at line 290 of file fixedjitterbuf.c.

References ASSERT, fixed_jb_frame::delivery, FIXED_JB_DROP, FIXED_JB_INTERP, FIXED_JB_NOFRAME, FIXED_JB_OK, frames, fixed_jb::frames, get_jb_head(), fixed_jb_frame::ms, and fixed_jb::next_delivery.

Referenced by jb_get_fixed().

{
   ASSERT(now >= 0);
   ASSERT(interpl >= 2);
   
   if (now < jb->next_delivery) {
      /* too early for the next frame */
      return FIXED_JB_NOFRAME;
   }
   
   /* Is the jb empty? */
   if (!jb->frames) {
      /* should interpolate a frame */
      /* update next */
      jb->next_delivery += interpl;
      
      return FIXED_JB_INTERP;
   }
   
   /* Isn't it too late for the first frame available in the jb? */
   if (now > jb->frames->delivery + jb->frames->ms) {
      /* yes - should drop this frame and update next to point the next frame (get_jb_head() does it) */
      get_jb_head(jb, frame);
      
      return FIXED_JB_DROP;
   }
   
   /* isn't it too early to play the first frame available? */
   if (now < jb->frames->delivery) {
      /* yes - should interpolate one frame */
      /* update next */
      jb->next_delivery += interpl;
      
      return FIXED_JB_INTERP;
   }
   
   /* we have a frame for playing now (get_jb_head() updates next) */
   get_jb_head(jb, frame);
   
   return FIXED_JB_OK;
}
struct fixed_jb* fixed_jb_new ( struct fixed_jb_conf conf) [read]

Definition at line 98 of file fixedjitterbuf.c.

References ast_calloc, fixed_jb::conf, fixed_jb::delay, FIXED_JB_RESYNCH_THRESHOLD_DEFAULT, FIXED_JB_SIZE_DEFAULT, fixed_jb_conf::jbsize, and fixed_jb_conf::resync_threshold.

Referenced by jb_create_fixed().

{
   struct fixed_jb *jb;
   
   if (!(jb = ast_calloc(1, sizeof(*jb))))
      return NULL;
   
   /* First copy our config */
   memcpy(&jb->conf, conf, sizeof(struct fixed_jb_conf));

   /* we dont need the passed config anymore - continue working with the saved one */
   conf = &jb->conf;
   
   /* validate the configuration */
   if (conf->jbsize < 1)
      conf->jbsize = FIXED_JB_SIZE_DEFAULT;

   if (conf->resync_threshold < 1)
      conf->resync_threshold = FIXED_JB_RESYNCH_THRESHOLD_DEFAULT;
   
   /* Set the constant delay to the jitterbuf */
   jb->delay = conf->jbsize;
   
   return jb;
}
long fixed_jb_next ( struct fixed_jb jb)

Definition at line 333 of file fixedjitterbuf.c.

References fixed_jb::next_delivery.

Referenced by jb_next_fixed().

{
   return jb->next_delivery;
}
int fixed_jb_put ( struct fixed_jb jb,
void *  data,
long  ms,
long  ts,
long  now 
)

Definition at line 196 of file fixedjitterbuf.c.

References alloc_jb_frame(), ASSERT, fixed_jb::conf, fixed_jb_frame::data, fixed_jb::delay, fixed_jb_frame::delivery, FIXED_JB_OK, fixed_jb::force_resynch, fixed_jb::frames, fixed_jb_frame::ms, fixed_jb_frame::next, fixed_jb::next_delivery, fixed_jb_frame::prev, fixed_jb_conf::resync_threshold, resynch_jb(), fixed_jb::rxcore, fixed_jb::tail, and fixed_jb_frame::ts.

Referenced by fixed_jb_put_first(), jb_put_fixed(), and resynch_jb().

{
   struct fixed_jb_frame *frame, *next, *newframe;
   long delivery;
   
   /* debug check the validity of the input params */
   ASSERT(data != NULL);
   /* do not allow frames shorter than 2 ms */
   ASSERT(ms >= 2);
   ASSERT(ts >= 0);
   ASSERT(now >= 0);
   
   delivery = jb->rxcore + jb->delay + ts;
   
   /* check if the new frame is not too late */
   if (delivery < jb->next_delivery) {
      /* should drop the frame, but let first resynch_jb() check if this is not a jump in ts, or
         the force resynch flag was not set. */
      return resynch_jb(jb, data, ms, ts, now);
   }
   
   /* what if the delivery time is bigger than next + delay? Seems like a frame for the future.
      However, allow more resync_threshold ms in advance */
   if (delivery > jb->next_delivery + jb->delay + jb->conf.resync_threshold) {
      /* should drop the frame, but let first resynch_jb() check if this is not a jump in ts, or
         the force resynch flag was not set. */
      return resynch_jb(jb, data, ms, ts, now);
   }

   /* find the right place in the frames list, sorted by delivery time */
   frame = jb->tail;
   while (frame && frame->delivery > delivery) {
      frame = frame->prev;
   }
   
   /* Check if the new delivery time is not covered already by the chosen frame */
   if (frame && (frame->delivery == delivery ||
               delivery < frame->delivery + frame->ms ||
               (frame->next && delivery + ms > frame->next->delivery)))
   {
      /* TODO: Should we check for resynch here? Be careful to do not allow threshold smaller than
         the size of the jb */
      
      /* should drop the frame, but let first resynch_jb() check if this is not a jump in ts, or
         the force resynch flag was not set. */
      return resynch_jb(jb, data, ms, ts, now);
   }
   
   /* Reset the force resynch flag */
   jb->force_resynch = 0;
   
   /* Get a new frame */
   newframe = alloc_jb_frame(jb);
   newframe->data = data;
   newframe->ts = ts;
   newframe->ms = ms;
   newframe->delivery = delivery;
   
   /* and insert it right on place */
   if (frame) {
      next = frame->next;
      frame->next = newframe;
      if (next) {
         newframe->next = next;
         next->prev = newframe;
      } else {
         /* insert after the last frame - should update tail */
         jb->tail = newframe;
         newframe->next = NULL;
      }
      newframe->prev = frame;
      
      return FIXED_JB_OK;
   } else if (!jb->frames) {
      /* the frame list is empty or thats just the first frame ever */
      /* tail should also be NULL is that case */
      ASSERT(jb->tail == NULL);
      jb->frames = jb->tail = newframe;
      newframe->next = NULL;
      newframe->prev = NULL;
      
      return FIXED_JB_OK;
   } else {
      /* insert on a first position - should update frames head */
      newframe->next = jb->frames;
      newframe->prev = NULL;
      jb->frames->prev = newframe;
      jb->frames = newframe;
      
      return FIXED_JB_OK;
   }
}
int fixed_jb_put_first ( struct fixed_jb jb,
void *  data,
long  ms,
long  ts,
long  now 
)

Definition at line 183 of file fixedjitterbuf.c.

References fixed_jb::delay, fixed_jb_put(), fixed_jb::next_delivery, fixed_jb::rxcore, and fixed_jb_frame::ts.

Referenced by jb_put_first_fixed(), and resynch_jb().

{
   /* this is our first frame - set the base of the receivers time */
   jb->rxcore = now - ts;
   
   /* init next for a first time - it should be the time the first frame should be played */
   jb->next_delivery = now + jb->delay;
   
   /* put the frame */
   return fixed_jb_put(jb, data, ms, ts, now);
}
int fixed_jb_remove ( struct fixed_jb jb,
struct fixed_jb_frame frameout 
)

Definition at line 339 of file fixedjitterbuf.c.

References FIXED_JB_NOFRAME, FIXED_JB_OK, fixed_jb::frames, and get_jb_head().

Referenced by jb_empty_and_reset_fixed(), and jb_remove_fixed().

{
   if (!jb->frames)
      return FIXED_JB_NOFRAME;
   
   get_jb_head(jb, frameout);
   
   return FIXED_JB_OK;
}
void fixed_jb_set_force_resynch ( struct fixed_jb jb)

Definition at line 177 of file fixedjitterbuf.c.

References fixed_jb::force_resynch.

Referenced by jb_force_resynch_fixed().

{
   jb->force_resynch = 1;
}
static void get_jb_head ( struct fixed_jb jb,
struct fixed_jb_frame frame 
) [static]

Definition at line 73 of file fixedjitterbuf.c.

References fixed_jb_frame::delivery, fixed_jb::frames, fixed_jb_frame::ms, fixed_jb_frame::next, fixed_jb::next_delivery, fixed_jb_frame::prev, release_jb_frame(), and fixed_jb::tail.

Referenced by fixed_jb_get(), and fixed_jb_remove().

{
   struct fixed_jb_frame *fr;
   
   /* unlink the frame */
   fr = jb->frames;
   jb->frames = fr->next;
   if (jb->frames) {
      jb->frames->prev = NULL;
   } else {
      /* the jb is empty - update tail */
      jb->tail = NULL;
   }
   
   /* update next */
   jb->next_delivery = fr->delivery + fr->ms;
   
   /* copy the destination */
   memcpy(frame, fr, sizeof(struct fixed_jb_frame));
   
   /* and release the frame */
   release_jb_frame(jb, fr);
}
static void release_jb_frame ( struct fixed_jb jb,
struct fixed_jb_frame frame 
) [inline, static]

Definition at line 68 of file fixedjitterbuf.c.

References ast_free.

Referenced by get_jb_head().

{
   ast_free(frame);
}
static int resynch_jb ( struct fixed_jb jb,
void *  data,
long  ms,
long  ts,
long  now 
) [static]

Definition at line 134 of file fixedjitterbuf.c.

References ASSERT, fixed_jb::conf, FIXED_JB_DROP, fixed_jb_put(), fixed_jb_put_first(), fixed_jb::force_resynch, fixed_jb::frames, fixed_jb_frame::ms, fixed_jb_frame::next, fixed_jb_conf::resync_threshold, fixed_jb::rxcore, fixed_jb::tail, and fixed_jb_frame::ts.

Referenced by fixed_jb_put().

{
   long diff, offset;
   struct fixed_jb_frame *frame;
   
   /* If jb is empty, just reinitialize the jb */
   if (!jb->frames) {
      /* debug check: tail should also be NULL */
      ASSERT(jb->tail == NULL);
      
      return fixed_jb_put_first(jb, data, ms, ts, now);
   }
   
   /* Adjust all jb state just as the new frame is with delivery = the delivery of the last
      frame (e.g. this one with max delivery) + the length of the last frame. */
   
   /* Get the diff in timestamps */
   diff = ts - jb->tail->ts;
   
   /* Ideally this should be just the length of the last frame. The deviation is the desired
      offset */
   offset = diff - jb->tail->ms;
   
   /* Do we really need to resynch, or this is just a frame for dropping? */
   if (!jb->force_resynch && (offset < jb->conf.resync_threshold && offset > -jb->conf.resync_threshold))
      return FIXED_JB_DROP;
   
   /* Reset the force resynch flag */
   jb->force_resynch = 0;
   
   /* apply the offset to the jb state */
   jb->rxcore -= offset;
   frame = jb->frames;
   while (frame) {
      frame->ts += offset;
      frame = frame->next;
   }
   
   /* now jb_put() should add the frame at a last position */
   return fixed_jb_put(jb, data, ms, ts, now);
}