Index: apps/app_dial.c
===================================================================
--- apps/app_dial.c	(revision 113648)
+++ apps/app_dial.c	(working copy)
@@ -146,6 +146,17 @@
 "           * LIMIT_CONNECT_FILE       File to play when call begins.\n"
 "           * LIMIT_WARNING_FILE       File to play as warning if 'y' is defined.\n"
 "                                      The default is to say the time remaining.\n"
+"           * LIMIT_RTCC_INTERVAL      Recalculate the timelimit every x ms.\n"
+"           * LIMIT_RTCC_APP           Application to use: AppName(AppArgs).\n"
+"                          Use as argument <bridge_combo> to pass to your app structure\n"
+"                          containing the two channels and the bridge configuration.\n"
+"                          The application should set CALL_LIMIT variable with the\n"
+"                          new total duration of the call limit (in ms) where 0 = no change\n"
+"                          or to change the limit and config->nexteventts accordingly.\n"
+"                          If no application is given, but interval is present,\n"
+"                          a Manager Event will be generated.\n"
+"           * LIMIT_RTCC_DELAY         Delay (in ms) before checking the CALL_LIMIT_DIFF value\n"
+"                          after the application is execututed or the Manager Event is sent.\n"
 "    m([class]) - Provide hold music to the calling party until a requested\n"
 "           channel answers. A specific MusicOnHold class can be\n"
 "           specified.\n"
@@ -789,6 +800,164 @@
 	return 0;
 }
 
