[Freeswitch-branches] [commit] r3521 - freeswitch/branches/knhor/trunk/src/mod/applications/mod_conference

Freeswitch SVN knhor at freeswitch.org
Sun Dec 3 17:26:32 EST 2006


Author: knhor
Date: Sun Dec  3 17:26:32 2006
New Revision: 3521

Modified:
   freeswitch/branches/knhor/trunk/src/mod/applications/mod_conference/mod_conference.c

Log:
add lots of comments
rename the thread functions
add conference pin retries
don't make outbound calls enter pin's to join
let outbound calls join locked conferences
reduce the number of parameters to many of the conference_loop_fn_xxx functions


Modified: freeswitch/branches/knhor/trunk/src/mod/applications/mod_conference/mod_conference.c
==============================================================================
--- freeswitch/branches/knhor/trunk/src/mod/applications/mod_conference/mod_conference.c	(original)
+++ freeswitch/branches/knhor/trunk/src/mod/applications/mod_conference/mod_conference.c	Sun Dec  3 17:26:32 2006
@@ -51,6 +51,9 @@
 #define MIN(a,b) ((a)<(b)?(a):(b))
 #endif
 
+// this doesn't work correctly yet, don't bother trying!
+//#define OPTION_IVR_MENU_SUPPORT
+
 typedef enum {
 	FILE_STOP_CURRENT,
 	FILE_STOP_ALL
@@ -240,7 +243,7 @@
 static switch_status_t conference_add_member(conference_obj_t *conference, conference_member_t *member);
 static void conference_del_member(conference_obj_t *conference, conference_member_t *member);
 static void *SWITCH_THREAD_FUNC conference_thread_run(switch_thread_t *thread, void *obj);
-static void conference_loop(conference_member_t *member);
+static void conference_loop_output(conference_member_t *member);
 static uint32_t conference_stop_file(conference_obj_t *conference, file_stop_t stop);
 static switch_status_t conference_play_file(conference_obj_t *conference, char *file, uint32_t leadin);
 static switch_status_t conference_say(conference_obj_t *conference, char *text, uint32_t leadin);
@@ -256,10 +259,10 @@
 										  char *cid_num);
 static void conference_function(switch_core_session_t *session, char *data);
 static void launch_conference_thread(conference_obj_t *conference);
-static void *SWITCH_THREAD_FUNC input_thread_run(switch_thread_t *thread, void *obj);
+static void *SWITCH_THREAD_FUNC conference_loop_input(switch_thread_t *thread, void *obj);
 static switch_status_t conference_local_play_file(switch_core_session_t *session, char *path, uint32_t leadin, char *buf, switch_size_t len);
 static switch_status_t conference_member_play_file(conference_member_t *member, char *file, uint32_t leadin);
-static switch_status_t conference_member_say(conference_obj_t *conference, conference_member_t *member, char *text, uint32_t leadin);
+static switch_status_t conference_member_say(conference_member_t *member, char *text, uint32_t leadin);
 static uint32_t conference_member_stop_file(conference_member_t *member, file_stop_t stop);
 static conference_obj_t *conference_new(char *name, switch_xml_t profile, switch_memory_pool_t *pool);
 static switch_status_t chat_send(char *proto, char *from, char *to, char *subject, char *body, char *hint);
@@ -766,7 +769,9 @@
 		switch_core_hash_delete(globals.conference_hash, conference->name);
 		switch_mutex_unlock(globals.hash_mutex);
 
-		switch_ivr_digit_stream_parser_destroy(&conference->dtmf_parser);
+		// this probably should be done, but doing it causes "shutdown" to hang until you kill it
+		// XXX - i'll chase this down later
+//		switch_ivr_digit_stream_parser_destroy(&conference->dtmf_parser);
 
 		if (conference->pool) {
 			switch_memory_pool_t *pool = conference->pool;
@@ -795,7 +800,6 @@
 {
 	if (switch_test_flag(member, MFLAG_CAN_SPEAK)) {
 		conf_api_sub_mute(member, NULL, NULL);
-//		conf_api_sub_deaf(member, NULL, NULL);
 	} else {
 		conf_api_sub_unmute(member, NULL, NULL);
 	}
@@ -823,7 +827,7 @@
 	}
 	switch_mutex_unlock(member->flag_mutex);
 	snprintf(msg, sizeof(msg), "Energy level %d", member->energy_level);
-	conference_member_say(member->conference, member, msg, 0);
+	conference_member_say(member, msg, 0);
 }
 
 static void conference_loop_fn_energy_equ_conf(conference_member_t *member, void *data)
@@ -834,7 +838,7 @@
 	member->energy_level = member->conference->energy_level;
 	switch_mutex_unlock(member->flag_mutex);
 	snprintf(msg, sizeof(msg), "Energy level %d", member->energy_level);
-	conference_member_say(member->conference, member, msg, 0);
+	conference_member_say(member, msg, 0);
 }
 					
 static void conference_loop_fn_energy_dn(conference_member_t *member, void *data)
@@ -848,7 +852,7 @@
 	}
 	switch_mutex_unlock(member->flag_mutex);
 	snprintf(msg, sizeof(msg), "Energy level %d", member->energy_level);
-	conference_member_say(member->conference, member, msg, 0);
+	conference_member_say(member, msg, 0);
 }
 
 static void conference_loop_fn_volume_talk_up(conference_member_t *member, void *data)
@@ -860,7 +864,7 @@
 	switch_normalize_volume(member->volume_out_level);
 	switch_mutex_unlock(member->flag_mutex);
 	snprintf(msg, sizeof(msg), "Volume level %d", member->volume_out_level);
-	conference_member_say(member->conference, member, msg, 0);
+	conference_member_say(member, msg, 0);
 }
 
 static void conference_loop_fn_volume_talk_zero(conference_member_t *member, void *data)
@@ -871,7 +875,7 @@
 	member->volume_out_level = 0;
 	switch_mutex_unlock(member->flag_mutex);
 	snprintf(msg, sizeof(msg), "Volume level %d", member->volume_out_level);
-	conference_member_say(member->conference, member, msg, 0);
+	conference_member_say(member, msg, 0);
 }
 
 static void conference_loop_fn_volume_talk_dn(conference_member_t *member, void *data)
@@ -883,7 +887,7 @@
 	switch_normalize_volume(member->volume_out_level);
 	switch_mutex_unlock(member->flag_mutex);
 	snprintf(msg, sizeof(msg), "Volume level %d", member->volume_out_level);
-	conference_member_say(member->conference, member, msg, 0);
+	conference_member_say(member, msg, 0);
 }
 
 static void conference_loop_fn_volume_listen_up(conference_member_t *member, void *data)
@@ -895,7 +899,7 @@
 	switch_normalize_volume(member->volume_in_level);
 	switch_mutex_unlock(member->flag_mutex);
 	snprintf(msg, sizeof(msg), "Gain level %d", member->volume_in_level);
