mirror of https://github.com/asterisk/asterisk
a new implementation of a fixed size jitterbuffer, as well as support for the existing adaptive jitterbuffer implementation. (issue #3854, Slav Klenov) Thank you very much to Slav Klenov of Securax and all of the people involved in the testing of this feature for all of your hard work! git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@31052 65c4cc65-6c06-0410-ace0-fbb531ad65f31.4
parent
503486e99d
commit
bb7dd96cfe
@ -1 +1 @@
|
||||
17
|
||||
18
|
||||
|
@ -0,0 +1,220 @@
|
||||
/*
|
||||
* abstract_jb: common implementation-independent jitterbuffer stuff
|
||||
*
|
||||
* Copyright (C) 2005, Attractel OOD
|
||||
*
|
||||
* Contributors:
|
||||
* Slav Klenov <slav@securax.org>
|
||||
*
|
||||
* Copyright on this file is disclaimed to Digium for inclusion in Asterisk
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/*! \file
|
||||
*
|
||||
* \brief Common implementation-independent jitterbuffer stuff.
|
||||
*
|
||||
* \author Slav Klenov <slav@securax.org>
|
||||
*/
|
||||
|
||||
#ifndef _ABSTRACT_JB_H_
|
||||
#define _ABSTRACT_JB_H_
|
||||
|
||||
#include <stdio.h>
|
||||
#include <sys/time.h>
|
||||
|
||||
#if defined(__cplusplus) || defined(c_plusplus)
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
struct ast_channel;
|
||||
struct ast_frame;
|
||||
|
||||
|
||||
/* Configuration flags */
|
||||
enum {
|
||||
AST_JB_ENABLED = (1 << 0),
|
||||
AST_JB_FORCED = (1 << 1),
|
||||
AST_JB_LOG = (1 << 2)
|
||||
};
|
||||
|
||||
#define AST_JB_IMPL_NAME_SIZE 12
|
||||
|
||||
/*!
|
||||
* \brief General jitterbuffer configuration.
|
||||
*/
|
||||
struct ast_jb_conf
|
||||
{
|
||||
/*! \brief Combination of the AST_JB_ENABLED, AST_JB_FORCED and AST_JB_LOG flags. */
|
||||
unsigned int flags;
|
||||
/*! \brief Max size of the jitterbuffer implementation. */
|
||||
long max_size;
|
||||
/*! \brief Resynchronization threshold of the jitterbuffer implementation. */
|
||||
long resync_threshold;
|
||||
/*! \brief Name of the jitterbuffer implementation to be used. */
|
||||
char impl[AST_JB_IMPL_NAME_SIZE];
|
||||
};
|
||||
|
||||
|
||||
/* Jitterbuffer configuration property names */
|
||||
#define AST_JB_CONF_PREFIX "jb"
|
||||
#define AST_JB_CONF_ENABLE "enable"
|
||||
#define AST_JB_CONF_FORCE "force"
|
||||
#define AST_JB_CONF_MAX_SIZE "maxsize"
|
||||
#define AST_JB_CONF_RESYNCH_THRESHOLD "resyncthreshold"
|
||||
#define AST_JB_CONF_IMPL "impl"
|
||||
#define AST_JB_CONF_LOG "log"
|
||||
|
||||
|
||||
struct ast_jb_impl;
|
||||
|
||||
|
||||
/*!
|
||||
* \brief General jitterbuffer state.
|
||||
*/
|
||||
struct ast_jb
|
||||
{
|
||||
/*! \brief Jitterbuffer configuration. */
|
||||
struct ast_jb_conf conf;
|
||||
/*! \brief Jitterbuffer implementation to be used. */
|
||||
struct ast_jb_impl *impl;
|
||||
/*! \brief Jitterbuffer object, passed to the implementation. */
|
||||
void *jbobj;
|
||||
/*! \brief The time the jitterbuffer was created. */
|
||||
struct timeval timebase;
|
||||
/*! \brief The time the next frame should be played. */
|
||||
long next;
|
||||
/*! \brief Voice format of the last frame in. */
|
||||
int last_format;
|
||||
/*! \brief File for frame timestamp tracing. */
|
||||
FILE *logfile;
|
||||
/*! \brief Jitterbuffer internal state flags. */
|
||||
unsigned int flags;
|
||||
};
|
||||
|
||||
|
||||
/*!
|
||||
* \brief Checks the need of a jb use in a generic bridge.
|
||||
* \param c0 first bridged channel.
|
||||
* \param c1 second bridged channel.
|
||||
*
|
||||
* Called from ast_generic_bridge() when two channels are entering in a bridge.
|
||||
* The function checks the need of a jitterbuffer, depending on both channel's
|
||||
* configuration and technology properties. As a result, this function sets
|
||||
* appropriate internal jb flags to the channels, determining further behaviour
|
||||
* of the bridged jitterbuffers.
|
||||
*
|
||||
* \return zero if there are no jitter buffers in use, non-zero if there are
|
||||
*/
|
||||
int ast_jb_do_usecheck(struct ast_channel *c0, struct ast_channel *c1);
|
||||
|
||||
|
||||
/*!
|
||||
* \brief Calculates the time, left to the closest delivery moment in a bridge.
|
||||
* \param c0 first bridged channel.
|
||||
* \param c1 second bridged channel.
|
||||
* \param time_left bridge time limit, or -1 if not set.
|
||||
*
|
||||
* Called from ast_generic_bridge() to determine the maximum time to wait for
|
||||
* activity in ast_waitfor_n() call. If neihter of the channels is using jb,
|
||||
* this function returns the time limit passed.
|
||||
*
|
||||
* \return maximum time to wait.
|
||||
*/
|
||||
int ast_jb_get_when_to_wakeup(struct ast_channel *c0, struct ast_channel *c1, int time_left);
|
||||
|
||||
|
||||
/*!
|
||||
* \brief Puts a frame into a channel jitterbuffer.
|
||||
* \param chan channel.
|
||||
* \param frame frame.
|
||||
*
|
||||
* Called from ast_generic_bridge() to put a frame into a channel's jitterbuffer.
|
||||
* The function will successfuly enqueue a frame if and only if:
|
||||
* 1. the channel is using a jitterbuffer (as determined by ast_jb_do_usecheck()),
|
||||
* 2. the frame's type is AST_FRAME_VOICE,
|
||||
* 3. the frame has timing info set and has length >= 2 ms,
|
||||
* 4. there is no some internal error happened (like failed memory allocation).
|
||||
* Frames, successfuly queued, should be delivered by the channel's jitterbuffer,
|
||||
* when their delivery time has came.
|
||||
* Frames, not successfuly queued, should be delivered immediately.
|
||||
* Dropped by the jb implementation frames are considered successfuly enqueued as
|
||||
* far as they should not be delivered at all.
|
||||
*
|
||||
* \return zero if the frame was queued, -1 if not.
|
||||
*/
|
||||
int ast_jb_put(struct ast_channel *chan, struct ast_frame *f);
|
||||
|
||||
|
||||
/*!
|
||||
* \brief Deliver the queued frames that should be delivered now for both channels.
|
||||
* \param c0 first bridged channel.
|
||||
* \param c1 second bridged channel.
|
||||
*
|
||||
* Called from ast_generic_bridge() to deliver any frames, that should be delivered
|
||||
* for the moment of invocation. Does nothing if neihter of the channels is using jb
|
||||
* or has any frames currently queued in. The function delivers frames usig ast_write()
|
||||
* each of the channels.
|
||||
*/
|
||||
void ast_jb_get_and_deliver(struct ast_channel *c0, struct ast_channel *c1);
|
||||
|
||||
|
||||
/*!
|
||||
* \brief Destroys jitterbuffer on a channel.
|
||||
* \param chan channel.
|
||||
*
|
||||
* Called from ast_channel_free() when a channel is destroyed.
|
||||
*/
|
||||
void ast_jb_destroy(struct ast_channel *chan);
|
||||
|
||||
|
||||
/*!
|
||||
* \brief Sets jitterbuffer configuration property.
|
||||
* \param conf configuration to store the property in.
|
||||
* \param varname property name.
|
||||
* \param value property value.
|
||||
*
|
||||
* Called from a channel driver to build a jitterbuffer configuration tipically when
|
||||
* reading a configuration file. It is not neccessary for a channel driver to know
|
||||
* each of the jb configuration property names. The jitterbuffer itself knows them.
|
||||
* The channel driver can pass each config var it reads through this function. It will
|
||||
* return 0 if the variable was consumed from the jb conf.
|
||||
*
|
||||
* \return zero if the property was set to the configuration, -1 if not.
|
||||
*/
|
||||
int ast_jb_read_conf(struct ast_jb_conf *conf, char *varname, char *value);
|
||||
|
||||
|
||||
/*!
|
||||
* \brief Configures a jitterbuffer on a channel.
|
||||
* \param chan channel to configure.
|
||||
* \param conf configuration to apply.
|
||||
*
|
||||
* Called from a channel driver when a channel is created and its jitterbuffer needs
|
||||
* to be configured.
|
||||
*/
|
||||
void ast_jb_configure(struct ast_channel *chan, const struct ast_jb_conf *conf);
|
||||
|
||||
|
||||
/*!
|
||||
* \brief Copies a channel's jitterbuffer configuration.
|
||||
* \param chan channel.
|
||||
* \param conf destination.
|
||||
*/
|
||||
void ast_jb_get_config(const struct ast_channel *chan, struct ast_jb_conf *conf);
|
||||
|
||||
|
||||
#if defined(__cplusplus) || defined(c_plusplus)
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* _ABSTRACT_JB_H_ */
|
@ -0,0 +1,351 @@
|
||||
/*
|
||||
* scx_jitterbuf: jitterbuffering algorithm
|
||||
*
|
||||
* Copyright (C) 2005, Attractel OOD
|
||||
*
|
||||
* Contributors:
|
||||
* Slav Klenov <slav@securax.org>
|
||||
*
|
||||
* Copyright on this file is disclaimed to Digium for inclusion in Asterisk
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/*! \file
|
||||
*
|
||||
* \brief Jitterbuffering algorithm.
|
||||
*
|
||||
* \author Slav Klenov <slav@securax.org>
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <assert.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "asterisk.h"
|
||||
ASTERISK_FILE_VERSION(__FILE__, "$Revision $")
|
||||
|
||||
#include "asterisk/utils.h"
|
||||
#include "scx_jitterbuf.h"
|
||||
|
||||
#undef SCX_JB_DEBUG
|
||||
|
||||
#ifdef SCX_JB_DEBUG
|
||||
#define ASSERT(a)
|
||||
#else
|
||||
#define ASSERT(a) assert(a)
|
||||
#endif
|
||||
|
||||
/*! \brief private scx_jb structure */
|
||||
struct scx_jb
|
||||
{
|
||||
struct scx_jb_frame *frames;
|
||||
struct scx_jb_frame *tail;
|
||||
struct scx_jb_conf conf;
|
||||
long rxcore;
|
||||
long delay;
|
||||
long next_delivery;
|
||||
int force_resynch;
|
||||
};
|
||||
|
||||
|
||||
static struct scx_jb_frame *alloc_jb_frame(struct scx_jb *jb);
|
||||
static void release_jb_frame(struct scx_jb *jb, struct scx_jb_frame *frame);
|
||||
static void get_jb_head(struct scx_jb *jb, struct scx_jb_frame *frame);
|
||||
static int resynch_jb(struct scx_jb *jb, void *data, long ms, long ts, long now);
|
||||
|
||||
static inline struct scx_jb_frame *alloc_jb_frame(struct scx_jb *jb)
|
||||
{
|
||||
return ast_calloc(1, sizeof(struct scx_jb_frame));
|
||||
}
|
||||
|
||||
static inline void release_jb_frame(struct scx_jb *jb, struct scx_jb_frame *frame)
|
||||
{
|
||||
free(frame);
|
||||
}
|
||||
|
||||
static void get_jb_head(struct scx_jb *jb, struct scx_jb_frame *frame)
|
||||
{
|
||||
struct scx_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 scx_jb_frame));
|
||||
|
||||
/* and release the frame */
|
||||
release_jb_frame(jb, fr);
|
||||
}
|
||||
|
||||
|
||||
struct scx_jb *scx_jb_new(struct scx_jb_conf *conf)
|
||||
{
|
||||
struct scx_jb *jb;
|
||||
|
||||
if (!(jb = ast_calloc(1, sizeof(*jb))))
|
||||
return NULL;
|
||||
|
||||
/* First copy our config */
|
||||
memcpy(&jb->conf, conf, sizeof(struct scx_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 = SCX_JB_SIZE_DEFAULT;
|
||||
|
||||
if (conf->resync_threshold < 1)
|
||||
conf->resync_threshold = SCX_JB_RESYNCH_THRESHOLD_DEFAULT;
|
||||
|
||||
/* Set the constant delay to the jitterbuf */
|
||||
jb->delay = conf->jbsize;
|
||||
|
||||
return jb;
|
||||
}
|
||||
|
||||
|
||||
void scx_jb_destroy(struct scx_jb *jb)
|
||||
{
|
||||
/* jitterbuf MUST be empty before it can be destroyed */
|
||||
ASSERT(jb->frames == NULL);
|
||||
|
||||
free(jb);
|
||||
}
|
||||
|
||||
|
||||
static int resynch_jb(struct scx_jb *jb, void *data, long ms, long ts, long now)
|
||||
{
|
||||
long diff, offset;
|
||||
struct scx_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 scx_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 SCX_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 scx_jb_put(jb, data, ms, ts, now);
|
||||
}
|
||||
|
||||
|
||||
void scx_jb_set_force_resynch(struct scx_jb *jb)
|
||||
{
|
||||
jb->force_resynch = 1;
|
||||
}
|
||||
|
||||
|
||||
int scx_jb_put_first(struct scx_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 scx_jb_put(jb, data, ms, ts, now);
|
||||
}
|
||||
|
||||
|
||||
int scx_jb_put(struct scx_jb *jb, void *data, long ms, long ts, long now)
|
||||
{
|
||||
struct scx_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 SCX_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 SCX_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 SCX_JB_OK;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
int scx_jb_get(struct scx_jb *jb, struct scx_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 SCX_JB_NOFRAME;
|
||||
}
|
||||
|
||||
/* Is the jb empty? */
|
||||
if (!jb->frames) {
|
||||
/* should interpolate a frame */
|
||||
/* update next */
|
||||
jb->next_delivery += interpl;
|
||||
|
||||
return SCX_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 SCX_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 SCX_JB_INTERP;
|
||||
}
|
||||
|
||||
/* we have a frame for playing now (get_jb_head() updates next) */
|
||||
get_jb_head(jb, frame);
|
||||
|
||||
return SCX_JB_OK;
|
||||
}
|
||||
|
||||
|
||||
long scx_jb_next(struct scx_jb *jb)
|
||||
{
|
||||
return jb->next_delivery;
|
||||
}
|
||||
|
||||
|
||||
int scx_jb_remove(struct scx_jb *jb, struct scx_jb_frame *frameout)
|
||||
{
|
||||
if (!jb->frames)
|
||||
return SCX_JB_NOFRAME;
|
||||
|
||||
get_jb_head(jb, frameout);
|
||||
|
||||
return SCX_JB_OK;
|
||||
}
|
@ -0,0 +1,93 @@
|
||||
/*
|
||||
* scx_jitterbuf: jitterbuffering algorithm
|
||||
*
|
||||
* Copyright (C) 2005, Attractel OOD
|
||||
*
|
||||
* Contributors:
|
||||
* Slav Klenov <slav@securax.org>
|
||||
*
|
||||
* Copyright on this file is disclaimed to Digium for inclusion in Asterisk
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/*! \file
|
||||
*
|
||||
* \brief Jitterbuffering algorithm.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef _SCX_JITTERBUF_H_
|
||||
#define _SCX_JITTERBUF_H_
|
||||
|
||||
#if defined(__cplusplus) || defined(c_plusplus)
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
|
||||
/* return codes */
|
||||
#define SCX_JB_OK 0
|
||||
#define SCX_JB_DROP 1
|
||||
#define SCX_JB_INTERP 2
|
||||
#define SCX_JB_NOFRAME 3
|
||||
|
||||
|
||||
/* defaults */
|
||||
#define SCX_JB_SIZE_DEFAULT 200
|
||||
#define SCX_JB_RESYNCH_THRESHOLD_DEFAULT 1000
|
||||
|
||||
|
||||
/* jb configuration properties */
|
||||
struct scx_jb_conf
|
||||
{
|
||||
long jbsize;
|
||||
long resync_threshold;
|
||||
};
|
||||
|
||||
|
||||
struct scx_jb_frame
|
||||
{
|
||||
void *data;
|
||||
long ts;
|
||||
long ms;
|
||||
long delivery;
|
||||
struct scx_jb_frame *next;
|
||||
struct scx_jb_frame *prev;
|
||||
};
|
||||
|
||||
|
||||
struct scx_jb;
|
||||
|
||||
|
||||
/* jb interface */
|
||||
|
||||
struct scx_jb * scx_jb_new(struct scx_jb_conf *conf);
|
||||
|
||||
void scx_jb_destroy(struct scx_jb *jb);
|
||||
|
||||
int scx_jb_put_first(struct scx_jb *jb, void *data, long ms, long ts, long now);
|
||||
|
||||
int scx_jb_put(struct scx_jb *jb, void *data, long ms, long ts, long now);
|
||||
|
||||
int scx_jb_get(struct scx_jb *jb, struct scx_jb_frame *frame, long now, long interpl);
|
||||
|
||||
long scx_jb_next(struct scx_jb *jb);
|
||||
|
||||
int scx_jb_remove(struct scx_jb *jb, struct scx_jb_frame *frameout);
|
||||
|
||||
void scx_jb_set_force_resynch(struct scx_jb *jb);
|
||||
|
||||
|
||||
#if defined(__cplusplus) || defined(c_plusplus)
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* _SCX_JITTERBUF_H_ */
|
Loading…
Reference in new issue