[asterisk-commits] file: branch file/bridging r92509 - in /team/file/bridging: apps/ include/ast...

SVN commits to the Asterisk project asterisk-commits at lists.digium.com
Wed Dec 12 11:24:54 CST 2007


Author: file
Date: Wed Dec 12 11:24:53 2007
New Revision: 92509

URL: http://svn.digium.com/view/asterisk?view=rev&rev=92509
Log:
Check in my latest batch of work. This adds support for single DTMF key based features, multiple ones do work but if you enter an incorrect string they are not sent to the other side after a timeout. This also fixes a few bugs elsewhere.

Modified:
    team/file/bridging/apps/app_bridgetest.c
    team/file/bridging/apps/app_confbridge.c
    team/file/bridging/include/asterisk/bridging.h
    team/file/bridging/main/bridging.c

Modified: team/file/bridging/apps/app_bridgetest.c
URL: http://svn.digium.com/view/asterisk/team/file/bridging/apps/app_bridgetest.c?view=diff&rev=92509&r1=92508&r2=92509
==============================================================================
--- team/file/bridging/apps/app_bridgetest.c (original)
+++ team/file/bridging/apps/app_bridgetest.c Wed Dec 12 11:24:53 2007
@@ -53,7 +53,7 @@
 static int bridge_test_exec(struct ast_channel *chan, void *data)
 {
 	struct ast_dial *dial = NULL;
-	struct ast_bridge *bridge = NULL;
+	struct ast_bridge *bridge0 = NULL, *bridge1 = NULL;
 	char *tech = NULL, *resource = NULL;
 
 	if (ast_strlen_zero(data)) {
@@ -84,19 +84,26 @@
 		return -1;
 	}
 
-	/* Create a new bridge to bring the two channels into */
-	if (!(bridge = ast_bridge_new(AST_BRIDGE_CAPABILITY_1TO1MIX, AST_BRIDGE_FLAG_DISSOLVE))) {
-		ast_log(LOG_WARNING, "Failed to create bridge ;(\n");
-		ast_dial_destroy(dial);
-		return -1;
-	}
+	/* Create a new bridge to put the channel that answered into */
+	bridge0 = ast_bridge_new(AST_BRIDGE_CAPABILITY_1TO1MIX, AST_BRIDGE_FLAG_DISSOLVE | AST_BRIDGE_FLAG_SMART);
 
-	/* Put the dialed channel into the bridge via async */
-	ast_bridge_impart(bridge, ast_dial_answered(dial), NULL, NULL);
+	/* Impart it onto the bridge... */
+	ast_bridge_impart(bridge0, ast_dial_answered(dial), NULL, NULL);
 
-	ast_bridge_join(bridge, chan, ast_dial_answered(dial), NULL);
-	
-	ast_bridge_destroy(bridge);
+	/* Create a new bridge to put the channel that called into */
+	bridge1 = ast_bridge_new(AST_BRIDGE_CAPABILITY_1TO1MIX, AST_BRIDGE_FLAG_DISSOLVE | AST_BRIDGE_FLAG_SMART);
+
+	/* Impart this one as well onto the bridge... */
+	ast_bridge_impart(bridge1, chan, NULL, NULL);
+
+	/* Merge them together */
+	ast_bridge_merge(bridge0, bridge1);
+
+	/* Destroy the second bridge since it's over */
+	ast_bridge_destroy(bridge1);
+
+	/* Hang out for a bit */
+	usleep(10000000);
 
 	return 0;
 }

Modified: team/file/bridging/apps/app_confbridge.c
URL: http://svn.digium.com/view/asterisk/team/file/bridging/apps/app_confbridge.c?view=diff&rev=92509&r1=92508&r2=92509
==============================================================================
--- team/file/bridging/apps/app_confbridge.c (original)
+++ team/file/bridging/apps/app_confbridge.c Wed Dec 12 11:24:53 2007
@@ -54,12 +54,14 @@
 	OPTION_DYNAMIC = (1 << 0),
 	OPTION_ADMIN = (1 << 1),
 	OPTION_MENU = (1 << 2),