+static pthread_t rtcc_thread = AST_PTHREADT_NULL;	/* The thread the real-time call control scheduler will be run on. */
+AST_MUTEX_DEFINE_STATIC(rtcc_threadcontrol_lock);
+
+static struct sched_context* rtcc_sched_con;		/* The real-time call control scheduling context. */
+
+static void* ast_rtcc_monitor(void * data)
+{
+	ast_mutex_t rtcc_mutex;
+	ast_cond_t rtcc_cond;
+	struct timespec timeout = {0,0};
+	struct timeval next_sched;
+
+	ast_mutex_init(&rtcc_mutex);
+	ast_cond_init(&rtcc_cond, NULL);
+
+	ast_log(LOG_DEBUG, "Call control monitor thread starting.\n");
+
+	for(;;)
+	{
+		ast_sched_runq(rtcc_sched_con);
+
+		int nexteventms = ast_sched_wait(rtcc_sched_con);
+		if(nexteventms == -1)  {
+			break;
+		}
+
+		next_sched = ast_tvadd(ast_tvnow(), ast_samp2tv(nexteventms, 1000));
+		timeout.tv_sec = next_sched.tv_sec;
+		timeout.tv_nsec = next_sched.tv_usec * 1000;
+
+		ast_mutex_lock(&rtcc_mutex);
+		ast_cond_timedwait(&rtcc_cond, &rtcc_mutex, &timeout);
+		ast_mutex_unlock(&rtcc_mutex);
+	}
+
+	ast_log(LOG_DEBUG, "Call control monitor thread stopping.\n");
+
+	ast_mutex_destroy(&rtcc_mutex);
+	ast_cond_destroy(&rtcc_cond);
+
+	ast_mutex_lock(&rtcc_threadcontrol_lock);
+	rtcc_thread = AST_PTHREADT_NULL;
+	ast_mutex_unlock(&rtcc_threadcontrol_lock);
+
+	return 0;
+}
+
+/*! \brief Finds and regulary starts the requested application for RealTime Call Control of a bridged call */
+static int ast_rtcc_callback(const void *data)
+{
+	struct ast_rtcc_bridge_combo *rtcc_data = (struct ast_rtcc_bridge_combo *)data;
+	struct ast_app *app;
+	struct timeval last_run = ast_tvnow();
+	long delay_ms = 0;
+	char *s, *app_name = "", *app_data = "";
+	int res = 0;
+	int rtcc_sched_id = rtcc_data->rtcc_sched_id;
+
+	if (!rtcc_data->config->rtcc_check_interval || (rtcc_data->config->timelimit && ((rtcc_data->config->timelimit - rtcc_data->config->play_warning) < ast_tvdiff_ms(ast_tvnow(), rtcc_data->config->start_time)))) {
+		/* No more schedules after the warning or if disabled */
+		return 0;
+	}
+
+	ast_log(LOG_DEBUG, "Call control callback started.\n");
+
+	if (!ast_strlen_zero(rtcc_data->config->rtcc_app)) {
+		s = ast_strdupa(rtcc_data->config->rtcc_app);
+		app_name = strsep(&s,"(");
+		memset(&app_data, 0, sizeof(app_data));
+		if (!ast_strlen_zero(s)) {
+			app_data = ast_strip(s);
+		}
+
+		if (!ast_strlen_zero(app_data) && (s = strrchr(app_data, ')'))) {
+			app_data[strlen(app_data)-strlen(s)] = '\0';
+		}
+	}
+
+	{
+		const char *r = pbx_builtin_getvar_helper(rtcc_data->ch0,"LIMIT_RTCC_DELAY");
+		if (!ast_strlen_zero(r)) {
+			delay_ms = atol(r);
+		}
+	}
+
+	if (!ast_strlen_zero(app_name) && app_data) {
+		app = pbx_findapp(app_name);
+		if (!app) {
+			ast_log(LOG_WARNING, "Could not find application (%s). Timelimit not checked for call (%s)\n", rtcc_data->config->rtcc_app, rtcc_data->ch0->uniqueid);
+		}
+		else if (strcmp(app_data, "<bridge_combo>")) {
+			char with_data[2048];
+			memset(with_data, 0, 2048);
+			pbx_substitute_variables_helper(rtcc_data->ch0, app_data, with_data, sizeof(with_data) - 1);
+			if (option_verbose > 10) {
+				ast_verbose( VERBOSE_PREFIX_4 "Substitute %s TO %s\n", app_data, with_data);
+			}
+			res = pbx_exec(rtcc_data->ch0, app, with_data);
+		}
+		else {
+			res = pbx_exec(rtcc_data->ch0, app, rtcc_data);
+		}
+
+		if (res) {
+			ast_log(LOG_ERROR, "Error executing application (%s). Timelimit not checked for call (%s)\n", rtcc_data->config->rtcc_app, rtcc_data->ch0->uniqueid);
+			res = 0;
+		}
+	}
+	else {
+		manager_event(EVENT_FLAG_CALL, "RTCC_GetTimeLimitChange", 
+		      "Channel1: %s\r\n"
+		      "Channel2: %s\r\n"
+		      "Uniqueid1: %s\r\n"
+		      "Uniqueid2: %s\r\n"
+		      "CallerID1: %s\r\n"
+		      "CallerID2: %s\r\n",
+		      rtcc_data->ch0->name, rtcc_data->ch1->name, rtcc_data->ch0->uniqueid, rtcc_data->ch1->uniqueid, rtcc_data->ch0->cid.cid_num, rtcc_data->ch1->cid.cid_num);
+	}
+
+	if (delay_ms > 0) {
+		usleep(delay_ms*1000);
+	}
+
+	/* Make sure the call was not hungup while work and wait */
+	ast_mutex_lock(&rtcc_threadcontrol_lock);
+	if (ast_sched_when(rtcc_sched_con, rtcc_sched_id) != -1) {
+		res = 0;
+	}
+	else {
+		long res_ms;
+		const char *r = pbx_builtin_getvar_helper(rtcc_data->ch0,"CALL_LIMIT");
+		if (r && (res_ms = atoi(r)) && (res_ms -= rtcc_data->config->timelimit)) {
+			res = res_ms/1000;
+			if (rtcc_data->ch0->whentohangup) {
+				rtcc_data->ch0->whentohangup += res_ms;
+			}
+			rtcc_data->config->timelimit += res_ms;
+			rtcc_data->config->nexteventts.tv_sec += res;
+			if (option_verbose > 10) {
+				ast_verbose( VERBOSE_PREFIX_4 "RTCC executed for call %s. New limit %ld\n", rtcc_data->ch0->uniqueid, rtcc_data->config->timelimit);
+			}
+		}
+		else if (option_verbose > 10) {
+			ast_verbose( VERBOSE_PREFIX_4 "RTCC executed for call %s. No changes. Current limit %ld\n", rtcc_data->ch0->uniqueid, rtcc_data->config->timelimit);
+		}
+		res = ast_tvdiff_ms(ast_tvadd(last_run, ast_samp2tv(rtcc_data->config->rtcc_check_interval, 1000)),ast_tvnow());
+	}
+
+	if ((res && !rtcc_data->config->rtcc_check_interval) || (rtcc_data->config->timelimit && ((rtcc_data->config->timelimit - rtcc_data->config->play_warning) < ast_tvdiff_ms(ast_tvnow(), rtcc_data->config->start_time)))) {
+		/* No more schedules after the warning or if disabled */
+		res = 0;
+	}
+
+	ast_mutex_unlock(&rtcc_threadcontrol_lock);
+
+	return res;
+}
+
 static int dial_exec_full(struct ast_channel *chan, void *data, struct ast_flags *peerflags, int *continue_exec)
 {
 	int res = -1;
@@ -806,6 +975,12 @@
 	int privdb_val = 0;
 	unsigned int calldurationlimit = 0;
 	long timelimit = 0;
+	long rtcc_check_interval = 0;
+	long rtcc_delay = 0;
+	const char *rtcc_app=NULL;
+	struct ast_rtcc_bridge_combo *rtcc_data = NULL;
+	pthread_t rtcc_thread = AST_PTHREADT_NULL;
+	int rtcc_sched_id = 0;
 	long play_warning = 0;
 	long warning_freq = 0;
 	const char *warning_sound = NULL;
@@ -934,10 +1109,21 @@
 		var = pbx_builtin_getvar_helper(chan,"LIMIT_CONNECT_FILE");
 		start_sound = S_OR(var, NULL);	/* XXX not much of a point in doing this! */
 
+		var = pbx_builtin_getvar_helper(chan,"LIMIT_RTCC_INTERVAL");
+		if (!ast_strlen_zero(var))
+			rtcc_check_interval = atol(var);
+
+		var = pbx_builtin_getvar_helper(chan,"LIMIT_RTCC_DELAY");
+		if (!ast_strlen_zero(var))
+			rtcc_delay = atol(var);
+
+		var = pbx_builtin_getvar_helper(chan,"LIMIT_RTCC_APP");
+		rtcc_app = S_OR(var, NULL);
+
 		/* undo effect of S(x) in case they are both used */
 		calldurationlimit = 0;
 		/* more efficient to do it like S(x) does since no advanced opts */
-		if (!play_warning && !start_sound && !end_sound && timelimit) {
+		if (!rtcc_check_interval && !play_warning && !start_sound && !end_sound && timelimit) {
 			calldurationlimit = timelimit / 1000;
 			if (option_verbose > 2)
 				ast_verbose(VERBOSE_PREFIX_3 "Setting call duration limit to %d seconds.\n", calldurationlimit);
@@ -945,6 +1131,11 @@
 		} else if (option_verbose > 2) {
 			ast_verbose(VERBOSE_PREFIX_3 "Limit Data for this call:\n");
 			ast_verbose(VERBOSE_PREFIX_4 "timelimit      = %ld\n", timelimit);
+			if (rtcc_check_interval) {
+				ast_verbose(VERBOSE_PREFIX_4 "RTCC check each   = %ld ms\n", rtcc_check_interval);
+				ast_verbose(VERBOSE_PREFIX_4 "RTCC app    = %s\n", rtcc_app ? rtcc_app : "Manager Event");
+				ast_verbose(VERBOSE_PREFIX_4 "RTCC delay  = %ld\n", rtcc_delay);
+			}
 			ast_verbose(VERBOSE_PREFIX_4 "play_warning   = %ld\n", play_warning);
 			ast_verbose(VERBOSE_PREFIX_4 "play_to_caller = %s\n", play_to_caller ? "yes" : "no");
 			ast_verbose(VERBOSE_PREFIX_4 "play_to_callee = %s\n", play_to_callee ? "yes" : "no");
@@ -1657,6 +1848,8 @@
 				ast_set_flag(&(config.features_caller), AST_FEATURE_PARKCALL);
 
 			config.timelimit = timelimit;
+			config.rtcc_check_interval = rtcc_check_interval;
+			config.rtcc_app = rtcc_app;
 			config.play_warning = play_warning;
 			config.warning_freq = warning_freq;
 			config.warning_sound = warning_sound;
@@ -1690,6 +1883,28 @@
 				ast_channel_setoption(chan,
 					AST_OPTION_OPRMODE,&oprmode,sizeof(struct oprmode),0);
 			}
+
+			if (rtcc_check_interval) {
+				rtcc_data = ast_malloc(sizeof(*rtcc_data));
+				rtcc_data->ch0 = chan;
+				rtcc_data->ch1 = peer;
+				rtcc_data->config = &config;
+				rtcc_data->rtcc_sched_id = 0;
+
+				ast_mutex_lock(&rtcc_threadcontrol_lock);
+				if(!rtcc_sched_con) {
+					rtcc_sched_con = sched_context_create();
+				}
+				if(rtcc_thread == AST_PTHREADT_NULL) {
+					if (ast_pthread_create_background(&rtcc_thread, NULL, &ast_rtcc_monitor, NULL) < 0) {
+						ast_log(LOG_ERROR, "Failed to start new limit RTCC thread for application %s on channel %s\n", rtcc_app, chan->name);
+					}
+				}
+				rtcc_sched_id = ast_sched_add_variable(rtcc_sched_con, rtcc_check_interval, &ast_rtcc_callback, rtcc_data, 1);
+				rtcc_data->rtcc_sched_id = rtcc_sched_id;
+				ast_mutex_unlock(&rtcc_threadcontrol_lock);
+			}
+
 			res = ast_bridge_call(chan,peer,&config);
 			time(&end_time);
 			{
@@ -1697,6 +1912,16 @@
 				snprintf(toast, sizeof(toast), "%ld", (long)(end_time - answer_time));
 				pbx_builtin_setvar_helper(chan, "ANSWEREDTIME", toast);
 			}
+
+			if (rtcc_check_interval) {
+				ast_mutex_lock(&rtcc_threadcontrol_lock);
+				if ( rtcc_sched_id && (ast_sched_when(rtcc_sched_con, rtcc_sched_id) != -1) ) {
+					ast_sched_del(rtcc_sched_con, rtcc_sched_id);
+				}
+				ast_free(rtcc_data);
+				ast_mutex_unlock(&rtcc_threadcontrol_lock);
+			}
+
 		} else {
 			time(&end_time);
 			res = -1;
Index: include/asterisk/channel.h
===================================================================
--- include/asterisk/channel.h	(revision 113648)
+++ include/asterisk/channel.h	(working copy)
@@ -526,17 +526,27 @@
 	struct ast_flags features_caller;
 	struct ast_flags features_callee;
 	struct timeval start_time;
+	struct timeval nexteventts;
 	long feature_timer;
 	long timelimit;
+	long rtcc_check_interval;
 	long play_warning;
 	long warning_freq;
 	const char *warning_sound;
 	const char *end_sound;
 	const char *start_sound;
+	const char *rtcc_app;
 	int firstpass;
 	unsigned int flags;
 };
 
+struct ast_rtcc_bridge_combo {
+	struct ast_channel *ch0;
+	struct ast_channel *ch1;
+	struct ast_bridge_config *config;
+	int rtcc_sched_id;
+};
+
 struct chanmon;
 
 #define LOAD_OH(oh) {	\
Index: main/channel.c
===================================================================
--- main/channel.c	(revision 113648)
+++ main/channel.c	(working copy)
@@ -3715,7 +3715,7 @@
 
 static enum ast_bridge_result ast_generic_bridge(struct ast_channel *c0, struct ast_channel *c1,
 						 struct ast_bridge_config *config, struct ast_frame **fo,
-						 struct ast_channel **rc, struct timeval bridge_end)
+						 struct ast_channel **rc)
 {
 	/* Copy voice back and forth between the two channels. */
 	struct ast_channel *cs[3];
@@ -3753,8 +3753,8 @@
 			res = AST_BRIDGE_RETRY;
 			break;
 		}
