diff --git a/.cleancount b/.cleancount index 98d9bcb75a..3c032078a4 100644 --- a/.cleancount +++ b/.cleancount @@ -1 +1 @@ -17 +18 diff --git a/Makefile b/Makefile index 3f62202511..540d7a5bc7 100644 --- a/Makefile +++ b/Makefile @@ -282,7 +282,7 @@ OBJS=io.o sched.o logger.o frame.o loader.o config.o channel.o \ astmm.o enum.o srv.o dns.o aescrypt.o aestab.o aeskey.o \ utils.o plc.o jitterbuf.o dnsmgr.o devicestate.o \ netsock.o slinfactory.o ast_expr2.o ast_expr2f.o \ - cryptostub.o sha1.o http.o + cryptostub.o sha1.o http.o scx_jitterbuf.o abstract_jb.o # we need to link in the objects statically, not as a library, because # otherwise modules will not have them available if none of the static diff --git a/channel.c b/channel.c index 59789e93f3..545e00231c 100644 --- a/channel.c +++ b/channel.c @@ -1012,6 +1012,9 @@ void ast_channel_free(struct ast_channel *chan) while ((vardata = AST_LIST_REMOVE_HEAD(headp, entries))) ast_var_delete(vardata); + /* Destroy the jitterbuffer */ + ast_jb_destroy(chan); + ast_string_field_free_all(chan); free(chan); AST_LIST_UNLOCK(&channels); @@ -3303,6 +3306,9 @@ static enum ast_bridge_result ast_generic_bridge(struct ast_channel *c0, struct int watch_c0_dtmf; int watch_c1_dtmf; void *pvt0, *pvt1; + /* Indicates whether a frame was queued into a jitterbuffer */ + int frame_put_in_jb = 0; + int jb_in_use; int to; cs[0] = c0; @@ -3314,6 +3320,9 @@ static enum ast_bridge_result ast_generic_bridge(struct ast_channel *c0, struct watch_c0_dtmf = config->flags & AST_BRIDGE_DTMF_CHANNEL_0; watch_c1_dtmf = config->flags & AST_BRIDGE_DTMF_CHANNEL_1; + /* Check the need of a jitterbuffer for each channel */ + jb_in_use = ast_jb_do_usecheck(c0, c1); + for (;;) { struct ast_channel *who, *other; @@ -3332,9 +3341,15 @@ static enum ast_bridge_result ast_generic_bridge(struct ast_channel *c0, struct } } else to = -1; + /* Calculate the appropriate max sleep interval - in general, this is the time, + left to the closest jb delivery moment */ + if (jb_in_use) + to = ast_jb_get_when_to_wakeup(c0, c1, to); who = ast_waitfor_n(cs, 2, &to); if (!who) { - ast_log(LOG_DEBUG, "Nobody there, continuing...\n"); + /* No frame received within the specified timeout - check if we have to deliver now */ + if (jb_in_use) + ast_jb_get_and_deliver(c0, c1); if (c0->_softhangup == AST_SOFTHANGUP_UNBRIDGE || c1->_softhangup == AST_SOFTHANGUP_UNBRIDGE) { if (c0->_softhangup == AST_SOFTHANGUP_UNBRIDGE) c0->_softhangup = 0; @@ -3354,6 +3369,9 @@ static enum ast_bridge_result ast_generic_bridge(struct ast_channel *c0, struct } other = (who == c0) ? c1 : c0; /* the 'other' channel */ + /* Try add the frame info the who's bridged channel jitterbuff */ + if (jb_in_use) + frame_put_in_jb = !ast_jb_put(other, f); if ((f->frametype == AST_FRAME_CONTROL) && !(config->flags & AST_BRIDGE_IGNORE_SIGS)) { int bridge_exit = 0; @@ -3390,8 +3408,13 @@ static enum ast_bridge_result ast_generic_bridge(struct ast_channel *c0, struct ast_log(LOG_DEBUG, "Got DTMF on channel (%s)\n", who->name); break; } - /* other frames go to the other side */ - ast_write(other, f); + /* Write immediately frames, not passed through jb */ + if (!frame_put_in_jb) + ast_write(other, f); + + /* Check if we have to deliver now */ + if (jb_in_use) + ast_jb_get_and_deliver(c0, c1); } /* XXX do we want to pass on also frames not matched above ? */ ast_frfree(f); diff --git a/channels/chan_alsa.c b/channels/chan_alsa.c index e85b051eed..a3cbfa2bc6 100644 --- a/channels/chan_alsa.c +++ b/channels/chan_alsa.c @@ -70,6 +70,17 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$") #include "alsa-monitor.h" #endif +#include "asterisk/abstract_jb.h" +/* Global jitterbuffer configuration - by default, jb is disabled */ +static struct ast_jb_conf default_jbconf = +{ + .flags = 0, + .max_size = -1, + .resync_threshold = -1, + .impl = "" +}; +static struct ast_jb_conf global_jbconf; + #define DEBUG 0 /* Which device to use */ #define ALSA_INDEV "hw:0,0" @@ -812,6 +823,8 @@ static struct ast_channel *alsa_new(struct chan_alsa_pvt *p, int state) tmp = NULL; } } + if (tmp) + ast_jb_configure(tmp, &global_jbconf); } return tmp; } @@ -1051,9 +1064,18 @@ static int load_module(void *mod) int x; struct ast_config *cfg; struct ast_variable *v; + + /* Copy the default jb config over global_jbconf */ + memcpy(&global_jbconf, &default_jbconf, sizeof(struct ast_jb_conf)); + if ((cfg = ast_config_load(config))) { v = ast_variable_browse(cfg, "general"); while(v) { + /* handle jb conf */ + if (!ast_jb_read_conf(&global_jbconf, v->name, v->value)) { + v = v->next; + continue; + } if (!strcasecmp(v->name, "autoanswer")) autoanswer = ast_true(v->value); else if (!strcasecmp(v->name, "silencesuppression")) diff --git a/channels/chan_iax2.c b/channels/chan_iax2.c index ae92d33601..9424c117bf 100644 --- a/channels/chan_iax2.c +++ b/channels/chan_iax2.c @@ -1543,6 +1543,7 @@ static int __do_deliver(void *data) the IAX thread with the iaxsl lock held. */ struct iax_frame *fr = data; fr->retrans = -1; + fr->af.has_timing_info = 0; if (iaxs[fr->callno] && !ast_test_flag(iaxs[fr->callno], IAX_ALREADYGONE)) iax2_queue_frame(fr->callno, &fr->af); /* Free our iax frame */ diff --git a/channels/chan_oss.c b/channels/chan_oss.c index 415539a83a..a25e7819c7 100644 --- a/channels/chan_oss.c +++ b/channels/chan_oss.c @@ -82,6 +82,17 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$") #include "ring10.h" #include "answer.h" +#include "asterisk/abstract_jb.h" +/* Global jitterbuffer configuration - by default, jb is disabled */ +static struct ast_jb_conf default_jbconf = +{ + .flags = 0, + .max_size = -1, + .resync_threshold = -1, + .impl = "" +}; +static struct ast_jb_conf global_jbconf; + /* * Basic mode of operation: * @@ -141,6 +152,33 @@ START_CONFIG ; queuesize = 10 ; frames in device driver ; frags = 8 ; argument to SETFRAGMENT + ;------------------------------ JITTER BUFFER CONFIGURATION -------------------------- + ; jbenable = yes ; Enables the use of a jitterbuffer on the receiving side of an + ; OSS channel. Defaults to "no". An enabled jitterbuffer will + ; be used only if the sending side can create and the receiving + ; side can not accept jitter. The ZAP channel can't accept jitter, + ; thus an enabled jitterbuffer on the receive ZAP side will always + ; be used if the sending side can create jitter or if ZAP jb is + ; forced. + + ; jbforce = no ; Forces the use of a jitterbuffer on the receive side of a ZAP + ; channel. Defaults to "no". + + ; jbmaxsize = 200 ; Max length of the jitterbuffer in milliseconds. + + ; jbresyncthreshold = 1000 ; Jump in the frame timestamps over which the jitterbuffer is + ; resynchronized. Useful to improve the quality of the voice, with + ; big jumps in/broken timestamps, usualy sent from exotic devices + ; and programs. Defaults to 1000. + + ; jbimpl = fixed ; Jitterbuffer implementation, used on the receiving side of a SIP + ; channel. Two implementation are currenlty available - "fixed" + ; (with size always equals to jbmax-size) and "adaptive" (with + ; variable size, actually the new jb of IAX2). Defaults to fixed. + + ; jblog = no ; Enables jitterbuffer frame logging. Defaults to "no". + ;----------------------------------------------------------------------------------- + [card1] ; device = /dev/dsp1 ; alternate device @@ -981,6 +1019,9 @@ static struct ast_channel *oss_new(struct chan_oss_pvt *o, /* XXX what about usecnt ? */ } } + if (c) + ast_jb_configure(c, &global_jbconf); + return c; } @@ -1407,6 +1448,10 @@ static struct chan_oss_pvt * store_config(struct ast_config *cfg, char *ctg) for (v = ast_variable_browse(cfg, ctg);v; v=v->next) { M_START(v->name, v->value); + /* handle jb conf */ + if (!ast_jb_read_conf(&global_jbconf, v->name, v->value)) + continue; + M_BOOL("autoanswer", o->autoanswer) M_BOOL("autohangup", o->autohangup) M_BOOL("overridecontext", o->overridecontext) @@ -1472,6 +1517,9 @@ static int load_module(void *mod) int i; struct ast_config *cfg; + /* Copy the default jb config over global_jbconf */ + memcpy(&global_jbconf, &default_jbconf, sizeof(struct ast_jb_conf)); + /* load config file */ cfg = ast_config_load(config); if (cfg != NULL) { diff --git a/channels/chan_sip.c b/channels/chan_sip.c index d3f329deb6..732a6b141e 100644 --- a/channels/chan_sip.c +++ b/channels/chan_sip.c @@ -202,6 +202,17 @@ static int expiry = DEFAULT_EXPIRY; #define INITIAL_CSEQ 101 /*!< our initial sip sequence number */ +#include "asterisk/abstract_jb.h" +/* Global jitterbuffer configuration - by default, jb is disabled */ +static struct ast_jb_conf default_jbconf = +{ + .flags = 0, + .max_size = -1, + .resync_threshold = -1, + .impl = "" +}; +static struct ast_jb_conf global_jbconf; + static const char tdesc[] = "Session Initiation Protocol (SIP)"; static const char config[] = "sip.conf"; static const char notify_config[] = "sip_notify.conf"; @@ -850,6 +861,7 @@ static struct sip_pvt { struct ast_variable *chanvars; /*!< Channel variables to set for inbound call */ struct sip_pvt *next; /*!< Next dialog in chain */ struct sip_invite_param *options; /*!< Options for INVITE */ + struct ast_jb_conf jbconf; } *iflist = NULL; #define FLAG_RESPONSE (1 << 0) @@ -1256,7 +1268,7 @@ static const struct ast_channel_tech sip_tech = { .type = "SIP", .description = "Session Initiation Protocol (SIP)", .capabilities = ((AST_FORMAT_MAX_AUDIO << 1) - 1), - .properties = AST_CHAN_TP_WANTSJITTER, + .properties = AST_CHAN_TP_WANTSJITTER | AST_CHAN_TP_CREATESJITTER, .requester = sip_request_call, .devicestate = sip_devicestate, .call = sip_call, @@ -3312,7 +3324,11 @@ static struct ast_channel *sip_new(struct sip_pvt *i, int state, const char *tit if (recordhistory) append_history(i, "NewChan", "Channel %s - from %s", tmp->name, i->callid); - + + /* Configure the new channel jb */ + if (tmp && i && i->rtp) + ast_jb_configure(tmp, &i->jbconf); + return tmp; } @@ -3647,6 +3663,9 @@ static struct sip_pvt *sip_alloc(ast_string_field callid, struct sockaddr_in *si p->noncodeccapability |= AST_RTP_DTMF; ast_string_field_set(p, context, default_context); + /* Assign default jb conf to the new sip_pvt */ + memcpy(&p->jbconf, &global_jbconf, sizeof(struct ast_jb_conf)); + /* Add to active dialog list */ ast_mutex_lock(&iflock); p->next = iflist; @@ -13247,7 +13266,6 @@ static struct sip_peer *build_peer(const char *name, struct ast_variable *v, int for (; v; v = v->next) { if (handle_common_options(&peerflags[0], &mask[0], v)) continue; - if (realtime && !strcasecmp(v->name, "regseconds")) { ast_get_time_t(v->value, ®seconds, 0, NULL); } else if (realtime && !strcasecmp(v->name, "ipaddr") && !ast_strlen_zero(v->value) ) { @@ -13540,12 +13558,19 @@ static int reload_config(enum channelreloadreason reason) global_relaxdtmf = FALSE; global_callevents = FALSE; global_t1min = DEFAULT_T1MIN; + + /* Copy the default jb config over global_jbconf */ + memcpy(&global_jbconf, &default_jbconf, sizeof(struct ast_jb_conf)); + ast_clear_flag(&global_flags[1], SIP_PAGE2_VIDEOSUPPORT); /* Read the [general] config section of sip.conf (or from realtime config) */ for (v = ast_variable_browse(cfg, "general"); v; v = v->next) { if (handle_common_options(&global_flags[0], &dummy[0], v)) continue; + /* handle jb conf */ + if (!ast_jb_read_conf(&global_jbconf, v->name, v->value)) + continue; /* Create the interface list */ if (!strcasecmp(v->name, "context")) { diff --git a/channels/chan_zap.c b/channels/chan_zap.c index 60b24e791e..016bd1a39c 100644 --- a/channels/chan_zap.c +++ b/channels/chan_zap.c @@ -109,6 +109,17 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$") #define SMDI_MD_WAIT_TIMEOUT 1500 /* 1.5 seconds */ #endif +#include "asterisk/abstract_jb.h" +/* Global jitterbuffer configuration - by default, jb is disabled */ +static struct ast_jb_conf default_jbconf = +{ + .flags = 0, + .max_size = -1, + .resync_threshold = -1, + .impl = "" +}; +static struct ast_jb_conf global_jbconf; + #if !defined(ZT_SIG_EM_E1) || (defined(HAVE_LIBPRI) && !defined(ZT_SIG_HARDHDLC)) #error "Your zaptel is too old. please update" #endif @@ -684,6 +695,7 @@ static struct zt_pvt { #endif int polarity; int dsp_features; + struct ast_jb_conf jbconf; } *iflist = NULL, *ifend = NULL; @@ -5195,6 +5207,9 @@ static struct ast_channel *zt_new(struct zt_pvt *i, int state, int startpbx, int } } else ast_log(LOG_WARNING, "Unable to allocate channel structure\n"); + /* Configure the new channel jb */ + if (tmp && i) + ast_jb_configure(tmp, &i->jbconf); return tmp; } @@ -6988,6 +7003,8 @@ static struct zt_pvt *mkintf(int channel, int signalling, int outsignalling, int for (x=0;x<3;x++) tmp->subs[x].zfd = -1; tmp->channel = channel; + /* Assign default jb conf to the new zt_pvt */ + memcpy(&tmp->jbconf, &global_jbconf, sizeof(struct ast_jb_conf)); } if (tmp) { @@ -10200,6 +10217,7 @@ static int setup_zap(int reload) { struct ast_config *cfg; struct ast_variable *v; + struct ast_variable *vjb; struct zt_pvt *tmp; char *chan; char *c; @@ -10289,6 +10307,11 @@ static int setup_zap(int reload) } #endif v = ast_variable_browse(cfg, "channels"); + /* Copy the default jb config over global_jbconf */ + memcpy(&global_jbconf, &default_jbconf, sizeof(struct ast_jb_conf)); + /* Traverse all variables to handle jb conf */ + for (vjb = v; vjb; vjb = vjb->next) + ast_jb_read_conf(&global_jbconf, vjb->name, vjb->value); while(v) { /* Create the interface list */ if (!strcasecmp(v->name, "channel") diff --git a/configs/alsa.conf.sample b/configs/alsa.conf.sample index cba49db178..bfe77101f0 100644 --- a/configs/alsa.conf.sample +++ b/configs/alsa.conf.sample @@ -29,3 +29,31 @@ extension=s ; To set which ALSA device to use, change this parameter ;input_device=hw:0,0 ;output_device=hw:0,0 + +;------------------------------ JITTER BUFFER CONFIGURATION -------------------------- +; jbenable = yes ; Enables the use of a jitterbuffer on the receiving side of an + ; ALSA channel. Defaults to "no". An enabled jitterbuffer will + ; be used only if the sending side can create and the receiving + ; side can not accept jitter. The ZAP channel can't accept jitter, + ; thus an enabled jitterbuffer on the receive ZAP side will always + ; be used if the sending side can create jitter or if ZAP jb is + ; forced. + +; jbforce = no ; Forces the use of a jitterbuffer on the receive side of a ZAP + ; channel. Defaults to "no". + +; jbmaxsize = 200 ; Max length of the jitterbuffer in milliseconds. + +; jbresyncthreshold = 1000 ; Jump in the frame timestamps over which the jitterbuffer is + ; resynchronized. Useful to improve the quality of the voice, with + ; big jumps in/broken timestamps, usualy sent from exotic devices + ; and programs. Defaults to 1000. + +; jbimpl = fixed ; Jitterbuffer implementation, used on the receiving side of a SIP + ; channel. Two implementation are currenlty available - "fixed" + ; (with size always equals to jbmax-size) and "adaptive" (with + ; variable size, actually the new jb of IAX2). Defaults to fixed. + +; jblog = no ; Enables jitterbuffer frame logging. Defaults to "no". +;----------------------------------------------------------------------------------- + diff --git a/configs/oss.conf.sample b/configs/oss.conf.sample index f2f90d3b5b..6997ccb9c2 100644 --- a/configs/oss.conf.sample +++ b/configs/oss.conf.sample @@ -46,6 +46,34 @@ ; queuesize = 10 ; frames in device driver ; frags = 8 ; argument to SETFRAGMENT + ;------------------------------ JITTER BUFFER CONFIGURATION -------------------------- + ; jbenable = yes ; Enables the use of a jitterbuffer on the receiving side of an + ; OSS channel. Defaults to "no". An enabled jitterbuffer will + ; be used only if the sending side can create and the receiving + ; side can not accept jitter. The ZAP channel can't accept jitter, + ; thus an enabled jitterbuffer on the receive ZAP side will always + ; be used if the sending side can create jitter or if ZAP jb is + ; forced. + + ; jbforce = no ; Forces the use of a jitterbuffer on the receive side of a ZAP + ; channel. Defaults to "no". + + ; jbmaxsize = 200 ; Max length of the jitterbuffer in milliseconds. + + ; jbresyncthreshold = 1000 ; Jump in the frame timestamps over which the jitterbuffer is + ; resynchronized. Useful to improve the quality of the voice, with + ; big jumps in/broken timestamps, usualy sent from exotic devices + ; and programs. Defaults to 1000. + + ; jbimpl = fixed ; Jitterbuffer implementation, used on the receiving side of a SIP + ; channel. Two implementation are currenlty available - "fixed" + ; (with size always equals to jbmax-size) and "adaptive" (with + ; variable size, actually the new jb of IAX2). Defaults to fixed. + + ; jblog = no ; Enables jitterbuffer frame logging. Defaults to "no". + ;----------------------------------------------------------------------------------- + + [card1] ; device = /dev/dsp1 ; alternate device diff --git a/configs/sip.conf.sample b/configs/sip.conf.sample index a78bae17f3..8a52b07a80 100644 --- a/configs/sip.conf.sample +++ b/configs/sip.conf.sample @@ -302,6 +302,32 @@ srvlookup=yes ; Enable DNS SRV lookups on outbound calls ; destinations which do not have a prior ; account relationship with your server. +;------------------------------ JITTER BUFFER CONFIGURATION -------------------------- +; jbenable = yes ; Enables the use of a jitterbuffer on the receiving side of a + ; SIP channel. Defaults to "no". An enabled jitterbuffer will + ; be used only if the sending side can create and the receiving + ; side can not accept jitter. The SIP channel can accept jitter, + ; thus a jitterbuffer on the receive SIP side will be used only + ; if it is forced and enabled. + +; jbforce = no ; Forces the use of a jitterbuffer on the receive side of a SIP + ; channel. Defaults to "no". + +; jbmaxsize = 200 ; Max length of the jitterbuffer in milliseconds. + +; jbresyncthreshold = 1000 ; Jump in the frame timestamps over which the jitterbuffer is + ; resynchronized. Useful to improve the quality of the voice, with + ; big jumps in/broken timestamps, usualy sent from exotic devices + ; and programs. Defaults to 1000. + +; jbimpl = fixed ; Jitterbuffer implementation, used on the receiving side of a SIP + ; channel. Two implementation are currenlty available - "fixed" + ; (with size always equals to jbmaxsize) and "adaptive" (with + ; variable size, actually the new jb of IAX2). Defaults to fixed. + +; jblog = no ; Enables jitterbuffer frame logging. Defaults to "no". +;----------------------------------------------------------------------------------- + [authentication] ; Global credentials for outbound calls, i.e. when a proxy challenges your ; Asterisk server for authentication. These credentials override diff --git a/configs/zapata.conf.sample b/configs/zapata.conf.sample index ee6db17b83..aacbce0206 100644 --- a/configs/zapata.conf.sample +++ b/configs/zapata.conf.sample @@ -495,6 +495,33 @@ immediate=no ; ;jitterbuffers=4 ; +;------------------------------ JITTER BUFFER CONFIGURATION -------------------------- +; jbenable = yes ; Enables the use of a jitterbuffer on the receiving side of a + ; ZAP channel. Defaults to "no". An enabled jitterbuffer will + ; be used only if the sending side can create and the receiving + ; side can not accept jitter. The ZAP channel can't accept jitter, + ; thus an enabled jitterbuffer on the receive ZAP side will always + ; be used if the sending side can create jitter or if ZAP jb is + ; forced. + +; jbforce = no ; Forces the use of a jitterbuffer on the receive side of a ZAP + ; channel. Defaults to "no". + +; jbmaxsize = 200 ; Max length of the jitterbuffer in milliseconds. + +; jbresyncthreshold = 1000 ; Jump in the frame timestamps over which the jitterbuffer is + ; resynchronized. Useful to improve the quality of the voice, with + ; big jumps in/broken timestamps, usualy sent from exotic devices + ; and programs. Defaults to 1000. + +; jbimpl = fixed ; Jitterbuffer implementation, used on the receiving side of a SIP + ; channel. Two implementation are currenlty available - "fixed" + ; (with size always equals to jbmax-size) and "adaptive" (with + ; variable size, actually the new jb of IAX2). Defaults to fixed. + +; jblog = no ; Enables jitterbuffer frame logging. Defaults to "no". +;----------------------------------------------------------------------------------- +; ; You can define your own custom ring cadences here. You can define up to 8 ; pairs. If the silence is negative, it indicates where the callerid spill is ; to be placed. Also, if you define any custom cadences, the default cadences diff --git a/frame.c b/frame.c index 9006d5f7cc..914d4b6b4e 100644 --- a/frame.c +++ b/frame.c @@ -316,6 +316,13 @@ struct ast_frame *ast_frisolate(struct ast_frame *fr) out->samples = fr->samples; out->offset = fr->offset; out->data = fr->data; + /* Copy the timing data */ + out->has_timing_info = fr->has_timing_info; + if (fr->has_timing_info) { + out->ts = fr->ts; + out->len = fr->len; + out->seqno = fr->seqno; + } } else out = fr; @@ -380,6 +387,12 @@ struct ast_frame *ast_frdup(struct ast_frame *f) out->prev = NULL; out->next = NULL; memcpy(out->data, f->data, out->datalen); + out->has_timing_info = f->has_timing_info; + if (f->has_timing_info) { + out->ts = f->ts; + out->len = f->len; + out->seqno = f->seqno; + } return out; } diff --git a/include/asterisk/abstract_jb.h b/include/asterisk/abstract_jb.h new file mode 100644 index 0000000000..622f49b466 --- /dev/null +++ b/include/asterisk/abstract_jb.h @@ -0,0 +1,220 @@ +/* + * abstract_jb: common implementation-independent jitterbuffer stuff + * + * Copyright (C) 2005, Attractel OOD + * + * Contributors: + * Slav Klenov + * + * 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 + */ + +#ifndef _ABSTRACT_JB_H_ +#define _ABSTRACT_JB_H_ + +#include +#include + +#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_ */ diff --git a/include/asterisk/channel.h b/include/asterisk/channel.h index 29e909e3a0..9d38dff5b4 100644 --- a/include/asterisk/channel.h +++ b/include/asterisk/channel.h @@ -86,6 +86,8 @@ #ifndef _ASTERISK_CHANNEL_H #define _ASTERISK_CHANNEL_H +#include "asterisk/abstract_jb.h" + #include #ifdef POLLCOMPAT #include "asterisk/poll-compat.h" @@ -445,6 +447,9 @@ struct ast_channel { /*! For easy linking */ AST_LIST_ENTRY(ast_channel) chan_list; + + /*! The jitterbuffer state */ + struct ast_jb jb; }; /* \defgroup chanprop Channel tech properties: @@ -452,6 +457,11 @@ struct ast_channel { /* @{ */ #define AST_CHAN_TP_WANTSJITTER (1 << 0) +/* \defgroup chanprop Channel tech properties: + \brief Channels have this property if they can create jitter; i.e. most VoIP channels */ +/* @{ */ +#define AST_CHAN_TP_CREATESJITTER (1 << 1) + /* This flag has been deprecated by the transfercapbilty data member in struct ast_channel */ /* #define AST_FLAG_DIGITAL (1 << 0) */ /* if the call is a digital ISDN call */ #define AST_FLAG_DEFER_DTMF (1 << 1) /*!< if dtmf should be deferred */ diff --git a/include/asterisk/frame.h b/include/asterisk/frame.h index 102a637f49..7029517d6a 100644 --- a/include/asterisk/frame.h +++ b/include/asterisk/frame.h @@ -109,6 +109,14 @@ struct ast_frame { struct ast_frame *prev; /*! Next/Prev for linking stand alone frames */ struct ast_frame *next; + /*! Timing data flag */ + int has_timing_info; + /*! Timestamp in milliseconds */ + long ts; + /*! Length in milliseconds */ + long len; + /*! Sequence number */ + int seqno; }; /*! diff --git a/rtp.c b/rtp.c index bfb2262038..210ed58616 100644 --- a/rtp.c +++ b/rtp.c @@ -696,7 +696,6 @@ struct ast_frame *ast_rtp_read(struct ast_rtp *rtp) int padding; int mark; int ext; - int x; char iabuf[INET_ADDRSTRLEN]; unsigned int ssrc; unsigned int timestamp; @@ -835,17 +834,6 @@ struct ast_frame *ast_rtp_read(struct ast_rtp *rtp) if (!rtp->lastrxts) rtp->lastrxts = timestamp; - if (rtp->rxseqno) { - for (x=rtp->rxseqno + 1; x < seqno; x++) { - /* Queue empty frames */ - rtp->f.mallocd = 0; - rtp->f.datalen = 0; - rtp->f.data = NULL; - rtp->f.offset = 0; - rtp->f.samples = 0; - rtp->f.src = "RTPMissedFrame"; - } - } rtp->rxseqno = seqno; if (rtp->dtmfcount) { @@ -877,6 +865,11 @@ struct ast_frame *ast_rtp_read(struct ast_rtp *rtp) if (rtp->f.subclass == AST_FORMAT_SLINEAR) ast_frame_byteswap_be(&rtp->f); calc_rxstamp(&rtp->f.delivery, rtp, timestamp, mark); + /* Add timing data to let ast_generic_bridge() put the frame into a jitterbuf */ + rtp->f.has_timing_info = 1; + rtp->f.ts = timestamp / 8; + rtp->f.len = rtp->f.samples / 8; + rtp->f.seqno = seqno; } else { /* Video -- samples is # of samples vs. 90000 */ if (!rtp->lastividtimestamp) @@ -1701,6 +1694,9 @@ static int ast_rtp_raw_write(struct ast_rtp *rtp, struct ast_frame *f, int codec if (rtp->lastts > rtp->lastdigitts) rtp->lastdigitts = rtp->lastts; + if (f->has_timing_info) + rtp->lastts = f->ts * 8; + /* Get a pointer to the header */ rtpheader = (unsigned char *)(f->data - hdrlen); diff --git a/scx_jitterbuf.c b/scx_jitterbuf.c new file mode 100644 index 0000000000..e0422f4692 --- /dev/null +++ b/scx_jitterbuf.c @@ -0,0 +1,351 @@ +/* + * scx_jitterbuf: jitterbuffering algorithm + * + * Copyright (C) 2005, Attractel OOD + * + * Contributors: + * Slav Klenov + * + * 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 + */ + +#include +#include +#include +#include +#include + +#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; +} diff --git a/scx_jitterbuf.h b/scx_jitterbuf.h new file mode 100644 index 0000000000..3344462942 --- /dev/null +++ b/scx_jitterbuf.h @@ -0,0 +1,93 @@ +/* + * scx_jitterbuf: jitterbuffering algorithm + * + * Copyright (C) 2005, Attractel OOD + * + * Contributors: + * Slav Klenov + * + * 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_ */ diff --git a/translate.c b/translate.c index d713817539..2db3525ab4 100644 --- a/translate.c +++ b/translate.c @@ -154,6 +154,12 @@ static int framein(struct ast_trans_pvt *pvt, struct ast_frame *f) int16_t *dst = (int16_t *)pvt->outbuf; int ret; int samples = pvt->samples; /* initial value */ + + /* Copy the last in jb timing info to the pvt */ + pvt->f.has_timing_info = f->has_timing_info; + pvt->f.ts = f->ts; + pvt->f.len = f->len; + pvt->f.seqno = f->seqno; if (f->samples == 0) { ast_log(LOG_WARNING, "no samples for %s\n", pvt->t->name); @@ -285,6 +291,15 @@ struct ast_frame *ast_translate(struct ast_trans_pvt *path, struct ast_frame *f, struct ast_trans_pvt *p = path; struct ast_frame *out = f; struct timeval delivery; + int has_timing_info; + long ts; + long len; + int seqno; + + has_timing_info = f->has_timing_info; + ts = f->ts; + len = f->len; + seqno = f->seqno; /* XXX hmmm... check this below */ if (!ast_tvzero(f->delivery)) { @@ -331,6 +346,12 @@ struct ast_frame *ast_translate(struct ast_trans_pvt *path, struct ast_frame *f, path->nextout = ast_tvadd(path->nextout, ast_samp2tv( out->samples, 8000)); } else { out->delivery = ast_tv(0, 0); + out->has_timing_info = has_timing_info; + if (has_timing_info) { + out->ts = ts; + out->len = len; + out->seqno = seqno; + } } /* Invalidate prediction if we're entering a silence period */ if (out->frametype == AST_FRAME_CNG)