+	OPTION_MUSICONHOLD = (1 << 3),
 } option_flags;
 
 AST_APP_OPTIONS(app_opts,{
 	AST_APP_OPTION('a', OPTION_ADMIN),
 	AST_APP_OPTION('d', OPTION_DYNAMIC),
 	AST_APP_OPTION('m', OPTION_MENU),
+	AST_APP_OPTION('M', OPTION_MUSICONHOLD),
 });
 
 struct conference_bridge {
@@ -111,6 +113,9 @@
 
 static int menu_callback_user(struct ast_bridge *bridge, struct ast_bridge_channel *bridge_channel)
 {
+	ast_streamfile(bridge_channel->chan, "demo-congrats", bridge_channel->chan->language);
+	ast_waitstream(bridge_channel->chan, "");
+	bridge_channel->state = AST_BRIDGE_CHANNEL_STATE_WAIT;
 	return 0;
 }
 
@@ -146,8 +151,18 @@
 	}
 
 	/* If the menu option is enabled provide a user or admin menu as a custom feature hook */
-	if (ast_test_flag(&flags, OPTION_MENU))
+	if (ast_test_flag(&flags, OPTION_MENU)) {
+		ast_bridge_features_init(&features);
 		ast_bridge_features_hook(&features, "#", (ast_test_flag(&flags, OPTION_ADMIN) ? menu_callback_admin : menu_callback_user), NULL);
+	}
+
+	/* If this is the first user in the conference announce it */
+	if (conference_bridge->users == 1) {
+		ast_streamfile(chan, "conf-onlyperson", chan->language);
+		ast_waitstream(chan, AST_DIGIT_ANY);
+		ast_stopstream(chan);
+		/* If the music on hold option is enabled start it up and wait for the next user to enter */
+	}
 
 	/* Actually join the bridge */
 	ast_bridge_join(conference_bridge->bridge, chan, NULL, &features);

Modified: team/file/bridging/include/asterisk/bridging.h
URL: http://svn.digium.com/view/asterisk/team/file/bridging/include/asterisk/bridging.h?view=diff&rev=92509&r1=92508&r2=92509
==============================================================================
--- team/file/bridging/include/asterisk/bridging.h (original)
+++ team/file/bridging/include/asterisk/bridging.h Wed Dec 12 11:24:53 2007
@@ -71,7 +71,7 @@
 enum ast_bridge_builtin_feature {
 	AST_BRIDGE_BUILTIN_BLINDTRANSFER = 0, /*! Blind transfer */
 	AST_BRIDGE_BUILTIN_ATTENDEDTRANSFER,  /*! Attended transfer */
-	AST_BRIDGE_BUILTIN_END,               /*! End terminator, has to be last */
+	AST_BRIDGE_BUILTIN_HANGUP,            /*! Hangup on DTMF stream */
 };
 
 struct ast_bridge;
@@ -106,8 +106,8 @@
 };
 
 struct ast_bridge_features {
-	enum ast_bridge_builtin_feature builtin_features[AST_BRIDGE_BUILTIN_END]; /*! Enabled built in features */
-	AST_LIST_HEAD_NOLOCK(, ast_bridge_features_hook) hooks;
+	AST_LIST_HEAD_NOLOCK(, ast_bridge_features_hook) hooks;                   /*! Attached hooks */
+	int usable:1;                                                             /*! Whether this should be considered usable or not */
 };
 
 struct ast_bridge_channel {
@@ -120,6 +120,8 @@
 	int fds[4];                              /*! Additional file descriptors to look at */
 	int suspended:1;                         /*! Is this bridged channel suspended from the bridge or not? */
 	int muted:1;                             /*! Is this bridged channel muted or not? */
+	int go_feature;                          /*! Once a DTMF end has been received, start the feature */
+	char dtmfq[8];                           /*! DTMF queue for features */
 	struct ast_bridge_features *features;    /*! Enabled features information */
 	AST_LIST_ENTRY(ast_bridge_channel) list; /*! Linked list information */
 };
@@ -132,6 +134,7 @@
 	struct ast_bridge_technology *technology;            /*! Technology in use on the bridge */
 	void *bridge_pvt;                                    /*! Private information unique to the bridge technology (not always needed) */
 	pthread_t thread;                                    /*! Thread running the bridge */
+	ast_cond_t cond;                                     /*! Condition */
 	struct ast_bridge_features features;                 /*! Enabled features information */
 	AST_LIST_HEAD_NOLOCK(, ast_bridge_channel) channels; /*! Channels participating in this bridge */
 };
@@ -192,6 +195,12 @@
  */
 int ast_bridge_change_state(struct ast_bridge_channel *bridge_channel, enum ast_bridge_channel_state new_state);
 
