123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355 |
- /*
- * Copyright (C) 2005, Attractel OOD
- *
- * Contributors:
- * Slav Klenov <slav@securax.org>
- *
- * See http://www.asterisk.org for more information about
- * the Asterisk project. Please do not directly contact
- * any of the maintainers of this project for assistance;
- * the project provides a web site, mailing lists and IRC
- * channels for your use.
- *
- * This program is free software, distributed under the terms of
- * the GNU General Public License Version 2. See the LICENSE file
- * at the top of the source tree.
- *
- * A license has been granted to Digium (via disclaimer) for the use of
- * this code.
- */
- /*! \file
- *
- * \brief Jitterbuffering algorithm.
- *
- * \author Slav Klenov <slav@securax.org>
- */
- /*** MODULEINFO
- <support_level>core</support_level>
- ***/
- #include "asterisk.h"
- ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
- #include <assert.h>
- #include "asterisk/utils.h"
- #include "fixedjitterbuf.h"
- #undef FIXED_JB_DEBUG
- #ifdef FIXED_JB_DEBUG
- #define ASSERT(a)
- #else
- #define ASSERT(a) assert(a)
- #endif
- /*! \brief private fixed_jb structure */
- struct fixed_jb
- {
- struct fixed_jb_frame *frames;
- struct fixed_jb_frame *tail;
- struct fixed_jb_conf conf;
- long rxcore;
- long delay;
- long next_delivery;
- int force_resynch;
- };
- static struct fixed_jb_frame *alloc_jb_frame(struct fixed_jb *jb);
- static void release_jb_frame(struct fixed_jb *jb, struct fixed_jb_frame *frame);
- static void get_jb_head(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);
- static inline struct fixed_jb_frame *alloc_jb_frame(struct fixed_jb *jb)
- {
- return ast_calloc(1, sizeof(*jb));
- }
- static inline void release_jb_frame(struct fixed_jb *jb, struct fixed_jb_frame *frame)
- {
- ast_free(frame);
- }
- static void get_jb_head(struct fixed_jb *jb, struct fixed_jb_frame *frame)
- {
- 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);
- }
- struct fixed_jb *fixed_jb_new(struct fixed_jb_conf *conf)
- {
- 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 don't 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;
- }
- void fixed_jb_destroy(struct fixed_jb *jb)
- {
- /* jitterbuf MUST be empty before it can be destroyed */
- ASSERT(jb->frames == NULL);
- ast_free(jb);
- }
- static int resynch_jb(struct fixed_jb *jb, void *data, long ms, long ts, long now)
- {
- 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);
- }
- void fixed_jb_set_force_resynch(struct fixed_jb *jb)
- {
- jb->force_resynch = 1;
- }
- int fixed_jb_put_first(struct fixed_jb *jb, void *data, long ms, long ts, long now)
- {
- /* 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_put(struct fixed_jb *jb, void *data, long ms, long ts, long now)
- {
- 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_get(struct fixed_jb *jb, struct fixed_jb_frame *frame, long now, long interpl)
- {
- 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;
- }
- long fixed_jb_next(struct fixed_jb *jb)
- {
- return jb->next_delivery;
- }
- int fixed_jb_remove(struct fixed_jb *jb, struct fixed_jb_frame *frameout)
- {
- if (!jb->frames)
- return FIXED_JB_NOFRAME;
- get_jb_head(jb, frameout);
- return FIXED_JB_OK;
- }
- int fixed_jb_is_late(struct fixed_jb *jb, long ts)
- {
- return jb->rxcore + jb->delay + ts < jb->next_delivery;
- }
|