[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