-	conference_member_say(member->conference, member, msg, 0);
+	conference_member_say(member, msg, 0);
 }
 
 static void conference_loop_fn_volume_listen_zero(conference_member_t *member, void *data)
@@ -906,7 +910,7 @@
 	member->volume_in_level = 0;
 	switch_mutex_unlock(member->flag_mutex);
 	snprintf(msg, sizeof(msg), "Gain level %d", member->volume_in_level);
-	conference_member_say(member->conference, member, msg, 0);
+	conference_member_say(member, msg, 0);
 }
 
 static void conference_loop_fn_volume_listen_dn(conference_member_t *member, void *data)
@@ -918,7 +922,7 @@
 	switch_normalize_volume(member->volume_in_level);
 	switch_mutex_unlock(member->flag_mutex);
 	snprintf(msg, sizeof(msg), "Gain level %d", member->volume_in_level);
-	conference_member_say(member->conference, member, msg, 0);
+	conference_member_say(member, msg, 0);
 }
 
 static void conference_loop_fn_hangup(conference_member_t *member, void *data)
@@ -926,6 +930,7 @@
 	switch_clear_flag_locked(member, MFLAG_RUNNING);
 }
 
+#ifdef OPTION_IVR_MENU_SUPPORT
 typedef struct caller_control_menu_ctx {
 	switch_ivr_menu_xml_ctx_t *xml_ctx;
 	switch_ivr_menu_t *menu_stack;
@@ -948,6 +953,7 @@
 		}
 	}
 }
+#endif
 
 static void conference_loop_fn_dial(conference_member_t *member, void *data)
 {
@@ -973,6 +979,152 @@
 	}
 }
 
+// marshall frames from the call leg to the conference thread for muxing to other call legs
+static void *SWITCH_THREAD_FUNC conference_loop_input(switch_thread_t *thread, void *obj)
+{
+	conference_member_t *member = obj;
+	switch_channel_t *channel;
+	switch_status_t status;
+	switch_frame_t *read_frame = NULL;
+	switch_codec_t *read_codec;
+	uint32_t hangover = 40,
+		hangunder = 15,
+		hangover_hits = 0,
+		hangunder_hits = 0,
+		energy_level = 0,
+		diff_level = 400;
+	uint8_t talking = 0;
+
+	assert(member != NULL);
+
+	channel = switch_core_session_get_channel(member->session);
+	assert(channel != NULL);
+
+	read_codec = switch_core_session_get_read_codec(member->session);
+	assert(read_codec != NULL);
+
+	/* As long as we have a valid read, feed that data into an input buffer where the conference thread will take it 
+	   and mux it with any audio from other channels. */
+
+	while(switch_test_flag(member, MFLAG_RUNNING) && switch_channel_ready(channel)) {
+		/* Read a frame. */
+		status = switch_core_session_read_frame(member->session, &read_frame, -1, 0);
+
+		/* end the loop, if appropriate */
+		if (!SWITCH_READ_ACCEPTABLE(status) || !switch_test_flag(member, MFLAG_RUNNING)) {
+			break;
+		}
+
+		if (switch_test_flag(read_frame, SFF_CNG)) {
+			continue;
+		}
+
+		energy_level = member->energy_level;
+
+		// if the member can speak, compute the audio energy level and
+		// generate events when the level crosses the threshold
+		if (switch_test_flag(member, MFLAG_CAN_SPEAK) && energy_level) {
+			uint32_t energy = 0, i = 0, samples = 0, j = 0, score = 0;
+			int16_t *data;
+
+			data = read_frame->data;
+			samples = read_frame->datalen / sizeof(*data);
+
+			for (i = 0; i < samples; i++) {
+				energy += abs(data[j]);
+				j += read_codec->implementation->number_of_channels;
+			}
+
+			score = energy / samples;
+
+			if (score > energy_level) {
+				uint32_t diff = score - energy_level;
+				if (hangover_hits) {
+					hangover_hits--;
+				}
+
+				if (diff >= diff_level || ++hangunder_hits >= hangunder) {
+					hangover_hits = hangunder_hits = 0;
+
+					if (!talking) {
+						switch_event_t *event;
+						talking = 1;		
+						if (switch_event_create_subclass(&event, SWITCH_EVENT_CUSTOM, CONF_EVENT_MAINT) == SWITCH_STATUS_SUCCESS) {
+							switch_channel_event_set_data(channel, event);
+							switch_event_add_header(event, SWITCH_STACK_BOTTOM, "Conference-Name", member->conference->name);
+							switch_event_add_header(event, SWITCH_STACK_BOTTOM, "Member-ID", "%u", member->id);
+							switch_event_add_header(event, SWITCH_STACK_BOTTOM, "Action", "start-talking");
+							switch_event_fire(&event);
+						}
+					}
+				} 
+			} else {
+				if (hangunder_hits) {
+					hangunder_hits--;
+				}
+				if (talking) {
+					switch_event_t *event;
+					if (++hangover_hits >= hangover) {
+						hangover_hits = hangunder_hits = 0;
+						talking = 0;
+
+						if (switch_event_create_subclass(&event, SWITCH_EVENT_CUSTOM, CONF_EVENT_MAINT) == SWITCH_STATUS_SUCCESS) {
+							switch_channel_event_set_data(channel, event);
+							switch_event_add_header(event, SWITCH_STACK_BOTTOM, "Conference-Name", member->conference->name);
+							switch_event_add_header(event, SWITCH_STACK_BOTTOM, "Member-ID", "%u", member->id);
+							switch_event_add_header(event, SWITCH_STACK_BOTTOM, "Action", "stop-talking");
+							switch_event_fire(&event);
+						}					
+					}
+				}
+			}
+		}
+
+		/* skip frames that are not actual media or when we are muted or silent */
+		if ((talking || energy_level == 0) && switch_test_flag(member, MFLAG_CAN_SPEAK)) {
+			if (member->read_resampler) {
+				int16_t *bptr = (int16_t *) read_frame->data;
+				int len = (int) read_frame->datalen;;
+
+				member->read_resampler->from_len = switch_short_to_float(bptr, member->read_resampler->from, (int) len / 2);
+				member->read_resampler->to_len = switch_resample_process(member->read_resampler, member->read_resampler->from,
+																		 member->read_resampler->from_len, member->read_resampler->to,
+																		 member->read_resampler->to_size, 0);
+				switch_float_to_short(member->read_resampler->to, read_frame->data, len);
+				len = member->read_resampler->to_len * 2;
+				read_frame->datalen = len;
+				read_frame->samples = len / 2;
+			}
+			/* Check for input volume adjustments */
+			if (member->volume_in_level) {
+				switch_change_sln_volume(read_frame->data, read_frame->datalen / 2, member->volume_in_level);
+			}
+
+			/* Write the audio into the input buffer */
+			switch_mutex_lock(member->audio_in_mutex);
+			switch_buffer_write(member->audio_buffer, read_frame->data, read_frame->datalen);
+			switch_mutex_unlock(member->audio_in_mutex);
+		}
+	}
+
+	switch_clear_flag_locked(member, MFLAG_ITHREAD);
+
+	return NULL;
+}
+
+// launch an input thread for the call leg
+static void launch_conference_loop_input(conference_member_t *member, switch_memory_pool_t *pool)
+{
+	switch_thread_t *thread;
+	switch_threadattr_t *thd_attr = NULL;
+
+	switch_threadattr_create(&thd_attr, pool);
+	switch_threadattr_detach_set(thd_attr, 1);
+	switch_threadattr_stacksize_set(thd_attr, SWITCH_THREAD_STACKSIZE);
+	switch_set_flag_locked(member, MFLAG_ITHREAD);
+	switch_thread_create(&thread, thd_attr, conference_loop_input, member, pool);
+}
+
 static struct caller_control_fn_table {
 	char *key;
 	char *digits;
@@ -991,27 +1143,17 @@
 	{"vol listen zero",	"5",	CALLER_CONTROL_VOL_LISTEN_ZERO,	conference_loop_fn_volume_listen_zero},
 	{"vol listen dn",	"4",	CALLER_CONTROL_VOL_LISTEN_DN,	conference_loop_fn_volume_listen_dn},
 	{"hangup",		"#",	CALLER_CONTROL_HANGUP,		conference_loop_fn_hangup},
+#ifdef OPTION_IVR_MENU_SUPPORT
 	{"menu",		NULL,	CALLER_CONTROL_MENU,		conference_loop_fn_menu},
+#endif
 	{"dial",		NULL,	CALLER_CONTROL_DIAL,		conference_loop_fn_dial},
 };
 #define CCFNTBL_QTY (sizeof(ccfntbl)/sizeof(ccfntbl[0]))
 