+/*! \brief Request that the bridge rebuild itself
+ * \param bridge Bridge in question
+ * \ return Returns 0 on success, -1 on failure
+ */
+int ast_bridge_rebuild(struct ast_bridge *bridge);
+
 /*! \brief Merge two bridges together
  * \param bridge0 First bridge
  * \param bridge1 Second bridge
@@ -221,9 +230,16 @@
 /*! \brief Enable a built in feature on a bridge features structure
  * \param features Bridge features structure
  * \param feature Feature to enable
- * \return Returns 0 on success, -1 on failure
- */
-int ast_bridge_features_enable(struct ast_bridge_features *features, enum ast_bridge_builtin_feature feature);
+ * \param dtmf Optionally the DTMF stream to trigger the feature, if not specified it will be the default
+ * \return Returns 0 on success, -1 on failure
+ */
+int ast_bridge_features_enable(struct ast_bridge_features *features, enum ast_bridge_builtin_feature feature, const char *dtmf);
+
+/*! \brief Initialize bridge features structure
+ * \param features Bridge featues structure
+ * \return Returns 0 on success, -1 on failure
+ */
+int ast_bridge_features_init(struct ast_bridge_features *features);
 
 /*! \brief Clean up the contents of a bridge features structure
  * \param features Bridge features structure

Modified: team/file/bridging/main/bridging.c
URL: http://svn.digium.com/view/asterisk/team/file/bridging/main/bridging.c?view=diff&rev=92509&r1=92508&r2=92509
==============================================================================
--- team/file/bridging/main/bridging.c (original)
+++ team/file/bridging/main/bridging.c Wed Dec 12 11:24:53 2007
@@ -120,6 +120,60 @@
 	return bridge_channel;
 }
 
+/*! \brief Internal function for handling DTMF frames when features are present on a bridge channel */
+static struct ast_frame *bridge_handle_feature_dtmf(struct ast_bridge *bridge, struct ast_bridge_channel *bridge_channel, struct ast_frame *frame)
+{
+	struct ast_bridge_features *features = (bridge_channel->features ? bridge_channel->features : &bridge->features);
+	struct ast_bridge_features_hook *hook = NULL;
+
+	/* features will always be non-NULL, so just see if it is usable. If it is not we just pass back the frame and be happy. */
+	if (!features->usable) {
+		ast_debug(1, "No usable features enabled on bridge channel %p participating in bridge %p\n", bridge_channel, bridge);
+		return frame;
+	}
+
+	/* DTMF end frames are handled a lil' bit differently */
+	if (frame->frametype == AST_FRAME_DTMF_END) {
+		/* See if the begin matched a DTMF feature, if not simply return it */
+		if (ast_strlen_zero(bridge_channel->dtmfq)) {
+			ast_debug(1, "DTMF feature queue on %p is empty, no feature was matched in the begin... just returning the frame.\n", bridge_channel);
+			return frame;
+		} else if (bridge_channel->go_feature) {
+			ast_debug(1, "DTMF feature matched on %p, going to execute the feature.\n", bridge_channel);
+			/* Tell the bridge thread that something is up... */
+			ast_bridge_rebuild(bridge);
+			ast_bridge_change_state(bridge_channel, AST_BRIDGE_CHANNEL_STATE_FEATURE);
+		}
+		/* Drop the frame and return NULL, nothing else to do */
+		ast_frfree(frame);
+		return NULL;
+	}
+
+	/* Add the DTMF coming in to the queue for easy matching */
+	bridge_channel->dtmfq[strlen(bridge_channel->dtmfq)] = frame->subclass;
+	ast_debug(1, "DTMF frame queue on %p is now %s\n", bridge_channel, bridge_channel->dtmfq);
+
+	/* See if the current DTMF stream in the queue matches or is close to a hook */
+	AST_LIST_TRAVERSE(&features->hooks, hook, list) {
+		/* See if the DTMF stream matches the hook */
+		if (!strcmp(hook->dtmf, bridge_channel->dtmfq)) {
+			ast_debug(1, "Bridge channel %p DTMF stream matches hook %p\n", bridge_channel, hook);
+			bridge_channel->go_feature = 1;
+			ast_frfree(frame);
+			return NULL;
+		} else if (!strncmp(hook->dtmf, bridge_channel->dtmfq, strlen(bridge_channel->dtmfq))) {
+			ast_debug(1, "Bridge channel %p DTMF stream CAN match hook %p\n", bridge_channel, hook);
+			ast_frfree(frame);
+			return NULL;
+		}
+	}
+
+	/* Alas... nothing matched so dump the DTMF queue and move on, for now */
+	bridge_channel->dtmfq[0] = '\0';
+	
+	return frame;
+}
+
 /*! \brief Internal function to handle when a channel or bridge needs servicing */
 static void bridge_handle_trip(struct ast_bridge *bridge, struct ast_bridge_channel *bridge_channel, struct ast_channel *chan, int outfd)
 {
@@ -144,8 +198,12 @@
 				}
 			}
 		} else {
-			/* Simply write the frame out to the bridge technology */
-			bridge->technology->write(bridge, bridge_channel, frame);
+			/* If this is DTMF pass it off to the feature handling code so it can determine whether to withhold it or let it through */
+			if (frame->frametype == AST_FRAME_DTMF_BEGIN || frame->frametype == AST_FRAME_DTMF_END)
+				frame = bridge_handle_feature_dtmf(bridge, bridge_channel, frame);
+			/* Simply write the frame out to the bridge technology if it still exists */
+			if (frame)
+				bridge->technology->write(bridge, bridge_channel, frame);
 		}
 
 		if (frame)