-		if (bridge_end.tv_sec) {
-			to = ast_tvdiff_ms(bridge_end, ast_tvnow());
+		if (config->nexteventts.tv_sec) {
+			to = ast_tvdiff_ms(config->nexteventts, ast_tvnow());
 			if (to <= 0) {
 				if (config->timelimit)
 					res = AST_BRIDGE_RETRY;
@@ -3928,9 +3928,11 @@
 	if (config->feature_timer) {
 		nexteventts = ast_tvadd(config->start_time, ast_samp2tv(config->feature_timer, 1000));
 	} else if (config->timelimit) {
-		nexteventts = ast_tvadd(config->start_time, ast_samp2tv(config->timelimit, 1000));
-		if (caller_warning || callee_warning)
-			nexteventts = ast_tvsub(nexteventts, ast_samp2tv(config->play_warning, 1000));
+		config->nexteventts = ast_tvadd(config->start_time, ast_samp2tv(config->timelimit, 1000));
+		if (caller_warning || callee_warning) {
+			config->nexteventts = ast_tvsub(config->nexteventts, ast_samp2tv(config->play_warning, 1000));
+		}
+		config->nexteventts = nexteventts;
 	}
 
 	if (!c0->tech->send_digit_begin)
@@ -3948,9 +3950,14 @@
 
 		to = -1;
 
-		if (!ast_tvzero(nexteventts)) {
+		if (!ast_tvzero(nexteventts) || !ast_tvzero(config->nexteventts)) {
 			now = ast_tvnow();
-			to = ast_tvdiff_ms(nexteventts, now);
+			if (!ast_tvzero(nexteventts)) {
+				to = ast_tvdiff_ms(nexteventts, now);
+			}
+			else {
+				to = ast_tvdiff_ms(config->nexteventts, now);
+			}
 			if (to <= 0) {
 				if (!config->timelimit) {
 					res = AST_BRIDGE_COMPLETE;
@@ -3986,9 +3993,9 @@
 						bridge_playfile(c1, c0, config->warning_sound, t);
 				}
 				if (config->warning_freq && (time_left_ms > (config->warning_freq + 5000)))
-					nexteventts = ast_tvadd(nexteventts, ast_samp2tv(config->warning_freq, 1000));
+					config->nexteventts = ast_tvadd(config->nexteventts, ast_samp2tv(config->warning_freq, 1000));
 				else
-					nexteventts = ast_tvadd(config->start_time, ast_samp2tv(config->timelimit, 1000));
+					config->nexteventts = ast_tvadd(config->start_time, ast_samp2tv(config->timelimit, 1000));
 			}
 		}
 
@@ -4097,7 +4104,7 @@
 			o0nativeformats = c0->nativeformats;
 			o1nativeformats = c1->nativeformats;
 		}
-		res = ast_generic_bridge(c0, c1, config, fo, rc, nexteventts);
+		res = ast_generic_bridge(c0, c1, config, fo, rc);
 		if (res != AST_BRIDGE_RETRY)
 			break;
 	}