-/* Create a thread for the conference and launch it */
-static void launch_input_thread(conference_member_t *member, switch_memory_pool_t *pool)
+// marshall frames from the conference (or file or tts output) to the call leg
+// NB. this starts the input thread after some initial setup for the call leg
+static void conference_loop_output(conference_member_t *member)
 {
-	switch_thread_t *thread;
-	switch_threadattr_t *thd_attr = NULL;
-
-	switch_threadattr_create(&thd_attr, pool);
-	switch_threadattr_detach_set(thd_attr, 1);
-	switch_threadattr_stacksize_set(thd_attr, SWITCH_THREAD_STACKSIZE);
-	switch_set_flag_locked(member, MFLAG_ITHREAD);
-	switch_thread_create(&thread, thd_attr, input_thread_run, member, pool);
-}
-
-/* Sub-Routine called by a channel inside a conference */
-static void conference_loop(conference_member_t *member)
-{
 	switch_channel_t *channel;
 	switch_frame_t write_frame = {0};
 	uint8_t data[SWITCH_RECCOMMENDED_BUFFER_SIZE];
@@ -1045,9 +1187,11 @@
 		switch_channel_answer(channel);
 	}
 
-	/* Start a thread to read data and feed it into the buffer and use this thread to generate output */
-	launch_input_thread(member, switch_core_session_get_pool(member->session));
+	// Start the input thread
+	launch_conference_loop_input(member, switch_core_session_get_pool(member->session));
 
+	// Fair WARNING, If you expect the caller to hear anything or for digit handling to be proccessed,
+	// you better not block this thread loop for more than the duration of member->conference->timer_name!
 	while(switch_test_flag(member, MFLAG_RUNNING) && switch_test_flag(member, MFLAG_ITHREAD) && switch_channel_ready(channel)) {
 		char dtmf[128] = "";
 		uint8_t file_frame[CONF_BUFFER_SIZE] = {0};
@@ -1055,6 +1199,7 @@
 		switch_size_t file_sample_len = samples;
 		char *digit;
 		switch_event_t *event;
+		caller_control_action_t *caller_action = NULL;
 
 		if (switch_core_session_dequeue_event(member->session, &event) == SWITCH_STATUS_SUCCESS) {
 			char *from = switch_event_get_header(event, "from");
@@ -1088,32 +1233,44 @@
 			}
 		}
 
+		// if we have caller digits, feed them to the parser to find an action
 		if (switch_channel_has_dtmf(channel)) {
 			switch_channel_dequeue_dtmf(channel, dtmf, sizeof(dtmf));
 
 			if(member->conference->dtmf_parser != NULL) {
-				caller_control_action_t *action;
 
-				for (digit = dtmf; *digit; digit++) {
-					action = (caller_control_action_t *)switch_ivr_digit_stream_parser_feed(member->conference->dtmf_parser, *digit);
-//				switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "digit_stream_parser_feed result != NULL %u\n",action != NULL);
-					if (action != NULL && action->handler != NULL) {
-						int i,found=0;
+				for (digit = dtmf; *digit && caller_action == NULL; digit++) {
+					caller_action = (caller_control_action_t *)switch_ivr_digit_stream_parser_feed(member->conference->dtmf_parser, *digit);
+				}
+			}
+		// otherwise, clock the parser so that it can handle digit timeout detection
+		} else if(member->conference->dtmf_parser != NULL) {
+			caller_action = (caller_control_action_t *)switch_ivr_digit_stream_parser_feed(member->conference->dtmf_parser, '\0');
+		}
 
-						for(i=0; i<CCFNTBL_QTY; i++) {
-							if (action->handler == ccfntbl[i].handler) {
-								switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "excuting caller control '%s' param '%s'\n",ccfntbl[i].key,(ccfntbl[i].action != CALLER_CONTROL_MENU ? action->data : ""));
-								found = 1;
-							}
-						}
+		// if a caller action has been detected, handle it
+		if (caller_action != NULL) {
+			
+			if (caller_action->handler != NULL) {
+				int i,found;
 
-						action->handler(member,action->data);
+				switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "digit_stream_parser_feed result != NULL %u\n",caller_action != NULL);
+				for(i=0,found=0; i<CCFNTBL_QTY && !found; i++) {
+					if (caller_action->handler == ccfntbl[i].handler) {
+						switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "excuting caller control '%s' param '%s'\n",ccfntbl[i].key,(ccfntbl[i].action != CALLER_CONTROL_MENU ? caller_action->data : ""));
+						found = 1;
 					}
 				}
+
+				caller_action->handler(member,caller_action->data);
 			}
+			// set up for next pass
+			caller_action = NULL;
 		}
 