@@ -196,12 +254,15 @@
 			cs[count-1] = cs[127];
 		}
 
-		ast_mutex_unlock(&bridge->lock);
-		if (count)
+		/* If channels are actually present simply wait on them, otherwise wait on the pthread condition to trip */
+		if (count) {
+			ast_mutex_unlock(&bridge->lock);
 			winner = ast_waitfor_n(cs, count, &to);
-		else
-			usleep(1);
-		ast_mutex_lock(&bridge->lock);
+			ast_mutex_lock(&bridge->lock);
+		} else {
+			ast_debug(1, "Entering pthread condition wait on %p since we have no channels\n", bridge);
+			ast_cond_wait(&bridge->cond, &bridge->lock);
+		}
 		
 		if (count)
 			bridge_handle_trip(bridge, NULL, winner, -1);
@@ -291,8 +352,9 @@
 	if (!(bridge = ast_calloc(1, sizeof(*bridge))))
 		return NULL;
 
-	/* Initialize our bridge lock */
+	/* Initialize our bridge lock and condition */
 	ast_mutex_init(&bridge->lock);
+	ast_cond_init(&bridge->cond, NULL);
 
 	bridge->technology = bridge_technology;
 	bridge->thread = AST_PTHREADT_NULL;
@@ -331,7 +393,7 @@
 		/* Change thread pointer to indicate it should stop */
 		bridge->thread = AST_PTHREADT_STOP;
 		/* Request that the bridge thread rebuild it's array, this will cause it to see no channels exist and the thread will exit */
-		bridge->rebuild = 1;
+		ast_bridge_rebuild(bridge);
 		/* Poke the bridge thread out of it's poll if in it */
 		pthread_kill(thread, SIGURG);
 		/* Give up our lock so that the bridge thread can acquire it */
@@ -361,8 +423,9 @@
 			ast_debug(1, "Bridge technology %s failed to destroy bridge structure %p... trying our best\n", bridge->technology->name, bridge);
 	}
 
-	/* Destroy the mutex that protects the bridge */
+	/* Destroy the mutex that protects the bridge and the condition */
 	ast_mutex_destroy(&bridge->lock);
+	ast_cond_destroy(&bridge->cond);
 
 	/* Before we free the entire bridge we need to pass off our features structure in case hooks were added */
 	ast_bridge_features_cleanup(&bridge->features);
@@ -465,6 +528,7 @@
 
 	/* We start off by getting the thread servicing the current technology to stop and give control to us */
 	bridge->thread = AST_PTHREADT_STOP;
+	ast_cond_signal(&bridge->cond);
 	pthread_kill(thread, SIGURG);
 	
 	/* Once we let go of the lock the bridge thread should quit and control should be given to us */
@@ -525,6 +589,7 @@
 			return -1;
 		} else {
 			ast_debug(1, "Poked thread servicing bridge %p\n", bridge);
+			ast_cond_signal(&bridge->cond);
 			pthread_kill(bridge->thread, SIGURG);
 		}
 	}
@@ -548,17 +613,18 @@
 	}
 
 	/* Go into a loop waiting for frames from the channel, or the associated file descriptors to trip */
