Put a jitterbuffer on the read side of a channel. More...
#include "asterisk.h"#include "asterisk/module.h"#include "asterisk/channel.h"#include "asterisk/framehook.h"#include "asterisk/pbx.h"#include "asterisk/abstract_jb.h"#include "asterisk/timing.h"#include "asterisk/app.h"
Go to the source code of this file.
Data Structures | |
| struct | jb_framedata |
Defines | |
| #define | DEFAULT_RESYNC 1000 |
| #define | DEFAULT_SIZE 200 |
| #define | DEFAULT_TARGET_EXTRA 40 |
| #define | DEFAULT_TIMER_INTERVAL 20 |
| #define | DEFAULT_TYPE AST_JB_FIXED |
Functions | |
| static void | __reg_module (void) |
| static void | __unreg_module (void) |
| static void | datastore_destroy_cb (void *data) |
| static void | hook_destroy_cb (void *framedata) |
| static struct ast_frame * | hook_event_cb (struct ast_channel *chan, struct ast_frame *frame, enum ast_framehook_event event, void *data) |
| static void | jb_conf_default (struct ast_jb_conf *conf) |
| static void | jb_framedata_destroy (struct jb_framedata *framedata) |
| static int | jb_framedata_init (struct jb_framedata *framedata, const char *data, const char *value) |
| static int | jb_helper (struct ast_channel *chan, const char *cmd, char *data, const char *value) |
| static int | load_module (void) |
| static int | unload_module (void) |
Variables | |
| static struct ast_module_info | __mod_info = { .name = AST_MODULE, .flags = AST_MODFLAG_LOAD_ORDER , .description = "Jitter buffer for read side of channel." , .key = "This paragraph is copyright (c) 2006 by Digium, Inc. \In order for your module to load, it must return this \key via a function called \"key\". Any code which \includes this paragraph must be licensed under the GNU \General Public License version 2 or later (at your \option). In addition to Digium's general reservations \of rights, Digium expressly reserves the right to \allow other parties to license this paragraph under \different terms. Any use of Digium, Inc. trademarks or \logos (including \"Asterisk\" or \"Digium\") without \express written permission of Digium, Inc. is prohibited.\n" , .buildopt_sum = AST_BUILDOPT_SUM, .load = load_module, .unload = unload_module, .load_pri = AST_MODPRI_DEFAULT, } |
| static struct ast_module_info * | ast_module_info = &__mod_info |
| static struct ast_datastore_info | jb_datastore |
| static struct ast_custom_function | jb_function |
Put a jitterbuffer on the read side of a channel.
Definition in file func_jitterbuffer.c.
| #define DEFAULT_RESYNC 1000 |
Definition at line 80 of file func_jitterbuffer.c.
Referenced by jb_conf_default().
| #define DEFAULT_SIZE 200 |
Definition at line 78 of file func_jitterbuffer.c.
Referenced by jb_conf_default().
| #define DEFAULT_TARGET_EXTRA 40 |
Definition at line 79 of file func_jitterbuffer.c.
Referenced by jb_conf_default().
| #define DEFAULT_TIMER_INTERVAL 20 |
Definition at line 77 of file func_jitterbuffer.c.
Referenced by jb_framedata_init().
| #define DEFAULT_TYPE AST_JB_FIXED |
Definition at line 81 of file func_jitterbuffer.c.
Referenced by jb_framedata_init().
| static void __reg_module | ( | void | ) | [static] |
Definition at line 397 of file func_jitterbuffer.c.
| static void __unreg_module | ( | void | ) | [static] |
Definition at line 397 of file func_jitterbuffer.c.
| static void datastore_destroy_cb | ( | void * | data | ) | [static] |
| static void hook_destroy_cb | ( | void * | framedata | ) | [static] |
Definition at line 199 of file func_jitterbuffer.c.
References ast_debug, and jb_framedata_destroy().
Referenced by jb_helper().
{
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 | ||
| ) | [static, read] |
Definition at line 205 of file func_jitterbuffer.c.
References ast_channel_fdno(), ast_format_copy(), ast_format_rate(), AST_FRAME_NULL, AST_FRAME_VOICE, AST_FRAMEHOOK_EVENT_ATTACHED, AST_FRAMEHOOK_EVENT_DETACHED, AST_FRAMEHOOK_EVENT_READ, AST_FRAMEHOOK_EVENT_WRITE, ast_frdup(), AST_FRFLAG_HAS_TIMING_INFO, ast_frfree, AST_FRIENDLY_OFFSET, ast_frisolate(), AST_JB_IMPL_DROP, AST_JB_IMPL_INTERP, AST_JB_IMPL_NOFRAME, AST_JB_IMPL_OK, AST_JITTERBUFFER_FD, ast_log(), ast_null_frame, ast_samp2tv(), ast_test_flag, ast_timer_ack(), ast_timer_set_rate(), ast_tvadd(), ast_tvdiff_ms(), ast_tvnow(), ast_frame::delivery, jb_framedata::first, ast_frame_subclass::format, ast_frame::frametype, ast_jb_impl::get, ast_format::id, jb_framedata::jb_impl, jb_framedata::jb_obj, jb_framedata::last_format, ast_frame::len, LOG_ERROR, ast_jb_impl::next, ast_frame::next, ast_frame::offset, ast_jb_impl::put, ast_jb_impl::put_first, ast_frame::samples, ast_frame::src, jb_framedata::start_tv, ast_frame::subclass, jb_framedata::timer, jb_framedata::timer_interval, and ast_frame::ts.
Referenced by jb_helper().
{
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;
}
}
return frame;
}
| static void jb_conf_default | ( | struct ast_jb_conf * | conf | ) | [static] |
Definition at line 112 of file func_jitterbuffer.c.
References ast_copy_string(), DEFAULT_RESYNC, DEFAULT_SIZE, DEFAULT_TARGET_EXTRA, ast_jb_conf::impl, ast_jb_conf::max_size, ast_jb_conf::resync_threshold, and ast_jb_conf::target_extra.
Referenced by jb_framedata_init().
{
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 jb_framedata_destroy | ( | struct jb_framedata * | framedata | ) | [static] |
Definition at line 95 of file func_jitterbuffer.c.
References ast_free, ast_frfree, AST_JB_IMPL_OK, ast_timer_close(), ast_jb_impl::destroy, f, jb_framedata::jb_impl, jb_framedata::jb_obj, ast_jb_impl::remove, and jb_framedata::timer.
Referenced by hook_destroy_cb(), and jb_helper().
{
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);
}
| static int jb_framedata_init | ( | struct jb_framedata * | framedata, |
| const char * | data, | ||
| const char * | value | ||
| ) | [static] |
Definition at line 121 of file func_jitterbuffer.c.
References args, AST_APP_ARG, ast_copy_string(), AST_DECLARE_APP_ARGS, AST_JB_ADAPTIVE, AST_JB_FIXED, ast_jb_get_impl(), ast_jb_read_conf(), ast_log(), AST_STANDARD_APP_ARGS, ast_strlen_zero(), ast_timer_fd(), ast_timer_open(), ast_timer_set_rate(), ast_tvnow(), ast_jb_impl::create, DEFAULT_TIMER_INTERVAL, DEFAULT_TYPE, ast_jb_conf::impl, jb_framedata::jb_conf, jb_conf_default(), jb_framedata::jb_impl, jb_framedata::jb_obj, LOG_WARNING, parse(), jb_framedata::start_tv, jb_framedata::timer, jb_framedata::timer_fd, and jb_framedata::timer_interval.
Referenced by jb_helper().
{
int jb_impl_type = DEFAULT_TYPE;
/* Initialize defaults */
framedata->timer_fd = -1;
jb_conf_default(&framedata->jb_conf);
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();
/* Now check user options to see if any of the defaults need to change. */
if (!ast_strlen_zero(data)) {
if (!strcasecmp(data, "fixed")) {
jb_impl_type = AST_JB_FIXED;
} else if (!strcasecmp(data, "adaptive")) {
jb_impl_type = AST_JB_ADAPTIVE;
} else {
ast_log(LOG_WARNING, "Unknown Jitterbuffer type %s. Failed to create jitterbuffer.\n", data);
return -1;
}
ast_copy_string(framedata->jb_conf.impl, data, sizeof(framedata->jb_conf.impl));
}
if (!ast_strlen_zero(value) && strcasecmp(value, "default")) {
char *parse = ast_strdupa(value);
int res = 0;
AST_DECLARE_APP_ARGS(args,
AST_APP_ARG(max_size);
AST_APP_ARG(resync_threshold);
AST_APP_ARG(target_extra);
);
AST_STANDARD_APP_ARGS(args, parse);
if (!ast_strlen_zero(args.max_size)) {
res |= ast_jb_read_conf(&framedata->jb_conf,
"jbmaxsize",
args.max_size);
}
if (!ast_strlen_zero(args.resync_threshold)) {
res |= ast_jb_read_conf(&framedata->jb_conf,
"jbresyncthreshold",
args.resync_threshold);
}
if (!ast_strlen_zero(args.target_extra)) {
res |= ast_jb_read_conf(&framedata->jb_conf,
"jbtargetextra",
args.target_extra);
}
if (res) {
ast_log(LOG_WARNING, "Invalid jitterbuffer parameters %s\n", value);
}
}
/* now that all the user parsing is done and nothing will change, create the jb obj */
framedata->jb_obj = framedata->jb_impl->create(&framedata->jb_conf);
return 0;
}
| static int jb_helper | ( | struct ast_channel * | chan, |
| const char * | cmd, | ||
| char * | data, | ||
| const char * | value | ||
| ) | [static] |
Definition at line 317 of file func_jitterbuffer.c.
References ast_calloc, ast_channel_datastore_add(), ast_channel_datastore_find(), ast_channel_datastore_remove(), ast_channel_lock, ast_channel_set_fd(), ast_channel_unlock, ast_datastore_alloc(), ast_datastore_free(), ast_framehook_attach(), ast_framehook_detach(), AST_FRAMEHOOK_INTERFACE_VERSION, AST_JITTERBUFFER_FD, ast_log(), ast_datastore::data, ast_framehook_interface::data, hook_destroy_cb(), hook_event_cb(), id, jb_framedata_destroy(), jb_framedata_init(), LOG_WARNING, jb_framedata::timer_fd, and ast_framehook_interface::version.
{
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 (!chan) {
ast_log(LOG_WARNING, "No channel was provided to %s function.\n", cmd);
return -1;
}
if (!(framedata = ast_calloc(1, sizeof(*framedata)))) {
return 0;
}
if (jb_framedata_init(framedata, data, value)) {
jb_framedata_destroy(framedata);
return 0;
}
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))) {
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 0;
}
if (!(id = ast_calloc(1, sizeof(int)))) {
ast_datastore_free(datastore);
ast_framehook_detach(chan, i);
ast_channel_unlock(chan);
return 0;
}
*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 0;
}
| static int load_module | ( | void | ) | [static] |
Definition at line 391 of file func_jitterbuffer.c.
References ast_custom_function_register, AST_MODULE_LOAD_DECLINE, and AST_MODULE_LOAD_SUCCESS.
{
int res = ast_custom_function_register(&jb_function);
return res ? AST_MODULE_LOAD_DECLINE : AST_MODULE_LOAD_SUCCESS;
}
| static int unload_module | ( | void | ) | [static] |
Definition at line 386 of file func_jitterbuffer.c.
References ast_custom_function_unregister().
{
return ast_custom_function_unregister(&jb_function);
}
struct ast_module_info __mod_info = { .name = AST_MODULE, .flags = AST_MODFLAG_LOAD_ORDER , .description = "Jitter buffer for read side of channel." , .key = "This paragraph is copyright (c) 2006 by Digium, Inc. \In order for your module to load, it must return this \key via a function called \"key\". Any code which \includes this paragraph must be licensed under the GNU \General Public License version 2 or later (at your \option). In addition to Digium's general reservations \of rights, Digium expressly reserves the right to \allow other parties to license this paragraph under \different terms. Any use of Digium, Inc. trademarks or \logos (including \"Asterisk\" or \"Digium\") without \express written permission of Digium, Inc. is prohibited.\n" , .buildopt_sum = AST_BUILDOPT_SUM, .load = load_module, .unload = unload_module, .load_pri = AST_MODPRI_DEFAULT, } [static] |
Definition at line 397 of file func_jitterbuffer.c.
struct ast_module_info* ast_module_info = &__mod_info [static] |
Definition at line 397 of file func_jitterbuffer.c.
struct ast_datastore_info jb_datastore [static] |
{
.type = "jitterbuffer",
.destroy = datastore_destroy_cb
}
Definition at line 194 of file func_jitterbuffer.c.
struct ast_custom_function jb_function [static] |
{
.name = "JITTERBUFFER",
.write = jb_helper,
}
Definition at line 381 of file func_jitterbuffer.c.