+		// handle file and TTS frames
 		if (member->fnode) {
+			// if we are done, clean it up
 			if (member->fnode->done) {
 				confernce_file_node_t *fnode;
 				switch_memory_pool_t *pool;
@@ -1135,9 +1292,10 @@
 				switch_core_destroy_memory_pool(&pool);
 
 			} else {
+				// skip this frame until leadin time has expired
 				if (member->fnode->leadin) {
 					member->fnode->leadin--;
-				} else {
+				} else {	// send the node frame instead of the conference frame to the call leg
 					if (member->fnode->type == NODE_TYPE_SPEECH) {
 						switch_speech_flag_t flags = SWITCH_SPEECH_FLAG_BLOCKING;
 						uint32_t rate = member->conference->rate;
@@ -1176,7 +1334,7 @@
 					switch_core_timer_next(&timer);
 				}
 			}
-		} else {
+		} else {	// send the conferecne frame to the call leg
 			switch_buffer_t *use_buffer = NULL;
 			uint32_t mux_used = (uint32_t)switch_buffer_inuse(member->mux_buffer);
 
@@ -1355,6 +1513,7 @@
 	return count;
 }
 
+// stop playing a file for the member of the conference
 static uint32_t conference_member_stop_file(conference_member_t *member, file_stop_t stop)
 {
 	confernce_file_node_t *nptr;
@@ -1379,7 +1538,7 @@
 	return count;
 }
 
-/* Play a file in the conference rooom */
+/* Play a file in the conference room */
 static switch_status_t conference_play_file(conference_obj_t *conference, char *file, uint32_t leadin)
 {
 	confernce_file_node_t *fnode, *nptr;
@@ -1445,14 +1604,14 @@
 	return SWITCH_STATUS_SUCCESS;
 }
 
-/* Play a file in the conference rooom to a member */
+/* Play a file in the conference room to a member */
 static switch_status_t conference_member_play_file(conference_member_t *member, char *file, uint32_t leadin)
 {
 	confernce_file_node_t *fnode, *nptr;
 	switch_memory_pool_t *pool;
 
 	if (*file != '/') {
-		return conference_member_say(member->conference, member, file, leadin);
+		return conference_member_say(member, file, leadin);
 	}
 
 	/* Setup a memory pool to use. */
@@ -1483,6 +1642,7 @@
 	fnode->pool = pool;
 
 	/* Queue the node */
+	switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "queueing file '%s' for play\n",file);
 	switch_mutex_lock(member->flag_mutex);
 	for (nptr = member->fnode; nptr && nptr->next; nptr = nptr->next);
 
@@ -1496,13 +1656,18 @@
 	return SWITCH_STATUS_SUCCESS;
 }
 
-/* Say some thing with TTS in the conference rooom */
-static switch_status_t conference_member_say(conference_obj_t *conference, conference_member_t *member, char *text, uint32_t leadin)
+/* Say some thing with TTS in the conference room */
+static switch_status_t conference_member_say(conference_member_t *member, char *text, uint32_t leadin)
 {
+	conference_obj_t *conference = (member != NULL ? member->conference : NULL);
 	confernce_file_node_t *fnode, *nptr;
 	switch_memory_pool_t *pool;
 	switch_speech_flag_t flags = SWITCH_SPEECH_FLAG_NONE;
 
+	if (conference == NULL) {
+		return SWITCH_STATUS_FALSE;
+	}
+
 	if (!(conference->tts_engine && conference->tts_voice)) {
 		return SWITCH_STATUS_SUCCESS;
 	}
@@ -1555,7 +1720,7 @@
 	return SWITCH_STATUS_SUCCESS;
 }
 
-/* Say some thing with TTS in the conference rooom */
+/* Say some thing with TTS in the conference room */
 static switch_status_t conference_say(conference_obj_t *conference, char *text, uint32_t leadin)
 {
 	confernce_file_node_t *fnode, *nptr;
@@ -1623,6 +1788,7 @@
 	return SWITCH_STATUS_SUCCESS;
 }
 