-	while (bridge_channel->state != AST_BRIDGE_CHANNEL_STATE_WAIT) {
+	while (bridge_channel->state == AST_BRIDGE_CHANNEL_STATE_WAIT) {
 		struct ast_channel *chan = NULL;
 		int outfd = -1, ms = -1;
 
-		/* Wait for data on any of the file descriptors */
-		ast_mutex_unlock(&bridge->lock);
-		if (!bridge_channel->suspended)
+		/* If the channel is not suspended wait on it (and the descriptors), if it is suspended wait on the condition */
+		if (!bridge_channel->suspended) {
+			ast_mutex_unlock(&bridge->lock);
 			chan = ast_waitfor_nandfds(&bridge_channel->chan, 1, fds, nfds, NULL, &outfd, &ms);
-		else
-			usleep(1);
-		ast_mutex_lock(&bridge->lock);
+			ast_mutex_lock(&bridge->lock);
+		} else {
+			ast_cond_wait(&bridge_channel->cond, &bridge->lock);
+		}
 
 		bridge_handle_trip(bridge, bridge_channel, chan, outfd);
 	}
@@ -596,6 +662,38 @@
 	return bridge_channel->state;
 }
 
+/*! \brief Internal function that executes a feature on a bridge channel */
+static void bridge_channel_feature(struct ast_bridge *bridge, struct ast_bridge_channel *bridge_channel)
+{
+	struct ast_bridge_features *features = (bridge_channel->features ? bridge_channel->features : &bridge->features);
+	ast_bridge_features_hook_callback callback = NULL;
+	void *hook_pvt = NULL;
+
+	if (bridge_channel->go_feature == 1) {
+		struct ast_bridge_features_hook *hook = NULL;
+		/* Look for the matching hook, we know it matched because we got this far */
+		AST_LIST_TRAVERSE(&features->hooks, hook, list) {
+			if (!strcmp(hook->dtmf, bridge_channel->dtmfq)) {
+				callback = hook->callback;
+				hook_pvt = hook->hook_pvt;
+				break;
+			}
+		}
+	} else if (bridge_channel->go_feature == 2) {
+	}
+
+	/* Wipe out the current DTMF queue, no longer needed and stop the indication to execute a feature... having it still be on would be bad */
+	bridge_channel->dtmfq[0] = '\0';
+	bridge_channel->go_feature = 0;
+
+	/* Now the logic is simple, release the bridge lock and actually execute the feature callback */
+	ast_mutex_unlock(&bridge->lock);
+	callback(bridge, bridge_channel);
+	ast_mutex_lock(&bridge->lock);
+
+	return;
+}
+
 /*! \brief Join a channel to a bridge and handle anything the bridge may want us to do */
 static enum ast_bridge_channel_state bridge_channel_join(struct ast_bridge *bridge, struct ast_bridge_channel *bridge_channel)
 {
@@ -605,7 +703,7 @@
 	bridge_channel->suspended = 0;
 
 	/* Notify the bridge thread that a new bridged channel is part of the bridge, this will cause it to rebuild the bridge array */
-	bridge->rebuild = 1;
+	ast_bridge_rebuild(bridge);
 
 	/* If this channel is part of a swap operation don't bother optimizing or starting a thread, it doesn't matter */
 	if (bridge_channel->state != AST_BRIDGE_CHANNEL_STATE_SWAP) {
@@ -621,6 +719,7 @@
 			} else {
 				ast_debug(1, "Poked thread servicing bridge %p\n", bridge);
 				/* Poke the bridge out of it's poll if there */
+				ast_cond_signal(&bridge->cond);
 				pthread_kill(bridge->thread, SIGURG);
 			}
 		}
@@ -654,6 +753,10 @@
 			ast_atomic_fetchadd_int(&old_bridge->num, -1);
 			ast_mutex_lock(&bridge->lock);
 			bridge_channel->state = AST_BRIDGE_CHANNEL_STATE_WAIT;
+		} else if (bridge_channel->state == AST_BRIDGE_CHANNEL_STATE_FEATURE) {
+			bridge_channel_feature(bridge, bridge_channel);
+			/* Just in case... tell the bridge thread to rebuild */
+			ast_bridge_rebuild(bridge);
 		}
 	}
 
@@ -736,7 +839,7 @@
 			ast_debug(1, "Removing bridge channel %p from bridge %p and setting it to a swap state\n", swap_channel, bridge);
 			AST_LIST_REMOVE(&bridge->channels, swap_channel, list);
 			/* Signal the bridge thread to rebuild and poke it if required */
-			bridge->rebuild = 1;
+			ast_bridge_rebuild(bridge);
 			if (bridge->thread != AST_PTHREADT_NULL)
 				pthread_kill(bridge->thread, SIGURG);
 			ast_debug(1, "Releasing bridge lock on %p to ensure swap operation from bridge thread\n", bridge);
