diff options
author | Richard Mudgett <rmudgett@digium.com> | 2013-05-21 18:00:22 +0000 |
---|---|---|
committer | Richard Mudgett <rmudgett@digium.com> | 2013-05-21 18:00:22 +0000 |
commit | 3d63833bd6c869b7efa383e8dea14be1a6eff998 (patch) | |
tree | 34957dd051b8f67c7cc58a510e24ee3873a61ad4 /main/abstract_jb.c | |
parent | e1e1cc2deefb92f8b43825f1f34e619354737842 (diff) |
Merge in the bridge_construction branch to make the system use the Bridging API.
Breaks many things until they can be reworked. A partial list:
chan_agent
chan_dahdi, chan_misdn, chan_iax2 native bridging
app_queue
COLP updates
DTMF attended transfers
Protocol attended transfers
git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@389378 65c4cc65-6c06-0410-ace0-fbb531ad65f3
Diffstat (limited to 'main/abstract_jb.c')
-rw-r--r-- | main/abstract_jb.c | 309 |
1 files changed, 309 insertions, 0 deletions
diff --git a/main/abstract_jb.c b/main/abstract_jb.c index 88a9b8e91..6e20b86cb 100644 --- a/main/abstract_jb.c +++ b/main/abstract_jb.c @@ -41,6 +41,8 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$") #include "asterisk/channel.h" #include "asterisk/term.h" #include "asterisk/utils.h" +#include "asterisk/pbx.h" +#include "asterisk/timing.h" #include "asterisk/abstract_jb.h" #include "fixedjitterbuf.h" @@ -567,6 +569,13 @@ int ast_jb_read_conf(struct ast_jb_conf *conf, const char *varname, const char * return 0; } +void ast_jb_enable_for_channel(struct ast_channel *chan) +{ + struct ast_jb_conf conf = ast_channel_jb(chan)->conf; + if (ast_test_flag(&conf, AST_JB_ENABLED)) { + ast_jb_create_framehook(chan, &conf, 1); + } +} void ast_jb_configure(struct ast_channel *chan, const struct ast_jb_conf *conf) { @@ -800,3 +809,303 @@ const struct ast_jb_impl *ast_jb_get_impl(enum ast_jb_type type) } return NULL; } + +#define DEFAULT_TIMER_INTERVAL 20 +#define DEFAULT_SIZE 200 +#define DEFAULT_TARGET_EXTRA 40 +#define DEFAULT_RESYNC 1000 +#define DEFAULT_TYPE AST_JB_FIXED + +struct jb_framedata { + const struct ast_jb_impl *jb_impl; + struct ast_jb_conf jb_conf; + struct timeval start_tv; + struct ast_format last_format; + struct ast_timer *timer; + int timer_interval; /* ms between deliveries */ + int timer_fd; + int first; + void *jb_obj; +}; + +static void jb_framedata_destroy(struct jb_framedata *framedata) +{ + if (framedata->timer) { + ast_timer_close(framedata->timer); + framedata->timer = NULL; + } + if (framedata->jb_impl && framedata->jb_obj) { + struct ast_frame *f; + while (framedata->jb_impl->remove(framedata->jb_obj, &f) == AST_JB_IMPL_OK) { + ast_frfree(f); + } + framedata->jb_impl->destroy(framedata->jb_obj); + framedata->jb_obj = NULL; + } + ast_free(framedata); +} + +void ast_jb_conf_default(struct ast_jb_conf *conf) +{ + conf->max_size = DEFAULT_SIZE; + conf->resync_threshold = DEFAULT_RESYNC; + ast_copy_string(conf->impl, "fixed", sizeof(conf->impl)); + conf->target_extra = DEFAULT_TARGET_EXTRA; +} + +static void datastore_destroy_cb(void *data) { + ast_free(data); + ast_debug(1, "JITTERBUFFER datastore destroyed\n"); +} + +static const struct ast_datastore_info jb_datastore = { + .type = "jitterbuffer", + .destroy = datastore_destroy_cb +}; + +static void hook_destroy_cb(void *framedata) +{ + ast_debug(1, "JITTERBUFFER hook destroyed\n"); + jb_framedata_destroy((struct jb_framedata *) framedata); +} + +static struct ast_frame *hook_event_cb(struct ast_channel *chan, struct ast_frame *frame, enum ast_framehook_event event, void *data) +{ + struct jb_framedata *framedata = data; + struct timeval now_tv; + unsigned long now; + int putframe = 0; /* signifies if audio frame was placed into the buffer or not */ + + switch (event) { + case AST_FRAMEHOOK_EVENT_READ: + break; + case AST_FRAMEHOOK_EVENT_ATTACHED: + case AST_FRAMEHOOK_EVENT_DETACHED: + case AST_FRAMEHOOK_EVENT_WRITE: + return frame; + } + + if (ast_channel_fdno(chan) == AST_JITTERBUFFER_FD && framedata->timer) { + if (ast_timer_ack(framedata->timer, 1) < 0) { + ast_log(LOG_ERROR, "Failed to acknowledge timer in jitter buffer\n"); + return frame; + } + } + + if (!frame) { + return frame; + } + + now_tv = ast_tvnow(); + now = ast_tvdiff_ms(now_tv, framedata->start_tv); + + if (frame->frametype == AST_FRAME_VOICE) { + int res; + struct ast_frame *jbframe; + + if (!ast_test_flag(frame, AST_FRFLAG_HAS_TIMING_INFO) || frame->len < 2 || frame->ts < 0) { + /* only frames with timing info can enter the jitterbuffer */ + return frame; + } + + jbframe = ast_frisolate(frame); + ast_format_copy(&framedata->last_format, &frame->subclass.format); + + if (frame->len && (frame->len != framedata->timer_interval)) { + framedata->timer_interval = frame->len; + ast_timer_set_rate(framedata->timer, 1000 / framedata->timer_interval); + } + if (!framedata->first) { + framedata->first = 1; + res = framedata->jb_impl->put_first(framedata->jb_obj, jbframe, now); + } else { + res = framedata->jb_impl->put(framedata->jb_obj, jbframe, now); + } + if (res == AST_JB_IMPL_OK) { + frame = &ast_null_frame; + } + putframe = 1; + } + + if (frame->frametype == AST_FRAME_NULL) { + int res; + long next = framedata->jb_impl->next(framedata->jb_obj); + + /* If now is earlier than the next expected output frame + * from the jitterbuffer we may choose to pass on retrieving + * a frame during this read iteration. The only exception + * to this rule is when an audio frame is placed into the buffer + * and the time for the next frame to come out of the buffer is + * at least within the timer_interval of the next output frame. By + * doing this we are able to feed off the timing of the input frames + * and only rely on our jitterbuffer timer when frames are dropped. + * During testing, this hybrid form of timing gave more reliable results. */ + if (now < next) { + long int diff = next - now; + if (!putframe) { + return frame; + } else if (diff >= framedata->timer_interval) { + return frame; + } + } + + res = framedata->jb_impl->get(framedata->jb_obj, &frame, now, framedata->timer_interval); + switch (res) { + case AST_JB_IMPL_OK: + /* got it, and pass it through */ + break; + case AST_JB_IMPL_DROP: + ast_frfree(frame); + frame = &ast_null_frame; + break; + case AST_JB_IMPL_INTERP: + if (framedata->last_format.id) { + struct ast_frame tmp = { 0, }; + tmp.frametype = AST_FRAME_VOICE; + ast_format_copy(&tmp.subclass.format, &framedata->last_format); + /* example: 8000hz / (1000 / 20ms) = 160 samples */ + tmp.samples = ast_format_rate(&framedata->last_format) / (1000 / framedata->timer_interval); + tmp.delivery = ast_tvadd(framedata->start_tv, ast_samp2tv(next, 1000)); + tmp.offset = AST_FRIENDLY_OFFSET; + tmp.src = "func_jitterbuffer interpolation"; + frame = ast_frdup(&tmp); + break; + } + /* else fall through */ + case AST_JB_IMPL_NOFRAME: + frame = &ast_null_frame; + break; + } + } + + if (frame->frametype == AST_FRAME_CONTROL) { + switch(frame->subclass.integer) { + case AST_CONTROL_SRCUPDATE: + case AST_CONTROL_SRCCHANGE: + framedata->jb_impl->force_resync(framedata->jb_obj); + break; + default: + break; + } + } + + return frame; +} + +/* set defaults */ +static int jb_framedata_init(struct jb_framedata *framedata, struct ast_jb_conf *jb_conf) +{ + int jb_impl_type = DEFAULT_TYPE; + /* Initialize defaults */ + framedata->timer_fd = -1; + memcpy(&framedata->jb_conf, jb_conf, sizeof(*jb_conf)); + + /* Figure out implementation type from the configuration implementation string */ + if (!ast_strlen_zero(jb_conf->impl)) { + if (!strcasecmp(jb_conf->impl, "fixed")) { + jb_impl_type = AST_JB_FIXED; + } else if (!strcasecmp(jb_conf->impl, "adaptive")) { + jb_impl_type = AST_JB_ADAPTIVE; + } else { + ast_log(LOG_WARNING, "Unknown Jitterbuffer type %s. Failed to create jitterbuffer.\n", jb_conf->impl); + return -1; + } + } + + if (!(framedata->jb_impl = ast_jb_get_impl(jb_impl_type))) { + return -1; + } + + if (!(framedata->timer = ast_timer_open())) { + return -1; + } + + framedata->timer_fd = ast_timer_fd(framedata->timer); + framedata->timer_interval = DEFAULT_TIMER_INTERVAL; + ast_timer_set_rate(framedata->timer, 1000 / framedata->timer_interval); + framedata->start_tv = ast_tvnow(); + + framedata->jb_obj = framedata->jb_impl->create(&framedata->jb_conf); + return 0; +} + + +void ast_jb_create_framehook(struct ast_channel *chan, struct ast_jb_conf *jb_conf, int prefer_existing) +{ + struct jb_framedata *framedata; + struct ast_datastore *datastore = NULL; + struct ast_framehook_interface interface = { + .version = AST_FRAMEHOOK_INTERFACE_VERSION, + .event_cb = hook_event_cb, + .destroy_cb = hook_destroy_cb, + }; + int i = 0; + + /* If disabled, strip any existing jitterbuffer and don't replace it. */ + if (!strcasecmp(jb_conf->impl, "disabled")) { + int *id; + ast_channel_lock(chan); + if ((datastore = ast_channel_datastore_find(chan, &jb_datastore, NULL))) { + id = datastore->data; + ast_framehook_detach(chan, *id); + ast_channel_datastore_remove(chan, datastore); + } + ast_channel_unlock(chan); + return; + } + + if (!(framedata = ast_calloc(1, sizeof(*framedata)))) { + return; + } + + if (jb_framedata_init(framedata, jb_conf)) { + jb_framedata_destroy(framedata); + return; + } + + interface.data = framedata; + + ast_channel_lock(chan); + i = ast_framehook_attach(chan, &interface); + if (i >= 0) { + int *id; + if ((datastore = ast_channel_datastore_find(chan, &jb_datastore, NULL))) { + /* There is already a jitterbuffer on the channel. */ + if (prefer_existing) { + /* We prefer the existing jitterbuffer, so remove the new one and keep the old one. */ + ast_framehook_detach(chan, i); + ast_channel_unlock(chan); + return; + } + /* We prefer the new jitterbuffer, so strip the old one. */ + id = datastore->data; + ast_framehook_detach(chan, *id); + ast_channel_datastore_remove(chan, datastore); + } + + if (!(datastore = ast_datastore_alloc(&jb_datastore, NULL))) { + ast_framehook_detach(chan, i); + ast_channel_unlock(chan); + return; + } + + if (!(id = ast_calloc(1, sizeof(int)))) { + ast_datastore_free(datastore); + ast_framehook_detach(chan, i); + ast_channel_unlock(chan); + return; + } + + *id = i; /* Store off the id. The channel is still locked so it is safe to access this ptr. */ + datastore->data = id; + ast_channel_datastore_add(chan, datastore); + + ast_channel_set_fd(chan, AST_JITTERBUFFER_FD, framedata->timer_fd); + } else { + jb_framedata_destroy(framedata); + framedata = NULL; + } + ast_channel_unlock(chan); + + return; +} |