+// execute a callback for every member of the conference
 static void conference_member_itterator(conference_obj_t *conference, switch_stream_handle_t *stream, conf_api_member_cmd_t pfncallback, void *data)
 {
 	conference_member_t *member = NULL;
@@ -1696,7 +1862,7 @@
 			char msg[512];
 
 			snprintf(msg, sizeof(msg), "Muted");
-			conference_member_say(member->conference, member, msg, 0);
+			conference_member_say(member, msg, 0);
 		}
 		if(stream != NULL) {
 			stream->write_function(stream, "OK mute %u\n", member->id);
@@ -1734,7 +1900,7 @@
 			char msg[512];
 
 			snprintf(msg, sizeof(msg), "Un-Muted");
-			conference_member_say(member->conference, member, msg, 0);
+			conference_member_say(member, msg, 0);
 		}
 		if (switch_event_create_subclass(&event, SWITCH_EVENT_CUSTOM, CONF_EVENT_MAINT) == SWITCH_STATUS_SUCCESS) {
 			switch_channel_t *channel = switch_core_session_get_channel(member->session);
@@ -2070,7 +2236,7 @@
 		}
 
 		if (id != 0 && (member = conference_member_get(conference, id))) {
-			if (text && conference_member_say(conference, member, text, 0) == SWITCH_STATUS_SUCCESS) {
+			if (text && conference_member_say(member, text, 0) == SWITCH_STATUS_SUCCESS) {
 				switch_event_t *event;
 
 				stream->write_function(stream, "(saymember) OK\n");
@@ -2370,28 +2536,34 @@
 	return err;
 }
 
+enum {
+	CONF_API_SUB_ARGS_SPLIT,
+	CONF_API_SUB_MEMBER_TARGET,
+	CONF_API_SUB_ARGS_AS_ONE,
+};
+
 /* API Interface Function sub-commands */
 static api_command_t conf_api_sub_commands[] = {
-	{"list",	&conf_api_sub_list,		0, "<confname> list [delim <string>]"},
-	{"energy",	&conf_api_sub_energy,		1, "<confname> energy <member_id|all|last> [<newval>]"},
-	{"volume in",	&conf_api_sub_volume_in,	1, "<confname> volume_in <member_id|all|last> [<newval>]"},
-	{"volume out",	&conf_api_sub_volume_out,	1, "<confname> volume_out <member_id|all|last> [<newval>]"},
-	{"play",	&conf_api_sub_play,		0, "<confname> play <file_path> [<member_id>]"},
-	{"say",		&conf_api_sub_say,		2, "<confname> say <text>"},
-	{"saymember",	&conf_api_sub_saymember,	2, "<confname> saymember <member_id><text>"},
-	{"stop",	&conf_api_sub_stop,		1, "<confname> stop <[current|all|last]> [<member_id>]"},
-	{"kick",	&conf_api_sub_kick,		1, "<confname> kick <[member_id|all|last]>"},
-	{"mute",	&conf_api_sub_mute,		1, "<confname> mute <[member_id|all]|last>"},
-	{"unmute",	&conf_api_sub_unmute,		1, "<confname> unmute <[member_id|all]|last>"},
-	{"deaf",	&conf_api_sub_deaf,		1, "<confname> deaf <[member_id|all]|last>"},
-	{"undeaf",	&conf_api_sub_undeaf,		1, "<confname> undef <[member_id|all]|last>"},
-	{"relate",	&conf_api_sub_relate,		0, "<confname> relate <member_id> <other_member_id> [nospeak|nohear|clear]"},
-	{"lock",	&conf_api_sub_lock,		0, "<confname> lock"},
-	{"unlock",	&conf_api_sub_unlock,		0, "<confname> unlock"},
-	{"dial",	&conf_api_sub_dial,		0, "<confname> dial <endpoint_module_name>/<destination> <callerid number> <callerid name>"},
-	{"transfer",	&conf_api_sub_transfer,		0, "<confname> transfer <member_id> <conference_name>"},
-	{"record",	&conf_api_sub_record,		0, "<confname> record <filename>"},
-	{"norecord",	&conf_api_sub_norecord,		0, "<confname> norecord <[filename|all]>"},
+	{"list",	&conf_api_sub_list,		CONF_API_SUB_ARGS_SPLIT,	"<confname> list [delim <string>]"},
+	{"energy",	&conf_api_sub_energy,		CONF_API_SUB_MEMBER_TARGET,	"<confname> energy <member_id|all|last> [<newval>]"},
+	{"volume in",	&conf_api_sub_volume_in,	CONF_API_SUB_MEMBER_TARGET,	"<confname> volume_in <member_id|all|last> [<newval>]"},
+	{"volume out",	&conf_api_sub_volume_out,	CONF_API_SUB_MEMBER_TARGET,	"<confname> volume_out <member_id|all|last> [<newval>]"},
+	{"play",	&conf_api_sub_play,		CONF_API_SUB_ARGS_SPLIT,	"<confname> play <file_path> [<member_id>]"},
+	{"say",		&conf_api_sub_say,		CONF_API_SUB_ARGS_AS_ONE,	"<confname> say <text>"},
+	{"saymember",	&conf_api_sub_saymember,	CONF_API_SUB_ARGS_AS_ONE,	"<confname> saymember <member_id><text>"},
+	{"stop",	&conf_api_sub_stop,		CONF_API_SUB_MEMBER_TARGET,	"<confname> stop <[current|all|last]> [<member_id>]"},
+	{"kick",	&conf_api_sub_kick,		CONF_API_SUB_MEMBER_TARGET,	"<confname> kick <[member_id|all|last]>"},
+	{"mute",	&conf_api_sub_mute,		CONF_API_SUB_MEMBER_TARGET,	"<confname> mute <[member_id|all]|last>"},
+	{"unmute",	&conf_api_sub_unmute,		CONF_API_SUB_MEMBER_TARGET,	"<confname> unmute <[member_id|all]|last>"},
+	{"deaf",	&conf_api_sub_deaf,		CONF_API_SUB_MEMBER_TARGET,	"<confname> deaf <[member_id|all]|last>"},
+	{"undeaf",	&conf_api_sub_undeaf,		CONF_API_SUB_MEMBER_TARGET,	"<confname> undef <[member_id|all]|last>"},
+	{"relate",	&conf_api_sub_relate,		CONF_API_SUB_ARGS_SPLIT,	"<confname> relate <member_id> <other_member_id> [nospeak|nohear|clear]"},
+	{"lock",	&conf_api_sub_lock,		CONF_API_SUB_ARGS_SPLIT,	"<confname> lock"},
+	{"unlock",	&conf_api_sub_unlock,		CONF_API_SUB_ARGS_SPLIT,	"<confname> unlock"},
+	{"dial",	&conf_api_sub_dial,		CONF_API_SUB_ARGS_SPLIT,	"<confname> dial <endpoint_module_name>/<destination> <callerid number> <callerid name>"},
+	{"transfer",	&conf_api_sub_transfer,		CONF_API_SUB_ARGS_SPLIT,	"<confname> transfer <member_id> <conference_name>"},
+	{"record",	&conf_api_sub_record,		CONF_API_SUB_ARGS_SPLIT,	"<confname> record <filename>"},
+	{"norecord",	&conf_api_sub_norecord,		CONF_API_SUB_ARGS_SPLIT,	"<confname> norecord <[filename|all]>"},
 };
 
 #define CONFFUNCAPISIZE (sizeof(conf_api_sub_commands)/sizeof(conf_api_sub_commands[0]))
@@ -2437,7 +2609,9 @@
 					if (strcasecmp(argv[argn],conf_api_sub_commands[i].pname) == 0) {
 						found = 1;
 						switch(conf_api_sub_commands[i].fntype) {
-							case 0:	// commands that we've broken the command line into arguments for
+
+							// commands that we've broken the command line into arguments for
+							case CONF_API_SUB_ARGS_SPLIT:
 								{	conf_api_args_cmd_t pfn = (conf_api_args_cmd_t)conf_api_sub_commands[i].pfnapicmd;
 
 									if (pfn(conference, stream, argc, &argv[0]) != 0) {
@@ -2446,7 +2620,9 @@
 										}
 								}
 								break;
-							case 1: // member specific command that can be itteratted
+
+							// member specific command that can be itteratted
+							case CONF_API_SUB_MEMBER_TARGET:
 								{
 									uint32_t id = atoi(argv[argn+1]);
 									int all = ( id == 0 && strcasecmp(argv[argn+1], "all") == 0 );
@@ -2492,7 +2668,9 @@
 									}
 								}
 								break;
-							case 2:	// commands that deals with all text after command
+
+							// commands that deals with all text after command
+							case CONF_API_SUB_ARGS_AS_ONE:
 								{	conf_api_text_cmd_t pfn = (conf_api_text_cmd_t)conf_api_sub_commands[i].pfnapicmd;
 									char *pstr = lbuf+strlen(conf_api_sub_commands[i].pname)+1;
 
@@ -2511,6 +2689,7 @@
 						}
 					}
 				}
+
 				// no command found
 				if(!found) {
 					stream->write_function(stream, "Confernece command '%s' not found.\nTry 'help conference'\n", argv[argn]);
@@ -2746,10 +2925,12 @@
 
 	conf_name = mydata;
 
+	// is there a conference pin ?
 	if ((dpin = strchr(conf_name, '+'))) {
 		*dpin++ = '\0';
 	}
 
+	// is there profile specification ?
 	if ((profile_name = strchr(conf_name, '@'))) {
 		*profile_name++ = '\0';
 
@@ -2789,6 +2970,7 @@
 		/* Set the minimum number of members (once you go above it you cannot go below it) */
 		conference->min = 2;
 
+		// if the dialplan specified a pin, override the profile's value
 		if (dpin) {
 			conference->pin = switch_core_strdup(conference->pool, dpin);
 		}
@@ -2812,6 +2994,7 @@
 				goto done;
 			}
 
+			// if the dialplan specified a pin, override the profile's value
 			if (dpin) {
 				conference->pin = switch_core_strdup(conference->pool, dpin);
 			}
@@ -2826,38 +3009,54 @@
 			launch_conference_thread(conference);
 		}
 
-		// deal with conference pins
-		if (conference->pin) {
-			char term = '\0';
-			char pin[80] = "";
-			char *buf;
+		// if this is not an outbound call, deal with conference pins
+		if (!switch_channel_test_flag(channel, CF_OUTBOUND) && conference->pin) {
+			char pin_buf[80] = "";
+			int  pin_retries = 3;	// XXX - this should be configurable - i'm too lazy to do it right now...
+			int  pin_valid = 0;
+			switch_status_t status = SWITCH_STATUS_SUCCESS;
 
 			/* Answer the channel */
 			switch_channel_answer(channel);
 
-			if (conference->pin_sound) {
-				conference_local_play_file(session, conference->pin_sound, 20, pin, sizeof(pin));
-			} 
+			while(!pin_valid && pin_retries && status == SWITCH_STATUS_SUCCESS) {
 
-			if (strlen(pin) < strlen(conference->pin)) {
-				buf = pin + strlen(pin);
-				switch_ivr_collect_digits_count(session,
+				// be friendly
+				if (conference->pin_sound) {
+					conference_local_play_file(session, conference->pin_sound, 20, pin_buf, sizeof(pin_buf));
+				} 
+				// wait for them if neccessary
+				if (strlen(pin_buf) < strlen(conference->pin)) {
+					char *buf = pin_buf + strlen(pin_buf);
+					char term = '\0';
+
+					status = switch_ivr_collect_digits_count(session,
 												buf,
-												sizeof(pin) - (unsigned int)strlen(pin),
-												(unsigned int)strlen(conference->pin) - (unsigned int)strlen(pin),
+												sizeof(pin_buf) - (unsigned int)strlen(pin_buf),
+												(unsigned int)strlen(conference->pin) - (unsigned int)strlen(pin_buf),
 												"#", &term, 10000);
-			}
+				}
 
-			if (strcmp(pin, conference->pin)) {
-				if (conference->bad_pin_sound) {
-					conference_local_play_file(session, conference->bad_pin_sound, 20, NULL, 0);
+				pin_valid = (status == SWITCH_STATUS_SUCCESS && strcmp(pin_buf, conference->pin) == 0);
+				if (!pin_valid) {
+					// zero the collected pin
+					memset(pin_buf,0,sizeof(pin_buf));
+
+					// more friendliness
+					if (conference->bad_pin_sound) {
+						conference_local_play_file(session, conference->bad_pin_sound, 20, pin_buf, sizeof(pin_buf));
+					}
 				}
+				pin_retries --;
+			}
+
+			if (!pin_valid) {
 				goto done;
 			}
 		}
 
-		// don't allow more callers if the conference is locked
-		if (switch_test_flag(conference, CFLAG_LOCKED)) {
+		// don't allow more callers if the conference is locked, unless we invited them
+		if (switch_test_flag(conference, CFLAG_LOCKED) && !switch_channel_test_flag(channel, CF_OUTBOUND)) {
 			switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_NOTICE, "Conference %s is locked.\n", conf_name);
 			if (conference->locked_sound) {
 				/* Answer the channel */
@@ -2985,7 +3184,7 @@
 	switch_core_session_receive_message(session, &msg);
 
 	/* Run the confernece loop */
-	conference_loop(&member);
+	conference_loop_output(&member);
 
 	/* Tell the channel we are no longer going to be in a bridge */
 	msg.message_id = SWITCH_MESSAGE_INDICATE_UNBRIDGE;
@@ -3069,138 +3268,6 @@
 	switch_thread_create(&thread, thd_attr, conference_record_thread_run, rec, rec->pool);
 }
 
-static void *SWITCH_THREAD_FUNC input_thread_run(switch_thread_t *thread, void *obj)
-{
-	conference_member_t *member = obj;
-	switch_channel_t *channel;
-	switch_status_t status;
-	switch_frame_t *read_frame = NULL;
-	switch_codec_t *read_codec;
-	uint32_t hangover = 40,
-		hangunder = 15,
-		hangover_hits = 0,
-		hangunder_hits = 0,
-		energy_level = 0,
-		diff_level = 400;
-	uint8_t talking = 0;
-
-	assert(member != NULL);
-
-	channel = switch_core_session_get_channel(member->session);
-	assert(channel != NULL);
-
-	read_codec = switch_core_session_get_read_codec(member->session);
-	assert(read_codec != NULL);
-
-	/* As long as we have a valid read, feed that data into an input buffer where the conference thread will take it 
-	   and mux it with any audio from other channels. */
-
-	while(switch_test_flag(member, MFLAG_RUNNING) && switch_channel_ready(channel)) {
-		/* Read a frame. */
-		status = switch_core_session_read_frame(member->session, &read_frame, -1, 0);
-
-		/* end the loop, if appropriate */
-		if (!SWITCH_READ_ACCEPTABLE(status) || !switch_test_flag(member, MFLAG_RUNNING)) {
-			break;
-		}
-
-		if (switch_test_flag(read_frame, SFF_CNG)) {
-			continue;
-		}
-
-		energy_level = member->energy_level;
-
-		// if the member can speak, compute the audio energy level and
-		// generate events when the level crosses the threshold
-		if (switch_test_flag(member, MFLAG_CAN_SPEAK) && energy_level) {
-			uint32_t energy = 0, i = 0, samples = 0, j = 0, score = 0;
-			int16_t *data;
-
-			data = read_frame->data;
-			samples = read_frame->datalen / sizeof(*data);
-
-			for (i = 0; i < samples; i++) {
-				energy += abs(data[j]);
-				j += read_codec->implementation->number_of_channels;
-			}
-
-			score = energy / samples;
-
-			if (score > energy_level) {
-				uint32_t diff = score - energy_level;
-				if (hangover_hits) {
-					hangover_hits--;
-				}
-
-				if (diff >= diff_level || ++hangunder_hits >= hangunder) {
-					hangover_hits = hangunder_hits = 0;
-
-					if (!talking) {
-						switch_event_t *event;
-						talking = 1;		
-						if (switch_event_create_subclass(&event, SWITCH_EVENT_CUSTOM, CONF_EVENT_MAINT) == SWITCH_STATUS_SUCCESS) {
-							switch_channel_event_set_data(channel, event);
-							switch_event_add_header(event, SWITCH_STACK_BOTTOM, "Conference-Name", member->conference->name);
-							switch_event_add_header(event, SWITCH_STACK_BOTTOM, "Member-ID", "%u", member->id);
-							switch_event_add_header(event, SWITCH_STACK_BOTTOM, "Action", "start-talking");
-							switch_event_fire(&event);
-						}
-					}
-				} 
-			} else {
-				if (hangunder_hits) {
-					hangunder_hits--;
-				}
-				if (talking) {
-					switch_event_t *event;
-					if (++hangover_hits >= hangover) {
-						hangover_hits = hangunder_hits = 0;
-						talking = 0;
-
-						if (switch_event_create_subclass(&event, SWITCH_EVENT_CUSTOM, CONF_EVENT_MAINT) == SWITCH_STATUS_SUCCESS) {
-							switch_channel_event_set_data(channel, event);
-							switch_event_add_header(event, SWITCH_STACK_BOTTOM, "Conference-Name", member->conference->name);
-							switch_event_add_header(event, SWITCH_STACK_BOTTOM, "Member-ID", "%u", member->id);
-							switch_event_add_header(event, SWITCH_STACK_BOTTOM, "Action", "stop-talking");
-							switch_event_fire(&event);
-						}					
-					}
-				}
-			}
-		}
-
-		/* skip frames that are not actual media or when we are muted or silent */
-		if ((talking || energy_level == 0) && switch_test_flag(member, MFLAG_CAN_SPEAK)) {
-			if (member->read_resampler) {
-				int16_t *bptr = (int16_t *) read_frame->data;
-				int len = (int) read_frame->datalen;;
-
-				member->read_resampler->from_len = switch_short_to_float(bptr, member->read_resampler->from, (int) len / 2);
-				member->read_resampler->to_len = switch_resample_process(member->read_resampler, member->read_resampler->from,
-																		 member->read_resampler->from_len, member->read_resampler->to,
-																		 member->read_resampler->to_size, 0);
-				switch_float_to_short(member->read_resampler->to, read_frame->data, len);
-				len = member->read_resampler->to_len * 2;
-				read_frame->datalen = len;
-				read_frame->samples = len / 2;
-			}
-			/* Check for input volume adjustments */
-			if (member->volume_in_level) {
-				switch_change_sln_volume(read_frame->data, read_frame->datalen / 2, member->volume_in_level);
-			}
-
-			/* Write the audio into the input buffer */
-			switch_mutex_lock(member->audio_in_mutex);
-			switch_buffer_write(member->audio_buffer, read_frame->data, read_frame->datalen);
-			switch_mutex_unlock(member->audio_in_mutex);
-		}
-	}
-
-	switch_clear_flag_locked(member, MFLAG_ITHREAD);
-
-	return NULL;
-}
-
 static const switch_application_interface_t conference_application_interface = {
 	/*.interface_name */ global_app_name,
 	/*.application_function */ conference_function,
@@ -3306,6 +3373,7 @@
 	/*.chat_interface */ &conference_chat_interface
 };
 
+#ifdef OPTION_IVR_MENU_SUPPORT
 static switch_ivr_action_t conference_caller_control_menu_handler(switch_ivr_menu_t *menu, char *param, char *buf, size_t buflen, void *obj)
 {
 	switch_ivr_action_t action = SWITCH_IVR_ACTION_NOOP;
@@ -3353,6 +3421,27 @@
 	return action;
 }
 
+/*
+static switch_status_t ivr_menu_callback_play_file(char *file, uint32_t leadin, void *member)
+{
+	switch_status_t status;
+
+	switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "ivr_menu_callback_play_file\n");
+	status = conference_member_play_file((conference_member_t *)member, file, leadin);
+	if (status != SWITCH_STATUS_SUCCESS) {
+		switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "ivr_menu_callback_play_file ERROR\n");
+	}
+
+	return status;
+}
+
+static switch_status_t ivr_menu_callback_say(char *file, uint32_t leadin, void *member)
+{
+	switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "ivr_menu_callback_say\n");
+	return conference_member_say((conference_member_t *)member, file, leadin);
+}
+*/
+
 static switch_status_t conference_caller_control_menu_build(caller_control_menu_ctx_t **ctx, conference_obj_t *conference, switch_xml_t profile, char *menu_name)
 {
 	switch_status_t status = SWITCH_STATUS_FALSE;
@@ -3382,6 +3471,16 @@
 					status = switch_ivr_menu_stack_xml_build((*ctx)->xml_ctx, &(*ctx)->menu_stack, xml_menus, xml_menu, conference->timer_name);
 					if (status != SWITCH_STATUS_SUCCESS)
 						switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "unable to build xml menu stack\n");
+
+/*
+					// instruct ivr menu to use our file play and tts routines
+					status = switch_ivr_menu_set_fileplay_callback((*ctx)->menu_stack, &ivr_menu_callback_play_file);
+					if (status != SWITCH_STATUS_SUCCESS)
+						switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "unable to set file play calback\n");
+					status = switch_ivr_menu_set_tts_callback((*ctx)->menu_stack, &ivr_menu_callback_say);
+					if (status != SWITCH_STATUS_SUCCESS)
+						switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "unable to set tts calback\n");
+*/
 				} else {
 					switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "unable to init xml menu context\n");
 				}
@@ -3400,14 +3499,107 @@
 
 	return status;
 }
+#endif
 
+static switch_status_t conference_new_install_caller_controls_default(conference_obj_t *conference)
+{
+	switch_status_t status = SWITCH_STATUS_FALSE;
+
+	if(conference != NULL) {
+		int i;
+		caller_control_action_t *action;
+
+		for(i=0,status=SWITCH_STATUS_SUCCESS; status == SWITCH_STATUS_SUCCESS && i<CCFNTBL_QTY; i++) {
+			if (!switch_strlen_zero(ccfntbl[i].digits)) {
+				switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Installing default caller control action '%s' bound to '%s'.\n",
+											ccfntbl[i].key,
+											ccfntbl[i].digits);
+				action = (caller_control_action_t *)switch_core_alloc(conference->pool,sizeof(caller_control_action_t));
+				if (action != NULL) {
+					action->handler = ccfntbl[i].handler;
+					action->data = NULL;
+					status = switch_ivr_digit_stream_parser_set_event(conference->dtmf_parser,ccfntbl[i].digits,action);
+				} else {
+					switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "unable to alloc memory for caller control binding '%s' to '%s'\n",ccfntbl[i].key,ccfntbl[i].digits);
+					status = SWITCH_STATUS_MEMERR;
+				}
+			}
+		}
+	}
+
+	return status;
+}
+
+static switch_status_t conference_new_install_caller_controls_custom(conference_obj_t *conference, switch_xml_t profile, switch_xml_t xml_controls)
+{
+	switch_status_t status = SWITCH_STATUS_FALSE;
+
+	if (conference != NULL && profile != NULL && xml_controls != NULL) {
+		switch_xml_t xml_kvp;
+
+		// parse the controls tree for caller control digit strings
+		for (xml_kvp = switch_xml_child(xml_controls, "control"); xml_kvp; xml_kvp = xml_kvp->next) {
+			char *key = (char *) switch_xml_attr(xml_kvp, "action");
+			char *val = (char *) switch_xml_attr(xml_kvp, "digits");
+			char *data = (char *)switch_xml_attr_soft(xml_kvp, "data");
+
+			if(!switch_strlen_zero(key) && !switch_strlen_zero(val)) {
+				int i;
+
+				// scan through all of the valid actions, and if found,
+				// set the new caller control action digit string, then
+				// stop scanning the table, and go to the next xml kvp.
+				for(i=0,status=SWITCH_STATUS_NOOP; i<CCFNTBL_QTY && status == SWITCH_STATUS_NOOP; i++) {
+
+					if(strcasecmp(ccfntbl[i].key,key) == 0) {
+						status = SWITCH_STATUS_SUCCESS;
+#ifdef OPTION_IVR_MENU_SUPPORT
+						if (ccfntbl[i].action == CALLER_CONTROL_MENU) {
+							caller_control_menu_ctx_t *menu_ctx = NULL;
+
+							status = conference_caller_control_menu_build(&menu_ctx,conference,profile,data);
+							data = (char *)menu_ctx;
+
+							if (status != SWITCH_STATUS_SUCCESS) {
+								switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "unable to build menu '%s' bound to '%s'\n",data,val);
+							}
+						}
+#endif
+
+						if (status == SWITCH_STATUS_SUCCESS) {
+							caller_control_action_t *action;
+
+							switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Installing caller control action '%s' bound to '%s'.\n",key,val);
+							action = (caller_control_action_t *)switch_core_alloc(conference->pool,sizeof(caller_control_action_t));
+							if (action != NULL) {
+								action->handler = ccfntbl[i].handler;
+								action->data = (void *)data;
+								status = switch_ivr_digit_stream_parser_set_event(conference->dtmf_parser,val,action);
+							} else {
+								switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "unable to alloc memory for caller control binding '%s' to '%s'\n",ccfntbl[i].key,ccfntbl[i].digits);
+								status = SWITCH_STATUS_MEMERR;
+							}
+						}
+					}
+				}
+				if(status == SWITCH_STATUS_NOOP) {
+					switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Invalid caller control action name '%s'.\n",key);
+				}
+			} else {
+				switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Invalid caller control config entry pair action='%s' digits='%s'\n",key,val);
+			}
+		}
+	}
+
+	return status;
+}
+
 /* create a new conferene with a specific profile */
 static conference_obj_t *conference_new(char *name, switch_xml_t profile, switch_memory_pool_t *pool)
 {
 	conference_obj_t *conference;
 	switch_xml_t xml_kvp;
 	switch_xml_t xml_controls;
-	switch_xml_t xml_menus;
 	char *controls_set = NULL;
 	char *rate_name = NULL;
 	char *interval_name = NULL;
@@ -3620,97 +3812,30 @@
 	conference->rate = rate;
 	conference->interval = interval;
 
+	// caller control configuration chores
 	xml_controls = switch_xml_child(profile, "controls");
 	controls_set = (xml_controls != NULL ? (char *) switch_xml_attr_soft(xml_controls, "set") : NULL);
 
+	// if no controls specified in the config, assume all should be specified
+	if (xml_controls == NULL) {
+		status = conference_new_install_caller_controls_default(conference);
+	} else if (xml_controls != NULL && controls_set != NULL && strcasecmp(controls_set,"none") != 0) {
 	// try to build caller control if the set has been specified and != "none"
-	if (xml_controls != NULL && controls_set != NULL && strcasecmp(controls_set,"none") != 0) {
 		if(switch_ivr_digit_stream_parser_new(conference->pool,&conference->dtmf_parser) == SWITCH_STATUS_SUCCESS) {
 			
 			// map in the default control handler strings if specified
 			if (strcasecmp(controls_set,"default") == 0) {
-				int i;
-				caller_control_action_t *action;
-
-				for(i=0,status=SWITCH_STATUS_SUCCESS; status == SWITCH_STATUS_SUCCESS && i<CCFNTBL_QTY; i++) {
-					if (!switch_strlen_zero(ccfntbl[i].digits)) {
-						switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Installing default caller control action '%s' bound to '%s'.\n",
-													ccfntbl[i].key,
-													ccfntbl[i].digits);
-						action = (caller_control_action_t *)switch_core_alloc(conference->pool,sizeof(caller_control_action_t));
-						if (action != NULL) {
-							action->handler = ccfntbl[i].handler;
-							action->data = NULL;
-							status = switch_ivr_digit_stream_parser_set_event(conference->dtmf_parser,ccfntbl[i].digits,action);
-						} else {
-							switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "unable to alloc memory for caller control binding '%s' to '%s'\n",ccfntbl[i].key,ccfntbl[i].digits);
-							status = SWITCH_STATUS_MEMERR;
-						}
-					}
-				}
+				status = conference_new_install_caller_controls_default(conference);
 			} else if (strcasecmp(controls_set,"custom") == 0) {
-				// parse the controls tree for caller control digit strings
-				for (xml_kvp = switch_xml_child(xml_controls, "control"); xml_kvp; xml_kvp = xml_kvp->next) {
-					char *key = (char *) switch_xml_attr(xml_kvp, "action");
-					char *val = (char *) switch_xml_attr(xml_kvp, "digits");
-					char *data = (char *)switch_xml_attr_soft(xml_kvp, "data");
-
-					if(!switch_strlen_zero(key) && !switch_strlen_zero(val)) {
-						int i;
-
-						// scan through all of the valid actions, and if found,
-						// set the new caller control action digit string, then
-						// stop scanning the table, and go to the next xml kvp.
-						for(i=0,status=SWITCH_STATUS_NOOP; i<CCFNTBL_QTY && status == SWITCH_STATUS_NOOP; i++) {
-
-							if(strcasecmp(ccfntbl[i].key,key) == 0) {
-								status = SWITCH_STATUS_SUCCESS;
-
-								if (ccfntbl[i].action == CALLER_CONTROL_MENU) {
-									caller_control_menu_ctx_t *menu_ctx = NULL;
-
-									status = conference_caller_control_menu_build(&menu_ctx,conference,profile,data);
-									data = (char *)menu_ctx;
-
-									if (status != SWITCH_STATUS_SUCCESS) {
-										switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "unable to build menu '%s' bound to '%s'\n",data,val);
-									}
-								}
-
-								if (status == SWITCH_STATUS_SUCCESS) {
-									caller_control_action_t *action;
-
-									switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Installing caller control action '%s' bound to '%s'.\n",key,val);
-									action = (caller_control_action_t *)switch_core_alloc(conference->pool,sizeof(caller_control_action_t));
-									if (action != NULL) {
-										action->handler = ccfntbl[i].handler;
-										action->data = (void *)data;
-										status = switch_ivr_digit_stream_parser_set_event(conference->dtmf_parser,val,action);
-									} else {
-										switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "unable to alloc memory for caller control binding '%s' to '%s'\n",ccfntbl[i].key,ccfntbl[i].digits);
-										status = SWITCH_STATUS_MEMERR;
-									}
-								}
-							}
-						}
-						if(status == SWITCH_STATUS_NOOP) {
-							switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Invalid caller control action name '%s'.\n",key);
-						}
-					} else {
-						switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Invalid caller control config entry pair action='%s' digits='%s'\n",key,val);
-					}
-				}
+				status = conference_new_install_caller_controls_custom(conference,profile,xml_controls);
+			} else {
+				switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Unknown caller control set.\n");
 			}
 		} else {
 			switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Unable to allocate caller control digit parser.\n");
 		}
 	} else {
 		switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "no caller controls intalled.\n");
-	}
-
-	xml_menus = switch_xml_child(profile, "menus");
-	if (xml_menus != NULL) {
-//		switch_xml_t xml_menu;
 	}
 
 	/* Activate the conference mutex for exclusivity */



More information about the Freeswitch-branches mailing list