[svn-commits] twilson: trunk r310941 - in /trunk: ./ main/features.c

SVN commits to the Digium repositories svn-commits at lists.digium.com
Wed Mar 16 12:29:28 CDT 2011


Author: twilson
Date: Wed Mar 16 12:29:16 2011
New Revision: 310941

URL: http://svnview.digium.com/svn/asterisk?view=rev&rev=310941
Log:
Merged revisions 310902 via svnmerge from 
https://origsvn.digium.com/svn/asterisk/branches/1.8

................
  r310902 | twilson | 2011-03-16 12:19:57 -0500 (Wed, 16 Mar 2011) | 43 lines
  
  Merged revisions 310889 via svnmerge from 
  https://origsvn.digium.com/svn/asterisk/branches/1.6.2
  
  ................
    r310889 | twilson | 2011-03-16 12:03:27 -0500 (Wed, 16 Mar 2011) | 36 lines
    
    Merged revisions 310888 via svnmerge from 
    https://origsvn.digium.com/svn/asterisk/branches/1.4
    
    ........
      r310888 | twilson | 2011-03-16 11:58:42 -0500 (Wed, 16 Mar 2011) | 29 lines
      
      Don't delay DTMF in core bridge while listening for DTMF features
      
      This patch is mostly the work of Olle Johansson. I did some cleanup and
      added the silence generating code if transmit_silence is set.
      
      When a channel listens for DTMF in the core bridge, the outbound DTMF is not
      sent until we have received DTMF_END. For a long DTMF, this is a disaster. We
      send 4 seconds of DTMF to Asterisk, which sends no audio for those 4 seconds.
      Some products see this delay and the time skew on RTP packets that results and
      start ignoring the audio that is sent afterward.
      
      With this change, the DTMF_BEGIN frame is inspected and checked. If it matches
      a feature code, we wait for DTMF_END and activate the feature as before. If
      transmit_silence=yes in asterisk.conf, silence is sent if we paritally match a
      multi-digit feature. If it doesn't match a feature, the frame is forwarded
      along with the DTMF_END without delay. By doing it this way, DTMF is not delayed.
      
      (closes issue #15642)
      Reported by: jasonshugart
      Patches: 
            issue_15652_dtmf_ast-1.4.patch.txt uploaded by twilson (license 396)
      Tested by: globalnetinc, jde
      
      (closes issue #16625)
      Reported by: sharvanek
      
      Review: https://reviewboard.asterisk.org/r/1092/
      Review: https://reviewboard.asterisk.org/r/1125/
    ........
  ................
................

Modified:
    trunk/   (props changed)
    trunk/main/features.c

Propchange: trunk/
------------------------------------------------------------------------------
Binary property 'branch-1.8-merged' - no diff available.

Modified: trunk/main/features.c
URL: http://svnview.digium.com/svn/asterisk/trunk/main/features.c?view=diff&rev=310941&r1=310940&r2=310941
==============================================================================
--- trunk/main/features.c (original)
+++ trunk/main/features.c Wed Mar 16 12:29:16 2011
@@ -371,6 +371,12 @@
 
 static AST_RWLIST_HEAD_STATIC(feature_groups, feature_group);
 
+typedef enum {
+	FEATURE_INTERPRET_DETECT, /* Used by ast_feature_detect */
+	FEATURE_INTERPRET_DO,     /* Used by feature_interpret */
+	FEATURE_INTERPRET_CHECK,  /* Used by feature_check */
+} feature_interpret_op;
+
 static char *parkedcall = "ParkedCall";
 
 static char pickup_ext[AST_MAX_EXTENSION];                 /*!< Call pickup extension */
@@ -2746,7 +2752,7 @@
 */
 static int feature_interpret_helper(struct ast_channel *chan, struct ast_channel *peer,
 	struct ast_bridge_config *config, const char *code, int sense, char *dynamic_features_buf,
-	struct ast_flags *features, int operation, struct ast_call_feature *feature)
+	struct ast_flags *features, feature_interpret_op operation, struct ast_call_feature *feature)
 {
 	int x;
 	struct feature_group *fg = NULL;
@@ -2756,7 +2762,7 @@
 	int res = AST_FEATURE_RETURN_PASSDIGITS;
 	int feature_detected = 0;
 
-	if (!(peer && chan && config) && operation) {
+	if (!(peer && chan && config) && operation == FEATURE_INTERPRET_DO) {
 		return -1; /* can not run feature operation */
 	}
 
@@ -2767,15 +2773,20 @@
 			/* Feature is up for consideration */
 			if (!strcmp(builtin_features[x].exten, code)) {
 				ast_debug(3, "Feature detected: fname=%s sname=%s exten=%s\n", builtin_features[x].fname, builtin_features[x].sname, builtin_features[x].exten);
-				if (operation) {
+				if (operation == FEATURE_INTERPRET_CHECK) {
+					res = AST_FEATURE_RETURN_SUCCESS; /* We found something */
+				} else if (operation == FEATURE_INTERPRET_DO) {
 					res = builtin_features[x].operation(chan, peer, config, code, sense, NULL);
 				}
-				memcpy(feature, &builtin_features[x], sizeof(feature));
+				if (feature) {
+					memcpy(feature, &builtin_features[x], sizeof(feature));
+				}
 				feature_detected = 1;
 				break;
 			} else if (!strncmp(builtin_features[x].exten, code, strlen(code))) {
-				if (res == AST_FEATURE_RETURN_PASSDIGITS)
+				if (res == AST_FEATURE_RETURN_PASSDIGITS) {
 					res = AST_FEATURE_RETURN_STOREDIGITS;
+				}
 			}
 		}
 	}
@@ -2825,10 +2836,14 @@
 		/* Feature is up for consideration */
 		if (!strcmp(tmpfeature->exten, code)) {
 			ast_verb(3, " Feature Found: %s exten: %s\n",tmpfeature->sname, tok);
-			if (operation) {
+			if (operation == FEATURE_INTERPRET_CHECK) {
+				res = AST_FEATURE_RETURN_SUCCESS; /* We found something */
+			} else if (operation == FEATURE_INTERPRET_DO) {
 				res = tmpfeature->operation(chan, peer, config, code, sense, tmpfeature);
 			}
-			memcpy(feature, tmpfeature, sizeof(feature));
+			if (feature) {
+				memcpy(feature, tmpfeature, sizeof(feature));
+			}
 			if (res != AST_FEATURE_RETURN_KEEPTRYING) {
 				AST_RWLIST_UNLOCK(&feature_list);
 				break;
@@ -2876,13 +2891,19 @@
 
 	ast_debug(3, "Feature interpret: chan=%s, peer=%s, code=%s, sense=%d, features=%d, dynamic=%s\n", chan->name, peer->name, code, sense, features.flags, dynamic_features_buf);
 
-	return feature_interpret_helper(chan, peer, config, code, sense, dynamic_features_buf, &features, 1, &feature);
+	return feature_interpret_helper(chan, peer, config, code, sense, dynamic_features_buf, &features, FEATURE_INTERPRET_DO, &feature);
 }
 
 
 int ast_feature_detect(struct ast_channel *chan, struct ast_flags *features, const char *code, struct ast_call_feature *feature) {
 
-	return feature_interpret_helper(chan, NULL, NULL, code, 0, NULL, features, 0, feature);
+	return feature_interpret_helper(chan, NULL, NULL, code, 0, NULL, features, FEATURE_INTERPRET_DETECT, feature);
+}
+
+/*! \brief Check if a feature exists */
+static int feature_check(struct ast_channel *chan, struct ast_flags *features, char *code) {
+
+	return feature_interpret_helper(chan, NULL, NULL, code, 0, NULL, features, FEATURE_INTERPRET_CHECK, NULL);
 }
 
 static void set_config_flags(struct ast_channel *chan, struct ast_channel *peer, struct ast_bridge_config *config)
@@ -3419,6 +3440,7 @@
 	int hasfeatures=0;
 	int hadfeatures=0;
 	int autoloopflag;
+	int sendingdtmfdigit = 0;
 	int we_disabled_peer_cdr = 0;
 	struct ast_option_header *aoh;
 	struct ast_cdr *bridge_cdr = NULL;
@@ -3427,6 +3449,7 @@
 	struct ast_cdr *peer_cdr = peer->cdr; /* the proper chan cdr, if there are forked cdrs */
 	struct ast_cdr *new_chan_cdr = NULL; /* the proper chan cdr, if there are forked cdrs */
 	struct ast_cdr *new_peer_cdr = NULL; /* the proper chan cdr, if there are forked cdrs */
+	struct ast_silence_generator *silgen = NULL;
 
 	if (chan && peer) {
 		pbx_builtin_setvar_helper(chan, "BRIDGEPEER", peer->name);
@@ -3693,7 +3716,38 @@
 				break;
 			}
 		} else if (f->frametype == AST_FRAME_DTMF_BEGIN) {
-			/* eat it */
+			struct ast_flags *cfg;
+			char dtmfcode[2] = { f->subclass.integer, };
+			size_t featurelen;
+
+			if (who == chan) {
+				featurelen = strlen(chan_featurecode);
+				cfg = &(config->features_caller);
+			} else {
+				featurelen = strlen(peer_featurecode);
+				cfg = &(config->features_callee);
+			}
+			/* Take a peek if this (possibly) matches a feature. If not, just pass this
+			 * DTMF along untouched. If this is not the first digit of a multi-digit code
+			 * then we need to fall through and stream the characters if it matches */
+			if (featurelen == 0
+				&& feature_check(chan, cfg, &dtmfcode[0]) == AST_FEATURE_RETURN_PASSDIGITS) {
+				if (option_debug > 3) {
+					ast_log(LOG_DEBUG, "Passing DTMF through, since it is not a feature code\n");
+				}
+				ast_write(other, f);
+				sendingdtmfdigit = 1;
+			} else {
+				/* If ast_opt_transmit_silence is set, then we need to make sure we are
+				 * transmitting something while we hold on to the DTMF waiting for a
+				 * feature. */
+				if (!silgen && ast_opt_transmit_silence) {
+					silgen = ast_channel_start_silence_generator(other);
+				}
+				if (option_debug > 3) {
+					ast_log(LOG_DEBUG, "Not passing DTMF through, since it may be a feature code\n");
+				}
+			}
 		} else if (f->frametype == AST_FRAME_DTMF) {
 			char *featurecode;
 			int sense;
@@ -3707,41 +3761,53 @@
 				sense = FEATURE_SENSE_PEER;
 				featurecode = peer_featurecode;
 			}
-			/*! append the event to featurecode. we rely on the string being zero-filled, and
-			 * not overflowing it. 
-			 * \todo XXX how do we guarantee the latter ?
-			 */
-			featurecode[strlen(featurecode)] = f->subclass.integer;
-			/* Get rid of the frame before we start doing "stuff" with the channels */
-			ast_frfree(f);
-			f = NULL;
-			config->feature_timer = 0;
-			res = feature_interpret(chan, peer, config, featurecode, sense);
-			switch(res) {
-			case AST_FEATURE_RETURN_PASSDIGITS:
-				ast_dtmf_stream(other, who, featurecode, 0, 0);
-				/* Fall through */
-			case AST_FEATURE_RETURN_SUCCESS:
-				memset(featurecode, 0, sizeof(chan_featurecode));
-				break;
-			}
-			if (res >= AST_FEATURE_RETURN_PASSDIGITS) {
-				res = 0;
+
+			if (sendingdtmfdigit == 1) {
+				/* We let the BEGIN go through happily, so let's not bother with the END,
+				 * since we already know it's not something we bother with */
+				ast_write(other, f);
+				sendingdtmfdigit = 0;
 			} else {
-				break;
-			}
-			hasfeatures = !ast_strlen_zero(chan_featurecode) || !ast_strlen_zero(peer_featurecode);
-			if (hadfeatures && !hasfeatures) {
-				/* Feature completed or timed out */
+				/*! append the event to featurecode. we rely on the string being zero-filled, and
+				 * not overflowing it. 
+				 * \todo XXX how do we guarantee the latter ?
+				 */
+				featurecode[strlen(featurecode)] = f->subclass.integer;
+				/* Get rid of the frame before we start doing "stuff" with the channels */
+				ast_frfree(f);
+				f = NULL;
+				if (silgen) {
+					ast_channel_stop_silence_generator(other, silgen);
+					silgen = NULL;
+				}
 				config->feature_timer = 0;
-			} else if (hasfeatures) {
-				if (config->timelimit) {
-					/* No warning next time - we are waiting for future */
-					ast_set_flag(config, AST_FEATURE_WARNING_ACTIVE);
+				res = feature_interpret(chan, peer, config, featurecode, sense);
+				switch(res) {
+				case AST_FEATURE_RETURN_PASSDIGITS:
+					ast_dtmf_stream(other, who, featurecode, 0, 0);
+					/* Fall through */
+				case AST_FEATURE_RETURN_SUCCESS:
+					memset(featurecode, 0, sizeof(chan_featurecode));
+					break;
 				}
-				config->feature_start_time = ast_tvnow();
-				config->feature_timer = featuredigittimeout;
-				ast_debug(1, "Set feature timer to %ld ms\n", config->feature_timer);
+				if (res >= AST_FEATURE_RETURN_PASSDIGITS) {
+					res = 0;
+				} else {
+					break;
+				}
+				hasfeatures = !ast_strlen_zero(chan_featurecode) || !ast_strlen_zero(peer_featurecode);
+				if (hadfeatures && !hasfeatures) {
+					/* Feature completed or timed out */
+					config->feature_timer = 0;
+				} else if (hasfeatures) {
+					if (config->timelimit) {
+						/* No warning next time - we are waiting for future */
+						ast_set_flag(config, AST_FEATURE_WARNING_ACTIVE);
+					}
+					config->feature_start_time = ast_tvnow();
+					config->feature_timer = featuredigittimeout;
+					ast_debug(1, "Set feature timer to %ld ms\n", config->feature_timer);
+				}
 			}
 		}
 		if (f)
@@ -3749,7 +3815,13 @@
 
 	}
 	ast_cel_report_event(chan, AST_CEL_BRIDGE_END, NULL, NULL, NULL);
-   before_you_go:
+
+before_you_go:
+	/* Just in case something weird happened and we didn't clean up the silence generator... */
+	if (silgen) {
+		ast_channel_stop_silence_generator(who == chan ? peer : chan, silgen);
+		silgen = NULL;
+	}
 
 	if (ast_test_flag(chan,AST_FLAG_BRIDGE_HANGUP_DONT)) {
 		ast_clear_flag(chan,AST_FLAG_BRIDGE_HANGUP_DONT); /* its job is done */




More information about the svn-commits mailing list