[asterisk-commits] mjordan: branch mjordan/out-of-order-dtmf r365691 - in /team/mjordan/out-of-o...
SVN commits to the Asterisk project
asterisk-commits at lists.digium.com
Tue May 8 15:03:12 CDT 2012
Author: mjordan
Date: Tue May 8 15:03:07 2012
New Revision: 365691
URL: http://svnview.digium.com/svn/asterisk?view=rev&rev=365691
Log:
Merge the current working out of order DTMF support to a branch
So, this gets things relatively close. res_rtp_asterisk has basic
knowledge of whether or not a jitter buffer exists that can hold
DTMF frames, and stops doing 'frame dropping' if that happens.
Unfortunately, the jitter buffer doesn't handle two things very well:
1) Collisions, which occurs when DTMF END events happen (usually 3
in a row)
2) Multiple frames in a list are inheriting the timestamp of the first,
so that doesn't work terribly well
3) DTMF frames are getting interpolated, and that's just no good
at all. If a DTMF frame doesn't exist, we need to switch to a different
jitter buffer for that cycle.
4) We may want to not change the timer frequency. The length of a DTMF
is fairly long, during which period we may be putting voice frames
that need to get read. I might move this back towards just keeping the
timer at a minimum frequency.
I'm trying to prevent putting a whole bunch of frame type checking
into func_jitterbuffer, but it looks like there's going to have to
be some level of that.
So, still some stuff to do. Need to think about some of the collision
issues.
Added:
team/mjordan/out-of-order-dtmf/
- copied from r365142, trunk/
Modified:
team/mjordan/out-of-order-dtmf/channels/chan_sip.c
team/mjordan/out-of-order-dtmf/funcs/func_frame_trace.c
team/mjordan/out-of-order-dtmf/funcs/func_jitterbuffer.c
team/mjordan/out-of-order-dtmf/include/asterisk/frame.h
team/mjordan/out-of-order-dtmf/include/asterisk/framehook.h
team/mjordan/out-of-order-dtmf/include/asterisk/rtp_engine.h
team/mjordan/out-of-order-dtmf/main/channel.c
team/mjordan/out-of-order-dtmf/main/file.c
team/mjordan/out-of-order-dtmf/main/framehook.c
team/mjordan/out-of-order-dtmf/main/jitterbuf.c
team/mjordan/out-of-order-dtmf/main/rtp_engine.c
team/mjordan/out-of-order-dtmf/main/translate.c
team/mjordan/out-of-order-dtmf/res/res_rtp_asterisk.c
Modified: team/mjordan/out-of-order-dtmf/channels/chan_sip.c
URL: http://svnview.digium.com/svn/asterisk/team/mjordan/out-of-order-dtmf/channels/chan_sip.c?view=diff&rev=365691&r1=365142&r2=365691
==============================================================================
--- team/mjordan/out-of-order-dtmf/channels/chan_sip.c (original)
+++ team/mjordan/out-of-order-dtmf/channels/chan_sip.c Tue May 8 15:03:07 2012
@@ -7094,6 +7094,12 @@
break;
case AST_CONTROL_UPDATE_RTP_PEER: /* Absorb this since it is handled by the bridge */
break;
+ case AST_CONTROL_JITTERBUFFER_UPDATE:
+ /* XXX we may want to someday inform the vrtp instance of a video jitterbuffer */
+ if (p->rtp) {
+ ast_rtp_instance_jitterbuffer_update(p->rtp, p->owner);
+ }
+ break;
case -1:
res = -1;
break;
Modified: team/mjordan/out-of-order-dtmf/funcs/func_frame_trace.c
URL: http://svnview.digium.com/svn/asterisk/team/mjordan/out-of-order-dtmf/funcs/func_frame_trace.c?view=diff&rev=365691&r1=365142&r2=365691
==============================================================================
--- team/mjordan/out-of-order-dtmf/funcs/func_frame_trace.c (original)
+++ team/mjordan/out-of-order-dtmf/funcs/func_frame_trace.c Tue May 8 15:03:07 2012
@@ -324,8 +324,10 @@
case AST_CONTROL_UPDATE_RTP_PEER:
ast_verbose("SubClass: UPDATE_RTP_PEER\n");
break;
- }
-
+ case AST_CONTROL_JITTERBUFFER_UPDATE:
+ ast_verbose("SubClass: JITTERBUFFER_UPDATE\n");
+ break;
+ }
if (frame->subclass.integer == -1) {
ast_verbose("SubClass: %d\n", frame->subclass.integer);
}
Modified: team/mjordan/out-of-order-dtmf/funcs/func_jitterbuffer.c
URL: http://svnview.digium.com/svn/asterisk/team/mjordan/out-of-order-dtmf/funcs/func_jitterbuffer.c?view=diff&rev=365691&r1=365142&r2=365691
==============================================================================
--- team/mjordan/out-of-order-dtmf/funcs/func_jitterbuffer.c (original)
+++ team/mjordan/out-of-order-dtmf/funcs/func_jitterbuffer.c Tue May 8 15:03:07 2012
@@ -44,14 +44,14 @@
/*** DOCUMENTATION
<function name="JITTERBUFFER" language="en_US">
<synopsis>
- Add a Jitterbuffer to the Read side of the channel. This dejitters the audio stream before it reaches the Asterisk core. This is a write only function.
+ Add a Jitterbuffer to the Read side of the channel. This dejitters the stream before it reaches the Asterisk core. This is a write only function.
</synopsis>
<syntax>
<parameter name="jitterbuffer type" required="true">
<para>Jitterbuffer type can be either <literal>fixed</literal> or <literal>adaptive</literal>.</para>
<para>Used as follows. </para>
- <para>Set(JITTERBUFFER(type)=max_size[,resync_threshold[,target_extra]])</para>
- <para>Set(JITTERBUFFER(type)=default) </para>
+ <para>Set(JITTERBUFFER(type)=max_size[,resync_threshold[,target_extra[,frame_type]]])</para>
+ <para>Set(JITTERBUFFER(type)=default[,frame_type]) </para>
</parameter>
</syntax>
<description>
@@ -63,13 +63,17 @@
<para> </para>
<para>target_extra: Defaults to 40ms</para>
<para>This option only affects the adaptive jitterbuffer. It represents the amount time in milliseconds by which the new jitter buffer will pad its size.</para>
+ <para>frame_type: The type of frame this jitter buffer applies to. Default is audio</para>
+ <para>Valid values are: audio,dtmf,video</para>
<para> </para>
<para>Examples:</para>
<para>exten => 1,1,Set(JITTERBUFFER(fixed)=default);Fixed with defaults. </para>
+ <para>exten => 1,1,Set(JITTERBUFFER(adaptive)=default,video);Adaptive with defaults for video frames. </para>
<para>exten => 1,1,Set(JITTERBUFFER(fixed)=200);Fixed with max size 200ms, default resync threshold and target extra. </para>
<para>exten => 1,1,Set(JITTERBUFFER(fixed)=200,1500);Fixed with max size 200ms resync threshold 1500. </para>
<para>exten => 1,1,Set(JITTERBUFFER(adaptive)=default);Adaptive with defaults. </para>
<para>exten => 1,1,Set(JITTERBUFFER(adaptive)=200,,60);Adaptive with max size 200ms, default resync threshold and 40ms target extra. </para>
+ <para>exten => 1,1,Set(JITTERBUFFER(fixed)=200,1000,,dtmf);Fixed with max size 200ms, resync threshold 1000, for DTMF. </para>
</description>
</function>
***/
@@ -79,33 +83,60 @@
#define DEFAULT_TARGET_EXTRA 40
#define DEFAULT_RESYNC 1000
#define DEFAULT_TYPE AST_JB_FIXED
+#define DEFAULT_FRAME_TYPE AST_FRAME_VOICE
+
+#define MAX_FRAME_TYPES AST_FRAME_DTMF_BEGIN + 1
+
+struct jb_container {
+ const struct ast_jb_impl *jb_impl; /*! The jitter buffer implementation definition */
+ struct ast_jb_conf jb_conf; /*! The jitter buffer configuration */
+ enum ast_frame_type frame_type; /*! The type of frame this jitter buffer will handle */
+ int first; /*! True if this is the first frame to go into the jb */
+ void *jb_obj; /*! The actual jitter buffer */
+ struct ast_format last_format; /*! The last format type stored in the jb */
+ int timer_interval; /*! ms between deliveries */
+};
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;
+ struct timeval start_tv; /*! The relative time at which the jitterbuffers could start receiving data */
+ struct ast_timer *timer; /*! A timer used to periodically check the jitterbuffers for new data */
+ int timer_fd; /*! The timer's file descriptor */
+ int framehook_id; /* The ID given to this by the framehook API */
+ /*! The possible jitterbuffers. If a frame type does not have a jitterbuffer
+ * created for it, its entry is NULL */
+ struct jb_container *containers[MAX_FRAME_TYPES];
};
+static void jb_container_destroy(struct jb_container *container)
+{
+ if (container->jb_impl && container->jb_obj) {
+ struct ast_frame *f;
+ while (container->jb_impl->remove(container->jb_obj, &f) == AST_JB_IMPL_OK) {
+ ast_frfree(f);
+ }
+ container->jb_impl->destroy(container->jb_obj);
+ container->jb_obj = NULL;
+ }
+ ast_free(container);
+}
+
static void jb_framedata_destroy(struct jb_framedata *framedata)
{
+ int i;
+
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;
- }
+ for (i = 0; i < MAX_FRAME_TYPES; ++i) {
+ /* Explicitly skip over AST_FRAME_DTMF_BEGIN - the DTMF jitter buffer will be
+ * dispose of in AST_FRAME_DTMF
+ */
+ if (framedata->containers[i] && i != AST_FRAME_DTMF_BEGIN) {
+ jb_container_destroy(framedata->containers[i]);
+ }
+ }
+
ast_free(framedata);
}
@@ -117,28 +148,93 @@
conf->target_extra = DEFAULT_TARGET_EXTRA;
}
-/* set defaults */
-static int jb_framedata_init(struct jb_framedata *framedata, const char *data, const char *value)
-{
- 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))) {
+static enum ast_frame_type parse_supported_frame_type(const char *type)
+{
+ if (!strcasecmp(type, "voice")) {
+ return AST_FRAME_VOICE;
+ } else if (!strcasecmp(type, "dtmf")) {
+ return AST_FRAME_DTMF;
+ } else if (!strcasecmp(type, "video")) {
+ return AST_FRAME_VIDEO;
+ }
+
+ return AST_FRAME_NULL;
+}
+
+static int parse_jb_args(struct jb_container *container, const char *value)
+{
+ 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_APP_ARG(frame_type);
+ );
+
+ AST_STANDARD_APP_ARGS(args, parse);
+ if (!ast_strlen_zero(args.max_size)) {
+ res |= ast_jb_read_conf(&container->jb_conf,
+ "jbmaxsize",
+ args.max_size);
+ }
+ if (!ast_strlen_zero(args.resync_threshold)) {
+ res |= ast_jb_read_conf(&container->jb_conf,
+ "jbresyncthreshold",
+ args.resync_threshold);
+ }
+ if (!ast_strlen_zero(args.target_extra)) {
+ res |= ast_jb_read_conf(&container->jb_conf,
+ "jbtargetextra",
+ args.target_extra);
+ }
+ if (!ast_strlen_zero(args.frame_type)) {
+ res |= ((container->frame_type = parse_supported_frame_type(args.frame_type)) == AST_FRAME_NULL);
+ }
+ if (res) {
+ ast_log(LOG_WARNING, "Invalid jitterbuffer parameters %s\n", value);
+ }
+
+ return res;
+}
+
+static int parse_default_args(struct jb_container *container, const char *value)
+{
+ char *parse = ast_strdupa(value);
+ int res = 0;
+ AST_DECLARE_APP_ARGS(args,
+ AST_APP_ARG(frame_type);
+ );
+
+ /* Skip past 'default' */
+ parse = strchr(parse, ',');
+ if (parse) {
+ AST_STANDARD_APP_ARGS(args, parse + 1);
+ ast_debug(1, "%s\n", args.frame_type);
+ if (!ast_strlen_zero(args.frame_type)) {
+ res |= ((container->frame_type = parse_supported_frame_type(args.frame_type)) == AST_FRAME_NULL);
+ }
+ if (res) {
+ ast_log(LOG_WARNING, "Invalid jitterbuffer parameters %s\n", value);
+ }
+ }
+ return res;
+}
+
+static int jb_framedata_update(struct jb_framedata *framedata, const char *data, const char *value)
+{
+ int res = 0;
+ struct jb_container *container;
+ enum ast_jb_type jb_impl_type = DEFAULT_TYPE;
+
+ if (!(container = ast_calloc(1, sizeof(*container)))) {
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. */
+ jb_conf_default(&container->jb_conf);
+ container->timer_interval = DEFAULT_TIMER_INTERVAL;
+ container->frame_type = DEFAULT_FRAME_TYPE;
+
+ /* Determine what type of jitter buffer to make and get the impl object */
if (!ast_strlen_zero(data)) {
if (!strcasecmp(data, "fixed")) {
jb_impl_type = AST_JB_FIXED;
@@ -146,48 +242,76 @@
jb_impl_type = AST_JB_ADAPTIVE;
} else {
ast_log(LOG_WARNING, "Unknown Jitterbuffer type %s. Failed to create jitterbuffer.\n", data);
+ ast_free(container);
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, framedata->jb_conf.resync_threshold);
+ ast_copy_string(container->jb_conf.impl, data, sizeof(container->jb_conf.impl));
+ }
+ if (!(container->jb_impl = ast_jb_get_impl(jb_impl_type))) {
+ ast_free(container);
+ return -1;
+ }
+
+ /* Parse and apply the user defined options */
+ if (!ast_strlen_zero(value)) {
+ if (!strncasecmp(value, "default", 7)) {
+ res = parse_default_args(container, value);
+ } else {
+ res = parse_jb_args(container, value);
+ }
+ }
+ if (res) {
+ ast_free(container);
+ return -1;
+ }
+
+ /* Create the actual jitter buffer */
+ container->jb_obj = container->jb_impl->create(&container->jb_conf, container->jb_conf.resync_threshold);
+
+ /* Store the new jitter buffer in the framedata, replacing the old if needed */
+ if (framedata->containers[container->frame_type]) {
+ jb_container_destroy(framedata->containers[container->frame_type]);
+ }
+ framedata->containers[container->frame_type] = container;
+
+ /* For the case of DTMF, we want both AST_FRAME_DTMF_BEGIN and AST_FRAME_DTMF_END
+ * to be placed into the same jitter buffer
+ */
+ if (container->frame_type == AST_FRAME_DTMF) {
+ framedata->containers[AST_FRAME_DTMF_BEGIN] = framedata->containers[AST_FRAME_DTMF];
+ }
+
return 0;
}
+static struct jb_framedata *jb_framedata_create(const char *data, const char *value)
+{
+ struct jb_framedata *framedata;
+
+ if (!(framedata = ast_calloc(1, sizeof(*framedata)))) {
+ return NULL;
+ }
+
+ /* Create the timer */
+ framedata->timer_fd = -1;
+ if (!(framedata->timer = ast_timer_open())) {
+ ast_free(framedata);
+ return NULL;
+ }
+ framedata->timer_fd = ast_timer_fd(framedata->timer);
+ ast_timer_set_rate(framedata->timer, 1000 / DEFAULT_TIMER_INTERVAL);
+ framedata->start_tv = ast_tvnow();
+
+ /* Create the jitterbuffer specified by the parameters */
+ if (jb_framedata_update(framedata, data, value)) {
+ jb_framedata_destroy(framedata);
+ return NULL;
+ }
+
+ return framedata;
+}
+
static void datastore_destroy_cb(void *data) {
- ast_free(data);
ast_debug(1, "JITTERBUFFER datastore destroyed\n");
}
@@ -206,8 +330,11 @@
{
struct jb_framedata *framedata = data;
struct timeval now_tv;
+ enum ast_frame_type in_frame_type = AST_FRAME_NULL;
unsigned long now;
- int putframe = 0; /* signifies if audio frame was placed into the buffer or not */
+ int res = AST_JB_IMPL_NOFRAME;
+ struct ast_frame *jbframe;
+ int putframe = 0;
switch (event) {
case AST_FRAMEHOOK_EVENT_READ:
@@ -226,31 +353,46 @@
return frame;
}
+ in_frame_type = frame->frametype;
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 (framedata->containers[in_frame_type]) {
+ struct jb_container *container = framedata->containers[in_frame_type];
+
+ /* only frames with timing info can enter a jitterbuffer */
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 */
+ ast_debug(1, "Dumping frame %p [%d] %ld %ld\n", frame, frame->frametype, frame->len, frame->ts);
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);
+ if (in_frame_type == AST_FRAME_VOICE || in_frame_type == AST_FRAME_VIDEO || in_frame_type == AST_FRAME_TEXT) {
+ ast_format_copy(&container->last_format, &frame->subclass.format);
+ }
+
+ /* Change the timer interval to match the rate at which the current frame type arrives */
+ if (frame->len && (frame->len != container->timer_interval)) {
+ container->timer_interval = frame->len;
+ ast_timer_set_rate(framedata->timer, 1000 / container->timer_interval);
+ }
+
+ /* Put the frame into the jitterbuffer */
+ if (!container->first) {
+ container->first = 1;
+ res = container->jb_impl->put_first(container->jb_obj, jbframe, now);
+ ast_debug(1, " Put frame %p [%d] %ld %ld in jb\n", frame, frame->frametype, frame->len, frame->ts);
} else {
- res = framedata->jb_impl->put(framedata->jb_obj, jbframe, now);
- }
+ long next = container->jb_impl->next(container->jb_obj);
+ ast_debug(1, "Collision: %ld %ld %ld\n", next, now, frame->ts);
+ if (next == now) {
+ return &ast_null_frame;
+ } else {
+ res = container->jb_impl->put(container->jb_obj, jbframe, now);
+ }
+ ast_debug(1, " Put frame %p [%d] %ld %ld in jb\n", frame, frame->frametype, frame->len, frame->ts);
+ }
+
if (res == AST_JB_IMPL_OK) {
frame = &ast_null_frame;
}
@@ -258,47 +400,64 @@
}
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;
+ /* Search for a candidate frame, treating the containers as a circular buffer */
+ enum ast_frame_type start = in_frame_type;
+ enum ast_frame_type end = start;
+ enum ast_frame_type i = start;
+ long next = 0;
+ do {
+ if (framedata->containers[i]) {
+ next = framedata->containers[i]->jb_impl->next(framedata->containers[i]->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 a 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) {
+ goto search_again;
+ } else if (diff >= framedata->containers[i]->timer_interval) {
+ goto search_again;
+ }
+ }
+ res = framedata->containers[i]->jb_impl->get(framedata->containers[i]->jb_obj,
+ &frame, now, framedata->containers[i]->timer_interval);
+ break;
}
- }
-
- res = framedata->jb_impl->get(framedata->jb_obj, &frame, now, framedata->timer_interval);
+search_again:
+ i = (i + 1) % MAX_FRAME_TYPES;
+ } while (i != end);
+
switch (res) {
case AST_JB_IMPL_OK:
/* got it, and pass it through */
+ ast_debug(1, " Got frame %p [%d] %ld %ld from jb\n", frame, frame->frametype, frame->len, frame->ts);
break;
case AST_JB_IMPL_DROP:
+ ast_debug(1, " Dropping frame %p [%d] %ld %ld from jb\n", frame, frame->frametype, frame->len, frame->ts);
ast_frfree(frame);
frame = &ast_null_frame;
break;
case AST_JB_IMPL_INTERP:
- if (framedata->last_format.id) {
+ ast_debug(1, " Interpolating frame type %d \n", i);
+ if (framedata->containers[i]->last_format.id) {
struct ast_frame tmp = { 0, };
- tmp.frametype = AST_FRAME_VOICE;
- ast_format_copy(&tmp.subclass.format, &framedata->last_format);
+ tmp.frametype = i;
+ ast_format_copy(&tmp.subclass.format, &framedata->containers[i]->last_format);
/* example: 8000hz / (1000 / 20ms) = 160 samples */
- tmp.samples = ast_format_rate(&framedata->last_format) / (1000 / framedata->timer_interval);
+ tmp.samples = ast_format_rate(&framedata->containers[i]->last_format)
+ / (1000 / framedata->containers[i]->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);
+ ast_debug(1, " Created %p [%d] %ld %ld from\n", frame, frame->frametype, frame->len, frame->ts);
break;
}
/* else fall through */
@@ -311,62 +470,106 @@
return frame;
}
+static int hook_capability_cb(struct ast_channel *chan, const char *capability)
+{
+ struct ast_datastore *datastore = NULL;
+ struct jb_framedata *framedata = NULL;
+ char *tmp_frametype;
+ enum ast_frame_type frametype;
+ int res = 1;
+
+ /* See if this capability request applies to us */
+ if (strncasecmp(capability, "jitterbuffer_", 13)) {
+ return 1;
+ }
+ tmp_frametype = ast_strdupa(capability);
+ tmp_frametype = tmp_frametype + 13;
+ if ((frametype = parse_supported_frame_type(tmp_frametype)) == AST_FRAME_NULL) {
+ ast_log(AST_LOG_WARNING, "Unsupported frame type %s queried for in capability request\n", tmp_frametype);
+ return 1;
+ }
+
+ ast_channel_lock(chan);
+ if (!(datastore = ast_channel_datastore_find(chan, &jb_datastore, NULL))) {
+ ast_channel_unlock(chan);
+ return 1;
+ }
+
+ framedata = datastore->data;
+ if (!framedata) {
+ ast_log(AST_LOG_WARNING, "Jitterbuffer datastore found with no framedata object\n");
+ ast_channel_unlock(chan);
+ return 1;
+ }
+
+ res = (framedata->containers[frametype] == NULL);
+ ast_debug(3, "Frame type capability '%s' %s have a jitterbuffer\n", tmp_frametype, (res ? "does not" : "does"));
+ ast_channel_unlock(chan);
+
+ return res;
+}
+
static int jb_helper(struct ast_channel *chan, const char *cmd, char *data, const char *value)
{
- struct jb_framedata *framedata;
+ struct jb_framedata *framedata = NULL;
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,
+ .capability_cb = hook_capability_cb,
};
- int i = 0;
-
- if (!(framedata = ast_calloc(1, sizeof(*framedata)))) {
+
+ ast_channel_lock(chan);
+ if ((datastore = ast_channel_datastore_find(chan, &jb_datastore, NULL))) {
+ /* Framehook already exists - update with new jitter buffer */
+ framedata = datastore->data;
+ if (!framedata) {
+ ast_log(AST_LOG_WARNING, "Jitterbuffer datastore found with no framedata object\n");
+ ast_channel_unlock(chan);
+ return 0;
+ }
+ if (jb_framedata_update(framedata, data, value)) {
+ ast_log(LOG_WARNING, "Failed to update framehook with new jitterbuffer\n");
+ ast_channel_unlock(chan);
+ return 0;
+ }
+ ast_channel_unlock(chan);
+
+ /* Notify the channel that a jitter bugger was updated */
+ ast_debug(1, " Putting control frame for jitterbuffer\n");
+ ast_indicate(chan, AST_CONTROL_JITTERBUFFER_UPDATE);
return 0;
}
-
- if (jb_framedata_init(framedata, data, value)) {
+ ast_channel_unlock(chan);
+
+ if (!(framedata = jb_framedata_create(data, value))) {
+ return 0;
+ }
+ interface.data = framedata;
+
+ if (!(datastore = ast_datastore_alloc(&jb_datastore, NULL))) {
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 {
+ framedata->framehook_id = ast_framehook_attach(chan, &interface);
+ if (framedata->framehook_id < 0) {
jb_framedata_destroy(framedata);
- framedata = NULL;
- }
+ ast_datastore_free(datastore);
+ ast_channel_unlock(chan);
+ return 0;
+ }
+
+ datastore->data = framedata;
+ ast_channel_datastore_add(chan, datastore);
+ ast_channel_set_fd(chan, AST_JITTERBUFFER_FD, framedata->timer_fd);
ast_channel_unlock(chan);
+ /* Notify the channel that a jitter bugger was updated */
+ ast_debug(1, " Putting control frame for jitterbuffer\n");
+ ast_indicate(chan, AST_CONTROL_JITTERBUFFER_UPDATE);
return 0;
}
Modified: team/mjordan/out-of-order-dtmf/include/asterisk/frame.h
URL: http://svnview.digium.com/svn/asterisk/team/mjordan/out-of-order-dtmf/include/asterisk/frame.h?view=diff&rev=365691&r1=365142&r2=365691
==============================================================================
--- team/mjordan/out-of-order-dtmf/include/asterisk/frame.h (original)
+++ team/mjordan/out-of-order-dtmf/include/asterisk/frame.h Tue May 8 15:03:07 2012
@@ -234,38 +234,39 @@
#define AST_HTML_LINKREJECT 20
enum ast_control_frame_type {
- AST_CONTROL_HANGUP = 1, /*!< Other end has hungup */
- AST_CONTROL_RING = 2, /*!< Local ring */
- AST_CONTROL_RINGING = 3, /*!< Remote end is ringing */
- AST_CONTROL_ANSWER = 4, /*!< Remote end has answered */
- AST_CONTROL_BUSY = 5, /*!< Remote end is busy */
- AST_CONTROL_TAKEOFFHOOK = 6, /*!< Make it go off hook */
- AST_CONTROL_OFFHOOK = 7, /*!< Line is off hook */
- AST_CONTROL_CONGESTION = 8, /*!< Congestion (circuits busy) */
- AST_CONTROL_FLASH = 9, /*!< Flash hook */
- AST_CONTROL_WINK = 10, /*!< Wink */
- AST_CONTROL_OPTION = 11, /*!< Set a low-level option */
- AST_CONTROL_RADIO_KEY = 12, /*!< Key Radio */
- AST_CONTROL_RADIO_UNKEY = 13, /*!< Un-Key Radio */
- AST_CONTROL_PROGRESS = 14, /*!< Indicate PROGRESS */
- AST_CONTROL_PROCEEDING = 15, /*!< Indicate CALL PROCEEDING */
- AST_CONTROL_HOLD = 16, /*!< Indicate call is placed on hold */
- AST_CONTROL_UNHOLD = 17, /*!< Indicate call is left from hold */
- AST_CONTROL_VIDUPDATE = 18, /*!< Indicate video frame update */
- _XXX_AST_CONTROL_T38 = 19, /*!< T38 state change request/notification \deprecated This is no longer supported. Use AST_CONTROL_T38_PARAMETERS instead. */
- AST_CONTROL_SRCUPDATE = 20, /*!< Indicate source of media has changed */
- AST_CONTROL_TRANSFER = 21, /*!< Indicate status of a transfer request */
- AST_CONTROL_CONNECTED_LINE = 22,/*!< Indicate connected line has changed */
- AST_CONTROL_REDIRECTING = 23, /*!< Indicate redirecting id has changed */
- AST_CONTROL_T38_PARAMETERS = 24,/*!< T38 state change request/notification with parameters */
- AST_CONTROL_CC = 25, /*!< Indication that Call completion service is possible */
- AST_CONTROL_SRCCHANGE = 26, /*!< Media source has changed and requires a new RTP SSRC */
- AST_CONTROL_READ_ACTION = 27, /*!< Tell ast_read to take a specific action */
- AST_CONTROL_AOC = 28, /*!< Advice of Charge with encoded generic AOC payload */
- AST_CONTROL_END_OF_Q = 29, /*!< Indicate that this position was the end of the channel queue for a softhangup. */
- AST_CONTROL_INCOMPLETE = 30, /*!< Indication that the extension dialed is incomplete */
- AST_CONTROL_MCID = 31, /*!< Indicate that the caller is being malicious. */
- AST_CONTROL_UPDATE_RTP_PEER = 32, /*!< Interrupt the bridge and have it update the peer */
+ AST_CONTROL_HANGUP = 1, /*!< Other end has hungup */
+ AST_CONTROL_RING = 2, /*!< Local ring */
+ AST_CONTROL_RINGING = 3, /*!< Remote end is ringing */
+ AST_CONTROL_ANSWER = 4, /*!< Remote end has answered */
+ AST_CONTROL_BUSY = 5, /*!< Remote end is busy */
+ AST_CONTROL_TAKEOFFHOOK = 6, /*!< Make it go off hook */
+ AST_CONTROL_OFFHOOK = 7, /*!< Line is off hook */
+ AST_CONTROL_CONGESTION = 8, /*!< Congestion (circuits busy) */
+ AST_CONTROL_FLASH = 9, /*!< Flash hook */
+ AST_CONTROL_WINK = 10, /*!< Wink */
+ AST_CONTROL_OPTION = 11, /*!< Set a low-level option */
+ AST_CONTROL_RADIO_KEY = 12, /*!< Key Radio */
+ AST_CONTROL_RADIO_UNKEY = 13, /*!< Un-Key Radio */
+ AST_CONTROL_PROGRESS = 14, /*!< Indicate PROGRESS */
+ AST_CONTROL_PROCEEDING = 15, /*!< Indicate CALL PROCEEDING */
+ AST_CONTROL_HOLD = 16, /*!< Indicate call is placed on hold */
+ AST_CONTROL_UNHOLD = 17, /*!< Indicate call is left from hold */
+ AST_CONTROL_VIDUPDATE = 18, /*!< Indicate video frame update */
+ _XXX_AST_CONTROL_T38 = 19, /*!< T38 state change request/notification \deprecated This is no longer supported. Use AST_CONTROL_T38_PARAMETERS instead. */
+ AST_CONTROL_SRCUPDATE = 20, /*!< Indicate source of media has changed */
+ AST_CONTROL_TRANSFER = 21, /*!< Indicate status of a transfer request */
+ AST_CONTROL_CONNECTED_LINE = 22, /*!< Indicate connected line has changed */
+ AST_CONTROL_REDIRECTING = 23, /*!< Indicate redirecting id has changed */
+ AST_CONTROL_T38_PARAMETERS = 24, /*!< T38 state change request/notification with parameters */
+ AST_CONTROL_CC = 25, /*!< Indication that Call completion service is possible */
+ AST_CONTROL_SRCCHANGE = 26, /*!< Media source has changed and requires a new RTP SSRC */
+ AST_CONTROL_READ_ACTION = 27, /*!< Tell ast_read to take a specific action */
+ AST_CONTROL_AOC = 28, /*!< Advice of Charge with encoded generic AOC payload */
+ AST_CONTROL_END_OF_Q = 29, /*!< Indicate that this position was the end of the channel queue for a softhangup. */
+ AST_CONTROL_INCOMPLETE = 30, /*!< Indication that the extension dialed is incomplete */
+ AST_CONTROL_MCID = 31, /*!< Indicate that the caller is being malicious. */
+ AST_CONTROL_UPDATE_RTP_PEER = 32, /*!< Interrupt the bridge and have it update the peer */
+ AST_CONTROL_JITTERBUFFER_UPDATE = 33, /*< A JitterBuffer has been updated on the channel */
};
enum ast_frame_read_action {
Modified: team/mjordan/out-of-order-dtmf/include/asterisk/framehook.h
URL: http://svnview.digium.com/svn/asterisk/team/mjordan/out-of-order-dtmf/include/asterisk/framehook.h?view=diff&rev=365691&r1=365142&r2=365691
==============================================================================
--- team/mjordan/out-of-order-dtmf/include/asterisk/framehook.h (original)
+++ team/mjordan/out-of-order-dtmf/include/asterisk/framehook.h Tue May 8 15:03:07 2012
@@ -48,6 +48,7 @@
.event_cb = hook_event_cb,
.destroy_cb = hook_destroy_cb,
.data = data, // where the data ptr points to any custom data used later by the hook cb.
+ .type = "MY_TYPE",
};
int id = ast_framehook_attach(channel, &interface);
\endcode
@@ -113,6 +114,7 @@
.version = AST_FRAMEHOOK_INTERFACE_VERSION,
.event_cb = hook_event_cb,
.destroy_cb = hook_destroy_cb,
+ .type = "SOME_TYPE",
};
int *id = ast_calloc(1, sizeof(int));
@@ -199,7 +201,21 @@
*/
typedef void (*ast_framehook_destroy_callback)(void *data);
-#define AST_FRAMEHOOK_INTERFACE_VERSION 1
+/*!
+ * \brief This callback allows a framehook to return whether or not it can fulfill
+ * some capability being requested
+ * \since 11
+ * \note This function does not have to be present in a framehook. If not implemented,
+ * any capabilities requested will assume to not be met by that framehook.
+ *
+ * \param chan The channel the framehook exists on
+ * \param capability The capability that the framehook be queried for
+ * \retval 0 if the framehook can meet the capability
+ * \retval 1 if the framehook cannot meet the capabilitiy
+ */
+typedef int (*ast_framehook_capability_exists)(struct ast_channel *chan, const char *capability);
+
+#define AST_FRAMEHOOK_INTERFACE_VERSION 2
/*! This interface is required for attaching a framehook to a channel. */
struct ast_framehook_interface {
/*! framehook interface version number */
@@ -209,10 +225,16 @@
/*! destroy_cb is optional. This function is called immediately before the framehook
* is destroyed to allow for stored_data cleanup. */
ast_framehook_destroy_callback destroy_cb;
+ /*! capability_cb is optional. This function is called if some external entity
+ * needs to determine if a framehook that matches some arbitrary capability exists
+ * on a particular channel
+ */
+ ast_framehook_capability_exists capability_cb;
/*! This pointer can represent any custom data to be stored on the !framehook. This
* data pointer will be provided during each event callback which allows the framehook
* to store any stateful data associated with the application using the hook. */
void *data;
+
};
/*!
@@ -222,7 +244,7 @@
* \param ast_channel, The channel to attach the hook on to.
* \param framehook interface, The framehook's callback functions and stored data.
*
- * \pre XXX The Channel must be locked during this function all.
+ * \pre XXX The Channel must be locked during this function call.
*
* \note The data pointer is never touched by the framehook API except to
* provide it during the event and destruction callbacks. It is entirely up to the
@@ -237,7 +259,7 @@
* \brief Detach an framehook from a channel.
* \since 1.8
*
- * \pre XXX The Channel must be locked during this function all.
+ * \pre XXX The Channel must be locked during this function call.
* If this function is never called after attaching an framehook,
* the framehook will be detached and destroyed during channel
* destruction.
@@ -250,6 +272,21 @@
* framehook either never existed on the channel, or was already detached.
*/
int ast_framehook_detach(struct ast_channel *chan, int framehook_id);
+
+/*!
+ * \brief Determine if a framehook is attached to a channel that provides some
+ * needed capability
+ * \since 11
+ *
+ * \pre The Channel must be locked during this function call.
+ *
+ * \param The channel that may possess a framehook
+ * \param The capability the framehook must provide
+ *
+ * \retval 0 a framehook providing the capability is on the channel
+ * \retval 1 no framehook providing the capability exists on the channel
+ */
+int ast_framehook_query_capability(struct ast_channel *chan, const char *capability);
/*!
* \brief This is used by the channel API to detach and destroy all
Modified: team/mjordan/out-of-order-dtmf/include/asterisk/rtp_engine.h
URL: http://svnview.digium.com/svn/asterisk/team/mjordan/out-of-order-dtmf/include/asterisk/rtp_engine.h?view=diff&rev=365691&r1=365142&r2=365691
==============================================================================
--- team/mjordan/out-of-order-dtmf/include/asterisk/rtp_engine.h (original)
+++ team/mjordan/out-of-order-dtmf/include/asterisk/rtp_engine.h Tue May 8 15:03:07 2012
@@ -333,6 +333,8 @@
void (*update_source)(struct ast_rtp_instance *instance);
/*! Callback to indicate that we should update the marker bit and ssrc */
void (*change_source)(struct ast_rtp_instance *instance);
+ /*! Callback to indicate that a jitterbuffer on the associated channel was updated */
+ void (*jitterbuffer_update)(struct ast_rtp_instance *instance, struct ast_channel *chan);
/*! Callback for setting an extended RTP property */
int (*extended_prop_set)(struct ast_rtp_instance *instance, int property, void *value);
/*! Callback for getting an extended RTP property */
@@ -1297,6 +1299,26 @@
void ast_rtp_instance_update_source(struct ast_rtp_instance *instance);
/*!
+ * \brief Indicate to the RTP engine that a jitterbuffer has been updated on the associated channel
+ *
+ * \param instance The instance whose channel's jitterbuffers were updated
+ * \param chan The channel that the jitterbuffers exist on
+ *
+ * Example usage:
+ *
+ * \code
+ * ast_rtp_instance_jitterbuffer_update(instance);
+ * \endcode
+ *
+ * This indicates that some jitterbuffer on the channel has been added or updated
+ * with new properties. The RTP layer can, if it chooses, make modifications as to
+ * how it treats frames based on this information.
+ *
+ * \since 11
+ */
+void ast_rtp_instance_jitterbuffer_update(struct ast_rtp_instance *instance, struct ast_channel *chan);
+
+/*!
* \brief Indicate a new source of audio has dropped in and the ssrc should change
*
* \param instance Instance that the new media source is feeding into
Modified: team/mjordan/out-of-order-dtmf/main/channel.c
URL: http://svnview.digium.com/svn/asterisk/team/mjordan/out-of-order-dtmf/main/channel.c?view=diff&rev=365691&r1=365142&r2=365691
==============================================================================
--- team/mjordan/out-of-order-dtmf/main/channel.c (original)
+++ team/mjordan/out-of-order-dtmf/main/channel.c Tue May 8 15:03:07 2012
@@ -3450,6 +3450,7 @@
case AST_CONTROL_UPDATE_RTP_PEER:
case AST_CONTROL_HOLD:
case AST_CONTROL_UNHOLD:
+ case AST_CONTROL_JITTERBUFFER_UPDATE:
case -1:
/* Unimportant */
break;
@@ -4168,6 +4169,7 @@
case AST_CONTROL_END_OF_Q:
case AST_CONTROL_MCID:
case AST_CONTROL_UPDATE_RTP_PEER:
+ case AST_CONTROL_JITTERBUFFER_UPDATE:
break;
case AST_CONTROL_INCOMPLETE:
@@ -4357,6 +4359,7 @@
case AST_CONTROL_END_OF_Q:
case AST_CONTROL_MCID:
case AST_CONTROL_UPDATE_RTP_PEER:
+ case AST_CONTROL_JITTERBUFFER_UPDATE:
/* Nothing left to do for these. */
res = 0;
break;
Modified: team/mjordan/out-of-order-dtmf/main/file.c
URL: http://svnview.digium.com/svn/asterisk/team/mjordan/out-of-order-dtmf/main/file.c?view=diff&rev=365691&r1=365142&r2=365691
==============================================================================
--- team/mjordan/out-of-order-dtmf/main/file.c (original)
+++ team/mjordan/out-of-order-dtmf/main/file.c Tue May 8 15:03:07 2012
@@ -1356,6 +1356,7 @@
[... 248 lines stripped ...]
More information about the asterisk-commits
mailing list