@@ -859,7 +962,7 @@
                         ast_debug(1, "Removing bridge channel %p from bridge %p and setting it to a swap state\n", swap_channel, bridge);
                         AST_LIST_REMOVE(&bridge->channels, swap_channel, list);
                         /* Signal the bridge thread to rebuild and poke it if required */
-                        bridge->rebuild = 1;
+			ast_bridge_rebuild(bridge);
                         if (bridge->thread != AST_PTHREADT_NULL)
                                 pthread_kill(bridge->thread, SIGURG);
                         ast_debug(1, "Releasing bridge lock on %p to ensure swap operation from bridge thread\n", bridge);
@@ -917,7 +1020,7 @@
 	ast_bridge_change_state(bridge_channel, AST_BRIDGE_CHANNEL_STATE_DEPART);
 
 	/* Queue a rebuild on the bridge itself */
-	bridge->rebuild = 1;
+	ast_bridge_rebuild(bridge);
 
 	/* Poke it just in case... */
 	pthread_kill(bridge->thread, SIGURG);
@@ -949,6 +1052,18 @@
 	pthread_kill(bridge_channel->thread, SIGURG);
 
 	return ast_cond_signal(&bridge_channel->cond);
+}
+
+/*! \brief Request that the bridge rebuild itself
+ * \param bridge Bridge in question
+ * \ return Returns 0 on success, -1 on failure
+ */
+int ast_bridge_rebuild(struct ast_bridge *bridge)
+{
+	/* Right now this is simple... just set an integer */
+	bridge->rebuild = 1;
+
+	return ast_cond_signal(&bridge->cond);
 }
 
 /*! \brief Merge two bridges together
@@ -995,6 +1110,7 @@
 
 		ast_debug(1, "Telling bridge thread on %p to stop so we can perform merge\n", bridge1);
 		bridge1->thread = AST_PTHREADT_STOP;
+		ast_cond_signal(&bridge1->cond);
 		pthread_kill(thread, SIGURG);
 		ast_debug(1, "Giving up lock on bridge %p so thread stops\n", bridge1);
 		ast_mutex_unlock(&bridge1->lock);
@@ -1079,22 +1195,58 @@
 	/* Once done we add it onto the list. Now it will be picked up when DTMF is used */
 	AST_LIST_INSERT_TAIL(&features->hooks, hook, list);
 
+	features->usable = 1;
+
 	return 0;
 }
 
 /*! \brief Enable a built in feature on a bridge features structure
  * \param features Bridge features structure
  * \param feature Feature to enable
+ * \param dtmf Optionally the DTMF stream to trigger the feature, if not specified it will be the default
  * \return Returns 0 on success, -1 on failure
  */
-int ast_bridge_features_enable(struct ast_bridge_features *features, enum ast_bridge_builtin_feature feature)
-{
-	/* This is relatively easy, built in features are done using an array */
-	if (features->builtin_features[feature])
+int ast_bridge_features_enable(struct ast_bridge_features *features, enum ast_bridge_builtin_feature feature, const char *dtmf)
+{
+	ast_bridge_features_hook_callback callback = NULL;
+
+	/* Depending on each feature find the callback and either use the DTMF provided or use the default */
+	switch (feature) {
+	case AST_BRIDGE_BUILTIN_BLINDTRANSFER:
+		callback = NULL;
+		if (ast_strlen_zero(dtmf))
+			dtmf = "*1";
+		break;
+	case AST_BRIDGE_BUILTIN_ATTENDEDTRANSFER:
+		callback = NULL;
+		if (ast_strlen_zero(dtmf))
+			dtmf = "*2";
+		break;
+	case AST_BRIDGE_BUILTIN_HANGUP:
+		callback = NULL;
+		if (ast_strlen_zero(dtmf))
+			dtmf = "*3";
+		break;
+	default:
 		return -1;
-
-	features->builtin_features[feature] = 1;
-
+	}
+
+	/* The rest is basically pretty easy. We create another hook using the provided callback/DTMF, easy as pie */
+	return ast_bridge_features_hook(features, dtmf, callback, NULL);
+}
+
+/*! \brief Initialize bridge features structure
+ * \param features Bridge featues structure
+ * \return Returns 0 on success, -1 on failure
+ */
+int ast_bridge_features_init(struct ast_bridge_features *features)
+{
+	/* Zero out the structure */
+	memset(features, 0, sizeof(*features));
+
+	/* Initialize the hooks list, just in case */
+	AST_LIST_HEAD_INIT_NOLOCK(&features->hooks);
+	
 	return 0;
 }
 




More information about the asterisk-commits mailing list